Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(370)

Side by Side Diff: media/video/capture/linux/v4l2_video_capture_delegate.cc

Issue 967793002: Linux Video Capture: Add V4L2VideoCaptureDelegate{Single,Multi}Plane. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: posciak@ comments on PS5. Minor rebase of video_capture_controller_unittest.cc and BUILD.gn Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/video/capture/linux/v4l2_video_capture_delegate.h"
6
7 #include <poll.h>
8 #include <sys/fcntl.h>
9 #include <sys/ioctl.h>
10 #include <sys/mman.h>
11
12 #include "base/bind.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/stringprintf.h"
16 #include "media/base/bind_to_current_loop.h"
17 #include "media/video/capture/linux/v4l2_capture_delegate_multi_plane.h"
18 #include "media/video/capture/linux/v4l2_capture_delegate_single_plane.h"
19 #include "media/video/capture/linux/video_capture_device_linux.h"
20
21 namespace media {
22
23 // Max number of video buffers VideoCaptureDeviceLinux can allocate.
24 const uint32_t kMaxVideoBuffers = 2;
25 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw.
26 const int kCaptureTimeoutMs = 200;
27 // The number of continuous timeouts tolerated before treated as error.
28 const int kContinuousTimeoutLimit = 10;
29 // MJPEG is preferred if the requested width or height is larger than this.
30 const int kMjpegWidth = 640;
31 const int kMjpegHeight = 480;
32 // Typical framerate, in fps
33 const int kTypicalFramerate = 30;
34
35 // V4L2 color formats supported by V4L2VideoCaptureDelegate derived classes.
36 // This list is ordered by precedence of use -- but see caveats for MJPEG.
37 static struct{
38 uint32_t fourcc;
39 VideoPixelFormat pixel_format;
40 size_t num_planes;
41 } const kSupportedFormatsAndPlanarity[] = {
42 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420, 1},
43 {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2, 1},
44 {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY, 1},
45 #if !defined(OS_OPENBSD)
46 // TODO(mcasas): add V4L2_PIX_FMT_YVU420M when available in bots.
47 {V4L2_PIX_FMT_YUV420M, PIXEL_FORMAT_I420, 3},
48 #endif
49 // MJPEG is usually sitting fairly low since we don't want to have to decode.
50 // However, is needed for large resolutions due to USB bandwidth limitations,
51 // so GetListOfUsableFourCcs() can duplicate it on top, see that method.
52 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1},
53 // JPEG works as MJPEG on some gspca webcams from field reports, see
54 // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the least
55 // preferred format.
56 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1},
57 };
58
59 // static
60 scoped_refptr<V4L2VideoCaptureDelegate>
61 V4L2VideoCaptureDelegate::CreateV4L2VideoCaptureDelegate(
62 const VideoCaptureDevice::Name& device_name,
63 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
64 int power_line_frequency) {
65 switch (device_name.capture_api_type()) {
66 case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE:
67 return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane(
68 device_name, v4l2_task_runner, power_line_frequency));
69 case VideoCaptureDevice::Name::V4L2_MULTI_PLANE:
70 #if !defined(OS_OPENBSD)
71 return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane(
72 device_name, v4l2_task_runner, power_line_frequency));
73 default:
74 #endif
75 NOTIMPLEMENTED() << "Unknown V4L2 capture API type";
76 return scoped_refptr<V4L2VideoCaptureDelegate>();
77 }
78 }
79
80 //static
81 size_t V4L2VideoCaptureDelegate::GetNumPlanesForFourCc(uint32_t fourcc) {
82 for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) {
83 if (fourcc_and_pixel_format.fourcc == fourcc)
84 return fourcc_and_pixel_format.num_planes;
85 }
86 DVLOG(1) << "Unknown fourcc " << FourccToString(fourcc);
87 return 0;
88 }
89
90 // static
91 VideoPixelFormat V4L2VideoCaptureDelegate::V4l2FourCcToChromiumPixelFormat(
92 uint32_t v4l2_fourcc) {
93 for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) {
94 if (fourcc_and_pixel_format.fourcc == v4l2_fourcc)
95 return fourcc_and_pixel_format.pixel_format;
96 }
97 // Not finding a pixel format is OK during device capabilities enumeration.
98 // Let the caller decide if PIXEL_FORMAT_UNKNOWN is an error or not.
99 DVLOG(1) << "Unsupported pixel format: " << FourccToString(v4l2_fourcc);
100 return PIXEL_FORMAT_UNKNOWN;
101 }
102
103 // static
104 std::list<uint32_t> V4L2VideoCaptureDelegate::GetListOfUsableFourCcs(
105 bool prefer_mjpeg) {
106 std::list<uint32_t> supported_formats;
107 for (const auto& format : kSupportedFormatsAndPlanarity)
108 supported_formats.push_back(format.fourcc);
109
110 // Duplicate MJPEG on top of the list depending on |prefer_mjpeg|.
111 if (prefer_mjpeg)
112 supported_formats.push_front(V4L2_PIX_FMT_MJPEG);
113
114 return supported_formats;
115 }
116
117 //static
118 std::string V4L2VideoCaptureDelegate::FourccToString(uint32_t fourcc) {
119 return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF,
120 (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF);
121 }
122
123 V4L2VideoCaptureDelegate::BufferTracker::BufferTracker() {
124 }
125
126 V4L2VideoCaptureDelegate::BufferTracker::~BufferTracker() {
127 for (const auto& plane : planes_) {
128 if (plane.start == nullptr)
129 continue;
130 const int result = munmap(plane.start, plane.length);
131 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer";
132 }
133 }
134
135 void V4L2VideoCaptureDelegate::BufferTracker::AddMmapedPlane(void* const start,
136 size_t length) {
137 Plane plane;
138 plane.start = start;
139 plane.length = length;
140 planes_.push_back(plane);
141 }
142
143 V4L2VideoCaptureDelegate::V4L2VideoCaptureDelegate(
144 const VideoCaptureDevice::Name& device_name,
145 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
146 int power_line_frequency)
147 : capture_type_((device_name.capture_api_type() ==
148 VideoCaptureDevice::Name::V4L2_SINGLE_PLANE)
149 ? V4L2_BUF_TYPE_VIDEO_CAPTURE
150 : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE),
151 v4l2_task_runner_(v4l2_task_runner),
152 device_name_(device_name),
153 power_line_frequency_(power_line_frequency),
154 is_capturing_(false),
155 timeout_count_(0),
156 rotation_(0) {
157 }
158
159 V4L2VideoCaptureDelegate::~V4L2VideoCaptureDelegate() {
160 }
161
162 void V4L2VideoCaptureDelegate::AllocateAndStart(
163 int width,
164 int height,
165 float frame_rate,
166 scoped_ptr<VideoCaptureDevice::Client> client) {
167 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
168 DCHECK(client);
169 client_ = client.Pass();
170
171 // Need to open camera with O_RDWR after Linux kernel 3.3.
172 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR)));
173 if (!device_fd_.is_valid()) {
174 SetErrorState("Failed to open V4L2 device driver file.");
175 return;
176 }
177
178 v4l2_capability cap = {};
179 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) &&
180 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ||
181 cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) &&
182 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) &&
183 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) {
184 device_fd_.reset();
185 SetErrorState("This is not a V4L2 video capture device");
186 return;
187 }
188
189 // Get supported video formats in preferred order.
190 // For large resolutions, favour mjpeg over raw formats.
191 const std::list<uint32_t>& desired_v4l2_formats =
192 GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight);
193 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end();
194
195 v4l2_fmtdesc fmtdesc = {};
196 fmtdesc.type = capture_type_;
197 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
198 ++fmtdesc.index) {
199 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat);
200 }
201 if (best == desired_v4l2_formats.end()) {
202 SetErrorState("Failed to find a supported camera format.");
203 return;
204 }
205
206 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best);
207
208 v4l2_format video_fmt = {};
209 video_fmt.type = capture_type_;
210 if (!FillV4L2Format(&video_fmt, width, height, *best)) {
211 SetErrorState("Failed filling in V4L2 Format");
212 return;
213 }
214
215 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt)) < 0) {
216 SetErrorState("Failed to set video capture format");
217 return;
218 }
219 const VideoPixelFormat pixel_format =
220 V4l2FourCcToChromiumPixelFormat(video_fmt.fmt.pix.pixelformat);
221 if (pixel_format == PIXEL_FORMAT_UNKNOWN) {
222 SetErrorState("Unsupported pixel format");
223 return;
224 }
225
226 // Set capture framerate in the form of capture interval.
227 v4l2_streamparm streamparm = {};
228 streamparm.type = capture_type_;
229 // The following line checks that the driver knows about framerate get/set.
230 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
231 // Now check if the device is able to accept a capture framerate set.
232 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
233 // |frame_rate| is float, approximate by a fraction.
234 streamparm.parm.capture.timeperframe.numerator =
235 media::kFrameRatePrecision;
236 streamparm.parm.capture.timeperframe.denominator =
237 (frame_rate) ? (frame_rate * media::kFrameRatePrecision)
238 : (kTypicalFramerate * media::kFrameRatePrecision);
239
240 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) <
241 0) {
242 SetErrorState("Failed to set camera framerate");
243 return;
244 }
245 DVLOG(2) << "Actual camera driverframerate: "
246 << streamparm.parm.capture.timeperframe.denominator << "/"
247 << streamparm.parm.capture.timeperframe.numerator;
248 }
249 }
250 // TODO(mcasas): what should be done if the camera driver does not allow
251 // framerate configuration, or the actual one is different from the desired?
252
253 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
254 // operation (|errno| == EINVAL in this case) or plain failure.
255 if ((power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ||
256 (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) ||
257 (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_AUTO)) {
258 struct v4l2_control control = {};
259 control.id = V4L2_CID_POWER_LINE_FREQUENCY;
260 control.value = power_line_frequency_;
261 const int retval =
262 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
263 if (retval != 0)
264 DVLOG(1) << "Error setting power line frequency removal";
265 }
266
267 capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
268 video_fmt.fmt.pix.height);
269 capture_format_.frame_rate = frame_rate;
270 capture_format_.pixel_format = pixel_format;
271
272 v4l2_requestbuffers r_buffer = {};
273 r_buffer.type = capture_type_;
274 r_buffer.memory = V4L2_MEMORY_MMAP;
275 r_buffer.count = kMaxVideoBuffers;
276 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
277 SetErrorState("Error requesting MMAP buffers from V4L2");
278 return;
279 }
280 DCHECK_EQ(r_buffer.count, kMaxVideoBuffers);
281 for (unsigned int i = 0; i < r_buffer.count; ++i) {
282 if (!MapAndQueueBuffer(i)) {
283 SetErrorState("Allocate buffer failed");
284 return;
285 }
286 }
287
288 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_))
289 < 0) {
290 SetErrorState("VIDIOC_STREAMON failed");
291 return;
292 }
293
294 is_capturing_ = true;
295 // Post task to start fetching frames from v4l2.
296 v4l2_task_runner_->PostTask(
297 FROM_HERE, base::Bind(&V4L2VideoCaptureDelegate::DoCapture, this));
298 }
299
300 void V4L2VideoCaptureDelegate::StopAndDeAllocate() {
301 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
302 // The order is important: stop streaming, clear |buffer_pool_|,
303 // thus munmap()ing the v4l2_buffers, and then return them to the OS.
304 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_))
305 < 0) {
306 SetErrorState("VIDIOC_STREAMOFF failed");
307 return;
308 }
309
310 buffer_tracker_pool_.clear();
311
312 v4l2_requestbuffers r_buffer = {};
313 r_buffer.type = capture_type_;
314 r_buffer.memory = V4L2_MEMORY_MMAP;
315 r_buffer.count = 0;
316 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0)
317 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0");
318
319 // At this point we can close the device.
320 // This is also needed for correctly changing settings later via VIDIOC_S_FMT.
321 device_fd_.reset();
322 is_capturing_ = false;
323 client_.reset();
324 }
325
326 void V4L2VideoCaptureDelegate::SetRotation(int rotation) {
327 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
328 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
329 rotation_ = rotation;
330 }
331
332 bool V4L2VideoCaptureDelegate::MapAndQueueBuffer(int index) {
333 v4l2_buffer buffer;
334 FillV4L2Buffer(&buffer, index);
335
336 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
337 DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer";
338 return false;
339 }
340
341 const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker();
342 if (!buffer_tracker->Init(device_fd_.get(), buffer)) {
343 DLOG(ERROR) << "Error creating BufferTracker";
344 return false;
345 }
346 buffer_tracker_pool_.push_back(buffer_tracker);
347
348 // Enqueue the buffer in the drivers incoming queue.
349 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
350 DLOG(ERROR) << "Error enqueuing a V4L2 buffer back into the driver";
351 return false;
352 }
353 return true;
354 }
355
356 void V4L2VideoCaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer,
357 int i) const {
358 memset(buffer, 0, sizeof(*buffer));
359 buffer->memory = V4L2_MEMORY_MMAP;
360 buffer->index = i;
361 FinishFillingV4L2Buffer(buffer);
362 }
363
364 void V4L2VideoCaptureDelegate::DoCapture() {
365 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
366 if (!is_capturing_)
367 return;
368
369 pollfd device_pfd = {};
370 device_pfd.fd = device_fd_.get();
371 device_pfd.events = POLLIN;
372 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs));
373 if (result < 0) {
374 SetErrorState("Poll failed");
375 return;
376 }
377 // Check if poll() timed out; track the amount of times it did in a row and
378 // throw an error if it times out too many times.
379 if (result == 0) {
380 timeout_count_++;
381 if (timeout_count_ >= kContinuousTimeoutLimit) {
382 SetErrorState("Multiple continuous timeouts while read-polling.");
383 timeout_count_ = 0;
384 return;
385 }
386 } else {
387 timeout_count_ = 0;
388 }
389
390 // Deenqueue, send and reenqueue a buffer if the driver has filled one in.
391 if (device_pfd.revents & POLLIN) {
392 v4l2_buffer buffer;
393 FillV4L2Buffer(&buffer, 0);
394
395 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) {
396 SetErrorState("Failed to dequeue capture buffer");
397 return;
398 }
399
400 SendBuffer(buffer_tracker_pool_[buffer.index]);
401
402 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
403 SetErrorState("Failed to enqueue capture buffer");
404 return;
405 }
406 }
407
408 v4l2_task_runner_->PostTask(
409 FROM_HERE, base::Bind(&V4L2VideoCaptureDelegate::DoCapture, this));
410 }
411
412 void V4L2VideoCaptureDelegate::SetErrorState(const std::string& reason) {
413 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
414 is_capturing_ = false;
415 client_->OnError(reason);
416 }
417
418 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698