[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / test / data / media / getusermedia.html
blobe4723636005fadec7b4e39b0989a7c86778324b1
1 <html>
2 <head>
3 <script type="text/javascript" src="webrtc_test_utilities.js"></script>
4 <script type="text/javascript">
5 $ = function(id) {
6 return document.getElementById(id);
7 };
9 setAllEventsOccuredHandler(function() {
10 reportTestSuccess();
11 });
13 function getSources() {
14 MediaStreamTrack.getSources(function(devices) {
15 document.title = 'Media devices available';
16 var results = [];
17 for (var device, i = 0; device = devices[i]; ++i) {
18 results.push({
19 'id': device.id,
20 'kind': device.kind,
21 'label': device.label,
22 'facing': device.facing
23 });
25 sendValueToTest(JSON.stringify(results));
26 });
29 // Creates a MediaStream and renders it locally. When the video is detected to
30 // be rolling, the stream should be stopped.
31 function getUserMediaAndStop(constraints) {
32 console.log('Calling getUserMediaAndStop.');
33 navigator.webkitGetUserMedia(
34 constraints,
35 function(stream) {
36 detectVideoInLocalView1(stream, function() {
37 stream.getVideoTracks()[0].stop();
38 waitForVideoToStop('local-view-1');
39 });
41 failedCallback);
44 // Requests getusermedia and expects it to fail. The error name is returned
45 // to the test.
46 function getUserMediaAndExpectFailure(constraints) {
47 console.log('Calling getUserMediaAndExpectFailure.');
48 navigator.webkitGetUserMedia(
49 constraints,
50 function(stream) { failTest('Unexpectedly succeeded getUserMedia.'); },
51 function(error) { sendValueToTest(error.name); });
54 function renderClonedMediastreamAndStop(constraints, waitTimeInSeconds) {
55 console.log('Calling renderClonedMediastreamAndStop.');
56 navigator.webkitGetUserMedia(
57 constraints,
58 function(stream) {
59 var duplicate = stream.clone();
60 assertEquals(stream.getVideoTracks().length, 1);
61 assertEquals(duplicate.getVideoTracks().length, 1);
62 assertNotEquals(stream.getVideoTracks()[0].id,
63 duplicate.getVideoTracks()[0].id);
64 detectVideoInLocalView1(
65 stream,
66 function() {
67 reportTestSuccess();
68 });
70 failedCallback);
73 function renderDuplicatedMediastreamAndStop(constraints, waitTimeInSeconds) {
74 console.log('Calling renderDuplicateMediastreamAndStop.');
75 navigator.webkitGetUserMedia(
76 constraints,
77 function(stream) {
78 var duplicate = new webkitMediaStream(stream);
79 assertEquals(stream.getVideoTracks().length, 1);
80 assertEquals(duplicate.getVideoTracks().length, 1);
81 assertEquals(stream.getVideoTracks()[0].id,
82 duplicate.getVideoTracks()[0].id);
83 detectVideoInLocalView1(
84 duplicate,
85 function() {
86 reportTestSuccess();
87 });
89 failedCallback);
92 function renderSameTrackMediastreamAndStop(constraints, waitTimeInSeconds) {
93 console.log('Calling renderSameTrackMediastreamAndStop.');
94 navigator.webkitGetUserMedia(
95 constraints,
96 function(stream) {
97 var duplicate = new webkitMediaStream();
98 duplicate.addTrack(stream.getVideoTracks()[0]);
99 assertEquals(duplicate.getVideoTracks().length, 1);
100 assertEquals(duplicate.getVideoTracks().length, 1);
101 assertEquals(stream.getVideoTracks()[0].id,
102 duplicate.getVideoTracks()[0].id);
103 detectVideoInLocalView1(
104 duplicate,
105 function() {
106 reportTestSuccess();
109 failedCallback);
112 function renderClonedTrackMediastreamAndStop(constraints, waitTimeInSeconds) {
113 console.log('Calling renderClonedTrackMediastreamAndStop.');
114 navigator.webkitGetUserMedia(
115 constraints,
116 function(stream) {
117 var duplicate = new webkitMediaStream();
118 duplicate.addTrack(stream.getVideoTracks()[0].clone());
119 assertEquals(duplicate.getVideoTracks().length, 1);
120 assertEquals(duplicate.getVideoTracks().length, 1);
121 assertNotEquals(stream.getVideoTracks()[0].id,
122 duplicate.getVideoTracks()[0].id)
123 detectVideoInLocalView1(
124 duplicate,
125 function() {
126 reportTestSuccess();
129 failedCallback);
132 // Creates a MediaStream and renders it locally. When the video is detected to
133 // be rolling we report success. The acquired stream is stored in window
134 // under the name |streamName|.
135 function getUserMediaAndGetStreamUp(constraints, streamName) {
136 console.log('Calling getUserMediaAndGetStreamUp.');
137 navigator.webkitGetUserMedia(
138 constraints,
139 function(stream) {
140 window[streamName] = stream;
141 detectVideoInLocalView1(
142 stream,
143 function() {
144 reportTestSuccess();
147 failedCallback);
150 function getUserMediaAndRenderInSeveralVideoTags() {
151 navigator.webkitGetUserMedia(
152 {video: true},
153 createMultipleVideoRenderersAndPause,
154 function(error) { failedCallback(); });
157 // Gets a video stream up, analyses it and returns the aspect ratio to the
158 // test through the automation controller.
159 function getUserMediaAndAnalyseAndStop(constraints) {
160 console.log('Calling getUserMediaAndAnalyseAndStop.');
161 navigator.webkitGetUserMedia(
162 constraints, displayDetectAndAnalyzeVideo, failedCallback);
165 // This test that a MediaStream can be cloned and that the clone can
166 // be rendered.
167 function getUserMediaAndClone() {
168 console.log('Calling getUserMediaAndClone.');
169 navigator.webkitGetUserMedia({video: true, audio: true},
170 createAndRenderClone, failedCallback);
173 // Creates two MediaStream and renders them locally. When the video of both
174 // streams are detected to be rolling, we stop the local video tracks one at
175 // the time. In particular, we verify that stopping one track does not stop
176 // the other.
177 function twoGetUserMediaAndStop(constraints) {
178 var stream1 = null;
179 var stream2 = null;
180 navigator.webkitGetUserMedia(
181 constraints,
182 function(stream) {
183 stream1 = stream;
184 attachMediaStream(stream, 'local-view-1');
185 requestSecondGetUserMedia();
187 failedCallback);
188 var requestSecondGetUserMedia = function() {
189 navigator.webkitGetUserMedia(
190 constraints,
191 function(stream) {
192 stream2 = stream;
193 attachMediaStream(stream, 'local-view-2');
194 stopBothVideoTracksAndVerify();
196 failedCallback);
199 var stopBothVideoTracksAndVerify = function() {
200 // Stop track 2, ensure track 2 stops but not track 1, then stop track 1.
201 stream2.getVideoTracks()[0].stop();
202 detectVideoStopped('local-view-2', function() {
203 detectVideoInLocalView1(stream1, function() {
204 stream1.getVideoTracks()[0].stop();
205 waitForVideoToStop('local-view-1');
211 function twoGetUserMedia(constraints1, constraints2) {
212 var result="";
213 navigator.webkitGetUserMedia(
214 constraints1,
215 function(stream) {
216 displayDetectAndAnalyzeVideoInElement(
217 stream,
218 function(aspectRatio) {
219 result = aspectRatio;
220 requestSecondGetUserMedia();
222 'local-view-1');
224 failedCallback);
225 var requestSecondGetUserMedia = function() {
226 navigator.webkitGetUserMedia(
227 constraints2,
228 function(stream) {
229 displayDetectAndAnalyzeVideoInElement(
230 stream,
231 function(aspectRatio) {
232 result = result + '-' + aspectRatio;
233 sendValueToTest(result);
235 'local-view-2');
237 failedCallback);
241 // Calls GetUserMedia twice and verify that the frame rate is as expected for
242 // both streams.
243 function twoGetUserMediaAndVerifyFrameRate(constraints1,
244 constraints2,
245 expected_frame_rate1,
246 expected_frame_rate2) {
247 addExpectedEvent();
248 addExpectedEvent();
249 var validateFrameRateCallback = function (success) {
250 if (!success)
251 failTest("Failed to validate frameRate.");
252 eventOccured();
255 navigator.webkitGetUserMedia(
256 constraints1,
257 function(stream) {
258 requestSecondGetUserMedia();
259 attachMediaStream(stream, 'local-view-1');
260 detectVideoPlaying(
261 'local-view-1',
262 function() {
263 validateFrameRate('local-view-1', expected_frame_rate1,
264 validateFrameRateCallback);
267 failedCallback);
268 var requestSecondGetUserMedia = function() {
269 navigator.webkitGetUserMedia(
270 constraints2,
271 function(stream) {
272 attachMediaStream(stream, 'local-view-2');
273 detectVideoPlaying(
274 'local-view-2',
275 function() {
276 validateFrameRate('local-view-2', expected_frame_rate2,
277 validateFrameRateCallback);
280 failedCallback);
284 function getUserMediaInIframeAndCloseInSuccessCb(constraints) {
285 var iframe = document.createElement('iframe');
286 iframe.onload = onIframeLoaded;
287 document.body.appendChild(iframe);
288 iframe.src = window.location;
290 function onIframeLoaded() {
291 var iframe = window.document.querySelector('iframe');
292 iframe.contentWindow.navigator.webkitGetUserMedia(
293 constraints,
294 function (stream) {
295 // Remove the iframe from the parent within the callback scope.
296 window.parent.document.querySelector('iframe').remove();
297 // This function enqueues reporting test success, rather than doing
298 // it directly. We do this so we catch crashes that occur in the
299 // current execution context, but after reportTestSuccess is
300 // invoked.
301 setTimeout(function () {
302 window.parent.reportTestSuccess();
303 }, 0);
305 window.parent.failedCallback);
309 function getUserMediaInIframeAndCloseInFailureCb(constraints) {
310 var iframe = document.createElement('iframe');
311 iframe.onload = onIframeLoaded;
312 document.body.appendChild(iframe);
313 iframe.src = window.location;
315 function onIframeLoaded() {
316 var iframe = window.document.querySelector('iframe');
317 iframe.contentWindow.navigator.webkitGetUserMedia(
318 constraints,
319 function (stream) {
320 window.parent.failTest('GetUserMedia call succeeded unexpectedly.');
322 function (error) {
323 // Remove the iframe from the parent within the callback scope.
324 window.parent.document.querySelector('iframe').remove();
325 // This function enqueues reporting test success, rather than doing
326 // it directly. We do this so we catch crashes that occur in the
327 // current execution context, but after reportTestSuccess is
328 // invoked.
329 setTimeout(function () {
330 window.parent.reportTestSuccess();
331 }, 0);
336 function failedCallback(error) {
337 failTest('GetUserMedia call failed with code ' + error.code);
340 function attachMediaStream(stream, videoElement) {
341 var localStreamUrl = URL.createObjectURL(stream);
342 $(videoElement).src = localStreamUrl;
345 function detectVideoInLocalView1(stream, callback) {
346 attachMediaStream(stream, 'local-view-1');
347 detectVideoPlaying('local-view-1', callback);
350 function displayDetectAndAnalyzeVideo(stream) {
351 displayDetectAndAnalyzeVideoInElement(stream,
352 function(aspectRatio) {
353 sendValueToTest(aspectRatio);
355 'local-view-1');
358 function displayDetectAndAnalyzeVideoInElement(
359 stream, callback, videoElementName) {
360 attachMediaStream(stream, videoElementName);
361 var videoElement = $(videoElementName);
362 var playbackTimeout = setTimeout(function() {
363 failTest("VideoElement playback not working.");
364 }, 1000);
365 videoElement.onloadedmetadata = function () {
366 clearTimeout(playbackTimeout);
367 detectAspectRatio(callback, videoElement);
371 function createAndRenderClone(stream) {
372 // TODO(perkj): --use-fake-device-for-media-stream do not currently
373 // work with audio devices and not all bots has a microphone.
374 newStream = new webkitMediaStream();
375 newStream.addTrack(stream.getVideoTracks()[0]);
376 assertEquals(newStream.getVideoTracks().length, 1);
377 if (stream.getAudioTracks().length > 0) {
378 newStream.addTrack(stream.getAudioTracks()[0]);
379 assertEquals(newStream.getAudioTracks().length, 1);
380 newStream.removeTrack(newStream.getAudioTracks()[0]);
381 assertEquals(newStream.getAudioTracks().length, 0);
384 detectVideoInLocalView1(newStream, reportTestSuccess);
387 // Calls stop on |stream|'s video track after a delay and reports success.
388 function waitAndStopVideoTrack(stream, waitTimeInSeconds) {
389 setTimeout(function() {
390 stream.getVideoTracks()[0].stop();
391 reportTestSuccess();
392 }, waitTimeInSeconds * 1000);
395 // This test makes sure multiple video renderers can be created for the same
396 // local video track, and makes sure a renderer can still render if other
397 // renderers are paused. See http://crbug/352619.
398 function createMultipleVideoRenderersAndPause(stream) {
399 function createDetectableRenderer(stream, id) {
400 var video = document.createElement('video');
401 document.body.appendChild(video);
402 var localStreamUrl = URL.createObjectURL(stream);
403 video.id = id;
404 video.style.display = 'none'
405 video.src = localStreamUrl;
406 video.autoplay = true;
407 video.play();
409 // The detector needs a canvas.
410 var canvas = document.createElement('canvas');
411 canvas.id = video.id + "-canvas";
412 canvas.style.display = 'none'
413 document.body.appendChild(canvas);
416 // Once 3 renderers are created and paused, create one last renderer and
417 // make sure it can play video.
418 setAllEventsOccuredHandler(function() {
419 var id = "lastVideoTag";
420 createDetectableRenderer(stream, id);
421 detectVideoPlaying(id, function () { reportTestSuccess(); });
424 // Create 3 video renderers and pause them once video is playing.
425 for (var i = 0; i < 3; ++i) {
426 var id = "video" + i;
427 createDetectableRenderer(stream, id);
428 addExpectedEvent();
429 // |video_detected_function| creates a new function that pause the video
430 // tag |id|.
431 var video_detected_function =
432 function (j) {
433 return function () {
434 console.log("pause " + j);
435 $(j).pause();
436 eventOccured();
439 // Detect video id |id| and trigger the function returned by
440 // |video_detected_function| when video is playing.
441 detectVideoPlaying(id, video_detected_function(id));
445 // This function tries to calculate the aspect ratio shown by the fake capture
446 // device in the video tag. For this, we count the amount of light green
447 // pixels along |aperture| pixels on the positive X and Y axis starting from
448 // the center of the image. In this very center there should be a time-varying
449 // pacman; the algorithm counts for a couple of iterations and keeps the
450 // maximum amount of light green pixels on both directions. From this data
451 // the aspect ratio is calculated and the test fails if the number of green
452 // pixels are not the same along the X and Y axis.
453 // The result of the analysis is sent back to the test as a string on the
454 // format "w=xxx:h=yyy".
455 function detectAspectRatio(callback, videoElement) {
456 var canvas = $(videoElement.id + '-canvas');
458 var maxLightGreenPixelsX = 0;
459 var maxLightGreenPixelsY = 0;
461 var attempt = 0;
462 var maxAttempts = 10;
464 var detectorFunction = function() {
465 var width = videoElement.videoWidth;
466 var height = videoElement.videoHeight;
467 if (width == 0 || height == 0)
468 failTest("VideoElement width and height set to 0.");
470 canvas.width = width;
471 canvas.height = height;
472 var aperture = Math.min(width, height) / 2;
473 var context = canvas.getContext('2d');
474 context.drawImage(videoElement, 0, 0, width, height);
476 // We are interested in a window starting from the center of the image
477 // where we expect the circle from the fake video capture to be rolling.
478 var pixels = context.getImageData(width / 2, height / 2,
479 aperture, aperture);
481 var lightGreenPixelsX = 0;
482 var lightGreenPixelsY = 0;
484 // Walk horizontally counting light green pixels.
485 for (var x = 0; x < aperture; ++x) {
486 if (pixels.data[4 * x + 1] != COLOR_BACKGROUND_GREEN)
487 lightGreenPixelsX++;
489 // Walk vertically counting light green pixels.
490 for (var y = 0; y < aperture; ++y) {
491 if (pixels.data[4 * y * aperture + 1] != COLOR_BACKGROUND_GREEN)
492 lightGreenPixelsY++;
494 if (lightGreenPixelsX > maxLightGreenPixelsX)
495 maxLightGreenPixelsX = lightGreenPixelsX;
496 if (lightGreenPixelsY > maxLightGreenPixelsY)
497 maxLightGreenPixelsY = lightGreenPixelsY;
499 // Allow maxLightGreenPixelsY = maxLightGreenPixelsX +-1 due to
500 // possible subpixel rendering on Mac and Android.
501 if (maxLightGreenPixelsY > maxLightGreenPixelsX + 1 ||
502 maxLightGreenPixelsY < maxLightGreenPixelsX -1 ||
503 maxLightGreenPixelsY == 0 ||
504 maxLightGreenPixelsX == width / 2 ||
505 maxLightGreenPixelsY == height / 2) {
506 if (++attempt > maxAttempts) {
507 clearInterval(detectorInterval);
508 failTest("Aspect ratio corrupted. X " + maxLightGreenPixelsX +
509 " Y " + maxLightGreenPixelsY);
511 else {
512 // We have a bad aspect ratio now; give a chance to shape up.
513 return;
517 clearInterval(detectorInterval);
518 var result = "w=" + width + ":h=" + height;
519 callback(result);
521 var detectorInterval = setInterval(detectorFunction, 50);
523 </script>
524 </head>
525 <body>
526 <table border="0">
527 <!-- Canvases are named after their corresponding video elements. -->
528 <tr>
529 <td><video id="local-view-1" width="320" height="240" autoplay
530 style="display:none"></video></td>
531 <td><canvas id="local-view-1-canvas" width="320" height="240"
532 style="display:none"></canvas></td>
533 </tr>
534 <tr>
535 <td><video id="local-view-2" width="320" height="240" autoplay
536 style="display:none"></video></td>
537 <td><canvas id="local-view-2-canvas" width="320" height="240"
538 style="display:none"></canvas></td>
539 </tr>
540 </table>
541 </body>
542 </html>