ORTP库局域网图传和VLC实时预览

news/2025/1/31 11:08:03/

1.ORTP的引入

1.1、视频网络传输的2种方式

(1)基于下载:http or ftp(网站播放视频,追求清晰度,哪怕时间晚一点)

(2)基于实时:RTP/RTSP/RTCP(直播、监控,追求实时,哪怕画面清晰度差一点)

1.2、ORTP的介绍

(1)openRTP,用C实现的一个RTP库(其实还有C++实现的,JAVA等实现的)

(2)RTP(及RTCP)的实现有国际标准RFC3550规定,只要符合协议谁都可以自己写一个

(3)实质是一个视频服务器,工作时客户端和服务器实时传递视频数据

(4)一般认为RTP工作在传输层,但是其实RTP比TCP/UDP高一个层次

2.ORTP库的移植

2.1、准备源码

(1)下载ortp源码:https://github.com/dmonakhov/ortp

(2)存放到临时工作目录并解压

2.2、源码修改

(1)增加H.264的payload支持。

在src/avprofile.c中357行添加:

rtp_profile_set_payload(profile,96,&payload_type_h264);

2.3、配置和编译、安装

(1)进入ortp目录执行./autogen.sh

(2)错误1:./autogen.sh: line 44: libtoolize: command not found

解决:sudo apt-get install libtool*

(2)错误2:libtoolize: error: Please install GNU M4, or ‘export M4=/path/to/gnu/m4’.

解决:sudo apt-get install m4

(3)错误3:Automake - aclocal: command not found

解决:sudo apt-get install automake

(4)继续执行./configure --prefix=/tmp/ortp --host=arm-hisiv300-linux

(5)make && make install 编译并安装

2.4、到/tmp/ortp目录下查看移植好的库和头文件

​​在这里插入图片描述

3.RTP传输视频实战

3.1、在官方SDK的sample中添加rtp传输代码

(1)venc/sample_venc.c中,设置:s32ChnNum = 1;

(2)common/sample_common_venc.c中,改了很多

3.2、重新编译sample

(1)复制ortp头文件“目录”到mpp/include目录下
在这里插入图片描述

(2)修改venc中Makefile,添加libortp动态库的链接支持
在这里插入图片描述

(3)make编译

3.3、开发板中部署并运行测试

(1)部署libortp.so等动态库到开发板中/usr/lib目录下

(2)在nfs中运行新的sample程序

(3)vlc中打开配置好的sdp文件,看到实时图像就说明网络传输成功了
demo.sdp文件内容:

m=video 8080 RTP/AVP 96
a=rtpmap:96 H264
a=framerate:25
c=IN IP4 192.168.1.20# sdp文件是VLC软件的配置文件,它告诉VLC播放器接收到的数据是怎样的。
# m=vidio,表示这是视频信息;8080是端口;
# RTP/AVP 96,表明传输过来的网络协议;96表示是h264的。
# a=framerate:25 这表示帧率;
# c=IN IP4 192.168.1.20 表示VLC所在主机的IP地址(window的ip地址)

4.ORTP库的源码分析

4.1、ORTP库概览

(1)库本身没有main,提供一堆功能函数,都在src目录下

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

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

(4)ortp实现了rtp和rtcp协议,前者负责传输,后者负责控制和同步协调

4.2、ORTP库的使用案例

(1)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;}ortp_init();//该函数主要用来初始化ORTP库ortp_scheduler_init();//ORTP调度器的初始化,调度器用于调度会话ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);//记录log信息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]));//设置要发出去的IP地址和端口号rtp_session_set_payload_type(session,0);//设置当前会话的格式---》H.264ssrc=getenv("SSRC");//SSRC是用于区分不同会话之间的标记if (ssrc!=NULL) {printf("using SSRC=%i.\n",atoi(ssrc));rtp_session_set_ssrc(session,atoi(ssrc));}infile=fopen(argv[1],"r");//打开编码文件if (infile==NULL) {perror("Cannot open file");return -1;}signal(SIGINT,stophandler);while( ((i=fread(buffer,1,160,infile))>0) && (runcond) )//读文件内容到buffer{rtp_session_send_with_ts(session,buffer,i,user_ts);//rtp包的发送函数,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;
}

session:会话,一个发射端和一个接收端之间的通信,ORTP库同时支持多路会话,RTP通过会话来管理数据发送和接收。

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

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

(2)ortp_init及av_profile_init
该函数主要用来初始化ORTP库

void ortp_init()
{if (ortp_initialized) return;ortp_initialized++;			//防止重复调用av_profile_init(&av_profile);//音频视频的规范	初始化ortp_global_stats_reset();//一些全局变量清0init_random_number_generator();//初始随机数生成器ortp_message("oRTP-" ORTP_VERSION " initialized.");
}

其中主要是av_profile_init函数,我们来重点分析这个函数。此函数记录着ORTP可以支持哪些会话格式,当需要支持新的会话格式(譬如h.264)时,只需要在该函数里添加相应的内容即可。

void av_profile_init(RtpProfile *profile)
{rtp_profile_clear_all(profile);profile->name="AV profile";rtp_profile_set_payload(profile,0,&payload_type_pcmu8000);rtp_profile_set_payload(profile,1,&payload_type_lpc1016);rtp_profile_set_payload(profile,3,&payload_type_gsm);rtp_profile_set_payload(profile,7,&payload_type_lpc);rtp_profile_set_payload(profile,4,&payload_type_g7231);rtp_profile_set_payload(profile,8,&payload_type_pcma8000);rtp_profile_set_payload(profile,9,&payload_type_g722);rtp_profile_set_payload(profile,10,&payload_type_l16_stereo);rtp_profile_set_payload(profile,11,&payload_type_l16_mono);rtp_profile_set_payload(profile,18,&payload_type_g729);rtp_profile_set_payload(profile,31,&payload_type_h261);rtp_profile_set_payload(profile,32,&payload_type_mpv);rtp_profile_set_payload(profile,34,&payload_type_h263);rtp_profile_set_payload(profile,96,&payload_type_h264);
}

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

