序言

RTP提供带有实时特性的端对端数据传输服务,传输的数据如:交互式的音频和视频。那些服务包括有效载荷类型定义,序列号,时间戳和传输监测控制。应用程序在UDP上运行RTP来使用它的多路技术和checksum服务。2种协议都提供传输协议的部分功能。

RTP本身没有提供任何的机制来确保实时的传输或其他的服务质量保证,而是由低层的服务来完成。它不保证传输或防止乱序传输,它不假定下层网络是否可靠,是否按顺序传送数据包。RTP包含的序列号允许接受方重构发送方的数据包顺序,但序列号也用来确定一个数据包的正确位置,例如,在视频解码的时候不用按顺序的对数据包进行解码。

  1. 介绍

doubango框架中tinyRTP文件夹实现RTP/RTCP/RTSP协议栈,目前只实现了

RTP,RTCP;RTSP还没实现。Rtp用来在网络上传输音频视频,协议栈实现时主要在音视频包的封装,拆包。

  1. 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;

  1. rtp包结构,文件trtp_rtp_packet.h

typedefstruct trtp_rtp_packet_s

  1. {

  2. TSK_DECLARE_OBJECT;

  3. trtp_rtp_header_t*header; //包头

  4. struct{

  5. void*data;

  6. constvoid* data_const;

  7. tsk_size_tsize;

  8. }payload; //负载,即承载内容

  9. /*extension header as per RFC 3550 section 5.3.1 */

  10. struct{

  11. void*data;

  12. tsk_size_tsize; /* contains the first two 16-bit fields */

  13. }extension;

  14. }

  15. trtp_rtp_packet_t;

  1. 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;

  1. 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)

  1. prack响应

//Send PRACK

intsend_PRACK(tsip_dialog_invite_t *self, const tsip_response_t* r1xx)

  1. //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 框架 分析之 多媒体部分的更多相关文章

  1. Android/Linux下CGroup框架分析及其使用

    1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...

  2. 几款开源的hybird移动app框架分析

    几款开源的Hybrid移动app框架分析 Ionic Onsen UI 与 ionic 相比 jQuery Mobile Mobile Angular UI 结论 很多移动开发者喜欢使用原生代码开发, ...

  3. 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一

    作者:唐老师,华清远见嵌入式学院讲师. 1. Sensor的概念 Sensor即传感器,在当前智能手机上大量存在:G-Sensor.LightsSensor. ProximitySensor.Temp ...

  4. 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析

    作者:唐老师,华清远见嵌入式学院讲师. 1. HAL Stub框架分析 HAL stub的框架比较简单,三个结构体.两个常量.一个函数,简称321架构,它的定义在:@hardware/libhardw ...

  5. openwrt: Makefile 框架分析

    openwrt: Makefile 框架分析 原文链接:blog.chinaunix.net/uid-26675482-id-4704952.html 本篇的主要目的是想通过分析Makefile,了解 ...

  6. Android 核心分析 之六 IPC框架分析 Binder,Service,Service manager

    IPC框架分析 Binder,Service,Service manager 我首先从宏观的角度观察Binder,Service,Service Manager,并阐述各自的概念.从Linux的概念空 ...

  7. VS2010/MFC编程入门之四(MFC应用程序框架分析)

    VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html   上一讲鸡啄米讲的是VS2010应用 ...

  8. Yii PHP 框架分析(二)

    Yii PHP 框架分析(二)作者:wdy http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html Yii是基于组件( ...

  9. Yii PHP 框架分析 (一)

    Yii PHP 框架分析 (一)作者:wdy http://hi.baidu.com/delphiss/blog/item/f7da86d787adb72506088b4b.html 基于yii1.0 ...

随机推荐

  1. 【BZOJ】【2733】【HNOI2012】永无乡

    平衡树+启发式合并+并查集 因为要求一坨数中第k大的……用平衡树会很好维护…… 但又要求连通块?所以用并查集来维护…… 大概就是让并查集的fa和Treap的根是同一个节点吧…… TLE了N多发,可能是 ...

  2. Installing Lua in Mac

    Lua is distributed in source form. You need to build it before using it. Building Lua should be stra ...

  3. javascript分享到的源码

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  4. Linux 按行分割文件(转载)

    将一个大文件分成若干个小文件方法 例如将一个BLM.txt文件分成前缀为 BLM_ 的1000个小文件,后缀为系数形式,且后缀为4位数字形式 先利用 wc -l BLM.txt       读出 BL ...

  5. Java快速排序 分别以数组0位作为基准 和最后一位作为基准的排序演示

    package util; public class Pub { public static void beforeSort(int[] arr){ System.out.println(" ...

  6. *[topcoder]PalindromicSubstringsDiv2

    http://community.topcoder.com/stat?c=problem_statement&pm=12967 计算一个字符串里Palindrome的数量.我的DP方法需要n^ ...

  7. Hibernate逍遥游记-第3章对象-关系映射基础-access="field"、dynamic-insert、dynamic-update、formula、update=false

    1. package mypack; import java.util.*; public class Monkey{ private Long id; private String firstnam ...

  8. 3.Spring-用反射模拟IoC

    1.BeanFactory.java package com.jike.spring.chapter03.reflect; import java.io.InputStream; import jav ...

  9. kaili开启sshd服务

    使用xshell远程连接kali 2.0时要开启kaili上的sshd服务,具体方法如下: 命令: vim /etc/ssh/sshd_config # Package generated confi ...

  10. Android AIDL-跨进程

    Android在设计理念上强调组件化,组件之间的依赖性很小.我们往往发一个Intent请求就可以启动另一个应用的Activity,或者一个你不知道在哪个进程的Service,或者可以注册一个广播,只要 ...