Merge commit 'origin/bug_449_fix_wlan_usbstick_recognition'
[enigma2.git] / lib / dvb / pvrparse.cpp
index d6978301447dde39d1f1ab2516421fb42f88ce5b..5cdecbd610095ec2172b811454f0b71ec1013ed6 100644 (file)
@@ -6,9 +6,36 @@
 #error no byte order defined!
 #endif
 
-int eMPEGStreamInformation::save(const char *filename)
+eMPEGStreamInformation::eMPEGStreamInformation()
+       : m_structure_cache_valid(0), m_structure_read(0), m_structure_write(0)
 {
-       FILE *f = fopen(filename, "wb");
+}
+
+eMPEGStreamInformation::~eMPEGStreamInformation()
+{
+       if (m_structure_read)
+               fclose(m_structure_read);
+       if (m_structure_write)
+               fclose(m_structure_write);
+}
+
+int eMPEGStreamInformation::startSave(const char *filename)
+{
+       m_filename = filename;
+       m_structure_write = fopen((m_filename + ".sc").c_str(), "wb");
+       return 0;
+}
+
+int eMPEGStreamInformation::stopSave(void)
+{
+       if (m_structure_write)
+       {
+               fclose(m_structure_write);
+               m_structure_write = 0;
+       }
+       if (m_filename == "")
+               return -1;
+       FILE *f = fopen((m_filename + ".ap").c_str(), "wb");
        if (!f)
                return -1;
        
@@ -31,13 +58,15 @@ int eMPEGStreamInformation::save(const char *filename)
 
 int eMPEGStreamInformation::load(const char *filename)
 {
-       FILE *f = fopen(filename, "rb");
+       m_filename = filename;
+       if (m_structure_read)
+               fclose(m_structure_read);
+       m_structure_read = fopen((std::string(m_filename) + ".sc").c_str(), "rb");
+       FILE *f = fopen((std::string(m_filename) + ".ap").c_str(), "rb");
        if (!f)
                return -1;
        m_access_points.clear();
        m_pts_to_offset.clear();
-       pts_t last = -(1LL<<62);
-       int loaded = 0, skipped = 0;
        while (1)
        {
                unsigned long long d[2];
@@ -48,16 +77,9 @@ int eMPEGStreamInformation::load(const char *filename)
                d[0] = bswap_64(d[0]);
                d[1] = bswap_64(d[1]);
 #endif
-               if ((d[1] - last) > 90000/2)
-               {
-                       m_access_points[d[0]] = d[1];
-                       m_pts_to_offset.insert(std::pair<pts_t,off_t>(d[1], d[0]));
-                       last = d[1];
-                       loaded++;
-               } else
-                       skipped++;
+               m_access_points[d[0]] = d[1];
+               m_pts_to_offset.insert(std::pair<pts_t,off_t>(d[1], d[0]));
        }
-       eDebug("loaded %d, skipped %d", loaded, skipped);
        fclose(f);
        fixupDiscontinuties();
        return 0;
@@ -146,7 +168,7 @@ int eMPEGStreamInformation::fixupPTS(const off_t &offset, pts_t &ts)
        std::multimap<pts_t, off_t>::const_iterator 
                l = m_pts_to_offset.upper_bound(ts - 60 * 90000), 
                u = m_pts_to_offset.upper_bound(ts + 60 * 90000), 
-               nearest = m_access_points.end();
+               nearest = m_pts_to_offset.end();
 
        while (l != u)
        {
@@ -154,7 +176,7 @@ int eMPEGStreamInformation::fixupPTS(const off_t &offset, pts_t &ts)
                        nearest = l;
                ++l;
        }
-       if (nearest == m_access_points.end())
+       if (nearest == m_pts_to_offset.end())
                return 1;
 
        ts -= getDelta(nearest->second);
@@ -216,31 +238,46 @@ pts_t eMPEGStreamInformation::getInterpolated(off_t offset)
        return before_ts + diff;
 }
  
-off_t eMPEGStreamInformation::getAccessPoint(pts_t ts)
+off_t eMPEGStreamInformation::getAccessPoint(pts_t ts, int marg)
 {
                /* FIXME: more efficient implementation */
-       pts_t delta = 0;
        off_t last = 0;
+       off_t last2 = 0;
+       pts_t lastc = 0;
+       ts += 1; // Add rounding error margin
        for (std::map<off_t, pts_t>::const_iterator i(m_access_points.begin()); i != m_access_points.end(); ++i)
        {
                pts_t delta = getDelta(i->first);
                pts_t c = i->second - delta;
-               if (c > ts)
-                       break;
+               if (c > ts) {
+                       if (marg > 0)
+                               return (last + i->first)/376*188;
+                       else if (marg < 0)
+                               return (last + last2)/376*188;
+                       else
+                               return last;
+               }
+               lastc = c;
+               last2 = last;
                last = i->first;
        }
-       return last;
+       if (marg < 0)
+               return (last + last2)/376*188;
+       else
+               return last;
 }
 
 int eMPEGStreamInformation::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction)
 {
        off_t offset = getAccessPoint(start);
+       pts_t c1, c2;
        std::map<off_t, pts_t>::const_iterator i = m_access_points.find(offset);
        if (i == m_access_points.end())
        {
                eDebug("getNextAccessPoint: initial AP not found");
                return -1;
        }
+       c1 = i->second - getDelta(i->first);
        while (direction)
        {
                if (direction > 0)
@@ -248,6 +285,12 @@ int eMPEGStreamInformation::getNextAccessPoint(pts_t &ts, const pts_t &start, in
                        if (i == m_access_points.end())
                                return -1;
                        ++i;
+                       c2 = i->second - getDelta(i->first);
+                       if (c1 == c2) { // Discontinuity
+                               ++i;
+                               c2 = i->second - getDelta(i->first);
+                       }
+                       c1 = c2;
                        direction--;
                }
                if (direction < 0)
@@ -258,6 +301,12 @@ int eMPEGStreamInformation::getNextAccessPoint(pts_t &ts, const pts_t &start, in
                                return -1;
                        }
                        --i;
+                       c2 = i->second - getDelta(i->first);
+                       if (c1 == c2) { // Discontinuity
+                               --i;
+                               c2 = i->second - getDelta(i->first);
+                       }
+                       c1 = c2;
                        direction++;
                }
        }
