ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/mintv/mintv.cpp
Revision: 1.1
Committed: 2003-02-08T17:51:47Z (21 years, 9 months ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

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