1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// These must match with how the video and canvas tags are declared in html. 6const VIDEO_TAG_WIDTH = 320; 7const VIDEO_TAG_HEIGHT = 240; 8 9// Fake video capture background green is of value 135. 10const COLOR_BACKGROUND_GREEN = 135; 11 12// Number of test events to occur before the test pass. When the test pass, 13// the function gAllEventsOccured is called. 14var gNumberOfExpectedEvents = 0; 15 16// Number of events that currently have occurred. 17var gNumberOfEvents = 0; 18 19var gAllEventsOccured = function () {}; 20 21// Use this function to set a function that will be called once all expected 22// events has occurred. 23function setAllEventsOccuredHandler(handler) { 24 gAllEventsOccured = handler; 25} 26 27function detectVideoPlaying(videoElementName, callback) { 28 detectVideo(videoElementName, isVideoPlaying, callback); 29} 30 31function detectVideoStopped(videoElementName, callback) { 32 detectVideo(videoElementName, 33 function (pixels, previous_pixels) { 34 return !isVideoPlaying(pixels, previous_pixels); 35 }, 36 callback); 37} 38 39function detectVideo(videoElementName, predicate, callback) { 40 var width = VIDEO_TAG_WIDTH; 41 var height = VIDEO_TAG_HEIGHT; 42 var videoElement = $(videoElementName); 43 var canvas = $(videoElementName + '-canvas'); 44 var old_pixels = []; 45 var waitVideo = setInterval(function() { 46 var context = canvas.getContext('2d'); 47 context.drawImage(videoElement, 0, 0, width, height); 48 var pixels = context.getImageData(0, 0 , width, height / 3).data; 49 // Check that there is an old and a new picture with the same size to 50 // compare and use the function |predicate| to detect the video state in 51 // that case. 52 if (old_pixels.length == pixels.length && 53 predicate(pixels, old_pixels)) { 54 clearInterval(waitVideo); 55 callback(); 56 } 57 old_pixels = pixels; 58 }, 200); 59} 60 61function waitForVideo(videoElement) { 62 document.title = 'Waiting for video...'; 63 addExpectedEvent(); 64 detectVideoPlaying(videoElement, function () { eventOccured(); }); 65} 66 67function waitForVideoToStop(videoElement) { 68 document.title = 'Waiting for video to stop...'; 69 addExpectedEvent(); 70 detectVideoStopped(videoElement, function () { eventOccured(); }); 71} 72 73function waitForConnectionToStabilize(peerConnection) { 74 addExpectedEvent(); 75 var waitForStabilization = setInterval(function() { 76 if (peerConnection.signalingState == 'stable') { 77 clearInterval(waitForStabilization); 78 eventOccured(); 79 } 80 }, 100); 81} 82 83function addExpectedEvent() { 84 ++gNumberOfExpectedEvents; 85} 86 87function eventOccured() { 88 ++gNumberOfEvents; 89 if (gNumberOfEvents == gNumberOfExpectedEvents) { 90 gAllEventsOccured(); 91 } 92} 93 94// This very basic video verification algorithm will be satisfied if any 95// pixels are changed. 96function isVideoPlaying(pixels, previous_pixels) { 97 for (var i = 0; i < pixels.length; i++) { 98 if (pixels[i] != previous_pixels[i]) { 99 return true; 100 } 101 } 102 return false; 103} 104 105// This function matches |left| and |right| and throws an exception if the 106// values don't match. 107function expectEquals(left, right) { 108 if (left != right) { 109 var s = "expectEquals failed left: " + left + " right: " + right; 110 document.title = s; 111 throw s; 112 } 113} 114 115// This function tries to calculate the aspect ratio shown by the fake capture 116// device in the video tag. For this, we count the amount of light green pixels 117// along |aperture| pixels on the positive X and Y axis starting from the 118// center of the image. In this very center there should be a time-varying 119// pacman; the algorithm counts for a couple of iterations and keeps the 120// maximum amount of light green pixels on both directions. From this data 121// the aspect ratio is calculated relative to a 320x240 window, so 4:3 would 122// show as a 1. Furthermore, since an original non-4:3 might be letterboxed or 123// cropped, the actual X and Y pixel amounts are compared with the fake video 124// capture expected pacman radius (see further below). 125function detectAspectRatio(callback) { 126 var width = VIDEO_TAG_WIDTH; 127 var height = VIDEO_TAG_HEIGHT; 128 var videoElement = $('local-view'); 129 var canvas = $('local-view-canvas'); 130 131 var maxLightGreenPixelsX = 0; 132 var maxLightGreenPixelsY = 0; 133 134 var aperture = Math.min(width, height) / 2; 135 var iterations = 0; 136 var maxIterations = 10; 137 138 var waitVideo = setInterval(function() { 139 var context = canvas.getContext('2d'); 140 context.drawImage(videoElement, 0, 0, width, height); 141 142 // We are interested in a window starting from the center of the image 143 // where we expect the circle from the fake video capture to be rolling. 144 var pixels = 145 context.getImageData(width / 2, height / 2, aperture, aperture); 146 147 var lightGreenPixelsX = 0; 148 var lightGreenPixelsY = 0; 149 150 // Walk horizontally counting light green pixels. 151 for (var x = 0; x < aperture; ++x) { 152 if (pixels.data[4 * x + 1] != COLOR_BACKGROUND_GREEN) 153 lightGreenPixelsX++; 154 } 155 // Walk vertically counting light green pixels. 156 for (var y = 0; y < aperture; ++y) { 157 if (pixels.data[4 * y * aperture + 1] != 135) 158 lightGreenPixelsY++; 159 } 160 if (lightGreenPixelsX > maxLightGreenPixelsX && 161 lightGreenPixelsX < aperture) 162 maxLightGreenPixelsX = lightGreenPixelsX; 163 if (lightGreenPixelsY > maxLightGreenPixelsY && 164 lightGreenPixelsY < aperture) 165 maxLightGreenPixelsY = lightGreenPixelsY; 166 167 var detectedAspectRatioString = ""; 168 if (++iterations > maxIterations) { 169 clearInterval(waitVideo); 170 observedAspectRatio = maxLightGreenPixelsY / maxLightGreenPixelsX; 171 // At this point the observed aspect ratio is either 1, for undistorted 172 // 4:3, or some other aspect ratio that is seen as distorted. 173 if (Math.abs(observedAspectRatio - 1.333) < 0.1) 174 detectedAspectRatioString = "16:9"; 175 else if (Math.abs(observedAspectRatio - 1.20) < 0.1) 176 detectedAspectRatioString = "16:10"; 177 else if (Math.abs(observedAspectRatio - 1.0) < 0.1) 178 detectedAspectRatioString = "4:3"; 179 else 180 detectedAspectRatioString = "UNKNOWN aspect ratio"; 181 console.log(detectedAspectRatioString + " observed aspect ratio (" + 182 observedAspectRatio + ")"); 183 184 // The FakeVideoCapture calculates the circle radius as 185 // std::min(capture_format_.width, capture_format_.height) / 4; 186 // we do the same and see if both dimensions are scaled, meaning 187 // we started from a cropped or stretched image. 188 var nonDistortedRadius = Math.min(width, height) / 4; 189 if ((maxLightGreenPixelsX != nonDistortedRadius) && 190 (maxLightGreenPixelsY != nonDistortedRadius)) { 191 detectedAspectRatioString += " cropped"; 192 } else 193 detectedAspectRatioString += " letterbox"; 194 195 console.log("Original image is: " + detectedAspectRatioString); 196 callback(detectedAspectRatioString); 197 } 198 }, 199 50); 200} 201