1 /*
2  * vidinput_v4l2.cxx
3  *
4  * Classes to support streaming video input (grabbing) and output.
5  *
6  * Portable Windows Library
7  *
8  * Copyright (c) 1998-2000 Equivalence Pty. Ltd.
9  * Copyright (c) 2003 March Networks
10  *
11  * The contents of this file are subject to the Mozilla Public License
12  * Version 1.0 (the "License"); you may not use this file except in
13  * compliance with the License. You may obtain a copy of the License at
14  * http://www.mozilla.org/MPL/
15  *
16  * Software distributed under the License is distributed on an "AS IS"
17  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
18  * the License for the specific language governing rights and limitations
19  * under the License.
20  *
21  * The Original Code is Portable Windows Library.
22  *
23  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24  *
25  * First V4L2 capture code written by March Networks
26  * (http://www.marchnetworks.com)
27  *
28  * This code is based on the Video4Linux 1 code.
29  *
30  * Contributor(s): Guilhem Tardy (gtardy@salyens.com)
31  *  Nicola Orru' <nigu@itadinanta.it>
32  *
33  * $Revision: 29220 $
34  * $Author: ededu $
35  * $Date: 2013-03-07 12:22:08 -0600 (Thu, 07 Mar 2013) $
36  */
37 
38 #pragma implementation "vidinput_v4l2.h"
39 
40 #include "vidinput_v4l2.h"
41 #include <sys/utsname.h>
42 
43 PCREATE_VIDINPUT_PLUGIN(V4L2);
44 #define CLEAR(x) memset (&(x), 0, sizeof (x))
45 
46 #include "vidinput_names.h"
47 
48 #ifdef HAS_LIBV4L
49 #include <libv4l2.h>
50 #else
51 #define v4l2_fd_open(fd, flags) (fd)
52 #define v4l2_open open
53 #define v4l2_close close
54 #define v4l2_ioctl ioctl
55 #define v4l2_read read
56 #define v4l2_mmap mmap
57 #define v4l2_munmap munmap
58 #endif
59 
60 class V4L2Names : public V4LXNames
61 {
62 
63   PCLASSINFO(V4L2Names, V4LXNames);
64 
65 public:
66 
V4L2Names()67   V4L2Names() { kernelVersion=KUNKNOWN; };
68 
69   virtual void Update ();
70 
71 protected:
72 
73   virtual PString BuildUserFriendly(PString devname);
74 
75   enum KernelVersionEnum {
76     K2_4,
77     K2_6,
78     KUNKNOWN,
79   } kernelVersion;
80 
81 };
82 
83 
84 PMutex creationMutex;
85 static
GetNames()86 V4L2Names & GetNames()
87 {
88   PWaitAndSignal m(creationMutex);
89   static V4L2Names names;
90   names.Update();
91   return names;
92 }
93 
94 ///////////////////////////////////////////////////////////////////////////////
95 // PVideoInputDevice_V4L2
96 
PVideoInputDevice_V4L2()97 PVideoInputDevice_V4L2::PVideoInputDevice_V4L2()
98 {
99   videoFd = -1;
100   canRead = PFalse;
101   canStream = PFalse;
102   canSelect = PFalse;
103   canSetFrameRate = PFalse;
104   isOpen = PFalse;
105   isMapped = PFalse;
106   isStreaming = PFalse;
107   started = PFalse;
108   areBuffersQueued = PFalse;
109   videoBufferCount = 0;
110   currentvideoBuffer = 0;
111   frameBytes = 0;
112   CLEAR(videoCapability);
113   CLEAR(videoStreamParm);
114   CLEAR(videoBuffer);
115 }
116 
~PVideoInputDevice_V4L2()117 PVideoInputDevice_V4L2::~PVideoInputDevice_V4L2()
118 {
119   Close();
120 }
121 
122 
123 #ifndef V4L2_PIX_FMT_H263
124 #define V4L2_PIX_FMT_H263       v4l2_fourcc('H','2','6','3')
125 #endif
126 
127 
128 static struct {
129   const char * colourFormat;
130 #ifdef SOLARIS
131   uint32_t code;
132 #else
133   __u32 code;
134 #endif
135 } colourFormatTab[] = {
136     { "Grey", V4L2_PIX_FMT_GREY },   //Entries in this table correspond
137     { "RGB32", V4L2_PIX_FMT_RGB32 }, //(line by line) to those in the
138     { "BGR32", V4L2_PIX_FMT_BGR32 }, //PVideoDevice ColourFormat table.
139     { "RGB24", V4L2_PIX_FMT_RGB24 },
140     { "BGR24", V4L2_PIX_FMT_BGR24 },
141     { "RGB565", V4L2_PIX_FMT_RGB565 },
142     { "RGB555", V4L2_PIX_FMT_RGB555 },
143     { "YUV411", V4L2_PIX_FMT_Y41P },
144     { "YUV411P", V4L2_PIX_FMT_YUV411P },
145     { "YUV420", V4L2_PIX_FMT_NV21 },
146     { "YUV420P", V4L2_PIX_FMT_YUV420 },
147     { "YUV422", V4L2_PIX_FMT_YUYV },   /* Note: YUV422 is for compatibility */
148     { "YUV422P", V4L2_PIX_FMT_YUV422P },
149     { "YUY2", V4L2_PIX_FMT_YUYV },
150     { "JPEG", V4L2_PIX_FMT_JPEG },
151     { "H263", V4L2_PIX_FMT_H263 },
152     { "SBGGR8", V4L2_PIX_FMT_SBGGR8 },
153     { "MJPEG", V4L2_PIX_FMT_MJPEG},
154     { "UYVY422", V4L2_PIX_FMT_UYVY}
155 };
156 
157 
Open(const PString & devName,PBoolean)158 PBoolean PVideoInputDevice_V4L2::Open(const PString & devName, PBoolean /* startImmediate */)
159 {
160   if (isOpen) {
161     PTRACE(1,"PVidInDev\tClosing " << deviceName << " already open on this instance, fd:" << videoFd);
162     Close();
163   }
164 
165   PString name = GetNames().GetDeviceName(devName);
166   deviceName=name;
167 
168   PTRACE(5,"PVidInDev\tOpen()\tdevName:" << name << "  videoFd:" << videoFd);
169   videoFd = ::v4l2_open((const char *)name, O_RDWR);
170   if (videoFd < 0) {
171     PTRACE(1,"PVidInDev\topen failed : " << ::strerror(errno));
172     return isOpen;
173   }
174 
175   isOpen = PTrue;
176 
177   PTRACE(5,"PVidInDev\tNew handle for " << deviceName << ": fd=" << videoFd);
178 
179   // Don't share the camera device with subprocesses - they could cause
180   // EBUSY errors on VIDIOC_STREAMON if the parent tries to close and reopen
181   // the camera while the child is still running.
182   ::fcntl(videoFd, F_SETFD, FD_CLOEXEC);
183 
184   /* Note the v4l2_xxx functions are designed so that if they get passed an
185      unknown fd, the will behave exactly as their regular xxx counterparts, so
186      if v4l2_fd_open fails, we continue as normal (missing the libv4l2 custom
187      cam format to normal formats conversion). Chances are big we will still
188      fail then though, as normally v4l2_fd_open only fails if the device is not
189      a v4l2 device. */
190   int libv4l2_fd = v4l2_fd_open(videoFd, 0);
191   if (libv4l2_fd != -1)
192     videoFd = libv4l2_fd;
193 
194   // get the device capabilities
195   if (v4l2_ioctl(videoFd, VIDIOC_QUERYCAP, &videoCapability) < 0) {
196     PTRACE(1,"PVidInDev\tQUERYCAP failed : " << ::strerror(errno));
197     Close();
198     return isOpen;
199   }
200 
201   canRead = videoCapability.capabilities & V4L2_CAP_READWRITE;
202   canStream = videoCapability.capabilities & V4L2_CAP_STREAMING;
203   canSelect = videoCapability.capabilities & V4L2_CAP_ASYNCIO;
204 
205   // set height and width
206   frameHeight = QCIFHeight;
207   frameWidth  = QCIFWidth;
208 
209 
210   // get the capture parameters
211   videoStreamParm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
212   if (v4l2_ioctl(videoFd, VIDIOC_G_PARM, &videoStreamParm) < 0)  {
213 
214     PTRACE(3,"PVidInDev\tG_PARM failed : " << ::strerror(errno));
215     canSetFrameRate = PFalse;
216 
217   } else {
218 
219     canSetFrameRate = videoStreamParm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME;
220     if (canSetFrameRate) {
221       if (videoStreamParm.parm.capture.timeperframe.numerator == 0) {
222         PTRACE(1,"PVidInDev\tDriver/webcam bug: numerator is zero and denominator is " << videoStreamParm.parm.capture.timeperframe.denominator << ", I assume it cannot set frame rate");
223         canSetFrameRate = PFalse;
224       } else
225         PVideoDevice::SetFrameRate (videoStreamParm.parm.capture.timeperframe.denominator / videoStreamParm.parm.capture.timeperframe.numerator);
226     }
227   }
228 
229   SetVideoFormat(videoFormat);
230   SetColourFormat(colourFormat);
231 
232   return PTrue;
233 }
234 
235 
IsOpen()236 PBoolean PVideoInputDevice_V4L2::IsOpen()
237 {
238   return isOpen;
239 }
240 
241 
Close()242 PBoolean PVideoInputDevice_V4L2::Close()
243 {
244   PTRACE(1,"PVidInDev\tClose()\tvideoFd:" << videoFd << "  started:" << started);
245   if (!IsOpen())
246     return PTrue;
247 
248   if (started)
249     Stop();
250 
251   if (v4l2_close(videoFd) < 0) {
252     PTRACE(2, "PVidInDev\tERROR errno = " << ::strerror(errno) << "(" << errno << ")");
253   }
254 
255   isOpen = PFalse;
256   started = PFalse;
257   areBuffersQueued = PFalse;
258   isStreaming = PFalse;
259   videoFd = -1;
260   canRead = PFalse;
261   canStream = PFalse;
262   canSelect = PFalse;
263   canSetFrameRate = PFalse;
264   isMapped = PFalse;
265   videoBufferCount = 0;
266   currentvideoBuffer = 0;
267   frameBytes = 0;
268 
269   CLEAR(videoBuffer);
270   CLEAR(videoCapability);
271   CLEAR(videoStreamParm);
272 
273   PTRACE(1,"PVidInDev\tClose()\tvideoFd:" << videoFd << "  started:" << started);
274   return PTrue;
275 }
276 
277 
Start()278 PBoolean PVideoInputDevice_V4L2::Start()
279 {
280   PTRACE(8, "PVidInDev\tStarting " << deviceName);
281 
282   if (started == PTrue) {
283     PTRACE(2, "PVidInDev\tVideo Input Device already started");
284     return started;
285   }
286 
287   // automatically set mapping
288   if (!SetMapping()) {
289     ClearMapping();
290     canStream = PFalse; // don't try again
291     return started;
292   }
293 
294   /* Queue all buffers */
295   if (!QueueBuffers()) {
296     PTRACE(2, "PVidInDev\tCould not QueueBuffers for Video Input Device!");
297     return started;
298   }
299 
300   /* Start streaming */
301   if (!StartStreaming()) {
302     PTRACE(2, "PVidInDev\tCould not StartStreaming for Video Input Device!");
303     return started;
304   }
305 
306   started = PTrue;
307 
308   return started;
309 }
310 
311 
Stop()312 PBoolean PVideoInputDevice_V4L2::Stop()
313 {
314   if (started) {
315     PTRACE(6,"PVidInDev\tstop streaming, fd=" << videoFd);
316 
317     StopStreaming();
318     ClearMapping();
319 
320     started = PFalse;
321 
322     // no need to dequeue filled buffers, as this is handled by V4L2 at the next VIDIOC_STREAMON
323   }
324 
325   return PTrue;
326 }
327 
328 
IsCapturing()329 PBoolean PVideoInputDevice_V4L2::IsCapturing()
330 {
331   return started;
332 }
333 
GetInputDeviceNames()334 PStringList PVideoInputDevice_V4L2::GetInputDeviceNames()
335 {
336   return GetNames().GetInputDeviceNames();
337 }
338 
339 
SetVideoFormat(VideoFormat newFormat)340 PBoolean PVideoInputDevice_V4L2::SetVideoFormat(VideoFormat newFormat)
341 {
342   PTRACE(8,"PVidInDev\tSet video format " << newFormat);
343 
344   if (newFormat == Auto) {
345     PBoolean videoStandardSetCorrectly = PFalse;
346     if (PTrue == (videoStandardSetCorrectly = SetVideoFormat(PAL))) {
347       return videoStandardSetCorrectly;
348     }
349     if (PTrue == (videoStandardSetCorrectly = SetVideoFormat(NTSC))) {
350       return videoStandardSetCorrectly;
351     }
352     if (PTrue == (videoStandardSetCorrectly = SetVideoFormat(SECAM))) {
353       return videoStandardSetCorrectly;
354     }
355     return videoStandardSetCorrectly;
356   }
357 
358   struct {
359 #ifdef SOLARIS
360     uint32_t code;
361 #else
362     __u32 code;
363 #endif
364     const char * name;
365   } static const fmt[3] = { {V4L2_STD_PAL, "PAL"},
366       {V4L2_STD_NTSC, "NTSC"},
367       {V4L2_STD_SECAM, "SECAM"} };
368 
369 #ifdef SOLARIS
370     uint32_t carg;
371 #else
372     __u32 carg;
373 #endif
374   carg = V4L2_STD_UNKNOWN;
375 
376   if (ioctl(videoFd, VIDIOC_G_STD, &carg) < 0) {
377     PTRACE(3, "PVidInDev\tG_STD failed for fd=" << videoFd << " with error: " << ::strerror(errno));
378     // Assume that if G_STD is not available, that the device still works correctly.
379     return PTrue;
380   } else {
381     PTRACE(5, "PVidInDev\tG_STD succeeded for " << newFormat << ", carg = " << carg);
382   }
383 
384   carg = fmt[newFormat].code;
385 
386   if (v4l2_ioctl(videoFd, VIDIOC_S_STD, &carg) < 0) {
387     PTRACE(2, "PVidInDev\tS_STD failed for " << newFormat << " with error: " << ::strerror(errno));
388     return PFalse;
389   } else {
390     PTRACE(5, "PVidInDev\tS_STD succeeded for " << newFormat << ", carg = " << carg);
391   }
392 
393   if (!PVideoDevice::SetVideoFormat(newFormat)) {
394     PTRACE(1,"PVideoDevice::SetVideoFormat failed for format " << newFormat);
395     return PFalse;
396   }
397 
398   return PTrue;
399 }
400 
401 
GetNumChannels()402 int PVideoInputDevice_V4L2::GetNumChannels()
403 {
404   PTRACE(8,"PVidInDev\tGet number of channels");
405   // if opened, return the capability value, else 1 as in videoio.cxx
406   if (IsOpen ()) {
407 
408     struct v4l2_input videoEnumInput;
409     videoEnumInput.index = 0;
410     while (v4l2_ioctl(videoFd, VIDIOC_ENUMINPUT, &videoEnumInput) >= 0)
411       videoEnumInput.index++;
412 
413     return videoEnumInput.index;
414   }
415   else
416     return 1;
417 }
418 
419 
SetChannel(int newChannel)420 PBoolean PVideoInputDevice_V4L2::SetChannel(int newChannel)
421 {
422   PTRACE(8,"PVidInDev\tSet channel #" << newChannel);
423 
424   if (!PVideoDevice::SetChannel(newChannel)) {
425     PTRACE(1,"PVideoDevice::SetChannel failed for channel " << newChannel);
426     return PFalse;
427   }
428 
429   // set the channel
430   if (v4l2_ioctl(videoFd, VIDIOC_S_INPUT, &channelNumber) < 0) {
431     PTRACE(1,"VideoInputDevice\tS_INPUT failed : " << ::strerror(errno));
432     return PFalse;
433   }
434 
435   PTRACE(6,"PVidInDev\tset channel " << newChannel << ", fd=" << videoFd);
436 
437   return PTrue;
438 }
439 
440 
SetVideoChannelFormat(int newChannel,VideoFormat videoFormat)441 PBoolean PVideoInputDevice_V4L2::SetVideoChannelFormat (int newChannel, VideoFormat videoFormat)
442 {
443   PTRACE(8,"PVidInDev\tSet channel #" << newChannel << " format \"" << videoFormat << "\"");
444 
445   if (!SetChannel(newChannel) ||
446       !SetVideoFormat(videoFormat))
447     return PFalse;
448 
449   return PTrue;
450 }
451 
452 
SetColourFormat(const PString & newFormat)453 PBoolean PVideoInputDevice_V4L2::SetColourFormat(const PString & newFormat)
454 {
455   PTRACE(8,"PVidInDev\tSet colour format \"" << newFormat << "\"");
456 
457   PBoolean colorFormatSet = PFalse;
458   PINDEX currentColourFormatIndex, colourFormatIndex = 0;
459   while (newFormat != colourFormatTab[colourFormatIndex].colourFormat) {
460     colourFormatIndex++;
461     PTRACE(9,"PVidInDev\tColourformat did not match" << colourFormatTab[colourFormatIndex].colourFormat);
462     if (colourFormatIndex >= PARRAYSIZE(colourFormatTab))
463       return colorFormatSet;
464   }
465 
466   if (!PVideoDevice::SetColourFormat(newFormat)) {
467     PTRACE(3,"PVidInDev\tSetColourFormat failed for colour format " << newFormat);
468     return colorFormatSet;
469   }
470 
471   PBoolean resume = started;
472   if (started == PTrue) {
473     Stop();
474   }
475   if (isMapped == PTrue) {
476     ClearMapping();
477   }
478 
479   struct v4l2_format videoFormat;
480   CLEAR(videoFormat);
481   videoFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
482 
483   // get the frame rate so we can preserve it throughout the S_FMT call
484   struct v4l2_streamparm streamParm;
485   CLEAR(streamParm);
486   unsigned int fi_n = 0, fi_d = 0;
487   streamParm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
488   if (v4l2_ioctl(videoFd, VIDIOC_G_PARM, &streamParm) == 0 &&
489         (streamParm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) {
490     fi_n = streamParm.parm.capture.timeperframe.numerator;
491     fi_d = streamParm.parm.capture.timeperframe.denominator;
492     PTRACE(8,"PVidInDev\tG_PARM succeeded (preserving frame rate at " << fi_n << "/" << fi_d << ")");
493   } else {
494     PTRACE(1,"PVidInDev\tG_PARM failed (preserving frame rate may not work) : " << ::strerror(errno));
495   }
496 
497   // get the colour format
498   if (v4l2_ioctl(videoFd, VIDIOC_G_FMT, &videoFormat) < 0) {
499     PTRACE(1,"PVidInDev\tG_FMT failed : " << ::strerror(errno));
500     return colorFormatSet;
501   } else {
502     PTRACE(8,"PVidInDev\tG_FMT succeeded");
503   }
504 
505   // update colourFormat to current value so in case of VIDIOC_S_FMT failure will have corect one
506   for (currentColourFormatIndex = 0; currentColourFormatIndex < PARRAYSIZE(colourFormatTab); currentColourFormatIndex++) {
507     if (videoFormat.fmt.pix.pixelformat == colourFormatTab[currentColourFormatIndex].code)
508     {
509       colourFormat = colourFormatTab[currentColourFormatIndex].colourFormat;
510       break;
511     }
512   }
513 
514   videoFormat.fmt.pix.pixelformat = colourFormatTab[colourFormatIndex].code;
515 
516   // set the colour format
517   if (v4l2_ioctl(videoFd, VIDIOC_S_FMT, &videoFormat) < 0) {
518     PTRACE(1,"PVidInDev\tS_FMT failed : " << ::strerror(errno));
519     PTRACE(1,"PVidInDev\tused code of " << videoFormat.fmt.pix.pixelformat << " for palette: " << colourFormatTab[colourFormatIndex].colourFormat);
520     return colorFormatSet;
521   } else {
522     PTRACE(8,"PVidInDev\tS_FMT succeeded for palette: " << colourFormatTab[colourFormatIndex].colourFormat);
523   }
524 
525   // get the colour format again to be careful about broken drivers
526   if (v4l2_ioctl(videoFd, VIDIOC_G_FMT, &videoFormat) < 0) {
527     PTRACE(1,"PVidInDev\tG_FMT failed : " << ::strerror(errno));
528     return colorFormatSet;
529   } else {
530     PTRACE(8,"PVidInDev\tG_FMT succeeded");
531   }
532 
533   if (videoFormat.fmt.pix.pixelformat != colourFormatTab[colourFormatIndex].code) {
534     PTRACE(3,"PVidInDev\tcolour format mismatch.");
535     return colorFormatSet;
536   } else {
537     colourFormat = newFormat;
538     PTRACE(8,"PVidInDev\tcolour format matches.");
539   }
540 
541   // reset the frame rate because it may have been overridden by the call to S_FMT
542   if (fi_n == 0 || fi_d == 0 || v4l2_ioctl(videoFd, VIDIOC_S_PARM, &streamParm) < 0) {
543     PTRACE(3,"PVidInDev\tunable to reset frame rate.");
544   } else if (streamParm.parm.capture.timeperframe.numerator != fi_n ||
545              streamParm.parm.capture.timeperframe.denominator  != fi_d) {
546     PTRACE(3, "PVidInDev\tnew frame interval (" << streamParm.parm.capture.timeperframe.numerator
547               << "/" << streamParm.parm.capture.timeperframe.denominator
548               << ") differs from what was requested (" << fi_n << "/" << fi_d << ").");
549   } else {
550     PTRACE(8,"PVidInDev\tS_PARM succeeded (preserving frame rate at " << fi_n << "/" << fi_d << ")");
551   }
552 
553   frameBytes = videoFormat.fmt.pix.sizeimage;
554 
555   PTRACE(6,"PVidInDev\tset colour format \"" << newFormat << "\" set for " << deviceName << ", fd=" << videoFd);
556 
557   colorFormatSet = PTrue;
558   if (resume) {
559     colorFormatSet = SetMapping();
560     if (colorFormatSet) {
561       colorFormatSet = Start();
562     }
563   }
564 
565   return colorFormatSet;
566 }
567 
568 
SetFrameRate(unsigned rate)569 PBoolean PVideoInputDevice_V4L2::SetFrameRate(unsigned rate)
570 {
571   if (!PVideoDevice::SetFrameRate(rate)) {
572     PTRACE(3,"PVidInDev\tSetFrameRate failed for rate " << rate);
573     return PTrue; // Ignore
574   }
575 
576   if (canSetFrameRate) {
577     videoStreamParm.parm.capture.timeperframe.numerator = 1;
578     videoStreamParm.parm.capture.timeperframe.denominator = (rate ? rate : 1);
579 
580     // set the stream parameters
581     if (v4l2_ioctl(videoFd, VIDIOC_S_PARM, &videoStreamParm) < 0)  {
582       PTRACE(1,"PVidInDev\tS_PARM failed : "<< ::strerror(errno));
583       return PTrue;
584     }
585 
586     PTRACE(6,"PVidInDev\tset frame rate " << rate << "fps, fd=" << videoFd);
587   }
588 
589   return PTrue;
590 }
591 
592 
GetFrameSizeLimits(unsigned & minWidth,unsigned & minHeight,unsigned & maxWidth,unsigned & maxHeight)593 PBoolean PVideoInputDevice_V4L2::GetFrameSizeLimits(unsigned & minWidth,
594                                                 unsigned & minHeight,
595                                                 unsigned & maxWidth,
596                                                 unsigned & maxHeight)
597 {
598   minWidth=0;
599   maxWidth=65535;
600   minHeight=0;
601   maxHeight=65535;
602 
603   // Before 2.6.19 there is no official way to enumerate frame sizes
604   // in V4L2, but we can use VIDIOC_TRY_FMT to find the largest supported
605   // size. This is roughly what the kernel V4L1 compatibility layer does.
606 
607   struct v4l2_format fmt;
608   fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
609   if (v4l2_ioctl(videoFd, VIDIOC_G_FMT, &fmt) < 0) {
610     return PFalse;
611   }
612 
613   fmt.fmt.pix.width = fmt.fmt.pix.height = 10000;
614   if (v4l2_ioctl(videoFd, VIDIOC_TRY_FMT, &fmt) < 0) {
615     return PFalse;
616   }
617   maxWidth = fmt.fmt.pix.width;
618   maxHeight = fmt.fmt.pix.height;
619 
620   PTRACE(8,"PVidInDev\tFrame size limits: [" << minWidth << "," << maxWidth << "]" << "x"
621                                       << "[" << minWidth << "," << maxWidth << "]");
622 
623   return PTrue;
624 }
625 
SetFrameSize(unsigned width,unsigned height)626 PBoolean PVideoInputDevice_V4L2::SetFrameSize(unsigned width, unsigned height) {
627   PBoolean resume = started;
628 
629   if (started == PTrue) {
630     Stop();
631   }
632 
633   unsigned requestedWidth = width;
634   unsigned requestedHeight = height;
635 
636   if (!VerifyHardwareFrameSize(requestedWidth, requestedHeight)) {
637     PTRACE(5, "PVidInDev\tVerifyHardwareFrameSize failed for size " << width << "x" << height);
638     PTRACE(4, "PVidInDev\tCurrent resolution (" << GetFrameWidth() << "x" << GetFrameHeight() << ")");
639     return PFalse;
640   }
641 
642   if ((requestedWidth != width) || (requestedHeight != height)){
643     PTRACE(4, "PVidInDev\t" << width << "x" << height << " requested but "
644                             << requestedWidth << "x" << requestedHeight << " returned");
645     return PFalse;
646   } else {
647     PTRACE(5, "PVidInDev\tVerifyHardwareFrameSize succeeded for size " << width << "x" << height);
648     PTRACE(4, "PVidInDev\tCurrent resolution (" << GetFrameWidth() << "x" << GetFrameHeight() << ")");
649   }
650 
651   if(!PVideoDevice::SetFrameSize(requestedWidth, requestedHeight)){
652     return PFalse;
653   }
654 
655   if (resume) {
656     if (PFalse == SetMapping()) {
657       return PFalse;
658     }
659     if (PFalse == Start()) {
660       return PFalse;
661     }
662   }
663 
664   return PTrue;
665 }
666 
SetNearestFrameSize(unsigned width,unsigned height)667 PBoolean PVideoInputDevice_V4L2::SetNearestFrameSize(unsigned width, unsigned height) {
668   PBoolean resume = started;
669 
670   if (started == PTrue) {
671     Stop();
672   }
673 
674   unsigned requestedWidth = width;
675   unsigned requestedHeight = height;
676 
677   if (!VerifyHardwareFrameSize(requestedWidth, requestedHeight)) {
678     PTRACE(5, "PVidInDev\tVerifyHardwareFrameSize failed for size " << width << "x" << height);
679     PTRACE(4, "PVidInDev\tCurrent resolution (" << GetFrameWidth() << "x" << GetFrameHeight() << ")");
680     return PFalse;
681   }
682 
683   if ((requestedWidth != width) || (requestedHeight != height)){
684     PTRACE(4, "PVidInDev\t" << width << "x" << height << " requested but "
685                             << requestedWidth << "x" << requestedHeight << " returned");
686   }
687 
688   if(!PVideoDevice::SetFrameSize(requestedWidth, requestedHeight)){
689     return PFalse;
690   }
691 
692   if (resume) {
693     if (PFalse == SetMapping()) {
694       return PFalse;
695     }
696     if (PFalse == Start()) {
697       return PFalse;
698     }
699   }
700 
701   return PTrue;
702 }
703 
704 
GetMaxFrameBytes()705 PINDEX PVideoInputDevice_V4L2::GetMaxFrameBytes()
706 {
707   return GetMaxFrameBytesConverted(frameBytes);
708 }
709 
710 
SetMapping()711 PBoolean PVideoInputDevice_V4L2::SetMapping()
712 {
713   if (isMapped) {
714     PTRACE(2, "PVidInDev\tVideo buffers already mapped! Do ClearMapping() first!");
715     ClearMapping();
716     if(isMapped)
717       return PFalse;
718   }
719 
720   if (!canStream)
721     return isMapped;
722 
723   struct v4l2_requestbuffers reqbuf;
724   CLEAR(reqbuf);
725   reqbuf.count = NUM_VIDBUF;
726   reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
727   reqbuf.memory = V4L2_MEMORY_MMAP;
728 
729   if (v4l2_ioctl(videoFd, VIDIOC_REQBUFS, &reqbuf) < 0) {
730     PTRACE(3,"PVidInDev\tREQBUFS failed : " << ::strerror(errno));
731     return isMapped;
732   }
733   if (reqbuf.count < 1) {
734     PTRACE(3,"PVidInDev\tNot enough video buffer available. (got " << reqbuf.count << ")");
735     return isMapped;
736   }
737   if (reqbuf.count > NUM_VIDBUF) {
738     PTRACE(3,"PVidInDev\tToo much video buffer allocated. (got " << reqbuf.count << ")");
739     return isMapped;
740   }
741 
742   struct v4l2_buffer buf;
743   videoBufferCount = reqbuf.count;
744 
745   for (uint i = 0; i < videoBufferCount; i++) {
746     CLEAR(buf);
747 
748     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
749     buf.memory = V4L2_MEMORY_MMAP;
750     buf.index = i;
751     if (v4l2_ioctl(videoFd, VIDIOC_QUERYBUF, &buf) < 0) {
752       PTRACE(3,"PVidInDev\tQUERYBUF failed : " << ::strerror(errno));
753       return isMapped;
754     }
755 
756     if ((videoBuffer[buf.index] = (BYTE *)v4l2_mmap(0, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, videoFd, buf.m.offset)) == MAP_FAILED) {
757       PTRACE(3,"PVidInDev\tmmap failed for buffer " << buf.index << " with error " << ::strerror(errno) << "(" << errno << ")");
758       return isMapped;
759     }
760   }
761 
762   isMapped = PTrue;
763 
764   PTRACE(7,"PVidInDev\tset mapping for " << videoBufferCount << " buffers, fd=" << videoFd);
765 
766 
767   return isMapped;
768 }
769 
770 
ClearMapping()771 void PVideoInputDevice_V4L2::ClearMapping()
772 {
773   PWaitAndSignal m(mmapMutex);
774   if (!canStream) // 'isMapped' wouldn't handle partial mappings
775     return;
776 
777   struct v4l2_buffer buf;
778   buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
779   buf.memory = V4L2_MEMORY_MMAP;
780 
781   for (buf.index = 0; ; buf.index++) {
782     if (v4l2_ioctl(videoFd, VIDIOC_QUERYBUF, &buf) < 0)
783       break;
784 
785 #ifdef SOLARIS
786     ::v4l2_munmap((char*)videoBuffer[buf.index], buf.length);
787 #else
788     ::v4l2_munmap(videoBuffer[buf.index], buf.length);
789 #endif
790   }
791 
792   isMapped = PFalse;
793 
794   PTRACE(7,"PVidInDev\tclear mapping, fd=" << videoFd);
795 }
796 
797 
GetFrameData(BYTE * buffer,PINDEX * bytesReturned)798 PBoolean PVideoInputDevice_V4L2::GetFrameData(BYTE * buffer, PINDEX * bytesReturned)
799 {
800   PTRACE(8,"PVidInDev\tGetFrameData()");
801 
802   m_pacing.Delay(1000/GetFrameRate());
803   return GetFrameDataNoDelay(buffer, bytesReturned);
804 }
805 
806 
GetFrameDataNoDelay(BYTE * buffer,PINDEX * bytesReturned)807 PBoolean PVideoInputDevice_V4L2::GetFrameDataNoDelay(BYTE * buffer, PINDEX * bytesReturned)
808 {
809   PTRACE(8,"PVidInDev\tGetFrameDataNoDelay()\tstarted:" << started << "  canSelect:" << canSelect);
810 
811   PWaitAndSignal m(mmapMutex);
812   if (!started)
813     return NormalReadProcess(buffer, bytesReturned);
814 
815   struct v4l2_buffer buf;
816   CLEAR(buf);
817   buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
818   buf.memory = V4L2_MEMORY_MMAP;
819   buf.index = currentvideoBuffer;
820 
821   if (v4l2_ioctl(videoFd, VIDIOC_DQBUF, &buf) < 0) {
822     // strace resistance
823     if (errno == EINTR) {
824         if (v4l2_ioctl(videoFd, VIDIOC_DQBUF, &buf) < 0) {
825           PTRACE(1,"PVidInDev\tDQBUF failed : " << ::strerror(errno));
826           return PFalse;
827         }
828     }
829   }
830 
831   currentvideoBuffer = (currentvideoBuffer+1) % NUM_VIDBUF;
832 
833   // If converting on the fly do it from frame store to output buffer,
834   // otherwise do straight copy.
835   if (converter != NULL && buf.bytesused)
836     converter->Convert(videoBuffer[buf.index], buffer, buf.bytesused, bytesReturned);
837   else {
838     memcpy(buffer, videoBuffer[buf.index], buf.bytesused);
839     if (bytesReturned != NULL)
840       *bytesReturned = buf.bytesused;
841   }
842 
843   PTRACE(8,"PVidInDev\tget frame data of " << buf.bytesused << "bytes, fd=" << videoFd);
844 
845   // requeue the buffer
846   if (v4l2_ioctl(videoFd, VIDIOC_QBUF, &buf) < 0) {
847     PTRACE(1,"PVidInDev\tQBUF failed : " << ::strerror(errno));
848   }
849 
850   return PTrue;
851 }
852 
853 
854 // This video device does not support memory mapping - so use
855 // normal read process to extract a frame of video data.
NormalReadProcess(BYTE * buffer,PINDEX * bytesReturned)856 PBoolean PVideoInputDevice_V4L2::NormalReadProcess(BYTE * buffer, PINDEX * bytesReturned)
857 {
858   if (!canRead)
859     return PFalse;
860 
861   ssize_t bytesRead;
862 
863   do
864     bytesRead = v4l2_read(videoFd, buffer, frameBytes);
865   while (bytesRead < 0 && errno == EINTR && IsOpen());
866 
867   if (bytesRead < 0) {
868 
869     PTRACE(1,"PVidInDev\tread failed (read = "<<bytesRead<< " expected " << frameBytes <<")");
870     bytesRead = frameBytes;
871   }
872 
873   if ((PINDEX)bytesRead != frameBytes) {
874     PTRACE(1,"PVidInDev\tread returned fewer bytes than expected");
875     // May result from a compressed format, otherwise indicates an error.
876   }
877 
878   if (converter != NULL)
879     return converter->ConvertInPlace(buffer, bytesReturned);
880 
881   if (bytesReturned != NULL)
882     *bytesReturned = (PINDEX)bytesRead;
883 
884   return PTrue;
885 }
886 
VerifyHardwareFrameSize(unsigned & width,unsigned & height)887 PBoolean PVideoInputDevice_V4L2::VerifyHardwareFrameSize(unsigned & width, unsigned & height)
888 {
889   struct v4l2_format videoFormat;
890   CLEAR(videoFormat);
891   videoFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
892   struct v4l2_streamparm streamParm;
893   CLEAR(streamParm);
894   unsigned int fi_n = 0, fi_d = 0;
895   streamParm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
896 
897   // get the frame size
898   if (v4l2_ioctl(videoFd, VIDIOC_G_FMT, &videoFormat) < 0) {
899     PTRACE(1,"PVidInDev\tG_FMT failed : " << ::strerror(errno));
900     return PFalse;
901   }
902 
903   // get the frame rate so we can preserve it throughout the S_FMT call
904   // Sidenote: V4L2 gives us the frame interval, i.e. 1/fps.
905   if (v4l2_ioctl(videoFd, VIDIOC_G_PARM, &streamParm) == 0 &&
906         (streamParm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) {
907     fi_n = streamParm.parm.capture.timeperframe.numerator;
908     fi_d = streamParm.parm.capture.timeperframe.denominator;
909   } else {
910     PTRACE(1,"PVidInDev\tG_PARM failed (preserving frame rate may not work) : " << ::strerror(errno));
911   }
912 
913   videoFormat.fmt.pix.width = width;
914   videoFormat.fmt.pix.height = height;
915 
916   PBoolean resChangedSuccessfully;
917 
918   PTRACE(4, "PVidInDev\tTry setting resolution: " << videoFormat.fmt.pix.width << "x" << videoFormat.fmt.pix.height);
919   if (v4l2_ioctl(videoFd, VIDIOC_S_FMT, &videoFormat) < 0) {
920     PTRACE(1,"PVidInDev\tS_FMT failed : " << ::strerror(errno));
921     resChangedSuccessfully = PFalse;
922   } else {
923     PTRACE(8, "PVidInDev\tS_FMT set resolution to: "<< videoFormat.fmt.pix.width << "x" <<
924             videoFormat.fmt.pix.height);
925     resChangedSuccessfully = PTrue;
926   }
927 
928   //
929   //	If the previous call was unsuccessful because of a busy device, we can close down the device and reopen it.
930   //	Note that at this point, the device stopped streaming and the memory is unmapped.
931   //
932   if ((resChangedSuccessfully == PFalse) && (errno == EBUSY)) {
933     Close();
934     Open(GetNames().GetUserFriendly(deviceName), PTrue);
935 
936     //
937     // Retry to set the new frame size
938     //
939     videoFormat.fmt.pix.width = width;
940     videoFormat.fmt.pix.height = height;
941 
942     if (v4l2_ioctl(videoFd, VIDIOC_S_FMT, &videoFormat) < 0) {
943       PTRACE(1,"PVidInDev\tS_FMT failed: " << ::strerror(errno));
944       return PFalse;
945     } else {
946       PTRACE(5, "PVidInDev\tVIDIOC_S_FMT succeeded after Close() and Open()!");
947     }
948   }
949 
950   //
951   //	Double checking that the resolution change was successful (in case of a broken driver)
952   //
953   CLEAR(videoFormat);
954   videoFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
955 
956   if (v4l2_ioctl(videoFd, VIDIOC_G_FMT, &videoFormat) < 0) {
957     PTRACE(1,"PVidInDev\tG_FMT failed : " << ::strerror(errno));
958     return PFalse;
959   } else {
960     frameBytes = videoFormat.fmt.pix.sizeimage;
961     PTRACE(8, "PVidInDev\tG_FMT returned resolution: "<< videoFormat.fmt.pix.width << "x" <<
962             videoFormat.fmt.pix.height);
963     width = videoFormat.fmt.pix.width;
964     height = videoFormat.fmt.pix.height;
965   }
966 
967   // reset the frame rate because it may have been overridden by the call to S_FMT
968   if (fi_n == 0 || fi_d == 0 || v4l2_ioctl(videoFd, VIDIOC_S_PARM, &streamParm) < 0) {
969     PTRACE(3,"PVidInDev\tunable to reset frame rate.");
970   } else if (streamParm.parm.capture.timeperframe.numerator != fi_n ||
971              streamParm.parm.capture.timeperframe.denominator  != fi_d) {
972     PTRACE(3, "PVidInDev\tnew frame interval (" << streamParm.parm.capture.timeperframe.numerator
973               << "/" << streamParm.parm.capture.timeperframe.denominator
974               << ") differs from what was requested (" << fi_n << "/" << fi_d << ").");
975   }
976 
977   return PTrue;
978 }
979 
980 /**
981  * Query the current control setting
982  * @param control is v4l2 control id (V4L2_CID_BRIGHTNESS, V4L2_CID_WHITENESS, ...)
983  * @return  -1 control is unknown, or an error occured
984  *         >=0 current value in a range [0-65535]
985  */
GetControlCommon(unsigned int control,int * value)986 int PVideoInputDevice_V4L2::GetControlCommon(unsigned int control, int *value)
987 {
988   if (!IsOpen())
989     return -1;
990 
991   struct v4l2_queryctrl q;
992   CLEAR(q);
993   q.id = control;
994   if (v4l2_ioctl(videoFd, VIDIOC_QUERYCTRL, &q) < 0)
995     return -1;
996 
997   struct v4l2_control c;
998   CLEAR(c);
999   c.id = control;
1000   if (v4l2_ioctl(videoFd, VIDIOC_G_CTRL, &c) < 0)
1001     return -1;
1002 
1003   *value = (int)((float)(c.value - q.minimum) / (q.maximum-q.minimum) * (1<<16));
1004 
1005   return *value;
1006 }
1007 
GetBrightness()1008 int PVideoInputDevice_V4L2::GetBrightness()
1009 {
1010   return GetControlCommon(V4L2_CID_BRIGHTNESS, &frameBrightness);
1011 }
1012 
GetWhiteness()1013 int PVideoInputDevice_V4L2::GetWhiteness()
1014 {
1015   return GetControlCommon(V4L2_CID_WHITENESS, &frameWhiteness);
1016 }
1017 
GetColour()1018 int PVideoInputDevice_V4L2::GetColour()
1019 {
1020   return GetControlCommon(V4L2_CID_SATURATION, &frameColour);
1021 }
1022 
GetContrast()1023 int PVideoInputDevice_V4L2::GetContrast()
1024 {
1025   return GetControlCommon(V4L2_CID_CONTRAST, &frameContrast);
1026 }
1027 
GetHue()1028 int PVideoInputDevice_V4L2::GetHue()
1029 {
1030   return GetControlCommon(V4L2_CID_HUE, &frameHue);
1031 }
1032 
1033 /**
1034  * Set a control to a new value
1035  *
1036  * @param control: V4L2_CID_BRIGHTNESS, V4L2_CID_WHITENESS, ...
1037  * @param newValue: 0-65535 Set this control to this range
1038  *                  -1 Set the default value
1039  * @return PFalse, if an error occur or the control is not supported
1040  */
SetControlCommon(unsigned int control,int newValue)1041 PBoolean PVideoInputDevice_V4L2::SetControlCommon(unsigned int control, int newValue)
1042 {
1043 #ifdef __sun
1044  PTRACE(1,"PVidInDev\t" << __FILE__ << ":" << __LINE__  << "() videoFd=" << videoFd);
1045 #else
1046   PTRACE(1,"PVidInDev\t" << __FUNCTION__  << "() videoFd=" << videoFd);
1047 #endif
1048   if (!IsOpen())
1049     return PFalse;
1050 
1051   struct v4l2_queryctrl q;
1052   CLEAR(q);
1053   q.id = control;
1054   if (v4l2_ioctl(videoFd, VIDIOC_QUERYCTRL, &q) < 0)
1055     return PFalse;
1056 
1057   struct v4l2_control c;
1058   CLEAR(c);
1059   c.id = control;
1060   if (newValue < 0)
1061     c.value = q.default_value;
1062   else
1063     c.value = (float)(q.minimum + ((q.maximum-q.minimum) * ((float)newValue) / (1<<16)));
1064 
1065   if (v4l2_ioctl(videoFd, VIDIOC_S_CTRL, &c) < 0)
1066     return PFalse;
1067 
1068   return PTrue;
1069 }
1070 
SetBrightness(unsigned newBrightness)1071 PBoolean PVideoInputDevice_V4L2::SetBrightness(unsigned newBrightness)
1072 {
1073   if (!SetControlCommon(V4L2_CID_BRIGHTNESS, newBrightness))
1074     return PFalse;
1075   frameBrightness = newBrightness;
1076   return PTrue;
1077 }
1078 
SetWhiteness(unsigned newWhiteness)1079 PBoolean PVideoInputDevice_V4L2::SetWhiteness(unsigned newWhiteness)
1080 {
1081   if (!SetControlCommon(V4L2_CID_WHITENESS, newWhiteness))
1082     return PFalse;
1083 
1084   frameWhiteness = newWhiteness;
1085   return PTrue;
1086 }
1087 
SetColour(unsigned newColour)1088 PBoolean PVideoInputDevice_V4L2::SetColour(unsigned newColour)
1089 {
1090   if (!SetControlCommon(V4L2_CID_SATURATION, newColour))
1091     return PFalse;
1092   frameColour = newColour;
1093   return PTrue;
1094 }
1095 
SetContrast(unsigned newContrast)1096 PBoolean PVideoInputDevice_V4L2::SetContrast(unsigned newContrast)
1097 {
1098   if (!SetControlCommon(V4L2_CID_CONTRAST, newContrast))
1099     return PFalse;
1100   frameContrast = newContrast;
1101   return PTrue;
1102 }
1103 
SetHue(unsigned newHue)1104 PBoolean PVideoInputDevice_V4L2::SetHue(unsigned newHue)
1105 {
1106   if (!SetControlCommon(V4L2_CID_HUE, newHue))
1107     return PFalse;
1108   frameHue=newHue;
1109   return PTrue;
1110 }
1111 
GetParameters(int * whiteness,int * brightness,int * colour,int * contrast,int * hue)1112 PBoolean PVideoInputDevice_V4L2::GetParameters (int *whiteness, int *brightness, int *colour, int *contrast, int *hue)
1113 {
1114   if (!IsOpen())
1115     return PFalse;
1116 
1117   frameWhiteness = -1;
1118   frameBrightness = -1;
1119   frameColour = -1;
1120   frameContrast = -1;
1121   frameHue = -1;
1122   GetWhiteness();
1123   GetBrightness();
1124   GetColour();
1125   GetContrast();
1126   GetHue();
1127 
1128   *whiteness  = frameWhiteness;
1129   *brightness = frameBrightness;
1130   *colour     = frameColour;
1131   *contrast   = frameContrast;
1132   *hue        = frameHue;
1133 
1134   return PTrue;
1135 }
1136 
TestAllFormats()1137 PBoolean PVideoInputDevice_V4L2::TestAllFormats()
1138 {
1139   return PTrue;
1140 }
1141 
1142 
1143 
1144 // this is used to get more userfriendly names:
1145 
1146 void
Update()1147 V4L2Names::Update()
1148 {
1149   PTRACE(1,"PV4L2Plugin\tDetecting V4L2 devices");
1150   PWaitAndSignal m(mutex);
1151   inputDeviceNames.RemoveAll (); // flush the previous run
1152 #if defined(P_FREEBSD)
1153   for (int i = 0; i < 10; i++) {
1154     PString thisDevice = PString("/dev/video") + PString(i);
1155     int videoFd=::v4l2_open((const char *)thisDevice, O_RDONLY | O_NONBLOCK);
1156     if ((videoFd > 0) || (errno == EBUSY)) {
1157       PBoolean valid = PFalse;
1158       struct v4l2_capability videoCaps;
1159       CLEAR(videoCaps);
1160       if ((errno == EBUSY) ||
1161           (v4l2_ioctl(videoFd, VIDIOC_QUERYCAP, &videoCaps) >= 0 &&
1162           (videoCaps.capabilities & V4L2_CAP_VIDEO_CAPTURE))) {
1163         PTRACE(1,"PV4L2Plugin\tdetected capture device " << videoCaps.card);
1164         valid = PTrue;
1165       }
1166       else {
1167         PTRACE(1,"PV4L2Plugin\t" << thisDevice << "is not deemed valid");
1168       }
1169       if (videoFd>0)
1170         ::v4l2_close(videoFd);
1171       if(valid)
1172         inputDeviceNames += thisDevice;
1173     }
1174   }
1175 #else
1176   PDirectory   procvideo2_4("/proc/video/dev");
1177   PDirectory   procvideo2_6("/sys/class/video4linux");
1178   PDirectory * procvideo;
1179   PString      entry;
1180   PStringList  devlist;
1181   PString      oldDevName;
1182   // Try and guess kernel version
1183   if (procvideo2_6.Exists()) {
1184     kernelVersion = K2_6;
1185     procvideo=&procvideo2_6;
1186   }
1187   else if (procvideo2_4.Exists()) {
1188     kernelVersion=K2_4;
1189     procvideo=&procvideo2_4;
1190   }
1191   else {
1192     kernelVersion=KUNKNOWN;
1193     procvideo=0;
1194   }
1195   if (procvideo) {
1196     PTRACE(2,"PV4L2Plugin\tdetected device metadata at "<<*procvideo);
1197     if (((kernelVersion==K2_6 && procvideo->Open(PFileInfo::SubDirectory)) ||
1198         (procvideo->Open(PFileInfo::RegularFile)))) {
1199       do {
1200         entry = procvideo->GetEntryName();
1201         if ((entry.Left(5) == "video")) {
1202           PString thisDevice = "/dev/" + entry;
1203           int videoFd=::v4l2_open((const char *)thisDevice, O_RDONLY | O_NONBLOCK);
1204           if ((videoFd > 0) || (errno == EBUSY)) {
1205             PBoolean valid = PFalse;
1206             struct v4l2_capability videoCaps;
1207             CLEAR(videoCaps);
1208             if ((errno == EBUSY) ||
1209                 (v4l2_ioctl(videoFd, VIDIOC_QUERYCAP, &videoCaps) >= 0 &&
1210                 (videoCaps.capabilities & V4L2_CAP_VIDEO_CAPTURE))) {
1211               PTRACE(1,"PV4L2Plugin\tdetected capture device " << videoCaps.card);
1212               valid = PTrue;
1213             }
1214             else {
1215               PTRACE(1,"PV4L2Plugin\t" << thisDevice << "is not deemed valid");
1216             }
1217             if (videoFd>0)
1218               ::v4l2_close(videoFd);
1219             if(valid)
1220               inputDeviceNames += thisDevice;
1221           }
1222           else {
1223             PTRACE(1,"PV4L2Plugin\tcould not open " << thisDevice);
1224           }
1225         }
1226       } while (procvideo->Next());
1227     }
1228   }
1229   else {
1230     PTRACE(1,"Unable to detect v4l2 directory");
1231   }
1232 #endif
1233   if (inputDeviceNames.GetSize() == 0) {
1234     POrdinalToString vid;
1235     ReadDeviceDirectory("/dev/", vid);
1236 
1237     for (PINDEX i = 0; i < vid.GetSize(); i++) {
1238       PINDEX cardnum = vid.GetKeyAt(i);
1239       int fd = ::v4l2_open(vid[cardnum], O_RDONLY | O_NONBLOCK);
1240       if ((fd >= 0) || (errno == EBUSY)) {
1241         if (fd >= 0)
1242           ::v4l2_close(fd);
1243         inputDeviceNames += vid[cardnum];
1244       }
1245     }
1246   }
1247   PopulateDictionary();
1248 }
1249 
BuildUserFriendly(PString devname)1250 PString V4L2Names::BuildUserFriendly(PString devname)
1251 {
1252   PString Result;
1253 
1254   int fd = ::v4l2_open((const char *)devname, O_RDONLY);
1255   if(fd < 0) {
1256     return devname;
1257   }
1258 
1259   struct v4l2_capability videocap;
1260   CLEAR(videocap);
1261   if (v4l2_ioctl(fd, VIDIOC_QUERYCAP, &videocap) < 0)  {
1262     ::v4l2_close(fd);
1263     return devname;
1264   }
1265 
1266   ::v4l2_close(fd);
1267   PString ufname((const char*)videocap.card);
1268 
1269   return ufname;
1270 }
1271 
1272 /**
1273  *
1274  *
1275  */
QueueBuffers()1276 PBoolean PVideoInputDevice_V4L2::QueueBuffers()
1277 {
1278   if (PTrue == areBuffersQueued) {
1279     PTRACE(3, "PVidInDev\tVideo buffers already queued! Do DequeueBuffers() first!");
1280     return areBuffersQueued;
1281   }
1282 
1283   if (PFalse == isMapped) {
1284     PTRACE(3, "Buffers are not mapped yet! Do SetMapping() first!");
1285     return areBuffersQueued;
1286   }
1287 
1288   /* Queue all buffers */
1289   currentvideoBuffer = 0;
1290 
1291   for (unsigned int i = 0; i < videoBufferCount; i++) {
1292     struct v4l2_buffer buf;
1293     CLEAR(buf);
1294 
1295     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1296     buf.memory = V4L2_MEMORY_MMAP;
1297     buf.index = i;
1298 
1299     if (v4l2_ioctl(videoFd, VIDIOC_QBUF, &buf) < 0) {
1300       PTRACE(3, "PVidInDev\tVIDIOC_QBUF failed for buffer " << i << ": " << ::strerror(errno));
1301       return areBuffersQueued;
1302     }
1303     PTRACE(6, "PVidInDev\tBuffer " << i << " queued");
1304   }
1305 
1306   areBuffersQueued = PTrue;
1307   PTRACE(8, "PVidInDev\t" << videoBufferCount << " buffers successfully queued.");
1308   return areBuffersQueued;
1309 }
1310 
1311 
StartStreaming()1312 PBoolean PVideoInputDevice_V4L2::StartStreaming()
1313 {
1314   PTRACE(8, "PVidInDev\tStart streaming for \"" << deviceName << "\" with fd=" << videoFd);
1315 
1316   if (PTrue == isStreaming) {
1317     PTRACE(4, "PVidInDev\tVideo buffers already streaming! Nothing to do.");
1318     return isStreaming;
1319   }
1320 
1321   if (PFalse == areBuffersQueued) {
1322     PTRACE(2, "Buffers are not queued yet! Do QueueBuffers() first!");
1323     return isStreaming;
1324   }
1325 
1326   //
1327   // Now start streaming
1328   //
1329   enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1330   if (v4l2_ioctl(videoFd, VIDIOC_STREAMON, &type) < 0) {
1331     PTRACE(2, "PVidInDev\tSTREAMON failed with error " << ::strerror(errno));
1332     return isStreaming;
1333   }
1334 
1335   isStreaming = PTrue;
1336   PTRACE(8, "PVidInDev\tVideo Input Device \"" << deviceName << "\" successfully started streaming.");
1337   return isStreaming;
1338 }
1339 
1340 
StopStreaming()1341 void PVideoInputDevice_V4L2::StopStreaming(){
1342   PTRACE(8, "PVidInDev\tStop streaming for \"" << deviceName << "\" with fd=" << videoFd);
1343 
1344   if (PFalse == isStreaming) {
1345     PTRACE(2, "PVidInDev\tVideo buffers already not streaming! Do StartStreaming() first.");
1346     return;
1347   }
1348 
1349   int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1350 
1351   if (v4l2_ioctl(videoFd, VIDIOC_STREAMOFF, &type) < 0) {
1352     PTRACE(2, "PVidInDev\tSTREAMOFF failed : " << ::strerror(errno));
1353     return;
1354   }
1355 
1356   isStreaming = PFalse;
1357   PTRACE(8, "PVidInDev\tVideo Input Device \"" << deviceName << "\" successfully stopped streaming.");
1358 }
1359 
1360 
1361 // End Of File ///////////////////////////////////////////////////////////////
1362