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"
34 #include "playervideorec.h"
36 static const char* TAG = "PlayerVideoRec";
38 #define USER_RESPONSE_TIME 500 // Milliseconds
40 // ----------------------------------- Called from outside, one offs or info funcs
42 PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
43 : vfeed(this), afeed(this), tfeed(this),
44 osdReceiver(tosdReceiver), messageQueue(tmessageQueue), messageReceiver(tmessageReceiver)
46 audio = Audio::getInstance();
47 video = Video::getInstance();
48 logger = LogNT::getInstance();
49 vdr = VDR::getInstance();
54 PlayerVideoRec::~PlayerVideoRec()
56 if (initted) shutdown();
59 int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
61 if (initted) return 0;
62 is_pesrecording = p_isPesRecording;
63 fps = framespersecond;
65 demuxer = new DemuxerVDR();
67 demuxer = new DemuxerTS();
68 if (!demuxer) return 0;
69 subtitles = new DVBSubtitles(osdReceiver);
70 if (!subtitles) return 0;
72 teletext = new TeletextDecoderVBIEBU();
73 if (!teletext) return 0;
74 teletext->setRecordigMode(true);
75 unsigned int demux_video_size = 2097152;
76 unsigned int demux_audio_size = 524288;
77 if (video->supportsh264())
79 demux_video_size *= 5 * 2;
81 if (audio->maysupportAc3())
83 //demux_audio_size*=2;
86 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
88 logger->error(TAG, "Demuxer failed to init");
97 if (Control::getInstance()->getSubDefault())
98 turnSubtitlesOn(true);
100 turnSubtitlesOn(false);
106 int PlayerVideoRec::shutdown()
108 if (!initted) return 0;
122 void PlayerVideoRec::threadStart()
124 playerThreadMutex.lock();
125 threadReqQuit = false;
126 playerThread = std::thread([this]
128 playerThreadMutex.lock();
129 playerThreadMutex.unlock();
132 playerThreadMutex.unlock();
135 void PlayerVideoRec::threadStop()
137 playerThreadMutex.lock();
138 threadReqQuit = true;
139 playerThreadCond.notify_one();
140 playerThreadMutex.unlock();
144 void PlayerVideoRec::setStartFrame(ULONG startFrame)
146 ULONG nextiframeNumber;
151 // newFrame could be anywhere, go forwards to next I-Frame
152 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
154 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
155 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
157 logger->debug(TAG, "setStartFrame {} {} {}", startFrame, nextiframeNumber,iframeNumber);
158 currentFrameNumber = iframeNumber;
161 void PlayerVideoRec::setLengthBytes(ULLONG length)
163 lengthBytes = length;
164 logger->debug(TAG, "Player has received length bytes of {}", lengthBytes);
167 void PlayerVideoRec::setLengthFrames(ULONG length)
169 lengthFrames = length;
170 logger->debug(TAG, "Player has received length frames of {}", lengthFrames);
173 ULONG PlayerVideoRec::getLengthFrames()
178 ULONG PlayerVideoRec::getCurrentFrameNum()
180 if (startup) return 0;
185 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
189 return currentFrameNumber;
191 return 0; // shouldn't happen
195 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
197 return demuxer->getmpAudioChannels();
200 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
202 return demuxer->getac3AudioChannels();
205 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
207 return demuxer->getSubtitleChannels();
210 int PlayerVideoRec::getCurrentAudioChannel()
213 return demuxer->getselAudioChannel();
215 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
218 int PlayerVideoRec::getCurrentSubtitleChannel()
221 return demuxer->getselSubtitleChannel();
223 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
226 void PlayerVideoRec::setSubtitleChannel(int newChannel)
229 demuxer->setDVBSubtitleStream(newChannel);
231 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
234 int *PlayerVideoRec::getTeletxtSubtitlePages()
236 return teletext->getSubtitlePages();
239 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
243 demuxer->setAudioStream(newChannel);
248 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
253 bool PlayerVideoRec::toggleSubtitles()
255 if (!subtitlesShowing)
257 subtitlesShowing = true;
262 subtitlesShowing = false;
265 return subtitlesShowing;
268 void PlayerVideoRec::turnSubtitlesOn(bool ison)
272 subtitlesShowing = true;
277 subtitlesShowing = false;
282 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
284 subtitles->setOSDMenuVisibility(visible);
287 Channel * PlayerVideoRec::getDemuxerChannel()
289 if (!is_pesrecording)
291 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
293 return NULL; //Should not happen!
297 // ----------------------------------- Externally called events
299 void PlayerVideoRec::play()
301 if (!initted) return;
302 if (state == S_PLAY) return;
305 bool doUnlock = false;
306 if (state == S_PAUSE_P) doUnlock = true;
308 if (doUnlock) stateMutex.unlock();
311 void PlayerVideoRec::playpause()
313 if (!initted) return;
316 bool doUnlock = false;
320 switchState(S_PAUSE_P);
324 if (state == S_PAUSE_P) doUnlock = true;
327 if (doUnlock) stateMutex.unlock();
330 void PlayerVideoRec::stop()
332 if (!initted) return;
333 if (state == S_STOP) return;
335 logger->debug(TAG, "Stop called lock");
340 void PlayerVideoRec::pause()
342 if (!initted) return;
345 if ((state == S_FFWD) || (state == S_FBWD))
347 switchState(S_PAUSE_I);
349 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
355 switchState(S_PAUSE_P);
361 void PlayerVideoRec::fastForward()
363 if (!initted) return;
371 case 4: ifactor = 8; break;
372 case 8: ifactor = 16; break;
373 case 16: ifactor = 32; break;
374 case 32: ifactor = 4; break;
385 void PlayerVideoRec::fastBackward()
387 if (!initted) return;
395 case 4: ifactor = 8; break;
396 case 8: ifactor = 16; break;
397 case 16: ifactor = 32; break;
398 case 32: ifactor = 4; break;
409 void PlayerVideoRec::jumpToPercent(double percent)
412 logger->debug(TAG, "JUMP TO {}%", percent);
413 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
414 switchState(S_JUMP, newFrame);
415 // stateMutex.unlock(); - let thread unlock this
418 void PlayerVideoRec::jumpToMark(int mark)
421 logger->debug(TAG, "JUMP TO MARK {}%", mark);
422 switchState(S_JUMP, mark);
423 // stateMutex.unlock(); - let thread unlock this
426 void PlayerVideoRec::jumpToFrameP(int newFrame)
429 logger->debug(TAG, "JUMP TO FRAME AND PAUSE {}", newFrame);
430 switchState(S_JUMP_PI, newFrame);
434 void PlayerVideoRec::skipForward(int seconds)
437 logger->debug(TAG, "SKIP FORWARD {} SECONDS", seconds);
438 ULONG newFrame = getCurrentFrameNum();
439 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
440 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
441 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
442 else switchState(S_JUMP, newFrame);
443 // stateMutex.unlock(); - let thread unlock this
446 void PlayerVideoRec::skipBackward(int seconds)
449 logger->debug(TAG, "SKIP BACKWARD {} SECONDS", seconds);
450 long newFrame = getCurrentFrameNum();
451 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
452 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
453 if (newFrame < 0) newFrame = 0;
454 switchState(S_JUMP, newFrame);
455 // stateMutex.unlock(); - let thread unlock this
458 // ----------------------------------- Implementations called events
460 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
462 if (!initted) return;
464 logger->debug(TAG, "Switch state from {} to {}", state, toState);
466 switch(state) // current state selector
468 case S_PLAY: // from S_PLAY -----------------------------------
472 case S_PLAY: // to S_PLAY
476 case S_PAUSE_P: // to S_PAUSE_P
478 #ifdef VOMP_PLATFORM_RASPBERRY
479 vfeed.stop(); // can't vfeed during pause
488 case S_PAUSE_I: // to S_PAUSE_I
493 case S_FFWD: // to S_FFWD
495 currentFrameNumber = getCurrentFrameNum();
496 audio->systemMuteOn();
507 case S_FBWD: // to S_FBWD
509 currentFrameNumber = getCurrentFrameNum();
510 audio->systemMuteOn();
521 case S_STOP: // to S_STOP
537 case S_JUMP: // to S_JUMP
539 restartAtFrame(jumpFrame);
542 case S_JUMP_PI: // to S_JUMP_PI
544 audio->systemMuteOn();
554 restartAtFramePI(jumpFrame);
559 FALLTHROUGH // keep compiler happy (all posibilities return)
560 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
564 case S_PLAY: // to S_PLAY
568 subtitles->unPause();
570 #ifdef VOMP_PLATFORM_RASPBERRY
577 case S_PAUSE_P: // to S_PAUSE_P
581 case S_PAUSE_I: // to S_PAUSE_I
585 case S_FFWD: // to S_FFWD
587 currentFrameNumber = getCurrentFrameNum();
588 audio->systemMuteOn();
600 case S_FBWD: // to S_FBWD
602 currentFrameNumber = getCurrentFrameNum();
603 audio->systemMuteOn();
615 case S_STOP: // to S_STOP
628 audio->systemMuteOff();
632 case S_JUMP: // to S_JUMP
635 audio->systemMuteOn();
637 restartAtFrame(jumpFrame);
640 case S_JUMP_PI: // to S_JUMP_PI
642 audio->systemMuteOn();
653 restartAtFramePI(jumpFrame);
658 FALLTHROUGH // keep compiler happy (all posibilities return)
659 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
663 case S_PLAY: // to S_PLAY
666 restartAtFrame(currentFrameNumber);
669 case S_PAUSE_P: // to S_PAUSE_P
673 case S_PAUSE_I: // to S_PAUSE_I
677 case S_FFWD: // to S_FFWD
683 case S_FBWD: // to S_FBWD
689 case S_STOP: // to S_STOP
696 audio->systemMuteOff();
700 case S_JUMP: // to S_JUMP
703 restartAtFrame(jumpFrame);
706 case S_JUMP_PI: // to S_JUMP_PI
708 restartAtFramePI(jumpFrame);
713 FALLTHROUGH // keep compiler happy (all posibilities return)
714 case S_FFWD: // from S_FFWD -----------------------------------
718 case S_PLAY: // to S_PLAY
721 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
722 if (stepback < currentFrameNumber)
723 currentFrameNumber -= stepback;
725 currentFrameNumber = 0;
726 restartAtFrame(currentFrameNumber);
729 case S_PAUSE_P: // to S_PAUSE_P
734 case S_PAUSE_I: // to S_PAUSE_I
740 case S_FFWD: // to S_FFWD
744 case S_FBWD: // to S_FBWD
751 case S_STOP: // to S_STOP
762 case S_JUMP: // to S_JUMP
765 restartAtFrame(jumpFrame);
768 case S_JUMP_PI: // to S_JUMP_PI
772 restartAtFramePI(jumpFrame);
777 FALLTHROUGH // keep compiler happy (all posibilities return)
778 case S_FBWD: // from S_FBWD -----------------------------------
782 case S_PLAY: // to S_PLAY
785 restartAtFrame(currentFrameNumber);
788 case S_PAUSE_P: // to S_PAUSE_P
793 case S_PAUSE_I: // to S_PAUSE_I
799 case S_FFWD: // to S_FFWD
806 case S_FBWD: // to S_FBWD
810 case S_STOP: // to S_STOP
821 case S_JUMP: // to S_JUMP
824 restartAtFrame(jumpFrame);
827 case S_JUMP_PI: // to S_JUMP_PI
831 restartAtFramePI(jumpFrame);
836 FALLTHROUGH // keep compiler happy (all posibilities return)
837 case S_STOP: // from S_STOP -----------------------------------
841 case S_PLAY: // to S_PLAY
846 audio->setStreamType(Audio::MPEG2_PES);
847 audio->systemMuteOff();
850 // FIXME use restartAtFrame here?
851 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
852 demuxer->setFrameNum(currentFrameNumber);
857 logger->debug(TAG, "Immediate play");
868 case S_PAUSE_P: // to S_PAUSE_P
872 case S_PAUSE_I: // to S_PAUSE_I
876 case S_FFWD: // to S_FFWD
880 case S_FBWD: // to S_FBWD
884 case S_STOP: // to S_STOP
888 case S_JUMP: // to S_JUMP
892 case S_JUMP_PI: // to S_JUMP_PI
898 // case S_JUMP cannot be a start state because it auto flips to play
899 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
903 // ----------------------------------- Internal functions
905 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
915 audio->setStreamType(Audio::MPEG2_PES);
918 currentFrameNumber = newFrame;
919 demuxer->setFrameNum(newFrame);
929 audio->systemMuteOff();
934 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
937 ULONG nextiframeNumber;
945 // newFrame could be anywhere, go forwards to next I-Frame
946 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
948 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
949 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
951 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
952 if (!vdr->isConnected())
954 if (buffer) free(buffer);
959 videoLength = demuxer->stripAudio(buffer, amountReceived);
960 video->displayIFrame(buffer, videoLength);
961 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
963 currentFrameNumber = iframeNumber;
967 void PlayerVideoRec::doConnectionLost()
969 logger->debug(TAG, "Connection lost, sending message");
970 Message* m = new Message();
971 m->to = messageReceiver;
973 m->message = Message::PLAYER_EVENT;
974 m->parameter = PlayerVideoRec::CONNECTION_LOST;
975 messageQueue->postMessage(m);
978 // ----------------------------------- Callback
980 void PlayerVideoRec::call(void* caller)
982 if (caller == demuxer)
984 logger->debug(TAG, "Callback from demuxer");
986 if (video->getTVsize() == Video::ASPECT4X3)
988 logger->debug(TAG, "TV is 4:3, ignoring aspect switching");
993 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
994 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
996 logger->debug(TAG, "Demuxer said video is 4:3 aspect, switching TV");
997 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
999 Message* m = new Message();
1001 m->to = messageReceiver;
1002 m->message = Message::PLAYER_EVENT;
1003 m->parameter = PlayerVideoRec::ASPECT43;
1004 messageQueue->postMessage(m);
1006 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1008 logger->debug(TAG, "Demuxer said video is 16:9 aspect, switching TV");
1009 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1011 Message* m = new Message();
1013 m->to = messageReceiver;
1014 m->message = Message::PLAYER_EVENT;
1015 m->parameter = PlayerVideoRec::ASPECT169;
1016 messageQueue->postMessage(m);
1020 logger->debug(TAG, "Demuxer said video is something else... setting it anyway");
1021 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1029 videoStartup = false;
1033 stateMutex.unlock();
1036 playerThreadCond.notify_one();
1040 // ----------------------------------- Feed thread
1042 void PlayerVideoRec::threadMethod()
1044 // this method used to be simple, the only thing it does
1045 // is farm out to threadFeed Live/Play/Scan
1046 // All the guff is to support scan hitting one end
1048 if ((state == S_FFWD) || (state == S_FBWD))
1051 if (video->PTSIFramePlayback()) hitEnd = threadPTSFeedScan();
1052 else hitEnd = threadFeedScan();
1054 if (!hitEnd) return; // thread wants to exit normally
1056 if (state == S_FFWD) // scan hit the end. stop
1058 if (threadReqQuit) return;
1059 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1060 m->to = messageReceiver;
1062 m->message = Message::PLAYER_EVENT;
1063 m->parameter = STOP_PLAYBACK;
1064 logger->debug(TAG, "Posting message to {}...", (void*)messageQueue);
1065 messageQueue->postMessage(m);
1066 logger->debug(TAG, "Message posted...");
1069 // if execution gets to here, threadFeedScan hit the start, go to play mode
1072 audio->setStreamType(Audio::MPEG2_PES);
1075 demuxer->setFrameNum(currentFrameNumber);
1076 videoStartup = true;
1083 audio->systemMuteOff();
1087 if (state == S_PLAY) threadFeedPlay();
1090 void PlayerVideoRec::threadFeedPlay()
1092 ULLONG feedPosition;
1093 UINT thisRead, writeLength, thisWrite, askFor;
1094 time_t lastRescan = time(NULL);
1096 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1097 if (!vdr->isConnected()) { doConnectionLost(); return; }
1098 logger->debug(TAG, "startFeedPlay: wantedframe {} goto {}", currentFrameNumber, feedPosition);
1100 Buffer threadBuffer;
1102 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
1110 if (threadReqQuit) return;
1112 // If we havn't rescanned for a while..
1113 if ((lastRescan + 60) < time(NULL))
1115 lengthBytes = vdr->rescanRecording(&lengthFrames);
1116 if (!vdr->isConnected()) { doConnectionLost(); return; }
1117 logger->debug(TAG, "Rescanned and reset length: {}", lengthBytes);
1118 lastRescan = time(NULL);
1121 if (feedPosition >= lengthBytes) break; // finished playback
1125 if (startupBlockSize > lengthBytes)
1126 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1128 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1132 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1133 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1137 //logger->log("Player", Log::DEBUG, "Get Block in");
1139 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
1140 //logger->log("Player", Log::DEBUG, "Get Block out");
1142 feedPosition += thisRead;
1144 if (!vdr->isConnected())
1150 if (threadBuffer.isNull()) break;
1154 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
1155 demuxer->setAudioStream(a_stream);
1156 logger->debug(TAG, "Startup Audio stream chosen {:#x}", a_stream);
1160 if (threadReqQuit) return;
1162 while(writeLength < thisRead)
1164 //logger->log("Player", Log::DEBUG, "Put in");
1165 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
1166 //logger->log("Player", Log::DEBUG, "Put out");
1167 writeLength += thisWrite;
1171 // demuxer is full and can't take anymore
1173 if (threadReqQuit) { ul.unlock(); return; }
1174 playerThreadCond.wait(ul);
1178 if (threadReqQuit) return;
1181 threadBuffer.release();
1185 logger->debug(TAG, "Recording playback ends");
1187 if (videoStartup) // oh woe. there never was a stream, I was conned!
1189 videoStartup = false;
1190 stateMutex.unlock();
1191 MILLISLEEP(500); // I think this will solve a race
1194 if (threadReqQuit) return;
1196 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1197 m->to = messageReceiver;
1199 m->message = Message::PLAYER_EVENT;
1200 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1201 logger->debug(TAG, "Posting message to {}...", (void*)messageQueue);
1202 messageQueue->postMessage(m);
1206 bool PlayerVideoRec::threadPTSFeedScan()
1208 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1210 ULONG direction = 0;
1212 ULONG baseFrameNumber = 0;
1213 ULONG iframeNumber = 0;
1214 ULONG iframeLength = 0;
1215 ULONG currentfeedFrameNumber=currentFrameNumber;
1216 ULONG firstFrameNumber=currentFrameNumber;
1218 UINT amountReceived;
1223 int frameTimeOffset = 0; // Time in msec between frames
1225 Buffer threadBuffer;
1227 if (state == S_FFWD)
1229 direction = 1; // and 0 for backward
1232 video->EnterIframePlayback();
1236 baseFrameNumber = currentfeedFrameNumber;
1238 if (threadReqQuit) return false;
1240 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1243 if (iframeNumber >= lengthFrames) return true;
1244 // scan has got to the end of what we knew to be there before we started scanning
1246 baseFrameNumber = iframeNumber;
1248 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1250 logger->debug(TAG, "XXX Got frame");
1252 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1254 if (!vdr->isConnected())
1260 if (threadReqQuit) return false;
1262 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1263 demuxer->changeTimes(threadBuffer.ucharp(), videoLength, playtime);
1265 while (!video->displayIFrame(threadBuffer.ucharp(), videoLength)) // the device might block
1268 if (threadReqQuit) return false;
1271 ULLONG cur_time=video->getCurrentTimestamp();
1272 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1273 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1275 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1280 playtime +=frameTimeOffset;
1281 currentfeedFrameNumber = iframeNumber;
1283 ULLONG cur_time=video->getCurrentTimestamp();
1284 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1285 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1287 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1291 threadBuffer.release();
1297 bool PlayerVideoRec::threadFeedScan()
1299 // This method is actually really simple - get frame from vdr,
1300 // spit it at the video chip, wait for a time. Most of the code here
1301 // is to get the wait right so that the scan occurs at the correct rate.
1303 ULONG direction = 0;
1304 ULONG baseFrameNumber = 0;
1305 ULONG iframeNumber = 0;
1306 ULONG iframeLength = 0;
1308 UINT amountReceived;
1312 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1313 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1314 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1316 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1319 int frameTimeOffset = 0; // Time in msec between frames
1320 int disp_msec = 0; // Time taken to display data
1321 int total_msec = 0; // Time taken to fetch data and display it
1324 if (state == S_FFWD) direction = 1; // and 0 for backward
1326 Buffer threadBuffer;
1330 // Fetch I-frames until we get one that can be displayed in good time
1331 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1333 baseFrameNumber = currentFrameNumber;
1336 if (threadReqQuit) return false;
1338 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1341 if (iframeNumber >= lengthFrames) return true;
1342 // scan has got to the end of what we knew to be there before we started scanning
1344 baseFrameNumber = iframeNumber;
1345 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1347 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
1350 gettimeofday(&clock0, NULL);
1352 clock0 = timeGetTime();
1356 while (clock2.tv_sec != 0 &&
1357 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1358 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1360 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1362 logger->debug(TAG, "XXX Got frame");
1364 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1366 if (!vdr->isConnected() || !amountReceived)
1373 gettimeofday(&clock1, NULL);
1374 if (clock2.tv_sec != 0)
1375 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1376 + (clock2.tv_usec - clock1.tv_usec) / 1000
1377 + frameTimeOffset - disp_msec;
1379 clock1 = timeGetTime();
1381 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1383 if (sleepTime < 0) sleepTime = 0;
1384 if (threadReqQuit) return false;
1385 MILLISLEEP(sleepTime);
1386 logger->debug(TAG, "XXX Slept for {}", sleepTime);
1388 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1389 video->displayIFrame(threadBuffer.ucharp(), videoLength);
1390 currentFrameNumber = iframeNumber;
1391 threadBuffer.release();
1394 gettimeofday(&clock2, NULL);
1395 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1396 + (clock2.tv_usec - clock0.tv_usec) / 1000
1398 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1399 + (clock2.tv_usec - clock1.tv_usec) / 1000
1402 clock2 = timeGetTime();
1403 total_msec = clock2 - clock0 - sleepTime;
1404 disp_msec = clock2 - clock1 - sleepTime;
1406 logger->debug(TAG, "XXX disp_msec = {} total_msec = {}", disp_msec, total_msec);
1410 // ----------------------------------- Dev
1413 void PlayerVideoRec::test1()
1415 logger->debug(TAG, "PLAYER TEST 1");
1418 void PlayerVideoRec::test2()
1420 logger->debug(TAG, "PLAYER TEST 2");