diff --git a/mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp b/mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp
index 3f02209..cbb685c 100644
--- a/mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp
+++ b/mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp
@@ -253,56 +253,7 @@ void ChannelScanSM::SetAnalog(bool is_analog)
 
 void ChannelScanSM::HandleAllGood(void)
 {
-    QMutexLocker locker(&lock);
-
-    QString cur_chan = (*current).FriendlyName;
-    QStringList list = cur_chan.split(" ", QString::SkipEmptyParts);
-    QString freqid = (list.size() >= 2) ? list[1] : cur_chan;
-
-    bool ok = false;
-
-    QString msg = QObject::tr("Updated Channel %1").arg(cur_chan);
-
-    if (!ChannelUtil::FindChannel(sourceID, freqid))
-    {
-        int chanid = ChannelUtil::CreateChanID(sourceID, freqid);
-
-        QString callsign = QString("%1-%2")
-            .arg(ChannelUtil::GetUnknownCallsign()).arg(chanid);
-
-        ok = ChannelUtil::CreateChannel(
-            0      /* mplexid */,
-            sourceID,
-            chanid,
-            callsign,
-            ""     /* service name       */,
-            freqid /* channum            */,
-            0      /* service id         */,
-            0      /* ATSC major channel */,
-            0      /* ATSC minor channel */,
-            false  /* use on air guide   */,
-            false  /* hidden             */,
-            false  /* hidden in guide    */,
-            freqid);
-
-        msg = (ok) ?
-            QObject::tr("Added Channel %1").arg(cur_chan) :
-            QObject::tr("Failed to add channel %1").arg(cur_chan);
-    }
-    else
-    {
-        // nothing to do here, XMLTV & DataDirect have better info
-    }
-
-    scan_monitor->ScanAppendTextToLog(msg);
-
-    // tell UI we are done with these channels
-    if (scanning)
-    {
-        UpdateScanPercentCompleted();
-        waitingForTables = false;
-        nextIt = current.nextTransport();
-    }
+    postEvent(Event(kEventReceivedAllGood));
 }
 
 /**
@@ -360,202 +311,79 @@ bool ChannelScanSM::ScanExistingTransports(uint sourceid, bool follow_nit)
 
 void ChannelScanSM::HandlePAT(const ProgramAssociationTable *pat)
 {
-    QMutexLocker locker(&lock);
-
-    LOG(VB_CHANSCAN, LOG_INFO, LOC +
-        QString("Got a Program Association Table for %1")
-            .arg((*current).FriendlyName) + "\n" + pat->toString());
-
-    // Add pmts to list, so we can do MPEG scan properly.
-    ScanStreamData *sd = GetDTVSignalMonitor()->GetScanStreamData();
-    for (uint i = 0; i < pat->ProgramCount(); i++)
-    {
-        if (pat->ProgramPID(i)) // don't add NIT "program", MPEG/ATSC safe.
-            sd->AddListeningPID(pat->ProgramPID(i));
-    }
+    Event event(kEventReceivedPAT);
+    event.table = new ProgramAssociationTable(*pat);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandlePMT(uint, const ProgramMapTable *pmt)
 {
-    QMutexLocker locker(&lock);
-
-    LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got a Program Map Table for %1")
-            .arg((*current).FriendlyName) + "\n" + pmt->toString());
-
-    if (!currentTestingDecryption && pmt->IsEncrypted(GetDTVChannel()->GetSIStandard()))
-        currentEncryptionStatus[pmt->ProgramNumber()] = kEncUnknown;
+    Event event(kEventReceivedPMT);
+    event.table = new ProgramMapTable(*pmt);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandleVCT(uint, const VirtualChannelTable *vct)
 {
-    QMutexLocker locker(&lock);
+    Event event(kEventReceivedVCT);
+    switch( vct->TableID() )
+    {
+    case TableID::TVCT:
+        event.table = new TerrestrialVirtualChannelTable(*vct);
+        break;
 
-    LOG(VB_CHANSCAN, LOG_INFO, LOC +
-        QString("Got a Virtual Channel Table for %1")
-            .arg((*current).FriendlyName) + "\n" + vct->toString());
+    case TableID::CVCT:
+        event.table = new CableVirtualChannelTable(*vct);
+        break;
 
-    for (uint i = 0; !currentTestingDecryption && i < vct->ChannelCount(); i++)
-    {
-        if (vct->IsAccessControlled(i))
-        {
-            currentEncryptionStatus[vct->ProgramNumber(i)] = kEncUnknown;
-        }
+    default: assert(0);
     }
-
-    UpdateChannelInfo(true);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandleMGT(const MasterGuideTable *mgt)
 {
-    QMutexLocker locker(&lock);
-
-    LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got the Master Guide for %1")
-            .arg((*current).FriendlyName) + "\n" + mgt->toString());
-
-    UpdateChannelInfo(true);
+    Event event(kEventReceivedMGT);
+    event.table = new MasterGuideTable(*mgt);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandleSDT(uint tsid, const ServiceDescriptionTable *sdt)
 {
-    QMutexLocker locker(&lock);
-
-    LOG(VB_CHANSCAN, LOG_INFO, LOC +
-        QString("Got a Service Description Table for %1")
-            .arg((*current).FriendlyName) + "\n" + sdt->toString());
-
-    // If this is Astra 28.2 add start listening for Freesat BAT and SDTo
-    if (!setOtherTables && (sdt->OriginalNetworkID() == 2 ||
-        sdt->OriginalNetworkID() == 59))
-    {
-        GetDTVSignalMonitor()->GetScanStreamData()->
-                               SetFreesatAdditionalSI(true);
-        setOtherTables = true;
-        // The whole BAT & SDTo group comes round in 10s
-        otherTableTimeout = 10000; 
-        // Delay processing the SDT until we've seen BATs and SDTos
-        otherTableTime = timer.elapsed() + otherTableTimeout;
-
-        LOG(VB_CHANSCAN, LOG_INFO, LOC +
-            QString("SDT has OriginalNetworkID %1, look for "
-                    "additional Freesat SI").arg(sdt->OriginalNetworkID()));
-    }
-
-    if ((uint)timer.elapsed() < otherTableTime)
-    {
-        // Set the version for the SDT so we see it again.
-        GetDTVSignalMonitor()->GetDVBStreamData()->SetVersionSDT(sdt->TSID(), -1, 0);
-    }
-
-    uint id = sdt->OriginalNetworkID() << 16 | sdt->TSID();
-    ts_scanned.insert(id);
-
-    for (uint i = 0; !currentTestingDecryption && i < sdt->ServiceCount(); i++)
-    {
-        if (sdt->IsEncrypted(i))
-        {
-            currentEncryptionStatus[sdt->ServiceID(i)] = kEncUnknown;
-        }
-    }
-
-    UpdateChannelInfo(true);
+    Event event(kEventReceivedSDT);
+    event.tsid = tsid;
+    event.table = new ServiceDescriptionTable(*sdt);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandleNIT(const NetworkInformationTable *nit)
 {
-    QMutexLocker locker(&lock);
-
-    LOG(VB_CHANSCAN, LOG_INFO, LOC +
-        QString("Got a Network Information Table for %1")
-            .arg((*current).FriendlyName) + "\n" + nit->toString());
-
-    UpdateChannelInfo(true);
+    Event event(kEventReceivedNIT);
+    event.table = new NetworkInformationTable(*nit);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandleBAT(const BouquetAssociationTable *bat)
 {
-    QMutexLocker locker(&lock);
-
-    LOG(VB_CHANSCAN, LOG_INFO, LOC + "Got a Bouquet Association Table\n" +
-        bat->toString());
-
-    otherTableTime = timer.elapsed() + otherTableTimeout;
-
-    for (uint i = 0; i < bat->TransportStreamCount(); i++)
-    {
-        uint tsid = bat->TSID(i);
-        uint netid = bat->OriginalNetworkID(i);
-        desc_list_t parsed =
-            MPEGDescriptor::Parse(bat->TransportDescriptors(i),
-                                  bat->TransportDescriptorsLength(i));
-        // Look for default authority
-        const unsigned char *def_auth =
-            MPEGDescriptor::Find(parsed, DescriptorID::default_authority);
-        const unsigned char *serv_list =
-            MPEGDescriptor::Find(parsed, DescriptorID::service_list);
-
-        if (def_auth && serv_list)
-        {
-            DefaultAuthorityDescriptor authority(def_auth);
-            ServiceListDescriptor services(serv_list);
-
-            for (uint j = 0; j < services.ServiceCount(); j++)
-            {
-                // If the default authority is given in the SDT this
-                // overrides any definition in the BAT (or in the NIT)
-                LOG(VB_CHANSCAN, LOG_INFO, LOC +
-                    QString("found default authority(BAT) for service %1 %2 %3")
-                        .arg(netid).arg(tsid).arg(services.ServiceID(j)));
-               uint64_t index = ((uint64_t)netid << 32) | (tsid << 16) |
-                                 services.ServiceID(j);
-               if (! defAuthorities.contains(index))
-                   defAuthorities[index] = authority.DefaultAuthority();
-            }
-        }
-    }
+    Event event(kEventReceivedBAT);
+    event.table = new BouquetAssociationTable(*bat);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandleSDTo(uint tsid, const ServiceDescriptionTable *sdt)
 {
-    QMutexLocker locker(&lock);
-
-    LOG(VB_CHANSCAN, LOG_INFO, LOC +
-        "Got a Service Description Table (other)\n" + sdt->toString());
-
-    otherTableTime = timer.elapsed() + otherTableTimeout;
-
-    uint netid = sdt->OriginalNetworkID();
-
-    for (uint i = 0; i < sdt->ServiceCount(); i++)
-    {
-        uint serviceId = sdt->ServiceID(i);
-        desc_list_t parsed =
-            MPEGDescriptor::Parse(sdt->ServiceDescriptors(i),
-                                  sdt->ServiceDescriptorsLength(i));
-        // Look for default authority
-        const unsigned char *def_auth =
-            MPEGDescriptor::Find(parsed, DescriptorID::default_authority);
-        if (def_auth)
-        {
-            DefaultAuthorityDescriptor authority(def_auth);
-            LOG(VB_CHANSCAN, LOG_INFO, LOC +
-                QString("found default authority(SDTo) for service %1 %2 %3")
-                    .arg(netid).arg(tsid).arg(serviceId));
-            defAuthorities[((uint64_t)netid << 32) | (tsid << 16) | serviceId] =
-                authority.DefaultAuthority();
-        }
-    }
+    Event event(kEventReceivedSDTo);
+    event.tsid = tsid;
+    event.table = new ServiceDescriptionTable(*sdt);
+    postEvent(event);
 }
 
 void ChannelScanSM::HandleEncryptionStatus(uint pnum, bool encrypted)
 {
-    QMutexLocker locker(&lock);
-
-    currentEncryptionStatus[pnum] = encrypted ? kEncEncrypted : kEncDecrypted;
-
-    if (kEncDecrypted == currentEncryptionStatus[pnum])
-        currentTestingDecryption = false;
-
-    UpdateChannelInfo(true);
+    Event event(kEventEncryptionStatus);
+    event.pnum = pnum;
+    event.encrypted = encrypted;
+    postEvent(event);
 }
 
 bool ChannelScanSM::TestNextProgramEncryption(void)
@@ -1478,10 +1306,14 @@ void ChannelScanSM::run(void)
 
     while (!threadExit)
     {
+        Event event;
+        if( waitEvent(event, 50) )
+        {
+            processEvent(event);
+        }
+        
         if (scanning)
             HandleActiveScan();
-
-        usleep(10 * 1000);
     }
 
     LOG(VB_CHANSCAN, LOG_INFO, LOC + "run -- end");
@@ -1571,8 +1403,6 @@ bool ChannelScanSM::HasTimedOut(void)
  */
 void ChannelScanSM::HandleActiveScan(void)
 {
-    QMutexLocker locker(&lock);
-
     bool do_post_insertion = waitingForTables;
 
     if (!HasTimedOut())
@@ -1585,9 +1415,7 @@ void ChannelScanSM::HandleActiveScan(void)
             return;
 
         // Stop signal monitor for previous transport
-        locker.unlock();
         signalMonitor->Stop();
-        locker.relock();
     }
 
     if (0 == nextIt.offset() && nextIt == scanTransports.begin())
