/*
 video4linux interface wrapper for kwintv

 Copyright (C) 1998,1999 Moritz Wenk (wenk@mathematik.uni-kl.de)

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <endian.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <asm/types.h>

#ifndef BTTV_VERSION
#define BTTV_VERSION _IOR('v', BASE_VIDIOCPRIVATE+6, int)
#endif

#include "v4lxif.h"

#include <except.h>

#ifndef GUILESS
#include <qapp.h>
#include <X11/Xlib.h>
#include <X11/Xmd.h>
#include "qt_visual.h"
#endif

#include <stdarg.h>

#ifndef GUILESS
#ifdef HAVE_LIBXXF86DGA
#include <X11/Xproto.h>
#include <X11/extensions/xf86dga.h>
#include <X11/extensions/xf86dgastr.h>

// Support deprecated XFree86 symbol names
#ifndef XDGA_MINOR_VERSION
#define XDGA_MINOR_VERSION XF86DGA_MINOR_VERSION
#endif
#ifndef XDGA_MAJOR_VERSION
#define XDGA_MAJOR_VERSION XF86DGA_MAJOR_VERSION
#endif

#endif /* HAVE_LIBXXF86DGA */

#endif /* GUILESS */

/*                            PAL  NTSC SECAM */
static int    maxwidth[]  = { 768, 640, 768 };
static int    maxheight[] = { 576, 480, 576 };

extern int errno;

const int one  = 1;
const int zero = 0;

//#define VIF_FATAL(a) {warning(a);_state=0;}
#define VIF_FATAL(a) (warning(a))
#define VIF_WARN(a) (warning(a))

#define VIF_FATAL_STR(a,b) (warning(a,(b)))
//#define VIF_FATAL(a) (fatal(a,(b)))
#define VIF_WARN_STR(a,b) (warning(a,(b)))

static unsigned short format2palette[] = {
    0,				/* unused */
    VIDEO_PALETTE_HI240,	/* RGB8   */
    VIDEO_PALETTE_GREY,		/* GRAY8  */
#if __BYTE_ORDER == __BIG_ENDIAN
    0,
    0,
    VIDEO_PALETTE_RGB555,	/* RGB15_BE  */
    VIDEO_PALETTE_RGB565,	/* RGB16_BE  */
    0,
    0,
    VIDEO_PALETTE_RGB24,	/* RGB24     */
    VIDEO_PALETTE_RGB32,	/* RGB32     */
#else // little endian (intel)
    VIDEO_PALETTE_RGB555,	/* RGB15_LE  */
    VIDEO_PALETTE_RGB565,	/* RGB16_LE  */
    0,
    0,
    VIDEO_PALETTE_RGB24,	/* BGR24     */
    VIDEO_PALETTE_RGB32,	/* BGR32     */
    0,
    0,
#endif
};

#define VIDEO_RGB08          1  /* bt848 dithered */
#define VIDEO_GRAY           2
#define VIDEO_RGB15_LE       3  /* 15 bpp little endian */
#define VIDEO_RGB16_LE       4  /* 16 bpp little endian */
#define VIDEO_RGB15_BE       5  /* 15 bpp big endian */
#define VIDEO_RGB16_BE       6  /* 16 bpp big endian */
#define VIDEO_BGR24          7  /* bgrbgrbgrbgr (LE) */
#define VIDEO_BGR32          8  /* bgr-bgr-bgr- (LE) */
#define VIDEO_RGB24          9  /* rgbrgbrgbrgb (BE)*/
#define VIDEO_RGB32         10  /* -rgb-rgb-rgb (BE)*/
#define VIDEO_LUT2          11  /* lookup-table 2 byte depth */
#define VIDEO_LUT4          12  /* lookup-table 4 byte depth */
#define VIDEO_YUYV	    13  /* YUV 4:2:2 */
#define VIDEO_YUV420	    14  /* YUV 4:2:0 */

unsigned int format2depth[] = {
    0,               /* unused   */
    8,               /* RGB8     */
    8,               /* GRAY8    */
    16,              /* RGB15 LE */
    16,              /* RGB16 LE */
    16,              /* RGB15 BE */
    16,              /* RGB16 BE */
    24,              /* BGR24    */
    32,              /* BGR32    */
    24,              /* RGB24    */
    32,              /* RGB32    */
    16,              /* LUT2     */
    32,              /* LUT4     */
    16,		     /* YUV422   */
    12,		     /* YUV420   */
};

char* format_desc[] = {
    "",
    "8 bit PseudoColor (dithering)",
    "8 bit StaticGray",
    "15 bit TrueColor (LE)",
    "16 bit TrueColor (LE)",
    "15 bit TrueColor (BE)",
    "16 bit TrueColor (BE)",
    "24 bit TrueColor (LE: bgr)",
    "32 bit TrueColor (LE: bgr-)",
    "24 bit TrueColor (BE: rgb)",
    "32 bit TrueColor (BE: -rgb)",
    "16 bit TrueColor (lut)",
    "32 bit TrueColor (lut)",
    "16 bit YUV 4:2:2",
    "12 bit YUV 4:2:0",
    "12 bit YUV 4:1:1"
};
//#define v4lDEBUG

