epgcache.cpp: fix segfault on unknown arg
[enigma2.git] / lib / dvb / pvrparse.cpp
index bde43765595c0b46459a56471f0f564bca980eae..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,10 +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();
        while (1)
        {
                unsigned long long d[2];
@@ -46,6 +78,7 @@ int eMPEGStreamInformation::load(const char *filename)
                d[1] = bswap_64(d[1]);
 #endif
                m_access_points[d[0]] = d[1];
+               m_pts_to_offset.insert(std::pair<pts_t,off_t>(d[1], d[0]));
        }
        fclose(f);
        fixupDiscontinuties();
@@ -63,7 +96,7 @@ void eMPEGStreamInformation::fixupDiscontinuties()
        if (!m_access_points.size())
                return;
                
-       eDebug("Fixing discontinuities ...");
+//     eDebug("Fixing discontinuities ...");
 
                        /* if we have no delta at the beginning, extrapolate it */
        if ((m_access_points.find(0) == m_access_points.end()) && (m_access_points.size() > 1))
@@ -77,7 +110,7 @@ void eMPEGStreamInformation::fixupDiscontinuties()
                        tdiff *= first->first;
                        tdiff /= diff;
                        m_timestamp_deltas[0] = first->second - tdiff;
-                       eDebug("first delta is %08llx", first->second - tdiff);
+//                     eDebug("first delta is %08llx", first->second - tdiff);
                }
        }
 
@@ -92,16 +125,16 @@ void eMPEGStreamInformation::fixupDiscontinuties()
                
                if (llabs(diff) > (90000*5)) // 5sec diff
                {
-                       eDebug("%llx < %llx, have discont. new timestamp is %llx (diff is %llx)!", current, lastpts_t, i->second, diff);
+//                     eDebug("%llx < %llx, have discont. new timestamp is %llx (diff is %llx)!", current, lastpts_t, i->second, diff);
                        currentDelta = i->second - lastpts_t; /* FIXME: should be the extrapolated new timestamp, based on the current rate */
-                       eDebug("current delta now %llx, making current to %llx", currentDelta, i->second - currentDelta);
+//                     eDebug("current delta now %llx, making current to %llx", currentDelta, i->second - currentDelta);
                        m_timestamp_deltas[i->first] = currentDelta;
                }
                lastpts_t = i->second - currentDelta;
        }
        
        
-       eDebug("ok, found %d disconts.", m_timestamp_deltas.size());
+//     eDebug("ok, found %d disconts.", m_timestamp_deltas.size());
 
 #if 0  
        for (off_t x=0x25807E34ULL; x < 0x25B3CF70; x+= 100000)
@@ -123,7 +156,7 @@ pts_t eMPEGStreamInformation::getDelta(off_t offset)
                /* i can be the first when you query for something before the first PTS */
        if (i != m_timestamp_deltas.begin())
                --i;
-       
+
        return i->second;
 }
 
@@ -132,24 +165,29 @@ int eMPEGStreamInformation::fixupPTS(const off_t &offset, pts_t &ts)
        if (!m_timestamp_deltas.size())
                return -1;
 
-       std::map<off_t, pts_t>::const_iterator i = m_access_points.upper_bound(offset - 4 * 1024 * 1024), nearest = m_access_points.end();
-       
-       while (i != m_access_points.end())
+       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_pts_to_offset.end();
+
+       while (l != u)
        {
-               if ((nearest == m_access_points.end()) || (llabs(i->second - ts) < llabs(nearest->second - ts)))
-                       nearest = i;
-               ++i;
+               if ((nearest == m_pts_to_offset.end()) || (llabs(l->first - ts) < llabs(nearest->first - ts)))
+                       nearest = l;
+               ++l;
        }
-       if (nearest == m_access_points.end())
-               return -1;
-       ts -= getDelta(nearest->first);
+       if (nearest == m_pts_to_offset.end())
+               return 1;
+
+       ts -= getDelta(nearest->second);
+
        return 0;
 }
 
 int eMPEGStreamInformation::getPTS(off_t &offset, pts_t &pts)
 {
        std::map<off_t,pts_t>::iterator before = m_access_points.lower_bound(offset);
-       
+
                /* usually, we prefer the AP before the given offset. however if there is none, we take any. */
        if (before != m_access_points.begin())
                --before;
@@ -170,7 +208,6 @@ pts_t eMPEGStreamInformation::getInterpolated(off_t offset)
 {
                /* get the PTS values before and after the offset. */
        std::map<off_t,pts_t>::iterator before, after;
-       
        after = m_access_points.upper_bound(offset);
        before = after;
 
@@ -201,23 +238,196 @@ 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)
+               {
+                       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)
+               {
+                       if (i == m_access_points.begin())
+                       {
+                               eDebug("at start");
+                               return -1;
+                       }
+                       --i;
+                       c2 = i->second - getDelta(i->first);
+                       if (c1 == c2) { // Discontinuity
+                               --i;
+                               c2 = i->second - getDelta(i->first);
+                       }
+                       c1 = c2;
+                       direction++;
+               }
+       }
+       ts = i->second - getDelta(i->first);
+       eDebug("fine, at %llx - %llx = %llx", ts, i->second, getDelta(i->first));
+       eDebug("fine, at %lld - %lld = %lld", ts, i->second, getDelta(i->first));
+       return 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)
+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)
 {
 }
 
@@ -226,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 */
 
@@ -244,56 +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;
-       
-               /* if startcode found */
-       if (!(pkt[0] || pkt[1] || (pkt[2] != 1)))
+
+       while (pkt < (end-4))
        {
-               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 (m_streamtype == 1) /* H.264 */
+                       {
+                               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;
 }
@@ -316,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)
@@ -327,6 +576,30 @@ void eMPEGStreamParserTS::parseData(off_t offset, const void *data, unsigned int
                        /* sorry for the redundant code here, but there are too many special cases... */
        while (len)
        {
+                       /* emergency resync. usually, this should not happen, because the data should 
+                          be sync-aligned.
+                          
+                          to make this code work for non-strictly-sync-aligned data, (for example, bad 
+                          files) we fix a possible resync here by skipping data until the next 0x47.
+                          
+                          if this is a false 0x47, the packet will be dropped by wantPacket, and the
+                          next time, sync will be re-established. */
+               int skipped = 0;
+               while (!m_pktptr && len)
+               {
+                       if (packet[0] == 0x47)
+                               break;
+                       len--;
+                       packet++;
+                       skipped++;
+               }
+               
+               if (skipped)
+                       eDebug("SYNC LOST: skipped %d bytes.", skipped);
+               
+               if (!len)
+                       break;
+               
                if (m_pktptr)
                {
                                /* skip last packet */
@@ -407,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;
+}
+