Instrument Neutral Distributed Interface INDI 2.2.5
v4l2_base.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2005 by Jasem Mutlaq <[email protected]>
3
4 Based on V4L 2 Example
5 http://v4l2spec.bytesex.org/spec-single/v4l2.html#CAPTURE-EXAMPLE
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21*/
22
23#include "v4l2_base.h"
24
25#include "ccvt.h"
26#include "eventloop.h"
27#include "indidevapi.h"
28#include "indilogger.h"
29#include "lilxml.h"
30// PWC framerate support
31#include "pwc-ioctl.h"
32
33#include <iostream>
34
35#include <sys/ioctl.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <assert.h>
39#include <fcntl.h>
40#include <unistd.h>
41#include <stdio.h>
42#include <cerrno>
43#include <sys/mman.h>
44#include <cstring>
45#include <ctime>
46#include <cmath>
47#include <sys/time.h>
48#include <inttypes.h>
49
50#ifdef __linux__
51#include <asm/types.h> /* for videodev2.h */
52/* Kernel headers version */
53#include <linux/version.h>
54#elif defined(__FreeBSD__) || defined(__OpenBSD__)
55#define LINUX_VERSION_CODE 1
56#define KERNEL_VERSION(...) 1
57#endif
58
59#define ERRMSGSIZ 1024
60
61#define CLEAR(x) memset(&(x), 0, sizeof(x))
62
63#define XIOCTL(fd, ioctl, arg) xioctl(fd, ioctl, arg, #ioctl)
64
65#define DBG_STR_PIX "%c%c%c%c"
66#define DBG_PIX(pf) ((pf) >> 0) & 0xFF, ((pf) >> 8) & 0xFF, ((pf) >> 16) & 0xFF, ((pf) >> 24) & 0xFF
67
68/* TODO: Before 3.17, the only way to determine a format is compressed is to
69 * consolidate a matrix with v4l2_pix_format::pixelformat and v4l2_fourcc
70 * values. After 3.17, field 'flags' in v4l2_pix_format is assumed to be
71 * properly filled. For now we rely on 'flags', but we could just check the
72 * most used pixel formats in a CCD whitelist (YUVx, RGBxxx...).
73 */
74
75#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
76#define DBG_STR_FLAGS "...%c .%c%c%c %c%c.%c .%c%c%c %c%c%c%c"
77#define DBG_FLAGS(b) \
78 /* 0x00010000 */ ((b).flags & V4L2_BUF_FLAG_TSTAMP_SRC_SOE) ? 'S' : 'E', \
79 /* 0x00004000 */ ((b).flags & V4L2_BUF_FLAG_TIMESTAMP_COPY) ? 'c' : '.', \
80 /* 0x00002000 */ ((b).flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) ? 'm' : '.', \
81 /* 0x00001000 */ ((b).flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN) ? 'C' : '.', \
82 /* 0x00000800 */ ((b).flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE) ? 'I' : '.', \
83 /* 0x00000400 */ ((b).flags & V4L2_BUF_FLAG_PREPARED) ? 'p' : '.', \
84 /* 0x00000100 */ ((b).flags & V4L2_BUF_FLAG_TIMECODE) ? 'T' : '.', \
85 /* 0x00000040 */ ((b).flags & V4L2_BUF_FLAG_ERROR) ? 'E' : '.', \
86 /* 0x00000020 */ ((b).flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '.', \
87 /* 0x00000010 */ ((b).flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : '.', \
88 /* 0x00000008 */ ((b).flags & V4L2_BUF_FLAG_KEYFRAME) ? 'K' : '.', \
89 /* 0x00000004 */ ((b).flags & V4L2_BUF_FLAG_DONE) ? 'd' : '.', \
90 /* 0x00000002 */ ((b).flags & V4L2_BUF_FLAG_QUEUED) ? 'q' : '.', \
91 /* 0x00000001 */ ((b).flags & V4L2_BUF_FLAG_MAPPED) ? 'm' : '.'
92#else
93#define DBG_STR_FLAGS "%s"
94#define DBG_FLAGS(b) ""
95#endif
96
97#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
98#define DBG_STR_FMT "%ux%u " DBG_STR_PIX " %scompressed (%ssupported)"
99#define DBG_FMT(f) \
100 (f).fmt.pix.width, (f).fmt.pix.height, DBG_PIX((f).fmt.pix.pixelformat), \
101 ((f).fmt.pix.flags & V4L2_FMT_FLAG_COMPRESSED) ? "" : "un", \
102 (decoder->issupportedformat((f).fmt.pix.pixelformat) ? "" : "un")
103#else
104#define DBG_STR_FMT "%ux%u " DBG_STR_PIX " (%ssupported)"
105#define DBG_FMT(f) \
106 (f).fmt.pix.width, (f).fmt.pix.height, DBG_PIX((f).fmt.pix.pixelformat), \
107 (decoder->issupportedformat((f).fmt.pix.pixelformat) ? "" : "un")
108#endif
109
110#define DBG_STR_BUF "#%d " DBG_STR_FLAGS " % 7d bytes %4.4s seq %d:%d stamp %ld.%06ld"
111#define DBG_BUF(b) \
112 (b).index, DBG_FLAGS(b), (b).bytesused, \
113 ((b).memory == V4L2_MEMORY_MMAP) ? \
114 "mmap" : \
115 ((b).memory == V4L2_MEMORY_USERPTR) ? "uptr" : \
116 ((b).memory == 4 /* kernel 3.8.0: V4L2_MEMORY_DMABUF */) ? \
117 "dma" : \
118 ((b).memory == V4L2_MEMORY_OVERLAY) ? "over" : "", \
119 (b).sequence, (b).field, (b).timestamp.tv_sec, (b).timestamp.tv_usec
120
121using namespace std;
122
123/*char *entityXML(char *src) {
124 char *s = src;
125 while (*s) {
126 if ((*s == '&') || (*s == '<') || (*s == '>') || (*s == '\'') || (*s == '"'))
127 *s = '_';
128 s += 1;
129 }
130 return src;
131}*/
132
133namespace INDI
134{
135
137{
138 frameRate.numerator = 1;
139 frameRate.denominator = 25;
140
141 selectCallBackID = -1;
142 //dropFrameCount = 1;
143 //dropFrame = 0;
144
145 xmax = xmin = 160;
146 ymax = ymin = 120;
147
149 fd = -1;
150 buffers = nullptr;
151 n_buffers = 0;
152
153 callback = nullptr;
154
155 cancrop = true;
156 cansetrate = true;
157 streamedonce = false;
158
159 v4l2_decode = new V4L2_Decode();
161 decoder->init();
162 dodecode = true;
163
164 bpp = 8;
165 has_ext_pix_format = false;
166 const std::vector<unsigned int> &vsuppformats = decoder->getsupportedformats();
168 "Using default decoder '%s'\n Supported V4L2 formats are:", decoder->getName());
169 for (std::vector<unsigned int>::const_iterator it = vsuppformats.begin(); it != vsuppformats.end(); ++it)
170 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%c%c%c%c ", (*it >> 0), (*it >> 8), (*it >> 16),
171 (*it >> 24));
172 //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,INDI::Logger::DBG_SESSION,"Default decoder: %s", decoder->getName());
173
174 getframerate = nullptr;
175 setframerate = nullptr;
176
177 reallocate_buffers = false;
178 path = nullptr;
179 uptr = nullptr;
180
182 streamactive = false;
183 cropset = false;
184}
185
187{
188 delete v4l2_decode;
189}
190
203{
204 /* See note at top of this file */
205#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
206 switch (fmt.fmt.pix.pixelformat)
207{
208 case V4L2_PIX_FMT_JPEG:
209 case V4L2_PIX_FMT_MJPEG:
210 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: format %c%c%c%c patched to be considered compressed",
211 __FUNCTION__, fmt.fmt.pix.pixelformat, fmt.fmt.pix.pixelformat >> 8,
212 fmt.fmt.pix.pixelformat >> 16, fmt.fmt.pix.pixelformat >> 24);
213 return true;
214
215 default:
216 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: format %c%c%c%c has compressed flag %d",
217 __FUNCTION__, fmt.fmt.pix.pixelformat, fmt.fmt.pix.pixelformat >> 8,
218 fmt.fmt.pix.pixelformat >> 16, fmt.fmt.pix.pixelformat >> 24,
219 fmt.fmt.pix.flags & V4L2_FMT_FLAG_COMPRESSED);
220 return fmt.fmt.pix.flags & V4L2_FMT_FLAG_COMPRESSED;
221 }
222#else
223 switch (fmt.fmt.pix.pixelformat)
224{
225 case V4L2_PIX_FMT_GREY:
226 /* case V4L2_PIX_FMT... add other uncompressed and supported formats here */
227 return false;
228
229 default:
230 return true;
231}
232#endif
233}
234
246int V4L2_Base::xioctl(int fd, int request, void * arg, char const * const request_str)
247{
248 int r = -1;
249
250 do
251 {
252 r = ioctl(fd, request, arg);
253 }
254 while (-1 == r && EINTR == errno);
255
256 if (-1 == r)
257 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: ioctl 0x%08X/%s received errno %d (%s)", __FUNCTION__,
258 request, request_str, errno, strerror(errno));
259
260 return r;
261}
262
263/* @internal Setting a V4L2 format through ioctl VIDIOC_S_FMT
264 *
265 * If the format type is non-zero, this function executes ioctl
266 * VIDIOC_S_FMT on the argument data format, and updates the instance
267 * data format on success. If an error arises, the instance data
268 * format is left unmodified.
269 *
270 * If the format type is zero, this function executes ioctl
271 * VIDIOC_G_FMT on a temporary data format, and updates the instance
272 * data format on success. If an error arises, the instance data
273 * format is left unmodified.
274 *
275 * @warning If the format type is non-zero and the device streamed
276 * at least once before the call, the device is closed and reopened
277 * before updating the format.
278 *
279 * @warning If successful, this function updates the instance data
280 * format 'fmt'.
281 *
282 * @note The frame decoder format is updated with the resulting format,
283 * and the instance depth field 'bpp' is updated with the resulting
284 * frame decoder depth.
285 *
286 * @param new_fmt is the v4l2_format to set, eventually with type set
287 * to zero to refresh the instance format with the current device format.
288 * @return 0 if ioctl is successful, or -1 with error message updated.
289 */
290int V4L2_Base::ioctl_set_format(struct v4l2_format new_fmt, char * errmsg)
291{
292 /* Reopen device if it streamed at least once and we want to update the format*/
293 if (streamedonce && new_fmt.type)
294 {
295 close_device();
296
297 if (open_device(path, errmsg))
298 {
299 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed reopening device %s (%s)", __FUNCTION__, path,
300 errmsg);
301 return -1;
302 }
303 }
304
305 /* Trying format with VIDIOC_TRY_FMT has no interesting advantage here */
306 if (false)
307 {
308 if (-1 == XIOCTL(fd, VIDIOC_TRY_FMT, &new_fmt))
309 {
310 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed VIDIOC_TRY_FMT with " DBG_STR_FMT,
311 __FUNCTION__, DBG_FMT(new_fmt));
312 return errno_exit("VIDIOC_TRY_FMT", errmsg);
313 }
314 }
315
316 if (new_fmt.type)
317 {
318 /* Set format */
319 if (-1 == XIOCTL(fd, VIDIOC_S_FMT, &new_fmt))
320 {
321 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed VIDIOC_S_FMT with " DBG_STR_FMT, __FUNCTION__,
322 DBG_FMT(new_fmt));
323 return errno_exit("VIDIOC_S_FMT", errmsg);
324 }
325 }
326 else
327 {
328 /* Retrieve format */
329 new_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
330 if (-1 == XIOCTL(fd, VIDIOC_G_FMT, &new_fmt))
331 {
332 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed VIDIOC_G_FMT", __FUNCTION__);
333 return errno_exit("VIDIOC_G_FMT", errmsg);
334 }
335 }
336
337 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: current format " DBG_STR_FMT, __FUNCTION__,
338 DBG_FMT(new_fmt));
339
340 /* Update internals */
342 this->bpp = decoder->getBpp();
343
344 /* Assign the format as current */
345 fmt = new_fmt;
346
347 return 0;
348}
349
350int V4L2_Base::errno_exit(const char * s, char * errmsg)
351{
352 fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
353 snprintf(errmsg, ERRMSGSIZ, "%s error %d, %s\n", s, errno, strerror(errno));
354
355 if (streamactive)
356 stop_capturing(errmsg);
357
358 return -1;
359}
360
362{
363 dodecode = d;
364}
365
366int V4L2_Base::connectCam(const char * devpath, char * errmsg, int pixelFormat, int width, int height)
367{
368 INDI_UNUSED(pixelFormat);
369 INDI_UNUSED(width);
370 INDI_UNUSED(height);
371 selectCallBackID = -1;
372 cancrop = true;
373 cansetrate = true;
374 streamedonce = false;
375 frameRate.numerator = 1;
376 frameRate.denominator = 25;
377
378 if (open_device(devpath, errmsg) < 0)
379 return -1;
380
381 path = devpath;
382
383 if (check_device(errmsg) < 0)
384 return -1;
385
386 //cerr << "V4L2 Check: All successful, returning\n";
387 return fd;
388}
389
390void V4L2_Base::disconnectCam(bool stopcapture)
391{
392 if (selectCallBackID != -1)
394
395 if (stopcapture)
396 {
397 char errmsg[ERRMSGSIZ] = {0};
398 stop_capturing(errmsg);
399 }
400
401 //uninit_device (errmsg);
402
403 close_device();
404
405 //fprintf(stderr, "Disconnect cam\n");
406}
407
409{
410 if (!(strcmp((const char *)cap.driver, "pwc")))
411 return true;
412 else
413 return false;
414}
415
416/* @internal Calculate epoch time shift
417 *
418 * The clock CLOCK_MONOTONIC starts counting from an undefined origin (boot time
419 * for instance). This function computes the offset between the current time returned
420 * by gettimeofday and the monotonic time returned by clock_gettime in milliseconds.
421 * This value can then be used to determine the time and date of frames from their
422 * timestamp as returned by the kernel.
423 *
424 * Code provided at:
425 * http://stackoverflow.com/questions/10266451/where-does-v4l2-buffer-timestamp-value-starts-counting
426 *
427 * @return the milliseconds offset to apply to the timestamp returned by gettimeofday for
428 * it to have the same reference as clock_gettime.
429 */
430//static long getEpochTimeShift()
431//{
432// struct timeval epochtime = { 0, 0 };
433// struct timespec vsTime = { 0, 0 };
434//
435// gettimeofday(&epochtime, nullptr);
436// clock_gettime(CLOCK_MONOTONIC, &vsTime);
437//
438// long const uptime_ms = vsTime.tv_sec * 1000 + (long)round(vsTime.tv_nsec / 1000000.0);
439// long const epoch_ms = epochtime.tv_sec * 1000 + (long)round(epochtime.tv_usec / 1000.0);
440//
441// long const epoch_shift = epoch_ms - uptime_ms;
442// //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"%s: epoch shift is %ld",__FUNCTION__,epoch_shift);
443//
444// return epoch_shift;
445//}
446
447/* @brief Reading a frame from the V4L2 driver.
448 *
449 * This function will attempt to read a frame with the adequate
450 * method for the device, and forward the frame read to the configured
451 * decoder and/or recorder.
452 *
453 * With the MMAP method, the first available buffer is dequeued to read
454 * the embedded frame. If the frame is marked erroneous by the driver, or
455 * the frame is known to be uncompressed but its length doesn't match the
456 * expected size, the buffer is re-enqueued immediately.
457 *
458 * Although only the MMAP method is actually supported, two other methods
459 * are also implemented:
460 * - With the READ method, the frame is read directly from the device
461 * descriptor, using the first buffer characteristics are address and
462 * length. But no processing is done actually.
463 * - With the USERPTR method, the first available buffer is dequeued, then
464 * verified against the buffer list. No processing is done actually, and
465 * the buffer is immediately requeued.
466 *
467 * @param errmsg is the error message updated in case of error.
468 * @return 0 if frame read is processed, or -1 with error message updated.
469 */
470int V4L2_Base::read_frame(char * errmsg)
471{
472 unsigned int i;
473 //cerr << "in read Frame" << endl;
474
475 switch (io)
476 {
477 case IO_METHOD_READ:
478 cerr << "in read Frame method read" << endl;
479 if (-1 == read(fd, buffers[0].start, buffers[0].length))
480 {
481 switch (errno)
482 {
483 case EAGAIN:
484 return 0;
485 case EIO:
486 /* Could ignore EIO, see spec. */
487 /* fall through */
488 default:
489 return errno_exit("read", errmsg);
490 }
491 }
492 //process_image (buffers[0].start);
493 break;
494
495 case IO_METHOD_MMAP:
496 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: using MMAP to recover frame buffer", __FUNCTION__);
497 CLEAR(buf);
498
499 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
500 buf.memory = V4L2_MEMORY_MMAP;
501
502 /* For debugging purposes */
503 if (false)
504 {
505 for (i = 0; i < n_buffers; ++i)
506 {
507 buf.index = i;
508 if (-1 == XIOCTL(fd, VIDIOC_QUERYBUF, &buf))
509 switch (errno)
510 {
511 case EINVAL:
513 "%s: invalid buffer query, doing as if buffer was in output queue",
514 __FUNCTION__);
515 break;
516
517 default:
518 return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QUERYBUF", errmsg);
519 }
520
522 }
523 }
524
525 if (-1 == XIOCTL(fd, VIDIOC_DQBUF, &buf))
526 switch (errno)
527 {
528 case EAGAIN:
530 "%s: no buffer found with DQBUF ioctl (EAGAIN) - frame not ready or not requested",
531 __FUNCTION__);
532 return 0;
533
534 case EIO:
535 /* Could ignore EIO, see spec. */
536 /* Fall through */
538 "%s: transitory internal error with DQBUF ioctl (EIO)", __FUNCTION__);
539 return 0;
540
541 case EINVAL:
542 case EPIPE:
543 default:
544 return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_DQBUF", errmsg);
545 }
546
547 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: buffer #%d dequeued from fd:%d\n", __FUNCTION__,
548 buf.index, fd);
549
550 if (buf.flags & V4L2_BUF_FLAG_ERROR)
551 {
553 "%s: recoverable error with DQBUF ioctl (BUF_FLAG_ERROR) - frame should be dropped",
554 __FUNCTION__);
555 if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
556 return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);
557 buf.bytesused = 0;
558 return 0;
559 }
560
561 if (!is_compressed() && buf.bytesused != fmt.fmt.pix.sizeimage)
562 {
564 "%s: frame is %d-byte long, expected %d - frame should be dropped", __FUNCTION__,
565 buf.bytesused, fmt.fmt.pix.sizeimage);
566
567 if (false)
568 {
569 unsigned char const * b = (unsigned char const *)buffers[buf.index].start;
570 unsigned char const * end = b + buf.bytesused;
571
572 do
574 "%s: [%p] %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X",
575 __FUNCTION__, b, b[0 * 4 + 0], b[0 * 4 + 1], b[0 * 4 + 2], b[0 * 4 + 3],
576 b[1 * 4 + 0], b[1 * 4 + 1], b[1 * 4 + 2], b[1 * 4 + 3], b[2 * 4 + 0], b[2 * 4 + 1],
577 b[2 * 4 + 2], b[2 * 4 + 3], b[3 * 4 + 0], b[3 * 4 + 1], b[3 * 4 + 2],
578 b[3 * 4 + 3]);
579 while ((b += 16) < end);
580 }
581
582 if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
583 return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);
584 buf.bytesused = 0;
585 return 0;
586 }
587
588#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
589 /* TODO: the timestamp can be checked against the expected exposure to validate the frame - doesn't work, yet */
590 switch (buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK)
591 {
592 case V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN:
593 /* FIXME: try monotonic clock when timestamp clock type is unknown */
594 case V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC:
595 {
596 struct timespec uptime = { 0, 0 };
597 clock_gettime(CLOCK_MONOTONIC, &uptime);
598
599 struct timeval epochtime = { 0, 0 };
600 /*gettimeofday(&epochtime, nullptr); uncomment this to get the timestamp from epoch start */
601
602 float const secs =
603 (epochtime.tv_sec - uptime.tv_sec + buf.timestamp.tv_sec) +
604 (epochtime.tv_usec - uptime.tv_nsec / 1000.0f + buf.timestamp.tv_usec) / 1000000.0f;
605
606 if (V4L2_BUF_FLAG_TSTAMP_SRC_SOE == (buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK))
607 {
609 "%s: frame exposure started %.03f seconds ago", __FUNCTION__, -secs);
610 }
611 else if (V4L2_BUF_FLAG_TSTAMP_SRC_EOF == (buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK))
612 {
614 "%s: frame finished capturing %.03f seconds ago", __FUNCTION__, -secs);
615 }
616 else
617 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: unsupported timestamp in frame",
618 __FUNCTION__);
619
620 break;
621 }
622
623 case V4L2_BUF_FLAG_TIMESTAMP_COPY:
624 default:
625 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: no usable timestamp found in frame",
626 __FUNCTION__);
627 }
628#endif
629
630 /* TODO: there is probably a better error handling than asserting the buffer index */
631 assert(buf.index < n_buffers);
632
633 if (dodecode)
634 {
635 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: [%p] decoding %d-byte buffer %p cropset %c",
636 __FUNCTION__, decoder, buf.bytesused, buffers[buf.index].start, cropset ? 'Y' : 'N');
637 decoder->decode((unsigned char *)(buffers[buf.index].start), &buf, m_Native);
638 }
639
640 /*
641 if (dorecord)
642 {
643 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: [%p] recording %d-byte buffer %p", __FUNCTION__,
644 recorder, buf.bytesused, buffers[buf.index].start);
645 recorder->writeFrame((unsigned char *)(buffers[buf.index].start));
646 }
647 */
648
649 //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"lxstate is %d, dropFrame %c\n", lxstate, (dropFrame?'Y':'N'));
650
651 /* Requeue buffer */
652 if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
653 return errno_exit("ReadFrame IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);
654
655 if (lxstate == LX_ACTIVE)
656 {
657 /* Call provided callback function if any */
658 //if (callback && !dorecord)
659 if (callback)
660 (*callback)(uptr);
661 }
662
663 if (lxstate == LX_TRIGGERED)
665
666 break;
667
669 cerr << "in read Frame method userptr" << endl;
670 CLEAR(buf);
671
672 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
673 buf.memory = V4L2_MEMORY_USERPTR;
674
675 if (-1 == XIOCTL(fd, VIDIOC_DQBUF, &buf))
676 {
677 switch (errno)
678 {
679 case EAGAIN:
680 return 0;
681 case EIO:
682 /* Could ignore EIO, see spec. */
683 /* fall through */
684 default:
685 errno_exit("VIDIOC_DQBUF", errmsg);
686 }
687 }
688
689 for (i = 0; i < n_buffers; ++i)
690 if (buf.m.userptr == (unsigned long)buffers[i].start && buf.length == buffers[i].length)
691 break;
692
693 assert(i < n_buffers);
694
695 //process_image ((void *) buf.m.userptr);
696
697 if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
698 errno_exit("ReadFrame IO_METHOD_USERPTR: VIDIOC_QBUF", errmsg);
699
700 break;
701 }
702
703 return 0;
704}
705
707{
708 enum v4l2_buf_type type;
709
710 switch (io)
711 {
712 case IO_METHOD_READ:
713 /* Nothing to do. */
714 break;
715
716 case IO_METHOD_MMAP:
717 /* Kernel 3.11 problem with streamoff: vb2_is_busy(queue) remains true so we can not change anything without disconnecting */
718 /* It seems that device should be closed/reopened to change any capture format settings. From V4L2 API Spec. (Data Negotiation) */
719 /* Switching the logical stream or returning into "panel mode" is possible by closing and reopening the device. */
720 /* Drivers may support a switch using VIDIOC_S_FMT. */
722 // N.B. I used this as a hack to solve a problem with capturing a frame
723 // long time ago. I recently tried taking this hack off, and it worked fine!
724
725 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
726 if (selectCallBackID != -1)
727 {
729 selectCallBackID = -1;
730 }
731 streamactive = false;
732 if (-1 == XIOCTL(fd, VIDIOC_STREAMOFF, &type))
733 return errno_exit("VIDIOC_STREAMOFF", errmsg);
734 break;
735 }
736 //uninit_device(errmsg);
737
738 return 0;
739}
740
742{
743 unsigned int i;
744 enum v4l2_buf_type type;
745
746 if (!streamedonce)
747 init_device(errmsg);
748
749 switch (io)
750 {
751 case IO_METHOD_READ:
752 /* Nothing to do. */
753 break;
754
755 case IO_METHOD_MMAP:
756 for (i = 0; i < n_buffers; ++i)
757 {
758 struct v4l2_buffer buf;
759
760 CLEAR(buf);
761
762 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
763 buf.memory = V4L2_MEMORY_MMAP;
764 buf.index = i;
765 //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"v4l2_start_capturing: enqueuing buffer %d for fd=%d\n", buf.index, fd);
766 /*if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
767 return errno_exit ("StartCapturing IO_METHOD_MMAP: VIDIOC_QBUF", errmsg);*/
768 XIOCTL(fd, VIDIOC_QBUF, &buf);
769 }
770
771 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
772 if (-1 == XIOCTL(fd, VIDIOC_STREAMON, &type))
773 return errno_exit("VIDIOC_STREAMON", errmsg);
774
776 streamactive = true;
777
778 break;
779
781 for (i = 0; i < n_buffers; ++i)
782 {
783 struct v4l2_buffer buf;
784
785 CLEAR(buf);
786
787 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
788 buf.memory = V4L2_MEMORY_USERPTR;
789 buf.m.userptr = (unsigned long)buffers[i].start;
791
792 if (-1 == XIOCTL(fd, VIDIOC_QBUF, &buf))
793 return errno_exit("StartCapturing IO_METHOD_USERPTR: VIDIOC_QBUF", errmsg);
794 }
795
796 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
797
798 if (-1 == XIOCTL(fd, VIDIOC_STREAMON, &type))
799 return errno_exit("VIDIOC_STREAMON", errmsg);
800
801 break;
802 }
803 //if (dropFrameEnabled)
804 //dropFrame = dropFrameCount;
805 streamedonce = true;
806 return 0;
807}
808
809void V4L2_Base::newFrame(int /*fd*/, void * p)
810{
811 char errmsg[ERRMSGSIZ];
812
813 ((V4L2_Base *)(p))->read_frame(errmsg);
814}
815
816int V4L2_Base::uninit_device(char * errmsg)
817{
818 switch (io)
819 {
820 case IO_METHOD_READ:
821 free(buffers[0].start);
822 break;
823
824 case IO_METHOD_MMAP:
825 for (unsigned int i = 0; i < n_buffers; ++i)
826 if (-1 == munmap(buffers[i].start, buffers[i].length))
827 return errno_exit("munmap", errmsg);
828 break;
829
831 for (unsigned int i = 0; i < n_buffers; ++i)
832 free(buffers[i].start);
833 break;
834 }
835
836 free(buffers);
837
838 return 0;
839}
840
841void V4L2_Base::init_read(unsigned int buffer_size)
842{
843 buffers = (buffer *)calloc(1, sizeof(*buffers));
844
845 if (!buffers)
846 {
847 fprintf(stderr, "Out of memory\n");
848 exit(EXIT_FAILURE);
849 }
850
851 buffers[0].length = buffer_size;
852 buffers[0].start = malloc(buffer_size);
853
854 if (!buffers[0].start)
855 {
856 fprintf(stderr, "Out of memory\n");
857 exit(EXIT_FAILURE);
858 }
859}
860
861int V4L2_Base::init_mmap(char * errmsg)
862{
863 struct v4l2_requestbuffers req;
864
865 CLEAR(req);
866
867 req.count = 4;
868 //req.count = 1;
869 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
870 req.memory = V4L2_MEMORY_MMAP;
871
872 if (-1 == XIOCTL(fd, VIDIOC_REQBUFS, &req))
873 {
874 if (EINVAL == errno)
875 {
876 fprintf(stderr, "%.*s does not support memory mapping\n", (int)sizeof(dev_name), dev_name);
877 snprintf(errmsg, ERRMSGSIZ, "%.*s does not support memory mapping\n", (int)sizeof(dev_name), dev_name);
878 return -1;
879 }
880 else
881 {
882 return errno_exit("VIDIOC_REQBUFS", errmsg);
883 }
884 }
885
886 if (req.count < 2)
887 {
888 fprintf(stderr, "Insufficient buffer memory on %.*s\n", (int)sizeof(dev_name), dev_name);
889 snprintf(errmsg, ERRMSGSIZ, "Insufficient buffer memory on %.*s\n", (int)sizeof(dev_name), dev_name);
890 return -1;
891 }
892
893 buffers = (buffer *)calloc(req.count, sizeof(*buffers));
894
895 if (!buffers)
896 {
897 fprintf(stderr, "buffers. Out of memory\n");
898 strncpy(errmsg, "buffers. Out of memory\n", ERRMSGSIZ);
899 return -1;
900 }
901
902 for (n_buffers = 0; n_buffers < req.count; n_buffers++)
903 {
904 struct v4l2_buffer buf;
905
906 CLEAR(buf);
907
908 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
909 buf.memory = V4L2_MEMORY_MMAP;
910 buf.index = n_buffers;
911
912 if (-1 == XIOCTL(fd, VIDIOC_QUERYBUF, &buf))
913 return errno_exit("VIDIOC_QUERYBUF", errmsg);
914
915 buffers[n_buffers].length = buf.length;
916 buffers[n_buffers].start = mmap(nullptr /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */,
917 MAP_SHARED /* recommended */, fd, buf.m.offset);
918
919 if (MAP_FAILED == buffers[n_buffers].start)
920 return errno_exit("mmap", errmsg);
921 }
922
923 return 0;
924}
925
926void V4L2_Base::init_userp(unsigned int buffer_size)
927{
928 struct v4l2_requestbuffers req;
929 char errmsg[ERRMSGSIZ];
930
931 CLEAR(req);
932
933 req.count = 4;
934 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
935 req.memory = V4L2_MEMORY_USERPTR;
936
937 if (-1 == XIOCTL(fd, VIDIOC_REQBUFS, &req))
938 {
939 if (EINVAL == errno)
940 {
941 fprintf(stderr, "%.*s does not support user pointer i/o\n", (int)sizeof(dev_name), dev_name);
942 exit(EXIT_FAILURE);
943 }
944 else
945 {
946 errno_exit("VIDIOC_REQBUFS", errmsg);
947 }
948 }
949
950 buffers = (buffer *)calloc(4, sizeof(*buffers));
951
952 if (!buffers)
953 {
954 fprintf(stderr, "Out of memory\n");
955 exit(EXIT_FAILURE);
956 }
957
958 for (n_buffers = 0; n_buffers < 4; ++n_buffers)
959 {
960 buffers[n_buffers].length = buffer_size;
961 buffers[n_buffers].start = malloc(buffer_size);
962
963 if (!buffers[n_buffers].start)
964 {
965 fprintf(stderr, "Out of memory\n");
966 exit(EXIT_FAILURE);
967 }
968 }
969}
970
971int V4L2_Base::check_device(char * errmsg)
972{
973 struct v4l2_input input_avail;
974
975 if (-1 == XIOCTL(fd, VIDIOC_QUERYCAP, &cap))
976 {
977 if (EINVAL == errno)
978 {
979 fprintf(stderr, "%.*s is no V4L2 device\n", (int)sizeof(dev_name), dev_name);
980 snprintf(errmsg, ERRMSGSIZ, "%.*s is no V4L2 device\n", (int)sizeof(dev_name), dev_name);
981 return -1;
982 }
983 else
984 {
985 return errno_exit("VIDIOC_QUERYCAP", errmsg);
986 }
987 }
988
989 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Driver %.*s (version %u.%u.%u)", (int)sizeof(cap.driver),
990 cap.driver, (cap.version >> 16) & 0xFF, (cap.version >> 8) & 0xFF, (cap.version & 0xFF));
991 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " card: %.*s", (int)sizeof(cap.card), cap.card);
992 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " bus: %.*s", (int)sizeof(cap.bus_info), cap.bus_info);
993
996
997 // if (!(strncmp((const char *)cap.driver, "pwc", sizeof(cap.driver))))
998 // {
999 //unsigned int qual = 3;
1000 //pwc driver does not allow to get current fps with VIDIOCPWC
1001 //frameRate.numerator=1; // using default module load fps
1002 //frameRate.denominator=10;
1003 //if (ioctl(fd, VIDIOCPWCSLED, &qual)) {
1004 // DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"ioctl: can't set pwc video quality to High (uncompressed).\n");
1005 //}
1006 //else
1007 // DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG," Setting pwc video quality to High (uncompressed)\n");
1008 //setframerate=&V4L2_Base::pwcsetframerate;
1009 // }
1010
1011 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Driver capabilities:");
1012 if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
1013 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_CAPTURE");
1014 if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)
1015 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_OUTPUT");
1016 if (cap.capabilities & V4L2_CAP_VIDEO_OVERLAY)
1017 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_OVERLAY");
1018 if (cap.capabilities & V4L2_CAP_VBI_CAPTURE)
1019 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VBI_CAPTURE");
1020 if (cap.capabilities & V4L2_CAP_VBI_OUTPUT)
1021 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VBI_OUTPUT");
1022 if (cap.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE)
1023 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_SLICED_VBI_CAPTURE");
1024 if (cap.capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
1025 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_SLICED_VBI_OUTPUT");
1026 if (cap.capabilities & V4L2_CAP_RDS_CAPTURE)
1027 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_RDS_CAPTURE");
1028 if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)
1029 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_VIDEO_OUTPUT_OVERLAY");
1030 if (cap.capabilities & V4L2_CAP_TUNER)
1031 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_TUNER");
1032 if (cap.capabilities & V4L2_CAP_AUDIO)
1033 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_AUDIO");
1034 if (cap.capabilities & V4L2_CAP_RADIO)
1035 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_RADIO");
1036 if (cap.capabilities & V4L2_CAP_READWRITE)
1037 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_READWRITE");
1038 if (cap.capabilities & V4L2_CAP_ASYNCIO)
1039 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_ASYNCIO");
1040 if (cap.capabilities & V4L2_CAP_STREAMING)
1041 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " V4L2_CAP_STREAMING");
1042 /*if (cap.capabilities & V4L2_CAP_EXT_PIX_FORMAT) {
1043 has_ext_pix_format=true;
1044 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG," V4L2_CAP_EXT_PIX_FORMAT\n");
1045 }*/
1046 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
1047 {
1048 fprintf(stderr, "%.*s is no video capture device\n", (int)sizeof(dev_name), dev_name);
1049 snprintf(errmsg, ERRMSGSIZ, "%.*s is no video capture device", (int)sizeof(dev_name), dev_name);
1050 return -1;
1051 }
1052
1053 switch (io)
1054 {
1055 case IO_METHOD_READ:
1056 if (!(cap.capabilities & V4L2_CAP_READWRITE))
1057 {
1058 fprintf(stderr, "%.*s does not support read i/o", (int)sizeof(dev_name), dev_name);
1059 snprintf(errmsg, ERRMSGSIZ, "%.*s does not support read i/o", (int)sizeof(dev_name), dev_name);
1060 return -1;
1061 }
1062 break;
1063
1064 case IO_METHOD_MMAP:
1065 case IO_METHOD_USERPTR:
1066 if (!(cap.capabilities & V4L2_CAP_STREAMING))
1067 {
1068 fprintf(stderr, "%.*s does not support streaming i/o", (int)sizeof(dev_name), dev_name);
1069 snprintf(errmsg, ERRMSGSIZ, "%.*s does not support streaming i/o", (int)sizeof(dev_name), dev_name);
1070 return -1;
1071 }
1072
1073 break;
1074 }
1075
1076 /* Select video input, video standard and tune here. */
1077
1078 if (-1 == ioctl(fd, VIDIOC_G_INPUT, &input.index))
1079 {
1080 perror("VIDIOC_G_INPUT");
1081 return -1;
1082 }
1083
1084 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Enumerating available Inputs:");
1085 for (input_avail.index = 0; ioctl(fd, VIDIOC_ENUMINPUT, &input_avail) != -1; input_avail.index++)
1086 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%2d. %.*s (type %s)%s", input_avail.index,
1087 (int)sizeof(input_avail.name), input_avail.name,
1088 (input_avail.type == V4L2_INPUT_TYPE_TUNER ? "Tuner/RF Demodulator" : "Composite/S-Video"),
1089 input.index == input_avail.index ? " current" : "");
1090 if (errno != EINVAL)
1091 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "(problem enumerating inputs)");
1092 enumeratedInputs = input_avail.index;
1093
1094 /* Cropping */
1095 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1096 cancrop = true;
1097 if (-1 == XIOCTL(fd, VIDIOC_CROPCAP, &cropcap))
1098 {
1099 perror("VIDIOC_CROPCAP");
1100 crop.c.top = -1;
1101 cancrop = false;
1102 /* Errors ignored. */
1103 }
1104 if (cancrop)
1105 {
1107 " Crop capabilities: bounds = (top=%d, left=%d, width=%d, height=%d)", cropcap.bounds.top,
1108 cropcap.bounds.left, cropcap.bounds.width, cropcap.bounds.height);
1110 " Crop capabilities: defrect = (top=%d, left=%d, width=%d, height=%d)", cropcap.defrect.top,
1111 cropcap.defrect.left, cropcap.defrect.width, cropcap.defrect.height);
1112 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " Crop capabilities: pixelaspect = %d / %d",
1113 cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator);
1114 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Explicitly resetting crop area to default...");
1115 crop.c.top = cropcap.defrect.top;
1116 crop.c.left = cropcap.defrect.left;
1117 crop.c.width = cropcap.defrect.width;
1118 crop.c.height = cropcap.defrect.height;
1119 if (-1 == XIOCTL(fd, VIDIOC_S_CROP, &crop))
1120 {
1121 perror("VIDIOC_S_CROP");
1122 cancrop = false;
1123 /* Errors ignored. */
1124 }
1125 if (-1 == XIOCTL(fd, VIDIOC_G_CROP, &crop))
1126 {
1127 perror("VIDIOC_G_CROP");
1128 crop.c.top = -1;
1129 cancrop = false;
1130 /* Errors ignored. */
1131 }
1132 }
1134
1135 // Enumerating capture format
1136 {
1137 struct v4l2_fmtdesc fmt_avail;
1138 fmt_avail.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1139 //LOG_INFO("Available Capture Image formats:");
1140 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Enumerating available Capture Image formats:");
1141 for (fmt_avail.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmt_avail) != -1; fmt_avail.index++)
1142 {
1143 //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,INDI::Logger::DBG_SESSION,"\t%d. %s (%c%c%c%c) %s\n", fmt_avail.index, fmt_avail.description, (fmt_avail.pixelformat)&0xFF, (fmt_avail.pixelformat >> 8)&0xFF,
1144 // (fmt_avail.pixelformat >> 16)&0xFF, (fmt_avail.pixelformat >> 24)&0xFF, (decoder->issupportedformat(fmt_avail.pixelformat)?"supported":"UNSUPPORTED"));
1145 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%2d. Format %.*s (%c%c%c%c) is %s", fmt_avail.index,
1146 (int)sizeof(fmt_avail.description), fmt_avail.description, (fmt_avail.pixelformat) & 0xFF,
1147 (fmt_avail.pixelformat >> 8) & 0xFF, (fmt_avail.pixelformat >> 16) & 0xFF,
1148 (fmt_avail.pixelformat >> 24) & 0xFF,
1149 (decoder->issupportedformat(fmt_avail.pixelformat) ? "supported" : "UNSUPPORTED"));
1150 {
1151 // Enumerating frame sizes available for this pixel format
1152 struct v4l2_frmsizeenum frm_sizeenum;
1153 frm_sizeenum.pixel_format = fmt_avail.pixelformat;
1155 " Enumerating available Frame sizes/rates for this format:");
1156 for (frm_sizeenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMESIZES, &frm_sizeenum) != -1;
1157 frm_sizeenum.index++)
1158 {
1159 switch (frm_sizeenum.type)
1160 {
1161 case V4L2_FRMSIZE_TYPE_DISCRETE:
1163 " %2d. (Discrete) width %d x height %d\n", frm_sizeenum.index,
1164 frm_sizeenum.discrete.width, frm_sizeenum.discrete.height);
1165 break;
1166 case V4L2_FRMSIZE_TYPE_STEPWISE:
1168 " (Stepwise) min. width %d, max. width %d step width %d",
1169 frm_sizeenum.stepwise.min_width, frm_sizeenum.stepwise.max_width,
1170 frm_sizeenum.stepwise.step_width);
1172 " (Stepwise) min. height %d, max. height %d step height %d ",
1173 frm_sizeenum.stepwise.min_height, frm_sizeenum.stepwise.max_height,
1174 frm_sizeenum.stepwise.step_height);
1175 break;
1176 case V4L2_FRMSIZE_TYPE_CONTINUOUS:
1178 " (Continuous--step=1) min. width %d, max. width %d",
1179 frm_sizeenum.stepwise.min_width, frm_sizeenum.stepwise.max_width);
1181 " (Continuous--step=1) min. height %d, max. height %d ",
1182 frm_sizeenum.stepwise.min_height, frm_sizeenum.stepwise.max_height);
1183 break;
1184 default:
1185 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " Unknown Frame size type: %d\n",
1186 frm_sizeenum.type);
1187 break;
1188 }
1189 {
1190 // Enumerating frame intervals available for this frame size and this pixel format
1191 struct v4l2_frmivalenum frmi_valenum;
1192 frmi_valenum.pixel_format = fmt_avail.pixelformat;
1193 if (frm_sizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE)
1194 {
1195 frmi_valenum.width = frm_sizeenum.discrete.width;
1196 frmi_valenum.height = frm_sizeenum.discrete.height;
1197 }
1198 else
1199 {
1200 frmi_valenum.width = frm_sizeenum.stepwise.max_width;
1201 frmi_valenum.height = frm_sizeenum.stepwise.max_height;
1202 }
1203 frmi_valenum.type = 0;
1204 frmi_valenum.stepwise.min.numerator = 0;
1205 frmi_valenum.stepwise.min.denominator = 0;
1206 frmi_valenum.stepwise.max.numerator = 0;
1207 frmi_valenum.stepwise.max.denominator = 0;
1208 frmi_valenum.stepwise.step.numerator = 0;
1209 frmi_valenum.stepwise.step.denominator = 0;
1210 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, " Frame intervals:");
1211 for (frmi_valenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmi_valenum) != -1;
1212 frmi_valenum.index++)
1213 {
1214 switch (frmi_valenum.type)
1215 {
1216 case V4L2_FRMIVAL_TYPE_DISCRETE:
1218 frmi_valenum.discrete.numerator, frmi_valenum.discrete.denominator);
1219 break;
1220 case V4L2_FRMIVAL_TYPE_STEPWISE:
1223 " (Stepwise) min. %d/%ds, max. %d / %d s, step %d / %d s",
1224 frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1225 frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator,
1226 frmi_valenum.stepwise.step.numerator, frmi_valenum.stepwise.step.denominator);
1227 break;
1228 case V4L2_FRMIVAL_TYPE_CONTINUOUS:
1231 " (Continuous) min. %d / %d s, max. %d / %d s",
1232 frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1233 frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator);
1234 break;
1235 default:
1237 " Unknown Frame rate type: %d", frmi_valenum.type);
1238 break;
1239 }
1240 }
1241 if (frmi_valenum.index == 0)
1242 {
1243 perror("VIDIOC_ENUM_FRAMEINTERVALS");
1244 switch (frmi_valenum.type)
1245 {
1246 case V4L2_FRMIVAL_TYPE_DISCRETE:
1248 frmi_valenum.discrete.numerator, frmi_valenum.discrete.denominator);
1249 break;
1250 case V4L2_FRMIVAL_TYPE_STEPWISE:
1253 " (Stepwise) min. %d/%ds, max. %d / %d s, step %d / %d s",
1254 frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1255 frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator,
1256 frmi_valenum.stepwise.step.numerator, frmi_valenum.stepwise.step.denominator);
1257 break;
1258 case V4L2_FRMIVAL_TYPE_CONTINUOUS:
1261 " (Continuous) min. %d / %d s, max. %d / %d s",
1262 frmi_valenum.stepwise.min.numerator, frmi_valenum.stepwise.min.denominator,
1263 frmi_valenum.stepwise.max.numerator, frmi_valenum.stepwise.max.denominator);
1264 break;
1265 default:
1267 " Unknown Frame rate type: %d", frmi_valenum.type);
1268 break;
1269 }
1270 }
1271 //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"error %d, %s\n", errno, strerror (errno));
1272 }
1273 }
1274 }
1275 }
1276 if (errno != EINVAL)
1277 DEBUGDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Problem enumerating capture formats.");
1278 enumeratedCaptureFormats = fmt_avail.index;
1279 }
1280
1281 // CLEAR (fmt);
1282
1283 // fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1284
1285 // if (-1 == XIOCTL(fd, VIDIOC_G_FMT, &fmt))
1286 // return errno_exit ("VIDIOC_G_FMT", errmsg);
1287
1288 // fmt.fmt.pix.width = (width == -1) ? fmt.fmt.pix.width : width;
1289 // fmt.fmt.pix.height = (height == -1) ? fmt.fmt.pix.height : height;
1290 // fmt.fmt.pix.pixelformat = (pixelFormat == -1) ? fmt.fmt.pix.pixelformat : pixelFormat;
1291 // //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
1292
1293 // if (-1 == XIOCTL(fd, VIDIOC_S_FMT, &fmt))
1294 // return errno_exit ("VIDIOC_S_FMT", errmsg);
1295
1296 // /* Note VIDIOC_S_FMT may change width and height. */
1297
1298 // /* Buggy driver paranoia. */
1299 // min = fmt.fmt.pix.width * 2;
1300 // if (fmt.fmt.pix.bytesperline < min)
1301 // fmt.fmt.pix.bytesperline = min;
1302 // min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
1303 // if (fmt.fmt.pix.sizeimage < min)
1304 // fmt.fmt.pix.sizeimage = min;
1305
1306 /* Refresh the instance format with the current device format */
1307 CLEAR(fmt);
1308 return ioctl_set_format(fmt, errmsg);
1309}
1310
1311int V4L2_Base::init_device(char * errmsg)
1312{
1313 //findMinMax();
1314
1315 // decode allocBuffers();
1316
1318
1319 switch (io)
1320 {
1321 case IO_METHOD_READ:
1322 init_read(fmt.fmt.pix.sizeimage);
1323 break;
1324
1325 case IO_METHOD_MMAP:
1326 return init_mmap(errmsg);
1327 break;
1328
1329 case IO_METHOD_USERPTR:
1330 init_userp(fmt.fmt.pix.sizeimage);
1331 break;
1332 }
1333 return 0;
1334}
1335
1337{
1338 char errmsg[ERRMSGSIZ];
1339 uninit_device(errmsg);
1340
1341 if (-1 == close(fd))
1342 errno_exit("close", errmsg);
1343
1344 fd = -1;
1345}
1346
1347int V4L2_Base::open_device(const char * devpath, char * errmsg)
1348{
1349 struct stat st;
1350
1351 snprintf(dev_name, 64, "%s", devpath);
1352
1353 if (-1 == stat(dev_name, &st))
1354 {
1355 fprintf(stderr, "Cannot identify %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno, strerror(errno));
1356 snprintf(errmsg, ERRMSGSIZ, "Cannot identify %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno,
1357 strerror(errno));
1358 return -1;
1359 }
1360
1361 if (!S_ISCHR(st.st_mode))
1362 {
1363 fprintf(stderr, "%.*s is no device\n", (int)sizeof(dev_name), dev_name);
1364 snprintf(errmsg, ERRMSGSIZ, "%.*s is no device\n", (int)sizeof(dev_name), dev_name);
1365 return -1;
1366 }
1367
1368 fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
1369
1370 if (-1 == fd)
1371 {
1372 fprintf(stderr, "Cannot open %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno, strerror(errno));
1373 snprintf(errmsg, ERRMSGSIZ, "Cannot open %.*s: %d, %s\n", (int)sizeof(dev_name), dev_name, errno,
1374 strerror(errno));
1375 return -1;
1376 }
1377
1378 streamedonce = false;
1379 snprintf(errmsg, ERRMSGSIZ, "%s\n", strerror(0));
1380 return 0;
1381}
1382
1383/* @brief Storing inputs in a switch vector property, marking current as selected.
1384 *
1385 * @param inputssp is the property to store to (nothing is stored if null).
1386 */
1388{
1389 if (!inputssp)
1390 return;
1391
1392 struct v4l2_input input_avail;
1393
1394 /* Allocate inputs from previously enumerated count */
1395 size_t const inputsLen = enumeratedInputs * sizeof(ISwitch);
1396 ISwitch * inputs = (ISwitch *)malloc(inputsLen);
1397 if (!inputs)
1398 exit(EXIT_FAILURE);
1399 memset(inputs, 0, inputsLen);
1400
1401 /* Ask device about each input */
1402 for (input_avail.index = 0; (int)input_avail.index < enumeratedInputs; input_avail.index++)
1403 {
1404 /* Enumeration ends with EINVAL */
1405 if (XIOCTL(fd, VIDIOC_ENUMINPUT, &input_avail))
1406 break;
1407
1408 /* Store input description */
1409 snprintf(inputs[input_avail.index].name, MAXINDINAME, "%s", (const char *)input_avail.name);
1410 snprintf(inputs[input_avail.index].label, MAXINDILABEL, "%s", (const char *)input_avail.name);
1411 }
1412
1413 /* Free inputs before replacing */
1414 if (inputssp->sp)
1415 free(inputssp->sp);
1416
1417 /* Store inputs */
1418 inputssp->sp = inputs;
1419 inputssp->nsp = input_avail.index;
1420
1421 IUResetSwitch(inputssp);
1422
1423 /* And mark current */
1424 inputs[input.index].s = ISS_ON;
1425 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current video input is %d. %.*s", input.index,
1426 (int)sizeof(inputs[input.index].name), inputs[input.index].name);
1427}
1428
1429int V4L2_Base::setinput(unsigned int inputindex, char * errmsg)
1430{
1431 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Setting Video input to %d", inputindex);
1432 if (streamedonce)
1433 {
1434 close_device();
1435
1436 if (open_device(path, errmsg))
1437 {
1438 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%s: failed reopening device %s (%s)", __FUNCTION__, path,
1439 errmsg);
1440 return -1;
1441 }
1442 }
1443 if (-1 == XIOCTL(fd, VIDIOC_S_INPUT, &inputindex))
1444 {
1445 return errno_exit("VIDIOC_S_INPUT", errmsg);
1446 }
1447 if (-1 == XIOCTL(fd, VIDIOC_G_INPUT, &input.index))
1448 {
1449 return errno_exit("VIDIOC_G_INPUT", errmsg);
1450 }
1451 //decode reallocate_buffers=true;
1452 return 0;
1453}
1454
1455/* @brief Storing capture formats in a switch vector property, marking current as selected.
1456 *
1457 * @param captureformatssp is the property to store to (nothing is stored if null).
1458 */
1460{
1461 if (!captureformatssp)
1462 return;
1463
1464 struct v4l2_fmtdesc fmt_avail;
1465
1466 /* Allocate capture formats from preliminary enumerated count */
1467 size_t const formatsLen = enumeratedCaptureFormats * sizeof(ISwitch);
1468 ISwitch * formats = (ISwitch *)malloc(formatsLen);
1469 if (!formats)
1470 exit(EXIT_FAILURE);
1471 memset(formats, 0, formatsLen);
1472
1473 /* Ask device about each format */
1474 fmt_avail.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1475 for (fmt_avail.index = 0; (int)fmt_avail.index < enumeratedCaptureFormats; fmt_avail.index++)
1476 {
1477 /* Enumeration ends with EINVAL */
1478 if (XIOCTL(fd, VIDIOC_ENUM_FMT, &fmt_avail))
1479 break;
1480
1481 /* Store format description */
1482 snprintf(formats[fmt_avail.index].name, MAXINDINAME, "%s", (const char *)fmt_avail.description);
1483 snprintf(formats[fmt_avail.index].label, MAXINDILABEL, "%s", (const char *)fmt_avail.description);
1484
1485 /* And store pixel format for reference */
1486 /* FIXME: store pixel format as void pointer to avoid that malloc */
1487 formats[fmt_avail.index].aux = (int *)malloc(sizeof(int));
1488 if (!formats[fmt_avail.index].aux)
1489 exit(EXIT_FAILURE);
1490 *(int *)formats[fmt_avail.index].aux = fmt_avail.pixelformat;
1491 }
1492
1493 /* Free formats before replacing */
1494 if (captureformatssp->sp)
1495 free(captureformatssp->sp);
1496
1497 /* Store formats */
1498 captureformatssp->sp = formats;
1499 captureformatssp->nsp = fmt_avail.index;
1500 IUResetSwitch(captureformatssp);
1501
1502 /* And mark current */
1503 for (unsigned int i = 0; i < fmt_avail.index; i++)
1504 {
1505 if ((int)fmt.fmt.pix.pixelformat == *(int *)formats[i].aux)
1506 {
1507 formats[i].s = ISS_ON;
1508 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current capture format is %d. %c%c%c%c.", i,
1509 (fmt.fmt.pix.pixelformat) & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
1510 (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
1511 }
1512 else
1513 formats[i].s = ISS_OFF;
1514 }
1515}
1516
1517/* @brief Setting the pixel format of the capture.
1518 *
1519 * @param captureformat is the identifier of the pixel format to set.
1520 * @param errmsg is the error message to return in case of failure.
1521 * @return 0 if successful, else -1 with error message updated.
1522 */
1523int V4L2_Base::setcaptureformat(unsigned int captureformat, char * errmsg)
1524{
1525 struct v4l2_format new_fmt;
1526 CLEAR(new_fmt);
1527
1528 new_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1529 new_fmt.fmt.pix.pixelformat = captureformat;
1530
1531 return ioctl_set_format(new_fmt, errmsg);
1532}
1533
1535{
1536 struct v4l2_frmsizeenum frm_sizeenum;
1537 ISwitch * sizes = nullptr;
1538 INumber * sizevalue = nullptr;
1539 bool sizefound = false;
1540
1541 // FIXME JM 2019-01-20: This leads to always a crash
1542 // with error: corrupted size vs. prev_size
1543 // Must CHECK and FIX
1544 if (capturesizessp->sp)
1545 free(capturesizessp->sp);
1546 if (capturesizenp->np)
1547 free(capturesizenp->np);
1548
1549 frm_sizeenum.pixel_format = fmt.fmt.pix.pixelformat;
1550 //DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"\t Available Frame sizes/rates for this format:\n");
1551 for (frm_sizeenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMESIZES, &frm_sizeenum) != -1; frm_sizeenum.index++)
1552 {
1553 switch (frm_sizeenum.type)
1554 {
1555 case V4L2_FRMSIZE_TYPE_DISCRETE:
1556 sizes = (sizes == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
1557 (ISwitch *)realloc(sizes, (frm_sizeenum.index + 1) * sizeof(ISwitch));
1558
1559 snprintf(sizes[frm_sizeenum.index].name, MAXINDINAME, "%dx%d", frm_sizeenum.discrete.width,
1560 frm_sizeenum.discrete.height);
1561 snprintf(sizes[frm_sizeenum.index].label, MAXINDINAME, "%dx%d", frm_sizeenum.discrete.width,
1562 frm_sizeenum.discrete.height);
1563 sizes[frm_sizeenum.index].s = ISS_OFF;
1564 if (!sizefound)
1565 {
1566 if ((fmt.fmt.pix.width == frm_sizeenum.discrete.width) &&
1567 (fmt.fmt.pix.height == frm_sizeenum.discrete.height))
1568 {
1569 sizes[frm_sizeenum.index].s = ISS_ON;
1570 sizefound = true;
1571 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current capture size is (%d.) %dx%d",
1572 frm_sizeenum.index, frm_sizeenum.discrete.width, frm_sizeenum.discrete.height);
1573 }
1574 }
1575 break;
1576 case V4L2_FRMSIZE_TYPE_STEPWISE:
1577 case V4L2_FRMSIZE_TYPE_CONTINUOUS:
1578 sizevalue = (INumber *)malloc(2 * sizeof(INumber));
1579 IUFillNumber(sizevalue, "Width", "Width", "%.0f", frm_sizeenum.stepwise.min_width,
1580 frm_sizeenum.stepwise.max_width, frm_sizeenum.stepwise.step_width, fmt.fmt.pix.width);
1581 IUFillNumber(sizevalue + 1, "Height", "Height", "%.0f", frm_sizeenum.stepwise.min_height,
1582 frm_sizeenum.stepwise.max_height, frm_sizeenum.stepwise.step_height, fmt.fmt.pix.height);
1583 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current capture size is %dx%d", fmt.fmt.pix.width,
1584 fmt.fmt.pix.height);
1585 break;
1586 default:
1587 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Unknown Frame size type: %d", frm_sizeenum.type);
1588 break;
1589 }
1590 }
1591
1592 if (sizes != nullptr)
1593 {
1594 capturesizessp->sp = sizes;
1595 capturesizessp->nsp = frm_sizeenum.index;
1596 capturesizenp->np = nullptr;
1597 }
1598 else
1599 {
1600 capturesizenp->np = sizevalue;
1601 capturesizenp->nnp = 2;
1602 capturesizessp->sp = nullptr;
1603 }
1604}
1605
1606/* @brief Updating the capture dimensions.
1607 *
1608 * @param w is the updated width of the capture.
1609 * @param h is the update height of the capture.
1610 * @param errmsg is the returned error message in case of failure.
1611 * @return 0 if successful, else -1 with error message updated.
1612 */
1613int V4L2_Base::setcapturesize(unsigned int w, unsigned int h, char * errmsg)
1614{
1615 struct v4l2_format new_fmt = fmt;
1616
1617 new_fmt.fmt.pix.width = w;
1618 new_fmt.fmt.pix.height = h;
1619
1620 return ioctl_set_format(new_fmt, errmsg);
1621}
1622
1624{
1625 struct v4l2_frmivalenum frmi_valenum;
1626 ISwitch * rates = nullptr;
1627 INumber * ratevalue = nullptr;
1628 struct v4l2_fract frate;
1629
1630 if (frameratessp->sp)
1631 free(frameratessp->sp);
1632 if (frameratenp->np)
1633 free(frameratenp->np);
1634 frate = (this->*getframerate)();
1635
1636 bzero(&frmi_valenum, sizeof(frmi_valenum));
1637 frmi_valenum.pixel_format = fmt.fmt.pix.pixelformat;
1638 frmi_valenum.width = fmt.fmt.pix.width;
1639 frmi_valenum.height = fmt.fmt.pix.height;
1640
1641 for (frmi_valenum.index = 0; XIOCTL(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmi_valenum) != -1; frmi_valenum.index++)
1642 {
1643 switch (frmi_valenum.type)
1644 {
1645 case V4L2_FRMIVAL_TYPE_DISCRETE:
1646 rates = (rates == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
1647 (ISwitch *)realloc(rates, (frmi_valenum.index + 1) * sizeof(ISwitch));
1648 snprintf(rates[frmi_valenum.index].name, MAXINDINAME, "%d/%d", frmi_valenum.discrete.numerator,
1649 frmi_valenum.discrete.denominator);
1650 snprintf(rates[frmi_valenum.index].label, MAXINDINAME, "%d/%d", frmi_valenum.discrete.numerator,
1651 frmi_valenum.discrete.denominator);
1652 if ((frate.numerator == frmi_valenum.discrete.numerator) &&
1653 (frate.denominator == frmi_valenum.discrete.denominator))
1654 {
1655 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Current frame interval is %d/%d",
1656 frmi_valenum.discrete.numerator, frmi_valenum.discrete.denominator);
1657 rates[frmi_valenum.index].s = ISS_ON;
1658 }
1659 else
1660 rates[frmi_valenum.index].s = ISS_OFF;
1661 break;
1662 case V4L2_FRMIVAL_TYPE_STEPWISE:
1663 case V4L2_FRMIVAL_TYPE_CONTINUOUS:
1664 ratevalue = (INumber *)malloc(sizeof(INumber));
1665 IUFillNumber(ratevalue, "V4L2_FRAME_INTERVAL", "Frame Interval", "%.0f",
1666 frmi_valenum.stepwise.min.numerator / (double)frmi_valenum.stepwise.min.denominator,
1667 frmi_valenum.stepwise.max.numerator / (double)frmi_valenum.stepwise.max.denominator,
1668 frmi_valenum.stepwise.step.numerator / (double)frmi_valenum.stepwise.step.denominator,
1669 frate.numerator / (double)frate.denominator);
1670 break;
1671 default:
1672 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Unknown Frame rate type: %d", frmi_valenum.type);
1673 break;
1674 }
1675 }
1676 frameratessp->sp = nullptr;
1677 frameratessp->nsp = 0;
1678 frameratenp->np = nullptr;
1679 frameratenp->nnp = 0;
1680 if (frmi_valenum.index != 0)
1681 {
1682 if (rates != nullptr)
1683 {
1684 frameratessp->sp = rates;
1685 frameratessp->nsp = frmi_valenum.index;
1686 }
1687 else
1688 {
1689 frameratenp->np = ratevalue;
1690 frameratenp->nnp = 1;
1691 }
1692 }
1693}
1694
1695int V4L2_Base::setcroprect(int x, int y, int w, int h, char * errmsg)
1696{
1697 // Do we set a full-frame crop?
1698 if (x == 0 && y == 0 && w == (int)fmt.fmt.pix.width && h == (int)fmt.fmt.pix.height)
1699 {
1700 cropset = false;
1701 decoder->resetcrop();
1702 return 0;
1703 }
1704
1705 int const pix_width = static_cast <int> (fmt.fmt.pix.width);
1706 int const pix_height = static_cast <int> (fmt.fmt.pix.height);
1707
1708 // Clamp against frame dimensions
1709 crop.c.left = 0 <= x ? x < pix_width ? x : pix_width - 1 : 0;
1710 crop.c.top = 0 <= y ? y < pix_height ? y : pix_height - 1 : 0;
1711 crop.c.width = 0 <= w ? w < pix_width ? w : pix_width : 0;
1712 crop.c.height = 0 <= h ? h < pix_height ? h : pix_height : 0;
1713
1714 // Sanitize arguments against frame dimensions
1715 if (x + w < 0 || y + h < 0 || pix_width <= x || pix_height <= y)
1716 {
1717 strncpy(errmsg, "requested crop rectangle is outside of frame", ERRMSGSIZ);
1718 return -1;
1719 }
1720
1721 // Clip arguments against left and top of frame
1722 if (x < 0)
1723 {
1724 w = x + w;
1725 x = 0;
1726 }
1727 if (y < 0)
1728 {
1729 h = y + h;
1730 y = 0;
1731 }
1732
1733 // Clip arguments against right and bottom of frame
1734 if (0 + pix_width < x + w)
1735 {
1736 w = pix_width - x;
1737 }
1738 if (0 + pix_height < y + h)
1739 {
1740 h = pix_height - y;
1741 }
1742
1743 // We have our required crop
1744 struct v4l2_crop software_crop;
1745 software_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1746 software_crop.c.left = x;
1747 software_crop.c.top = y;
1748 software_crop.c.width = w;
1749 software_crop.c.height = h;
1750
1751 // If the hardware is able to crop, request the kernel driver
1752 if (cancrop)
1753 {
1754 // We need to adjust our hardware crop to driver-level V4L2 capabilities
1755 struct v4l2_crop hardware_crop = software_crop;
1756
1757 // Make sure vertical coordinate is on an even line
1758 if (y % 2)
1759 {
1760 hardware_crop.c.top--;
1761 hardware_crop.c.height++;
1762 }
1763
1764 // Make sure we crop an even number of horizontal lines
1765 if (h % 2)
1766 {
1767 hardware_crop.c.height++;
1768 }
1769
1770 // Ask the hardware to crop - don't fail, fallback to software cropping if not possible
1771 if (-1 == XIOCTL(fd, VIDIOC_S_CROP, &hardware_crop))
1772 {
1774 "Failed V4L2 hardware crop request 0x%08X (%dx%d at (%d, %d)), falling back to software crop",
1775 (unsigned int)VIDIOC_S_CROP, hardware_crop.c.width, hardware_crop.c.height,
1776 hardware_crop.c.left, hardware_crop.c.top);
1777 //return errno_exit("VIDIOC_S_CROP", errmsg);
1778 }
1779 else if (-1 == XIOCTL(fd, VIDIOC_G_CROP, &hardware_crop))
1780 {
1781 //return errno_exit("VIDIOC_G_CROP", errmsg);
1782 }
1783 else
1784 {
1785 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_SESSION, "V4L2 hardware crop request 0x%08X accepted as %dx%d at (%d, %d)",
1786 (unsigned int)VIDIOC_S_CROP, hardware_crop.c.width, hardware_crop.c.height,
1787 hardware_crop.c.left, hardware_crop.c.top);
1788 }
1789 }
1790
1791 // In any case, request the decoder to crop
1792 bool const softcrop = decoder->setcrop(software_crop);
1793
1794 if (!softcrop && !cancrop)
1795 {
1796 cropset = false;
1797 strncpy(errmsg, "No hardware and software cropping for this format", ERRMSGSIZ);
1798 return -1;
1799 }
1800
1801 // Crop request is set, store state
1802 cropset = true;
1803 crop = software_crop;
1804
1805 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "V4L2 base setcroprect %dx%d at (%d, %d)", crop.c.width,
1806 crop.c.height, crop.c.left, crop.c.top);
1807 return 0;
1808}
1809
1811{
1812 if (cropset)
1813 return crop.c.width;
1814 else
1815 return fmt.fmt.pix.width;
1816}
1817
1819{
1820 if (cropset)
1821 return crop.c.height;
1822 else
1823 return fmt.fmt.pix.height;
1824}
1825
1827{
1828 return bpp;
1829}
1830
1832{
1833 return fmt.fmt.pix.pixelformat;
1834}
1835
1836struct v4l2_rect V4L2_Base::getcroprect()
1837{
1838 return crop.c;
1839}
1840
1841int V4L2_Base::stdsetframerate(struct v4l2_fract frate, char * errmsg)
1842{
1843 struct v4l2_streamparm sparm;
1844 //if (!cansetrate) {sprintf(errmsg, "Can not set rate"); return -1;}
1845 bzero(&sparm, sizeof(struct v4l2_streamparm));
1846 sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1847 sparm.parm.capture.timeperframe = frate;
1848 if (-1 == XIOCTL(fd, VIDIOC_S_PARM, &sparm))
1849 {
1850 //cansetrate=false;
1851 return errno_exit("VIDIOC_S_PARM", errmsg);
1852 }
1853 return 0;
1854}
1855
1856/* @brief Setting the framerate for Philips-based PWC devices.
1857 *
1858 * @param frate is the v4l2_fract structure defining framerate.
1859 * @param errmsg is the returned error message in case of error.
1860 * @return 0 if successful, else -1 with error message updated.
1861 */
1862int V4L2_Base::pwcsetframerate(struct v4l2_fract frate, char * errmsg)
1863{
1864 int const fps = frate.denominator / frate.numerator;
1865
1866 struct v4l2_format new_fmt = fmt;
1867 new_fmt.fmt.pix.priv |= (fps << PWC_FPS_SHIFT);
1868
1869 if (-1 == ioctl_set_format(new_fmt, errmsg))
1870 return errno_exit("pwcsetframerate", errmsg);
1871
1872 frameRate = frate;
1873
1874 return 0;
1875}
1876
1877struct v4l2_fract V4L2_Base::stdgetframerate()
1878{
1879 struct v4l2_streamparm sparm;
1880 //if (!cansetrate) return frameRate;
1881 bzero(&sparm, sizeof(struct v4l2_streamparm));
1882 sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1883 if (-1 == XIOCTL(fd, VIDIOC_G_PARM, &sparm))
1884 {
1885 perror("VIDIOC_G_PARM");
1886 }
1887 else
1888 {
1889 frameRate = sparm.parm.capture.timeperframe;
1890 }
1891 //if (!(sparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) cansetrate=false;
1892
1893 return frameRate;
1894}
1895
1897{
1898 return ((char *)cap.card);
1899}
1900
1901void V4L2_Base::getMaxMinSize(int &x_max, int &y_max, int &x_min, int &y_min)
1902{
1903 x_max = xmax;
1904 y_max = ymax;
1905 x_min = xmin;
1906 y_min = ymin;
1907}
1908
1909/* @brief Setting the dimensions of the capture frame.
1910 *
1911 * @param x is the width of the capture frame.
1912 * @param y is the height of the capture frame.
1913 * @return 0 if successful, else -1.
1914 */
1915int V4L2_Base::setSize(int x, int y)
1916{
1917 char errmsg[ERRMSGSIZ];
1918 struct v4l2_format new_fmt = fmt;
1919
1920 new_fmt.fmt.pix.width = x;
1921 new_fmt.fmt.pix.height = y;
1922
1923 if (-1 == ioctl_set_format(new_fmt, errmsg))
1924 return -1;
1925
1926 /* PWC bug? It seems that setting the "wrong" width and height will mess something in the driver.
1927 Only 160x120, 320x280, and 640x480 are accepted. If I try to set it for example to 300x200, it wii
1928 get set to 320x280, which is fine, but then the video information is messed up for some reason. */
1929 // XIOCTL(fd, VIDIOC_S_FMT, &fmt);
1930
1931 //allocBuffers();
1932
1933 return 0;
1934}
1935
1936void V4L2_Base::setColorProcessing(bool quantization, bool colorconvert, bool linearization)
1937{
1938 INDI_UNUSED(colorconvert);
1939 decoder->setQuantization(quantization);
1940 decoder->setLinearization(linearization);
1941 bpp = decoder->getBpp();
1942}
1943
1944unsigned char * V4L2_Base::getY()
1945{
1946 return decoder->getY();
1947}
1948
1949unsigned char * V4L2_Base::getU()
1950{
1951 return decoder->getU();
1952}
1953
1954unsigned char * V4L2_Base::getV()
1955{
1956 return decoder->getV();
1957}
1958
1959unsigned char * V4L2_Base::getMJPEGBuffer(int &size)
1960{
1961 return decoder->getMJPEGBuffer(size);
1962}
1963
1965{
1966 return decoder->getRGBBuffer();
1967}
1968
1970{
1971 return decoder->getLinearY();
1972}
1973
1975{
1976 callback = fp;
1977 uptr = ud;
1978}
1979
1981{
1982 char errmsg[ERRMSGSIZ];
1983 struct v4l2_format tryfmt;
1984 CLEAR(tryfmt);
1985
1986 xmin = xmax = fmt.fmt.pix.width;
1987 ymin = ymax = fmt.fmt.pix.height;
1988
1989 tryfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1990 tryfmt.fmt.pix.width = 10;
1991 tryfmt.fmt.pix.height = 10;
1992 tryfmt.fmt.pix.pixelformat = fmt.fmt.pix.pixelformat;
1993 tryfmt.fmt.pix.field = fmt.fmt.pix.field;
1994
1995 if (-1 == XIOCTL(fd, VIDIOC_TRY_FMT, &tryfmt))
1996 {
1997 errno_exit("VIDIOC_TRY_FMT 1", errmsg);
1998 return;
1999 }
2000
2001 xmin = tryfmt.fmt.pix.width;
2002 ymin = tryfmt.fmt.pix.height;
2003
2004 tryfmt.fmt.pix.width = 1600;
2005 tryfmt.fmt.pix.height = 1200;
2006
2007 if (-1 == XIOCTL(fd, VIDIOC_TRY_FMT, &tryfmt))
2008 {
2009 errno_exit("VIDIOC_TRY_FMT 2", errmsg);
2010 return;
2011 }
2012
2013 xmax = tryfmt.fmt.pix.width;
2014 ymax = tryfmt.fmt.pix.height;
2015
2016 cerr << "Min X: " << xmin << " - Max X: " << xmax << " - Min Y: " << ymin << " - Max Y: " << ymax << endl;
2017}
2018
2020{
2021 char errmsg[ERRMSGSIZ];
2023
2024 for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++)
2025 {
2026 if (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2027 {
2028 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2029 {
2030 cerr << "DISABLED--Control " << queryctrl.name << endl;
2031 continue;
2032 }
2033 cerr << "Control " << queryctrl.name << endl;
2034#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2035 if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2036#else
2037 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2038#endif
2040 if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2041 cerr << " boolean" << endl;
2042 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2043 cerr << " integer" << endl;
2044 //if (queryctrl.type == V4L2_CTRL_TYPE_BITMASK) //not in < 3.1
2045 // cerr << " bitmask" <<endl;
2046 if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2047 cerr << " button" << endl;
2048 }
2049 else
2050 {
2051 if (errno == EINVAL)
2052 continue;
2053
2054 errno_exit("VIDIOC_QUERYCTRL", errmsg);
2055 return;
2056 }
2057 }
2058
2059 for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++)
2060 {
2061 if (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2062 {
2063 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2064 {
2065 cerr << "DISABLED--Private Control " << queryctrl.name << endl;
2066 continue;
2067 }
2068
2069 cerr << "Private Control " << queryctrl.name << endl;
2070#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2071 if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2072#else
2073 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2074#endif
2076 if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2077 cerr << " boolean" << endl;
2078 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2079 cerr << " integer" << endl;
2080 //if (queryctrl.type == V4L2_CTRL_TYPE_BITMASK)
2081 // cerr << " bitmask" <<endl;
2082 if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2083 cerr << " button" << endl;
2084 }
2085 else
2086 {
2087 if (errno == EINVAL)
2088 break;
2089
2090 errno_exit("VIDIOC_QUERYCTRL", errmsg);
2091 return;
2092 }
2093 }
2094}
2095
2097{
2098#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2099 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2100 cerr << " Menu items:" << endl;
2101 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2102 cerr << " Integer Menu items:" << endl;
2103#else
2104 cerr << " Menu items:" << endl;
2105#endif
2106
2108 querymenu.id = queryctrl.id;
2109
2110 for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2111 {
2112 if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2113 {
2114 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2115 cerr << " " << querymenu.name << endl;
2116#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2117 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2118 {
2119 char menuname[19];
2120 menuname[18] = '\0';
2121 snprintf(menuname, 19, "0x%016" PRIX64, static_cast<int64_t>(querymenu.value));
2122 cerr << " " << menuname << endl;
2123 }
2124#endif
2125 }
2126 //else
2127 //{
2128 // errno_exit("VIDIOC_QUERYMENU", errmsg);
2129 // return;
2130 //}
2131 }
2132}
2133
2134int V4L2_Base::query_ctrl(unsigned int ctrl_id, double &ctrl_min, double &ctrl_max, double &ctrl_step,
2135 double &ctrl_value, char * errmsg)
2136{
2137 struct v4l2_control control;
2138
2140
2141 queryctrl.id = ctrl_id;
2142
2143 if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2144 {
2145 if (errno != EINVAL)
2146 return errno_exit("VIDIOC_QUERYCTRL", errmsg);
2147
2148 else
2149 {
2150 cerr << "#" << ctrl_id << " is not supported" << endl;
2151 snprintf(errmsg, ERRMSGSIZ, "# %d is not supported", ctrl_id);
2152 return -1;
2153 }
2154 }
2155 else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2156 {
2157 cerr << "#" << ctrl_id << " is disabled" << endl;
2158 snprintf(errmsg, ERRMSGSIZ, "# %d is disabled", ctrl_id);
2159 return -1;
2160 }
2161
2162 ctrl_min = queryctrl.minimum;
2163 ctrl_max = queryctrl.maximum;
2164 ctrl_step = queryctrl.step;
2165 ctrl_value = queryctrl.default_value;
2166
2167 /* Get current value */
2168 CLEAR(control);
2169 control.id = ctrl_id;
2170
2171 if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2172 ctrl_value = control.value;
2173
2174 cerr << queryctrl.name << " -- min: " << ctrl_min << " max: " << ctrl_max << " step: " << ctrl_step
2175 << " value: " << ctrl_value << endl;
2176
2177 return 0;
2178}
2179
2180void V4L2_Base::queryControls(INumberVectorProperty * nvp, unsigned int * nnumber, ISwitchVectorProperty ** options,
2181 unsigned int * noptions, const char * dev, const char * group)
2182{
2183 struct v4l2_control control;
2184
2185 INumber * numbers = nullptr;
2186 unsigned int * num_ctrls = nullptr;
2187 int nnum = 0;
2188 ISwitchVectorProperty * opt = nullptr;
2189 unsigned int nopt = 0;
2190 char optname[] = "OPT000";
2191 char swonname[] = "SET_OPT000";
2192 char swoffname[] = "UNSET_OPT000";
2193 char menuname[] = "MENU000";
2194 char menuoptname[] = "MENU000_OPT000";
2195 *noptions = 0;
2196 *nnumber = 0;
2197
2199
2200 for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++)
2201 {
2202 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2203 {
2204 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2205 {
2206 cerr << queryctrl.name << " is disabled." << endl;
2207 continue;
2208 }
2209
2210 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2211 {
2212 numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2213 (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2214
2215 num_ctrls = (num_ctrls == nullptr) ?
2216 (unsigned int *)malloc(sizeof(unsigned int)) :
2217 (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2218
2219 snprintf(numbers[nnum].name, MAXINDINAME, "%s", (const char *)entityXML((char *)queryctrl.name));
2220 snprintf(numbers[nnum].label, MAXINDILABEL, "%s", (const char *)entityXML((char *)queryctrl.name));
2221 strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2222 numbers[nnum].min = queryctrl.minimum;
2223 numbers[nnum].max = queryctrl.maximum;
2224 numbers[nnum].step = queryctrl.step;
2225 numbers[nnum].value = queryctrl.default_value;
2226
2227 /* Get current value if possible */
2228 CLEAR(control);
2229 control.id = queryctrl.id;
2230 if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2231 numbers[nnum].value = control.value;
2232
2233 /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2234 num_ctrls[nnum] = queryctrl.id;
2235
2236 cerr << "Adding " << queryctrl.name << " -- min: " << queryctrl.minimum << " max: " << queryctrl.maximum
2237 << " step: " << queryctrl.step << " value: " << numbers[nnum].value << endl;
2238
2239 nnum++;
2240 }
2241 if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2242 {
2243 ISwitch * sw = (ISwitch *)malloc(2 * sizeof(ISwitch));
2244 snprintf(optname + 3, 4, "%03d", nopt);
2245 snprintf(swonname + 7, 4, "%03d", nopt);
2246 snprintf(swoffname + 9, 4, "%03d", nopt);
2247
2248 opt = (opt == nullptr) ?
2249 (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2250 (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2251
2252 CLEAR(control);
2253 control.id = queryctrl.id;
2254 XIOCTL(fd, VIDIOC_G_CTRL, &control);
2255
2256 IUFillSwitch(sw, swonname, "Off", (control.value ? ISS_OFF : ISS_ON));
2257 IUFillSwitch(sw + 1, swoffname, "On", (control.value ? ISS_ON : ISS_OFF));
2258 queryctrl.name[31] = '\0';
2259 IUFillSwitchVector(&opt[nopt], sw, 2, dev, optname, (const char *)entityXML((char *)queryctrl.name),
2260 group, IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2261 opt[nopt].aux = malloc(sizeof(unsigned int));
2262 *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2263
2264 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding switch %.*s (%s)\n",
2265 (int)sizeof(queryctrl.name), queryctrl.name, (control.value ? "On" : "Off"));
2266 nopt += 1;
2267 }
2268#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2269 if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2270#else
2271 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2272#endif
2273 {
2274 ISwitch * sw = nullptr;
2275 unsigned int nmenuopt = 0;
2276 char sname[32];
2277 snprintf(menuname + 4, 4, "%03d", nopt);
2278 snprintf(menuoptname + 4, 4, "%03d", nopt);
2279 menuoptname[7] = '_';
2280
2281 opt = (opt == nullptr) ?
2282 (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2283 (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2284
2285 CLEAR(control);
2286 control.id = queryctrl.id;
2287 XIOCTL(fd, VIDIOC_G_CTRL, &control);
2288
2290 querymenu.id = queryctrl.id;
2291
2292 for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2293 {
2294 if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2295 {
2296 sw = (sw == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
2297 (ISwitch *)realloc(sw, (nmenuopt + 1) * sizeof(ISwitch));
2298 snprintf(menuoptname + 11, 4, "%03d", nmenuopt);
2299#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2300 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2301 {
2302 snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2303 sname[31] = '\0';
2304 }
2305 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2306 {
2307 snprintf(sname, 19, "0x%016" PRIX64, static_cast<int64_t>(querymenu.value));
2308 sname[31] = '\0';
2309 }
2310#else
2311 snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2312 sname[31] = '\0';
2313#endif
2314 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu item %.*s %.*s item %d",
2315 (int)sizeof(sname), sname, (int)sizeof(menuoptname), menuoptname, nmenuopt);
2316 //IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)sname, (control.value==nmenuopt?ISS_ON:ISS_OFF));
2317 IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)entityXML((char *)sname),
2318 (control.value == (int)nmenuopt ? ISS_ON : ISS_OFF));
2319 nmenuopt += 1;
2320 }
2321 else
2322 {
2323 //errno_exit("VIDIOC_QUERYMENU", errmsg);
2324 //exit(3);
2325 }
2326 }
2327
2328 queryctrl.name[31] = '\0';
2329 IUFillSwitchVector(&opt[nopt], sw, nmenuopt, dev, menuname,
2330 (const char *)entityXML((char *)queryctrl.name), group, IP_RW, ISR_1OFMANY, 0.0,
2331 IPS_IDLE);
2332 opt[nopt].aux = malloc(sizeof(unsigned int));
2333 *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2334
2335 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu %.*s (item %d set)",
2336 (int)sizeof(queryctrl.name), queryctrl.name, control.value);
2337 nopt += 1;
2338 }
2339 }
2340 else
2341 {
2342 if (errno != EINVAL)
2343 {
2344 if (numbers)
2345 free(numbers);
2346 if (opt)
2347 free(opt);
2348 perror("VIDIOC_QUERYCTRL");
2349 return;
2350 }
2351 }
2352 }
2353
2354 for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++)
2355 {
2356 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2357 {
2358 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2359 {
2360 cerr << queryctrl.name << " is disabled." << endl;
2361 continue;
2362 }
2363
2364 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2365 {
2366 numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2367 (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2368
2369 num_ctrls = (num_ctrls == nullptr) ?
2370 (unsigned int *)malloc(sizeof(unsigned int)) :
2371 (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2372
2373 snprintf(numbers[nnum].name, MAXINDINAME, "%s", (const char *)entityXML((char *)queryctrl.name));
2374 snprintf(numbers[nnum].label, MAXINDILABEL, "%s", (const char *)entityXML((char *)queryctrl.name));
2375 strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2376 numbers[nnum].min = queryctrl.minimum;
2377 numbers[nnum].max = queryctrl.maximum;
2378 numbers[nnum].step = queryctrl.step;
2379 numbers[nnum].value = queryctrl.default_value;
2380
2381 /* Get current value if possible */
2382 CLEAR(control);
2383 control.id = queryctrl.id;
2384 if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2385 numbers[nnum].value = control.value;
2386
2387 /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2388 num_ctrls[nnum] = queryctrl.id;
2389 cerr << "Adding ext. " << queryctrl.name << " -- min: " << queryctrl.minimum
2390 << " max: " << queryctrl.maximum << " step: " << queryctrl.step
2391 << " value: " << numbers[nnum].value << endl;
2392
2393 nnum++;
2394 }
2395 if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2396 {
2397 ISwitch * sw = (ISwitch *)malloc(2 * sizeof(ISwitch));
2398 snprintf(optname + 3, 4, "%03d", nopt);
2399 snprintf(swonname + 7, 4, "%03d", nopt);
2400 snprintf(swoffname + 9, 4, "%03d", nopt);
2401
2402 opt = (opt == nullptr) ?
2403 (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2404 (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2405
2406 CLEAR(control);
2407 control.id = queryctrl.id;
2408 XIOCTL(fd, VIDIOC_G_CTRL, &control);
2409
2410 IUFillSwitch(sw, swonname, "On", (control.value ? ISS_ON : ISS_OFF));
2411 IUFillSwitch(sw + 1, swoffname, "Off", (control.value ? ISS_OFF : ISS_ON));
2412 queryctrl.name[31] = '\0';
2413 IUFillSwitchVector(&opt[nopt], sw, 2, dev, optname, (const char *)entityXML((char *)queryctrl.name),
2414 group, IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2415
2416 opt[nopt].aux = malloc(sizeof(unsigned int));
2417 *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2418
2419 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding ext. switch %.*s (%s)\n",
2420 (int)sizeof(queryctrl.name), queryctrl.name, (control.value ? "On" : "Off"));
2421 nopt += 1;
2422 }
2423#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2424 if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2425#else
2426 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2427#endif
2428 {
2429 ISwitch * sw = nullptr;
2430 unsigned int nmenuopt = 0;
2431 char sname[32];
2432 snprintf(menuname + 4, 4, "%03d", nopt);
2433 snprintf(menuoptname + 4, 4, "%03d", nopt);
2434 menuoptname[7] = '_';
2435
2436 opt = (opt == nullptr) ?
2437 (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2438 (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2439
2440 CLEAR(control);
2441 control.id = queryctrl.id;
2442 XIOCTL(fd, VIDIOC_G_CTRL, &control);
2443
2445 querymenu.id = queryctrl.id;
2446
2447 for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2448 {
2449 if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2450 {
2451 sw = (sw == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
2452 (ISwitch *)realloc(sw, (nmenuopt + 1) * sizeof(ISwitch));
2453 snprintf(menuoptname + 11, 4, "%03d", nmenuopt);
2454#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2455 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2456 {
2457 snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2458 sname[31] = '\0';
2459 }
2460 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2461 {
2462 snprintf(sname, 19, "0x%016" PRIX64, static_cast<int64_t>(querymenu.value));
2463 sname[31] = '\0';
2464 }
2465#else
2466 snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2467 sname[31] = '\0';
2468#endif
2469 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu item %.*s %.*s item %d",
2470 (int)sizeof(sname), sname, (int)sizeof(menuoptname), menuoptname, nmenuopt);
2471 //IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)sname, (control.value==nmenuopt?ISS_ON:ISS_OFF));
2472 IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)entityXML((char *)querymenu.name),
2473 (control.value == (int)nmenuopt ? ISS_ON : ISS_OFF));
2474 nmenuopt += 1;
2475 }
2476 else
2477 {
2478 //errno_exit("VIDIOC_QUERYMENU", errmsg);
2479 //exit(3);
2480 }
2481 }
2482
2483 queryctrl.name[31] = '\0';
2484 IUFillSwitchVector(&opt[nopt], sw, nmenuopt, dev, menuname,
2485 (const char *)entityXML((char *)queryctrl.name), group, IP_RW, ISR_1OFMANY, 0.0,
2486 IPS_IDLE);
2487
2488 opt[nopt].aux = malloc(sizeof(unsigned int));
2489 *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2490
2491 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding ext. menu %.*s (item %d set)",
2492 (int)sizeof(queryctrl.name), queryctrl.name, control.value);
2493 nopt += 1;
2494 }
2495 }
2496 else
2497 break;
2498 }
2499
2500 /* Store numbers in aux0 */
2501 for (int i = 0; i < nnum; i++)
2502 numbers[i].aux0 = &num_ctrls[i];
2503
2504 nvp->np = numbers;
2505 nvp->nnp = nnum;
2506 *nnumber = nnum;
2507
2508 *options = opt;
2509 *noptions = nopt;
2510}
2511
2513{
2514 struct v4l2_control control;
2515 char errmsg[ERRMSGSIZ];
2517 INumber * numbers = nullptr;
2518 unsigned int * num_ctrls = nullptr;
2519 int nnum = 0;
2520
2521 for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++)
2522 {
2523 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2524 {
2525 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2526 {
2527 cerr << queryctrl.name << " is disabled." << endl;
2528 continue;
2529 }
2530
2531 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2532 {
2533 numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2534 (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2535
2536 num_ctrls = (num_ctrls == nullptr) ?
2537 (unsigned int *)malloc(sizeof(unsigned int)) :
2538 (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2539
2540 snprintf(numbers[nnum].name, MAXINDINAME, "%s", (const char *)queryctrl.name);
2541 snprintf(numbers[nnum].label, MAXINDILABEL, "%s", (const char *)queryctrl.name);
2542 strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2543 numbers[nnum].min = queryctrl.minimum;
2544 numbers[nnum].max = queryctrl.maximum;
2545 numbers[nnum].step = queryctrl.step;
2546 numbers[nnum].value = queryctrl.default_value;
2547
2548 /* Get current value if possible */
2549 CLEAR(control);
2550 control.id = queryctrl.id;
2551 if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2552 numbers[nnum].value = control.value;
2553
2554 /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2555 num_ctrls[nnum] = queryctrl.id;
2556
2557 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%.*s -- min: %d max: %d step: %d value: %d",
2558 (int)sizeof(queryctrl.name), queryctrl.name, queryctrl.minimum, queryctrl.maximum,
2559 queryctrl.step, numbers[nnum].value);
2560
2561 nnum++;
2562 }
2563 }
2564 else if (errno != EINVAL)
2565 {
2566 if (numbers)
2567 free(numbers);
2568 return errno_exit("VIDIOC_QUERYCTRL", errmsg);
2569 }
2570 }
2571
2572 for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++)
2573 {
2574 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2575 {
2576 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2577 {
2578 cerr << queryctrl.name << " is disabled." << endl;
2579 continue;
2580 }
2581
2582 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2583 {
2584 numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2585 (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2586
2587 num_ctrls = (num_ctrls == nullptr) ?
2588 (unsigned int *)malloc(sizeof(unsigned int)) :
2589 (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2590
2591 snprintf(numbers[nnum].name, MAXINDINAME, "%s", (const char *)queryctrl.name);
2592 snprintf(numbers[nnum].label, MAXINDILABEL, "%s", (const char *)queryctrl.name);
2593 strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2594 numbers[nnum].min = queryctrl.minimum;
2595 numbers[nnum].max = queryctrl.maximum;
2596 numbers[nnum].step = queryctrl.step;
2597 numbers[nnum].value = queryctrl.default_value;
2598
2599 /* Get current value if possible */
2600 CLEAR(control);
2601 control.id = queryctrl.id;
2602 if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2603 numbers[nnum].value = control.value;
2604
2605 /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2606 num_ctrls[nnum] = queryctrl.id;
2607
2608 nnum++;
2609 }
2610 }
2611 else
2612 break;
2613 }
2614
2615 /* Store numbers in aux0 */
2616 for (int i = 0; i < nnum; i++)
2617 numbers[i].aux0 = &num_ctrls[i];
2618
2619 nvp->np = numbers;
2620 nvp->nnp = nnum;
2621
2622 return nnum;
2623}
2624
2625int V4L2_Base::getControl(unsigned int ctrl_id, double * value, char * errmsg)
2626{
2627 struct v4l2_control control;
2628
2629 CLEAR(control);
2630 control.id = ctrl_id;
2631
2632 if (-1 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2633 return errno_exit("VIDIOC_G_CTRL", errmsg);
2634 *value = (double)control.value;
2635 return 0;
2636}
2637
2638int V4L2_Base::setINTControl(unsigned int ctrl_id, double new_value, char * errmsg)
2639{
2640 struct v4l2_control control;
2641
2643
2644 queryctrl.id = ctrl_id;
2645 if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2646 return 0;
2647 if ((queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) || (queryctrl.flags & V4L2_CTRL_FLAG_GRABBED) ||
2648 (queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE) || (queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE))
2649 {
2650 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_WARNING, "Setting INT control %.*s will fail, currently %s%s%s%s",
2651 (int)sizeof(queryctrl.name), queryctrl.name,
2652 queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY ? "read only " : "",
2653 queryctrl.flags & V4L2_CTRL_FLAG_GRABBED ? "grabbed " : "",
2654 queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE ? "inactive " : "",
2655 queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE ? "volatile" : "");
2656 return 0;
2657 }
2658
2659 CLEAR(control);
2660
2661 //cerr << "The id is " << ctrl_id << " new value is " << new_value << endl;
2662
2663 control.id = ctrl_id;
2664 control.value = (int)new_value;
2665 if (-1 == XIOCTL(fd, VIDIOC_S_CTRL, &control))
2666 {
2667 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_ERROR, "Setting INT control %.*s failed (%s)",
2668 (int)sizeof(queryctrl.name), queryctrl.name, errmsg);
2669 return errno_exit("VIDIOC_S_CTRL", errmsg);
2670 }
2671 return 0;
2672}
2673
2674int V4L2_Base::setOPTControl(unsigned int ctrl_id, unsigned int new_value, char * errmsg)
2675{
2676 struct v4l2_control control;
2677
2679
2680 queryctrl.id = ctrl_id;
2681 if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2682 return 0;
2683 if ((queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) || (queryctrl.flags & V4L2_CTRL_FLAG_GRABBED) ||
2684 (queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE) || (queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE))
2685 {
2686 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Setting OPT control %.*s will fail, currently %s%s%s%s",
2687 (int)sizeof(queryctrl.name), queryctrl.name,
2688 queryctrl.flags & V4L2_CTRL_FLAG_READ_ONLY ? "read only " : "",
2689 queryctrl.flags & V4L2_CTRL_FLAG_GRABBED ? "grabbed " : "",
2690 queryctrl.flags & V4L2_CTRL_FLAG_INACTIVE ? "inactive " : "",
2691 queryctrl.flags & V4L2_CTRL_FLAG_VOLATILE ? "volatile" : "");
2692 return 0;
2693 }
2694
2695 CLEAR(control);
2696
2697 //cerr << "The id is " << ctrl_id << " new value is " << new_value << endl;
2698
2699 control.id = ctrl_id;
2700 control.value = new_value;
2701 if (-1 == XIOCTL(fd, VIDIOC_S_CTRL, &control))
2702 {
2703 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_ERROR, "Setting INT control %.*s failed (%s)",
2704 (int)sizeof(queryctrl.name), queryctrl.name, errmsg);
2705 return errno_exit("VIDIOC_S_CTRL", errmsg);
2706 }
2707 return 0;
2708}
2709
2711{
2712 //struct v4l2_queryctrl queryctrl;
2713
2715
2716 queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2717 if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2718 return false;
2719
2720 queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2721 while (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2722 {
2723 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2724 {
2725 cerr << "DISABLED--Control " << queryctrl.name << endl;
2726 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2727 continue;
2728 }
2729
2730 if (queryctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS)
2731 {
2732 cerr << "Control Class " << queryctrl.name << endl;
2733 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2734 continue;
2735 }
2736
2737 cerr << "Control " << queryctrl.name << endl;
2738
2739#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2740 if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2741#else
2742 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2743#endif
2745 if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2746 cerr << " boolean" << endl;
2747 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2748 cerr << " integer" << endl;
2749 //if (queryctrl.type == V4L2_CTRL_TYPE_BITMASK) //not in < 3.1
2750 // cerr << " bitmask" <<endl;
2751 if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2752 cerr << " button" << endl;
2753 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2754 }
2755 return true;
2756}
2757
2758// TODO free opt/menu aux placeholders
2759
2760bool V4L2_Base::queryExtControls(INumberVectorProperty * nvp, unsigned int * nnumber, ISwitchVectorProperty ** options,
2761 unsigned int * noptions, const char * dev, const char * group)
2762{
2763 struct v4l2_control control;
2764
2765 INumber * numbers = nullptr;
2766 unsigned int * num_ctrls = nullptr;
2767 int nnum = 0;
2768 ISwitchVectorProperty * opt = nullptr;
2769 unsigned int nopt = 0;
2770 char optname[] = "OPT000";
2771 char swonname[] = "SET_OPT000";
2772 char swoffname[] = "UNSET_OPT000";
2773 char menuname[] = "MENU000";
2774 char menuoptname[] = "MENU000_OPT000";
2775 *noptions = 0;
2776 *nnumber = 0;
2777
2779
2780 queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2781 if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl))
2782 return false;
2783
2785 queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
2786 while (0 == XIOCTL(fd, VIDIOC_QUERYCTRL, &queryctrl))
2787 {
2788 if (queryctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS)
2789 {
2790 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Control Class %.*s", (int)sizeof(queryctrl.name),
2791 queryctrl.name);
2792 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2793 continue;
2794 }
2795
2796 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
2797 {
2798 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "%.*s is disabled.", (int)sizeof(queryctrl.name),
2799 queryctrl.name);
2800 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2801 continue;
2802 }
2803
2804 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
2805 {
2806 numbers = (numbers == nullptr) ? (INumber *)malloc(sizeof(INumber)) :
2807 (INumber *)realloc(numbers, (nnum + 1) * sizeof(INumber));
2808
2809 num_ctrls = (num_ctrls == nullptr) ? (unsigned int *)malloc(sizeof(unsigned int)) :
2810 (unsigned int *)realloc(num_ctrls, (nnum + 1) * sizeof(unsigned int));
2811
2812 snprintf(numbers[nnum].name, MAXINDINAME, "%s", (const char *)entityXML((char *)queryctrl.name));
2813 snprintf(numbers[nnum].label, MAXINDILABEL, "%s", (const char *)entityXML((char *)queryctrl.name));
2814 strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT);
2815 numbers[nnum].min = queryctrl.minimum;
2816 numbers[nnum].max = queryctrl.maximum;
2817 numbers[nnum].step = queryctrl.step;
2818 numbers[nnum].value = queryctrl.default_value;
2819
2820 /* Get current value if possible */
2821 CLEAR(control);
2822 control.id = queryctrl.id;
2823 if (0 == XIOCTL(fd, VIDIOC_G_CTRL, &control))
2824 numbers[nnum].value = control.value;
2825
2826 /* Store ID info in INumber. This is the first time ever I make use of aux0!! */
2827 num_ctrls[nnum] = queryctrl.id;
2828
2829 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding %.*s -- min: %d max: %d step: %d value: %d",
2830 (int)sizeof(queryctrl.name), queryctrl.name, queryctrl.minimum, queryctrl.maximum,
2831 queryctrl.step, numbers[nnum].value);
2832
2833 nnum++;
2834 }
2835 if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
2836 {
2837 ISwitch * sw = (ISwitch *)malloc(2 * sizeof(ISwitch));
2838 snprintf(optname + 3, 4, "%03d", nopt);
2839 snprintf(swonname + 7, 4, "%03d", nopt);
2840 snprintf(swoffname + 9, 4, "%03d", nopt);
2841
2842 opt = (opt == nullptr) ? (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2843 (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2844
2845 CLEAR(control);
2846 control.id = queryctrl.id;
2847 XIOCTL(fd, VIDIOC_G_CTRL, &control);
2848
2849 IUFillSwitch(sw, swonname, "Off", (control.value ? ISS_OFF : ISS_ON));
2850 sw->aux = nullptr;
2851 IUFillSwitch(sw + 1, swoffname, "On", (control.value ? ISS_ON : ISS_OFF));
2852 (sw + 1)->aux = nullptr;
2853 queryctrl.name[31] = '\0';
2854 IUFillSwitchVector(&opt[nopt], sw, 2, dev, optname, (const char *)entityXML((char *)queryctrl.name), group,
2855 IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2856 opt[nopt].aux = malloc(sizeof(unsigned int));
2857 *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2858
2859 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding switch %.*s (%s)", (int)sizeof(queryctrl.name),
2860 queryctrl.name, (control.value ? "On" : "Off"));
2861 nopt += 1;
2862 }
2863 if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
2864 {
2865 ISwitch * sw = (ISwitch *)malloc(sizeof(ISwitch));
2866 snprintf(optname + 3, 4, "%03d", nopt);
2867 snprintf(swonname + 7, 4, "%03d", nopt);
2868 //snprintf(swoffname+9, 4, "%03d", nopt);
2869
2870 opt = (opt == nullptr) ? (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2871 (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2872
2873 queryctrl.name[31] = '\0';
2874 IUFillSwitch(sw, swonname, (const char *)entityXML((char *)queryctrl.name), ISS_OFF);
2875 sw->aux = nullptr;
2876 IUFillSwitchVector(&opt[nopt], sw, 1, dev, optname, (const char *)entityXML((char *)queryctrl.name), group,
2877 IP_RW, ISR_NOFMANY, 0.0, IPS_IDLE);
2878 opt[nopt].aux = malloc(sizeof(unsigned int));
2879 *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2880 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding Button %.*s", (int)sizeof(queryctrl.name),
2881 queryctrl.name);
2882 nopt += 1;
2883 }
2884#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2885 if ((queryctrl.type == V4L2_CTRL_TYPE_MENU) || (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU))
2886#else
2887 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2888#endif
2889 {
2890 ISwitch * sw = nullptr;
2891 unsigned int nmenuopt = 0;
2892 char sname[32];
2893 snprintf(menuname + 4, 4, "%03d", nopt);
2894 snprintf(menuoptname + 4, 4, "%03d", nopt);
2895 menuoptname[7] = '_';
2896
2897 opt = (opt == nullptr) ? (ISwitchVectorProperty *)malloc(sizeof(ISwitchVectorProperty)) :
2898 (ISwitchVectorProperty *)realloc(opt, (nopt + 1) * sizeof(ISwitchVectorProperty));
2899
2900 CLEAR(control);
2901 control.id = queryctrl.id;
2902 XIOCTL(fd, VIDIOC_G_CTRL, &control);
2903
2905 querymenu.id = queryctrl.id;
2906
2907 for (querymenu.index = queryctrl.minimum; (int)querymenu.index <= queryctrl.maximum; querymenu.index++)
2908 {
2909 if (0 == XIOCTL(fd, VIDIOC_QUERYMENU, &querymenu))
2910 {
2911 sw = (sw == nullptr) ? (ISwitch *)malloc(sizeof(ISwitch)) :
2912 (ISwitch *)realloc(sw, (nmenuopt + 1) * sizeof(ISwitch));
2913 snprintf(menuoptname + 11, 4, "%03d", nmenuopt);
2914#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
2915 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
2916 {
2917 snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2918 sname[31] = '\0';
2919 }
2920 if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2921 {
2922 snprintf(sname, 19, "0x%016" PRIX64, static_cast<int64_t>(querymenu.value));
2923 sname[31] = '\0';
2924 }
2925#else
2926 snprintf(sname, 31, "%.*s", (int)sizeof(querymenu.name), querymenu.name);
2927 sname[31] = '\0';
2928#endif
2929 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu item %.*s %.*s item %d index %d",
2930 (int)sizeof(sname), sname, (int)sizeof(menuoptname), menuoptname, nmenuopt,
2931 querymenu.index);
2932 //IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)sname, (control.value==nmenuopt?ISS_ON:ISS_OFF));
2933 IUFillSwitch(&sw[nmenuopt], menuoptname, (const char *)entityXML((char *)querymenu.name),
2934 (control.value == (int)nmenuopt ? ISS_ON : ISS_OFF));
2935 sw[nmenuopt].aux = malloc(sizeof(unsigned int));
2936 *(unsigned int *)(sw[nmenuopt].aux) = (querymenu.index);
2937 nmenuopt += 1;
2938 }
2939 else
2940 {
2941 //errno_exit("VIDIOC_QUERYMENU", errmsg);
2942 //exit(3);
2943 }
2944 }
2945
2946 queryctrl.name[31] = '\0';
2947 IUFillSwitchVector(&opt[nopt], sw, nmenuopt, dev, menuname, (const char *)entityXML((char *)queryctrl.name),
2948 group, IP_RW, ISR_1OFMANY, 0.0, IPS_IDLE);
2949 opt[nopt].aux = malloc(sizeof(unsigned int));
2950 *(unsigned int *)(opt[nopt].aux) = (queryctrl.id);
2951
2952 DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG, "Adding menu %.*s (item %d set)",
2953 (int)sizeof(queryctrl.name), queryctrl.name, control.value);
2954 nopt += 1;
2955 }
2956
2957 //if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
2958 // {
2959 // DEBUGFDEVICE(deviceName, INDI::Logger::DBG_DEBUG,"Control type not implemented\n");
2960 //}
2961
2962 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
2963 }
2964
2965 /* Store numbers in aux0 */
2966 for (int i = 0; i < nnum; i++)
2967 numbers[i].aux0 = &num_ctrls[i];
2968
2969 nvp->np = numbers;
2970 nvp->nnp = nnum;
2971 *nnumber = nnum;
2972
2973 *options = opt;
2974 *noptions = nopt;
2975
2976 return true;
2977}
2978
2979void V4L2_Base::setDeviceName(const char * name)
2980{
2981 snprintf(deviceName, MAXINDIDEVICE, "%s", name);
2982}
2983
2984
2992std::map<std::string, std::string> V4L2_Base::enumerate()
2993{
2994 std::map<std::string, std::string> devices;
2995
2996 auto searchPath = [&](std::string prefix)
2997 {
2998 struct dirent **namelist;
2999 std::vector<std::string> detectedDevices;
3000 int devCount = 0;
3001 devCount = scandir(prefix.c_str(), &namelist, video_dev_file_select, alphasort);
3002 // Need to do this in reverse because some devices might have two end points
3003 // e.g. /dev/video0 and /dev/video1 for the same device but only /dev/video0 can be used to control it.
3004 for (int i = devCount - 1; i >= 0; i--)
3005 {
3006 std::string s(namelist[i]->d_name);
3007 s.erase(s.find_last_not_of(" \n\r\t") + 1);
3008 detectedDevices.push_back(prefix + s);
3009 free(namelist[i]);
3010 }
3011 free(namelist);
3012 return detectedDevices;
3013 };
3014
3015 const std::vector<std::string> videoDevices = searchPath("/dev/");
3016
3017 for (const auto &oneDevice : videoDevices)
3018 {
3019 int fd = open(oneDevice.c_str(), O_RDWR | O_NONBLOCK, 0);
3020 if (fd >= 0)
3021 {
3022 struct v4l2_capability cap;
3023 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) >= 0)
3024 {
3025 devices[std::string(reinterpret_cast<const char *>(cap.card))] = oneDevice;
3026 }
3027 close(fd);
3028 }
3029 }
3030
3031 return devices;
3032}
3033
3037int V4L2_Base::video_dev_file_select(const dirent * entry)
3038{
3039 static const char *filter_names[] = { "video", nullptr };
3040 const char **filter;
3041
3042 for (filter = filter_names; *filter; ++filter)
3043 {
3044 if (strstr(entry->d_name, *filter) != nullptr)
3045 {
3046 return (true);
3047 }
3048 }
3049 return (false);
3050}
3051}
void init_read(unsigned int buffer_size)
bool has_ext_pix_format
Definition v4l2_base.h:204
struct v4l2_querymenu querymenu
Definition v4l2_base.h:203
int read_frame(char *errsg)
char deviceName[MAXINDIDEVICE]
Definition v4l2_base.h:235
int stop_capturing(char *errmsg)
io_method io
Definition v4l2_base.h:213
bool reallocate_buffers
Definition v4l2_base.h:217
void doDecode(bool)
int check_device(char *errmsg)
int xioctl(int fd, int request, void *arg, char const *const request_str)
virtual void disconnectCam(bool stopcapture)
struct v4l2_queryctrl queryctrl
Definition v4l2_base.h:202
float * getLinearY()
const char * path
Definition v4l2_base.h:211
int(V4L2_Base::* setframerate)(struct v4l2_fract frate, char *errmsg)
Definition v4l2_base.h:101
int pwcsetframerate(struct v4l2_fract frate, char *errmsg)
static int video_dev_file_select(const dirent *entry)
bool enumerate_ext_ctrl()
int stdsetframerate(struct v4l2_fract frate, char *errmsg)
int open_device(const char *devpath, char *errmsg)
int setINTControl(unsigned int ctrl_id, double new_value, char *errmsg)
struct v4l2_capability cap
Definition v4l2_base.h:187
int query_ctrl(unsigned int ctrl_id, double &ctrl_min, double &ctrl_max, double &ctrl_step, double &ctrl_value, char *errmsg)
void queryControls(INumberVectorProperty *nvp, unsigned int *nnumber, ISwitchVectorProperty **options, unsigned int *noptions, const char *dev, const char *group)
int getControl(unsigned int ctrl_id, double *value, char *errmsg)
int errno_exit(const char *s, char *errmsg)
void setColorProcessing(bool quantization, bool colorconvert, bool linearization)
void getframerates(ISwitchVectorProperty *frameratessp, INumberVectorProperty *frameratenp)
unsigned char * getV()
bool is_compressed() const
Helper indicating whether current pixel format is compressed or not.
void registerCallback(WPF *fp, void *ud)
V4L2_Decode * v4l2_decode
Definition v4l2_base.h:227
struct buffer * buffers
Definition v4l2_base.h:215
struct v4l2_format fmt
Definition v4l2_base.h:190
unsigned char * getU()
int uninit_device(char *errmsg)
void getcaptureformats(ISwitchVectorProperty *captureformatssp)
static void newFrame(int fd, void *p)
unsigned char * getY()
int start_capturing(char *errmsg)
struct v4l2_fract stdgetframerate()
void getcapturesizes(ISwitchVectorProperty *capturesizessp, INumberVectorProperty *capturesizenp)
unsigned char * getMJPEGBuffer(int &size)
void init_userp(unsigned int buffer_size)
struct v4l2_buffer buf
Definition v4l2_base.h:192
struct v4l2_fract(V4L2_Base::* getframerate)()
Definition v4l2_base.h:102
void setDeviceName(const char *name)
int init_device(char *errmsg)
int setcapturesize(unsigned int w, unsigned int h, char *errmsg)
int setcaptureformat(unsigned int captureformatindex, char *errmsg)
struct v4l2_cropcap cropcap
Definition v4l2_base.h:188
int ioctl_set_format(struct v4l2_format new_fmt, char *errmsg)
char dev_name[64]
Definition v4l2_base.h:210
static std::map< std::string, std::string > enumerate()
int enumeratedCaptureFormats
Definition v4l2_base.h:180
virtual void getMaxMinSize(int &x_max, int &y_max, int &x_min, int &y_min)
unsigned char * getRGBBuffer()
char * getDeviceName()
int setcroprect(int x, int y, int w, int h, char *errmsg)
bool queryExtControls(INumberVectorProperty *nvp, unsigned int *nnumber, ISwitchVectorProperty **options, unsigned int *noptions, const char *dev, const char *group)
unsigned int n_buffers
Definition v4l2_base.h:216
struct v4l2_fract frameRate
Definition v4l2_base.h:222
V4L2_Decoder * decoder
Definition v4l2_base.h:228
int init_mmap(char *errmsg)
struct v4l2_crop crop
Definition v4l2_base.h:189
void getinputs(ISwitchVectorProperty *inputssp)
virtual int setSize(int x, int y)
struct v4l2_input input
Definition v4l2_base.h:191
int setinput(unsigned int inputindex, char *errmsg)
int setOPTControl(unsigned int ctrl_id, unsigned int new_value, char *errmsg)
int queryINTControls(INumberVectorProperty *nvp)
bool isLXmodCapable()
virtual int connectCam(const char *devpath, char *errmsg, int pixelFormat=-1, int width=-1, int height=-1)
virtual ~V4L2_Base()
V4L2_Decoder * getDefaultDecoder()
virtual unsigned char * getRGBBuffer()=0
virtual void setQuantization(bool)=0
virtual void resetcrop()=0
virtual void decode(unsigned char *frame, struct v4l2_buffer *buf, bool native)=0
virtual void setformat(struct v4l2_format f, bool use_ext_pix_format)=0
virtual void setLinearization(bool)=0
virtual float * getLinearY()=0
virtual unsigned char * getMJPEGBuffer(int &size)=0
virtual void init()=0
virtual unsigned char * getU()=0
virtual int getBpp()=0
virtual bool issupportedformat(unsigned int format)=0
virtual bool setcrop(struct v4l2_crop c)=0
virtual unsigned char * getY()=0
virtual unsigned char * getV()=0
virtual void usesoftcrop(bool c)=0
virtual const std::vector< unsigned int > & getsupportedformats()=0
virtual const char * getName()
void IERmCallback(int callbackid)
Remove a callback function.
Definition eventloop.c:581
void rmCallback(int cid)
Definition eventloop.c:193
int IEAddCallback(int readfiledes, IE_CBF *fp, void *p)
Register a new callback, fp, to be called with userpointer as argument when readfiledes is ready.
Definition eventloop.c:576
Public interface to INDI's eventloop mechanism.
void() WPF(void *)
Signature of a work procedure function.
Definition eventloop.h:43
int errno
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
#define MAXINDIDEVICE
Definition indiapi.h:191
#define MAXINDIFORMAT
Definition indiapi.h:193
@ IP_RW
Definition indiapi.h:184
@ IPS_IDLE
Definition indiapi.h:159
#define MAXINDILABEL
Definition indiapi.h:190
struct _ISwitchVectorProperty ISwitchVectorProperty
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_NOFMANY
Definition indiapi.h:173
#define MAXINDINAME
Definition indiapi.h:189
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition indidevapi.c:398
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
Interface to the reference INDI C API device implementation on the Device Driver side.
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define DEBUGDEVICE(device, priority, msg)
Definition indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61
int fd
char * entityXML(char *s)
return a string with all xml-sensitive characters within the passed string replaced with their entity...
Definition lilxml.cpp:975
A little DOM-style library to handle parsing and processing an XML file.
Namespace to encapsulate INDI client, drivers, and mediator classes.
#define PWC_FPS_SHIFT
Definition pwc-ioctl.h:74
uint16_t type
Definition pwc-ioctl.h:0
One number descriptor.
One switch descriptor.
Number vector property descriptor.
Definition indiapi.h:317
Switch vector property descriptor.
Definition indiapi.h:365
#define DBG_BUF(b)
#define DBG_STR_BUF
#define DBG_FMT(f)
Definition v4l2_base.cpp:99
#define CLEAR(x)
Definition v4l2_base.cpp:61
#define XIOCTL(fd, ioctl, arg)
Definition v4l2_base.cpp:63
#define DBG_STR_FMT
Definition v4l2_base.cpp:98
@ LX_ACTIVE
Definition v4l2_base.h:55
@ LX_TRIGGERED
Definition v4l2_base.h:56
#define ERRMSGSIZ
Definition v4l2driver.h:38