1 // Copyright (c) 2010 The Chromium OS 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_v4l2_device.h"
6
7 #include <assert.h>
8 #include <poll.h>
9 #include <time.h>
10 #include <sys/stat.h>
11
12 #include <string>
13 #include <utility>
14
15 #define CHECK(a) assert(a)
16 #define MAJOR(dev) (((uint32_t)(dev)) >> 8)
17 #define MINOR(dev) (((uint32_t)(dev)) & 0xff)
18 #define V4L2_VIDEO_CAPTURE_MAJOR 81
19 #define V4L2_VIDEO_CAPTURE_MINOR_MIN 0
20 #define V4L2_VIDEO_CAPTURE_MINOR_MAX 64
21
V4L2Device(const char * dev_name,uint32_t buffers)22 V4L2Device::V4L2Device(const char* dev_name,
23 uint32_t buffers)
24 : dev_name_(dev_name),
25 io_(IO_METHOD_UNDEFINED),
26 fd_(-1),
27 v4l2_buffers_(NULL),
28 num_buffers_(0),
29 min_buffers_(buffers),
30 stopped_(false),
31 initialized_(false) {
32 }
33
~V4L2Device()34 V4L2Device::~V4L2Device() {
35 if (initialized_) {
36 if (stream_on_) {
37 StopCapture();
38 }
39 UninitDevice();
40 }
41 CloseDevice();
42 }
43
OpenDevice()44 bool V4L2Device::OpenDevice() {
45 struct stat st;
46 if (-1 == stat(dev_name_, &st)) {
47 printf("<<< Error: could not find v4l2 device %s: (%d) %s.>>>\n",
48 dev_name_, errno, strerror(errno));
49 return false;
50 }
51
52 if (!S_ISCHR(st.st_mode)) {
53 printf("<<< Error: specified v4l2 device %s is not char device.>>>\n",
54 dev_name_);
55 return false;
56 }
57
58 if (MAJOR(st.st_rdev) != V4L2_VIDEO_CAPTURE_MAJOR
59 || MINOR(st.st_rdev) >= V4L2_VIDEO_CAPTURE_MINOR_MAX) {
60 printf("<<< Error: specified v4l2 device %s is not v4l2 device.>>>\n",
61 dev_name_);
62 return false;
63 }
64
65 fd_ = open(dev_name_, O_RDWR | O_NONBLOCK, 0);
66 if (-1 == fd_) {
67 printf("<<< Error: specified v4l2 device %s could not be opened.>>>\n",
68 dev_name_);
69 return false;
70 }
71
72 v4l2_capability cap;
73 if (!ProbeCaps(&cap))
74 return false;
75
76 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
77 printf("<<< Error: %s does not support video capture.>>>\n", dev_name_);
78 return false;
79 }
80
81 return true;
82 }
83
CloseDevice()84 void V4L2Device::CloseDevice() {
85 if (fd_ != -1)
86 close(fd_);
87 fd_ = -1;
88 }
89
InitDevice(IOMethod io,uint32_t width,uint32_t height,uint32_t pixfmt,float fps,ConstantFramerate constant_framerate,uint32_t num_skip_frames)90 bool V4L2Device::InitDevice(IOMethod io,
91 uint32_t width,
92 uint32_t height,
93 uint32_t pixfmt,
94 float fps,
95 ConstantFramerate constant_framerate,
96 uint32_t num_skip_frames) {
97 io_ = io;
98
99 v4l2_format fmt;
100 if (!GetV4L2Format(&fmt))
101 return false;
102
103 fmt.fmt.pix.width = width;
104 fmt.fmt.pix.height = height;
105 fmt.fmt.pix.pixelformat = pixfmt;
106 fmt.fmt.pix.field = V4L2_FIELD_NONE;
107
108 if (-1 == DoIoctl(VIDIOC_S_FMT, &fmt)) {
109 printf("<<< Error: VIDIOC_S_FMT on %s.>>>\n", dev_name_);
110 return false;
111 }
112
113 v4l2_capability cap;
114 if (!ProbeCaps(&cap))
115 return false;
116
117 switch (io_) {
118 case IO_METHOD_MMAP:
119 case IO_METHOD_USERPTR:
120 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
121 printf("<<< Error: %s does not support streaming.>>>\n", dev_name_);
122 return false;
123 }
124 break;
125 default:
126 printf("<<< Error: IO method should be defined.>>>\n");
127 return false;
128 }
129
130 v4l2_streamparm param;
131 if (!GetParam(¶m))
132 return false;
133
134 if (param.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
135 if (fps > 0) {
136 SetFrameRate(fps);
137 } else {
138 printf("<<< Error: fps %f should be a positive number.>>>\n", fps);
139 return false;
140 }
141 }
142 float actual_fps = GetFrameRate();
143
144 int32_t constant_framerate_setting;
145 std::string constant_framerate_msg = "";
146 switch (constant_framerate) {
147 case DEFAULT_FRAMERATE_SETTING:
148 constant_framerate_setting = 1;
149 break;
150 case ENABLE_CONSTANT_FRAMERATE:
151 constant_framerate_setting = 0;
152 constant_framerate_msg = " with constant framerate";
153 break;
154 case DISABLE_CONSTANT_FRAMERATE:
155 constant_framerate_setting = 1;
156 constant_framerate_msg = " without constant framerate";
157 break;
158 default:
159 printf("<<< Error: Invalid constant framerate setting: %d. >>>\n",
160 constant_framerate);
161 return false;
162 }
163 SetControl(V4L2_CID_EXPOSURE_AUTO_PRIORITY, constant_framerate_setting);
164
165 printf("actual format for capture %dx%d %c%c%c%c picture at %.2f fps%s\n",
166 fmt.fmt.pix.width, fmt.fmt.pix.height,
167 (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
168 (pixfmt >> 16) & 0xff, (pixfmt >> 24 ) & 0xff, actual_fps,
169 constant_framerate_msg.c_str());
170 frame_timestamps_.clear();
171 num_skip_frames_ = num_skip_frames;
172
173 bool ret = false;
174 switch (io_) {
175 case IO_METHOD_MMAP:
176 ret = InitMmapIO();
177 break;
178 case IO_METHOD_USERPTR:
179 ret = InitUserPtrIO(fmt.fmt.pix.sizeimage);
180 break;
181 default:
182 printf("<<< Error: IO method should be defined.>>>\n");
183 return false;
184 }
185 if (ret)
186 initialized_ = true;
187 return ret;
188 }
189
UninitDevice()190 bool V4L2Device::UninitDevice() {
191 if (!initialized_) {
192 return true;
193 }
194 v4l2_requestbuffers req;
195 memset(&req, 0, sizeof(req));
196 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
197 switch (io_) {
198 case IO_METHOD_MMAP:
199 for (uint32_t i = 0; i < num_buffers_; ++i)
200 if (-1 == munmap(v4l2_buffers_[i].start, v4l2_buffers_[i].length)) {
201 printf("<<< Error: munmap() on %s failed.>>>\n", dev_name_);
202 return false;
203 }
204
205 req.memory = V4L2_MEMORY_MMAP;
206 if (-1 == DoIoctl(VIDIOC_REQBUFS, &req)) {
207 printf("<<< Error: VIDIOC_REQBUFS for MMAP failed on %s: %s.>>>\n",
208 dev_name_, strerror(errno));
209 return false;
210 }
211 break;
212 case IO_METHOD_USERPTR:
213 req.memory = V4L2_MEMORY_USERPTR;
214 if (-1 == DoIoctl(VIDIOC_REQBUFS, &req)) {
215 printf("<<< Error: VIDIOC_REQBUFS for USERPTR failed on %s.: %s>>>\n",
216 dev_name_, strerror(errno));
217 return false;
218 }
219
220 for (uint32_t i = 0; i < num_buffers_; ++i)
221 free(v4l2_buffers_[i].start);
222 break;
223 default:
224 printf("<<< Error: IO method should be defined.>>>\n");
225 return false;
226 }
227 FreeBuffer();
228 initialized_ = false;
229 return true;
230 }
231
StartCapture()232 bool V4L2Device::StartCapture() {
233 for (uint32_t i = 0; i < num_buffers_; ++i) {
234 if (!EnqueueBuffer(i))
235 return false;
236 }
237 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
238 if (-1 == DoIoctl(VIDIOC_STREAMON, &type)) {
239 printf("<<< Error: VIDIOC_STREAMON on %s.>>>\n", dev_name_);
240 return false;
241 }
242 stream_on_ = true;
243
244 uint32_t buf_index, data_size;
245 for (size_t i = 0; i < num_skip_frames_; i++) {
246 int ret;
247 while ((ret = ReadOneFrame(&buf_index, &data_size)) == 0);
248 if (ret < 0)
249 return false;
250 if (!EnqueueBuffer(buf_index))
251 return false;
252 }
253
254 return true;
255 }
256
StopCapture()257 bool V4L2Device::StopCapture() {
258 if (!stream_on_) {
259 return true;
260 }
261 v4l2_buf_type type;
262 switch (io_) {
263 case IO_METHOD_MMAP:
264 case IO_METHOD_USERPTR:
265 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
266 if (-1 == DoIoctl(VIDIOC_STREAMOFF, &type)) {
267 printf("<<< Error: VIDIOC_STREAMOFF on %s.>>>\n", dev_name_);
268 return false;
269 }
270 break;
271 default:
272 printf("<<< Error: IO method should be defined.>>>\n");
273 return false;
274 }
275 stream_on_ = false;
276 return true;
277 }
278
ProcessImage(const void * p)279 void V4L2Device::ProcessImage(const void* p) {
280 printf(".");
281 fflush(stdout);
282 }
283
284 // Do capture for duration of |time_in_sec|.
Run(uint32_t time_in_sec)285 bool V4L2Device::Run(uint32_t time_in_sec) {
286 stopped_ = false;
287 if (!time_in_sec)
288 return false;
289
290 uint64_t start_in_nanosec = 0;
291 uint32_t buffer_index, data_size;
292 while (!stopped_) {
293 int32_t r = ReadOneFrame(&buffer_index, &data_size);
294 if (r < 0)
295 return false;
296 if (r) {
297 if (start_in_nanosec == 0)
298 start_in_nanosec = Now();
299 ProcessImage(v4l2_buffers_[buffer_index].start);
300 if (!EnqueueBuffer(buffer_index))
301 return false;
302 }
303 if (start_in_nanosec) {
304 uint64_t end_in_nanosec = Now();
305 if (end_in_nanosec - start_in_nanosec >= time_in_sec * 1000000000ULL)
306 break;
307 }
308 }
309 // All resolutions should have at least 1 fps.
310 float actual_fps = static_cast<float>(GetNumFrames() - 1) / time_in_sec;
311 printf("\n<<< Info: Actual fps is %f on %s.>>>\n", actual_fps, dev_name_);
312 return true;
313 }
314
Stop()315 bool V4L2Device::Stop() {
316 stopped_ = true;
317 return true;
318 }
319
DoIoctl(int32_t request,void * arg)320 int32_t V4L2Device::DoIoctl(int32_t request, void* arg) {
321 int32_t r;
322 do {
323 r = ioctl(fd_, request, arg);
324 } while (-1 == r && EINTR == errno);
325 return r;
326 }
327
328 // return 1 : successful to retrieve a frame from device
329 // return 0 : EAGAIN
330 // negative : error
ReadOneFrame(uint32_t * buffer_index,uint32_t * data_size)331 int32_t V4L2Device::ReadOneFrame(uint32_t* buffer_index, uint32_t* data_size) {
332 const int kCaptureTimeoutMs = 1000;
333 pollfd device_pfd = {};
334 device_pfd.fd = fd_;
335 device_pfd.events = POLLIN;
336 const int result = poll(&device_pfd, 1, kCaptureTimeoutMs);
337 if (result < 0) {
338 printf("<<< Error: poll() failed on %s: %s.>>>\n", dev_name_, strerror(errno));
339 return -1;
340 }
341 if (result == 0) {
342 return 0;
343 }
344
345 v4l2_buffer buf;
346 int64_t ts;
347 memset(&buf, 0, sizeof(buf));
348 switch (io_) {
349 case IO_METHOD_MMAP:
350 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
351 buf.memory = V4L2_MEMORY_MMAP;
352 if (-1 == DoIoctl(VIDIOC_DQBUF, &buf)) {
353 switch (errno) {
354 case EAGAIN:
355 return 0;
356 case EIO:
357 // Could ignore EIO, see spec.
358 // Fall through.
359 default:
360 printf("<<< Error: VIDIOC_DQBUF failed on %s.>>>\n", dev_name_);
361 return -2;
362 }
363 }
364 // For checking constant frame rate, we have to use HW timestamp from
365 // v4l2_buffer to get more stable timestamp.
366 // Since kerenel after 3.18 have a fix to disable hardware timestamp
367 // (https://patchwork.kernel.org/patch/6874491/), we have to manually
368 // enable HW timestamp via /sys/module/uvcvideo/parameters/hwtimestamps.
369 ts = buf.timestamp.tv_sec * 1000000000LL + buf.timestamp.tv_usec * 1000;
370 frame_timestamps_.push_back(ts);
371 CHECK(buf.index < num_buffers_);
372 // TODO: uvcvideo driver ignores this field. This is negligible,
373 // so disabling this for now until we get a fix into the upstream driver.
374 // CHECK(buf.field == V4L2_FIELD_NONE); // progressive only.
375 break;
376 case IO_METHOD_USERPTR:
377 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
378 buf.memory = V4L2_MEMORY_USERPTR;
379 if (-1 == DoIoctl(VIDIOC_DQBUF, &buf)) {
380 switch (errno) {
381 case EAGAIN:
382 return 0;
383 case EIO:
384 // Could ignore EIO, see spec.
385 // Fall through.
386 default:
387 printf("<<< Error: VIDIOC_DQBUF failed on %s.>>>\n", dev_name_);
388 return -2;
389 }
390 }
391 ts = buf.timestamp.tv_sec * 1000000000LL + buf.timestamp.tv_usec * 1000;
392 frame_timestamps_.push_back(ts);
393 CHECK(buf.index < num_buffers_);
394 break;
395 default:
396 printf("<<< Error: IO method should be defined.>>>\n");
397 return -1;
398 }
399 if (buffer_index)
400 *buffer_index = buf.index;
401 if (data_size)
402 *data_size = buf.bytesused;
403 return 1;
404 }
405
EnqueueBuffer(uint32_t buffer_index)406 bool V4L2Device::EnqueueBuffer(uint32_t buffer_index) {
407 v4l2_buffer buf;
408 memset(&buf, 0, sizeof(buf));
409 switch (io_) {
410 case IO_METHOD_MMAP:
411 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
412 buf.memory = V4L2_MEMORY_MMAP;
413 buf.index = buffer_index;
414 if (-1 == DoIoctl(VIDIOC_QBUF, &buf)) {
415 printf("<<< Error: VIDIOC_QBUF failed on %s.>>>\n", dev_name_);
416 return false;
417 }
418 break;
419 case IO_METHOD_USERPTR:
420 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
421 buf.memory = V4L2_MEMORY_USERPTR;
422 buf.index = buffer_index;
423 buf.m.userptr = (unsigned long) v4l2_buffers_[buffer_index].start;
424 buf.length = v4l2_buffers_[buffer_index].length;
425 if (-1 == DoIoctl(VIDIOC_QBUF, &buf)) {
426 printf("<<< Error: VIDIOC_QBUF failed on %s.>>>\n", dev_name_);
427 return false;
428 }
429 break;
430 default:
431 printf("<<< Error: IO method should be defined.>>>\n");
432 return false;
433 }
434 return true;
435 }
436
AllocateBuffer(uint32_t buffer_count)437 bool V4L2Device::AllocateBuffer(uint32_t buffer_count) {
438 v4l2_buffers_ = new Buffer[buffer_count];
439 if (!v4l2_buffers_) {
440 printf("<<< Error: Out of memory.>>>\n");
441 return false;
442 }
443 return true;
444 }
445
FreeBuffer()446 bool V4L2Device::FreeBuffer() {
447 free(v4l2_buffers_);
448 v4l2_buffers_ = NULL;
449 return true;
450 }
451
InitMmapIO()452 bool V4L2Device::InitMmapIO() {
453 v4l2_requestbuffers req;
454 memset(&req, 0, sizeof(req));
455 req.count = min_buffers_;
456 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
457 req.memory = V4L2_MEMORY_MMAP;
458 if (-1 == DoIoctl(VIDIOC_REQBUFS, &req)) {
459 if (EINVAL == errno)
460 printf("<<< Error: mmap() io is not supported on %s.>>>\n", dev_name_);
461 else
462 printf("<<< Error: VIDIOC_REQBUFS for MMAP(%d) failed on %s: %s.>>>\n",
463 min_buffers_, dev_name_, strerror(errno));
464 return false;
465 }
466
467 if (req.count < min_buffers_) {
468 printf("<<< Error: Insufficient buffer memory on %s >>>\n",
469 dev_name_); // TODO(jiesun) :add flexibilities.
470 return false;
471 }
472
473 if (!AllocateBuffer(req.count))
474 return false;
475
476 for (num_buffers_ = 0; num_buffers_ < req.count; ++num_buffers_) {
477 v4l2_buffer buf;
478 memset(&buf, 0, sizeof(buf));
479 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
480 buf.memory = V4L2_MEMORY_MMAP;
481 buf.index = num_buffers_;
482 if (-1 == DoIoctl(VIDIOC_QUERYBUF, &buf)) {
483 printf("<<< Error: VIDIOC_QUERYBUF failed on %s.>>>\n", dev_name_);
484 return false;
485 }
486 v4l2_buffers_[num_buffers_].length = buf.length;
487 v4l2_buffers_[num_buffers_].start =
488 mmap(NULL, // Start anywhere.
489 buf.length,
490 PROT_READ | PROT_WRITE,
491 MAP_SHARED,
492 fd_, buf.m.offset);
493 if (MAP_FAILED == v4l2_buffers_[num_buffers_].start) {
494 printf("<<< Error: mmap() failed on %s.>>>\n", dev_name_);
495 return false;
496 }
497 }
498 return true;
499 }
500
InitUserPtrIO(uint32_t buffer_size)501 bool V4L2Device::InitUserPtrIO(uint32_t buffer_size) {
502 v4l2_requestbuffers req;
503 memset(&req, 0, sizeof(req));
504 req.count = min_buffers_;
505 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
506 req.memory = V4L2_MEMORY_USERPTR;
507
508 // Align up buffer_size to page size boundary.
509 uint32_t page_size = getpagesize();
510 buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);
511 if (-1 == DoIoctl(VIDIOC_REQBUFS, &req)) {
512 if (EINVAL == errno)
513 printf("<<< Error: user pointer is not supported on %s.>>>\n", dev_name_);
514 else
515 printf("<<< Error: VIDIOC_REQBUFS for USERPTR(%d) failed on %s: %s.>>>\n",
516 min_buffers_, dev_name_, strerror(errno));
517 return false;
518 }
519
520 if (!AllocateBuffer(req.count))
521 return false;
522
523 for (num_buffers_ = 0; num_buffers_ < req.count; ++num_buffers_) {
524 v4l2_buffers_[num_buffers_].length = buffer_size;
525 v4l2_buffers_[num_buffers_].start = memalign(page_size, buffer_size);
526
527 if (!v4l2_buffers_[num_buffers_].start) {
528 printf("<<< Error: Out of memory.>>>\n");
529 return false;
530 }
531 }
532 return true;
533 }
534
EnumInput()535 bool V4L2Device::EnumInput() {
536 v4l2_input input;
537 int32_t index;
538 if (-1 == DoIoctl(VIDIOC_G_INPUT, &index)) {
539 printf("<<< Info: VIDIOC_G_INPUT not supported.>>>\n");
540 return false;
541 }
542
543 for (int32_t i = 0 ; ; ++i) {
544 memset(&input, 0, sizeof(input));
545 input.index = i;
546 if (-1 == DoIoctl(VIDIOC_ENUMINPUT, &input)) {
547 if (i == 0) {
548 printf("<<< Info: VIDIOC_ENUMINPUT not supported.>>>\n");
549 return false;
550 } else {
551 break;
552 }
553 }
554 printf("Current input: %s %s\n", input.name, i == index ? "*" : "");
555 }
556 return true;
557 }
558
EnumStandard()559 bool V4L2Device::EnumStandard() {
560 v4l2_input input;
561 v4l2_standard standard;
562 memset(&input, 0, sizeof(input));
563 if (-1 == DoIoctl(VIDIOC_G_INPUT, &input.index)) {
564 printf("<<< Info: VIDIOC_G_INPUT not supported.>>>\n");
565 return false;
566 }
567
568 if (-1 == DoIoctl(VIDIOC_ENUMINPUT, &input)) {
569 printf("<<< Info: VIDIOC_ENUMINPUT not supported.>>>\n");
570 return false;
571 }
572
573 printf("Current input %s supports:\n", input.name);
574 memset(&standard, 0, sizeof(standard));
575 standard.index = 0;
576 while (0 == DoIoctl(VIDIOC_ENUMSTD, &standard)) {
577 if (standard.id & input.std)
578 printf("%s\n", standard.name);
579 standard.index++;
580 }
581 // EINVAL indicates the end of the enumeration, which cannot be
582 // empty unless this device falls under the USB exception.
583 if (errno != EINVAL || standard.index == 0) {
584 printf("<<< Info: VIDIOC_ENUMSTD not supported.>>>\n");
585 return false;
586 }
587 return true;
588 }
589
EnumControl(bool show_menu)590 bool V4L2Device::EnumControl(bool show_menu) {
591 v4l2_queryctrl query_ctrl;
592 memset(&query_ctrl, 0, sizeof(query_ctrl));
593 // Query V4L2_CID_CAMERA_CLASS_BASE is for V4L2_CID_EXPOSURE_AUTO_PRIORITY.
594 std::vector<std::pair<uint32_t, uint32_t>> query_ctrl_sets;
595 query_ctrl_sets.push_back(std::make_pair(V4L2_CID_BASE, V4L2_CID_LASTP1));
596 query_ctrl_sets.push_back(std::make_pair(V4L2_CID_CAMERA_CLASS_BASE,
597 V4L2_CID_TILT_SPEED));
598
599 for (int i = 0; i < query_ctrl_sets.size(); i++) {
600 for (query_ctrl.id = query_ctrl_sets[i].first;
601 query_ctrl.id < query_ctrl_sets[i].second;
602 ++query_ctrl.id) {
603 if (0 == DoIoctl(VIDIOC_QUERYCTRL, &query_ctrl)) {
604 if (query_ctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
605 printf("Control %s is disabled\n", query_ctrl.name);
606 } else {
607 printf("Control %s is enabled(%d-%d:%d)\n",
608 query_ctrl.name, query_ctrl.minimum,
609 query_ctrl.maximum, query_ctrl.default_value);
610 }
611 if (query_ctrl.type == V4L2_CTRL_TYPE_MENU && show_menu)
612 EnumControlMenu(query_ctrl);
613 } else if (errno != EINVAL) {
614 printf("<<< Info: VIDIOC_query_ctrl not supported.>>>\n");
615 return false;
616 }
617 }
618 }
619
620 for (query_ctrl.id = V4L2_CID_PRIVATE_BASE;; query_ctrl.id++) {
621 if (0 == DoIoctl(VIDIOC_QUERYCTRL, &query_ctrl)) {
622 if (query_ctrl.flags & V4L2_CTRL_FLAG_DISABLED)
623 printf("Private Control %s is disabled\n", query_ctrl.name);
624 else
625 printf("Private Control %s is enabled\n", query_ctrl.name);
626 if (query_ctrl.type == V4L2_CTRL_TYPE_MENU && show_menu)
627 EnumControlMenu(query_ctrl);
628 } else {
629 // Assume private control ids are contiguous.
630 if (errno == EINVAL)
631 break;
632 printf("<<< Info: VIDIOC_query_ctrl not supported.>>>\n");
633 return false;
634 }
635 }
636 return true;
637 }
638
EnumControlMenu(const v4l2_queryctrl & query_ctrl)639 bool V4L2Device::EnumControlMenu(const v4l2_queryctrl& query_ctrl) {
640 v4l2_querymenu query_menu;
641 memset(&query_menu, 0, sizeof(query_menu));
642 printf("\t\tMenu items:\n");
643 query_menu.id = query_ctrl.id;
644 for (query_menu.index = query_ctrl.minimum;
645 query_menu.index <= query_ctrl.maximum;
646 ++query_menu.index) {
647 if (0 == DoIoctl(VIDIOC_QUERYMENU, &query_menu)) {
648 printf("\t\t\t%s\n", query_menu.name);
649 } else {
650 printf("<<< Info: VIDIOC_QUERYMENU not supported.>>>\n");
651 return false;
652 }
653 }
654 return true;
655 }
656
EnumFormat(uint32_t * num_formats,bool show_fmt)657 bool V4L2Device::EnumFormat(uint32_t* num_formats, bool show_fmt) {
658 uint32_t i;
659 for (i = 0; ; ++i) {
660 v4l2_fmtdesc format_desc;
661 memset(&format_desc, 0, sizeof(format_desc));
662 format_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
663 format_desc.index = i;
664 if (-1 == DoIoctl(VIDIOC_ENUM_FMT, &format_desc)) {
665 if (i == 0) {
666 printf("<<< Info: VIDIOC_ENUM_FMT not supported.>>>\n");
667 return false;
668 } else {
669 break;
670 }
671 }
672 if (show_fmt)
673 printf("<<< Info supported format #%d: %s (%c%c%c%c) >>>\n",
674 i+1, format_desc.description,
675 (format_desc.pixelformat >> 0) & 0xff,
676 (format_desc.pixelformat >> 8) & 0xff,
677 (format_desc.pixelformat >> 16) & 0xff,
678 (format_desc.pixelformat >> 24) & 0xff);
679 }
680
681 if (num_formats)
682 *num_formats = i;
683 return true;
684 }
685
GetPixelFormat(uint32_t index,uint32_t * pixfmt)686 bool V4L2Device::GetPixelFormat(uint32_t index, uint32_t* pixfmt) {
687 v4l2_fmtdesc format_desc;
688 memset(&format_desc, 0, sizeof(format_desc));
689 format_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
690 format_desc.index = index;
691 if (-1 == DoIoctl(VIDIOC_ENUM_FMT, &format_desc))
692 return false;
693 if (pixfmt)
694 *pixfmt = format_desc.pixelformat;
695 return true;
696 }
697
EnumFrameSize(uint32_t pixfmt,uint32_t * num_sizes,bool show_frmsize)698 bool V4L2Device::EnumFrameSize(
699 uint32_t pixfmt, uint32_t* num_sizes, bool show_frmsize) {
700 uint32_t i;
701 for (i = 0; ; ++i) {
702 v4l2_frmsizeenum frmsize_desc;
703 memset(&frmsize_desc, 0, sizeof(frmsize_desc));
704 frmsize_desc.pixel_format = pixfmt;
705 frmsize_desc.index = i;
706 if (-1 == DoIoctl(VIDIOC_ENUM_FRAMESIZES, &frmsize_desc)) {
707 if (i == 0) {
708 printf("<<< Info: VIDIOC_ENUM_FRAMESIZES not supported.>>>\n");
709 return false;
710 } else {
711 break;
712 }
713 }
714 if (show_frmsize) {
715 switch (frmsize_desc.type) {
716 case V4L2_FRMSIZE_TYPE_DISCRETE:
717 printf("<<< Info supported discrete frame size #%d:"
718 " for pixel format(%c%c%c%c): %dx%d >>>\n", i+1,
719 (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
720 (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
721 frmsize_desc.discrete.width,
722 frmsize_desc.discrete.height);
723 break;
724 case V4L2_FRMSIZE_TYPE_CONTINUOUS:
725 printf("<<< Info supported discrete frame size #%d:"
726 " for pixel format(%c%c%c%c): "
727 " from %dx%d to %dx%d >>>\n", i+1,
728 (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
729 (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
730 frmsize_desc.stepwise.min_width,
731 frmsize_desc.stepwise.min_height,
732 frmsize_desc.stepwise.max_width,
733 frmsize_desc.stepwise.max_height);
734 break;
735 case V4L2_FRMSIZE_TYPE_STEPWISE:
736 printf("<<< Info supported discrete frame size #%d:"
737 " for pixel format(%c%c%c%c): "
738 " from %dx%d to %dx%d step(%d,%d) >>>\n", i+1,
739 (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
740 (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
741 frmsize_desc.stepwise.min_width,
742 frmsize_desc.stepwise.min_height,
743 frmsize_desc.stepwise.max_width,
744 frmsize_desc.stepwise.max_height,
745 frmsize_desc.stepwise.step_width,
746 frmsize_desc.stepwise.step_height);
747 break;
748 }
749 }
750 }
751 if (num_sizes)
752 *num_sizes = i;
753 return true;
754 }
755
GetFrameSize(uint32_t index,uint32_t pixfmt,uint32_t * width,uint32_t * height)756 bool V4L2Device::GetFrameSize(
757 uint32_t index, uint32_t pixfmt, uint32_t *width, uint32_t *height) {
758 v4l2_frmsizeenum frmsize_desc;
759 memset(&frmsize_desc, 0, sizeof(frmsize_desc));
760 frmsize_desc.pixel_format = pixfmt;
761 frmsize_desc.index = index;
762 if (-1 == DoIoctl(VIDIOC_ENUM_FRAMESIZES, &frmsize_desc)) {
763 printf("<<< Error: VIDIOC_ENUM_FRAMESIZES not supported.>>>\n");
764 return false;
765 }
766 if (frmsize_desc.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
767 printf("<<< Error: frame size type %d not supported.>>>\n",
768 frmsize_desc.type);
769 return false;
770 }
771
772 if (width && height) {
773 *width = frmsize_desc.discrete.width;
774 *height = frmsize_desc.discrete.height;
775 }
776 return true;
777 }
778
EnumFrameInterval(uint32_t pixfmt,uint32_t width,uint32_t height,uint32_t * num_intervals,bool show_intervals)779 bool V4L2Device::EnumFrameInterval(
780 uint32_t pixfmt, uint32_t width, uint32_t height, uint32_t* num_intervals,
781 bool show_intervals) {
782 uint32_t i;
783 for (i = 0; ; ++i) {
784 v4l2_frmivalenum frm_interval;
785 memset(&frm_interval, 0, sizeof(frm_interval));
786 frm_interval.pixel_format = pixfmt;
787 frm_interval.width = width;
788 frm_interval.height = height;
789 frm_interval.index = i;
790 if (-1 == DoIoctl(VIDIOC_ENUM_FRAMEINTERVALS, &frm_interval)) {
791 if (i == 0) {
792 printf("<<< Error: VIDIOC_ENUM_FRAMEINTERVALS not supported.>>>\n");
793 return false;
794 } else {
795 break;
796 }
797 }
798 if (show_intervals) {
799 switch(frm_interval.type) {
800 case V4L2_FRMIVAL_TYPE_DISCRETE:
801 printf("<<< Info supported discrete frame interval #%d:"
802 " for pixel format(%c%c%c%c): %dx%d: %d/%d >>>\n", i+1,
803 (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
804 (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
805 width, height, frm_interval.discrete.numerator,
806 frm_interval.discrete.denominator);
807 break;
808 case V4L2_FRMIVAL_TYPE_CONTINUOUS:
809 printf("<<< Info supported continuous frame interval #%d:"
810 " for pixel format(%c%c%c%c): %dx%d:"
811 " from %d/%d to %d/%d >>>\n", i+1,
812 (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
813 (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
814 width, height,
815 frm_interval.stepwise.min.numerator,
816 frm_interval.stepwise.min.denominator,
817 frm_interval.stepwise.max.numerator,
818 frm_interval.stepwise.max.denominator);
819 break;
820 case V4L2_FRMIVAL_TYPE_STEPWISE:
821 printf("<<< Info supported stepwise frame interval #%d:"
822 " for pixel format(%c%c%c%c): %dx%d:"
823 " from %d/%d to %d/%d step(%d,%d) >>>\n", i+1,
824 (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
825 (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
826 width, height,
827 frm_interval.stepwise.min.numerator,
828 frm_interval.stepwise.min.denominator,
829 frm_interval.stepwise.max.numerator,
830 frm_interval.stepwise.max.denominator,
831 frm_interval.stepwise.step.numerator,
832 frm_interval.stepwise.step.denominator);
833 break;
834 default:
835 printf("<<< Error: unsupported frame interval type %d: for index %d"
836 " pixel format(%c%c%c%c): %dx%d >>>\n", frm_interval.type,
837 i+1, (pixfmt >> 0) & 0xff, (pixfmt >> 8) & 0xff,
838 (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff, width, height);
839 return false;
840 }
841 }
842 }
843 if (num_intervals)
844 *num_intervals = i;
845 return true;
846 }
847
GetFrameInterval(uint32_t index,uint32_t pixfmt,uint32_t width,uint32_t height,float * frame_rate)848 bool V4L2Device::GetFrameInterval(
849 uint32_t index, uint32_t pixfmt, uint32_t width, uint32_t height,
850 float* frame_rate) {
851 v4l2_frmivalenum frm_interval;
852 memset(&frm_interval, 0, sizeof(frm_interval));
853 frm_interval.pixel_format = pixfmt;
854 frm_interval.width = width;
855 frm_interval.height = height;
856 frm_interval.index = index;
857 if (-1 == DoIoctl(VIDIOC_ENUM_FRAMEINTERVALS, &frm_interval)) {
858 printf("<<< Error: VIDIOC_ENUM_FRAMEINTERVALS not supported.>>>\n");
859 return false;
860 }
861 if (frm_interval.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
862 printf("<<< Error: frame interval type %d not supported.>>>\n",
863 frm_interval.type);
864 return false;
865 }
866
867 if (frame_rate) {
868 *frame_rate = static_cast<float>(frm_interval.discrete.denominator) /
869 frm_interval.discrete.numerator;
870 }
871 return true;
872 }
873
QueryControl(uint32_t id,v4l2_queryctrl * ctrl)874 bool V4L2Device::QueryControl(uint32_t id, v4l2_queryctrl* ctrl) {
875 memset(ctrl, 0, sizeof(*ctrl));
876 ctrl->id = id;
877 if (-1 == DoIoctl(VIDIOC_QUERYCTRL, ctrl)) {
878 if (errno != EINVAL) return false;
879 printf("%d is not supported\n", id);
880 return false;
881 }
882 if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED) {
883 printf("%d is not supported\n", id);
884 return false;
885 }
886 return true;
887 }
888
SetControl(uint32_t id,int32_t value)889 bool V4L2Device::SetControl(uint32_t id, int32_t value) {
890 v4l2_control control;
891 control.id = id;
892 control.value = value;
893 if (-1 == DoIoctl(VIDIOC_S_CTRL, &control)) {
894 printf("<<< Error: VIDIOC_S_CTRL failed. %d>>>\n", errno);
895 return false;
896 }
897 return true;
898 }
899
GetCropCap(v4l2_cropcap * cropcap)900 bool V4L2Device::GetCropCap(v4l2_cropcap* cropcap) {
901 if (-1 == DoIoctl(VIDIOC_CROPCAP, cropcap)) {
902 printf("<<< Warning: VIDIOC_CROPCAP not supported.>>>\n");
903 return false;
904 }
905 return true;
906 }
907
GetCrop(v4l2_crop * crop)908 bool V4L2Device::GetCrop(v4l2_crop* crop) {
909 if (-1 == DoIoctl(VIDIOC_G_CROP, crop)) {
910 printf("<<< Warning: VIDIOC_G_CROP not supported.>>>\n");
911 return false;
912 }
913 printf("crop: %d, %d, %d, %d\n",
914 crop->c.left, crop->c.top,
915 crop->c.width, crop->c.height);
916 return true;
917 }
918
SetCrop(v4l2_crop * crop)919 bool V4L2Device::SetCrop(v4l2_crop* crop) {
920 if (-1 == DoIoctl(VIDIOC_S_CROP, crop)) {
921 printf("<<< Warning: VIDIOC_S_CROP not supported.>>>\n");
922 return false;
923 }
924 return true;
925 }
926
ProbeCaps(v4l2_capability * cap,bool show_caps)927 bool V4L2Device::ProbeCaps(v4l2_capability* cap, bool show_caps) {
928 if (-1 == DoIoctl(VIDIOC_QUERYCAP, cap)) {
929 printf("<<< Error: VIDIOC_QUERYCAP on %s.>>>\n", dev_name_);
930 return false;
931 }
932
933 if (show_caps) {
934 if (cap->capabilities & V4L2_CAP_VIDEO_CAPTURE)
935 printf("<<< Info: %s support video capture interface.>>>\n", dev_name_);
936 if (cap->capabilities & V4L2_CAP_VIDEO_OUTPUT)
937 printf("<<< Info: %s support video output interface.>>>\n", dev_name_);
938 if (cap->capabilities & V4L2_CAP_VIDEO_OVERLAY)
939 printf("<<< Info: %s support video overlay interface.>>>\n", dev_name_);
940 if (cap->capabilities & V4L2_CAP_AUDIO)
941 printf("<<< Info: %s support audio i/o interface.>>>\n", dev_name_);
942
943 if (cap->capabilities & V4L2_CAP_STREAMING)
944 printf("<<< Info: %s support streaming i/o interface.>>>\n", dev_name_);
945 }
946
947 return true;
948 }
949
MapFourCC(const char * fourcc)950 uint32_t V4L2Device::MapFourCC(const char* fourcc) {
951 return v4l2_fourcc(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
952 }
953
GetParam(v4l2_streamparm * param)954 bool V4L2Device::GetParam(v4l2_streamparm* param) {
955 param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
956 if (-1 == DoIoctl(VIDIOC_G_PARM, param)) {
957 printf("<<< Warning: VIDIOC_G_PARM not supported.>>>\n");
958 return false;
959 }
960
961 return true;
962 }
963
SetParam(v4l2_streamparm * param)964 bool V4L2Device::SetParam(v4l2_streamparm* param) {
965 if (-1 == DoIoctl(VIDIOC_S_PARM, param)) {
966 printf("<<< Warning: VIDIOC_S_PARM not supported.>>>\n");
967 return false;
968 }
969 return true;
970 }
971
SetFrameRate(float fps)972 bool V4L2Device::SetFrameRate(float fps) {
973 v4l2_streamparm param;
974 if (!GetParam(¶m))
975 return false;
976
977 const int kFrameRatePrecision = 10000;
978 param.parm.capture.timeperframe.numerator = kFrameRatePrecision;
979 param.parm.capture.timeperframe.denominator = fps * kFrameRatePrecision;
980 return SetParam(¶m);
981 }
982
GetFrameRate()983 float V4L2Device::GetFrameRate() {
984 v4l2_streamparm param;
985 if (!GetParam(¶m))
986 return -1;
987 return static_cast<float>(param.parm.capture.timeperframe.denominator) /
988 param.parm.capture.timeperframe.numerator;
989 }
990
GetV4L2Format(v4l2_format * format)991 bool V4L2Device::GetV4L2Format(v4l2_format* format) {
992 memset(format, 0, sizeof(v4l2_format));
993 format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
994
995 if (-1 == DoIoctl(VIDIOC_G_FMT, format)) {
996 printf("<<< Error: VIDIOC_G_FMT on %s.>>>\n", dev_name_);
997 return false;
998 }
999 return true;
1000 }
1001
Now()1002 uint64_t V4L2Device::Now() {
1003 struct timespec ts;
1004 int res = clock_gettime(CLOCK_MONOTONIC, &ts);
1005 CHECK(res == 0);
1006 return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
1007 }
1008