2 Copyright 2004-2006 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP. If not, see <https://www.gnu.org/licenses/>.
23 #include "demuxervdr.h"
24 #include "demuxerts.h"
28 #include "messagequeue.h"
30 #include "playerradiorec.h"
32 // ----------------------------------- Called from outside, one offs or info funcs
34 PlayerRadioRec::PlayerRadioRec(MessageQueue* tmessageQueue, void* tmessageReceiver)
35 : messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this)
37 audio = Audio::getInstance();
38 logger = Log::getInstance();
39 vdr = VDR::getInstance();
40 Video::getInstance()->turnVideoOff();
43 PlayerRadioRec::~PlayerRadioRec()
45 if (initted) shutdown();
48 bool PlayerRadioRec::init(ULLONG tlengthBytes, ULONG tlengthFrames, bool isPesRecording)
50 if (initted) return false;
53 demuxer = new DemuxerVDR();
55 demuxer = new DemuxerTS();
56 if (!demuxer) return false;
58 if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
60 logger->log("PlayerRadioRec", Log::ERR, "Demuxer failed to init");
67 lengthBytes = tlengthBytes;
68 lengthFrames = tlengthFrames;
70 logger->log("PlayerRadioRec", Log::DEBUG, "PlayerRadioRec has received length bytes of %llu", lengthBytes);
75 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
78 logger->log("PlayerRadioRec", Log::ERR, "Failed to get start block");
80 if (!vdr->isConnected()) doConnectionLost();
84 success = demuxer->findPTS(buffer, thisRead, &startPTS);
87 logger->log("PlayerRadioRec", Log::ERR, "Failed to get start PTS");
95 if (!setLengthSeconds())
97 logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds");
106 bool PlayerRadioRec::setLengthSeconds()
111 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
114 logger->log("PlayerRadioRec", Log::ERR, "Failed to get end block");
115 if (!vdr->isConnected()) doConnectionLost();
119 success = demuxer->findPTS(buffer, thisRead, &endPTS);
123 logger->log("PlayerRadioRec", Log::ERR, "Failed to get end PTS");
127 if (startPTS < endPTS)
129 lengthSeconds = static_cast<ULONG>((endPTS - startPTS) / 90000);
133 lengthSeconds = static_cast<ULONG>((startPTS - endPTS) / 90000);
139 int PlayerRadioRec::shutdown()
141 if (!initted) return 0;
152 void PlayerRadioRec::setCurrentFrameNumber(ULONG num)
154 currentFrameNumber = num;
157 ULONG PlayerRadioRec::getLengthSeconds()
159 return lengthSeconds;
162 ULONG PlayerRadioRec::getCurrentSeconds()
164 if (startup) return 0;
166 long long currentPTS = demuxer->getAudioPTS();
167 currentPTS -= startPTS;
168 if (currentPTS < 0) currentPTS += 8589934592ULL;
169 ULONG ret = static_cast<ULONG>(currentPTS / 90000);
173 // ----------------------------------- Externally called events
175 void PlayerRadioRec::play()
177 if (!initted) return;
178 if (state == S_PLAY) return;
185 void PlayerRadioRec::playpause()
187 if (!initted) return;
190 switchState(S_PAUSE_P);
197 void PlayerRadioRec::stop()
199 if (!initted) return;
200 if (state == S_STOP) return;
202 logger->log("PlayerRadioRec", Log::DEBUG, "Stop called lock");
207 void PlayerRadioRec::pause()
209 if (!initted) return;
212 if (state == S_PAUSE_P)
218 switchState(S_PAUSE_P);
224 void PlayerRadioRec::jumpToPercent(double percent)
227 logger->log("PlayerRadioRec", Log::DEBUG, "JUMP TO %i%%", percent);
228 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
229 switchState(S_JUMP, newFrame);
233 void PlayerRadioRec::skipForward(UINT seconds)
236 logger->log("PlayerRadioRec", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
237 ULONG currentSeconds = getCurrentSeconds();
238 ULONG currentFrame = demuxer->getPacketNum();
240 if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
241 if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
243 ULONG newFrame = currentFrame + (currentFrame * seconds / currentSeconds);
244 if (newFrame > lengthFrames) { switchState(S_PLAY); stateLock.unlock(); }
245 else switchState(S_JUMP, newFrame);
249 void PlayerRadioRec::skipBackward(UINT seconds)
252 logger->log("PlayerRadioRec", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
254 ULONG currentSeconds = getCurrentSeconds();
255 ULONG currentFrame = demuxer->getPacketNum();
257 if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
258 if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
261 if (seconds > currentSeconds)
264 newFrame = currentFrame - (currentFrame * seconds / currentSeconds);
266 switchState(S_JUMP, newFrame);
270 // ----------------------------------- Implementations called events
272 void PlayerRadioRec::switchState(UCHAR toState, ULONG jumpToFrame)
274 if (!initted) return;
276 logger->log("PlayerRadioRec", Log::DEBUG, "Switch state from %u to %u", state, toState);
278 switch(state) // current state selector
280 case S_PLAY: // from S_PLAY -----------------------------------
284 case S_PLAY: // to S_PLAY
288 case S_PAUSE_P: // to S_PAUSE_P
294 case S_STOP: // to S_STOP
304 case S_JUMP: // to S_JUMP
306 restartAtFrame(jumpToFrame);
311 FALLTHROUGH // keep compiler happy (all posibilities return)
312 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
316 case S_PLAY: // to S_PLAY
322 case S_PAUSE_P: // to S_PAUSE_P
326 case S_STOP: // to S_STOP
333 audio->systemMuteOff();
337 case S_JUMP: // to S_JUMP
341 restartAtFrame(jumpToFrame);
346 FALLTHROUGH // keep compiler happy (all posibilities return)
347 case S_STOP: // from S_STOP -----------------------------------
351 case S_PLAY: // to S_PLAY
356 audio->setStreamType(Audio::MPEG2_PES);
357 audio->systemMuteOff();
360 // FIXME use restartAtFrame here?
361 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
362 demuxer->setPacketNum(currentFrameNumber);
365 logger->log("PlayerRadioRec", Log::DEBUG, "Immediate play");
371 case S_PAUSE_P: // to S_PAUSE_P
375 case S_STOP: // to S_STOP
379 case S_JUMP: // to S_JUMP
385 // case S_JUMP cannot be selected as a start state because it auto flips to play
389 // ----------------------------------- Internal functions
391 void PlayerRadioRec::restartAtFrame(ULONG newFrame)
396 audio->setStreamType(Audio::MPEG2_PES);
398 currentFrameNumber = newFrame;
399 demuxer->setPacketNum(newFrame);
403 audio->systemMuteOff();
407 void PlayerRadioRec::doConnectionLost()
409 logger->log("PlayerRadioRec", Log::DEBUG, "Connection lost, sending message");
410 Message* m = new Message();
411 m->to = messageReceiver;
413 m->message = Message::PLAYER_EVENT;
414 m->parameter = PlayerRadioRec::CONNECTION_LOST;
415 messageQueue->postMessage(m);
418 // ----------------------------------- Callback
420 void PlayerRadioRec::call(void* /*caller*/)
422 threadSignalNoLock();
425 // ----------------------------------- Feed thread
427 void PlayerRadioRec::threadMethod()
429 if (state == S_PLAY) threadFeedPlay();
432 void PlayerRadioRec::threadFeedPlay()
435 UINT thisRead, writeLength, thisWrite, askFor;
436 time_t lastRescan = time(NULL);
438 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
439 if (!vdr->isConnected()) { doConnectionLost(); return; }
440 logger->log("PlayerRadioRec", Log::DEBUG, "startFeedPlay: wantedFrame %i goto %llu", currentFrameNumber, feedPosition);
451 // If we havn't rescanned for a while..
452 if ((lastRescan + 60) < time(NULL))
454 lengthBytes = vdr->rescanRecording(&lengthFrames);
455 if (!vdr->isConnected()) { doConnectionLost(); return; }
456 logger->log("PlayerRadioRec", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
457 lastRescan = time(NULL);
459 if (!setLengthSeconds())
461 logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds in thread");
466 if (feedPosition >= lengthBytes) break; // finished playback
470 if (startupBlockSize > lengthBytes)
471 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
473 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
477 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
478 askFor = static_cast<UINT>(lengthBytes - feedPosition);
483 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
484 feedPosition += thisRead;
486 if (!vdr->isConnected())
492 if (!threadBuffer) break;
496 int a_stream = demuxer->scan(threadBuffer, thisRead);
497 demuxer->setAudioStream(a_stream);
498 logger->log("PlayerRadioRec", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
504 while(writeLength < thisRead)
506 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
507 writeLength += thisWrite;
511 // demuxer is full and can't take anymore
513 threadWaitForSignal();
526 logger->log("PlayerRadioRec", Log::DEBUG, "Recording playback ends");
531 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
532 m->to = messageReceiver;
534 m->message = Message::PLAYER_EVENT;
535 m->parameter = PlayerRadioRec::STOP_PLAYBACK;
536 logger->log("PlayerRadioRec", Log::DEBUG, "Posting message to %p...", messageQueue);
537 messageQueue->postMessage(m);
540 void PlayerRadioRec::threadPostStopCleanup()