MediaStreamTrack-independent-clones.https.html (8393B)
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=../mediacapture-streams/settings-helper.js></script> 10 <script src=../mediacapture-streams/video-test-helper.js></script> 11 <script> 12 "use strict" 13 14 setup(() => assert_implements("getDisplayMedia" in navigator.mediaDevices, "getDisplayMedia() not supported")); 15 16 // Note these gDM tests will fail if our own window is on a screen different 17 // than the system's first screen. They're functions in case the browser 18 // window needs to be moved to the first screen during the test in order to 19 // pass. 20 function screenPixelRatio() { return SpecialPowers.wrap(window).desktopToDeviceScale; } 21 function screenWidth() { return window.screen.width * window.devicePixelRatio; } 22 function screenHeight() { return window.screen.height * window.devicePixelRatio; } 23 function desktopWidth() { 24 // TODO: Bug 1965499 - scale down by screenPixelRatio by default in resizeMode: crop-and-scale. 25 // return screenWidth() / screenPixelRatio(); 26 return screenWidth(); 27 } 28 function desktopHeight() { 29 // TODO: Bug 1965499 - scale down by screenPixelRatio by default in resizeMode: crop-and-scale. 30 // return screenHeight() / screenPixelRatio(); 31 return screenHeight(); 32 } 33 34 // TODO: By default we shouldn't be multiplying with window.devicePixelRatio (bug 1703991). 35 function defaultScreen() { 36 return { 37 resizeMode: "crop-and-scale", 38 width: screenWidth(), 39 height: screenHeight(), 40 frameRate: 30, 41 }; 42 } 43 // TODO: Should get the source's real refresh rate for frameRate (bug 1984363). 44 function nativeScreen() { 45 return { 46 resizeMode: "none", 47 width: screenWidth(), 48 height: screenHeight(), 49 frameRate: 60 50 }; 51 } 52 53 [ 54 [ 55 [{resizeMode: "none", width: 100}, {resizeMode: "none", height: 500}], 56 [nativeScreen, nativeScreen], 57 ], 58 [ 59 [{resizeMode: "none", frameRate: 50}, {resizeMode: "none", frameRate: 1}], 60 [nativeScreen, nativeScreen], 61 ], 62 [ 63 [{resizeMode: "none"}, {resizeMode: "crop-and-scale"}], 64 [nativeScreen, defaultScreen], 65 ], 66 [ 67 [{resizeMode: "crop-and-scale"}, {resizeMode: "crop-and-scale", height: 100}], 68 [ 69 defaultScreen, 70 () => ({ 71 resizeMode: "crop-and-scale", 72 width: Math.round(screenWidth() / screenHeight() * 100), 73 height: 100, 74 frameRate: 30 75 }), 76 ], 77 ], 78 [ 79 [{resizeMode: "crop-and-scale", height: 100}, {resizeMode: "crop-and-scale"}], 80 [ 81 () => ({ 82 resizeMode: "crop-and-scale", 83 width: Math.round(screenWidth() / screenHeight() * 100), 84 height: 100, 85 frameRate: 30 86 }), 87 defaultScreen, 88 ], 89 ], 90 [ 91 [{resizeMode: "crop-and-scale", frameRate: 5}, {resizeMode: "crop-and-scale", frameRate: 50}], 92 [ 93 () => { 94 const { width, height } = defaultScreen(); 95 return { width, height, frameRate: 5}; 96 }, 97 () => { 98 const { width, height } = defaultScreen(); 99 return { width, height, frameRate: 50}; 100 }, 101 ], 102 [[2, 7], [2, 50]], 103 ], 104 [ 105 [{resizeMode: "crop-and-scale", frameRate: 50}, {resizeMode: "crop-and-scale", frameRate: 5}], 106 [ 107 () => { 108 const { width, height } = defaultScreen(); 109 return { width, height, frameRate: 50}; 110 }, 111 () => { 112 const { width, height } = defaultScreen(); 113 return { width, height, frameRate: 5}; 114 }, 115 [[2, 50], [2, 7]] 116 ], 117 ], 118 [ 119 [{resizeMode: "none"}, {resizeMode: "crop-and-scale", frameRate: 5000}, {}], 120 [ 121 nativeScreen, 122 () => { 123 const { width, height } = defaultScreen(); 124 return { width, height, frameRate: 120}; 125 }, 126 ] 127 ], 128 ].forEach( 129 ([ 130 [video, cloneVideo], 131 [expectedFunc, cloneExpectedFunc], 132 [testFramerate, cloneTestFramerate] = [null, null] 133 ]) => { 134 let expected, cloneExpected; 135 promise_test(async t => { 136 expected = expectedFunc(); 137 cloneExpected = cloneExpectedFunc(); 138 await test_driver.bless('getDisplayMedia()'); 139 const stream = await navigator.mediaDevices.getDisplayMedia({video}); 140 const [track] = stream.getTracks(); 141 const clone = track.clone(); 142 t.add_cleanup(() => { 143 track.stop(); 144 clone.stop(); 145 }); 146 let settings = track.getSettings(); 147 let cloneSettings = clone.getSettings(); 148 for (const key of Object.keys(expected)) { 149 assert_equals(settings[key], expected[key], `original: ${key}`); 150 assert_equals(cloneSettings[key], expected[key], `clone: ${key}`); 151 } 152 await clone.applyConstraints(cloneVideo); 153 settings = track.getSettings(); 154 cloneSettings = clone.getSettings(); 155 for (const key of Object.keys(expected)) { 156 assert_equals(settings[key], expected[key], `original-post: ${key}`); 157 } 158 for (const key of Object.keys(cloneExpected)) { 159 assert_equals(cloneSettings[key], cloneExpected[key], `clone-post: ${key}`); 160 } 161 await test_resolution_equals(t, track, settings.width, settings.height); 162 await test_resolution_equals(t, clone, cloneSettings.width, cloneSettings.height); 163 if (testFramerate) { 164 const [low, high] = testFramerate; 165 await test_framerate_between_exclusive(t, track, low, high); 166 } 167 if (cloneTestFramerate) { 168 const [low, high] = cloneTestFramerate; 169 await test_framerate_between_exclusive(t, clone, low, high); 170 } 171 }, `gDM gets expected modes by ${JSON.stringify(video)} + (cloned) ${JSON.stringify(cloneVideo)}`); 172 }); 173 174 promise_test(async t => { 175 await test_driver.bless('getDisplayMedia()'); 176 const stream = await navigator.mediaDevices.getDisplayMedia({video: {resizeMode: "none"}}); 177 const [track] = stream.getTracks(); 178 const clone = track.clone(); 179 t.add_cleanup(() => { 180 track.stop(); 181 clone.stop(); 182 }); 183 await clone.applyConstraints( 184 { 185 resizeMode: "crop-and-scale", 186 width: 400, 187 height: 400 188 } 189 ); 190 const expected = findFittestResolutionSetting( 191 screenWidth(), 192 screenHeight(), 193 clone.getConstraints() 194 ); 195 assert_equals(clone.getSettings().width, expected.width, "width"); 196 assert_equals(clone.getSettings().height, expected.height, "height"); 197 await test_resolution_equals(t, clone, clone.getSettings().width, clone.getSettings().height); 198 assert_approx_equals( 199 clone.getSettings().width / clone.getSettings().height, 200 desktopWidth() / desktopHeight(), 201 0.01, 202 "aspect ratio" 203 ); 204 assert_equals(clone.getSettings().resizeMode, "crop-and-scale", "resizeMode"); 205 }, "applyConstraints on gDM clone doesn't crop with only ideal dimensions"); 206 207 promise_test(async t => { 208 await test_driver.bless('getDisplayMedia()'); 209 const stream = await navigator.mediaDevices.getDisplayMedia({video: {resizeMode: "none"}}); 210 const [track] = stream.getTracks(); 211 const clone = track.clone(); 212 t.add_cleanup(() => { 213 track.stop(); 214 clone.stop(); 215 }); 216 await clone.applyConstraints( 217 { 218 resizeMode: "crop-and-scale", 219 width: {max: 400}, 220 height: {ideal: 400} 221 } 222 ); 223 assert_equals(clone.getSettings().width, 400, "width"); 224 assert_equals( 225 clone.getSettings().height, 226 Math.round(screenHeight() / screenWidth() * 400), 227 "height" 228 ); 229 await test_resolution_equals(t, clone, clone.getSettings().width, clone.getSettings().height); 230 assert_equals(clone.getSettings().resizeMode, "crop-and-scale", "resizeMode"); 231 }, "applyConstraints on gDM clone doesn't crop with ideal and max dimensions"); 232 </script>