]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Merge PR151 - Fixes some bugs in EIT status handling.
authorRichard Kunze <richard.kunze@web.de>
Sun, 23 Sep 2012 19:51:06 +0000 (21:51 +0200)
committerAdam Sutton <dev@adamsutton.me.uk>
Tue, 25 Sep 2012 08:38:04 +0000 (09:38 +0100)
* Fix eit_status_find() to correctly handle EIT schedule tables
  with segments which do not use all 8 sections of the segment

Both bugs forced EIT scanning to always time out instead of
terminating after all EIT sections have been processed.

At least for me (DVB-T in Germany), this fix drastically reduces
EIT scanning times.

src/epggrab/module/eit.c
src/epggrab/otamux.c

index 22cb7449198359142dbca9ec0776620c7b5ad182..98e59431f675b773bf619c46d6eb478ef8c3ef3a 100644 (file)
@@ -39,7 +39,11 @@ typedef struct eit_status
   uint16_t               sid;
   uint32_t               sec[8];
   uint8_t                ver;
-  int                    done;
+  enum {
+    EIT_STATUS_START,
+    EIT_STATUS_PROCESS,
+    EIT_STATUS_DONE
+  }                     stage;
 } eit_status_t;
 typedef LIST_HEAD(, eit_status) eit_status_list_t;
 
@@ -47,8 +51,8 @@ static eit_status_t *eit_status_find
   ( eit_status_list_t *el, int tableid, uint16_t tsid, uint16_t sid,
     uint8_t sec, uint8_t lst, uint8_t seg, uint8_t ver )
 {
-  int i;
-  uint32_t msk;
+  int i, sec_index;
+  uint32_t sec_seen_mask;
   eit_status_t *sta;
 
   /* Find */
@@ -57,7 +61,7 @@ static eit_status_t *eit_status_find
       break;
 
   /* Already complete */
-  if (sta && sta->done && sta->ver == ver) return NULL;
+  if (sta && sta->stage == EIT_STATUS_DONE && sta->ver == ver) return NULL;
 
   /* Insert new entry */
   if (!sta) {
@@ -74,25 +78,61 @@ static eit_status_t *eit_status_find
     sta->ver = ver;
     for (i = 0; i < (lst / 32); i++)
       sta->sec[i] = 0xFFFFFFFF;
-    if (lst % 32)
-      sta->sec[i] = (0xFFFFFFFF >> (31-(lst%32)));
+    sta->sec[i] = (0xFFFFFFFF >> (31-(lst%32)));
+    sta->stage = EIT_STATUS_PROCESS;
   }
 
-  /* Get section mask */
-  if (sec == seg) {
-    msk = 0xFF << (((sec/8)%4) * 8);
-  } else {
-    msk = 0x1  << (sec%32);
+  /* Get "section(s) seen" mask. See ETSI TS 101 211 (V1.11.1) section 4.1.4 
+   * and ETSI EN 300 468 (V1.13.1) section 5.2.4 for usage of the
+   * of the "seg" (= segment_last_section_number) field in the
+   * now/next (tableid < 0x50) and schedule (tableid >= 0x50) tables
+   */
+  sec_index = sec/32;
+  sec_seen_mask = 0x1 << (sec%32);
+  if (tableid >= 0x50) {
+    // ETSI TS 101 211 (V1.11.1) section 4.1.4 specifies seg in eit schedule tables 
+    // as the index of the first section in the segment (always a multiple of 8, 
+    // because a segment has eight sections) plus an offset thaqt depends on the
+    // number of sections used:
+    // * seg_first_section + n - 1  if 0<n<8 segments are used (see section 4.1.4 e)
+    // * seg_first_section + 7      if all segments are used (see section 4.1.4 f)
+    // * seg_first_section + 0      if the section is empty (see section 4.1.4 g)
+    // This means that we can calculate the mask offset of the last used section
+    // simply by taking the lowest three bits of seg + 1 (empty segments count as 
+    // "one section used"). We use this offset to calculate a mask for all *unused* 
+    // sections by shifting 0xff left by this offset, take the lowest eight bits, shift 
+    // them left by the first section offset and finally expand seen_mask (seen_mask) 
+    // by calculating "seen_mask |= unused_mask"
+    
+    uint32_t seg_first_section = seg & ~0x07;
+    uint32_t seg_last_used_section_offset = (seg & 0x07) + 1;
+    uint32_t unused_mask = ((0xff << seg_last_used_section_offset) & 0xff) << (seg_first_section%32);
+    sec_seen_mask |= unused_mask;
   }
 
   /* Already seen */
-  if (!(sta->sec[sec/32] & msk)) return NULL;
+  if (!(sta->sec[sec_index] & sec_seen_mask)) {
+    if (sta->stage == EIT_STATUS_START) {
+      sta->stage = EIT_STATUS_DONE;
+      for (i = 0; i < 8; i++ ) {
+        if (sta->sec[i]) {
+          sta->stage = EIT_STATUS_PROCESS;
+          return NULL;
+        }
+      }
+    }
+    return NULL;
+  }
 
   /* Update */
-  sta->sec[sec/32] &= ~msk;
-  sta->done = 1;
-  for (i = 0; i < 8; i++ )
-    if (sta->sec[i]) sta->done = 0;
+  sta->sec[sec_index] &= ~sec_seen_mask;
+  sta->stage = EIT_STATUS_DONE;
+  for (i = 0; i < 8; i++ ) {
+    if (sta->sec[i]) {
+      sta->stage = EIT_STATUS_PROCESS;
+      return sta;
+    }
+  }
   return sta;
 }
 
@@ -603,9 +643,9 @@ static int _eit_callback
   epggrab_module_t *mod = opaque;
   epggrab_ota_mux_t *ota;
   th_dvb_adapter_t *tda;
-  service_t *svc;
+  service_t *svc = NULL;
   eit_status_list_t *stal;
-  eit_status_t *sta;
+  eit_status_t *sta = NULL;
   int resched = 0, save = 0;
   uint16_t tsid, sid;
   uint16_t sec, lst, seg, ver;
@@ -618,10 +658,11 @@ static int _eit_callback
   if (!ota || !ota->status) return -1;
   stal = ota->status;
 
-  /* Begin - reset done stats */
+  /* Begin: reset sta->stage to force revisiting of all tables */
   if (epggrab_ota_begin(ota)) {
-    LIST_FOREACH(sta, stal, link)
-      sta->done = 0;
+    LIST_FOREACH(sta, stal, link) {
+      sta->stage = EIT_STATUS_START;
+    }
   }
 
   /* Get table info */
@@ -637,13 +678,6 @@ static int _eit_callback
          tableid, tsid, sid, sec, lst, seg, ver);
 #endif
 
