• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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