// This code uses the tab capture and Cast streaming APIs to capture the content
// and send it to a Cast receiver end-point controlled by
-// CastStreamingApiTestcode. It generates audio/video test patterns that rotate
-// cyclicly, and these test patterns are checked for by an in-process Cast
-// receiver to confirm the correct end-to-end functionality of the Cast
+// CastStreamingApiTest code. It generates audio/video test patterns that
+// rotate cyclicly, and these test patterns are checked for by an in-process
+// Cast receiver to confirm the correct end-to-end functionality of the Cast
// streaming API.
//
// Once everything is set up and fully operational, chrome.test.succeed() is
var curTestIdx = 0;
function updateTestPattern() {
- if (document.body && document.body.style) // Check that page is loaded.
- document.body.style.backgroundColor = "rgb(" + colors[curTestIdx] + ")";
-
- // Important: Blink the testing message so that the capture pipeline will
- // observe drawing updates and continue to produce video frames.
- var message = document.getElementById("message");
- if (message && !message.blinkInterval) {
- message.innerHTML = "Testing...";
- message.blinkInterval = setInterval(
- function toggleVisibility() {
- message.style.visibility =
- message.style.visibility == "hidden" ? "visible" : "hidden";
- },
- 125);
+ if (!this.canvas) {
+ this.canvas = document.createElement("canvas");
+ this.canvas.width = 320;
+ this.canvas.height = 200;
+ this.canvas.style.position = "absolute";
+ this.canvas.style.top = "0px";
+ this.canvas.style.left = "0px";
+ this.canvas.style.width = "100%";
+ this.canvas.style.height = "100%";
+ document.body.appendChild(this.canvas);
}
+ var context = this.canvas.getContext("2d");
+ // Fill with solid color.
+ context.fillStyle = "rgb(" + colors[curTestIdx] + ")";
+ context.fillRect(0, 0, this.canvas.width, this.canvas.height);
+ // Draw the circle that moves around the page.
+ context.fillStyle = "rgb(" + colors[(curTestIdx + 1) % colors.length] + ")";
+ context.beginPath();
+ if (!this.frameNumber) {
+ this.frameNumber = 1;
+ } else {
+ ++this.frameNumber;
+ }
+ var i = this.frameNumber % 200;
+ var t = (this.frameNumber + 3000) * (0.01 + i / 8000.0);
+ var x = (Math.sin(t) * 0.45 + 0.5) * this.canvas.width;
+ var y = (Math.cos(t * 0.9) * 0.45 + 0.5) * this.canvas.height;
+ context.arc(x, y, 16, 0, 2 * Math.PI, false);
+ context.closePath();
+ context.fill();
if (!this.audioContext) {
this.audioContext = new AudioContext();
this.gainNode = this.audioContext.createGain();
this.gainNode.gain.value = 0.5;
this.gainNode.connect(this.audioContext.destination);
- } else {
- this.oscillator.stop();
- this.oscillator.disconnect();
}
- // Note: We recreate the oscillator each time because this switches the audio
- // frequency immediately. Re-using the same oscillator tends to take several
- // hundred milliseconds to ramp-up/down the frequency.
- this.oscillator = audioContext.createOscillator();
- this.oscillator.type = OscillatorNode.SINE;
- this.oscillator.frequency.value = freqs[curTestIdx];
- this.oscillator.connect(this.gainNode);
- this.oscillator.start();
+ if (!this.oscillator ||
+ this.oscillator.frequency.value != freqs[curTestIdx]) {
+ // Note: We recreate the oscillator each time because this switches the
+ // audio frequency immediately. Re-using the same oscillator tends to take
+ // several hundred milliseconds to ramp-up/down the frequency.
+ if (this.oscillator) {
+ this.oscillator.stop();
+ this.oscillator.disconnect();
+ }
+ this.oscillator = this.audioContext.createOscillator();
+ this.oscillator.type = OscillatorNode.SINE;
+ this.oscillator.frequency.value = freqs[curTestIdx];
+ this.oscillator.connect(this.gainNode);
+ this.oscillator.start();
+ }
}
-// Calls updateTestPattern(), then waits and calls itself again to advance to
-// the next one.
-function runTestPatternLoop() {
+// Called to render each frame of video, and also to update the main fill color
+// and audio frequency.
+function renderTestPatternLoop() {
+ requestAnimationFrame(renderTestPatternLoop);
updateTestPattern();
- if (!this.curAdvanceWaitTimeMillis) {
- this.curAdvanceWaitTimeMillis = 750;
+
+ if (!this.stepTimeMillis) {
+ this.stepTimeMillis = 100;
+ }
+ var now = new Date().getTime();
+ if (!this.nextSteppingAt) {
+ this.nextSteppingAt = now + this.stepTimeMillis;
+ } else if (now >= this.nextSteppingAt) {
+ ++curTestIdx;
+ if (curTestIdx >= colors.length) { // Completed a cycle.
+ curTestIdx = 0;
+ // Increase the wait time between switching test patterns for overloaded
+ // bots that aren't capturing all the frames of video.
+ this.stepTimeMillis *= 1.25;
+ }
+ this.nextSteppingAt = now + this.stepTimeMillis;
}
- setTimeout(
- function advanceTestPattern() {
- ++curTestIdx;
- if (curTestIdx >= colors.length) { // Completed a cycle.
- curTestIdx = 0;
- // Increase the wait time between switching test patterns for
- // overloaded bots that aren't capturing all the frames of video.
- this.curAdvanceWaitTimeMillis *= 1.25;
- }
- runTestPatternLoop();
- },
- this.curAdvanceWaitTimeMillis);
}
chrome.test.runTests([
// The receive port changes between browser_test invocations, and is passed
// as an query parameter in the URL.
var recvPort;
+ var aesKey;
+ var aesIvMask;
try {
- recvPort = parseInt(window.location.search.substring("?port=".length));
+ recvPort = parseInt(window.location.search.match(/(\?|&)port=(\d+)/)[2]);
chrome.test.assertTrue(recvPort > 0);
+ aesKey = window.location.search.match(/(\?|&)aesKey=(\w+)/)[2];
+ chrome.test.assertTrue(aesKey.length > 0);
+ aesIvMask = window.location.search.match(/(\?|&)aesIvMask=(\w+)/)[2];
+ chrome.test.assertTrue(aesIvMask.length > 0);
} catch (err) {
- chrome.test.fail("Error parsing ?port=### -- " + err.message);
+ chrome.test.fail("Error parsing query params -- " + err.message);
return;
}
// Set to true if you want to confirm the sender color/tone changes are
// working, without starting tab capture and Cast sending.
if (false) {
- setTimeout(runTestPatternLoop, 0);
+ renderTestPatternLoop();
return;
}
- var width = 400;
- var height = 400;
+ var width = 320;
+ var height = 200;
var frameRate = 15;
chrome.tabCapture.capture(
chrome.cast.streaming.udpTransport.setDestination(
udpId, { address: "127.0.0.1", port: recvPort } );
var rtpStream = chrome.cast.streaming.rtpStream;
- rtpStream.start(audioId,
- rtpStream.getSupportedParams(audioId)[0]);
+ var audioParams = rtpStream.getSupportedParams(audioId)[0];
+ audioParams.payload.aesKey = aesKey;
+ audioParams.payload.aesIvMask = aesIvMask;
+ rtpStream.start(audioId, audioParams);
var videoParams = rtpStream.getSupportedParams(videoId)[0];
videoParams.payload.width = width;
videoParams.payload.height = height;
- videoParams.payload.clockRate = frameRate;
+ videoParams.payload.maxFrameRate = frameRate;
+ videoParams.payload.aesKey = aesKey;
+ videoParams.payload.aesIvMask = aesIvMask;
rtpStream.start(videoId, videoParams);
- setTimeout(runTestPatternLoop, 0);
- if (window.innerWidth > 2 * width ||
- window.innerHeight > 2 & height) {
- console.warn("***TIMEOUT HAZARD*** Tab size is " +
- window.innerWidth + "x" + window.innerHeight +
- ", which is much larger than the expected " +
- width + "x" + height);
- }
+ renderTestPatternLoop();
chrome.test.succeed();
});
});