OBJ_COMMON = command.o tcp.o dsock.o thread.o timers.o i18n.o vdp6.o \
message.o messagequeue.o wol.o audio.o video.o log.o \
vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o \
- directory.o mark.o option.o playerradio.o vfeed.o afeed.o \
+ directory.o mark.o option.o vfeed.o afeed.o \
demuxer.o demuxervdr.o demuxerts.o stream.o osd.o surface.o \
region.o colour.o boxstack.o boxx.o tbboxx.o vrecording.o \
vinfo.o vquestion.o vrecordinglist.o vrecordinglistclassic.o \
teletextdecodervbiebu.o teletxt/txtfont.o movieinfo.o seriesinfo.o \
wmovieview.o wseriesview.o tvmedia.o wtvmedia.o wpictureview.o \
osdvector.o surfacevector.o buffer.o \
- playervideorec.o playervideolive.o playerradiolive.o
+ playervideorec.o playervideolive.o playerradiolive.o playerradiorec.o
OBJ_RASPBERRY = main.o threadp.o osdopenvg.o \
ledraspberry.o videoomx.o audioomx.o imageomx.o \
+++ /dev/null
-/*
- Copyright 2004-2006 Chris Tallon
-
- This file is part of VOMP.
-
- VOMP is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- VOMP is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with VOMP. If not, see <https://www.gnu.org/licenses/>.
-*/
-
-#include "log.h"
-#include "audio.h"
-#include "video.h"
-#include "demuxervdr.h"
-#include "demuxerts.h"
-#include "input.h"
-#include "vdr.h"
-#include "message.h"
-#include "messagequeue.h"
-
-#include "playerradio.h"
-
-// ----------------------------------- Called from outside, one offs or info funcs
-
-PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver)
-: messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this)
-{
- audio = Audio::getInstance();
- logger = Log::getInstance();
- vdr = VDR::getInstance();
- Video::getInstance()->turnVideoOff();
-}
-
-PlayerRadio::~PlayerRadio()
-{
- if (initted) shutdown();
-}
-
-bool PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthFrames, bool isPesRecording)
-{
- if (initted) return false;
-
- if (isPesRecording)
- demuxer = new DemuxerVDR();
- else
- demuxer = new DemuxerTS();
- if (!demuxer) return false;
-
- if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
- {
- logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
- shutdown();
- return false;
- }
-
- audio->stop();
-
- lengthBytes = tlengthBytes;
- lengthFrames = tlengthFrames;
-
- logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
-
- UINT thisRead = 0;
- int success;
-
- UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
- if (!buffer)
- {
- logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
- shutdown();
- if (!vdr->isConnected()) doConnectionLost();
- return false;
- }
-
- success = demuxer->findPTS(buffer, thisRead, &startPTS);
- if (!success)
- {
- logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
- free(buffer);
- shutdown();
- return false;
- }
-
- free(buffer);
-
- if (!setLengthSeconds())
- {
- logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
- shutdown();
- return false;
- }
-
- initted = true;
- return true;
-}
-
-bool PlayerRadio::setLengthSeconds()
-{
- int success;
- ULLONG endPTS = 0;
- UINT thisRead = 0;
- UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
- if (!buffer)
- {
- logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
- if (!vdr->isConnected()) doConnectionLost();
- return false;
- }
-
- success = demuxer->findPTS(buffer, thisRead, &endPTS);
- free(buffer);
- if (!success)
- {
- logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
- return false;
- }
-
- if (startPTS < endPTS)
- {
- lengthSeconds = static_cast<ULONG>((endPTS - startPTS) / 90000);
- }
- else
- {
- lengthSeconds = static_cast<ULONG>((startPTS - endPTS) / 90000);
- }
-
- return true;
-}
-
-int PlayerRadio::shutdown()
-{
- if (!initted) return 0;
- switchState(S_STOP);
- initted = false;
-
- delete demuxer;
- demuxer = NULL;
-
- return 1;
-}
-
-
-void PlayerRadio::setCurrentFrameNumber(ULONG num)
-{
- currentFrameNumber = num;
-}
-
-ULONG PlayerRadio::getLengthSeconds()
-{
- return lengthSeconds;
-}
-
-ULONG PlayerRadio::getCurrentSeconds()
-{
- if (startup) return 0;
-
- long long currentPTS = demuxer->getAudioPTS();
- currentPTS -= startPTS;
- if (currentPTS < 0) currentPTS += 8589934592ULL;
- ULONG ret = static_cast<ULONG>(currentPTS / 90000);
- return ret;
-}
-
-// ----------------------------------- Externally called events
-
-void PlayerRadio::play()
-{
- if (!initted) return;
- if (state == S_PLAY) return;
- stateLock.lock();
- switchState(S_PLAY);
- stateLock.unlock();
-}
-
-
-void PlayerRadio::playpause()
-{
- if (!initted) return;
- stateLock.lock();
- if (state==S_PLAY) {
- switchState(S_PAUSE_P);
- } else {
- switchState(S_PLAY);
- }
- stateLock.unlock();
-}
-
-void PlayerRadio::stop()
-{
- if (!initted) return;
- if (state == S_STOP) return;
- stateLock.lock();
- logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
- switchState(S_STOP);
- stateLock.unlock();
-}
-
-void PlayerRadio::pause()
-{
- if (!initted) return;
- stateLock.lock();
-
- if (state == S_PAUSE_P)
- {
- switchState(S_PLAY);
- }
- else
- {
- switchState(S_PAUSE_P);
- }
-
- stateLock.unlock();
-}
-
-void PlayerRadio::jumpToPercent(double percent)
-{
- stateLock.lock();
- logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
- ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
- switchState(S_JUMP, newFrame);
- stateLock.unlock();
-}
-
-void PlayerRadio::skipForward(UINT seconds)
-{
- stateLock.lock();
- logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
- ULONG currentSeconds = getCurrentSeconds();
- ULONG currentFrame = demuxer->getPacketNum();
-
- if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
- if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
-
- ULONG newFrame = currentFrame + (currentFrame * seconds / currentSeconds);
- if (newFrame > lengthFrames) { switchState(S_PLAY); stateLock.unlock(); }
- else switchState(S_JUMP, newFrame);
- stateLock.unlock();
-}
-
-void PlayerRadio::skipBackward(UINT seconds)
-{
- stateLock.lock();
- logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
-
- ULONG currentSeconds = getCurrentSeconds();
- ULONG currentFrame = demuxer->getPacketNum();
-
- if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
- if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
-
- ULONG newFrame;
- if (seconds > currentSeconds)
- newFrame = 0;
- else
- newFrame = currentFrame - (currentFrame * seconds / currentSeconds);
-
- switchState(S_JUMP, newFrame);
- stateLock.unlock();
-}
-
-// ----------------------------------- Implementations called events
-
-void PlayerRadio::switchState(UCHAR toState, ULONG jumpToFrame)
-{
- if (!initted) return;
-
- logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
-
- switch(state) // current state selector
- {
- case S_PLAY: // from S_PLAY -----------------------------------
- {
- switch(toState)
- {
- case S_PLAY: // to S_PLAY
- {
- return;
- }
- case S_PAUSE_P: // to S_PAUSE_P
- {
- audio->pause();
- state = S_PAUSE_P;
- return;
- }
- case S_STOP: // to S_STOP
- {
- afeed.stop();
- threadStop();
- audio->stop();
- audio->unPause();
- demuxer->reset();
- state = S_STOP;
- return;
- }
- case S_JUMP: // to S_JUMP
- {
- restartAtFrame(jumpToFrame);
- return;
- }
- }
- }
- FALLTHROUGH // keep compiler happy (all posibilities return)
- case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
- {
- switch(toState)
- {
- case S_PLAY: // to S_PLAY
- {
- audio->unPause();
- state = S_PLAY;
- return;
- }
- case S_PAUSE_P: // to S_PAUSE_P
- {
- return;
- }
- case S_STOP: // to S_STOP
- {
- afeed.stop();
- threadStop();
- audio->stop();
- audio->unPause();
- demuxer->reset();
- audio->systemMuteOff();
- state = S_STOP;
- return;
- }
- case S_JUMP: // to S_JUMP
- {
- state = S_PLAY;
- audio->unPause();
- restartAtFrame(jumpToFrame);
- return;
- }
- }
- }
- FALLTHROUGH // keep compiler happy (all posibilities return)
- case S_STOP: // from S_STOP -----------------------------------
- {
- switch(toState)
- {
- case S_PLAY: // to S_PLAY
- {
- startup = true;
-
- audio->reset();
- audio->setStreamType(Audio::MPEG2_PES);
- audio->systemMuteOff();
- demuxer->reset();
-
- // FIXME use restartAtFrame here?
- if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
- demuxer->setPacketNum(currentFrameNumber);
- state = S_PLAY;
- threadStart();
- logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
- afeed.start();
- audio->play();
-
- return;
- }
- case S_PAUSE_P: // to S_PAUSE_P
- {
- return;
- }
- case S_STOP: // to S_STOP
- {
- return;
- }
- case S_JUMP: // to S_JUMP
- {
- return;
- }
- }
- }
- // case S_JUMP cannot be selected as a start state because it auto flips to play
- }
-}
-
-// ----------------------------------- Internal functions
-
-void PlayerRadio::restartAtFrame(ULONG newFrame)
-{
- afeed.stop();
- threadStop();
- audio->reset();
- audio->setStreamType(Audio::MPEG2_PES);
- demuxer->flush();
- currentFrameNumber = newFrame;
- demuxer->setPacketNum(newFrame);
- afeed.start();
- threadStart();
- audio->play();
- audio->systemMuteOff();
- audio->doMuting();
-}
-
-void PlayerRadio::doConnectionLost()
-{
- logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
- Message* m = new Message();
- m->to = messageReceiver;
- m->from = this;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerRadio::CONNECTION_LOST;
- messageQueue->postMessage(m);
-}
-
-// ----------------------------------- Callback
-
-void PlayerRadio::call(void* /*caller*/)
-{
- threadSignalNoLock();
-}
-
-// ----------------------------------- Feed thread
-
-void PlayerRadio::threadMethod()
-{
- if (state == S_PLAY) threadFeedPlay();
-}
-
-void PlayerRadio::threadFeedPlay()
-{
- ULLONG feedPosition;
- UINT thisRead, writeLength, thisWrite, askFor;
- time_t lastRescan = time(NULL);
-
- feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
- if (!vdr->isConnected()) { doConnectionLost(); return; }
- logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedFrame %i goto %llu", currentFrameNumber, feedPosition);
-
-
- while(1)
- {
- thisRead = 0;
- writeLength = 0;
- thisWrite = 0;
-
- threadCheckExit();
-
- // If we havn't rescanned for a while..
- if ((lastRescan + 60) < time(NULL))
- {
- lengthBytes = vdr->rescanRecording(&lengthFrames);
- if (!vdr->isConnected()) { doConnectionLost(); return; }
- logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
- lastRescan = time(NULL);
-
- if (!setLengthSeconds())
- {
- logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
- return;
- }
- }
-
- if (feedPosition >= lengthBytes) break; // finished playback
-
- if (startup)
- {
- if (startupBlockSize > lengthBytes)
- askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
- else
- askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
- }
- else
- {
- if ((feedPosition + blockSize) > lengthBytes) // last block of recording
- askFor = static_cast<UINT>(lengthBytes - feedPosition);
- else // normal
- askFor = blockSize;
- }
-
- threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
- feedPosition += thisRead;
-
- if (!vdr->isConnected())
- {
- doConnectionLost();
- return;
- }
-
- if (!threadBuffer) break;
-
- if (startup)
- {
- int a_stream = demuxer->scan(threadBuffer, thisRead);
- demuxer->setAudioStream(a_stream);
- logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
- startup = false;
- }
-
- threadCheckExit();
-
- while(writeLength < thisRead)
- {
- thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
- writeLength += thisWrite;
-
- if (!thisWrite)
- {
- // demuxer is full and can't take anymore
- threadLock();
- threadWaitForSignal();
- threadUnlock();
- }
-
- threadCheckExit();
- }
-
- free(threadBuffer);
- threadBuffer = NULL;
-
- }
-
- // end of recording
- logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
-
- threadCheckExit();
-
-
- Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
- m->to = messageReceiver;
- m->from = this;
- m->message = Message::PLAYER_EVENT;
- m->parameter = PlayerRadio::STOP_PLAYBACK;
- logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
- messageQueue->postMessage(m);
-}
-
-void PlayerRadio::threadPostStopCleanup()
-{
- if (threadBuffer)
- {
- free(threadBuffer);
- threadBuffer = NULL;
- }
-}
-
+++ /dev/null
-/*
- Copyright 2004-2006 Chris Tallon
-
- This file is part of VOMP.
-
- VOMP is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- VOMP is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with VOMP; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-#ifndef PLAYERRADIO_H
-#define PLAYERRADIO_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifndef WIN32
-#include <sys/time.h>
-#endif
-#include <time.h>
-
-#include <mutex>
-
-#include "threadsystem.h"
-
-#include "callback.h"
-#include "defines.h"
-#include "afeed.h"
-
-class Log;
-class Audio;
-class Video;
-class Demuxer;
-class VDR;
-class MessageQueue;
-
-/*
- * Frames...
- *
- * VDR tells me there are around 8.3 "frames" per second in radio recordings.
- * I don't know where this comes from but things seem to work.
- */
-
-class PlayerRadio : public Thread_TYPE, public Callback
-{
- public:
- PlayerRadio(MessageQueue* messageQueue, void* messageReceiver);
- virtual ~PlayerRadio();
-
- bool init(ULLONG lengthBytes, ULONG lengthFrames, bool IsPesRecording);
- int shutdown();
- void setCurrentFrameNumber(ULONG num);
-
- void play();
- void stop();
- void pause();
- void playpause();
- void jumpToPercent(double percent);
- void skipForward(UINT seconds);
- void skipBackward(UINT seconds);
-
- UCHAR getState() { return state; }
- ULONG getCurrentSeconds();
- ULONG getLengthSeconds();
-
- void call(void*); // for callback interface
-
- const static UCHAR S_PLAY = 1;
- const static UCHAR S_PAUSE_P = 2;
- const static UCHAR S_PAUSE_I = 3;
- const static UCHAR S_STOP = 6;
- const static UCHAR S_JUMP = 7;
-
- // Player events
-
- const static UCHAR CONNECTION_LOST = 1;
- const static UCHAR STOP_PLAYBACK = 2;
- const static UCHAR STREAM_END = 3;
-
- protected:
- void threadMethod();
- void threadPostStopCleanup();
-
- private:
- void switchState(UCHAR newState, ULONG jumpToFrame=0);
-
- void threadFeedPlay();
- void threadFeedScan();
-
- void doConnectionLost();
- void restartAtFrame(ULONG newFrame);
- bool setLengthSeconds();
-
- MessageQueue* messageQueue;
- void* messageReceiver;
- Log* logger;
- Audio* audio;
- Demuxer* demuxer;
- VDR* vdr;
- AFeed afeed;
-
- bool initted{};
- bool startup;
-
- ULLONG startPTS{};
- ULONG lengthSeconds{};
-
- std::mutex stateLock;
-
- ULLONG lengthBytes{};
- ULONG lengthFrames{};
- ULONG currentFrameNumber{};
- static const UINT blockSize{10000};
- static const UINT startupBlockSize{20000};
- UCHAR* threadBuffer{};
- UCHAR state{S_STOP};
-};
-
-#endif
-
-
-/*
-
-Possible states:
-
-Play, Pause, FFwd, FBwd, (Stop), [Jump]
-
- Possible Working
-
-Play -> PauseP * *
- -> Stop * *
- -> Jump * *
-
-PauseP -> Play * *
- -> Stop * *
- -> Jump * *
-
-PauseI -> Play * *
- -> PauseP
- -> Stop * *
- -> Jump * *
-
-Stop -> Play * *
- -> PauseP
- -> Jump
-
-Jump -> Play
- -> PauseP
- -> Stop
-
-*/
--- /dev/null
+/*
+ Copyright 2004-2006 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ VOMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "log.h"
+#include "audio.h"
+#include "video.h"
+#include "demuxervdr.h"
+#include "demuxerts.h"
+#include "input.h"
+#include "vdr.h"
+#include "message.h"
+#include "messagequeue.h"
+
+#include "playerradiorec.h"
+
+// ----------------------------------- Called from outside, one offs or info funcs
+
+PlayerRadioRec::PlayerRadioRec(MessageQueue* tmessageQueue, void* tmessageReceiver)
+: messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this)
+{
+ audio = Audio::getInstance();
+ logger = Log::getInstance();
+ vdr = VDR::getInstance();
+ Video::getInstance()->turnVideoOff();
+}
+
+PlayerRadioRec::~PlayerRadioRec()
+{
+ if (initted) shutdown();
+}
+
+bool PlayerRadioRec::init(ULLONG tlengthBytes, ULONG tlengthFrames, bool isPesRecording)
+{
+ if (initted) return false;
+
+ if (isPesRecording)
+ demuxer = new DemuxerVDR();
+ else
+ demuxer = new DemuxerTS();
+ if (!demuxer) return false;
+
+ if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
+ {
+ logger->log("PlayerRadioRec", Log::ERR, "Demuxer failed to init");
+ shutdown();
+ return false;
+ }
+
+ audio->stop();
+
+ lengthBytes = tlengthBytes;
+ lengthFrames = tlengthFrames;
+
+ logger->log("PlayerRadioRec", Log::DEBUG, "PlayerRadioRec has received length bytes of %llu", lengthBytes);
+
+ UINT thisRead = 0;
+ int success;
+
+ UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
+ if (!buffer)
+ {
+ logger->log("PlayerRadioRec", Log::ERR, "Failed to get start block");
+ shutdown();
+ if (!vdr->isConnected()) doConnectionLost();
+ return false;
+ }
+
+ success = demuxer->findPTS(buffer, thisRead, &startPTS);
+ if (!success)
+ {
+ logger->log("PlayerRadioRec", Log::ERR, "Failed to get start PTS");
+ free(buffer);
+ shutdown();
+ return false;
+ }
+
+ free(buffer);
+
+ if (!setLengthSeconds())
+ {
+ logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds");
+ shutdown();
+ return false;
+ }
+
+ initted = true;
+ return true;
+}
+
+bool PlayerRadioRec::setLengthSeconds()
+{
+ int success;
+ ULLONG endPTS = 0;
+ UINT thisRead = 0;
+ UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
+ if (!buffer)
+ {
+ logger->log("PlayerRadioRec", Log::ERR, "Failed to get end block");
+ if (!vdr->isConnected()) doConnectionLost();
+ return false;
+ }
+
+ success = demuxer->findPTS(buffer, thisRead, &endPTS);
+ free(buffer);
+ if (!success)
+ {
+ logger->log("PlayerRadioRec", Log::ERR, "Failed to get end PTS");
+ return false;
+ }
+
+ if (startPTS < endPTS)
+ {
+ lengthSeconds = static_cast<ULONG>((endPTS - startPTS) / 90000);
+ }
+ else
+ {
+ lengthSeconds = static_cast<ULONG>((startPTS - endPTS) / 90000);
+ }
+
+ return true;
+}
+
+int PlayerRadioRec::shutdown()
+{
+ if (!initted) return 0;
+ switchState(S_STOP);
+ initted = false;
+
+ delete demuxer;
+ demuxer = NULL;
+
+ return 1;
+}
+
+
+void PlayerRadioRec::setCurrentFrameNumber(ULONG num)
+{
+ currentFrameNumber = num;
+}
+
+ULONG PlayerRadioRec::getLengthSeconds()
+{
+ return lengthSeconds;
+}
+
+ULONG PlayerRadioRec::getCurrentSeconds()
+{
+ if (startup) return 0;
+
+ long long currentPTS = demuxer->getAudioPTS();
+ currentPTS -= startPTS;
+ if (currentPTS < 0) currentPTS += 8589934592ULL;
+ ULONG ret = static_cast<ULONG>(currentPTS / 90000);
+ return ret;
+}
+
+// ----------------------------------- Externally called events
+
+void PlayerRadioRec::play()
+{
+ if (!initted) return;
+ if (state == S_PLAY) return;
+ stateLock.lock();
+ switchState(S_PLAY);
+ stateLock.unlock();
+}
+
+
+void PlayerRadioRec::playpause()
+{
+ if (!initted) return;
+ stateLock.lock();
+ if (state==S_PLAY) {
+ switchState(S_PAUSE_P);
+ } else {
+ switchState(S_PLAY);
+ }
+ stateLock.unlock();
+}
+
+void PlayerRadioRec::stop()
+{
+ if (!initted) return;
+ if (state == S_STOP) return;
+ stateLock.lock();
+ logger->log("PlayerRadioRec", Log::DEBUG, "Stop called lock");
+ switchState(S_STOP);
+ stateLock.unlock();
+}
+
+void PlayerRadioRec::pause()
+{
+ if (!initted) return;
+ stateLock.lock();
+
+ if (state == S_PAUSE_P)
+ {
+ switchState(S_PLAY);
+ }
+ else
+ {
+ switchState(S_PAUSE_P);
+ }
+
+ stateLock.unlock();
+}
+
+void PlayerRadioRec::jumpToPercent(double percent)
+{
+ stateLock.lock();
+ logger->log("PlayerRadioRec", Log::DEBUG, "JUMP TO %i%%", percent);
+ ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
+ switchState(S_JUMP, newFrame);
+ stateLock.unlock();
+}
+
+void PlayerRadioRec::skipForward(UINT seconds)
+{
+ stateLock.lock();
+ logger->log("PlayerRadioRec", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
+ ULONG currentSeconds = getCurrentSeconds();
+ ULONG currentFrame = demuxer->getPacketNum();
+
+ if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
+ if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
+
+ ULONG newFrame = currentFrame + (currentFrame * seconds / currentSeconds);
+ if (newFrame > lengthFrames) { switchState(S_PLAY); stateLock.unlock(); }
+ else switchState(S_JUMP, newFrame);
+ stateLock.unlock();
+}
+
+void PlayerRadioRec::skipBackward(UINT seconds)
+{
+ stateLock.lock();
+ logger->log("PlayerRadioRec", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
+
+ ULONG currentSeconds = getCurrentSeconds();
+ ULONG currentFrame = demuxer->getPacketNum();
+
+ if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
+ if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
+
+ ULONG newFrame;
+ if (seconds > currentSeconds)
+ newFrame = 0;
+ else
+ newFrame = currentFrame - (currentFrame * seconds / currentSeconds);
+
+ switchState(S_JUMP, newFrame);
+ stateLock.unlock();
+}
+
+// ----------------------------------- Implementations called events
+
+void PlayerRadioRec::switchState(UCHAR toState, ULONG jumpToFrame)
+{
+ if (!initted) return;
+
+ logger->log("PlayerRadioRec", Log::DEBUG, "Switch state from %u to %u", state, toState);
+
+ switch(state) // current state selector
+ {
+ case S_PLAY: // from S_PLAY -----------------------------------
+ {
+ switch(toState)
+ {
+ case S_PLAY: // to S_PLAY
+ {
+ return;
+ }
+ case S_PAUSE_P: // to S_PAUSE_P
+ {
+ audio->pause();
+ state = S_PAUSE_P;
+ return;
+ }
+ case S_STOP: // to S_STOP
+ {
+ afeed.stop();
+ threadStop();
+ audio->stop();
+ audio->unPause();
+ demuxer->reset();
+ state = S_STOP;
+ return;
+ }
+ case S_JUMP: // to S_JUMP
+ {
+ restartAtFrame(jumpToFrame);
+ return;
+ }
+ }
+ }
+ FALLTHROUGH // keep compiler happy (all posibilities return)
+ case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
+ {
+ switch(toState)
+ {
+ case S_PLAY: // to S_PLAY
+ {
+ audio->unPause();
+ state = S_PLAY;
+ return;
+ }
+ case S_PAUSE_P: // to S_PAUSE_P
+ {
+ return;
+ }
+ case S_STOP: // to S_STOP
+ {
+ afeed.stop();
+ threadStop();
+ audio->stop();
+ audio->unPause();
+ demuxer->reset();
+ audio->systemMuteOff();
+ state = S_STOP;
+ return;
+ }
+ case S_JUMP: // to S_JUMP
+ {
+ state = S_PLAY;
+ audio->unPause();
+ restartAtFrame(jumpToFrame);
+ return;
+ }
+ }
+ }
+ FALLTHROUGH // keep compiler happy (all posibilities return)
+ case S_STOP: // from S_STOP -----------------------------------
+ {
+ switch(toState)
+ {
+ case S_PLAY: // to S_PLAY
+ {
+ startup = true;
+
+ audio->reset();
+ audio->setStreamType(Audio::MPEG2_PES);
+ audio->systemMuteOff();
+ demuxer->reset();
+
+ // FIXME use restartAtFrame here?
+ if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
+ demuxer->setPacketNum(currentFrameNumber);
+ state = S_PLAY;
+ threadStart();
+ logger->log("PlayerRadioRec", Log::DEBUG, "Immediate play");
+ afeed.start();
+ audio->play();
+
+ return;
+ }
+ case S_PAUSE_P: // to S_PAUSE_P
+ {
+ return;
+ }
+ case S_STOP: // to S_STOP
+ {
+ return;
+ }
+ case S_JUMP: // to S_JUMP
+ {
+ return;
+ }
+ }
+ }
+ // case S_JUMP cannot be selected as a start state because it auto flips to play
+ }
+}
+
+// ----------------------------------- Internal functions
+
+void PlayerRadioRec::restartAtFrame(ULONG newFrame)
+{
+ afeed.stop();
+ threadStop();
+ audio->reset();
+ audio->setStreamType(Audio::MPEG2_PES);
+ demuxer->flush();
+ currentFrameNumber = newFrame;
+ demuxer->setPacketNum(newFrame);
+ afeed.start();
+ threadStart();
+ audio->play();
+ audio->systemMuteOff();
+ audio->doMuting();
+}
+
+void PlayerRadioRec::doConnectionLost()
+{
+ logger->log("PlayerRadioRec", Log::DEBUG, "Connection lost, sending message");
+ Message* m = new Message();
+ m->to = messageReceiver;
+ m->from = this;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerRadioRec::CONNECTION_LOST;
+ messageQueue->postMessage(m);
+}
+
+// ----------------------------------- Callback
+
+void PlayerRadioRec::call(void* /*caller*/)
+{
+ threadSignalNoLock();
+}
+
+// ----------------------------------- Feed thread
+
+void PlayerRadioRec::threadMethod()
+{
+ if (state == S_PLAY) threadFeedPlay();
+}
+
+void PlayerRadioRec::threadFeedPlay()
+{
+ ULLONG feedPosition;
+ UINT thisRead, writeLength, thisWrite, askFor;
+ time_t lastRescan = time(NULL);
+
+ feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
+ if (!vdr->isConnected()) { doConnectionLost(); return; }
+ logger->log("PlayerRadioRec", Log::DEBUG, "startFeedPlay: wantedFrame %i goto %llu", currentFrameNumber, feedPosition);
+
+
+ while(1)
+ {
+ thisRead = 0;
+ writeLength = 0;
+ thisWrite = 0;
+
+ threadCheckExit();
+
+ // If we havn't rescanned for a while..
+ if ((lastRescan + 60) < time(NULL))
+ {
+ lengthBytes = vdr->rescanRecording(&lengthFrames);
+ if (!vdr->isConnected()) { doConnectionLost(); return; }
+ logger->log("PlayerRadioRec", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
+ lastRescan = time(NULL);
+
+ if (!setLengthSeconds())
+ {
+ logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds in thread");
+ return;
+ }
+ }
+
+ if (feedPosition >= lengthBytes) break; // finished playback
+
+ if (startup)
+ {
+ if (startupBlockSize > lengthBytes)
+ askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
+ else
+ askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
+ }
+ else
+ {
+ if ((feedPosition + blockSize) > lengthBytes) // last block of recording
+ askFor = static_cast<UINT>(lengthBytes - feedPosition);
+ else // normal
+ askFor = blockSize;
+ }
+
+ threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
+ feedPosition += thisRead;
+
+ if (!vdr->isConnected())
+ {
+ doConnectionLost();
+ return;
+ }
+
+ if (!threadBuffer) break;
+
+ if (startup)
+ {
+ int a_stream = demuxer->scan(threadBuffer, thisRead);
+ demuxer->setAudioStream(a_stream);
+ logger->log("PlayerRadioRec", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
+ startup = false;
+ }
+
+ threadCheckExit();
+
+ while(writeLength < thisRead)
+ {
+ thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
+ writeLength += thisWrite;
+
+ if (!thisWrite)
+ {
+ // demuxer is full and can't take anymore
+ threadLock();
+ threadWaitForSignal();
+ threadUnlock();
+ }
+
+ threadCheckExit();
+ }
+
+ free(threadBuffer);
+ threadBuffer = NULL;
+
+ }
+
+ // end of recording
+ logger->log("PlayerRadioRec", Log::DEBUG, "Recording playback ends");
+
+ threadCheckExit();
+
+
+ Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
+ m->to = messageReceiver;
+ m->from = this;
+ m->message = Message::PLAYER_EVENT;
+ m->parameter = PlayerRadioRec::STOP_PLAYBACK;
+ logger->log("PlayerRadioRec", Log::DEBUG, "Posting message to %p...", messageQueue);
+ messageQueue->postMessage(m);
+}
+
+void PlayerRadioRec::threadPostStopCleanup()
+{
+ if (threadBuffer)
+ {
+ free(threadBuffer);
+ threadBuffer = NULL;
+ }
+}
+
--- /dev/null
+/*
+ Copyright 2004-2006 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ VOMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with VOMP; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef PLAYERRADIOREC_H
+#define PLAYERRADIOREC_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#include <mutex>
+
+#include "threadsystem.h"
+
+#include "callback.h"
+#include "defines.h"
+#include "afeed.h"
+
+class Log;
+class Audio;
+class Video;
+class Demuxer;
+class VDR;
+class MessageQueue;
+
+/*
+ * Frames...
+ *
+ * VDR tells me there are around 8.3 "frames" per second in radio recordings.
+ * I don't know where this comes from but things seem to work.
+ */
+
+class PlayerRadioRec : public Thread_TYPE, public Callback
+{
+ public:
+ PlayerRadioRec(MessageQueue* messageQueue, void* messageReceiver);
+ virtual ~PlayerRadioRec();
+
+ bool init(ULLONG lengthBytes, ULONG lengthFrames, bool IsPesRecording);
+ int shutdown();
+ void setCurrentFrameNumber(ULONG num);
+
+ void play();
+ void stop();
+ void pause();
+ void playpause();
+ void jumpToPercent(double percent);
+ void skipForward(UINT seconds);
+ void skipBackward(UINT seconds);
+
+ UCHAR getState() { return state; }
+ ULONG getCurrentSeconds();
+ ULONG getLengthSeconds();
+
+ void call(void*); // for callback interface
+
+ const static UCHAR S_PLAY = 1;
+ const static UCHAR S_PAUSE_P = 2;
+ const static UCHAR S_PAUSE_I = 3;
+ const static UCHAR S_STOP = 6;
+ const static UCHAR S_JUMP = 7;
+
+ // Player events
+
+ const static UCHAR CONNECTION_LOST = 1;
+ const static UCHAR STOP_PLAYBACK = 2;
+ const static UCHAR STREAM_END = 3;
+
+ protected:
+ void threadMethod();
+ void threadPostStopCleanup();
+
+ private:
+ void switchState(UCHAR newState, ULONG jumpToFrame=0);
+
+ void threadFeedPlay();
+ void threadFeedScan();
+
+ void doConnectionLost();
+ void restartAtFrame(ULONG newFrame);
+ bool setLengthSeconds();
+
+ MessageQueue* messageQueue;
+ void* messageReceiver;
+ Log* logger;
+ Audio* audio;
+ Demuxer* demuxer;
+ VDR* vdr;
+ AFeed afeed;
+
+ bool initted{};
+ bool startup;
+
+ ULLONG startPTS{};
+ ULONG lengthSeconds{};
+
+ std::mutex stateLock;
+
+ ULLONG lengthBytes{};
+ ULONG lengthFrames{};
+ ULONG currentFrameNumber{};
+ static const UINT blockSize{10000};
+ static const UINT startupBlockSize{20000};
+ UCHAR* threadBuffer{};
+ UCHAR state{S_STOP};
+};
+
+#endif
+
+
+/*
+
+Possible states:
+
+Play, Pause, FFwd, FBwd, (Stop), [Jump]
+
+ Possible Working
+
+Play -> PauseP * *
+ -> Stop * *
+ -> Jump * *
+
+PauseP -> Play * *
+ -> Stop * *
+ -> Jump * *
+
+PauseI -> Play * *
+ -> PauseP
+ -> Stop * *
+ -> Jump * *
+
+Stop -> Play * *
+ -> PauseP
+ -> Jump
+
+Jump -> Play
+ -> PauseP
+ -> Stop
+
+*/
#include "message.h"
#include "vdr.h"
#include "video.h"
-#include "playerradio.h"
+#include "playerradiorec.h"
#include "boxstack.h"
#include "input.h"
#include "vinfo.h"
startMargin = 0;
endMargin = 0;
- player = new PlayerRadio(Command::getInstance(), this);
+ player = new PlayerRadioRec(Command::getInstance(), this);
char* cstartMargin = vdr->configLoad("Timers", "Start margin");
char* cendMargin = vdr->configLoad("Timers", "End margin");
switch(m->parameter)
{
- case PlayerRadio::CONNECTION_LOST: // connection lost detected
+ case PlayerRadioRec::CONNECTION_LOST: // connection lost detected
{
// I can't handle this, send it to command
Message* m2 = new Message();
MessageQueue::getInstance()->postMessage(m2);
break;
}
- case PlayerRadio::STOP_PLAYBACK:
+ case PlayerRadioRec::STOP_PLAYBACK:
{
// FIXME Obselete ish - improve this
Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex
else
{
playerState = player->getState();
- if (playerState == PlayerRadio::S_PAUSE_P) w.nextSymbol = WSymbol::PAUSE;
- else if (playerState == PlayerRadio::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
+ if (playerState == PlayerRadioRec::S_PAUSE_P) w.nextSymbol = WSymbol::PAUSE;
+ else if (playerState == PlayerRadioRec::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE;
else w.nextSymbol = WSymbol::PLAY;
}
class Message;
class VDR;
class Video;
-class PlayerRadio;
+class PlayerRadioRec;
class BoxStack;
class VRadioRec : public Boxx, public TimerReceiver
VDR* vdr;
Video* video;
Timers* timers;
- PlayerRadio* player;
+ PlayerRadioRec* player;
Recording* myRec;
BoxStack* boxstack;