程序代四码分析1

上传人:沈*** 文档编号:151556639 上传时间:2022-09-13 格式:DOC 页数:17 大小:66.03KB
收藏 版权申诉 举报 下载
程序代四码分析1_第1页
第1页 / 共17页
程序代四码分析1_第2页
第2页 / 共17页
程序代四码分析1_第3页
第3页 / 共17页
资源描述:

《程序代四码分析1》由会员分享,可在线阅读,更多相关《程序代四码分析1(17页珍藏版)》请在装配图网上搜索。

1、一、源代码的构建框架 Ortp是一种开源软件,实现了 RTP 与 RTCP 协议。首先在RTP中有几种重要的结构体,第一种比较重要的结构体是payload type,该结构用于指定编码类型,以及与其相关的时钟速率、采样率等一些参数,参见下图。struct _PayloadTypeint 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 patte

2、rn_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 f

3、ormat parameters for the outgoing stream */int flags;void *user_data; 在代码中,不同的媒体类型有不同的 payloadtype 结构体与之对应,h263,g729,MPEG4等。 因为每种编码都有其独有的特点,而且许多参数也不一样,所以 RTP 包头中使用 payload 域标记负载的类型,一方面接收端可以就此判断负载的类型,从而选择对应的解码器进行解码播放;另一方面,代码在进行时间戳等方面的计算时可以更加方便一点。所有系统当前支持的payload 类型都被放在一个数组中, 由全局变量 av_profile 这个结构体实例统

4、领。 除了 payloadtype 结构体外,一个更重要的结构体是 rtpsession。该结构体即是一个会话的抽象,与会话相关的各种信息都定义在该结构体上或者能够通过该结构体找到。要使用oRTP 进行媒体数据的传输,需要先创建一个会话,之后所有数据的传输都在会话上完成或 基于会话完成。rtpsession结构体的定义如下:struct _RtpSessionRtpSession *next;/* next RtpSession, when the session are enqueued by the scheduler */int mask_pos;/* the position in t

5、he scheduler mask of RtpSession : do not move this field: it is part of the ABI since the session_set macros use it*/struct RtpProfile *profile;int pt;unsigned int ssrc;WaitPoint wp;int telephone_events_pt;/* the payload type used for telephony events */ snd,rcv;unsigned int inc_ssrc_candidate;int i

6、nc_same_ssrc_count;int hw_recv_pt; /* recv payload type before jitter buffer */int recv_buf_size;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

7、_error;RtpSignalTable on_rtcp_bye;struct _OList *signal_tables;struct _OList *eventqs;msgb_allocator_t allocator;RtpStream rtp;RtcpStream rtcp;RtpSessionMode mode;struct _RtpScheduler *sched;uint32_t flags;int dscp;int multicast_ttl;int multicast_loopback;void * user_data;/* FIXME: Should be a table

8、 for all session participants. */struct timeval last_recv_time; /* Time of receiving the RTP/RTCP packet. */mblk_t *pending;/* telephony events extension */mblk_t *current_tev;/* the pending telephony events */mblk_t *sd;queue_t contributing_sources;unsigned int lost_packets_test_vector;unsigned int

9、 interarrival_jitter_test_vector;unsigned int delay_test_vector;float rtt;/*last round trip delay calculated*/OrtpNetworkSimulatorCtx *net_sim_ctx;bool_t symmetric_rtp;bool_t permissive; /*use the permissive algorithm*/bool_t use_connect; /* use connect() on the socket */bool_t ssrc_set;Session 的初始化

10、通过接口 rtp_session_init 完成,外部获得一个新的 session是通过调用接口rtp_session_new 完成。关于 session 的其他有关配置和获取信息的操作都可以在文件 rtpsession.c 中找到定义。使用 oRTP 进行数据传输时,可以在一个任务上完成多个会话流的接收和发送。这得益于oRTP 中调度模块的支持。要使用调度模块,应用需要在进行 oRTP 的初始化时对调度进行初始化,将需要调度管理的会话注册到调度模块中,这样当进行接收和发送操作时,先向调度询问当前会话是否可以进行发送和接收,如果不能进行收发操作,则处理下一个会话。 这有点类似I/O 接口上的

