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.
5 // These must match with how the video and canvas tags are declared in html.
6 const VIDEO_TAG_WIDTH
= 320;
7 const VIDEO_TAG_HEIGHT
= 240;
9 // Fake video capture background green is of value 135.
10 const COLOR_BACKGROUND_GREEN
= 135;
12 // Number of test events to occur before the test pass. When the test pass,
13 // the function gAllEventsOccured is called.
14 var gNumberOfExpectedEvents
= 0;
16 // Number of events that currently have occurred.
17 var gNumberOfEvents
= 0;
19 var gAllEventsOccured = function () {};
21 // Use this function to set a function that will be called once all expected
22 // events has occurred.
23 function setAllEventsOccuredHandler(handler
) {
24 gAllEventsOccured
= handler
;
27 function detectVideoIn(videoElementName
, callback
) {
28 var width
= VIDEO_TAG_WIDTH
;
29 var height
= VIDEO_TAG_HEIGHT
;
30 var videoElement
= $(videoElementName
);
31 var canvas
= $(videoElementName
+ '-canvas');
32 var waitVideo
= setInterval(function() {
33 var context
= canvas
.getContext('2d');
34 context
.drawImage(videoElement
, 0, 0, width
, height
);
35 var pixels
= context
.getImageData(0, 0, width
, height
).data
;
37 if (isVideoPlaying(pixels
, width
, height
)) {
38 clearInterval(waitVideo
);
44 function waitForVideo(videoElement
) {
45 document
.title
= 'Waiting for video...';
47 detectVideoIn(videoElement
, function () { eventOccured(); });
50 function waitForConnectionToStabilize(peerConnection
) {
52 var waitForStabilization
= setInterval(function() {
53 if (peerConnection
.signalingState
== 'stable') {
54 clearInterval(waitForStabilization
);
60 function addExpectedEvent() {
61 ++gNumberOfExpectedEvents
;
64 function eventOccured() {
66 if (gNumberOfEvents
== gNumberOfExpectedEvents
) {
71 // This very basic video verification algorithm will be satisfied if any
72 // pixels are nonzero in a small sample area in the middle. It relies on the
73 // assumption that a video element with null source just presents zeroes.
74 function isVideoPlaying(pixels
, width
, height
) {
75 // Sample somewhere near the middle of the image.
76 var middle
= width
* height
/ 2;
77 for (var i
= 0; i
< 20; i
++) {
78 if (pixels
[middle
+ i
] > 0) {
85 // This function matches |left| and |right| and throws an exception if the
86 // values don't match.
87 function expectEquals(left
, right
) {
89 var s
= "expectEquals failed left: " + left
+ " right: " + right
;
95 // This function tries to calculate the aspect ratio shown by the fake capture
96 // device in the video tag. For this, we count the amount of light green pixels
97 // along |aperture| pixels on the positive X and Y axis starting from the
98 // center of the image. In this very center there should be a time-varying
99 // pacman; the algorithm counts for a couple of iterations and keeps the
100 // maximum amount of light green pixels on both directions. From this data
101 // the aspect ratio is calculated relative to a 320x240 window, so 4:3 would
102 // show as a 1. Furthermore, since an original non-4:3 might be letterboxed or
103 // cropped, the actual X and Y pixel amounts are compared with the fake video
104 // capture expected pacman radius (see further below).
105 function detectAspectRatio(callback
) {
106 var width
= VIDEO_TAG_WIDTH
;
107 var height
= VIDEO_TAG_HEIGHT
;
108 var videoElement
= $('local-view');
109 var canvas
= $('local-view-canvas');
111 var maxLightGreenPixelsX
= 0;
112 var maxLightGreenPixelsY
= 0;
114 var aperture
= Math
.min(width
, height
) / 2;
116 var maxIterations
= 10;
118 var waitVideo
= setInterval(function() {
119 var context
= canvas
.getContext('2d');
120 context
.drawImage(videoElement
, 0, 0, width
, height
);
122 // We are interested in a window starting from the center of the image
123 // where we expect the circle from the fake video capture to be rolling.
125 context
.getImageData(width
/ 2, height
/ 2, aperture
, aperture
);
127 var lightGreenPixelsX
= 0;
128 var lightGreenPixelsY
= 0;
130 // Walk horizontally counting light green pixels.
131 for (var x
= 0; x
< aperture
; ++x
) {
132 if (pixels
.data
[4 * x
+ 1] != COLOR_BACKGROUND_GREEN
)
135 // Walk vertically counting light green pixels.
136 for (var y
= 0; y
< aperture
; ++y
) {
137 if (pixels
.data
[4 * y
* aperture
+ 1] != 135)
140 if (lightGreenPixelsX
> maxLightGreenPixelsX
&&
141 lightGreenPixelsX
< aperture
)
142 maxLightGreenPixelsX
= lightGreenPixelsX
;
143 if (lightGreenPixelsY
> maxLightGreenPixelsY
&&
144 lightGreenPixelsY
< aperture
)
145 maxLightGreenPixelsY
= lightGreenPixelsY
;
147 var detectedAspectRatioString
= "";
148 if (++iterations
> maxIterations
) {
149 clearInterval(waitVideo
);
150 observedAspectRatio
= maxLightGreenPixelsY
/ maxLightGreenPixelsX
;
151 // At this point the observed aspect ratio is either 1, for undistorted
152 // 4:3, or some other aspect ratio that is seen as distorted.
153 if (Math
.abs(observedAspectRatio
- 1.333) < 0.1)
154 detectedAspectRatioString
= "16:9";
155 else if (Math
.abs(observedAspectRatio
- 1.20) < 0.1)
156 detectedAspectRatioString
= "16:10";
157 else if (Math
.abs(observedAspectRatio
- 1.0) < 0.1)
158 detectedAspectRatioString
= "4:3";
160 detectedAspectRatioString
= "UNKNOWN aspect ratio";
161 console
.log(detectedAspectRatioString
+ " observed aspect ratio (" +
162 observedAspectRatio
+ ")");
164 // The FakeVideoCapture calculates the circle radius as
165 // std::min(capture_format_.width, capture_format_.height) / 4;
166 // we do the same and see if both dimensions are scaled, meaning
167 // we started from a cropped or stretched image.
168 var nonDistortedRadius
= Math
.min(width
, height
) / 4;
169 if ((maxLightGreenPixelsX
!= nonDistortedRadius
) &&
170 (maxLightGreenPixelsY
!= nonDistortedRadius
)) {
171 detectedAspectRatioString
+= " cropped";
173 detectedAspectRatioString
+= " letterbox";
175 console
.log("Original image is: " + detectedAspectRatioString
);
176 callback(detectedAspectRatioString
);