ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/mintv/mintv.cpp
Revision: 1.3
Committed: 2003-03-01T15:23:26Z (21 years, 2 months ago) by cebix
Branch: MAIN
Changes since 1.2: +24 -24 lines
Log Message:
parse arguments before initializing X

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 static bool ntsc = false;
31
32 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 static int brightness = 50, contrast = 50, color = 50;
47
48 static int channel = 1;
49
50 #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 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 static XtActionsRec actionTable[] = {
193 {"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 };
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 // Parse options
332 for (;;) {
333 int c;
334 if ((c = getopt_long(argc, argv, "hnp:", long_opts,NULL)) == -1)
335 break;
336
337 switch (c) {
338 case 0: /* Long option */
339 break;
340 case 'n':
341 ntsc = true;
342 break;
343 case 'p':
344 port = atoi(optarg);
345 break;
346 case 'h':
347 default:
348 fprintf(stderr,
349 "50/60Hz video display application\n\n"
350 "Options:\n"
351 " -h | --help this text\n"
352 " -n | --ntsc NTSC mode\n"
353 " -p | --port n Xv output port\n\n"
354 "Keyboard commands:\n"
355 " q quit\n"
356 " 1/2 select channel\n"
357 " KP 7/4/1 adjust brightness\n"
358 " KP 8/5/2 adjust contrast\n"
359 " KP 9/6/3 adjust color\n"
360 );
361 exit(1);
362 break;
363 }
364 }
365
366 // Init X11
367 XtAppContext app_context;
368 app_shell = XtAppInitialize(&app_context, "mintv", NULL, 0, &argc, argv, NULL, NULL, 0);
369 dpy = XtDisplay(app_shell);
370 XtAppAddActions(app_context,actionTable, sizeof(actionTable) / sizeof(XtActionsRec));
371 XtOverrideTranslations(app_shell, XtParseTranslationTable(
372 "<Message>WM_PROTOCOLS: quit()\n"
373 "<Key>q: quit()\n"
374 "<Key>1: select_channel_1()\n"
375 "<Key>2: select_channel_2()\n"
376 "<Key>KP_7: inc_brightness()\n"
377 "<Key>KP_4: reset_brightness()\n"
378 "<Key>KP_1: dec_brightness()\n"
379 "<Key>KP_8: inc_contrast()\n"
380 "<Key>KP_5: reset_contrast()\n"
381 "<Key>KP_2: dec_contrast()\n"
382 "<Key>KP_9: inc_color()\n"
383 "<Key>KP_6: reset_color()\n"
384 "<Key>KP_3: dec_color()"
385 ));
386 XtAddEventHandler(app_shell, StructureNotifyMask, True, resize_event, NULL);
387 wm = XInternAtom(XtDisplay(app_shell), "WM_DELETE_WINDOW", False);
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 set_channel();
489
490 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 set_bcc();
504
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 }