使用pjsip传输已经编码的视频,源码在github

简介: pjsip功能很强,做sip rtp语音通话库首选。在2.0之后,也支持视频。不过,它的视频功能缺省是从视频设备采集,然后进行编译,再发送出去的。假设,我们已经有了视频源,比如IP摄像机,不需要采集和编码这个过程,怎么处理呢?假设我们采用pjsip附带的pjsua为例。

pjsip功能很强,做sip rtp语音通话库首选。在2.0之后,也支持视频。不过,它的视频功能缺省是从视频设备采集,然后进行编译,再发送出去的。假设,我们已经有了视频源,比如IP摄像机,不需要采集和编码这个过程,怎么处理呢?假设我们采用pjsip附带的pjsua为例。

通常的方法:
1 把视频源当然文件来处理,sample有。不过这种方法用的不多。
2 修改vid_stream.c,在put_frame和get_frame里,换上我们自己的视频源。这种方法使用的最多,很多人在1.x版本里支持视频,就用这种方法。
3 本文采用的:重新构造sdp,自己创建rtp通道。

前两个方法思路直接,代码量都不小,尤其是第二种,需要修改pj底层代码。
如果基于pjsua做一个简单的视频通信,可以采用本文中的方法。其实代码量也不小,不过sample提供了参考,实现起来也比较容易。下面简单说明:

1 关键点在sdp上,pjsua_call_make_call这个函数非常方便,直接呼叫对方。不过它在底层做了太多工作,比如启动了声卡。而不用这个函数,直接用比较底层的pjsip_inv_send_msg,自己处理的工作相对比较多(但不难,不过这样就不需要pjsua这个现成的程序了,所以我们继续用pjsua_call_make_call)。
不过还好,pj库提供了大量的回调,其中一个:on_call_sdp_created,就是在创建sdp后回调上来,由我们自己再修改。比如我们自己定义rtp的端口g_local_port。

void on_call_sdp_created(pjsua_call_id call_id,
                                   pjmedia_sdp_session *sdp,
                                   pj_pool_t *pool,
                                   const pjmedia_sdp_session *rem_sdp)
{
int nPort;
if (sdp != NULL)
{

pjmedia_sdp_media *m = sdp->media[sdp->media_count-1];
m->desc.port = g_local_port;


pjmedia_sdp_conn *c = sdp->conn;
char* addr;
if (c)
addr= c->addr.ptr;
else
{

const pj_str_t *hostname;
pj_sockaddr_in tmp_addr;
char *addr;

hostname = pj_gethostname();
pj_sockaddr_in_init(&tmp_addr, hostname, 0);
addr = pj_inet_ntoa(tmp_addr.sin_addr);
sdp->conn = (pjmedia_sdp_conn *)pj_pool_zalloc (pool, sizeof(pjmedia_sdp_conn));
sdp->conn->net_type = pj_str("IN");
sdp->conn->addr_type = pj_str("IP4");
sdp->conn->addr = pj_str(addr);
}

sdp->origin.addr = *pj_gethostname();
}
}

同样,这里还可以修改payload type等。

这是发起呼叫时的,接收方收到后的回应之后,也会触发这个回调,自己设定RTP端口,payload type就可以了。


呼叫成功后,双方建立起连接关系,这时需要传rtp数据了。pjsua把这些工作都放在底层了,不做任何修改,只需要在发送和接收时,自己做一些处理就行。
先说接收方(参考siprtp.c源码):
pj_status_t init_local_rtp()
{
if (m_bInitMedia)
{
destroy_media();
}


//g_local_port = local_port;


pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
    pool = pj_pool_create(&(cp.factory), "test", 1000, 512, NULL);


int status;

//status = pjmedia_endpt_create(&cp.factory,  pjsip_endpt_get_ioqueue(pjsua_get_pjsip_endpt()), 0, &med_endpt);


status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);


status = pjmedia_rtp_session_init(&video.out_sess, 97, pj_rand());
status = pjmedia_rtp_session_init(&video.in_sess, 97, 0);


status = pjmedia_transport_udp_create(med_endpt, NULL, g_local_port,  0, &video.transport);


m_bInitMedia = true;
video.active = true;
return 0;
}

这段代码是本地启动rtp一个端口用来接收视频数据。
然后,从sdp得到对方发送的ip和端口,调用pjmedia_transport_attach,建立关联就可以了。


发送方同样调用上面函数,初始化本地端口,但不需要pjmedia_transport_attach。(假定视频传输是单方向的)
发送动作就比较简单了,先把要发的数据分包,大小不超过1400,然后pjmedia_rtp_encode_rtp,再pjmedia_transport_send_rtp。


上面还没有讲接收方是怎么接数据的,这里也用到了pj提供的回调机制:
status = pjmedia_transport_attach(video.transport, &video, 
//&info.rem_addr, 
&remote_addr,
NULL, 
sizeof(pj_sockaddr_in),
&on_rx_rtp,
NULL);

这里面的on_rx_rtp就是接收RTP的回调。

video.transport等定义:


struct media_stream
{
    /* Static: */
    unsigned call_index;    /* Call owner. */
    unsigned media_index;    /* Media index in call. */
    pjmedia_transport   *transport;    /* To send/recv RTP/RTCP */


    /* Active? */
    pj_bool_t active;    /* Non-zero if is in call. */


    /* Current stream info: */
    pjmedia_stream_info si;    /* Current stream info. */


