javascripthtml5-videodrawimage

Using drawImage with a video as a source appears to only copy one pixel?


I have a video that's streaming parts of a video into a MediaSource and playing it. I have a canvas that I want to draw the video on an then overlay some effects. However, in my handler, the call to drawImage, while it works, it only appears to copy a single pixel. The video itself plays fine.

I have a screenshot from my app, which has the source video next to the destination canvas element: video and canvas side-by-side

As you can sorta see, the solid color of the canvas is the color of the very first pixel of the source video.

The actual code in question looks similar to this:

  startWatch() {
    this.timer = setInterval(this.copyFrame.bind(this), 1000 / this.fps)
  }

  copyFrame() {
    const { width, height } = this.video.getBoundingClientRect()
    // I've tried the 3, 5, and 9 argument versions of this function
    // width is 300 and height is 168.75.
    this.ctx.drawImage(this.video, 0, 0, width, height, 0, 0, width, height)
  }

(where this.video is a video element)

This code does "work" in that the canvas's color does update over time as the video plays. But it's always just the initial pixel. So the timing loop works, but the drawImage call isn't doing what I expect.


Solution

  • (1) Using const { width, height } may not be correct. Check the values you are given.
    What happens if you console.log the width or height values, are they correct/expected numbers?

    (2) "...But the drawImage call isn't doing what I expect."

    ctx.drawImage( someElement, 0, 0, A1, A2, 0, 0, B1, B2 );
    

    Consider the above code,

    In your shown code, your const { width, height } is either

    Possible solutions:

    Try (if you must use bounding rectangle):

    function copyFrame() 
    {
        //# width is 300 and height is 168.75.
        //const { width, height } = this.video.getBoundingClientRect()
        
        let myRect = this.video.getBoundingClientRect();
        
        this.ctx.drawImage( this.video, 0, 0, 
                            this.video.videoWidth, this.video.videoHeight, 
                            0, 0, myRect.width, myRect.height);
    }
    

    Alternativey you could just draw the picture into canvas size:

    function copyFrame() 
    {
        //# assuming your Canvas is called "this.cnvs"
        this.ctx.drawImage( this.video, 0, 0, 
                            this.video.videoWidth, this.video.videoHeight, 
                            0, 0, this.cnvs.width, this.cnvs.height);
    }
    

    For aspect ratio you can use the below formula (swap all width & height references for a new drawn_width)

    myRatio = ( vid_height / vid_width ); 
    drawn_height = ( myCanvas_width * myRatio );