@@ -267,7 +316,118 @@ int eMPEGStreamInformation::getNextAccessPoint(pts_t &ts, const pts_t &start, in
        return 0;
 }
 
-eMPEGStreamParserTS::eMPEGStreamParserTS(eMPEGStreamInformation &streaminfo): m_streaminfo(streaminfo), m_pktptr(0), m_pid(-1), m_need_next_packet(0), m_skip(0)
+void eMPEGStreamInformation::writeStructureEntry(off_t offset, structure_data data)
+{
+       unsigned long long d[2];
+#if BYTE_ORDER == BIG_ENDIAN
+       d[0] = offset;
+       d[1] = data;
+#else
+       d[0] = bswap_64(offset);
+       d[1] = bswap_64(data);
+#endif
+       if (m_structure_write)
+               fwrite(d, sizeof(d), 1, m_structure_write);
+}
+
+int eMPEGStreamInformation::getStructureEntry(off_t &offset, unsigned long long &data, int get_next)
+{
+       if (!m_structure_read)
+       {
+               eDebug("getStructureEntry failed because of no m_structure_read");
+               return -1;
+       }
+
+       const int struture_cache_entries = sizeof(m_structure_cache) / 16;
+       if ((!m_structure_cache_valid) || ((off_t)m_structure_cache[0] > offset) || ((off_t)m_structure_cache[(struture_cache_entries - (get_next ? 2 : 1)) * 2] <= offset))
+       {
+               fseek(m_structure_read, 0, SEEK_END);
+               int l = ftell(m_structure_read);
+               unsigned long long d[2];
+               const int entry_size = sizeof d;
+
+                       /* do a binary search */
+               int count = l / entry_size;
+               int i = 0;
+               
+               while (count)
+               {
+                       int step = count >> 1;
+                       
+                       fseek(m_structure_read, (i + step) * entry_size, SEEK_SET);
+                       if (!fread(d, 1, entry_size, m_structure_read))
+                       {
+                               eDebug("read error at entry %d", i);
+                               return -1;
+                       }
+                       
+#if BYTE_ORDER != BIG_ENDIAN
+                       d[0] = bswap_64(d[0]);
+                       d[1] = bswap_64(d[1]);
+#endif
+//                     eDebug("%d: %08llx > %llx", i, d[0], d[1]);
+                       
+                       if (d[0] < (unsigned long long)offset)
+                       {
+                               i += step + 1;
+                               count -= step + 1;
+                       } else
+                               count = step;
+               }
+               
+               eDebug("found %d", i);
+               
+                       /* put that in the middle */
+               i -= struture_cache_entries / 2;
+               if (i < 0)
+                       i = 0;
+               eDebug("cache starts at %d", i);
+               fseek(m_structure_read, i * entry_size, SEEK_SET);
+               int num = fread(m_structure_cache, entry_size, struture_cache_entries, m_structure_read);
+               eDebug("%d entries", num);
+               for (i = 0; i < struture_cache_entries; ++i)
+               {
+                       if (i < num)
+                       {
+#if BYTE_ORDER != BIG_ENDIAN
+                               m_structure_cache[i * 2] = bswap_64(m_structure_cache[i * 2]);
+                               m_structure_cache[i * 2 + 1] = bswap_64(m_structure_cache[i * 2 + 1]);
+#endif
+                       } else
+                       {
+                               m_structure_cache[i * 2] = 0x7fffffffffffffffULL; /* fill with harmless content */
+                               m_structure_cache[i * 2 + 1] = 0;
+                       }
+               }
+               m_structure_cache_valid = 1;
+       }
+       
+       int i = 0;
+       while ((off_t)m_structure_cache[i * 2] <= offset)
+       {
+               ++i;
+               if (i == struture_cache_entries)
+               {
+                       eDebug("structure data consistency fail!, we are looking for %llx, but last entry is %llx", offset, m_structure_cache[i*2-2]);
+                       return -1;
+               }
+       }
+       if (!i)
+       {
+               eDebug("structure data (first entry) consistency fail!");
+               return -1;
+       }
+       
+       if (!get_next)
+               --i;
+
+//     eDebug("[%d] looked for %llx, found %llx=%llx", sizeof offset, offset, m_structure_cache[i * 2], m_structure_cache[i * 2 + 1]);
+       offset = m_structure_cache[i * 2];
+       data = m_structure_cache[i * 2 + 1];
+       return 0;
+}
+
+eMPEGStreamParserTS::eMPEGStreamParserTS(eMPEGStreamInformation &streaminfo): m_streaminfo(streaminfo), m_pktptr(0), m_pid(-1), m_need_next_packet(0), m_skip(0), m_last_pts_valid(0)
 {
 }
 
