blob: 407e855edbd2b05c88e1163ed76bb953e47eabda [file] [log] [blame]
Soares Chend8b19bc2017-07-19 07:44:511<!doctype html>
2<meta charset=utf-8>
3<title>RTCRtpTransceiver.prototype.setCodecPreferences</title>
4<script src="/resources/testharness.js"></script>
5<script src="/resources/testharnessreport.js"></script>
Philipp Hanckeef0384e2020-09-07 09:12:426<script src="./third_party/sdp/sdp.js"></script>
Soares Chend8b19bc2017-07-19 07:44:517<script>
8 'use strict';
9
Philipp Hanckeef0384e2020-09-07 09:12:4210 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:5111 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:4212 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:5113 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:0614 const capabilities = RTCRtpReceiver.getCapabilities('audio');
Soares Chend8b19bc2017-07-19 07:44:5115 transceiver.setCodecPreferences(capabilities.codecs);
Blink WPT Botf664d062024-02-13 19:51:0616 }, `setCodecPreferences() on audio transceiver with codecs returned from RTCRtpReceiver.getCapabilities('audio') should succeed`);
Soares Chend8b19bc2017-07-19 07:44:5117
Philipp Hanckeef0384e2020-09-07 09:12:4218 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:5119 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:4220 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:5121 const transceiver = pc.addTransceiver('video');
22 const capabilities = RTCRtpReceiver.getCapabilities('video');
23 transceiver.setCodecPreferences(capabilities.codecs);
Soares Chend8b19bc2017-07-19 07:44:5124 }, `setCodecPreferences() on video transceiver with codecs returned from RTCRtpReceiver.getCapabilities('video') should succeed`);
25
Philipp Hanckeef0384e2020-09-07 09:12:4226 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:5127 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:4228 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:5129 const transceiver = pc.addTransceiver('audio');
Soares Chend8b19bc2017-07-19 07:44:5130 transceiver.setCodecPreferences([]);
Soares Chend8b19bc2017-07-19 07:44:5131 }, `setCodecPreferences([]) should succeed`);
32
Philipp Hanckeef0384e2020-09-07 09:12:4233 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:5134 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:4235 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:5136 const transceiver = pc.addTransceiver('audio');
phancke@microsoft.com079a2cb2024-02-27 11:06:1937 const capabilities = RTCRtpReceiver.getCapabilities('audio');
Soares Chend8b19bc2017-07-19 07:44:5138 const { codecs } = capabilities;
39
40 if(codecs.length >= 2) {
41 const tmp = codecs[0];
42 codecs[0] = codecs[1];
43 codecs[1] = tmp;
44 }
45
46 transceiver.setCodecPreferences(codecs);
Soares Chend8b19bc2017-07-19 07:44:5147 }, `setCodecPreferences() with reordered codecs should succeed`);
48
Philipp Hanckeef0384e2020-09-07 09:12:4249 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:5150 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:4251 t.add_cleanup(() => pc.close());
Harald Alvestrand3130f022020-02-06 10:50:2752 const transceiver = pc.addTransceiver('video');
Blink WPT Botf664d062024-02-13 19:51:0653 const capabilities = RTCRtpReceiver.getCapabilities('video');
Harald Alvestrand3130f022020-02-06 10:50:2754 const { codecs } = capabilities;
55 // This test verifies that the mandatory VP8 codec is present
Blink WPT Botf664d062024-02-13 19:51:0656 // and can be preferred.
57 const codec = codecs.find(c => c.mimeType === 'video/VP8');
58 assert_true(!!codec, 'VP8 video codec was found');
59 transceiver.setCodecPreferences([codec]);
youennfb8ff0872020-09-03 08:35:0160 }, `setCodecPreferences() with only VP8 should succeed`);
61
62 test(() => {
63 const pc = new RTCPeerConnection();
64 const transceiver = pc.addTransceiver('video');
Blink WPT Botf664d062024-02-13 19:51:0665 const capabilities = RTCRtpReceiver.getCapabilities('video');
youennfb8ff0872020-09-03 08:35:0166 const { codecs } = capabilities;
67 // This test verifies that the mandatory H264 codec is present
Blink WPT Botf664d062024-02-13 19:51:0668 // and can be preferred.
69 const codec = codecs.find(c => c.mimeType === 'video/H264');
70 assert_true(!!codec, 'H264 video codec was found');
71 transceiver.setCodecPreferences([codec]);
youennfb8ff0872020-09-03 08:35:0172 }, `setCodecPreferences() with only H264 should succeed`);
73
74 async function getRTPMapLinesWithCodecAsFirst(firstCodec)
75 {
Blink WPT Botf664d062024-02-13 19:51:0676 const codecs = RTCRtpReceiver.getCapabilities('video').codecs;
77 codecs.forEach((codec, idx) => {
youennfb8ff0872020-09-03 08:35:0178 if (codec.mimeType === firstCodec) {
Blink WPT Botf664d062024-02-13 19:51:0679 codecs.splice(idx, 1);
80 codecs.unshift(codec);
youennfb8ff0872020-09-03 08:35:0181 }
82 });
83
84 const pc = new RTCPeerConnection();
85 const transceiver = pc.addTransceiver('video');
Blink WPT Botf664d062024-02-13 19:51:0686 transceiver.setCodecPreferences(codecs);
youennfb8ff0872020-09-03 08:35:0187 const offer = await pc.createOffer();
88
Blink WPT Botf664d062024-02-13 19:51:0689 return offer.sdp.split('\r\n').filter(line => line.startsWith('a=rtpmap:'));
youennfb8ff0872020-09-03 08:35:0190 }
91
92 promise_test(async () => {
93 const lines = await getRTPMapLinesWithCodecAsFirst('video/H264');
94
95 assert_greater_than(lines.length, 1);
Blink WPT Botf664d062024-02-13 19:51:0696 assert_true(lines[0].indexOf('H264') !== -1, 'H264 should be the first codec');
youennfb8ff0872020-09-03 08:35:0197 }, `setCodecPreferences() should allow setting H264 as first codec`);
98
99 promise_test(async () => {
100 const lines = await getRTPMapLinesWithCodecAsFirst('video/VP8');
101
102 assert_greater_than(lines.length, 1);
Blink WPT Botf664d062024-02-13 19:51:06103 assert_true(lines[0].indexOf('VP8') !== -1, 'VP8 should be the first codec');
youennfb8ff0872020-09-03 08:35:01104 }, `setCodecPreferences() should allow setting VP8 as first codec`);
Harald Alvestrand3130f022020-02-06 10:50:27105
Philipp Hanckeef0384e2020-09-07 09:12:42106 test((t) => {
Harald Alvestrand3130f022020-02-06 10:50:27107 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42108 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:51109 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:06110 const capabilities = RTCRtpReceiver.getCapabilities('video');
Stephen McGruerd5103042020-01-23 21:45:45111 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(capabilities.codecs));
Florent Castellidd2829c2019-05-08 15:36:36112 }, `setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidModificationError`);
113
Philipp Hanckeef0384e2020-09-07 09:12:42114 test((t) => {
Florent Castellidd2829c2019-05-08 15:36:36115 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42116 t.add_cleanup(() => pc.close());
Florent Castellidd2829c2019-05-08 15:36:36117 const transceiver = pc.addTransceiver('audio');
118 const codecs = [{
119 mimeType: 'data',
120 clockRate: 2000,
121 channels: 2,
122 sdpFmtpLine: '0-15'
123 }];
124
Stephen McGruerd5103042020-01-23 21:45:45125 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
Florent Castellidd2829c2019-05-08 15:36:36126 }, `setCodecPreferences() with user defined codec with invalid mimeType should throw InvalidModificationError`);
Soares Chend8b19bc2017-07-19 07:44:51127
Philipp Hanckeef0384e2020-09-07 09:12:42128 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:51129 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42130 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:51131 const transceiver = pc.addTransceiver('audio');
132 const codecs = [{
133 mimeType: 'audio/piepiper',
134 clockRate: 2000,
135 channels: 2,
Florent Castellidd2829c2019-05-08 15:36:36136 sdpFmtpLine: '0-15'
Soares Chend8b19bc2017-07-19 07:44:51137 }];
138
Stephen McGruerd5103042020-01-23 21:45:45139 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
Florent Castellidd2829c2019-05-08 15:36:36140 }, `setCodecPreferences() with user defined codec should throw InvalidModificationError`);
Soares Chend8b19bc2017-07-19 07:44:51141
Philipp Hanckeef0384e2020-09-07 09:12:42142 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:51143 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42144 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:51145 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:06146 const capabilities = RTCRtpReceiver.getCapabilities('audio');
Soares Chend8b19bc2017-07-19 07:44:51147 const codecs = [
148 ...capabilities.codecs,
149 {
150 mimeType: 'audio/piepiper',
151 clockRate: 2000,
152 channels: 2,
Florent Castellidd2829c2019-05-08 15:36:36153 sdpFmtpLine: '0-15'
Soares Chend8b19bc2017-07-19 07:44:51154 }];
155
Stephen McGruerd5103042020-01-23 21:45:45156 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
Florent Castellidd2829c2019-05-08 15:36:36157 }, `setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidModificationError`);
158
Philipp Hanckeef0384e2020-09-07 09:12:42159 test((t) => {
Florent Castellidd2829c2019-05-08 15:36:36160 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42161 t.add_cleanup(() => pc.close());
Florent Castellidd2829c2019-05-08 15:36:36162 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:06163 const capabilities = RTCRtpReceiver.getCapabilities('audio');
Florent Castellidd2829c2019-05-08 15:36:36164 const codecs = [capabilities.codecs[0]];
165 codecs[0].clockRate = codecs[0].clockRate / 2;
166
Stephen McGruerd5103042020-01-23 21:45:45167 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
Florent Castellidd2829c2019-05-08 15:36:36168 }, `setCodecPreferences() with modified codec clock rate should throw InvalidModificationError`);
169
Philipp Hanckeef0384e2020-09-07 09:12:42170 test((t) => {
Florent Castellidd2829c2019-05-08 15:36:36171 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42172 t.add_cleanup(() => pc.close());
Florent Castellidd2829c2019-05-08 15:36:36173 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:06174 const capabilities = RTCRtpReceiver.getCapabilities('audio');
Florent Castellidd2829c2019-05-08 15:36:36175 const codecs = [capabilities.codecs[0]];
176 codecs[0].channels = codecs[0].channels + 11;
177
Stephen McGruerd5103042020-01-23 21:45:45178 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
Florent Castellidd2829c2019-05-08 15:36:36179 }, `setCodecPreferences() with modified codec channel count should throw InvalidModificationError`);
180
Philipp Hanckeef0384e2020-09-07 09:12:42181 test((t) => {
Florent Castellidd2829c2019-05-08 15:36:36182 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42183 t.add_cleanup(() => pc.close());
Florent Castellidd2829c2019-05-08 15:36:36184 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:06185 const capabilities = RTCRtpReceiver.getCapabilities('audio');
Florent Castellidd2829c2019-05-08 15:36:36186 const codecs = [capabilities.codecs[0]];
187 codecs[0].sdpFmtpLine = "modifiedparameter=1";
188
Stephen McGruerd5103042020-01-23 21:45:45189 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
Florent Castellidd2829c2019-05-08 15:36:36190 }, `setCodecPreferences() with modified codec parameters should throw InvalidModificationError`);
Soares Chend8b19bc2017-07-19 07:44:51191
Philipp Hanckeef0384e2020-09-07 09:12:42192 test((t) => {
Soares Chend8b19bc2017-07-19 07:44:51193 const pc = new RTCPeerConnection();
Philipp Hanckeef0384e2020-09-07 09:12:42194 t.add_cleanup(() => pc.close());
Soares Chend8b19bc2017-07-19 07:44:51195 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:06196 const capabilities = RTCRtpReceiver.getCapabilities('audio');
Soares Chend8b19bc2017-07-19 07:44:51197
198 const { codecs } = capabilities;
199 assert_greater_than(codecs.length, 0,
200 'Expect at least one codec available');
201
202 const [ codec ] = codecs;
203 const { channels=2 } = codec;
204 codec.channels = channels+1;
205
Stephen McGruerd5103042020-01-23 21:45:45206 assert_throws_dom('InvalidModificationError', () => transceiver.setCodecPreferences(codecs));
Florent Castellidd2829c2019-05-08 15:36:36207 }, `setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidModificationError`);
Soares Chend8b19bc2017-07-19 07:44:51208
Philipp Hanckeef0384e2020-09-07 09:12:42209 promise_test(async (t) => {
210 const pc = new RTCPeerConnection();
211 t.add_cleanup(() => pc.close());
212 const transceiver = pc.addTransceiver('audio');
Blink WPT Botf664d062024-02-13 19:51:06213 const {codecs} = RTCRtpReceiver.getCapabilities('audio');
Philipp Hanckeef0384e2020-09-07 09:12:42214 // Reorder codecs, put PCMU/PCMA first.
215 let firstCodec;
216 let i;
217 for (i = 0; i < codecs.length; i++) {
218 const codec = codecs[i];
219 if (codec.mimeType === 'audio/PCMU' || codec.mimeType === 'audio/PCMA') {
220 codecs.splice(i, 1);
221 codecs.unshift(codec);
222 firstCodec = codec.mimeType.substr(6);
223 break;
224 }
225 }
226 assert_not_equals(firstCodec, undefined, 'PCMU or PCMA codec not found');
227 transceiver.setCodecPreferences(codecs);
228
229 const offer = await pc.createOffer();
230 const mediaSection = SDPUtils.getMediaSections(offer.sdp)[0];
231 const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
232 assert_equals(rtpParameters.codecs[0].name, firstCodec);
233 }, `setCodecPreferences() modifies the order of audio codecs in createOffer`);
234
235 promise_test(async (t) => {
236 const pc = new RTCPeerConnection();
237 t.add_cleanup(() => pc.close());
238 const transceiver = pc.addTransceiver('video');
Blink WPT Botf664d062024-02-13 19:51:06239 const {codecs} = RTCRtpReceiver.getCapabilities('video');
Philipp Hanckeef0384e2020-09-07 09:12:42240 // Reorder codecs, swap H264 and VP8.
241 let vp8 = -1;
242 let h264 = -1;
243 let firstCodec;
244 let i;
245 for (i = 0; i < codecs.length; i++) {
246 const codec = codecs[i];
youennff8d620c2022-07-29 15:11:26247 if (codec.mimeType === 'video/VP8' && vp8 === -1) {
Philipp Hanckeef0384e2020-09-07 09:12:42248 vp8 = i;
249 if (h264 !== -1) {
250 codecs[vp8] = codecs[h264];
251 codecs[h264] = codec;
252 firstCodec = 'VP8';
253 break;
254 }
255 }
youennff8d620c2022-07-29 15:11:26256 if (codec.mimeType === 'video/H264' && h264 === -1) {
Philipp Hanckeef0384e2020-09-07 09:12:42257 h264 = i;
258 if (vp8 !== -1) {
259 codecs[h264] = codecs[vp8];
260 codecs[vp8] = codec;
261 firstCodec = 'H264';
262 break;
263 }
264 }
265 }
266 assert_not_equals(firstCodec, undefined, 'VP8 and H264 codecs not found');
267 transceiver.setCodecPreferences(codecs);
268
269 const offer = await pc.createOffer();
270 const mediaSection = SDPUtils.getMediaSections(offer.sdp)[0];
271 const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
272 assert_equals(rtpParameters.codecs[0].name, firstCodec);
273 }, `setCodecPreferences() modifies the order of video codecs in createOffer`);
274
Philipp Hancke8d6d3ba2025-01-22 15:41:01275 ['rtx', 'red', 'ulpfec'].forEach(resiliencyMechanism => {
276 promise_test(async (t) => {
277 const pc = new RTCPeerConnection();
278 t.add_cleanup(() => pc.close());
279 const transceiver = pc.addTransceiver('video');
280 const {codecs} = RTCRtpReceiver.getCapabilities('video');
281 const filteredCodecs = codecs.
282 filter(codec => codec.mimeType !== 'video/' + resiliencyMechanism);
283 assert_not_equals(codecs.length, filteredCodecs.length);
284 transceiver.setCodecPreferences(filteredCodecs);
285
286 const offer = await pc.createOffer();
287 const mediaSection = SDPUtils.getMediaSections(offer.sdp)[0];
288 const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
289 assert_equals(rtpParameters.codecs.find(codec => codec.name === resiliencyMechanism),
290 undefined);
291 }, `setCodecPreferences() can remove ${resiliencyMechanism}`);
292 });
293
Philipp Hancke89d651c2024-02-21 12:06:07294 // Tests the note removed as result of discussion in
295 // https://github.com/w3c/webrtc-pc/issues/2933
296 promise_test(async (t) => {
297 const pc1 = new RTCPeerConnection();
298 t.add_cleanup(() => pc1.close());
299 const pc2 = new RTCPeerConnection();
300 t.add_cleanup(() => pc2.close());
301
302 const transceiver = pc1.addTransceiver('video');
303 const {codecs} = RTCRtpReceiver.getCapabilities('video');
304 const vp8 = codecs.find(codec => codec.mimeType === 'video/VP8');
305 const h264 = codecs.find(codec => codec.mimeType === 'video/H264');
306 const thirdCodec = codecs.find(codec => ['video/VP9', 'video/AV1'].includes(codec.mimeType));
307 assert_true(!!vp8);
308 assert_true(!!h264);
309 assert_true(!!thirdCodec);
310
311 transceiver.setCodecPreferences([vp8, thirdCodec]);
312 await pc1.setLocalDescription();
313 await pc2.setRemoteDescription(pc1.localDescription);
314 const transceiver2 = pc2.getTransceivers()[0];
315 transceiver2.setCodecPreferences([h264, thirdCodec, vp8]);
316 await pc2.setLocalDescription();
317 await pc1.setRemoteDescription(pc2.localDescription);
318 const mediaSection = SDPUtils.getMediaSections(pc2.localDescription.sdp)[0];
319 const rtpParameters = SDPUtils.parseRtpParameters(mediaSection);
320 // Order is determined by pc2 but H264 is not present.
321 assert_equals(rtpParameters.codecs.length, 2);
322 assert_equals(rtpParameters.codecs[0].name, thirdCodec.mimeType.substring(6));
323 assert_equals(rtpParameters.codecs[1].name, 'VP8');
324
325 }, `setCodecPreferences() filters on receiver and prefers receiver order`);
326
Dan Baker06fc5f72024-05-29 00:13:01327["audio", "video"].forEach(kind => promise_test(async (t) => {
328 const pc = new RTCPeerConnection();
329 t.add_cleanup(() => pc.close());
330 const [codec] = RTCRtpReceiver.getCapabilities(kind).codecs;
331 codec.mimeType = codec.mimeType.toUpperCase();
332 const transceiver = pc.addTransceiver(kind);
333 transceiver.setCodecPreferences([codec]);
334
335 codec.mimeType = codec.mimeType.toLowerCase();
336 transceiver.setCodecPreferences([codec]);
337 }, `setCodecPreferences should accept ${kind} codecs regardless of mimeType case`));
338
Soares Chend8b19bc2017-07-19 07:44:51339 </script>