HTML5 视频:使用 Blob URL 流式传输视频

2024-05-17

我有一个 Blob 数组(实际上是二进制数据 - 我可以表达它,但是效率最高。我现在正在使用 Blob,但也许Uint8Array或者有什么会更好)。每个 Blob 包含 1 秒的音频/视频数据。每秒都会生成一个新的 Blob 并将其附加到我的数组中。所以代码大致如下:

var arrayOfBlobs = [];
setInterval(function() {
    arrayOfBlobs.append(nextChunk());
}, 1000);

我的目标是将这些音频/视频数据流式传输到 HTML5 元素。我知道可以像这样生成和播放 Blob URL:

var src = URL.createObjectURL(arrayOfBlobs[0]);
var video = document.getElementsByTagName("video")[0];
video.src = src;

当然这只会播放视频的前 1 秒。我还假设我可以轻松地将当前数组中的所有 Blob 连接起来以播放超过一秒的时间:

// Something like this (untested)
var concatenatedBlob = new Blob(arrayOfBlobs);
var src = ...

然而,这最终仍然会耗尽数据。由于 Blob 是不可变的,我不知道如何在收到数据时继续追加数据。

我确信这应该是可能的,因为 YouTube 和许多其他视频流服务都使用 Blob URL 进行视频播放。怎么办they do it?


Solution

经过一番重要的谷歌搜索后,我设法找到了拼图中缺失的部分:媒体源 https://developer.mozilla.org/en-US/docs/Web/API/MediaSource

实际上,这个过程是这样的:

  1. 创建一个MediaSource
  2. 从以下位置创建一个对象 URLMediaSource
  3. 设置视频的src到对象 URL
  4. On the sourceopen事件,创建一个SourceBuffer
  5. Use SourceBuffer.appendBuffer()将所有块添加到视频中

这样您就可以不断添加新的视频片段,而无需更改对象 URL。

Caveats

  • The SourceBuffer对象是very对编解码器很挑剔。这些必须声明,并且必须准确,否则不起作用
  • 您只能将一团视频数据附加到SourceBuffer一次,并且在第一个 blob 完成(异步)处理之前,您无法附加第二个 blob
  • 如果您附加太多数据到SourceBuffer不打电话.remove()那么你最终会耗尽内存并且视频将停止播放。我在笔记本电脑上使用大约 1 小时就达到了这个限制

示例代码

根据您的设置,其中一些可能是不必要的(特别是我们在获得视频数据之前构建视频数据队列的部分)SourceBuffer然后使用慢慢地追加我们的队列updateend)。如果你能等到SourceBuffer已经创建开始抓取视频数据,您的代码看起来会更好。

<html>
<head>
</head>
<body>
    <video id="video"></video>
    <script>
        // As before, I'm regularly grabbing blobs of video data
        // The implementation of "nextChunk" could be various things:
        //   - reading from a MediaRecorder
        //   - reading from an XMLHttpRequest
        //   - reading from a local webcam
        //   - generating the files on the fly in JavaScript
        //   - etc
        var arrayOfBlobs = [];
        setInterval(function() {
            arrayOfBlobs.append(nextChunk());
            // NEW: Try to flush our queue of video data to the video element
            appendToSourceBuffer();
        }, 1000);

        // 1. Create a `MediaSource`
        var mediaSource = new MediaSource();

        // 2. Create an object URL from the `MediaSource`
        var url = URL.createObjectURL(mediaSource);

        // 3. Set the video's `src` to the object URL
        var video = document.getElementById("video");
        video.src = url;

        // 4. On the `sourceopen` event, create a `SourceBuffer`
        var sourceBuffer = null;
        mediaSource.addEventListener("sourceopen", function()
        {
            // NOTE: Browsers are VERY picky about the codec being EXACTLY
            // right here. Make sure you know which codecs you're using!
            sourceBuffer = mediaSource.addSourceBuffer("video/webm; codecs=\"opus,vp8\"");

            // If we requested any video data prior to setting up the SourceBuffer,
            // we want to make sure we only append one blob at a time
            sourceBuffer.addEventListener("updateend", appendToSourceBuffer);
        });

        // 5. Use `SourceBuffer.appendBuffer()` to add all of your chunks to the video
        function appendToSourceBuffer()
        {
            if (
                mediaSource.readyState === "open" &&
                sourceBuffer &&
                sourceBuffer.updating === false
            )
            {
                sourceBuffer.appendBuffer(arrayOfBlobs.shift());
            }

            // Limit the total buffer size to 20 minutes
            // This way we don't run out of RAM
            if (
                video.buffered.length &&
                video.buffered.end(0) - video.buffered.start(0) > 1200
            )
            {
                sourceBuffer.remove(0, video.buffered.end(0) - 1200)
            }
        }
    </script>
</body>
</html>

作为额外的好处,这会自动为您提供实时流的 DVR 功能,因为您在缓冲区中保留了 20 分钟的视频数据(您可以通过简单地使用video.currentTime = ...)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HTML5 视频:使用 Blob URL 流式传输视频 的相关文章

随机推荐