HTML5-видео: потоковое видео с URL-адресами BLOB-объектов - PullRequest
0 голосов
/ 14 мая 2018

У меня есть массив BLOB-объектов (на самом деле двоичные данные - я могу выразить это, однако, это наиболее эффективно. Сейчас я использую BLOB-объекты, но, возможно, Uint8Array или что-то было бы лучше).Каждый BLOB-объект содержит 1 секунду аудио / видео данных.Каждую секунду новый BLOB-объект генерируется и добавляется в мой массив.Таким образом, код выглядит примерно так:

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

Моя цель - передать эти аудио / видео данные в элемент HTML5.Я знаю, что URL-адрес BLOB-объекта может быть сгенерирован и воспроизведен следующим образом:

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 и многие другие службы потокового видео используют URL-адреса Blob для воспроизведения видео.Как они делают это?

1 Ответ

0 голосов
/ 15 мая 2018

Решение

После некоторого значительного поиска в Google мне удалось найти недостающую часть головоломки: MediaSource

Эффективно процесс выглядит так:

  1. Создать MediaSource
  2. Создание URL объекта из MediaSource
  3. Установите для видео src URL объекта
  4. В событии sourceopen создайте SourceBuffer
  5. Используйте SourceBuffer.appendBuffer(), чтобы добавить все свои фрагменты к видео

Таким образом, вы можете добавлять новые фрагменты видео без изменения URL объекта.

Предостережения

  • SourceBuffer объект очень требователен к кодекам. Они должны быть объявлены и должны быть точными, иначе это не сработает
  • Вы можете добавлять только один блок видеоданных в SourceBuffer за один раз, и вы не можете добавить второй блок, пока первый не завершит (асинхронно) обработку
  • Если вы добавите слишком много данных в 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 = ...)

...