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, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "playerradio.h"
26 #include "demuxervdr.h"
27 #include "demuxerts.h"
31 #include "messagequeue.h"
33 // ----------------------------------- Called from outside, one offs or info funcs
35 PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver)
38 messageQueue = tmessageQueue;
39 messageReceiver = tmessageReceiver;
40 audio = Audio::getInstance();
41 logger = Log::getInstance();
42 vdr = VDR::getInstance();
55 startupBlockSize = 20000;
57 Video::getInstance()->turnVideoOff();
60 PlayerRadio::~PlayerRadio()
62 if (initted) shutdown();
65 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording)
67 if (initted) return 0;
69 pthread_mutex_init(&mutex, NULL);
71 mutex=CreateMutex(NULL,FALSE,NULL);
75 demuxer = new DemuxerVDR();
77 demuxer = new DemuxerTS();
78 if (!demuxer) return 0;
80 if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
82 logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
90 lengthBytes = tlengthBytes;
91 lengthPackets = tlengthPackets;
93 logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
98 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
101 logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
103 if (!vdr->isConnected()) doConnectionLost();
107 success = demuxer->findPTS(buffer, thisRead, &startPTS);
110 logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
118 if (!setLengthSeconds())
120 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
129 bool PlayerRadio::setLengthSeconds()
134 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
137 logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
138 if (!vdr->isConnected()) doConnectionLost();
142 success = demuxer->findPTS(buffer, thisRead, &endPTS);
146 logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
150 if (startPTS < endPTS)
152 lengthSeconds = (endPTS - startPTS) / 90000;
156 lengthSeconds = (startPTS - endPTS) / 90000;
162 int PlayerRadio::shutdown()
164 if (!initted) return 0;
179 void PlayerRadio::setStartBytes(ULLONG startBytes)
181 streamPos = startBytes;
184 ULONG PlayerRadio::getLengthSeconds()
186 return lengthSeconds;
189 ULONG PlayerRadio::getCurrentSeconds()
191 if (startup) return 0;
193 long long currentPTS = demuxer->getAudioPTS();
194 currentPTS -= startPTS;
195 if (currentPTS < 0) currentPTS += 8589934592ULL;
196 ULONG ret = currentPTS / 90000;
200 // ----------------------------------- Externally called events
202 void PlayerRadio::play()
204 if (!initted) return;
205 if (state == S_PLAY) return;
211 void PlayerRadio::stop()
213 if (!initted) return;
214 if (state == S_STOP) return;
216 logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
221 void PlayerRadio::pause()
223 if (!initted) return;
226 if (state == S_PAUSE_P)
232 switchState(S_PAUSE_P);
238 void PlayerRadio::jumpToPercent(double percent)
241 logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
242 ULONG newPacket = (ULONG)(percent * lengthPackets / 100);
243 switchState(S_JUMP, newPacket);
247 void PlayerRadio::skipForward(UINT seconds)
250 logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
251 ULONG currentSeconds = getCurrentSeconds();
252 ULONG currentPacket = demuxer->getPacketNum();
254 if (currentSeconds == 0) { unLock(); return; } // div by zero
255 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
257 ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds);
258 if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
259 else switchState(S_JUMP, newPacket);
263 void PlayerRadio::skipBackward(UINT seconds)
266 logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
268 ULONG currentSeconds = getCurrentSeconds();
269 ULONG currentPacket = demuxer->getPacketNum();
271 if (currentSeconds == 0) { unLock(); return; } // div by zero
272 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
275 if ((UINT)seconds > currentSeconds)
278 newPacket = currentPacket - (currentPacket * seconds / currentSeconds);
280 switchState(S_JUMP, newPacket);
284 // ----------------------------------- Implementations called events
286 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
288 if (!initted) return;
290 logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
292 switch(state) // current state selector
294 case S_PLAY: // from S_PLAY -----------------------------------
298 case S_PLAY: // to S_PLAY
302 case S_PAUSE_P: // to S_PAUSE_P
308 case S_STOP: // to S_STOP
318 case S_JUMP: // to S_JUMP
320 restartAtPacket(jumpPacket);
325 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
329 case S_PLAY: // to S_PLAY
335 case S_PAUSE_P: // to S_PAUSE_P
339 case S_STOP: // to S_STOP
346 audio->systemMuteOff();
350 case S_JUMP: // to S_JUMP
354 restartAtPacket(jumpPacket);
359 case S_STOP: // from S_STOP -----------------------------------
363 case S_PLAY: // to S_PLAY
368 audio->setStreamType(Audio::MPEG2_PES);
369 audio->systemMuteOff();
372 // FIXME use restartAtPacket here?
373 if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
374 demuxer->setPacketNum(currentPacketNumber);
377 logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
383 case S_PAUSE_P: // to S_PAUSE_P
387 case S_STOP: // to S_STOP
391 case S_JUMP: // to S_JUMP
397 // case S_JUMP cannot be selected as a start state because it auto flips to play
401 // ----------------------------------- Internal functions
403 void PlayerRadio::lock()
406 pthread_mutex_lock(&mutex);
407 logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
410 WaitForSingleObject(mutex, INFINITE);
414 void PlayerRadio::unLock()
417 logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
418 pthread_mutex_unlock(&mutex);
424 void PlayerRadio::restartAtPacket(ULONG newPacket)
429 audio->setStreamType(Audio::MPEG2_PES);
431 currentPacketNumber = newPacket;
432 demuxer->setPacketNum(newPacket);
436 audio->systemMuteOff();
440 void PlayerRadio::doConnectionLost()
442 logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
443 Message* m = new Message();
444 m->to = messageReceiver;
446 m->message = Message::PLAYER_EVENT;
447 m->parameter = PlayerRadio::CONNECTION_LOST;
448 messageQueue->postMessage(m);
451 // ----------------------------------- Callback
453 void PlayerRadio::call(void* caller)
455 threadSignalNoLock();
458 // ----------------------------------- Feed thread
460 void PlayerRadio::threadMethod()
462 if (state == S_PLAY) threadFeedPlay();
465 void PlayerRadio::threadFeedPlay()
468 UINT thisRead, writeLength, thisWrite, askFor;
469 time_t lastRescan = time(NULL);
471 feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
472 if (!vdr->isConnected()) { doConnectionLost(); return; }
473 logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
484 // If we havn't rescanned for a while..
485 if ((lastRescan + 60) < time(NULL))
487 lengthBytes = vdr->rescanRecording(&lengthPackets);
488 if (!vdr->isConnected()) { doConnectionLost(); return; }
489 logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
490 lastRescan = time(NULL);
492 if (!setLengthSeconds())
494 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
499 if (feedPosition >= lengthBytes) break; // finished playback
503 if (startupBlockSize > lengthBytes)
504 askFor = lengthBytes; // is a very small recording!
506 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
510 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
511 askFor = lengthBytes - feedPosition;
516 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
517 feedPosition += thisRead;
519 if (!vdr->isConnected())
525 if (!threadBuffer) break;
529 int a_stream = demuxer->scan(threadBuffer, thisRead);
530 demuxer->setAudioStream(a_stream);
531 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
537 while(writeLength < thisRead)
539 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
540 writeLength += thisWrite;
544 // demuxer is full and can't take anymore
546 threadWaitForSignal();
559 logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
564 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
565 m->to = messageReceiver;
567 m->message = Message::PLAYER_EVENT;
568 m->parameter = PlayerRadio::STOP_PLAYBACK;
569 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
570 messageQueue->postMessage(m);
573 void PlayerRadio::threadPostStopCleanup()