11、select 操作。调度模块使用的数据结构主要为 rtpscheduler,定义如下 :struct _RtpScheduler RtpSession *list;/* list of scheduled sessions*/SessionSetall_sessions; /* mask of scheduled sessions */intall_max;/* the highest pos in the all mask */SessionSet r_sessions;/* mask of sessions that have a recv event */intr_max;Session

12、Setw_sessions;/* mask of sessions that have a send event */int w_max;SessionSete_sessions;/* mask of session that have error event */inte_max;int max_sessions;/* the number of position in the masks */ /* GMutex *unblock_select_mutex; */ortp_cond_t unblock_select_cond;ortp_mutex_tlock;ortp_thread_t t

13、hread;int thread_running;struct _RtpTimer *timer;uint32_t time_; /*number of miliseconds elapsed since the start of the thread */uint32_t timer_inc;/* the timer increment in milisec */;调度结构体中的r/w/e分别代表接收,发送,异常。Avprofile.c文件中定义了一些负载类型。接收的 rtp和 rtcp 包的解析处理函数在文件 rtpparse.c 和 rtcpparse.c 文件 中实现。实现的是将包中的

14、事件包取出放入列队中。rtpsession_inet.c 文件中定义了数据在网络中传输的过程,利用socket 接口完成了。介绍了socket ,远程地址,端口的建立等设置。其相关的声明在rtpsession_priv.h 中定义,Rtpsignaltable.c主要实现一些信号量的初始化,加入一个信号量,以及发射信号量。定义一些回调函数等。rtptimer.c,scheduler.c,sessionset.c等文件实现了调度模块。 port.c 文件中实现常用的任务(如分配内存)的创建及销毁,条件变量及互斥锁,进程间的管道通信机制等。文件utils.c 文件中讲述了一些数据结构,讲述了一些链

15、表的操作。Str_utils.c文件中讲述了一些队列的操作。首先,队列数据结构由三部分组成:队列头、消息块以及数据块队列头指向消息块,消息块之间可以构成双向链表,这是队列的基本要素。消息块本身不带buffer,数据是由专门的数据块来保存的, 并被消息块指向。 在发送上层应用的 payload 数据之前,oRTP 会构造一个消息块,数据指针会指向payload, 这避免了数据拷贝。较低层的接口处理数据时依赖于消息块结构。接收后的数据从消息块中拷贝到用户buffer。接收的 rtp和 rtcp 包的解析处理函数在文件 rtpparse.c 和 rtcpparse.c 文件 中实现。在使用oRTP

16、提供的 rtp 库之前,需要先对其进行初始化,这部分的实现在 oRTP.c 文件中。oRTP的初始化主要调用两个接口:ortp_init 和 ortp_scheduler_init。其中 ortp_init完成 了 payload 的注册,ortp_scheduler_init完成了调度任务的初始化。写程序首先你要了解你的程序是干什么,要解决的什么问题,需要那些变量,以及各变量的关系。我们RTP的目的是实现对数据的打包输出,以及对数据的接收,我们前期的工作就是实现各种初始化,将我们的想要的传输的数据,地址信息等通过调用库函数提供的接口将我们的数据传入内部,在做这些以前,我们还需要判断我们的系统

17、参数个数是否正确,同时还要注意数据类型的一致性。一系列的初始化结束后就要进行数据的发送与接收了,这些数据都是要经过缓冲区的,再写入我们的文件系统的,或者发送出去。整个过程结束后,就是要进行资源的释放。编程的难点是消息是怎样交换。二、主要函数介绍rtp_session_init函数原型:void rtp_session_init (RtpSession * session, int mode)函数功能:执行rtp会话的一些必要的初始化工作参数含义:session: rtp会话结构体,含有一些rtp会话的基本信息mode: 传输模式,有以下几种,决定本会话的一些特性。RTP_SESSION_REC

18、VONLY:只进行rtp数据的接收RTP_SESSION_SENDONLY:只进行rtp数据的发送RTP_SESSION_SENDRECV:可以进行rtp数据的接收和发送执行的操作:1) 首先判断会话是否为空,若不是,则将会话初始化,即清零2) 根据传输模式设置标志变量的值,在rtpsession中一共设置了14个标志位3) 随机产生SSRC和同步源描述信息4) 传入全局的av_profile,即使用默认的profile配置,profile 包含名字与负载类型,负载类型的确认先要得到库中的所拥有的负载,再比较是否与我们所传入的负载相匹配,若是传回类型号5) 套接字的初始化,包含套接字的文件描述

