blob: 5c81349b1528aaa17f8b17920c15e6664f9709e3 [file] [log] [blame]
Florent Castelli9f783ba2023-05-16 13:09:151<!DOCTYPE html>
2<meta charset="utf-8">
3<title>RTCRtpEncodingParameters codec property</title>
4<script src="/resources/testharness.js"></script>
5<script src="/resources/testharnessreport.js"></script>
6<script src="../webrtc/RTCPeerConnection-helper.js"></script>
7<script src="../webrtc/third_party/sdp/sdp.js"></script>
8<script src="../webrtc/simulcast/simulcast.js"></script>
9<script>
10 'use strict';
11
12 function findFirstCodec(name) {
13 return RTCRtpSender.getCapabilities(name.split('/')[0]).codecs.filter(c => c.mimeType.localeCompare(name, undefined, { sensitivity: 'base' }) === 0)[0];
14 }
15
16 function codecsNotMatching(mimeType) {
17 return RTCRtpSender.getCapabilities(mimeType.split('/')[0]).codecs.filter(c => c.mimeType.localeCompare(mimeType, undefined, {sensitivity: 'base'}) !== 0);
18 }
19
20 function assertCodecEquals(a, b) {
21 assert_equals(a.mimeType, b.mimeType);
22 assert_equals(a.clockRate, b.clockRate);
23 assert_equals(a.channels, b.channels);
24 assert_equals(a.sdpFmtpLine, b.sdpFmtpLine);
25 }
26
27 async function codecsForSender(sender) {
28 const rids = sender.getParameters().encodings.map(e => e.rid);
29 const stats = await sender.getStats();
30 const codecs = [...stats]
31 .filter(([k, v]) => v.type === 'outbound-rtp')
32 .sort(([k, v], [k2, v2]) => rids.indexOf(v.rid) - rids.indexOf(v2.rid))
33 .map(([k, v]) => stats.get(v.codecId).mimeType);
34 return codecs;
35 }
36
37 promise_test(async t => {
38 const pc = new RTCPeerConnection();
39 t.add_cleanup(() => pc.close());
40
41 const { sender } = pc.addTransceiver('audio');
42
43 let param = sender.getParameters();
44 let encoding = param.encodings[0];
45
46 assert_equals(encoding.codec, undefined);
47 }, `Codec should be undefined by default on audio encodings`);
48
49 promise_test(async t => {
50 const pc = new RTCPeerConnection();
51 t.add_cleanup(() => pc.close());
52
53 const { sender } = pc.addTransceiver('video');
54
55 let param = sender.getParameters();
56 let encoding = param.encodings[0];
57
58 assert_equals(encoding.codec, undefined);
59 }, `Codec should be undefined by default on video encodings`);
60
61 promise_test(async t => {
62 const pc = new RTCPeerConnection();
63 t.add_cleanup(() => pc.close());
64
65 const opus = findFirstCodec('audio/opus');
66
67 const { sender } = pc.addTransceiver('audio', {
68 sendEncodings: [{codec: opus}],
69 });
70
71 let param = sender.getParameters();
72 let encoding = param.encodings[0];
73
74 assertCodecEquals(opus, encoding.codec);
75 }, `Creating an audio sender with addTransceiver and codec should work`);
76
77 promise_test(async t => {
78 const pc = new RTCPeerConnection();
79 t.add_cleanup(() => pc.close());
80
81 const vp8 = findFirstCodec('video/VP8');
82
83 const { sender } = pc.addTransceiver('video', {
84 sendEncodings: [{codec: vp8}],
85 });
86
87 let param = sender.getParameters();
88 let encoding = param.encodings[0];
89
90 assertCodecEquals(vp8, encoding.codec);
91 }, `Creating a video sender with addTransceiver and codec should work`);
92
93 promise_test(async t => {
94 const pc = new RTCPeerConnection();
95 t.add_cleanup(() => pc.close());
96
97 const opus = findFirstCodec('audio/opus');
98
99 const { sender } = pc.addTransceiver('audio');
100
101 let param = sender.getParameters();
102 let encoding = param.encodings[0];
103
104 encoding.codec = opus;
105 await sender.setParameters(param);
106 param = sender.getParameters();
107 encoding = param.encodings[0];
108
109 assertCodecEquals(opus, encoding.codec);
110
111 delete encoding.codec;
112 await sender.setParameters(param);
113 param = sender.getParameters();
114 encoding = param.encodings[0];
115
116 assert_equals(encoding.codec, undefined);
117 }, `Setting codec on an audio sender with setParameters should work`);
118
119 promise_test(async t => {
120 const pc = new RTCPeerConnection();
121 t.add_cleanup(() => pc.close());
122
123 const vp8 = findFirstCodec('video/VP8');
124
125 const { sender } = pc.addTransceiver('video');
126
127 let param = sender.getParameters();
128 let encoding = param.encodings[0];
129
130 encoding.codec = vp8;
131 await sender.setParameters(param);
132 param = sender.getParameters();
133 encoding = param.encodings[0];
134
135 assertCodecEquals(vp8, encoding.codec);
136
137 delete encoding.codec;
138 await sender.setParameters(param);
139 param = sender.getParameters();
140 encoding = param.encodings[0];
141
142 assert_equals(encoding.codec, undefined);
143 }, `Setting codec on a video sender with setParameters should work`);
144
145 promise_test(async t => {
146 const pc = new RTCPeerConnection();
147 t.add_cleanup(() => pc.close());
148
149 const newCodec = {
150 mimeType: "audio/newCodec",
151 clockRate: 90000,
152 channel: 2,
153 };
154
155 assert_throws_dom('OperationError', () => pc.addTransceiver('video', {
156 sendEncodings: [{codec: newCodec}],
157 }));
158 }, `Creating an audio sender with addTransceiver and non-existing codec should throw OperationError`);
159
160 promise_test(async t => {
161 const pc = new RTCPeerConnection();
162 t.add_cleanup(() => pc.close());
163
164 const newCodec = {
165 mimeType: "video/newCodec",
166 clockRate: 90000,
167 };
168
169 assert_throws_dom('OperationError', () => pc.addTransceiver('video', {
170 sendEncodings: [{codec: newCodec}],
171 }));
172 }, `Creating a video sender with addTransceiver and non-existing codec should throw OperationError`);
173
174 promise_test(async t => {
175 const pc = new RTCPeerConnection();
176 t.add_cleanup(() => pc.close());
177
178 const newCodec = {
179 mimeType: "audio/newCodec",
180 clockRate: 90000,
181 channel: 2,
182 };
183
184 const { sender } = pc.addTransceiver('audio');
185
186 let param = sender.getParameters();
187 let encoding = param.encodings[0];
188
189 encoding.codec = newCodec;
190 await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
191 }, `Setting a non-existing codec on an audio sender with setParameters should throw InvalidModificationError`);
192
193 promise_test(async t => {
194 const pc = new RTCPeerConnection();
195 t.add_cleanup(() => pc.close());
196
197 const newCodec = {
198 mimeType: "video/newCodec",
199 clockRate: 90000,
200 };
201
202 const { sender } = pc.addTransceiver('video');
203
204 let param = sender.getParameters();
205 let encoding = param.encodings[0];
206
207 encoding.codec = newCodec;
208 await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
209 }, `Setting a non-existing codec on a video sender with setParameters should throw InvalidModificationError`);
210
211 promise_test(async t => {
212 const pc = new RTCPeerConnection();
213 t.add_cleanup(() => pc.close());
214
215 const opus = findFirstCodec('audio/opus');
216 const nonOpus = codecsNotMatching(opus.mimeType);
217
218 const transceiver = pc.addTransceiver('audio');
219 const sender = transceiver.sender;
220
221 transceiver.setCodecPreferences(nonOpus);
222
223 let param = sender.getParameters();
224 let encoding = param.encodings[0];
225
226 encoding.codec = opus;
227 await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
228 }, `Setting a non-preferred codec on an audio sender with setParameters should throw InvalidModificationError`);
229
230 promise_test(async t => {
231 const pc = new RTCPeerConnection();
232 t.add_cleanup(() => pc.close());
233
234 const vp8 = findFirstCodec('video/VP8');
235 const nonVP8 = codecsNotMatching(vp8.mimeType);
236
237 const transceiver = pc.addTransceiver('video');
238 const sender = transceiver.sender;
239
240 transceiver.setCodecPreferences(nonVP8);
241
242 let param = sender.getParameters();
243 let encoding = param.encodings[0];
244
245 encoding.codec = vp8;
246 await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
247 }, `Setting a non-preferred codec on a video sender with setParameters should throw InvalidModificationError`);
248
249 promise_test(async (t) => {
250 const pc1 = new RTCPeerConnection();
251 const pc2 = new RTCPeerConnection();
252 t.add_cleanup(() => pc1.close());
253 t.add_cleanup(() => pc2.close());
254
255 const opus = findFirstCodec('audio/opus');
256 const nonOpus = codecsNotMatching(opus.mimeType);
257
258 const transceiver = pc1.addTransceiver('audio');
259 const sender = transceiver.sender;
260
261 transceiver.setCodecPreferences(nonOpus);
262
263 exchangeIceCandidates(pc1, pc2);
264 await exchangeOfferAnswer(pc1, pc2);
265
266 let param = sender.getParameters();
267 let encoding = param.encodings[0];
268
269 encoding.codec = opus;
270 await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
271 }, `Setting a non-negotiated codec on an audio sender with setParameters should throw InvalidModificationError`);
272
273 promise_test(async (t) => {
274 const pc1 = new RTCPeerConnection();
275 const pc2 = new RTCPeerConnection();
276 t.add_cleanup(() => pc1.close());
277 t.add_cleanup(() => pc2.close());
278
279 const vp8 = findFirstCodec('video/VP8');
280 const nonVP8 = codecsNotMatching(vp8.mimeType);
281
282 const transceiver = pc1.addTransceiver('video');
283 const sender = transceiver.sender;
284
285 transceiver.setCodecPreferences(nonVP8);
286
287 exchangeIceCandidates(pc1, pc2);
288 await exchangeOfferAnswer(pc1, pc2);
289
290 let param = sender.getParameters();
291 let encoding = param.encodings[0];
292
293 encoding.codec = vp8;
294 await promise_rejects_dom(t, "InvalidModificationError", sender.setParameters(param));
295 }, `Setting a non-negotiated codec on a video sender with setParameters should throw InvalidModificationError`);
296
297 promise_test(async (t) => {
298 const pc1 = new RTCPeerConnection();
299 const pc2 = new RTCPeerConnection();
300 t.add_cleanup(() => pc1.close());
301 t.add_cleanup(() => pc2.close());
302
303 const opus = findFirstCodec('audio/opus');
304 const nonOpus = codecsNotMatching(opus.mimeType);
305
306 const transceiver = pc1.addTransceiver('audio', {
307 sendEncodings: [{codec: opus}],
308 });
309 const sender = transceiver.sender;
310
311 exchangeIceCandidates(pc1, pc2);
312 await exchangeOfferAnswer(pc1, pc2);
313
314 let param = sender.getParameters();
315 let encoding = param.encodings[0];
316
317 assertCodecEquals(opus, encoding.codec);
318
319 transceiver.setCodecPreferences(nonOpus);
320 await exchangeOfferAnswer(pc1, pc2);
321
322 param = sender.getParameters();
323 encoding = param.encodings[0];
324
325 assert_equals(encoding.codec, undefined);
326 }, `Codec should be undefined after negotiating away the currently set codec on an audio sender`);
327
328 promise_test(async (t) => {
329 const pc1 = new RTCPeerConnection();
330 const pc2 = new RTCPeerConnection();
331 t.add_cleanup(() => pc1.close());
332 t.add_cleanup(() => pc2.close());
333
334 const vp8 = findFirstCodec('video/VP8');
335 const nonVP8 = codecsNotMatching(vp8.mimeType);
336
337 const transceiver = pc1.addTransceiver('video', {
338 sendEncodings: [{codec: vp8}],
339 });
340 const sender = transceiver.sender;
341
342 exchangeIceCandidates(pc1, pc2);
343 await exchangeOfferAnswer(pc1, pc2);
344
345 let param = sender.getParameters();
346 let encoding = param.encodings[0];
347
348 assertCodecEquals(vp8, encoding.codec);
349
350 transceiver.setCodecPreferences(nonVP8);
351 await exchangeOfferAnswer(pc1, pc2);
352
353 param = sender.getParameters();
354 encoding = param.encodings[0];
355
356 assert_equals(encoding.codec, undefined);
357 }, `Codec should be undefined after negotiating away the currently set codec on a video sender`);
358
359 promise_test(async (t) => {
360 const pc1 = new RTCPeerConnection();
361 const pc2 = new RTCPeerConnection();
362 t.add_cleanup(() => pc1.close());
363 t.add_cleanup(() => pc2.close());
364 const stream = await getNoiseStream({audio:true});
365 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
366
367 const opus = findFirstCodec('audio/opus');
368 const nonOpus = codecsNotMatching(opus.mimeType);
369
370 const transceiver = pc1.addTransceiver(stream.getTracks()[0]);
371 const sender = transceiver.sender;
372
373 transceiver.setCodecPreferences(nonOpus.concat([opus]));
374
375 exchangeIceCandidates(pc1, pc2);
376 await exchangeOfferAnswer(pc1, pc2);
377
378 let codecs = await codecsForSender(sender);
379 assert_not_equals(codecs[0], opus.mimeType);
380
381 let param = sender.getParameters();
382 let encoding = param.encodings[0];
383 encoding.codec = opus;
384
385 await sender.setParameters(param);
386
387 codecs = await codecsForSender(sender);
388 assert_array_equals(codecs, [opus.mimeType]);
389 }, `Stats output-rtp should match the selected codec in non-simulcast usecase on an audio sender`);
390
391 promise_test(async (t) => {
392 const pc1 = new RTCPeerConnection();
393 const pc2 = new RTCPeerConnection();
394 t.add_cleanup(() => pc1.close());
395 t.add_cleanup(() => pc2.close());
396 const stream = await getNoiseStream({video:true});
397 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
398
399 const vp8 = findFirstCodec('video/VP8');
400 const nonVP8 = codecsNotMatching(vp8.mimeType);
401
402 const transceiver = pc1.addTransceiver(stream.getTracks()[0]);
403 const sender = transceiver.sender;
404
405 transceiver.setCodecPreferences(nonVP8.concat([vp8]));
406
407 exchangeIceCandidates(pc1, pc2);
408 await exchangeOfferAnswer(pc1, pc2);
409
410 let codecs = await codecsForSender(sender);
411 assert_not_equals(codecs[0], vp8.mimeType);
412
413 let param = sender.getParameters();
414 let encoding = param.encodings[0];
415 encoding.codec = vp8;
416
417 await sender.setParameters(param);
418
419 codecs = await codecsForSender(sender);
420 assert_array_equals(codecs, [vp8.mimeType]);
421 }, `Stats output-rtp should match the selected codec in non-simulcast usecase on a video sender`);
422</script>