test_peerConnection_glean.html (75657B)
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="simulcast.js"></script> 8 <script type="application/javascript" src="iceTestUtils.js"></script> 9 <script type="application/javascript" src="helpers_from_wpt/sdp.js"></script> 10 </head> 11 12 <body> 13 <pre id="test"> 14 <script type="application/javascript"> 15 createHTML({ 16 bug: "1401592", 17 title: "Test that glean is recording stats as expected", 18 visible: true 19 }); 20 21 const { AppConstants } = SpecialPowers.ChromeUtils.importESModule( 22 "resource://gre/modules/AppConstants.sys.mjs" 23 ); 24 25 async function gleanResetTestValues() { 26 return SpecialPowers.spawnChrome([], async () => { 27 await Services.fog.testFlushAllChildren(); 28 Services.fog.testResetFOG(); 29 } 30 ) 31 }; 32 33 async function gleanFlushChildren() { 34 return SpecialPowers.spawnChrome([], async () => { 35 await Services.fog.testFlushAllChildren(); 36 } 37 ) 38 }; 39 40 41 async function getAllWarningRates() { 42 return { 43 warnNoGetparameters: 44 await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue(), 45 warnLengthChanged: 46 await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue(), 47 warnNoTransactionid: 48 await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue(), 49 }; 50 } 51 52 const tests = [ 53 // Keep these tests first; we do not want previous test-cases that might 54 // still be doing DTLS handshake stuff while their PCs are closing. 55 async function checkDtlsHandshakeSuccess() { 56 const pc1 = new RTCPeerConnection(); 57 const pc2 = new RTCPeerConnection(); 58 await gleanResetTestValues(); 59 let client_successes = await GleanTest.webrtcdtls.clientHandshakeResult.SUCCESS.testGetValue() || 0; 60 let server_successes = await GleanTest.webrtcdtls.serverHandshakeResult.SUCCESS.testGetValue() || 0; 61 let cipher_count = await GleanTest.webrtcdtls.cipher["0x1301"].testGetValue() || 0; 62 let srtp_cipher_count = await GleanTest.webrtcdtls.srtpCipher["0x0007"].testGetValue() || 0; 63 is(client_successes, 0); 64 is(server_successes, 0); 65 is(cipher_count, 0); 66 is(srtp_cipher_count, 0); 67 68 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 69 pc1.addTrack(stream.getTracks()[0]); 70 71 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 72 // This telemetry happens on STS/socket process 73 await gleanFlushChildren(); 74 75 client_successes = await GleanTest.webrtcdtls.clientHandshakeResult.SUCCESS.testGetValue() || 0; 76 server_successes = await GleanTest.webrtcdtls.serverHandshakeResult.SUCCESS.testGetValue() || 0; 77 cipher_count = await GleanTest.webrtcdtls.cipher["0x1301"].testGetValue() || 0; 78 srtp_cipher_count = await GleanTest.webrtcdtls.srtpCipher["0x0007"].testGetValue() || 0; 79 is(client_successes, 1); 80 is(server_successes, 1); 81 is(cipher_count, 2); 82 is(srtp_cipher_count, 2); 83 }, 84 85 async function checkDtlsHandshakeFailure() { 86 // We don't have many failures we can induce here, but messing up the 87 // fingerprint is one way. 88 const offerer = new RTCPeerConnection(); 89 const answerer = new RTCPeerConnection(); 90 await gleanResetTestValues(); 91 let client_failures = await GleanTest.webrtcdtls.clientHandshakeResult.SSL_ERROR_BAD_CERTIFICATE.testGetValue() || 0; 92 let server_failures = await GleanTest.webrtcdtls.serverHandshakeResult.SSL_ERROR_BAD_CERT_ALERT.testGetValue() || 0; 93 is(client_failures, 0); 94 is(server_failures, 0); 95 96 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 97 offerer.addTrack(stream.getTracks()[0]); 98 99 trickleIce(offerer, answerer); 100 trickleIce(answerer, offerer); 101 await offerer.setLocalDescription(); 102 let badSdp = offerer.localDescription.sdp; 103 // Tweak the last digit in the fingerprint sent to the answerer. Answerer 104 // (which will be the DTLS client) will get an SSL_ERROR_BAD_CERTIFICATE 105 // error, and the offerer (which will be the DTLS server) will get an 106 // SSL_ERROR_BAD_CERT_ALERT. 107 const lastDigit = badSdp.match(/a=fingerprint:.*([0-9A-F])$/m)[1]; 108 const newLastDigit = lastDigit == '0' ? '1' : '0'; 109 badSdp = badSdp.replace(/(a=fingerprint:.*)[0-9A-F]$/m, "$1" + newLastDigit); 110 info(badSdp); 111 await answerer.setRemoteDescription({sdp: badSdp, type: "offer"}); 112 await answerer.setLocalDescription(); 113 await offerer.setRemoteDescription(answerer.localDescription); 114 115 const throwOnTimeout = async () => { 116 await wait(32000); 117 throw new Error( 118 `ICE did not complete within ${timeout} ms`); 119 }; 120 121 const connectionPromises = [connectionStateReached(offerer, "failed"), 122 connectionStateReached(answerer, "failed")]; 123 124 await Promise.race([ 125 Promise.all(connectionPromises), 126 throwOnTimeout() 127 ]); 128 // This telemetry happens on STS/socket process 129 await gleanFlushChildren(); 130 131 client_failures = await GleanTest.webrtcdtls.clientHandshakeResult.SSL_ERROR_BAD_CERTIFICATE.testGetValue() || 0; 132 server_failures = await GleanTest.webrtcdtls.serverHandshakeResult.SSL_ERROR_BAD_CERT_ALERT.testGetValue() || 0; 133 is(client_failures, 1); 134 is(server_failures, 1); 135 }, 136 137 async function checkDtlsVersion1_3() { 138 // 1.3 should be the default 139 const pc1 = new RTCPeerConnection(); 140 const pc2 = new RTCPeerConnection(); 141 await gleanResetTestValues(); 142 let count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0; 143 let count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0; 144 let count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0; 145 is(count1_0, 0); 146 is(count1_2, 0); 147 is(count1_3, 0); 148 149 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 150 pc1.addTrack(stream.getTracks()[0]); 151 152 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 153 // This telemetry happens on STS/socket process 154 await gleanFlushChildren(); 155 156 count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0; 157 count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0; 158 count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0; 159 is(count1_0, 0); 160 is(count1_2, 0); 161 is(count1_3, 2); 162 }, 163 164 async function checkDtlsVersion1_2() { 165 // Make 1.2 the default 166 await withPrefs([["media.peerconnection.dtls.version.max", 771]], 167 async () => { 168 const pc1 = new RTCPeerConnection(); 169 const pc2 = new RTCPeerConnection(); 170 await gleanResetTestValues(); 171 let count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0; 172 let count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0; 173 let count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0; 174 is(count1_0, 0); 175 is(count1_2, 0); 176 is(count1_3, 0); 177 178 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 179 pc1.addTrack(stream.getTracks()[0]); 180 181 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 182 // This telemetry happens on STS/socket process 183 await gleanFlushChildren(); 184 185 count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0; 186 count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0; 187 count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0; 188 is(count1_0, 0); 189 is(count1_2, 2); 190 is(count1_3, 0); 191 }); 192 }, 193 194 async function checkDtlsVersion1_0() { 195 // Make 1.0 the default 196 await withPrefs([["media.peerconnection.dtls.version.max", 770], 197 ["media.peerconnection.dtls.version.min", 770]], 198 async () => { 199 const pc1 = new RTCPeerConnection(); 200 const pc2 = new RTCPeerConnection(); 201 await gleanResetTestValues(); 202 let count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0; 203 let count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0; 204 let count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0; 205 is(count1_0, 0); 206 is(count1_2, 0); 207 is(count1_3, 0); 208 209 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 210 pc1.addTrack(stream.getTracks()[0]); 211 212 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 213 // This telemetry happens on STS/socket process 214 await gleanFlushChildren(); 215 216 count1_0 = await GleanTest.webrtcdtls.protocolVersion["1.0"].testGetValue() || 0; 217 count1_2 = await GleanTest.webrtcdtls.protocolVersion["1.2"].testGetValue() || 0; 218 count1_3 = await GleanTest.webrtcdtls.protocolVersion["1.3"].testGetValue() || 0; 219 is(count1_0, 2); 220 is(count1_2, 0); 221 is(count1_3, 0); 222 }); 223 }, 224 225 async function checkDtlsKEA() { 226 // "media.webrtc.enable_pq_hybrid_kex" and "send_mlkem_keyshare" are enabled by default 227 // By default we send 2 key shares, PQ and X25519 228 // PQ Key share (ECDH Hybrid) has a higher preference, so it will be chosen as KEA 229 const pc1 = new RTCPeerConnection(); 230 const pc2 = new RTCPeerConnection(); 231 await gleanResetTestValues(); 232 // SSL Handshake Key Exchange Algorithm (null=0, rsa=1, dh=2, ecdh=4, ecdh_hybrid=8) 233 let keyExchangeValue = await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue() || 0; 234 is(keyExchangeValue, 0, "Expected no keyExchange distribution defined"); 235 236 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 237 pc1.addTrack(stream.getTracks()[0]); 238 239 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 240 // This telemetry happens on STS/socket process 241 await gleanFlushChildren(); 242 243 let count1_0 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["0"] || 0; 244 let count1_1 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["1"] || 0; 245 let count1_2 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["2"] || 0; 246 let count1_4 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["4"] || 0; 247 let count1_8 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["8"] || 0; 248 is(count1_0, 0, "Expected 0 connections using NULL"); 249 is(count1_1, 0, "Expected 0 connections using RSA"); 250 is(count1_2, 0, "Expected 0 connections using DH"); 251 is(count1_4, 0, "Expected 0 connections using ECDH"); 252 is(count1_8, 2, "Expected 2 connections using ECDH Hybrid"); 253 }, 254 255 async function checkDtlsKEA_DTLSBelow13() { 256 // DTLS1.2 does not use Kyber 257 // In this case, X25519 (ECDH) key share will be used 258 await withPrefs([["media.peerconnection.dtls.version.max", 771]], 259 async () => { 260 const pc1 = new RTCPeerConnection(); 261 const pc2 = new RTCPeerConnection(); 262 await gleanResetTestValues(); 263 // SSL Handshake Key Exchange Algorithm (null=0, rsa=1, dh=2, ecdh=4, ecdh_hybrid=8) 264 let keyExchangeValue = await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue() || 0; 265 is(keyExchangeValue, 0, "Expected no keyExchange distribution defined"); 266 267 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 268 pc1.addTrack(stream.getTracks()[0]); 269 270 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 271 // This telemetry happens on STS/socket process 272 await gleanFlushChildren(); 273 274 let count1_0 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["0"] || 0; 275 let count1_1 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["1"] || 0; 276 let count1_2 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["2"] || 0; 277 let count1_4 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["4"] || 0; 278 let count1_8 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["8"] || 0; 279 is(count1_0, 0, "Expected 0 connections using NULL"); 280 is(count1_1, 0, "Expected 0 connections using RSA"); 281 is(count1_2, 0, "Expected 0 connections using DH"); 282 is(count1_4, 2, "Expected 2 connections using ECDH"); 283 is(count1_8, 0, "Expected 0 connections using ECDH Hybrid"); 284 })}, 285 286 async function checkDtlsKEA_DTLS13DisablePQ() { 287 await withPrefs([["media.webrtc.enable_pq_hybrid_kex", false]], 288 async () => { 289 const pc1 = new RTCPeerConnection(); 290 const pc2 = new RTCPeerConnection(); 291 await gleanResetTestValues(); 292 // SSL Handshake Key Exchange Algorithm (null=0, rsa=1, dh=2, ecdh=4, ecdh_hybrid=8) 293 let keyExchangeValue = await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue() || 0; 294 is(keyExchangeValue, 0, "Expected no keyExchange distribution defined"); 295 296 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 297 pc1.addTrack(stream.getTracks()[0]); 298 299 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 300 // This telemetry happens on STS/socket process 301 await gleanFlushChildren(); 302 303 let count1_0 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["0"] || 0; 304 let count1_1 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["1"] || 0; 305 let count1_2 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["2"] || 0; 306 let count1_4 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["4"] || 0; 307 let count1_8 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["8"] || 0; 308 is(count1_0, 0, "Expected 0 connections using NULL"); 309 is(count1_1, 0, "Expected 0 connections using RSA"); 310 is(count1_2, 0, "Expected 0 connections using DH"); 311 is(count1_4, 2, "Expected 2 connections using ECDH"); 312 is(count1_8, 0, "Expected 0 connections using ECDH Hybrid"); 313 })}, 314 315 async function checkDtlsKEA_DTLS13DisablePQEnablePQShare() { 316 // Safety measures, when PQ is disabled, even if the sending ml-kem share is enabled 317 // it should not be sent. 318 await withPrefs([["media.webrtc.enable_pq_hybrid_kex", false], 319 ["media.webrtc.send_mlkem_keyshare", true] 320 ], 321 async () => { 322 const pc1 = new RTCPeerConnection(); 323 const pc2 = new RTCPeerConnection(); 324 await gleanResetTestValues(); 325 // SSL Handshake Key Exchange Algorithm (null=0, rsa=1, dh=2, ecdh=4, ecdh_hybrid=8) 326 let keyExchangeValue = await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue() || 0; 327 is(keyExchangeValue, 0, "Expected no keyExchange distribution defined"); 328 329 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 330 pc1.addTrack(stream.getTracks()[0]); 331 332 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 333 // This telemetry happens on STS/socket process 334 await gleanFlushChildren(); 335 336 let count1_0 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["0"] || 0; 337 let count1_1 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["1"] || 0; 338 let count1_2 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["2"] || 0; 339 let count1_4 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["4"] || 0; 340 let count1_8 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["8"] || 0; 341 is(count1_0, 0, "Expected 0 connections using NULL"); 342 is(count1_1, 0, "Expected 0 connections using RSA"); 343 is(count1_2, 0, "Expected 0 connections using DH"); 344 is(count1_4, 2, "Expected 2 connections using ECDH"); 345 is(count1_8, 0, "Expected 0 connections using ECDH Hybrid"); 346 })}, 347 348 async function checkDtlsKEA_DTLS13EnablePQDisablePQShare() { 349 // We will still advertise PQ, but we won't send a key share. 350 // See bug 1992457. 351 await withPrefs([["media.webrtc.enable_pq_hybrid_kex", true], 352 ["media.webrtc.send_mlkem_keyshare", false] 353 ], 354 async () => { 355 const pc1 = new RTCPeerConnection(); 356 const pc2 = new RTCPeerConnection(); 357 await gleanResetTestValues(); 358 // SSL Handshake Key Exchange Algorithm (null=0, rsa=1, dh=2, ecdh=4, ecdh_hybrid=8) 359 let keyExchangeValue = await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue() || 0; 360 is(keyExchangeValue, 0, "Expected no keyExchange distribution defined"); 361 362 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 363 pc1.addTrack(stream.getTracks()[0]); 364 365 await connect(pc1, pc2, 32000, "DTLS connected", true, true); 366 // This telemetry happens on STS/socket process 367 await gleanFlushChildren(); 368 369 let count1_0 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["0"] || 0; 370 let count1_1 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["1"] || 0; 371 let count1_2 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["2"] || 0; 372 let count1_4 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["4"] || 0; 373 let count1_8 = (await GleanTest.webrtcdtls.keyExchangeAlgorithm.testGetValue()).values["8"] || 0; 374 is(count1_0, 0, "Expected 0 connections using NULL"); 375 is(count1_1, 0, "Expected 0 connections using RSA"); 376 is(count1_2, 0, "Expected 0 connections using DH"); 377 is(count1_4, 2, "Expected 2 connections using ECDH"); 378 is(count1_8, 0, "Expected 0 connections using ECDH Hybrid"); 379 })}, 380 381 async function checkRTCRtpSenderCount() { 382 const pc = new RTCPeerConnection(); 383 const oldCount = await GleanTest.rtcrtpsender.count.testGetValue() ?? 0; 384 const { sender } = pc.addTransceiver('video', { 385 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 386 }); 387 const countDiff = await GleanTest.rtcrtpsender.count.testGetValue() - oldCount; 388 is(countDiff, 1, "Glean should have recorded the creation of a single RTCRtpSender"); 389 }, 390 391 async function checkRTCRtpSenderSetParametersCompatCount() { 392 await pushPrefs( 393 ["media.peerconnection.allow_old_setParameters", true]); 394 const pc = new RTCPeerConnection(); 395 const oldCount = await GleanTest.rtcrtpsender.countSetparametersCompat.testGetValue() ?? 0; 396 const { sender } = pc.addTransceiver('video', { 397 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 398 }); 399 const countDiff = await GleanTest.rtcrtpsender.countSetparametersCompat.testGetValue() - oldCount; 400 is(countDiff, 1, "Glean should have recorded the creation of a single RTCRtpSender that uses the setParameters compat mode"); 401 }, 402 403 async function checkSendEncodings() { 404 const pc = new RTCPeerConnection(); 405 const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue(); 406 const { sender } = pc.addTransceiver('video', { 407 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 408 }); 409 const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue(); 410 is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender"); 411 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded the use of sendEncodings"); 412 }, 413 414 async function checkAddTransceiverNoSendEncodings() { 415 const pc = new RTCPeerConnection(); 416 const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue(); 417 const { sender } = pc.addTransceiver('video'); 418 const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue(); 419 is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender"); 420 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded a use of sendEncodings"); 421 }, 422 423 async function checkAddTrack() { 424 const pc = new RTCPeerConnection(); 425 const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue(); 426 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 427 const sender = pc.addTrack(stream.getTracks()[0]); 428 const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue(); 429 is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender"); 430 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded a use of sendEncodings"); 431 }, 432 433 async function checkGoodSetParametersCompatMode() { 434 await pushPrefs( 435 ["media.peerconnection.allow_old_setParameters", true]); 436 const pc = new RTCPeerConnection(); 437 const { sender } = pc.addTransceiver('video', { 438 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 439 }); 440 const oldWarningRates = await getAllWarningRates(); 441 await sender.setParameters(sender.getParameters()); 442 const newWarningRates = await getAllWarningRates(); 443 isDeeply(oldWarningRates, newWarningRates); 444 }, 445 446 async function checkBadSetParametersNoGetParametersWarning() { 447 await pushPrefs( 448 ["media.peerconnection.allow_old_setParameters", true]); 449 const pc = new RTCPeerConnection(); 450 const { sender } = pc.addTransceiver('video', { 451 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 452 }); 453 454 let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue(); 455 456 await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] }); 457 458 let newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue(); 459 460 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning in setParameters due to lack of a getParameters call"); 461 462 oldRate = newRate; 463 464 // Glean should only record the warning once per sender! 465 await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] }); 466 467 newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue(); 468 469 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning in setParameters due to lack of a getParameters call"); 470 }, 471 472 async function checkBadSetParametersLengthChangedWarning() { 473 await pushPrefs( 474 ["media.peerconnection.allow_old_setParameters", true]); 475 const pc = new RTCPeerConnection(); 476 const { sender } = pc.addTransceiver('video', { 477 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 478 }); 479 480 let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue(); 481 482 let params = sender.getParameters(); 483 params.encodings.pop(); 484 await sender.setParameters(params); 485 486 let newRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue(); 487 488 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to a length change in encodings"); 489 490 oldRate = newRate; 491 492 // Glean should only record the warning once per sender! 493 params = sender.getParameters(); 494 params.encodings.pop(); 495 await sender.setParameters(params); 496 497 newRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue(); 498 499 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to a length change in encodings"); 500 }, 501 502 async function checkBadSetParametersRidChangedWarning() { 503 // This pref does not let rid change errors slide anymore 504 await pushPrefs( 505 ["media.peerconnection.allow_old_setParameters", true]); 506 const pc = new RTCPeerConnection(); 507 const { sender } = pc.addTransceiver('video', { 508 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 509 }); 510 511 let oldRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue(); 512 513 let params = sender.getParameters(); 514 params.encodings[1].rid = "foo"; 515 try { 516 await sender.setParameters(params); 517 } catch (e) { 518 } 519 let newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue(); 520 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a rid change in encodings"); 521 522 // Glean should only record the error once per sender! 523 params = sender.getParameters(); 524 params.encodings[1].rid = "bar"; 525 oldRate = newRate; 526 try { 527 await sender.setParameters(params); 528 } catch (e) { 529 } 530 newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue(); 531 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a rid change in encodings"); 532 }, 533 534 async function checkBadSetParametersNoTransactionIdWarning() { 535 await pushPrefs( 536 ["media.peerconnection.allow_old_setParameters", true]); 537 const pc = new RTCPeerConnection(); 538 const { sender } = pc.addTransceiver('video', { 539 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 540 }); 541 542 let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue(); 543 544 await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] }); 545 546 let newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue(); 547 548 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to missing transactionId in setParameters"); 549 550 oldRate = newRate; 551 552 // Glean should only record the warning once per sender! 553 await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] }); 554 555 newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue(); 556 557 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to missing transactionId in setParameters"); 558 }, 559 560 async function checkBadSetParametersLengthChangedError() { 561 await pushPrefs( 562 ["media.peerconnection.allow_old_setParameters", false]); 563 const pc = new RTCPeerConnection(); 564 const { sender } = pc.addTransceiver('video', { 565 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 566 }); 567 let oldRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue(); 568 let params = sender.getParameters(); 569 params.encodings.pop(); 570 try { 571 await sender.setParameters(params); 572 } catch (e) { 573 } 574 let newRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue(); 575 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a length change in encodings"); 576 577 // Glean should only record the error once per sender! 578 params = sender.getParameters(); 579 params.encodings.pop(); 580 oldRate = newRate; 581 try { 582 await sender.setParameters(params); 583 } catch (e) { 584 } 585 newRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue(); 586 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a length change in encodings"); 587 }, 588 589 async function checkBadSetParametersRidChangedError() { 590 await pushPrefs( 591 ["media.peerconnection.allow_old_setParameters", false]); 592 const pc = new RTCPeerConnection(); 593 const { sender } = pc.addTransceiver('video', { 594 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 595 }); 596 let oldRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue(); 597 let params = sender.getParameters(); 598 params.encodings[1].rid = "foo"; 599 try { 600 await sender.setParameters(params); 601 } catch (e) { 602 } 603 let newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue(); 604 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a rid change in encodings"); 605 606 // Glean should only record the error once per sender! 607 params = sender.getParameters(); 608 params.encodings[1].rid = "bar"; 609 oldRate = newRate; 610 try { 611 await sender.setParameters(params); 612 } catch (e) { 613 } 614 newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue(); 615 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a rid change in encodings"); 616 }, 617 618 async function checkBadSetParametersNoGetParametersError() { 619 await pushPrefs( 620 ["media.peerconnection.allow_old_setParameters", false]); 621 const pc = new RTCPeerConnection(); 622 const { sender } = pc.addTransceiver('video', { 623 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 624 }); 625 let oldRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue(); 626 try { 627 await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] }); 628 } catch (e) { 629 } 630 let newRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue(); 631 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error in setParameters due to lack of a getParameters call"); 632 633 // Glean should only record the error once per sender! 634 oldRate = newRate; 635 try { 636 await sender.setParameters({ encodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] }); 637 } catch (e) { 638 } 639 newRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue(); 640 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error in setParameters due to lack of a getParameters call"); 641 }, 642 643 async function checkBadSetParametersStaleTransactionIdError() { 644 await pushPrefs( 645 ["media.peerconnection.allow_old_setParameters", false]); 646 const pc = new RTCPeerConnection(); 647 const { sender } = pc.addTransceiver('video', { 648 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 649 }); 650 let oldRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue(); 651 let params = sender.getParameters(); 652 // Cause transactionId to be stale 653 await pc.createOffer(); 654 // ...but make sure there is a recent getParameters call 655 sender.getParameters(); 656 try { 657 await sender.setParameters(params); 658 } catch (e) { 659 } 660 let newRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue(); 661 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to stale transactionId in setParameters"); 662 663 // Glean should only record the error once per sender! 664 oldRate = newRate; 665 params = sender.getParameters(); 666 // Cause transactionId to be stale 667 await pc.createOffer(); 668 // ...but make sure there is a recent getParameters call 669 sender.getParameters(); 670 try { 671 await sender.setParameters(params); 672 } catch (e) { 673 } 674 newRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue(); 675 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to stale transactionId in setParameters"); 676 }, 677 678 async function checkBadSetParametersNoEncodingsError() { 679 // If we do not allow the old setParameters, this will fail the length check 680 // instead. 681 await pushPrefs( 682 ["media.peerconnection.allow_old_setParameters", true]); 683 const pc = new RTCPeerConnection(); 684 const { sender } = pc.addTransceiver('video', { 685 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 686 }); 687 let oldRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue(); 688 let params = sender.getParameters(); 689 params.encodings = []; 690 try { 691 await sender.setParameters(params); 692 } catch (e) { 693 } 694 let newRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue(); 695 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded an error due to empty encodings in setParameters"); 696 697 // Glean should only record the error once per sender! 698 oldRate = newRate; 699 params = sender.getParameters(); 700 params.encodings = []; 701 try { 702 await sender.setParameters(params); 703 } catch (e) { 704 } 705 newRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue(); 706 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded an error due empty encodings in setParameters"); 707 }, 708 709 async function checkBadSetParametersOtherError() { 710 const pc = new RTCPeerConnection(); 711 const { sender } = pc.addTransceiver('video', { 712 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 713 }); 714 let oldRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue(); 715 let params = sender.getParameters(); 716 params.encodings[0].scaleResolutionDownBy = 0.5; 717 try { 718 await sender.setParameters(params); 719 } catch (e) { 720 } 721 let newRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue(); 722 is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to some other failure"); 723 724 // Glean should only record the error once per sender! 725 oldRate = newRate; 726 params = sender.getParameters(); 727 params.encodings[0].scaleResolutionDownBy = 0.5; 728 try { 729 await sender.setParameters(params); 730 } catch (e) { 731 } 732 newRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue(); 733 is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to some other failure"); 734 }, 735 736 async function checkUlpfecNegotiated() { 737 const pc1 = new RTCPeerConnection(); 738 const pc2 = new RTCPeerConnection(); 739 await gleanResetTestValues(); 740 741 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 742 const sender = pc1.addTrack(stream.getTracks()[0]); 743 let offer = await pc1.createOffer(); 744 await pc1.setLocalDescription(offer); 745 await pc2.setRemoteDescription(offer); 746 let answer = await pc2.createAnswer(); 747 await pc2.setLocalDescription(answer); 748 await pc1.setRemoteDescription(answer); 749 750 // Validate logging shows ulpfec negotiated and preferred video VP8 751 let ulpfecNotNegotiated = await GleanTest.codecStats.ulpfecNegotiated.not_negotiated.testGetValue() || 0; 752 ok(ulpfecNotNegotiated == 0, "checkUlpfecNegotiated glean should not count not_negotiated"); 753 let ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0; 754 ok(ulpfecNegotiated == 2, "checkUlpfecNegotiated glean should show ulpfec negotiated"); 755 let preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0; 756 ok(preferredVideoCodec == 2, "checkUlpfecNegotiated glean should show preferred video codec VP8"); 757 758 // Validate negotiation event logging 759 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 760 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 761 for (let i = 0; i < sdps.length; ++i) { 762 const e = sdps[i].extra; 763 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 764 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 765 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 766 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 767 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 768 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 769 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 770 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 771 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 772 is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`); 773 is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`); 774 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 775 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 776 } 777 778 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 779 is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 780 781 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 782 is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 783 for (let i = 0; i < videoMsections.length; ++i) { 784 const e = videoMsections[i].extra; 785 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 786 is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`); 787 is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`); 788 is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`); 789 is(e.preferred_recv_codec, i == 0 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`); 790 is(e.preferred_send_codec, i == 0 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`); 791 ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`); 792 ok(e.codecs.includes("ulpfec"), `No ulpfec codec present for i=${i}`); 793 is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`); 794 } 795 796 pc1.close(); 797 pc2.close(); 798 }, 799 800 async function checkNoUlpfecNegotiated() { 801 802 const pc1 = new RTCPeerConnection(); 803 const pc2 = new RTCPeerConnection(); 804 await gleanResetTestValues(); 805 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 806 const sender = pc1.addTrack(stream.getTracks()[0]); 807 await pc1.setLocalDescription(); 808 const offer = pc1.localDescription; 809 const payloadType = sdputils.findCodecId(offer.sdp, "ulpfec"); 810 const sdp = sdputils.removeCodec(offer.sdp, payloadType); 811 await pc2.setRemoteDescription({type: 'offer', sdp}); 812 let answer = await pc2.createAnswer(); 813 await pc2.setLocalDescription(answer); 814 await pc1.setRemoteDescription(answer); 815 816 // Validate logging shows ulpfec not negotiated and preferred video VP8 817 const ulpfecNotNegotiated = await GleanTest.codecStats.ulpfecNegotiated.not_negotiated.testGetValue() || 0; 818 is(ulpfecNotNegotiated, 2, "checkNoUlpfecNegotiated glean should count not_negotiated"); 819 const ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0; 820 is(ulpfecNegotiated, 0, "checkNoUlpfecNegotiated glean should not show ulpfec negotiated " + ulpfecNegotiated); 821 const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0; 822 is(preferredVideoCodec, 2, "checkNoUlpfecNegotiated glean should show preferred video codec VP8"); 823 824 // Validate negotiation event logging 825 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 826 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 827 for (let i = 0; i < sdps.length; ++i) { 828 const e = sdps[i].extra; 829 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 830 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 831 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 832 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 833 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 834 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 835 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 836 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 837 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 838 is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`); 839 is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`); 840 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 841 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 842 } 843 844 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 845 is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 846 847 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 848 is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 849 for (let i = 0; i < videoMsections.length; ++i) { 850 const e = videoMsections[i].extra; 851 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 852 is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`); 853 is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`); 854 is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`); 855 is(e.preferred_recv_codec, i == 0 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`); 856 is(e.preferred_send_codec, i == 0 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`); 857 ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`); 858 ok(!e.codecs.includes("ulpfec"), `No ulpfec codec present for i=${i}`); 859 is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`); 860 } 861 862 pc1.close(); 863 pc2.close(); 864 }, 865 866 async function checkFlexfecOffered() { 867 868 const pc1 = new RTCPeerConnection(); 869 const pc2 = new RTCPeerConnection(); 870 await gleanResetTestValues(); 871 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 872 const sender = pc1.addTrack(stream.getTracks()[0]); 873 await pc1.setLocalDescription(); 874 const offer = pc1.localDescription; 875 const sdp = offer.sdp.replaceAll('VP8','flexfec'); 876 await pc2.setRemoteDescription({type: 'offer', sdp}); 877 let answer = await pc2.createAnswer(); 878 await pc2.setLocalDescription(answer); 879 await pc1.setRemoteDescription(answer); 880 881 // Validate logging shows flexfec counted once ulpfec negotiated twice 882 // and preferred video VP9 since no VP8 was offered. 883 const flexfecOffered = await GleanTest.codecStats.otherFecSignaled.flexfec.testGetValue() || 0; 884 is(flexfecOffered, 1, "checkFlexfecOffered glean should count flexfec being offered" + flexfecOffered); 885 const ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0; 886 is(ulpfecNegotiated, 2, "checkUlpfecNegotiated glean should show ulpfec negotiated"); 887 const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP9.testGetValue() || 0; 888 is(preferredVideoCodec, 2, "checkFlexfecOffered glean should show preferred video codec VP9"); 889 890 // Validate negotiation event logging 891 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 892 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 893 for (let i = 0; i < sdps.length; ++i) { 894 const e = sdps[i].extra; 895 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 896 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 897 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 898 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 899 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 900 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 901 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 902 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 903 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 904 is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`); 905 is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`); 906 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 907 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 908 } 909 910 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 911 is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 912 913 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 914 is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 915 for (let i = 0; i < videoMsections.length; ++i) { 916 const e = videoMsections[i].extra; 917 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 918 is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`); 919 is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`); 920 is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`); 921 is(e.preferred_recv_codec, i == 0 ? "VP9" : undefined, `Expected preferred recv video codec for i=${i}`); 922 is(e.preferred_send_codec, i == 0 ? undefined : "VP9", `Expected preferred send video codec for i=${i}`); 923 ok(!e.codecs.includes("VP8"), `No VP8 codec present for i=${i}`); 924 ok(!e.codecs.includes("flexfec"), `No flexfec codec present for i=${i}`); 925 ok(e.codecs.includes("VP9"), `VP9 codec present for i=${i}`); 926 ok(e.codecs.includes("ulpfec"), `ulpfec codec present for i=${i}`); 927 is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`); 928 } 929 930 pc1.close(); 931 pc2.close(); 932 }, 933 934 async function checkPreferredVideoCodec() { 935 936 const pc1 = new RTCPeerConnection(); 937 const pc2 = new RTCPeerConnection(); 938 await gleanResetTestValues(); 939 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 940 const sender = pc1.addTrack(stream.getTracks()[0]); 941 await pc1.setLocalDescription(); 942 const offer = pc1.localDescription; 943 // Set a video codec that does not exist to simulate receiving codecs we 944 // dont support and verify it gets logged. 945 const sdp = offer.sdp.replaceAll('VP8','AVADA1'); 946 await pc2.setRemoteDescription({type: 'offer', sdp}); 947 let answer = await pc2.createAnswer(); 948 await pc2.setLocalDescription(answer); 949 await pc1.setRemoteDescription(answer); 950 951 // We should show AVADA1 as the preferred codec from the offer and the 952 // answer should prefer VP9 since VP8 was removed. 953 const preferredVideoCodecAVADA1 = await GleanTest.codecStats.videoPreferredCodec.AVADA1.testGetValue() || 0; 954 is(preferredVideoCodecAVADA1, 1, "checkPreferredVideoCodec glean should show preferred video codec AVADA1" + preferredVideoCodecAVADA1); 955 const preferredVideoCodecVP9 = await GleanTest.codecStats.videoPreferredCodec.VP9.testGetValue() || 0; 956 is(preferredVideoCodecVP9, 1, "checkPreferredVideoCodec glean should show preferred video codec VP9" + preferredVideoCodecVP9); 957 958 // Validate negotiation event logging 959 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 960 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 961 for (let i = 0; i < sdps.length; ++i) { 962 const e = sdps[i].extra; 963 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 964 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 965 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 966 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 967 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 968 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 969 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 970 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 971 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 972 is(e.num_msections_video_recvonly, i == 0 ? "1" : "0", `Expected number of video recvonly m-sections for i=${i}`); 973 is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`); 974 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 975 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 976 } 977 978 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 979 is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 980 981 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 982 is(videoMsections.length, 2, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 983 for (let i = 0; i < videoMsections.length; ++i) { 984 const e = videoMsections[i].extra; 985 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 986 is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`); 987 is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`); 988 is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`); 989 is(e.preferred_recv_codec, i == 0 ? "AVADA1" : undefined, `Expected preferred recv video codec for i=${i}`); 990 is(e.preferred_send_codec, i == 0 ? undefined : "VP9", `Expected preferred send video codec for i=${i}`); 991 ok(!e.codecs.includes("VP8"), `No VP8 codec present for i=${i}`); 992 ok(!e.codecs.includes("AVADA1"), `No AVADA1 codec present for i=${i}`); 993 ok(e.codecs.includes("VP9"), `VP9 codec present for i=${i}`); 994 is(e.num_send_simulcast_layers, i == 0 ? undefined : "1", `Expected number of simulcast layers for i=${i}`); 995 } 996 997 pc1.close(); 998 pc2.close(); 999 }, 1000 1001 async function checkPreferredAudioCodec() { 1002 1003 const pc1 = new RTCPeerConnection(); 1004 const pc2 = new RTCPeerConnection(); 1005 await gleanResetTestValues(); 1006 const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); 1007 const sender = pc1.addTrack(stream.getTracks()[0]); 1008 await pc1.setLocalDescription(); 1009 const offer = pc1.localDescription; 1010 // Set an audio codec that does not exist to simulate receiving codecs we 1011 // dont support and verify it gets logged. 1012 const sdp = offer.sdp.replaceAll('opus','FAKECodec'); 1013 await pc2.setRemoteDescription({type: 'offer', sdp}); 1014 let answer = await pc2.createAnswer(); 1015 await pc2.setLocalDescription(answer); 1016 await pc1.setRemoteDescription(answer); 1017 1018 // We should show CN as the preferred codec from the offer and the answer 1019 // should prefer G722 since opus was removed. 1020 const preferredAudioCodecFAKECodec = await GleanTest.codecStats.audioPreferredCodec.FAKECodec.testGetValue() || 0; 1021 is(preferredAudioCodecFAKECodec, 1, "checkPreferredAudioCodec Glean should show preferred audio codec FAKECodec " + preferredAudioCodecFAKECodec); 1022 const preferredAudioCodecG722 = await GleanTest.codecStats.audioPreferredCodec.G722.testGetValue() || 0; 1023 is(preferredAudioCodecG722, 1, "checkPreferredAudioCodec Glean should show preferred audio codec G722 " + preferredAudioCodecG722); 1024 1025 // Validate negotiation event logging 1026 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 1027 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 1028 for (let i = 0; i < sdps.length; ++i) { 1029 const e = sdps[i].extra; 1030 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1031 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1032 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 1033 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 1034 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 1035 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 1036 is(e.num_msections_audio_recvonly, i == 0 ? "1" : "0", `Expected number of audio recvonly m-sections for i=${i}`); 1037 is(e.num_msections_audio_sendonly, i == 0 ? "0" : "1", `Expected number of audio sendonly m-sections for i=${i}`); 1038 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 1039 is(e.num_msections_video_recvonly, "0", `Expected number of video recvonly m-sections for i=${i}`); 1040 is(e.num_msections_video_sendonly, "0", `Expected number of video sendonly m-sections for i=${i}`); 1041 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 1042 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 1043 } 1044 1045 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 1046 is(audioMsections.length, 2, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 1047 for (let i = 0; i < audioMsections.length; ++i) { 1048 const e = audioMsections[i].extra; 1049 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1050 is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1051 is(e.has_rtcp_mux, "true", `Expected audio rtcp-mux for i=${i}`); 1052 is(e.direction, i == 0 ? "recvonly" : "sendonly", `Expected audio direction for i=${i}`); 1053 is(e.preferred_recv_codec, i == 0 ? "FAKECodec" : undefined, `Expected preferred recv video codec for i=${i}`); 1054 is(e.preferred_send_codec, i == 0 ? undefined : "G722", `Expected preferred send video codec for i=${i}`); 1055 ok(!e.codecs.includes("opus"), `No opus present for i=${i}`); 1056 ok(!e.codecs.includes("FAKECodec"), `No FAKECodec present for i=${i}`); 1057 ok(e.codecs.includes("G722"), `VP9 codec present for i=${i}`); 1058 } 1059 1060 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 1061 is(videoMsections.length, 0, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 1062 1063 pc1.close(); 1064 pc2.close(); 1065 }, 1066 1067 async function checkLoggingMultipleTransceivers() { 1068 const pc1 = new RTCPeerConnection(); 1069 const pc2 = new RTCPeerConnection(); 1070 await gleanResetTestValues(); 1071 1072 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 1073 const sender = pc1.addTrack(stream.getTracks()[0]); 1074 pc1.addTransceiver(stream.getTracks()[0]); 1075 pc1.addTransceiver(stream.getTracks()[0]); 1076 1077 const offer = await pc1.createOffer(); 1078 await pc1.setLocalDescription(offer); 1079 await pc2.setRemoteDescription(offer); 1080 const answer = await pc2.createAnswer(); 1081 await pc2.setLocalDescription(answer); 1082 await pc1.setRemoteDescription(answer); 1083 1084 // Renegotiate 1085 const newOffer = await pc1.createOffer(); 1086 await pc1.setLocalDescription(newOffer); 1087 await pc2.setRemoteDescription(newOffer); 1088 const newAnswer = await pc2.createAnswer(); 1089 await pc2.setLocalDescription(newAnswer); 1090 await pc1.setRemoteDescription(newAnswer); 1091 1092 // Validate logging shows for each transciever but is not duplicated with the renegotiation 1093 const ulpfecNotNegotiated = await GleanTest.codecStats.ulpfecNegotiated.not_negotiated.testGetValue() || 0; 1094 is(ulpfecNotNegotiated, 0, "checkLoggingMultipleTransceivers glean should not count not_negotiated"); 1095 const ulpfecNegotiated = await GleanTest.codecStats.ulpfecNegotiated.negotiated.testGetValue() || 0; 1096 is(ulpfecNegotiated, 6, "checkLoggingMultipleTransceivers glean should show ulpfec negotiated " + ulpfecNegotiated); 1097 const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0; 1098 is(preferredVideoCodec, 6, "checkLoggingMultipleTransceivers glean should show preferred video codec VP8 " + preferredVideoCodec); 1099 1100 // Validate negotiation event logging 1101 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 1102 is(sdps.length, 4, "Expected number of sdps"); // pc2 finished negotiation before pc1 1103 for (let i = 0; i < sdps.length; ++i) { 1104 const e = sdps[i].extra; 1105 is(e.pc_id, SpecialPowers.wrap(i % 2 == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1106 is(e.negotiation_count, i > 1 ? "2" : "1", `Expected number of negotiations for i=${i}`); 1107 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 1108 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 1109 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 1110 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 1111 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 1112 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 1113 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 1114 is(e.num_msections_video_recvonly, i % 2 == 0 ? "3" : "0", `Expected number of video recvonly m-sections for i=${i}`); 1115 is(e.num_msections_video_sendonly, i % 2 == 1 ? "3" : "0", `Expected number of video sendonly m-sections for i=${i}`); 1116 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 1117 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 1118 } 1119 1120 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 1121 is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 1122 1123 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 1124 is(videoMsections.length, 12, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 1125 for (let i = 0; i < videoMsections.length; ++i) { 1126 const e = videoMsections[i].extra; 1127 is(e.pc_id, SpecialPowers.wrap(i % 6 < 3 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1128 is(e.pc_negotiation_count, `${Math.floor(i / 6) + 1}`, `Expected number of negotiations for i=${i}`); 1129 is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`); 1130 is(e.direction, i % 6 < 3 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`); 1131 is(e.preferred_recv_codec, i % 6 < 3 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`); 1132 is(e.preferred_send_codec, i % 6 < 3 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`); 1133 ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`); 1134 is(e.num_send_simulcast_layers, i % 6 < 3 ? undefined : "1", `Expected number of simulcast layers for i=${i}`); 1135 } 1136 1137 pc1.close(); 1138 pc2.close(); 1139 }, 1140 1141 async function checkSimulcastSendEncodings() { 1142 const pc1 = new RTCPeerConnection(); 1143 const pc2 = new RTCPeerConnection(); 1144 await gleanResetTestValues(); 1145 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 1146 const { sender } = pc1.addTransceiver(stream.getTracks()[0], { 1147 sendEncodings: [{ rid: "0" }, { rid: "1" }, { rid: "2" }] 1148 }); 1149 const offer = await pc1.createOffer(); 1150 const mungedOffer = ridToMid(offer); 1151 await pc1.setLocalDescription(offer); 1152 await pc2.setRemoteDescription({type: 'offer', sdp: mungedOffer}); 1153 const answer = await pc2.createAnswer(); 1154 const mungedAnswer = midToRid(answer); 1155 await pc2.setLocalDescription(answer); 1156 await pc1.setRemoteDescription({type: 'answer', sdp: mungedAnswer}); 1157 1158 const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0; 1159 is(preferredVideoCodec, 4, "checkSimulcastSendEncodings glean should show preferred video codec VP8"); 1160 1161 // Validate negotiation event logging 1162 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 1163 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 1164 for (let i = 0; i < sdps.length; ++i) { 1165 const e = sdps[i].extra; 1166 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1167 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1168 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 1169 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 1170 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 1171 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 1172 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 1173 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 1174 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 1175 is(e.num_msections_video_recvonly, i == 0 ? "3" : "0", `Expected number of video recvonly m-sections for i=${i}`); 1176 is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`); 1177 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 1178 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 1179 } 1180 1181 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 1182 is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 1183 1184 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 1185 is(videoMsections.length, 4, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 1186 for (let i = 0; i < videoMsections.length; ++i) { 1187 const e = videoMsections[i].extra; 1188 is(e.pc_id, SpecialPowers.wrap(i < 3 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1189 is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1190 is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`); 1191 is(e.direction, i < 3 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`); 1192 is(e.preferred_recv_codec, i < 3 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`); 1193 is(e.preferred_send_codec, i < 3 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`); 1194 ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`); 1195 is(e.num_send_simulcast_layers, i < 3 ? undefined : "3", `Expected number of simulcast layers for i=${i}`); 1196 } 1197 1198 pc1.close(); 1199 pc2.close(); 1200 }, 1201 1202 async function checkSimulcastJsep() { 1203 const pc1 = new RTCPeerConnection(); 1204 const pc2 = new RTCPeerConnection(); 1205 await gleanResetTestValues(); 1206 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 1207 pc2.addTransceiver('video', { direction: "recvonly" }); 1208 pc2.addTransceiver('video', { direction: "recvonly" }); 1209 const offer = await pc2.createOffer(); 1210 const mungedOffer = midToRid(offer); 1211 await pc2.setLocalDescription(offer); 1212 await pc1.setRemoteDescription({type: 'offer', sdp: mungedOffer}); 1213 pc1.addTrack(stream.getTracks()[0], stream); 1214 const answer = await pc1.createAnswer(); 1215 const mungedAnswer = ridToMid(answer); 1216 await pc2.setRemoteDescription({type: 'answer', sdp: mungedAnswer}); 1217 await pc1.setLocalDescription(answer); 1218 1219 const preferredVideoCodec = await GleanTest.codecStats.videoPreferredCodec.VP8.testGetValue() || 0; 1220 is(preferredVideoCodec, 3, "checkSimulcastJsep glean should show preferred video codec VP8"); 1221 1222 // Validate negotiation event logging 1223 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 1224 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 1225 for (let i = 0; i < sdps.length; ++i) { 1226 const e = sdps[i].extra; 1227 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1228 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1229 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 1230 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 1231 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 1232 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 1233 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 1234 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 1235 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 1236 is(e.num_msections_video_recvonly, i == 0 ? "2" : "0", `Expected number of video recvonly m-sections for i=${i}`); 1237 is(e.num_msections_video_sendonly, i == 1 ? "1" : "0", `Expected number of video sendonly m-sections for i=${i}`); 1238 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 1239 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 1240 } 1241 1242 const audioMsections = await GleanTest.webrtcSignaling.audioMsectionNegotiated.testGetValue() || []; 1243 is(audioMsections.length, 0, "Expected number of audio m-sections"); // pc2 finished negotiation before pc1 1244 1245 const videoMsections = await GleanTest.webrtcSignaling.videoMsectionNegotiated.testGetValue() || []; 1246 is(videoMsections.length, 3, "Expected number of video m-sections"); // pc2 finished negotiation before pc1 1247 for (let i = 0; i < videoMsections.length; ++i) { 1248 const e = videoMsections[i].extra; 1249 is(e.pc_id, SpecialPowers.wrap(i < 2 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1250 is(e.pc_negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1251 is(e.has_rtcp_mux, "true", `Expected video rtcp-mux for i=${i}`); 1252 is(e.direction, i < 2 ? "recvonly" : "sendonly", `Expected video direction for i=${i}`); 1253 is(e.preferred_recv_codec, i < 2 ? "VP8" : undefined, `Expected preferred recv video codec for i=${i}`); 1254 is(e.preferred_send_codec, i < 2 ? undefined : "VP8", `Expected preferred send video codec for i=${i}`); 1255 ok(e.codecs.includes("VP8"), `VP8 codec present for i=${i}`); 1256 is(e.num_send_simulcast_layers, i < 2 ? undefined : "2", `Expected number of simulcast layers for i=${i}`); 1257 } 1258 1259 pc1.close(); 1260 pc2.close(); 1261 }, 1262 1263 async function checkConfigurationNoBundle() { 1264 const pc1 = new RTCPeerConnection({bundlePolicy: "max-compat"}); 1265 const pc2 = new RTCPeerConnection({iceTransportPolicy: "relay"}); 1266 await gleanResetTestValues(); 1267 const stream = await navigator.mediaDevices.getUserMedia({ video: true }); 1268 const sender1 = pc1.addTrack(stream.getTracks()[0]); 1269 const sender2 = pc1.addTrack(sender1.track.clone()); 1270 await pc1.setLocalDescription(); 1271 const offer = pc1.localDescription; 1272 const mungedOffer = offer.sdp.replace("BUNDLE", "BUNGLE"); 1273 await pc2.setRemoteDescription({ type: "offer", sdp: mungedOffer }); 1274 const answer = await pc2.createAnswer(); 1275 await pc2.setLocalDescription(answer); 1276 await pc1.setRemoteDescription(answer); 1277 1278 // Validate negotiation event logging 1279 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 1280 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 1281 for (let i = 0; i < sdps.length; ++i) { 1282 const e = sdps[i].extra; 1283 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1284 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1285 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 1286 is(e.bundle_policy, i == 0 ? "balanced" : "max-compat", `Expected BundlePolicy for i=${i}`); 1287 is(e.ice_transport_policy, i == 0 ? "relay" : "all", `Expected IceTransportPolicy for i=${i}`); 1288 is(e.num_transports, "2", `Expected number of transports for i=${i}`); 1289 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 1290 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 1291 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 1292 is(e.num_msections_video_recvonly, i == 0 ? "2" : "0", `Expected number of video recvonly m-sections for i=${i}`); 1293 is(e.num_msections_video_sendonly, i == 1 ? "2" : "0", `Expected number of video sendonly m-sections for i=${i}`); 1294 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 1295 is(e.num_msections_data, "0", `Expected number of data m-sections for i=${i}`); 1296 } 1297 1298 pc1.close(); 1299 pc2.close(); 1300 }, 1301 1302 async function checkDataChannel() { 1303 const pc1 = new RTCPeerConnection(); 1304 const pc2 = new RTCPeerConnection(); 1305 await gleanResetTestValues(); 1306 pc1.createDataChannel('test'); 1307 await pc1.setLocalDescription(); 1308 const offer = pc1.localDescription; 1309 await pc2.setRemoteDescription(offer); 1310 const answer = await pc2.createAnswer(); 1311 await pc2.setLocalDescription(answer); 1312 await pc1.setRemoteDescription(answer); 1313 1314 // Validate negotiation event logging 1315 const sdps = await GleanTest.webrtcSignaling.sdpNegotiated.testGetValue() || []; 1316 is(sdps.length, 2, "Expected number of sdps"); // pc2 finished negotiation before pc1 1317 for (let i = 0; i < sdps.length; ++i) { 1318 const e = sdps[i].extra; 1319 is(e.pc_id, SpecialPowers.wrap(i == 0 ? pc2 : pc1).id.split(' ', 1)[0], `Expected RTCPeerConnection identifier for i=${i}`); 1320 is(e.negotiation_count, "1", `Expected number of negotiations for i=${i}`); 1321 is(e.is_remote_ice_lite, "false", `Expected remote ICE-Lite for i=${i}`); 1322 is(e.bundle_policy, "balanced", `Expected BundlePolicy for i=${i}`); 1323 is(e.ice_transport_policy, "all", `Expected IceTransportPolicy for i=${i}`); 1324 is(e.num_transports, "1", `Expected number of transports for i=${i}`); 1325 is(e.num_msections_audio_recvonly, "0", `Expected number of audio recvonly m-sections for i=${i}`); 1326 is(e.num_msections_audio_sendonly, "0", `Expected number of audio sendonly m-sections for i=${i}`); 1327 is(e.num_msections_audio_sendrecv, "0", `Expected number of audio sendrecv m-sections for i=${i}`); 1328 is(e.num_msections_video_recvonly, "0", `Expected number of video recvonly m-sections for i=${i}`); 1329 is(e.num_msections_video_sendonly, "0", `Expected number of video sendonly m-sections for i=${i}`); 1330 is(e.num_msections_video_sendrecv, "0", `Expected number of video sendrecv m-sections for i=${i}`); 1331 is(e.num_msections_data, "1", `Expected number of data m-sections for i=${i}`); 1332 } 1333 1334 pc1.close(); 1335 pc2.close(); 1336 }, 1337 1338 ]; 1339 1340 runNetworkTest(async () => { 1341 for (const test of tests) { 1342 info(`Running test: ${test.name}`); 1343 await test(); 1344 info(`Done running test: ${test.name}`); 1345 } 1346 }); 1347 1348 </script> 1349 </pre> 1350 </body> 1351 </html>