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, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "demuxervdr.h"
27 #include "demuxerts.h"
29 #include "messagequeue.h"
33 #define USER_RESPONSE_TIME 500 // Milliseconds
35 // ----------------------------------- Called from outside, one offs or info funcs
37 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver)
38 : vfeed(this), afeed(this)
40 messageQueue = tmessageQueue;
41 messageReceiver = tmessageReceiver;
42 audio = Audio::getInstance();
43 video = Video::getInstance();
44 logger = Log::getInstance();
45 vdr = VDR::getInstance();
49 currentFrameNumber = 0;
57 startupBlockSize = 250000;
63 if (initted) shutdown();
68 if (initted) return 0;
70 pthread_mutex_init(&mutex, NULL);
72 mutex=CreateMutex(NULL,FALSE,NULL);
75 demuxer = new DemuxerVDR();
76 if (!demuxer) return 0;
78 if (!demuxer->init(this, audio, video, 2097152, 524288))
80 logger->log("Player", Log::ERR, "Demuxer failed to init");
96 int Player::shutdown()
98 if (!initted) return 0;
112 void Player::setStartFrame(ULONG startFrame)
114 currentFrameNumber = startFrame;
117 void Player::setLengthBytes(ULLONG length)
119 lengthBytes = length;
120 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
123 void Player::setLengthFrames(ULONG length)
125 lengthFrames = length;
126 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
129 ULONG Player::getLengthFrames()
134 ULONG Player::getCurrentFrameNum()
136 if (startup) return 0;
141 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
145 return currentFrameNumber;
147 return 0; // shouldn't happen
151 bool* Player::getDemuxerMpegAudioChannels()
153 return demuxer->getmpAudioChannels();
156 bool* Player::getDemuxerAc3AudioChannels()
158 return demuxer->getac3AudioChannels();
161 int Player::getCurrentAudioChannel()
163 return demuxer->getselAudioChannel();
166 void Player::setAudioChannel(int newChannel, int type)
168 demuxer->setAudioChannel(newChannel);
171 // ----------------------------------- Externally called events
175 if (!initted) return;
176 if (state == S_PLAY) return;
179 bool doUnlock = false;
180 if (state == S_PAUSE_P) doUnlock = true;
182 if (doUnlock) unLock();
187 if (!initted) return;
188 if (state == S_STOP) return;
190 logger->log("Player", Log::DEBUG, "Stop called lock");
197 if (!initted) return;
200 if ((state == S_FFWD) || (state == S_FBWD))
202 switchState(S_PAUSE_I);
204 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
210 switchState(S_PAUSE_P);
216 void Player::fastForward()
218 if (!initted) return;
226 case 4: ifactor = 8; break;
227 case 8: ifactor = 16; break;
228 case 16: ifactor = 32; break;
229 case 32: ifactor = 4; break;
240 void Player::fastBackward()
242 if (!initted) return;
250 case 4: ifactor = 8; break;
251 case 8: ifactor = 16; break;
252 case 16: ifactor = 32; break;
253 case 32: ifactor = 4; break;
264 void Player::jumpToPercent(double percent)
267 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
268 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
269 switchState(S_JUMP, newFrame);
270 // unLock(); - let thread unlock this
273 void Player::jumpToMark(int mark)
276 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
277 switchState(S_JUMP, mark);
278 // unLock(); - let thread unlock this
281 void Player::jumpToFrameP(int newFrame)
284 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
285 switchState(S_JUMP_PI, newFrame);
289 void Player::skipForward(int seconds)
292 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
293 ULONG newFrame = getCurrentFrameNum();
294 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
295 newFrame += seconds * video->getFPS();
296 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
297 else switchState(S_JUMP, newFrame);
298 // unLock(); - let thread unlock this
301 void Player::skipBackward(int seconds)
304 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
305 long newFrame = getCurrentFrameNum();
306 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
307 newFrame -= seconds * video->getFPS();
308 if (newFrame < 0) newFrame = 0;
309 switchState(S_JUMP, newFrame);
310 // unLock(); - let thread unlock this
313 // ----------------------------------- Implementations called events
315 void Player::switchState(UCHAR toState, ULONG jumpFrame)
317 if (!initted) return;
319 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
321 switch(state) // current state selector
323 case S_PLAY: // from S_PLAY -----------------------------------
327 case S_PLAY: // to S_PLAY
331 case S_PAUSE_P: // to S_PAUSE_P
338 case S_PAUSE_I: // to S_PAUSE_I
343 case S_FFWD: // to S_FFWD
345 currentFrameNumber = getCurrentFrameNum();
346 audio->systemMuteOn();
355 case S_FBWD: // to S_FBWD
357 currentFrameNumber = getCurrentFrameNum();
358 audio->systemMuteOn();
367 case S_STOP: // to S_STOP
381 case S_JUMP: // to S_JUMP
383 restartAtFrame(jumpFrame);
386 case S_JUMP_PI: // to S_JUMP_PI
388 audio->systemMuteOn();
396 restartAtFramePI(jumpFrame);
401 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
405 case S_PLAY: // to S_PLAY
412 case S_PAUSE_P: // to S_PAUSE_P
416 case S_PAUSE_I: // to S_PAUSE_I
420 case S_FFWD: // to S_FFWD
422 currentFrameNumber = getCurrentFrameNum();
423 audio->systemMuteOn();
433 case S_FBWD: // to S_FBWD
435 currentFrameNumber = getCurrentFrameNum();
436 audio->systemMuteOn();
446 case S_STOP: // to S_STOP
457 audio->systemMuteOff();
461 case S_JUMP: // to S_JUMP
464 audio->systemMuteOn();
466 restartAtFrame(jumpFrame);
469 case S_JUMP_PI: // to S_JUMP_PI
471 audio->systemMuteOn();
480 restartAtFramePI(jumpFrame);
485 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
489 case S_PLAY: // to S_PLAY
492 restartAtFrame(currentFrameNumber);
495 case S_PAUSE_P: // to S_PAUSE_P
499 case S_PAUSE_I: // to S_PAUSE_I
503 case S_FFWD: // to S_FFWD
509 case S_FBWD: // to S_FBWD
515 case S_STOP: // to S_STOP
522 audio->systemMuteOff();
526 case S_JUMP: // to S_JUMP
529 restartAtFrame(jumpFrame);
532 case S_JUMP_PI: // to S_JUMP_PI
534 restartAtFramePI(jumpFrame);
539 case S_FFWD: // from S_FFWD -----------------------------------
543 case S_PLAY: // to S_PLAY
546 ULONG stepback = USER_RESPONSE_TIME * video->getFPS() * ifactor / 1000;
547 if (stepback < currentFrameNumber)
548 currentFrameNumber -= stepback;
550 currentFrameNumber = 0;
551 restartAtFrame(currentFrameNumber);
554 case S_PAUSE_P: // to S_PAUSE_P
559 case S_PAUSE_I: // to S_PAUSE_I
565 case S_FFWD: // to S_FFWD
569 case S_FBWD: // to S_FBWD
576 case S_STOP: // to S_STOP
587 case S_JUMP: // to S_JUMP
590 restartAtFrame(jumpFrame);
593 case S_JUMP_PI: // to S_JUMP_PI
597 restartAtFramePI(jumpFrame);
602 case S_FBWD: // from S_FBWD -----------------------------------
606 case S_PLAY: // to S_PLAY
609 restartAtFrame(currentFrameNumber);
612 case S_PAUSE_P: // to S_PAUSE_P
617 case S_PAUSE_I: // to S_PAUSE_I
623 case S_FFWD: // to S_FFWD
630 case S_FBWD: // to S_FBWD
634 case S_STOP: // to S_STOP
645 case S_JUMP: // to S_JUMP
648 restartAtFrame(jumpFrame);
651 case S_JUMP_PI: // to S_JUMP_PI
655 restartAtFramePI(jumpFrame);
660 case S_STOP: // from S_STOP -----------------------------------
664 case S_PLAY: // to S_PLAY
669 audio->setStreamType(Audio::MPEG2_PES);
670 audio->systemMuteOff();
673 // FIXME use restartAtFrame here?
674 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
675 demuxer->setFrameNum(currentFrameNumber);
680 logger->log("Player", Log::DEBUG, "Immediate play");
689 case S_PAUSE_P: // to S_PAUSE_P
693 case S_PAUSE_I: // to S_PAUSE_I
697 case S_FFWD: // to S_FFWD
701 case S_FBWD: // to S_FBWD
705 case S_STOP: // to S_STOP
709 case S_JUMP: // to S_JUMP
713 case S_JUMP_PI: // to S_JUMP_PI
719 // case S_JUMP cannot be a start state because it auto flips to play
720 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
724 // ----------------------------------- Internal functions
729 pthread_mutex_lock(&mutex);
730 logger->log("Player", Log::DEBUG, "LOCKED");
733 WaitForSingleObject(mutex, INFINITE);
737 void Player::unLock()
740 logger->log("Player", Log::DEBUG, "UNLOCKING");
741 pthread_mutex_unlock(&mutex);
747 void Player::restartAtFrame(ULONG newFrame)
755 audio->setStreamType(Audio::MPEG2_PES);
758 currentFrameNumber = newFrame;
759 demuxer->setFrameNum(newFrame);
767 audio->systemMuteOff();
772 void Player::restartAtFramePI(ULONG newFrame)
775 ULONG nextiframeNumber;
783 // newFrame could be anywhere, go forwards to next I-Frame
784 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
786 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
787 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
789 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
790 if (!vdr->isConnected())
792 if (buffer) free(buffer);
797 videoLength = demuxer->stripAudio(buffer, amountReceived);
798 video->displayIFrame(buffer, videoLength);
799 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
801 currentFrameNumber = iframeNumber;
805 void Player::doConnectionLost()
807 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
808 Message* m = new Message();
809 m->to = messageReceiver;
811 m->message = Message::PLAYER_EVENT;
812 m->parameter = Player::CONNECTION_LOST;
813 messageQueue->postMessage(m);
816 // ----------------------------------- Callback
818 void Player::call(void* caller)
820 if (caller == demuxer)
822 logger->log("Player", Log::DEBUG, "Callback from demuxer");
824 if (video->getTVsize() == Video::ASPECT4X3)
826 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
830 int dxCurrentAspect = demuxer->getAspectRatio();
831 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
833 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
834 video->setAspectRatio(Video::ASPECT4X3);
836 Message* m = new Message();
838 m->to = messageReceiver;
839 m->message = Message::PLAYER_EVENT;
840 m->parameter = Player::ASPECT43;
841 messageQueue->postMessageFromOuterSpace(m);
843 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
845 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
846 video->setAspectRatio(Video::ASPECT16X9);
848 Message* m = new Message();
850 m->to = messageReceiver;
851 m->message = Message::PLAYER_EVENT;
852 m->parameter = Player::ASPECT169;
853 messageQueue->postMessageFromOuterSpace(m);
857 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
865 videoStartup = false;
873 threadSignalNoLock();
877 // ----------------------------------- Feed thread
879 void Player::threadMethod()
881 // this method used to be simple, the only thing it does
882 // is farm out to threadFeed Live/Play/Scan
883 // All the guff is to support scan hitting one end
885 if ((state == S_FFWD) || (state == S_FBWD))
888 // if this returns then scan hit one end
889 if (state == S_FFWD) // scan hit the end. stop
892 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
893 m->to = messageReceiver;
895 m->message = Message::PLAYER_EVENT;
896 m->parameter = STOP_PLAYBACK;
897 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
898 messageQueue->postMessage(m);
899 logger->log("Player", Log::DEBUG, "Message posted...");
902 // if execution gets to here, threadFeedScan hit the start, go to play mode
905 audio->setStreamType(Audio::MPEG2_PES);
908 demuxer->setFrameNum(currentFrameNumber);
914 audio->systemMuteOff();
918 if (state == S_PLAY) threadFeedPlay();
921 void Player::threadFeedPlay()
924 UINT thisRead, writeLength, thisWrite, askFor;
925 time_t lastRescan = time(NULL);
927 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
928 if (!vdr->isConnected()) { doConnectionLost(); return; }
929 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
940 // If we havn't rescanned for a while..
941 if ((lastRescan + 60) < time(NULL))
943 lengthBytes = vdr->rescanRecording(&lengthFrames);
944 if (!vdr->isConnected()) { doConnectionLost(); return; }
945 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
946 lastRescan = time(NULL);
949 if (feedPosition >= lengthBytes) break; // finished playback
953 if (startupBlockSize > lengthBytes)
954 askFor = lengthBytes; // is a very small recording!
956 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
960 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
961 askFor = lengthBytes - feedPosition;
966 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
967 feedPosition += thisRead;
969 if (!vdr->isConnected())
975 if (!threadBuffer) break;
979 int a_stream = demuxer->scan(threadBuffer, thisRead);
980 demuxer->setAudioStream(a_stream);
981 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
987 while(writeLength < thisRead)
989 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
990 writeLength += thisWrite;
994 // demuxer is full and can't take anymore
996 threadWaitForSignal();
1004 threadBuffer = NULL;
1009 logger->log("Player", Log::DEBUG, "Recording playback ends");
1011 if (videoStartup) // oh woe. there never was a stream, I was conned!
1013 videoStartup = false;
1015 MILLISLEEP(500); // I think this will solve a race
1021 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1022 m->to = messageReceiver;
1024 m->message = Message::PLAYER_EVENT;
1025 m->parameter = Player::STOP_PLAYBACK;
1026 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1027 messageQueue->postMessage(m);
1030 void Player::threadFeedScan()
1032 // This method is actually really simple - get frame from vdr,
1033 // spit it at the video chip, wait for a time. Most of the code here
1034 // is to get the wait right so that the scan occurs at the correct rate.
1036 ULONG direction = 0;
1037 ULONG baseFrameNumber = 0;
1038 ULONG iframeNumber = 0;
1039 ULONG iframeLength = 0;
1041 UINT amountReceived;
1045 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1046 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1047 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1049 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1052 int frameTimeOffset = 0; // Time in msec between frames
1053 int disp_msec = 0; // Time taken to display data
1054 int total_msec = 0; // Time taken to fetch data and display it
1057 if (state == S_FFWD) direction = 1; // and 0 for backward
1061 // Fetch I-frames until we get one that can be displayed in good time
1062 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1063 baseFrameNumber = currentFrameNumber;
1067 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1070 if (iframeNumber >= lengthFrames) return;
1071 // scan has got to the end of what we knew to be there before we started scanning
1073 baseFrameNumber = iframeNumber;
1074 frameTimeOffset = abs((int)iframeNumber - (int)currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1076 gettimeofday(&clock0, NULL);
1078 clock0 = timeGetTime();
1082 while (clock2.tv_sec != 0 &&
1083 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1084 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1086 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1088 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1090 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1092 if (!vdr->isConnected())
1094 if (threadBuffer) free(threadBuffer);
1100 gettimeofday(&clock1, NULL);
1101 if (clock2.tv_sec != 0)
1102 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1103 + (clock2.tv_usec - clock1.tv_usec) / 1000
1104 + frameTimeOffset - disp_msec;
1106 clock1 = timeGetTime();
1108 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1110 if (sleepTime < 0) sleepTime = 0;
1112 MILLISLEEP(sleepTime);
1113 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1115 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1116 video->displayIFrame(threadBuffer, videoLength);
1117 currentFrameNumber = iframeNumber;
1119 threadBuffer = NULL;
1122 gettimeofday(&clock2, NULL);
1123 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1124 + (clock2.tv_usec - clock0.tv_usec) / 1000
1126 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1127 + (clock2.tv_usec - clock1.tv_usec) / 1000
1130 clock2 = timeGetTime();
1131 total_msec = clock2 - clock0 - sleepTime;
1132 disp_msec = clock2 - clock1 - sleepTime;
1134 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1138 void Player::threadPostStopCleanup()
1143 threadBuffer = NULL;
1147 // ----------------------------------- Dev
1150 void Player::test1()
1152 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1155 void Player::test2()
1157 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");