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 unsigned int demux_audio_size=524288;
\r
95 if (video->supportsh264()) {
\r
96 demux_video_size*=5*2;//5;
\r
99 if (audio->maysupportAc3()) {
\r
100 //demux_audio_size*=2;
\r
103 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
\r
105 logger->log("Player", Log::ERR, "Demuxer failed to init");
\r
122 int Player::shutdown()
\r
124 if (!initted) return 0;
\r
125 switchState(S_STOP);
\r
136 CloseHandle(mutex);
\r
142 void Player::setStartFrame(ULONG startFrame)
\r
144 currentFrameNumber = startFrame;
\r
147 void Player::setLengthBytes(ULLONG length)
\r
149 lengthBytes = length;
\r
150 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
\r
153 void Player::setLengthFrames(ULONG length)
\r
155 lengthFrames = length;
\r
156 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
\r
159 ULONG Player::getLengthFrames()
\r
161 return lengthFrames;
\r
164 ULONG Player::getCurrentFrameNum()
\r
166 if (startup) return 0;
\r
171 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
\r
175 return currentFrameNumber;
\r
177 return 0; // shouldn't happen
\r
181 bool* Player::getDemuxerMpegAudioChannels()
\r
183 return demuxer->getmpAudioChannels();
\r
186 bool* Player::getDemuxerAc3AudioChannels()
\r
188 return demuxer->getac3AudioChannels();
\r
191 bool* Player::getDemuxerSubtitleChannels()
\r
193 return demuxer->getSubtitleChannels();
\r
196 int Player::getCurrentAudioChannel()
\r
198 if (is_pesrecording) {
\r
199 return demuxer->getselAudioChannel();
\r
201 return ((DemuxerTS*)demuxer)->getAID();
\r
205 int Player::getCurrentSubtitleChannel()
\r
207 if (is_pesrecording) {
\r
208 return demuxer->getselSubtitleChannel();
\r
210 return ((DemuxerTS*)demuxer)->getSubID();
\r
214 void Player::setSubtitleChannel(int newChannel)
\r
216 if (is_pesrecording) {
\r
217 demuxer->setDVBSubtitleStream(newChannel);
\r
219 ((DemuxerTS*)demuxer)->setSubID(newChannel);
\r
223 int *Player::getTeletxtSubtitlePages()
\r
225 return teletext->getSubtitlePages();
\r
228 void Player::setAudioChannel(int newChannel, int type, int streamtype)
\r
230 if (is_pesrecording) {
\r
231 demuxer->setAudioStream(newChannel);
\r
234 ((DemuxerTS*)demuxer)->setAID(newChannel,type,streamtype);
\r
239 bool Player::toggleSubtitles()
\r
241 if (!subtitlesShowing)
\r
243 subtitlesShowing = true;
\r
248 subtitlesShowing = false;
\r
251 return subtitlesShowing;
\r
254 void Player::turnSubtitlesOn(bool ison) {
\r
257 subtitlesShowing = true;
\r
262 subtitlesShowing = false;
\r
268 Channel * Player::getDemuxerChannel() {
\r
269 if (!is_pesrecording) {
\r
270 return ((DemuxerTS*) demuxer)->getChannelInfo();
\r
272 return NULL; //Should not happen!
\r
276 // ----------------------------------- Externally called events
\r
278 void Player::play()
\r
280 if (!initted) return;
\r
281 if (state == S_PLAY) return;
\r
284 bool doUnlock = false;
\r
285 if (state == S_PAUSE_P) doUnlock = true;
\r
286 switchState(S_PLAY);
\r
287 if (doUnlock) unLock();
\r
290 void Player::playpause()
\r
292 if (!initted) return;
\r
295 bool doUnlock = false;
\r
296 if (state==S_PLAY) {
\r
298 switchState(S_PAUSE_P);
\r
300 if (state == S_PAUSE_P) doUnlock = true;
\r
301 switchState(S_PLAY);
\r
303 if (doUnlock) unLock();
\r
310 void Player::stop()
\r
312 if (!initted) return;
\r
313 if (state == S_STOP) return;
\r
315 logger->log("Player", Log::DEBUG, "Stop called lock");
\r
316 switchState(S_STOP);
\r
320 void Player::pause()
\r
322 if (!initted) return;
\r
325 if ((state == S_FFWD) || (state == S_FBWD))
\r
327 switchState(S_PAUSE_I);
\r
329 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
\r
331 switchState(S_PLAY);
\r
335 switchState(S_PAUSE_P);
\r
341 void Player::fastForward()
\r
343 if (!initted) return;
\r
346 if (state == S_FFWD)
\r
351 case 4: ifactor = 8; break;
\r
352 case 8: ifactor = 16; break;
\r
353 case 16: ifactor = 32; break;
\r
354 case 32: ifactor = 4; break;
\r
360 switchState(S_FFWD);
\r
365 void Player::fastBackward()
\r
367 if (!initted) return;
\r
370 if (state == S_FBWD)
\r
375 case 4: ifactor = 8; break;
\r
376 case 8: ifactor = 16; break;
\r
377 case 16: ifactor = 32; break;
\r
378 case 32: ifactor = 4; break;
\r
384 switchState(S_FBWD);
\r
389 void Player::jumpToPercent(double percent)
\r
392 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
\r
393 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
\r
394 switchState(S_JUMP, newFrame);
\r
395 // unLock(); - let thread unlock this
\r
398 void Player::jumpToMark(int mark)
\r
401 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
\r
402 switchState(S_JUMP, mark);
\r
403 // unLock(); - let thread unlock this
\r
406 void Player::jumpToFrameP(int newFrame)
\r
409 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
\r
410 switchState(S_JUMP_PI, newFrame);
\r
414 void Player::skipForward(int seconds)
\r
417 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
\r
418 ULONG newFrame = getCurrentFrameNum();
\r
419 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
420 newFrame +=(ULONG) (((double)seconds) * fps);
\r
421 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
\r
422 else switchState(S_JUMP, newFrame);
\r
423 // unLock(); - let thread unlock this
\r
426 void Player::skipBackward(int seconds)
\r
429 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
\r
430 long newFrame = getCurrentFrameNum();
\r
431 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
432 newFrame -= (ULONG) (((double)seconds) * fps);
\r
433 if (newFrame < 0) newFrame = 0;
\r
434 switchState(S_JUMP, newFrame);
\r
435 // unLock(); - let thread unlock this
\r
438 // ----------------------------------- Implementations called events
\r
440 void Player::switchState(UCHAR toState, ULONG jumpFrame)
\r
442 if (!initted) return;
\r
444 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
\r
446 switch(state) // current state selector
\r
448 case S_PLAY: // from S_PLAY -----------------------------------
\r
452 case S_PLAY: // to S_PLAY
\r
456 case S_PAUSE_P: // to S_PAUSE_P
\r
463 case S_PAUSE_I: // to S_PAUSE_I
\r
468 case S_FFWD: // to S_FFWD
\r
470 currentFrameNumber = getCurrentFrameNum();
\r
471 audio->systemMuteOn();
\r
482 case S_FBWD: // to S_FBWD
\r
484 currentFrameNumber = getCurrentFrameNum();
\r
485 audio->systemMuteOn();
\r
496 case S_STOP: // to S_STOP
\r
512 case S_JUMP: // to S_JUMP
\r
514 restartAtFrame(jumpFrame);
\r
517 case S_JUMP_PI: // to S_JUMP_PI
\r
519 audio->systemMuteOn();
\r
529 restartAtFramePI(jumpFrame);
\r
534 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
\r
538 case S_PLAY: // to S_PLAY
\r
545 case S_PAUSE_P: // to S_PAUSE_P
\r
549 case S_PAUSE_I: // to S_PAUSE_I
\r
553 case S_FFWD: // to S_FFWD
\r
555 currentFrameNumber = getCurrentFrameNum();
\r
556 audio->systemMuteOn();
\r
568 case S_FBWD: // to S_FBWD
\r
570 currentFrameNumber = getCurrentFrameNum();
\r
571 audio->systemMuteOn();
\r
583 case S_STOP: // to S_STOP
\r
596 audio->systemMuteOff();
\r
600 case S_JUMP: // to S_JUMP
\r
603 audio->systemMuteOn();
\r
605 restartAtFrame(jumpFrame);
\r
608 case S_JUMP_PI: // to S_JUMP_PI
\r
610 audio->systemMuteOn();
\r
621 restartAtFramePI(jumpFrame);
\r
626 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
\r
630 case S_PLAY: // to S_PLAY
\r
633 restartAtFrame(currentFrameNumber);
\r
636 case S_PAUSE_P: // to S_PAUSE_P
\r
640 case S_PAUSE_I: // to S_PAUSE_I
\r
644 case S_FFWD: // to S_FFWD
\r
650 case S_FBWD: // to S_FBWD
\r
656 case S_STOP: // to S_STOP
\r
663 audio->systemMuteOff();
\r
667 case S_JUMP: // to S_JUMP
\r
670 restartAtFrame(jumpFrame);
\r
673 case S_JUMP_PI: // to S_JUMP_PI
\r
675 restartAtFramePI(jumpFrame);
\r
680 case S_FFWD: // from S_FFWD -----------------------------------
\r
684 case S_PLAY: // to S_PLAY
\r
687 ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
\r
688 if (stepback < currentFrameNumber)
\r
689 currentFrameNumber -= stepback;
\r
691 currentFrameNumber = 0;
\r
692 restartAtFrame(currentFrameNumber);
\r
695 case S_PAUSE_P: // to S_PAUSE_P
\r
700 case S_PAUSE_I: // to S_PAUSE_I
\r
706 case S_FFWD: // to S_FFWD
\r
710 case S_FBWD: // to S_FBWD
\r
717 case S_STOP: // to S_STOP
\r
728 case S_JUMP: // to S_JUMP
\r
731 restartAtFrame(jumpFrame);
\r
734 case S_JUMP_PI: // to S_JUMP_PI
\r
738 restartAtFramePI(jumpFrame);
\r
743 case S_FBWD: // from S_FBWD -----------------------------------
\r
747 case S_PLAY: // to S_PLAY
\r
750 restartAtFrame(currentFrameNumber);
\r
753 case S_PAUSE_P: // to S_PAUSE_P
\r
758 case S_PAUSE_I: // to S_PAUSE_I
\r
764 case S_FFWD: // to S_FFWD
\r
771 case S_FBWD: // to S_FBWD
\r
775 case S_STOP: // to S_STOP
\r
786 case S_JUMP: // to S_JUMP
\r
789 restartAtFrame(jumpFrame);
\r
792 case S_JUMP_PI: // to S_JUMP_PI
\r
796 restartAtFramePI(jumpFrame);
\r
801 case S_STOP: // from S_STOP -----------------------------------
\r
805 case S_PLAY: // to S_PLAY
\r
810 audio->setStreamType(Audio::MPEG2_PES);
\r
811 audio->systemMuteOff();
\r
814 // FIXME use restartAtFrame here?
\r
815 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
\r
816 demuxer->setFrameNum(currentFrameNumber);
\r
818 videoStartup = true;
\r
821 logger->log("Player", Log::DEBUG, "Immediate play");
\r
825 subtitles->start();
\r
832 case S_PAUSE_P: // to S_PAUSE_P
\r
836 case S_PAUSE_I: // to S_PAUSE_I
\r
840 case S_FFWD: // to S_FFWD
\r
844 case S_FBWD: // to S_FBWD
\r
848 case S_STOP: // to S_STOP
\r
852 case S_JUMP: // to S_JUMP
\r
856 case S_JUMP_PI: // to S_JUMP_PI
\r
862 // case S_JUMP cannot be a start state because it auto flips to play
\r
863 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
\r
867 // ----------------------------------- Internal functions
\r
869 void Player::lock()
\r
872 pthread_mutex_lock(&mutex);
\r
873 logger->log("Player", Log::DEBUG, "LOCKED");
\r
876 WaitForSingleObject(mutex, INFINITE);
\r
880 void Player::unLock()
\r
883 logger->log("Player", Log::DEBUG, "UNLOCKING");
\r
884 pthread_mutex_unlock(&mutex);
\r
886 ReleaseMutex(mutex);
\r
890 void Player::restartAtFrame(ULONG newFrame)
\r
900 audio->setStreamType(Audio::MPEG2_PES);
\r
903 currentFrameNumber = newFrame;
\r
904 demuxer->setFrameNum(newFrame);
\r
905 videoStartup = true;
\r
909 subtitles->start();
\r
914 audio->systemMuteOff();
\r
919 void Player::restartAtFramePI(ULONG newFrame)
\r
922 ULONG nextiframeNumber;
\r
923 ULONG iframeLength;
\r
924 ULONG iframeNumber;
\r
927 UINT amountReceived;
\r
930 // newFrame could be anywhere, go forwards to next I-Frame
\r
931 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
\r
933 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
\r
934 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
\r
936 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
937 if (!vdr->isConnected())
\r
939 if (buffer) free(buffer);
\r
940 doConnectionLost();
\r
944 videoLength = demuxer->stripAudio(buffer, amountReceived);
\r
945 video->displayIFrame(buffer, videoLength);
\r
946 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
\r
948 currentFrameNumber = iframeNumber;
\r
952 void Player::doConnectionLost()
\r
954 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
\r
955 Message* m = new Message();
\r
956 m->to = messageReceiver;
\r
958 m->message = Message::PLAYER_EVENT;
\r
959 m->parameter = Player::CONNECTION_LOST;
\r
960 messageQueue->postMessage(m);
\r
963 // ----------------------------------- Callback
\r
965 void Player::call(void* caller)
\r
967 if (caller == demuxer)
\r
969 logger->log("Player", Log::DEBUG, "Callback from demuxer");
\r
971 if (video->getTVsize() == Video::ASPECT4X3)
\r
973 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
\r
977 int dxCurrentAspect = demuxer->getAspectRatio();
\r
978 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
\r
980 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
\r
981 video->setAspectRatio(Video::ASPECT4X3);
\r
983 Message* m = new Message();
\r
985 m->to = messageReceiver;
\r
986 m->message = Message::PLAYER_EVENT;
\r
987 m->parameter = Player::ASPECT43;
\r
988 messageQueue->postMessageFromOuterSpace(m);
\r
990 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
\r
992 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
\r
993 video->setAspectRatio(Video::ASPECT16X9);
\r
995 Message* m = new Message();
\r
997 m->to = messageReceiver;
\r
998 m->message = Message::PLAYER_EVENT;
\r
999 m->parameter = Player::ASPECT169;
\r
1000 messageQueue->postMessageFromOuterSpace(m);
\r
1004 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
\r
1012 videoStartup = false;
\r
1020 threadSignalNoLock();
\r
1024 // ----------------------------------- Feed thread
\r
1026 void Player::threadMethod()
\r
1028 // this method used to be simple, the only thing it does
\r
1029 // is farm out to threadFeed Live/Play/Scan
\r
1030 // All the guff is to support scan hitting one end
\r
1032 if ((state == S_FFWD) || (state == S_FBWD))
\r
1034 if (video->PTSIFramePlayback()) threadPTSFeedScan();
\r
1035 else threadFeedScan();
\r
1036 // if this returns then scan hit one end
\r
1037 if (state == S_FFWD) // scan hit the end. stop
\r
1039 threadCheckExit();
\r
1040 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
1041 m->to = messageReceiver;
\r
1043 m->message = Message::PLAYER_EVENT;
\r
1044 m->parameter = STOP_PLAYBACK;
\r
1045 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
\r
1046 messageQueue->postMessage(m);
\r
1047 logger->log("Player", Log::DEBUG, "Message posted...");
\r
1050 // if execution gets to here, threadFeedScan hit the start, go to play mode
\r
1053 audio->setStreamType(Audio::MPEG2_PES);
\r
1056 demuxer->setFrameNum(currentFrameNumber);
\r
1057 videoStartup = true;
\r
1061 subtitles->start();
\r
1064 audio->systemMuteOff();
\r
1065 audio->doMuting();
\r
1068 if (state == S_PLAY) threadFeedPlay();
\r
1071 void Player::threadFeedPlay()
\r
1073 ULLONG feedPosition;
\r
1074 UINT thisRead, writeLength, thisWrite, askFor;
\r
1075 time_t lastRescan = time(NULL);
\r
1077 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
\r
1078 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
1079 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
\r
1088 threadCheckExit();
\r
1090 // If we havn't rescanned for a while..
\r
1091 if ((lastRescan + 60) < time(NULL))
\r
1093 lengthBytes = vdr->rescanRecording(&lengthFrames);
\r
1094 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
1095 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
\r
1096 lastRescan = time(NULL);
\r
1099 if (feedPosition >= lengthBytes) break; // finished playback
\r
1103 if (startupBlockSize > lengthBytes)
\r
1104 askFor = lengthBytes; // is a very small recording!
\r
1106 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
\r
1110 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
\r
1111 askFor = lengthBytes - feedPosition;
\r
1113 askFor = blockSize;
\r
1115 //logger->log("Player", Log::DEBUG, "Get Block in");
\r
1117 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
\r
1118 //logger->log("Player", Log::DEBUG, "Get Block out");
\r
1120 feedPosition += thisRead;
\r
1122 if (!vdr->isConnected())
\r
1124 doConnectionLost();
\r
1128 if (!threadBuffer) break;
\r
1132 int a_stream = demuxer->scan(threadBuffer, thisRead);
\r
1133 demuxer->setAudioStream(a_stream);
\r
1134 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
\r
1138 threadCheckExit();
\r
1140 while(writeLength < thisRead)
\r
1142 //logger->log("Player", Log::DEBUG, "Put in");
\r
1143 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
\r
1144 //logger->log("Player", Log::DEBUG, "Put out");
\r
1145 writeLength += thisWrite;
\r
1149 // demuxer is full and can't take anymore
\r
1151 threadWaitForSignal();
\r
1155 threadCheckExit();
\r
1158 free(threadBuffer);
\r
1159 threadBuffer = NULL;
\r
1163 // end of recording
\r
1164 logger->log("Player", Log::DEBUG, "Recording playback ends");
\r
1166 if (videoStartup) // oh woe. there never was a stream, I was conned!
\r
1168 videoStartup = false;
\r
1170 MILLISLEEP(500); // I think this will solve a race
\r
1173 threadCheckExit();
\r
1176 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
1177 m->to = messageReceiver;
\r
1179 m->message = Message::PLAYER_EVENT;
\r
1180 m->parameter = Player::STOP_PLAYBACK;
\r
1181 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
\r
1182 messageQueue->postMessage(m);
\r
1186 void Player::threadPTSFeedScan()
\r
1188 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
\r
1190 ULONG direction = 0;
\r
1192 ULONG baseFrameNumber = 0;
\r
1193 ULONG iframeNumber = 0;
\r
1194 ULONG iframeLength = 0;
\r
1195 ULONG currentfeedFrameNumber=currentFrameNumber;
\r
1196 ULONG firstFrameNumber=currentFrameNumber;
\r
1198 UINT amountReceived;
\r
1204 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
\r
1205 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
\r
1206 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
\r
1208 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
\r
1211 int frameTimeOffset = 0; // Time in msec between frames
\r
1212 int disp_msec = 0; // Time taken to display data
\r
1213 int total_msec = 0; // Time taken to fetch data and display it
\r
1214 int sleepTime = 0;
\r
1216 if (state == S_FFWD) {
\r
1217 direction = 1; // and 0 for backward
\r
1220 video->EnterIframePlayback();
\r
1224 // Fetch I-frames until we get one that can be displayed in good time
\r
1225 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
\r
1227 baseFrameNumber = currentfeedFrameNumber;
\r
1229 threadCheckExit();
\r
1230 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
\r
1233 if (iframeNumber >= lengthFrames) return;
\r
1234 // scan has got to the end of what we knew to be there before we started scanning
\r
1236 baseFrameNumber = iframeNumber;
\r
1238 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
\r
1240 logger->log("Player", Log::DEBUG, "XXX Got frame");
\r
1242 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
1244 if (!vdr->isConnected())
\r
1246 if (threadBuffer) free(threadBuffer);
\r
1247 doConnectionLost();
\r
1252 threadCheckExit();
\r
1255 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
\r
1256 demuxer->changeTimes(threadBuffer,videoLength,playtime);
\r
1258 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
\r
1261 threadCheckExit();
\r
1263 if (count%300==0) {
\r
1264 ULLONG cur_time=video->getCurrentTimestamp();
\r
1265 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
\r
1266 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
\r
1267 if (cur_time!=0) {
\r
1268 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
\r
1273 playtime +=frameTimeOffset;
\r
1274 currentfeedFrameNumber = iframeNumber;
\r
1276 ULLONG cur_time=video->getCurrentTimestamp();
\r
1277 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
\r
1278 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
\r
1279 if (cur_time!=0) {
\r
1280 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
\r
1284 free(threadBuffer);
\r
1285 threadBuffer = NULL;
\r
1291 void Player::threadFeedScan()
\r
1293 // This method is actually really simple - get frame from vdr,
\r
1294 // spit it at the video chip, wait for a time. Most of the code here
\r
1295 // is to get the wait right so that the scan occurs at the correct rate.
\r
1297 ULONG direction = 0;
\r
1298 ULONG baseFrameNumber = 0;
\r
1299 ULONG iframeNumber = 0;
\r
1300 ULONG iframeLength = 0;
\r
1302 UINT amountReceived;
\r
1306 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
\r
1307 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
\r
1308 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
\r
1310 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
\r
1313 int frameTimeOffset = 0; // Time in msec between frames
\r
1314 int disp_msec = 0; // Time taken to display data
\r
1315 int total_msec = 0; // Time taken to fetch data and display it
\r
1316 int sleepTime = 0;
\r
1318 if (state == S_FFWD) direction = 1; // and 0 for backward
\r
1322 // Fetch I-frames until we get one that can be displayed in good time
\r
1323 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
\r
1325 baseFrameNumber = currentFrameNumber;
\r
1328 threadCheckExit();
\r
1329 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
\r
1332 if (iframeNumber >= lengthFrames) return;
\r
1333 // scan has got to the end of what we knew to be there before we started scanning
\r
1335 baseFrameNumber = iframeNumber;
\r
1336 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
\r
1338 gettimeofday(&clock0, NULL);
\r
1340 clock0 = timeGetTime();
\r
1344 while (clock2.tv_sec != 0 &&
\r
1345 (clock0.tv_sec - clock2.tv_sec) * 1000 +
\r
1346 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
\r
1348 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
\r
1350 logger->log("Player", Log::DEBUG, "XXX Got frame");
\r
1352 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
1354 if (!vdr->isConnected())
\r
1356 if (threadBuffer) free(threadBuffer);
\r
1357 doConnectionLost();
\r
1362 gettimeofday(&clock1, NULL);
\r
1363 if (clock2.tv_sec != 0)
\r
1364 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
\r
1365 + (clock2.tv_usec - clock1.tv_usec) / 1000
\r
1366 + frameTimeOffset - disp_msec;
\r
1368 clock1 = timeGetTime();
\r
1370 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
\r
1372 if (sleepTime < 0) sleepTime = 0;
\r
1373 threadCheckExit();
\r
1374 MILLISLEEP(sleepTime);
\r
1375 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
\r
1377 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
\r
1378 video->displayIFrame(threadBuffer, videoLength);
\r
1379 currentFrameNumber = iframeNumber;
\r
1380 free(threadBuffer);
\r
1381 threadBuffer = NULL;
\r
1384 gettimeofday(&clock2, NULL);
\r
1385 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
\r
1386 + (clock2.tv_usec - clock0.tv_usec) / 1000
\r
1388 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
\r
1389 + (clock2.tv_usec - clock1.tv_usec) / 1000
\r
1392 clock2 = timeGetTime();
\r
1393 total_msec = clock2 - clock0 - sleepTime;
\r
1394 disp_msec = clock2 - clock1 - sleepTime;
\r
1396 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
\r
1400 void Player::threadPostStopCleanup()
\r
1404 free(threadBuffer);
\r
1405 threadBuffer = NULL;
\r
1409 // ----------------------------------- Dev
\r
1412 void Player::test1()
\r
1414 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
\r
1417 void Player::test2()
\r
1419 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
\r