diff --git a/debian/changelog b/debian/changelog index c801cb35..616931db 100644 --- a/debian/changelog +++ b/debian/changelog @@ -69,6 +69,13 @@ hts-tvheadend (2.3) hts; urgency=low * Add exponential backoff for reconnect attempt in code word client. Ticket #80 + * Try to detect duplicate EPG entries from the DVB feed and adjust + EPG accordingly. The EPG code will search for events with the same + DVB event ID +- 2 events from the current one. If the event id is + equal, the prvious (old) entry will be removed in favor of the new one. + Reason for not blindingly trusting the event id is that some networks + seem to (incorrectly) reuse IDs. + hts-tvheadend (2.2) hts; urgency=low * Set $HOME so forked processes (XMLTV) will have correct environment diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 34addda2..2364b7c6 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -522,7 +522,8 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, continue; } - if((e = epg_event_create(ch, start_time, start_time + duration)) == NULL) { + if((e = epg_event_create(ch, start_time, start_time + duration, + event_id)) == NULL) { len -= dllen; ptr += dllen; continue; diff --git a/src/epg.c b/src/epg.c index 938a1c3c..ce2a5630 100644 --- a/src/epg.c +++ b/src/epg.c @@ -165,114 +165,6 @@ epg_event_set_content_type(event_t *e, epg_content_type_t *ect) } -/** - * - */ -event_t * -epg_event_create(channel_t *ch, time_t start, time_t stop) -{ - static event_t *skel; - event_t *e; - static int tally; - - if((stop - start) > 11 * 3600) - return NULL; - - if(stop <= start) - return NULL; - - lock_assert(&global_lock); - - if(skel == NULL) - skel = calloc(1, sizeof(event_t)); - - skel->e_start = start; - - e = RB_INSERT_SORTED(&ch->ch_epg_events, skel, e_channel_link, e_ch_cmp); - if(e == NULL) { - /* New entry was inserted */ - e = skel; - skel = NULL; - - e->e_id = ++tally; - e->e_stop = stop; - - LIST_INSERT_HEAD(&epg_hash[e->e_id & EPG_GLOBAL_HASH_MASK], e, - e_global_link); - - e->e_refcount = 1; - e->e_channel = ch; - epg_event_changed(e); - - if(e == RB_FIRST(&ch->ch_epg_events)) { - /* First in temporal order, arm expiration timer */ - - gtimer_arm_abs(&ch->ch_epg_timer_head, epg_expire_event_from_channel, - ch, e->e_stop); - } - - - - if(ch->ch_epg_timer_current.gti_callback == NULL || - start < ch->ch_epg_timer_current.gti_expire) { - - gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event, - ch, e->e_start); - } - } else { - /* Already exist */ - - if(stop > e->e_stop) { - /* We allow the event to extend in time */ -#if 0 - printf("Event %s on %s extended stop time\n", e->e_title, - e->e_channel->ch_name); - printf("Previous %s", ctime(&e->e_stop)); - printf(" New %s", ctime(&stop)); -#endif - e->e_stop = stop; - epg_event_changed(e); - - if(e == ch->ch_epg_current) { - gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event, - ch, e->e_start); - } - } - } - return e; -} - - -/** - * - */ -event_t * -epg_event_find_by_time(channel_t *ch, time_t t) -{ - event_t skel, *e; - - skel.e_start = t; - e = RB_FIND_LE(&ch->ch_epg_events, &skel, e_channel_link, e_ch_cmp); - if(e == NULL || e->e_stop < t) - return NULL; - return e; -} - - -/** - * - */ -event_t * -epg_event_find_by_id(int eventid) -{ - event_t *e; - - LIST_FOREACH(e, &epg_hash[eventid & EPG_GLOBAL_HASH_MASK], e_global_link) - if(e->e_id == eventid) - break; - return e; -} - /** * @@ -333,6 +225,140 @@ epg_remove_event_from_channel(channel_t *ch, event_t *e) } +/** + * + */ +event_t * +epg_event_create(channel_t *ch, time_t start, time_t stop, int dvb_id) +{ + static event_t *skel; + event_t *e, *p, *n; + static int tally; + + if((stop - start) > 11 * 3600) + return NULL; + + if(stop <= start) + return NULL; + + lock_assert(&global_lock); + + if(skel == NULL) + skel = calloc(1, sizeof(event_t)); + + skel->e_start = start; + + e = RB_INSERT_SORTED(&ch->ch_epg_events, skel, e_channel_link, e_ch_cmp); + if(e == NULL) { + /* New entry was inserted */ + e = skel; + skel = NULL; + + e->e_id = ++tally; + e->e_stop = stop; + e->e_dvb_id = dvb_id; + + LIST_INSERT_HEAD(&epg_hash[e->e_id & EPG_GLOBAL_HASH_MASK], e, + e_global_link); + + e->e_refcount = 1; + e->e_channel = ch; + epg_event_changed(e); + + if(e == RB_FIRST(&ch->ch_epg_events)) { + /* First in temporal order, arm expiration timer */ + + gtimer_arm_abs(&ch->ch_epg_timer_head, epg_expire_event_from_channel, + ch, e->e_stop); + } + + + + if(ch->ch_epg_timer_current.gti_callback == NULL || + start < ch->ch_epg_timer_current.gti_expire) { + + gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event, + ch, e->e_start); + } + } else { + /* Already exist */ + + if(stop > e->e_stop) { + /* We allow the event to extend in time */ +#if 0 + printf("Event %s on %s extended stop time\n", e->e_title, + e->e_channel->ch_name); + printf("Previous %s", ctime(&e->e_stop)); + printf(" New %s", ctime(&stop)); +#endif + e->e_stop = stop; + epg_event_changed(e); + + if(e == ch->ch_epg_current) { + gtimer_arm_abs(&ch->ch_epg_timer_current, epg_ch_check_current_event, + ch, e->e_start); + } + } + } + + + if(dvb_id != -1) { + /* Erase any close events with the same DVB event id */ + + if((p = RB_PREV(e, e_channel_link)) != NULL) { + if(p->e_dvb_id == dvb_id) { + epg_remove_event_from_channel(ch, p); + } else if((p = RB_PREV(p, e_channel_link)) != NULL) { + if(p->e_dvb_id == dvb_id) + epg_remove_event_from_channel(ch, p); + } + } + + if((n = RB_NEXT(e, e_channel_link)) != NULL) { + if(n->e_dvb_id == dvb_id) { + epg_remove_event_from_channel(ch, n); + } else if((n = RB_NEXT(n, e_channel_link)) != NULL) { + if(n->e_dvb_id == dvb_id) + epg_remove_event_from_channel(ch, n); + } + } + } + return e; +} + + +/** + * + */ +event_t * +epg_event_find_by_time(channel_t *ch, time_t t) +{ + event_t skel, *e; + + skel.e_start = t; + e = RB_FIND_LE(&ch->ch_epg_events, &skel, e_channel_link, e_ch_cmp); + if(e == NULL || e->e_stop < t) + return NULL; + return e; +} + + +/** + * + */ +event_t * +epg_event_find_by_id(int eventid) +{ + event_t *e; + + LIST_FOREACH(e, &epg_hash[eventid & EPG_GLOBAL_HASH_MASK], e_global_link) + if(e->e_id == eventid) + break; + return e; +} + + + /** * */ diff --git a/src/epg.h b/src/epg.h index 3a3952b1..9c922e2d 100644 --- a/src/epg.h +++ b/src/epg.h @@ -61,10 +61,7 @@ typedef struct event { const char *e_title; /* UTF-8 encoded */ const char *e_desc; /* UTF-8 encoded */ - int e_source; /* higer is better, and we never downgrade */ - -#define EVENT_SRC_XMLTV 1 -#define EVENT_SRC_DVB 2 + int e_dvb_id; } event_t; @@ -80,7 +77,8 @@ void epg_event_set_desc(event_t *e, const char *desc); void epg_event_set_content_type(event_t *e, epg_content_type_t *ect); -event_t *epg_event_create(channel_t *ch, time_t start, time_t stop); +event_t *epg_event_create(channel_t *ch, time_t start, time_t stop, + int dvb_id); event_t *epg_event_find_by_time(channel_t *ch, time_t t); diff --git a/src/xmltv.c b/src/xmltv.c index 247d7db5..db03b1ea 100644 --- a/src/xmltv.c +++ b/src/xmltv.c @@ -334,7 +334,7 @@ xmltv_parse_programme_tags(xmltv_channel_t *xc, htsmsg_t *tags, const char *desc = xmltv_get_cdata_by_tag(tags, "desc"); LIST_FOREACH(ch, &xc->xc_channels, ch_xc_link) { - if((e = epg_event_create(ch, start, stop)) == NULL) + if((e = epg_event_create(ch, start, stop, -1)) == NULL) continue; if(title != NULL) epg_event_set_title(e, title);