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;
42 isRecording = tIsRecording;
50 startupBlockSize = 20000;
51 preBufferSize = 20000;
52 video->turnVideoOff();
57 startupBlockSize = 250000;
58 preBufferSize = 500000;
65 if (initted) shutdown();
70 if (initted) return 0;
72 pthread_mutex_init(&mutex, NULL);
74 mutex=CreateMutex(NULL,FALSE,NULL);
77 demuxer = new DemuxerVDR();
78 if (!demuxer) return 0;
80 if (!demuxer->init(this, audio, video))
82 logger->log("Player", Log::ERR, "Demuxer failed to init");
98 int Player::shutdown()
100 if (!initted) return 0;
114 void Player::setStartFrame(ULONG startFrame)
116 currentFrameNumber = startFrame;
119 void Player::setLengthBytes(ULLONG length)
121 lengthBytes = length;
122 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
125 void Player::setLengthFrames(ULONG length)
127 lengthFrames = length;
128 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
131 ULONG Player::getLengthFrames()
136 ULONG Player::getCurrentFrameNum()
138 if (startup) return 0;
143 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
147 return currentFrameNumber;
149 return 0; // shouldn't happen
153 // ----------------------------------- Externally called events
157 if (!initted) return;
158 if (state == S_PLAY) return;
160 bool doUnlock = false;
161 if (state == S_PAUSE_P) doUnlock = true;
163 if (doUnlock) unLock();
168 if (!initted) return;
169 if (state == S_STOP) return;
171 logger->log("Player", Log::DEBUG, "Stop called lock");
178 if (!initted) return;
181 if ((state == S_FFWD) || (state == S_FBWD))
183 switchState(S_PAUSE_I);
185 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
191 switchState(S_PAUSE_P);
197 void Player::fastForward()
199 if (!initted) return;
207 case 2: ifactor = 4; break;
208 case 4: ifactor = 6; break;
209 case 6: ifactor = 8; break;
210 case 8: ifactor = 2; break;
221 void Player::fastBackward()
223 if (!initted) return;
231 case 2: ifactor = 4; break;
232 case 4: ifactor = 6; break;
233 case 6: ifactor = 8; break;
234 case 8: ifactor = 2; break;
245 void Player::jumpToPercent(int percent)
248 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
249 ULONG newFrame = percent * lengthFrames / 100;
250 switchState(S_JUMP, newFrame);
251 // unLock(); - let thread unlock this
254 void Player::skipForward(int seconds)
257 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
258 ULONG newFrame = getCurrentFrameNum();
259 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
260 newFrame += seconds * video->getFPS();
261 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
262 else switchState(S_JUMP, newFrame);
263 // unLock(); - let thread unlock this
266 void Player::skipBackward(int seconds)
269 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
270 long newFrame = getCurrentFrameNum();
271 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
272 newFrame -= seconds * video->getFPS();
273 if (newFrame < 0) newFrame = 0;
274 switchState(S_JUMP, newFrame);
275 // unLock(); - let thread unlock this
278 // ----------------------------------- Implementations called events
280 void Player::switchState(UCHAR toState, ULONG jumpFrame)
282 if (!initted) return;
284 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
286 switch(state) // current state selector
288 case S_PLAY: // from S_PLAY -----------------------------------
292 case S_PLAY: // to S_PLAY
296 case S_PAUSE_P: // to S_PAUSE_P
303 case S_PAUSE_I: // to S_PAUSE_I
308 case S_FFWD: // to S_FFWD
310 currentFrameNumber = getCurrentFrameNum();
311 audio->systemMuteOn();
320 case S_FBWD: // to S_FBWD
322 currentFrameNumber = getCurrentFrameNum();
323 audio->systemMuteOn();
332 case S_STOP: // to S_STOP
346 case S_JUMP: // to S_JUMP
348 restartAtFrame(jumpFrame);
353 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
357 case S_PLAY: // to S_PLAY
364 case S_PAUSE_P: // to S_PAUSE_P
368 case S_PAUSE_I: // to S_PAUSE_I
372 case S_FFWD: // to S_FFWD
374 currentFrameNumber = getCurrentFrameNum();
375 audio->systemMuteOn();
385 case S_FBWD: // to S_FBWD
387 currentFrameNumber = getCurrentFrameNum();
388 audio->systemMuteOn();
398 case S_STOP: // to S_STOP
409 audio->systemMuteOff();
413 case S_JUMP: // to S_JUMP
417 restartAtFrame(jumpFrame);
422 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
426 case S_PLAY: // to S_PLAY
429 restartAtFrame(currentFrameNumber);
432 case S_PAUSE_P: // to S_PAUSE_P
436 case S_PAUSE_I: // to S_PAUSE_I
440 case S_FFWD: // to S_FFWD
446 case S_FBWD: // to S_FBWD
452 case S_STOP: // to S_STOP
459 audio->systemMuteOff();
463 case S_JUMP: // to S_JUMP
466 restartAtFrame(jumpFrame);
471 case S_FFWD: // from S_FFWD -----------------------------------
475 case S_PLAY: // to S_PLAY
478 restartAtFrame(currentFrameNumber);
481 case S_PAUSE_P: // to S_PAUSE_P
486 case S_PAUSE_I: // to S_PAUSE_I
492 case S_FFWD: // to S_FFWD
496 case S_FBWD: // to S_FBWD
503 case S_STOP: // to S_STOP
514 case S_JUMP: // to S_JUMP
517 restartAtFrame(jumpFrame);
522 case S_FBWD: // from S_FBWD -----------------------------------
526 case S_PLAY: // to S_PLAY
529 restartAtFrame(currentFrameNumber);
532 case S_PAUSE_P: // to S_PAUSE_P
537 case S_PAUSE_I: // to S_PAUSE_I
543 case S_FFWD: // to S_FFWD
550 case S_FBWD: // to S_FBWD
554 case S_STOP: // to S_STOP
565 case S_JUMP: // to S_JUMP
568 restartAtFrame(jumpFrame);
573 case S_STOP: // from S_STOP -----------------------------------
577 case S_PLAY: // to S_PLAY
582 audio->systemMuteOff();
587 // FIXME use restartAtFrame here?
588 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
589 demuxer->setFrameNum(currentFrameNumber);
591 if (!isRadio) demuxer->seek();
599 logger->log("Player", Log::DEBUG, "Immediate play");
607 else // do prebuffering
609 logger->log("Player", Log::DEBUG, "Prebuffering...");
614 case S_PAUSE_P: // to S_PAUSE_P
618 case S_PAUSE_I: // to S_PAUSE_I
622 case S_FFWD: // to S_FFWD
626 case S_FBWD: // to S_FBWD
630 case S_STOP: // to S_STOP
634 case S_JUMP: // to S_JUMP
640 // case S_JUMP cannot be selected as a start state because it auto flips to play
644 // ----------------------------------- Internal functions
649 pthread_mutex_lock(&mutex);
650 logger->log("Player", Log::DEBUG, "LOCKED");
653 WaitForSingleObject(mutex, INFINITE);
657 void Player::unLock()
660 logger->log("Player", Log::DEBUG, "UNLOCKING");
661 pthread_mutex_unlock(&mutex);
667 void Player::restartAtFrame(ULONG newFrame)
676 if (!isRadio) demuxer->seek();
677 currentFrameNumber = newFrame;
678 demuxer->setFrameNum(newFrame);
686 audio->systemMuteOff();
690 void Player::doConnectionLost()
692 Message* m = new Message();
693 m->message = Message::CONNECTION_LOST;
695 commandMessageQueue->postMessage(m);
698 // ----------------------------------- Callback
700 void Player::call(void* caller)
702 if (caller == demuxer)
704 logger->log("Player", Log::DEBUG, "Callback from demuxer");
706 if (video->getTVsize() == Video::ASPECT4X3)
708 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
712 int dxCurrentAspect = demuxer->getAspectRatio();
713 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
715 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
716 video->setAspectRatio(Video::ASPECT4X3);
718 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
720 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
721 video->setAspectRatio(Video::ASPECT16X9);
725 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
733 videoStartup = false;
741 threadSignalNoLock();
745 // ----------------------------------- Feed thread
747 void Player::threadMethod()
749 // this method used to be simple, the only thing it does
750 // is farm out to threadFeed Live/Play/Scan
751 // All the guff is to support scan hitting one end
755 if ((state == S_FFWD) || (state == S_FBWD))
758 // if this returns then scan hit one end
759 if (state == S_FFWD) // scan hit the end. stop
762 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
763 m->message = Message::STOP_PLAYBACK;
764 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
765 commandMessageQueue->postMessage(m);
766 logger->log("Player", Log::DEBUG, "Message posted...");
769 // if execution gets to here, threadFeedScan hit the start, go to play mode
773 if (!isRadio) demuxer->seek();
774 demuxer->setFrameNum(currentFrameNumber);
780 audio->systemMuteOff();
784 if (state == S_PLAY) threadFeedPlay();
792 void Player::threadFeedLive()
797 UINT preBufferTotal = 0;
809 askFor = startupBlockSize; // find audio streams sized block
811 askFor = blockSize; // normal
813 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
815 if (!vdr->isConnected())
821 if (!threadBuffer) break;
825 int a_stream = demuxer->scan(threadBuffer, thisRead);
826 demuxer->setAudioStream(a_stream);
827 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
833 preBufferTotal += thisRead;
834 if (preBufferTotal >= preBufferSize)
836 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
838 preBuffering = false;
847 // unLock(); // thread will be locked by play until here
848 // FIXME - see if this can segfault because it is starting threads out of the master mutex
854 while(writeLength < thisRead)
856 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
857 writeLength += thisWrite;
861 // demuxer is full and can't take anymore
863 threadWaitForSignal();
875 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
877 if (videoStartup) // oh woe. there never was a stream, I was conned!
879 videoStartup = false;
881 MILLISLEEP(500); // I think this will solve a race
886 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
887 m->message = Message::STREAM_END;
888 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
889 commandMessageQueue->postMessage(m);
890 logger->log("Player", Log::DEBUG, "Message posted...");
893 void Player::threadFeedPlay()
896 UINT thisRead, writeLength, thisWrite, askFor;
897 time_t lastRescan = time(NULL);
899 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
900 if (!vdr->isConnected()) { doConnectionLost(); return; }
901 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
912 // If we havn't rescanned for a while..
913 if ((lastRescan + 60) < time(NULL))
915 lengthBytes = vdr->rescanRecording(&lengthFrames);
916 if (!vdr->isConnected()) { doConnectionLost(); return; }
917 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
918 lastRescan = time(NULL);
921 if (feedPosition >= lengthBytes) break; // finished playback
925 if (startupBlockSize > lengthBytes)
926 askFor = lengthBytes; // is a very small recording!
928 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
932 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
933 askFor = lengthBytes - feedPosition;
938 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
939 feedPosition += thisRead;
941 if (!vdr->isConnected())
947 if (!threadBuffer) break;
951 int a_stream = demuxer->scan(threadBuffer, thisRead);
952 demuxer->setAudioStream(a_stream);
953 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
959 while(writeLength < thisRead)
961 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
962 writeLength += thisWrite;
966 // demuxer is full and can't take anymore
968 threadWaitForSignal();
981 logger->log("Player", Log::DEBUG, "Recording playback ends");
983 if (videoStartup) // oh woe. there never was a stream, I was conned!
985 videoStartup = false;
987 MILLISLEEP(500); // I think this will solve a race
992 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
993 m->message = Message::STOP_PLAYBACK; // recording
994 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
995 commandMessageQueue->postMessage(m);
996 logger->log("Player", Log::DEBUG, "Message posted...");
999 void Player::threadFeedScan()
1001 // This method is actually really simple - get frame from vdr,
1002 // spit it at the video chip, wait for a time. Most of the code here
1003 // is to get the wait right so that the scan occurs at the correct rate.
1005 ULONG direction = 0;
1006 ULONG iframeNumber = 0;
1007 ULONG iframeLength = 0;
1009 UINT amountReceived;
1015 struct timeval loopTime;
1016 struct timeval loopTimeN;
1023 ULONG sleepTime = 0;
1026 if (state == S_FFWD) direction = 1; // and 0 for backward
1029 gettimeofday(&loopTime, NULL);
1031 loopTime = timeGetTime();
1036 if (!vdr->getNextIFrame(currentFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) break;
1037 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1038 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1039 video->displayIFrame(threadBuffer, videoLength);
1040 iframesep = abs(iframeNumber - currentFrameNumber);
1041 currentFrameNumber = iframeNumber;
1043 threadBuffer = NULL;
1046 // Calculate next jump delay
1048 gettimeofday(&loopTimeN, NULL);
1049 realLoopTime = ((loopTimeN.tv_sec - loopTime.tv_sec) * 1000) + ((loopTimeN.tv_usec - loopTime.tv_usec) / 1000);
1050 loopTime.tv_sec = loopTimeN.tv_sec;
1051 loopTime.tv_usec = loopTimeN.tv_usec;
1053 loopTimeN = timeGetTime();
1054 realLoopTime = loopTimeN - loopTime;
1055 loopTime = loopTimeN;
1059 if (sleepTime) offset = realLoopTime - sleepTime;
1060 if (realLoopTime < sleepTime) offset = 0; // sanity check - loop was shorter than requested?
1062 sleepTime = (1000 * iframesep) / (video->getFPS() * ifactor);
1063 if (offset > sleepTime) sleepTime = 0;
1064 else sleepTime -= offset;
1066 logger->log("Player", Log::DEBUG, "iframesep %lu, fps %u, ifactor %u, offset %lu, sleep %lu", iframesep, video->getFPS(), ifactor, offset, sleepTime);
1067 MILLISLEEP(sleepTime);
1070 // scan has hit one end
1073 void Player::threadPostStopCleanup()
1078 threadBuffer = NULL;
1082 // ----------------------------------- Dev
1085 void Player::test1()
1087 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1089 // video->setAspectRatio(Video::ASPECT4X3);
1092 void Player::test2()
1094 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1095 video->setAspectRatio(Video::ASPECT16X9);