广播的业务还是挺好实现的,但业务链条比较长,作为练手项目绝对不错,主要涉及到几个点:
1、音频数据采集;
2、音频数据编码;
3、媒体流组包;
4、 (组播)UDP Socket服务器和客户端,socket接收和发送实现;
5、音频抖动缓冲区,及音频播放器实现;
逻辑框图:
从业务层面看,每个广播都可以通过广播按钮给同组的其他广播喊话;
从网络层面来看,每个广播都有一个信令监听端口,监听广播发起、广播结束的通知;
被喊话的广播接收到喊话通知后,开一个udp端口,接收广播的音频数据包;
业务框图:
录制和播放器参考pjsip的audiotest.c的代码实现;
录制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | //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; } |
播放器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | //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; } |
头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //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