 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 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 70 // decode. However, it is needed for large resolutions due to USB bandwidth | 71 // decode. However, it is needed for large resolutions due to USB bandwidth | 
| 71 // limitations, so GetListOfUsableFourCcs() can duplicate it on top, see | 72 // limitations, so GetListOfUsableFourCcs() can duplicate it on top, see | 
| 72 // that method. | 73 // that method. | 
| 73 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1}, | 74 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1}, | 
| 74 // JPEG works as MJPEG on some gspca webcams from field reports, see | 75 // JPEG works as MJPEG on some gspca webcams from field reports, see | 
| 75 // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the | 76 // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the | 
| 76 // least preferred format. | 77 // least preferred format. | 
| 77 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1}, | 78 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1}, | 
| 78 }; | 79 }; | 
| 79 | 80 | 
| 81 // Maximum number of ioctl retries before giving up trying to reset controls. | |
| 82 const int kMaxIOCtrlRetries = 5; | |
| 83 | |
| 84 // Base id and class identifier for Controls to be reset. | |
| 85 static struct { | |
| 86 uint32_t control_base; | |
| 87 uint32_t class_id; | |
| 88 } const kControls[] = {{V4L2_CID_USER_BASE, V4L2_CID_USER_CLASS}, | |
| 89 {V4L2_CID_CAMERA_CLASS_BASE, V4L2_CID_CAMERA_CLASS}}; | |
| 90 | |
| 80 // Fill in |format| with the given parameters. | 91 // Fill in |format| with the given parameters. | 
| 81 static void FillV4L2Format(v4l2_format* format, | 92 static void FillV4L2Format(v4l2_format* format, | 
| 82 uint32_t width, | 93 uint32_t width, | 
| 83 uint32_t height, | 94 uint32_t height, | 
| 84 uint32_t pixelformat_fourcc) { | 95 uint32_t pixelformat_fourcc) { | 
| 85 memset(format, 0, sizeof(*format)); | 96 memset(format, 0, sizeof(*format)); | 
| 86 format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 97 format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 
| 87 format->fmt.pix.width = width; | 98 format->fmt.pix.width = width; | 
| 88 format->fmt.pix.height = height; | 99 format->fmt.pix.height = height; | 
| 89 format->fmt.pix.pixelformat = pixelformat_fourcc; | 100 format->fmt.pix.pixelformat = pixelformat_fourcc; | 
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 127 | 138 | 
| 128 v4l2_control current = {}; | 139 v4l2_control current = {}; | 
| 129 current.id = control_id; | 140 current.id = control_id; | 
| 130 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_G_CTRL, ¤t)) < 0) | 141 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_G_CTRL, ¤t)) < 0) | 
| 131 return mojom::Range::New(); | 142 return mojom::Range::New(); | 
| 132 capability->current = current.value; | 143 capability->current = current.value; | 
| 133 | 144 | 
| 134 return capability; | 145 return capability; | 
| 135 } | 146 } | 
| 136 | 147 | 
| 148 // Determines if |control_id| is special, i.e. controls another one's state. | |
| 149 static bool IsSpecialControl(int control_id) { | |
| 150 switch (control_id) { | |
| 151 case V4L2_CID_AUTO_WHITE_BALANCE: | |
| 152 case V4L2_CID_EXPOSURE_AUTO: | |
| 153 case V4L2_CID_EXPOSURE_AUTO_PRIORITY: | |
| 154 case V4L2_CID_FOCUS_AUTO: | |
| 155 return true; | |
| 156 } | |
| 157 return false; | |
| 158 } | |
| 159 | |
| 160 // Sets all user control to their default. Some controls are enabled by another | |
| 161 // flag, usually having the word "auto" in the name, see IsSpecialControl(). | |
| 162 // These flags are preset beforehand, then set to their defaults individually | |
| 163 // afterwards. | |
| 164 static void ResetUserAndCameraControlsToDefault(int device_fd) { | |
| 165 // Set V4L2_CID_AUTO_WHITE_BALANCE to false first. | |
| 166 v4l2_control auto_white_balance = {}; | |
| 167 auto_white_balance.id = V4L2_CID_AUTO_WHITE_BALANCE; | |
| 168 auto_white_balance.value = false; | |
| 169 int num_retries = 0; | |
| 170 // Setting up the first control right after stopping streaming seems | |
| 171 // not to the work the first time, so retry a few times. | |
| 172 for (; num_retries < kMaxIOCtrlRetries && | |
| 173 HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_CTRL, &auto_white_balance)) < 0; | |
| 174 ++num_retries) { | |
| 175 DPLOG(WARNING) << "VIDIOC_S_CTRL"; | |
| 176 } | |
| 177 if (num_retries == kMaxIOCtrlRetries) { | |
| 178 DLOG(ERROR) << "Cannot access device controls"; | |
| 179 return; | |
| 180 } | |
| 181 | |
| 182 std::vector<struct v4l2_ext_control> special_camera_controls; | |
| 183 // Set V4L2_CID_EXPOSURE_AUTO to V4L2_EXPOSURE_MANUAL. | |
| 184 v4l2_ext_control auto_exposure = {}; | |
| 185 auto_exposure.id = V4L2_CID_EXPOSURE_AUTO; | |
| 186 auto_exposure.value = V4L2_EXPOSURE_MANUAL; | |
| 187 special_camera_controls.push_back(auto_exposure); | |
| 188 // Set V4L2_CID_EXPOSURE_AUTO_PRIORITY to false. | |
| 189 v4l2_ext_control priority_auto_exposure = {}; | |
| 190 priority_auto_exposure.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY; | |
| 191 priority_auto_exposure.value = false; | |
| 192 special_camera_controls.push_back(priority_auto_exposure); | |
| 193 // Set V4L2_CID_FOCUS_AUTO to false. | |
| 194 v4l2_ext_control auto_focus = {}; | |
| 195 auto_focus.id = V4L2_CID_FOCUS_AUTO; | |
| 196 auto_focus.value = false; | |
| 197 special_camera_controls.push_back(auto_focus); | |
| 198 | |
| 199 struct v4l2_ext_controls ext_controls = {}; | |
| 200 ext_controls.ctrl_class = V4L2_CID_CAMERA_CLASS; | |
| 201 ext_controls.count = special_camera_controls.size(); | |
| 202 ext_controls.controls = special_camera_controls.data(); | |
| 203 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_EXT_CTRLS, &ext_controls)) < 0) | |
| 204 DPLOG(ERROR) << "VIDIOC_S_EXT_CTRLS"; | |
| 205 | |
| 206 std::vector<struct v4l2_ext_control> camera_controls; | |
| 207 for (const auto& control : kControls) { | |
| 208 std::vector<struct v4l2_ext_control> camera_controls; | |
| 209 | |
| 210 v4l2_queryctrl range = {}; | |
| 211 range.id = control.control_base | V4L2_CTRL_FLAG_NEXT_CTRL; | |
| 212 while (0 == HANDLE_EINTR(ioctl(device_fd, VIDIOC_QUERYCTRL, &range))) { | |
| 
emircan
2016/12/09 19:19:37
This is different than before, as loop will exit w
 
mcasas
2016/12/09 21:01:17
Before we skipped the failing controls because
we
 | |
| 213 if (V4L2_CTRL_ID2CLASS(range.id) != V4L2_CTRL_ID2CLASS(control.class_id)) | |
| 214 break; | |
| 215 range.id |= V4L2_CTRL_FLAG_NEXT_CTRL; | |
| 216 | |
| 217 if (IsSpecialControl(range.id & ~V4L2_CTRL_FLAG_NEXT_CTRL)) | |
| 218 continue; | |
| 219 | |
| 220 struct v4l2_ext_control ext_control = {}; | |
| 221 ext_control.id = range.id & ~V4L2_CTRL_FLAG_NEXT_CTRL; | |
| 222 ext_control.value = range.default_value; | |
| 223 camera_controls.push_back(ext_control); | |
| 224 } | |
| 225 | |
| 226 if (!camera_controls.empty()) { | |
| 227 struct v4l2_ext_controls ext_controls = {}; | |
| 228 ext_controls.ctrl_class = control.class_id; | |
| 229 ext_controls.count = camera_controls.size(); | |
| 230 ext_controls.controls = camera_controls.data(); | |
| 231 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_EXT_CTRLS, &ext_controls)) < 0) | |
| 232 DPLOG(ERROR) << "VIDIOC_S_EXT_CTRLS"; | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 // Now set the special flags to the default values | |
| 237 v4l2_queryctrl range = {}; | |
| 238 range.id = V4L2_CID_AUTO_WHITE_BALANCE; | |
| 239 HANDLE_EINTR(ioctl(device_fd, VIDIOC_QUERYCTRL, &range)); | |
| 240 auto_white_balance.value = range.default_value; | |
| 241 HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_CTRL, &auto_white_balance)); | |
| 242 | |
| 243 special_camera_controls.clear(); | |
| 244 memset(&range, sizeof(struct v4l2_queryctrl), 0); | |
| 245 range.id = V4L2_CID_EXPOSURE_AUTO; | |
| 246 HANDLE_EINTR(ioctl(device_fd, VIDIOC_QUERYCTRL, &range)); | |
| 247 auto_exposure.value = range.default_value; | |
| 248 special_camera_controls.push_back(auto_exposure); | |
| 249 | |
| 250 memset(&range, sizeof(struct v4l2_queryctrl), 0); | |
| 251 range.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY; | |
| 252 HANDLE_EINTR(ioctl(device_fd, VIDIOC_QUERYCTRL, &range)); | |
| 253 priority_auto_exposure.value = range.default_value; | |
| 254 special_camera_controls.push_back(priority_auto_exposure); | |
| 255 | |
| 256 memset(&range, sizeof(struct v4l2_queryctrl), 0); | |
| 257 range.id = V4L2_CID_FOCUS_AUTO; | |
| 258 HANDLE_EINTR(ioctl(device_fd, VIDIOC_QUERYCTRL, &range)); | |
| 259 auto_focus.value = range.default_value; | |
| 260 special_camera_controls.push_back(auto_focus); | |
| 261 | |
| 262 memset(&ext_controls, sizeof(struct v4l2_ext_controls), 0); | |
| 263 ext_controls.ctrl_class = V4L2_CID_CAMERA_CLASS; | |
| 264 ext_controls.count = special_camera_controls.size(); | |
| 265 ext_controls.controls = special_camera_controls.data(); | |
| 266 if (HANDLE_EINTR(ioctl(device_fd, VIDIOC_S_EXT_CTRLS, &ext_controls)) < 0) | |
| 267 DPLOG(ERROR) << "VIDIOC_S_EXT_CTRLS"; | |
| 268 } | |
| 269 | |
| 137 // Class keeping track of a SPLANE V4L2 buffer, mmap()ed on construction and | 270 // Class keeping track of a SPLANE V4L2 buffer, mmap()ed on construction and | 
| 138 // munmap()ed on destruction. | 271 // munmap()ed on destruction. | 
| 139 class V4L2CaptureDelegate::BufferTracker | 272 class V4L2CaptureDelegate::BufferTracker | 
| 140 : public base::RefCounted<BufferTracker> { | 273 : public base::RefCounted<BufferTracker> { | 
| 141 public: | 274 public: | 
| 142 BufferTracker(); | 275 BufferTracker(); | 
| 143 // Abstract method to mmap() given |fd| according to |buffer|. | 276 // Abstract method to mmap() given |fd| according to |buffer|. | 
| 144 bool Init(int fd, const v4l2_buffer& buffer); | 277 bool Init(int fd, const v4l2_buffer& buffer); | 
| 145 | 278 | 
| 146 const uint8_t* start() const { return start_; } | 279 const uint8_t* start() const { return start_; } | 
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 348 return; | 481 return; | 
| 349 } | 482 } | 
| 350 | 483 | 
| 351 buffer_tracker_pool_.clear(); | 484 buffer_tracker_pool_.clear(); | 
| 352 | 485 | 
| 353 v4l2_requestbuffers r_buffer; | 486 v4l2_requestbuffers r_buffer; | 
| 354 FillV4L2RequestBuffer(&r_buffer, 0); | 487 FillV4L2RequestBuffer(&r_buffer, 0); | 
| 355 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 488 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 
| 356 SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); | 489 SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); | 
| 357 | 490 | 
| 491 ResetUserAndCameraControlsToDefault(device_fd_.get()); | |
| 358 // At this point we can close the device. | 492 // At this point we can close the device. | 
| 359 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 493 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 
| 360 device_fd_.reset(); | 494 device_fd_.reset(); | 
| 361 is_capturing_ = false; | 495 is_capturing_ = false; | 
| 362 client_.reset(); | 496 client_.reset(); | 
| 363 } | 497 } | 
| 364 | 498 | 
| 365 void V4L2CaptureDelegate::TakePhoto( | 499 void V4L2CaptureDelegate::TakePhoto( | 
| 366 VideoCaptureDevice::TakePhotoCallback callback) { | 500 VideoCaptureDevice::TakePhotoCallback callback) { | 
| 367 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 501 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"; | 780 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace"; | 
| 647 return false; | 781 return false; | 
| 648 } | 782 } | 
| 649 start_ = static_cast<uint8_t*>(start); | 783 start_ = static_cast<uint8_t*>(start); | 
| 650 length_ = buffer.length; | 784 length_ = buffer.length; | 
| 651 payload_size_ = 0; | 785 payload_size_ = 0; | 
| 652 return true; | 786 return true; | 
| 653 } | 787 } | 
| 654 | 788 | 
| 655 } // namespace media | 789 } // namespace media | 
| OLD | NEW |