第三季2:ORTP库与RTP发送实验的源码分析

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

本文首先分析ORTP库的组成,然后以rtpsend.c为例说明ORTP库的使用方法,最后分析第三季1中的RTP发送实验的源码。

一、ORTP库的源码分析

1、ORTP库概览

(1)库提供一堆功能函数(本身没有main),在ortp-master/src目录下。

root@ubuntu:/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
root@ubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/src#

(2)库的使用给了案例(有main),在ortp-master/src/tests目录下。

root@ubuntu:/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
root@ubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/src/tests# 

(3)相关数据结构和头文件,在ortp-master/include/ortp目录下。

root@ubuntu:/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
root@ubuntu:/home/xjh/iot/hisi_development/ortp/ortp-master/include/ortp#

(4)ortp实现了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 runcond=1;void stophandler(int signum)
{runcond=0;
}static const char *help="usage: 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_ts=0;int clockslide=0;int jitter=0;if (argc<4){printf("%s", help);return -1;}for(i=4;i<argc;i++){if (strcmp(argv[i],"--with-clockslide")==0){i++;if (i>=argc) {printf("%s", help);return -1;}clockslide=atoi(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 (i>=argc) {printf("%s", help);return -1;}jitter=atoi(argv[i]);}}ortp_init();ortp_scheduler_init();ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);session=rtp_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);ssrc=getenv("SSRC");if (ssrc!=NULL) {printf("using SSRC=%i.\n",atoi(ssrc));rtp_session_set_ssrc(session,atoi(ssrc));}#ifndef _WIN32infile=fopen(argv[1],"r");#elseinfile=fopen(argv[1],"rb");#endifif (infile==NULL) {perror("Cannot open file");return -1;}signal(SIGINT,stophandler);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(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_sec=jitter/1000;pausetime.tv_nsec=(jitter%1000)*1000000;while(nanosleep(&pausetime,&remtime)==-1 && errno==EINTR){pausetime=remtime;}}}fclose(infile);rtp_session_destroy(session);ortp_exit();ortp_global_stats_display();return 0;
}

(1)RTP中的会话(session)

RTP通过会话来管理数据发送和接收。

会话的本质,是RtpSession这个结构体类型的实例化(或者这个类型的一个变量),比如这里就定义了一个RtpSession类型的指针变量session。

创建会话用rtp_session_new函数,发送数据包用rtp_session_send_with_ts函数,而底层真正干活的还是socket接口那一套,参考rtpsession_inet.c文件。

(2)ortp_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时,要在该函数里添加相应的内容。这就是在博文第三季1:ORTP库的移植与视频的实时传输第二节第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->name="AV 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)
};

(3)ortp_scheduler_init函数

该函数是ORTP调度器或者说仲裁机构(一段代码),功能是在一个任务中完成多个会话的发送和接收,类似于select。

3、ORTP的一些细节

(1)port.c中对OS的常用机制(任务创建和销毁、进程管理和信号量等)进行了封装,便于将ortp移植到不同平台中。

(2)utils.c中实现了一个双向链表。

(3)str_util.c中实现了一个队列管理。

(4)rtpparse.c和rtcpparse.c文件实现了解析收到的rtp数据包的部分。

(5)jitterctl.c中实现了jitter buffer来防抖。jitter buffer技术是ip 音视频通信里相对比较高级的主题,jitter buffer模块好坏通常是衡量一个voip客户端/服务器好坏的技术点之一,尤其是在网络抖动比较严重,如3g, wifi环境,数据包的rtt值不均衡往往会导致语音卡顿,丢字等现象,jitter buffer 模块通过缓存一段数据包,把数据包重排,并均匀的送给播放端,一个好的jitter buffer实现通长是动态调整缓存大小的,在网络延迟大,抖动严重时会动态增加缓存大小,在网络恢复时动态减小缓存大小以减少端到端的播放延迟。

二、RTP发送实验的源码分析

RTP发送实验的源码,是指博文第三季1:ORTP库的移植与视频的实时传输中提到的修改后的两个文件:mpp/sample/venc/sample_venc.c、mpp/sample/common/sample_common_venc.c

使用ORTP库的sample和第二季的sample,它们的步骤相同,只是在“ 如何处理编码得到的码流 ”这个问题上不同。第二季的sample中是将码流保存为裸流文件,而使用 ORTP库的 sample是将码流数据以包的形式在网络上传输。因此修改点就在第二季的sample中将码流保存为裸流文件的代码处。

 博文第三季1:ORTP库的移植与视频的实时传输中提到,修改点主要有以下两处。