@@ -276,16 +436,15 @@ int eMPEGStreamParserTS::processPacket(const unsigned char *pkt, off_t offset)
        if (!wantPacket(pkt))
                eWarning("something's wrong.");
 
-       const unsigned char *end = pkt + 188;
+       const unsigned char *end = pkt + 188, *begin = pkt;
        
-       if (!(pkt[3] & 0x10))
-       {
-               eWarning("[TSPARSE] PUSI set but no payload.");
-               return 0;
-       }
+       int pusi = !!(pkt[1] & 0x40);
        
-       if (pkt[3] & 0x20) // adaption field present?
-               pkt += pkt[4] + 4 + 1;  /* skip adaption field and header */
+       if (!(pkt[3] & 0x10)) /* no payload? */
+               return 0;
+
+       if (pkt[3] & 0x20) // adaptation field present?
+               pkt += pkt[4] + 4 + 1;  /* skip adaptation field and header */
        else
                pkt += 4; /* skip header */
 
@@ -294,74 +453,96 @@ int eMPEGStreamParserTS::processPacket(const unsigned char *pkt, off_t offset)
                eWarning("[TSPARSE] dropping huge adaption field");
                return 0;
        }
-       
-               // ok, we now have the start of the payload, aligned with the PES packet start.
-       if (pkt[0] || pkt[1] || (pkt[2] != 1))
-       {
-               eWarning("broken startcode");
-               return 0;
-       }
-       
-       
+
        pts_t pts = 0;
        int ptsvalid = 0;
        
