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