@@ -2077,3 +1905,311 @@ bool ChannelScanSM::CheckImportedList(
 
     return found;
 }
+
+void ChannelScanSM::postEvent(const Event &event)
+{
+    {
+        QMutexLocker locker(&eventLock);
+        eventQueue.push_back(event);
+    }
+    eventSemaphore.release();
+}
+
+bool ChannelScanSM::waitEvent(Event &event, int32_t timeout)
+{
+    if( eventSemaphore.tryAcquire(1, timeout) )
+    {
+        QMutexLocker locker(&eventLock);
+        event = eventQueue.front();
+        eventQueue.pop_front();
+        return true;
+    }
+    return false;
+}
+void ChannelScanSM::processEvent(Event &event)
+{
+    if( !scanning )
+    {
+        delete event.table;
+        event.table = NULL;
+        return;
+    }
+
+    assert( event.type!=kEventInvalid );
+    switch( event.type )
+    {
+        case kEventInvalid:
+            //TODO:: print error or assert
+            break;
+        case kEventWakeup:
+            UpdateChannelInfo(true);
+            break;
+        case kEventReceivedPAT:
+            processPAT(static_cast<ProgramAssociationTable *>(event.table));
+            break;
+        case kEventReceivedPMT:
+            processPMT(static_cast<ProgramMapTable *>(event.table));
+            break;
+        case kEventEncryptionStatus:
+            processEncryptionStatus(event.pnum, event.encrypted);
+            break;
+        case kEventReceivedMGT:
+            processMGT(static_cast<MasterGuideTable *>(event.table));
+            break;
+        case kEventReceivedVCT:
+            processVCT(event.tsid, static_cast<VirtualChannelTable *>(event.table));
+            break;
+        case kEventReceivedNIT:
+            processNIT(static_cast<NetworkInformationTable *>(event.table));
+            break;
+        case kEventReceivedSDT:
+            processSDT(event.tsid, static_cast<ServiceDescriptionTable *>(event.table));
+            break;
+        case kEventReceivedSDTo:
+            processSDTo(event.tsid, static_cast<ServiceDescriptionTable *>(event.table));
+            break;
+        case kEventReceivedBAT:
+            processBAT(static_cast<BouquetAssociationTable *>(event.table));
+            break;
+        case kEventReceivedAllGood:
+            processAllGood();
+            break;
+     }
+
+    delete event.table;
+    event.table = NULL;
+}
+
+void ChannelScanSM::processPAT(const ProgramAssociationTable *pat)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC +
+        QString("Got a Program Association Table for %1")
+            .arg((*current).FriendlyName) + "\n" + pat->toString());
+
+    // Add pmts to list, so we can do MPEG scan properly.
+    ScanStreamData *sd = GetDTVSignalMonitor()->GetScanStreamData();
+    for (uint i = 0; i < pat->ProgramCount(); i++)
+    {
+        if (pat->ProgramPID(i)) // don't add NIT "program", MPEG/ATSC safe.
+            sd->AddListeningPID(pat->ProgramPID(i));
+    }
+}
+
+void ChannelScanSM::processPMT(const ProgramMapTable*pmt)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got a Program Map Table for %1")
+            .arg((*current).FriendlyName) + "\n" + pmt->toString());
+
+    if (!currentTestingDecryption && pmt->IsEncrypted(GetDTVChannel()->GetSIStandard()))
+        currentEncryptionStatus[pmt->ProgramNumber()] = kEncUnknown;
+}
+
+void ChannelScanSM::processEncryptionStatus(uint pnum, bool encrypted)
+{
+    currentEncryptionStatus[pnum] = encrypted ? kEncEncrypted : kEncDecrypted;
+
+    if (kEncDecrypted == currentEncryptionStatus[pnum])
+        currentTestingDecryption = false;
+
+    UpdateChannelInfo(true);
+}
+
+void ChannelScanSM::processMGT(const MasterGuideTable *mgt)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got the Master Guide for %1")
+            .arg((*current).FriendlyName) + "\n" + mgt->toString());
+
+    UpdateChannelInfo(true);
+}
+
+void ChannelScanSM::processVCT(uint tsid, const VirtualChannelTable *vct)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC +
+        QString("Got a Virtual Channel Table for %1")
+            .arg((*current).FriendlyName) + "\n" + vct->toString());
+
+    for (uint i = 0; !currentTestingDecryption && i < vct->ChannelCount(); i++)
+    {
+        if (vct->IsAccessControlled(i))
+        {
+            currentEncryptionStatus[vct->ProgramNumber(i)] = kEncUnknown;
+        }
+    }
+
+    UpdateChannelInfo(true);
+}
+
+void ChannelScanSM::processNIT(const NetworkInformationTable *nit)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC +
+        QString("Got a Network Information Table for %1")
+            .arg((*current).FriendlyName) + "\n" + nit->toString());
+
+    UpdateChannelInfo(true);
+}
+
+void ChannelScanSM::processSDT(uint tsid, const ServiceDescriptionTable *sdt)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC +
+        QString("Got a Service Description Table for %1")
+            .arg((*current).FriendlyName) + "\n" + sdt->toString());
+
+    // If this is Astra 28.2 add start listening for Freesat BAT and SDTo
+    if (!setOtherTables && (sdt->OriginalNetworkID() == 2 ||
+        sdt->OriginalNetworkID() == 59))
+    {
+        GetDTVSignalMonitor()->GetScanStreamData()->
+                               SetFreesatAdditionalSI(true);
+        setOtherTables = true;
+        // The whole BAT & SDTo group comes round in 10s
+        otherTableTimeout = 10000; 
+        // Delay processing the SDT until we've seen BATs and SDTos
+        otherTableTime = timer.elapsed() + otherTableTimeout;
+
+        LOG(VB_CHANSCAN, LOG_INFO, LOC +
+            QString("SDT has OriginalNetworkID %1, look for "
+                    "additional Freesat SI").arg(sdt->OriginalNetworkID()));
+    }
+
+    if ((uint)timer.elapsed() < otherTableTime)
+    {
+        // Set the version for the SDT so we see it again.
+        GetDTVSignalMonitor()->GetDVBStreamData()->SetVersionSDT(sdt->TSID(), -1, 0);
+    }
+
+    uint id = sdt->OriginalNetworkID() << 16 | sdt->TSID();
+    ts_scanned.insert(id);
+
+    for (uint i = 0; !currentTestingDecryption && i < sdt->ServiceCount(); i++)
+    {
+        if (sdt->IsEncrypted(i))
+        {
+            currentEncryptionStatus[sdt->ServiceID(i)] = kEncUnknown;
+        }
+    }
+
+    UpdateChannelInfo(true);
+}
+
+void ChannelScanSM::processSDTo(uint tsid, const ServiceDescriptionTable *sdt)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC +
+        "Got a Service Description Table (other)\n" + sdt->toString());
+
+    otherTableTime = timer.elapsed() + otherTableTimeout;
+
+    uint netid = sdt->OriginalNetworkID();
+
+    for (uint i = 0; i < sdt->ServiceCount(); i++)
+    {
+        uint serviceId = sdt->ServiceID(i);
+        desc_list_t parsed =
+            MPEGDescriptor::Parse(sdt->ServiceDescriptors(i),
+                                  sdt->ServiceDescriptorsLength(i));
+        // Look for default authority
+        const unsigned char *def_auth =
+            MPEGDescriptor::Find(parsed, DescriptorID::default_authority);
+        if (def_auth)
+        {
+            DefaultAuthorityDescriptor authority(def_auth);
+            LOG(VB_CHANSCAN, LOG_INFO, LOC +
+                QString("found default authority(SDTo) for service %1 %2 %3")
+                    .arg(netid).arg(tsid).arg(serviceId));
+            defAuthorities[((uint64_t)netid << 32) | (tsid << 16) | serviceId] =
+                authority.DefaultAuthority();
+        }
+    }
+}
+
+void ChannelScanSM::processBAT(const BouquetAssociationTable *bat)
+{
+    LOG(VB_CHANSCAN, LOG_INFO, LOC + "Got a Bouquet Association Table\n" +
+        bat->toString());
+
+    otherTableTime = timer.elapsed() + otherTableTimeout;
+
+    for (uint i = 0; i < bat->TransportStreamCount(); i++)
+    {
+        uint tsid = bat->TSID(i);
+        uint netid = bat->OriginalNetworkID(i);
+        desc_list_t parsed =
+            MPEGDescriptor::Parse(bat->TransportDescriptors(i),
+                                  bat->TransportDescriptorsLength(i));
+        // Look for default authority
+        const unsigned char *def_auth =
+            MPEGDescriptor::Find(parsed, DescriptorID::default_authority);
+        const unsigned char *serv_list =
+            MPEGDescriptor::Find(parsed, DescriptorID::service_list);
+
+        if (def_auth && serv_list)
+        {
+            DefaultAuthorityDescriptor authority(def_auth);
+            ServiceListDescriptor services(serv_list);
+
+            for (uint j = 0; j < services.ServiceCount(); j++)
+            {
+                // If the default authority is given in the SDT this
+                // overrides any definition in the BAT (or in the NIT)
+                LOG(VB_CHANSCAN, LOG_INFO, LOC +
+                    QString("found default authority(BAT) for service %1 %2 %3")
+                        .arg(netid).arg(tsid).arg(services.ServiceID(j)));
+               uint64_t index = ((uint64_t)netid << 32) | (tsid << 16) |
+                                 services.ServiceID(j);
+               if (! defAuthorities.contains(index))
+                   defAuthorities[index] = authority.DefaultAuthority();
+            }
+        }
+    }
+}
+
+void ChannelScanSM::processAllGood()
+{
+    QString cur_chan = (*current).FriendlyName;
+    QStringList list = cur_chan.split(" ", QString::SkipEmptyParts);
+    QString freqid = (list.size() >= 2) ? list[1] : cur_chan;
+
+    bool ok = false;
+
+    QString msg = QObject::tr("Updated Channel %1").arg(cur_chan);
+
+    if (!ChannelUtil::FindChannel(sourceID, freqid))
+    {
+        int chanid = ChannelUtil::CreateChanID(sourceID, freqid);
+
+        QString callsign = QString("%1-%2")
+            .arg(ChannelUtil::GetUnknownCallsign()).arg(chanid);
+
+        ok = ChannelUtil::CreateChannel(
+            0      /* mplexid */,
+            sourceID,
+            chanid,
+            callsign,
+            ""     /* service name       */,
+            freqid /* channum            */,
+            0      /* service id         */,
+            0      /* ATSC major channel */,
+            0      /* ATSC minor channel */,
+            false  /* use on air guide   */,
+            false  /* hidden             */,
+            false  /* hidden in guide    */,
+            freqid);
+
+        msg = (ok) ?
+            QObject::tr("Added Channel %1").arg(cur_chan) :
+            QObject::tr("Failed to add channel %1").arg(cur_chan);
+    }
+    else
+    {
+        // nothing to do here, XMLTV & DataDirect have better info
+    }
+
+    scan_monitor->ScanAppendTextToLog(msg);
+
+    // tell UI we are done with these channels
+    if (scanning)
+    {
+        UpdateScanPercentCompleted();
+        waitingForTables = false;
+        nextIt = current.nextTransport();
+    }
+}
diff --git a/mythtv/libs/libmythtv/channelscan/channelscan_sm.h b/mythtv/libs/libmythtv/channelscan/channelscan_sm.h
index 813113a..2c90a9b 100644
--- a/mythtv/libs/libmythtv/channelscan/channelscan_sm.h
+++ b/mythtv/libs/libmythtv/channelscan/channelscan_sm.h
@@ -37,6 +37,7 @@
 #include <QPair>
 #include <QMap>
 #include <QSet>
