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 static const char* TAG = "PlayerRadioRec";
41 // ----------------------------------- Called from outside, one offs or info funcs
43 PlayerRadioRec::PlayerRadioRec(MessageQueue* tmessageQueue, MessageReceiver* tmessageReceiver)
44 : messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this)
46 audio = Audio::getInstance();
47 logger = LogNT::getInstance();
48 vdr = VDR::getInstance();
49 Video::getInstance()->turnVideoOff();
52 PlayerRadioRec::~PlayerRadioRec()
54 if (initted) shutdown();
57 bool PlayerRadioRec::init(u8 tlengthBytes, u4 tlengthFrames, bool isPesRecording)
59 if (initted) return false;
62 demuxer = new DemuxerVDR();
64 demuxer = new DemuxerTS();
65 if (!demuxer) return false;
67 if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
69 logger->error(TAG, "Demuxer failed to init");
76 lengthBytes = tlengthBytes;
77 lengthFrames = tlengthFrames;
79 logger->debug(TAG, "PlayerRadioRec has received length bytes of {}", lengthBytes);
84 u1* buffer = vdr->getBlock(0, 10000, &thisRead);
87 logger->error(TAG, "Failed to get start block");
89 if (!vdr->isConnected()) doConnectionLost();
93 success = demuxer->findPTS(buffer, thisRead, &startPTS);
96 logger->error(TAG, "Failed to get start PTS");
104 if (!setLengthSeconds())
106 logger->error(TAG, "Failed to setLengthSeconds");
115 bool PlayerRadioRec::setLengthSeconds()
120 u1* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
123 logger->error(TAG, "Failed to get end block");
124 if (!vdr->isConnected()) doConnectionLost();
128 success = demuxer->findPTS(buffer, thisRead, &endPTS);
132 logger->error(TAG, "Failed to get end PTS");
136 if (startPTS < endPTS)
137 lengthSeconds = static_cast<u4>((endPTS - startPTS) / 90000);
139 lengthSeconds = static_cast<u4>((startPTS - endPTS) / 90000);
144 int PlayerRadioRec::shutdown()
146 if (!initted) return 0;
156 void PlayerRadioRec::threadStop()
158 playerThreadMutex.lock();
159 threadReqQuit = true;
160 playerThreadCond.notify_one();
161 playerThreadMutex.unlock();
165 void PlayerRadioRec::setCurrentFrameNumber(u4 num)
167 currentFrameNumber = num;
170 u4 PlayerRadioRec::getLengthSeconds()
172 return lengthSeconds;
175 u4 PlayerRadioRec::getCurrentSeconds()
177 if (startup) return 0;
179 long long currentPTS = demuxer->getAudioPTS();
180 currentPTS -= startPTS;
181 if (currentPTS < 0) currentPTS += 8589934592ULL;
182 u4 ret = static_cast<u4>(currentPTS / 90000);
186 // ----------------------------------- Externally called events
188 void PlayerRadioRec::play()
190 if (!initted) return;
191 if (state == S_PLAY) return;
197 void PlayerRadioRec::playpause()
199 if (!initted) return;
202 switchState(S_PAUSE_P);
208 void PlayerRadioRec::stop()
210 if (!initted) return;
211 if (state == S_STOP) return;
213 logger->debug(TAG, "Stop called lock");
218 void PlayerRadioRec::pause()
220 if (!initted) return;
223 if (state == S_PAUSE_P)
226 switchState(S_PAUSE_P);
231 void PlayerRadioRec::jumpToPercent(double percent)
234 logger->debug(TAG, "JUMP TO {}%", percent);
235 u4 newFrame = static_cast<u4>(percent * lengthFrames / 100);
236 switchState(S_JUMP, newFrame);
240 void PlayerRadioRec::skipForward(u4 seconds)
242 std::lock_guard<std::mutex> lg(stateLock);
243 logger->debug(TAG, "SKIP FORWARD {} SECONDS", seconds);
244 u4 currentSeconds = getCurrentSeconds();
245 u4 currentFrame = demuxer->getPacketNum();
247 if (currentSeconds == 0) return; // div by zero
248 if (currentFrame == 0) return; // Current pos from demuxer is not valid
250 u4 newFrame = currentFrame + (currentFrame * seconds / currentSeconds);
251 if (newFrame > lengthFrames) switchState(S_PLAY);
252 else switchState(S_JUMP, newFrame);
255 void PlayerRadioRec::skipBackward(u4 seconds)
257 std::lock_guard<std::mutex> lg(stateLock);
258 logger->debug(TAG, "SKIP BACKWARD {} SECONDS", seconds);
259 u4 currentSeconds = getCurrentSeconds();
260 u4 currentFrame = demuxer->getPacketNum();
262 if (currentSeconds == 0) return; // div by zero
263 if (currentFrame == 0) return; // Current pos from demuxer is not valid
266 if (seconds > currentSeconds)
269 newFrame = currentFrame - (currentFrame * seconds / currentSeconds);
271 switchState(S_JUMP, newFrame);
274 // ----------------------------------- Implementations called events
276 void PlayerRadioRec::switchState(u1 toState, u4 jumpToFrame)
278 if (!initted) return;
280 logger->debug(TAG, "Switch state from {} to {}", state, toState);
282 switch(state) // current state selector
284 case S_PLAY: // from S_PLAY -----------------------------------
288 case S_PLAY: // to S_PLAY
292 case S_PAUSE_P: // to S_PAUSE_P
298 case S_STOP: // to S_STOP
308 case S_JUMP: // to S_JUMP
310 restartAtFrame(jumpToFrame);
315 FALLTHROUGH // keep compiler happy (all posibilities return)
316 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
320 case S_PLAY: // to S_PLAY
326 case S_PAUSE_P: // to S_PAUSE_P
330 case S_STOP: // to S_STOP
337 audio->systemMuteOff();
341 case S_JUMP: // to S_JUMP
345 restartAtFrame(jumpToFrame);
350 FALLTHROUGH // keep compiler happy (all posibilities return)
351 case S_STOP: // from S_STOP -----------------------------------
355 case S_PLAY: // to S_PLAY
358 restartAtFrame(currentFrameNumber);
361 case S_PAUSE_P: // to S_PAUSE_P
365 case S_STOP: // to S_STOP
369 case S_JUMP: // to S_JUMP
375 // case S_JUMP cannot be selected as a start state because it auto flips to play
379 // ----------------------------------- Internal functions
381 void PlayerRadioRec::restartAtFrame(u4 newFrame)
390 audio->setStreamType(Audio::MPEG2_PES);
392 currentFrameNumber = newFrame;
393 demuxer->setPacketNum(newFrame);
397 playerThreadMutex.lock();
398 threadReqQuit = false;
399 playerThread = std::thread([this]
401 playerThreadMutex.lock();
402 playerThreadMutex.unlock();
405 playerThreadMutex.unlock();
409 audio->systemMuteOff();
413 void PlayerRadioRec::doConnectionLost()
415 logger->debug(TAG, "Connection lost, sending message");
416 Message* m = new Message();
417 m->to = messageReceiver;
419 m->message = Message::PLAYER_EVENT;
420 m->parameter = PlayerRadioRec::CONNECTION_LOST;
421 messageQueue->postMessage(m);
424 // ----------------------------------- Callback
426 void PlayerRadioRec::call(void* /*caller*/)
429 playerThreadCond.notify_one();
432 // ----------------------------------- Feed thread
434 void PlayerRadioRec::threadMethod()
436 if (state != S_PLAY) return;
439 u4 thisRead, writeLength, thisWrite, askFor;
440 time_t lastRescan = time(NULL);
442 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
443 if (!vdr->isConnected()) { doConnectionLost(); return; }
444 logger->debug(TAG, "startFeedPlay: wantedFrame {} goto {}", currentFrameNumber, feedPosition);
449 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
457 if (threadReqQuit) return;
459 // If we havn't rescanned for a while..
460 if ((lastRescan + 60) < time(NULL))
462 lengthBytes = vdr->rescanRecording(&lengthFrames);
463 if (!vdr->isConnected()) { doConnectionLost(); return; }
464 logger->debug(TAG, "Rescanned and reset length: {}", lengthBytes);
465 lastRescan = time(NULL);
467 if (!setLengthSeconds())
469 logger->error(TAG, "Failed to setLengthSeconds in thread");
474 if (feedPosition >= lengthBytes) break; // finished playback
478 if (startupBlockSize > lengthBytes)
479 askFor = static_cast<u4>(lengthBytes); // is a very small recording!
481 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
485 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
486 askFor = static_cast<u4>(lengthBytes - feedPosition);
491 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
492 feedPosition += thisRead;
494 if (!vdr->isConnected())
500 if (threadBuffer.isNull()) break;
504 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
505 demuxer->setAudioStream(a_stream);
506 logger->debug(TAG, "Startup Audio stream chosen {:#x}", a_stream);
510 if (threadReqQuit) return;
512 while(writeLength < thisRead)
514 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
515 writeLength += thisWrite;
519 // demuxer is full and can't take anymore
522 if (threadReqQuit) { ul.unlock(); return; }
523 playerThreadCond.wait(ul);
527 if (threadReqQuit) return;
530 threadBuffer.release();
534 logger->debug(TAG, "Recording playback ends");
536 if (threadReqQuit) return;
539 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
540 m->to = messageReceiver;
542 m->message = Message::PLAYER_EVENT;
543 m->parameter = PlayerRadioRec::STOP_PLAYBACK;
544 logger->debug(TAG, "Posting message to {}...", static_cast<void*>(messageQueue));
545 messageQueue->postMessage(m);