0

I have a video and I need to capture 3 images from it, at different time. Time is not random, I have to take images at specified time (13:10:02, 13:10:03, 13:10:04). The images should be displayed on the page when I access the page, not when I click a button or something like that. Until now I tried with an example that I found on the internet, but on this example, the image is displayed after a button was clicked, and I don't know how to give a specified time.

<video controls>
    <source src="http://www.jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v" type="video/mp4"></source>
    <source src="http://www.jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v" type="video/mp4"></source>
</video>
<canvas id="canvas" width="640" height="480"></canvas>
<button id="snap" onclick="snap()">Snap Photo</button>

<script>
    var video=document.querySelector('video');
    var canvas=document.querySelector('canvas');
    var context=canvas.getContext('2d');
    var w,h,ratio;
    //add loadedmetadata which will helps to identify video attributes......
    video.addEventListener('loadedmetadata',function()
    {
        ratio=video.videoWidth/video.videoHeight;
        w=video.videoWidth-100;
        h=parseInt(w/ratio,10);
        canvas.width=w;
        canvas.height=h;
    },false);
    ///define a function

    function snap()
    {
        context.fillRect(0,0,w,h);
        context.drawImage(video,0,0,w,h);
    }
</script>
eu127
  • 113
  • 1
  • 13

1 Answers1

1

You can set the time, for which frame you want to draw to canvas, by setting the currentTime on the video object.
Then you need to wait for video to finish seeking, draw the video onto canvas an move to the next frame.
Of course you need to wait for the video to load before you start drawing its frames.

var images = [ // defined canvases and times for frames
    {
        canvas: '#canvas1',
        time: 10.0 // in seconds
    },
    {
        canvas: '#canvas2',
        time: 19.0
    },
    {
        canvas: '#canvas3',
        time: 26.0
    },
];

function drawFrame(video, time, canvas, callback)
{
    var context = canvas.getContext("2d");
    video.addEventListener("seeked", function(e) {
        e.target.removeEventListener(e.type, arguments.callee); // remove the handler or else it will draw another frame on the same canvas, when the next seek happens
        context.fillRect(0, 0, canvas.width, canvas.height);
        context.drawImage(video, 0, 0, canvas.width, canvas.height);
        callback();
    });
    video.currentTime = time;
}
function draw(video, images, i)
{
    if (i >= images.length)
        return;
    drawFrame(video, images[i].time, images[i].canvas, function() {
        draw(video, images, i+1);
    });
}
var video = document.createElement('video');
video.addEventListener('loadedmetadata', function() // when the metadata is loaded set the canvases size and start drawing frames
{
    var ratio = video.videoWidth / video.videoHeight;
    var w = video.videoWidth - 100; // set the width of the images to 100 px less than the video
    var h = w / ratio; // fix the height accordingly
    for (var i=0; i<images.length; i++)
    {
        images[i].canvas = document.querySelector(images[i].canvas);

        images[i].canvas.width = w;
        images[i].canvas.height = h;
    }

    draw(video, images, 0);
});
video.src = "http://www.jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v";

JSFiddle

gre_gor
  • 6,115
  • 9
  • 39
  • 46
  • 1
    Safari 10 on macOS 10 unfortunately has been known to fire the "seeked" event before the first frame from the new position is actually ready for drawImage. A kludge to work around this is: function myDrawImg(video,x,y,w,h,done_callback) { c.fillRect(x,y,w,h); c.stroke(); var d=c.getImageData(x,y,w,h).data; c.drawImage(video,x,y,w,h); if (JSON.stringify(d)==JSON.stringify(c.getImageData(x,y,w,h).data)) { setTimeout(function() { myDrawImg(video,x,y,w,h,callback); },100); } else done_callback()} it relies on reading pixels to see if the video has actually been drawn; won't work if video is blank – Silas S. Brown Jul 15 '17 at 18:54
  • My above comment is still not a complete solution, because on some videos Safari 10 sometimes renders the image 90-degrees rotated if you call drawImage too soon, even though "loadedmetadata" has fired. I'm afraid on browsers like this you'll just have to insert artificial delays and hope for the best. – Silas S. Brown Jul 15 '17 at 19:29