dvb.cpp: add demux policy for dm800/500hd
[enigma2.git] / lib / dvb / dvb.cpp
index 7e9a39c6da6544b2448d5ba27f7e825abdd988c2..a7428493967a6d9e79bea8911e86212adda670a9 100644 (file)
@@ -70,19 +70,19 @@ eDVBResourceManager::eDVBResourceManager()
 
        if (!instance)
                instance = this;
-               
+
                /* search available adapters... */
 
                // add linux devices
-       
+
        int num_adapter = 0;
        while (eDVBAdapterLinux::exist(num_adapter))
        {
                addAdapter(new eDVBAdapterLinux(num_adapter));
                num_adapter++;
        }
-       
-       eDebug("found %d adapter, %d frontends(%d sim) and %d demux", 
+
+       eDebug("found %d adapter, %d frontends(%d sim) and %d demux",
                m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size());
 
        eDVBCAService::registerChannelCallback(this);
@@ -104,7 +104,7 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
 {
                // scan frontends
        int num_fe = 0;
-       
+
        eDebug("scanning for frontends..");
        while (1)
        {
@@ -133,7 +133,7 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
                }
                ++num_fe;
        }
-       
+
                // scan demux
        int num_demux = 0;
        while (1)
@@ -148,10 +148,10 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
                if (stat(filename, &s))
                        break;
                ePtr<eDVBDemux> demux;
-               
+
                demux = new eDVBDemux(m_nr, num_demux);
                m_demux.push_back(demux);
-                       
+
                ++num_demux;
        }
 }
@@ -169,12 +169,12 @@ RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
                --nr;
                ++i;
        }
-       
+
        if (i != m_demux.end())
                demux = *i;
        else
                return -1;
-               
+
        return 0;
 }
 
@@ -191,12 +191,12 @@ RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr, bool simula
                --nr;
                ++i;
        }
-       
+
        if (i != m_frontend.end())
                fe = *i;
        else
                return -1;
-               
+
        return 0;
 }
 
@@ -224,9 +224,9 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
 {
        int num_fe = adapter->getNumFrontends();
        int num_demux = adapter->getNumDemux();
-       
+
        m_adapter.push_back(adapter);
-       
+
        int i;
        for (i=0; i<num_demux; ++i)
        {
@@ -426,10 +426,28 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
 
        if (i == m_demux.end())
                return -1;
-       
+
        ePtr<eDVBRegisteredDemux> unused;
+
+       if (m_demux.size() == 3) // dm800 / 500hd
+       {
+               for (; i != m_demux.end(); ++i, ++n)
+               {
+                       if (!i->m_inuse)
+                       {
+                               if (!unused)
+                                       unused = i;
+                       }
+                       else if (i->m_adapter == fe->m_adapter &&
+                           i->m_demux->getSource() == fe->m_frontend->getDVBID())
+                       {
+                               demux = new eDVBAllocatedDemux(i);
+                               return 0;
+                       }
+               }
        
-       if (m_demux.size() < 5)
+       }
+       else if (m_demux.size() < 5) // ATI
        {
                /* FIXME: hardware demux policy */
                if (!(cap & iDVBChannel::capDecode))
@@ -444,14 +462,14 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
                for (; i != m_demux.end(); ++i, ++n)
                {
                        int is_decode = n < 2;
-               
+
                        int in_use = is_decode ? (i->m_demux->getRefCount() != 2) : i->m_inuse;
-               
+
                        if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
                        {
                                if ((cap & iDVBChannel::capDecode) && !is_decode)
                                        continue;
-                               unused = i;     
+                               unused = i;
                                break;
                        }
                }
@@ -467,7 +485,7 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
                                        if (!unused)
                                                unused = i;
                                }
-                               else if (i->m_adapter == fe->m_adapter && 
+                               else if (i->m_adapter == fe->m_adapter &&
                                    i->m_demux->getSource() == fe->m_frontend->getDVBID())
                                {
                                        demux = new eDVBAllocatedDemux(i);
@@ -573,7 +591,7 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse
        }
 
        /* allocate a frontend. */
-       
+
        ePtr<eDVBAllocatedFrontend> fe;
 
        int err = allocateFrontend(fe, feparm, simulate);
@@ -974,7 +992,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
        }
 #endif
 
-#if 1 /* not yet */
+#if 0
        if (!m_iframe_search)
                return len;
 
@@ -990,16 +1008,16 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
                unsigned char *ts = data + ts_offset;
                int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF;
 
-               if ((d[3] == 0) && (m_pid == pid))  /* picture start */
+               if ((d[3] == 0 || d[3] == 0x09 && d[-1] == 0 && (ts[1] & 0x40)) && (m_pid == pid))  /* picture start */
                {
-                       int picture_type = (d[5] >> 3) & 7;
+                       int picture_type = (d[3]==0 ? (d[5] >> 3) & 7 : (d[4] >> 5) + 1);
                        d += 4;
 
 //                     eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid);
 
                        if (m_iframe_state == 1)
                        {
-                                       /* we are allowing data, and stop allowing data on the next frame. 
+                                       /* we are allowing data, and stop allowing data on the next frame.
                                           we now found a frame. so stop here. */
                                memset(data + offset, 0, 188 - (offset%188)); /* zero out rest of TS packet */
                                current_span_remaining = 0;
@@ -1026,26 +1044,30 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
 
                                        fts += 188;
                                }
-                                               /* force payload only */
-                               ts[3] &= ~0x30;
-                               ts[3] |=  0x10;
-
-//                             memset(ts + 4, 0xFF, (offset % 188) - 4);
 
                                m_iframe_state = 1;
                        }
                } else if ((d[3] & 0xF0) == 0xE0) /* video stream */
                {
-                       if (m_pid != pid)
+                               /* verify that this is actually a PES header, not just some ES data */
+                       if (ts[1] & 0x40) /* PUSI set */
                        {
-                               eDebug("now locked to pid %04x", pid);
-                               m_pid = pid;
+                               int payload_start = 4;
+                               if (ts[3] & 0x20) /* adaptation field present */
+                                       payload_start += ts[4] + 1; /* skip AF */
+                               if (payload_start == (offset%188)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */
+                               {
+                                       if (m_pid != pid)
+                                       {
+                                               eDebug("now locked to pid %04x (%02x %02x %02x %02x)", pid, ts[0], ts[1], ts[2], ts[3]);
+                                               m_pid = pid;
+                                       }
+                               }
                        }
 //                     m_pid = 0x6e;
                        d += 4;
                } else
                        d += 4; /* ignore */
-
        }
 
        if (m_iframe_state == 1)
@@ -1064,9 +1086,9 @@ eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *fronte
        m_frontend = frontend;
 
        m_pvr_thread = 0;
-       
-       m_skipmode_n = m_skipmode_m = 0;
-       
+
+       m_skipmode_n = m_skipmode_m = m_skipmode_frames = 0;
+
        if (m_frontend)
                m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
 }
