tjyuanxi
2021-07-26 19:36
采纳率: 50%
浏览 95

pjsip 怎么实现单向视频通话? vid_out_auto_transmit 这个参数好像不管用?

各位道友,最近在搞pjsip的项目,我用的是pjsip2.10版本。视频通话已经调通了,但是遇到这么一个需求,在某些情况下需要无论拨打还是接听电话,都只能接受对端的视频,不能将自己的视频发送出去。
我看官方文档,只需要要将这个参数AccountVideoConfig::autoTransmitOutgoing/vid_out_auto_transmit 设置为false 就可以了,这个参数的意思是不自动发送视频到对端。 但是我将它设置为false后,视频通话接通后,对端过来的视频数据也没有show出来了,show出来视频为绿色幕布,也就是没有数据。到处咨询都找不到原因,又谁知道啊? 如果本端没有视频,这参数即使设置为true,也是没有问题,可以看到对端视频。
下面是我的设置:

m_aCfg.videoConfig.autoShowIncoming = true;
m_aCfg.videoConfig.autoTransmitOutgoing = false;
m_aCfg.videoConfig.defaultCaptureDevice = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
m_aCfg.videoConfig.defaultRenderDevice = PJMEDIA_VID_DEFAULT_RENDER_DEV;

我的日志:

img

img

img

img

img

img
img

  • 点赞
  • 收藏

