web-dev-qa-db-ja.com

HTML5および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);
}

私が抱えている問題は、最初に生成された2つの画像が同じであり、持続時間5秒の画像が生成されないことです。特定の時間のビデオフレームが< video>タグに表示される前にサムネイルが生成されることがわかりました。

たとえば、video.currentTime = 5の場合、フレーム0sの画像が生成されます。その後、ビデオフレームは時間5秒にジャンプします。したがって、video.currentTime = 10の場合、フレーム5の画像が生成されます。

39
Lin

原因

問題は、ビデオのシーク(currentTimeの設定による)が非同期であることです。

seekedイベントをリッスンする必要があります。そうしないと、古い値である可能性のある実際の現在のフレームを取得するリスクがあります。

非同期であるため、setInterval()も非同期に使用しないでください。次のフレームがシークされたときに適切に同期することはできません。代わりにseekedイベントを使用するため、setInterval()を使用する必要はありません。これにより、すべての同期が維持されます。

溶液

コードを少し書き直すことで、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 passed end, seek to next interval
    if (i <= this.duration) {
        // this will trigger another seeked event
        this.currentTime = i;
    }
    else {
        // Done!, next action
    }
});

ここでデモ

48
user1693593