【问题标题】:Extract frames from video in JS用JS从视频中提取帧
【发布时间】:2017-11-01 23:45:44
【问题描述】:

我正在尝试构建一个从 JavaScript 视频中提取帧的函数。这是我想出的代码。该函数接收源和回调。从那里,我用源创建一个视频,我想在画布中以设定的间隔绘制视频的帧。

很遗憾,返回的帧都是透明图片。

我尝试了一些不同的方法,但无法成功。有人可以帮忙吗? 谢谢。

const extractFramesFromVideo = function(src, callback) {
  var video = document.createElement('video');
  video.src = src;

  video.addEventListener('loadeddata', function() {
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');
    canvas.setAttribute('width', video.videoWidth);
    canvas.setAttribute('height', video.videoHeight);

    var frames = [];
    var fps = 1; // Frames per seconds to
    var interval = 1 / fps; // Frame interval
    var maxDuration = 10; // 10 seconds max duration
    var currentTime = 0; // Start at 0

    while (currentTime < maxDuration) {
      video.currentTime = currentTime; 
      context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
      var base64ImageData = canvas.toDataURL();
      frames.push(base64ImageData);

      currentTime += interval;

      if (currentTime >= maxDuration) {
        console.log(frames);
        callback(frames);
      }
    }
  });
}

export default extractFramesFromVideo;

【问题讨论】:

  • 如果我将 context.drawImage 移到 while 之外,它会绘制第一帧。
  • 我认为这个问题是在更改video.currentTime之后,您实际上需要等待视频搜索到该位置并更新帧数据,然后您才能提取它。它可能需要下载一些数据,所以它不是同步的。 loadeddata 事件仅表示第一帧已下载(至少)。我认为,在更改currentTime 之后,您需要等待seeked 事件。

标签: javascript video canvas frame


【解决方案1】:

在调整您的代码以等待seeked 事件并修复一些点点滴滴之后,它似乎工作正常:

async function extractFramesFromVideo(videoUrl, fps=25) {
  return new Promise(async (resolve) => {

    // fully download it first (no buffering):
    let videoBlob = await fetch(videoUrl).then(r => r.blob());
    let videoObjectUrl = URL.createObjectURL(videoBlob);
    let video = document.createElement("video");

    let seekResolve;
    video.addEventListener('seeked', async function() {
      if(seekResolve) seekResolve();
    });

    video.addEventListener('loadeddata', async function() {
      let canvas = document.createElement('canvas');
      let context = canvas.getContext('2d');
      let [w, h] = [video.videoWidth, video.videoHeight]
      canvas.width =  w;
      canvas.height = h;

      let frames = [];
      let interval = 1 / fps;
      let currentTime = 0;
      let duration = video.duration;

      while(currentTime < duration) {
        video.currentTime = currentTime;
        await new Promise(r => seekResolve=r);

        context.drawImage(video, 0, 0, w, h);
        let base64ImageData = canvas.toDataURL();
        frames.push(base64ImageData);

        currentTime += interval;
      }
      resolve(frames);
    });

    // set video src *after* listening to events in case it loads so fast
    // that the events occur before we were listening.
    video.src = videoObjectUrl; 

  });
}

用法:

let frames = await extractFramesFromVideo("https://example.com/video.webm");

【讨论】:

  • 从哪里获取从这段代码中提取的所有帧?
【解决方案2】:

currentComponent.refs.videopreview 在 HTML 页面中是 &lt;video ref="videopreview" autoPlay&gt;&lt;/video&gt; 下面是从视频中提取帧的代码。

   const getFrame = () => {

        const video = currentComponent.refs.videopreview;
        if(!video.srcObject) {
          return null;
        }
        const canvas = document.createElement('canvas');
        canvas.width = canvasWidth;
        canvas.height = canvasHeight;
        canvas.getContext('2d').drawImage(video, 0,0);
        const data = canvas.toDataURL('image/jpeg');     
        return data; // frame data
      }

getFrame 函数可以在需要的时间间隔调用。

【讨论】:

    猜你喜欢
    • 2022-01-06
    • 2014-02-27
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 2012-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多