(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 (pRtpSession==NULL)   {   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);session=rtp_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);ssrc=getenv("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].pu8Addr+pstStream->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_len=len-4;unsigned char NALU=buffer[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 k=0,l=0;k=valid_len/MAX_RTP_PKT_LENGTH;l=valid_len%MAX_RTP_PKT_LENGTH;int t=0;int pos=5;if(l!=0){k=k+1;}while(t<k)//||(t==k&&l>0)){if(t<(k-1))//(t<k&&l!=0)||(t<(k-1))&&(l==0))//(0==t)||(t<k&&0!=l)){buffer[pos-2]=(NALU & 0x60)|28;buffer[pos-1]=(NALU & 0x1f);if(0==t){buffer[pos-1]|=0x80;}sendBytes = rtp_session_send_with_ts(session,&buffer[pos-2],MAX_RTP_PKT_LENGTH+2,g_userts);t++;pos+=MAX_RTP_PKT_LENGTH;}else //if((k==t&&l>0)||((t==k-1)&&l==0)){int iSendLen;if(l>0){iSendLen=valid_len-t*MAX_RTP_PKT_LENGTH;}elseiSendLen=MAX_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],iSendLen+2,g_userts);t++;}}}g_userts += DefaultTimestampIncrement;//timestamp increasereturn  len;
}

三、可拓展的内容

(1)将这个sample裁剪到最简化。

(2)修改一些参数做实验(譬如每包字节数、IP地址、端口号等)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460924.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线程让出实验【RT-Thread学习笔记 4】

API: rt_thread_yield 线程函数中调用&#xff0c;本线程释放MCU。如果此时有别的相同优先级的任务整处于等待状态&#xff0c;将获得MCU使用权。 线程让出就是给OS增加一个任务调度的机会。 创建两个线程&#xff0c;观察他们的结果&#xff1a; //线程让出试验 void yield_te…

你或许不了解的C++函数调用(1)

这篇博客名字起得可能太自大了&#xff0c;搞得自己像C大牛一样&#xff0c;其实并非如此。C有很多隐藏在语法之下的特性&#xff0c;使得用户可以在不是特别了解的情况下简单使用&#xff0c;这是非常好的一件事情。但是有时我们可能会突然间发现一个很有意思的现象&#xff0…

有赞下拉菜单html,有赞vant-ui Tabs、List、PullRefresh组件实践

Vant ui Vue.js 部分组件实践功能需求是实现一个移动端的栏目列表切换&#xff0c;于此同时列表需要进行下拉刷新&#xff0c;上拉加载如下图&#xff0c;大概是一个这样的东西看起来是挺简单的&#xff0c;实现起来很方便。没错&#xff0c;我当时也是这么想的&#xff0c;结…

第五季1:AP模式USB-WIFI网卡移植与测试

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 内容总结 &#xff08;1&#xff09;HI3518EWIFI做AP&#xff0c;PC做STA&#xff0c;实现局域网内ortp视频传输。 &#xff08;2&#xff09;HI2518EWIFI做STA&#xff0c;PC做STA&#xff0c;外部…

免费社交类图标集

免费下载 这是一套免费社交类型的图标集&#xff0c;包含35种不同内容的图标。包括当前最热的Instagram&#xff0c;Evernatoe等。和往常推荐的一样&#xff0c;PSD文件可自行调整大小,50*50像素。它们可以被用在商业项目上&#xff0c;如果你也喜欢相关类型的图标&#xff0c;…

如何把图片嵌到html中,自给自足,轻松将图片图片内嵌到HTML

自给自足&#xff0c;轻松将图片图片内嵌到HTML1.为什么要将图片内嵌到HTML&#xff1f;针对于这个问题&#xff0c;我们有如下几种答案1)能减少并发请求&#xff0c;适合且合理的使用&#xff0c;能提高网站的访问速度2)便于分享&#xff0c;完全可以将需要的图片&#xff0c;…

第五季2:STA模式USB-WIFI网卡移植与测试

以下内容源于朱有鹏课程&#xff0c;如有侵权&#xff0c;请告知删除。 参考博客 hisi3518ev200移植MT7601_fsczp的博客-CSDN博客 wpa_supplicant移植与使用_andylauren的博客-CSDN博客 Linux wpa_cli 调试方法_weixin_34168700的博客-CSDN博客 12 海思Hi3518E移植WIFI驱动(ST…

关于ping命令的工作原理

2019独角兽企业重金招聘Python工程师标准>>> 当我们在用ping命令ping地址的时候会向target主机发送一个ICMP协议中的echo包&#xff0c;如果对方living&#xff08;活动状态&#xff09;。 则会返回一个echo包。 TTL的值&#xff1a;这个值对我们有什么用&#xff0…

SPOJ-OPTM Optimal Marks ★★(按位建图 最小割)

