[英]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.