Альтернатива ImageCapture API для лучшей поддержки браузеров - PullRequest
2 голосов
/ 18 июня 2020

Я использую OCR для распознавания текста на изображениях. Я делаю фотографии / снимки с камеры / видеовхода пользователя, используя ImageCapture API из MediaStream . Вот код, отвечающий за это:

function getBlobFromMediaStream() {
    const videoTrack = mediaStream.getVideoTracks()[0]

    const imageCapture = new ImageCapture(videoTrack);

    return imageCapture.takePhoto().then(blob => {
        if (!blob) throw "Photo could not be taken";

        return blob;
    })
}

Жаль, что этот API не работает во многих браузерах (Firefox, IE, Safari). Есть ли альтернатива, которую можно использовать?

1 Ответ

2 голосов
/ 18 июня 2020

Вы можете сделать откат с HTMLVideoElement и HTMLCanvasElement.
Сначала проверьте, поддерживает ли ваш браузер API захвата изображений MediaStream, проверив, существует ли конструктор в объекте окна.

if ('ImageCapture' in window) { 
  // Has support for API.
} else {
  // No support, use fallback.
}

Затем в зависимости от этого либо используйте API, либо используйте резервный вариант.

В резервном варианте создайте элемент видео, элемент холста и контекст холста. Установите MediaStreamTrack как srcObject видеоэлемента. Видео может декодировать данные и создавать реальное изображение.

Затем используйте изображение видео, чтобы нарисовать изображение на элементе холста с помощью метода CanvasRenderingContext2D.drawImage(). Теперь текущий кадр видео был нарисован на элементе холста.

Теперь вы можете извлечь данные, которые были нарисованы на холсте, и превратить их в большой двоичный объект с помощью HTMLCanvasElement.toBlob() метод получения Blob из холста. Оберните вызов функции в Promise по двум причинам:

  1. Вам нужно будет извлечь большой двоичный объект из обратного вызова, в этом вам может помочь оболочка Promise.
  2. ImageCapture.takePhoto() метод также возвращает обещание. Таким образом, у вас будет такое же поведение, как и при поддержке API.

Все вместе это будет выглядеть так.

function getBlobFromMediaStream(stream) {

  if ('ImageCapture' in window) {

    const videoTrack = stream.getVideoTracks()[0];
    const imageCapture = new ImageCapture(videoTrack);
    return imageCapture.takePhoto();

  } else {

    const video = document.createElement('video');
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    video.srcObject = stream;

    return new Promise((resolve, reject) => {
      try {
        video.addEventListener('loadeddata', event => {
          canvas.width = video.videoWidth;
          canvas.height = video.videoHeight;
          video.play().then(() => {
            context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
            canvas.toBlob(resolve, 'image/png');
          });
        });
      } catch(error) {
        reject(error);
      }
    });

  }

}

Затем вызовите свою функцию как Ниже. Я отказался от оператора throw, чтобы все отклоненные состояния были пойманы в один и тот же момент, когда вы вызываете функцию getBlobFromMediaStream. Кроме того, согласно документации, вы получаете либо Blob, когда обещание выполнено, либо ошибку, когда оно отклонено.

То же самое касается и отката. Единственная разница в том, что функция HTMLCanvasElement.toBlob() выдает SecurityError, если растровое изображение холста не является чистым.

getBlobFromMediaStream(mediaStream).then(blob => {
  // Work with your blob.
}).catch(error => {
  console.log(`Photo could not be taken. ${error}`);
})
...