test_peerConnection_getParameters.html (16935B)
1 <!DOCTYPE HTML> 2 <html> 3 4 <head> 5 <script type="application/javascript" src="pc.js"></script> 6 <script type="application/javascript" src="sdpUtils.js"></script> 7 <script type="application/javascript" src="helpers_from_wpt/sdp.js"></script> 8 </head> 9 10 <body> 11 <pre id="test"> 12 <script type="application/javascript"> 13 createHTML({ 14 bug: "1534687", 15 title: "getParameters tests (that we can't do in wpt)", 16 visible: true 17 }); 18 19 function compareCodecParam(observed, expected) { 20 info(`Comparing ${JSON.stringify(observed)} to ${JSON.stringify(expected)}`); 21 is(observed.payloadType, expected.payloadType); 22 is(observed.clockRate, expected.clockRate); 23 is(observed.channels, expected.channels); 24 is(observed.mimeType.toLowerCase(), expected.mimeType.toLowerCase()); 25 if (expected.hasOwnProperty('sdpFmtpLine')) { 26 is(observed.sdpFmtpLine, expected.sdpFmtpLine); 27 } 28 } 29 30 function buildExpectedCodecs(msection) { 31 const rtpParameters = SDPUtils.parseRtpParameters(msection); 32 const {kind} = SDPUtils.parseMLine(msection); 33 const sdpCodecs = new Map; 34 for (const fromSdp of rtpParameters.codecs) { 35 const expected = { 36 payloadType: fromSdp.payloadType, 37 clockRate: fromSdp.clockRate, 38 mimeType: `${kind}/${fromSdp.name}`, 39 }; 40 if (kind == 'audio') { 41 expected.channels = fromSdp.channels; 42 } 43 const fmtps = SDPUtils.matchPrefixAndTrim(msection, `a=fmtp:${fromSdp.payloadType}`); 44 if (fmtps.length == 1) { 45 expected.sdpFmtpLine = fmtps[0]; 46 } else { 47 // compareCodecParam will check if observed.sdpFmtpLine is undefined if we 48 // set this, but will not perform any checks if we do not. 49 expected.sdpFmtpLine = undefined; 50 } 51 ok(!sdpCodecs.has(expected.payloadType), "payload types must be unique"); 52 sdpCodecs.set(expected.payloadType, expected); 53 } 54 return sdpCodecs; 55 } 56 57 // Does not support disregarding unsupported codecs in the SDP, so is not 58 // suitable for all test-cases. 59 function checkCodecsAgainstSDP(codecs, msection) { 60 const expectedCodecs = Array.from(buildExpectedCodecs(msection).values()); 61 is(codecs.length, expectedCodecs.length); 62 isnot(codecs.length, 0); 63 for (let i = 0; i < codecs.length; ++i) { 64 compareCodecParam(codecs[i], expectedCodecs[i]); 65 } 66 } 67 68 function checkCodecsAgainstSDPUnordered(codecs, msection) { 69 const expectedCodecsMap = buildExpectedCodecs(msection); 70 is(codecs.length, expectedCodecsMap.size); 71 isnot(codecs.length, 0); 72 for (const observed of codecs) { 73 compareCodecParam(observed, expectedCodecsMap.get(observed.payloadType)); 74 } 75 } 76 77 async function getNumExpectedH264SendCodecs() { 78 const h264Support = await checkPlatformH264CodecPrefs(); 79 if (h264Support.webrtc) { 80 // Constrained Baseline and Baseline multiplied with packetization-mode 0 and 1. 81 return 4; 82 } 83 ok(h264Support.platform, "There should always be some H264 support"); 84 // packetization-mode=0 is not supported with MediaDataEncoder. 85 return 2; 86 } 87 88 // SDP with unusual values in fmtp, but in the same formatting that Firefox 89 // typically uses. This lets us check that we're putting the right param values 90 // in sdpFmtpLine, if not what appears in the SDP verbatim. 91 const audioSdp = `v=0 92 o=- 1878890426675213188 2 IN IP4 127.0.0.1 93 s=- 94 t=0 0 95 a=fingerprint:sha-256 EB:74:E9:5F:EB:FB:79:D4:36:3A:06:89:DD:49:D0:C7:A5:EA:2A:B2:38:74:C8:AF:E4:A0:5A:EF:A9:58:B5:1A 96 m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101 97 c=IN IP4 0.0.0.0 98 a=sendrecv 99 a=fmtp:109 maxplaybackrate=48001;stereo=0;useinbandfec=0 100 a=fmtp:101 0-14 101 a=ice-pwd:60840251a559417c253d68478b0020fb 102 a=ice-ufrag:741347dd 103 a=mid:0 104 a=msid:{0df6a81e-d3f8-4d0f-ab93-892762bd2af7} {b15b10a1-061b-4685-9ca4-99e110744b2e} 105 a=rtcp-mux 106 a=rtpmap:109 opus/48000/2 107 a=rtpmap:9 G722/8000/1 108 a=rtpmap:0 PCMU/8000 109 a=rtpmap:8 PCMA/8000 110 a=rtpmap:101 telephone-event/8000/1 111 a=setup:passive 112 `; 113 114 let videoSdp; 115 if (navigator.userAgent.includes("Android")) { 116 // Alternate form with no packetization-mode=0 h264 117 videoSdp = `v=0 118 o=- 1878890426675213188 2 IN IP4 127.0.0.1 119 s=- 120 t=0 0 121 a=fingerprint:sha-256 EB:74:E9:5F:EB:FB:79:D4:36:3A:06:89:DD:49:D0:C7:A5:EA:2A:B2:38:74:C8:AF:E4:A0:5A:EF:A9:58:B5:1A 122 m=video 9 UDP/TLS/RTP/SAVPF 121 125 120 124 126 127 105 106 123 122 119 123 c=IN IP4 0.0.0.0 124 a=sendonly 125 a=fmtp:126 profile-level-id=42e00b;level-asymmetry-allowed=1;packetization-mode=1 126 a=fmtp:105 profile-level-id=420015;level-asymmetry-allowed=1;packetization-mode=1 127 a=fmtp:121 max-fs=12277;max-fr=50 128 a=fmtp:125 apt=121 129 a=fmtp:127 apt=126 130 a=fmtp:106 apt=105 131 a=fmtp:120 max-fs=12266;max-fr=40 132 a=fmtp:124 apt=120 133 a=fmtp:119 apt=122 134 a=ice-pwd:60840251a559417c253d68478b0020fb 135 a=ice-ufrag:741347dd 136 a=mid:0 137 a=msid:{debeb004-97f0-44ca-b6b2-a6bd8e42ddb2} {7fcd72c7-b112-49b4-892c-feee2cc9525d} 138 a=rtcp-mux 139 a=rtcp-rsize 140 a=rtpmap:121 VP9/90000 141 a=rtpmap:125 rtx/90000 142 a=rtpmap:120 VP8/90000 143 a=rtpmap:124 rtx/90000 144 a=rtpmap:126 H264/90000 145 a=rtpmap:127 rtx/90000 146 a=rtpmap:105 H264/90000 147 a=rtpmap:106 rtx/90000 148 a=rtpmap:123 ulpfec/90000 149 a=rtpmap:122 red/90000 150 a=rtpmap:119 rtx/90000 151 a=setup:passive 152 `; 153 } else { 154 videoSdp = `v=0 155 o=- 1878890426675213188 2 IN IP4 127.0.0.1 156 s=- 157 t=0 0 158 a=fingerprint:sha-256 EB:74:E9:5F:EB:FB:79:D4:36:3A:06:89:DD:49:D0:C7:A5:EA:2A:B2:38:74:C8:AF:E4:A0:5A:EF:A9:58:B5:1A 159 m=video 9 UDP/TLS/RTP/SAVPF 121 125 120 124 126 127 97 98 105 106 103 104 123 122 119 160 c=IN IP4 0.0.0.0 161 a=sendonly 162 a=fmtp:126 profile-level-id=42e00b;level-asymmetry-allowed=1;packetization-mode=1 163 a=fmtp:97 profile-level-id=42e00b;level-asymmetry-allowed=1 164 a=fmtp:105 profile-level-id=420015;level-asymmetry-allowed=1;packetization-mode=1 165 a=fmtp:103 profile-level-id=420015;level-asymmetry-allowed=1 166 a=fmtp:121 max-fs=12277;max-fr=50 167 a=fmtp:125 apt=121 168 a=fmtp:120 max-fs=12266;max-fr=40 169 a=fmtp:124 apt=120 170 a=fmtp:127 apt=126 171 a=fmtp:98 apt=97 172 a=fmtp:106 apt=105 173 a=fmtp:104 apt=103 174 a=fmtp:100 apt=99 175 a=fmtp:119 apt=122 176 a=ice-pwd:60840251a559417c253d68478b0020fb 177 a=ice-ufrag:741347dd 178 a=mid:0 179 a=msid:{debeb004-97f0-44ca-b6b2-a6bd8e42ddb2} {7fcd72c7-b112-49b4-892c-feee2cc9525d} 180 a=rtcp-mux 181 a=rtcp-rsize 182 a=rtpmap:121 VP9/90000 183 a=rtpmap:125 rtx/90000 184 a=rtpmap:120 VP8/90000 185 a=rtpmap:124 rtx/90000 186 a=rtpmap:126 H264/90000 187 a=rtpmap:127 rtx/90000 188 a=rtpmap:97 H264/90000 189 a=rtpmap:98 rtx/90000 190 a=rtpmap:105 H264/90000 191 a=rtpmap:106 rtx/90000 192 a=rtpmap:103 H264/90000 193 a=rtpmap:104 rtx/90000 194 a=rtpmap:123 ulpfec/90000 195 a=rtpmap:122 red/90000 196 a=rtpmap:119 rtx/90000 197 a=setup:passive 198 `; 199 } 200 201 let tests = [ 202 // fmtp testing in wpt requires a verbatim match, which we do not support 203 // yet (see bug 1751671). These test that we have a semantic match at least, 204 // because the sdp's fmtps use the same formatting that Firefox uses. 205 // TODO(https://bugzilla.mozilla.org/show_bug.cgi?id=1751671) 206 async function checkSenderFmtpAudioAnswerer() { 207 const pc = new RTCPeerConnection(); 208 await pc.setRemoteDescription({sdp: audioSdp, type: 'offer'}); 209 await pc.setLocalDescription(); 210 const {codecs} = pc.getSenders()[0].getParameters(); 211 const sections = SDPUtils.splitSections(audioSdp); 212 checkCodecsAgainstSDP(codecs, sections[1]); 213 }, 214 215 async function checkSenderFmtpVideoAnswerer() { 216 const pc = new RTCPeerConnection(); 217 await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'}); 218 await pc.setLocalDescription(); 219 const {codecs} = pc.getSenders()[0].getParameters(); 220 const sections = SDPUtils.splitSections(videoSdp); 221 checkCodecsAgainstSDP(codecs, sections[1]); 222 }, 223 224 async function checkSenderFmtpAudioOfferer() { 225 const pc = new RTCPeerConnection(); 226 pc.addTransceiver('audio', { direction: 'sendrecv' }); 227 await pc.setLocalDescription(); 228 await pc.setRemoteDescription({sdp: audioSdp, type: 'answer'}); 229 const {codecs} = pc.getSenders()[0].getParameters(); 230 const sections = SDPUtils.splitSections(audioSdp); 231 checkCodecsAgainstSDP(codecs, sections[1]); 232 }, 233 234 async function checkSenderFmtpVideoOfferer() { 235 const pc = new RTCPeerConnection(); 236 pc.addTransceiver('video', { direction: 'sendrecv' }); 237 await pc.setLocalDescription(); 238 await pc.setRemoteDescription({sdp: videoSdp, type: 'answer'}); 239 const {codecs} = pc.getSenders()[0].getParameters(); 240 const sections = SDPUtils.splitSections(videoSdp); 241 checkCodecsAgainstSDP(codecs, sections[1]); 242 }, 243 244 // wpt does not allow us to test that .codecs omits things that the pref 245 // system has disabled 246 async function checkRedUlpfecDisabled() { 247 await withPrefs([["media.navigator.video.red_ulpfec_enabled", false]], async () => { 248 const pc = new RTCPeerConnection(); 249 await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'}); 250 await pc.setLocalDescription(); 251 const {codecs} = pc.getSenders()[0].getParameters(); 252 // Control 253 is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp8'), true); 254 // No red or ulpfec 255 is(codecs.some(c => c.mimeType.toLowerCase() == 'video/red'), false); 256 is(codecs.some(c => c.mimeType.toLowerCase() == 'video/ulpfec'), false); 257 } 258 ); 259 }, 260 261 async function checkRtxDisabled() { 262 await withPrefs([["media.peerconnection.video.use_rtx", false]], async () => { 263 const pc = new RTCPeerConnection(); 264 await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'}); 265 await pc.setLocalDescription(); 266 const {codecs} = pc.getSenders()[0].getParameters(); 267 // Control 268 is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp8'), true); 269 // No rtx 270 is(codecs.some(c => c.mimeType.toLowerCase() == 'video/rtx'), false); 271 } 272 ); 273 }, 274 275 async function checkVP9Disabled() { 276 await withPrefs([["media.peerconnection.video.vp9_enabled", false]], async () => { 277 const pc = new RTCPeerConnection(); 278 await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'}); 279 await pc.setLocalDescription(); 280 const {codecs} = pc.getSenders()[0].getParameters(); 281 // Control 282 is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp8'), true); 283 // No VP9 284 is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp9'), false); 285 } 286 ); 287 }, 288 289 async function checkH264Sender() { 290 const numExpectedH264Codecs = await getNumExpectedH264SendCodecs(); 291 const pc1 = new RTCPeerConnection(); 292 const pc2 = new RTCPeerConnection(); 293 const {sender} = pc1.addTransceiver('video'); 294 await pc1.setLocalDescription(); 295 await pc2.setRemoteDescription(pc1.localDescription); 296 await pc2.setLocalDescription(); 297 await pc1.setRemoteDescription(pc2.localDescription); 298 { 299 const {codecs} = pc1.getSenders()[0].getParameters(); 300 info("pc1 codecs: " + JSON.stringify(codecs, null, 2)); 301 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 302 const sections = SDPUtils.splitSections(pc1.remoteDescription.sdp); 303 info("pc1 msection: " + sections[1].replace(/\r\n/g, "\n")); 304 checkCodecsAgainstSDP(codecs, sections[1]); 305 } 306 307 { 308 const {codecs} = pc2.getSenders()[0].getParameters(); 309 info("pc2 codecs: " + JSON.stringify(codecs, null, 2)); 310 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 311 const sections = SDPUtils.splitSections(pc2.remoteDescription.sdp); 312 info("pc2 msection: " + sections[1].replace(/\r\n/g, "\n")); 313 checkCodecsAgainstSDP(codecs, sections[1]); 314 } 315 }, 316 317 async function checkH264Receiver() { 318 const numExpectedH264Codecs = await getNumExpectedH264SendCodecs(); 319 const pc1 = new RTCPeerConnection(); 320 const pc2 = new RTCPeerConnection(); 321 pc1.addTransceiver('video'); 322 await pc1.setLocalDescription(); 323 await pc2.setRemoteDescription(pc1.localDescription); 324 await pc2.setLocalDescription(); 325 await pc1.setRemoteDescription(pc2.localDescription); 326 { 327 const {codecs} = pc1.getReceivers()[0].getParameters(); 328 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 329 const sections = SDPUtils.splitSections(pc1.localDescription.sdp); 330 checkCodecsAgainstSDP(codecs, sections[1]); 331 } 332 333 { 334 const {codecs} = pc2.getReceivers()[0].getParameters(); 335 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 336 const sections = SDPUtils.splitSections(pc2.localDescription.sdp); 337 checkCodecsAgainstSDP(codecs, sections[1]); 338 } 339 }, 340 341 async function checkH264Unidirectional() { 342 const numExpectedH264Codecs = await getNumExpectedH264SendCodecs(); 343 const pc1 = new RTCPeerConnection(); 344 const pc2 = new RTCPeerConnection(); 345 pc1.addTransceiver('video', {direction: 'recvonly'}); 346 await pc1.setLocalDescription(); 347 { 348 const sections = SDPUtils.splitSections(pc1.localDescription.sdp); 349 const {codecs} = SDPUtils.parseRtpParameters(sections[1]); 350 is(codecs.filter(c => c.name.toLowerCase() == 'h264').length, 4); 351 } 352 353 await pc2.setRemoteDescription(pc1.localDescription); 354 pc2.getTransceivers()[0].direction = 'sendonly'; 355 await pc2.setLocalDescription(); 356 await pc1.setRemoteDescription(pc2.localDescription); 357 { 358 const {codecs} = pc1.getReceivers()[0].getParameters(); 359 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4); 360 const sections = SDPUtils.splitSections(pc1.localDescription.sdp); 361 checkCodecsAgainstSDPUnordered(codecs, sections[1]); 362 } 363 364 { 365 const {codecs} = pc2.getSenders()[0].getParameters(); 366 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 367 const sections = SDPUtils.splitSections(pc2.localDescription.sdp); 368 checkCodecsAgainstSDP(codecs, sections[1]); 369 } 370 }, 371 372 async function checkH264NoLevelAsymmetryInOffer() { 373 const numExpectedH264Codecs = await getNumExpectedH264SendCodecs(); 374 const pc1 = new RTCPeerConnection(); 375 const pc2 = new RTCPeerConnection(); 376 pc1.addTransceiver('video'); 377 await pc1.setLocalDescription(); 378 const mungedOffer = { 379 sdp: pc1.localDescription.sdp.replace(/level-asymmetry-allowed=1/g, 'level-asymmetry-allowed=0'), 380 type: 'offer' 381 }; 382 await pc2.setRemoteDescription(mungedOffer); 383 await pc2.setLocalDescription(); 384 { 385 const {codecs} = pc2.getSenders()[0].getParameters(); 386 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 387 const sections = SDPUtils.splitSections(pc2.remoteDescription.sdp); 388 checkCodecsAgainstSDP(codecs, sections[1]); 389 } 390 391 { 392 const {codecs} = pc2.getReceivers()[0].getParameters(); 393 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 394 const sections = SDPUtils.splitSections(pc2.localDescription.sdp); 395 checkCodecsAgainstSDP(codecs, sections[1]); 396 } 397 }, 398 399 async function checkH264NoLevelAsymmetryInAnswer() { 400 const numExpectedH264Codecs = await getNumExpectedH264SendCodecs(); 401 const pc1 = new RTCPeerConnection(); 402 const pc2 = new RTCPeerConnection(); 403 pc1.addTransceiver('video'); 404 await pc1.setLocalDescription(); 405 await pc2.setRemoteDescription(pc1.localDescription); 406 await pc2.setLocalDescription(); 407 const mungedAnswer = { 408 sdp: pc2.localDescription.sdp.replace(/level-asymmetry-allowed=1/g, 'level-asymmetry-allowed=0'), 409 type: 'answer' 410 }; 411 await pc1.setRemoteDescription(mungedAnswer); 412 { 413 const {codecs} = pc1.getSenders()[0].getParameters(); 414 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 415 const sections = SDPUtils.splitSections(pc1.remoteDescription.sdp); 416 checkCodecsAgainstSDP(codecs, sections[1]); 417 } 418 419 { 420 const {codecs} = pc1.getReceivers()[0].getParameters(); 421 is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, numExpectedH264Codecs); 422 const sections = SDPUtils.splitSections(pc1.localDescription.sdp); 423 checkCodecsAgainstSDP(codecs, sections[1]); 424 } 425 }, 426 ]; 427 428 runNetworkTest(async () => { 429 await SpecialPowers.pushPrefEnv({ set: [["media.navigator.video.disable_h264_baseline", false]] }); 430 for (const test of tests) { 431 info(`Running test: ${test.name}`); 432 await test(); 433 info(`Done running test: ${test.name}`); 434 } 435 }); 436 437 </script> 438 </pre> 439 </body> 440 </html>