Add mbtk_loopbuff and rtp support.

Change-Id: Idc80af4efb8ff76c4afc59fdf1d3896b453e6ff4
diff --git a/mbtk/Makefile b/mbtk/Makefile
index 4c90a12..a68830e 100755
--- a/mbtk/Makefile
+++ b/mbtk/Makefile
@@ -10,7 +10,7 @@
 
 # Build bin file.
 ifeq ($(MBTK_SOURCE_VERSION), 2)
-DIRS += mbtk_rild_v2 mbtk_servicesd_v2 mbtk_sdk_ready_v2
+DIRS += mbtk_rild_v2 mbtk_servicesd_v2 mbtk_sdk_ready_v2 mbtk_rtpd
 else
 DIRS += mbtk_rild mbtk_servicesd mbtk_sdk_ready
 endif
diff --git a/mbtk/include/mbtk/mbtk_audio2.h b/mbtk/include/mbtk/mbtk_audio2.h
index b55f01c..c17efc2 100755
--- a/mbtk/include/mbtk/mbtk_audio2.h
+++ b/mbtk/include/mbtk/mbtk_audio2.h
@@ -38,12 +38,14 @@
 
 int mbtk_audio_pcm_sample_rate_set(mbtk_audio_sample_rate_enum sample_rate);
 
+// Send PCM to location SPK.
 int mbtk_audio_pcm_play_start();
 
 int mbtk_audio_pcm_play_data_send(const void* data,uint32 data_len);
 
 int mbtk_audio_pcm_play_stop();
 
+// Get PCM from location MIC.
 int mbtk_audio_pcm_recorder_start(mbtk_recorder_callback_func recorder_cb);
 
 int mbtk_audio_pcm_recorder_pause();
@@ -76,6 +78,12 @@
 
 int mbtk_audio_wav_deinit();
 
+// Send PCM to the other end of the voice.
+int mbtk_audio_voice_pcm_playback_start();
+
+// Get PCM from the other end of the voice.
+int mbtk_audio_voice_pcm_record_start();
+
 /**
  *    device: UINT32
  *         0: earpiece
diff --git a/mbtk/include/mbtk/mbtk_loop_buffer.h b/mbtk/include/mbtk/mbtk_loop_buffer.h
new file mode 100755
index 0000000..314978d
--- /dev/null
+++ b/mbtk/include/mbtk/mbtk_loop_buffer.h
@@ -0,0 +1,34 @@
+/*
+* mbtk_loop_buffer.h
+*
+* MBTK loop buffer header.
+*
+* Author : lb
+* Date   : 2024/12/5 19:46:11
+*/
+#ifndef _MBTK_LOOP_BUFFER_H
+#define _MBTK_LOOP_BUFFER_H
+#include "mbtk_type.h"
+
+typedef void mbtk_loop_buff_handle;
+
+mbtk_loop_buff_handle* mbtk_loopbuff_get(int size);
+
+int mbtk_loopbuff_free(mbtk_loop_buff_handle* handle);
+
+int mbtk_loopbuff_write(mbtk_loop_buff_handle* handle, const void *data, int data_len);
+
+int mbtk_loopbuff_read(mbtk_loop_buff_handle* handle, void *data, int data_len);
+
+int mbtk_loopbuff_writen(mbtk_loop_buff_handle* handle, const void *data, int data_len);
+
+int mbtk_loopbuff_readn(mbtk_loop_buff_handle* handle, void *data, int data_len);
+
+// Only for read seek.
+int mbtk_loopbuff_seek(mbtk_loop_buff_handle* handle, int offset);
+
+int mbtk_loopbuff_size(mbtk_loop_buff_handle* handle);
+
+void mbtk_loopbuff_print(mbtk_loop_buff_handle* handle);
+
+#endif /* _MBTK_LOOP_BUFFER_H */
diff --git a/mbtk/include/mbtk/mbtk_rtp.h b/mbtk/include/mbtk/mbtk_rtp.h
new file mode 100755
index 0000000..8786c36
--- /dev/null
+++ b/mbtk/include/mbtk/mbtk_rtp.h
@@ -0,0 +1,31 @@
+/*
+* mbtk_rtp.h
+*
+* MBTK RTP API header.
+*
+* Author : lb
+* Date   : 2024/12/2 19:51:25
+*/
+#ifndef _MBTK_RTP_H
+#define _MBTK_RTP_H
+#include "mbtk_type.h"
+
+int mbtk_rtp_init();
+
+int mbtk_rtp_deinit();
+
+int mbtk_rtp_enable(bool enable);
+
+int mbtk_rtp_volume_set(int volume);
+
+int mbtk_rtp_remote_ip_set(const char *ipv4);
+
+int mbtk_rtp_server_port_set(int port);
+
+int mbtk_rtp_client_port_set(int port);
+
+int mbtk_rtp_sample_rate_set(int sample_rate);
+
+int mbtk_rtp_channel_set(int channel);
+
+#endif /* _MBTK_RTP_H */
diff --git a/mbtk/libmbtk_lib/Makefile b/mbtk/libmbtk_lib/Makefile
index 91c38de..90fc89f 100755
--- a/mbtk/libmbtk_lib/Makefile
+++ b/mbtk/libmbtk_lib/Makefile
@@ -18,7 +18,8 @@
 	-I$(LOCAL_PATH)/tcpip \
 	-I$(LOCAL_PATH)/ril/inc \
 	-I$(LOCAL_PATH)/sleep \
-    -I$(LOCAL_PATH)/wifi
+    -I$(LOCAL_PATH)/wifi \
+    -I$(BUILD_ROOT)/mbtk_rtpd/inc
 
 LIB_DIR +=
 
@@ -67,7 +68,8 @@
 	common/mbtk_version.c \
 	common/mbtk_gpio.c \
 	common/mbtk_adc.c \
-	common/mbtk_debug.c
+	common/mbtk_debug.c \
+	common/mbtk_loopbuff.c
 
 # audio
 LOCAL_SRC_FILES += \
@@ -77,6 +79,10 @@
 	audio/mbtk_wav.c \
 	audio/mbtk_audio_ubus.c
 
+# rtp
+LOCAL_SRC_FILES += \
+    rtp/mbtk_rtp.c
+
 # coap
 LOCAL_SRC_FILES += \
     coap/mbtk_coap.c \
diff --git a/mbtk/libmbtk_lib/audio/mbtk_audio_internal.h b/mbtk/libmbtk_lib/audio/mbtk_audio_internal.h
index b4b3dc2..0f020b7 100755
--- a/mbtk/libmbtk_lib/audio/mbtk_audio_internal.h
+++ b/mbtk/libmbtk_lib/audio/mbtk_audio_internal.h
@@ -46,10 +46,9 @@
     audio_hw_device_t *audio_ahw_dev_ubus;
 
     mbtk_audio_direction_enum direction;
-    union {
-        audio_play_info_t play;
-        audio_recorder_info_t recorder;
-    }info;
+
+    audio_play_info_t play;
+    audio_recorder_info_t recorder;
 } audio_inter_info_t;
 
 #endif /* _MBTK_AUDIO_INTERNAL_H */
diff --git a/mbtk/libmbtk_lib/audio/mbtk_pcm_stream.c b/mbtk/libmbtk_lib/audio/mbtk_pcm_stream.c
index 40f89e5..9e5f8ce 100755
--- a/mbtk/libmbtk_lib/audio/mbtk_pcm_stream.c
+++ b/mbtk/libmbtk_lib/audio/mbtk_pcm_stream.c
@@ -1,3 +1,21 @@
+/*
+*    mbtk_pcm_stream.c
+*
+*    MBTK audio playback/record PCM source.
+*
+*/
+/******************************************************************************
+
+                          EDIT HISTORY FOR FILE
+
+  WHEN        WHO       WHAT,WHERE,WHY
+--------    --------    -------------------------------------------------------
+2024/03/23     LiuBin      Initial version.
+2024/10/15     LiuBin      Add PA and volume control API.
+2024/11/30     LiuBin      Add get/send PCM from/to the end of voice(RTP).
+
+******************************************************************************/
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -78,28 +96,28 @@
     int len, frames = 0;
     char buff[MBTK_PCM_WB_BUF_SIZE];
 
