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"
33 #include "playervideorec.h"
35 #define USER_RESPONSE_TIME 500 // Milliseconds
37 // ----------------------------------- Called from outside, one offs or info funcs
39 PlayerVideoRec::PlayerVideoRec(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
40 : vfeed(this), afeed(this), tfeed(this),
41 osdReceiver(tosdReceiver), messageQueue(tmessageQueue), messageReceiver(tmessageReceiver)
43 audio = Audio::getInstance();
44 video = Video::getInstance();
45 logger = Log::getInstance();
46 vdr = VDR::getInstance();
51 PlayerVideoRec::~PlayerVideoRec()
53 if (initted) shutdown();
56 int PlayerVideoRec::init(bool p_isPesRecording, double framespersecond)
58 if (initted) return 0;
59 is_pesrecording = p_isPesRecording;
60 fps = framespersecond;
62 demuxer = new DemuxerVDR();
64 demuxer = new DemuxerTS();
65 if (!demuxer) return 0;
66 subtitles = new DVBSubtitles(osdReceiver);
67 if (!subtitles) return 0;
69 teletext = new TeletextDecoderVBIEBU();
70 if (!teletext) return 0;
71 teletext->setRecordigMode(true);
72 unsigned int demux_video_size = 2097152;
73 unsigned int demux_audio_size = 524288;
74 if (video->supportsh264())
76 demux_video_size *= 5 * 2;
78 if (audio->maysupportAc3())
80 //demux_audio_size*=2;
83 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
85 logger->log("Player", Log::ERR, "Demuxer failed to init");
94 if (Command::getInstance()->getSubDefault())
95 turnSubtitlesOn(true);
97 turnSubtitlesOn(false);
103 int PlayerVideoRec::shutdown()
105 if (!initted) return 0;
119 void PlayerVideoRec::setStartFrame(ULONG startFrame)
121 ULONG nextiframeNumber;
126 // newFrame could be anywhere, go forwards to next I-Frame
127 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
129 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
130 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
132 logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
133 currentFrameNumber = iframeNumber;
136 void PlayerVideoRec::setLengthBytes(ULLONG length)
138 lengthBytes = length;
139 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
142 void PlayerVideoRec::setLengthFrames(ULONG length)
144 lengthFrames = length;
145 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
148 ULONG PlayerVideoRec::getLengthFrames()
153 ULONG PlayerVideoRec::getCurrentFrameNum()
155 if (startup) return 0;
160 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
164 return currentFrameNumber;
166 return 0; // shouldn't happen
170 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
172 return demuxer->getmpAudioChannels();
175 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
177 return demuxer->getac3AudioChannels();
180 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
182 return demuxer->getSubtitleChannels();
185 int PlayerVideoRec::getCurrentAudioChannel()
188 return demuxer->getselAudioChannel();
190 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
193 int PlayerVideoRec::getCurrentSubtitleChannel()
196 return demuxer->getselSubtitleChannel();
198 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
201 void PlayerVideoRec::setSubtitleChannel(int newChannel)
204 demuxer->setDVBSubtitleStream(newChannel);
206 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
209 int *PlayerVideoRec::getTeletxtSubtitlePages()
211 return teletext->getSubtitlePages();
214 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
218 demuxer->setAudioStream(newChannel);
223 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
228 bool PlayerVideoRec::toggleSubtitles()
230 if (!subtitlesShowing)
232 subtitlesShowing = true;
237 subtitlesShowing = false;
240 return subtitlesShowing;
243 void PlayerVideoRec::turnSubtitlesOn(bool ison)
247 subtitlesShowing = true;
252 subtitlesShowing = false;
257 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
259 subtitles->setOSDMenuVisibility(visible);
262 Channel * PlayerVideoRec::getDemuxerChannel()
264 if (!is_pesrecording)
266 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
268 return NULL; //Should not happen!
272 // ----------------------------------- Externally called events
274 void PlayerVideoRec::play()
276 if (!initted) return;
277 if (state == S_PLAY) return;
280 bool doUnlock = false;
281 if (state == S_PAUSE_P) doUnlock = true;
283 if (doUnlock) stateMutex.unlock();
286 void PlayerVideoRec::playpause()
288 if (!initted) return;
291 bool doUnlock = false;
295 switchState(S_PAUSE_P);
299 if (state == S_PAUSE_P) doUnlock = true;
302 if (doUnlock) stateMutex.unlock();
305 void PlayerVideoRec::stop()
307 if (!initted) return;
308 if (state == S_STOP) return;
310 logger->log("Player", Log::DEBUG, "Stop called lock");
315 void PlayerVideoRec::pause()
317 if (!initted) return;
320 if ((state == S_FFWD) || (state == S_FBWD))
322 switchState(S_PAUSE_I);
324 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
330 switchState(S_PAUSE_P);
336 void PlayerVideoRec::fastForward()
338 if (!initted) return;
346 case 4: ifactor = 8; break;
347 case 8: ifactor = 16; break;
348 case 16: ifactor = 32; break;
349 case 32: ifactor = 4; break;
360 void PlayerVideoRec::fastBackward()
362 if (!initted) return;
370 case 4: ifactor = 8; break;
371 case 8: ifactor = 16; break;
372 case 16: ifactor = 32; break;
373 case 32: ifactor = 4; break;
384 void PlayerVideoRec::jumpToPercent(double percent)
387 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
388 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
389 switchState(S_JUMP, newFrame);
390 // stateMutex.unlock(); - let thread unlock this
393 void PlayerVideoRec::jumpToMark(int mark)
396 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
397 switchState(S_JUMP, mark);
398 // stateMutex.unlock(); - let thread unlock this
401 void PlayerVideoRec::jumpToFrameP(int newFrame)
404 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
405 switchState(S_JUMP_PI, newFrame);
409 void PlayerVideoRec::skipForward(int seconds)
412 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
413 ULONG newFrame = getCurrentFrameNum();
414 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
415 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
416 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
417 else switchState(S_JUMP, newFrame);
418 // stateMutex.unlock(); - let thread unlock this
421 void PlayerVideoRec::skipBackward(int seconds)
424 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
425 long newFrame = getCurrentFrameNum();
426 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
427 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
428 if (newFrame < 0) newFrame = 0;
429 switchState(S_JUMP, newFrame);
430 // stateMutex.unlock(); - let thread unlock this
433 // ----------------------------------- Implementations called events
435 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
437 if (!initted) return;
439 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
441 switch(state) // current state selector
443 case S_PLAY: // from S_PLAY -----------------------------------
447 case S_PLAY: // to S_PLAY
451 case S_PAUSE_P: // to S_PAUSE_P
453 #ifdef VOMP_PLATFORM_RASPBERRY
454 vfeed.stop(); // can't vfeed during pause
463 case S_PAUSE_I: // to S_PAUSE_I
468 case S_FFWD: // to S_FFWD
470 currentFrameNumber = getCurrentFrameNum();
471 audio->systemMuteOn();
482 case S_FBWD: // to S_FBWD
484 currentFrameNumber = getCurrentFrameNum();
485 audio->systemMuteOn();
496 case S_STOP: // to S_STOP
512 case S_JUMP: // to S_JUMP
514 restartAtFrame(jumpFrame);
517 case S_JUMP_PI: // to S_JUMP_PI
519 audio->systemMuteOn();
529 restartAtFramePI(jumpFrame);
534 FALLTHROUGH // keep compiler happy (all posibilities return)
535 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
539 case S_PLAY: // to S_PLAY
543 subtitles->unPause();
545 #ifdef VOMP_PLATFORM_RASPBERRY
552 case S_PAUSE_P: // to S_PAUSE_P
556 case S_PAUSE_I: // to S_PAUSE_I
560 case S_FFWD: // to S_FFWD
562 currentFrameNumber = getCurrentFrameNum();
563 audio->systemMuteOn();
575 case S_FBWD: // to S_FBWD
577 currentFrameNumber = getCurrentFrameNum();
578 audio->systemMuteOn();
590 case S_STOP: // to S_STOP
603 audio->systemMuteOff();
607 case S_JUMP: // to S_JUMP
610 audio->systemMuteOn();
612 restartAtFrame(jumpFrame);
615 case S_JUMP_PI: // to S_JUMP_PI
617 audio->systemMuteOn();
628 restartAtFramePI(jumpFrame);
633 FALLTHROUGH // keep compiler happy (all posibilities return)
634 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
638 case S_PLAY: // to S_PLAY
641 restartAtFrame(currentFrameNumber);
644 case S_PAUSE_P: // to S_PAUSE_P
648 case S_PAUSE_I: // to S_PAUSE_I
652 case S_FFWD: // to S_FFWD
658 case S_FBWD: // to S_FBWD
664 case S_STOP: // to S_STOP
671 audio->systemMuteOff();
675 case S_JUMP: // to S_JUMP
678 restartAtFrame(jumpFrame);
681 case S_JUMP_PI: // to S_JUMP_PI
683 restartAtFramePI(jumpFrame);
688 FALLTHROUGH // keep compiler happy (all posibilities return)
689 case S_FFWD: // from S_FFWD -----------------------------------
693 case S_PLAY: // to S_PLAY
696 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
697 if (stepback < currentFrameNumber)
698 currentFrameNumber -= stepback;
700 currentFrameNumber = 0;
701 restartAtFrame(currentFrameNumber);
704 case S_PAUSE_P: // to S_PAUSE_P
709 case S_PAUSE_I: // to S_PAUSE_I
715 case S_FFWD: // to S_FFWD
719 case S_FBWD: // to S_FBWD
726 case S_STOP: // to S_STOP
737 case S_JUMP: // to S_JUMP
740 restartAtFrame(jumpFrame);
743 case S_JUMP_PI: // to S_JUMP_PI
747 restartAtFramePI(jumpFrame);
752 FALLTHROUGH // keep compiler happy (all posibilities return)
753 case S_FBWD: // from S_FBWD -----------------------------------
757 case S_PLAY: // to S_PLAY
760 restartAtFrame(currentFrameNumber);
763 case S_PAUSE_P: // to S_PAUSE_P
768 case S_PAUSE_I: // to S_PAUSE_I
774 case S_FFWD: // to S_FFWD
781 case S_FBWD: // to S_FBWD
785 case S_STOP: // to S_STOP
796 case S_JUMP: // to S_JUMP
799 restartAtFrame(jumpFrame);
802 case S_JUMP_PI: // to S_JUMP_PI
806 restartAtFramePI(jumpFrame);
811 FALLTHROUGH // keep compiler happy (all posibilities return)
812 case S_STOP: // from S_STOP -----------------------------------
816 case S_PLAY: // to S_PLAY
821 audio->setStreamType(Audio::MPEG2_PES);
822 audio->systemMuteOff();
825 // FIXME use restartAtFrame here?
826 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
827 demuxer->setFrameNum(currentFrameNumber);
832 logger->log("Player", Log::DEBUG, "Immediate play");
843 case S_PAUSE_P: // to S_PAUSE_P
847 case S_PAUSE_I: // to S_PAUSE_I
851 case S_FFWD: // to S_FFWD
855 case S_FBWD: // to S_FBWD
859 case S_STOP: // to S_STOP
863 case S_JUMP: // to S_JUMP
867 case S_JUMP_PI: // to S_JUMP_PI
873 // case S_JUMP cannot be a start state because it auto flips to play
874 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
878 // ----------------------------------- Internal functions
880 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
890 audio->setStreamType(Audio::MPEG2_PES);
893 currentFrameNumber = newFrame;
894 demuxer->setFrameNum(newFrame);
904 audio->systemMuteOff();
909 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
912 ULONG nextiframeNumber;
920 // newFrame could be anywhere, go forwards to next I-Frame
921 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
923 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
924 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
926 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
927 if (!vdr->isConnected())
929 if (buffer) free(buffer);
934 videoLength = demuxer->stripAudio(buffer, amountReceived);
935 video->displayIFrame(buffer, videoLength);
936 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
938 currentFrameNumber = iframeNumber;
942 void PlayerVideoRec::doConnectionLost()
944 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
945 Message* m = new Message();
946 m->to = messageReceiver;
948 m->message = Message::PLAYER_EVENT;
949 m->parameter = PlayerVideoRec::CONNECTION_LOST;
950 messageQueue->postMessage(m);
953 // ----------------------------------- Callback
955 void PlayerVideoRec::call(void* caller)
957 if (caller == demuxer)
959 logger->log("Player", Log::DEBUG, "Callback from demuxer");
961 if (video->getTVsize() == Video::ASPECT4X3)
963 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
968 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
969 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
971 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
972 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
974 Message* m = new Message();
976 m->to = messageReceiver;
977 m->message = Message::PLAYER_EVENT;
978 m->parameter = PlayerVideoRec::ASPECT43;
979 messageQueue->postMessage(m);
981 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
983 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
984 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
986 Message* m = new Message();
988 m->to = messageReceiver;
989 m->message = Message::PLAYER_EVENT;
990 m->parameter = PlayerVideoRec::ASPECT169;
991 messageQueue->postMessage(m);
995 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
996 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1004 videoStartup = false;
1008 stateMutex.unlock();
1011 threadSignalNoLock();
1015 // ----------------------------------- Feed thread
1017 void PlayerVideoRec::threadMethod()
1019 // this method used to be simple, the only thing it does
1020 // is farm out to threadFeed Live/Play/Scan
1021 // All the guff is to support scan hitting one end
1023 if ((state == S_FFWD) || (state == S_FBWD))
1025 if (video->PTSIFramePlayback()) threadPTSFeedScan();
1026 else threadFeedScan();
1027 // if this returns then scan hit one end
1028 if (state == S_FFWD) // scan hit the end. stop
1031 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1032 m->to = messageReceiver;
1034 m->message = Message::PLAYER_EVENT;
1035 m->parameter = STOP_PLAYBACK;
1036 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1037 messageQueue->postMessage(m);
1038 logger->log("Player", Log::DEBUG, "Message posted...");
1041 // if execution gets to here, threadFeedScan hit the start, go to play mode
1044 audio->setStreamType(Audio::MPEG2_PES);
1047 demuxer->setFrameNum(currentFrameNumber);
1048 videoStartup = true;
1055 audio->systemMuteOff();
1059 if (state == S_PLAY) threadFeedPlay();
1062 void PlayerVideoRec::threadFeedPlay()
1064 ULLONG feedPosition;
1065 UINT thisRead, writeLength, thisWrite, askFor;
1066 time_t lastRescan = time(NULL);
1068 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1069 if (!vdr->isConnected()) { doConnectionLost(); return; }
1070 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1072 Buffer threadBuffer;
1082 // If we havn't rescanned for a while..
1083 if ((lastRescan + 60) < time(NULL))
1085 lengthBytes = vdr->rescanRecording(&lengthFrames);
1086 if (!vdr->isConnected()) { doConnectionLost(); return; }
1087 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1088 lastRescan = time(NULL);
1091 if (feedPosition >= lengthBytes) break; // finished playback
1095 if (startupBlockSize > lengthBytes)
1096 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1098 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1102 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1103 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1107 //logger->log("Player", Log::DEBUG, "Get Block in");
1109 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
1110 //logger->log("Player", Log::DEBUG, "Get Block out");
1112 feedPosition += thisRead;
1114 if (!vdr->isConnected())
1120 if (threadBuffer.isNull()) break;
1124 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
1125 demuxer->setAudioStream(a_stream);
1126 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1132 while(writeLength < thisRead)
1134 //logger->log("Player", Log::DEBUG, "Put in");
1135 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
1136 //logger->log("Player", Log::DEBUG, "Put out");
1137 writeLength += thisWrite;
1141 // demuxer is full and can't take anymore
1143 if (!threadActive) { threadUnlock(); threadCheckExit(); }
1144 threadWaitForSignal();
1151 threadBuffer.release();
1155 logger->log("Player", Log::DEBUG, "Recording playback ends");
1157 if (videoStartup) // oh woe. there never was a stream, I was conned!
1159 videoStartup = false;
1160 stateMutex.unlock();
1161 MILLISLEEP(500); // I think this will solve a race
1167 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1168 m->to = messageReceiver;
1170 m->message = Message::PLAYER_EVENT;
1171 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1172 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1173 messageQueue->postMessage(m);
1177 void PlayerVideoRec::threadPTSFeedScan()
1179 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1181 ULONG direction = 0;
1183 ULONG baseFrameNumber = 0;
1184 ULONG iframeNumber = 0;
1185 ULONG iframeLength = 0;
1186 ULONG currentfeedFrameNumber=currentFrameNumber;
1187 ULONG firstFrameNumber=currentFrameNumber;
1189 UINT amountReceived;
1194 int frameTimeOffset = 0; // Time in msec between frames
1196 Buffer threadBuffer;
1198 if (state == S_FFWD)
1200 direction = 1; // and 0 for backward
1203 video->EnterIframePlayback();
1207 baseFrameNumber = currentfeedFrameNumber;
1210 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1213 if (iframeNumber >= lengthFrames) return;
1214 // scan has got to the end of what we knew to be there before we started scanning
1216 baseFrameNumber = iframeNumber;
1218 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1220 logger->log("Player", Log::DEBUG, "XXX Got frame");
1222 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1224 if (!vdr->isConnected())
1232 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1233 demuxer->changeTimes(threadBuffer.ucharp(), videoLength, playtime);
1235 while (!video->displayIFrame(threadBuffer.ucharp(), videoLength)) // the device might block
1241 ULLONG cur_time=video->getCurrentTimestamp();
1242 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1243 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1245 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1250 playtime +=frameTimeOffset;
1251 currentfeedFrameNumber = iframeNumber;
1253 ULLONG cur_time=video->getCurrentTimestamp();
1254 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1255 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1257 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1261 threadBuffer.release();
1267 void PlayerVideoRec::threadFeedScan()
1269 // This method is actually really simple - get frame from vdr,
1270 // spit it at the video chip, wait for a time. Most of the code here
1271 // is to get the wait right so that the scan occurs at the correct rate.
1273 ULONG direction = 0;
1274 ULONG baseFrameNumber = 0;
1275 ULONG iframeNumber = 0;
1276 ULONG iframeLength = 0;
1278 UINT amountReceived;
1282 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1283 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1284 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1286 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1289 int frameTimeOffset = 0; // Time in msec between frames
1290 int disp_msec = 0; // Time taken to display data
1291 int total_msec = 0; // Time taken to fetch data and display it
1294 if (state == S_FFWD) direction = 1; // and 0 for backward
1296 Buffer threadBuffer;
1300 // Fetch I-frames until we get one that can be displayed in good time
1301 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1303 baseFrameNumber = currentFrameNumber;
1307 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1310 if (iframeNumber >= lengthFrames) return;
1311 // scan has got to the end of what we knew to be there before we started scanning
1313 baseFrameNumber = iframeNumber;
1314 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1316 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
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.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1335 if (!vdr->isConnected() || !amountReceived)
1342 gettimeofday(&clock1, NULL);
1343 if (clock2.tv_sec != 0)
1344 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1345 + (clock2.tv_usec - clock1.tv_usec) / 1000
1346 + frameTimeOffset - disp_msec;
1348 clock1 = timeGetTime();
1350 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1352 if (sleepTime < 0) sleepTime = 0;
1354 MILLISLEEP(sleepTime);
1355 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1357 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1358 video->displayIFrame(threadBuffer.ucharp(), videoLength);
1359 currentFrameNumber = iframeNumber;
1360 threadBuffer.release();
1363 gettimeofday(&clock2, NULL);
1364 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1365 + (clock2.tv_usec - clock0.tv_usec) / 1000
1367 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1368 + (clock2.tv_usec - clock1.tv_usec) / 1000
1371 clock2 = timeGetTime();
1372 total_msec = clock2 - clock0 - sleepTime;
1373 disp_msec = clock2 - clock1 - sleepTime;
1375 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1379 // ----------------------------------- Dev
1382 void PlayerVideoRec::test1()
1384 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1387 void PlayerVideoRec::test2()
1389 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");