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 #define USER_RESPONSE_TIME 500 // Milliseconds
38 // ----------------------------------- Called from outside, one offs or info funcs
40 PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
41 : vfeed(this), afeed(this), tfeed(this),
42 osdReceiver(tosdReceiver), messageQueue(tmessageQueue), messageReceiver(tmessageReceiver)
44 audio = Audio::getInstance();
45 video = Video::getInstance();
46 logger = Log::getInstance();
47 vdr = VDR::getInstance();
52 PlayerVideoRec::~PlayerVideoRec()
54 if (initted) shutdown();
57 int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
59 if (initted) return 0;
60 is_pesrecording = p_isPesRecording;
61 fps = framespersecond;
63 demuxer = new DemuxerVDR();
65 demuxer = new DemuxerTS();
66 if (!demuxer) return 0;
67 subtitles = new DVBSubtitles(osdReceiver);
68 if (!subtitles) return 0;
70 teletext = new TeletextDecoderVBIEBU();
71 if (!teletext) return 0;
72 teletext->setRecordigMode(true);
73 unsigned int demux_video_size = 2097152;
74 unsigned int demux_audio_size = 524288;
75 if (video->supportsh264())
77 demux_video_size *= 5 * 2;
79 if (audio->maysupportAc3())
81 //demux_audio_size*=2;
84 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
86 logger->log("Player", Log::ERR, "Demuxer failed to init");
95 if (Control::getInstance()->getSubDefault())
96 turnSubtitlesOn(true);
98 turnSubtitlesOn(false);
104 int PlayerVideoRec::shutdown()
106 if (!initted) return 0;
120 void PlayerVideoRec::threadStart()
122 playerThreadMutex.lock();
123 threadReqQuit = false;
124 playerThread = std::thread([this]
126 playerThreadMutex.lock();
127 playerThreadMutex.unlock();
130 playerThreadMutex.unlock();
133 void PlayerVideoRec::threadStop()
135 playerThreadMutex.lock();
136 threadReqQuit = true;
137 playerThreadCond.notify_one();
138 playerThreadMutex.unlock();
142 void PlayerVideoRec::setStartFrame(ULONG startFrame)
144 ULONG nextiframeNumber;
149 // newFrame could be anywhere, go forwards to next I-Frame
150 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
152 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
153 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
155 logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
156 currentFrameNumber = iframeNumber;
159 void PlayerVideoRec::setLengthBytes(ULLONG length)
161 lengthBytes = length;
162 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
165 void PlayerVideoRec::setLengthFrames(ULONG length)
167 lengthFrames = length;
168 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
171 ULONG PlayerVideoRec::getLengthFrames()
176 ULONG PlayerVideoRec::getCurrentFrameNum()
178 if (startup) return 0;
183 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
187 return currentFrameNumber;
189 return 0; // shouldn't happen
193 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
195 return demuxer->getmpAudioChannels();
198 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
200 return demuxer->getac3AudioChannels();
203 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
205 return demuxer->getSubtitleChannels();
208 int PlayerVideoRec::getCurrentAudioChannel()
211 return demuxer->getselAudioChannel();
213 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
216 int PlayerVideoRec::getCurrentSubtitleChannel()
219 return demuxer->getselSubtitleChannel();
221 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
224 void PlayerVideoRec::setSubtitleChannel(int newChannel)
227 demuxer->setDVBSubtitleStream(newChannel);
229 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
232 int *PlayerVideoRec::getTeletxtSubtitlePages()
234 return teletext->getSubtitlePages();
237 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
241 demuxer->setAudioStream(newChannel);
246 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
251 bool PlayerVideoRec::toggleSubtitles()
253 if (!subtitlesShowing)
255 subtitlesShowing = true;
260 subtitlesShowing = false;
263 return subtitlesShowing;
266 void PlayerVideoRec::turnSubtitlesOn(bool ison)
270 subtitlesShowing = true;
275 subtitlesShowing = false;
280 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
282 subtitles->setOSDMenuVisibility(visible);
285 Channel * PlayerVideoRec::getDemuxerChannel()
287 if (!is_pesrecording)
289 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
291 return NULL; //Should not happen!
295 // ----------------------------------- Externally called events
297 void PlayerVideoRec::play()
299 if (!initted) return;
300 if (state == S_PLAY) return;
303 bool doUnlock = false;
304 if (state == S_PAUSE_P) doUnlock = true;
306 if (doUnlock) stateMutex.unlock();
309 void PlayerVideoRec::playpause()
311 if (!initted) return;
314 bool doUnlock = false;
318 switchState(S_PAUSE_P);
322 if (state == S_PAUSE_P) doUnlock = true;
325 if (doUnlock) stateMutex.unlock();
328 void PlayerVideoRec::stop()
330 if (!initted) return;
331 if (state == S_STOP) return;
333 logger->log("Player", Log::DEBUG, "Stop called lock");
338 void PlayerVideoRec::pause()
340 if (!initted) return;
343 if ((state == S_FFWD) || (state == S_FBWD))
345 switchState(S_PAUSE_I);
347 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
353 switchState(S_PAUSE_P);
359 void PlayerVideoRec::fastForward()
361 if (!initted) return;
369 case 4: ifactor = 8; break;
370 case 8: ifactor = 16; break;
371 case 16: ifactor = 32; break;
372 case 32: ifactor = 4; break;
383 void PlayerVideoRec::fastBackward()
385 if (!initted) return;
393 case 4: ifactor = 8; break;
394 case 8: ifactor = 16; break;
395 case 16: ifactor = 32; break;
396 case 32: ifactor = 4; break;
407 void PlayerVideoRec::jumpToPercent(double percent)
410 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
411 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
412 switchState(S_JUMP, newFrame);
413 // stateMutex.unlock(); - let thread unlock this
416 void PlayerVideoRec::jumpToMark(int mark)
419 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
420 switchState(S_JUMP, mark);
421 // stateMutex.unlock(); - let thread unlock this
424 void PlayerVideoRec::jumpToFrameP(int newFrame)
427 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
428 switchState(S_JUMP_PI, newFrame);
432 void PlayerVideoRec::skipForward(int seconds)
435 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
436 ULONG newFrame = getCurrentFrameNum();
437 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
438 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
439 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
440 else switchState(S_JUMP, newFrame);
441 // stateMutex.unlock(); - let thread unlock this
444 void PlayerVideoRec::skipBackward(int seconds)
447 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
448 long 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 < 0) newFrame = 0;
452 switchState(S_JUMP, newFrame);
453 // stateMutex.unlock(); - let thread unlock this
456 // ----------------------------------- Implementations called events
458 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
460 if (!initted) return;
462 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
464 switch(state) // current state selector
466 case S_PLAY: // from S_PLAY -----------------------------------
470 case S_PLAY: // to S_PLAY
474 case S_PAUSE_P: // to S_PAUSE_P
476 #ifdef VOMP_PLATFORM_RASPBERRY
477 vfeed.stop(); // can't vfeed during pause
486 case S_PAUSE_I: // to S_PAUSE_I
491 case S_FFWD: // to S_FFWD
493 currentFrameNumber = getCurrentFrameNum();
494 audio->systemMuteOn();
505 case S_FBWD: // to S_FBWD
507 currentFrameNumber = getCurrentFrameNum();
508 audio->systemMuteOn();
519 case S_STOP: // to S_STOP
535 case S_JUMP: // to S_JUMP
537 restartAtFrame(jumpFrame);
540 case S_JUMP_PI: // to S_JUMP_PI
542 audio->systemMuteOn();
552 restartAtFramePI(jumpFrame);
557 FALLTHROUGH // keep compiler happy (all posibilities return)
558 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
562 case S_PLAY: // to S_PLAY
566 subtitles->unPause();
568 #ifdef VOMP_PLATFORM_RASPBERRY
575 case S_PAUSE_P: // to S_PAUSE_P
579 case S_PAUSE_I: // to S_PAUSE_I
583 case S_FFWD: // to S_FFWD
585 currentFrameNumber = getCurrentFrameNum();
586 audio->systemMuteOn();
598 case S_FBWD: // to S_FBWD
600 currentFrameNumber = getCurrentFrameNum();
601 audio->systemMuteOn();
613 case S_STOP: // to S_STOP
626 audio->systemMuteOff();
630 case S_JUMP: // to S_JUMP
633 audio->systemMuteOn();
635 restartAtFrame(jumpFrame);
638 case S_JUMP_PI: // to S_JUMP_PI
640 audio->systemMuteOn();
651 restartAtFramePI(jumpFrame);
656 FALLTHROUGH // keep compiler happy (all posibilities return)
657 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
661 case S_PLAY: // to S_PLAY
664 restartAtFrame(currentFrameNumber);
667 case S_PAUSE_P: // to S_PAUSE_P
671 case S_PAUSE_I: // to S_PAUSE_I
675 case S_FFWD: // to S_FFWD
681 case S_FBWD: // to S_FBWD
687 case S_STOP: // to S_STOP
694 audio->systemMuteOff();
698 case S_JUMP: // to S_JUMP
701 restartAtFrame(jumpFrame);
704 case S_JUMP_PI: // to S_JUMP_PI
706 restartAtFramePI(jumpFrame);
711 FALLTHROUGH // keep compiler happy (all posibilities return)
712 case S_FFWD: // from S_FFWD -----------------------------------
716 case S_PLAY: // to S_PLAY
719 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
720 if (stepback < currentFrameNumber)
721 currentFrameNumber -= stepback;
723 currentFrameNumber = 0;
724 restartAtFrame(currentFrameNumber);
727 case S_PAUSE_P: // to S_PAUSE_P
732 case S_PAUSE_I: // to S_PAUSE_I
738 case S_FFWD: // to S_FFWD
742 case S_FBWD: // to S_FBWD
749 case S_STOP: // to S_STOP
760 case S_JUMP: // to S_JUMP
763 restartAtFrame(jumpFrame);
766 case S_JUMP_PI: // to S_JUMP_PI
770 restartAtFramePI(jumpFrame);
775 FALLTHROUGH // keep compiler happy (all posibilities return)
776 case S_FBWD: // from S_FBWD -----------------------------------
780 case S_PLAY: // to S_PLAY
783 restartAtFrame(currentFrameNumber);
786 case S_PAUSE_P: // to S_PAUSE_P
791 case S_PAUSE_I: // to S_PAUSE_I
797 case S_FFWD: // to S_FFWD
804 case S_FBWD: // to S_FBWD
808 case S_STOP: // to S_STOP
819 case S_JUMP: // to S_JUMP
822 restartAtFrame(jumpFrame);
825 case S_JUMP_PI: // to S_JUMP_PI
829 restartAtFramePI(jumpFrame);
834 FALLTHROUGH // keep compiler happy (all posibilities return)
835 case S_STOP: // from S_STOP -----------------------------------
839 case S_PLAY: // to S_PLAY
844 audio->setStreamType(Audio::MPEG2_PES);
845 audio->systemMuteOff();
848 // FIXME use restartAtFrame here?
849 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
850 demuxer->setFrameNum(currentFrameNumber);
855 logger->log("Player", Log::DEBUG, "Immediate play");
866 case S_PAUSE_P: // to S_PAUSE_P
870 case S_PAUSE_I: // to S_PAUSE_I
874 case S_FFWD: // to S_FFWD
878 case S_FBWD: // to S_FBWD
882 case S_STOP: // to S_STOP
886 case S_JUMP: // to S_JUMP
890 case S_JUMP_PI: // to S_JUMP_PI
896 // case S_JUMP cannot be a start state because it auto flips to play
897 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
901 // ----------------------------------- Internal functions
903 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
913 audio->setStreamType(Audio::MPEG2_PES);
916 currentFrameNumber = newFrame;
917 demuxer->setFrameNum(newFrame);
927 audio->systemMuteOff();
932 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
935 ULONG nextiframeNumber;
943 // newFrame could be anywhere, go forwards to next I-Frame
944 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
946 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
947 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
949 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
950 if (!vdr->isConnected())
952 if (buffer) free(buffer);
957 videoLength = demuxer->stripAudio(buffer, amountReceived);
958 video->displayIFrame(buffer, videoLength);
959 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
961 currentFrameNumber = iframeNumber;
965 void PlayerVideoRec::doConnectionLost()
967 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
968 Message* m = new Message();
969 m->to = messageReceiver;
971 m->message = Message::PLAYER_EVENT;
972 m->parameter = PlayerVideoRec::CONNECTION_LOST;
973 messageQueue->postMessage(m);
976 // ----------------------------------- Callback
978 void PlayerVideoRec::call(void* caller)
980 if (caller == demuxer)
982 logger->log("Player", Log::DEBUG, "Callback from demuxer");
984 if (video->getTVsize() == Video::ASPECT4X3)
986 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
991 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
992 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
994 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
995 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
997 Message* m = new Message();
999 m->to = messageReceiver;
1000 m->message = Message::PLAYER_EVENT;
1001 m->parameter = PlayerVideoRec::ASPECT43;
1002 messageQueue->postMessage(m);
1004 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1006 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
1007 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1009 Message* m = new Message();
1011 m->to = messageReceiver;
1012 m->message = Message::PLAYER_EVENT;
1013 m->parameter = PlayerVideoRec::ASPECT169;
1014 messageQueue->postMessage(m);
1018 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1019 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1027 videoStartup = false;
1031 stateMutex.unlock();
1034 playerThreadCond.notify_one();
1038 // ----------------------------------- Feed thread
1040 void PlayerVideoRec::threadMethod()
1042 // this method used to be simple, the only thing it does
1043 // is farm out to threadFeed Live/Play/Scan
1044 // All the guff is to support scan hitting one end
1046 if ((state == S_FFWD) || (state == S_FBWD))
1049 if (video->PTSIFramePlayback()) hitEnd = threadPTSFeedScan();
1050 else hitEnd = threadFeedScan();
1052 if (!hitEnd) return; // thread wants to exit normally
1054 if (state == S_FFWD) // scan hit the end. stop
1056 if (threadReqQuit) return;
1057 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1058 m->to = messageReceiver;
1060 m->message = Message::PLAYER_EVENT;
1061 m->parameter = STOP_PLAYBACK;
1062 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1063 messageQueue->postMessage(m);
1064 logger->log("Player", Log::DEBUG, "Message posted...");
1067 // if execution gets to here, threadFeedScan hit the start, go to play mode
1070 audio->setStreamType(Audio::MPEG2_PES);
1073 demuxer->setFrameNum(currentFrameNumber);
1074 videoStartup = true;
1081 audio->systemMuteOff();
1085 if (state == S_PLAY) threadFeedPlay();
1088 void PlayerVideoRec::threadFeedPlay()
1090 ULLONG feedPosition;
1091 UINT thisRead, writeLength, thisWrite, askFor;
1092 time_t lastRescan = time(NULL);
1094 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1095 if (!vdr->isConnected()) { doConnectionLost(); return; }
1096 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1098 Buffer threadBuffer;
1100 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
1108 if (threadReqQuit) return;
1110 // If we havn't rescanned for a while..
1111 if ((lastRescan + 60) < time(NULL))
1113 lengthBytes = vdr->rescanRecording(&lengthFrames);
1114 if (!vdr->isConnected()) { doConnectionLost(); return; }
1115 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1116 lastRescan = time(NULL);
1119 if (feedPosition >= lengthBytes) break; // finished playback
1123 if (startupBlockSize > lengthBytes)
1124 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1126 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1130 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1131 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1135 //logger->log("Player", Log::DEBUG, "Get Block in");
1137 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
1138 //logger->log("Player", Log::DEBUG, "Get Block out");
1140 feedPosition += thisRead;
1142 if (!vdr->isConnected())
1148 if (threadBuffer.isNull()) break;
1152 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
1153 demuxer->setAudioStream(a_stream);
1154 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1158 if (threadReqQuit) return;
1160 while(writeLength < thisRead)
1162 //logger->log("Player", Log::DEBUG, "Put in");
1163 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
1164 //logger->log("Player", Log::DEBUG, "Put out");
1165 writeLength += thisWrite;
1169 // demuxer is full and can't take anymore
1171 if (threadReqQuit) { ul.unlock(); return; }
1172 playerThreadCond.wait(ul);
1176 if (threadReqQuit) return;
1179 threadBuffer.release();
1183 logger->log("Player", Log::DEBUG, "Recording playback ends");
1185 if (videoStartup) // oh woe. there never was a stream, I was conned!
1187 videoStartup = false;
1188 stateMutex.unlock();
1189 MILLISLEEP(500); // I think this will solve a race
1192 if (threadReqQuit) return;
1194 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1195 m->to = messageReceiver;
1197 m->message = Message::PLAYER_EVENT;
1198 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1199 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1200 messageQueue->postMessage(m);
1204 bool PlayerVideoRec::threadPTSFeedScan()
1206 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1208 ULONG direction = 0;
1210 ULONG baseFrameNumber = 0;
1211 ULONG iframeNumber = 0;
1212 ULONG iframeLength = 0;
1213 ULONG currentfeedFrameNumber=currentFrameNumber;
1214 ULONG firstFrameNumber=currentFrameNumber;
1216 UINT amountReceived;
1221 int frameTimeOffset = 0; // Time in msec between frames
1223 Buffer threadBuffer;
1225 if (state == S_FFWD)
1227 direction = 1; // and 0 for backward
1230 video->EnterIframePlayback();
1234 baseFrameNumber = currentfeedFrameNumber;
1236 if (threadReqQuit) return false;
1238 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1241 if (iframeNumber >= lengthFrames) return true;
1242 // scan has got to the end of what we knew to be there before we started scanning
1244 baseFrameNumber = iframeNumber;
1246 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1248 logger->log("Player", Log::DEBUG, "XXX Got frame");
1250 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1252 if (!vdr->isConnected())
1258 if (threadReqQuit) return false;
1260 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1261 demuxer->changeTimes(threadBuffer.ucharp(), videoLength, playtime);
1263 while (!video->displayIFrame(threadBuffer.ucharp(), videoLength)) // the device might block
1266 if (threadReqQuit) return false;
1269 ULLONG cur_time=video->getCurrentTimestamp();
1270 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1271 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1273 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1278 playtime +=frameTimeOffset;
1279 currentfeedFrameNumber = iframeNumber;
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
1289 threadBuffer.release();
1295 bool PlayerVideoRec::threadFeedScan()
1297 // This method is actually really simple - get frame from vdr,
1298 // spit it at the video chip, wait for a time. Most of the code here
1299 // is to get the wait right so that the scan occurs at the correct rate.
1301 ULONG direction = 0;
1302 ULONG baseFrameNumber = 0;
1303 ULONG iframeNumber = 0;
1304 ULONG iframeLength = 0;
1306 UINT amountReceived;
1310 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1311 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1312 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1314 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1317 int frameTimeOffset = 0; // Time in msec between frames
1318 int disp_msec = 0; // Time taken to display data
1319 int total_msec = 0; // Time taken to fetch data and display it
1322 if (state == S_FFWD) direction = 1; // and 0 for backward
1324 Buffer threadBuffer;
1328 // Fetch I-frames until we get one that can be displayed in good time
1329 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1331 baseFrameNumber = currentFrameNumber;
1334 if (threadReqQuit) return false;
1336 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1339 if (iframeNumber >= lengthFrames) return true;
1340 // scan has got to the end of what we knew to be there before we started scanning
1342 baseFrameNumber = iframeNumber;
1343 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1345 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
1348 gettimeofday(&clock0, NULL);
1350 clock0 = timeGetTime();
1354 while (clock2.tv_sec != 0 &&
1355 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1356 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1358 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1360 logger->log("Player", Log::DEBUG, "XXX Got frame");
1362 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1364 if (!vdr->isConnected() || !amountReceived)
1371 gettimeofday(&clock1, NULL);
1372 if (clock2.tv_sec != 0)
1373 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1374 + (clock2.tv_usec - clock1.tv_usec) / 1000
1375 + frameTimeOffset - disp_msec;
1377 clock1 = timeGetTime();
1379 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1381 if (sleepTime < 0) sleepTime = 0;
1382 if (threadReqQuit) return false;
1383 MILLISLEEP(sleepTime);
1384 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1386 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1387 video->displayIFrame(threadBuffer.ucharp(), videoLength);
1388 currentFrameNumber = iframeNumber;
1389 threadBuffer.release();
1392 gettimeofday(&clock2, NULL);
1393 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1394 + (clock2.tv_usec - clock0.tv_usec) / 1000
1396 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1397 + (clock2.tv_usec - clock1.tv_usec) / 1000
1400 clock2 = timeGetTime();
1401 total_msec = clock2 - clock0 - sleepTime;
1402 disp_msec = clock2 - clock1 - sleepTime;
1404 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1408 // ----------------------------------- Dev
1411 void PlayerVideoRec::test1()
1413 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1416 void PlayerVideoRec::test2()
1418 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");