PJSIP 收发自定义的音频流

原文

会议桥

自定义媒体端口

 

 

在《pjSIP注册呼叫流程简介》中介绍了pjSIP注册与呼叫的基本流程,本节对自定义媒体流与端口做下介绍。

会议桥

pjSIP中通过会议桥(Conference)把媒体流(Stream)与抽象音频设备端口(Sound Device Port)连接起来(并负责各路媒体的混流);他们之间数据传递都是通过pjmedia_port接口来实现的。若要收发自定义的媒体数据,只需连接会议桥,从中接收或发送媒体数据即可。


   
   
  1. typedef struct pjmedia_port
  2. {
  3.     pj_ status_t ( *put_frame)(struct pjmedia_port  *this_port, 
  4.                  pjmedia_frame  *frame);
  5.     pj_ status_t ( * get_frame)(struct pjmedia_port  *this_port, 
  6.                  pjmedia_frame  *frame);
  7. } pjmedia_port;

两个接口根据需要定义(put与get是从会议桥视角看),若要从会议桥中接收媒体则实现put接口,若要向会议桥中发送媒体则实现get接口;若要收发媒体,则两个都要定义。

Conference内部有一个默认master_port(索引为0)的端口,用作与音频设备间的连接接口(被动被sound device调用):

  • 音频设备采集的PCM数据通过put_frame传递给会议桥;

  • 音频设备通过get_frame从会议桥获取PCM数据(各输入流mix后);

自定义媒体端口

只要端口满足pjmedia_port接口定义,就可以连接(pjsua_conf_connect)到会议桥。

获取媒体