int con_printf(const char* fmt, ...)
{
    va_list va;
    va_start(va, fmt);
    vprintf(fmt, va);
    printf("\n");
    return 0;
}

//==============================================================================
#define __MODULE__ "v1lx"
v4lxif::v4lxif(const char * _device, const char* _vbidev, v4lxif_version _ifv )
: ifv(_ifv), device(_device)
{
// validate the name of device
    const char * p=_device;
    while(*p)
    {
	if(
	    ((*p>0) && (*p<32))
	    || (*p<0))
	{
	    fprintf(stderr, "Invalid video device filename %s suspected\n", _device);
	    fprintf(stderr, "Setting to default value ( /dev/video )\n");
	    device=_device="/dev/video";
	    break;
	}
	p++;
    }
#ifdef v4lDEBUG
    debug("v4l1: Using interface %s in ::v4lxif",ifname[ifv].str);
#endif
    // open v4l device
    if ( -1 == (devv4l=::open(device,O_RDWR)) ) 
	throw FATAL("Error opening v4lx device %s: %s in ::v4lxif",device,strerror(errno));
    int driver=-1;
    int tmp;
    driver=ioctl(devv4l, BTTV_VERSION, &tmp);
    if(driver>=0)
    {
	fprintf(stderr, "BTTV driver version %d.%d.%d detected\n",
	    driver>>16, (driver & 0xff00) >>8, driver & 0xff);
    }
    else
	fprintf(stderr, "Error querying BTTV driver version: %d ( %s )\n",
	    driver, strerror(errno));
    if((driver<0) || ((driver>>16)>0) || (((driver&0xff00)>>8)>=8))
    /**
	Versions of bttv before 0.8.x behave strangely when program
	opens /dev/video and /dev/vbi simultaneously.
	We assume that non-bttv video4windows devices do it correctly
	( does it work at all with non-bttv hardware? )
    **/
    {	
        devvbi=::open(_vbidev, O_RDONLY);
	if(devvbi<0)
	    fprintf(stderr, "Warning: failed to open /dev/vbi device ( %s ); closed captioning won't be available\n",
		strerror(errno));
    }	
    else
    {
	devvbi=-1;
	fprintf(stderr, "Broken VBI implementation; disabling closed captioning\n");
    }
    pthread_mutex_init(&mutex, 0);
}

int v4lxif::readvbi(void* buf, size_t cnt)
{
    if(devvbi<0)
	return -1;
    pthread_mutex_lock(&mutex);
    int result=::read(devvbi, buf, cnt);
    pthread_mutex_unlock(&mutex);
    return result;
}
#undef __MODULE__
#define __MODULE__ "v4l1"
v4lxif::~v4lxif()
{
#ifdef v4lDEBUG
    debug("v4lx: Close device in ::~v4lxif");
#endif
    if ( -1 == ::close(devv4l) )
	fprintf(stderr, "v4l1: Error closing the v4lx device %s: %s in ::v4lxif",device,strerror(errno));
    pthread_mutex_destroy(&mutex);	
}

//==============================================================================
v4l1if::v4l1if( int fd )
{
    devv4l=fd;
}

