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 = 20000;
51 PlayerRadio::~PlayerRadio()
53 if (initted) shutdown();
56 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets)
58 if (initted) return 0;
60 pthread_mutex_init(&mutex, NULL);
62 mutex=CreateMutex(NULL,FALSE,NULL);
65 demuxer = new DemuxerVDR();
66 if (!demuxer) return 0;
68 if (!demuxer->init(this, audio, NULL, 0, 40000))
70 logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
78 lengthBytes = tlengthBytes;
79 lengthPackets = tlengthPackets;
81 logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
87 threadBuffer = vdr->getBlock(0, 10000, &thisRead);
90 logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
95 success = demuxer->findPTS(threadBuffer, thisRead, &startPTS);
98 logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
103 threadBuffer = vdr->getBlock(tlengthBytes - 10000, 10000, &thisRead);
106 logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
111 success = demuxer->findPTS(threadBuffer, thisRead, &endPTS);
114 logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
119 logger->log("PlayerRadio", Log::DEBUG, "Start: %llu, End: %llu", startPTS, endPTS);
121 if (startPTS < endPTS)
123 lengthSeconds = (endPTS - startPTS) / 90000;
127 lengthSeconds = (startPTS - endPTS) / 90000;
134 int PlayerRadio::shutdown()
136 if (!initted) return 0;
151 void PlayerRadio::setStartBytes(ULLONG startBytes)
153 streamPos = startBytes;
156 ULONG PlayerRadio::getLengthSeconds()
158 return lengthSeconds;
161 ULONG PlayerRadio::getCurrentSeconds()
163 if (startup) return 0;
165 long long currentPTS = demuxer->getAudioPTS();
166 currentPTS -= startPTS;
167 if (currentPTS < 0) currentPTS += 8589934592ULL;
168 ULONG ret = currentPTS / 90000;
172 // ----------------------------------- Externally called events
174 void PlayerRadio::play()
176 if (!initted) return;
177 if (state == S_PLAY) return;
183 void PlayerRadio::stop()
185 if (!initted) return;
186 if (state == S_STOP) return;
188 logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
193 void PlayerRadio::pause()
195 if (!initted) return;
198 if (state == S_PAUSE_P)
204 switchState(S_PAUSE_P);
210 void PlayerRadio::jumpToPercent(int percent)
213 logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
214 ULONG newPacket = percent * lengthPackets / 100;
215 switchState(S_JUMP, newPacket);
219 void PlayerRadio::skipForward(int seconds)
222 logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
223 ULONG currentSeconds = getCurrentSeconds();
224 ULONG currentPacket = demuxer->getPacketNum();
226 if (currentSeconds == 0) { unLock(); return; } // div by zero
227 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
229 ULONG newPacket = demuxer->getPacketNum() / currentSeconds * (currentSeconds + seconds);
230 if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
231 else switchState(S_JUMP, newPacket);
235 void PlayerRadio::skipBackward(int seconds) // FIXME why are these signed?!
238 logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
240 ULONG currentSeconds = getCurrentSeconds();
241 ULONG currentPacket = demuxer->getPacketNum();
243 if (currentSeconds == 0) { unLock(); return; } // div by zero
244 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
247 if ((UINT)seconds > currentSeconds)
250 newPacket = demuxer->getPacketNum() / currentSeconds * (currentSeconds - seconds);
252 switchState(S_JUMP, newPacket);
256 // ----------------------------------- Implementations called events
258 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
260 if (!initted) return;
262 logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
264 switch(state) // current state selector
266 case S_PLAY: // from S_PLAY -----------------------------------
270 case S_PLAY: // to S_PLAY
274 case S_PAUSE_P: // to S_PAUSE_P
280 case S_STOP: // to S_STOP
290 case S_JUMP: // to S_JUMP
292 restartAtPacket(jumpPacket);
297 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
301 case S_PLAY: // to S_PLAY
307 case S_PAUSE_P: // to S_PAUSE_P
311 case S_STOP: // to S_STOP
318 audio->systemMuteOff();
322 case S_JUMP: // to S_JUMP
326 restartAtPacket(jumpPacket);
331 case S_STOP: // from S_STOP -----------------------------------
335 case S_PLAY: // to S_PLAY
340 audio->systemMuteOff();
344 // FIXME use restartAtPacket here?
345 if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
346 demuxer->setPacketNum(currentPacketNumber);
354 logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
358 else // do prebuffering
360 logger->log("PlayerRadio", Log::DEBUG, "Prebuffering...");
365 case S_PAUSE_P: // to S_PAUSE_P
369 case S_STOP: // to S_STOP
373 case S_JUMP: // to S_JUMP
379 // case S_JUMP cannot be selected as a start state because it auto flips to play
383 // ----------------------------------- Internal functions
385 void PlayerRadio::lock()
388 pthread_mutex_lock(&mutex);
389 logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
392 WaitForSingleObject(mutex, INFINITE);
396 void PlayerRadio::unLock()
399 logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
400 pthread_mutex_unlock(&mutex);
406 void PlayerRadio::restartAtPacket(ULONG newPacket)
412 currentPacketNumber = newPacket;
413 demuxer->setPacketNum(newPacket);
417 audio->systemMuteOff();
421 void PlayerRadio::doConnectionLost()
423 logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
424 Message* m = new Message();
425 m->to = messageReceiver;
427 m->message = Message::PLAYER_EVENT;
428 m->parameter = PlayerRadio::CONNECTION_LOST;
429 messageQueue->postMessage(m);
432 // ----------------------------------- Callback
434 void PlayerRadio::call(void* caller)
436 threadSignalNoLock();
439 // ----------------------------------- Feed thread
441 void PlayerRadio::threadMethod()
445 if (state == S_PLAY) threadFeedPlay();
453 void PlayerRadio::threadFeedLive()
458 UINT preBufferTotal = 0;
470 askFor = startupBlockSize; // find audio streams sized block
472 askFor = blockSize; // normal
474 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
476 if (!vdr->isConnected())
482 if (!threadBuffer) break;
486 int a_stream = demuxer->scan(threadBuffer, thisRead);
487 demuxer->setAudioStream(a_stream);
488 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
494 preBufferTotal += thisRead;
495 if (preBufferTotal >= preBufferSize)
497 logger->log("PlayerRadio", Log::DEBUG, "Got >500K, prebuffering complete");
499 preBuffering = false;
504 // unLock(); // thread will be locked by play until here
505 // FIXME - see if this can segfault because it is starting threads out of the master mutex
511 while(writeLength < thisRead)
513 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
514 writeLength += thisWrite;
518 // demuxer is full and can't take anymore
520 threadWaitForSignal();
532 logger->log("PlayerRadio", Log::DEBUG, "Live play failed to start or interrupted");
536 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
537 m->to = messageReceiver;
539 m->message = Message::PLAYER_EVENT;
540 m->parameter = PlayerRadio::STREAM_END;
541 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
542 messageQueue->postMessage(m);
543 logger->log("PlayerRadio", Log::DEBUG, "Message posted...");
546 void PlayerRadio::threadFeedPlay()
549 UINT thisRead, writeLength, thisWrite, askFor;
550 time_t lastRescan = time(NULL);
552 feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
553 if (!vdr->isConnected()) { doConnectionLost(); return; }
554 logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
565 // If we havn't rescanned for a while..
566 if ((lastRescan + 60) < time(NULL))
568 lengthBytes = vdr->rescanRecording(&lengthPackets);
569 if (!vdr->isConnected()) { doConnectionLost(); return; }
570 logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
571 lastRescan = time(NULL);
574 if (feedPosition >= lengthBytes) break; // finished playback
578 if (startupBlockSize > lengthBytes)
579 askFor = lengthBytes; // is a very small recording!
581 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
585 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
586 askFor = lengthBytes - feedPosition;
591 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
592 feedPosition += thisRead;
594 if (!vdr->isConnected())
600 if (!threadBuffer) break;
604 int a_stream = demuxer->scan(threadBuffer, thisRead);
605 demuxer->setAudioStream(a_stream);
606 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
612 while(writeLength < thisRead)
614 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
615 writeLength += thisWrite;
619 // demuxer is full and can't take anymore
621 threadWaitForSignal();
634 logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
639 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
640 m->to = messageReceiver;
642 m->message = Message::PLAYER_EVENT;
643 m->parameter = PlayerRadio::STOP_PLAYBACK;
644 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
645 messageQueue->postMessage(m);
648 void PlayerRadio::threadPostStopCleanup()