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"
32 #include "playervideorec.h"
34 #define USER_RESPONSE_TIME 500 // Milliseconds
36 // ----------------------------------- Called from outside, one offs or info funcs
38 PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
39 : vfeed(this), afeed(this), tfeed(this), messageQueue(tmessageQueue),
40 messageReceiver(tmessageReceiver), osdReceiver(tosdReceiver)
42 audio = Audio::getInstance();
43 video = Video::getInstance();
44 logger = Log::getInstance();
45 vdr = VDR::getInstance();
50 PlayerVideoRec::~PlayerVideoRec()
52 if (initted) shutdown();
55 int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
57 if (initted) return 0;
58 is_pesrecording = p_isPesRecording;
59 fps = framespersecond;
61 demuxer = new DemuxerVDR();
63 demuxer = new DemuxerTS();
64 if (!demuxer) return 0;
65 subtitles = new DVBSubtitles(osdReceiver);
66 if (!subtitles) return 0;
68 teletext = new TeletextDecoderVBIEBU();
69 if (!teletext) return 0;
70 teletext->setRecordigMode(true);
71 unsigned int demux_video_size = 2097152;
72 unsigned int demux_audio_size = 524288;
73 if (video->supportsh264())
75 demux_video_size *= 5 * 2;
77 if (audio->maysupportAc3())
79 //demux_audio_size*=2;
82 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
84 logger->log("Player", Log::ERR, "Demuxer failed to init");
97 if (Command::getInstance()->getSubDefault())
98 turnSubtitlesOn(true);
100 turnSubtitlesOn(false);
106 int PlayerVideoRec::shutdown()
108 if (!initted) return 0;
122 void PlayerVideoRec::setStartFrame(ULONG startFrame)
124 ULONG nextiframeNumber;
129 // newFrame could be anywhere, go forwards to next I-Frame
130 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
132 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
133 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
135 logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
136 currentFrameNumber = iframeNumber;
139 void PlayerVideoRec::setLengthBytes(ULLONG length)
141 lengthBytes = length;
142 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
145 void PlayerVideoRec::setLengthFrames(ULONG length)
147 lengthFrames = length;
148 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
151 ULONG PlayerVideoRec::getLengthFrames()
156 ULONG PlayerVideoRec::getCurrentFrameNum()
158 if (startup) return 0;
163 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
167 return currentFrameNumber;
169 return 0; // shouldn't happen
173 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
175 return demuxer->getmpAudioChannels();
178 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
180 return demuxer->getac3AudioChannels();
183 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
185 return demuxer->getSubtitleChannels();
188 int PlayerVideoRec::getCurrentAudioChannel()
191 return demuxer->getselAudioChannel();
193 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
196 int PlayerVideoRec::getCurrentSubtitleChannel()
199 return demuxer->getselSubtitleChannel();
201 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
204 void PlayerVideoRec::setSubtitleChannel(int newChannel)
207 demuxer->setDVBSubtitleStream(newChannel);
209 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
212 int *PlayerVideoRec::getTeletxtSubtitlePages()
214 return teletext->getSubtitlePages();
217 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
221 demuxer->setAudioStream(newChannel);
226 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
231 bool PlayerVideoRec::toggleSubtitles()
233 if (!subtitlesShowing)
235 subtitlesShowing = true;
240 subtitlesShowing = false;
243 return subtitlesShowing;
246 void PlayerVideoRec::turnSubtitlesOn(bool ison)
250 subtitlesShowing = true;
255 subtitlesShowing = false;
260 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
262 subtitles->setOSDMenuVisibility(visible);
265 Channel * PlayerVideoRec::getDemuxerChannel()
267 if (!is_pesrecording)
269 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
271 return NULL; //Should not happen!
275 // ----------------------------------- Externally called events
277 void PlayerVideoRec::play()
279 if (!initted) return;
280 if (state == S_PLAY) return;
283 bool doUnlock = false;
284 if (state == S_PAUSE_P) doUnlock = true;
286 if (doUnlock) stateMutex.unlock();
289 void PlayerVideoRec::playpause()
291 if (!initted) return;
294 bool doUnlock = false;
298 switchState(S_PAUSE_P);
302 if (state == S_PAUSE_P) doUnlock = true;
305 if (doUnlock) stateMutex.unlock();
308 void PlayerVideoRec::stop()
310 if (!initted) return;
311 if (state == S_STOP) return;
313 logger->log("Player", Log::DEBUG, "Stop called lock");
318 void PlayerVideoRec::pause()
320 if (!initted) return;
323 if ((state == S_FFWD) || (state == S_FBWD))
325 switchState(S_PAUSE_I);
327 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
333 switchState(S_PAUSE_P);
339 void PlayerVideoRec::fastForward()
341 if (!initted) return;
349 case 4: ifactor = 8; break;
350 case 8: ifactor = 16; break;
351 case 16: ifactor = 32; break;
352 case 32: ifactor = 4; break;
363 void PlayerVideoRec::fastBackward()
365 if (!initted) return;
373 case 4: ifactor = 8; break;
374 case 8: ifactor = 16; break;
375 case 16: ifactor = 32; break;
376 case 32: ifactor = 4; break;
387 void PlayerVideoRec::jumpToPercent(double percent)
390 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
391 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
392 switchState(S_JUMP, newFrame);
393 // stateMutex.unlock(); - let thread unlock this
396 void PlayerVideoRec::jumpToMark(int mark)
399 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
400 switchState(S_JUMP, mark);
401 // stateMutex.unlock(); - let thread unlock this
404 void PlayerVideoRec::jumpToFrameP(int newFrame)
407 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
408 switchState(S_JUMP_PI, newFrame);
412 void PlayerVideoRec::skipForward(int seconds)
415 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
416 ULONG newFrame = getCurrentFrameNum();
417 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
418 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
419 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
420 else switchState(S_JUMP, newFrame);
421 // stateMutex.unlock(); - let thread unlock this
424 void PlayerVideoRec::skipBackward(int seconds)
427 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
428 long newFrame = getCurrentFrameNum();
429 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
430 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
431 if (newFrame < 0) newFrame = 0;
432 switchState(S_JUMP, newFrame);
433 // stateMutex.unlock(); - let thread unlock this
436 // ----------------------------------- Implementations called events
438 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
440 if (!initted) return;
442 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
444 switch(state) // current state selector
446 case S_PLAY: // from S_PLAY -----------------------------------
450 case S_PLAY: // to S_PLAY
454 case S_PAUSE_P: // to S_PAUSE_P
456 #ifdef VOMP_PLATFORM_RASPBERRY
457 vfeed.stop(); // can't vfeed during pause
465 case S_PAUSE_I: // to S_PAUSE_I
470 case S_FFWD: // to S_FFWD
472 currentFrameNumber = getCurrentFrameNum();
473 audio->systemMuteOn();
484 case S_FBWD: // to S_FBWD
486 currentFrameNumber = getCurrentFrameNum();
487 audio->systemMuteOn();
498 case S_STOP: // to S_STOP
514 case S_JUMP: // to S_JUMP
516 restartAtFrame(jumpFrame);
519 case S_JUMP_PI: // to S_JUMP_PI
521 audio->systemMuteOn();
531 restartAtFramePI(jumpFrame);
536 FALLTHROUGH // keep compiler happy (all posibilities return)
537 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
541 case S_PLAY: // to S_PLAY
546 #ifdef VOMP_PLATFORM_RASPBERRY
553 case S_PAUSE_P: // to S_PAUSE_P
557 case S_PAUSE_I: // to S_PAUSE_I
561 case S_FFWD: // to S_FFWD
563 currentFrameNumber = getCurrentFrameNum();
564 audio->systemMuteOn();
576 case S_FBWD: // to S_FBWD
578 currentFrameNumber = getCurrentFrameNum();
579 audio->systemMuteOn();
591 case S_STOP: // to S_STOP
604 audio->systemMuteOff();
608 case S_JUMP: // to S_JUMP
611 audio->systemMuteOn();
613 restartAtFrame(jumpFrame);
616 case S_JUMP_PI: // to S_JUMP_PI
618 audio->systemMuteOn();
629 restartAtFramePI(jumpFrame);
634 FALLTHROUGH // keep compiler happy (all posibilities return)
635 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
639 case S_PLAY: // to S_PLAY
642 restartAtFrame(currentFrameNumber);
645 case S_PAUSE_P: // to S_PAUSE_P
649 case S_PAUSE_I: // to S_PAUSE_I
653 case S_FFWD: // to S_FFWD
659 case S_FBWD: // to S_FBWD
665 case S_STOP: // to S_STOP
672 audio->systemMuteOff();
676 case S_JUMP: // to S_JUMP
679 restartAtFrame(jumpFrame);
682 case S_JUMP_PI: // to S_JUMP_PI
684 restartAtFramePI(jumpFrame);
689 FALLTHROUGH // keep compiler happy (all posibilities return)
690 case S_FFWD: // from S_FFWD -----------------------------------
694 case S_PLAY: // to S_PLAY
697 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
698 if (stepback < currentFrameNumber)
699 currentFrameNumber -= stepback;
701 currentFrameNumber = 0;
702 restartAtFrame(currentFrameNumber);
705 case S_PAUSE_P: // to S_PAUSE_P
710 case S_PAUSE_I: // to S_PAUSE_I
716 case S_FFWD: // to S_FFWD
720 case S_FBWD: // to S_FBWD
727 case S_STOP: // to S_STOP
738 case S_JUMP: // to S_JUMP
741 restartAtFrame(jumpFrame);
744 case S_JUMP_PI: // to S_JUMP_PI
748 restartAtFramePI(jumpFrame);
753 FALLTHROUGH // keep compiler happy (all posibilities return)
754 case S_FBWD: // from S_FBWD -----------------------------------
758 case S_PLAY: // to S_PLAY
761 restartAtFrame(currentFrameNumber);
764 case S_PAUSE_P: // to S_PAUSE_P
769 case S_PAUSE_I: // to S_PAUSE_I
775 case S_FFWD: // to S_FFWD
782 case S_FBWD: // to S_FBWD
786 case S_STOP: // to S_STOP
797 case S_JUMP: // to S_JUMP
800 restartAtFrame(jumpFrame);
803 case S_JUMP_PI: // to S_JUMP_PI
807 restartAtFramePI(jumpFrame);
812 FALLTHROUGH // keep compiler happy (all posibilities return)
813 case S_STOP: // from S_STOP -----------------------------------
817 case S_PLAY: // to S_PLAY
822 audio->setStreamType(Audio::MPEG2_PES);
823 audio->systemMuteOff();
826 // FIXME use restartAtFrame here?
827 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
828 demuxer->setFrameNum(currentFrameNumber);
833 logger->log("Player", Log::DEBUG, "Immediate play");
844 case S_PAUSE_P: // to S_PAUSE_P
848 case S_PAUSE_I: // to S_PAUSE_I
852 case S_FFWD: // to S_FFWD
856 case S_FBWD: // to S_FBWD
860 case S_STOP: // to S_STOP
864 case S_JUMP: // to S_JUMP
868 case S_JUMP_PI: // to S_JUMP_PI
874 // case S_JUMP cannot be a start state because it auto flips to play
875 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
879 // ----------------------------------- Internal functions
881 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
891 audio->setStreamType(Audio::MPEG2_PES);
894 currentFrameNumber = newFrame;
895 demuxer->setFrameNum(newFrame);
905 audio->systemMuteOff();
910 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
913 ULONG nextiframeNumber;
921 // newFrame could be anywhere, go forwards to next I-Frame
922 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
924 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
925 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
927 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
928 if (!vdr->isConnected())
930 if (buffer) free(buffer);
935 videoLength = demuxer->stripAudio(buffer, amountReceived);
936 video->displayIFrame(buffer, videoLength);
937 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
939 currentFrameNumber = iframeNumber;
943 void PlayerVideoRec::doConnectionLost()
945 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
946 Message* m = new Message();
947 m->to = messageReceiver;
949 m->message = Message::PLAYER_EVENT;
950 m->parameter = PlayerVideoRec::CONNECTION_LOST;
951 messageQueue->postMessage(m);
954 // ----------------------------------- Callback
956 void PlayerVideoRec::call(void* caller)
958 if (caller == demuxer)
960 logger->log("Player", Log::DEBUG, "Callback from demuxer");
962 if (video->getTVsize() == Video::ASPECT4X3)
964 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
969 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
970 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
972 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
973 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
975 Message* m = new Message();
977 m->to = messageReceiver;
978 m->message = Message::PLAYER_EVENT;
979 m->parameter = PlayerVideoRec::ASPECT43;
980 messageQueue->postMessage(m);
982 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
984 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
985 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
987 Message* m = new Message();
989 m->to = messageReceiver;
990 m->message = Message::PLAYER_EVENT;
991 m->parameter = PlayerVideoRec::ASPECT169;
992 messageQueue->postMessage(m);
996 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
997 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1005 videoStartup = false;
1010 stateMutex.unlock();
1013 threadSignalNoLock();
1017 // ----------------------------------- Feed thread
1019 void PlayerVideoRec::threadMethod()
1021 // this method used to be simple, the only thing it does
1022 // is farm out to threadFeed Live/Play/Scan
1023 // All the guff is to support scan hitting one end
1025 if ((state == S_FFWD) || (state == S_FBWD))
1027 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1028 else threadFeedScan();
1029 // if this returns then scan hit one end
1030 if (state == S_FFWD) // scan hit the end. stop
1033 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1034 m->to = messageReceiver;
1036 m->message = Message::PLAYER_EVENT;
1037 m->parameter = STOP_PLAYBACK;
1038 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1039 messageQueue->postMessage(m);
1040 logger->log("Player", Log::DEBUG, "Message posted...");
1043 // if execution gets to here, threadFeedScan hit the start, go to play mode
1046 audio->setStreamType(Audio::MPEG2_PES);
1049 demuxer->setFrameNum(currentFrameNumber);
1050 videoStartup = true;
1057 audio->systemMuteOff();
1061 if (state == S_PLAY) threadFeedPlay();
1064 void PlayerVideoRec::threadFeedPlay()
1066 ULLONG feedPosition;
1067 UINT thisRead, writeLength, thisWrite, askFor;
1068 time_t lastRescan = time(NULL);
1070 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1071 if (!vdr->isConnected()) { doConnectionLost(); return; }
1072 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1083 // If we havn't rescanned for a while..
1084 if ((lastRescan + 60) < time(NULL))
1086 lengthBytes = vdr->rescanRecording(&lengthFrames);
1087 if (!vdr->isConnected()) { doConnectionLost(); return; }
1088 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1089 lastRescan = time(NULL);
1092 if (feedPosition >= lengthBytes) break; // finished playback
1096 if (startupBlockSize > lengthBytes)
1097 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1099 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1103 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1104 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1108 //logger->log("Player", Log::DEBUG, "Get Block in");
1110 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1111 //logger->log("Player", Log::DEBUG, "Get Block out");
1113 feedPosition += thisRead;
1115 if (!vdr->isConnected())
1121 if (!threadBuffer) break;
1125 int a_stream = demuxer->scan(threadBuffer, thisRead);
1126 demuxer->setAudioStream(a_stream);
1127 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1133 while(writeLength < thisRead)
1135 //logger->log("Player", Log::DEBUG, "Put in");
1136 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1137 //logger->log("Player", Log::DEBUG, "Put out");
1138 writeLength += thisWrite;
1142 // demuxer is full and can't take anymore
1144 if (!threadActive) { threadUnlock(); threadCheckExit(); }
1145 threadWaitForSignal();
1153 threadBuffer = NULL;
1158 logger->log("Player", Log::DEBUG, "Recording playback ends");
1160 if (videoStartup) // oh woe. there never was a stream, I was conned!
1162 videoStartup = false;
1163 stateMutex.unlock();
1164 MILLISLEEP(500); // I think this will solve a race
1170 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1171 m->to = messageReceiver;
1173 m->message = Message::PLAYER_EVENT;
1174 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1175 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1176 messageQueue->postMessage(m);
1180 void PlayerVideoRec::threadPTSFeedScan()
1182 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1184 ULONG direction = 0;
1186 ULONG baseFrameNumber = 0;
1187 ULONG iframeNumber = 0;
1188 ULONG iframeLength = 0;
1189 ULONG currentfeedFrameNumber=currentFrameNumber;
1190 ULONG firstFrameNumber=currentFrameNumber;
1192 UINT amountReceived;
1197 int frameTimeOffset = 0; // Time in msec between frames
1199 if (state == S_FFWD)
1201 direction = 1; // and 0 for backward
1204 video->EnterIframePlayback();
1208 baseFrameNumber = currentfeedFrameNumber;
1211 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1214 if (iframeNumber >= lengthFrames) return;
1215 // scan has got to the end of what we knew to be there before we started scanning
1217 baseFrameNumber = iframeNumber;
1219 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
1221 logger->log("Player", Log::DEBUG, "XXX Got frame");
1223 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1225 if (!vdr->isConnected())
1227 if (threadBuffer) free(threadBuffer);
1236 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1237 demuxer->changeTimes(threadBuffer,videoLength,playtime);
1239 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
1245 ULLONG cur_time=video->getCurrentTimestamp();
1246 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1247 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1249 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1254 playtime +=frameTimeOffset;
1255 currentfeedFrameNumber = iframeNumber;
1257 ULLONG cur_time=video->getCurrentTimestamp();
1258 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1259 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1261 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
1266 threadBuffer = NULL;
1272 void PlayerVideoRec::threadFeedScan()
1274 // This method is actually really simple - get frame from vdr,
1275 // spit it at the video chip, wait for a time. Most of the code here
1276 // is to get the wait right so that the scan occurs at the correct rate.
1278 ULONG direction = 0;
1279 ULONG baseFrameNumber = 0;
1280 ULONG iframeNumber = 0;
1281 ULONG iframeLength = 0;
1283 UINT amountReceived;
1287 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1288 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1289 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1291 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1294 int frameTimeOffset = 0; // Time in msec between frames
1295 int disp_msec = 0; // Time taken to display data
1296 int total_msec = 0; // Time taken to fetch data and display it
1299 if (state == S_FFWD) direction = 1; // and 0 for backward
1303 // Fetch I-frames until we get one that can be displayed in good time
1304 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1306 baseFrameNumber = currentFrameNumber;
1310 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1313 if (iframeNumber >= lengthFrames) return;
1314 // scan has got to the end of what we knew to be there before we started scanning
1316 baseFrameNumber = iframeNumber;
1317 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
1319 gettimeofday(&clock0, NULL);
1321 clock0 = timeGetTime();
1325 while (clock2.tv_sec != 0 &&
1326 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1327 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1329 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1331 logger->log("Player", Log::DEBUG, "XXX Got frame");
1333 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1335 if (!vdr->isConnected() || !amountReceived)
1337 if (threadBuffer) free(threadBuffer);
1343 gettimeofday(&clock1, NULL);
1344 if (clock2.tv_sec != 0)
1345 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1346 + (clock2.tv_usec - clock1.tv_usec) / 1000
1347 + frameTimeOffset - disp_msec;
1349 clock1 = timeGetTime();
1351 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1353 if (sleepTime < 0) sleepTime = 0;
1355 MILLISLEEP(sleepTime);
1356 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1358 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1359 video->displayIFrame(threadBuffer, videoLength);
1360 currentFrameNumber = iframeNumber;
1362 threadBuffer = NULL;
1365 gettimeofday(&clock2, NULL);
1366 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1367 + (clock2.tv_usec - clock0.tv_usec) / 1000
1369 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1370 + (clock2.tv_usec - clock1.tv_usec) / 1000
1373 clock2 = timeGetTime();
1374 total_msec = clock2 - clock0 - sleepTime;
1375 disp_msec = clock2 - clock1 - sleepTime;
1377 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1381 void PlayerVideoRec::threadPostStopCleanup()
1386 threadBuffer = NULL;
1390 // ----------------------------------- Dev
1393 void PlayerVideoRec::test1()
1395 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1398 void PlayerVideoRec::test2()
1400 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");