2 Copyright 2004-2006 Chris Tallon
\r
4 This file is part of VOMP.
\r
6 VOMP is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 VOMP is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with VOMP; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
21 #include "playerradio.h"
\r
26 #include "demuxervdr.h"
\r
27 #include "demuxerts.h"
\r
30 #include "message.h"
\r
31 #include "messagequeue.h"
\r
33 // ----------------------------------- Called from outside, one offs or info funcs
\r
35 PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver)
\r
38 messageQueue = tmessageQueue;
\r
39 messageReceiver = tmessageReceiver;
\r
40 audio = Audio::getInstance();
\r
41 logger = Log::getInstance();
\r
42 vdr = VDR::getInstance();
\r
52 threadBuffer = NULL;
\r
55 startupBlockSize = 20000;
\r
57 Video::getInstance()->turnVideoOff();
\r
60 PlayerRadio::~PlayerRadio()
\r
62 if (initted) shutdown();
\r
65 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording)
\r
67 if (initted) return 0;
\r
69 pthread_mutex_init(&mutex, NULL);
\r
71 mutex=CreateMutex(NULL,FALSE,NULL);
\r
75 demuxer = new DemuxerVDR();
\r
77 demuxer = new DemuxerTS();
\r
78 if (!demuxer) return 0;
\r
80 if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
\r
82 logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
\r
90 lengthBytes = tlengthBytes;
\r
91 lengthPackets = tlengthPackets;
\r
93 logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
\r
98 UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
\r
101 logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
\r
103 if (!vdr->isConnected()) doConnectionLost();
\r
107 success = demuxer->findPTS(buffer, thisRead, &startPTS);
\r
110 logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
\r
118 if (!setLengthSeconds())
\r
120 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
\r
129 bool PlayerRadio::setLengthSeconds()
\r
134 UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
\r
137 logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
\r
138 if (!vdr->isConnected()) doConnectionLost();
\r
142 success = demuxer->findPTS(buffer, thisRead, &endPTS);
\r
146 logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
\r
150 if (startPTS < endPTS)
\r
152 lengthSeconds = (endPTS - startPTS) / 90000;
\r
156 lengthSeconds = (startPTS - endPTS) / 90000;
\r
162 int PlayerRadio::shutdown()
\r
164 if (!initted) return 0;
\r
165 switchState(S_STOP);
\r
172 CloseHandle(mutex);
\r
179 void PlayerRadio::setStartBytes(ULLONG startBytes)
\r
181 streamPos = startBytes;
\r
184 ULONG PlayerRadio::getLengthSeconds()
\r
186 return lengthSeconds;
\r
189 ULONG PlayerRadio::getCurrentSeconds()
\r
191 if (startup) return 0;
\r
193 long long currentPTS = demuxer->getAudioPTS();
\r
194 currentPTS -= startPTS;
\r
195 if (currentPTS < 0) currentPTS += 8589934592ULL;
\r
196 ULONG ret = currentPTS / 90000;
\r
200 // ----------------------------------- Externally called events
\r
202 void PlayerRadio::play()
\r
204 if (!initted) return;
\r
205 if (state == S_PLAY) return;
\r
207 switchState(S_PLAY);
\r
212 void PlayerRadio::playpause()
\r
214 if (!initted) return;
\r
216 if (state==S_PLAY) {
\r
217 switchState(S_PAUSE_P);
\r
219 switchState(S_PLAY);
\r
224 void PlayerRadio::stop()
\r
226 if (!initted) return;
\r
227 if (state == S_STOP) return;
\r
229 logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
\r
230 switchState(S_STOP);
\r
234 void PlayerRadio::pause()
\r
236 if (!initted) return;
\r
239 if (state == S_PAUSE_P)
\r
241 switchState(S_PLAY);
\r
245 switchState(S_PAUSE_P);
\r
251 void PlayerRadio::jumpToPercent(double percent)
\r
254 logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
\r
255 ULONG newPacket = (ULONG)(percent * lengthPackets / 100);
\r
256 switchState(S_JUMP, newPacket);
\r
260 void PlayerRadio::skipForward(UINT seconds)
\r
263 logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
\r
264 ULONG currentSeconds = getCurrentSeconds();
\r
265 ULONG currentPacket = demuxer->getPacketNum();
\r
267 if (currentSeconds == 0) { unLock(); return; } // div by zero
\r
268 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
270 ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds);
\r
271 if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
\r
272 else switchState(S_JUMP, newPacket);
\r
276 void PlayerRadio::skipBackward(UINT seconds)
\r
279 logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
\r
281 ULONG currentSeconds = getCurrentSeconds();
\r
282 ULONG currentPacket = demuxer->getPacketNum();
\r
284 if (currentSeconds == 0) { unLock(); return; } // div by zero
\r
285 if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
288 if ((UINT)seconds > currentSeconds)
\r
291 newPacket = currentPacket - (currentPacket * seconds / currentSeconds);
\r
293 switchState(S_JUMP, newPacket);
\r
297 // ----------------------------------- Implementations called events
\r
299 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
\r
301 if (!initted) return;
\r
303 logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
\r
305 switch(state) // current state selector
\r
307 case S_PLAY: // from S_PLAY -----------------------------------
\r
311 case S_PLAY: // to S_PLAY
\r
315 case S_PAUSE_P: // to S_PAUSE_P
\r
321 case S_STOP: // to S_STOP
\r
331 case S_JUMP: // to S_JUMP
\r
333 restartAtPacket(jumpPacket);
\r
338 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
\r
342 case S_PLAY: // to S_PLAY
\r
348 case S_PAUSE_P: // to S_PAUSE_P
\r
352 case S_STOP: // to S_STOP
\r
359 audio->systemMuteOff();
\r
363 case S_JUMP: // to S_JUMP
\r
367 restartAtPacket(jumpPacket);
\r
372 case S_STOP: // from S_STOP -----------------------------------
\r
376 case S_PLAY: // to S_PLAY
\r
381 audio->setStreamType(Audio::MPEG2_PES);
\r
382 audio->systemMuteOff();
\r
385 // FIXME use restartAtPacket here?
\r
386 if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
\r
387 demuxer->setPacketNum(currentPacketNumber);
\r
390 logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
\r
396 case S_PAUSE_P: // to S_PAUSE_P
\r
400 case S_STOP: // to S_STOP
\r
404 case S_JUMP: // to S_JUMP
\r
410 // case S_JUMP cannot be selected as a start state because it auto flips to play
\r
414 // ----------------------------------- Internal functions
\r
416 void PlayerRadio::lock()
\r
419 pthread_mutex_lock(&mutex);
\r
420 logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
\r
423 WaitForSingleObject(mutex, INFINITE);
\r
427 void PlayerRadio::unLock()
\r
430 logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
\r
431 pthread_mutex_unlock(&mutex);
\r
433 ReleaseMutex(mutex);
\r
437 void PlayerRadio::restartAtPacket(ULONG newPacket)
\r
442 audio->setStreamType(Audio::MPEG2_PES);
\r
444 currentPacketNumber = newPacket;
\r
445 demuxer->setPacketNum(newPacket);
\r
449 audio->systemMuteOff();
\r
453 void PlayerRadio::doConnectionLost()
\r
455 logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
\r
456 Message* m = new Message();
\r
457 m->to = messageReceiver;
\r
459 m->message = Message::PLAYER_EVENT;
\r
460 m->parameter = PlayerRadio::CONNECTION_LOST;
\r
461 messageQueue->postMessage(m);
\r
464 // ----------------------------------- Callback
\r
466 void PlayerRadio::call(void* caller)
\r
468 threadSignalNoLock();
\r
471 // ----------------------------------- Feed thread
\r
473 void PlayerRadio::threadMethod()
\r
475 if (state == S_PLAY) threadFeedPlay();
\r
478 void PlayerRadio::threadFeedPlay()
\r
480 ULLONG feedPosition;
\r
481 UINT thisRead, writeLength, thisWrite, askFor;
\r
482 time_t lastRescan = time(NULL);
\r
484 feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
\r
485 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
486 logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
\r
497 // If we havn't rescanned for a while..
\r
498 if ((lastRescan + 60) < time(NULL))
\r
500 lengthBytes = vdr->rescanRecording(&lengthPackets);
\r
501 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
502 logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
\r
503 lastRescan = time(NULL);
\r
505 if (!setLengthSeconds())
\r
507 logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
\r
512 if (feedPosition >= lengthBytes) break; // finished playback
\r
516 if (startupBlockSize > lengthBytes)
\r
517 askFor = lengthBytes; // is a very small recording!
\r
519 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
\r
523 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
\r
524 askFor = lengthBytes - feedPosition;
\r
526 askFor = blockSize;
\r
529 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
\r
530 feedPosition += thisRead;
\r
532 if (!vdr->isConnected())
\r
534 doConnectionLost();
\r
538 if (!threadBuffer) break;
\r
542 int a_stream = demuxer->scan(threadBuffer, thisRead);
\r
543 demuxer->setAudioStream(a_stream);
\r
544 logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
\r
550 while(writeLength < thisRead)
\r
552 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
\r
553 writeLength += thisWrite;
\r
557 // demuxer is full and can't take anymore
\r
559 threadWaitForSignal();
\r
566 free(threadBuffer);
\r
567 threadBuffer = NULL;
\r
571 // end of recording
\r
572 logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
\r
577 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
578 m->to = messageReceiver;
\r
580 m->message = Message::PLAYER_EVENT;
\r
581 m->parameter = PlayerRadio::STOP_PLAYBACK;
\r
582 logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
\r
583 messageQueue->postMessage(m);
\r
586 void PlayerRadio::threadPostStopCleanup()
\r
590 free(threadBuffer);
\r
591 threadBuffer = NULL;
\r