camera.cc revision 513209b27ff55e2841eac0e4120199c23acce758
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" 25513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/thread.h" 26731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/time.h" 27731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/browser/browser_thread.h" 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "gfx/size.h" 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "skia/ext/image_operations.h" 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkBitmap.h" 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkColorPriv.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; 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch frame_width_ = frame_size.width(); 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch frame_height_ = frame_size.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) { 327513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 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)) { 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(ERROR) << device_name << "is not adevice"; 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 { 530513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 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 { 576513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 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) { 582513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 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