Web 端支持 h265 硬解 web播放H.265流媒体 网页播放H.265

这篇具有很好参考价值的文章主要介绍了Web 端支持 h265 硬解 web播放H.265流媒体 网页播放H.265。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、背景
Web 端实时预览 H.265 需求一直存在,但由于之前 Chrome 本身不支持 H.265 硬解,软解性能消耗大,仅能支持一路播放,该需求被搁置。

去年 9 月份,Chrome 发布 M106 版本,默认开启 H.265 硬解,使得实时预览支持 H.265 硬解具备可行性。

然而 WebRTC 本身支持的视频编码格式仅包括 VP8、VP9、H.264、AV1,并不包含 H.265。根据 w3c 发布的 2023 WebRTC Next Version Use Cases 来看,近期也没有打算支持 H.265 的迹象,因而决定自研实现 WebRTC 对 H.265 的支持。

2、DataChannel
背景说到 chrome 支持了 h265 的硬解,但 WebRTC 并不支持直接传输 h265 视频流。但可以通过 datachannel 来绕过这个限制。

WebRTC 的数据通道 DataChannel 是专门用来传输除音视频数据之外的任何数据的(但并不意味着不可以传输音视频数据,本质上它就是一条 socket 通道),如短消息、实时文字聊天、文件传输、远程桌面、游戏控制、P2P加速等。

1)SCTP协议

DataChannel 使用的协议是 SCTP(Stream Control Transport Protocol) (是一种与TCP、UDP同级的传输协议),可以直接在 IP 协议之上运行。

但在 WebRTC 的情况下,SCTP 通过安全的 DTLS 隧道进行隧道传输,该隧道本身在 UDP 之上运行,同时支持流控、拥塞控制、按消息传输、传输模式可配置等特性。需注意单次发送消息大小不能超过 maxMessageSize(只读, 默认65535字节)。

2)可配置传输模式

DataChannel 可以配置在不同模式中,一种是使用重传机制的可靠传输模式(默认模式),可以确保数据成功传输到对等端;另一种是不可靠传输模式,该模式下可以通过设置 maxRetransmits 指定最大传输次数,或通过 maxPacketLife 设置传输间隔时间实现;

这两种配置项是互斥的,不可同时设置,当同为null 时使用可靠传输模式,有一个值不为 null 时开启不可靠传输模式。

3)支持数据类型

数据通道支持 string 类型或 ArrayBuffer 类型,即二进制流或字符串数据。

后续两种方案,都是基于 datachannel 来做。

3、方案一 WebCodecs
官方文档: github.com/w3c/webcode…

思路: DataChannel 传输 H.265 裸流 + Webcodecs 解码 + Canvas 渲染。即 WebRTC 的音视频传输通道(PeerConnection) 不支持 H.265 编码格式,但可采用其数据通道(DataChannel)来传输 H.265数据,前端收到后使用 Wecodecs 解码、Canvas 渲染。
web播放h265 wes,前端,h.265,html5,网页播放器,webrtc,H5播放器

优点:

直接传输 H.265 裸码流,无需额外封装,实现简单方便;无冗余数据,传输效率高

Wecodecs 解码延迟低,实时性很高

缺点:

音频需额外单独传输、解码和播放,需处理音视频同步问题

既有 sdk 基于 video 封装,webcodes 方案依赖 canvas,既有 video 相关操作,需要全部重写,比如截图,录像等操作

由于线上各项目等历史原因,既有 sdk 改动大,时间上不允许

4、方案二 MSE

官方例子: github.com/bitmovin/ms…

思路:Fmp4封装 + DataChannel 传输 + MSE 解码播放。即先将 H.265 视频数据封装成 Fmp4 格式,再通过 WebRTC DataChannel 通道进行传输,前端收到后采用 MSE 解码, video 进行播放。


web播放h265 wes,前端,h.265,html5,网页播放器,webrtc,H5播放器

优点:

复用 video 标签播放,无需单独实现渲染

音视频已封装到 Fmp4 中,web 端无需考虑音视频同步问题

整体工作量相比 Wecodecs 小,可快速上线

缺点:

设备端实现 Fmp4 封装可能存在性能问题,因此需要云端转发实时进行解封装,或者前端解封装

MSE 解码实时性不好(云端首次切片会有 1~2 秒延迟)

5、方案抉择
第一版本先以 MSE 上线。云端,前端开发量相对少,roi 高。

计划第二版上 wecodecs,不仅低延迟,而且可以避免云端耗流量的问题,节省成本。假设在第二版期间,WebRTC 官方支持了 H.265,那么直接兼容官方方案即可。

