ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/mintv/mintv.cpp
Revision: 1.4
Committed: 2003-03-07T18:17:09Z (21 years, 2 months ago) by cebix
Branch: MAIN
Changes since 1.3: +16 -8 lines
Log Message:
display width can be specified with the -w switch

File Contents

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