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();
139 if (!playerThread.joinable()) // restartAtFrame sometimes calls threadStop when it's not running
141 playerThreadMutex.unlock();
145 threadReqQuit = true;
146 playerThreadCond.notify_one();
147 playerThreadMutex.unlock();
151 void PlayerVideoRec::setStartFrame(ULONG startFrame)
153 ULONG nextiframeNumber;
158 // newFrame could be anywhere, go forwards to next I-Frame
159 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
161 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
162 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
164 logger->debug(TAG, "setStartFrame {} {} {}", startFrame, nextiframeNumber,iframeNumber);
165 currentFrameNumber = iframeNumber;
168 void PlayerVideoRec::setLengthBytes(ULLONG length)
170 lengthBytes = length;
171 logger->debug(TAG, "Player has received length bytes of {}", lengthBytes);
174 void PlayerVideoRec::setLengthFrames(ULONG length)
176 lengthFrames = length;
177 logger->debug(TAG, "Player has received length frames of {}", lengthFrames);
180 ULONG PlayerVideoRec::getLengthFrames()
185 ULONG PlayerVideoRec::getCurrentFrameNum()
187 if (startup) return 0;
192 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
196 return currentFrameNumber;
198 return 0; // shouldn't happen
202 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
204 return demuxer->getmpAudioChannels();
207 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
209 return demuxer->getac3AudioChannels();
212 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
214 return demuxer->getSubtitleChannels();
217 int PlayerVideoRec::getCurrentAudioChannel()
220 return demuxer->getselAudioChannel();
222 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
225 int PlayerVideoRec::getCurrentSubtitleChannel()
228 return demuxer->getselSubtitleChannel();
230 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
233 void PlayerVideoRec::setSubtitleChannel(int newChannel)
236 demuxer->setDVBSubtitleStream(newChannel);
238 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
241 int *PlayerVideoRec::getTeletxtSubtitlePages()
243 return teletext->getSubtitlePages();
246 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
250 demuxer->setAudioStream(newChannel);
255 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
260 bool PlayerVideoRec::toggleSubtitles()
262 if (!subtitlesShowing)
264 subtitlesShowing = true;
269 subtitlesShowing = false;
272 return subtitlesShowing;
275 void PlayerVideoRec::turnSubtitlesOn(bool ison)
279 subtitlesShowing = true;
284 subtitlesShowing = false;
289 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
291 subtitles->setOSDMenuVisibility(visible);
294 Channel * PlayerVideoRec::getDemuxerChannel()
296 if (!is_pesrecording)
298 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
300 return NULL; //Should not happen!
304 // ----------------------------------- Externally called events
306 void PlayerVideoRec::play()
308 if (!initted) return;
309 if (state == S_PLAY) return;
312 bool doUnlock = false;
313 if (state == S_PAUSE_P) doUnlock = true;
315 if (doUnlock) stateMutex.unlock();
318 void PlayerVideoRec::playpause()
320 if (!initted) return;
323 bool doUnlock = false;
327 switchState(S_PAUSE_P);
331 if (state == S_PAUSE_P) doUnlock = true;
334 if (doUnlock) stateMutex.unlock();
337 void PlayerVideoRec::stop()
339 if (!initted) return;
340 if (state == S_STOP) return;
342 logger->debug(TAG, "Stop called lock");
347 void PlayerVideoRec::pause()
349 if (!initted) return;
352 if ((state == S_FFWD) || (state == S_FBWD))
354 switchState(S_PAUSE_I);
356 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
362 switchState(S_PAUSE_P);
368 void PlayerVideoRec::fastForward()
370 if (!initted) return;
378 case 4: ifactor = 8; break;
379 case 8: ifactor = 16; break;
380 case 16: ifactor = 32; break;
381 case 32: ifactor = 4; break;
392 void PlayerVideoRec::fastBackward()
394 if (!initted) return;
402 case 4: ifactor = 8; break;
403 case 8: ifactor = 16; break;
404 case 16: ifactor = 32; break;
405 case 32: ifactor = 4; break;
416 void PlayerVideoRec::jumpToPercent(double percent)
419 logger->debug(TAG, "JUMP TO {}%", percent);
420 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
421 switchState(S_JUMP, newFrame);
422 // stateMutex.unlock(); - let thread unlock this
425 void PlayerVideoRec::jumpToMark(int mark)
428 logger->debug(TAG, "JUMP TO MARK {}%", mark);
429 switchState(S_JUMP, mark);
430 // stateMutex.unlock(); - let thread unlock this
433 void PlayerVideoRec::jumpToFrameP(int newFrame)
436 logger->debug(TAG, "JUMP TO FRAME AND PAUSE {}", newFrame);
437 switchState(S_JUMP_PI, newFrame);
441 void PlayerVideoRec::skipForward(int seconds)
444 logger->debug(TAG, "SKIP FORWARD {} SECONDS", seconds);
445 ULONG newFrame = getCurrentFrameNum();
446 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
447 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
448 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
449 else switchState(S_JUMP, newFrame);
450 // stateMutex.unlock(); - let thread unlock this
453 void PlayerVideoRec::skipBackward(int seconds)
456 logger->debug(TAG, "SKIP BACKWARD {} SECONDS", seconds);
457 long newFrame = getCurrentFrameNum();
458 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
459 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
460 if (newFrame < 0) newFrame = 0;
461 switchState(S_JUMP, newFrame);
462 // stateMutex.unlock(); - let thread unlock this
465 // ----------------------------------- Implementations called events
467 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
469 if (!initted) return;
471 logger->debug(TAG, "Switch state from {} to {}", state, toState);
473 switch(state) // current state selector
475 case S_PLAY: // from S_PLAY -----------------------------------
479 case S_PLAY: // to S_PLAY
483 case S_PAUSE_P: // to S_PAUSE_P
485 #ifdef VOMP_PLATFORM_RASPBERRY
486 vfeed.stop(); // can't vfeed during pause
495 case S_PAUSE_I: // to S_PAUSE_I
500 case S_FFWD: // to S_FFWD
502 currentFrameNumber = getCurrentFrameNum();
503 audio->systemMuteOn();
514 case S_FBWD: // to S_FBWD
516 currentFrameNumber = getCurrentFrameNum();
517 audio->systemMuteOn();
528 case S_STOP: // to S_STOP
544 case S_JUMP: // to S_JUMP
546 restartAtFrame(jumpFrame);
549 case S_JUMP_PI: // to S_JUMP_PI
551 audio->systemMuteOn();
561 restartAtFramePI(jumpFrame);
566 FALLTHROUGH // keep compiler happy (all posibilities return)
567 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
571 case S_PLAY: // to S_PLAY
575 subtitles->unPause();
577 #ifdef VOMP_PLATFORM_RASPBERRY
584 case S_PAUSE_P: // to S_PAUSE_P
588 case S_PAUSE_I: // to S_PAUSE_I
592 case S_FFWD: // to S_FFWD
594 currentFrameNumber = getCurrentFrameNum();
595 audio->systemMuteOn();
607 case S_FBWD: // to S_FBWD
609 currentFrameNumber = getCurrentFrameNum();
610 audio->systemMuteOn();
622 case S_STOP: // to S_STOP
635 audio->systemMuteOff();
639 case S_JUMP: // to S_JUMP
642 audio->systemMuteOn();
644 restartAtFrame(jumpFrame);
647 case S_JUMP_PI: // to S_JUMP_PI
649 audio->systemMuteOn();
660 restartAtFramePI(jumpFrame);
665 FALLTHROUGH // keep compiler happy (all posibilities return)
666 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
670 case S_PLAY: // to S_PLAY
673 restartAtFrame(currentFrameNumber);
676 case S_PAUSE_P: // to S_PAUSE_P
680 case S_PAUSE_I: // to S_PAUSE_I
684 case S_FFWD: // to S_FFWD
690 case S_FBWD: // to S_FBWD
696 case S_STOP: // to S_STOP
703 audio->systemMuteOff();
707 case S_JUMP: // to S_JUMP
710 restartAtFrame(jumpFrame);
713 case S_JUMP_PI: // to S_JUMP_PI
715 restartAtFramePI(jumpFrame);
720 FALLTHROUGH // keep compiler happy (all posibilities return)
721 case S_FFWD: // from S_FFWD -----------------------------------
725 case S_PLAY: // to S_PLAY
728 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
729 if (stepback < currentFrameNumber)
730 currentFrameNumber -= stepback;
732 currentFrameNumber = 0;
733 restartAtFrame(currentFrameNumber);
736 case S_PAUSE_P: // to S_PAUSE_P
741 case S_PAUSE_I: // to S_PAUSE_I
747 case S_FFWD: // to S_FFWD
751 case S_FBWD: // to S_FBWD
758 case S_STOP: // to S_STOP
769 case S_JUMP: // to S_JUMP
772 restartAtFrame(jumpFrame);
775 case S_JUMP_PI: // to S_JUMP_PI
779 restartAtFramePI(jumpFrame);
784 FALLTHROUGH // keep compiler happy (all posibilities return)
785 case S_FBWD: // from S_FBWD -----------------------------------
789 case S_PLAY: // to S_PLAY
792 restartAtFrame(currentFrameNumber);
795 case S_PAUSE_P: // to S_PAUSE_P
800 case S_PAUSE_I: // to S_PAUSE_I
806 case S_FFWD: // to S_FFWD
813 case S_FBWD: // to S_FBWD
817 case S_STOP: // to S_STOP
828 case S_JUMP: // to S_JUMP
831 restartAtFrame(jumpFrame);
834 case S_JUMP_PI: // to S_JUMP_PI
838 restartAtFramePI(jumpFrame);
843 FALLTHROUGH // keep compiler happy (all posibilities return)
844 case S_STOP: // from S_STOP -----------------------------------
848 case S_PLAY: // to S_PLAY
853 audio->setStreamType(Audio::MPEG2_PES);
854 audio->systemMuteOff();
857 // FIXME use restartAtFrame here?
858 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
859 demuxer->setFrameNum(currentFrameNumber);
864 logger->debug(TAG, "Immediate play");
875 case S_PAUSE_P: // to S_PAUSE_P
879 case S_PAUSE_I: // to S_PAUSE_I
883 case S_FFWD: // to S_FFWD
887 case S_FBWD: // to S_FBWD
891 case S_STOP: // to S_STOP
895 case S_JUMP: // to S_JUMP
899 case S_JUMP_PI: // to S_JUMP_PI
905 // case S_JUMP cannot be a start state because it auto flips to play
906 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
910 // ----------------------------------- Internal functions
912 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
922 audio->setStreamType(Audio::MPEG2_PES);
925 currentFrameNumber = newFrame;
926 demuxer->setFrameNum(newFrame);
936 audio->systemMuteOff();
941 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
944 ULONG nextiframeNumber;
952 // newFrame could be anywhere, go forwards to next I-Frame
953 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
955 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
956 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
958 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
959 if (!vdr->isConnected())
961 if (buffer) free(buffer);
966 videoLength = demuxer->stripAudio(buffer, amountReceived);
967 video->displayIFrame(buffer, videoLength);
968 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
970 currentFrameNumber = iframeNumber;
974 void PlayerVideoRec::doConnectionLost()
976 logger->debug(TAG, "Connection lost, sending message");
977 Message* m = new Message();
978 m->to = messageReceiver;
980 m->message = Message::PLAYER_EVENT;
981 m->parameter = PlayerVideoRec::CONNECTION_LOST;
982 messageQueue->postMessage(m);
985 // ----------------------------------- Callback
987 void PlayerVideoRec::call(void* caller)
989 if (caller == demuxer)
991 logger->debug(TAG, "Callback from demuxer");
993 if (video->getTVsize() == Video::ASPECT4X3)
995 logger->debug(TAG, "TV is 4:3, ignoring aspect switching");
1000 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
1001 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
1003 logger->debug(TAG, "Demuxer said video is 4:3 aspect, switching TV");
1004 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
1006 Message* m = new Message();
1008 m->to = messageReceiver;
1009 m->message = Message::PLAYER_EVENT;
1010 m->parameter = PlayerVideoRec::ASPECT43;
1011 messageQueue->postMessage(m);
1013 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1015 logger->debug(TAG, "Demuxer said video is 16:9 aspect, switching TV");
1016 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1018 Message* m = new Message();
1020 m->to = messageReceiver;
1021 m->message = Message::PLAYER_EVENT;
1022 m->parameter = PlayerVideoRec::ASPECT169;
1023 messageQueue->postMessage(m);
1027 logger->debug(TAG, "Demuxer said video is something else... setting it anyway");
1028 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1036 videoStartup = false;
1040 stateMutex.unlock();
1043 playerThreadCond.notify_one();
1047 // ----------------------------------- Feed thread
1049 void PlayerVideoRec::threadMethod()
1051 // this method used to be simple, the only thing it does
1052 // is farm out to threadFeed Live/Play/Scan
1053 // All the guff is to support scan hitting one end
1055 if ((state == S_FFWD) || (state == S_FBWD))
1058 if (video->PTSIFramePlayback()) hitEnd = threadPTSFeedScan();
1059 else hitEnd = threadFeedScan();
1061 if (!hitEnd) return; // thread wants to exit normally
1063 if (state == S_FFWD) // scan hit the end. stop
1065 if (threadReqQuit) return;
1066 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1067 m->to = messageReceiver;
1069 m->message = Message::PLAYER_EVENT;
1070 m->parameter = STOP_PLAYBACK;
1071 logger->debug(TAG, "Posting message to {}...", (void*)messageQueue);
1072 messageQueue->postMessage(m);
1073 logger->debug(TAG, "Message posted...");
1076 // if execution gets to here, threadFeedScan hit the start, go to play mode
1079 audio->setStreamType(Audio::MPEG2_PES);
1082 demuxer->setFrameNum(currentFrameNumber);
1083 videoStartup = true;
1090 audio->systemMuteOff();
1094 if (state == S_PLAY) threadFeedPlay();
1097 void PlayerVideoRec::threadFeedPlay()
1099 ULLONG feedPosition;
1100 UINT thisRead, writeLength, thisWrite, askFor;
1101 time_t lastRescan = time(NULL);
1103 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1104 if (!vdr->isConnected()) { doConnectionLost(); return; }
1105 logger->debug(TAG, "startFeedPlay: wantedframe {} goto {}", currentFrameNumber, feedPosition);
1107 Buffer threadBuffer;
1109 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
1117 if (threadReqQuit) return;
1119 // If we havn't rescanned for a while..
1120 if ((lastRescan + 60) < time(NULL))
1122 lengthBytes = vdr->rescanRecording(&lengthFrames);
1123 if (!vdr->isConnected()) { doConnectionLost(); return; }
1124 logger->debug(TAG, "Rescanned and reset length: {}", lengthBytes);
1125 lastRescan = time(NULL);
1128 if (feedPosition >= lengthBytes) break; // finished playback
1132 if (startupBlockSize > lengthBytes)
1133 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1135 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1139 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1140 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1144 //logger->log("Player", Log::DEBUG, "Get Block in");
1146 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
1147 //logger->log("Player", Log::DEBUG, "Get Block out");
1149 feedPosition += thisRead;
1151 if (!vdr->isConnected())
1157 if (threadBuffer.isNull()) break;
1161 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
1162 demuxer->setAudioStream(a_stream);
1163 logger->debug(TAG, "Startup Audio stream chosen {:#x}", a_stream);
1167 if (threadReqQuit) return;
1169 while(writeLength < thisRead)
1171 //logger->log("Player", Log::DEBUG, "Put in");
1172 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
1173 //logger->log("Player", Log::DEBUG, "Put out");
1174 writeLength += thisWrite;
1178 // demuxer is full and can't take anymore
1180 if (threadReqQuit) { ul.unlock(); return; }
1181 playerThreadCond.wait(ul);
1185 if (threadReqQuit) return;
1188 threadBuffer.release();
1192 logger->debug(TAG, "Recording playback ends");
1194 if (videoStartup) // oh woe. there never was a stream, I was conned!
1196 videoStartup = false;
1197 stateMutex.unlock();
1198 MILLISLEEP(500); // I think this will solve a race
1201 if (threadReqQuit) return;
1203 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1204 m->to = messageReceiver;
1206 m->message = Message::PLAYER_EVENT;
1207 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1208 logger->debug(TAG, "Posting message to {}...", (void*)messageQueue);
1209 messageQueue->postMessage(m);
1213 bool PlayerVideoRec::threadPTSFeedScan()
1215 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1217 ULONG direction = 0;
1219 ULONG baseFrameNumber = 0;
1220 ULONG iframeNumber = 0;
1221 ULONG iframeLength = 0;
1222 ULONG currentfeedFrameNumber=currentFrameNumber;
1223 ULONG firstFrameNumber=currentFrameNumber;
1225 UINT amountReceived;
1230 int frameTimeOffset = 0; // Time in msec between frames
1232 Buffer threadBuffer;
1234 if (state == S_FFWD)
1236 direction = 1; // and 0 for backward
1239 video->EnterIframePlayback();
1243 baseFrameNumber = currentfeedFrameNumber;
1245 if (threadReqQuit) return false;
1247 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1250 if (iframeNumber >= lengthFrames) return true;
1251 // scan has got to the end of what we knew to be there before we started scanning
1253 baseFrameNumber = iframeNumber;
1255 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1257 logger->debug(TAG, "XXX Got frame");
1259 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1261 if (!vdr->isConnected())
1267 if (threadReqQuit) return false;
1269 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1270 demuxer->changeTimes(threadBuffer.ucharp(), videoLength, playtime);
1272 while (!video->displayIFrame(threadBuffer.ucharp(), videoLength)) // the device might block
1275 if (threadReqQuit) return false;
1278 ULLONG cur_time=video->getCurrentTimestamp();
1279 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1280 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1282 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1287 playtime +=frameTimeOffset;
1288 currentfeedFrameNumber = iframeNumber;
1290 ULLONG cur_time=video->getCurrentTimestamp();
1291 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1292 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1294 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1298 threadBuffer.release();
1304 bool PlayerVideoRec::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
1333 Buffer threadBuffer;
1337 // Fetch I-frames until we get one that can be displayed in good time
1338 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1340 baseFrameNumber = currentFrameNumber;
1343 if (threadReqQuit) return false;
1345 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1348 if (iframeNumber >= lengthFrames) return true;
1349 // scan has got to the end of what we knew to be there before we started scanning
1351 baseFrameNumber = iframeNumber;
1352 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1354 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
1357 gettimeofday(&clock0, NULL);
1359 clock0 = timeGetTime();
1363 while (clock2.tv_sec != 0 &&
1364 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1365 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1367 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1369 logger->debug(TAG, "XXX Got frame");
1371 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1373 if (!vdr->isConnected() || !amountReceived)
1380 gettimeofday(&clock1, NULL);
1381 if (clock2.tv_sec != 0)
1382 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1383 + (clock2.tv_usec - clock1.tv_usec) / 1000
1384 + frameTimeOffset - disp_msec;
1386 clock1 = timeGetTime();
1388 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1390 if (sleepTime < 0) sleepTime = 0;
1391 if (threadReqQuit) return false;
1392 MILLISLEEP(sleepTime);
1393 logger->debug(TAG, "XXX Slept for {}", sleepTime);
1395 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1396 video->displayIFrame(threadBuffer.ucharp(), videoLength);
1397 currentFrameNumber = iframeNumber;
1398 threadBuffer.release();
1401 gettimeofday(&clock2, NULL);
1402 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1403 + (clock2.tv_usec - clock0.tv_usec) / 1000
1405 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1406 + (clock2.tv_usec - clock1.tv_usec) / 1000
1409 clock2 = timeGetTime();
1410 total_msec = clock2 - clock0 - sleepTime;
1411 disp_msec = clock2 - clock1 - sleepTime;
1413 logger->debug(TAG, "XXX disp_msec = {} total_msec = {}", disp_msec, total_msec);
1417 // ----------------------------------- Dev
1420 void PlayerVideoRec::test1()
1422 logger->debug(TAG, "PLAYER TEST 1");
1425 void PlayerVideoRec::test2()
1427 logger->debug(TAG, "PLAYER TEST 2");