2 Copyright 2004-2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "demuxervdr.h"
27 #include "demuxerts.h"
29 #include "messagequeue.h"
32 #include "dvbsubtitles.h"
33 #include "osdreceiver.h"
35 #define USER_RESPONSE_TIME 500 // Milliseconds
37 // ----------------------------------- Called from outside, one offs or info funcs
39 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
40 : vfeed(this), afeed(this), tfeed(this)
42 messageQueue = tmessageQueue;
43 messageReceiver = tmessageReceiver;
44 osdReceiver = tosdReceiver;
45 audio = Audio::getInstance();
46 video = Video::getInstance();
47 logger = Log::getInstance();
48 vdr = VDR::getInstance();
52 currentFrameNumber = 0;
57 subtitlesShowing = false;
63 startupBlockSize = 250000;
69 if (initted) shutdown();
72 int Player::init(bool p_isPesRecording,double framespersecond)
74 if (initted) return 0;
76 pthread_mutex_init(&mutex, NULL);
78 mutex=CreateMutex(NULL,FALSE,NULL);
80 is_pesrecording = p_isPesRecording;
83 demuxer = new DemuxerVDR();
85 demuxer = new DemuxerTS();
86 if (!demuxer) return 0;
87 subtitles = new DVBSubtitles(osdReceiver);
88 if (!subtitles) return 0;
90 teletext = new TeletextDecoderVBIEBU();
91 if (!teletext) return 0;
92 teletext->setRecordigMode(true);
93 unsigned int demux_video_size=2097152;
94 unsigned int demux_audio_size=524288;
95 if (video->supportsh264()) {
96 demux_video_size*=5*2;//5;
99 if (audio->maysupportAc3()) {
100 //demux_audio_size*=2;
103 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
105 logger->log("Player", Log::ERR, "Demuxer failed to init");
122 int Player::shutdown()
124 if (!initted) return 0;
142 void Player::setStartFrame(ULONG startFrame)
144 currentFrameNumber = startFrame;
147 void Player::setLengthBytes(ULLONG length)
149 lengthBytes = length;
150 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
153 void Player::setLengthFrames(ULONG length)
155 lengthFrames = length;
156 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
159 ULONG Player::getLengthFrames()
164 ULONG Player::getCurrentFrameNum()
166 if (startup) return 0;
171 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
175 return currentFrameNumber;
177 return 0; // shouldn't happen
181 bool* Player::getDemuxerMpegAudioChannels()
183 return demuxer->getmpAudioChannels();
186 bool* Player::getDemuxerAc3AudioChannels()
188 return demuxer->getac3AudioChannels();
191 bool* Player::getDemuxerSubtitleChannels()
193 return demuxer->getSubtitleChannels();
196 int Player::getCurrentAudioChannel()
198 if (is_pesrecording) {
199 return demuxer->getselAudioChannel();
201 return ((DemuxerTS*)demuxer)->getAID();
205 int Player::getCurrentSubtitleChannel()
207 if (is_pesrecording) {
208 return demuxer->getselSubtitleChannel();
210 return ((DemuxerTS*)demuxer)->getSubID();
214 void Player::setSubtitleChannel(int newChannel)
216 if (is_pesrecording) {
217 demuxer->setDVBSubtitleStream(newChannel);
219 ((DemuxerTS*)demuxer)->setSubID(newChannel);
223 int *Player::getTeletxtSubtitlePages()
225 return teletext->getSubtitlePages();
228 void Player::setAudioChannel(int newChannel, int type, int streamtype)
230 if (is_pesrecording) {
231 demuxer->setAudioStream(newChannel);
234 ((DemuxerTS*)demuxer)->setAID(newChannel,type,streamtype,false);
239 bool Player::toggleSubtitles()
241 if (!subtitlesShowing)
243 subtitlesShowing = true;
248 subtitlesShowing = false;
251 return subtitlesShowing;
254 void Player::turnSubtitlesOn(bool ison) {
257 subtitlesShowing = true;
262 subtitlesShowing = false;
268 void Player::tellSubtitlesOSDVisible(bool visible){
269 subtitles->setOSDMenuVisibility(visible);
272 Channel * Player::getDemuxerChannel() {
273 if (!is_pesrecording) {
274 return ((DemuxerTS*) demuxer)->getChannelInfo();
276 return NULL; //Should not happen!
280 // ----------------------------------- Externally called events
284 if (!initted) return;
285 if (state == S_PLAY) return;
288 bool doUnlock = false;
289 if (state == S_PAUSE_P) doUnlock = true;
291 if (doUnlock) unLock();
294 void Player::playpause()
296 if (!initted) return;
299 bool doUnlock = false;
302 switchState(S_PAUSE_P);
304 if (state == S_PAUSE_P) doUnlock = true;
307 if (doUnlock) unLock();
316 if (!initted) return;
317 if (state == S_STOP) return;
319 logger->log("Player", Log::DEBUG, "Stop called lock");
326 if (!initted) return;
329 if ((state == S_FFWD) || (state == S_FBWD))
331 switchState(S_PAUSE_I);
333 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
339 switchState(S_PAUSE_P);
345 void Player::fastForward()
347 if (!initted) return;
355 case 4: ifactor = 8; break;
356 case 8: ifactor = 16; break;
357 case 16: ifactor = 32; break;
358 case 32: ifactor = 4; break;
369 void Player::fastBackward()
371 if (!initted) return;
379 case 4: ifactor = 8; break;
380 case 8: ifactor = 16; break;
381 case 16: ifactor = 32; break;
382 case 32: ifactor = 4; break;
393 void Player::jumpToPercent(double percent)
396 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
397 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
398 switchState(S_JUMP, newFrame);
399 // unLock(); - let thread unlock this
402 void Player::jumpToMark(int mark)
405 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
406 switchState(S_JUMP, mark);
407 // unLock(); - let thread unlock this
410 void Player::jumpToFrameP(int newFrame)
413 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
414 switchState(S_JUMP_PI, newFrame);
418 void Player::skipForward(int seconds)
421 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
422 ULONG newFrame = getCurrentFrameNum();
423 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
424 newFrame +=(ULONG) (((double)seconds) * fps);
425 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
426 else switchState(S_JUMP, newFrame);
427 // unLock(); - let thread unlock this
430 void Player::skipBackward(int seconds)
433 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
434 long newFrame = getCurrentFrameNum();
435 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
436 newFrame -= (ULONG) (((double)seconds) * fps);
437 if (newFrame < 0) newFrame = 0;
438 switchState(S_JUMP, newFrame);
439 // unLock(); - let thread unlock this
442 // ----------------------------------- Implementations called events
444 void Player::switchState(UCHAR toState, ULONG jumpFrame)
446 if (!initted) return;
448 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
450 switch(state) // current state selector
452 case S_PLAY: // from S_PLAY -----------------------------------
456 case S_PLAY: // to S_PLAY
460 case S_PAUSE_P: // to S_PAUSE_P
467 case S_PAUSE_I: // to S_PAUSE_I
472 case S_FFWD: // to S_FFWD
474 currentFrameNumber = getCurrentFrameNum();
475 audio->systemMuteOn();
486 case S_FBWD: // to S_FBWD
488 currentFrameNumber = getCurrentFrameNum();
489 audio->systemMuteOn();
500 case S_STOP: // to S_STOP
516 case S_JUMP: // to S_JUMP
518 restartAtFrame(jumpFrame);
521 case S_JUMP_PI: // to S_JUMP_PI
523 audio->systemMuteOn();
533 restartAtFramePI(jumpFrame);
538 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
542 case S_PLAY: // to S_PLAY
549 case S_PAUSE_P: // to S_PAUSE_P
553 case S_PAUSE_I: // to S_PAUSE_I
557 case S_FFWD: // to S_FFWD
559 currentFrameNumber = getCurrentFrameNum();
560 audio->systemMuteOn();
572 case S_FBWD: // to S_FBWD
574 currentFrameNumber = getCurrentFrameNum();
575 audio->systemMuteOn();
587 case S_STOP: // to S_STOP
600 audio->systemMuteOff();
604 case S_JUMP: // to S_JUMP
607 audio->systemMuteOn();
609 restartAtFrame(jumpFrame);
612 case S_JUMP_PI: // to S_JUMP_PI
614 audio->systemMuteOn();
625 restartAtFramePI(jumpFrame);
630 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
634 case S_PLAY: // to S_PLAY
637 restartAtFrame(currentFrameNumber);
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
654 case S_FBWD: // to S_FBWD
660 case S_STOP: // to S_STOP
667 audio->systemMuteOff();
671 case S_JUMP: // to S_JUMP
674 restartAtFrame(jumpFrame);
677 case S_JUMP_PI: // to S_JUMP_PI
679 restartAtFramePI(jumpFrame);
684 case S_FFWD: // from S_FFWD -----------------------------------
688 case S_PLAY: // to S_PLAY
691 ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
692 if (stepback < currentFrameNumber)
693 currentFrameNumber -= stepback;
695 currentFrameNumber = 0;
696 restartAtFrame(currentFrameNumber);
699 case S_PAUSE_P: // to S_PAUSE_P
704 case S_PAUSE_I: // to S_PAUSE_I
710 case S_FFWD: // to S_FFWD
714 case S_FBWD: // to S_FBWD
721 case S_STOP: // to S_STOP
732 case S_JUMP: // to S_JUMP
735 restartAtFrame(jumpFrame);
738 case S_JUMP_PI: // to S_JUMP_PI
742 restartAtFramePI(jumpFrame);
747 case S_FBWD: // from S_FBWD -----------------------------------
751 case S_PLAY: // to S_PLAY
754 restartAtFrame(currentFrameNumber);
757 case S_PAUSE_P: // to S_PAUSE_P
762 case S_PAUSE_I: // to S_PAUSE_I
768 case S_FFWD: // to S_FFWD
775 case S_FBWD: // to S_FBWD
779 case S_STOP: // to S_STOP
790 case S_JUMP: // to S_JUMP
793 restartAtFrame(jumpFrame);
796 case S_JUMP_PI: // to S_JUMP_PI
800 restartAtFramePI(jumpFrame);
805 case S_STOP: // from S_STOP -----------------------------------
809 case S_PLAY: // to S_PLAY
814 audio->setStreamType(Audio::MPEG2_PES);
815 audio->systemMuteOff();
818 // FIXME use restartAtFrame here?
819 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
820 demuxer->setFrameNum(currentFrameNumber);
825 logger->log("Player", Log::DEBUG, "Immediate play");
836 case S_PAUSE_P: // to S_PAUSE_P
840 case S_PAUSE_I: // to S_PAUSE_I
844 case S_FFWD: // to S_FFWD
848 case S_FBWD: // to S_FBWD
852 case S_STOP: // to S_STOP
856 case S_JUMP: // to S_JUMP
860 case S_JUMP_PI: // to S_JUMP_PI
866 // case S_JUMP cannot be a start state because it auto flips to play
867 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
871 // ----------------------------------- Internal functions
876 pthread_mutex_lock(&mutex);
877 logger->log("Player", Log::DEBUG, "LOCKED");
880 WaitForSingleObject(mutex, INFINITE);
884 void Player::unLock()
887 logger->log("Player", Log::DEBUG, "UNLOCKING");
888 pthread_mutex_unlock(&mutex);
894 void Player::restartAtFrame(ULONG newFrame)
904 audio->setStreamType(Audio::MPEG2_PES);
907 currentFrameNumber = newFrame;
908 demuxer->setFrameNum(newFrame);
918 audio->systemMuteOff();
923 void Player::restartAtFramePI(ULONG newFrame)
926 ULONG nextiframeNumber;
934 // newFrame could be anywhere, go forwards to next I-Frame
935 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
937 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
938 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
940 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
941 if (!vdr->isConnected())
943 if (buffer) free(buffer);
948 videoLength = demuxer->stripAudio(buffer, amountReceived);
949 video->displayIFrame(buffer, videoLength);
950 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
952 currentFrameNumber = iframeNumber;
956 void Player::doConnectionLost()
958 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
959 Message* m = new Message();
960 m->to = messageReceiver;
962 m->message = Message::PLAYER_EVENT;
963 m->parameter = Player::CONNECTION_LOST;
964 messageQueue->postMessage(m);
967 // ----------------------------------- Callback
969 void Player::call(void* caller)
971 if (caller == demuxer)
973 logger->log("Player", Log::DEBUG, "Callback from demuxer");
975 if (video->getTVsize() == Video::ASPECT4X3)
977 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
982 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
983 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
985 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
986 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
988 Message* m = new Message();
990 m->to = messageReceiver;
991 m->message = Message::PLAYER_EVENT;
992 m->parameter = Player::ASPECT43;
993 messageQueue->postMessageFromOuterSpace(m);
995 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
997 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
998 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1000 Message* m = new Message();
1002 m->to = messageReceiver;
1003 m->message = Message::PLAYER_EVENT;
1004 m->parameter = Player::ASPECT169;
1005 messageQueue->postMessageFromOuterSpace(m);
1009 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1010 video->setAspectRatio(dxCurrentAspect,parx,pary);
1018 videoStartup = false;
1026 threadSignalNoLock();
1030 // ----------------------------------- Feed thread
1032 void Player::threadMethod()
1034 // this method used to be simple, the only thing it does
1035 // is farm out to threadFeed Live/Play/Scan
1036 // All the guff is to support scan hitting one end
1038 if ((state == S_FFWD) || (state == S_FBWD))
1040 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1041 else threadFeedScan();
1042 // if this returns then scan hit one end
1043 if (state == S_FFWD) // scan hit the end. stop
1046 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1047 m->to = messageReceiver;
1049 m->message = Message::PLAYER_EVENT;
1050 m->parameter = STOP_PLAYBACK;
1051 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1052 messageQueue->postMessage(m);
1053 logger->log("Player", Log::DEBUG, "Message posted...");
1056 // if execution gets to here, threadFeedScan hit the start, go to play mode
1059 audio->setStreamType(Audio::MPEG2_PES);
1062 demuxer->setFrameNum(currentFrameNumber);
1063 videoStartup = true;
1070 audio->systemMuteOff();
1074 if (state == S_PLAY) threadFeedPlay();
1077 void Player::threadFeedPlay()
1079 ULLONG feedPosition;
1080 UINT thisRead, writeLength, thisWrite, askFor;
1081 time_t lastRescan = time(NULL);
1083 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1084 if (!vdr->isConnected()) { doConnectionLost(); return; }
1085 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1096 // If we havn't rescanned for a while..
1097 if ((lastRescan + 60) < time(NULL))
1099 lengthBytes = vdr->rescanRecording(&lengthFrames);
1100 if (!vdr->isConnected()) { doConnectionLost(); return; }
1101 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1102 lastRescan = time(NULL);
1105 if (feedPosition >= lengthBytes) break; // finished playback
1109 if (startupBlockSize > lengthBytes)
1110 askFor = lengthBytes; // is a very small recording!
1112 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1116 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1117 askFor = lengthBytes - feedPosition;
1121 //logger->log("Player", Log::DEBUG, "Get Block in");
1123 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1124 //logger->log("Player", Log::DEBUG, "Get Block out");
1126 feedPosition += thisRead;
1128 if (!vdr->isConnected())
1134 if (!threadBuffer) break;
1138 int a_stream = demuxer->scan(threadBuffer, thisRead);
1139 demuxer->setAudioStream(a_stream);
1140 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1146 while(writeLength < thisRead)
1148 //logger->log("Player", Log::DEBUG, "Put in");
1149 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1150 //logger->log("Player", Log::DEBUG, "Put out");
1151 writeLength += thisWrite;
1155 // demuxer is full and can't take anymore
1157 threadWaitForSignal();
1165 threadBuffer = NULL;
1170 logger->log("Player", Log::DEBUG, "Recording playback ends");
1172 if (videoStartup) // oh woe. there never was a stream, I was conned!
1174 videoStartup = false;
1176 MILLISLEEP(500); // I think this will solve a race
1182 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1183 m->to = messageReceiver;
1185 m->message = Message::PLAYER_EVENT;
1186 m->parameter = Player::STOP_PLAYBACK;
1187 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1188 messageQueue->postMessage(m);
1192 void Player::threadPTSFeedScan()
1194 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1196 ULONG direction = 0;
1198 ULONG baseFrameNumber = 0;
1199 ULONG iframeNumber = 0;
1200 ULONG iframeLength = 0;
1201 ULONG currentfeedFrameNumber=currentFrameNumber;
1202 ULONG firstFrameNumber=currentFrameNumber;
1204 UINT amountReceived;
1210 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1211 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1212 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1214 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1217 int frameTimeOffset = 0; // Time in msec between frames
1218 int disp_msec = 0; // Time taken to display data
1219 int total_msec = 0; // Time taken to fetch data and display it
1222 if (state == S_FFWD) {
1223 direction = 1; // and 0 for backward
1226 video->EnterIframePlayback();
1230 // Fetch I-frames until we get one that can be displayed in good time
1231 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1233 baseFrameNumber = currentfeedFrameNumber;
1236 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1239 if (iframeNumber >= lengthFrames) return;
1240 // scan has got to the end of what we knew to be there before we started scanning
1242 baseFrameNumber = iframeNumber;
1244 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1246 logger->log("Player", Log::DEBUG, "XXX Got frame");
1248 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1250 if (!vdr->isConnected())
1252 if (threadBuffer) free(threadBuffer);
1261 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1262 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1264 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1270 ULLONG cur_time=video->getCurrentTimestamp();
1271 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1272 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1274 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1279 playtime +=frameTimeOffset;
1280 currentfeedFrameNumber = iframeNumber;
1282 ULLONG cur_time=video->getCurrentTimestamp();
1283 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1284 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1286 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1291 threadBuffer = NULL;
1297 void Player::threadFeedScan()
1299 // This method is actually really simple - get frame from vdr,
1300 // spit it at the video chip, wait for a time. Most of the code here
1301 // is to get the wait right so that the scan occurs at the correct rate.
1303 ULONG direction = 0;
1304 ULONG baseFrameNumber = 0;
1305 ULONG iframeNumber = 0;
1306 ULONG iframeLength = 0;
1308 UINT amountReceived;
1312 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1313 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1314 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1316 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1319 int frameTimeOffset = 0; // Time in msec between frames
1320 int disp_msec = 0; // Time taken to display data
1321 int total_msec = 0; // Time taken to fetch data and display it
1324 if (state == S_FFWD) direction = 1; // and 0 for backward
1328 // Fetch I-frames until we get one that can be displayed in good time
1329 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1331 baseFrameNumber = currentFrameNumber;
1335 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1338 if (iframeNumber >= lengthFrames) return;
1339 // scan has got to the end of what we knew to be there before we started scanning
1341 baseFrameNumber = iframeNumber;
1342 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1344 gettimeofday(&clock0, NULL);
1346 clock0 = timeGetTime();
1350 while (clock2.tv_sec != 0 &&
1351 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1352 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1354 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1356 logger->log("Player", Log::DEBUG, "XXX Got frame");
1358 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1360 if (!vdr->isConnected())
1362 if (threadBuffer) free(threadBuffer);
1368 gettimeofday(&clock1, NULL);
1369 if (clock2.tv_sec != 0)
1370 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1371 + (clock2.tv_usec - clock1.tv_usec) / 1000
1372 + frameTimeOffset - disp_msec;
1374 clock1 = timeGetTime();
1376 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1378 if (sleepTime < 0) sleepTime = 0;
1380 MILLISLEEP(sleepTime);
1381 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1383 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1384 video->displayIFrame(threadBuffer, videoLength);
1385 currentFrameNumber = iframeNumber;
1387 threadBuffer = NULL;
1390 gettimeofday(&clock2, NULL);
1391 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1392 + (clock2.tv_usec - clock0.tv_usec) / 1000
1394 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1395 + (clock2.tv_usec - clock1.tv_usec) / 1000
1398 clock2 = timeGetTime();
1399 total_msec = clock2 - clock0 - sleepTime;
1400 disp_msec = clock2 - clock1 - sleepTime;
1402 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1406 void Player::threadPostStopCleanup()
1411 threadBuffer = NULL;
1415 // ----------------------------------- Dev
1418 void Player::test1()
1420 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1423 void Player::test2()
1425 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");