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"
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");
111 success = demuxer->findPTS(buffer, thisRead, &startPTS);
114 logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
122 if (!setLengthSeconds())
124 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
134 bool PlayerRadio::setLengthSeconds()
139 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
142 logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
146 success = demuxer->findPTS(buffer, thisRead, &endPTS);
150 logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
154 if (startPTS < endPTS)
156 lengthSeconds = (endPTS - startPTS) / 90000;
160 lengthSeconds = (startPTS - endPTS) / 90000;
166 int PlayerRadio::shutdown()
168 if (!initted) return 0;
183 void PlayerRadio::setStartBytes(ULLONG startBytes)
185 streamPos = startBytes;
188 ULONG PlayerRadio::getLengthSeconds()
190 return lengthSeconds;
193 ULONG PlayerRadio::getCurrentSeconds()
195 if (startup) return 0;
197 long long currentPTS = demuxer->getAudioPTS();
198 currentPTS -= startPTS;
199 if (currentPTS < 0) currentPTS += 8589934592ULL;
200 ULONG ret = currentPTS / 90000;
204 // ----------------------------------- Externally called events
206 void PlayerRadio::play(ULONG Apid)
209 DemuxerTS* dts = (DemuxerTS*)demuxer;
214 void PlayerRadio::play()
216 if (!initted) return;
217 if (state == S_PLAY) return;
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 newPacket = (ULONG)(percent * lengthPackets / 100);
255 switchState(S_JUMP, newPacket);
259 void PlayerRadio::skipForward(UINT seconds)
262 logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
263 ULONG currentSeconds = getCurrentSeconds();
264 ULONG currentPacket = demuxer->getPacketNum();
266 if (currentSeconds == 0) { unLock(); return; } // div by zero
267 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
269 ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds);
270 if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
271 else switchState(S_JUMP, newPacket);
275 void PlayerRadio::skipBackward(UINT seconds)
278 logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
280 ULONG currentSeconds = getCurrentSeconds();
281 ULONG currentPacket = demuxer->getPacketNum();
283 if (currentSeconds == 0) { unLock(); return; } // div by zero
284 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
287 if ((UINT)seconds > currentSeconds)
290 newPacket = currentPacket - (currentPacket * seconds / currentSeconds);
292 switchState(S_JUMP, newPacket);
296 // ----------------------------------- Implementations called events
298 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
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 restartAtPacket(jumpPacket);
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 restartAtPacket(jumpPacket);
371 case S_STOP: // from S_STOP -----------------------------------
375 case S_PLAY: // to S_PLAY
380 audio->setStreamType(Audio::MPEG2_PES);
381 audio->systemMuteOff();
385 // FIXME use restartAtPacket here?
386 if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
387 demuxer->setPacketNum(currentPacketNumber);
395 logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
399 else // do prebuffering
401 logger->log("PlayerRadio", Log::DEBUG, "Prebuffering...");
406 case S_PAUSE_P: // to S_PAUSE_P
410 case S_STOP: // to S_STOP
414 case S_JUMP: // to S_JUMP
420 // case S_JUMP cannot be selected as a start state because it auto flips to play
424 // ----------------------------------- Internal functions
426 void PlayerRadio::lock()
429 pthread_mutex_lock(&mutex);
430 logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
433 WaitForSingleObject(mutex, INFINITE);
437 void PlayerRadio::unLock()
440 logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
441 pthread_mutex_unlock(&mutex);
447 void PlayerRadio::restartAtPacket(ULONG newPacket)
452 audio->setStreamType(Audio::MPEG2_PES);
454 currentPacketNumber = newPacket;
455 demuxer->setPacketNum(newPacket);
459 audio->systemMuteOff();
463 void PlayerRadio::doConnectionLost()
465 logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
466 Message* m = new Message();
467 m->to = messageReceiver;
469 m->message = Message::PLAYER_EVENT;
470 m->parameter = PlayerRadio::CONNECTION_LOST;
471 messageQueue->postMessage(m);
474 // ----------------------------------- Callback
476 void PlayerRadio::call(void* caller)
478 threadSignalNoLock();
481 // ----------------------------------- Feed thread
483 void PlayerRadio::threadMethod()
487 if (state == S_PLAY) threadFeedPlay();
495 void PlayerRadio::threadFeedLive()
500 UINT preBufferTotal = 0;
512 askFor = startupBlockSize; // find audio streams sized block
514 askFor = blockSize; // normal
516 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
518 if (!vdr->isConnected())
524 if (!threadBuffer) break;
528 int a_stream = demuxer->scan(threadBuffer, thisRead);
529 demuxer->setAudioStream(a_stream);
530 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
536 preBufferTotal += thisRead;
537 if (preBufferTotal >= preBufferSize)
539 logger->log("PlayerRadio", Log::DEBUG, "Got >500K, prebuffering complete");
541 preBuffering = false;
546 // unLock(); // thread will be locked by play until here
547 // FIXME - see if this can segfault because it is starting threads out of the master mutex
553 while(writeLength < thisRead)
555 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
556 writeLength += thisWrite;
560 // demuxer is full and can't take anymore
562 threadWaitForSignal();
574 logger->log("PlayerRadio", Log::DEBUG, "Live play failed to start or interrupted");
578 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
579 m->to = messageReceiver;
581 m->message = Message::PLAYER_EVENT;
582 m->parameter = PlayerRadio::STREAM_END;
583 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
584 messageQueue->postMessage(m);
585 logger->log("PlayerRadio", Log::DEBUG, "Message posted...");
588 void PlayerRadio::threadFeedPlay()
591 UINT thisRead, writeLength, thisWrite, askFor;
592 time_t lastRescan = time(NULL);
594 feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
595 if (!vdr->isConnected()) { doConnectionLost(); return; }
596 logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
607 // If we havn't rescanned for a while..
608 if ((lastRescan + 60) < time(NULL))
610 lengthBytes = vdr->rescanRecording(&lengthPackets);
611 if (!vdr->isConnected()) { doConnectionLost(); return; }
612 logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
613 lastRescan = time(NULL);
615 if (!setLengthSeconds())
617 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
622 if (feedPosition >= lengthBytes) break; // finished playback
626 if (startupBlockSize > lengthBytes)
627 askFor = lengthBytes; // is a very small recording!
629 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
633 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
634 askFor = lengthBytes - feedPosition;
639 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
640 feedPosition += thisRead;
642 if (!vdr->isConnected())
648 if (!threadBuffer) break;
652 int a_stream = demuxer->scan(threadBuffer, thisRead);
653 demuxer->setAudioStream(a_stream);
654 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
660 while(writeLength < thisRead)
662 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
663 writeLength += thisWrite;
667 // demuxer is full and can't take anymore
669 threadWaitForSignal();
682 logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
687 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
688 m->to = messageReceiver;
690 m->message = Message::PLAYER_EVENT;
691 m->parameter = PlayerRadio::STOP_PLAYBACK;
692 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
693 messageQueue->postMessage(m);
696 void PlayerRadio::threadPostStopCleanup()