19、符,套接字大小6) 初始化rtp包缓冲区队列7) 信号量的初始化,包含了7种信号量,分别为ssrc的变化 ;负载类型的改变;电话事件;电话事件包;时间戳的跳变;网络错误;RTCP结束包。8) 发送负载类型默认设置为0(pcmu音频),接收负载类型默认设置为-1(未定义)9) 将session的其他成员的值均设置一个默认值。10) 还有抖动的设置,rtcp报告的间隔,job运行等一些设置。rtp_session_new 函数原型:RtpSession *rtp_session_new (int mode)函数功能:建立一个会话该函数很简单,首先为会话分配一个内存,再调用rtp_session_i

20、nit()进行会话的初始化。成功则返回一个会话。rtp_session_set_scheduling_mode函数原型:void rtp_session_set_scheduling_mode (RtpSession * session, int yesno)函数功能: RtpScheduler管理多个session的调度和收发的控制,本函数设置是否使用该session调度管理功能。参数含义:session: rtp会话结构体yesno: 是否使用rtp session的系统调度功能说明:如果yesno为1,调用ortp_get_scheduler ()得到一个调度器,将会话添加到调度会话集合

21、中则表明使用系统的session调度管理功能,意味着可以使用以下功能:1) 可以使用session_set_select在多个rtp会话之间进行选择,根据时间戳判定某个会话是否到达了收发的时间。2) 可以使用rtp_session_set_blocking_mode()设置是否使用阻塞模式来进行rtp包的发送和接收。如果yesno为0,则表明该会话不受系统管理和调度。关于rtp session的管理和调度,由全局的变量RtpScheduler *_ortp_scheduler来负责,该变量必须通过ortp_scheduler_init() 来进行初始化操作。在ortp_scheduler_in

22、it()rtp_scheduler_new() rtp_scheduler_init(sched)rtp_session_set_blocking_mode函数原型:void rtp_session_set_blocking_mode (RtpSession * session, int yesno)函数功能:设置是否使用阻塞模式,参数含义:session: rtp会话结构体yesno: 是否使用阻塞模式注意,阻塞模式只有在scheduling mode被开启的情况下才能使用。rtp_session_set_local_addr函数原型:int rtp_session_set_local_ad

23、dr( RtpSession * session, const char * addr,int port)函数功能:设置本地rtp数据监听地址参数含义:session: rtp会话结构体addr: 本地IP地址,例如127.0.0.1,如果为NULL,则系统分配0.0.0.0port: 监听端口,如果设置为-1,则系统为其自动分配端口返回值: 0表示成功rtp_session_set_remote_addr函数原型:int rtp_session_set_remote_addr (RtpSession * session, const char * addr, int port)函数功能:设置

24、RTP发送的目的地址参数含义:session: rtp会话结构体addr: 目的IP地址port: 目的地址的监听端口号返回值: 0表示成功rtp_session_set_send_payload_type函数原型:int rtp_session_set_send_payload_type (RtpSession * session, int pt)函数功能:设置RTP发送数据的负载类型(即将外部的负载类型传入会话中)参数含义:session: rtp会话结构体pt:负载类型返回值: 0表示成功,-1表示负载未定义说明:负载类型在payloadtype.h文件中有详细的定义,RTP接收端有着类

25、似的负载类型设置函数,int rtp_session_set_recv_payload_type ( RtpSession * session, int paytype ) ,注意,发送的负载类型必须与接收的负载类型一致才能正常完成收发。rtp_session_signal_connect函数原型:int rtp_session_signal_connect (RtpSession * session, const char *signal, RtpCallback cb, unsigned long user_data)函数功能:本函数提供一种方式,用于通知应用程序各种可能发生的RTP事件(

26、信号)。可能通过注册回调函数的形式来实现本功能。参数含义:session: rtp会话结构体signal: 信号的名称cb: 回调函数user_data:传递给回调函数的数据返回值:0表示成功怎样实现:将回调函数的地址,用户数据加入RtpSignalTable表中,若成功则返回0注意参数中的信号是在rtp_session_init()中定义的7种信号量。rtp_session_send_with_ts函数原型:int rp_session_send_with_ts (RtpSession * session, const uint8_t * buffer, int len, uint32_t

27、userts)函数功能:发送RTP数据包参数含义:session: rtp会话结构体buffer: 需要发送的RTP数据的缓冲区len: 需要发送的RTP数据的长度userts: 本RTP数据包的时间戳返回值: 成功发送到网络中的字节数说明:这个函数定义了外部的接口,内部定义了怎样为数据打包,将包发送调用了函数_rtp_session_sendm_with_ts ,而这个函数主要的目的是讲述包是怎样在进程中发送,讲述了调度的过程。其中调用了底层的发送函数rtp_session_rtp_send,该函数利用SOCKET进行数据的传输。rtp_session_recv_with_ts函数原型:in

