| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 1 | <!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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 6 | <script src="RTCPeerConnection-helper.js"></script> | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 7 | <script> | 
|  | 8 | 'use strict'; | 
|  | 9 |  | 
|  | 10 | // Test is based on the following editor draft: | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 11 | // https://rawgit.com/w3c/webrtc-pc/cc8d80f455b86c8041d63bceb8b457f45c72aa89/webrtc.html | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 12 |  | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 13 | // The following helper functions are called from RTCPeerConnection-helper.js: | 
|  | 14 | // generateMediaStreamTrack() | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 15 |  | 
|  | 16 | /* | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 17 | 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 Chen | e01045d | 2017-06-21 05:16:19 | [diff] [blame] | 34 | enum RTCRtpTransceiverDirection { | 
|  | 35 | "sendrecv", | 
|  | 36 | "sendonly", | 
|  | 37 | "recvonly", | 
|  | 38 | "inactive" | 
|  | 39 | }; | 
|  | 40 |  | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 41 | 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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 68 |  | 
|  | 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 Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 74 | */ | 
|  | 75 |  | 
|  | 76 | /* | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 77 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 83 | t.add_cleanup(() => pc.close()); | 
|  | 84 |  | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 85 | assert_idl_attribute(pc, 'addTransceiver'); | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 86 | 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 Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 127 | */ | 
|  | 128 | test(t => { | 
|  | 129 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 130 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 131 |  | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 132 | assert_idl_attribute(pc, 'addTransceiver'); | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 133 |  | 
|  | 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 Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 141 | assert_equals(transceiver.currentDirection, null); | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 142 |  | 
|  | 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 Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 160 | const track = receiver.track; | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 161 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 176 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 177 |  | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 178 | assert_idl_attribute(pc, 'addTransceiver'); | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 179 |  | 
|  | 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 Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 205 | const track = receiver.track; | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 206 | 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 Chen | e01045d | 2017-06-21 05:16:19 | [diff] [blame] | 219 | test(t => { | 
|  | 220 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 221 | t.add_cleanup(() => pc.close()); | 
|  | 222 |  | 
| Soares Chen | e01045d | 2017-06-21 05:16:19 | [diff] [blame] | 223 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 229 | t.add_cleanup(() => pc.close()); | 
|  | 230 |  | 
| Soares Chen | e01045d | 2017-06-21 05:16:19 | [diff] [blame] | 231 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 237 | t.add_cleanup(() => pc.close()); | 
|  | 238 |  | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 239 | assert_idl_attribute(pc, 'addTransceiver'); | 
| Soares Chen | e01045d | 2017-06-21 05:16:19 | [diff] [blame] | 240 | assert_throws(new TypeError(), () => | 
|  | 241 | pc.addTransceiver('audio', { direction: 'invalid' })); | 
|  | 242 | }, `addTransceiver() with invalid direction should throw TypeError`); | 
|  | 243 |  | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 244 | /* | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 245 | 5.1. addTransceiver | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 246 | 5. If the first argument is a MediaStreamTrack , let it be track and let | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 247 | kind be track.kind. | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 248 | */ | 
|  | 249 | test(t => { | 
|  | 250 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 251 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 252 |  | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 253 | const track = generateMediaStreamTrack('audio'); | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 254 | 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 Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 266 | const receiverTrack = receiver.track; | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 267 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 290 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 291 |  | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 292 | const track = generateMediaStreamTrack('audio'); | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 293 | 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 Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 317 | /* | 
|  | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 324 | test(t => { | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 325 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 326 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 327 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 337 | test(t => { | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 338 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 339 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 340 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 350 | test(t => { | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 351 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 352 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 353 | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 370 | test(t => { | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 371 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 372 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 373 |  | 
|  | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 382 | test(t => { | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 383 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 384 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 385 |  | 
|  | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 396 | test(t => { | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 397 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 398 | t.add_cleanup(() => pc.close()); | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 399 |  | 
|  | 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 Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 410 | test(t => { | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 411 | const pc = new RTCPeerConnection(); | 
| Soares Chen | ec114aa | 2017-11-20 09:02:21 | [diff] [blame] | 412 | t.add_cleanup(() => pc.close()); | 
|  | 413 |  | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 414 | 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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 427 | /* | 
|  | 428 | TODO | 
|  | 429 | 5.1. addTransceiver | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 430 | - 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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 433 |  | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 434 | - 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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 437 | 1. If the dictionary argument is present, and it has a streams member, let | 
|  | 438 | streams be that list of MediaStream objects. | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 439 |  | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 440 | 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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 468 | configured to send multiple RTP encodings as defined in [JSEP] | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 469 | (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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 478 | 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 Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 484 |  | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 485 | Coverage Report | 
|  | 486 | Tested Not-Tested Non-Testable Total | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 487 | addTransceiver 14 1 3 18 | 
| Soares Chen | 7d2c2d7 | 2017-06-21 05:09:36 | [diff] [blame] | 488 | Create Sender 3 4 0 7 | 
|  | 489 | Create Receiver 8 1 0 9 | 
|  | 490 | Create Transceiver 7 0 0 7 | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 491 |  | 
| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame] | 492 | Total 32 6 3 41 | 
| Soares Chen | 607f71c | 2017-06-27 15:23:37 | [diff] [blame] | 493 | */ | 
| Soares Chen | 0042ebb | 2017-06-05 13:42:06 | [diff] [blame] | 494 | </script> |