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, bool tIsRecording)
38 messageQueue = tmessageQueue;
39 messageReceiver = tmessageReceiver;
40 audio = Audio::getInstance();
41 logger = Log::getInstance();
42 vdr = VDR::getInstance();
52 isRecording = tIsRecording;
57 startupBlockSize = 20000;
58 preBufferSize = 40000;
60 Video::getInstance()->turnVideoOff();
63 PlayerRadio::~PlayerRadio()
65 if (initted) shutdown();
68 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets)
70 if (initted) return 0;
72 pthread_mutex_init(&mutex, NULL);
74 mutex=CreateMutex(NULL,FALSE,NULL);
78 demuxer = new DemuxerVDR();
80 demuxer = new DemuxerTS();
81 if (!demuxer) return 0;
83 if (!demuxer->init(this, audio, NULL, 0, 40000))
85 logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
93 lengthBytes = tlengthBytes;
94 lengthPackets = tlengthPackets;
96 logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
103 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
106 logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
108 if (!vdr->isConnected()) doConnectionLost();
112 success = demuxer->findPTS(buffer, thisRead, &startPTS);
115 logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
123 if (!setLengthSeconds())
125 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
135 bool PlayerRadio::setLengthSeconds()
140 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
143 logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
144 if (!vdr->isConnected()) doConnectionLost();
148 success = demuxer->findPTS(buffer, thisRead, &endPTS);
152 logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
156 if (startPTS < endPTS)
158 lengthSeconds = (endPTS - startPTS) / 90000;
162 lengthSeconds = (startPTS - endPTS) / 90000;
168 int PlayerRadio::shutdown()
170 if (!initted) return 0;
185 void PlayerRadio::setStartBytes(ULLONG startBytes)
187 streamPos = startBytes;
190 ULONG PlayerRadio::getLengthSeconds()
192 return lengthSeconds;
195 ULONG PlayerRadio::getCurrentSeconds()
197 if (startup) return 0;
199 long long currentPTS = demuxer->getAudioPTS();
200 currentPTS -= startPTS;
201 if (currentPTS < 0) currentPTS += 8589934592ULL;
202 ULONG ret = currentPTS / 90000;
206 // ----------------------------------- Externally called events
208 void PlayerRadio::play(ULONG Apid)
211 DemuxerTS* dts = (DemuxerTS*)demuxer;
212 dts->setAID(Apid,0/*Radio is always mpeg?*/);
216 void PlayerRadio::play()
218 if (!initted) return;
219 if (state == S_PLAY) return;
225 void PlayerRadio::stop()
227 if (!initted) return;
228 if (state == S_STOP) return;
230 logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
235 void PlayerRadio::pause()
237 if (!initted) return;
240 if (state == S_PAUSE_P)
246 switchState(S_PAUSE_P);
252 void PlayerRadio::jumpToPercent(double percent)
255 logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
256 ULONG newPacket = (ULONG)(percent * lengthPackets / 100);
257 switchState(S_JUMP, newPacket);
261 void PlayerRadio::skipForward(UINT seconds)
264 logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
265 ULONG currentSeconds = getCurrentSeconds();
266 ULONG currentPacket = demuxer->getPacketNum();
268 if (currentSeconds == 0) { unLock(); return; } // div by zero
269 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
271 ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds);
272 if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
273 else switchState(S_JUMP, newPacket);
277 void PlayerRadio::skipBackward(UINT seconds)
280 logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
282 ULONG currentSeconds = getCurrentSeconds();
283 ULONG currentPacket = demuxer->getPacketNum();
285 if (currentSeconds == 0) { unLock(); return; } // div by zero
286 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
289 if ((UINT)seconds > currentSeconds)
292 newPacket = currentPacket - (currentPacket * seconds / currentSeconds);
294 switchState(S_JUMP, newPacket);
298 // ----------------------------------- Implementations called events
300 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
302 if (!initted) return;
304 logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
306 switch(state) // current state selector
308 case S_PLAY: // from S_PLAY -----------------------------------
312 case S_PLAY: // to S_PLAY
316 case S_PAUSE_P: // to S_PAUSE_P
322 case S_STOP: // to S_STOP
332 case S_JUMP: // to S_JUMP
334 restartAtPacket(jumpPacket);
339 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
343 case S_PLAY: // to S_PLAY
349 case S_PAUSE_P: // to S_PAUSE_P
353 case S_STOP: // to S_STOP
360 audio->systemMuteOff();
364 case S_JUMP: // to S_JUMP
368 restartAtPacket(jumpPacket);
373 case S_STOP: // from S_STOP -----------------------------------
377 case S_PLAY: // to S_PLAY
382 audio->setStreamType(Audio::MPEG2_PES);
383 audio->systemMuteOff();
387 // FIXME use restartAtPacket here?
388 if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
389 demuxer->setPacketNum(currentPacketNumber);
397 logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
401 else // do prebuffering
403 logger->log("PlayerRadio", Log::DEBUG, "Prebuffering...");
408 case S_PAUSE_P: // to S_PAUSE_P
412 case S_STOP: // to S_STOP
416 case S_JUMP: // to S_JUMP
422 // case S_JUMP cannot be selected as a start state because it auto flips to play
426 // ----------------------------------- Internal functions
428 void PlayerRadio::lock()
431 pthread_mutex_lock(&mutex);
432 logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
435 WaitForSingleObject(mutex, INFINITE);
439 void PlayerRadio::unLock()
442 logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
443 pthread_mutex_unlock(&mutex);
449 void PlayerRadio::restartAtPacket(ULONG newPacket)
454 audio->setStreamType(Audio::MPEG2_PES);
456 currentPacketNumber = newPacket;
457 demuxer->setPacketNum(newPacket);
461 audio->systemMuteOff();
465 void PlayerRadio::doConnectionLost()
467 logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
468 Message* m = new Message();
469 m->to = messageReceiver;
471 m->message = Message::PLAYER_EVENT;
472 m->parameter = PlayerRadio::CONNECTION_LOST;
473 messageQueue->postMessage(m);
476 // ----------------------------------- Callback
478 void PlayerRadio::call(void* caller)
480 threadSignalNoLock();
483 // ----------------------------------- Feed thread
485 void PlayerRadio::threadMethod()
489 if (state == S_PLAY) threadFeedPlay();
497 void PlayerRadio::threadFeedLive()
502 UINT preBufferTotal = 0;
514 askFor = startupBlockSize; // find audio streams sized block
516 askFor = blockSize; // normal
518 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
520 if (!vdr->isConnected())
526 if (!threadBuffer) break;
530 int a_stream = demuxer->scan(threadBuffer, thisRead);
531 demuxer->setAudioStream(a_stream);
532 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
538 preBufferTotal += thisRead;
539 if (preBufferTotal >= preBufferSize)
541 logger->log("PlayerRadio", Log::DEBUG, "Got >500K, prebuffering complete");
543 preBuffering = false;
548 // unLock(); // thread will be locked by play until here
549 // FIXME - see if this can segfault because it is starting threads out of the master mutex
555 while(writeLength < thisRead)
557 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
558 writeLength += thisWrite;
562 // demuxer is full and can't take anymore
564 threadWaitForSignal();
576 logger->log("PlayerRadio", Log::DEBUG, "Live play failed to start or interrupted");
580 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
581 m->to = messageReceiver;
583 m->message = Message::PLAYER_EVENT;
584 m->parameter = PlayerRadio::STREAM_END;
585 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
586 messageQueue->postMessage(m);
587 logger->log("PlayerRadio", Log::DEBUG, "Message posted...");
590 void PlayerRadio::threadFeedPlay()
593 UINT thisRead, writeLength, thisWrite, askFor;
594 time_t lastRescan = time(NULL);
596 feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
597 if (!vdr->isConnected()) { doConnectionLost(); return; }
598 logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
609 // If we havn't rescanned for a while..
610 if ((lastRescan + 60) < time(NULL))
612 lengthBytes = vdr->rescanRecording(&lengthPackets);
613 if (!vdr->isConnected()) { doConnectionLost(); return; }
614 logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
615 lastRescan = time(NULL);
617 if (!setLengthSeconds())
619 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
624 if (feedPosition >= lengthBytes) break; // finished playback
628 if (startupBlockSize > lengthBytes)
629 askFor = lengthBytes; // is a very small recording!
631 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
635 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
636 askFor = lengthBytes - feedPosition;
641 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
642 feedPosition += thisRead;
644 if (!vdr->isConnected())
650 if (!threadBuffer) break;
654 int a_stream = demuxer->scan(threadBuffer, thisRead);
655 demuxer->setAudioStream(a_stream);
656 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
662 while(writeLength < thisRead)
664 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
665 writeLength += thisWrite;
669 // demuxer is full and can't take anymore
671 threadWaitForSignal();
684 logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
689 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
690 m->to = messageReceiver;
692 m->message = Message::PLAYER_EVENT;
693 m->parameter = PlayerRadio::STOP_PLAYBACK;
694 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
695 messageQueue->postMessage(m);
698 void PlayerRadio::threadPostStopCleanup()