2 Copyright 2004-2005 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
23 // ----------------------------------- Called from outside, one offs or info funcs
25 Player::Player(MessageQueue* messageQueue, bool tIsRecording, bool tIsRadio)
26 : vfeed(this), afeed(this)
28 commandMessageQueue = messageQueue;
29 audio = Audio::getInstance();
30 video = Video::getInstance();
31 logger = Log::getInstance();
39 feedMode = MODE_NORMAL;
46 isRecording = tIsRecording;
54 startupBlockSize = 60000;
55 video->turnVideoOff();
60 startupBlockSize = 250000;
67 if (initted) shutdown();
72 if (initted) return 0;
74 pthread_mutex_init(&mutex, NULL);
76 demuxer = new DemuxerVDR();
77 if (!demuxer) return 0;
79 if (!demuxer->init(this))
81 logger->log("Player", Log::ERR, "Demuxer failed to init");
86 vfeed.init(video->getFD());
87 afeed.init(audio->getFD());
101 int Player::shutdown()
103 if (!initted) return 0;
128 void Player::setPosition(ULLONG position)
130 feedPosition = position;
133 void Player::setLength(ULLONG length)
135 lastRescan = time(NULL);
136 streamLength = length;
137 logger->log("Player", Log::DEBUG, "Player has received length of %llu", streamLength);
140 ULLONG Player::getEndTS() // used internally (jump to percent)
142 long long rendTS = endTS - startTS;
143 if (rendTS < 0) rendTS += 8589934592ULL;
144 return (ULLONG)rendTS;
147 ULLONG Player::getPositionTS() // used internall (skip fw/bw)
149 if (startup) return 0ULL;
150 long long currentTS = video->getCurrentTimestamp() - startTS;
151 if (currentTS < 0) currentTS += 8589934592ULL;
152 return (ULLONG)currentTS;
155 // ----------------------------------- Externally called events
161 // unLock(); - let thread unlock this
171 void Player::togglePause()
178 void Player::toggleFastForward()
181 toggleFastForwardInt();
185 void Player::toggleFastBackward()
188 toggleFastBackwardInt();
192 void Player::jumpToPercent(int percent)
195 jumpToPercentInt(percent);
196 // unLock(); - let thread unlock this
199 void Player::skipForward(int seconds)
202 skipForwardInt(seconds);
203 // unLock(); - let thread unlock this
206 void Player::skipBackward(int seconds)
209 skipBackwardInt(seconds);
210 // unLock(); - let thread unlock this
213 // ----------------------------------- Implementations called events
216 int Player::playInt()
218 if (!initted) return 0;
220 // If we are just paused, unpause!
227 // If we are fast forwarding, set to normal
230 toggleFastForwardInt();
234 // If we are fast backwarding, set to normal
237 toggleFastBackwardInt();
241 // Standard play start
242 logger->log("Player", Log::DEBUG, "Standard play start");
249 if (!isRadio) demuxer->seek();
256 logger->log("Player", Log::DEBUG, "Immediate play");
264 else // do prebuffering
266 logger->log("Player", Log::DEBUG, "Prebuffering...");
274 void Player::stopInt()
276 if (!initted) return;
277 if (!playing) return;
284 video->unFastForward();
285 audio->systemMuteOff();
286 feedMode = MODE_NORMAL;
305 void Player::togglePauseInt()
307 if (!initted) return;
308 if (!playing) return;
310 if (ffwd) toggleFastForwardInt();
311 if (fbwd) toggleFastBackwardInt();
327 void Player::toggleFastForwardInt()
329 if (!initted) return;
330 if (!playing) return;
332 if (paused) togglePauseInt();
333 if (fbwd) toggleFastBackwardInt();
346 if (!isRadio) demuxer->seek();
356 audio->systemMuteOff();
364 audio->systemMuteOn();
365 video->fastForward();
369 void Player::toggleFastBackwardInt()
371 if (!initted) return;
372 if (!playing) return;
374 if (paused) togglePauseInt();
375 if (ffwd) toggleFastForwardInt();
381 audio->systemMuteOff();
384 feedMode = MODE_NORMAL;
391 audio->systemMuteOn();
394 feedMode = MODE_BACKWARDS;
398 if (!isRadio) demuxer->seek();
404 void Player::skipForwardInt(int seconds)
406 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
407 restartAt(getPositionTS() + (seconds * 90000));
410 void Player::skipBackwardInt(int seconds)
412 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
413 long long newTimeCode = getPositionTS() - (seconds * 90000);
414 if (newTimeCode < 0) newTimeCode = 0;
415 restartAt(newTimeCode);
418 void Player::jumpToPercentInt(int percent)
420 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
421 ULLONG newTimeCode = (ULLONG)(getEndTS() * ((float)percent / 100));
422 restartAt(newTimeCode);
426 // ----------------------------------- Internal functions
431 pthread_mutex_lock(&mutex);
437 void Player::unLock()
440 pthread_mutex_unlock(&mutex);
446 void Player::restartAt(ULLONG timecode)
448 if (paused) togglePauseInt();
449 if (ffwd) toggleFastForwardInt();
451 ULONG wantedFrameNumber = video->timecodeToFrameNumber(timecode);
452 ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
453 if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
454 logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
463 if (!isRadio) demuxer->seek();
464 feedPosition = newPosition;
472 audio->systemMuteOff();
477 void Player::setStartTS(UINT dataInBuffer)
479 if (isRecording && feedPosition) // (feedPosition != 0)
481 // FIXME find out how much data need to get to find a TS
482 // Need to get the actual start of the recording
485 UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
486 if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
487 if (!tempBuffer) return;
488 if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &startTS);
493 demuxer->findVideoPTS(threadBuffer, dataInBuffer, &startTS);
497 void Player::setEndTS()
499 logger->log("Player", Log::DEBUG, "Setting end TS");
502 UCHAR* tempBuffer = VDR::getInstance()->getBlock((streamLength - 100000), 100000, &thisRead);
503 if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
504 if (!tempBuffer) return;
505 if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &endTS);
507 logger->log("Player", Log::DEBUG, "Set end TS");
510 void Player::doConnectionLost()
512 Message* m = new Message();
513 m->message = Message::CONNECTION_LOST;
515 commandMessageQueue->postMessage(m);
518 // ----------------------------------- Callback
520 void Player::call(void* caller)
522 if (caller == demuxer)
524 logger->log("Player", Log::DEBUG, "Callback from demuxer");
526 if (video->getTVsize() == Video::ASPECT4X3)
528 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
532 int dxCurrentAspect = demuxer->getAspectRatio();
533 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
535 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
536 video->setAspectRatio(Video::ASPECT4X3);
538 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
540 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
541 video->setAspectRatio(Video::ASPECT16X9);
545 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
553 videoStartup = false;
561 threadSignalNoLock();
565 // ----------------------------------- Feed thread
567 void Player::threadMethod()
574 VDR* vdr = VDR::getInstance();
585 // If we havn't rescanned for a while..
586 if (isRecording && ((lastRescan + 60) < time(NULL)))
588 streamLength = vdr->rescanRecording();
589 if (!vdr->isConnected()) { doConnectionLost(); return; }
590 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", streamLength);
591 lastRescan = time(NULL);
595 if (streamLength) // is playing a recording
597 if (feedPosition >= streamLength) break; // finished playback
601 if (startupBlockSize > streamLength)
602 askFor = streamLength; // is a very small recording!
604 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
608 if ((feedPosition + blockSize) > streamLength) // last block of recording
609 askFor = streamLength - feedPosition;
614 else // is playing live
617 askFor = startupBlockSize; // find audio streams sized block
619 askFor = blockSize; // normal
622 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
623 if (!vdr->isConnected())
629 if (!threadBuffer) break;
633 int a_stream = demuxer->scan(threadBuffer, thisRead);
634 demuxer->setAudioStream(a_stream);
635 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
637 setStartTS(thisRead);
639 if (isRecording) setEndTS();
646 preBufferTotal += thisRead;
647 if (preBufferTotal > 500000)
649 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
651 preBuffering = false;
660 // unLock(); // thread will be locked by play until here
661 // FIXME - see if this can segfault because it is starting threads out of the master mutex
665 if (feedMode == MODE_NORMAL)
667 feedPosition += thisRead;
669 else if (feedMode == MODE_BACKWARDS)
671 if (feedPosition >= blockSize)
673 feedPosition -= blockSize;
674 if (!isRadio) demuxer->seek();
678 // got to the start of the recording.. revert to play mode? how?
679 feedPosition += thisRead;
685 while(writeLength < thisRead)
687 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
688 writeLength += thisWrite;
689 // logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
693 // logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
694 // demuxer is full and can't take anymore
696 threadWaitForSignal();
698 // logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
710 logger->log("Player", Log::DEBUG, "Recording playback ends");
714 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
715 if (streamLength) m->message = Message::STOP_PLAYBACK; // recording
716 else m->message = Message::STREAM_END; // live
717 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
718 commandMessageQueue->postMessage(m);
719 logger->log("Player", Log::DEBUG, "Message posted...");
722 void Player::threadPostStopCleanup()
731 // ----------------------------------- Dev
736 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
738 // video->setAspectRatio(Video::ASPECT4X3);
743 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
744 video->setAspectRatio(Video::ASPECT16X9);