Index: mpegstreamdata.cpp
===================================================================
--- mpegstreamdata.cpp	(revision 24087)
+++ mpegstreamdata.cpp	(working copy)
@@ -21,6 +21,13 @@
 
 //#define DEBUG_MPEG_RADIO // uncomment to strip video streams from TS stream
 
+// Defined:   CA pids are added to the PMT
+// Undefined: CA pids are added as listening pids only
+// Undefined option is cleaner and seems to work. Also has the benefit
+// of not requiring the CAT to be cached before handling the PMT
+// so it won't break if there is no CAT.
+//#define ADD_CA_TO_PMT
+
 void init_sections(sections_t &sect, uint last_section)
 {
     static const unsigned char init_bits[8] =
@@ -86,6 +93,7 @@
     memset(_si_time_offsets, 0, sizeof(_si_time_offsets));
 
     AddListeningPID(MPEG_PAT_PID);
+    AddListeningPID(MPEG_CAT_PID);
 }
 
 MPEGStreamData::~MPEGStreamData()
@@ -209,11 +217,17 @@
         for (; it2 != _cached_pmts.end(); ++it2)
             DeleteCachedTable(*it2);
         _cached_pmts.clear();
+
+        cat_cache_t::iterator it3 = _cached_cats.begin();
+        for (; it3 != _cached_cats.end(); ++it3)
+            DeleteCachedTable(*it3);
+        _cached_cats.clear();
     }
 
     ResetDecryptionMonitoringState();
 
     AddListeningPID(MPEG_PAT_PID);
+    AddListeningPID(MPEG_CAT_PID);
 }
 
 void MPEGStreamData::DeletePartialPES(uint pid)
@@ -505,6 +519,13 @@
         VERBOSE(VB_RECORD, "no PAT yet...");
         return false; // no way to properly rewrite pids without PAT
     }
+#ifdef ADD_CA_TO_PMT
+    if (!HasCachedAnyCAT())
+    {
+        VERBOSE(VB_RECORD, "no CAT yet...");
+        return false;
+    }
+#endif
     pmt.Parse();
 
     uint programNumber = 1; // MPEG Program Number
@@ -517,9 +538,34 @@
 
     if (!_strip_pmt_descriptors)
     {
+#ifdef ADD_CA_TO_PMT
+/*
+   Do not exclude the CA descriptors or we break AU (auto update).
+   This is especially noticable on the Optus C1 Aurora service
+   where all AU data is in a separate PID.
+*/
+        gdesc = MPEGDescriptor::Parse(pmt.ProgramInfo(), pmt.ProgramInfoLength());
+
+/* looks like we also need to bring in the PIDs listed in the
+   CAT (Conditional Access Table). The program keys in the PIDs
+   referenced in the PMT seem to be changed daily, and the ones
+   in the CAT are changed weekly.
+*/
+        cat_vec_t cats = GetCachedCATs();
+        VERBOSE(VB_RECORD, QString("Adding CA PIDS from %1 CAT tables").arg(cats.size()));
+        for (uint i = 0; i < cats.size(); i++)
+        {
+           desc_list_t cdesc = MPEGDescriptor::ParseOnlyInclude(
+               cats[i]->Descriptors(), cats[i]->DescriptorsLength(),
+               DescriptorID::conditional_access);
+           if (cdesc.size())
+              gdesc.insert(gdesc.end(), cdesc.begin(), cdesc.end());
+        }
+#else
         gdesc = MPEGDescriptor::ParseAndExclude(
             pmt.ProgramInfo(), pmt.ProgramInfoLength(),
             DescriptorID::conditional_access);
+#endif
 
         // If there is no caption descriptor in PMT, copy any caption
         // descriptor found in VCT to global descriptors...
@@ -618,6 +664,20 @@
         return false;
     }
 
+#ifndef ADD_CA_TO_PMT
+    desc_list_t cdesc = MPEGDescriptor::ParseOnlyInclude(
+         pmt.ProgramInfo(), pmt.ProgramInfoLength(),
+         DescriptorID::conditional_access);
+
+    VERBOSE(VB_RECORD, QString("Adding %1 CA pids from PMT").arg(cdesc.size()));
+    for (uint i = 0; i < cdesc.size(); i++)
+    {
+        ConditionalAccessDescriptor cad(cdesc[i]);
+        VERBOSE(VB_RECORD, QString("CA PID from PMT:%1").arg(cad.PID()));
+        AddListeningPID(cad.PID());
+    }
+#endif
+
     _pids_audio.clear();
     for (uint i = 0; i < audioPIDs.size(); i++)
         AddAudioPID(audioPIDs[i]);
@@ -679,7 +739,11 @@
     }
 
     if (TableID::CAT == table_id)
