diff options
Diffstat (limited to 'dom/media/webrtc/tests/mochitests/blacksilence.js')
-rw-r--r-- | dom/media/webrtc/tests/mochitests/blacksilence.js | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/dom/media/webrtc/tests/mochitests/blacksilence.js b/dom/media/webrtc/tests/mochitests/blacksilence.js new file mode 100644 index 0000000000..bb16e4d78e --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/blacksilence.js @@ -0,0 +1,134 @@ +(function(global) { + "use strict"; + + // an invertible check on the condition. + // if the constraint is applied, then the check is direct + // if not applied, then the result should be reversed + function check(constraintApplied, condition, message) { + var good = constraintApplied ? condition : !condition; + message = + (constraintApplied ? "with" : "without") + + " constraint: should " + + (constraintApplied ? "" : "not ") + + message + + " = " + + (good ? "OK" : "waiting..."); + info(message); + return good; + } + + function mkElement(type) { + // This makes an unattached element. + // It's not rendered to save the cycles that costs on b2g emulator + // and it gets dropped (and GC'd) when the test is done. + var e = document.createElement(type); + e.width = 32; + e.height = 24; + document.getElementById("display").appendChild(e); + return e; + } + + // Runs checkFunc until it reports success. + // This is kludgy, but you have to wait for media to start flowing, and it + // can't be any old media, it has to include real data, for which we have no + // reliable signals to use as a trigger. + function periodicCheck(checkFunc) { + var resolve; + var done = false; + // This returns a function so that we create 10 closures in the loop, not + // one; and so that the timers don't all start straight away + var waitAndCheck = counter => () => { + if (done) { + return Promise.resolve(); + } + return new Promise(r => setTimeout(r, 200 << counter)).then(() => { + if (checkFunc()) { + done = true; + resolve(); + } + }); + }; + + var chain = Promise.resolve(); + for (var i = 0; i < 10; ++i) { + chain = chain.then(waitAndCheck(i)); + } + return new Promise(r => (resolve = r)); + } + + function isSilence(audioData) { + var silence = true; + for (var i = 0; i < audioData.length; ++i) { + if (audioData[i] !== 128) { + silence = false; + } + } + return silence; + } + + function checkAudio(constraintApplied, stream) { + var audio = mkElement("audio"); + audio.srcObject = stream; + audio.play(); + + var context = new AudioContext(); + var source = context.createMediaStreamSource(stream); + var analyser = context.createAnalyser(); + source.connect(analyser); + analyser.connect(context.destination); + + return periodicCheck(() => { + var sampleCount = analyser.frequencyBinCount; + info("got some audio samples: " + sampleCount); + var buffer = new Uint8Array(sampleCount); + analyser.getByteTimeDomainData(buffer); + + var silent = check( + constraintApplied, + isSilence(buffer), + "be silence for audio" + ); + return sampleCount > 0 && silent; + }).then(() => { + source.disconnect(); + analyser.disconnect(); + audio.pause(); + ok(true, "audio is " + (constraintApplied ? "" : "not ") + "silent"); + }); + } + + function checkVideo(constraintApplied, stream) { + var video = mkElement("video"); + video.srcObject = stream; + video.play(); + + return periodicCheck(() => { + try { + var canvas = mkElement("canvas"); + var ctx = canvas.getContext("2d"); + // Have to guard drawImage with the try as well, due to bug 879717. If + // we get an error, this round fails, but that failure is usually just + // transitory. + ctx.drawImage(video, 0, 0); + ctx.getImageData(0, 0, 1, 1); + return check( + constraintApplied, + false, + "throw on getImageData for video" + ); + } catch (e) { + return check( + constraintApplied, + e.name === "SecurityError", + "get a security error: " + e.name + ); + } + }).then(() => { + video.pause(); + ok(true, "video is " + (constraintApplied ? "" : "not ") + "protected"); + }); + } + + global.audioIsSilence = checkAudio; + global.videoIsBlack = checkVideo; +})(this); |