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 ULONG nextiframeNumber;
155 // newFrame could be anywhere, go forwards to next I-Frame
156 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
158 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
159 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
161 logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
162 currentFrameNumber = iframeNumber;
166 void Player::setLengthBytes(ULLONG length)
168 lengthBytes = length;
169 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
172 void Player::setLengthFrames(ULONG length)
174 lengthFrames = length;
175 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
178 ULONG Player::getLengthFrames()
183 ULONG Player::getCurrentFrameNum()
185 if (startup) return 0;
190 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
194 return currentFrameNumber;
196 return 0; // shouldn't happen
200 bool* Player::getDemuxerMpegAudioChannels()
202 return demuxer->getmpAudioChannels();
205 bool* Player::getDemuxerAc3AudioChannels()
207 return demuxer->getac3AudioChannels();
210 bool* Player::getDemuxerSubtitleChannels()
212 return demuxer->getSubtitleChannels();
215 int Player::getCurrentAudioChannel()
217 if (is_pesrecording) {
218 return demuxer->getselAudioChannel();
220 return ((DemuxerTS*)demuxer)->getAID();
224 int Player::getCurrentSubtitleChannel()
226 if (is_pesrecording) {
227 return demuxer->getselSubtitleChannel();
229 return ((DemuxerTS*)demuxer)->getSubID();
233 void Player::setSubtitleChannel(int newChannel)
235 if (is_pesrecording) {
236 demuxer->setDVBSubtitleStream(newChannel);
238 ((DemuxerTS*)demuxer)->setSubID(newChannel);
242 int *Player::getTeletxtSubtitlePages()
244 return teletext->getSubtitlePages();
247 void Player::setAudioChannel(int newChannel, int type, int streamtype)
249 if (is_pesrecording) {
250 demuxer->setAudioStream(newChannel);
253 ((DemuxerTS*)demuxer)->setAID(newChannel,type,streamtype,false);
258 bool Player::toggleSubtitles()
260 if (!subtitlesShowing)
262 subtitlesShowing = true;
267 subtitlesShowing = false;
270 return subtitlesShowing;
273 void Player::turnSubtitlesOn(bool ison) {
276 subtitlesShowing = true;
281 subtitlesShowing = false;
287 void Player::tellSubtitlesOSDVisible(bool visible){
288 subtitles->setOSDMenuVisibility(visible);
291 Channel * Player::getDemuxerChannel() {
292 if (!is_pesrecording) {
293 return ((DemuxerTS*) demuxer)->getChannelInfo();
295 return NULL; //Should not happen!
299 // ----------------------------------- Externally called events
303 if (!initted) return;
304 if (state == S_PLAY) return;
307 bool doUnlock = false;
308 if (state == S_PAUSE_P) doUnlock = true;
310 if (doUnlock) unLock();
313 void Player::playpause()
315 if (!initted) return;
318 bool doUnlock = false;
321 switchState(S_PAUSE_P);
323 if (state == S_PAUSE_P) doUnlock = true;
326 if (doUnlock) unLock();
335 if (!initted) return;
336 if (state == S_STOP) return;
338 logger->log("Player", Log::DEBUG, "Stop called lock");
345 if (!initted) return;
348 if ((state == S_FFWD) || (state == S_FBWD))
350 switchState(S_PAUSE_I);
352 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
358 switchState(S_PAUSE_P);
364 void Player::fastForward()
366 if (!initted) return;
374 case 4: ifactor = 8; break;
375 case 8: ifactor = 16; break;
376 case 16: ifactor = 32; break;
377 case 32: ifactor = 4; break;
388 void Player::fastBackward()
390 if (!initted) return;
398 case 4: ifactor = 8; break;
399 case 8: ifactor = 16; break;
400 case 16: ifactor = 32; break;
401 case 32: ifactor = 4; break;
412 void Player::jumpToPercent(double percent)
415 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
416 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
417 switchState(S_JUMP, newFrame);
418 // unLock(); - let thread unlock this
421 void Player::jumpToMark(int mark)
424 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
425 switchState(S_JUMP, mark);
426 // unLock(); - let thread unlock this
429 void Player::jumpToFrameP(int newFrame)
432 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
433 switchState(S_JUMP_PI, newFrame);
437 void Player::skipForward(int seconds)
440 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
441 ULONG newFrame = getCurrentFrameNum();
442 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
443 newFrame +=(ULONG) (((double)seconds) * fps);
444 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
445 else switchState(S_JUMP, newFrame);
446 // unLock(); - let thread unlock this
449 void Player::skipBackward(int seconds)
452 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
453 long newFrame = getCurrentFrameNum();
454 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
455 newFrame -= (ULONG) (((double)seconds) * fps);
456 if (newFrame < 0) newFrame = 0;
457 switchState(S_JUMP, newFrame);
458 // unLock(); - let thread unlock this
461 // ----------------------------------- Implementations called events
463 void Player::switchState(UCHAR toState, ULONG jumpFrame)
465 if (!initted) return;
467 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
469 switch(state) // current state selector
471 case S_PLAY: // from S_PLAY -----------------------------------
475 case S_PLAY: // to S_PLAY
479 case S_PAUSE_P: // to S_PAUSE_P
481 #ifdef VOMP_PLATTFORM_RASPBERRY
482 vfeed.stop(); // can't vfeed during pause
490 case S_PAUSE_I: // to S_PAUSE_I
495 case S_FFWD: // to S_FFWD
497 currentFrameNumber = getCurrentFrameNum();
498 audio->systemMuteOn();
509 case S_FBWD: // to S_FBWD
511 currentFrameNumber = getCurrentFrameNum();
512 audio->systemMuteOn();
523 case S_STOP: // to S_STOP
539 case S_JUMP: // to S_JUMP
541 restartAtFrame(jumpFrame);
544 case S_JUMP_PI: // to S_JUMP_PI
546 audio->systemMuteOn();
556 restartAtFramePI(jumpFrame);
561 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
565 case S_PLAY: // to S_PLAY
570 #ifdef VOMP_PLATTFORM_RASPBERRY
577 case S_PAUSE_P: // to S_PAUSE_P
581 case S_PAUSE_I: // to S_PAUSE_I
585 case S_FFWD: // to S_FFWD
587 currentFrameNumber = getCurrentFrameNum();
588 audio->systemMuteOn();
600 case S_FBWD: // to S_FBWD
602 currentFrameNumber = getCurrentFrameNum();
603 audio->systemMuteOn();
615 case S_STOP: // to S_STOP
628 audio->systemMuteOff();
632 case S_JUMP: // to S_JUMP
635 audio->systemMuteOn();
637 restartAtFrame(jumpFrame);
640 case S_JUMP_PI: // to S_JUMP_PI
642 audio->systemMuteOn();
653 restartAtFramePI(jumpFrame);
658 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
662 case S_PLAY: // to S_PLAY
665 restartAtFrame(currentFrameNumber);
668 case S_PAUSE_P: // to S_PAUSE_P
672 case S_PAUSE_I: // to S_PAUSE_I
676 case S_FFWD: // to S_FFWD
682 case S_FBWD: // to S_FBWD
688 case S_STOP: // to S_STOP
695 audio->systemMuteOff();
699 case S_JUMP: // to S_JUMP
702 restartAtFrame(jumpFrame);
705 case S_JUMP_PI: // to S_JUMP_PI
707 restartAtFramePI(jumpFrame);
712 case S_FFWD: // from S_FFWD -----------------------------------
716 case S_PLAY: // to S_PLAY
719 ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
720 if (stepback < currentFrameNumber)
721 currentFrameNumber -= stepback;
723 currentFrameNumber = 0;
724 restartAtFrame(currentFrameNumber);
727 case S_PAUSE_P: // to S_PAUSE_P
732 case S_PAUSE_I: // to S_PAUSE_I
738 case S_FFWD: // to S_FFWD
742 case S_FBWD: // to S_FBWD
749 case S_STOP: // to S_STOP
760 case S_JUMP: // to S_JUMP
763 restartAtFrame(jumpFrame);
766 case S_JUMP_PI: // to S_JUMP_PI
770 restartAtFramePI(jumpFrame);
775 case S_FBWD: // from S_FBWD -----------------------------------
779 case S_PLAY: // to S_PLAY
782 restartAtFrame(currentFrameNumber);
785 case S_PAUSE_P: // to S_PAUSE_P
790 case S_PAUSE_I: // to S_PAUSE_I
796 case S_FFWD: // to S_FFWD
803 case S_FBWD: // to S_FBWD
807 case S_STOP: // to S_STOP
818 case S_JUMP: // to S_JUMP
821 restartAtFrame(jumpFrame);
824 case S_JUMP_PI: // to S_JUMP_PI
828 restartAtFramePI(jumpFrame);
833 case S_STOP: // from S_STOP -----------------------------------
837 case S_PLAY: // to S_PLAY
842 audio->setStreamType(Audio::MPEG2_PES);
843 audio->systemMuteOff();
846 // FIXME use restartAtFrame here?
847 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
848 demuxer->setFrameNum(currentFrameNumber);
853 logger->log("Player", Log::DEBUG, "Immediate play");
864 case S_PAUSE_P: // to S_PAUSE_P
868 case S_PAUSE_I: // to S_PAUSE_I
872 case S_FFWD: // to S_FFWD
876 case S_FBWD: // to S_FBWD
880 case S_STOP: // to S_STOP
884 case S_JUMP: // to S_JUMP
888 case S_JUMP_PI: // to S_JUMP_PI
894 // case S_JUMP cannot be a start state because it auto flips to play
895 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
899 // ----------------------------------- Internal functions
904 pthread_mutex_lock(&mutex);
905 logger->log("Player", Log::DEBUG, "LOCKED");
908 WaitForSingleObject(mutex, INFINITE);
912 void Player::unLock()
915 logger->log("Player", Log::DEBUG, "UNLOCKING");
916 pthread_mutex_unlock(&mutex);
922 void Player::restartAtFrame(ULONG newFrame)
932 audio->setStreamType(Audio::MPEG2_PES);
935 currentFrameNumber = newFrame;
936 demuxer->setFrameNum(newFrame);
946 audio->systemMuteOff();
951 void Player::restartAtFramePI(ULONG newFrame)
954 ULONG nextiframeNumber;
962 // newFrame could be anywhere, go forwards to next I-Frame
963 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
965 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
966 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
968 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
969 if (!vdr->isConnected())
971 if (buffer) free(buffer);
976 videoLength = demuxer->stripAudio(buffer, amountReceived);
977 video->displayIFrame(buffer, videoLength);
978 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
980 currentFrameNumber = iframeNumber;
984 void Player::doConnectionLost()
986 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
987 Message* m = new Message();
988 m->to = messageReceiver;
990 m->message = Message::PLAYER_EVENT;
991 m->parameter.num = Player::CONNECTION_LOST;
992 messageQueue->postMessage(m);
995 // ----------------------------------- Callback
997 void Player::call(void* caller)
999 if (caller == demuxer)
1001 logger->log("Player", Log::DEBUG, "Callback from demuxer");
1003 if (video->getTVsize() == Video::ASPECT4X3)
1005 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
1010 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
1011 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
1013 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
1014 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
1016 Message* m = new Message();
1018 m->to = messageReceiver;
1019 m->message = Message::PLAYER_EVENT;
1020 m->parameter.num = Player::ASPECT43;
1021 messageQueue->postMessageFromOuterSpace(m);
1023 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1025 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
1026 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1028 Message* m = new Message();
1030 m->to = messageReceiver;
1031 m->message = Message::PLAYER_EVENT;
1032 m->parameter.num = Player::ASPECT169;
1033 messageQueue->postMessageFromOuterSpace(m);
1037 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1038 video->setAspectRatio(dxCurrentAspect,parx,pary);
1046 videoStartup = false;
1054 threadSignalNoLock();
1058 // ----------------------------------- Feed thread
1060 void Player::threadMethod()
1062 // this method used to be simple, the only thing it does
1063 // is farm out to threadFeed Live/Play/Scan
1064 // All the guff is to support scan hitting one end
1066 if ((state == S_FFWD) || (state == S_FBWD))
1068 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1069 else threadFeedScan();
1070 // if this returns then scan hit one end
1071 if (state == S_FFWD) // scan hit the end. stop
1074 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1075 m->to = messageReceiver;
1077 m->message = Message::PLAYER_EVENT;
1078 m->parameter.num = STOP_PLAYBACK;
1079 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1080 messageQueue->postMessage(m);
1081 logger->log("Player", Log::DEBUG, "Message posted...");
1084 // if execution gets to here, threadFeedScan hit the start, go to play mode
1087 audio->setStreamType(Audio::MPEG2_PES);
1090 demuxer->setFrameNum(currentFrameNumber);
1091 videoStartup = true;
1098 audio->systemMuteOff();
1102 if (state == S_PLAY) threadFeedPlay();
1105 void Player::threadFeedPlay()
1107 ULLONG feedPosition;
1108 UINT thisRead, writeLength, thisWrite, askFor;
1109 time_t lastRescan = time(NULL);
1111 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1112 if (!vdr->isConnected()) { doConnectionLost(); return; }
1113 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1124 // If we havn't rescanned for a while..
1125 if ((lastRescan + 60) < time(NULL))
1127 lengthBytes = vdr->rescanRecording(&lengthFrames);
1128 if (!vdr->isConnected()) { doConnectionLost(); return; }
1129 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1130 lastRescan = time(NULL);
1133 if (feedPosition >= lengthBytes) break; // finished playback
1137 if (startupBlockSize > lengthBytes)
1138 askFor = lengthBytes; // is a very small recording!
1140 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1144 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1145 askFor = lengthBytes - feedPosition;
1149 //logger->log("Player", Log::DEBUG, "Get Block in");
1151 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1152 //logger->log("Player", Log::DEBUG, "Get Block out");
1154 feedPosition += thisRead;
1156 if (!vdr->isConnected())
1162 if (!threadBuffer) break;
1166 int a_stream = demuxer->scan(threadBuffer, thisRead);
1167 demuxer->setAudioStream(a_stream);
1168 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1174 while(writeLength < thisRead)
1176 //logger->log("Player", Log::DEBUG, "Put in");
1177 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1178 //logger->log("Player", Log::DEBUG, "Put out");
1179 writeLength += thisWrite;
1183 // demuxer is full and can't take anymore
1185 threadWaitForSignal();
1193 threadBuffer = NULL;
1198 logger->log("Player", Log::DEBUG, "Recording playback ends");
1200 if (videoStartup) // oh woe. there never was a stream, I was conned!
1202 videoStartup = false;
1204 MILLISLEEP(500); // I think this will solve a race
1210 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1211 m->to = messageReceiver;
1213 m->message = Message::PLAYER_EVENT;
1214 m->parameter.num = Player::STOP_PLAYBACK;
1215 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1216 messageQueue->postMessage(m);
1220 void Player::threadPTSFeedScan()
1222 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1224 ULONG direction = 0;
1226 ULONG baseFrameNumber = 0;
1227 ULONG iframeNumber = 0;
1228 ULONG iframeLength = 0;
1229 ULONG currentfeedFrameNumber=currentFrameNumber;
1230 ULONG firstFrameNumber=currentFrameNumber;
1232 UINT amountReceived;
1237 int frameTimeOffset = 0; // Time in msec between frames
1239 if (state == S_FFWD)
1241 direction = 1; // and 0 for backward
1244 video->EnterIframePlayback();
1248 baseFrameNumber = currentfeedFrameNumber;
1251 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1254 if (iframeNumber >= lengthFrames) return;
1255 // scan has got to the end of what we knew to be there before we started scanning
1257 baseFrameNumber = iframeNumber;
1259 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1261 logger->log("Player", Log::DEBUG, "XXX Got frame");
1263 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1265 if (!vdr->isConnected())
1267 if (threadBuffer) free(threadBuffer);
1276 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1277 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1279 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1285 ULLONG cur_time=video->getCurrentTimestamp();
1286 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1287 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1289 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1294 playtime +=frameTimeOffset;
1295 currentfeedFrameNumber = iframeNumber;
1297 ULLONG cur_time=video->getCurrentTimestamp();
1298 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1299 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1301 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1306 threadBuffer = NULL;
1312 void Player::threadFeedScan()
1314 // This method is actually really simple - get frame from vdr,
1315 // spit it at the video chip, wait for a time. Most of the code here
1316 // is to get the wait right so that the scan occurs at the correct rate.
1318 ULONG direction = 0;
1319 ULONG baseFrameNumber = 0;
1320 ULONG iframeNumber = 0;
1321 ULONG iframeLength = 0;
1323 UINT amountReceived;
1327 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1328 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1329 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1331 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1334 int frameTimeOffset = 0; // Time in msec between frames
1335 int disp_msec = 0; // Time taken to display data
1336 int total_msec = 0; // Time taken to fetch data and display it
1339 if (state == S_FFWD) direction = 1; // and 0 for backward
1343 // Fetch I-frames until we get one that can be displayed in good time
1344 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1346 baseFrameNumber = currentFrameNumber;
1350 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1353 if (iframeNumber >= lengthFrames) return;
1354 // scan has got to the end of what we knew to be there before we started scanning
1356 baseFrameNumber = iframeNumber;
1357 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1359 gettimeofday(&clock0, NULL);
1361 clock0 = timeGetTime();
1365 while (clock2.tv_sec != 0 &&
1366 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1367 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1369 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1371 logger->log("Player", Log::DEBUG, "XXX Got frame");
1373 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1375 if (!vdr->isConnected() || !amountReceived)
1377 if (threadBuffer) free(threadBuffer);
1383 gettimeofday(&clock1, NULL);
1384 if (clock2.tv_sec != 0)
1385 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1386 + (clock2.tv_usec - clock1.tv_usec) / 1000
1387 + frameTimeOffset - disp_msec;
1389 clock1 = timeGetTime();
1391 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1393 if (sleepTime < 0) sleepTime = 0;
1395 MILLISLEEP(sleepTime);
1396 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1398 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1399 video->displayIFrame(threadBuffer, videoLength);
1400 currentFrameNumber = iframeNumber;
1402 threadBuffer = NULL;
1405 gettimeofday(&clock2, NULL);
1406 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1407 + (clock2.tv_usec - clock0.tv_usec) / 1000
1409 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1410 + (clock2.tv_usec - clock1.tv_usec) / 1000
1413 clock2 = timeGetTime();
1414 total_msec = clock2 - clock0 - sleepTime;
1415 disp_msec = clock2 - clock1 - sleepTime;
1417 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1421 void Player::threadPostStopCleanup()
1426 threadBuffer = NULL;
1430 // ----------------------------------- Dev
1433 void Player::test1()
1435 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1438 void Player::test2()
1440 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");