-        return false;
+    {
+        if (VersionCAT(psip.TableIDExtension()) != version)
+           return false;
+        return CATSectionSeen(psip.TableIDExtension(), psip.Section());
+    }
 
     if (TableID::PMT == table_id)
     {
@@ -720,13 +784,17 @@
         }
         case TableID::CAT:
         {
+            uint tsid = psip.TableIDExtension();
+            SetVersionCAT(tsid, version, psip.LastSection());
+            SetCATSectionSeen(tsid, psip.Section());
+
             ConditionalAccessTable cat(psip);
 
-            _listener_lock.lock();
-            for (uint i = 0; i < _mpeg_listeners.size(); i++)
-                _mpeg_listeners[i]->HandleCAT(&cat);
-            _listener_lock.unlock();
+            if (_cache_tables)
+                CacheCAT(&cat);
 
+            ProcessCAT(&cat);
+
             return true;
         }
         case TableID::PMT:
@@ -798,6 +866,29 @@
     }
 }
 
+
+void MPEGStreamData::ProcessCAT(const ConditionalAccessTable *cat)
+{
+    _listener_lock.lock();
+    for (uint i = 0; i < _mpeg_listeners.size(); i++)
+        _mpeg_listeners[i]->HandleCAT(cat);
+    _listener_lock.unlock();
+    
+#ifndef ADD_CA_TO_PMT
+    desc_list_t cdesc = MPEGDescriptor::ParseOnlyInclude(
+         cat->Descriptors(), cat->DescriptorsLength(),
+         DescriptorID::conditional_access);
+
+    VERBOSE(VB_RECORD, QString("Adding %1 CA pids from CAT").arg(cdesc.size()));
+    for (uint i = 0; i < cdesc.size(); i++)
+    {
+        ConditionalAccessDescriptor cad(cdesc[i]);
+        VERBOSE(VB_RECORD, QString("CA PID from CAT:%1").arg(cad.PID()));
+        AddListeningPID(cad.PID());
+    }
+#endif
+}
+            
 void MPEGStreamData::ProcessPMT(const ProgramMapTable *pmt)
 {
     _listener_lock.lock();
@@ -967,6 +1058,8 @@
 {
     bool ok = !tspacket.TransportError();
 
+    VERBOSE(VB_RECORD, tspacket.toString());
+
     if (IsEncryptionTestPID(tspacket.PID()))
     {
         ProcessEncryptedPacket(tspacket);
@@ -1146,6 +1239,36 @@
     return true;
 }
 
+void MPEGStreamData::SetCATSectionSeen(uint tsid, uint section)
+{
+    sections_map_t::iterator it = _cat_section_seen.find(tsid);
+    if (it == _cat_section_seen.end())
+    {
+        _cat_section_seen[tsid].resize(32, 0);
+        it = _cat_section_seen.find(tsid);
+    }
+    (*it)[section>>3] |= bit_sel[section & 0x7];
+}
+
+bool MPEGStreamData::CATSectionSeen(uint tsid, uint section) const
+{
+    sections_map_t::const_iterator it = _cat_section_seen.find(tsid);
+    if (it == _cat_section_seen.end())
+        return false;
+    return (bool) ((*it)[section>>3] & bit_sel[section & 0x7]);
+}
+
+bool MPEGStreamData::HasAllCATSections(uint tsid) const
+{
+    sections_map_t::const_iterator it = _cat_section_seen.find(tsid);
+    if (it == _cat_section_seen.end())
+        return false;
+    for (uint i = 0; i < 32; i++)
+        if ((*it)[i] != 0xff)
+            return false;
+    return true;
+}
+
 void MPEGStreamData::SetPMTSectionSeen(uint prog_num, uint section)
 {
     sections_map_t::iterator it = _pmt_section_seen.find(prog_num);
@@ -1221,6 +1344,42 @@
     return _cached_pats.size();
 }
 
+bool MPEGStreamData::HasCachedAllCAT(uint tsid) const
+{
+    QMutexLocker locker(&_cache_lock);
+
+    cat_cache_t::const_iterator it = _cached_cats.find(tsid << 8);
+    if (it == _cached_cats.end())
+        return false;
+
+    uint last_section = (*it)->LastSection();
+    if (!last_section)
+        return true;
+
+    for (uint i = 1; i <= last_section; i++)
+        if (_cached_cats.find((tsid << 8) | i) == _cached_cats.end())
+            return false;
+
+    return true;
+}
+
+bool MPEGStreamData::HasCachedAnyCAT(uint tsid) const
+{
+    QMutexLocker locker(&_cache_lock);
+
+    for (uint i = 0; i <= 255; i++)
+        if (_cached_cats.find((tsid << 8) | i) != _cached_cats.end())
+            return true;
+
+    return false;
+}
+
+bool MPEGStreamData::HasCachedAnyCAT(void) const
+{
+    QMutexLocker locker(&_cache_lock);
+    return _cached_cats.size();
+}
+
 bool MPEGStreamData::HasCachedAllPMT(uint pnum) const
 {
     QMutexLocker locker(&_cache_lock);
@@ -1324,6 +1483,51 @@
     return pats;
 }
 
+const cat_ptr_t MPEGStreamData::GetCachedCAT(
+    uint tsid, uint section_num) const
+{
+    QMutexLocker locker(&_cache_lock);
+    ConditionalAccessTable *cat = NULL;
+
+    uint key = (tsid << 8) | section_num;
+    cat_cache_t::const_iterator it = _cached_cats.find(key);
+    if (it != _cached_cats.end())
+        IncrementRefCnt(cat = *it);
+
+    return cat;
+}
+
+cat_vec_t MPEGStreamData::GetCachedCATs(uint tsid) const
+{
+    QMutexLocker locker(&_cache_lock);
+    cat_vec_t cats;
+
+    for (uint i=0; i < 256; i++)
+    {
+        ConditionalAccessTable *cat = GetCachedCAT(tsid, i);
+        if (cat)
+            cats.push_back(cat);
+    }
+
+    return cats;
+}
+
+cat_vec_t MPEGStreamData::GetCachedCATs(void) const
+{
+    QMutexLocker locker(&_cache_lock);
+    cat_vec_t cats;
+
+    cat_cache_t::const_iterator it = _cached_cats.begin();
+    for (; it != _cached_cats.end(); ++it)
+    {
+        ConditionalAccessTable* cat = *it;
+        IncrementRefCnt(cat);
+        cats.push_back(cat);
+    }
+
+    return cats;
+}
+
 const pmt_ptr_t MPEGStreamData::GetCachedPMT(
     uint program_num, uint section_num) const
 {
@@ -1401,6 +1605,20 @@
     pats.clear();
 }
 
+void MPEGStreamData::ReturnCachedCATTables(cat_vec_t &cats) const
+{
+    for (cat_vec_t::iterator it = cats.begin(); it != cats.end(); ++it)
+        ReturnCachedTable(*it);
+    cats.clear();
+}
+
+void MPEGStreamData::ReturnCachedCATTables(cat_map_t &cats) const
+{
+    for (cat_map_t::iterator it = cats.begin(); it != cats.end(); ++it)
+        ReturnCachedCATTables(*it);
+    cats.clear();
+}
+
 void MPEGStreamData::ReturnCachedPMTTables(pmt_vec_t &pmts) const
 {
     for (pmt_vec_t::iterator it = pmts.begin(); it != pmts.end(); ++it)
@@ -1440,6 +1658,12 @@
         _cached_pats[(tid << 8) | psip->Section()] = NULL;
         delete psip;
     }
+    else if (TableID::CAT == psip->TableID() &&
+             (_cached_cats[(tid << 8) | psip->Section()] == psip))
+    {
+        _cached_cats[(tid << 8) | psip->Section()] = NULL;
+        delete psip;
+    }
     else if ((TableID::PMT == psip->TableID()) &&
              (_cached_pmts[(tid << 8) | psip->Section()] == psip))
     {
@@ -1473,6 +1697,20 @@
     _cached_pats[key] = pat;
 }
 
+void MPEGStreamData::CacheCAT(const ConditionalAccessTable *_cat)
+{
+    ConditionalAccessTable *cat = new ConditionalAccessTable(*_cat);
+    uint key = (_cat->TableIDExtension() << 8) | _cat->Section();
+
+    QMutexLocker locker(&_cache_lock);
+
+    cat_cache_t::iterator it = _cached_cats.find(key);
+    if (it != _cached_cats.end())
+        DeleteCachedTable(*it);
+
+    _cached_cats[key] = cat;
+}
+
 void MPEGStreamData::CachePMT(const ProgramMapTable *_pmt)
 {
     ProgramMapTable *pmt = new ProgramMapTable(*_pmt);
Index: mpegstreamdata.h
===================================================================
--- mpegstreamdata.h	(revision 24087)
+++ mpegstreamdata.h	(working copy)
@@ -33,6 +33,11 @@
 typedef QMap<uint, pat_vec_t>                  pat_map_t;
 typedef QMap<uint, ProgramAssociationTable*>   pat_cache_t;
 
+typedef ConditionalAccessTable*                cat_ptr_t;
+typedef vector<const ConditionalAccessTable*>  cat_vec_t;
+typedef QMap<uint, cat_vec_t>                  cat_map_t;
+typedef QMap<uint, ConditionalAccessTable*>    cat_cache_t;
+
 typedef ProgramMapTable*                pmt_ptr_t;
 typedef vector<const ProgramMapTable*>  pmt_vec_t;
 typedef QMap<uint, pmt_vec_t>           pmt_map_t;
@@ -169,6 +174,21 @@
         return *it;
     }
 
+    void SetVersionCAT(uint tsid, int version, uint last_section)
+    {
+        if (VersionCAT(tsid) == version)
+            return;
+        _cat_version[tsid] = version;
+        init_sections(_cat_section_seen[tsid], last_section);
+    }
+    int  VersionCAT(uint tsid) const
+    {
+        const QMap<uint, int>::const_iterator it = _cat_version.find(tsid);
+        if (it == _cat_version.end())
+            return -1;
+        return *it;
+    }
+
     void SetVersionPMT(uint program_num, int version, uint last_section)
     {
         if (VersionPMT(program_num) == version)
@@ -189,6 +209,10 @@
     bool PATSectionSeen(   uint tsid, uint section) const;
     bool HasAllPATSections(uint tsid) const;
 
+    void SetCATSectionSeen(uint tsid, uint section);
+    bool CATSectionSeen(   uint tsid, uint section) const;
+    bool HasAllCATSections(uint tsid) const;
+
     void SetPMTSectionSeen(uint prog_num, uint section);
     bool PMTSectionSeen(   uint prog_num, uint section) const;
     bool HasAllPMTSections(uint prog_num) const;
@@ -200,6 +224,10 @@
     bool HasCachedAnyPAT(uint tsid) const;
     bool HasCachedAnyPAT(void) const;
 
+    bool HasCachedAllCAT(uint tsid) const;
+    bool HasCachedAnyCAT(uint tsid) const;
+    bool HasCachedAnyCAT(void) const;
+
     bool HasCachedAllPMT(uint program_num) const;
     bool HasCachedAnyPMT(uint program_num) const;
     bool HasCachedAllPMTs(void) const;
@@ -210,6 +238,11 @@
     pat_vec_t       GetCachedPATs(void) const;
     pat_map_t       GetCachedPATMap(void) const;
 
+    const cat_ptr_t GetCachedCAT(uint tsid, uint section_num) const;
+    cat_vec_t       GetCachedCATs(uint tsid) const;
+    cat_vec_t       GetCachedCATs(void) const;
+    cat_map_t       GetCachedCATMap(void) const;
+
     const pmt_ptr_t GetCachedPMT(uint program_num, uint section_num) const;
     pmt_vec_t GetCachedPMTs(void) const;
     pmt_map_t GetCachedPMTMap(void) const;
@@ -217,6 +250,8 @@
     virtual void ReturnCachedTable(const PSIPTable *psip) const;
     virtual void ReturnCachedPATTables(pat_vec_t&) const;
     virtual void ReturnCachedPATTables(pat_map_t&) const;
+    virtual void ReturnCachedCATTables(cat_vec_t&) const;
+    virtual void ReturnCachedCATTables(cat_map_t&) const;
     virtual void ReturnCachedPMTTables(pmt_vec_t&) const;
     virtual void ReturnCachedPMTTables(pmt_map_t&) const;
 
@@ -297,6 +332,7 @@
         { _partial_pes_packet_cache.remove(pid); }
     void DeletePartialPES(uint pid);
     void ProcessPAT(const ProgramAssociationTable *pat);
+    void ProcessCAT(const ConditionalAccessTable *pat);
     void ProcessPMT(const ProgramMapTable *pmt);
     void ProcessEncryptedPacket(const TSPacket&);
 
@@ -308,6 +344,7 @@
     void IncrementRefCnt(const PSIPTable *psip) const;
     virtual bool DeleteCachedTable(PSIPTable *psip) const;
     void CachePAT(const ProgramAssociationTable *pat);
+    void CacheCAT(const ConditionalAccessTable *pat);
     void CachePMT(const ProgramMapTable *pmt);
 
   protected:
@@ -348,9 +385,11 @@
 
     // Table versions
     QMap<uint, int>           _pat_version;
+    QMap<uint, int>           _cat_version;
     QMap<uint, int>           _pmt_version;
 
     sections_map_t            _pat_section_seen;
+    sections_map_t            _cat_section_seen;
     sections_map_t            _pmt_section_seen;
 
     // PSIP construction
@@ -360,6 +399,7 @@
     bool                             _cache_tables;
     mutable QMutex                   _cache_lock;
     mutable pat_cache_t              _cached_pats;
+    mutable cat_cache_t              _cached_cats;
     mutable pmt_cache_t              _cached_pmts;
     mutable psip_refcnt_map_t        _cached_ref_cnt;
     mutable psip_refcnt_map_t        _cached_slated_for_deletion;