@@ -1082,14 +1104,14 @@ eDVBChannel::~eDVBChannel()
 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
 {
        int state, ourstate = 0;
-       
+
                /* if we are already in shutdown, don't change state. */
        if (m_state == state_release)
                return;
-       
+
        if (fe->getState(state))
                return;
-       
+
        if (state == iDVBFrontend::stateLock)
        {
                eDebug("OURSTATE: ok");
@@ -1118,7 +1140,7 @@ void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
                ourstate = state_failed;
        } else
                eFatal("state unknown");
-       
+
        if (ourstate != m_state)
        {
                m_state = ourstate;
@@ -1166,6 +1188,8 @@ void eDVBChannel::cueSheetEvent(int event)
                                                /* 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 / 8;
+                               m_skipmode_frames = m_cue->m_skipmode_ratio / 90000;
+                               m_skipmode_frames_remainder = 0;
 
                                if (m_cue->m_skipmode_ratio < 0)
                                        m_skipmode_m -= m_skipmode_n;
@@ -1175,12 +1199,12 @@ void eDVBChannel::cueSheetEvent(int event)
                                if (abs(m_skipmode_m) < abs(m_skipmode_n))
                                {
                                        eWarning("something is wrong with this calculation");
-                                       m_skipmode_n = m_skipmode_m = 0;
+                                       m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
                                }
                        } else
                        {
                                eDebug("skipmode ratio is 0, normal play");
-                               m_skipmode_n = m_skipmode_m = 0;
+                               m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
                        }
                }
                m_pvr_thread->setIFrameSearch(m_skipmode_n != 0);
@@ -1200,7 +1224,7 @@ void eDVBChannel::cueSheetEvent(int event)
                {
                        off_t offset_in, offset_out;
                        pts_t pts_in = i->first, pts_out = i->second;
-                       if (m_tstools.getOffset(offset_in, pts_in) || m_tstools.getOffset(offset_out, pts_out))
+                       if (m_tstools.getOffset(offset_in, pts_in, -1) || m_tstools.getOffset(offset_out, pts_out, 1))
                        {
                                eDebug("span translation failed.\n");
                                continue;
@@ -1223,6 +1247,23 @@ static inline long long align(long long x, int align)
 
        x -= x % align;
 
+       if (sign)
+               x = -x;
+
+       return x;
+}
+
+       /* align toward zero */
+static inline long long align_with_len(long long x, int align, size_t &len)
+{
+       int sign = x < 0;
+
+       if (sign)
+               x = -x;
+
+       x -= x % align;
+       len += x % align;
+
        if (sign)
                x = -x;
 
@@ -1235,7 +1276,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
        const int blocksize = 188;
        unsigned int max = align(10*1024*1024, blocksize);
        current_offset = align(current_offset, blocksize);
-       
+
        if (!m_cue)
        {
                eDebug("no cue sheet. forcing normal play");
@@ -1256,13 +1297,55 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
 
        if (m_skipmode_n)
        {
-               eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n);
+               eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames);
                max = align(m_skipmode_n, blocksize);
        }
 
        eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
+       
+       int frame_skip_success = 0;
 
-       current_offset += align(m_skipmode_m, blocksize);
+       if (m_skipmode_m)
+       {
+               int frames_to_skip = m_skipmode_frames + m_skipmode_frames_remainder;
+               eDebug("we are at %llx, and we try to skip %d+%d frames from here", current_offset, m_skipmode_frames, m_skipmode_frames_remainder);
+               size_t iframe_len;
+               off_t iframe_start = current_offset;
+               int frames_skipped = frames_to_skip;
+               if (!m_tstools.findNextPicture(iframe_start, iframe_len, frames_skipped))
+               {
+                       m_skipmode_frames_remainder = frames_to_skip - frames_skipped;
+                       eDebug("successfully skipped %d (out of %d, rem now %d) frames.", frames_skipped, frames_to_skip, m_skipmode_frames_remainder);
+                       current_offset = align_with_len(iframe_start, blocksize, iframe_len);
+                       max = align(iframe_len + 187, blocksize);
+                       frame_skip_success = 1;
+               } else
+               {
+                       m_skipmode_frames_remainder = 0;
+                       eDebug("frame skipping failed, reverting to byte-skipping");
+               }
+       }
+       
+       if (!frame_skip_success)
+       {
+               current_offset += align(m_skipmode_m, blocksize);
+               
+               if (m_skipmode_m)
+               {
+                       eDebug("we are at %llx, and we try to find the iframe here:", current_offset);
+                       size_t iframe_len;
+                       off_t iframe_start = current_offset;
+                       
+                       int direction = (m_skipmode_m < 0) ? -1 : +1;
+                       if (m_tstools.findFrame(iframe_start, iframe_len, direction))
+                               eDebug("failed");
+                       else
+                       {
+                               current_offset = align_with_len(iframe_start, blocksize, iframe_len);
+                               max = align(iframe_len, blocksize);
+                       }
+               }
+       }
 
        while (!m_cue->m_seek_requests.empty())
        {
@@ -1306,7 +1389,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                                continue;
                        }
                }
-               
+
                if (relative == 1) /* pts relative */
                {
                        pts += now;
@@ -1317,7 +1400,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                if (relative != 2)
                        if (pts < 0)
                                pts = 0;
-               
+
                if (relative == 2) /* AP relative */
                {
                        eDebug("AP relative seeking: %lld, at %lld", pts, now);
@@ -1328,19 +1411,24 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                                eDebug("AP relative seeking failed!");
                        } else
                        {
-                               eDebug("next ap is %llx\n", pts);
                                pts = nextap;
+                               eDebug("next ap is %llx\n", pts);
                        }
                }
-               
+
                off_t offset = 0;
-               if (m_tstools.getOffset(offset, pts))
+               if (m_tstools.getOffset(offset, pts, -1))
                {
                        eDebug("get offset for pts=%lld failed!", pts);
                        continue;
                }
+               
+               size_t iframe_len;
+                       /* try to align to iframe */
+               int direction = pts < 0 ? -1 : 1;
+               m_tstools.findFrame(offset, iframe_len, direction);
 
-               eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
+               eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx (skipped additional %d frames due to iframe re-align)", relative, pts, offset, direction);
                current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
        }
 
