Flv.js直播分析及延迟解决方案

突然发现这篇文章躺在草稿箱里几年时间,在直播最火的时候帮了大忙…
希望还能够提供帮助…

前言

flv/rtmp是当下最流行的视频传输协议,这也和flash的没落有很大的关系,众浏览器已经默认禁止flash,而在使用flv.js过程中根据项目实际需求的不同,对直播视频的延迟要求均不同,正常flv.js的延迟在5s左右。那么如何降低呢?

常见的直播协议

  1. RTMP:底层基于TCP,在浏览器端依赖Flash。

  2. HTTP-FLV:基于HTTP流式IO传输FLV,依赖浏览器支持播放FLV。

  3. HLS: Http Live Streaming,苹果提出基于HTTP的流媒体传输协议。HTML5可以直接打开播放。

  4. WebSocket-FLV:基于WebSocket传输FLV,依赖浏览器支持播放FLV。WebSocket建立在HTTP之上,建立WebSocket连接前还要先建立HTTP连接。

而就官方给出的延迟排序,HLS排名最后,其他延迟均相等。

Flv.js 限制

  1. FLV里所包含的视频编码必须是H.264,音频编码必须是AAC或MP3, IE11和Edge浏览器不支持MP3音频编码,所以FLV里采用的编码最好是H.264+AAC,这个让音视频服务兼容不是问题。
  2. 对于录播,依赖 原生HTML5 Video标签 和 Media Source Extensions API
  3. 由于依赖Media Source Extensions,目前所有iOS和Android4.4.4以下里的浏览器都不支持,也就是说目前对于移动端flv.js基本是不能用的。(媒体源扩展(MSE)实现后,情况就不一样了。MSE 使我们可以把通常的单个媒体文件的 src值替换成引用 MediaSource 对象(一个包含即将播放的媒体文件的准备状态等信息的容器),以及引用多个 SourceBuffer 对象(代表多个组成整个串流的不同媒体块)的元素。MSE 让我们能够根据内容获取的大小和频率,或是内存占用详情(例如什么时候缓存被回收),进行更加精准地控制。 它是基于它可扩展的 API 建立自适应比特率流客户端(例如DASH 或 HLS 的客户端)的基础)

Flv.js 延迟优化

在进行延迟优化时我们需要了解Flv.js的工作流程:(内容来自网络)

  1. 主播端在采集到一段时间的音视频原数据后,因为音视频原数据庞大需要先压缩数据:

    • 通过H264视频编码压缩数据数据

    • 通过PCM音频编码压缩音频AAC数据

  2. 压缩完后再通过FLV容器格式封装压缩后的数据,封装成一个FLV TAG

  3. 再把FLV TAG通过RTMP协议推流到音视频服务器,音视频服务器再从RTMP协议里解析出FLV TAG。

  4. 音视频服务器再通过HTTP协议通过和浏览器建立的长链接流式把FLV TAG传给浏览器。

  5. flv.js 获取FLV TAG后解析出压缩后的音视频数据喂给Video播放。nginx-http-flv-moudle

流程后我们就知道从哪入手优化了,以下解决方案收集自网络 划重点

  • 主播端采集时收集了一段时间的音视频原数据,它专业的叫法是GOP。缩短这个收集时间(也就是减少GOP长度)可以优化延迟,但这样做的坏处是导致视频压缩率不高,传输效率低。

  • 关闭音视频服务器的I桢缓存可以优化延迟,坏处是用户看到直播首屏的时间变大。

  • 减少音视频服务器的buffer可以优化延迟,坏处是音视频服务器处理效率降低。

  • 减少浏览器端flv.js的buffer可以优化延迟,坏处是浏览器端处理效率降低。

  • 浏览器端开启flv.js的Worker,多进程运行flv.js提升解析速度可以优化延迟,这样做的flv.js配置代码是:

{
          enableWorker: true,
          enableStashBuffer: false,
          stashInitialSize: 128,
}

但是问题来了,根据网上各种解决延迟的方案我们会发现,延迟仍在5s左右!并伴随着卡顿,丢帧。

通过查询MDN,

媒体元素支持在媒体的内容中从当前播放位置移到某个特定点。 这是通过设置元素的属性currentTime的值来达成的

也就是说我们可以通过更改播放器的currentTime来修改当前播放内容,那么具体更改到哪里呢。我可以从缓存中读取,因为播放器在处理直播流时,他会预先缓存你的视频内容,比如,当前视频播放之12:00,但因为延迟,播放器只播放到11:58,但其实当前直播流已经将12:00的视频进行缓存,我们直接从中读取即可。

// 当前播放器 dom 节点
flvPlayer.buffered.end(0) 

buffered 属性返回 TimeRanges 对象。

TimeRanges 对象属性:

length – 获得音视频中已缓冲范围的数量
start(index) – 获得某个已缓冲范围的开始位置
end(index) – 获得某个已缓冲范围的结束位置
注释:首个缓冲范围的下表是 0。

这样我们就拿到了当前已缓存的最新帧。也就可以计算出当前直播流的延迟时间

let delayTime = flvPlayer.buffered.end(0) - flvPlayer.currentTime

只需判断如果延迟 > 2s ,强行将直播流拽回当前缓存的最新帧即可

if ((flvPlayer.buffered.end(0) - flvPlayer.currentTime) > 2) {
      videoElement.currentTime = videoElement.buffered.end(0.1)
}

细心的同学发现了,buffered.end(index),index给的值是0.1,index的范围时[0, 1],为什么不是0?因为如果将index的值设置为0,当前直播流会变成0缓存解码加载,在网络较差的环境下会导致卡顿,丢帧等问题。

解决方案来自flv.js作者xqq

123

所以可以优化为 全部代码入下:

setInterval(function () {
          if (videoElement.buffered.length > 0) {
            var delayTime = flvPlayer.buffered.end(0) - flvPlayer.currentTime
            if ((flvPlayer.buffered.end(0) - flvPlayer.currentTime) > 2) {
              videoElement.currentTime = videoElement.buffered.end(0.1)
            }
          }
}, 1000)

每秒去计算延迟,然后解决延迟。可以有效的将代码控制在2s以内。

其实还有一些小问题!

也就是buffered可能不存在!我们只需在处理延迟之前先去判断当前是否有缓存!

buffered.length > 0

其他

其实前端能对延迟做的优化很少,并不是每个人都能去写出flv.js或者去修改它的源码!那么直播流的质量就额外的重要!

以下几点均可能导致视频延迟:

  • 视频帧率及清晰度
  • 推流工具
  • 视/音频质量 根据情况进行压缩
  • 推流服务器
  • 网络
  • 摄像头参数

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY8RBCRq' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片