setParameters-encodings.https.html (19258B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>RTCPeerConnection Simulcast Tests - setParameters/encodings</title> 4 <meta name="timeout" content="long"> 5 <script src="../third_party/sdp/sdp.js"></script> 6 <script src="simulcast.js"></script> 7 <script src="../RTCPeerConnection-helper.js"></script> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="/resources/testdriver.js"></script> 11 <script src="/resources/testdriver-vendor.js"></script> 12 <script src="../../mediacapture-streams/permission-helper.js"></script> 13 <script> 14 15 promise_test(async t => { 16 const pc1 = new RTCPeerConnection(); 17 t.add_cleanup(() => pc1.close()); 18 const pc2 = new RTCPeerConnection(); 19 t.add_cleanup(() => pc2.close()); 20 21 const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 22 23 await doOfferToSendSimulcast(pc1, pc2); 24 25 await pc2.setLocalDescription(); 26 const simulcastAnswer = midToRid(pc2.localDescription, pc1.localDescription, ["foo"]); 27 28 const parameters = sender.getParameters(); 29 parameters.encodings[1].scaleResolutionDownBy = 3.3; 30 const answerDone = pc1.setRemoteDescription({type: "answer", sdp: simulcastAnswer}); 31 await sender.setParameters(parameters); 32 await answerDone; 33 34 assert_equals(pc1.getTransceivers().length, 1); 35 const {encodings} = sender.getParameters(); 36 const rids = encodings.map(({rid}) => rid); 37 assert_array_equals(rids, ["foo"]); 38 }, 'sRD(simulcast answer) can narrow the simulcast envelope when interrupted by a setParameters'); 39 40 promise_test(async t => { 41 const pc1 = new RTCPeerConnection(); 42 t.add_cleanup(() => pc1.close()); 43 const pc2 = new RTCPeerConnection(); 44 t.add_cleanup(() => pc2.close()); 45 46 const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 47 48 await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]); 49 50 assert_equals(pc1.getTransceivers().length, 1); 51 let encodings = sender.getParameters().encodings; 52 let rids = encodings.map(({rid}) => rid); 53 assert_array_equals(rids, ["foo", "bar"]); 54 55 const reoffer = await pc2.createOffer(); 56 const simulcastSdp = midToRid(reoffer, pc1.localDescription, ["foo"]); 57 58 const parameters = sender.getParameters(); 59 parameters.encodings[1].scaleResolutionDownBy = 3.3; 60 const reofferDone = pc1.setRemoteDescription({type: "offer", sdp: simulcastSdp}); 61 await sender.setParameters(parameters); 62 await reofferDone; 63 await pc1.setLocalDescription(); 64 65 encodings = sender.getParameters().encodings; 66 rids = encodings.map(({rid}) => rid); 67 assert_array_equals(rids, ["foo"]); 68 }, 'sRD(simulcast offer) can narrow the simulcast envelope when interrupted by a setParameters'); 69 70 promise_test(async t => { 71 const pc1 = new RTCPeerConnection(); 72 t.add_cleanup(() => pc1.close()); 73 const pc2 = new RTCPeerConnection(); 74 t.add_cleanup(() => pc2.close()); 75 76 const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 77 78 const parameters = sender.getParameters(); 79 parameters.encodings[0].scaleResolutionDownBy = 2.3; 80 parameters.encodings[1].scaleResolutionDownBy = 3.3; 81 await sender.setParameters(parameters); 82 83 await doOfferToSendSimulcast(pc1, pc2); 84 await doAnswerToRecvSimulcast(pc1, pc2, []); 85 86 assert_equals(pc1.getTransceivers().length, 1); 87 const encodings = sender.getParameters().encodings; 88 const rids = encodings.map(({rid}) => rid); 89 assert_array_equals(rids, ["foo"]); 90 assert_equals(encodings[0].scaleResolutionDownBy, 2.3); 91 }, 'a simulcast setParameters followed by a sRD(unicast answer) results in keeping the first encoding'); 92 93 promise_test(async t => { 94 const pc1 = new RTCPeerConnection(); 95 t.add_cleanup(() => pc1.close()); 96 const pc2 = new RTCPeerConnection(); 97 t.add_cleanup(() => pc2.close()); 98 99 const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 100 await doOfferToSendSimulcast(pc1, pc2); 101 102 await pc2.setLocalDescription(); 103 const unicastAnswer = midToRid(pc2.localDescription, pc1.localDescription, []); 104 105 const parameters = sender.getParameters(); 106 parameters.encodings[0].scaleResolutionDownBy = 2.3; 107 parameters.encodings[1].scaleResolutionDownBy = 3.3; 108 const answerDone = pc1.setRemoteDescription({type: "answer", sdp: unicastAnswer}); 109 await sender.setParameters(parameters); 110 await answerDone; 111 112 assert_equals(pc1.getTransceivers().length, 1); 113 const encodings = sender.getParameters().encodings; 114 const rids = encodings.map(({rid}) => rid); 115 assert_array_equals(rids, ["foo"]); 116 assert_equals(encodings[0].scaleResolutionDownBy, 2.3); 117 }, 'sRD(unicast answer) interrupted by setParameters(simulcast) results in keeping the first encoding'); 118 119 promise_test(async t => { 120 const pc1 = new RTCPeerConnection(); 121 t.add_cleanup(() => pc1.close()); 122 const pc2 = new RTCPeerConnection(); 123 t.add_cleanup(() => pc2.close()); 124 125 const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 126 127 await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]); 128 assert_equals(pc1.getTransceivers().length, 1); 129 let encodings = sender.getParameters().encodings; 130 let rids = encodings.map(({rid}) => rid); 131 assert_array_equals(rids, ["foo", "bar"]); 132 133 const reoffer = await pc2.createOffer(); 134 const unicastSdp = midToRid(reoffer, pc1.localDescription, []); 135 const parameters = sender.getParameters(); 136 parameters.encodings[0].scaleResolutionDownBy = 2.3; 137 parameters.encodings[1].scaleResolutionDownBy = 3.3; 138 const reofferDone = pc1.setRemoteDescription({type: "offer", sdp: unicastSdp}); 139 await sender.setParameters(parameters); 140 await reofferDone; 141 await pc1.setLocalDescription(); 142 143 encodings = sender.getParameters().encodings; 144 rids = encodings.map(({rid}) => rid); 145 assert_array_equals(rids, ["foo"]); 146 assert_equals(encodings[0].scaleResolutionDownBy, 2.3); 147 }, 'sRD(unicast reoffer) interrupted by setParameters(simulcast) results in keeping the first encoding'); 148 149 promise_test(async t => { 150 const pc1 = new RTCPeerConnection(); 151 t.add_cleanup(() => pc1.close()); 152 const pc2 = new RTCPeerConnection(); 153 t.add_cleanup(() => pc2.close()); 154 155 const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 156 157 await doOfferToSendSimulcast(pc1, pc2); 158 await pc2.setLocalDescription(); 159 const simulcastAnswer = midToRid(pc2.localDescription, pc1.localDescription, ["foo"]); 160 const parameters = sender.getParameters(); 161 parameters.encodings[0].scaleResolutionDownBy = 3.3; 162 const answerDone = pc1.setRemoteDescription({type: "answer", sdp: simulcastAnswer}); 163 await sender.setParameters(parameters); 164 await answerDone; 165 166 const {encodings} = sender.getParameters(); 167 assert_equals(encodings.length, 1); 168 assert_equals(encodings[0].scaleResolutionDownBy, 3.3); 169 }, 'sRD(simulcast answer) interrupted by a setParameters does not result in losing modifications from the setParameters to the encodings that remain'); 170 171 const simulcastOffer = `v=0 172 o=- 3840232462471583827 0 IN IP4 127.0.0.1 173 s=- 174 t=0 0 175 a=group:BUNDLE 0 176 a=msid-semantic: WMS 177 m=video 9 UDP/TLS/RTP/SAVPF 96 178 c=IN IP4 0.0.0.0 179 a=rtcp:9 IN IP4 0.0.0.0 180 a=ice-ufrag:Li6+ 181 a=ice-pwd:3C05CTZBRQVmGCAq7hVasHlT 182 a=ice-options:trickle 183 a=fingerprint:sha-256 5B:D3:8E:66:0E:7D:D3:F3:8E:E6:80:28:19:FC:55:AD:58:5D:B9:3D:A8:DE:45:4A:E7:87:02:F8:3C:0B:3B:B3 184 a=setup:actpass 185 a=mid:0 186 a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid 187 a=extmap:2 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id 188 a=recvonly 189 a=rtcp-mux 190 a=rtpmap:96 VP8/90000 191 a=rtcp-fb:96 goog-remb 192 a=rtcp-fb:96 transport-cc 193 a=rtcp-fb:96 ccm fir 194 a=rid:foo recv 195 a=rid:bar recv 196 a=simulcast:recv foo;bar 197 `; 198 199 promise_test(async t => { 200 const pc1 = new RTCPeerConnection(); 201 t.add_cleanup(() => pc1.close()); 202 203 const stream = await getNoiseStream({video: true}); 204 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 205 const sender = pc1.addTrack(stream.getTracks()[0]); 206 const parameters = sender.getParameters(); 207 parameters.encodings[0].scaleResolutionDownBy = 3.0; 208 await sender.setParameters(parameters); 209 210 await pc1.setRemoteDescription({type: "offer", sdp: simulcastOffer}); 211 212 const {encodings} = sender.getParameters(); 213 const rids = encodings.map(({rid}) => rid); 214 assert_array_equals(rids, ["foo", "bar"]); 215 assert_equals(encodings[0].scaleResolutionDownBy, 2.0); 216 assert_equals(encodings[1].scaleResolutionDownBy, 1.0); 217 }, 'addTrack, then a unicast setParameters, then sRD(simulcast offer) results in simulcast without the settings from setParameters'); 218 219 promise_test(async t => { 220 const pc1 = new RTCPeerConnection(); 221 t.add_cleanup(() => pc1.close()); 222 223 const stream = await getNoiseStream({video: true}); 224 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 225 const sender = pc1.addTrack(stream.getTracks()[0]); 226 const parameters = sender.getParameters(); 227 parameters.encodings[0].scaleResolutionDownBy = 3.0; 228 229 const offerDone = pc1.setRemoteDescription({type: "offer", sdp: simulcastOffer}); 230 await sender.setParameters(parameters); 231 await offerDone; 232 233 const {encodings} = sender.getParameters(); 234 const rids = encodings.map(({rid}) => rid); 235 assert_array_equals(rids, ["foo", "bar"]); 236 assert_equals(encodings[0].scaleResolutionDownBy, 2.0); 237 assert_equals(encodings[1].scaleResolutionDownBy, 1.0); 238 }, 'addTrack, then sRD(simulcast offer) interrupted by a unicast setParameters results in simulcast without the settings from setParameters'); 239 240 promise_test(async t => { 241 const pc1 = new RTCPeerConnection(); 242 const pc2 = new RTCPeerConnection(); 243 t.add_cleanup(() => pc1.close()); 244 t.add_cleanup(() => pc2.close()); 245 246 const stream = await getNoiseStream({video: true}); 247 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 248 const {sender} = pc1.addTransceiver(stream.getTracks()[0], {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 249 await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]); 250 pc2.getTransceivers()[0].direction = "sendrecv"; 251 pc2.getTransceivers()[1].direction = "sendrecv"; 252 253 await doOfferToRecvSimulcast(pc2, pc1, []); 254 // Race simulcast setParameters against sLD(unicast reanswer) 255 const answer = await pc1.createAnswer(); 256 const aTask = queueAWebrtcTask(); 257 // This also queues a task to clear [[LastReturnedParameters]] 258 const parameters = sender.getParameters(); 259 // This might or might not queue a task right away (it might do some 260 // microtask stuff first), but it doesn't really matter. 261 const sLDDone = pc1.setLocalDescription(answer); 262 await aTask; 263 // Task queue should now have the task that clears 264 // [[LastReturnedParameters]], _then_ the success task for sLD. 265 // setParameters should succeed because [[LastReturnedParameters]] has not 266 // yet been cleared, and the steps in the success task for sLD have not run 267 // either. 268 await sender.setParameters(parameters); 269 await sLDDone; 270 271 assert_equals(pc1.getTransceivers().length, 1); 272 const {encodings} = sender.getParameters(); 273 const rids = encodings.map(({rid}) => rid); 274 assert_array_equals(rids, ["foo"]); 275 }, 'getParameters, then sLD(unicast answer) interrupted by a simulcast setParameters results in unicast'); 276 277 promise_test(async t => { 278 const pc1 = new RTCPeerConnection(); 279 const pc2 = new RTCPeerConnection(); 280 t.add_cleanup(() => pc1.close()); 281 t.add_cleanup(() => pc2.close()); 282 283 const stream = await getNoiseStream({video: true}); 284 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 285 const {sender} = pc1.addTransceiver(stream.getTracks()[0], {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 286 await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]); 287 pc2.getTransceivers()[0].direction = "sendrecv"; 288 pc2.getTransceivers()[1].direction = "sendrecv"; 289 290 await doOfferToRecvSimulcast(pc2, pc1, []); 291 const answer = await pc1.createAnswer(); 292 293 // The timing on this is very difficult. We want to ensure that our 294 // getParameters call happens after the initial steps in sLD, but 295 // before the queued task that sLD runs when it completes. 296 const aTask = queueAWebrtcTask(); 297 const sLDDone = pc1.setLocalDescription(answer); 298 // We now have a queued task (aTask). We might also have the success task for 299 // sLD, but maybe not. Allowing aTask to finish gives us our best chance that 300 // the success task for sLD is queued, but not run yet. 301 await aTask; 302 const parameters = sender.getParameters(); 303 // Hopefully we now have the success task for sLD, followed by the 304 // success task for getParameters. 305 await sLDDone; 306 // Success task for getParameters should not have run yet. 307 await promise_rejects_dom(t, 'InvalidStateError', sender.setParameters(parameters)); 308 },'Success task for setLocalDescription(answer) clears [[LastReturnedParameters]]'); 309 310 promise_test(async t => { 311 const pc1 = new RTCPeerConnection(); 312 const pc2 = new RTCPeerConnection(); 313 t.add_cleanup(() => pc1.close()); 314 t.add_cleanup(() => pc2.close()); 315 316 const stream = await getNoiseStream({video: true}); 317 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 318 const {sender} = pc1.addTransceiver(stream.getTracks()[0], {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 319 await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]); 320 pc2.getTransceivers()[0].direction = "sendrecv"; 321 pc2.getTransceivers()[1].direction = "sendrecv"; 322 323 await pc2.setLocalDescription(); 324 const simulcastOffer = midToRid( 325 pc2.localDescription, 326 pc1.localDescription, 327 [] 328 ); 329 330 // The timing on this is very difficult. We need to ensure that our 331 // getParameters call happens after the initial steps in sRD, but 332 // before the queued task that sRD runs when it completes. 333 const aTask = queueAWebrtcTask(); 334 const sRDDone = pc1.setRemoteDescription({ type: "offer", sdp: simulcastOffer }); 335 336 await aTask; 337 const parameters = sender.getParameters(); 338 await sRDDone; 339 await promise_rejects_dom(t, 'InvalidStateError', sender.setParameters(parameters)); 340 },'Success task for setRemoteDescription(offer) clears [[LastReturnedParameters]]'); 341 342 promise_test(async t => { 343 const pc1 = new RTCPeerConnection(); 344 const pc2 = new RTCPeerConnection(); 345 t.add_cleanup(() => pc1.close()); 346 t.add_cleanup(() => pc2.close()); 347 348 const stream = await getNoiseStream({video: true}); 349 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 350 const {sender} = pc1.addTransceiver(stream.getTracks()[0], {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 351 await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]); 352 pc2.getTransceivers()[0].direction = "sendrecv"; 353 pc2.getTransceivers()[1].direction = "sendrecv"; 354 355 await doOfferToSendSimulcast(pc1, pc2); 356 await pc2.setLocalDescription(); 357 const simulcastAnswer = midToRid( 358 pc2.localDescription, 359 pc1.localDescription, 360 [] 361 ); 362 363 // The timing on this is very difficult. We need to ensure that our 364 // getParameters call happens after the initial steps in sRD, but 365 // before the queued task that sRD runs when it completes. 366 const aTask = queueAWebrtcTask(); 367 const sRDDone = pc1.setRemoteDescription({ type: "answer", sdp: simulcastAnswer }); 368 await aTask; 369 370 const parameters = sender.getParameters(); 371 await sRDDone; 372 await promise_rejects_dom(t, 'InvalidStateError', sender.setParameters(parameters)); 373 },'Success task for setRemoteDescription(answer) clears [[LastReturnedParameters]]'); 374 375 promise_test(async t => { 376 const pc1 = new RTCPeerConnection(); 377 t.add_cleanup(() => pc1.close()); 378 const pc2 = new RTCPeerConnection(); 379 t.add_cleanup(() => pc2.close()); 380 381 const stream = await getNoiseStream({video: true}); 382 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 383 const sender = pc1.addTrack(stream.getTracks()[0]); 384 pc2.addTrack(stream.getTracks()[0]); 385 386 await doOfferToRecvSimulcast(pc2, pc1, ["foo", "bar"]); 387 let parameters = sender.getParameters(); 388 let rids = parameters.encodings.map(({rid}) => rid); 389 assert_array_equals(rids, ["foo", "bar"]); 390 parameters.encodings[0].scaleResolutionDownBy = 3; 391 parameters.encodings[1].scaleResolutionDownBy = 5; 392 await sender.setParameters(parameters); 393 394 await pc1.setRemoteDescription({sdp: "", type: "rollback"}); 395 396 parameters = sender.getParameters(); 397 rids = parameters.encodings.map(({rid}) => rid); 398 assert_array_equals(rids, [undefined]); 399 assert_equals(parameters.encodings[0].scaleResolutionDownBy, 1); 400 }, 'addTrack, then rollback of sRD(simulcast offer), brings us back to having a single encoding without any previously set parameters'); 401 402 promise_test(async t => { 403 const pc1 = new RTCPeerConnection(); 404 t.add_cleanup(() => pc1.close()); 405 const pc2 = new RTCPeerConnection(); 406 t.add_cleanup(() => pc2.close()); 407 408 const stream = await getNoiseStream({video: true}); 409 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 410 const {sender} = pc1.addTransceiver(stream.getTracks()[0], {sendEncodings: [{rid: "foo"}, {rid: "bar"}]}); 411 412 await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]); 413 let parameters = sender.getParameters(); 414 let rids = parameters.encodings.map(({rid}) => rid); 415 assert_array_equals(rids, ["foo", "bar"]); 416 parameters.encodings[0].scaleResolutionDownBy = 3; 417 parameters.encodings[1].scaleResolutionDownBy = 5; 418 await sender.setParameters(parameters); 419 420 await doOfferToRecvSimulcast(pc2, pc1, []); 421 parameters = sender.getParameters(); 422 rids = parameters.encodings.map(({rid}) => rid); 423 assert_array_equals(rids, ["foo", "bar"]); 424 425 await pc1.setRemoteDescription({sdp: "", type: "rollback"}); 426 parameters = sender.getParameters(); 427 rids = parameters.encodings.map(({rid}) => rid); 428 assert_array_equals(rids, ["foo", "bar"]); 429 assert_equals(parameters.encodings[0].scaleResolutionDownBy, 3); 430 assert_equals(parameters.encodings[1].scaleResolutionDownBy, 5); 431 }, 'rollback of a remote offer that disabled a previously negotiated simulcast should restore simulcast along with any previously set parameters'); 432 433 promise_test(async t => { 434 const pc1 = new RTCPeerConnection(); 435 t.add_cleanup(() => pc1.close()); 436 const pc2 = new RTCPeerConnection(); 437 t.add_cleanup(() => pc2.close()); 438 439 const stream = await getNoiseStream({video: true}); 440 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 441 const sender = pc1.addTrack(stream.getTracks()[0]); 442 pc2.addTrack(stream.getTracks()[0]); 443 444 await doOfferToRecvSimulcast(pc2, pc1, ["foo", "bar"]); 445 const aTask = queueAWebrtcTask(); 446 let parameters = sender.getParameters(); 447 let rids = parameters.encodings.map(({rid}) => rid); 448 assert_array_equals(rids, ["foo", "bar"]); 449 parameters.encodings[0].scaleResolutionDownBy = 3; 450 parameters.encodings[1].scaleResolutionDownBy = 5; 451 452 const rollbackDone = pc1.setRemoteDescription({sdp: "", type: "rollback"}); 453 await aTask; 454 await sender.setParameters(parameters); 455 await rollbackDone; 456 457 parameters = sender.getParameters(); 458 rids = parameters.encodings.map(({rid}) => rid); 459 assert_array_equals(rids, [undefined]); 460 assert_equals(parameters.encodings[0].scaleResolutionDownBy, 1); 461 }, 'rollback of sRD(simulcast offer) interrupted by setParameters(simulcast) brings us back to having a single encoding without any previously set parameters'); 462 463 </script>