@@ -1350,7 +1438,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
        {
                long long aligned_start = align(i->first, blocksize);
                long long aligned_end = align(i->second, blocksize);
-       
+
                if ((current_offset >= aligned_start) && (current_offset < aligned_end))
                {
                        start = current_offset;
@@ -1415,10 +1503,16 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                m_pvr_thread->sendEvent(eFilePushThread::evtUser);
        }
 
-       start = current_offset;
-       size = max;
-
-       eDebug("END OF CUESHEET. (%08llx, %d)", start, size);
+       if (m_source_span.empty())
+       {
+               start = current_offset;
+               size = max;
+               eDebug("NO CUESHEET. (%08llx, %d)", start, size);
+       } else
+       {
+               start = current_offset;
+               size = 0;
+       }
        return;
 }
 
@@ -1449,7 +1543,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
 {
        if (m_channel_id)
                m_mgr->removeChannel(this);
-               
+
        if (!channelid)
                return 0;
 
@@ -1458,7 +1552,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
                eDebug("no frontend to tune!");
                return -ENODEV;
        }
-       
+
        m_channel_id = channelid;
        m_mgr->addChannel(channelid, this);
        m_state = state_tuning;
@@ -1466,14 +1560,14 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
        int res;
        res = m_frontend->get().tune(*feparm);
        m_current_frontend_parameters = feparm;
