summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/mst-content-hint
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/mst-content-hint
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/mst-content-hint')
-rw-r--r--testing/web-platform/tests/mst-content-hint/META.yml3
-rw-r--r--testing/web-platform/tests/mst-content-hint/MediaStreamTrack-contentHint.html111
-rw-r--r--testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationEffect.html129
-rw-r--r--testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationPreference.html129
-rw-r--r--testing/web-platform/tests/mst-content-hint/idlharness.window.js19
5 files changed, 391 insertions, 0 deletions
diff --git a/testing/web-platform/tests/mst-content-hint/META.yml b/testing/web-platform/tests/mst-content-hint/META.yml
new file mode 100644
index 0000000000..7f79eccbaa
--- /dev/null
+++ b/testing/web-platform/tests/mst-content-hint/META.yml
@@ -0,0 +1,3 @@
+spec: https://w3c.github.io/mst-content-hint/
+suggested_reviewers:
+ - alvestrand
diff --git a/testing/web-platform/tests/mst-content-hint/MediaStreamTrack-contentHint.html b/testing/web-platform/tests/mst-content-hint/MediaStreamTrack-contentHint.html
new file mode 100644
index 0000000000..98c88e66ea
--- /dev/null
+++ b/testing/web-platform/tests/mst-content-hint/MediaStreamTrack-contentHint.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<canvas id="canvas">
+</canvas>
+<script>
+
+function createAudioTrack() {
+ ac = new AudioContext();
+ var osc = ac.createOscillator();
+ var dest = ac.createMediaStreamDestination();
+ osc.connect(dest);
+ audio_track = dest.stream.getAudioTracks()[0];
+
+ assert_equals(audio_track.kind, "audio");
+ return audio_track;
+}
+
+function createVideoTrack() {
+ canvas = document.getElementById("canvas");
+ video_track = canvas.captureStream().getVideoTracks()[0];
+
+ assert_equals(video_track.kind, "video");
+ return video_track;
+}
+
+test(t => {
+ audio_track = createAudioTrack();
+ assert_equals("", audio_track.contentHint);
+
+ video_track = createVideoTrack();
+ assert_equals("", video_track.contentHint);
+}, "Tracks have empty default content hint");
+
+test(t => {
+ audio_track = createAudioTrack();
+ audio_track.contentHint = "speech";
+ assert_equals(audio_track.contentHint, "speech");
+ audio_track.contentHint = "music";
+ assert_equals(audio_track.contentHint, "music");
+ audio_track.contentHint = "";
+ assert_equals(audio_track.contentHint, "");
+}, "Accepts valid audio contentHints");
+
+test(t => {
+ audio_track = createAudioTrack();
+ audio_track.contentHint = "speech";
+ assert_equals(audio_track.contentHint, "speech");
+ audio_track.contentHint = "motion";
+ assert_equals(audio_track.contentHint, "speech",
+ "Audio tracks should ignore video-only contentHints.");
+ audio_track.contentHint = "bogus";
+ assert_equals(audio_track.contentHint, "speech",
+ "Audio tracks should ignore garbage contentHints");
+}, "Audio tracks ignore invalid/video contentHints");
+
+test(t => {
+ video_track = createVideoTrack();
+ video_track.contentHint = "motion";
+ assert_equals(video_track.contentHint, "motion");
+ video_track.contentHint = "detail";
+ assert_equals(video_track.contentHint, "detail");
+ video_track.contentHint = "text";
+ assert_equals(video_track.contentHint, "text");
+ video_track.contentHint = "";
+ assert_equals(video_track.contentHint, "");
+}, "Accepts valid video contentHints");
+
+test(t => {
+ video_track = createVideoTrack();
+ video_track.contentHint = "motion";
+ assert_equals(video_track.contentHint, "motion");
+ video_track.contentHint = "speech";
+ assert_equals(video_track.contentHint, "motion",
+ "Video tracks should ignore audio-only contentHints.");
+ video_track.contentHint = "bogus";
+ assert_equals(video_track.contentHint, "motion",
+ "Video tracks should ignore garbage contentHints");
+}, "Video tracks ignore invalid/audio contentHints");
+
+test(t => {
+ video_track = createVideoTrack();
+ video_track.contentHint = "motion";
+ assert_equals(video_track.contentHint, "motion");
+
+ // Cloning a track should preserve contentHint.
+ video_track_clone = video_track.clone();
+ assert_equals(video_track_clone.contentHint, "motion");
+
+ // Changing a cloned track's contentHint should not change the original.
+ video_track_clone.contentHint = "detail";
+ assert_equals(video_track_clone.contentHint, "detail");
+ assert_equals(video_track.contentHint, "motion");
+}, "Cloned video tracks have separate contentHints");
+
+test(t => {
+ audio_track = createAudioTrack();
+ audio_track.contentHint = "speech";
+ assert_equals(audio_track.contentHint, "speech");
+
+ // Cloning a track should preserve contentHint.
+ audio_track_clone = audio_track.clone();
+ assert_equals(audio_track_clone.contentHint, "speech");
+
+ // Changing a cloned track's contentHint should not change the original.
+ audio_track_clone.contentHint = "music";
+ assert_equals(audio_track_clone.contentHint, "music");
+ assert_equals(audio_track.contentHint, "speech");
+}, "Cloned audio tracks have separate contentHints");
+
+</script>
diff --git a/testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationEffect.html b/testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationEffect.html
new file mode 100644
index 0000000000..a2da6cd139
--- /dev/null
+++ b/testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationEffect.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<title>RTCRtpSendParameters degradationPreference effect</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../webrtc/RTCPeerConnection-helper.js"></script>
+<script>
+ 'use strict';
+
+// This file contains tests that check that degradation preference
+// actually has the desired effect. These tests take a long time to run.
+
+// The signal generator will generate a video stream with at least this
+// many bits per second if unconstrained.
+const minUnconstrainedBandwidth = 30000;
+
+// Returns incoming bandwidth usage between stats1 and stats2
+// in bits per second.
+function bandwidth(stats1, stats2) {
+ if (!stats1 || !stats2) {
+ return null;
+ }
+ const transport1 = [...stats1.values()].filter(({type}) => type === 'transport')[0];
+ const transport2 = [...stats2.values()].filter(({type}) => type === 'transport')[0];
+ const bytes = transport2.bytesReceived - transport1.bytesReceived;
+ // If time interval is too short for proper measurement, return null.
+ if (transport1.timestamp > transport2.timestamp - 100) {
+ return null;
+ }
+ // Multiply by 1000 to get per second, multiply by 8 to get bits.
+ const bandwidth = 1000 * 8 * bytes /
+ (transport2.timestamp - transport1.timestamp);
+ return bandwidth;
+}
+
+let oldStats;
+
+// Returns tuple of { bandwidth, fps, x-res, y-res }
+// Updates oldStats.
+async function measureStuff(pc) {
+ const stats = await pc.getStats();
+ if (!oldStats) {
+ oldStats = stats;
+ return {};
+ }
+ // RTCInboundStreamStats
+ const oldRtpList = [...oldStats.values()].filter(({type}) => type === 'inbound-rtp');
+ const inboundRtpList = [...stats.values()].filter(({type}) => type === 'inbound-rtp');
+ const oldRtp = oldRtpList[0];
+ const inboundRtp = inboundRtpList[0];
+ const fps = 1000.0 * (inboundRtp.framesReceived - oldRtp.framesReceived) /
+ (inboundRtp.timestamp - oldRtp.timestamp);
+ const result = {
+ bandwidth: bandwidth(oldStats, stats),
+ fps: fps,
+ width: inboundRtp.frameWidth,
+ height: inboundRtp.frameHeight
+ };
+ oldStats = stats;
+ if (!result.bandwidth) {
+ return {};
+ }
+ // Unbreak for debugging.
+ // con sole.log('Measure: ', performance.now(), " ", JSON.stringify(result));
+ return result;
+}
+
+promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const stream = await getNoiseStream({video: true});
+ t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+ const track = stream.getTracks()[0];
+ const { sender } = pc1.addTransceiver(track);
+
+ let param = sender.getParameters();
+
+ param.degradationPreference = 'maintain-framerate';
+ await sender.setParameters(param);
+
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ exchangeIceCandidates(pc1, pc2);
+ await exchangeOfferAnswer(pc1, pc2);
+ await listenToConnected(pc1);
+ // Allow the keyframe to pass.
+ await new Promise(r => t.step_timeout(r, 1000));
+ // Wait a few seconds to allow things to settle (rampup)
+ // We know that the generator is supposed to produce 640x480
+ // at 10 fps with a bandwidth exceeding 30 kbits/second.
+ await t.step_wait(async () => {
+ const measure = await measureStuff(pc2);
+ return (measure.bandwidth > minUnconstrainedBandwidth &&
+ measure.width == 640 &&
+ measure.fps > 9);
+ }, 'Test error: Preconditions not achieved', 30000, 500);
+
+ // Measure BW, resolution and frame rate over one second
+ // after measurements have stabilized.
+ await new Promise(r => t.step_timeout(r, 1000));
+ const stats1 = await measureStuff(pc2);
+
+ // Constrain BW to 1/2 of measured value
+ const newBandwidth = stats1.bandwidth / 2;
+ // Guard against inappropriate bandwidth
+ assert_greater_than(newBandwidth, minUnconstrainedBandwidth/2,
+ "Test error: Constraint too low");
+
+ const parameters = sender.getParameters();
+ parameters.encodings[0].maxBitrate = newBandwidth;
+ await sender.setParameters(parameters);
+ // Wait until the expected result happens.
+ const kBandwidthMargin = 1.3;
+ // It takes time to adapt to a new bandwidth, time to scale down,
+ // and time to acknowledge that framerate should not be reduced.
+ // Measured time is around 16 seconds.
+ await t.step_wait(async () => {
+ let measure = await measureStuff(pc2);
+ return (measure.bandwidth &&
+ measure.bandwidth < newBandwidth * kBandwidthMargin &&
+ measure.width < stats1.width &&
+ measure.fps > stats1.fps * 0.9);
+ }, 'Adaptation did not succeed',
+ 30000, 500);
+}, 'Maintain-framerate reduces resolution on bandwidth cut', { timeout: 35000 });
+
+</script>
diff --git a/testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationPreference.html b/testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationPreference.html
new file mode 100644
index 0000000000..3573bb5876
--- /dev/null
+++ b/testing/web-platform/tests/mst-content-hint/RTCRtpSendParameters-degradationPreference.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCRtpSendParameters degradationPreference</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ 'use strict';
+
+promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const { sender } = pc.addTransceiver('video');
+
+ let param = sender.getParameters();
+
+ assert_equals(param.degradationPreference, undefined,
+ 'Expect initial param.degradationPreference to be undefined');
+
+ param.degradationPreference = 'maintain-framerate';
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, 'maintain-framerate');
+
+ param.degradationPreference = 'maintain-resolution';
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, 'maintain-resolution');
+
+ param.degradationPreference = 'balanced';
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, 'balanced');
+
+ param.degradationPreference = undefined;
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, undefined);
+}, 'setParameters with degradationPreference set should succeed on video transceiver');
+
+promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const { sender } = pc.addTransceiver('video');
+
+ let param = sender.getParameters();
+
+ assert_equals(param.degradationPreference, undefined,
+ 'Expect initial param.degradationPreference to be undefined');
+
+ param.degradationPreference = undefined;
+
+ await sender.setParameters(param);
+
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, undefined);
+}, 'setParameters with degradationPreference unset should succeed on video transceiver');
+
+promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const { sender } = pc.addTransceiver('video');
+
+ let param = sender.getParameters();
+ param.degradationPreference = 'invalid';
+
+ return promise_rejects_js(t, TypeError, sender.setParameters(param));
+}, 'setParameters with invalid degradationPreference should throw TypeError on video transceiver');
+
+promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const { sender } = pc.addTransceiver('audio');
+
+ let param = sender.getParameters();
+
+ assert_equals(param.degradationPreference, undefined,
+ 'Expect initial param.degradationPreference to be undefined');
+
+ param.degradationPreference = 'maintain-framerate';
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, 'maintain-framerate');
+
+ param.degradationPreference = 'maintain-resolution';
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, 'maintain-resolution');
+
+ param.degradationPreference = 'balanced';
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, 'balanced');
+
+ param.degradationPreference = undefined;
+ await sender.setParameters(param);
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, undefined);
+}, 'setParameters with degradationPreference set should succeed on audio transceiver');
+
+promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const { sender } = pc.addTransceiver('audio');
+
+ let param = sender.getParameters();
+
+ assert_equals(param.degradationPreference, undefined,
+ 'Expect initial param.degradationPreference to be undefined');
+
+ param.degradationPreference = undefined;
+
+ await sender.setParameters(param);
+
+ param = sender.getParameters();
+ assert_equals(param.degradationPreference, undefined);
+}, 'setParameters with degradationPreference unset should succeed on audio transceiver');
+
+promise_test(async t => {
+ const pc = new RTCPeerConnection();
+ t.add_cleanup(() => pc.close());
+ const { sender } = pc.addTransceiver('audio');
+
+ let param = sender.getParameters();
+ param.degradationPreference = 'invalid';
+
+ return promise_rejects_js(t, TypeError, sender.setParameters(param));
+}, 'setParameters with invalid degradationPreference should throw TypeError on audio transceiver');
+
+</script>
diff --git a/testing/web-platform/tests/mst-content-hint/idlharness.window.js b/testing/web-platform/tests/mst-content-hint/idlharness.window.js
new file mode 100644
index 0000000000..ef0e5e9812
--- /dev/null
+++ b/testing/web-platform/tests/mst-content-hint/idlharness.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+// META: script=/webrtc/RTCPeerConnection-helper.js
+
+'use strict';
+
+idl_test(
+ ['mst-content-hint'],
+ ['mediacapture-streams', 'webrtc', 'dom'],
+ async idl_array => {
+ idl_array.add_objects({
+ MediaStreamTrack: ['audioTrack', 'videoTrack'],
+ });
+
+ const stream = await getNoiseStream({ audio: true, video: true });
+ self.audioTrack = stream.getAudioTracks()[0];
+ self.videoTrack = stream.getVideoTracks()[0];
+ }
+);