Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Side by Side Diff: chrome/test/data/webrtc/video_extraction.js

Issue 2183873002: Use MediaRecorder to capture video for quality test. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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.
9 * @private
10 */
11 var gStartedAt = 0;
12
13 /**
14 * The duration of the all frame capture in milliseconds. 8 * The duration of the all frame capture in milliseconds.
15 * @private 9 * @private
16 */ 10 */
17 var gCaptureDuration = 0; 11 var gCaptureDuration = 0;
18 12
19 /** 13 /**
20 * The time interval at which the video is sampled. 14 * The recorded video encoded in Base64.
21 * @private 15 * @private
22 */ 16 */
23 var gFrameCaptureInterval = 0; 17 var gVideoBase64 = '';
24 18
25 /** 19 /**
26 * The global array of frames. Frames are pushed, i.e. this should be treated as 20 * Chunks of the video recorded by MediaRecorded as they become available.
27 * a queue and we should read from the start.
28 * @private 21 * @private
29 */ 22 */
30 var gFrames = []; 23 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 24
38 /** 25 /**
39 * A string to be returned to the test about the current status of capture. 26 * A string to be returned to the test about the current status of capture.
40 */ 27 */
41 var gCapturingStatus = 'capturing-not-started'; 28 var gCapturingStatus = 'capturing-not-started';
42 29
43 /** 30 /**
44 * Starts the frame capturing. 31 * Starts the frame capturing.
45 * 32 *
46 * @param {!Object} The video tag from which the height and width parameters are 33 * @param {!Object} The video tag from which the height and width parameters are
47 to be extracted. 34 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. 35 * @param {Number} The duration of the frame capture in seconds.
50 */ 36 */
51 function startFrameCapture(videoTag, frameRate, duration) { 37 function startFrameCapture(videoTag, duration) {
52 gFrameCaptureInterval = 1000 / frameRate; 38 debug('inputElement stream: ' + getStreamFromElement_(videoTag));
39 var mediaRecorder = new MediaRecorder(getStreamFromElement_(videoTag));
mcasas 2016/07/29 16:20:34 Creating the MediaRecorder like this means that th
kjellander_chromium 2016/08/01 05:53:34 Sounds like a good idea to address this as the com
40 mediaRecorder.ondataavailable = function(recording) {
41 gChunks.push(recording.data);
42 }
43 mediaRecorder.onstop = function() {
44 var videoBlob = new Blob(gChunks, {type: "video/webm"});
45 gChunks = [];
46 var reader = new FileReader();
47 reader.onloadend = function() {
48 gVideoBase64 = reader.result.substr(reader.result.indexOf(',') + 1);
49 gCapturingStatus = 'done-capturing';
50 debug('done-capturing');
51 }
52 reader.readAsDataURL(videoBlob);
53 }
54 mediaRecorder.start();
53 gCaptureDuration = 1000 * duration; 55 gCaptureDuration = 1000 * duration;
54 inputElement = document.getElementById("local-view"); 56 setTimeout(function() { mediaRecorder.stop(); }, gCaptureDuration);
55 var width = inputElement.videoWidth; 57 gCapturingStatus = 'still-capturing';
56 var height = inputElement.videoHeight; 58 }
57 59
58 // The WebRTC code is free to start in VGA, so make sure that the output video 60 /**
59 // tag scales up to whatever the input size is (otherwise the video quality 61 * Returns the video recorded by RecordMedia encoded in Base64.
60 // comparison will go poorly. 62 */
61 videoTag.width = width; 63 function getRecordedVideoAsBase64() {
62 videoTag.height = height; 64 silentReturnToTest(gVideoBase64);
63
64 if (width == 0 || height == 0) {
65 // Video must be playing at this point since this function is invoked from
66 // onplay on the <video> tag. See http://crbug.com/625943.
67 gCapturingStatus = 'failed-video-was-0x0-after-onplay'
68 return;
69 }
70
71 console.log('Received width is: ' + width + ', received height is: ' + height
72 + ', capture interval is: ' + gFrameCaptureInterval +
73 ', duration is: ' + gCaptureDuration);
74 gCapturingStatus = 'still-capturing';
75
76 var remoteCanvas = document.createElement('canvas');
77 remoteCanvas.width = width;
78 remoteCanvas.height = height;
79 document.body.appendChild(remoteCanvas);
80
81 gStartedAt = new Date().getTime();
82 gFrames = [];
83 setTimeout(function() { shoot_(videoTag, remoteCanvas, width, height); },
84 gFrameCaptureInterval);
85 } 65 }
86 66
87 /** 67 /**
88 * Queries if we're done with the frame capturing yet. 68 * Queries if we're done with the frame capturing yet.
89 */ 69 */
90 function doneFrameCapturing() { 70 function doneFrameCapturing() {
91 returnToTest(gCapturingStatus); 71 returnToTest(gCapturingStatus);
92 } 72 }
93 73
94 /** 74 /**
95 * Retrieves the number of captured frames. 75 * Returns the stream from the input element to be attached to MediaRecorder.
76 * @private
96 */ 77 */
97 function getTotalNumberCapturedFrames() { 78 function getStreamFromElement_(element) {
98 returnToTest(gFrames.length.toString()); 79 if (typeof element.srcObject !== 'undefined') {
99 } 80 return element.srcObject;
100 81 } else if (typeof element.mozSrcObject !== 'undefined') {
101 /** 82 return element.mozSrcObject;
102 * Retrieves one captured frame in ARGB format as a base64-encoded string. 83 } else if (typeof element.src !== 'undefined') {
103 * 84 return element.src;
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 { 85 } else {
170 // Done capturing! 86 console.log('Error attaching stream to element.');
171 gCapturingStatus = 'done-capturing';
172 prepareProgressBar_();
173 } 87 }
174 } 88 }
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698