Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/video/capture/linux/video_capture_device_factory_linux.h" | 5 #include "media/video/capture/linux/video_capture_device_factory_linux.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <fcntl.h> | 8 #include <fcntl.h> |
| 9 #if defined(OS_OPENBSD) | 9 #if defined(OS_OPENBSD) |
| 10 #include <sys/videoio.h> | 10 #include <sys/videoio.h> |
| 11 #else | 11 #else |
| 12 #include <linux/videodev2.h> | 12 #include <linux/videodev2.h> |
| 13 #endif | 13 #endif |
| 14 #include <sys/ioctl.h> | 14 #include <sys/ioctl.h> |
| 15 | 15 |
| 16 #include "base/files/file_enumerator.h" | 16 #include "base/files/file_enumerator.h" |
| 17 #include "base/files/scoped_file.h" | 17 #include "base/files/scoped_file.h" |
| 18 #include "base/posix/eintr_wrapper.h" | 18 #include "base/posix/eintr_wrapper.h" |
| 19 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 20 #if defined(OS_CHROMEOS) | 20 #if defined(OS_CHROMEOS) |
| 21 #include "media/video/capture/linux/video_capture_device_chromeos.h" | 21 #include "media/video/capture/linux/video_capture_device_chromeos.h" |
| 22 #endif | 22 #endif |
| 23 #include "media/video/capture/linux/video_capture_device_linux.h" | 23 #include "media/video/capture/linux/video_capture_device_linux.h" |
| 24 | 24 |
| 25 namespace media { | 25 namespace media { |
| 26 | 26 |
| 27 static bool HasUsableFormats(int fd) { | 27 static int Ioctl(const base::ScopedFD& device_fd, int request, void* arg) { |
| 28 v4l2_fmtdesc fmtdesc; | 28 return VideoCaptureDeviceLinux::Ioctl(device_fd, request, arg); |
| 29 std::list<int> usable_fourccs; | 29 } |
| 30 | 30 |
| 31 media::VideoCaptureDeviceLinux::GetListOfUsableFourCCs(false, | 31 static bool HasUsableFormats(const base::ScopedFD& device_fd) { |
| 32 &usable_fourccs); | 32 const std::list<int>& usable_fourccs = |
| 33 VideoCaptureDeviceLinux::GetListOfUsableFourCCs(false); | |
| 33 | 34 |
| 34 memset(&fmtdesc, 0, sizeof(v4l2_fmtdesc)); | 35 v4l2_fmtdesc fmtdesc = {}; |
| 35 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 36 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 36 | 37 for (; Ioctl(device_fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) { |
| 37 while (HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0) { | |
| 38 if (std::find(usable_fourccs.begin(), usable_fourccs.end(), | 38 if (std::find(usable_fourccs.begin(), usable_fourccs.end(), |
| 39 fmtdesc.pixelformat) != usable_fourccs.end()) | 39 fmtdesc.pixelformat) != usable_fourccs.end()) |
| 40 return true; | 40 return true; |
| 41 | |
| 42 fmtdesc.index++; | |
| 43 } | 41 } |
| 44 return false; | 42 return false; |
| 45 } | 43 } |
| 46 | 44 |
| 47 VideoCaptureDeviceFactoryLinux::VideoCaptureDeviceFactoryLinux( | 45 VideoCaptureDeviceFactoryLinux::VideoCaptureDeviceFactoryLinux( |
| 48 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) | 46 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) |
| 49 : ui_task_runner_(ui_task_runner) { | 47 : ui_task_runner_(ui_task_runner) { |
| 50 } | 48 } |
| 51 | 49 |
| 52 VideoCaptureDeviceFactoryLinux::~VideoCaptureDeviceFactoryLinux() { | 50 VideoCaptureDeviceFactoryLinux::~VideoCaptureDeviceFactoryLinux() { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 73 return scoped_ptr<VideoCaptureDevice>(); | 71 return scoped_ptr<VideoCaptureDevice>(); |
| 74 } | 72 } |
| 75 | 73 |
| 76 return scoped_ptr<VideoCaptureDevice>(self); | 74 return scoped_ptr<VideoCaptureDevice>(self); |
| 77 } | 75 } |
| 78 | 76 |
| 79 void VideoCaptureDeviceFactoryLinux::GetDeviceNames( | 77 void VideoCaptureDeviceFactoryLinux::GetDeviceNames( |
| 80 VideoCaptureDevice::Names* const device_names) { | 78 VideoCaptureDevice::Names* const device_names) { |
| 81 DCHECK(thread_checker_.CalledOnValidThread()); | 79 DCHECK(thread_checker_.CalledOnValidThread()); |
| 82 DCHECK(device_names->empty()); | 80 DCHECK(device_names->empty()); |
| 83 base::FilePath path("/dev/"); | 81 const base::FilePath path("/dev/"); |
| 84 base::FileEnumerator enumerator( | 82 base::FileEnumerator enumerator( |
| 85 path, false, base::FileEnumerator::FILES, "video*"); | 83 path, false, base::FileEnumerator::FILES, "video*"); |
| 86 | 84 |
| 87 while (!enumerator.Next().empty()) { | 85 while (!enumerator.Next().empty()) { |
| 88 base::FileEnumerator::FileInfo info = enumerator.GetInfo(); | 86 const base::FileEnumerator::FileInfo info = enumerator.GetInfo(); |
| 89 | 87 const std::string unique_id = path.value() + info.GetName().value(); |
| 90 std::string unique_id = path.value() + info.GetName().value(); | 88 const base::ScopedFD fd(HANDLE_EINTR(open(unique_id.c_str(), O_RDONLY))); |
| 91 base::ScopedFD fd(HANDLE_EINTR(open(unique_id.c_str(), O_RDONLY))); | |
| 92 if (!fd.is_valid()) { | 89 if (!fd.is_valid()) { |
| 93 // Failed to open this device. | 90 DLOG(ERROR) << "Couldn't open " << info.GetName().value(); |
| 94 continue; | 91 continue; |
| 95 } | 92 } |
| 96 // Test if this is a V4L2 capture device. | 93 // Test if this is a V4L2 capture device and if it has at least one |
| 94 // supported capture format. Single plane capture devices should not be | |
| 95 // output devices, see http://crbug.com/139356. | |
|
perkj_chrome
2014/12/16 10:34:43
? Can you expand on this?
mcasas
2014/12/16 18:08:17
Done.
| |
| 97 v4l2_capability cap; | 96 v4l2_capability cap; |
| 98 if ((HANDLE_EINTR(ioctl(fd.get(), VIDIOC_QUERYCAP, &cap)) == 0) && | 97 if ((Ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) && |
| 99 (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && | 98 (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE && |
| 100 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) { | 99 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) && |
|
perkj_chrome
2014/12/16 10:34:43
the indentation was corret from the beginning.
mcasas
2014/12/16 18:08:17
Done.
| |
| 101 // This is a V4L2 video capture device | 100 HasUsableFormats(fd)) { |
| 102 if (HasUsableFormats(fd.get())) { | 101 device_names->push_front(VideoCaptureDevice::Name( |
| 103 VideoCaptureDevice::Name device_name(base::StringPrintf("%s", cap.card), | 102 base::StringPrintf("%s", cap.card), unique_id)); |
| 104 unique_id); | |
| 105 device_names->push_back(device_name); | |
| 106 } else { | |
| 107 DVLOG(1) << "No usable formats reported by " << info.GetName().value(); | |
| 108 } | |
| 109 } | 103 } |
| 110 } | 104 } |
| 111 } | 105 } |
| 112 | 106 |
| 113 void VideoCaptureDeviceFactoryLinux::GetDeviceSupportedFormats( | 107 void VideoCaptureDeviceFactoryLinux::GetDeviceSupportedFormats( |
| 114 const VideoCaptureDevice::Name& device, | 108 const VideoCaptureDevice::Name& device, |
| 115 VideoCaptureFormats* supported_formats) { | 109 VideoCaptureFormats* supported_formats) { |
| 116 DCHECK(thread_checker_.CalledOnValidThread()); | 110 DCHECK(thread_checker_.CalledOnValidThread()); |
| 117 if (device.id().empty()) | 111 if (device.id().empty()) |
| 118 return; | 112 return; |
| 119 base::ScopedFD fd(HANDLE_EINTR(open(device.id().c_str(), O_RDONLY))); | 113 base::ScopedFD fd(HANDLE_EINTR(open(device.id().c_str(), O_RDONLY))); |
| 120 if (!fd.is_valid()) { | 114 if (!fd.is_valid()) // Failed to open this device. |
| 121 // Failed to open this device. | |
| 122 return; | 115 return; |
| 123 } | |
| 124 supported_formats->clear(); | 116 supported_formats->clear(); |
| 125 | 117 |
| 126 // Retrieve the caps one by one, first get pixel format, then sizes, then | 118 // Retrieve the caps one by one, first get pixel format, then sizes, then |
| 127 // frame rates. See http://linuxtv.org/downloads/v4l-dvb-apis for reference. | 119 // frame rates. |
| 128 v4l2_fmtdesc pixel_format = {}; | 120 v4l2_fmtdesc pixel_format = {}; |
| 129 pixel_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | 121 pixel_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| 130 while (HANDLE_EINTR(ioctl(fd.get(), VIDIOC_ENUM_FMT, &pixel_format)) == 0) { | 122 for (; Ioctl(fd, VIDIOC_ENUM_FMT, &pixel_format) == 0; ++pixel_format.index) { |
| 131 VideoCaptureFormat supported_format; | 123 VideoCaptureFormat supported_format; |
| 132 supported_format.pixel_format = | 124 supported_format.pixel_format = |
| 133 VideoCaptureDeviceLinux::V4l2ColorToVideoCaptureColorFormat( | 125 VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat( |
| 134 (int32)pixel_format.pixelformat); | 126 pixel_format.pixelformat); |
| 135 if (supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) { | 127 if (supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) |
| 136 ++pixel_format.index; | |
| 137 continue; | 128 continue; |
| 138 } | |
| 139 | 129 |
| 140 v4l2_frmsizeenum frame_size = {}; | 130 v4l2_frmsizeenum frame_size = {}; |
| 141 frame_size.pixel_format = pixel_format.pixelformat; | 131 frame_size.pixel_format = pixel_format.pixelformat; |
| 142 while (HANDLE_EINTR(ioctl(fd.get(), VIDIOC_ENUM_FRAMESIZES, &frame_size)) == | 132 for (; Ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frame_size) == 0; |
| 143 0) { | 133 ++frame_size.index) { |
| 144 if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) { | 134 if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) { |
| 145 supported_format.frame_size.SetSize( | 135 supported_format.frame_size.SetSize( |
| 146 frame_size.discrete.width, frame_size.discrete.height); | 136 frame_size.discrete.width, frame_size.discrete.height); |
| 147 } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE) { | 137 } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE || |
| 148 // TODO(mcasas): see http://crbug.com/249953, support these devices. | 138 frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { |
| 149 NOTIMPLEMENTED(); | |
| 150 } else if (frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { | |
| 151 // TODO(mcasas): see http://crbug.com/249953, support these devices. | 139 // TODO(mcasas): see http://crbug.com/249953, support these devices. |
| 152 NOTIMPLEMENTED(); | 140 NOTIMPLEMENTED(); |
| 153 } | 141 } |
| 142 | |
| 154 v4l2_frmivalenum frame_interval = {}; | 143 v4l2_frmivalenum frame_interval = {}; |
| 155 frame_interval.pixel_format = pixel_format.pixelformat; | 144 frame_interval.pixel_format = pixel_format.pixelformat; |
| 156 frame_interval.width = frame_size.discrete.width; | 145 frame_interval.width = frame_size.discrete.width; |
| 157 frame_interval.height = frame_size.discrete.height; | 146 frame_interval.height = frame_size.discrete.height; |
| 158 std::list<float> frame_rates; | 147 std::list<float> frame_rates; |
| 159 while (HANDLE_EINTR(ioctl( | 148 for (; Ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frame_interval) == 0; |
| 160 fd.get(), VIDIOC_ENUM_FRAMEINTERVALS, &frame_interval)) == 0) { | 149 ++frame_interval.index) { |
|
perkj_chrome
2014/12/16 10:34:43
nit indentation.
mcasas
2014/12/16 18:08:17
Done.
| |
| 161 if (frame_interval.type == V4L2_FRMIVAL_TYPE_DISCRETE) { | 150 if (frame_interval.type == V4L2_FRMIVAL_TYPE_DISCRETE) { |
| 162 if (frame_interval.discrete.numerator != 0) { | 151 if (frame_interval.discrete.numerator != 0) { |
| 163 frame_rates.push_back( | 152 frame_rates.push_back(frame_interval.discrete.denominator / |
| 164 static_cast<float>(frame_interval.discrete.denominator) / | |
| 165 static_cast<float>(frame_interval.discrete.numerator)); | 153 static_cast<float>(frame_interval.discrete.numerator)); |
| 166 } | 154 } |
| 167 } else if (frame_interval.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) { | 155 } else if (frame_interval.type == V4L2_FRMIVAL_TYPE_CONTINUOUS || |
| 168 // TODO(mcasas): see http://crbug.com/249953, support these devices. | 156 frame_interval.type == V4L2_FRMIVAL_TYPE_STEPWISE) { |
| 169 NOTIMPLEMENTED(); | |
| 170 break; | |
| 171 } else if (frame_interval.type == V4L2_FRMIVAL_TYPE_STEPWISE) { | |
| 172 // TODO(mcasas): see http://crbug.com/249953, support these devices. | 157 // TODO(mcasas): see http://crbug.com/249953, support these devices. |
| 173 NOTIMPLEMENTED(); | 158 NOTIMPLEMENTED(); |
| 174 break; | 159 break; |
| 175 } | 160 } |
| 176 ++frame_interval.index; | |
| 177 } | 161 } |
| 162 // Some devices, e.g. Kinect, do not enumerate any frame rates, see | |
| 163 // http://crbug.com/412284. Set their frame_rate to zero. | |
| 164 if (frame_rates.empty()) | |
| 165 frame_rates.push_back(0.0f); | |
| 178 | 166 |
| 179 // Some devices, e.g. Kinect, do not enumerate any frame rates. For these | 167 for (const auto& it : frame_rates) { |
| 180 // devices, we do not want to lose all enumeration (pixel format and | 168 supported_format.frame_rate = it; |
| 181 // resolution), so we return a frame rate of zero instead. | |
| 182 if (frame_rates.empty()) | |
| 183 frame_rates.push_back(0); | |
| 184 | |
| 185 for (std::list<float>::iterator it = frame_rates.begin(); | |
| 186 it != frame_rates.end(); ++it) { | |
| 187 supported_format.frame_rate = *it; | |
| 188 supported_formats->push_back(supported_format); | 169 supported_formats->push_back(supported_format); |
| 189 DVLOG(1) << device.name() << " " << supported_format.ToString(); | 170 DVLOG(1) << device.name() << " " << supported_format.ToString(); |
| 190 } | 171 } |
| 191 ++frame_size.index; | |
| 192 } | 172 } |
| 193 ++pixel_format.index; | |
| 194 } | 173 } |
| 195 return; | 174 return; |
| 196 } | 175 } |
| 197 | 176 |
| 198 // static | 177 // static |
| 199 VideoCaptureDeviceFactory* | 178 VideoCaptureDeviceFactory* |
| 200 VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory( | 179 VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory( |
| 201 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { | 180 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { |
| 202 return new VideoCaptureDeviceFactoryLinux(ui_task_runner); | 181 return new VideoCaptureDeviceFactoryLinux(ui_task_runner); |
| 203 } | 182 } |
| 204 | 183 |
| 205 } // namespace media | 184 } // namespace media |
| OLD | NEW |