=== libs/libmythtv/eitcache.h
==================================================================
--- libs/libmythtv/eitcache.h	(revision 225)
+++ libs/libmythtv/eitcache.h	(revision 242)
@@ -1,4 +1,4 @@
-/*
+/* -*- Mode: c++ -*-
  * Copyright 2006 (C) Stuart Auchterlonie <stuarta at squashedfrog.net>
  * License: GPL v2
  */
@@ -10,6 +10,7 @@
 
 // Qt headers
 #include <qmap.h>
+#include <qmutex.h>
 #include <qstring.h>
 
 typedef QMap<uint64_t, uint64_t> key_map_t;
@@ -20,24 +21,31 @@
     EITCache();
    ~EITCache() {};
 
-    bool IsNewEIT(const uint tsid,       const uint eventid,
-                  const uint serviceid,  const uint tableid,
-                  const uint version,
-                  const unsigned char * const eitdata,
-                  const uint eitlength);
+    bool IsNewEIT(const uint onid, const uint tsid,
+                  const uint serviceid, const uint eventid,
+                  const uint tableid, const uint version, 
+                  const uint endtime);
 
+    uint PruneOldEntries(uint timestamp);
+
     void ResetStatistics(void);
-    QString GetStatistics(void) const;
+    QString GetStatistics(void);
 
   private:
     // event key cache
     key_map_t   eventMap;
 
+    QMutex      eventMapLock;
+
+    uint        lastPruneTime;
+
     // statistics
     uint        accessCnt;
     uint        hitCnt;
     uint        tblChgCnt;
     uint        verChgCnt;
+    uint        pruneCnt;
+    uint        prunedHitCnt;
 
     static const uint kVersionMax;
 };
=== libs/libmythtv/eithelper.cpp
==================================================================
--- libs/libmythtv/eithelper.cpp	(revision 225)
+++ libs/libmythtv/eithelper.cpp	(revision 242)
@@ -26,8 +26,8 @@
 #define LOC QString("EITHelper: ")
 #define LOC_ERR QString("EITHelper, Error: ")
 
-EITHelper::EITHelper() :
-    eitfixup(new EITFixUp()), eitcache(new EITCache()),
+EITHelper::EITHelper(EITCache *cache) :
+    eitfixup(new EITFixUp()), eitcache(cache),
     gps_offset(-1 * GPS_LEAP_SECONDS),          utc_offset(0),
     sourceid(0)
 {
@@ -209,12 +209,12 @@
 
     for (uint i = 0; i < eit->EventCount(); i++)
     {
+        QDateTime starttime = MythUTCToLocal(eit->StartTimeUTC(i));
         // Skip event if we have already processed it before...
-        if (!eitcache->IsNewEIT(
-                eit->TSID(),      eit->EventID(i),
-                eit->ServiceID(), eit->TableID(),
-                eit->Version(),
-                eit->Descriptors(i), eit->DescriptorsLength(i)))
+        if (!eitcache->IsNewEIT(eit->OriginalNetworkID(), eit->TSID(),
+                                eit->ServiceID(), eit->EventID(i),
+                                eit->TableID(), eit->Version(),
+                                starttime.toTime_t()+eit->DurationInSeconds(i)))
         {
             continue;
         }
@@ -320,7 +320,6 @@
         if (!chanid)
             continue;
 
-        QDateTime starttime = MythUTCToLocal(eit->StartTimeUTC(i));
         EITFixUp::TimeFix(starttime);
         QDateTime endtime   = starttime.addSecs(eit->DurationInSeconds(i));
 
=== libs/libmythtv/eitcache.cpp
==================================================================
--- libs/libmythtv/eitcache.cpp	(revision 225)
+++ libs/libmythtv/eitcache.cpp	(revision 242)
@@ -1,18 +1,30 @@
 // -*- Mode: c++ -*-
 /*
  * Copyright 2006 (C) Stuart Auchterlonie <stuarta at squashedfrog.net>
+ * Copyright 2006 (C) Janne Grunau <janne-mythtv at grunau.be>
  * License: GPL v2
  */
 
+
+#include <iostream>
+using namespace std;
+
+#include <qdatetime.h>
+
 #include "eitcache.h"
-#include <stdio.h>
+#include "mythcontext.h"
 
+#define LOC "EITCache: "
+
 // Highest version number. version is 5bits
 const uint EITCache::kVersionMax = 31;
 
 EITCache::EITCache()
-    : accessCnt(0), hitCnt(0), tblChgCnt(0), verChgCnt(0)
+    : accessCnt(0), hitCnt(0), tblChgCnt(0), verChgCnt(0),
+      pruneCnt(0), prunedHitCnt(0)
 {
+    // six hours ago
+    lastPruneTime = QDateTime::currentDateTime().addSecs(-21600).toTime_t();
 }
 
 void EITCache::ResetStatistics(void)
@@ -21,26 +33,31 @@
     hitCnt    = 0;
     tblChgCnt = 0;
     verChgCnt = 0;
+    pruneCnt  = 0;
+    prunedHitCnt = 0;
 }
 
-QString EITCache::GetStatistics(void) const
+QString EITCache::GetStatistics(void)
 {
+    QMutexLocker locker(&eventMapLock);
     return QString(
         "EITCache::statistics: Accesses: %1, Hits: %2, "
-        "Table Upgrades %3, New Versions: %4")
-        .arg(accessCnt).arg(hitCnt).arg(tblChgCnt).arg(verChgCnt);
+        "Table Upgrades %3, New Versions: %4, Entries: %5 "
+        "Pruned entries: %6, pruned Hits: %7.")
+        .arg(accessCnt).arg(hitCnt).arg(tblChgCnt).arg(verChgCnt)
+        .arg(eventMap.size()).arg(pruneCnt).arg(prunedHitCnt);
 }
 
