2 Copyright 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 // ----------------------------------- Called from outside, one offs or info funcs
25 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, bool tIsRecording, bool tIsRadio)
26 : vfeed(this), afeed(this)
28 messageQueue = tmessageQueue;
29 messageReceiver = tmessageReceiver;
30 audio = Audio::getInstance();
31 video = Video::getInstance();
32 logger = Log::getInstance();
33 vdr = VDR::getInstance();
37 currentFrameNumber = 0;
43 isRecording = tIsRecording;
51 startupBlockSize = 20000;
52 preBufferSize = 20000;
53 video->turnVideoOff();
58 startupBlockSize = 250000;
59 preBufferSize = 500000;
66 if (initted) shutdown();
71 if (initted) return 0;
73 pthread_mutex_init(&mutex, NULL);
75 mutex=CreateMutex(NULL,FALSE,NULL);
78 demuxer = new DemuxerVDR();
79 if (!demuxer) return 0;
81 if (!demuxer->init(this, audio, video))
83 logger->log("Player", Log::ERR, "Demuxer failed to init");
99 int Player::shutdown()
101 if (!initted) return 0;
115 void Player::setStartFrame(ULONG startFrame)
117 currentFrameNumber = startFrame;
120 void Player::setLengthBytes(ULLONG length)
122 lengthBytes = length;
123 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
126 void Player::setLengthFrames(ULONG length)
128 lengthFrames = length;
129 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
132 ULONG Player::getLengthFrames()
137 ULONG Player::getCurrentFrameNum()
139 if (startup) return 0;
144 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
148 return currentFrameNumber;
150 return 0; // shouldn't happen
154 bool* Player::getDemuxerAudioChannels()
156 return demuxer->getmpAudioChannels();
159 int Player::getCurrentAudioChannel()
161 return demuxer->getselAudioChannel();
164 void Player::setAudioChannel(int newChannel)
166 demuxer->setmpAudioChannel(newChannel);
169 // ----------------------------------- Externally called events
173 if (!initted) return;
174 if (state == S_PLAY) return;
176 bool doUnlock = false;
177 if (state == S_PAUSE_P) doUnlock = true;
179 if (doUnlock) unLock();
184 if (!initted) return;
185 if (state == S_STOP) return;
187 logger->log("Player", Log::DEBUG, "Stop called lock");
194 if (!initted) return;
197 if ((state == S_FFWD) || (state == S_FBWD))
199 switchState(S_PAUSE_I);
201 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
207 switchState(S_PAUSE_P);
213 void Player::fastForward()
215 if (!initted) return;
223 case 4: ifactor = 8; break;
224 case 8: ifactor = 16; break;
225 case 16: ifactor = 32; break;
226 case 32: ifactor = 4; break;
237 void Player::fastBackward()
239 if (!initted) return;
247 case 4: ifactor = 8; break;
248 case 8: ifactor = 16; break;
249 case 16: ifactor = 32; break;
250 case 32: ifactor = 4; break;
261 void Player::jumpToPercent(int percent)
264 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
265 ULONG newFrame = percent * lengthFrames / 100;
266 switchState(S_JUMP, newFrame);
267 // unLock(); - let thread unlock this
270 void Player::skipForward(int seconds)
273 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
274 ULONG newFrame = getCurrentFrameNum();
275 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
276 newFrame += seconds * video->getFPS();
277 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
278 else switchState(S_JUMP, newFrame);
279 // unLock(); - let thread unlock this
282 void Player::skipBackward(int seconds)
285 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
286 long newFrame = getCurrentFrameNum();
287 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
288 newFrame -= seconds * video->getFPS();
289 if (newFrame < 0) newFrame = 0;
290 switchState(S_JUMP, newFrame);
291 // unLock(); - let thread unlock this
294 // ----------------------------------- Implementations called events
296 void Player::switchState(UCHAR toState, ULONG jumpFrame)
298 if (!initted) return;
300 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
302 switch(state) // current state selector
304 case S_PLAY: // from S_PLAY -----------------------------------
308 case S_PLAY: // to S_PLAY
312 case S_PAUSE_P: // to S_PAUSE_P
319 case S_PAUSE_I: // to S_PAUSE_I
324 case S_FFWD: // to S_FFWD
326 currentFrameNumber = getCurrentFrameNum();
327 audio->systemMuteOn();
336 case S_FBWD: // to S_FBWD
338 currentFrameNumber = getCurrentFrameNum();
339 audio->systemMuteOn();
348 case S_STOP: // to S_STOP
362 case S_JUMP: // to S_JUMP
364 restartAtFrame(jumpFrame);
369 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
373 case S_PLAY: // to S_PLAY
380 case S_PAUSE_P: // to S_PAUSE_P
384 case S_PAUSE_I: // to S_PAUSE_I
388 case S_FFWD: // to S_FFWD
390 currentFrameNumber = getCurrentFrameNum();
391 audio->systemMuteOn();
401 case S_FBWD: // to S_FBWD
403 currentFrameNumber = getCurrentFrameNum();
404 audio->systemMuteOn();
414 case S_STOP: // to S_STOP
425 audio->systemMuteOff();
429 case S_JUMP: // to S_JUMP
433 restartAtFrame(jumpFrame);
438 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
442 case S_PLAY: // to S_PLAY
445 restartAtFrame(currentFrameNumber);
448 case S_PAUSE_P: // to S_PAUSE_P
452 case S_PAUSE_I: // to S_PAUSE_I
456 case S_FFWD: // to S_FFWD
462 case S_FBWD: // to S_FBWD
468 case S_STOP: // to S_STOP
475 audio->systemMuteOff();
479 case S_JUMP: // to S_JUMP
482 restartAtFrame(jumpFrame);
487 case S_FFWD: // from S_FFWD -----------------------------------
491 case S_PLAY: // to S_PLAY
494 restartAtFrame(currentFrameNumber);
497 case S_PAUSE_P: // to S_PAUSE_P
502 case S_PAUSE_I: // to S_PAUSE_I
508 case S_FFWD: // to S_FFWD
512 case S_FBWD: // to S_FBWD
519 case S_STOP: // to S_STOP
530 case S_JUMP: // to S_JUMP
533 restartAtFrame(jumpFrame);
538 case S_FBWD: // from S_FBWD -----------------------------------
542 case S_PLAY: // to S_PLAY
545 restartAtFrame(currentFrameNumber);
548 case S_PAUSE_P: // to S_PAUSE_P
553 case S_PAUSE_I: // to S_PAUSE_I
559 case S_FFWD: // to S_FFWD
566 case S_FBWD: // to S_FBWD
570 case S_STOP: // to S_STOP
581 case S_JUMP: // to S_JUMP
584 restartAtFrame(jumpFrame);
589 case S_STOP: // from S_STOP -----------------------------------
593 case S_PLAY: // to S_PLAY
598 audio->systemMuteOff();
603 // FIXME use restartAtFrame here?
604 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
605 demuxer->setFrameNum(currentFrameNumber);
607 if (!isRadio) demuxer->seek();
615 logger->log("Player", Log::DEBUG, "Immediate play");
623 else // do prebuffering
625 logger->log("Player", Log::DEBUG, "Prebuffering...");
630 case S_PAUSE_P: // to S_PAUSE_P
634 case S_PAUSE_I: // to S_PAUSE_I
638 case S_FFWD: // to S_FFWD
642 case S_FBWD: // to S_FBWD
646 case S_STOP: // to S_STOP
650 case S_JUMP: // to S_JUMP
656 // case S_JUMP cannot be selected as a start state because it auto flips to play
660 // ----------------------------------- Internal functions
665 pthread_mutex_lock(&mutex);
666 logger->log("Player", Log::DEBUG, "LOCKED");
669 WaitForSingleObject(mutex, INFINITE);
673 void Player::unLock()
676 logger->log("Player", Log::DEBUG, "UNLOCKING");
677 pthread_mutex_unlock(&mutex);
683 void Player::restartAtFrame(ULONG newFrame)
692 if (!isRadio) demuxer->seek();
693 currentFrameNumber = newFrame;
694 demuxer->setFrameNum(newFrame);
702 audio->systemMuteOff();
706 void Player::doConnectionLost()
708 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
709 Message* m = new Message();
710 m->to = messageReceiver;
712 m->message = Message::PLAYER_EVENT;
713 m->parameter = Player::CONNECTION_LOST;
714 messageQueue->postMessage(m);
717 // ----------------------------------- Callback
719 void Player::call(void* caller)
721 if (caller == demuxer)
723 logger->log("Player", Log::DEBUG, "Callback from demuxer");
725 if (video->getTVsize() == Video::ASPECT4X3)
727 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
731 int dxCurrentAspect = demuxer->getAspectRatio();
732 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
734 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
735 video->setAspectRatio(Video::ASPECT4X3);
737 Message* m = new Message();
739 m->to = messageReceiver;
740 m->message = Message::PLAYER_EVENT;
741 m->parameter = Player::ASPECT43;
742 messageQueue->postMessageFromOuterSpace(m);
744 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
746 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
747 video->setAspectRatio(Video::ASPECT16X9);
749 Message* m = new Message();
751 m->to = messageReceiver;
752 m->message = Message::PLAYER_EVENT;
753 m->parameter = Player::ASPECT169;
754 messageQueue->postMessageFromOuterSpace(m);
758 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
766 videoStartup = false;
774 threadSignalNoLock();
778 // ----------------------------------- Feed thread
780 void Player::threadMethod()
782 // this method used to be simple, the only thing it does
783 // is farm out to threadFeed Live/Play/Scan
784 // All the guff is to support scan hitting one end
788 if ((state == S_FFWD) || (state == S_FBWD))
791 // if this returns then scan hit one end
792 if (state == S_FFWD) // scan hit the end. stop
795 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
796 m->to = messageReceiver;
798 m->message = Message::PLAYER_EVENT;
799 m->parameter = STOP_PLAYBACK;
800 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
801 messageQueue->postMessage(m);
802 logger->log("Player", Log::DEBUG, "Message posted...");
805 // if execution gets to here, threadFeedScan hit the start, go to play mode
809 if (!isRadio) demuxer->seek();
810 demuxer->setFrameNum(currentFrameNumber);
816 audio->systemMuteOff();
820 if (state == S_PLAY) threadFeedPlay();
828 void Player::threadFeedLive()
833 UINT preBufferTotal = 0;
845 askFor = startupBlockSize; // find audio streams sized block
847 askFor = blockSize; // normal
849 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
851 if (!vdr->isConnected())
857 if (!threadBuffer) break;
861 int a_stream = demuxer->scan(threadBuffer, thisRead);
862 demuxer->setAudioStream(a_stream);
863 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
869 preBufferTotal += thisRead;
870 if (preBufferTotal >= preBufferSize)
872 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
874 preBuffering = false;
878 // FIXME check the win32 code on the mvp
895 // unLock(); // thread will be locked by play until here
896 // FIXME - see if this can segfault because it is starting threads out of the master mutex
902 while(writeLength < thisRead)
904 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
905 writeLength += thisWrite;
909 // demuxer is full and can't take anymore
911 threadWaitForSignal();
923 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
925 if (videoStartup) // oh woe. there never was a stream, I was conned!
927 videoStartup = false;
929 MILLISLEEP(500); // I think this will solve a race
934 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
935 m->to = messageReceiver;
937 m->message = Message::PLAYER_EVENT;
938 m->parameter = Player::STREAM_END;
939 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
940 messageQueue->postMessage(m);
941 logger->log("Player", Log::DEBUG, "Message posted...");
944 void Player::threadFeedPlay()
947 UINT thisRead, writeLength, thisWrite, askFor;
948 time_t lastRescan = time(NULL);
950 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
951 if (!vdr->isConnected()) { doConnectionLost(); return; }
952 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
963 // If we havn't rescanned for a while..
964 if ((lastRescan + 60) < time(NULL))
966 lengthBytes = vdr->rescanRecording(&lengthFrames);
967 if (!vdr->isConnected()) { doConnectionLost(); return; }
968 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
969 lastRescan = time(NULL);
972 if (feedPosition >= lengthBytes) break; // finished playback
976 if (startupBlockSize > lengthBytes)
977 askFor = lengthBytes; // is a very small recording!
979 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
983 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
984 askFor = lengthBytes - feedPosition;
989 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
990 feedPosition += thisRead;
992 if (!vdr->isConnected())
998 if (!threadBuffer) break;
1002 int a_stream = demuxer->scan(threadBuffer, thisRead);
1003 demuxer->setAudioStream(a_stream);
1004 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1010 while(writeLength < thisRead)
1012 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1013 writeLength += thisWrite;
1017 // demuxer is full and can't take anymore
1019 threadWaitForSignal();
1027 threadBuffer = NULL;
1032 logger->log("Player", Log::DEBUG, "Recording playback ends");
1034 if (videoStartup) // oh woe. there never was a stream, I was conned!
1036 videoStartup = false;
1038 MILLISLEEP(500); // I think this will solve a race
1044 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1045 m->to = messageReceiver;
1047 m->message = Message::PLAYER_EVENT;
1048 m->parameter = Player::STOP_PLAYBACK;
1049 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1050 messageQueue->postMessage(m);
1053 void Player::threadFeedScan()
1055 // This method is actually really simple - get frame from vdr,
1056 // spit it at the video chip, wait for a time. Most of the code here
1057 // is to get the wait right so that the scan occurs at the correct rate.
1059 ULONG direction = 0;
1060 ULONG baseFrameNumber = 0;
1061 ULONG iframeNumber = 0;
1062 ULONG iframeLength = 0;
1064 UINT amountReceived;
1068 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1069 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1070 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1072 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1075 int frameTimeOffset = 0; // Time in msec between frames
1076 int disp_msec = 0; // Time taken to display data
1077 int total_msec = 0; // Time taken to fetch data and display it
1080 if (state == S_FFWD) direction = 1; // and 0 for backward
1084 // Fetch I-frames until we get one that can be displayed in good time
1085 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1086 baseFrameNumber = currentFrameNumber;
1089 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1092 baseFrameNumber = iframeNumber;
1093 frameTimeOffset = abs(iframeNumber - currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1095 gettimeofday(&clock0, NULL);
1097 clock0 = timeGetTime();
1101 while (clock2.tv_sec != 0 &&
1102 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1103 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1105 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1107 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1109 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1111 gettimeofday(&clock1, NULL);
1112 if (clock2.tv_sec != 0)
1113 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1114 + (clock2.tv_usec - clock1.tv_usec) / 1000
1115 + frameTimeOffset - disp_msec;
1117 clock1 = timeGetTime();
1119 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1121 if (sleepTime < 0) sleepTime = 0;
1123 MILLISLEEP(sleepTime);
1124 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1126 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1127 video->displayIFrame(threadBuffer, videoLength);
1128 currentFrameNumber = iframeNumber;
1130 threadBuffer = NULL;
1133 gettimeofday(&clock2, NULL);
1134 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1135 + (clock2.tv_usec - clock0.tv_usec) / 1000
1137 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1138 + (clock2.tv_usec - clock1.tv_usec) / 1000
1141 clock2 = timeGetTime();
1142 total_msec = clock2 - clock0 - sleepTime;
1143 disp_msec = clock2 - clock1 - sleepTime;
1145 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1149 void Player::threadPostStopCleanup()
1154 threadBuffer = NULL;
1158 // ----------------------------------- Dev
1161 void Player::test1()
1163 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1165 // video->setAspectRatio(Video::ASPECT4X3);
1168 void Player::test2()
1170 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1171 video->setAspectRatio(Video::ASPECT16X9);