MediaRecorder-canvas-media-source-legacy.https.html (5276B)
1 <!doctype html> 2 <html> 3 <meta name="timeout" content="long"> 4 5 <head> 6 <title>MediaRecorder canvas media source</title> 7 <meta name=variant content="?mimeType=''"> 8 <meta name=variant content="?mimeType=video/webm;codecs=vp8,opus"> 9 <meta name=variant content="?mimeType=video/webm;codecs=vp9,opus"> 10 <meta name=variant content="?mimeType=video/webm;codecs=av1,opus"> 11 <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2"> 12 <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2"> 13 <meta name=variant content="?mimeType=video/mp4;codecs=vp9,opus"> 14 <meta name=variant content="?mimeType=video/mp4;codecs=av01,opus"> 15 <meta name=variant content="?mimeType=video/mp4;codecs=av01,mp4a.40.2"> 16 <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus"> 17 <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus"> 18 <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2"> 19 <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2"> 20 <meta name=variant content="?mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus"> 21 <meta name=variant content="?mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus"> 22 <meta name=variant content="?mimeType=video/mp4"> 23 <link rel="help" 24 href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#dom-mediarecorder-mimeType"> 25 <script src="/resources/testharness.js"></script> 26 <script src="/resources/testharnessreport.js"></script> 27 <script src="/resources/testdriver.js"></script> 28 <script src="/resources/testdriver-vendor.js"></script> 29 <script src="../../mediacapture-streams/permission-helper.js"></script> 30 </head> 31 32 <body> 33 <canvas id="canvas"></canvas> 34 <script> 35 36 async_test(test => { 37 const CANVAS_WIDTH = 256; 38 const CANVAS_HEIGHT = 144; 39 40 let large_size_data_available = false; 41 42 // Empty video frames from this resolution consistently have ~750 bytes in my 43 // tests, while valid video frames usually contain 7-8KB. A threshold of 44 // 1.5KB consistently fails when video frames are empty but passes when video 45 // frames are non-empty. 46 const THRESHOLD_FOR_EMPTY_FRAMES = 1500; 47 48 const CAMERA_CONSTRAINTS = { 49 video: { 50 width: { ideal: CANVAS_WIDTH }, 51 height: { ideal: CANVAS_HEIGHT } 52 } 53 }; 54 55 function useUserMedia(constraints) { 56 let activeStream = null; 57 58 function startCamera() { 59 return navigator.mediaDevices.getUserMedia(constraints).then( 60 (stream) => { 61 activeStream = stream; 62 return stream; 63 } 64 ); 65 } 66 67 function stopCamera() { 68 activeStream?.getTracks().forEach((track) => track.stop()); 69 } 70 71 return { startCamera, stopCamera }; 72 } 73 74 function useMediaRecorder(stream, mimeType, frameSizeCallback) { 75 const mediaRecorder = new MediaRecorder( 76 stream, { mimeType } 77 ); 78 79 mediaRecorder.ondataavailable = event => { 80 const {size} = event.data; 81 frameSizeCallback(size); 82 83 if (mediaRecorder.state !== "inactive") { 84 mediaRecorder.stop(); 85 } 86 }; 87 88 mediaRecorder.onstop = event => { 89 assert_equals(large_size_data_available, true), 90 "onstop is called after valid data is available"; 91 }; 92 93 mediaRecorder.start(1000); 94 } 95 96 const params = new URLSearchParams(window.location.search); 97 const mimeType = params.get('mimeType'); 98 if (mimeType && !MediaRecorder.isTypeSupported(mimeType)) { 99 test.done(); 100 return; 101 } 102 103 if (!window.MediaStreamTrackProcessor) { 104 test.done(); 105 return; 106 } 107 108 const canvas = document.querySelector("canvas"); 109 const ctx = canvas.getContext("2d", { 110 alpha: false, 111 }); 112 113 canvas.width = CANVAS_WIDTH; 114 canvas.height = CANVAS_HEIGHT; 115 116 const {startCamera, stopCamera} = useUserMedia(CAMERA_CONSTRAINTS); 117 startCamera().then(async stream => { 118 const videoTrack = stream.getVideoTracks()[0]; 119 const { readable: readableStream } = new MediaStreamTrackProcessor({ 120 track: videoTrack 121 }); 122 123 const composedTrackGenerator = new MediaStreamTrackGenerator({ 124 kind: "video" 125 }); 126 const sink = composedTrackGenerator.writable; 127 128 ctx.fillStyle = "#333"; 129 ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); 130 131 const transformer = new TransformStream({ 132 async transform(cameraFrame, controller) { 133 if (cameraFrame && cameraFrame?.codedWidth > 0) { 134 const leftPos = (CANVAS_WIDTH - cameraFrame.displayWidth) / 2; 135 const topPos = (CANVAS_HEIGHT - cameraFrame.displayHeight) / 2; 136 137 ctx.drawImage(cameraFrame, leftPos, topPos); 138 139 const newFrame = new VideoFrame(canvas, { 140 timestamp: cameraFrame.timestamp 141 }); 142 cameraFrame.close(); 143 controller.enqueue(newFrame); 144 } 145 } 146 }); 147 148 readableStream.pipeThrough(transformer).pipeTo(sink); 149 150 const compositedMediaStream = new MediaStream([composedTrackGenerator]); 151 152 useMediaRecorder(compositedMediaStream, mimeType, (size => { 153 if (size > THRESHOLD_FOR_EMPTY_FRAMES) { 154 large_size_data_available = true; 155 stopCamera(); 156 test.done(); 157 } 158 })); 159 }); 160 }, "MediaRecorder returns frames containing video content"); 161 162 </script> 163 </body> 164 165 </html>