获取媒体端口,只需定义put_frame即可,当会议桥中有媒体数据时,会调用此接口把媒体发送给此端口。


   
   
  1. static pj_ status_t listenPutFrame(pjmedia_port  *pPort, pjmedia_frame  *pFrame){
  2.    / /  get the frame, can be deal iter_swap
  3.    / / g_getFrammeCallback(pFrame);
  4.    return PJ_SUCCESS;
  5. }
  6. static pj_ status_t createListenPort(pjmedia_port  **ppPort){
  7.   auto locPort  = (pjmedia_port *)pj_pool_zalloc(g_pool, sizeof(pjmedia_port);
  8.    / / fill port-info
  9.   pj_str_t name  = pj_str( "pj.listen");
  10.   pjmedia_port_info_init( &locPort- >info,
  11.      &name,
  12.     PJMEDIA_SIG_ CLASS_PORT_AUD( "X""L");
  13.      16000/ / Sample-rate
  14.      1,     / / channels
  15.      16,    / /  Bit per sample
  16.      16000 *  20 /  1000  *  1);   / / samples per frame: sampleRate *MillSecPerPacket *Channel / 1000
  17.   locPort- >put_frame  =  &listenPutFrame;
  18.    / /  set port info,  if need
  19.   locPort- >port_ data.pdata  = pj_pool_alloca(g_pool, sizeof(YOU_ DATA));  / / this  is user-data
  20.    *ppPort  = locPort;
  21.    return PJ_SUCCESS;
  22. }

在listenPutFrame的参数pFrame中即为获取到的媒体数据,根据需要处理即可。

发送媒体

发送媒体端口,只需定义get_frame即可,会议桥会定时调用此接口来获取数据。


   
   
  1. static pj_ status_t speakGetFrame(pjmedia_port  *pPort, pjmedia_frame  *pMedia){
  2.    / / fill fram  to pMedia
  3.    / / pMedia- > type  = PJMEDIA_FRAME_ TYPE_AUDIO;
  4.    / /  Set  size  & buf
  5.    / / pMedia- >timestamp.u 64  =  0/ / the pjsip auto-deal the stamp
  6.    return PJ_SUCCESS;
  7. }
  8. static pj_ status_t createSpeakPort(pjmedia_port  **ppPort){
  9.   auto locPort  = (pjmedia_port *)pj_pool_zalloc(g_pool, sizeof(pjmedia_port);
  10.    / / fill port-info
  11.   pj_str_t name  = pj_str( "pj.speak");
  12.   pjmedia_port_info_init( &locPort- >info,
  13.      &name,
  14.     PJMEDIA_SIG_ CLASS_PORT_AUD( "X""S");
  15.      16000/ / Sample-rate
  16.      1,     / / channels
  17.      16,    / /  Bit per sample
  18.      16000 *  20 /  1000  *  1);   / / samples per frame: sampleRate *MillSecPerPacket *Channel / 1000
  19.   locPort- > get_frame  =  &speakGetFrame;
  20.    / /  set port info,  if need
  21.   locPort- >port_ data.pdata  = pj_pool_alloca(g_pool, sizeof(YOU_ DATA));  / / this  is user-data
  22.    *ppPort  = locPort;
  23.    return PJ_SUCCESS;
  24. }

创建与连接端口

通过上面定义的接口即可创建相应的端口,然后把创建的端口连接到会议桥中,即完成了媒体端口的创建工作。


   
   
  1. pjmedia_port  *g_listenMedia  =  NULL;
  2. pjsua_conf_port_id g_listenConf  = - 1;
  3. pjmedia_port  *g_speakMedia  =  NULL;
  4. pjsua_conf_port_id g_speakConf  = - 1;
  5. void ToStartMedia(pjsua_ call_info  &callInfo){
  6.   auto confPort  = callInfo.conf_slot;
  7.    / / listen port
  8.   auto nStatus  = createListenPort( &g_listenMedia);
  9.    if (!PjRetsuccess(nStatus)){
  10.     ...
  11.   }
  12.   nStatus  = pjsua_conf_ add_port(g_pool, g_listenMedia,  &g_listenConf);
  13.    if (!PjRetsuccess(nStatus)){
  14.     ...
  15.   }
  16.   nStatus  = pjsua_conf_connect(confPort, g_listenConf);  / / conf- >listen
  17.    if (!PjRetsuccess(nStatus)){
  18.     ...
  19.   }
  20.    / / speak port
  21.   nStatus  = createSpeakPort( &g_speakMedia);
  22.    if (!PjRetsuccess(nStatus)){
  23.     ...
  24.   }
  25.   nStatus  = pjsua_conf_ add_port(g_pool, g_speakMedia,  &g_speakConf);
  26.    if (!PjRetsuccess(nStatus)){
  27.     ...
  28.   }
  29.   nStatus  = pjsua_conf_connect(g_speakConf, confPort);  / / speak- >conf
  30.    if (!PjRetsuccess(nStatus)){
  31.     ...
  32.   }
  33.    / / ...
  34. }

此接口一般在媒体事件OnMediaState中,在媒体状态变为可用PJSUA_CALL_MEDIA_ACTIVE时调用。

端口停止

当通话完成时(OnCallState事件中,通话状态变为PJSIP_INV_STATE_DISCONNECTED),需要断开端口连接,并销毁端口。


   
   
  1. / / pjsua_conf_disconnect
  2. / / pjsua_conf_remove_port
  3. void ToStopMedia(pjsua_ call_info  &callInfo){
  4.   auto confPort  = callInfo.conf_slot;
  5.    if(g_listenConf ! = - 1){
  6.     pjsua_conf_disconnect(confPort, g_listenConf);
  7.     pjsua_conf_remove_port(g_listenConf);
  8.     g_listenConf  = - 1;
  9.   }
  10.    / / ... speak
  11.    / / destroy
  12.   pj_thread_sleep( 20);  / / wait fo media clear
  13.    if(g_listenMedia ! =  NULL){
  14.     pjmedia_port_destroy(g_listenMedia);
  15.     g_listenMedia  =  NULL;
  16.   }
  17.    / / ... speak
  18. }

辅助接口

端口创建等使用的内存,都通过内存池获取,所以需要提前创建内存池,并在完成时销毁。


   
   
  1. / / media
  2. pj_caching_pool g_cachingPool;
  3. pj_pool_t  *g_pool  =  NULL;
  4. void InitMedi a(){
  5.   pj_caching_pool_init( &g_cachingPool,  &pj_pool_ factory_ default_policy,  0);
  6.   g_pool  = pj_pool_create( &g_cachingPool. factory,
  7.      "pjTest",
  8.      4000,
  9.      4000,
  10.      NULL);
  11. }
  12. void ClearMedi a(){
  13.   pj_pool_safe_ release( &g_pool);
  14.   pj_caching_pool_destroy( &g_cachingPool);
  15. }

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值