diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index d9c10f75..1f6ba229 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -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; - if((r = dvb_get_string_with_len(title, titlelen, ptr, len, dvb_default_charset)) < 0) - return -1; - ptr += r; len -= r; + /* Language (skip) */ + len -= 3; + ptr += 3; - if((r = dvb_get_string_with_len(desc, desclen, 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; + + 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) { + if (len < 6) return -1; - /* 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; + /* 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); + len -= r; + ptr += r; - // TODO: should this extend existing strings? - htsmsg_add_str(extra, ikey, ival); + /* 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 */