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
97 demux_audio_size*=2;
\r
100 if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))
\r
102 logger->log("Player", Log::ERR, "Demuxer failed to init");
\r
119 int Player::shutdown()
\r
121 if (!initted) return 0;
\r
122 switchState(S_STOP);
\r
133 CloseHandle(mutex);
\r
139 void Player::setStartFrame(ULONG startFrame)
\r
141 currentFrameNumber = startFrame;
\r
144 void Player::setLengthBytes(ULLONG length)
\r
146 lengthBytes = length;
\r
147 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
\r
150 void Player::setLengthFrames(ULONG length)
\r
152 lengthFrames = length;
\r
153 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
\r
156 ULONG Player::getLengthFrames()
\r
158 return lengthFrames;
\r
161 ULONG Player::getCurrentFrameNum()
\r
163 if (startup) return 0;
\r
168 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
\r
172 return currentFrameNumber;
\r
174 return 0; // shouldn't happen
\r
178 bool* Player::getDemuxerMpegAudioChannels()
\r
180 return demuxer->getmpAudioChannels();
\r
183 bool* Player::getDemuxerAc3AudioChannels()
\r
185 return demuxer->getac3AudioChannels();
\r
188 bool* Player::getDemuxerSubtitleChannels()
\r
190 return demuxer->getSubtitleChannels();
\r
193 int Player::getCurrentAudioChannel()
\r
195 if (is_pesrecording) {
\r
196 return demuxer->getselAudioChannel();
\r
198 return ((DemuxerTS*)demuxer)->getAID();
\r
202 int Player::getCurrentSubtitleChannel()
\r
204 if (is_pesrecording) {
\r
205 return demuxer->getselSubtitleChannel();
\r
207 return ((DemuxerTS*)demuxer)->getSubID();
\r
211 void Player::setSubtitleChannel(int newChannel)
\r
213 if (is_pesrecording) {
\r
214 demuxer->setDVBSubtitleStream(newChannel);
\r
216 ((DemuxerTS*)demuxer)->setSubID(newChannel);
\r
220 int *Player::getTeletxtSubtitlePages()
\r
222 return teletext->getSubtitlePages();
\r
225 void Player::setAudioChannel(int newChannel, int type)
\r
227 if (is_pesrecording) {
\r
228 demuxer->setAudioStream(newChannel);
\r
231 ((DemuxerTS*)demuxer)->setAID(newChannel,type);
\r
236 bool Player::toggleSubtitles()
\r
238 if (!subtitlesShowing)
\r
240 subtitlesShowing = true;
\r
245 subtitlesShowing = false;
\r
248 return subtitlesShowing;
\r
251 void Player::turnSubtitlesOn(bool ison) {
\r
254 subtitlesShowing = true;
\r
259 subtitlesShowing = false;
\r
265 Channel Player::getDemuxerChannel() {
\r
266 if (!is_pesrecording) {
\r
267 return ((DemuxerTS*) demuxer)->getChannelInfo();
\r
269 return Channel(); //Should not happen!
\r
273 // ----------------------------------- Externally called events
\r
275 void Player::play()
\r
277 if (!initted) return;
\r
278 if (state == S_PLAY) return;
\r
281 bool doUnlock = false;
\r
282 if (state == S_PAUSE_P) doUnlock = true;
\r
283 switchState(S_PLAY);
\r
284 if (doUnlock) unLock();
\r
287 void Player::playpause()
\r
289 if (!initted) return;
\r
292 bool doUnlock = false;
\r
293 if (state==S_PLAY) {
\r
295 switchState(S_PAUSE_P);
\r
297 if (state == S_PAUSE_P) doUnlock = true;
\r
298 switchState(S_PLAY);
\r
300 if (doUnlock) unLock();
\r
307 void Player::stop()
\r
309 if (!initted) return;
\r
310 if (state == S_STOP) return;
\r
312 logger->log("Player", Log::DEBUG, "Stop called lock");
\r
313 switchState(S_STOP);
\r
317 void Player::pause()
\r
319 if (!initted) return;
\r
322 if ((state == S_FFWD) || (state == S_FBWD))
\r
324 switchState(S_PAUSE_I);
\r
326 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
\r
328 switchState(S_PLAY);
\r
332 switchState(S_PAUSE_P);
\r
338 void Player::fastForward()
\r
340 if (!initted) return;
\r
343 if (state == S_FFWD)
\r
348 case 4: ifactor = 8; break;
\r
349 case 8: ifactor = 16; break;
\r
350 case 16: ifactor = 32; break;
\r
351 case 32: ifactor = 4; break;
\r
357 switchState(S_FFWD);
\r
362 void Player::fastBackward()
\r
364 if (!initted) return;
\r
367 if (state == S_FBWD)
\r
372 case 4: ifactor = 8; break;
\r
373 case 8: ifactor = 16; break;
\r
374 case 16: ifactor = 32; break;
\r
375 case 32: ifactor = 4; break;
\r
381 switchState(S_FBWD);
\r
386 void Player::jumpToPercent(double percent)
\r
389 logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);
\r
390 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
\r
391 switchState(S_JUMP, newFrame);
\r
392 // unLock(); - let thread unlock this
\r
395 void Player::jumpToMark(int mark)
\r
398 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
\r
399 switchState(S_JUMP, mark);
\r
400 // unLock(); - let thread unlock this
\r
403 void Player::jumpToFrameP(int newFrame)
\r
406 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
\r
407 switchState(S_JUMP_PI, newFrame);
\r
411 void Player::skipForward(int seconds)
\r
414 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
\r
415 ULONG newFrame = getCurrentFrameNum();
\r
416 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
417 newFrame +=(ULONG) (((double)seconds) * fps);
\r
418 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
\r
419 else switchState(S_JUMP, newFrame);
\r
420 // unLock(); - let thread unlock this
\r
423 void Player::skipBackward(int seconds)
\r
426 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
\r
427 long newFrame = getCurrentFrameNum();
\r
428 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
\r
429 newFrame -= (ULONG) (((double)seconds) * fps);
\r
430 if (newFrame < 0) newFrame = 0;
\r
431 switchState(S_JUMP, newFrame);
\r
432 // unLock(); - let thread unlock this
\r
435 // ----------------------------------- Implementations called events
\r
437 void Player::switchState(UCHAR toState, ULONG jumpFrame)
\r
439 if (!initted) return;
\r
441 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
\r
443 switch(state) // current state selector
\r
445 case S_PLAY: // from S_PLAY -----------------------------------
\r
449 case S_PLAY: // to S_PLAY
\r
453 case S_PAUSE_P: // to S_PAUSE_P
\r
460 case S_PAUSE_I: // to S_PAUSE_I
\r
465 case S_FFWD: // to S_FFWD
\r
467 currentFrameNumber = getCurrentFrameNum();
\r
468 audio->systemMuteOn();
\r
479 case S_FBWD: // to S_FBWD
\r
481 currentFrameNumber = getCurrentFrameNum();
\r
482 audio->systemMuteOn();
\r
493 case S_STOP: // to S_STOP
\r
509 case S_JUMP: // to S_JUMP
\r
511 restartAtFrame(jumpFrame);
\r
514 case S_JUMP_PI: // to S_JUMP_PI
\r
516 audio->systemMuteOn();
\r
526 restartAtFramePI(jumpFrame);
\r
531 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
\r
535 case S_PLAY: // to S_PLAY
\r
542 case S_PAUSE_P: // to S_PAUSE_P
\r
546 case S_PAUSE_I: // to S_PAUSE_I
\r
550 case S_FFWD: // to S_FFWD
\r
552 currentFrameNumber = getCurrentFrameNum();
\r
553 audio->systemMuteOn();
\r
565 case S_FBWD: // to S_FBWD
\r
567 currentFrameNumber = getCurrentFrameNum();
\r
568 audio->systemMuteOn();
\r
580 case S_STOP: // to S_STOP
\r
593 audio->systemMuteOff();
\r
597 case S_JUMP: // to S_JUMP
\r
600 audio->systemMuteOn();
\r
602 restartAtFrame(jumpFrame);
\r
605 case S_JUMP_PI: // to S_JUMP_PI
\r
607 audio->systemMuteOn();
\r
618 restartAtFramePI(jumpFrame);
\r
623 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
\r
627 case S_PLAY: // to S_PLAY
\r
630 restartAtFrame(currentFrameNumber);
\r
633 case S_PAUSE_P: // to S_PAUSE_P
\r
637 case S_PAUSE_I: // to S_PAUSE_I
\r
641 case S_FFWD: // to S_FFWD
\r
647 case S_FBWD: // to S_FBWD
\r
653 case S_STOP: // to S_STOP
\r
660 audio->systemMuteOff();
\r
664 case S_JUMP: // to S_JUMP
\r
667 restartAtFrame(jumpFrame);
\r
670 case S_JUMP_PI: // to S_JUMP_PI
\r
672 restartAtFramePI(jumpFrame);
\r
677 case S_FFWD: // from S_FFWD -----------------------------------
\r
681 case S_PLAY: // to S_PLAY
\r
684 ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);
\r
685 if (stepback < currentFrameNumber)
\r
686 currentFrameNumber -= stepback;
\r
688 currentFrameNumber = 0;
\r
689 restartAtFrame(currentFrameNumber);
\r
692 case S_PAUSE_P: // to S_PAUSE_P
\r
697 case S_PAUSE_I: // to S_PAUSE_I
\r
703 case S_FFWD: // to S_FFWD
\r
707 case S_FBWD: // to S_FBWD
\r
714 case S_STOP: // to S_STOP
\r
725 case S_JUMP: // to S_JUMP
\r
728 restartAtFrame(jumpFrame);
\r
731 case S_JUMP_PI: // to S_JUMP_PI
\r
735 restartAtFramePI(jumpFrame);
\r
740 case S_FBWD: // from S_FBWD -----------------------------------
\r
744 case S_PLAY: // to S_PLAY
\r
747 restartAtFrame(currentFrameNumber);
\r
750 case S_PAUSE_P: // to S_PAUSE_P
\r
755 case S_PAUSE_I: // to S_PAUSE_I
\r
761 case S_FFWD: // to S_FFWD
\r
768 case S_FBWD: // to S_FBWD
\r
772 case S_STOP: // to S_STOP
\r
783 case S_JUMP: // to S_JUMP
\r
786 restartAtFrame(jumpFrame);
\r
789 case S_JUMP_PI: // to S_JUMP_PI
\r
793 restartAtFramePI(jumpFrame);
\r
798 case S_STOP: // from S_STOP -----------------------------------
\r
802 case S_PLAY: // to S_PLAY
\r
807 audio->setStreamType(Audio::MPEG2_PES);
\r
808 audio->systemMuteOff();
\r
811 // FIXME use restartAtFrame here?
\r
812 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
\r
813 demuxer->setFrameNum(currentFrameNumber);
\r
815 videoStartup = true;
\r
818 logger->log("Player", Log::DEBUG, "Immediate play");
\r
822 subtitles->start();
\r
829 case S_PAUSE_P: // to S_PAUSE_P
\r
833 case S_PAUSE_I: // to S_PAUSE_I
\r
837 case S_FFWD: // to S_FFWD
\r
841 case S_FBWD: // to S_FBWD
\r
845 case S_STOP: // to S_STOP
\r
849 case S_JUMP: // to S_JUMP
\r
853 case S_JUMP_PI: // to S_JUMP_PI
\r
859 // case S_JUMP cannot be a start state because it auto flips to play
\r
860 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
\r
864 // ----------------------------------- Internal functions
\r
866 void Player::lock()
\r
869 pthread_mutex_lock(&mutex);
\r
870 logger->log("Player", Log::DEBUG, "LOCKED");
\r
873 WaitForSingleObject(mutex, INFINITE);
\r
877 void Player::unLock()
\r
880 logger->log("Player", Log::DEBUG, "UNLOCKING");
\r
881 pthread_mutex_unlock(&mutex);
\r
883 ReleaseMutex(mutex);
\r
887 void Player::restartAtFrame(ULONG newFrame)
\r
897 audio->setStreamType(Audio::MPEG2_PES);
\r
900 currentFrameNumber = newFrame;
\r
901 demuxer->setFrameNum(newFrame);
\r
902 videoStartup = true;
\r
906 subtitles->start();
\r
911 audio->systemMuteOff();
\r
916 void Player::restartAtFramePI(ULONG newFrame)
\r
919 ULONG nextiframeNumber;
\r
920 ULONG iframeLength;
\r
921 ULONG iframeNumber;
\r
924 UINT amountReceived;
\r
927 // newFrame could be anywhere, go forwards to next I-Frame
\r
928 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
\r
930 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
\r
931 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
\r
933 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
934 if (!vdr->isConnected())
\r
936 if (buffer) free(buffer);
\r
937 doConnectionLost();
\r
941 videoLength = demuxer->stripAudio(buffer, amountReceived);
\r
942 video->displayIFrame(buffer, videoLength);
\r
943 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
\r
945 currentFrameNumber = iframeNumber;
\r
949 void Player::doConnectionLost()
\r
951 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
\r
952 Message* m = new Message();
\r
953 m->to = messageReceiver;
\r
955 m->message = Message::PLAYER_EVENT;
\r
956 m->parameter = Player::CONNECTION_LOST;
\r
957 messageQueue->postMessage(m);
\r
960 // ----------------------------------- Callback
\r
962 void Player::call(void* caller)
\r
964 if (caller == demuxer)
\r
966 logger->log("Player", Log::DEBUG, "Callback from demuxer");
\r
968 if (video->getTVsize() == Video::ASPECT4X3)
\r
970 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
\r
974 int dxCurrentAspect = demuxer->getAspectRatio();
\r
975 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
\r
977 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
\r
978 video->setAspectRatio(Video::ASPECT4X3);
\r
980 Message* m = new Message();
\r
982 m->to = messageReceiver;
\r
983 m->message = Message::PLAYER_EVENT;
\r
984 m->parameter = Player::ASPECT43;
\r
985 messageQueue->postMessageFromOuterSpace(m);
\r
987 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
\r
989 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
\r
990 video->setAspectRatio(Video::ASPECT16X9);
\r
992 Message* m = new Message();
\r
994 m->to = messageReceiver;
\r
995 m->message = Message::PLAYER_EVENT;
\r
996 m->parameter = Player::ASPECT169;
\r
997 messageQueue->postMessageFromOuterSpace(m);
\r
1001 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
\r
1009 videoStartup = false;
\r
1017 threadSignalNoLock();
\r
1021 // ----------------------------------- Feed thread
\r
1023 void Player::threadMethod()
\r
1025 // this method used to be simple, the only thing it does
\r
1026 // is farm out to threadFeed Live/Play/Scan
\r
1027 // All the guff is to support scan hitting one end
\r
1029 if ((state == S_FFWD) || (state == S_FBWD))
\r
1031 if (video->PTSIFramePlayback()) threadPTSFeedScan();
\r
1032 else threadFeedScan();
\r
1033 // if this returns then scan hit one end
\r
1034 if (state == S_FFWD) // scan hit the end. stop
\r
1036 threadCheckExit();
\r
1037 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
1038 m->to = messageReceiver;
\r
1040 m->message = Message::PLAYER_EVENT;
\r
1041 m->parameter = STOP_PLAYBACK;
\r
1042 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
\r
1043 messageQueue->postMessage(m);
\r
1044 logger->log("Player", Log::DEBUG, "Message posted...");
\r
1047 // if execution gets to here, threadFeedScan hit the start, go to play mode
\r
1050 audio->setStreamType(Audio::MPEG2_PES);
\r
1053 demuxer->setFrameNum(currentFrameNumber);
\r
1054 videoStartup = true;
\r
1058 subtitles->start();
\r
1061 audio->systemMuteOff();
\r
1062 audio->doMuting();
\r
1065 if (state == S_PLAY) threadFeedPlay();
\r
1068 void Player::threadFeedPlay()
\r
1070 ULLONG feedPosition;
\r
1071 UINT thisRead, writeLength, thisWrite, askFor;
\r
1072 time_t lastRescan = time(NULL);
\r
1074 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
\r
1075 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
1076 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
\r
1085 threadCheckExit();
\r
1087 // If we havn't rescanned for a while..
\r
1088 if ((lastRescan + 60) < time(NULL))
\r
1090 lengthBytes = vdr->rescanRecording(&lengthFrames);
\r
1091 if (!vdr->isConnected()) { doConnectionLost(); return; }
\r
1092 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
\r
1093 lastRescan = time(NULL);
\r
1096 if (feedPosition >= lengthBytes) break; // finished playback
\r
1100 if (startupBlockSize > lengthBytes)
\r
1101 askFor = lengthBytes; // is a very small recording!
\r
1103 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
\r
1107 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
\r
1108 askFor = lengthBytes - feedPosition;
\r
1110 askFor = blockSize;
\r
1112 //logger->log("Player", Log::DEBUG, "Get Block in");
\r
1114 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
\r
1115 //logger->log("Player", Log::DEBUG, "Get Block out");
\r
1117 feedPosition += thisRead;
\r
1119 if (!vdr->isConnected())
\r
1121 doConnectionLost();
\r
1125 if (!threadBuffer) break;
\r
1129 int a_stream = demuxer->scan(threadBuffer, thisRead);
\r
1130 demuxer->setAudioStream(a_stream);
\r
1131 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
\r
1135 threadCheckExit();
\r
1137 while(writeLength < thisRead)
\r
1139 //logger->log("Player", Log::DEBUG, "Put in");
\r
1140 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
\r
1141 //logger->log("Player", Log::DEBUG, "Put out");
\r
1142 writeLength += thisWrite;
\r
1146 // demuxer is full and can't take anymore
\r
1148 threadWaitForSignal();
\r
1152 threadCheckExit();
\r
1155 free(threadBuffer);
\r
1156 threadBuffer = NULL;
\r
1160 // end of recording
\r
1161 logger->log("Player", Log::DEBUG, "Recording playback ends");
\r
1163 if (videoStartup) // oh woe. there never was a stream, I was conned!
\r
1165 videoStartup = false;
\r
1167 MILLISLEEP(500); // I think this will solve a race
\r
1170 threadCheckExit();
\r
1173 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
\r
1174 m->to = messageReceiver;
\r
1176 m->message = Message::PLAYER_EVENT;
\r
1177 m->parameter = Player::STOP_PLAYBACK;
\r
1178 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
\r
1179 messageQueue->postMessage(m);
\r
1183 void Player::threadPTSFeedScan()
\r
1185 // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?
\r
1187 ULONG direction = 0;
\r
1189 ULONG baseFrameNumber = 0;
\r
1190 ULONG iframeNumber = 0;
\r
1191 ULONG iframeLength = 0;
\r
1192 ULONG currentfeedFrameNumber=currentFrameNumber;
\r
1193 ULONG firstFrameNumber=currentFrameNumber;
\r
1195 UINT amountReceived;
\r
1201 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
\r
1202 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
\r
1203 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
\r
1205 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
\r
1208 int frameTimeOffset = 0; // Time in msec between frames
\r
1209 int disp_msec = 0; // Time taken to display data
\r
1210 int total_msec = 0; // Time taken to fetch data and display it
\r
1211 int sleepTime = 0;
\r
1213 if (state == S_FFWD) {
\r
1214 direction = 1; // and 0 for backward
\r
1217 video->EnterIframePlayback();
\r
1221 // Fetch I-frames until we get one that can be displayed in good time
\r
1222 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
\r
1224 baseFrameNumber = currentfeedFrameNumber;
\r
1226 threadCheckExit();
\r
1227 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
\r
1230 if (iframeNumber >= lengthFrames) return;
\r
1231 // scan has got to the end of what we knew to be there before we started scanning
\r
1233 baseFrameNumber = iframeNumber;
\r
1235 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));
\r
1237 logger->log("Player", Log::DEBUG, "XXX Got frame");
\r
1239 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
1241 if (!vdr->isConnected())
\r
1243 if (threadBuffer) free(threadBuffer);
\r
1244 doConnectionLost();
\r
1249 threadCheckExit();
\r
1252 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
\r
1253 demuxer->changeTimes(threadBuffer,videoLength,playtime);
\r
1255 while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block
\r
1258 threadCheckExit();
\r
1260 if (count%300==0) {
\r
1261 ULLONG cur_time=video->getCurrentTimestamp();
\r
1262 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
\r
1263 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
\r
1264 if (cur_time!=0) {
\r
1265 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
\r
1270 playtime +=frameTimeOffset;
\r
1271 currentfeedFrameNumber = iframeNumber;
\r
1273 ULLONG cur_time=video->getCurrentTimestamp();
\r
1274 // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",
\r
1275 // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);
\r
1276 if (cur_time!=0) {
\r
1277 currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));
\r
1281 free(threadBuffer);
\r
1282 threadBuffer = NULL;
\r
1288 void Player::threadFeedScan()
\r
1290 // This method is actually really simple - get frame from vdr,
\r
1291 // spit it at the video chip, wait for a time. Most of the code here
\r
1292 // is to get the wait right so that the scan occurs at the correct rate.
\r
1294 ULONG direction = 0;
\r
1295 ULONG baseFrameNumber = 0;
\r
1296 ULONG iframeNumber = 0;
\r
1297 ULONG iframeLength = 0;
\r
1299 UINT amountReceived;
\r
1303 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
\r
1304 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
\r
1305 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
\r
1307 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
\r
1310 int frameTimeOffset = 0; // Time in msec between frames
\r
1311 int disp_msec = 0; // Time taken to display data
\r
1312 int total_msec = 0; // Time taken to fetch data and display it
\r
1313 int sleepTime = 0;
\r
1315 if (state == S_FFWD) direction = 1; // and 0 for backward
\r
1319 // Fetch I-frames until we get one that can be displayed in good time
\r
1320 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
\r
1322 baseFrameNumber = currentFrameNumber;
\r
1325 threadCheckExit();
\r
1326 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
\r
1329 if (iframeNumber >= lengthFrames) return;
\r
1330 // scan has got to the end of what we knew to be there before we started scanning
\r
1332 baseFrameNumber = iframeNumber;
\r
1333 frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));
\r
1335 gettimeofday(&clock0, NULL);
\r
1337 clock0 = timeGetTime();
\r
1341 while (clock2.tv_sec != 0 &&
\r
1342 (clock0.tv_sec - clock2.tv_sec) * 1000 +
\r
1343 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
\r
1345 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
\r
1347 logger->log("Player", Log::DEBUG, "XXX Got frame");
\r
1349 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
\r
1351 if (!vdr->isConnected())
\r
1353 if (threadBuffer) free(threadBuffer);
\r
1354 doConnectionLost();
\r
1359 gettimeofday(&clock1, NULL);
\r
1360 if (clock2.tv_sec != 0)
\r
1361 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
\r
1362 + (clock2.tv_usec - clock1.tv_usec) / 1000
\r
1363 + frameTimeOffset - disp_msec;
\r
1365 clock1 = timeGetTime();
\r
1367 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
\r
1369 if (sleepTime < 0) sleepTime = 0;
\r
1370 threadCheckExit();
\r
1371 MILLISLEEP(sleepTime);
\r
1372 logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
\r
1374 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
\r
1375 video->displayIFrame(threadBuffer, videoLength);
\r
1376 currentFrameNumber = iframeNumber;
\r
1377 free(threadBuffer);
\r
1378 threadBuffer = NULL;
\r
1381 gettimeofday(&clock2, NULL);
\r
1382 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
\r
1383 + (clock2.tv_usec - clock0.tv_usec) / 1000
\r
1385 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
\r
1386 + (clock2.tv_usec - clock1.tv_usec) / 1000
\r
1389 clock2 = timeGetTime();
\r
1390 total_msec = clock2 - clock0 - sleepTime;
\r
1391 disp_msec = clock2 - clock1 - sleepTime;
\r
1393 logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
\r
1397 void Player::threadPostStopCleanup()
\r
1401 free(threadBuffer);
\r
1402 threadBuffer = NULL;
\r
1406 // ----------------------------------- Dev
\r
1409 void Player::test1()
\r
1411 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
\r
1414 void Player::test2()
\r
1416 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
\r