音频广播播放功能实现逻辑分享

广播的业务还是挺好实现的,但业务链条比较长,作为练手项目绝对不错,主要涉及到几个点:

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, &param);
    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(&param, &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, &param);
    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(&param, 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__


本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com

请先登录后发表评论
  • 最新评论
  • 总共0条评论