From: Chris Tallon Date: Tue, 3 Mar 2020 22:02:20 +0000 (+0000) Subject: Rename class: PlayerRadio to PlayerRadioRec X-Git-Url: https://git.vomp.tv/gitweb/?a=commitdiff_plain;h=f5796a769e0f958c2c6372e1374f2e59d81fd50d;p=vompclient.git Rename class: PlayerRadio to PlayerRadioRec --- diff --git a/objects.mk b/objects.mk index 5fa2805..8be3227 100644 --- a/objects.mk +++ b/objects.mk @@ -1,7 +1,7 @@ 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 \ @@ -19,7 +19,7 @@ OBJ_COMMON = command.o tcp.o dsock.o thread.o timers.o i18n.o vdp6.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 \ diff --git a/playerradio.cc b/playerradio.cc deleted file mode 100644 index a1f9c1f..0000000 --- a/playerradio.cc +++ /dev/null @@ -1,548 +0,0 @@ -/* - 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 . -*/ - -#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((endPTS - startPTS) / 90000); - } - else - { - lengthSeconds = static_cast((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(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(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(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(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; - } -} - diff --git a/playerradio.h b/playerradio.h deleted file mode 100644 index 57e6625..0000000 --- a/playerradio.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - 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 -#include -#ifndef WIN32 -#include -#endif -#include - -#include - -#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 - -*/ diff --git a/playerradiorec.cc b/playerradiorec.cc new file mode 100644 index 0000000..0fdf0ad --- /dev/null +++ b/playerradiorec.cc @@ -0,0 +1,548 @@ +/* + 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 . +*/ + +#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((endPTS - startPTS) / 90000); + } + else + { + lengthSeconds = static_cast((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(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(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(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(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; + } +} + diff --git a/playerradiorec.h b/playerradiorec.h new file mode 100644 index 0000000..56a9fc9 --- /dev/null +++ b/playerradiorec.h @@ -0,0 +1,160 @@ +/* + 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 +#include +#ifndef WIN32 +#include +#endif +#include + +#include + +#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 + +*/ diff --git a/vradiorec.cc b/vradiorec.cc index f574ef3..dec56c2 100644 --- a/vradiorec.cc +++ b/vradiorec.cc @@ -25,7 +25,7 @@ #include "message.h" #include "vdr.h" #include "video.h" -#include "playerradio.h" +#include "playerradiorec.h" #include "boxstack.h" #include "input.h" #include "vinfo.h" @@ -46,7 +46,7 @@ VRadioRec::VRadioRec(Recording* rec) 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"); @@ -311,7 +311,7 @@ void VRadioRec::processMessage(Message* m) 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(); @@ -320,7 +320,7 @@ void VRadioRec::processMessage(Message* m) 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 @@ -385,8 +385,8 @@ void VRadioRec::doBar(int action) 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; } diff --git a/vradiorec.h b/vradiorec.h index dfff16d..b333628 100644 --- a/vradiorec.h +++ b/vradiorec.h @@ -30,7 +30,7 @@ class Recording; class Message; class VDR; class Video; -class PlayerRadio; +class PlayerRadioRec; class BoxStack; class VRadioRec : public Boxx, public TimerReceiver @@ -50,7 +50,7 @@ class VRadioRec : public Boxx, public TimerReceiver VDR* vdr; Video* video; Timers* timers; - PlayerRadio* player; + PlayerRadioRec* player; Recording* myRec; BoxStack* boxstack;