您的位置:首页 > 编程语言 > PHP开发

oRTP源码分析

2016-03-02 15:35 525 查看
oRTP源码分析前言本文主要是想在阅读oRTP协议栈时,为了以后的回忆,做些笔录。

一、AVProfile模块

1.1 Pt的总结:

引用:http://en.wikipedia.org/wiki/RTP_audio_video_profile

RTP/AVP audio and video payload types

Payload type (PT)NameTypeNo. of channelsClock rate (Hz)DescriptionReferences
0PCMUaudio18000ITU-T G.711PCMµ-LawAudio 64 kbit/sRFC 3551
1reserved (previously 1016)audio18000reserved, previouslyCELPAudio 4.8 kbit/sRFC 3551,previouslyRFC 1890
2reserved (previously G721)audio18000reserved, previously ITU-TG.721ADPCM Audio32 kbit/sRFC 3551,previouslyRFC 1890
3GSMaudio18000European GSMFull Rate Audio 13 kbit/s (GSM 06.10)RFC 3551
4G723audio18000ITU-T G.723.1RFC 3551
5DVI4audio18000IMAADPCM Audio32 kbit/sRFC 3551
6DVI4audio116000IMAADPCM 64 kbit/sRFC 3551
7LPCaudio18000ExperimentalLinearPredictive Coding AudioRFC 3551
8PCMAaudio18000ITU-T G.711 PCM A-LawAudio 64 kbit/sRFC 3551
9G722audio18000ITU-T G.722AudioRFC 3551- Page 14
10L16audio244100Linear PCM16-bit Stereo Audio 1411.2 kbit/s,[2][3][4]uncompressedRFC 3551,Page 27
11L16audio144100Linear PCM 16-bit Audio 705.6 kbit/s, uncompressedRFC 3551,Page 27
12QCELPaudio18000Qualcomm CodeExcited Linear PredictionRFC 2658,RFC3551
13CNaudio18000Comfortnoise. Payload type used with audio codecs that do notsupport comfort noise as part of the codec itself such asG.711,G.722.1,G.722,G.726,G.727,G.728,GSM 06.10,Siren, andRTAudio.RFC 3389
14MPAaudio190000MPEG-1 orMPEG-2 AudioOnlyRFC 3551,RFC2250
15G728audio18000ITU-T G.728Audio 16 kbit/sRFC 3551
16DVI4audio111025IMAADPCMRFC 3551
17DVI4audio122050IMA ADPCMRFC 3551
18G729audio18000ITU-T G.729and G.729aRFC 3551,Page 20
25CELBvideo190000Sun'sCellB Video Encoding[5]RFC 2029
26JPEGvideo190000JPEG VideoRFC 2435
28NVvideo190000XeroxPARC's Network Video (nv)[6]RFC 3551,Page 32
31H261video190000ITU-T H.261VideoRFC 4587
32MPVvideo190000MPEG-1 and MPEG-2 VideoRFC 2250
33MP2Taudio/video190000MPEG-2transportstream VideoRFC 2250
34H263video 90000H.263 video,first version (1996)RFC 3551,RFC2190
35 - 71unassigned    RFC 3551,Page 32
72 - 76Reserved for RTCP conflict avoidanceN/A N/A RFC 3551,Page 32
77 - 95unassigned    RFC 3551,Page 32
dynamicH263-1998video 90000H.263 video,second version (1998)RFC 3551,RFC4629, RFC 2190
dynamicH263-2000video 90000H.263 video,third version (2000)RFC 4629
dynamic (or profile)H264video 90000H.264 video(MPEG-4 Part 10)RFC 6184,previouslyRFC 3984
dynamic (or profile)theoravideo 90000Theora videodraft-barbato-avt-rtp-theora-01
dynamiciLBCaudio1Internet lowBitrate Codec 13.33 or 15.2 kbit/sRFC 3951
dynamicPCMA-WBaudio 16000ITU-T G.711.1,A-lawRFC 5391
dynamicPCMU-WBaudio 16000ITU-T G.711.1,µ-lawRFC 5391
dynamicG718audio 32000ITU-T G.718draft-ietf-avt-rtp-g718-03
dynamicG719audio(various)48000ITU-T G.719RFC 5404
dynamicG7221audio 16 or 32 kHzITU-T G.722.1RFC 5577
dynamicG726-16audio18000ITU-T G.726audio with 16 kbit/sRFC 3551
dynamicG726-24audio18000ITU-T G.726 audio with 24 kbit/sRFC 3551
dynamicG726-32audio18000ITU-T G.726 audio with 32 kbit/sRFC 3551
dynamicG726-40audio18000ITU-T G.726 audio with 40 kbit/sRFC 3551
dynamicG729Daudio18000ITU-T G.729Annex DRFC 3551
dynamicG729Eaudio18000ITU-T G.729Annex ERFC 3551
dynamicG7291audio (various)ITU-T G.729.1RFC 4749
dynamicGSM-EFRaudio18000ITU-T GSM-EFR(GSM 06.60)RFC 3551
dynamicGSM-HR-08audio18000ITU-T GSM-HR(GSM 06.20)RFC 5993
dynamic (or profile)AMRaudio(various)8000AdaptiveMulti-Rate audioRFC 4867
dynamic (or profile)AMR-WBaudio(various)16000AdaptiveMulti-Rate Wideband audio (ITU-T G.722.2)RFC 4867
dynamic (or profile)AMR-WB+audio1, 2 or omit72000ExtendedAdaptive Multi Rate – WideBand audioRFC 4352
dynamic (or profile)vorbisaudio(various)from 8 kHz to 192 kHzRTP Payload Format forVorbisEncoded AudioRFC 5215
dynamic (or profile)opusaudio1, 248000 (the actual clock rate is signaled inside the payload)RTP Payload Format forOpusSpeech and Audio Codecdraft
dynamic (or profile)speexaudio18000, 16000 or 32000RTP Payload Format for theSpeexCodecRFC 5574
dynamic (96-127)mpa-robustaudio 90000A More Loss-Tolerant RTP Payload Format forMP3AudioRFC 5219
dynamic (or profile)MP4A-LATMaudio 90000 or othersRTP Payload Format forMPEG-4AudioRFC 6416(previouslyRFC3016)
dynamic (or profile)MP4V-ESvideo 90000 or othersRTP Payload Format forMPEG-4VisualRFC 6416(previouslyRFC3016)
dynamic (or profile)mpeg4-genericaudio/video 90000 or otherRTP Payload Format for Transport ofMPEG-4Elementary StreamsRFC 3640
dynamicVP8video 90000RTP Payload Format for Transport of VP8 Streamsdraft-ietf-payload-vp8-08
dynamicL8audio(various)(various)Linear PCM8-bit audio with 128 offsetRFC 3551Section 4.5.10 and Table 5
dynamicDAT12audio(various)8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 orothersIEC 61119 12-bit nonlinear audioRFC 3190Section 3
dynamicL16audio(various)8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 orothersLinear PCM16-bit audioRFC 3551Section 4.5.11,RFC2586
dynamicL20audio(various)8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 orothersLinear PCM20-bit audioRFC 3190Section 4
dynamicL24audio(various)8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 orothersLinear PCM24-bit audioRFC 3190Section 4
RFC 3551 listsdetails of thecodec,or a reference for the details is provided. Payload identifiers96–127 are reserved for payloads defined dynamically during asession. The minimum payload support is defined as 0 (PCMU) and5(DVI4). The document recommends dynamically assigned port numbers,although 5004 and 5005 have been registered for use of the profileand can be used instead. The standard also describes the process ofregistering new payload types with IANA.H.264可以填(96).

