Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 10 Jul 1998 00:26:18 -0700
From:      Amancio Hasty <hasty@rah.star-gate.com>
To:        Kevin Day <toasty@home.dragondata.com>
Cc:        multimedia@FreeBSD.ORG
Subject:   Re: bt848 driver will be checked in this Friday 
Message-ID:  <199807100726.AAA10706@rah.star-gate.com>
In-Reply-To: Your message of "Fri, 10 Jul 1998 02:15:39 CDT." <199807100715.CAA16725@home.dragondata.com> 

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Please mail me the output of ./eeprom 0 128 which is available from
http://www.freebsd.org/~ahasty/Bt848.html

The Bt878 is an upgrade to the Bt848.

Please recompile fxtv-0.47 with this tvcapture.c the problem is that your
model has one extra MUX which Hauppauge uses for video input.

	Tnks,
	Amancio
	
> > Download the latest driver from my ftp and it looks like you have a Bt878.
> > 
> > ftp://rah.star-gate.com/pub/bt848.tar.gz
> > 
> > 	Have Fun,
> > 	Amancio
> > 
> 
> I've already got the latest from there.... Still no go.
> 
> Yeah, looking now... the picture of the card on the box shows a bt848... the
> box says it's a bt848, but the chip itself has writing too smudged to read
> what it really is....
> 
> Are bt878's compatible?
> 
> Kevin


[-- Attachment #2 --]
/*
 * tvcapture.c
 *
 * API for controlling the capture card attributes and state.
 *
 * (C) 1997 Randall Hopper
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 Files                ************** */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <machine/ioctl_bt848.h>
#include <signal.h>
#include "tvdefines.h"
#include "tvtypes.h"
#include "tvcapture.h"
#include "app_rsrc.h"
#include "glob.h"

/*      ******************** Local defines                ************** */

/*  For Fxtv 0.41/bktr driver Linux port -- remove when the latest is ported */
#ifdef OLD_TUNER_IFACE
#  define BT848SFMT METEORSFMT
#endif

#if defined(__FreeBSD__)
#  define DEV_BT848   "/dev/bktr%d"
#  define DEV_TUNER   "/dev/tuner%d"
#elif defined(linux)
#  warning FIXME: Does/how does Linux name multiple devices of the same type
#  warning        Is there a consistent policy as in FreeBSD?
#  define DEV_BT848   "/dev/bt848"
#  define DEV_TUNER   "/dev/bt848t"
#endif

/*  Macros  */
#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) )

#define HUE_MIN            BT848_HUEMIN
#define HUE_MAX            (BT848_HUEMIN + BT848_HUERANGE)
#define HUE_RANGE          BT848_HUERANGE
#define HUE_DRV_MIN        BT848_HUEREGMIN
#define HUE_DRV_RANGE      (BT848_HUEREGMAX - BT848_HUEREGMIN + 1)

#define BRIGHT_MIN         BT848_BRIGHTMIN
#define BRIGHT_MAX         (BT848_BRIGHTMIN + BT848_BRIGHTRANGE)
#define BRIGHT_RANGE       BT848_BRIGHTRANGE
#define BRIGHT_DRV_MIN     BT848_BRIGHTREGMIN
#define BRIGHT_DRV_RANGE   (BT848_BRIGHTREGMAX - BT848_BRIGHTREGMIN + 1)
 
#define CONTR_MIN          BT848_CONTRASTMIN
#define CONTR_MAX          (BT848_CONTRASTMIN + BT848_CONTRASTRANGE)
#define CONTR_RANGE        BT848_CONTRASTRANGE
#define CONTR_DRV_MIN      BT848_CONTRASTREGMIN
#define CONTR_DRV_RANGE    (BT848_CONTRASTREGMAX - BT848_CONTRASTREGMIN + 1)

#define SATU_MIN            BT848_SATUMIN
#define SATU_MAX            (BT848_SATUMIN + BT848_SATURANGE)
#define SATU_RANGE          BT848_SATURANGE
#define SATU_DRV_MIN        BT848_SATUREGMIN
#define SATU_DRV_RANGE      (BT848_SATUREGMAX - BT848_SATUREGMIN + 1)

#define SATV_MIN            BT848_SATVMIN
#define SATV_MAX            (BT848_SATVMIN + BT848_SATVRANGE)
#define SATV_RANGE          BT848_SATVRANGE
#define SATV_DRV_MIN        BT848_SATVREGMIN
#define SATV_DRV_RANGE      (BT848_SATVREGMAX - BT848_SATVREGMIN + 1)

#define NTSC_DIM_X          640
#define NTSC_DIM_Y          480
#define NTSC_FPS            30
#define PAL_DIM_X           768
#define PAL_DIM_Y           576
#define PAL_FPS             25

/*  Max Single Frame Size (in bytes) for NTSC & PAL (4Bpp max)  */
#define MAX_MMAP_BUF_SIZE   ( MAX( NTSC_DIM_X,PAL_DIM_X ) * \
                              MAX( NTSC_DIM_Y,PAL_DIM_Y ) * 4 )

#define FRAME_TIMER_DELAY_MS(fps) (1000/(fps))

/*      ******************** Private variables            ************** */

static TV_INT32     S_frame_done_count = 0;

static XtIntervalId S_frame_timer;
static TV_BOOL      S_frame_timer_set = False;


/*      ******************** Forward declarations         ************** */
/*      ******************** Function Definitions         ************** */

/**@BEGINFUNC**************************************************************

    Prototype  : static void TVCAPTUREDestroy()

    Purpose    : Cleans up capture attributes (stops capture, closes files,
                 frees mem, etc.

    Programmer : 07-Mar-97  Randall Hopper

    Parameters : None.

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

static void TVCAPTUREDestroy()
{
    TV_CAPTURE   *c = &G_glob.capture;

    if ( c->fd >= 0 ) {
        if ( c->contin_on )
            TVCAPTUREStop( c );

        close( c->fd );
    }
    if ( c->tfd >= 0 )
        close( c->tfd );
}


/**@BEGINFUNC**************************************************************

    Prototype  : static void TVCAPTUREFrameDoneSigHdlr()

    Purpose    : Called when the driver finishes capturing a frame.

    Programmer : 07-Mar-97  Randall Hopper

    Parameters : None.

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

static void TVCAPTUREFrameDoneSigHdlr()
{
    S_frame_done_count++;
#ifdef linux
#  warning Huh?  Do signal handlers get unregistered in Linux after delivery?
    signal( SIGUSR1, TVCAPTUREFrameDoneSigHdlr );
#endif
}

/**@BEGINFUNC**************************************************************

    Prototype  : void TVCAPTUREWorkProc()

    Purpose    : Xt work proc slave routine invoked to initiate behaviors 
                 associated with capturing frames.

    Programmer : 07-Mar-97  Randall Hopper

    Parameters : None.

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

void TVCAPTUREWorkProc()
{
    static TV_CAPTURE   *c = &G_glob.capture;
    TV_IMAGE             img;

    if (( S_frame_done_count == 0 ) || ( c->frame_done_cb == NULL ))
        return;

/*  if ( S_frame_done_count > 0 )
        DRVPRINTF(( "%d frames behind\n", S_frame_done_count ));
*/
    S_frame_done_count--;

    img.buf = c->drv_buf;
    memcpy( &img.geom, &c->geom, sizeof( img.geom ) );
    memcpy( &img.pix_geom, &c->pix_geom_list[ c->pix_geom_idx ],
            sizeof( img.pix_geom ) );
    c->frame_done_cb( &img );
}

/*  TVCAPTUREClearPendingFrames:  Reset/ignore any outstanding frames.  */
void TVCAPTUREClearPendingFrames()
{
    S_frame_done_count = 0;
}

/*  TVCAPTUREGetPendingFrame - Get pending frame & ret T (ret F if none)  */
TV_BOOL TVCAPTUREGetPendingFrame( TV_IMAGE **img )
{
    if ( S_frame_done_count > 0 ) {
        /*  FIXME:  Share this glob -- it's the WorkProc above w/o CB  */
        static TV_CAPTURE   *c = &G_glob.capture;
        static TV_IMAGE      image;

        if (( S_frame_done_count == 0 ) || ( c->frame_done_cb == NULL ))
            return;

        if ( S_frame_done_count-- > 0 )
            DRVPRINTF(( "%d frames behind\n", S_frame_done_count ));

        image.buf = c->drv_buf;
        memcpy( &image.geom, &c->geom, sizeof( image.geom ) );
        memcpy( &image.pix_geom, &c->pix_geom_list[ c->pix_geom_idx ],
                sizeof( image.pix_geom ) );
        *img = &image;
        return TRUE;
    }
    else {
        *img = NULL;
        return False;
    }
}


#ifdef OLD_ALWAYS_USE_SIGNALS_NOW
/**@BEGINFUNC**************************************************************

    Prototype  : static void TVCAPTUREFrameTimeoutCB(
                      XtPointer          cl_data,
                      XtIntervalId      *timer )

    Purpose    : Invoked during continuous capture to let us kick in
                 frame done callback periodically (since signals aren't
                 issued by the driver for frame completes when in 
                 continuous mode).

    Programmer : 08-Mar-97  Randall Hopper

    Parameters : cl_data - I: not used
                 timer   - I: not used

    Returns    :

    Globals    : None.

 **@ENDFUNC*****************************************************************/

