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/>.
24 #include "demuxervdr.h"
25 #include "demuxerts.h"
27 #include "messagequeue.h"
30 #include "dvbsubtitles.h"
31 #include "osdreceiver.h"
35 #include "playervideorec.h"
37 static const char* TAG = "PlayerVideoRec";
39 #define USER_RESPONSE_TIME 500 // Milliseconds
41 // ----------------------------------- Called from outside, one offs or info funcs
43 PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, MessageReceiver* tmessageReceiver, OSDReceiver* tosdReceiver)
44 : vfeed(this), afeed(this), tfeed(this),
45 osdReceiver(tosdReceiver), messageQueue(tmessageQueue), messageReceiver(tmessageReceiver)
47 audio = Audio::getInstance();
48 video = Video::getInstance();
49 logger = LogNT::getInstance();
50 vdr = VDR::getInstance();
55 PlayerVideoRec::~PlayerVideoRec()
57 if (initted) shutdown();
60 int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
62 if (initted) return 0;
63 is_pesrecording = p_isPesRecording;
64 fps = framespersecond;
66 demuxer = new DemuxerVDR();
68 demuxer = new DemuxerTS();
69 if (!demuxer) return 0;
70 subtitles = new DVBSubtitles(osdReceiver);
71 if (!subtitles) return 0;
73 teletext = new TeletextDecoderVBIEBU();
74 if (!teletext) return 0;
75 teletext->setRecordigMode(true);
76 unsigned int demux_video_size = 2097152;
77 unsigned int demux_audio_size = 524288;
78 if (video->supportsh264())
80 demux_video_size *= 5 * 2;
82 if (audio->maysupportAc3())
84 //demux_audio_size*=2;
87 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
89 logger->error(TAG, "Demuxer failed to init");
99 Config::getInstance()->getInt("subtitles", "default", subDefault);
101 turnSubtitlesOn(true);
103 turnSubtitlesOn(false);
109 int PlayerVideoRec::shutdown()
111 if (!initted) return 0;
125 void PlayerVideoRec::threadStart()
127 playerThreadMutex.lock();
128 threadReqQuit = false;
129 playerThread = std::thread([this]
131 playerThreadMutex.lock();
132 playerThreadMutex.unlock();
135 playerThreadMutex.unlock();
138 void PlayerVideoRec::threadStop()
140 playerThreadMutex.lock();
142 if (!playerThread.joinable()) // restartAtFrame sometimes calls threadStop when it's not running
144 playerThreadMutex.unlock();
148 threadReqQuit = true;
149 playerThreadCond.notify_one();
150 playerThreadMutex.unlock();
154 void PlayerVideoRec::setStartFrame(ULONG startFrame)
156 ULONG nextiframeNumber;
161 // newFrame could be anywhere, go forwards to next I-Frame
162 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
164 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
165 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
167 logger->debug(TAG, "setStartFrame {} {} {}", startFrame, nextiframeNumber,iframeNumber);
168 currentFrameNumber = iframeNumber;
171 void PlayerVideoRec::setLengthBytes(ULLONG length)
173 lengthBytes = length;
174 logger->debug(TAG, "Player has received length bytes of {}", lengthBytes);
177 void PlayerVideoRec::setLengthFrames(ULONG length)
179 lengthFrames = length;
180 logger->debug(TAG, "Player has received length frames of {}", lengthFrames);
183 ULONG PlayerVideoRec::getLengthFrames()
188 ULONG PlayerVideoRec::getCurrentFrameNum()
190 if (startup) return 0;
195 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
199 return currentFrameNumber;
201 return 0; // shouldn't happen
205 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
207 return demuxer->getmpAudioChannels();
210 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
212 return demuxer->getac3AudioChannels();
215 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
217 return demuxer->getSubtitleChannels();
220 int PlayerVideoRec::getCurrentAudioChannel()
223 return demuxer->getselAudioChannel();
225 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
228 int PlayerVideoRec::getCurrentSubtitleChannel()
231 return demuxer->getselSubtitleChannel();
233 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
236 void PlayerVideoRec::setSubtitleChannel(int newChannel)
239 demuxer->setDVBSubtitleStream(newChannel);
241 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
244 int *PlayerVideoRec::getTeletxtSubtitlePages()
246 return teletext->getSubtitlePages();
249 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
253 demuxer->setAudioStream(newChannel);
258 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
263 bool PlayerVideoRec::toggleSubtitles()
265 if (!subtitlesShowing)
267 subtitlesShowing = true;
272 subtitlesShowing = false;
275 return subtitlesShowing;
278 void PlayerVideoRec::turnSubtitlesOn(bool ison)
282 subtitlesShowing = true;
287 subtitlesShowing = false;
292 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
294 subtitles->setOSDMenuVisibility(visible);
297 Channel * PlayerVideoRec::getDemuxerChannel()
299 if (!is_pesrecording)
301 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
303 return NULL; //Should not happen!
307 // ----------------------------------- Externally called events
309 void PlayerVideoRec::play()
311 if (!initted) return;
312 if (state == S_PLAY) return;
315 bool doUnlock = false;
316 if (state == S_PAUSE_P) doUnlock = true;
318 if (doUnlock) stateMutex.unlock();
321 void PlayerVideoRec::playpause()
323 if (!initted) return;
326 bool doUnlock = false;
330 switchState(S_PAUSE_P);
334 if (state == S_PAUSE_P) doUnlock = true;
337 if (doUnlock) stateMutex.unlock();
340 void PlayerVideoRec::stop()
342 if (!initted) return;
343 if (state == S_STOP) return;
345 logger->debug(TAG, "Stop called lock");
350 void PlayerVideoRec::pause()
352 if (!initted) return;
355 if ((state == S_FFWD) || (state == S_FBWD))
357 switchState(S_PAUSE_I);
359 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
365 switchState(S_PAUSE_P);
371 void PlayerVideoRec::fastForward()
373 if (!initted) return;
381 case 4: ifactor = 8; break;
382 case 8: ifactor = 16; break;
383 case 16: ifactor = 32; break;
384 case 32: ifactor = 4; break;
395 void PlayerVideoRec::fastBackward()
397 if (!initted) return;
405 case 4: ifactor = 8; break;
406 case 8: ifactor = 16; break;
407 case 16: ifactor = 32; break;
408 case 32: ifactor = 4; break;
419 void PlayerVideoRec::jumpToPercent(double percent)
422 logger->debug(TAG, "JUMP TO {}%", percent);
423 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
424 switchState(S_JUMP, newFrame);
425 // stateMutex.unlock(); - let thread unlock this
428 void PlayerVideoRec::jumpToMark(int mark)
431 logger->debug(TAG, "JUMP TO MARK {}%", mark);
432 switchState(S_JUMP, mark);
433 // stateMutex.unlock(); - let thread unlock this
436 void PlayerVideoRec::jumpToFrameP(int newFrame)
439 logger->debug(TAG, "JUMP TO FRAME AND PAUSE {}", newFrame);
440 switchState(S_JUMP_PI, newFrame);
444 void PlayerVideoRec::skipForward(int seconds)
447 logger->debug(TAG, "SKIP FORWARD {} SECONDS", seconds);
448 ULONG newFrame = getCurrentFrameNum();
449 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
450 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
451 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
452 else switchState(S_JUMP, newFrame);
453 // stateMutex.unlock(); - let thread unlock this
456 void PlayerVideoRec::skipBackward(int seconds)
459 logger->debug(TAG, "SKIP BACKWARD {} SECONDS", seconds);
460 long newFrame = getCurrentFrameNum();
461 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
462 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
463 if (newFrame < 0) newFrame = 0;
464 switchState(S_JUMP, newFrame);
465 // stateMutex.unlock(); - let thread unlock this
468 // ----------------------------------- Implementations called events
470 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
472 if (!initted) return;
474 logger->debug(TAG, "Switch state from {} to {}", state, toState);
476 switch(state) // current state selector
478 case S_PLAY: // from S_PLAY -----------------------------------
482 case S_PLAY: // to S_PLAY
486 case S_PAUSE_P: // to S_PAUSE_P
488 #ifdef VOMP_PLATFORM_RASPBERRY
489 vfeed.stop(); // can't vfeed during pause
498 case S_PAUSE_I: // to S_PAUSE_I
503 case S_FFWD: // to S_FFWD
505 currentFrameNumber = getCurrentFrameNum();
506 audio->systemMuteOn();
517 case S_FBWD: // to S_FBWD
519 currentFrameNumber = getCurrentFrameNum();
520 audio->systemMuteOn();
531 case S_STOP: // to S_STOP
547 case S_JUMP: // to S_JUMP
549 restartAtFrame(jumpFrame);
552 case S_JUMP_PI: // to S_JUMP_PI
554 audio->systemMuteOn();
564 restartAtFramePI(jumpFrame);
569 FALLTHROUGH // keep compiler happy (all posibilities return)
570 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
574 case S_PLAY: // to S_PLAY
578 subtitles->unPause();
580 #ifdef VOMP_PLATFORM_RASPBERRY
587 case S_PAUSE_P: // to S_PAUSE_P
591 case S_PAUSE_I: // to S_PAUSE_I
595 case S_FFWD: // to S_FFWD
597 currentFrameNumber = getCurrentFrameNum();
598 audio->systemMuteOn();
610 case S_FBWD: // to S_FBWD
612 currentFrameNumber = getCurrentFrameNum();
613 audio->systemMuteOn();
625 case S_STOP: // to S_STOP
638 audio->systemMuteOff();
642 case S_JUMP: // to S_JUMP
645 audio->systemMuteOn();
647 restartAtFrame(jumpFrame);
650 case S_JUMP_PI: // to S_JUMP_PI
652 audio->systemMuteOn();
663 restartAtFramePI(jumpFrame);
668 FALLTHROUGH // keep compiler happy (all posibilities return)
669 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
673 case S_PLAY: // to S_PLAY
676 restartAtFrame(currentFrameNumber);
679 case S_PAUSE_P: // to S_PAUSE_P
683 case S_PAUSE_I: // to S_PAUSE_I
687 case S_FFWD: // to S_FFWD
693 case S_FBWD: // to S_FBWD
699 case S_STOP: // to S_STOP
706 audio->systemMuteOff();
710 case S_JUMP: // to S_JUMP
713 restartAtFrame(jumpFrame);
716 case S_JUMP_PI: // to S_JUMP_PI
718 restartAtFramePI(jumpFrame);
723 FALLTHROUGH // keep compiler happy (all posibilities return)
724 case S_FFWD: // from S_FFWD -----------------------------------
728 case S_PLAY: // to S_PLAY
731 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
732 if (stepback < currentFrameNumber)
733 currentFrameNumber -= stepback;
735 currentFrameNumber = 0;
736 restartAtFrame(currentFrameNumber);
739 case S_PAUSE_P: // to S_PAUSE_P
744 case S_PAUSE_I: // to S_PAUSE_I
750 case S_FFWD: // to S_FFWD
754 case S_FBWD: // to S_FBWD
761 case S_STOP: // to S_STOP
772 case S_JUMP: // to S_JUMP
775 restartAtFrame(jumpFrame);
778 case S_JUMP_PI: // to S_JUMP_PI
782 restartAtFramePI(jumpFrame);
787 FALLTHROUGH // keep compiler happy (all posibilities return)
788 case S_FBWD: // from S_FBWD -----------------------------------
792 case S_PLAY: // to S_PLAY
795 restartAtFrame(currentFrameNumber);
798 case S_PAUSE_P: // to S_PAUSE_P
803 case S_PAUSE_I: // to S_PAUSE_I
809 case S_FFWD: // to S_FFWD
816 case S_FBWD: // to S_FBWD
820 case S_STOP: // to S_STOP
831 case S_JUMP: // to S_JUMP
834 restartAtFrame(jumpFrame);
837 case S_JUMP_PI: // to S_JUMP_PI
841 restartAtFramePI(jumpFrame);
846 FALLTHROUGH // keep compiler happy (all posibilities return)
847 case S_STOP: // from S_STOP -----------------------------------
851 case S_PLAY: // to S_PLAY
856 audio->setStreamType(Audio::MPEG2_PES);
857 audio->systemMuteOff();
860 // FIXME use restartAtFrame here?
861 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
862 demuxer->setFrameNum(currentFrameNumber);
867 logger->debug(TAG, "Immediate play");
878 case S_PAUSE_P: // to S_PAUSE_P
882 case S_PAUSE_I: // to S_PAUSE_I
886 case S_FFWD: // to S_FFWD
890 case S_FBWD: // to S_FBWD
894 case S_STOP: // to S_STOP
898 case S_JUMP: // to S_JUMP
902 case S_JUMP_PI: // to S_JUMP_PI
908 // case S_JUMP cannot be a start state because it auto flips to play
909 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
913 // ----------------------------------- Internal functions
915 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
925 audio->setStreamType(Audio::MPEG2_PES);
928 currentFrameNumber = newFrame;
929 demuxer->setFrameNum(newFrame);
939 audio->systemMuteOff();
944 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
947 ULONG nextiframeNumber;
955 // newFrame could be anywhere, go forwards to next I-Frame
956 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
958 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
959 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
961 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
962 if (!vdr->isConnected())
964 if (buffer) free(buffer);
969 videoLength = demuxer->stripAudio(buffer, amountReceived);
970 video->displayIFrame(buffer, videoLength);
971 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
973 currentFrameNumber = iframeNumber;
977 void PlayerVideoRec::doConnectionLost()
979 logger->debug(TAG, "Connection lost, sending message");
980 Message* m = new Message();
981 m->to = messageReceiver;
983 m->message = Message::PLAYER_EVENT;
984 m->parameter = PlayerVideoRec::CONNECTION_LOST;
985 messageQueue->postMessage(m);
988 // ----------------------------------- Callback
990 void PlayerVideoRec::call(void* caller)
992 if (caller == demuxer)
994 logger->debug(TAG, "Callback from demuxer");
996 if (video->getTVsize() == Video::ASPECT4X3)
998 logger->debug(TAG, "TV is 4:3, ignoring aspect switching");
1003 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
1004 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
1006 logger->debug(TAG, "Demuxer said video is 4:3 aspect, switching TV");
1007 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
1009 Message* m = new Message();
1011 m->to = messageReceiver;
1012 m->message = Message::PLAYER_EVENT;
1013 m->parameter = PlayerVideoRec::ASPECT43;
1014 messageQueue->postMessage(m);
1016 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1018 logger->debug(TAG, "Demuxer said video is 16:9 aspect, switching TV");
1019 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1021 Message* m = new Message();
1023 m->to = messageReceiver;
1024 m->message = Message::PLAYER_EVENT;
1025 m->parameter = PlayerVideoRec::ASPECT169;
1026 messageQueue->postMessage(m);
1030 logger->debug(TAG, "Demuxer said video is something else... setting it anyway");
1031 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1039 videoStartup = false;
1043 stateMutex.unlock();
1046 playerThreadCond.notify_one();
1050 // ----------------------------------- Feed thread
1052 void PlayerVideoRec::threadMethod()
1054 // this method used to be simple, the only thing it does
1055 // is farm out to threadFeed Live/Play/Scan
1056 // All the guff is to support scan hitting one end
1058 if ((state == S_FFWD) || (state == S_FBWD))
1061 if (video->PTSIFramePlayback()) hitEnd = threadPTSFeedScan();
1062 else hitEnd = threadFeedScan();
1064 if (!hitEnd) return; // thread wants to exit normally
1066 if (state == S_FFWD) // scan hit the end. stop
1068 if (threadReqQuit) return;
1069 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1070 m->to = messageReceiver;
1072 m->message = Message::PLAYER_EVENT;
1073 m->parameter = STOP_PLAYBACK;
1074 logger->debug(TAG, "Posting message to {}...", (void*)messageQueue);
1075 messageQueue->postMessage(m);
1076 logger->debug(TAG, "Message posted...");
1079 // if execution gets to here, threadFeedScan hit the start, go to play mode
1082 audio->setStreamType(Audio::MPEG2_PES);
1085 demuxer->setFrameNum(currentFrameNumber);
1086 videoStartup = true;
1093 audio->systemMuteOff();
1097 if (state == S_PLAY) threadFeedPlay();
1100 void PlayerVideoRec::threadFeedPlay()
1102 ULLONG feedPosition;
1103 UINT thisRead, writeLength, thisWrite, askFor;
1104 time_t lastRescan = time(NULL);
1106 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1107 if (!vdr->isConnected()) { doConnectionLost(); return; }
1108 logger->debug(TAG, "startFeedPlay: wantedframe {} goto {}", currentFrameNumber, feedPosition);
1110 Buffer threadBuffer;
1112 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
1120 if (threadReqQuit) return;
1122 // If we havn't rescanned for a while..
1123 if ((lastRescan + 60) < time(NULL))
1125 lengthBytes = vdr->rescanRecording(&lengthFrames);
1126 if (!vdr->isConnected()) { doConnectionLost(); return; }
1127 logger->debug(TAG, "Rescanned and reset length: {}", lengthBytes);
1128 lastRescan = time(NULL);
1131 if (feedPosition >= lengthBytes) break; // finished playback
1135 if (startupBlockSize > lengthBytes)
1136 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1138 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1142 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1143 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1147 //logger->log("Player", Log::DEBUG, "Get Block in");
1149 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
1150 //logger->log("Player", Log::DEBUG, "Get Block out");
1152 feedPosition += thisRead;
1154 if (!vdr->isConnected())
1160 if (threadBuffer.isNull()) break;
1164 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
1165 demuxer->setAudioStream(a_stream);
1166 logger->debug(TAG, "Startup Audio stream chosen {:#x}", a_stream);
1170 if (threadReqQuit) return;
1172 while(writeLength < thisRead)
1174 //logger->log("Player", Log::DEBUG, "Put in");
1175 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
1176 //logger->log("Player", Log::DEBUG, "Put out");
1177 writeLength += thisWrite;
1181 // demuxer is full and can't take anymore
1183 if (threadReqQuit) { ul.unlock(); return; }
1184 playerThreadCond.wait(ul);
1188 if (threadReqQuit) return;
1191 threadBuffer.release();
1195 logger->debug(TAG, "Recording playback ends");
1197 if (videoStartup) // oh woe. there never was a stream, I was conned!
1199 videoStartup = false;
1200 stateMutex.unlock();
1201 MILLISLEEP(500); // I think this will solve a race
1204 if (threadReqQuit) return;
1206 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1207 m->to = messageReceiver;
1209 m->message = Message::PLAYER_EVENT;
1210 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1211 logger->debug(TAG, "Posting message to {}...", (void*)messageQueue);
1212 messageQueue->postMessage(m);
1216 bool PlayerVideoRec::threadPTSFeedScan()
1218 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1220 ULONG direction = 0;
1222 ULONG baseFrameNumber = 0;
1223 ULONG iframeNumber = 0;
1224 ULONG iframeLength = 0;
1225 ULONG currentfeedFrameNumber=currentFrameNumber;
1226 ULONG firstFrameNumber=currentFrameNumber;
1228 UINT amountReceived;
1233 int frameTimeOffset = 0; // Time in msec between frames
1235 Buffer threadBuffer;
1237 if (state == S_FFWD)
1239 direction = 1; // and 0 for backward
1242 video->EnterIframePlayback();
1246 baseFrameNumber = currentfeedFrameNumber;
1248 if (threadReqQuit) return false;
1250 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1253 if (iframeNumber >= lengthFrames) return true;
1254 // scan has got to the end of what we knew to be there before we started scanning
1256 baseFrameNumber = iframeNumber;
1258 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1260 logger->debug(TAG, "XXX Got frame");
1262 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1264 if (!vdr->isConnected())
1270 if (threadReqQuit) return false;
1272 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1273 demuxer->changeTimes(threadBuffer.ucharp(), videoLength, playtime);
1275 while (!video->displayIFrame(threadBuffer.ucharp(), videoLength)) // the device might block
1278 if (threadReqQuit) return false;
1281 ULLONG cur_time=video->getCurrentTimestamp();
1282 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1283 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1285 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1290 playtime +=frameTimeOffset;
1291 currentfeedFrameNumber = iframeNumber;
1293 ULLONG cur_time=video->getCurrentTimestamp();
1294 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1295 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1297 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1301 threadBuffer.release();
1307 bool PlayerVideoRec::threadFeedScan()
1309 // This method is actually really simple - get frame from vdr,
1310 // spit it at the video chip, wait for a time. Most of the code here
1311 // is to get the wait right so that the scan occurs at the correct rate.
1313 ULONG direction = 0;
1314 ULONG baseFrameNumber = 0;
1315 ULONG iframeNumber = 0;
1316 ULONG iframeLength = 0;
1318 UINT amountReceived;
1322 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1323 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1324 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1326 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1329 int frameTimeOffset = 0; // Time in msec between frames
1330 int disp_msec = 0; // Time taken to display data
1331 int total_msec = 0; // Time taken to fetch data and display it
1334 if (state == S_FFWD) direction = 1; // and 0 for backward
1336 Buffer threadBuffer;
1340 // Fetch I-frames until we get one that can be displayed in good time
1341 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1343 baseFrameNumber = currentFrameNumber;
1346 if (threadReqQuit) return false;
1348 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1351 if (iframeNumber >= lengthFrames) return true;
1352 // scan has got to the end of what we knew to be there before we started scanning
1354 baseFrameNumber = iframeNumber;
1355 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1357 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
1360 gettimeofday(&clock0, NULL);
1362 clock0 = timeGetTime();
1366 while (clock2.tv_sec != 0 &&
1367 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1368 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1370 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1372 logger->debug(TAG, "XXX Got frame");
1374 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1376 if (!vdr->isConnected() || !amountReceived)
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;
1394 if (threadReqQuit) return false;
1395 MILLISLEEP(sleepTime);
1396 logger->debug(TAG, "XXX Slept for {}", sleepTime);
1398 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1399 video->displayIFrame(threadBuffer.ucharp(), videoLength);
1400 currentFrameNumber = iframeNumber;
1401 threadBuffer.release();
1404 gettimeofday(&clock2, NULL);
1405 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1406 + (clock2.tv_usec - clock0.tv_usec) / 1000
1408 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1409 + (clock2.tv_usec - clock1.tv_usec) / 1000
1412 clock2 = timeGetTime();
1413 total_msec = clock2 - clock0 - sleepTime;
1414 disp_msec = clock2 - clock1 - sleepTime;
1416 logger->debug(TAG, "XXX disp_msec = {} total_msec = {}", disp_msec, total_msec);
1420 // ----------------------------------- Dev
1423 void PlayerVideoRec::test1()
1425 logger->debug(TAG, "PLAYER TEST 1");
1428 void PlayerVideoRec::test2()
1430 logger->debug(TAG, "PLAYER TEST 2");