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
* ***********************************************************************/
}
/* ************************************************************************
- * 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 )
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)
/* 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 */
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
/* 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
/* 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 */