28、t rtp_session_recv_with_ts (RtpSession * session, char * buffer,int len, uint32_t time, int * have_more)函数功能:接收RTP数据包参数含义:session: rtp会话结构体buffer: 存放接收的RTP数据的缓冲区len: 期望接收的RTP数据的长度time: 期望接收的RTP数据的时间戳have_more:标识接收缓冲区是否还有数据没有传递完。当用户给出的缓冲区不够大时,为了标识缓冲区数据未取完,则have_more指向的数据为1,期望用户以同样的时间戳再次调用本函数;否则为0,标识取

29、完。rtp_session_destroy【原型】: void rtp_session_destroy(RtpSession *session)【功能】:摧毁rtp会话对象,释放资源【参数】:session已经创建的RTP会话对象三、程序示例本程序选取的是RTP发送的示例#include #include #include /条件编译,如果不是win32系统则包含下面的头文件#ifndef _WIN32 #include #include #include #endifint runcond=1;/当一个信号发生时,使请求停止,在下面的退出循环中将使用void stophandler(int

30、signum)runcond=0;static const char *help=usage: rtpsendfilename dest_ip4addr dest_port -with-clockslide -with-jitter n;int main(int argc, char *argv)RtpSession *session;unsigned char buffer160;int i;FILE *infile;char *ssrc;uint32_t user_ts=0;int clockslide=0;int jitter=0;/说明了系统参数将大于4个if (argc4)print

31、f(%s, help);return -1;for(i=4;i=argc) printf(%s, help);return -1;clockslide=atoi(argvi);/将字符串换成整数型ortp_message(Using clockslide of %i milisecond every 50 packets.,clockslide);else if (strcmp(argvi,-with-jitter)=0)ortp_message(Jitter will be added to outgoing stream.);i+;if (i=argc) printf(%s, help);

32、return -1;jitter=atoi(argvi);/设置抖动/rtp库的一些基本初始化ortp_init();/profile初始化,全局统计量清零,产生随机数ortp_scheduler_init();/对一些信号集合的初始化设置,建立并开启rtp调度器ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);/设置日记的级别/创建一个rtp会话session=rtp_session_new(RTP_SESSION_SENDONLY);rtp_session_set_scheduling_mode(session,1)

