/* * frontend_local.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id: frontend_local.c,v 1.31 2008/04/03 08:08:27 phintuka Exp $ * */ #include #include #include #include #include #include #include #include #if VDRVERSNUM >= 10501 || (defined(PATCH_SHUTDOWN_REWRITE) && PATCH_SHUTDOWN_REWRITE >= 100) #include #endif #include "logdefs.h" #include "config.h" #include "xine_frontend.h" #include "frontend_local.h" //------------------------------ cRwLockBlock --------------------------------- class cRwLockBlock { private: cRwLock& m_Lock; public: cRwLockBlock(cRwLock& lock, bool write) : m_Lock(lock) { m_Lock.Lock(write);} ~cRwLockBlock() { m_Lock.Unlock(); } }; #define LOCK_FE cRwLockBlock(m_feLock, false) #define LOCK_FE_WR cRwLockBlock(m_feLock, true) //----------------- keyboard control handler (C callback) -------------------- extern "C" { static void keypress_handler(const char *keymap, const char *key) { if(!strncmp("INFO ", keymap, 5)) { cXinelibThread::InfoHandler(keymap+5); } else if(!xc.use_x_keyboard || !key) { /* Only X11 key events came this way in local mode. Keyboard is handled by vdr. */ LOGMSG("keypress_handler(%s): X11 Keyboard disabled in config", key); } else { cXinelibThread::KeypressHandler(keymap, key, false, false); } } }; //----------------------------- cXinelibLocal -------------------------------- cXinelibLocal::cXinelibLocal(const char *frontend_name) : cXinelibThread("Local decoder/display (cXinelibThread)"), m_feLock(true) { fe = NULL; h_fe_lib = NULL; m_bReconfigRequest = true; } cXinelibLocal::~cXinelibLocal() { TRACEF("cXinelibLocal::~cXinelibLocal"); m_bReady = false; Stop(); if(fe) { fe->fe_free(fe); fe = NULL; } if(h_fe_lib) { dlclose(h_fe_lib); } } void cXinelibLocal::Stop(void) { TRACEF("cXinelibLocal::Stop"); SetStopSignal(); { LOCK_FE; m_bReady = false; if(fe) fe->fe_interrupt(fe); } cXinelibThread::Stop(); } // // Data transfer // int cXinelibLocal::Play_PES(const uchar *data, int len) { TRACEF("cXinelibLocal::Play_PES"); { LOCK_FE; if(fe && !m_bStopThread) { int done = fe->xine_queue_pes_packet(fe, (char*)data, len); if(done>0) { Lock(); m_StreamPos += done; Unlock(); return done; } } } //cCondWait::SleepMs(5); return len; } void cXinelibLocal::OsdCmd(void *cmd) { TRACEF("cXinelibLocal::OsdCmd"); LOCK_FE; if(cmd && fe && m_bReady) fe->xine_osd_command(fe, (struct osd_command_s*)cmd); } uchar *cXinelibLocal::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { uchar *data; LOCK_FE; if(fe && fe->grab && m_bReady) if((data = (uchar*)fe->grab(fe, &Size, Jpeg, Quality, SizeX, SizeY))) return data; return NULL; } int64_t cXinelibLocal::GetSTC() { TRACEF("cXinelibLocal::GetSTC"); int64_t pts = -1; char buf[32] = {0}; strcpy(buf, "GETSTC\r\n"); LOCK_FE; if(fe && m_bReady) if(0 == fe->xine_control(fe, (char*)buf)) //if(*((int64_t *)buf) < MAX_SCR) // if(*((int64_t *)buf) >= 0LL) pts = *((int64_t *)buf); return pts; } // // Playback files // bool cXinelibLocal::EndOfStreamReached(void) { LOCK_THREAD; if(fe && fe->xine_is_finished(fe, 1)) return true; return cXinelibThread::EndOfStreamReached(); } // // Configuration // void cXinelibLocal::ConfigureWindow(int fullscreen, int width, int height, int modeswitch, const char *modeline, int aspect, int scale_video, int field_order) { LOCK_FE; if(fe) fe->fe_display_config(fe, width, height, fullscreen, modeswitch, modeline, aspect, scale_video, field_order); } void cXinelibLocal::ConfigureDecoder(int pes_buffers) { // needs xine restart { LOCK_FE; xc.pes_buffers = pes_buffers; if(!fe) return; m_bReady = false; m_bReconfigRequest = true; fe->fe_interrupt(fe); } while(!m_bReady && !GetStopSignal()) cCondWait::SleepMs(100); cCondWait::SleepMs(100); } // // Xine control // int cXinelibLocal::Xine_Control(const char *cmd) { TRACEF("cXinelibLocal::Xine_Control"); if(cmd && *cmd && !GetStopSignal()) { char buf[4096]; if(snprintf(buf, sizeof(buf), "%s\r\n", cmd) >= (int)sizeof(buf)) { buf[sizeof(buf)-1] = 0; LOGMSG("Xine_Control: message too long ! (%s)", buf); return 0; } LOCK_FE; if(fe) return fe->xine_control(fe, (char*)buf); } return 0; } // // Frontend loader // frontend_t *cXinelibLocal::load_frontend(const char *fe_name) { Dl_info info; struct stat statbuffer; char libname[4096]=""; void *lib = NULL; fe_creator_f *fe_creator = NULL; static int my_marker = 0; if(!dladdr((void *)&my_marker, &info)) { LOGERR("Error searching plugin: dladdr() returned false (%s)",dlerror()); return NULL; } LOGDBG("xineliboutput: plugin file is %s", info.dli_fname); int fe_ind = strstra(fe_name, xc.s_frontends, FRONTEND_NONE); bool fe_try = false; if(fe_ind == FRONTEND_NONE) { LOGMSG("Front-end %s unknown!", fe_name); fe_ind = 0; fe_try = true; } strn0cpy(libname, info.dli_fname, sizeof(libname) - 128); if(strrchr(libname, '/')) *(strrchr(libname, '/')+1) = 0; LOGDBG("Searching frontend %s from %s", xc.s_frontends[fe_ind], libname); do { strncat(libname, xc.s_frontend_files[fe_ind], 64); LOGDBG("Probing %s", libname); if (stat(libname, &statbuffer)) { LOGERR("load_frontend: can't stat %s",libname); } else if((statbuffer.st_mode & S_IFMT) != S_IFREG) { LOGMSG("load_frontend: %s not regular file ! trying to load anyway ...", libname); } if( !(lib = dlopen (libname, RTLD_LAZY | RTLD_GLOBAL))) { char *dl_error_msg = dlerror(); LOGERR("load_frontend: cannot dlopen file %s: %s", libname, dl_error_msg); } else if ( (fe_creator = (fe_creator_f*)dlsym(lib, "fe_creator"))) { LOGDBG("load_frontend: entry at %p", fe_creator); frontend_t *fe = (**fe_creator)(); if(fe) { if(h_fe_lib) dlclose(h_fe_lib); h_fe_lib = lib; LOGDBG("Using frontend %s (%s) from %s", xc.s_frontends[fe_ind], xc.s_frontendNames[fe_ind], xc.s_frontend_files[fe_ind]); return fe; } else { LOGMSG("Frontend %s (%s) creation failed", xc.s_frontends[fe_ind], xc.s_frontendNames[fe_ind]); } } else { LOGERR("Frontend entry point not found"); dlclose(lib); } fe_ind++; // try next frontend ... } while(fe_try && fe_ind < FRONTEND_count); LOGMSG("No usable frontends found, giving up !"); return NULL; } // // Thread main loop // void cXinelibLocal::Action(void) { frontend_t *curr_fe = NULL; TRACEF("cXinelibLocal::Action"); SetPriority(2); /* lower priority */ // init frontend if(!curr_fe) { curr_fe = load_frontend(xc.local_frontend); if(!curr_fe) { LOGMSG("cXinelibLocal: Error initializing frontend"); SetStopSignal(); } else { LOGDBG("cXinelibLocal::Action - fe created"); if(!curr_fe->fe_display_open(curr_fe, xc.width, xc.height, xc.fullscreen, xc.hud_osd, xc.modeswitch, xc.modeline, xc.display_aspect, keypress_handler, xc.video_port, xc.scale_video, xc.field_order)) { LOGMSG("cXinelibLocal: Error initializing display"); SetStopSignal(); } else { LOGDBG("cXinelibLocal::Action - fe->fe_display_open ok"); } } } // main loop while (!GetStopSignal()) { { // init and start xine engine LOCK_FE_WR; LOGDBG("cXinelibLocal::Action - xine_init"); fe = curr_fe; if(m_bReconfigRequest) { if(!fe->xine_init(fe, xc.audio_driver, xc.audio_port, xc.video_driver, xc.pes_buffers, xc.post_plugins)) { LOGMSG("cXinelibLocal: Error initializing frontend"); break; } LOGDBG("cXinelibLocal::Action - fe->xine_init ok"); m_bReconfigRequest = false; } // open (xine) stream LOGDBG("cXinelibLocal::Action - xine_open"); if(!fe->xine_open(fe, NULL)) { LOGMSG("cXinelibLocal: Error opening xvdr://"); break; } LOGDBG("cXinelibLocal::Action - fe->xine_open ok"); // start playing (xine) stream if(!fe->xine_play(fe)) { LOGMSG("cXinelibLocal: Error playing xvdr://"); break; } LOGDBG("cXinelibLocal::Action - fe->xine_play ok"); m_StreamPos = 0; Xine_Control("STREAMPOS 0"); Xine_Control("VERSION " XINELIBOUTPUT_VERSION " " "\r\n"); } // configure frontend and xine m_bNoVideo = false; Configure(); LOGDBG("cXinelibLocal::Action - fe config OK"); LogoDisplay(); LOGDBG("cXinelibLocal::Action - logo sent"); { LOCK_THREAD; Xine_Control("NOVIDEO 0"); Xine_Control("LIVE 1"); Xine_Control("CLEAR"); m_bNoVideo = false; m_bLiveMode = true; m_bReady = true; } // main event loop LOGDBG("cXinelibLocal:Action - Starting event loop"); { LOCK_FE; while(!GetStopSignal() && m_bReady && (/*m_bLoopPlay ||*/ !fe->xine_is_finished(fe, 0)) && fe->fe_run(fe)) /*cCondWait::SleepMs(50)*/ ; } LOGDBG("cXinelibLocal::Action - event loop terminated, " "xine_is_finished=%d", fe->xine_is_finished(fe, 0)); { LOCK_THREAD; m_bReady = false; m_bEndOfStreamReached = true; } { LOCK_FE_WR; if(fe) fe->xine_close(fe); fe = NULL; } LOGMSG("cXinelibLocal::Action: Xine closed"); if(!m_bReconfigRequest && xc.exit_on_close) { LOGMSG("Shutting down VDR"); #if VDRVERSNUM >= 10501 || (defined(PATCH_SHUTDOWN_REWRITE) && PATCH_SHUTDOWN_REWRITE >= 100) ShutdownHandler.RequestEmergencyExit(); #else cThread::EmergencyExit(true); #endif break; } } if(curr_fe) { curr_fe->xine_exit(fe); curr_fe->fe_display_close(curr_fe); curr_fe->fe_free(curr_fe); } m_bIsFinished = true; LOGMSG("cXinelibLocal::Action: thread finished"); }