pjsip 的采集与播放的流和会议桥的关系
欢迎进入q群761341723,大家一起讨论问题。hpng该网站为我自己网站,一些想法也会发到这里
上一篇文章我们已经对 pjsip 的通道实现有了基本的认识,下面我们来分析下 pjsip 中的流的流向问题,主要是流向到会议桥这个过程究竟如何生效的。
前言
在分析问题前,我们先要对会议桥(conference bridge)有个基本认识。
从上图可以看出,会议桥起到一个承上启下的作用,是媒体设备与媒体流之间的交换重点。它与媒体流和音频设备之间的数据传递都是通过pjmedia_port接口来实现的,该接口主要是在 pjmedia/src/pjmeida/port.c 中实现,其定义如下所示:
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;
很显然,从设备到会议桥有个 sound device port 的对于 pjmeida_port 的实现,这里需要我们关注下。
有了上面的基本认识后,我们再从媒体设备接收与发送流开始不断分析,一直到会议桥。
设备层的音频流回调
从上面的图我们发现,设备实现到设备抽象之间有个回调,这个回调函数的定义如下所示:
typedef pj_status_t (*pjmedia_aud_play_cb)(void *user_data,
pjmedia_frame *frame);
typedef pj_status_t (*pjmedia_aud_rec_cb)(void *user_data,
pjmedia_frame *frame);
Sound port 怎么与 conference bridge关联
我们通过前面的流程分析知道修改输入输出通道就涉及到 audio dev 的创建与释放,因而我们沿着创建与释放流程走一遍。
其中 pjsua_aud 的抽象程度更高,用于能够统合调用 sound port 和 conference(主要是会议桥的初始化就在这里完成)。
pjsua_set_snd_dev2 函数的定义位于对 audiodev 的更高级的抽象,方便 sip 这边调用的 pjsip/src/pjsua-lib/pjsua_aud.c 中,其中内容如下所示:
/*
* Select or change sound device. Application may call this function at
* any time to replace current sound device.
*/
PJ_DEF(pj_status_t) pjsua_set_snd_dev2(const pjsua_snd_dev_param *snd_param)
{
unsigned alt_cr_cnt = 1;
unsigned alt_cr[] = {
0, 44100, 48000, 32000, 16000, 8000};
unsigned i;
pj_status_t status = -1;
unsigned orig_snd_dev_mode = pjsua_var.snd_mode;
PJ_ASSERT_RETURN(snd_param, PJ_EINVAL);
PJ_LOG(4,(THIS_FILE, "Set sound device: capture=%d, playback=%d, mode=%d, "
"use_default_settings=%d",
snd_param->capture_dev, snd_param->playback_dev,
snd_param->mode, snd_param->use_default_settings));
pj_log_push_indent();
PJSUA_LOCK();
/* Check if there are no changes in sound device settings */
if (pjsua_var.cap_dev == snd_param->capture_dev &&
pjsua_var.play_dev == snd_param->playback_dev &&
pjsua_var.snd_mode == snd_param->mode)
{
/* If sound device is already opened, just print log and return.
* Also if PJSUA_SND_DEV_NO_IMMEDIATE_OPEN is set.
*/
if (pjsua_var.snd_is_on || (snd_param->mode &
PJSUA_SND_DEV_NO_IMMEDIATE_OPEN))
{
PJ_LOG(4,(THIS_FILE,"No changes in capture and playback devices"));
PJSUA_UNLOCK();
pj_log_pop_indent();
return PJ_SUCCESS;
}
}
/* No sound */
if (snd_param->capture_dev == PJSUA_SND_NO_DEV &&
snd_param->playback_dev == PJSUA_SND_NO_DEV)
{
PJSUA_UNLOCK();
PJ_LOG(4, (THIS_FILE, "No sound device, mode setting is ignored"));
if (!pjsua_var.no_snd)
pjsua_set_no_snd_dev();
pj_log_pop_indent();
return status;
}
/* Null-sound */
if (snd_param->capture_dev == PJSUA_SND_NULL_DEV &&
snd_param->playback_dev == PJSUA_SND_NULL_DEV)
{
PJSUA_UNLOCK();
PJ_LOG(4, (THIS_FILE, "Null sound device, mode setting is ignored"));
status = pjsua_set_null_snd_dev();
pj_log_pop_indent();
return status;
}
pjsua_var.snd_mode = snd_param->mode;
/* Just update the IDs if app does not want to open now and currently
* audio is off.
*/
if (!pjsua_var.snd_is_on &&
(snd_param->mode & PJSUA_SND_DEV_NO_IMMEDIATE_OPEN))
{
pjsua_var.cap_dev = snd_param->capture_dev;
pjsua_var.play_dev = snd_param->playback_dev;
pjsua_var.no_snd = PJ_FALSE;
PJSUA_UNLOCK();
pj_log_pop_indent();
return PJ_SUCCESS;
}
/* Set default clock rate */
alt_cr[0] = pjsua_var.media_cfg.snd_clock_rate;
if (alt_cr[0] == 0)
alt_cr[0] = pjsua_var.media_cfg.clock_rate;
/* Allow retrying of different clock rate if we're using conference
* bridge (meaning audio format is always PCM), otherwise lock on
* to one clock rate.
*/
if (pjsua_var.is_mswitch) {
alt_cr_cnt = 1;
} else {
alt_cr_cnt = PJ_ARRAY_SIZE(alt_cr);
}
/* Attempts to open the sound device with different clock rates */
for (i=0; i<alt_cr_cnt; ++i) {
pjmedia_snd_port_param param;
unsigned samples_per_frame;
/* Create the default audio param */
samples_per_frame = alt_cr[i] *
pjsua_var.media_cfg.audio_frame_ptime *
pjsua_var.media_cfg.channel_count / 1000;
pjmedia_snd_port_param_default(¶m);
param.ec_options = pjsua_var.media_cfg.ec_options;
status = create_aud_param(¶m.base, snd_param->capture_dev,
snd_param->playback_dev,
alt_cr[i], pjsua_var.media_cfg.channel_count,
samples_per_frame, 16,
snd_param->use_default_settings);
if (status != PJ_SUCCESS)
goto on_error;
/* Open! */
param.options = 0;
status = open_snd_dev(¶m);
if (status == PJ_SUCCESS)
break;
}
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to open sound device", status);
goto on_error;
}
pjsua_var.no_snd = PJ_FALSE;
pjsua_var.snd_is_on = PJ_TRUE;
PJSUA_UNLOCK();
pj_log_pop_indent();
return PJ_SUCCESS;
on_error:
pjsua_var.snd_mode = orig_snd_dev_mode;
PJSUA_UNLOCK();
pj_log_pop_indent();
return status;
}
这里最终的就是 open_snd_dev 这个函数,这个函数就是我们要分析的核心代码。
open_snd_dev
open_snd_dev 的函数位于 pjsip/src/pjsua-lib/pjsua_aud.c 中,其内容如下所示:
/* Open sound device with the setting. */
static pj_status_t open_snd_dev(pjmedia_snd_port_param *param)
{
pjmedia_port *conf_port;
pj_status_t status;
pj_bool_t speaker_only = (pjsua_var.snd_mode & PJSUA_SND_DEV_SPEAKER_ONLY);
PJ_ASSERT_RETURN(param, PJ_EINVAL);
/* Check if NULL sound device is used */
if (PJSUA_SND_NULL_DEV==param->base.rec_id ||
PJSUA_SND_NULL_DEV==param->base.play_id)
{
return pjsua_set_null_snd_dev();
}
/* Close existing sound port */
close_snd_dev();
/* Save the device IDs */
pjsua_var.cap_dev = param->base.rec_id;
pjsua_var.play_dev = param->base.play_id;
/* Notify app */
if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) {
(*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1);
}
/* Create memory pool for sound device. */
pjsua_var.snd_pool = pjsua_pool_create("pjsua_snd", 4000, 4000);
PJ_ASSERT_RETURN(pjsua_var.snd_pool, PJ_ENOMEM);
/* Setup preview callbacks, if configured */
if

最低0.47元/天 解锁文章
2095

被折叠的 条评论
为什么被折叠?



