From 60b8e825bb5bcbb6bd56063a62f14df91d541f1f Mon Sep 17 00:00:00 2001
From: carlpny
Date: Fri, 20 Nov 2015 00:07:26 -0800
Subject: [PATCH 1/2] Fix for EIT and ETT mismatch.

---
 mythtv/libs/libmythtv/eithelper.cpp | 56 +++++++++++++++++++++++++++----------
 mythtv/libs/libmythtv/eithelper.h   | 45 ++++++++++++++++++++++-------
 2 files changed, 77 insertions(+), 24 deletions(-)

diff --git a/mythtv/libs/libmythtv/eithelper.cpp b/mythtv/libs/libmythtv/eithelper.cpp
index ab14a71..04035ca 100644
--- a/mythtv/libs/libmythtv/eithelper.cpp
+++ b/mythtv/libs/libmythtv/eithelper.cpp
@@ -159,23 +159,33 @@ void EITHelper::AddEIT(uint atsc_major, uint atsc_minor,
                      eit->title(i).GetBestMatch(languagePreferences),
                      eit->Descriptors(i), eit->DescriptorsLength(i));
 
+        // Look to see if there has been a recent ett message with the same event id.
         EventIDToETT::iterator it = etts.find(eit->EventID(i));
-
+        QString ett_text = QString::null;
+        bool found_matching_ett = false;
         if (it != etts.end())
         {
-            CompleteEvent(atsc_major, atsc_minor, ev, *it);
+            // Don't use an ett description if it was scanned long in the past.
+            if (!it->IsStale()) {
+              ett_text = it->ett_text;
+              found_matching_ett = true;
+            }
             etts.erase(it);
         }
-        else if (!ev.etm)
+
+        // Create an event immediately if a matching ett description was found,
+        // or if item is false, indicating that no ett description should be
+        // expected.
+        if (found_matching_ett || !ev.etm)
         {
-            CompleteEvent(atsc_major, atsc_minor, ev, QString::null);
+            CompleteEvent(atsc_major, atsc_minor, ev, ett_text);
         }
         else
         {
             unsigned char *tmp = new unsigned char[ev.desc_length];
             memcpy(tmp, eit->Descriptors(i), ev.desc_length);
             ev.desc = tmp;
-            events[eit->EventID(i)] = ev;
+            events.insert(eit->EventID(i), ev);
         }
     }
 }