5.1 细说 Mse 及第一版 sdk 改造
Media Source Extensions, 媒体源扩展。官方文档: developer.mozilla.org/zh-CN/docs/…

引入 MSE 之后,支持 HTML5 的 Web 浏览器就变成了能够解析流协议的播放器了。

从另一个角度来说,通过引入 MSE,HTML5 标签不仅可以直接播放其默认支持的 mp4、m3u8、webm、ogg 等格式,还可以支持能够被 (具备MSE功能的)JS 处理的视频流格式。如此一来,我们就可以通过 (具备MSE功能的)JS,把一些原本不支持的视频流格式,转化为其支持的格式(如 H.264 的 mp4,H.265 的 fmp4)。

比如 B站开源的 flv.js 就是一个典型应用场景。B站的 HTML5 播放器,通过使用 MSE 技术,将 FLV源用 JS(flv.js) 实时转码成 HTML5 支持的视频流编码格式,提供给 HTML5 播放器播放。


 

// 此 demo 来自下面链接的官方示例, 可以直接跑起来,比较直观
// https://github.com/bitmovin/mse-demo/blob/main/index.html
​
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>MSE Demo</title>
</head>
<body>
  <h1>MSE Demo</h1>
  <div>
    <video controls width="80%"></video>
  </div>
​
  <script type="text/javascript">
    (function() {
      var baseUrl = 'https://bitdash-a.akamaihd.net/content/MI201109210084_1/video/720_2400000/dash/';
      var initUrl = baseUrl + 'init.mp4';
      var templateUrl = baseUrl + 'segment_$Number$.m4s';
      var sourceBuffer;
      var index = 0;
      var numberOfChunks = 52;
      var video = document.querySelector('video');
​
      if (!window.MediaSource) {
        console.error('No Media Source API available');
        return;
      }
        
      // 初始化 mse
      var ms = new MediaSource();
      video.src = window.URL.createObjectURL(ms);
      ms.addEventListener('sourceopen', onMediaSourceOpen);
​
      function onMediaSourceOpen() {
        // codecs,初始化 sourceBuffer
        sourceBuffer = ms.addSourceBuffer('video/mp4; codecs="avc1.4d401f"');
        sourceBuffer.addEventListener('updateend', nextSegment);
​
        GET(initUrl, appendToBuffer);
        
        // 播放
        video.play();
      }
​
      function nextSegment() {
        var url = templateUrl.replace('$Number$', index);
        GET(url, appendToBuffer);
        index++;
        if (index > numberOfChunks) {
          sourceBuffer.removeEventListener('updateend', nextSegment);
        }
      }
​
      function appendToBuffer(videoChunk) {
        if (videoChunk) {
          // 二进制流转换为 Uint8Array,sourceBuffer 进行消费
          sourceBuffer.appendBuffer(new Uint8Array(videoChunk));
        }
      }
​
      function GET(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.responseType = 'arraybuffer';
​
        xhr.onload = function(e) {
          if (xhr.status != 200) {
            console.warn('Unexpected status code ' + xhr.status + ' for ' + url);
            return false;
          }
          // 获取 mp4 二进制流
          callback(xhr.response);
        };
​
        xhr.send();
      }
    })();
  </script>
</body>
</html>

 文章来源地址https://www.toymoban.com/news/detail-786090.html

通过上面的 demo,以及测试(将 dmeo 中的 fmp4 片段换成我们自己的 IPC 设备(摄像头),H.265 类型的)得知,chrome 可以硬解 H.265 类型的 fmp4 片段。So,事情变得明朗了起来。大方向有了,无非就是 H.265 裸流,转换成 fmp4 片段,chrome 底层硬解。

5.2 fmp4 前端实时解封装
H.265 裸流解封装 fmp4,调研下来,如果纯 js 进行封装,工作量挺大。尝试用 wasm 调 c++ 的库,发现即使解封装性能也不大好。所以放在前端被 pass 掉了。

5.3 fmp4 云端实时解封装
性能好,对前端 0 侵入。确定了云端解封装,接下来讲讲这段时间开发遇到的核心链路演变,及最终的流程方案。

6、阶段一
云端实时解封装 Fmp4,写死 codecs(音视频编码类型) -> 前端 MSE 解码播放 -> 播放几秒后,失败,MSE 会抛异常,大概意思就是你的数据不对了,前后衔接不上。

排查下来,是 MSE 处于 updating 的时候,不能进行消费,数据直接被丢掉,导致后续数据衔接不上。那既然不能丢,我们就缓存下来。具体可以看下面的代码注释。
web播放h265 wes,前端,h.265,html5,网页播放器,webrtc,H5播放器

