Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1765005",
title: "Verify audio channel limits for each negotiated audio codec",
});
const matchesChannels = (sdp, codec, channels) =>
!!sdp.match(new RegExp(`a=rtpmap:\\d* ${codec}\/\\d*\/${channels}\r\n`, "g")) ||
(channels <= 1 &&
!!sdp.match(new RegExp(`a=rtpmap:\\d* ${codec}\/\\d*\r\n`, "g")));
async function testAudioChannels(track, codec, channels, accepted, expectedChannels) {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
try {
pc1.addTrack(track);
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
let {type, sdp} = await pc2.createAnswer();
sdp = sdp.replace(new RegExp(`a=rtpmap:(\\d*) ${codec}\/(\\d*)\/?\\d*\r\n`, "g"),
`a=rtpmap:$1 ${codec}/$2/${channels}\r\n`);
const payloadType = Number(sdputils.findCodecId(sdp, codec));
sdp = sdputils.removeAllButPayloadType(sdp, payloadType);
ok(matchesChannels(sdp, codec, channels), "control");
await pc2.setLocalDescription({type, sdp});
is(matchesChannels(pc2.localDescription.sdp, codec, channels),
accepted,
`test pc2.localDescription`);
try {
await pc1.setRemoteDescription(pc2.localDescription);
ok(expectedChannels, "SRD should succeed iff we're expecting channels");
const [receiver] = pc2.getReceivers();
await new Promise(r => receiver.track.onunmute = r);
let stats = await receiver.getStats();
let inboundStat = [...stats.values()].find(({type}) => type == "inbound-rtp");
if (!inboundStat) {
info("work around bug 1765215"); // TODO bug 1765215
await new Promise(r => setTimeout(r, 200));
stats = await receiver.getStats();
inboundStat = [...stats.values()].find(({type}) => type == "inbound-rtp");
}
ok(inboundStat, "has inbound-rtp stats after track unmute (w/workaround)");
const codecStat = stats.get(inboundStat.codecId);
ok(codecStat.mimeType.includes(codec), "correct codec");
is(codecStat.payloadType, payloadType, "correct payloadType");
is(codecStat.channels, expectedChannels, "expected channels");
} catch (e) {
ok(!expectedChannels, "SRD should fail iff we're not expecting channels");
}
} finally {
pc1.close();
pc2.close();
}
}
runNetworkTest(async () => {
const [track] = (await navigator.mediaDevices.getUserMedia({audio: true}))
.getAudioTracks();
try {
for (let [codec, channels, accepted, expectedChannels] of [
["opus", 2, true, 2],
["opus", 1, true, 0],
["opus", 1000, true, 0],
["G722", 1, true, 1],
["G722", 2, true, 0],
["G722", 1000, true, 0],
["PCMU", 1, true, 1],
["PCMU", 2, false, 1],
["PCMU", 1000, false, 1],
["PCMA", 1, true, 1],
["PCMA", 2, false, 1],
["PCMA", 1000, false, 1]
]) {
const testName = `${codec} with ${channels} channel(s) is ` +
`${accepted? "accepted" : "ignored"} and produces ` +
`${expectedChannels || "no"} channels`;
try {
info(`Testing that ${testName}`);
await testAudioChannels(track, codec, channels, accepted, expectedChannels);
} catch (e) {
ok(false, `Error testing that ${testName}: ${e}\n${e.stack}`);
}
}
} finally {
track.stop();
}
});
</script>
</pre>
</body>
</html>