/*! * \file vdr_player.c * \brief A generic PCM player for a VDR media plugin (muggle) * * \version $Revision: 1.7 $ * \date $Date$ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner, Wolfgang Rohdewald * \author Responsible author: $Author$ * * $Id$ * * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001,2002 Stefan Huelswitt */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vdr_player.h" #include "vdr_decoder.h" #include "vdr_config.h" #include "mg_setup.h" #include "mg_image_provider.h" #include "i18n.h" #include "mg_tools.h" // ---------------------------------------------------------------- // #define DEBUGPES // TODO: check for use of constants #define OUT_BITS 16 // output 16 bit samples to DVB driver #define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes // cResample #define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame // cNormalize #define MAX_GAIN 3.0 // max. allowed gain #define LIM_ACC 12 // bit, accuracy for lookup table // max. value covered by lookup table #define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1) #define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC) // shift value for table lookup #define F_LIM_JMP (mad_fixed_t)(1<(m_playlist->getCurrentItem ()); if (newcurr) { delete m_current; m_current = new mgItemGd(newcurr); } Play (); } void mgPCMPlayer::Activate (bool on) { MGLOG ("mgPCMPlayer::Activate"); if (on) { if (m_playlist && !m_started) { m_playmode = pmStartup; Start (); m_started = true; m_current = 0; m_playmode_mutex.Lock (); WaitPlayMode (pmStartup, true); // wait for the decoder to become ready m_playmode_mutex.Unlock (); Lock (); PlayTrack(); Unlock (); } } else if (m_started && m_active) { Lock (); StopPlay (); Unlock (); m_active = false; SetPlayMode (pmStartup); Cancel (2); } } void mgPCMPlayer::ReloadPlaylist() { Lock (); m_playlist->clearCache(); if (!m_playing) { SkipFile(); Play (); } Unlock (); } void mgPCMPlayer::NewPlaylist (mgSelection * plist) { MGLOG ("mgPCMPlayer::NewPlaylist"); Lock (); StopPlay (); delete m_current; m_current = 0; delete m_playlist; m_playlist = plist; PlayTrack(); Unlock (); } void mgPCMPlayer::NewImagePlaylist( const char *directory ) { MGLOG ("mgPCMPlayer::NewImagePlaylist"); Lock (); if( m_img_provider ) { delete m_img_provider; m_img_provider = NULL; } m_img_provider = mgImageProvider::Create( directory ); m_hasimages = true; // assume we have some images here! Unlock (); } void mgPCMPlayer::SetPlayMode (emgPlayMode mode) { m_playmode_mutex.Lock (); if (mode != m_playmode) { m_playmode = mode; m_playmode_cond.Broadcast (); } m_playmode_mutex.Unlock (); } void mgPCMPlayer::WaitPlayMode(emgPlayMode mode, bool inv) { // must be called with m_playmode_mutex LOCKED !!! while (m_active && ((!inv && mode != m_playmode) || (inv && mode == m_playmode))) { m_playmode_cond.Wait (m_playmode_mutex); } } void mgPCMPlayer::Action (void) { MGLOG ("mgPCMPlayer::Action"); struct mgDecode *ds = 0; struct mad_pcm *pcm = 0; cResample resample[2]; unsigned int nsamples[2]; const mad_fixed_t *data[2]; cScale scale; cLevel level; cNormalize norm; bool haslevel = false; struct LPCMFrame lpcmFrame; const unsigned char *p = 0; int pc = 0, only48khz = the_setup.Only48kHz; cPoller poll; #ifdef DEBUG // int beat = 0; #endif #ifdef DEBUGPES FILE *peslog = fopen( "pes.dump", "w" ); #endif dsyslog ("muggle: player thread started (pid=%d)", getpid ()); memset (&lpcmFrame, 0, sizeof (lpcmFrame)); lpcmFrame.PES[2] = 0x01; lpcmFrame.PES[3] = 0xbd; lpcmFrame.PES[6] = 0x87; lpcmFrame.LPCM[0] = 0xa0; // substream ID lpcmFrame.LPCM[1] = 0xff; lpcmFrame.LPCM[2] = 0; lpcmFrame.LPCM[3] = 4; lpcmFrame.LPCM[5] = 0x01; lpcmFrame.LPCM[6] = 0x80; dvbSampleRate = 48000; m_state = msStop; SetPlayMode (pmStopped); #if VDRVERSNUM >= 10318 cDevice::PrimaryDevice()->SetCurrentAudioTrack(ttAudio); #endif while (m_active) { #ifdef DEBUG /* if (time (0) >= beat + 30) { std:: cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer-> Available () << std::endl << std::flush; scale.Stats (); if (haslevel) norm.Stats (); beat = time (0); } */ #endif Lock (); if (!m_rframe && m_playmode == pmPlay) { switch (m_state) { case msStart: { if( m_img_provider && the_setup.BackgrMode == 1 ) { m_hasimages = m_img_provider->updateItem( m_current ); m_lastshow = -1; // never showed a picture during this song replay } m_index = 0; m_playing = true; if( m_current ) { string filename = m_current->getSourceFile (); if ((m_decoder = mgDecoders::findDecoder (m_current)) && m_decoder->start ()) { levelgood = true; haslevel = false; level.Init (); m_state = msDecode; break; } else { mgWarning("found no decoder for %s",filename.c_str()); m_state=msStop; // if loop mode is on and no decoder // for any track is found, we would // otherwise get into an endless loop // not stoppable with the remote. break; } } m_state = msEof; } break; case msDecode: { if( the_setup.BackgrMode == 1 ) { CheckImage(); } ds = m_decoder->decode (); switch (ds->status) { case dsPlay: { pcm = ds->pcm; m_index = ds->index / 1000; m_state = msNormalize; } break; case dsSkip: case dsSoftError: { // skipping, state unchanged, next decode } break; case dsEof: { m_state = msEof; } break; case dsOK: case dsError: { m_state = msError; } break; } } break; case msNormalize: { if (!haslevel) { if (levelgood) { level.GetPower (pcm); } } else { norm.AddGain (pcm); } m_state = msResample; } break; case msResample: { #ifdef DEBUG { static unsigned int oldrate = 0; if (oldrate != pcm->samplerate) { mgDebug ("mgPCMPlayer::Action: new input sample rate %d",pcm->samplerate); oldrate = pcm->samplerate; } } #endif nsamples[0] = nsamples[1] = pcm->length; data[0] = pcm->samples[0]; data[1] = pcm->channels > 1 ? pcm->samples[1] : 0; lpcmFrame.LPCM[5] &= 0xcf; dvbSampleRate = 48000; if (!only48khz) { switch (pcm->samplerate) { // If one of the supported frequencies, do it without resampling. case 96000: { // Select a "even" upsampling frequency if possible, too. lpcmFrame.LPCM[5] |= 1 << 4; dvbSampleRate = 96000; } break; //case 48000: // this is already the default ... // lpcmFrame.LPCM[5]|=0<<4; // dvbSampleRate=48000; // break; case 11025: case 22050: case 44100: { // lpcmFrame.LPCM[5] |= 2 << 4; lpcmFrame.LPCM[5] |= 0x20; dvbSampleRate = 44100; } break; case 8000: case 16000: case 32000: { // lpcmFrame.LPCM[5] |= 3 << 4; lpcmFrame.LPCM[5] |= 0x30; dvbSampleRate = 32000; } break; } } if (dvbSampleRate != pcm->samplerate) { if (resample[0]. SetInputRate (pcm->samplerate, dvbSampleRate)) { nsamples[0] = resample[0].ResampleBlock (nsamples[0], data[0]); data[0] = resample[0].Resampled (); } if (data[1] && resample[1].SetInputRate (pcm->samplerate, dvbSampleRate)) { nsamples[1] = resample[1].ResampleBlock (nsamples[1], data[1]); data[1] = resample[1].Resampled (); } } m_state = msOutput; } break; case msOutput: { if (nsamples[0] > 0) { unsigned int outlen = scale.ScaleBlock ( lpcmFrame.Data, sizeof (lpcmFrame.Data), nsamples[0], data[0], data[1], the_setup.AudioMode ? amDither : amRound ); if (outlen) { outlen += sizeof (lpcmFrame.LPCM) + LEN_CORR; lpcmFrame.PES[4] = outlen >> 8; lpcmFrame.PES[5] = outlen; // lPCM has 600 fps which is 80 samples at 48kHz per channel // Frame size = (sample_rate * quantization * channels)/4800 size_t frame_size = 2 * (dvbSampleRate*16)/4800; lpcmFrame.LPCM[1] = (outlen - LPCM_SIZE)/frame_size; m_rframe = new cFrame ((unsigned char *) &lpcmFrame, outlen + sizeof (lpcmFrame.PES) - LEN_CORR); } } else { m_state = msDecode; } } break; case msError: case msEof: { if (SkipFile ()) { m_state = msStart; } else { m_state = msWait; } } // fall through case msStop: { m_playing = false; if (m_decoder) { // who deletes decoder? m_decoder->stop (); delete m_decoder; m_decoder = 0; } levelgood = false; scale.Stats (); if (haslevel) { norm.Stats (); } if (m_state == msStop) { // might be unequal in case of fall through from eof/error SetPlayMode (pmStopped); } } break; case msWait: { if (m_ringbuffer->Available () == 0) { // m_active = false; SetPlayMode (pmStopped); } } break; } } if (m_rframe && m_ringbuffer->Put (m_rframe)) { m_rframe = 0; } if (!m_pframe && m_playmode == pmPlay) { m_pframe = m_ringbuffer->Get (); if (m_pframe) { p = m_pframe->Data (); pc = m_pframe->Count (); } } if (m_pframe) { #ifdef DEBUGPES fwrite( (void *)p, pc, sizeof( char ), peslog ); #endif #if VDRVERSNUM >= 10318 int w = PlayPes (p, pc); #else int w = PlayVideo (p, pc); #endif if (w > 0) { p += w; pc -= w; if (pc <= 0) { m_ringbuffer->Drop (m_pframe); m_pframe = 0; } } else if (w < 0 && FATALERRNO) { LOG_ERROR; break; } } eState curr_m_state = m_state; // avoid helgrind warning Unlock (); if ((m_rframe || curr_m_state == msWait) && m_pframe) { // Wait for output to become ready DevicePoll (poll, 500); } else { if (m_playmode != pmPlay) { m_playmode_mutex.Lock (); if (m_playmode != pmPlay) { // Wait on playMode change WaitPlayMode (m_playmode, true); } m_playmode_mutex.Unlock (); } } } Lock (); if (m_rframe) { delete m_rframe; m_rframe = 0; } if (m_decoder) { // who deletes decoder? m_decoder->stop (); delete m_decoder; m_decoder = 0; } m_playing = false; SetPlayMode (pmStopped); #ifdef DEBUGPES fclose( peslog ); #endif Unlock (); m_active = false; dsyslog ("muggle: player thread ended (pid=%d)", getpid ()); } void mgPCMPlayer::Empty (void) { MGLOG ("mgPCMPlayer::Empty"); Lock (); m_ringbuffer->Clear (); DeviceClear (); delete m_rframe; m_rframe = 0; m_pframe = 0; Unlock (); } void mgPCMPlayer::StopPlay () { // StopPlay() must be called in locked state!!! MGLOG ("mgPCMPlayer::StopPlay"); if (m_playmode != pmStopped) { Empty (); m_state = msStop; SetPlayMode (pmPlay); Unlock (); // let the decode thread process the stop signal m_playmode_mutex.Lock (); WaitPlayMode (pmStopped, false); m_playmode_mutex.Unlock (); Lock (); } } bool mgPCMPlayer::SkipFile (bool skipforward) { MGLOG("mgPCMPlayer::SkipFile"); mgItemGd * newcurr = NULL; int skip_direction=1; if (!skipforward) skip_direction=-1; if (m_playlist->skipItems (skip_direction)) { newcurr = dynamic_cast(m_playlist->getCurrentItem ()); if (newcurr) { delete m_current; m_current = new mgItemGd(newcurr); } } return (newcurr != NULL); } void mgPCMPlayer::ToggleShuffle () { m_playlist->toggleShuffleMode (); } void mgPCMPlayer::ToggleLoop (void) { m_playlist->toggleLoopMode (); } void mgPCMPlayer::Pause (void) { if (m_playmode == pmPaused) { Play (); } else { if (m_playmode == pmPlay) { // DeviceFreeze(); SetPlayMode (pmPaused); } } } void mgPCMPlayer::Play (void) { MGLOG ("mgPCMPlayer::Play"); if (m_playmode != pmPlay && m_current) { Lock (); if (m_playmode == pmStopped) { m_state = msStart; } // DevicePlay(); // TODO? Commented out in original code, too SetPlayMode (pmPlay); Unlock (); } } void mgPCMPlayer::Forward () { MGLOG ("mgPCMPlayer::Forward"); Lock (); if (SkipFile ()) { StopPlay (); Play (); } Unlock (); } void mgPCMPlayer::Backward (void) { MGLOG ("mgPCMPlayer::Backward"); Lock (); if (SkipFile (false)) { StopPlay (); Play (); } Unlock (); } void mgPCMPlayer::Goto (int index, bool still) { m_playlist->GotoItemPosition (index - 1); mgItemGd *next = dynamic_cast(m_playlist->getCurrentItem ()); if (next) { Lock (); StopPlay (); delete m_current; m_current = new mgItemGd(next); Play (); Unlock (); } } void mgPCMPlayer::SkipSeconds (int secs) { if (m_playmode != pmStopped) { Lock (); if (m_playmode == pmPaused) { SetPlayMode (pmPlay); } if ( m_decoder && m_decoder->skip (secs, m_ringbuffer->Available(), dvbSampleRate) ) { levelgood = false; } Empty (); Unlock (); } } bool mgPCMPlayer::GetIndex (int ¤t, int &total, bool snaptoiframe) { if (m_current) { current = SecondsToFrames (m_index); total = SecondsToFrames (m_current->getDuration ()); return true; } return false; } void mgPCMPlayer::CheckImage() { if( m_hasimages && m_img_provider ) { if( ( m_index % the_setup.ImageShowDuration == 0 && m_index > m_lastshow) || m_lastshow < 0 ) { // all n decoding steps string source; m_current_image = m_img_provider->getImagePath( source ); // check for TFT display of image if( !source.empty() ) { TransferImageTFT( source ); } // check for background display of image if( !m_current_image.empty() ) { if( the_setup.BackgrMode == 1 ) { ShowImage(); } m_lastshow = m_index; } } } } void mgPCMPlayer::ShowImage( ) { // show image .mpg referred in m_current_image uchar *buffer; int fd; struct stat st; struct video_still_picture sp; if( (fd = open( m_current_image.c_str(), O_RDONLY ) ) >= 0 ) { fstat (fd, &st); sp.iFrame = (char *) malloc (st.st_size); if( sp.iFrame ) { sp.size = st.st_size; if( read (fd, sp.iFrame, sp.size) > 0 ) { buffer = (uchar *) sp.iFrame; if( the_setup.UseDeviceStillPicture ) { cCondWait::SleepMs(80); DeviceStillPicture( buffer, sp.size ); } else { for (int i = 1; i <= 25; i++) { send_pes_packet (buffer, sp.size, i); } } } free (sp.iFrame); } else { esyslog ("muggle[%d]: cannot allocate memory (%d bytes) for still image", getpid(), (int) st.st_size); } close (fd); } else { esyslog ("muggle[%d]: cannot open image file '%s'", getpid(), m_current_image.c_str() ); } } void mgPCMPlayer::send_pes_packet(unsigned char *data, int len, int timestamp) { #define PES_MAX_SIZE 2048 int ptslen = timestamp ? 5 : 1; static unsigned char pes_header[PES_MAX_SIZE]; pes_header[0] = pes_header[1] = 0; pes_header[2] = 1; pes_header[3] = 0xe0; while(len > 0) { int payload_size = len; if(6 + ptslen + payload_size > PES_MAX_SIZE) payload_size = PES_MAX_SIZE - (6 + ptslen); pes_header[4] = (ptslen + payload_size) >> 8; pes_header[5] = (ptslen + payload_size) & 255; if(ptslen == 5) { int x; x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1; pes_header[8] = x; x = ((((timestamp >> 15) & 0x7fff) << 1) | 1); pes_header[7] = x >> 8; pes_header[8] = x & 255; x = ((((timestamp) & 0x7fff) < 1) | 1); pes_header[9] = x >> 8; pes_header[10] = x & 255; } else { pes_header[6] = 0x0f; } memcpy(&pes_header[6 + ptslen], data, payload_size); #if VDRVERSNUM >= 10318 PlayPes(pes_header, 6 + ptslen + payload_size); #else PlayVideo(pes_header, 6 + ptslen + payload_size); #endif len -= payload_size; data += payload_size; ptslen = 1; } } void mgPCMPlayer::TransferImageTFT( string cover ) { cPlugin * graphtft = cPluginManager::GetPlugin("graphtft"); if( graphtft ) { graphtft->SetupParse( "CoverImage", cover.c_str() ); } } // --- mgPlayerControl ------------------------------------------------------- mgPlayerControl::mgPlayerControl (mgSelection * plist):cControl (player = new mgPCMPlayer (plist)) { #if VDRVERSNUM >= 10307 m_display = NULL; m_menu = NULL; #endif m_visible = false; m_has_osd = false; m_track_view = true; m_progress_view = true; m_szLastShowStatusMsg = NULL; // Notify all cStatusMonitor StatusMsgReplaying (); } mgPlayerControl::~mgPlayerControl () { // Stop(); // Notify cleanup all cStatusMonitor #if VDRVERSNUM >= 10338 cStatus::MsgReplaying (this, 0, 0, false); #else cStatus::MsgReplaying (this, 0); #endif if (m_szLastShowStatusMsg) { free (m_szLastShowStatusMsg); m_szLastShowStatusMsg = NULL; } InternalHide (); Stop (); } bool mgPlayerControl::Active (void) { return player && player->Active (); } bool mgPlayerControl::Playing (void) { return player && player->Playing (); } void mgPlayerControl::Stop (void) { #if VDRVERSNUM >= 10338 cStatus::MsgReplaying( this, 0, 0, false ); #else cStatus::MsgReplaying( this, 0); #endif if (player) { delete player; player = 0; } } void mgPlayerControl::Pause (void) { if (player) { player->Pause (); } } void mgPlayerControl::Play (void) { if (player) { player->Play (); } } void mgPlayerControl::Forward (void) { if (player) { player->Forward (); } } void mgPlayerControl::Backward (void) { if (player) { player->Backward (); } } void mgPlayerControl::SkipSeconds(int Seconds) { if (player) { player->SkipSeconds(Seconds); } } void mgPlayerControl::Goto (int Position, bool Still) { if (player) { player->Goto (Position, Still); } } void mgPlayerControl::ToggleShuffle (void) { if (player) { player->ToggleShuffle (); } } void mgPlayerControl::ToggleLoop (void) { if (player) { player->ToggleLoop (); } } void mgPlayerControl::ReloadPlaylist () { if (player) { player->ReloadPlaylist (); } } void mgPlayerControl::NewPlaylist (mgSelection * plist) { if (player) { player->NewPlaylist (plist); } } void mgPlayerControl::NewImagePlaylist( const char *directory ) { if (player) { // cout << "Signaling new image playlist to player: " << directory << endl << flush; player->NewImagePlaylist (directory); } } void mgPlayerControl::ShowContents () { #if VDRVERSNUM >= 10307 if (!m_menu) { m_menu = Skins.Current ()->DisplayMenu (); } if (player && m_menu) { int num_items = m_menu->MaxItems (); if (m_track_view) { m_menu->Clear (); m_menu->SetTitle ("Track info view"); m_menu->SetTabs (15); char *buf; if (num_items > 0) { asprintf (&buf, "Title:\t%s", player->getCurrent ()->getTitle ().c_str ()); m_menu->SetItem (buf, 0, false, false); free (buf); } if (num_items > 1) { asprintf (&buf, "Artist:\t%s", player->getCurrent ()->getArtist ().c_str ()); m_menu->SetItem (buf, 1, false, false); free (buf); } if (num_items > 2) { asprintf (&buf, "Album:\t%s", player->getCurrent ()->getAlbum ().c_str ()); m_menu->SetItem (buf, 2, false, false); free (buf); } if (num_items > 3) { asprintf (&buf, "Genre:\t%s", player->getCurrent ()->getGenre ().c_str ()); m_menu->SetItem (buf, 3, false, false); free (buf); } if( num_items > 4 ) { asprintf (&buf, "Year:\t%d", player->getCurrent ()->getYear () ); m_menu->SetItem (buf, 4, false, false); free (buf); } if (num_items > 5) { int len = player->getCurrent ()->getDuration (); asprintf (&buf, "Length:\t%s", #if VDRVERSNUM >= 10318 *IndexToHMSF (SecondsToFrames (len))); #else IndexToHMSF (SecondsToFrames (len))); #endif m_menu->SetItem (buf, 5, false, false); free (buf); } if (num_items > 6) { asprintf (&buf, "Bit rate:\t%s", player->getCurrent ()->getBitrate ().c_str ()); m_menu->SetItem (buf, 6, false, false); free (buf); } if (num_items > 7) { int sr = player->getCurrent ()->getSampleRate (); asprintf (&buf, "Sampling rate:\t%d", sr); m_menu->SetItem (buf, 7, false, false); free (buf); } if (num_items > 8) { int t = player->getCurrent ()->getTrack(); asprintf (&buf, "File name:\t%d", t); m_menu->SetItem (buf, 8, false, false); free (buf); } if (num_items > 9) { string sf = player->getCurrent ()->getSourceFile (); char *p = strrchr(sf.c_str(),'/'); asprintf (&buf, "File name:\t%s", p+1); m_menu->SetItem (buf, 9, false, false); free (buf); } } else { mgSelection *list = player->getPlaylist (); if (list) { // use items for playlist tag display m_menu->Clear (); m_menu->SetTitle ("Now playing"); m_menu->SetTabs (25); int cur = list->getItemPosition (); for (int i = 0; i < num_items; i++) { mgItemGd *item = dynamic_cast(list->getItem (cur + i)); if (item) { char *buf; asprintf (&buf, "%s\t%s", item->getTitle ().c_str (), item->getArtist ().c_str ()); m_menu->SetItem (buf, i, i == 0, i >= 0); free (buf); } } } } } #endif } void mgPlayerControl::ShowProgress () { if (player) { char *buf; bool play = true, forward = true; int speed = -1; int current_frame, total_frames; player->GetIndex (current_frame, total_frames); if (!m_track_view) { // playlist stuff mgSelection *list = player->getPlaylist (); if (list) { total_frames = SecondsToFrames (list->getLength ()); current_frame += SecondsToFrames (list->getCompletedLength ()); asprintf (&buf, "(%d/%zd) %s:%s", list->getItemPosition () + 1, list->items().size(), player->getCurrent ()->getArtist ().c_str (), player->getCurrent ()->getTitle ().c_str ()); } } else { // track view asprintf (&buf, "%s: %s", player->getCurrent ()->getArtist ().c_str (), player->getCurrent ()->getTitle ().c_str ()); } #if VDRVERSNUM >= 10307 if (!m_display) { m_display = Skins.Current ()->DisplayReplay (false); } if (m_display) { m_display->SetProgress (current_frame, total_frames); m_display->SetCurrent (IndexToHMSF (current_frame)); m_display->SetTotal (IndexToHMSF (total_frames)); m_display->SetTitle (buf); m_display->SetMode (play, forward, speed); m_display->Flush (); } #else int w = Interface->Width (); int h = Interface->Height (); Interface->WriteText (w / 2, h / 2, "Muggle is active!"); Interface->Flush (); #endif free (buf); } } void mgPlayerControl::Display () { if (m_visible) { if (!m_has_osd) { // open the osd if its not already there... #if VDRVERSNUM >= 10307 #else Interface->Open (); #endif m_has_osd = true; } // now an osd is open, go on if (m_progress_view) { #if VDRVERSNUM >= 10307 if (m_menu) { delete m_menu; m_menu = NULL; } #endif ShowProgress (); } else { #if VDRVERSNUM >= 10307 if (m_display) { delete m_display; m_display = NULL; } #endif ShowContents (); } } else { InternalHide (); } } void mgPlayerControl::Hide () { m_visible = false; InternalHide (); } void mgPlayerControl::InternalHide () { if (m_has_osd) { #if VDRVERSNUM >= 10307 if (m_display) { delete m_display; m_display = NULL; } if (m_menu) { delete m_menu; m_menu = NULL; } #else Interface->Close (); #endif m_has_osd = false; } } eOSState mgPlayerControl::ProcessKey (eKeys key) { if( key != kNone ) { mgDebug (1,"mgPlayerControl::ProcessKey(%u)",key); } if (!Active ()) { return osEnd; } StatusMsgReplaying (); Display (); eOSState state = cControl::ProcessKey (key); if (state == osUnknown) { switch (key) { case kUp: { if (m_visible && !m_progress_view && !m_track_view) { Backward(); } else { Forward (); } Display(); } break; case kDown: { if (m_visible && !m_progress_view && !m_track_view) { Forward (); } else { Backward(); } } break; case kRed: { if (!m_visible && player) { mgSelection * pl = player->getPlaylist (); std::string s; switch (pl->toggleLoopMode ()) { case mgSelection::LM_NONE: { s = tr ("Loop mode off"); } break; case mgSelection::LM_SINGLE: { s = tr ("Loop mode single"); } break; case mgSelection::LM_FULL: { s = tr ("Loop mode full"); } break; default: { s = tr ("Unknown loop mode"); } } #if VDRVERSNUM >= 10307 Skins.Message (mtInfo, s.c_str ()); Skins.Flush (); #else Interface->Status (s.c_str ()); Interface->Flush (); #endif } else { // toggle progress display between simple and detail m_progress_view = !m_progress_view; Display (); } } break; case kGreen: { if (!m_visible && player) { mgSelection * pl = player->getPlaylist (); std::string s; switch (pl->toggleShuffleMode ()) { case mgSelection::SM_NONE: { s = tr ("Shuffle mode off"); } break; case mgSelection::SM_NORMAL: { s = tr ("Shuffle mode normal"); } break; case mgSelection::SM_PARTY: { s = tr ("Shuffle mode party"); } break; default: { s = tr ("Unknown shuffle mode"); } } #if VDRVERSNUM >= 10307 Skins.Message (mtInfo, s.c_str ()); Skins.Flush (); #else Interface->Status (s.c_str ()); Interface->Flush (); #endif } else { // toggle progress display between playlist and track m_track_view = !m_track_view; Display (); } } break; case kPause: case kYellow: { Pause (); } break; case kStop: case kBlue: { InternalHide (); Stop (); return osEnd; } break; case kOk: { m_visible = !m_visible; Display (); return osContinue; } break; case kBack: { InternalHide (); Stop (); return osEnd; } break; case kLeft: { SkipSeconds( -60 ); Display(); } break; case kRight: { SkipSeconds( 60 ); Display(); } break; default: { return osUnknown; } } } return osContinue; } void mgPlayerControl::StatusMsgReplaying () { MGLOG ("mgPlayerControl::StatusMsgReplaying()"); char *szBuf = NULL; mgSelection * sel = NULL; mgItemGd * item = NULL; if (player) { sel = player->getPlaylist(); item = dynamic_cast(player->getCurrent ()); } if (player && sel && item) { char cLoopMode; char cShuffle; switch (sel->getLoopMode ()) { default: case mgSelection::LM_NONE: cLoopMode = '.'; // Loop mode off break; case mgSelection::LM_SINGLE: cLoopMode = 'S'; // Loop mode single break; case mgSelection::LM_FULL: cLoopMode = 'P'; // Loop mode fuel break; } switch (sel->getShuffleMode ()) { default: case mgSelection::SM_NONE: cShuffle = '.'; // Shuffle mode off break; case mgSelection::SM_NORMAL: cShuffle = 'S'; // Shuffle mode normal break; case mgSelection::SM_PARTY: cShuffle = 'P'; // Shuffle mode party break; } if (item->getArtist ().length () > 0) { asprintf (&szBuf, "[%c%c] (%d/%zd) %s - %s", cLoopMode, cShuffle, sel->getItemPosition () + 1, sel->items().size(), item->getArtist ().c_str (), item->getTitle ().c_str ()); } else { asprintf (&szBuf, "[%c%c] (%d/%zd) %s", cLoopMode, cShuffle, sel->getItemPosition () + 1, sel->items().size(), item->getTitle ().c_str ()); } } else { asprintf (&szBuf, "[muggle]"); } //fprintf(stderr,"StatusMsgReplaying(%s)\n",szBuf); if (szBuf) { if (m_szLastShowStatusMsg == NULL || 0 != strcmp (szBuf, m_szLastShowStatusMsg)) { if (m_szLastShowStatusMsg) { free (m_szLastShowStatusMsg); } m_szLastShowStatusMsg = szBuf; #if VDRVERSNUM >= 10338 cStatus::MsgReplaying (this, m_szLastShowStatusMsg, 0, true); #else cStatus::MsgReplaying (this, m_szLastShowStatusMsg); #endif } else { free (szBuf); } } }