+#include <QSemaphore>
 
 // MythTV includes
 #include "frequencytables.h"
@@ -154,6 +155,35 @@ class ChannelScanSM : public MPEGStreamListener,
     void HandleBAT(const BouquetAssociationTable*);
 
   private:
+    enum EventType
+    {
+        kEventInvalid,
+        kEventWakeup,
+        kEventReceivedPAT,
+        kEventReceivedPMT,
+        kEventEncryptionStatus,
+        kEventReceivedMGT,
+        kEventReceivedVCT,
+        kEventReceivedNIT,
+        kEventReceivedSDT,
+        kEventReceivedSDTo,
+        kEventReceivedBAT,
+        kEventReceivedAllGood,
+    };
+    struct Event
+    {
+        Event() : type(kEventInvalid), tsid(0), table(NULL), encrypted(false) {}
+        Event(EventType type_) : type(type_), tsid(0), table(NULL), encrypted(false) {}
+        EventType   type;
+        union
+        {
+            uint    tsid;
+            uint    pnum;
+        };
+        PSIPTable  *table;
+        bool        encrypted;
+    };
+  private:
     // some useful gets
     DTVChannel       *GetDTVChannel(void);
     const DTVChannel *GetDTVChannel(void) const;
@@ -195,6 +225,22 @@ class ChannelScanSM : public MPEGStreamListener,
 
     static QString loc(const ChannelScanSM*);
 