1.2 源码分析

这部分代码比较好理解,实现主要在avprofle.c和payloadtype.h,rtpprofile.h,rtpprofile.c中。
struct _PayloadType
{
int type; /**< one of PAYLOAD_* macros*/
int clock_rate; /**< rtp clock rate*/
char bits_per_sample;   /* in case of continuous audio data */
char *zero_pattern;
int pattern_length;
/* other useful information for the application*/
int normal_bitrate;     /*in bit/s */
char *mime_type; /**<actually the submime, ex: pcm, pcma, gsm*/
int channels; /**< number of channels of audio */
char *recv_fmtp; /* various format parameters for the incoming stream */
char *send_fmtp; /* various format parameters for the outgoing stream */
int flags;
void *user_data;
};
这里定义出PayloadType的基本的元素,具体变量的意义看代码注释。
变量 recv_fmtp/send_fmtp ,是用来保存SDP 的参数。
/**
* The RTP profile is a table RTP_PROFILE_MAX_PAYLOADS entries to make the matching
* between RTP payload type number and the PayloadType that defines the type of
* media.
**/
struct _RtpProfile
{
char *name;
PayloadType *payload[RTP_PROFILE_MAX_PAYLOADS];
};
typedef struct _RtpProfile RtpProfile;
RtpProfile av_profile; 是一个全局的变量。
这里定义一个Payload的集合,然后调用rtp_profile_set_payload(...)函数,把格式话好的playload赋值到这个集合中,这是在用户调用ortp_init()时完成的。未初始化的可以调用rtp_profile_set_payload()来在应用层设置。

二、oRTP中的event和signal模块

2.1 Signal的实现

