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");
118 if (Command::getInstance()->getSubDefault()) {
119 turnSubtitlesOn(true);
121 turnSubtitlesOn(false);
128 int Player::shutdown()
130 if (!initted) return 0;
148 void Player::setStartFrame(ULONG startFrame)
150 currentFrameNumber = startFrame;
153 void Player::setLengthBytes(ULLONG length)
155 lengthBytes = length;
156 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
159 void Player::setLengthFrames(ULONG length)
161 lengthFrames = length;
162 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
165 ULONG Player::getLengthFrames()
170 ULONG Player::getCurrentFrameNum()
172 if (startup) return 0;
177 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
181 return currentFrameNumber;
183 return 0; // shouldn't happen
187 bool* Player::getDemuxerMpegAudioChannels()
189 return demuxer->getmpAudioChannels();
192 bool* Player::getDemuxerAc3AudioChannels()
194 return demuxer->getac3AudioChannels();
197 bool* Player::getDemuxerSubtitleChannels()
199 return demuxer->getSubtitleChannels();
202 int Player::getCurrentAudioChannel()
204 if (is_pesrecording) {
205 return demuxer->getselAudioChannel();
207 return ((DemuxerTS*)demuxer)->getAID();
211 int Player::getCurrentSubtitleChannel()
213 if (is_pesrecording) {
214 return demuxer->getselSubtitleChannel();
216 return ((DemuxerTS*)demuxer)->getSubID();
220 void Player::setSubtitleChannel(int newChannel)
222 if (is_pesrecording) {
223 demuxer->setDVBSubtitleStream(newChannel);
225 ((DemuxerTS*)demuxer)->setSubID(newChannel);
229 int *Player::getTeletxtSubtitlePages()
231 return teletext->getSubtitlePages();
234 void Player::setAudioChannel(int newChannel, int type, int streamtype)
236 if (is_pesrecording) {
237 demuxer->setAudioStream(newChannel);
240 ((DemuxerTS*)demuxer)->setAID(newChannel,type,streamtype,false);
245 bool Player::toggleSubtitles()
247 if (!subtitlesShowing)
249 subtitlesShowing = true;
254 subtitlesShowing = false;
257 return subtitlesShowing;
260 void Player::turnSubtitlesOn(bool ison) {
263 subtitlesShowing = true;
268 subtitlesShowing = false;
274 void Player::tellSubtitlesOSDVisible(bool visible){
275 subtitles->setOSDMenuVisibility(visible);
278 Channel * Player::getDemuxerChannel() {
279 if (!is_pesrecording) {
280 return ((DemuxerTS*) demuxer)->getChannelInfo();
282 return NULL; //Should not happen!
286 // ----------------------------------- Externally called events
290 if (!initted) return;
291 if (state == S_PLAY) return;
294 bool doUnlock = false;
295 if (state == S_PAUSE_P) doUnlock = true;
297 if (doUnlock) unLock();
300 void Player::playpause()
302 if (!initted) return;
305 bool doUnlock = false;
308 switchState(S_PAUSE_P);
310 if (state == S_PAUSE_P) doUnlock = true;
313 if (doUnlock) unLock();
322 if (!initted) return;
323 if (state == S_STOP) return;
325 logger->log("Player", Log::DEBUG, "Stop called lock");
332 if (!initted) return;
335 if ((state == S_FFWD) || (state == S_FBWD))
337 switchState(S_PAUSE_I);
339 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
345 switchState(S_PAUSE_P);
351 void Player::fastForward()
353 if (!initted) return;
361 case 4: ifactor = 8; break;
362 case 8: ifactor = 16; break;
363 case 16: ifactor = 32; break;
364 case 32: ifactor = 4; break;
375 void Player::fastBackward()
377 if (!initted) return;
385 case 4: ifactor = 8; break;
386 case 8: ifactor = 16; break;
387 case 16: ifactor = 32; break;
388 case 32: ifactor = 4; break;
399 void Player::jumpToPercent(double percent)
402 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
403 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
404 switchState(S_JUMP, newFrame);
405 // unLock(); - let thread unlock this
408 void Player::jumpToMark(int mark)
411 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
412 switchState(S_JUMP, mark);
413 // unLock(); - let thread unlock this
416 void Player::jumpToFrameP(int newFrame)
419 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
420 switchState(S_JUMP_PI, newFrame);
424 void Player::skipForward(int seconds)
427 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
428 ULONG newFrame = getCurrentFrameNum();
429 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
430 newFrame +=(ULONG) (((double)seconds) * fps);
431 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
432 else switchState(S_JUMP, newFrame);
433 // unLock(); - let thread unlock this
436 void Player::skipBackward(int seconds)
439 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
440 long newFrame = getCurrentFrameNum();
441 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
442 newFrame -= (ULONG) (((double)seconds) * fps);
443 if (newFrame < 0) newFrame = 0;
444 switchState(S_JUMP, newFrame);
445 // unLock(); - let thread unlock this
448 // ----------------------------------- Implementations called events
450 void Player::switchState(UCHAR toState, ULONG jumpFrame)
452 if (!initted) return;
454 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
456 switch(state) // current state selector
458 case S_PLAY: // from S_PLAY -----------------------------------
462 case S_PLAY: // to S_PLAY
466 case S_PAUSE_P: // to S_PAUSE_P
473 case S_PAUSE_I: // to S_PAUSE_I
478 case S_FFWD: // to S_FFWD
480 currentFrameNumber = getCurrentFrameNum();
481 audio->systemMuteOn();
492 case S_FBWD: // to S_FBWD
494 currentFrameNumber = getCurrentFrameNum();
495 audio->systemMuteOn();
506 case S_STOP: // to S_STOP
522 case S_JUMP: // to S_JUMP
524 restartAtFrame(jumpFrame);
527 case S_JUMP_PI: // to S_JUMP_PI
529 audio->systemMuteOn();
539 restartAtFramePI(jumpFrame);
544 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
548 case S_PLAY: // to S_PLAY
555 case S_PAUSE_P: // to S_PAUSE_P
559 case S_PAUSE_I: // to S_PAUSE_I
563 case S_FFWD: // to S_FFWD
565 currentFrameNumber = getCurrentFrameNum();
566 audio->systemMuteOn();
578 case S_FBWD: // to S_FBWD
580 currentFrameNumber = getCurrentFrameNum();
581 audio->systemMuteOn();
593 case S_STOP: // to S_STOP
606 audio->systemMuteOff();
610 case S_JUMP: // to S_JUMP
613 audio->systemMuteOn();
615 restartAtFrame(jumpFrame);
618 case S_JUMP_PI: // to S_JUMP_PI
620 audio->systemMuteOn();
631 restartAtFramePI(jumpFrame);
636 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
640 case S_PLAY: // to S_PLAY
643 restartAtFrame(currentFrameNumber);
646 case S_PAUSE_P: // to S_PAUSE_P
650 case S_PAUSE_I: // to S_PAUSE_I
654 case S_FFWD: // to S_FFWD
660 case S_FBWD: // to S_FBWD
666 case S_STOP: // to S_STOP
673 audio->systemMuteOff();
677 case S_JUMP: // to S_JUMP
680 restartAtFrame(jumpFrame);
683 case S_JUMP_PI: // to S_JUMP_PI
685 restartAtFramePI(jumpFrame);
690 case S_FFWD: // from S_FFWD -----------------------------------
694 case S_PLAY: // to S_PLAY
697 ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
698 if (stepback < currentFrameNumber)
699 currentFrameNumber -= stepback;
701 currentFrameNumber = 0;
702 restartAtFrame(currentFrameNumber);
705 case S_PAUSE_P: // to S_PAUSE_P
710 case S_PAUSE_I: // to S_PAUSE_I
716 case S_FFWD: // to S_FFWD
720 case S_FBWD: // to S_FBWD
727 case S_STOP: // to S_STOP
738 case S_JUMP: // to S_JUMP
741 restartAtFrame(jumpFrame);
744 case S_JUMP_PI: // to S_JUMP_PI
748 restartAtFramePI(jumpFrame);
753 case S_FBWD: // from S_FBWD -----------------------------------
757 case S_PLAY: // to S_PLAY
760 restartAtFrame(currentFrameNumber);
763 case S_PAUSE_P: // to S_PAUSE_P
768 case S_PAUSE_I: // to S_PAUSE_I
774 case S_FFWD: // to S_FFWD
781 case S_FBWD: // to S_FBWD
785 case S_STOP: // to S_STOP
796 case S_JUMP: // to S_JUMP
799 restartAtFrame(jumpFrame);
802 case S_JUMP_PI: // to S_JUMP_PI
806 restartAtFramePI(jumpFrame);
811 case S_STOP: // from S_STOP -----------------------------------
815 case S_PLAY: // to S_PLAY
820 audio->setStreamType(Audio::MPEG2_PES);
821 audio->systemMuteOff();
824 // FIXME use restartAtFrame here?
825 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
826 demuxer->setFrameNum(currentFrameNumber);
831 logger->log("Player", Log::DEBUG, "Immediate play");
842 case S_PAUSE_P: // to S_PAUSE_P
846 case S_PAUSE_I: // to S_PAUSE_I
850 case S_FFWD: // to S_FFWD
854 case S_FBWD: // to S_FBWD
858 case S_STOP: // to S_STOP
862 case S_JUMP: // to S_JUMP
866 case S_JUMP_PI: // to S_JUMP_PI
872 // case S_JUMP cannot be a start state because it auto flips to play
873 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
877 // ----------------------------------- Internal functions
882 pthread_mutex_lock(&mutex);
883 logger->log("Player", Log::DEBUG, "LOCKED");
886 WaitForSingleObject(mutex, INFINITE);
890 void Player::unLock()
893 logger->log("Player", Log::DEBUG, "UNLOCKING");
894 pthread_mutex_unlock(&mutex);
900 void Player::restartAtFrame(ULONG newFrame)
910 audio->setStreamType(Audio::MPEG2_PES);
913 currentFrameNumber = newFrame;
914 demuxer->setFrameNum(newFrame);
924 audio->systemMuteOff();
929 void Player::restartAtFramePI(ULONG newFrame)
932 ULONG nextiframeNumber;
940 // newFrame could be anywhere, go forwards to next I-Frame
941 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
943 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
944 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
946 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
947 if (!vdr->isConnected())
949 if (buffer) free(buffer);
954 videoLength = demuxer->stripAudio(buffer, amountReceived);
955 video->displayIFrame(buffer, videoLength);
956 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
958 currentFrameNumber = iframeNumber;
962 void Player::doConnectionLost()
964 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
965 Message* m = new Message();
966 m->to = messageReceiver;
968 m->message = Message::PLAYER_EVENT;
969 m->parameter.num = Player::CONNECTION_LOST;
970 messageQueue->postMessage(m);
973 // ----------------------------------- Callback
975 void Player::call(void* caller)
977 if (caller == demuxer)
979 logger->log("Player", Log::DEBUG, "Callback from demuxer");
981 if (video->getTVsize() == Video::ASPECT4X3)
983 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
988 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
989 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
991 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
992 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
994 Message* m = new Message();
996 m->to = messageReceiver;
997 m->message = Message::PLAYER_EVENT;
998 m->parameter.num = Player::ASPECT43;
999 messageQueue->postMessageFromOuterSpace(m);
1001 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1003 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
1004 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1006 Message* m = new Message();
1008 m->to = messageReceiver;
1009 m->message = Message::PLAYER_EVENT;
1010 m->parameter.num = Player::ASPECT169;
1011 messageQueue->postMessageFromOuterSpace(m);
1015 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1016 video->setAspectRatio(dxCurrentAspect,parx,pary);
1024 videoStartup = false;
1032 threadSignalNoLock();
1036 // ----------------------------------- Feed thread
1038 void Player::threadMethod()
1040 // this method used to be simple, the only thing it does
1041 // is farm out to threadFeed Live/Play/Scan
1042 // All the guff is to support scan hitting one end
1044 if ((state == S_FFWD) || (state == S_FBWD))
1046 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1047 else threadFeedScan();
1048 // if this returns then scan hit one end
1049 if (state == S_FFWD) // scan hit the end. stop
1052 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1053 m->to = messageReceiver;
1055 m->message = Message::PLAYER_EVENT;
1056 m->parameter.num = STOP_PLAYBACK;
1057 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1058 messageQueue->postMessage(m);
1059 logger->log("Player", Log::DEBUG, "Message posted...");
1062 // if execution gets to here, threadFeedScan hit the start, go to play mode
1065 audio->setStreamType(Audio::MPEG2_PES);
1068 demuxer->setFrameNum(currentFrameNumber);
1069 videoStartup = true;
1076 audio->systemMuteOff();
1080 if (state == S_PLAY) threadFeedPlay();
1083 void Player::threadFeedPlay()
1085 ULLONG feedPosition;
1086 UINT thisRead, writeLength, thisWrite, askFor;
1087 time_t lastRescan = time(NULL);
1089 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1090 if (!vdr->isConnected()) { doConnectionLost(); return; }
1091 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1102 // If we havn't rescanned for a while..
1103 if ((lastRescan + 60) < time(NULL))
1105 lengthBytes = vdr->rescanRecording(&lengthFrames);
1106 if (!vdr->isConnected()) { doConnectionLost(); return; }
1107 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1108 lastRescan = time(NULL);
1111 if (feedPosition >= lengthBytes) break; // finished playback
1115 if (startupBlockSize > lengthBytes)
1116 askFor = lengthBytes; // is a very small recording!
1118 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1122 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1123 askFor = lengthBytes - feedPosition;
1127 //logger->log("Player", Log::DEBUG, "Get Block in");
1129 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1130 //logger->log("Player", Log::DEBUG, "Get Block out");
1132 feedPosition += thisRead;
1134 if (!vdr->isConnected())
1140 if (!threadBuffer) break;
1144 int a_stream = demuxer->scan(threadBuffer, thisRead);
1145 demuxer->setAudioStream(a_stream);
1146 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1152 while(writeLength < thisRead)
1154 //logger->log("Player", Log::DEBUG, "Put in");
1155 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1156 //logger->log("Player", Log::DEBUG, "Put out");
1157 writeLength += thisWrite;
1161 // demuxer is full and can't take anymore
1163 threadWaitForSignal();
1171 threadBuffer = NULL;
1176 logger->log("Player", Log::DEBUG, "Recording playback ends");
1178 if (videoStartup) // oh woe. there never was a stream, I was conned!
1180 videoStartup = false;
1182 MILLISLEEP(500); // I think this will solve a race
1188 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1189 m->to = messageReceiver;
1191 m->message = Message::PLAYER_EVENT;
1192 m->parameter.num = Player::STOP_PLAYBACK;
1193 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1194 messageQueue->postMessage(m);
1198 void Player::threadPTSFeedScan()
1200 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1202 ULONG direction = 0;
1204 ULONG baseFrameNumber = 0;
1205 ULONG iframeNumber = 0;
1206 ULONG iframeLength = 0;
1207 ULONG currentfeedFrameNumber=currentFrameNumber;
1208 ULONG firstFrameNumber=currentFrameNumber;
1210 UINT amountReceived;
1216 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1217 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1218 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1220 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1223 int frameTimeOffset = 0; // Time in msec between frames
1224 int disp_msec = 0; // Time taken to display data
1225 int total_msec = 0; // Time taken to fetch data and display it
1228 if (state == S_FFWD) {
1229 direction = 1; // and 0 for backward
1232 video->EnterIframePlayback();
1236 // Fetch I-frames until we get one that can be displayed in good time
1237 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1239 baseFrameNumber = currentfeedFrameNumber;
1242 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1245 if (iframeNumber >= lengthFrames) return;
1246 // scan has got to the end of what we knew to be there before we started scanning
1248 baseFrameNumber = iframeNumber;
1250 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1252 logger->log("Player", Log::DEBUG, "XXX Got frame");
1254 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1256 if (!vdr->isConnected())
1258 if (threadBuffer) free(threadBuffer);
1267 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1268 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1270 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1276 ULLONG cur_time=video->getCurrentTimestamp();
1277 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1278 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1280 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1285 playtime +=frameTimeOffset;
1286 currentfeedFrameNumber = iframeNumber;
1288 ULLONG cur_time=video->getCurrentTimestamp();
1289 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1290 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1292 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1297 threadBuffer = NULL;
1303 void Player::threadFeedScan()
1305 // This method is actually really simple - get frame from vdr,
1306 // spit it at the video chip, wait for a time. Most of the code here
1307 // is to get the wait right so that the scan occurs at the correct rate.
1309 ULONG direction = 0;
1310 ULONG baseFrameNumber = 0;
1311 ULONG iframeNumber = 0;
1312 ULONG iframeLength = 0;
1314 UINT amountReceived;
1318 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1319 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1320 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1322 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1325 int frameTimeOffset = 0; // Time in msec between frames
1326 int disp_msec = 0; // Time taken to display data
1327 int total_msec = 0; // Time taken to fetch data and display it
1330 if (state == S_FFWD) direction = 1; // and 0 for backward
1334 // Fetch I-frames until we get one that can be displayed in good time
1335 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1337 baseFrameNumber = currentFrameNumber;
1341 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1344 if (iframeNumber >= lengthFrames) return;
1345 // scan has got to the end of what we knew to be there before we started scanning
1347 baseFrameNumber = iframeNumber;
1348 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1350 gettimeofday(&clock0, NULL);
1352 clock0 = timeGetTime();
1356 while (clock2.tv_sec != 0 &&
1357 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1358 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1360 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1362 logger->log("Player", Log::DEBUG, "XXX Got frame");
1364 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1366 if (!vdr->isConnected())
1368 if (threadBuffer) free(threadBuffer);
1374 gettimeofday(&clock1, NULL);
1375 if (clock2.tv_sec != 0)
1376 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1377 + (clock2.tv_usec - clock1.tv_usec) / 1000
1378 + frameTimeOffset - disp_msec;
1380 clock1 = timeGetTime();
1382 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1384 if (sleepTime < 0) sleepTime = 0;
1386 MILLISLEEP(sleepTime);
1387 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1389 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1390 video->displayIFrame(threadBuffer, videoLength);
1391 currentFrameNumber = iframeNumber;
1393 threadBuffer = NULL;
1396 gettimeofday(&clock2, NULL);
1397 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1398 + (clock2.tv_usec - clock0.tv_usec) / 1000
1400 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1401 + (clock2.tv_usec - clock1.tv_usec) / 1000
1404 clock2 = timeGetTime();
1405 total_msec = clock2 - clock0 - sleepTime;
1406 disp_msec = clock2 - clock1 - sleepTime;
1408 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1412 void Player::threadPostStopCleanup()
1417 threadBuffer = NULL;
1421 // ----------------------------------- Dev
1424 void Player::test1()
1426 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1429 void Player::test2()
1431 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");