]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Updated EIT code to (hopefully) more robust implementation and included the primary...
authorAdam Sutton <dev@adamsutton.me.uk>
Wed, 11 Jul 2012 12:18:10 +0000 (13:18 +0100)
committerAdam Sutton <dev@adamsutton.me.uk>
Mon, 30 Jul 2012 15:03:16 +0000 (16:03 +0100)
src/epggrab/module/eit.c

index d9c10f7542a8e3cc89b535583ede251b796b6749..1f6ba22966523341d225361a58e40342495ced70 100644 (file)
@@ -38,6 +38,24 @@ typedef struct eit_status
   int sec;
 } eit_status_t;
 
+typedef struct eit_event
+{
+  char              title[256];
+  char              summary[256];
+  char              desc[2000];
+
+  char             *default_charset;
+
+  htsmsg_t         *extra;
+
+  epg_genre_list_t *genre;
+
+  uint8_t           hd, ws;
+  uint8_t           ad, st, ds;
+  uint8_t           bw;
+
+} eit_event_t;
+
 /* ************************************************************************
  * Diagnostics
  * ***********************************************************************/
@@ -61,86 +79,304 @@ static void _eit_dtag_dump ( uint8_t dtag, uint8_t dlen, uint8_t *buf )
 }
 
 /* ************************************************************************
- * Processing
+ * EIT Event descriptors
  * ***********************************************************************/
 
-/**
- * DVB Descriptor; Short Event
+/*
+ * Short Event - 0x4d
  */
-static int
-dvb_desc_short_event(uint8_t *ptr, int len, 
-         char *title, size_t titlelen,
-         char *desc,  size_t desclen,
-         char *dvb_default_charset)
+static int _eit_desc_short_event
+  ( uint8_t *ptr, int len, eit_event_t *ev )
 {
   int r;
 
-  if(len < 4)
-    return -1;
-  ptr += 3; len -= 3;
+  if ( len < 5 ) return -1;
+
+  /* Language (skip) */
+  len -= 3;
+  ptr += 3;
 
-  if((r = dvb_get_string_with_len(title, titlelen, ptr, len, dvb_default_charset)) < 0)
+  /* Title */
+  if ( (r = dvb_get_string_with_len(ev->title, sizeof(ev->title),
+                                    ptr, len, ev->default_charset)) < 0 )
     return -1;
-  ptr += r; len -= r;
 
-  if((r = dvb_get_string_with_len(desc, desclen, ptr, len, dvb_default_charset)) < 0)
+  len -= r;
+  ptr += r;
+  if ( len < 1 ) return -1;
+
+  /* Summary */
+  if ( (r = dvb_get_string_with_len(ev->summary, sizeof(ev->summary),
+                                    ptr, len, ev->default_charset)) < 0 )
     return -1;
 
   return 0;
 }
 
-/**
- * DVB Descriptor; Extended Event
+/*
+ * Extended Event - 0x4e
  */
