Doubango ims 框架 分析之 多媒体部分
序言
RTP提供带有实时特性的端对端数据传输服务,传输的数据如:交互式的音频和视频。那些服务包括有效载荷类型定义,序列号,时间戳和传输监测控制。应用程序在UDP上运行RTP来使用它的多路技术和checksum服务。2种协议都提供传输协议的部分功能。
RTP本身没有提供任何的机制来确保实时的传输或其他的服务质量保证,而是由低层的服务来完成。它不保证传输或防止乱序传输,它不假定下层网络是否可靠,是否按顺序传送数据包。RTP包含的序列号允许接受方重构发送方的数据包顺序,但序列号也用来确定一个数据包的正确位置,例如,在视频解码的时候不用按顺序的对数据包进行解码。
介绍
doubango框架中tinyRTP文件夹实现RTP/RTCP/RTSP协议栈,目前只实现了
RTP,RTCP;RTSP还没实现。Rtp用来在网络上传输音频视频,协议栈实现时主要在音视频包的封装,拆包。
rtp包由消息头及消息体组成,消息头的结构封装
typedefstruct trtp_rtp_header_s 文件trtp_rtp_header.h
{
TSK_DECLARE_OBJECT;
/*RFC 3550 section 5.1 - RTP Fixed Header Fields
0 1 2 3
01 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
unsignedversion:2;
//版本(V):2比特 此域定义了RTP的版本。此协议定义的版本是2。(值1被RTP草案版本使用,值0用在最初"vat"语音工具使用的协议中。)
unsignedpadding:1;
//填充(P):1比特 若填料比特被设置,则此包包含一到多个附加在末端的填充比特,填充比特不算作负载的一部分。填充的最后一个字节指明可以忽略多少个填充比特。填充可能用于某些具有固定长度的加密算法,或者用于在底层数据单元中传输多个RTP包。
unsignedextension:1;
//扩展
//扩展(X):1比特 若设置扩展比特,固定头(仅)后面跟随一个头扩展。
unsignedcsrc_count:4;
unsignedmarker:1;
//标志位
unsignedpayload_type:7;
//负载类型,即承载的语音编码类型
//负载类型(PT):7比特 此域定义了负载的格式,由具体应用决定其解释。协议可以规定负载类型码和负载格式之间一个默认的匹配。其他的负载类型码可以通过非RTP方法动态定义。RTP发送端在任意给定时间发出一个单独的RTP负载类型;此域不用来复用不同的媒体流。
uint16_tseq_num;
//序列号,重新组包
//序列号(sequencenumber):16比特 每发送一个RTP数据包,序列号加1,接收端可以据此检测丢包和重建包序列。序列号的初始值是随机的(不可预测),以使即便在源本身不加密时(有时包要通过翻译器,它会这样做),对加密算法泛知的普通文本攻击也会更加困难。
uint32_ttimestamp;
//时间戳,负责流同步
uint32_tssrc;
//同步源标识,32比特 用以识别同步源。标识符被随机生成,以使在同一个RTP会话期中没有任何两个同步源有相同的SSRC识别符。尽管多个源选择同一个SSRC识别符的概率很低,所有RTP实现工具都必须准备检测和解决冲突。若一个源改变本身的源传输地址,必须选择新的SSRC识别符,以避免被当作一个环路源。
uint32_tcsrc[15];
//贡献源标识
}
trtp_rtp_header_t;
rtp包结构,文件trtp_rtp_packet.h
typedefstruct trtp_rtp_packet_s
{
TSK_DECLARE_OBJECT;
trtp_rtp_header_t*header; //包头
struct{
void*data;
constvoid* data_const;
tsk_size_tsize;
}payload; //负载,即承载内容
/*extension header as per RFC 3550 section 5.3.1 */
struct{
void*data;
tsk_size_tsize; /* contains the first two 16-bit fields */
}extension;
}
trtp_rtp_packet_t;
rtp包的控制
上面两个结构用来标示一个rtp包,同时提供了包的解析,创建等函数。
结构trtp_manager_s负责rtp.rtcp包的管理,是更高层的抽象,上层应用直接通过trtp_manager_s提供的api控制
rtp包,比如在网络上发送音频数据,在音频session结构中包含trtp_manager_s用来管理经过封装的rtp包。
/**RTP/RTCP manager */
typedefstruct trtp_manager_s
{
TSK_DECLARE_OBJECT;
struct{
uint16_tseq_num;
uint32_ttimestamp;
uint32_tssrc;
uint8_tpayload_type;
char*remote_ip;
tnet_port_tremote_port;
structsockaddr_storage remote_addr;
char*public_ip;
tnet_port_tpublic_port;
constvoid* callback_data;
trtp_manager_rtp_cb_fcallback;
}rtp;
struct{
char*remote_ip;
tnet_port_tremote_port;
structsockaddr_storage remote_addr;
tnet_socket_t*local_socket;
char*public_ip;
tnet_port_tpublic_port;
constvoid* callback_data;
trtp_manager_rtcp_cb_fcallback;
}rtcp;
char*local_ip;
tsk_bool_tipv6;
tsk_bool_tstarted;
tsk_bool_tenable_rtcp;
tsk_bool_tsocket_disabled;
tnet_transport_t*transport;
}
trtp_manager_t;
tdav是音视频会话的抽象层,负责传输层的启动,音频会话,视频会话,各种编码的注册。
对于音频/视频会话(session)被tmedia_session_mgr_t管理,而tmedia_session_mgr_t则具体由sip信令控制会话的状态。比如sip客户端请求时通过tmedia_session_mgr_t构造自己的sdp信息要借助此结构,当客户端对invite作ACK应答时同样要指定自己的媒体信息。整个rtp流的启动入口都由tmedia_session_mgr_t控制。
各种媒体会话以插件的形式注册,如音频会话在启动时注册到tmedia_session_mgr_t的插件链表,并绑定start,stop,prepare回调。tmedia_session_mgr_t为sip信令控制媒体流的接口。
tmedia_session_plugin_def_t为音频视频抽象接口,指定回调。如音频会话,内部会实现相应的回调函数。
/**Virtual table used to define a session plugin */
typedefstruct tmedia_session_plugin_def_s
{
//!object definition used to create an instance of the session
consttsk_object_def_t* objdef;
//!the type of the session
tmedia_type_ttype;
//!the media name. e.g. "audio", "video", "message","image" etc.
constchar* media;
int(*set) (tmedia_session_t* , const tmedia_param_t*);
int(* prepare) (tmedia_session_t* );
int(* start) (tmedia_session_t* );
int(* pause) (tmedia_session_t* );
int(* stop) (tmedia_session_t* );
struct{/* Special case */
int(* send_dtmf) (tmedia_session_t*, uint8_t );
}audio;
consttsdp_header_M_t* (* get_local_offer) (tmedia_session_t* );
/*return zero if can handle the ro and non-zero otherwise */
int(* set_remote_offer) (tmedia_session_t* , const tsdp_header_M_t* );
}
tmedia_session_plugin_def_t;
tmedia_session_t为会话的抽象层,包含tmedia_session_plugin_def_t,
/**Base objct used for all media sessions */
typedefstruct tmedia_session_s
{
TSK_DECLARE_OBJECT;
//!unique id. If you want to modifiy this field then you must use @reftmedia_session_get_unique_id()
uint64_tid;
//!session type
tmedia_type_ttype;
//!list of codecs managed by this session
tmedia_codecs_L_t*codecs;
//!negociated codec
tmedia_codecs_L_t*neg_codecs;
//!whether the ro have been prepared (up to the manager to update thevalue)
tsk_bool_tro_changed;
//!whether the session have been initialized (up to the manager toupdate the value)
tsk_bool_tinitialized;
//!whether the session have been prepared (up to the manager to updatethe value)
tsk_bool_tprepared;
//!QoS
tmedia_qos_tline_t*qos;
//!bandwidth level
tmedia_bandwidth_level_tbl;
struct{
tsdp_header_M_t*lo;
tsdp_header_M_t*ro;
}M;
//!plugin used to create the session
conststruct tmedia_session_plugin_def_s* plugin;
}
tmedia_session_t;
sipsession
trtp_manager_t
tmedia_session_t tmedia_session_plugin_def_s
使用过程:
tdav_init注册音频,视频,多媒体session;注册支持的编码类型,注册支持的媒体信息承载类型(文本,流等)。
tdav_init-> register sessions, codecs.
tmedia_session_mgr_create-> tmedia_session_mgr_ctor ,sessions,qos,sdp.
_tmedia_session_mgr_load_sessions,创建音视频会话。
tmedia_session_create,创建具体会话插件类型,tdav_session_video/audio_ctor
tmedia_session_init,初始化
tmedia_session_load_codecs,此会话支持的编码类型
tmedia_codec_create ,穿件具体编码类型。
创建过程
准备阶段
trtp_manager_prepare,指定传输层接收数据回调trtp_transport_layer_cb
tdav_session_audio_prepare,trtp_manager_create,trtp_manager_set_rtp_callback
tnet_transport_create
tnet_transport_set_callback
启动
tmedia_session_mgr_start(),启动所有上面创建的会话类型,启动之前一定要设置sdp信息
(Startsthe session manager by
startingall underlying sessions.You should set
bothremote and local offers before calling this
session->plugin->start(),如视频会话启动 ,tdav_session_video/audio.c
trtp_manager_set_rtp_remote, 设置对端ip,port,后续发送rtp包时构造包头用
trtp_manager_set_payload_type,设置此次会话用什么编码类型,编码类型通过协商后选择最佳
trtp_manager_start,启动rtp,rtcp包管理,
tnet_transport_start,启动传输层线程,绑定socket地址,开始接收udp数据, tnet_transport_mainthread
请求或响应中sdp与codec匹配过程
tmedia_session_match_codec->tmedia_codec_match_fmtp->tdav_codec_h264_fmtp_match->
tdav_codec_h264_get_profile(根据fmt获取对方的profile版本),
当发起外乎请求时codec与sdp处理关系,
发起invite或对方更改媒体信息时要把codec信息加载到sdp消息体中,
对于video,audio过程是一样的。
(videosession from codecs to sdp)
tdav_session_video_get_lo
|
tsdp_header_M_create(创建sdp媒体头)
|
tmedia_session_match_codec(此函数最终会返回一个协商成功的编码类型)
对于h264编码格式,此函数内部调用过程,遍历协议栈初始化时指定的编码链表,用此次请求的sdp消息体中的编码与自己的编码链表比较。->tmedia_codec_match_fmtp->tdav_codec_h264_fmtp_match->
tdav_codec_h264_get_profile
tmedia_session_match_codec返回协商成功的编码列表(即双方都支持的编码类型列表)后复制给协议栈,
self->neg_codecs= tmedia_session_match_codec
然后调用tmedia_codec_video_set_callback设置此编码类型对应的回调函数,当想发送rtp包时直接触发此回调函数即可完成发送rtp包的任务。
tmedia_codec_video_set_callback((tmedia_codec_video_t*)TSK_LIST_FIRST_DATA(self->neg_codecs),tdav_session_video_raw_cb, self);
tdav_session_video_raw_cb为具体的毁掉函数,内部为调用trtp_manager_send_rtp,发送rtp包。
值得注意的是传给函数的tdav_session_video_raw_cb数据只是未经过加工成rtp包的裸数据,tdav_session_video_raw_cb内部调用trtp_manager_send_rt,由trtp_manager_send_rt来把数据加工成rtp包,
然后调用传输层发送到网络上。
/*Encapsulate raw data into RTP packet and send it over the network
*Very IMPORTANT: For voice packets, the marker bits indicates thebeginning of a talkspurt */
inttrtp_manager_send_rtp(trtp_manager_t* self, const void* data,tsk_size_t size, uint32_t duration, tsk_bool_t marker, tsk_bool_tlast_packet)
trtp_manager_send_rtp内部又具体调用trtp_rtp_packet_create,创建rtp格式的数据包,包括rtp消息头的创建,初始化默认参数(version,marker,payload_type,seq_num等)。然后调用trtp_rtp_packet_serialize把rtp包序列化到一个buffer中。
trtp_manager_send_rtp最后调用tnet_sockfd_sendto传输层函数完成实际发送到网络上。
回到设置tmedia_codec_video_set_callback完毕后,tdav_session_video_get_lo调用tmedia_codec_to_sdp
把协商后的编码类型的信息转换成sdp格式的信息。
tmedia_codec_to_sdp(self->neg_codecs,self->M.lo); 保存到M.lo属性,即本地的媒体信息。
tmedia_codec_to_sdp分析:
此函数的功能即把协商后的编码链表放到协议栈的sdp属性中,这样以后发送invite请求时就可以直接用。
/**@ingrouptmedia_codec_group
*Serialize a list of codecs to sdp (m= line) message.<br>
*Will add: fmt, rtpmap and fmtp.
*@param codecs The list of codecs to convert
*@param m The destination
*@retval Zero if succeed and non-zero error code otherwise
*/
inttmedia_codec_to_sdp(const tmedia_codecs_L_t* codecs, tsdp_header_M_t*m)
TSK_DEBUG_INFO("Serializea list of codecs to sdp (m= line) message/n");
tsk_list_foreach(item,codecs){
遍历每个编码类型,添加fmt,rtpmap属性,fmtp属性(tmedia_codec_get_fmtp,对于h264格式即调用tmedia_codec_h264_get_fmtp)
最后,tdav_session_video_get_lo内部在属性M.ro(即已经有请求的sdp信息)非空时考虑此请求是否为
保持还是接回,通过设置spd属性,sendrecv,sendonly来提示类型。最后,设置Qos信息。
流程tdav_session_video_get_lo
|
tsdp_header_M_create(创建sdp媒体头)
|
tmedia_session_match_codec
|
tmedia_codec_video_set_callback
|
tmedia_codec_to_sdp
但是
tdav_session_video_get_lo又是由谁触发的呢?tdav_session_video_get_lo为某一具体session的回调,
比如视频的session回调,音频的回调,视频,音频的session以plugin的方式挂在到session中。
/**Virtual table used to define a session plugin */
typedefstruct tmedia_session_plugin_def_s
{
//!object definition used to create an instance of the session
consttsk_object_def_t* objdef;
//!the type of the session
tmedia_type_ttype;
//!the media name. e.g. "audio", "video", "message","image" etc.
constchar* media;
int(*set) (tmedia_session_t* , const tmedia_param_t*);
int(* prepare) (tmedia_session_t* );
int(* start) (tmedia_session_t* );
int(* pause) (tmedia_session_t* );
int(* stop) (tmedia_session_t* );
struct{/* Special case */
int(* send_dtmf) (tmedia_session_t*, uint8_t );
}audio;
consttsdp_header_M_t* (* get_local_offer) (tmedia_session_t* );
/*return zero if can handle the ro and non-zero otherwise */
int(* set_remote_offer) (tmedia_session_t* , const tsdp_header_M_t* );
}
tmedia_session_plugin_def_t;
tdav_session_video_get_lo即为get_local_offer的具体回调。
get_local_offer被tmedia_session_get_lo调用。tmedia_session_get_lo又被tmedia_session_mgr_get_lo】
调用,正是上面提到的tmedia_session_mgr为管理session的抽象接口,用来与sip信令交互。
整个流程为:
tmedia_session_mgr
|
tmedia_session_get_lo
|
tdav_session_video_get_lo
|
tsdp_header_M_create(创建sdp媒体头)
|
tmedia_session_match_codec
|
tmedia_codec_video_set_callback
|
tmedia_codec_to_sdp
tmedia_session_mgr_get_lo又被谁触发呢?
刚才说了,是由sip协议栈调用的,具体有这样几个与sdp协商有关的sip点,我们知道,invite请求以及200ok应答,183响应,100响应的确认(prack)中有sdp信息:
(1)发送或者更新请求(invite)
send_INVITEorUPDATE
//send INVITE/UPDATE request
intsend_INVITEorUPDATE(tsip_dialog_invite_t *self, tsk_bool_t is_INVITE,tsk_bool_t force_sdp)
prack响应
//Send PRACK
intsend_PRACK(tsip_dialog_invite_t *self, const tsip_response_t* r1xx)
//Send ACK
intsend_ACK(tsip_dialog_invite_t *self, const tsip_response_t*r2xxINVITE)
初始请求中没有sdp信息,在ack中需要携带sdp信息
(4)发送响应时
/Send any response
intsend_RESPONSE(tsip_dialog_invite_t *self, const tsip_request_t*request, short code, const char* phrase, tsk_bool_t force_sdp)
2.处理请求中的sdp信息过程
tsip_dialog_invite_process_ro
|
tmedia_session_mgr_set_ro
tsip_dialog_invite_process_ro为sip信令中处理sdp信息的入口,在状态机的回调中适时调用
。比如在保持状态转到接回状态。
tsip_dialog_invite_process_ro会初始化mgr,启动,
tmedia_session_mgr_create,tmedia_session_mgr_set_ro,tmedia_session_mgr_set_natt_ctx,
tmedia_session_mgr_start
Doubango ims 框架 分析之 多媒体部分的更多相关文章
- Android/Linux下CGroup框架分析及其使用
1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...
- 几款开源的hybird移动app框架分析
几款开源的Hybrid移动app框架分析 Ionic Onsen UI 与 ionic 相比 jQuery Mobile Mobile Angular UI 结论 很多移动开发者喜欢使用原生代码开发, ...
- 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一
作者:唐老师,华清远见嵌入式学院讲师. 1. Sensor的概念 Sensor即传感器,在当前智能手机上大量存在:G-Sensor.LightsSensor. ProximitySensor.Temp ...
- 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析
作者:唐老师,华清远见嵌入式学院讲师. 1. HAL Stub框架分析 HAL stub的框架比较简单,三个结构体.两个常量.一个函数,简称321架构,它的定义在:@hardware/libhardw ...
- openwrt: Makefile 框架分析
openwrt: Makefile 框架分析 原文链接:blog.chinaunix.net/uid-26675482-id-4704952.html 本篇的主要目的是想通过分析Makefile,了解 ...
- Android 核心分析 之六 IPC框架分析 Binder,Service,Service manager
IPC框架分析 Binder,Service,Service manager 我首先从宏观的角度观察Binder,Service,Service Manager,并阐述各自的概念.从Linux的概念空 ...
- VS2010/MFC编程入门之四(MFC应用程序框架分析)
VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html 上一讲鸡啄米讲的是VS2010应用 ...
- Yii PHP 框架分析(二)
Yii PHP 框架分析(二)作者:wdy http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html Yii是基于组件( ...
- Yii PHP 框架分析 (一)
Yii PHP 框架分析 (一)作者:wdy http://hi.baidu.com/delphiss/blog/item/f7da86d787adb72506088b4b.html 基于yii1.0 ...
随机推荐
- 有关javascript中的JSON.parse和JSON.stringify的使用一二
有没有想过,当我们的大后台只是扮演一个数据库的角色,json在前后台的数据交换中扮演极其重要的角色时,作为依托node的前端开发,其实相当多的时间都是在处理数据,准确地说就是在处理逻辑和数据(这周实习 ...
- 精通ASP.Net MVC 3 框架(第三版)学习笔记
精通ASP.Net MVC 3 框架(第三版)学习笔记 代码才是王道. http://pan.baidu.com/s/1pJyL1cn
- hdu 2196
树形dp 本文出自 http://blog.csdn.net/shuangde800 题目传送门 题意: 给出一棵树,求离每个节点最远的点的距离 思路: 把无根树转化成有根树分析, 对于上面那棵树 ...
- spoj 138
离散化 去掉重复点 排序 二分查找 #include<cstdio> #include<cstring> #include<algorithm> #define ...
- POJ 2407 Relatives(欧拉函数)
题目链接 题意 : 求小于等于n中与n互质的数的个数. 思路 : 看数学的时候有一部分是将欧拉函数的,虽然我没怎么看懂,但是模板我记得了,所以直接套了一下模板. 这里是欧拉函数的简介. #includ ...
- REST_FRAMEWORK加深记忆-极致抽象,极致完结
余下的就是静心看官方文档了. 这个是最抽象的了. urls.py """tutorial URL Configuration The `urlpatterns` list ...
- 转载CSDN (MVC WebAPI 三层分布式框架开发)
前言:SOA(面向服务的架构)是目前企业应用开发过程中普遍采用的技术,基于MVC WebAPI三层分布式框架开发,以此适用于企业信息系统的业务处理,是本文论述的重点.此外,插件技术的应用,富客户端JQ ...
- Swift入门(十一)——类型转换与is、as操作
三种操作:is.as?和as! Swift是强类型语言,但也允许开发者通过is.as?和as!这三种操作来对类型进行判断和强制转换.其中is用作类型判断,而as?和as!则分别是类型转换的可选形式和强 ...
- 被称为同步神器的 BTSync,你可以怎么用?
在这高速运作的信息化时代,使用云端来衔接工作和生活的点滴已是寻常事.可你是否曾扪心自问过:用各大云端备份自己的信息资料,真的安全放心吗? 毫不夸张的说,其实恶意代码和漏洞早已和你如影随形.你甚至都不用 ...
- 对于delphi for linux心存梦想的同学,彻底醒醒吧
Borland 曾经多次做市场调查,很多人呼吁推出delphi for linux.可是直到kylix发展到第三版,borland才明白:一个内置C编译器环境并与之关系紧密的免费OS,没有人愿意花钱去 ...