-static uint64_t construct_key(uint tsid, uint eventid, uint serviceid)
+static uint64_t construct_key(uint onid, uint tsid, uint serviceid, uint eventid)
 {
-    return (((uint64_t) tsid      << 48) | ((uint64_t) eventid   << 32) |
-            ((uint64_t) serviceid << 16));
+    return (((uint64_t) onid      << 48) | ((uint64_t) tsid    << 32) |
+            ((uint64_t) serviceid << 16) | ((uint64_t) eventid      ));
 }
 
-static uint64_t construct_sig(uint tableid, uint version, uint chksum)
+static uint64_t construct_sig(uint tableid, uint version, uint endtime)
 {
     return (((uint64_t) tableid   << 40) | ((uint64_t) version   << 32) |
-            ((uint64_t) chksum));
+            ((uint64_t) endtime));
 }
 
 static uint extract_table_id(uint64_t sig)
@@ -53,15 +70,28 @@
     return (sig >> 32) & 0x1f;
 }
 
-bool EITCache::IsNewEIT(const uint tsid,      const uint eventid,
-                        const uint serviceid, const uint tableid,
-                        const uint version,
-                        const unsigned char * const /*eitdata*/,
-                        const uint /*eitlength*/)
+static uint extract_endtime(uint64_t sig)
 {
+    return sig & 0xffffffff;
+}
+
+bool EITCache::IsNewEIT(const uint onid, const uint tsid,
+                        const uint serviceid, const uint eventid,
+                        const uint tableid, const uint version, 
+                        const uint endtime)
+{
     accessCnt++;
 
-    uint64_t key = construct_key(tsid, eventid, serviceid);
+    // don't readd pruned entries
+    if (endtime < lastPruneTime)
+    {
+        prunedHitCnt++;
+        return false;
+    }
+
+    uint64_t key = construct_key(onid, tsid, serviceid, eventid);
+
+    QMutexLocker locker(&eventMapLock);
     key_map_t::const_iterator it = eventMap.find(key);
 
     if (it != eventMap.end())
@@ -87,9 +117,39 @@
         }
     }
 
-    eventMap[key] = construct_sig(tableid, version, 0 /*chksum*/);
+    eventMap[key] = construct_sig(tableid, version, endtime);
 
     return true;
 }
 
