From 70748b69495c62140e43bd579c654250da4b3f18 Mon Sep 17 00:00:00 2001
From: Sascha Hinck <SHinck@web.de>
Date: Tue, 7 Aug 2012 01:10:11 +0200
Subject: [PATCH] libmythtv: Stop EIT-Active scan during recording

During recording and Live-TV EIT-Active scan will be stopped on all
Capture-Cards. Active-Scan-IdleTimer will be restarted after the last
recording exited.
---
 mythtv/libs/libmythtv/eitscanner.cpp         |   14 ++-
 mythtv/libs/libmythtv/tv_rec.cpp             |  171 +++++++++++++++++++++++---
 mythtv/libs/libmythtv/tv_rec.h               |    6 +-
 mythtv/programs/mythbackend/main_helpers.cpp |    4 +-
 4 files changed, 175 insertions(+), 24 deletions(-)

diff --git a/mythtv/libs/libmythtv/eitscanner.cpp b/mythtv/libs/libmythtv/eitscanner.cpp
index 6135e08..e419a8f 100644
--- a/mythtv/libs/libmythtv/eitscanner.cpp
+++ b/mythtv/libs/libmythtv/eitscanner.cpp
@@ -133,11 +133,13 @@ void EITScanner::run(void)
             if (!(*activeScanNextChan).isEmpty())
             {
                 eitHelper->WriteEITCache();
-                rec->SetChannel(*activeScanNextChan, TVRec::kFlagEITScan);
-                LOG(VB_EIT, LOG_INFO,
-                    LOC_ID + QString("Now looking for EIT data on "
-                                     "multiplex of channel %1")
+                if (rec->QueueEITChannelChange(*activeScanNextChan))
+                {
+                    LOG(VB_EIT, LOG_INFO,
+                        LOC_ID + QString("Now looking for EIT data on "
+                                         "multiplex of channel %1")
                         .arg(*activeScanNextChan));
+                }
             }
 
             activeScanNextTrig = QDateTime::currentDateTime()
@@ -216,6 +218,8 @@ void EITScanner::StopPassiveScan(void)
 
     eitHelper->WriteEITCache();
     eitHelper->SetSourceID(0);
+
+    LOG(VB_EIT, LOG_INFO, LOC_ID + "Stopped passive scan.");
 }
 
 void EITScanner::StartActiveScan(TVRec *_rec, uint max_seconds_per_source)
@@ -280,4 +284,6 @@ void EITScanner::StopActiveScan()
     activeScan = false;
     rec = NULL;
     StopPassiveScan();
+
+    LOG(VB_EIT, LOG_INFO, LOC_ID + "Stopped active scan.");
 }
diff --git a/mythtv/libs/libmythtv/tv_rec.cpp b/mythtv/libs/libmythtv/tv_rec.cpp
index 3d38c6d..9b53f5f 100644
--- a/mythtv/libs/libmythtv/tv_rec.cpp
+++ b/mythtv/libs/libmythtv/tv_rec.cpp
@@ -36,6 +36,7 @@
 #include "tv_rec.h"
 #include "mythmiscutil.h"
 #include "osd.h"
+#include "cardutil.h"
 
 #define DEBUG_CHANNEL_PREFIX 0 /**< set to 1 to channel prefixing */
 
@@ -52,7 +53,7 @@ static QString load_profile(QString,void*,RecordingInfo*,RecordingProfile&);
 static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
                      bool on_host, bool transcode_bfr_comm, bool on_line_comm);
 static void apply_broken_dvb_driver_crc_hack(ChannelBase*, MPEGStreamData*);
