广播的业务还是挺好实现的,但业务链条比较长,作为练手项目绝对不错,主要涉及到几个点:
1、音频数据采集;
2、音频数据编码;
3、媒体流组包;
4、 (组播)UDP Socket服务器和客户端,socket接收和发送实现;
5、音频抖动缓冲区,及音频播放器实现;
逻辑框图:
从业务层面看,每个广播都可以通过广播按钮给同组的其他广播喊话;
从网络层面来看,每个广播都有一个信令监听端口,监听广播发起、广播结束的通知;
被喊话的广播接收到喊话通知后,开一个udp端口,接收广播的音频数据包;
业务框图:
录制和播放器参考pjsip的audiotest.c的代码实现;
录制:
//broadcast_record.c /* 录制wav,编码? 通过组播发送出去 组播接收wav, 解码?播放wav */ #include <pjmedia-audiodev/audiodev.h> #include <pjmedia.h> #include <pjlib.h> #include <pjlib-util.h> #include "broadcast_app.h" #define THIS_FILE "broadcast_record.c" static pj_pool_t *pool = NULL; static pjmedia_aud_param param; static pjmedia_aud_stream *strm = NULL; static unsigned playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; static unsigned capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY; static volatile RecordCallback recordCallback = NULL; int stop_record(void) { recordCallback = NULL; if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (pool){ pj_pool_release(pool); } strm = NULL; pool = NULL; return 0; } static pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame) { //回调函数 //return pjmedia_port_put_frame((pjmedia_port*)user_data, frame); pj_int16_t *pcm_in = (pj_int16_t*)frame->buf; //编码? //发送? if (recordCallback != NULL){ recordCallback(user_data, pcm_in, (int)frame->size, frame->timestamp.u32.lo); } return 0; } int start_record(void *user_data, RecordCallback callback) { pj_status_t status; pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav", 1000, 1000, NULL); status = pjmedia_aud_dev_default_param(0, ¶m); if (status != PJ_SUCCESS) { printf("pjmedia_aud_dev_default_param()", status); goto on_return; } param.dir = PJMEDIA_DIR_CAPTURE; param.clock_rate = 16000; param.samples_per_frame = 320; param.channel_count = 1; param.bits_per_sample = 16; /* Latency settings */ param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY); param.input_latency_ms = capture_lat; param.output_latency_ms = playback_lat; if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } status = pjmedia_aud_stream_create(¶m, &wav_rec_cb, NULL, user_data, &strm); if (status != PJ_SUCCESS) { printf("Error opening the sound device", status); goto on_failed; } status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { printf("Error starting the sound device", status); goto on_failed; } //增加回调函数 recordCallback = callback; PJ_LOG(3,(THIS_FILE, "Recording started")); goto on_return; on_failed: if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (pool){ pj_pool_release(pool); } on_return: return 0; }
播放器:
//broadcast_play.c #include <pjmedia-audiodev/audiodev.h> #include <pjmedia.h> #include <pjlib.h> #include <pjlib-util.h> #include "broadcast_app.h" #define THIS_FILE "broadcast_play.c" static pj_pool_t *pool = NULL; static pjmedia_aud_param param; static pjmedia_aud_stream *strm = NULL; static unsigned playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; static unsigned capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY; static volatile RecordCallback playCallback = NULL; static pj_status_t wav_play_cb(void *user_data, pjmedia_frame *frame) { //return pjmedia_port_get_frame((pjmedia_port*)user_data, frame); if (playCallback != NULL){ playCallback(user_data, frame->buf, (int)frame->size, frame->timestamp.u32.lo); } return 0; } int stop_play(void){ playCallback = NULL; if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (pool){ pj_pool_release(pool); } return 0; } int start_play(void *user_data, PlayCallback callback) { pj_status_t status; if (pool == NULL){ pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "play", 1000, 1000, NULL); } status = pjmedia_aud_dev_default_param(0, ¶m); if (status != PJ_SUCCESS) { printf("pjmedia_aud_dev_default_param()", status); goto on_return; } param.dir = PJMEDIA_DIR_PLAYBACK; param.clock_rate = 16000; param.samples_per_frame = 320; param.channel_count = 1; param.bits_per_sample = 16; /* Latency settings */ param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY); param.input_latency_ms = capture_lat; param.output_latency_ms = playback_lat; status = pjmedia_aud_stream_create(¶m, NULL, &wav_play_cb, user_data, &strm); if (status != PJ_SUCCESS) { printf("Error opening the sound device", status); goto on_failed; } status = pjmedia_aud_stream_start(strm); if (status != PJ_SUCCESS) { printf("Error starting the sound device", status); goto on_failed; } playCallback = callback; PJ_LOG(3,(THIS_FILE, "play started,strm:%08x",strm)); goto on_return; on_failed: if (strm) { pjmedia_aud_stream_stop(strm); pjmedia_aud_stream_destroy(strm); } if (pool){ pj_pool_release(pool); } on_return: return 0; }
头文件:
//broadcast_app.h #ifndef __BROADCAST_APP__ #define __BROADCAST_APP__ #include <unistd.h> #ifdef __cplusplus extern "C"{ #endif typedef int (*PlayCallback)(void *user_data,uint8_t *rawData, int len, uint32_t timestamp); typedef int (*RecordCallback)(void *user_data,uint16_t *rawData, int len, uint32_t timestamp); int start_play(void *user_data, PlayCallback callback); int stop_play(void); int start_record(void *user_data, RecordCallback callback); int stop_record(void); #ifdef __cplusplus } #endif #endif//__BROADCAST_APP__
-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com
本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com