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