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();
40 feedMode = MODE_NORMAL;
47 isRecording = tIsRecording;
55 startupBlockSize = 60000;
56 video->turnVideoOff();
61 startupBlockSize = 250000;
68 if (initted) shutdown();
73 if (initted) return 0;
75 pthread_mutex_init(&mutex, NULL);
77 mutex=CreateMutex(NULL,FALSE,NULL);
80 demuxer = new DemuxerVDR();
81 if (!demuxer) return 0;
83 if (!demuxer->init(this, audio, video))
85 logger->log("Player", Log::ERR, "Demuxer failed to init");
101 int Player::shutdown()
103 if (!initted) return 0;
131 void Player::setStartFrame(ULONG startFrame)
133 startFrameNum = startFrame;
136 feedPosition = VDR::getInstance()->positionFromFrameNumber(startFrameNum);
144 void Player::setLengthBytes(ULLONG length)
146 lastRescan = time(NULL);
147 lengthBytes = length;
148 logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);
151 void Player::setLengthFrames(ULONG length)
153 lengthFrames = length;
154 logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);
157 ULONG Player::getLengthFrames()
162 ULONG Player::getCurrentFrameNum()
164 if (startup) return 0;
165 return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());
168 // ----------------------------------- Externally called events
173 bool doUnlock = false;
174 int result = playInt(&doUnlock);
175 if (doUnlock) unLock();
182 logger->log("Player", Log::DEBUG, "Stop called lock");
187 void Player::togglePause()
194 void Player::toggleFastForward()
197 toggleFastForwardInt();
201 void Player::toggleFastBackward()
204 toggleFastBackwardInt();
208 void Player::jumpToPercent(int percent)
211 jumpToPercentInt(percent);
212 // unLock(); - let thread unlock this
215 void Player::skipForward(int seconds)
218 skipForwardInt(seconds);
219 // unLock(); - let thread unlock this
222 void Player::skipBackward(int seconds)
225 skipBackwardInt(seconds);
226 // unLock(); - let thread unlock this
229 // ----------------------------------- Implementations called events
232 int Player::playInt(bool* doUnlock)
234 if (!initted) return 0;
236 // If we are just paused, unpause!
244 // If we are fast forwarding, set to normal
247 toggleFastForwardInt();
251 // If we are fast backwarding, set to normal
254 toggleFastBackwardInt();
258 // If we are already playing, ignore. no resyncing to do now!
265 // Standard play start
266 logger->log("Player", Log::DEBUG, "Standard play start");
275 if (startFrameNum > lengthFrames) startFrameNum = 0;
276 demuxer->setFrameNum(startFrameNum);
278 if (!isRadio) demuxer->seek();
285 logger->log("Player", Log::DEBUG, "Immediate play");
293 else // do prebuffering
295 logger->log("Player", Log::DEBUG, "Prebuffering...");
303 void Player::stopInt()
305 if (!initted) return;
306 if (!playing) return;
313 video->unFastForward();
314 audio->systemMuteOff();
315 feedMode = MODE_NORMAL;
323 if (threadIsActive()) threadStop();
334 void Player::togglePauseInt()
336 if (!initted) return;
337 if (!playing) return;
339 if (ffwd) toggleFastForwardInt();
340 if (fbwd) toggleFastBackwardInt();
356 void Player::toggleFastForwardInt()
358 if (!initted) return;
359 if (!playing) return;
361 if (paused) togglePauseInt();
362 if (fbwd) toggleFastBackwardInt();
375 if (!isRadio) demuxer->seek();
385 audio->systemMuteOff();
395 audio->systemMuteOn();
396 video->fastForward();
400 void Player::toggleFastBackwardInt()
402 if (!initted) return;
403 if (!playing) return;
405 if (paused) togglePauseInt();
406 if (ffwd) toggleFastForwardInt();
412 audio->systemMuteOff();
415 feedMode = MODE_NORMAL;
424 audio->systemMuteOn();
427 feedMode = MODE_BACKWARDS;
431 if (!isRadio) demuxer->seek();
437 void Player::skipForwardInt(int seconds)
439 logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
440 restartAtFrame(getCurrentFrameNum() + (seconds * video->getFPS()));
443 void Player::skipBackwardInt(int seconds)
445 logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
446 long newFrameNum = getCurrentFrameNum() - (seconds * video->getFPS());
447 if (newFrameNum < 0) newFrameNum = 0;
448 restartAtFrame(newFrameNum);
451 void Player::jumpToPercentInt(int percent)
453 logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
454 ULONG newFrame = percent * lengthFrames / 100;
455 restartAtFrame(newFrame);
459 // ----------------------------------- Internal functions
464 pthread_mutex_lock(&mutex);
465 logger->log("Player", Log::DEBUG, "LOCKED");
468 WaitForSingleObject(mutex, INFINITE );
472 void Player::unLock()
475 logger->log("Player", Log::DEBUG, "UNLOCKING");
476 pthread_mutex_unlock(&mutex);
482 void Player::restartAtFrame(ULONG newFrame)
484 if (paused) togglePauseInt();
485 if (ffwd) toggleFastForwardInt();
487 ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(newFrame);
488 if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
489 logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", newFrame, feedPosition, newPosition);
498 if (!isRadio) demuxer->seek();
499 feedPosition = newPosition;
500 demuxer->setFrameNum(newFrame);
508 audio->systemMuteOff();
513 void Player::doConnectionLost()
515 Message* m = new Message();
516 m->message = Message::CONNECTION_LOST;
518 commandMessageQueue->postMessage(m);
521 // ----------------------------------- Callback
523 void Player::call(void* caller)
525 if (caller == demuxer)
527 logger->log("Player", Log::DEBUG, "Callback from demuxer");
529 if (video->getTVsize() == Video::ASPECT4X3)
531 logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
535 int dxCurrentAspect = demuxer->getAspectRatio();
536 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
538 logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
539 video->setAspectRatio(Video::ASPECT4X3);
541 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
543 logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
544 video->setAspectRatio(Video::ASPECT16X9);
548 logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
556 videoStartup = false;
564 threadSignalNoLock();
568 // ----------------------------------- Feed thread
570 void Player::threadMethod()
575 UINT preBufferTotal = 0;
577 ULLONG currentposition;
580 VDR* vdr = VDR::getInstance();
591 // If we havn't rescanned for a while..
592 if (isRecording && ((lastRescan + 60) < time(NULL)))
594 lengthBytes = vdr->rescanRecording(&lengthFrames);
595 if (!vdr->isConnected()) { doConnectionLost(); return; }
596 logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
597 lastRescan = time(NULL);
601 if (lengthBytes) // is playing a recording
603 if (feedPosition >= lengthBytes) break; // finished playback
607 if (startupBlockSize > lengthBytes)
608 askFor = lengthBytes; // is a very small recording!
610 askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
614 if ((feedPosition + blockSize) > lengthBytes) // last block of recording
615 askFor = lengthBytes - feedPosition;
620 else // is playing live
623 askFor = startupBlockSize; // find audio streams sized block
625 askFor = blockSize; // normal
628 threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
630 currentposition=feedPosition;
632 if (!vdr->isConnected())
638 if (!threadBuffer) break;
642 int a_stream = demuxer->scan(threadBuffer, thisRead);
643 demuxer->setAudioStream(a_stream);
644 logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
646 // setStartTS(thisRead);
648 // if (isRecording) setEndTS();
655 preBufferTotal += thisRead;
656 if (preBufferTotal > 500000)
658 logger->log("Player", Log::DEBUG, "Got >500K, prebuffering complete");
660 preBuffering = false;
669 // unLock(); // thread will be locked by play until here
670 // FIXME - see if this can segfault because it is starting threads out of the master mutex
674 if (feedMode == MODE_NORMAL)
676 feedPosition += thisRead;
678 else if (feedMode == MODE_BACKWARDS)
680 if (feedPosition >= blockSize)
682 feedPosition -= blockSize;
683 if (!isRadio) demuxer->seek();
687 // got to the start of the recording.. revert to play mode? how?
688 feedPosition += thisRead;
694 while(writeLength < thisRead)
697 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
699 thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength,
700 currentposition+(ULLONG)writeLength);
702 writeLength += thisWrite;
703 // logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite);
707 // logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
708 // demuxer is full and can't take anymore
710 threadWaitForSignal();
712 // logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
724 logger->log("Player", Log::DEBUG, "Recording playback ends");
726 if (videoStartup) // oh woe. there never was a stream, I was conned!
728 videoStartup = false;
730 MILLISLEEP(500); // I think this will solve a race
735 Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
736 if (lengthBytes) m->message = Message::STOP_PLAYBACK; // recording
737 else m->message = Message::STREAM_END; // live
738 logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
739 commandMessageQueue->postMessage(m);
740 logger->log("Player", Log::DEBUG, "Message posted...");
743 void Player::threadPostStopCleanup()
752 // ----------------------------------- Dev
757 logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
759 // video->setAspectRatio(Video::ASPECT4X3);
764 logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
765 video->setAspectRatio(Video::ASPECT16X9);