具体可以看代码注释:

const updating = this.sourceBuffer?.updating === true;
const bufferQueueEmpty = this.bufferQueue.length === 0;
​
  if (!updating) {
    if (bufferQueueEmpty) {
      // 缓存队列为空: 仅消费本次 buffer
      this.appendBuffer(curBuffer);
    } else {
      // 缓存队列不为空: 消费队列 + 本次 buffer
      this.bufferQueue.push(curBuffer);
​
      // 队列中多个 buffer 的合并
      const totalBufferByteLen = this.bufferQueue.reduce(
        (pre, cur) => pre + cur.byteLength,
        0
      );
      const combinedBuffer = new Uint8Array(totalBufferByteLen);
      let offset = 0;
      this.bufferQueue.forEach((array, index) => {
        offset += index > 0 ? this.bufferQueue[index - 1].length : 0;
        combinedBuffer.set(array, offset);
      });
​
      this.appendBuffer(combinedBuffer);
      this.bufferQueue = [];
    }
  } else {
    // mse 还在消费上一次 buffer(处于 updating 中), 缓存本次 buffer, 否则会有丢帧问题
    this.bufferQueue.push(curBuffer);
  }

考虑到 Fmp4 数据每一帧都不可丢失,因此 datachannel 走的是可靠传输。

但是测试下来,发现了新的问题。随着时间的增长,延迟会累积增大。因为丢包后,网络层会进行重试,重试的时间会累积进延时。我们测试下来,网络情况不好的时候,延迟会高达 30 秒及以上,理论上会一直增加,如果你拉流时间足够久的话。

7、阶段二
ok,换个思路,既然不丢帧 + 可靠传输带来的延时问题完全不能接受,那么如果换用不可靠传输呢?

不可靠传输,意味着会丢帧。调研下来,Fmp4 可以丢掉一整个切片(一个切片包含多帧),既然如此,我们可以设计一套丢帧算法,只要判断到一个切片是不完整的,我们就把整个切片丢掉。

这样的话,理论上来讲,最多只会有一个切片的延迟,大概在2秒左右,业务层可以接受。
web播放h265 wes,前端,h.265,html5,网页播放器,webrtc,H5播放器

丢帧算法设计思路:在每一帧数据头部增加 4 个字节的数据,用来标识每一帧的具体信息。

segNum: 2个字节,大端模式,Fmp4片段序列号,从1开始,每次加1

fragCount: 1个字节,Fmp4片段分片总数,最小为1

fragSeq: 1个字节,Fmp4片段分片序列号,从1开始

前端拿到每帧数据后,对前 4 个字节进行解析,就能获取到每帧数据的详细信息。举个例子,假如我要判断当前帧是否为最后一帧,只需要判断 fragCount 是否等于 fragSeq 即可。

算法大致流程图:
web播放h265 wes,前端,h.265,html5,网页播放器,webrtc,H5播放器