-  /* Current status */
-  sta = eit_status_find(stal, tableid, tsid, sid, sec, lst, seg, ver);
-#ifdef EPG_TRACE
-  tvhlog(LOG_DEBUG, mod->id, sta ? "section process" : "section seen");
-#endif
-  if (!sta) goto done;
-
   /* Don't process */
   if((ptr[2] & 1) == 0) goto done;
 
@@ -672,6 +706,13 @@ static int _eit_callback
   /* Ignore (not primary EPG service) */
   if (!service_is_primary_epg(svc)) goto done;
 
+  /* Current status */
+  sta = eit_status_find(stal, tableid, tsid, sid, sec, lst, seg, ver);
+#ifdef EPG_TRACE
+  tvhlog(LOG_DEBUG, mod->id, sta ? "section process" : "section seen");
+#endif
+  if (!sta) goto done;
+
   /* Register as interesting */
   if (tableid < 0x50)
     epggrab_ota_register(ota, 20, 300); // 20s grab, 5min interval
@@ -695,10 +736,24 @@ static int _eit_callback
 
   /* Complete */
 done:
-  if (!sta || sta->done) {
+  if (!sta || sta->stage == EIT_STATUS_DONE) {
     LIST_FOREACH(sta, stal, link)
-      if (!sta->done) break;
+      if (sta->stage != EIT_STATUS_DONE) break;
     if (!sta)    epggrab_ota_complete(ota);
+#ifdef EPG_TRACE
+    else {
+      int total = 0;
+      int finished = 0;
+      LIST_FOREACH(sta, stal, link) {
+        total++;
+        tvhlog(LOG_DEBUG, mod->id, "scan status: table=0x%02x, sid=0x%04x, ver=0x%02d, done=%u, mask=%8x|%8x|%8x|%8x|%8x|%8x|%8x|%8x", 
+                    sta->tid, sta->sid, sta->ver, sta->stage,
+                    sta->sec[7], sta->sec[6], sta->sec[5], sta->sec[4], sta->sec[3], sta->sec[2], sta->sec[1], sta->sec[0]);
+        if (sta->stage == EIT_STATUS_DONE) finished++;
+      }
+      tvhlog(LOG_DEBUG, mod->id, "scan status: completed %d of %d for service %s", finished, total, svc?svc->s_nicename:"<unknown>");
+    }
+#endif
   }
   
   /* Update EPG */
index c383eb3185f27a56c0f007be72c683fb86c2ee7e..2dd9aa3e3aeaea3e6a3731b8a8b22113113651cc 100644 (file)
@@ -374,8 +374,10 @@ void epggrab_ota_complete  ( epggrab_ota_mux_t *ota )
 /* Reset */
 void epggrab_ota_cancel    ( epggrab_ota_mux_t *ota )
 {
-  tvhlog(LOG_DEBUG, ota->grab->id, "processing cancelled");
-  ota->state = EPGGRAB_OTA_MUX_IDLE;
+  if (ota->state == EPGGRAB_OTA_MUX_RUNNING) {
+    tvhlog(LOG_DEBUG, ota->grab->id, "processing cancelled");
+    ota->state = EPGGRAB_OTA_MUX_IDLE;
+  }
   _epggrab_ota_finished(ota);
 }