大的音视屏文件播放思路

在前端播放一个大小为 10GB 的视频时,直接下载整个视频文件然后播放显然是不切实际的,因为这样会导致极大的延迟和带宽消耗。为了尽快开始播放大文件,可以采用以下几种技术来优化视频播放体验。

1.视频流式传输 (Streaming)

流式传输允许前端在不下载完整文件的情况下,就能开始播放视频。具体的方法有两种常见的流式传输方式:

HLS (HTTP Live Streaming)

HLS 是 Apple 提供的一种基于 HTTP 的流媒体协议,可以将大视频文件分割成多个小的 .ts 片段(通常几秒钟一个),并通过 .m3u8 播放列表进行管理。前端播放器根据网络情况动态加载这些小片段,而不需要等待整个视频文件加载完成。

  • 优势:支持实时加载并开始播放,适应不同的网络带宽,避免了一开始就下载整个大文件。
  • 实现方式:使用如 Video.jshls.js 等 JavaScript 库播放 HLS 流媒体。
<video id="video"></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
  if (Hls.isSupported()) {
    var video = document.getElementById('video');
    var hls = new Hls();
    hls.loadSource('https://example.com/playlist.m3u8');  // 加载 .m3u8 播放列表
    hls.attachMedia(video);  // 将媒体附加到 <video> 元素
    hls.on(Hls.Events.MANIFEST_PARSED, function () {
      video.play();  // 播放视频
    });
  }
</script>

DASH (Dynamic Adaptive Streaming over HTTP)

DASH 是一种类似 HLS 的流媒体协议,适用于动态调整视频质量以适应带宽条件。视频文件被切分为多个片段,并且客户端会根据带宽情况自动选择合适的清晰度播放。

  • 优势:支持多种设备和浏览器,提供了与 HLS 类似的流式传输体验。
  • 实现方式:可以使用 Shaka Playerdash.js 来播放 DASH 流媒体
<video id="video" controls>
  <source src="https://example.com/video.mpd" type="application/dash+xml">
</video>

2. 使用 Media Source Extensions (MSE)

是一个允许网页动态地创建媒体流并将其送入 <video> 元素进行播放的浏览器 API。MSE 使得浏览器能够支持自适应流媒体(如 HLS、DASH)和实时视频播放(例如,视频片段的动态加载和拼接)。前端通过使用 MSE 可以更灵活地处理视频源,按需加载并控制视频流的播放。

<video id="video" controls></video>
<script>
  // 创建 MediaSource 对象
  const mediaSource = new MediaSource();
  const video = document.getElementById('video');
  video.src = URL.createObjectURL(mediaSource);

  mediaSource.addEventListener('sourceopen', onSourceOpen);

  function onSourceOpen() {
    const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001F"');

    fetchVideoSegment('path/to/video/segment1.mp4', sourceBuffer);
  }

  function fetchVideoSegment(url, sourceBuffer) {
    fetch(url)
      .then(response => response.arrayBuffer())
      .then(data => {
        // 将视频数据添加到 SourceBuffer
        sourceBuffer.appendBuffer(data);

        // 等待更新结束后加载下一个片段
        sourceBuffer.addEventListener('updateend', () => {
          console.log('Segment loaded, loading next...');
          // 加载下一个视频片段
          fetchNextSegment();
        });
      });
  }

  function fetchNextSegment() {
    fetchVideoSegment('path/to/video/segment2.mp4', sourceBuffer);
  }
</script>

3.使用 JavaScript 实现 Range 请求(分片下载)

在渐进式下载中,你可以通过 JavaScript 控制 <video> 元素和 HTTP Range 请求。首先,我们可以使用 fetch() API 和 Range 请求头来加载视频的一部分。接下来,将其传输到 <video> 元素中进行播放。

服务器端需要支持 HTTP Range 请求。大部分现代 HTTP 服务器(如 Apache、Nginx)默认支持 Range 请求,但有些老旧的服务器可能需要额外配置。

服务器示例:

  • Nginx 配置: 在 Nginx 配置文件中,可以启用 range 模式来支持渐进式下载:

    server { location /video.mp4 { add_header Accept-Ranges bytes; # 其他配置... } }

    这确保了服务器在接收到 Range 请求时,返回文件的特定部分,而不是整个文件。

客户端代码示例:

const video = document.querySelector('video');
const videoUrl = 'video.mp4';

// 使用 fetch 发起 Range 请求
fetch(videoUrl, {
  headers: {
    'Range': 'bytes=0-999999' // 请求文件的前 1MB
  }
})
.then(response => response.blob())
.then(blob => {
  // 使用 Blob 对象将视频数据加载到 video 元素
  video.src = URL.createObjectURL(blob);
  video.play();
});

// 假设进度条向前推进,继续加载剩余数据
function loadNextChunk(startByte, endByte) {
  fetch(videoUrl, {
    headers: {
      'Range': `bytes=${startByte}-${endByte}` // 请求接下来的数据部分
    }
  })
  .then(response => response.blob())
  .then(blob => {
    // 将数据追加到视频元素
    const videoSourceBuffer = new MediaSource();
    const sourceBuffer = videoSourceBuffer.addSourceBuffer('video/mp4; codecs="avc1.4d401f, mp4a.40.2"');
    sourceBuffer.appendBuffer(blob);
  });
}

4、总结

  • MSE:提供了高度的灵活性和控制,适用于需要按需加载、动态拼接视频片段、低延迟播放等复杂场景。它适合实时流媒体、直播和大文件按需加载的场景。
  • Range 请求:实现简单,适用于视频文件的按需加载,但不支持自适应比特率或流媒体协议,因此它更适合于简单的视频播放需求,且不适用于需要自适应质量或高效流控制的场景。
  • HLS:适用于直播、点播等流媒体应用,特别是在网络条件变化时,HLS 的自适应比特率特性能够提供更好的用户体验。它要求服务器端对视频进行切割并提供 .m3u8 播放列表,适合大规模的视频流传输。

根据应用场景选择合适的技术:

  • 复杂流媒体应用(低延迟、动态质量调整等):推荐使用 MSE
  • 简单的按需视频加载:可以使用 Range 请求
  • 大规模流媒体直播或点播(需要自适应比特率):推荐使用 HLS
posted @ 2024-12-05 16:16  我是格鲁特  阅读(65)  评论(0编辑  收藏  举报