JavaScript:可靠地提取视频帧 [英] JavaScript: Extract video frames reliably

查看:1327
本文介绍了JavaScript:可靠地提取视频帧的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个客户端项目,它允许用户提供视频文件并对其应用基本操作。我试图从视频中可靠地提取帧。目前我有一个< video> ,我正在加载所选视频,然后拉出每个框架如下:


  1. 寻找开始

  2. 暂停视频

  3. 绘制 ; video> 到< canvas>

  4. 从画布捕获 .toDataUrl()

  5. 向前搜索1/30秒(1帧)。

  6. 冲洗重复

这是一个相当低效的过程,更具体地说,是证明不可靠,因为我经常遇到框架。这似乎是因为它不绘制到画布之前更新实际的< video> 元素。



我宁愿不必将原始视频上传到服务器,只是拆分帧,然后将其下载回客户端。



任何建议更好的方法做到这一点非常感谢。唯一的警告是,我需要它的任何格式的浏览器支持(在JS中解码不是一个很好的选择)。

解决方案

大部分取自这个伟大的答案 GameAlchemist



由于浏览器不尊重视频的帧率,而是使用一些技巧,电影的速度和屏幕的刷新率,你假设每30秒,一个新的帧将被绘制是相当不准确。

不过, timeupdate 事件应该在当前时间



因此,我会这样做:



< preEventListener('change',extractFrames,false); function extractFrames(){var video = document.createElement('video'); var array = []; var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var pro = document.querySelector('#progress'); function initCanvas(e){canvas.width = this.videoWidth; canvas.height = this.videoHeight; } function drawFrame(e){this.pause(); ctx.drawImage(this,0,0); / *这将保存为一个Blob,比toDataURL更少的内存消耗polyfill可以在https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Polyfill * / canvas.toBlob中找到(saveFrame,'image / jpeg'); pro.innerHTML =((this.currentTime / this.duration)* 100).toFixed(2)+'%'; if(this.currentTime< this.duration){this.play(); }} function saveFrame(blob){array.push(blob); } function revokeURL(e){URL.revokeObjectURL(this.src); } function onend(e){var img; // do every with the frames for(var i = 0; i

 < input type =fileaccept =video / */>< p id =progress>< / p>  


I'm working on a client-side project which lets a user supply a video file and apply basic manipulations to it. I'm trying to extract the frames from the video reliably. At the moment I have a <video> which I'm loading selected video into, and then pulling out each frame as follows:

  1. Seek to the beginning
  2. Pause the video
  3. Draw <video> to a <canvas>
  4. Capture the frame from the canvas with .toDataUrl()
  5. Seek forward by 1 / 30 seconds (1 frame).
  6. Rinse and repeat

This is a rather inefficient process, and more specifically, is proving unreliable as I'm often getting stuck frames. This seems to be from it not updating the actual <video> element before it draws to the canvas.

I'd rather not have to upload the original video to the server just to split the frames, and then download them back to the client.

Any suggestions for a better way to do this are greatly appreciated. The only caveat is that I need it to work with any format the browser supports (decoding in JS isn't a great option).

解决方案

Mostly taken from this great answer by GameAlchemist :

Since browsers doesn't respect videos' framerates, but instead "use of some tricks to make a match between the frame-rate of the movie and the refresh-rate of the screen", your assumption that every 30th of a second, a new frame will be painted is quite inaccurate.
However, the timeupdate event should fire when the currentTime has changed, and we can assume that a new frame was painted.

So, I would do it like so :

document.querySelector('input').addEventListener('change', extractFrames, false);

function extractFrames() {
  var video = document.createElement('video');
  var array = [];
  var canvas = document.createElement('canvas');
  var ctx = canvas.getContext('2d');
  var pro = document.querySelector('#progress');

  function initCanvas(e) {
    canvas.width = this.videoWidth;
    canvas.height = this.videoHeight;
  }

  function drawFrame(e) {
    this.pause();
    ctx.drawImage(this, 0, 0);
    /* 
    this will save as a Blob, less memory consumptive than toDataURL
    a polyfill can be found at
    https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Polyfill
    */
    canvas.toBlob(saveFrame, 'image/jpeg');
    pro.innerHTML = ((this.currentTime / this.duration) * 100).toFixed(2) + ' %';
    if (this.currentTime < this.duration) {
      this.play();
    }
  }

  function saveFrame(blob) {
    array.push(blob);
  }

  function revokeURL(e) {
    URL.revokeObjectURL(this.src);
  }
  
  function onend(e) {
    var img;
    // do whatever with the frames
    for (var i = 0; i < array.length; i++) {
      img = new Image();
      img.onload = revokeURL;
      img.src = URL.createObjectURL(array[i]);
      document.body.appendChild(img);
    }
    // we don't need the video's objectURL anymore
    URL.revokeObjectURL(this.src);
  }
  
  video.autoplay = true;
  video.muted = true;

  video.addEventListener('loadedmetadata', initCanvas, false);
  video.addEventListener('timeupdate', drawFrame, false);
  video.addEventListener('ended', onend, false);

  video.src = URL.createObjectURL(this.files[0]);
}

<input type="file" accept="video/*" />
<p id="progress"></p>

这篇关于JavaScript:可靠地提取视频帧的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