建收费网站,下载免费网络,创意设计logo,建设银行官网首页网站公告以下内容源于朱有鹏嵌入式课程的学习与整理#xff0c;如有侵权请告知删除。
本文首先分析ORTP库的组成#xff0c;然后以rtpsend.c为例说明ORTP库的使用方法#xff0c;最后分析第三季1中的RTP发送实验的源码。 一、ORTP库的源码分析
1、ORTP库概览
#xff08;1#…以下内容源于朱有鹏嵌入式课程的学习与整理如有侵权请告知删除。
本文首先分析ORTP库的组成然后以rtpsend.c为例说明ORTP库的使用方法最后分析第三季1中的RTP发送实验的源码。 一、ORTP库的源码分析
1、ORTP库概览
1库提供一堆功能函数本身没有main在ortp-master/src目录下。
rootubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/src# ls
avprofile.c jitterctl.c master ortp_srtp.c rtcp.c rtpsession.c rtptimer.c sessionset.c system utils.h
b64.c jitterctl.h netsim.c payloadtype.c rtcpparse.c rtpsession_inet.c rtptimer.h str_utils.c telephonyevents.c zrtp.c
dll_entry.c logging.c ortp.c port.c rtpparse.c rtpsession_priv.h scheduler.c stun.c tests
event.c Makefile.am ortp-config-win32.h posixtimer.c rtpprofile.c rtpsignaltable.c scheduler.h stun_udp.c utils.c
rootubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/src#
2库的使用给了案例有main在ortp-master/src/tests目录下。
rootubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/src/tests# ls
Makefile.am mrtpsend.c rtprecv.c rtpsend_stupid.c tevmrtprecv.c tevrtpsend.c win_sender
mrtprecv.c rtpmemtest.c rtpsend.c test_timer.c tevrtprecv.c win_receiver
rootubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/src/tests#
3相关数据结构和头文件在ortp-master/include/ortp目录下。
rootubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/include/ortp# ls
b64.h logging.h ortp.h payloadtype.h rtcp.h rtpprofile.h rtpsignaltable.h str_utils.h stun_udp.h zrtp.h
event.h Makefile.am ortp_srtp.h port.h rtp.h rtpsession.h sessionset.h stun.h telephonyevents.h
rootubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/include/ortp#
4ortp实现了rtp和rtcp协议前者负责传输后者负责控制和同步协调。
2、ORTP库的使用案例分析
这里以ortp-master/src/tests/rtpsend.c文件为例进行说明该文件内容如下
#include ortp/ortp.h
#include signal.h
#include stdlib.h#ifndef _WIN32
#include sys/types.h
#include sys/time.h
#include stdio.h
#endifint runcond1;void stophandler(int signum)
{runcond0;
}static const char *helpusage: rtpsend filename dest_ip4addr dest_port [ --with-clockslide value ] [ --with-jitter milliseconds]\n;int main(int argc, char *argv[])
{RtpSession *session;//定义一个会话unsigned char buffer[160];int i;FILE *infile;char *ssrc;uint32_t user_ts0;int clockslide0;int jitter0;if (argc4){printf(%s, help);return -1;}for(i4;iargc;i){if (strcmp(argv[i],--with-clockslide)0){i;if (iargc) {printf(%s, help);return -1;}clockslideatoi(argv[i]);ortp_message(Using clockslide of %i milisecond every 50 packets.,clockslide);}else if (strcmp(argv[i],--with-jitter)0){ortp_message(Jitter will be added to outgoing stream.);i;if (iargc) {printf(%s, help);return -1;}jitteratoi(argv[i]);}}ortp_init();ortp_scheduler_init();ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);sessionrtp_session_new(RTP_SESSION_SENDONLY); rtp_session_set_scheduling_mode(session,1);rtp_session_set_blocking_mode(session,1);rtp_session_set_connected_mode(session,TRUE);rtp_session_set_remote_addr(session,argv[2],atoi(argv[3]));rtp_session_set_payload_type(session,0);ssrcgetenv(SSRC);if (ssrc!NULL) {printf(using SSRC%i.\n,atoi(ssrc));rtp_session_set_ssrc(session,atoi(ssrc));}#ifndef _WIN32infilefopen(argv[1],r);#elseinfilefopen(argv[1],rb);#endifif (infileNULL) {perror(Cannot open file);return -1;}signal(SIGINT,stophandler);while( ((ifread(buffer,1,160,infile))0) (runcond) ){rtp_session_send_with_ts(session,buffer,i,user_ts);user_ts160;if (clockslide!0 user_ts%(160*50)0){ortp_message(Clock sliding of %i miliseconds now,clockslide);rtp_session_make_time_distorsion(session,clockslide);}/*this will simulate a burst of late packets */if (jitter (user_ts%(8000)0)) {struct timespec pausetime, remtime;ortp_message(Simulating late packets now (%i milliseconds),jitter);pausetime.tv_secjitter/1000;pausetime.tv_nsec(jitter%1000)*1000000;while(nanosleep(pausetime,remtime)-1 errnoEINTR){pausetimeremtime;}}}fclose(infile);rtp_session_destroy(session);ortp_exit();ortp_global_stats_display();return 0;
}1RTP中的会话session
RTP通过会话来管理数据发送和接收。
会话的本质是RtpSession这个结构体类型的实例化或者这个类型的一个变量比如这里就定义了一个RtpSession类型的指针变量session。
创建会话用rtp_session_new函数发送数据包用rtp_session_send_with_ts函数而底层真正干活的还是socket接口那一套参考rtpsession_inet.c文件。
2ortp_init函数
该函数主要用来初始化ORTP库其内容如下
/*** Initialize the oRTP library. You should call this function first before using* oRTP API.
**/
void ortp_init()
{if (ortp_initialized) return;ortp_initialized;
//省略部分av_profile_init(av_profile);//函数返回时ortp_global_stats_reset();init_random_number_generator();//省略部分ortp_message(oRTP- ORTP_VERSION initialized.);
}
其中主要是av_profile_init函数我们来重点分析这个函数。此函数主要用来初始化RtpProfile类型的变量av_profile该变量记录着可以识别哪些profile当需要识别新的profile时要在该函数里添加相应的内容。这就是在博文第三季1ORTP库的移植与视频的实时传输第二节第2点中进行那样操作的原因。
av_profile变量的数据类型即RtpProfile类型的定义如下
/*** 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];
}; av_profile_init函数内容如下
void av_profile_init(RtpProfile *profile)
{rtp_profile_clear_all(profile);profile-nameAV profile;//名字是不变的//省略部分代码//即profile-payload[32] payload_type_mpvrtp_profile_set_payload(profile,32,payload_type_mpv);//即profile-payload[34] payload_type_h263rtp_profile_set_payload(profile,34,payload_type_h263);//即profile-payload[96] payload_type_h264rtp_profile_set_payload(profile,96,payload_type_h264);//这个就是后来添加的
}//其中payload_type_h264定义如下
PayloadType payload_type_h264{TYPE( PAYLOAD_VIDEO),CLOCK_RATE(90000),BITS_PER_SAMPLE(0),ZERO_PATTERN(NULL),PATTERN_LENGTH(0),NORMAL_BITRATE(256000),MIME_TYPE (H264),CHANNELS(0)
};
3ortp_scheduler_init函数
该函数是ORTP调度器或者说仲裁机构一段代码功能是在一个任务中完成多个会话的发送和接收类似于select。
3、ORTP的一些细节
1port.c中对OS的常用机制任务创建和销毁、进程管理和信号量等进行了封装便于将ortp移植到不同平台中。
2utils.c中实现了一个双向链表。
3str_util.c中实现了一个队列管理。
4rtpparse.c和rtcpparse.c文件实现了解析收到的rtp数据包的部分。
5jitterctl.c中实现了jitter buffer来防抖。jitter buffer技术是ip 音视频通信里相对比较高级的主题jitter buffer模块好坏通常是衡量一个voip客户端/服务器好坏的技术点之一尤其是在网络抖动比较严重如3g, wifi环境数据包的rtt值不均衡往往会导致语音卡顿丢字等现象jitter buffer 模块通过缓存一段数据包把数据包重排并均匀的送给播放端一个好的jitter buffer实现通长是动态调整缓存大小的在网络延迟大抖动严重时会动态增加缓存大小在网络恢复时动态减小缓存大小以减少端到端的播放延迟。 二、RTP发送实验的源码分析
RTP发送实验的源码是指博文第三季1ORTP库的移植与视频的实时传输中提到的修改后的两个文件mpp/sample/venc/sample_venc.c、mpp/sample/common/sample_common_venc.c。
使用ORTP库的sample和第二季的sample它们的步骤相同只是在“ 如何处理编码得到的码流 ”这个问题上不同。第二季的sample中是将码流保存为裸流文件而使用 ORTP库的 sample是将码流数据以包的形式在网络上传输。因此修改点就在第二季的sample中将码流保存为裸流文件的代码处。 博文第三季1ORTP库的移植与视频的实时传输中提到修改点主要有以下两处。
1在SAMPLE_COMM_VENC_GetVencStreamProc函数的step2之前添加下面代码
/******************************************************************************
* funciton : get stream from each channels and save them
******************************************************************************/
HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID *p)
{//省略部分代码#if ORTP_ENABLE/***rtp init****/pRtpSession rtpInit( LOCAL_HOST_IP ,8080); if (pRtpSessionNULL) { printf( error rtpInit ); exit(-1); return 0; } #endif/******************************************step 2: Start to get streams of each channel.***********************************************///省略部分代码
上面调用了rtpInit函数这个函数主要用来初始化RTP及其他参数函数内容如下。分析可知它和ortp-master/src/tests/rtpsend.c文件中main函数部分代码很类似。
/** 初始化 * * 主要用于对ortp以及其它参数进行初始化 * param: char * ipStr 目的端IP地址描述串 * param: int port 目的端RTP监听端口 * return: RtpSession * 返回指向RtpSession对象的指针,如果为NULL则初始化失败 * note: */
RtpSession * rtpInit( char * ipStr, int port)
{RtpSession *session; char *ssrc;printf(********oRTP for H.264 Init********\n);ortp_init();ortp_scheduler_init();ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);sessionrtp_session_new(RTP_SESSION_SENDONLY); rtp_session_set_scheduling_mode(session,1);rtp_session_set_blocking_mode(session,0);//rtp_session_set_connected_mode(session,TRUE);rtp_session_set_remote_addr(session,ipStr,port);rtp_session_set_payload_type(session,Y_PLOAD_TYPE);ssrcgetenv(SSRC);if (ssrc!NULL) {printf(using SSRC%i.\n,atoi(ssrc));// 设置输出流的SSRC。不做此步的话将会给个随机值 rtp_session_set_ssrc(session,atoi(ssrc));}return session;
}
2修改SAMPLE_COMM_VENC_SaveH264函数如下所示
/******************************
* funciton : save H264 stream
*******************************/
HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{HI_S32 i;for (i 0; i pstStream-u32PackCount; i){#if ORTP_ENABLE//这里修改过rtpSend(pRtpSession,pstStream-pstPack[i].pu8Addr, pstStream-pstPack[i].u32Len);#elsefwrite(pstStream-pstPack[i].pu8AddrpstStream-pstPack[i].u32Offset,pstStream-pstPack[i].u32Len-pstStream-pstPack[i].u32Offset, 1, fpH264File);fflush(fpH264File);#endif}return HI_SUCCESS;
}
上面调用了rtpSend函数这个函数主要用来发送RTP数据包。该函数位于修改后的/sample_comm_venc.c文件中内容如下。
/** 发送rtp数据包 * * 主要用于发送rtp数据包 * param: RtpSession *session RTP会话对象的指针 * param: const char *buffer 要发送的数据的缓冲区地址 * param: int len 要发送的数据长度 * return: int 实际发送的数据包数目 * note: 如果要发送的数据包长度大于BYTES_PER_COUNT本函数内部会进行分包处理 */
int rtpSend(RtpSession *session, char *buffer, int len)
{ int sendBytes 0; int status; uint32_t valid_lenlen-4;unsigned char NALUbuffer[4];//如果数据小于MAX_RTP_PKT_LENGTH字节直接发送单一NAL单元模式if(valid_len MAX_RTP_PKT_LENGTH){sendBytes rtp_session_send_with_ts(session,buffer[4],valid_len,g_userts);}else if (valid_len MAX_RTP_PKT_LENGTH){//切分为很多个包发送每个包前要对头进行处理如第一个包valid_len - 1;int k0,l0;kvalid_len/MAX_RTP_PKT_LENGTH;lvalid_len%MAX_RTP_PKT_LENGTH;int t0;int pos5;if(l!0){kk1;}while(tk)//||(tkl0)){if(t(k-1))//(tkl!0)||(t(k-1))(l0))//(0t)||(tk0!l)){buffer[pos-2](NALU 0x60)|28;buffer[pos-1](NALU 0x1f);if(0t){buffer[pos-1]|0x80;}sendBytes rtp_session_send_with_ts(session,buffer[pos-2],MAX_RTP_PKT_LENGTH2,g_userts);t;posMAX_RTP_PKT_LENGTH;}else //if((ktl0)||((tk-1)l0)){int iSendLen;if(l0){iSendLenvalid_len-t*MAX_RTP_PKT_LENGTH;}elseiSendLenMAX_RTP_PKT_LENGTH;buffer[pos-2](NALU 0x60)|28;buffer[pos-1](NALU 0x1f);buffer[pos-1]|0x40;sendBytes rtp_session_send_with_ts(session,buffer[pos-2],iSendLen2,g_userts);t;}}}g_userts DefaultTimestampIncrement;//timestamp increasereturn len;
} 三、可拓展的内容
1将这个sample裁剪到最简化。
2修改一些参数做实验譬如每包字节数、IP地址、端口号等。