JAVA音视频解决方案--- SRS Edge 2.0version 延迟调优
序言
在搭建了SRS的边缘服务器后,可能会有延迟,至少我遇到了.所以整理下2.0version的延迟解决方法.
主要针对的是流媒体服务器的延迟调优,播放器的调优需要根据播放器的类型自行选择.
SRS也是根据你的情况需要选择不同的配置组合.来满足你的直播需要
参数配置说明(根据自己的情况选择配置).
GOP-Cache(鱼与熊掌不可兼得,鱼:立即能看 熊掌:低延迟)
什么是GOP?就是视频流中两个I帧的时间距离,如果问什么是I帧就去百度。
GOP有什么影响?Flash(解码器)只有拿到GOP才能开始解码播放。也就是说,服务器一般先给一个I帧给Flash。可惜问题来了,假设GOP是10秒,也就是每隔10秒才有关键帧,如果用户在第5秒时开始播放,会怎么样?
第一种方案:等待下一个I帧,也就是说,再等5秒才开始给客户端数据。这样延迟就很低了,总是实时的流。问题是:等待的这5秒,会黑屏,现象就是播放器卡在那里,什么也没有,有些用户可能以为死掉了,就会刷新页面。总之,某些客户会认为等待关键帧是个不可饶恕的错误,延时有什么关系?我就希望能快速启动和播放视频,最好打开就能放!
第二种方案:马上开始放,放什么呢?你肯定知道了,放前一个I帧。也就是说,服务器需要总是cache一个gop,这样客户端上来就从前一个I帧开始播放,就可以快速启动了。问题是:延迟自然就大了。
有没有好的方案?有!至少有两种:
- 编码器调低GOP,譬如0.5秒一个GOP,这样延迟也很低,也不用等待。坏处是编码器压缩率会降低,图像质量没有那么好。
- 服务器提供配置,可以选择前面两个方案之一:SRS就这么做,有个gop_cache配置项,on就会马上播放,off就低延迟。
# the listen ports, split by space.
listen 1935;
vhost __defaultVhost__ {
# [email protected]
# whether cache the last gop.
# if on, cache the last gop and dispatch to client,
# to enable fast startup for client, client play immediately.
# if off, send the latest media data to client,
# client need to wait for the next Iframe to decode and show the video.
# set to off if requires min delay;
# set to on if requires client fast startup.
# default: on
gop_cache on;
}
累积延迟(服务器会缓存,但是不能太长,客户端也会缓存,客户端只能重新连接服务器)
除了GOP-Cache,还有一个有关系,就是累积延迟。SRS可以配置直播队列的长度,服务器会将数据放在直播队列中,如果超过这个长度就清空到最后一个I帧:
# [email protected]
# the max live queue length in seconds.
# if the messages in the queue exceed the max length,
# drop the old whole gop.
# default: 30
queue_length 10;
当然这个不能配置太小,譬如GOP是1秒,queue_length是1秒,这样会导致有1秒数据就清空,会导致跳跃。
有更好的方法?有的。延迟基本上就等于客户端的缓冲区长度,因为延迟大多由于网络带宽低,服务器缓存后一起发给客户端,现象就是客户端的缓冲区变大了,譬如NetStream.BufferLength=5秒,那么说明缓冲区中至少有5秒数据。(服务器会一次性发给浏览器么)
处理累积延迟的最好方法,是客户端检测到缓冲区有很多数据了,如果可以的话,就重连服务器。当然如果网络一直不好,那就没有办法了。
Min-Latency
当开启最低延迟配置后,SRS会禁用mr(merged-read),并且在consumer队列中使用超时等待,大约每收到1-2个视频包就发送给客户端,达到最低延迟目标。
测试vp6纯视频流能达到0.1秒延迟,参考#257。配置文件:
vhost mrw.srs.com {
# whether enable min delay mode for vhost.
# for min latence mode:
# 1. disable the mr for vhost.
# 2. use timeout for cond wait for consumer queue.
# @see https://github.com/ossrs/srs/issues/257
# default: on
min_latency off;
}
Merged-Read
RTMP的Read效率非常低,需要先读一个字节,判断是哪个chunk,然后读取header,接着读取payload。因此上行支持的流的路数大约只有下行的1/3,譬如SRS1.0支持下行2700上行只有1000,SRS2.0支持下行10000上行只有4500。
为了提高性能,SRS对于上行的read使用merged-read,即SRS在读写时一次读取N毫秒的数据,这个可以配置:
# the MR(merged-read) setting for publisher.
vhost mrw.srs.com {
# about MR, read https://github.com/ossrs/srs/issues/241
mr {
# [email protected]
# whether enable the MR(merged-read)
# default: off
enabled on;
# the latency in ms for MR(merged-read),
# the performance+ when latency+, and memory+,
# memory(buffer) = latency * kbps / 8
# for example, latency=500ms, kbps=3000kbps, each publish connection will consume
# memory = 500 * 3000 / 8 = 187500B = 183KB
# when there are 2500 publisher, the total memory of SRS atleast:
# 183KB * 2500 = 446MB
# the value recomment is [300, 2000]
# default: 350
latency 350;
}
}
也就是说,当开启merged-read之后,服务器的接收缓冲区至少会有latency毫秒的数据,延迟也就会有这么多毫秒。
若需要低延迟配置,关闭merged-read,服务器每次收到1个包就会解析。
Merged-Write
SRS永远使用Merged-Write,即一次发送N毫秒的包给客户端。这个算法可以将RTMP下行的效率提升5倍左右,SRS1.0每次writev一个packet支持2700客户端,SRS2.0一次writev多个packet支持10000客户端。
用户可以配置merged-write一次写入的包的数目,建议不做修改:
# the MW(merged-write) settings for player.
vhost mrw.srs.com {
# set the MW(merged-write) latency in ms.
# SRS always set mw on, so we just set the latency value.
# the latency of stream >= mw_latency + mr_latency
# the value recomment is [300, 1800]
# default: 350
mw_latency 350;
}
若需要极低延迟(损失较多性能),可以设置为100毫秒,SRS大约一次发送几个包。
结果(推荐极端低延迟的最终配置)
当然,服务器的性能也要考虑,不可以让一个SRS进程跑太高带宽,一般CPU在80%以下不会影响延迟,连接数参考性能。
# the listen ports, split by space.
listen 1935;
vhost __defaultVhost__ {
gop_cache off;
queue_length 10;
min_latency on;
mr {
enabled off;
}
mw_latency 100;
tcp_nodelay on;
}