OLD | NEW |
---|---|
1 /** | 1 /** |
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 * Use of this source code is governed by a BSD-style license that can be |
4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
5 */ | 5 */ |
6 | 6 |
7 /** | 7 /** |
8 * The gStartedAt when the capturing begins. Used for timeout adjustments. | 8 * FPS at which we would like to sample. |
9 * @private | 9 * @private |
10 */ | 10 */ |
11 var gStartedAt = 0; | 11 var gFps = 30; |
12 | 12 |
13 /** | 13 /** |
14 * The duration of the all frame capture in milliseconds. | 14 * The duration of the all frame capture in milliseconds. |
15 * @private | 15 * @private |
16 */ | 16 */ |
17 var gCaptureDuration = 0; | 17 var gCaptureDuration = 0; |
18 | 18 |
19 /** | 19 /** |
20 * The time interval at which the video is sampled. | 20 * The recorded video encoded in Base64. |
21 * @private | 21 * @private |
22 */ | 22 */ |
23 var gFrameCaptureInterval = 0; | 23 var gVideoBase64 = ''; |
24 | 24 |
25 /** | 25 /** |
26 * The global array of frames. Frames are pushed, i.e. this should be treated as | 26 * Chunks of the video recorded by MediaRecorded as they become available. |
27 * a queue and we should read from the start. | |
28 * @private | 27 * @private |
29 */ | 28 */ |
30 var gFrames = []; | 29 var gChunks = []; |
31 | |
32 /** | |
33 * We need to skip the first two frames due to timing issues. | |
34 * @private | |
35 */ | |
36 var gHasThrownAwayFirstTwoFrames = false; | |
37 | 30 |
38 /** | 31 /** |
39 * A string to be returned to the test about the current status of capture. | 32 * A string to be returned to the test about the current status of capture. |
40 */ | 33 */ |
41 var gCapturingStatus = 'capturing-not-started'; | 34 var gCapturingStatus = 'capturing-not-started'; |
42 | 35 |
43 /** | 36 /** |
44 * Starts the frame capturing. | 37 * Starts the frame capturing. |
45 * | 38 * |
46 * @param {!Object} The video tag from which the height and width parameters are | 39 * @param {!Object} The video tag from which the height and width parameters are |
47 to be extracted. | 40 * to be extracted. |
48 * @param {Number} The frame rate at which we would like to capture frames. | |
49 * @param {Number} The duration of the frame capture in seconds. | 41 * @param {Number} The duration of the frame capture in seconds. |
50 */ | 42 */ |
51 function startFrameCapture(videoTag, frameRate, duration) { | 43 function startFrameCapture(videoTag, duration) { |
52 gFrameCaptureInterval = 1000 / frameRate; | 44 var inputElement = document.getElementById('local-view'); |
53 gCaptureDuration = 1000 * duration; | |
54 inputElement = document.getElementById("local-view"); | |
55 var width = inputElement.videoWidth; | 45 var width = inputElement.videoWidth; |
56 var height = inputElement.videoHeight; | 46 var height = inputElement.videoHeight; |
57 | 47 |
58 // The WebRTC code is free to start in VGA, so make sure that the output video | 48 // |videoBitsPerSecond| is set to a large number to indicate VP8 to throw as |
59 // tag scales up to whatever the input size is (otherwise the video quality | 49 // little information away as possible. |
60 // comparison will go poorly. | 50 var mediaRecorderOptions = {'videoBitsPerSecond': 4 * width * height * gFps}; |
61 videoTag.width = width; | 51 var stream = getStreamFromElement_(videoTag); |
62 videoTag.height = height; | 52 gCaptureDuration = 1000 * duration; |
63 | 53 |
64 if (width == 0 || height == 0) { | 54 var mediaRecorder = new MediaRecorder(stream, mediaRecorderOptions); |
65 // Video must be playing at this point since this function is invoked from | 55 |
66 // onplay on the <video> tag. See http://crbug.com/625943. | 56 mediaRecorder.ondataavailable = function(recording) { |
67 gCapturingStatus = 'failed-video-was-0x0-after-onplay' | 57 gChunks.push(recording.data); |
68 return; | 58 } |
59 mediaRecorder.onstop = function() { | |
60 var videoBlob = new Blob(gChunks, {type: "video/webm"}); | |
61 gChunks = []; | |
62 var reader = new FileReader(); | |
63 reader.onloadend = function() { | |
64 gVideoBase64 = reader.result.substr(reader.result.indexOf(',') + 1); | |
65 gCapturingStatus = 'done-capturing'; | |
66 debug('done-capturing'); | |
67 } | |
68 reader.readAsDataURL(videoBlob); | |
69 } | 69 } |
70 | 70 |
71 console.log('Received width is: ' + width + ', received height is: ' + height | 71 mediaRecorder.start(); |
72 + ', capture interval is: ' + gFrameCaptureInterval + | 72 setTimeout(function() { mediaRecorder.stop(); }, gCaptureDuration); |
73 ', duration is: ' + gCaptureDuration); | |
74 gCapturingStatus = 'still-capturing'; | 73 gCapturingStatus = 'still-capturing'; |
74 } | |
75 | 75 |
76 var remoteCanvas = document.createElement('canvas'); | 76 /** |
77 remoteCanvas.width = width; | 77 * Returns the video recorded by RecordMedia encoded in Base64. |
78 remoteCanvas.height = height; | 78 */ |
79 document.body.appendChild(remoteCanvas); | 79 function getRecordedVideoAsBase64() { |
80 | 80 silentReturnToTest(gVideoBase64); |
81 gStartedAt = new Date().getTime(); | |
82 gFrames = []; | |
83 setTimeout(function() { shoot_(videoTag, remoteCanvas, width, height); }, | |
84 gFrameCaptureInterval); | |
85 } | 81 } |
86 | 82 |
87 /** | 83 /** |
88 * Queries if we're done with the frame capturing yet. | 84 * Queries if we're done with the frame capturing yet. |
89 */ | 85 */ |
90 function doneFrameCapturing() { | 86 function doneFrameCapturing() { |
91 returnToTest(gCapturingStatus); | 87 returnToTest(gCapturingStatus); |
92 } | 88 } |
93 | 89 |
94 /** | 90 /** |
95 * Retrieves the number of captured frames. | 91 * Returns the stream from the input element to be attached to MediaRecorder. |
92 * @private | |
96 */ | 93 */ |
97 function getTotalNumberCapturedFrames() { | 94 function getStreamFromElement_(element) { |
98 returnToTest(gFrames.length.toString()); | 95 if (typeof element.srcObject !== 'undefined') { |
tommi (sloooow) - chröme
2016/08/04 08:50:07
nit: typeof always returns a string so there's no
| |
99 } | 96 return element.srcObject; |
100 | 97 } else if (typeof element.src !== 'undefined') { |
101 /** | 98 return element.src; |
102 * Retrieves one captured frame in ARGB format as a base64-encoded string. | |
103 * | |
104 * Also updates the page's progress bar. | |
105 * | |
106 * @param frameIndex A frame index in the range 0 to total-1 where total is | |
107 * given by getTotalNumberCapturedFrames. | |
108 */ | |
109 function getOneCapturedFrame(frameIndex) { | |
110 var codedFrame = convertArrayBufferToBase64String_(gFrames[frameIndex]); | |
111 updateProgressBar_(frameIndex); | |
112 silentReturnToTest(codedFrame); | |
113 } | |
114 | |
115 /** | |
116 * @private | |
117 * | |
118 * @param {ArrayBuffer} buffer An array buffer to convert to a base 64 string. | |
119 * @return {String} A base 64 string. | |
120 */ | |
121 function convertArrayBufferToBase64String_(buffer) { | |
122 var binary = ''; | |
123 var bytes = new Uint8Array(buffer); | |
124 for (var i = 0; i < bytes.byteLength; i++) { | |
125 binary += String.fromCharCode(bytes[i]); | |
126 } | |
127 return window.btoa(binary); | |
128 } | |
129 | |
130 /** | |
131 * The function which is called at the end of every gFrameCaptureInterval. Gets | |
132 * the current frame from the video and extracts the data from it. Then it saves | |
133 * it in the frames array and adjusts the capture interval (timers in JavaScript | |
134 * aren't precise). | |
135 * | |
136 * @private | |
137 * | |
138 * @param {!Object} The video whose frames are to be captured. | |
139 * @param {Canvas} The canvas on which the image will be captured. | |
140 * @param {Number} The width of the video/canvas area to be captured. | |
141 * @param {Number} The height of the video area to be captured. | |
142 */ | |
143 function shoot_(video, canvas, width, height) { | |
144 // The first two captured frames have big difference between the ideal time | |
145 // interval between two frames and the real one. As a consequence this affects | |
146 // enormously the interval adjustment for subsequent frames. That's why we | |
147 // have to reset the time after the first two frames and get rid of these two | |
148 // frames. | |
149 if (gFrames.length == 1 && !gHasThrownAwayFirstTwoFrames) { | |
150 gStartedAt = new Date().getTime(); | |
151 gHasThrownAwayFirstTwoFrames = true; | |
152 gFrames = []; | |
153 } | |
154 | |
155 // We capture the whole video frame. | |
156 var img = captureFrame_(video, canvas.getContext('2d'), width, height); | |
157 gFrames.push(img.data.buffer); | |
158 | |
159 // Adjust the timer and try to account for timer incorrectness. | |
160 var currentTime = new Date().getTime(); | |
161 var idealTime = gFrames.length * gFrameCaptureInterval; | |
162 var realTimeElapsed = currentTime - gStartedAt; | |
163 var diff = realTimeElapsed - idealTime; | |
164 | |
165 if (realTimeElapsed < gCaptureDuration) { | |
166 // If duration isn't over shoot_ again. | |
167 setTimeout(function() { shoot_(video, canvas, width, height); }, | |
168 gFrameCaptureInterval - diff); | |
169 } else { | 99 } else { |
170 // Done capturing! | 100 failTest('Error extracting stream from element.'); |
171 gCapturingStatus = 'done-capturing'; | |
172 prepareProgressBar_(); | |
173 } | 101 } |
174 } | 102 } |
175 | |
176 /** | |
177 * @private | |
178 */ | |
179 function captureFrame_(video, context, width, height) { | |
180 context.drawImage(video, 0, 0, width, height); | |
181 return context.getImageData(0, 0, width, height); | |
182 } | |
183 | |
184 /** | |
185 * @private | |
186 */ | |
187 function prepareProgressBar_() { | |
188 document.body.innerHTML = | |
189 '<html><body>' + | |
190 '<p id="progressBar" style="position: absolute; top: 50%; left: 40%;">' + | |
191 'Preparing to send frames.</p>' + | |
192 '</body></html>'; | |
193 } | |
194 | |
195 /** | |
196 * @private | |
197 */ | |
198 function updateProgressBar_(currentFrame) { | |
199 progressBar.innerHTML = | |
200 'Transferring captured frames: ' + '(' + currentFrame + '/' + | |
201 gFrames.length + ')'; | |
202 } | |
OLD | NEW |