/* * This Software is the original work of Daniel TUNG * This Software is submitted in partial fulfillment of the * requirements for the degree of BCSE. * Dept. Computer Science, Monash University 2000 * * Copyright (c) 2000 beetung@cs.monash.edu.au * beetung@geocities.com */ #ifndef CCC_BTTV_H #define CCC_BTTV_H #include #include #include extern "C" { #include #include #include #include #include #include #include }; #include "fastbuf.h" #include "xwin.h" #include "timer.h" #include "thread.h" #include "semaphore.h" //// //// //// ///////////////////////////////////////// //class BttvCapture { struct BttvCapture : public BaseSema { ///////////////////////////////////////// #define SBS (527250) // freq for SBS (channel 28) #define CCC (541250) // freq for channel 30 public: // our bttv card supports only 2 buffers Fastbuf framebuf[2]; // frame buffer for memcopy() static int fd ; // file descriptor for /dev/video0 static int frameCurr; // currentFrame ID private: struct video_capability vCap; // V4L stuff struct video_window vWin; // struct video_picture vPic; // struct video_tuner vTun; // struct video_channel *vCha; // static struct video_mbuf vMbuf; // static struct video_mmap vMmap[3]; // unsigned long freq; char *map ; // mm allocated buffer pointer bool mmBufReady; // is mm buffer allocated already? int grab,sync; enum { DISABLE = 0, ENABLE =1 }; operator Fastbuf (void) { return GetCurrentFrame() ; } public: //---------------------- bool isOpen (void) { return fd > -1; } //---------------------- inline Fastbuf& GetCurrentFrame(void) { return framebuf[frameCurr]; } //---------------------- public: //---------------------- BttvCapture (MySemaphore& aSem, string devID = "/dev/video0" ) : BaseSema(aSem), vCha(0), map(0), mmBufReady(false),grab(0),sync(0) { //---------------------- fd =open(devID.c_str(), O_RDWR); // Dont use O_RDONLY !!! frameCurr = 0 ; if (fd <0 ) { cout << "ERROR : bttv fail" << endl ; return; //XXXXX // exit(-1); } else { } /// /// Query device for capability /// if (ioctl(fd,VIDIOCGCAP, &vCap) < 0) { cout << "ERROR : VIDIOGCAP error, not a v4l device?" << endl; exit(-1); } else { ShowCapType(); } /// /// Query Input channel (Number of Jack-ins) /// vCha = new struct video_channel[vCap.channels]; cout << "Capability Input Channels " << endl; for ( int i=0; i!= vCap.channels; i++) { // change channel and get info // vCha[i].channel = i; if (ioctl(fd,VIDIOCSCHAN, &vCha[i]) < 0) { cout << "ERROR : VIDIOCSCHAN - cannot change input " << "channel to " << i << endl; exit(-1); } // get info about channel // if (ioctl(fd,VIDIOCGCHAN, &vCha[i]) < 0) { cout << "ERROR : VIDIOCGCHAN "; exit(-1); } ShowChannels(i); } // for (i != vCap.channels); // switch back to channel 0, <<>> // if (ioctl(fd,VIDIOCSCHAN, &vCha[0]) < 0) { cout << "ERROR : VIDIOCSCHAN - cannot switch to TV-tuner"; exit(-1); } else cout << "switching back to Television " << endl; /// /// Query Capture Image properties /// if (ioctl(fd,VIDIOCGPICT, &vPic) < 0) { cout << "ERROR : VIDIOCGPICT error" << endl; exit(-1); } /// /// Only Can handling MonoChrome? /// if (vCap.type & VID_TYPE_MONOCHROME ) { cout << "ERROR : Device only capable of handling MonoChrome images." << endl << " We have only implemented color handling routines!" << endl; exit(-1); } else { vPic.depth=24; vPic.palette = VIDEO_PALETTE_RGB32; // we only implemented RBG 32 if (ioctl(fd,VIDIOCSPICT, &vPic) < 0) { cout << "WARNING : Device not supporting VIDEO_PALETTE_RGB24" << endl; exit(-1); } else cout << "Palette set to RGB32 " << endl; } /// /// Query Capture window info /// if (ioctl(fd,VIDIOCGWIN, &vWin) < 0) { cout << "ERROR : VIDIOCGWIN error" << endl; exit(-1); } else { ShowCapWin(); /* vWin.width =384; vWin.height =288; if (ioctl(fd,VIDIOCSWIN, &vWin) < 0) { cout << "ERROR : VIDIOCSWIN - setting window size" << endl; exit(-1); } else cout << " Win size set to 384x288 " << endl; */ } /// /// Setting Tuner to CCC channel (AUSTRALIA - channel 30) /// vTun.tuner= vTun.flags = vTun.signal = vTun.mode = 0xFF; if (vCap.type & VID_TYPE_TUNER ) { if ( ioctl(fd,VIDIOCGTUNER, &vTun ) < 0) { #define BTTV_VERSION _IOR('v',BASE_VIDIOCPRIVATE+6, int) //------------------------------------------- // Using the bttv hack as addressed in xawtv! //------------------------------------------- if ( ioctl(fd,BTTV_VERSION, &vTun ) < 0) { cout << "ERROR : VIDIOCGTUNER and BTTV_VERSION both failed"; exit(-1); } else cout << " ...Using VIDIOCGTUNER BTTV_VERSION OK!!!" << endl; } else cout << " tuner : " << vTun.tuner << endl << " name : " << vTun.name << endl << " rangeL : " << vTun.rangelow << endl << " rangeH : " << vTun.rangehigh << endl << " flags : " << vTun.flags << endl << " mode : " << vTun.mode << endl << " signal : " << vTun.signal << endl; /// /// Setting freq to SBS /// // freq = CCC*static_cast(16*0.001) ; // freq = SBS*static_cast(16*0.001) ; // freq = static_cast((double)SBS*(double)16*0.001) ; freq = static_cast((double)CCC*(double)16*0.001) ; cout << "current Freq : "<< (unsigned long)freq << "(SBS)"<sync) mmWait(); if ( map != 0) { munmap(map,vMbuf.size); mmBufReady = false; // necessary at all? its dtor here ! } close(fd); fd = -1; } // change input feed to Composite1 // //---------------------- void SwitchToComposite1() { //---------------------- // switch to channel 1, <<>> // if (ioctl(fd,VIDIOCSCHAN, &vCha[1]) < 0) { cout << "ERROR : VIDIOCSCHAN - cannot switch to TV-tuner"; exit(-1); } else cout << "switching to Composite1" << endl; } // disable by default param // //---------------------- bool setOverlayMode(int itmp =DISABLE) { //---------------------- if (ioctl(fd,VIDIOCCAPTURE, itmp) < 0 ) { cout << "ERROR : VIDIOCCAPTURE " << endl; return false; } else return true; } //---------------------- void OpenDevice() {;} void CloseDevice() {;} //---------------------- void* run(void) { StartCapture(); return 0;} //---------------------- void mmSetParams(int width =384, int height =288) { //---------------------- // setOverlayMode( DISABLE ); if (ioctl (fd, VIDIOCGMBUF, &vMbuf) < 0) { cerr << "WARNING : bttv device is not mmap capable! not implemented yet!" << endl; return ; exit(-1); } cout << "size : " << vMbuf.size << endl; cout << "frames : " << vMbuf.frames << endl; cout << "offsets : " << vMbuf.offsets << endl; /// /// set associated video_mbuf correctly with frameID /// for (int i=0; i!= vMbuf.frames; i++) { vMmap[i].format=vPic.palette; vMmap[i].frame=i; // this is the frame ID!!!! vMmap[i].width=width; vMmap[i].height=height; } } //mmSetParams(...) //// //// Set the mmap(...) buffer space and return a pointer to it //// //---------------------- bool mmGetBuffer(char *& img) { //---------------------- if (mmBufReady) { // done it already? img=map + ((frameCurr)*(vMbuf.size/vMbuf.frames)); return true; } void *vptr; // // mmap allocates a bufferSize of .size and attach it to a filedescr (fd) // vptr = mmap(0, vMbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); map = reinterpret_cast(vptr); //// // // Successfully mapped? // if (map) mmBufReady = true; else { cerr << "ERROR : Capture error, inside BttvCapture::mmGetBuffer(..)," << " mmap(...) not working!"; exit(1); } img = map; return true; } //// //// This fn grab 1 frame from this bttv device and returns a pointer //// to the XShm buffer, which also holds "a copy" of the captured //// frame image //// //---------------------- Fastbuf* mmGrab1Frame(void) { //---------------------- if (fd < 0) { SemPost(); // so our thread can exit if required... return &framebuf[0]; } char *image_new =0; int frameID; mmGetBuffer(image_new); // cout << " Obtaining 1 frame only using mmGrab1Frame() ..." << endl; // // start timer for profiling // Timer tp; /// ///----------------------------------- /// time for 1 frame ///----------------------------------- tp.start(); frameID = GetFrame(); // grab 1 frame to buffer[0/1] tp.stop(); // cout << " GetFrame() : " << tp << endl; framebuf[frameID].mXCopy( image_new , (vMbuf.size/vMbuf.frames)>>2); ///----------------------------------- /// signal 1 frame ready ///----------------------------------- SemPost(); // increment semaphore // cout << " GetFrame() 24 frames would take : " << ((double)tp * 24.0) << endl; return &framebuf[frameID]; } /// /// Start Capture engine here! /// //---------------------- void StartCapture(void) { mmCapture(); } //---------------------- void readCapture(void) { ; /* capture using read() Not implemented yet */ } /// /// get 1 frame from bttv /// //---------------------- inline int GetFrame() { //---------------------- while (grab - sync < 2) // for streaming.... mmQueue(); // read 2 frames ahead frameCurr = mmWait(); // sync it if (frameCurr == -1) return 0; else return frameCurr; } //---------------------- inline void mmQueue(void) { //---------------------- // int frame = sync++ % bufCount; int itmp; int frame = grab++ % 2; if ((itmp =ioctl(fd, VIDIOCMCAPTURE, vMmap +frame)) == -1) { // read from it if (itmp ==EAGAIN) cerr << " ERROR : bttv can't sync " << endl; else cerr << " ERROR : VIDIOCMCAPTURE indicated an error! " << endl; return ; } } /// /// Sync the buffer /// //---------------------- inline int mmWait(void) { //---------------------- // int frame = sync++ % bufCount; int itmp; int frame = sync++ % 2; do { if ( (itmp=ioctl(fd, VIDIOCSYNC, vMmap+frame)) == -1) // accept and sync if (itmp !=EAGAIN ) { // if not EAGAIN, an error; perror("ERROR : ioctl VIDIOCSYNC"); return -1; } } while (itmp ==EAGAIN ) ; // if EAGAIN, do again! return frame; } //---------------------- void mmCapture(void) { //---------------------- char *image_new =0; XImage *xim; mmGetBuffer(image_new); if (!image_new) { perror("Capture error"); exit(1); } cout << " Starting video streaming loop ..." << endl; // xThread(); // listen to x.serveEvent() thread now // int *iptr, frame =0; int i =1; // // start timer for profiling // Timer t; t.start(); Timer tp; for(;;){ /// ///----------------------------------- /// time for 1 frame ///----------------------------------- tp.start(); GetFrameFast(); // grab 1 frame to buffer[0/1] tp.stop(); cout << " GetFrame() : " << tp << endl; /// ///----------------------------------- /// time for 24 frames ///----------------------------------- if (!(i%24)) { t.stop(); cout << " timer 24 frames: " << t << endl; i=0; t.start(); } ///----------------------------------- /// time for draw 1 frame to screen ///----------------------------------- tp.start(); framebuf[frameCurr].mXCopy( image_new , vMbuf.size/vMbuf.frames/4); // (32bits == > /4) xim =framebuf[frameCurr]; // x.drawXShm(xim); tp.stop(); cout << " drawXShm() : " << tp << endl; ///----------------------------------- /// signal 1 frame ready ///----------------------------------- SemPost(); // increment semaphore i++; frameCurr ++; frameCurr %= 2; // x.wait4Click(); // if (!x) return; // if Xwin is dead, let disappear together. } // while(next_capture()) } // StartCapture(); /// /// Try to speed up grabbing operation to the buffer /// //---------------------- int GetFrameFast(void) { //---------------------- int itmp; Timer tp; Timer tpcap; Timer tpsync; tp.start(); tpcap.start(); /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: grab++; frameCurr = grab++ % 2; if ((itmp =ioctl(fd, VIDIOCMCAPTURE, vMmap +frameCurr)) == -1) { // read from it if (itmp ==EAGAIN) cerr << " ERROR : bttv can't sync " << endl; else cerr << " ERROR : VIDIOCMCAPTURE indicated an error! " << endl; goto GetFrameFast_Error; } /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// OK UP TO HERE.... /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: tpcap.stop(); cout << " ::::::::cap() : " << tpcap << endl; tpsync.start(); /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: sync++; frameCurr = sync++ % 2; retry: if ( ioctl(fd, VIDIOCSYNC, vMmap+frameCurr) == -1) { // kernal sync buffer if (errno !=EINTR ) // if not EAGAIN, an error; cerr << " ERROR : VIDIOCSYNC indicated an error! " << endl; else goto retry; } /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: /// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: tpsync.stop(); cout << " :::::: ::::::::::::::::::::::: :::::: Sync() : " << tpsync << endl; tp.stop(); cout << " ...........Inside GetFrameFast() : " << tp << endl; return 1; GetFrameFast_Error: return -1; } //---------------------- void perror(string msg) { //---------------------- cout << msg << endl; } //---------------------- void ShowCapType() { //---------------------- cout << "Capability " << endl; cout << "name[32] : " << vCap.name << endl << "channels : " << vCap.channels << endl << "type : " ; if (vCap.type & VID_TYPE_CAPTURE) cout << " CAPTURE 1"; if (vCap.type & VID_TYPE_TUNER) cout << " TUNER 2"; if (vCap.type & VID_TYPE_TELETEXT) cout << " TELETEXT 3" ; if (vCap.type & VID_TYPE_OVERLAY) cout << " OVERLAY 4"; if (vCap.type & VID_TYPE_CHROMAKEY) cout << " CHROMAKEY 5"; if (vCap.type & VID_TYPE_CLIPPING) cout << " CLIPPING 6"; if (vCap.type & VID_TYPE_FRAMERAM) cout << " FRAMERAM 7"; if (vCap.type & VID_TYPE_SCALES) cout << " SCALES 8"; if (vCap.type & VID_TYPE_MONOCHROME) cout << " MONOCHROME 9"; if (vCap.type & VID_TYPE_SUBCAPTURE) cout << " SUBCAPTURE 10"; cout << endl; cout << "audios : " << vCap.audios << endl << "x,y/x,y : " << vCap.maxwidth <<"x" << vCap.maxheight <<" / " << vCap.minwidth << "x" << vCap.minheight <<" " << endl; } //---------------------- void ShowCapWin() { //---------------------- cout << "Capability Window Area " << endl; cout << " x : " << vWin.x << endl << " y : " << vWin.y << endl << " chromakey: " << vWin.chromakey << endl << " width : " << vWin.width << endl << " height : " << vWin.height << endl << " flags : " << vWin.flags << endl; cout << endl; } //---------------------- void ShowChannels(int i) { //---------------------- cout << " channel : " << vCha[i].channel << " " << vCha[i].name << "\ttuners(" << vCha[i].tuners << ") " << " flags(" << vCha[i].flags << ") " << " type(" << vCha[i].type <<") " << " norm(" << vCha[i].norm << ") " << endl; } //---------------------- operator bool (void) { return fd; } //---------------------- protected: }; #endif