Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / video / capture / linux / video_capture_device_factory_linux.cc
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
3 // found in the LICENSE file.
4
5 #include "media/video/capture/linux/video_capture_device_factory_linux.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #if defined(OS_OPENBSD)
10 #include <sys/videoio.h>
11 #else
12 #include <linux/videodev2.h>
13 #endif
14 #include <sys/ioctl.h>
15
16 #include "base/files/file_enumerator.h"
17 #include "base/files/scoped_file.h"
18 #include "base/posix/eintr_wrapper.h"
19 #include "base/strings/stringprintf.h"
20 #if defined(OS_CHROMEOS)
21 #include "media/video/capture/linux/video_capture_device_chromeos.h"
22 #endif
23 #include "media/video/capture/linux/video_capture_device_linux.h"
24
25 namespace media {
26
27 static bool HasUsableFormats(int fd) {
28   v4l2_fmtdesc fmtdesc;
29   std::list<int> usable_fourccs;
30
31   media::VideoCaptureDeviceLinux::GetListOfUsableFourCCs(false,
32                                                          &usable_fourccs);
33
34   memset(&fmtdesc, 0, sizeof(v4l2_fmtdesc));
35   fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
36
37   while (HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0) {
38     if (std::find(usable_fourccs.begin(), usable_fourccs.end(),
39                   fmtdesc.pixelformat) != usable_fourccs.end())
40       return true;
41
42     fmtdesc.index++;
43   }
44   return false;
45 }
46
47 VideoCaptureDeviceFactoryLinux::VideoCaptureDeviceFactoryLinux(
48     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
49     : ui_task_runner_(ui_task_runner) {
50 }
51
52 VideoCaptureDeviceFactoryLinux::~VideoCaptureDeviceFactoryLinux() {
53 }
54
55 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryLinux::Create(
56     const VideoCaptureDevice::Name& device_name) {
57   DCHECK(thread_checker_.CalledOnValidThread());
58 #if defined(OS_CHROMEOS)
59   VideoCaptureDeviceChromeOS* self =
60       new VideoCaptureDeviceChromeOS(ui_task_runner_, device_name);
61 #else
62   VideoCaptureDeviceLinux* self = new VideoCaptureDeviceLinux(device_name);
63 #endif
64   if (!self)
65     return scoped_ptr<VideoCaptureDevice>();
66   // Test opening the device driver. This is to make sure it is available.
67   // We will reopen it again in our worker thread when someone
68   // allocates the camera.
69   base::ScopedFD fd(HANDLE_EINTR(open(device_name.id().c_str(), O_RDONLY)));
70   if (!fd.is_valid()) {
71     DVLOG(1) << "Cannot open device";
72     delete self;
73     return scoped_ptr<VideoCaptureDevice>();
74   }
75
76   return scoped_ptr<VideoCaptureDevice>(self);
77 }
78
79 void VideoCaptureDeviceFactoryLinux::GetDeviceNames(
80     VideoCaptureDevice::Names* const device_names) {
81   DCHECK(thread_checker_.CalledOnValidThread());
82   DCHECK(device_names->empty());
83   base::FilePath path("/dev/");
84   base::FileEnumerator enumerator(
85       path, false, base::FileEnumerator::FILES, "video*");
86
87   while (!enumerator.Next().empty()) {
88     base::FileEnumerator::FileInfo info = enumerator.GetInfo();
89
90     std::string unique_id = path.value() + info.GetName().value();
91     base::ScopedFD fd(HANDLE_EINTR(open(unique_id.c_str(), O_RDONLY)));
92     if (!fd.is_valid()) {
93       // Failed to open this device.
94       continue;
95     }
96     // Test if this is a V4L2 capture device.
97     v4l2_capability cap;
98     if ((HANDLE_EINTR(ioctl(fd.get(), VIDIOC_QUERYCAP, &cap)) == 0) &&
99         (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
100         !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) {
101       // This is a V4L2 video capture device
102       if (HasUsableFormats(fd.get())) {
103         VideoCaptureDevice::Name device_name(base::StringPrintf("%s", cap.card),
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     }
110   }
111 }
112
113 void VideoCaptureDeviceFactoryLinux::GetDeviceSupportedFormats(
114     const VideoCaptureDevice::Name& device,
115     VideoCaptureFormats* supported_formats) {
116   DCHECK(thread_checker_.CalledOnValidThread());
117   if (device.id().empty())
118     return;
119   base::ScopedFD fd(HANDLE_EINTR(open(device.id().c_str(), O_RDONLY)));
120   if (!fd.is_valid()) {
121     // Failed to open this device.
122     return;
123   }
124   supported_formats->clear();
125
126   // 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.
128   v4l2_fmtdesc pixel_format = {};
129   pixel_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
130   while (HANDLE_EINTR(ioctl(fd.get(), VIDIOC_ENUM_FMT, &pixel_format)) == 0) {
131     VideoCaptureFormat supported_format;
132     supported_format.pixel_format =
133         VideoCaptureDeviceLinux::V4l2ColorToVideoCaptureColorFormat(
134             (int32)pixel_format.pixelformat);
135     if (supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) {
136       ++pixel_format.index;
137       continue;
138     }
139
140     v4l2_frmsizeenum frame_size = {};
141     frame_size.pixel_format = pixel_format.pixelformat;
142     while (HANDLE_EINTR(ioctl(fd.get(), VIDIOC_ENUM_FRAMESIZES, &frame_size)) ==
143            0) {
144       if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
145         supported_format.frame_size.SetSize(
146             frame_size.discrete.width, frame_size.discrete.height);
147       } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
148         // TODO(mcasas): see http://crbug.com/249953, support these devices.
149         NOTIMPLEMENTED();
150       } else if (frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
151         // TODO(mcasas): see http://crbug.com/249953, support these devices.
152         NOTIMPLEMENTED();
153       }
154       v4l2_frmivalenum frame_interval = {};
155       frame_interval.pixel_format = pixel_format.pixelformat;
156       frame_interval.width = frame_size.discrete.width;
157       frame_interval.height = frame_size.discrete.height;
158       std::list<float> frame_rates;
159       while (HANDLE_EINTR(ioctl(
160                  fd.get(), VIDIOC_ENUM_FRAMEINTERVALS, &frame_interval)) == 0) {
161         if (frame_interval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
162           if (frame_interval.discrete.numerator != 0) {
163             frame_rates.push_back(
164                 static_cast<float>(frame_interval.discrete.denominator) /
165                 static_cast<float>(frame_interval.discrete.numerator));
166           }
167         } else if (frame_interval.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
168           // TODO(mcasas): see http://crbug.com/249953, support these devices.
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.
173           NOTIMPLEMENTED();
174           break;
175         }
176         ++frame_interval.index;
177       }
178
179       // Some devices, e.g. Kinect, do not enumerate any frame rates. For these
180       // devices, we do not want to lose all enumeration (pixel format and
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);
189         DVLOG(1) << device.name() << " " << supported_format.ToString();
190       }
191       ++frame_size.index;
192     }
193     ++pixel_format.index;
194   }
195   return;
196 }
197
198 }  // namespace media