MediaStreamTrack-independent-clones.https.html (8182B)
1 <!doctype html> 2 <title>MediaStreamTrack clones have independent settings. Assumes Mozilla's fake camera source with 480p and 720p capabilities.</title> 3 <meta name="timeout" content="long"> 4 <p class="instructions">When prompted, accept to share your video stream.</p> 5 <script src=/resources/testharness.js></script> 6 <script src=/resources/testharnessreport.js></script> 7 <script src=/resources/testdriver.js></script> 8 <script src=/resources/testdriver-vendor.js></script> 9 <script src=video-test-helper.js></script> 10 <script> 11 "use strict" 12 13 // Native capabilities supported by the fake camera. 14 const nativeLow = {width: 640, height: 480, frameRate: 30, resizeMode: "none"}; 15 const nativeHigh = {width: 1280, height: 720, frameRate: 10, resizeMode: "none"}; 16 17 [ 18 [ 19 [{resizeMode: "none", width: 1000}, {resizeMode: "none", width: 500}], 20 [nativeHigh, nativeLow], 21 ], 22 [ 23 [{resizeMode: "none", height: 500}, {resizeMode: "none", width: 1000}], 24 [nativeLow, nativeHigh], 25 ], 26 [ 27 [{resizeMode: "none", width: 500, height: 500}, {resizeMode: "crop-and-scale", width: 1000, height: 750}], 28 [nativeLow, {resizeMode: "crop-and-scale", width: 1000, height: 720, frameRate: 10}], 29 ], 30 [ 31 [{resizeMode: "crop-and-scale", width: 800, height: 600}, {resizeMode: "none"}], 32 [{resizeMode: "crop-and-scale", width: 800, height: 600, frameRate: 10}, nativeLow], 33 ], 34 [ 35 [{resizeMode: "crop-and-scale", height: 500}, {resizeMode: "crop-and-scale", height: 300}], 36 [ 37 {resizeMode: "crop-and-scale", width: 889, height: 500, frameRate: 10}, 38 {resizeMode: "crop-and-scale", width: 400, height: 300, frameRate: 30} 39 ], 40 ], 41 [ 42 [ 43 {resizeMode: "crop-and-scale", frameRate: {exact: 5}}, 44 {resizeMode: "crop-and-scale", frameRate: {exact: 1}}, 45 ], 46 [ 47 {resizeMode: "crop-and-scale", width: 640, height: 480, frameRate: 5}, 48 {resizeMode: "crop-and-scale", width: 640, height: 480, frameRate: 1}, 49 ], 50 [[2, 7], [0.5, 3]], 51 ], 52 ].forEach( 53 ([ 54 [video, cloneVideo], 55 [expected, cloneExpected], 56 [testFramerate, cloneTestFramerate] = [null, null] 57 ]) => promise_test(async t => { 58 const stream = await navigator.mediaDevices.getUserMedia({video}); 59 const [track] = stream.getTracks(); 60 const clone = track.clone(); 61 t.add_cleanup(() => { 62 track.stop(); 63 clone.stop(); 64 }); 65 let settings = track.getSettings(); 66 let cloneSettings = clone.getSettings(); 67 for (const key of Object.keys(expected)) { 68 assert_equals(settings[key], expected[key], `original: ${key}`); 69 assert_equals(cloneSettings[key], expected[key], `clone: ${key}`); 70 } 71 await clone.applyConstraints(cloneVideo); 72 settings = track.getSettings(); 73 cloneSettings = clone.getSettings(); 74 for (const key of Object.keys(expected)) { 75 assert_equals(settings[key], expected[key], `original-post: ${key}`); 76 } 77 for (const key of Object.keys(cloneExpected)) { 78 assert_equals(cloneSettings[key], cloneExpected[key], `clone-post: ${key}`); 79 } 80 await test_resolution_equals(t, track, settings.width, settings.height); 81 await test_resolution_equals(t, clone, cloneSettings.width, cloneSettings.height); 82 if (testFramerate) { 83 const [low, high] = testFramerate; 84 await test_framerate_between_exclusive(t, track, low, high); 85 } 86 if (cloneTestFramerate) { 87 const [low, high] = cloneTestFramerate; 88 await test_framerate_between_exclusive(t, clone, low, high); 89 } 90 }, `gUM gets ${JSON.stringify(expected)} + clone ` + 91 `${JSON.stringify(cloneExpected)} by ${JSON.stringify(video)} ` + 92 `and ${JSON.stringify(cloneVideo)}`)); 93 94 [ 95 [ 96 [{echoCancellation: true}, {echoCancellation: false, noiseSuppression: true, autoGainControl: true}], 97 [ 98 {echoCancellation: true, noiseSuppression: true, autoGainControl: true}, 99 {echoCancellation: false, noiseSuppression: true, autoGainControl: true}, 100 ], 101 ], 102 [ 103 [{echoCancellation: false, noiseSuppression: false, autoGainControl: false}, {}], 104 [ 105 {echoCancellation: false, noiseSuppression: false, autoGainControl: false}, 106 {echoCancellation: true, noiseSuppression: true, autoGainControl: true}, 107 ], 108 ], 109 ].forEach( 110 ([ 111 [audio, cloneAudio], 112 [expected, cloneExpected], 113 ]) => promise_test(async t => { 114 const stream = await navigator.mediaDevices.getUserMedia({audio}); 115 const [track] = stream.getTracks(); 116 const clone = track.clone(); 117 t.add_cleanup(() => { 118 track.stop(); 119 clone.stop(); 120 }); 121 let settings = track.getSettings(); 122 let cloneSettings = clone.getSettings(); 123 for (const key of Object.keys(expected)) { 124 assert_equals(settings[key], expected[key], `original: ${key}`); 125 assert_equals(cloneSettings[key], expected[key], `clone: ${key}`); 126 } 127 await clone.applyConstraints(cloneAudio); 128 settings = track.getSettings(); 129 cloneSettings = clone.getSettings(); 130 for (const key of Object.keys(expected)) { 131 assert_equals(settings[key], expected[key], `original-post: ${key}`); 132 } 133 for (const key of Object.keys(cloneExpected)) { 134 assert_equals(cloneSettings[key], cloneExpected[key], `clone-post: ${key}`); 135 } 136 }, `gUM gets ${JSON.stringify(expected)} + clone ` + 137 `${JSON.stringify(cloneExpected)} by ${JSON.stringify(audio)} ` + 138 `and ${JSON.stringify(cloneAudio)}`)); 139 140 141 142 promise_test(async t => { 143 const stream = await navigator.mediaDevices.getUserMedia({video: true}); 144 const [track] = stream.getTracks(); 145 const clone = track.clone(); 146 t.add_cleanup(() => { 147 track.stop(); 148 clone.stop(); 149 }); 150 try { 151 await clone.applyConstraints({resizeMode: "none", width: {min: 2000}}) 152 } catch(e) { 153 assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`); 154 return; 155 } 156 assert_unreached("applyConstraints is rejected with impossible width"); 157 }, "applyConstraints on gUM clone is rejected by resizeMode none and impossible min-width"); 158 159 promise_test(async t => { 160 const stream = await navigator.mediaDevices.getUserMedia({video: true}); 161 const [track] = stream.getTracks(); 162 const clone = track.clone(); 163 t.add_cleanup(() => { 164 track.stop(); 165 clone.stop(); 166 }); 167 try { 168 await clone.applyConstraints({resizeMode: "none", width: {max: 200}}) 169 } catch(e) { 170 assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`); 171 return; 172 } 173 assert_unreached("applyConstraints is rejected with impossible width"); 174 }, "applyConstraints on gUM clone is rejected by resizeMode none and impossible max-width"); 175 176 promise_test(async t => { 177 const stream = await navigator.mediaDevices.getUserMedia({video: true}); 178 const [track] = stream.getTracks(); 179 const clone = track.clone(); 180 t.add_cleanup(() => { 181 track.stop(); 182 clone.stop(); 183 }); 184 try { 185 await clone.applyConstraints({resizeMode: "crop-and-scale", width: {min: 2000}}) 186 } catch(e) { 187 assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`); 188 return; 189 } 190 assert_unreached("applyConstraints is rejected with impossible width"); 191 }, "applyConstraints on gUM clone is rejected by resizeMode crop-and-scale and impossible width"); 192 193 promise_test(async t => { 194 const stream = await navigator.mediaDevices.getUserMedia({video: true}); 195 const [track] = stream.getTracks(); 196 const clone = track.clone(); 197 t.add_cleanup(() => { 198 track.stop(); 199 clone.stop(); 200 }); 201 try { 202 await clone.applyConstraints({resizeMode: "crop-and-scale", frameRate: {min: 50}}); 203 } catch(e) { 204 assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`); 205 return; 206 } 207 assert_unreached("applyConstraints is rejected with impossible fps"); 208 }, "applyConstraints on gUM clone is rejected by resizeMode crop-and-scale impossible fps"); 209 </script>