4.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实现通长是动态调整缓存大小的,在网络延迟大,抖动严重时会动态增加缓存大小,在网络恢复时动态减小缓存大小以减少端到端的播放延迟。

5.RTP发送实验源码分析

(1)在SAMPLE_COMM_VENC_GetVencStreamProc函数的step2之前,添加下面代码:

HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID *p)
{//省略部分代码#if ORTP_ENABLE/***rtp init****/pRtpSession = rtpInit( LOCAL_HOST_IP ,8080); //LOCAL_HOST_IP  宏为 "192.168.1.20"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//使用ORTP库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)//MAX_RTP_PKT_LENGTH为1400{sendBytes = rtp_session_send_with_ts(session,&buffer[4],valid_len,g_userts);//从第四个字节开始发包}else if (valid_len > MAX_RTP_PKT_LENGTH){//切分为很多个包发送,每个包前要对头进行处理,如第一个包valid_len -= 1;//之前-4,现在又-1,总共-5,跳过了h.264的包头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;
}

关于H264码流解析和NALU参考博文

:https://www.cnblogs.com/jingzhishen/p/3965868.html

6.可拓展的内容

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

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


http://www.ppmy.cn/news/179689.html

相关文章

哪款蓝牙耳机性价比最高?2023性价比高的蓝牙耳机推荐

近年来&#xff0c;随着蓝牙耳机市场的快速膨胀&#xff0c;蓝牙耳机的使用频率越来越高。那么&#xff0c;在众多的蓝牙耳机当中&#xff0c;哪款蓝牙耳机性价比最高&#xff1f;下面&#xff0c;我来给大家推荐几款性价比高的蓝牙耳机。 一、南卡小音舱蓝牙耳机 售价&#…

无线蓝牙耳机哪款性价比最高?2022蓝牙耳机性价比排行榜

如今购买蓝牙耳机的人是越来越多了&#xff0c;也是由此证明了无线蓝牙耳机在当下市场的火爆性&#xff0c;这主要还是因为蓝牙耳机外出方便携带的原因吧。但也因市面上的蓝牙耳机太多了&#xff0c;使得很多小伙伴们在挑选的时候会比较迷茫&#xff0c;不过不要担心&#xff0…

高性价比真无线耳机哪款好?2022性价比蓝牙耳机推荐

性价比高的蓝牙耳机有哪些&#xff1f;高性价比高的东西最终总能让人产生购买欲望。这几款高性价比蓝耳机都是性价比蓝牙耳机排行榜的前几名。想购买高性价比蓝牙耳机&#xff0c;可以从以下性价比高排行榜中挑选。 1.南卡小音舱蓝牙耳机 参考价格&#xff1a;199元 蓝牙版本…

400元左右的蓝牙耳机啥牌子好?400元价位蓝牙耳机推荐

随着人们越来越倾向于使用随身便携的电子产品&#xff0c;轻松上阵、无线自由的TWS蓝牙耳机越来越受消费者的青睐&#xff0c;成为现在耳机行业的新星&#xff0c;下面整理了几款400元价位的耳机品牌。 一、南卡小音舱Lite2蓝牙耳机 参考价格&#xff1a;239元 佩戴方式&…

2022有什么耳机音质好又便宜?便宜音质好的蓝牙耳机推荐

蓝牙耳机怎么挑选&#xff1f;有没有性价比高的蓝牙耳机推荐&#xff1f;这是很多朋友都咨询过我的问题&#xff0c;作为蓝牙耳机的常年使用者&#xff0c;对于蓝牙耳机十分了解&#xff0c;也因此比较了解市面上的蓝牙耳机&#xff0c;现在市面上涌现出了不少优质的平价国货产…

【动态规划】通配符匹配与正则表达式匹配

文章目录 一、通配符匹配1.1 思路分析1.2 初始化处理1.3 代码1.4 优化 二、正则表达式匹配2.1 思路分析2.2 初始化设置2.3 代码 一、通配符匹配 题目描述&#xff1a; 给你一个输入字符串 (s) 和一个字符模式 &#xff0c;请你实现一个支持 ‘?’ 和 ‘*’ 匹配规则的通配符…

2022高性价比蓝牙耳机推荐,2022真无线耳机音质排行榜

近年来&#xff0c;随着蓝牙技术的发展&#xff0c;蓝牙耳机的音质呈现也有了巨大的突破&#xff0c;这就吸引更多的人开始使用蓝牙耳机了&#xff0c;下面就整理了一些音质出色的蓝牙耳机。 1、Xisem西圣ares pro 蓝牙耳机 如果市面上的蓝牙耳机非得选出“蓝牙耳机性价比之王…

400左右的蓝牙耳机哪个比较好?400元价位蓝牙耳机推荐

耳机是我们生活中常见的数码设备&#xff0c;尤其是当一个在外的时候&#xff0c;有了耳机的怕陪伴显得没有那么孤独&#xff0c;仿佛有了自己的小天地一样&#xff0c;除此以外&#xff0c;对于经常打电话的人&#xff0c;也更加的便携&#xff0c;下面推荐几款400元价位的整体…