@@ -184,33 +194,51 @@ void EITHelper::AddETT(uint atsc_major, uint atsc_minor,
                        const ExtendedTextTable *ett)
 {
     uint atsc_key = (atsc_major << 16) | atsc_minor;
-    // Try to complete an Event
+    // Try to match up the ett with an eit event.
     ATSCSRCToEvents::iterator eits_it = incomplete_events.find(atsc_key);
     if (eits_it != incomplete_events.end())
     {
         EventIDToATSCEvent::iterator it = (*eits_it).find(ett->EventID());
         if (it != (*eits_it).end())
         {
-            CompleteEvent(
-                atsc_major, atsc_minor, *it,
-                ett->ExtendedTextMessage().GetBestMatch(languagePreferences));
+            bool completed_event = false;
+            // Only consider eit events from the recent past.
+            if (!it->IsStale()) {
+              completed_event = true;
+              CompleteEvent(
+                  atsc_major, atsc_minor, *it,
+                  ett->ExtendedTextMessage().GetBestMatch(languagePreferences));
+            }
 
             if ((*it).desc)
                 delete [] (*it).desc;
 
             (*eits_it).erase(it);
 
-            return;
+            if (completed_event) return;
         }
     }
 
-    // Couldn't find matching EIT. If not yet in unmatched ETT map, insert it.
+    // Report if an unmatched ett was previously noted and overwrite it.
+    // See also https://code.mythtv.org/trac/ticket/11739
     EventIDToETT &elist = unmatched_etts[atsc_key];
-    if (elist.find(ett->EventID()) == elist.end())
+    EventIDToETT::iterator existing_unmatched_ett_it =
+        elist.find(ett->EventID());
+    const QString next_ett_text = ett->ExtendedTextMessage()
+        .GetBestMatch(languagePreferences);
+    if (existing_unmatched_ett_it != elist.end() &&
+        existing_unmatched_ett_it->ett_text != next_ett_text)
     {
-        elist[ett->EventID()] = ett->ExtendedTextMessage()
-            .GetBestMatch(languagePreferences);
+       LOG(VB_EIT, LOG_INFO, LOC +
+           QString("Overwriting previously unmatched ett. stale: %1 major: %2 "
+                   "minor: %3 old ett: %4  new ett: %5")
+               .arg(existing_unmatched_ett_it->IsStale())
+               .arg(atsc_major)
+               .arg(atsc_minor)
+               .arg(existing_unmatched_ett_it->ett_text)
+               .arg(next_ett_text));
     }
+    elist.insert(ett->EventID(), ATSCEtt(next_ett_text));
 }
 
 static void parse_dvb_event_descriptors(desc_list_t list, uint fix,
diff --git a/mythtv/libs/libmythtv/eithelper.h b/mythtv/libs/libmythtv/eithelper.h
index a60e5d5..903235e 100644
--- a/mythtv/libs/libmythtv/eithelper.h
+++ b/mythtv/libs/libmythtv/eithelper.h
@@ -17,32 +17,57 @@
 
 class MSqlQuery;
 
+// An entry from the EIT table containing event details.
 class ATSCEvent
 {
   public:
-    /// This empty constructor is needed for the QMap<> to work, it is
-    /// not intended to be used to initialize an ATSC Event.
-    /// Since we immediately initialize the value inserted into the
-    /// QMap this is safe in that use.
-    ATSCEvent() : start_time(0), length(0), etm(0), desc_length(0), desc(NULL) {}
-    /// This is the only valid constructor for ATSCEvent.
     ATSCEvent(uint a, uint b, uint c, QString d,
               const unsigned char *e, uint f)
-        : start_time(a), length(b), etm(c), desc_length(f), title(d), desc(e)
-    {
+        : start_time(a), length(b), etm(c), desc_length(f), title(d), desc(e),
+          scan_time(time(NULL)) {}
+
+    bool IsStale() const {
+      // The minimum recommended repetition time for EIT events according to
+      // http://atsc.org/wp-content/uploads/2015/03/Program-and-system-information-protocol-implementation-guidelines-for-broadcaster.pdf
+      // is one minute. Consider any EIT event seen > 2 minutes in the past as stale.
+      return scan_time + 2 * 60 < time(NULL);
     }
 
-  public:
     uint32_t start_time;
     uint32_t length;
     uint32_t etm;
     uint32_t desc_length;
     QString  title;
     const unsigned char *desc;
+
+  private:
+    // The time the event was created.
+    time_t scan_time;
 };
 
+// An entry from the ETT table containing description text for an event.
+class ATSCEtt
+{
+  public:
+    explicit ATSCEtt(QString text) : ett_text(text), scan_time(time(NULL)) {}
+
+    bool IsStale() const {
+      // The minimum recommended repetition time for ETT events according to
+      // http://atsc.org/wp-content/uploads/2015/03/Program-and-system-information-protocol-implementation-guidelines-for-broadcaster.pdf
+      // is one minute. Consider any ETT event seen > 2 minutes in the past as stale.
+      return scan_time + 2 * 60 < time(NULL);
+    }
+
+    QString ett_text;
+
+  private:
+    // The time the ETT was created.
+    time_t scan_time;
+};
+
+
 typedef QMap<uint,ATSCEvent>               EventIDToATSCEvent;
-typedef QMap<uint,QString>                 EventIDToETT;
+typedef QMap<uint,ATSCEtt>                 EventIDToETT;
 typedef QMap<uint,EventIDToATSCEvent>      ATSCSRCToEvents;
 typedef QMap<uint,EventIDToETT>            ATSCSRCToETTs;
 typedef QMap<unsigned long long,uint>      ServiceToChanID;
-- 
2.5.0


From ed9ccf18022f7d241209c25639408a582fed6a9e Mon Sep 17 00:00:00 2001
From: carlpny <carlpny@gmail.com>
Date: Fri, 20 Nov 2015 09:33:36 -0800
Subject: [PATCH 2/2] Change LOG to DEBUG.

---
 mythtv/libs/libmythtv/eithelper.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mythtv/libs/libmythtv/eithelper.cpp b/mythtv/libs/libmythtv/eithelper.cpp
index 04035ca..58ea61f 100644
--- a/mythtv/libs/libmythtv/eithelper.cpp
+++ b/mythtv/libs/libmythtv/eithelper.cpp
@@ -229,7 +229,7 @@ void EITHelper::AddETT(uint atsc_major, uint atsc_minor,
     if (existing_unmatched_ett_it != elist.end() &&
         existing_unmatched_ett_it->ett_text != next_ett_text)
     {
-       LOG(VB_EIT, LOG_INFO, LOC +
+       LOG(VB_EIT, LOG_DEBUG, LOC +
            QString("Overwriting previously unmatched ett. stale: %1 major: %2 "
                    "minor: %3 old ett: %4  new ett: %5")
                .arg(existing_unmatched_ett_it->IsStale())
-- 
2.5.0

