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 }; 8 9 var gLocalStream = null; 10 11 setAllEventsOccuredHandler(function() { 12 gLocalStream.stop(); 13 reportTestSuccess(); 14 }); 15 16 function getSources() { 17 MediaStreamTrack.getSources(function(devices) { 18 document.title = 'Media devices available'; 19 sendValueToTest(JSON.stringify(devices)); 20 }); 21 } 22 23 // Creates a MediaStream and renders it locally. When the video is detected to 24 // be rolling, the stream should be stopped. 25 function getUserMediaAndStop(constraints) { 26 console.log('Calling getUserMediaAndStop.'); 27 navigator.webkitGetUserMedia( 28 constraints, 29 function(stream) { displayAndDetectVideo(stream, stopVideoTrack); }, 30 failedCallback); 31 } 32 33 // Requests getusermedia and expects it to fail. The error name is returned 34 // to the test. 35 function getUserMediaAndExpectFailure(constraints) { 36 console.log('Calling getUserMediaAndExpectFailure.'); 37 navigator.webkitGetUserMedia( 38 constraints, 39 function(stream) { failTest('Unexpectedly succeeded getUserMedia.'); }, 40 function(error) { sendValueToTest(error.name); }); 41 } 42 43 function renderClonedMediastreamAndStop(constraints, waitTimeInSeconds) { 44 console.log('Calling renderClonedMediastreamAndStop.'); 45 navigator.webkitGetUserMedia( 46 constraints, 47 function(stream) { 48 var s = stream.clone(); 49 assertEquals(stream.getVideoTracks().length, 1); 50 assertEquals(s.getVideoTracks().length, 1); 51 assertNotEquals(stream.getVideoTracks()[0].id, 52 s.getVideoTracks()[0].id); 53 displayAndDetectVideo( 54 s, 55 function() { 56 reportTestSuccess(); 57 }); 58 }, 59 failedCallback); 60 } 61 62 function renderDuplicatedMediastreamAndStop(constraints, waitTimeInSeconds) { 63 console.log('Calling renderDuplicateMediastreamAndStop.'); 64 navigator.webkitGetUserMedia( 65 constraints, 66 function(stream) { 67 s = new webkitMediaStream(stream); 68 assertEquals(stream.getVideoTracks().length, 1); 69 assertEquals(s.getVideoTracks().length, 1); 70 assertEquals(stream.getVideoTracks()[0].id, 71 s.getVideoTracks()[0].id); 72 displayAndDetectVideo( 73 s, 74 function() { 75 reportTestSuccess(); 76 }); 77 }, 78 failedCallback); 79 } 80 81 function renderSameTrackMediastreamAndStop(constraints, waitTimeInSeconds) { 82 console.log('Calling renderSameTrackMediastreamAndStop.'); 83 navigator.webkitGetUserMedia( 84 constraints, 85 function(stream) { 86 s = new webkitMediaStream(); 87 s.addTrack(stream.getVideoTracks()[0]); 88 assertEquals(s.getVideoTracks().length, 1); 89 assertEquals(s.getVideoTracks().length, 1); 90 assertEquals(stream.getVideoTracks()[0].id, s.getVideoTracks()[0].id); 91 displayAndDetectVideo( 92 s, 93 function() { 94 reportTestSuccess(); 95 }); 96 }, 97 failedCallback); 98 } 99 100 function renderClonedTrackMediastreamAndStop(constraints, waitTimeInSeconds) { 101 console.log('Calling renderClonedTrackMediastreamAndStop.'); 102 navigator.webkitGetUserMedia( 103 constraints, 104 function(stream) { 105 s = new webkitMediaStream(); 106 s.addTrack(stream.getVideoTracks()[0].clone()); 107 assertEquals(s.getVideoTracks().length, 1); 108 assertEquals(s.getVideoTracks().length, 1); 109 assertNotEquals(stream.getVideoTracks()[0].id, 110 s.getVideoTracks()[0].id) 111 displayAndDetectVideo( 112 s, 113 function() { 114 reportTestSuccess(); 115 }); 116 }, 117 failedCallback); 118 } 119 120 // Creates a MediaStream and renders it locally. When the video is detected to 121 // be rolling we return ok-stream-running through the automation controller. 122 function getUserMediaAndGetStreamUp(constraints, waitTimeInSeconds) { 123 console.log('Calling getUserMediaAndGetStreamUp.'); 124 navigator.webkitGetUserMedia( 125 constraints, 126 function(stream) { 127 displayAndDetectVideo( 128 stream, 129 function() { 130 reportTestSuccess(); 131 }); 132 }, 133 failedCallback); 134 } 135 136 function getUserMediaAndRenderInSeveralVideoTags() { 137 navigator.webkitGetUserMedia( 138 {video: true}, 139 createMultipleVideoRenderersAndPause, 140 function(error) { failedCallback(); }); 141 } 142 143 // Gets a video stream up, analyses it and returns the aspect ratio to the 144 // test through the automation controller. 145 function getUserMediaAndAnalyseAndStop(constraints) { 146 console.log('Calling getUserMediaAndAnalyseAndStop.'); 147 navigator.webkitGetUserMedia( 148 constraints, displayDetectAndAnalyzeVideo, failedCallback); 149 } 150 151 // This test that a MediaStream can be cloned and that the clone can 152 // be rendered. 153 function getUserMediaAndClone() { 154 console.log('Calling getUserMediaAndClone.'); 155 navigator.webkitGetUserMedia({video: true, audio: true}, 156 createAndRenderClone, failedCallback); 157 } 158 159 // Creates two MediaStream and renders them locally. When the video of both 160 // streams are detected to be rolling, we stop the local video tracks one at 161 // the time. 162 function twoGetUserMediaAndStop(constraints) { 163 console.log('Calling Two GetUserMedia'); 164 navigator.webkitGetUserMedia( 165 constraints, 166 function(stream) { 167 displayAndDetectVideo(stream, requestSecondGetUserMedia); 168 }, 169 failedCallback); 170 var requestSecondGetUserMedia = function() { 171 navigator.webkitGetUserMedia( 172 constraints, 173 function(stream) { 174 displayIntoVideoElement(stream, 175 function() { 176 stopBothVideoTracksAndVerify(stream); 177 }, 178 'local-view-2'); 179 }, 180 failedCallback); 181 }; 182 183 var stopBothVideoTracksAndVerify = function(streamPlayingInLocalView2) { 184 streamPlayingInLocalView2.getVideoTracks()[0].stop(); 185 waitForVideoToStop('local-view-2'); 186 // Make sure the video track in gLocalStream is still playing in 187 // 'local-view1' and then stop it. 188 displayAndDetectVideo(gLocalStream, stopVideoTrack); 189 }; 190 } 191 192 function twoGetUserMedia(constraints1, 193 constraints2) { 194 var result=""; 195 navigator.webkitGetUserMedia( 196 constraints1, 197 function(stream) { 198 displayDetectAndAnalyzeVideoInElement( 199 stream, 200 function(aspectRatio) { 201 result = aspectRatio; 202 requestSecondGetUserMedia(); 203 }, 204 'local-view'); 205 }, 206 failedCallback); 207 var requestSecondGetUserMedia = function() { 208 navigator.webkitGetUserMedia( 209 constraints2, 210 function(stream) { 211 displayDetectAndAnalyzeVideoInElement( 212 stream, 213 function(aspectRatio) { 214 result = result + '-' + aspectRatio; 215 sendValueToTest(result); 216 }, 217 'local-view-2'); 218 }, 219 failedCallback); 220 } 221 } 222 223 // Calls GetUserMedia twice and verify that the frame rate is as expected for 224 // both streams. 225 function twoGetUserMediaAndVerifyFrameRate(constraints1, 226 constraints2, 227 expected_frame_rate1, 228 expected_frame_rate2) { 229 addExpectedEvent(); 230 addExpectedEvent(); 231 var validateFrameRateCallback = function (success) { 232 if (!success) 233 failTest("Failed to validate frameRate."); 234 eventOccured(); 235 }; 236 237 navigator.webkitGetUserMedia( 238 constraints1, 239 function(stream) { 240 requestSecondGetUserMedia(); 241 plugStreamIntoVideoElement(stream, 'local-view'); 242 detectVideoPlaying('local-view', 243 function() { 244 validateFrameRate('local-view', expected_frame_rate1, 245 validateFrameRateCallback); 246 }); 247 }, 248 failedCallback); 249 var requestSecondGetUserMedia = function() { 250 navigator.webkitGetUserMedia( 251 constraints2, 252 function(stream) { 253 plugStreamIntoVideoElement(stream, 'local-view-2'); 254 detectVideoPlaying('local-view-2', 255 function() { 256 validateFrameRate('local-view-2', expected_frame_rate2, 257 validateFrameRateCallback); 258 }); 259 }, 260 failedCallback); 261 } 262 } 263 264 function failedCallback(error) { 265 failTest('GetUserMedia call failed with code ' + error.code); 266 } 267 268 function plugStreamIntoVideoElement(stream, videoElement) { 269 gLocalStream = stream; 270 var localStreamUrl = URL.createObjectURL(stream); 271 $(videoElement).src = localStreamUrl; 272 } 273 274 function displayIntoVideoElement(stream, callback, videoElement) { 275 plugStreamIntoVideoElement(stream, videoElement); 276 detectVideoPlaying(videoElement, callback); 277 } 278 279 function displayAndDetectVideo(stream, callback) { 280 displayIntoVideoElement(stream, callback, 'local-view'); 281 } 282 283 function displayDetectAndAnalyzeVideo(stream) { 284 displayDetectAndAnalyzeVideoInElement(stream, 285 function(aspectRatio) { 286 sendValueToTest(aspectRatio); 287 }, 288 'local-view'); 289 } 290 291 function displayDetectAndAnalyzeVideoInElement( 292 stream, callback, videoElement) { 293 plugStreamIntoVideoElement(stream, videoElement); 294 detectAspectRatio(callback, videoElement); 295 } 296 297 function createAndRenderClone(stream) { 298 gLocalStream = stream; 299 // TODO(perkj): --use-fake-device-for-media-stream do not currently 300 // work with audio devices and not all bots has a microphone. 301 new_stream = new webkitMediaStream(); 302 new_stream.addTrack(stream.getVideoTracks()[0]); 303 assertEquals(new_stream.getVideoTracks().length, 1); 304 if (stream.getAudioTracks().length > 0) { 305 new_stream.addTrack(stream.getAudioTracks()[0]); 306 assertEquals(new_stream.getAudioTracks().length, 1); 307 new_stream.removeTrack(new_stream.getAudioTracks()[0]); 308 assertEquals(new_stream.getAudioTracks().length, 0); 309 } 310 311 var newStreamUrl = URL.createObjectURL(new_stream); 312 $('local-view').src = newStreamUrl; 313 waitForVideo('local-view'); 314 } 315 316 function stopVideoTrack() { 317 gLocalStream.getVideoTracks()[0].stop(); 318 waitForVideoToStop('local-view'); 319 } 320 321 function waitAndStopVideoTrack(waitTimeInSeconds) { 322 setTimeout(stopVideoTrack, waitTimeInSeconds * 1000); 323 } 324 325 // This test make sure multiple video renderers can be created for the same 326 // local video track and make sure a renderer can still render if other 327 // renderers are paused. See http://crbug/352619. 328 function createMultipleVideoRenderersAndPause(stream) { 329 function createDetectableRenderer(stream, id) { 330 var video = document.createElement('video'); 331 document.body.appendChild(video); 332 var localStreamUrl = URL.createObjectURL(stream); 333 video.id = id; 334 video.src = localStreamUrl; 335 video.autoplay = true; 336 video.play(); 337 // The detector needs a canvas. 338 var canvas = document.createElement('canvas'); 339 canvas.id = video.id + "-canvas"; 340 document.body.appendChild(canvas); 341 }; 342 343 // Once 3 renderers are created and paused, create one last renderer and 344 // make sure it can play video. 345 setAllEventsOccuredHandler(function() { 346 var id = "lastVideoTag"; 347 createDetectableRenderer(stream, id); 348 detectVideoPlaying(id, function () { reportTestSuccess(); }); 349 }); 350 351 // Create 3 video renderers and pause them once video is playing. 352 for (var i = 0; i < 3; ++i) { 353 var id = "video" + i; 354 createDetectableRenderer(stream, id); 355 addExpectedEvent(); 356 // |video_detected_function| creates a new function that pause the video 357 // tag |id|. 358 var video_detected_function = 359 function (j) { 360 return function () { 361 console.log("pause " + j); 362 $(j).pause(); 363 eventOccured(); 364 }; 365 }; 366 // Detect video id |id| and trigger the function returned by 367 // |video_detected_function| when video is playing. 368 detectVideoPlaying(id, video_detected_function(id)); 369 } 370 } 371 372 // This function tries to calculate the aspect ratio shown by the fake capture 373 // device in the video tag. For this, we count the amount of light green 374 // pixels along |aperture| pixels on the positive X and Y axis starting from 375 // the center of the image. In this very center there should be a time-varying 376 // pacman; the algorithm counts for a couple of iterations and keeps the 377 // maximum amount of light green pixels on both directions. From this data 378 // the aspect ratio is calculated and the test fails if the number of green 379 // pixels are not the same along the X and Y axis. 380 // The result of the analysis is sent back to the test as a string on the 381 // format "w=xxx:h=yyy". 382 function detectAspectRatio(callback, videoElementName) { 383 var videoElement = $(videoElementName); 384 var canvas = $(videoElementName + '-canvas'); 385 386 var maxLightGreenPixelsX = 0; 387 var maxLightGreenPixelsY = 0; 388 389 var iterations = 0; 390 var maxIterations = 10; 391 392 var detectorFunction = function() { 393 var width = videoElement.videoWidth; 394 var height = videoElement.videoHeight; 395 if (width == 0 || height == 0) 396 return; 397 398 canvas.width = width; 399 canvas.height = height; 400 var aperture = Math.min(width, height) / 2; 401 var context = canvas.getContext('2d'); 402 context.drawImage(videoElement, 0, 0, width, height); 403 404 // We are interested in a window starting from the center of the image 405 // where we expect the circle from the fake video capture to be rolling. 406 var pixels = context.getImageData(width / 2, height / 2, 407 aperture, aperture); 408 409 var lightGreenPixelsX = 0; 410 var lightGreenPixelsY = 0; 411 412 // Walk horizontally counting light green pixels. 413 for (var x = 0; x < aperture; ++x) { 414 if (pixels.data[4 * x + 1] != COLOR_BACKGROUND_GREEN) 415 lightGreenPixelsX++; 416 } 417 // Walk vertically counting light green pixels. 418 for (var y = 0; y < aperture; ++y) { 419 if (pixels.data[4 * y * aperture + 1] != COLOR_BACKGROUND_GREEN) 420 lightGreenPixelsY++; 421 } 422 if (lightGreenPixelsX > maxLightGreenPixelsX) 423 maxLightGreenPixelsX = lightGreenPixelsX; 424 if (lightGreenPixelsY > maxLightGreenPixelsY) 425 maxLightGreenPixelsY = lightGreenPixelsY; 426 427 if (++iterations > maxIterations) { 428 clearInterval(detectorInterval); 429 // Allow maxLightGreenPixelsY = maxLightGreenPixelsX +-1 due to 430 // possible subpixel rendering on Mac and Android. 431 if (maxLightGreenPixelsY > maxLightGreenPixelsX + 1 || 432 maxLightGreenPixelsY < maxLightGreenPixelsX -1 || 433 maxLightGreenPixelsY == 0 || 434 maxLightGreenPixelsX == width / 2 || 435 maxLightGreenPixelsY == height / 2) { 436 failTest("Aspect ratio corrupted. X " + maxLightGreenPixelsX + 437 " Y " + maxLightGreenPixelsY); 438 } 439 440 var result = "w=" + width + ":h=" + height; 441 console.log(result); 442 callback(result); 443 } 444 } 445 var detectorInterval = setInterval(detectorFunction, 50); 446 } 447 </script> 448 </head> 449 <body> 450 <table border="0"> 451 <tr> 452 <td>Local Preview</td> 453 </tr> 454 <tr> 455 <td><video width="320" height="240" id="local-view" 456 autoplay="autoplay"></video></td> 457 <td><canvas id="local-view-canvas" 458 style="display:none"></canvas></td> 459 </tr> 460 <tr> 461 <td>Local Preview 2</td> 462 </tr> 463 <tr> 464 <td><video width="320" height="240" id="local-view-2" 465 autoplay="autoplay"></video></td> 466 <!-- Canvases are named after their corresponding video elements. --> 467 <td><canvas width="320" height="240" id="local-view-2-canvas" 468 style="display:none"></canvas></td> 469 </tr> 470 </table> 471 </body> 472 </html> 473