#include "config.h" #include #include "caviplay.h" #include "zoomer.h" #include "cfgvalues.h" #include #include #include #include #include #include #define TRK_DISPLAY #include "ctrackbar.h" #define _zabs(a) (((a)>=0)?(a):(-(a))) #define PLAYER_CAPTION "Movie window" #define RESIZE_LATENCY 12 #define YUVSPACE fccYUY2 #define REFRESH if (!needResize) needResize=1; extern "C" { gint ctrlsocket_get_session_id(void); /* FIXME */ }; extern int cfgRefresh; int sf1time=0; int doing_output; #if !HAVE_UNSETENV static void unsetenv(const char *name) { extern char **environ; char **ep; int len = strlen(name); for (ep = environ; *ep; ep++) { if (strncmp(*ep, name, len) == 0 && (*ep)[len] == '=') { while ((ep[0] = ep[1]) != NULL) ep++; break; } } } #endif class cPlayerData { public: SDL_Surface *dest; SDL_Overlay *yuv; IAviPlayer *avi; int bpp; //bitdepth int w,h,rw,rh; //width and height of SDL surface, and real W/H int do_yuv; double len; SDL_Rect rect; //output rect SDL_mutex *gui,*sweep; SDL_Thread *thread; cPlayerData():dest(0),yuv(0),avi(0),bpp(24),gui(0),thread(0) {} }; cPlayer *current=NULL; static CImage *prevImg=NULL; static int systemBPP=16,systemW=640,systemH=480; /* FIXME */ avmode_t *avmodes=NULL; int avmodes_num=0; #define ddof(a) (*((cPlayerData *)((a).pvtData))) static int ms_shown; static int show_mouse() { return ms_shown; } static void show_mouse(int v) { if (v!=ms_shown) SDL_ShowCursor(v); ms_shown=v; } static int add_avmode(int _w,int _h,int _bpp) { avmode_t *avm=new avmode_t(); avm->w=_w; avm->h=_h; avm->bpp=_bpp; avm->next=avmodes; avmodes=avm; avmodes_num++; return 1; } static int destroy_avmodes() { avmodes_num=0; while(avmodes) { avmode_t *tmp=avmodes->next; delete avmodes; avmodes=tmp; } } static char *sweepBuffer=NULL; static long sweepBufferSize=0; static long sweepSize=0; int needResize=-1; int sweepUpdated=0; static int countFrames=0; static int barResult=0; extern int keyHandler(const SDL_Event &event); int toggle_dsize() { int midx=(avicfg.doSizePX+avicfg.doSizePY)/2; switch(avicfg.typeDS) { case DS_100_200: if (midx<=150000) { avicfg.doSizePX=200000; avicfg.doSizePY=200000; }else { avicfg.doSizePX=100000; avicfg.doSizePY=100000; } break; case DS_CURRENTSZ: if (midx<=150000) { avicfg.doSizePX*=2; avicfg.doSizePY*=2; }else { avicfg.doSizePX/=2; avicfg.doSizePY/=2; } break; case DS_50_100: if (midx<=75000) { avicfg.doSizePX=100000; avicfg.doSizePY=100000; }else { avicfg.doSizePX=50000; avicfg.doSizePY=50000; } break; case DS_DISABLE: // do nothing break; }; REFRESH countFrames=RESIZE_LATENCY; cfgStore(); cfgRefresh=1; return 1; } static int parseBarResult(int res) { if (!res) return 0; switch(res) { case AX_PLAY_PAUSE: xmms_remote_pause(ctrlsocket_get_session_id()); break; case AX_TRACK: trk_newpos(trk_manualpos()); xmms_remote_jump_to_time(ctrlsocket_get_session_id(), (long)(trk_manualpos()*current->len())); break; case AX_PREV: xmms_remote_playlist_prev(ctrlsocket_get_session_id()); break; case AX_NEXT: xmms_remote_playlist_next(ctrlsocket_get_session_id()); break; case AX_HIDE: avicfg.doFullscreen=!avicfg.doFullscreen; needResize=1; countFrames=RESIZE_LATENCY; break; } return 1; } int cb_eventfilter(const SDL_Event *event) { if ((event->type==SDL_VIDEORESIZE)&&(avicfg.doEnableResize)&&(!avicfg.doFullscreen)) { if (current) { if ((event->resize.wresize.hresize.w/ddof(*current).rw; avicfg.doSizePY=100000*event->resize.h/ddof(*current).rh; if (avicfg.doAspect) { switch(avicfg.typeAR) { case AR_MOST: if ( _zabs(event->resize.w - ddof(*current).w) > _zabs(event->resize.h - ddof(*current).h) ) avicfg.doSizePY=avicfg.doSizePX; else avicfg.doSizePX=avicfg.doSizePY; break; case AR_AVERAGE: // do nothing - it's in video_thread break; case AR_WIDTH: avicfg.doSizePY=avicfg.doSizePX; break; case AR_HEIGHT: avicfg.doSizePX=avicfg.doSizePY; break; }; } needResize=1; countFrames=RESIZE_LATENCY; } } }else if (event->type==SDL_MOUSEBUTTONUP) { if (parseBarResult(barResult)) { barResult=0; return 0; } if (event->button.button==SDL_BUTTON_MIDDLE) { toggle_dsize(); }else if (event->button.button==SDL_BUTTON_RIGHT) { avicfg.doFullscreen=!avicfg.doFullscreen; needResize=1; countFrames=RESIZE_LATENCY; }else if (event->button.button==SDL_BUTTON_LEFT) { int xx,yy; SDL_GetMouseState(&xx,&yy); switch(avicfg.typeLMB) { case LMB_DISABLE: break; case LMB_FULLSCREEN: if (!avicfg.doFullscreen) break; case LMB_ALWAYS: xmms_remote_pause(ctrlsocket_get_session_id()); break; }; } }else if (event->type==SDL_QUIT) { xmms_remote_stop(ctrlsocket_get_session_id()); }else if (event->type==SDL_KEYUP) { keyHandler(*event); }else if (event->type==SDL_MOUSEMOTION) { if ( (event->motion.x==(ddof(*current).dest->w-1)) &&(event->motion.y==(ddof(*current).dest->h-1))) { if (show_mouse()) show_mouse(0); }else { if (!show_mouse()) show_mouse(1); } } return 0; } static int thread_video(void *_data) { cPlayer *current=(cPlayer *)_data; SDL_Event event; if (current) current->hwtype(avicfg.doYUV); #define dd ddof(*current) if(SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD)<0) return 0; SDL_mutexP(dd.gui); reApply: if (avicfg.doAspect) { avicfg.doSizePX = (avicfg.doSizePX+avicfg.doSizePY)/2; avicfg.doSizePY = avicfg.doSizePX; } cfgStore(); cfgRefresh=1; if (avicfg.doFullscreen) { if ((avicfg.typeFSR==FSR_DESKTOP)||(!avicfg.doFullscreenMode)) { do_set_desktop: dd.w=systemW; dd.h=systemH; }else if (avicfg.typeFSR==FSR_SPECIFY) { dd.w=avicfg.fsW; dd.h=avicfg.fsH; }else { avmode_t *z=avmodes,*f=avmodes; while(z) { if ( (f->w+f->h) > (z->w+z->h)) f=z; z=z->next; } if (f) { dd.w=f->w; dd.h=f->h; }else goto do_set_desktop; } }else { dd.w=dd.rw*avicfg.doSizePX/100000; dd.h=dd.rh*avicfg.doSizePY/100000; } if ((avicfg.doFullscreen)&&(avicfg.doFullscreenAspect)&&(dd.rw)&&(dd.rh)) { double coeff1=0.75*dd.rw/dd.rh; coeff1=((double)(long)(1000*coeff1))/1000; if (coeff1==1.0) ;else if (coeff1>1) { dd.h=(int)(dd.h/coeff1); }else { dd.w=(int)(dd.w*coeff1); } } dd.w&=(~1); dd.dest = SDL_SetVideoMode ( dd.w, dd.h, dd.bpp, 0 |(avicfg.doFullscreen?SDL_FULLSCREEN:0) |(avicfg.doEnableResize?SDL_RESIZABLE:0) |SDL_ASYNCBLIT ); SDL_ShowCursor(0); ms_shown=0; if (avicfg.doFullscreen) SDL_WarpMouse(dd.w-1, dd.h-1); if (avicfg.trackBar==TRK_NEVER) trk_enable(0); else if (avicfg.trackBar==TRK_FULLSCREEN) trk_enable(avicfg.doFullscreen); else if (avicfg.trackBar==TRK_ALWAYS) { trk_enable(1); } if (trk_enabled()) { if (!avicfg.doAutoHide) trk_show(1); } trk_width(dd.w); unsetenv("SDL_VIDEO_CENTERED"); { char buf[256]; sprintf(buf,"%s - (%dx%d)",PLAYER_CAPTION,dd.rw,dd.rh); SDL_WM_SetCaption(buf, NULL); } dd.rect.x=dd.rect.y=0; dd.rect.w=dd.w; dd.rect.h=trk_enabled()?(dd.h-trk_height()):(dd.h); buildZoomer(dd.rw,dd.rh,dd.rw,dd.rh,dd.bpp); buildZoomer(dd.rw,dd.rh,dd.w,dd.rect.h,dd.bpp); if (dd.do_yuv) { dd.yuv=SDL_CreateYUVOverlay(dd.rw,dd.rh, YUVSPACE, dd.dest); if ( dd.yuv &&((!!dd.yuv->hw_overlay)||(sf1time)) ) { dd.do_yuv=1; }else { if (dd.yuv) SDL_FreeYUVOverlay(dd.yuv); dd.yuv=NULL; dd.do_yuv=0; } }else dd.yuv=NULL; needResize=0; SDL_mutexV(dd.gui); countFrames=0; while((!needResize)||(countFrames)) { int delay = 0; SDL_mutexP(dd.gui); while (SDL_PollEvent(&event)) { cb_eventfilter(&event); } if ((trk_enabled())&&(sweepUpdated)) { int mx,my; int btn=SDL_GetMouseState(&mx,&my); if (my == (dd.dest->h-1)) { if (mx == (dd.dest->w-1)) {if (trk_shown()) trk_show(0);} else trk_show(1); }else if ( my < (dd.dest->h-trk_height()) ) { if (avicfg.doAutoHide) if (trk_shown()) trk_show(0); } if (!avicfg.doAutoHide) if ((my != (dd.dest->h-1))||(mx != (dd.dest->w-1))) if (!trk_shown()) trk_show(1); if ( my < (dd.dest->h-trk_height()) ) {mx=-1;btn=0;} if ((trk_shown())&&(!countFrames)) barResult=trk_display(dd.dest,dd.dest->h-trk_height(),mx, xmms_remote_is_paused(ctrlsocket_get_session_id()),btn&SDL_BUTTON(1)); else { SDL_Rect r1; r1.x=0; r1.w=dd.w; r1.y=dd.dest->h-trk_height(); r1.h=trk_height(); SDL_FillRect(dd.dest, &r1, 0); barResult=0; } if ( my < (dd.dest->h-trk_height()) ) barResult=0; } if (!sweepUpdated) { /* FIXME */ if (!trk_enabled()) { delay = 10; }else { if (xmms_remote_is_paused(ctrlsocket_get_session_id())) { SDL_Rect r1; r1.x=0; r1.w=dd.w; r1.y=dd.dest->h-trk_height(); r1.h=trk_height(); SDL_UpdateRects(dd.dest, 1, &r1); }else { delay = 10; } } }else { if (dd.do_yuv) { SDL_LockYUVOverlay(dd.yuv); doing_output=1; SDL_mutexP(dd.sweep); if (sweepBuffer) memcpy(dd.yuv->pixels[0], sweepBuffer, sweepSize); sweepUpdated=0; SDL_mutexV(dd.sweep); doing_output=0; SDL_UnlockYUVOverlay(dd.yuv); SDL_DisplayYUVOverlay(dd.yuv, &(ddof(*current).rect)); if (!trk_enabled()) /* FIXME */ { /* */ }else { SDL_Rect r1; r1.x=0; r1.w=dd.w; r1.y=dd.dest->h-trk_height(); r1.h=trk_height(); SDL_UpdateRects(dd.dest, 1, &r1); } }else if ((dd.dest->w==dd.rw)&&(dd.dest->h==dd.rh)&&(!needResize)&&(!trk_enabled())) { SDL_LockSurface(dd.dest); doing_output=1; SDL_mutexP(dd.sweep); if (sweepBuffer) memcpy(dd.dest->pixels, sweepBuffer, sweepSize); sweepUpdated=0; SDL_mutexV(dd.sweep); doing_output=0; SDL_UnlockSurface(dd.dest); SDL_Flip(dd.dest); }else { SDL_LockSurface(dd.dest); doing_output=1; SDL_mutexP(dd.sweep); if (sweepBuffer) reZoom(sweepBuffer, dd.dest->pixels); sweepUpdated=0; SDL_mutexV(dd.sweep); doing_output=0; SDL_UnlockSurface(dd.dest); SDL_Flip(dd.dest); } } if (countFrames) countFrames--; SDL_mutexV(dd.gui); if (delay > 0) SDL_Delay(delay); } SDL_mutexP(dd.gui); if (dd.yuv) { SDL_FreeYUVOverlay(dd.yuv); dd.yuv=NULL; } if (dd.dest) { SDL_FreeSurface(dd.dest); dd.dest=NULL; } if (needResize==1) {goto reApply;} SDL_QuitSubSystem(SDL_INIT_VIDEO|SDL_INIT_EVENTTHREAD); needResize=-1; SDL_mutexV(dd.gui); return 1; } #undef dd #define dd (*((cPlayerData *)pvtData)) static void cb_kill(int dummy) { cPlayer *z; if (!current) return; current->close(); } static void cb_kill_dummy(int dummy) { } static void cb_sweep_dummy(CImage *img) { } static void cb_sweep(CImage *img) { if (!current) return; // if (doing_output) return; if (!img) return; if (img) prevImg=img;else img=prevImg; if (img&&(img->data())&&(img->bytes())) { SDL_mutexP(ddof(*current).sweep); if (sweepBufferSizebytes()) { if (sweepBuffer) delete[] sweepBuffer; sweepBuffer=new char[img->bytes()]; sweepBufferSize=img->bytes(); } memcpy(sweepBuffer, img->data(), img->bytes()); sweepUpdated=1; sweepSize=img->bytes(); SDL_mutexV(ddof(*current).sweep); } } cPlayer::cPlayer() { pvtData=(void *)new cPlayerData(); dd.gui=SDL_CreateMutex(); dd.sweep=SDL_CreateMutex(); trk_init(); } cPlayer::~cPlayer() { unsetenv("SDL_VIDEO_CENTERED"); unsetenv("SDL_VIDEO_X11_NODIRECTCOLOR"); SDL_DestroyMutex(dd.gui); SDL_DestroyMutex(dd.sweep); delete ⅆ } ////////////////////////// ROUTINE TO OPEN MOVIE //////////////////////////// int cPlayer::open(const char *filename) { if (dd.avi) close(); needResize=-1; if (!query(filename,NULL,NULL)) return 0; dd.bpp = systemBPP; current=this; // setup pointer for global routines #ifdef HAVE_AVIFILE6 dd.avi = CreateAviPlayer(filename,dd.bpp); #else dd.avi = CreateAviPlayer(); #endif if (!dd.avi) return 0; #ifdef HAVE_AVIFILE6 #else dd.avi->initPlayer(filename,dd.bpp); //TODO: implement subtitles #endif if (!dd.avi->isValid()) {delete dd.avi;return 0;} dd.avi->setKillHandler(cb_kill); // setup closing handler if (avicfg.doSyncAudio!=0) dd.avi->setAsync(avicfg.doSyncAudio*1.0/1000.0); dd.rw = dd.avi->width(); // real movie width dd.rh = dd.avi->height(); // and height dd.w = dd.rw; dd.h = dd.rh; dd.yuv=NULL; // YUV overlay dd.dest=NULL; // RGB surface if ((avicfg.doYUV)&&(dd.avi->setColorSpace(YUVSPACE, true)==0)) { printf("*** YUV is here! super!\n"); dd.do_yuv=1; }else dd.do_yuv=0; sf1time=avicfg.doSoftYUV; SDL_mutexP(dd.gui); msize(avicfg.doSizePX,avicfg.doSizePY); manualsize(avicfg.doEnableResize); hwtype(avicfg.doYUV); postype(avicfg.doPosition); dd.thread = SDL_CreateThread(thread_video,(void *)this); SDL_mutexV(dd.gui); while(needResize!=0) SDL_Delay(50); SDL_mutexP(dd.gui); if (dd.do_yuv) { printf("YUV mode\n"); dd.avi->setColorSpace(YUVSPACE, false); }else { printf("RGB mode\n"); } //// INSTALL DOUBLEBUFFERING CALLBACK //// dd.avi->setDrawCallback2(cb_sweep); dd.len = dd.avi->GetVideoLength(); if (dd.len==0) dd.len = dd.avi->GetAudioLength(); printf("Initialization completed\n"); //// CONTINUE WITH SDL THREAD //// SDL_mutexV(dd.gui); return 1; } ////////////////////////// ROUTINE TO CLOSE MOVIE /////////////////////////// int cPlayer::close() { //////////////////// WAIT FOR VIDEO THREAD TO STOP ////////////////////// if (needResize!=-1) { needResize=2; while(needResize!=-1) SDL_Delay(50); SDL_WaitThread(dd.thread, NULL); dd.thread=0; } //////////// SET DUMMY CALLBACKS TO DISABLE OUTPUT AND NOFITY /////////// dd.avi->setDrawCallback2(cb_sweep_dummy); dd.avi->setKillHandler(cb_kill_dummy); ///////////////////// STOP AND KILL AVIFILE ENTITY ////////////////////// if (dd.avi && (dd.avi->isPlaying())) dd.avi->stop(); if (dd.avi) {delete dd.avi;dd.avi=NULL;} ///////////////////////////////////////////////////////////////////////// SDL_mutexP(dd.sweep); sweepBufferSize=0; delete[] sweepBuffer; sweepBuffer=NULL; SDL_mutexV(dd.sweep); cfgStore(); return 1; } //// STUB FOR SOME BUGGY LIBRARIES //// int main(int argc,char *argv[]) { new cPlayer(); return 0; } ////////////////////////// INITIALIZATION ROUTINE /////////////////////////// int cPlayer::init() { sweepBufferSize=0; sweepBuffer=NULL; cfgInit(); cfgRetrieve(); /////////////////// QUERY AND STORE DEFAULT SYSTEM VALUES /////////////// if(SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD)<0) return 0; systemBPP=SDL_GetVideoInfo()->vfmt->BitsPerPixel; SDL_Rect **modes; modes = SDL_ListModes(NULL, SDL_FULLSCREEN); if ( (modes!=(SDL_Rect **)0) &&(modes!=(SDL_Rect **)-1) ) { systemW=modes[0]->w; systemH=modes[0]->h; destroy_avmodes(); while(modes[0]) { add_avmode(modes[0]->w,modes[0]->h,systemBPP); /* FIXME */ //systemBPP modes++; } // add_avmode(320,240,systemBPP); }else { systemW=640; /* FIXME */ systemH=480; } if (!avmodes) { add_avmode(systemW,systemH,systemBPP); /* FIXME */ } SDL_QuitSubSystem(SDL_INIT_VIDEO|SDL_INIT_EVENTTHREAD); ///////////////////////////////////////////////////////////////////////// return 1; } //// SEEK SOMEWHERE ? //// int cPlayer::seek(long pos) { if (dd.avi) dd.avi->reseek_exact(pos); return 1; } long cPlayer::tell() { if (!dd.avi) return -1; return (long)(dd.avi->GetPos()*1000); } //// THIS IS PLAY THING //// int cPlayer::play() { dd.avi->start(); dd.avi->play(); return 1; } //// PAUSE STUFF //// int cPlayer::pause(int value) { dd.avi->pause(value); return 1; } int cPlayer::rewind() { dd.avi->reseek(0); return 1; } int cPlayer::deinit() { cfgStore(); cfgRefresh=2; if (sweepBuffer) delete[] sweepBuffer; return 1; } int cPlayer::postype(int yes) { unsetenv("SDL_VIDEO_CENTERED"); avicfg.doPosition=yes; if (yes==1) { putenv("SDL_VIDEO_CENTERED=1"); } cfgStore(); } int cPlayer::hwtype(int yes) { avicfg.doYUV=yes; cfgStore(); unsetenv("SDL_VIDEO_X11_NODIRECTCOLOR"); if (avicfg.doYUV) { putenv("SDL_VIDEO_X11_NODIRECTCOLOR=1"); } cfgRefresh=1; } int cPlayer::msize(int px,int py) { avicfg.doSizePX=px; avicfg.doSizePY=py; cfgStore(); cfgRefresh=1; REFRESH return 1; } int cPlayer::manualsize(int yes) { avicfg.doEnableResize=yes; cfgStore(); cfgRefresh=1; REFRESH return 1; } long cPlayer::len() { return (long)(dd.len*1000); } int cPlayer::query(const char *filename,long *length,char **title) { int is_avi=1; if (length) *length=0; {///////////////////////////////////// DUMB AVI CHECKING //// FILE *f=fopen(filename,"rb"); if (!f) return 0; char buf[5]={0,0,0,0,0}; if ( (!fread(buf,4,1,f)) ||(!!strcmp(buf,"RIFF")) ) { is_avi=0; } /* FIXME */ fclose(f); }/////////////////////////////////////////////////////////// IAviReadFile *rf=CreateIAviReadFile(filename); MainAVIHeader hdr; if ( (!rf) ||(!rf->StreamCount()) ||(rf->GetFileHeader(&hdr)) ) { delete rf; return 0; } if (length) { if (is_avi) *length=(long)(1.0*hdr.dwMicroSecPerFrame*hdr.dwTotalFrames/1000.0); } if (title) { *title=strdup(filename); } delete rf; return 1; }