Index: mpegstreamdata.cpp
===================================================================
--- mpegstreamdata.cpp	(revision 7416)
+++ mpegstreamdata.cpp	(working copy)
@@ -89,17 +89,62 @@
 /** \fn MPEGStreamData::AssemblePSIP(const TSPacket* tspacket)
  *  \brief PES packet assembler.
  *
- *   This only assembles PES packets when new PES packets
- *   start in their own TSPacket. This is always true of
- *   ATSC & DVB tables, but not true of Audio and Video
- *   PES packets.
+ *   This only starts assembling PES packets when new PES
+ *   packets start in their own TSPacket.
+ *
+ *   DVB TSPackets may contain multiple segments of the PSI
+ *   stream.  (see ISO 13818-1 section 2.4.3.3, particularly
+ *   the definition of payload_unit_start_indicator, which
+ *   indicates there is at least one segment start, but not
+ *   limited to only one segment start.)
+ *
+ *   PSI stuffing bytes are 0xFF and will complete the
+ *   remaining portion of the TSPacket.  (Section 2.4.4)
+ *
+ *   \note This method makes the assumption that AddTSPacket
+ *   correctly handles duplicate packets.
  */
-PSIPTable* MPEGStreamData::AssemblePSIP(const TSPacket* tspacket)
+PSIPTable* MPEGStreamData::AssemblePSIP(const TSPacket* tspacket, bool &moreTablePackets)
 {
+    moreTablePackets = true;
+
+    PESPacket* partial = GetPartialPES(tspacket->PID());
+    if (partial)
+    {
+		if (partial->AddTSPacket(tspacket))
+        {
+            PSIPTable* psip = new PSIPTable(*partial);
+
+            // Advance to the next packet
+            unsigned int packetStart = partial->PSIOffset() + psip->SectionLength();
+            if (packetStart < partial->TSSizeInBuffer()) {
+                if (partial->pesdata()[psip->SectionLength() + 1] != 0xff) {
+                    // If the next section starts in the new tspacket
+                    // create a new partial packet to prevent overflow
+                    if ((partial->TSSizeInBuffer() > TSPacket::HEADER_SIZE + TSPacket::PAYLOAD_SIZE)
+                            && (packetStart > partial->TSSizeInBuffer() - TSPacket::PAYLOAD_SIZE)) {
+                        // Saving will handle deleting the old one
+                        SavePartialPES(tspacket->PID(), new PESPacket(*tspacket));
+                    } else {
+                        partial->SetPSIOffset(partial->PSIOffset() + psip->SectionLength());
+                    }
+                    return psip;
+                }
+            }
+
+            moreTablePackets = false;
+            DeletePartialPES(tspacket->PID());
+            return psip;
+        }
+        moreTablePackets = false;
+        return 0; // partial packet is not yet complete.
+    }
+
     if (tspacket->PayloadStart())
     {
         const int offset = tspacket->AFCOffset() +
             tspacket->StartOfFieldPointer();
+
         if (offset>181)
         {
             VERBOSE(VB_IMPORTANT, "Error: offset>181, pes length & "
@@ -111,27 +156,33 @@
         const PESPacket pes = PESPacket::View(*tspacket);
         if ((pes_length + offset)>188)
         {
-            if (!bool(pes.pesdata()[6]&1)/*current*/)
-                return 0; // we only care about current psip packets for now
             SavePartialPES(tspacket->PID(), new PESPacket(*tspacket));
+            moreTablePackets = false;
             return 0;
         }
-        return new PSIPTable(*tspacket); // must be complete packet
-    }
 
-    PESPacket* partial = GetPartialPES(tspacket->PID());
-    if (partial)
-    {
-        if (partial->AddTSPacket(tspacket))
-        {
-            PSIPTable* psip = new PSIPTable(*partial);
-            DeletePartialPES(tspacket->PID());
-            return psip;
+        PSIPTable *psip = new PSIPTable(*tspacket); // must be complete packet
+
+        /* There might be another section after this one in the current packet.
+         * We need room before the end of the packet, and it must not be packet stuffing.
+         */
+        if (offset + psip->SectionLength() < 188) {
+            if (pesdata[psip->SectionLength() + 1] != 0xff) {
+                // This isn't sutffing, so we need to put this on as a partial packet
+                PESPacket *pesp = new PESPacket(*tspacket);
+                pesp->SetPSIOffset(offset + psip->SectionLength());
+                SavePartialPES(tspacket->PID(), pesp);
+                return psip;
+            }
         }
-        return 0; // partial packet is not yet complete.
+
+        moreTablePackets = false;
+        return psip;
     }
+
     // We didn't see this PES packet's start, so this must be the
     // tail end of something we missed. Ignore it.
+    moreTablePackets = false;
     return 0;
 }
 
@@ -359,55 +410,63 @@
  */
 bool MPEGStreamData::HandleTSTables(const TSPacket* tspacket)
 {
-#define HT_RETURN(HANDLED) { delete psip; return HANDLED; }
     // Assemble PSIP
-    PSIPTable *psip = AssemblePSIP(tspacket);
-    if (!psip)
-        return true;
+    bool morePSIPPackets = true;
+    while (morePSIPPackets) {
+        PSIPTable *psip = AssemblePSIP(tspacket, morePSIPPackets);
+        if (!psip)
+            return true;
 
-    // Validate PSIP
-    // but don't validate PMT if our driver has the PMT CRC bug.
-    bool validate = !_have_pmt_CRC_bug || (TableID::PMT != psip->TableID());
-    if (validate && !psip->IsGood())
-    {
-        VERBOSE(VB_RECORD, QString("PSIP packet failed CRC check. "
-                                   "pid(0x%1) type(0x%2)")
-                .arg(tspacket->PID(),0,16).arg(psip->TableID(),0,16));
-        HT_RETURN(true);
-    }
+        // Validate PSIP
+        // but don't validate PMT if our driver has the PMT CRC bug.
+        bool validate = !_have_pmt_CRC_bug || (TableID::PMT != psip->TableID());
+        if (validate && !psip->IsGood())
+        {
+            VERBOSE(VB_RECORD, QString("PSIP packet failed CRC check. "
+                        "pid(0x%1) type(0x%2)")
+                    .arg(tspacket->PID(),0,16).arg(psip->TableID(),0,16));
+            delete psip;
+            continue;
+        }
 
-    if (!psip->IsCurrent()) // we don't cache the next table, for now
-        HT_RETURN(true);
+        if (!psip->IsCurrent()) { // we don't cache the next table, for now
+            delete psip;
+            continue;
+        }
 
-    if (1!=tspacket->AdaptationFieldControl())
-    { // payload only, ATSC req.
-        VERBOSE(VB_RECORD,
-                "PSIP packet has Adaptation Field Control, not ATSC compiant");
-        HT_RETURN(true);
-    }
+        if (1!=tspacket->AdaptationFieldControl())
+        { // payload only, ATSC req.
+            VERBOSE(VB_RECORD,
+                    "PSIP packet has Adaptation Field Control, not ATSC compiant");
+            delete psip;
+            continue;
+        }
 
-    if (tspacket->ScramplingControl())
-    { // scrambled! ATSC, DVB require tables not to be scrambled
-        VERBOSE(VB_RECORD,
-                "PSIP packet is scrambled, not ATSC/DVB compiant");
-        HT_RETURN(true);
-    }
+        if (tspacket->ScramplingControl())
+        { // scrambled! ATSC, DVB require tables not to be scrambled
+            VERBOSE(VB_RECORD,
+                    "PSIP packet is scrambled, not ATSC/DVB compiant");
+            delete psip;
+            continue;
+        }
 
-    // Don't decode redundant packets,
-    // but if it is a desired PAT or PMT emit a "heartbeat" signal.
-    if (IsRedundant(*psip))
-    {
-        if (TableID::PAT == psip->TableID())
-            emit UpdatePATSingleProgram(PATSingleProgram());
-        if (TableID::PMT == psip->TableID() &&
-            tspacket->PID() == _pid_pmt_single_program)
-            emit UpdatePMTSingleProgram(PMTSingleProgram());
-        HT_RETURN(true); // already parsed this table, toss it.
-    }
+        // Don't decode redundant packets,
+        // but if it is a desired PAT or PMT emit a "heartbeat" signal.
+        if (IsRedundant(*psip))
+        {
+            if (TableID::PAT == psip->TableID())
+                emit UpdatePATSingleProgram(PATSingleProgram());
+            if (TableID::PMT == psip->TableID() &&
+                    tspacket->PID() == _pid_pmt_single_program)
+                emit UpdatePMTSingleProgram(PMTSingleProgram());
+            delete psip; // already parsed this table, toss it.
+            continue;
+        }
 
-    bool handled = HandleTables(tspacket->PID(), *psip);
-    HT_RETURN(handled);
-#undef HT_RETURN
+        HandleTables(tspacket->PID(), *psip);
+        delete psip;
+    };
+    return true;
 }
 
 int MPEGStreamData::ProcessData(unsigned char *buffer, int len)
Index: mpegstreamdata.h
===================================================================
--- mpegstreamdata.h	(revision 7416)
+++ mpegstreamdata.h	(working copy)
@@ -128,7 +128,7 @@
 
   protected:
     // Table processing -- for internal use
-    PSIPTable* AssemblePSIP(const TSPacket* tspacket);
+    PSIPTable* AssemblePSIP(const TSPacket* tspacket, bool& moreTablePackets);
     bool AssemblePSIP(PSIPTable& psip, TSPacket* tspacket);
     void SavePartialPES(uint pid, PESPacket* packet);
     PESPacket* GetPartialPES(uint pid)
Index: pespacket.h
===================================================================
--- pespacket.h	(revision 7416)
+++ pespacket.h	(working copy)
@@ -32,11 +32,13 @@
         {
             _pesdata = tspacket.data() + tspacket.AFCOffset() + 
                 tspacket.StartOfFieldPointer();
+            _PSIOffset = tspacket.AFCOffset() + tspacket.StartOfFieldPointer();
         }
         else
         {
             cerr<<"Started PESPacket, but !payloadStart()"<<endl;
             _pesdata = tspacket.data() + tspacket.AFCOffset();
+            _PSIOffset = tspacket.AFCOffset();
         }
 
         _badPacket = true;
@@ -49,7 +51,7 @@
   protected:
     // does not create it's own data
     PESPacket(const TSPacket* tspacket, bool)
-        : _pesdata(0), _fullbuffer(0), _allocSize(0)
+        : _pesdata(0), _fullbuffer(0), _allocSize(0), _PSIOffset(0)
     {
         InitPESPacket(const_cast<TSPacket&>(*tspacket));
         _fullbuffer = const_cast<unsigned char*>(tspacket->data());
@@ -64,7 +66,8 @@
         _allocSize = pkt._allocSize;
         _fullbuffer = new unsigned char[_allocSize+188];
         memcpy(_fullbuffer, pkt._fullbuffer, _allocSize+188);
-        _pesdata = _fullbuffer + (pkt._pesdata - pkt._fullbuffer);
+        _PSIOffset = pkt._PSIOffset;
+        _pesdata = _fullbuffer + _PSIOffset;
         _ccLast = pkt._ccLast;
         _cnt = pkt._cnt;
         _badPacket = pkt._badPacket;
@@ -76,10 +79,9 @@
         InitPESPacket(const_cast<TSPacket&>(tspacket));
 
         int len = (4*1024) - 188;
-        int offset = _pesdata - tspacket.data();
-        _allocSize=((len+offset)/188)*188+188;
+        _allocSize=((len+_PSIOffset)/188)*188+188;
         _fullbuffer = new unsigned char[_allocSize+188];
-        _pesdata = _fullbuffer + offset;
+        _pesdata = _fullbuffer + _PSIOffset;
         memcpy(_fullbuffer, tspacket.data(), 188);
         _ccLast = tspacket.ContinuityCounter();
         _cnt = 1;
@@ -116,6 +118,9 @@
     unsigned int Length() const
         { return (pesdata()[2] & 0x0f) << 8 | pesdata()[3]; }
 
+    unsigned int TSSizeInBuffer() { return (_cnt*TSPacket::PAYLOAD_SIZE)+TSPacket::HEADER_SIZE; }
+    unsigned int PSIOffset() const { return _PSIOffset; }
+
     const unsigned char* pesdata() const { return _pesdata; }
     unsigned char* pesdata() { return _pesdata; }
 
@@ -153,6 +158,11 @@
         _pesdata[Length()+3] = (crc & 0x000000ff);
     }
 
+    void SetPSIOffset(unsigned int offset) {
+        _PSIOffset = offset;
+        _pesdata = _fullbuffer + _PSIOffset;
+    }
+
     bool VerifyCRC() const { return CalcCRC() == CRC(); }
 
     /*
@@ -177,6 +187,7 @@
     unsigned int _ccLast;
     unsigned int _cnt;
     unsigned int _allocSize;
+    unsigned int _PSIOffset;
     bool _badPacket;
 };
 
