| 1 |
// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 |
// Use of this source code is governed by a BSD-style license that can be |
| 3 |
// found in the LICENSE file. |
| 4 |
|
| 5 |
#include "media/video/capture/linux/video_capture_device_linux.h" |
| 6 |
|
| 7 |
#include <errno.h> |
| 8 |
#include <fcntl.h> |
| 9 |
#if defined(OS_OPENBSD) |
| 10 |
#include <sys/videoio.h> |
| 11 |
#else |
| 12 |
#include <linux/videodev2.h> |
| 13 |
#endif |
| 14 |
#include <sys/ioctl.h> |
| 15 |
#include <sys/mman.h> |
| 16 |
|
| 17 |
#include <list> |
| 18 |
#include <string> |
| 19 |
|
| 20 |
#include "base/bind.h" |
| 21 |
#include "base/files/file_enumerator.h" |
| 22 |
#include "base/files/scoped_file.h" |
| 23 |
#include "base/posix/eintr_wrapper.h" |
| 24 |
#include "base/strings/stringprintf.h" |
| 25 |
|
| 26 |
namespace media { |
| 27 |
|
| 28 |
// Max number of video buffers VideoCaptureDeviceLinux can allocate. |
| 29 |
enum { kMaxVideoBuffers = 2 }; |
| 30 |
// Timeout in microseconds v4l2_thread_ blocks waiting for a frame from the hw. |
| 31 |
enum { kCaptureTimeoutUs = 200000 }; |
| 32 |
// The number of continuous timeouts tolerated before treated as error. |
| 33 |
enum { kContinuousTimeoutLimit = 10 }; |
| 34 |
// Time to wait in milliseconds before v4l2_thread_ reschedules OnCaptureTask |
| 35 |
// if an event is triggered (select) but no video frame is read. |
| 36 |
enum { kCaptureSelectWaitMs = 10 }; |
| 37 |
// MJPEG is preferred if the width or height is larger than this. |
| 38 |
enum { kMjpegWidth = 640 }; |
| 39 |
enum { kMjpegHeight = 480 }; |
| 40 |
// Typical framerate, in fps |
| 41 |
enum { kTypicalFramerate = 30 }; |
| 42 |
|
| 43 |
// V4L2 color formats VideoCaptureDeviceLinux support. |
| 44 |
static const int32 kV4l2RawFmts[] = { |
| 45 |
V4L2_PIX_FMT_YUV420, |
| 46 |
V4L2_PIX_FMT_YUYV |
| 47 |
}; |
| 48 |
|
| 49 |
// USB VID and PID are both 4 bytes long. |
| 50 |
static const size_t kVidPidSize = 4; |
| 51 |
|
| 52 |
// /sys/class/video4linux/video{N}/device is a symlink to the corresponding |
| 53 |
// USB device info directory. |
| 54 |
static const char kVidPathTemplate[] = |
| 55 |
"/sys/class/video4linux/%s/device/../idVendor"; |
| 56 |
static const char kPidPathTemplate[] = |
| 57 |
"/sys/class/video4linux/%s/device/../idProduct"; |
| 58 |
|
| 59 |
bool ReadIdFile(const std::string path, std::string* id) { |
| 60 |
char id_buf[kVidPidSize]; |
| 61 |
FILE* file = fopen(path.c_str(), "rb"); |
| 62 |
if (!file) |
| 63 |
return false; |
| 64 |
const bool success = fread(id_buf, kVidPidSize, 1, file) == 1; |
| 65 |
fclose(file); |
| 66 |
if (!success) |
| 67 |
return false; |
| 68 |
id->append(id_buf, kVidPidSize); |
| 69 |
return true; |
| 70 |
} |
| 71 |
|
| 72 |
// This function translates Video4Linux pixel formats to Chromium pixel formats, |
| 73 |
// should only support those listed in GetListOfUsableFourCCs. |
| 74 |
// static |
| 75 |
VideoPixelFormat VideoCaptureDeviceLinux::V4l2ColorToVideoCaptureColorFormat( |
| 76 |
int32 v4l2_fourcc) { |
| 77 |
VideoPixelFormat result = PIXEL_FORMAT_UNKNOWN; |
| 78 |
switch (v4l2_fourcc) { |
| 79 |
case V4L2_PIX_FMT_YUV420: |
| 80 |
result = PIXEL_FORMAT_I420; |
| 81 |
break; |
| 82 |
case V4L2_PIX_FMT_YUYV: |
| 83 |
result = PIXEL_FORMAT_YUY2; |
| 84 |
break; |
| 85 |
case V4L2_PIX_FMT_MJPEG: |
| 86 |
case V4L2_PIX_FMT_JPEG: |
| 87 |
result = PIXEL_FORMAT_MJPEG; |
| 88 |
break; |
| 89 |
default: |
| 90 |
DVLOG(1) << "Unsupported pixel format " << std::hex << v4l2_fourcc; |
| 91 |
} |
| 92 |
return result; |
| 93 |
} |
| 94 |
|
| 95 |
// static |
| 96 |
void VideoCaptureDeviceLinux::GetListOfUsableFourCCs(bool favour_mjpeg, |
| 97 |
std::list<int>* fourccs) { |
| 98 |
for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i) |
| 99 |
fourccs->push_back(kV4l2RawFmts[i]); |
| 100 |
if (favour_mjpeg) |
| 101 |
fourccs->push_front(V4L2_PIX_FMT_MJPEG); |
| 102 |
else |
| 103 |
fourccs->push_back(V4L2_PIX_FMT_MJPEG); |
| 104 |
|
| 105 |
// JPEG works as MJPEG on some gspca webcams from field reports. |
| 106 |
// Put it as the least preferred format. |
| 107 |
fourccs->push_back(V4L2_PIX_FMT_JPEG); |
| 108 |
} |
| 109 |
|
| 110 |
const std::string VideoCaptureDevice::Name::GetModel() const { |
| 111 |
// |unique_id| is of the form "/dev/video2". |file_name| is "video2". |
| 112 |
const std::string dev_dir = "/dev/"; |
| 113 |
DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir)); |
| 114 |
const std::string file_name = |
| 115 |
unique_id_.substr(dev_dir.length(), unique_id_.length()); |
| 116 |
|
| 117 |
const std::string vidPath = |
| 118 |
base::StringPrintf(kVidPathTemplate, file_name.c_str()); |
| 119 |
const std::string pidPath = |
| 120 |
base::StringPrintf(kPidPathTemplate, file_name.c_str()); |
| 121 |
|
| 122 |
std::string usb_id; |
| 123 |
if (!ReadIdFile(vidPath, &usb_id)) |
| 124 |
return ""; |
| 125 |
usb_id.append(":"); |
| 126 |
if (!ReadIdFile(pidPath, &usb_id)) |
| 127 |
return ""; |
| 128 |
|
| 129 |
return usb_id; |
| 130 |
} |
| 131 |
|
| 132 |
VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(const Name& device_name) |
| 133 |
: state_(kIdle), |
| 134 |
device_name_(device_name), |
| 135 |
v4l2_thread_("V4L2Thread"), |
| 136 |
buffer_pool_(NULL), |
| 137 |
buffer_pool_size_(0), |
| 138 |
timeout_count_(0), |
| 139 |
rotation_(0) { |
| 140 |
} |
| 141 |
|
| 142 |
VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() { |
| 143 |
state_ = kIdle; |
| 144 |
// Check if the thread is running. |
| 145 |
// This means that the device have not been DeAllocated properly. |
| 146 |
DCHECK(!v4l2_thread_.IsRunning()); |
| 147 |
v4l2_thread_.Stop(); |
| 148 |
} |
| 149 |
|
| 150 |
void VideoCaptureDeviceLinux::AllocateAndStart( |
| 151 |
const VideoCaptureParams& params, |
| 152 |
scoped_ptr<VideoCaptureDevice::Client> client) { |
| 153 |
if (v4l2_thread_.IsRunning()) { |
| 154 |
return; // Wrong state. |
| 155 |
} |
| 156 |
v4l2_thread_.Start(); |
| 157 |
v4l2_thread_.message_loop()->PostTask( |
| 158 |
FROM_HERE, |
| 159 |
base::Bind(&VideoCaptureDeviceLinux::OnAllocateAndStart, |
| 160 |
base::Unretained(this), |
| 161 |
params.requested_format.frame_size.width(), |
| 162 |
params.requested_format.frame_size.height(), |
| 163 |
params.requested_format.frame_rate, |
| 164 |
base::Passed(&client))); |
| 165 |
} |
| 166 |
|
| 167 |
void VideoCaptureDeviceLinux::StopAndDeAllocate() { |
| 168 |
if (!v4l2_thread_.IsRunning()) { |
| 169 |
return; // Wrong state. |
| 170 |
} |
| 171 |
v4l2_thread_.message_loop()->PostTask( |
| 172 |
FROM_HERE, |
| 173 |
base::Bind(&VideoCaptureDeviceLinux::OnStopAndDeAllocate, |
| 174 |
base::Unretained(this))); |
| 175 |
v4l2_thread_.Stop(); |
| 176 |
// Make sure no buffers are still allocated. |
| 177 |
// This can happen (theoretically) if an error occurs when trying to stop |
| 178 |
// the camera. |
| 179 |
DeAllocateVideoBuffers(); |
| 180 |
} |
| 181 |
|
| 182 |
void VideoCaptureDeviceLinux::SetRotation(int rotation) { |
| 183 |
if (v4l2_thread_.IsRunning()) { |
| 184 |
v4l2_thread_.message_loop()->PostTask( |
| 185 |
FROM_HERE, |
| 186 |
base::Bind(&VideoCaptureDeviceLinux::SetRotationOnV4L2Thread, |
| 187 |
base::Unretained(this), rotation)); |
| 188 |
} else { |
| 189 |
// If the |v4l2_thread_| is not running, there's no race condition and |
| 190 |
// |rotation_| can be set directly. |
| 191 |
rotation_ = rotation; |
| 192 |
} |
| 193 |
} |
| 194 |
|
| 195 |
void VideoCaptureDeviceLinux::SetRotationOnV4L2Thread(int rotation) { |
| 196 |
DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); |
| 197 |
DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0); |
| 198 |
rotation_ = rotation; |
| 199 |
} |
| 200 |
|
| 201 |
void VideoCaptureDeviceLinux::OnAllocateAndStart(int width, |
| 202 |
int height, |
| 203 |
int frame_rate, |
| 204 |
scoped_ptr<Client> client) { |
| 205 |
DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); |
| 206 |
|
| 207 |
client_ = client.Pass(); |
| 208 |
|
| 209 |
// Need to open camera with O_RDWR after Linux kernel 3.3. |
| 210 |
device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR))); |
| 211 |
if (!device_fd_.is_valid()) { |
| 212 |
SetErrorState("Failed to open V4L2 device driver."); |
| 213 |
return; |
| 214 |
} |
| 215 |
|
| 216 |
// Test if this is a V4L2 capture device. |
| 217 |
v4l2_capability cap; |
| 218 |
if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) && |
| 219 |
(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && |
| 220 |
!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))) { |
| 221 |
// This is not a V4L2 video capture device. |
| 222 |
device_fd_.reset(); |
| 223 |
SetErrorState("This is not a V4L2 video capture device"); |
| 224 |
return; |
| 225 |
} |
| 226 |
|
| 227 |
// Get supported video formats in preferred order. |
| 228 |
// For large resolutions, favour mjpeg over raw formats. |
| 229 |
std::list<int> v4l2_formats; |
| 230 |
GetListOfUsableFourCCs(width > kMjpegWidth || height > kMjpegHeight, |
| 231 |
&v4l2_formats); |
| 232 |
|
| 233 |
v4l2_fmtdesc fmtdesc = {0}; |
| 234 |
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 235 |
|
| 236 |
// Enumerate image formats. |
| 237 |
std::list<int>::iterator best = v4l2_formats.end(); |
| 238 |
while (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == |
| 239 |
0) { |
| 240 |
best = std::find(v4l2_formats.begin(), best, fmtdesc.pixelformat); |
| 241 |
fmtdesc.index++; |
| 242 |
} |
| 243 |
|
| 244 |
if (best == v4l2_formats.end()) { |
| 245 |
SetErrorState("Failed to find a supported camera format."); |
| 246 |
return; |
| 247 |
} |
| 248 |
|
| 249 |
// Set format and frame size now. |
| 250 |
v4l2_format video_fmt; |
| 251 |
memset(&video_fmt, 0, sizeof(v4l2_format)); |
| 252 |
video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 253 |
video_fmt.fmt.pix.sizeimage = 0; |
| 254 |
video_fmt.fmt.pix.width = width; |
| 255 |
video_fmt.fmt.pix.height = height; |
| 256 |
video_fmt.fmt.pix.pixelformat = *best; |
| 257 |
|
| 258 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt)) < 0) { |
| 259 |
SetErrorState( |
| 260 |
base::StringPrintf("Failed to set camera format: %s", strerror(errno))); |
| 261 |
return; |
| 262 |
} |
| 263 |
|
| 264 |
// Set capture framerate in the form of capture interval. |
| 265 |
v4l2_streamparm streamparm; |
| 266 |
memset(&streamparm, 0, sizeof(v4l2_streamparm)); |
| 267 |
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 268 |
// The following line checks that the driver knows about framerate get/set. |
| 269 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { |
| 270 |
// Now check if the device is able to accept a capture framerate set. |
| 271 |
if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { |
| 272 |
// |frame_rate| is float, approximate by a fraction. |
| 273 |
streamparm.parm.capture.timeperframe.numerator = |
| 274 |
media::kFrameRatePrecision; |
| 275 |
streamparm.parm.capture.timeperframe.denominator = (frame_rate) ? |
| 276 |
(frame_rate * media::kFrameRatePrecision) : |
| 277 |
(kTypicalFramerate * media::kFrameRatePrecision); |
| 278 |
|
| 279 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < |
| 280 |
0) { |
| 281 |
SetErrorState("Failed to set camera framerate"); |
| 282 |
return; |
| 283 |
} |
| 284 |
DVLOG(2) << "Actual camera driverframerate: " |
| 285 |
<< streamparm.parm.capture.timeperframe.denominator << "/" |
| 286 |
<< streamparm.parm.capture.timeperframe.numerator; |
| 287 |
} |
| 288 |
} |
| 289 |
// TODO(mcasas): what should be done if the camera driver does not allow |
| 290 |
// framerate configuration, or the actual one is different from the desired? |
| 291 |
|
| 292 |
// Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported |
| 293 |
// operation (|errno| == EINVAL in this case) or plain failure. |
| 294 |
const int power_line_frequency = GetPowerLineFrequencyForLocation(); |
| 295 |
if ((power_line_frequency == kPowerLine50Hz) || |
| 296 |
(power_line_frequency == kPowerLine60Hz)) { |
| 297 |
struct v4l2_control control = {}; |
| 298 |
control.id = V4L2_CID_POWER_LINE_FREQUENCY; |
| 299 |
control.value = (power_line_frequency == kPowerLine50Hz) ? |
| 300 |
V4L2_CID_POWER_LINE_FREQUENCY_50HZ : |
| 301 |
V4L2_CID_POWER_LINE_FREQUENCY_60HZ; |
| 302 |
HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control)); |
| 303 |
} |
| 304 |
|
| 305 |
// Store our current width and height. |
| 306 |
capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width, |
| 307 |
video_fmt.fmt.pix.height); |
| 308 |
capture_format_.frame_rate = frame_rate; |
| 309 |
capture_format_.pixel_format = |
| 310 |
V4l2ColorToVideoCaptureColorFormat(video_fmt.fmt.pix.pixelformat); |
| 311 |
|
| 312 |
// Start capturing. |
| 313 |
if (!AllocateVideoBuffers()) { |
| 314 |
// Error, We can not recover. |
| 315 |
SetErrorState("Allocate buffer failed"); |
| 316 |
return; |
| 317 |
} |
| 318 |
|
| 319 |
// Start UVC camera. |
| 320 |
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 321 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) == -1) { |
| 322 |
SetErrorState("VIDIOC_STREAMON failed"); |
| 323 |
return; |
| 324 |
} |
| 325 |
|
| 326 |
state_ = kCapturing; |
| 327 |
// Post task to start fetching frames from v4l2. |
| 328 |
v4l2_thread_.message_loop()->PostTask( |
| 329 |
FROM_HERE, |
| 330 |
base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask, |
| 331 |
base::Unretained(this))); |
| 332 |
} |
| 333 |
|
| 334 |
void VideoCaptureDeviceLinux::OnStopAndDeAllocate() { |
| 335 |
DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); |
| 336 |
|
| 337 |
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 338 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &type)) < 0) { |
| 339 |
SetErrorState("VIDIOC_STREAMOFF failed"); |
| 340 |
return; |
| 341 |
} |
| 342 |
// We don't dare to deallocate the buffers if we can't stop |
| 343 |
// the capture device. |
| 344 |
DeAllocateVideoBuffers(); |
| 345 |
|
| 346 |
// We need to close and open the device if we want to change the settings |
| 347 |
// Otherwise VIDIOC_S_FMT will return error |
| 348 |
// Sad but true. |
| 349 |
device_fd_.reset(); |
| 350 |
state_ = kIdle; |
| 351 |
client_.reset(); |
| 352 |
} |
| 353 |
|
| 354 |
void VideoCaptureDeviceLinux::OnCaptureTask() { |
| 355 |
DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current()); |
| 356 |
|
| 357 |
if (state_ != kCapturing) { |
| 358 |
return; |
| 359 |
} |
| 360 |
|
| 361 |
fd_set r_set; |
| 362 |
FD_ZERO(&r_set); |
| 363 |
FD_SET(device_fd_.get(), &r_set); |
| 364 |
timeval timeout; |
| 365 |
|
| 366 |
timeout.tv_sec = 0; |
| 367 |
timeout.tv_usec = kCaptureTimeoutUs; |
| 368 |
|
| 369 |
// First argument to select is the highest numbered file descriptor +1. |
| 370 |
// Refer to http://linux.die.net/man/2/select for more information. |
| 371 |
int result = |
| 372 |
HANDLE_EINTR(select(device_fd_.get() + 1, &r_set, NULL, NULL, &timeout)); |
| 373 |
// Check if select have failed. |
| 374 |
if (result < 0) { |
| 375 |
// EINTR is a signal. This is not really an error. |
| 376 |
if (errno != EINTR) { |
| 377 |
SetErrorState("Select failed"); |
| 378 |
return; |
| 379 |
} |
| 380 |
v4l2_thread_.message_loop()->PostDelayedTask( |
| 381 |
FROM_HERE, |
| 382 |
base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask, |
| 383 |
base::Unretained(this)), |
| 384 |
base::TimeDelta::FromMilliseconds(kCaptureSelectWaitMs)); |
| 385 |
} |
| 386 |
|
| 387 |
// Check if select timeout. |
| 388 |
if (result == 0) { |
| 389 |
timeout_count_++; |
| 390 |
if (timeout_count_ >= kContinuousTimeoutLimit) { |
| 391 |
SetErrorState(base::StringPrintf( |
| 392 |
"Continuous timeout %d times", timeout_count_)); |
| 393 |
timeout_count_ = 0; |
| 394 |
return; |
| 395 |
} |
| 396 |
} else { |
| 397 |
timeout_count_ = 0; |
| 398 |
} |
| 399 |
|
| 400 |
// Check if the driver have filled a buffer. |
| 401 |
if (FD_ISSET(device_fd_.get(), &r_set)) { |
| 402 |
v4l2_buffer buffer; |
| 403 |
memset(&buffer, 0, sizeof(buffer)); |
| 404 |
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 405 |
buffer.memory = V4L2_MEMORY_MMAP; |
| 406 |
// Dequeue a buffer. |
| 407 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) == 0) { |
| 408 |
client_->OnIncomingCapturedData( |
| 409 |
static_cast<uint8*>(buffer_pool_[buffer.index].start), |
| 410 |
buffer.bytesused, |
| 411 |
capture_format_, |
| 412 |
rotation_, |
| 413 |
base::TimeTicks::Now()); |
| 414 |
|
| 415 |
// Enqueue the buffer again. |
| 416 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) == -1) { |
| 417 |
SetErrorState(base::StringPrintf( |
| 418 |
"Failed to enqueue capture buffer errno %d", errno)); |
| 419 |
} |
| 420 |
} else { |
| 421 |
SetErrorState(base::StringPrintf( |
| 422 |
"Failed to dequeue capture buffer errno %d", errno)); |
| 423 |
return; |
| 424 |
} |
| 425 |
} |
| 426 |
|
| 427 |
v4l2_thread_.message_loop()->PostTask( |
| 428 |
FROM_HERE, |
| 429 |
base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask, |
| 430 |
base::Unretained(this))); |
| 431 |
} |
| 432 |
|
| 433 |
bool VideoCaptureDeviceLinux::AllocateVideoBuffers() { |
| 434 |
v4l2_requestbuffers r_buffer; |
| 435 |
memset(&r_buffer, 0, sizeof(r_buffer)); |
| 436 |
|
| 437 |
r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 438 |
r_buffer.memory = V4L2_MEMORY_MMAP; |
| 439 |
r_buffer.count = kMaxVideoBuffers; |
| 440 |
|
| 441 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
| 442 |
return false; |
| 443 |
} |
| 444 |
|
| 445 |
if (r_buffer.count > kMaxVideoBuffers) { |
| 446 |
r_buffer.count = kMaxVideoBuffers; |
| 447 |
} |
| 448 |
|
| 449 |
buffer_pool_size_ = r_buffer.count; |
| 450 |
|
| 451 |
// Map the buffers. |
| 452 |
buffer_pool_ = new Buffer[r_buffer.count]; |
| 453 |
for (unsigned int i = 0; i < r_buffer.count; i++) { |
| 454 |
v4l2_buffer buffer; |
| 455 |
memset(&buffer, 0, sizeof(buffer)); |
| 456 |
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 457 |
buffer.memory = V4L2_MEMORY_MMAP; |
| 458 |
buffer.index = i; |
| 459 |
|
| 460 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) { |
| 461 |
return false; |
| 462 |
} |
| 463 |
|
| 464 |
// Some devices require mmap() to be called with both READ and WRITE. |
| 465 |
// See crbug.com/178582. |
| 466 |
buffer_pool_[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, |
| 467 |
MAP_SHARED, device_fd_.get(), buffer.m.offset); |
| 468 |
if (buffer_pool_[i].start == MAP_FAILED) { |
| 469 |
return false; |
| 470 |
} |
| 471 |
buffer_pool_[i].length = buffer.length; |
| 472 |
// Enqueue the buffer in the drivers incoming queue. |
| 473 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| 474 |
return false; |
| 475 |
} |
| 476 |
} |
| 477 |
return true; |
| 478 |
} |
| 479 |
|
| 480 |
void VideoCaptureDeviceLinux::DeAllocateVideoBuffers() { |
| 481 |
if (!buffer_pool_) |
| 482 |
return; |
| 483 |
|
| 484 |
// Unmaps buffers. |
| 485 |
for (int i = 0; i < buffer_pool_size_; i++) { |
| 486 |
munmap(buffer_pool_[i].start, buffer_pool_[i].length); |
| 487 |
} |
| 488 |
v4l2_requestbuffers r_buffer; |
| 489 |
memset(&r_buffer, 0, sizeof(r_buffer)); |
| 490 |
r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 491 |
r_buffer.memory = V4L2_MEMORY_MMAP; |
| 492 |
r_buffer.count = 0; |
| 493 |
|
| 494 |
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
| 495 |
SetErrorState("Failed to reset buf."); |
| 496 |
} |
| 497 |
|
| 498 |
delete [] buffer_pool_; |
| 499 |
buffer_pool_ = NULL; |
| 500 |
buffer_pool_size_ = 0; |
| 501 |
} |
| 502 |
|
| 503 |
void VideoCaptureDeviceLinux::SetErrorState(const std::string& reason) { |
| 504 |
DCHECK(!v4l2_thread_.IsRunning() || |
| 505 |
v4l2_thread_.message_loop() == base::MessageLoop::current()); |
| 506 |
DVLOG(1) << reason; |
| 507 |
state_ = kError; |
| 508 |
client_->OnError(reason); |
| 509 |
} |
| 510 |
|
| 511 |
} // namespace media |