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 #define USER_RESPONSE_TIME 500 // Milliseconds
25 // ----------------------------------- Called from outside, one offs or info funcs
27 Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, bool tIsRecording)
28 : vfeed(this), afeed(this)
30 messageQueue = tmessageQueue;
31 messageReceiver = tmessageReceiver;
32 audio = Audio::getInstance();
33 video = Video::getInstance();
34 logger = Log::getInstance();
35 vdr = VDR::getInstance();
39 currentFrameNumber = 0;
45 isRecording = tIsRecording;
50 startupBlockSize = 250000;
51 preBufferSize = 500000;
57 if (initted) shutdown();
62 if (initted) return 0;
64 pthread_mutex_init(&mutex, NULL);
66 mutex=CreateMutex(NULL,FALSE,NULL);
70 demuxer = new DemuxerVDR();
72 demuxer = new DemuxerTS();
73 if (!demuxer) return 0;
75 if (!demuxer->init(this, audio, video, 2097152, 524288))
77 logger->log("Player", Log::ERR, "Demuxer failed to init");
93 int Player::shutdown()
95 if (!initted) return 0;
109 void Player::setStartFrame(ULONG startFrame)
111 currentFrameNumber = startFrame;
114 void Player::setLengthBytes(ULLONG length)
116 lengthBytes = length;
117 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
120 void Player::setLengthFrames(ULONG length)
122 lengthFrames = length;
123 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
126 ULONG Player::getLengthFrames()
131 ULONG Player::getCurrentFrameNum()
133 if (startup) return 0;
138 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
142 return currentFrameNumber;
144 return 0; // shouldn't happen
148 bool* Player::getDemuxerMpegAudioChannels()
150 return demuxer->getmpAudioChannels();
153 bool* Player::getDemuxerAc3AudioChannels()
155 return demuxer->getac3AudioChannels();
158 int Player::getCurrentAudioChannel()
160 return demuxer->getselAudioChannel();
163 void Player::setAudioChannel(int newChannel)
165 demuxer->setAudioChannel(newChannel);
168 // ----------------------------------- Externally called events
170 void Player::play(ULONG Vpid, ULONG Apid)
173 DemuxerTS* dts = (DemuxerTS*)demuxer;
181 if (!initted) return;
182 if (state == S_PLAY) return;
185 bool doUnlock = false;
186 if (state == S_PAUSE_P) doUnlock = true;
188 if (doUnlock) unLock();
193 if (!initted) return;
194 if (state == S_STOP) return;
196 logger->log("Player", Log::DEBUG, "Stop called lock");
203 if (!initted) return;
206 if ((state == S_FFWD) || (state == S_FBWD))
208 switchState(S_PAUSE_I);
210 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
216 switchState(S_PAUSE_P);
222 void Player::fastForward()
224 if (!initted) return;
232 case 4: ifactor = 8; break;
233 case 8: ifactor = 16; break;
234 case 16: ifactor = 32; break;
235 case 32: ifactor = 4; break;
246 void Player::fastBackward()
248 if (!initted) return;
256 case 4: ifactor = 8; break;
257 case 8: ifactor = 16; break;
258 case 16: ifactor = 32; break;
259 case 32: ifactor = 4; break;
270 void Player::jumpToPercent(double percent)
273 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
274 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
275 switchState(S_JUMP, newFrame);
276 // unLock(); - let thread unlock this
279 void Player::jumpToMark(int mark)
282 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
283 switchState(S_JUMP, mark);
284 // unLock(); - let thread unlock this
287 void Player::jumpToFrameP(int newFrame)
290 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
291 switchState(S_JUMP_PI, newFrame);
295 void Player::skipForward(int seconds)
298 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
299 ULONG newFrame = getCurrentFrameNum();
300 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
301 newFrame += seconds * video->getFPS();
302 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
303 else switchState(S_JUMP, newFrame);
304 // unLock(); - let thread unlock this
307 void Player::skipBackward(int seconds)
310 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
311 long newFrame = getCurrentFrameNum();
312 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
313 newFrame -= seconds * video->getFPS();
314 if (newFrame < 0) newFrame = 0;
315 switchState(S_JUMP, newFrame);
316 // unLock(); - let thread unlock this
319 // ----------------------------------- Implementations called events
321 void Player::switchState(UCHAR toState, ULONG jumpFrame)
323 if (!initted) return;
325 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
327 switch(state) // current state selector
329 case S_PLAY: // from S_PLAY -----------------------------------
333 case S_PLAY: // to S_PLAY
337 case S_PAUSE_P: // to S_PAUSE_P
344 case S_PAUSE_I: // to S_PAUSE_I
349 case S_FFWD: // to S_FFWD
351 currentFrameNumber = getCurrentFrameNum();
352 audio->systemMuteOn();
361 case S_FBWD: // to S_FBWD
363 currentFrameNumber = getCurrentFrameNum();
364 audio->systemMuteOn();
373 case S_STOP: // to S_STOP
387 case S_JUMP: // to S_JUMP
389 restartAtFrame(jumpFrame);
392 case S_JUMP_PI: // to S_JUMP_PI
394 audio->systemMuteOn();
402 restartAtFramePI(jumpFrame);
407 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
411 case S_PLAY: // to S_PLAY
418 case S_PAUSE_P: // to S_PAUSE_P
422 case S_PAUSE_I: // to S_PAUSE_I
426 case S_FFWD: // to S_FFWD
428 currentFrameNumber = getCurrentFrameNum();
429 audio->systemMuteOn();
439 case S_FBWD: // to S_FBWD
441 currentFrameNumber = getCurrentFrameNum();
442 audio->systemMuteOn();
452 case S_STOP: // to S_STOP
463 audio->systemMuteOff();
467 case S_JUMP: // to S_JUMP
470 audio->systemMuteOn();
472 restartAtFrame(jumpFrame);
475 case S_JUMP_PI: // to S_JUMP_PI
477 audio->systemMuteOn();
486 restartAtFramePI(jumpFrame);
491 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
495 case S_PLAY: // to S_PLAY
498 restartAtFrame(currentFrameNumber);
501 case S_PAUSE_P: // to S_PAUSE_P
505 case S_PAUSE_I: // to S_PAUSE_I
509 case S_FFWD: // to S_FFWD
515 case S_FBWD: // to S_FBWD
521 case S_STOP: // to S_STOP
528 audio->systemMuteOff();
532 case S_JUMP: // to S_JUMP
535 restartAtFrame(jumpFrame);
538 case S_JUMP_PI: // to S_JUMP_PI
540 restartAtFramePI(jumpFrame);
545 case S_FFWD: // from S_FFWD -----------------------------------
549 case S_PLAY: // to S_PLAY
552 ULONG stepback = USER_RESPONSE_TIME * video->getFPS() * ifactor / 1000;
553 if (stepback < currentFrameNumber)
554 currentFrameNumber -= stepback;
556 currentFrameNumber = 0;
557 restartAtFrame(currentFrameNumber);
560 case S_PAUSE_P: // to S_PAUSE_P
565 case S_PAUSE_I: // to S_PAUSE_I
571 case S_FFWD: // to S_FFWD
575 case S_FBWD: // to S_FBWD
582 case S_STOP: // to S_STOP
593 case S_JUMP: // to S_JUMP
596 restartAtFrame(jumpFrame);
599 case S_JUMP_PI: // to S_JUMP_PI
603 restartAtFramePI(jumpFrame);
608 case S_FBWD: // from S_FBWD -----------------------------------
612 case S_PLAY: // to S_PLAY
615 restartAtFrame(currentFrameNumber);
618 case S_PAUSE_P: // to S_PAUSE_P
623 case S_PAUSE_I: // to S_PAUSE_I
629 case S_FFWD: // to S_FFWD
636 case S_FBWD: // to S_FBWD
640 case S_STOP: // to S_STOP
651 case S_JUMP: // to S_JUMP
654 restartAtFrame(jumpFrame);
657 case S_JUMP_PI: // to S_JUMP_PI
661 restartAtFramePI(jumpFrame);
666 case S_STOP: // from S_STOP -----------------------------------
670 case S_PLAY: // to S_PLAY
675 audio->setStreamType(Audio::MPEG2_PES);
676 audio->systemMuteOff();
681 // FIXME use restartAtFrame here?
682 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
683 demuxer->setFrameNum(currentFrameNumber);
693 logger->log("Player", Log::DEBUG, "Immediate play");
701 else // do prebuffering
703 logger->log("Player", Log::DEBUG, "Prebuffering...");
708 case S_PAUSE_P: // to S_PAUSE_P
712 case S_PAUSE_I: // to S_PAUSE_I
716 case S_FFWD: // to S_FFWD
720 case S_FBWD: // to S_FBWD
724 case S_STOP: // to S_STOP
728 case S_JUMP: // to S_JUMP
732 case S_JUMP_PI: // to S_JUMP_PI
738 // case S_JUMP cannot be a start state because it auto flips to play
739 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
743 // ----------------------------------- Internal functions
748 pthread_mutex_lock(&mutex);
749 logger->log("Player", Log::DEBUG, "LOCKED");
752 WaitForSingleObject(mutex, INFINITE);
756 void Player::unLock()
759 logger->log("Player", Log::DEBUG, "UNLOCKING");
760 pthread_mutex_unlock(&mutex);
766 void Player::restartAtFrame(ULONG newFrame)
774 audio->setStreamType(Audio::MPEG2_PES);
777 currentFrameNumber = newFrame;
778 demuxer->setFrameNum(newFrame);
786 audio->systemMuteOff();
791 void Player::restartAtFramePI(ULONG newFrame)
794 ULONG nextiframeNumber;
802 // newFrame could be anywhere, go forwards to next I-Frame
803 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
805 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
806 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
808 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
809 videoLength = demuxer->stripAudio(buffer, amountReceived);
810 video->displayIFrame(buffer, videoLength);
811 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
814 currentFrameNumber = iframeNumber;
817 void Player::doConnectionLost()
819 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
820 Message* m = new Message();
821 m->to = messageReceiver;
823 m->message = Message::PLAYER_EVENT;
824 m->parameter = Player::CONNECTION_LOST;
825 messageQueue->postMessage(m);
828 // ----------------------------------- Callback
830 void Player::call(void* caller)
832 if (caller == demuxer)
834 logger->log("Player", Log::DEBUG, "Callback from demuxer");
836 if (video->getTVsize() == Video::ASPECT4X3)
838 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
842 int dxCurrentAspect = demuxer->getAspectRatio();
843 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
845 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
846 video->setAspectRatio(Video::ASPECT4X3);
848 Message* m = new Message();
850 m->to = messageReceiver;
851 m->message = Message::PLAYER_EVENT;
852 m->parameter = Player::ASPECT43;
853 messageQueue->postMessageFromOuterSpace(m);
855 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
857 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
858 video->setAspectRatio(Video::ASPECT16X9);
860 Message* m = new Message();
862 m->to = messageReceiver;
863 m->message = Message::PLAYER_EVENT;
864 m->parameter = Player::ASPECT169;
865 messageQueue->postMessageFromOuterSpace(m);
869 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
877 videoStartup = false;
885 threadSignalNoLock();
889 // ----------------------------------- Feed thread
891 void Player::threadMethod()
893 // this method used to be simple, the only thing it does
894 // is farm out to threadFeed Live/Play/Scan
895 // All the guff is to support scan hitting one end
899 if ((state == S_FFWD) || (state == S_FBWD))
902 // if this returns then scan hit one end
903 if (state == S_FFWD) // scan hit the end. stop
906 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
907 m->to = messageReceiver;
909 m->message = Message::PLAYER_EVENT;
910 m->parameter = STOP_PLAYBACK;
911 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
912 messageQueue->postMessage(m);
913 logger->log("Player", Log::DEBUG, "Message posted...");
916 // if execution gets to here, threadFeedScan hit the start, go to play mode
919 audio->setStreamType(Audio::MPEG2_PES);
922 demuxer->setFrameNum(currentFrameNumber);
928 audio->systemMuteOff();
932 if (state == S_PLAY) threadFeedPlay();
940 void Player::threadFeedLive()
945 UINT preBufferTotal = 0;
957 askFor = startupBlockSize; // find audio streams sized block
959 askFor = blockSize; // normal
961 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
963 if (!vdr->isConnected())
969 if (!threadBuffer) break;
973 int a_stream = demuxer->scan(threadBuffer, thisRead);
974 demuxer->setAudioStream(a_stream);
975 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
981 preBufferTotal += thisRead;
982 if (preBufferTotal >= preBufferSize)
984 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
986 preBuffering = false;
990 // FIXME check the win32 code on the mvp
1007 // unLock(); // thread will be locked by play until here
1008 // FIXME - see if this can segfault because it is starting threads out of the master mutex
1014 while(writeLength < thisRead)
1016 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1017 writeLength += thisWrite;
1019 // logger->log("Player", Log::DEBUG, "Player put %u to demuxer", thisWrite);
1023 // logger->log("Player", Log::DEBUG, "Demuxer full");
1024 // demuxer is full and can't take anymore
1026 threadWaitForSignal();
1034 threadBuffer = NULL;
1038 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
1040 if (videoStartup) // oh woe. there never was a stream, I was conned!
1042 videoStartup = false;
1044 MILLISLEEP(500); // I think this will solve a race
1049 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1050 m->to = messageReceiver;
1052 m->message = Message::PLAYER_EVENT;
1053 m->parameter = Player::STREAM_END;
1054 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1055 messageQueue->postMessage(m);
1056 logger->log("Player", Log::DEBUG, "Message posted...");
1059 void Player::threadFeedPlay()
1061 ULLONG feedPosition;
1062 UINT thisRead, writeLength, thisWrite, askFor;
1063 time_t lastRescan = time(NULL);
1065 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1066 if (!vdr->isConnected()) { doConnectionLost(); return; }
1067 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1078 // If we havn't rescanned for a while..
1079 if ((lastRescan + 60) < time(NULL))
1081 lengthBytes = vdr->rescanRecording(&lengthFrames);
1082 if (!vdr->isConnected()) { doConnectionLost(); return; }
1083 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1084 lastRescan = time(NULL);
1087 if (feedPosition >= lengthBytes) break; // finished playback
1091 if (startupBlockSize > lengthBytes)
1092 askFor = lengthBytes; // is a very small recording!
1094 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1098 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1099 askFor = lengthBytes - feedPosition;
1104 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1105 feedPosition += thisRead;
1107 if (!vdr->isConnected())
1113 if (!threadBuffer) break;
1117 int a_stream = demuxer->scan(threadBuffer, thisRead);
1118 demuxer->setAudioStream(a_stream);
1119 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1125 while(writeLength < thisRead)
1127 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1128 writeLength += thisWrite;
1132 // demuxer is full and can't take anymore
1134 threadWaitForSignal();
1142 threadBuffer = NULL;
1147 logger->log("Player", Log::DEBUG, "Recording playback ends");
1149 if (videoStartup) // oh woe. there never was a stream, I was conned!
1151 videoStartup = false;
1153 MILLISLEEP(500); // I think this will solve a race
1159 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1160 m->to = messageReceiver;
1162 m->message = Message::PLAYER_EVENT;
1163 m->parameter = Player::STOP_PLAYBACK;
1164 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1165 messageQueue->postMessage(m);
1168 void Player::threadFeedScan()
1170 // This method is actually really simple - get frame from vdr,
1171 // spit it at the video chip, wait for a time. Most of the code here
1172 // is to get the wait right so that the scan occurs at the correct rate.
1174 ULONG direction = 0;
1175 ULONG baseFrameNumber = 0;
1176 ULONG iframeNumber = 0;
1177 ULONG iframeLength = 0;
1179 UINT amountReceived;
1183 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1184 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1185 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1187 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1190 int frameTimeOffset = 0; // Time in msec between frames
1191 int disp_msec = 0; // Time taken to display data
1192 int total_msec = 0; // Time taken to fetch data and display it
1195 if (state == S_FFWD) direction = 1; // and 0 for backward
1199 // Fetch I-frames until we get one that can be displayed in good time
1200 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1201 baseFrameNumber = currentFrameNumber;
1205 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1208 if (iframeNumber >= lengthFrames) return;
1209 // scan has got to the end of what we knew to be there before we started scanning
1211 baseFrameNumber = iframeNumber;
1212 frameTimeOffset = abs((int)iframeNumber - (int)currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1214 gettimeofday(&clock0, NULL);
1216 clock0 = timeGetTime();
1220 while (clock2.tv_sec != 0 &&
1221 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1222 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1224 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1226 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1228 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1230 gettimeofday(&clock1, NULL);
1231 if (clock2.tv_sec != 0)
1232 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1233 + (clock2.tv_usec - clock1.tv_usec) / 1000
1234 + frameTimeOffset - disp_msec;
1236 clock1 = timeGetTime();
1238 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1240 if (sleepTime < 0) sleepTime = 0;
1242 MILLISLEEP(sleepTime);
1243 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1245 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1246 video->displayIFrame(threadBuffer, videoLength);
1247 currentFrameNumber = iframeNumber;
1249 threadBuffer = NULL;
1252 gettimeofday(&clock2, NULL);
1253 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1254 + (clock2.tv_usec - clock0.tv_usec) / 1000
1256 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1257 + (clock2.tv_usec - clock1.tv_usec) / 1000
1260 clock2 = timeGetTime();
1261 total_msec = clock2 - clock0 - sleepTime;
1262 disp_msec = clock2 - clock1 - sleepTime;
1264 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1268 void Player::threadPostStopCleanup()
1273 threadBuffer = NULL;
1277 // ----------------------------------- Dev
1280 void Player::test1()
1282 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1284 // video->setAspectRatio(Video::ASPECT4X3);
1285 jumpToFrameP(37507);
1288 void Player::test2()
1290 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1291 video->setAspectRatio(Video::ASPECT16X9);