Search code examples
javascripthtmlvideocanvasresolution

Matching canvas resolution with the video resolution - superimposition


I have a canvas and a video defined in a div:

<div id="videos">
    <canvas id="my-canvas"></canvas>
    <video id="remote-video" autoplay></video>
</div>

Here is the css:

#my-canvas {
    display: block;
    height: 100%;
    max-height: 100%;
    max-width: 100%;
    object-fit: cover;
    /* no letterboxing */
    opacity: 1;
    z-index: 2;
    position: absolute;
    -moz-transform: rotateY(180deg);
    -ms-transform: rotateY(180deg);
    -o-transform: rotateY(180deg);
    -webkit-transform: rotateY(180deg);
    transform: rotateY(180deg);
    transition: opacity 1s;
    width: 100%;
}

#remote-video {
    display: block;
    height: 100%;
    max-height: 100%;
    max-width: 100%;
    object-fit: cover;
    /* no letterboxing */
    opacity: 0;
    -moz-transform: rotateY(180deg);
    -ms-transform: rotateY(180deg);
    -o-transform: rotateY(180deg);
    -webkit-transform: rotateY(180deg);
    transform: rotateY(180deg);
    transition: opacity 1s;
    width: 100%;
}

When I draw the canvas, the resolution doesn't seem to match the resolution of the video. Here is the javascript code:

var my_canvas = (function() {

var options;
var video;
var canvas;
var context;
var renderTimer;

function initVideoStream() {
    if (options.videoElement) {
        video = document.getElementById(options.videoElement);
        video.setAttribute('width', options.width);
        video.setAttribute('height', options.height);

        initCanvas();
    } else {
        options.onNotSupported();
    }
}

function initCanvas() {
    canvas = document.createElement("canvas");
    canvas.setAttribute('width', options.width);
    canvas.setAttribute('height', options.height);

    context = canvas.getContext('2d');

    startCapture();
}

function startCapture() {
    renderTimer = setInterval(function() {
        try {
            context.drawImage(video, 0, 0, video.width, video.height);
            options.onFrame(canvas);
        } catch (e) {
            // TODO
        }
    }, Math.round(1000 / options.fps));
}

function stopCapture() {
    if (renderTimer) {
        clearInterval(renderTimer);
    }

    options.onClear();
}

return {
    init: function(captureOptions) {
        var doNothing = function(){};

        options = captureOptions || {};

        options.fps = options.fps || 30;
        options.width = options.width || 640;
        options.height = options.height || 480;
        options.videoElement = options.videoElement || null;

        options.onSuccess = options.onSuccess || doNothing;
        options.onError = options.onError || doNothing;
        options.onNotSupported = options.onNotSupported || doNothing;
        options.onFrame = options.onFrame || doNothing;
        options.onClear = options.onClear || doNothing;

        initVideoStream();
    },

    start: startCapture,

    stop: stopCapture
};
})();

Note:

1) options.videoElement = document.getElementById('my-canvas');

2) I played with different values for options.width and height. It doesn't fix.

How to completely superimpose the canvas on the video?


Solution

  • This resizing code fixed the problem:

        function startCapture() {
            window.requestAnimFrame = (function() {
                return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( /* function */ callback, /* DOMElement */ element) {
                    window.setTimeout(callback, 1000 / options.fps);
                };
            })();
    
            animate();
        }
    
        function stopCapture() {
            requestAnimFrame = null;
            options.onClear();
        }
    
        function animate() {
            if (requestAnimFrame != null) {
                requestAnimFrame(animate);
                render();
            }
        }
    
        function render() {
            try {
                var video_width = video.offsetWidth;
                var video_height = video.offsetHeight;
                var ratio = video_width / video_height;
                var target_width;
                var target_height;
                var y_of_video = 0;
                var x_of_video = 0
                if (video_width > video_height) {
                    target_width = canvas.width;
                    target_height = canvas.width / ratio;
                    y_of_video = (canvas.height - target_height) / 2;
                } else {
                    target_width = canvas.height;
                    target_height = canvas.height * ratio;
                    x_of_video = (canvas.width - target_width) / 2;
                }
                context.drawImage(video, x_of_video, y_of_video, target_width, target_height);
                options.onFrame(canvas);
            } catch (e) {
                console.log("Failed to render canvas");
            }
        }
    

    Note: Answer edited based on 350D's suggestion.