add PVR packet parser to assist finding cutpoints
authorFelix Domke <tmbinc@elitedvb.net>
Wed, 1 Feb 2006 15:50:54 +0000 (15:50 +0000)
committerFelix Domke <tmbinc@elitedvb.net>
Wed, 1 Feb 2006 15:50:54 +0000 (15:50 +0000)
lib/dvb/Makefile.am
lib/dvb/pvrparse.cpp [new file with mode: 0644]
lib/dvb/pvrparse.h [new file with mode: 0644]

index 930a32e..a255a61 100644 (file)
@@ -5,4 +5,4 @@ noinst_LIBRARIES = libenigma_dvb.a
 
 libenigma_dvb_a_SOURCES = dvb.cpp demux.cpp frontend.cpp esection.cpp db.cpp \
        sec.cpp scan.cpp crc32.cpp pmt.cpp decoder.cpp eit.cpp rotor_calc.cpp \
-       epgcache.cpp dvbtime.cpp metaparser.cpp volume.cpp tstools.cpp
+       epgcache.cpp dvbtime.cpp metaparser.cpp volume.cpp tstools.cpp pvrparse.cpp
diff --git a/lib/dvb/pvrparse.cpp b/lib/dvb/pvrparse.cpp
new file mode 100644 (file)
index 0000000..5d30137
--- /dev/null
@@ -0,0 +1,392 @@
+#include <lib/dvb/pvrparse.h>
+#include <lib/base/eerror.h>
+#include <byteswap.h>
+
+#ifndef BYTE_ORDER
+#error no byte order defined!
+#endif
+
+int eMPEGStreamInformation::save(const char *filename)
+{
+       FILE *f = fopen(filename, "wb");
+       if (!f)
+               return -1;
+       
+       for (std::map<off_t, pts_t>::const_iterator i(m_access_points.begin()); i != m_access_points.end(); ++i)
+       {
+               unsigned long long d[2];
+#if BYTE_ORDER == BIG_ENDIAN
+               d[0] = i->first;
+               d[1] = i->second;
+#else
+               d[0] = bswap_64(i->first);
+               d[1] = bswap_64(i->second);
+#endif
+               fwrite(d, sizeof(d), 1, f);
+       }
+       fclose(f);
+       
+       return 0;
+}
+
+int eMPEGStreamInformation::load(const char *filename)
+{
+       FILE *f = fopen(filename, "rb");
+       if (!f)
+               return -1;
+       m_access_points.clear();
+       while (1)
+       {
+               unsigned long long d[2];
+               if (fread(d, sizeof(d), 1, f) < 1)
+                       break;
+               
+#if BYTE_ORDER == LITTLE_ENDIAN
+               d[0] = bswap_64(d[0]);
+               d[1] = bswap_64(d[1]);
+#endif
+               m_access_points[d[0]] = d[1];
+       }
+       fclose(f);
+       fixupDiscontinuties();
+       return 0;
+}
+
+bool eMPEGStreamInformation::empty()
+{
+       return m_access_points.empty();
+}
+
+void eMPEGStreamInformation::fixupDiscontinuties()
+{
+       m_timestamp_deltas.clear();
+       if (!m_access_points.size())
+               return;
+               
+       int first_is_unreliable = 0;
+       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))
+       {
+               std::map<off_t,pts_t>::const_iterator second = m_access_points.begin();
+               std::map<off_t,pts_t>::const_iterator first  = second++;
+               if (first->first < second->first) /* i.e., not equal or broken */
+               {
+                       off_t diff = second->first - first->first;
+                       pts_t tdiff = second->second - first->second;
+                       tdiff *= first->first;
+                       tdiff /= diff;
+                       m_timestamp_deltas[0] = first->second - tdiff;
+                       first_is_unreliable = 1;
+                       eDebug("first delta is %08llx", first->second - tdiff);
+               }
+       }
+
+       if (m_timestamp_deltas.empty())
+               m_timestamp_deltas[m_access_points.begin()->first] = m_access_points.begin()->second;
+
+       pts_t currentDelta = m_timestamp_deltas.begin()->second, lastpts_t = 0;
+       for (std::map<off_t,pts_t>::const_iterator i(m_access_points.begin()); i != m_access_points.end(); ++i)
+       {
+               pts_t current = i->second - currentDelta;
+               pts_t diff = current - lastpts_t;
+               if ((first_is_unreliable) || (diff > (90000*5))) // 5sec diff
+               {
+                       first_is_unreliable = 0;
+                       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);
+                       m_timestamp_deltas[i->first] = currentDelta;
+               }
+               lastpts_t = i->second - currentDelta;
+       }
+       
+       
+       eDebug("ok, found %d disconts.", m_timestamp_deltas.size());
+       
+       
+       for (off_t x=0; x < 1000000; x+= 100000)
+       {
+               eDebug("%08llx -> %08llx | %08llx", x, getDelta(x), getInterpolated(x));
+       }
+}
+
+pts_t eMPEGStreamInformation::getDelta(off_t offset)
+{
+       if (!m_timestamp_deltas.size())
+               return 0;
+       std::map<off_t,pts_t>::iterator i = m_timestamp_deltas.upper_bound(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;
+}
+
+pts_t eMPEGStreamInformation::fixuppts_t(off_t offset, pts_t ts)
+{
+       if (!m_timestamp_deltas.size())
+               return 0;
+       std::map<off_t, pts_t>::const_iterator i = m_access_points.upper_bound(offset - 1024 * 1024), nearest = m_access_points.end();
+       
+       while (i != m_access_points.end())
+       {
+               if ((nearest == m_access_points.end()) || (llabs(i->second - ts) < llabs(nearest->second - ts)))
+                       nearest = i;
+               ++i;
+       }
+       if (nearest == m_access_points.end())
+               return 0;
+       return ts - getDelta(nearest->first);
+}
+
+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;
+
+       if (before != m_access_points.begin())
+               --before;
+       else    /* we query before the first known timestamp ... FIXME */
+               return 0;
+
+               /* empty... */
+       if (before == m_access_points.end())
+               return 0;
+
+               /* if after == end, then we need to extrapolate ... FIXME */
+       if ((before->first == offset) || (after == m_access_points.end()))
+               return before->second - getDelta(offset);
+       
+       pts_t before_ts = before->second - getDelta(before->first);
+       pts_t after_ts = after->second - getDelta(after->first);
+       
+//     eDebug("%08llx .. ? .. %08llx", before_ts, after_ts);
+//     eDebug("%08llx .. %08llx .. %08llx", before->first, offset, after->first);
+       
+       pts_t diff = after_ts - before_ts;
+       off_t diff_off = after->first - before->first;
+       
+       diff = (offset - before->first) * diff / diff_off;
+//     eDebug("%08llx .. %08llx .. %08llx", before_ts, before_ts + diff, after_ts);
+       return before_ts + diff;
+}
+off_t eMPEGStreamInformation::getAccessPoint(pts_t ts)
+{
+               /* FIXME: more efficient implementation */
+       pts_t delta = 0;
+       off_t last = 0;
+       for (std::map<off_t, pts_t>::const_iterator i(m_access_points.begin()); i != m_access_points.end(); ++i)
+       {
+               std::map<off_t, pts_t>::const_iterator d = m_timestamp_deltas.find(i->first);
+               if (d != m_timestamp_deltas.end())
+                       delta = d->second;
+               pts_t c = i->second - delta;
+               if (c > ts)
+                       break;
+               last = i->first;
+       }
+       return last;
+}
+
+eMPEGStreamParserTS::eMPEGStreamParserTS(eMPEGStreamInformation &streaminfo): m_streaminfo(streaminfo), m_pktptr(0), m_pid(-1), m_need_next_packet(0), m_skip(0)
+{
+}
+
+int eMPEGStreamParserTS::processPacket(const unsigned char *pkt, off_t offset)
+{
+       if (!wantPacket(pkt))
+               printf("ne irgendwas ist da falsch\n");
+
+       const unsigned char *end = pkt + 188;
+       
+       if (!(pkt[3] & 0x10))
+       {
+               eWarning("[TSPARSE] PUSI set but no payload.");
+               return 0;
+       }
+       
+       if (pkt[3] & 0x20) // adaption field present?
+               pkt += pkt[4] + 4 + 1;  /* skip adaption field and header */
+       else
+               pkt += 4; /* skip header */
+
+       if (pkt > end)
+       {
+               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?
+       {
+               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;
+               
+               eDebug("pts: %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm);
+#endif
+       }
+       
+               /* advance to payload */
+       pkt += pkt[8] + 9;
+       
+               /* if startcode found */
+       if (!(pkt[0] || pkt[1] || (pkt[2] != 1)))
+       {
+               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.");
+               }
+       }
+       return 0;
+}
+
+inline int eMPEGStreamParserTS::wantPacket(const unsigned char *hdr) const
+{
+       if (hdr[0] != 0x47)
+       {
+               eDebug("missing sync!");
+               return 0;
+       }
+       int ppid = ((hdr[1]&0x1F) << 8) | hdr[2];
+
+       if (ppid != m_pid)
+               return 0;
+               
+       if (m_need_next_packet)  /* next packet (on this pid) was required? */
+               return 1;
+       
+       if (hdr[1] & 0x40)       /* pusi set: yes. */
+               return 1;
+
+       return 0;
+}
+
+void eMPEGStreamParserTS::parseData(off_t offset, const void *data, unsigned int len)
+{
+       const unsigned char *packet = (const unsigned char*)data;
+       const unsigned char *packet_start = packet;
+       
+                       /* sorry for the redundant code here, but there are too many special cases... */
+       while (len)
+       {
+               if (m_pktptr)
+               {
+                               /* skip last packet */
+                       if (m_pktptr < 0)
+                       {
+                               unsigned int skiplen = -m_pktptr;
+                               if (skiplen > len)
+                                       skiplen = len;
+                               packet += skiplen;
+                               len -= skiplen;
+                               m_pktptr += skiplen;
+                               continue;
+                       } else if (m_pktptr < 4) /* header not complete, thus we don't know if we want this packet */
+                       {
+                               unsigned int storelen = 4 - m_pktptr;
+                               if (storelen > len)
+                                       storelen = len;
+                               memcpy(m_pkt + m_pktptr, packet,  storelen);
+                               
+                               m_pktptr += storelen;
+                               len -= storelen;
+                               packet += storelen;
+                               
+                               if (m_pktptr == 4)
+                                       if (!wantPacket(m_pkt))
+                                       {
+                                                       /* skip packet */
+                                               packet += 184;
+                                               len -= 184;
+                                               m_pktptr = 0;
+                                               continue;
+                                       }
+                       }
+                               /* otherwise we complete up to the full packet */
+                       unsigned int storelen = 188 - m_pktptr;
+                       if (storelen > len)
+                               storelen = len;
+                       memcpy(m_pkt + m_pktptr, packet,  storelen);
+                       m_pktptr += storelen;
+                       len -= storelen;
+                       packet += storelen;
+                       
+                       if (m_pktptr == 188)
+                       {
+                               m_need_next_packet = processPacket(m_pkt, offset + (packet - packet_start));
+                               m_pktptr = 0;
+                       }
+               } else if (len >= 4)  /* if we have a full header... */
+               {
+                       if (wantPacket(packet))  /* decide wheter we need it ... */
+                       {
+                               if (len >= 188)          /* packet complete? */
+                               {
+                                       m_need_next_packet = processPacket(packet, offset + (packet - packet_start)); /* process it now. */
+                               } else
+                               {
+                                       memcpy(m_pkt, packet, len);  /* otherwise queue it up */
+                                       m_pktptr = len;
+                               }
+                       }
+
+                               /* skip packet */
+                       int sk = len;
+                       if (sk >= 188)
+                               sk = 188;
+                       else if (!m_pktptr) /* we dont want this packet, otherwise m_pktptr = sk (=len) > 4 */
+                               m_pktptr = sk - 188;
+
+                       len -= sk;
+                       packet += sk;
+               } else             /* if we don't have a complete header */
+               {
+                       memcpy(m_pkt, packet, len);   /* complete header next time */
+                       m_pktptr = len;
+                       packet += len;
+                       len = 0;
+               }
+       }
+}
+
+void eMPEGStreamParserTS::setPid(int _pid)
+{
+       m_pktptr = 0;
+       m_pid = _pid;
+}
diff --git a/lib/dvb/pvrparse.h b/lib/dvb/pvrparse.h
new file mode 100644 (file)
index 0000000..5ca6263
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef __include_lib_dvb_pvrparse_h
+#define __include_lib_dvb_pvrparse_h
+
+#include <lib/dvb/idvb.h>
+#include <map>
+#include <set>
+
+       /* This module parses TS data and collects valuable information  */
+       /* about it, like PTS<->offset correlations and sequence starts. */
+
+       /* At first, we define the collector class: */
+class eMPEGStreamInformation
+{
+public:
+               /* we order by off_t here, since the timestamp may */
+               /* wrap around. */
+               /* we only record sequence start's pts values here. */
+       std::map<off_t, pts_t> m_access_points;
+               /* timestampDelta is in fact the difference between */
+               /* the PTS in the stream and a real PTS from 0..max */
+       std::map<off_t, pts_t> m_timestamp_deltas;
+       
+       int save(const char *filename);
+       int load(const char *filename);
+       
+               /* recalculates timestampDeltas */
+       void fixupDiscontinuties();
+       
+               /* get delta at specific offset */
+       pts_t getDelta(off_t offset);
+       
+               /* fixup timestamp near offset */
+       pts_t fixuppts_t(off_t offset, pts_t ts);
+       
+               /* inter/extrapolate timestamp from offset */
+       pts_t getInterpolated(off_t offset);
+       
+       off_t getAccessPoint(pts_t ts);
+       
+       bool empty();
+};
+
+       /* Now we define the parser's state: */
+class eMPEGStreamParserTS
+{
+public:
+       eMPEGStreamParserTS(eMPEGStreamInformation &streaminfo);
+       void parseData(off_t offset, const void *data, unsigned int len);
+       void setPid(int pid);
+private:
+       eMPEGStreamInformation &m_streaminfo;
+       unsigned char m_pkt[188];
+       int m_pktptr;
+       int processPacket(const unsigned char *pkt, off_t offset);
+       inline int wantPacket(const unsigned char *hdr) const;
+       int m_pid;
+       int m_need_next_packet;
+       int m_skip;
+};
+
+#endif