33、;rtp_session_set_blocking_mode(session,1);rtp_session_set_connected_mode(session,TRUE); / 设置远程RTP客户端的的IP和监听端口(即本rtp数据包的发送目的地址rtp_session_set_remote_addr(session,argv2,atoi(argv3);/设置希望发送包的负载类型rtp_session_set_payload_type(session,0);/获取同步标示源ssrc=getenv(SSRC);if (ssrc!=NULL) printf(using SSRC=%i.n,ato

34、i(ssrc);/ 设置输出流的SSRC。不做此步的话将会给个随机值rtp_session_set_ssrc(session,atoi(ssrc);/打开一个文件,准备发送文件中的数据#ifndef _WIN32infile=fopen(argv1,r);#elseinfile=fopen(argv1,rb);#endifif (infile=NULL) perror(Cannot open file);return -1;/外部的一个动作,内核送一个SIGINT信号给进程,启动stophandler这个处理函数,即终止进程signal(SIGINT,stophandler);/将文件中的内容

35、读入缓冲区中,并将缓冲区中的数据发送,/每发送一次,时间戳加160,如果时间片没有到0 ,继续发送while( (i=fread(buffer,1,160,infile)0) & (runcond) )rtp_session_send_with_ts(session,buffer,i,user_ts);user_ts+=160;if (clockslide!=0 & user_ts%(160*50)=0)ortp_message(Clock sliding of %i miliseconds now,clockslide);rtp_session_make_time_distorsion(se

36、ssion,clockslide);/设置时间偏移量/*this will simulate a burst of late packets */利用nanosleep()进行延时减缓抖动if (jitter & (user_ts%(8000)=0) struct timespec pausetime, remtime;ortp_message(Simulating late packets now (%i milliseconds),jitter);pausetime.tv_sec=jitter/1000;pausetime.tv_nsec=(jitter%1000)*1000000;whi

37、le(nanosleep(&pausetime,&remtime)=-1 & errno=EINTR)pausetime=remtime;/退出,释放资源fclose(infile);/关闭文件rtp_session_destroy(session);ortp_exit();ortp_global_stats_display();/打印全局统计变量return 0; 文件修改部分,主要是在avprofile.c中av_profile_init()添加了一个负载类型,因为我们的TBCP定义为106,同时在rtpsession_priv.h中将rtcp接收函数部分的返回类型进行了修改为mblk_t

38、*,定义为一个函数指针,目的是为了获得RTCP包,之前的函数只是返回接收到的数据字节数。Rtpsession_inet.c没有做多大的修改,只是加了一个TBCP的头文件。 在源程序中增加了tbcp.c文件和tbcp.h文件,主要讲述了一些控制信息功能。四、TBCP首先是tbcp语音冲突发送消息请求,可以包含一个或多个可选域。每个TBCP Talk Burst请求消息可选域应该包含三个子域:第一个子域是可选ID子域。这个可选ID子域应该标识选来作为8-bit可选ID的选项;第二个子域是可选长度子域。可选长度子域应该由一个字节组成,说明整个可选域的长度。可选长度子域的值应该等于可选ID子域、可选长

39、度子域和可选值子域字节数的总和;第三个子域是可选值子域。这个值子域应该包含一个字节数的整数值。这个子域的格式和值是依赖于应用选项的。TBCP Talk Burst请求消息还定义了三种优先级:普通优先级,高优先级和抢占优先级。如果PoC用户已经指明Talk Burst请求优先级高于普通优先级,或者PoC用户想改变一个已经排队的Talk Burst请求的优先级,PoC客户端应该包含这个优先级域。可选ID子域的值应该是1。可选长度子域的值应该是3。同一优先级请求但没有时间戳和不同优先级请求位置将由PoC服务器自己决定。可选ID子域的值应该是2。可选长度子域的值应该是10。可选值子域应该由八个字节组成

40、,给出NTP时间签,这个NTP时间签在RFC1305中定义。在代码中,首先申请分配指定的16个字节的空间date,将上述说明的各个子域的值分别赋给datei,最后一起打包发给服务器。其次是tbcp语音冲突释放消息,在此消息中与应用相关的数据域由4个字节组成:前16(0-15)个比特表示了在Talk Burst序列中最后一个RTP媒体包的序列号。比特16是忽略序列号域,该值为0是序列号有效,为1时序列号无效。在应用相关数据中的最后15个比特是填充比特,建议全部设为0。PoC服务器应该忽略填充比特的值。如果PoC没有把正确的序列码写进相应的域中,PoC客户端应该把Ignore Sequence N

41、umber域的值设为1,这时完成控制功能的服务器应该忽略这个域中所表示的最后一个RTP的值,因为PoC客户端本来就没有把它设值为正确的值。在代码中,首先申请分配指定的4字节的空间date,随后取到序列号的地址,将序列号的低8位放入date0中,高8位放入date1中,然后判断Ignore Sequence Number域的值是否为0,如果为0,则date3=0,否则date2=-128,最后将这些数据打包,一起发到服务器。最后是tbcp语音冲突确定消息,应用相关数据包含一或二个定义的子域:subtype和紧随其后的11比特原因代码域,最后是16比特的填充位。应用相关数据中的subtype域的值

42、应该与被确认的消息的subtype域的值相同。以下是原因编码的定义:(1)原因代码的值是“0”(十进制值),表示PoC客户端已经接受了到来的PoC会话。(2)原因代码的值是“1”(十进制值),表示PoC客户端因为忙而没有接受了到来的PoC会话。(3)原因代码的值是“2”(十进制值),表示PoC客户端还没有接受到来的PoC会话。在代码中,当subtype在1-31之间,原因编码在1-3之间时,首先申请分配指定的4个字节的空间date,随后定义一个i取到subtype的5位,和原因编码的11位,然后在date0中放入前8位,date1中放入后8位,date2和date3中放0,最后将这些数据打包,发送到服务器。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!