| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/capture/video/linux/v4l2_capture_delegate.h" | 5 #include "media/capture/video/linux/v4l2_capture_delegate.h" |
| 6 | 6 |
| 7 #include <poll.h> | 7 #include <poll.h> |
| 8 #include <sys/fcntl.h> | 8 #include <sys/fcntl.h> |
| 9 #include <sys/ioctl.h> | 9 #include <sys/ioctl.h> |
| 10 #include <sys/mman.h> | 10 #include <sys/mman.h> |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 int height, | 173 int height, |
| 174 float frame_rate, | 174 float frame_rate, |
| 175 scoped_ptr<VideoCaptureDevice::Client> client) { | 175 scoped_ptr<VideoCaptureDevice::Client> client) { |
| 176 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 176 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 177 DCHECK(client); | 177 DCHECK(client); |
| 178 client_ = client.Pass(); | 178 client_ = client.Pass(); |
| 179 | 179 |
| 180 // Need to open camera with O_RDWR after Linux kernel 3.3. | 180 // Need to open camera with O_RDWR after Linux kernel 3.3. |
| 181 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR))); | 181 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR))); |
| 182 if (!device_fd_.is_valid()) { | 182 if (!device_fd_.is_valid()) { |
| 183 SetErrorState("Failed to open V4L2 device driver file."); | 183 SetErrorState(FROM_HERE, "Failed to open V4L2 device driver file."); |
| 184 return; | 184 return; |
| 185 } | 185 } |
| 186 | 186 |
| 187 v4l2_capability cap = {}; | 187 v4l2_capability cap = {}; |
| 188 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) && | 188 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) && |
| 189 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE || | 189 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE || |
| 190 cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) && | 190 cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) && |
| 191 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && | 191 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && |
| 192 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) { | 192 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) { |
| 193 device_fd_.reset(); | 193 device_fd_.reset(); |
| 194 SetErrorState("This is not a V4L2 video capture device"); | 194 SetErrorState(FROM_HERE, "This is not a V4L2 video capture device"); |
| 195 return; | 195 return; |
| 196 } | 196 } |
| 197 | 197 |
| 198 // Get supported video formats in preferred order. | 198 // Get supported video formats in preferred order. |
| 199 // For large resolutions, favour mjpeg over raw formats. | 199 // For large resolutions, favour mjpeg over raw formats. |
| 200 const std::list<uint32_t>& desired_v4l2_formats = | 200 const std::list<uint32_t>& desired_v4l2_formats = |
| 201 GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight); | 201 GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight); |
| 202 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end(); | 202 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end(); |
| 203 | 203 |
| 204 v4l2_fmtdesc fmtdesc = {}; | 204 v4l2_fmtdesc fmtdesc = {}; |
| 205 fmtdesc.type = capture_type_; | 205 fmtdesc.type = capture_type_; |
| 206 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0; | 206 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0; |
| 207 ++fmtdesc.index) { | 207 ++fmtdesc.index) { |
| 208 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat); | 208 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat); |
| 209 } | 209 } |
| 210 if (best == desired_v4l2_formats.end()) { | 210 if (best == desired_v4l2_formats.end()) { |
| 211 SetErrorState("Failed to find a supported camera format."); | 211 SetErrorState(FROM_HERE, "Failed to find a supported camera format."); |
| 212 return; | 212 return; |
| 213 } | 213 } |
| 214 | 214 |
| 215 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best); | 215 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best); |
| 216 | 216 |
| 217 video_fmt_.type = capture_type_; | 217 video_fmt_.type = capture_type_; |
| 218 if (!FillV4L2Format(&video_fmt_, width, height, *best)) { | 218 if (!FillV4L2Format(&video_fmt_, width, height, *best)) { |
| 219 SetErrorState("Failed filling in V4L2 Format"); | 219 SetErrorState(FROM_HERE, "Failed filling in V4L2 Format"); |
| 220 return; | 220 return; |
| 221 } | 221 } |
| 222 | 222 |
| 223 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) { | 223 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) { |
| 224 SetErrorState("Failed to set video capture format"); | 224 SetErrorState(FROM_HERE, "Failed to set video capture format"); |
| 225 return; | 225 return; |
| 226 } | 226 } |
| 227 const VideoPixelFormat pixel_format = | 227 const VideoPixelFormat pixel_format = |
| 228 V4l2FourCcToChromiumPixelFormat(video_fmt_.fmt.pix.pixelformat); | 228 V4l2FourCcToChromiumPixelFormat(video_fmt_.fmt.pix.pixelformat); |
| 229 if (pixel_format == PIXEL_FORMAT_UNKNOWN) { | 229 if (pixel_format == PIXEL_FORMAT_UNKNOWN) { |
| 230 SetErrorState("Unsupported pixel format"); | 230 SetErrorState(FROM_HERE, "Unsupported pixel format"); |
| 231 return; | 231 return; |
| 232 } | 232 } |
| 233 | 233 |
| 234 // Set capture framerate in the form of capture interval. | 234 // Set capture framerate in the form of capture interval. |
| 235 v4l2_streamparm streamparm = {}; | 235 v4l2_streamparm streamparm = {}; |
| 236 streamparm.type = capture_type_; | 236 streamparm.type = capture_type_; |
| 237 // The following line checks that the driver knows about framerate get/set. | 237 // The following line checks that the driver knows about framerate get/set. |
| 238 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { | 238 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { |
| 239 // Now check if the device is able to accept a capture framerate set. | 239 // Now check if the device is able to accept a capture framerate set. |
| 240 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { | 240 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { |
| 241 // |frame_rate| is float, approximate by a fraction. | 241 // |frame_rate| is float, approximate by a fraction. |
| 242 streamparm.parm.capture.timeperframe.numerator = | 242 streamparm.parm.capture.timeperframe.numerator = |
| 243 media::kFrameRatePrecision; | 243 media::kFrameRatePrecision; |
| 244 streamparm.parm.capture.timeperframe.denominator = | 244 streamparm.parm.capture.timeperframe.denominator = |
| 245 (frame_rate) ? (frame_rate * media::kFrameRatePrecision) | 245 (frame_rate) ? (frame_rate * media::kFrameRatePrecision) |
| 246 : (kTypicalFramerate * media::kFrameRatePrecision); | 246 : (kTypicalFramerate * media::kFrameRatePrecision); |
| 247 | 247 |
| 248 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < | 248 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < |
| 249 0) { | 249 0) { |
| 250 SetErrorState("Failed to set camera framerate"); | 250 SetErrorState(FROM_HERE, "Failed to set camera framerate"); |
| 251 return; | 251 return; |
| 252 } | 252 } |
| 253 DVLOG(2) << "Actual camera driverframerate: " | 253 DVLOG(2) << "Actual camera driverframerate: " |
| 254 << streamparm.parm.capture.timeperframe.denominator << "/" | 254 << streamparm.parm.capture.timeperframe.denominator << "/" |
| 255 << streamparm.parm.capture.timeperframe.numerator; | 255 << streamparm.parm.capture.timeperframe.numerator; |
| 256 } | 256 } |
| 257 } | 257 } |
| 258 // TODO(mcasas): what should be done if the camera driver does not allow | 258 // TODO(mcasas): what should be done if the camera driver does not allow |
| 259 // framerate configuration, or the actual one is different from the desired? | 259 // framerate configuration, or the actual one is different from the desired? |
| 260 | 260 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 275 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, | 275 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, |
| 276 video_fmt_.fmt.pix.height); | 276 video_fmt_.fmt.pix.height); |
| 277 capture_format_.frame_rate = frame_rate; | 277 capture_format_.frame_rate = frame_rate; |
| 278 capture_format_.pixel_format = pixel_format; | 278 capture_format_.pixel_format = pixel_format; |
| 279 | 279 |
| 280 v4l2_requestbuffers r_buffer = {}; | 280 v4l2_requestbuffers r_buffer = {}; |
| 281 r_buffer.type = capture_type_; | 281 r_buffer.type = capture_type_; |
| 282 r_buffer.memory = V4L2_MEMORY_MMAP; | 282 r_buffer.memory = V4L2_MEMORY_MMAP; |
| 283 r_buffer.count = kNumVideoBuffers; | 283 r_buffer.count = kNumVideoBuffers; |
| 284 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { | 284 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
| 285 SetErrorState("Error requesting MMAP buffers from V4L2"); | 285 SetErrorState(FROM_HERE, "Error requesting MMAP buffers from V4L2"); |
| 286 return; | 286 return; |
| 287 } | 287 } |
| 288 for (unsigned int i = 0; i < r_buffer.count; ++i) { | 288 for (unsigned int i = 0; i < r_buffer.count; ++i) { |
| 289 if (!MapAndQueueBuffer(i)) { | 289 if (!MapAndQueueBuffer(i)) { |
| 290 SetErrorState("Allocate buffer failed"); | 290 SetErrorState(FROM_HERE, "Allocate buffer failed"); |
| 291 return; | 291 return; |
| 292 } | 292 } |
| 293 } | 293 } |
| 294 | 294 |
| 295 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) < | 295 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) < |
| 296 0) { | 296 0) { |
| 297 SetErrorState("VIDIOC_STREAMON failed"); | 297 SetErrorState(FROM_HERE, "VIDIOC_STREAMON failed"); |
| 298 return; | 298 return; |
| 299 } | 299 } |
| 300 | 300 |
| 301 is_capturing_ = true; | 301 is_capturing_ = true; |
| 302 // Post task to start fetching frames from v4l2. | 302 // Post task to start fetching frames from v4l2. |
| 303 v4l2_task_runner_->PostTask( | 303 v4l2_task_runner_->PostTask( |
| 304 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); | 304 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
| 305 } | 305 } |
| 306 | 306 |
| 307 void V4L2CaptureDelegate::StopAndDeAllocate() { | 307 void V4L2CaptureDelegate::StopAndDeAllocate() { |
| 308 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 308 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 309 // The order is important: stop streaming, clear |buffer_pool_|, | 309 // The order is important: stop streaming, clear |buffer_pool_|, |
| 310 // thus munmap()ing the v4l2_buffers, and then return them to the OS. | 310 // thus munmap()ing the v4l2_buffers, and then return them to the OS. |
| 311 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) < | 311 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) < |
| 312 0) { | 312 0) { |
| 313 SetErrorState("VIDIOC_STREAMOFF failed"); | 313 SetErrorState(FROM_HERE, "VIDIOC_STREAMOFF failed"); |
| 314 return; | 314 return; |
| 315 } | 315 } |
| 316 | 316 |
| 317 buffer_tracker_pool_.clear(); | 317 buffer_tracker_pool_.clear(); |
| 318 | 318 |
| 319 v4l2_requestbuffers r_buffer = {}; | 319 v4l2_requestbuffers r_buffer = {}; |
| 320 r_buffer.type = capture_type_; | 320 r_buffer.type = capture_type_; |
| 321 r_buffer.memory = V4L2_MEMORY_MMAP; | 321 r_buffer.memory = V4L2_MEMORY_MMAP; |
| 322 r_buffer.count = 0; | 322 r_buffer.count = 0; |
| 323 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 323 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) |
| 324 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0"); | 324 SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); |
| 325 | 325 |
| 326 // At this point we can close the device. | 326 // At this point we can close the device. |
| 327 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 327 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. |
| 328 device_fd_.reset(); | 328 device_fd_.reset(); |
| 329 is_capturing_ = false; | 329 is_capturing_ = false; |
| 330 client_.reset(); | 330 client_.reset(); |
| 331 } | 331 } |
| 332 | 332 |
| 333 void V4L2CaptureDelegate::SetRotation(int rotation) { | 333 void V4L2CaptureDelegate::SetRotation(int rotation) { |
| 334 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 334 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 370 void V4L2CaptureDelegate::DoCapture() { | 370 void V4L2CaptureDelegate::DoCapture() { |
| 371 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 371 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 372 if (!is_capturing_) | 372 if (!is_capturing_) |
| 373 return; | 373 return; |
| 374 | 374 |
| 375 pollfd device_pfd = {}; | 375 pollfd device_pfd = {}; |
| 376 device_pfd.fd = device_fd_.get(); | 376 device_pfd.fd = device_fd_.get(); |
| 377 device_pfd.events = POLLIN; | 377 device_pfd.events = POLLIN; |
| 378 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs)); | 378 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs)); |
| 379 if (result < 0) { | 379 if (result < 0) { |
| 380 SetErrorState("Poll failed"); | 380 SetErrorState(FROM_HERE, "Poll failed"); |
| 381 return; | 381 return; |
| 382 } | 382 } |
| 383 // Check if poll() timed out; track the amount of times it did in a row and | 383 // Check if poll() timed out; track the amount of times it did in a row and |
| 384 // throw an error if it times out too many times. | 384 // throw an error if it times out too many times. |
| 385 if (result == 0) { | 385 if (result == 0) { |
| 386 timeout_count_++; | 386 timeout_count_++; |
| 387 if (timeout_count_ >= kContinuousTimeoutLimit) { | 387 if (timeout_count_ >= kContinuousTimeoutLimit) { |
| 388 SetErrorState("Multiple continuous timeouts while read-polling."); | 388 SetErrorState(FROM_HERE, |
| 389 "Multiple continuous timeouts while read-polling."); |
| 389 timeout_count_ = 0; | 390 timeout_count_ = 0; |
| 390 return; | 391 return; |
| 391 } | 392 } |
| 392 } else { | 393 } else { |
| 393 timeout_count_ = 0; | 394 timeout_count_ = 0; |
| 394 } | 395 } |
| 395 | 396 |
| 396 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. | 397 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. |
| 397 if (device_pfd.revents & POLLIN) { | 398 if (device_pfd.revents & POLLIN) { |
| 398 v4l2_buffer buffer; | 399 v4l2_buffer buffer; |
| 399 FillV4L2Buffer(&buffer, 0); | 400 FillV4L2Buffer(&buffer, 0); |
| 400 | 401 |
| 401 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { | 402 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { |
| 402 SetErrorState("Failed to dequeue capture buffer"); | 403 SetErrorState(FROM_HERE, "Failed to dequeue capture buffer"); |
| 403 return; | 404 return; |
| 404 } | 405 } |
| 405 | 406 |
| 406 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); | 407 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); |
| 407 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); | 408 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); |
| 408 | 409 |
| 409 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { | 410 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| 410 SetErrorState("Failed to enqueue capture buffer"); | 411 SetErrorState(FROM_HERE, "Failed to enqueue capture buffer"); |
| 411 return; | 412 return; |
| 412 } | 413 } |
| 413 } | 414 } |
| 414 | 415 |
| 415 v4l2_task_runner_->PostTask( | 416 v4l2_task_runner_->PostTask( |
| 416 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); | 417 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
| 417 } | 418 } |
| 418 | 419 |
| 419 void V4L2CaptureDelegate::SetErrorState(const std::string& reason) { | 420 void V4L2CaptureDelegate::SetErrorState( |
| 421 const tracked_objects::Location& from_here, |
| 422 const std::string& reason) { |
| 420 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 423 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| 421 is_capturing_ = false; | 424 is_capturing_ = false; |
| 422 client_->OnError(reason); | 425 client_->OnError(from_here, reason); |
| 423 } | 426 } |
| 424 | 427 |
| 425 } // namespace media | 428 } // namespace media |
| OLD | NEW |