应用层可以调用 rtp_session_signal_connect(...)函数来绑定一个signal 和一个callback函数。当协议栈遇到某一事件时 就会调用rtp_signal_table_emit_x(...) 来向应用层报告。这里的实现思想也不难,主要就是回调函数的原理。
应用层 调用rtp_session_new(),那么就会调用rtp_session_init().在这个初始化函数中初始化了singnal_table。在struct_RtpSession结构体中定义了
RtpSignalTable on_ssrc_changed;
RtpSignalTable on_payload_type_changed;
RtpSignalTable on_telephone_event_packet;
RtpSignalTable on_telephone_event;
RtpSignalTable on_timestamp_jump;
RtpSignalTable on_network_error;
RtpSignalTable on_rtcp_bye;
struct _OList *signal_tables;

分别表示RTP ssrc 变化,pt变化等事件的发生。
typedef void (*RtpCallback)(struct _RtpSession *, ...);
struct _RtpSignalTable
{
RtpCallback callback[RTP_CALLBACK_TABLE_MAX_ENTRIES];
unsigned long user_data[RTP_CALLBACK_TABLE_MAX_ENTRIES];
struct _RtpSession *session;
const char *signal_name;
int count;
};
这里需要注意 一个struct_OList*signal_tables;的变量,在调用rtp_signal_table_init()时会把这些signaletable 加入到一个list当中,这是为了后面应用层在添加事件时方便查寻。见代码:
int
rtp_session_signal_connect (RtpSession * session, const char *signal_name,
RtpCallback cb, unsigned long user_data)
{
OList *elem;
for (elem=session->signal_tables;elem!=NULL;elem=o_list_next(elem)){
RtpSignalTable *s=(RtpSignalTable*) elem->data;
if (strcmp(signal_name,s->signal_name)==0){
return rtp_signal_table_add(s,cb,user_data);
}
}
ortp_warning ("rtp_session_signal_connect: inexistant signal %s",signal_name);
return -1;
}
函数rtp_session_signal_connect在session->signal_tables中先根据signal_name来找到具体的一个RtpSignalTable,然后再在这个表中调节事件的回调函数。

2.2 Event 实现

Event的实现似乎和signal功能差不多,但实现却不一样,至于为什么这样实现,我们先不考虑,就看看作者到是是如何实现这一过程的吧。初看Event的实现有点搞不明白事件到底是如何发送和接受,尤其是时间如何被接受。后来结合linphone的应用程序才有点搞明白。在 struct_RtpSession的结构中有个成员变量struct_OList*eventqs;这个List用来存放OrtpEvQueue的头指针,结构如下图。/**
* Register an event queue.
* An application can use an event queue to get informed about various RTP events.
**/
void rtp_session_register_event_queue(RtpSession *session, OrtpEvQueue *q){
session->eventqs=o_list_append(session->eventqs,q);
}
void rtp_session_unregister_event_queue(RtpSession *session, OrtpEvQueue *q){
session->eventqs=o_list_remove(session->eventqs,q);
}
event的 OrtpEvQueue是在应用层定义,通过rtp_session_register_event_queue()注册到oRTP协议栈中。协议栈通过rtp_session_dispatch_event()向session->eventqs中的每一个OrtpEvQueue分发消息,应用层中,需要自己调用ortp_ev_queue_get()来获得消息,并处理。消息类型有
/* type is one of the following*/
#define ORTP_EVENT_STUN_PACKET_RECEIVED         1
#define ORTP_EVENT_PAYLOAD_TYPE_CHANGED         2
#define ORTP_EVENT_TELEPHONE_EVENT              3
#define ORTP_EVENT_RTCP_PACKET_RECEIVED         4 /**<when a RTCP packet is 																received from far end */
#define ORTP_EVENT_RTCP_PACKET_EMITTED          5 /**<fired when oRTP decides 												to send an automatic RTCP SR or RR */
#define ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED      6
#define ORTP_EVENT_ZRTP_SAS_READY               7
#define ORTP_EVENT_ICE_CHECK_LIST_PROCESSING_FINISHED   8
#define ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED      9
#define ORTP_EVENT_ICE_GATHERING_FINISHED               10
#define ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED           11
#define ORTP_EVENT_ICE_RESTART_NEEDED                   12
ORTP_EVENT_RTCP_PACKET_RECEIVED消息接收到后,应用层可以根据RTCP包做流量控制。typedefmblk_tOrtpEvent;消息体的结构就是个mblk_t的类型,这个结构块中存放了typedefunsignedlongOrtpEventType;和
struct _OrtpEventData{
mblk_t *packet; /* most events are associated to a received packet */
RtpEndpoint *ep;
ortpTimeSpec ts;
union {
int telephone_event;
int payload_type;
bool_t zrtp_stream_encrypted;
struct _ZrtpSas{
char sas[5]; // 4 characters
bool_t verified;
} zrtp_sas;
OrtpSocketType socket_type;
bool_t ice_processing_successful;
} info;
};
两个数据类型。

三、定时器RtpTimer

