blob: 0436673f52837dcf85b3d96f8b678192b83f4bf2 [file] [log] [blame]
/**
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/**
* The gStartedAt when the capturing begins. Used for timeout adjustments.
* @private
*/
var gStartedAt = 0;
/**
* The duration of the all frame capture in milliseconds.
* @private
*/
var gCaptureDuration = 0;
/**
* The time interval at which the video is sampled.
* @private
*/
var gFrameCaptureInterval = 0;
/**
* The global array of frames. Frames are pushed, i.e. this should be treated as
* a queue and we should read from the start.
* @private
*/
var gFrames = [];
/**
* We need to skip the first two frames due to timing issues.
* @private
*/
var gHasThrownAwayFirstTwoFrames = false;
/**
* We need this global variable to synchronize with the test how long to run the
* call between the two peers.
*/
var gDoneFrameCapturing = false;
/**
* Starts the frame capturing.
*
* @param {!Object} The video tag from which the height and width parameters are
to be extracted.
* @param {Number} The frame rate at which we would like to capture frames.
* @param {Number} The duration of the frame capture in seconds.
*/
function startFrameCapture(videoTag, frameRate, duration) {
gFrameCaptureInterval = 1000 / frameRate;
gCaptureDuration = 1000 * duration;
var width = videoTag.videoWidth;
var height = videoTag.videoHeight;
if (width == 0 || height == 0) {
throw failTest('Trying to capture from ' + videoTag.id +
' but it is not playing any video.');
}
console.log('Received width is: ' + width + ', received height is: ' + height
+ ', capture interval is: ' + gFrameCaptureInterval +
', duration is: ' + gCaptureDuration);
var remoteCanvas = document.createElement('canvas');
remoteCanvas.width = width;
remoteCanvas.height = height;
document.body.appendChild(remoteCanvas);
gStartedAt = new Date().getTime();
gFrames = [];
setTimeout(function() { shoot_(videoTag, remoteCanvas, width, height); },
gFrameCaptureInterval);
}
/**
* Queries if we're done with the frame capturing yet.
*/
function doneFrameCapturing() {
if (gDoneFrameCapturing) {
returnToTest('done-capturing');
} else {
returnToTest('still-capturing');
}
}
/**
* Retrieves the number of captured frames.
*/
function getTotalNumberCapturedFrames() {
returnToTest(gFrames.length.toString());
}
/**
* Retrieves one captured frame in ARGB format as a base64-encoded string.
*
* Also updates the page's progress bar.
*
* @param frameIndex A frame index in the range 0 to total-1 where total is
* given by getTotalNumberCapturedFrames.
*/
function getOneCapturedFrame(frameIndex) {
var codedFrame = convertArrayBufferToBase64String_(gFrames[frameIndex]);
updateProgressBar_(frameIndex);
silentReturnToTest(codedFrame);
}
/**
* @private
*
* @param {ArrayBuffer} buffer An array buffer to convert to a base 64 string.
* @return {String} A base 64 string.
*/
function convertArrayBufferToBase64String_(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
for (var i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
/**
* The function which is called at the end of every gFrameCaptureInterval. Gets
* the current frame from the video and extracts the data from it. Then it saves
* it in the frames array and adjusts the capture interval (timers in JavaScript
* aren't precise).
*
* @private
*
* @param {!Object} The video whose frames are to be captured.
* @param {Canvas} The canvas on which the image will be captured.
* @param {Number} The width of the video/canvas area to be captured.
* @param {Number} The height of the video area to be captured.
*/
function shoot_(video, canvas, width, height) {
// The first two captured frames have big difference between the ideal time
// interval between two frames and the real one. As a consequence this affects
// enormously the interval adjustment for subsequent frames. That's why we
// have to reset the time after the first two frames and get rid of these two
// frames.
if (gFrames.length == 1 && !gHasThrownAwayFirstTwoFrames) {
gStartedAt = new Date().getTime();
gHasThrownAwayFirstTwoFrames = true;
gFrames = [];
}
// We capture the whole video frame.
var img = captureFrame_(video, canvas.getContext('2d'), width, height);
gFrames.push(img.data.buffer);
// Adjust the timer and try to account for timer incorrectness.
var currentTime = new Date().getTime();
var idealTime = gFrames.length * gFrameCaptureInterval;
var realTimeElapsed = currentTime - gStartedAt;
var diff = realTimeElapsed - idealTime;
if (realTimeElapsed < gCaptureDuration) {
// If duration isn't over shoot_ again.
setTimeout(function() { shoot_(video, canvas, width, height); },
gFrameCaptureInterval - diff);
} else {
// Done capturing!
gDoneFrameCapturing = true;
prepareProgressBar_();
}
}
/**
* @private
*/
function captureFrame_(video, context, width, height) {
context.drawImage(video, 0, 0, width, height);
return context.getImageData(0, 0, width, height);
}
/**
* @private
*/
function prepareProgressBar_() {
document.body.innerHTML =
'<html><body>' +
'<p id="progressBar" style="position: absolute; top: 50%; left: 40%;">' +
'Preparing to send frames.</p>' +
'</body></html>';
}
/**
* @private
*/
function updateProgressBar_(currentFrame) {
progressBar.innerHTML =
'Transferring captured frames: ' + '(' + currentFrame + '/' +
gFrames.length + ')';
}