v4l1if::v4l1if( const char* mem, const char * _device, int _bpp, int _palette )
: v4lxif( _device, "/dev/vbi", v4lxif::v4l1 ), vchan(0), vaudio(0), grabbermem(0), _state(1)
{
#ifndef GUILESS
    int visual_class;
    Visual *myvisual;

    Display *disp= qt_xdisplay();
#endif
    ushort i;

    setupok=true;
    //    // capture off
    if ( -1 == ioctl(devv4l, VIDIOCCAPTURE, &zero) )
	throw FATAL("VIDIOCCAPTURE in ::v4l1if");

    // get capabilities
    if( -1 == ioctl(devv4l,VIDIOCGCAP,&vcap) )
	throw FATAL("VIDIOC_G_CAP in ::v4l1if");

    // get input sources (channels)
    if(vcap.channels)
    {
	vchan = (struct video_channel*)malloc(sizeof(struct video_channel)*vcap.channels);
	memset(vchan,0,sizeof(struct video_channel)*vcap.channels);
    }
    
    // get audios
    if(vcap.audios)
    {
	vaudio = (struct video_audio*)malloc(sizeof(struct video_audio)*vcap.audios);
	memset(vaudio,0,sizeof(struct video_audio)*vcap.audios);
    }

    //    return;

#ifdef v4lDEBUG
    debug("v4l1: Grabber name: %s",vcap.name);
    for (i = 0; device_cap[i].str != NULL; i++)
	if (vcap.type & (1 << i))
	    debug("\t %s: %s",device_cap[i].str,device_cap[i].description);
    debug("v4l1: range of tv size : %dx%d => %dx%d",
	  vcap.minwidth,vcap.minheight,
	  vcap.maxwidth,vcap.maxheight);
    debug("v4l1: input channels: %d",vcap.channels);
#endif

    // get information about each channel
    for (i = 0; i < vcap.channels; i++) {
	vchan[i].channel = i;
	if ( -1 == ioctl(devv4l,VIDIOCGCHAN,&vchan[i]) )
	    VIF_FATAL("v4l1: VIDIOC_G_CHAN in ::v4l1if");
    }
    achan= 0;

#ifdef v4lDEBUG
    for (i = 0; i < vcap.channels; i++) {
	debug("\t [%d] %s: %s [%d], %s%s%s%s",i+1,
	      vchan[i].name,
	      (vchan[i].flags & VIDEO_VC_TUNER)   ? "has tuner"  : "no tuner",
	      vchan[i].tuners,
	      (vchan[i].flags & VIDEO_VC_AUDIO)   ? "audio, "  : "",
	      "",//(vchan[i].flags & VIDEO_VC_NORM)    ? "norm, "  : "",
	      (vchan[i].type & VIDEO_TYPE_TV)     ? "tv"     : "",
	      (vchan[i].type & VIDEO_TYPE_CAMERA) ? "camera" : "");
    }
#endif

    // set v4l to the first aviable input source, also set first norm
    vchan[0].norm= 0;
    if ( -1 == ioctl(devv4l, VIDIOCSCHAN, &vchan[0]) )
	VIF_WARN("v4l1: you need a newer bttv version (>= 0.5.14)");

    // get information for each audio
    for (i = 0; i < vcap.audios; i++) {
	vaudio[i].audio = i;
	if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[i]) )
	    VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::v4l1if");
    }
    aaudio= 0;

    // mute them all
    for (i = 0; i < vcap.audios; i++)
	if (vaudio[i].flags & VIDEO_AUDIO_MUTABLE) {
	    vaudio[i].flags |= VIDEO_AUDIO_MUTE;
	    if ( -1 == ioctl(devv4l,VIDIOCSAUDIO,&vaudio[i]) )
		VIF_FATAL("v4l1: VIDIOC_S_AUDIO in ::v4l1if");
	}
    smute= true;

#ifdef v4lDEBUG
    debug("v4l1: input audios: %d",vcap.audios);
    for (i = 0; i < vcap.audios; i++) {
	debug("\t [%d] %s: %s%s vol=%d, bass=%d, ter=%d",i+1,vaudio[i].name,
	      (vaudio[i].flags & VIDEO_AUDIO_MUTABLE)?"mutable":"not mutable",
	      (vaudio[i].flags & VIDEO_AUDIO_MUTE)?"[unmuted]":"[muted]",
	      (vaudio[i].flags & VIDEO_AUDIO_VOLUME)?vaudio[i].volume:-1,
	      (vaudio[i].flags & VIDEO_AUDIO_BASS)?vaudio[i].bass:-1,
	      (vaudio[i].flags & VIDEO_AUDIO_TREBLE)?vaudio[i].treble:-1);
    }
