簡體   English   中英

使用 HTML5 和 JavaScript 從視頻中捕獲幀

[英]Capture frames from video with HTML5 and JavaScript

我想每 5 秒從視頻中捕獲一幀。

這是我的 JavaScript 代碼:

video.addEventListener('loadeddata', function() {
    var duration = video.duration;
    var i = 0;

    var interval = setInterval(function() {
        video.currentTime = i;
        generateThumbnail(i);
        i = i+5;
        if (i > duration) clearInterval(interval);
    }, 300);
});

function generateThumbnail(i) {     
    //generate thumbnail URL data
    var context = thecanvas.getContext('2d');
    context.drawImage(video, 0, 0, 220, 150);
    var dataURL = thecanvas.toDataURL();

    //create img
    var img = document.createElement('img');
    img.setAttribute('src', dataURL);

    //append img in container div
    document.getElementById('thumbnailContainer').appendChild(img);
}

我遇到的問題是第一個生成的兩個圖像是相同的,並且沒有生成持續時間為 5 秒的圖像。 我發現縮略圖是在< video>標簽中顯示特定時間的視頻幀之前生成的。

例如,當video.currentTime = 5時,生成第 0 幀的圖像。 然后視頻幀跳轉到時間 5s。 所以當video.currentTime = 10時,生成第 5 幀的圖像。

原因

問題是尋找視頻(通過設置它的currentTime )是異步的。

您需要收聽所seeked事件,否則它將冒着采用實際當前幀的風險,這可能是您的舊值。

由於它是異步的,因此您不能使用setInterval()因為它也是異步的,並且在尋找下一幀時您將無法正確同步。 不需要使用setInterval()因為我們將利用所seeked事件來保持一切同步。

解決方案

通過稍微重新編寫代碼,您可以使用所seeked事件來瀏覽視頻以捕獲正確的幀,因為該事件通過設置currentTime屬性確保我們實際上處於我們請求的幀。

示例

// global or parent scope of handlers
var video = document.getElementById("video"); // added for clarity: this is needed
var i = 0;

video.addEventListener('loadeddata', function() {
    this.currentTime = i;
});

將此事件處理程序添加到聚會中:

video.addEventListener('seeked', function() {

  // now video has seeked and current frames will show
  // at the time as we expect
  generateThumbnail(i);

  // when frame is captured, increase here by 5 seconds
  i += 5;

  // if we are not past end, seek to next interval
  if (i <= this.duration) {
    // this will trigger another seeked event
    this.currentTime = i;
  }
  else {
    // Done!, next action
  }
});

如果您想從視頻中提取所有幀,請參閱此答案 下面的示例假設您希望按照 OP 的要求每 5 秒提取一幀。


這個答案需要在撰寫本文時 Chrome 和 Edge 支持的WebCodecs。

<canvas id="canvasEl"></canvas>
<script type="module">
  import getVideoFrames from "https://deno.land/x/get_video_frames@v0.0.8/mod.js"

  let ctx = canvasEl.getContext("2d");

  // `getVideoFrames` requires a video URL as input.
  // If you have a file/blob instead of a videoUrl, turn it into a URL like this:
  let videoUrl = URL.createObjectURL(fileOrBlob);

  const saveFrameEverySeconds = 5;
  let elapsedSinceLastSavedFrame = 0;
  await getVideoFrames({
    videoUrl,
    onFrame(frame) {  // `frame` is a VideoFrame object: 
      elapsedSinceLastSavedFrame += frame.duration / 1e6; // frame.duration is in microseconds, so we convert to seconds
      if(elapsedSinceLastSavedFrame > saveFrameEverySeconds) {
        ctx.drawImage(frame, 0, 0, canvasEl.width, canvasEl.height);
        elapsedSinceLastSavedFrame = 0;
      }
      frame.close();
    },
    onConfig(config) {
      canvasEl.width = config.codedWidth;
      canvasEl.height = config.codedHeight;
    },
  });
  
  URL.revokeObjectURL(fileOrBlob); // revoke URL to prevent memory leak
</script>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM