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);
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 Channel * Player::getDemuxerChannel() {
269 if (!is_pesrecording) {
270 return ((DemuxerTS*) demuxer)->getChannelInfo();
272 return NULL; //Should not happen!
276 // ----------------------------------- Externally called events
280 if (!initted) return;
281 if (state == S_PLAY) return;
284 bool doUnlock = false;
285 if (state == S_PAUSE_P) doUnlock = true;
287 if (doUnlock) unLock();
290 void Player::playpause()
292 if (!initted) return;
295 bool doUnlock = false;
298 switchState(S_PAUSE_P);
300 if (state == S_PAUSE_P) doUnlock = true;
303 if (doUnlock) unLock();
312 if (!initted) return;
313 if (state == S_STOP) return;
315 logger->log("Player", Log::DEBUG, "Stop called lock");
322 if (!initted) return;
325 if ((state == S_FFWD) || (state == S_FBWD))
327 switchState(S_PAUSE_I);
329 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
335 switchState(S_PAUSE_P);
341 void Player::fastForward()
343 if (!initted) return;
351 case 4: ifactor = 8; break;
352 case 8: ifactor = 16; break;
353 case 16: ifactor = 32; break;
354 case 32: ifactor = 4; break;
365 void Player::fastBackward()
367 if (!initted) return;
375 case 4: ifactor = 8; break;
376 case 8: ifactor = 16; break;
377 case 16: ifactor = 32; break;
378 case 32: ifactor = 4; break;
389 void Player::jumpToPercent(double percent)
392 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
393 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
394 switchState(S_JUMP, newFrame);
395 // unLock(); - let thread unlock this
398 void Player::jumpToMark(int mark)
401 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
402 switchState(S_JUMP, mark);
403 // unLock(); - let thread unlock this
406 void Player::jumpToFrameP(int newFrame)
409 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
410 switchState(S_JUMP_PI, newFrame);
414 void Player::skipForward(int seconds)
417 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
418 ULONG newFrame = getCurrentFrameNum();
419 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
420 newFrame +=(ULONG) (((double)seconds) * fps);
421 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
422 else switchState(S_JUMP, newFrame);
423 // unLock(); - let thread unlock this
426 void Player::skipBackward(int seconds)
429 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
430 long newFrame = getCurrentFrameNum();
431 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
432 newFrame -= (ULONG) (((double)seconds) * fps);
433 if (newFrame < 0) newFrame = 0;
434 switchState(S_JUMP, newFrame);
435 // unLock(); - let thread unlock this
438 // ----------------------------------- Implementations called events
440 void Player::switchState(UCHAR toState, ULONG jumpFrame)
442 if (!initted) return;
444 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
446 switch(state) // current state selector
448 case S_PLAY: // from S_PLAY -----------------------------------
452 case S_PLAY: // to S_PLAY
456 case S_PAUSE_P: // to S_PAUSE_P
463 case S_PAUSE_I: // to S_PAUSE_I
468 case S_FFWD: // to S_FFWD
470 currentFrameNumber = getCurrentFrameNum();
471 audio->systemMuteOn();
482 case S_FBWD: // to S_FBWD
484 currentFrameNumber = getCurrentFrameNum();
485 audio->systemMuteOn();
496 case S_STOP: // to S_STOP
512 case S_JUMP: // to S_JUMP
514 restartAtFrame(jumpFrame);
517 case S_JUMP_PI: // to S_JUMP_PI
519 audio->systemMuteOn();
529 restartAtFramePI(jumpFrame);
534 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
538 case S_PLAY: // to S_PLAY
545 case S_PAUSE_P: // to S_PAUSE_P
549 case S_PAUSE_I: // to S_PAUSE_I
553 case S_FFWD: // to S_FFWD
555 currentFrameNumber = getCurrentFrameNum();
556 audio->systemMuteOn();
568 case S_FBWD: // to S_FBWD
570 currentFrameNumber = getCurrentFrameNum();
571 audio->systemMuteOn();
583 case S_STOP: // to S_STOP
596 audio->systemMuteOff();
600 case S_JUMP: // to S_JUMP
603 audio->systemMuteOn();
605 restartAtFrame(jumpFrame);
608 case S_JUMP_PI: // to S_JUMP_PI
610 audio->systemMuteOn();
621 restartAtFramePI(jumpFrame);
626 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
630 case S_PLAY: // to S_PLAY
633 restartAtFrame(currentFrameNumber);
636 case S_PAUSE_P: // to S_PAUSE_P
640 case S_PAUSE_I: // to S_PAUSE_I
644 case S_FFWD: // to S_FFWD
650 case S_FBWD: // to S_FBWD
656 case S_STOP: // to S_STOP
663 audio->systemMuteOff();
667 case S_JUMP: // to S_JUMP
670 restartAtFrame(jumpFrame);
673 case S_JUMP_PI: // to S_JUMP_PI
675 restartAtFramePI(jumpFrame);
680 case S_FFWD: // from S_FFWD -----------------------------------
684 case S_PLAY: // to S_PLAY
687 ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
688 if (stepback < currentFrameNumber)
689 currentFrameNumber -= stepback;
691 currentFrameNumber = 0;
692 restartAtFrame(currentFrameNumber);
695 case S_PAUSE_P: // to S_PAUSE_P
700 case S_PAUSE_I: // to S_PAUSE_I
706 case S_FFWD: // to S_FFWD
710 case S_FBWD: // to S_FBWD
717 case S_STOP: // to S_STOP
728 case S_JUMP: // to S_JUMP
731 restartAtFrame(jumpFrame);
734 case S_JUMP_PI: // to S_JUMP_PI
738 restartAtFramePI(jumpFrame);
743 case S_FBWD: // from S_FBWD -----------------------------------
747 case S_PLAY: // to S_PLAY
750 restartAtFrame(currentFrameNumber);
753 case S_PAUSE_P: // to S_PAUSE_P
758 case S_PAUSE_I: // to S_PAUSE_I
764 case S_FFWD: // to S_FFWD
771 case S_FBWD: // to S_FBWD
775 case S_STOP: // to S_STOP
786 case S_JUMP: // to S_JUMP
789 restartAtFrame(jumpFrame);
792 case S_JUMP_PI: // to S_JUMP_PI
796 restartAtFramePI(jumpFrame);
801 case S_STOP: // from S_STOP -----------------------------------
805 case S_PLAY: // to S_PLAY
810 audio->setStreamType(Audio::MPEG2_PES);
811 audio->systemMuteOff();
814 // FIXME use restartAtFrame here?
815 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
816 demuxer->setFrameNum(currentFrameNumber);
821 logger->log("Player", Log::DEBUG, "Immediate play");
832 case S_PAUSE_P: // to S_PAUSE_P
836 case S_PAUSE_I: // to S_PAUSE_I
840 case S_FFWD: // to S_FFWD
844 case S_FBWD: // to S_FBWD
848 case S_STOP: // to S_STOP
852 case S_JUMP: // to S_JUMP
856 case S_JUMP_PI: // to S_JUMP_PI
862 // case S_JUMP cannot be a start state because it auto flips to play
863 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
867 // ----------------------------------- Internal functions
872 pthread_mutex_lock(&mutex);
873 logger->log("Player", Log::DEBUG, "LOCKED");
876 WaitForSingleObject(mutex, INFINITE);
880 void Player::unLock()
883 logger->log("Player", Log::DEBUG, "UNLOCKING");
884 pthread_mutex_unlock(&mutex);
890 void Player::restartAtFrame(ULONG newFrame)
900 audio->setStreamType(Audio::MPEG2_PES);
903 currentFrameNumber = newFrame;
904 demuxer->setFrameNum(newFrame);
914 audio->systemMuteOff();
919 void Player::restartAtFramePI(ULONG newFrame)
922 ULONG nextiframeNumber;
930 // newFrame could be anywhere, go forwards to next I-Frame
931 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
933 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
934 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
936 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
937 if (!vdr->isConnected())
939 if (buffer) free(buffer);
944 videoLength = demuxer->stripAudio(buffer, amountReceived);
945 video->displayIFrame(buffer, videoLength);
946 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
948 currentFrameNumber = iframeNumber;
952 void Player::doConnectionLost()
954 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
955 Message* m = new Message();
956 m->to = messageReceiver;
958 m->message = Message::PLAYER_EVENT;
959 m->parameter = Player::CONNECTION_LOST;
960 messageQueue->postMessage(m);
963 // ----------------------------------- Callback
965 void Player::call(void* caller)
967 if (caller == demuxer)
969 logger->log("Player", Log::DEBUG, "Callback from demuxer");
971 if (video->getTVsize() == Video::ASPECT4X3)
973 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
977 int dxCurrentAspect = demuxer->getAspectRatio();
978 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
980 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
981 video->setAspectRatio(Video::ASPECT4X3);
983 Message* m = new Message();
985 m->to = messageReceiver;
986 m->message = Message::PLAYER_EVENT;
987 m->parameter = Player::ASPECT43;
988 messageQueue->postMessageFromOuterSpace(m);
990 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
992 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
993 video->setAspectRatio(Video::ASPECT16X9);
995 Message* m = new Message();
997 m->to = messageReceiver;
998 m->message = Message::PLAYER_EVENT;
999 m->parameter = Player::ASPECT169;
1000 messageQueue->postMessageFromOuterSpace(m);
1004 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
1012 videoStartup = false;
1020 threadSignalNoLock();
1024 // ----------------------------------- Feed thread
1026 void Player::threadMethod()
1028 // this method used to be simple, the only thing it does
1029 // is farm out to threadFeed Live/Play/Scan
1030 // All the guff is to support scan hitting one end
1032 if ((state == S_FFWD) || (state == S_FBWD))
1034 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1035 else threadFeedScan();
1036 // if this returns then scan hit one end
1037 if (state == S_FFWD) // scan hit the end. stop
1040 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1041 m->to = messageReceiver;
1043 m->message = Message::PLAYER_EVENT;
1044 m->parameter = STOP_PLAYBACK;
1045 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1046 messageQueue->postMessage(m);
1047 logger->log("Player", Log::DEBUG, "Message posted...");
1050 // if execution gets to here, threadFeedScan hit the start, go to play mode
1053 audio->setStreamType(Audio::MPEG2_PES);
1056 demuxer->setFrameNum(currentFrameNumber);
1057 videoStartup = true;
1064 audio->systemMuteOff();
1068 if (state == S_PLAY) threadFeedPlay();
1071 void Player::threadFeedPlay()
1073 ULLONG feedPosition;
1074 UINT thisRead, writeLength, thisWrite, askFor;
1075 time_t lastRescan = time(NULL);
1077 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1078 if (!vdr->isConnected()) { doConnectionLost(); return; }
1079 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1090 // If we havn't rescanned for a while..
1091 if ((lastRescan + 60) < time(NULL))
1093 lengthBytes = vdr->rescanRecording(&lengthFrames);
1094 if (!vdr->isConnected()) { doConnectionLost(); return; }
1095 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1096 lastRescan = time(NULL);
1099 if (feedPosition >= lengthBytes) break; // finished playback
1103 if (startupBlockSize > lengthBytes)
1104 askFor = lengthBytes; // is a very small recording!
1106 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1110 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1111 askFor = lengthBytes - feedPosition;
1115 //logger->log("Player", Log::DEBUG, "Get Block in");
1117 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1118 //logger->log("Player", Log::DEBUG, "Get Block out");
1120 feedPosition += thisRead;
1122 if (!vdr->isConnected())
1128 if (!threadBuffer) break;
1132 int a_stream = demuxer->scan(threadBuffer, thisRead);
1133 demuxer->setAudioStream(a_stream);
1134 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1140 while(writeLength < thisRead)
1142 //logger->log("Player", Log::DEBUG, "Put in");
1143 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1144 //logger->log("Player", Log::DEBUG, "Put out");
1145 writeLength += thisWrite;
1149 // demuxer is full and can't take anymore
1151 threadWaitForSignal();
1159 threadBuffer = NULL;
1164 logger->log("Player", Log::DEBUG, "Recording playback ends");
1166 if (videoStartup) // oh woe. there never was a stream, I was conned!
1168 videoStartup = false;
1170 MILLISLEEP(500); // I think this will solve a race
1176 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1177 m->to = messageReceiver;
1179 m->message = Message::PLAYER_EVENT;
1180 m->parameter = Player::STOP_PLAYBACK;
1181 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1182 messageQueue->postMessage(m);
1186 void Player::threadPTSFeedScan()
1188 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1190 ULONG direction = 0;
1192 ULONG baseFrameNumber = 0;
1193 ULONG iframeNumber = 0;
1194 ULONG iframeLength = 0;
1195 ULONG currentfeedFrameNumber=currentFrameNumber;
1196 ULONG firstFrameNumber=currentFrameNumber;
1198 UINT amountReceived;
1204 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1205 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1206 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1208 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1211 int frameTimeOffset = 0; // Time in msec between frames
1212 int disp_msec = 0; // Time taken to display data
1213 int total_msec = 0; // Time taken to fetch data and display it
1216 if (state == S_FFWD) {
1217 direction = 1; // and 0 for backward
1220 video->EnterIframePlayback();
1224 // Fetch I-frames until we get one that can be displayed in good time
1225 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1227 baseFrameNumber = currentfeedFrameNumber;
1230 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1233 if (iframeNumber >= lengthFrames) return;
1234 // scan has got to the end of what we knew to be there before we started scanning
1236 baseFrameNumber = iframeNumber;
1238 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1240 logger->log("Player", Log::DEBUG, "XXX Got frame");
1242 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1244 if (!vdr->isConnected())
1246 if (threadBuffer) free(threadBuffer);
1255 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1256 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1258 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1264 ULLONG cur_time=video->getCurrentTimestamp();
1265 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1266 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1268 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1273 playtime +=frameTimeOffset;
1274 currentfeedFrameNumber = iframeNumber;
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 threadBuffer = NULL;
1291 void Player::threadFeedScan()
1293 // This method is actually really simple - get frame from vdr,
1294 // spit it at the video chip, wait for a time. Most of the code here
1295 // is to get the wait right so that the scan occurs at the correct rate.
1297 ULONG direction = 0;
1298 ULONG baseFrameNumber = 0;
1299 ULONG iframeNumber = 0;
1300 ULONG iframeLength = 0;
1302 UINT amountReceived;
1306 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1307 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1308 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1310 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1313 int frameTimeOffset = 0; // Time in msec between frames
1314 int disp_msec = 0; // Time taken to display data
1315 int total_msec = 0; // Time taken to fetch data and display it
1318 if (state == S_FFWD) direction = 1; // and 0 for backward
1322 // Fetch I-frames until we get one that can be displayed in good time
1323 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1325 baseFrameNumber = currentFrameNumber;
1329 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1332 if (iframeNumber >= lengthFrames) return;
1333 // scan has got to the end of what we knew to be there before we started scanning
1335 baseFrameNumber = iframeNumber;
1336 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1338 gettimeofday(&clock0, NULL);
1340 clock0 = timeGetTime();
1344 while (clock2.tv_sec != 0 &&
1345 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1346 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1348 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1350 logger->log("Player", Log::DEBUG, "XXX Got frame");
1352 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1354 if (!vdr->isConnected())
1356 if (threadBuffer) free(threadBuffer);
1362 gettimeofday(&clock1, NULL);
1363 if (clock2.tv_sec != 0)
1364 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1365 + (clock2.tv_usec - clock1.tv_usec) / 1000
1366 + frameTimeOffset - disp_msec;
1368 clock1 = timeGetTime();
1370 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1372 if (sleepTime < 0) sleepTime = 0;
1374 MILLISLEEP(sleepTime);
1375 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1377 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1378 video->displayIFrame(threadBuffer, videoLength);
1379 currentFrameNumber = iframeNumber;
1381 threadBuffer = NULL;
1384 gettimeofday(&clock2, NULL);
1385 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1386 + (clock2.tv_usec - clock0.tv_usec) / 1000
1388 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1389 + (clock2.tv_usec - clock1.tv_usec) / 1000
1392 clock2 = timeGetTime();
1393 total_msec = clock2 - clock0 - sleepTime;
1394 disp_msec = clock2 - clock1 - sleepTime;
1396 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1400 void Player::threadPostStopCleanup()
1405 threadBuffer = NULL;
1409 // ----------------------------------- Dev
1412 void Player::test1()
1414 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1417 void Player::test2()
1419 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");