| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>RTCRtpSender.setParameters</title> |
| <script src="../../resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| </head> |
| <body> |
| <script> |
| promise_test(async () => { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({ video: true }); |
| let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream); |
| |
| await doSignalingHandshake(pc1, pc2); |
| let videoParameters = videoSender.getParameters(); |
| videoParameters.encodings[0].active = false; |
| videoParameters.encodings[0].priority = 'high'; |
| videoParameters.encodings[0].maxBitrate = 1337000; |
| await videoSender.setParameters(videoParameters); |
| |
| videoParameters = videoSender.getParameters(); |
| assert_equals(videoParameters.encodings[0].active, false); |
| assert_equals(videoParameters.encodings[0].priority, 'high'); |
| assert_equals(videoParameters.encodings[0].maxBitrate, 1337000); |
| }, 'setParameters() changes getParameters() returned values'); |
| |
| promise_test(async () => { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({ video: true }); |
| let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream); |
| |
| await doSignalingHandshake(pc1, pc2); |
| let videoParameters = videoSender.getParameters(); |
| videoParameters.encodings[0].maxBitrate = 0xFFFFFFFF; |
| await videoSender.setParameters(videoParameters); |
| |
| videoParameters = videoSender.getParameters(); |
| assert_equals(videoParameters.encodings[0].maxBitrate, 0xFFFFFFFF); |
| }, 'setParameters() with large maxBitrate changes getParameters() returned values'); |
| |
| promise_test(async (t) => { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({ video: true }, 1); |
| let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream); |
| await doSignalingHandshake(pc1, pc2); |
| |
| let videoParameters = videoSender.getParameters(); |
| videoParameters.encodings[0].active = false; |
| videoParameters.encodings[0].priority = 'high'; |
| videoParameters.encodings[0].maxBitrate = 1337000; |
| await videoSender.setParameters(videoParameters); |
| |
| videoParameters.encodings[0].active = true; |
| videoParameters.encodings[0].priority = 'low'; |
| videoParameters.encodings[0].maxBitrate = 1337001; |
| return promise_rejects( |
| t, |
| new DOMException( |
| "getParameters() needs to be called before setParameters().", |
| "InvalidStateError"), |
| videoSender.setParameters(videoParameters)); |
| }, 'setParameters() with multiple calls after single getParameters()'); |
| |
| promise_test(async (t) => { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({ video: true }, 1); |
| let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream); |
| await doSignalingHandshake(pc1, pc2); |
| |
| return promise_rejects( |
| t, |
| new DOMException( |
| "getParameters() needs to be called before setParameters().", |
| "InvalidStateError"), |
| videoSender.setParameters({ |
| transactionId: "", |
| codecs: [], |
| encodings: [], |
| rtcp: {}, |
| headerExtensions: [] |
| })); |
| }, 'setParameters() fails without getParameters()'); |
| |
| /** |
| * Tests for read only parameters check. |
| * Those are mostly similar, so an array of object is used to generate them. |
| * They have the following properties: |
| * - name: name of the test |
| * - transform: function transforming the parameters to trigger an error |
| * - check: function checking if the transform can be applied |
| */ |
| var readOnlyParameters = [ |
| { |
| name: "transactionId modification", |
| transform: (p) => { p.transactionId = "foo"; }, |
| }, |
| { |
| name: "codecs remove one", |
| transform: (p) => { p.codecs.pop(); }, |
| }, |
| { |
| name: "codecs add one", |
| transform: (p) => { |
| p.codecs.push({ |
| payloadType: 26, |
| mimeType: "video/dummy", |
| clockRate: 24000, |
| channels: 0, |
| sdpFmtpLine: "sdpFmtpLine", |
| }); |
| }, |
| }, |
| { |
| name: "codecs reorder", |
| transform: (p) => { p.codecs.concat(p.codecs.splice(0, 1)); }, |
| }, |
| { |
| name: "codecs.payloadtype modification", |
| transform: (p) => { p.codecs[0].payloadType = 1337; }, |
| }, |
| { |
| name: "codecs.mimeType modification", |
| transform: (p) => { p.codecs[0].mimeType = "mime"; }, |
| }, |
| { |
| name: "codecs.clockRate modification", |
| transform: (p) => { p.codecs[0].clockRate = 42; }, |
| }, |
| { |
| name: "codecs.channels modification", |
| transform: (p) => { p.codecs[0].channels = 16; }, |
| }, |
| { |
| name: "codecs.channels removed", |
| transform: (p) => { delete p.codecs[0].channels; }, |
| check: (p) => { p.codecs[0].channels !== undefined }, |
| }, |
| { |
| name: "codecs.sdpFmtpLine modification", |
| transform: (p) => { p.codecs[0].sdpFmtpLine = "sdpFmtpLine"; }, |
| }, |
| { |
| name: "codecs.sdpFmtpLine removed", |
| transform: (p) => { delete p.codecs[0].sdpFmtpLine; }, |
| check: (p) => { return p.codecs[0].sdpFmtpLine !== undefined; }, |
| }, |
| { |
| name: "encodings remove one", |
| transform: (p) => { p.encodings.pop(); }, |
| }, |
| { |
| name: "encodings add one", |
| transform: (p) => { p.encodings.push({}); }, |
| }, |
| { |
| name: "encodings.rid modification", |
| transform: (p) => { p.encodings[0].rid = "rid"; }, |
| }, |
| { |
| name: "headerExtensions remove one", |
| transform: (p) => { p.headerExtensions.pop(); }, |
| check: (p) => { return p.headerExtensions.length != 0; }, |
| }, |
| { |
| name: "headerExtensions add one", |
| transform: (p) => { |
| p.headerExtensions.push({ |
| uri: "uri", |
| id: 42, |
| encrypted: false, |
| }); |
| }, |
| }, |
| { |
| name: "headerExtensions.uri modification", |
| transform: (p) => { p.headerExtensions[0].uri = "uri"; }, |
| check: (p) => { return p.headerExtensions.length != 0; }, |
| }, |
| { |
| name: "headerExtensions.id modification", |
| transform: (p) => { |
| p.headerExtensions[0].id = p.headerExtensions[0].id + 1; |
| }, |
| check: (p) => { |
| return p.headerExtensions.length != 0; |
| }, |
| }, |
| { |
| name: "headerExtensions.encrypted modification", |
| transform: (p) => { |
| p.headerExtensions[0].encrypted = !p.headerExtensions[0].encrypted; |
| }, |
| check: (p) => { |
| return p.headerExtensions.length != 0; |
| }, |
| }, |
| { |
| name: "headerExtensions.encrypted removed", |
| transform: (p) => { |
| p.headerExtensions[0].encrypted = !p.headerExtensions[0].encrypted; |
| }, |
| check: (p) => { |
| return p.headerExtensions.length != 0 |
| && p.headerExtensions[0].encrypted !== undefined; |
| }, |
| }, |
| { |
| name: "rtcp.cname modification", |
| transform: (p) => { p.rtcp.cname = "cname"; }, |
| }, |
| { |
| name: "rtcp.reducedSize modification", |
| transform: (p) => { p.rtcp.reducedSize = !p.rtcp.reducedSize; }, |
| }, |
| ]; |
| |
| for (testCase of readOnlyParameters) { |
| promise_test((async (name, transform, check, t) => { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({ video: true }, 1); |
| let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream); |
| await doSignalingHandshake(pc1, pc2); |
| |
| let videoParameters = videoSender.getParameters(); |
| if (!check || check(videoParameters)) { |
| transform(videoParameters); |
| return promise_rejects(t, |
| new DOMException("Read-only field modified in setParameters().", |
| "InvalidModificationError"), |
| videoSender.setParameters(videoParameters)); |
| } |
| }).bind(undefined, testCase.name, testCase.transform, testCase.check), |
| 'video setParameters() check for ' + testCase.name); |
| } |
| |
| for (testCase of readOnlyParameters) { |
| promise_test((async (name, transform, check, t) => { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({ audio: true }, 1); |
| let audioSender = pc1.addTrack(stream.getAudioTracks()[0], stream); |
| await doSignalingHandshake(pc1, pc2); |
| |
| let audioParameters = audioSender.getParameters(); |
| if (!check || check(audioParameters)) { |
| transform(audioParameters); |
| return promise_rejects(t, |
| new DOMException("Read-only field modified in setParameters().", |
| "InvalidModificationError"), |
| audioSender.setParameters(audioParameters)); |
| } |
| }).bind(undefined, testCase.name, testCase.transform, testCase.check), |
| 'audio setParameters() check for ' + testCase.name); |
| } |
| |
| var requiredParameters = [ |
| { |
| name: "transactionId is required", |
| transform: (p) => { delete p.transactionId; }, |
| }, |
| { |
| name: "codecs is required", |
| transform: (p) => { delete p.codecs; }, |
| }, |
| { |
| name: "codecs.payloadType is required", |
| transform: (p) => { delete p.codecs[0].payloadType; }, |
| }, |
| { |
| name: "codecs.mimeType is required", |
| transform: (p) => { delete p.codecs[0].mimeType; }, |
| }, |
| { |
| name: "codecs.clockRate is required", |
| transform: (p) => { delete p.codecs[0].clockRate; }, |
| }, |
| { |
| name: "encodings is required", |
| transform: (p) => { delete p.encodings; }, |
| }, |
| { |
| name: "headerExtensions is required", |
| transform: (p) => { delete p.headerExtensions; }, |
| }, |
| { |
| name: "headerExtensions.uri is required", |
| transform: (p) => { delete p.headerExtensions[0].uri; }, |
| }, |
| { |
| name: "headerExtensions.id is required", |
| transform: (p) => { delete p.headerExtensions[0].id; }, |
| }, |
| { |
| name: "rtcp is required", |
| transform: (p) => { delete p.rtcp; }, |
| }, |
| ]; |
| |
| for (testCase of requiredParameters) { |
| promise_test((async (name, transform, check, t) => { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({ video: true }, 1); |
| let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream); |
| await doSignalingHandshake(pc1, pc2); |
| |
| let videoParameters = videoSender.getParameters(); |
| transform(videoParameters); |
| return promise_rejects(t, |
| new TypeError(), |
| videoSender.setParameters(videoParameters)); |
| }).bind(undefined, testCase.name, testCase.transform, testCase.check), |
| 'video setParameters() check for ' + testCase.name); |
| } |
| |
| promise_test(async function() { |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| let stream = await navigator.mediaDevices.getUserMedia({video:true}); |
| let videoSender = pc1.addTrack(stream.getVideoTracks()[0], stream); |
| |
| await doSignalingHandshake(pc1, pc2); |
| let videoParameters = videoSender.getParameters(); |
| videoParameters.encodings[0].maxBitrate = 50; |
| await videoSender.setParameters(videoParameters); |
| |
| videoParameters = videoSender.getParameters(); |
| assert_equals(videoParameters.encodings[0].maxBitrate, 50); |
| }, 'setParameters() set low maxBitrate value'); |
| |
| /** |
| * Helper functions to tests. |
| */ |
| async function doSignalingHandshake(localPc, remotePc) { |
| let offer = await localPc.createOffer(); |
| await localPc.setLocalDescription(offer); |
| await remotePc.setRemoteDescription(offer); |
| |
| let answer = await remotePc.createAnswer(); |
| await remotePc.setLocalDescription(answer); |
| await localPc.setRemoteDescription(answer); |
| } |
| |
| </script> |
| </body> |
| </html> |