pjsip 的采集与播放的流和会议桥的关系

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(&param);
        param.ec_options = pjsua_var.media_cfg.ec_options;
        status = create_aud_param(&param.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(&param);
        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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值