会议桥
自定义媒体端口
在《pjSIP注册呼叫流程简介》中介绍了pjSIP注册与呼叫的基本流程,本节对自定义媒体流与端口做下介绍。
会议桥
pjSIP中通过会议桥(Conference)把媒体流(Stream)与抽象音频设备端口(Sound Device Port)连接起来(并负责各路媒体的混流);他们之间数据传递都是通过pjmedia_port接口来实现的。若要收发自定义的媒体数据,只需连接会议桥,从中接收或发送媒体数据即可。
-
typedef struct pjmedia_port
-
{
-
pj_
status_t (
*put_frame)(struct pjmedia_port
*this_port,
-
pjmedia_frame
*frame);
-
-
pj_
status_t (
*
get_frame)(struct pjmedia_port
*this_port,
-
pjmedia_frame
*frame);
-
} 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
即可,当会议桥中有媒体数据时,会调用此接口把媒体发送给此端口。
-
static pj_
status_t listenPutFrame(pjmedia_port
*pPort, pjmedia_frame
*pFrame){
-
/
/
get the frame, can be deal iter_swap
-
/
/ g_getFrammeCallback(pFrame);
-
-
return PJ_SUCCESS;
-
}
-
-
static pj_
status_t createListenPort(pjmedia_port
**ppPort){
-
auto locPort
= (pjmedia_port
*)pj_pool_zalloc(g_pool, sizeof(pjmedia_port);
-
-
/
/ fill port-info
-
pj_str_t name
= pj_str(
"pj.listen");
-
pjmedia_port_info_init(
&locPort-
>info,
-
&name,
-
PJMEDIA_SIG_
CLASS_PORT_AUD(
"X",
"L");
-
16000,
/
/ Sample-rate
-
1,
/
/ channels
-
16,
/
/
Bit per sample
-
16000
*
20
/
1000
*
1);
/
/ samples per frame: sampleRate
*MillSecPerPacket
*Channel
/
1000
-
locPort-
>put_frame
=
&listenPutFrame;
-
-
/
/
set port info,
if need
-
locPort-
>port_
data.pdata
= pj_pool_alloca(g_pool, sizeof(YOU_
DATA));
/
/ this
is user-data
-
-
*ppPort
= locPort;
-
return PJ_SUCCESS;
-
}
在listenPutFrame的参数pFrame中即为获取到的媒体数据,根据需要处理即可。
发送媒体
发送媒体端口,只需定义get_frame
即可,会议桥会定时调用此接口来获取数据。
-
static pj_
status_t speakGetFrame(pjmedia_port
*pPort, pjmedia_frame
*pMedia){
-
/
/ fill fram
to pMedia
-
/
/ pMedia-
>
type
= PJMEDIA_FRAME_
TYPE_AUDIO;
-
/
/
Set
size
& buf
-
/
/ pMedia-
>timestamp.u
64
=
0;
/
/ the pjsip auto-deal the stamp
-
-
return PJ_SUCCESS;
-
}
-
-
static pj_
status_t createSpeakPort(pjmedia_port
**ppPort){
-
auto locPort
= (pjmedia_port
*)pj_pool_zalloc(g_pool, sizeof(pjmedia_port);
-
-
/
/ fill port-info
-
pj_str_t name
= pj_str(
"pj.speak");
-
pjmedia_port_info_init(
&locPort-
>info,
-
&name,
-
PJMEDIA_SIG_
CLASS_PORT_AUD(
"X",
"S");
-
16000,
/
/ Sample-rate
-
1,
/
/ channels
-
16,
/
/
Bit per sample
-
16000
*
20
/
1000
*
1);
/
/ samples per frame: sampleRate
*MillSecPerPacket
*Channel
/
1000
-
locPort-
>
get_frame
=
&speakGetFrame;
-
-
/
/
set port info,
if need
-
locPort-
>port_
data.pdata
= pj_pool_alloca(g_pool, sizeof(YOU_
DATA));
/
/ this
is user-data
-
-
*ppPort
= locPort;
-
return PJ_SUCCESS;
-
}
创建与连接端口
通过上面定义的接口即可创建相应的端口,然后把创建的端口连接到会议桥中,即完成了媒体端口的创建工作。
-
pjmedia_port
*g_listenMedia
=
NULL;
-
pjsua_conf_port_id g_listenConf
= -
1;
-
pjmedia_port
*g_speakMedia
=
NULL;
-
pjsua_conf_port_id g_speakConf
= -
1;
-
-
void ToStartMedia(pjsua_
call_info
&callInfo){
-
auto confPort
= callInfo.conf_slot;
-
-
/
/ listen port
-
auto nStatus
= createListenPort(
&g_listenMedia);
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
nStatus
= pjsua_conf_
add_port(g_pool, g_listenMedia,
&g_listenConf);
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
nStatus
= pjsua_conf_connect(confPort, g_listenConf);
/
/ conf-
>listen
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
-
/
/ speak port
-
nStatus
= createSpeakPort(
&g_speakMedia);
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
nStatus
= pjsua_conf_
add_port(g_pool, g_speakMedia,
&g_speakConf);
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
nStatus
= pjsua_conf_connect(g_speakConf, confPort);
/
/ speak-
>conf
-
if (!PjRetsuccess(nStatus)){
-
...
-
}
-
-
/
/ ...
-
}
此接口一般在媒体事件OnMediaState
中,在媒体状态变为可用PJSUA_CALL_MEDIA_ACTIVE
时调用。
端口停止
当通话完成时(OnCallState
事件中,通话状态变为PJSIP_INV_STATE_DISCONNECTED
),需要断开端口连接,并销毁端口。
-
/
/ pjsua_conf_disconnect
-
/
/ pjsua_conf_remove_port
-
void ToStopMedia(pjsua_
call_info
&callInfo){
-
auto confPort
= callInfo.conf_slot;
-
-
if(g_listenConf !
= -
1){
-
pjsua_conf_disconnect(confPort, g_listenConf);
-
pjsua_conf_remove_port(g_listenConf);
-
g_listenConf
= -
1;
-
}
-
/
/ ... speak
-
-
/
/ destroy
-
pj_thread_sleep(
20);
/
/ wait fo media clear
-
if(g_listenMedia !
=
NULL){
-
pjmedia_port_destroy(g_listenMedia);
-
g_listenMedia
=
NULL;
-
}
-
/
/ ... speak
-
}
辅助接口
端口创建等使用的内存,都通过内存池获取,所以需要提前创建内存池,并在完成时销毁。
-
/
/ media
-
pj_caching_pool g_cachingPool;
-
pj_pool_t
*g_pool
=
NULL;
-
-
void InitMedi
a(){
-
pj_caching_pool_init(
&g_cachingPool,
&pj_pool_
factory_
default_policy,
0);
-
g_pool
= pj_pool_create(
&g_cachingPool.
factory,
-
"pjTest",
-
4000,
-
4000,
-
NULL);
-
}
-
-
void ClearMedi
a(){
-
pj_pool_safe_
release(
&g_pool);
-
pj_caching_pool_destroy(
&g_cachingPool);
-
}