| 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))) { | 
|  | 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, 0, sizeof(struct v4l2_queryctrl)); | 
|  | 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, 0, sizeof(struct v4l2_queryctrl)); | 
|  | 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, 0, sizeof(struct v4l2_queryctrl)); | 
|  | 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, 0, sizeof(struct v4l2_ext_controls)); | 
|  | 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 | 
|---|