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/>.
23 #include "demuxervdr.h"
24 #include "demuxerts.h"
26 #include "messagequeue.h"
29 #include "dvbsubtitles.h"
30 #include "osdreceiver.h"
32 #include "playervideorec.h"
34 #define USER_RESPONSE_TIME 500 // Milliseconds
36 // ----------------------------------- Called from outside, one offs or info funcs
38 PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
39 : vfeed(this), afeed(this), tfeed(this),
40 osdReceiver(tosdReceiver), messageQueue(tmessageQueue), messageReceiver(tmessageReceiver)
42 audio = Audio::getInstance();
43 video = Video::getInstance();
44 logger = Log::getInstance();
45 vdr = VDR::getInstance();
50 PlayerVideoRec::~PlayerVideoRec()
52 if (initted) shutdown();
55 int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
57 if (initted) return 0;
58 is_pesrecording = p_isPesRecording;
59 fps = framespersecond;
61 demuxer = new DemuxerVDR();
63 demuxer = new DemuxerTS();
64 if (!demuxer) return 0;
65 subtitles = new DVBSubtitles(osdReceiver);
66 if (!subtitles) return 0;
68 teletext = new TeletextDecoderVBIEBU();
69 if (!teletext) return 0;
70 teletext->setRecordigMode(true);
71 unsigned int demux_video_size = 2097152;
72 unsigned int demux_audio_size = 524288;
73 if (video->supportsh264())
75 demux_video_size *= 5 * 2;
77 if (audio->maysupportAc3())
79 //demux_audio_size*=2;
82 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
84 logger->log("Player", Log::ERR, "Demuxer failed to init");
93 if (Command::getInstance()->getSubDefault())
94 turnSubtitlesOn(true);
96 turnSubtitlesOn(false);
102 int PlayerVideoRec::shutdown()
104 if (!initted) return 0;
118 void PlayerVideoRec::setStartFrame(ULONG startFrame)
120 ULONG nextiframeNumber;
125 // newFrame could be anywhere, go forwards to next I-Frame
126 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
128 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
129 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
131 logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
132 currentFrameNumber = iframeNumber;
135 void PlayerVideoRec::setLengthBytes(ULLONG length)
137 lengthBytes = length;
138 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
141 void PlayerVideoRec::setLengthFrames(ULONG length)
143 lengthFrames = length;
144 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
147 ULONG PlayerVideoRec::getLengthFrames()
152 ULONG PlayerVideoRec::getCurrentFrameNum()
154 if (startup) return 0;
159 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
163 return currentFrameNumber;
165 return 0; // shouldn't happen
169 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
171 return demuxer->getmpAudioChannels();
174 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
176 return demuxer->getac3AudioChannels();
179 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
181 return demuxer->getSubtitleChannels();
184 int PlayerVideoRec::getCurrentAudioChannel()
187 return demuxer->getselAudioChannel();
189 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
192 int PlayerVideoRec::getCurrentSubtitleChannel()
195 return demuxer->getselSubtitleChannel();
197 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
200 void PlayerVideoRec::setSubtitleChannel(int newChannel)
203 demuxer->setDVBSubtitleStream(newChannel);
205 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
208 int *PlayerVideoRec::getTeletxtSubtitlePages()
210 return teletext->getSubtitlePages();
213 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
217 demuxer->setAudioStream(newChannel);
222 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
227 bool PlayerVideoRec::toggleSubtitles()
229 if (!subtitlesShowing)
231 subtitlesShowing = true;
236 subtitlesShowing = false;
239 return subtitlesShowing;
242 void PlayerVideoRec::turnSubtitlesOn(bool ison)
246 subtitlesShowing = true;
251 subtitlesShowing = false;
256 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
258 subtitles->setOSDMenuVisibility(visible);
261 Channel * PlayerVideoRec::getDemuxerChannel()
263 if (!is_pesrecording)
265 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
267 return NULL; //Should not happen!
271 // ----------------------------------- Externally called events
273 void PlayerVideoRec::play()
275 if (!initted) return;
276 if (state == S_PLAY) return;
279 bool doUnlock = false;
280 if (state == S_PAUSE_P) doUnlock = true;
282 if (doUnlock) stateMutex.unlock();
285 void PlayerVideoRec::playpause()
287 if (!initted) return;
290 bool doUnlock = false;
294 switchState(S_PAUSE_P);
298 if (state == S_PAUSE_P) doUnlock = true;
301 if (doUnlock) stateMutex.unlock();
304 void PlayerVideoRec::stop()
306 if (!initted) return;
307 if (state == S_STOP) return;
309 logger->log("Player", Log::DEBUG, "Stop called lock");
314 void PlayerVideoRec::pause()
316 if (!initted) return;
319 if ((state == S_FFWD) || (state == S_FBWD))
321 switchState(S_PAUSE_I);
323 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
329 switchState(S_PAUSE_P);
335 void PlayerVideoRec::fastForward()
337 if (!initted) return;
345 case 4: ifactor = 8; break;
346 case 8: ifactor = 16; break;
347 case 16: ifactor = 32; break;
348 case 32: ifactor = 4; break;
359 void PlayerVideoRec::fastBackward()
361 if (!initted) return;
369 case 4: ifactor = 8; break;
370 case 8: ifactor = 16; break;
371 case 16: ifactor = 32; break;
372 case 32: ifactor = 4; break;
383 void PlayerVideoRec::jumpToPercent(double percent)
386 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
387 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
388 switchState(S_JUMP, newFrame);
389 // stateMutex.unlock(); - let thread unlock this
392 void PlayerVideoRec::jumpToMark(int mark)
395 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
396 switchState(S_JUMP, mark);
397 // stateMutex.unlock(); - let thread unlock this
400 void PlayerVideoRec::jumpToFrameP(int newFrame)
403 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
404 switchState(S_JUMP_PI, newFrame);
408 void PlayerVideoRec::skipForward(int seconds)
411 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
412 ULONG newFrame = getCurrentFrameNum();
413 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
414 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
415 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
416 else switchState(S_JUMP, newFrame);
417 // stateMutex.unlock(); - let thread unlock this
420 void PlayerVideoRec::skipBackward(int seconds)
423 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
424 long newFrame = getCurrentFrameNum();
425 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
426 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
427 if (newFrame < 0) newFrame = 0;
428 switchState(S_JUMP, newFrame);
429 // stateMutex.unlock(); - let thread unlock this
432 // ----------------------------------- Implementations called events
434 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
436 if (!initted) return;
438 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
440 switch(state) // current state selector
442 case S_PLAY: // from S_PLAY -----------------------------------
446 case S_PLAY: // to S_PLAY
450 case S_PAUSE_P: // to S_PAUSE_P
452 #ifdef VOMP_PLATFORM_RASPBERRY
453 vfeed.stop(); // can't vfeed during pause
461 case S_PAUSE_I: // to S_PAUSE_I
466 case S_FFWD: // to S_FFWD
468 currentFrameNumber = getCurrentFrameNum();
469 audio->systemMuteOn();
480 case S_FBWD: // to S_FBWD
482 currentFrameNumber = getCurrentFrameNum();
483 audio->systemMuteOn();
494 case S_STOP: // to S_STOP
510 case S_JUMP: // to S_JUMP
512 restartAtFrame(jumpFrame);
515 case S_JUMP_PI: // to S_JUMP_PI
517 audio->systemMuteOn();
527 restartAtFramePI(jumpFrame);
532 FALLTHROUGH // keep compiler happy (all posibilities return)
533 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
537 case S_PLAY: // to S_PLAY
542 #ifdef VOMP_PLATFORM_RASPBERRY
549 case S_PAUSE_P: // to S_PAUSE_P
553 case S_PAUSE_I: // to S_PAUSE_I
557 case S_FFWD: // to S_FFWD
559 currentFrameNumber = getCurrentFrameNum();
560 audio->systemMuteOn();
572 case S_FBWD: // to S_FBWD
574 currentFrameNumber = getCurrentFrameNum();
575 audio->systemMuteOn();
587 case S_STOP: // to S_STOP
600 audio->systemMuteOff();
604 case S_JUMP: // to S_JUMP
607 audio->systemMuteOn();
609 restartAtFrame(jumpFrame);
612 case S_JUMP_PI: // to S_JUMP_PI
614 audio->systemMuteOn();
625 restartAtFramePI(jumpFrame);
630 FALLTHROUGH // keep compiler happy (all posibilities return)
631 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
635 case S_PLAY: // to S_PLAY
638 restartAtFrame(currentFrameNumber);
641 case S_PAUSE_P: // to S_PAUSE_P
645 case S_PAUSE_I: // to S_PAUSE_I
649 case S_FFWD: // to S_FFWD
655 case S_FBWD: // to S_FBWD
661 case S_STOP: // to S_STOP
668 audio->systemMuteOff();
672 case S_JUMP: // to S_JUMP
675 restartAtFrame(jumpFrame);
678 case S_JUMP_PI: // to S_JUMP_PI
680 restartAtFramePI(jumpFrame);
685 FALLTHROUGH // keep compiler happy (all posibilities return)
686 case S_FFWD: // from S_FFWD -----------------------------------
690 case S_PLAY: // to S_PLAY
693 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
694 if (stepback < currentFrameNumber)
695 currentFrameNumber -= stepback;
697 currentFrameNumber = 0;
698 restartAtFrame(currentFrameNumber);
701 case S_PAUSE_P: // to S_PAUSE_P
706 case S_PAUSE_I: // to S_PAUSE_I
712 case S_FFWD: // to S_FFWD
716 case S_FBWD: // to S_FBWD
723 case S_STOP: // to S_STOP
734 case S_JUMP: // to S_JUMP
737 restartAtFrame(jumpFrame);
740 case S_JUMP_PI: // to S_JUMP_PI
744 restartAtFramePI(jumpFrame);
749 FALLTHROUGH // keep compiler happy (all posibilities return)
750 case S_FBWD: // from S_FBWD -----------------------------------
754 case S_PLAY: // to S_PLAY
757 restartAtFrame(currentFrameNumber);
760 case S_PAUSE_P: // to S_PAUSE_P
765 case S_PAUSE_I: // to S_PAUSE_I
771 case S_FFWD: // to S_FFWD
778 case S_FBWD: // to S_FBWD
782 case S_STOP: // to S_STOP
793 case S_JUMP: // to S_JUMP
796 restartAtFrame(jumpFrame);
799 case S_JUMP_PI: // to S_JUMP_PI
803 restartAtFramePI(jumpFrame);
808 FALLTHROUGH // keep compiler happy (all posibilities return)
809 case S_STOP: // from S_STOP -----------------------------------
813 case S_PLAY: // to S_PLAY
818 audio->setStreamType(Audio::MPEG2_PES);
819 audio->systemMuteOff();
822 // FIXME use restartAtFrame here?
823 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
824 demuxer->setFrameNum(currentFrameNumber);
829 logger->log("Player", Log::DEBUG, "Immediate play");
840 case S_PAUSE_P: // to S_PAUSE_P
844 case S_PAUSE_I: // to S_PAUSE_I
848 case S_FFWD: // to S_FFWD
852 case S_FBWD: // to S_FBWD
856 case S_STOP: // to S_STOP
860 case S_JUMP: // to S_JUMP
864 case S_JUMP_PI: // to S_JUMP_PI
870 // case S_JUMP cannot be a start state because it auto flips to play
871 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
875 // ----------------------------------- Internal functions
877 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
887 audio->setStreamType(Audio::MPEG2_PES);
890 currentFrameNumber = newFrame;
891 demuxer->setFrameNum(newFrame);
901 audio->systemMuteOff();
906 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
909 ULONG nextiframeNumber;
917 // newFrame could be anywhere, go forwards to next I-Frame
918 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
920 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
921 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
923 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
924 if (!vdr->isConnected())
926 if (buffer) free(buffer);
931 videoLength = demuxer->stripAudio(buffer, amountReceived);
932 video->displayIFrame(buffer, videoLength);
933 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
935 currentFrameNumber = iframeNumber;
939 void PlayerVideoRec::doConnectionLost()
941 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
942 Message* m = new Message();
943 m->to = messageReceiver;
945 m->message = Message::PLAYER_EVENT;
946 m->parameter = PlayerVideoRec::CONNECTION_LOST;
947 messageQueue->postMessage(m);
950 // ----------------------------------- Callback
952 void PlayerVideoRec::call(void* caller)
954 if (caller == demuxer)
956 logger->log("Player", Log::DEBUG, "Callback from demuxer");
958 if (video->getTVsize() == Video::ASPECT4X3)
960 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
965 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
966 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
968 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
969 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
971 Message* m = new Message();
973 m->to = messageReceiver;
974 m->message = Message::PLAYER_EVENT;
975 m->parameter = PlayerVideoRec::ASPECT43;
976 messageQueue->postMessage(m);
978 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
980 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
981 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
983 Message* m = new Message();
985 m->to = messageReceiver;
986 m->message = Message::PLAYER_EVENT;
987 m->parameter = PlayerVideoRec::ASPECT169;
988 messageQueue->postMessage(m);
992 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
993 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1001 videoStartup = false;
1005 stateMutex.unlock();
1008 threadSignalNoLock();
1012 // ----------------------------------- Feed thread
1014 void PlayerVideoRec::threadMethod()
1016 // this method used to be simple, the only thing it does
1017 // is farm out to threadFeed Live/Play/Scan
1018 // All the guff is to support scan hitting one end
1020 if ((state == S_FFWD) || (state == S_FBWD))
1022 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1023 else threadFeedScan();
1024 // if this returns then scan hit one end
1025 if (state == S_FFWD) // scan hit the end. stop
1028 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1029 m->to = messageReceiver;
1031 m->message = Message::PLAYER_EVENT;
1032 m->parameter = STOP_PLAYBACK;
1033 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1034 messageQueue->postMessage(m);
1035 logger->log("Player", Log::DEBUG, "Message posted...");
1038 // if execution gets to here, threadFeedScan hit the start, go to play mode
1041 audio->setStreamType(Audio::MPEG2_PES);
1044 demuxer->setFrameNum(currentFrameNumber);
1045 videoStartup = true;
1052 audio->systemMuteOff();
1056 if (state == S_PLAY) threadFeedPlay();
1059 void PlayerVideoRec::threadFeedPlay()
1061 ULLONG feedPosition;
1062 UINT thisRead, writeLength, thisWrite, askFor;
1063 time_t lastRescan = time(NULL);
1065 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1066 if (!vdr->isConnected()) { doConnectionLost(); return; }
1067 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1078 // If we havn't rescanned for a while..
1079 if ((lastRescan + 60) < time(NULL))
1081 lengthBytes = vdr->rescanRecording(&lengthFrames);
1082 if (!vdr->isConnected()) { doConnectionLost(); return; }
1083 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1084 lastRescan = time(NULL);
1087 if (feedPosition >= lengthBytes) break; // finished playback
1091 if (startupBlockSize > lengthBytes)
1092 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1094 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1098 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1099 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1103 //logger->log("Player", Log::DEBUG, "Get Block in");
1105 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1106 //logger->log("Player", Log::DEBUG, "Get Block out");
1108 feedPosition += thisRead;
1110 if (!vdr->isConnected())
1116 if (!threadBuffer) break;
1120 int a_stream = demuxer->scan(threadBuffer, thisRead);
1121 demuxer->setAudioStream(a_stream);
1122 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1128 while(writeLength < thisRead)
1130 //logger->log("Player", Log::DEBUG, "Put in");
1131 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1132 //logger->log("Player", Log::DEBUG, "Put out");
1133 writeLength += thisWrite;
1137 // demuxer is full and can't take anymore
1139 if (!threadActive) { threadUnlock(); threadCheckExit(); }
1140 threadWaitForSignal();
1148 threadBuffer = NULL;
1153 logger->log("Player", Log::DEBUG, "Recording playback ends");
1155 if (videoStartup) // oh woe. there never was a stream, I was conned!
1157 videoStartup = false;
1158 stateMutex.unlock();
1159 MILLISLEEP(500); // I think this will solve a race
1165 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1166 m->to = messageReceiver;
1168 m->message = Message::PLAYER_EVENT;
1169 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1170 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1171 messageQueue->postMessage(m);
1175 void PlayerVideoRec::threadPTSFeedScan()
1177 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1179 ULONG direction = 0;
1181 ULONG baseFrameNumber = 0;
1182 ULONG iframeNumber = 0;
1183 ULONG iframeLength = 0;
1184 ULONG currentfeedFrameNumber=currentFrameNumber;
1185 ULONG firstFrameNumber=currentFrameNumber;
1187 UINT amountReceived;
1192 int frameTimeOffset = 0; // Time in msec between frames
1194 if (state == S_FFWD)
1196 direction = 1; // and 0 for backward
1199 video->EnterIframePlayback();
1203 baseFrameNumber = currentfeedFrameNumber;
1206 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1209 if (iframeNumber >= lengthFrames) return;
1210 // scan has got to the end of what we knew to be there before we started scanning
1212 baseFrameNumber = iframeNumber;
1214 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1216 logger->log("Player", Log::DEBUG, "XXX Got frame");
1218 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1220 if (!vdr->isConnected())
1222 if (threadBuffer) free(threadBuffer);
1231 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1232 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1234 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1240 ULLONG cur_time=video->getCurrentTimestamp();
1241 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1242 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1244 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1249 playtime +=frameTimeOffset;
1250 currentfeedFrameNumber = iframeNumber;
1252 ULLONG cur_time=video->getCurrentTimestamp();
1253 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1254 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1256 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1261 threadBuffer = NULL;
1267 void PlayerVideoRec::threadFeedScan()
1269 // This method is actually really simple - get frame from vdr,
1270 // spit it at the video chip, wait for a time. Most of the code here
1271 // is to get the wait right so that the scan occurs at the correct rate.
1273 ULONG direction = 0;
1274 ULONG baseFrameNumber = 0;
1275 ULONG iframeNumber = 0;
1276 ULONG iframeLength = 0;
1278 UINT amountReceived;
1282 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1283 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1284 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1286 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1289 int frameTimeOffset = 0; // Time in msec between frames
1290 int disp_msec = 0; // Time taken to display data
1291 int total_msec = 0; // Time taken to fetch data and display it
1294 if (state == S_FFWD) direction = 1; // and 0 for backward
1298 // Fetch I-frames until we get one that can be displayed in good time
1299 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1301 baseFrameNumber = currentFrameNumber;
1305 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1308 if (iframeNumber >= lengthFrames) return;
1309 // scan has got to the end of what we knew to be there before we started scanning
1311 baseFrameNumber = iframeNumber;
1312 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1314 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
1317 gettimeofday(&clock0, NULL);
1319 clock0 = timeGetTime();
1323 while (clock2.tv_sec != 0 &&
1324 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1325 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1327 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1329 logger->log("Player", Log::DEBUG, "XXX Got frame");
1331 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1333 if (!vdr->isConnected() || !amountReceived)
1335 if (threadBuffer) free(threadBuffer);
1341 gettimeofday(&clock1, NULL);
1342 if (clock2.tv_sec != 0)
1343 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1344 + (clock2.tv_usec - clock1.tv_usec) / 1000
1345 + frameTimeOffset - disp_msec;
1347 clock1 = timeGetTime();
1349 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1351 if (sleepTime < 0) sleepTime = 0;
1353 MILLISLEEP(sleepTime);
1354 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1356 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1357 video->displayIFrame(threadBuffer, videoLength);
1358 currentFrameNumber = iframeNumber;
1360 threadBuffer = NULL;
1363 gettimeofday(&clock2, NULL);
1364 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1365 + (clock2.tv_usec - clock0.tv_usec) / 1000
1367 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1368 + (clock2.tv_usec - clock1.tv_usec) / 1000
1371 clock2 = timeGetTime();
1372 total_msec = clock2 - clock0 - sleepTime;
1373 disp_msec = clock2 - clock1 - sleepTime;
1375 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1379 void PlayerVideoRec::threadPostStopCleanup()
1384 threadBuffer = NULL;
1388 // ----------------------------------- Dev
1391 void PlayerVideoRec::test1()
1393 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1396 void PlayerVideoRec::test2()
1398 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");