RtpTimer所涉及的文件有rtptimer.c posixtimer.crtptimer.h .RtpTimer就是一个状态和函数指针的结构体。具体的函数在posixtimer.c中赋值。
//这里是个函数的指针类型;
typedef void (*RtpTimerFunc)(void);

struct _RtpTimer
{
int state;
#define RTP_TIMER_RUNNING 1
#define RTP_TIMER_STOPPED 0
RtpTimerFunc timer_init;
RtpTimerFunc timer_do;
RtpTimerFunc timer_uninit;
struct timeval interval;
};
typedef struct _RtpTimer RtpTimer;
RtpTimer posix_timer; 这是个全局的变量作为协议栈的定时器。
/*
定义RtpTimer , 全局变量
*/
RtpTimer posix_timer={  0,
posix_timer_init,
posix_timer_do,
posix_timer_uninit,
{0,POSIXTIMER_INTERVAL}};
最需要具体理解下的就是posix_timer_do()函数,这个函数实现了精确的定时处理。Timer的实现可以由下图表示:
posix_timer_time是安自己的步调向前走,每次posix_timer_do()被调用,函数就会比较实际时间和posix_timer_time的差距,如果diff为0,就表示时间到了,如何不为0,就利用select(0,NULL,NULL,NULL,&tv);做延时。对照下面的代码可以很好理解了。
void posix_timer_do(){int diff,time;struct timeval tv;gettimeofday(&cur,NULL);time=((cur.tv_usec-orig.tv_usec)/1000 ) + ((cur.tv_sec-orig.tv_sec)*1000 );if ( (diff=time-posix_timer_time)>50){ortp_warning("Must catchup %i miliseconds.",diff);}while((diff = posix_timer_time-time) > 0){tv.tv_sec = diff/1000;tv.tv_usec = (diff%1000)*1000;#if     defined(_WIN32) || defined(_WIN32_WCE)/* this kind of select is not supported on windows */Sleep(tv.tv_usec/1000 + tv.tv_sec * 1000);#elseselect(0,NULL,NULL,NULL,&tv);#endifgettimeofday(&cur,NULL);time=((cur.tv_usec-orig.tv_usec)/1000 ) + ((cur.tv_sec-orig.tv_sec)*1000 );}posix_timer_time+=POSIXTIMER_INTERVAL/1000;}时间单位秒 (s)     second毫秒(ms)   millisecond微秒 (us)  microsecond纳秒 (ns)皮秒 (ps)飞秒 (fs)阿托秒(简称阿秒)(as)

四、RtpScheduler模块

RtpScheduler主要是在上一节中提到的RtpTimer的基础上实现的一个系统的调度处理,实现了类似linux的select()syscall的功能。要明白RtpShcedule的工作原理,先看看几个重要的数据结构的关系。调度器的工作看起来比较复杂,但慢慢分析还是可以理解里面的原理的。调度器是在一个void*rtp_scheduler_schedule(void*psched)的线程中实现的。他以一个恒定的速度调用rtp_session_process来轮训所有的rtp_session。在这个rtp_session_process中又通过
        struct {RtpProfile *profile;int pt;unsigned int ssrc;WaitPoint wp;int telephone_events_pt;/* the payload type used for telephony events */} snd,rcv;
这个变量wp来控制rtp_session_recvm_with_t和rtp_session_sendm_with_t的速度。对几个时间变量的理解:(1)sched->time_:在调度器时间轴的时间值,从0开始,安sched->timer_inc向前运行。(2)session->rtp.snd_ts_offset:第一次发送数据的时间戳值;(3)session->rtp.snd_time_offset:第一发送数据时的在调度器时间轴上的时间值;session->rtp.snd_ts_offset和session->rtp.snd_time_offset是为了计算packet_time,也就是把发送数据时的时间都统一到调度器的时间轴上来做比较(比较使用宏TIME_IS_STRICTLY_NEWER_THAN,大于)。图中T1-T0 = T2-T1 =Tn-T(n-1)表示sched在匀速的向前。图中t 0,t1,t2表示packet_time的时间图中黄色的部分为记录集的状态
	sched 还通过 sched->unblock_select_cond变量来控制了 session_set_select的速度。从以上分析可以看出调度器 分别控制着 数据发送和接受 和select 的速度。所有sched的颗粒度决定了协议栈运行速度的快慢。
其他:1. oRTP中UDP也使用了connectsyscall, 这样使得UDP的效率更高。UDP调用CONNECT在末连接UDP套接口上给两个数据报调用函数sendto导致内核执行下列六步:1.连接套接口;2.输出第一个数据报3.断开套接口连接;4.连接套接口,5.输出第二个数据报;6.断开套接口连接已连接套接口发送两个数据报的导致内核执行如下步骤;1.连接套接口;2.输出第一个数据报;3.输出第二个数据报。对同一套接口发送时,耗时减少1/3
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: