Mercurial > ~darius > hgwebdir.cgi > simpletv
diff simpletv.c @ 1:769b155a34f9 SIMPETV_0_1
Initial revision
author | darius |
---|---|
date | Tue, 04 Jan 2005 05:17:43 +0000 |
parents | |
children | 8d7d1680db7d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simpletv.c Tue Jan 04 05:17:43 2005 +0000 @@ -0,0 +1,860 @@ +/* Capture and store a 24 bit RGB image as a PPM file */ +/* Copyright (c) 2000 Randall Hopper + * + * Based on a grab.c from Roger Hardiman, which was + * based on an original program by Mark Tinguely and Jim Lowe . + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Mark Tinguely and Jim Lowe + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <poll.h> +#include <sys/mman.h> +#include <sys/fcntl.h> +#include <sys/time.h> +#include <libgen.h> +#include <sys/soundcard.h> +#include <machine/ioctl_bt848.h> +#include <machine/ioctl_meteor.h> +#include <machine/param.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/XShm.h> +#include <X11/extensions/Xvlib.h> +#ifndef USE_XVIMAGES +#include "Hermes/Hermes.h" +#endif + +#include <lirc/lirc_client.h> + +/* Some BT848/BT878 cards have gain control hardware which takes + 1 or 2 seconds to settle. If you get a washed out / too bright + image, change the GRABBER_SETTLE_TIME from 0 to either 1 or 2 +*/ +/*#define GRABBER_SETTLE_TIME 10*/ +#define GRABBER_SETTLE_TIME 0 + + +#define PAL 1 +#define NTSC 2 + +/* PAL is 768 x 576. NTSC is 640 x 480 */ +#define PAL_HEIGHT 576 +#define NTSC_HEIGHT 480 + +int bktr_fd; +int tuner_fd; +unsigned char *bktr_buffer; +#if 1 +int width = 320; +int height = 240; +#else +int width = 640; +int height = 480; +#endif + +#ifndef USE_XVIMAGES +static XImage *rgb_image; +static Pixmap pmap; +#endif +static XShmSegmentInfo shminfo; +static Display *disp; +static Window win; +static Visual *vis; +static int scr; +static GC gc; +static int depth; +static Colormap cmap; +static XVisualInfo *vi; +static int bits_per_pixel; +static XvImage *yuv_image; +static XvAdaptorInfo *xv_adaptors; +static int xv_num_adaptors; +static int xv_adaptor; +static int xv_format_id; + +static int channel; + +#define DO_IOCTL_GERR(str) fprintf(stderr, "ioctl(%s) failed: %s\n", \ + str, strerror(errno) ) +#define DO_IOCTL_SERR(str,arg) fprintf(stderr, "ioctl(%s, %ld) failed: %s\n",\ + str, (long)arg, strerror(errno) ) +/*--------------------------------------------------------------------------*/ + +void Close() +{ + close(tuner_fd); + close(bktr_fd); +} + +void Open() +{ + struct meteor_geomet geo; + int buffer_size, format, source, c; + char *device_name; + + format = PAL; /* default value */ + source = 1; /* default value */ + device_name = "/dev/bktr0"; /* default value */ + + /* Open the Meteor or Bt848/Bt878 grabber */ + if ((bktr_fd = open(device_name, O_RDONLY)) < 0) { + printf("open failed: %d\n", errno); + exit(1); + } + + if ((tuner_fd = open("/dev/tuner0", O_RDONLY)) < 0) { + printf("open failed: %d\n", errno); + exit(1); + } + + /* set up the capture type and size */ + geo.rows = height; + geo.columns = width; + geo.frames = 1; +#ifdef USE_XVIMAGES + /* Should be YUV_12, but 422 actually gives a synced picture. Though */ + + /*geo.oformat = METEOR_GEO_YUV_12;*/ + geo.oformat = METEOR_GEO_YUV_422 | METEOR_GEO_YUV_12; + /* geo.oformat = METEOR_GEO_YUV_12; */ +#else + geo.oformat = METEOR_GEO_RGB24; +#endif + + /* switch from interlaced capture to single field capture if */ + /* the grab height is less than half the normal TV height */ + /* this gives better quality captures when the object in the TV */ + /* picture is moving */ + if ((format == PAL) && (height <= (PAL_HEIGHT/2))) + geo.oformat |= METEOR_GEO_ODD_ONLY; + if ((format == NTSC) && (height <= (NTSC_HEIGHT/2))) + geo.oformat |= METEOR_GEO_ODD_ONLY; + + if (ioctl(bktr_fd, METEORSETGEO, &geo) < 0) { + printf("METEORSETGEO ioctl failed: %d\n", errno); + exit(1); + } + + /* Select PAL or NTSC */ + switch (format) { + case PAL: c = METEOR_FMT_PAL; break; + case NTSC: c = METEOR_FMT_NTSC; break; + default: c = METEOR_FMT_NTSC; break; + } + + c = BT848_IFORM_F_PALBDGHI; + if ( ioctl( bktr_fd, BT848SFMT, &c ) < 0 ) { + DO_IOCTL_SERR( "BT848SFMT", c ); + return; + } + c = AUDIO_TUNER; + if ( ioctl( tuner_fd, BT848_SAUDIO, &c ) < 0 ) { + DO_IOCTL_SERR( "BT848SFMT", c ); + return; + } + c = CHNLSET_AUSTRALIA; + if ( ioctl( tuner_fd, TVTUNER_SETTYPE, &c ) < 0 ) { + DO_IOCTL_SERR( "TVTUNER_SETTYPE", c ); + return; + } + + if ( ioctl( tuner_fd, TVTUNER_SETCHNL, &channel ) < 0 ) { + DO_IOCTL_SERR( "TVTUNER_SETCHNL", channel ); + return; + } + + + /* Select the Video Source */ + /* Video In, Tuner, S-Video */ + switch (source) { + case 0: c = METEOR_INPUT_DEV0; break; + case 1: c = METEOR_INPUT_DEV1; break; + case 2: c = METEOR_INPUT_DEV2; break; + case 3: c = METEOR_INPUT_DEV3; break; + default: c = METEOR_INPUT_DEV0; break; + } + + printf("Input - %x\n", c); + if (ioctl(bktr_fd, METEORSINPUT, &c) < 0) { + printf("ioctl failed: %d\n", errno); + exit(1); + } + + /* Use mmap to Map the drivers grab buffer */ + buffer_size = width*height*4; /* R,G,B,spare */ + bktr_buffer = (unsigned char *)mmap((caddr_t)0,buffer_size,PROT_READ, + MAP_SHARED, bktr_fd, (off_t)0); + + if (bktr_buffer == (unsigned char *) MAP_FAILED) + exit(1); + + + /* We may need to wait for a short time to allow the grabber */ + /* brightness to settle down */ + sleep(GRABBER_SETTLE_TIME); +} + +/*--------------------------------------------------------------------------*/ + +void Capture() +{ + int c; + + /* Perform a single frame capture */ + c = METEOR_CAP_SINGLE ; + ioctl(bktr_fd, METEORCAPTUR, &c); +} + +/*--------------------------------------------------------------------------*/ + +void SaveImage() +{ + unsigned char *line_buffer; + int o,w,h; + unsigned char *p; + unsigned char header[30]; + char *filename = "t.ppm" /* argv[3] */; + + /* Create the output file */ + if ((o = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) { + printf("ppm open failed: %d\n", errno); + exit(1); + } + + /* make PPM header and save to file */ + sprintf(header, "P6\n%d\n%d\n255\n",width,height); + write (o, header, strlen(header)); + + /* save the RGB data to PPM file */ + /* save this one line at a time */ + line_buffer =(unsigned char *)malloc( width *3 * sizeof(unsigned char)); + p = bktr_buffer; + for (h = 0; h < height; h++) { + for (w = 0; w < width; w++) { + line_buffer[(w*3) + 2] = *p++; /* blue */ + line_buffer[(w*3) + 1] = *p++; /* green */ + line_buffer[(w*3) + 0] = *p++; /* red */ + p++; /* NULL byte */ + } + write(o,line_buffer, width*3); + } + close(o); + free(line_buffer); +} + +void X_ShowCursor(void) { + XDefineCursor(disp, win, 0); +} + +void X_HideCursor(void) { + Cursor no_ptr; + Pixmap bm_no; + XColor black,dummy; + Colormap colormap; + static unsigned char bm_no_data[] = {0,0,0,0, 0,0,0,0}; + + colormap = DefaultColormap(disp, DefaultScreen(disp)); + XAllocNamedColor(disp, colormap, "black", &black, &dummy); + bm_no = XCreateBitmapFromData(disp, win, bm_no_data, 8, 8); + no_ptr = XCreatePixmapCursor(disp, bm_no, bm_no, &black, &black, 0, 0); + + XDefineCursor(disp, win, no_ptr); + XFreeCursor(disp, no_ptr); + if (bm_no != None) + XFreePixmap(disp, bm_no); + +} + +/* + * Sends the EWMH fullscreen state event. + * + * action: could be on of _NET_WM_STATE_REMOVE -- remove state + * _NET_WM_STATE_ADD -- add state + * _NET_WM_STATE_TOGGLE -- toggle + */ +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ +void +vo_x11_ewmh_fullscreen(int action) { + XEvent xev; + + assert(action == _NET_WM_STATE_REMOVE || + action == _NET_WM_STATE_ADD || action == _NET_WM_STATE_TOGGLE); + + + /* init X event structure for _NET_WM_FULLSCREEN client msg */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.message_type = XInternAtom(disp, "_NET_WM_STATE", False); + xev.xclient.window = win; + xev.xclient.format = 32; + xev.xclient.data.l[0] = action; + xev.xclient.data.l[1] = XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False); + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + /* finally send that damn thing */ + if (!XSendEvent(disp, DefaultRootWindow(disp), False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev)) { + fprintf(stderr, "Failed to send fullscreen command\n"); + } +} + + +void X_Setup(int w,int h) +{ + XGCValues gcvals; + Window root; + XVisualInfo vinfo_pref; + int num_vis; + XPixmapFormatValues *pf; + int num_pf, pfi, i,j; + + disp = XOpenDisplay(NULL); + if (!disp) { + fprintf(stderr,"X-Error: unable to connect to display\n"); + exit(1); + } + + XSynchronize( disp, True ); + + scr=DefaultScreen(disp); + vis=DefaultVisual(disp,scr); + root=DefaultRootWindow(disp); + depth=DefaultDepth(disp,scr); + + vinfo_pref.screen = scr; + vinfo_pref.visualid = XVisualIDFromVisual( vis ); + vi = XGetVisualInfo( disp, VisualScreenMask | VisualIDMask, + &vinfo_pref, &num_vis ); + assert ( num_vis == 1 ); + + win = XCreateSimpleWindow(disp, root, 0, 0, w, h, 0, 0, 0); + gc = XCreateGC(disp, win, (unsigned long)0, &gcvals); + XSetForeground(disp, gc, 0); + XSetBackground(disp, gc, 1); + + XMapWindow(disp,win); + cmap = DefaultColormap(disp, scr); + XSync(disp,False); + + /* Setup with Xv extension */ + xv_adaptor = -1; + xv_format_id = -1; + XvQueryAdaptors( disp, root, &xv_num_adaptors, &xv_adaptors ); + for ( i = 0; i < xv_num_adaptors; i++ ) { + XvAdaptorInfo *adaptor = &xv_adaptors[i]; + int takes_images; + + takes_images = adaptor->type & ( XvInputMask | XvImageMask ); + if ( takes_images ) { + XvImageFormatValues *formats; + int num_formats; + + formats = XvListImageFormats( disp, adaptor->base_id, &num_formats ); + for ( j = 0; j < num_formats; j++ ) + if ( formats[j].type == XvYUV && formats[j].format == XvPlanar && + strcmp( formats[j].guid, "YV12" ) == 0 ) + break; + if ( j < num_formats ) { + xv_adaptor = i; + xv_format_id = formats[j].id; + break; + } + } + } + assert( xv_adaptor >= 0 ); + + /* Create an image to captured frames */ +#ifdef USE_XVIMAGES + yuv_im age = XvShmCreateImage( disp, xv_adaptors[xv_adaptor].base_id, + xv_format_id, 0, w, h, &shminfo ); + if (!yuv_image) + fprintf(stderr,"X-Error: unable to create XvShm XImage\n"); + + shminfo.shmid = shmget( IPC_PRIVATE, yuv_image->data_size, IPC_CREAT|0777); + if (shminfo.shmid == -1) + fprintf(stderr,"SharedMemory Error: unable to get identifier\n"); + + shminfo.shmaddr = yuv_image->data = shmat(shminfo.shmid, 0, 0); +#else + rgb_image = XShmCreateImage( disp, vis, depth, ZPixmap, NULL, &shminfo, w, h); + if (!rgb_image) + fprintf(stderr,"X-Error: unable to create XShm XImage\n"); + + shminfo.shmid = shmget( IPC_PRIVATE, + rgb_image->bytes_per_line * rgb_image->height, + IPC_CREAT|0777); + if (shminfo.shmid == -1) + fprintf(stderr, "SharedMemory Error: unable to get identifier\n"); + + shminfo.shmaddr = rgb_image->data = shmat(shminfo.shmid, 0, 0); +#endif + + if(!XShmAttach( disp,&shminfo )) + fprintf(stderr,"X-Error: unable to attach XShm Shared Memory Segment\n"); + + /* Create a pixmap for the window background */ +#ifdef OLD + pmap = XShmCreatePixmap(disp, win, shminfo.shmaddr, &shminfo, w, h, depth); + if (!pmap) + fprintf(stderr,"Unable to create pixmap\n"); +#endif + + /* Determine bits-per-pixel for pixmaps */ + pf = XListPixmapFormats( disp, &num_pf); + assert(pf); + for (pfi = 0; pfi < num_pf; pfi++) + if (pf[pfi].depth == vi->depth) + break; + assert(pfi < num_pf); + bits_per_pixel = pf[pfi].bits_per_pixel; + XFree (pf); + +#ifdef OLD + XSetWindowBackgroundPixmap(disp,win,pmap); +#endif +} + +/*--------------------------------------------------------------------------*/ + +void X_Shutdown() +{ + XShmDetach( disp, &shminfo ); +#ifdef USE_XVIMAGES +# warning How do we destroy an XvImage? +#else + XDestroyImage( rgb_image ); +#endif + shmdt( shminfo.shmaddr ); + shmctl( shminfo.shmid, IPC_RMID, 0 ); +} + + +/*--------------------------------------------------------------------------*/ + +void X_Display(void) +{ + int _w,_h,_d; + Window _dw; + +#ifdef USE_XVIMAGES + XGetGeometry( disp, win, &_dw, &_d, &_d, &_w, &_h, &_d, &_d); + XvShmPutImage( disp, xv_adaptors[ xv_adaptor ].base_id, win, + gc, yuv_image, 0, 0, yuv_image->width, yuv_image->height, + 0, 0, _w, _h, True ); +#else + XShmPutImage( disp, win, gc, rgb_image, 0,0,0,0, + rgb_image->width, rgb_image->height, False ); +#endif + XSync(disp, False); +} + +#define CMD_NONE 0 +#define CMD_CHNUP 1 +#define CMD_CHNDN 2 +#define CMD_MUTE 3 +#define CMD_QUIT 4 +#define CMD_RELOAD 5 +#define CMD_CURSOR 6 +#define CMD_VOLDN 7 +#define CMD_VOLUP 8 +#define CMD_FSTOGGLE 9 + +/*--------------------------------------------------------------------------*/ + +int main(int argc, char *argv[]) { +#ifndef USE_XVIMAGES + HermesHandle conv; + HermesFormat fmt_source,fmt_dest; +#endif + XEvent e; + char *scratch_buf, *code, *c, *mixerdev; + int frames, channelidx, oldchan, mute, cursor, fd, ret, cmd; + int exitnow, mfd, uselirc, curvol; + struct timeval then, now, diff, lastmove; + float rate; + KeySym key; + char text[255]; + int channellist[] = { 2, 7, 9, 10, 28 }; + struct lirc_config *config; + struct pollfd fds[1]; + +#define NUMCHANS (sizeof(channellist) / sizeof(channellist[0])) + + channelidx = mute = cursor = 0; + + oldchan = channel = channellist[channelidx]; + +#ifndef USE_XVIMAGES + if (!Hermes_Init()) { + printf("Couldn't initialise Hermes!\n"); + exit(1); + } + + conv=Hermes_ConverterInstance(HERMES_CONVERT_NORMAL); + if (!conv) { + printf("Could not obtain converter instance from Hermes!\n"); + exit(1); + } +#endif + + X_Setup(width, height); + XSelectInput(disp, win, KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask); + + X_HideCursor(); + + /* Open Capture device */ + Open(); + + /* Open audio mixer */ + mixerdev = "/dev/mixer"; + if ((mfd = open(mixerdev, O_RDWR)) == -1) + fprintf(stderr, "Unable to open %s - %s\n", mixerdev, strerror(errno)); + + /* Talk to LIRC */ + if ((fd = lirc_init(basename(argv[0]), 1)) == -1) { + fprintf(stderr, "Unable to init lirc client library\n"); + uselirc = 0; + } else { + if (lirc_readconfig(NULL, &config, NULL) != 0) { + fprintf(stderr, "Unable to parse config file\n"); + uselirc = 0; + } else + uselirc = 1; + fds[0].fd = fd; + fds[0].events = POLLRDNORM; + } + +#ifndef USE_XVIMAGES + /* Conversion from and to formats */ + fmt_source.indexed=0; + fmt_source.bits=32; + fmt_source.r=0xff0000; + fmt_source.g=0x00ff00; + fmt_source.b=0x0000ff; + fmt_source.a=0; + + fmt_dest.indexed=0; + fmt_dest.bits=bits_per_pixel; + fmt_dest.r=vi->red_mask; + fmt_dest.g=vi->green_mask; + fmt_dest.b=vi->blue_mask; + fmt_dest.a=0; +#else + scratch_buf = malloc(width * height); +#endif + + frames = 0; + gettimeofday(&then, NULL); + gettimeofday(&lastmove, NULL); + + exitnow = 0; + + /* Capture loop */ + while (1) { + if (frames == 50) { + gettimeofday(&now, NULL); + timersub(&now, &then, &diff); + + rate = (float)frames / (float)(diff.tv_usec / 1000000.0 + diff.tv_sec); + + printf("%d frames in %.2f seconds, rate %.2f\n", frames, (float)(diff.tv_usec / 1000000.0) + diff.tv_sec, rate); + gettimeofday(&then, NULL); + frames = 0; + XResetScreenSaver(disp); + } + frames++; + + timersub(&now, &lastmove, &diff); + if (((float)diff.tv_usec / 1000000.0 + (float)diff.tv_sec) > 2.0) { + if (cursor) { + X_HideCursor(); + cursor = 0; + } + } else { + if (!cursor) { + X_ShowCursor(); + cursor = 1; + } + } + + if (XCheckMaskEvent(disp, PointerMotionMask, &e) && e.type == MotionNotify) { + gettimeofday(&lastmove, NULL); + } + + cmd = CMD_NONE; + + if (XCheckMaskEvent(disp, ButtonReleaseMask, &e)) { + printf("e.type = %d\n", e.type); + cmd = CMD_MUTE; + } + + if (XCheckMaskEvent(disp, KeyReleaseMask, &e) && e.type == KeyRelease) { + gettimeofday(&lastmove, NULL); + + XLookupString(&e.xkey, text, 255, &key, 0); + printf("Press - %c\n", text[0]); + + switch (text[0]) { + case 'q': + cmd = CMD_QUIT; + break; + + case '=': + case '+': + cmd = CMD_CHNUP; + break; + + case '-': + cmd = CMD_CHNDN; + break; + + case 'h': + cmd = CMD_CURSOR; + break; + + case 'm': + cmd = CMD_MUTE; + break; + + case 'r': + cmd = CMD_RELOAD; + break; + + case ',': + cmd = CMD_VOLDN; + break; + + case '.': + cmd = CMD_VOLUP; + break; + + case 'f': + cmd = CMD_FSTOGGLE; + break; + } + } + + /* Poll for IR events */ + if (uselirc) { + fds[0].revents = 0; + + while (1) { + if (poll(fds, 1, 0) == -1) { + fprintf(stderr, "Poll failed - %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + if ((fds[0].revents & POLLRDNORM) != 0) { + fprintf(stderr, "Processing IR..\n"); + + if (lirc_nextcode(&code) == 0) { + if(code == NULL) + continue; + + while ((ret = lirc_code2char(config, code, &c)) == 0 && + c != NULL) { + fprintf(stderr, "Got command \"%s\"\n", c); + + if (!strcmp(c, "Mute")) + cmd = CMD_MUTE; + else if (!strcmp(c, "CH_UP")) + cmd = CMD_CHNUP; + else if (!strcmp(c, "CH_DOWN")) + cmd = CMD_CHNDN; + else if (!strcmp(c, "Power")) + cmd = CMD_QUIT; + else if (!strcmp(c, "VOL_UP")) + cmd = CMD_VOLUP; + else if (!strcmp(c, "VOL_DOWN")) + cmd = CMD_VOLDN; + else if (!strcmp(c, "AV/TV")) + cmd = CMD_FSTOGGLE; + + free(code); + } + } + } else + break; + } + } + + switch (cmd) { + case CMD_QUIT: + exitnow = 1; + break; + + case CMD_CHNUP: + case CMD_CHNDN: + if (cmd == CMD_CHNUP) + channelidx++; + else + channelidx--; + + if (channelidx < 0) + channelidx = NUMCHANS - 1; + if (channelidx > NUMCHANS - 1) + channelidx = 0; + + channel = channellist[channelidx]; + + printf("Channel - %d\n", channel); + if (oldchan != channel) { + oldchan = channel; + + if ( ioctl( tuner_fd, TVTUNER_SETCHNL, &channel ) < 0 ) { + DO_IOCTL_SERR( "TVTUNER_SETCHNL", channel ); + exit(1); + } + } + break; + + case CMD_MUTE: + if (mute) + mute = 0; + else + mute = 1; + + printf("Mute - %d\n", mute); + if (ioctl(tuner_fd, BT848_SAUDIO, &mute) < 0) { + DO_IOCTL_SERR("BT848_SAUDIO", mute); + exit(1); + } + break; + + case CMD_CURSOR: + if (cursor) { + printf("Cursor hidden\n"); + X_HideCursor(); + cursor = 0; + } else { + printf("Cursor revealed\n"); + X_ShowCursor(); + cursor = 1; + } + break; + + case CMD_RELOAD: + printf("Reloading\n"); + Close(); + Open(); + break; + + case CMD_VOLUP: + case CMD_VOLDN: + if (ioctl(mfd, MIXER_READ(SOUND_MIXER_VOLUME), &curvol) == -1) { + fprintf(stderr, "Unable to read current volume - %s\n", strerror(errno)); + break; + } + + curvol = curvol & 0x7f; + + if (cmd == CMD_VOLUP) + curvol += 5; + else + curvol -= 5; + + if (curvol < 0) + curvol = 0; + if (curvol > 100) + curvol = 100; + + printf("Setting volume to %d\n", curvol); + curvol |= curvol << 8; + + if (ioctl(mfd, MIXER_WRITE(SOUND_MIXER_VOLUME), &curvol) == -1) { + fprintf(stderr, "Unable to write volume - %s\n", strerror(errno)); + break; + } + break; + + case CMD_FSTOGGLE: + vo_x11_ewmh_fullscreen(_NET_WM_STATE_TOGGLE); + break; + + } + + Capture(); + +#ifdef USE_XVIMAGES + /* bktr's YUV_12 is planar W*H bytes Y, W/2*H/2 bytes U, */ + /* W/2*H/2 bytes V. Xv's YV12 is the same with U and V */ + /* planes reversed. */ + { + int y_off, u_off, v_off; + y_off = 0; + u_off = width * height; + v_off = u_off + width*height/4; + + assert(yuv_image->data_size == width * height * 3 / 2 ); + memcpy(yuv_image->data , bktr_buffer , u_off-y_off ); + memcpy(yuv_image->data + u_off, bktr_buffer + v_off, v_off-u_off ); + memcpy(yuv_image->data + v_off, bktr_buffer + u_off, v_off-u_off ); + } +#else + /*SaveImage();*/ + + Hermes_ConverterRequest(conv,&fmt_source,&fmt_dest); + Hermes_ConverterCopy(conv,bktr_buffer,0,0,width,height,width*4, + rgb_image->data,0,0,width,height, + rgb_image->bytes_per_line); +#endif + + X_Display(); + if (exitnow) { + printf("quitting\n"); + break; + } + } + +#ifndef USE_XVIMAGES + Hermes_ConverterReturn(conv); + Hermes_Done(); +#endif + + X_Shutdown(); + lirc_freeconfig(config); + lirc_deinit(); + + return 0; +}