static void TVCAPTUREFrameTimeoutCB(
         XtPointer          cl_data,
         XtIntervalId      *timer )
{        
    TV_CAPTURE   *c = &G_glob.capture;
    TV_IMAGE      img;

    /*  Invoke user-defined frame completion callback  */
    if ( c->frame_done_cb != NULL ) {
        img.buf = c->drv_buf;
        memcpy( &img.geom, &c->geom, sizeof( img.geom ) );
        memcpy( &img.pix_geom, &c->pix_geom_list[ c->pix_geom_idx ],
                sizeof( img.pix_geom ) );
        c->frame_done_cb( &img );
    }

    /*  And restart timer for another "frame"  */
    S_frame_timer = XtAppAddTimeOut( TVAPPCTX, 
                                     FRAME_TIMER_DELAY_MS(c->fps_max),
                                     TVCAPTUREFrameTimeoutCB, NULL );
}
#endif


void TVCAPTURESetBrightness( TV_CAPTURE *c, double brightness )
{
    TV_INT32 larg;

    brightness = MAX( BRIGHT_MIN, MIN( BRIGHT_MAX, brightness ) );

    larg = (brightness - BRIGHT_MIN) / (BRIGHT_RANGE + 0.01)
                                     * BRIGHT_DRV_RANGE + BRIGHT_DRV_MIN;
    larg = MAX( BRIGHT_DRV_MIN, 
                MIN( BRIGHT_DRV_MIN+BRIGHT_DRV_RANGE-1, larg ));

    if ( ioctl( c->tfd, BT848_SBRIG, &larg ) < 0 ) {
        DO_IOCTL_SERR( "BT848_SBRIG", larg );
        return;
    }

    /*  FIXME: Dead code  */
    c->brightness = brightness;
}


void TVCAPTURESetContrast( TV_CAPTURE *c, double contrast )
{
    TV_INT32 larg;
    
    contrast  = MIN( CONTR_MAX, MAX( CONTR_MIN, contrast ) );

    larg = (contrast - CONTR_MIN) / (CONTR_RANGE + 0.01)
                                  * CONTR_DRV_RANGE + CONTR_DRV_MIN;
    larg = MAX( CONTR_DRV_MIN, 
                MIN( CONTR_DRV_MIN+CONTR_DRV_RANGE-1, larg ) );

    if ( ioctl( c->tfd, BT848_SCONT, &larg ) < 0 ) {
        DO_IOCTL_SERR( "BT848_SCONT", larg );
        return;
    }

    /*  FIXME: Dead code  */
    c->contrast = contrast;
}


void TVCAPTURESetHue( TV_CAPTURE *c, double hue )
{
    TV_INT32 larg;

    hue  = MIN( HUE_MAX, MAX( HUE_MIN, hue ) );
    larg = (hue - HUE_MIN) / (HUE_RANGE + 0.01)
                           * HUE_DRV_RANGE + HUE_DRV_MIN;
    larg = MAX( HUE_DRV_MIN, 
                MIN( HUE_DRV_MIN+HUE_DRV_RANGE-1, larg ));

    if ( ioctl( c->tfd, BT848_SHUE, &larg ) < 0 ) {
        DO_IOCTL_SERR( "BT848_SHUE", larg );
        return;
    }

    /*  FIXME: Dead code  */
    c->hue = hue;
}


void TVCAPTURESetSatU( TV_CAPTURE *c, double sat_u )
{
    TV_INT32 larg;
    
    sat_u  = MIN( SATU_MAX, MAX( SATU_MIN, sat_u ) );
    larg = (sat_u - SATU_MIN) / (SATU_RANGE + 0.01)
                              * SATU_DRV_RANGE + SATU_DRV_MIN;
    larg = MAX( SATU_DRV_MIN, 
                MIN( SATU_DRV_MIN+SATU_DRV_RANGE-1, larg ) );

    if ( ioctl( c->tfd, BT848_SUSAT, &larg ) < 0 ) {
        DO_IOCTL_SERR( "BT848_SUSAT", larg );
        return;
    }

    /*  FIXME: Dead code  */
    c->sat_u = sat_u;
}


void TVCAPTURESetSatV( TV_CAPTURE *c, double sat_v )
{
    TV_INT32 larg;
    
    sat_v  = MIN( SATV_MAX, MAX( SATV_MIN, sat_v ) );
    larg = (sat_v - SATV_MIN) / (SATV_RANGE + 0.01)
                              * SATV_DRV_RANGE + SATV_DRV_MIN;
    larg = MAX( SATV_DRV_MIN, 
                MIN( SATV_DRV_MIN+SATV_DRV_RANGE-1, larg ) );

    if ( ioctl( c->tfd, BT848_SVSAT, &larg ) < 0 ) {
        DO_IOCTL_SERR( "BT848_SVSAT", larg );
        return;
    }

    /*  FIXME: Dead code  */
    c->sat_v = sat_v;
}

void TVCAPTURESetAppearanceParam( TV_CAPTURE *c, TV_DRIVER_PARAM p,
                                  double val )
{
    switch ( p ) {
        case TV_PARAM_HUE      :  TVCAPTURESetHue       ( c, val );  break;
        case TV_PARAM_BRIGHT   :  TVCAPTURESetBrightness( c, val );  break;
        case TV_PARAM_CONTRAST :  TVCAPTURESetContrast  ( c, val );  break;
        case TV_PARAM_SATU     :  TVCAPTURESetSatU      ( c, val );  break;
        case TV_PARAM_SATV     :  TVCAPTURESetSatV      ( c, val );  break;
        default :
            fprintf( stderr, 
               "TVCAPTURESetAppearanceParam: Unsupported param %d\n", p );
            exit(1);
    }
}

void TVCAPTURESetInputFormat( TV_CAPTURE *c, TV_INPUT_FORMAT format )
{
    TV_INT32 larg;

    switch ( format ) {
        case TV_INPUT_NTSCM    : larg = BT848_IFORM_F_NTSCM   ;  break;
        case TV_INPUT_NTSCJ    : larg = BT848_IFORM_F_NTSCJ   ;  break;
        case TV_INPUT_PALBDGHI : larg = BT848_IFORM_F_PALBDGHI;  break;
        case TV_INPUT_PALM     : larg = BT848_IFORM_F_PALM    ;  break;
        case TV_INPUT_PALN     : larg = BT848_IFORM_F_PALN    ;  break;
        case TV_INPUT_SECAM    : larg = BT848_IFORM_F_SECAM   ;  break;
        case TV_INPUT_PALNCOMB : larg = BT848_IFORM_F_RSVD    ;  break;
        default                :
        case TV_INPUT_AUTO     :
            fprintf( stderr, 
               "TVCAPTURESetInputFormat: Unsupported format %d\n", format );
            exit(1);
    }
    if ( ioctl( c->fd, BT848SFMT, &larg ) < 0 ) {
        DO_IOCTL_SERR( "BT848SFMT", larg );
        return;
    }

    /*  Update max capture size based on driver format  */
    /*  FIXME: These belong in the driver/driver include file  */
    switch ( format ) {
        case TV_INPUT_NTSCM :
        case TV_INPUT_NTSCJ :
        case TV_INPUT_PALM  :
            c->width_max  = NTSC_DIM_X;
            c->height_max = NTSC_DIM_Y;
            c->fps_max    = NTSC_FPS;
            break;
        case TV_INPUT_PALBDGHI :
        case TV_INPUT_PALN     :
        case TV_INPUT_SECAM    :
        case TV_INPUT_PALNCOMB :
            c->width_max  = PAL_DIM_X;
            c->height_max = PAL_DIM_Y;
            c->fps_max    = PAL_FPS;
            break;
    }
    /*  FIXME: Dead code  */
    c->input_format = format;
}


