ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/mintv/mintv.cpp
Revision: 1.1.1.1 (vendor branch)
Committed: 2003-02-08T17:51:47Z (21 years, 9 months ago) by cebix
Branch: cebix
CVS Tags: start
Changes since 1.1: +0 -0 lines
Log Message:
imported sources

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/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 }