+uint EITCache::PruneOldEntries(uint timestamp)
+{
+    QDateTime extime = QDateTime();
+    extime.setTime_t(timestamp);
+    VERBOSE(VB_EIT, LOC + QString("Pruning all entries that ended before %1,")
+            .arg(extime.toString()));
+
+    QMutexLocker locker(&eventMapLock);
+    uint size = eventMap.size();
+
+    key_map_t::iterator it = eventMap.begin();
+    while (it != eventMap.end())
+    {
+        if (extract_endtime(*it) < timestamp)
+        {
+            key_map_t::iterator tmp = it;
+            ++tmp;
+            eventMap.erase(it);
+            it = tmp;
+        }
+        else
+            ++it;
+    }
+
+    lastPruneTime = timestamp;
+    size -= eventMap.size();
+    prunedHitCnt += size;
+    return size;
+}
+
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
=== libs/libmythtv/eithelper.h
==================================================================
--- libs/libmythtv/eithelper.h	(revision 225)
+++ libs/libmythtv/eithelper.h	(revision 242)
@@ -51,7 +51,7 @@
 class EITHelper
 {
   public:
-    EITHelper();
+    EITHelper(EITCache *cache);
     virtual ~EITHelper();
 
     uint GetListSize(void) const;
=== libs/libmythtv/eitscanner.h
==================================================================
--- libs/libmythtv/eitscanner.h	(revision 225)
+++ libs/libmythtv/eitscanner.h	(revision 242)
@@ -17,6 +17,7 @@
 class EITHelper;
 class dvb_channel_t;
 class ProgramMapTable;
+class EITCache;
 
 class EITSource
 {
@@ -28,16 +29,18 @@
 class EITScanner
 {
   public:
-    EITScanner();
+    EITScanner(EITCache *cache);
     ~EITScanner() { TeardownAll(); }
 
     void StartPassiveScan(ChannelBase*, EITSource*, bool ignore_source);
     void StopPassiveScan(void);
 
+#if 0
     void StartActiveScan(TVRec*, uint max_seconds_per_source,
                          bool ignore_source);
 
     void StopActiveScan(void);        
+#endif
 
   private:
     void TeardownAll(void);
@@ -56,10 +59,12 @@
 
     TVRec           *rec;
     bool             activeScan;
+#if 0
     QDateTime        activeScanNextTrig;
     uint             activeScanTrigTime;
     QStringList      activeScanChannels;
     QStringList::iterator activeScanNextChan;
+#endif 
 
     bool             ignore_source;
 
=== libs/libmythtv/tv_rec.cpp
==================================================================
--- libs/libmythtv/tv_rec.cpp	(revision 225)
+++ libs/libmythtv/tv_rec.cpp	(revision 242)
@@ -117,6 +117,7 @@
        // Various components TVRec coordinates
     : recorder(NULL), channel(NULL), signalMonitor(NULL),
       scanner(NULL), dvbsiparser(NULL), dummyRecorder(NULL),
+      eitcache(NULL),
       // Configuration variables from database
       transcodeFirst(false), earlyCommFlag(false), runJobOnHostOnly(false),
       audioSampleRateDB(0), overRecordSecNrml(0), overRecordSecCat(0),
@@ -151,8 +152,8 @@
     bool init_run = false;
     if (genOpt.cardtype == "DVB")
     {
-        if (!scanner)
-            scanner = new EITScanner();
+        if (!scanner && eitcache)
+            scanner = new EITScanner(eitcache);
 
 #ifdef USING_DVB
         channel = new DVBChannel(genOpt.videodev.toInt(), this);
@@ -726,7 +727,7 @@
     // to avoid race condition with it's tuning requests.
     if (HasFlags(kFlagEITScannerRunning))
     {
-        scanner->StopActiveScan();
+        scanner->StopPassiveScan();
         ClearFlags(kFlagEITScannerRunning);
     }
 
@@ -770,11 +771,9 @@
     eitScanStartTime = QDateTime::currentDateTime();    
     if ((internalState == kState_None) && (genOpt.cardtype == "DVB"))
     {
-        // Add some randomness to avoid all cards starting
-        // EIT scanning at nearly the same time.
+        // start the eit scanning only if the encoder is idle_start sec idle
         uint idle_start = gContext->GetNumSetting("EITCrawIdleStart", 60);
-        uint timeout = idle_start + (random() % 59);
-        eitScanStartTime = eitScanStartTime.addSecs(timeout);
+        eitScanStartTime = eitScanStartTime.addSecs(idle_start);
     }
     else
         eitScanStartTime = eitScanStartTime.addYears(1);
@@ -906,10 +905,10 @@
 #ifdef USING_HDHOMERUN
         HDHRChannel  *hdhr_channel  = GetHDHRChannel();
         HDHRRecorder *hdhr_recorder = GetHDHRRecorder();
-        if (hdhr_channel && hdhr_recorder && !scanner)
+        if (hdhr_channel && hdhr_recorder && !scanner && eitcache)
         {
             uint ignore = gContext->GetNumSetting("EITIgnoresSource", 0);
-            scanner = new EITScanner();
+            scanner = new EITScanner(eitcache);
             scanner->StartPassiveScan(hdhr_channel, hdhr_recorder, ignore);
         }
 #endif // USING_HDHOMERUN
@@ -1246,11 +1245,9 @@
     SetFlags(kFlagRunMainLoop);
     ClearFlags(kFlagExitPlayer | kFlagFinishRecording);
 
-    // Add some randomness to avoid all cards starting
-    // EIT scanning at nearly the same time.
+    // start the eit scanning only if the encoder is idle_start sec idle
     uint idle_start = gContext->GetNumSetting("EITCrawIdleStart", 60);
-    uint timeout = idle_start + (random() % 59);
-    eitScanStartTime = QDateTime::currentDateTime().addSecs(timeout);
+    eitScanStartTime = QDateTime::currentDateTime().addSecs(idle_start);
 
     while (HasFlags(kFlagRunMainLoop))
     {
@@ -1380,6 +1377,7 @@
                         "for all sources on this card.");
                 eitScanStartTime = eitScanStartTime.addYears(1);
             }
+/*
             else
             {
                 uint ttMin = gContext->GetNumSetting("EITTransportTimeout", 5);
@@ -1388,6 +1386,7 @@
                 SetFlags(kFlagEITScannerRunning);
                 eitScanStartTime = QDateTime::currentDateTime().addYears(1);
             }
+*/
         }
 
         // We should be no more than a few thousand milliseconds,
@@ -3149,6 +3148,31 @@
     VERBOSE(VB_RECORD, LOC + "SetChannel()" + " -- end");
 }
 
+/** \fn TVRec::StartEITScan(QString)
+ *  \brief Starts EIT scanning on the named channel and the current tuner.
+ *
+ *  \param name channum of channel to scan on
+ */
+bool TVRec::StartEITScan(QString name)
+{
+    // initialize eit scanner if needed
+    if (!scanner && eitcache)
+        scanner = new EITScanner(eitcache);
+
+    // Is this enough to guard against channel changes while recording?
+    if (QDateTime::currentDateTime() > eitScanStartTime)
+    {
+        SetChannel(name, kFlagEITScan);
+        //TODO: check for successful tuning
+        return true;
+    }
+    VERBOSE(VB_EIT, LOC + QString("Now: %1 - start scan only after: %2")
+            .arg(QDateTime::currentDateTime().toString())
+            .arg(eitScanStartTime.toString()));
+
+    return false;
+}
+
 /** \fn TVRec::GetNextProgram(int,QString&,QString&,QString&,QString&,QString&,QString&,QString&,QString&,QString&,QString&,QString&,QString&)
  *  \brief Returns information about the program that would be seen if we changed
  *         the channel using ChangeChannel(int) with "direction".
@@ -3540,7 +3564,7 @@
 
     if (!(request.flags & kFlagEITScan) && HasFlags(kFlagEITScannerRunning))
     {
-        scanner->StopActiveScan();
+        scanner->StopPassiveScan();
         ClearFlags(kFlagEITScannerRunning);
     }
 
=== libs/libmythtv/eitscanner.cpp
==================================================================
--- libs/libmythtv/eitscanner.cpp	(revision 225)
+++ libs/libmythtv/eitscanner.cpp	(revision 242)
@@ -25,8 +25,8 @@
 QDateTime  EITScanner::resched_next_time      = QDateTime::currentDateTime();
 const uint EITScanner::kMinRescheduleInterval = 150;
 
-EITScanner::EITScanner()
-    : channel(NULL), eitSource(NULL), eitHelper(new EITHelper()),
+EITScanner::EITScanner(EITCache *cache)
+    : channel(NULL), eitSource(NULL), eitHelper(new EITHelper(cache)),
       exitThread(false), rec(NULL), activeScan(false)
 {
     QStringList langPref = iso639_get_language_list();
@@ -41,7 +41,7 @@
 
 void EITScanner::TeardownAll(void)
 {
-    StopActiveScan();
+    StopPassiveScan();
     if (!exitThread)
     {
         exitThread = true;
@@ -114,6 +114,7 @@
             RescheduleRecordings();
         }
 
+#if 0
         if (activeScan && (QDateTime::currentDateTime() > activeScanNextTrig))
         {
             // if there have been any new events, tell scheduler to run.
@@ -140,6 +141,7 @@
                 .addSecs(activeScanTrigTime);
             activeScanNextChan++;
         }
+#endif
 
         exitThreadCond.wait(200); // sleep up to 200 ms.
     }
@@ -209,6 +211,7 @@
     eitHelper->SetSourceID(0);
 }
 
+#if 0
 void EITScanner::StartActiveScan(TVRec *_rec, uint max_seconds_per_source,
                                  bool _ignore_source)
 {
@@ -272,3 +275,4 @@
     rec = NULL;
     StopPassiveScan();
 }
+#endif
=== libs/libmythtv/tv_rec.h
==================================================================
--- libs/libmythtv/tv_rec.h	(revision 225)
+++ libs/libmythtv/tv_rec.h	(revision 242)
@@ -20,6 +20,7 @@
 class QSocket;
 class NuppelVideoRecorder;
 class RingBuffer;
+class EITCache;
 class EITScanner;
 class DVBSIParser;
 class DummyDTVRecorder;
@@ -221,6 +222,8 @@
         { SetChannel(QString("NextChannel %1").arg((int)dir)); }
     void SetChannel(QString name, uint requestType = kFlagDetect);
 
+    bool StartEITScan(QString name);
+
     int SetSignalMonitoringRate(int msec, int notifyFrontend = 1);
     int ChangeColour(bool direction);
     int ChangeContrast(bool direction);
@@ -254,6 +257,8 @@
     void DVBGotPMT(void)
         { QMutexLocker lock(&stateChangeLock); triggerEventLoop.wakeAll(); }
 
+    void SetEITCache(EITCache *cache) { eitcache = cache; }
+
   public slots:
     void SignalMonitorAllGood() { triggerEventLoop.wakeAll(); }
     void deleteLater(void);
@@ -340,6 +345,7 @@
     RecorderBase     *recorder;
     ChannelBase      *channel;
     SignalMonitor    *signalMonitor;
+    EITCache         *eitcache;
     EITScanner       *scanner;
     DVBSIParser      *dvbsiparser;
     DummyDTVRecorder *dummyRecorder;
=== libs/libmyth/mythcontext.h
==================================================================
--- libs/libmyth/mythcontext.h	(revision 225)
+++ libs/libmyth/mythcontext.h	(revision 242)
@@ -229,7 +229,7 @@
  *   You must also update this value in
  *   mythplugins/mythweb/includes/mythbackend.php
  */
-#define MYTH_PROTO_VERSION "29"
+#define MYTH_PROTO_VERSION "30"
 
 /** \class MythContext
  *  \brief This class contains the runtime context for MythTV.
=== programs/mythbackend/encoderlink.h
==================================================================
--- programs/mythbackend/encoderlink.h	(revision 225)
+++ programs/mythbackend/encoderlink.h	(revision 242)
@@ -82,6 +82,7 @@
     void ToggleChannelFavorite(void);
     void ChangeChannel(int channeldirection);
     void SetChannel(const QString &name);
+    bool StartEITScan(const QString chanid);
     int ChangeContrast(bool direction);
     int ChangeBrightness(bool direction);
     int ChangeColour(bool direction);
=== programs/mythbackend/playbacksock.cpp
==================================================================
--- programs/mythbackend/playbacksock.cpp	(revision 225)
+++ programs/mythbackend/playbacksock.cpp	(revision 242)
@@ -285,3 +285,16 @@
     int ret = strlist[0].toInt();
     return ret;
 }
+
+bool PlaybackSock::StartEITScan(int capturecardnum, QString chanid)
+{
+    QStringList strlist = QString("QUERY_REMOTEENCODER %1").arg(capturecardnum);
+    strlist << "START_EIT_SCAN";
+    strlist << chanid;
+
+    SendReceiveStringList(strlist);
+
+    bool ret = strlist[0].toInt();
+    return ret;
+}
+
=== programs/mythbackend/mythbackend.pro
==================================================================
--- programs/mythbackend/mythbackend.pro	(revision 225)
+++ programs/mythbackend/mythbackend.pro	(revision 242)
@@ -13,11 +13,11 @@
 
 # Input
 HEADERS += autoexpire.h encoderlink.h filetransfer.h httpstatus.h mainserver.h
-HEADERS += playbacksock.h scheduler.h server.h housekeeper.h
+HEADERS += playbacksock.h scheduler.h server.h housekeeper.h eitactivescanner.h
 
 SOURCES += autoexpire.cpp encoderlink.cpp filetransfer.cpp httpstatus.cpp
 SOURCES += main.cpp mainserver.cpp playbacksock.cpp scheduler.cpp server.cpp
-SOURCES += housekeeper.cpp
+SOURCES += housekeeper.cpp eitactivescanner.cpp
 
 using_oss:DEFINES += USING_OSS
 
=== programs/mythbackend/mainserver.cpp
==================================================================
--- programs/mythbackend/mainserver.cpp	(revision 225)
+++ programs/mythbackend/mainserver.cpp	(revision 242)
@@ -2767,6 +2767,10 @@
         info->ToStringList(retlist);
         delete info;
     }
+    else if (command == "START_EIT_SCAN")
+    {
+        retlist << QString::number((int)enc->StartEITScan(slist[2]));
+    }
 
     SendResponse(pbssock, retlist);
 }
=== programs/mythbackend/playbacksock.h
==================================================================
--- programs/mythbackend/playbacksock.h	(revision 225)
+++ programs/mythbackend/playbacksock.h	(revision 242)
@@ -60,6 +60,7 @@
                                  const ProgramInfo *pginfo);
     void RecordPending(int capturecardnum, const ProgramInfo *pginfo, int secsleft);
     int SetSignalMonitoringRate(int capturecardnum, int rate, int notifyFrontend);
+    bool StartEITScan(int capturecardnum, QString chanid);
 
   private:
     bool SendReceiveStringList(QStringList &strlist);
=== programs/mythbackend/main.cpp
==================================================================
--- programs/mythbackend/main.cpp	(revision 225)
+++ programs/mythbackend/main.cpp	(revision 242)
@@ -26,6 +26,7 @@
 #include "encoderlink.h"
 #include "remoteutil.h"
 #include "housekeeper.h"
+#include "eitactivescanner.h"
 
 #include "libmyth/mythcontext.h"
 #include "libmyth/mythdbcon.h"
@@ -42,6 +43,7 @@
 QString lockfile_location;
 HouseKeeper *housekeeping = NULL;
 QString logfile = "";
+EITActiveScanner *eitscanner = NULL;
 
 bool setupTVs(bool ismaster, bool &error)
 {
@@ -595,6 +597,7 @@
     else
         jobqueue = new JobQueue(ismaster);
 
+    eitscanner = new EITActiveScanner(ismaster, &tvList);
     VERBOSE(VB_IMPORTANT, QString("%1 version: %2 www.mythtv.org")
                             .arg(binname).arg(MYTH_BINARY_VERSION));
 
=== programs/mythbackend/eitactivescanner.cpp
==================================================================
--- programs/mythbackend/eitactivescanner.cpp	(revision 225)
+++ programs/mythbackend/eitactivescanner.cpp	(revision 242)
@@ -0,0 +1,236 @@
+// -*- Mode: c++ -*-
+
+#include "eitactivescanner.h"
+
+#include "libmythtv/tv_rec.h"
+
+
+/*****************************************************************************
+ *
+ * Active EIT scanner: controls the TVRecs on the master backend
+                       and holds a persistant event cache on each backend
+*/
+
+EITActiveScanner::EITActiveScanner(bool _ismaster, QMap<int, EncoderLink *> *tvList)
+    : exitThread(false), eitCache(new EITCache()),
+      cardList(tvList), ismaster(_ismaster)
+{
+    pthread_create(&eventThread, NULL, SpawnEventLoop, this);
+}
+/*
+void EITActiveScanner::TeardownAll(void)
+{
+    StopActiveScan();
+    if (!exitThread)
+    {
+        exitThread = true;
+        exitThreadCond.wakeAll();
+        pthread_join(eventThread, NULL);
+    }
+}*/
+
+/** \fn EITActiveScanner::SpawnEventLoop(void*)
+ *  \brief Thunk that allows scanner_thread pthread to
+ *         call EITActiveScanner::RunEventLoop().
+ */
+void *EITActiveScanner::SpawnEventLoop(void *param)
+{
+    EITActiveScanner *scanner = (EITActiveScanner*) param;
+    scanner->RunEventLoop();
+    return NULL;
+}
+
+/** \fn EITActiveScanner::RunEventLoop()
+ *  \brief This runs the event loop for EITActiveScanner until 'exitThread' is true.
+ */
+void EITActiveScanner::RunEventLoop(void)
+{
+    exitThread = false;
+
+    QMap<int, EncoderLink *>::iterator it = cardList->begin();
+    for (; it != cardList->end(); ++it)
+    {
+        TVRec *rec = it.data()->GetTVRec();
+        if (rec)
+        {
+            rec->SetEITCache(eitCache);
+        }
+    }
+    if (ismaster)
+        StartActiveScan();
+
+    while (!exitThread)
+    {
+        QDateTime now = QDateTime::currentDateTime();
+
+        if (ismaster && (now > nextTriggerTime) && !cardList->isEmpty())
+        {
+            VERBOSE(VB_EIT, eitCache->GetStatistics());
+            if (nextCard == cardsSources.end())
+            {
+                nextCard = cardsSources.begin();
+            }
+
+            // find source with the highest priority
+            vector<uint>::iterator source = sources.begin();
+            while (source != sources.end())
+            {
+                if ((*nextCard).end() != find((*nextCard).begin(),
+                                            (*nextCard).end(), *source))
+                    break;
+                ++source;
+            }
+            if (source == sources.end())
+            {
+                VERBOSE(VB_EIT, QString("Couldn't find source for card %1.")
+                        .arg(nextCard.key()));
+                continue;
+            }
+            uint sid = *source;
+            int cardid = nextCard.key();
+
+
+            if (!(*(nextChan[sid])).isEmpty() &&
+                (*cardList)[cardid]->StartEITScan(*(nextChan[sid])))
+            {
+                VERBOSE(VB_GENERAL, QString("DVB(%1): Now looking for EIT "
+                                            "data on multiplex of channel %2 "
+                                            "of source %3")
+                        .arg(cardid).arg(*(nextChan[sid])).arg(sid));
+                /* advace to the next channel and test for valid iterator.
+                   needed to simplify selection of the source
+                   with the highest priority. */
+                nextChan[sid]++;
+                if (nextChan[sid] == channels[sid].end())
+                {
+                    nextChan[sid] = channels[sid].begin();
+                    sources.erase(source);
+                    sources.push_back(sid);
+                    //cerr << "Moved source " << sid << " to the back of the queue." << endl; 
+                }
+            }
+            else
+            {
+                VERBOSE(VB_GENERAL, QString("Skipping card %1 in scan for EIT "
+                                            "data on multiplex of channel %2 "
+                                            "of source %3")
+                        .arg(cardid).arg(*(nextChan[sid])).arg(sid));
+            }
+
+            nextTriggerTime = QDateTime::currentDateTime().addSecs(triggerTime);
+
+            nextCard++;
+        }
+
+        // prune cache entries that ended more than kPruneCacheTimeOffset ago
+        if (now > nextPruneCacheTime)
+        {
+            uint prunedEntries = eitCache->PruneOldEntries(now
+                                 .addSecs(-kPruneCacheTimeOffset).toTime_t());
+            nextPruneCacheTime =  QDateTime::currentDateTime()
+                .addSecs(kPruneCacheTimeOffset);
+
+            VERBOSE(VB_EIT, eitCache->GetStatistics());
+            VERBOSE(VB_EIT, QString("Pruned %1 entries from the eit cache")
+                        .arg(prunedEntries));
+        }
+            
+        exitThreadCond.wait(1000); // sleep up to 1 second.
+    }
+}
+
+void EITActiveScanner::StartActiveScan()
+{
+    if (!sources.size())
+    {
+        // get all source ids with useeit == 1
+        MSqlQuery query(MSqlQuery::InitCon());
+        query.prepare(
+            "SELECT DISTINCT channel.sourceid "
+            "FROM channel, videosource "
+            "WHERE videosource.sourceid = channel.sourceid AND "
+            "      channel.mplexid        IS NOT NULL      AND "
+            "      useonairguide        = 1                AND "
+            "      useeit               = 1 "
+            "GROUP BY mplexid "
+            "ORDER BY channel.sourceid, atscsrcid, mplexid");
+        
+        if (!query.exec() || !query.isActive())
+        {
+            MythContext::DBError("EITActiveScanner::StartActiveScan", query);
+            return;
+        }
+        
+        while (query.next())
+            sources.push_back(query.value(0).toUInt());
+
+        // getting mapping from cardid to sourceid with useeit == 1
+        query.prepare(
+            "SELECT cardid, videosource.sourceid "
+            "FROM videosource,cardinput "
+            "WHERE videosource.sourceid = cardinput.sourceid AND "
+            "      useeit               = 1 "
+            "ORDER BY cardid");
+        
+        if (!query.exec() || !query.isActive())
+        {
+            MythContext::DBError("EITActiveScanner::StartActiveScan", query);
+            return;
+        }
+        
+        while (query.next())
+            cardsSources[query.value(0).toInt()]
+                .push_back(query.value(1).toUInt());
+
+
+        // get one channel with sourceid per mplexid
+        query.prepare(
+            "SELECT channel.sourceid, min(channum) "
+            "FROM channel, videosource "
+            "WHERE videosource.sourceid = channel.sourceid AND "
+            "      channel.mplexid        IS NOT NULL      AND "
+            "      useonairguide        = 1                AND "
+            "      useeit               = 1                AND "
+            "      channum             != '' "
+            "GROUP BY mplexid "
+            "ORDER BY channel.sourceid, atscsrcid, mplexid");
+
+        if (!query.exec() || !query.isActive())
+        {
+            MythContext::DBError("EITScanner::StartActiveScan", query);
+            return;
+        }
+
+        while (query.next())
+        {
+            VERBOSE(VB_EIT,QString("Adding channel %1 for source %2 "
+                                   "to scan list")
+                    .arg(query.value(0).toString())
+                    .arg(query.value(1).toUInt()));
+            channels[query.value(0).toUInt()]
+                .push_back(query.value(1).toString());
+        }
+    }
+    int number = 0;
+    for (vector<uint>::iterator it = sources.begin(); it != sources.end(); ++it)
+    {
+        nextChan[*it] = channels[*it].begin();
+        number += channels[*it].size();
+    }
+
+    VERBOSE(VB_EIT,
+            QString("StartActiveScan called with %1 multiplexes on %2 sources")
+            .arg(number).arg(sources.size()));
+
+    if (sources.size())
+    {
+        uint scantime = gContext->GetNumSetting("EITTransportTimeout", 5) * 60;
+        uint idle_start = gContext->GetNumSetting("EITCrawIdleStart", 60);
+
+        nextTriggerTime    = QDateTime::currentDateTime().addSecs(idle_start+5);
+        triggerTime        = scantime / cardList->size();
+        nextCard           = cardsSources.begin();
+        nextPruneCacheTime = QDateTime::currentDateTime()
+            .addSecs(kPruneCacheTimeOffset);
+    }
+}

Property changes on: programs/mythbackend/eitactivescanner.cpp
___________________________________________________________________
Name: svn:mime-type
 +text/cpp

=== programs/mythbackend/eitactivescanner.h
==================================================================
--- programs/mythbackend/eitactivescanner.h	(revision 225)
+++ programs/mythbackend/eitactivescanner.h	(revision 242)
@@ -0,0 +1,59 @@
+// -*- Mode: c++ -*-
+#ifndef EITACTIVESCANNER_H
+#define EITACTIVESCANNER_H
+
+// C includes
+#include <pthread.h>
+
+// Qt includes
+#include <qobject.h>
+#include <qdatetime.h>
+#include <qstringlist.h>
+#include <qwaitcondition.h>
+
+// myth includes
+#include "encoderlink.h"
+
+#include "libmythtv/eitcache.h"
+
+
+
+class EITActiveScanner
+{
+  public:
+    EITActiveScanner(bool _ismaster, QMap<int, EncoderLink *> *tvList);
+    ~EITActiveScanner() {}
+
+  private:
+    void StartActiveScan(void);
+    void RunEventLoop(void);
+    static void *SpawnEventLoop(void*);
+
+    QMutex           lock;
+
+    pthread_t        eventThread;
+    bool             exitThread;
+    QWaitCondition   exitThreadCond;
+
+    EITCache         *eitCache;
+
+    QDateTime        nextTriggerTime;
+    uint             triggerTime;
+
+    vector<uint>                        sources;
+    QMap<uint, QStringList>             channels;
+    QMap<uint, QStringList::iterator>   nextChan;
+
+    QMap<int, EncoderLink *>            *cardList;
+
+    QMap<int, vector<uint> >            cardsSources;
+    QMap<int, vector<uint> >::iterator  nextCard;
+
+    bool                ismaster;
+
+    QDateTime           nextPruneCacheTime;
+
+    static const int    kPruneCacheTimeOffset = 21600; // six hours
+};
+
+#endif //EITACTIVESCANNER_H

Property changes on: programs/mythbackend/eitactivescanner.h
___________________________________________________________________
Name: svn:mime-type
 +text/cpp

=== programs/mythbackend/encoderlink.cpp
==================================================================
--- programs/mythbackend/encoderlink.cpp	(revision 225)
+++ programs/mythbackend/encoderlink.cpp	(revision 242)
@@ -199,6 +199,21 @@
     return retval;
 }
 
+/** \fn EncoderLink::StartEITScan(const QString)
+ *  \brief Tells TVRec to scan for EIT on channel chanid.
+ *  \param chanid      Channel to scan
+ */
+bool EncoderLink::StartEITScan(const QString chanid)
+{
+    if (local)
+        return tv->StartEITScan(chanid);
+    else if (sock)
+    {
+        return sock->StartEITScan(m_capturecardnum, chanid);
+    }
+}
+
+
 /** \fn EncoderLink::RecordPending(const ProgramInfo*, int)
  *  \brief Tells TVRec there is a pending recording "rec" in "secsleft" seconds.
  *  \param rec      Recording to make.
