| Soares Chen | 172fe9d | 2017-07-18 09:30:18 | [diff] [blame^] | 1 | <!doctype html> | 
|  | 2 | <meta charset=utf-8> | 
|  | 3 | <title>RTCRtpParameters encodings</title> | 
|  | 4 | <script src="/resources/testharness.js"></script> | 
|  | 5 | <script src="/resources/testharnessreport.js"></script> | 
|  | 6 | <script src="dictionary-helper.js"></script> | 
|  | 7 | <script src="RTCRtpParameters-helper.js"></script> | 
|  | 8 | <script> | 
|  | 9 | 'use strict'; | 
|  | 10 |  | 
|  | 11 | // Test is based on the following editor draft: | 
|  | 12 | // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html | 
|  | 13 |  | 
|  | 14 | // The following helper functions are called from RTCRtpParameters-helper.js: | 
|  | 15 | // validateSenderRtpParameters | 
|  | 16 |  | 
|  | 17 | /* | 
|  | 18 | 5.1. RTCPeerConnection Interface Extensions | 
|  | 19 | partial interface RTCPeerConnection { | 
|  | 20 | RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind, | 
|  | 21 | optional RTCRtpTransceiverInit init); | 
|  | 22 | ... | 
|  | 23 | }; | 
|  | 24 |  | 
|  | 25 | dictionary RTCRtpTransceiverInit { | 
|  | 26 | RTCRtpTransceiverDirection direction = "sendrecv"; | 
|  | 27 | sequence<MediaStream> streams; | 
|  | 28 | sequence<RTCRtpEncodingParameters> sendEncodings; | 
|  | 29 | }; | 
|  | 30 |  | 
|  | 31 | 5.2. RTCRtpSender Interface | 
|  | 32 | interface RTCRtpSender { | 
|  | 33 | Promise<void> setParameters(optional RTCRtpParameters parameters); | 
|  | 34 | RTCRtpParameters getParameters(); | 
|  | 35 | }; | 
|  | 36 |  | 
|  | 37 | dictionary RTCRtpParameters { | 
|  | 38 | DOMString transactionId; | 
|  | 39 | sequence<RTCRtpEncodingParameters> encodings; | 
|  | 40 | sequence<RTCRtpHeaderExtensionParameters> headerExtensions; | 
|  | 41 | RTCRtcpParameters rtcp; | 
|  | 42 | sequence<RTCRtpCodecParameters> codecs; | 
|  | 43 | RTCDegradationPreference degradationPreference; | 
|  | 44 | }; | 
|  | 45 |  | 
|  | 46 | dictionary RTCRtpEncodingParameters { | 
|  | 47 | [readonly] | 
|  | 48 | unsigned long ssrc; | 
|  | 49 |  | 
|  | 50 | [readonly] | 
|  | 51 | RTCRtpRtxParameters rtx; | 
|  | 52 |  | 
|  | 53 | [readonly] | 
|  | 54 | RTCRtpFecParameters fec; | 
|  | 55 |  | 
|  | 56 | RTCDtxStatus dtx; | 
|  | 57 | boolean active; | 
|  | 58 | RTCPriorityType priority; | 
|  | 59 | unsigned long ptime; | 
|  | 60 | unsigned long maxBitrate; | 
|  | 61 | double maxFramerate; | 
|  | 62 |  | 
|  | 63 | [readonly] | 
|  | 64 | DOMString rid; | 
|  | 65 |  | 
|  | 66 | double scaleResolutionDownBy; | 
|  | 67 | }; | 
|  | 68 |  | 
|  | 69 | dictionary RTCRtpRtxParameters { | 
|  | 70 | [readonly] | 
|  | 71 | unsigned long ssrc; | 
|  | 72 | }; | 
|  | 73 |  | 
|  | 74 | dictionary RTCRtpFecParameters { | 
|  | 75 | [readonly] | 
|  | 76 | unsigned long ssrc; | 
|  | 77 | }; | 
|  | 78 |  | 
|  | 79 | enum RTCDtxStatus { | 
|  | 80 | "disabled", | 
|  | 81 | "enabled" | 
|  | 82 | }; | 
|  | 83 |  | 
|  | 84 | enum RTCPriorityType { | 
|  | 85 | "very-low", | 
|  | 86 | "low", | 
|  | 87 | "medium", | 
|  | 88 | "high" | 
|  | 89 | }; | 
|  | 90 |  | 
|  | 91 | getParameters | 
|  | 92 | - encodings is set to the value of the [[send encodings]] internal slot. | 
|  | 93 | */ | 
|  | 94 |  | 
|  | 95 | // Get the first encoding in param.encodings. | 
|  | 96 | // Asserts that param.encodings has at least one element. | 
|  | 97 | function getFirstEncoding(param) { | 
|  | 98 | const { encodings } = param; | 
|  | 99 | assert_equals(encodings.length, 1); | 
|  | 100 | return encodings[0]; | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | /* | 
|  | 104 | 5.1. addTransceiver | 
|  | 105 | 7. Create an RTCRtpSender with track, streams and sendEncodings and let sender | 
|  | 106 | be the result. | 
|  | 107 |  | 
|  | 108 | 5.2. create an RTCRtpSender | 
|  | 109 | 5. Let sender have a [[send encodings]] internal slot, representing a list | 
|  | 110 | of RTCRtpEncodingParameters dictionaries. | 
|  | 111 | 6. If sendEncodings is given as input to this algorithm, and is non-empty, | 
|  | 112 | set the [[send encodings]] slot to sendEncodings. | 
|  | 113 |  | 
|  | 114 | Otherwise, set it to a list containing a single RTCRtpEncodingParameters | 
|  | 115 | with active set to true. | 
|  | 116 | */ | 
|  | 117 | test(() => { | 
|  | 118 | const pc = new RTCPeerConnection(); | 
|  | 119 | const transceiver = pc.addTransceiver('audio'); | 
|  | 120 | const param = transceiver.sender.getParameters(); | 
|  | 121 | validateSenderRtpParameters(param); | 
|  | 122 | const { encodings } = param; | 
|  | 123 | const encoding = getFirstEncoding(param); | 
|  | 124 |  | 
|  | 125 | assert_equals(encoding.active, true); | 
|  | 126 | }, 'addTransceiver() with undefined sendEncodings should have default encoding parameter with active set to true'); | 
|  | 127 |  | 
|  | 128 | test(() => { | 
|  | 129 | const pc = new RTCPeerConnection(); | 
|  | 130 | const transceiver = pc.addTransceiver('audio', { sendEncodings: [] }); | 
|  | 131 |  | 
|  | 132 | const param = transceiver.sender.getParameters(); | 
|  | 133 | validateSenderRtpParameters(param); | 
|  | 134 | const { encodings } = param; | 
|  | 135 | const encoding = getFirstEncoding(param); | 
|  | 136 |  | 
|  | 137 | assert_equals(encoding.active, true); | 
|  | 138 | }, 'addTransceiver() with empty list sendEncodings should have default encoding parameter with active set to true'); | 
|  | 139 |  | 
|  | 140 | /* | 
|  | 141 | 5.2. create an RTCRtpSender | 
|  | 142 | To create an RTCRtpSender with a MediaStreamTrack , track, a list of MediaStream | 
|  | 143 | objects, streams, and optionally a list of RTCRtpEncodingParameters objects, | 
|  | 144 | sendEncodings, run the following steps: | 
|  | 145 | 5. Let sender have a [[send encodings]] internal slot, representing a list | 
|  | 146 | of RTCRtpEncodingParameters dictionaries. | 
|  | 147 |  | 
|  | 148 | 6. If sendEncodings is given as input to this algorithm, and is non-empty, | 
|  | 149 | set the [[send encodings]] slot to sendEncodings. | 
|  | 150 |  | 
|  | 151 | 5.2. getParameters | 
|  | 152 | - encodings is set to the value of the [[send encodings]] internal slot. | 
|  | 153 | */ | 
|  | 154 | test(() => { | 
|  | 155 | const pc = new RTCPeerConnection(); | 
|  | 156 | const { sender } = pc.addTransceiver('audio', { | 
|  | 157 | sendEncodings: [{ | 
|  | 158 | dtx: 'enabled', | 
|  | 159 | active: false, | 
|  | 160 | priority: 'low', | 
|  | 161 | ptime: 5, | 
|  | 162 | maxBitrate: 8, | 
|  | 163 | maxFramerate: 25, | 
|  | 164 | rid: 'foo' | 
|  | 165 | }] | 
|  | 166 | }); | 
|  | 167 |  | 
|  | 168 | const param = sender.getParameters(); | 
|  | 169 | validateSenderRtpParameters(param); | 
|  | 170 | const encoding = getFirstEncoding(param); | 
|  | 171 |  | 
|  | 172 | assert_equals(encoding.dtx, 'enabled'); | 
|  | 173 | assert_equals(encoding.active, false); | 
|  | 174 | assert_equals(encoding.priority, 'low'); | 
|  | 175 | assert_equals(encoding.ptime, 5); | 
|  | 176 | assert_equals(encoding.maxBitrate, 8); | 
|  | 177 | assert_equals(encoding.maxFramerate, 25); | 
|  | 178 | assert_equals(encoding.rid, 'foo'); | 
|  | 179 |  | 
|  | 180 | }, `sender.getParameters() should return sendEncodings set by addTransceiver()`); | 
|  | 181 |  | 
|  | 182 | /* | 
|  | 183 | 5.2. setParameters | 
|  | 184 | 3. Let N be the number of RTCRtpEncodingParameters stored in sender's internal | 
|  | 185 | [[send encodings]] slot. | 
|  | 186 | 7. If parameters.encodings.length is different from N, or if any parameter | 
|  | 187 | in the parameters argument, marked as a Read-only parameter, has a value | 
|  | 188 | that is different from the corresponding parameter value returned from | 
|  | 189 | sender.getParameters(), abort these steps and return a promise rejected | 
|  | 190 | with a newly created InvalidModificationError. Note that this also applies | 
|  | 191 | to transactionId. | 
|  | 192 | */ | 
|  | 193 | promise_test(t => { | 
|  | 194 | const pc = new RTCPeerConnection(); | 
|  | 195 | const { sender } = pc.addTransceiver('audio'); | 
|  | 196 | const param = sender.getParameters(); | 
|  | 197 | validateSenderRtpParameters(param); | 
|  | 198 |  | 
|  | 199 | const { encodings } = param; | 
|  | 200 | assert_equals(encodings.length, 1); | 
|  | 201 |  | 
|  | 202 | // {} is valid RTCRtpEncodingParameters because all fields are optional | 
|  | 203 | encodings.push({}); | 
|  | 204 | assert_equals(param.encodings.length, 2); | 
|  | 205 |  | 
|  | 206 | return promise_rejects(t, 'InvalidModificationError', | 
|  | 207 | sender.setParameters(param)); | 
|  | 208 |  | 
|  | 209 | }, `sender.setParameters() with mismatch number of encodings should reject with InvalidModificationError`); | 
|  | 210 |  | 
|  | 211 | promise_test(t => { | 
|  | 212 | const pc = new RTCPeerConnection(); | 
|  | 213 | const { sender } = pc.addTransceiver('audio'); | 
|  | 214 | const param = sender.getParameters(); | 
|  | 215 | validateSenderRtpParameters(param); | 
|  | 216 |  | 
|  | 217 | param.encodings = undefined; | 
|  | 218 |  | 
|  | 219 | return promise_rejects(t, 'InvalidModificationError', | 
|  | 220 | sender.setParameters(param)); | 
|  | 221 |  | 
|  | 222 | }, `sender.setParameters() with encodings unset should reject with InvalidModificationError`); | 
|  | 223 |  | 
|  | 224 | promise_test(t => { | 
|  | 225 | const pc = new RTCPeerConnection(); | 
|  | 226 | const { sender } = pc.addTransceiver('audio'); | 
|  | 227 |  | 
|  | 228 | const param = sender.getParameters(); | 
|  | 229 | validateSenderRtpParameters(param); | 
|  | 230 | const encoding = getFirstEncoding(param); | 
|  | 231 | const { ssrc } = encoding; | 
|  | 232 |  | 
|  | 233 | // ssrc may not be set since it is optional | 
|  | 234 | if(ssrc === undefined) { | 
|  | 235 | encoding.ssrc = 2; | 
|  | 236 | } else { | 
|  | 237 | // If it is set, increase the number by 1 to make it different from original | 
|  | 238 | encoding.ssrc = ssrc + 1; | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | return promise_rejects(t, 'InvalidModificationError', | 
|  | 242 | sender.setParameters(param)); | 
|  | 243 |  | 
|  | 244 | }, `setParameters() with modified encoding.ssrc field should reject with InvalidModificationError`); | 
|  | 245 |  | 
|  | 246 | promise_test(t => { | 
|  | 247 | const pc = new RTCPeerConnection(); | 
|  | 248 | const { sender } = pc.addTransceiver('audio'); | 
|  | 249 |  | 
|  | 250 | const param = sender.getParameters(); | 
|  | 251 | validateSenderRtpParameters(param); | 
|  | 252 | const encoding = getFirstEncoding(param); | 
|  | 253 | const { rtx } = encoding; | 
|  | 254 |  | 
|  | 255 | if(rtx === undefined) { | 
|  | 256 | encoding.rtx = { ssrc: 2 }; | 
|  | 257 | } else if(rtx.ssrc === undefined) { | 
|  | 258 | rtx.ssrc = 2; | 
|  | 259 | } else { | 
|  | 260 | rtx.ssrc += 1; | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | return promise_rejects(t, 'InvalidModificationError', | 
|  | 264 | sender.setParameters(param)); | 
|  | 265 |  | 
|  | 266 | }, `setParameters() with modified encoding.rtx field should reject with InvalidModificationError`); | 
|  | 267 |  | 
|  | 268 | promise_test(t => { | 
|  | 269 | const pc = new RTCPeerConnection(); | 
|  | 270 | const { sender } = pc.addTransceiver('audio'); | 
|  | 271 |  | 
|  | 272 | const param = sender.getParameters(); | 
|  | 273 | validateSenderRtpParameters(param); | 
|  | 274 | const encoding = getFirstEncoding(param); | 
|  | 275 | const { fec } = encoding; | 
|  | 276 |  | 
|  | 277 | if(fec === undefined) { | 
|  | 278 | encoding.fec = { ssrc: 2 }; | 
|  | 279 | } else if(fec.ssrc === undefined) { | 
|  | 280 | fec.ssrc = 2; | 
|  | 281 | } else { | 
|  | 282 | fec.ssrc += 1; | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | return promise_rejects(t, 'InvalidModificationError', | 
|  | 286 | sender.setParameters(param)); | 
|  | 287 |  | 
|  | 288 | }, `setParameters() with modified encoding.rtx field should reject with InvalidModificationError`); | 
|  | 289 |  | 
|  | 290 | promise_test(t => { | 
|  | 291 | const pc = new RTCPeerConnection(); | 
|  | 292 | const { sender } = pc.addTransceiver('audio', { | 
|  | 293 | sendEncodings: [{ rid: 'foo' }], | 
|  | 294 | }); | 
|  | 295 |  | 
|  | 296 | const param = sender.getParameters(); | 
|  | 297 | validateSenderRtpParameters(param); | 
|  | 298 | const encoding = getFirstEncoding(param); | 
|  | 299 |  | 
|  | 300 | assert_equals(encoding.rid, 'foo'); | 
|  | 301 |  | 
|  | 302 | encoding.rid = 'bar'; | 
|  | 303 | return promise_rejects(t, 'InvalidModificationError', | 
|  | 304 | sender.setParameters(param)); | 
|  | 305 |  | 
|  | 306 | }, `setParameters() with modified encoding.rid field should reject with InvalidModificationError`); | 
|  | 307 |  | 
|  | 308 | /* | 
|  | 309 | 5.2. setParameters | 
|  | 310 | 8. If the scaleResolutionDownBy parameter in the parameters argument has a | 
|  | 311 | value less than 1.0, abort these steps and return a promise rejected with | 
|  | 312 | a newly created RangeError. | 
|  | 313 | */ | 
|  | 314 | promise_test(t => { | 
|  | 315 | const pc = new RTCPeerConnection(); | 
|  | 316 | const { sender } = pc.addTransceiver('audio'); | 
|  | 317 |  | 
|  | 318 | const param = sender.getParameters(); | 
|  | 319 | validateSenderRtpParameters(param); | 
|  | 320 | const encoding = getFirstEncoding(param); | 
|  | 321 |  | 
|  | 322 | encoding.scaleResolutionDownBy = 0.5; | 
|  | 323 | return promise_rejects(t, 'RangeError', | 
|  | 324 | sender.setParameters(param)); | 
|  | 325 |  | 
|  | 326 | }, `setParameters() with encoding.scaleResolutionDownBy field set to less than 1.0 should reject with RangeError`); | 
|  | 327 |  | 
|  | 328 | promise_test(t => { | 
|  | 329 | const pc = new RTCPeerConnection(); | 
|  | 330 | const { sender } = pc.addTransceiver('audio'); | 
|  | 331 |  | 
|  | 332 | const param = sender.getParameters(); | 
|  | 333 | validateSenderRtpParameters(param); | 
|  | 334 | const encoding = getFirstEncoding(param); | 
|  | 335 |  | 
|  | 336 | encoding.scaleResolutionDownBy = 1.5; | 
|  | 337 | return sender.setParameters(param) | 
|  | 338 | .then(() => { | 
|  | 339 | const param = sender.getParameters(); | 
|  | 340 | validateSenderRtpParameters(param); | 
|  | 341 | const encoding = getFirstEncoding(param); | 
|  | 342 |  | 
|  | 343 | assert_approx_equals(encoding.scaleResolutionDownBy, 1.5, 0.01); | 
|  | 344 | }); | 
|  | 345 |  | 
|  | 346 | }, `setParameters() with encoding.scaleResolutionDownBy field set to greater than 1.0 should succeed`); | 
|  | 347 |  | 
|  | 348 | // Helper function to test that modifying an encoding field should succeed | 
|  | 349 | function test_modified_encoding(field, value1, value2, desc) { | 
|  | 350 | promise_test(t => { | 
|  | 351 | const pc = new RTCPeerConnection(); | 
|  | 352 | const { sender } = pc.addTransceiver('audio', { | 
|  | 353 | sendEncodings: [{ | 
|  | 354 | [field]: value1 | 
|  | 355 | }] | 
|  | 356 | }); | 
|  | 357 |  | 
|  | 358 | const param = sender.getParameters(); | 
|  | 359 | validateSenderRtpParameters(param); | 
|  | 360 | const encoding = getFirstEncoding(param); | 
|  | 361 |  | 
|  | 362 | assert_equals(encoding[field], value1); | 
|  | 363 | encoding[field] = value2; | 
|  | 364 |  | 
|  | 365 | return sender.setParameters(param) | 
|  | 366 | .then(() => { | 
|  | 367 | const param = sender.getParameters(); | 
|  | 368 | validateSenderRtpParameters(param); | 
|  | 369 | const encoding = getFirstEncoding(param); | 
|  | 370 | assert_equals(encoding[field], value2); | 
|  | 371 | }); | 
|  | 372 | }, desc); | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | test_modified_encoding('dtx', 'enabled', 'disabled', | 
|  | 376 | 'setParameters() with modified encoding.dtx should succeed'); | 
|  | 377 |  | 
|  | 378 | test_modified_encoding('dtx', 'enabled', undefined, | 
|  | 379 | 'setParameters() with unset encoding.dtx should succeed'); | 
|  | 380 |  | 
|  | 381 | test_modified_encoding('active', true, false, | 
|  | 382 | 'setParameters() with modified encoding.active should succeed'); | 
|  | 383 |  | 
|  | 384 | test_modified_encoding('priority', 'very-low', 'high', | 
|  | 385 | 'setParameters() with modified encoding.priority should succeed'); | 
|  | 386 |  | 
|  | 387 | test_modified_encoding('ptime', 2, 4, | 
|  | 388 | 'setParameters() with modified encoding.ptime should succeed'); | 
|  | 389 |  | 
|  | 390 | test_modified_encoding('maxBitrate', 2, 4, | 
|  | 391 | 'setParameters() with modified encoding.maxBitrate should succeed'); | 
|  | 392 |  | 
|  | 393 | test_modified_encoding('maxBitrate', 24, 16, | 
|  | 394 | 'setParameters() with modified encoding.maxFramerate should succeed'); | 
|  | 395 |  | 
|  | 396 | </script> |