-static int
-dvb_desc_extended_event(uint8_t *ptr, int len, 
-                        char *desc, size_t desclen,
-                        htsmsg_t *extra,
-                        char *dvb_default_charset)
+static int _eit_desc_ext_event
+  ( uint8_t *ptr, int len, eit_event_t *ev )
 {
-  int ilen, nitems = ptr[4];
+  int r, nitem;
   char ikey[256], ival[256];
-  if (len < 8) return -1;
-  ptr += 5;
-  len -= 5;
-
-  /* key/value items */
-  while (nitems-- > 0) {
-
-    /* key */
-    ilen = *ptr;
-    if (ilen+1 > len) return -1;
-    if(dvb_get_string_with_len(ikey, sizeof(ikey),
-                               ptr+1, ilen,
-                               dvb_default_charset) < 0) return -1;
+
+  if (len < 6) return -1;
+
+  /* Descriptor numbering (skip) */
+  len -= 1;
+  ptr += 1;
+
+  /* Language (skip) */
+  len -= 3;
+  ptr += 3;
+
+  /* Key/Value items */
+  nitem = *ptr;
+  len  -= 1;
+  ptr  += 1;
+  if (len < (2 * nitem) + 1) return -1;
+
+  while (nitem--) {
+
+    /* Key */
+    if ( (r = dvb_get_string_with_len(ikey, sizeof(ikey),
+                                      ptr, len, ev->default_charset)) < 0 )
       return -1;
-    ptr += (ilen + 1);
-    len -= (ilen + 1);
-
-    /* value */
-    ilen = *ptr;
-    if (ilen+1 > len) return -1;
-    if(dvb_get_string_with_len(ival, sizeof(ival),
-                               ptr, ilen,
-                               dvb_default_charset) < 0) return -1;
-    ptr += (ilen + 1);
-    len -= (ilen + 1);
-
-    // TODO: should this extend existing strings?
-    htsmsg_add_str(extra, ikey, ival);
+
+    len -= r;
+    ptr += r;
+
+    /* Value */
+    if ( (r = dvb_get_string_with_len(ival, sizeof(ival),
+                                      ptr, len, ev->default_charset)) < 0 )
+      return -1;
+
+    len -= r;
+    ptr += r;
+
+    /* Store */
+    // TODO: extend existing?
+    if (*ikey && *ival)
+      htsmsg_add_str(ev->extra, ikey, ival);
   }
 
-  /* raw text (append) */
-  ilen = *ptr;
-  if (ilen+1 > len) return -1;
-  if(dvb_get_string_with_len(desc    + strlen(desc),
-                             desclen - strlen(desc),
-                             ptr+1, ilen,
-                             dvb_default_charset) < 0) return -1;
+  /* Description */
+  if ( dvb_get_string_with_len(ev->desc         + strlen(ev->desc),
+                               sizeof(ev->desc) - strlen(ev->desc),
+                               ptr, len,
+                               ev->default_charset) < 0 )
+    return -1;
 
   return 0;
 }
 
+/*
+ * Component Descriptor - 0x50
+ */
+
+static int _eit_desc_component
+  ( uint8_t *ptr, int len, eit_event_t *ev )
+{
+  uint8_t c, t;
+
+  if (len < 6) return -1;
+
+  /* Stream Content and Type */
+  c = *ptr & 0x0f;
+  t = ptr[1];
+
+  /* MPEG2 (video) */
+  if (c == 0x1) {
+    if (t > 0x08 && t < 0x11) {
+      ev->hd = 1;
+      if ( t != 0x09 && t != 0x0d )
+        ev->ws = 1;
+    } else if (t == 0x02 || t == 0x03 || t == 0x04 ||
+               t == 0x06 || t == 0x07 || t == 0x08 ) {
+      ev->ws = 1;
+    }
+
+  /* MPEG2 (audio) */
+  } else if (c == 0x2) {
+
+    /* Described */
+    if (t == 0x40 || t == 0x41)
+      ev->ad = 1;
+
+  /* Misc */
+  } else if (c == 0x3) {
+    if (t == 0x1 || (t >= 0x10 && t <= 0x14) || (t >= 0x20 && t <= 0x24))
+      ev->st = 1;
+    else if (t == 0x30 || t == 0x31)
+      ev->ds = 1;
+
+  /* H264 */
+  } else if (c == 0x5) {
+    if (t == 0x0b || t == 0x0c || t == 0x10)
+      ev->hd = ev->ws = 1;
+    else if (t == 0x03 || t == 0x04 || t == 0x07 || t == 0x08)
+      ev->ws = 1;
+
+  /* AAC */
+  } else if ( c == 0x6 ) {
+
+    /* Described */
+    if (t == 0x40 || t == 0x44)
+      ev->ad = 1;
+  }
+
+  return 0;
+}
+
+/*
+ * Content Descriptor - 0x54
+ */
+
+static int _eit_desc_content
+  ( uint8_t *ptr, int len, eit_event_t *ev )
+{
+  while (len > 1) {
+    if (*ptr == 0xb1)
+      ev->bw = 1;
+    else if (*ptr < 0xb0) {
+      if (!ev->genre) ev->genre = calloc(1, sizeof(epg_genre_list_t));
+      epg_genre_list_add_by_eit(ev->genre, *ptr);
+    }
+    len -= 2;
+    ptr += 2;
+  }
+  return 0;
+}
+
+/* ************************************************************************
+ * EIT Event
+ * ***********************************************************************/
+
+static int _eit_process_event
+  ( epggrab_module_t *mod, int tableid,
+    service_t *svc, uint8_t *ptr, int len,
+    int *resched, int *save )
+{
+  int save2 = 0;
+  int ret, dllen;
+  time_t start, stop;
+  uint16_t eid;
+  uint8_t dtag, dlen;
+  epg_broadcast_t *ebc;
+  epg_episode_t *ee;
+  eit_event_t ev;
+
+  if ( len < 12 ) return -1;
+
+  /* Core fields */
+  eid   = ptr[0] << 8 | ptr[1];
+  start = dvb_convert_date(&ptr[2]);
+  stop  = start + bcdtoint(ptr[7] & 0xff) * 3600 +
+                  bcdtoint(ptr[8] & 0xff) * 60 +
+                  bcdtoint(ptr[9] & 0xff);
+  dllen = ((ptr[10] & 0x0f) << 8) | ptr[11];
+
+  len -= 12;
+  ptr += 12;
+  if ( len < dllen ) return -1;
+  ret  = 12 + dllen;
+
+  /* Find broadcast */
+  ebc  = epg_broadcast_find_by_time(svc->s_ch, start, stop, eid, 1, &save2);
+  if (!ebc) return dllen + 12;
+  tvhlog(LOG_DEBUG, "eit", "process eid %d event %p %"PRIu64" on %s",
+         eid, ebc, ebc->id, svc->s_ch->ch_name);
+
+  /* Mark re-schedule detect (only now/next) */
+  if (save2 && tableid < 0x50) *resched = 1;
+  *save |= save2;
+
+  /* Process tags */
+  memset(&ev, 0, sizeof(ev));
+  ev.default_charset = svc->s_dvb_default_charset;
+  while (dllen > 2) {
+    int r;
+    dtag = ptr[0];
+    dlen = ptr[1];
+
+    dllen -= 2;
+    ptr   += 2;
+    if (dllen < dlen) return ret;
+
+    switch (dtag) {
+      case DVB_DESC_SHORT_EVENT:
+        r = _eit_desc_short_event(ptr, dlen, &ev);
+        break;
+      case DVB_DESC_EXT_EVENT:
+        r = _eit_desc_ext_event(ptr, dlen, &ev);
+        break;
+      case DVB_DESC_CONTENT:
+        r = _eit_desc_content(ptr, dlen, &ev);
+        break;
+      case DVB_DESC_COMPONENT:
+        r = _eit_desc_component(ptr, dlen, &ev);
+        break;
+#if TODO_AGE_RATING
+      case DVB_DESC_PARENTAL_RAT:
+        r = _eit_desc_parental(ptr, dlen, &ev);
+        break;
+#endif
+      default:
+        r = 0;
+        _eit_dtag_dump(dtag, dlen, ptr);
+        break;
+    }
+
+    if (r < 0) return ret;
+    dllen -= dlen;
+    ptr   += dlen;
+  }
+
+  /* Metadata */
+  *save |= epg_broadcast_set_is_hd(ebc, ev.hd, mod);
+  *save |= epg_broadcast_set_is_widescreen(ebc, ev.ws, mod);
+  *save |= epg_broadcast_set_is_audio_desc(ebc, ev.ad, mod);
+  *save |= epg_broadcast_set_is_subtitled(ebc, ev.st, mod);
+  *save |= epg_broadcast_set_is_deafsigned(ebc, ev.ds, mod);
+
+  /* Create episode */
+  ee = ebc->episode;
+  if ( !ee ) {
+    char *uri;
+    uri   = epg_hash(ev.title, ev.summary, ev.desc);
+    if (uri) {
+      if ((ee = epg_episode_find_by_uri(uri, 1, save)))
+        *save |= epg_broadcast_set_episode(ebc, ee, mod);
+      free(uri);
+    }
+  }
+
+  /* Episode data */
+  if (ee) {
+    *save |= epg_episode_set_is_bw(ee, ev.bw, mod);
+    if ( *ev.title )
+      *save |= epg_episode_set_title(ee, ev.title, mod);
+    if ( *ev.summary )
+      *save |= epg_episode_set_summary(ee, ev.summary, mod);
+    if ( *ev.desc )
+      *save |= epg_episode_set_description(ee, ev.desc, mod);
+    if ( ev.genre )
+      *save |= epg_episode_set_genre(ee, ev.genre, mod);
+#if TODO_ADD_EXTRA
+    if ( extra )
+      *save |= epg_episode_set_extra(ee, extra, mod);
+#endif
+  }
+
+  /* Tidy up */
+  if (ev.extra) free(ev.extra);
+  if (ev.genre) epg_genre_list_destroy(ev.genre);
+
+  return ret;
+}
+
 static int _eit_callback
   ( th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, 
     uint8_t tableid, void *opaque )
@@ -149,19 +385,9 @@ static int _eit_callback
   epggrab_ota_mux_t *ota;
   th_dvb_adapter_t *tda;
   service_t *svc;
-  channel_t *ch;
-  epg_broadcast_t *ebc;
-  epg_episode_t *ee;
-  epg_genre_list_t *egl = NULL;
   eit_status_t *sta;
-  int resched = 0, save = 0, save2 = 0, dllen, dtag, dlen;
-  uint16_t tsid, sid, eid;
-  uint8_t bw, hd, ws, ad, ds, st;
-  time_t start, stop;
-  char title[256];
-  char summary[256];
-  char desc[5000];
-  htsmsg_t *extra;
+  int resched = 0, save = 0;
+  uint16_t tsid, sid;
 
   /* Invalid */
   if(tableid < 0x4e || tableid > 0x6f || len < 11)
@@ -169,7 +395,7 @@ static int _eit_callback
 
   /* Get OTA */
   ota = epggrab_ota_find((epggrab_module_ota_t*)mod, tdmi);
-  if (!ota || !ota->status) return 0;
+  if (!ota || !ota->status) return -1;
   sta = ota->status;
 
   /* Already complete */
@@ -187,14 +413,16 @@ static int _eit_callback
     sta->sec  = ptr[3];
 
   /* Complete */
-  } else if (sta->tid == tableid && sta->sec == ptr[3] && sta->tsid == tsid && sta->sid == sid) {
+  } else if (sta->tid  == tableid &&
+             sta->sec  == ptr[3]  &&
+             sta->tsid == tsid    &&
+             sta->sid  == sid) {
     epggrab_ota_complete(ota);
     return 0;
   }
 
   /* Don't process */