1条回答 默认 最新

  • 只是个小学生 2021-07-26 20:28
    已采纳

    一、pjsip 2.0只支持ffmpeg中的视频编码器。如果要添加额外的编码器,可以参考
    https://bitbucket.org/secollab/pjsip-mikey-sakke/commits/c865a04598b27e6eaafe4e16bf8610dd5bb38551
    以添加vp8编码器为例的

    二、呼叫是否使用视频,pjsua命令 vid enable改变的是app_config.vid.vid_cnt。这个值会影响call setting
    在 make_call 的时候,
    /* Update call setting */
    pjsua_call_setting_default(&call_opt);
    call_opt.aud_cnt = app_config.aud_cnt;
    call_opt.vid_cnt = app_config.vid.vid_cnt;

    在接电话的时候
    if (app_config.auto_answer > 0) {
    pjsua_call_setting call_opt;

    pjsua_call_setting_default(&call_opt);
    call_opt.aud_cnt = app_config.aud_cnt;
    call_opt.vid_cnt = app_config.vid.vid_cnt;

    pjsua_call_answer2(call_id, &call_opt, app_config.auto_answer, NULL,
    NULL);
    }

    三、编码器编码参数的设置,发生在
    pjsua_media_channel_update 调用
    pjmedia_vid_stream_info_from_sdp 调用
    get_video_codec_info_param 调用
    pjmedia_vid_codec_mgr_get_default_param,获取codec默认编码参数。codec的默认编码参数是放在ffmpeg_codec_desc codec_desc这个数组中的。
    然后根据remote sdp中的 b=TIAS 调整编码码率,使编码码率不超过对方的偏好获取后的编码参数放在pjmedia_vid_stream_info the_si结构体中,直至运行到pjmedia_vid_stream_create 的时候,参数会写入call_med->strm.v.stream(pjmedia_vid_stream_create的最后一个参数)。想修改视频编码的默认参数,可以参考vid_handle_menu

    四、采集参数的设置
    pjsua_media_channel_update 调用
    pjsua_vid_channel_update 调用
    pjmedia_vid_stream_create 调用
    pjmedia_vid_codec_init和pjmedia_vid_codec_open(
    (*codec->op->open)(codec, param);编解码库中实现,如Ffmpeg_vid_codecs.c、Pj_vpx.c、Openh264.cpp) 调用
    pjmedia_vid_stream_get_port 获得call_med->strm.v.stream里面指定的video的format

    在create_vid_win里面
    if (fmt)
    vp_param.vidparam.fmt = *fmt;
    media_port的fmt会覆盖掉采集器默认的参数

    create_vid_win 调用
    pjmedia_vid_dev_stream_create 调用
    f->op->create_stream 创建采集源

    五、通过SDP,与对方解码能力相匹配
    ffmpeg_codec_open 时会调用
    open_ffmpeg_codec 调用
    h264_preopen 调用
    pjmedia_vid_codec_h264_apply_fmtp,更改自身的编码参数以适应对方SDP中声明的profile-level-id。

    find_highest_res 负责具体计算编码的分辨率以适应profile level。先根据当前的设置的编码分辨率,计算出aspect ratio。然后根据当前的帧率和level的限制,计算出每帧最多可以有多少个mb。然后再根据mb的数量和原来的aspect ratio,推算出宽高

    h264每个level对应的最大mbs,码率等参数,在 const h264_level_info_t level_info[] 表中定义
    csipsimple在设置完分辨率参数之后,会自动设置profile-level-id,具体参考codec_h264_set_profile函数
    http://code.google.com/p/csipsimple/source/browse/trunk/CSipSimple/jni/csipsimple-wrapper/src/csipsimple_codecs_utils.c

    六、video port
    一次视频通话需要创建三个video port。第一个是回显对方的视频,第二个是本地采集源,第三个是本地采集的预览。

    vp->stream_role 分为主动型和被动型。主动型video port自己有回调函数,在获取到帧数据时可以自动回调strm->vid_cb.capture_cb,把采集的的帧传给pjsip。一般的采集源都有自己的线程在驱动,都属于主动型。只有colorbar、avi_dev是属于被动型采集源,因为帧都不是实时采集回来的,不需要独立的线程来采集

    七、分辨率的转换
    当采集源的分辨率或者图像格式与编码器不一致时,需要调用转换器进行格式转换。相关代码实现主要在vid_tee.c中,涉及的函数包括 pjmedia_vid_tee_add_dst_port2, tee_put_frame。目前pjsip后端只有一种converter,是使用ffmpeg的converter_libswscale.c
    一个采集源一般要创建两个tee的dst port,一个创建在create_vid_win,连接到preview窗口;另一个创建调用在pjsua_vid_channel_update,连接到编码器上面

    八、通话过程中通过INFO来请求对方发送I帧
    发送方在 call_media_on_event ,接收到PJMEDIA_EVENT_KEYFRAME_MISSING事件后,通过调用 pjsua_call_send_request 会发送的INFO消息给对方,INFO里面带的数据是application/media_control+xml。接收方处理该事件是在 pjsua_call_on_tsx_state_changed,里面有一串条件判断,如果是INFO消息,而且是application/media_control+xml,会调用 pjsua_media_apply_xml_control。pjsua_media_apply_xml_control 里面调用 pjmedia_vid_stream_send_keyframe 控制发送I帧

    九、获取通话中是否带有视频
    判断来电是否包含视频,on_incoming_call里面
    调用
    pjsua_call_get_info(call_id, &call_info);
    获取通话信息
    if (call_info.rem_offerer && call_info.rem_vid_cnt) {
    // 有视频
    }

    判断被叫是否开启了视频,on_call_state 里面
    if (call_info.state == PJSIP_INV_STATE_CONFIRMED)
    {
    pj_bool_t has_video = PJ_FALSE;

    if (call_info.media_cnt == 2
        && call_info.media[1].type == PJMEDIA_TYPE_VIDEO  
        && call_info.media[1].dir != PJMEDIA_DIR_NONE)
    {
        has_video = PJ_TRUE;
    }
    

    }
    1
    2
    3
    4
    5
    6
    7
    8
    十、解码
    on_rx_rtp,当stream->dec_frame.size == 0时,也就是没有已解码好的帧时,会尝试调用decode_frame 解码一帧数据。从另一方面来说,pjsip解码和接收rtp数据是在同一个线程中完成的,因此解码速度不够快会导致丢包。

    decode_frame 不断从jitter buf中取出帧数据,看是否jitter buf中是否包含两种不同时间戳的帧。如果有,认为前面一个时间戳的所有数据帧都已经发送完毕,开始解码。

    ffmpeg_codec_decode 负责具体的解码。它会调用ffmpeg_unpacketize来对RFC 3984的RTP格式进行解码,重新提取出每个nal。

    回显有自己一个独立的clock_thread。该线程最终会调用vid_stream.c中的get_frame,从buffer中取走解码完的数据帧,并重新把stream->dec_frame.size置0。

    目前pjsip的实现,实际上不会出现FU-A的拆包或者STEP-A的聚包。因为h264_preopen函数里面,已经写死了
    /* Better always send in single NAL mode for better compatibility /
    pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL;
    因此不会出现拆包的情况。但是它又是如何保证NAL的大小不会超过MTU的呢?具体在这里
    /
    Limit NAL unit size as we prefer single NAL unit packetization */
    if (!AV_OPT_SET_INT(ctx->priv_data, “slice-max-size”, ff->param.enc_mtu))
    {
    PJ_LOG(3, (THIS_FILE, “Failed to set H264 max NAL size to %d”,
    ff->param.enc_mtu));
    }

    点赞 打赏 评论

相关推荐 更多相似问题