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