-       
+
        if (res)
        {
                m_state = state_release;
                m_stateChanged(this);
                return res;
        }
-       
+
        return 0;
 }
 
@@ -1502,7 +1596,7 @@ RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
 
 void eDVBChannel::SDTready(int result)
 {
-       ePyObject args = PyTuple_New(2);
+       ePyObject args = PyTuple_New(2), ret;
        bool ok=false;
        if (!result)
        {
@@ -1521,7 +1615,9 @@ void eDVBChannel::SDTready(int result)
                Py_INCREF(Py_None);
                Py_INCREF(Py_None);
        }
-       PyObject_CallObject(m_tsid_onid_callback, args);
+       ret = PyObject_CallObject(m_tsid_onid_callback, args);
+       if (ret)
+               Py_DECREF(ret);
        Py_DECREF(args);
        Py_DECREF(m_tsid_onid_callback);
        m_tsid_onid_callback = ePyObject();
@@ -1556,21 +1652,21 @@ RESULT eDVBChannel::requestTsidOnid(ePyObject callback)
 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
 {
        ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
-       
+
        if (!our_demux)
        {
                demux = 0;
-               
+
                if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
                        return -1;
        }
-       
+
        demux = *our_demux;
                /* don't hold a reference to the decoding demux, we don't need it. */
-               
+
                /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
-                  the refcount is lost. thus, decoding demuxes are never allocated. 
-                  
+                  the refcount is lost. thus, decoding demuxes are never allocated.
+
                   this poses a big problem for PiP. */
        if (cap & capDecode)
                our_demux = 0;
@@ -1603,12 +1699,12 @@ RESULT eDVBChannel::playFile(const char *file)
                delete m_pvr_thread;
                m_pvr_thread = 0;
        }
-       
+
        m_tstools.openFile(file);
-       
+
                /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
                   THEN DO A REAL FIX HERE! */
-       
+
                /* (this codepath needs to be improved anyway.) */
 #if HAVE_DVB_API_VERSION < 3
        m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
@@ -1669,11 +1765,11 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
 {
        if (!decoding_demux)
                return -1;
-       
+
        pts_t now;
-       
+
        int r;
-       
+
        if (mode == 0) /* demux */
        {
                r = decoding_demux->getSTC(now, 0);
@@ -1684,7 +1780,7 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
                }
        } else
                now = pos; /* fixup supplied */
-       
+
        off_t off = 0; /* TODO: fixme */
        r = m_tstools.fixupPTS(off, now);
        if (r)
@@ -1692,9 +1788,9 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
                eDebug("fixup PTS failed");
                return -1;
        }
-       
+
        pos = now;
-       
+
        return 0;
 }
 
@@ -1705,7 +1801,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
                           a.) the filepush's internal buffer
                           b.) the PVR buffer (before demux)
                           c.) the ratebuffer (after demux)
-                          
+
                           it's important to clear them in the correct order, otherwise
                           the ratebuffer (for example) would immediately refill from
                           the not-yet-flushed PVR buffer.
@@ -1716,7 +1812,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
        m_pvr_thread->flush();
                /* HACK: flush PVR buffer */
        ::ioctl(m_pvr_fd_dst, 0);
-       
+
                /* flush ratebuffers (video, audio) */
        if (decoding_demux)
                decoding_demux->flush();
@@ -1740,7 +1836,7 @@ void eCueSheet::seekTo(int relative, const pts_t &pts)
        m_lock.Unlock();
        m_event(evtSeek);
 }
-       
+
 void eCueSheet::clear()
 {
        m_lock.WrLock();
@@ -1750,7 +1846,7 @@ void eCueSheet::clear()
 
 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
 {
-       assert(begin < end);
+       ASSERT(begin < end);
        m_lock.WrLock();
        m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
        m_lock.Unlock();