ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/mintv/mintv.cpp
Revision: 1.5
Committed: 2003-03-07T20:08:37Z (21 years, 8 months ago) by cebix
Branch: MAIN
Changes since 1.4: +5 -1 lines
Log Message:
- input focus hint is set correctly
- more sensible mouse pointer

File Contents

# Content
1 /*
2 * mintv.cpp - 50/60Hz video display using v4l and XVideo
3 *
4 * Written in 2003 by Christian Bauer
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <getopt.h>
10 #include <errno.h>
11
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <sys/time.h>
15 #include <sys/ioctl.h>
16 #include <sys/mman.h>
17 #include <sys/shm.h>
18 #include <linux/videodev.h>
19
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22 #include <X11/StringDefs.h>
23 #include <X11/Intrinsic.h>
24 #include <X11/Shell.h>
25 #include <X11/cursorfont.h>
26 #include <X11/Xaw/Simple.h>
27 #include <X11/extensions/XShm.h>
28 #include <X11/extensions/Xv.h>
29 #include <X11/extensions/Xvlib.h>
30
31 static bool ntsc = false;
32
33 const int PAL_WIDTH = 384;
34 const int PAL_HEIGHT = 288;
35 const int NTSC_WIDTH = 360;
36 const int NTSC_HEIGHT = 240;
37
38 static int grab_width = -1, grab_height;
39 static int image_width, image_height;
40 static int win_width, win_height;
41
42 const int PAL_FPS = 50;
43 const int NTSC_FPS = 60;
44
45 static int fps;
46
47 static int brightness = 50, contrast = 50, color = 50;
48
49 static int channel = 1;
50
51 #define XV_FORMAT 0x32595559
52 #define BYTES_PER_PIXEL 2
53
54 #define DOUBLE_FRAME 1
55 #define LOGGING 0
56
57 #if LOGGING
58 static FILE *log;
59 #endif
60
61 static int port = -1;
62 static Display *dpy;
63 static GC gc;
64 static Atom wm;
65 static Widget app_shell, video;
66 static XShmSegmentInfo shminfo;
67 static XvImage *image[2];
68 static int fd = -1;
69
70 static void quit(Widget widget, XEvent *event, String *params, Cardinal *num_params)
71 {
72 XShmDetach(dpy, &shminfo);
73 shmdt(shminfo.shmaddr);
74
75 if (fd >= 0) {
76 video_audio au;
77 au.audio = 1;
78 ioctl(fd, VIDIOCGAUDIO, &au);
79 au.flags |= VIDEO_AUDIO_MUTE;
80 ioctl(fd, VIDIOCSAUDIO, &au);
81 close(fd);
82 }
83
84 exit(0);
85 }
86
87 static void set_bcc(void)
88 {
89 video_picture pict;
90 if (ioctl(fd, VIDIOCGPICT, &pict) < 0) {
91 fprintf(stderr, "ioctl VIDIOCGPICT: %s\n", strerror(errno));
92 exit(1);
93 }
94 pict.brightness = brightness * 65536 / 100;
95 pict.contrast = contrast * 65536 / 100;
96 pict.colour = color * 65536 / 100;
97 if (ioctl(fd, VIDIOCSPICT, &pict) < 0) {
98 fprintf(stderr, "ioctl VIDIOCSPICT: %s\n", strerror(errno));
99 exit(1);
100 }
101 }
102
103 static void inc_brightness(Widget widget, XEvent *event, String *params, Cardinal *num_params)
104 {
105 if (brightness < 100)
106 brightness++;
107 set_bcc();
108 }
109
110 static void dec_brightness(Widget widget, XEvent *event, String *params, Cardinal *num_params)
111 {
112 if (brightness > 0)
113 brightness--;
114 set_bcc();
115 }
116
117 static void reset_brightness(Widget widget, XEvent *event, String *params, Cardinal *num_params)
118 {
119 brightness = 50;
120 set_bcc();
121 }
122
123 static void inc_contrast(Widget widget, XEvent *event, String *params, Cardinal *num_params)
124 {
125 if (contrast < 100)
126 contrast++;
127 set_bcc();
128 }
129
130 static void dec_contrast(Widget widget, XEvent *event, String *params, Cardinal *num_params)
131 {
132 if (contrast > 0)
133 contrast--;
134 set_bcc();
135 }
136
137 static void reset_contrast(Widget widget, XEvent *event, String *params, Cardinal *num_params)
138 {
139 contrast = 50;
140 set_bcc();
141 }
142
143 static void inc_color(Widget widget, XEvent *event, String *params, Cardinal *num_params)
144 {
145 if (color < 100)
146 color++;
147 set_bcc();
148 }
149
150 static void dec_color(Widget widget, XEvent *event, String *params, Cardinal *num_params)
151 {
152 if (color > 0)
153 color--;
154 set_bcc();
155 }
156
157 static void reset_color(Widget widget, XEvent *event, String *params, Cardinal *num_params)
158 {
159 color = 50;
160 set_bcc();
161 }
162
163 static void set_channel(void)
164 {
165 video_channel chan;
166 chan.channel = channel;
167 if (ioctl(fd, VIDIOCGCHAN, &chan) < 0) {
168 fprintf(stderr, "ioctl VIDIOCGCHAN: %s\n", strerror(errno));
169 exit(1);
170 }
171 if (ntsc)
172 chan.norm = VIDEO_MODE_NTSC;
173 else
174 chan.norm = VIDEO_MODE_PAL;
175 if (ioctl(fd, VIDIOCSCHAN, &chan) < 0) {
176 fprintf(stderr, "ioctl VIDIOCSCHAN: %s\n", strerror(errno));
177 exit(1);
178 }
179 }
180
181 static void select_channel_1(Widget widget, XEvent *event, String *params, Cardinal *num_params)
182 {
183 channel = 1;
184 set_channel();
185 }
186
187 static void select_channel_2(Widget widget, XEvent *event, String *params, Cardinal *num_params)
188 {
189 channel = 2;
190 set_channel();
191 }
192
193 static XtActionsRec actionTable[] = {
194 {"quit", quit},
195 {"inc_brightness", inc_brightness},
196 {"dec_brightness", dec_brightness},
197 {"reset_brightness", reset_brightness},
198 {"inc_contrast", inc_contrast},
199 {"dec_contrast", dec_contrast},
200 {"reset_contrast", reset_contrast},
201 {"inc_color", inc_color},
202 {"dec_color", dec_color},
203 {"reset_color", reset_color},
204 {"select_channel_1", select_channel_1},
205 {"select_channel_2", select_channel_2}
206 };
207
208 static void resize_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
209 {
210 if (event->type == ConfigureNotify) {
211 win_width = event->xconfigure.width;
212 win_height = event->xconfigure.height;
213 }
214 }
215
216 static video_mbuf mbuf;
217 static void *buf = NULL;
218 static video_mmap mm[2];
219 static int bufnum = 0;
220
221 typedef long long int64;
222 typedef unsigned long long uint64;
223
224 uint64 GetTicks_usec(void)
225 {
226 struct timeval t;
227 gettimeofday(&t, NULL);
228 return (uint64)t.tv_sec * 1000000 + t.tv_usec;
229 }
230
231 void Delay_usec(unsigned usec)
232 {
233 int was_error;
234 struct timeval tv;
235
236 /* Set the timeout interval - Linux only needs to do this once */
237 tv.tv_sec = 0;
238 tv.tv_usec = usec;
239 do {
240 errno = 0;
241 was_error = select(0, NULL, NULL, NULL, &tv);
242 } while (was_error && (errno == EINTR));
243 }
244
245 uint64 frame_time = 0;
246
247 static Boolean work_proc(XtPointer client_data)
248 {
249 ioctl(fd, VIDIOCMCAPTURE, &(mm[bufnum ^ 1]));
250 ioctl(fd, VIDIOCSYNC, &bufnum);
251
252 uint64 now = GetTicks_usec();
253 #if LOGGING
254 fprintf(log, "frame_time: %Ld\n", now - frame_time);
255 #endif
256 frame_time = now;
257
258 #if DOUBLE_FRAME
259 char *src = (char *)buf + mbuf.offsets[bufnum];
260 char *dst = image[0]->data;
261 for (unsigned y=0; y<grab_height; y+=2) {
262 memcpy(dst, src, grab_width * BYTES_PER_PIXEL);
263 src += grab_width * BYTES_PER_PIXEL * 2;
264 dst += grab_width * BYTES_PER_PIXEL;
265 }
266
267 uint64 prev = GetTicks_usec();
268
269 XvShmPutImage(
270 dpy, port, XtWindow(video), gc, image[0],
271 0, 0, image_width, image_height,
272 0, 0, win_width, win_height,
273 False
274 );
275
276 src = (char *)buf + mbuf.offsets[bufnum] + grab_width * BYTES_PER_PIXEL;
277 dst = image[1]->data;
278 for (unsigned y=0; y<grab_height; y+=2) {
279 memcpy(dst, src, grab_width * BYTES_PER_PIXEL);
280 src += grab_width * BYTES_PER_PIXEL * 2;
281 dst += grab_width * BYTES_PER_PIXEL;
282 }
283
284 XSync(dpy, False);
285
286 now = GetTicks_usec();
287 uint64 elapsed = now - prev;
288 int64 delay = 1000000 / fps - (now - prev);
289 #if LOGGING
290 fprintf(log, "elapsed %Ld usec, delay %Ld usec\n", elapsed, delay);
291 #endif
292 if (delay > 0)
293 Delay_usec(delay);
294
295 XvShmPutImage(
296 dpy, port, XtWindow(video), gc, image[1],
297 0, 0, image_width, image_height,
298 0, 0, win_width, win_height,
299 False
300 );
301 XSync(dpy, False);
302 #else
303 memcpy(image[0]->data, (char *)buf + mbuf.offsets[bufnum], image[0]->data_size);
304
305 XvShmPutImage(
306 dpy, port, XtWindow(video), gc, image[0],
307 0, 0, image_width, image_height,
308 0, 0, win_width, win_height,
309 False
310 );
311 XSync(dpy, False);
312 #endif
313
314 bufnum ^= 1;
315
316 return False;
317 }
318
319 int main(int argc, char **argv)
320 {
321 static struct option long_opts[] = {
322 {"help", 0, 0, 'h'},
323 {"ntsc", 0, 0, 'n'},
324 {"port", 1, 0, 'p'},
325 {"width", 1, 0, 'w'},
326 {NULL, 0, 0, 0}
327 };
328
329 #if LOGGING
330 log = fopen("log", "w");
331 #endif
332
333 // Parse options
334 for (;;) {
335 int c;
336 if ((c = getopt_long(argc, argv, "hnp:w:", long_opts,NULL)) == -1)
337 break;
338
339 switch (c) {
340 case 0: /* Long option */
341 break;
342 case 'n':
343 ntsc = true;
344 break;
345 case 'p':
346 port = atoi(optarg);
347 break;
348 case 'w':
349 grab_width = atoi(optarg);
350 break;
351 case 'h':
352 default:
353 fprintf(stderr,
354 "50/60Hz video display application\n\n"
355 "Options:\n"
356 " -h | --help this text\n"
357 " -n | --ntsc NTSC mode\n"
358 " -p | --port n Xv output port\n"
359 " -w | --width n image width\n\n"
360 "Keyboard commands:\n"
361 " q quit\n"
362 " 1/2 select channel\n"
363 " KP 7/4/1 adjust brightness\n"
364 " KP 8/5/2 adjust contrast\n"
365 " KP 9/6/3 adjust color\n"
366 );
367 exit(1);
368 break;
369 }
370 }
371
372 // Init X11
373 XtAppContext app_context;
374 app_shell = XtAppInitialize(&app_context, "mintv", NULL, 0, &argc, argv, NULL, NULL, 0);
375 dpy = XtDisplay(app_shell);
376 XtAppAddActions(app_context,actionTable, sizeof(actionTable) / sizeof(XtActionsRec));
377 XtOverrideTranslations(app_shell, XtParseTranslationTable(
378 "<Message>WM_PROTOCOLS: quit()\n"
379 "<Key>q: quit()\n"
380 "<Key>Escape: quit()\n"
381 "<Key>1: select_channel_1()\n"
382 "<Key>2: select_channel_2()\n"
383 "<Key>KP_7: inc_brightness()\n"
384 "<Key>KP_4: reset_brightness()\n"
385 "<Key>KP_1: dec_brightness()\n"
386 "<Key>KP_8: inc_contrast()\n"
387 "<Key>KP_5: reset_contrast()\n"
388 "<Key>KP_2: dec_contrast()\n"
389 "<Key>KP_9: inc_color()\n"
390 "<Key>KP_6: reset_color()\n"
391 "<Key>KP_3: dec_color()"
392 ));
393 XtAddEventHandler(app_shell, StructureNotifyMask, True, resize_event, NULL);
394 wm = XInternAtom(XtDisplay(app_shell), "WM_DELETE_WINDOW", False);
395
396 // Xvideo available?
397 unsigned ver, rel, req, ev, err, val;
398 if (XvQueryExtension(dpy, &ver, &rel, &req, &ev, &err) != Success) {
399 fprintf(stderr, "Server doesn't support Xvideo\n");
400 exit(1);
401 }
402
403 // Yes, query available adaptors
404 unsigned num_adaptors;
405 XvAdaptorInfo *ai;
406 if (XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &num_adaptors, &ai) != Success) {
407 fprintf(stderr, "XvQueryAdaptors failed\n");
408 exit(1);
409 }
410
411 // Find usable port
412 if (port < 0) {
413 for (unsigned i=0; i<num_adaptors; i++) {
414 if (ai[i].type & XvImageMask) {
415 for (unsigned p=ai[i].base_id; p<ai[i].base_id+ai[i].num_ports; p++) {
416 int num_formats;
417 XvImageFormatValues *fo = XvListImageFormats(dpy, p, &num_formats);
418 if (fo) {
419 for (unsigned j=0; j<num_formats; j++) {
420 if (fo[j].id == XV_FORMAT) {
421 port = p;
422 goto port_found;
423 }
424 }
425 XFree(fo);
426 }
427 }
428 }
429 }
430 port_found:
431 ;
432 }
433 if (port < 0) {
434 fprintf(stderr, "No suitable XVideo port found\n");
435 exit(1);
436 } else
437 printf("Using XVideo port %d\n", port);
438
439 // Set grab and window dimensions
440 if (ntsc) {
441 if (grab_width == -1)
442 grab_width = NTSC_WIDTH;
443 grab_height = NTSC_HEIGHT;
444 fps = NTSC_FPS;
445 } else {
446 if (grab_width == -1)
447 grab_width = PAL_WIDTH;
448 grab_height = PAL_HEIGHT;
449 fps = PAL_FPS;
450 }
451 image_width = grab_width;
452 image_height = grab_height;
453 win_width = DisplayWidth(dpy, DefaultScreen(dpy));
454 win_height = DisplayHeight(dpy, DefaultScreen(dpy));
455 #if DOUBLE_FRAME
456 grab_height *= 2;
457 #endif
458
459 // Open window
460 video = XtVaCreateManagedWidget("video", simpleWidgetClass, app_shell,
461 XtNwidth, win_width,
462 XtNheight, win_height,
463 NULL
464 );
465 XtRealizeWidget(app_shell);
466 Cursor null_cursor = XCreateFontCursor(dpy, XC_left_ptr);
467 XDefineCursor(dpy, XtWindow(app_shell), null_cursor);
468 XtVaSetValues(app_shell,
469 XtNtitle, "mintv",
470 XtNinput, True,
471 NULL
472 );
473 XSetWMProtocols(dpy, XtWindow(app_shell), &wm, 1);
474 gc = XCreateGC(dpy, XtWindow(video), 0, NULL);
475
476 // Set image format
477 unsigned format = XV_FORMAT;
478
479 // Create image
480 image[0] = XvShmCreateImage(dpy, port, format, NULL, image_width, image_height, &shminfo);
481 image[1] = XvShmCreateImage(dpy, port, format, NULL, image_width, image_height, &shminfo);
482 shminfo.shmid = shmget(IPC_PRIVATE, image[0]->data_size * 2, IPC_CREAT | 0777);
483 shminfo.shmaddr = (char *)shmat(shminfo.shmid, NULL, 0);
484 shminfo.readOnly = False;
485 image[0]->data = shminfo.shmaddr;
486 image[1]->data = shminfo.shmaddr + image[0]->data_size;
487 XShmAttach(dpy, &shminfo);
488 XSync(dpy, False);
489 shmctl(shminfo.shmid, IPC_RMID, 0);
490 memset(image[0]->data, 0x80, image[0]->data_size * 2);
491
492 // Open frame grabber device
493 fd = open("/dev/video0", O_RDWR);
494 if (fd < 0) {
495 fprintf(stderr, "Can't open /dev/video0: %s\n", strerror(errno));
496 exit(1);
497 }
498
499 // Set channel
500 set_channel();
501
502 video_audio au;
503 au.audio = 1;
504 if (ioctl(fd, VIDIOCGAUDIO, &au) < 0) {
505 fprintf(stderr, "ioctl VIDIOCGAUDIO: %s\n", strerror(errno));
506 exit(1);
507 }
508 au.flags &= ~VIDEO_AUDIO_MUTE;
509 if (ioctl(fd, VIDIOCSAUDIO, &au) < 0) {
510 fprintf(stderr, "ioctl VIDIOCSAUDIO: %s\n", strerror(errno));
511 exit(1);
512 }
513
514 // Configure frame grabber
515 set_bcc();
516
517 if (ioctl(fd, VIDIOCGMBUF, &mbuf) < 0) {
518 fprintf(stderr, "ioctl VIDIOCGMBUF: %s\n", strerror(errno));
519 exit(1);
520 }
521
522 buf = mmap(NULL, mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
523 if (buf == NULL) {
524 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
525 exit(1);
526 }
527
528 mm[0].frame = 0;
529 mm[0].width = grab_width;
530 mm[0].height = grab_height;
531 mm[0].format = VIDEO_PALETTE_YUV422;
532 mm[1].frame = 1;
533 mm[1].width = grab_width;
534 mm[1].height = grab_height;
535 mm[1].format = VIDEO_PALETTE_YUV422;
536 ioctl(fd, VIDIOCMCAPTURE, &(mm[bufnum]));
537
538 // Disable screen saver
539 XSetScreenSaver(dpy, 0, 0, DefaultBlanking, DefaultExposures);
540
541 // Main loop
542 XtAppAddWorkProc(app_context, work_proc, NULL);
543 XtAppMainLoop(app_context);
544
545 return 0;
546 }