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* tmessageQueue, void* tmessageReceiver, bool tIsRecording, bool tIsRadio)
26 : vfeed(this), afeed(this)
28 messageQueue = tmessageQueue;
29 messageReceiver = tmessageReceiver;
30 audio = Audio::getInstance();
31 video = Video::getInstance();
32 logger = Log::getInstance();
33 vdr = VDR::getInstance();
37 currentFrameNumber = 0;
43 isRecording = tIsRecording;
51 startupBlockSize = 20000;
52 preBufferSize = 20000;
53 video->turnVideoOff();
58 startupBlockSize = 250000;
59 preBufferSize = 500000;
66 if (initted) shutdown();
71 if (initted) return 0;
73 pthread_mutex_init(&mutex, NULL);
75 mutex=CreateMutex(NULL,FALSE,NULL);
78 demuxer = new DemuxerVDR();
79 if (!demuxer) return 0;
81 if (!demuxer->init(this, audio, video))
83 logger->log("Player", Log::ERR, "Demuxer failed to init");
99 int Player::shutdown()
101 if (!initted) return 0;
115 void Player::setStartFrame(ULONG startFrame)
117 currentFrameNumber = startFrame;
120 void Player::setLengthBytes(ULLONG length)
122 lengthBytes = length;
123 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
126 void Player::setLengthFrames(ULONG length)
128 lengthFrames = length;
129 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
132 ULONG Player::getLengthFrames()
137 ULONG Player::getCurrentFrameNum()
139 if (startup) return 0;
144 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
148 return currentFrameNumber;
150 return 0; // shouldn't happen
154 // ----------------------------------- Externally called events
158 if (!initted) return;
159 if (state == S_PLAY) return;
161 bool doUnlock = false;
162 if (state == S_PAUSE_P) doUnlock = true;
164 if (doUnlock) unLock();
169 if (!initted) return;
170 if (state == S_STOP) return;
172 logger->log("Player", Log::DEBUG, "Stop called lock");
179 if (!initted) return;
182 if ((state == S_FFWD) || (state == S_FBWD))
184 switchState(S_PAUSE_I);
186 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
192 switchState(S_PAUSE_P);
198 void Player::fastForward()
200 if (!initted) return;
208 case 2: ifactor = 4; break;
209 case 4: ifactor = 8; break;
210 case 8: ifactor = 16; break;
211 case 16: ifactor = 64; break;
212 case 64: ifactor = 2; break;
223 void Player::fastBackward()
225 if (!initted) return;
233 case 2: ifactor = 4; break;
234 case 4: ifactor = 8; break;
235 case 8: ifactor = 16; break;
236 case 16: ifactor = 64; break;
237 case 64: ifactor = 2; break;
248 void Player::jumpToPercent(int percent)
251 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
252 ULONG newFrame = percent * lengthFrames / 100;
253 switchState(S_JUMP, newFrame);
254 // unLock(); - let thread unlock this
257 void Player::skipForward(int seconds)
260 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
261 ULONG newFrame = getCurrentFrameNum();
262 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
263 newFrame += seconds * video->getFPS();
264 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
265 else switchState(S_JUMP, newFrame);
266 // unLock(); - let thread unlock this
269 void Player::skipBackward(int seconds)
272 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
273 long newFrame = getCurrentFrameNum();
274 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
275 newFrame -= seconds * video->getFPS();
276 if (newFrame < 0) newFrame = 0;
277 switchState(S_JUMP, newFrame);
278 // unLock(); - let thread unlock this
281 // ----------------------------------- Implementations called events
283 void Player::switchState(UCHAR toState, ULONG jumpFrame)
285 if (!initted) return;
287 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
289 switch(state) // current state selector
291 case S_PLAY: // from S_PLAY -----------------------------------
295 case S_PLAY: // to S_PLAY
299 case S_PAUSE_P: // to S_PAUSE_P
306 case S_PAUSE_I: // to S_PAUSE_I
311 case S_FFWD: // to S_FFWD
313 currentFrameNumber = getCurrentFrameNum();
314 audio->systemMuteOn();
323 case S_FBWD: // to S_FBWD
325 currentFrameNumber = getCurrentFrameNum();
326 audio->systemMuteOn();
335 case S_STOP: // to S_STOP
349 case S_JUMP: // to S_JUMP
351 restartAtFrame(jumpFrame);
356 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
360 case S_PLAY: // to S_PLAY
367 case S_PAUSE_P: // to S_PAUSE_P
371 case S_PAUSE_I: // to S_PAUSE_I
375 case S_FFWD: // to S_FFWD
377 currentFrameNumber = getCurrentFrameNum();
378 audio->systemMuteOn();
388 case S_FBWD: // to S_FBWD
390 currentFrameNumber = getCurrentFrameNum();
391 audio->systemMuteOn();
401 case S_STOP: // to S_STOP
412 audio->systemMuteOff();
416 case S_JUMP: // to S_JUMP
420 restartAtFrame(jumpFrame);
425 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
429 case S_PLAY: // to S_PLAY
432 restartAtFrame(currentFrameNumber);
435 case S_PAUSE_P: // to S_PAUSE_P
439 case S_PAUSE_I: // to S_PAUSE_I
443 case S_FFWD: // to S_FFWD
449 case S_FBWD: // to S_FBWD
455 case S_STOP: // to S_STOP
462 audio->systemMuteOff();
466 case S_JUMP: // to S_JUMP
469 restartAtFrame(jumpFrame);
474 case S_FFWD: // from S_FFWD -----------------------------------
478 case S_PLAY: // to S_PLAY
481 restartAtFrame(currentFrameNumber);
484 case S_PAUSE_P: // to S_PAUSE_P
489 case S_PAUSE_I: // to S_PAUSE_I
495 case S_FFWD: // to S_FFWD
499 case S_FBWD: // to S_FBWD
506 case S_STOP: // to S_STOP
517 case S_JUMP: // to S_JUMP
520 restartAtFrame(jumpFrame);
525 case S_FBWD: // from S_FBWD -----------------------------------
529 case S_PLAY: // to S_PLAY
532 restartAtFrame(currentFrameNumber);
535 case S_PAUSE_P: // to S_PAUSE_P
540 case S_PAUSE_I: // to S_PAUSE_I
546 case S_FFWD: // to S_FFWD
553 case S_FBWD: // to S_FBWD
557 case S_STOP: // to S_STOP
568 case S_JUMP: // to S_JUMP
571 restartAtFrame(jumpFrame);
576 case S_STOP: // from S_STOP -----------------------------------
580 case S_PLAY: // to S_PLAY
585 audio->systemMuteOff();
590 // FIXME use restartAtFrame here?
591 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
592 demuxer->setFrameNum(currentFrameNumber);
594 if (!isRadio) demuxer->seek();
602 logger->log("Player", Log::DEBUG, "Immediate play");
610 else // do prebuffering
612 logger->log("Player", Log::DEBUG, "Prebuffering...");
617 case S_PAUSE_P: // to S_PAUSE_P
621 case S_PAUSE_I: // to S_PAUSE_I
625 case S_FFWD: // to S_FFWD
629 case S_FBWD: // to S_FBWD
633 case S_STOP: // to S_STOP
637 case S_JUMP: // to S_JUMP
643 // case S_JUMP cannot be selected as a start state because it auto flips to play
647 // ----------------------------------- Internal functions
652 pthread_mutex_lock(&mutex);
653 logger->log("Player", Log::DEBUG, "LOCKED");
656 WaitForSingleObject(mutex, INFINITE);
660 void Player::unLock()
663 logger->log("Player", Log::DEBUG, "UNLOCKING");
664 pthread_mutex_unlock(&mutex);
670 void Player::restartAtFrame(ULONG newFrame)
679 if (!isRadio) demuxer->seek();
680 currentFrameNumber = newFrame;
681 demuxer->setFrameNum(newFrame);
689 audio->systemMuteOff();
693 void Player::doConnectionLost()
695 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
696 Message* m = new Message();
697 m->to = messageReceiver;
699 m->message = Message::PLAYER_EVENT;
700 m->parameter = Player::CONNECTION_LOST;
701 messageQueue->postMessage(m);
704 // ----------------------------------- Callback
706 void Player::call(void* caller)
708 if (caller == demuxer)
710 logger->log("Player", Log::DEBUG, "Callback from demuxer");
712 if (video->getTVsize() == Video::ASPECT4X3)
714 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
718 int dxCurrentAspect = demuxer->getAspectRatio();
719 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
721 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
722 video->setAspectRatio(Video::ASPECT4X3);
724 Message* m = new Message();
726 m->to = messageReceiver;
727 m->message = Message::PLAYER_EVENT;
728 m->parameter = Player::ASPECT43;
729 messageQueue->postMessageFromOuterSpace(m);
731 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
733 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
734 video->setAspectRatio(Video::ASPECT16X9);
736 Message* m = new Message();
738 m->to = messageReceiver;
739 m->message = Message::PLAYER_EVENT;
740 m->parameter = Player::ASPECT169;
741 messageQueue->postMessageFromOuterSpace(m);
745 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
753 videoStartup = false;
761 threadSignalNoLock();
765 // ----------------------------------- Feed thread
767 void Player::threadMethod()
769 // this method used to be simple, the only thing it does
770 // is farm out to threadFeed Live/Play/Scan
771 // All the guff is to support scan hitting one end
775 if ((state == S_FFWD) || (state == S_FBWD))
778 // if this returns then scan hit one end
779 if (state == S_FFWD) // scan hit the end. stop
782 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
783 m->to = messageReceiver;
785 m->message = Message::PLAYER_EVENT;
786 m->parameter = STOP_PLAYBACK;
787 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
788 messageQueue->postMessage(m);
789 logger->log("Player", Log::DEBUG, "Message posted...");
792 // if execution gets to here, threadFeedScan hit the start, go to play mode
796 if (!isRadio) demuxer->seek();
797 demuxer->setFrameNum(currentFrameNumber);
803 audio->systemMuteOff();
807 if (state == S_PLAY) threadFeedPlay();
815 void Player::threadFeedLive()
820 UINT preBufferTotal = 0;
832 askFor = startupBlockSize; // find audio streams sized block
834 askFor = blockSize; // normal
836 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
838 if (!vdr->isConnected())
844 if (!threadBuffer) break;
848 int a_stream = demuxer->scan(threadBuffer, thisRead);
849 demuxer->setAudioStream(a_stream);
850 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
856 preBufferTotal += thisRead;
857 if (preBufferTotal >= preBufferSize)
859 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
861 preBuffering = false;
870 // unLock(); // thread will be locked by play until here
871 // FIXME - see if this can segfault because it is starting threads out of the master mutex
877 while(writeLength < thisRead)
879 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
880 writeLength += thisWrite;
884 // demuxer is full and can't take anymore
886 threadWaitForSignal();
898 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
900 if (videoStartup) // oh woe. there never was a stream, I was conned!
902 videoStartup = false;
904 MILLISLEEP(500); // I think this will solve a race
909 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
910 m->to = messageReceiver;
912 m->message = Message::PLAYER_EVENT;
913 m->parameter = Player::STREAM_END;
914 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
915 messageQueue->postMessage(m);
916 logger->log("Player", Log::DEBUG, "Message posted...");
919 void Player::threadFeedPlay()
922 UINT thisRead, writeLength, thisWrite, askFor;
923 time_t lastRescan = time(NULL);
925 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
926 if (!vdr->isConnected()) { doConnectionLost(); return; }
927 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
938 // If we havn't rescanned for a while..
939 if ((lastRescan + 60) < time(NULL))
941 lengthBytes = vdr->rescanRecording(&lengthFrames);
942 if (!vdr->isConnected()) { doConnectionLost(); return; }
943 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
944 lastRescan = time(NULL);
947 if (feedPosition >= lengthBytes) break; // finished playback
951 if (startupBlockSize > lengthBytes)
952 askFor = lengthBytes; // is a very small recording!
954 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
958 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
959 askFor = lengthBytes - feedPosition;
964 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
965 feedPosition += thisRead;
967 if (!vdr->isConnected())
973 if (!threadBuffer) break;
977 int a_stream = demuxer->scan(threadBuffer, thisRead);
978 demuxer->setAudioStream(a_stream);
979 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
985 while(writeLength < thisRead)
987 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
988 writeLength += thisWrite;
992 // demuxer is full and can't take anymore
994 threadWaitForSignal();
1002 threadBuffer = NULL;
1007 logger->log("Player", Log::DEBUG, "Recording playback ends");
1009 if (videoStartup) // oh woe. there never was a stream, I was conned!
1011 videoStartup = false;
1013 MILLISLEEP(500); // I think this will solve a race
1019 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1020 m->to = messageReceiver;
1022 m->message = Message::PLAYER_EVENT;
1023 m->parameter = Player::STOP_PLAYBACK;
1024 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1025 messageQueue->postMessage(m);
1028 void Player::threadFeedScan()
1030 // This method is actually really simple - get frame from vdr,
1031 // spit it at the video chip, wait for a time. Most of the code here
1032 // is to get the wait right so that the scan occurs at the correct rate.
1034 ULONG direction = 0;
1035 ULONG baseFrameNumber = 0;
1036 ULONG iframeNumber = 0;
1037 ULONG iframeLength = 0;
1039 UINT amountReceived;
1043 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1044 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1045 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1047 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1050 int frameTimeOffset = 0; // Time in msec between frames
1051 int disp_msec = 0; // Time taken to display data
1052 int total_msec = 0; // Time taken to fetch data and display it
1055 if (state == S_FFWD) direction = 1; // and 0 for backward
1059 // Fetch I-frames until we get one that can be displayed in good time
1060 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1061 baseFrameNumber = currentFrameNumber;
1064 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1067 baseFrameNumber = iframeNumber;
1068 frameTimeOffset = abs(iframeNumber - currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1070 gettimeofday(&clock0, NULL);
1072 clock0 = timeGetTime();
1076 while (clock2.tv_sec != 0 &&
1077 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1078 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1080 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1082 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1084 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1086 gettimeofday(&clock1, NULL);
1087 if (clock2.tv_sec != 0)
1088 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1089 + (clock2.tv_usec - clock1.tv_usec) / 1000
1090 + frameTimeOffset - disp_msec;
1092 clock1 = timeGetTime();
1094 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1096 if (sleepTime < 0) sleepTime = 0;
1098 MILLISLEEP(sleepTime);
1099 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1101 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1102 video->displayIFrame(threadBuffer, videoLength);
1103 currentFrameNumber = iframeNumber;
1105 threadBuffer = NULL;
1108 gettimeofday(&clock2, NULL);
1109 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1110 + (clock2.tv_usec - clock0.tv_usec) / 1000
1112 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1113 + (clock2.tv_usec - clock1.tv_usec) / 1000
1116 clock2 = timeGetTime();
1117 total_msec = clock2 - clock0 - sleepTime;
1118 disp_msec = clock2 - clock1 - sleepTime;
1120 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1124 void Player::threadPostStopCleanup()
1129 threadBuffer = NULL;
1133 // ----------------------------------- Dev
1136 void Player::test1()
1138 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1140 // video->setAspectRatio(Video::ASPECT4X3);
1143 void Player::test2()
1145 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1146 video->setAspectRatio(Video::ASPECT16X9);