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="stats.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1395853",
title: "Verify video content over WebRTC for every video codec",
});
async function testVideoCodec(options = {}, codec) {
const test = new PeerConnectionTest(options);
test.setMediaConstraints([{video: true}], []);
let payloadType;
test.chain.insertBefore("PC_LOCAL_SET_LOCAL_DESCRIPTION", [
function PC_LOCAL_FILTER_OUT_CODECS() {
const id = sdputils.findCodecId(test.originalOffer.sdp, codec.name, codec.offset);
payloadType = Number(id);
const otherIds = [];
for (const otherCodec of codecs) {
const otherId = sdputils.findCodecId(test.originalOffer.sdp, otherCodec.name, otherCodec.offset);
const otherRtpmapMatcher = new RegExp(`a=rtpmap:${otherId}.*\\r\\n`, "gi");
if (codec.offset) {
isnot(id, sdputils.findCodecId(test.originalOffer.sdp, codec.name, 0),
"Different offsets should return different payload types");
}
if (Number(otherId) != id) {
otherIds.push(otherId);
}
}
test.originalOffer.sdp =
sdputils.removeAllButPayloadType(test.originalOffer.sdp, id);
if (codec.hasOwnProperty("fmtpOverride")) {
test.originalOffer.sdp = test.originalOffer.sdp.replace(new RegExp(`(a=fmtp:${id}).*(\\r\\n)`, "gi"), `$1 ${codec.fmtpOverride}$2`);
}
for (const otherId of otherIds) {
ok(!test.originalOffer.sdp.match(new RegExp(`m=.*UDP/TLS/RTP/SAVPF.* ${otherId}[^0-9]`, "gi")),
`Other codec ${otherId} should be removed after filtering`);
}
ok(test.originalOffer.sdp.match(new RegExp(`m=.*UDP/TLS/RTP/SAVPF.* ${id}[^0-9]`, "gi")),
`Tested codec ${id} should remain after filtering`);
// We only set it now, or the framework would remove non-H264 codecs
// for us.
options.h264 = codec.name == "H264";
// Ditto for AV1.
options.av1 = codec.name == "AV1";
},
]);
test.chain.insertAfter("PC_LOCAL_WAIT_FOR_MEDIA_FLOW",
[PC_LOCAL_TEST_LOCAL_STATS]);
test.chain.insertAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW",
[PC_REMOTE_TEST_REMOTE_STATS]);
test.chain.append([
async function PC_LOCAL_TEST_CODEC() {
const stats = await test.pcLocal._pc.getStats();
let codecCount = 0;
stats.forEach(stat => {
if (stat.type == "codec") {
is(codecCount++, 0, "expected only one encode codec stat");
is(stat.payloadType, payloadType, "payloadType as expected");
is(stat.mimeType, `video/${codec.name}`, "mimeType as expected");
is(stat.codecType, "encode", "codecType as expected");
}
});
},
async function PC_REMOTE_TEST_CODEC() {
const stats = await test.pcRemote._pc.getStats();
let codecCount = 0;
stats.forEach(stat => {
if (stat.type == "codec") {
is(codecCount++, 0, "expected only one decode codec stat");
is(stat.payloadType, payloadType, "payloadType as expected");
is(stat.mimeType, `video/${codec.name}`, "mimeType as expected");
is(stat.codecType, "decode", "codecType as expected");
}
});
},
]);
if (!navigator.userAgent.includes("Android")) {
test.chain.append([
async function CHECK_VIDEO_FLOW() {
try {
const h = new VideoStreamHelper();
await h.checkVideoPlaying(
test.pcRemote.remoteMediaElements[0],
10, 10, 128);
ok(true, `Got video flow for codec ${codec.name}, offset ${codec.offset}`);
} catch(e) {
ok(false, `No video flow for codec ${codec.name}, offset ${codec.offset}: ${e}`);
}
},
]);
}
await test.run();
}
// We match the name against the sdp to figure out the payload type,
// so all other present codecs can be removed.
// Use `offset` when there are multiple instances of a codec expected in an sdp.
const codecs = [
{ name: "VP8" },
{ name: "VP9" },
{ name: "H264" },
{ name: "AV1" },
];
runNetworkTest(async (options) => {
const h264Support = checkPlatformH264CodecPrefs();
// This test expects the video being captured will change color. Use fake
// video device as loopback does not currently change.
await pushPrefs(
['media.video_loopback_dev', ''],
['media.navigator.streams.fake', true],
["media.navigator.video.disable_h264_baseline", false],
);
if (h264Support.webrtc) {
codecs.push(
{ name: "H264", offset: 1 },
{ name: "H264", offset: 2 },
{ name: "H264", offset: 2, fmtpOverride: "packetization-mode=1" },
{ name: "H264", offset: 3 },
{ name: "H264", offset: 3, fmtpOverride: "packetization-mode=0" },
{ name: "H264", offset: 3, fmtpOverride: "" }
);
} else {
// With only platform encoders we don't signal packetization-mode=0 as
// not all platforms support slice size control.
codecs.push(
{ name: "H264", offset: 1 },
{ name: "H264", offset: 1, fmtpOverride: "packetization-mode=1" },
);
}
for (const codec of codecs) {
let libWebrtcCodec = true;
if (codec.name == "H264") {
libWebrtcCodec = h264Support.webrtc;
}
// Use builtin webrtc encoders where possible.
await pushPrefs(
['media.webrtc.encoder_creation_strategy', !libWebrtcCodec],
// Don't use MediaDataDecoder with fakeopenh264. Remove this when we
['media.navigator.mediadatadecoder_h264_enabled', !libWebrtcCodec],
);
info(`Testing video for codec ${codec.name} offset ${codec.offset}`);
try {
await timerGuard(testVideoCodec(options, codec), 20000, `codec=${JSON.stringify(codec)}`);
} catch(e) {
ok(false, `Error in test for codec ${codec.name}: ${e}\n${e.stack}`);
}
info(`Tested video for codec ${codec.name}`);
}
});
</script>
</pre>
</body>
</html>