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");
978 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
979 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
981 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
982 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
984 Message* m = new Message();
986 m->to = messageReceiver;
987 m->message = Message::PLAYER_EVENT;
988 m->parameter = Player::ASPECT43;
989 messageQueue->postMessageFromOuterSpace(m);
991 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
993 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
994 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
996 Message* m = new Message();
998 m->to = messageReceiver;
999 m->message = Message::PLAYER_EVENT;
1000 m->parameter = Player::ASPECT169;
1001 messageQueue->postMessageFromOuterSpace(m);
1005 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1006 video->setAspectRatio(dxCurrentAspect,parx,pary);
1014 videoStartup = false;
1022 threadSignalNoLock();
1026 // ----------------------------------- Feed thread
1028 void Player::threadMethod()
1030 // this method used to be simple, the only thing it does
1031 // is farm out to threadFeed Live/Play/Scan
1032 // All the guff is to support scan hitting one end
1034 if ((state == S_FFWD) || (state == S_FBWD))
1036 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1037 else threadFeedScan();
1038 // if this returns then scan hit one end
1039 if (state == S_FFWD) // scan hit the end. stop
1042 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1043 m->to = messageReceiver;
1045 m->message = Message::PLAYER_EVENT;
1046 m->parameter = STOP_PLAYBACK;
1047 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1048 messageQueue->postMessage(m);
1049 logger->log("Player", Log::DEBUG, "Message posted...");
1052 // if execution gets to here, threadFeedScan hit the start, go to play mode
1055 audio->setStreamType(Audio::MPEG2_PES);
1058 demuxer->setFrameNum(currentFrameNumber);
1059 videoStartup = true;
1066 audio->systemMuteOff();
1070 if (state == S_PLAY) threadFeedPlay();
1073 void Player::threadFeedPlay()
1075 ULLONG feedPosition;
1076 UINT thisRead, writeLength, thisWrite, askFor;
1077 time_t lastRescan = time(NULL);
1079 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1080 if (!vdr->isConnected()) { doConnectionLost(); return; }
1081 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1092 // If we havn't rescanned for a while..
1093 if ((lastRescan + 60) < time(NULL))
1095 lengthBytes = vdr->rescanRecording(&lengthFrames);
1096 if (!vdr->isConnected()) { doConnectionLost(); return; }
1097 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1098 lastRescan = time(NULL);
1101 if (feedPosition >= lengthBytes) break; // finished playback
1105 if (startupBlockSize > lengthBytes)
1106 askFor = lengthBytes; // is a very small recording!
1108 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1112 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1113 askFor = lengthBytes - feedPosition;
1117 //logger->log("Player", Log::DEBUG, "Get Block in");
1119 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1120 //logger->log("Player", Log::DEBUG, "Get Block out");
1122 feedPosition += thisRead;
1124 if (!vdr->isConnected())
1130 if (!threadBuffer) break;
1134 int a_stream = demuxer->scan(threadBuffer, thisRead);
1135 demuxer->setAudioStream(a_stream);
1136 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1142 while(writeLength < thisRead)
1144 //logger->log("Player", Log::DEBUG, "Put in");
1145 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1146 //logger->log("Player", Log::DEBUG, "Put out");
1147 writeLength += thisWrite;
1151 // demuxer is full and can't take anymore
1153 threadWaitForSignal();
1161 threadBuffer = NULL;
1166 logger->log("Player", Log::DEBUG, "Recording playback ends");
1168 if (videoStartup) // oh woe. there never was a stream, I was conned!
1170 videoStartup = false;
1172 MILLISLEEP(500); // I think this will solve a race
1178 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1179 m->to = messageReceiver;
1181 m->message = Message::PLAYER_EVENT;
1182 m->parameter = Player::STOP_PLAYBACK;
1183 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1184 messageQueue->postMessage(m);
1188 void Player::threadPTSFeedScan()
1190 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1192 ULONG direction = 0;
1194 ULONG baseFrameNumber = 0;
1195 ULONG iframeNumber = 0;
1196 ULONG iframeLength = 0;
1197 ULONG currentfeedFrameNumber=currentFrameNumber;
1198 ULONG firstFrameNumber=currentFrameNumber;
1200 UINT amountReceived;
1206 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1207 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1208 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1210 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1213 int frameTimeOffset = 0; // Time in msec between frames
1214 int disp_msec = 0; // Time taken to display data
1215 int total_msec = 0; // Time taken to fetch data and display it
1218 if (state == S_FFWD) {
1219 direction = 1; // and 0 for backward
1222 video->EnterIframePlayback();
1226 // Fetch I-frames until we get one that can be displayed in good time
1227 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1229 baseFrameNumber = currentfeedFrameNumber;
1232 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1235 if (iframeNumber >= lengthFrames) return;
1236 // scan has got to the end of what we knew to be there before we started scanning
1238 baseFrameNumber = iframeNumber;
1240 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1242 logger->log("Player", Log::DEBUG, "XXX Got frame");
1244 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1246 if (!vdr->isConnected())
1248 if (threadBuffer) free(threadBuffer);
1257 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1258 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1260 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1266 ULLONG cur_time=video->getCurrentTimestamp();
1267 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1268 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1270 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1275 playtime +=frameTimeOffset;
1276 currentfeedFrameNumber = iframeNumber;
1278 ULLONG cur_time=video->getCurrentTimestamp();
1279 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1280 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1282 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1287 threadBuffer = NULL;
1293 void Player::threadFeedScan()
1295 // This method is actually really simple - get frame from vdr,
1296 // spit it at the video chip, wait for a time. Most of the code here
1297 // is to get the wait right so that the scan occurs at the correct rate.
1299 ULONG direction = 0;
1300 ULONG baseFrameNumber = 0;
1301 ULONG iframeNumber = 0;
1302 ULONG iframeLength = 0;
1304 UINT amountReceived;
1308 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1309 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1310 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1312 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1315 int frameTimeOffset = 0; // Time in msec between frames
1316 int disp_msec = 0; // Time taken to display data
1317 int total_msec = 0; // Time taken to fetch data and display it
1320 if (state == S_FFWD) direction = 1; // and 0 for backward
1324 // Fetch I-frames until we get one that can be displayed in good time
1325 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1327 baseFrameNumber = currentFrameNumber;
1331 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1334 if (iframeNumber >= lengthFrames) return;
1335 // scan has got to the end of what we knew to be there before we started scanning
1337 baseFrameNumber = iframeNumber;
1338 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1340 gettimeofday(&clock0, NULL);
1342 clock0 = timeGetTime();
1346 while (clock2.tv_sec != 0 &&
1347 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1348 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1350 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1352 logger->log("Player", Log::DEBUG, "XXX Got frame");
1354 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1356 if (!vdr->isConnected())
1358 if (threadBuffer) free(threadBuffer);
1364 gettimeofday(&clock1, NULL);
1365 if (clock2.tv_sec != 0)
1366 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1367 + (clock2.tv_usec - clock1.tv_usec) / 1000
1368 + frameTimeOffset - disp_msec;
1370 clock1 = timeGetTime();
1372 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1374 if (sleepTime < 0) sleepTime = 0;
1376 MILLISLEEP(sleepTime);
1377 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1379 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1380 video->displayIFrame(threadBuffer, videoLength);
1381 currentFrameNumber = iframeNumber;
1383 threadBuffer = NULL;
1386 gettimeofday(&clock2, NULL);
1387 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1388 + (clock2.tv_usec - clock0.tv_usec) / 1000
1390 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1391 + (clock2.tv_usec - clock1.tv_usec) / 1000
1394 clock2 = timeGetTime();
1395 total_msec = clock2 - clock0 - sleepTime;
1396 disp_msec = clock2 - clock1 - sleepTime;
1398 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1402 void Player::threadPostStopCleanup()
1407 threadBuffer = NULL;
1411 // ----------------------------------- Dev
1414 void Player::test1()
1416 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1419 void Player::test2()
1421 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");