Mercurial > ~darius > hgwebdir.cgi > simpletv
comparison simpletv.c @ 1:769b155a34f9 SIMPETV_0_1
Initial revision
author | darius |
---|---|
date | Tue, 04 Jan 2005 05:17:43 +0000 |
parents | |
children | 8d7d1680db7d |
comparison
equal
deleted
inserted
replaced
0:da66cf40a013 | 1:769b155a34f9 |
---|---|
1 /* Capture and store a 24 bit RGB image as a PPM file */ | |
2 /* Copyright (c) 2000 Randall Hopper | |
3 * | |
4 * Based on a grab.c from Roger Hardiman, which was | |
5 * based on an original program by Mark Tinguely and Jim Lowe . | |
6 * | |
7 * All rights reserved. | |
8 * | |
9 * Redistribution and use in source and binary forms, with or without | |
10 * modification, are permitted provided that the following conditions | |
11 * are met: | |
12 * 1. Redistributions of source code must retain the above copyright | |
13 * notice, this list of conditions and the following disclaimer. | |
14 * 2. Redistributions in binary form must reproduce the above copyright | |
15 * notice, this list of conditions and the following disclaimer in the | |
16 * documentation and/or other materials provided with the distribution. | |
17 * 3. All advertising materials mentioning features or use of this software | |
18 * must display the following acknowledgement: | |
19 * This product includes software developed by Mark Tinguely and Jim Lowe | |
20 * 4. The name of the author may not be used to endorse or promote products | |
21 * derived from this software without specific prior written permission. | |
22 * | |
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
33 * POSSIBILITY OF SUCH DAMAGE. | |
34 */ | |
35 | |
36 #include <assert.h> | |
37 #include <stdio.h> | |
38 #include <unistd.h> | |
39 #include <string.h> | |
40 #include <stdlib.h> | |
41 #include <errno.h> | |
42 #include <sys/types.h> | |
43 #include <poll.h> | |
44 #include <sys/mman.h> | |
45 #include <sys/fcntl.h> | |
46 #include <sys/time.h> | |
47 #include <libgen.h> | |
48 #include <sys/soundcard.h> | |
49 #include <machine/ioctl_bt848.h> | |
50 #include <machine/ioctl_meteor.h> | |
51 #include <machine/param.h> | |
52 #include <sys/types.h> | |
53 #include <sys/ipc.h> | |
54 #include <sys/shm.h> | |
55 #include <X11/Xlib.h> | |
56 #include <X11/Xutil.h> | |
57 #include <X11/extensions/XShm.h> | |
58 #include <X11/extensions/Xvlib.h> | |
59 #ifndef USE_XVIMAGES | |
60 #include "Hermes/Hermes.h" | |
61 #endif | |
62 | |
63 #include <lirc/lirc_client.h> | |
64 | |
65 /* Some BT848/BT878 cards have gain control hardware which takes | |
66 1 or 2 seconds to settle. If you get a washed out / too bright | |
67 image, change the GRABBER_SETTLE_TIME from 0 to either 1 or 2 | |
68 */ | |
69 /*#define GRABBER_SETTLE_TIME 10*/ | |
70 #define GRABBER_SETTLE_TIME 0 | |
71 | |
72 | |
73 #define PAL 1 | |
74 #define NTSC 2 | |
75 | |
76 /* PAL is 768 x 576. NTSC is 640 x 480 */ | |
77 #define PAL_HEIGHT 576 | |
78 #define NTSC_HEIGHT 480 | |
79 | |
80 int bktr_fd; | |
81 int tuner_fd; | |
82 unsigned char *bktr_buffer; | |
83 #if 1 | |
84 int width = 320; | |
85 int height = 240; | |
86 #else | |
87 int width = 640; | |
88 int height = 480; | |
89 #endif | |
90 | |
91 #ifndef USE_XVIMAGES | |
92 static XImage *rgb_image; | |
93 static Pixmap pmap; | |
94 #endif | |
95 static XShmSegmentInfo shminfo; | |
96 static Display *disp; | |
97 static Window win; | |
98 static Visual *vis; | |
99 static int scr; | |
100 static GC gc; | |
101 static int depth; | |
102 static Colormap cmap; | |
103 static XVisualInfo *vi; | |
104 static int bits_per_pixel; | |
105 static XvImage *yuv_image; | |
106 static XvAdaptorInfo *xv_adaptors; | |
107 static int xv_num_adaptors; | |
108 static int xv_adaptor; | |
109 static int xv_format_id; | |
110 | |
111 static int channel; | |
112 | |
113 #define DO_IOCTL_GERR(str) fprintf(stderr, "ioctl(%s) failed: %s\n", \ | |
114 str, strerror(errno) ) | |
115 #define DO_IOCTL_SERR(str,arg) fprintf(stderr, "ioctl(%s, %ld) failed: %s\n",\ | |
116 str, (long)arg, strerror(errno) ) | |
117 /*--------------------------------------------------------------------------*/ | |
118 | |
119 void Close() | |
120 { | |
121 close(tuner_fd); | |
122 close(bktr_fd); | |
123 } | |
124 | |
125 void Open() | |
126 { | |
127 struct meteor_geomet geo; | |
128 int buffer_size, format, source, c; | |
129 char *device_name; | |
130 | |
131 format = PAL; /* default value */ | |
132 source = 1; /* default value */ | |
133 device_name = "/dev/bktr0"; /* default value */ | |
134 | |
135 /* Open the Meteor or Bt848/Bt878 grabber */ | |
136 if ((bktr_fd = open(device_name, O_RDONLY)) < 0) { | |
137 printf("open failed: %d\n", errno); | |
138 exit(1); | |
139 } | |
140 | |
141 if ((tuner_fd = open("/dev/tuner0", O_RDONLY)) < 0) { | |
142 printf("open failed: %d\n", errno); | |
143 exit(1); | |
144 } | |
145 | |
146 /* set up the capture type and size */ | |
147 geo.rows = height; | |
148 geo.columns = width; | |
149 geo.frames = 1; | |
150 #ifdef USE_XVIMAGES | |
151 /* Should be YUV_12, but 422 actually gives a synced picture. Though */ | |
152 | |
153 /*geo.oformat = METEOR_GEO_YUV_12;*/ | |
154 geo.oformat = METEOR_GEO_YUV_422 | METEOR_GEO_YUV_12; | |
155 /* geo.oformat = METEOR_GEO_YUV_12; */ | |
156 #else | |
157 geo.oformat = METEOR_GEO_RGB24; | |
158 #endif | |
159 | |
160 /* switch from interlaced capture to single field capture if */ | |
161 /* the grab height is less than half the normal TV height */ | |
162 /* this gives better quality captures when the object in the TV */ | |
163 /* picture is moving */ | |
164 if ((format == PAL) && (height <= (PAL_HEIGHT/2))) | |
165 geo.oformat |= METEOR_GEO_ODD_ONLY; | |
166 if ((format == NTSC) && (height <= (NTSC_HEIGHT/2))) | |
167 geo.oformat |= METEOR_GEO_ODD_ONLY; | |
168 | |
169 if (ioctl(bktr_fd, METEORSETGEO, &geo) < 0) { | |
170 printf("METEORSETGEO ioctl failed: %d\n", errno); | |
171 exit(1); | |
172 } | |
173 | |
174 /* Select PAL or NTSC */ | |
175 switch (format) { | |
176 case PAL: c = METEOR_FMT_PAL; break; | |
177 case NTSC: c = METEOR_FMT_NTSC; break; | |
178 default: c = METEOR_FMT_NTSC; break; | |
179 } | |
180 | |
181 c = BT848_IFORM_F_PALBDGHI; | |
182 if ( ioctl( bktr_fd, BT848SFMT, &c ) < 0 ) { | |
183 DO_IOCTL_SERR( "BT848SFMT", c ); | |
184 return; | |
185 } | |
186 c = AUDIO_TUNER; | |
187 if ( ioctl( tuner_fd, BT848_SAUDIO, &c ) < 0 ) { | |
188 DO_IOCTL_SERR( "BT848SFMT", c ); | |
189 return; | |
190 } | |
191 c = CHNLSET_AUSTRALIA; | |
192 if ( ioctl( tuner_fd, TVTUNER_SETTYPE, &c ) < 0 ) { | |
193 DO_IOCTL_SERR( "TVTUNER_SETTYPE", c ); | |
194 return; | |
195 } | |
196 | |
197 if ( ioctl( tuner_fd, TVTUNER_SETCHNL, &channel ) < 0 ) { | |
198 DO_IOCTL_SERR( "TVTUNER_SETCHNL", channel ); | |
199 return; | |
200 } | |
201 | |
202 | |
203 /* Select the Video Source */ | |
204 /* Video In, Tuner, S-Video */ | |
205 switch (source) { | |
206 case 0: c = METEOR_INPUT_DEV0; break; | |
207 case 1: c = METEOR_INPUT_DEV1; break; | |
208 case 2: c = METEOR_INPUT_DEV2; break; | |
209 case 3: c = METEOR_INPUT_DEV3; break; | |
210 default: c = METEOR_INPUT_DEV0; break; | |
211 } | |
212 | |
213 printf("Input - %x\n", c); | |
214 if (ioctl(bktr_fd, METEORSINPUT, &c) < 0) { | |
215 printf("ioctl failed: %d\n", errno); | |
216 exit(1); | |
217 } | |
218 | |
219 /* Use mmap to Map the drivers grab buffer */ | |
220 buffer_size = width*height*4; /* R,G,B,spare */ | |
221 bktr_buffer = (unsigned char *)mmap((caddr_t)0,buffer_size,PROT_READ, | |
222 MAP_SHARED, bktr_fd, (off_t)0); | |
223 | |
224 if (bktr_buffer == (unsigned char *) MAP_FAILED) | |
225 exit(1); | |
226 | |
227 | |
228 /* We may need to wait for a short time to allow the grabber */ | |
229 /* brightness to settle down */ | |
230 sleep(GRABBER_SETTLE_TIME); | |
231 } | |
232 | |
233 /*--------------------------------------------------------------------------*/ | |
234 | |
235 void Capture() | |
236 { | |
237 int c; | |
238 | |
239 /* Perform a single frame capture */ | |
240 c = METEOR_CAP_SINGLE ; | |
241 ioctl(bktr_fd, METEORCAPTUR, &c); | |
242 } | |
243 | |
244 /*--------------------------------------------------------------------------*/ | |
245 | |
246 void SaveImage() | |
247 { | |
248 unsigned char *line_buffer; | |
249 int o,w,h; | |
250 unsigned char *p; | |
251 unsigned char header[30]; | |
252 char *filename = "t.ppm" /* argv[3] */; | |
253 | |
254 /* Create the output file */ | |
255 if ((o = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) { | |
256 printf("ppm open failed: %d\n", errno); | |
257 exit(1); | |
258 } | |
259 | |
260 /* make PPM header and save to file */ | |
261 sprintf(header, "P6\n%d\n%d\n255\n",width,height); | |
262 write (o, header, strlen(header)); | |
263 | |
264 /* save the RGB data to PPM file */ | |
265 /* save this one line at a time */ | |
266 line_buffer =(unsigned char *)malloc( width *3 * sizeof(unsigned char)); | |
267 p = bktr_buffer; | |
268 for (h = 0; h < height; h++) { | |
269 for (w = 0; w < width; w++) { | |
270 line_buffer[(w*3) + 2] = *p++; /* blue */ | |
271 line_buffer[(w*3) + 1] = *p++; /* green */ | |
272 line_buffer[(w*3) + 0] = *p++; /* red */ | |
273 p++; /* NULL byte */ | |
274 } | |
275 write(o,line_buffer, width*3); | |
276 } | |
277 close(o); | |
278 free(line_buffer); | |
279 } | |
280 | |
281 void X_ShowCursor(void) { | |
282 XDefineCursor(disp, win, 0); | |
283 } | |
284 | |
285 void X_HideCursor(void) { | |
286 Cursor no_ptr; | |
287 Pixmap bm_no; | |
288 XColor black,dummy; | |
289 Colormap colormap; | |
290 static unsigned char bm_no_data[] = {0,0,0,0, 0,0,0,0}; | |
291 | |
292 colormap = DefaultColormap(disp, DefaultScreen(disp)); | |
293 XAllocNamedColor(disp, colormap, "black", &black, &dummy); | |
294 bm_no = XCreateBitmapFromData(disp, win, bm_no_data, 8, 8); | |
295 no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0); | |
296 | |
297 XDefineCursor(disp, win, no_ptr); | |
298 XFreeCursor(disp, no_ptr); | |
299 if (bm_no != None) | |
300 XFreePixmap(disp, bm_no); | |
301 | |
302 } | |
303 | |
304 /* | |
305 * Sends the EWMH fullscreen state event. | |
306 * | |
307 * action: could be on of _NET_WM_STATE_REMOVE -- remove state | |
308 * _NET_WM_STATE_ADD -- add state | |
309 * _NET_WM_STATE_TOGGLE -- toggle | |
310 */ | |
311 #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ | |
312 #define _NET_WM_STATE_ADD 1 /* add/set property */ | |
313 #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ | |
314 void | |
315 vo_x11_ewmh_fullscreen(int action) { | |
316 XEvent xev; | |
317 | |
318 assert(action == _NET_WM_STATE_REMOVE || | |
319 action == _NET_WM_STATE_ADD || action == _NET_WM_STATE_TOGGLE); | |
320 | |
321 | |
322 /* init X event structure for _NET_WM_FULLSCREEN client msg */ | |
323 xev.xclient.type = ClientMessage; | |
324 xev.xclient.serial = 0; | |
325 xev.xclient.send_event = True; | |
326 xev.xclient.message_type = XInternAtom(disp, "_NET_WM_STATE", False); | |
327 xev.xclient.window = win; | |
328 xev.xclient.format = 32; | |
329 xev.xclient.data.l[0] = action; | |
330 xev.xclient.data.l[1] = XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False); | |
331 xev.xclient.data.l[2] = 0; | |
332 xev.xclient.data.l[3] = 0; | |
333 xev.xclient.data.l[4] = 0; | |
334 | |
335 /* finally send that damn thing */ | |
336 if (!XSendEvent(disp, DefaultRootWindow(disp), False, | |
337 SubstructureRedirectMask | SubstructureNotifyMask, | |
338 &xev)) { | |
339 fprintf(stderr, "Failed to send fullscreen command\n"); | |
340 } | |
341 } | |
342 | |
343 | |
344 void X_Setup(int w,int h) | |
345 { | |
346 XGCValues gcvals; | |
347 Window root; | |
348 XVisualInfo vinfo_pref; | |
349 int num_vis; | |
350 XPixmapFormatValues *pf; | |
351 int num_pf, pfi, i,j; | |
352 | |
353 disp = XOpenDisplay(NULL); | |
354 if (!disp) { | |
355 fprintf(stderr,"X-Error: unable to connect to display\n"); | |
356 exit(1); | |
357 } | |
358 | |
359 XSynchronize( disp, True ); | |
360 | |
361 scr=DefaultScreen(disp); | |
362 vis=DefaultVisual(disp,scr); | |
363 root=DefaultRootWindow(disp); | |
364 depth=DefaultDepth(disp,scr); | |
365 | |
366 vinfo_pref.screen = scr; | |
367 vinfo_pref.visualid = XVisualIDFromVisual( vis ); | |
368 vi = XGetVisualInfo( disp, VisualScreenMask | VisualIDMask, | |
369 &vinfo_pref, &num_vis ); | |
370 assert ( num_vis == 1 ); | |
371 | |
372 win = XCreateSimpleWindow(disp, root, 0, 0, w, h, 0, 0, 0); | |
373 gc = XCreateGC(disp, win, (unsigned long)0, &gcvals); | |
374 XSetForeground(disp, gc, 0); | |
375 XSetBackground(disp, gc, 1); | |
376 | |
377 XMapWindow(disp,win); | |
378 cmap = DefaultColormap(disp, scr); | |
379 XSync(disp,False); | |
380 | |
381 /* Setup with Xv extension */ | |
382 xv_adaptor = -1; | |
383 xv_format_id = -1; | |
384 XvQueryAdaptors( disp, root, &xv_num_adaptors, &xv_adaptors ); | |
385 for ( i = 0; i < xv_num_adaptors; i++ ) { | |
386 XvAdaptorInfo *adaptor = &xv_adaptors[i]; | |
387 int takes_images; | |
388 | |
389 takes_images = adaptor->type & ( XvInputMask | XvImageMask ); | |
390 if ( takes_images ) { | |
391 XvImageFormatValues *formats; | |
392 int num_formats; | |
393 | |
394 formats = XvListImageFormats( disp, adaptor->base_id, &num_formats ); | |
395 for ( j = 0; j < num_formats; j++ ) | |
396 if ( formats[j].type == XvYUV && formats[j].format == XvPlanar && | |
397 strcmp( formats[j].guid, "YV12" ) == 0 ) | |
398 break; | |
399 if ( j < num_formats ) { | |
400 xv_adaptor = i; | |
401 xv_format_id = formats[j].id; | |
402 break; | |
403 } | |
404 } | |
405 } | |
406 assert( xv_adaptor >= 0 ); | |
407 | |
408 /* Create an image to captured frames */ | |
409 #ifdef USE_XVIMAGES | |
410 yuv_im age = XvShmCreateImage( disp, xv_adaptors[xv_adaptor].base_id, | |
411 xv_format_id, 0, w, h, &shminfo ); | |
412 if (!yuv_image) | |
413 fprintf(stderr,"X-Error: unable to create XvShm XImage\n"); | |
414 | |
415 shminfo.shmid = shmget( IPC_PRIVATE, yuv_image->data_size, IPC_CREAT|0777); | |
416 if (shminfo.shmid == -1) | |
417 fprintf(stderr,"SharedMemory Error: unable to get identifier\n"); | |
418 | |
419 shminfo.shmaddr = yuv_image->data = shmat(shminfo.shmid, 0, 0); | |
420 #else | |
421 rgb_image = XShmCreateImage( disp, vis, depth, ZPixmap, NULL, &shminfo, w, h); | |
422 if (!rgb_image) | |
423 fprintf(stderr,"X-Error: unable to create XShm XImage\n"); | |
424 | |
425 shminfo.shmid = shmget( IPC_PRIVATE, | |
426 rgb_image->bytes_per_line * rgb_image->height, | |
427 IPC_CREAT|0777); | |
428 if (shminfo.shmid == -1) | |
429 fprintf(stderr, "SharedMemory Error: unable to get identifier\n"); | |
430 | |
431 shminfo.shmaddr = rgb_image->data = shmat(shminfo.shmid, 0, 0); | |
432 #endif | |
433 | |
434 if(!XShmAttach( disp,&shminfo )) | |
435 fprintf(stderr,"X-Error: unable to attach XShm Shared Memory Segment\n"); | |
436 | |
437 /* Create a pixmap for the window background */ | |
438 #ifdef OLD | |
439 pmap = XShmCreatePixmap(disp, win, shminfo.shmaddr, &shminfo, w, h, depth); | |
440 if (!pmap) | |
441 fprintf(stderr,"Unable to create pixmap\n"); | |
442 #endif | |
443 | |
444 /* Determine bits-per-pixel for pixmaps */ | |
445 pf = XListPixmapFormats( disp, &num_pf); | |
446 assert(pf); | |
447 for (pfi = 0; pfi < num_pf; pfi++) | |
448 if (pf[pfi].depth == vi->depth) | |
449 break; | |
450 assert(pfi < num_pf); | |
451 bits_per_pixel = pf[pfi].bits_per_pixel; | |
452 XFree (pf); | |
453 | |
454 #ifdef OLD | |
455 XSetWindowBackgroundPixmap(disp,win,pmap); | |
456 #endif | |
457 } | |
458 | |
459 /*--------------------------------------------------------------------------*/ | |
460 | |
461 void X_Shutdown() | |
462 { | |
463 XShmDetach( disp, &shminfo ); | |
464 #ifdef USE_XVIMAGES | |
465 # warning How do we destroy an XvImage? | |
466 #else | |
467 XDestroyImage( rgb_image ); | |
468 #endif | |
469 shmdt( shminfo.shmaddr ); | |
470 shmctl( shminfo.shmid, IPC_RMID, 0 ); | |
471 } | |
472 | |
473 | |
474 /*--------------------------------------------------------------------------*/ | |
475 | |
476 void X_Display(void) | |
477 { | |
478 int _w,_h,_d; | |
479 Window _dw; | |
480 | |
481 #ifdef USE_XVIMAGES | |
482 XGetGeometry( disp, win, &_dw, &_d, &_d, &_w, &_h, &_d, &_d); | |
483 XvShmPutImage( disp, xv_adaptors[ xv_adaptor ].base_id, win, | |
484 gc, yuv_image, 0, 0, yuv_image->width, yuv_image->height, | |
485 0, 0, _w, _h, True ); | |
486 #else | |
487 XShmPutImage( disp, win, gc, rgb_image, 0,0,0,0, | |
488 rgb_image->width, rgb_image->height, False ); | |
489 #endif | |
490 XSync(disp, False); | |
491 } | |
492 | |
493 #define CMD_NONE 0 | |
494 #define CMD_CHNUP 1 | |
495 #define CMD_CHNDN 2 | |
496 #define CMD_MUTE 3 | |
497 #define CMD_QUIT 4 | |
498 #define CMD_RELOAD 5 | |
499 #define CMD_CURSOR 6 | |
500 #define CMD_VOLDN 7 | |
501 #define CMD_VOLUP 8 | |
502 #define CMD_FSTOGGLE 9 | |
503 | |
504 /*--------------------------------------------------------------------------*/ | |
505 | |
506 int main(int argc, char *argv[]) { | |
507 #ifndef USE_XVIMAGES | |
508 HermesHandle conv; | |
509 HermesFormat fmt_source,fmt_dest; | |
510 #endif | |
511 XEvent e; | |
512 char *scratch_buf, *code, *c, *mixerdev; | |
513 int frames, channelidx, oldchan, mute, cursor, fd, ret, cmd; | |
514 int exitnow, mfd, uselirc, curvol; | |
515 struct timeval then, now, diff, lastmove; | |
516 float rate; | |
517 KeySym key; | |
518 char text[255]; | |
519 int channellist[] = { 2, 7, 9, 10, 28 }; | |
520 struct lirc_config *config; | |
521 struct pollfd fds[1]; | |
522 | |
523 #define NUMCHANS (sizeof(channellist) / sizeof(channellist[0])) | |
524 | |
525 channelidx = mute = cursor = 0; | |
526 | |
527 oldchan = channel = channellist[channelidx]; | |
528 | |
529 #ifndef USE_XVIMAGES | |
530 if (!Hermes_Init()) { | |
531 printf("Couldn't initialise Hermes!\n"); | |
532 exit(1); | |
533 } | |
534 | |
535 conv=Hermes_ConverterInstance(HERMES_CONVERT_NORMAL); | |
536 if (!conv) { | |
537 printf("Could not obtain converter instance from Hermes!\n"); | |
538 exit(1); | |
539 } | |
540 #endif | |
541 | |
542 X_Setup(width, height); | |
543 XSelectInput(disp, win, KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask); | |
544 | |
545 X_HideCursor(); | |
546 | |
547 /* Open Capture device */ | |
548 Open(); | |
549 | |
550 /* Open audio mixer */ | |
551 mixerdev = "/dev/mixer"; | |
552 if ((mfd = open(mixerdev, O_RDWR)) == -1) | |
553 fprintf(stderr, "Unable to open %s - %s\n", mixerdev, strerror(errno)); | |
554 | |
555 /* Talk to LIRC */ | |
556 if ((fd = lirc_init(basename(argv[0]), 1)) == -1) { | |
557 fprintf(stderr, "Unable to init lirc client library\n"); | |
558 uselirc = 0; | |
559 } else { | |
560 if (lirc_readconfig(NULL, &config, NULL) != 0) { | |
561 fprintf(stderr, "Unable to parse config file\n"); | |
562 uselirc = 0; | |
563 } else | |
564 uselirc = 1; | |
565 fds[0].fd = fd; | |
566 fds[0].events = POLLRDNORM; | |
567 } | |
568 | |
569 #ifndef USE_XVIMAGES | |
570 /* Conversion from and to formats */ | |
571 fmt_source.indexed=0; | |
572 fmt_source.bits=32; | |
573 fmt_source.r=0xff0000; | |
574 fmt_source.g=0x00ff00; | |
575 fmt_source.b=0x0000ff; | |
576 fmt_source.a=0; | |
577 | |
578 fmt_dest.indexed=0; | |
579 fmt_dest.bits=bits_per_pixel; | |
580 fmt_dest.r=vi->red_mask; | |
581 fmt_dest.g=vi->green_mask; | |
582 fmt_dest.b=vi->blue_mask; | |
583 fmt_dest.a=0; | |
584 #else | |
585 scratch_buf = malloc(width * height); | |
586 #endif | |
587 | |
588 frames = 0; | |
589 gettimeofday(&then, NULL); | |
590 gettimeofday(&lastmove, NULL); | |
591 | |
592 exitnow = 0; | |
593 | |
594 /* Capture loop */ | |
595 while (1) { | |
596 if (frames == 50) { | |
597 gettimeofday(&now, NULL); | |
598 timersub(&now, &then, &diff); | |
599 | |
600 rate = (float)frames / (float)(diff.tv_usec / 1000000.0 + diff.tv_sec); | |
601 | |
602 printf("%d frames in %.2f seconds, rate %.2f\n", frames, (float)(diff.tv_usec / 1000000.0) + diff.tv_sec, rate); | |
603 gettimeofday(&then, NULL); | |
604 frames = 0; | |
605 XResetScreenSaver(disp); | |
606 } | |
607 frames++; | |
608 | |
609 timersub(&now, &lastmove, &diff); | |
610 if (((float)diff.tv_usec / 1000000.0 + (float)diff.tv_sec) > 2.0) { | |
611 if (cursor) { | |
612 X_HideCursor(); | |
613 cursor = 0; | |
614 } | |
615 } else { | |
616 if (!cursor) { | |
617 X_ShowCursor(); | |
618 cursor = 1; | |
619 } | |
620 } | |
621 | |
622 if (XCheckMaskEvent(disp, PointerMotionMask, &e) && e.type == MotionNotify) { | |
623 gettimeofday(&lastmove, NULL); | |
624 } | |
625 | |
626 cmd = CMD_NONE; | |
627 | |
628 if (XCheckMaskEvent(disp, ButtonReleaseMask, &e)) { | |
629 printf("e.type = %d\n", e.type); | |
630 cmd = CMD_MUTE; | |
631 } | |
632 | |
633 if (XCheckMaskEvent(disp, KeyReleaseMask, &e) && e.type == KeyRelease) { | |
634 gettimeofday(&lastmove, NULL); | |
635 | |
636 XLookupString(&e.xkey, text, 255, &key, 0); | |
637 printf("Press - %c\n", text[0]); | |
638 | |
639 switch (text[0]) { | |
640 case 'q': | |
641 cmd = CMD_QUIT; | |
642 break; | |
643 | |
644 case '=': | |
645 case '+': | |
646 cmd = CMD_CHNUP; | |
647 break; | |
648 | |
649 case '-': | |
650 cmd = CMD_CHNDN; | |
651 break; | |
652 | |
653 case 'h': | |
654 cmd = CMD_CURSOR; | |
655 break; | |
656 | |
657 case 'm': | |
658 cmd = CMD_MUTE; | |
659 break; | |
660 | |
661 case 'r': | |
662 cmd = CMD_RELOAD; | |
663 break; | |
664 | |
665 case ',': | |
666 cmd = CMD_VOLDN; | |
667 break; | |
668 | |
669 case '.': | |
670 cmd = CMD_VOLUP; | |
671 break; | |
672 | |
673 case 'f': | |
674 cmd = CMD_FSTOGGLE; | |
675 break; | |
676 } | |
677 } | |
678 | |
679 /* Poll for IR events */ | |
680 if (uselirc) { | |
681 fds[0].revents = 0; | |
682 | |
683 while (1) { | |
684 if (poll(fds, 1, 0) == -1) { | |
685 fprintf(stderr, "Poll failed - %s\n", strerror(errno)); | |
686 exit(EXIT_FAILURE); | |
687 } | |
688 if ((fds[0].revents & POLLRDNORM) != 0) { | |
689 fprintf(stderr, "Processing IR..\n"); | |
690 | |
691 if (lirc_nextcode(&code) == 0) { | |
692 if(code == NULL) | |
693 continue; | |
694 | |
695 while ((ret = lirc_code2char(config, code, &c)) == 0 && | |
696 c != NULL) { | |
697 fprintf(stderr, "Got command \"%s\"\n", c); | |
698 | |
699 if (!strcmp(c, "Mute")) | |
700 cmd = CMD_MUTE; | |
701 else if (!strcmp(c, "CH_UP")) | |
702 cmd = CMD_CHNUP; | |
703 else if (!strcmp(c, "CH_DOWN")) | |
704 cmd = CMD_CHNDN; | |
705 else if (!strcmp(c, "Power")) | |
706 cmd = CMD_QUIT; | |
707 else if (!strcmp(c, "VOL_UP")) | |
708 cmd = CMD_VOLUP; | |
709 else if (!strcmp(c, "VOL_DOWN")) | |
710 cmd = CMD_VOLDN; | |
711 else if (!strcmp(c, "AV/TV")) | |
712 cmd = CMD_FSTOGGLE; | |
713 | |
714 free(code); | |
715 } | |
716 } | |
717 } else | |
718 break; | |
719 } | |
720 } | |
721 | |
722 switch (cmd) { | |
723 case CMD_QUIT: | |
724 exitnow = 1; | |
725 break; | |
726 | |
727 case CMD_CHNUP: | |
728 case CMD_CHNDN: | |
729 if (cmd == CMD_CHNUP) | |
730 channelidx++; | |
731 else | |
732 channelidx--; | |
733 | |
734 if (channelidx < 0) | |
735 channelidx = NUMCHANS - 1; | |
736 if (channelidx > NUMCHANS - 1) | |
737 channelidx = 0; | |
738 | |
739 channel = channellist[channelidx]; | |
740 | |
741 printf("Channel - %d\n", channel); | |
742 if (oldchan != channel) { | |
743 oldchan = channel; | |
744 | |
745 if ( ioctl( tuner_fd, TVTUNER_SETCHNL, &channel ) < 0 ) { | |
746 DO_IOCTL_SERR( "TVTUNER_SETCHNL", channel ); | |
747 exit(1); | |
748 } | |
749 } | |
750 break; | |
751 | |
752 case CMD_MUTE: | |
753 if (mute) | |
754 mute = 0; | |
755 else | |
756 mute = 1; | |
757 | |
758 printf("Mute - %d\n", mute); | |
759 if (ioctl(tuner_fd, BT848_SAUDIO, &mute) < 0) { | |
760 DO_IOCTL_SERR("BT848_SAUDIO", mute); | |
761 exit(1); | |
762 } | |
763 break; | |
764 | |
765 case CMD_CURSOR: | |
766 if (cursor) { | |
767 printf("Cursor hidden\n"); | |
768 X_HideCursor(); | |
769 cursor = 0; | |
770 } else { | |
771 printf("Cursor revealed\n"); | |
772 X_ShowCursor(); | |
773 cursor = 1; | |
774 } | |
775 break; | |
776 | |
777 case CMD_RELOAD: | |
778 printf("Reloading\n"); | |
779 Close(); | |
780 Open(); | |
781 break; | |
782 | |
783 case CMD_VOLUP: | |
784 case CMD_VOLDN: | |
785 if (ioctl(mfd, MIXER_READ(SOUND_MIXER_VOLUME), &curvol) == -1) { | |
786 fprintf(stderr, "Unable to read current volume - %s\n", strerror(errno)); | |
787 break; | |
788 } | |
789 | |
790 curvol = curvol & 0x7f; | |
791 | |
792 if (cmd == CMD_VOLUP) | |
793 curvol += 5; | |
794 else | |
795 curvol -= 5; | |
796 | |
797 if (curvol < 0) | |
798 curvol = 0; | |
799 if (curvol > 100) | |
800 curvol = 100; | |
801 | |
802 printf("Setting volume to %d\n", curvol); | |
803 curvol |= curvol << 8; | |
804 | |
805 if (ioctl(mfd, MIXER_WRITE(SOUND_MIXER_VOLUME), &curvol) == -1) { | |
806 fprintf(stderr, "Unable to write volume - %s\n", strerror(errno)); | |
807 break; | |
808 } | |
809 break; | |
810 | |
811 case CMD_FSTOGGLE: | |
812 vo_x11_ewmh_fullscreen(_NET_WM_STATE_TOGGLE); | |
813 break; | |
814 | |
815 } | |
816 | |
817 Capture(); | |
818 | |
819 #ifdef USE_XVIMAGES | |
820 /* bktr's YUV_12 is planar W*H bytes Y, W/2*H/2 bytes U, */ | |
821 /* W/2*H/2 bytes V. Xv's YV12 is the same with U and V */ | |
822 /* planes reversed. */ | |
823 { | |
824 int y_off, u_off, v_off; | |
825 y_off = 0; | |
826 u_off = width * height; | |
827 v_off = u_off + width*height/4; | |
828 | |
829 assert(yuv_image->data_size == width * height * 3 / 2 ); | |
830 memcpy(yuv_image->data , bktr_buffer , u_off-y_off ); | |
831 memcpy(yuv_image->data + u_off, bktr_buffer + v_off, v_off-u_off ); | |
832 memcpy(yuv_image->data + v_off, bktr_buffer + u_off, v_off-u_off ); | |
833 } | |
834 #else | |
835 /*SaveImage();*/ | |
836 | |
837 Hermes_ConverterRequest(conv,&fmt_source,&fmt_dest); | |
838 Hermes_ConverterCopy(conv,bktr_buffer,0,0,width,height,width*4, | |
839 rgb_image->data,0,0,width,height, | |
840 rgb_image->bytes_per_line); | |
841 #endif | |
842 | |
843 X_Display(); | |
844 if (exitnow) { | |
845 printf("quitting\n"); | |
846 break; | |
847 } | |
848 } | |
849 | |
850 #ifndef USE_XVIMAGES | |
851 Hermes_ConverterReturn(conv); | |
852 Hermes_Done(); | |
853 #endif | |
854 | |
855 X_Shutdown(); | |
856 lirc_freeconfig(config); | |
857 lirc_deinit(); | |
858 | |
859 return 0; | |
860 } |