Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="sdpUtils.js"></script>
<script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1534687",
title: "getParameters tests (that we can't do in wpt)",
visible: true
});
function compareCodecParam(observed, expected) {
info(`Comparing ${JSON.stringify(observed)} to ${JSON.stringify(expected)}`);
is(observed.payloadType, expected.payloadType);
is(observed.clockRate, expected.clockRate);
is(observed.channels, expected.channels);
is(observed.mimeType.toLowerCase(), expected.mimeType.toLowerCase());
if (expected.hasOwnProperty('sdpFmtpLine')) {
is(observed.sdpFmtpLine, expected.sdpFmtpLine);
}
}
// Does not support disregarding unsupported codecs in the SDP, so is not
// suitable for all test-cases.
function checkCodecsAgainstSDP(codecs, msection) {
const rtpParameters = SDPUtils.parseRtpParameters(msection);
const {kind} = SDPUtils.parseMLine(msection);
is(codecs.length, rtpParameters.codecs.length);
isnot(codecs.length, 0);
for (let i = 0; i < codecs.length; ++i) {
const observed = codecs[i];
const fromSdp = rtpParameters.codecs[i];
const expected = {
payloadType: fromSdp.payloadType,
clockRate: fromSdp.clockRate,
mimeType: `${kind}/${fromSdp.name}`,
};
if (kind == 'audio') {
expected.channels = fromSdp.channels;
}
const fmtps = SDPUtils.matchPrefixAndTrim(msection, `a=fmtp:${fromSdp.payloadType}`);
if (fmtps.length == 1) {
expected.sdpFmtpLine = fmtps[0];
} else {
// compareCodecParam will check if observed.sdpFmtpLine is undefined if we
// set this, but will not perform any checks if we do not.
expected.sdpFmtpLine = undefined;
}
compareCodecParam(observed, expected);
}
}
// SDP with unusual values in fmtp, but in the same formatting that Firefox
// typically uses. This lets us check that we're putting the right param values
// in sdpFmtpLine, if not what appears in the SDP verbatim.
const audioSdp = `v=0
o=- 1878890426675213188 2 IN IP4 127.0.0.1
s=-
t=0 0
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
m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101
c=IN IP4 0.0.0.0
a=sendrecv
a=fmtp:109 maxplaybackrate=48001;stereo=0;useinbandfec=0
a=fmtp:101 0-14
a=ice-pwd:60840251a559417c253d68478b0020fb
a=ice-ufrag:741347dd
a=mid:0
a=msid:{0df6a81e-d3f8-4d0f-ab93-892762bd2af7} {b15b10a1-061b-4685-9ca4-99e110744b2e}
a=rtcp-mux
a=rtpmap:109 opus/48000/2
a=rtpmap:9 G722/8000/1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000/1
a=setup:passive
`;
let videoSdp;
if (navigator.userAgent.includes("Android")) {
// Alternate form with no h264
videoSdp = `v=0
o=- 1878890426675213188 2 IN IP4 127.0.0.1
s=-
t=0 0
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
m=video 9 UDP/TLS/RTP/SAVPF 121 125 120 124 123 122 119
c=IN IP4 0.0.0.0
a=sendonly
a=fmtp:121 max-fs=12277;max-fr=50
a=fmtp:125 apt=121
a=fmtp:120 max-fs=12266;max-fr=40
a=fmtp:124 apt=120
a=fmtp:119 apt=122
a=ice-pwd:60840251a559417c253d68478b0020fb
a=ice-ufrag:741347dd
a=mid:0
a=msid:{debeb004-97f0-44ca-b6b2-a6bd8e42ddb2} {7fcd72c7-b112-49b4-892c-feee2cc9525d}
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:121 VP9/90000
a=rtpmap:125 rtx/90000
a=rtpmap:120 VP8/90000
a=rtpmap:124 rtx/90000
a=rtpmap:123 ulpfec/90000
a=rtpmap:122 red/90000
a=rtpmap:119 rtx/90000
a=setup:passive
`;
} else {
videoSdp = `v=0
o=- 1878890426675213188 2 IN IP4 127.0.0.1
s=-
t=0 0
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
m=video 9 UDP/TLS/RTP/SAVPF 121 125 120 124 126 127 97 98 123 122 119
c=IN IP4 0.0.0.0
a=sendonly
a=fmtp:126 profile-level-id=42e00b;level-asymmetry-allowed=1;packetization-mode=1
a=fmtp:97 profile-level-id=42e00b;level-asymmetry-allowed=1
a=fmtp:121 max-fs=12277;max-fr=50
a=fmtp:125 apt=121
a=fmtp:120 max-fs=12266;max-fr=40
a=fmtp:124 apt=120
a=fmtp:127 apt=126
a=fmtp:98 apt=97
a=fmtp:100 apt=99
a=fmtp:119 apt=122
a=ice-pwd:60840251a559417c253d68478b0020fb
a=ice-ufrag:741347dd
a=mid:0
a=msid:{debeb004-97f0-44ca-b6b2-a6bd8e42ddb2} {7fcd72c7-b112-49b4-892c-feee2cc9525d}
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:121 VP9/90000
a=rtpmap:125 rtx/90000
a=rtpmap:120 VP8/90000
a=rtpmap:124 rtx/90000
a=rtpmap:126 H264/90000
a=rtpmap:127 rtx/90000
a=rtpmap:97 H264/90000
a=rtpmap:98 rtx/90000
a=rtpmap:123 ulpfec/90000
a=rtpmap:122 red/90000
a=rtpmap:119 rtx/90000
a=setup:passive
`;
}
let tests = [
// fmtp testing in wpt requires a verbatim match, which we do not support
// because the sdp's fmtps use the same formatting that Firefox uses.
async function checkSenderFmtpAudioAnswerer() {
const pc = new RTCPeerConnection();
await pc.setRemoteDescription({sdp: audioSdp, type: 'offer'});
await pc.setLocalDescription();
const {codecs} = pc.getSenders()[0].getParameters();
const sections = SDPUtils.splitSections(audioSdp);
checkCodecsAgainstSDP(codecs, sections[1]);
},
async function checkSenderFmtpVideoAnswerer() {
const pc = new RTCPeerConnection();
await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'});
await pc.setLocalDescription();
const {codecs} = pc.getSenders()[0].getParameters();
const sections = SDPUtils.splitSections(videoSdp);
checkCodecsAgainstSDP(codecs, sections[1]);
},
async function checkSenderFmtpAudioOfferer() {
const pc = new RTCPeerConnection();
pc.addTransceiver('audio', { direction: 'sendrecv' });
await pc.setLocalDescription();
await pc.setRemoteDescription({sdp: audioSdp, type: 'answer'});
const {codecs} = pc.getSenders()[0].getParameters();
const sections = SDPUtils.splitSections(audioSdp);
checkCodecsAgainstSDP(codecs, sections[1]);
},
async function checkSenderFmtpVideoOfferer() {
const pc = new RTCPeerConnection();
pc.addTransceiver('video', { direction: 'sendrecv' });
await pc.setLocalDescription();
await pc.setRemoteDescription({sdp: videoSdp, type: 'answer'});
const {codecs} = pc.getSenders()[0].getParameters();
const sections = SDPUtils.splitSections(videoSdp);
checkCodecsAgainstSDP(codecs, sections[1]);
},
// wpt does not allow us to test that .codecs omits things that the pref
// system has disabled
async function checkRedUlpfecDisabled() {
await withPrefs([["media.navigator.video.red_ulpfec_enabled", false]], async () => {
const pc = new RTCPeerConnection();
await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'});
await pc.setLocalDescription();
const {codecs} = pc.getSenders()[0].getParameters();
// Control
is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp8'), true);
// No red or ulpfec
is(codecs.some(c => c.mimeType.toLowerCase() == 'video/red'), false);
is(codecs.some(c => c.mimeType.toLowerCase() == 'video/ulpfec'), false);
}
);
},
async function checkRtxDisabled() {
await withPrefs([["media.peerconnection.video.use_rtx", false]], async () => {
const pc = new RTCPeerConnection();
await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'});
await pc.setLocalDescription();
const {codecs} = pc.getSenders()[0].getParameters();
// Control
is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp8'), true);
// No rtx
is(codecs.some(c => c.mimeType.toLowerCase() == 'video/rtx'), false);
}
);
},
async function checkVP9Disabled() {
await withPrefs([["media.peerconnection.video.vp9_enabled", false]], async () => {
const pc = new RTCPeerConnection();
await pc.setRemoteDescription({sdp: videoSdp, type: 'offer'});
await pc.setLocalDescription();
const {codecs} = pc.getSenders()[0].getParameters();
// Control
is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp8'), true);
// No rtx
is(codecs.some(c => c.mimeType.toLowerCase() == 'video/vp9'), false);
}
);
},
];
if (!navigator.userAgent.includes("Android")) {
tests = tests.concat([
// h264 is currently not supported in wpt because the plugin is never
// installed, so we have some h264 tests here.
async function checkH264Sender() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
const {sender} = pc1.addTransceiver('video');
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription();
await pc1.setRemoteDescription(pc2.localDescription);
{
const {codecs} = pc1.getSenders()[0].getParameters();
info("pc1 codecs: " + JSON.stringify(codecs, null, 2));
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc1.remoteDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
{
const {codecs} = pc2.getSenders()[0].getParameters();
info("pc2 codecs: " + JSON.stringify(codecs, null, 2));
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc2.remoteDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
},
async function checkH264Receiver() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
pc1.addTransceiver('video');
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription();
await pc1.setRemoteDescription(pc2.localDescription);
{
const {codecs} = pc1.getReceivers()[0].getParameters();
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc1.localDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
{
const {codecs} = pc2.getReceivers()[0].getParameters();
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc2.localDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
},
async function checkH264NoLevelAsymmetryInOffer() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
pc1.addTransceiver('video');
await pc1.setLocalDescription();
const mungedOffer = {
sdp: pc1.localDescription.sdp.replace(/level-asymmetry-allowed=1/g, 'level-asymmetry-allowed=0'),
type: 'offer'
};
await pc2.setRemoteDescription(mungedOffer);
await pc2.setLocalDescription();
{
const {codecs} = pc2.getSenders()[0].getParameters();
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc2.remoteDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
{
const {codecs} = pc2.getReceivers()[0].getParameters();
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc2.localDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
},
async function checkH264NoLevelAsymmetryInAnswer() {
const pc1 = new RTCPeerConnection();
const pc2 = new RTCPeerConnection();
pc1.addTransceiver('video');
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription();
const mungedAnswer = {
sdp: pc2.localDescription.sdp.replace(/level-asymmetry-allowed=1/g, 'level-asymmetry-allowed=0'),
type: 'answer'
};
await pc1.setRemoteDescription(mungedAnswer);
{
const {codecs} = pc1.getSenders()[0].getParameters();
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc1.remoteDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
{
const {codecs} = pc1.getReceivers()[0].getParameters();
is(codecs.filter(c => c.mimeType.toLowerCase() == 'video/h264').length, 4);
const sections = SDPUtils.splitSections(pc1.localDescription.sdp);
checkCodecsAgainstSDP(codecs, sections[1]);
}
},
]);
}
runNetworkTest(async () => {
await SpecialPowers.pushPrefEnv({ set: [["media.navigator.video.disable_h264_baseline", false]] });
for (const test of tests) {
info(`Running test: ${test.name}`);
await test();
info(`Done running test: ${test.name}`);
}
});
</script>
</pre>
</body>
</html>