lib/base/console.cpp: fix eConsoleAppContainer write (when called with one string...
[enigma2.git] / lib / dvb / dvb.cpp
index 87e763056dd8e3c9e33f1b93ab9dd00d71fca5c1..17712dde154bb39d5beed4096db20dc79474fa8e 100644 (file)
@@ -82,8 +82,32 @@ eDVBResourceManager::eDVBResourceManager()
                num_adapter++;
        }
 
-       eDebug("found %d adapter, %d frontends(%d sim) and %d demux",
-               m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size());
+       int fd = open("/proc/stb/info/model", O_RDONLY);
+       char tmp[255];
+       int rd = fd >= 0 ? read(fd, tmp, 255) : 0;
+       if (fd >= 0)
+               close(fd);
+
+       if (!strncmp(tmp, "dm7025\n", rd))
+               m_boxtype = DM7025;
+       else if (!strncmp(tmp, "dm8000\n", rd))
+               m_boxtype = DM8000;
+       else if (!strncmp(tmp, "dm800\n", rd))
+               m_boxtype = DM800;
+       else if (!strncmp(tmp, "dm500hd\n", rd))
+               m_boxtype = DM500HD;
+       else {
+               eDebug("boxtype detection via /proc/stb/info not possible... use fallback via demux count!\n");
+               if (m_demux.size() == 3)
+                       m_boxtype = DM800;
+               else if (m_demux.size() < 5)
+                       m_boxtype = DM7025;
+               else
+                       m_boxtype = DM8000;
+       }
+
+       eDebug("found %d adapter, %d frontends(%d sim) and %d demux, boxtype %d",
+               m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size(), m_boxtype);
 
        eDVBCAService::registerChannelCallback(this);
 
@@ -414,7 +438,9 @@ alloc_fe_by_id_not_possible:
        return err;
 }
 
-RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int cap)
+#define capHoldDecodeReference 64
+
+RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int &cap)
 {
                /* find first unused demux which is on same adapter as frontend (or any, if PVR)
                   never use the first one unless we need a decoding demux. */
@@ -429,7 +455,36 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
 
        ePtr<eDVBRegisteredDemux> unused;
 
-       if (m_demux.size() < 5)
+       if (m_boxtype == DM800 || m_boxtype == DM500HD) // dm800 / 500hd
+       {
+               cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
+               for (; i != m_demux.end(); ++i, ++n)
+               {
+                       if (!i->m_inuse)
+                       {
+                               if (!unused)
+                                       unused = i;
+                       }
+                       else
+                       {
+                               if (fe)
+                               {
+                                       if (i->m_adapter == fe->m_adapter && 
+                                           i->m_demux->getSource() == fe->m_frontend->getDVBID())
+                                       {
+                                               demux = new eDVBAllocatedDemux(i);
+                                               return 0;
+                                       }
+                               }
+                               else if (i->m_demux->getSource() == -1) // PVR
+                               {
+                                       demux = new eDVBAllocatedDemux(i);
+                                       return 0;
+                               }
+                       }
+               }
+       }
+       else if (m_boxtype == DM7025) // ATI
        {
                /* FIXME: hardware demux policy */
                if (!(cap & iDVBChannel::capDecode))
@@ -456,8 +511,9 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
                        }
                }
        }
-       else // we asume dm8000
+       else if (m_boxtype == DM8000)
        {
+               cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
                for (; i != m_demux.end(); ++i, ++n)
                {
                        if (fe)
@@ -974,7 +1030,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,9 +1046,9 @@ 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);
@@ -1031,16 +1087,25 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
                        }
                } 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)
@@ -1059,8 +1124,9 @@ eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *fronte
        m_frontend = frontend;
 
        m_pvr_thread = 0;
+       m_pvr_fd_dst = -1;
 
-       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);
@@ -1161,6 +1227,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;
@@ -1170,12 +1238,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);
@@ -1195,7 +1263,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;
@@ -1218,6 +1286,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;
 
@@ -1239,25 +1324,58 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                return;
        }
 
-       m_cue->m_lock.RdLock();
-       if (!m_cue->m_decoding_demux)
-       {
-               start = current_offset;
-               size = max;
-               eDebug("getNextSourceSpan, no decoding demux. forcing normal play");
-               m_cue->m_lock.Unlock();
-               return;
-       }
-
        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);
+                       }
+               }
+       }
+
+       m_cue->m_lock.RdLock();
 
        while (!m_cue->m_seek_requests.empty())
        {
@@ -1283,6 +1401,13 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                                eDebug("decoder getPTS failed, can't seek relative");
                                continue;
                        }
+                       if (!m_cue->m_decoding_demux)
+                       {
+                               eDebug("getNextSourceSpan, no decoding demux. couldn't seek to %llx... ignore request!", pts);
+                               start = current_offset;
+                               size = max;
+                               continue;
+                       }
                        if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
                        {
                                eDebug("seekTo: getCurrentPosition failed!");
@@ -1323,18 +1448,18 @@ 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;
                }
-
+               
                eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
                current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
        }
@@ -1410,10 +1535,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;
 }
 
@@ -1560,17 +1691,24 @@ RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
 
                if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
                        return -1;
-       }
 
-       demux = *our_demux;
+               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.
 
                   this poses a big problem for PiP. */
-       if (cap & capDecode)
-               our_demux = 0;
+
+               if (cap & capHoldDecodeReference) // this is set in eDVBResourceManager::allocateDemux for Dm500HD/DM800 and DM8000
+                       ;
+               else if (cap & capDecode)
+                       our_demux = 0;
+       }
+       else
+               demux = *our_demux;
+
        return 0;
 }
 
@@ -1606,16 +1744,19 @@ RESULT eDVBChannel::playFile(const char *file)
                /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
                   THEN DO A REAL FIX HERE! */
 
+       if (m_pvr_fd_dst < 0)
+       {
                /* (this codepath needs to be improved anyway.) */
 #if HAVE_DVB_API_VERSION < 3
-       m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
+               m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
 #else
-       m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
+               m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
 #endif
-       if (m_pvr_fd_dst < 0)
-       {
-               eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
-               return -ENODEV;
+               if (m_pvr_fd_dst < 0)
+               {
+                       eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
+                       return -ENODEV;
+               }
        }
 
        m_pvr_thread = new eDVBChannelFilePush();
@@ -1623,10 +1764,14 @@ RESULT eDVBChannel::playFile(const char *file)
        m_pvr_thread->setStreamMode(1);
        m_pvr_thread->setScatterGather(this);
 
+       m_event(this, evtPreStart);
+
        if (m_pvr_thread->start(file, m_pvr_fd_dst))
        {
                delete m_pvr_thread;
                m_pvr_thread = 0;
+               ::close(m_pvr_fd_dst);
+               m_pvr_fd_dst = -1;
                eDebug("can't open PVR file %s (%m)", file);
                return -ENOENT;
        }
@@ -1643,10 +1788,11 @@ void eDVBChannel::stopFile()
        if (m_pvr_thread)
        {
                m_pvr_thread->stop();
-               ::close(m_pvr_fd_dst);
                delete m_pvr_thread;
                m_pvr_thread = 0;
        }
+       if (m_pvr_fd_dst >= 0)
+               ::close(m_pvr_fd_dst);
 }
 
 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
@@ -1747,7 +1893,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();