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/>.
23 #include "demuxervdr.h"
24 #include "demuxerts.h"
28 #include "messagequeue.h"
31 #include "playerradiorec.h"
33 // ----------------------------------- Called from outside, one offs or info funcs
35 PlayerRadioRec::PlayerRadioRec(MessageQueue* tmessageQueue, void* tmessageReceiver)
36 : messageQueue(tmessageQueue), messageReceiver(tmessageReceiver), afeed(this)
38 audio = Audio::getInstance();
39 logger = Log::getInstance();
40 vdr = VDR::getInstance();
41 Video::getInstance()->turnVideoOff();
44 PlayerRadioRec::~PlayerRadioRec()
46 if (initted) shutdown();
49 bool PlayerRadioRec::init(ULLONG tlengthBytes, ULONG tlengthFrames, bool isPesRecording)
51 if (initted) return false;
54 demuxer = new DemuxerVDR();
56 demuxer = new DemuxerTS();
57 if (!demuxer) return false;
59 if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
61 logger->log("PlayerRadioRec", Log::ERR, "Demuxer failed to init");
68 lengthBytes = tlengthBytes;
69 lengthFrames = tlengthFrames;
71 logger->log("PlayerRadioRec", Log::DEBUG, "PlayerRadioRec has received length bytes of %llu", lengthBytes);
76 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
79 logger->log("PlayerRadioRec", Log::ERR, "Failed to get start block");
81 if (!vdr->isConnected()) doConnectionLost();
85 success = demuxer->findPTS(buffer, thisRead, &startPTS);
88 logger->log("PlayerRadioRec", Log::ERR, "Failed to get start PTS");
96 if (!setLengthSeconds())
98 logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds");
107 bool PlayerRadioRec::setLengthSeconds()
112 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
115 logger->log("PlayerRadioRec", Log::ERR, "Failed to get end block");
116 if (!vdr->isConnected()) doConnectionLost();
120 success = demuxer->findPTS(buffer, thisRead, &endPTS);
124 logger->log("PlayerRadioRec", Log::ERR, "Failed to get end PTS");
128 if (startPTS < endPTS)
130 lengthSeconds = static_cast<ULONG>((endPTS - startPTS) / 90000);
134 lengthSeconds = static_cast<ULONG>((startPTS - endPTS) / 90000);
140 int PlayerRadioRec::shutdown()
142 if (!initted) return 0;
153 void PlayerRadioRec::setCurrentFrameNumber(ULONG num)
155 currentFrameNumber = num;
158 ULONG PlayerRadioRec::getLengthSeconds()
160 return lengthSeconds;
163 ULONG PlayerRadioRec::getCurrentSeconds()
165 if (startup) return 0;
167 long long currentPTS = demuxer->getAudioPTS();
168 currentPTS -= startPTS;
169 if (currentPTS < 0) currentPTS += 8589934592ULL;
170 ULONG ret = static_cast<ULONG>(currentPTS / 90000);
174 // ----------------------------------- Externally called events
176 void PlayerRadioRec::play()
178 if (!initted) return;
179 if (state == S_PLAY) return;
186 void PlayerRadioRec::playpause()
188 if (!initted) return;
191 switchState(S_PAUSE_P);
198 void PlayerRadioRec::stop()
200 if (!initted) return;
201 if (state == S_STOP) return;
203 logger->log("PlayerRadioRec", Log::DEBUG, "Stop called lock");
208 void PlayerRadioRec::pause()
210 if (!initted) return;
213 if (state == S_PAUSE_P)
219 switchState(S_PAUSE_P);
225 void PlayerRadioRec::jumpToPercent(double percent)
228 logger->log("PlayerRadioRec", Log::DEBUG, "JUMP TO %i%%", percent);
229 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
230 switchState(S_JUMP, newFrame);
234 void PlayerRadioRec::skipForward(UINT seconds)
237 logger->log("PlayerRadioRec", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
238 ULONG currentSeconds = getCurrentSeconds();
239 ULONG currentFrame = demuxer->getPacketNum();
241 if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
242 if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
244 ULONG newFrame = currentFrame + (currentFrame * seconds / currentSeconds);
245 if (newFrame > lengthFrames) { switchState(S_PLAY); stateLock.unlock(); }
246 else switchState(S_JUMP, newFrame);
250 void PlayerRadioRec::skipBackward(UINT seconds)
253 logger->log("PlayerRadioRec", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
255 ULONG currentSeconds = getCurrentSeconds();
256 ULONG currentFrame = demuxer->getPacketNum();
258 if (currentSeconds == 0) { stateLock.unlock(); return; } // div by zero
259 if (currentFrame == 0) { stateLock.unlock(); return; } // Current pos from demuxer is not valid
262 if (seconds > currentSeconds)
265 newFrame = currentFrame - (currentFrame * seconds / currentSeconds);
267 switchState(S_JUMP, newFrame);
271 // ----------------------------------- Implementations called events
273 void PlayerRadioRec::switchState(UCHAR toState, ULONG jumpToFrame)
275 if (!initted) return;
277 logger->log("PlayerRadioRec", Log::DEBUG, "Switch state from %u to %u", state, toState);
279 switch(state) // current state selector
281 case S_PLAY: // from S_PLAY -----------------------------------
285 case S_PLAY: // to S_PLAY
289 case S_PAUSE_P: // to S_PAUSE_P
295 case S_STOP: // to S_STOP
305 case S_JUMP: // to S_JUMP
307 restartAtFrame(jumpToFrame);
312 FALLTHROUGH // keep compiler happy (all posibilities return)
313 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
317 case S_PLAY: // to S_PLAY
323 case S_PAUSE_P: // to S_PAUSE_P
327 case S_STOP: // to S_STOP
334 audio->systemMuteOff();
338 case S_JUMP: // to S_JUMP
342 restartAtFrame(jumpToFrame);
347 FALLTHROUGH // keep compiler happy (all posibilities return)
348 case S_STOP: // from S_STOP -----------------------------------
352 case S_PLAY: // to S_PLAY
357 audio->setStreamType(Audio::MPEG2_PES);
358 audio->systemMuteOff();
361 // FIXME use restartAtFrame here?
362 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
363 demuxer->setPacketNum(currentFrameNumber);
366 logger->log("PlayerRadioRec", Log::DEBUG, "Immediate play");
372 case S_PAUSE_P: // to S_PAUSE_P
376 case S_STOP: // to S_STOP
380 case S_JUMP: // to S_JUMP
386 // case S_JUMP cannot be selected as a start state because it auto flips to play
390 // ----------------------------------- Internal functions
392 void PlayerRadioRec::restartAtFrame(ULONG newFrame)
397 audio->setStreamType(Audio::MPEG2_PES);
399 currentFrameNumber = newFrame;
400 demuxer->setPacketNum(newFrame);
404 audio->systemMuteOff();
408 void PlayerRadioRec::doConnectionLost()
410 logger->log("PlayerRadioRec", Log::DEBUG, "Connection lost, sending message");
411 Message* m = new Message();
412 m->to = messageReceiver;
414 m->message = Message::PLAYER_EVENT;
415 m->parameter = PlayerRadioRec::CONNECTION_LOST;
416 messageQueue->postMessage(m);
419 // ----------------------------------- Callback
421 void PlayerRadioRec::call(void* /*caller*/)
423 threadSignalNoLock();
426 // ----------------------------------- Feed thread
428 void PlayerRadioRec::threadMethod()
430 if (state == S_PLAY) threadFeedPlay();
433 void PlayerRadioRec::threadFeedPlay()
436 UINT thisRead, writeLength, thisWrite, askFor;
437 time_t lastRescan = time(NULL);
439 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
440 if (!vdr->isConnected()) { doConnectionLost(); return; }
441 logger->log("PlayerRadioRec", Log::DEBUG, "startFeedPlay: wantedFrame %i goto %llu", currentFrameNumber, feedPosition);
454 // If we havn't rescanned for a while..
455 if ((lastRescan + 60) < time(NULL))
457 lengthBytes = vdr->rescanRecording(&lengthFrames);
458 if (!vdr->isConnected()) { doConnectionLost(); return; }
459 logger->log("PlayerRadioRec", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
460 lastRescan = time(NULL);
462 if (!setLengthSeconds())
464 logger->log("PlayerRadioRec", Log::ERR, "Failed to setLengthSeconds in thread");
469 if (feedPosition >= lengthBytes) break; // finished playback
473 if (startupBlockSize > lengthBytes)
474 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
476 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
480 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
481 askFor = static_cast<UINT>(lengthBytes - feedPosition);
486 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
487 feedPosition += thisRead;
489 if (!vdr->isConnected())
495 if (threadBuffer.isNull()) break;
499 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
500 demuxer->setAudioStream(a_stream);
501 logger->log("PlayerRadioRec", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
507 while(writeLength < thisRead)
509 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
510 writeLength += thisWrite;
514 // demuxer is full and can't take anymore
516 threadWaitForSignal();
523 threadBuffer.release();
527 logger->log("PlayerRadioRec", Log::DEBUG, "Recording playback ends");
532 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
533 m->to = messageReceiver;
535 m->message = Message::PLAYER_EVENT;
536 m->parameter = PlayerRadioRec::STOP_PLAYBACK;
537 logger->log("PlayerRadioRec", Log::DEBUG, "Posting message to %p...", messageQueue);
538 messageQueue->postMessage(m);