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 (Control::getInstance()->getSubDefault())
95 turnSubtitlesOn(true);
97 turnSubtitlesOn(false);
103 int PlayerVideoRec::shutdown()
105 if (!initted) return 0;
119 void PlayerVideoRec::threadStart()
121 playerThreadMutex.lock();
122 threadReqQuit = false;
123 playerThread = std::thread([this]
125 playerThreadMutex.lock();
126 playerThreadMutex.unlock();
129 playerThreadMutex.unlock();
132 void PlayerVideoRec::threadStop()
134 playerThreadMutex.lock();
135 threadReqQuit = true;
136 playerThreadCond.notify_one();
137 playerThreadMutex.unlock();
141 void PlayerVideoRec::setStartFrame(ULONG startFrame)
143 ULONG nextiframeNumber;
148 // newFrame could be anywhere, go forwards to next I-Frame
149 if (!vdr->getNextIFrame(startFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
151 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
152 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
154 logger->log("Player", Log::DEBUG, "setStartFrame %lu %lu %lu", startFrame, nextiframeNumber,iframeNumber);
155 currentFrameNumber = iframeNumber;
158 void PlayerVideoRec::setLengthBytes(ULLONG length)
160 lengthBytes = length;
161 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
164 void PlayerVideoRec::setLengthFrames(ULONG length)
166 lengthFrames = length;
167 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
170 ULONG PlayerVideoRec::getLengthFrames()
175 ULONG PlayerVideoRec::getCurrentFrameNum()
177 if (startup) return 0;
182 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
186 return currentFrameNumber;
188 return 0; // shouldn't happen
192 bool* PlayerVideoRec::getDemuxerMpegAudioChannels()
194 return demuxer->getmpAudioChannels();
197 bool* PlayerVideoRec::getDemuxerAc3AudioChannels()
199 return demuxer->getac3AudioChannels();
202 bool* PlayerVideoRec::getDemuxerSubtitleChannels()
204 return demuxer->getSubtitleChannels();
207 int PlayerVideoRec::getCurrentAudioChannel()
210 return demuxer->getselAudioChannel();
212 return dynamic_cast<DemuxerTS*>(demuxer)->getAID();
215 int PlayerVideoRec::getCurrentSubtitleChannel()
218 return demuxer->getselSubtitleChannel();
220 return dynamic_cast<DemuxerTS*>(demuxer)->getSubID();
223 void PlayerVideoRec::setSubtitleChannel(int newChannel)
226 demuxer->setDVBSubtitleStream(newChannel);
228 dynamic_cast<DemuxerTS*>(demuxer)->setSubID(newChannel);
231 int *PlayerVideoRec::getTeletxtSubtitlePages()
233 return teletext->getSubtitlePages();
236 void PlayerVideoRec::setAudioChannel(int newChannel, int type, int streamtype)
240 demuxer->setAudioStream(newChannel);
245 dynamic_cast<DemuxerTS*>(demuxer)->setAID(newChannel,type,streamtype,false);
250 bool PlayerVideoRec::toggleSubtitles()
252 if (!subtitlesShowing)
254 subtitlesShowing = true;
259 subtitlesShowing = false;
262 return subtitlesShowing;
265 void PlayerVideoRec::turnSubtitlesOn(bool ison)
269 subtitlesShowing = true;
274 subtitlesShowing = false;
279 void PlayerVideoRec::tellSubtitlesOSDVisible(bool visible)
281 subtitles->setOSDMenuVisibility(visible);
284 Channel * PlayerVideoRec::getDemuxerChannel()
286 if (!is_pesrecording)
288 return dynamic_cast<DemuxerTS*>(demuxer)->getChannelInfo();
290 return NULL; //Should not happen!
294 // ----------------------------------- Externally called events
296 void PlayerVideoRec::play()
298 if (!initted) return;
299 if (state == S_PLAY) return;
302 bool doUnlock = false;
303 if (state == S_PAUSE_P) doUnlock = true;
305 if (doUnlock) stateMutex.unlock();
308 void PlayerVideoRec::playpause()
310 if (!initted) return;
313 bool doUnlock = false;
317 switchState(S_PAUSE_P);
321 if (state == S_PAUSE_P) doUnlock = true;
324 if (doUnlock) stateMutex.unlock();
327 void PlayerVideoRec::stop()
329 if (!initted) return;
330 if (state == S_STOP) return;
332 logger->log("Player", Log::DEBUG, "Stop called lock");
337 void PlayerVideoRec::pause()
339 if (!initted) return;
342 if ((state == S_FFWD) || (state == S_FBWD))
344 switchState(S_PAUSE_I);
346 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
352 switchState(S_PAUSE_P);
358 void PlayerVideoRec::fastForward()
360 if (!initted) return;
368 case 4: ifactor = 8; break;
369 case 8: ifactor = 16; break;
370 case 16: ifactor = 32; break;
371 case 32: ifactor = 4; break;
382 void PlayerVideoRec::fastBackward()
384 if (!initted) return;
392 case 4: ifactor = 8; break;
393 case 8: ifactor = 16; break;
394 case 16: ifactor = 32; break;
395 case 32: ifactor = 4; break;
406 void PlayerVideoRec::jumpToPercent(double percent)
409 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
410 ULONG newFrame = static_cast<ULONG>(percent * lengthFrames / 100);
411 switchState(S_JUMP, newFrame);
412 // stateMutex.unlock(); - let thread unlock this
415 void PlayerVideoRec::jumpToMark(int mark)
418 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
419 switchState(S_JUMP, mark);
420 // stateMutex.unlock(); - let thread unlock this
423 void PlayerVideoRec::jumpToFrameP(int newFrame)
426 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
427 switchState(S_JUMP_PI, newFrame);
431 void PlayerVideoRec::skipForward(int seconds)
434 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
435 ULONG newFrame = getCurrentFrameNum();
436 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
437 newFrame += static_cast<ULONG>(static_cast<double>(seconds) * fps);
438 if (newFrame > lengthFrames) { switchState(S_PLAY); stateMutex.unlock(); }
439 else switchState(S_JUMP, newFrame);
440 // stateMutex.unlock(); - let thread unlock this
443 void PlayerVideoRec::skipBackward(int seconds)
446 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
447 long newFrame = getCurrentFrameNum();
448 if (newFrame == 0) { stateMutex.unlock(); return; } // Current pos from demuxer is not valid
449 newFrame -= static_cast<ULONG>(static_cast<double>(seconds) * fps);
450 if (newFrame < 0) newFrame = 0;
451 switchState(S_JUMP, newFrame);
452 // stateMutex.unlock(); - let thread unlock this
455 // ----------------------------------- Implementations called events
457 void PlayerVideoRec::switchState(UCHAR toState, ULONG jumpFrame)
459 if (!initted) return;
461 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
463 switch(state) // current state selector
465 case S_PLAY: // from S_PLAY -----------------------------------
469 case S_PLAY: // to S_PLAY
473 case S_PAUSE_P: // to S_PAUSE_P
475 #ifdef VOMP_PLATFORM_RASPBERRY
476 vfeed.stop(); // can't vfeed during pause
485 case S_PAUSE_I: // to S_PAUSE_I
490 case S_FFWD: // to S_FFWD
492 currentFrameNumber = getCurrentFrameNum();
493 audio->systemMuteOn();
504 case S_FBWD: // to S_FBWD
506 currentFrameNumber = getCurrentFrameNum();
507 audio->systemMuteOn();
518 case S_STOP: // to S_STOP
534 case S_JUMP: // to S_JUMP
536 restartAtFrame(jumpFrame);
539 case S_JUMP_PI: // to S_JUMP_PI
541 audio->systemMuteOn();
551 restartAtFramePI(jumpFrame);
556 FALLTHROUGH // keep compiler happy (all posibilities return)
557 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
561 case S_PLAY: // to S_PLAY
565 subtitles->unPause();
567 #ifdef VOMP_PLATFORM_RASPBERRY
574 case S_PAUSE_P: // to S_PAUSE_P
578 case S_PAUSE_I: // to S_PAUSE_I
582 case S_FFWD: // to S_FFWD
584 currentFrameNumber = getCurrentFrameNum();
585 audio->systemMuteOn();
597 case S_FBWD: // to S_FBWD
599 currentFrameNumber = getCurrentFrameNum();
600 audio->systemMuteOn();
612 case S_STOP: // to S_STOP
625 audio->systemMuteOff();
629 case S_JUMP: // to S_JUMP
632 audio->systemMuteOn();
634 restartAtFrame(jumpFrame);
637 case S_JUMP_PI: // to S_JUMP_PI
639 audio->systemMuteOn();
650 restartAtFramePI(jumpFrame);
655 FALLTHROUGH // keep compiler happy (all posibilities return)
656 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
660 case S_PLAY: // to S_PLAY
663 restartAtFrame(currentFrameNumber);
666 case S_PAUSE_P: // to S_PAUSE_P
670 case S_PAUSE_I: // to S_PAUSE_I
674 case S_FFWD: // to S_FFWD
680 case S_FBWD: // to S_FBWD
686 case S_STOP: // to S_STOP
693 audio->systemMuteOff();
697 case S_JUMP: // to S_JUMP
700 restartAtFrame(jumpFrame);
703 case S_JUMP_PI: // to S_JUMP_PI
705 restartAtFramePI(jumpFrame);
710 FALLTHROUGH // keep compiler happy (all posibilities return)
711 case S_FFWD: // from S_FFWD -----------------------------------
715 case S_PLAY: // to S_PLAY
718 ULONG stepback = static_cast<ULONG>(USER_RESPONSE_TIME * ifactor * fps / 1000);
719 if (stepback < currentFrameNumber)
720 currentFrameNumber -= stepback;
722 currentFrameNumber = 0;
723 restartAtFrame(currentFrameNumber);
726 case S_PAUSE_P: // to S_PAUSE_P
731 case S_PAUSE_I: // to S_PAUSE_I
737 case S_FFWD: // to S_FFWD
741 case S_FBWD: // to S_FBWD
748 case S_STOP: // to S_STOP
759 case S_JUMP: // to S_JUMP
762 restartAtFrame(jumpFrame);
765 case S_JUMP_PI: // to S_JUMP_PI
769 restartAtFramePI(jumpFrame);
774 FALLTHROUGH // keep compiler happy (all posibilities return)
775 case S_FBWD: // from S_FBWD -----------------------------------
779 case S_PLAY: // to S_PLAY
782 restartAtFrame(currentFrameNumber);
785 case S_PAUSE_P: // to S_PAUSE_P
790 case S_PAUSE_I: // to S_PAUSE_I
796 case S_FFWD: // to S_FFWD
803 case S_FBWD: // to S_FBWD
807 case S_STOP: // to S_STOP
818 case S_JUMP: // to S_JUMP
821 restartAtFrame(jumpFrame);
824 case S_JUMP_PI: // to S_JUMP_PI
828 restartAtFramePI(jumpFrame);
833 FALLTHROUGH // keep compiler happy (all posibilities return)
834 case S_STOP: // from S_STOP -----------------------------------
838 case S_PLAY: // to S_PLAY
843 audio->setStreamType(Audio::MPEG2_PES);
844 audio->systemMuteOff();
847 // FIXME use restartAtFrame here?
848 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
849 demuxer->setFrameNum(currentFrameNumber);
854 logger->log("Player", Log::DEBUG, "Immediate play");
865 case S_PAUSE_P: // to S_PAUSE_P
869 case S_PAUSE_I: // to S_PAUSE_I
873 case S_FFWD: // to S_FFWD
877 case S_FBWD: // to S_FBWD
881 case S_STOP: // to S_STOP
885 case S_JUMP: // to S_JUMP
889 case S_JUMP_PI: // to S_JUMP_PI
895 // case S_JUMP cannot be a start state because it auto flips to play
896 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
900 // ----------------------------------- Internal functions
902 void PlayerVideoRec::restartAtFrame(ULONG newFrame)
912 audio->setStreamType(Audio::MPEG2_PES);
915 currentFrameNumber = newFrame;
916 demuxer->setFrameNum(newFrame);
926 audio->systemMuteOff();
931 void PlayerVideoRec::restartAtFramePI(ULONG newFrame)
934 ULONG nextiframeNumber;
942 // newFrame could be anywhere, go forwards to next I-Frame
943 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
945 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
946 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
948 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
949 if (!vdr->isConnected())
951 if (buffer) free(buffer);
956 videoLength = demuxer->stripAudio(buffer, amountReceived);
957 video->displayIFrame(buffer, videoLength);
958 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
960 currentFrameNumber = iframeNumber;
964 void PlayerVideoRec::doConnectionLost()
966 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
967 Message* m = new Message();
968 m->to = messageReceiver;
970 m->message = Message::PLAYER_EVENT;
971 m->parameter = PlayerVideoRec::CONNECTION_LOST;
972 messageQueue->postMessage(m);
975 // ----------------------------------- Callback
977 void PlayerVideoRec::call(void* caller)
979 if (caller == demuxer)
981 logger->log("Player", Log::DEBUG, "Callback from demuxer");
983 if (video->getTVsize() == Video::ASPECT4X3)
985 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
990 UCHAR dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
991 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
993 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
994 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
996 Message* m = new Message();
998 m->to = messageReceiver;
999 m->message = Message::PLAYER_EVENT;
1000 m->parameter = PlayerVideoRec::ASPECT43;
1001 messageQueue->postMessage(m);
1003 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
1005 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
1006 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
1008 Message* m = new Message();
1010 m->to = messageReceiver;
1011 m->message = Message::PLAYER_EVENT;
1012 m->parameter = PlayerVideoRec::ASPECT169;
1013 messageQueue->postMessage(m);
1017 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... setting it anyway");
1018 video->setAspectRatio(static_cast<UCHAR>(dxCurrentAspect), parx, pary);
1026 videoStartup = false;
1030 stateMutex.unlock();
1033 playerThreadCond.notify_one();
1037 // ----------------------------------- Feed thread
1039 void PlayerVideoRec::threadMethod()
1041 // this method used to be simple, the only thing it does
1042 // is farm out to threadFeed Live/Play/Scan
1043 // All the guff is to support scan hitting one end
1045 if ((state == S_FFWD) || (state == S_FBWD))
1048 if (video->PTSIFramePlayback()) hitEnd = threadPTSFeedScan();
1049 else hitEnd = threadFeedScan();
1051 if (!hitEnd) return; // thread wants to exit normally
1053 if (state == S_FFWD) // scan hit the end. stop
1055 if (threadReqQuit) return;
1056 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1057 m->to = messageReceiver;
1059 m->message = Message::PLAYER_EVENT;
1060 m->parameter = STOP_PLAYBACK;
1061 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1062 messageQueue->postMessage(m);
1063 logger->log("Player", Log::DEBUG, "Message posted...");
1066 // if execution gets to here, threadFeedScan hit the start, go to play mode
1069 audio->setStreamType(Audio::MPEG2_PES);
1072 demuxer->setFrameNum(currentFrameNumber);
1073 videoStartup = true;
1080 audio->systemMuteOff();
1084 if (state == S_PLAY) threadFeedPlay();
1087 void PlayerVideoRec::threadFeedPlay()
1089 ULLONG feedPosition;
1090 UINT thisRead, writeLength, thisWrite, askFor;
1091 time_t lastRescan = time(NULL);
1093 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1094 if (!vdr->isConnected()) { doConnectionLost(); return; }
1095 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1097 Buffer threadBuffer;
1099 std::unique_lock<std::mutex> ul(playerThreadMutex, std::defer_lock);
1107 if (threadReqQuit) return;
1109 // If we havn't rescanned for a while..
1110 if ((lastRescan + 60) < time(NULL))
1112 lengthBytes = vdr->rescanRecording(&lengthFrames);
1113 if (!vdr->isConnected()) { doConnectionLost(); return; }
1114 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1115 lastRescan = time(NULL);
1118 if (feedPosition >= lengthBytes) break; // finished playback
1122 if (startupBlockSize > lengthBytes)
1123 askFor = static_cast<UINT>(lengthBytes); // is a very small recording!
1125 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1129 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1130 askFor = static_cast<UINT>(lengthBytes - feedPosition);
1134 //logger->log("Player", Log::DEBUG, "Get Block in");
1136 threadBuffer.set(vdr->getBlock(feedPosition, askFor, &thisRead));
1137 //logger->log("Player", Log::DEBUG, "Get Block out");
1139 feedPosition += thisRead;
1141 if (!vdr->isConnected())
1147 if (threadBuffer.isNull()) break;
1151 int a_stream = demuxer->scan(threadBuffer.ucharp(), thisRead);
1152 demuxer->setAudioStream(a_stream);
1153 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1157 if (threadReqQuit) return;
1159 while(writeLength < thisRead)
1161 //logger->log("Player", Log::DEBUG, "Put in");
1162 thisWrite = demuxer->put(threadBuffer.ucharp() + writeLength, thisRead - writeLength);
1163 //logger->log("Player", Log::DEBUG, "Put out");
1164 writeLength += thisWrite;
1168 // demuxer is full and can't take anymore
1170 if (threadReqQuit) { ul.unlock(); return; }
1171 playerThreadCond.wait(ul);
1175 if (threadReqQuit) return;
1178 threadBuffer.release();
1182 logger->log("Player", Log::DEBUG, "Recording playback ends");
1184 if (videoStartup) // oh woe. there never was a stream, I was conned!
1186 videoStartup = false;
1187 stateMutex.unlock();
1188 MILLISLEEP(500); // I think this will solve a race
1191 if (threadReqQuit) return;
1193 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1194 m->to = messageReceiver;
1196 m->message = Message::PLAYER_EVENT;
1197 m->parameter = PlayerVideoRec::STOP_PLAYBACK;
1198 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1199 messageQueue->postMessage(m);
1203 bool PlayerVideoRec::threadPTSFeedScan()
1205 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
1207 ULONG direction = 0;
1209 ULONG baseFrameNumber = 0;
1210 ULONG iframeNumber = 0;
1211 ULONG iframeLength = 0;
1212 ULONG currentfeedFrameNumber=currentFrameNumber;
1213 ULONG firstFrameNumber=currentFrameNumber;
1215 UINT amountReceived;
1220 int frameTimeOffset = 0; // Time in msec between frames
1222 Buffer threadBuffer;
1224 if (state == S_FFWD)
1226 direction = 1; // and 0 for backward
1229 video->EnterIframePlayback();
1233 baseFrameNumber = currentfeedFrameNumber;
1235 if (threadReqQuit) return false;
1237 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1240 if (iframeNumber >= lengthFrames) return true;
1241 // scan has got to the end of what we knew to be there before we started scanning
1243 baseFrameNumber = iframeNumber;
1245 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentfeedFrameNumber)) * 1000) / (fps * ifactor));
1247 logger->log("Player", Log::DEBUG, "XXX Got frame");
1249 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1251 if (!vdr->isConnected())
1257 if (threadReqQuit) return false;
1259 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1260 demuxer->changeTimes(threadBuffer.ucharp(), videoLength, playtime);
1262 while (!video->displayIFrame(threadBuffer.ucharp(), videoLength)) // the device might block
1265 if (threadReqQuit) return false;
1268 ULLONG cur_time=video->getCurrentTimestamp();
1269 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1270 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1272 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1277 playtime +=frameTimeOffset;
1278 currentfeedFrameNumber = iframeNumber;
1280 ULLONG cur_time=video->getCurrentTimestamp();
1281 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
1282 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
1284 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); // old
1288 threadBuffer.release();
1294 bool PlayerVideoRec::threadFeedScan()
1296 // This method is actually really simple - get frame from vdr,
1297 // spit it at the video chip, wait for a time. Most of the code here
1298 // is to get the wait right so that the scan occurs at the correct rate.
1300 ULONG direction = 0;
1301 ULONG baseFrameNumber = 0;
1302 ULONG iframeNumber = 0;
1303 ULONG iframeLength = 0;
1305 UINT amountReceived;
1309 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1310 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1311 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1313 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1316 int frameTimeOffset = 0; // Time in msec between frames
1317 int disp_msec = 0; // Time taken to display data
1318 int total_msec = 0; // Time taken to fetch data and display it
1321 if (state == S_FFWD) direction = 1; // and 0 for backward
1323 Buffer threadBuffer;
1327 // Fetch I-frames until we get one that can be displayed in good time
1328 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1330 baseFrameNumber = currentFrameNumber;
1333 if (threadReqQuit) return false;
1335 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1338 if (iframeNumber >= lengthFrames) return true;
1339 // scan has got to the end of what we knew to be there before we started scanning
1341 baseFrameNumber = iframeNumber;
1342 frameTimeOffset = static_cast<int>((abs(static_cast<long>(iframeNumber - currentFrameNumber)) * 1000) / (fps * ifactor));
1344 // logger->log("Player", Log::DEBUG, "Frame Time Offset: %i", frameTimeOffset);
1347 gettimeofday(&clock0, NULL);
1349 clock0 = timeGetTime();
1353 while (clock2.tv_sec != 0 &&
1354 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1355 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1357 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1359 logger->log("Player", Log::DEBUG, "XXX Got frame");
1361 threadBuffer.set(vdr->getBlock(filePos, iframeLength, &amountReceived));
1363 if (!vdr->isConnected() || !amountReceived)
1370 gettimeofday(&clock1, NULL);
1371 if (clock2.tv_sec != 0)
1372 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1373 + (clock2.tv_usec - clock1.tv_usec) / 1000
1374 + frameTimeOffset - disp_msec;
1376 clock1 = timeGetTime();
1378 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1380 if (sleepTime < 0) sleepTime = 0;
1381 if (threadReqQuit) return false;
1382 MILLISLEEP(sleepTime);
1383 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1385 videoLength = demuxer->stripAudio(threadBuffer.ucharp(), amountReceived);
1386 video->displayIFrame(threadBuffer.ucharp(), videoLength);
1387 currentFrameNumber = iframeNumber;
1388 threadBuffer.release();
1391 gettimeofday(&clock2, NULL);
1392 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1393 + (clock2.tv_usec - clock0.tv_usec) / 1000
1395 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1396 + (clock2.tv_usec - clock1.tv_usec) / 1000
1399 clock2 = timeGetTime();
1400 total_msec = clock2 - clock0 - sleepTime;
1401 disp_msec = clock2 - clock1 - sleepTime;
1403 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1407 // ----------------------------------- Dev
1410 void PlayerVideoRec::test1()
1412 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1415 void PlayerVideoRec::test2()
1417 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");