blob: 97205714f0739cdc9f7b5b21eecbc32162b60e39 [file] [log] [blame]
Soares Chen0042ebb2017-06-05 13:42:061<!doctype html>
2<meta charset=utf-8>
3<title>RTCPeerConnection.prototype.addTransceiver</title>
4<script src="/resources/testharness.js"></script>
5<script src="/resources/testharnessreport.js"></script>
Soares Chen607f71c2017-06-27 15:23:376<script src="RTCPeerConnection-helper.js"></script>
Soares Chen0042ebb2017-06-05 13:42:067<script>
8 'use strict';
9
10 // Test is based on the following editor draft:
Soares Chen7d2c2d72017-06-21 05:09:3611 // https://rawgit.com/w3c/webrtc-pc/cc8d80f455b86c8041d63bceb8b457f45c72aa89/webrtc.html
Soares Chen607f71c2017-06-27 15:23:3712
Soares Chen7d2c2d72017-06-21 05:09:3613 // The following helper functions are called from RTCPeerConnection-helper.js:
14 // generateMediaStreamTrack()
Soares Chen0042ebb2017-06-05 13:42:0615
16 /*
Soares Chen7d2c2d72017-06-21 05:09:3617 5.1. RTCPeerConnection Interface Extensions
18
19 partial interface RTCPeerConnection {
20 sequence<RTCRtpSender> getSenders();
21 sequence<RTCRtpReceiver> getReceivers();
22 sequence<RTCRtpTransceiver> getTransceivers();
23 RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind,
24 optional RTCRtpTransceiverInit init);
25 ...
26 };
27
28 dictionary RTCRtpTransceiverInit {
29 RTCRtpTransceiverDirection direction = "sendrecv";
30 sequence<MediaStream> streams;
31 sequence<RTCRtpEncodingParameters> sendEncodings;
32 };
33
Soares Chene01045d2017-06-21 05:16:1934 enum RTCRtpTransceiverDirection {
35 "sendrecv",
36 "sendonly",
37 "recvonly",
38 "inactive"
39 };
40
Soares Chen7d2c2d72017-06-21 05:09:3641 5.2. RTCRtpSender Interface
42
43 interface RTCRtpSender {
44 readonly attribute MediaStreamTrack? track;
45 ...
46 };
47
48 5.3. RTCRtpReceiver Interface
49
50 interface RTCRtpReceiver {
51 readonly attribute MediaStreamTrack track;
52 ...
53 };
54
55 5.4. RTCRtpTransceiver Interface
56
57 interface RTCRtpTransceiver {
58 readonly attribute DOMString? mid;
59 [SameObject]
60 readonly attribute RTCRtpSender sender;
61 [SameObject]
62 readonly attribute RTCRtpReceiver receiver;
63 readonly attribute boolean stopped;
64 readonly attribute RTCRtpTransceiverDirection direction;
65 readonly attribute RTCRtpTransceiverDirection? currentDirection;
66 ...
67 };
Soares Chen607f71c2017-06-27 15:23:3768
69 Note
70 While addTrack checks if the MediaStreamTrack given as an argument is
71 already being sent to avoid sending the same MediaStreamTrack twice,
72 the other ways do not, allowing the same MediaStreamTrack to be sent
73 several times simultaneously.
Soares Chen0042ebb2017-06-05 13:42:0674 */
75
76 /*
Soares Chen7d2c2d72017-06-21 05:09:3677 5.1. addTransceiver
78 3. If the first argument is a string, let it be kind and run the following steps:
79 1. If kind is not a legal MediaStreamTrack kind, throw a TypeError.
80 */
81 test(t => {
82 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:2183 t.add_cleanup(() => pc.close());
84
Soares Chen172fe9d2017-07-18 09:30:1885 assert_idl_attribute(pc, 'addTransceiver');
Soares Chen7d2c2d72017-06-21 05:09:3686 assert_throws(new TypeError(), () => pc.addTransceiver('invalid'));
87 }, 'addTransceiver() with string argument as invalid kind should throw TypeError');
88
89 /*
90 5.1. addTransceiver
91 The initial value of mid is null.
92
93 3. If the dictionary argument is present, let direction be the value of the
94 direction member. Otherwise let direction be sendrecv.
95 4. If the first argument is a string, let it be kind and run the following steps:
96 2. Let track be null.
97 8. Create an RTCRtpSender with track, streams and sendEncodings and let
98 sender be the result.
99 9. Create an RTCRtpReceiver with kind and let receiver be the result.
100 10. Create an RTCRtpTransceiver with sender, receiver and direction, and let
101 transceiver be the result.
102 11. Add transceiver to connection's set of transceivers.
103
104 5.2. RTCRtpSender Interface
105 Create an RTCRtpSender
106 2. Set sender.track to track.
107
108 5.3. RTCRtpReceiver Interface
109 Create an RTCRtpReceiver
110 2. Let track be a new MediaStreamTrack object [GETUSERMEDIA]. The source of
111 track is a remote source provided by receiver.
112 3. Initialize track.kind to kind.
113 5. Initialize track.label to the result of concatenating the string "remote "
114 with kind.
115 6. Initialize track.readyState to live.
116 7. Initialize track.muted to true.
117 8. Set receiver.track to track.
118
119 5.4. RTCRtpTransceiver Interface
120 Create an RTCRtpTransceiver
121 2. Set transceiver.sender to sender.
122 3. Set transceiver.receiver to receiver.
123 4. Let transceiver have a [[Direction]] internal slot, initialized to direction.
124 5. Let transceiver have a [[CurrentDirection]] internal slot, initialized
125 to null.
126 6. Set transceiver.stopped to false.
Soares Chen0042ebb2017-06-05 13:42:06127 */
128 test(t => {
129 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21130 t.add_cleanup(() => pc.close());
Soares Chen0042ebb2017-06-05 13:42:06131
Soares Chen172fe9d2017-07-18 09:30:18132 assert_idl_attribute(pc, 'addTransceiver');
Soares Chen0042ebb2017-06-05 13:42:06133
134 const transceiver = pc.addTransceiver('audio');
135 assert_true(transceiver instanceof RTCRtpTransceiver,
136 'Expect transceiver to be instance of RTCRtpTransceiver');
137
138 assert_equals(transceiver.mid, null);
139 assert_equals(transceiver.stopped, false);
140 assert_equals(transceiver.direction, 'sendrecv');
Soares Chen7d2c2d72017-06-21 05:09:36141 assert_equals(transceiver.currentDirection, null);
Soares Chen0042ebb2017-06-05 13:42:06142
143 assert_array_equals([transceiver], pc.getTransceivers(),
144 `Expect added transceiver to be the only element in connection's list of transceivers`);
145
146 const sender = transceiver.sender;
147
148 assert_true(sender instanceof RTCRtpSender,
149 'Expect sender to be instance of RTCRtpSender');
150
151 assert_equals(sender.track, null);
152
153 assert_array_equals([sender], pc.getSenders(),
154 `Expect added sender to be the only element in connection's list of senders`);
155
156 const receiver = transceiver.receiver;
157 assert_true(receiver instanceof RTCRtpReceiver,
158 'Expect receiver to be instance of RTCRtpReceiver');
159
Soares Chen172fe9d2017-07-18 09:30:18160 const track = receiver.track;
Soares Chen0042ebb2017-06-05 13:42:06161 assert_true(track instanceof MediaStreamTrack,
162 'Expect receiver.track to be instance of MediaStreamTrack');
163
164 assert_equals(track.kind, 'audio');
165 assert_equals(track.label, 'remote audio');
166 assert_equals(track.readyState, 'live');
167 assert_equals(track.muted, true);
168
169 assert_array_equals([receiver], pc.getReceivers(),
170 `Expect added receiver to be the only element in connection's list of receivers`);
171
172 }, `addTransceiver('audio') should return an audio transceiver`);
173
174 test(t => {
175 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21176 t.add_cleanup(() => pc.close());
Soares Chen0042ebb2017-06-05 13:42:06177
Soares Chen172fe9d2017-07-18 09:30:18178 assert_idl_attribute(pc, 'addTransceiver');
Soares Chen0042ebb2017-06-05 13:42:06179
180 const transceiver = pc.addTransceiver('video');
181 assert_true(transceiver instanceof RTCRtpTransceiver,
182 'Expect transceiver to be instance of RTCRtpTransceiver');
183
184 assert_equals(transceiver.mid, null);
185 assert_equals(transceiver.stopped, false);
186 assert_equals(transceiver.direction, 'sendrecv');
187
188 assert_array_equals([transceiver], pc.getTransceivers(),
189 `Expect added transceiver to be the only element in connection's list of transceivers`);
190
191 const sender = transceiver.sender;
192
193 assert_true(sender instanceof RTCRtpSender,
194 'Expect sender to be instance of RTCRtpSender');
195
196 assert_equals(sender.track, null);
197
198 assert_array_equals([sender], pc.getSenders(),
199 `Expect added sender to be the only element in connection's list of senders`);
200
201 const receiver = transceiver.receiver;
202 assert_true(receiver instanceof RTCRtpReceiver,
203 'Expect receiver to be instance of RTCRtpReceiver');
204
Soares Chen172fe9d2017-07-18 09:30:18205 const track = receiver.track;
Soares Chen0042ebb2017-06-05 13:42:06206 assert_true(track instanceof MediaStreamTrack,
207 'Expect receiver.track to be instance of MediaStreamTrack');
208
209 assert_equals(track.kind, 'video');
210 assert_equals(track.label, 'remote video');
211 assert_equals(track.readyState, 'live');
212 assert_equals(track.muted, true);
213
214 assert_array_equals([receiver], pc.getReceivers(),
215 `Expect added receiver to be the only element in connection's list of receivers`);
216
217 }, `addTransceiver('video') should return a video transceiver`);
218
Soares Chene01045d2017-06-21 05:16:19219 test(t => {
220 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21221 t.add_cleanup(() => pc.close());
222
Soares Chene01045d2017-06-21 05:16:19223 const transceiver = pc.addTransceiver('audio', { direction: 'sendonly' });
224 assert_equals(transceiver.direction, 'sendonly');
225 }, `addTransceiver() with direction sendonly should have result transceiver.direction be the same`);
226
227 test(t => {
228 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21229 t.add_cleanup(() => pc.close());
230
Soares Chene01045d2017-06-21 05:16:19231 const transceiver = pc.addTransceiver('audio', { direction: 'inactive' });
232 assert_equals(transceiver.direction, 'inactive');
233 }, `addTransceiver() with direction inactive should have result transceiver.direction be the same`);
234
235 test(t => {
236 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21237 t.add_cleanup(() => pc.close());
238
Soares Chen172fe9d2017-07-18 09:30:18239 assert_idl_attribute(pc, 'addTransceiver');
Soares Chene01045d2017-06-21 05:16:19240 assert_throws(new TypeError(), () =>
241 pc.addTransceiver('audio', { direction: 'invalid' }));
242 }, `addTransceiver() with invalid direction should throw TypeError`);
243
Soares Chen0042ebb2017-06-05 13:42:06244 /*
Soares Chen607f71c2017-06-27 15:23:37245 5.1. addTransceiver
Soares Chen7d2c2d72017-06-21 05:09:36246 5. If the first argument is a MediaStreamTrack , let it be track and let
Soares Chen607f71c2017-06-27 15:23:37247 kind be track.kind.
Soares Chen607f71c2017-06-27 15:23:37248 */
249 test(t => {
250 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21251 t.add_cleanup(() => pc.close());
Soares Chen607f71c2017-06-27 15:23:37252
Soares Chenec114aa2017-11-20 09:02:21253 const track = generateMediaStreamTrack('audio');
Soares Chen607f71c2017-06-27 15:23:37254 const transceiver = pc.addTransceiver(track);
255 const { sender, receiver } = transceiver;
256
257 assert_true(sender instanceof RTCRtpSender,
258 'Expect sender to be instance of RTCRtpSender');
259
260 assert_true(receiver instanceof RTCRtpReceiver,
261 'Expect receiver to be instance of RTCRtpReceiver');
262
263 assert_equals(sender.track, track,
264 'Expect sender.track should be the track that is added');
265
Soares Chen172fe9d2017-07-18 09:30:18266 const receiverTrack = receiver.track;
Soares Chen607f71c2017-06-27 15:23:37267 assert_true(receiverTrack instanceof MediaStreamTrack,
268 'Expect receiver.track to be instance of MediaStreamTrack');
269
270 assert_equals(receiverTrack.kind, 'audio',
271 `receiver.track should have the same kind as added track's kind`);
272
273 assert_equals(receiverTrack.label, 'remote audio');
274 assert_equals(receiverTrack.readyState, 'live');
275 assert_equals(receiverTrack.muted, true);
276
277 assert_array_equals([transceiver], pc.getTransceivers(),
278 `Expect added transceiver to be the only element in connection's list of transceivers`);
279
280 assert_array_equals([sender], pc.getSenders(),
281 `Expect added sender to be the only element in connection's list of senders`);
282
283 assert_array_equals([receiver], pc.getReceivers(),
284 `Expect added receiver to be the only element in connection's list of receivers`);
285
286 }, 'addTransceiver(track) should have result with sender.track be given track');
287
288 test(t => {
289 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21290 t.add_cleanup(() => pc.close());
Soares Chen607f71c2017-06-27 15:23:37291
Soares Chenec114aa2017-11-20 09:02:21292 const track = generateMediaStreamTrack('audio');
Soares Chen607f71c2017-06-27 15:23:37293 const transceiver1 = pc.addTransceiver(track);
294 const transceiver2 = pc.addTransceiver(track);
295
296 assert_not_equals(transceiver1, transceiver2);
297
298 const sender1 = transceiver1.sender;
299 const sender2 = transceiver2.sender;
300
301 assert_not_equals(sender1, sender2);
302 assert_equals(transceiver1.sender.track, track);
303 assert_equals(transceiver2.sender.track, track);
304
305 const transceivers = pc.getTransceivers();
306 assert_equals(transceivers.length, 2);
307 assert_true(transceivers.includes(transceiver1));
308 assert_true(transceivers.includes(transceiver2));
309
310 const senders = pc.getSenders();
311 assert_equals(senders.length, 2);
312 assert_true(senders.includes(sender1));
313 assert_true(senders.includes(sender2));
314
315 }, 'addTransceiver(track) multiple times should create multiple transceivers');
316
Soares Chen172fe9d2017-07-18 09:30:18317 /*
318 5.1. addTransceiver
319 6. Verify that each rid value in sendEncodings is composed only of
320 case-sensitive alphanumeric characters (a-z, A-Z, 0-9) up to a maximum
321 of 16 characters. If one of the RIDs does not meet these requirements,
322 throw a TypeError.
323 */
Soares Chenec114aa2017-11-20 09:02:21324 test(t => {
Soares Chen172fe9d2017-07-18 09:30:18325 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21326 t.add_cleanup(() => pc.close());
Soares Chen172fe9d2017-07-18 09:30:18327 assert_idl_attribute(pc, 'addTransceiver');
328
329 assert_throws(new TypeError(), () =>
330 pc.addTransceiver('audio', {
331 sendEncodings: [{
332 rid: '@Invalid!'
333 }]
334 }));
335 }, 'addTransceiver() with rid containing invalid non-alphanumeric characters should throw TypeError');
336
Soares Chenec114aa2017-11-20 09:02:21337 test(t => {
Soares Chen172fe9d2017-07-18 09:30:18338 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21339 t.add_cleanup(() => pc.close());
Soares Chen172fe9d2017-07-18 09:30:18340 assert_idl_attribute(pc, 'addTransceiver');
341
342 assert_throws(new TypeError(), () =>
343 pc.addTransceiver('audio', {
344 sendEncodings: [{
345 rid: 'a'.repeat(17)
346 }]
347 }));
348 }, 'addTransceiver() with rid longer than 16 characters should throw TypeError');
349
Soares Chenec114aa2017-11-20 09:02:21350 test(t => {
Soares Chen172fe9d2017-07-18 09:30:18351 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21352 t.add_cleanup(() => pc.close());
Soares Chen172fe9d2017-07-18 09:30:18353 pc.addTransceiver('audio', {
354 sendEncodings: [{
355 rid: 'foo'
356 }]
357 });
358 }, `addTransceiver() with valid rid value should succeed`);
359
360 /*
361 5.1. addTransceiver
362 7. If any RTCRtpEncodingParameters dictionary in sendEncodings contains a
363 read-only parameter other than rid, throw an InvalidAccessError.
364
365 - The sendEncodings argument can be used to specify the number of offered
366 simulcast encodings, and optionally their RIDs and encoding parameters.
367 Aside from rid , all read-only parameters in the RTCRtpEncodingParameters
368 dictionaries, such as ssrc, must be left unset, or an error will be thrown.
369 */
Soares Chenec114aa2017-11-20 09:02:21370 test(t => {
Soares Chen172fe9d2017-07-18 09:30:18371 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21372 t.add_cleanup(() => pc.close());
Soares Chen172fe9d2017-07-18 09:30:18373
374 assert_throws('InvalidAccessError', () =>
375 pc.addTransceiver('audio', {
376 sendEncodings: [{
377 ssrc: 2
378 }]
379 }));
380 }, `addTransceiver() with readonly ssrc set should throw InvalidAccessError`);
381
Soares Chenec114aa2017-11-20 09:02:21382 test(t => {
Soares Chen172fe9d2017-07-18 09:30:18383 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21384 t.add_cleanup(() => pc.close());
Soares Chen172fe9d2017-07-18 09:30:18385
386 assert_throws('InvalidAccessError', () =>
387 pc.addTransceiver('audio', {
388 sendEncodings: [{
389 rtx: {
390 ssrc: 2
391 }
392 }]
393 }));
394 }, `addTransceiver() with readonly rtx set should throw InvalidAccessError`);
395
Soares Chenec114aa2017-11-20 09:02:21396 test(t => {
Soares Chen172fe9d2017-07-18 09:30:18397 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21398 t.add_cleanup(() => pc.close());
Soares Chen172fe9d2017-07-18 09:30:18399
400 assert_throws('InvalidAccessError', () =>
401 pc.addTransceiver('audio', {
402 sendEncodings: [{
403 fec: {
404 ssrc: 2
405 }
406 }]
407 }));
408 }, `addTransceiver() with readonly fec set should throw InvalidAccessError`);
409
Soares Chenec114aa2017-11-20 09:02:21410 test(t => {
Soares Chen172fe9d2017-07-18 09:30:18411 const pc = new RTCPeerConnection();
Soares Chenec114aa2017-11-20 09:02:21412 t.add_cleanup(() => pc.close());
413
Soares Chen172fe9d2017-07-18 09:30:18414 pc.addTransceiver('audio', {
415 sendEncodings: [{
416 dtx: 'enabled',
417 active: false,
418 priority: 'low',
419 ptime: 5,
420 maxBitrate: 8,
421 maxFramerate: 25,
422 rid: 'foo'
423 }]
424 });
425 }, `addTransceiver() with valid sendEncodings should succeed`);
426
Soares Chen607f71c2017-06-27 15:23:37427 /*
428 TODO
429 5.1. addTransceiver
Soares Chen7d2c2d72017-06-21 05:09:36430 - Adding a transceiver will cause future calls to createOffer to add a media
431 description for the corresponding transceiver, as defined in [JSEP]
432 (section 5.2.2.).
Soares Chen607f71c2017-06-27 15:23:37433
Soares Chen7d2c2d72017-06-21 05:09:36434 - Setting a new RTCSessionDescription may change mid to a non-null value,
435 as defined in [JSEP] (section 5.5. and section 5.6.).
436
Soares Chen607f71c2017-06-27 15:23:37437 1. If the dictionary argument is present, and it has a streams member, let
438 streams be that list of MediaStream objects.
Soares Chen7d2c2d72017-06-21 05:09:36439
Soares Chen7d2c2d72017-06-21 05:09:36440 5.2. RTCRtpSender Interface
441 Create an RTCRtpSender
442 3. Let sender have an [[associated MediaStreams]] internal slot, representing
443 a list of MediaStream objects that the MediaStreamTrack object of this
444 sender is associated with.
445
446 4. Set sender's [[associated MediaStreams]] slot to streams.
447
448 5. Let sender have a [[send encodings]] internal slot, representing a list
449 of RTCRtpEncodingParameters dictionaries.
450
451 6. If sendEncodings is given as input to this algorithm, and is non-empty,
452 set the [[send encodings]] slot to sendEncodings. Otherwise, set it to a
453 list containing a single RTCRtpEncodingParameters with active set to true.
454
455 5.3. RTCRtpReceiver Interface
456 Create an RTCRtpReceiver
457 4. If an id string, id, was given as input to this algorithm, initialize
458 track.id to id. (Otherwise the value generated when track was created
459 will be used.)
460
461 Tested in RTCPeerConnection-onnegotiationneeded.html
462 5.1. addTransceiver
463 12. Update the negotiation-needed flag for connection.
464
465 Out of Scope
466 5.1. addTransceiver
467 8. If sendEncodings is set, then subsequent calls to createOffer will be
Soares Chen607f71c2017-06-27 15:23:37468 configured to send multiple RTP encodings as defined in [JSEP]
Soares Chen7d2c2d72017-06-21 05:09:36469 (section 5.2.2. and section 5.2.1.).
470
471 When setRemoteDescription is called with a corresponding remote
472 description that is able to receive multiple RTP encodings as defined
473 in [JSEP] (section 3.7.), the RTCRtpSender may send multiple RTP
474 encodings and the parameters retrieved via the transceiver's
475 sender.getParameters() will reflect the encodings negotiated.
476
477 9. This specification does not define how to configure createOffer to
Soares Chen607f71c2017-06-27 15:23:37478 receive multiple RTP encodings. However when setRemoteDescription is
479 called with a corresponding remote description that is able to send
480 multiple RTP encodings as defined in [JSEP], the RTCRtpReceiver may
481 receive multiple RTP encodings and the parameters retrieved via the
482 transceiver's receiver.getParameters() will reflect the encodings
483 negotiated.
Soares Chen607f71c2017-06-27 15:23:37484
Soares Chen7d2c2d72017-06-21 05:09:36485 Coverage Report
486 Tested Not-Tested Non-Testable Total
Soares Chen172fe9d2017-07-18 09:30:18487 addTransceiver 14 1 3 18
Soares Chen7d2c2d72017-06-21 05:09:36488 Create Sender 3 4 0 7
489 Create Receiver 8 1 0 9
490 Create Transceiver 7 0 0 7
Soares Chen607f71c2017-06-27 15:23:37491
Soares Chen172fe9d2017-07-18 09:30:18492 Total 32 6 3 41
Soares Chen607f71c2017-06-27 15:23:37493 */
Soares Chen0042ebb2017-06-05 13:42:06494</script>