Igal
Igal

Reputation: 6103

Drawing video on canvas

I'm trying to draw a video from video element on canvas. I wrote the following JS code:

$(function () {
    var video = $("#video");
    video.bind("loadedmetadata", function () {
        var vw = this.videoWidth;
        var vh = this.videoHeight;
        var canvas = $('#canvas');
        canvas.width(vw).height(vh);
        var context = canvas.getContext("2d");

        video.addEventListener("play", function () { draw(video, context, vw, vh); }, false);
    });
});
function draw(video, context, vw, vh)
{
    if (video.paused || video.ended)
    {              
        return false;
    }

    context.drawImage(video, 0, 0, vw, vh);
    setTimeout(draw, 20, video, context, vw, vh);
}

This is my HTML:

<body>
<video id="video" autoplay controls>
    <source src="src1.avi" />
    <source src="src2.mov" />
    <source src="src3.ogg" />
    <source src="src4.avi" />
</video>
<canvas id="canvas">
    Please update your browser.
</canvas>

JSFiddle here

I can see the video playing, but it's not being redrawn onto the canvas. Where's my mistake?

Upvotes: 0

Views: 5293

Answers (1)

user1693593
user1693593

Reputation:

Using jQuery with elements such as video and canvas (and audio) is asking for trouble if you are not absolute sure what you're doing.

You are for example calling getContext(), videoWidth, videoHeight etc. on the jQuery objects which won't work well, and also mixing vanilla JavaScript with jQuery such as bind() and addEventListener().

My first recommendation is to use plain vanilla JavaScript when working with these elements as not only will life become less troublesome, but you'll also gain some performance which can be critical with these elements.

Second, always use requestAnimationFrame with video and canvas. There is no chance synching these properly with setTimeout/setInterval. rAF will make sure a frame is updated to monitor at the exact right time. With video you can toggle the speed as video rarely is above 30 FPS (in Europe it's 25 FPS), and it will give you some extra performance space.

Here is a working code (still need clean-up though...):

HTML - add preload = "auto" to the video tag to catch meta data before playing:

<video id="video" preload=auto autoplay controls>

JavaScript

var video = $("#video")[0];               // video element itself is at index 0 
var vw;
var vh;

var canvas = $('#canvas')[0];             // canvas element itself is at index 0
var context = canvas.getContext("2d");    // getContext on canvas element itself

// setup canvas when metadata is available
video.addEventListener("loadedmetadata", function() {
    vw = this.videoWidth || this.width;   // these are on video element itself
    vh = this.videoHeight || this.height;
    canvas.width = vw
    canvas.height = vh;
}, false);

// call it straight, use global (or parent) variables
video.addEventListener("play", draw, false);

function draw() {
    if (video.paused || video.ended) {
        return;
    }

    context.drawImage(video, 0, 0, vw, vh);

    requestAnimationFrame(draw);  // loop anim. using rAF
}

Upvotes: 6

Related Questions