具体解释一下:

  • frameQueue, 用来缓存每一帧的数据,用来跟后面一帧数据进行对比,判断是否为完整帧

  • bufferQueue, 此队列中的数据,都是完整的切片数据,保证 MSE 进行消费时,数据没有缺失

  1.   /**
       * fmp4 切片队列 frameQueue,处理丢帧,生产 bufferQueue 内容
       *
       * @param frameObj 每一帧的相关数据
       *      每来一帧进行判断
       *      buffer中加上当前帧是否为连续帧(从第一帧开始的连续帧)
       *        是
       *          当前帧是否为最后一帧
       *            是 拼接buffer帧以及当前帧,组成完整帧,放入另外一个待消费 buffer
       *            否 当前帧入 buffer
       *        否 清空 buffer,当前帧入 buffer
       */
    ​
    const frameQueueLen = this.frameQueue.length;
    const frameQueueEmpty = frameQueueLen === 0;
    ​
      // 单一完整分片帧单独处理,直接进行消费
      if (frameObj.fragCount === 1) {
        if (!frameQueueEmpty) {
          this.frameQueue = [];
        }
        this.bufferQueue.push(frameObj.value);
        return;
      }
    ​
      if (frameQueueEmpty) {
        this.frameQueue.push(frameObj);
        return;
      }
    ​
      // 是否为首帧
      let isFirstFragSeq = this.frameQueue[0].fragSeq === 1;
      // 当前帧加上queue帧是否为连续帧
      let isContinuousFragSeq = true;
      for (let i = 0; i < frameQueueLen; i++) {
        const isLast = i === frameQueueLen - 1;
    ​
        const curFragSeq = this.frameQueue[i].fragSeq;
        const nextFragSeq = isLast
          ? frameObj.fragSeq
          : this.frameQueue[i + 1].fragSeq;
    ​
        const curSegNum = this.frameQueue[i].segNum;
        const nextSeqNum = isLast
          ? frameObj.segNum
          : this.frameQueue[i + 1].segNum;
    ​
        if (curFragSeq + 1 !== nextFragSeq || curSegNum !== nextSeqNum) {
          isContinuousFragSeq = false;
          break;
        }
      }
    ​
      if (isFirstFragSeq && isContinuousFragSeq) {
        // 是否为最后一帧
        const isLastFrame = frameObj.fragCount === frameObj.fragSeq;
        if (isLastFrame) {
          this.frameQueue.forEach((item) => {
            this.bufferQueue.push(item.value);
          });
          this.frameQueue = [];
          this.bufferQueue.push(frameObj.value);
        } else {
          this.frameQueue.push(frameObj);
        }
      } else {
        // 丢帧则清空 frameQueue,则代表直接丢弃整个 segment 切片
        this.emit(EVENTS_ERROR.frameDropError);
        this.frameQueue = [];
        this.frameQueue.push(frameObj);
      }

    原本以为大功告成,结果意想不到的事情发生了。

    当出现丢帧时,通过上面的算法,确实是把整个切片的数据丢弃掉了,但是 MSE 此时居然再次异常了,意思也是说数据序列不对,导致解析失败。

    可是用 ffplay 在本地测试(丢掉一整个切片后,是可以继续播放的),陷入僵局,继续排查。

    8、阶段三
    话说最近 chatgpt 不是挺火,尝试着用了下,确实找到了原因。MSE 在消费 fmp4 数据时,需要根据内部序列号进行索引标识,因此即使是丢掉整个切片数据,还是会播放失败。怎么办?难道要回到不可靠传输?

    经过一番权衡,最终决定,当出现丢帧时,前端通知云端,重新进行切片,并且此时前端重新初始化 MSE。

    改造下来发现,效果还不错,我们把不可靠传输,datachannel 重传次数设置为 5。
    web播放h265 wes,前端,h.265,html5,网页播放器,webrtc,H5播放器

    出现丢帧的概率大大减小,就算出现丢帧,也只会有不到 2 秒的 loading,然后继续出画面,业务层可以接受。

    最终,经过上面 3 个阶段的改造,就有了整个链路图。当然其实还有很多细节,没有讲到,比如利用 mp4box 获取 codec, 前端定时检查 datachannel 状态等,就不展开细说了。有兴趣的可以留言讨论。

    完整的链路图,简单画了下。
    web播放h265 wes,前端,h.265,html5,网页播放器,webrtc,H5播放器

    9、liveweb

    目前liveweb的方案已经上线,测试下来,线上同时硬解 16 路没有性能问题,超低延时(150—200毫秒左右),秒启动 h264   h265   web播放器,h5播放器,支持协议:RTSP、RTMP、HLS、HTTP-FLV、WebSocket-FLV、GB28181、HTTP-TS、WebSocket-TS、HTTP-fMP4、WebSocket-fMP4、MP4、WebRTC

    Liveweb是好游科技自主开发的网页播放器,支持 RTSP、RTMP、HTTP、HLS、UDP、RTP、File 等多种流媒体协议播放,同时也有多种显示方式 (GDI,D3D) 及格式 (RGB24,YV12,YUY2,RGB565),经过 7x24 小时连续拷机测试,能够很好的处理断连.

    iveweb是可支持H.264/H.265视频播放的流媒体播放器,性能稳定、播放流畅,可支持的视频流格式有RTSP、RTMP、HLS、FLV、WebRTC等,具备较高的可用性。liveweb还拥有Windows、Android、iOS版本,其灵活的视频能力,极大满足了用户的多样化场景需求。

    liveweb具备较强的灵活性,在视频直播过程中liveweb可通过H5进行视频解码,只要客户端支持H5,就能完美进行视频的无插件直播,同时还支持大码率视频直播,并可支持H.264、H.265两种编码格式。如果大家正在找寻一款供能强大的流媒体播放器,那么liveweb将会是一个不错的选择,我们也欢迎大家的了解和试用

 

 

 

 

