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)
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;
48 startupBlockSize = 250000;
49 preBufferSize = 500000;
55 if (initted) shutdown();
60 if (initted) return 0;
62 pthread_mutex_init(&mutex, NULL);
64 mutex=CreateMutex(NULL,FALSE,NULL);
67 demuxer = new DemuxerVDR();
68 if (!demuxer) return 0;
70 if (!demuxer->init(this, audio, video, 2097152, 524288))
72 logger->log("Player", Log::ERR, "Demuxer failed to init");
88 int Player::shutdown()
90 if (!initted) return 0;
104 void Player::setStartFrame(ULONG startFrame)
106 currentFrameNumber = startFrame;
109 void Player::setLengthBytes(ULLONG length)
111 lengthBytes = length;
112 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
115 void Player::setLengthFrames(ULONG length)
117 lengthFrames = length;
118 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
121 ULONG Player::getLengthFrames()
126 ULONG Player::getCurrentFrameNum()
128 if (startup) return 0;
133 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
137 return currentFrameNumber;
139 return 0; // shouldn't happen
143 bool* Player::getDemuxerAudioChannels()
145 return demuxer->getmpAudioChannels();
148 int Player::getCurrentAudioChannel()
150 return demuxer->getselAudioChannel();
153 void Player::setAudioChannel(int newChannel)
155 demuxer->setmpAudioChannel(newChannel);
158 // ----------------------------------- Externally called events
162 if (!initted) return;
163 if (state == S_PLAY) return;
165 bool doUnlock = false;
166 if (state == S_PAUSE_P) doUnlock = true;
168 if (doUnlock) unLock();
173 if (!initted) return;
174 if (state == S_STOP) return;
176 logger->log("Player", Log::DEBUG, "Stop called lock");
183 if (!initted) return;
186 if ((state == S_FFWD) || (state == S_FBWD))
188 switchState(S_PAUSE_I);
190 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
196 switchState(S_PAUSE_P);
202 void Player::fastForward()
204 if (!initted) return;
212 case 4: ifactor = 8; break;
213 case 8: ifactor = 16; break;
214 case 16: ifactor = 32; break;
215 case 32: ifactor = 4; break;
226 void Player::fastBackward()
228 if (!initted) return;
236 case 4: ifactor = 8; break;
237 case 8: ifactor = 16; break;
238 case 16: ifactor = 32; break;
239 case 32: ifactor = 4; break;
250 void Player::jumpToPercent(int percent)
253 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
254 ULONG newFrame = percent * lengthFrames / 100;
255 switchState(S_JUMP, newFrame);
256 // unLock(); - let thread unlock this
259 void Player::skipForward(int seconds)
262 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
263 ULONG newFrame = getCurrentFrameNum();
264 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
265 newFrame += seconds * video->getFPS();
266 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
267 else switchState(S_JUMP, newFrame);
268 // unLock(); - let thread unlock this
271 void Player::skipBackward(int seconds)
274 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
275 long newFrame = getCurrentFrameNum();
276 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
277 newFrame -= seconds * video->getFPS();
278 if (newFrame < 0) newFrame = 0;
279 switchState(S_JUMP, newFrame);
280 // unLock(); - let thread unlock this
283 // ----------------------------------- Implementations called events
285 void Player::switchState(UCHAR toState, ULONG jumpFrame)
287 if (!initted) return;
289 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
291 switch(state) // current state selector
293 case S_PLAY: // from S_PLAY -----------------------------------
297 case S_PLAY: // to S_PLAY
301 case S_PAUSE_P: // to S_PAUSE_P
308 case S_PAUSE_I: // to S_PAUSE_I
313 case S_FFWD: // to S_FFWD
315 currentFrameNumber = getCurrentFrameNum();
316 audio->systemMuteOn();
325 case S_FBWD: // to S_FBWD
327 currentFrameNumber = getCurrentFrameNum();
328 audio->systemMuteOn();
337 case S_STOP: // to S_STOP
351 case S_JUMP: // to S_JUMP
353 restartAtFrame(jumpFrame);
358 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
362 case S_PLAY: // to S_PLAY
369 case S_PAUSE_P: // to S_PAUSE_P
373 case S_PAUSE_I: // to S_PAUSE_I
377 case S_FFWD: // to S_FFWD
379 currentFrameNumber = getCurrentFrameNum();
380 audio->systemMuteOn();
390 case S_FBWD: // to S_FBWD
392 currentFrameNumber = getCurrentFrameNum();
393 audio->systemMuteOn();
403 case S_STOP: // to S_STOP
414 audio->systemMuteOff();
418 case S_JUMP: // to S_JUMP
422 restartAtFrame(jumpFrame);
427 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
431 case S_PLAY: // to S_PLAY
434 restartAtFrame(currentFrameNumber);
437 case S_PAUSE_P: // to S_PAUSE_P
441 case S_PAUSE_I: // to S_PAUSE_I
445 case S_FFWD: // to S_FFWD
451 case S_FBWD: // to S_FBWD
457 case S_STOP: // to S_STOP
464 audio->systemMuteOff();
468 case S_JUMP: // to S_JUMP
471 restartAtFrame(jumpFrame);
476 case S_FFWD: // from S_FFWD -----------------------------------
480 case S_PLAY: // to S_PLAY
483 restartAtFrame(currentFrameNumber);
486 case S_PAUSE_P: // to S_PAUSE_P
491 case S_PAUSE_I: // to S_PAUSE_I
497 case S_FFWD: // to S_FFWD
501 case S_FBWD: // to S_FBWD
508 case S_STOP: // to S_STOP
519 case S_JUMP: // to S_JUMP
522 restartAtFrame(jumpFrame);
527 case S_FBWD: // from S_FBWD -----------------------------------
531 case S_PLAY: // to S_PLAY
534 restartAtFrame(currentFrameNumber);
537 case S_PAUSE_P: // to S_PAUSE_P
542 case S_PAUSE_I: // to S_PAUSE_I
548 case S_FFWD: // to S_FFWD
555 case S_FBWD: // to S_FBWD
559 case S_STOP: // to S_STOP
570 case S_JUMP: // to S_JUMP
573 restartAtFrame(jumpFrame);
578 case S_STOP: // from S_STOP -----------------------------------
582 case S_PLAY: // to S_PLAY
587 audio->systemMuteOff();
592 // FIXME use restartAtFrame here?
593 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
594 demuxer->setFrameNum(currentFrameNumber);
604 logger->log("Player", Log::DEBUG, "Immediate play");
612 else // do prebuffering
614 logger->log("Player", Log::DEBUG, "Prebuffering...");
619 case S_PAUSE_P: // to S_PAUSE_P
623 case S_PAUSE_I: // to S_PAUSE_I
627 case S_FFWD: // to S_FFWD
631 case S_FBWD: // to S_FBWD
635 case S_STOP: // to S_STOP
639 case S_JUMP: // to S_JUMP
645 // case S_JUMP cannot be selected as a start state because it auto flips to play
649 // ----------------------------------- Internal functions
654 pthread_mutex_lock(&mutex);
655 logger->log("Player", Log::DEBUG, "LOCKED");
658 WaitForSingleObject(mutex, INFINITE);
662 void Player::unLock()
665 logger->log("Player", Log::DEBUG, "UNLOCKING");
666 pthread_mutex_unlock(&mutex);
672 void Player::restartAtFrame(ULONG newFrame)
682 currentFrameNumber = newFrame;
683 demuxer->setFrameNum(newFrame);
691 audio->systemMuteOff();
695 void Player::doConnectionLost()
697 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
698 Message* m = new Message();
699 m->to = messageReceiver;
701 m->message = Message::PLAYER_EVENT;
702 m->parameter = Player::CONNECTION_LOST;
703 messageQueue->postMessage(m);
706 // ----------------------------------- Callback
708 void Player::call(void* caller)
710 if (caller == demuxer)
712 logger->log("Player", Log::DEBUG, "Callback from demuxer");
714 if (video->getTVsize() == Video::ASPECT4X3)
716 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
720 int dxCurrentAspect = demuxer->getAspectRatio();
721 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
723 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
724 video->setAspectRatio(Video::ASPECT4X3);
726 Message* m = new Message();
728 m->to = messageReceiver;
729 m->message = Message::PLAYER_EVENT;
730 m->parameter = Player::ASPECT43;
731 messageQueue->postMessageFromOuterSpace(m);
733 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
735 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
736 video->setAspectRatio(Video::ASPECT16X9);
738 Message* m = new Message();
740 m->to = messageReceiver;
741 m->message = Message::PLAYER_EVENT;
742 m->parameter = Player::ASPECT169;
743 messageQueue->postMessageFromOuterSpace(m);
747 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
755 videoStartup = false;
763 threadSignalNoLock();
767 // ----------------------------------- Feed thread
769 void Player::threadMethod()
771 // this method used to be simple, the only thing it does
772 // is farm out to threadFeed Live/Play/Scan
773 // All the guff is to support scan hitting one end
777 if ((state == S_FFWD) || (state == S_FBWD))
780 // if this returns then scan hit one end
781 if (state == S_FFWD) // scan hit the end. stop
784 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
785 m->to = messageReceiver;
787 m->message = Message::PLAYER_EVENT;
788 m->parameter = STOP_PLAYBACK;
789 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
790 messageQueue->postMessage(m);
791 logger->log("Player", Log::DEBUG, "Message posted...");
794 // if execution gets to here, threadFeedScan hit the start, go to play mode
799 demuxer->setFrameNum(currentFrameNumber);
805 audio->systemMuteOff();
809 if (state == S_PLAY) threadFeedPlay();
817 void Player::threadFeedLive()
822 UINT preBufferTotal = 0;
834 askFor = startupBlockSize; // find audio streams sized block
836 askFor = blockSize; // normal
838 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
840 if (!vdr->isConnected())
846 if (!threadBuffer) break;
850 int a_stream = demuxer->scan(threadBuffer, thisRead);
851 demuxer->setAudioStream(a_stream);
852 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
858 preBufferTotal += thisRead;
859 if (preBufferTotal >= preBufferSize)
861 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
863 preBuffering = false;
867 // FIXME check the win32 code on the mvp
884 // unLock(); // thread will be locked by play until here
885 // FIXME - see if this can segfault because it is starting threads out of the master mutex
891 while(writeLength < thisRead)
893 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
894 writeLength += thisWrite;
898 // demuxer is full and can't take anymore
900 threadWaitForSignal();
912 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
914 if (videoStartup) // oh woe. there never was a stream, I was conned!
916 videoStartup = false;
918 MILLISLEEP(500); // I think this will solve a race
923 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
924 m->to = messageReceiver;
926 m->message = Message::PLAYER_EVENT;
927 m->parameter = Player::STREAM_END;
928 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
929 messageQueue->postMessage(m);
930 logger->log("Player", Log::DEBUG, "Message posted...");
933 void Player::threadFeedPlay()
936 UINT thisRead, writeLength, thisWrite, askFor;
937 time_t lastRescan = time(NULL);
939 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
940 if (!vdr->isConnected()) { doConnectionLost(); return; }
941 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
952 // If we havn't rescanned for a while..
953 if ((lastRescan + 60) < time(NULL))
955 lengthBytes = vdr->rescanRecording(&lengthFrames);
956 if (!vdr->isConnected()) { doConnectionLost(); return; }
957 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
958 lastRescan = time(NULL);
961 if (feedPosition >= lengthBytes) break; // finished playback
965 if (startupBlockSize > lengthBytes)
966 askFor = lengthBytes; // is a very small recording!
968 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
972 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
973 askFor = lengthBytes - feedPosition;
978 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
979 feedPosition += thisRead;
981 if (!vdr->isConnected())
987 if (!threadBuffer) break;
991 int a_stream = demuxer->scan(threadBuffer, thisRead);
992 demuxer->setAudioStream(a_stream);
993 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
999 while(writeLength < thisRead)
1001 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1002 writeLength += thisWrite;
1006 // demuxer is full and can't take anymore
1008 threadWaitForSignal();
1016 threadBuffer = NULL;
1021 logger->log("Player", Log::DEBUG, "Recording playback ends");
1023 if (videoStartup) // oh woe. there never was a stream, I was conned!
1025 videoStartup = false;
1027 MILLISLEEP(500); // I think this will solve a race
1033 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1034 m->to = messageReceiver;
1036 m->message = Message::PLAYER_EVENT;
1037 m->parameter = Player::STOP_PLAYBACK;
1038 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1039 messageQueue->postMessage(m);
1042 void Player::threadFeedScan()
1044 // This method is actually really simple - get frame from vdr,
1045 // spit it at the video chip, wait for a time. Most of the code here
1046 // is to get the wait right so that the scan occurs at the correct rate.
1048 ULONG direction = 0;
1049 ULONG baseFrameNumber = 0;
1050 ULONG iframeNumber = 0;
1051 ULONG iframeLength = 0;
1053 UINT amountReceived;
1057 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1058 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1059 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1061 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1064 int frameTimeOffset = 0; // Time in msec between frames
1065 int disp_msec = 0; // Time taken to display data
1066 int total_msec = 0; // Time taken to fetch data and display it
1069 if (state == S_FFWD) direction = 1; // and 0 for backward
1073 // Fetch I-frames until we get one that can be displayed in good time
1074 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1075 baseFrameNumber = currentFrameNumber;
1078 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1081 if (iframeNumber >= lengthFrames) return;
1082 // scan has got to the end of what we knew to be there before we started scanning
1084 baseFrameNumber = iframeNumber;
1085 frameTimeOffset = abs(iframeNumber - currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1087 gettimeofday(&clock0, NULL);
1089 clock0 = timeGetTime();
1093 while (clock2.tv_sec != 0 &&
1094 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1095 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1097 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1099 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1101 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1103 gettimeofday(&clock1, NULL);
1104 if (clock2.tv_sec != 0)
1105 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1106 + (clock2.tv_usec - clock1.tv_usec) / 1000
1107 + frameTimeOffset - disp_msec;
1109 clock1 = timeGetTime();
1111 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1113 if (sleepTime < 0) sleepTime = 0;
1115 MILLISLEEP(sleepTime);
1116 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1118 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1119 video->displayIFrame(threadBuffer, videoLength);
1120 currentFrameNumber = iframeNumber;
1122 threadBuffer = NULL;
1125 gettimeofday(&clock2, NULL);
1126 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1127 + (clock2.tv_usec - clock0.tv_usec) / 1000
1129 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1130 + (clock2.tv_usec - clock1.tv_usec) / 1000
1133 clock2 = timeGetTime();
1134 total_msec = clock2 - clock0 - sleepTime;
1135 disp_msec = clock2 - clock1 - sleepTime;
1137 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1141 void Player::threadPostStopCleanup()
1146 threadBuffer = NULL;
1150 // ----------------------------------- Dev
1153 void Player::test1()
1155 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1157 // video->setAspectRatio(Video::ASPECT4X3);
1160 void Player::test2()
1162 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1163 video->setAspectRatio(Video::ASPECT16X9);