1、pjsip 是否采用 srtp 来传输媒体流是由两方面控制的:
(1)、pjsip是否支持 srtp 协议:
在 pjsua_call_media_init 中首先判断是采用 ICE 还是 UDP:
if (pjsua_var.media_cfg.enable_ice) {
status = create_ice_media_transport(tcfg, call_med, async);
if (async && status == PJ_EPENDING) {
/* We will resume call media initialization in the
* on_ice_complete() callback.
*/
call_med->med_create_cb = &call_media_init_cb;
call_med->med_init_cb = cb;
return PJ_EPENDING;
}
} else {
status = create_udp_media_transport(tcfg, call_med);
}
在该函数最后调用 call_media_init_cb,而该函数中则会强制采用 SRTP 覆盖之前的设定:
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
/* This function may be called when SRTP transport already exists
* (e.g: in re-invite, update), don't need to destroy/re-create.
*/
if (!call_med->tp_orig) {
pjmedia_srtp_setting srtp_opt;
pjmedia_transport *srtp = NULL;
/* Check if SRTP requires secure signaling */
if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
if (security_level < acc->cfg.srtp_secure_signaling) {
err_code = PJSIP_SC_NOT_ACCEPTABLE;
status = PJSIP_ESESSIONINSECURE;
goto on_return;
}
}
/* Always create SRTP adapter */
pjmedia_srtp_setting_default(&srtp_opt);
srtp_opt.close_member_tp = PJ_TRUE;
/* If media session has been ever established, let's use remote's
* preference in SRTP usage policy, especially when it is stricter.
*/
if (call_med->rem_srtp_use > acc->cfg.use_srtp)
srtp_opt.use = call_med->rem_srtp_use;
else
srtp_opt.use = acc->cfg.use_srtp;
status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
call_med->tp,
&srtp_opt, &srtp);
if (status != PJ_SUCCESS) {
err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
goto on_return;
}
/* Set SRTP as current media transport */
call_med->tp_orig = call_med->tp;
call_med->tp = srtp;
}
#else
call_med->tp_orig = call_med->tp;
PJ_UNUSED_ARG(security_level);
#endif
由此可见,要禁用 SRTP,则必须将宏定义 PJMEDIA_HAS_SRTP 删除或定义为 0。
(2)、在传输媒体流的时候是否使用 SRTP:
srtp->bypass_srtp 是控制是否采用 SRTP 来传输媒体流的变量。首先,stream.c 中的函数 put_frame_imp 调用 pjmedia_transport_send_rtp,该函数中的回调函数(即 stream->transport->op->send_rtp)指向 transport_srtp.c 中的 transport_send_rtp 函数。当srtp->bypass_srtp = 1 的时候,transport_srtp.c 中的 transport_send_rtp 函数调用 pjmedia_transport_send_rtp,而此时该函数中的回调函数(即 stream->transport->member_tp->op->send_rtp)指向的就是 transport_udp.c 中的 transport_send_rtp 函数。因此,这种情况下并没有采用 SRTP 来传输媒体流,此时与直接使用 UDP 传输等效。
2、SRTP bypass 模式接收到对方媒体数据后的回调:
首先,stream.c 文件中的 pjmedia_stream_create 函数调用 pjmedia_transport_attach,该函数中的回调函数(即 tp->op->attach)指向 transport_srtp.c 中的 transport_attach,并将 stream.c 文件中的 on_rx_rtp 作为收到媒体数据后的回调传下去。
然后,transport_srtp.c 文件中的 transport_attach 函数调用 pjmedia_transport_attach,该函数中的回调函数(即 srtp->member_tp->op->attach)指向 transport_udp.c 中的 transport_attach,并将 transport_srtp.c 文件中的 srtp_rtp_cb 作为收到媒体数据后的回调传下去。
这样,当 transport_udp.c 收到对方的媒体数据的时候就会去调用 transport_srtp.c 文件中的 srtp_rtp_cb,而 srtp_rtp_cb 则会调用 srtp->rtp_cb。srtp->rtp_cb 指向的则是 stream.c 中的 on_rx_rtp。
即收到对方媒体数据后最终调用的回调函数是 stream.c 中的 on_rx_rtp。
3、windows平台下的声音采集和播放:
功能 | 实现函数 | 所在文件 | 调用函数 |
---|---|---|---|
注册设备 | pjmedia_aud_subsys_init | audiodev.c | pjmedia_endpt_create |
初始化设备 | factory_init | wmme_dev.c | init_driver |
启动设备 | factory_create_stream | wmme_dev.c | pjmedia_aud_stream_create |
启动媒体流 | stream_start | wmme_dev.c | pjmedia_aud_stream_start |
声音采集 | wmme_dev_thread | wmme_dev.c | 单独线程 |
声音播放 | wmme_dev_thread | wmme_dev.c | 单独线程 |
“启动设备”阶段,factory_create_stream 调用 init_player_stream 创建声音回放的内核事件,调用 init_capture_stream 创建声音采集的内核事件,而 wmme_dev_thread 中对这些事件进行循环监听,监听到有内核事件发生(声音回放、采集或退出监听线程)后就执行该事件。
退出监听线程事件也是在 factory_create_stream 中创建的,相应代码为:
/* Create the stop event */
strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
退出监听线程事件的触发是在 stream_destroy 中完成的,相应代码为:
SetEvent(stream->thread_quit_event);