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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "playerradio.h"
23 // ----------------------------------- Called from outside, one offs or info funcs
25 PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, bool tIsRecording)
28 messageQueue = tmessageQueue;
29 messageReceiver = tmessageReceiver;
30 audio = Audio::getInstance();
31 logger = Log::getInstance();
32 vdr = VDR::getInstance();
42 isRecording = tIsRecording;
47 startupBlockSize = 20000;
48 preBufferSize = 40000;
50 Video::getInstance()->turnVideoOff();
53 PlayerRadio::~PlayerRadio()
55 if (initted) shutdown();
58 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets)
60 if (initted) return 0;
62 pthread_mutex_init(&mutex, NULL);
64 mutex=CreateMutex(NULL,FALSE,NULL);
68 demuxer = new DemuxerVDR();
70 demuxer = new DemuxerTS();
71 if (!demuxer) return 0;
73 if (!demuxer->init(this, audio, NULL, 0, 40000))
75 logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
83 lengthBytes = tlengthBytes;
84 lengthPackets = tlengthPackets;
86 logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
93 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
96 logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
101 success = demuxer->findPTS(buffer, thisRead, &startPTS);
104 logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
112 if (!setLengthSeconds())
114 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
124 bool PlayerRadio::setLengthSeconds()
129 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
132 logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
136 success = demuxer->findPTS(buffer, thisRead, &endPTS);
140 logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
144 if (startPTS < endPTS)
146 lengthSeconds = (endPTS - startPTS) / 90000;
150 lengthSeconds = (startPTS - endPTS) / 90000;
156 int PlayerRadio::shutdown()
158 if (!initted) return 0;
173 void PlayerRadio::setStartBytes(ULLONG startBytes)
175 streamPos = startBytes;
178 ULONG PlayerRadio::getLengthSeconds()
180 return lengthSeconds;
183 ULONG PlayerRadio::getCurrentSeconds()
185 if (startup) return 0;
187 long long currentPTS = demuxer->getAudioPTS();
188 currentPTS -= startPTS;
189 if (currentPTS < 0) currentPTS += 8589934592ULL;
190 ULONG ret = currentPTS / 90000;
194 // ----------------------------------- Externally called events
196 void PlayerRadio::play(ULONG Apid)
199 DemuxerTS* dts = (DemuxerTS*)demuxer;
204 void PlayerRadio::play()
206 if (!initted) return;
207 if (state == S_PLAY) return;
213 void PlayerRadio::stop()
215 if (!initted) return;
216 if (state == S_STOP) return;
218 logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
223 void PlayerRadio::pause()
225 if (!initted) return;
228 if (state == S_PAUSE_P)
234 switchState(S_PAUSE_P);
240 void PlayerRadio::jumpToPercent(double percent)
243 logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
244 ULONG newPacket = (ULONG)(percent * lengthPackets / 100);
245 switchState(S_JUMP, newPacket);
249 void PlayerRadio::skipForward(UINT seconds)
252 logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
253 ULONG currentSeconds = getCurrentSeconds();
254 ULONG currentPacket = demuxer->getPacketNum();
256 if (currentSeconds == 0) { unLock(); return; } // div by zero
257 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
259 ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds);
260 if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
261 else switchState(S_JUMP, newPacket);
265 void PlayerRadio::skipBackward(UINT seconds)
268 logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
270 ULONG currentSeconds = getCurrentSeconds();
271 ULONG currentPacket = demuxer->getPacketNum();
273 if (currentSeconds == 0) { unLock(); return; } // div by zero
274 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
277 if ((UINT)seconds > currentSeconds)
280 newPacket = currentPacket - (currentPacket * seconds / currentSeconds);
282 switchState(S_JUMP, newPacket);
286 // ----------------------------------- Implementations called events
288 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
290 if (!initted) return;
292 logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
294 switch(state) // current state selector
296 case S_PLAY: // from S_PLAY -----------------------------------
300 case S_PLAY: // to S_PLAY
304 case S_PAUSE_P: // to S_PAUSE_P
310 case S_STOP: // to S_STOP
320 case S_JUMP: // to S_JUMP
322 restartAtPacket(jumpPacket);
327 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
331 case S_PLAY: // to S_PLAY
337 case S_PAUSE_P: // to S_PAUSE_P
341 case S_STOP: // to S_STOP
348 audio->systemMuteOff();
352 case S_JUMP: // to S_JUMP
356 restartAtPacket(jumpPacket);
361 case S_STOP: // from S_STOP -----------------------------------
365 case S_PLAY: // to S_PLAY
370 audio->setStreamType(Audio::MPEG2_PES);
371 audio->systemMuteOff();
375 // FIXME use restartAtPacket here?
376 if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
377 demuxer->setPacketNum(currentPacketNumber);
385 logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
389 else // do prebuffering
391 logger->log("PlayerRadio", Log::DEBUG, "Prebuffering...");
396 case S_PAUSE_P: // to S_PAUSE_P
400 case S_STOP: // to S_STOP
404 case S_JUMP: // to S_JUMP
410 // case S_JUMP cannot be selected as a start state because it auto flips to play
414 // ----------------------------------- Internal functions
416 void PlayerRadio::lock()
419 pthread_mutex_lock(&mutex);
420 logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
423 WaitForSingleObject(mutex, INFINITE);
427 void PlayerRadio::unLock()
430 logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
431 pthread_mutex_unlock(&mutex);
437 void PlayerRadio::restartAtPacket(ULONG newPacket)
442 audio->setStreamType(Audio::MPEG2_PES);
444 currentPacketNumber = newPacket;
445 demuxer->setPacketNum(newPacket);
449 audio->systemMuteOff();
453 void PlayerRadio::doConnectionLost()
455 logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
456 Message* m = new Message();
457 m->to = messageReceiver;
459 m->message = Message::PLAYER_EVENT;
460 m->parameter = PlayerRadio::CONNECTION_LOST;
461 messageQueue->postMessage(m);
464 // ----------------------------------- Callback
466 void PlayerRadio::call(void* caller)
468 threadSignalNoLock();
471 // ----------------------------------- Feed thread
473 void PlayerRadio::threadMethod()
477 if (state == S_PLAY) threadFeedPlay();
485 void PlayerRadio::threadFeedLive()
490 UINT preBufferTotal = 0;
502 askFor = startupBlockSize; // find audio streams sized block
504 askFor = blockSize; // normal
506 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
508 if (!vdr->isConnected())
514 if (!threadBuffer) break;
518 int a_stream = demuxer->scan(threadBuffer, thisRead);
519 demuxer->setAudioStream(a_stream);
520 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
526 preBufferTotal += thisRead;
527 if (preBufferTotal >= preBufferSize)
529 logger->log("PlayerRadio", Log::DEBUG, "Got >500K, prebuffering complete");
531 preBuffering = false;
536 // unLock(); // thread will be locked by play until here
537 // FIXME - see if this can segfault because it is starting threads out of the master mutex
543 while(writeLength < thisRead)
545 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
546 writeLength += thisWrite;
550 // demuxer is full and can't take anymore
552 threadWaitForSignal();
564 logger->log("PlayerRadio", Log::DEBUG, "Live play failed to start or interrupted");
568 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
569 m->to = messageReceiver;
571 m->message = Message::PLAYER_EVENT;
572 m->parameter = PlayerRadio::STREAM_END;
573 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
574 messageQueue->postMessage(m);
575 logger->log("PlayerRadio", Log::DEBUG, "Message posted...");
578 void PlayerRadio::threadFeedPlay()
581 UINT thisRead, writeLength, thisWrite, askFor;
582 time_t lastRescan = time(NULL);
584 feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
585 if (!vdr->isConnected()) { doConnectionLost(); return; }
586 logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
597 // If we havn't rescanned for a while..
598 if ((lastRescan + 60) < time(NULL))
600 lengthBytes = vdr->rescanRecording(&lengthPackets);
601 if (!vdr->isConnected()) { doConnectionLost(); return; }
602 logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
603 lastRescan = time(NULL);
605 if (!setLengthSeconds())
607 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
612 if (feedPosition >= lengthBytes) break; // finished playback
616 if (startupBlockSize > lengthBytes)
617 askFor = lengthBytes; // is a very small recording!
619 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
623 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
624 askFor = lengthBytes - feedPosition;
629 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
630 feedPosition += thisRead;
632 if (!vdr->isConnected())
638 if (!threadBuffer) break;
642 int a_stream = demuxer->scan(threadBuffer, thisRead);
643 demuxer->setAudioStream(a_stream);
644 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
650 while(writeLength < thisRead)
652 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
653 writeLength += thisWrite;
657 // demuxer is full and can't take anymore
659 threadWaitForSignal();
672 logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
677 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
678 m->to = messageReceiver;
680 m->message = Message::PLAYER_EVENT;
681 m->parameter = PlayerRadio::STOP_PLAYBACK;
682 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
683 messageQueue->postMessage(m);
686 void PlayerRadio::threadPostStopCleanup()