Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!doctype html>
<meta charset=utf-8>
<title>RTX codec integrity checks</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../RTCPeerConnection-helper.js"></script>
<script>
'use strict';
const kProfileIdKey = 'profile-id';
const kLevelIdKey = 'level-id';
// The level-id value for Level X.Y is calculated as (X * 10 + Y) * 3.
// The lowest Level, 1.0, is thus (1 * 10 + 0) * 3 = 30.
const kH265Level1dot0 = '30';
function parseFmtpMap(sdpFmtpLine) {
const map = new Map();
// For each entry (semi-colon separated key=value).
for (let i = 0; i < sdpFmtpLine.length; ++i) {
let entryEnd = sdpFmtpLine.indexOf(';', i);
if (entryEnd == -1) {
entryEnd = sdpFmtpLine.length;
}
const entryStr = sdpFmtpLine.substring(i, entryEnd);
const keyValue = entryStr.split('=');
if (keyValue.length != 2) {
throw 'Failed to parse sdpFmtpLine';
}
map.set(keyValue[0], keyValue[1]);
i = entryEnd;
}
return map;
}
function findCodecWithProfileId(codecs, mimeType, profileId) {
return codecs.find(codec => {
if (codec.mimeType != mimeType) {
return false;
}
return parseFmtpMap(codec.sdpFmtpLine).get(kProfileIdKey) == profileId;
});
}
// Returns `[h265SendCodec, h265RecvCodec]` or aborts the calling test with
// [PRECONDITION_FAILED].
function getH265CodecsOrFailPrecondition() {
const h265SendCodec = RTCRtpSender.getCapabilities('video').codecs.find(
c => c.mimeType == 'video/H265');
assert_implements_optional(
h265SendCodec !== undefined,
`H265 is not available for sending.`);
const h265SendCodecFmtpMap = parseFmtpMap(h265SendCodec.sdpFmtpLine);
const profileId = h265SendCodecFmtpMap.get(kProfileIdKey);
assert_not_equals(profileId, undefined,
`profile-id is missing from sdpFmtpLine`);
const h265RecvCodec = findCodecWithProfileId(
RTCRtpReceiver.getCapabilities('video').codecs, 'video/H265', profileId);
assert_implements_optional(
h265RecvCodec !== undefined,
`H265 profile-id=${profileId} is not available for receiving.`);
return [h265SendCodec, h265RecvCodec];
}
function sdpModifyFmtpLevelId(sdp, newLevelId) {
const lines = sdp.split('\r\n');
for (let i = 0; i < lines.length; ++i) {
if (!lines[i].startsWith('a=fmtp:')) {
continue;
}
const spaceIndex = lines[i].indexOf(' ');
if (spaceIndex == -1) {
continue;
}
const fmtpMap = parseFmtpMap(lines[i].substring(spaceIndex + 1));
if (!fmtpMap.has(kLevelIdKey)) {
continue;
}
fmtpMap.set(kLevelIdKey, newLevelId);
const sdpFmtpLine =
Array.from(fmtpMap, ([key,value]) => `${key}=${value}`).join(';');
lines[i] = lines[i].substring(0, spaceIndex) + ' ' + sdpFmtpLine;
}
return lines.join('\r\n');
}
promise_test(async t => {
const [h265SendCodec, h265RecvCodec] = getH265CodecsOrFailPrecondition();
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const pc1Transceiver = pc1.addTransceiver('video', {direction: 'sendonly'});
pc1Transceiver.setCodecPreferences([h265SendCodec]);
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription();
// Modify SDP to tell `pc1` that `pc2` can only receive level-id=30.
await pc1.setRemoteDescription({
type: 'answer',
sdp: sdpModifyFmtpLevelId(pc2.localDescription.sdp, kH265Level1dot0)
});
// Confirm level-id=30 was negotiated regardless of sender capabilities.
const sender = pc1Transceiver.sender;
const params = sender.getParameters();
assert_equals(params.codecs.length, 1);
const negotiatedFmtpMap = parseFmtpMap(params.codecs[0].sdpFmtpLine);
assert_equals(negotiatedFmtpMap.get(kLevelIdKey), kH265Level1dot0);
}, `Offer to send H265, answer to receive level-id=30 results in level-id=30`);
promise_test(async t => {
const [h265SendCodec, h265RecvCodec] = getH265CodecsOrFailPrecondition();
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const pc1Transceiver = pc1.addTransceiver('video', {direction: 'recvonly'});
pc1Transceiver.setCodecPreferences([h265RecvCodec]);
await pc1.setLocalDescription();
// Modify SDP to tell `pc2` that `pc1` can only receive level-id=30.
await pc2.setRemoteDescription({
type: 'offer',
sdp: sdpModifyFmtpLevelId(pc1.localDescription.sdp, kH265Level1dot0)
});
const [pc2Transceiver] = pc2.getTransceivers();
pc2Transceiver.direction = 'sendonly';
await pc2.setLocalDescription();
await pc1.setRemoteDescription(pc2.localDescription);
// Confirm level-id=30 was negotiated regardless of sender capabilities.
const sender = pc2Transceiver.sender;
const params = sender.getParameters();
assert_equals(params.codecs.length, 1);
const negotiatedFmtpMap = parseFmtpMap(params.codecs[0].sdpFmtpLine);
assert_equals(negotiatedFmtpMap.get(kLevelIdKey), kH265Level1dot0);
// Setting a codec that was negotiated should always work, regardless of the
// level-id in sender capabilities.
params.encodings[0].codec = params.codecs[0];
await sender.setParameters(params);
assert_equals(sender.getParameters().encodings[0].codec.mimeType,
params.codecs[0].mimeType);
assert_equals(sender.getParameters().encodings[0].codec.sdpFmtpLine,
params.codecs[0].sdpFmtpLine);
}, `Offer to receive level-id=30 and set codec from getParameters`);
promise_test(async t => {
const [h265SendCodec, h265RecvCodec] = getH265CodecsOrFailPrecondition();
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const pc1Transceiver = pc1.addTransceiver('video', {direction: 'recvonly'});
pc1Transceiver.setCodecPreferences([h265RecvCodec]);
await pc1.setLocalDescription();
// Modify SDP to tell `pc2` that `pc1` can only receive level-id=30.
await pc2.setRemoteDescription({
type: 'offer',
sdp: sdpModifyFmtpLevelId(pc1.localDescription.sdp, kH265Level1dot0)
});
const [pc2Transceiver] = pc2.getTransceivers();
pc2Transceiver.direction = 'sendonly';
await pc2.setLocalDescription();
await pc1.setRemoteDescription(pc2.localDescription);
// Confirm level-id=30 was negotiated regardless of sender capabilities.
const sender = pc2Transceiver.sender;
const params = sender.getParameters();
assert_equals(params.codecs.length, 1);
const negotiatedFmtpMap = parseFmtpMap(params.codecs[0].sdpFmtpLine);
assert_equals(negotiatedFmtpMap.get(kLevelIdKey), kH265Level1dot0);
// Setting a codec from getCapabilities should work, even if a lower level-id
// was negotiated.
params.encodings[0].codec = h265SendCodec;
await sender.setParameters(params);
assert_equals(sender.getParameters().encodings[0].codec.mimeType,
h265SendCodec.mimeType);
assert_equals(sender.getParameters().encodings[0].codec.sdpFmtpLine,
h265SendCodec.sdpFmtpLine);
}, `Offer to receive level-id=30 and set codec from getCapabilities`);
</script>