ImageCapture API 的替代品以获得更好的浏览器支持

Alternative for the ImageCapture API for better browser support

我使用 OCR 来识别图像中的文本。我使用 ImageCapture API from a MediaStream 从用户的 camera/video 输入中获取 photos/snapshots。这是负责的代码:

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)。有没有可以使用的替代品?

您可以使用 HTMLVideoElementHTMLCanvasElement 进行后备。
首先通过检查 window 对象中是否存在构造函数来检查您的浏览器是否支持 MediaStream Image Capture API。

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

然后基于此使用 API 或使用您的后备。

在回退中创建一个视频元素、canvas 元素和一个 canvas 上下文。将 MediaStreamTrack 设置为视频元素的 srcObject。该视频能够解码数据并制作实际图片。

使用视频的图像通过CanvasRenderingContext2D.drawImage()方法将图像绘制到canvas元素。这样,canvas 元素将绘制视频中当前帧的图像。

现在您可以提取在 canvas 上绘制的数据,并使用 HTMLCanvasElement.toBlob() 方法将其转换为 Blob

将函数调用包装在 Promise 中有两个原因:

  1. 您需要从回调中提取 blob,Promise 包装器可以帮助您完成此操作。
  2. ImageCapture.takePhoto() 方法也是 returns 一个 Promise。因此,您将拥有与支持 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) => {
      video.addEventListener('loadeddata', async () => {
        const { videoWidth, videoHeight } = video;
        canvas.width = videoWidth;
        canvas.height = videoHeight;

        try {
          await video.play();
          context.drawImage(video, 0, 0, videoWidth, videoHeight);
          canvas.toBlob(resolve, 'image/png');
        } catch (error) {
          reject(error);
        }
      });
    });

  }
  
}

像下面的例子一样调用你的函数。我已经放弃了 throw 语句,这样当您调用 getBlobFromMediaStream 函数时,所有 rejected 状态都将在同一点被捕获。

根据 ImageCapture.takePhoto() 的文档,您要么在承诺已履行时得到一个 Blob,而在它被拒绝时得到一个错误。

回退也是如此。唯一的区别是,如果 canvas 的位图不是原点干净的,HTMLCanvasElement.toBlob() 函数会抛出 SecurityError

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