+    void postEvent(const Event &event);
+    bool waitEvent(Event &event, int32_t timeout);
+
+    void processEvent(Event &event);
+    void processPAT(const ProgramAssociationTable*);
+    void processPMT(const ProgramMapTable*);
+    void processEncryptionStatus(uint pnum, bool encrypted);
+
+    void processMGT(const MasterGuideTable*);
+    void processVCT(uint tsid, const VirtualChannelTable*);
+    void processNIT(const NetworkInformationTable*);
+    void processSDT(uint tsid, const ServiceDescriptionTable*);
+    void processSDTo(uint tsid, const ServiceDescriptionTable*);
+    void processBAT(const BouquetAssociationTable*);
+    void processAllGood();
+
     static const uint kDVBTableTimeout;
     static const uint kATSCTableTimeout;
     static const uint kMPEGTableTimeout;
@@ -217,9 +263,6 @@ class ChannelScanSM : public MPEGStreamListener,
     // Optional info
     DTVTunerType      scanDTVTunerType;
 
-    /// The big lock
-    mutable QMutex    lock;
-
     // State
     bool              scanning;
     volatile bool     threadExit;
@@ -248,6 +291,11 @@ class ChannelScanSM : public MPEGStreamListener,
 
     /// Scanner thread, runs ChannelScanSM::run()
     MThread          *scannerThread;
+
+    QSemaphore       eventSemaphore;
+    QMutex           eventLock;
+    QVector<Event>   eventQueue;
+    
 };
 
 inline void ChannelScanSM::UpdateScanPercentCompleted(void)