到了这里,关于Web 端支持 h265 硬解 web播放H.265流媒体 网页播放H.265的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux+nginx-http-flv-module+ffmpeg实现搭建简易流媒体服务器将rtsp流转flv格式在web端和微信小程序实时播放监控视频

    一.介绍背景 公司项目开发需求:将海康摄像头的rtsp流在web端及微信小程序端进行播放。之前我写过一篇关于web端使用webtrc+videojs播放rtsp流的文章,确实能够解决web端播放rtsp流的需求,但是这次多加了一个微信小程序....所以要考虑小程序的播放问题。本着探索实践的精神在

    2024年02月08日
    浏览(52)
  • H265格式兼容各个浏览器web端播放方案

    可能有很多朋友会遇到H265格式的视频流无法播放,毕竟现在很多相机都支持h265了,确实有很多优点,但是它最大的问题就是很多浏览器无法播放,也有部分浏览器能够兼容h265,但是总不能让用户指定浏览器使用吧,下面来说说怎么兼容各个浏览器播放。 无非两种方案,第一

    2024年02月11日
    浏览(39)
  • 基于GB28181-2022实现web无插件播放H265视频

            目前发布的GB28181-2022增加了对前端设备视频H265编码格式的支持,所以实现国标平台通过浏览器对H265视频流的无插件的解码播放将是未来的趋势。         目前大多的方案都是通过平台端把H265转码为H264,再推送到web前端进行解码播放,这种方式因为需要中间的媒体

    2024年02月06日
    浏览(34)
  • LiveNVR监控流媒体Onvif/RTSP功能-支持无人机、IPC等设备RTMP推流转码分发H5无插件播放也支持GB28181输出

    目前不是所有的无人机都支持GB28181的国标注册,有的只能输出直播流,有的只能支持RTMP的推流。比如大疆无人机有的产品上不能直接注册国标平台,只能rtmp推流。那么,项目中如果将无人机的rtmp的推流转成GB/T28181 示例:rtmp://192.168.2.135:10935/live/stream_1 192.168.2.135 是部署Liv

    2024年02月16日
    浏览(34)
  • c++高性能264/265实时h5流媒体服务器前后端整体解决方案

    c++高性能264/265实时h5流媒体服务器前后端整体解决方案 下图展示了前端播放效果。 播放1路264视频流,4路265视频流 CPU占用率10%(测试机器上运行着c++服务端和其他工具程序) GPU0占用率17% 1.1 作者测试机器配置 处理器 11th Gen Intel® Core™ i7-11800H @ 2.30GHz 2.30 GHz 机带 RAM 32.0 GB (31.

    2024年02月12日
    浏览(37)
  • Docker搭建Plex流媒体服务并播放自己本地视频

    安装Docker 创建存储配置文件的目录 创建Plex容器 配置Plex 设置媒体库 访问Plex Plex是一个流媒体服务器,可以轻松地将你的媒体文件库(如电影、电视节目和音乐)通过网络流式传输到各种设备上。 Plex 是一套媒体播放器及媒体服务器软件,提供了一整套完整的解决方案(Se

    2024年02月04日
    浏览(54)
  • H265视频硬解

    硬解,使用非CPU进行编码,如显卡GPU、专用的DSP、FPGA、ASIC芯片等。目前的主流GPU加速平台:INTEL、AMD、NVIDIA。 一、软编码和硬编码比较 软编码:实现直接、简单,参数调整方便,升级易,但CPU负载重,性能较硬编码低,低码率下质量通常比硬编码要好一点。 硬编码:性能高

    2024年02月10日
    浏览(33)
  • Video.js实现在html页面播放rtmp流媒体

    要在HTML页面中使用Video.js播放RTMP流媒体,需要使用videojs-contrib-media-sources插件和videojs-flash插件。以下是一个示例代码: !DOCTYPE html html   head     link href=\\\"//vjs.zencdn.net/7.14.3/video-js.css\\\" rel=\\\"stylesheet\\\"     script src=\\\"//vjs.zencdn.net/7.14.3/video.js\\\"/script     script src=\\\"https://cdnjs.cloudflare.c

    2024年02月08日
    浏览(40)
  • 基于OpenCV设计的流媒体播放器(RTSP、RTMP)

    随着互联网的普及和发展,流媒体技术已成为日常生活中不可或缺的一部分。流媒体播放器作为流媒体技术的重要组成部分,其性能和功能直接影响到用户的观影体验。本文介绍使用OpenCV和Qt设计一款流媒体播放器,专门用于播放直播视频流,例如RTSP、RTMP。该播放器只播放实

    2024年02月03日
    浏览(54)
  • 原来爱优腾等视频网站都是用这个来播放流媒体的

    本篇文章介绍主流的两种协议 HLS 和 DASH,以及如何制作并使用支持这些协议开源的客户端库来播放视频。 HLS (HTTP Live Streaming) 是苹果公司开发的流媒体传输协议,它使用 HTTP 来传输视频,可以防止被防火墙屏蔽。现在大部分视频网站都在使用,比如优酷、腾讯视频。 它的工

    2023年04月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包