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., 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, bool tIsRecording)
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;
55 isRecording = tIsRecording;
60 startupBlockSize = 250000;
61 preBufferSize = 500000;
67 if (initted) shutdown();
72 if (initted) return 0;
74 pthread_mutex_init(&mutex, NULL);
76 mutex=CreateMutex(NULL,FALSE,NULL);
80 demuxer = new DemuxerVDR();
82 demuxer = new DemuxerTS();
83 if (!demuxer) return 0;
85 if (!demuxer->init(this, audio, video, 2097152, 524288))
87 logger->log("Player", Log::ERR, "Demuxer failed to init");
103 int Player::shutdown()
105 if (!initted) return 0;
119 void Player::setStartFrame(ULONG startFrame)
121 currentFrameNumber = startFrame;
124 void Player::setLengthBytes(ULLONG length)
126 lengthBytes = length;
127 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
130 void Player::setLengthFrames(ULONG length)
132 lengthFrames = length;
133 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
136 ULONG Player::getLengthFrames()
141 ULONG Player::getCurrentFrameNum()
143 if (startup) return 0;
148 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
152 return currentFrameNumber;
154 return 0; // shouldn't happen
158 bool* Player::getDemuxerMpegAudioChannels()
160 return demuxer->getmpAudioChannels();
163 bool* Player::getDemuxerAc3AudioChannels()
165 return demuxer->getac3AudioChannels();
168 int Player::getCurrentAudioChannel()
171 return demuxer->getselAudioChannel();
173 return ((DemuxerTS*)demuxer)->getAID();
176 void Player::setAudioChannel(int newChannel, int type)
179 demuxer->setAudioChannel(newChannel);
181 ((DemuxerTS*)demuxer)->setAID(newChannel,type);
184 // ----------------------------------- Externally called events
186 void Player::play(ULONG Vpid, ULONG Apid)
189 DemuxerTS* dts = (DemuxerTS*)demuxer;
197 if (!initted) return;
198 if (state == S_PLAY) return;
201 bool doUnlock = false;
202 if (state == S_PAUSE_P) doUnlock = true;
204 if (doUnlock) unLock();
209 if (!initted) return;
210 if (state == S_STOP) return;
212 logger->log("Player", Log::DEBUG, "Stop called lock");
219 if (!initted) return;
222 if ((state == S_FFWD) || (state == S_FBWD))
224 switchState(S_PAUSE_I);
226 else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))
232 switchState(S_PAUSE_P);
238 void Player::fastForward()
240 if (!initted) return;
248 case 4: ifactor = 8; break;
249 case 8: ifactor = 16; break;
250 case 16: ifactor = 32; break;
251 case 32: ifactor = 4; break;
262 void Player::fastBackward()
264 if (!initted) return;
272 case 4: ifactor = 8; break;
273 case 8: ifactor = 16; break;
274 case 16: ifactor = 32; break;
275 case 32: ifactor = 4; break;
286 void Player::jumpToPercent(double percent)
289 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
290 ULONG newFrame = (ULONG)(percent * lengthFrames / 100);
291 switchState(S_JUMP, newFrame);
292 // unLock(); - let thread unlock this
295 void Player::jumpToMark(int mark)
298 logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);
299 switchState(S_JUMP, mark);
300 // unLock(); - let thread unlock this
303 void Player::jumpToFrameP(int newFrame)
306 logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);
307 switchState(S_JUMP_PI, newFrame);
311 void Player::skipForward(int seconds)
314 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
315 ULONG newFrame = getCurrentFrameNum();
316 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
317 newFrame += seconds * video->getFPS();
318 if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }
319 else switchState(S_JUMP, newFrame);
320 // unLock(); - let thread unlock this
323 void Player::skipBackward(int seconds)
326 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
327 long newFrame = getCurrentFrameNum();
328 if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid
329 newFrame -= seconds * video->getFPS();
330 if (newFrame < 0) newFrame = 0;
331 switchState(S_JUMP, newFrame);
332 // unLock(); - let thread unlock this
335 // ----------------------------------- Implementations called events
337 void Player::switchState(UCHAR toState, ULONG jumpFrame)
339 if (!initted) return;
341 logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);
343 switch(state) // current state selector
345 case S_PLAY: // from S_PLAY -----------------------------------
349 case S_PLAY: // to S_PLAY
353 case S_PAUSE_P: // to S_PAUSE_P
360 case S_PAUSE_I: // to S_PAUSE_I
365 case S_FFWD: // to S_FFWD
367 currentFrameNumber = getCurrentFrameNum();
368 audio->systemMuteOn();
377 case S_FBWD: // to S_FBWD
379 currentFrameNumber = getCurrentFrameNum();
380 audio->systemMuteOn();
389 case S_STOP: // to S_STOP
403 case S_JUMP: // to S_JUMP
405 restartAtFrame(jumpFrame);
408 case S_JUMP_PI: // to S_JUMP_PI
410 audio->systemMuteOn();
418 restartAtFramePI(jumpFrame);
423 case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
427 case S_PLAY: // to S_PLAY
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
444 currentFrameNumber = getCurrentFrameNum();
445 audio->systemMuteOn();
455 case S_FBWD: // to S_FBWD
457 currentFrameNumber = getCurrentFrameNum();
458 audio->systemMuteOn();
468 case S_STOP: // to S_STOP
479 audio->systemMuteOff();
483 case S_JUMP: // to S_JUMP
486 audio->systemMuteOn();
488 restartAtFrame(jumpFrame);
491 case S_JUMP_PI: // to S_JUMP_PI
493 audio->systemMuteOn();
502 restartAtFramePI(jumpFrame);
507 case S_PAUSE_I: // from S_PAUSE_I -----------------------------------
511 case S_PLAY: // to S_PLAY
514 restartAtFrame(currentFrameNumber);
517 case S_PAUSE_P: // to S_PAUSE_P
521 case S_PAUSE_I: // to S_PAUSE_I
525 case S_FFWD: // to S_FFWD
531 case S_FBWD: // to S_FBWD
537 case S_STOP: // to S_STOP
544 audio->systemMuteOff();
548 case S_JUMP: // to S_JUMP
551 restartAtFrame(jumpFrame);
554 case S_JUMP_PI: // to S_JUMP_PI
556 restartAtFramePI(jumpFrame);
561 case S_FFWD: // from S_FFWD -----------------------------------
565 case S_PLAY: // to S_PLAY
568 ULONG stepback = USER_RESPONSE_TIME * video->getFPS() * ifactor / 1000;
569 if (stepback < currentFrameNumber)
570 currentFrameNumber -= stepback;
572 currentFrameNumber = 0;
573 restartAtFrame(currentFrameNumber);
576 case S_PAUSE_P: // to S_PAUSE_P
581 case S_PAUSE_I: // to S_PAUSE_I
587 case S_FFWD: // to S_FFWD
591 case S_FBWD: // to S_FBWD
598 case S_STOP: // to S_STOP
609 case S_JUMP: // to S_JUMP
612 restartAtFrame(jumpFrame);
615 case S_JUMP_PI: // to S_JUMP_PI
619 restartAtFramePI(jumpFrame);
624 case S_FBWD: // from S_FBWD -----------------------------------
628 case S_PLAY: // to S_PLAY
631 restartAtFrame(currentFrameNumber);
634 case S_PAUSE_P: // to S_PAUSE_P
639 case S_PAUSE_I: // to S_PAUSE_I
645 case S_FFWD: // to S_FFWD
652 case S_FBWD: // to S_FBWD
656 case S_STOP: // to S_STOP
667 case S_JUMP: // to S_JUMP
670 restartAtFrame(jumpFrame);
673 case S_JUMP_PI: // to S_JUMP_PI
677 restartAtFramePI(jumpFrame);
682 case S_STOP: // from S_STOP -----------------------------------
686 case S_PLAY: // to S_PLAY
691 audio->setStreamType(Audio::MPEG2_PES);
692 audio->systemMuteOff();
697 // FIXME use restartAtFrame here?
698 if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;
699 demuxer->setFrameNum(currentFrameNumber);
709 logger->log("Player", Log::DEBUG, "Immediate play");
717 else // do prebuffering
719 logger->log("Player", Log::DEBUG, "Prebuffering...");
724 case S_PAUSE_P: // to S_PAUSE_P
728 case S_PAUSE_I: // to S_PAUSE_I
732 case S_FFWD: // to S_FFWD
736 case S_FBWD: // to S_FBWD
740 case S_STOP: // to S_STOP
744 case S_JUMP: // to S_JUMP
748 case S_JUMP_PI: // to S_JUMP_PI
754 // case S_JUMP cannot be a start state because it auto flips to play
755 // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I
759 // ----------------------------------- Internal functions
764 pthread_mutex_lock(&mutex);
765 logger->log("Player", Log::DEBUG, "LOCKED");
768 WaitForSingleObject(mutex, INFINITE);
772 void Player::unLock()
775 logger->log("Player", Log::DEBUG, "UNLOCKING");
776 pthread_mutex_unlock(&mutex);
782 void Player::restartAtFrame(ULONG newFrame)
790 audio->setStreamType(Audio::MPEG2_PES);
793 currentFrameNumber = newFrame;
794 demuxer->setFrameNum(newFrame);
802 audio->systemMuteOff();
807 void Player::restartAtFramePI(ULONG newFrame)
810 ULONG nextiframeNumber;
818 // newFrame could be anywhere, go forwards to next I-Frame
819 if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;
821 // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame
822 vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);
824 buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
825 videoLength = demuxer->stripAudio(buffer, amountReceived);
826 video->displayIFrame(buffer, videoLength);
827 video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)
830 currentFrameNumber = iframeNumber;
833 void Player::doConnectionLost()
835 logger->log("Player", Log::DEBUG, "Connection lost, sending message");
836 Message* m = new Message();
837 m->to = messageReceiver;
839 m->message = Message::PLAYER_EVENT;
840 m->parameter = Player::CONNECTION_LOST;
841 messageQueue->postMessage(m);
844 // ----------------------------------- Callback
846 void Player::call(void* caller)
848 if (caller == demuxer)
850 logger->log("Player", Log::DEBUG, "Callback from demuxer");
852 if (video->getTVsize() == Video::ASPECT4X3)
854 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
858 int dxCurrentAspect = demuxer->getAspectRatio();
859 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
861 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
862 video->setAspectRatio(Video::ASPECT4X3);
864 Message* m = new Message();
866 m->to = messageReceiver;
867 m->message = Message::PLAYER_EVENT;
868 m->parameter = Player::ASPECT43;
869 messageQueue->postMessageFromOuterSpace(m);
871 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
873 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
874 video->setAspectRatio(Video::ASPECT16X9);
876 Message* m = new Message();
878 m->to = messageReceiver;
879 m->message = Message::PLAYER_EVENT;
880 m->parameter = Player::ASPECT169;
881 messageQueue->postMessageFromOuterSpace(m);
885 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
893 videoStartup = false;
901 threadSignalNoLock();
905 // ----------------------------------- Feed thread
907 void Player::threadMethod()
909 // this method used to be simple, the only thing it does
910 // is farm out to threadFeed Live/Play/Scan
911 // All the guff is to support scan hitting one end
915 if ((state == S_FFWD) || (state == S_FBWD))
918 // if this returns then scan hit one end
919 if (state == S_FFWD) // scan hit the end. stop
922 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
923 m->to = messageReceiver;
925 m->message = Message::PLAYER_EVENT;
926 m->parameter = STOP_PLAYBACK;
927 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
928 messageQueue->postMessage(m);
929 logger->log("Player", Log::DEBUG, "Message posted...");
932 // if execution gets to here, threadFeedScan hit the start, go to play mode
935 audio->setStreamType(Audio::MPEG2_PES);
938 demuxer->setFrameNum(currentFrameNumber);
944 audio->systemMuteOff();
948 if (state == S_PLAY) threadFeedPlay();
956 void Player::threadFeedLive()
961 UINT preBufferTotal = 0;
973 askFor = startupBlockSize; // find audio streams sized block
975 askFor = blockSize; // normal
977 threadBuffer = vdr->getBlock(0, askFor, &thisRead);
979 if (!vdr->isConnected())
985 if (!threadBuffer) break;
989 int a_stream = demuxer->scan(threadBuffer, thisRead);
990 demuxer->setAudioStream(a_stream);
991 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
997 preBufferTotal += thisRead;
998 if (preBufferTotal >= preBufferSize)
1000 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
1002 preBuffering = false;
1006 // FIXME check the win32 code on the mvp
1023 // unLock(); // thread will be locked by play until here
1024 // FIXME - see if this can segfault because it is starting threads out of the master mutex
1030 while(writeLength < thisRead)
1032 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1033 writeLength += thisWrite;
1035 // logger->log("Player", Log::DEBUG, "Player put %u to demuxer", thisWrite);
1039 // logger->log("Player", Log::DEBUG, "Demuxer full");
1040 // demuxer is full and can't take anymore
1042 threadWaitForSignal();
1050 threadBuffer = NULL;
1054 logger->log("Player", Log::DEBUG, "Live play failed to start or interrupted");
1056 if (videoStartup) // oh woe. there never was a stream, I was conned!
1058 videoStartup = false;
1060 MILLISLEEP(500); // I think this will solve a race
1065 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1066 m->to = messageReceiver;
1068 m->message = Message::PLAYER_EVENT;
1069 m->parameter = Player::STREAM_END;
1070 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1071 messageQueue->postMessage(m);
1072 logger->log("Player", Log::DEBUG, "Message posted...");
1075 void Player::threadFeedPlay()
1077 ULLONG feedPosition;
1078 UINT thisRead, writeLength, thisWrite, askFor;
1079 time_t lastRescan = time(NULL);
1081 feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);
1082 if (!vdr->isConnected()) { doConnectionLost(); return; }
1083 logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);
1094 // If we havn't rescanned for a while..
1095 if ((lastRescan + 60) < time(NULL))
1097 lengthBytes = vdr->rescanRecording(&lengthFrames);
1098 if (!vdr->isConnected()) { doConnectionLost(); return; }
1099 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
1100 lastRescan = time(NULL);
1103 if (feedPosition >= lengthBytes) break; // finished playback
1107 if (startupBlockSize > lengthBytes)
1108 askFor = lengthBytes; // is a very small recording!
1110 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
1114 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
1115 askFor = lengthBytes - feedPosition;
1120 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
1121 feedPosition += thisRead;
1123 if (!vdr->isConnected())
1129 if (!threadBuffer) break;
1133 int a_stream = demuxer->scan(threadBuffer, thisRead);
1134 demuxer->setAudioStream(a_stream);
1135 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
1141 while(writeLength < thisRead)
1143 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
1144 writeLength += thisWrite;
1148 // demuxer is full and can't take anymore
1150 threadWaitForSignal();
1158 threadBuffer = NULL;
1163 logger->log("Player", Log::DEBUG, "Recording playback ends");
1165 if (videoStartup) // oh woe. there never was a stream, I was conned!
1167 videoStartup = false;
1169 MILLISLEEP(500); // I think this will solve a race
1175 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
1176 m->to = messageReceiver;
1178 m->message = Message::PLAYER_EVENT;
1179 m->parameter = Player::STOP_PLAYBACK;
1180 logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);
1181 messageQueue->postMessage(m);
1184 void Player::threadFeedScan()
1186 // This method is actually really simple - get frame from vdr,
1187 // spit it at the video chip, wait for a time. Most of the code here
1188 // is to get the wait right so that the scan occurs at the correct rate.
1190 ULONG direction = 0;
1191 ULONG baseFrameNumber = 0;
1192 ULONG iframeNumber = 0;
1193 ULONG iframeLength = 0;
1195 UINT amountReceived;
1199 struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info
1200 struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data
1201 struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame
1203 DWORD clock0 = 0, clock1 = 0, clock2 = 0;
1206 int frameTimeOffset = 0; // Time in msec between frames
1207 int disp_msec = 0; // Time taken to display data
1208 int total_msec = 0; // Time taken to fetch data and display it
1211 if (state == S_FFWD) direction = 1; // and 0 for backward
1215 // Fetch I-frames until we get one that can be displayed in good time
1216 // Repeat while clock0 + total_msec > clock2 + frameTimeOffset
1217 baseFrameNumber = currentFrameNumber;
1221 if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))
1224 if (iframeNumber >= lengthFrames) return;
1225 // scan has got to the end of what we knew to be there before we started scanning
1227 baseFrameNumber = iframeNumber;
1228 frameTimeOffset = abs((int)iframeNumber - (int)currentFrameNumber) * 1000 / (video->getFPS() * ifactor);
1230 gettimeofday(&clock0, NULL);
1232 clock0 = timeGetTime();
1236 while (clock2.tv_sec != 0 &&
1237 (clock0.tv_sec - clock2.tv_sec) * 1000 +
1238 (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);
1240 while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);
1242 // logger->log("Player", Log::DEBUG, "XXX Got frame");
1244 threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);
1246 gettimeofday(&clock1, NULL);
1247 if (clock2.tv_sec != 0)
1248 sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000
1249 + (clock2.tv_usec - clock1.tv_usec) / 1000
1250 + frameTimeOffset - disp_msec;
1252 clock1 = timeGetTime();
1254 sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;
1256 if (sleepTime < 0) sleepTime = 0;
1258 MILLISLEEP(sleepTime);
1259 // logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);
1261 videoLength = demuxer->stripAudio(threadBuffer, amountReceived);
1262 video->displayIFrame(threadBuffer, videoLength);
1263 currentFrameNumber = iframeNumber;
1265 threadBuffer = NULL;
1268 gettimeofday(&clock2, NULL);
1269 total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000
1270 + (clock2.tv_usec - clock0.tv_usec) / 1000
1272 disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000
1273 + (clock2.tv_usec - clock1.tv_usec) / 1000
1276 clock2 = timeGetTime();
1277 total_msec = clock2 - clock0 - sleepTime;
1278 disp_msec = clock2 - clock1 - sleepTime;
1280 // logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);
1284 void Player::threadPostStopCleanup()
1289 threadBuffer = NULL;
1293 // ----------------------------------- Dev
1296 void Player::test1()
1298 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
1300 // video->setAspectRatio(Video::ASPECT4X3);
1301 jumpToFrameP(37507);
1304 void Player::test2()
1306 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
1307 video->setAspectRatio(Video::ASPECT16X9);