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);
68 demuxer = new DemuxerVDR();
70 demuxer = new DemuxerTS();
71 if (!demuxer) return 0;
73 if (!demuxer->init(this, audio, video, 2097152, 524288))
75 logger->log("Player", Log::ERR, "Demuxer failed to init");
91 int Player::shutdown()
93 if (!initted) return 0;
107 void Player::setStartFrame(ULONG startFrame)
109 currentFrameNumber = startFrame;
112 void Player::setLengthBytes(ULLONG length)
114 lengthBytes = length;
115 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
118 void Player::setLengthFrames(ULONG length)
120 lengthFrames = length;
121 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
124 ULONG Player::getLengthFrames()
129 ULONG Player::getCurrentFrameNum()
131 if (startup) return 0;
136 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
140 return currentFrameNumber;
142 return 0; // shouldn't happen
146 bool* Player::getDemuxerAudioChannels()
148 return demuxer->getmpAudioChannels();
151 int Player::getCurrentAudioChannel()
153 return demuxer->getselAudioChannel();
156 void Player::setAudioChannel(int newChannel)
158 demuxer->setmpAudioChannel(newChannel);
161 // ----------------------------------- Externally called events
163 void Player::play(ULONG Vpid, ULONG Apid)
166 DemuxerTS* dts = (DemuxerTS*)demuxer;
174 if (!initted) return;
175 if (state == S_PLAY) return;
178 bool doUnlock = false;
179 if (state == S_PAUSE_P) doUnlock = true;
181 if (doUnlock) unLock();
186 if (!initted) return;
187 if (state == S_STOP) return;
189 logger->log("Player", Log::DEBUG, "Stop called lock");
196 if (!initted) return;
199 if ((state == S_FFWD) || (state == S_FBWD))
201 switchState(S_PAUSE_I);
203 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
209 switchState(S_PAUSE_P);
215 void Player::fastForward()
217 if (!initted) return;
225 case 4: ifactor = 8; break;
226 case 8: ifactor = 16; break;
227 case 16: ifactor = 32; break;
228 case 32: ifactor = 4; break;
239 void Player::fastBackward()
241 if (!initted) return;
249 case 4: ifactor = 8; break;
250 case 8: ifactor = 16; break;
251 case 16: ifactor = 32; break;
252 case 32: ifactor = 4; break;
263 void Player::jumpToPercent(int percent)
266 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
267 ULONG newFrame = percent * lengthFrames / 100;
268 switchState(S_JUMP, newFrame);
269 // unLock(); - let thread unlock this
272 void Player::jumpToMark(int mark)
275 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
276 switchState(S_JUMP, mark);
277 // unLock(); - let thread unlock this
280 void Player::skipForward(int seconds)
283 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
284 ULONG newFrame = getCurrentFrameNum();
285 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
286 newFrame += seconds * video->getFPS();
287 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
288 else switchState(S_JUMP, newFrame);
289 // unLock(); - let thread unlock this
292 void Player::skipBackward(int seconds)
295 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
296 long newFrame = getCurrentFrameNum();
297 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
298 newFrame -= seconds * video->getFPS();
299 if (newFrame < 0) newFrame = 0;
300 switchState(S_JUMP, newFrame);
301 // unLock(); - let thread unlock this
304 // ----------------------------------- Implementations called events
306 void Player::switchState(UCHAR toState, ULONG jumpFrame)
308 if (!initted) return;
310 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
312 switch(state) // current state selector
314 case S_PLAY: // from S_PLAY -----------------------------------
318 case S_PLAY: // to S_PLAY
322 case S_PAUSE_P: // to S_PAUSE_P
329 case S_PAUSE_I: // to S_PAUSE_I
334 case S_FFWD: // to S_FFWD
336 currentFrameNumber = getCurrentFrameNum();
337 audio->systemMuteOn();
346 case S_FBWD: // to S_FBWD
348 currentFrameNumber = getCurrentFrameNum();
349 audio->systemMuteOn();
358 case S_STOP: // to S_STOP
372 case S_JUMP: // to S_JUMP
374 restartAtFrame(jumpFrame);
379 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
383 case S_PLAY: // to S_PLAY
390 case S_PAUSE_P: // to S_PAUSE_P
394 case S_PAUSE_I: // to S_PAUSE_I
398 case S_FFWD: // to S_FFWD
400 currentFrameNumber = getCurrentFrameNum();
401 audio->systemMuteOn();
411 case S_FBWD: // to S_FBWD
413 currentFrameNumber = getCurrentFrameNum();
414 audio->systemMuteOn();
424 case S_STOP: // to S_STOP
435 audio->systemMuteOff();
439 case S_JUMP: // to S_JUMP
443 restartAtFrame(jumpFrame);
448 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
452 case S_PLAY: // to S_PLAY
455 restartAtFrame(currentFrameNumber);
458 case S_PAUSE_P: // to S_PAUSE_P
462 case S_PAUSE_I: // to S_PAUSE_I
466 case S_FFWD: // to S_FFWD
472 case S_FBWD: // to S_FBWD
478 case S_STOP: // to S_STOP
485 audio->systemMuteOff();
489 case S_JUMP: // to S_JUMP
492 restartAtFrame(jumpFrame);
497 case S_FFWD: // from S_FFWD -----------------------------------
501 case S_PLAY: // to S_PLAY
504 restartAtFrame(currentFrameNumber);
507 case S_PAUSE_P: // to S_PAUSE_P
512 case S_PAUSE_I: // to S_PAUSE_I
518 case S_FFWD: // to S_FFWD
522 case S_FBWD: // to S_FBWD
529 case S_STOP: // to S_STOP
540 case S_JUMP: // to S_JUMP
543 restartAtFrame(jumpFrame);
548 case S_FBWD: // from S_FBWD -----------------------------------
552 case S_PLAY: // to S_PLAY
555 restartAtFrame(currentFrameNumber);
558 case S_PAUSE_P: // to S_PAUSE_P
563 case S_PAUSE_I: // to S_PAUSE_I
569 case S_FFWD: // to S_FFWD
576 case S_FBWD: // to S_FBWD
580 case S_STOP: // to S_STOP
591 case S_JUMP: // to S_JUMP
594 restartAtFrame(jumpFrame);
599 case S_STOP: // from S_STOP -----------------------------------
603 case S_PLAY: // to S_PLAY
608 audio->systemMuteOff();
613 // FIXME use restartAtFrame here?
614 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
615 demuxer->setFrameNum(currentFrameNumber);
625 logger->log("Player", Log::DEBUG, "Immediate play");
633 else // do prebuffering
635 logger->log("Player", Log::DEBUG, "Prebuffering...");
640 case S_PAUSE_P: // to S_PAUSE_P
644 case S_PAUSE_I: // to S_PAUSE_I
648 case S_FFWD: // to S_FFWD
652 case S_FBWD: // to S_FBWD
656 case S_STOP: // to S_STOP
660 case S_JUMP: // to S_JUMP
666 // case S_JUMP cannot be selected as a start state because it auto flips to play
670 // ----------------------------------- Internal functions
675 pthread_mutex_lock(&mutex);
676 logger->log("Player", Log::DEBUG, "LOCKED");
679 WaitForSingleObject(mutex, INFINITE);
683 void Player::unLock()
686 logger->log("Player", Log::DEBUG, "UNLOCKING");
687 pthread_mutex_unlock(&mutex);
693 void Player::restartAtFrame(ULONG newFrame)
703 currentFrameNumber = newFrame;
704 demuxer->setFrameNum(newFrame);
712 audio->systemMuteOff();
716 void Player::doConnectionLost()
718 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
719 Message* m = new Message();
720 m->to = messageReceiver;
722 m->message = Message::PLAYER_EVENT;
723 m->parameter = Player::CONNECTION_LOST;
724 messageQueue->postMessage(m);
727 // ----------------------------------- Callback
729 void Player::call(void* caller)
731 if (caller == demuxer)
733 logger->log("Player", Log::DEBUG, "Callback from demuxer");
735 if (video->getTVsize() == Video::ASPECT4X3)
737 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
741 int dxCurrentAspect = demuxer->getAspectRatio();
742 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
744 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
745 video->setAspectRatio(Video::ASPECT4X3);
747 Message* m = new Message();
749 m->to = messageReceiver;
750 m->message = Message::PLAYER_EVENT;
751 m->parameter = Player::ASPECT43;
752 messageQueue->postMessageFromOuterSpace(m);
754 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
756 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
757 video->setAspectRatio(Video::ASPECT16X9);
759 Message* m = new Message();
761 m->to = messageReceiver;
762 m->message = Message::PLAYER_EVENT;
763 m->parameter = Player::ASPECT169;
764 messageQueue->postMessageFromOuterSpace(m);
768 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
776 videoStartup = false;
784 threadSignalNoLock();
788 // ----------------------------------- Feed thread
790 void Player::threadMethod()
792 // this method used to be simple, the only thing it does
793 // is farm out to threadFeed Live/Play/Scan
794 // All the guff is to support scan hitting one end
798 if ((state == S_FFWD) || (state == S_FBWD))
801 // if this returns then scan hit one end
802 if (state == S_FFWD) // scan hit the end. stop
805 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
806 m->to = messageReceiver;
808 m->message = Message::PLAYER_EVENT;
809 m->parameter = STOP_PLAYBACK;
810 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
811 messageQueue->postMessage(m);
812 logger->log("Player", Log::DEBUG, "Message posted...");
815 // if execution gets to here, threadFeedScan hit the start, go to play mode
820 demuxer->setFrameNum(currentFrameNumber);
826 audio->systemMuteOff();
830 if (state == S_PLAY) threadFeedPlay();
838 void Player::threadFeedLive()
843 UINT preBufferTotal = 0;
855 askFor = startupBlockSize; // find audio streams sized block
857 askFor = blockSize; // normal
859 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
861 if (!vdr->isConnected())
867 if (!threadBuffer) break;
871 int a_stream = demuxer->scan(threadBuffer, thisRead);
872 demuxer->setAudioStream(a_stream);
873 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
879 preBufferTotal += thisRead;
880 if (preBufferTotal >= preBufferSize)
882 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
884 preBuffering = false;
888 // FIXME check the win32 code on the mvp
905 // unLock(); // thread will be locked by play until here
906 // FIXME - see if this can segfault because it is starting threads out of the master mutex
912 while(writeLength < thisRead)
914 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
915 writeLength += thisWrite;
917 // logger->log("Player", Log::DEBUG, "Player put %u to demuxer", thisWrite);
921 // logger->log("Player", Log::DEBUG, "Demuxer full");
922 // demuxer is full and can't take anymore
924 threadWaitForSignal();
936 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
938 if (videoStartup) // oh woe. there never was a stream, I was conned!
940 videoStartup = false;
942 MILLISLEEP(500); // I think this will solve a race
947 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
948 m->to = messageReceiver;
950 m->message = Message::PLAYER_EVENT;
951 m->parameter = Player::STREAM_END;
952 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
953 messageQueue->postMessage(m);
954 logger->log("Player", Log::DEBUG, "Message posted...");
957 void Player::threadFeedPlay()
960 UINT thisRead, writeLength, thisWrite, askFor;
961 time_t lastRescan = time(NULL);
963 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
964 if (!vdr->isConnected()) { doConnectionLost(); return; }
965 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
976 // If we havn't rescanned for a while..
977 if ((lastRescan + 60) < time(NULL))
979 lengthBytes = vdr->rescanRecording(&lengthFrames);
980 if (!vdr->isConnected()) { doConnectionLost(); return; }
981 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
982 lastRescan = time(NULL);
985 if (feedPosition >= lengthBytes) break; // finished playback
989 if (startupBlockSize > lengthBytes)
990 askFor = lengthBytes; // is a very small recording!
992 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
996 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
997 askFor = lengthBytes - feedPosition;
1002 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1003 feedPosition += thisRead;
1005 if (!vdr->isConnected())
1011 if (!threadBuffer) break;
1015 int a_stream = demuxer->scan(threadBuffer, thisRead);
1016 demuxer->setAudioStream(a_stream);
1017 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1023 while(writeLength < thisRead)
1025 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1026 writeLength += thisWrite;
1030 // demuxer is full and can't take anymore
1032 threadWaitForSignal();
1040 threadBuffer = NULL;
1045 logger->log("Player", Log::DEBUG, "Recording playback ends");
1047 if (videoStartup) // oh woe. there never was a stream, I was conned!
1049 videoStartup = false;
1051 MILLISLEEP(500); // I think this will solve a race
1057 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1058 m->to = messageReceiver;
1060 m->message = Message::PLAYER_EVENT;
1061 m->parameter = Player::STOP_PLAYBACK;
1062 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1063 messageQueue->postMessage(m);
1066 void Player::threadFeedScan()
1068 // This method is actually really simple - get frame from vdr,
1069 // spit it at the video chip, wait for a time. Most of the code here
1070 // is to get the wait right so that the scan occurs at the correct rate.
1072 ULONG direction = 0;
1073 ULONG baseFrameNumber = 0;
1074 ULONG iframeNumber = 0;
1075 ULONG iframeLength = 0;
1077 UINT amountReceived;
1081 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1082 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1083 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1085 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1088 int frameTimeOffset = 0; // Time in msec between frames
1089 int disp_msec = 0; // Time taken to display data
1090 int total_msec = 0; // Time taken to fetch data and display it
1093 if (state == S_FFWD) direction = 1; // and 0 for backward
1097 // Fetch I-frames until we get one that can be displayed in good time
1098 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1099 baseFrameNumber = currentFrameNumber;
1102 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1105 if (iframeNumber >= lengthFrames) return;
1106 // scan has got to the end of what we knew to be there before we started scanning
1108 baseFrameNumber = iframeNumber;
1109 frameTimeOffset = abs(iframeNumber - currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1111 gettimeofday(&clock0, NULL);
1113 clock0 = timeGetTime();
1117 while (clock2.tv_sec != 0 &&
1118 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1119 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1121 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1123 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1125 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1127 gettimeofday(&clock1, NULL);
1128 if (clock2.tv_sec != 0)
1129 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1130 + (clock2.tv_usec - clock1.tv_usec) / 1000
1131 + frameTimeOffset - disp_msec;
1133 clock1 = timeGetTime();
1135 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1137 if (sleepTime < 0) sleepTime = 0;
1139 MILLISLEEP(sleepTime);
1140 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1142 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1143 video->displayIFrame(threadBuffer, videoLength);
1144 currentFrameNumber = iframeNumber;
1146 threadBuffer = NULL;
1149 gettimeofday(&clock2, NULL);
1150 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1151 + (clock2.tv_usec - clock0.tv_usec) / 1000
1153 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1154 + (clock2.tv_usec - clock1.tv_usec) / 1000
1157 clock2 = timeGetTime();
1158 total_msec = clock2 - clock0 - sleepTime;
1159 disp_msec = clock2 - clock1 - sleepTime;
1161 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1165 void Player::threadPostStopCleanup()
1170 threadBuffer = NULL;
1174 // ----------------------------------- Dev
1177 void Player::test1()
1179 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1181 // video->setAspectRatio(Video::ASPECT4X3);
1184 void Player::test2()
1186 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1187 video->setAspectRatio(Video::ASPECT16X9);