diff options
author | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-08 16:52:25 +0000 |
---|---|---|
committer | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-08 16:52:25 +0000 |
commit | 25bd71ae5f7770990ac30c5ad5ca8745a952e1c0 (patch) | |
tree | 2be2fd9c29d958ea434ba28da9cd92484a44734c | |
parent | 03eb799a4dac9b1d11b776fca068585db29cb20e (diff) | |
download | chromium_src-25bd71ae5f7770990ac30c5ad5ca8745a952e1c0.zip chromium_src-25bd71ae5f7770990ac30c5ad5ca8745a952e1c0.tar.gz chromium_src-25bd71ae5f7770990ac30c5ad5ca8745a952e1c0.tar.bz2 |
add workarounds for some popular webcam.
1. bring device back to normal from bad state by querying all controls.
2. call ioctl with VIDIOC_TRY_FMT twice.
3. incorporate change from http://codereview.chromium.org/7753002/. "Improve stability when using a Logitech 9000 camera on Linux.
Changed so that the device driver is opened and closed on the same thread as all the v4l2 calls."
BUG=94134
TEST=
Review URL: http://codereview.chromium.org/7743002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@100165 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/media.gyp | 6 | ||||
-rw-r--r-- | media/video/capture/linux/video_capture_device_linux.cc | 85 | ||||
-rw-r--r-- | media/video/capture/linux/video_capture_device_linux.h | 4 | ||||
-rw-r--r-- | media/video/capture/video_capture_device_unittest.cc | 24 |
4 files changed, 68 insertions, 51 deletions
diff --git a/media/media.gyp b/media/media.gyp index 24dcc58..fcc9b53 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -202,8 +202,11 @@ 'video/capture/linux/video_capture_device_linux.h', 'video/capture/video_capture.h', 'video/capture/video_capture_device.h', + 'video/capture/video_capture_device_dummy.cc', + 'video/capture/video_capture_device_dummy.h', 'video/capture/video_capture_proxy.cc', 'video/capture/video_capture_proxy.h', + 'video/capture/video_capture_types.h', 'video/capture/win/filter_base_win.cc', 'video/capture/win/filter_base_win.h', 'video/capture/win/pin_base_win.cc', @@ -215,9 +218,6 @@ 'video/capture/win/sink_input_pin_win.h', 'video/capture/win/video_capture_device_win.cc', 'video/capture/win/video_capture_device_win.h', - 'video/capture/video_capture_device_dummy.cc', - 'video/capture/video_capture_device_dummy.h', - 'video/capture/video_capture_types.h', 'video/ffmpeg_video_decode_engine.cc', 'video/ffmpeg_video_decode_engine.h', 'video/picture.cc', diff --git a/media/video/capture/linux/video_capture_device_linux.cc b/media/video/capture/linux/video_capture_device_linux.cc index 8585a26..3062e6e 100644 --- a/media/video/capture/linux/video_capture_device_linux.cc +++ b/media/video/capture/linux/video_capture_device_linux.cc @@ -15,10 +15,22 @@ #include "base/file_util.h" #include "base/stringprintf.h" +// Workaround for some device. This query of all controls magically brings +// device back to normal from bad state. +// See http://crbug.com/94134. +static void ResetCameraByEnumeratingIoctlsHACK(int fd) { + struct v4l2_queryctrl query_ctrl; + memset(&query_ctrl, 0, sizeof(query_ctrl)); + + for (query_ctrl.id = V4L2_CID_BASE; + query_ctrl.id < V4L2_CID_LASTP1; + query_ctrl.id++) { + ioctl(fd, VIDIOC_QUERYCTRL, &query_ctrl); + } +} + namespace media { -// Number of supported v4l2 color formats. -enum { kColorFormats = 2 }; // Max number of video buffers VideoCaptureDeviceLinux can allocate. enum { kMaxVideoBuffers = 2 }; // Timeout in microseconds v4l2_thread_ blocks waiting for a frame from the hw. @@ -28,7 +40,7 @@ enum { kCaptureTimeoutUs = 200000 }; enum { kCaptureSelectWaitMs = 10 }; // V4L2 color formats VideoCaptureDeviceLinux support. -static const int32 kV4l2Fmts[kColorFormats] = { +static const int32 kV4l2Fmts[] = { V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YUYV }; @@ -84,10 +96,17 @@ VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) { VideoCaptureDeviceLinux* self = new VideoCaptureDeviceLinux(device_name); if (!self) return NULL; - if (self->Init() != true) { + // Test opening the device driver. This is to make sure it is available. + // We will reopen it again in our worker thread when someone + // allocates the camera. + int fd = open(device_name.unique_id.c_str(), O_RDONLY); + if (fd < 0) { + DPLOG(ERROR) << "Cannot open device"; delete self; return NULL; } + close(fd); + return self; } @@ -113,13 +132,6 @@ VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() { } } -bool VideoCaptureDeviceLinux::Init() { - if ((device_fd_ = open(device_name_.unique_id.c_str(), O_RDONLY)) < 0) { - return false; - } - return true; -} - void VideoCaptureDeviceLinux::Allocate(int width, int height, int frame_rate, @@ -161,21 +173,10 @@ void VideoCaptureDeviceLinux::DeAllocate() { NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnDeAllocate)); v4l2_thread_.Stop(); - // We need to close and open the device if we want to change the settings - // Otherwise VIDIOC_S_FMT will return error - // Sad but true. We still open the device in the Init function. - close(device_fd_); - device_fd_ = -1; - // Make sure no buffers are still allocated. // This can happen (theoretically) if an error occurs when trying to stop // the camera. DeAllocateVideoBuffers(); - - if (!Init() && state_ != kError) { - SetErrorState("Failed to reinitialize camera"); - return; - } } const VideoCaptureDevice::Name& VideoCaptureDeviceLinux::device_name() { @@ -190,6 +191,11 @@ void VideoCaptureDeviceLinux::OnAllocate(int width, observer_ = observer; + if ((device_fd_ = open(device_name_.unique_id.c_str(), O_RDONLY)) < 0) { + SetErrorState("Failed to open V4L2 device driver."); + return; + } + // Test if this is a V4L2 device. v4l2_capability cap; if (!((ioctl(device_fd_, VIDIOC_QUERYCAP, &cap) == 0) && @@ -208,13 +214,21 @@ void VideoCaptureDeviceLinux::OnAllocate(int width, video_fmt.fmt.pix.width = width; video_fmt.fmt.pix.height = height; + // Some device failed in first VIDIOC_TRY_FMT with EBUSY or EIO. + // But second VIDIOC_TRY_FMT succeeds. + // See http://crbug.com/94134. bool format_match = false; - for (int i = 0; i < kColorFormats; i++) { + for (unsigned int i = 0; i < arraysize(kV4l2Fmts) && !format_match; i++) { video_fmt.fmt.pix.pixelformat = kV4l2Fmts[i]; - if (ioctl(device_fd_, VIDIOC_TRY_FMT, &video_fmt) < 0) { - continue; + for (int attempt = 0; attempt < 2 && !format_match; attempt++) { + ResetCameraByEnumeratingIoctlsHACK(device_fd_); + if (ioctl(device_fd_, VIDIOC_TRY_FMT, &video_fmt) < 0) { + if (errno != EIO) + break; + } else { + format_match = true; + } } - format_match = true; } if (!format_match) { @@ -251,6 +265,12 @@ void VideoCaptureDeviceLinux::OnDeAllocate() { if (state_ == kAllocated) { state_ = kIdle; } + + // We need to close and open the device if we want to change the settings + // Otherwise VIDIOC_S_FMT will return error + // Sad but true. + close(device_fd_); + device_fd_ = -1; } void VideoCaptureDeviceLinux::OnStart() { @@ -397,10 +417,23 @@ bool VideoCaptureDeviceLinux::AllocateVideoBuffers() { } void VideoCaptureDeviceLinux::DeAllocateVideoBuffers() { + if (!buffer_pool_) + return; + // Unmaps buffers. for (int i = 0; i < buffer_pool_size_; i++) { munmap(buffer_pool_[i].start, buffer_pool_[i].length); } + v4l2_requestbuffers r_buffer; + memset(&r_buffer, 0, sizeof(r_buffer)); + r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + r_buffer.memory = V4L2_MEMORY_MMAP; + r_buffer.count = 0; + + if (ioctl(device_fd_, VIDIOC_REQBUFS, &r_buffer) < 0) { + SetErrorState("Failed to reset buf."); + } + delete [] buffer_pool_; buffer_pool_ = NULL; buffer_pool_size_ = 0; diff --git a/media/video/capture/linux/video_capture_device_linux.h b/media/video/capture/linux/video_capture_device_linux.h index adcb592..24caf17 100644 --- a/media/video/capture/linux/video_capture_device_linux.h +++ b/media/video/capture/linux/video_capture_device_linux.h @@ -22,10 +22,6 @@ class VideoCaptureDeviceLinux : public VideoCaptureDevice { explicit VideoCaptureDeviceLinux(const Name& device_name); virtual ~VideoCaptureDeviceLinux(); - // Opens the device driver for this device. - // This function is used by the static VideoCaptureDevice::Create function. - bool Init(); - // VideoCaptureDevice implementation. virtual void Allocate(int width, int height, diff --git a/media/video/capture/video_capture_device_unittest.cc b/media/video/capture/video_capture_device_unittest.cc index 8ba7a40..7cb40bc 100644 --- a/media/video/capture/video_capture_device_unittest.cc +++ b/media/video/capture/video_capture_device_unittest.cc @@ -69,9 +69,7 @@ TEST_F(VideoCaptureDeviceTest, OpenInvalidDevice) { EXPECT_TRUE(device == NULL); } -// TODO(perkj): This test is disabled due to stability problem with certain -// cameras. http://www.crbug.com/94134 -TEST_F(VideoCaptureDeviceTest, DISABLED_CaptureVGA) { +TEST_F(VideoCaptureDeviceTest, CaptureVGA) { VideoCaptureDevice::GetDeviceNames(&names_); if (!names_.size()) { LOG(WARNING) << "No camera available. Exiting test."; @@ -98,9 +96,7 @@ TEST_F(VideoCaptureDeviceTest, DISABLED_CaptureVGA) { device->DeAllocate(); } -// TODO(perkj): This test is disabled due to stability problem with certain -// cameras. http://www.crbug.com/94134 -TEST_F(VideoCaptureDeviceTest, DISABLED_Capture720p) { +TEST_F(VideoCaptureDeviceTest, Capture720p) { VideoCaptureDevice::GetDeviceNames(&names_); if (!names_.size()) { LOG(WARNING) << "No camera available. Exiting test."; @@ -129,9 +125,7 @@ TEST_F(VideoCaptureDeviceTest, DISABLED_Capture720p) { device->DeAllocate(); } -// TODO(perkj): This test is disabled due to stability problem with certain -// cameras. http://www.crbug.com/94134 -TEST_F(VideoCaptureDeviceTest, DISABLED_AllocateSameCameraTwice) { +TEST_F(VideoCaptureDeviceTest, AllocateSameCameraTwice) { VideoCaptureDevice::GetDeviceNames(&names_); if (!names_.size()) { LOG(WARNING) << "No camera available. Exiting test."; @@ -158,9 +152,7 @@ TEST_F(VideoCaptureDeviceTest, DISABLED_AllocateSameCameraTwice) { device2->DeAllocate(); } -// TODO(perkj): This test is disabled due to stability problem with certain -// cameras. http://www.crbug.com/94134 -TEST_F(VideoCaptureDeviceTest, DISABLED_AllocateBadSize) { +TEST_F(VideoCaptureDeviceTest, AllocateBadSize) { VideoCaptureDevice::GetDeviceNames(&names_); if (!names_.size()) { LOG(WARNING) << "No camera available. Exiting test."; @@ -181,9 +173,7 @@ TEST_F(VideoCaptureDeviceTest, DISABLED_AllocateBadSize) { device->DeAllocate(); } -// TODO(perkj): This test is disabled due to stability problem with certain -// cameras. http://www.crbug.com/94134 -TEST_F(VideoCaptureDeviceTest, DISABLED_ReAllocateCamera) { +TEST_F(VideoCaptureDeviceTest, ReAllocateCamera) { VideoCaptureDevice::GetDeviceNames(&names_); if (!names_.size()) { LOG(WARNING) << "No camera available. Exiting test."; @@ -215,9 +205,7 @@ TEST_F(VideoCaptureDeviceTest, DISABLED_ReAllocateCamera) { device->DeAllocate(); } -// TODO(perkj): This test is disabled due to stability problem with certain -// cameras. http://www.crbug.com/94134 -TEST_F(VideoCaptureDeviceTest, DISABLED_DeAllocateCameraWhileRunning) { +TEST_F(VideoCaptureDeviceTest, DeAllocateCameraWhileRunning) { VideoCaptureDevice::GetDeviceNames(&names_); if (!names_.size()) { LOG(WARNING) << "No camera available. Exiting test."; |