• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&param))
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(&param))
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(&param);
981 }
982 
GetFrameRate()983 float V4L2Device::GetFrameRate() {
984   v4l2_streamparm param;
985   if (!GetParam(&param))
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