Updated EIT code to (hopefully) more robust implementation and included the primary service checking.

This commit is contained in:
Adam Sutton 2012-07-11 13:18:10 +01:00
parent d9f6fd39e9
commit d6ca0eb120

View 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;
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 */