ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/mintv/mintv.cpp
Revision: 1.2
Committed: 2003-02-25T21:10:35Z (21 years, 2 months ago) by cebix
Branch: MAIN
Changes since 1.1: +147 -31 lines
Log Message:
added picture controls and input selection

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     const int NTSC_WIDTH = 320;
35     const int NTSC_HEIGHT = 240;
36    
37     static int grab_width, grab_height;
38     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     {NULL, 0, 0, 0}
325     };
326    
327     #if LOGGING
328     log = fopen("log", "w");
329     #endif
330    
331     // Init X11
332     XtAppContext app_context;
333     app_shell = XtAppInitialize(&app_context, "mintv", NULL, 0, &argc, argv, NULL, NULL, 0);
334     dpy = XtDisplay(app_shell);
335     XtAppAddActions(app_context,actionTable, sizeof(actionTable) / sizeof(XtActionsRec));
336     XtOverrideTranslations(app_shell, XtParseTranslationTable(
337     "<Message>WM_PROTOCOLS: quit()\n"
338 cebix 1.2 "<Key>q: quit()\n"
339     "<Key>1: select_channel_1()\n"
340     "<Key>2: select_channel_2()\n"
341     "<Key>KP_7: inc_brightness()\n"
342     "<Key>KP_4: reset_brightness()\n"
343     "<Key>KP_1: dec_brightness()\n"
344     "<Key>KP_8: inc_contrast()\n"
345     "<Key>KP_5: reset_contrast()\n"
346     "<Key>KP_2: dec_contrast()\n"
347     "<Key>KP_9: inc_color()\n"
348     "<Key>KP_6: reset_color()\n"
349     "<Key>KP_3: dec_color()"
350 cebix 1.1 ));
351     XtAddEventHandler(app_shell, StructureNotifyMask, True, resize_event, NULL);
352     wm = XInternAtom(XtDisplay(app_shell), "WM_DELETE_WINDOW", False);
353    
354     // Parse options
355     for (;;) {
356     int c;
357     if ((c = getopt_long(argc, argv, "hnp:", long_opts,NULL)) == -1)
358     break;
359    
360     switch (c) {
361     case 0: /* Long option */
362     break;
363     case 'n':
364     ntsc = true;
365     break;
366     case 'p':
367     port = atoi(optarg);
368     break;
369     case 'h':
370     default:
371     fprintf(stderr,
372 cebix 1.2 "50/60Hz video display application\n"
373 cebix 1.1 "Options:\n"
374     " -h | --help this text\n"
375     " -n | --ntsc NTSC mode\n"
376 cebix 1.2 " -p | --port n Xv output port\n\n"
377     "Keyboard commands:\n"
378     " q quit\n"
379     " 1/2 select channel\n"
380     " KP 7/4/1 adjust brightness\n"
381     " KP 8/5/2 adjust contrast\n"
382     " KP 9/6/3 adjust color\n"
383 cebix 1.1 );
384     exit(1);
385     break;
386     }
387     }
388    
389     // Xvideo available?
390     unsigned ver, rel, req, ev, err, val;
391     if (XvQueryExtension(dpy, &ver, &rel, &req, &ev, &err) != Success) {
392     fprintf(stderr, "Server doesn't support Xvideo\n");
393     exit(1);
394     }
395    
396     // Yes, query available adaptors
397     unsigned num_adaptors;
398     XvAdaptorInfo *ai;
399     if (XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &num_adaptors, &ai) != Success) {
400     fprintf(stderr, "XvQueryAdaptors failed\n");
401     exit(1);
402     }
403    
404     // Find usable port
405     if (port < 0) {
406     for (unsigned i=0; i<num_adaptors; i++) {
407     if (ai[i].type & XvImageMask) {
408     for (unsigned p=ai[i].base_id; p<ai[i].base_id+ai[i].num_ports; p++) {
409     int num_formats;
410     XvImageFormatValues *fo = XvListImageFormats(dpy, p, &num_formats);
411     if (fo) {
412     for (unsigned j=0; j<num_formats; j++) {
413     if (fo[j].id == XV_FORMAT) {
414     port = p;
415     goto port_found;
416     }
417     }
418     XFree(fo);
419     }
420     }
421     }
422     }
423     port_found:
424     ;
425     }
426     if (port < 0) {
427     fprintf(stderr, "No suitable XVideo port found\n");
428     exit(1);
429     } else
430     printf("Using XVideo port %d\n", port);
431    
432     // Set grab and window dimensions
433     if (ntsc) {
434     grab_width = NTSC_WIDTH;
435     grab_height = NTSC_HEIGHT;
436     fps = NTSC_FPS;
437     } else {
438     grab_width = PAL_WIDTH;
439     grab_height = PAL_HEIGHT;
440     fps = PAL_FPS;
441     }
442     image_width = grab_width;
443     image_height = grab_height;
444     win_width = DisplayWidth(dpy, DefaultScreen(dpy));
445     win_height = DisplayHeight(dpy, DefaultScreen(dpy));
446     #if DOUBLE_FRAME
447     grab_height *= 2;
448     #endif
449    
450     // Open window
451     video = XtVaCreateManagedWidget("video", simpleWidgetClass, app_shell,
452     XtNwidth, win_width,
453     XtNheight, win_height,
454     NULL
455     );
456     XtRealizeWidget(app_shell);
457     XtVaSetValues(app_shell,
458     XtNtitle, "mintv",
459     NULL
460     );
461     XSetWMProtocols(XtDisplay(app_shell), XtWindow(app_shell), &wm, 1);
462     gc = XCreateGC(dpy, XtWindow(video), 0, NULL);
463    
464     // Set image format
465     unsigned format = XV_FORMAT;
466    
467     // Create image
468     image[0] = XvShmCreateImage(dpy, port, format, NULL, image_width, image_height, &shminfo);
469     image[1] = XvShmCreateImage(dpy, port, format, NULL, image_width, image_height, &shminfo);
470     shminfo.shmid = shmget(IPC_PRIVATE, image[0]->data_size * 2, IPC_CREAT | 0777);
471     shminfo.shmaddr = (char *)shmat(shminfo.shmid, NULL, 0);
472     shminfo.readOnly = False;
473     image[0]->data = shminfo.shmaddr;
474     image[1]->data = shminfo.shmaddr + image[0]->data_size;
475     XShmAttach(dpy, &shminfo);
476     XSync(dpy, False);
477     shmctl(shminfo.shmid, IPC_RMID, 0);
478     memset(image[0]->data, 0x80, image[0]->data_size * 2);
479    
480     // Open frame grabber device
481     fd = open("/dev/video0", O_RDWR);
482     if (fd < 0) {
483     fprintf(stderr, "Can't open /dev/video0: %s\n", strerror(errno));
484     exit(1);
485     }
486    
487     // Set channel
488 cebix 1.2 set_channel();
489    
490 cebix 1.1 video_audio au;
491     au.audio = 1;
492     if (ioctl(fd, VIDIOCGAUDIO, &au) < 0) {
493     fprintf(stderr, "ioctl VIDIOCGAUDIO: %s\n", strerror(errno));
494     exit(1);
495     }
496     au.flags &= ~VIDEO_AUDIO_MUTE;
497     if (ioctl(fd, VIDIOCSAUDIO, &au) < 0) {
498     fprintf(stderr, "ioctl VIDIOCSAUDIO: %s\n", strerror(errno));
499     exit(1);
500     }
501    
502     // Configure frame grabber
503 cebix 1.2 set_bcc();
504 cebix 1.1
505     if (ioctl(fd, VIDIOCGMBUF, &mbuf) < 0) {
506     fprintf(stderr, "ioctl VIDIOCGMBUF: %s\n", strerror(errno));
507     exit(1);
508     }
509    
510     buf = mmap(NULL, mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
511     if (buf == NULL) {
512     fprintf(stderr, "mmap failed: %s\n", strerror(errno));
513     exit(1);
514     }
515    
516     mm[0].frame = 0;
517     mm[0].width = grab_width;
518     mm[0].height = grab_height;
519     mm[0].format = VIDEO_PALETTE_YUV422;
520     mm[1].frame = 1;
521     mm[1].width = grab_width;
522     mm[1].height = grab_height;
523     mm[1].format = VIDEO_PALETTE_YUV422;
524     ioctl(fd, VIDIOCMCAPTURE, &(mm[bufnum]));
525    
526     // Disable screen saver
527     XSetScreenSaver(dpy, 0, 0, DefaultBlanking, DefaultExposures);
528    
529     // Main loop
530     XtAppAddWorkProc(app_context, work_proc, NULL);
531     XtAppMainLoop(app_context);
532    
533     return 0;
534     }