void TVCAPTURESetInputDevice( TV_CAPTURE *c, TV_INPUT_DEVICE dev )
{
    int arg,
        old_audio;

    /*  FIXME:  Move all driver set calls out of Start() into these  */
    /*    set calls.                                                 */
    if ( c->contin_on ) {
        fprintf( stderr, 
                 "TVCAPTURESetInputDevice(): Called when capture is on\n" );
        return;
    }

    /*  Driver FIXME: Hack to get around driver unmuting audio across  */
    /*    channel, freq, and input device changes.                     */
    if ( ioctl( c->tfd, BT848_GAUDIO, &old_audio ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GAUDIO" );
        return;
    }

    switch ( dev ) {
        case TV_DEVICE_TUNER : arg = METEOR_DEV1;  break;
        case TV_DEVICE_VIDEO  : arg = METEOR_INPUT_DEV3;  break;
        case TV_DEVICE_SVIDEO : arg = METEOR_DEV2;  break;
        default :
             fprintf( stderr, "TVCAPTURESetInputDevice(): Bad value %d\n",
                      dev );
             return;
    }
    if ( ioctl( c->fd, METEORSINPUT, &arg ) < 0 ) {
        fprintf( stderr, "ioctl(METEORSINPUT, %d) failed: %s\n", 
                 arg, strerror(errno) );
        return;
    }

    /*  FIXME: Dead code  */
    c->input_dev         = arg;

    /*  When we did that last SINPUT, the driver auto-selected the  */
    /*    audio input correct for that video input.  Override it    */
    /*    if the user has requested this.                           */
    if ( c->audio_input_dev != TV_AUDIO_INPUT_AUTO ) {
        switch ( c->audio_input_dev ) {
            default                    :
            case TV_AUDIO_INPUT_TUNER  : arg = AUDIO_TUNER;   break;
            case TV_AUDIO_INPUT_EXTERN : arg = AUDIO_EXTERN;  break;
            case TV_AUDIO_INPUT_INTERN : arg = AUDIO_INTERN;  break;
        }
        if ( ioctl( c->tfd, BT848_SAUDIO, &arg ) < 0 ) {
            fprintf( stderr, "ioctl(BT848_SAUDIO, %d) failed: %s\n", 
                     arg, strerror(errno) );
            return;
        }
    }

    /*  Restore the old mute setting  */
    old_audio &= AUDIO_MUTE;
    if ( old_audio ) 
        TVCAPTURESetAudioMute( c, TRUE );
}


void TVCAPTURESetAudioInputDevice( TV_CAPTURE *c, TV_AUDIO_INPUT_DEVICE dev )
{
    int arg;

    /*  FIXME:  Move all driver set calls out of Start() into these  */
    /*    set calls.                                                 */
    if ( c->contin_on ) {
        fprintf( stderr, 
                 "TVCAPTURESetAudioInputDevice(): "
                 "Called when capture is on\n" );
        return;
    }

    switch ( dev ) {
        case TV_AUDIO_INPUT_AUTO   : break;
        case TV_AUDIO_INPUT_TUNER  : arg = AUDIO_TUNER;   break;
        case TV_AUDIO_INPUT_EXTERN : arg = AUDIO_EXTERN;  break;
        case TV_AUDIO_INPUT_INTERN : arg = AUDIO_INTERN;  break;
        default :
             fprintf( stderr, "TVCAPTURESetAudioInputDevice(): Bad value %d\n",
                      dev );
             return;
    }

    /*  NOTE:  AUTO means we don't monkey with the defaults selected by  */
    /*    the driver.  Other values mean we always override the driver   */
    /*    to keep that setting whenever it wants to change the setting.  */
    if ( dev != TV_AUDIO_INPUT_AUTO ) {
        if ( ioctl( c->tfd, BT848_SAUDIO, &arg ) < 0 ) {
            fprintf( stderr, "ioctl(BT848_SAUDIO, %d) failed: %s\n", 
                     arg, strerror(errno) );
            return;
        }
        c->audio_input_dev = dev;
    }
    else {
        TV_DRIVER_STATE  s;

        c->audio_input_dev = dev;

        /*  Force driver to reset audio input back to its default  */
        if ( !TVCAPTUREQueryDriverState( c, &s ) ) {
            fprintf( stderr, "TVCAPTUREQueryDriverState() failed\n" );
            exit(1);
        }

        /*  This funny business so the driver will actually reset the input */
        TVCAPTURESetInputDevice( c, TV_DEVICE_TUNER );
        TVCAPTURESetInputDevice( c, TV_DEVICE_VIDEO );
        TVCAPTURESetInputDevice( c, s.input_dev );
    }
}


void TVCAPTURESetTunerChannel( TV_CAPTURE *c, TV_INT32 chan_num )
{
    TV_INT32 larg = chan_num;
    int   old_audio;

    /*  Driver FIXME: Hack to get around driver unmuting audio across  */
    /*    channel, freq, and input device changes.                     */
    if ( ioctl( c->tfd, BT848_GAUDIO, &old_audio ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GAUDIO" );
        return;
    }

    if ( ioctl( c->tfd, TVTUNER_SETCHNL, &larg ) < 0 ) {
        DO_IOCTL_SERR( "TVTUNER_SETCHNL", larg );
        return;
    }
    c->tuner_chan_active = TRUE;

    old_audio &= AUDIO_MUTE;
    if ( old_audio ) 
        TVCAPTURESetAudioMute( c, TRUE );
}


void TVCAPTURESetTunerFreq( TV_CAPTURE *c, double freq )
{
    TV_INT32 larg = freq * FREQFACTOR;
    int      old_audio;

    /*  Driver FIXME: Hack to get around driver unmuting audio across  */
    /*    channel, freq, and input device changes.                     */
    if ( ioctl( c->tfd, BT848_GAUDIO, &old_audio ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GAUDIO" );
        return;
    }

    if ( ioctl( c->tfd, TVTUNER_SETFREQ, &larg ) < 0 ) {
        DO_IOCTL_SERR( "TVTUNER_SETFREQ", larg );
        return;
    }
    c->tuner_chan_active = FALSE;

    old_audio &= AUDIO_MUTE;
    if ( old_audio ) 
        TVCAPTURESetAudioMute( c, TRUE );
}


void TVCAPTURESetTunerFreqSet( TV_CAPTURE *c, TV_FREQ_SET set )
{
    TV_INT32 larg;

    switch ( set ) {
        case TV_FREQ_SET_NABCST   : larg = CHNLSET_NABCST  ;  break;
        case TV_FREQ_SET_CABLEIRC : larg = CHNLSET_CABLEIRC;  break;
        case TV_FREQ_SET_CABLEHRC : larg = CHNLSET_CABLEHRC;  break;
        case TV_FREQ_SET_WEUROPE  : larg = CHNLSET_WEUROPE ;  break;
        case TV_FREQ_SET_JPNBCST  : larg = CHNLSET_JPNBCST ;  break;
        case TV_FREQ_SET_JPNCABLE : larg = CHNLSET_JPNCABLE;  break;
        default :
             fprintf( stderr,
                      "TVCAPTURESetTunerFreqSet(): Bad freq set %d\n", set ); 
             return;
    }
    
    if ( ioctl( c->tfd, TVTUNER_SETTYPE, &larg ) < 0 ) {
        DO_IOCTL_SERR( "TVTUNER_SETTYPE", larg );
        return;
    }

    /*  FIXME:  Hack to force a channel freq recompute in the driver  */
    /*    when we change modes.                                       */
    {
        TV_DRIVER_STATE  s;
        if ( !TVCAPTUREQueryDriverState( c, &s ) ) {
            fprintf( stderr, "TVCAPTUREQueryDriverState() failed\n" );
            exit(1);
        }
        TVCAPTURESetTunerChannel( c, s.tuner_chan );
    }
}

void TVCAPTURESetAfc( TV_CAPTURE *c, TV_BOOL afc )
{
    if ( ioctl( c->tfd, TVTUNER_SETAFC, &afc ) < 0 ) {
        DO_IOCTL_SERR( "TVTUNER_SETAFC", afc );
        return;
    }
}

void TVCAPTURESetAudioMute( TV_CAPTURE *c, TV_BOOL mute )
{
    int arg = ( mute ? AUDIO_MUTE : AUDIO_UNMUTE );

    /*  If audio is disabled, don't ever unmute  */
    if ( !App_res.do_audio )
        arg = AUDIO_MUTE;

    /*  Don't change audio source; just mute it  */
    if ( ioctl( c->tfd, BT848_SAUDIO, &arg ) < 0 ) {
        DO_IOCTL_SERR( "BT848_SAUDIO", arg );
        return;
    }
}

void TVCAPTURESetColorbars   ( TV_CAPTURE *c, TV_BOOL colorbars )
{
    int   ioctl_num = ( colorbars ?  BT848_SCBARS  :  BT848_CCBARS  );
    char *ioctl_str = ( colorbars ? "BT848_SCBARS" : "BT848_CCBARS" );
    int   dummy;

    /*  Don't change audio source; just mute it  */
    if ( ioctl( c->tfd, ioctl_num, &dummy ) < 0 ) {
        DO_IOCTL_SERR( ioctl_str, dummy );
        return;
    }
}

void TVCAPTURESetFPS       ( TV_CAPTURE *c, TV_UINT32 fps )
{
    TV_UINT16 usarg = MAX( 0, MIN( c->fps_max, fps ) );

    if ( ioctl( c->fd, METEORSFPS, &usarg ) < 0 ) {
        DO_IOCTL_SERR( "METEORSFPS", usarg );
        return;
    }

    /*  FIXME: Dead code  */
    c->fps = usarg;
}


void TVCAPTURESetFrameDoneCBEnabled( TV_CAPTURE *c, TV_BOOL enable )
{
    c->frame_cb_enabled = enable;
}


void TVCAPTUREGetFPSMax( TV_CAPTURE *c, TV_UINT32 *fps_max )
{
    *fps_max = c->fps_max;
}


/**@BEGINFUNC**************************************************************

    Prototype  : TV_BOOL TVCAPTUREQueryDriverState(
                      TV_CAPTURE *c,
                      TV_DRIVER_STATE *s )

    Purpose    : Queries the driver for all of its current parameters.

    Programmer : 16-Mar-97  Randall Hopper

    Parameters : c - I: capture device struct
                 s - O: driver state

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

TV_BOOL TVCAPTUREQueryDriverState( TV_CAPTURE *c, TV_DRIVER_STATE *s )
{
    TV_INT32     larg;
    TV_UINT16    usarg;
#ifdef NOT_NEEDED
    struct meteor_counts counts;
    struct meteor_video  video;
#endif

    memset( s, '\0', sizeof( *s ) );

    /*  INPUT DEVICE  */
    if ( ioctl( c->fd, METEORGINPUT, &larg ) < 0 ) {
        DO_IOCTL_GERR( "METEORGINPUT" );
        return False;
    }

    switch ( larg ) {
        case METEOR_DEV0:
        case METEOR_INPUT_DEV3 : s->input_dev = TV_DEVICE_VIDEO ;  break;
        case METEOR_DEV1 : s->input_dev = TV_DEVICE_TUNER ;  break;
        case METEOR_DEV2 : s->input_dev = TV_DEVICE_SVIDEO;  break;
        default :
             fprintf( stderr, "TVCAPTUREQueryDriverState(): Bad INPUT %d\n",
                      larg );
             return False;
    }

    /*  INPUT FORMAT  */
    if ( ioctl( c->fd, BT848GFMT, &larg ) < 0 ) {
        DO_IOCTL_GERR( "BT848GFMT" );
        return False;
    }
    switch ( larg ) {
        case BT848_IFORM_F_AUTO    : s->input_fmt = TV_INPUT_AUTO    ;  break;
        case BT848_IFORM_F_NTSCM   : s->input_fmt = TV_INPUT_NTSCM   ;  break;
        case BT848_IFORM_F_NTSCJ   : s->input_fmt = TV_INPUT_NTSCJ   ;  break;
        case BT848_IFORM_F_PALBDGHI: s->input_fmt = TV_INPUT_PALBDGHI;  break;
        case BT848_IFORM_F_PALM    : s->input_fmt = TV_INPUT_PALM    ;  break;
        case BT848_IFORM_F_PALN    : s->input_fmt = TV_INPUT_PALN    ;  break;
        case BT848_IFORM_F_SECAM   : s->input_fmt = TV_INPUT_SECAM   ;  break;
        case BT848_IFORM_F_RSVD    : s->input_fmt = TV_INPUT_PALNCOMB;  break;
        default :
             fprintf( stderr, "TVCAPTUREQueryDriverState(): Bad FMT %d\n",
                      larg );
             return False;
    }

    /*  CAPTURE DESTINATION  */
#ifdef NOT_NEEDED
    if ( ioctl( c->fd, METEOR_GVIDEO, &video ) < 0 ) {
        DO_IOCTL_GERR( "METEOR_GVIDEO" );
        return False;
    }
#endif

    /*  FRAMES PER SEC  */
    if ( ioctl( c->fd, METEORGFPS, &usarg ) < 0 ) {
        DO_IOCTL_GERR( "METEORGFPS" );
        return False;
    }
    s->fps = usarg;

    /*  SIGNAL  */
#ifdef NOT_NEEDED
    if ( ioctl( c->fd, METEOR_GSIGNAL, &larg ) < 0 ) {
        DO_IOCTL_GERR( "METEOR_GSIGNAL" );
        return False;
    }
    s->signal = larg;
#endif

    /*  CAPTURE STATISTICS  */
#ifdef NOT_NEEDED
    if ( ioctl( c->fd, METEOR_GCOUNT, &counts ) < 0 ) {
        DO_IOCTL_GERR( "METEOR_GCOUNT" );
        return False;
    }
    s->stats.frames_captured = counts.frames_captured;
#endif

    /*  HUE  */
    if ( ioctl( c->tfd, BT848_GHUE, &larg ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GHUE" );
        return False;
    }

    /*  Driver FIXME: Hack needed since 970322 driver is using an int, but */
    /*    isn't returning -128-127 range as in SHUE, but rather 0..255.    */
    if ( larg >= 128 )
        larg -= 256;

    s->hue = ((double)larg - HUE_DRV_MIN) / HUE_DRV_RANGE 
                                          * HUE_RANGE + HUE_MIN;

    /*  BRIGHTNESS  */
    if ( ioctl( c->tfd, BT848_GBRIG, &larg ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GBRIG" );
        return False;
    }

    /*  Driver FIXME: Hack needed since 970322 driver is using an int, but  */
    /*    isn't returning -128-127 range as in SHUE, but rather 0..255.     */
    if ( larg >= 128 )
        larg -= 256;

    s->brightness = ((double)larg - BRIGHT_DRV_MIN) / BRIGHT_DRV_RANGE 
                                                 * BRIGHT_RANGE + BRIGHT_MIN;

    /*  CONTRAST  */
    if ( ioctl( c->tfd, BT848_GCONT, &larg ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GCONT" );
        return False;
    }
    s->contrast = ((double)larg - CONTR_DRV_MIN) / CONTR_DRV_RANGE 
                                                 * CONTR_RANGE + CONTR_MIN;

    /*  CHROMA U SATURATION  */
    if ( ioctl( c->tfd, BT848_GUSAT, &larg ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GUSAT" );
        return False;
    }
    s->sat_u = ((double)larg - SATU_DRV_MIN) / SATU_DRV_RANGE 
                                              * SATU_RANGE + SATU_MIN;

    /*  CHROMA V SATURATION  */
    if ( ioctl( c->tfd, BT848_GVSAT, &larg ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GVSAT" );
        return False;
    }
    s->sat_v = ((double)larg - SATV_DRV_MIN) / SATV_DRV_RANGE 
                                              * SATV_RANGE + SATV_MIN;

    /*  TUNER TYPE  */
    if ( ioctl( c->tfd, TVTUNER_GETTYPE, &larg ) < 0 ) {
        DO_IOCTL_GERR( "TVTUNER_GETTYPE" );
        return False;
    }
    switch ( larg ) {
        case CHNLSET_NABCST   : s->tuner_freq_set = TV_FREQ_SET_NABCST  ;  
                                break;
        case CHNLSET_CABLEIRC : s->tuner_freq_set = TV_FREQ_SET_CABLEIRC;  
                                break;
        case CHNLSET_CABLEHRC : s->tuner_freq_set = TV_FREQ_SET_CABLEHRC;   
                                break;
        case CHNLSET_WEUROPE  : s->tuner_freq_set = TV_FREQ_SET_WEUROPE ;  
                                break;
        case CHNLSET_JPNBCST  : s->tuner_freq_set = TV_FREQ_SET_JPNBCST ;  
                                break;
        case CHNLSET_JPNCABLE : s->tuner_freq_set = TV_FREQ_SET_JPNCABLE;  
                                break;
        default :
             fprintf( stderr,
                      "TVCAPTUREQueryDriverState(): Bad CHNLSET %d\n", 
                      larg );
             return False;
    }

    /*  FIXME:  This is a hack because we can't query this info  */
    /*    from the driver.                                       */
    s->tuner_chan_active = c->tuner_chan_active;

    /*  TUNER CHANNEL  */
    if ( ioctl( c->tfd, TVTUNER_GETCHNL, &larg ) < 0 ) {
        DO_IOCTL_GERR( "TVTUNER_GETCHNL" );
        return False;
    }
    s->tuner_chan = larg;

    /*  TUNER FREQUENCY  */
    if ( ioctl( c->tfd, TVTUNER_GETFREQ, &larg ) < 0 ) {
        DO_IOCTL_GERR( "TVTUNER_GETFREQ" );
        return False;
    }
    s->tuner_freq = (double)larg / FREQFACTOR;

    /*  AUDIO MUTE AND INPUT STATE  */
    if ( ioctl( c->tfd, BT848_GAUDIO, &larg ) < 0 ) {
        DO_IOCTL_GERR( "BT848_GAUDIO" );
        return False;
    }
    s->audio_mute = (larg & AUDIO_MUTE) != 0;
    switch ( larg & ~AUDIO_MUTE ) {
        default           :
        case AUDIO_TUNER  : s->audio_input_dev = TV_AUDIO_INPUT_TUNER ;  break;
        case AUDIO_EXTERN : s->audio_input_dev = TV_AUDIO_INPUT_EXTERN;  break;
        case AUDIO_INTERN : s->audio_input_dev = TV_AUDIO_INPUT_INTERN;  break;
    }

    return True;
}


void TVCAPTUREQueryParamLimits( TV_CAPTURE *c, TV_DRIVER_PARAM p,
                                double lim[2] )
{
    static struct {
        TV_DRIVER_PARAM p;
        double          lim[2];
    } S_param_lim[] = {
        { TV_PARAM_HUE     , { HUE_MIN   , HUE_MAX    } },
        { TV_PARAM_BRIGHT  , { BRIGHT_MIN, BRIGHT_MAX } },
        { TV_PARAM_CONTRAST, { CONTR_MIN , CONTR_MAX  } },
        { TV_PARAM_SATU    , { SATU_MIN  , SATU_MAX   } },
        { TV_PARAM_SATV    , { SATV_MIN  , SATV_MAX   } }
    };

    TV_INT32 i;

    for ( i = 0; i < XtNumber( S_param_lim ); i++ )
        if ( S_param_lim[i].p == p )
            break;

    if ( i >= XtNumber( S_param_lim ) ) {
        fprintf( stderr, 
               "TVCAPTUREQueryParamLimits: Unsupported driver param (%d)", p );
        exit(1);
    }

    memcpy( lim, S_param_lim[i].lim, sizeof( S_param_lim[i].lim ) );
}


/**@BEGINFUNC**************************************************************

    Prototype  : void TVCAPTUREQueryPixelFormats(
                      TV_CAPTURE     *c,
                      TV_PIXEL_GEOM **list,
                      TV_UINT32      *list_size )

    Purpose    : Query all the support RGB pixel formats from the capture 
                driver and store them in a local list for fast access.

    Programmer : 20-Apr-97  Randall Hopper

    Parameters : c         - I: capture definition structure
                 list      - O: allocated/filled in list of pixel formats
                 list_size - O: resulting size of list

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

void TVCAPTUREQueryPixelFormats( TV_CAPTURE     *c,
                                 TV_PIXEL_GEOM **list,
                                 TV_UINT32      *list_size )
{
    /*  FIXME:  This belongs in the driver where we can query it using  */
    /*    the pixmap formats interface.                                 */
    static TV_PIXEL_GEOM 
        YUV_h422_v111 =
             { -1,TV_PIXELTYPE_YUV,0,{},0,0,
               {8,8,8},{1,2,2},{1,1,1},TV_FRAME_PLANAR,"YUV",1,1,0 },
        YUV_h422_v422 =
             { -1,TV_PIXELTYPE_YUV,0,{},0,0,
               {8,8,8},{1,2,2},{1,2,2},TV_FRAME_PLANAR,"YUV",1,1,0 };

    struct meteor_pixfmt pf;
    TV_UINT32            i;
    TV_PIXEL_GEOM       *pg;

    *list      = NULL;
    *list_size = 0;

    for ( i = 0; ; i++ ) {
        pf.index = i;
        if ( ioctl( c->fd, METEORGSUPPIXFMT, &pf ) < 0 ) {
            if ( errno == EINVAL )
                break;
            DO_IOCTL_GERR( "METEORGSUPPIXFMT" );
        }

        /*  Expand list  */
        (*list_size)++;
        *list = realloc( *list, *list_size * sizeof( (*list)[0] ) );
        if ( *list == NULL )
            TVUTILOutOfMemory();

        /*  Store new record  */
        pg = &(*list)[ *list_size - 1 ];
        memset( pg, '\0', sizeof( *pg ) );

        if ( pf.type == METEOR_PIXTYPE_RGB ) {
            pg->type        = TV_PIXELTYPE_RGB;
            pg->Bpp         = pf.Bpp;
            pg->mask[0]     = pf.masks[0];
            pg->mask[1]     = pf.masks[1];
            pg->mask[2]     = pf.masks[2];
            pg->swap_bytes  = pf.swap_bytes;
            pg->swap_shorts = pf.swap_shorts;
        }

        /*  FIXME:  These should be parameterized subtypes of YUV in  */
        /*    the driver, not separate types.  This would eliminate   */
        /*    confusion due to these legacy names, and make us        */
        /*    more independent of the capabilities of the driver.     */
        /*  FIXME:  Extend the driver interface to pass us these YUV  */
        /*          parms for each YUV subtype                        */
        else if (( pf.type == METEOR_PIXTYPE_YUV        ) ||
                 ( pf.type == METEOR_PIXTYPE_YUV_PACKED ) ||
                 ( pf.type == METEOR_PIXTYPE_YUV_12     )) {

            pg->type = TV_PIXELTYPE_YUV;

            switch ( pf.type ) {

                case METEOR_PIXTYPE_YUV        :
                    memcpy( pg, &YUV_h422_v111, sizeof(*pg) );
                    pg->frame_packing = TV_FRAME_PLANAR;
                    strcpy( pg->comp_order, "YUV" );
                    break;

                case METEOR_PIXTYPE_YUV_PACKED :    /*  4CC Code: YUY2  */
                    memcpy( pg, &YUV_h422_v111, sizeof(*pg) );
                    pg->frame_packing = TV_FRAME_PACKED;
                    strcpy( pg->comp_order, "YUYV" );
                    break;
                
                case METEOR_PIXTYPE_YUV_12     :    /*  4CC Code: IYUV/I420  */
                    memcpy( pg, &YUV_h422_v422, sizeof(*pg) );
                    pg->frame_packing = TV_FRAME_PLANAR;
                    strcpy( pg->comp_order, "YUV" );
                    break;
            }
        }
        else {
            fprintf( stderr, "Unsupported pixel type: %d\n", pg->type );
            exit(1);
        }

        pg->index       = pf.index;
    }
}

/**@BEGINFUNC**************************************************************

    Prototype  : void TVCAPTUREGetNumPixFmts(
                      TV_CAPTURE *c,
                      TV_UINT32 *num )

    Prototype  : void TVCAPTUREGetNthPixFmt (
                      TV_CAPTURE *c,
                      TV_UINT32 index,
                      TV_PIXEL_GEOM *geom )
                      
    Prototype  : void TVCAPTUREGetPixFmtByDriverHandle(
                      TV_CAPTURE *c,
                      TV_UINT32 handle,
                      TV_INT32 *index )

    Prototype  : void TVCAPTUREGetPixFmtByPixGeom(
                      TV_CAPTURE *c,
                      TV_PIXEL_GEOM *geom,
                      TV_INT32 *index )

    Purpose    : Query API for the pixel formats supported by out capture
                 device.

    Programmer : 20-Apr-97  Randall Hopper

    Parameters : c      - I: capture struct
                 num    - O: num pixel formats
                 index  - I: index into pixel formats list
                 handle - I: driver handle for pixel format
                 geom   - O: nth pixel format definition

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

void TVCAPTUREGetNumPixFmts( TV_CAPTURE *c, TV_UINT32 *num )
{
    *num = c->pix_geom_list_len;
}

void TVCAPTUREGetNthPixFmt ( TV_CAPTURE *c, TV_UINT32 index,
                             TV_PIXEL_GEOM *geom )
{
    if ( index >= c->pix_geom_list_len ) {
        fprintf( stderr, "TVCAPTUREGetNthPixFmt: Index out of range\n" );
        exit(1);
    }
    *geom = c->pix_geom_list[ index ];
}

void TVCAPTUREGetPixFmtByDriverHandle( TV_CAPTURE *c, TV_UINT32 handle,
                                       TV_INT32 *index )
{
    TV_UINT32 i;

    for ( i = 0; i < c->pix_geom_list_len; i++ )
        if ( handle == c->pix_geom_list[i].index )
            break;
    
    *index = ( i >= c->pix_geom_list_len ) ? -1 : i;
}

static TV_BOOL TVCAPTUREMaskIsaByte ( TV_UINT32 mask )
{
    TV_BOOL  isa_byte = FALSE;
    TV_INT32 i;

    for ( i = 0; i < sizeof( mask ); i++, mask >>= 8 )
        if ( mask & 0xff ) {
            if (( (mask & 0xff) == 0xff ) && ( (mask >> 8) == 0 ))
                isa_byte = TRUE;
            break;
        }
    return isa_byte;
}


void TVCAPTUREGetPixFmtByPixGeom( TV_CAPTURE *c, TV_PIXEL_GEOM *geom,
                                  TV_INT32 *index )
{
    static    TV_BOOL S_recursive_lock = FALSE;
    TV_UINT32 i;

    for ( i = 0; i < c->pix_geom_list_len; i++ )
        if (( geom->type == c->pix_geom_list[i].type ) &&
            ( geom->Bpp  == c->pix_geom_list[i].Bpp  ) &&
            !memcmp( geom->mask, c->pix_geom_list[i].mask,
                     sizeof( geom->mask ) ) &&
            ( geom->swap_bytes == c->pix_geom_list[i].swap_bytes   ) &&
            ( geom->swap_shorts == c->pix_geom_list[i].swap_shorts ))
            break;

    /*  Handle a few of the strange byte-swapped combinations for  */
    /*    3 and 4 bpp.                                             */
    /*    This lets us deal with visuals that return masks like    */
    /*    the Mach64 which is RGBA frame buffer (with pixmap       */
    /*    depth of 32 with visual masks of ff,ff00,ff0000)         */
    if ( ( i >= c->pix_geom_list_len ) &&
         (( geom->Bpp == 3 ) || ( geom->Bpp == 4 )) &&
         TVCAPTUREMaskIsaByte( geom->mask[0] ) &&
         TVCAPTUREMaskIsaByte( geom->mask[1] ) &&
         TVCAPTUREMaskIsaByte( geom->mask[2] ) ) {

        TV_PIXEL_GEOM g = *geom;

        g.mask[0] = geom->mask[2],
        g.mask[2] = geom->mask[0];
        g.swap_bytes = !g.swap_bytes;
        if ( g.Bpp == 4 )
            g.swap_shorts = !g.swap_shorts;
    }
    
    *index = ( i >= c->pix_geom_list_len ) ? -1 : i;
}


void TVCAPTUREPrintPixelFormats( TV_CAPTURE *c )
{
    TV_INT32       i,
                   j,
                   bpp,
                   swap;
    TV_UINT32      mask;
    TV_PIXEL_GEOM *pg;

    /*  Dump capture pixel formats for debugging purposes  */


    /*  PRINT RGB FORMATS  */
    SUPRINTF(( "\nSupported RGB Capture Pixel Formats:\n"
       "   bpp  Bpp  RGB Masks                     Swap\n"
       "   ---  ---  ----------------------------  ----\n" ));

    for ( i = 0; i < c->pix_geom_list_len; i++ ) {
        char          swap_chars[5];
        TV_INT32      j;
        TV_PIXEL_GEOM scratch;
        TV_BOOL       printit = TRUE;

        pg = &c->pix_geom_list[ i ];

        if ( pg->type != TV_PIXELTYPE_RGB )
            continue;
        
        /*  Browse list and see if we've got other pixel formats           */
        /*    with the same type/Bpp/mask but different swap permutations  */
        memset( swap_chars, ' ', sizeof( swap_chars ) );
        swap_chars[4] = '\0';

        for ( swap = 0; swap < 4; swap++ ) {
            scratch             = *pg;
            scratch.swap_bytes  = (swap & 0x01) != 0;
            scratch.swap_shorts = (swap & 0x02) != 0;

            TVCAPTUREGetPixFmtByPixGeom( c, &scratch, &j );
            if ( j < 0 )
                continue;
            if ( j < i ) {
                printit = FALSE;
                break;
            }
            swap_chars[ swap ] = "NBWb"[ swap ];
        }
        if ( !printit )
            continue;

        /*  Calculate bpp from masks  */
        mask = pg->mask[0] | pg->mask[1] | pg->mask[2];
        bpp  = 0;
        while ( mask != 0 ) {
            if ( mask & 0x01 )
                bpp++;
            mask >>= 1;
        }

        SUPRINTF(( 
            "    %2d  %2d   %.8x, %.8x, %.8x  %s\n",
            bpp, pg->Bpp, pg->mask[0], pg->mask[1], pg->mask[2], 
            swap_chars ));
    }

    /*  PRINT YUV FORMATS  */
    SUPRINTF(( "\nSupported YUV Capture Pixel Formats:\n"
       "   YUVSize  HSamp  VSamp  Pack    CompOrder  T->B  L->R  YTrans\n"
       "   -------  -----  -----  ------  ---------  ----  ----  ------\n" ));

    for ( i = 0; i < c->pix_geom_list_len; i++ ) {
        char          swap_chars[5];
        TV_INT32      j;
        TV_PIXEL_GEOM scratch;
        TV_BOOL       printit = TRUE;

        pg = &c->pix_geom_list[ i ];

        if ( pg->type != TV_PIXELTYPE_YUV )
            continue;
        
        SUPRINTF(( 
          "    %d,%d,%d   %d,%d,%d  %d,%d,%d  %-7s %-10s  %-5s %-6s %s\n",
          pg->samp_size [0], pg->samp_size [1], pg->samp_size [2],
          pg->samp_int_h[0], pg->samp_int_h[1], pg->samp_int_h[2],
          pg->samp_int_v[0], pg->samp_int_v[1], pg->samp_int_v[2],
          (pg->frame_packing == TV_FRAME_PLANAR ? "PLANAR" : "PACKED"),
          pg->comp_order, 
          (pg->order_t_to_b ? "Y" : "N"), (pg->order_l_to_r ? "Y" : "N"),
          (pg->y_trans ? "Y" : "N") ));
    }

    SUPRINTF(( "\n" ));
}


/**@BEGINFUNC**************************************************************

    Prototype  : static void TVCAPTUREDumpCaptureCardInfo(
                      TV_CAPTURE *c )

    Purpose    : Dump useful info about the capture card that can be
                 used by developers for debugging.

    Programmer : 09-Mar-98  Randall Hopper

    Parameters : c - I: capture definition struct 
                        (with open handles to tuner and capture devices)

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

static void TVCAPTUREDumpCaptureCardInfo( TV_CAPTURE *c )
{
    struct eeProm   eeprom;
    u_char         *p;
    int             x;
    int             cnt;
    TV_INT32        eeprom_size;
    FILE           *fp;
    char            linebuf[128];

    /*  Grab the pertinent dmesg output  */
    printf( "DETECTED CAPTURE CARD(S) [DRIVER PROBES]:\n" );
    if ( (fp = popen( "dmesg", "r" )) == NULL )
        fprintf( stderr, "    Failed to dump dmesg output.\n" );
    else {
        /*  FIXME:  Parsing boot-ups is BAD.  Make the driver return this  */
        /*          via ioctl.                                             */
        while ( !feof(fp) ) {
            if ( fgets( linebuf, sizeof(linebuf)-1, fp ) == NULL )
                break;
            if ( strncmp( linebuf, "bktr", 4 ) == 0 ) {
                printf( "    %s", linebuf );
                if ( fgets( linebuf, sizeof(linebuf)-1, fp) != NULL )
                    printf( "    %s", linebuf );
            }
        }
        pclose(fp);
    }

    /*  Dump the relevent bktr driver sysctl values (if present)  */
    printf( "\nSYSCTL MIB VALUES:\n" );
    if ( (fp = popen( "sysctl kern.version hw.bt848", "r" )) == NULL )
        fprintf( stderr, "    Failed to dump sysctl output.\n" );
    else {
        while ( !feof(fp) ) {
            if ( fgets( linebuf, sizeof(linebuf)-1, fp ) == NULL )
                break;
            printf( "    %s", linebuf );
        }
        pclose(fp);
    }
    

    /*  Read the card signature  */
    eeprom.offset = 0x01;
    eeprom.count  = 128;

    if ( ioctl( c->tfd, BT848_SIGNATURE, &eeprom ) < 0 )
        DO_IOCTL_GERR( "BT848_SIGNATURE" );
    else {
        printf( "\nTUNER SIGNATURE (0x%02x - 0x%02x):\n",
                eeprom.offset,
                (2 * ((eeprom.offset - 1) + eeprom.count)) - 1 );
        for ( p = &eeprom.bytes[ 0 ], x = 0; x < 16; ++x ) {
            if ( (x % 16) == 0 )
                printf( "%s   ", ((x > 0) ? "\n" : "") );
            printf( " %02x", p[ x ] );
        }
        printf( "\n" );
    
        printf( "\nTUNER I2C DEVICES FOUND AT:\n   " );
        cnt = 0;
        for ( p = &eeprom.bytes[ 0 ], x = 0; x < 128; ++x )
            if ( p[ x / 8 ] & (1 << (x % 8)) ) {
                printf( "%s 0x%02x", ((cnt > 0) ? "," : ""), x * 2 );
                cnt++;
            }
        printf( "\n" );
    }

    /*  Read the EEPROM  */
    /*    FIXME:  There's no way to query the size of the EEPROM from the   */
    /*    app, and the driver bails if the request is too big.  Modify the  */
    /*    driver to return the EEPROM size.  For now, use a heuristic.      */
    eeprom.offset = 0;
    eeprom.count  = 256;
    if ( ioctl( c->tfd, BT848_REEPROM, &eeprom ) < 0 ) {
        eeprom.count = 128;
        if ( ioctl( c->tfd, BT848_REEPROM, &eeprom ) < 0 ) {
            eeprom.count = 0;
            DO_IOCTL_GERR( "BT848_REEPROM" );
        }
    }
    printf( "\nCAPTURE CARD EEPROM CONTENTS:\n"
            "   Read %d EEPROM bytes ", eeprom.count );
    if ( eeprom.count == 0 )
        printf( "\n" );
    else {
        printf( "(0x00 - 0x%.2x)\n", eeprom.count-1 );

        for ( p = &eeprom.bytes[ 0 ], x = 0; x < eeprom.count; ++x ) {
            if ( (x % 16) == 0 )
                printf( "%s   ", ((x > 0) ? "\n" : "") );
            printf( " %02x", p[ x ] );
        }
        printf( "\n" );
    }

    /*  It'd also be helpful to be able to print the bktr driver version  */
}

            


/**@BEGINFUNC**************************************************************

    Prototype  : void TVCAPTUREInit(
                      TV_CAPTURE *c )

    Purpose    : Initialize the capture attributes.

    Programmer : 03-Mar-97  Randall Hopper

    Parameters : c       - I/O: Capture definition structure 

    Returns    : None.

    Globals    : G_tvcapture

 **@ENDFUNC*****************************************************************/

void TVCAPTUREInit( TV_CAPTURE *c )
{
    char dev_capture[80],
         dev_tuner  [80];

    /*  Build names of capture and tuner devices  */
    sprintf( dev_capture, DEV_BT848, App_res.device_number );
    sprintf( dev_tuner  , DEV_TUNER, App_res.device_number );

    c->fd = open( dev_capture, O_RDONLY );
    if ( c->fd < 0 ) {
        fprintf( stderr, "open(\"%s\") failed: %s\n", 
                 dev_capture, strerror(errno) );
        exit(1);
    }
    c->tfd = open( dev_tuner, O_RDONLY );
    if ( c->tfd < 0 ) {
        fprintf( stderr, "open(\"%s\") failed: %s\n", 
                 dev_tuner  , strerror(errno) );
        exit(1);
    }

    /*  Dump debug info on capture card  */
    if ( G_debug & DEBUG_STARTUP )
        TVCAPTUREDumpCaptureCardInfo( c );

    /*  Mute the audio until such time as we're ready to display  */
    TVCAPTURESetAudioMute( c, TRUE );

    /*  Just mmap the biggest buffer we'll need and be done with it.  */
    /*    (Buffer used for non-directvideo captures)                  */
    c->drv_buf = (TV_UINT8 *) mmap( (caddr_t)0, MAX_MMAP_BUF_SIZE,
                                    PROT_READ, MAP_SHARED, c->fd, (off_t)0 );
    if ( c->drv_buf == (TV_UINT8 *) -1 ) {
        fprintf( stderr, "mmap of driver buffer failed: %s\n", 
                 strerror(errno) );
        exit(1);
    }

    c->input_format    = TV_INPUT_NTSCM;
    c->input_dev       = METEOR_INPUT_DEV0;
    c->audio_input_dev = TV_AUDIO_INPUT_AUTO;

    c->bpp_format      = METEOR_GEO_RGB16;
    c->cap_mode        = TV_CAPTURE_CONTINUOUS;
    c->xfer_mode       = TV_TRANSFER_DIRECT;

    TVCAPTUREQueryPixelFormats( c, &c->pix_geom_list, &c->pix_geom_list_len);
    if ( c->pix_geom_list_len == 0 ) {
        fprintf( stderr, "capture device supports 0 pixel formats\n" );
        exit(1);
    }
    c->pix_geom_idx = 0;

    /*  FIXME: This is a hack -- here because we can't query whether the  */
    /*    the tuner is set to a specific channel number or whether an     */
    /*    off-channel frequency override is in-place.                     */
    c->tuner_chan_active = TRUE;

    TVCAPTUREPrintPixelFormats( c );

    c->field_targ[0] = TV_FIELD_DISPLAY;
    c->field_targ[1] = TV_FIELD_DISPLAY;
    c->hue           =   0.0; /* % */
    c->brightness    = +20.0; /* % */
    c->contrast      =  70.0; /* % */
    c->sat_u         =  100.0; /* % */
    c->sat_v         =  100.0; /* % */
    c->addr          = NULL;
    /*  geom -- see below */

    c->frame_done_cb    = NULL;
    c->frame_cb_enabled = FALSE;

    /*  FIXME: These belong in the driver/driver include file  */
    c->width_min  = 2;
    c->width_res  = 2;
    c->height_min = 2;
    c->height_res = 2;
    switch ( c->input_format ) {
        case TV_INPUT_NTSCM :
        case TV_INPUT_NTSCJ :
        case TV_INPUT_PALM  :
            c->width_max  = NTSC_DIM_X;
            c->height_max = NTSC_DIM_Y;
            c->fps_max    = NTSC_FPS;
            break;
        case TV_INPUT_PALBDGHI :
        case TV_INPUT_PALN     :
        case TV_INPUT_SECAM    :
        case TV_INPUT_PALNCOMB :
            c->width_max  = PAL_DIM_X;
            c->height_max = PAL_DIM_Y;
            c->fps_max    = PAL_FPS;
            break;
        default                :
        case TV_INPUT_AUTO     :
            fprintf( stderr, 
             "TVCAPTUREInit: Unsupported input format %d\n", c->input_format );
            exit(1);
    }
    c->fps           = c->fps_max;
    
    c->geom.x = c->geom.y = 0; /*  Unused  */
    c->geom.w = c->width_max ;
    c->geom.h = c->height_max;

    c->contin_on     = False;

    signal( SIGUSR1, TVCAPTUREFrameDoneSigHdlr );
    atexit( TVCAPTUREDestroy );
}


/**@BEGINFUNC**************************************************************

    Prototype  : void TVCAPTURESetCaptureMode(
    Prototype  : void TVCAPTURESetTransferMode(
    Prototype  : void TVCAPTURESetRegionGeom(
    Prototype  : void TVCAPTURESetPixelGeom(

    Purpose    : Routines which set the core capture parameters.

                 It would be good policy to set all these before every
                 capture.

    Programmer : 06-Jun-97  Randall Hopper

    Parameters : c         - I: capture definition
                 cap_mode  - I: new capture/transfer modes (single/contin)
                 xfer_mode - I: images (driver buf) or direct video
                 reg_geom  - I: image res (& origin, for direct video)
                 pix_geom  - I: captured pixel geometry 
                                (images only; ignored for xfer_mode = DIRECT)

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

void TVCAPTURESetCaptureMode( 
         TV_CAPTURE      *c,
         TV_CAPTURE_MODE  cap_mode )
{
    if ( c->contin_on == TRUE ) {
        fprintf( stderr, "SetCaptureMode called when contin running\n" );
        return;
    }
    c->cap_mode = cap_mode;
}


void TVCAPTURESetTransferMode( 
         TV_CAPTURE       *c,
         TV_TRANSFER_MODE  xfer_mode )
{
    if ( c->contin_on == TRUE ) {
        fprintf( stderr, "SetTransferMode called when contin running\n" );
        return;
    }
    c->xfer_mode = xfer_mode;
}


void TVCAPTURESetRegionGeom( 
         TV_CAPTURE      *c,
         TV_GEOM         *reg_geom )
{
    if ( c->contin_on == TRUE ) {
        fprintf( stderr, "SetRegionGeom called when contin running\n" );
        return;
    }
    c->geom = *reg_geom;
}


void TVCAPTURESetPixelGeom( 
         TV_CAPTURE      *c,
         TV_PIXEL_GEOM   *pix_geom )
{
    TV_INT32 idx;

    if ( c->contin_on == TRUE ) {
        fprintf( stderr, "SetPixelGeom called when contin running\n" );
        return;
    }

    TVCAPTUREGetPixFmtByDriverHandle( c, pix_geom->index, &idx );
    if (( idx < 0 ) || ( idx >= c->pix_geom_list_len )) {
        fprintf( stderr, "TVCAPTUREConfigure: Bad pixel format index\n" );
        exit(1);
    }
    c->pix_geom_idx  = idx;
}

/*  TVCAPTUREValidRegionGeom  -  Validates capture resolution  */
TV_BOOL TVCAPTUREValidRegionGeom(
            TV_CAPTURE      *c,
            TV_GEOM         *reg_geom )
{
    TV_INT32 w, h;

    w = reg_geom->w / c->width_res  * c->width_res;
    h = reg_geom->h / c->height_res * c->height_res;

    w = MAX( c->width_min , MIN( c->width_max , w ) );
    h = MAX( c->height_min, MIN( c->height_max, h ) );

    return (( w == reg_geom->w ) || ( h == reg_geom->h ));
}



/**@BEGINFUNC**************************************************************

    Prototype  : TV_BOOL TVCAPTUREConfigure(
                      TV_CAPTURE       *c,
                      char            **fail_reason )

    Purpose    : Looks at the requested capture parameters and determines
                 whether capture can be started using the current settings.
                 
    Programmer : 03-Mar-97  Randall Hopper

    Parameters : c           - I: capture definition
                 fail_reason - O: if configure failed, reason why (string)

    Returns    : T = Params OK - capture away; F = Nope, tweak somethin'

    Globals    : None.

 **@ENDFUNC*****************************************************************/

TV_BOOL TVCAPTUREConfigure( TV_CAPTURE       *c, 
                            char            **fail_reason )
{
    static char S_err_msg[ 128 ];

    TV_BOOL           ok = FALSE;
    TV_XSCREEN       *x = &G_glob.x;
    TV_DISPLAY       *d = &G_glob.display;
    TV_GEOM           g;
    TV_UINT32         addr;
    TV_INT32          Bpp,
                      idx;

    if ( c->contin_on == TRUE ) {
        strcpy( S_err_msg, "Continuous is running" );
        goto RETURN;
    }

    if ( c->frame_cb_enabled && !c->frame_done_cb ) {
        strcpy( S_err_msg, "Frame done callback on, but no frame handler" );
        goto RETURN;
    }

    if (( c->xfer_mode == TV_TRANSFER_DIRECT ) && ( d->win == None )) {
        strcpy( S_err_msg, "No X Window set for video" );
        goto RETURN;
    }

    if ( c->xfer_mode == TV_TRANSFER_DIRECT )
        if ( !( x->visual_modes[ x->active_visual ] & TV_TRANSFER_DIRECT )) {
            strcpy( S_err_msg, "Active visual does not support direct video");
            goto RETURN;
        }
        else if ( !d->enabled ) {
            strcpy( S_err_msg, "Direct transfer only supported for video" );
            goto RETURN;
        }

    /*  Get capture geometry  */
    if ( c->xfer_mode == TV_TRANSFER_DIRECT ) {
        TVSCREENUpdateWinGeometry();
        TVSCREENGetVideoWinGeom( &g );
        g.w = g.w / c->width_res  * c->width_res;
        g.h = g.h / c->height_res * c->height_res;
    }
    else
        g = c->geom;
    
    /*  Verify res limits and precision (all modes)  */
    if (( g.w != g.w / c->width_res  * c->width_res  ) ||
        ( g.h != g.h / c->height_res * c->height_res )) {
        sprintf( S_err_msg, "Capture geometry must be a multiple of %dx%d",
                 c->width_res, c->height_res );
        goto RETURN;
    }

    if (( g.w < c->width_min  ) || ( g.w > c->width_max   ) ||
        ( g.h < c->height_min ) || ( g.h > c->height_max  )) {
        strcpy( S_err_msg, 
                "Resolution beyond hardware or driver capabilities" );
        goto RETURN;
    }

    /*  Direct-video mode specific checks  */
    if ( c->xfer_mode == TV_TRANSFER_DIRECT ) {

        if ( !(x->visual_modes[ x->active_visual ] & TV_TRANSFER_DIRECT) ) {
            strcpy( S_err_msg, 
                    "Active X Visual does not support direct video" );
            goto RETURN;
        }
        if ( x->visual[ x->active_visual ].visualid != 
               x->fb_visual->visualid ) {
            strcpy( S_err_msg, 
                    "Active X Visual is not the default visual" );
            goto RETURN;
        }
        if ( d->win == None ) {
            strcpy( S_err_msg, 
                    "Direct video attempted with no window set" );
            goto RETURN;
        }

        /*  Make sure capture region lies entirely within video memory  */
        /*    (we don't have complex capture clipping support in the    */
        /*    driver yet, but when we do, update this).                 */

        XUTILGetVisualBpp( TVDISPLAY, x->fb_visual, NULL, &Bpp );
        addr = x->base_addr + (g.y * x->pitch + g.x) * Bpp;

        if (( g.x < 0 ) || ( g.y < 0 ) ||
            ( ( g.x+g.w-1 ) >= DisplayWidth ( TVDISPLAY, TVSCREEN ) ) ||
            ( ( g.y+g.h-1 ) >= DisplayHeight( TVDISPLAY, TVSCREEN ) )) {
            strcpy( S_err_msg, 
                    "Direct video region outside bounds of display" );
            goto RETURN;
        }
        else if ( addr + ( (g.h-1) * x->pitch + g.w ) * Bpp
                       >= x->base_addr + x->bank_size ) {
            strcpy( S_err_msg, 
                    "Direct video region outside bounds of display" );
            goto RETURN;
        }
    }

    ok = TRUE;

 RETURN:
    if ( fail_reason != NULL )
        *fail_reason = ok ? NULL : S_err_msg;
    return ok;
}

/**@BEGINFUNC**************************************************************

    Prototype  : void TVCAPTUREStart(
                      TV_CAPTURE *c )

    Purpose    : Initiates capture using the mode/geom/etc. parameters
                 set up previously.  
                 
                 Note:  ALWAYS call TVCAPTUREStart before calling this
                 function.

    Programmer : 08-Mar-97  Randall Hopper

    Parameters : c - I: capture state struct

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

void TVCAPTUREStart( TV_CAPTURE *c )
{
    static TV_BOOL S_done_a_single = False;

    TV_DISPLAY          *d = &G_glob.display;
    TV_XSCREEN          *x = &G_glob.x;
    TV_INT32             larg;
    struct meteor_video  video;
    struct meteor_geomet geom;
    TV_GEOM              g;
    TV_PIXEL_GEOM        pix_geom;
    TV_INT32             Bpp,
                         idx;
    char                *cfg_fail_msg;
    TV_BOOL              audio_mute,
                         flush_buf;

    DRVPRINTF(( "\tCAPTURE Start %s\n",
               c->cap_mode == TV_CAPTURE_CONTINUOUS ? "Continuous":"Single"));

    /*  FIXME:  Also lock out start when single captures in progress  */
    if ( c->contin_on ) {
        fprintf( stderr, "TVCAPTUREStart when already running...ignored\n" );
        return;
    }
    
    /*  Double-check parameters (the caller should have already done this)  */
    if ( !TVCAPTUREConfigure( c, &cfg_fail_msg ) ) {
        fprintf( stderr, 
                 "TVCAPTUREConfigure() in TVCAPTUREStart() failed: %s\n",
                 cfg_fail_msg );
        return;
    }
    
    /*-- Set destination attributes & geometry --*/
    g = c->geom;

    geom.columns = g.w;
    geom.rows    = g.h;
    geom.frames  = 1;
    flush_buf    = FALSE;

    if ( c->xfer_mode == TV_TRANSFER_DIRECT ) {
        TV_BOOL  swap_b, 
                 swap_s;

        XUTILGetVisualBpp( TVDISPLAY, x->fb_visual, NULL, &Bpp );

        video.addr      = x->base_addr + (g.y * x->pitch + g.x) * Bpp;
        /*printf( "\n\tbase + (y * pitch + x) * Bpp = "
                  "0x%x + (%d * %d + %d) * %d\n",
                  x->base_addr, g.y, x->pitch, g.x, Bpp );*/
        video.width     = x->pitch * Bpp;
        video.banksize  = x->bank_size;
        video.ramsize   = x->ram_size / 1024;

        memset( &pix_geom, '\0', sizeof( pix_geom ) );
        pix_geom.type     = TV_PIXELTYPE_RGB;
        pix_geom.Bpp      = Bpp;
        pix_geom.mask[0]  = x->fb_visual->red_mask;
        pix_geom.mask[1]  = x->fb_visual->green_mask;
        pix_geom.mask[2]  = x->fb_visual->blue_mask;

        XUTILGetVisualSwaps( TVDISPLAY, x->fb_visual, &swap_b, &swap_s );
        pix_geom.swap_bytes  = swap_b;
        pix_geom.swap_shorts = swap_s;

        TVCAPTUREGetPixFmtByPixGeom( c, &pix_geom, &idx );
        if ( idx < 0 ) {
            fprintf( stderr, "TVCAPTUREStart: Visual pixel format not direct "
                             "video capable\n" );
            return;
        }
        c->pix_geom_idx = idx;
    }
    else {
        video.addr     = 0,
        video.width    = 0,
        video.banksize = 0,
        video.ramsize  = 0;

        /*  If TDEC is on, may be a while before old trash gets written on.  */
        /*    So tell the driver to flush the frame buffer before starting   */
        /*    capture.                                                       */
        if ( c->fps != c->fps_max )
            flush_buf = TRUE;
    }
    memcpy( &pix_geom, &c->pix_geom_list[ c->pix_geom_idx ], 
            sizeof( pix_geom ) );

    geom.oformat = c->bpp_format;
    if ( geom.rows <= c->height_max / 2 )
        geom.oformat |= METEOR_GEO_ODD_ONLY;

    c->addr = (TV_UINT32) video.addr;

    /*printf( "VIDEO      = { addr 0x%.8x, width %d, banksize %d, ramsize %d }\n",
            video.addr, video.width, video.banksize, video.ramsize );*/
    if ( ioctl( c->fd, METEORSVIDEO, &video ) < 0 ) {
        DO_IOCTL_SERR( "METEORSVIDEO", &video );
        return;
    }
    /*printf( "GEOM       = { %dx%d, frames %d, oformat 0x%.8x }\n",
            geom.columns, geom.rows, geom.frames, geom.oformat );*/
    if ( ioctl( c->fd, METEORSETGEO, &geom ) < 0 ) {
        DO_IOCTL_SERR( "METEORSETGEO", &geom );
        return;
    }
    /*printf( "SACTPIXFMT = { idx %d, Bpp %d, masks { %x,%x,%x }, "
			   "swapb/s %d,%d }\n",
            pix_geom.index, pix_geom.Bpp, pix_geom.mask[0],
	    pix_geom.mask[1], pix_geom.mask[2], pix_geom.swap_bytes,
	    pix_geom.swap_shorts );*/
    if ( ioctl( c->fd, METEORSACTPIXFMT, &pix_geom.index ) < 0 ) {
        DO_IOCTL_SERR( "METEORSACTPIXFMT", &geom );
        return;
    }

    larg = flush_buf;
    if ( ioctl( c->fd, BT848SCBUF, &larg ) < 0 ) {
        DO_IOCTL_SERR( "BT848SCBUF", larg );
        return;
    }

    /*  If user wants to know whenever a/the frame is complete,            */
    /*    add in a signal handler for this for single captures, or an Xt   */
    /*    timer for continuous captures.                                   */
    /*    NOTE:  Done callback makes no sense for direct video transfers.  */
    larg = METEOR_SIG_MODE_MASK;

#ifdef OLD__ALWAYS_USE_SIGNALS_NOW
    if ( c->frame_done_cb && c->frame_cb_enabled )
        if ( c->cap_mode == TV_CAPTURE_SINGLE )
            larg = SIGUSR1;
        else {
            S_frame_timer = XtAppAddTimeOut( TVAPPCTX, 
                                             FRAME_TIMER_DELAY_MS(c->fps_max),
                                             TVCAPTUREFrameTimeoutCB, NULL );
            S_frame_timer_set = True;
        }
#else
    if ( c->frame_done_cb && c->frame_cb_enabled )
        larg = SIGUSR1;
#endif

    if ( ioctl( c->fd, METEORSSIGNAL, &larg ) < 0 ) {
        DO_IOCTL_SERR( "METEORSSIGNAL", larg );
        return;
    }

    /*  Verify audio mute state is in sync (in case it didn't get started  */
    /*    on the initial map).                                             */
    TVAUDIOGetMuteState( &audio_mute );
    TVCAPTURESetAudioMute( c, audio_mute );

    /*  OK, NOW FIRE IT UP  */

    if ( c->cap_mode == TV_CAPTURE_CONTINUOUS ) {

        /*  Queue up a CAP_SINGLE -- turns on audio on Wincast board  */
        /*  FIXME:  We may not need to do this anymore  */
        if ( !S_done_a_single ) {
            larg = METEOR_CAP_SINGLE;
            if ( ioctl( c->fd, METEORCAPTUR, &larg ) < 0 ) {
                DO_IOCTL_SERR( "METEOR_CAP_SINGLE", 0 );
                return;
            }
            S_done_a_single = True;
        }

        /*  FIXME:  Fix spelling of "continuous" in the driver sometime  */
        larg = METEOR_CAP_CONTINOUS;
        if ( ioctl( c->fd, METEORCAPTUR, &larg ) < 0 ) {
            DO_IOCTL_SERR( "METEOR_CAP_CONTINUOUS", 0 );
            return;
        }
        c->contin_on = True;
    }
    else {
        larg = METEOR_CAP_SINGLE;
        if ( ioctl( c->fd, METEORCAPTUR, &larg ) < 0 ) {
            DO_IOCTL_SERR( "METEOR_CAP_SINGLE", 0 );
            return;
        }
        S_done_a_single = True;
    }
}


/**@BEGINFUNC**************************************************************

    Prototype  : void TVCAPTUREStop(
                      TV_CAPTURE *c )

    Purpose    : Stop a continuous capture, if one is running.

    Programmer : 08-Mar-97  Randall Hopper

    Parameters : c - I: capture state struct

    Returns    : None.

    Globals    : None.

 **@ENDFUNC*****************************************************************/

void TVCAPTUREStop( TV_CAPTURE *c )
{
    TV_INT32 larg;

    DRVPRINTF(( "\tCAPTURE Stop\n" ));

    if ( !c->contin_on ) {
        fprintf( stderr, "TVCAPTUREStop called when not running...ignored\n");
        return;
    }

    if ( S_frame_timer_set ) {
        XtRemoveTimeOut( S_frame_timer );
        S_frame_timer_set = False;
    }

    /*  Reset driver's signal so we don't keep getting interrupted  */
    larg = METEOR_SIG_MODE_MASK;
    if ( ioctl( c->fd, METEORSSIGNAL, &larg ) < 0 ) {
        DO_IOCTL_SERR( "METEORSSIGNAL", larg );
        return;
    }

    larg = METEOR_CAP_STOP_CONT;
    if ( ioctl( c->fd, METEORCAPTUR, &larg ) < 0 ) {
        DO_IOCTL_SERR( "METEOR_CAP_STOP_CONT", 0 );
        return;
    }
    c->contin_on = False;


    TVCAPTUREClearPendingFrames();
}


Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199807100726.AAA10706>