From fe478efd1c354b304a3b68aa8fbcc5e5a53e7833 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Wed, 1 Feb 2006 15:50:54 +0000 Subject: [PATCH 1/1] add PVR packet parser to assist finding cutpoints --- lib/dvb/Makefile.am | 2 +- lib/dvb/pvrparse.cpp | 392 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/dvb/pvrparse.h | 61 ++++++++ 3 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 lib/dvb/pvrparse.cpp create mode 100644 lib/dvb/pvrparse.h diff --git a/lib/dvb/Makefile.am b/lib/dvb/Makefile.am index 930a32e..a255a61 100644 --- a/lib/dvb/Makefile.am +++ b/lib/dvb/Makefile.am @@ -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 index 0000000..5d30137 --- /dev/null +++ b/lib/dvb/pvrparse.cpp @@ -0,0 +1,392 @@ +#include +#include +#include + +#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::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::const_iterator second = m_access_points.begin(); + std::map::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::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::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::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::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::const_iterator i(m_access_points.begin()); i != m_access_points.end(); ++i) + { + std::map::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 index 0000000..5ca6263 --- /dev/null +++ b/lib/dvb/pvrparse.h @@ -0,0 +1,61 @@ +#ifndef __include_lib_dvb_pvrparse_h +#define __include_lib_dvb_pvrparse_h + +#include +#include +#include + + /* 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 m_access_points; + /* timestampDelta is in fact the difference between */ + /* the PTS in the stream and a real PTS from 0..max */ + std::map 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 -- 2.7.4