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();
32 vdr = VDR::getInstance();
36 currentFrameNumber = 0;
41 isRecording = tIsRecording;
49 startupBlockSize = 60000;
50 video->turnVideoOff();
55 startupBlockSize = 250000;
62 if (initted) shutdown();
67 if (initted) return 0;
69 pthread_mutex_init(&mutex, NULL);
71 mutex=CreateMutex(NULL,FALSE,NULL);
74 demuxer = new DemuxerVDR();
75 if (!demuxer) return 0;
77 if (!demuxer->init(this, audio, video))
79 logger->log("Player", Log::ERR, "Demuxer failed to init");
95 int Player::shutdown()
97 if (!initted) return 0;
111 void Player::setStartFrame(ULONG startFrame)
113 currentFrameNumber = startFrame;
116 void Player::setLengthBytes(ULLONG length)
118 lengthBytes = length;
119 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
122 void Player::setLengthFrames(ULONG length)
124 lengthFrames = length;
125 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
128 ULONG Player::getLengthFrames()
133 ULONG Player::getCurrentFrameNum()
135 if (startup) return 0;
140 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
144 return currentFrameNumber;
146 return 0; // shouldn't happen
150 // ----------------------------------- Externally called events
154 if (!initted) return;
155 if (state == S_PLAY) return;
157 bool doUnlock = false;
158 if (state == S_PAUSE_P) doUnlock = true;
160 if (doUnlock) unLock();
165 if (!initted) return;
166 if (state == S_STOP) return;
168 logger->log("Player", Log::DEBUG, "Stop called lock");
173 void Player::togglePause()
175 if (!initted) return;
178 if ((state == S_FFWD) || (state == S_FBWD))
180 switchState(S_PAUSE_I);
182 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
188 switchState(S_PAUSE_P);
194 void Player::toggleFastForward()
196 if (!initted) return;
198 if (state == S_FFWD) switchState(S_PLAY);
199 else switchState(S_FFWD);
203 void Player::toggleFastBackward()
205 if (!initted) return;
207 if (state == S_FBWD) switchState(S_PLAY);
208 else switchState(S_FBWD);
212 void Player::jumpToPercent(int percent)
215 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
216 ULONG newFrame = percent * lengthFrames / 100;
217 switchState(S_JUMP, newFrame);
218 // unLock(); - let thread unlock this
221 void Player::skipForward(int seconds)
224 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
225 ULONG newFrame = getCurrentFrameNum();
226 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
227 newFrame += seconds * video->getFPS();
228 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
229 else switchState(S_JUMP, newFrame);
230 // unLock(); - let thread unlock this
233 void Player::skipBackward(int seconds)
236 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
237 long newFrame = getCurrentFrameNum();
238 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
239 newFrame -= seconds * video->getFPS();
240 if (newFrame < 0) newFrame = 0;
241 switchState(S_JUMP, newFrame);
242 // unLock(); - let thread unlock this
245 // ----------------------------------- Implementations called events
247 void Player::switchState(UCHAR toState, ULONG jumpFrame)
249 if (!initted) return;
251 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
253 switch(state) // current state selector
255 case S_PLAY: // from S_PLAY -----------------------------------
259 case S_PLAY: // to S_PLAY
263 case S_PAUSE_P: // to S_PAUSE_P
270 case S_PAUSE_I: // to S_PAUSE_I
275 case S_FFWD: // to S_FFWD
277 currentFrameNumber = getCurrentFrameNum();
278 audio->systemMuteOn();
287 case S_FBWD: // to S_FBWD
289 currentFrameNumber = getCurrentFrameNum();
290 audio->systemMuteOn();
299 case S_STOP: // to S_STOP
313 case S_JUMP: // to S_JUMP
315 restartAtFrame(jumpFrame);
320 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
324 case S_PLAY: // to S_PLAY
331 case S_PAUSE_P: // to S_PAUSE_P
335 case S_PAUSE_I: // to S_PAUSE_I
339 case S_FFWD: // to S_FFWD
341 currentFrameNumber = getCurrentFrameNum();
342 audio->systemMuteOn();
352 case S_FBWD: // to S_FBWD
354 currentFrameNumber = getCurrentFrameNum();
355 audio->systemMuteOn();
365 case S_STOP: // to S_STOP
376 audio->systemMuteOff();
380 case S_JUMP: // to S_JUMP
384 restartAtFrame(jumpFrame);
389 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
393 case S_PLAY: // to S_PLAY
396 restartAtFrame(currentFrameNumber);
399 case S_PAUSE_P: // to S_PAUSE_P
403 case S_PAUSE_I: // to S_PAUSE_I
407 case S_FFWD: // to S_FFWD
413 case S_FBWD: // to S_FBWD
419 case S_STOP: // to S_STOP
426 audio->systemMuteOff();
430 case S_JUMP: // to S_JUMP
433 restartAtFrame(jumpFrame);
438 case S_FFWD: // from S_FFWD -----------------------------------
442 case S_PLAY: // to S_PLAY
445 restartAtFrame(currentFrameNumber);
448 case S_PAUSE_P: // to S_PAUSE_P
453 case S_PAUSE_I: // to S_PAUSE_I
459 case S_FFWD: // to S_FFWD
463 case S_FBWD: // to S_FBWD
470 case S_STOP: // to S_STOP
481 case S_JUMP: // to S_JUMP
484 restartAtFrame(jumpFrame);
489 case S_FBWD: // from S_FBWD -----------------------------------
493 case S_PLAY: // to S_PLAY
496 restartAtFrame(currentFrameNumber);
499 case S_PAUSE_P: // to S_PAUSE_P
504 case S_PAUSE_I: // to S_PAUSE_I
510 case S_FFWD: // to S_FFWD
517 case S_FBWD: // to S_FBWD
521 case S_STOP: // to S_STOP
532 case S_JUMP: // to S_JUMP
535 restartAtFrame(jumpFrame);
540 case S_STOP: // from S_STOP -----------------------------------
544 case S_PLAY: // to S_PLAY
549 audio->systemMuteOff();
554 // FIXME use restartAtFrame here?
555 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
556 demuxer->setFrameNum(currentFrameNumber);
558 if (!isRadio) demuxer->seek();
566 logger->log("Player", Log::DEBUG, "Immediate play");
574 else // do prebuffering
576 logger->log("Player", Log::DEBUG, "Prebuffering...");
581 case S_PAUSE_P: // to S_PAUSE_P
585 case S_PAUSE_I: // to S_PAUSE_I
589 case S_FFWD: // to S_FFWD
593 case S_FBWD: // to S_FBWD
597 case S_STOP: // to S_STOP
601 case S_JUMP: // to S_JUMP
607 // case S_JUMP cannot be selected as a start state because it auto flips to play
611 // ----------------------------------- Internal functions
616 pthread_mutex_lock(&mutex);
617 logger->log("Player", Log::DEBUG, "LOCKED");
620 WaitForSingleObject(mutex, INFINITE);
624 void Player::unLock()
627 logger->log("Player", Log::DEBUG, "UNLOCKING");
628 pthread_mutex_unlock(&mutex);
634 void Player::restartAtFrame(ULONG newFrame)
643 if (!isRadio) demuxer->seek();
644 currentFrameNumber = newFrame;
645 demuxer->setFrameNum(newFrame);
653 audio->systemMuteOff();
657 void Player::doConnectionLost()
659 Message* m = new Message();
660 m->message = Message::CONNECTION_LOST;
662 commandMessageQueue->postMessage(m);
665 // ----------------------------------- Callback
667 void Player::call(void* caller)
669 if (caller == demuxer)
671 logger->log("Player", Log::DEBUG, "Callback from demuxer");
673 if (video->getTVsize() == Video::ASPECT4X3)
675 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
679 int dxCurrentAspect = demuxer->getAspectRatio();
680 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
682 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
683 video->setAspectRatio(Video::ASPECT4X3);
685 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
687 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
688 video->setAspectRatio(Video::ASPECT16X9);
692 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
700 videoStartup = false;
708 threadSignalNoLock();
712 // ----------------------------------- Feed thread
714 void Player::threadMethod()
716 // this method used to be simple, the only thing it does
717 // is farm out to threadFeed Live/Play/Scan
718 // All the guff is to support scan hitting one end
722 if ((state == S_FFWD) || (state == S_FBWD))
725 // if this returns then scan hit one end
726 if (state == S_FFWD) // scan hit the end. stop
729 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
730 m->message = Message::STOP_PLAYBACK;
731 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
732 commandMessageQueue->postMessage(m);
733 logger->log("Player", Log::DEBUG, "Message posted...");
736 // if execution gets to here, threadFeedScan hit the start, go to play mode
740 if (!isRadio) demuxer->seek();
741 demuxer->setFrameNum(currentFrameNumber);
747 audio->systemMuteOff();
751 if (state == S_PLAY) threadFeedPlay();
759 void Player::threadFeedLive()
764 UINT preBufferTotal = 0;
776 askFor = startupBlockSize; // find audio streams sized block
778 askFor = blockSize; // normal
780 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
782 if (!vdr->isConnected())
788 if (!threadBuffer) break;
792 int a_stream = demuxer->scan(threadBuffer, thisRead);
793 demuxer->setAudioStream(a_stream);
794 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
800 preBufferTotal += thisRead;
801 if (preBufferTotal > 500000)
803 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
805 preBuffering = false;
814 // unLock(); // thread will be locked by play until here
815 // FIXME - see if this can segfault because it is starting threads out of the master mutex
821 while(writeLength < thisRead)
823 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
824 writeLength += thisWrite;
828 // demuxer is full and can't take anymore
830 threadWaitForSignal();
842 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
844 if (videoStartup) // oh woe. there never was a stream, I was conned!
846 videoStartup = false;
848 MILLISLEEP(500); // I think this will solve a race
853 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
854 m->message = Message::STREAM_END;
855 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
856 commandMessageQueue->postMessage(m);
857 logger->log("Player", Log::DEBUG, "Message posted...");
860 void Player::threadFeedPlay()
863 UINT thisRead, writeLength, thisWrite, askFor;
864 time_t lastRescan = time(NULL);
866 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
867 if (!vdr->isConnected()) { doConnectionLost(); return; }
868 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
879 // If we havn't rescanned for a while..
880 if ((lastRescan + 60) < time(NULL))
882 lengthBytes = vdr->rescanRecording(&lengthFrames);
883 if (!vdr->isConnected()) { doConnectionLost(); return; }
884 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
885 lastRescan = time(NULL);
888 if (feedPosition >= lengthBytes) break; // finished playback
892 if (startupBlockSize > lengthBytes)
893 askFor = lengthBytes; // is a very small recording!
895 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
899 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
900 askFor = lengthBytes - feedPosition;
905 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
906 feedPosition += thisRead;
908 if (!vdr->isConnected())
914 if (!threadBuffer) break;
918 int a_stream = demuxer->scan(threadBuffer, thisRead);
919 demuxer->setAudioStream(a_stream);
920 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
926 while(writeLength < thisRead)
928 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
929 writeLength += thisWrite;
933 // demuxer is full and can't take anymore
935 threadWaitForSignal();
948 logger->log("Player", Log::DEBUG, "Recording playback ends");
950 if (videoStartup) // oh woe. there never was a stream, I was conned!
952 videoStartup = false;
954 MILLISLEEP(500); // I think this will solve a race
959 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
960 m->message = Message::STOP_PLAYBACK; // recording
961 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
962 commandMessageQueue->postMessage(m);
963 logger->log("Player", Log::DEBUG, "Message posted...");
967 void Player::threadFeedScan()
970 ULONG iframeNumber = 0;
971 ULONG iframeLength = 0;
976 struct timeval displayDelayA;
977 struct timeval displayDelayB;
983 if (state == S_FFWD) direction = 1; // and 0 for backward
987 gettimeofday(&displayDelayA, NULL);
989 if (!vdr->getNextIFrame(currentFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) break;
990 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
991 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
992 video->displayIFrame(threadBuffer, videoLength);
993 iframesep = abs(iframeNumber - currentFrameNumber);
994 currentFrameNumber = iframeNumber;
999 // Calculate next jump delay
1000 gettimeofday(&displayDelayB, NULL);
1001 displayDelay = ((displayDelayB.tv_sec - displayDelayA.tv_sec) * 1000) + ((displayDelayB.tv_usec - displayDelayA.tv_usec) / 1000);
1002 sleeptime = (1000 * iframesep) / (video->getFPS() * ifactor);
1003 if (displayDelay > sleeptime) sleeptime = 0;
1004 else sleeptime -= displayDelay;
1005 logger->log("Player", Log::DEBUG, "iframesep %lu, fps %u, ifactor %u, displayDelay %lu, sleep %lu", iframesep, video->getFPS(), ifactor, displayDelay, sleeptime);
1006 MILLISLEEP(sleeptime);
1008 // scan has hit one end
1012 void Player::threadFeedScan()
1014 ULONG direction = 0;
1015 ULONG iframeNumber = 0;
1016 ULONG iframeLength = 0;
1018 UINT amountReceived;
1024 struct timeval loopTime;
1025 struct timeval loopTimeN;
1032 ULONG sleepTime = 0;
1035 if (state == S_FFWD) direction = 1; // and 0 for backward
1038 gettimeofday(&loopTime, NULL);
1040 loopTime = timeGetTime();
1045 if (!vdr->getNextIFrame(currentFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) break;
1046 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1047 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1048 video->displayIFrame(threadBuffer, videoLength);
1049 iframesep = abs(iframeNumber - currentFrameNumber);
1050 currentFrameNumber = iframeNumber;
1052 threadBuffer = NULL;
1055 // Calculate next jump delay
1057 gettimeofday(&loopTimeN, NULL);
1058 realLoopTime = ((loopTimeN.tv_sec - loopTime.tv_sec) * 1000) + ((loopTimeN.tv_usec - loopTime.tv_usec) / 1000);
1059 loopTime.tv_sec = loopTimeN.tv_sec;
1060 loopTime.tv_usec = loopTimeN.tv_usec;
1062 loopTimeN = timeGetTime();
1063 realLoopTime = loopTimeN - loopTime;
1064 loopTime = loopTimeN;
1068 if (sleepTime) offset = realLoopTime - sleepTime;
1069 if (realLoopTime < sleepTime) offset = 0; // sanity check - loop was shorter than requested?
1071 sleepTime = (1000 * iframesep) / (video->getFPS() * ifactor);
1072 if (offset > sleepTime) sleepTime = 0;
1073 else sleepTime -= offset;
1075 logger->log("Player", Log::DEBUG, "iframesep %lu, fps %u, ifactor %u, offset %lu, sleep %lu", iframesep, video->getFPS(), ifactor, offset, sleepTime);
1076 MILLISLEEP(sleepTime);
1078 // scan has hit one end
1081 void Player::threadPostStopCleanup()
1086 threadBuffer = NULL;
1090 // ----------------------------------- Dev
1093 void Player::test1()
1095 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1097 // video->setAspectRatio(Video::ASPECT4X3);
1100 void Player::test2()
1102 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1103 video->setAspectRatio(Video::ASPECT16X9);