test_peerConnection_audioChannels.html (3700B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <script type="application/javascript" src="pc.js"></script> 5 </head> 6 <body> 7 <pre id="test"> 8 <script type="application/javascript"> 9 10 createHTML({ 11 bug: "1765005", 12 title: "Verify audio channel limits for each negotiated audio codec", 13 }); 14 15 const matchesChannels = (sdp, codec, channels) => 16 !!sdp.match(new RegExp(`a=rtpmap:\\d* ${codec}\/\\d*\/${channels}\r\n`, "g")) || 17 (channels <= 1 && 18 !!sdp.match(new RegExp(`a=rtpmap:\\d* ${codec}\/\\d*\r\n`, "g"))); 19 20 async function testAudioChannels(track, codec, channels, accepted, expectedChannels) { 21 const pc1 = new RTCPeerConnection(); 22 const pc2 = new RTCPeerConnection(); 23 try { 24 pc1.addTrack(track); 25 pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate); 26 pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate); 27 await pc1.setLocalDescription(); 28 await pc2.setRemoteDescription(pc1.localDescription); 29 let {type, sdp} = await pc2.createAnswer(); 30 sdp = sdp.replace(new RegExp(`a=rtpmap:(\\d*) ${codec}\/(\\d*)\/?\\d*\r\n`, "g"), 31 `a=rtpmap:$1 ${codec}/$2/${channels}\r\n`); 32 const payloadType = Number(sdputils.findCodecId(sdp, codec)); 33 sdp = sdputils.removeAllButPayloadType(sdp, payloadType); 34 ok(matchesChannels(sdp, codec, channels), "control"); 35 await pc2.setLocalDescription({type, sdp}); 36 is(matchesChannels(pc2.localDescription.sdp, codec, channels), 37 accepted, 38 `test pc2.localDescription`); 39 try { 40 await pc1.setRemoteDescription(pc2.localDescription); 41 ok(expectedChannels, "SRD should succeed iff we're expecting channels"); 42 const [receiver] = pc2.getReceivers(); 43 await new Promise(r => receiver.track.onunmute = r); 44 let stats = await receiver.getStats(); 45 let inboundStat = [...stats.values()].find(({type}) => type == "inbound-rtp"); 46 if (!inboundStat) { 47 info("work around bug 1765215"); // TODO bug 1765215 48 await new Promise(r => setTimeout(r, 200)); 49 stats = await receiver.getStats(); 50 inboundStat = [...stats.values()].find(({type}) => type == "inbound-rtp"); 51 } 52 ok(inboundStat, "has inbound-rtp stats after track unmute (w/workaround)"); 53 const codecStat = stats.get(inboundStat.codecId); 54 ok(codecStat.mimeType.includes(codec), "correct codec"); 55 is(codecStat.payloadType, payloadType, "correct payloadType"); 56 is(codecStat.channels, expectedChannels, "expected channels"); 57 } catch (e) { 58 ok(!expectedChannels, "SRD should fail iff we're not expecting channels"); 59 } 60 } finally { 61 pc1.close(); 62 pc2.close(); 63 } 64 } 65 66 runNetworkTest(async () => { 67 const [track] = (await navigator.mediaDevices.getUserMedia({audio: true})) 68 .getAudioTracks(); 69 try { 70 for (let [codec, channels, accepted, expectedChannels] of [ 71 ["opus", 2, true, 2], 72 ["opus", 1, true, 0], 73 ["opus", 1000, true, 0], 74 ["G722", 1, true, 1], 75 ["G722", 2, true, 0], 76 ["G722", 1000, true, 0], 77 ["PCMU", 1, true, 1], 78 ["PCMU", 2, false, 1], 79 ["PCMU", 1000, false, 1], 80 ["PCMA", 1, true, 1], 81 ["PCMA", 2, false, 1], 82 ["PCMA", 1000, false, 1] 83 ]) { 84 const testName = `${codec} with ${channels} channel(s) is ` + 85 `${accepted? "accepted" : "ignored"} and produces ` + 86 `${expectedChannels || "no"} channels`; 87 try { 88 info(`Testing that ${testName}`); 89 await testAudioChannels(track, codec, channels, accepted, expectedChannels); 90 } catch (e) { 91 ok(false, `Error testing that ${testName}: ${e}\n${e.stack}`); 92 } 93 } 94 } finally { 95 track.stop(); 96 } 97 }); 98 99 </script> 100 </pre> 101 </body> 102 </html>