/* * frontend.c: * * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * * $Id: frontend.c,v 1.1 2006-06-03 10:01:17 phintuka Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "logdefs.h" #include "config.h" #include "frontend.h" #include "tools/pes.h" #include "tools/general_remote.h" //#define LOG_CONTROL_MESSAGES //#define XINELIBOUTPUT_LOG_KEYS //----------------------------- cXinelibThread -------------------------------- // // keyboard control handler // /*static*/ void cXinelibThread::KeypressHandler(char *keymap, char *key, bool repeat, bool release) { #ifdef XINELIBOUTPUT_LOG_KEYS static FILE *flog = fopen("/video/keys.log","w"); fprintf(flog,"KEY %s %s %d %d\n",keymap,key,repeat,release);fflush(flog); #endif TRACE("keypress_handler: " << (keymap?keymap:"") << " " << key); if(!key) return; if(keymap) { cRemote *item = Remotes.First(); while(item) { if(!strcmp(item->Name(), keymap)) { // dirty... but only way to support learning ... ((cGeneralRemote*)item)->Put(key, repeat, release); return; } item = Remotes.Next(item); } cGeneralRemote *r = new cGeneralRemote(keymap); if(*key) r->Put(key, repeat, release); } else { cRemote::Put(cKey::FromString(key)); } } cXinelibThread::cXinelibThread(const char *Description) : cThread(Description) { TRACEF("cXinelibThread::cXinelibThread"); m_bStopThread = false; m_bReady = false; m_bIsFinished = false; m_bNoVideo = true; m_bLiveMode = false; m_StreamPos = 0; m_bEndOfStreamReached = false; m_bPlayingFile = false; m_FileName = NULL; } cXinelibThread::~cXinelibThread() { TRACEF("cXinelibThread::~cXinelibThread"); if(Active()) Cancel(); if(m_FileName) free(m_FileName); } // // Thread control // void cXinelibThread::Start(void) { TRACEF("cXinelibThread::Start"); cThread::Start(); } void cXinelibThread::Stop(void) { TRACEF("cXinelibThread::Stop"); SetStopSignal(); //if(Active()) Cancel(5); } void cXinelibThread::SetStopSignal(void) { TRACEF("cXinelibThread::SetStopSignal"); LOCK_THREAD; m_bStopThread = true; } bool cXinelibThread::GetStopSignal(void) { TRACEF("cXinelibThread::GetStopSignal"); LOCK_THREAD; return m_bStopThread; } bool cXinelibThread::IsReady(void) { LOCK_THREAD; return m_bReady; } bool cXinelibThread::IsFinished(void) { LOCK_THREAD; return m_bIsFinished; } // // Playback control // void cXinelibThread::SetVolume(int NewVolume) { Xine_Control("VOLUME", NewVolume * 100 / 255); } void cXinelibThread::TrickSpeed(int Speed) { TRACEF("cXinelibThread::TrickSpeed"); Xine_Control("TRICKSPEED", Speed); } void cXinelibThread::SetLiveMode(bool LiveModeOn) { TRACEF("cXinelibThread::SetLiveMode"); Lock(); if(m_bLiveMode == LiveModeOn) { Unlock(); return; } m_bLiveMode = LiveModeOn; Unlock(); Xine_Control("LIVE", m_bLiveMode ? 1 : 0); Xine_Sync(); } void cXinelibThread::SetStillMode(bool StillModeOn) { TRACEF("cXinelibThread::SetStillMode"); Xine_Control("STILL", StillModeOn ? 1 : 0); Xine_Sync(); } void cXinelibThread::SetNoVideo(bool bVal) { TRACEF("cXinelibThread::SetNoVideo"); Lock(); if(m_bNoVideo == bVal) { Unlock(); return; } m_bNoVideo = bVal; Unlock(); Xine_Control("NOVIDEO", m_bNoVideo ? 1 : 0); if(m_bNoVideo && strcmp(xc.audio_visualization, "none")) { ConfigurePostprocessing(xc.audio_visualization, true, NULL); } else { ConfigurePostprocessing("AudioVisualization", false, NULL); } } void cXinelibThread::AudioStreamChanged(bool ac3, int StreamId) { TRACEF("cXinelibThread::AudioStreamChanged"); if(ac3) Xine_Control("NEWAUDIOSTREAM AC3"); else Xine_Control("NEWAUDIOSTREAM", StreamId); } void cXinelibThread::SpuStreamChanged(int StreamId) { TRACEF("cXinelibThread::SpuStreamChanged"); Xine_Control("NEWSPUSTREAM", StreamId); } void cXinelibThread::Clear(void) { TRACEF("cXinelibThread::Clear"); Lock(); int64_t tmp = m_StreamPos; Unlock(); Xine_Control("DISCARD", tmp); } bool cXinelibThread::Flush(int TimeoutMs) { TRACEF("cXinelibThread::Flush"); return Xine_Control("FLUSH", TimeoutMs) <= 0; } bool cXinelibThread::Poll(cPoller& Poller, int TimeoutMs) { TRACEF("cXinelibThread::Poll"); int n = Xine_Control("POLL", TimeoutMs); if(n>0) return true; return false; // Poller.Poll(TimeoutMs); } // // Data transfer // int cXinelibThread::Play_PES(const uchar *data, int len) { Lock(); m_StreamPos += len; /*m_bEndOfStreamReached = false;*/ Unlock(); return len; } // // Stream conversions // // Convert MPEG1 PES headers to MPEG2 PES headers int cXinelibThread::Play_Mpeg1_PES(const uchar *data1, int len) { if(!data1[0] && !data1[1] && data1[2] == 0x01 && len>7 && /* header sync bytes */ ( VIDEO_STREAM == (data1[3] & ~VIDEO_STREAM_MASK) || /* video stream */ AUDIO_STREAM == (data1[3] & ~AUDIO_STREAM_MASK) || /* audio stream */ PRIVATE_STREAM1 == data1[3]) && /* private stream 1 */ ((data1[6] & 0xC0) != 0x80) && /* really mpeg1 pes */ (len == ((data1[4]<<8) | data1[5]) + 6)) { /* whole PES packet and nothing else */ uchar *data2 = new uchar[len+64]; int i1=0, i2=0, r=0; data2[i2++]=data1[i1++]; // 00 (sync) data2[i2++]=data1[i1++]; // 00 (sync) data2[i2++]=data1[i1++]; // 01 (sync) data2[i2++]=data1[i1++]; // stream ID data2[i2++]=data1[i1++]; // len hi data2[i2++]=data1[i1++]; // len lo // skip stuffing while ((data1[i1] & 0x80) == 0x80) i1++; if ((data1[i1] & 0xc0) == 0x40) { // skip STD_buffer_scale, STD_buffer_size i1 += 2; } if(len>8; data2[5] = ((newlen)&0xff); if(len-i1 > 0) { memcpy(data2+i2, data1+i1, len-i1); cPoller p; while(!Poll(p,100) && loops++ < 10) { LOGDBG("Play_Mpeg1_PES: poll failed"); } r = Play_PES(data2,newlen+6); } delete data2; return r==newlen+6 ? ((data1[4]<<8)|data1[5])+6 : 0; } return len; // nothing useful found ... } // Pack elementary MPEG stream to PES bool cXinelibThread::Play_Mpeg2_ES(const uchar *data, int len, int streamID) { static uchar hdr[] = {0x00,0x00,0x01,0xe0, 0x00,0x00,0x80,0,0}; /* mpeg2 */ int todo = len, done = 0, hdrlen = 9/*sizeof(hdr)*/; uchar *frame = new uchar[PES_CHUNK_SIZE+32]; hdr[3] = (uchar)streamID; while(todo) { int blocklen = todo; if(blocklen > ((PES_CHUNK_SIZE - hdrlen) & 0xfffc)) blocklen = (PES_CHUNK_SIZE - hdrlen) & 0xfffc; hdr[4] = ((blocklen+3)&0xff00)>>8; hdr[5] = ((blocklen+3)&0xff); memcpy(frame, hdr, hdrlen); memcpy(frame+hdrlen, data+done, blocklen); done += blocklen; todo -= blocklen; cPoller p; #if 1 Poll(p, 100); #else int loops=0; while(!Poll(p,100) && loops++ < 10) { LOGDBG("Play_ES: Poll Failed"); } #endif if(blocklen+hdrlen != Play_PES(frame,blocklen+hdrlen)) { delete frame; return false; } } // append sequence end code to video if((streamID & 0xF0) == 0xE0) { static uchar seqend[] = {0x00,0x00,0x01,0xe0, 0x00,0x07,0x80,0x00, 0x00, 0x00,0x00,0x01,0xB7}; /* mpeg2 */ #if 0 frame[0] = 0x00; frame[1] = 0x00; frame[2] = 0x01; frame[3] = (uchar)streamID; frame[4] = 0x00; frame[5] = 0x07; frame[6] = 0x80; frame[7] = 0x00; frame[8] = 0x00; frame[9] = 0x00; frame[10] = 0x00; frame[11] = 0x01; frame[12] = 0xB7; Play_PES(frame, 13); #else seqend[3] = (uchar)streamID; Play_PES(seqend, 13); #endif } delete frame; return true; } // // Built-in still images // bool cXinelibThread::QueueBlankDisplay(void) { TRACEF("cXinelibThread::BlankDisplay"); extern unsigned char v_mpg_black[]; // black_720x576.c extern int v_mpg_black_length; return Play_Mpeg2_ES(v_mpg_black, v_mpg_black_length, VIDEO_STREAM); } bool cXinelibThread::BlankDisplay(void) { TRACEF("cXinelibThread::BlankDisplay"); bool r = QueueBlankDisplay(); for(int i=0; i<5 && !Flush(100); i++) ; return r; } bool cXinelibThread::LogoDisplay(void) { TRACEF("cXinelibThread::LogoDisplay"); extern unsigned char v_mpg_vdrlogo[]; // vdrlogo_720x576.c extern int v_mpg_vdrlogo_length; bool r = Play_Mpeg2_ES(v_mpg_vdrlogo, v_mpg_vdrlogo_length, VIDEO_STREAM); for(int i=0; i<5 && !Flush(100); i++) ; return r; } bool cXinelibThread::NoSignalDisplay(void) { TRACEF("cXinelibThread::NoSignalDisplay"); extern unsigned char v_mpg_nosignal[]; // nosignal_720x576.c extern int v_mpg_nosignal_length; bool r = Play_Mpeg2_ES(v_mpg_nosignal, v_mpg_nosignal_length, VIDEO_STREAM); for(int i=0; i<5 && !Flush(100); i++) ; return r; } // // Xine Control // int cXinelibThread::Xine_Control(const char *cmd, int p1) { char buf[128]; sprintf(buf, "%s %d", cmd, p1); return Xine_Control(buf); } int cXinelibThread::Xine_Control(const char *cmd, int64_t p1) { char buf[128]; sprintf(buf, "%s %lld", cmd, p1); return Xine_Control(buf); } int cXinelibThread::Xine_Control(const char *cmd, char *p1) { char buf[128]; sprintf(buf, "%s %s", cmd, p1); return Xine_Control(buf); } bool cXinelibThread::PlayFile(const char *FileName, int Position, bool LoopPlay) { TRACEF("cXinelibThread::PlayFile"); char buf[2048]; m_bEndOfStreamReached = false; sprintf(buf, "PLAYFILE %s %d %s\r\n", LoopPlay ? "Loop" : "", Position, FileName ? FileName : ""); int result = PlayFileCtrl(buf); if(!FileName || result != 0) { m_bPlayingFile = false; if(m_FileName) free(m_FileName); } else { m_FileName = strdup(FileName); m_bPlayingFile = true; } return (!GetStopSignal()) && (result==0); } // // Configuration // int cXinelibThread::ConfigureOSD(bool prescale_osd, bool unscaled_osd) { char s[256]; sprintf(s, "OSDSCALING %d", prescale_osd); if(!xc.prescale_osd_downscale) strcat(s, " NoDownscale"); if(unscaled_osd) strcat(s, " UnscaledAlways"); if(xc.unscaled_osd_opaque) strcat(s, " UnscaledOpaque"); if(xc.unscaled_osd_lowresvideo) strcat(s, " UnscaledLowRes"); return Xine_Control(s); } int cXinelibThread::ConfigurePostprocessing(char *deinterlace_method, int audio_delay, int audio_compression, int *audio_equalizer, int audio_surround) { char tmp[255]; int r = true; if(strcmp(deinterlace_method, "tvtime")) r = ConfigurePostprocessing("tvtime", false, NULL) && r; r = Xine_Control("DEINTERLACE", deinterlace_method) && r; r = Xine_Control("AUDIODELAY", audio_delay) && r; r = Xine_Control("AUDIOCOMPRESSION", audio_compression) && r; r = Xine_Control("AUDIOSURROUND", audio_compression) && r; sprintf(tmp,"EQUALIZER %d %d %d %d %d %d %d %d %d %d", audio_equalizer[0],audio_equalizer[1], audio_equalizer[2],audio_equalizer[3], audio_equalizer[4],audio_equalizer[5], audio_equalizer[6],audio_equalizer[7], audio_equalizer[8],audio_equalizer[9]); r = Xine_Control(tmp) && r; if(m_bNoVideo && strcmp(xc.audio_visualization, "none")) { //fe->post_open(fe, xc.audio_visualization, NULL); r = ConfigurePostprocessing(xc.audio_visualization, true, NULL) && r; } else { //fe->post_close(fe, NULL, 0); r = ConfigurePostprocessing("AudioVisualization", false, NULL) && r; } if(!strcmp(deinterlace_method, "tvtime")) r = ConfigurePostprocessing("tvtime", true, xc.deinterlace_opts) && r; return r; } int cXinelibThread::ConfigurePostprocessing(char *name, bool on, char *args) { char tmp[1024]; if(!name) name = "0"; if(!args) args = ""; if(on) { sprintf(tmp, "POST %s On %s", name, args); return Xine_Control(tmp); } else { // 0 - audio vis. // 1 - audio post // 2 - video post //return fe->post_close(fe, name, -1); sprintf(tmp, "POST %s Off", name); return Xine_Control(tmp); } return -1; } int cXinelibThread::ConfigureVideo(int hue, int saturation, int brightness, int contrast) { char cmd[128]; sprintf(cmd, "VIDEO_PROPERTIES %d %d %d %d", hue, saturation, brightness, contrast); return Xine_Control(cmd); } // // Playback files // bool cXinelibThread::EndOfStreamReached(void) { LOCK_THREAD; bool r = m_bEndOfStreamReached; return r; }