Files
sousa-gecko/testing/web-platform/tests/webrtc/protocol/h265-level-id.https.html
T
Henrik Boström 626de2e4a6 Bug 1941579 [wpt PR 50070] - Fix assert in WPT h265-level-id.https.html, a=testonly
Automatic update from web-platform-tests
Fix assert in WPT h265-level-id.https.html

In preparation for a WebRTC level fix that will make these tests pass,
replace some assert_equals() lines that incorrectly does a reference
comparison when a value comparison is the right thing to do.

RTCCodec is passed by value i.e. the JS object returned by
getParameters(). Instead we should value compare the mimeType and
sdpFmtpLine strings.

Bug: chromium:381407888
Change-Id: Iaa2deff981338bfa86670a3f99241af60ebf845e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6170449
Commit-Queue: Henrik Boström <hbos@chromium.org>
Reviewed-by: Evan Shrubsole <eshr@google.com>
Cr-Commit-Position: refs/heads/main@{#1406069}

--

wpt-commits: 14d1ed3f303636bab2df92893798642cc99df239
wpt-pr: 50070
2025-01-16 08:28:00 +00:00

194 lines
7.1 KiB
HTML

<!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>