    /* More info: */
    unsigned clock_rate;    /* clock rate */
    unsigned samples_per_frame; /* samples per frame */
    unsigned bytes_per_frame;   /* frame size. */


    /* RTP session: */
    pjmedia_rtp_session out_sess;    /* outgoing RTP session */
    pjmedia_rtp_session in_sess;    /* incoming RTP session */


    /* RTCP stats: */
    pjmedia_rtcp_session rtcp;    /* incoming RTCP session. */


    /* Thread: */
    pj_bool_t thread_quit_flag;  /* Stop media thread. */
    pj_thread_t *thread;    /* Media thread. */
};

struct media_stream video;


源码在:https://github.com/sxcong/pjsipvideo_demo
视频源RTSP,可以直接使用IPCAM,比如海康摄像机。
SIP SERVER是开源的resiprocate,编译出来可直接使用。
DEMO程序是vc2008写的,包括SIP的登录,发送请求,发送和接收视频并解码播放。可在同一台机器上运行两个实例测试。
不过毕竟是DEMO,只是演示怎么使用,细节还有很多问题需要修改。

 

from:http://blog.chinaunix.net/uid-15063109-id-4445165.html

目录
相关文章
|
7月前
|
设计模式 Java 程序员
太爆了!阿里最新出品2023版JDK源码学习指南,Github三天已万赞
最近后台收到很多粉丝私信,说的是程序员究竟要不要去读源码?当下行情,面试什么样的薪资/岗位才会被问到源码? 对此,我的回答是:一定要去读,并且要提到日程上来! 据不完全统计,现在市面上不管是初级,中级,还是高级岗,面试的时候都有可能会问到源码中的问题,它已经成为程序员常规必备的一个技术点。如果你当下想通过一个面试,或者想把中级薪资要到相对于比较高的话,源码这块就必须要会。
92 0
|
4月前
|
安全 Java 程序员
火爆全网的Spring Security手册及源码笔记,在Github上标星103K
Spring Security 是一个基于 Spring AOP 和 Servlet 过滤器的安全框架,它提供了安全性方面的解决方案
|
4月前
|
前端开发 IDE JavaScript
【源码共读之omit.js】如何在线使用github
【源码共读之omit.js】如何在线使用github
39 0
|
4月前
|
缓存 开发工具 git
GitHub标星36k+的超火高质量Git中文教程,源码、实战一把抓
高质量的 Git 中文教程,源于国外社区的优秀文章和个人实践
|
4月前
|
Java 容器
膜拜!清华大佬手撸多线程并发源码笔记Github上线3天星标35k+
你为什么要学习多线程?是因为理想吗?是因为热爱吗? 哦~原来是为了面试打基础、做准备啊!没错,这真的很现实!
膜拜!清华大佬手撸多线程并发源码笔记Github上线3天星标35k+
|
5月前
|
小程序 开发工具 iOS开发
Github小程序客户端源码-Gitter
Github小程序客户端源码-Gitter
52 0
|
5月前
|
Dubbo Java 应用服务中间件
制霸GitHub热榜的Spring Cloud Alibaba源码笔记,果然是阿里传出的
6年前面试最常问的并且可以顺利拿到高薪的技能是 Dubbo 2年前面试,只要你简历上有Spring Cloud 项目的相关经验,肯定会打动面试官,现在呢?恐怕简历上有Dubbo和简单的Spring Cloud技术和经验是无法让面试官高看你的。
|
5月前
|
设计模式 Java 程序员
又搞事!阿里员工竟把上司的400页JDK并发源码指南拿去GitHub置顶
不知道大家闲下来的时候有没有去阅读过JDK源码,虽然看源码有点找虐的感觉,但作为一名程序员,在闲暇时看一下jdk源码,有利于对自己日常的开发环境了解得更加深刻。而且还有以下五点好处。
|
5月前
|
存储 算法 安全
“字节”再次起跳!内部651页剖析HotSpot 源码手册,GitHub开源
阿嘴记得2年前开始研究Java虚拟机时,查遍了网上所有的资料,凡是能遇到一点好的关于Java虚拟机的资料,我都会认真读、一遍读不懂没关系,阿嘴都会搬到自己的日志中记录下来,后面就是对这些日志进行分类整理,然后加上我的理解后不断加工,最后内化成自己的东西。现在市面上系统介绍Java虚拟机的书籍非常少,细致全面的书籍更是市场空白,所以我把我这几年的所学写成了这本“深入剖析Java虚拟机源码剖析与实例详解(基础卷)”,本书以通俗易懂的语言详细介绍Java虚拟机HotSpot的源码实现,可以帮助读者系统掌握类的生命周期和垃圾回收等基本组件的相关知识。对于想全面了解HotSpot虚拟机工作原理和源码实现
|
7月前
|
安全 Java 数据安全/隐私保护
爆肝了!阿里最新版的这份Spring Security源码手册,狂揽GitHub榜首
写在前面 自从 Spring Boot、Spring Cloud 火起来之后,Spring Security 也跟着沾了一把光! 其实我一直觉得 Spring Security 是一个比 Shiro 优秀很多的权限管理框架,但是重量级、配置繁琐、门槛高这些问题一直困扰着 Spring Security 的开发者,也让很多初学者望而却步。直到 Spring Boot 横空出世,这些问题统统都得到缓解。 在 Spring Boot 或者 Spring Cloud 中,如果想选择一个权限管理框架,几乎毫无疑问的选择 Spring Security,Shiro 在这个环境下已经不具备优势了。 但
118 0