Видеосъемка элемента холста с помощью API записи MediaStream не работает

Я пытаюсь записать и загрузить видео элемента canvas, используя официальный MediaStream Recording API

<!DOCTYPE html>
<html>

<body>
    <h1>Lets test mediaRecorder</h1>

    <canvas id="myCanvas" width="200" height="100" style="border:1px solid #d3d3d3;">
        Your browser does not support the HTML canvas tag.
    </canvas>

    <script>
        var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");
        ctx.font = "30px Arial";
        ctx.fillText("Hello World", 10, 50);

        const stream = c.captureStream(25);
        var recordedChunks = [];
        console.log(stream);
        var options = { mimeType: "video/webm; codecs=vp9" };
        mediaRecorder = new MediaRecorder(stream, options);

        mediaRecorder.ondataavailable = handleDataAvailable;
        mediaRecorder.start();

        function handleDataAvailable(event) {
            console.log("data-available");
            if (event.data.size > 0) {
                recordedChunks.push(event.data);
                console.log(recordedChunks);
                download();
            } else {
                // ...
            }
        }

        function download() {
            var blob = new Blob(recordedChunks, {
                type: "video/webm"
            });
            var url = URL.createObjectURL(blob);
            var a = document.createElement("a");
            document.body.appendChild(a);
            a.style = "display: none";
            a.href = url;
            a.download = "test.webm";
            a.click();
            window.URL.revokeObjectURL(url);
        }

        // demo: to download after 10 sec
        setTimeout(event => {
            console.log("stopping");
            mediaRecorder.stop();
        }, 10000);
    </script>

</body>

</html>

код работает, и я могу загрузить test.webm, но я предполагаю, что у него нет никаких данных, так как я не вижу никакого контента при воспроизведении этого файла в VLC Media Player

Чего мне не хватает, чтобы заставить его работать?


person Alok Singh Mahor    schedule 26.03.2021    source источник


Ответы (1)


Здесь вы столкнулись с несколькими ошибками.

Первый из них немного простителен: Chrome не создает доступные для поиска веб-файлы. Это связано с тем, как файлы мультимедиа создан и как работает MediaRecorder API. Чтобы они могли добавить эту информацию, им нужно сохранить фрагмент, в котором находятся метаданные, чтобы добавить эту информацию, когда запись будет завершена.

Я не слишком уверен, что Firefox здесь делает по-другому, но VLC предпочитает их файл.

Другая ошибка Chrome, гораздо менее простительная, заключается в том, что они не передают новый кадр в MediaRecorder до тех пор, пока мы снова не нарисуем исходный холст. MediaRecorder, вы ничего не получите на выходе...

Чтобы обойти это, простого рисования кадра прямо перед тем, как мы остановим записывающее устройство, должно быть достаточно, за исключением того, что ничто не позволяет нам точно знать, когда браузер отправит этот кадр в записывающее устройство...

Таким образом, единственным рабочим решением здесь является постоянное рисование на холсте, пока мы записываем его. Хорошо то, что ему не нужно рисовать что-то новое: мы можем обмануть браузер, думая, что что-то новое было нарисовано, рисуя прозрачный прямоугольник.

И последнее замечание: хотя Chrome поддерживает экспорт холста с прозрачностью, не все браузеры могут, и даже когда они поддерживаются, большинство плееров имеют черный фон по умолчанию. Поэтому обязательно нарисуйте себе фон другого цвета, когда будете его записывать.

Все, что сказано, вот фиксированная демонстрация:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// draw a white background
ctx.fillStyle = "white";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "black";
ctx.font = "30px Arial";
ctx.fillText("Hello World", 10, 50);

const stream = c.captureStream(25);
var recordedChunks = [];
var options = {};
mediaRecorder = new MediaRecorder(stream, options);

mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start();
// Chrome requires we draw on the canvas while recording
mediaRecorder.onstart = animationLoop;

function animationLoop() {
  // draw nothing, but still draw
  ctx.globalAlpha = 0;
  ctx.fillRect(0, 0, 1, 1);
  // while we're recording
  if (mediaRecorder.state !== "inactive") {
    requestAnimationFrame(animationLoop);
  }
}
// wait for the stop event to export the final video
// the dataavailable can fire before
mediaRecorder.onstop = (evt) => download();

function handleDataAvailable(event) {
  recordedChunks.push(event.data);
}

function download() {
  var blob = new Blob(recordedChunks, {
    type: "video/webm"
  });
  var url = URL.createObjectURL(blob);
  // exporting to a video element for that demo
  // the downloaded video will still not work in some programs
  // For this one would need to fix the markers using something like ffmpeg.
  var video = document.getElementById('video');
  video.src = url;
  // hack to make the video seekable in the browser
  // see https://stackoverflow.com/questions/38443084/
  video.onloadedmetadata = (evt) => {
    video.currentTime = 10e6;
    video.addEventListener("seeked", () => video.currentTime = 0, {
      once: true
    })
  }
}

setTimeout(() => {
  console.clear();
  mediaRecorder.stop();
}, 10000);
console.log("please wait while recording (10s)");
<h1>Lets test mediaRecorder</h1>

<canvas id="myCanvas" width="200" height="100" style="border:1px solid #d3d3d3;">
  Your browser does not support the HTML canvas tag.
</canvas>
<video controls id="video"></video>

person Kaiido    schedule 26.03.2021
comment
Спасибо за этот развернутый ответ. Вместо того, чтобы добавлять видео или страницу или загружать их, можем ли мы транслировать их на YouTube в прямом эфире? - person Alok Singh Mahor; 26.03.2021
comment
О, извините, я совершенно не знаю ни этот сервис, ни то, что они принимают в качестве входных данных... Но да, должен быть способ притвориться, что такой MediaStream является устройством, но это, вероятно, потребует некоторых сторонних приложений, которые я я не уверен, что они уже были написаны ;-) - person Kaiido; 26.03.2021