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/>.
20 #include "playerradio.h"
25 #include "demuxervdr.h"
26 #include "demuxerts.h"
30 #include "messagequeue.h"
32 // ----------------------------------- Called from outside, one offs or info funcs
34 PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver)
37 messageQueue = tmessageQueue;
38 messageReceiver = tmessageReceiver;
39 audio = Audio::getInstance();
40 logger = Log::getInstance();
41 vdr = VDR::getInstance();
45 currentFrameNumber = 0;
54 startupBlockSize = 20000;
56 Video::getInstance()->turnVideoOff();
59 PlayerRadio::~PlayerRadio()
61 if (initted) shutdown();
64 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthFrames, bool isPesRecording)
66 if (initted) return 0;
68 pthread_mutex_init(&mutex, NULL);
70 mutex=CreateMutex(NULL,FALSE,NULL);
74 demuxer = new DemuxerVDR();
76 demuxer = new DemuxerTS();
77 if (!demuxer) return 0;
79 if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
81 logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
89 lengthBytes = tlengthBytes;
90 lengthFrames = tlengthFrames;
92 logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
97 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
100 logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
102 if (!vdr->isConnected()) doConnectionLost();
106 success = demuxer->findPTS(buffer, thisRead, &startPTS);
109 logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
117 if (!setLengthSeconds())
119 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
128 bool PlayerRadio::setLengthSeconds()
133 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
136 logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
137 if (!vdr->isConnected()) doConnectionLost();
141 success = demuxer->findPTS(buffer, thisRead, &endPTS);
145 logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
149 if (startPTS < endPTS)
151 lengthSeconds = (endPTS - startPTS) / 90000;
155 lengthSeconds = (startPTS - endPTS) / 90000;
161 int PlayerRadio::shutdown()
163 if (!initted) return 0;
178 void PlayerRadio::setCurrentFrameNumber(ULONG num)
180 currentFrameNumber = num;
183 ULONG PlayerRadio::getLengthSeconds()
185 return lengthSeconds;
188 ULONG PlayerRadio::getCurrentSeconds()
190 if (startup) return 0;
192 long long currentPTS = demuxer->getAudioPTS();
193 currentPTS -= startPTS;
194 if (currentPTS < 0) currentPTS += 8589934592ULL;
195 ULONG ret = currentPTS / 90000;
199 // ----------------------------------- Externally called events
201 void PlayerRadio::play()
203 if (!initted) return;
204 if (state == S_PLAY) return;
211 void PlayerRadio::playpause()
213 if (!initted) return;
216 switchState(S_PAUSE_P);
223 void PlayerRadio::stop()
225 if (!initted) return;
226 if (state == S_STOP) return;
228 logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
233 void PlayerRadio::pause()
235 if (!initted) return;
238 if (state == S_PAUSE_P)
244 switchState(S_PAUSE_P);
250 void PlayerRadio::jumpToPercent(double percent)
253 logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
254 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
255 switchState(S_JUMP, newFrame);
259 void PlayerRadio::skipForward(UINT seconds)
262 logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
263 ULONG currentSeconds = getCurrentSeconds();
264 ULONG currentFrame = demuxer->getPacketNum();
266 if (currentSeconds == 0) { unLock(); return; } // div by zero
267 if (currentFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
269 ULONG newFrame = currentFrame + (currentFrame * seconds / currentSeconds);
270 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
271 else switchState(S_JUMP, newFrame);
275 void PlayerRadio::skipBackward(UINT seconds)
278 logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
280 ULONG currentSeconds = getCurrentSeconds();
281 ULONG currentFrame = demuxer->getPacketNum();
283 if (currentSeconds == 0) { unLock(); return; } // div by zero
284 if (currentFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
287 if ((UINT)seconds > currentSeconds)
290 newFrame = currentFrame - (currentFrame * seconds / currentSeconds);
292 switchState(S_JUMP, newFrame);
296 // ----------------------------------- Implementations called events
298 void PlayerRadio::switchState(UCHAR toState, ULONG jumpToFrame)
300 if (!initted) return;
302 logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
304 switch(state) // current state selector
306 case S_PLAY: // from S_PLAY -----------------------------------
310 case S_PLAY: // to S_PLAY
314 case S_PAUSE_P: // to S_PAUSE_P
320 case S_STOP: // to S_STOP
330 case S_JUMP: // to S_JUMP
332 restartAtFrame(jumpToFrame);
337 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
341 case S_PLAY: // to S_PLAY
347 case S_PAUSE_P: // to S_PAUSE_P
351 case S_STOP: // to S_STOP
358 audio->systemMuteOff();
362 case S_JUMP: // to S_JUMP
366 restartAtFrame(jumpToFrame);
371 case S_STOP: // from S_STOP -----------------------------------
375 case S_PLAY: // to S_PLAY
380 audio->setStreamType(Audio::MPEG2_PES);
381 audio->systemMuteOff();
384 // FIXME use restartAtFrame here?
385 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
386 demuxer->setPacketNum(currentFrameNumber);
389 logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
395 case S_PAUSE_P: // to S_PAUSE_P
399 case S_STOP: // to S_STOP
403 case S_JUMP: // to S_JUMP
409 // case S_JUMP cannot be selected as a start state because it auto flips to play
413 // ----------------------------------- Internal functions
415 void PlayerRadio::lock()
418 pthread_mutex_lock(&mutex);
419 logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
422 WaitForSingleObject(mutex, INFINITE);
426 void PlayerRadio::unLock()
429 logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
430 pthread_mutex_unlock(&mutex);
436 void PlayerRadio::restartAtFrame(ULONG newFrame)
441 audio->setStreamType(Audio::MPEG2_PES);
443 currentFrameNumber = newFrame;
444 demuxer->setPacketNum(newFrame);
448 audio->systemMuteOff();
452 void PlayerRadio::doConnectionLost()
454 logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
455 Message* m = new Message();
456 m->to = messageReceiver;
458 m->message = Message::PLAYER_EVENT;
459 m->parameter = PlayerRadio::CONNECTION_LOST;
460 messageQueue->postMessage(m);
463 // ----------------------------------- Callback
465 void PlayerRadio::call(void* caller)
467 threadSignalNoLock();
470 // ----------------------------------- Feed thread
472 void PlayerRadio::threadMethod()
474 if (state == S_PLAY) threadFeedPlay();
477 void PlayerRadio::threadFeedPlay()
480 UINT thisRead, writeLength, thisWrite, askFor;
481 time_t lastRescan = time(NULL);
483 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
484 if (!vdr->isConnected()) { doConnectionLost(); return; }
485 logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedFrame %i goto %llu", currentFrameNumber, feedPosition);
496 // If we havn't rescanned for a while..
497 if ((lastRescan + 60) < time(NULL))
499 lengthBytes = vdr->rescanRecording(&lengthFrames);
500 if (!vdr->isConnected()) { doConnectionLost(); return; }
501 logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
502 lastRescan = time(NULL);
504 if (!setLengthSeconds())
506 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
511 if (feedPosition >= lengthBytes) break; // finished playback
515 if (startupBlockSize > lengthBytes)
516 askFor = lengthBytes; // is a very small recording!
518 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
522 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
523 askFor = lengthBytes - feedPosition;
528 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
529 feedPosition += thisRead;
531 if (!vdr->isConnected())
537 if (!threadBuffer) break;
541 int a_stream = demuxer->scan(threadBuffer, thisRead);
542 demuxer->setAudioStream(a_stream);
543 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
549 while(writeLength < thisRead)
551 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
552 writeLength += thisWrite;
556 // demuxer is full and can't take anymore
558 threadWaitForSignal();
571 logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
576 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
577 m->to = messageReceiver;
579 m->message = Message::PLAYER_EVENT;
580 m->parameter = PlayerRadio::STOP_PLAYBACK;
581 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
582 messageQueue->postMessage(m);
585 void PlayerRadio::threadPostStopCleanup()