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 = 20000;
51 preBufferSize = 20000;
52 video->turnVideoOff();
57 startupBlockSize = 250000;
58 preBufferSize = 500000;
65 if (initted) shutdown();
70 if (initted) return 0;
72 pthread_mutex_init(&mutex, NULL);
74 mutex=CreateMutex(NULL,FALSE,NULL);
77 demuxer = new DemuxerVDR();
78 if (!demuxer) return 0;
80 if (!demuxer->init(this, audio, video))
82 logger->log("Player", Log::ERR, "Demuxer failed to init");
98 int Player::shutdown()
100 if (!initted) return 0;
114 void Player::setStartFrame(ULONG startFrame)
116 currentFrameNumber = startFrame;
119 void Player::setLengthBytes(ULLONG length)
121 lengthBytes = length;
122 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
125 void Player::setLengthFrames(ULONG length)
127 lengthFrames = length;
128 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
131 ULONG Player::getLengthFrames()
136 ULONG Player::getCurrentFrameNum()
138 if (startup) return 0;
143 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
147 return currentFrameNumber;
149 return 0; // shouldn't happen
153 // ----------------------------------- Externally called events
157 if (!initted) return;
158 if (state == S_PLAY) return;
160 bool doUnlock = false;
161 if (state == S_PAUSE_P) doUnlock = true;
163 if (doUnlock) unLock();
168 if (!initted) return;
169 if (state == S_STOP) return;
171 logger->log("Player", Log::DEBUG, "Stop called lock");
178 if (!initted) return;
181 if ((state == S_FFWD) || (state == S_FBWD))
183 switchState(S_PAUSE_I);
185 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
191 switchState(S_PAUSE_P);
197 void Player::fastForward()
199 if (!initted) return;
207 case 2: ifactor = 4; break;
208 case 4: ifactor = 8; break;
209 case 8: ifactor = 16; break;
210 case 16: ifactor = 64; break;
211 case 64: ifactor = 2; break;
222 void Player::fastBackward()
224 if (!initted) return;
232 case 2: ifactor = 4; break;
233 case 4: ifactor = 8; break;
234 case 8: ifactor = 16; break;
235 case 16: ifactor = 64; break;
236 case 64: ifactor = 2; break;
247 void Player::jumpToPercent(int percent)
250 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
251 ULONG newFrame = percent * lengthFrames / 100;
252 switchState(S_JUMP, newFrame);
253 // unLock(); - let thread unlock this
256 void Player::skipForward(int seconds)
259 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
260 ULONG newFrame = getCurrentFrameNum();
261 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
262 newFrame += seconds * video->getFPS();
263 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
264 else switchState(S_JUMP, newFrame);
265 // unLock(); - let thread unlock this
268 void Player::skipBackward(int seconds)
271 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
272 long newFrame = getCurrentFrameNum();
273 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
274 newFrame -= seconds * video->getFPS();
275 if (newFrame < 0) newFrame = 0;
276 switchState(S_JUMP, newFrame);
277 // unLock(); - let thread unlock this
280 // ----------------------------------- Implementations called events
282 void Player::switchState(UCHAR toState, ULONG jumpFrame)
284 if (!initted) return;
286 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
288 switch(state) // current state selector
290 case S_PLAY: // from S_PLAY -----------------------------------
294 case S_PLAY: // to S_PLAY
298 case S_PAUSE_P: // to S_PAUSE_P
305 case S_PAUSE_I: // to S_PAUSE_I
310 case S_FFWD: // to S_FFWD
312 currentFrameNumber = getCurrentFrameNum();
313 audio->systemMuteOn();
322 case S_FBWD: // to S_FBWD
324 currentFrameNumber = getCurrentFrameNum();
325 audio->systemMuteOn();
334 case S_STOP: // to S_STOP
348 case S_JUMP: // to S_JUMP
350 restartAtFrame(jumpFrame);
355 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
359 case S_PLAY: // to S_PLAY
366 case S_PAUSE_P: // to S_PAUSE_P
370 case S_PAUSE_I: // to S_PAUSE_I
374 case S_FFWD: // to S_FFWD
376 currentFrameNumber = getCurrentFrameNum();
377 audio->systemMuteOn();
387 case S_FBWD: // to S_FBWD
389 currentFrameNumber = getCurrentFrameNum();
390 audio->systemMuteOn();
400 case S_STOP: // to S_STOP
411 audio->systemMuteOff();
415 case S_JUMP: // to S_JUMP
419 restartAtFrame(jumpFrame);
424 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
428 case S_PLAY: // to S_PLAY
431 restartAtFrame(currentFrameNumber);
434 case S_PAUSE_P: // to S_PAUSE_P
438 case S_PAUSE_I: // to S_PAUSE_I
442 case S_FFWD: // to S_FFWD
448 case S_FBWD: // to S_FBWD
454 case S_STOP: // to S_STOP
461 audio->systemMuteOff();
465 case S_JUMP: // to S_JUMP
468 restartAtFrame(jumpFrame);
473 case S_FFWD: // from S_FFWD -----------------------------------
477 case S_PLAY: // to S_PLAY
480 restartAtFrame(currentFrameNumber);
483 case S_PAUSE_P: // to S_PAUSE_P
488 case S_PAUSE_I: // to S_PAUSE_I
494 case S_FFWD: // to S_FFWD
498 case S_FBWD: // to S_FBWD
505 case S_STOP: // to S_STOP
516 case S_JUMP: // to S_JUMP
519 restartAtFrame(jumpFrame);
524 case S_FBWD: // from S_FBWD -----------------------------------
528 case S_PLAY: // to S_PLAY
531 restartAtFrame(currentFrameNumber);
534 case S_PAUSE_P: // to S_PAUSE_P
539 case S_PAUSE_I: // to S_PAUSE_I
545 case S_FFWD: // to S_FFWD
552 case S_FBWD: // to S_FBWD
556 case S_STOP: // to S_STOP
567 case S_JUMP: // to S_JUMP
570 restartAtFrame(jumpFrame);
575 case S_STOP: // from S_STOP -----------------------------------
579 case S_PLAY: // to S_PLAY
584 audio->systemMuteOff();
589 // FIXME use restartAtFrame here?
590 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
591 demuxer->setFrameNum(currentFrameNumber);
593 if (!isRadio) demuxer->seek();
601 logger->log("Player", Log::DEBUG, "Immediate play");
609 else // do prebuffering
611 logger->log("Player", Log::DEBUG, "Prebuffering...");
616 case S_PAUSE_P: // to S_PAUSE_P
620 case S_PAUSE_I: // to S_PAUSE_I
624 case S_FFWD: // to S_FFWD
628 case S_FBWD: // to S_FBWD
632 case S_STOP: // to S_STOP
636 case S_JUMP: // to S_JUMP
642 // case S_JUMP cannot be selected as a start state because it auto flips to play
646 // ----------------------------------- Internal functions
651 pthread_mutex_lock(&mutex);
652 logger->log("Player", Log::DEBUG, "LOCKED");
655 WaitForSingleObject(mutex, INFINITE);
659 void Player::unLock()
662 logger->log("Player", Log::DEBUG, "UNLOCKING");
663 pthread_mutex_unlock(&mutex);
669 void Player::restartAtFrame(ULONG newFrame)
678 if (!isRadio) demuxer->seek();
679 currentFrameNumber = newFrame;
680 demuxer->setFrameNum(newFrame);
688 audio->systemMuteOff();
692 void Player::doConnectionLost()
694 Message* m = new Message();
695 m->message = Message::CONNECTION_LOST;
697 commandMessageQueue->postMessage(m);
700 // ----------------------------------- Callback
702 void Player::call(void* caller)
704 if (caller == demuxer)
706 logger->log("Player", Log::DEBUG, "Callback from demuxer");
708 if (video->getTVsize() == Video::ASPECT4X3)
710 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
714 int dxCurrentAspect = demuxer->getAspectRatio();
715 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
717 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
718 video->setAspectRatio(Video::ASPECT4X3);
720 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
722 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
723 video->setAspectRatio(Video::ASPECT16X9);
727 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
735 videoStartup = false;
743 threadSignalNoLock();
747 // ----------------------------------- Feed thread
749 void Player::threadMethod()
751 // this method used to be simple, the only thing it does
752 // is farm out to threadFeed Live/Play/Scan
753 // All the guff is to support scan hitting one end
757 if ((state == S_FFWD) || (state == S_FBWD))
760 // if this returns then scan hit one end
761 if (state == S_FFWD) // scan hit the end. stop
764 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
765 m->message = Message::STOP_PLAYBACK;
766 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
767 commandMessageQueue->postMessage(m);
768 logger->log("Player", Log::DEBUG, "Message posted...");
771 // if execution gets to here, threadFeedScan hit the start, go to play mode
775 if (!isRadio) demuxer->seek();
776 demuxer->setFrameNum(currentFrameNumber);
782 audio->systemMuteOff();
786 if (state == S_PLAY) threadFeedPlay();
794 void Player::threadFeedLive()
799 UINT preBufferTotal = 0;
811 askFor = startupBlockSize; // find audio streams sized block
813 askFor = blockSize; // normal
815 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
817 if (!vdr->isConnected())
823 if (!threadBuffer) break;
827 int a_stream = demuxer->scan(threadBuffer, thisRead);
828 demuxer->setAudioStream(a_stream);
829 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
835 preBufferTotal += thisRead;
836 if (preBufferTotal >= preBufferSize)
838 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
840 preBuffering = false;
849 // unLock(); // thread will be locked by play until here
850 // FIXME - see if this can segfault because it is starting threads out of the master mutex
856 while(writeLength < thisRead)
858 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
859 writeLength += thisWrite;
863 // demuxer is full and can't take anymore
865 threadWaitForSignal();
877 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
879 if (videoStartup) // oh woe. there never was a stream, I was conned!
881 videoStartup = false;
883 MILLISLEEP(500); // I think this will solve a race
888 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
889 m->message = Message::STREAM_END;
890 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
891 commandMessageQueue->postMessage(m);
892 logger->log("Player", Log::DEBUG, "Message posted...");
895 void Player::threadFeedPlay()
898 UINT thisRead, writeLength, thisWrite, askFor;
899 time_t lastRescan = time(NULL);
901 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
902 if (!vdr->isConnected()) { doConnectionLost(); return; }
903 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
914 // If we havn't rescanned for a while..
915 if ((lastRescan + 60) < time(NULL))
917 lengthBytes = vdr->rescanRecording(&lengthFrames);
918 if (!vdr->isConnected()) { doConnectionLost(); return; }
919 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
920 lastRescan = time(NULL);
923 if (feedPosition >= lengthBytes) break; // finished playback
927 if (startupBlockSize > lengthBytes)
928 askFor = lengthBytes; // is a very small recording!
930 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
934 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
935 askFor = lengthBytes - feedPosition;
940 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
941 feedPosition += thisRead;
943 if (!vdr->isConnected())
949 if (!threadBuffer) break;
953 int a_stream = demuxer->scan(threadBuffer, thisRead);
954 demuxer->setAudioStream(a_stream);
955 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
961 while(writeLength < thisRead)
963 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
964 writeLength += thisWrite;
968 // demuxer is full and can't take anymore
970 threadWaitForSignal();
983 logger->log("Player", Log::DEBUG, "Recording playback ends");
985 if (videoStartup) // oh woe. there never was a stream, I was conned!
987 videoStartup = false;
989 MILLISLEEP(500); // I think this will solve a race
994 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
995 m->message = Message::STOP_PLAYBACK; // recording
996 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
997 commandMessageQueue->postMessage(m);
998 logger->log("Player", Log::DEBUG, "Message posted...");
1001 void Player::threadFeedScan()
1003 // This method is actually really simple - get frame from vdr,
1004 // spit it at the video chip, wait for a time. Most of the code here
1005 // is to get the wait right so that the scan occurs at the correct rate.
1007 ULONG direction = 0;
1008 ULONG baseFrameNumber = 0;
1009 ULONG iframeNumber = 0;
1010 ULONG iframeLength = 0;
1012 UINT amountReceived;
1016 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1017 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1018 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1020 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1023 int frameTimeOffset = 0; // Time in msec between frames
1024 int disp_msec = 0; // Time taken to display data
1025 int total_msec = 0; // Time taken to fetch data and display it
1028 if (state == S_FFWD) direction = 1; // and 0 for backward
1032 // Fetch I-frames until we get one that can be displayed in good time
1033 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1034 baseFrameNumber = currentFrameNumber;
1037 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1040 baseFrameNumber = iframeNumber;
1041 frameTimeOffset = abs(iframeNumber - currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1043 gettimeofday(&clock0, NULL);
1045 clock0 = timeGetTime();
1049 while (clock2.tv_sec != 0 &&
1050 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1051 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1053 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1055 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1057 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1059 gettimeofday(&clock1, NULL);
1060 if (clock2.tv_sec != 0)
1061 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1062 + (clock2.tv_usec - clock1.tv_usec) / 1000
1063 + frameTimeOffset - disp_msec;
1065 clock1 = timeGetTime():
1067 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1069 if (sleepTime < 0) sleepTime = 0;
1071 MILLISLEEP(sleepTime);
1072 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1074 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1075 video->displayIFrame(threadBuffer, videoLength);
1076 currentFrameNumber = iframeNumber;
1078 threadBuffer = NULL;
1081 gettimeofday(&clock2, NULL);
1082 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1083 + (clock2.tv_usec - clock0.tv_usec) / 1000
1085 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1086 + (clock2.tv_usec - clock1.tv_usec) / 1000
1089 clock2 = timeGetTime();
1090 total_msec = clock2 - clock0 - sleepTime;
1091 disp_msec = clock2 - clock1 - sleepTime;
1093 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1097 void Player::threadPostStopCleanup()
1102 threadBuffer = NULL;
1106 // ----------------------------------- Dev
1109 void Player::test1()
1111 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1113 // video->setAspectRatio(Video::ASPECT4X3);
1116 void Player::test2()
1118 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1119 video->setAspectRatio(Video::ASPECT16X9);