libsrt_操作及相关记录
Posted 朱韦刚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了libsrt_操作及相关记录相关的知识,希望对你有一定的参考价值。
libsrt_操作及相关记录(一)
时间:2020年6月11日;
一:libsrt的相关链接:
1:https://www.srtalliance.org/(libstr的官网);
2:https://github.com/Haivision/srt/(libsrt的源码下载地址);
3:https://github.com/Haivision/srt/releases/(libsrt的
源码更新情况及相关添加情况和bug修改情况);
4:https://github.com/Haivision/srt/blob/master/docs/;(libsrt的
相关的资料整理)
5:https://blog.csdn.net/mediapro/article/details/105790057/
(libsrt的的相关文章1);
6:https://blog.csdn.net/zhuweigangzwg/category_10052053.html/
(我的博客:libsrt的相关文章整理1);
二:ffmpeg关于libsrt的支持情况及测试步骤;
(一):ffmpeg的windows版本支持;
srt已经编译好的下载地址:https://ffmpeg.zeranoe.com/builds/;如果用api就自己组装include,lib,dll。如果直接用ffmpeg.exe下载找到用即可.
(二):关于srt的一些较少编译什么的可以看这篇文章:https://blog.csdn.net/zhuweigangzwg/article/details/106241458;里面的sls服务器是专门用于srt的服务器。
(三):srt的linux编译:步骤如下:
1:sudo yum install openssl-devel(下载openssl);
2:https://github.com/Haivision/srt(下载srt源码);
3:cd srt-master;
4:cmake -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_C_DEPS=ON -DENABLE_SHARED=OFF -DENABLE_STATIC=ON;
5:sudo make && sudo make install;会生成/usr/local/lib64/libsrt.a; 和/usr/local/include/srt;
(四):srt的服务器编译;
https://github.com/Edward-Wu/srt-live-server;步骤如下:
1: cd到srt-live-server-master目录下;
2:sudo make,该目录下有makefile文件.
会出现:slscore/SLSEpollThread.hpp:29:21: 致命错误:srt/srt.h:没有那个文件或目录;
参考README.md会发现:Requirements:
please install the SRT first, refer to SRT(https://github.com/Haivision/srt) for system enviroment. SLS can only run on OS based on linux, such as mac, centos or ubuntu etc.
要求
请先安装SRT,有关系统环境,请参考SRT(https://github.com/Haivision/srt)。 SLS只能在基于Linux的OS上运行,例如mac,centos或ubuntu等。
如果安装srt将做上面"三"的操作:
3:会出现如下错误:
cryspr-openssl.c:(.text+0x129):对‘AES_set_encrypt_key’未定义的引用
cryspr-openssl.c:(.text+0x141):对‘AES_set_decrypt_key’未定义的引用
collect2: 错误:ld 返回 1
make: *** [all] 错误 1;
解决方法:vim Makefile;将 LIBRARY_FILE = -lpthread -lz -lsrt 修改为 LIBRARY_FILE = -lpthread -lz -lsrt -lssl -lcrypto 即可;
4:会在./bin目录下生成sls和slc两个可执行文件;
5:run with default config file $ sudo ./sls -c ../sls.conf;(默认端口8080);
五:ffmpeg的push端命令;
./ffmpeg -f gdigrab -framerate 30 -i desktop -vcodec libx264 -preset ultrafast -tune zerolatency -flags2 local_header -acodec libmp3lame -g 30 -pkt_size 1316 -flush_packets 0 -f mpegts test.ts
./ffmpeg -f gdigrab -framerate 30 -i desktop -vcodec libx264 -preset ultrafast -tune zerolatency -flags2 local_header -acodec libmp3lame -g 30 -pkt_size 1316 -flush_packets 0 -f mpegts srt://10.10.0.226:9999?streamid=uplive.sls.com/uplive/test1
有可能出现如下错误:
[srt @ 072d3a80] Connection to srt://[192.168.239.133]:8080?streamid=uplive.sls.com/live/test failed: I/O error
srt://[192.168.239.133]:8080?streamid=uplive.sls.com/live/test: I/O error;
首先一定要关闭linux防火墙:# 关闭 sudo service firewalld stop ;
六:ffmpeg的play端命令;
./ffplay -fflags nobuffer -analyzeduration 2000 -i srt://10.10.0.226:9999?streamid=live.sls.com/live/test1
./ffplay -fflags nobuffer -analyzeduration 2000 -i srt://10.10.0.226:9999?streamid=live.sls.com/live/libsrt
./ffplay -fflags nobuffer -analyzeduration 2000 -i srt://47.95.14.230:9999?streamid=live.sls.com/live/libsrt
七:效果:
查看运行结果;
三:libsrt_vs2015编译步骤;
(一)python安装:
下载python(https://www.python.org/downloads/下载和curses对应的版本比如37对应3.7)以及Windows安装Python3 curses模块:参考:https://www.cnblogs.com/hardcoreYutian/p/11270871.html;或者
https://blog.csdn.net/afu42832/article/details/101767845?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase;
还有:https://blog.csdn.net/u012487272/article/details/80933179;
(二):下载并安装openssl:
参考:https://blog.csdn.net/zha6476003/article/details/80900988;并将其配置到环境变量中;
注意版本(openssl版本一定要和vs工程匹配,32还是64);
(三):下载并安装windows版本的pthread库:
参考https://blog.csdn.net/qianchenglenger/article/details/16907821?
utm_medium=distribute.pc_relevant.none-task-blog-baidujs-1;并将其配置到环境变量中;
(四):下载cmake-gui:
参考https://blog.csdn.net/qq_35488967/article/details/56480053;并按照其步骤利用cmake-gui将工程导入到vs2015中;
注:如果出现错误:
1:C2011 “timespec”:“struct”类型重定义:参考:
https://blog.csdn.net/u010536615/article/details/70231441;
2:libsrt编译的的时候会出现错误:使用OpenSSL 的AES加密报错undefined reference to `AES_set_encrypt_key';
错误 LNK2019 无法解析的外部符号 _AES_set_encrypt_key,该符号在函数 _crysprOpenSSL_AES_SetKey 中被引用 srt_shared E:\\libsrt_windows_build\\srt-master\\cryspr-openssl.obj 1;
解决方法参考:(openssl版本一定要和vs工程匹配,32还是64);
3:会提示“version.h”找不到,需要自己根据version.h.in编译出来一个或者下载一个放到srt.h同级目录下面;
CMakeLists.txt文件中可以看到版本号:#project(SRT VERSION "1.4.1");
4:其他问题暂定;
四:模拟丢包延迟等测试需求;
Clumsy是一款小巧而功能强大的开源弱网模拟工具,支持windows平台,可用于模拟:丢包(Drop)、延时(Lag)、重复(Duplicate)、乱序(Out of order)、篡改(Tamper)、抖动(Throttle)等。其项目地址:http://jagt.github.io/clumsy/cn/index.html;
如果要对发往指定某IP的UDP包进行丢包,可将Filtering条件设置为:
udp and (ip.DstAddr == 192.168.1.255);
clumsy 首先根据用户选择的 filter 来拦截指定的网络数据。在 filter 中可以设定你感兴趣的协议(tcp/udp),端口号,是接收还是发出的端口。你也可以通过简单的逻辑语句来进一步缩小范围。当 clumsy 被激活时,只有符合这些标准的网络数据会被进行处理,而你不感兴趣的数据仍然会由系统正常传输。
当被 filter 的网络数据包被拦截后,你可以选择 clumsy 提供的功能来有目的性的调整网络情况:
1:延迟(Lag),把数据包缓存一段时间后再发出,这样能够模拟网络延迟的状况。
2:掉包(Drop),随机丢弃一些数据。
3:节流(Throttle),把一小段时间内的数据拦截下来后再在之后的同一时间一同发出去。
4:重发(Duplicate),随机复制一些数据并与其本身一同发送。
5:乱序(Out of order),打乱数据包发送的顺序。
6:篡改(Tamper),随机修改小部分的包裹内容。
五:libsrt关于SRT_SOCKOPT参数的设置;
在这个枚举typedef enum SRT_SOCKOPT中记录了所有可以设置的参数;
1:SRTO_STREAMID:
有些和rtmp类似填写vhost,ip,port,streamid等等,例如// live.sls.com/live/libsrt;如果是推流到服务器端则服务器配置vhost等等要匹配上选择接收不接收,如果是peertopeer点对点的方式推流端一样需要,但接收端可以选择不处理;
2:SRTO_SNDSYN/SRTO_RCVSYN:
设置udp发送接收是否是阻塞的;0非阻塞,1阻塞;
3:SRTO_LATENCY/SRTO_TSBPDDELAY:
// 不建议。SET:SRTO_RCVLATENCY和SRTO_PEERLATENCY。GET:与SRTO_RCVLATENCY相同(暂时没用过);
4:SRTO_PEERLATENCY:
对侧(对等)的TsbPd接收器延迟的最小值毫秒(暂时没用过);
5:SRTO_RCVLATENCY:
(默认120毫秒);// Latency for Live transmission: default is 120
static const int SRT_LIVE_DEF_LATENCY_MS = 120;
表示接收端Jitter buff缓存毫秒数,对应SRT的SRTO_RCVLATENCY参数。为了抵抗网络传输、丢包重传等行为带来的抖动,SRT需要设置接收端缓存以保障输出的流畅性(可以在发送端和接收端中任意一方或双方设置,实际缓存时间取二者中的较大值),SRT的默认缓存时间为120ms,官方建议设置为RTT*4,最小值不低于120ms。因为流畅性和实时性(时延)是一对矛盾的指标,Jitter buff必然将引入一定延时。在后面的测试过程中,将会对BuffTime进行调整,查看调整前后的效果对比。
6:SRTO_SNDDROPDELAY:
(默认120毫秒);// Latency for Live transmission: default is 120
static const int SRT_LIVE_DEF_LATENCY_MS = 120;
发送方TLPKTDROP决策的延迟增加了额外的延迟(关闭为-1),这个就是和fec相关的额外延迟;
当FEC的ARQ选项配置为ALWAYS时,即只要发现丢包即刻发起NAK重传请求,则推荐的延时为max(RTT*4, LatencyFec)。当FEC的ARQ选项配置为ONREQ时,推荐的延时为RTT*4 + LatencyFec即二者之和,这是因为ONREQ模式下,需要等到确认FEC失败才发起NAK重传,确认失败的条件是当前FEC GROUP已经接收完成了仍旧无法恢复GROUP内丢失的包。(具体看下后面fec关于延迟的解释);
7: SRTO_TRANSTYPE:
//传输类型(给定传输类型所需的选项集;
相关三种模式结构体如下;
typedef enum SRT_TRANSTYPE
SRTT_LIVE,
SRTT_FILE,
SRTT_INVALID
SRT_TRANSTYPE;
支持Message消息模式、文件Buff模式和Live实时模式(默认),前两者可保证传输不丢包但不保证实时性,后者以实时性为目标允许丢包。传输音视频一般采用Live实时模式。
8:SRTO_MAXBW:
设置SRT最大码率MAXBW为编码码率VideoBitrate;
当设置为-1时,SRT将不限码率。
9:SRTO_PAYLOADSIZE:
有效负载长度:#define SRT_LIVE_DEFAULT_PAYLOAD_SIZE 188*7;
#define SRT_LIVE_MAX_PAYLOAD_SIZE 1456;
10:SRTO_PACKETFILTER:
Filter相关:看下面六:libsrt关于FEC相关的支持情况;
11:
六:libsrt关于FEC相关的支持情况;
首先看下相关的研究文章;
https://github.com/Haivision/srt/blob/master/docs/packet-filtering-and-fec.md;
https://blog.wmspanel.com/2020/04/srt-fec-forward-error-correction.html;
srt,关于fec的支持情况的说明;
https://blog.csdn.net/mediapro/article/details/105790057;
说明情况根据暂定根据测试结果说明;
(一)SRT中的NAK机制可与FEC相结合:
可以选择三种模式:仅对FEC恢复失败的情况下发起NAK重传请求、丢包均发起NAK、丢包不发起NAK。
另外SRT会在预估超时的情况下,提前结束无意义的重传动作,在延时和码率允许的情况下支持反复重传直至成功。
SRT采用滤镜的方式引入FEC并且默认情况下关闭了FEC功能,可通过SRTO_PACKETFILTER选项设置FEC描述字符串来启用。描述字符串格式为:
"fec,rows:%d,cols:%d,layout:%s,arq:%s"
例如传入的字符串是
"fec,cols:2,rows:2,layout:even,arq:onreq";
play端必须是没有arq:onreq字段的否则报错;
例如传入的字符串是
"fec,cols:2,rows:2,layout:even";
参照:srt-master\\srtcore\\fec.cpp中的::FECFilterBuiltin::FECFilterBuiltin配置参数;
1:cols和rows:
其中cols用于描述2D 异或FEC的列数,rows描述行数(填写数字符合上面规则);
至少必须指定'cols'并且> 1;
“rows”必须> = 1或负数<-1;
//支持的配置:
//-仅行(number_rows == 1)
//-仅列,无行FEC / CTL(number_rows <-1)
//-列和行(均> 1)
//不允许的配置:
//-number_cols <1
//-number_rows [-1,0]
2:layout:
描述FEC的布局(“even”、“staircase”,只有这两个值均匀或楼梯);
3:arq:
描述FEC与NAK的结合方式("never", "onreq", "always",只有这三个值解释在下面)。
//额外的解释级别(如果找到),默认为永不("never")。仅检查那些被管理的。
enum SRT_ARQLevel
SRT_ARQ_NEVER, //< Never send LOSSREPORT//丢包不发起NAK;
SRT_ARQ_ONREQ, //< Only record the loss, but report only those that are returned in receive()//仅对FEC恢复失败的情况下发起NAK重传请求;
SRT_ARQ_ALWAYS, //< always send LOSSREPORT immediately after detecting a loss//丢包均发起NAK
;
(二):如果是服务器传输支持FEC需要修改服务器的代码:
FEC和数据包筛选器文档当前缺少有关双方如何协商数据包筛选器配置的信息。
如果A侧未指定任何数据包过滤器,而B侧指定了,则A侧仅接受B侧的配置。
如果双方指定不同的过滤器类型,则拒绝连接。
对于FEC,至少必须指定列数。这将启用单行FEC。
如果过滤器类型匹配,但是配置不匹配(例如,列和行),则侦听器将仅应用未指定自身的值,调用者将采用侦听器的配置。因此服务器和客户端建议行数,列数配置相同,其他参数发送端或接收端填写,服务端不填写。
1: srt-live-server-master/slscore/SLSSrt.hpp中114行添加:
int libsrt_read2(char *buf, int size);
2: srt-live-server-master/slscore/SLSSrt.hpp中139行添加:
private:
long long time_long_ms_last = 0;
3:srt-live-server-master/slscore/SLSSrt.cpp中185行添加:
//统计一次信息用于修改码率或者接受策略;
char SRTO_PACKETFILTER_value[256] = 0 ;
//sprintf((char *)SRTO_PACKETFILTER_value, "fec,cols:%d,rows:%d,layout:%s,arq:%s",
//FEC_cols, FEC_rows, FEC_layout, FEC_arq);
sprintf((char *)SRTO_PACKETFILTER_value, "fec,cols:%d,rows:%d",
20, 20);
//sprintf((char *)SRTO_PACKETFILTER_value, "fec,cols:%d,rows:%d,layout:%s,arq:%s",
// 20, 20, "staircase", "always");
//sprintf((char *)SRTO_PACKETFILTER_value, "fec");
// peertopeer点对点publish端配置参数就可以,playpull端不需要配置参数;
ret = srt_setsockopt(fd, 0, SRTO_PACKETFILTER, &SRTO_PACKETFILTER_value, sizeof(SRTO_PACKETFILTER_value));
if (ret == SRT_ERROR)
printf("SRTO_PACKETFILTER: %s\\n", srt_getlasterror_str());
return 0;
4:srt-live-server-master/slscore/SLSSrt.cpp中371行添加:
int CSLSSrt::libsrt_read2(char *buf, int size)
int ret;
ret = srt_recvmsg(m_sc.fd, buf, size);
if (ret < 0)
int err_no = libsrt_neterrno();
sls_log(SLS_LOG_WARNING, "[%p]CSLSSrt::libsrt_read failed, sock=%d, ret=%d, err_no=%d.",
this, m_sc.fd, ret, err_no);
//
//zwg添加的代码;
SRT_TRACEBSTATS temp_perf_play = 0 ; //每一次统计信息结构体;
int retbstats = 0;
//获取当前时间;
struct timeval m_tv_now;
gettimeofday(&m_tv_now, NULL);
long long time_long_ms_now = m_tv_now.tv_sec * 1000 + m_tv_now.tv_usec / 1000;
//第一次获取;
if (time_long_ms_last == 0)
time_long_ms_last = time_long_ms_now;
//统计一次信息用于修改码率或者接受策略;
retbstats = srt_bstats(m_sc.fd, &temp_perf_play, true);
printf("retbstats :%d\\n", retbstats);
else
if ((time_long_ms_now - time_long_ms_last) > 300)
//统计一次信息用于修改码率或者接受策略;
retbstats = srt_bstats(m_sc.fd, &temp_perf_play, true);
printf("retbstats :%d\\n", retbstats);
time_long_ms_last = time_long_ms_now;
else
retbstats = -1;
if (retbstats != 0 || ret == SRT_ERROR)
//printf("vitsrt_push_pull_recvdata srt_bstats: %s\\n", srt_getlasterror_str());
else
//打印每次统计的信息;
printf("\\n");
printf("rtt:%d ", int(temp_perf_play.msRTT));
printf("mbpsRecvRate:%0.2f ", temp_perf_play.mbpsRecvRate);
printf("inflight:%d ", temp_perf_play.pktFlightSize);
int m_pktRecv = temp_perf_play.pktRecv;
int m_pktRecvLoss = temp_perf_play.pktRcvLoss;
int m_lossRecvPercentage = (100 * m_pktRecvLoss / m_pktRecv);
if (m_lossRecvPercentage > 100)
m_lossRecvPercentage = 100;
printf("pktRecv:%d ", temp_perf_play.pktRecv);
printf("pktRcvLoss:%d ", temp_perf_play.pktRcvLoss);
printf("losspercent:percent : %d ", m_lossRecvPercentage);
printf("m_pktRcvFilterExtra:%d ", temp_perf_play.pktRcvFilterExtra);
printf("m_pktRcvFilterSupply:%d ", temp_perf_play.pktRcvFilterSupply);
printf("m_pktRcvFilterLoss:%d ", temp_perf_play.pktRcvFilterLoss);
printf("\\n");
//这里的作用是将接收端收到的信息比如丢包率和fec的补包数量返回给发送端以作用于调整码率;
//否则发送端不清楚接收端的情况,即不清楚接收端下行带宽是否可以满足发送要求;
char bstats_send_info[256] = 0 ;
bstats_send_info[0] = 0x01; //做一定的标志key;
bstats_send_info[1] = 0x02;
bstats_send_info[2] = 0x03;
bstats_send_info[3] = 0x04;
bstats_send_info[4] = 0x05;
bstats_send_info[5] = 0x06;
//接收端丢包百分比;char cNumber= (char) (number+'0');
bstats_send_info[6] = (char)(m_lossRecvPercentage + '0');
//接收端m_pktRcvFilterSupply;fec的补包数量;
if (temp_perf_play.pktRcvFilterSupply > 256)
temp_perf_play.pktRcvFilterSupply = 256;
bstats_send_info[7] = (char)(temp_perf_play.pktRcvFilterSupply + '0');
int bstats_send_info_size = 8;
//发送出去;
int ret_send = srt_sendmsg(m_sc.fd, bstats_send_info, bstats_send_info_size, -1, 0);
if (ret < 0)
//SRTS_BROKEN
int err_no = libsrt_neterrno();
printf("srt_bstats srt_sendmsg error!!!!\\n");
sls_log(SLS_LOG_WARNING, "[%p]CSLSSrt::libsrt_write failed, sock=%d, ret=%d, errno=%d.",
this, m_sc.fd, ret, err_no);
else
printf("srt_bstats srt_sendmsg success!!!!\\n");
//
return ret;
5:srt-live-server-master/slscore/ SLSRole.cpp中455行添加:
int n = 0;
if (m_is_write == 1) //player;
n = m_srt->libsrt_read(szData, TS_UDP_LEN);
else
n = m_srt->libsrt_read2(szData, TS_UDP_LEN);
这个相当重要,因为play端也会调用read函数,如果将统计信息发送给play端则会出现发送的数据和接收的数据不一样的情况。
6:修改完服务器代码之后,在服务器的接收端和play的接收端如果有补包pktRcvFilterSupply;这个值就有变化了;
可以用clumsy.exe中的allseding/allrecving来测试;
(三):如果需要根据接收端的信息控制发送端的码率应该如下:
1:int m_pktRcvFilterSupply; //过滤器额外提供的数据包数量(例如,重建的FEC);// total number of data bytes, sent by the application;
2: int64_t m_pktRecv; //一次接收包数量;
int64_t m_pktRecvLoss; //一次接收包丢失的数量;
int64_t m_lossRecvPercentage; //一次接收丢包百分比;
3:其他统计的信息值;
4:以及其他统计的信息值以及其他统计信息值做参考可决定发送端码率的情况,比如接收端的pktRcvFilterSupply补包数量最近6个补包都大于5个。以及接收端丢包率最近的6个丢包率都大于30%,以及发送端的丢包率最近的6个丢包率都大于30%,再这三个值或者其他值参考以上的时候就需要要降低码率,以控制防止fec的补包能力不足造成不可逆的丢包情况。这个还需要研究有 那些参数可以参考。
5:下面的bbr或者gcc算法只能算出来比如网络测速的时候的上行或者下行带宽是多大,对于fec或者ack的恢复能力是不清楚地,还需要我们自己根据得到的统计信息做码率控制。
在不考虑设置发送延迟和接收延迟的情况下,在不考虑丢包的情况下,bbr算法应该可以将带宽顶满。
七:libsrt关于google的bbr和gcc拥塞控制算法统计的重要参数和其他需要统计的比如FEC相关的重要统计参数;
在这个结构体中记录了相关统计信息:struct CBytePerfMon:typedef struct CBytePerfMon SRT_TRACEBSTATS;
统计的目的是为了推流端动态调整码率,分辨率等等以及动态根据丢包调整fec的冗余包情况,或者调整延迟和流畅的平衡;
统计的目的是为了播放端可以根据不同的统计信息控制接受的码率等等相关的调整;
1:发送及丢包统计(接收也有相同的与之对应);
int64_t m_pktSent; //一次发送包数量;
int64_t m_pktSndLoss; //一次发送包丢失的数量;
int64_t m_pktSndLossTotal; //发送的所有包丢失的总数;
int64_t m_lossSendPercentage; //一次发送丢包百分比;
2:已经发送且其还未收到ACK的报文的数量:
int64_t m_inflight(pktFlightSize); //已经发送且其还未收到ACK的报文的数量;
3://发送带宽,bps/s接收也有相同的与之对应);
int64_t m_bpsSendRate; //发送带宽,bps/s;
4:rtt统计(毫秒);
int m_rtt; //rtt统计(毫秒),RTT(往返延时)表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延;
5: //filter用(目前只有fec);
int m_pktSndFilterExtra; //数据包过滤器提供的控制数据包数;// total number of data packets sent by the application;
int m_pktRcvFilterExtra; //收到但未提供的控制包数量;// total number of packets to be received by the application;
int m_pktRcvFilterSupply; //过滤器额外提供的数据包数量(例如,重建的FEC);// total number of data bytes, sent by the application;
int m_pktRcvFilterLoss; //过滤器无法覆盖的丢包数;// total number of data bytes to be received by the application;
6:其他统计参数;
Bbr和gcc算法暂时自行百度查找相关原理;
如有错误请指正:
交流请加QQ群:62054820
QQ:379969650.
以上是关于libsrt_操作及相关记录的主要内容,如果未能解决你的问题,请参考以下文章