 Chromium Code Reviews
 Chromium Code Reviews Issue 2479413002:
  Image Capture v4l2: reset all user controls to default values when closing device fd  (Closed)
    
  
    Issue 2479413002:
  Image Capture v4l2: reset all user controls to default values when closing device fd  (Closed) 
  | 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 <linux/version.h> | 7 #include <linux/version.h> | 
| 8 #include <linux/videodev2.h> | |
| 8 #include <poll.h> | 9 #include <poll.h> | 
| 9 #include <sys/fcntl.h> | 10 #include <sys/fcntl.h> | 
| 10 #include <sys/ioctl.h> | 11 #include <sys/ioctl.h> | 
| 11 #include <sys/mman.h> | 12 #include <sys/mman.h> | 
| 12 #include <utility> | 13 #include <utility> | 
| 13 | 14 | 
| 14 #include "base/bind.h" | 15 #include "base/bind.h" | 
| 15 #include "base/files/file_enumerator.h" | 16 #include "base/files/file_enumerator.h" | 
| 16 #include "base/posix/eintr_wrapper.h" | 17 #include "base/posix/eintr_wrapper.h" | 
| 17 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" | 
| (...skipping 27 matching lines...) Expand all Loading... | |
| 45 // https://crbug.com/470717 | 46 // https://crbug.com/470717 | 
| 46 const int kCaptureTimeoutMs = 1000; | 47 const int kCaptureTimeoutMs = 1000; | 
| 47 // The number of continuous timeouts tolerated before treated as error. | 48 // The number of continuous timeouts tolerated before treated as error. | 
| 48 const int kContinuousTimeoutLimit = 10; | 49 const int kContinuousTimeoutLimit = 10; | 
| 49 // MJPEG is preferred if the requested width or height is larger than this. | 50 // MJPEG is preferred if the requested width or height is larger than this. | 
| 50 const int kMjpegWidth = 640; | 51 const int kMjpegWidth = 640; | 
| 51 const int kMjpegHeight = 480; | 52 const int kMjpegHeight = 480; | 
| 52 // Typical framerate, in fps | 53 // Typical framerate, in fps | 
| 53 const int kTypicalFramerate = 30; | 54 const int kTypicalFramerate = 30; | 
| 54 | 55 | 
| 56 const int kMaxIOCtrlRetries = 5; | |
| 
emircan
2016/12/07 21:45:31
Add a comment for this here as well.
 
mcasas
2016/12/08 23:46:52
Done.
 | |
| 57 | |
| 55 // V4L2 color formats supported by V4L2CaptureDelegate derived classes. | 58 // V4L2 color formats supported by V4L2CaptureDelegate derived classes. | 
| 56 // This list is ordered by precedence of use -- but see caveats for MJPEG. | 59 // This list is ordered by precedence of use -- but see caveats for MJPEG. | 
| 57 static struct { | 60 static struct { | 
| 58 uint32_t fourcc; | 61 uint32_t fourcc; | 
| 59 VideoPixelFormat pixel_format; | 62 VideoPixelFormat pixel_format; | 
| 60 size_t num_planes; | 63 size_t num_planes; | 
| 61 } const kSupportedFormatsAndPlanarity[] = { | 64 } const kSupportedFormatsAndPlanarity[] = { | 
| 62 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420, 1}, | 65 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420, 1}, | 
| 63 {V4L2_PIX_FMT_Y16, PIXEL_FORMAT_Y16, 1}, | 66 {V4L2_PIX_FMT_Y16, PIXEL_FORMAT_Y16, 1}, | 
| 64 {V4L2_PIX_FMT_Z16, PIXEL_FORMAT_Y16, 1}, | 67 {V4L2_PIX_FMT_Z16, PIXEL_FORMAT_Y16, 1}, | 
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 127 | 130 | 
| 128 v4l2_control current = {}; | 131 v4l2_control current = {}; | 
| 129 current.id = control_id; | 132 current.id = control_id; | 
| 130 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_G_CTRL, ¤t)) < 0) | 133 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_G_CTRL, ¤t)) < 0) | 
| 131 return mojom::Range::New(); | 134 return mojom::Range::New(); | 
| 132 capability->current = current.value; | 135 capability->current = current.value; | 
| 133 | 136 | 
| 134 return capability; | 137 return capability; | 
| 135 } | 138 } | 
| 136 | 139 | 
| 140 // Determines if a |control_id| is special, i.e. controls another one's state. | |
| 141 static bool IsSpecialControl(int control_id) { | |
| 142 if (control_id == V4L2_CID_AUTO_WHITE_BALANCE) | |
| 
emircan
2016/12/07 21:45:31
Can you regroup this into a switch statement?
 
mcasas
2016/12/08 23:46:52
Done.
 | |
| 143 return true; | |
| 144 else if (control_id == V4L2_CID_EXPOSURE_AUTO) | |
| 145 return true; | |
| 146 else if (control_id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) | |
| 147 return true; | |
| 148 else if (control_id == V4L2_CID_FOCUS_AUTO) | |
| 149 return true; | |
| 150 return false; | |
| 151 } | |
| 152 | |
| 153 // Sets all user control to their default. Some controls are enabled by another | |
| 154 // flag, usually having the word "auto" in the name, see ISpecialControl(). | |
| 155 // These flags are preset beforehand. | |
| 156 static void ResetUserAndCameraControlsToDefault(int device_fd) { | |
| 157 // Set V4L2_CID_AUTO_WHITE_BALANCE to false first. | |
| 158 v4l2_control auto_white_balance = {}; | |
| 159 auto_white_balance.id = V4L2_CID_AUTO_WHITE_BALANCE; | |
| 160 auto_white_balance.value = false; | |
| 161 int num_retries = 0; | |
| 162 // Setting up the first control right after stopping streaming seems | |
| 163 // not to the work the first time, so retry a few times. | |
| 164 for (; num_retries < kMaxIOCtrlRetries && | |
| 165 HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_CTRL, &auto_white_balance)) < 0; | |
| 
emircan
2016/12/07 21:45:31
Would it help to log the return value here?
 
