From a6014454bb8627882386ced03f75628304078637 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Thu, 26 Jan 2006 13:30:49 +0000 Subject: [PATCH 1/1] add cue sheets --- lib/dvb/dvb.cpp | 261 ++++++++++++++++++++++++++++++++++++++++-------- lib/dvb/dvb.h | 23 +++-- lib/dvb/idvb.h | 48 +++++++-- lib/dvb/pmt.cpp | 47 +++++---- lib/dvb/pmt.h | 3 +- 5 files changed, 311 insertions(+), 71 deletions(-) diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 8af8bb0e..cac5a679 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -552,6 +552,8 @@ eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *fronte m_pvr_thread = 0; + m_skipmode_n = m_skipmode_m = 0; + if (m_frontend) m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged); } @@ -560,14 +562,8 @@ eDVBChannel::~eDVBChannel() { if (m_channel_id) m_mgr->removeChannel(this); - - if (m_pvr_thread) - { - m_pvr_thread->stop(); - ::close(m_pvr_fd_src); - ::close(m_pvr_fd_dst); - delete m_pvr_thread; - } + + stopFile(); } void eDVBChannel::frontendStateChanged(iDVBFrontend*fe) @@ -625,9 +621,161 @@ void eDVBChannel::pvrEvent(int event) eDebug("eDVBChannel: End of file!"); m_event(this, evtEOF); break; + case eFilePushThread::evtUser: /* start */ + eDebug("SOF"); + m_event(this, evtSOF); + break; } } +void eDVBChannel::cueSheetEvent(int event) +{ + /* we need proper locking here! */ + eDebug("CUE SHEET EVENT %d", event); + + switch (event) + { + case eCueSheet::evtSeek: + eDebug("seek."); + flushPVR(m_cue->m_decoding_demux); + break; + case eCueSheet::evtSkipmode: + { + m_cue->m_seek_requests.push_back(std::pair(1, 0)); /* resync */ + if (m_cue->m_skipmode_ratio) + { + int bitrate = m_tstools.calcBitrate(); /* in bits/s */ + eDebug("skipmode ratio is %lld:90000, bitrate is %d bit/s", m_cue->m_skipmode_ratio, bitrate); + /* i agree that this might look a bit like black magic. */ + m_skipmode_n = 512*1024; /* must be 1 iframe at least. */ + m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio; + + eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n); + + if (abs(m_skipmode_m) < abs(m_skipmode_n)) + { + eFatal("damn, something is wrong with this calculation"); + m_skipmode_n = m_skipmode_m = 0; + } + + } else + { + eDebug("skipmode ratio is 0, normal play"); + m_skipmode_n = m_skipmode_m = 0; + } + flushPVR(m_cue->m_decoding_demux); + break; + } + case eCueSheet::evtSpanChanged: + eDebug("source span translation not yet supported"); + // recheckCuesheetSpans(); + break; + } +} + + /* remember, this gets called from another thread. */ +void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size) +{ + unsigned int max = 10*1024*1024; + + if (!m_cue) + { + eDebug("no cue sheet. forcing normal play"); + start = current_offset; + size = max; + return; + } + + if (!m_cue->m_decoding_demux) + { + start = current_offset; + size = max; + eDebug("getNextSourceSpan, no decoding demux. forcing normal play"); + return; + } + + if (m_skipmode_n) + { + eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n); + max = m_skipmode_n; + } + + eDebug("getNextSourceSpan, current offset is %08llx!", current_offset); + + if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) + { + eDebug("reached SOF"); + m_skipmode_m = 0; + m_pvr_thread->sendEvent(eFilePushThread::evtUser); + } + + current_offset += m_skipmode_m; + + while (!m_cue->m_seek_requests.empty()) + { + std::pair seek = m_cue->m_seek_requests.front(); + m_cue->m_seek_requests.pop_front(); + int relative = seek.first; + pts_t pts = seek.second; + + int bitrate = m_tstools.calcBitrate(); /* in bits/s */ + + if (bitrate == -1) + continue; + + if (relative) + { + pts_t now; + /* we're using the decoder's timestamp here. this + won't work for radio (ouch). */ + if (getCurrentPosition(m_cue->m_decoding_demux, now, 1)) + { + eDebug("seekTo: getCurrentPosition failed!"); + continue; + } + pts += now; + } + + if (pts < 0) + pts = 0; + + off_t offset = (pts * (pts_t)bitrate) / 8ULL / 90000ULL; + + eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset); + current_offset = offset; + } + + for (std::list >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i) + { + if ((current_offset >= i->first) && (current_offset < i->second)) + { + start = current_offset; + size = i->second - current_offset; + if (size > max) + size = max; + eDebug("HIT, %lld < %lld < %lld", i->first, current_offset, i->second); + return; + } + if (current_offset < i->first) + { + start = i->first; + size = i->second - i->first; + if (size > max) + size = max; + eDebug("skip"); + return; + } + } + + start = current_offset; + size = max; + eDebug("END OF CUESHEET. (%08llx, %d)", start, size); + + if (size < 4096) + eFatal("blub"); + return; +} + void eDVBChannel::AddUse() { ++m_use_count; @@ -761,12 +909,34 @@ RESULT eDVBChannel::playFile(const char *file) m_pvr_thread = new eFilePushThread(); m_pvr_thread->enablePVRCommit(1); + m_pvr_thread->setScatterGather(this); + m_pvr_thread->start(m_pvr_fd_src, m_pvr_fd_dst); CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent); return 0; } +void eDVBChannel::stopFile() +{ + if (m_pvr_thread) + { + m_pvr_thread->stop(); + ::close(m_pvr_fd_src); + ::close(m_pvr_fd_dst); + delete m_pvr_thread; + m_pvr_thread = 0; + } +} + +void eDVBChannel::setCueSheet(eCueSheet *cuesheet) +{ + m_conn_cueSheetEvent = 0; + m_cue = cuesheet; + if (m_cue) + m_cue->connectEvent(slot(*this, &eDVBChannel::cueSheetEvent), m_conn_cueSheetEvent); +} + RESULT eDVBChannel::getLength(pts_t &len) { return m_tstools.calcLen(len); @@ -814,34 +984,7 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in return 0; } -RESULT eDVBChannel::seekTo(iDVBDemux *decoding_demux, int relative, pts_t &pts) -{ - int bitrate = m_tstools.calcBitrate(); /* in bits/s */ - - if (bitrate == -1) - return -1; - - if (relative) - { - pts_t now; - if (getCurrentPosition(decoding_demux, now, 0)) - { - eDebug("seekTo: getCurrentPosition failed!"); - return -1; - } - pts += now; - } - - if (pts < 0) - pts = 0; - - off_t offset = (pts * (pts_t)bitrate) / 8ULL / 90000ULL; - - seekToPosition(decoding_demux, offset); - return 0; -} - -RESULT eDVBChannel::seekToPosition(iDVBDemux *decoding_demux, const off_t &r) +void eDVBChannel::flushPVR(iDVBDemux *decoding_demux) { /* when seeking, we have to ensure that all buffers are flushed. there are basically 3 buffers: @@ -853,12 +996,10 @@ RESULT eDVBChannel::seekToPosition(iDVBDemux *decoding_demux, const off_t &r) the ratebuffer (for example) would immediately refill from the not-yet-flushed PVR buffer. */ - eDebug("eDVBChannel: seekToPosition .. %llx", r); - m_pvr_thread->pause(); + m_pvr_thread->pause(); /* flush internal filepush buffer */ m_pvr_thread->flush(); - /* HACK: flush PVR buffer */ ::ioctl(m_pvr_fd_dst, 0); @@ -867,7 +1008,47 @@ RESULT eDVBChannel::seekToPosition(iDVBDemux *decoding_demux, const off_t &r) decoding_demux->flush(); /* demux will also flush all decoder.. */ - m_pvr_thread->seek(SEEK_SET, r); + /* resume will re-query the SG */ m_pvr_thread->resume(); +} + +DEFINE_REF(eCueSheet); + +eCueSheet::eCueSheet() +{ + m_skipmode_ratio = 0; +} + +void eCueSheet::seekTo(int relative, const pts_t &pts) +{ + m_seek_requests.push_back(std::pair(relative, pts)); + m_event(evtSeek); +} + +void eCueSheet::clear() +{ + m_spans.clear(); +} + +void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end) +{ + m_spans.push_back(std::pair(begin, end)); + m_event(evtSpanChanged); +} + +void eCueSheet::setSkipmode(const pts_t &ratio) +{ + m_skipmode_ratio = ratio; + m_event(evtSkipmode); +} + +void eCueSheet::setDecodingDemux(iDVBDemux *demux) +{ + m_decoding_demux = demux; +} + +RESULT eCueSheet::connectEvent(const Slot1 &event, ePtr &connection) +{ + connection = new eConnection(this, m_event.connect(event)); return 0; } diff --git a/lib/dvb/dvb.h b/lib/dvb/dvb.h index 5fc2f043..0c652e35 100644 --- a/lib/dvb/dvb.h +++ b/lib/dvb/dvb.h @@ -2,6 +2,7 @@ #define __dvb_dvb_h #include +#include #include #include #include @@ -190,10 +191,8 @@ public: bool canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID &ignore); }; -class eFilePushThread; - /* iDVBPVRChannel includes iDVBChannel. don't panic. */ -class eDVBChannel: public iDVBPVRChannel, public Object +class eDVBChannel: public iDVBPVRChannel, public iFilePushScatterGather, public Object { DECLARE_REF(eDVBChannel); public: @@ -216,12 +215,12 @@ public: /* iDVBPVRChannel */ RESULT playFile(const char *file); + void stopFile(); + + void setCueSheet(eCueSheet *cuesheet); + RESULT getLength(pts_t &len); RESULT getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode); - RESULT seekTo(iDVBDemux *decoding_demux, int relative, pts_t &pts); - /* seeking to relative positions won't work - - there is an unknown amount of buffers in between */ - RESULT seekToPosition(iDVBDemux *decoding_demux, const off_t &off); int getUseCount() { return m_use_count; } private: @@ -247,6 +246,16 @@ private: int m_pvr_fd_src, m_pvr_fd_dst; eDVBTSTools m_tstools; + + ePtr m_cue; + + void cueSheetEvent(int event); + ePtr m_conn_cueSheetEvent; + int m_skipmode_m, m_skipmode_n; + + std::list > m_source_span; + void getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size); + void flushPVR(iDVBDemux *decoding_demux=0); friend class eUsePtr; /* use count */ diff --git a/lib/dvb/idvb.h b/lib/dvb/idvb.h index c7db9779..7dbad780 100644 --- a/lib/dvb/idvb.h +++ b/lib/dvb/idvb.h @@ -396,7 +396,7 @@ public: enum { - evtEOF, evtFailed + evtEOF, evtSOF, evtFailed }; virtual RESULT connectStateChange(const Slot1 &stateChange, ePtr &connection)=0; virtual RESULT connectEvent(const Slot2 &eventChange, ePtr &connection)=0; @@ -420,8 +420,41 @@ public: }; /* signed, so we can express deltas. */ + typedef long long pts_t; +class iFilePushScatterGather; + + /* note that a cue sheet describes the logical positions. thus + everything is specified in pts and not file positions */ + + /* implemented in dvb.cpp */ +class eCueSheet: public iObject, public Object +{ + DECLARE_REF(eCueSheet); +public: + eCueSheet(); + + /* frontend */ + void seekTo(int relative, const pts_t &pts); + + void clear(); + void addSourceSpan(const pts_t &begin, const pts_t &end); + + void setSkipmode(const pts_t &ratio); /* 90000 is 1:1 */ + void setDecodingDemux(iDVBDemux *demux); + + /* backend */ + enum { evtSeek, evtSkipmode, evtSpanChanged }; + RESULT connectEvent(const Slot1 &event, ePtr &connection); + + std::list > m_spans; /* begin, end */ + std::list > m_seek_requests; /* relative, delta */ + pts_t m_skipmode_ratio; + Signal1 m_event; + ePtr m_decoding_demux; +}; + class iDVBPVRChannel: public iDVBChannel { public: @@ -431,18 +464,21 @@ public: }; /* FIXME: there are some very ugly buffer-end and ... related problems */ - /* so this is VERY UGLY. */ + /* so this is VERY UGLY. + + ok, it's going to get better. but still...*/ virtual RESULT playFile(const char *file) = 0; + virtual void stopFile() = 0; + + virtual void setCueSheet(eCueSheet *cuesheet) = 0; virtual RESULT getLength(pts_t &pts) = 0; /* we explicitely ask for the decoding demux here because a channel can be shared between multiple decoders. - Of couse skipping doesn't make much sense - then, but getCurrentPosition does. */ + */ virtual RESULT getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode) = 0; - virtual RESULT seekTo(iDVBDemux *decoding_demux, int relative, pts_t &pts) = 0; - virtual RESULT seekToPosition(iDVBDemux *decoding_demux, const off_t &pts) = 0; + /* skipping must be done with a cue sheet */ }; class iDVBSectionReader; diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index f7767541..d1644e7a 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -22,22 +22,7 @@ eDVBServicePMTHandler::eDVBServicePMTHandler() eDVBServicePMTHandler::~eDVBServicePMTHandler() { - if (m_ca_servicePtr) - { - eDebug("unregister caservice"); - int demuxes[2] = {0,0}; - uint8_t tmp; - m_demux->getCADemuxID(tmp); - demuxes[0]=tmp; - if (m_decode_demux_num != 0xFF) - demuxes[1]=m_decode_demux_num; - else - demuxes[1]=demuxes[0]; - ePtr > ptr; - m_PMT.getCurrent(ptr); - eDVBCAService::unregister_service(m_reference, demuxes, ptr); - eDVBCIInterfaces::getInstance()->removePMTHandler(this); - } + free(); } void eDVBServicePMTHandler::channelStateChanged(iDVBChannel *channel) @@ -78,6 +63,9 @@ void eDVBServicePMTHandler::channelEvent(iDVBChannel *channel, int event) case iDVBChannel::evtEOF: serviceEvent(eventEOF); break; + case iDVBChannel::evtSOF: + serviceEvent(eventSOF); + break; default: break; } @@ -375,7 +363,7 @@ int eDVBServicePMTHandler::getPVRChannel(ePtr &pvr_channel) return -1; } -int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux) +int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *cue) { RESULT res; m_reference = ref; @@ -424,7 +412,10 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux) } if (m_pvr_channel) + { + m_pvr_channel->setCueSheet(cue); m_pvr_channel->playFile(ref.path.c_str()); + } ePtr db; if (!m_resourceManager->getChannelList(db)) @@ -435,6 +426,28 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux) void eDVBServicePMTHandler::free() { + if (m_ca_servicePtr) + { + int demuxes[2] = {0,0}; + uint8_t tmp; + m_demux->getCADemuxID(tmp); + demuxes[0]=tmp; + if (m_decode_demux_num != 0xFF) + demuxes[1]=m_decode_demux_num; + else + demuxes[1]=demuxes[0]; + ePtr > ptr; + m_PMT.getCurrent(ptr); + eDVBCAService::unregister_service(m_reference, demuxes, ptr); + eDVBCIInterfaces::getInstance()->removePMTHandler(this); + m_ca_servicePtr = 0; + } + + if (m_pvr_channel) + { + m_pvr_channel->stopFile(); + m_pvr_channel->setCueSheet(0); + } m_PMT.stop(); m_PAT.stop(); m_service = 0; diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index 2ad57026..b7115ae2 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -84,6 +84,7 @@ public: eventNewProgramInfo, // we just received a PMT eventTuned, // a channel was sucessfully (re-)tuned in, you may start additional filters now + eventSOF, // seek pre start eventEOF, // a file playback did end }; @@ -125,7 +126,7 @@ public: int getPMT(ePtr > &ptr) { return m_PMT.getCurrent(ptr); } int getChannel(eUsePtr &channel); - int tune(eServiceReferenceDVB &ref, int use_decode_demux); + int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0); void free(); }; -- 2.30.2