-
+static int eit_start_rand(uint cardid, int eitTransportTimeout);
 
 /** \class TVRec
  *  \brief This is the coordinating class of the \ref recorder_subsystem.
@@ -161,7 +162,8 @@ bool TVRec::Init(void)
         gCoreContext->GetNumSetting("AutoTranscodeBeforeAutoCommflag", 0);
     earlyCommFlag     = gCoreContext->GetNumSetting("AutoCommflagWhileRecording", 0);
     runJobOnHostOnly  = gCoreContext->GetNumSetting("JobsRunOnRecordHost", 0);
-    eitTransportTimeout=gCoreContext->GetNumSetting("EITTransportTimeout", 5) * 60;
+    eitTransportTimeout =
+        max(gCoreContext->GetNumSetting("EITTransportTimeout", 5) * 60, 6);
     eitCrawlIdleStart = gCoreContext->GetNumSetting("EITCrawIdleStart", 60);
     audioSampleRateDB = gCoreContext->GetNumSetting("AudioSampleRate");
     overRecordSecNrml = gCoreContext->GetNumSetting("RecordOverTime");
@@ -974,6 +976,10 @@ void TVRec::HandleStateChange(void)
 
     bool changed = false;
 
+    LOG(VB_GENERAL, LOG_INFO, LOC + "GetCardIDs to stop EITscan");
+    vector<uint> cardids = CardUtil::GetCardIDs("","DVB","");
+    uint no_cards = cardids.size();
+
     QString transMsg = QString(" %1 to %2")
         .arg(StateToString(nextState))
         .arg(StateToString(desiredNextState));
@@ -998,11 +1004,46 @@ void TVRec::HandleStateChange(void)
     if (TRANSITION(kState_None, kState_WatchingLiveTV))
     {
         tuningRequests.enqueue(TuningRequest(kFlagLiveTV));
+        for (uint i = 0; i < no_cards; i++)
+        {
+           if (cardids[i] != cardid)
+	    {	    
+               TVRec *rec = GetTVRec(cardids[i]);
+               if (rec && !rec->HasFlags(kFlagEITScanPaused))
+                   rec->SetFlags(kFlagEITScanPaused);
+           }
+        }	
         SET_NEXT();
     }
     else if (TRANSITION(kState_WatchingLiveTV, kState_None))
     {
         tuningRequests.enqueue(TuningRequest(kFlagKillRec|kFlagKillRingBuffer));
+	 eitScanStartTime = QDateTime::currentDateTime();
+	 uint j = 0;
+	 TVRec *arec = NULL;
+        for (uint i = 0; i < no_cards; i++)
+	 {
+            if (cardids[i] != cardid)
+            {	    
+                TVRec *rec = GetTVRec(cardids[i]);        
+                if (!HasFlags(kFlagEITScanPaused))
+                {	    	
+                    if (rec && rec->HasFlags(kFlagEITScanPaused))
+                    {
+                        rec->eitScanStartTime = eitScanStartTime.addSecs(
+                            eitCrawlIdleStart + eit_start_rand(cardids[i], eitTransportTimeout));
+                        rec->ClearFlags(kFlagEITScanPaused);
+                        LOG(VB_IDLE, LOG_INFO, LOC + "Start EIT-ActiveScanIdle-Timer for " +
+                            QString("cardid %1, next start time: ").arg(cardids[i]) +
+				 rec->eitScanStartTime.toString(Qt::ISODate));
+                    }
+                }
+                else if (rec && rec->GetState() != kState_None)
+	             arec = rec; j++;
+	     }			
+	 }
+	 if (j == 1)
+	     arec->ClearFlags(kFlagEITScanPaused);        
         SET_NEXT();
     }
     else if (TRANSITION(kState_WatchingLiveTV, kState_RecordingOnly))
@@ -1015,6 +1058,15 @@ void TVRec::HandleStateChange(void)
     {
         SetPseudoLiveTVRecording(NULL);
         tuningRequests.enqueue(TuningRequest(kFlagRecording, curRecording));
+        for (uint i = 0; i < no_cards; i++)
+	{
+	    if (cardids[i] != cardid)
+	    {	    
+	        TVRec *rec = GetTVRec(cardids[i]);
+		if (rec && !rec->HasFlags(kFlagEITScanPaused))
+		    	rec->SetFlags(kFlagEITScanPaused);
+	    }
+	}	
         SET_NEXT();
     }
     else if (TRANSITION(kState_RecordingOnly, kState_None))
@@ -1022,6 +1074,32 @@ void TVRec::HandleStateChange(void)
         tuningRequests.enqueue(
             TuningRequest(kFlagCloseRec|kFlagKillRingBuffer|
                           (GetFlags()&kFlagKillRec)));
+        eitScanStartTime = QDateTime::currentDateTime();
+        uint j = 0;
+        TVRec *arec = NULL;
+        for (uint i = 0; i < no_cards; i++)
+        {
+            if (cardids[i] != cardid)
+            {	    
+                TVRec *rec = GetTVRec(cardids[i]);        
+                if (!HasFlags(kFlagEITScanPaused))
+                {	    	
+                    if (rec && rec->HasFlags(kFlagEITScanPaused))
+                    {
+                        rec->eitScanStartTime = eitScanStartTime.addSecs(
+            	             eitCrawlIdleStart + eit_start_rand(cardids[i], eitTransportTimeout));
+                        rec->ClearFlags(kFlagEITScanPaused);
+                        LOG(VB_IDLE, LOG_INFO, LOC + "Start EIT-ActiveScanIdle-Timer for " +
+                            QString("cardid %1, next start time: ").arg(cardids[i]) +
+                                rec->eitScanStartTime.toString(Qt::ISODate));
+                    }
+                }
+                else if (rec && rec->GetState() != kState_None)
+                    arec = rec; j++;
+            }			
+        }
+        if (j == 1)
+            arec->ClearFlags(kFlagEITScanPaused);        
         SET_NEXT();
     }
 
@@ -1034,7 +1114,10 @@ void TVRec::HandleStateChange(void)
 
     eitScanStartTime = QDateTime::currentDateTime();
     if (scanner && (internalState == kState_None))
-        eitScanStartTime = eitScanStartTime.addSecs(eitCrawlIdleStart);
+    {
+        eitScanStartTime = eitScanStartTime.addSecs(
+            eitCrawlIdleStart + eit_start_rand(cardid, eitTransportTimeout));
+    }
     else
         eitScanStartTime = eitScanStartTime.addYears(1);
 }
@@ -1130,6 +1213,9 @@ void TVRec::CloseChannel(void)
          CardUtil::IsV4L(genOpt.cardtype)))
     {
         channel->Close();
+	LOG(VB_GENERAL, LOG_INFO, LOC + QString("Close Card %1")
+		.arg(CardUtil::ProbeDVBFrontendName(CardUtil::GetVideoDevice(cardid))) );
+//			GetDeviceLabel(cardid))
     }
 }
 
@@ -1213,6 +1299,19 @@ static int no_capturecards(uint cardid)
     return -1;
 }
 
+static int eit_start_rand(uint cardid, int eitTransportTimeout)
+{
+    // randomize start time a bit
+    int timeout = random() % (eitTransportTimeout / 3);
+    // get the number of capture cards and the position of the current card
+    // to distribute the the scan start evenly over eitTransportTimeout
+    int card_pos = no_capturecards(cardid);
+    int no_cards = no_capturecards(0);
+    if (no_cards > 0 && card_pos >= 0)
+        timeout += eitTransportTimeout * card_pos / no_cards;
+    return timeout;
+}
+
 /// \brief Event handling method, contains event loop.
 void TVRec::run(void)
 {
@@ -1227,17 +1326,8 @@ void TVRec::run(void)
         (dvbOpt.dvb_eitscan || get_use_eit(cardid)))
     {
         scanner = new EITScanner(cardid);
-        uint timeout = eitCrawlIdleStart;
-        // get the number of capture cards and the position of the current card
-        // to distribute the the scan start evenly over eitTransportTimeout
-        int card_pos = no_capturecards(cardid);
-        int no_cards = no_capturecards(0);
-        if (no_cards > 0 && card_pos >= 0)
-            timeout += eitTransportTimeout * card_pos / no_cards;
-        else
-            timeout += random() % eitTransportTimeout;
-
-        eitScanStartTime = eitScanStartTime.addSecs(timeout);
+        eitScanStartTime = eitScanStartTime.addSecs(
+            eitCrawlIdleStart + eit_start_rand(cardid, eitTransportTimeout));
     }
     else
         eitScanStartTime = eitScanStartTime.addYears(1);
@@ -1370,7 +1460,8 @@ void TVRec::run(void)
         }
 
         if (scanner && channel &&
-            QDateTime::currentDateTime() > eitScanStartTime)
+            QDateTime::currentDateTime() > eitScanStartTime &&
+	    !HasFlags(kFlagEITScanPaused))
         {
             if (!dvbOpt.dvb_eitscan)
             {
@@ -1392,6 +1483,15 @@ void TVRec::run(void)
             }
         }
 
+        //Stop EIT-Scanning if recording begins on another card, 
+        if (scanner && HasFlags(kFlagEITScanPaused) &&
+            HasFlags(kFlagEITScannerRunning))
+        {
+            scanner->StopActiveScan();
+            ClearFlags(kFlagEITScannerRunning);
+            TuningShutdowns(TuningRequest(kFlagNoRec));
+        }
+
         // We should be no more than a few thousand milliseconds,
         // as the end recording code does not have a trigger...
         // NOTE: If you change anything here, make sure that
@@ -3052,6 +3152,16 @@ void TVRec::SetChannel(QString name, uint requestType)
     // Clear the RingBuffer reset flag, in case we wait for a reset below
     ClearFlags(kFlagRingBufferReady);
 
+    // Clear out any EITScan channel change requests
+    TuningQueue::iterator it = tuningRequests.begin();
+    while (it != tuningRequests.end())
+    {
+        if ((*it).flags & kFlagEITScan)
+            it = tuningRequests.erase(it);
+        else
+            ++it;
+    }
+
     // Actually add the tuning request to the queue, and
     // then wait for it to start tuning
     tuningRequests.enqueue(TuningRequest(requestType, name));
@@ -3066,6 +3176,35 @@ void TVRec::SetChannel(QString name, uint requestType)
     LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
 }
 
+/** \brief Queues up a channel change for the EITScanner.
+ *
+ *   Unlike the normal SetChannel() this does not block until
+ *   the channel change occurs to avoid a deadlock if
+ *   EITScanner::StopActiveScan() is called with the stateChangeLock
+ *   held while the EITScanner is calling TVRec::SetChannel().
+ */
+bool TVRec::QueueEITChannelChange(const QString &name)
+{
+    LOG(VB_CHANNEL, LOG_INFO, LOC +
+        QString("QueueEITChannelChange(%1) -- begin").arg(name));
+
+    bool ok = false;
+    if (stateChangeLock.tryLock())
+    {
+        if (tuningRequests.empty())
+        {
+            tuningRequests.enqueue(TuningRequest(kFlagEITScan, name));
+            ok = true;
+        }
+        stateChangeLock.unlock();
+    }
+
+    LOG(VB_CHANNEL, LOG_INFO, LOC +
+        QString("QueueEITChannelChange(%1) -- end --> %2").arg(name).arg(ok));
+
+    return ok;
+}
+
 void TVRec::GetNextProgram(BrowseDirection direction,
                            QString &title,       QString &subtitle,
                            QString &desc,        QString &category,
@@ -4204,6 +4343,8 @@ QString TVRec::FlagToString(uint f)
         msg += "Errored,";
     if (kFlagCancelNextRecording & f)
         msg += "CancelNextRecording,";
+    if (kFlagEITScanPaused & f)
+        msg += "EITScanPaused,";
 
     // Tuning flags
     if ((kFlagRec & f) == kFlagRec)
diff --git a/mythtv/libs/libmythtv/tv_rec.h b/mythtv/libs/libmythtv/tv_rec.h
index a830c33..b169434 100644
--- a/mythtv/libs/libmythtv/tv_rec.h
+++ b/mythtv/libs/libmythtv/tv_rec.h
@@ -156,6 +156,8 @@ class MTV_PUBLIC TVRec : public SignalMonitorListener, public QRunnable
     void FinishRecording(void)  { SetFlags(kFlagFinishRecording); }
     /// \brief Tells TVRec that the frontend's TV class is ready for messages.
     void FrontendReady(void)    { SetFlags(kFlagFrontendReady); }
+    void PauseEITActiveScan(void)    { SetFlags(kFlagEITScanPaused); }
+    void RestartEITActiveScan(void)    { ClearFlags(kFlagEITScanPaused); }
     void CancelNextRecording(bool cancel);
     ProgramInfo *GetRecording(void);
 
@@ -198,6 +200,7 @@ class MTV_PUBLIC TVRec : public SignalMonitorListener, public QRunnable
     void ChangeChannel(ChannelChangeDirection dir)
         { SetChannel(QString("NextChannel %1").arg((int)dir)); }
     void SetChannel(QString name, uint requestType = kFlagDetect);
+    bool QueueEITChannelChange(const QString &name);
 
     int SetSignalMonitoringRate(int msec, int notifyFrontend = 1);
     int  GetPictureAttribute(PictureAttribute attr);
@@ -409,6 +412,7 @@ class MTV_PUBLIC TVRec : public SignalMonitorListener, public QRunnable
     static const uint kFlagFinishRecording      = 0x00000008;
     static const uint kFlagErrored              = 0x00000010;
     static const uint kFlagCancelNextRecording  = 0x00000020;
+    static const uint kFlagEITScanPaused	= 0x00000040;
 
     // Tuning flags
     /// final result desired is LiveTV recording
@@ -432,7 +436,7 @@ class MTV_PUBLIC TVRec : public SignalMonitorListener, public QRunnable
 
     // Waiting stuff
     static const uint kFlagWaitingForRecPause   = 0x00100000;
-    static const uint kFlagWaitingForSignal     = 0x00200000;
+    static const uint kFlagWaitingForSignal     = 0x00200000;    
     static const uint kFlagNeedToStartRecorder  = 0x00800000;
     static const uint kFlagPendingActions       = 0x00F00000;
 
diff --git a/mythtv/programs/mythbackend/main_helpers.cpp b/mythtv/programs/mythbackend/main_helpers.cpp
index 28d5a4a..d2283ca 100644
--- a/mythtv/programs/mythbackend/main_helpers.cpp
+++ b/mythtv/programs/mythbackend/main_helpers.cpp
@@ -192,8 +192,8 @@ bool setupTVs(bool ismaster, bool &error)
                 }
                 else
                 {
-                    LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards" +
-                            cidmsg + "failed init");
+                    LOG(VB_GENERAL, LOG_ERR, "Problem with capture cards - " +
+                            cidmsg + " failed init");
                     delete tv;
                 }
             }
