2 Copyright 2004-2008 Chris Tallon
\r
4 This file is part of VOMP.
\r
6 VOMP is free software; you can redistribute it and/or modify
\r
7 it under the terms of the GNU General Public License as published by
\r
8 the Free Software Foundation; either version 2 of the License, or
\r
9 (at your option) any later version.
\r
11 VOMP is distributed in the hope that it will be useful,
\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 GNU General Public License for more details.
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with VOMP; if not, write to the Free Software
\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
26 #include "demuxervdr.h"
\r
27 #include "demuxerts.h"
\r
29 #include "messagequeue.h"
\r
31 #include "message.h"
\r
32 #include "dvbsubtitles.h"
\r
33 #include "osdreceiver.h"
\r
35 #define USER_RESPONSE_TIME 500 // Milliseconds
\r
37 // ----------------------------------- Called from outside, one offs or info funcs
\r
39 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)
\r
40 : vfeed(this), afeed(this), tfeed(this)
\r
42 messageQueue = tmessageQueue;
\r
43 messageReceiver = tmessageReceiver;
\r
44 osdReceiver = tosdReceiver;
\r
45 audio = Audio::getInstance();
\r
46 video = Video::getInstance();
\r
47 logger = Log::getInstance();
\r
48 vdr = VDR::getInstance();
\r
52 currentFrameNumber = 0;
\r
55 is_pesrecording=true;
\r
57 subtitlesShowing = false;
\r
59 videoStartup = false;
\r
60 threadBuffer = NULL;
\r
63 startupBlockSize = 250000;
\r
64 video->turnVideoOn();
\r
69 if (initted) shutdown();
\r
72 int Player::init(bool p_isPesRecording,double framespersecond)
\r
74 if (initted) return 0;
\r
76 pthread_mutex_init(&mutex, NULL);
\r
78 mutex=CreateMutex(NULL,FALSE,NULL);
\r
80 is_pesrecording = p_isPesRecording;
\r
81 fps=framespersecond;
\r
82 if (is_pesrecording)
\r
83 demuxer = new DemuxerVDR();
\r
85 demuxer = new DemuxerTS();
\r
86 if (!demuxer) return 0;
\r
87 subtitles = new DVBSubtitles(osdReceiver);
\r
88 if (!subtitles) return 0;
\r
90 teletext = new TeletextDecoderVBIEBU();
\r
91 if (!teletext) return 0;
\r
92 teletext->setRecordigMode(true);
\r
93 unsigned int demux_video_size=2097152;
\r
94 if (video->supportsh264()) demux_video_size*=5;
\r
96 if (!demuxer->init(this, audio, video,teletext, demux_video_size,524288,65536, framespersecond, subtitles))
\r
98 logger->log("Player", Log::ERR, "Demuxer failed to init");
\r
115 int Player::shutdown()
\r
117 if (!initted) return 0;
\r
118 switchState(S_STOP);
\r
129 CloseHandle(mutex);
\r
135 void Player::setStartFrame(ULONG startFrame)
\r
137 currentFrameNumber = startFrame;
\r
140 void Player::setLengthBytes(ULLONG length)
\r
142 lengthBytes = length;
\r
143 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
\r
146 void Player::setLengthFrames(ULONG length)
\r
148 lengthFrames = length;
\r
149 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
\r
152 ULONG Player::getLengthFrames()
\r
154 return lengthFrames;
\r
157 ULONG Player::getCurrentFrameNum()
\r
159 if (startup) return 0;
\r
164 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
\r
168 return currentFrameNumber;
\r
170 return 0; // shouldn't happen
\r
174 bool* Player::getDemuxerMpegAudioChannels()
\r
176 return demuxer->getmpAudioChannels();
\r
179 bool* Player::getDemuxerAc3AudioChannels()
\r
181 return demuxer->getac3AudioChannels();
\r
184 bool* Player::getDemuxerSubtitleChannels()
\r
186 return demuxer->getSubtitleChannels();
\r
189 int Player::getCurrentAudioChannel()
\r
191 if (is_pesrecording) {
\r
192 return demuxer->getselAudioChannel();
\r
194 return ((DemuxerTS*)demuxer)->getAID();
\r
198 int Player::getCurrentSubtitleChannel()
\r
200 if (is_pesrecording) {
\r
201 return demuxer->getselSubtitleChannel();
\r
203 return ((DemuxerTS*)demuxer)->getSubID();
\r
207 void Player::setSubtitleChannel(int newChannel)
\r
209 if (is_pesrecording) {
\r
210 demuxer->setDVBSubtitleStream(newChannel);
\r
212 ((DemuxerTS*)demuxer)->setSubID(newChannel);
\r
216 int *Player::getTeletxtSubtitlePages()
\r
218 return teletext->getSubtitlePages();
\r
221 void Player::setAudioChannel(int newChannel, int type)
\r
223 if (is_pesrecording) {
\r
224 demuxer->setAudioStream(newChannel);
\r
227 ((DemuxerTS*)demuxer)->setAID(newChannel,type);
\r
232 bool Player::toggleSubtitles()
\r
234 if (!subtitlesShowing)
\r
236 subtitlesShowing = true;
\r
241 subtitlesShowing = false;
\r
244 return subtitlesShowing;
\r
247 void Player::turnSubtitlesOn(bool ison) {
\r
250 subtitlesShowing = true;
\r
255 subtitlesShowing = false;
\r
261 Channel Player::getDemuxerChannel() {
\r
262 if (!is_pesrecording) {
\r
263 return ((DemuxerTS*) demuxer)->getChannelInfo();
\r
265 return Channel(); //Should not happen!
\r
269 // ----------------------------------- Externally called events
\r
271 void Player::play()
\r
273 if (!initted) return;
\r
274 if (state == S_PLAY) return;
\r
277 bool doUnlock = false;
\r
278 if (state == S_PAUSE_P) doUnlock = true;
\r
279 switchState(S_PLAY);
\r
280 if (doUnlock) unLock();
\r
283 void Player::playpause()
\r
285 if (!initted) return;
\r
288 bool doUnlock = false;
\r
289 if (state==S_PLAY) {
\r
291 switchState(S_PAUSE_P);
\r
293 if (state == S_PAUSE_P) doUnlock = true;
\r
294 switchState(S_PLAY);
\r
296 if (doUnlock) unLock();
\r
303 void Player::stop()
\r
305 if (!initted) return;
\r
306 if (state == S_STOP) return;
\r
308 logger->log("Player", Log::DEBUG, "Stop called lock");
\r
309 switchState(S_STOP);
\r
313 void Player::pause()
\r
315 if (!initted) return;
\r
318 if ((state == S_FFWD) || (state == S_FBWD))
\r
320 switchState(S_PAUSE_I);
\r
322 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
\r
324 switchState(S_PLAY);
\r
328 switchState(S_PAUSE_P);
\r
334 void Player::fastForward()
\r
336 if (!initted) return;
\r
339 if (state == S_FFWD)
\r
344 case 4: ifactor = 8; break;
\r
345 case 8: ifactor = 16; break;
\r
346 case 16: ifactor = 32; break;
\r
347 case 32: ifactor = 4; break;
\r
353 switchState(S_FFWD);
\r
358 void Player::fastBackward()
\r
360 if (!initted) return;
\r
363 if (state == S_FBWD)
\r
368 case 4: ifactor = 8; break;
\r
369 case 8: ifactor = 16; break;
\r
370 case 16: ifactor = 32; break;
\r
371 case 32: ifactor = 4; break;
\r
377 switchState(S_FBWD);
\r
382 void Player::jumpToPercent(double percent)
\r
385 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
\r
386 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
\r
387 switchState(S_JUMP, newFrame);
\r
388 // unLock(); - let thread unlock this
\r
391 void Player::jumpToMark(int mark)
\r
394 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
\r
395 switchState(S_JUMP, mark);
\r
396 // unLock(); - let thread unlock this
\r
399 void Player::jumpToFrameP(int newFrame)
\r
402 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
\r
403 switchState(S_JUMP_PI, newFrame);
\r
407 void Player::skipForward(int seconds)
\r
410 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
\r
411 ULONG newFrame = getCurrentFrameNum();
\r
412 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
413 newFrame +=(ULONG) (((double)seconds) * fps);
\r
414 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
\r
415 else switchState(S_JUMP, newFrame);
\r
416 // unLock(); - let thread unlock this
\r
419 void Player::skipBackward(int seconds)
\r
422 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
\r
423 long newFrame = getCurrentFrameNum();
\r
424 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
425 newFrame -= (ULONG) (((double)seconds) * fps);
\r
426 if (newFrame < 0) newFrame = 0;
\r
427 switchState(S_JUMP, newFrame);
\r
428 // unLock(); - let thread unlock this
\r
431 // ----------------------------------- Implementations called events
\r
433 void Player::switchState(UCHAR toState, ULONG jumpFrame)
\r
435 if (!initted) return;
\r
437 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
\r
439 switch(state) // current state selector
\r
441 case S_PLAY: // from S_PLAY -----------------------------------
\r
445 case S_PLAY: // to S_PLAY
\r
449 case S_PAUSE_P: // to S_PAUSE_P
\r
456 case S_PAUSE_I: // to S_PAUSE_I
\r
461 case S_FFWD: // to S_FFWD
\r
463 currentFrameNumber = getCurrentFrameNum();
\r
464 audio->systemMuteOn();
\r
475 case S_FBWD: // to S_FBWD
\r
477 currentFrameNumber = getCurrentFrameNum();
\r
478 audio->systemMuteOn();
\r
489 case S_STOP: // to S_STOP
\r
505 case S_JUMP: // to S_JUMP
\r
507 restartAtFrame(jumpFrame);
\r
510 case S_JUMP_PI: // to S_JUMP_PI
\r
512 audio->systemMuteOn();
\r
522 restartAtFramePI(jumpFrame);
\r
527 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
\r
531 case S_PLAY: // to S_PLAY
\r
538 case S_PAUSE_P: // to S_PAUSE_P
\r
542 case S_PAUSE_I: // to S_PAUSE_I
\r
546 case S_FFWD: // to S_FFWD
\r
548 currentFrameNumber = getCurrentFrameNum();
\r
549 audio->systemMuteOn();
\r
561 case S_FBWD: // to S_FBWD
\r
563 currentFrameNumber = getCurrentFrameNum();
\r
564 audio->systemMuteOn();
\r
576 case S_STOP: // to S_STOP
\r
589 audio->systemMuteOff();
\r
593 case S_JUMP: // to S_JUMP
\r
596 audio->systemMuteOn();
\r
598 restartAtFrame(jumpFrame);
\r
601 case S_JUMP_PI: // to S_JUMP_PI
\r
603 audio->systemMuteOn();
\r
614 restartAtFramePI(jumpFrame);
\r
619 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
\r
623 case S_PLAY: // to S_PLAY
\r
626 restartAtFrame(currentFrameNumber);
\r
629 case S_PAUSE_P: // to S_PAUSE_P
\r
633 case S_PAUSE_I: // to S_PAUSE_I
\r
637 case S_FFWD: // to S_FFWD
\r
643 case S_FBWD: // to S_FBWD
\r
649 case S_STOP: // to S_STOP
\r
656 audio->systemMuteOff();
\r
660 case S_JUMP: // to S_JUMP
\r
663 restartAtFrame(jumpFrame);
\r
666 case S_JUMP_PI: // to S_JUMP_PI
\r
668 restartAtFramePI(jumpFrame);
\r
673 case S_FFWD: // from S_FFWD -----------------------------------
\r
677 case S_PLAY: // to S_PLAY
\r
680 ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
\r
681 if (stepback < currentFrameNumber)
\r
682 currentFrameNumber -= stepback;
\r
684 currentFrameNumber = 0;
\r
685 restartAtFrame(currentFrameNumber);
\r
688 case S_PAUSE_P: // to S_PAUSE_P
\r
693 case S_PAUSE_I: // to S_PAUSE_I
\r
699 case S_FFWD: // to S_FFWD
\r
703 case S_FBWD: // to S_FBWD
\r
710 case S_STOP: // to S_STOP
\r
721 case S_JUMP: // to S_JUMP
\r
724 restartAtFrame(jumpFrame);
\r
727 case S_JUMP_PI: // to S_JUMP_PI
\r
731 restartAtFramePI(jumpFrame);
\r
736 case S_FBWD: // from S_FBWD -----------------------------------
\r
740 case S_PLAY: // to S_PLAY
\r
743 restartAtFrame(currentFrameNumber);
\r
746 case S_PAUSE_P: // to S_PAUSE_P
\r
751 case S_PAUSE_I: // to S_PAUSE_I
\r
757 case S_FFWD: // to S_FFWD
\r
764 case S_FBWD: // to S_FBWD
\r
768 case S_STOP: // to S_STOP
\r
779 case S_JUMP: // to S_JUMP
\r
782 restartAtFrame(jumpFrame);
\r
785 case S_JUMP_PI: // to S_JUMP_PI
\r
789 restartAtFramePI(jumpFrame);
\r
794 case S_STOP: // from S_STOP -----------------------------------
\r
798 case S_PLAY: // to S_PLAY
\r
803 audio->setStreamType(Audio::MPEG2_PES);
\r
804 audio->systemMuteOff();
\r
807 // FIXME use restartAtFrame here?
\r
808 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
\r
809 demuxer->setFrameNum(currentFrameNumber);
\r
811 videoStartup = true;
\r
814 logger->log("Player", Log::DEBUG, "Immediate play");
\r
818 subtitles->start();
\r
825 case S_PAUSE_P: // to S_PAUSE_P
\r
829 case S_PAUSE_I: // to S_PAUSE_I
\r
833 case S_FFWD: // to S_FFWD
\r
837 case S_FBWD: // to S_FBWD
\r
841 case S_STOP: // to S_STOP
\r
845 case S_JUMP: // to S_JUMP
\r
849 case S_JUMP_PI: // to S_JUMP_PI
\r
855 // case S_JUMP cannot be a start state because it auto flips to play
\r
856 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
\r
860 // ----------------------------------- Internal functions
\r
862 void Player::lock()
\r
865 pthread_mutex_lock(&mutex);
\r
866 logger->log("Player", Log::DEBUG, "LOCKED");
\r
869 WaitForSingleObject(mutex, INFINITE);
\r
873 void Player::unLock()
\r
876 logger->log("Player", Log::DEBUG, "UNLOCKING");
\r
877 pthread_mutex_unlock(&mutex);
\r
879 ReleaseMutex(mutex);
\r
883 void Player::restartAtFrame(ULONG newFrame)
\r
893 audio->setStreamType(Audio::MPEG2_PES);
\r
896 currentFrameNumber = newFrame;
\r
897 demuxer->setFrameNum(newFrame);
\r
898 videoStartup = true;
\r
902 subtitles->start();
\r
907 audio->systemMuteOff();
\r
912 void Player::restartAtFramePI(ULONG newFrame)
\r
915 ULONG nextiframeNumber;
\r
916 ULONG iframeLength;
\r
917 ULONG iframeNumber;
\r
920 UINT amountReceived;
\r
923 // newFrame could be anywhere, go forwards to next I-Frame
\r
924 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
\r
926 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
\r
927 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
\r
929 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
930 if (!vdr->isConnected())
\r
932 if (buffer) free(buffer);
\r
933 doConnectionLost();
\r
937 videoLength = demuxer->stripAudio(buffer, amountReceived);
\r
938 video->displayIFrame(buffer, videoLength);
\r
939 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
\r
941 currentFrameNumber = iframeNumber;
\r
945 void Player::doConnectionLost()
\r
947 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
\r
948 Message* m = new Message();
\r
949 m->to = messageReceiver;
\r
951 m->message = Message::PLAYER_EVENT;
\r
952 m->parameter = Player::CONNECTION_LOST;
\r
953 messageQueue->postMessage(m);
\r
956 // ----------------------------------- Callback
\r
958 void Player::call(void* caller)
\r
960 if (caller == demuxer)
\r
962 logger->log("Player", Log::DEBUG, "Callback from demuxer");
\r
964 if (video->getTVsize() == Video::ASPECT4X3)
\r
966 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
\r
970 int dxCurrentAspect = demuxer->getAspectRatio();
\r
971 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
\r
973 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
\r
974 video->setAspectRatio(Video::ASPECT4X3);
\r
976 Message* m = new Message();
\r
978 m->to = messageReceiver;
\r
979 m->message = Message::PLAYER_EVENT;
\r
980 m->parameter = Player::ASPECT43;
\r
981 messageQueue->postMessageFromOuterSpace(m);
\r
983 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
\r
985 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
\r
986 video->setAspectRatio(Video::ASPECT16X9);
\r
988 Message* m = new Message();
\r
990 m->to = messageReceiver;
\r
991 m->message = Message::PLAYER_EVENT;
\r
992 m->parameter = Player::ASPECT169;
\r
993 messageQueue->postMessageFromOuterSpace(m);
\r
997 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
\r
1005 videoStartup = false;
\r
1013 threadSignalNoLock();
\r
1017 // ----------------------------------- Feed thread
\r
1019 void Player::threadMethod()
\r
1021 // this method used to be simple, the only thing it does
\r
1022 // is farm out to threadFeed Live/Play/Scan
\r
1023 // All the guff is to support scan hitting one end
\r
1025 if ((state == S_FFWD) || (state == S_FBWD))
\r
1027 if (video->PTSIFramePlayback()) threadPTSFeedScan();
\r
1028 else threadFeedScan();
\r
1029 // if this returns then scan hit one end
\r
1030 if (state == S_FFWD) // scan hit the end. stop
\r
1032 threadCheckExit();
\r
1033 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
1034 m->to = messageReceiver;
\r
1036 m->message = Message::PLAYER_EVENT;
\r
1037 m->parameter = STOP_PLAYBACK;
\r
1038 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
\r
1039 messageQueue->postMessage(m);
\r
1040 logger->log("Player", Log::DEBUG, "Message posted...");
\r
1043 // if execution gets to here, threadFeedScan hit the start, go to play mode
\r
1046 audio->setStreamType(Audio::MPEG2_PES);
\r
1049 demuxer->setFrameNum(currentFrameNumber);
\r
1050 videoStartup = true;
\r
1054 subtitles->start();
\r
1057 audio->systemMuteOff();
\r
1058 audio->doMuting();
\r
1061 if (state == S_PLAY) threadFeedPlay();
\r
1064 void Player::threadFeedPlay()
\r
1066 ULLONG feedPosition;
\r
1067 UINT thisRead, writeLength, thisWrite, askFor;
\r
1068 time_t lastRescan = time(NULL);
\r
1070 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
\r
1071 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
1072 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
\r
1081 threadCheckExit();
\r
1083 // If we havn't rescanned for a while..
\r
1084 if ((lastRescan + 60) < time(NULL))
\r
1086 lengthBytes = vdr->rescanRecording(&lengthFrames);
\r
1087 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
1088 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
\r
1089 lastRescan = time(NULL);
\r
1092 if (feedPosition >= lengthBytes) break; // finished playback
\r
1096 if (startupBlockSize > lengthBytes)
\r
1097 askFor = lengthBytes; // is a very small recording!
\r
1099 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
\r
1103 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
\r
1104 askFor = lengthBytes - feedPosition;
\r
1106 askFor = blockSize;
\r
1108 //logger->log("Player", Log::DEBUG, "Get Block in");
\r
1110 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
\r
1111 //logger->log("Player", Log::DEBUG, "Get Block out");
\r
1113 feedPosition += thisRead;
\r
1115 if (!vdr->isConnected())
\r
1117 doConnectionLost();
\r
1121 if (!threadBuffer) break;
\r
1125 int a_stream = demuxer->scan(threadBuffer, thisRead);
\r
1126 demuxer->setAudioStream(a_stream);
\r
1127 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
\r
1131 threadCheckExit();
\r
1133 while(writeLength < thisRead)
\r
1135 //logger->log("Player", Log::DEBUG, "Put in");
\r
1136 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
\r
1137 //logger->log("Player", Log::DEBUG, "Put out");
\r
1138 writeLength += thisWrite;
\r
1142 // demuxer is full and can't take anymore
\r
1144 threadWaitForSignal();
\r
1148 threadCheckExit();
\r
1151 free(threadBuffer);
\r
1152 threadBuffer = NULL;
\r
1156 // end of recording
\r
1157 logger->log("Player", Log::DEBUG, "Recording playback ends");
\r
1159 if (videoStartup) // oh woe. there never was a stream, I was conned!
\r
1161 videoStartup = false;
\r
1163 MILLISLEEP(500); // I think this will solve a race
\r
1166 threadCheckExit();
\r
1169 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
1170 m->to = messageReceiver;
\r
1172 m->message = Message::PLAYER_EVENT;
\r
1173 m->parameter = Player::STOP_PLAYBACK;
\r
1174 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
\r
1175 messageQueue->postMessage(m);
\r
1179 void Player::threadPTSFeedScan()
\r
1181 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
\r
1183 ULONG direction = 0;
\r
1185 ULONG baseFrameNumber = 0;
\r
1186 ULONG iframeNumber = 0;
\r
1187 ULONG iframeLength = 0;
\r
1188 ULONG currentfeedFrameNumber=currentFrameNumber;
\r
1189 ULONG firstFrameNumber=currentFrameNumber;
\r
1191 UINT amountReceived;
\r
1197 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
\r
1198 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
\r
1199 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
\r
1201 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
\r
1204 int frameTimeOffset = 0; // Time in msec between frames
\r
1205 int disp_msec = 0; // Time taken to display data
\r
1206 int total_msec = 0; // Time taken to fetch data and display it
\r
1207 int sleepTime = 0;
\r
1209 if (state == S_FFWD) {
\r
1210 direction = 1; // and 0 for backward
\r
1213 video->EnterIframePlayback();
\r
1217 // Fetch I-frames until we get one that can be displayed in good time
\r
1218 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
\r
1220 baseFrameNumber = currentfeedFrameNumber;
\r
1222 threadCheckExit();
\r
1223 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
\r
1226 if (iframeNumber >= lengthFrames) return;
\r
1227 // scan has got to the end of what we knew to be there before we started scanning
\r
1229 baseFrameNumber = iframeNumber;
\r
1231 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
\r
1233 logger->log("Player", Log::DEBUG, "XXX Got frame");
\r
1235 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
1237 if (!vdr->isConnected())
\r
1239 if (threadBuffer) free(threadBuffer);
\r
1240 doConnectionLost();
\r
1245 threadCheckExit();
\r
1248 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
\r
1249 demuxer->changeTimes(threadBuffer,videoLength,playtime);
\r
1251 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
\r
1254 threadCheckExit();
\r
1256 if (count%300==0) {
\r
1257 ULLONG cur_time=video->getCurrentTimestamp();
\r
1258 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
\r
1259 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
\r
1260 if (cur_time!=0) {
\r
1261 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
\r
1266 playtime +=frameTimeOffset;
\r
1267 currentfeedFrameNumber = iframeNumber;
\r
1269 ULLONG cur_time=video->getCurrentTimestamp();
\r
1270 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
\r
1271 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
\r
1272 if (cur_time!=0) {
\r
1273 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
\r
1277 free(threadBuffer);
\r
1278 threadBuffer = NULL;
\r
1284 void Player::threadFeedScan()
\r
1286 // This method is actually really simple - get frame from vdr,
\r
1287 // spit it at the video chip, wait for a time. Most of the code here
\r
1288 // is to get the wait right so that the scan occurs at the correct rate.
\r
1290 ULONG direction = 0;
\r
1291 ULONG baseFrameNumber = 0;
\r
1292 ULONG iframeNumber = 0;
\r
1293 ULONG iframeLength = 0;
\r
1295 UINT amountReceived;
\r
1299 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
\r
1300 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
\r
1301 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
\r
1303 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
\r
1306 int frameTimeOffset = 0; // Time in msec between frames
\r
1307 int disp_msec = 0; // Time taken to display data
\r
1308 int total_msec = 0; // Time taken to fetch data and display it
\r
1309 int sleepTime = 0;
\r
1311 if (state == S_FFWD) direction = 1; // and 0 for backward
\r
1315 // Fetch I-frames until we get one that can be displayed in good time
\r
1316 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
\r
1318 baseFrameNumber = currentFrameNumber;
\r
1321 threadCheckExit();
\r
1322 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
\r
1325 if (iframeNumber >= lengthFrames) return;
\r
1326 // scan has got to the end of what we knew to be there before we started scanning
\r
1328 baseFrameNumber = iframeNumber;
\r
1329 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
\r
1331 gettimeofday(&clock0, NULL);
\r
1333 clock0 = timeGetTime();
\r
1337 while (clock2.tv_sec != 0 &&
\r
1338 (clock0.tv_sec - clock2.tv_sec) * 1000 +
\r
1339 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
\r
1341 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
\r
1343 logger->log("Player", Log::DEBUG, "XXX Got frame");
\r
1345 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
1347 if (!vdr->isConnected())
\r
1349 if (threadBuffer) free(threadBuffer);
\r
1350 doConnectionLost();
\r
1355 gettimeofday(&clock1, NULL);
\r
1356 if (clock2.tv_sec != 0)
\r
1357 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
\r
1358 + (clock2.tv_usec - clock1.tv_usec) / 1000
\r
1359 + frameTimeOffset - disp_msec;
\r
1361 clock1 = timeGetTime();
\r
1363 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
\r
1365 if (sleepTime < 0) sleepTime = 0;
\r
1366 threadCheckExit();
\r
1367 MILLISLEEP(sleepTime);
\r
1368 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
\r
1370 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
\r
1371 video->displayIFrame(threadBuffer, videoLength);
\r
1372 currentFrameNumber = iframeNumber;
\r
1373 free(threadBuffer);
\r
1374 threadBuffer = NULL;
\r
1377 gettimeofday(&clock2, NULL);
\r
1378 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
\r
1379 + (clock2.tv_usec - clock0.tv_usec) / 1000
\r
1381 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
\r
1382 + (clock2.tv_usec - clock1.tv_usec) / 1000
\r
1385 clock2 = timeGetTime();
\r
1386 total_msec = clock2 - clock0 - sleepTime;
\r
1387 disp_msec = clock2 - clock1 - sleepTime;
\r
1389 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
\r
1393 void Player::threadPostStopCleanup()
\r
1397 free(threadBuffer);
\r
1398 threadBuffer = NULL;
\r
1402 // ----------------------------------- Dev
\r
1405 void Player::test1()
\r
1407 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
\r
1410 void Player::test2()
\r
1412 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
\r