1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chromeos/login/camera.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <stdlib.h>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <fcntl.h>  // low-level i/o
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <unistd.h>
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <errno.h>
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sys/stat.h>
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sys/types.h>
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sys/time.h>
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sys/mman.h>
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sys/ioctl.h>
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <asm/types.h>  // for videodev2.h
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <linux/videodev2.h>
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector>
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/stringprintf.h"
253f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread.h"
26731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/time.h"
27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h"
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "skia/ext/image_operations.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkBitmap.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkColorPriv.h"
3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/size.h"
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace chromeos {
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Logs errno number and its text.
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid log_errno(const std::string& message) {
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  LOG(ERROR) << message << " errno: " << errno << ", " << strerror(errno);
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Helpful wrapper around ioctl that retries it upon failure in cases when
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// this is appropriate.
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint xioctl(int fd, int request, void* arg) {
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int r;
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  do {
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    r = ioctl(fd, request, arg);
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } while (r == -1 && errno == EINTR);
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return r;
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Clips integer value to 1 byte boundaries. Saturates the result on
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// overflow or underflow.
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochuint8_t clip_to_byte(int value) {
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (value > 255)
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    value = 255;
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (value < 0)
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    value = 0;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<uint8_t>(value);
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Converts color from YUV colorspace to RGB. Returns the result in Skia
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// format suitable for use with SkBitmap. For the formula see
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// "Converting between YUV and RGB" article on MSDN:
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// http://msdn.microsoft.com/en-us/library/ms893078.aspx
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochuint32_t convert_yuv_to_rgba(int y, int u, int v) {
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int c = y - 16;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int d = u - 128;
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int e = v - 128;
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  uint8_t r = clip_to_byte((298 * c + 409 * e + 128) >> 8);
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  uint8_t g = clip_to_byte((298 * c - 100 * d - 208 * e + 128) >> 8);
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  uint8_t b = clip_to_byte((298 * c + 516 * d + 128) >> 8);
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return SkPackARGB32(255U, r, g, b);
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Enumerates available frame sizes for specified pixel format and picks up the
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// best one to set for the desired image resolution.
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Size get_best_frame_size(int fd,
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              int pixel_format,
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              int desired_width,
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              int desired_height) {
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  v4l2_frmsizeenum size = {};
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size.index = 0;
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size.pixel_format = pixel_format;
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<gfx::Size> sizes;
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int r = xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size);
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (r != -1) {
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (size.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      sizes.push_back(gfx::Size(size.discrete.width, size.discrete.height));
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ++size.index;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    r = xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &size);
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (sizes.empty()) {
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return gfx::Size(desired_width, desired_height);
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < sizes.size(); ++i) {
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (sizes[i].width() >= desired_width &&
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        sizes[i].height() >= desired_height)
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return sizes[i];
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If higher resolution is not available, choose the highest available.
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t max_size_index = 0;
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int max_area = sizes[0].GetArea();
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 1; i < sizes.size(); ++i) {
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (sizes[i].GetArea() > max_area) {
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      max_size_index = i;
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      max_area = sizes[i].GetArea();
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return sizes[max_size_index];
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Default camera device name.
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char kDeviceName[] = "/dev/video0";
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Default width of each frame received from the camera.
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kFrameWidth = 640;
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Default height of each frame received from the camera.
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kFrameHeight = 480;
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Number of buffers to request from the device.
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kRequestBuffersCount = 4;
123731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Timeout for select() call in microseconds.
124731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickconst long kSelectTimeout = 1 * base::Time::kMicrosecondsPerSecond;
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Camera, public members:
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
131513209b27ff55e2841eac0e4120199c23acce758Ben MurdochCamera::Camera(Delegate* delegate, base::Thread* thread, bool mirrored)
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : delegate_(delegate),
133513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      thread_(thread),
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      device_name_(kDeviceName),
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      device_descriptor_(-1),
136731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      is_capturing_(false),
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      desired_width_(kFrameWidth),
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      desired_height_(kFrameHeight),
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      frame_width_(kFrameWidth),
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      frame_height_(kFrameHeight),
141731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      mirrored_(mirrored) {
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCamera::~Camera() {
145731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK_EQ(-1, device_descriptor_) << "Don't forget to uninitialize camera.";
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
148731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::ReportFailure() {
149513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
150731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (device_descriptor_ == -1) {
151731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    BrowserThread::PostTask(
152731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        BrowserThread::UI,
153731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        FROM_HERE,
154731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        NewRunnableMethod(this,
155731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                          &Camera::OnInitializeFailure));
156731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (!is_capturing_) {
157731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    BrowserThread::PostTask(
158731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        BrowserThread::UI,
159731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        FROM_HERE,
160731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        NewRunnableMethod(this,
161731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                          &Camera::OnStartCapturingFailure));
162731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else {
163731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    BrowserThread::PostTask(
164731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        BrowserThread::UI,
165731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        FROM_HERE,
166731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        NewRunnableMethod(this,
167731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                          &Camera::OnCaptureFailure));
168731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
169731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
170731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
171731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::Initialize(int desired_width, int desired_height) {
172731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
173513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  PostCameraTask(
174731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      FROM_HERE,
175731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      NewRunnableMethod(this,
176731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                        &Camera::DoInitialize,
177731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                        desired_width,
178731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                        desired_height));
179731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
180731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
181731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::DoInitialize(int desired_width, int desired_height) {
182513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
183731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (device_descriptor_ != -1) {
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << "Camera is initialized already.";
186731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
188731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int fd = OpenDevice(device_name_.c_str());
190731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (fd == -1) {
191731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
192731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
193731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  v4l2_capability cap;
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (errno == EINVAL)
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(ERROR) << device_name_ << " is no V4L2 device";
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      log_errno("VIDIOC_QUERYCAP failed.");
201731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
202731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(ERROR) << device_name_ << " is no video capture device";
206731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
207731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(ERROR) << device_name_ << " does not support streaming i/o";
211731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
212731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size frame_size = get_best_frame_size(fd,
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             V4L2_PIX_FMT_YUYV,
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             desired_width,
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             desired_height);
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  v4l2_format format = {};
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  format.fmt.pix.width = frame_size.width();
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  format.fmt.pix.height = frame_size.height();
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  format.fmt.pix.field = V4L2_FIELD_INTERLACED;
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (xioctl(fd, VIDIOC_S_FMT, &format) == -1) {
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(ERROR) << "VIDIOC_S_FMT failed.";
227731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
228731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
231731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!InitializeReadingMode(fd)) {
232731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
233731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
234731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  device_descriptor_ = fd;
237dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  frame_width_ = format.fmt.pix.width;
238dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  frame_height_ = format.fmt.pix.height;
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  desired_width_ = desired_width;
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  desired_height_ = desired_height;
241731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
242731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::UI,
243731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      FROM_HERE,
244731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      NewRunnableMethod(this, &Camera::OnInitializeSuccess));
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid Camera::Uninitialize() {
248731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
249513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  PostCameraTask(FROM_HERE, NewRunnableMethod(this, &Camera::DoUninitialize));
250731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
251731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
252731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::DoUninitialize() {
253513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (device_descriptor_ == -1) {
255731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    LOG(WARNING) << "Calling uninitialize for uninitialized camera.";
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
258731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DoStopCapturing();
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UnmapVideoBuffers();
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (close(device_descriptor_) == -1)
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    log_errno("Closing the device failed.");
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  device_descriptor_ = -1;
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
265513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid Camera::StartCapturing() {
266731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  PostCameraTask(FROM_HERE,
268513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                 NewRunnableMethod(this, &Camera::DoStartCapturing));
269731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
270731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
271513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid Camera::DoStartCapturing() {
272513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
273731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (is_capturing_) {
274731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    LOG(WARNING) << "Capturing is already started.";
275731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
277731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < buffers_.size(); ++i) {
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    v4l2_buffer buffer = {};
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    buffer.memory = V4L2_MEMORY_MMAP;
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    buffer.index = i;
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (xioctl(device_descriptor_, VIDIOC_QBUF, &buffer) == -1) {
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      log_errno("VIDIOC_QBUF failed.");
285731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      ReportFailure();
286731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return;
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (xioctl(device_descriptor_, VIDIOC_STREAMON, &type) == -1) {
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    log_errno("VIDIOC_STREAMON failed.");
292731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
293731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
295513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // No need to post DidProcessCameraThreadMethod() as this method is
296513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // being posted instead.
297731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
298731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::UI,
299731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      FROM_HERE,
300731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      NewRunnableMethod(this,
301731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                        &Camera::OnStartCapturingSuccess));
302731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  is_capturing_ = true;
303513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  PostCameraTask(FROM_HERE,
304513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                 NewRunnableMethod(this, &Camera::OnCapture));
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid Camera::StopCapturing() {
308731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
309513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  PostCameraTask(FROM_HERE,
310513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                 NewRunnableMethod(this, &Camera::DoStopCapturing));
311731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
312731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
313731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::DoStopCapturing() {
314513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
315731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!is_capturing_) {
316731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    LOG(WARNING) << "Calling StopCapturing when capturing is not started.";
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
319731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // OnCapture must exit if this flag is not set.
320731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  is_capturing_ = false;
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (xioctl(device_descriptor_, VIDIOC_STREAMOFF, &type) == -1)
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    log_errno("VIDIOC_STREAMOFF failed.");
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
326513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid Camera::GetFrame(SkBitmap* frame) {
32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::AutoLock lock(image_lock_);
328513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  frame->swap(frame_image_);
329513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
330513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Camera, private members:
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint Camera::OpenDevice(const char* device_name) const {
335513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  struct stat st;
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (stat(device_name, &st) == -1) {
3383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    log_errno(base::StringPrintf("Cannot identify %s", device_name));
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!S_ISCHR(st.st_mode)) {
342ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    LOG(ERROR) << device_name << "is not a device";
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int fd = open(device_name, O_RDWR | O_NONBLOCK, 0);
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (fd == -1) {
3473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    log_errno(base::StringPrintf("Cannot open %s", device_name));
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return fd;
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool Camera::InitializeReadingMode(int fd) {
354513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  v4l2_requestbuffers req;
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  req.count = kRequestBuffersCount;
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  req.memory = V4L2_MEMORY_MMAP;
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (errno == EINVAL)
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(ERROR) << device_name_ << " does not support memory mapping.";
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      log_errno("VIDIOC_REQBUFS failed.");
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (req.count < 2U) {
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(ERROR) << "Insufficient buffer memory on " << device_name_;
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (unsigned i = 0; i < req.count; ++i) {
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    v4l2_buffer buffer = {};
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    buffer.memory = V4L2_MEMORY_MMAP;
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    buffer.index = i;
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (xioctl(fd, VIDIOC_QUERYBUF, &buffer) == -1) {
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      log_errno("VIDIOC_QUERYBUF failed.");
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    VideoBuffer video_buffer;
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    video_buffer.length = buffer.length;
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    video_buffer.start = mmap(
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NULL,  // Start anywhere.
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        buffer.length,
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        PROT_READ | PROT_WRITE,
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        MAP_SHARED,
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        fd,
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        buffer.m.offset);
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (video_buffer.start == MAP_FAILED) {
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      log_errno("mmap() failed.");
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UnmapVideoBuffers();
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    buffers_.push_back(video_buffer);
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid Camera::UnmapVideoBuffers() {
399513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < buffers_.size(); ++i) {
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (munmap(buffers_[i].start, buffers_[i].length) == -1)
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      log_errno("munmap failed.");
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid Camera::OnCapture() {
407513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
408731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!is_capturing_)
409731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return;
410731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  do {
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    fd_set fds;
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FD_ZERO(&fds);
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FD_SET(device_descriptor_, &fds);
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    timeval tv = {};
417731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    tv.tv_sec = kSelectTimeout / base::Time::kMicrosecondsPerSecond;
418731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    tv.tv_usec = kSelectTimeout % base::Time::kMicrosecondsPerSecond;
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int result = select(device_descriptor_ + 1, &fds, NULL, NULL, &tv);
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (result == -1) {
422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (errno == EINTR)
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      log_errno("select() failed.");
425731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      ReportFailure();
426731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (result == 0) {
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(ERROR) << "select() timeout.";
430731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      ReportFailure();
431731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // EAGAIN - continue select loop.
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } while (!ReadFrame());
435731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
436513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  PostCameraTask(FROM_HERE,
437513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                 NewRunnableMethod(this, &Camera::OnCapture));
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool Camera::ReadFrame() {
441513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  v4l2_buffer buffer = {};
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  buffer.memory = V4L2_MEMORY_MMAP;
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (xioctl(device_descriptor_, VIDIOC_DQBUF, &buffer) == -1) {
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Return false only in this case to try again.
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (errno == EAGAIN)
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    log_errno("VIDIOC_DQBUF failed.");
451731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (buffer.index >= buffers_.size()) {
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(ERROR) << "Index of buffer is out of range.";
456731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailure();
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ProcessImage(buffers_[buffer.index].start);
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (xioctl(device_descriptor_, VIDIOC_QBUF, &buffer) == -1)
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    log_errno("VIDIOC_QBUF failed.");
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid Camera::ProcessImage(void* data) {
466513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(IsOnCameraThread());
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If desired resolution is higher than available, we crop the available
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // image to get the same aspect ratio and scale the result.
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int desired_width = desired_width_;
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int desired_height = desired_height_;
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (desired_width > frame_width_ || desired_height > frame_height_) {
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Compare aspect ratios of the desired and available images.
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The same as desired_width / desired_height > frame_width / frame_height.
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (desired_width_ * frame_height_ > frame_width_ * desired_height_) {
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      desired_width = frame_width_;
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      desired_height = (desired_height_ * frame_width_) / desired_width_;
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      desired_width = (desired_width_ * frame_height_) / desired_height_;
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      desired_height = frame_height_;
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkBitmap image;
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int crop_left = (frame_width_ - desired_width) / 2;
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int crop_right = frame_width_ - crop_left - desired_width;
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int crop_top = (frame_height_ - desired_height_) / 2;
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  image.setConfig(SkBitmap::kARGB_8888_Config, desired_width, desired_height);
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  image.allocPixels();
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  {
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SkAutoLockPixels lock_image(image);
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We should reflect the image from the Y axis depending on the value of
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // |mirrored_|. Hence variable increments and origin point.
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int dst_x_origin = 0;
493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int dst_x_increment = 1;
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int dst_y_increment = 0;
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (mirrored_) {
496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      dst_x_origin = image.width() - 1;
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      dst_x_increment = -1;
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      dst_y_increment = 2 * image.width();
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    uint32_t* dst = image.getAddr32(dst_x_origin, 0);
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    uint32_t* src = reinterpret_cast<uint32_t*>(data) +
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    crop_top * (frame_width_ / 2);
504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int y = 0; y < image.height(); ++y) {
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      src += crop_left / 2;
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      for (int x = 0; x < image.width(); x += 2) {
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        uint32_t yuyv = *src++;
508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        uint8_t y0 = yuyv & 0xFF;
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        uint8_t u = (yuyv >> 8) & 0xFF;
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        uint8_t y1 = (yuyv >> 16) & 0xFF;
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        uint8_t v = (yuyv >> 24) & 0xFF;
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        *dst = convert_yuv_to_rgba(y0, u, v);
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        dst += dst_x_increment;
514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        *dst = convert_yuv_to_rgba(y1, u, v);
515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        dst += dst_x_increment;
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      dst += dst_y_increment;
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      src += crop_right / 2;
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (image.width() < desired_width_ || image.height() < desired_height_) {
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    image = skia::ImageOperations::Resize(
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        image,
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        skia::ImageOperations::RESIZE_LANCZOS3,
525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        desired_width_,
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        desired_height_);
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  image.setIsOpaque(true);
529513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  {
53072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::AutoLock lock(image_lock_);
531513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    frame_image_.swap(image);
532513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  }
533731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
534731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::UI,
535731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      FROM_HERE,
536513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      NewRunnableMethod(this, &Camera::OnCaptureSuccess));
537731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
538731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
539731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::OnInitializeSuccess() {
540731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
541731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (delegate_)
542731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    delegate_->OnInitializeSuccess();
543731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
544731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
545731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::OnInitializeFailure() {
546731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
547731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (delegate_)
548731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    delegate_->OnInitializeFailure();
549731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
550731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
551731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::OnStartCapturingSuccess() {
552731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
553731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (delegate_)
554731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    delegate_->OnStartCapturingSuccess();
555731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
556731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
557731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::OnStartCapturingFailure() {
558731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
559731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (delegate_)
560731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    delegate_->OnStartCapturingFailure();
561731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
562731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
563513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid Camera::OnCaptureSuccess() {
564731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
565731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (delegate_)
566513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    delegate_->OnCaptureSuccess();
567731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
568731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
569731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid Camera::OnCaptureFailure() {
570731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (delegate_)
572731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    delegate_->OnCaptureFailure();
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
575513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool Camera::IsOnCameraThread() const {
57672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::AutoLock lock(thread_lock_);
577513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  return thread_ && MessageLoop::current() == thread_->message_loop();
578513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
579513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
580513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid Camera::PostCameraTask(const tracked_objects::Location& from_here,
581513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                            Task* task) {
58272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::AutoLock lock(thread_lock_);
583513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (!thread_)
584513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    return;
585513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(thread_->IsRunning());
586513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  thread_->message_loop()->PostTask(from_here, task);
587513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
588513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace chromeos
590