Best way to capture paused video frame to canvas

2019-08-29 13:03发布

问题:

I know how to copy on frame by frame basis from a playing HTML5 video to a canvas using 2D context.

But I want to work with a paused video, change dinamically its currentTime and copy current frame of video to canvas.

My guess is some process is not called yet when video position is set with currentTime property, although the video itself does update the image it shows (but not to the canvas).

I've found that it's posible to overcome this by setting a setTimeout to do the canvas ´drawImage´ in the next step.

You can see here a jsfiddle that proves the point.

As you can see in the fiddle you can play the video, and canvas updates, but if you pause the video, mouse scroll moves currentTime of video. There, a ´seTimeout´ is needed to update the canvas, if I call the drawImage method directly, canvas doesn't update.

In short, my question is:

Is there a better way to do this? Is it posible to do this without setTimeout and inside de loop itself? Pros & Cons?

Thank you very much for reading through here!

回答1:

Every time you change the currentTime of your VideoElement, a seeked Event will trigger when the video actually changed its position.

var vid = document.getElementById("v");
var canvas = document.getElementById("c");
var context = canvas.getContext('2d');
var targetFrame = document.getElementById('t');
var cw = canvas.width = 200;
var ch = canvas.height = Math.round(cw / 1.7777);
var targetOffset = 0;

window.addEventListener('wheel', function(e) {
  e.preventDefault();
  targetOffset = targetOffset + (e.deltaY / 1000);
  targetFrame.value = targetOffset;
  seek(); // for demo purpose, we only listen to wheel
  return false;
});
// that's all is needed
vid.addEventListener('seeked', function() {
  context.drawImage(vid, 0, 0, cw, ch);
});
// for demo
// removed the rendering loop
// now it only changes the video's currentTime property
function seek() {
  targetOffset = targetOffset * 0.9;
  targetFrame.value = Math.round(targetOffset * 100) / 100;
  var vct = vid.currentTime - targetOffset;
  if (vct < 0) {
    vct = vid.duration + vct;
  } else if (vct > vid.duration) {
    vct = vct - vid.duration;
  }
  vid.currentTime = vct;
}
.column {
  float: left;
  width: 50%;
}
.row:after {
  content: "";
  display: table;
  clear: both;
}
#c {
  border: 1px solid black;
}
<h3>
  scroll up is forward
</h3>
<div class="row">
  <div class="column">
    <div>
      Video element:
    </div>
    <video controls height="120" id="v" tabindex="-1" autobuffer="auto" preload="auto">
    <source type="video/webm" src="https://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.webm"/>
</video>
  </div>
  <div class="column">
    <div>
      Canvas element:
    </div>
    <canvas id="c"></canvas>
    <div>
      Momentum: <input type=text id="t">
    </div>
  </div>
</div>