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"
25 #include "messagequeue.h"
30 #include "mediaplayer.h"
32 //how long to wait for new data (100ms units)
36 PlayerMedia::PlayerMedia(Boxx *parent) : afeed(this), vfeed(this)
39 audio = Audio::getInstance();
40 video = Video::getInstance();
41 logger = Log::getInstance();
42 vdr = VDR::getInstance();
43 logger->log("PlayerMedia", Log::DEBUG, "ctorI");
50 feederState=FEEDER_STOP;
54 Video::getInstance()->turnVideoOn();
57 demuxer=new DemuxerMedia();
58 logger->log("PlayerMedia", Log::DEBUG, "ctorII");
59 if (!demuxer->init(this, audio, video, NULL, 2097152, 524288, 0))
61 logger->log("PlayerMedia", Log::ERR, "Demuxer failed to init");
65 demuxer->setFrameNum(0);
66 logger->log("PlayerMedia", Log::DEBUG, " ctorIII");
73 logger->log("PlayerMedia", Log::DEBUG, "player created");
83 PlayerMedia::~PlayerMedia()
85 if (threadBuffer) free(threadBuffer);
86 Timers::getInstance()->cancelTimer(this,1);
87 controlFeeder(FEEDER_STOP);
90 audio->setStreamType(Audio::MPEG2_PES);
93 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
96 void PlayerMedia::controlFeeder(int feederAction) {
97 logger->log("PlayerMedia",Log::DEBUG,"control feeder old=%d, new=%d",feederState,feederAction);
98 switch (feederAction) {
101 if (feederState == FEEDER_STOP) {
108 if (feederState != FEEDER_STOP) {
117 feederState=feederAction;
121 void PlayerMedia::run() {
122 if (playerRunnig) return;
128 void PlayerMedia::shutdown()
132 threadSignalNoLock();
133 //wait for the player thread to stop
134 logger->log("PlayerMedia",Log::DEBUG,"shutdown - warting for player thread to stop");
136 for (int loopcount=200;playerRunnig && loopcount > 0;loopcount--) {
140 logger->log("PlayerMedia",Log::ERR,"shutdown - unable to stop player within 10s");
145 bool PlayerMedia::isPlayerRunning() {
149 int PlayerMedia::setRequestedState(UCHAR rstate) {
154 rt=requestedSequence;
160 UCHAR PlayerMedia::getState() {
168 void PlayerMedia::setState(UCHAR s) {
174 //------------------- externally called functions --------------------------
176 //we leave all the open and close handling to the frontend
177 //so we only need the channel
178 int PlayerMedia::playNew(ULONG mChannel,ULLONG lBytes,ULLONG lFrames)
180 logger->log("PlayerMedia", Log::DEBUG, "play request for channel %lu", mChannel);
181 if (getState() != S_STOP) {
185 int rt=MediaPlayer::getInstance()->getMediaInfo(mChannel,&mi);
186 canPosition=mi.canPosition;
187 if (rt == 0 && mi.canPosition) {
189 UCHAR buf[STARTBUFLEN];
192 logger->log("PlayerMedia", Log::DEBUG, "try to find last PTS with %lu bytes", STARTBUFLEN);
193 int rb=MediaPlayer::getInstance()->getMediaBlock(mChannel,lBytes-STARTBUFLEN,STARTBUFLEN,&rtlen,&bpointer);
195 demuxer->findLastPTS(bpointer,rtlen);
197 if (bpointer != buf) free(bpointer);
199 mediaChannel=mChannel;
201 lengthFrames=lFrames;
204 logger->log("PlayerMedia", Log::DEBUG, "play request setState");
207 rt=requestedSequence;
210 threadSignalNoLock();
211 logger->log("PlayerMedia", Log::DEBUG, "play request rt=%d",rt);
215 int PlayerMedia::stop()
217 int rt= setRequestedState(S_STOP);
218 threadSignalNoLock();
222 int PlayerMedia::play(){
223 if (! playingStarted) return -1;
224 int rt=setRequestedState(S_PLAY);
225 threadSignalNoLock();
229 int PlayerMedia::pause()
231 int rt= setRequestedState(S_PAUSE);
232 threadSignalNoLock();
235 int PlayerMedia::unpause()
237 int rt= setRequestedState(S_PLAY);
238 threadSignalNoLock();
242 int PlayerMedia::fastForward(){
243 if (! canPosition) return -1;
244 int rt=setRequestedState(S_FF);
245 threadSignalNoLock();
249 int PlayerMedia::fastBackward(){
250 if (! canPosition) return -1;
251 int rt=setRequestedState(S_BACK);
252 threadSignalNoLock();
255 int PlayerMedia::jumpToPercent(double percent){
256 if (! canPosition) return -1;
257 logger->log("PlayerMedia",Log::DEBUG,"jump to %i%% ",percent);
259 ULLONG npos=streampos;
260 //TODO: where to go - currently we simply take bytes...
261 npos=(ULLONG)(percent*lengthBytes/100);
262 if (npos > lengthBytes) npos=lengthBytes-1;
263 requestedStreampos=npos;
264 requestState=S_POSITION;
267 //no need to wait here...
271 int PlayerMedia::skipForward(int seconds) {
272 if (! canPosition) return -1;
273 logger->log("PlayerMedia",Log::DEBUG,"skip fw %ds",seconds);
275 ULLONG bps=getBytesPerSecond();
276 requestedStreampos=streampos+ (ULLONG)seconds*bps;
277 requestState=S_POSITION;
280 logger->log("PlayerMedia",Log::DEBUG,"skip fw to %llu, %llu bps",requestedStreampos,bps);
283 int PlayerMedia::skipBackward(int seconds) {
284 if (! canPosition) return -1;
285 logger->log("PlayerMedia",Log::DEBUG,"skip back %ds",seconds);
287 ULLONG bps=getBytesPerSecond();
288 if (streampos > (ULLONG)seconds*bps)
289 requestedStreampos=streampos- (ULLONG)seconds*bps;
291 requestedStreampos=0;
292 requestState=S_POSITION;
295 logger->log("PlayerMedia",Log::DEBUG,"skip bw to %llu, %llu bps",requestedStreampos,bps);
301 // ----------------------------------- Internal functions
303 void PlayerMedia::gotoSeek() {
304 controlFeeder(FEEDER_STOP);
308 audio->setStreamType(Audio::MPEG2_PES);
311 controlFeeder(FEEDER_START);
315 audio->systemMuteOff();
319 onStartup=true; //search for Audio again
320 //TODO: should be done only at startup
325 void PlayerMedia::sendFrontendMessage(ULONG para)
327 logger->log("PlayerMedia", Log::DEBUG, "sending frontend message %ld",para);
328 Message* m = new Message();
333 m->message = Message::PLAYER_EVENT;
335 MessageQueue::getInstance()->postMessage(m);
338 //method called by the playing thread to handle
339 //"commands" by the frontend
340 UCHAR PlayerMedia::checkState()
343 UCHAR rstate=requestState;
345 int rseq=requestedSequence;
350 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u", cstate, rstate);
353 case S_PLAY: // to S_PLAY
355 if (cstate != S_SEEK && cstate != S_PAUSE) break;
357 if (cstate == S_PAUSE) {
363 controlFeeder(FEEDER_START);
371 case S_SEEK: //INITIAL_PLAY
377 case S_PAUSE: // to S_PAUSE
379 if (cstate == S_PAUSE) {
386 if (cstate != S_PLAY) {
394 case S_FF: // to S_FF
400 case S_BACK: // to S_BACK
406 case S_STOP: // to S_STOP
408 if (cstate == S_STOP) {
411 controlFeeder(FEEDER_STOP);
419 demuxer->setFrameNum(0);//trigger framecounting
422 case S_POSITION: // to S_JUMP
427 controlFeeder(FEEDER_STOP);
430 if (threadBuffer) free(threadBuffer);
433 ULLONG curPts=getCurrentPTS();
434 if (curPts != 0) bpts=streampos/curPts;
435 if (bpts != 0) appDestinationPTS=requestedStreampos/bpts;
436 else appDestinationPTS=0;
437 streampos=requestedStreampos;
438 bytesWritten=streampos;
439 Log::getInstance()->log("PlayerMedia",Log::DEBUG,"position to %llu",streampos);
445 Timers::getInstance()->setTimerD(this,1,15);
452 if (cstate != rstate && rstate != S_DONE ) {
453 sendFrontendMessage(STATUS_CHANGE);
454 //any change after done cancels the "done" timer
455 Timers::getInstance()->cancelTimer(this,1);
457 logger->log("PlayerMedia", Log::DEBUG, "Switch state from %u to %u completed", cstate, rstate);
458 //we return the newly set state
461 //rstate could be different but no new request - so return cstate
468 // ----------------------------------- Feed thread
470 void PlayerMedia::waitTimed(int ms) {
474 int us=1000*(ms - 1000*sec);
477 gettimeofday(&ct,NULL);
478 nt.tv_sec=ct.tv_sec+sec;
479 nt.tv_nsec=1000*us+1000*ct.tv_usec;
481 DWORD ct=timeGetTime();
482 nt.tv_sec=ct/1000+sec;
483 nt.tv_nsec=1000*us+1000*ct*1000;
485 threadWaitForSignalTimed(&nt);
489 void PlayerMedia::threadMethod()
491 logger->log("PlayerMedia", Log::DEBUG, "player thread started");
497 UCHAR cstate=checkState();
501 if (cstate != S_PLAY && cstate != S_FF && cstate != S_BACK && cstate != S_SEEK) {
507 if (thisWrite == thisRead) {
512 if (onStartup) rd=STARTBUFLEN;
513 int rt=MediaPlayer::getInstance()->getMediaBlock(mediaChannel,streampos,rd,&thisRead,&threadBuffer);
514 if ( thisRead == 0 && threadBuffer) {
518 if ((!threadBuffer || thisRead == 0) && rt == 0) {
520 if (retrycount > MAXTRY) rt=-1;
522 logger->log("PlayerMedia", Log::DEBUG, "no data - retrying");
528 if (!threadBuffer || thisRead == 0 || rt != 0) {
529 //OK we count this as end of stream
530 logger->log("PlayerMedia", Log::DEBUG, "stream end");
531 setRequestedState(S_DONE);
532 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
535 //logger->log("PlayerMedia", Log::DEBUG, "read %ld bytes at pos %ld",thisRead,streampos);
543 if (demuxer->getselAudioChannel() < Demuxer::PESTYPE_AUD0) {
544 int a_stream = demuxer->scan(threadBuffer, thisRead);
546 demuxer->setAudioStream(a_stream);
547 logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
550 logger->log("PlayerMedia", Log::DEBUG, "Startup Audio stream not choosen - leave to demux");
554 setRequestedState(S_PLAY); //OK - start playing even if we have no audio
559 memset(&p,sizeof(p),0);
564 audio->PrepareMediaSample(pl,0);
566 UINT rt=audio->DeliverMediaSample(threadBuffer,&bytesWritten);
567 ULONG written=thisRead;
569 written=bytesWritten;
571 ULONG written= demuxer->put(threadBuffer + thisWrite, thisRead - thisWrite);
573 bytesWritten+=written;
574 if (thisWrite < thisRead) {
576 //logger->log("PlayerMedia", Log::DEBUG, "demuxer full, waiting ");
577 // demuxer is full and can't take anymore
582 //logger->log("PlayerMedia", Log::DEBUG, "block written %d", thisWrite);
591 logger->log("PlayerMedia", Log::DEBUG, "finished");
592 MediaPlayer::getInstance()->closeMediaChannel(mediaChannel);
594 setRequestedState(S_STOP);
598 int PlayerMedia::waitForSequence(int timeout, int seq) {
599 time_t starttime=time(NULL)+timeout;
601 logger->log("PlayerMedia", Log::DEBUG, "waiting for sequence %d",seq);
602 while ((curtime=time(NULL)) < starttime) {
603 int cseq=getSequence();
604 if (cseq >= seq) return cseq;
610 int PlayerMedia::getSequence() {
620 void PlayerMedia::threadPostStopCleanup()
624 delete(threadBuffer);
631 void PlayerMedia::timercall(int ref) {
633 logger->log("PlayerMedia", Log::DEBUG, "stream end - informing frontend");
634 sendFrontendMessage(STREAM_END);
639 void PlayerMedia::call(void* caller)
641 if (caller == demuxer)
643 logger->log("PlayerMedia", Log::DEBUG, "Callback from demuxer");
645 if (demuxer->getHorizontalSize() > 0 && demuxer->getVerticalSize() > 0) {
646 int xpos=(video->getScreenWidth()-demuxer->getHorizontalSize())/2;
647 if (xpos <0 ) xpos=0;
648 int ypos=(video->getScreenHeight()-demuxer->getVerticalSize())/2;
649 if (ypos < 0) ypos=0;
650 logger->log("PlayerMedia", Log::DEBUG, "setting pos x=%d,y=%d",xpos,ypos);
651 //video->setPosition(xpos,ypos); // needs to be fixed
654 if (video->getTVsize() == Video::ASPECT4X3)
656 logger->log("PlayerMedia", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
661 int dxCurrentAspect = demuxer->getAspectRatio(&parx,&pary);
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,parx,pary);
667 Message* m = new Message();
670 m->message = Message::PLAYER_EVENT;
671 m->parameter = PlayerMedia::ASPECT43;
672 MessageQueue::getInstance()->postMessage(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,parx,pary);
679 Message* m = new Message();
682 m->message = Message::PLAYER_EVENT;
683 m->parameter = PlayerMedia::ASPECT169;
684 MessageQueue::getInstance()->postMessage(m);
688 logger->log("PlayerMedia", Log::DEBUG, "Demuxer said video is something else... switch anayway");
689 video->setAspectRatio(dxCurrentAspect,parx,pary);
695 if (getState() == S_SEEK)
697 logger->log("PlayerMedia", Log::DEBUG, "feeder started writing");
698 setRequestedState(S_PLAY);
701 threadSignalNoLock();
705 void PlayerMedia::setAudioChannel(int newChannel)
707 demuxer->setAudioStream(newChannel);
710 bool* PlayerMedia::getDemuxerMpegAudioChannels()
712 return demuxer->getmpAudioChannels();
715 bool* PlayerMedia::getDemuxerAc3AudioChannels()
717 return demuxer->getac3AudioChannels();
720 int PlayerMedia::getCurrentAudioChannel()
722 return demuxer->getselAudioChannel();
725 ULLONG PlayerMedia::getLengthBytes() {
728 ULLONG PlayerMedia::getLengthFrames() {
732 //PTS is always relative to first
733 ULLONG PlayerMedia::getCurrentPTS()
735 if (appDestinationPTS != 0) {
736 ULLONG lenPts=demuxer->getLenPTS();
737 if (appDestinationPTS < lenPts) return appDestinationPTS;
740 return demuxer->getCurrentPTS();
742 ULLONG PlayerMedia::getLenPTS()
744 return demuxer->getLenPTS();
747 ULLONG PlayerMedia::getBytesPerSecond() {
748 ULLONG currsec=getCurrentPTS()/90000;
749 if (currsec == 0) return 0;
750 return streampos/currsec;
753 char * PlayerMedia::getInfo() {
754 char * ibuf=new char[500];
755 int br=demuxer->getBitRate();
757 if (br == 0x3ffff ) {
758 //VBR - use estimated
759 sprintf(brbuf,"VBR, cur %llu kBit/s",getBytesPerSecond()*8/1000);
762 sprintf(brbuf,"CBR, %d kBit/s",br*4/10);
765 SNPRINTF(ibuf,500,"%s: %dx%d\n"
769 demuxer->getHorizontalSize(),
770 demuxer->getVerticalSize(),
772 demuxer->getAspectRatio(&parx,&pary)==2?"4:3":"16:9",