-       if (pkt[7] & 0x80) // PTS present?
+       if (pusi)
        {
-               pts  = ((unsigned long long)(pkt[ 9]&0xE))  << 29;
-               pts |= ((unsigned long long)(pkt[10]&0xFF)) << 22;
-               pts |= ((unsigned long long)(pkt[11]&0xFE)) << 14;
-               pts |= ((unsigned long long)(pkt[12]&0xFF)) << 7;
-               pts |= ((unsigned long long)(pkt[13]&0xFE)) >> 1;
-               ptsvalid = 1;
-
-#if 0          
-               int sec = pts / 90000;
-               int frm = pts % 90000;
-               int min = sec / 60;
-               sec %= 60;
-               int hr = min / 60;
-               min %= 60;
-               int d = hr / 24;
-               hr %= 24;
+                       // ok, we now have the start of the payload, aligned with the PES packet start.
+               if (pkt[0] || pkt[1] || (pkt[2] != 1))
+               {
+                       eWarning("broken startcode");
+                       return 0;
+               }
+
+               if (pkt[7] & 0x80) // PTS present?
+               {
+                       pts  = ((unsigned long long)(pkt[ 9]&0xE))  << 29;
+                       pts |= ((unsigned long long)(pkt[10]&0xFF)) << 22;
+                       pts |= ((unsigned long long)(pkt[11]&0xFE)) << 14;
+                       pts |= ((unsigned long long)(pkt[12]&0xFF)) << 7;
+                       pts |= ((unsigned long long)(pkt[13]&0xFE)) >> 1;
+                       ptsvalid = 1;
+                       
+                       m_last_pts = pts;
+                       m_last_pts_valid = 1;
+
+       #if 0           
+                       int sec = pts / 90000;
+                       int frm = pts % 90000;
+                       int min = sec / 60;
+                       sec %= 60;
+                       int hr = min / 60;
+                       min %= 60;
+                       int d = hr / 24;
+                       hr %= 24;
+                       
+                       eDebug("pts: %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm);
+       #endif
+               }
                
-               eDebug("pts: %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm);
-#endif
+                       /* advance to payload */
+               pkt += pkt[8] + 9;
        }
-       
-               /* advance to payload */
-       pkt += pkt[8] + 9;
 
-               /* sometimes, there are zeros before the startcode. */
        while (pkt < (end-4))
-               if (pkt[0] || pkt[1] || pkt[2])
-                       break;
-               else
-                       pkt++;
-
-               /* if startcode found */
-//     eDebug("%02x %02x %02x %02x", pkt[0], pkt[1], pkt[2], pkt[3]);
-       if (!(pkt[0] || pkt[1] || (pkt[2] != 1)))
        {
-               if (pkt[3] == 0xb3) /* sequence header */
+               int pkt_offset = pkt - begin;
+               if (!(pkt[0] || pkt[1] || (pkt[2] != 1)))
                {
-                       if (ptsvalid)
+//                     eDebug("SC %02x %02x %02x %02x, %02x", pkt[0], pkt[1], pkt[2], pkt[3], pkt[4]);
+                       int sc = pkt[3];
+                       
+                       if (m_streamtype == 0) /* mpeg2 */
                        {
-                               m_streaminfo.m_access_points[offset] = pts;
-                               eDebug("Sequence header at %llx, pts %llx", offset, pts);
-                       } else
-                               eDebug("Sequence header but no valid PTS value.");
-               }
+                               if ((sc == 0x00) || (sc == 0xb3) || (sc == 0xb8)) /* picture, sequence, group start code */
+                               {
+                                       unsigned long long data = sc | (pkt[4] << 8) | (pkt[5] << 16) | (pkt[6] << 24);
+                                       m_streaminfo.writeStructureEntry(offset + pkt_offset, data  & 0xFFFFFFFFULL);
+                               }
+                               if (pkt[3] == 0xb3) /* sequence header */
+                               {
+                                       if (ptsvalid)
+                                       {
+                                               m_streaminfo.m_access_points[offset] = pts;
+       //                                      eDebug("Sequence header at %llx, pts %llx", offset, pts);
+                                       } else
+                                               /*eDebug("Sequence header but no valid PTS value.")*/;
+                               }
+                       }
 
-               if (pkt[3] == 0x09) /* MPEG4 AVC unit access delimiter */
-               {
-                       if (ptsvalid)
+                       if (m_streamtype == 1) /* H.264 */
                        {
-                               m_streaminfo.m_access_points[offset] = pts;
-                               eDebug("MPEG4 AVC UAD at %llx, pts %llx", offset, pts);
-                       } else
-                               eDebug("MPEG4 AVC UAD but no valid PTS value.");
+                               if (sc == 0x09)
+                               {
+                                               /* store image type */
+                                       unsigned long long data = sc | (pkt[4] << 8);
+                                       m_streaminfo.writeStructureEntry(offset + pkt_offset, data);
+                               }
+                               if (pkt[3] == 0x09 &&   /* MPEG4 AVC NAL unit access delimiter */
+                                        (pkt[4] >> 5) == 0) /* and I-frame */
+                               {
+                                       if (ptsvalid)
+                                       {
+                                               m_streaminfo.m_access_points[offset] = pts;
+       //                              eDebug("MPEG4 AVC UAD at %llx, pts %llx", offset, pts);
+                                       } else
+                                               /*eDebug("MPEG4 AVC UAD but no valid PTS value.")*/;
+                               }
+                       }
                }
+               ++pkt;
        }
        return 0;
 }
@@ -384,7 +565,7 @@ inline int eMPEGStreamParserTS::wantPacket(const unsigned char *hdr) const
        if (hdr[1] & 0x40)       /* pusi set: yes. */
                return 1;
 
-       return 0;
+       return m_streamtype == 0; /* we need all packets for MPEG2, but only PUSI packets for H.264 */
 }
 
 void eMPEGStreamParserTS::parseData(off_t offset, const void *data, unsigned int len)
@@ -499,8 +680,21 @@ void eMPEGStreamParserTS::parseData(off_t offset, const void *data, unsigned int
        }
 }
 
-void eMPEGStreamParserTS::setPid(int _pid)
+void eMPEGStreamParserTS::setPid(int _pid, int type)
 {
        m_pktptr = 0;
        m_pid = _pid;
+       m_streamtype = type;
+}
+
+int eMPEGStreamParserTS::getLastPTS(pts_t &last_pts)
+{
+       if (!m_last_pts_valid)
+       {
+               last_pts = 0;
+               return -1;
+       }
+       last_pts = m_last_pts;
+       return 0;
 }
+