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(×tamp, (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;
+}
+
+