add cue sheets
authorFelix Domke <tmbinc@elitedvb.net>
Thu, 26 Jan 2006 13:30:49 +0000 (13:30 +0000)
committerFelix Domke <tmbinc@elitedvb.net>
Thu, 26 Jan 2006 13:30:49 +0000 (13:30 +0000)
lib/dvb/dvb.cpp
lib/dvb/dvb.h
lib/dvb/idvb.h
lib/dvb/pmt.cpp
lib/dvb/pmt.h

index 8af8bb0..cac5a67 100644 (file)
@@ -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<int, pts_t>(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<int, pts_t> 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<std::pair<off_t, off_t> >::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<int, pts_t>(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<pts_t, pts_t>(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<void,int> &event, ePtr<eConnection> &connection)
+{
+       connection = new eConnection(this, m_event.connect(event));
        return 0;
 }
index 5fc2f04..0c652e3 100644 (file)
@@ -2,6 +2,7 @@
 #define __dvb_dvb_h
 
 #include <lib/base/ebase.h>
+#include <lib/base/filepush.h>
 #include <lib/dvb/idvb.h>
 #include <lib/dvb/demux.h>
 #include <lib/dvb/frontend.h>
@@ -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<eCueSheet> m_cue;
+       
+       void cueSheetEvent(int event);
+       ePtr<eConnection> m_conn_cueSheetEvent;
+       int m_skipmode_m, m_skipmode_n;
+       
+       std::list<std::pair<off_t, off_t> > 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<eDVBChannel>;
                /* use count */
index c7db977..7dbad78 100644 (file)
@@ -396,7 +396,7 @@ public:
        
        enum 
        {
-               evtEOF, evtFailed
+               evtEOF, evtSOF, evtFailed
        };
        virtual RESULT connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)=0;
        virtual RESULT connectEvent(const Slot2<void,iDVBChannel*,int> &eventChange, ePtr<eConnection> &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<void, int> &event, ePtr<eConnection> &connection);
+
+       std::list<std::pair<pts_t,pts_t> > m_spans;     /* begin, end */
+       std::list<std::pair<int, pts_t> > m_seek_requests; /* relative, delta */
+       pts_t m_skipmode_ratio;
+       Signal1<void,int> m_event;
+       ePtr<iDVBDemux> 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;
index f776754..d1644e7 100644 (file)
@@ -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<eTable<ProgramMapSection> > 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<iDVBPVRChannel> &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<iDVBChannelList> 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<eTable<ProgramMapSection> > 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;
index 2ad5702..b7115ae 100644 (file)
@@ -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<eTable<ProgramMapSection> > &ptr) { return m_PMT.getCurrent(ptr); }
        int getChannel(eUsePtr<iDVBChannel> &channel);
 
-       int tune(eServiceReferenceDVB &ref, int use_decode_demux);
+       int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0);
        void free();
 };