1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <inttypes.h>
6 #include <getopt.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <sys/ioctl.h>
13 #include <sys/time.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include <vector>
18
19 #include "v4l2-ctl.h"
20
21 #if !defined(__FreeBSD__) && !defined(__DragonFly__)
22 #include <linux/fb.h>
23
24 static unsigned int set_fbuf;
25 static unsigned int set_overlay_fmt;
26 static struct v4l2_format overlay_fmt; /* set_format/get_format video overlay */
27 static struct v4l2_framebuffer fbuf; /* fbuf */
28 static int overlay; /* overlay */
29 static const char *fb_device;
30 static std::vector<struct v4l2_clip> clips;
31 static std::vector<struct v4l2_rect> bitmap_rects;
32
overlay_usage()33 void overlay_usage()
34 {
35 printf("\nVideo Overlay options:\n"
36 " --list-formats-overlay\n"
37 " display supported overlay formats [VIDIOC_ENUM_FMT]\n"
38 " --find-fb find the fb device corresponding with the overlay\n"
39 " --overlay <on> turn overlay on (1) or off (0) (VIDIOC_OVERLAY)\n"
40 " --get-fmt-overlay query the video or video output overlay format [VIDIOC_G_FMT]\n"
41 " --set-fmt-overlay\n"
42 " --try-fmt-overlay chromakey=<key>,global_alpha=<alpha>,\n"
43 " top=<t>,left=<l>,width=<w>,height=<h>,field=<f>\n"
44 " set/try the video or video output overlay format [VIDIOC_TRY/S_FMT]\n"
45 " <f> can be one of:\n"
46 " any, none, top, bottom, interlaced, seq_tb, seq_bt,\n"
47 " alternate, interlaced_tb, interlaced_bt\n"
48 " If the width or height changed then the old clip list and bitmap will\n"
49 " be invalidated.\n"
50 " --clear-clips clear any old clips, to be used in combination with --try/set-fmt-overlay\n"
51 " --clear-bitmap clear any old bitmap, to be used in combination with --try/set-fmt-overlay\n"
52 " --add-clip top=<t>,left=<l>,width=<w>,height=<h>\n"
53 " Add an entry to the clip list. May be used multiple times.\n"
54 " This clip list will be passed to --try/set-fmt-overlay\n"
55 " --add-bitmap top=<t>,left=<l>,width=<w>,height=<h>\n"
56 " Set the bits in the given rectangle in the bitmap to 1. May be\n"
57 " used multiple times.\n"
58 " The bitmap will be passed to --try/set-fmt-overlay\n"
59 " --get-fbuf query the overlay framebuffer data [VIDIOC_G_FBUF]\n"
60 " --set-fbuf chromakey=<b>,src_chromakey=<b>,global_alpha=<b>,local_alpha=<b>,local_inv_alpha=<b>,fb=<fb>\n"
61 " set the overlay framebuffer [VIDIOC_S_FBUF]\n"
62 " <b> is 0 or 1\n"
63 " <fb> is the framebuffer device (/dev/fbX)\n"
64 " if <fb> starts with a digit, then /dev/fb<fb> is used\n"
65 );
66 }
67
printfbuf(const struct v4l2_framebuffer & fb)68 static void printfbuf(const struct v4l2_framebuffer &fb)
69 {
70 int is_ext = fb.capability & V4L2_FBUF_CAP_EXTERNOVERLAY;
71
72 printf("Framebuffer Format:\n");
73 printf("\tCapability : %s", fbufcap2s(fb.capability).c_str() + 3);
74 printf("\tFlags : %s", fbufflags2s(fb.flags).c_str() + 3);
75 if (fb.base)
76 printf("\tBase : %p\n", fb.base);
77 printf("\tWidth : %d\n", fb.fmt.width);
78 printf("\tHeight : %d\n", fb.fmt.height);
79 printf("\tPixel Format : '%s'\n", fcc2s(fb.fmt.pixelformat).c_str());
80 if (!is_ext) {
81 printf("\tBytes per Line: %d\n", fb.fmt.bytesperline);
82 printf("\tSize image : %d\n", fb.fmt.sizeimage);
83 printf("\tColorspace : %s\n", colorspace2s(fb.fmt.colorspace).c_str());
84 if (fb.fmt.priv)
85 printf("\tCustom Info : %08x\n", fb.fmt.priv);
86 }
87 }
88
find_fb(int fd)89 static void find_fb(int fd)
90 {
91 struct v4l2_framebuffer fbuf;
92 unsigned int i;
93 int fb_fd;
94
95 if (doioctl(fd, VIDIOC_G_FBUF, &fbuf))
96 return;
97 if (fbuf.base == 0) {
98 printf("No framebuffer base address was defined\n");
99 return;
100 }
101
102 for (i = 0; i < 30; i++) {
103 struct fb_fix_screeninfo si;
104 char dev_name[16];
105
106 snprintf(dev_name, sizeof(dev_name), "/dev/fb%u", i);
107
108 fb_fd = open(dev_name, O_RDWR);
109 if (fb_fd == -1) {
110 switch (errno) {
111 case ENOENT: /* no such file */
112 case ENXIO: /* no driver */
113 continue;
114
115 default:
116 return;
117 }
118 }
119
120 if (!ioctl(fb_fd, FBIOGET_FSCREENINFO, &si))
121 if (si.smem_start == (unsigned long)fbuf.base) {
122 printf("%s is the framebuffer associated with base address %p\n",
123 dev_name, fbuf.base);
124 close(fb_fd);
125 return;
126 }
127 close(fb_fd);
128 fb_fd = -1;
129 }
130 printf("No matching framebuffer found for base address %p\n", fbuf.base);
131 }
132
133 struct bitfield2fmt {
134 unsigned red_off, red_len;
135 unsigned green_off, green_len;
136 unsigned blue_off, blue_len;
137 unsigned transp_off, transp_len;
138 uint32_t pixfmt;
139 };
140
141 static const struct bitfield2fmt fb_formats[] = {
142 { 10, 5, 5, 5, 0, 5, 15, 1, V4L2_PIX_FMT_ARGB555 },
143 { 11, 5, 5, 6, 0, 5, 0, 0, V4L2_PIX_FMT_RGB565 },
144 { 1, 5, 6, 5, 11, 5, 0, 1, V4L2_PIX_FMT_RGB555X },
145 { 0, 5, 5, 6, 11, 5, 0, 0, V4L2_PIX_FMT_RGB565X },
146 { 16, 8, 8, 8, 0, 8, 0, 0, V4L2_PIX_FMT_BGR24 },
147 { 0, 8, 8, 8, 16, 8, 0, 0, V4L2_PIX_FMT_RGB24 },
148 { 16, 8, 8, 8, 0, 8, 24, 8, V4L2_PIX_FMT_ABGR32 },
149 { 8, 8, 16, 8, 24, 8, 0, 8, V4L2_PIX_FMT_ARGB32 },
150 { }
151 };
152
match_bitfield(const struct fb_bitfield & bf,unsigned off,unsigned len)153 static bool match_bitfield(const struct fb_bitfield &bf, unsigned off, unsigned len)
154 {
155 if (bf.msb_right || bf.length != len)
156 return false;
157 return !len || bf.offset == off;
158 }
159
fbuf_fill_from_fb(struct v4l2_framebuffer & fb,const char * fb_device)160 static int fbuf_fill_from_fb(struct v4l2_framebuffer &fb, const char *fb_device)
161 {
162 struct fb_fix_screeninfo si;
163 struct fb_var_screeninfo vi;
164 int fb_fd;
165
166 if (fb_device == NULL)
167 return 0;
168 fb_fd = open(fb_device, O_RDWR);
169 if (fb_fd == -1) {
170 fprintf(stderr, "cannot open %s\n", fb_device);
171 return -1;
172 }
173 if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &si)) {
174 fprintf(stderr, "could not obtain fscreeninfo from %s\n", fb_device);
175 close(fb_fd);
176 return -1;
177 }
178 if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vi)) {
179 fprintf(stderr, "could not obtain vscreeninfo from %s\n", fb_device);
180 close(fb_fd);
181 return -1;
182 }
183 fb.base = (void *)si.smem_start;
184 fb.fmt.sizeimage = si.smem_len;
185 fb.fmt.width = vi.xres;
186 fb.fmt.height = vi.yres;
187 fb.fmt.bytesperline = si.line_length;
188 if (fb.fmt.height * fb.fmt.bytesperline > fb.fmt.sizeimage) {
189 fprintf(stderr, "height * bytesperline > sizeimage?!\n");
190 close(fb_fd);
191 return -1;
192 }
193 fb.fmt.pixelformat = 0;
194 if ((si.capabilities & FB_CAP_FOURCC) && vi.grayscale > 1) {
195 fb.fmt.pixelformat = vi.grayscale;
196 fb.fmt.colorspace = vi.colorspace;
197 } else {
198 if (vi.grayscale == 1) {
199 if (vi.bits_per_pixel == 8)
200 fb.fmt.pixelformat = V4L2_PIX_FMT_GREY;
201 } else {
202 for (int i = 0; fb_formats[i].pixfmt; i++) {
203 const struct bitfield2fmt &p = fb_formats[i];
204
205 if (match_bitfield(vi.blue, p.blue_off, p.blue_len) &&
206 match_bitfield(vi.green, p.green_off, p.green_len) &&
207 match_bitfield(vi.red, p.red_off, p.red_len) &&
208 match_bitfield(vi.transp, p.transp_off, p.transp_len)) {
209 fb.fmt.pixelformat = p.pixfmt;
210 break;
211 }
212 }
213 }
214 fb.fmt.colorspace = V4L2_COLORSPACE_SRGB;
215 }
216 printfbuf(fb);
217 close(fb_fd);
218 return 0;
219 }
220
overlay_cmd(int ch,char * optarg)221 void overlay_cmd(int ch, char *optarg)
222 {
223 struct v4l2_rect r;
224 char *value, *subs;
225
226 switch (ch) {
227 case OptSetOverlayFormat:
228 case OptTryOverlayFormat:
229 subs = optarg;
230 while (subs && *subs != '\0') {
231 static const char *const subopts[] = {
232 "chromakey",
233 "global_alpha",
234 "left",
235 "top",
236 "width",
237 "height",
238 "field",
239 NULL
240 };
241
242 switch (parse_subopt(&subs, subopts, &value)) {
243 case 0:
244 overlay_fmt.fmt.win.chromakey = strtoul(value, 0L, 0);
245 set_overlay_fmt |= FmtChromaKey;
246 break;
247 case 1:
248 overlay_fmt.fmt.win.global_alpha = strtoul(value, 0L, 0);
249 set_overlay_fmt |= FmtGlobalAlpha;
250 break;
251 case 2:
252 overlay_fmt.fmt.win.w.left = strtol(value, 0L, 0);
253 set_overlay_fmt |= FmtLeft;
254 break;
255 case 3:
256 overlay_fmt.fmt.win.w.top = strtol(value, 0L, 0);
257 set_overlay_fmt |= FmtTop;
258 break;
259 case 4:
260 overlay_fmt.fmt.win.w.width = strtoul(value, 0L, 0);
261 set_overlay_fmt |= FmtWidth;
262 break;
263 case 5:
264 overlay_fmt.fmt.win.w.height = strtoul(value, 0L, 0);
265 set_overlay_fmt |= FmtHeight;
266 break;
267 case 6:
268 overlay_fmt.fmt.win.field = parse_field(value);
269 set_overlay_fmt |= FmtField;
270 break;
271 default:
272 overlay_usage();
273 break;
274 }
275 }
276 break;
277 case OptAddClip:
278 case OptAddBitmap:
279 subs = optarg;
280 memset(&r, 0, sizeof(r));
281 while (*subs != '\0') {
282 static const char *const subopts[] = {
283 "left",
284 "top",
285 "width",
286 "height",
287 NULL
288 };
289
290 switch (parse_subopt(&subs, subopts, &value)) {
291 case 0:
292 r.left = strtoul(value, 0L, 0);
293 break;
294 case 1:
295 r.top = strtoul(value, 0L, 0);
296 break;
297 case 2:
298 r.width = strtoul(value, 0L, 0);
299 break;
300 case 3:
301 r.height = strtoul(value, 0L, 0);
302 break;
303 default:
304 overlay_usage();
305 return;
306 }
307 }
308 if (r.width == 0 || r.height == 0) {
309 overlay_usage();
310 return;
311 }
312 if (ch == OptAddBitmap) {
313 bitmap_rects.push_back(r);
314 } else {
315 struct v4l2_clip c;
316
317 c.c = r;
318 c.next = NULL;
319 clips.push_back(c);
320 }
321 break;
322 case OptSetFBuf:
323 subs = optarg;
324 while (*subs != '\0') {
325 const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY |
326 V4L2_FBUF_FLAG_SRC_CHROMAKEY;
327 const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA |
328 V4L2_FBUF_FLAG_LOCAL_ALPHA |
329 V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
330 static const char *const subopts[] = {
331 "chromakey",
332 "src_chromakey",
333 "global_alpha",
334 "local_alpha",
335 "local_inv_alpha",
336 "fb",
337 NULL
338 };
339
340 switch (parse_subopt(&subs, subopts, &value)) {
341 case 0:
342 fbuf.flags &= ~chroma_flags;
343 fbuf.flags |= strtol(value, 0L, 0) ? V4L2_FBUF_FLAG_CHROMAKEY : 0;
344 set_fbuf |= chroma_flags;
345 break;
346 case 1:
347 fbuf.flags &= ~chroma_flags;
348 fbuf.flags |= strtol(value, 0L, 0) ? V4L2_FBUF_FLAG_SRC_CHROMAKEY : 0;
349 set_fbuf |= chroma_flags;
350 break;
351 case 2:
352 fbuf.flags &= ~alpha_flags;
353 fbuf.flags |= strtol(value, 0L, 0) ? V4L2_FBUF_FLAG_GLOBAL_ALPHA : 0;
354 set_fbuf |= alpha_flags;
355 break;
356 case 3:
357 fbuf.flags &= ~alpha_flags;
358 fbuf.flags |= strtol(value, 0L, 0) ? V4L2_FBUF_FLAG_LOCAL_ALPHA : 0;
359 set_fbuf |= alpha_flags;
360 break;
361 case 4:
362 fbuf.flags &= ~alpha_flags;
363 fbuf.flags |= strtol(value, 0L, 0) ? V4L2_FBUF_FLAG_LOCAL_INV_ALPHA : 0;
364 set_fbuf |= alpha_flags;
365 break;
366 case 5:
367 fb_device = value;
368 if (fb_device[0] >= '0' && fb_device[0] <= '9' && strlen(fb_device) <= 3) {
369 static char newdev[20];
370
371 sprintf(newdev, "/dev/fb%s", fb_device);
372 fb_device = newdev;
373 }
374 break;
375 default:
376 overlay_usage();
377 break;
378 }
379 }
380 break;
381 case OptOverlay:
382 overlay = strtol(optarg, 0L, 0);
383 break;
384 }
385 }
386
do_try_set_overlay(struct v4l2_format & fmt,int fd)387 static void do_try_set_overlay(struct v4l2_format &fmt, int fd)
388 {
389 struct v4l2_window &win = fmt.fmt.win;
390 bool keep_old_clip = true;
391 bool keep_old_bitmap = true;
392 struct v4l2_clip *cliplist = NULL;
393 unsigned stride = (win.w.width + 7) / 8;
394 unsigned char *bitmap = NULL;
395 int ret;
396
397 if (((set_overlay_fmt & FmtWidth) && win.w.width != overlay_fmt.fmt.win.w.width) ||
398 ((set_overlay_fmt & FmtHeight) && win.w.height != overlay_fmt.fmt.win.w.height))
399 keep_old_bitmap = keep_old_clip = false;
400 if (options[OptClearBitmap] || !bitmap_rects.empty())
401 keep_old_bitmap = false;
402 if (options[OptClearClips] || !clips.empty())
403 keep_old_clip = false;
404
405 win.bitmap = NULL;
406 if (keep_old_bitmap) {
407 bitmap = static_cast<unsigned char *>(calloc(1, stride * win.w.height));
408 win.bitmap = bitmap;
409 }
410 if (keep_old_clip) {
411 if (win.clipcount)
412 cliplist = static_cast<struct v4l2_clip *>(malloc(win.clipcount * sizeof(*cliplist)));
413 win.clips = cliplist;
414 }
415 if (keep_old_clip || keep_old_bitmap)
416 if (doioctl(fd, VIDIOC_G_FMT, &fmt))
417 goto free;
418
419 if (set_overlay_fmt & FmtChromaKey)
420 win.chromakey = overlay_fmt.fmt.win.chromakey;
421 if (set_overlay_fmt & FmtGlobalAlpha)
422 win.global_alpha = overlay_fmt.fmt.win.global_alpha;
423 if (set_overlay_fmt & FmtLeft)
424 win.w.left = overlay_fmt.fmt.win.w.left;
425 if (set_overlay_fmt & FmtTop)
426 win.w.top = overlay_fmt.fmt.win.w.top;
427 if (set_overlay_fmt & FmtWidth)
428 win.w.width = overlay_fmt.fmt.win.w.width;
429 if (set_overlay_fmt & FmtHeight)
430 win.w.height = overlay_fmt.fmt.win.w.height;
431 if (set_overlay_fmt & FmtField)
432 win.field = overlay_fmt.fmt.win.field;
433 if (!clips.empty()) {
434 win.clipcount = clips.size();
435 win.clips = &clips[0];
436 }
437 if (!bitmap_rects.empty()) {
438 free(bitmap);
439 stride = (win.w.width + 7) / 8;
440 bitmap = static_cast<unsigned char *>(calloc(1, stride * win.w.height));
441 win.bitmap = bitmap;
442 for (unsigned i = 0; i < bitmap_rects.size(); i++) {
443 const v4l2_rect &r = bitmap_rects[i];
444
445 if (r.left + r.width > win.w.width ||
446 r.top + r.height > win.w.height) {
447 fprintf(stderr, "rectangle is out of range\n");
448 return;
449 }
450 for (unsigned y = r.top; y < r.top + r.height; y++)
451 for (unsigned x = r.left; x < r.left + r.width; x++)
452 bitmap[y * stride + x / 8] |= 1 << (x & 7);
453 }
454 win.bitmap = bitmap;
455 }
456 if (options[OptSetOverlayFormat])
457 ret = doioctl(fd, VIDIOC_S_FMT, &fmt);
458 else
459 ret = doioctl(fd, VIDIOC_TRY_FMT, &fmt);
460 if (ret == 0 && (verbose || options[OptTryOverlayFormat]))
461 printfmt(fd, fmt);
462
463 free:
464 if (bitmap)
465 free(bitmap);
466 if (cliplist)
467 free(cliplist);
468 }
469
overlay_set(cv4l_fd & _fd)470 void overlay_set(cv4l_fd &_fd)
471 {
472 int fd = _fd.g_fd();
473
474 if ((options[OptSetOverlayFormat] || options[OptTryOverlayFormat]) &&
475 (set_overlay_fmt || options[OptClearClips] || options[OptClearBitmap] ||
476 !bitmap_rects.empty() || !clips.empty())) {
477 struct v4l2_format fmt;
478
479 memset(&fmt, 0, sizeof(fmt));
480 // You can never have both VIDEO_OVERLAY and VIDEO_OUTPUT_OVERLAY
481 if (capabilities & V4L2_CAP_VIDEO_OVERLAY)
482 fmt.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
483 else
484 fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY;
485 if (doioctl(fd, VIDIOC_G_FMT, &fmt) == 0)
486 do_try_set_overlay(fmt, fd);
487 }
488
489 if (options[OptSetFBuf]) {
490 struct v4l2_framebuffer fb;
491
492 if (doioctl(fd, VIDIOC_G_FBUF, &fb) == 0) {
493 fb.flags &= ~set_fbuf;
494 fb.flags |= fbuf.flags;
495 if (!fbuf_fill_from_fb(fb, fb_device))
496 doioctl(fd, VIDIOC_S_FBUF, &fb);
497 }
498 }
499
500 if (options[OptOverlay]) {
501 doioctl(fd, VIDIOC_OVERLAY, &overlay);
502 }
503 }
504
overlay_get(cv4l_fd & _fd)505 void overlay_get(cv4l_fd &_fd)
506 {
507 int fd = _fd.g_fd();
508
509 if (options[OptGetOverlayFormat]) {
510 struct v4l2_format fmt;
511 unsigned char *bitmap = NULL;
512
513 memset(&fmt, 0, sizeof(fmt));
514 // You can never have both VIDEO_OVERLAY and VIDEO_OUTPUT_OVERLAY
515 if (capabilities & V4L2_CAP_VIDEO_OVERLAY)
516 fmt.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
517 else
518 fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY;
519 if (doioctl(fd, VIDIOC_G_FMT, &fmt) == 0) {
520 unsigned stride = (fmt.fmt.win.w.width + 7) / 8;
521
522 if (fmt.fmt.win.clipcount)
523 fmt.fmt.win.clips = static_cast<struct v4l2_clip *>(malloc(fmt.fmt.win.clipcount * sizeof(clips[0])));
524 bitmap = static_cast<unsigned char *>(calloc(1, stride * fmt.fmt.win.w.height));
525 fmt.fmt.win.bitmap = bitmap;
526 doioctl(fd, VIDIOC_G_FMT, &fmt);
527 printfmt(fd, fmt);
528 if (fmt.fmt.win.clips)
529 free(fmt.fmt.win.clips);
530 if (bitmap)
531 free(bitmap);
532 }
533 }
534
535 if (options[OptGetFBuf]) {
536 struct v4l2_framebuffer fb;
537 if (doioctl(fd, VIDIOC_G_FBUF, &fb) == 0)
538 printfbuf(fb);
539 }
540 }
541
overlay_list(cv4l_fd & fd)542 void overlay_list(cv4l_fd &fd)
543 {
544 if (options[OptListOverlayFormats]) {
545 printf("ioctl: VIDIOC_ENUM_FMT\n");
546 print_video_formats(fd, V4L2_BUF_TYPE_VIDEO_OVERLAY);
547 }
548 if (options[OptFindFb])
549 find_fb(fd.g_fd());
550 }
551 #else
overlay_usage(void)552 void overlay_usage(void)
553 {
554 }
555
overlay_cmd(int ch,char * optarg)556 void overlay_cmd(int ch, char *optarg)
557 {
558 }
559
overlay_set(cv4l_fd & _fd)560 void overlay_set(cv4l_fd &_fd)
561 {
562 }
563
overlay_get(cv4l_fd & _fd)564 void overlay_get(cv4l_fd &_fd)
565 {
566 }
567
overlay_list(cv4l_fd & fd)568 void overlay_list(cv4l_fd &fd)
569 {
570 }
571 #endif
572