在 web worker 中创建只读元素

createElement for read-only in web worker

我知道从 web worker 访问 DOM 对线程安全来说是个坏主意,但我相信在我的用例中它应该是绝对安全的。

我试图在特定时间范围内从视频中获取图像数据(对于我的用例,一遍又一遍)。因为这是一个繁重且不重要的过程,所以在工作线程中执行此操作是理想的。这是我想在工作线程中 运行 的(剥离版本)代码:

 const video = document.createElement('video')
 video.src = 'My video src'
 video.currentTime = desiredTimestamp
 const canvas = document.createElement('canvas')
 const context = canvas.getContext('2d')
 context.drawImage(video, 0, 0, w, h) //paints data onto a canvas
 return canvas.toDataURL() //takes data and makes it a string

在worker中运行时,JS抛出document is undefined的错误。有什么办法可以做到这一点而不必在主线程中做到这一点?如果这不是工人的正确用例,还有其他方法可以“在后台”执行此操作吗?我希望所有其他进程优先于此。

答案是后台任务 API,可在此处找到:https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API 我宁愿在后台线程上执行此加载,所以保留它,但这就是我要使用的除非有更好的东西出现。

不幸的是,仍然无法在 Worker 上下文中从视频中抓取帧。
也许 Web-Codecs API 会提供,但它只是为了将来。

目前,您能做的最好的事情是从视频创建一个 ImageBitmap,并将其传递给 Worker 中的 OffscreenCanvas,这样它将能够在另一个线程中处理静止图像。

不过,由于您只是生成这些静止图像,因此 Web-Worker 不会有太大帮助,因为抓取视频帧的最大工作已经完成,除非您正在绘制一个非常大的视频帧,整个过程无论如何都不应该阻塞你的页面,而且如果你只使用异步 API。

这是一个注释代码,我认为它提供了从视频中抓取静止图像的优先级最低的方法。

async function getFrame( url, time ) {

  const video = document.createElement('video');
  video.crossOrigin = 'anonymous';
  // Fetching and seeking are done in parallel, should not block the main thread
  video.src = url;
  video.currentTime = time;
  await new Promise( res => { video.onseeked = res; } );
  
  // createImageBitmap is truly async only with Blobs
  // so if you really want to be sure it doesn't interfere with other running scripts
  // you can wait for the next idle
  await new Promise( res => requestIdleCallback( res ) );
  const img = await createImageBitmap( video );
  // This can be done synchronously, very small computation required
  const canvas = document.createElement('canvas');
  canvas.width = img.width;
  canvas.height = img.height;
  const context = canvas.getContext('bitmaprenderer')
  context.transferFromImageBitmap( img );

  // You probably don't need a data:// URL so instead we'll generate a blob:// URL
  // Even though the copy of pixels is still done synchronously
  // the compression is most likely done in parallel
  const blob = await new Promise( res => canvas.toBlob( res ) )
  return URL.createObjectURL( blob );
}

getFrame( 'https://upload.wikimedia.org/wikipedia/commons/a/a4/BBH_gravitational_lensing_of_gw150914.webm', 2 ).then( url => {
  const img = new Image();
  document.body.append( img );
  img.src = url;
} )
.catch( console.error );