pjsip视频

一、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;
    }
}

十、解码
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));
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
根据提供的引用内容,我无法直接回答您的问题。但是,我可以为您提供一些有用的信息。 首先,Qt是一个跨平台的C++应用程序开发框架,可以用于开发桌面应用程序、移动应用程序和嵌入式系统。OpenCV是一个开源计算机视觉库,可以用于图像处理、计算机视觉和机器学习等领域。PJSIP是一个开源的SIP协议栈,可以用于实现VoIP应用程序。 如果您想使用Qt和OpenCV抓取PJSIP视频流,您可以考虑使用以下步骤: 1.使用PJSIP实现SIP协议栈,建立SIP会话并获取视频流。 2.使用OpenCV读取视频流,并进行必要的图像处理。 3.使用Qt将处理后的图像显示在界面上。 下面是一个简单的示例代码,演示如何使用Qt和OpenCV抓取PJSIP视频流: ```cpp #include <QApplication> #include <QLabel> #include <opencv2/opencv.hpp> #include <pjsua2.hpp> using namespace cv; using namespace pj; class MyVideoWindow : public VideoWindow { public: MyVideoWindow() : VideoWindow() {} virtual void onVideoUpdate(VideoFrame &frame) { Mat img(frame.height, frame.width, CV_8UC3, frame.data[0], frame.stride[0]); cvtColor(img, img, COLOR_YUV2BGR_I420); imshow("Video", img); waitKey(1); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); // 初始化PJSUA2 Endpoint ep; ep.libCreate(); EpConfig ep_cfg; ep.libInit(ep_cfg); // 创建SIP账号 AccountConfig acc_cfg; acc_cfg.idUri = "sip:[email protected]"; acc_cfg.regConfig.registrarUri = "sip:domain.com"; acc_cfg.sipConfig.proxies = {"sip:proxy.domain.com"}; acc_cfg.authCreds.push_back(AuthCredInfo("digest", "*", "username", 0, "password")); MyAccount *acc = new MyAccount; acc->create(acc_cfg); // 创建视频窗口 MyVideoWindow *vw = new MyVideoWindow; VideoWindowHandle vwh = (VideoWindowHandle)vw; // 创建SIP会话 CallOpParam prm; Call *call = new Call; call->makeCall("sip:[email protected]", prm, vwh); // 开始Qt事件循环 return app.exec(); } ``` 请注意,这只是一个简单的示例代码,您需要根据您的具体需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值