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();
40 feedMode = MODE_NORMAL;
47 isRecording = tIsRecording;
55 startupBlockSize = 60000;
56 video->turnVideoOff();
61 startupBlockSize = 250000;
68 if (initted) shutdown();
73 if (initted) return 0;
75 pthread_mutex_init(&mutex, NULL);
77 mutex=CreateMutex(NULL,FALSE,NULL);
80 demuxer = new DemuxerVDR();
81 if (!demuxer) return 0;
83 if (!demuxer->init(this, audio, video))
85 logger->log("Player", Log::ERR, "Demuxer failed to init");
101 int Player::shutdown()
103 if (!initted) return 0;
131 void Player::setPosition(ULLONG position)
133 feedPosition = position;
136 void Player::setLengthBytes(ULLONG length)
138 lastRescan = time(NULL);
139 lengthBytes = length;
140 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
143 void Player::setLengthFrames(ULONG length)
145 lengthFrames = length;
146 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
150 ULLONG Player::getEndTS() // FIXME delme - used only by bar clocks until current frame code is done
152 long long rendTS = endTS - startTS;
153 if (rendTS < 0) rendTS += 8589934592ULL;
154 return (ULLONG)rendTS;
159 hmsf Player::getEndHMSF()
161 return video->framesToHMSF(lengthFrames);
165 ULONG Player::getLengthFrames()
170 ULONG Player::getCurrentFrameNum()
172 if (startup) return 0;
173 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
176 // ----------------------------------- Externally called events
181 bool doUnlock = false;
182 int result = playInt(&doUnlock);
183 if (doUnlock) unLock();
194 void Player::togglePause()
201 void Player::toggleFastForward()
204 toggleFastForwardInt();
208 void Player::toggleFastBackward()
211 toggleFastBackwardInt();
215 void Player::jumpToPercent(int percent)
218 jumpToPercentInt(percent);
219 // unLock(); - let thread unlock this
222 void Player::skipForward(int seconds)
225 skipForwardInt(seconds);
226 // unLock(); - let thread unlock this
229 void Player::skipBackward(int seconds)
232 skipBackwardInt(seconds);
233 // unLock(); - let thread unlock this
236 // ----------------------------------- Implementations called events
239 int Player::playInt(bool* doUnlock)
241 if (!initted) return 0;
243 // If we are just paused, unpause!
251 // If we are fast forwarding, set to normal
254 toggleFastForwardInt();
258 // If we are fast backwarding, set to normal
261 toggleFastBackwardInt();
265 // If we are already playing, ignore. no resyncing to do now!
272 // Standard play start
273 logger->log("Player", Log::DEBUG, "Standard play start");
280 demuxer->setFrameNum(0);
281 if (!isRadio) demuxer->seek();
288 logger->log("Player", Log::DEBUG, "Immediate play");
296 else // do prebuffering
298 logger->log("Player", Log::DEBUG, "Prebuffering...");
306 void Player::stopInt()
308 if (!initted) return;
309 if (!playing) return;
316 video->unFastForward();
317 audio->systemMuteOff();
318 feedMode = MODE_NORMAL;
337 void Player::togglePauseInt()
339 if (!initted) return;
340 if (!playing) return;
342 if (ffwd) toggleFastForwardInt();
343 if (fbwd) toggleFastBackwardInt();
359 void Player::toggleFastForwardInt()
361 if (!initted) return;
362 if (!playing) return;
364 if (paused) togglePauseInt();
365 if (fbwd) toggleFastBackwardInt();
378 if (!isRadio) demuxer->seek();
388 audio->systemMuteOff();
398 audio->systemMuteOn();
399 video->fastForward();
403 void Player::toggleFastBackwardInt()
405 if (!initted) return;
406 if (!playing) return;
408 if (paused) togglePauseInt();
409 if (ffwd) toggleFastForwardInt();
415 audio->systemMuteOff();
418 feedMode = MODE_NORMAL;
427 audio->systemMuteOn();
430 feedMode = MODE_BACKWARDS;
434 if (!isRadio) demuxer->seek();
440 void Player::skipForwardInt(int seconds)
442 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
443 restartAtFrame(getCurrentFrameNum() + (seconds * video->getFPS()));
446 void Player::skipBackwardInt(int seconds)
448 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
449 long newFrameNum = getCurrentFrameNum() - (seconds * video->getFPS());
450 if (newFrameNum < 0) newFrameNum = 0;
451 restartAtFrame(newFrameNum);
454 void Player::jumpToPercentInt(int percent)
456 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
457 ULONG newFrame = percent * lengthFrames / 100;
458 restartAtFrame(newFrame);
462 // ----------------------------------- Internal functions
467 pthread_mutex_lock(&mutex);
468 logger->log("Player", Log::DEBUG, "LOCKED");
471 WaitForSingleObject(mutex, INFINITE );
475 void Player::unLock()
478 logger->log("Player", Log::DEBUG, "UNLOCKING");
479 pthread_mutex_unlock(&mutex);
485 void Player::restartAt(ULLONG timecode)
487 if (paused) togglePauseInt();
488 if (ffwd) toggleFastForwardInt();
490 ULONG wantedFrameNumber = video->timecodeToFrameNumber(timecode);
491 ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
492 if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
493 logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
502 if (!isRadio) demuxer->seek();
503 feedPosition = newPosition;
504 demuxer->setFrameNum(wantedFrameNumber);
512 audio->systemMuteOff();
517 void Player::restartAtFrame(ULONG newFrame)
519 if (paused) togglePauseInt();
520 if (ffwd) toggleFastForwardInt();
522 ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(newFrame);
523 if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
524 logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", newFrame, feedPosition, newPosition);
533 if (!isRadio) demuxer->seek();
534 feedPosition = newPosition;
535 demuxer->setFrameNum(newFrame);
543 audio->systemMuteOff();
549 void Player::setStartTS(UINT dataInBuffer)
552 if (isRecording && feedPosition) // (feedPosition != 0)
554 // FIXME find out how much data need to get to find a TS
555 // Need to get the actual start of the recording
558 UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
559 if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
560 if (!tempBuffer) return;
561 if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &startTS);
566 demuxer->findVideoPTS(threadBuffer, dataInBuffer, &startTS);
575 void Player::setEndTS()
577 logger->log("Player", Log::DEBUG, "Setting end TS");
580 UCHAR* tempBuffer = VDR::getInstance()->getBlock((lengthBytes - 100000), 100000, &thisRead);
581 if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
582 if (!tempBuffer) return;
583 if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &endTS);
585 #else //The replacement code relias completely on VDRs timecode and not the pts
586 endTS=video->frameNumberToTimecode(
587 VDR::getInstance()->frameNumberFromPosition((lengthBytes - 100000)));
589 logger->log("Player", Log::DEBUG, "Set end TS");
593 void Player::doConnectionLost()
595 Message* m = new Message();
596 m->message = Message::CONNECTION_LOST;
598 commandMessageQueue->postMessage(m);
601 // ----------------------------------- Callback
603 void Player::call(void* caller)
605 if (caller == demuxer)
607 logger->log("Player", Log::DEBUG, "Callback from demuxer");
609 if (video->getTVsize() == Video::ASPECT4X3)
611 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
615 int dxCurrentAspect = demuxer->getAspectRatio();
616 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
618 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
619 video->setAspectRatio(Video::ASPECT4X3);
621 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
623 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
624 video->setAspectRatio(Video::ASPECT16X9);
628 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
636 videoStartup = false;
644 threadSignalNoLock();
648 // ----------------------------------- Feed thread
650 void Player::threadMethod()
655 UINT preBufferTotal = 0;
657 ULLONG currentposition;
660 VDR* vdr = VDR::getInstance();
671 // If we havn't rescanned for a while..
672 if (isRecording && ((lastRescan + 60) < time(NULL)))
674 lengthBytes = vdr->rescanRecording(&lengthFrames);
675 if (!vdr->isConnected()) { doConnectionLost(); return; }
676 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
677 lastRescan = time(NULL);
681 if (lengthBytes) // is playing a recording
683 if (feedPosition >= lengthBytes) break; // finished playback
687 if (startupBlockSize > lengthBytes)
688 askFor = lengthBytes; // is a very small recording!
690 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
694 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
695 askFor = lengthBytes - feedPosition;
700 else // is playing live
703 askFor = startupBlockSize; // find audio streams sized block
705 askFor = blockSize; // normal
708 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
710 currentposition=feedPosition;
712 if (!vdr->isConnected())
718 if (!threadBuffer) break;
722 int a_stream = demuxer->scan(threadBuffer, thisRead);
723 demuxer->setAudioStream(a_stream);
724 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
726 // setStartTS(thisRead);
728 // if (isRecording) setEndTS();
735 preBufferTotal += thisRead;
736 if (preBufferTotal > 500000)
738 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
740 preBuffering = false;
749 // unLock(); // thread will be locked by play until here
750 // FIXME - see if this can segfault because it is starting threads out of the master mutex
754 if (feedMode == MODE_NORMAL)
756 feedPosition += thisRead;
758 else if (feedMode == MODE_BACKWARDS)
760 if (feedPosition >= blockSize)
762 feedPosition -= blockSize;
763 if (!isRadio) demuxer->seek();
767 // got to the start of the recording.. revert to play mode? how?
768 feedPosition += thisRead;
774 while(writeLength < thisRead)
777 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
779 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength,
780 currentposition+(ULLONG)writeLength);
782 writeLength += thisWrite;
783 // logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
787 // logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
788 // demuxer is full and can't take anymore
790 threadWaitForSignal();
792 // logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
804 logger->log("Player", Log::DEBUG, "Recording playback ends");
808 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
809 if (lengthBytes) m->message = Message::STOP_PLAYBACK; // recording
810 else m->message = Message::STREAM_END; // live
811 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
812 commandMessageQueue->postMessage(m);
813 logger->log("Player", Log::DEBUG, "Message posted...");
816 void Player::threadPostStopCleanup()
825 // ----------------------------------- Dev
830 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
832 // video->setAspectRatio(Video::ASPECT4X3);
837 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
838 video->setAspectRatio(Video::ASPECT16X9);