Reputation: 3070
According to MDN:
The
HTMLMediaElement
interface adds toHTMLElement
the properties and methods needed to support basic media-related capabilities that are common to audio and video.
HTMLMediaElement.captureStream()
. It can be used with both <video>
and <canvas>
elements to capture their stream.
Conversely, one can add a video stream as srcObject
to a <video>
element, then it shows it. Is it possible for <canvas>
element too?
Is it possible to add a stream as source to an html <canvas>
element?
Upvotes: 13
Views: 14225
Reputation: 136986
No there is nothing in any of the Canvas APIs able to consume a MediaStream.
The canvas APIs work only with raw pixels, and contain no decoder of any sort. You must use either either javascript objects that are able to do this decode (e.g ImageBitmap), or HTMLElements.
So in the case of a MediaStream, currently the only object able to decode it's video content will be an HTMLVideoElement, that you'll be able to draw on your canvas easily.
The WebCodecs API has made great progress recently, and is becoming more and more mature that it's now worth being mentioned as a solution.
This API offers a new interface called VideoFrame which will soon be part of the CanvasImageSources type, meaning, we can use it directly with drawImage
, texImage2D
and everywhere such a CanvasImageSource can be used.
The MediaCapture Transform W3C group has developed a MediaStreamTrackProcessor that does return such VideoFrames from a video MediaStreamTrack.
So we now have a more direct way to render a MediaStream to a canvas, which currently only works in Chrome browser with the #enable-experimental-web-platform-features
flag on...
if( window.MediaStreamTrackProcessor ) {
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const track = getCanvasTrack(); // MediaStream.getVideoTracks()[0]
const processor = new MediaStreamTrackProcessor( track );
const reader = processor.readable.getReader();
readChunk();
function readChunk() {
reader.read().then( ({ done, value }) => {
// the MediaStream video can have dynamic size
if( canvas.width !== value.displayWidth || canvas.height !== value.displayHeight ) {
canvas.width = value.displayWidth;
canvas.height = value.displayHeight;
}
ctx.clearRect( 0, 0, canvas.width, canvas.height );
// value is a VideoFrame
ctx.drawImage( value, 0, 0 );
value.close(); // close the VideoFrame when we're done with it
if( !done ) {
readChunk();
}
});
}
}
else {
console.error("Your browser doesn't support this API yet");
}
// We can't use getUserMedia in StackSnippets
// So here we use a simple canvas as source
// for our MediaStream.
function getCanvasTrack() {
// just some noise...
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const img = new ImageData(300, 150);
const data = new Uint32Array(img.data.buffer);
const track = canvas.captureStream().getVideoTracks()[0];
anim();
return track;
function anim() {
for( let i=0; i<data.length;i++ ) {
data[i] = Math.random() * 0xFFFFFF + 0xFF000000;
}
ctx.putImageData(img, 0, 0);
if( track.readyState === "live" ) {
requestAnimationFrame(anim);
}
}
}
<canvas></canvas>
As a glitch project (source) using the camera as source.
Upvotes: 11
Reputation: 163438
@Kaiido is correct in that there isn't any way to do this directly. So, here's what you must do:
function onFrame() {
window.requestAnimationFrame(onFrame);
canvasContext.drawImage(video, 0, 0);
}
onFrame();
A couple gotchas you're going to run into:
captureStream
from this canvas as well, due to throttling policies, it isn't going to work if the tab doesn't have focus.captureStream
from the canvas as well.Upvotes: 4