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, see <https://www.gnu.org/licenses/>.
25 #include "demuxervdr.h"
26 #include "demuxerts.h"
28 #include "messagequeue.h"
31 #include "dvbsubtitles.h"
32 #include "osdreceiver.h"
34 #define USER_RESPONSE_TIME 500 // Milliseconds
36 // ----------------------------------- Called from outside, one offs or info funcs
38 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
39 : vfeed(this), afeed(this), tfeed(this)
41 messageQueue = tmessageQueue;
42 messageReceiver = tmessageReceiver;
43 osdReceiver = tosdReceiver;
44 audio = Audio::getInstance();
45 video = Video::getInstance();
46 logger = Log::getInstance();
47 vdr = VDR::getInstance();
54 if (initted) shutdown();
57 int Player::init(bool p_isPesRecording, double framespersecond)
59 if (initted) return 0;
61 pthread_mutex_init(&mutex, NULL);
63 mutex=CreateMutex(NULL,FALSE,NULL);
65 is_pesrecording = p_isPesRecording;
66 fps = framespersecond;
68 demuxer = new DemuxerVDR();
70 demuxer = new DemuxerTS();
71 if (!demuxer) return 0;
72 subtitles = new DVBSubtitles(osdReceiver);
73 if (!subtitles) return 0;
75 teletext = new TeletextDecoderVBIEBU();
76 if (!teletext) return 0;
77 teletext->setRecordigMode(true);
78 unsigned int demux_video_size = 2097152;
79 unsigned int demux_audio_size = 524288;
80 if (video->supportsh264())
82 demux_video_size *= 5 * 2;
84 if (audio->maysupportAc3())
86 //demux_audio_size*=2;
89 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
91 logger->log("Player", Log::ERR, "Demuxer failed to init");
104 if (Command::getInstance()->getSubDefault())
105 turnSubtitlesOn(true);
107 turnSubtitlesOn(false);
113 int Player::shutdown()
115 if (!initted) return 0;
133 void Player::setStartFrame(ULONG startFrame)
135 ULONG nextiframeNumber;
140 // newFrame could be anywhere, go forwards to next I-Frame
141 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
143 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
144 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
146 logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
147 currentFrameNumber = iframeNumber;
150 void Player::setLengthBytes(ULLONG length)
152 lengthBytes = length;
153 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
156 void Player::setLengthFrames(ULONG length)
158 lengthFrames = length;
159 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
162 ULONG Player::getLengthFrames()
167 ULONG Player::getCurrentFrameNum()
169 if (startup) return 0;
174 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
178 return currentFrameNumber;
180 return 0; // shouldn't happen
184 bool* Player::getDemuxerMpegAudioChannels()
186 return demuxer->getmpAudioChannels();
189 bool* Player::getDemuxerAc3AudioChannels()
191 return demuxer->getac3AudioChannels();
194 bool* Player::getDemuxerSubtitleChannels()
196 return demuxer->getSubtitleChannels();
199 int Player::getCurrentAudioChannel()
202 return demuxer->getselAudioChannel();
204 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
207 int Player::getCurrentSubtitleChannel()
210 return demuxer->getselSubtitleChannel();
212 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
215 void Player::setSubtitleChannel(int newChannel)
218 demuxer->setDVBSubtitleStream(newChannel);
220 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
223 int *Player::getTeletxtSubtitlePages()
225 return teletext->getSubtitlePages();
228 void Player::setAudioChannel(int newChannel, int type, int streamtype)
232 demuxer->setAudioStream(newChannel);
237 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
242 bool Player::toggleSubtitles()
244 if (!subtitlesShowing)
246 subtitlesShowing = true;
251 subtitlesShowing = false;
254 return subtitlesShowing;
257 void Player::turnSubtitlesOn(bool ison)
261 subtitlesShowing = true;
266 subtitlesShowing = false;
271 void Player::tellSubtitlesOSDVisible(bool visible)
273 subtitles->setOSDMenuVisibility(visible);
276 Channel * Player::getDemuxerChannel()
278 if (!is_pesrecording)
280 return dynamic_cast<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;
309 switchState(S_PAUSE_P);
313 if (state == S_PAUSE_P) doUnlock = true;
316 if (doUnlock) unLock();
321 if (!initted) return;
322 if (state == S_STOP) return;
324 logger->log("Player", Log::DEBUG, "Stop called lock");
331 if (!initted) return;
334 if ((state == S_FFWD) || (state == S_FBWD))
336 switchState(S_PAUSE_I);
338 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
344 switchState(S_PAUSE_P);
350 void Player::fastForward()
352 if (!initted) return;
360 case 4: ifactor = 8; break;
361 case 8: ifactor = 16; break;
362 case 16: ifactor = 32; break;
363 case 32: ifactor = 4; break;
374 void Player::fastBackward()
376 if (!initted) return;
384 case 4: ifactor = 8; break;
385 case 8: ifactor = 16; break;
386 case 16: ifactor = 32; break;
387 case 32: ifactor = 4; break;
398 void Player::jumpToPercent(double percent)
401 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
402 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
403 switchState(S_JUMP, newFrame);
404 // unLock(); - let thread unlock this
407 void Player::jumpToMark(int mark)
410 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
411 switchState(S_JUMP, mark);
412 // unLock(); - let thread unlock this
415 void Player::jumpToFrameP(int newFrame)
418 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
419 switchState(S_JUMP_PI, newFrame);
423 void Player::skipForward(int seconds)
426 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
427 ULONG newFrame = getCurrentFrameNum();
428 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
429 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
430 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
431 else switchState(S_JUMP, newFrame);
432 // unLock(); - let thread unlock this
435 void Player::skipBackward(int seconds)
438 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
439 long newFrame = getCurrentFrameNum();
440 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
441 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
442 if (newFrame < 0) newFrame = 0;
443 switchState(S_JUMP, newFrame);
444 // unLock(); - let thread unlock this
447 // ----------------------------------- Implementations called events
449 void Player::switchState(UCHAR toState, ULONG jumpFrame)
451 if (!initted) return;
453 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
455 switch(state) // current state selector
457 case S_PLAY: // from S_PLAY -----------------------------------
461 case S_PLAY: // to S_PLAY
465 case S_PAUSE_P: // to S_PAUSE_P
467 #ifdef VOMP_PLATFORM_RASPBERRY
468 vfeed.stop(); // can't vfeed during pause
476 case S_PAUSE_I: // to S_PAUSE_I
481 case S_FFWD: // to S_FFWD
483 currentFrameNumber = getCurrentFrameNum();
484 audio->systemMuteOn();
495 case S_FBWD: // to S_FBWD
497 currentFrameNumber = getCurrentFrameNum();
498 audio->systemMuteOn();
509 case S_STOP: // to S_STOP
525 case S_JUMP: // to S_JUMP
527 restartAtFrame(jumpFrame);
530 case S_JUMP_PI: // to S_JUMP_PI
532 audio->systemMuteOn();
542 restartAtFramePI(jumpFrame);
547 FALLTHROUGH // keep compiler happy (all posibilities return)
548 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
552 case S_PLAY: // to S_PLAY
557 #ifdef VOMP_PLATFORM_RASPBERRY
564 case S_PAUSE_P: // to S_PAUSE_P
568 case S_PAUSE_I: // to S_PAUSE_I
572 case S_FFWD: // to S_FFWD
574 currentFrameNumber = getCurrentFrameNum();
575 audio->systemMuteOn();
587 case S_FBWD: // to S_FBWD
589 currentFrameNumber = getCurrentFrameNum();
590 audio->systemMuteOn();
602 case S_STOP: // to S_STOP
615 audio->systemMuteOff();
619 case S_JUMP: // to S_JUMP
622 audio->systemMuteOn();
624 restartAtFrame(jumpFrame);
627 case S_JUMP_PI: // to S_JUMP_PI
629 audio->systemMuteOn();
640 restartAtFramePI(jumpFrame);
645 FALLTHROUGH // keep compiler happy (all posibilities return)
646 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
650 case S_PLAY: // to S_PLAY
653 restartAtFrame(currentFrameNumber);
656 case S_PAUSE_P: // to S_PAUSE_P
660 case S_PAUSE_I: // to S_PAUSE_I
664 case S_FFWD: // to S_FFWD
670 case S_FBWD: // to S_FBWD
676 case S_STOP: // to S_STOP
683 audio->systemMuteOff();
687 case S_JUMP: // to S_JUMP
690 restartAtFrame(jumpFrame);
693 case S_JUMP_PI: // to S_JUMP_PI
695 restartAtFramePI(jumpFrame);
700 FALLTHROUGH // keep compiler happy (all posibilities return)
701 case S_FFWD: // from S_FFWD -----------------------------------
705 case S_PLAY: // to S_PLAY
708 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
709 if (stepback < currentFrameNumber)
710 currentFrameNumber -= stepback;
712 currentFrameNumber = 0;
713 restartAtFrame(currentFrameNumber);
716 case S_PAUSE_P: // to S_PAUSE_P
721 case S_PAUSE_I: // to S_PAUSE_I
727 case S_FFWD: // to S_FFWD
731 case S_FBWD: // to S_FBWD
738 case S_STOP: // to S_STOP
749 case S_JUMP: // to S_JUMP
752 restartAtFrame(jumpFrame);
755 case S_JUMP_PI: // to S_JUMP_PI
759 restartAtFramePI(jumpFrame);
764 FALLTHROUGH // keep compiler happy (all posibilities return)
765 case S_FBWD: // from S_FBWD -----------------------------------
769 case S_PLAY: // to S_PLAY
772 restartAtFrame(currentFrameNumber);
775 case S_PAUSE_P: // to S_PAUSE_P
780 case S_PAUSE_I: // to S_PAUSE_I
786 case S_FFWD: // to S_FFWD
793 case S_FBWD: // to S_FBWD
797 case S_STOP: // to S_STOP
808 case S_JUMP: // to S_JUMP
811 restartAtFrame(jumpFrame);
814 case S_JUMP_PI: // to S_JUMP_PI
818 restartAtFramePI(jumpFrame);
823 FALLTHROUGH // keep compiler happy (all posibilities return)
824 case S_STOP: // from S_STOP -----------------------------------
828 case S_PLAY: // to S_PLAY
833 audio->setStreamType(Audio::MPEG2_PES);
834 audio->systemMuteOff();
837 // FIXME use restartAtFrame here?
838 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
839 demuxer->setFrameNum(currentFrameNumber);
844 logger->log("Player", Log::DEBUG, "Immediate play");
855 case S_PAUSE_P: // to S_PAUSE_P
859 case S_PAUSE_I: // to S_PAUSE_I
863 case S_FFWD: // to S_FFWD
867 case S_FBWD: // to S_FBWD
871 case S_STOP: // to S_STOP
875 case S_JUMP: // to S_JUMP
879 case S_JUMP_PI: // to S_JUMP_PI
885 // case S_JUMP cannot be a start state because it auto flips to play
886 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
890 // ----------------------------------- Internal functions
895 pthread_mutex_lock(&mutex);
896 logger->log("Player", Log::DEBUG, "LOCKED");
899 WaitForSingleObject(mutex, INFINITE);
903 void Player::unLock()
906 logger->log("Player", Log::DEBUG, "UNLOCKING");
907 pthread_mutex_unlock(&mutex);
913 void Player::restartAtFrame(ULONG newFrame)
923 audio->setStreamType(Audio::MPEG2_PES);
926 currentFrameNumber = newFrame;
927 demuxer->setFrameNum(newFrame);
937 audio->systemMuteOff();
942 void Player::restartAtFramePI(ULONG newFrame)
945 ULONG nextiframeNumber;
953 // newFrame could be anywhere, go forwards to next I-Frame
954 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
956 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
957 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
959 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
960 if (!vdr->isConnected())
962 if (buffer) free(buffer);
967 videoLength = demuxer->stripAudio(buffer, amountReceived);
968 video->displayIFrame(buffer, videoLength);
969 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
971 currentFrameNumber = iframeNumber;
975 void Player::doConnectionLost()
977 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
978 Message* m = new Message();
979 m->to = messageReceiver;
981 m->message = Message::PLAYER_EVENT;
982 m->parameter = Player::CONNECTION_LOST;
983 messageQueue->postMessage(m);
986 // ----------------------------------- Callback
988 void Player::call(void* caller)
990 if (caller == demuxer)
992 logger->log("Player", Log::DEBUG, "Callback from demuxer");
994 if (video->getTVsize() == Video::ASPECT4X3)
996 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
1001 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
1002 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
1004 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
1005 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
1007 Message* m = new Message();
1009 m->to = messageReceiver;
1010 m->message = Message::PLAYER_EVENT;
1011 m->parameter = Player::ASPECT43;
1012 messageQueue->postMessage(m);
1014 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1016 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
1017 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1019 Message* m = new Message();
1021 m->to = messageReceiver;
1022 m->message = Message::PLAYER_EVENT;
1023 m->parameter = Player::ASPECT169;
1024 messageQueue->postMessage(m);
1028 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1029 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1037 videoStartup = false;
1045 threadSignalNoLock();
1049 // ----------------------------------- Feed thread
1051 void Player::threadMethod()
1053 // this method used to be simple, the only thing it does
1054 // is farm out to threadFeed Live/Play/Scan
1055 // All the guff is to support scan hitting one end
1057 if ((state == S_FFWD) || (state == S_FBWD))
1059 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1060 else threadFeedScan();
1061 // if this returns then scan hit one end
1062 if (state == S_FFWD) // scan hit the end. stop
1065 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1066 m->to = messageReceiver;
1068 m->message = Message::PLAYER_EVENT;
1069 m->parameter = STOP_PLAYBACK;
1070 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1071 messageQueue->postMessage(m);
1072 logger->log("Player", Log::DEBUG, "Message posted...");
1075 // if execution gets to here, threadFeedScan hit the start, go to play mode
1078 audio->setStreamType(Audio::MPEG2_PES);
1081 demuxer->setFrameNum(currentFrameNumber);
1082 videoStartup = true;
1089 audio->systemMuteOff();
1093 if (state == S_PLAY) threadFeedPlay();
1096 void Player::threadFeedPlay()
1098 ULLONG feedPosition;
1099 UINT thisRead, writeLength, thisWrite, askFor;
1100 time_t lastRescan = time(NULL);
1102 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1103 if (!vdr->isConnected()) { doConnectionLost(); return; }
1104 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1115 // If we havn't rescanned for a while..
1116 if ((lastRescan + 60) < time(NULL))
1118 lengthBytes = vdr->rescanRecording(&lengthFrames);
1119 if (!vdr->isConnected()) { doConnectionLost(); return; }
1120 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1121 lastRescan = time(NULL);
1124 if (feedPosition >= lengthBytes) break; // finished playback
1128 if (startupBlockSize > lengthBytes)
1129 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1131 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1135 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1136 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1140 //logger->log("Player", Log::DEBUG, "Get Block in");
1142 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1143 //logger->log("Player", Log::DEBUG, "Get Block out");
1145 feedPosition += thisRead;
1147 if (!vdr->isConnected())
1153 if (!threadBuffer) break;
1157 int a_stream = demuxer->scan(threadBuffer, thisRead);
1158 demuxer->setAudioStream(a_stream);
1159 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1165 while(writeLength < thisRead)
1167 //logger->log("Player", Log::DEBUG, "Put in");
1168 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1169 //logger->log("Player", Log::DEBUG, "Put out");
1170 writeLength += thisWrite;
1174 // demuxer is full and can't take anymore
1176 if (!threadActive) { threadUnlock(); threadCheckExit(); }
1177 threadWaitForSignal();
1185 threadBuffer = NULL;
1190 logger->log("Player", Log::DEBUG, "Recording playback ends");
1192 if (videoStartup) // oh woe. there never was a stream, I was conned!
1194 videoStartup = false;
1196 MILLISLEEP(500); // I think this will solve a race
1202 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1203 m->to = messageReceiver;
1205 m->message = Message::PLAYER_EVENT;
1206 m->parameter = Player::STOP_PLAYBACK;
1207 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1208 messageQueue->postMessage(m);
1212 void Player::threadPTSFeedScan()
1214 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1216 ULONG direction = 0;
1218 ULONG baseFrameNumber = 0;
1219 ULONG iframeNumber = 0;
1220 ULONG iframeLength = 0;
1221 ULONG currentfeedFrameNumber=currentFrameNumber;
1222 ULONG firstFrameNumber=currentFrameNumber;
1224 UINT amountReceived;
1229 int frameTimeOffset = 0; // Time in msec between frames
1231 if (state == S_FFWD)
1233 direction = 1; // and 0 for backward
1236 video->EnterIframePlayback();
1240 baseFrameNumber = currentfeedFrameNumber;
1243 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1246 if (iframeNumber >= lengthFrames) return;
1247 // scan has got to the end of what we knew to be there before we started scanning
1249 baseFrameNumber = iframeNumber;
1251 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1253 logger->log("Player", Log::DEBUG, "XXX Got frame");
1255 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1257 if (!vdr->isConnected())
1259 if (threadBuffer) free(threadBuffer);
1268 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1269 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1271 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1277 ULLONG cur_time=video->getCurrentTimestamp();
1278 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1279 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1281 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1286 playtime +=frameTimeOffset;
1287 currentfeedFrameNumber = iframeNumber;
1289 ULLONG cur_time=video->getCurrentTimestamp();
1290 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1291 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1293 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1298 threadBuffer = NULL;
1304 void Player::threadFeedScan()
1306 // This method is actually really simple - get frame from vdr,
1307 // spit it at the video chip, wait for a time. Most of the code here
1308 // is to get the wait right so that the scan occurs at the correct rate.
1310 ULONG direction = 0;
1311 ULONG baseFrameNumber = 0;
1312 ULONG iframeNumber = 0;
1313 ULONG iframeLength = 0;
1315 UINT amountReceived;
1319 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1320 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1321 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1323 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1326 int frameTimeOffset = 0; // Time in msec between frames
1327 int disp_msec = 0; // Time taken to display data
1328 int total_msec = 0; // Time taken to fetch data and display it
1331 if (state == S_FFWD) direction = 1; // and 0 for backward
1335 // Fetch I-frames until we get one that can be displayed in good time
1336 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1338 baseFrameNumber = currentFrameNumber;
1342 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1345 if (iframeNumber >= lengthFrames) return;
1346 // scan has got to the end of what we knew to be there before we started scanning
1348 baseFrameNumber = iframeNumber;
1349 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1351 gettimeofday(&clock0, NULL);
1353 clock0 = timeGetTime();
1357 while (clock2.tv_sec != 0 &&
1358 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1359 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1361 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1363 logger->log("Player", Log::DEBUG, "XXX Got frame");
1365 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1367 if (!vdr->isConnected() || !amountReceived)
1369 if (threadBuffer) free(threadBuffer);
1375 gettimeofday(&clock1, NULL);
1376 if (clock2.tv_sec != 0)
1377 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1378 + (clock2.tv_usec - clock1.tv_usec) / 1000
1379 + frameTimeOffset - disp_msec;
1381 clock1 = timeGetTime();
1383 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1385 if (sleepTime < 0) sleepTime = 0;
1387 MILLISLEEP(sleepTime);
1388 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1390 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1391 video->displayIFrame(threadBuffer, videoLength);
1392 currentFrameNumber = iframeNumber;
1394 threadBuffer = NULL;
1397 gettimeofday(&clock2, NULL);
1398 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1399 + (clock2.tv_usec - clock0.tv_usec) / 1000
1401 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1402 + (clock2.tv_usec - clock1.tv_usec) / 1000
1405 clock2 = timeGetTime();
1406 total_msec = clock2 - clock0 - sleepTime;
1407 disp_msec = clock2 - clock1 - sleepTime;
1409 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1413 void Player::threadPostStopCleanup()
1418 threadBuffer = NULL;
1422 // ----------------------------------- Dev
1425 void Player::test1()
1427 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1430 void Player::test2()
1432 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");