From b7575b18e514d03e9131951e949ca092f5902a1e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 25 May 2012 09:55:24 +0100 Subject: [PATCH] Significant update to the EPG code, now have reference counting and timeouts (not tested) and some general simplifications to the API. --- src/epg.c | 516 ++++++++++++++++++++++++++++++++++-------------------- src/epg.h | 86 +++------ 2 files changed, 353 insertions(+), 249 deletions(-) diff --git a/src/epg.c b/src/epg.c index 4e0cf013..ee1ab553 100644 --- a/src/epg.c +++ b/src/epg.c @@ -16,6 +16,16 @@ * along with this program. If not, see . */ +/* + * TODO: list: + * + * - sanity checks on _destroy calls could just do what we're told and + * unreference all links? + * + * - does the broadcast <-> channel referencing need to be 2 way? + * i.e. do we need to hold onto the CHANNEL info for DVR held broadcasts? + */ + #include #include #include @@ -42,11 +52,15 @@ struct epg_object_tree epg_episodes; struct epg_object_tree epg_channels; struct epg_object_tree epg_broadcasts; -/* Unlinked channels */ -LIST_HEAD(epg_unlinked_channel_list1, epg_channel); -LIST_HEAD(epg_unlinked_channel_list2, channel); -struct epg_unlinked_channel_list1 epg_unlinked_channels1; -struct epg_unlinked_channel_list2 epg_unlinked_channels2; +/* Unmapped */ +LIST_HEAD(epg_channel_unmapped_list, epg_channel); +LIST_HEAD(channel_unmapped_list, channel); +struct epg_channel_unmapped_list epg_channel_unmapped; +struct channel_unmapped_list channel_unmapped; + +/* Unreferenced */ +LIST_HEAD(epg_object_unref_list, epg_object); +struct epg_object_unref_list epg_object_unref; /* Global counters */ static uint64_t _epg_object_idx = 0; @@ -177,11 +191,10 @@ static int _epg_write_sect ( int fd, const char *sect ) void epg_save ( void ) { + // TODO: skip unref'd objects? int fd; epg_object_t *eo, *ec; - // TODO: requires markers in the file or some other means of - // determining where the various object types are? fd = hts_settings_open_file(1, "epgdb"); /* Channels */ @@ -218,7 +231,7 @@ void epg_init ( void ) char *sect = NULL; const char *s; epggrab_stats_t stats; - + /* Map file to memory */ fd = hts_settings_open_file(0, "epgdb"); if ( fd < 0 ) { @@ -313,45 +326,38 @@ 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); + } + + // TODO: remove this if (0)_epg_dump(); } -void epg_add_channel ( channel_t *ch ) -{ -#if TODO_REDO_CHANNEL_LINKING - epg_channel_t *ec; - LIST_INSERT_HEAD(&epg_unlinked_channels2, ch, ch_eulink); - LIST_FOREACH(ec, &epg_unlinked_channels1, ulink) { - if ( _epg_channel_cmp(ec, ch) ) { - epg_channel_set_channel(ec, ch); - break; - } - } -#endif -} - -void epg_rem_channel ( channel_t *ch ) -{ -#if TODO_REDO_CHANNEL_LINKING - if ( ch->ch_epg_channel ) { - ch->ch_epg_channel->channel = NULL; - // TODO: free the channel? - } else { - LIST_REMOVE(ch, ch_eulink); - } -#endif -} - -void epg_mod_channel ( channel_t *ch ) -{ - if ( !ch->ch_epg_channel ) epg_add_channel(ch); -} - - /* ************************************************************************** * Object * *************************************************************************/ +static void _epg_object_destroy ( epg_object_t *eo ) +{ + if (eo->uri) free(eo->uri); +} + +static void _epg_object_getref ( epg_object_t *eo ) +{ + if (eo->refcount == 0) LIST_REMOVE(eo, ulink); + eo->refcount++; +} + +static void _epg_object_putref ( epg_object_t *eo ) +{ + assert(eo->refcount>0); // Sanity! + eo->refcount--; + // TODO: do this here or defer to the epg_updated call? + if (!eo->refcount) eo->destroy(eo); +} + static epg_object_t *_epg_object_find ( int create, int *save, epg_object_tree_t *tree, epg_object_t **skel, int (*cmp) (const void*,const void*)) @@ -369,7 +375,10 @@ static epg_object_t *_epg_object_find *save |= 1; eo = *skel; *skel = NULL; + if (!eo->getref) eo->getref = _epg_object_getref; + if (!eo->putref) eo->putref = _epg_object_putref; _epg_object_idx++; + LIST_INSERT_HEAD(&epg_object_unref, eo, ulink); } } @@ -378,13 +387,15 @@ static epg_object_t *_epg_object_find static epg_object_t *_epg_object_find_by_uri ( const char *uri, int create, int *save, - epg_object_tree_t *tree, size_t size ) + epg_object_tree_t *tree, size_t size, + void (*destroy) (epg_object_t*) ) { int save2 = 0; epg_object_t *eo; static epg_object_t* skel = NULL; static size_t skelsz = 0; + assert(destroy != NULL); // sanity lock_assert(&global_lock); // pointless! if ( !skel || size > skelsz ) { @@ -398,7 +409,8 @@ static epg_object_t *_epg_object_find_by_uri eo = _epg_object_find(create, &save2, tree, &skel, _uri_cmp); if (save2) { - eo->uri = strdup(uri); + eo->uri = strdup(uri); + eo->destroy = destroy; *save |= 1; /* Shrink - not necessary (but saves RAM) */ @@ -422,12 +434,30 @@ static epg_object_t *_epg_object_find_by_id * Brand * *************************************************************************/ +static void _epg_brand_destroy ( epg_object_t *eo ) +{ + epg_brand_t *eb = (epg_brand_t*)eo; + if (RB_FIRST(&eb->seasons)) { + tvhlog(LOG_CRIT, "epg", "attempt to destroy brand with seasons"); + assert(0); + } + if (RB_FIRST(&eb->episodes)) { + tvhlog(LOG_CRIT, "epg", "attempt to destroy brand with episodes"); + assert(0); + } + _epg_object_destroy(eo); + if (eb->title) free(eb->title); + if (eb->summary) free(eb->summary); + free(eb); +} + epg_brand_t* epg_brand_find_by_uri ( const char *uri, int create, int *save ) { return (epg_brand_t*) _epg_object_find_by_uri(uri, create, save, - &epg_brands, sizeof(epg_brand_t)); + &epg_brands, sizeof(epg_brand_t), + _epg_brand_destroy); } epg_brand_t *epg_brand_find_by_id ( uint64_t id ) @@ -471,58 +501,28 @@ int epg_brand_set_season_count ( epg_brand_t *brand, uint16_t count ) return save; } -int epg_brand_add_season ( epg_brand_t *brand, epg_season_t *season, int u ) +static void _epg_brand_add_season + ( epg_brand_t *brand, epg_season_t *season ) { - int save = 0; - epg_season_t *es; - if ( !brand || !season ) return 0; - es = RB_INSERT_SORTED(&brand->seasons, season, blink, _uri_cmp); - if ( es == NULL ) { - if ( u ) save |= epg_season_set_brand(season, brand, 0); - save = 1; - } - return save; + RB_INSERT_SORTED(&brand->seasons, season, blink, _uri_cmp); } -int epg_brand_rem_season ( epg_brand_t *brand, epg_season_t *season, int u ) +static void _epg_brand_rem_season + ( epg_brand_t *brand, epg_season_t *season ) { - int save = 0; - epg_season_t *es; - if ( !brand || !season ) return 0; - es = RB_FIND(&brand->seasons, season, blink, _uri_cmp); - if ( es != NULL ) { - if ( u ) save |= epg_season_set_brand(season, NULL, 0); // TODO: this will do nothing - RB_REMOVE(&brand->seasons, season, blink); - save = 1; - } - return save; + RB_REMOVE(&brand->seasons, season, blink); } -int epg_brand_add_episode ( epg_brand_t *brand, epg_episode_t *episode, int u ) +static void _epg_brand_add_episode + ( epg_brand_t *brand, epg_episode_t *episode ) { - int save = 0; - epg_episode_t *ee; - if ( !brand || !episode ) return 0; - ee = RB_INSERT_SORTED(&brand->episodes, episode, blink, _uri_cmp); - if ( ee == NULL ) { - if ( u ) save |= epg_episode_set_brand(episode, brand, 0); - save = 1; - } - return save; + RB_INSERT_SORTED(&brand->episodes, episode, blink, _uri_cmp); } -int epg_brand_rem_episode ( epg_brand_t *brand, epg_episode_t *episode, int u ) +static void _epg_brand_rem_episode + ( epg_brand_t *brand, epg_episode_t *episode ) { - int save = 0; - epg_episode_t *ee; - if ( !brand || !episode ) return 0; - ee = RB_FIND(&brand->episodes, episode, blink, _uri_cmp); - if ( ee != NULL ) { - if ( u ) save |= epg_episode_set_brand(episode, NULL, 0); // TODO: will do nothing - RB_REMOVE(&brand->episodes, episode, blink); - save = 1; - } - return save; + RB_REMOVE(&brand->episodes, episode, blink); } htsmsg_t *epg_brand_serialize ( epg_brand_t *brand ) @@ -565,12 +565,29 @@ epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create, int *save ) * Season * *************************************************************************/ +static void _epg_season_destroy ( epg_object_t *eo ) +{ + 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); + if (es->brand) { + _epg_brand_rem_season(es->brand, es); + es->brand->_.putref((epg_object_t*)es->brand); + } + if (es->summary) free(es->summary); + free(es); +} + epg_season_t* epg_season_find_by_uri ( const char *uri, int create, int *save ) { return (epg_season_t*) _epg_object_find_by_uri(uri, create, save, - &epg_seasons, sizeof(epg_season_t)); + &epg_seasons, sizeof(epg_season_t), + _epg_season_destroy); } epg_season_t *epg_season_find_by_id ( uint64_t id ) @@ -618,43 +635,28 @@ int epg_season_set_brand ( epg_season_t *season, epg_brand_t *brand, int u ) int save = 0; if ( !season || !brand ) return 0; if ( season->brand != brand ) { - if ( u ) save |= epg_brand_rem_season(season->brand, season, 0); + if ( season->brand ) { + _epg_brand_rem_season(season->brand, season); + season->brand->_.putref((epg_object_t*)season->brand); + } season->brand = brand; - if ( u ) save |= epg_brand_add_season(season->brand, season, 0); + _epg_brand_add_season(brand, season); + season->_.getref((epg_object_t*)season); save = 1; } return save; } -int epg_season_add_episode - ( epg_season_t *season, epg_episode_t *episode, int u ) +static void _epg_season_add_episode + ( epg_season_t *season, epg_episode_t *episode ) { - int save = 0; - epg_episode_t *ee; - if ( !season || !episode ) return 0; - ee = RB_INSERT_SORTED(&season->episodes, episode, slink, _uri_cmp); - if ( ee == NULL ) { - if ( u ) save |= epg_episode_set_season(episode, season, 0); - save = 1; - } - // TODO?: else assert(ee == episode) - return save; + RB_INSERT_SORTED(&season->episodes, episode, slink, _uri_cmp); } -int epg_season_rem_episode - ( epg_season_t *season, epg_episode_t *episode, int u ) +static void _epg_season_rem_episode + ( epg_season_t *season, epg_episode_t *episode ) { - int save = 0; - epg_episode_t *ee; - if ( !season || !episode ) return 0; - ee = RB_FIND(&season->episodes, episode, slink, _uri_cmp); - if ( ee != NULL ) { - // TODO?: assert(ee == episode) - if ( u ) save |= epg_episode_set_season(episode, NULL, 0); // does nothing - RB_REMOVE(&season->episodes, episode, slink); - save = 1; - } - return save; + RB_REMOVE(&season->episodes, episode, slink); } htsmsg_t *epg_season_serialize ( epg_season_t *season ) @@ -704,12 +706,36 @@ epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save ) * Episode * *************************************************************************/ +static void _epg_episode_destroy ( epg_object_t *eo ) +{ + 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); + if (ee->brand) { + _epg_brand_rem_episode(ee->brand, ee); + ee->brand->_.putref((epg_object_t*)ee->brand); + } + if (ee->season) { + _epg_season_rem_episode(ee->season, ee); + ee->season->_.putref((epg_object_t*)ee->season); + } + if (ee->title) free(ee->title); + if (ee->subtitle) free(ee->subtitle); + if (ee->summary) free(ee->summary); + if (ee->description) free(ee->description); + free(ee); +} + epg_episode_t* epg_episode_find_by_uri ( const char *uri, int create, int *save ) { return (epg_episode_t*) _epg_object_find_by_uri(uri, create, save, - &epg_episodes, sizeof(epg_episode_t)); + &epg_episodes, sizeof(epg_episode_t), + _epg_episode_destroy); } epg_episode_t *epg_episode_find_by_id ( uint64_t id ) @@ -792,63 +818,51 @@ int epg_episode_set_part ( epg_episode_t *episode, uint16_t part, uint16_t count return save; } -int epg_episode_set_brand ( epg_episode_t *episode, epg_brand_t *brand, int u ) +int epg_episode_set_brand ( epg_episode_t *episode, epg_brand_t *brand ) { int save = 0; if ( !episode || !brand ) return 0; if ( episode->brand != brand ) { - if ( u ) save |= epg_brand_rem_episode(episode->brand, episode, 0); /// does nothing + if ( episode->brand ) { + _epg_brand_rem_episode(episode->brand, episode); + episode->brand->_.putref((epg_object_t*)episode->brand); + } episode->brand = brand; - if ( u ) save |= epg_brand_add_episode(episode->brand, episode, 0); - save |= 1; + _epg_brand_add_episode(brand, episode); + brand->_.getref((epg_object_t*)brand); + save = 1; } return save; } -int epg_episode_set_season ( epg_episode_t *episode, epg_season_t *season, int u ) +int epg_episode_set_season ( epg_episode_t *episode, epg_season_t *season ) { int save = 0; if ( !episode || !season ) return 0; if ( episode->season != season ) { - if ( u ) save |= epg_season_rem_episode(episode->season, episode, 0); - episode->season = season; - if ( u && episode->season ) { - save |= epg_season_add_episode(episode->season, episode, 0); - save |= epg_brand_add_episode(episode->season->brand, episode, 0); - // TODO: is this the right place? + if ( episode->season ) { + _epg_season_rem_episode(episode->season, episode); + episode->season->_.putref((epg_object_t*)episode->season); } + episode->season = season; + _epg_season_add_episode(season, episode); + season->_.getref((epg_object_t*)season); + if ( season->brand ) save |= epg_episode_set_brand(episode, season->brand); save = 1; } return save; } -int epg_episode_add_broadcast - ( epg_episode_t *episode, epg_broadcast_t *broadcast, int u ) +static void _epg_episode_add_broadcast + ( epg_episode_t *episode, epg_broadcast_t *broadcast ) { - int save = 0; - epg_broadcast_t *eb; - if ( !episode || !broadcast ) return 0; - eb = RB_INSERT_SORTED(&episode->broadcasts, broadcast, elink, _id_cmp); - if ( eb == NULL ) { - if ( u ) save |= epg_broadcast_set_episode(broadcast, episode, 0); - save = 1; - } - return save; + RB_INSERT_SORTED(&episode->broadcasts, broadcast, elink, _id_cmp); } -int epg_episode_rem_broadcast - ( epg_episode_t *episode, epg_broadcast_t *broadcast, int u ) +static void _epg_episode_rem_broadcast + ( epg_episode_t *episode, epg_broadcast_t *broadcast ) { - int save = 0; - epg_broadcast_t *eb; - if ( !episode || !broadcast ) return 0; - eb = RB_FIND(&episode->broadcasts, broadcast, elink, _id_cmp); - if ( eb != NULL ) { - if ( u ) save |= epg_broadcast_set_episode(broadcast, episode, 0); - RB_REMOVE(&episode->broadcasts, broadcast, elink); - save = 1; - } - return save; + RB_REMOVE(&episode->broadcasts, broadcast, elink); } int epg_episode_get_number_onscreen @@ -920,10 +934,10 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save ) if ( (str = htsmsg_get_str(m, "brand")) ) if ( (eb = epg_brand_find_by_uri(str, 0, NULL)) ) - *save |= epg_episode_set_brand(ee, eb, 1); + *save |= epg_episode_set_brand(ee, eb); if ( (str = htsmsg_get_str(m, "season")) ) if ( (es = epg_season_find_by_uri(str, 0, NULL)) ) - *save |= epg_episode_set_season(ee, es, 1); + *save |= epg_episode_set_season(ee, es); return ee; } @@ -932,43 +946,50 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save ) * Broadcast * *************************************************************************/ +static void _epg_broadcast_destroy ( epg_object_t *eo ) +{ + epg_broadcast_t *ebc = (epg_broadcast_t*)eo; + if (ebc->episode) { + _epg_episode_rem_broadcast(ebc->episode, ebc); + ebc->episode->_.putref((epg_object_t*)ebc->episode); + } + free(ebc); +} + epg_broadcast_t* epg_broadcast_find_by_time ( epg_channel_t *channel, time_t start, time_t stop, int create, int *save ) { - static epg_broadcast_t *skel; - if ( !channel || !start || !stop ) return 0; - if ( stop < start ) return 0; - - if ( skel == NULL ) skel = calloc(1, sizeof(epg_broadcast_t)); - skel->channel = channel; - skel->start = start; - skel->stop = stop; - skel->_.id = _epg_object_idx; // TODO: prefer if this wasn't here! - - return (epg_broadcast_t*) - _epg_object_find(create, save, &channel->schedule, - (epg_object_t**)&skel, _ebc_win_cmp); + return epg_channel_get_broadcast(channel, start, stop, create, save); } -// TODO: optional channel? -epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id ) +// TODO: allow optional channel parameter? +epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, epg_channel_t *ec ) { - epg_object_t *eo, *ec; - RB_FOREACH(ec, &epg_channels, glink) { + epg_object_t *eo = NULL, *eoc; + if ( ec ) { eo = _epg_object_find_by_id(id, &((epg_channel_t*)ec)->schedule); - if (eo) return (epg_broadcast_t*)eo; + } else { + RB_FOREACH(eoc, &epg_channels, glink) { + eo = _epg_object_find_by_id(id, &((epg_channel_t*)eoc)->schedule); + if (eo) break; + } } - return NULL; + return (epg_broadcast_t*)eo; } int epg_broadcast_set_episode - ( epg_broadcast_t *broadcast, epg_episode_t *episode, int u ) + ( epg_broadcast_t *broadcast, epg_episode_t *episode ) { int save = 0; if ( !broadcast || !episode ) return 0; if ( broadcast->episode != episode ) { - if ( u ) save |= epg_episode_add_broadcast(episode, broadcast, 0); + if ( broadcast->episode ) { + _epg_episode_rem_broadcast(broadcast->episode, broadcast); + broadcast->episode->_.putref((epg_object_t*)broadcast->episode); + } + _epg_episode_add_broadcast(episode, broadcast); broadcast->episode = episode; + episode->_.getref((epg_object_t*)episode); save = 1; } return save; @@ -1023,7 +1044,7 @@ epg_broadcast_t *epg_broadcast_deserialize ebc = epg_broadcast_find_by_time(ec, start, stop, create, save); if ( !ebc ) return NULL; - *save |= epg_broadcast_set_episode(ebc, ee, 1); + *save |= epg_broadcast_set_episode(ebc, ee); /* Bodge the ID - keep them the same */ ebc->_.id = id; @@ -1042,12 +1063,61 @@ epg_broadcast_t *epg_broadcast_deserialize * Channel * *************************************************************************/ +static void _epg_channel_expire_callback ( void *p ) +{ + time_t next = 0; + epg_object_t *eo; + epg_broadcast_t *ebc; + epg_channel_t *ec = (epg_channel_t*)p; + while ( (eo = RB_FIRST(&ec->schedule)) ) { + ebc = (epg_broadcast_t*)eo; + if ( ebc->stop < dispatch_clock ) { + RB_REMOVE(&ec->schedule, eo, glink); + eo->putref(eo); + } else { + next = ebc->stop; + break; + } + } + + /* re-arm */ + if ( next ) + gtimer_arm_abs(&ec->expire, _epg_channel_expire_callback, ec, next); +} + +static void _epg_channel_destroy ( epg_object_t *eo ) +{ + epg_channel_t *ec = (epg_channel_t*)eo; + if (ec->channel) { + tvhlog(LOG_CRIT, "epg", "attempt to destroy mapped channel"); + assert(0); + } + if (RB_FIRST(&ec->schedule)) { + tvhlog(LOG_CRIT, "epg", "attempt to destroy channel with schedule"); + assert(0); + } + gtimer_disarm(&ec->expire); + if (ec->name) free(ec->name); +#if TODO_NOT_IMPLEMENTED + if (ec->sname) free(ec->sname); + if (ec->sid) free(ec->sid); +#endif + free(ec); +} + epg_channel_t* epg_channel_find_by_uri ( const char *uri, int create, int *save ) { - return (epg_channel_t*) - _epg_object_find_by_uri(uri, create, save, - &epg_channels, sizeof(epg_channel_t)); + int save2 = 0; + epg_channel_t *ec = (epg_channel_t*) + _epg_object_find_by_uri(uri, create, &save2, + &epg_channels, sizeof(epg_channel_t), + _epg_channel_destroy); + if (save2) { + LIST_INSERT_HEAD(&epg_channel_unmapped, ec, umlink); + *save |= 1; + } + return ec; } epg_channel_t *epg_channel_find_by_id ( uint64_t id ) @@ -1062,16 +1132,16 @@ int epg_channel_set_name ( epg_channel_t *channel, const char *name ) if ( !channel || !name ) return 0; if ( !channel->name || strcmp(channel->name, name) ) { channel->name = strdup(name); + // NOTE: does not remap if ( !channel->channel ) { - LIST_FOREACH(ch, &epg_unlinked_channels2, ch_eulink) { + LIST_FOREACH(ch, &channel_unmapped, ch_eulink) { if ( _epg_channel_cmp(channel, ch) ) { epg_channel_set_channel(channel, ch); break; } } } - // TODO: should be try to override? - save = 1; + save |= 1; } return save; } @@ -1079,22 +1149,60 @@ int epg_channel_set_name ( epg_channel_t *channel, const char *name ) int epg_channel_set_channel ( epg_channel_t *ec, channel_t *ch ) { int save = 0; -#if TODO_REDO_CHANNEL_LINKING - if ( !ec || !ch ) return 0; + if ( !ec ) return 0; if ( ec->channel != ch ) { - if (!ec->channel) LIST_REMOVE(ec, ulink); - if (!ch->ch_epg_channel) LIST_REMOVE(ch, ch_eulink); - // TODO: should it be possible to "change" it - //if(ec->channel) - // channel_set_epg_source(ec->channel, NULL) - // LIST_INSERT_HEAD(&epg_unlinked_channels2, ec->channel, ch_eulink); + if (ec->channel) { + tvhlog(LOG_DEBUG, "epg", "unlink channels %-30s -> %s", + ec->_.uri, ec->channel->ch_name); + channel_set_epg_source(ec->channel, NULL); + LIST_INSERT_HEAD(&channel_unmapped, ec->channel, ch_eulink); + } else { + LIST_REMOVE(ec, umlink); + } ec->channel = ch; - channel_set_epg_source(ch, ec); + if (!ch) { + LIST_INSERT_HEAD(&epg_channel_unmapped, ec, umlink); + ec->_.putref((epg_object_t*)ec); + } else { + tvhlog(LOG_DEBUG, "epg", "link channels %-30s -> %s", + ec->_.uri, ch->ch_name); + channel_set_epg_source(ch, ec); + ec->_.getref((epg_object_t*)ec); + } + save |= 1; } -#endif return save; } +epg_broadcast_t *epg_channel_get_broadcast + ( epg_channel_t *channel, time_t start, time_t stop, int create, int *save ) +{ + int save2 = 0; + epg_broadcast_t *ebc; + static epg_broadcast_t *skel; + if ( !channel || !start || !stop ) return NULL; + if ( stop <= start ) return NULL; + if ( stop < dispatch_clock ) return NULL; + + if ( skel == NULL ) skel = calloc(1, sizeof(epg_broadcast_t)); + skel->channel = channel; + skel->start = start; + skel->stop = stop; + skel->_.id = _epg_object_idx; + skel->_.destroy = _epg_broadcast_destroy; + + ebc = (epg_broadcast_t*) + _epg_object_find(create, &save2, &channel->schedule, + (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); + *save |= 1; + } + return ebc; +} + epg_broadcast_t *epg_channel_get_current_broadcast ( epg_channel_t *channel ) { // TODO: its not really the head! @@ -1137,6 +1245,38 @@ epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create, int *save ) return ec; } +/* ************************************************************************** + * Channel mapping + * *************************************************************************/ + +void epg_channel_map_add ( channel_t *ch ) +{ + epg_channel_t *ec; + LIST_FOREACH(ec, &epg_channel_unmapped, umlink) { + if ( _epg_channel_cmp(ec, ch) ) { + epg_channel_set_channel(ec, ch); + return; + } + } + LIST_INSERT_HEAD(&channel_unmapped, ch, ch_eulink); +} + +void epg_channel_map_rem ( channel_t *ch ) +{ + if (ch->ch_epg_channel) { + epg_channel_set_channel(ch->ch_epg_channel, NULL); + } else { + LIST_REMOVE(ch, ch_eulink); + } +} + +void epg_channel_map_mod ( channel_t *ch ) +{ + // If already mapped, ignore + if (!ch->ch_epg_channel) epg_channel_map_add(ch); +} + + /* ************************************************************************** * Querying * *************************************************************************/ diff --git a/src/epg.h b/src/epg.h index 297c15a7..9f833c5a 100644 --- a/src/epg.h +++ b/src/epg.h @@ -62,12 +62,12 @@ typedef struct epg_broadcast_tree epg_broadcast_tree_t; /* Object */ typedef struct epg_object { - RB_ENTRY(epg_object) glink; ///< Global list link - RB_ENTRY(epg_object) ulink; ///< Global unref'd link + RB_ENTRY(epg_object) glink; ///< Global list link + LIST_ENTRY(epg_object) ulink; ///< Global unref'd link - char *uri; ///< Unique ID (from grabber) - uint64_t id; ///< Internal ID - int refcount; ///< Reference counting + char *uri; ///< Unique ID (from grabber) + uint64_t id; ///< Internal ID + int refcount; ///< Reference counting void (*getref) ( epg_object_t* ); ///< Get a reference void (*putref) ( epg_object_t* ); ///< Release a reference @@ -105,14 +105,6 @@ int epg_brand_set_summary ( epg_brand_t *b, const char *summary ) __attribute__((warn_unused_result)); int epg_brand_set_season_count ( epg_brand_t *b, uint16_t season_count ) __attribute__((warn_unused_result)); -int epg_brand_add_season ( epg_brand_t *b, epg_season_t *s, int u ) - __attribute__((warn_unused_result)); -int epg_brand_rem_season ( epg_brand_t *b, epg_season_t *s, int u ) - __attribute__((warn_unused_result)); -int epg_brand_add_episode ( epg_brand_t *b, epg_episode_t *s, int u ) - __attribute__((warn_unused_result)); -int epg_brand_rem_episode ( epg_brand_t *b, epg_episode_t *s, int u ) - __attribute__((warn_unused_result)); /* Serialization */ htsmsg_t *epg_brand_serialize ( epg_brand_t *b ); @@ -151,10 +143,6 @@ int epg_season_set_episode_count ( epg_season_t *s, uint16_t episode_count ) __attribute__((warn_unused_result)); int epg_season_set_brand ( epg_season_t *s, epg_brand_t *b, int u ) __attribute__((warn_unused_result)); -int epg_season_add_episode ( epg_season_t *s, epg_episode_t *e, int u ) - __attribute__((warn_unused_result)); -int epg_season_rem_episode ( epg_season_t *s, epg_episode_t *e, int u ) - __attribute__((warn_unused_result)); /* Serialization */ htsmsg_t *epg_season_serialize ( epg_season_t *b ); @@ -207,13 +195,9 @@ int epg_episode_set_number ( epg_episode_t *e, uint16_t number ) int epg_episode_set_part ( epg_episode_t *e, uint16_t number, uint16_t count ) __attribute__((warn_unused_result)); -int epg_episode_set_brand ( epg_episode_t *e, epg_brand_t *b, int u ) +int epg_episode_set_brand ( epg_episode_t *e, epg_brand_t *b ) __attribute__((warn_unused_result)); -int epg_episode_set_season ( epg_episode_t *e, epg_season_t *s, int u ) - __attribute__((warn_unused_result)); -int epg_episode_add_broadcast ( epg_episode_t *e, epg_broadcast_t *b, int u ) - __attribute__((warn_unused_result)); -int epg_episode_rem_broadcast ( epg_episode_t *e, epg_broadcast_t *b, int u ) +int epg_episode_set_season ( epg_episode_t *e, epg_season_t *s ) __attribute__((warn_unused_result)); /* Acessors */ @@ -261,13 +245,14 @@ typedef struct epg_broadcast /* Lookup */ epg_broadcast_t *epg_broadcast_find_by_time ( epg_channel_t *ch, time_t start, time_t stop, int create, int *save ); -epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id ); +epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, epg_channel_t *ch ); /* Mutators */ -int epg_broadcast_set_episode ( epg_broadcast_t *b, epg_episode_t *e, int u ) +int epg_broadcast_set_episode ( epg_broadcast_t *b, epg_episode_t *e ) __attribute__((warn_unused_result)); /* Accessors */ +// TODO: remove this! epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *b ); /* Serialization */ @@ -288,8 +273,11 @@ typedef struct epg_channel char **sname; ///< DVB svc names (to map) int **sid; ///< DVB svc ids (to map) + epg_object_tree_t schedule; ///< List of broadcasts + LIST_ENTRY(epg_channel) umlink; ///< Unmapped channel link channel_t *channel; ///< Link to real channel - epg_object_tree_t schedule; ///< Schedule (broadcasts) + + gtimer_t expire; ///< Expiration timer } epg_channel_t; /* Lookup */ @@ -304,11 +292,18 @@ 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 ); /* Serialization */ htsmsg_t *epg_channel_serialize ( epg_channel_t *b ); epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create, int *save ); +/* Channel mapping */ +void epg_channel_map_add ( channel_t *ch ); +void epg_channel_map_rem ( channel_t *ch ); +void epg_channel_map_mod ( channel_t *ch ); + /* ************************************************************************ * Querying * ***********************************************************************/ @@ -336,43 +331,12 @@ void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, /* ************************************************************************ - * Function prototypes + * Setup/Shutdown * ***********************************************************************/ -/* - * Load/Save - */ - -void epg_init(void); -void epg_save(void); - -void epg_updated ( void ); - -/* - * Channel linking - */ -void epg_add_channel ( channel_t *ch ); -void epg_rem_channel ( channel_t *ch ); -void epg_mod_channel ( channel_t *ch ); - -/* - * Simple lookup - */ - - -epg_broadcast_t *epg_event_find_by_id(int eventid); - -/* - * Advanced Query - */ - -/* - * Genres? - */ -uint8_t epg_content_group_find_by_name(const char *name); - -const char *epg_content_group_get_name(uint8_t type); - +void epg_init (void); +void epg_save (void); +void epg_updated (void); /* ************************************************************************ * Compatibility code