diff options
author | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2005-03-22 06:47:53 +0000 |
---|---|---|
committer | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2005-03-22 06:47:53 +0000 |
commit | e2de0c5ed7bbbe4b236246e8bfd71cc87c8d974f (patch) | |
tree | 616f2f0a482597e3968e281ccf8adcfd04f45bbc /muggle-plugin/vdr_player.c | |
parent | 101360901576c7e91196de60e2e6ebd6a4b145dd (diff) | |
download | vdr-plugin-muggle-0.1.6-BETA.tar.gz vdr-plugin-muggle-0.1.6-BETA.tar.bz2 |
Added 0.1.6 beta tag0.1.6-BETA
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/tags/0.1.6-BETA@586 e10066b5-e1e2-0310-b819-94efdf66514b
Diffstat (limited to 'muggle-plugin/vdr_player.c')
-rw-r--r-- | muggle-plugin/vdr_player.c | 1808 |
1 files changed, 1808 insertions, 0 deletions
diff --git a/muggle-plugin/vdr_player.c b/muggle-plugin/vdr_player.c new file mode 100644 index 0000000..01e7337 --- /dev/null +++ b/muggle-plugin/vdr_player.c @@ -0,0 +1,1808 @@ +/*! + * \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 <huels@iname.com> + */ + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <unistd.h> +#include <math.h> + +#include <string> +#include <iostream> + +#include <mad.h> + +#include <player.h> +#include <device.h> +#include <thread.h> +#include <ringbuffer.h> +#include <tools.h> +#include <recording.h> +#include <status.h> + +#include "vdr_player.h" +#include "vdr_decoder.h" +#include "vdr_config.h" +#include "vdr_setup.h" +#include "i18n.h" + +#include "mg_tools.h" + +// ---------------------------------------------------------------- + +// 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<<LIM_SHIFT) // lookup table jump between values + +// cLevel +#define POW_WIN 100 // window width for smoothing power values +#define EPSILON 0.00000000001 // anything less than EPSILON is considered zero + +// cMP3Player +#define MAX_FRAMESIZE 2048 // max. frame size allowed for DVB driver +#define HDR_SIZE 9 +#define LPCM_SIZE 7 +#define LEN_CORR 3 + // play time to fill buffers before speed check starts (ms) +#define SPEEDCHECKSTART ((MP3BUFSIZE*1000/32000/2/2)+1000) +#define SPEEDCHECKTIME 3000 // play time for speed check (ms) + +/* +struct LPCMHeader { int id:8; // id + int frame_count:8; // number of frames + int access_ptr:16; // first acces unit pointer, i.e. start of audio frame + bool emphasis:1; // audio emphasis on-off + bool mute:1; // audio mute on-off + bool reserved:1; // reserved + int frame_number:5; // audio frame number + int quant_wlen:2; // quantization word length + int sample_freq:2; // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3) + bool reserved2:1; // reserved +int chan_count:3; // number of audio channels - 1 (e.g. stereo = 1) +int dyn_range_ctrl:8; // dynamic range control (0x80 if off) +}; +*/ + +struct LPCMFrame +{ + unsigned char PES[HDR_SIZE]; + unsigned char LPCM[LPCM_SIZE]; + unsigned char Data[MAX_FRAMESIZE - HDR_SIZE - LPCM_SIZE]; +}; + +#include "vdr_sound.c" + +// --- mgPCMPlayer ---------------------------------------------------------- + +/*! + * \brief a generic PCM player class + * + * this class implements a state machine that obtains decoded data from a generic data + * and moves it to the DVB device. It inherits from cPlayer in order to be hooked into + * VDR as a player and inherits from cThread in order to implement a separate thread + * for the decoding process. + */ +class mgPCMPlayer:public cPlayer, cThread +{ + private: + +//! \brief indicates, whether the player is currently active + bool m_active; + +//! \brief indicates, whether the player has been started + bool m_started; + +//! \brief a buffer for decoded sound + cRingBufferFrame *m_ringbuffer; + +//! \brief a mutex for the playmode + cMutex m_playmode_mutex; + +//! \brief a condition to signal playmode changes + cCondVar m_playmode_cond; + +//! \brief the current playlist + mgSelection *m_playlist; + +//! \brief the currently played or to be played item + mgContentItem *m_current; + +//! \brief the currently playing item + mgContentItem *m_playing; + +//! \brief the decoder responsible for the currently playing item + mgDecoder *m_decoder; + + cFrame *m_rframe, *m_pframe; + + enum ePlayMode + { + pmPlay, + pmStopped, + pmPaused, + pmStartup + }; + ePlayMode m_playmode; + + enum eState + { + msStart, msStop, + msDecode, msNormalize, + msResample, msOutput, + msError, msEof, msWait + }; + eState m_state; + + bool levelgood; + unsigned int dvbSampleRate; + +// + int m_index; + + void Empty (); + bool SkipFile (int step=0); + void PlayTrack(); + void StopPlay (); + + void SetPlayMode (ePlayMode mode); + void WaitPlayMode (ePlayMode mode, bool inv); + + int skip_direction; + + protected: + virtual void Activate (bool On); + virtual void Action (void); + + public: + mgPCMPlayer (mgSelection * plist); + virtual ~ mgPCMPlayer (); + + bool Active () + { + return m_active; + } + + void Pause (); + void Play (); + void Forward (); + void Backward (); + + void Goto (int Index, bool Still = false); + void SkipSeconds (int secs); + void ToggleShuffle (void); + void ToggleLoop (void); + + virtual bool GetIndex (int &Current, int &Total, bool SnapToIFrame = false); +// bool GetPlayInfo(cMP3PlayInfo *rm); // LVW + + void ReloadPlaylist (); + void NewPlaylist (mgSelection * plist); + mgContentItem *getCurrent () + { + return m_current; + } + mgSelection *getPlaylist () + { + return m_playlist; + } +}; + +mgPCMPlayer::mgPCMPlayer (mgSelection * plist):cPlayer (the_setup. +BackgrMode ? pmAudioOnly : +pmAudioOnlyBlack) +{ + m_playlist = plist; + + m_active = true; + m_started = false; + + m_ringbuffer = new cRingBufferFrame (MP3BUFSIZE); + + m_rframe = 0; + m_pframe = 0; + m_decoder = 0; + + m_playmode = pmStartup; + m_state = msStop; + + m_index = 0; + m_playing = 0; + m_current = 0; + skip_direction = 1; +} + + +mgPCMPlayer::~mgPCMPlayer () +{ + Detach (); + delete m_playlist; + delete m_current; + delete m_ringbuffer; +} + +void +mgPCMPlayer::PlayTrack() +{ + mgContentItem * newcurr = m_playlist->getCurrentTrack (); + if (newcurr) + { + delete m_current; + m_current = new mgContentItem(newcurr); + } + Play (); +} + +void +mgPCMPlayer::Activate (bool on) +{ + MGLOG ("mgPCMPlayer::Activate"); + if (on) + { + if (m_playlist && !m_started) + { + m_playmode = pmStartup; + Start (); + + m_started = true; + delete m_current; + 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(1); + Play (); + } + Unlock (); +} + +void +mgPCMPlayer::NewPlaylist (mgSelection * plist) +{ + MGLOG ("mgPCMPlayer::NewPlaylist"); + + Lock (); + StopPlay (); + + delete m_playlist; + m_playlist = plist; + PlayTrack(); + Unlock (); +} + +void +mgPCMPlayer::SetPlayMode (ePlayMode mode) +{ + m_playmode_mutex.Lock (); + if (mode != m_playmode) + { + m_playmode = mode; + m_playmode_cond.Broadcast (); + } + m_playmode_mutex.Unlock (); +} + + +void +mgPCMPlayer::WaitPlayMode (ePlayMode 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 + + 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[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: + { + m_index = 0; + m_playing = m_current; + + if (m_playing) + { + std::string filename = m_playing->getSourceFile (); + if ((m_decoder = mgDecoders::findDecoder (m_playing)) + && 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: + { + 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) + { + std:: + cout << "mgPCMPlayer::Action: new input sample rate " + << pcm->samplerate << std::endl << std::flush; + 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; + dvbSampleRate = 44100; + } + break; + case 8000: + case 16000: + case 32000: + { + lpcmFrame.LPCM[5] |= 3 << 4; + 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; + 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 = 0; + 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) + { +#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 = 0; + + SetPlayMode (pmStopped); + + 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 (int step) +{ + MGLOG("mgPCMPlayer::SkipFile"); + mgDebug(1,"SkipFile:step=%d, skip_direction=%d",step,skip_direction); + mgContentItem * newcurr = NULL; + if (step!=0) + skip_direction=step; + if (m_playlist->skipTracks (skip_direction)) + { + newcurr = m_playlist->getCurrentTrack (); + if (newcurr) { + delete m_current; + m_current = new mgContentItem(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 (1)) + { + StopPlay (); + Play (); + } + Unlock (); +} + + +void +mgPCMPlayer::Backward (void) +{ + MGLOG ("mgPCMPlayer::Backward"); + Lock (); + if (SkipFile (-1)) + { + StopPlay (); + Play (); + } + Unlock (); +} + + +void +mgPCMPlayer::Goto (int index, bool still) +{ + m_playlist->setTrackPosition (index - 1); + mgContentItem *next = m_playlist->getCurrentTrack (); + + if (next) + { + Lock (); + StopPlay (); + delete m_current; + m_current = new mgContentItem(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; +} + + +/* +string mgPCMPlayer::CheckImage( string fileName, size_t j ) +{ +static char tmpFile[1024]; +char *tmp[2048]; +char *result = NULL; +FILE *fp; + +sprintf (tmpFile, "%s/%s", MP3Setup.ImageCacheDir, &fileName[j]); // ??? +strcpy (strrchr (tmpFile, '.'), ".mpg"); +d(printf("mp3[%d]: cache %s\n", getpid (), tmpFile)) +if ((fp = fopen (tmpFile, "rb"))) +{ +fclose (fp); +result = tmpFile; +} +else +{ +if ((fp = fopen (fileName, "rb"))) +{ +fclose (fp); +d(printf("mp3[%d]: image %s found\n", getpid (), fileName)) +sprintf ((char *) tmp, "image_convert.sh \"%s\" \"%s\"", fileName, tmpFile); +system ((const char*) tmp); +result = tmpFile; +} +} +fp = fopen ("/tmp/vdr_mp3_current_image.txt", "w"); +fprintf (fp, "%s\n", fileName); +fclose (fp); +return result; +} + +char *cMP3Player::LoadImage(const char *fullname) +{ +size_t i, j = strlen (MP3Sources.GetSource()->BaseDir()) + 1; +char imageFile[1024]; +static char mpgFile[1024]; +char *p, *q = NULL; +char *imageSuffixes[] = { "png", "gif", "jpg" }; + +d(printf("mp3[%d]: checking %s for images\n", getpid (), fullname)) +strcpy (imageFile, fullname); +strcpy (mpgFile, ""); +// +// track specific image, e.g. <song>.jpg +// +p = strrchr (imageFile, '.'); +if (p) +{ +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +strcpy (p + 1, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, j); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +// +// album specific image, e.g. cover.jpg in song directory +// +if (!strlen (mpgFile)) +{ +p = strrchr (imageFile, '/'); +if (p) +{ +strcpy (p + 1, "cover."); +p += 6; +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +strcpy (p + 1, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, j); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +} +// +// artist specific image, e.g. artist.jpg in artist directory +// +if (!strlen (mpgFile)) +{ +p = strrchr (imageFile, '/'); +if (p) +{ +*p = '\0'; +p = strrchr (imageFile, '/'); +} +if (p) +{ +strcpy (p + 1, "artist."); +p += 7; +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +strcpy (p + 1, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, j); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +} +// +// default background image +// +if (!strlen (mpgFile)) +{ +for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) +{ +sprintf (imageFile, "%s/background.%s", MP3Setup.ImageCacheDir, imageSuffixes[i]); +d(printf("mp3[%d]: %s\n", getpid (), imageFile)) +q = CheckImage (imageFile, strlen(MP3Setup.ImageCacheDir) + 1); +if (q) +{ +strcpy (mpgFile, q); +} +} +} +if (!strlen (mpgFile)) +{ +sprintf (mpgFile, "%s/background.mpg", MP3Setup.ImageCacheDir); +} +return mpgFile; +} + +void mgPCMPlayer::ShowImage (char *file) +{ +uchar *buffer; +int fd; +struct stat st; +struct video_still_picture sp; + +if ((fd = open (file, O_RDONLY)) >= 0) +{ +d(printf("mp3[%d]: cover still picture %s\n", getpid (), file)) +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; +d(printf("mp3[%d]: new image frame (size %d)\n", getpid(), sp.size)) +if(MP3Setup.UseDeviceStillPicture) +DeviceStillPicture (buffer, sp.size); +else +{ +for (int i = 1; i <= 25; i++) +{ +send_pes_packet (buffer, sp.size, i); +} +} +} +free (sp.iFrame); +} +else +{ +esyslog ("mp3[%d]: cannot allocate memory (%d bytes) for still image", +getpid(), (int) st.st_size); +} +close (fd); +} +else +{ +esyslog ("mp3[%d]: cannot open image file '%s'", +getpid(), file); +} +} + +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; +} +} +*/ + +// --- 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 + cStatus::MsgReplaying (this, NULL); + if (m_szLastShowStatusMsg) + { + free (m_szLastShowStatusMsg); + m_szLastShowStatusMsg = NULL; + } + + InternalHide (); + Stop (); +} + + +bool mgPlayerControl::Active (void) +{ + return player && player->Active (); +} + + +void +mgPlayerControl::Stop (void) +{ + 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::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) + { + 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, 4, false, false); + free (buf); + } + if (num_items > 5) + { + asprintf (&buf, "Bit rate:\t%s", + player->getCurrent ()->getBitrate ().c_str ()); + m_menu->SetItem (buf, 5, false, false); + free (buf); + } + if (num_items > 6) + { + int sr = player->getCurrent ()->getSampleRate (); + + asprintf (&buf, "Sampling rate:\t%d", sr); + m_menu->SetItem (buf, 6, false, false); + free (buf); + } + if (num_items > 6) + { + string sf = player->getCurrent ()->getSourceFile (); + char *p = strrchr(sf.c_str(),'/'); + asprintf (&buf, "File name:\t%s", p+1); + m_menu->SetItem (buf, 7, 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->getTrackPosition (); + for (int i = 0; i < num_items; i++) + { + mgContentItem *item = list->getTrack (cur - 3 + i); + if (item) + { + char *buf; + asprintf (&buf, "%s\t%s", item->getTitle ().c_str (), + item->getArtist ().c_str ()); + m_menu->SetItem (buf, i, i == 3, i > 3); + 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, "%s (%d/%d)", list->getListname ().c_str (), + list->getTrackPosition () + 1, list->getNumTracks ()); + } + } + 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 (); + } + 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; + default: + { + return osUnknown; + } + } + } + return osContinue; +} + + +void +mgPlayerControl::StatusMsgReplaying () +{ + MGLOG ("mgPlayerControl::StatusMsgReplaying()"); + char *szBuf = NULL; + if (player && player->getCurrent () && player->getPlaylist ()) + { + char cLoopMode; + char cShuffle; + + switch (player->getPlaylist ()->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 (player->getPlaylist ()->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; + } + + mgContentItem *tmp = player->getCurrent (); + if (tmp == NULL) + mgError("mgPlayerControl::StatusMsgReplaying: getCurrent() is NULL"); + if (tmp->getArtist ().length () > 0) + { + asprintf (&szBuf, "[%c%c] (%d/%d) %s - %s", + cLoopMode, + cShuffle, + player->getPlaylist ()->getTrackPosition () + 1, + player->getPlaylist ()->getNumTracks (), + player->getCurrent ()->getArtist ().c_str (), + player->getCurrent ()->getTitle ().c_str ()); + } + else + { + asprintf (&szBuf, "[%c%c] (%d/%d) %s", + cLoopMode, + cShuffle, + player->getPlaylist ()->getTrackPosition () + 1, + player->getPlaylist ()->getNumTracks (), + player->getCurrent ()->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; + cStatus::MsgReplaying (this, m_szLastShowStatusMsg); + } + else + { + free (szBuf); + } + } +} |