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

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

Issue 2214533002: move //media/capture to //device/capture (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 4 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/capture/video/linux/v4l2_capture_delegate.h"
6
7 #include <poll.h>
8 #include <sys/fcntl.h>
9 #include <sys/ioctl.h>
10 #include <sys/mman.h>
11 #include <utility>
12
13 #include "base/bind.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/posix/eintr_wrapper.h"
16 #include "base/strings/stringprintf.h"
17 #include "build/build_config.h"
18 #include "media/base/bind_to_current_loop.h"
19 #include "media/capture/video/linux/video_capture_device_linux.h"
20
21 namespace media {
22
23 // Desired number of video buffers to allocate. The actual number of allocated
24 // buffers by v4l2 driver can be higher or lower than this number.
25 // kNumVideoBuffers should not be too small, or Chrome may not return enough
26 // buffers back to driver in time.
27 const uint32_t kNumVideoBuffers = 4;
28 // Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw.
29 // This value has been fine tuned. Before changing or modifying it see
30 // https://crbug.com/470717
31 const int kCaptureTimeoutMs = 1000;
32 // The number of continuous timeouts tolerated before treated as error.
33 const int kContinuousTimeoutLimit = 10;
34 // MJPEG is preferred if the requested width or height is larger than this.
35 const int kMjpegWidth = 640;
36 const int kMjpegHeight = 480;
37 // Typical framerate, in fps
38 const int kTypicalFramerate = 30;
39
40 // V4L2 color formats supported by V4L2CaptureDelegate derived classes.
41 // This list is ordered by precedence of use -- but see caveats for MJPEG.
42 static struct {
43 uint32_t fourcc;
44 VideoPixelFormat pixel_format;
45 size_t num_planes;
46 } const kSupportedFormatsAndPlanarity[] = {
47 {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420, 1},
48 {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2, 1},
49 {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY, 1},
50 {V4L2_PIX_FMT_RGB24, PIXEL_FORMAT_RGB24, 1},
51 // MJPEG is usually sitting fairly low since we don't want to have to
52 // decode. However, it is needed for large resolutions due to USB bandwidth
53 // limitations, so GetListOfUsableFourCcs() can duplicate it on top, see
54 // that method.
55 {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1},
56 // JPEG works as MJPEG on some gspca webcams from field reports, see
57 // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the
58 // least preferred format.
59 {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1},
60 };
61
62 // Fill in |format| with the given parameters.
63 static void FillV4L2Format(v4l2_format* format,
64 uint32_t width,
65 uint32_t height,
66 uint32_t pixelformat_fourcc) {
67 memset(format, 0, sizeof(*format));
68 format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
69 format->fmt.pix.width = width;
70 format->fmt.pix.height = height;
71 format->fmt.pix.pixelformat = pixelformat_fourcc;
72 }
73
74 // Fills all parts of |buffer|.
75 static void FillV4L2Buffer(v4l2_buffer* buffer, int index) {
76 memset(buffer, 0, sizeof(*buffer));
77 buffer->memory = V4L2_MEMORY_MMAP;
78 buffer->index = index;
79 buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
80 }
81
82 static void FillV4L2RequestBuffer(v4l2_requestbuffers* request_buffer,
83 int count) {
84 memset(request_buffer, 0, sizeof(*request_buffer));
85 request_buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
86 request_buffer->memory = V4L2_MEMORY_MMAP;
87 request_buffer->count = count;
88 }
89
90 // Returns the input |fourcc| as a std::string four char representation.
91 static std::string FourccToString(uint32_t fourcc) {
92 return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF,
93 (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF);
94 }
95
96 // Class keeping track of a SPLANE V4L2 buffer, mmap()ed on construction and
97 // munmap()ed on destruction.
98 class V4L2CaptureDelegate::BufferTracker
99 : public base::RefCounted<BufferTracker> {
100 public:
101 BufferTracker();
102 // Abstract method to mmap() given |fd| according to |buffer|.
103 bool Init(int fd, const v4l2_buffer& buffer);
104
105 const uint8_t* start() const { return start_; }
106 size_t payload_size() const { return payload_size_; }
107 void set_payload_size(size_t payload_size) {
108 DCHECK_LE(payload_size, length_);
109 payload_size_ = payload_size;
110 }
111
112 private:
113 friend class base::RefCounted<BufferTracker>;
114 virtual ~BufferTracker();
115
116 uint8_t* start_;
117 size_t length_;
118 size_t payload_size_;
119 };
120
121 // static
122 size_t V4L2CaptureDelegate::GetNumPlanesForFourCc(uint32_t fourcc) {
123 for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) {
124 if (fourcc_and_pixel_format.fourcc == fourcc)
125 return fourcc_and_pixel_format.num_planes;
126 }
127 DVLOG(1) << "Unknown fourcc " << FourccToString(fourcc);
128 return 0;
129 }
130
131 // static
132 VideoPixelFormat V4L2CaptureDelegate::V4l2FourCcToChromiumPixelFormat(
133 uint32_t v4l2_fourcc) {
134 for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) {
135 if (fourcc_and_pixel_format.fourcc == v4l2_fourcc)
136 return fourcc_and_pixel_format.pixel_format;
137 }
138 // Not finding a pixel format is OK during device capabilities enumeration.
139 // Let the caller decide if PIXEL_FORMAT_UNKNOWN is an error or
140 // not.
141 DVLOG(1) << "Unsupported pixel format: " << FourccToString(v4l2_fourcc);
142 return PIXEL_FORMAT_UNKNOWN;
143 }
144
145 // static
146 std::list<uint32_t> V4L2CaptureDelegate::GetListOfUsableFourCcs(
147 bool prefer_mjpeg) {
148 std::list<uint32_t> supported_formats;
149 for (const auto& format : kSupportedFormatsAndPlanarity)
150 supported_formats.push_back(format.fourcc);
151
152 // Duplicate MJPEG on top of the list depending on |prefer_mjpeg|.
153 if (prefer_mjpeg)
154 supported_formats.push_front(V4L2_PIX_FMT_MJPEG);
155
156 return supported_formats;
157 }
158
159 V4L2CaptureDelegate::V4L2CaptureDelegate(
160 const VideoCaptureDeviceDescriptor& device_descriptor,
161 const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
162 int power_line_frequency)
163 : v4l2_task_runner_(v4l2_task_runner),
164 device_descriptor_(device_descriptor),
165 power_line_frequency_(power_line_frequency),
166 is_capturing_(false),
167 timeout_count_(0),
168 rotation_(0) {}
169
170 void V4L2CaptureDelegate::AllocateAndStart(
171 int width,
172 int height,
173 float frame_rate,
174 std::unique_ptr<VideoCaptureDevice::Client> client) {
175 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
176 DCHECK(client);
177 client_ = std::move(client);
178
179 // Need to open camera with O_RDWR after Linux kernel 3.3.
180 device_fd_.reset(
181 HANDLE_EINTR(open(device_descriptor_.device_id.c_str(), O_RDWR)));
182 if (!device_fd_.is_valid()) {
183 SetErrorState(FROM_HERE, "Failed to open V4L2 device driver file.");
184 return;
185 }
186
187 v4l2_capability cap = {};
188 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) &&
189 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
190 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)))) {
191 device_fd_.reset();
192 SetErrorState(FROM_HERE, "This is not a V4L2 video capture device");
193 return;
194 }
195
196 // Get supported video formats in preferred order. For large resolutions,
197 // favour mjpeg over raw formats.
198 const std::list<uint32_t>& desired_v4l2_formats =
199 GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight);
200 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end();
201
202 v4l2_fmtdesc fmtdesc = {};
203 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
204 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
205 ++fmtdesc.index) {
206 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat);
207 }
208 if (best == desired_v4l2_formats.end()) {
209 SetErrorState(FROM_HERE, "Failed to find a supported camera format.");
210 return;
211 }
212
213 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best);
214 FillV4L2Format(&video_fmt_, width, height, *best);
215
216 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) {
217 SetErrorState(FROM_HERE, "Failed to set video capture format");
218 return;
219 }
220 const VideoPixelFormat pixel_format =
221 V4l2FourCcToChromiumPixelFormat(video_fmt_.fmt.pix.pixelformat);
222 if (pixel_format == PIXEL_FORMAT_UNKNOWN) {
223 SetErrorState(FROM_HERE, "Unsupported pixel format");
224 return;
225 }
226
227 // Set capture framerate in the form of capture interval.
228 v4l2_streamparm streamparm = {};
229 streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
230 // The following line checks that the driver knows about framerate get/set.
231 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
232 // Now check if the device is able to accept a capture framerate set.
233 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
234 // |frame_rate| is float, approximate by a fraction.
235 streamparm.parm.capture.timeperframe.numerator =
236 media::kFrameRatePrecision;
237 streamparm.parm.capture.timeperframe.denominator =
238 (frame_rate) ? (frame_rate * media::kFrameRatePrecision)
239 : (kTypicalFramerate * media::kFrameRatePrecision);
240
241 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) <
242 0) {
243 SetErrorState(FROM_HERE, "Failed to set camera framerate");
244 return;
245 }
246 DVLOG(2) << "Actual camera driverframerate: "
247 << streamparm.parm.capture.timeperframe.denominator << "/"
248 << streamparm.parm.capture.timeperframe.numerator;
249 }
250 }
251 // TODO(mcasas): what should be done if the camera driver does not allow
252 // framerate configuration, or the actual one is different from the desired?
253
254 // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
255 // operation (|errno| == EINVAL in this case) or plain failure.
256 if ((power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ||
257 (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) ||
258 (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_AUTO)) {
259 struct v4l2_control control = {};
260 control.id = V4L2_CID_POWER_LINE_FREQUENCY;
261 control.value = power_line_frequency_;
262 const int retval =
263 HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
264 if (retval != 0)
265 DVLOG(1) << "Error setting power line frequency removal";
266 }
267
268 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width,
269 video_fmt_.fmt.pix.height);
270 capture_format_.frame_rate = frame_rate;
271 capture_format_.pixel_format = pixel_format;
272
273 v4l2_requestbuffers r_buffer;
274 FillV4L2RequestBuffer(&r_buffer, kNumVideoBuffers);
275 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
276 SetErrorState(FROM_HERE, "Error requesting MMAP buffers from V4L2");
277 return;
278 }
279 for (unsigned int i = 0; i < r_buffer.count; ++i) {
280 if (!MapAndQueueBuffer(i)) {
281 SetErrorState(FROM_HERE, "Allocate buffer failed");
282 return;
283 }
284 }
285
286 v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
287 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type)) <
288 0) {
289 SetErrorState(FROM_HERE, "VIDIOC_STREAMON failed");
290 return;
291 }
292
293 is_capturing_ = true;
294 // Post task to start fetching frames from v4l2.
295 v4l2_task_runner_->PostTask(
296 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this));
297 }
298
299 void V4L2CaptureDelegate::StopAndDeAllocate() {
300 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
301 // The order is important: stop streaming, clear |buffer_pool_|,
302 // thus munmap()ing the v4l2_buffers, and then return them to the OS.
303 v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
304 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type)) <
305 0) {
306 SetErrorState(FROM_HERE, "VIDIOC_STREAMOFF failed");
307 return;
308 }
309
310 buffer_tracker_pool_.clear();
311
312 v4l2_requestbuffers r_buffer;
313 FillV4L2RequestBuffer(&r_buffer, 0);
314 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0)
315 SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0");
316
317 // At this point we can close the device.
318 // This is also needed for correctly changing settings later via VIDIOC_S_FMT.
319 device_fd_.reset();
320 is_capturing_ = false;
321 client_.reset();
322 }
323
324 void V4L2CaptureDelegate::SetRotation(int rotation) {
325 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
326 DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
327 rotation_ = rotation;
328 }
329
330 V4L2CaptureDelegate::~V4L2CaptureDelegate() {}
331
332 bool V4L2CaptureDelegate::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(new BufferTracker());
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 V4L2CaptureDelegate::DoCapture() {
357 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
358 if (!is_capturing_)
359 return;
360
361 pollfd device_pfd = {};
362 device_pfd.fd = device_fd_.get();
363 device_pfd.events = POLLIN;
364 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs));
365 if (result < 0) {
366 SetErrorState(FROM_HERE, "Poll failed");
367 return;
368 }
369 // Check if poll() timed out; track the amount of times it did in a row and
370 // throw an error if it times out too many times.
371 if (result == 0) {
372 timeout_count_++;
373 if (timeout_count_ >= kContinuousTimeoutLimit) {
374 SetErrorState(FROM_HERE,
375 "Multiple continuous timeouts while read-polling.");
376 timeout_count_ = 0;
377 return;
378 }
379 } else {
380 timeout_count_ = 0;
381 }
382
383 // Deenqueue, send and reenqueue a buffer if the driver has filled one in.
384 if (device_pfd.revents & POLLIN) {
385 v4l2_buffer buffer;
386 FillV4L2Buffer(&buffer, 0);
387
388 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) {
389 SetErrorState(FROM_HERE, "Failed to dequeue capture buffer");
390 return;
391 }
392
393 buffer_tracker_pool_[buffer.index]->set_payload_size(buffer.bytesused);
394 const scoped_refptr<BufferTracker>& buffer_tracker =
395 buffer_tracker_pool_[buffer.index];
396
397 base::TimeDelta timestamp =
398 base::TimeDelta::FromSeconds(buffer.timestamp.tv_sec) +
399 base::TimeDelta::FromMicroseconds(buffer.timestamp.tv_usec);
400 client_->OnIncomingCapturedData(
401 buffer_tracker->start(), buffer_tracker->payload_size(),
402 capture_format_, rotation_, base::TimeTicks::Now(), timestamp);
403
404 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
405 SetErrorState(FROM_HERE, "Failed to enqueue capture buffer");
406 return;
407 }
408 }
409
410 v4l2_task_runner_->PostTask(
411 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this));
412 }
413
414 void V4L2CaptureDelegate::SetErrorState(
415 const tracked_objects::Location& from_here,
416 const std::string& reason) {
417 DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
418 is_capturing_ = false;
419 client_->OnError(from_here, reason);
420 }
421
422 V4L2CaptureDelegate::BufferTracker::BufferTracker() {}
423
424 V4L2CaptureDelegate::BufferTracker::~BufferTracker() {
425 if (start_ == nullptr)
426 return;
427 const int result = munmap(start_, length_);
428 PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer";
429 }
430
431 bool V4L2CaptureDelegate::BufferTracker::Init(int fd,
432 const v4l2_buffer& buffer) {
433 // Some devices require mmap() to be called with both READ and WRITE.
434 // See http://crbug.com/178582.
435 void* const start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
436 MAP_SHARED, fd, buffer.m.offset);
437 if (start == MAP_FAILED) {
438 DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace";
439 return false;
440 }
441 start_ = static_cast<uint8_t*>(start);
442 length_ = buffer.length;
443 payload_size_ = 0;
444 return true;
445 }
446
447 } // namespace media
OLDNEW
« no previous file with comments | « media/capture/video/linux/v4l2_capture_delegate.h ('k') | media/capture/video/linux/video_capture_device_chromeos.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698