2 Copyright 2004-2006 Chris Tallon, Andreas Vogel
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.
21 #include "playermedia.h"
22 //we most probably need a new demux...
23 #include "demuxermedia.h"
31 #include "mediaplayer.h"
33 //how long to wait for new data (100ms units)
37 PlayerMedia::PlayerMedia(Boxx *parent) : afeed(this), vfeed(this)
40 audio = Audio::getInstance();
41 video = Video::getInstance();
42 logger = Log::getInstance();
43 vdr = VDR::getInstance();
44 logger->log("PlayerMedia", Log::DEBUG, "ctorI");
51 feederState=FEEDER_STOP;
55 Video::getInstance()->turnVideoOn();
58 demuxer=new DemuxerMedia();
59 logger->log("PlayerMedia", Log::DEBUG, "ctorII");
60 if (!demuxer->init(this, audio, video, NULL, 2097152, 524288, 0))
62 logger->log("PlayerMedia", Log::ERR, "Demuxer failed to init");
66 demuxer->setFrameNum(0);
67 logger->log("PlayerMedia", Log::DEBUG, " ctorIII");
74 logger->log("PlayerMedia", Log::DEBUG, "player created");
84 PlayerMedia::~PlayerMedia()
86 if (threadBuffer) free(threadBuffer);
87 Timers::getInstance()->cancelTimer(this,1);
88 controlFeeder(FEEDER_STOP);
91 audio->setStreamType(Audio::MPEG2_PES);
94 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
97 void PlayerMedia::controlFeeder(int feederAction) {
98 logger->log("PlayerMedia",Log::DEBUG,"control feeder old=%d, new=%d",feederState,feederAction);
99 switch (feederAction) {
102 if (feederState == FEEDER_STOP) {
109 if (feederState != FEEDER_STOP) {
118 feederState=feederAction;
122 void PlayerMedia::run() {
123 if (playerRunnig) return;
129 void PlayerMedia::shutdown()
133 threadSignalNoLock();
134 //wait for the player thread to stop
135 logger->log("PlayerMedia",Log::DEBUG,"shutdown - warting for player thread to stop");
137 for (int loopcount=200;playerRunnig && loopcount > 0;loopcount--) {
141 logger->log("PlayerMedia",Log::ERR,"shutdown - unable to stop player within 10s");
146 bool PlayerMedia::isPlayerRunning() {
150 int PlayerMedia::setRequestedState(UCHAR rstate) {
155 rt=requestedSequence;
161 UCHAR PlayerMedia::getState() {
169 void PlayerMedia::setState(UCHAR s) {
175 //------------------- externally called functions --------------------------
177 //we leave all the open and close handling to the frontend
178 //so we only need the channel
179 int PlayerMedia::playNew(ULONG mChannel,ULLONG lBytes,ULLONG lFrames)
181 logger->log("PlayerMedia", Log::DEBUG, "play request for channel %lu", mChannel);
182 if (getState() != S_STOP) {
186 int rt=MediaPlayer::getInstance()->getMediaInfo(mChannel,&mi);
187 canPosition=mi.canPosition;
188 if (rt == 0 && mi.canPosition) {
190 UCHAR buf[STARTBUFLEN];
193 logger->log("PlayerMedia", Log::DEBUG, "try to find last PTS with %lu bytes", STARTBUFLEN);
194 int rb=MediaPlayer::getInstance()->getMediaBlock(mChannel,lBytes-STARTBUFLEN,STARTBUFLEN,&rtlen,&bpointer);
196 demuxer->findLastPTS(bpointer,rtlen);
198 if (bpointer != buf) free(bpointer);
200 mediaChannel=mChannel;
202 lengthFrames=lFrames;
205 logger->log("PlayerMedia", Log::DEBUG, "play request setState");
208 rt=requestedSequence;
211 threadSignalNoLock();
212 logger->log("PlayerMedia", Log::DEBUG, "play request rt=%d",rt);
216 int PlayerMedia::stop()
218 int rt= setRequestedState(S_STOP);
219 threadSignalNoLock();
223 int PlayerMedia::play(){
224 if (! playingStarted) return -1;
225 int rt=setRequestedState(S_PLAY);
226 threadSignalNoLock();
230 int PlayerMedia::pause()
232 int rt= setRequestedState(S_PAUSE);
233 threadSignalNoLock();
236 int PlayerMedia::unpause()
238 int rt= setRequestedState(S_PLAY);
239 threadSignalNoLock();
243 int PlayerMedia::fastForward(){
244 if (! canPosition) return -1;
245 int rt=setRequestedState(S_FF);
246 threadSignalNoLock();
250 int PlayerMedia::fastBackward(){
251 if (! canPosition) return -1;
252 int rt=setRequestedState(S_BACK);
253 threadSignalNoLock();
256 int PlayerMedia::jumpToPercent(double percent){
257 if (! canPosition) return -1;
258 logger->log("PlayerMedia",Log::DEBUG,"jump to %i%% ",percent);
260 ULLONG npos=streampos;
261 //TODO: where to go - currently we simply take bytes...
262 npos=(ULLONG)(percent*lengthBytes/100);
263 if (npos > lengthBytes) npos=lengthBytes-1;
264 requestedStreampos=npos;
265 requestState=S_POSITION;
268 //no need to wait here...
272 int PlayerMedia::skipForward(int seconds) {
273 if (! canPosition) return -1;
274 logger->log("PlayerMedia",Log::DEBUG,"skip fw %ds",seconds);
276 ULLONG bps=getBytesPerSecond();
277 requestedStreampos=streampos+ (ULLONG)seconds*bps;
278 requestState=S_POSITION;
281 logger->log("PlayerMedia",Log::DEBUG,"skip fw to %llu, %llu bps",requestedStreampos,bps);
284 int PlayerMedia::skipBackward(int seconds) {
285 if (! canPosition) return -1;
286 logger->log("PlayerMedia",Log::DEBUG,"skip back %ds",seconds);
288 ULLONG bps=getBytesPerSecond();
289 if (streampos > (ULLONG)seconds*bps)
290 requestedStreampos=streampos- (ULLONG)seconds*bps;
292 requestedStreampos=0;
293 requestState=S_POSITION;
296 logger->log("PlayerMedia",Log::DEBUG,"skip bw to %llu, %llu bps",requestedStreampos,bps);
302 // ----------------------------------- Internal functions
304 void PlayerMedia::gotoSeek() {
305 controlFeeder(FEEDER_STOP);
309 audio->setStreamType(Audio::MPEG2_PES);
312 controlFeeder(FEEDER_START);
316 audio->systemMuteOff();
320 onStartup=true; //search for Audio again
321 //TODO: should be done only at startup
326 void PlayerMedia::sendFrontendMessage(ULONG para)
328 logger->log("PlayerMedia", Log::DEBUG, "sending frontend message %ld",para);
329 Message* m = new Message();
334 m->message = Message::PLAYER_EVENT;
336 Command::getInstance()->postMessageFromOuterSpace(m);
339 //method called by the playing thread to handle
340 //"commands" by the frontend
341 UCHAR PlayerMedia::checkState()
344 UCHAR rstate=requestState;
346 int rseq=requestedSequence;
351 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u", cstate, rstate);
354 case S_PLAY: // to S_PLAY
356 if (cstate != S_SEEK && cstate != S_PAUSE) break;
358 if (cstate == S_PAUSE) {
364 controlFeeder(FEEDER_START);
372 case S_SEEK: //INITIAL_PLAY
378 case S_PAUSE: // to S_PAUSE
380 if (cstate == S_PAUSE) {
387 if (cstate != S_PLAY) {
395 case S_FF: // to S_FF
401 case S_BACK: // to S_BACK
407 case S_STOP: // to S_STOP
409 if (cstate == S_STOP) {
412 controlFeeder(FEEDER_STOP);
420 demuxer->setFrameNum(0);//trigger framecounting
423 case S_POSITION: // to S_JUMP
428 controlFeeder(FEEDER_STOP);
431 if (threadBuffer) free(threadBuffer);
434 ULLONG curPts=getCurrentPTS();
435 if (curPts != 0) bpts=streampos/curPts;
436 if (bpts != 0) appDestinationPTS=requestedStreampos/bpts;
437 else appDestinationPTS=0;
438 streampos=requestedStreampos;
439 bytesWritten=streampos;
440 Log::getInstance()->log("PlayerMedia",Log::DEBUG,"position to %llu",streampos);
446 Timers::getInstance()->setTimerD(this,1,15);
453 if (cstate != rstate && rstate != S_DONE ) {
454 sendFrontendMessage(STATUS_CHANGE);
455 //any change after done cancels the "done" timer
456 Timers::getInstance()->cancelTimer(this,1);
458 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u completed", cstate, rstate);
459 //we return the newly set state
462 //rstate could be different but no new request - so return cstate
469 // ----------------------------------- Feed thread
471 void PlayerMedia::waitTimed(int ms) {
475 int us=1000*(ms - 1000*sec);
478 gettimeofday(&ct,NULL);
479 nt.tv_sec=ct.tv_sec+sec;
480 nt.tv_nsec=1000*us+1000*ct.tv_usec;
482 DWORD ct=timeGetTime();
483 nt.tv_sec=ct/1000+sec;
484 nt.tv_nsec=1000*us+1000*ct*1000;
486 threadWaitForSignalTimed(&nt);
490 void PlayerMedia::threadMethod()
492 logger->log("PlayerMedia", Log::DEBUG, "player thread started");
498 UCHAR cstate=checkState();
502 if (cstate != S_PLAY && cstate != S_FF && cstate != S_BACK && cstate != S_SEEK) {
508 if (thisWrite == thisRead) {
513 if (onStartup) rd=STARTBUFLEN;
514 int rt=MediaPlayer::getInstance()->getMediaBlock(mediaChannel,streampos,rd,&thisRead,&threadBuffer);
515 if ( thisRead == 0 && threadBuffer) {
519 if ((!threadBuffer || thisRead == 0) && rt == 0) {
521 if (retrycount > MAXTRY) rt=-1;
523 logger->log("PlayerMedia", Log::DEBUG, "no data - retrying");
529 if (!threadBuffer || thisRead == 0 || rt != 0) {
530 //OK we count this as end of stream
531 logger->log("PlayerMedia", Log::DEBUG, "stream end");
532 setRequestedState(S_DONE);
533 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
536 //logger->log("PlayerMedia", Log::DEBUG, "read %ld bytes at pos %ld",thisRead,streampos);
544 if (demuxer->getselAudioChannel() < Demuxer::PESTYPE_AUD0) {
545 int a_stream = demuxer->scan(threadBuffer, thisRead);
547 demuxer->setAudioStream(a_stream);
548 logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
551 logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream not choosen - leave to demux");
555 setRequestedState(S_PLAY); //OK - start playing even if we have no audio
560 memset(&p,sizeof(p),0);
565 audio->PrepareMediaSample(pl,0);
567 UINT rt=audio->DeliverMediaSample(threadBuffer,&bytesWritten);
568 ULONG written=thisRead;
570 written=bytesWritten;
572 ULONG written= demuxer->put(threadBuffer + thisWrite, thisRead - thisWrite);
574 bytesWritten+=written;
575 if (thisWrite < thisRead) {
577 //logger->log("PlayerMedia", Log::DEBUG, "demuxer full, waiting ");
578 // demuxer is full and can't take anymore
583 //logger->log("PlayerMedia", Log::DEBUG, "block written %d", thisWrite);
592 logger->log("PlayerMedia", Log::DEBUG, "finished");
593 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
595 setRequestedState(S_STOP);
599 int PlayerMedia::waitForSequence(int timeout, int seq) {
600 time_t starttime=time(NULL)+timeout;
602 logger->log("PlayerMedia", Log::DEBUG, "waiting for sequence %d",seq);
603 while ((curtime=time(NULL)) < starttime) {
604 int cseq=getSequence();
605 if (cseq >= seq) return cseq;
611 int PlayerMedia::getSequence() {
621 void PlayerMedia::threadPostStopCleanup()
625 delete(threadBuffer);
632 void PlayerMedia::timercall(int ref) {
634 logger->log("PlayerMedia", Log::DEBUG, "stream end - informing frontend");
635 sendFrontendMessage(STREAM_END);
640 void PlayerMedia::call(void* caller)
642 if (caller == demuxer)
644 logger->log("PlayerMedia", Log::DEBUG, "Callback from demuxer");
646 if (demuxer->getHorizontalSize() > 0 && demuxer->getVerticalSize() > 0) {
647 int xpos=(video->getScreenWidth()-demuxer->getHorizontalSize())/2;
648 if (xpos <0 ) xpos=0;
649 int ypos=(video->getScreenHeight()-demuxer->getVerticalSize())/2;
650 if (ypos < 0) ypos=0;
651 logger->log("PlayerMedia", Log::DEBUG, "setting pos x=%d,y=%d",xpos,ypos);
652 video->setPosition(xpos,ypos);
655 if (video->getTVsize() == Video::ASPECT4X3)
657 logger->log("PlayerMedia", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
661 int dxCurrentAspect = demuxer->getAspectRatio();
662 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
664 logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
665 video->setAspectRatio(Video::ASPECT4X3);
667 Message* m = new Message();
670 m->message = Message::PLAYER_EVENT;
671 m->parameter = PlayerMedia::ASPECT43;
672 Command::getInstance()->postMessageFromOuterSpace(m);
674 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
676 logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
677 video->setAspectRatio(Video::ASPECT16X9);
679 Message* m = new Message();
682 m->message = Message::PLAYER_EVENT;
683 m->parameter = PlayerMedia::ASPECT169;
684 Command::getInstance()->postMessageFromOuterSpace(m);
688 logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is something else... ignoring");
694 if (getState() == S_SEEK)
696 logger->log("PlayerMedia", Log::DEBUG, "feeder started writing");
697 setRequestedState(S_PLAY);
700 threadSignalNoLock();
704 void PlayerMedia::setAudioChannel(int newChannel)
706 demuxer->setAudioStream(newChannel);
709 bool* PlayerMedia::getDemuxerMpegAudioChannels()
711 return demuxer->getmpAudioChannels();
714 bool* PlayerMedia::getDemuxerAc3AudioChannels()
716 return demuxer->getac3AudioChannels();
719 int PlayerMedia::getCurrentAudioChannel()
721 return demuxer->getselAudioChannel();
724 ULLONG PlayerMedia::getLengthBytes() {
727 ULLONG PlayerMedia::getLengthFrames() {
731 //PTS is always relative to first
732 ULLONG PlayerMedia::getCurrentPTS()
734 if (appDestinationPTS != 0) {
735 ULLONG lenPts=demuxer->getLenPTS();
736 if (appDestinationPTS < lenPts) return appDestinationPTS;
739 return demuxer->getCurrentPTS();
741 ULLONG PlayerMedia::getLenPTS()
743 return demuxer->getLenPTS();
746 ULLONG PlayerMedia::getBytesPerSecond() {
747 ULLONG currsec=getCurrentPTS()/90000;
748 if (currsec == 0) return 0;
749 return streampos/currsec;
752 char * PlayerMedia::getInfo() {
753 char * ibuf=new char[500];
754 int br=demuxer->getBitRate();
756 if (br == 0x3ffff ) {
757 //VBR - use estimated
758 sprintf(brbuf,"VBR, cur %llu kBit/s",getBytesPerSecond()*8/1000);
761 sprintf(brbuf,"CBR, %d kBit/s",br*4/10);
763 SNPRINTF(ibuf,500,"%s: %dx%d\n"
767 demuxer->getHorizontalSize(),
768 demuxer->getVerticalSize(),
770 demuxer->getAspectRatio()==2?"4:3":"16:9",