/* (C)2002 Charles R. Henrich (henrich@msu.edu) *BSD (hopefully, requires working driver!) BrookTree capture support. Still in (active) development! v1.0 Feb 19 2002 First Release, need to add support for changing audio parameters. */ #include "config.h" #if defined(USE_TV) && defined(HAVE_TV_BSDBT848) #define TRUE (1==1) #define FALSE (1==0) #define PAL_WIDTH 768 #define PAL_HEIGHT 576 #define PAL_FPS 25 #define NTSC_WIDTH 640 #define NTSC_HEIGHT 480 #define NTSC_FPS 30 #include #include #include #include #include #include #include #include #include #include #include #include "../libvo/img_format.h" #include "tv.h" /* information about this file */ static tvi_info_t info = { "Brooktree848 Support", "bt848", "Charles Henrich", "in development" }; /* private data's */ typedef struct { /* Audio */ char *dspdev; int dspready; int dspfd; int dspsamplesize; int dspstereo; int dspspeed; int dspfmt; int dspframesize; /* Video */ char *btdev; int videoready; int btfd; int source; int maxfps; int fps; int iformat; int maxheight; int maxwidth; struct meteor_geomet geom; struct meteor_capframe capframe; int buffersize; unsigned char *buffer; int currentframe; /* Inputs */ int input; /* Tuner */ char *tunerdev; int tunerfd; int tunerready; u_long tunerfreq; struct bktr_chnlset cset; } priv_t; #include "tvi_def.h" static priv_t *G_private=NULL; static void processframe(int signal) { G_private->currentframe++; return; } /* handler creator - entry point ! */ tvi_handle_t *tvi_init_bsdbt848(char *device) { return(new_handle()); } static int control(priv_t *priv, int cmd, void *arg) { switch(cmd) { /* Tuner Controls */ case TVI_CONTROL_IS_TUNER: if(priv->tunerready == FALSE) return TVI_CONTROL_FALSE; return(TVI_CONTROL_TRUE); case TVI_CONTROL_TUN_GET_FREQ: { if(ioctl(priv->tunerfd, TVTUNER_GETFREQ, &priv->tunerfreq) < 0) { perror("GETFREQ:ioctl"); return(TVI_CONTROL_FALSE); } (int)*(void **)arg = priv->tunerfreq; return(TVI_CONTROL_TRUE); } case TVI_CONTROL_TUN_SET_FREQ: { priv->tunerfreq = (int)*(void **)arg; if(ioctl(priv->tunerfd, TVTUNER_SETFREQ, &priv->tunerfreq) < 0) { perror("SETFREQ:ioctl"); return(0); } return(TVI_CONTROL_TRUE); } case TVI_CONTROL_TUN_GET_TUNER: case TVI_CONTROL_TUN_SET_TUNER: /* Inputs */ case TVI_CONTROL_SPC_GET_INPUT: { if(ioctl(priv->btfd, METEORGINPUT, &priv->input) < 0) { perror("GINPUT:ioctl"); return(TVI_CONTROL_FALSE); } (int)*(void **)arg = priv->input; return(TVI_CONTROL_TRUE); } case TVI_CONTROL_SPC_SET_INPUT: { priv->input = getinput((int)*(void **)arg); if(ioctl(priv->btfd, METEORSINPUT, &priv->input) < 0) { perror("tunerfreq:ioctl"); return(0); } return(TVI_CONTROL_TRUE); } /* Audio Controls */ case TVI_CONTROL_IS_AUDIO: if(priv->dspready == FALSE) return TVI_CONTROL_FALSE; return(TVI_CONTROL_TRUE); case TVI_CONTROL_AUD_GET_FORMAT: { (int)*(void **)arg = AFMT_S16_LE; return(TVI_CONTROL_TRUE); } case TVI_CONTROL_AUD_GET_CHANNELS: { (int)*(void **)arg = 2; return(TVI_CONTROL_TRUE); } case TVI_CONTROL_AUD_GET_SAMPLERATE: { (int)*(void **)arg = 44100; return(TVI_CONTROL_TRUE); } case TVI_CONTROL_AUD_GET_SAMPLESIZE: { (int)*(void **)arg = priv->dspsamplesize; return(TVI_CONTROL_TRUE); } /* Video Controls */ case TVI_CONTROL_IS_VIDEO: if(priv->videoready == FALSE) return TVI_CONTROL_FALSE; return(TVI_CONTROL_TRUE); case TVI_CONTROL_TUN_SET_NORM: { int req_mode = (int)*(void **)arg; priv->iformat = METEOR_FMT_AUTOMODE; if(req_mode == TV_NORM_PAL) { priv->iformat = METEOR_FMT_PAL; priv->maxheight = PAL_HEIGHT; priv->maxwidth = PAL_WIDTH; priv->maxfps = PAL_FPS; priv->fps = PAL_FPS; if(priv->fps > priv->maxfps) priv->fps = priv->maxfps; if(priv->geom.rows > priv->maxheight) { priv->geom.rows = priv->maxheight; } if(priv->geom.columns > priv->maxwidth) { priv->geom.columns = priv->maxwidth; } } if(req_mode == TV_NORM_NTSC) { priv->iformat = METEOR_FMT_NTSC; priv->maxheight = NTSC_HEIGHT; priv->maxwidth = NTSC_WIDTH; priv->maxfps = NTSC_FPS; priv->fps = NTSC_FPS; priv->dspframesize = priv->dspspeed*priv->dspsamplesize/8/ priv->fps * (priv->dspstereo+1); if(priv->fps > priv->maxfps) priv->fps = priv->maxfps; if(priv->geom.rows > priv->maxheight) { priv->geom.rows = priv->maxheight; } if(priv->geom.columns > priv->maxwidth) { priv->geom.columns = priv->maxwidth; } } if(req_mode == TV_NORM_SECAM) priv->iformat = METEOR_FMT_SECAM; if(ioctl(priv->btfd, METEORSFMT, &priv->iformat) < 0) { perror("format:ioctl"); return(TVI_CONTROL_FALSE); } if(ioctl(priv->btfd, METEORSETGEO, &priv->geom) < 0) { perror("geo:ioctl"); return(0); } if(ioctl(priv->btfd, METEORSFPS, &priv->fps) < 0) { perror("fps:ioctl"); return(0); } return(TVI_CONTROL_TRUE); } case TVI_CONTROL_VID_GET_FORMAT: (int)*(void **)arg = IMGFMT_UYVY; return(TVI_CONTROL_TRUE); case TVI_CONTROL_VID_SET_FORMAT: { int req_fmt = (int)*(void **)arg; if(req_fmt != IMGFMT_UYVY) return(TVI_CONTROL_FALSE); return(TVI_CONTROL_TRUE); } case TVI_CONTROL_VID_SET_WIDTH: priv->geom.columns = (int)*(void **)arg; if(priv->geom.columns > priv->maxwidth) { priv->geom.columns = priv->maxwidth; } if(ioctl(priv->btfd, METEORSETGEO, &priv->geom) < 0) { perror("width:ioctl"); return(0); } return(TVI_CONTROL_TRUE); case TVI_CONTROL_VID_GET_WIDTH: (int)*(void **)arg = priv->geom.columns; return(TVI_CONTROL_TRUE); case TVI_CONTROL_VID_SET_HEIGHT: priv->geom.rows = (int)*(void **)arg; if(priv->geom.rows > priv->maxheight) { priv->geom.rows = priv->maxheight; } if(priv->geom.rows <= priv->maxheight / 2) { priv->geom.oformat |= METEOR_GEO_EVEN_ONLY; } if(ioctl(priv->btfd, METEORSETGEO, &priv->geom) < 0) { perror("height:ioctl"); return(0); } return(TVI_CONTROL_TRUE); case TVI_CONTROL_VID_GET_HEIGHT: (int)*(void **)arg = priv->geom.rows; return(TVI_CONTROL_TRUE); case TVI_CONTROL_VID_GET_FPS: (int)*(void **)arg = (int)priv->fps; return(TVI_CONTROL_TRUE); /* case TVI_CONTROL_VID_SET_FPS: priv->fps = (int)*(void **)arg; if(priv->fps > priv->maxfps) priv->fps = priv->maxfps; if(ioctl(priv->btfd, METEORSFPS, &priv->fps) < 0) { perror("fps:ioctl"); return(0); } return(TVI_CONTROL_TRUE); */ case TVI_CONTROL_VID_CHK_WIDTH: case TVI_CONTROL_VID_CHK_HEIGHT: return(TVI_CONTROL_TRUE); } return(TVI_CONTROL_UNKNOWN); } static int init(priv_t *priv) { int marg; G_private = priv; /* Oooh, sick */ /* Video Configuration */ priv->videoready = TRUE; priv->btdev = strdup("/dev/bktr0"); priv->iformat = METEOR_FMT_PAL; priv->maxheight = PAL_HEIGHT; priv->maxwidth = PAL_WIDTH; priv->maxfps = PAL_FPS; priv->source = METEOR_INPUT_DEV0; priv->fps = priv->maxfps; priv->currentframe=0; priv->geom.columns = priv->maxwidth; priv->geom.rows = priv->maxheight; priv->geom.frames = 1; priv->geom.oformat = METEOR_GEO_YUV_PACKED; priv->btfd = open(priv->btdev, O_RDONLY); if(priv->btfd < 0) { perror("bktr open"); priv->videoready = FALSE; } if(priv->videoready == TRUE && ioctl(priv->btfd, METEORSFMT, &priv->iformat) < 0) { perror("FMT:ioctl"); } if(priv->videoready == TRUE && ioctl(priv->btfd, METEORSINPUT, &priv->source) < 0) { perror("SINPUT:ioctl"); } if(priv->videoready == TRUE && ioctl(priv->btfd, METEORSFPS, &priv->fps) < 0) { perror("SFPS:ioctl"); } if(priv->videoready == TRUE && ioctl(priv->btfd, METEORSETGEO, &priv->geom) < 0) { perror("SGEO:ioctl"); } if(priv->videoready == TRUE) { priv->buffersize = (priv->geom.columns * priv->geom.rows * 2); priv->buffer = (u_char *)mmap((caddr_t)0, priv->buffersize, PROT_READ, MAP_SHARED, priv->btfd, (off_t)0); if(priv->buffer == (u_char *) MAP_FAILED) { perror("mmap"); priv->videoready = FALSE; } } /* Tuner Configuration */ priv->tunerdev = strdup("/dev/tuner0"); priv->tunerready = TRUE; priv->tunerfd = open(priv->tunerdev, O_RDONLY); if(priv->tunerfd < 0) { perror("tune open"); priv->tunerready = FALSE; } /* Audio Configuration */ priv->dspready = TRUE; priv->dspdev = strdup("/dev/dsp"); priv->dspsamplesize = 16; priv->dspstereo = 1; priv->dspspeed = 44100; priv->dspfmt = AFMT_S16_LE; priv->dspframesize = priv->dspspeed*priv->dspsamplesize/8/priv->fps * (priv->dspstereo+1); if((priv->dspfd = open ("/dev/dsp", O_RDWR, 0)) < 0) { perror("/dev/dsp open"); priv->dspready = FALSE; } marg = (256 << 16) | 13; if (ioctl(priv->dspfd, SNDCTL_DSP_SETFRAGMENT, &marg ) < 0 ) { perror("setfrag"); priv->dspready = FALSE; } if((priv->dspready == TRUE) && (ioctl(priv->dspfd, SNDCTL_DSP_SAMPLESIZE, &priv->dspsamplesize) == -1) || (ioctl(priv->dspfd, SNDCTL_DSP_STEREO, &priv->dspstereo) == -1) || (ioctl(priv->dspfd, SNDCTL_DSP_SPEED, &priv->dspspeed) == -1) || (ioctl(priv->dspfd, SNDCTL_DSP_SETFMT, &priv->dspfmt) == -1)) { perror ("configuration of /dev/dsp failed"); close(priv->dspfd); priv->dspready = FALSE; } return(1); } /* that's the real start, we'got the format parameters (checked with control) */ static int start(priv_t *priv) { int marg; if(priv->videoready == FALSE) return(0); signal(SIGUSR1, processframe); signal(SIGALRM, processframe); marg = SIGUSR1; if(ioctl(priv->btfd, METEORSSIGNAL, &marg) < 0) { perror("METEORSSIGNAL failed"); return(0); } marg = METEOR_CAP_CONTINOUS; if(ioctl(priv->btfd, METEORCAPTUR, &marg) < 0) { perror("METEORCAPTUR failed"); return(0); } return(1); } static int uninit(priv_t *priv) { int marg; if(priv->videoready == FALSE) return(0); marg = METEOR_SIG_MODE_MASK; if(ioctl( priv->btfd, METEORSSIGNAL, &marg) < 0 ) { perror("METEORSSIGNAL"); return(0); } marg = METEOR_CAP_STOP_CONT; if(ioctl(priv->btfd, METEORCAPTUR, &marg) < 0 ) { perror("METEORCAPTUR STOP"); return(0); } close(priv->btfd); close(priv->dspfd); priv->dspfd = -1; priv->btfd = -1; priv->dspready = priv->videoready = FALSE; return(1); } static int grab_video_frame(priv_t *priv, char *buffer, int len) { sigset_t sa_mask; if(priv->videoready == FALSE) return(0); alarm(1); sigfillset(&sa_mask); sigdelset(&sa_mask,SIGINT); sigdelset(&sa_mask,SIGUSR1); sigdelset(&sa_mask,SIGALRM); sigsuspend(&sa_mask); alarm(0); memcpy(buffer, priv->buffer, len); return(priv->currentframe); } static int get_video_framesize(priv_t *priv) { return(priv->geom.columns*priv->geom.rows*16/8); } static int grab_audio_frame(priv_t *priv, char *buffer, int len) { struct audio_buf_info abi; int bytesread; int ret; if(priv->dspready == FALSE) return 0; /* Get exactly one frame of audio, which forces video sync to audio.. */ bytesread=read(priv->dspfd, buffer, len); while(bytesread < len) { ret=read(priv->dspfd, &buffer[bytesread], len-bytesread); if(ret == -1) { perror("Audio read failed!"); return 0; } bytesread+=ret; } if(ioctl(priv->dspfd, SNDCTL_DSP_GETISPACE, &abi) < 0) { perror("abi:ioctl"); return(TVI_CONTROL_FALSE); } return(abi.bytes/len); } static int get_audio_framesize(priv_t *priv) { if(priv->dspready == FALSE) return 0; return(priv->dspframesize); } static int getinput(int innumber) { switch(innumber) { case 0: return METEOR_INPUT_DEV0; /* RCA */ case 1: return METEOR_INPUT_DEV1; /* Tuner */ case 2: return METEOR_INPUT_DEV2; /* In 1 */ case 3: return METEOR_INPUT_DEV3; /* In 2 */ case 4: return METEOR_INPUT_DEV_RGB; /* RGB */ case 5: return METEOR_INPUT_DEV_SVIDEO; /* SVid */ } return 0; } #endif /* USE_TV */