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* messageQueue, bool tIsRecording, bool tIsRadio)
26 : vfeed(this), afeed(this)
28 commandMessageQueue = messageQueue;
29 audio = Audio::getInstance();
30 video = Video::getInstance();
31 logger = Log::getInstance();
32 vdr = VDR::getInstance();
36 currentFrameNumber = 0;
42 isRecording = tIsRecording;
50 startupBlockSize = 60000;
51 video->turnVideoOff();
56 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))
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 // ----------------------------------- Externally called events
155 if (!initted) return;
156 if (state == S_PLAY) return;
158 bool doUnlock = false;
159 if (state == S_PAUSE_P) doUnlock = true;
161 if (doUnlock) unLock();
166 if (!initted) return;
167 if (state == S_STOP) return;
169 logger->log("Player", Log::DEBUG, "Stop called lock");
176 if (!initted) return;
179 if ((state == S_FFWD) || (state == S_FBWD))
181 switchState(S_PAUSE_I);
183 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
189 switchState(S_PAUSE_P);
195 void Player::fastForward()
197 if (!initted) return;
205 case 2: ifactor = 4; break;
206 case 4: ifactor = 6; break;
207 case 6: ifactor = 8; break;
208 case 8: ifactor = 2; break;
219 void Player::fastBackward()
221 if (!initted) return;
229 case 2: ifactor = 4; break;
230 case 4: ifactor = 6; break;
231 case 6: ifactor = 8; break;
232 case 8: ifactor = 2; break;
243 void Player::jumpToPercent(int percent)
246 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
247 ULONG newFrame = percent * lengthFrames / 100;
248 switchState(S_JUMP, newFrame);
249 // unLock(); - let thread unlock this
252 void Player::skipForward(int seconds)
255 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
256 ULONG newFrame = getCurrentFrameNum();
257 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
258 newFrame += seconds * video->getFPS();
259 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
260 else switchState(S_JUMP, newFrame);
261 // unLock(); - let thread unlock this
264 void Player::skipBackward(int seconds)
267 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
268 long newFrame = getCurrentFrameNum();
269 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
270 newFrame -= seconds * video->getFPS();
271 if (newFrame < 0) newFrame = 0;
272 switchState(S_JUMP, newFrame);
273 // unLock(); - let thread unlock this
276 // ----------------------------------- Implementations called events
278 void Player::switchState(UCHAR toState, ULONG jumpFrame)
280 if (!initted) return;
282 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
284 switch(state) // current state selector
286 case S_PLAY: // from S_PLAY -----------------------------------
290 case S_PLAY: // to S_PLAY
294 case S_PAUSE_P: // to S_PAUSE_P
301 case S_PAUSE_I: // to S_PAUSE_I
306 case S_FFWD: // to S_FFWD
308 currentFrameNumber = getCurrentFrameNum();
309 audio->systemMuteOn();
318 case S_FBWD: // to S_FBWD
320 currentFrameNumber = getCurrentFrameNum();
321 audio->systemMuteOn();
330 case S_STOP: // to S_STOP
344 case S_JUMP: // to S_JUMP
346 restartAtFrame(jumpFrame);
351 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
355 case S_PLAY: // to S_PLAY
362 case S_PAUSE_P: // to S_PAUSE_P
366 case S_PAUSE_I: // to S_PAUSE_I
370 case S_FFWD: // to S_FFWD
372 currentFrameNumber = getCurrentFrameNum();
373 audio->systemMuteOn();
383 case S_FBWD: // to S_FBWD
385 currentFrameNumber = getCurrentFrameNum();
386 audio->systemMuteOn();
396 case S_STOP: // to S_STOP
407 audio->systemMuteOff();
411 case S_JUMP: // to S_JUMP
415 restartAtFrame(jumpFrame);
420 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
424 case S_PLAY: // to S_PLAY
427 restartAtFrame(currentFrameNumber);
430 case S_PAUSE_P: // to S_PAUSE_P
434 case S_PAUSE_I: // to S_PAUSE_I
438 case S_FFWD: // to S_FFWD
444 case S_FBWD: // to S_FBWD
450 case S_STOP: // to S_STOP
457 audio->systemMuteOff();
461 case S_JUMP: // to S_JUMP
464 restartAtFrame(jumpFrame);
469 case S_FFWD: // from S_FFWD -----------------------------------
473 case S_PLAY: // to S_PLAY
476 restartAtFrame(currentFrameNumber);
479 case S_PAUSE_P: // to S_PAUSE_P
484 case S_PAUSE_I: // to S_PAUSE_I
490 case S_FFWD: // to S_FFWD
494 case S_FBWD: // to S_FBWD
501 case S_STOP: // to S_STOP
512 case S_JUMP: // to S_JUMP
515 restartAtFrame(jumpFrame);
520 case S_FBWD: // from S_FBWD -----------------------------------
524 case S_PLAY: // to S_PLAY
527 restartAtFrame(currentFrameNumber);
530 case S_PAUSE_P: // to S_PAUSE_P
535 case S_PAUSE_I: // to S_PAUSE_I
541 case S_FFWD: // to S_FFWD
548 case S_FBWD: // to S_FBWD
552 case S_STOP: // to S_STOP
563 case S_JUMP: // to S_JUMP
566 restartAtFrame(jumpFrame);
571 case S_STOP: // from S_STOP -----------------------------------
575 case S_PLAY: // to S_PLAY
580 audio->systemMuteOff();
585 // FIXME use restartAtFrame here?
586 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
587 demuxer->setFrameNum(currentFrameNumber);
589 if (!isRadio) demuxer->seek();
597 logger->log("Player", Log::DEBUG, "Immediate play");
605 else // do prebuffering
607 logger->log("Player", Log::DEBUG, "Prebuffering...");
612 case S_PAUSE_P: // to S_PAUSE_P
616 case S_PAUSE_I: // to S_PAUSE_I
620 case S_FFWD: // to S_FFWD
624 case S_FBWD: // to S_FBWD
628 case S_STOP: // to S_STOP
632 case S_JUMP: // to S_JUMP
638 // case S_JUMP cannot be selected as a start state because it auto flips to play
642 // ----------------------------------- Internal functions
647 pthread_mutex_lock(&mutex);
648 logger->log("Player", Log::DEBUG, "LOCKED");
651 WaitForSingleObject(mutex, INFINITE);
655 void Player::unLock()
658 logger->log("Player", Log::DEBUG, "UNLOCKING");
659 pthread_mutex_unlock(&mutex);
665 void Player::restartAtFrame(ULONG newFrame)
674 if (!isRadio) demuxer->seek();
675 currentFrameNumber = newFrame;
676 demuxer->setFrameNum(newFrame);
684 audio->systemMuteOff();
688 void Player::doConnectionLost()
690 Message* m = new Message();
691 m->message = Message::CONNECTION_LOST;
693 commandMessageQueue->postMessage(m);
696 // ----------------------------------- Callback
698 void Player::call(void* caller)
700 if (caller == demuxer)
702 logger->log("Player", Log::DEBUG, "Callback from demuxer");
704 if (video->getTVsize() == Video::ASPECT4X3)
706 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
710 int dxCurrentAspect = demuxer->getAspectRatio();
711 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
713 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
714 video->setAspectRatio(Video::ASPECT4X3);
716 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
718 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
719 video->setAspectRatio(Video::ASPECT16X9);
723 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
731 videoStartup = false;
739 threadSignalNoLock();
743 // ----------------------------------- Feed thread
745 void Player::threadMethod()
747 // this method used to be simple, the only thing it does
748 // is farm out to threadFeed Live/Play/Scan
749 // All the guff is to support scan hitting one end
753 if ((state == S_FFWD) || (state == S_FBWD))
756 // if this returns then scan hit one end
757 if (state == S_FFWD) // scan hit the end. stop
760 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
761 m->message = Message::STOP_PLAYBACK;
762 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
763 commandMessageQueue->postMessage(m);
764 logger->log("Player", Log::DEBUG, "Message posted...");
767 // if execution gets to here, threadFeedScan hit the start, go to play mode
771 if (!isRadio) demuxer->seek();
772 demuxer->setFrameNum(currentFrameNumber);
778 audio->systemMuteOff();
782 if (state == S_PLAY) threadFeedPlay();
790 void Player::threadFeedLive()
795 UINT preBufferTotal = 0;
807 askFor = startupBlockSize; // find audio streams sized block
809 askFor = blockSize; // normal
811 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
813 if (!vdr->isConnected())
819 if (!threadBuffer) break;
823 int a_stream = demuxer->scan(threadBuffer, thisRead);
824 demuxer->setAudioStream(a_stream);
825 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
831 preBufferTotal += thisRead;
832 if (preBufferTotal > 500000)
834 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
836 preBuffering = false;
845 // unLock(); // thread will be locked by play until here
846 // FIXME - see if this can segfault because it is starting threads out of the master mutex
852 while(writeLength < thisRead)
854 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
855 writeLength += thisWrite;
859 // demuxer is full and can't take anymore
861 threadWaitForSignal();
873 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
875 if (videoStartup) // oh woe. there never was a stream, I was conned!
877 videoStartup = false;
879 MILLISLEEP(500); // I think this will solve a race
884 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
885 m->message = Message::STREAM_END;
886 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
887 commandMessageQueue->postMessage(m);
888 logger->log("Player", Log::DEBUG, "Message posted...");
891 void Player::threadFeedPlay()
894 UINT thisRead, writeLength, thisWrite, askFor;
895 time_t lastRescan = time(NULL);
897 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
898 if (!vdr->isConnected()) { doConnectionLost(); return; }
899 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
910 // If we havn't rescanned for a while..
911 if ((lastRescan + 60) < time(NULL))
913 lengthBytes = vdr->rescanRecording(&lengthFrames);
914 if (!vdr->isConnected()) { doConnectionLost(); return; }
915 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
916 lastRescan = time(NULL);
919 if (feedPosition >= lengthBytes) break; // finished playback
923 if (startupBlockSize > lengthBytes)
924 askFor = lengthBytes; // is a very small recording!
926 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
930 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
931 askFor = lengthBytes - feedPosition;
936 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
937 feedPosition += thisRead;
939 if (!vdr->isConnected())
945 if (!threadBuffer) break;
949 int a_stream = demuxer->scan(threadBuffer, thisRead);
950 demuxer->setAudioStream(a_stream);
951 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
957 while(writeLength < thisRead)
959 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
960 writeLength += thisWrite;
964 // demuxer is full and can't take anymore
966 threadWaitForSignal();
979 logger->log("Player", Log::DEBUG, "Recording playback ends");
981 if (videoStartup) // oh woe. there never was a stream, I was conned!
983 videoStartup = false;
985 MILLISLEEP(500); // I think this will solve a race
990 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
991 m->message = Message::STOP_PLAYBACK; // recording
992 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
993 commandMessageQueue->postMessage(m);
994 logger->log("Player", Log::DEBUG, "Message posted...");
997 void Player::threadFeedScan()
999 // This method is actually really simple - get frame from vdr,
1000 // spit it at the video chip, wait for a time. Most of the code here
1001 // is to get the wait right so that the scan occurs at the correct rate.
1003 ULONG direction = 0;
1004 ULONG iframeNumber = 0;
1005 ULONG iframeLength = 0;
1007 UINT amountReceived;
1013 struct timeval loopTime;
1014 struct timeval loopTimeN;
1021 ULONG sleepTime = 0;
1024 if (state == S_FFWD) direction = 1; // and 0 for backward
1027 gettimeofday(&loopTime, NULL);
1029 loopTime = timeGetTime();
1034 if (!vdr->getNextIFrame(currentFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) break;
1035 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1036 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1037 video->displayIFrame(threadBuffer, videoLength);
1038 iframesep = abs(iframeNumber - currentFrameNumber);
1039 currentFrameNumber = iframeNumber;
1041 threadBuffer = NULL;
1044 // Calculate next jump delay
1046 gettimeofday(&loopTimeN, NULL);
1047 realLoopTime = ((loopTimeN.tv_sec - loopTime.tv_sec) * 1000) + ((loopTimeN.tv_usec - loopTime.tv_usec) / 1000);
1048 loopTime.tv_sec = loopTimeN.tv_sec;
1049 loopTime.tv_usec = loopTimeN.tv_usec;
1051 loopTimeN = timeGetTime();
1052 realLoopTime = loopTimeN - loopTime;
1053 loopTime = loopTimeN;
1057 if (sleepTime) offset = realLoopTime - sleepTime;
1058 if (realLoopTime < sleepTime) offset = 0; // sanity check - loop was shorter than requested?
1060 sleepTime = (1000 * iframesep) / (video->getFPS() * ifactor);
1061 if (offset > sleepTime) sleepTime = 0;
1062 else sleepTime -= offset;
1064 logger->log("Player", Log::DEBUG, "iframesep %lu, fps %u, ifactor %u, offset %lu, sleep %lu", iframesep, video->getFPS(), ifactor, offset, sleepTime);
1065 MILLISLEEP(sleepTime);
1068 // scan has hit one end
1071 void Player::threadPostStopCleanup()
1076 threadBuffer = NULL;
1080 // ----------------------------------- Dev
1083 void Player::test1()
1085 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1087 // video->setAspectRatio(Video::ASPECT4X3);
1090 void Player::test2()
1092 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1093 video->setAspectRatio(Video::ASPECT16X9);