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"
36 #include "playervideorec.h"
38 static const char* TAG = "PlayerVideoRec";
40 #define USER_RESPONSE_TIME 500 // Milliseconds
42 // ----------------------------------- Called from outside, one offs or info funcs
44 PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, MessageReceiver* tmessageReceiver, OSDReceiver* tosdReceiver)
45 : vfeed(this), afeed(this), tfeed(this),
46 osdReceiver(tosdReceiver), messageQueue(tmessageQueue), messageReceiver(tmessageReceiver)
48 audio = Audio::getInstance();
49 video = Video::getInstance();
50 logger = LogNT::getInstance();
51 vdr = VDR::getInstance();
56 PlayerVideoRec::~PlayerVideoRec()
58 if (initted) shutdown();
61 int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
63 if (initted) return 0;
64 is_pesrecording = p_isPesRecording;
65 fps = framespersecond;
67 demuxer = new DemuxerVDR();
69 demuxer = new DemuxerTS();
70 if (!demuxer) return 0;
71 subtitles = new DVBSubtitles(osdReceiver);
72 if (!subtitles) return 0;
74 teletext = new TeletextDecoderVBIEBU();
75 if (!teletext) return 0;
76 teletext->setRecordigMode(true);
77 unsigned int demux_video_size = 2097152;
78 unsigned int demux_audio_size = 524288;
79 if (video->supportsh264())
81 demux_video_size *= 5 * 2;
83 if (audio->maysupportAc3())
85 //demux_audio_size*=2;
88 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
90 logger->error(TAG, "Demuxer failed to init");
100 Config::getInstance()->getInt("subtitles", "default", subDefault);
102 turnSubtitlesOn(true);
104 turnSubtitlesOn(false);
110 int PlayerVideoRec::shutdown()
112 if (!initted) return 0;
126 void PlayerVideoRec::threadStart()
128 playerThreadMutex.lock();
129 threadReqQuit = false;
130 playerThread = std::thread([this]
132 playerThreadMutex.lock();
133 playerThreadMutex.unlock();
136 playerThreadMutex.unlock();
139 void PlayerVideoRec::threadStop()
141 playerThreadMutex.lock();
143 if (!playerThread.joinable()) // restartAtFrame sometimes calls threadStop when it's not running
145 playerThreadMutex.unlock();
149 threadReqQuit = true;
150 playerThreadCond.notify_one();
151 playerThreadMutex.unlock();
155 void PlayerVideoRec::setStartFrame(u4 startFrame)
162 // newFrame could be anywhere, go forwards to next I-Frame
163 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
165 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
166 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
168 logger->debug(TAG, "setStartFrame {} {} {}", startFrame, nextiframeNumber,iframeNumber);
169 currentFrameNumber = iframeNumber;
172 void PlayerVideoRec::setLengthBytes(u8 length)
174 lengthBytes = length;
175 logger->debug(TAG, "Player has received length bytes of {}", lengthBytes);
178 void PlayerVideoRec::setLengthFrames(u4 length)
180 lengthFrames = length;
181 logger->debug(TAG, "Player has received length frames of {}", lengthFrames);
184 u4 PlayerVideoRec::getLengthFrames()
189 u4 PlayerVideoRec::getCurrentFrameNum()
191 if (startup) return 0;
196 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
200 return currentFrameNumber;
202 return 0; // shouldn't happen
206 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
208 return demuxer->getmpAudioChannels();
211 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
213 return demuxer->getac3AudioChannels();
216 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
218 return demuxer->getSubtitleChannels();
221 int PlayerVideoRec::getCurrentAudioChannel()
224 return demuxer->getselAudioChannel();
226 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
229 int PlayerVideoRec::getCurrentSubtitleChannel()
232 return demuxer->getselSubtitleChannel();
234 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
237 void PlayerVideoRec::setSubtitleChannel(int newChannel)
240 demuxer->setDVBSubtitleStream(newChannel);
242 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
245 int *PlayerVideoRec::getTeletxtSubtitlePages()
247 return teletext->getSubtitlePages();
250 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
254 demuxer->setAudioStream(newChannel);
259 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
264 bool PlayerVideoRec::toggleSubtitles()
266 if (!subtitlesShowing)
268 subtitlesShowing = true;
273 subtitlesShowing = false;
276 return subtitlesShowing;
279 void PlayerVideoRec::turnSubtitlesOn(bool ison)
283 subtitlesShowing = true;
288 subtitlesShowing = false;
293 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
295 subtitles->setOSDMenuVisibility(visible);
298 Channel * PlayerVideoRec::getDemuxerChannel()
300 if (!is_pesrecording)
302 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
304 return NULL; //Should not happen!
308 // ----------------------------------- Externally called events
310 void PlayerVideoRec::play()
312 if (!initted) return;
313 if (state == S_PLAY) return;
316 bool doUnlock = false;
317 if (state == S_PAUSE_P) doUnlock = true;
319 if (doUnlock) stateMutex.unlock();
322 void PlayerVideoRec::playpause()
324 if (!initted) return;
327 bool doUnlock = false;
331 switchState(S_PAUSE_P);
335 if (state == S_PAUSE_P) doUnlock = true;
338 if (doUnlock) stateMutex.unlock();
341 void PlayerVideoRec::stop()
343 if (!initted) return;
344 if (state == S_STOP) return;
346 logger->debug(TAG, "Stop called lock");
351 void PlayerVideoRec::pause()
353 if (!initted) return;
356 if ((state == S_FFWD) || (state == S_FBWD))
358 switchState(S_PAUSE_I);
360 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
366 switchState(S_PAUSE_P);
372 void PlayerVideoRec::fastForward()
374 if (!initted) return;
382 case 4: ifactor = 8; break;
383 case 8: ifactor = 16; break;
384 case 16: ifactor = 32; break;
385 case 32: ifactor = 4; break;
396 void PlayerVideoRec::fastBackward()
398 if (!initted) return;
406 case 4: ifactor = 8; break;
407 case 8: ifactor = 16; break;
408 case 16: ifactor = 32; break;
409 case 32: ifactor = 4; break;
420 void PlayerVideoRec::jumpToPercent(double percent)
423 logger->debug(TAG, "JUMP TO {}%", percent);
424 u4 newFrame = static_cast<u4>(percent * lengthFrames / 100);
425 switchState(S_JUMP, newFrame);
426 // stateMutex.unlock(); - let thread unlock this
429 void PlayerVideoRec::jumpToMark(int mark)
432 logger->debug(TAG, "JUMP TO MARK {}%", mark);
433 switchState(S_JUMP, mark);
434 // stateMutex.unlock(); - let thread unlock this
437 void PlayerVideoRec::jumpToFrameP(int newFrame)
440 logger->debug(TAG, "JUMP TO FRAME AND PAUSE {}", newFrame);
441 switchState(S_JUMP_PI, newFrame);
445 void PlayerVideoRec::skipForward(int seconds)
448 logger->debug(TAG, "SKIP FORWARD {} SECONDS", seconds);
449 u4 newFrame = getCurrentFrameNum();
450 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
451 newFrame += static_cast<u4>(static_cast<double>(seconds) * fps);
452 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
453 else switchState(S_JUMP, newFrame);
454 // stateMutex.unlock(); - let thread unlock this
457 void PlayerVideoRec::skipBackward(int seconds)
460 logger->debug(TAG, "SKIP BACKWARD {} SECONDS", seconds);
461 long newFrame = getCurrentFrameNum();
462 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
463 newFrame -= static_cast<u4>(static_cast<double>(seconds) * fps);
464 if (newFrame < 0) newFrame = 0;
465 switchState(S_JUMP, newFrame);
466 // stateMutex.unlock(); - let thread unlock this
469 // ----------------------------------- Implementations called events
471 void PlayerVideoRec::switchState(u1 toState, u4 jumpFrame)
473 if (!initted) return;
475 logger->debug(TAG, "Switch state from {} to {}", state, toState);
477 switch(state) // current state selector
479 case S_PLAY: // from S_PLAY -----------------------------------
483 case S_PLAY: // to S_PLAY
487 case S_PAUSE_P: // to S_PAUSE_P
489 #ifdef VOMP_PLATFORM_RASPBERRY
490 vfeed.stop(); // can't vfeed during pause
499 case S_PAUSE_I: // to S_PAUSE_I
504 case S_FFWD: // to S_FFWD
506 currentFrameNumber = getCurrentFrameNum();
507 audio->systemMuteOn();
518 case S_FBWD: // to S_FBWD
520 currentFrameNumber = getCurrentFrameNum();
521 audio->systemMuteOn();
532 case S_STOP: // to S_STOP
548 case S_JUMP: // to S_JUMP
550 restartAtFrame(jumpFrame);
553 case S_JUMP_PI: // to S_JUMP_PI
555 audio->systemMuteOn();
565 restartAtFramePI(jumpFrame);
570 FALLTHROUGH // keep compiler happy (all posibilities return)
571 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
575 case S_PLAY: // to S_PLAY
579 subtitles->unPause();
581 #ifdef VOMP_PLATFORM_RASPBERRY
588 case S_PAUSE_P: // to S_PAUSE_P
592 case S_PAUSE_I: // to S_PAUSE_I
596 case S_FFWD: // to S_FFWD
598 currentFrameNumber = getCurrentFrameNum();
599 audio->systemMuteOn();
611 case S_FBWD: // to S_FBWD
613 currentFrameNumber = getCurrentFrameNum();
614 audio->systemMuteOn();
626 case S_STOP: // to S_STOP
639 audio->systemMuteOff();
643 case S_JUMP: // to S_JUMP
646 audio->systemMuteOn();
648 restartAtFrame(jumpFrame);
651 case S_JUMP_PI: // to S_JUMP_PI
653 audio->systemMuteOn();
664 restartAtFramePI(jumpFrame);
669 FALLTHROUGH // keep compiler happy (all posibilities return)
670 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
674 case S_PLAY: // to S_PLAY
677 restartAtFrame(currentFrameNumber);
680 case S_PAUSE_P: // to S_PAUSE_P
684 case S_PAUSE_I: // to S_PAUSE_I
688 case S_FFWD: // to S_FFWD
694 case S_FBWD: // to S_FBWD
700 case S_STOP: // to S_STOP
707 audio->systemMuteOff();
711 case S_JUMP: // to S_JUMP
714 restartAtFrame(jumpFrame);
717 case S_JUMP_PI: // to S_JUMP_PI
719 restartAtFramePI(jumpFrame);
724 FALLTHROUGH // keep compiler happy (all posibilities return)
725 case S_FFWD: // from S_FFWD -----------------------------------
729 case S_PLAY: // to S_PLAY
732 u4 stepback = static_cast<u4>(USER_RESPONSE_TIME * ifactor * fps / 1000);
733 if (stepback < currentFrameNumber)
734 currentFrameNumber -= stepback;
736 currentFrameNumber = 0;
737 restartAtFrame(currentFrameNumber);
740 case S_PAUSE_P: // to S_PAUSE_P
745 case S_PAUSE_I: // to S_PAUSE_I
751 case S_FFWD: // to S_FFWD
755 case S_FBWD: // to S_FBWD
762 case S_STOP: // to S_STOP
773 case S_JUMP: // to S_JUMP
776 restartAtFrame(jumpFrame);
779 case S_JUMP_PI: // to S_JUMP_PI
783 restartAtFramePI(jumpFrame);
788 FALLTHROUGH // keep compiler happy (all posibilities return)
789 case S_FBWD: // from S_FBWD -----------------------------------
793 case S_PLAY: // to S_PLAY
796 restartAtFrame(currentFrameNumber);
799 case S_PAUSE_P: // to S_PAUSE_P
804 case S_PAUSE_I: // to S_PAUSE_I
810 case S_FFWD: // to S_FFWD
817 case S_FBWD: // to S_FBWD
821 case S_STOP: // to S_STOP
832 case S_JUMP: // to S_JUMP
835 restartAtFrame(jumpFrame);
838 case S_JUMP_PI: // to S_JUMP_PI
842 restartAtFramePI(jumpFrame);
847 FALLTHROUGH // keep compiler happy (all posibilities return)
848 case S_STOP: // from S_STOP -----------------------------------
852 case S_PLAY: // to S_PLAY
857 audio->setStreamType(Audio::MPEG2_PES);
858 audio->systemMuteOff();
861 // FIXME use restartAtFrame here?
862 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
863 demuxer->setFrameNum(currentFrameNumber);
868 logger->debug(TAG, "Immediate play");
879 case S_PAUSE_P: // to S_PAUSE_P
883 case S_PAUSE_I: // to S_PAUSE_I
887 case S_FFWD: // to S_FFWD
891 case S_FBWD: // to S_FBWD
895 case S_STOP: // to S_STOP
899 case S_JUMP: // to S_JUMP
903 case S_JUMP_PI: // to S_JUMP_PI
909 // case S_JUMP cannot be a start state because it auto flips to play
910 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
914 // ----------------------------------- Internal functions
916 void PlayerVideoRec::restartAtFrame(u4 newFrame)
926 audio->setStreamType(Audio::MPEG2_PES);
929 currentFrameNumber = newFrame;
930 demuxer->setFrameNum(newFrame);
940 audio->systemMuteOff();
945 void PlayerVideoRec::restartAtFramePI(u4 newFrame)
956 // newFrame could be anywhere, go forwards to next I-Frame
957 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
959 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
960 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
962 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
963 if (!vdr->isConnected())
965 if (buffer) free(buffer);
970 videoLength = demuxer->stripAudio(buffer, amountReceived);
971 video->displayIFrame(buffer, videoLength);
972 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
974 currentFrameNumber = iframeNumber;
978 void PlayerVideoRec::doConnectionLost()
980 logger->debug(TAG, "Connection lost, sending message");
981 Message* m = new Message();
982 m->to = messageReceiver;
984 m->message = Message::PLAYER_EVENT;
985 m->parameter = PlayerVideoRec::CONNECTION_LOST;
986 messageQueue->postMessage(m);
989 // ----------------------------------- Callback
991 void PlayerVideoRec::call(void* caller)
993 if (caller == demuxer)
995 logger->debug(TAG, "Callback from demuxer");
997 if (video->getTVsize() == Video::ASPECT4X3)
999 logger->debug(TAG, "TV is 4:3, ignoring aspect switching");
1004 u1 dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
1005 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
1007 logger->debug(TAG, "Demuxer said video is 4:3 aspect, switching TV");
1008 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
1010 Message* m = new Message();
1012 m->to = messageReceiver;
1013 m->message = Message::PLAYER_EVENT;
1014 m->parameter = PlayerVideoRec::ASPECT43;
1015 messageQueue->postMessage(m);
1017 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1019 logger->debug(TAG, "Demuxer said video is 16:9 aspect, switching TV");
1020 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1022 Message* m = new Message();
1024 m->to = messageReceiver;
1025 m->message = Message::PLAYER_EVENT;
1026 m->parameter = PlayerVideoRec::ASPECT169;
1027 messageQueue->postMessage(m);
1031 logger->debug(TAG, "Demuxer said video is something else... setting it anyway");
1032 video->setAspectRatio(static_cast<u1>(dxCurrentAspect), parx, pary);
1040 videoStartup = false;
1044 stateMutex.unlock();
1047 playerThreadCond.notify_one();
1051 // ----------------------------------- Feed thread
1053 void PlayerVideoRec::threadMethod()
1055 // this method used to be simple, the only thing it does
1056 // is farm out to threadFeed Live/Play/Scan
1057 // All the guff is to support scan hitting one end
1059 if ((state == S_FFWD) || (state == S_FBWD))
1062 if (video->PTSIFramePlayback()) hitEnd = threadPTSFeedScan();
1063 else hitEnd = threadFeedScan();
1065 if (!hitEnd) return; // thread wants to exit normally
1067 if (state == S_FFWD) // scan hit the end. stop
1069 if (threadReqQuit) return;
1070 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1071 m->to = messageReceiver;
1073 m->message = Message::PLAYER_EVENT;
1074 m->parameter = STOP_PLAYBACK;
1075 logger->debug(TAG, "Posting message to {}...", (void*)messageQueue);
1076 messageQueue->postMessage(m);
1077 logger->debug(TAG, "Message posted...");
1080 // if execution gets to here, threadFeedScan hit the start, go to play mode
1083 audio->setStreamType(Audio::MPEG2_PES);
1086 demuxer->setFrameNum(currentFrameNumber);
1087 videoStartup = true;
1094 audio->systemMuteOff();
1098 if (state == S_PLAY) threadFeedPlay();
1101 void PlayerVideoRec::threadFeedPlay()
1104 u4 thisRead, writeLength, thisWrite, askFor;
1105 time_t lastRescan = time(NULL);
1107 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1108 if (!vdr->isConnected()) { doConnectionLost(); return; }
1109 logger->debug(TAG, "startFeedPlay: wantedframe {} goto {}", currentFrameNumber, feedPosition);
1111 Buffer threadBuffer;
1113 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
1121 if (threadReqQuit) return;
1123 // If we havn't rescanned for a while..
1124 if ((lastRescan + 60) < time(NULL))
1126 lengthBytes = vdr->rescanRecording(&lengthFrames);
1127 if (!vdr->isConnected()) { doConnectionLost(); return; }
1128 logger->debug(TAG, "Rescanned and reset length: {}", lengthBytes);
1129 lastRescan = time(NULL);
1132 if (feedPosition >= lengthBytes) break; // finished playback
1136 if (startupBlockSize > lengthBytes)
1137 askFor = static_cast<u4>(lengthBytes); // is a very small recording!
1139 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1143 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1144 askFor = static_cast<u4>(lengthBytes - feedPosition);
1148 //logger->log("Player", Log::DEBUG, "Get Block in");
1150 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
1151 //logger->log("Player", Log::DEBUG, "Get Block out");
1153 TELEM(1, feedPosition);
1155 feedPosition += thisRead;
1157 if (!vdr->isConnected())
1163 if (threadBuffer.isNull()) break;
1167 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
1168 demuxer->setAudioStream(a_stream);
1169 logger->debug(TAG, "Startup Audio stream chosen {:#x}", a_stream);
1173 if (threadReqQuit) return;
1175 while(writeLength < thisRead)
1177 //logger->log("Player", Log::DEBUG, "Put in");
1178 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
1179 //logger->log("Player", Log::DEBUG, "Put out");
1180 writeLength += thisWrite;
1184 // demuxer is full and can't take anymore
1186 if (threadReqQuit) { ul.unlock(); return; }
1187 playerThreadCond.wait(ul);
1191 if (threadReqQuit) return;
1194 threadBuffer.release();
1198 logger->debug(TAG, "Recording playback ends");
1200 if (videoStartup) // oh woe. there never was a stream, I was conned!
1202 videoStartup = false;
1203 stateMutex.unlock();
1204 MILLISLEEP(500); // I think this will solve a race
1207 if (threadReqQuit) return;
1209 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1210 m->to = messageReceiver;
1212 m->message = Message::PLAYER_EVENT;
1213 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1214 logger->debug(TAG, "Posting message to {}...", static_cast<void*>(messageReceiver));
1215 messageQueue->postMessage(m);
1219 bool PlayerVideoRec::threadPTSFeedScan()
1221 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1225 u4 baseFrameNumber = 0;
1226 u4 iframeNumber = 0;
1227 u4 iframeLength = 0;
1228 u4 currentfeedFrameNumber=currentFrameNumber;
1229 u4 firstFrameNumber=currentFrameNumber;
1236 int frameTimeOffset = 0; // Time in msec between frames
1238 Buffer threadBuffer;
1240 if (state == S_FFWD)
1242 direction = 1; // and 0 for backward
1245 video->EnterIframePlayback();
1249 baseFrameNumber = currentfeedFrameNumber;
1251 if (threadReqQuit) return false;
1253 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1256 if (iframeNumber >= lengthFrames) return true;
1257 // scan has got to the end of what we knew to be there before we started scanning
1259 baseFrameNumber = iframeNumber;
1261 frameTimeOffset = toi4((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1263 logger->debug(TAG, "XXX Got frame");
1265 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1267 if (!vdr->isConnected())
1273 if (threadReqQuit) return false;
1275 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1276 demuxer->changeTimes(threadBuffer.ucharp(), videoLength, playtime);
1278 while (!video->displayIFrame(threadBuffer.ucharp(), videoLength)) // the device might block
1281 if (threadReqQuit) return false;
1284 u8 cur_time=video->getCurrentTimestamp();
1285 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1286 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1288 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1293 playtime +=frameTimeOffset;
1294 currentfeedFrameNumber = iframeNumber;
1296 u8 cur_time=video->getCurrentTimestamp();
1297 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1298 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1300 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1304 threadBuffer.release();
1310 bool PlayerVideoRec::threadFeedScan()
1312 // This method is actually really simple - get frame from vdr,
1313 // spit it at the video chip, wait for a time. Most of the code here
1314 // is to get the wait right so that the scan occurs at the correct rate.
1317 u4 baseFrameNumber = 0;
1318 u4 iframeNumber = 0;
1319 u4 iframeLength = 0;
1325 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1326 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1327 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1329 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1332 int frameTimeOffset = 0; // Time in msec between frames
1333 int disp_msec = 0; // Time taken to display data
1334 int total_msec = 0; // Time taken to fetch data and display it
1337 if (state == S_FFWD) direction = 1; // and 0 for backward
1339 Buffer threadBuffer;
1343 // Fetch I-frames until we get one that can be displayed in good time
1344 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1346 baseFrameNumber = currentFrameNumber;
1349 if (threadReqQuit) return false;
1351 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1354 if (iframeNumber >= lengthFrames) return true;
1355 // scan has got to the end of what we knew to be there before we started scanning
1357 baseFrameNumber = iframeNumber;
1358 frameTimeOffset = toi4((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1360 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
1363 gettimeofday(&clock0, NULL);
1365 clock0 = timeGetTime();
1369 while (clock2.tv_sec != 0 &&
1370 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1371 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1373 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1375 logger->debug(TAG, "XXX Got frame");
1377 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1379 if (!vdr->isConnected() || !amountReceived)
1386 gettimeofday(&clock1, NULL);
1387 if (clock2.tv_sec != 0)
1388 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1389 + (clock2.tv_usec - clock1.tv_usec) / 1000
1390 + frameTimeOffset - disp_msec;
1392 clock1 = timeGetTime();
1394 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1396 if (sleepTime < 0) sleepTime = 0;
1397 if (threadReqQuit) return false;
1398 MILLISLEEP(sleepTime);
1399 logger->debug(TAG, "XXX Slept for {}", sleepTime);
1401 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1402 video->displayIFrame(threadBuffer.ucharp(), videoLength);
1403 currentFrameNumber = iframeNumber;
1404 threadBuffer.release();
1407 gettimeofday(&clock2, NULL);
1408 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1409 + (clock2.tv_usec - clock0.tv_usec) / 1000
1411 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1412 + (clock2.tv_usec - clock1.tv_usec) / 1000
1415 clock2 = timeGetTime();
1416 total_msec = clock2 - clock0 - sleepTime;
1417 disp_msec = clock2 - clock1 - sleepTime;
1419 logger->debug(TAG, "XXX disp_msec = {} total_msec = {}", disp_msec, total_msec);
1423 // ----------------------------------- Dev
1426 void PlayerVideoRec::test1()
1428 logger->debug(TAG, "PLAYER TEST 1");
1431 void PlayerVideoRec::test2()
1433 logger->debug(TAG, "PLAYER TEST 2");