From e9a8ad5a4827c0eff3ac2f3dbb1c772230fe833c Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 25 May 2012 11:36:44 +0100 Subject: [PATCH] Some fixes and updates to get broadcast expiration and now/next working. --- src/epg.c | 111 ++++++++++++++++++++++++++++++++++------------ src/epg.h | 4 +- src/htsp.c | 14 +++--- src/webui/webui.c | 2 +- 4 files changed, 94 insertions(+), 37 deletions(-) diff --git a/src/epg.c b/src/epg.c index ee1ab553..33fc6c04 100644 --- a/src/epg.c +++ b/src/epg.c @@ -104,6 +104,7 @@ static int _epg_channel_cmp ( epg_channel_t *ec, channel_t *ch ) * Testing/Debug * *************************************************************************/ +#if 0 static void _epg_dump ( void ) { epg_object_t *eo; @@ -155,6 +156,7 @@ static void _epg_dump ( void ) } } } +#endif /* ************************************************************************** * Setup / Update @@ -327,21 +329,22 @@ void epg_init ( void ) void epg_updated ( void ) { epg_object_t *eo; - LIST_FOREACH(eo, &epg_object_unref, ulink) { - printf("unref'd object %lu\n", eo->id); + while ((eo = LIST_FIRST(&epg_object_unref))) { + tvhlog(LOG_DEBUG, "epg", + "unref'd object %lu (%s) created during update", eo->id, eo->uri); + LIST_REMOVE(eo, ulink); + eo->destroy(eo); } - - // TODO: remove this - if (0)_epg_dump(); } /* ************************************************************************** * Object * *************************************************************************/ -static void _epg_object_destroy ( epg_object_t *eo ) +static void _epg_object_destroy ( epg_object_t *eo, epg_object_tree_t *tree ) { if (eo->uri) free(eo->uri); + if (tree) RB_REMOVE(tree, eo, glink); } static void _epg_object_getref ( epg_object_t *eo ) @@ -354,6 +357,7 @@ static void _epg_object_putref ( epg_object_t *eo ) { assert(eo->refcount>0); // Sanity! eo->refcount--; + printf("putref(%lu) = %d\n", eo->id, eo->refcount); // TODO: do this here or defer to the epg_updated call? if (!eo->refcount) eo->destroy(eo); } @@ -436,6 +440,7 @@ static epg_object_t *_epg_object_find_by_id static void _epg_brand_destroy ( epg_object_t *eo ) { + printf("_epg_brand_destroy(%lu, %s)\n", eo->id, eo->uri); epg_brand_t *eb = (epg_brand_t*)eo; if (RB_FIRST(&eb->seasons)) { tvhlog(LOG_CRIT, "epg", "attempt to destroy brand with seasons"); @@ -445,7 +450,7 @@ static void _epg_brand_destroy ( epg_object_t *eo ) tvhlog(LOG_CRIT, "epg", "attempt to destroy brand with episodes"); assert(0); } - _epg_object_destroy(eo); + _epg_object_destroy(eo, &epg_brands); if (eb->title) free(eb->title); if (eb->summary) free(eb->summary); free(eb); @@ -567,12 +572,13 @@ epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create, int *save ) static void _epg_season_destroy ( epg_object_t *eo ) { + printf("_epg_season_destroy(%lu, %s)\n", eo->id, eo->uri); epg_season_t *es = (epg_season_t*)eo; if (RB_FIRST(&es->episodes)) { tvhlog(LOG_CRIT, "epg", "attempt to destory season with episodes"); assert(0); } - _epg_object_destroy(eo); + _epg_object_destroy(eo, &epg_seasons); if (es->brand) { _epg_brand_rem_season(es->brand, es); es->brand->_.putref((epg_object_t*)es->brand); @@ -641,7 +647,7 @@ int epg_season_set_brand ( epg_season_t *season, epg_brand_t *brand, int u ) } season->brand = brand; _epg_brand_add_season(brand, season); - season->_.getref((epg_object_t*)season); + brand->_.getref((epg_object_t*)brand); save = 1; } return save; @@ -708,12 +714,13 @@ epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save ) static void _epg_episode_destroy ( epg_object_t *eo ) { + printf("_epg_episode_destroy(%lu, %s)\n", eo->id, eo->uri); epg_episode_t *ee = (epg_episode_t*)eo; if (RB_FIRST(&ee->broadcasts)) { tvhlog(LOG_CRIT, "epg", "attempt to destroy episode with broadcasts"); assert(0); } - _epg_object_destroy(eo); + _epg_object_destroy(eo, &epg_episodes); if (ee->brand) { _epg_brand_rem_episode(ee->brand, ee); ee->brand->_.putref((epg_object_t*)ee->brand); @@ -948,7 +955,9 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save ) static void _epg_broadcast_destroy ( epg_object_t *eo ) { + printf("_epg_broadcast_destroy(%lu)\n", eo->id); epg_broadcast_t *ebc = (epg_broadcast_t*)eo; + _epg_object_destroy(eo, NULL); if (ebc->episode) { _epg_episode_rem_broadcast(ebc->episode, ebc); ebc->episode->_.putref((epg_object_t*)ebc->episode); @@ -1012,8 +1021,8 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast ) htsmsg_add_u32(m, "id", broadcast->_.id); htsmsg_add_u32(m, "start", broadcast->start); htsmsg_add_u32(m, "stop", broadcast->stop); - // TODO: should these be optional? - htsmsg_add_str(m, "channel", broadcast->channel->_.uri); + if (broadcast->channel) + htsmsg_add_str(m, "channel", broadcast->channel->_.uri); htsmsg_add_str(m, "episode", broadcast->episode->_.uri); if (broadcast->dvb_id) @@ -1032,10 +1041,13 @@ epg_broadcast_t *epg_broadcast_deserialize uint32_t start, stop; uint64_t id; + // TODO: need to handle broadcasts without a channel + // this will happen for DVR maintained broadcasts which + // reference a no longer used channel (and also they will be + // outside the time limit). if ( htsmsg_get_u64(m, "id", &id) ) return NULL; if ( htsmsg_get_u32(m, "start", &start) ) return NULL; if ( htsmsg_get_u32(m, "stop", &stop) ) return NULL; - // TODO: should these be optional? if ( !(str = htsmsg_get_str(m, "channel")) ) return NULL; if ( !(ec = epg_channel_find_by_uri(str, 0, NULL)) ) return NULL; if ( !(str = htsmsg_get_str(m, "episode")) ) return NULL; @@ -1063,30 +1075,72 @@ epg_broadcast_t *epg_broadcast_deserialize * Channel * *************************************************************************/ -static void _epg_channel_expire_callback ( void *p ) +static void _epg_channel_timer_callback ( void *p ) { time_t next = 0; epg_object_t *eo; - epg_broadcast_t *ebc; + epg_broadcast_t *ebc, *cur; epg_channel_t *ec = (epg_channel_t*)p; + + /* Clear now/next */ + cur = ec->now; + ec->now = ec->next = NULL; +#if 0 + printf("dispatch_clock = %lu\n", dispatch_clock); + RB_FOREACH(eo, &ec->schedule, glink) { + ebc = (epg_broadcast_t*)eo; + printf("entry %lu @ %lu to %lu\n", eo->id, ebc->start, ebc->stop); + } +#endif + + /* Check events */ while ( (eo = RB_FIRST(&ec->schedule)) ) { ebc = (epg_broadcast_t*)eo; - if ( ebc->stop < dispatch_clock ) { + + /* Expire */ + if ( ebc->stop <= dispatch_clock ) { RB_REMOVE(&ec->schedule, eo, glink); eo->putref(eo); + tvhlog(LOG_DEBUG, "epg", "expire event %lu from %s", + eo->id, ec->_.uri); + continue; // skip to next + + /* No now */ + } else if ( ebc->start > dispatch_clock ) { + ec->next = ebc; + next = ebc->start; + + /* Now/Next */ } else { - next = ebc->stop; - break; + ec->now = ebc; + ec->next = (epg_broadcast_t*)RB_NEXT(eo, glink); + next = ebc->stop; } + break; } + tvhlog(LOG_DEBUG, "epg", "now/next %lu/%lu set on %s", + ec->now ? ec->now->_.id : 0, + ec->next ? ec->next->_.id : 0, + ec->_.uri); /* re-arm */ - if ( next ) - gtimer_arm_abs(&ec->expire, _epg_channel_expire_callback, ec, next); + if ( next ) { + tvhlog(LOG_DEBUG, "epg", "arm channel timer @ %lu for %s", + next, ec->_.uri); + gtimer_arm_abs(&ec->expire, _epg_channel_timer_callback, ec, next); + } + + /* Update HTSP */ + if ( (cur != ec->now) && ec->channel ) { + tvhlog(LOG_DEBUG, "epg", "inform HTSP of now event change on %s", + ec->_.uri); + htsp_channel_update_current(ec->channel); + } } static void _epg_channel_destroy ( epg_object_t *eo ) { + printf("_epg_channel_destroy(%lu, %s)\n", eo->id, eo->uri); epg_channel_t *ec = (epg_channel_t*)eo; if (ec->channel) { tvhlog(LOG_CRIT, "epg", "attempt to destroy mapped channel"); @@ -1096,6 +1150,7 @@ static void _epg_channel_destroy ( epg_object_t *eo ) tvhlog(LOG_CRIT, "epg", "attempt to destroy channel with schedule"); assert(0); } + _epg_object_destroy(eo, &epg_channels); gtimer_disarm(&ec->expire); if (ec->name) free(ec->name); #if TODO_NOT_IMPLEMENTED @@ -1196,20 +1251,18 @@ epg_broadcast_t *epg_channel_get_broadcast (epg_object_t**)&skel, _ebc_win_cmp); if (save2) { ebc->_.getref((epg_object_t*)ebc); - if (RB_FIRST(&channel->schedule) == (epg_object_t*)ebc) - _epg_channel_expire_callback(channel); + + /* New current/next */ + if ( (RB_FIRST(&channel->schedule) == (epg_object_t*)ebc) || + (channel->now && + RB_NEXT((epg_object_t*)channel->now, glink) == (epg_object_t*)ebc) ) { + _epg_channel_timer_callback(channel); + } *save |= 1; } return ebc; } -epg_broadcast_t *epg_channel_get_current_broadcast ( epg_channel_t *channel ) -{ - // TODO: its not really the head! - if ( !channel ) return NULL; - return (epg_broadcast_t*)RB_FIRST(&channel->schedule); -} - htsmsg_t *epg_channel_serialize ( epg_channel_t *channel ) { htsmsg_t *m; diff --git a/src/epg.h b/src/epg.h index 9f833c5a..90c9c502 100644 --- a/src/epg.h +++ b/src/epg.h @@ -277,6 +277,9 @@ typedef struct epg_channel LIST_ENTRY(epg_channel) umlink; ///< Unmapped channel link channel_t *channel; ///< Link to real channel + epg_broadcast_t *now; ///< Current broadcast + epg_broadcast_t *next; ///< Next broadcast + gtimer_t expire; ///< Expiration timer } epg_channel_t; @@ -291,7 +294,6 @@ int epg_channel_set_name ( epg_channel_t *c, const char *n ) int epg_channel_set_channel ( epg_channel_t *c, channel_t *ch ); /* Accessors */ -epg_broadcast_t *epg_channel_get_current_broadcast ( epg_channel_t *c ); epg_broadcast_t *epg_channel_get_broadcast ( epg_channel_t *ch, time_t start, time_t stop, int create, int *save ); diff --git a/src/htsp.c b/src/htsp.c index 8660ee12..285ea31f 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -310,10 +310,12 @@ htsp_build_channel(channel_t *ch, const char *method) if(ch->ch_icon != NULL) htsmsg_add_str(out, "channelIcon", ch->ch_icon); - now = epg_channel_get_current_broadcast(ch->ch_epg_channel); - if ( now ) next = epg_broadcast_get_next(now); - htsmsg_add_u32(out, "eventId", now ? now->_.id : 0); - htsmsg_add_u32(out, "nextEventId", next ? next->_.id : 0); + if (ch->ch_epg_channel) { + now = ch->ch_epg_channel->now; + next = ch->ch_epg_channel->next; + htsmsg_add_u32(out, "eventId", now ? now->_.id : 0); + htsmsg_add_u32(out, "nextEventId", next ? next->_.id : 0); + } LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) { ct = ctm->ctm_tag; @@ -1425,8 +1427,8 @@ htsp_channel_update_current(channel_t *ch) htsmsg_add_str(m, "method", "channelUpdate"); htsmsg_add_u32(m, "channelId", ch->ch_id); - now = epg_channel_get_current_broadcast(ch->ch_epg_channel); - next = epg_broadcast_get_next(now); + now = ch->ch_epg_channel->now; + next = ch->ch_epg_channel->next; htsmsg_add_u32(m, "eventId", now ? now->_.id : 0); htsmsg_add_u32(m, "nextEventId", next ? next->_.id : 0); htsp_async_send(m); diff --git a/src/webui/webui.c b/src/webui/webui.c index 14706634..0d9db992 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -181,7 +181,7 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, th_subscription_t sm->sm_data = NULL; epg_broadcast_t *e = NULL; - if(s->ths_channel) e = epg_channel_get_current_broadcast(s->ths_channel->ch_epg_channel); + if(s->ths_channel) e = s->ths_channel->ch_epg_channel->now; if(e && event_id != e->_.id) { event_id = e->_.id;