Add mbtk_loopbuff and rtp support.

Change-Id: Idc80af4efb8ff76c4afc59fdf1d3896b453e6ff4
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;
+}
+