2 Copyright 2004-2020 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/>.
29 #include "demuxervdr.h"
30 #include "demuxerts.h"
34 #include "messagequeue.h"
37 #include "playerradiorec.h"
39 // ----------------------------------- Called from outside, one offs or info funcs
41 PlayerRadioRec::PlayerRadioRec(MessageQueue* tmessageQueue, void* tmessageReceiver)
42 : messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this)
44 audio = Audio::getInstance();
45 logger = Log::getInstance();
46 vdr = VDR::getInstance();
47 Video::getInstance()->turnVideoOff();
50 PlayerRadioRec::~PlayerRadioRec()
52 if (initted) shutdown();
55 bool PlayerRadioRec::init(ULLONG tlengthBytes, ULONG tlengthFrames, bool isPesRecording)
57 if (initted) return false;
60 demuxer = new DemuxerVDR();
62 demuxer = new DemuxerTS();
63 if (!demuxer) return false;
65 if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
67 logger->log("PlayerRadioRec", Log::ERR, "Demuxer failed to init");
74 lengthBytes = tlengthBytes;
75 lengthFrames = tlengthFrames;
77 logger->log("PlayerRadioRec", Log::DEBUG, "PlayerRadioRec has received length bytes of %llu", lengthBytes);
82 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
85 logger->log("PlayerRadioRec", Log::ERR, "Failed to get start block");
87 if (!vdr->isConnected()) doConnectionLost();
91 success = demuxer->findPTS(buffer, thisRead, &startPTS);
94 logger->log("PlayerRadioRec", Log::ERR, "Failed to get start PTS");
102 if (!setLengthSeconds())
104 logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds");
113 bool PlayerRadioRec::setLengthSeconds()
118 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
121 logger->log("PlayerRadioRec", Log::ERR, "Failed to get end block");
122 if (!vdr->isConnected()) doConnectionLost();
126 success = demuxer->findPTS(buffer, thisRead, &endPTS);
130 logger->log("PlayerRadioRec", Log::ERR, "Failed to get end PTS");
134 if (startPTS < endPTS)
135 lengthSeconds = static_cast<ULONG>((endPTS - startPTS) / 90000);
137 lengthSeconds = static_cast<ULONG>((startPTS - endPTS) / 90000);
142 int PlayerRadioRec::shutdown()
144 if (!initted) return 0;
154 void PlayerRadioRec::threadStop()
156 playerThreadMutex.lock();
157 threadReqQuit = true;
158 playerThreadCond.notify_one();
159 playerThreadMutex.unlock();
163 void PlayerRadioRec::setCurrentFrameNumber(ULONG num)
165 currentFrameNumber = num;
168 ULONG PlayerRadioRec::getLengthSeconds()
170 return lengthSeconds;
173 ULONG PlayerRadioRec::getCurrentSeconds()
175 if (startup) return 0;
177 long long currentPTS = demuxer->getAudioPTS();
178 currentPTS -= startPTS;
179 if (currentPTS < 0) currentPTS += 8589934592ULL;
180 ULONG ret = static_cast<ULONG>(currentPTS / 90000);
184 // ----------------------------------- Externally called events
186 void PlayerRadioRec::play()
188 if (!initted) return;
189 if (state == S_PLAY) return;
195 void PlayerRadioRec::playpause()
197 if (!initted) return;
200 switchState(S_PAUSE_P);
206 void PlayerRadioRec::stop()
208 if (!initted) return;
209 if (state == S_STOP) return;
211 logger->log("PlayerRadioRec", Log::DEBUG, "Stop called lock");
216 void PlayerRadioRec::pause()
218 if (!initted) return;
221 if (state == S_PAUSE_P)
224 switchState(S_PAUSE_P);
229 void PlayerRadioRec::jumpToPercent(double percent)
232 logger->log("PlayerRadioRec", Log::DEBUG, "JUMP TO %i%%", percent);
233 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
234 switchState(S_JUMP, newFrame);
238 void PlayerRadioRec::skipForward(UINT seconds)
240 std::lock_guard<std::mutex> lg(stateLock);
241 logger->log("PlayerRadioRec", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
242 ULONG currentSeconds = getCurrentSeconds();
243 ULONG currentFrame = demuxer->getPacketNum();
245 if (currentSeconds == 0) return; // div by zero
246 if (currentFrame == 0) return; // Current pos from demuxer is not valid
248 ULONG newFrame = currentFrame + (currentFrame * seconds / currentSeconds);
249 if (newFrame > lengthFrames) switchState(S_PLAY);
250 else switchState(S_JUMP, newFrame);
253 void PlayerRadioRec::skipBackward(UINT seconds)
255 std::lock_guard<std::mutex> lg(stateLock);
256 logger->log("PlayerRadioRec", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
257 ULONG currentSeconds = getCurrentSeconds();
258 ULONG currentFrame = demuxer->getPacketNum();
260 if (currentSeconds == 0) return; // div by zero
261 if (currentFrame == 0) return; // Current pos from demuxer is not valid
264 if (seconds > currentSeconds)
267 newFrame = currentFrame - (currentFrame * seconds / currentSeconds);
269 switchState(S_JUMP, newFrame);
272 // ----------------------------------- Implementations called events
274 void PlayerRadioRec::switchState(UCHAR toState, ULONG jumpToFrame)
276 if (!initted) return;
278 logger->log("PlayerRadioRec", Log::DEBUG, "Switch state from %u to %u", state, toState);
280 switch(state) // current state selector
282 case S_PLAY: // from S_PLAY -----------------------------------
286 case S_PLAY: // to S_PLAY
290 case S_PAUSE_P: // to S_PAUSE_P
296 case S_STOP: // to S_STOP
306 case S_JUMP: // to S_JUMP
308 restartAtFrame(jumpToFrame);
313 FALLTHROUGH // keep compiler happy (all posibilities return)
314 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
318 case S_PLAY: // to S_PLAY
324 case S_PAUSE_P: // to S_PAUSE_P
328 case S_STOP: // to S_STOP
335 audio->systemMuteOff();
339 case S_JUMP: // to S_JUMP
343 restartAtFrame(jumpToFrame);
348 FALLTHROUGH // keep compiler happy (all posibilities return)
349 case S_STOP: // from S_STOP -----------------------------------
353 case S_PLAY: // to S_PLAY
356 restartAtFrame(currentFrameNumber);
359 case S_PAUSE_P: // to S_PAUSE_P
363 case S_STOP: // to S_STOP
367 case S_JUMP: // to S_JUMP
373 // case S_JUMP cannot be selected as a start state because it auto flips to play
377 // ----------------------------------- Internal functions
379 void PlayerRadioRec::restartAtFrame(ULONG newFrame)
388 audio->setStreamType(Audio::MPEG2_PES);
390 currentFrameNumber = newFrame;
391 demuxer->setPacketNum(newFrame);
395 playerThreadMutex.lock();
396 threadReqQuit = false;
397 playerThread = std::thread([this]
399 playerThreadMutex.lock();
400 playerThreadMutex.unlock();
403 playerThreadMutex.unlock();
407 audio->systemMuteOff();
411 void PlayerRadioRec::doConnectionLost()
413 logger->log("PlayerRadioRec", Log::DEBUG, "Connection lost, sending message");
414 Message* m = new Message();
415 m->to = messageReceiver;
417 m->message = Message::PLAYER_EVENT;
418 m->parameter = PlayerRadioRec::CONNECTION_LOST;
419 messageQueue->postMessage(m);
422 // ----------------------------------- Callback
424 void PlayerRadioRec::call(void* /*caller*/)
427 playerThreadCond.notify_one();
430 // ----------------------------------- Feed thread
432 void PlayerRadioRec::threadMethod()
434 if (state != S_PLAY) return;
437 UINT thisRead, writeLength, thisWrite, askFor;
438 time_t lastRescan = time(NULL);
440 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
441 if (!vdr->isConnected()) { doConnectionLost(); return; }
442 logger->log("PlayerRadioRec", Log::DEBUG, "startFeedPlay: wantedFrame %i goto %llu", currentFrameNumber, feedPosition);
447 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
455 if (threadReqQuit) return;
457 // If we havn't rescanned for a while..
458 if ((lastRescan + 60) < time(NULL))
460 lengthBytes = vdr->rescanRecording(&lengthFrames);
461 if (!vdr->isConnected()) { doConnectionLost(); return; }
462 logger->log("PlayerRadioRec", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
463 lastRescan = time(NULL);
465 if (!setLengthSeconds())
467 logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds in thread");
472 if (feedPosition >= lengthBytes) break; // finished playback
476 if (startupBlockSize > lengthBytes)
477 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
479 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
483 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
484 askFor = static_cast<UINT>(lengthBytes - feedPosition);
489 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
490 feedPosition += thisRead;
492 if (!vdr->isConnected())
498 if (threadBuffer.isNull()) break;
502 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
503 demuxer->setAudioStream(a_stream);
504 logger->log("PlayerRadioRec", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
508 if (threadReqQuit) return;
510 while(writeLength < thisRead)
512 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
513 writeLength += thisWrite;
517 // demuxer is full and can't take anymore
520 if (threadReqQuit) { ul.unlock(); return; }
521 playerThreadCond.wait(ul);
525 if (threadReqQuit) return;
528 threadBuffer.release();
532 logger->log("PlayerRadioRec", Log::DEBUG, "Recording playback ends");
534 if (threadReqQuit) return;
537 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
538 m->to = messageReceiver;
540 m->message = Message::PLAYER_EVENT;
541 m->parameter = PlayerRadioRec::STOP_PLAYBACK;
542 logger->log("PlayerRadioRec", Log::DEBUG, "Posting message to %p...", messageQueue);
543 messageQueue->postMessage(m);