blob: ce1c2827aac4cee0a762426713e4457667598fe2 [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 Chen172fe9d2017-07-18 09:30:1883 assert_idl_attribute(pc, 'addTransceiver');
Soares Chen7d2c2d72017-06-21 05:09:3684 assert_throws(new TypeError(), () => pc.addTransceiver('invalid'));
85 }, 'addTransceiver() with string argument as invalid kind should throw TypeError');
86
87 /*
88 5.1. addTransceiver
89 The initial value of mid is null.
90
91 3. If the dictionary argument is present, let direction be the value of the
92 direction member. Otherwise let direction be sendrecv.
93 4. If the first argument is a string, let it be kind and run the following steps:
94 2. Let track be null.
95 8. Create an RTCRtpSender with track, streams and sendEncodings and let
96 sender be the result.
97 9. Create an RTCRtpReceiver with kind and let receiver be the result.
98 10. Create an RTCRtpTransceiver with sender, receiver and direction, and let
99 transceiver be the result.
100 11. Add transceiver to connection's set of transceivers.
101
102 5.2. RTCRtpSender Interface
103 Create an RTCRtpSender
104 2. Set sender.track to track.
105
106 5.3. RTCRtpReceiver Interface
107 Create an RTCRtpReceiver
108 2. Let track be a new MediaStreamTrack object [GETUSERMEDIA]. The source of
109 track is a remote source provided by receiver.
110 3. Initialize track.kind to kind.
111 5. Initialize track.label to the result of concatenating the string "remote "
112 with kind.
113 6. Initialize track.readyState to live.
114 7. Initialize track.muted to true.
115 8. Set receiver.track to track.
116
117 5.4. RTCRtpTransceiver Interface
118 Create an RTCRtpTransceiver
119 2. Set transceiver.sender to sender.
120 3. Set transceiver.receiver to receiver.
121 4. Let transceiver have a [[Direction]] internal slot, initialized to direction.
122 5. Let transceiver have a [[CurrentDirection]] internal slot, initialized
123 to null.
124 6. Set transceiver.stopped to false.
Soares Chen0042ebb2017-06-05 13:42:06125 */
126 test(t => {
127 const pc = new RTCPeerConnection();
128
Soares Chen172fe9d2017-07-18 09:30:18129 assert_idl_attribute(pc, 'addTransceiver');
Soares Chen0042ebb2017-06-05 13:42:06130
131 const transceiver = pc.addTransceiver('audio');
132 assert_true(transceiver instanceof RTCRtpTransceiver,
133 'Expect transceiver to be instance of RTCRtpTransceiver');
134
135 assert_equals(transceiver.mid, null);
136 assert_equals(transceiver.stopped, false);
137 assert_equals(transceiver.direction, 'sendrecv');
Soares Chen7d2c2d72017-06-21 05:09:36138 assert_equals(transceiver.currentDirection, null);
Soares Chen0042ebb2017-06-05 13:42:06139
140 assert_array_equals([transceiver], pc.getTransceivers(),
141 `Expect added transceiver to be the only element in connection's list of transceivers`);
142
143 const sender = transceiver.sender;
144
145 assert_true(sender instanceof RTCRtpSender,
146 'Expect sender to be instance of RTCRtpSender');
147
148 assert_equals(sender.track, null);
149
150 assert_array_equals([sender], pc.getSenders(),
151 `Expect added sender to be the only element in connection's list of senders`);
152
153 const receiver = transceiver.receiver;
154 assert_true(receiver instanceof RTCRtpReceiver,
155 'Expect receiver to be instance of RTCRtpReceiver');
156
Soares Chen172fe9d2017-07-18 09:30:18157 const track = receiver.track;
Soares Chen0042ebb2017-06-05 13:42:06158 assert_true(track instanceof MediaStreamTrack,
159 'Expect receiver.track to be instance of MediaStreamTrack');
160
161 assert_equals(track.kind, 'audio');
162 assert_equals(track.label, 'remote audio');
163 assert_equals(track.readyState, 'live');
164 assert_equals(track.muted, true);
165
166 assert_array_equals([receiver], pc.getReceivers(),
167 `Expect added receiver to be the only element in connection's list of receivers`);
168
169 }, `addTransceiver('audio') should return an audio transceiver`);
170
171 test(t => {
172 const pc = new RTCPeerConnection();
173
Soares Chen172fe9d2017-07-18 09:30:18174 assert_idl_attribute(pc, 'addTransceiver');
Soares Chen0042ebb2017-06-05 13:42:06175
176 const transceiver = pc.addTransceiver('video');
177 assert_true(transceiver instanceof RTCRtpTransceiver,
178 'Expect transceiver to be instance of RTCRtpTransceiver');
179
180 assert_equals(transceiver.mid, null);
181 assert_equals(transceiver.stopped, false);
182 assert_equals(transceiver.direction, 'sendrecv');
183
184 assert_array_equals([transceiver], pc.getTransceivers(),
185 `Expect added transceiver to be the only element in connection's list of transceivers`);
186
187 const sender = transceiver.sender;
188
189 assert_true(sender instanceof RTCRtpSender,
190 'Expect sender to be instance of RTCRtpSender');
191
192 assert_equals(sender.track, null);
193
194 assert_array_equals([sender], pc.getSenders(),
195 `Expect added sender to be the only element in connection's list of senders`);
196
197 const receiver = transceiver.receiver;
198 assert_true(receiver instanceof RTCRtpReceiver,
199 'Expect receiver to be instance of RTCRtpReceiver');
200
Soares Chen172fe9d2017-07-18 09:30:18201 const track = receiver.track;
Soares Chen0042ebb2017-06-05 13:42:06202 assert_true(track instanceof MediaStreamTrack,
203 'Expect receiver.track to be instance of MediaStreamTrack');
204
205 assert_equals(track.kind, 'video');
206 assert_equals(track.label, 'remote video');
207 assert_equals(track.readyState, 'live');
208 assert_equals(track.muted, true);
209
210 assert_array_equals([receiver], pc.getReceivers(),
211 `Expect added receiver to be the only element in connection's list of receivers`);
212
213 }, `addTransceiver('video') should return a video transceiver`);
214
Soares Chene01045d2017-06-21 05:16:19215 test(t => {
216 const pc = new RTCPeerConnection();
217 const transceiver = pc.addTransceiver('audio', { direction: 'sendonly' });
218 assert_equals(transceiver.direction, 'sendonly');
219 }, `addTransceiver() with direction sendonly should have result transceiver.direction be the same`);
220
221 test(t => {
222 const pc = new RTCPeerConnection();
223 const transceiver = pc.addTransceiver('audio', { direction: 'inactive' });
224 assert_equals(transceiver.direction, 'inactive');
225 }, `addTransceiver() with direction inactive should have result transceiver.direction be the same`);
226
227 test(t => {
228 const pc = new RTCPeerConnection();
Soares Chen172fe9d2017-07-18 09:30:18229 assert_idl_attribute(pc, 'addTransceiver');
Soares Chene01045d2017-06-21 05:16:19230 assert_throws(new TypeError(), () =>
231 pc.addTransceiver('audio', { direction: 'invalid' }));
232 }, `addTransceiver() with invalid direction should throw TypeError`);
233
Soares Chen0042ebb2017-06-05 13:42:06234 /*
Soares Chen607f71c2017-06-27 15:23:37235 5.1. addTransceiver
Soares Chen7d2c2d72017-06-21 05:09:36236 5. If the first argument is a MediaStreamTrack , let it be track and let
Soares Chen607f71c2017-06-27 15:23:37237 kind be track.kind.
Soares Chen607f71c2017-06-27 15:23:37238 */
239 test(t => {
240 const pc = new RTCPeerConnection();
241 const track = generateMediaStreamTrack('audio');
242
243 const transceiver = pc.addTransceiver(track);
244 const { sender, receiver } = transceiver;
245
246 assert_true(sender instanceof RTCRtpSender,
247 'Expect sender to be instance of RTCRtpSender');
248
249 assert_true(receiver instanceof RTCRtpReceiver,
250 'Expect receiver to be instance of RTCRtpReceiver');
251
252 assert_equals(sender.track, track,
253 'Expect sender.track should be the track that is added');
254
Soares Chen172fe9d2017-07-18 09:30:18255 const receiverTrack = receiver.track;
Soares Chen607f71c2017-06-27 15:23:37256 assert_true(receiverTrack instanceof MediaStreamTrack,
257 'Expect receiver.track to be instance of MediaStreamTrack');
258
259 assert_equals(receiverTrack.kind, 'audio',
260 `receiver.track should have the same kind as added track's kind`);
261
262 assert_equals(receiverTrack.label, 'remote audio');
263 assert_equals(receiverTrack.readyState, 'live');
264 assert_equals(receiverTrack.muted, true);
265
266 assert_array_equals([transceiver], pc.getTransceivers(),
267 `Expect added transceiver to be the only element in connection's list of transceivers`);
268
269 assert_array_equals([sender], pc.getSenders(),
270 `Expect added sender to be the only element in connection's list of senders`);
271
272 assert_array_equals([receiver], pc.getReceivers(),
273 `Expect added receiver to be the only element in connection's list of receivers`);
274
275 }, 'addTransceiver(track) should have result with sender.track be given track');
276
277 test(t => {
278 const pc = new RTCPeerConnection();
279 const track = generateMediaStreamTrack('audio');
280
281 const transceiver1 = pc.addTransceiver(track);
282 const transceiver2 = pc.addTransceiver(track);
283
284 assert_not_equals(transceiver1, transceiver2);
285
286 const sender1 = transceiver1.sender;
287 const sender2 = transceiver2.sender;
288
289 assert_not_equals(sender1, sender2);
290 assert_equals(transceiver1.sender.track, track);
291 assert_equals(transceiver2.sender.track, track);
292
293 const transceivers = pc.getTransceivers();
294 assert_equals(transceivers.length, 2);
295 assert_true(transceivers.includes(transceiver1));
296 assert_true(transceivers.includes(transceiver2));
297
298 const senders = pc.getSenders();
299 assert_equals(senders.length, 2);
300 assert_true(senders.includes(sender1));
301 assert_true(senders.includes(sender2));
302
303 }, 'addTransceiver(track) multiple times should create multiple transceivers');
304
Soares Chen172fe9d2017-07-18 09:30:18305
306 /*
307 5.1. addTransceiver
308 6. Verify that each rid value in sendEncodings is composed only of
309 case-sensitive alphanumeric characters (a-z, A-Z, 0-9) up to a maximum
310 of 16 characters. If one of the RIDs does not meet these requirements,
311 throw a TypeError.
312 */
313 test(() => {
314 const pc = new RTCPeerConnection();
315 assert_idl_attribute(pc, 'addTransceiver');
316
317 assert_throws(new TypeError(), () =>
318 pc.addTransceiver('audio', {
319 sendEncodings: [{
320 rid: '@Invalid!'
321 }]
322 }));
323 }, 'addTransceiver() with rid containing invalid non-alphanumeric characters should throw TypeError');
324
325 test(() => {
326 const pc = new RTCPeerConnection();
327 assert_idl_attribute(pc, 'addTransceiver');
328
329 assert_throws(new TypeError(), () =>
330 pc.addTransceiver('audio', {
331 sendEncodings: [{
332 rid: 'a'.repeat(17)
333 }]
334 }));
335 }, 'addTransceiver() with rid longer than 16 characters should throw TypeError');
336
337 test(() => {
338 const pc = new RTCPeerConnection();
339 pc.addTransceiver('audio', {
340 sendEncodings: [{
341 rid: 'foo'
342 }]
343 });
344 }, `addTransceiver() with valid rid value should succeed`);
345
346 /*
347 5.1. addTransceiver
348 7. If any RTCRtpEncodingParameters dictionary in sendEncodings contains a
349 read-only parameter other than rid, throw an InvalidAccessError.
350
351 - The sendEncodings argument can be used to specify the number of offered
352 simulcast encodings, and optionally their RIDs and encoding parameters.
353 Aside from rid , all read-only parameters in the RTCRtpEncodingParameters
354 dictionaries, such as ssrc, must be left unset, or an error will be thrown.
355 */
356 test(() => {
357 const pc = new RTCPeerConnection();
358
359 assert_throws('InvalidAccessError', () =>
360 pc.addTransceiver('audio', {
361 sendEncodings: [{
362 ssrc: 2
363 }]
364 }));
365 }, `addTransceiver() with readonly ssrc set should throw InvalidAccessError`);
366
367 test(() => {
368 const pc = new RTCPeerConnection();
369
370 assert_throws('InvalidAccessError', () =>
371 pc.addTransceiver('audio', {
372 sendEncodings: [{
373 rtx: {
374 ssrc: 2
375 }
376 }]
377 }));
378 }, `addTransceiver() with readonly rtx set should throw InvalidAccessError`);
379
380 test(() => {
381 const pc = new RTCPeerConnection();
382
383 assert_throws('InvalidAccessError', () =>
384 pc.addTransceiver('audio', {
385 sendEncodings: [{
386 fec: {
387 ssrc: 2
388 }
389 }]
390 }));
391 }, `addTransceiver() with readonly fec set should throw InvalidAccessError`);
392
393 test(() => {
394 const pc = new RTCPeerConnection();
395 pc.addTransceiver('audio', {
396 sendEncodings: [{
397 dtx: 'enabled',
398 active: false,
399 priority: 'low',
400 ptime: 5,
401 maxBitrate: 8,
402 maxFramerate: 25,
403 rid: 'foo'
404 }]
405 });
406 }, `addTransceiver() with valid sendEncodings should succeed`);
407
Soares Chen607f71c2017-06-27 15:23:37408 /*
409 TODO
410 5.1. addTransceiver
Soares Chen7d2c2d72017-06-21 05:09:36411 - Adding a transceiver will cause future calls to createOffer to add a media
412 description for the corresponding transceiver, as defined in [JSEP]
413 (section 5.2.2.).
Soares Chen607f71c2017-06-27 15:23:37414
Soares Chen7d2c2d72017-06-21 05:09:36415 - Setting a new RTCSessionDescription may change mid to a non-null value,
416 as defined in [JSEP] (section 5.5. and section 5.6.).
417
Soares Chen607f71c2017-06-27 15:23:37418 1. If the dictionary argument is present, and it has a streams member, let
419 streams be that list of MediaStream objects.
Soares Chen7d2c2d72017-06-21 05:09:36420
Soares Chen7d2c2d72017-06-21 05:09:36421 5.2. RTCRtpSender Interface
422 Create an RTCRtpSender
423 3. Let sender have an [[associated MediaStreams]] internal slot, representing
424 a list of MediaStream objects that the MediaStreamTrack object of this
425 sender is associated with.
426
427 4. Set sender's [[associated MediaStreams]] slot to streams.
428
429 5. Let sender have a [[send encodings]] internal slot, representing a list
430 of RTCRtpEncodingParameters dictionaries.
431
432 6. If sendEncodings is given as input to this algorithm, and is non-empty,
433 set the [[send encodings]] slot to sendEncodings. Otherwise, set it to a
434 list containing a single RTCRtpEncodingParameters with active set to true.
435
436 5.3. RTCRtpReceiver Interface
437 Create an RTCRtpReceiver
438 4. If an id string, id, was given as input to this algorithm, initialize
439 track.id to id. (Otherwise the value generated when track was created
440 will be used.)
441
442 Tested in RTCPeerConnection-onnegotiationneeded.html
443 5.1. addTransceiver
444 12. Update the negotiation-needed flag for connection.
445
446 Out of Scope
447 5.1. addTransceiver
448 8. If sendEncodings is set, then subsequent calls to createOffer will be
Soares Chen607f71c2017-06-27 15:23:37449 configured to send multiple RTP encodings as defined in [JSEP]
Soares Chen7d2c2d72017-06-21 05:09:36450 (section 5.2.2. and section 5.2.1.).
451
452 When setRemoteDescription is called with a corresponding remote
453 description that is able to receive multiple RTP encodings as defined
454 in [JSEP] (section 3.7.), the RTCRtpSender may send multiple RTP
455 encodings and the parameters retrieved via the transceiver's
456 sender.getParameters() will reflect the encodings negotiated.
457
458 9. This specification does not define how to configure createOffer to
Soares Chen607f71c2017-06-27 15:23:37459 receive multiple RTP encodings. However when setRemoteDescription is
460 called with a corresponding remote description that is able to send
461 multiple RTP encodings as defined in [JSEP], the RTCRtpReceiver may
462 receive multiple RTP encodings and the parameters retrieved via the
463 transceiver's receiver.getParameters() will reflect the encodings
464 negotiated.
Soares Chen607f71c2017-06-27 15:23:37465
Soares Chen7d2c2d72017-06-21 05:09:36466 Coverage Report
467 Tested Not-Tested Non-Testable Total
Soares Chen172fe9d2017-07-18 09:30:18468 addTransceiver 14 1 3 18
Soares Chen7d2c2d72017-06-21 05:09:36469 Create Sender 3 4 0 7
470 Create Receiver 8 1 0 9
471 Create Transceiver 7 0 0 7
Soares Chen607f71c2017-06-27 15:23:37472
Soares Chen172fe9d2017-07-18 09:30:18473 Total 32 6 3 41
Soares Chen607f71c2017-06-27 15:23:37474 */
Soares Chen0042ebb2017-06-05 13:42:06475</script>