Skip to content

Commit

Permalink
webrtc: add setParameterOptions to sender.setParameters
Browse files Browse the repository at this point in the history
adding a new RTCSetParameterOptions object which has a sequence of
RTCEncodingOptions similar to
WebCodecs VideoEncoderEncodeOptions
  https://w3c.github.io/webcodecs/#dictdef-videoencoderencodeoptions
and its keyFrame flag.

On the native side, this adds the request_key_frame flag to the
RtpEncodingParameters.

WebRTC CL:
  https://webrtc-review.googlesource.com/c/src/+/286741
Spec PRs:
  w3c/webrtc-pc#2885
  w3c/webrtc-extensions#167

Chromestatus feature:
  https://chromestatus.com/feature/5161082937409536

BUG=chromium:1354101

Change-Id: I5bfe266eac5990b1921212babdee1af35edc4242
  • Loading branch information
fippo authored and chromium-wpt-export-bot committed Jan 15, 2024
1 parent 1643e87 commit 73e87f5
Showing 1 changed file with 94 additions and 0 deletions.
94 changes: 94 additions & 0 deletions webrtc/RTCRtpSender-setParameters-keyFrame.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<!doctype html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>RTCRtpSender.prototype.setParameters for generating keyFrames</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
<script>
'use strict';

// https://w3c.github.io/webrtc-extensions/#rtcrtpsender-setparameters-keyframe

async function waitForKeyFrameCount(t, pc, spatialLayer, minimumKeyFrames) {
// return after 5 seconds.
const startTime = performance.now();
while (true) {
const report = await pc.getStats();
const stats = [...report.values()].find(({type, rid}) => type === 'outbound-rtp' && rid === spatialLayer);
if (stats && stats.keyFramesEncoded >= minimumKeyFrames) {
return stats;
}
await new Promise(r => t.step_timeout(r, 100));
if (performance.now() > startTime + 5000) {
break;
}
}
}

promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
// Video must be small enough to reach a key frame of the right size immediately.
const stream = await getNoiseStream({video: {width: 320, height: 160}});
t.add_cleanup(() => stream.getTracks().forEach(t => t.stop()));

const sender = pc1.addTrack(stream.getTracks()[0], stream);
exchangeIceCandidates(pc1, pc2);
await exchangeOfferAnswer(pc1, pc2);

const rid = undefined;
const first_stats = await waitForKeyFrameCount(t, pc1, rid, 1);
assert_true(!!first_stats);
sender.setParameters(sender.getParameters(), {
encodingOptions: [{keyFrame: true}],
});
const second_stats = await waitForKeyFrameCount(t, pc1, rid, first_stats.keyFramesEncoded + 1);
assert_true(!!second_stats);
assert_greater_than(second_stats.keyFramesEncoded, first_stats.keyFramesEncoded);
}, `setParameters() second argument can be used to trigger keyFrame generation`);

promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
// Video must be small enough to reach a key frame of the right size immediately.
const stream = await getNoiseStream({video: {width: 640, height: 360}});
t.add_cleanup(() => stream.getTracks().forEach(t => t.stop()));

const {sender} = pc1.addTransceiver(stream.getTracks()[0], {
streams: [stream],
sendEncodings: [{rid: 0}, {rid: 1}],
});
exchangeIceCandidates(pc1, pc2);
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription();
await pc1.setRemoteDescription({type: 'answer', sdp: pc2.localDescription.sdp +
'a=rid:0 recv\r\n' +
'a=rid:1 recv\r\n' +
'a=simulcast:recv 0;1\r\n'
});

const first_stats_l0 = await waitForKeyFrameCount(t, pc1, "0", 1);
assert_true(!!first_stats_l0);
const first_stats_l1 = await waitForKeyFrameCount(t, pc1, "1", 1);
assert_true(!!first_stats_l1);

// Generate a keyframe on the second layer. This may, depending on the encoder, force
// a key frame on the first layer as well.
sender.setParameters(sender.getParameters(), {
encodingOptions: [{keyFrame: false}, {keyFrame: true}],
});
const second_stats_l1 = await waitForKeyFrameCount(t, pc1, "1", first_stats_l1.keyFramesEncoded + 1);
assert_true(!!second_stats_l1);
assert_greater_than(second_stats_l1.keyFramesEncoded, first_stats_l1.keyFramesEncoded);

const second_stats_l0 = await waitForKeyFrameCount(t, pc1, "0", first_stats_l0.keyFramesEncoded);
assert_true(!!second_stats_l0);
assert_greater_than_equal(second_stats_l0.keyFramesEncoded, first_stats_l0.keyFramesEncoded);
}, `setParameters() second argument can be used to trigger keyFrame generation (simulcast)`);
</script>

0 comments on commit 73e87f5

Please sign in to comment.