-  if((ptr[2] & 1) == 0)
-    return 0;
+  if((ptr[2] & 1) == 0) return 0;
 
   /* Get transport stream */
   // Note: tableid=0x4f,0x60-0x6f is other TS
@@ -209,11 +437,14 @@ static int _eit_callback
 
   /* Get service */
   svc = dvb_transport_find(tdmi, sid, 0, NULL);
-  if (!svc || !svc->s_enabled || !(ch = svc->s_ch)) return 0;
+  if (!svc || !svc->s_enabled || !svc->s_ch) return 0;
 
   /* Ignore (disabled) */
   if (!svc->s_dvb_eit_enable) return 0;
 
+  /* Ignore (not primary EPG service) */
+  if (!service_is_primary_epg(svc)) return 0;
+
   /* Register as interesting */
   if (tableid < 0x50)
     epggrab_ota_register(ota, 20, 300); // 20s grab, 5min interval
@@ -232,177 +463,13 @@ static int _eit_callback
   /* Process events */
   len -= 11;
   ptr += 11;
-  while(len >= 12) {
-    eid   = ptr[0] << 8 | ptr[1];
-    start = dvb_convert_date(&ptr[2]);
-    stop  = start + bcdtoint(ptr[7] & 0xff) * 3600 +
-                    bcdtoint(ptr[8] & 0xff) * 60 +
-                    bcdtoint(ptr[9] & 0xff);
-    dllen = ((ptr[10] & 0x0f) << 8) | ptr[11];
-
-    len -= 12;
-    ptr += 12;
-    if(dllen > len) break;
-
-    /* Get the event */
-    ebc  = epg_broadcast_find_by_time(ch, start, stop, eid, 1, &save2);
-    if (!ebc) {
-      len -= dllen;
-      ptr += dllen;
-      continue;
-    }
-    tvhlog(LOG_DEBUG, "eit", "process tid 0x%02x eid %d event %p %"PRIu64" on %s", tableid, eid, ebc, ebc->id, ch->ch_name);
-
-    /* Mark re-schedule detect (only now/next) */
-    if (save2 && tableid < 0x50) resched = 1;
-
-    /* Process tags */
-    *title    = *summary = *desc = 0;
-    extra     = NULL;
-    hd = ws = bw = ad = st = ds = 0;
-    while(dllen > 0) {
-      dtag = ptr[0];
-      dlen = ptr[1];
-
-      len -= 2; ptr += 2; dllen -= 2; 
-
-      if(dlen > len) break;
-
-      switch(dtag) {
-
-        /* Short descriptor (title/summary) */
-        case DVB_DESC_SHORT_EVENT:
-          dvb_desc_short_event(ptr, dlen,
-                               title, sizeof(title),
-                               summary,  sizeof(summary),
-                               svc->s_dvb_default_charset);
-        break;
-
-        /* Extended (description) */
-        case DVB_DESC_EXT_EVENT:
-          if (!extra) extra = htsmsg_create_map();
-          dvb_desc_extended_event(ptr, dlen,
-                                  desc, sizeof(desc),
-                                  extra,
-                                  svc->s_dvb_default_charset);
-          break;
-
-        /* Content type */
-        case DVB_DESC_CONTENT:
-          while (dlen > 0) {
-            ptr  += 2;
-            dlen -= 2;
-            if   ( *ptr == 0xb1 )
-              bw = 1;
-            else if ( *ptr < 0xb0 ) {
-              if (!egl) egl = calloc(1, sizeof(epg_genre_list_t));
-              epg_genre_list_add_by_eit(egl, *ptr);
-            }
-          }
-          break;
-
-        /* Component descriptor */
-        case DVB_DESC_COMPONENT: {
-          uint8_t c = *ptr & 0x0f;
-          uint8_t t = ptr[1];
-
-          /* MPEG2 (video) */
-          if (c == 0x1) {
-            if (t > 0x08 && t < 0x11) {
-              hd = 1;
-              if ( t != 0x09 && t != 0x0d )
-                ws = 1;
-            } else if (t == 0x02 || t == 0x03 || t == 0x04 || 
-                       t == 0x06 || t == 0x07 || t == 0x08 ) {
-              ws = 1;
-            }
-
-          /* MPEG2 (audio) */
-          } else if (c == 0x2) {
-
-            /* Described */
-            if (t == 0x40 || t == 0x41) 
-              ad = 1;
-
-          /* Subtitles */
-          } else if (c == 0x3) {
-            st = 1;
-    
-          /* H264 */
-          } else if (c == 0x5) {
-            if (t == 0x0b || t == 0x0c || t == 0x10)
-              hd = ws = 1;
-            else if (t == 0x03 || t == 0x04 || t == 0x07 || t == 0x08)
-              ws = 1;
-
-          /* AAC */
-          } else if ( c == 0x6 ) {
-
-            /* Described */
-            if (t == 0x40 || t == 0x44)
-              ad = 1;
-          }
-        }
-        break;
-
-        /* Parental Rating */
-#if TODO_AGE_RATING
-        case DVB_DESC_PARENTAL_RAT:
-          if (*ptr > 0 && *ptr < 15)
-            minage = *ptr + 3;
-#endif
-
-        /* Ignore */
-        default:
-          _eit_dtag_dump(dtag, dlen, ptr);
-          break;
-      }
-
-      len -= dlen; ptr += dlen; dllen -= dlen;
-    }
-
-    /* Metadata */
-    if ( save2 ) {
-      save |= epg_broadcast_set_is_hd(ebc, hd, mod);
-      save |= epg_broadcast_set_is_widescreen(ebc, ws, mod);
-      save |= epg_broadcast_set_is_audio_desc(ebc, ad, mod);
-      save |= epg_broadcast_set_is_subtitled(ebc, st, mod);
-      save |= epg_broadcast_set_is_deafsigned(ebc, ds, mod);
-    }
-
-    /* Create episode */
-    ee = ebc->episode;
-    if ( !ee || save2 ) {
-      char *uri;
-      uri   = epg_hash(title, summary, desc);
-      if (uri) {
-        if ((ee    = epg_episode_find_by_uri(uri, 1, &save2)))
-          save |= epg_broadcast_set_episode(ebc, ee, mod);
-        free(uri);
-      }
-    }
-    save |= save2;
-
-    /* Episode data */
-    if (ee) {
-      save |= epg_episode_set_is_bw(ee, bw, mod);
-      if ( *title )
-        save |= epg_episode_set_title(ee, title, mod);
-      if ( *summary )
-        save |= epg_episode_set_summary(ee, summary, mod);
-      if ( *desc )
-        save |= epg_episode_set_description(ee, desc, mod);
-      if ( egl )
-        save |= epg_episode_set_genre(ee, egl, mod);
-#if TODO_ADD_EXTRA
-      if ( extra )
-        save |= epg_episode_set_extra(ee, extra, mod);
-#endif
-    }
-
-    /* Tidy up */
-    if (extra) free(extra);
-    if (egl)   epg_genre_list_destroy(egl);
+  while (len) {
+    int r;
+    if ((r = _eit_process_event(mod, tableid, svc, ptr, len,
+                                &resched, &save)) < 0)
+      break;
+    len -= r;
+    ptr += r;
   }
   
   /* Update EPG */