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, see <https://www.gnu.org/licenses/>.
20 #include "playermedia.h"
21 //we most probably need a new demux...
22 #include "demuxermedia.h"
24 #include "messagequeue.h"
29 #include "mediaplayer.h"
31 //how long to wait for new data (100ms units)
35 PlayerMedia::PlayerMedia(Boxx *parent) : afeed(this), vfeed(this)
38 audio = Audio::getInstance();
39 video = Video::getInstance();
40 logger = Log::getInstance();
41 vdr = VDR::getInstance();
42 logger->log("PlayerMedia", Log::DEBUG, "ctorI");
49 feederState=FEEDER_STOP;
53 Video::getInstance()->turnVideoOn();
56 demuxer=new DemuxerMedia();
57 logger->log("PlayerMedia", Log::DEBUG, "ctorII");
58 if (!demuxer->init(this, audio, video, NULL, 2097152, 524288, 0))
60 logger->log("PlayerMedia", Log::ERR, "Demuxer failed to init");
64 demuxer->setFrameNum(0);
65 logger->log("PlayerMedia", Log::DEBUG, " ctorIII");
72 logger->log("PlayerMedia", Log::DEBUG, "player created");
82 PlayerMedia::~PlayerMedia()
84 if (threadBuffer) free(threadBuffer);
85 Timers::getInstance()->cancelTimer(this,1);
86 controlFeeder(FEEDER_STOP);
89 audio->setStreamType(Audio::MPEG2_PES);
92 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
95 void PlayerMedia::controlFeeder(int feederAction) {
96 logger->log("PlayerMedia",Log::DEBUG,"control feeder old=%d, new=%d",feederState,feederAction);
97 switch (feederAction) {
100 if (feederState == FEEDER_STOP) {
107 if (feederState != FEEDER_STOP) {
116 feederState=feederAction;
120 void PlayerMedia::run() {
121 if (playerRunnig) return;
127 void PlayerMedia::shutdown()
131 threadSignalNoLock();
132 //wait for the player thread to stop
133 logger->log("PlayerMedia",Log::DEBUG,"shutdown - warting for player thread to stop");
135 for (int loopcount=200;playerRunnig && loopcount > 0;loopcount--) {
139 logger->log("PlayerMedia",Log::ERR,"shutdown - unable to stop player within 10s");
144 bool PlayerMedia::isPlayerRunning() {
148 int PlayerMedia::setRequestedState(UCHAR rstate) {
153 rt=requestedSequence;
159 UCHAR PlayerMedia::getState() {
167 void PlayerMedia::setState(UCHAR s) {
173 //------------------- externally called functions --------------------------
175 //we leave all the open and close handling to the frontend
176 //so we only need the channel
177 int PlayerMedia::playNew(ULONG mChannel,ULLONG lBytes,ULLONG lFrames)
179 logger->log("PlayerMedia", Log::DEBUG, "play request for channel %lu", mChannel);
180 if (getState() != S_STOP) {
184 int rt=MediaPlayer::getInstance()->getMediaInfo(mChannel,&mi);
185 canPosition=mi.canPosition;
186 if (rt == 0 && mi.canPosition) {
188 UCHAR buf[STARTBUFLEN];
191 logger->log("PlayerMedia", Log::DEBUG, "try to find last PTS with %lu bytes", STARTBUFLEN);
192 int rb=MediaPlayer::getInstance()->getMediaBlock(mChannel,lBytes-STARTBUFLEN,STARTBUFLEN,&rtlen,&bpointer);
194 demuxer->findLastPTS(bpointer,rtlen);
196 if (bpointer != buf) free(bpointer);
198 mediaChannel=mChannel;
200 lengthFrames=lFrames;
203 logger->log("PlayerMedia", Log::DEBUG, "play request setState");
206 rt=requestedSequence;
209 threadSignalNoLock();
210 logger->log("PlayerMedia", Log::DEBUG, "play request rt=%d",rt);
214 int PlayerMedia::stop()
216 int rt= setRequestedState(S_STOP);
217 threadSignalNoLock();
221 int PlayerMedia::play(){
222 if (! playingStarted) return -1;
223 int rt=setRequestedState(S_PLAY);
224 threadSignalNoLock();
228 int PlayerMedia::pause()
230 int rt= setRequestedState(S_PAUSE);
231 threadSignalNoLock();
234 int PlayerMedia::unpause()
236 int rt= setRequestedState(S_PLAY);
237 threadSignalNoLock();
241 int PlayerMedia::fastForward(){
242 if (! canPosition) return -1;
243 int rt=setRequestedState(S_FF);
244 threadSignalNoLock();
248 int PlayerMedia::fastBackward(){
249 if (! canPosition) return -1;
250 int rt=setRequestedState(S_BACK);
251 threadSignalNoLock();
254 int PlayerMedia::jumpToPercent(double percent){
255 if (! canPosition) return -1;
256 logger->log("PlayerMedia",Log::DEBUG,"jump to %i%% ",percent);
258 ULLONG npos=streampos;
259 //TODO: where to go - currently we simply take bytes...
260 npos=(ULLONG)(percent*lengthBytes/100);
261 if (npos > lengthBytes) npos=lengthBytes-1;
262 requestedStreampos=npos;
263 requestState=S_POSITION;
266 //no need to wait here...
270 int PlayerMedia::skipForward(int seconds) {
271 if (! canPosition) return -1;
272 logger->log("PlayerMedia",Log::DEBUG,"skip fw %ds",seconds);
274 ULLONG bps=getBytesPerSecond();
275 requestedStreampos=streampos+ (ULLONG)seconds*bps;
276 requestState=S_POSITION;
279 logger->log("PlayerMedia",Log::DEBUG,"skip fw to %llu, %llu bps",requestedStreampos,bps);
282 int PlayerMedia::skipBackward(int seconds) {
283 if (! canPosition) return -1;
284 logger->log("PlayerMedia",Log::DEBUG,"skip back %ds",seconds);
286 ULLONG bps=getBytesPerSecond();
287 if (streampos > (ULLONG)seconds*bps)
288 requestedStreampos=streampos- (ULLONG)seconds*bps;
290 requestedStreampos=0;
291 requestState=S_POSITION;
294 logger->log("PlayerMedia",Log::DEBUG,"skip bw to %llu, %llu bps",requestedStreampos,bps);
300 // ----------------------------------- Internal functions
302 void PlayerMedia::gotoSeek() {
303 controlFeeder(FEEDER_STOP);
307 audio->setStreamType(Audio::MPEG2_PES);
310 controlFeeder(FEEDER_START);
314 audio->systemMuteOff();
318 onStartup=true; //search for Audio again
319 //TODO: should be done only at startup
324 void PlayerMedia::sendFrontendMessage(ULONG para)
326 logger->log("PlayerMedia", Log::DEBUG, "sending frontend message %ld",para);
327 Message* m = new Message();
332 m->message = Message::PLAYER_EVENT;
334 MessageQueue::getInstance()->postMessage(m);
337 //method called by the playing thread to handle
338 //"commands" by the frontend
339 UCHAR PlayerMedia::checkState()
342 UCHAR rstate=requestState;
344 int rseq=requestedSequence;
349 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u", cstate, rstate);
352 case S_PLAY: // to S_PLAY
354 if (cstate != S_SEEK && cstate != S_PAUSE) break;
356 if (cstate == S_PAUSE) {
362 controlFeeder(FEEDER_START);
370 case S_SEEK: //INITIAL_PLAY
376 case S_PAUSE: // to S_PAUSE
378 if (cstate == S_PAUSE) {
385 if (cstate != S_PLAY) {
393 case S_FF: // to S_FF
399 case S_BACK: // to S_BACK
405 case S_STOP: // to S_STOP
407 if (cstate == S_STOP) {
410 controlFeeder(FEEDER_STOP);
418 demuxer->setFrameNum(0);//trigger framecounting
421 case S_POSITION: // to S_JUMP
426 controlFeeder(FEEDER_STOP);
429 if (threadBuffer) free(threadBuffer);
432 ULLONG curPts=getCurrentPTS();
433 if (curPts != 0) bpts=streampos/curPts;
434 if (bpts != 0) appDestinationPTS=requestedStreampos/bpts;
435 else appDestinationPTS=0;
436 streampos=requestedStreampos;
437 bytesWritten=streampos;
438 Log::getInstance()->log("PlayerMedia",Log::DEBUG,"position to %llu",streampos);
444 Timers::getInstance()->setTimerD(this,1,15);
451 if (cstate != rstate && rstate != S_DONE ) {
452 sendFrontendMessage(STATUS_CHANGE);
453 //any change after done cancels the "done" timer
454 Timers::getInstance()->cancelTimer(this,1);
456 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u completed", cstate, rstate);
457 //we return the newly set state
460 //rstate could be different but no new request - so return cstate
467 // ----------------------------------- Feed thread
469 void PlayerMedia::waitTimed(int ms) {
473 int us=1000*(ms - 1000*sec);
476 gettimeofday(&ct,NULL);
477 nt.tv_sec=ct.tv_sec+sec;
478 nt.tv_nsec=1000*us+1000*ct.tv_usec;
480 DWORD ct=timeGetTime();
481 nt.tv_sec=ct/1000+sec;
482 nt.tv_nsec=1000*us+1000*ct*1000;
484 threadWaitForSignalTimed(&nt);
488 void PlayerMedia::threadMethod()
490 logger->log("PlayerMedia", Log::DEBUG, "player thread started");
496 UCHAR cstate=checkState();
500 if (cstate != S_PLAY && cstate != S_FF && cstate != S_BACK && cstate != S_SEEK) {
506 if (thisWrite == thisRead) {
511 if (onStartup) rd=STARTBUFLEN;
512 int rt=MediaPlayer::getInstance()->getMediaBlock(mediaChannel,streampos,rd,&thisRead,&threadBuffer);
513 if ( thisRead == 0 && threadBuffer) {
517 if ((!threadBuffer || thisRead == 0) && rt == 0) {
519 if (retrycount > MAXTRY) rt=-1;
521 logger->log("PlayerMedia", Log::DEBUG, "no data - retrying");
527 if (!threadBuffer || thisRead == 0 || rt != 0) {
528 //OK we count this as end of stream
529 logger->log("PlayerMedia", Log::DEBUG, "stream end");
530 setRequestedState(S_DONE);
531 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
534 //logger->log("PlayerMedia", Log::DEBUG, "read %ld bytes at pos %ld",thisRead,streampos);
542 if (demuxer->getselAudioChannel() < Demuxer::PESTYPE_AUD0) {
543 int a_stream = demuxer->scan(threadBuffer, thisRead);
545 demuxer->setAudioStream(a_stream);
546 logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
549 logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream not choosen - leave to demux");
553 setRequestedState(S_PLAY); //OK - start playing even if we have no audio
558 memset(&p,sizeof(p),0);
563 audio->PrepareMediaSample(pl,0);
565 UINT rt=audio->DeliverMediaSample(threadBuffer,&bytesWritten);
566 ULONG written=thisRead;
568 written=bytesWritten;
570 ULONG written= demuxer->put(threadBuffer + thisWrite, thisRead - thisWrite);
572 bytesWritten+=written;
573 if (thisWrite < thisRead) {
575 //logger->log("PlayerMedia", Log::DEBUG, "demuxer full, waiting ");
576 // demuxer is full and can't take anymore
581 //logger->log("PlayerMedia", Log::DEBUG, "block written %d", thisWrite);
590 logger->log("PlayerMedia", Log::DEBUG, "finished");
591 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
593 setRequestedState(S_STOP);
597 int PlayerMedia::waitForSequence(int timeout, int seq) {
598 time_t starttime=time(NULL)+timeout;
600 logger->log("PlayerMedia", Log::DEBUG, "waiting for sequence %d",seq);
601 while ((curtime=time(NULL)) < starttime) {
602 int cseq=getSequence();
603 if (cseq >= seq) return cseq;
609 int PlayerMedia::getSequence() {
619 void PlayerMedia::threadPostStopCleanup()
623 delete(threadBuffer);
630 void PlayerMedia::timercall(int ref) {
632 logger->log("PlayerMedia", Log::DEBUG, "stream end - informing frontend");
633 sendFrontendMessage(STREAM_END);
638 void PlayerMedia::call(void* caller)
640 if (caller == demuxer)
642 logger->log("PlayerMedia", Log::DEBUG, "Callback from demuxer");
644 if (demuxer->getHorizontalSize() > 0 && demuxer->getVerticalSize() > 0) {
645 int xpos=(video->getScreenWidth()-demuxer->getHorizontalSize())/2;
646 if (xpos <0 ) xpos=0;
647 int ypos=(video->getScreenHeight()-demuxer->getVerticalSize())/2;
648 if (ypos < 0) ypos=0;
649 logger->log("PlayerMedia", Log::DEBUG, "setting pos x=%d,y=%d",xpos,ypos);
650 //video->setPosition(xpos,ypos); // needs to be fixed
653 if (video->getTVsize() == Video::ASPECT4X3)
655 logger->log("PlayerMedia", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
660 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
661 if (dxCurrentAspect == Demuxer::ASPECT_4_3)
663 logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
664 video->setAspectRatio(Video::ASPECT4X3,parx,pary);
666 Message* m = new Message();
669 m->message = Message::PLAYER_EVENT;
670 m->parameter = PlayerMedia::ASPECT43;
671 MessageQueue::getInstance()->postMessage(m);
673 else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
675 logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
676 video->setAspectRatio(Video::ASPECT16X9,parx,pary);
678 Message* m = new Message();
681 m->message = Message::PLAYER_EVENT;
682 m->parameter = PlayerMedia::ASPECT169;
683 MessageQueue::getInstance()->postMessage(m);
687 logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is something else... switch anayway");
688 video->setAspectRatio(dxCurrentAspect,parx,pary);
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);
764 SNPRINTF(ibuf,500,"%s: %dx%d\n"
768 demuxer->getHorizontalSize(),
769 demuxer->getVerticalSize(),
771 demuxer->getAspectRatio(&parx,&pary)==2?"4:3":"16:9",