Самый быстрый способ захвата изображения с HTML Canvas - PullRequest
1 голос
/ 13 апреля 2020

Вот мой код для захвата изображения из видео, воспроизводимого на Canvas:

    let drawImage = function(time) {
        prevCtx.drawImage(videoPlayer, 0, 0, w, h);
        requestAnimationFrame(drawImage);
    }
    requestAnimationFrame(drawImage);

    let currIndex = 0;
    setInterval(function () {
        if(currIndex === 30) {
            currIndex = 0;
            console.log("Finishing video...");
            videoWorker.postMessage({action : "finish"});
        } else {
            console.log("Adding frame...");
            // w/o this `toDataURL` this loop runs at 30 cycle / second
            // so that means, this is the hot-spot and needs optimization:
            const base64img = preview.toDataURL(mimeType, 0.9);
            videoWorker.postMessage({ action: "addFrame", data: base64img});
            currIndex++;
        }
    }, 1000 / 30)

Цель состоит в том, чтобы каждые 30 кадров (что должно составлять 1 секунду) приводили к перекодированию кадры добавлены.

Проблема в том, что preview.toDataURL(mimeType, 0.9); добавляет не менее 1 секунды, без этого журнал показывает, что currIndex === 30 срабатывает каждую секунду. Что было бы лучшим подходом, чтобы иметь возможность захватывать по крайней мере около 30 кадров в секунду. Какой самый быстрый способ захвата изображения с HTML Canvas , чтобы он не был узким местом процесса транскодирования видео в реальном времени?

1 Ответ

1 голос
/ 13 апреля 2020

Вероятно, вам следует пересмотреть свой проект, потому что сохранение всего видео в виде неподвижных изображений мгновенно выбьет память из большинства устройств. Вместо этого взгляните на MediaStreams и MediaRecorder API, которые способны выполнять перекодирование и сжатие в реальном времени. Вы можете запросить MediaStream с холста с помощью его метода captureStream().


Быстрее всего, вероятно, отправить ImageBitmap в ваш рабочий поток, это действительно быстро генерировать из холста (простая копия буфера пикселей), и может быть передал в ваш рабочий скрипт, откуда вы сможете нарисовать его на OffscreenCanvas.

Основной недостаток: в настоящее время поддерживается только в последних Chrome и Firefox (через webgl ), и это не может быть заполнено ...

main. js

else {
  console.log("Adding frame...");
  const bitmap = await createImageBitmap(preview);
  videoWorker.postMessage({ action: "addFrame", data: bitmap }, [bitmap]);
  currIndex++;
}

работник. js

const canvas = new OffscreenCanvas(width,height);
const ctx = canvas.getContext('2d'); // Chrome only
onmessage = async (evt) => {
  // ...
  ctx.drawImage( evt.data.data, 0, 0 );
  const image = await canvas.convertToBlob();
  storeImage(image);
};

Другим вариантом является передача данных ImageData . Не так быстро, как ImageBitmap, он по-прежнему обладает тем преимуществом, что не останавливает ваш основной поток с помощью компонента сжатия, и, поскольку его можно передавать, сообщение Worker также не требует больших вычислительных ресурсов.
Если вы go, это дорога, вы можете сжать данные, используя что-то вроде pako (который использует алгоритм сжатия, используемый изображениями PNG) из вашего рабочего потока.

main. js

else {
  console.log("Adding frame...");
  const img_data = prevCtx.getImageData(0,0,width,height);
  videoWorker.postMessage({ action: "addFrame", data: img_data }, [img_data.data]);
  currIndex++;
}

рабочий. js

onmessage = (evt) => {
 // ...
 const image = pako.deflate(evt.data.data); // compress to store
 storeImage(image);
};
...