mcasas
2016/12/08 23:46:52
Not really, DPLOG() in l.167 will show what has
ha
 | |
| 166 ++num_retries) { | |
| 167 DPLOG(WARNING) << "VIDIOC_S_CTRL"; | |
| 168 } | |
| 169 if (num_retries == kMaxIOCtrlRetries) { | |
| 170 DLOG(ERROR) << "Cannot access device controls"; | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 std::vector<struct v4l2_ext_control> user_controls; | |
| 175 for (int control_id = V4L2_CID_USER_BASE; control_id < V4L2_CID_LASTP1; | |
| 176 ++control_id) { | |
| 177 if (IsSpecialControl(control_id)) | |
| 
emircan
2016/12/07 21:45:31
Can you refactor l.177-187 and l.226-236 into a st
 
mcasas
2016/12/08 23:46:52
I've rewritten the whole implementation so it's
ho
 | |
| 178 continue; | |
| 179 v4l2_queryctrl range = {}; | |
| 180 range.id = control_id; | |
| 181 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_QUERYCTRL, &range)) < 0) | |
| 182 continue; | |
| 183 | |
| 184 struct v4l2_ext_control ext_control = {}; | |
| 185 ext_control.id = control_id; | |
| 186 ext_control.value = range.default_value; | |
| 187 user_controls.push_back(ext_control); | |
| 188 } | |
| 189 | |
| 190 if (!user_controls.empty()) { | |
| 191 struct v4l2_ext_controls ext_controls = {}; | |
| 192 ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; | |
| 193 ext_controls.count = user_controls.size(); | |
| 194 ext_controls.controls = user_controls.data(); | |
| 195 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_EXT_CTRLS, &ext_controls)) < 0) | |
| 196 DPLOG(ERROR) << "VIDIOC_S_EXT_CTRLS"; | |
| 197 } | |
| 198 | |
| 199 std::vector<struct v4l2_ext_control> special_camera_controls; | |
| 200 // Set V4L2_CID_EXPOSURE_AUTO to V4L2_EXPOSURE_MANUAL. | |
| 201 v4l2_ext_control auto_exposure = {}; | |
| 202 auto_exposure.id = V4L2_CID_EXPOSURE_AUTO; | |
| 203 auto_exposure.value = V4L2_EXPOSURE_MANUAL; | |
| 204 special_camera_controls.push_back(auto_exposure); | |
| 205 // Set V4L2_CID_EXPOSURE_AUTO_PRIORITY to false. | |
| 206 v4l2_ext_control priority_auto_exposure = {}; | |
| 207 priority_auto_exposure.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY; | |
| 208 priority_auto_exposure.value = false; | |
| 209 special_camera_controls.push_back(priority_auto_exposure); | |
| 210 // Set V4L2_CID_FOCUS_AUTO to false. | |
| 211 v4l2_ext_control auto_focus = {}; | |
| 212 auto_focus.id = V4L2_CID_FOCUS_AUTO; | |
| 213 auto_focus.value = false; | |
| 214 special_camera_controls.push_back(auto_focus); | |
| 215 | |
| 216 struct v4l2_ext_controls ext_controls = {}; | |
| 217 ext_controls.ctrl_class = V4L2_CID_CAMERA_CLASS; | |
| 218 ext_controls.count = special_camera_controls.size(); | |
| 219 ext_controls.controls = special_camera_controls.data(); | |
| 220 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_EXT_CTRLS, &ext_controls)) < 0) | |
| 221 DPLOG(ERROR) << "VIDIOC_S_EXT_CTRLS"; | |
| 222 | |
| 223 std::vector<struct v4l2_ext_control> camera_controls; | |
| 224 for (int control_id = V4L2_CID_CAMERA_CLASS_BASE; | |
| 225 control_id < V4L2_CID_CAMERA_CLASS_BASE + 32; ++control_id) { | |
| 
emircan
2016/12/07 21:45:31
Where is this 32 coming from? I was doing a quick
 
mcasas
2016/12/08 23:46:52
In the latest kernel yes, but in the 3.13 that
we
 | |
| 226 if (IsSpecialControl(control_id)) | |
| 227 continue; | |
| 228 v4l2_queryctrl range = {}; | |
| 229 range.id = control_id; | |
| 230 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_QUERYCTRL, &range)) < 0) | |
| 231 continue; | |
| 232 | |
| 233 struct v4l2_ext_control ext_control = {}; | |
| 234 ext_control.id = control_id; | |
| 235 ext_control.value = range.default_value; | |
| 236 camera_controls.push_back(ext_control); | |
| 237 } | |
| 238 | |
| 239 if (!camera_controls.empty()) { | |
| 240 struct v4l2_ext_controls ext_controls = {}; | |
| 241 ext_controls.ctrl_class = V4L2_CID_CAMERA_CLASS; | |
| 242 ext_controls.count = camera_controls.size(); | |
| 243 ext_controls.controls = camera_controls.data(); | |
| 244 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_EXT_CTRLS, &ext_controls)) < 0) | |
| 245 DPLOG(ERROR) << "VIDIOC_S_EXT_CTRLS"; | |
| 246 } | |
| 247 } | |
| 248 | |
| 137 // Class keeping track of a SPLANE V4L2 buffer, mmap()ed on construction and | 249 // Class keeping track of a SPLANE V4L2 buffer, mmap()ed on construction and | 
| 138 // munmap()ed on destruction. | 250 // munmap()ed on destruction. | 
| 139 class V4L2CaptureDelegate::BufferTracker | 251 class V4L2CaptureDelegate::BufferTracker | 
| 140 : public base::RefCounted<BufferTracker> { | 252 : public base::RefCounted<BufferTracker> { | 
| 141 public: | 253 public: | 
| 142 BufferTracker(); | 254 BufferTracker(); | 
| 143 // Abstract method to mmap() given |fd| according to |buffer|. | 255 // Abstract method to mmap() given |fd| according to |buffer|. | 
| 144 bool Init(int fd, const v4l2_buffer& buffer); | 256 bool Init(int fd, const v4l2_buffer& buffer); | 
| 145 | 257 | 
| 146 const uint8_t* start() const { return start_; } | 258 const uint8_t* start() const { return start_; } | 
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 348 return; | 460 return; | 
| 349 } | 461 } | 
| 350 | 462 | 
| 351 buffer_tracker_pool_.clear(); | 463 buffer_tracker_pool_.clear(); | 
| 352 | 464 | 
| 353 v4l2_requestbuffers r_buffer; | 465 v4l2_requestbuffers r_buffer; | 
| 354 FillV4L2RequestBuffer(&r_buffer, 0); | 466 FillV4L2RequestBuffer(&r_buffer, 0); | 
| 355 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 467 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 
| 356 SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); | 468 SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); | 
| 357 | 469 | 
| 470 ResetUserAndCameraControlsToDefault(device_fd_.get()); | |
| 358 // At this point we can close the device. | 471 // At this point we can close the device. | 
| 359 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 472 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 
| 360 device_fd_.reset(); | 473 device_fd_.reset(); | 
| 361 is_capturing_ = false; | 474 is_capturing_ = false; | 
| 362 client_.reset(); | 475 client_.reset(); | 
| 363 } | 476 } | 
| 364 | 477 | 
| 365 void V4L2CaptureDelegate::TakePhoto( | 478 void V4L2CaptureDelegate::TakePhoto( | 
| 366 VideoCaptureDevice::TakePhotoCallback callback) { | 479 VideoCaptureDevice::TakePhotoCallback callback) { | 
| 367 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 480 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 
| (...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 646 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace"; | 759 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace"; | 
| 647 return false; | 760 return false; | 
| 648 } | 761 } | 
| 649 start_ = static_cast<uint8_t*>(start); | 762 start_ = static_cast<uint8_t*>(start); | 
| 650 length_ = buffer.length; | 763 length_ = buffer.length; | 
| 651 payload_size_ = 0; | 764 payload_size_ = 0; | 
| 652 return true; | 765 return true; | 
| 653 } | 766 } | 
| 654 | 767 | 
| 655 } // namespace media | 768 } // namespace media | 
| OLD | NEW |