【题意】给出一个无向图&#xff0c;每个点有一个标号mark[i]&#xff0c;不同点可能有相同的标号。对于一条边(u, v)&#xff0c;它的权值定义为mark[u] xor mark[v]。现在一些点的标号已定&#xff0c;请决定剩下点的标号&#xff0c;使得总的边权和最小。&#xff08;0 <…

第七季1:分析MP4文件封装格式

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考内容 &#xff08;1&#xff09;mp4文件格式解析 - nigaopeng - 博客园&#xff08;推荐&#xff09; &#xff08;2&#xff09;你真的懂 MP4 格式吗&#xff1f; &#xff08;3&#xff09;m…

ESXi主机管理内存资源的方式

因为内存通常是最有限的资源&#xff0c;ESXi采用内存过量配置&#xff08;Memory overcommitment&#xff0c;即配置后的虚拟机内存可能超过物理内存&#xff08;RAM&#xff09;&#xff09;对内存进行管理。使用内存过量配置时&#xff0c;ESXi必须使用技术从一个或多个VM中…

第七季2:移植MP4v2库 与 将H.264码流打包成MP4格式的文件

以下内容源于朱有鹏课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 本文首先移植MP4V2库&#xff0c;然后运行修改后的sample代码以生成MP4文件。 利用MP4v2库&#xff0c;可以将编码方式为H264的视频码流打包、封装为MP4格式的文件。 一、移植MP4v2库 1、下载MP4…

新高考不选物理可以学计算机吗,新高考选科中物理真的那么重要吗?没有选物理是不是完了?...

文/圆梦志愿 伏老师众所周知&#xff0c;选科是我国新高考中最重要的事情之一&#xff0c;且涉及到了多门选考科目。其中&#xff0c;要数物理的话题性相对最强。那么&#xff0c;高考选科是不是物理特别重要&#xff1f;新高考没有选物理是不是完了&#xff1f;一、高考选科是…

参加第三届信息化创新克拉玛依国际学术论坛

据老孙说前二届在克拉玛依的数字油田的会议规格很高&#xff0c;可惜一直没机会参加。2014年9月3日至4日举办第三届&#xff0c;无论如何抓住了这次机会&#xff0c;不过会议的内容有些让我失望&#xff0c;克拉玛依的心思已经放在数字城市上了&#xff0c;数字油田的主题被冲淡…

UNICODE,GBK,UTF-8区别

简 单来说&#xff0c;unicode&#xff0c;gbk和大五码就是编码的值&#xff0c;而utf-8,uft-16之类就是这个值的表现形式&#xff0e;而前面那三种编码是一兼容的&#xff0c;同一个汉字&#xff0c; 那三个码值是完全不一样的&#xff0e;如&#xff02;汉&#xff02;的unco…

学生使用计算机中怎么关机,学会正确开关机初中计算机教案

第1篇&#xff1a;学会正确开关机初中计算机教案学会正确开、关机教案教学目的和要求学会开、关机教学难点&#xff1a;1、了解计算机外设的开、关顺序2、正确学会开、关机教学准备&#xff1a;计算机、网络教学过程()&#xff1a;一、教学导入同学们&#xff0c;在你们面前看到…

Telnet远程登录 与 海思proc文件系统

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c; 如有侵权请告知删除。 内容总结 &#xff08;1&#xff09;添加“ telnet远程登录 ”这种调试方式。 &#xff08;2&#xff09;介绍“ 海思proc文件系统 ”这种调试接口。 一、telnet远程登录 1、telnet远程登录的意…

北邮-上机-提交错误解决及一些经验

2019独角兽企业重金招聘Python工程师标准>>> 1、出现Time Limit Exceed问题 答&#xff1a;三种原因&#xff1a;1、程序死循环或太多的循环&#xff1b;2、对极限状况计算量太大&#xff0c;需要优化程序&#xff0c;如对10000个以上进行排序必须使用快速排序&…

study notes for python

some useful materials Python完全新手教程 http://www.cnblogs.com/taowen/articles/11239.aspx (from taowen, BITer) Note: Part 1 Basic Data Structure List, Dict(dictionary) and Turple are three main data structures in python, which are respond to set,mapping a…

html如何制作滑块,网页制作html5实现滑块功能之type=quot;rangequot;属性-建站-建站教程-建站方法-米云建站 - 米云问答...

html5实现滑块功能之type"range"属性1.html5中添加了关于滑块的标签,其实际是扩展了input标签,type属性值为range。2.感觉挺有意思,就做了一个关于滑块的动画的例子&#xff0c;可以通过开始和结束按钮来控制滑块的滑动和停止&#xff0c;有点类似视频的自动播放和暂…