天天看點

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

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 */

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

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;

tnet_socket_t*local_socket;

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

//!unique id. If you want to modifiy this field then you must use @reftmedia_session_get_unique_id()

uint64_tid;

//!session type

//!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;

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

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中。

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

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

繼續閱讀