-    audio_info->info.recorder.state = AUDIO_RECORDER_STATE_RUNNING;
-    pthread_mutex_init(&audio_info->info.recorder.mutex, NULL);
-    pthread_cond_init(&audio_info->info.recorder.cond, NULL);
+    audio_info->recorder.state = AUDIO_RECORDER_STATE_RUNNING;
+    pthread_mutex_init(&audio_info->recorder.mutex, NULL);
+    pthread_cond_init(&audio_info->recorder.cond, NULL);
 
     while (TRUE) {
         /* Playback loop */
-        pthread_mutex_lock(&audio_info->info.recorder.mutex);
-        if(audio_info->info.recorder.state == AUDIO_RECORDER_STATE_STOP) {
+        pthread_mutex_lock(&audio_info->recorder.mutex);
+        if(audio_info->recorder.state == AUDIO_RECORDER_STATE_STOP) {
             LOGD("Stop recorder...");
-            pthread_mutex_unlock(&audio_info->info.recorder.mutex);
+            pthread_mutex_unlock(&audio_info->recorder.mutex);
             break;
-        } else if(audio_info->info.recorder.state == AUDIO_RECORDER_STATE_PAUSE) {
-            pthread_cond_wait(&audio_info->info.recorder.cond, &audio_info->info.recorder.mutex);
-            pthread_mutex_unlock(&audio_info->info.recorder.mutex);
+        } else if(audio_info->recorder.state == AUDIO_RECORDER_STATE_PAUSE) {
+            pthread_cond_wait(&audio_info->recorder.cond, &audio_info->recorder.mutex);
+            pthread_mutex_unlock(&audio_info->recorder.mutex);
             continue;
         } else {
-            pthread_mutex_unlock(&audio_info->info.recorder.mutex);
+            pthread_mutex_unlock(&audio_info->recorder.mutex);
         }
 
         //record the needed format stream from the device.
         //only read pcm stream, no send command.
-        len = audio_info->info.recorder.stream_in->read(audio_info->info.recorder.stream_in, buff,
+        len = audio_info->recorder.stream_in->read(audio_info->recorder.stream_in, buff,
                     audio_info->playback_size);
         if (len <= 0) {
             LOGE("%s: error reading!", __FUNCTION__);
@@ -108,45 +126,44 @@
 
         AUDIO_LOG("Recorder data : len - %d", len);
 
-        if(audio_info->info.recorder.recorder_cb) {
-            audio_info->info.recorder.recorder_cb(buff, len);
+        if(audio_info->recorder.recorder_cb) {
+            audio_info->recorder.recorder_cb(buff, len);
         }
 
-        LOGD("%s: No.%d frame playback.", __FUNCTION__, ++frames);
+        AUDIO_LOG("%s: No.%d frame playback.", __FUNCTION__, ++frames);
     }
 
 
 thread_end:
-    pthread_mutex_destroy(&audio_info->info.recorder.mutex);
-    pthread_cond_destroy(&audio_info->info.recorder.cond);
+    pthread_mutex_destroy(&audio_info->recorder.mutex);
+    pthread_cond_destroy(&audio_info->recorder.cond);
 
-    audio_info->info.recorder.stream_in->common.standby(&audio_info->info.recorder.stream_in->common);
-    audio_info->audio_ahw_dev_ubus->close_input_stream(audio_info->audio_ahw_dev_ubus, audio_info->info.recorder.stream_in);
+    audio_info->recorder.stream_in->common.standby(&audio_info->recorder.stream_in->common);
+    audio_info->audio_ahw_dev_ubus->close_input_stream(audio_info->audio_ahw_dev_ubus, audio_info->recorder.stream_in);
     VCMDeinit();//close the fd of audiostub_ctl when exit the thread.
-    audio_info->info.recorder.stream_in = NULL;
+    audio_info->recorder.stream_in = NULL;
     LOGD("%s: finished pcm playback.", __FUNCTION__);
 
     // Notify audio recorder data end.
-    if(audio_info->info.recorder.recorder_cb) {
-        audio_info->info.recorder.recorder_cb(NULL, 0);
+    if(audio_info->recorder.recorder_cb) {
+        audio_info->recorder.recorder_cb(NULL, 0);
     }
     return;
 }
 
 
-static int config_parameters(mbtk_audio_direction_enum direction, mbtk_audio_sample_rate_enum NBWB)
+// srcdst = 1;/* 0-None, 1-Near end, 2-Far end, 3-Both ends */
+// priority = 1;/* 0-Do not combine(override), 1-Combine */
+// dest = 1;/* 0-Near codec, 1-Near Vocoder */
+static int config_parameters(mbtk_audio_direction_enum direction, mbtk_audio_sample_rate_enum NBWB,
+                unsigned int srcdst, unsigned int priority, unsigned int dest)
 {
-    unsigned int srcdst, priority, dest;
     char kvpair[128];
     struct str_parms *param = NULL;
     int data[5];
     const char *key = NULL;
     bool update_vcm = false;
 
-    srcdst = 1;/* 0-None, 1-Near end, 2-Far end, 3-Both ends */
-    priority = 1;/* 0-Do not combine(override), 1-Combine */
-    dest = 1;/* 0-Near codec, 1-Near Vocoder */
-
     if(direction == MBTK_AUDIO_DIRECTION_OUTPUT){//output
         if(NBWB == MBTK_AUDIO_SAMPLE_RATE_8000)
             audio_info->playback_size = MBTK_PCM_NB_BUF_SIZE;
@@ -272,7 +289,7 @@
     return 0;
 }
 
-int mbtk_audio_pcm_play_start()
+int audio_pcm_play_start(unsigned int srcdst, unsigned int priority, unsigned int dest)
 {
     if(!audio_info) {
         LOGE("Not inited.");
@@ -284,18 +301,18 @@
         return -1;
     }
 
-    config_parameters(MBTK_AUDIO_DIRECTION_OUTPUT, audio_info->sample_rate);
+    config_parameters(MBTK_AUDIO_DIRECTION_OUTPUT, audio_info->sample_rate, srcdst, priority, dest);
 
     int rc = audio_info->audio_ahw_dev_ubus->open_output_stream(audio_info->audio_ahw_dev_ubus, 0,
             audio_info->audio_ahw_dev_ubus->get_supported_devices(audio_info->audio_ahw_dev_ubus),
-            AUDIO_OUTPUT_FLAG_DIRECT, NULL, &(audio_info->info.play.stream_out), 0);
+            AUDIO_OUTPUT_FLAG_DIRECT, NULL, &(audio_info->play.stream_out), 0);
     if (rc < 0) {
         LOGE("Open output device fail:%d", rc);
         goto play_start_fail;
     }
 
     audio_info->direction = MBTK_AUDIO_DIRECTION_OUTPUT;
-    audio_info->info.play.buff_remain_len = 0;
+    audio_info->play.buff_remain_len = 0;
     audio_pcm_write_count = 0;
 
     return 0;
@@ -304,6 +321,39 @@
     return -1;
 }
 
+// Send PCM to the other end of the voice.
+int mbtk_audio_voice_pcm_playback_start()
+{
+    // direction = 0;/* 0-play, 1-record */
+    // type = 0/1; /* 0:PCM_NB_BUF_SIZE, 1:PCM_WB_BUF_SIZE */
+    // srcdst = 2;/* 0-None, 1-Near end, 2-Far end, 3-Both ends */
+    // priority = 0;/* 0-Do not combine(override), 1-Combine */
+    // dest = 0;/* 0-Near codec, 1-Near Vocoder */
+    return audio_pcm_play_start(2, 0, 0);
+#if 0
+    if(!audio_info) {
+        LOGE("Not inited.");
+        return -1;
+    }
+
+    if(!audio_info->audio_ahw_dev_ubus) {
+        LOGE("audio_info->audio_ahw_dev_ubus is NULL.");
+        return -1;
+    }
+
+    config_parameters(MBTK_AUDIO_DIRECTION_OUTPUT, audio_info->sample_rate, 2, 0, 0);
+    audio_info->play.is_voip = TRUE;
+
+    return 0;
+#endif
+}
+
+// Send PCM to location SPK.
+int mbtk_audio_pcm_play_start()
+{
+    return audio_pcm_play_start(1, 1, 1);
+}
+
 int mbtk_audio_pcm_play_data_send(const void* data,uint32 data_len)
 {
     UNUSED(data);
@@ -314,25 +364,25 @@
         return -1;
     }
 
-    if(!audio_info->info.play.stream_out) {
+    if(!audio_info->play.stream_out) {
         LOGE("Output device not open.");
         return -1;
     }
 
     uint32 index = 0;
     // There are remaining data from the previous package。
-    if(audio_info->info.play.buff_remain_len > 0) {
+    if(audio_info->play.buff_remain_len > 0) {
         // Too less for one package.
-        if(data_len + audio_info->info.play.buff_remain_len < audio_info->playback_size) {
+        if(data_len + audio_info->play.buff_remain_len < audio_info->playback_size) {
             AUDIO_LOG("Save remain data : len - %d", data_len);
-            memcpy(audio_info->info.play.buff_remain + audio_info->info.play.buff_remain_len, data, data_len);
-            audio_info->info.play.buff_remain_len += data_len;
+            memcpy(audio_info->play.buff_remain + audio_info->play.buff_remain_len, data, data_len);
+            audio_info->play.buff_remain_len += data_len;
             return data_len;
         } else {
-            AUDIO_LOG("Write remain data : %d + %d", audio_info->info.play.buff_remain_len, audio_info->playback_size - audio_info->info.play.buff_remain_len);
-            memcpy(audio_info->info.play.buff_remain + audio_info->info.play.buff_remain_len, data, audio_info->playback_size - audio_info->info.play.buff_remain_len);
+            AUDIO_LOG("Write remain data : %d + %d", audio_info->play.buff_remain_len, audio_info->playback_size - audio_info->play.buff_remain_len);
+            memcpy(audio_info->play.buff_remain + audio_info->play.buff_remain_len, data, audio_info->playback_size - audio_info->play.buff_remain_len);
 
-            int rc = audio_info->info.play.stream_out->write(audio_info->info.play.stream_out, audio_info->info.play.buff_remain, audio_info->playback_size);
+            int rc = audio_info->play.stream_out->write(audio_info->play.stream_out, audio_info->play.buff_remain, audio_info->playback_size);
             if (rc < 0) {
                 LOGE("%s: error writing (child).", __FUNCTION__);
                 goto send_fail;
@@ -357,14 +407,14 @@
                 }
             }
 
-            index += (audio_info->playback_size - audio_info->info.play.buff_remain_len);
-            audio_info->info.play.buff_remain_len = 0;
+            index += (audio_info->playback_size - audio_info->play.buff_remain_len);
+            audio_info->play.buff_remain_len = 0;
         }
     }
 
     while(data_len - index >= audio_info->playback_size) {
         AUDIO_LOG("Package : %d -> %d", index, index + audio_info->playback_size - 1);
-        int rc = audio_info->info.play.stream_out->write(audio_info->info.play.stream_out, (const char*)data + index, audio_info->playback_size);
+        int rc = audio_info->play.stream_out->write(audio_info->play.stream_out, (const char*)data + index, audio_info->playback_size);
         if (rc < 0) {
             LOGE("%s: error writing (child).", __FUNCTION__);
             goto send_fail;
@@ -380,8 +430,8 @@
     // Save to buffer audio_info->play_buff_remain
     if(data_len - index > 0) {
         AUDIO_LOG("Save remain data : len - %d", data_len - index);
-        memcpy(audio_info->info.play.buff_remain, data + index, data_len - index);
-        audio_info->info.play.buff_remain_len = data_len - index;
+        memcpy(audio_info->play.buff_remain, data + index, data_len - index);
+        audio_info->play.buff_remain_len = data_len - index;
     }
 
     return data_len;
@@ -396,20 +446,20 @@
         return -1;
     }
 
-    if(!audio_info->info.play.stream_out) {
+    if(!audio_info->play.stream_out) {
         LOGE("Output device not open.");
         return -1;
     }
 
     // Write last package.
-    if(audio_info->info.play.buff_remain_len > 0) {
+    if(audio_info->play.buff_remain_len > 0) {
         char buf[MBTK_PCM_WB_BUF_SIZE];
         memset(buf, 0x00, sizeof(buf));
-        memcpy(buf, audio_info->info.play.buff_remain, audio_info->info.play.buff_remain_len);
+        memcpy(buf, audio_info->play.buff_remain, audio_info->play.buff_remain_len);
 
-        LOGD("len %d is smaller than needed %d, so fill the buffer with 0.", audio_info->info.play.buff_remain_len, audio_info->playback_size);
+        LOGD("len %d is smaller than needed %d, so fill the buffer with 0.", audio_info->play.buff_remain_len, audio_info->playback_size);
 
-        int rc = audio_info->info.play.stream_out->write(audio_info->info.play.stream_out, buf, audio_info->playback_size);
+        int rc = audio_info->play.stream_out->write(audio_info->play.stream_out, buf, audio_info->playback_size);
         if (rc < 0) {
             LOGE("%s: error writing (child).", __FUNCTION__);
             //goto send_fail;
@@ -417,7 +467,7 @@
             LOGW("%s: wrote less than buffer size, rc=%d.", __FUNCTION__, rc);
             //goto send_fail;
         }
-        audio_info->info.play.buff_remain_len = 0;
+        audio_info->play.buff_remain_len = 0;
     }
 
     // Close PA
@@ -427,15 +477,15 @@
 
     vcm_playback_drain(80);//wait for drain the AP audiostub queue.
     usleep(80000);//delay 80ms until DSP play out its buffered data.
-    audio_info->info.play.stream_out->common.standby(&(audio_info->info.play.stream_out->common));
-    audio_info->audio_ahw_dev_ubus->close_output_stream(audio_info->audio_ahw_dev_ubus, audio_info->info.play.stream_out);
+    audio_info->play.stream_out->common.standby(&(audio_info->play.stream_out->common));
+    audio_info->audio_ahw_dev_ubus->close_output_stream(audio_info->audio_ahw_dev_ubus, audio_info->play.stream_out);
 
-    audio_info->info.play.stream_out = NULL;
+    audio_info->play.stream_out = NULL;
     return 0;
 }
 
-
-int mbtk_audio_pcm_recorder_start(mbtk_recorder_callback_func recorder_cb)
+static int audio_pcm_recorder_start(mbtk_recorder_callback_func recorder_cb,
+                unsigned int srcdst, unsigned int priority, unsigned int dest)
 {
     if(!audio_info) {
         LOGE("Not inited.");
@@ -447,25 +497,25 @@
         return -1;
     }
 
-    if(audio_info->info.recorder.state != AUDIO_RECORDER_STATE_STOP) {
+    if(audio_info->recorder.state != AUDIO_RECORDER_STATE_STOP) {
         LOGW("Audio is recorder...");
         return -1;
     }
 
-    config_parameters(MBTK_AUDIO_DIRECTION_INPUT, audio_info->sample_rate);
+    config_parameters(MBTK_AUDIO_DIRECTION_INPUT, audio_info->sample_rate, srcdst, priority, dest);
 
     VCMInit();
 
     int rc = audio_info->audio_ahw_dev_ubus->open_input_stream(audio_info->audio_ahw_dev_ubus, 0,
                 audio_info->audio_ahw_dev_ubus->get_supported_devices(audio_info->audio_ahw_dev_ubus),
-                NULL, &(audio_info->info.recorder.stream_in), 0, 0, AUDIO_SOURCE_VOICE_CALL);
+                NULL, &(audio_info->recorder.stream_in), 0, 0, AUDIO_SOURCE_VOICE_CALL);
     if (rc < 0) {
         LOGE("Open input device fail:%d", rc);
         goto recorder_start_fail;
     }
 
     audio_info->direction = MBTK_AUDIO_DIRECTION_INPUT;
-    audio_info->info.recorder.recorder_cb = recorder_cb;
+    audio_info->recorder.recorder_cb = recorder_cb;
 
     pthread_attr_t thread_attr;
     pthread_attr_init(&thread_attr);
@@ -486,32 +536,70 @@
     return -1;
 }
 
+// Get PCM from the other end of the voice.
+int mbtk_audio_voice_pcm_record_start(mbtk_recorder_callback_func recorder_cb)
+{
+    // direction = 1;/* 0-play, 1-record */
+    // type = 0/1; /* 0:PCM_NB_BUF_SIZE, 1:PCM_WB_BUF_SIZE */
+    // srcdst = 2;/* 0-None, 1-Near end, 2-Far end, 3-Both ends */
+    // priority = 0;/* 0-Do not combine(override), 1-Combine */
+    // dest = 0;/* 0-Near codec, 1-Near Vocoder */
+    return audio_pcm_recorder_start(recorder_cb, 2, 0, 0);
+#if 0
+    if(!audio_info) {
+        LOGE("Not inited.");
+        return -1;
+    }
+
+    if(!audio_info->audio_ahw_dev_ubus) {
+        LOGE("audio_info->audio_ahw_dev_ubus is NULL.");
+        return -1;
+    }
+
+    if(audio_info->recorder.state != AUDIO_RECORDER_STATE_STOP) {
+        LOGW("Audio is recorder...");
+        return -1;
+    }
+
+    config_parameters(MBTK_AUDIO_DIRECTION_INPUT, audio_info->sample_rate, 2, 0, 0);
+    audio_info->recorder.is_voip = TRUE;
+
+    return 0;
+#endif
+}
+
+// Get PCM from location MIC.
+int mbtk_audio_pcm_recorder_start(mbtk_recorder_callback_func recorder_cb)
+{
+    return audio_pcm_recorder_start(recorder_cb, 1, 1, 1);
+}
+
 int mbtk_audio_pcm_recorder_pause()
 {
     int result = 0;
-    pthread_mutex_lock(&audio_info->info.recorder.mutex);
-    if(audio_info->info.recorder.state == AUDIO_RECORDER_STATE_RUNNING) {
-        audio_info->info.recorder.state = AUDIO_RECORDER_STATE_PAUSE;
+    pthread_mutex_lock(&audio_info->recorder.mutex);
+    if(audio_info->recorder.state == AUDIO_RECORDER_STATE_RUNNING) {
+        audio_info->recorder.state = AUDIO_RECORDER_STATE_PAUSE;
     } else {
         result = -1;
-        LOGW("Audio state : %d", audio_info->info.recorder.state);
+        LOGW("Audio state : %d", audio_info->recorder.state);
     }
-    pthread_mutex_unlock(&audio_info->info.recorder.mutex);
+    pthread_mutex_unlock(&audio_info->recorder.mutex);
     return result;
 }
 
 int mbtk_audio_pcm_recorder_resume()
 {
     int result = 0;
-    pthread_mutex_lock(&audio_info->info.recorder.mutex);
-    if(audio_info->info.recorder.state == AUDIO_RECORDER_STATE_PAUSE) {
-        audio_info->info.recorder.state = AUDIO_RECORDER_STATE_RUNNING;
-        pthread_cond_signal(&audio_info->info.recorder.cond);
+    pthread_mutex_lock(&audio_info->recorder.mutex);
+    if(audio_info->recorder.state == AUDIO_RECORDER_STATE_PAUSE) {
+        audio_info->recorder.state = AUDIO_RECORDER_STATE_RUNNING;
+        pthread_cond_signal(&audio_info->recorder.cond);
     } else {
         result = -1;
-        LOGW("Audio state : %d", audio_info->info.recorder.state);
+        LOGW("Audio state : %d", audio_info->recorder.state);
     }
-    pthread_mutex_unlock(&audio_info->info.recorder.mutex);
+    pthread_mutex_unlock(&audio_info->recorder.mutex);
     return result;
 }
 
@@ -519,13 +607,13 @@
 int mbtk_audio_pcm_recorder_stop()
 {
     int result = 0;
-    pthread_mutex_lock(&audio_info->info.recorder.mutex);
-    if(audio_info->info.recorder.state == AUDIO_RECORDER_STATE_PAUSE || audio_info->info.recorder.state == AUDIO_RECORDER_STATE_RUNNING) {
-        if(audio_info->info.recorder.state == AUDIO_RECORDER_STATE_PAUSE) {
-            pthread_cond_signal(&audio_info->info.recorder.cond);
+    pthread_mutex_lock(&audio_info->recorder.mutex);
+    if(audio_info->recorder.state == AUDIO_RECORDER_STATE_PAUSE || audio_info->recorder.state == AUDIO_RECORDER_STATE_RUNNING) {
+        if(audio_info->recorder.state == AUDIO_RECORDER_STATE_PAUSE) {
+            pthread_cond_signal(&audio_info->recorder.cond);
         }
-        audio_info->info.recorder.state = AUDIO_RECORDER_STATE_STOP;
-        pthread_mutex_unlock(&audio_info->info.recorder.mutex);
+        audio_info->recorder.state = AUDIO_RECORDER_STATE_STOP;
+        pthread_mutex_unlock(&audio_info->recorder.mutex);
 
         LOGD("Waitting recorder thread exit...");
         if (pthread_join(recorder_thread_play, NULL)) {
@@ -534,9 +622,9 @@
         }
         LOGD("Recorder thread exit success.");
     } else {
-        pthread_mutex_unlock(&audio_info->info.recorder.mutex);
+        pthread_mutex_unlock(&audio_info->recorder.mutex);
         result = -1;
-        LOGW("Audio state : %d", audio_info->info.recorder.state);
+        LOGW("Audio state : %d", audio_info->recorder.state);
     }
 
     return result;
diff --git a/mbtk/libmbtk_lib/common/mbtk_loopbuff.c b/mbtk/libmbtk_lib/common/mbtk_loopbuff.c
new file mode 100755
index 0000000..37dbade
--- /dev/null
+++ b/mbtk/libmbtk_lib/common/mbtk_loopbuff.c
@@ -0,0 +1,309 @@
+/*
+*    mbtk_loop_buffer.c
+*
+*    MBTK loop buffer sources.
+*
+*/
+/******************************************************************************
+
+                          EDIT HISTORY FOR FILE
+
+  WHEN        WHO       WHAT,WHERE,WHY
+--------    --------    -------------------------------------------------------
+2024/12/5     LiuBin      Initial version
+
+******************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "mbtk_str.h"
+#include "mbtk_utils.h"
+#include "mbtk_log.h"
+#include "mbtk_type.h"
+#include "mbtk_loop_buffer.h"
+
+typedef struct {
+    int size;
+    uint8 *buffer;
+
+    int head;
+    int tail;
+
+    int avail_size;
+
+    pthread_mutex_t mutex;
+} loop_buffer_t;
+
+mbtk_loop_buff_handle* mbtk_loopbuff_get(int size)
+{
+    if(size <= 0) {
+        LOGE("size error : %d", size);
+        return NULL;
+    }
+
+    loop_buffer_t *buff = (loop_buffer_t*)malloc(sizeof(loop_buffer_t));
+    if(buff == NULL) {
+        LOGE("malloc() fail:%d", errno);
+        return NULL;
+    }
+
+    memset(buff, 0, sizeof(loop_buffer_t));
+    buff->buffer = (uint8*)malloc(size);
+    if(buff == NULL) {
+        free(buff);
+        LOGE("malloc() fail:%d", errno);
+        return NULL;
+    }
+    buff->size = buff->avail_size = size;
+
+    pthread_mutex_init(&buff->mutex, NULL);
+
+    return buff;
+}
+
+int mbtk_loopbuff_free(mbtk_loop_buff_handle* handle)
+{
+    if(handle == NULL) {
+        LOGE("handle is NULL");
+        return -1;
+    }
+
+    loop_buffer_t *buff = (loop_buffer_t*)handle;
+    if(buff->buffer) {
+        free(buff->buffer);
+    }
+    pthread_mutex_destroy(&buff->mutex);
+    free(buff);
+
+    return 0;
+}
+
+int mbtk_loopbuff_write(mbtk_loop_buff_handle* handle, const void *data, int data_len)
+{
+    if(handle == NULL || data == NULL || data_len <= 0) {
+        LOGE("ARG error.");
+        return -1;
+    }
+
+    loop_buffer_t *loop_buff = (loop_buffer_t*)handle;
+    pthread_mutex_lock(&loop_buff->mutex);
+    if(loop_buff->avail_size == 0) {
+        LOGV("Buffer full.");
+        pthread_mutex_unlock(&loop_buff->mutex);
+        return -1;
+    } else {
+        int size = loop_buff->avail_size >= data_len ? data_len : loop_buff->avail_size;
+        //     h      t
+        // [...xxxxxxx...]
+        if(loop_buff->tail >= loop_buff->head) {
+            //     h      t  t
+            // [...xxxxxxxyyy.]
+            if(loop_buff->tail + size <= loop_buff->size) {
+                memcpy(loop_buff->buffer + loop_buff->tail, data, size);
+                loop_buff->tail += size;
+                loop_buff->tail %= loop_buff->size;
+            } else {
+                //     t  h   t
+                // [yyy...xxxxyy]
+                int len_right = loop_buff->size - loop_buff->tail; // len_right < size
+                memcpy(loop_buff->buffer + loop_buff->tail, data, len_right);
+                loop_buff->tail = 0;
+                memcpy(loop_buff->buffer + loop_buff->tail, data + len_right, size - len_right);
+                loop_buff->tail += (size - len_right);
+            }
+        } else {
+            //     t  t  h
+            // [xxxyyy...xxxxx]
+            memcpy(loop_buff->buffer + loop_buff->tail, data, size);
+            loop_buff->tail += size;
+            loop_buff->tail %= loop_buff->size;
+        }
+        loop_buff->avail_size -= size;
+        pthread_mutex_unlock(&loop_buff->mutex);
+        return size;
+    }
+}
+
+int mbtk_loopbuff_writen(mbtk_loop_buff_handle* handle, const void *data, int data_len)
+{
+    int len_count = 0;
+    // LOGD("mbtk_loopbuff_write() start.");
+    while(len_count < data_len) {
+        int len = mbtk_loopbuff_write(handle, data + len_count, data_len - len_count);
+        if(len > 0) {
+#if 0
+            if(len != data_len - len_count) {
+                LOGD("%d/%d", len, data_len - len_count);
+            }
+#endif
+            len_count += len;
+        } else {
+            usleep(5000);
+        }
+    }
+#if 0
+    if(data_len != len_count) {
+        LOGD("mbtk_loopbuff_write() xxxxxxxxxxxxxxx fail : %d/%d", len_count, data_len);
+    }
+#endif
+    return len_count;
+}
+
+int mbtk_loopbuff_read(mbtk_loop_buff_handle* handle, void *data, int data_len)
+{
+    if(handle == NULL || data == NULL || data_len <= 0) {
+        LOGE("ARG error.");
+        return -1;
+    }
+
+    loop_buffer_t *loop_buff = (loop_buffer_t*)handle;
+    pthread_mutex_lock(&loop_buff->mutex);
+    if(loop_buff->avail_size == loop_buff->size) { // Buffer is empty.
+        pthread_mutex_unlock(&loop_buff->mutex);
+        LOGV("Buffer is empty.");
+        return 0;
+    } else {
+        int size = (loop_buff->size - loop_buff->avail_size) >= data_len ? data_len :
+                    (loop_buff->size - loop_buff->avail_size);
+        //     h  h   t
+        // [...yyyxxxx...]
+        if(loop_buff->tail > loop_buff->head) {
+            memcpy(data, loop_buff->buffer + loop_buff->head, size);
+            loop_buff->head += size;
+        } else {
+            //       t    h  h
+            // [xxxxx.....yyyxx]
+            if(loop_buff->head + size <= loop_buff->size) {// [xxt...hxxxxxx]
+                memcpy(data, loop_buff->buffer + loop_buff->head, size);
+                loop_buff->head += size;
+                loop_buff->head %= loop_buff->size;
+            } else {
+                //     h   t    h
+                // [yyyxxxx.....yyy]
+                int len_right = loop_buff->size - loop_buff->head; // len_right < size
+                memcpy(data, loop_buff->buffer + loop_buff->head, len_right);
+                loop_buff->head = 0;
+                memcpy(data + len_right, loop_buff->buffer + loop_buff->head, size - len_right);
+                loop_buff->head += (size - len_right);
+            }
+        }
+        loop_buff->avail_size += size;
+        pthread_mutex_unlock(&loop_buff->mutex);
+        return size;
+    }
+}
+
+int mbtk_loopbuff_readn(mbtk_loop_buff_handle* handle, void *data, int data_len)
+{
+    int len_count = 0;
+    int read_count = 0;
+    while(len_count < data_len) {
+        int len = mbtk_loopbuff_read(handle, data + len_count, data_len - len_count);
+        if(len > 0) {
+            len_count += len;
+        } else {
+            usleep(5000);
+        }
+        read_count++;
+
+        if(read_count >= 20)
+            break;
+    }
+    return len_count;
+}
+
+
+// Only for read seek.
+int mbtk_loopbuff_seek(mbtk_loop_buff_handle* handle, int offset)
+{
+    if(handle == NULL) {
+        LOGE("ARG error.");
+        return -1;
+    }
+
+    if(offset == 0)
+        return 0;
+
+    loop_buffer_t *loop_buff = (loop_buffer_t*)handle;
+    pthread_mutex_lock(&loop_buff->mutex);
+    if(/*loop_buff->avail_size == loop_buff->size || */
+        offset > loop_buff->size || offset < -loop_buff->size) {
+        pthread_mutex_unlock(&loop_buff->mutex);
+        return -1;
+    } else {
+        if(offset > 0) {
+            int change = offset > (loop_buff->size - loop_buff->avail_size) ?
+                (loop_buff->size - loop_buff->avail_size) : offset;
+            loop_buff->head += change;
+            loop_buff->head %= loop_buff->size;
+        } else {
+            int change = -offset > loop_buff->avail_size ? loop_buff->avail_size : -offset;
+            loop_buff->head -= change;
+            if(loop_buff->head < 0)
+                loop_buff->head = loop_buff->size + loop_buff->head;
+        }
+
+        loop_buff->avail_size += offset;
+        pthread_mutex_unlock(&loop_buff->mutex);
+        return 0;
+    }
+}
+
+int mbtk_loopbuff_size(mbtk_loop_buff_handle* handle)
+{
+    if(handle == NULL) {
+        LOGE("ARG error.");
+        return -1;
+    }
+
+    loop_buffer_t *loop_buff = (loop_buffer_t*)handle;
+    int size = 0;
+    pthread_mutex_lock(&loop_buff->mutex);
+    size = loop_buff->size - loop_buff->avail_size;
+    pthread_mutex_unlock(&loop_buff->mutex);
+    return size;
+}
+
+void mbtk_loopbuff_print(mbtk_loop_buff_handle* handle)
+{
+    if(handle == NULL) {
+        LOGE("ARG error.");
+        return;
+    }
+
+    loop_buffer_t *loop_buff = (loop_buffer_t*)handle;
+    pthread_mutex_lock(&loop_buff->mutex);
+    int data_size = loop_buff->size - loop_buff->avail_size;
+    if(data_size <= 0) {
+        LOGD("Buffer is NULL.");
+    } else {
+        uint8 *buff = (uint8*)malloc(data_size);
+        if(buff) {
+            //     h      t
+            // [...xxxxxxx...]
+            if(loop_buff->tail > loop_buff->head) {
+                memcpy(buff, loop_buff->buffer + loop_buff->head, data_size);
+            } else {
+                //       t    h
+                // [xxxxx.....xxxxx]
+                int len_right = loop_buff->size - loop_buff->head; // len_right < size
+                memcpy(buff, loop_buff->buffer + loop_buff->head, len_right);
+                if(data_size - len_right > 0) {
+                    memcpy(buff + len_right, loop_buff->buffer, data_size - len_right);
+                }
+            }
+
+            LOGD("Head = %d, Tail = %d, Lenght = %d / %d", loop_buff->head, loop_buff->tail,
+                data_size, loop_buff->size);
+            log_hex("DATA", buff, data_size);
+            free(buff);
+            buff = NULL;
+        }
+    }
+    pthread_mutex_unlock(&loop_buff->mutex);
+}
+
diff --git a/mbtk/libmbtk_lib/rtp/mbtk_rtp.c b/mbtk/libmbtk_lib/rtp/mbtk_rtp.c
new file mode 100755
index 0000000..961b9c5
--- /dev/null
+++ b/mbtk/libmbtk_lib/rtp/mbtk_rtp.c
@@ -0,0 +1,312 @@
+/*
+*    mbtk_rtp.c
+*
+*    MBTK RTP(VOIP) API source. This source depend on mbtk_rtpd server.
+*
+*/
+/******************************************************************************
+
+                          EDIT HISTORY FOR FILE
+
+  WHEN        WHO       WHAT,WHERE,WHY
+--------    --------    -------------------------------------------------------
+2024/12/2     LiuBin      Initial version
+
+******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include "mbtk_rtp_internal.h"
+#include "mbtk_log.h"
+#include "mbtk_str.h"
+
+static int rtp_cli_fd = -1;
+
+static char* rtp_cmd_exec(const char* cmd, char *rsp, int rsp_len)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return NULL;
+    }
+
+    int len = write(rtp_cli_fd, cmd, strlen(cmd));
+    if(len != strlen(cmd)) {
+        LOGE("write() fail(%d) : %d/%d", errno, len, strlen(cmd));
+        return NULL;
+    }
+    LOGD("Write cmd[%s] success.", cmd);
+
+    memset(rsp, 0, rsp_len);
+    len = read(rtp_cli_fd, rsp, rsp_len);
+    if(len <= 0) {
+        LOGE("read() fail(%d) : len = %d", errno, len);
+        return NULL;
+    }
+    LOGD("Read rsp[%s] success.", rsp);
+
+    if(rsp[0] == MBTK_IND_START_FLAG && rsp[len - 1] == MBTK_IND_END_FLAG) {
+        rsp[len - 1] = '\0';
+        return rsp + 1;
+    } else {
+        log_hex("RSP_ERR", rsp, len);
+        return NULL;
+    }
+}
+
+
+int mbtk_rtp_init()
+{
+    if(rtp_cli_fd > 0) {
+        LOGW("RTP client has inited.");
+        return 0;
+    }
+
+    rtp_cli_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if(rtp_cli_fd < 0)
+    {
+        LOGE("socket() fail[%d].", errno);
+        goto error;
+    }
+
+    struct sockaddr_un cli_addr;
+    memset(&cli_addr, 0, sizeof(cli_addr));
+    cli_addr.sun_family = AF_LOCAL;
+    strcpy(cli_addr.sun_path, RTP_IPC_SOCK_PATH);
+    if(connect(rtp_cli_fd, (struct sockaddr *)&cli_addr, sizeof(cli_addr)))
+    {
+        LOGE("connect() fail[%d].", errno);
+        goto error;
+    }
+
+    return 0;
+error:
+    if(rtp_cli_fd > 0) {
+        close(rtp_cli_fd);
+        rtp_cli_fd = -1;
+    }
+
+    return -1;
+}
+
+int mbtk_rtp_deinit()
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+    close(rtp_cli_fd);
+    rtp_cli_fd = -1;
+
+    return 0;
+}
+
+int mbtk_rtp_enable(bool enable)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+    // gnss_init:x
+    char cmd[100] = {0};
+    char rsp[100] = {0};
+    snprintf(cmd, sizeof(cmd), "rtp_mode %d", enable ? 1 : 0); // rtp_mode <0/1>
+    char *result = rtp_cmd_exec(cmd, rsp, sizeof(rsp));
+    if(!result) {
+        return -1;
+    }
+
+    // rtp_mode:<err>
+    if(strcmp(result, "rtp_mode:0") == 0) {
+        return 0;
+    } else {
+        LOGE("CMD exec error:%s", result);
+        return -1;
+    }
+}
+
+int mbtk_rtp_volume_set(int volume)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+    // gnss_init:x
+    char cmd[100] = {0};
+    char rsp[100] = {0};
+    snprintf(cmd, sizeof(cmd), "volume %d", volume); // volume <0-7>
+    char *result = rtp_cmd_exec(cmd, rsp, sizeof(rsp));
+    if(!result) {
+        return -1;
+    }
+
+    // volume:<err>
+    if(strcmp(result, "volume:0") == 0) {
+        return 0;
+    } else {
+        LOGE("CMD exec error:%s", result);
+        return -1;
+    }
+}
+
+int mbtk_rtp_remote_ip_set(const char *ipv4)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+    uint32 IPAddr;
+    if(str_empty(ipv4) || inet_pton(AF_INET, ipv4, &IPAddr) < 0) {
+        LOGE("inet_pton() fail.");
+        return -1;
+    }
+
+    // gnss_init:x
+    char cmd[100] = {0};
+    char rsp[100] = {0};
+    snprintf(cmd, sizeof(cmd), "remote_ip %s", ipv4); // remote_ip xxx.xxx.xxx.xxx
+    char *result = rtp_cmd_exec(cmd, rsp, sizeof(rsp));
+    if(!result) {
+        return -1;
+    }
+
+    // rtp_mode:<err>
+    if(strcmp(result, "remote_ip:0") == 0) {
+        return 0;
+    } else {
+        LOGE("CMD exec error:%s", result);
+        return -1;
+    }
+}
+
+int mbtk_rtp_server_port_set(int port)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+#if 0
+    if(53248 != port) {
+        LOGE("Only support 53248 port[For GXX].");
+        return -1;
+    }
+#endif
+
+    // gnss_init:x
+    char cmd[100] = {0};
+    char rsp[100] = {0};
+    snprintf(cmd, sizeof(cmd), "server_port %d", port); // server_port <port>
+    char *result = rtp_cmd_exec(cmd, rsp, sizeof(rsp));
+    if(!result) {
+        return -1;
+    }
+
+    // volume:<err>
+    if(strcmp(result, "server_port:0") == 0) {
+        return 0;
+    } else {
+        LOGE("CMD exec error:%s", result);
+        return -1;
+    }
+}
+
+int mbtk_rtp_client_port_set(int port)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+    // gnss_init:x
+    char cmd[100] = {0};
+    char rsp[100] = {0};
+    snprintf(cmd, sizeof(cmd), "client_port %d", port); // client_port <port>
+    char *result = rtp_cmd_exec(cmd, rsp, sizeof(rsp));
+    if(!result) {
+        return -1;
+    }
+
+    // volume:<err>
+    if(strcmp(result, "client_port:0") == 0) {
+        return 0;
+    } else {
+        LOGE("CMD exec error:%s", result);
+        return -1;
+    }
+}
+
+int mbtk_rtp_sample_rate_set(int sample_rate)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+    if(sample_rate != 8000 && sample_rate != 16000) {
+        LOGE("Only support 8000/16000.");
+        return -1;
+    }
+
+    // gnss_init:x
+    char cmd[100] = {0};
+    char rsp[100] = {0};
+    snprintf(cmd, sizeof(cmd), "sample_rate %d", sample_rate); // sample_rate <sample_rate>
+    char *result = rtp_cmd_exec(cmd, rsp, sizeof(rsp));
+    if(!result) {
+        return -1;
+    }
+
+    // volume:<err>
+    if(strcmp(result, "sample_rate:0") == 0) {
+        return 0;
+    } else {
+        LOGE("CMD exec error:%s", result);
+        return -1;
+    }
+}
+
+int mbtk_rtp_channel_set(int channel)
+{
+    if(rtp_cli_fd < 0) {
+        LOGW("RTP client not inited.");
+        return -1;
+    }
+
+    if(channel != 1) {
+        LOGE("Only support 1 channel.");
+        return -1;
+    }
+
+    // gnss_init:x
+    char cmd[100] = {0};
+    char rsp[100] = {0};
+    snprintf(cmd, sizeof(cmd), "channel %d", channel); // channel <channel>
+    char *result = rtp_cmd_exec(cmd, rsp, sizeof(rsp));
+    if(!result) {
+        return -1;
+    }
+
+    // volume:<err>
+    if(strcmp(result, "channel:0") == 0) {
+        return 0;
+    } else {
+        LOGE("CMD exec error:%s", result);
+        return -1;
+    }
+}
+
+
diff --git a/mbtk/mbtk_rtpd/Makefile b/mbtk/mbtk_rtpd/Makefile
new file mode 100755
index 0000000..255248b
--- /dev/null
+++ b/mbtk/mbtk_rtpd/Makefile
@@ -0,0 +1,46 @@
+BUILD_ROOT = $(shell pwd)/..
+include $(BUILD_ROOT)/Make.defines
+
+LOCAL_PATH=$(BUILD_ROOT)/mbtk_rtpd
+
+INC_DIR += \
+		-I$(LOCAL_PATH)/inc
+
+LIB_DIR +=
+
+LIBS += -lmbtk_lib -lrilutil -lprop2uci -lmtel -laudio-apu -lcutils -ltinyalsa -lacm
+
+CFLAGS +=
+
+DEFINE +=
+
+MY_FILES_PATH:=$(LOCAL_PATH)/src
+#ifeq ($(CONFIG_MBTK_QL_SUPPORT),y)
+#MY_FILES_PATH += $(LOCAL_PATH)/ql
+#endif
+
+#ifeq ($(CONFIG_MBTK_PLATFORM),linux)
+#MY_FILES_PATH += $(LOCAL_PATH)/platform/linux
+#endif
+
+LOCAL_SRC_FILES = $(wildcard src/*.c) $(wildcard src/*.cpp)
+OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(LOCAL_SRC_FILES)))
+$(info OBJS = $(OBJS))
+
+dtarget := $(OUT_DIR)/bin/mbtk_rtpd
+
+all: $(dtarget)
+
+$(dtarget): $(OBJS)
+	@echo "  BIN     $@"
+	$(CC) $(CFLAGS) $(LIB_DIR) $(LIBS) $(OBJS) -o $@
+
+%.o:%.c
+	$(CC) $(CFLAGS) $(INC_DIR) $(DEFINE) -c $< -o $@
+
+%.o:%.cpp
+	$(CC) $(CFLAGS) $(INC_DIR) $(DEFINE) -c $< -o $@
+
+clean:
+	rm -f $(OBJS) $(dtarget)
+
diff --git a/mbtk/mbtk_rtpd/inc/mbtk_rtp_internal.h b/mbtk/mbtk_rtpd/inc/mbtk_rtp_internal.h
new file mode 100755
index 0000000..70995dc
--- /dev/null
+++ b/mbtk/mbtk_rtpd/inc/mbtk_rtp_internal.h
@@ -0,0 +1,123 @@
+/*
+* mbtk_rtp.h
+*
+* MBTK RTP(VOIP) internal header file.
+*
+* Author : lb
+* Date   : 2024/12/2 14:14:06
+*/
+#ifndef _MBTK_RTP_INTERNAL_H
+#define _MBTK_RTP_INTERNAL_H
+#include "mbtk_type.h"
+#include "mbtk_audio2.h"
+#include "mbtk_ril_api.h"
+#include "mbtk_loop_buffer.h"
+
+#define MBTK_IND_START_FLAG     0xFF
+#define MBTK_IND_END_FLAG       0xEE
+
+#define RTP_UDP_SER_PORT_DEFAULT 53248
+#define RTP_UDP_CLI_PORT_DEFAULT 55555
+#define RTP_IPC_SOCK_PATH "/tmp/mbtk_rtp_sock"
+
+#define RTP_SAMPLE_NUMBER 2     // 2 Byte for every sample.
+#define RTP_DEFAULT_MTU 1280
+#define MAX_IOVECS 16
+
+typedef void (*rtp_socket_read_cb_func)(int fd);
+
+typedef enum {
+    RTP_STATE_DISABLE = 0,      // RTP disable,do nothing. Only wait audio mode change.
+    RTP_STATE_ENABLE,           // RTP enable,but not on call.
+    RTP_STATE_VOIP_PROCESS      // RTP enable and on call.
+} rtp_state_enum;
+
+typedef enum {
+    RTP_UDP_SER_STATE_IDEL,
+    RTP_UDP_SER_STATE_STARTING,
+    RTP_UDP_SER_STATE_RUNNING,
+    RTP_UDP_SER_STATE_STOPING
+} rtp_udp_ser_state_enum;
+
+typedef enum {
+    RTP_VOIP_SER_STATE_IDEL,
+    RTP_VOIP_SER_STATE_STARTING,
+    RTP_VOIP_SER_STATE_RUNNING,
+    RTP_VOIP_SER_STATE_STOPING
+} rtp_voip_ser_state_enum;
+
+typedef struct {
+    rtp_state_enum rtp_state_pre;
+    rtp_state_enum rtp_state_cur;
+    int volume;
+    char remote_ip[20];
+    int server_port;    // Must be 53248 (remote port)
+    int client_port;     // local port
+
+    char vlan[32];
+    mbtk_audio_sample_rate_enum sample_rate;
+    int channel;    // Only support : 1
+} rtp_config_t;
+
+typedef struct {
+    uint32 version;     // 2 (2 bit)
+    uint32 padding;     // 0 (1 bit)
+    uint32 extension;   // 0 (1 bit)
+    uint32 csrc_count;        // 1 (4 bit)
+
+    uint32 marker;      // 0 (1 bit)
+    uint32 payload_type; // 0x60 (7 bit)
+
+    uint32 sequence;    // (16 bit)
+
+    uint32 timestamp;   // (32 bit)
+
+    uint32 ssrc;        // 0xFFFF0000 (32 bit)
+
+    uint32 csrc;        // 0xFFFF0000 (32 bit)
+} rtp_header_info_t;
+
+typedef struct {
+    int fd;
+    rtp_socket_read_cb_func read_cb;
+} rtp_socket_info_t;
+
+typedef struct {
+    uint32 mtu;
+
+    uint32 pack_size;
+    struct iovec iov[MAX_IOVECS];
+    int iov_idx;
+
+    uint8 remain_buff[RTP_DEFAULT_MTU];
+    uint32 remain_buff_len;
+} rtp_udp_send_info_t;
+
+typedef struct {
+    bool first_packet;
+    uint32_t offset;
+
+    mbtk_loop_buff_handle *recv_buff;
+} rtp_udp_recv_info_t;
+
+typedef struct {
+    mbtk_ril_handle* ril_handle;
+    int epoll_fd;
+
+    rtp_socket_info_t unix_sock_cli;
+
+    rtp_socket_info_t udp_recv_sock;  // Server
+    rtp_socket_info_t udp_send_sock;  // Client
+
+    uint32 frame_size;
+    uint32 playback_size;
+    uint32 sample_for_ms;
+
+    rtp_udp_send_info_t send;
+    rtp_udp_recv_info_t recv;
+
+    pthread_cond_t cond;
+    pthread_mutex_t mutex;
+} rtp_info_t;
+
+#endif /* _MBTK_RTP_INTERNAL_H */
diff --git a/mbtk/mbtk_rtpd/src/main.c b/mbtk/mbtk_rtpd/src/main.c
new file mode 100755
index 0000000..a0219fa
--- /dev/null
+++ b/mbtk/mbtk_rtpd/src/main.c
@@ -0,0 +1,522 @@
+/*
+*    main.c
+*
+*    RTP server main source.
+*
+*/
+/******************************************************************************
+
+                          EDIT HISTORY FOR FILE
+
+  WHEN        WHO       WHAT,WHERE,WHY
+--------    --------    -------------------------------------------------------
+2024/11/30     LiuBin      Initial version
+
+******************************************************************************/
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <sys/epoll.h>
+
+#include "mbtk_rtp_internal.h"
+#include "mbtk_log.h"
+#include "mbtk_utils.h"
+
+#define SOCK_CLIENT_MAX 5
+#define EPOLL_LISTEN_MAX 100
+
+rtp_info_t rtp_info;
+static rtp_config_t rtp_confs =
+{
+    .rtp_state_pre = RTP_STATE_DISABLE,
+    .rtp_state_cur = RTP_STATE_DISABLE,
+    .volume = 7,
+    .remote_ip = "198.18.38.15",        // 198.18.38.15
+    .server_port = RTP_UDP_SER_PORT_DEFAULT,
+    .client_port = RTP_UDP_CLI_PORT_DEFAULT,
+    .vlan = {0},
+    .sample_rate = MBTK_AUDIO_SAMPLE_RATE_8000,
+    .channel = 1
+};
+
+int rtp_udp_server_start(rtp_config_t *conf_info);
+int rtp_udp_server_stop();
+int rtp_voip_server_start(const rtp_config_t *conf_info);
+int rtp_voip_server_stop();
+
+static void rtp_main_thread_wait(const char* tag)
+{
+    LOGD("main(%s) waitting...", tag);
+    pthread_mutex_lock(&rtp_info.mutex);
+    pthread_cond_wait(&rtp_info.cond, &rtp_info.mutex);
+    pthread_mutex_unlock(&rtp_info.mutex);
+    LOGD("main(%s) running...", tag);
+}
+
+static void rtp_main_thread_cond()
+{
+    pthread_mutex_lock(&rtp_info.mutex);
+    pthread_cond_signal(&rtp_info.cond);
+    pthread_mutex_unlock(&rtp_info.mutex);
+}
+
+static void rtp_msg_process(int fd, const char *msg, int msg_len)
+{
+    LOGD("CMD <%s> <len-%d>", msg, msg_len);
+    // gnss_init:x
+    usleep(10 * 1000); // sleep 10ms
+    /*
+        volume = 7,
+        .remote_ip = "127.0.0.1",        // 198.18.38.15
+        .server_port = RTP_UDP_SER_PORT_DEFAULT,
+        .client_port = RTP_UDP_CLI_PORT_DEFAULT,
+        .vlan = {0},
+        .sample_rate = MBTK_AUDIO_SAMPLE_RATE_8000,
+        .channel = 1
+    */
+    if(memcmp(msg, "rtp_mode", 8) == 0) { // rtp_mode <0/1>
+        int rtp_mode = atoi(msg + 9);
+        int ret = 0;
+        if(rtp_mode == 0) { // Disable RTP
+            if(rtp_confs.rtp_state_cur == RTP_STATE_ENABLE) {
+                rtp_confs.rtp_state_pre = rtp_confs.rtp_state_cur;
+                rtp_confs.rtp_state_cur = RTP_STATE_DISABLE;
+                rtp_main_thread_cond();
+            } else if(rtp_confs.rtp_state_cur == RTP_STATE_VOIP_PROCESS) {
+                rtp_confs.rtp_state_pre = rtp_confs.rtp_state_cur;
+                rtp_confs.rtp_state_cur = RTP_STATE_DISABLE;
+                // rtp_main_thread_cond();
+            }
+        } else {
+            if(rtp_confs.rtp_state_cur == RTP_STATE_DISABLE) {
+                rtp_confs.rtp_state_pre = rtp_confs.rtp_state_cur;
+                rtp_confs.rtp_state_cur = RTP_STATE_ENABLE;
+                rtp_main_thread_cond();
+            }
+        }
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%crtp_mode:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        mbtk_write(fd, rsp, strlen(rsp));
+    } else if(memcmp(msg, "volume", 6) == 0) {// volume <0-7>
+        int volume = atoi(msg + 7);
+        int ret = -1;
+        if(volume >= 0 && volume <= 7) {
+            rtp_confs.volume = volume;
+            ret = 0;
+        }
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%cvolume:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        mbtk_write(fd, rsp, strlen(rsp));
+    } else if(memcmp(msg, "remote_ip", 9) == 0) {// remote_ip <xxx:xxx:xxx:xxx>
+        int ret = 0;
+        memcpy(rtp_confs.remote_ip, msg + 10, strlen(msg + 10) + 1);
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%cremote_ip:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        mbtk_write(fd, rsp, strlen(rsp));
+    } else if(memcmp(msg, "server_port", 11) == 0) {// client_port <port>
+        int port = atoi(msg + 12);
+        int ret = -1;
+        if(port >= 0 && port <= 7) {
+            rtp_confs.server_port = port;
+            ret = 0;
+        }
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%cserver_port:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        mbtk_write(fd, rsp, strlen(rsp));
+    } else if(memcmp(msg, "client_port", 11) == 0) {// client_port <port>
+        int port = atoi(msg + 12);
+        int ret = -1;
+        if(port > 1024 && port < 65535) {
+            rtp_confs.client_port = port;
+            ret = 0;
+        }
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%cclient_port:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        mbtk_write(fd, rsp, strlen(rsp));
+    } else if(memcmp(msg, "sample_rate", 11) == 0) {// client_port <port>
+        int sample_rate = atoi(msg + 12);
+        int ret = 0;
+        if(sample_rate == 8000) {
+            rtp_confs.sample_rate = MBTK_AUDIO_SAMPLE_RATE_8000;
+        } else if(sample_rate == 16000) {
+            rtp_confs.sample_rate = MBTK_AUDIO_SAMPLE_RATE_16000;
+        } else {
+            ret = -1;
+        }
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%csample_rate:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        mbtk_write(fd, rsp, strlen(rsp));
+    } else if(memcmp(msg, "channel", 7) == 0) {// client_port <port>
+        int channel = atoi(msg + 8);
+        int ret = -1;
+        if(channel == 1) {
+            rtp_confs.channel = channel;
+            ret = 0;
+        }
+
+        char rsp[100] = {0};
+        sprintf(rsp, "%cchannel:%d%c", MBTK_IND_START_FLAG, ret, MBTK_IND_END_FLAG);
+        mbtk_write(fd, rsp, strlen(rsp));
+    } else {
+        LOGW("Unknown RTP msg : %s", msg);
+    }
+}
+
+int epoll_fd_add(int fd)
+{
+    if(rtp_info.epoll_fd > 0) {
+        struct epoll_event ev;
+        memset(&ev, 0, sizeof(struct epoll_event));
+        ev.data.fd = fd;
+        ev.events = EPOLLIN | EPOLLET;
+        return epoll_ctl(rtp_info.epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+    } else {
+        return -1;
+    }
+}
+
+int epoll_fd_del(int fd)
+{
+    if(rtp_info.epoll_fd > 0) {
+        struct epoll_event ev;
+        memset(&ev, 0, sizeof(struct epoll_event));
+        ev.data.fd = fd;
+        ev.events = EPOLLIN | EPOLLERR | EPOLLET;
+        return epoll_ctl(rtp_info.epoll_fd, EPOLL_CTL_DEL, fd, &ev);
+    } else {
+        return -1;
+    }
+}
+
+
+static void* rtp_ipc_ser_pthread(void* arg)
+{
+    int sock_listen_fd = *((int*)arg);
+    rtp_info.epoll_fd = epoll_create(SOCK_CLIENT_MAX + 1);
+    if(rtp_info.epoll_fd < 0)
+    {
+        LOGE("epoll_create() fail[%d].", errno);
+        return NULL;
+    }
+
+    rtp_info.unix_sock_cli.fd = -1;
+    rtp_info.unix_sock_cli.read_cb = NULL;
+
+    epoll_fd_add(sock_listen_fd);
+
+    int nready = -1;
+    int i = 0;
+    struct epoll_event epoll_events[EPOLL_LISTEN_MAX];
+    while(1)
+    {
+        nready = epoll_wait(rtp_info.epoll_fd, epoll_events, EPOLL_LISTEN_MAX, -1);
+        if(nready > 0)
+        {
+            for(i = 0; i < nready; i++)
+            {
+                LOGV("fd[%d] event = %x",epoll_events[i].data.fd, epoll_events[i].events);
+                if(epoll_events[i].events & EPOLLHUP)   // Client Close.
+                {
+                    epoll_fd_del(epoll_events[i].data.fd);
+                    close(epoll_events[i].data.fd);
+                    if(rtp_info.unix_sock_cli.fd == epoll_events[i].data.fd) {
+                        rtp_info.unix_sock_cli.fd = -1;
+                        LOGD("Local unix socket client close.");
+                    } else if(rtp_info.udp_recv_sock.fd == epoll_events[i].data.fd) {
+                        rtp_info.udp_recv_sock.fd = -1;
+                        LOGD("RTP UDP socket client close.");
+                    } else {
+                        LOGE("Can not occur.");
+                    }
+                }
+                else if(epoll_events[i].events & EPOLLIN)
+                {
+                    if(epoll_events[i].data.fd == sock_listen_fd)   // New clients connected.
+                    {
+                        int client_fd = -1;
+                        while(1)
+                        {
+                            struct sockaddr_in cliaddr;
+                            socklen_t clilen = sizeof(cliaddr);
+                            client_fd = accept(epoll_events[i].data.fd, (struct sockaddr *) &cliaddr, &clilen);
+                            if(client_fd <= 0)
+                            {
+                                if(errno == EAGAIN)
+                                {
+                                    LOGE("All client connect get.");
+                                }
+                                else
+                                {
+                                    LOGE("accept() error[%d].", errno);
+                                }
+                                break;
+                            } else {
+                                if(rtp_info.unix_sock_cli.fd > 0) {
+                                    LOGE("Client is full.");
+                                    break;
+                                }
+                                rtp_info.unix_sock_cli.fd = client_fd;
+                                rtp_info.unix_sock_cli.read_cb = NULL;
+                            }
+
+                            epoll_fd_add(client_fd);
+
+                            LOGD("Start monitor client cmd : %d", client_fd);
+                        }
+                    }
+                    else if(rtp_info.unix_sock_cli.fd == epoll_events[i].data.fd)    // Client data arrive.
+                    {
+                        char buff[1024] = {0};
+                        int len = read(epoll_events[i].data.fd, buff, sizeof(buff));
+                        if(len > 0) {
+                            rtp_msg_process(epoll_events[i].data.fd, buff, len);
+                        }
+                    }
+                    else if(rtp_info.udp_recv_sock.fd == epoll_events[i].data.fd)    // RTP UDP data reach.
+                    {
+                        if(rtp_info.udp_recv_sock.read_cb) {
+                            rtp_info.udp_recv_sock.read_cb(epoll_events[i].data.fd);
+                        }
+                    }
+                    else
+                    {
+                        LOGE("Unknown socket : %d", epoll_events[i].data.fd);
+                    }
+                }
+                else
+                {
+                    LOGE("Unknown event : %x", epoll_events[i].events);
+                }
+            }
+        }
+        else
+        {
+            LOGE("epoll_wait() fail[%d].", errno);
+        }
+    }
+
+    return NULL;
+}
+
+
+static int rtp_ipc_server_start()
+{
+    struct sockaddr_un server_addr;
+    int sock_listen_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if(sock_listen_fd < 0)
+    {
+        LOGE("socket() fail[%d].", errno);
+        return -1;
+    }
+
+#if 1
+    // Set O_NONBLOCK
+    int flags = fcntl(sock_listen_fd, F_GETFL, 0);
+    if (flags < 0)
+    {
+        LOGE("Get flags error:%d", errno);
+        goto error;
+    }
+    flags |= O_NONBLOCK;
+    if (fcntl(sock_listen_fd, F_SETFL, flags) < 0)
+    {
+        LOGE("Set flags error:%d", errno);
+        goto error;
+    }
+#endif
+
+    unlink(RTP_IPC_SOCK_PATH);
+    memset(&server_addr, 0, sizeof(struct sockaddr_un));
+    server_addr.sun_family = AF_LOCAL;
+    strcpy(server_addr.sun_path, RTP_IPC_SOCK_PATH);
+    if(bind(sock_listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)))
+    {
+        LOGE("bind() fail[%d].", errno);
+        goto error;
+    }
+
+    if(listen(sock_listen_fd, SOCK_CLIENT_MAX))
+    {
+        LOGE("listen() fail[%d].", errno);
+        goto error;
+    }
+
+    pthread_t pid;
+    pthread_attr_t thread_attr;
+    pthread_attr_init(&thread_attr);
+    if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
+    {
+        LOGE("pthread_attr_setdetachstate() fail.");
+        goto error;
+    }
+
+    if(pthread_create(&pid, &thread_attr, rtp_ipc_ser_pthread, &sock_listen_fd))
+    {
+        LOGE("pthread_create() fail.");
+        goto error;
+    }
+
+    LOGD("RTP IPC service is running...");
+    return 0;
+error:
+    close(sock_listen_fd);
+    return -1;
+}
+
+static void call_state_change_cb(const void* data, int data_len)
+{
+    if(data) {
+        mbtk_ril_call_state_info_t *state = (mbtk_ril_call_state_info_t*)data;
+        LOGD("call state change : call_id-%d, dir-%d, state-%d, num_type-%d,number-%s", state->call_id,
+            state->dir, state->state, state->num_type, state->call_number);
+        if(state->state == MBTK_RIL_CALL_STATE_DISCONNECT) {
+            if(rtp_confs.rtp_state_cur == RTP_STATE_VOIP_PROCESS) {
+                rtp_confs.rtp_state_pre = rtp_confs.rtp_state_cur;
+                rtp_confs.rtp_state_cur = RTP_STATE_ENABLE;
+                rtp_main_thread_cond();
+            } else if(rtp_confs.rtp_state_cur == RTP_STATE_DISABLE) { // 通话过程中 Disable,挂断后需要单独处理
+                rtp_confs.rtp_state_pre = RTP_STATE_VOIP_PROCESS;
+                rtp_confs.rtp_state_cur = RTP_STATE_DISABLE;
+                rtp_main_thread_cond();
+            }
+        } else if(state->state == MBTK_RIL_CALL_STATE_ACTIVE /*state->state == MBTK_RIL_CALL_STATE_ALERTING || state->state == MBTK_RIL_CALL_STATE_INCOMING*/) {
+            if(rtp_confs.rtp_state_cur == RTP_STATE_ENABLE) {
+                rtp_confs.rtp_state_pre = rtp_confs.rtp_state_cur;
+                rtp_confs.rtp_state_cur = RTP_STATE_VOIP_PROCESS;
+                rtp_main_thread_cond();
+            }
+        }
+    }
+}
+
+static int ril_ser_switch(bool open)
+{
+    if(open) {
+        if(rtp_info.ril_handle) {
+            LOGW("RIL has opened.");
+            return 0;
+        }
+        rtp_info.ril_handle = mbtk_ril_open(MBTK_AT_PORT_DEF);
+        if(rtp_info.ril_handle == NULL) {
+            LOGE("mbtk_ril_open(MBTK_AT_PORT_DEF) fail.");
+            return -1;
+        }
+
+        mbtk_call_state_change_cb_reg(call_state_change_cb);
+    } else {
+        if(!rtp_info.ril_handle) {
+            LOGW("RIL not open.");
+            return 0;
+        }
+
+        if(MBTK_RIL_ERR_SUCCESS != mbtk_ril_close(MBTK_AT_PORT_DEF)) {
+            LOGE("mbtk_ril_close(MBTK_AT_PORT_DEF) fail.");
+            return -1;
+        }
+
+        rtp_info.ril_handle = NULL;
+    }
+    return 0;
+}
+
+static int rtp_start(rtp_state_enum state_pre, rtp_state_enum state_cur)
+{
+    LOGD("RTP start, state : %d -> %d", state_pre, state_cur);
+    char *tag = NULL;
+    if(state_cur == RTP_STATE_DISABLE) {
+        if(state_pre == RTP_STATE_VOIP_PROCESS || state_pre == RTP_STATE_ENABLE) {
+            // Close ril server.
+            ril_ser_switch(FALSE);
+
+            // Close RTP UDP forward server.
+            if(rtp_voip_server_stop()) {
+                LOGE("rtp_voip_server_stop() fail.");
+            }
+
+            if(rtp_udp_server_stop()) {
+                LOGE("rtp_udp_server_stop() fail.");
+            }
+        } else {
+            LOGW("Can not occur[Except for the first time].");
+        }
+
+        tag = "RTP_STATE_DISABLE";
+    } else if(state_cur == RTP_STATE_ENABLE) {
+        if(state_pre == RTP_STATE_VOIP_PROCESS) {
+            // Close RTP UDP forward server.
+            if(rtp_voip_server_stop()) {
+                LOGE("rtp_udp_server_stop() fail.");
+            }
+        } else if(state_pre == RTP_STATE_DISABLE) {
+            // Open ril server.
+            ril_ser_switch(TRUE);
+
+            if(rtp_udp_server_start(&rtp_confs)) {
+                LOGE("rtp_udp_server_start() fail.");
+            }
+        } else {
+            LOGW("Can not occur.");
+        }
+
+        tag = "RTP_STATE_ENABLE";
+    } else if(state_cur == RTP_STATE_VOIP_PROCESS) {
+        if(state_pre == RTP_STATE_DISABLE) {
+            LOGW("Can not occur.");
+        } else if(state_pre == RTP_STATE_ENABLE) {
+            // Open RTP UDP forward server.
+            if(rtp_voip_server_start(&rtp_confs)) {
+                LOGE("rtp_voip_server_start() fail.");
+            }
+        } else {
+            LOGW("Can not occur.");
+        }
+
+        tag = "RTP_STATE_VOIP_PROCESS";
+    } else {
+        LOGE("Unknown state : %d", state_cur);
+        return -1;
+    }
+
+    // Wait for state change.
+    rtp_main_thread_wait(tag);
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    mbtk_log_init("radio", "MBTK_RTP");
+
+    LOGD("mbtk_rtpd start.");
+
+    memset(&rtp_info, 0, sizeof(rtp_info_t));
+    pthread_mutex_init(&rtp_info.mutex, NULL);
+    pthread_cond_init(&rtp_info.cond, NULL);
+
+    // Start server to monitor client messages.
+    if(rtp_ipc_server_start()) {
+        LOGE("rtp_ipc_server_start() fail.");
+        return -1;
+    }
+
+    while(!rtp_start(rtp_confs.rtp_state_pre, rtp_confs.rtp_state_cur))
+    {
+        LOGD("RTP will restart with state %d -> %d", rtp_confs.rtp_state_pre, rtp_confs.rtp_state_cur);
+    }
+
+    LOGE("RTP exit. rtp_start() fail.");
+
+    return 0;
+}
+
diff --git a/mbtk/mbtk_rtpd/src/rtp_server.c b/mbtk/mbtk_rtpd/src/rtp_server.c
new file mode 100755
index 0000000..4ae8a74
--- /dev/null
+++ b/mbtk/mbtk_rtpd/src/rtp_server.c
@@ -0,0 +1,828 @@
+/*
+*    rtp_server.c
+*
+*    RTP transport service.
+*
+*                  -------------------------
+*                 |                         |
+*    ------  UDP  |                -------  |
+*   |      | -----|-> playback -> |       | |
+*   | MCU  |      |               | Voice | |
+*   |      |  UDP |               |       | |
+*    ------  <----|-- record <--- |       | |
+*                 |                -------  |
+*                  -------------------------
+*/
+/******************************************************************************
+
+                          EDIT HISTORY FOR FILE
+
+  WHEN        WHO       WHAT,WHERE,WHY
+--------    --------    -------------------------------------------------------
+2024/12/3     LiuBin      Initial version
+
+******************************************************************************/
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <sys/epoll.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "mbtk_log.h"
+#include "mbtk_utils.h"
+#include "mbtk_audio2.h"
+#include "mbtk_rtp_internal.h"
+
+#define RTP_DEBUG_PCM_FILE 1
+#define RTP_UDP_READ_BUFF_SIZE 2048
+
+static rtp_udp_ser_state_enum udp_state = RTP_UDP_SER_STATE_IDEL;
+static rtp_voip_ser_state_enum voip_state = RTP_VOIP_SER_STATE_IDEL;
+extern rtp_info_t rtp_info;
+static pthread_t voip_playback;
+
+static rtp_header_info_t rtp_head = {
+    .version = 2,     // 2 (2 bit)
+    .padding = 0,     // 0 (1 bit)
+    .extension = 0,   // 0 (1 bit)
+    .csrc = 1,        // 1 (4 bit)
+    .marker = 0,      // 0 (1 bit)
+    .payload_type = 0x60, // 0x60 (7 bit)
+    .sequence = 0,    // (16 bit)
+    .timestamp = 0,   // (32 bit)
+    .ssrc = 0xFFFF0000,    // 0xFFFF0000 (32 bit)
+    .csrc = 0xFFFF0000     // 0xFFFF0000 (32 bit)
+};
+
+int epoll_fd_add(int fd);
+int epoll_fd_del(int fd);
+
+static void *mbtk_memcpy(const void *src, unsigned int n)
+{
+    void *dest = malloc(n);
+    if(dest) {
+        return memcpy(dest, src, n);
+    } else {
+        return NULL;
+    }
+}
+
+static void rtp_send_init()
+{
+    rtp_info.send.iov_idx = 1;      // First is RTP header.
+    rtp_info.send.pack_size = 0;
+    rtp_info.send.remain_buff_len = 0;
+    rtp_info.recv.first_packet = TRUE;
+
+    if(rtp_info.recv.recv_buff == NULL)
+        rtp_info.recv.recv_buff = mbtk_loopbuff_get(1024 * 1024);
+
+    rtp_head.sequence = (uint32) (rand()*rand());
+    rtp_head.timestamp = 0;
+}
+
+static int rtp_pack_push(const uint8 *data, uint32 data_len, uint32_t timestamp)
+{
+    UNUSED(data);
+    UNUSED(data_len);
+    UNUSED(timestamp);
+
+    if(rtp_info.recv.recv_buff) {
+        if(data_len != mbtk_loopbuff_write(rtp_info.recv.recv_buff, data, data_len))
+            return -1;
+    }
+    return 0;
+}
+
+static void udp_read_cb(int fd)
+{
+    int size;
+    size_t audio_length;
+    size_t metadata_length;
+    struct msghdr m;
+    //struct cmsghdr *cm;
+    struct iovec iov;
+    uint32_t header;
+    uint32_t ssrc;
+    uint8_t payload;
+    unsigned cc;
+    ssize_t r;
+    uint8_t aux[1024];
+    //bool found_tstamp = false;
+    uint32_t timestamp;
+    int64_t k, j, delta;
+
+    uint8_t recv_buf[RTP_UDP_READ_BUFF_SIZE];
+    //size_t recv_buf_size = 0;
+
+    if (ioctl(fd, FIONREAD, &size) < 0) {
+        LOGE("FIONREAD failed: %d", errno);
+        goto fail;
+    }
+
+    if (size <= 0) {
+        /* size can be 0 due to any of the following reasons:
+         *
+         * 1. Somebody sent us a perfectly valid zero-length UDP packet.
+         * 2. Somebody sent us a UDP packet with a bad CRC.
+         *
+         * It is unknown whether size can actually be less than zero.
+         *
+         * In the first case, the packet has to be read out, otherwise the
+         * kernel will tell us again and again about it, thus preventing
+         * reception of any further packets. So let's just read it out
+         * now and discard it later, when comparing the number of bytes
+         * received (0) with the number of bytes wanted (1, see below).
+         *
+         * In the second case, recvmsg() will fail, thus allowing us to
+         * return the error.
+         *
+         * Just to avoid passing zero-sized memchunks and NULL pointers to
+         * recvmsg(), let's force allocation of at least one byte by setting
+         * size to 1.
+         */
+        size = 1;
+    }
+
+    iov.iov_base = recv_buf;
+    iov.iov_len = (size_t) size;
+
+    m.msg_name = NULL;
+    m.msg_namelen = 0;
+    m.msg_iov = &iov;
+    m.msg_iovlen = 1;
+    m.msg_control = aux;
+    m.msg_controllen = sizeof(aux);
+    m.msg_flags = 0;
+
+    r = recvmsg(fd, &m, 0);
+
+    if (r != size) {
+        if (r < 0 && errno != EAGAIN && errno != EINTR)
+            LOGE("recvmsg() failed: %s", r < 0 ? strerror(errno) : "size mismatch");
+
+        goto fail;
+    }
+
+    if(voip_state != RTP_VOIP_SER_STATE_RUNNING) {
+        return;
+    }
+
+#if 0
+    printf("RECV : %d\n", r);
+
+    int send_len = mbtk_audio_pcm_play_data_send(recv_buf, r);
+    if(r != send_len) {
+        printf("play_data_send fail: %d/%d\n", send_len, r);
+    }
+#endif
+    if (size < 12) {
+        LOGE("RTP packet too short.");
+        goto fail;
+    }
+
+#if 1
+    memcpy(&header, iov.iov_base, sizeof(uint32_t));
+    memcpy(&timestamp, (uint8_t*) iov.iov_base + 4, sizeof(uint32_t));
+    memcpy(&ssrc, (uint8_t*) iov.iov_base + 8, sizeof(uint32_t));
+
+    header = ntohl(header);
+    timestamp = ntohl(timestamp);
+    ssrc = ntohl(ssrc);
+
+    if ((header >> 30) != 2) {
+        LOGE("Unsupported RTP version.");
+        goto fail;
+    }
+
+    if ((header >> 29) & 1) {
+        LOGE("RTP padding not supported.");
+        goto fail;
+    }
+
+    if ((header >> 28) & 1) {
+        LOGE("RTP header extensions not supported.");
+        goto fail;
+    }
+
+    if (ssrc != rtp_head.ssrc) {
+        LOGE("Got unexpected SSRC");
+        goto fail;
+    }
+
+    cc = (header >> 24) & 0xF;
+    payload = (uint8_t) ((header >> 16) & 127U);
+    rtp_head.sequence = (uint16_t) (header & 0xFFFFU);
+
+    metadata_length = 12 + cc * 4;
+
+    if (payload != rtp_head.payload_type) {
+        LOGE("Got unexpected payload: %u", payload);
+        goto fail;
+    }
+
+    if (metadata_length > (unsigned) size) {
+        LOGE("RTP packet too short. (CSRC)");
+        goto fail;
+    }
+
+    audio_length = size - metadata_length;
+
+    if (audio_length % rtp_info.frame_size != 0) {
+        LOGE("Bad RTP packet size.");
+        goto fail;
+    }
+
+    if (rtp_info.recv.first_packet) {
+        rtp_info.recv.first_packet = FALSE;
+        rtp_info.recv.offset = timestamp;
+    }
+
+    /* Check whether there was a timestamp overflow */
+    k = (int64_t) timestamp - (int64_t) rtp_info.recv.offset;
+    j = (int64_t) 0x100000000LL - (int64_t) rtp_info.recv.offset + (int64_t) timestamp;
+
+    if ((k < 0 ? -k : k) < (j < 0 ? -j : j))
+        delta = k;
+    else
+        delta = j;
+
+    LOGD("RECV : %d, delta = %d", audio_length, delta);
+//     pa_memblockq_seek(s->memblockq, delta * (int64_t) rtp_info.frame_size, PA_SEEK_RELATIVE, true);
+
+    /* The next timestamp we expect */
+    rtp_info.recv.offset = timestamp + (uint32_t) (audio_length / rtp_info.frame_size);
+
+
+    if(rtp_pack_push(recv_buf + metadata_length, audio_length, timestamp)) {
+        LOGE("rtp_pack_push() fail.");
+    }
+
+#endif
+
+    return;
+
+fail:
+
+    return;
+}
+
+static void voip_recorder_cb(void *data, uint32 data_len)
+{
+    if(data_len > 0) {
+        LOGD("Record : %d", data_len);
+        if(rtp_info.udp_send_sock.fd > 0) {
+#if 0
+            int len = sendto(rtp_info.udp_send_sock.fd, data, data_len, 0, NULL, 0);
+            printf("SEND : %d / %d\n", len, data_len);
+#else
+            // 有剩余数据
+            if(rtp_info.send.iov_idx == 1 && rtp_info.send.remain_buff_len > 0) {
+                rtp_info.send.iov[rtp_info.send.iov_idx].iov_base = mbtk_memcpy(rtp_info.send.remain_buff,
+                            rtp_info.send.remain_buff_len);
+                rtp_info.send.iov[rtp_info.send.iov_idx].iov_len = rtp_info.send.remain_buff_len;
+                rtp_info.send.iov_idx++;
+
+                rtp_info.send.pack_size += rtp_info.send.remain_buff_len;
+                rtp_info.send.remain_buff_len = 0;
+            }
+
+            // UDP各分包总大小不超过 c->mtu (           默认: DEFAULT_MTU )
+            // k 为分包大小
+            uint32 k = rtp_info.send.pack_size + data_len > rtp_info.send.mtu ?
+                            rtp_info.send.mtu - rtp_info.send.pack_size : data_len;
+
+            rtp_info.send.iov[rtp_info.send.iov_idx].iov_base = mbtk_memcpy(data, k);
+            rtp_info.send.iov[rtp_info.send.iov_idx].iov_len = k;
+            rtp_info.send.iov_idx++;
+
+            rtp_info.send.pack_size += k;
+
+            if(rtp_info.send.pack_size % rtp_info.frame_size != 0) {
+                LOGW("pack size error: %d - %d", rtp_info.send.pack_size, rtp_info.frame_size);
+                return;
+            }
+
+            if (rtp_info.send.pack_size >= rtp_info.send.mtu || rtp_info.send.iov_idx >= MAX_IOVECS) {
+                uint32_t header[4];
+                struct msghdr m;
+                ssize_t k;
+                int i;
+
+                header[0] = htonl(((uint32_t) rtp_head.version << 30) | ((uint32_t) rtp_head.csrc_count << 24) | ((uint32_t) rtp_head.payload_type << 16) | ((uint32_t) rtp_head.sequence));
+                header[1] = htonl(rtp_head.timestamp);
+                header[2] = htonl(rtp_head.ssrc);
+                header[3] = htonl(rtp_head.csrc);
+
+                rtp_info.send.iov[0].iov_base = (void*)header;
+                rtp_info.send.iov[0].iov_len = sizeof(header);
+
+                m.msg_name = NULL;
+                m.msg_namelen = 0;
+                m.msg_iov = rtp_info.send.iov;
+                m.msg_iovlen = (size_t) rtp_info.send.iov_idx;
+                m.msg_control = NULL;
+                m.msg_controllen = 0;
+                m.msg_flags = 0;
+
+                k = sendmsg(rtp_info.udp_send_sock.fd, &m, MSG_DONTWAIT);
+
+                for (i = 1; i < rtp_info.send.iov_idx; i++) {
+                    free(rtp_info.send.iov[i].iov_base);
+                    rtp_info.send.iov[i].iov_base = NULL;
+                }
+
+                rtp_head.sequence++;
+
+                // 时间单位转为帧数(每帧多少us)
+                rtp_head.timestamp += (unsigned) (rtp_info.send.pack_size / rtp_info.frame_size);
+
+                if (k < 0) {
+                    if (errno != EAGAIN && errno != EINTR) /* If the queue is full, just ignore it */
+                        LOGE("sendmsg() failed: %s", strerror(errno));
+                    return;
+                }
+
+                rtp_info.send.pack_size = 0;
+                rtp_info.send.iov_idx = 1;
+            }
+#endif
+        }
+    } else {
+        LOGD("Recorver data end.");
+    }
+}
+
+static int64 time_us_get()
+{
+    struct timespec ts;
+    memset(&ts, 0, sizeof(struct timespec));
+
+    if(clock_gettime(CLOCK_REALTIME, &ts)) {
+        LOGE("clock_gettime() fail:%d", errno);
+        return -1;
+    }
+
+    return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+}
+
+static void voip_playback_thread(void *arg)
+{
+    UNUSED(arg);
+    char *buff = (char*)malloc(rtp_info.playback_size * 2);
+    if(buff == NULL) {
+        LOGE("malloc() fail.");
+        return;
+    }
+
+    if(mbtk_audio_voice_pcm_playback_start()) {
+        LOGE("mbtk_audio_voice_pcm_playback_start() fail.");
+        return;
+    }
+
+    usleep(100000);
+
+    int64 play_start = time_us_get();   // us
+    //uint64 play_count = 0;
+    //char num_buff[1024] = {0};
+    while (voip_state == RTP_VOIP_SER_STATE_RUNNING) {
+        int len;
+        if((len = mbtk_loopbuff_read(rtp_info.recv.recv_buff, buff, rtp_info.playback_size * 2)) > 0) {
+            int send_len = mbtk_audio_pcm_play_data_send(buff, len);
+            if(send_len > 0) {
+                if(len != send_len) {
+                    LOGW("play_data_send fail: %d/%d\n", send_len, len);
+                }
+                //play_count += send_len;
+                int64 play_now = time_us_get();
+                int time_offset = (int)((play_now - play_start) / 1000 * rtp_info.sample_for_ms);
+                LOGD("loopbuff:%d, time_offset:%d, send_len:%d", mbtk_loopbuff_size(rtp_info.recv.recv_buff), time_offset,
+                    send_len / 2);
+#if 0
+                int offset = send_len / 2 - time_offset;
+                if(offset < 0 && len > -offset * 2) {
+#if 0
+                    if(mbtk_loopbuff_seek(rtp_info.recv.recv_buff, offset * 2)) {
+                        LOGE("mbtk_loopbuff_seek() fail.");
+                    }
+                    mbtk_loopbuff_print(rtp_info.recv.recv_buff);
+#else
+                    mbtk_audio_pcm_play_data_send(buff + len + offset * 2, -offset * 2);
+#endif
+                }
+#endif
+                play_start = play_now;
+            }
+        }
+#if 0
+        else {
+            if(voip_state == RTP_VOIP_SER_STATE_RUNNING)
+                usleep(5000);
+        }
+        // LOGD("%s: No.%d frame playback.", __FUNCTION__, ++frames);
+
+        int time_offset = (int)((time_us_get() - play_start) / 1000 * rtp_info.sample_for_ms);
+        LOGD("loopbuff:%d, time_offset:%d, play_count:%d", mbtk_loopbuff_size(rtp_info.recv.recv_buff), time_offset,
+            play_count / 2);
+#endif
+    }
+
+    if(mbtk_audio_pcm_play_stop()) {
+        LOGE("mbtk_audio_pcm_play_stop() fail.");
+        return;
+    }
+
+    free(buff);
+    return;
+}
+
+
+static int udp_setsockopt(int fd)
+{
+    int priority = 6;
+    if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (const void *) &priority, sizeof(priority)) < 0) {
+        LOGE("setsockopt(SO_PRIORITY) fail:errno - %d", errno);
+        return -1;
+    }
+
+    int one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) {
+        LOGE("SO_TIMESTAMP failed: %d", errno);
+        return -1;
+    }
+
+    one = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
+        LOGE("SO_REUSEADDR failed: %d", errno);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int socket_noblock(int fd)
+{
+    // Set O_NONBLOCK
+    int flags = fcntl(fd, F_GETFL, 0);
+    if (flags < 0)
+    {
+        LOGE("Get flags error:%d", errno);
+        return -1;
+    }
+    flags |= O_NONBLOCK;
+    if (fcntl(fd, F_SETFL, flags) < 0)
+    {
+        LOGE("Set flags error:%d", errno);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int rtp_udp_ser_open(const char *local_addr, int local_port)
+{
+    // No set local addr.
+    UNUSED(local_addr);
+
+    int fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if(fd < 0){
+        LOGE("socket() fail.[%d]", errno);
+        return -1;
+    }
+
+    if(udp_setsockopt(fd)) {
+        goto result_fail_with_close;
+    }
+
+    struct sockaddr_in servaddr;
+    memset(&servaddr, 0, sizeof(servaddr));
+    servaddr.sin_family = AF_INET;
+    servaddr.sin_port = htons(local_port);
+    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    if (bind(fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) < 0) {
+        LOGE("bind() failed: %d", errno);
+        goto result_fail_with_close;
+    }
+
+    return fd;
+result_fail_with_close:
+    close(fd);
+    fd = -1;
+    LOGE("mbtk_sock_open() end:fail");
+    return -1;
+}
+
+static int rtp_udp_cli_open(const char *remote_addr, int remote_port)
+{
+    struct sockaddr_in dst_sa4, src_sa4;
+    if (inet_pton(AF_INET, "0.0.0.0", &src_sa4.sin_addr) > 0) {
+        src_sa4.sin_family = AF_INET;
+        src_sa4.sin_port = htons(0);
+        memset(&src_sa4.sin_zero, 0, sizeof(src_sa4.sin_zero));
+    } else {
+        LOGE("Set src addr fail.");
+        return -1;
+    }
+
+    if (inet_pton(AF_INET, remote_addr, &dst_sa4.sin_addr) > 0) {
+        dst_sa4.sin_family = AF_INET;
+        dst_sa4.sin_port = htons(remote_port);
+        memset(&dst_sa4.sin_zero, 0, sizeof(dst_sa4.sin_zero));
+    } else {
+        LOGE("Set dst addr fail.");
+        return -1;
+    }
+
+    int fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if(fd < 0){
+        LOGE("socket() fail.[%d]", errno);
+        return -1;
+    }
+
+    if (bind(fd, (struct sockaddr*) &src_sa4, sizeof(src_sa4)) < 0) {
+        LOGE("bind() failed: %d", errno);
+        goto result_fail_with_close;
+    }
+
+    if (connect(fd, (struct sockaddr*) &dst_sa4, sizeof(dst_sa4)) < 0) {
+        LOGE("connect() failed: %d", errno);
+        goto result_fail_with_close;
+    }
+
+    if(socket_noblock(fd)) {
+        goto result_fail_with_close;
+    }
+
+    if(udp_setsockopt(fd)) {
+        goto result_fail_with_close;
+    }
+
+    return fd;
+result_fail_with_close:
+    close(fd);
+    fd = -1;
+    LOGE("mbtk_sock_open() end:fail");
+    return -1;
+}
+
+
+/*===========================================================================
+FUNCTION rtp_udp_server_start
+
+DESCRIPTION:
+  Start RTP UDP server,will monitor rtp packet from MCU.
+
+PARAMETERS:
+  conf_info : RTP config informations.
+
+RETURN VALUE:
+  int : 0 for success, other for fail.
+
+===========================================================================*/
+int rtp_udp_server_start(rtp_config_t *conf_info)
+{
+    UNUSED(conf_info);
+    if(udp_state != RTP_UDP_SER_STATE_IDEL) {
+        LOGE("udp_state error : %d", udp_state);
+        return -1;
+    }
+
+    udp_state = RTP_UDP_SER_STATE_STARTING;
+    rtp_info.frame_size = conf_info->channel * RTP_SAMPLE_NUMBER;
+    rtp_info.send.mtu = (RTP_DEFAULT_MTU / rtp_info.frame_size) * rtp_info.frame_size;
+    if(conf_info->sample_rate == MBTK_AUDIO_SAMPLE_RATE_8000) {
+        rtp_info.playback_size = MBTK_PCM_NB_BUF_SIZE;
+        rtp_info.sample_for_ms = 8;
+    } else {
+        rtp_info.playback_size = MBTK_PCM_WB_BUF_SIZE;
+        rtp_info.sample_for_ms = 16;
+    }
+    LOGD("frame_size = %d, MTU = %d", rtp_info.frame_size, rtp_info.send.mtu);
+
+    // Open UDP server socket.
+    LOGD("Start open UDP server : NULL-%d", conf_info->client_port);
+    rtp_info.udp_recv_sock.fd = rtp_udp_ser_open(NULL, conf_info->client_port);
+    if(rtp_info.udp_recv_sock.fd < 0) {
+        LOGE("socket(udp_recv_sock) fail : errno = %d", errno);
+        goto fail;
+    }
+    rtp_info.udp_recv_sock.read_cb = udp_read_cb;
+    socket_noblock(rtp_info.udp_recv_sock.fd);
+    epoll_fd_add(rtp_info.udp_recv_sock.fd);
+
+#if 0
+    rtp_info.udp_send_sock.fd = rtp_udp_cli_open(conf_info->remote_ip, conf_info->server_port);
+    if(rtp_info.udp_send_sock.fd < 0) {
+        LOGW("socket(udp_send_sock) fail : errno = %d", errno);
+        LOGW("Can not connected to %s:%d.", conf_info->remote_ip, conf_info->server_port);
+        // goto fail;
+    }
+    rtp_info.udp_send_sock.read_cb = NULL;
+#endif
+
+    udp_state = RTP_UDP_SER_STATE_RUNNING;
+
+    LOGD("UDP server is running...");
+    return 0;
+
+fail:
+    if(rtp_info.udp_recv_sock.fd > 0) {
+        epoll_fd_del(rtp_info.udp_recv_sock.fd);
+        close(rtp_info.udp_recv_sock.fd);
+        rtp_info.udp_recv_sock.fd = -1;
+        rtp_info.udp_recv_sock.read_cb = NULL;
+    }
+
+#if 0
+    if(rtp_info.udp_send_sock.fd > 0) {
+        close(rtp_info.udp_send_sock.fd);
+        rtp_info.udp_send_sock.fd = -1;
+        rtp_info.udp_send_sock.read_cb = NULL;
+    }
+#endif
+
+    udp_state = RTP_UDP_SER_STATE_IDEL;
+    return -1;
+}
+
+/*===========================================================================
+FUNCTION rtp_udp_server_stop
+
+DESCRIPTION:
+  Stop RTP UDP server.
+
+PARAMETERS:
+  Non.
+
+RETURN VALUE:
+  int : 0 for success, other for fail.
+
+===========================================================================*/
+int rtp_udp_server_stop()
+{
+    if(udp_state != RTP_UDP_SER_STATE_RUNNING) {
+        LOGE("udp_state error : %d", udp_state);
+        return -1;
+    }
+
+    udp_state = RTP_UDP_SER_STATE_STOPING;
+    if(rtp_info.udp_recv_sock.fd > 0) {
+        epoll_fd_del(rtp_info.udp_recv_sock.fd);
+
+        close(rtp_info.udp_recv_sock.fd);
+        rtp_info.udp_recv_sock.fd = -1;
+        rtp_info.udp_recv_sock.read_cb = NULL;
+    }
+
+#if 0
+    if(rtp_info.udp_send_sock.fd > 0) {
+        close(rtp_info.udp_send_sock.fd);
+        rtp_info.udp_send_sock.fd = -1;
+        rtp_info.udp_send_sock.read_cb = NULL;
+    }
+#endif
+
+    udp_state = RTP_UDP_SER_STATE_IDEL;
+
+    return 0;
+}
+
+
+/*===========================================================================
+FUNCTION rtp_voip_server_start
+
+DESCRIPTION:
+  Start RTP voip server.will start playback/record PCM to/from voice path.
+
+PARAMETERS:
+  conf_info : RTP config informations.
+
+RETURN VALUE:
+  int : 0 for success, other for fail.
+
+===========================================================================*/
+int rtp_voip_server_start(const rtp_config_t *conf_info)
+{
+    UNUSED(conf_info);
+    if(voip_state != RTP_VOIP_SER_STATE_IDEL) {
+        LOGE("voip_state error : %d", voip_state);
+        return -1;
+    }
+
+    voip_state = RTP_VOIP_SER_STATE_STARTING;
+    if(mbtk_audio_pcm_init()) {
+        LOGE("mbtk_audio_pcm_init() fail.");
+        voip_state = RTP_VOIP_SER_STATE_IDEL;
+    }
+
+    LOGD("Start open UDP client : %s-%d", conf_info->remote_ip, conf_info->server_port);
+    rtp_info.udp_send_sock.fd = rtp_udp_cli_open(conf_info->remote_ip, conf_info->server_port);
+    if(rtp_info.udp_send_sock.fd < 0) {
+        LOGE("Can not connected to %s:%d [errno-%d].", conf_info->remote_ip, conf_info->server_port, errno);
+        goto error;
+    }
+    rtp_info.udp_send_sock.read_cb = NULL;
+
+    rtp_send_init();
+
+    if(mbtk_audio_voice_pcm_record_start(voip_recorder_cb)) {
+        LOGE("mbtk_audio_voice_pcm_record_start() fail.");
+        goto error;
+    }
+
+    voip_state = RTP_VOIP_SER_STATE_RUNNING;
+
+    pthread_attr_t thread_attr;
+    pthread_attr_init(&thread_attr);
+    if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED))
+    {
+        LOGE("pthread_attr_setdetachstate() fail.");
+        goto error;
+    }
+
+    if (pthread_create(&voip_playback, NULL, (void *)&voip_playback_thread, NULL) < 0) {
+        LOGE("%s: error creating thread_recorder!", __FUNCTION__);
+        goto error;
+    }
+
+    LOGD("VOIP server is running...");
+    return 0;
+error:
+    if(mbtk_audio_pcm_deinit()) {
+        LOGE("mbtk_audio_pcm_deinit() fail.");
+    }
+
+    if(rtp_info.udp_send_sock.fd > 0) {
+        close(rtp_info.udp_send_sock.fd);
+        rtp_info.udp_send_sock.fd = -1;
+        rtp_info.udp_send_sock.read_cb = NULL;
+    }
+
+    voip_state = RTP_VOIP_SER_STATE_IDEL;
+    return -1;
+}
+
+
+/*===========================================================================
+FUNCTION rtp_server_stop
+
+DESCRIPTION:
+  Stop RTP voip server.
+
+PARAMETERS:
+  Non.
+
+RETURN VALUE:
+  int : 0 for success, other for fail.
+
+===========================================================================*/
+int rtp_voip_server_stop()
+{
+    if(voip_state != RTP_VOIP_SER_STATE_RUNNING) {
+        LOGE("voip_state error : %d", voip_state);
+        return -1;
+    }
+
+    voip_state = RTP_VOIP_SER_STATE_STOPING;
+#if 0
+    if(mbtk_audio_pcm_play_stop()) {
+        LOGE("mbtk_audio_pcm_play_stop() fail.");
+        return -1;
+    }
+#else
+    if (pthread_join(voip_playback, NULL)) {
+        LOGE("error join voip_playback!");
+        return -1;
+    }
+#endif
+
+    if(mbtk_audio_pcm_recorder_stop()) {
+        LOGE("mbtk_audio_pcm_recorder_stop() fail.");
+        return -1;
+    }
+
+    if(mbtk_audio_pcm_deinit()) {
+        LOGE("mbtk_audio_pcm_deinit() fail.");
+        return -1;
+    }
+
+    if(rtp_info.recv.recv_buff) {
+        mbtk_loopbuff_free(rtp_info.recv.recv_buff);
+        rtp_info.recv.recv_buff = NULL;
+    }
+
+    voip_state = RTP_VOIP_SER_STATE_IDEL;
+
+    return 0;
+}
+
diff --git a/mbtk/test/libmbtk_audio/Makefile b/mbtk/test/libmbtk_audio/Makefile
index 99172c3..d6e275c 100755
--- a/mbtk/test/libmbtk_audio/Makefile
+++ b/mbtk/test/libmbtk_audio/Makefile
@@ -7,7 +7,7 @@
 
 LIBS += -lmbtk_lib
 
-CFLAGS += 
+CFLAGS = $(CFLAGS_TEST)
 
 DEFINE +=
 
diff --git a/mbtk/test/libmbtk_audio/mbtk_pcm_voip_test.c b/mbtk/test/libmbtk_audio/mbtk_pcm_voip_test.c
new file mode 100755
index 0000000..20938ff
--- /dev/null
+++ b/mbtk/test/libmbtk_audio/mbtk_pcm_voip_test.c
@@ -0,0 +1,222 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "audio_if_api.h"
+#include "mbtk_audio2.h"
+#include "mbtk_log.h"
+#include "mbtk_utils.h"
+
+#define BUFF_SIZE 4096
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+#define FORMAT_PCM 1
+
+#define PCM_WB_BUF_SIZE     640
+#define PCM_NARROW_BUF_SIZE 320
+
+static int record_fd = -1;
+static bool running = FALSE;
+
+static void recorder_cb(void *data, uint32 data_len)
+{
+    if(record_fd > 0) {
+        if(data_len > 0) {
+            LOGD("Recorver data:%d", data_len);
+            mbtk_write(record_fd, data, data_len);
+        } else {
+            LOGD("Recorver data end.");
+        }
+    }
+}
+
+static void voip_playback_run(void *arg)
+{
+    int rc, len, fd, frames = 0;
+    int pack_size = 1024;
+    //char buf[MBTK_PCM_WB_BUF_SIZE];
+    char buf[BUFF_SIZE];
+    char *path = "/data/voip_playback.wav";
+    struct stat st;
+    struct riff_wave_header riff_wave_header;
+    struct chunk_header chunk_header;
+    struct chunk_fmt chunk_fmt = {0};
+    unsigned int more_chunks = 1;
+
+    /* Check and open source file */
+    if (access(path, F_OK) || stat(path, &st)) {
+        printf("%s: error reading from file %s\n", __FUNCTION__, path);
+        return;
+    }
+
+    if (!st.st_size) {
+        printf("%s: empty file %s\n", __FUNCTION__, path);
+        return;
+    }
+
+    fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        printf("%s: error opening file %s\n", __FUNCTION__, path);
+        return;
+    }
+
+    lseek(fd, sizeof(struct wav_header), SEEK_SET);
+
+
+    if(mbtk_audio_voice_pcm_playback_start()) {
+        printf("mbtk_audio_voice_pcm_playback_start() fail.\n");
+        return;
+    }
+
+#if 1
+    if(mbtk_audio_voice_pcm_record_start(recorder_cb)) {
+        printf("mbtk_audio_voice_pcm_record_start() fail.\n");
+        //goto exit;
+    }
+#endif
+
+    while (running) {
+        /* Playback loop */
+        memset(buf, 0x00, sizeof(buf));
+        len = read(fd, buf, pack_size);
+        if (len == -1) {
+            printf("%s: error reading from file\n", __FUNCTION__);
+            break;
+        }
+
+        if (len == 0) {
+            /* reached EOF */
+            printf("%s: nothing to read\n", __FUNCTION__);
+            break;
+        }
+
+        if((rc = mbtk_audio_pcm_play_data_send(buf, len)) < len) {
+            printf("Send data %d/%d\n", rc, len);
+            break;
+        }
+
+        printf("%s: No.%d frame playback[len - %d]\n", __FUNCTION__, ++frames, len);
+    }
+
+#if 1
+    if(mbtk_audio_pcm_recorder_stop()) {
+        printf("mbtk_audio_pcm_recorder_stop() fail.\n");
+        //goto exit;
+    }
+#endif
+
+    LOGD("playback_thread exit.");
+}
+
+#if 0
+static void voip_record_run(void *arg)
+{
+
+}
+#endif
+
+static void sig_handler(int sig)
+{
+    running = FALSE;
+
+    printf("Success exit by signal...\n");
+
+    sleep(1);
+
+    exit(0);
+}
+
+
+int main(int argc, char *argv[])
+{
+    mbtk_log_init("radio", "RTP_TEST");
+
+    signal(SIGINT, sig_handler);
+    signal(SIGTERM, sig_handler);
+
+    if(mbtk_audio_pcm_init()) {
+        printf("mbtk_audio_pcm_init() fail.\n");
+        return -1;
+    }
+
+    struct wav_header header;
+    int rc = 0;
+    char *path = "/data/voip_record.wav";
+
+    header.riff_id = ID_RIFF;
+    header.riff_sz = 0;
+    header.riff_fmt = ID_WAVE;
+    header.fmt_id = ID_FMT;
+    header.fmt_sz = 16;
+    header.audio_format = 1;        //FORMAT_PCM;
+    header.num_channels = 1;        //Modem ONLY support mono recording
+    header.sample_rate = 8000;
+    header.bits_per_sample = 16;    //PCM_SAMPLEBITS_S16_LE;
+    header.byte_rate = (header.bits_per_sample / 8) * header.num_channels * header.sample_rate;
+    header.block_align = header.num_channels * (header.bits_per_sample / 8);
+    header.data_id = ID_DATA;
+
+    record_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    if (record_fd < 0) {
+        printf("%s: error opening file %s!\n", __FUNCTION__, path);
+        rc = -1;
+        goto exit;
+    }
+
+    //leave enough room for header
+    lseek(record_fd, sizeof(struct wav_header), SEEK_SET);
+
+    running = TRUE;
+
+    pthread_t playabck_thread/*, record_thread*/;
+    rc = pthread_create(&playabck_thread, NULL, (void *)&voip_playback_run, NULL);
+    if (rc < 0) {
+        LOGE("error creating thread_start!");
+        rc = -1;
+        goto exit;
+    }
+
+#if 0
+    rc = pthread_create(&record_thread, NULL, (void *)&voip_record_run, NULL);
+    if (rc < 0) {
+        LOGE("error creating thread_start!");
+        rc = -1;
+        goto exit;
+    }
+#endif
+
+    if (pthread_join(playabck_thread, NULL)){
+        printf("error join thread!\n");
+        abort();
+    }
+    LOGD("playabck_thread() join exit.");
+
+    lseek(record_fd, 0, SEEK_SET);
+    mbtk_write(record_fd, &header, sizeof(struct wav_header));
+    close(record_fd);
+
+#if 0
+    if (pthread_join(record_thread, NULL)){
+        printf("error join thread!\n");
+        abort();
+    }
+    LOGD("record_thread() join exit.");
+#endif
+
+exit:
+    if(mbtk_audio_pcm_deinit()) {
+        printf("mbtk_audio_pcm_deinit() fail.\n");
+        return -1;
+    }
+    return rc;
+}
+
diff --git a/mbtk/test/libmbtk_lib/mbtk_loopbuff_test.c b/mbtk/test/libmbtk_lib/mbtk_loopbuff_test.c
new file mode 100755
index 0000000..4ae6684
--- /dev/null
+++ b/mbtk/test/libmbtk_lib/mbtk_loopbuff_test.c
@@ -0,0 +1,107 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "mbtk_loop_buffer.h"
+#include "mbtk_log.h"
+#include "mbtk_utils.h"
+
+
+static void* read_run(void* arg)
+{
+    mbtk_loop_buff_handle *handle = (mbtk_loop_buff_handle*)arg;
+    int fd = open("/data/voip_playback.wav", O_RDONLY);
+    int read_count = 0;
+    int buff_count = 0;
+    if(fd > 0) {
+        char buff[1569];
+        int len;
+        int temp_len;
+        while((len = read(fd, buff, sizeof(buff))) > 0) {
+            read_count += len;
+
+            temp_len = mbtk_loopbuff_writen(handle, buff, len);
+            if(temp_len > 0) {
+                if(temp_len != len) {
+                    printf("mbtk_loopbuff_writen() fail : %d/%d\n", temp_len, len);
+                }
+                buff_count += temp_len;
+            } else {
+                printf("mbtk_loopbuff_writen() fail.\n");
+            }
+        }
+        close(fd);
+    }
+    printf("read complete : %d / %d\n", read_count, buff_count);
+
+    return NULL;
+}
+
+static void* write_run(void* arg)
+{
+    mbtk_loop_buff_handle *handle = (mbtk_loop_buff_handle*)arg;
+    int write_count = 0;
+    int buff_count = 0;
+    int fd = open("/data/voip_playback_copy.wav", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    if(fd > 0) {
+        char buff[999];
+        int len;
+        while((len = mbtk_loopbuff_readn(handle, buff, sizeof(buff))) > 0) {
+            buff_count += len;
+            len = write(fd, buff, len);
+            if(len > 0)
+                write_count += len;
+            else
+                printf("mbtk_write() fail.\n");
+        }
+        close(fd);
+    }
+
+    printf("write complete : %d / %d\n", write_count, buff_count);
+    return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+    mbtk_log_init("radio", "LOOPBUFF");
+    mbtk_loop_buff_handle *handle = mbtk_loopbuff_get(7 * 1001);
+    if(handle) {
+        pthread_t read_pid, write_pid;
+        if(pthread_create(&read_pid, NULL, read_run, handle))
+        {
+            LOGE("pthread_create() fail.");
+            return -1;
+        }
+
+        if(pthread_create(&write_pid, NULL, write_run, handle))
+        {
+            LOGE("pthread_create() fail.");
+            return -1;
+        }
+
+        sleep(1);
+
+        if (pthread_join(read_pid, NULL)){
+            printf("error join thread!\n");
+            abort();
+        }
+
+        if (pthread_join(write_pid, NULL)){
+            printf("error join thread!\n");
+            abort();
+        }
+
+        mbtk_loopbuff_free(handle);
+    }
+    return 0;
+}
+
+
diff --git a/mbtk/test/libmbtk_lib/mbtk_rtp_test.c b/mbtk/test/libmbtk_lib/mbtk_rtp_test.c
new file mode 100755
index 0000000..2140e25
--- /dev/null
+++ b/mbtk/test/libmbtk_lib/mbtk_rtp_test.c
@@ -0,0 +1,155 @@
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mbtk_rtp.h"
+#include "mbtk_log.h"
+
+
+static void help()
+{
+    printf("rtp_mode <0/1>: Disable/Enable RTP.\n");
+    printf("volume <0-7>: Set volume.\n");
+    printf("remote_ip <xxx:xxx:xxx:xxx>: Set remote ip.\n");
+    printf("client_port <port>: Set client(local) port.\n");
+    printf("server_port <port>: Set server(remote) port.\n");
+    printf("sample_rate <8000/16000>: Set sample rate.\n");
+    printf("channel <1>: Set channel.\n");
+}
+
+
+static void sig_handler(int sig)
+{
+    if(mbtk_rtp_deinit()) {
+        printf("mbtk_rtp_deinit() fail.\n");
+    }
+
+    printf("Success exit by signal...\n");
+    exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+    mbtk_log_init("radio", "RTP_CLI");
+
+    if(mbtk_rtp_init()) {
+        printf("mbtk_rtp_init() fail.\n");
+        return -1;
+    }
+
+    signal(SIGINT, sig_handler);
+    signal(SIGTERM, sig_handler);
+
+    char cmd[100];
+    bool running = TRUE;
+    while(running)
+    {
+        memset(cmd, 0, 100);
+//        int err;
+        // printf("0 : Disable    1 : Enable    Other : Exit\n");
+        help();
+        if(fgets(cmd, 100, stdin))
+        {
+            if(cmd[0] == '\r' || cmd[0] == '\n')
+                continue;
+            char *ptr = cmd + strlen(cmd) - 1;
+            while(ptr >= cmd && (*ptr == '\r' || *ptr == '\n'))
+            {
+                *ptr-- = '\0';
+            }
+
+            if(!strncasecmp(cmd, "rtp_mode", 8))
+            {
+                int temp;
+                if(1 == sscanf(cmd, "rtp_mode %d", &temp)) {
+                    if(mbtk_rtp_enable((bool)temp)) {
+                        printf("Error\n");
+                    } else {
+                        printf("Success\n");
+                    }
+                }
+            }
+            else if(!strncasecmp(cmd, "volume", 6)){
+                int temp;
+                if(1 == sscanf(cmd, "volume %d", &temp)) {
+                    if(mbtk_rtp_volume_set(temp)) {
+                        printf("Error\n");
+                    } else {
+                        printf("Success\n");
+                    }
+                }
+            }
+            else if(!strncasecmp(cmd, "remote_ip", 9)){
+                char ipv4[20] = {0};
+                if(1 == sscanf(cmd, "remote_ip %s", ipv4)) {
+                    if(mbtk_rtp_remote_ip_set(ipv4)) {
+                        printf("Error\n");
+                    } else {
+                        printf("Success\n");
+                    }
+                }
+            }
+            else if(!strncasecmp(cmd, "client_port", 11)){
+                int temp;
+                if(1 == sscanf(cmd, "client_port %d", &temp)) {
+                    if(mbtk_rtp_client_port_set(temp)) {
+                        printf("Error\n");
+                    } else {
+                        printf("Success\n");
+                    }
+                }
+            }
+            else if(!strncasecmp(cmd, "server_port", 11)){
+                int temp;
+                if(1 == sscanf(cmd, "server_port %d", &temp)) {
+                    if(mbtk_rtp_server_port_set(temp)) {
+                        printf("Error\n");
+                    } else {
+                        printf("Success\n");
+                    }
+                }
+            }
+            else if(!strncasecmp(cmd, "sample_rate", 11)){
+                int temp;
+                if(1 == sscanf(cmd, "sample_rate %d", &temp)) {
+                    if(mbtk_rtp_sample_rate_set(temp)) {
+                        printf("Error\n");
+                    } else {
+                        printf("Success\n");
+                    }
+                }
+            }
+            else if(!strncasecmp(cmd, "channel", 7)){
+                int temp;
+                if(1 == sscanf(cmd, "channel %d", &temp)) {
+                    if(mbtk_rtp_channel_set(temp)) {
+                        printf("Error\n");
+                    } else {
+                        printf("Success\n");
+                    }
+                }
+            }
+
+            else if(!strcasecmp(cmd, "h") || !strcasecmp(cmd, "help")) {
+                help();
+            } else if(!strcasecmp(cmd, "q")) {
+                break;
+            } else {
+                printf("\n");
+            }
+        }
+    }
+
+//exit:
+    if(mbtk_rtp_deinit()) {
+        printf("mbtk_rtp_deinit() fail.\n");
+        return -1;
+    }
+
+    printf("Success exit.\n");
+    return 0;
+}
diff --git a/mbtk/test/others/mbtk_rtp_udp_cli.c b/mbtk/test/others/mbtk_rtp_udp_cli.c
new file mode 100755
index 0000000..92c19cf
--- /dev/null
+++ b/mbtk/test/others/mbtk_rtp_udp_cli.c
@@ -0,0 +1,373 @@
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <sys/epoll.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+
+//#include "mbtk_log.h"
+//#include "mbtk_utils.h"
+// #include "audio_if_api.h"
+//#include "mbtk_audio2.h"
+
+#define RTP_UDP_SER_PORT_DEFAULT 53248
+#define RTP_UDP_CLI_PORT_DEFAULT 55555
+
+
+#define BUFF_SIZE 4096
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT  0x20746d66
+#define ID_DATA 0x61746164
+#define FORMAT_PCM 1
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef UNUSED
+#define UNUSED(a)    (void)(a)
+#endif
+
+typedef unsigned int uint32; /* Unsigned 32 bit value */
+
+struct riff_wave_header {
+	unsigned int riff_id;
+	unsigned int riff_sz;
+	unsigned int wave_id;
+};
+
+struct chunk_header {
+	unsigned int id;
+	unsigned int sz;
+};
+
+struct chunk_fmt {
+	unsigned short audio_format;
+	unsigned short num_channels;
+	unsigned int sample_rate;
+	unsigned int byte_rate;
+	unsigned short block_align;
+	unsigned short bits_per_sample;
+};
+
+struct wav_header {
+	unsigned int riff_id;
+	unsigned int riff_sz;
+	unsigned int riff_fmt;
+	unsigned int fmt_id;
+	unsigned int fmt_sz;
+	unsigned short audio_format;
+	unsigned short num_channels;
+	unsigned int sample_rate;
+	unsigned int byte_rate;
+	unsigned short block_align;
+	unsigned short bits_per_sample;
+	unsigned int data_id;
+	unsigned int data_sz;
+};
+
+
+#define PCM_WB_BUF_SIZE     640
+#define PCM_NARROW_BUF_SIZE 320
+
+static int record_fd = -1;
+static int send_fd = -1;
+static bool running = FALSE;
+
+
+static void voip_playback_run(void *arg)
+{
+    int rc, len, fd, frames = 0;
+    int pack_size = 320;
+    //char buf[MBTK_PCM_WB_BUF_SIZE];
+    char buf[BUFF_SIZE];
+    char *path = "/data/voip_playback.wav";
+    struct stat st;
+    struct riff_wave_header riff_wave_header;
+    struct chunk_header chunk_header;
+    struct chunk_fmt chunk_fmt = {0};
+    unsigned int more_chunks = 1;
+    uint32 header[4];
+
+    if(send_fd < 0) {
+        printf("Client socket not open.");
+        return;
+    }
+
+    /* Check and open source file */
+    if (access(path, F_OK) || stat(path, &st)) {
+        printf("%s: error reading from file %s\n", __FUNCTION__, path);
+        return;
+    }
+
+    if (!st.st_size) {
+        printf("%s: empty file %s\n", __FUNCTION__, path);
+        return;
+    }
+
+    fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        printf("%s: error opening file %s\n", __FUNCTION__, path);
+        return;
+    }
+
+    lseek(fd, sizeof(struct wav_header), SEEK_SET);
+    uint32 sequence = 1;
+    uint32 timestamp = 0;
+    while (running) {
+        /* Playback loop */
+        memset(buf, 0x00, sizeof(buf));
+        len = read(fd, buf + 16, pack_size);
+        if (len == -1) {
+            printf("%s: error reading from file\n", __FUNCTION__);
+            break;
+        }
+
+        if (len == 0) {
+            /* reached EOF */
+            printf("%s: nothing to read\n", __FUNCTION__);
+            break;
+        }
+
+
+        header[0] = htonl(((uint32_t) 2 << 30) | ((uint32_t) 1 << 24) | ((uint32_t) 0x60 << 16) | ((uint32_t) sequence));
+        header[1] = htonl(timestamp);
+        header[2] = htonl(0xFFFF0000);
+        header[3] = htonl(0xFFFF0000);
+        memcpy(buf, &header, sizeof(header));
+
+        if((rc = sendto(send_fd, buf, len + 16, 0, NULL, 0)) < len + 16) {
+            printf("Send data fail: %d/%d\n", rc, len);
+            break;
+        } else {
+            printf("SEND : %d / %d\n", rc, len);
+        }
+
+        sequence++;
+        timestamp += len / 2;
+
+        ++frames;
+        //printf("%s: No.%d frame playback[len - %d]\n", __FUNCTION__, ++frames, len);
+        usleep(21000);
+    }
+
+    printf("playback_thread exit.\n");
+}
+
+static void sig_handler(int sig)
+{
+    running = FALSE;
+
+    printf("Success exit by signal...\n");
+
+    sleep(1);
+
+    exit(0);
+}
+
+static int rtp_udp_ser_open(const char *local_addr, int local_port)
+{
+    // No set local addr.
+    UNUSED(local_addr);
+
+    int fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if(fd < 0){
+        printf("socket() fail.[%d]\n", errno);
+        return -1;
+    }
+
+    struct sockaddr_in servaddr;
+    memset(&servaddr, 0, sizeof(servaddr));
+    servaddr.sin_family = AF_INET;
+    servaddr.sin_port = htons(local_port);
+    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    if (bind(fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) < 0) {
+        printf("bind() failed: %d\n", errno);
+        goto result_fail_with_close;
+    }
+
+    return fd;
+result_fail_with_close:
+    close(fd);
+    fd = -1;
+    printf("mbtk_sock_open() end:fail\n");
+    return -1;
+}
+
+static int rtp_udp_cli_open(const char *remote_addr, int remote_port)
+{
+    struct sockaddr_in dst_sa4, src_sa4;
+    if (inet_pton(AF_INET, "0.0.0.0", &src_sa4.sin_addr) > 0) {
+        src_sa4.sin_family = AF_INET;
+        src_sa4.sin_port = htons(0);
+        memset(&src_sa4.sin_zero, 0, sizeof(src_sa4.sin_zero));
+    } else {
+        printf("Set src addr fail.\n");
+        return -1;
+    }
+
+    if (inet_pton(AF_INET, remote_addr, &dst_sa4.sin_addr) > 0) {
+        dst_sa4.sin_family = AF_INET;
+        dst_sa4.sin_port = htons(remote_port);
+        memset(&dst_sa4.sin_zero, 0, sizeof(dst_sa4.sin_zero));
+    } else {
+        printf("Set dst addr fail.\n");
+        return -1;
+    }
+
+    int fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if(fd < 0){
+        printf("socket() fail.[%d]\n", errno);
+        return -1;
+    }
+
+    if (bind(fd, (struct sockaddr*) &src_sa4, sizeof(src_sa4)) < 0) {
+        printf("bind() failed: %d\n", errno);
+        goto result_fail_with_close;
+    }
+
+    if (connect(fd, (struct sockaddr*) &dst_sa4, sizeof(dst_sa4)) < 0) {
+        printf("connect() failed: %d\n", errno);
+        goto result_fail_with_close;
+    }
+
+#if 0
+    if(socket_noblock(fd)) {
+        goto result_fail_with_close;
+    }
+#endif
+
+    return fd;
+result_fail_with_close:
+    close(fd);
+    fd = -1;
+    printf("mbtk_sock_open() end:fail\n");
+    return -1;
+}
+
+int main(int argc, char *argv[])
+{
+    if(argc != 2) {
+        printf("mbtk_rtp_udp_cli <IP>\n");
+        return -1;
+    }
+
+    // mbtk_log_init("radio", "RTP_TEST");
+
+    signal(SIGINT, sig_handler);
+    signal(SIGTERM, sig_handler);
+
+
+    int ser_fd = rtp_udp_ser_open(NULL, RTP_UDP_SER_PORT_DEFAULT);
+    if(ser_fd < 0) {
+        printf("rtp_udp_ser_open() fail.\n");
+        return -1;
+    }
+
+    send_fd = rtp_udp_cli_open(argv[1], RTP_UDP_CLI_PORT_DEFAULT);
+    if(send_fd < 0) {
+        printf("rtp_udp_cli_open() fail.\n");
+        // return -1;
+    }
+
+    struct wav_header header;
+    int rc = 0;
+    char *path = "/data/voip_record.wav";
+
+    header.riff_id = ID_RIFF;
+    header.riff_sz = 0;
+    header.riff_fmt = ID_WAVE;
+    header.fmt_id = ID_FMT;
+    header.fmt_sz = 16;
+    header.audio_format = 1;        //FORMAT_PCM;
+    header.num_channels = 1;        //Modem ONLY support mono recording
+    header.sample_rate = 8000;
+    header.bits_per_sample = 16;    //PCM_SAMPLEBITS_S16_LE;
+    header.byte_rate = (header.bits_per_sample / 8) * header.num_channels * header.sample_rate;
+    header.block_align = header.num_channels * (header.bits_per_sample / 8);
+    header.data_id = ID_DATA;
+
+    record_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    if (record_fd < 0) {
+        printf("%s: error opening file %s!\n", __FUNCTION__, path);
+        return -1;
+    }
+
+    lseek(record_fd, 0, SEEK_SET);
+    write(record_fd, &header, sizeof(struct wav_header));
+
+    //leave enough room for header
+    lseek(record_fd, sizeof(struct wav_header), SEEK_SET);
+
+    char buff[2048];
+    int len_recv;
+    int len_send;
+    running = TRUE;
+    bool is_first = TRUE;
+    while(running) {
+        len_recv = recvfrom(ser_fd, buff, sizeof(buff), 0, NULL, NULL);
+        if(len_recv < 0) {
+            printf("recvfrom() ret is %d,errno - %d\n", len_recv, errno);
+            continue;
+        } else if(len_recv == 0) {
+            printf("ret is 0\n");
+        } else if(len_recv > 16){
+            printf("RECV:len - %d\n", len_recv);
+            write(record_fd, buff + 16, len_recv - 16);
+
+            if(is_first) {
+                pthread_t playabck_thread/*, record_thread*/;
+                rc = pthread_create(&playabck_thread, NULL, (void *)&voip_playback_run, NULL);
+                if (rc < 0) {
+                    printf("error creating thread_start!");
+                    break;
+                }
+                is_first = FALSE;
+            }
+
+#if 0
+            if(cli_fd < 0) {
+                cli_fd = rtp_udp_cli_open(argv[1], RTP_UDP_CLI_PORT_DEFAULT);
+                if(cli_fd < 0) {
+                    printf("rtp_udp_cli_open() fail.\n");
+                    // return -1;
+                } else {
+                    printf("rtp_udp_cli_open() success.\n");
+                }
+            }
+
+            if(cli_fd > 0) {
+                len_send = sendto(cli_fd, buff, len_recv, 0, NULL, 0);
+                printf("SEND : %d / %d\n", len_send, len_recv);
+            }
+#endif
+        } else {
+            printf("RTP Header error.\n");
+        }
+    }
+
+    close(record_fd);
+
+    close(ser_fd);
+
+    return 0;
+}
+
+