From: Chris Tallon Date: Tue, 23 Dec 2008 17:25:46 +0000 (+0000) Subject: Vogel Media Player 2008-11-28 X-Git-Url: https://git.vomp.tv/gitweb/?a=commitdiff_plain;h=1bd6e826db76d298b79ff644c2042d6e8d6acb35;p=vompclient-marten.git Vogel Media Player 2008-11-28 --- diff --git a/audioplayer.cc b/audioplayer.cc index 85a8a7b..a982437 100644 --- a/audioplayer.cc +++ b/audioplayer.cc @@ -19,13 +19,18 @@ */ #include "audioplayer.h" -#include "vaudioplayer.h" #include "demuxeraudio.h" #include "timers.h" #include "video.h" #include "command.h" #include "i18n.h" #include "boxx.h" +#include "log.h" +#include "media.h" +#include "mediaplayer.h" + +//how often do we retry if there is no data in stream +#define MAXTRY 50 AudioPlayer * AudioPlayer::instance=NULL; @@ -66,7 +71,7 @@ AudioPlayer::AudioPlayer(Boxx *parent) : afeed(this) sequence=0; playSequence=0; currentPlaySequence=-1; - filename=NULL; + uri=NULL; demuxer=new DemuxerAudio(); logger->log("AudioPlayer", Log::DEBUG, "Audio player ctorII"); if (!demuxer->init(this, audio, NULL, 0, DemuxerAudio::PACKET_SIZE+200)) @@ -80,6 +85,7 @@ AudioPlayer::AudioPlayer(Boxx *parent) : afeed(this) audio->reset(); lenInBytes=0; logger->log("AudioPlayer", Log::DEBUG, "Audio player created"); + canPosition=false; } AudioPlayer::~AudioPlayer() @@ -91,7 +97,7 @@ AudioPlayer::~AudioPlayer() audio->setStreamType(Audio::MPEG2_PES); delete demuxer; demuxer=NULL; - delete filename; + delete uri; } void AudioPlayer::controlFeeder(int feederAction) { @@ -171,14 +177,13 @@ void AudioPlayer::setState(UCHAR s) { } //------------------- externally called functions -------------------------- -int AudioPlayer::play(const char * fn) +int AudioPlayer::play(const MediaURI * u) { - logger->log("AudioPlayer", Log::DEBUG, "play request for %s", fn); + logger->log("AudioPlayer", Log::DEBUG, "play request for %s", u->getName()); int rt=0; threadLock(); - if (filename) delete filename; - filename=new char[strlen(fn)+1]; - strcpy(filename,fn); + if (uri) delete uri; + uri=new MediaURI(u); requestState=S_PLAY; requestedSequence++; rt=requestedSequence; @@ -209,17 +214,20 @@ int AudioPlayer::unpause() } int AudioPlayer::fastForward(){ + if (! canPosition) return 1; int rt=setRequestedState(S_FF); threadSignalNoLock(); return rt; } int AudioPlayer::fastBackward(){ + if (! canPosition) return 1; int rt=setRequestedState(S_BACK); threadSignalNoLock(); return rt; } int AudioPlayer::jumpToPercent(double percent){ + if (! canPosition) return 1; threadLock(); ULONG fsec=demuxer->getSecondsFromLen(lenInBytes); ULONG npos=streampos; @@ -243,6 +251,7 @@ int AudioPlayer::jumpToPercent(double percent){ } int AudioPlayer::skipForward(int seconds) { + if (! canPosition) return 1; threadLock(); ULONG curr=demuxer->getSecondsFromLen(streampos); ULONG dest=demuxer->positionFromSeconds(curr+(UINT)seconds); @@ -256,6 +265,7 @@ int AudioPlayer::skipForward(int seconds) { return 0; } int AudioPlayer::skipBackward(int seconds) { + if (! canPosition) return 1; threadLock(); ULONG curr=demuxer->getSecondsFromLen(streampos); if (curr > (UINT)seconds) { @@ -289,39 +299,35 @@ void AudioPlayer::sendFrontendMessage(ULONG para) Command::getInstance()->postMessageFromOuterSpace(m); } -void AudioPlayer::handleVDRerror(){ - if (!vdr->isConnected()) - { - logger->log("AudioPlayer", Log::ERR, "disconnect"); - sendFrontendMessage(CONNECTION_LOST); - setState(S_ERROR); - } -} //open a new file //called within the thread! int AudioPlayer::openFile() { - char * fn=NULL; threadLock(); - if (filename) { - fn=new char[strlen(filename)+1]; - strcpy(fn,filename); - } + MediaURI fn(uri); threadUnlock(); demuxer->reset(); streampos=0; bytesWritten=0; - lenInBytes=vdr->loadImage(fn,0,0); - Log::getInstance()->log("Audioplayer", Log::DEBUG, "request file rt=%d file=%s",lenInBytes,fn); - handleVDRerror(); - if (lenInBytes <= 0) { + MediaPlayer::getInstance()->closeMediaChannel(2); + int rt=MediaPlayer::getInstance()->openMedium(2,&fn,&lenInBytes,0,0); + Log::getInstance()->log("Audioplayer", Log::DEBUG, "request file rt=%d file=%s",rt,fn.getDisplayName()); + if (rt != 0) { return 1; } - UINT rsize=0; - UCHAR *idbuf=vdr->getImageBlock(0,demuxer->headerBytes(),&rsize); - handleVDRerror(); - if (rsize < demuxer->headerBytes() || idbuf == NULL) { + MediaInfo mi; + rt=MediaPlayer::getInstance()->getMediaInfo(2,&mi); + canPosition=mi.canPosition; + ULONG rsize=0; + UCHAR *idbuf=NULL; + if (canPosition) { + rt=MediaPlayer::getInstance()->getMediaBlock(2,0,demuxer->headerBytes(),&rsize,&idbuf); + } + else { + rt=-1; + } + if (rsize < demuxer->headerBytes() || rt != 0 ) { if (idbuf) free(idbuf); - Log::getInstance()->log("VAudioplayer", Log::DEBUG, "unable to get header for file %s",fn); + Log::getInstance()->log("Audioplayer", Log::DEBUG, "unable to get header for file %s",fn.getName()); return 0; } threadLock(); @@ -332,20 +338,19 @@ int AudioPlayer::openFile() { } if (idbuf) free(idbuf); idbuf=NULL; - if (demuxer->getId3Tag() == NULL) { + if (demuxer->getId3Tag() == NULL && canPosition) { //OK - look at the end - idbuf=vdr->getImageBlock(lenInBytes-demuxer->footerBytes(),demuxer->footerBytes(),&rsize); - handleVDRerror(); - if (rsize < demuxer->footerBytes() || idbuf == NULL) { + rt=MediaPlayer::getInstance()->getMediaBlock(2,lenInBytes-demuxer->footerBytes(),demuxer->footerBytes(),&rsize,&idbuf); + if (rsize < demuxer->footerBytes() || rt != 0) { if (idbuf) free(idbuf); - Log::getInstance()->log("VAudioplayer", Log::DEBUG, "unable to get footer for file %s",fn); + Log::getInstance()->log("Audioplayer", Log::DEBUG, "unable to get footer for file %s",fn.getName()); return 0; } threadLock(); hdrpos=demuxer->checkID3(idbuf,rsize); threadUnlock(); if (hdrpos < 0) { - Log::getInstance()->log("VAudioplayer", Log::DEBUG, "no ID3 in footer for file %s",fn); + Log::getInstance()->log("Audioplayer", Log::DEBUG, "no ID3 in footer for file %s",fn.getName()); } free(idbuf); } @@ -371,14 +376,21 @@ UCHAR AudioPlayer::checkState() switch(rstate) { case S_PAUSE: - if (cstate != S_PLAY && cstate != S_FF) rstate=cstate; //ignore request + if (cstate != S_PLAY && cstate != S_FF && cstate != S_PAUSE) { + rstate=cstate; //ignore request + break; + } else { - skipfactor=0; - demuxer->setSkipFactor(0); - audio->mute(); - audio->pause(); + if (cstate != S_PAUSE) { + skipfactor=0; + demuxer->setSkipFactor(0); + audio->mute(); + audio->pause(); + break; + } } - break; + //if cstate==S_PAUSE fallthrough to S_PLAY + rstate=S_PLAY; case S_PLAY: // to S_PLAY skipfactor=0; demuxer->setSkipFactor(0); @@ -492,7 +504,7 @@ UCHAR AudioPlayer::checkState() //any change after done cancels the "done" timer Timers::getInstance()->cancelTimer(this,1); } - logger->log("AudioPlayer", Log::DEBUG, "Switch state from %u to %u completed nf=%s", cstate, rstate,newFile?"true":"false"); + logger->log("AudioPlayer", Log::DEBUG, "Switch state from %u to %u completed seq=%d, nf=%s", cstate, rstate,rseq,newFile?"true":"false"); //we return the newly set state return rstate; } @@ -529,6 +541,7 @@ void AudioPlayer::threadMethod() logger->log("AudioPlayer", Log::DEBUG, "player thread started"); thisWrite=0; thisRead=0; + int retrycount=0; while(1) { UCHAR cstate=checkState(); @@ -545,15 +558,31 @@ void AudioPlayer::threadMethod() //TODO: use normal blocks... thisRead=0; thisWrite=0; - threadBuffer = vdr->getImageBlock(streampos, BUFLEN , &thisRead); - handleVDRerror(); - if (!threadBuffer || thisRead == 0) { + threadBuffer=NULL; + int rt=MediaPlayer::getInstance()->getMediaBlock(2,streampos,BUFLEN,&thisRead,&threadBuffer); + if (thisRead == 0 && threadBuffer) { + free(threadBuffer); + threadBuffer=NULL; + } + if ((!threadBuffer || thisRead == 0 ) && rt == 0) { + retrycount++; + if (retrycount > MAXTRY) rt=-1; + else { + logger->log("AudioPlayer", Log::DEBUG, "no data read, retrying"); + continue; + } + } + + if (!threadBuffer || thisRead == 0 || rt != 0) { //OK we count this as end of stream //hmm --- we should be able to detect if the audio has gone... logger->log("AudioPlayer", Log::DEBUG, "stream end"); setRequestedState(S_DONE); + MediaPlayer::getInstance()->closeMediaChannel(2); + retrycount=0; continue; } + retrycount=0; //logger->log("AudioPlayer", Log::DEBUG, "read %ld bytes at pos %ld",thisRead,streampos); streampos+=thisRead; } @@ -593,6 +622,7 @@ void AudioPlayer::threadMethod() } logger->log("AudioPlayer", Log::DEBUG, "finished"); + MediaPlayer::getInstance()->closeMediaChannel(2); playerRunnig=false; return; } @@ -601,12 +631,18 @@ int AudioPlayer::waitForSequence(int timeout, int seq) { time_t starttime=time(NULL)+timeout; time_t curtime=0; logger->log("AudioPlayer", Log::DEBUG, "waiting for sequence %d",seq); + int rt=-1; while ((curtime=time(NULL)) < starttime) { int cseq=getSequence(); - if (cseq >= seq) return cseq; - MILLISLEEP(10); + if (cseq >= seq) { + rt=cseq; + break; + } + //logger->log("AudioPlayer", Log::DEBUG, "waiting for sequence loop"); + MILLISLEEP(100); } - return -1; + logger->log("AudioPlayer", Log::DEBUG, "waiting for sequence %d returns %d",seq,rt); + return rt; } int AudioPlayer::getSequence() { @@ -677,17 +713,19 @@ char * AudioPlayer::getID3Info() { if (info && info->avrBitrate != info->bitRate) bitrateType='V'; rt=new char[len]; if (!tag && info) { - SNPRINTF(rt,len-1,"%s: %s/L%d %cBR,SR=%dk,%s\n",tr("MpegInfo"), - info->mpegVersion,info->mpegLayer,bitrateType,info->sampleRate/1000, + int sr=info->sampleRate/1000; + SNPRINTF(rt,len-1,"%s: %s/L%d %cBR,SR=%d.%02dkBit/s,%s\n",tr("MpegInfo"), + info->mpegVersion,info->mpegLayer,bitrateType,sr,(info->sampleRate/10-sr*100), info->info); } else if (tag && info){ + int sr=info->sampleRate/1000; char *tmp=new char[taglen+1]; SNPRINTF(rt,len-1,"%s\n" - "%s: %s/L%d %cBR,SR=%dk,%s\n", + "%s: %s/L%d %cBR,SR=%d.%02dkBit/s,%s\n", tag->toString(tmp,taglen,false), tr("MpegInfo"), - info->mpegVersion,info->mpegLayer,bitrateType,info->sampleRate/1000, + info->mpegVersion,info->mpegLayer,bitrateType,sr,(info->sampleRate/10-sr*100), info->info); delete [] tmp; } diff --git a/audioplayer.h b/audioplayer.h index 43c4a31..86aa712 100644 --- a/audioplayer.h +++ b/audioplayer.h @@ -48,6 +48,7 @@ class Boxx; class DemuxerAudio; +class MediaURI; class AudioPlayer : public Thread_TYPE, public Callback, public TimerReceiver @@ -71,7 +72,7 @@ class AudioPlayer : public Thread_TYPE, public Callback, public TimerReceiver //each of the commands works as a request //only after getSequence returned the same sequence as those commands this is //handled by the player and getError is valid - int play(const char * filename); + int play(const MediaURI *uri); //stop the player without shutting it down int stop(); int pause(); @@ -126,6 +127,7 @@ class AudioPlayer : public Thread_TYPE, public Callback, public TimerReceiver const static ULONG STATUS_CHANGE=4; //some info has been changed const static ULONG NEW_SONG=5; //some info has been changed const static ULONG SHORT_UPDATE=6; //timer info update + const static ULONG EXTERN1=7; //for other users as parameter to player event virtual void timercall(int reference); @@ -163,17 +165,17 @@ class AudioPlayer : public Thread_TYPE, public Callback, public TimerReceiver UCHAR checkState(); //variables used by the thread - UINT thisWrite; - UINT thisRead; + ULONG thisWrite; + ULONG thisRead; bool running; UCHAR *threadBuffer; UCHAR state; UCHAR requestState; - ULONG streampos; - ULONG lenInBytes; - ULONG bytesWritten; - ULONG requestedStreampos; + ULLONG streampos; + ULLONG lenInBytes; + ULLONG bytesWritten; + ULLONG requestedStreampos; int skipfactor; //the buffer len in bytes @@ -187,10 +189,12 @@ class AudioPlayer : public Thread_TYPE, public Callback, public TimerReceiver int currentPlaySequence; //sequence that is changed for each new filename int playSequence; + //can we position? + bool canPosition; - char * filename; + + MediaURI *uri; int openFile(); - void handleVDRerror(); void sendFrontendMessage(ULONG para); diff --git a/boxx.cc b/boxx.cc index af6ce48..1a26213 100644 --- a/boxx.cc +++ b/boxx.cc @@ -390,3 +390,9 @@ int Boxx::charWidth(char c) if (parent) return parent->charWidth(c); else return surface->getCharWidth(c); } + +Surface * Boxx::getSurface() { + if (parent) return parent->getSurface(); + return surface; +} + diff --git a/boxx.h b/boxx.h index 439fa54..d5eb7c1 100644 --- a/boxx.h +++ b/boxx.h @@ -126,6 +126,8 @@ class Boxx friend class BoxStack; protected: + //get the surface this box is drawing to + Surface *getSurface(); Boxx* parent; Region area; vector children; diff --git a/demuxermedia.cc b/demuxermedia.cc new file mode 100644 index 0000000..777a96c --- /dev/null +++ b/demuxermedia.cc @@ -0,0 +1,348 @@ +/* + Copyright 2005-2007 Mark Calderbank + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "demuxermedia.h" +#include "callback.h" + +#include "video.h" +#include "log.h" + +#ifndef WIN32 +#include +#else +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#define PTS_JUMP_MARGIN 10000 +#define PTS_ALLOWANCE 90000 + +// TODO: PTS class to handle wrapping arithmetic & comparisons? +static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate shortest distance between + ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1; + if (ret > (1LL<<32)) ret = (1LL<<33) - ret; + return ret; +} + +DemuxerMedia::DemuxerMedia() +{ + frameCounting = false; + packetCounting = false; +} + +void DemuxerMedia::reset() +{ + frameCounting = false; + packetCounting = false; + pts_map.clear(); + Demuxer::reset(); + firstPTS=0; + lastPTS=0; + currentPTS=0; + last_horizontal_size=-1; + last_vertical_size=-1; +} + +void DemuxerMedia::flush() +{ + state = 0; + submitting = false; + Demuxer::flush(); +} + +int DemuxerMedia::scan(UCHAR *buf, int len) +{ + int ret = 0; + int astate=0; //hdrbyte + + while ((len + astate)> 3) + { + switch (astate) { + case 0: //1st hdr byte + if (*buf != 0) break; + astate++; + break; + case 1: //2nd hdrbyte + if (*buf != 0) { + astate=0; + break; + } + astate++; + break; + case 2: //3rd header byte + if (*buf != 1) { + astate=0; + break; + } + astate++; + break; + case 3: //4th header byte + if ((*buf PESTYPE_AUDMAX) && *buf != PESTYPE_PRIVATE_1) { + astate=0; + break; + } + if (*buf != PESTYPE_PRIVATE_1) { + //found audio + ret=*buf; + return ret; + } + //AC3 - TODO + astate=0; + default: + astate=0; + break; + } + buf++; + len--; + } + return ret; +} + +int DemuxerMedia::findPTS(UCHAR* buf, int len, ULLONG* dest) +{ + // nobody uses this + // No PTS found. + return 0; +} + +void DemuxerMedia::setFrameNum(ULONG frame) +{ + frameCounting = true; + frameNumber = frame; + Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame); +} + +void DemuxerMedia::setPacketNum(ULONG npacket) +{ + packetCounting = true; + packetNumber = npacket; + Log::getInstance()->log("Demuxer", Log::DEBUG, "setPacketNum %d", npacket); +} + +int DemuxerMedia::put(UCHAR* buf, int len) +{ + int ret = 0; // return number of bytes consumed + if (submitting) + { + if (submitPacket(packet) == 0) // Still full! + return ret; + else + submitting = false; + } + + if (state > 0) // We are half way through a PES packet. + { + if (len >= state) // The remainder of the packet is available. + { + packet.write(buf, state); + buf += state; len -= state; ret += state; + state = 0; + parseVDRPacketDetails(); + if (submitPacket(packet) == 0) // Stream is full + { + submitting = true; + return ret; + } + } + else // Write what we have, then exit. + { + packet.write(buf, len); + state -= len; + return len; + } + } + + while (len > 0) + { + switch (state) + { + case 0: + case -1: + if (*buf == 0x00) state--; else state = 0; + buf++; len--; ret++; + break; + case -2: + if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0; + buf++; len--; ret++; + break; + case -3: + if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) || + (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) || + (*buf == PESTYPE_PRIVATE_1)) + { + packet.init(*buf); + state--; + } + else if (*buf == 0x00) + state = -1; + else + state = 0; + buf++; len--; ret++; + break; + case -4: + packetLength = ((UINT)*buf) << 8; + state--; + buf++; len--; ret++; + break; + case -5: + packetLength += *buf; + state--; + buf++; len--; ret++; + break; + } + + if (state == -6) // Packet header complete + { + if (len >= packetLength) // The entire packet is available. + { + packet.write(buf, packetLength); + buf += packetLength; len -= packetLength; ret += packetLength; + state = 0; + parseVDRPacketDetails(); + if (submitPacket(packet) == 0) // Stream is full + { + submitting = true; + return ret; + } + } + else // Write what we have. + { + packet.write(buf, len); + state = packetLength - len; + ret += len; + len = 0; + } + } + } + return ret; +} + +ULONG DemuxerMedia::getPacketNum() +{ + return packetNumber; +} + +void DemuxerMedia::parseVDRPacketDetails() +{ + parsePacketDetails(packet); + + if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 && + packet.getPacketType() <= PESTYPE_AUDMAX) + { + packetNumber++; + } + + if (frameCounting && packet.findPictureHeader() && + packet.getPacketType() >= PESTYPE_VID0 && + packet.getPacketType() <= PESTYPE_VIDMAX) + { + ULLONG pts=packet.getPTS(); + if (packet.findSeqHeader() > 1 && pts != PESPacket::PTS_INVALID) + { + if (firstPTS == 0) firstPTS=pts; + currentPTS=pts; + + } + } + if (packet.getPacketType() >= PESTYPE_VID0 && + packet.getPacketType() <= PESTYPE_VIDMAX && + packet.findSeqHeader()) { + //check video size + if (horizontal_size != last_horizontal_size || + vertical_size != last_vertical_size) { + last_horizontal_size=horizontal_size; + last_vertical_size=vertical_size; + Log::getInstance()->log("Demux", Log::DEBUG,"signal size change, new x %d, y %d", + horizontal_size,vertical_size); + if (callback) callback->call(this); + } + } + +} + +//find a sequence header backward +//search for 00 00 01