2 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
8 * The gStartedAt when the capturing begins. Used for timeout adjustments.
14 * The duration of the all frame capture in milliseconds.
17 var gCaptureDuration = 0;
20 * The time interval at which the video is sampled.
23 var gFrameCaptureInterval = 0;
26 * The global array of frames. Frames are pushed, i.e. this should be treated as
27 * a queue and we should read from the start.
33 * We need to skip the first two frames due to timing issues.
36 var gHasThrownAwayFirstTwoFrames = false;
39 * We need this global variable to synchronize with the test how long to run the
40 * call between the two peers.
42 var gDoneFrameCapturing = false;
45 * Starts the frame capturing.
47 * @param {!Object} The video tag from which the height and width parameters are
49 * @param {Number} The frame rate at which we would like to capture frames.
50 * @param {Number} The duration of the frame capture in seconds.
52 function startFrameCapture(videoTag, frameRate, duration) {
53 gFrameCaptureInterval = 1000 / frameRate;
54 gCaptureDuration = 1000 * duration;
55 var width = videoTag.videoWidth;
56 var height = videoTag.videoHeight;
58 if (width == 0 || height == 0) {
59 throw failTest('Trying to capture from ' + videoTag.id +
60 ' but it is not playing any video.');
63 console.log('Received width is: ' + width + ', received height is: ' + height
64 + ', capture interval is: ' + gFrameCaptureInterval +
65 ', duration is: ' + gCaptureDuration);
67 var remoteCanvas = document.createElement('canvas');
68 remoteCanvas.width = width;
69 remoteCanvas.height = height;
70 document.body.appendChild(remoteCanvas);
72 gStartedAt = new Date().getTime();
74 setTimeout(function() { shoot_(videoTag, remoteCanvas, width, height); },
75 gFrameCaptureInterval);
79 * Queries if we're done with the frame capturing yet.
81 function doneFrameCapturing() {
82 if (gDoneFrameCapturing) {
83 returnToTest('done-capturing');
85 returnToTest('still-capturing');
90 * Retrieves the number of captured frames.
92 function getTotalNumberCapturedFrames() {
93 returnToTest(gFrames.length.toString());
97 * Retrieves one captured frame in ARGB format as a base64-encoded string.
99 * Also updates the page's progress bar.
101 * @param frameIndex A frame index in the range 0 to total-1 where total is
102 * given by getTotalNumberCapturedFrames.
104 function getOneCapturedFrame(frameIndex) {
105 var codedFrame = convertArrayBufferToBase64String_(gFrames[frameIndex]);
106 updateProgressBar_(frameIndex);
107 silentReturnToTest(codedFrame);
113 * @param {ArrayBuffer} buffer An array buffer to convert to a base 64 string.
114 * @return {String} A base 64 string.
116 function convertArrayBufferToBase64String_(buffer) {
118 var bytes = new Uint8Array(buffer);
119 for (var i = 0; i < bytes.byteLength; i++) {
120 binary += String.fromCharCode(bytes[i]);
122 return window.btoa(binary);
126 * The function which is called at the end of every gFrameCaptureInterval. Gets
127 * the current frame from the video and extracts the data from it. Then it saves
128 * it in the frames array and adjusts the capture interval (timers in JavaScript
133 * @param {!Object} The video whose frames are to be captured.
134 * @param {Canvas} The canvas on which the image will be captured.
135 * @param {Number} The width of the video/canvas area to be captured.
136 * @param {Number} The height of the video area to be captured.
138 function shoot_(video, canvas, width, height) {
139 // The first two captured frames have big difference between the ideal time
140 // interval between two frames and the real one. As a consequence this affects
141 // enormously the interval adjustment for subsequent frames. That's why we
142 // have to reset the time after the first two frames and get rid of these two
144 if (gFrames.length == 1 && !gHasThrownAwayFirstTwoFrames) {
145 gStartedAt = new Date().getTime();
146 gHasThrownAwayFirstTwoFrames = true;
150 // We capture the whole video frame.
151 var img = captureFrame_(video, canvas.getContext('2d'), width, height);
152 gFrames.push(img.data.buffer);
154 // Adjust the timer and try to account for timer incorrectness.
155 var currentTime = new Date().getTime();
156 var idealTime = gFrames.length * gFrameCaptureInterval;
157 var realTimeElapsed = currentTime - gStartedAt;
158 var diff = realTimeElapsed - idealTime;
160 if (realTimeElapsed < gCaptureDuration) {
161 // If duration isn't over shoot_ again.
162 setTimeout(function() { shoot_(video, canvas, width, height); },
163 gFrameCaptureInterval - diff);
166 gDoneFrameCapturing = true;
167 prepareProgressBar_();
174 function captureFrame_(video, context, width, height) {
175 context.drawImage(video, 0, 0, width, height);
176 return context.getImageData(0, 0, width, height);
182 function prepareProgressBar_() {
183 document.body.innerHTML =
185 '<p id="progressBar" style="position: absolute; top: 50%; left: 40%;">' +
186 'Preparing to send frames.</p>' +
193 function updateProgressBar_(currentFrame) {
194 progressBar.innerHTML =
195 'Transferring captured frames: ' + '(' + currentFrame + '/' +
196 gFrames.length + ')';