#endif

    // check tuner ( are there any tv cards with more than one ??? )
    for (i = 0; i < vcap.channels; i++) {
	if ( vchan[i].tuners > 1 ) {
	    warning("v4l1: Found more than one [%d] tuner for source channel %s.",vchan[i].tuners,vchan[i].name);
	    warning("v4l1: Only the first tuner of channel %s will be supported!",vchan[i].name);
	    warning("v4l1: This warning can be ignored...");
	}
    }
    atuner= 0;
    memset(&vtuner,0,sizeof(struct video_tuner));
    //  vtuner.tuner=atuner;
    if ( vcap.type & VID_TYPE_TUNER) {
	if ( -1 == ioctl(devv4l, VIDIOCGTUNER, &vtuner) )
	    VIF_FATAL("v4l1: VIDIOC_G_TUNER in ::v4l1if");

#ifdef v4lDEBUG
	debug("v4l1: tuner: %s %lu-%lu",vtuner.name,vtuner.rangelow,vtuner.rangehigh);
	debug("v4l1: tuner sees stereo: %s",vtuner.flags & VIDEO_TUNER_STEREO_ON?"yes":"no");
	//if ( vtuner.flags & VIDEO_TUNER_NORM ) {
	debug("v4l1: tuner supports modes (active: %s): ",norms[vtuner.mode].str);
	for (i = 0; norms[i].str != NULL; i++) {
	    if (vtuner.flags & (1<<i)) {
		debug("\t %s",norms[i].str);
	    }
	}
	//} else debug("v4l1: tuner supports no modes (fixed: %s)",norms[vtuner.mode].str);
#endif
    }

    // get window parameters
    if (  -1 == ioctl(devv4l, VIDIOCGWIN, &(vwin)) )
	VIF_FATAL("v4l1: VIDIOC_G_WIN in ::v4l1if");

    if ( -1 == ioctl(devv4l, VIDIOCGPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_G_PICT in ::v4l1if");

#ifdef v4lDEBUG
    debug("v4l1:  picture:  brightness=%d hue=%d colour=%d contrast=%d",
	  vpic.brightness, vpic.hue, vpic.colour, vpic.contrast);
    debug("v4l1:  picture : whiteness=%d depth=%d palette=%d[%s]",
	  vpic.whiteness, vpic.depth, vpic.palette, PALETTE(vpic.palette));
#endif

    // get frame buffer parameters
    if (  -1 == ioctl(devv4l, VIDIOCGFBUF, &(vbuf)) )
	VIF_FATAL("v4l1: ioctl VIDIOC_G_FBUF in ::v4l1if");

#ifndef GUILESS
#ifdef v4lDEBUG
    List_visuals(disp);
#endif
#endif

#ifndef GUILESS
    // get the visual
    int display_bits= find_visual(disp,NULL,&myvisual,&visual_class);

    // do some checks for color depth, size and buffer pos
    // first, guess the color depth of the screen

    int n,pixmap_bytes = 0;
    XPixmapFormatValues *pf = XListPixmapFormats(disp,&n);
    for (i = 0; i < n; i++) {
	if (pf[i].depth == QColor::numBitPlanes())
	    pixmap_bytes = pf[i].bits_per_pixel/8;
    }

    /* guess physical screen format */
    if (ImageByteOrder(disp) == MSBFirst) {
	switch (pixmap_bytes) {
	case 1: x11_format = VIDEO_RGB08; break;
	case 2: x11_format = (display_bits==15) ? VIDEO_RGB15_BE : VIDEO_RGB16_BE; break;
	case 3: x11_format = VIDEO_RGB24; break;
	case 4: x11_format = VIDEO_RGB32; break;
	default:
	    VIF_WARN_STR("v4l1: Unknown color depth found: %d",pixmap_bytes);
	}
    } else { // little endian (intel)
	switch (pixmap_bytes) {
	case 1: x11_format = VIDEO_RGB08; break;
	case 2: x11_format = (display_bits==15) ? VIDEO_RGB15_LE : VIDEO_RGB16_LE; break;
	case 3: x11_format = VIDEO_BGR24; break;
	case 4: x11_format = VIDEO_BGR32; break;
	default:
	    VIF_WARN_STR("v4l1: Unknown color depth found: %d",pixmap_bytes);
	}
    }

#ifdef v4lDEBUG
    debug("v4l1: hope physical screen format is <%s>",format_desc[x11_format]);
#endif

    if ( _palette > 0 && _palette < 11 ) {
	x11_format = _palette;
    }
    vpic.palette = (x11_format < sizeof(format2palette)/sizeof(unsigned short)) ?
	format2palette[x11_format] : 0;
    if ( vpic.palette == 0 ) {
	warning("v4l1: unsupported overlay video format <%d-%s> in ::v4l1if",
		x11_format,format_desc[x11_format]);
	vpic.palette = VIDEO_RGB08;
    }
#else /* GUILESS */
    vpic.palette = VIDEO_RGB15_LE;
#endif

#ifdef v4lDEBUG
    debug("v4l1: set palette to %d",format2palette[x11_format]);
#endif

    if ( -1 == ioctl(devv4l, VIDIOCSPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_S_PICT in ::v4l1if");

#ifdef v4lDEBUG
    // reread palette
    if ( -1 == ioctl(devv4l, VIDIOCGPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_G_PICT in ::v4l1if");
    debug("v4l1: palette is %d",vpic.palette);
#endif

#ifndef GUILESS
#ifdef HAVE_LIBXXF86DGA // do some strange things with dga
    int major, minor, width, bank, ram;
    int flags;
    void *base = NULL;
    bool have_dga= false;

#ifndef XDGA_MAJOR_VERSION
#define XDGA_MAJOR_VERSION XF86DGA_MAJOR_VERSION
#endif

#ifndef XDGA_MINOR_VERSION
#define XDGA_MINOR_VERSION XF86DGA_MINOR_VERSION
#endif

    if (XF86DGAQueryExtension(disp, &major, &minor)) {
	XF86DGAQueryDirectVideo(disp,XDefaultScreen(disp),&flags);
	if (flags & XF86DGADirectPresent) {
	    have_dga= true;
	    XF86DGAQueryVersion(disp,&major,&minor);
	    if ((major != XDGA_MAJOR_VERSION) || (minor != XDGA_MINOR_VERSION)) {
		warning("v4l1: X-Server DGA extension version mismatch, disabled");
		warning("v4l1: server version %d.%d != included version %d.%d",major,minor,
			XDGA_MAJOR_VERSION,XDGA_MINOR_VERSION);
		have_dga= false;
	    } else {
		XF86DGAGetVideoLL(disp, DefaultScreen(disp), (int *)&base, &width, &bank, &ram );
		if (!base) fatal("v4l1: can not allocate frame buffer base: 0x%x",(int)base);
	    }
	}
    }

    if (have_dga) {
	// check framebuffer
	if ( (void*)((unsigned long)vbuf.base & 0xfffff000) !=
	     (void*)((unsigned long)base      & 0xfffff000) ) {
	    warning("v4l1: Video4Linux and DGA disagree about the framebuffer base");
	    warning("v4l1: Video4Linux: 0x%x, dga: 0x%x!",
		    (unsigned int)vbuf.base ,(unsigned int)base);
	    warning("v4l1: you probably want to insmod the bttv module with "
		    "\"vidmem=0x%03lx\"\n",(unsigned long)base >> 20);
	    setupok=false;
	}
	if ( _bpp == 0  && _palette == 0 ) {
	    // check color depth
	    if ( (unsigned int)((vbuf.depth+7)&0xf8) != format2depth[x11_format] ) {
		warning("v4l1: Video4Linux and DGA disagree about the color depth");
		warning("v4l1: Video4Linux: %d, DGA: %d.", (vbuf.depth+7)&0xf8,format2depth[x11_format]);
		warning("v4l1: Is kv4lsetup installed correctly?");
		setupok=false;
	    }
	}
#ifdef v4lDEBUG
	else debug("v4l1: forced bpp %d, palette %d",_bpp,_palette);
#endif
    } // have_dga
#endif /* HAVE_LIBXXF86DGA */

#endif /* GUILESS */
    // get infos about snapshot buffer memory size
    if ( -1 == ioctl(devv4l, VIDIOCGMBUF, &(vmbuf)) )
	VIF_FATAL("v4l1: VIDIOC_G_MBUF in ::v4l1if");
    msize = vmbuf.size;

    // Alloc memory for snapshot
    grabbermem=(char *)mmap(0,msize,PROT_READ|PROT_WRITE,MAP_SHARED,devv4l,0);
    if ((char*)-1 == grabbermem) {
	warning("v4l1: unable to allocate memory for snap shots and video clips!");
    }

//#ifdef v4lDEBUG
    debug("v4l1: bpp %d, bpl %d, width %d, height %d",vbuf.depth,vbuf.bytesperline,vwin.width,vwin.height);
    debug("v4l1: video buffer: size %d, frames %d",vmbuf.size,vmbuf.frames);
    debug("v4l1: memory for snap shots: 0x%08x", (long) grabbermem);
//#endif
    gsync=ggrab=0;
    geven=true;

    if ( !setupok ) exit(1);
}

v4l1if::~v4l1if()
{
#ifdef v4lDEBUG
    debug("v4l1: ::~v4l1if begin");
#endif

    // mute all
    int i;
    if(vaudio)
    {
	for (i = 0; i < vcap.audios; i++)
	    if (vaudio[i].flags & VIDEO_AUDIO_MUTABLE) {
		vaudio[i].flags |= VIDEO_AUDIO_MUTE;
		if ( -1 == ioctl(devv4l,VIDIOCSAUDIO,&vaudio[i]) )
		    VIF_FATAL("v4l1: VIDIOC_S_AUDIO in ::v4l1if");
	    }
	smute= true;
    }
#if 1
    if (ggrab > gsync) {
	if ( -1 == ioctl(devv4l,VIDIOCSYNC,0) )
	    VIF_WARN("v4l1: VIDIOCSYNC in ::~v4l1if");
	else
	    gsync++;
    }
#endif

    // capture off
    if ( -1 == ioctl(devv4l, VIDIOCCAPTURE, &zero) )
	fatal("v4l1: ioctl VIDIOCCAPTURE in v4l1if::~v4l1if");

    if(grabbermem)
	if ((char*)-1 != grabbermem) 
	    munmap(grabbermem,msize);

    if(vchan)
	free(vchan);
    if(vaudio)
        free(vaudio);

#ifdef v4lDEBUG
    debug("v4l1: ::~v4l1if end");
#endif
}

void v4l1if::setCapture( bool on )
{
    pthread_mutex_lock(&mutex);
    if ( -1 == ioctl(devv4l, VIDIOCCAPTURE, (on?&one:&zero) ) )
	VIF_FATAL("v4l1: VIDIOCCAPTURE in ::setCapture");
    scapture= on;
    pthread_mutex_unlock(&mutex);
}
void v4l1if::setFreq( unsigned long freq )
{
    if ( -1 == ioctl(devv4l, VIDIOCSFREQ, &freq) )
	VIF_FATAL("v4l1: VIDIOC_S_FREQ in ::setFreq");
}
void v4l1if::setPicBrightness( int bri )
{
#ifdef PREREAD
    if ( -1 == ioctl(devv4l, VIDIOCGPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_G_PICT in ::setBrightness");
#endif
    vpic.brightness= (bri+128)<<8;
    if ( -1 == ioctl(devv4l, VIDIOCSPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_S_PICT in ::setBrightness");
}
void v4l1if::setPicConstrast( int contr )
{
#ifdef PREREAD
    if ( -1 == ioctl(devv4l, VIDIOCGPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_G_PICT in ::setContrast");
#endif
    vpic.contrast= contr<<7;
    if ( -1 == ioctl(devv4l, VIDIOCSPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_S_PICT in ::setContrast");
}
void v4l1if::setPicColor( int color )
{
#ifdef PREREAD
    if ( -1 == ioctl(devv4l, VIDIOCGPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_G_PICT in ::setColor");
#endif
    vpic.colour= color<<7;
    if ( -1 == ioctl(devv4l, VIDIOCSPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_S_PICT in ::setColor");
}
void v4l1if::setPicHue( int hue )
{
#ifdef PREREAD
    if( -1 == ioctl(devv4l, VIDIOCGPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_G_PICT in ::setHue");
#endif
    vpic.hue= (hue+128)<<8;
    if ( -1 == ioctl(devv4l, VIDIOCSPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_S_PICT in ::setHue");
}
void v4l1if::setChannel( int source )
{
    if((source<0)||(source>=vcap.channels))
    {
	fprintf(stderr, "warning: no such channel %d\n", source);
	return;
    }
#ifdef v4lDEBUG
    debug("v4l1: setChannel %d",source);
#endif
#if 0
    if ( -1 == ioctl(devv4l, VIDIOCSCHAN, &source) )
	VIF_WARN("v4l1: VIDIOC_S_CHAN in ::setChannel");
#else
    if ( -1 == ioctl(devv4l, VIDIOCSCHAN, &vchan[source]) )
    {
	VIF_WARN("v4l1: VIDIOC_S_CHAN in ::setChannel");
	return;
    }
#endif
    achan= source;
    if(source<vcap.audios)
	aaudio= source;
}
void v4l1if::setChannelNorm( int norm )
{
#ifdef v4lDEBUG
    debug("v4l1: setChannelNorm %d",norm);
#endif
    if ( -1 == ioctl(devv4l, VIDIOCSCHAN, &vchan[achan]) )
    {
	VIF_WARN("v4l1: VIDIOC_S_CHAN in ::setChannelNorm");
	return;
    }
    vchan[achan].norm= norm;
}
void v4l1if::setTuner( int no )
{
    atuner= no;
}
void v4l1if::setTunerMode( int norm )
{
    vtuner.tuner= atuner;
#ifdef PREREAD
    if ( -1 == ioctl(devv4l, VIDIOCGTUNER, &vtuner) )
	VIF_FATAL("v4l1: VIDIOC_G_TUNER in ::setTunerMode");
#endif
    vtuner.mode= norm;
    if ( -1 == ioctl(devv4l, VIDIOCSCHAN, &vtuner) )
	VIF_FATAL("v4l1: VIDIOC_S_TUNER in ::setTunerMode");
    warning("v4l1: Use ::setChannelNorm to set input NORM in ::setTunerMode");
}
void v4l1if::setAudioMute( bool on )
{
    if(aaudio>=vcap.audios)
    {
	fprintf(stderr, "WARNING: no such audio channel %d\n", aaudio);
	return;
    }
    if ( vaudio[aaudio].flags & VIDEO_AUDIO_MUTABLE ) {
	if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	    VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::setMute");

	if ( on )
	    vaudio[aaudio].flags |= VIDEO_AUDIO_MUTE;
	else
	    vaudio[aaudio].flags &= ~VIDEO_AUDIO_MUTE;

	if ( -1 == ioctl(devv4l,VIDIOCSAUDIO,&vaudio[aaudio]) )
	    VIF_FATAL("v4l1: VIDIOC_S_AUDIO in ::setMute");

	smute= on;
    }
#ifdef v4lDEBUG
    debug("v4l1: setAutioMute [%d] %s",aaudio,smute?"muted":"unmuted");
#endif
}
void v4l1if::setAudioVolume( int vol )
{
    if(aaudio>=vcap.audios)
    {
	fprintf(stderr, "WARNING: no such audio channel %d\n", aaudio);
	return;
    }
#ifdef PREREAD
    if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::setVolume");
#endif
    vaudio[aaudio].volume= vol;
    if ( -1 == ioctl(devv4l,VIDIOCSAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_S_AUDIO in ::setVolume");
}
void  v4l1if::setAudioMode( int mode )
{
    if(aaudio>=vcap.audios)
    {
	fprintf(stderr, "WARNING: no such audio channel %d\n", aaudio);
	return;
    }
#ifdef PREREAD
    if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::setAudioMode");
#endif
#ifdef v4lDEBUG
    debug("v4l1: setAudioMode= %d [%s]",mode,audiodescr[mode]);
#endif
    vaudio[aaudio].mode= mode;
    if ( -1 == ioctl(devv4l,VIDIOCSAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_S_AUDIO in ::setAudioMode");
}

void v4l1if::setPalette( int pal )
{

#ifndef GUILESS  
    x11_format = ((unsigned int)pal < sizeof(format2palette)/sizeof(unsigned short))?format2palette[pal]:0;
    vpic.palette= x11_format;

    if ( vpic.palette == 0 ) {
	VIF_FATAL_STR("v4l1: unsupported overlay video format <%s> in ::v4l1if",
		      format_desc[x11_format]);
    }
#else
    vpic.palette = VIDEO_RGB15_LE;
#endif /* GUILESS */

#ifdef v4lDEBUG
    //debug("v4l1: palette= %s, %s",PALETTE(vpic.palette),format_desc[pal]);
#endif

    if ( -1 == ioctl(devv4l, VIDIOCSPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_S_PICT in ::v4l1if");
}

bool v4l1if::getAudioMute()
{
    if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::getMute");
#ifdef v4lDEBUG
    debug("v4l1: getAudioMute %s, %d",
	  (vaudio[aaudio].flags & VIDEO_AUDIO_MUTE)?"true":"false",
	  vaudio[aaudio].flags);
#endif
    return ( vaudio[aaudio].flags & VIDEO_AUDIO_MUTE );
}
int  v4l1if::getAudioVolume()
{
    if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::getVolume");
    return vaudio[aaudio].volume;
}
int  v4l1if::getAudioMode()
{
    if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::getAudioMode");
#ifdef v4lDEBUG
    debug("v4l1: getAudioMode= %d",vaudio[aaudio].mode);
#endif
    return vaudio[aaudio].mode;
}
unsigned long v4l1if::getTunerSignal()
{
    vtuner.tuner= atuner;
    if ( -1 == ioctl(devv4l, VIDIOCGTUNER, &vtuner) )
	VIF_FATAL("v4l1: VIDIOC_G_TUNER in ::getTunerSignal");
    return vtuner.signal;
}
int v4l1if::getTunerMode()
{
    vtuner.tuner= atuner;
    if ( -1 == ioctl(devv4l, VIDIOCGTUNER, &vtuner) )
	VIF_FATAL("v4l1: VIDIOC_G_TUNER in ::getTunerMode");
    return vtuner.mode;
}
int v4l1if::getTunerFlags()
{
    vtuner.tuner= atuner;
    if ( -1 == ioctl(devv4l, VIDIOCGTUNER, &vtuner) )
	VIF_FATAL("v4l1: VIDIOC_G_TUNER in ::getTunerFlags");
    return vtuner.flags;
}

bool v4l1if::capAudioVolume()
{
    if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::capAudioVolume");
    return ( vaudio[aaudio].flags & VIDEO_AUDIO_VOLUME );
}
bool v4l1if::capAudioMutable()
{
    if ( -1 == ioctl(devv4l,VIDIOCGAUDIO,&vaudio[aaudio]) )
	VIF_FATAL("v4l1: VIDIOC_G_AUDIO in ::capAudioMutable");
    return ( vaudio[aaudio].flags & VIDEO_AUDIO_MUTABLE );
}

bool v4l1if::capTunerStereo()
{
    vtuner.tuner= atuner;
    if ( -1 == ioctl(devv4l, VIDIOCGTUNER, &vtuner) )
	VIF_FATAL("v4l1: VIDIOC_G_TUNER in ::getTunerMode");
    return vtuner.flags & VIDEO_TUNER_STEREO_ON;
}
bool v4l1if::capTunerNorm()
{
    vtuner.tuner= atuner;
    if ( -1 == ioctl(devv4l, VIDIOCGTUNER, &vtuner) )
	VIF_FATAL("v4l1: VIDIOC_G_TUNER in ::getTunerMode");
    return vtuner.flags & VIDEO_TUNER_NORM;
}

void v4l1if::capCapSize( unsigned int *minw, unsigned int *minh, unsigned int *maxw, unsigned int *maxh )
{
#if 0
    *minw= vcap.minwidth; *minh= vcap.minheight;
    *maxw= vcap.maxwidth; *maxh= vcap.maxheight;
#else
    *minw= vcap.minwidth;
    *minh= vcap.minheight;
    *maxw= maxwidth[vchan[achan].norm];
    *maxh= maxheight[vchan[achan].norm];
#endif
#ifdef v4lDEBUG
    debug("v4l1: minw %d, maxw %d, minh %d, maxh %d in ::capCapSize",*minw,*maxw,*minh,*maxh);
#endif
}

int v4l1if::addCapAClip( int x1,int y1, unsigned int x2, unsigned int y2, int xadj, int yadj )
{
    //    return 0;
    if ( vcap.type & VID_TYPE_CLIPPING ) {
#ifdef v4lDEBUG
	debug("v4l1: addCapAClip: [%d] %dx%d+%d+%d",nrofclips,x2-x1,y2-y1,x1-xadj,y1-yadj);
#endif
	if ( cliprecs[nrofclips].x !=  x1 - xadj ||
	     cliprecs[nrofclips].y != y1 - yadj ||
	     cliprecs[nrofclips].width != (int)x2 - x1 ||
	     cliprecs[nrofclips].height != (int)y2 - y1 )
	    clipTabChanged= true;

	cliprecs[nrofclips].x= x1 - xadj;
	cliprecs[nrofclips].y= y1 - yadj;
	cliprecs[nrofclips].width= x2 - x1;
	cliprecs[nrofclips].height= y2 - y1;
	nrofclips++;
    }
    return nrofclips;
}
int v4l1if::setCapAClip( int x, int y, unsigned int width, unsigned int height )
{
    //    return 0;
    width &= ~1;
    height &= ~1;
    x &= ~1;
    y &= ~1;
#if 0
    if ( -1 == ioctl(devv4l, VIDIOCGWIN, &vwin) )
	VIF_FATAL("v4l1: VIDIOC_G_WIN in ::setCapAClip");
#endif
#ifdef v4lDEBUG
    debug("v4l1: setCapAClip: flags %d, chromakey %d",vwin.flags,vwin.chromakey);
#endif
    pthread_mutex_lock(&mutex);
    if ( vcap.type & VID_TYPE_CHROMAKEY ) {
	vwin.chromakey  = 0;    /* XXX */
    }

    vwin.flags= 0;
    if ( vcap.type & VID_TYPE_CLIPPING ) {
	vwin.clips= cliprecs;
	vwin.clipcount= nrofclips;
    }

    vwin.x= x;
    vwin.y= y;
    vwin.height= height;
    vwin.width= width;

#ifdef v4lDEBUG
    debug("v4l1: setCapAClip new values: %d, %d, %d, %d",x,y,width,height);
#endif

    pthread_mutex_unlock(&mutex);
    return nrofclips;
}

int v4l1if::applyCapAClip( int n )
{
    //    return 0;
    int ox,oy,oh,ow;

    // save old values
    ox=vwin.x;
    oy=vwin.y;
    ow=vwin.width;
    oh=vwin.height;

#ifdef v4lDEBUG_CAP
    debug("v4l1: applyCapAClip given %d, %d, %d, %d [%d]",x,y,width,height,nrofclips);
#endif

//    if ( n == 0 ) {
	vwin.clipcount= n;
//    }
    pthread_mutex_lock(&mutex);
    if ( -1 == ioctl(devv4l, VIDIOCSWIN, &vwin) )
	VIF_FATAL("v4l1: VIDIOC_S_WIN in ::setCapAClip");

#if 1
#if 0
    if ( -1 == ioctl(devv4l, VIDIOCGWIN, &vwin) )
	VIF_FATAL("v4l1: VIDIOC_G_WIN in ::setCapAClip");
    if ( vwin.x != x ) vwin.x=x;
    if ( vwin.y != y ) vwin.y=y;
    if ( vwin.height != height ) vwin.height= height;
    if ( vwin.width != width ) vwin.width= width;
#else
    // set old values
    vwin.x= ox;
    vwin.y= oy;
    vwin.height= oh;
    vwin.width= ow;
#endif
#endif

#if 0
    vpic.palette = x11_format;

#ifdef v4lDEBUG
    //debug("v4l1: palette=%s",PALETTE(vpic.palette));
#endif

    if ( -1 == ioctl(devv4l, VIDIOCSPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_S_PICT in ::v4l1if");

    if ( -1 == ioctl(devv4l, VIDIOCGPICT, &vpic) )
	VIF_FATAL("v4l1: VIDIOC_G_PICT in ::v4l1if");

#ifdef v4lDEBUG
    //debug("v4l1: palette=%s",PALETTE(vpic.palette));
#endif
#endif

    pthread_mutex_unlock(&mutex);
    return n;
}

void v4l1if::getCapAClip( int *x,int *y, unsigned int *width, unsigned int *height )
{
    //return 0;
#if 0
    if (-1 == ioctl(devv4l, VIDIOCGWIN, &vwin) )
	VIF_FATAL("v4l1: VIDIOC_G_WIN in ::getCapAClip");
#endif
    *x= vwin.x;
    *y= vwin.y;
    *height= vwin.height;
    *width= vwin.width;
#ifdef v4lDEBUG_CAP
    debug("v4l1: getCapAClip: %d, %d, %d, %d",*x,*y,*width,*height);
#endif
}

bool v4l1if::grabSetParams( int width, int height, int palette )
{
    vgrab[0].width= width;
    vgrab[0].height= height;

    vgrab[0].frame=0;
    vgrab[0].format=palette;

    memcpy(&(vgrab[1]),&(vgrab[0]),sizeof(struct video_mmap));
    vgrab[1].frame=1;

    grabCapture( true );

    return true;
}
char * v4l1if::grabCapture( bool single )
{
    // even frame: 0
    // odd frame:  1
    char * rmem;
    if ( !single && (ggrab == gsync) )
	if ( !grabOne( geven ? 0 : 1 ) ) return NULL;

    if ( !grabOne( geven ? 1 : 0 ) ) return NULL;
    if ( ggrab > gsync+1 ) {
	grabWait( geven ? 0 : 1 );
	rmem= (grabbermem + vmbuf.offsets[ geven ? 0 : 1 ]);
    } else {
	grabWait( geven ? 1 : 0 );
	rmem= (grabbermem + vmbuf.offsets[ geven ? 1 : 0 ]);
    }
    geven= !geven;

    return rmem;
}
bool v4l1if::grabOne( int frame )
{
    if ( -1 == ioctl(devv4l,VIDIOCMCAPTURE,&(vgrab[frame])) ) {
	if (errno == EAGAIN) {
	    VIF_WARN("v4l1: Grabber chip can't sync");
	    return false;
	} else {
	    VIF_FATAL_STR("v4l1: VIDIOCMCAPTURE in ::grabOne: %s",strerror(errno));
	}
    }
    ggrab++;
    return true;
}
void v4l1if::grabWait( int frame )
{
    if ( -1 == ioctl(devv4l,VIDIOCSYNC,&(vgrab[frame].frame)) ) {
	VIF_WARN_STR("v4l1: VIDIOCSYNC in ::grabWait (frame %d)",frame);
    } else gsync++;
}
//==============================================================================

// local Variables: ***
// compile-command: "make v4lx" ***
// End: ***
