diff --git a/src/epggrab.h b/src/epggrab.h index 091c0008..7956aea0 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -198,10 +198,9 @@ struct epggrab_ota_mux LIST_HEAD(,epggrab_ota_map) om_modules; ///< List of linked mods int om_complete; ///< Has completed a scan - int om_active; - time_t om_when; ///< Next event time + gtimer_t om_timer; ///< Per mux active timer - LIST_ENTRY(epggrab_ota_mux) om_q_link; + TAILQ_ENTRY(epggrab_ota_mux) om_q_link; RB_ENTRY(epggrab_ota_mux) om_global_link; }; diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index 34d7ee0c..47653373 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -30,31 +30,36 @@ #include #include -RB_HEAD(,epggrab_ota_mux) epggrab_ota_all; -LIST_HEAD(,epggrab_ota_mux) epggrab_ota_pending; -LIST_HEAD(,epggrab_ota_mux) epggrab_ota_active; +#define EPGGRAB_OTA_MIN_INTERVAL 300 +#define EPGGRAB_OTA_MIN_TIMEOUT 30 -gtimer_t epggrab_ota_pending_timer; -gtimer_t epggrab_ota_active_timer; +#define EPGGRAB_OTA_DONE_COMPLETE 0 +#define EPGGRAB_OTA_DONE_TIMEOUT 1 +#define EPGGRAB_OTA_DONE_STOLEN 2 + +typedef TAILQ_HEAD(epggrab_ota_head,epggrab_ota_mux) epggrab_ota_head_t; + +RB_HEAD(,epggrab_ota_mux) epggrab_ota_all; +epggrab_ota_head_t epggrab_ota_pending; +epggrab_ota_head_t epggrab_ota_active; + +gtimer_t epggrab_ota_kick_timer; +gtimer_t epggrab_ota_pending_timer; SKEL_DECLARE(epggrab_ota_mux_skel, epggrab_ota_mux_t); SKEL_DECLARE(epggrab_svc_link_skel, epggrab_ota_svc_link_t); -static void epggrab_ota_active_timer_cb ( void *p ); -static void epggrab_ota_pending_timer_cb ( void *p ); +static void epggrab_ota_timeout_cb ( void *p ); +static void epggrab_ota_kick_cb ( void *p ); static void epggrab_ota_save ( epggrab_ota_mux_t *ota ); +static void epggrab_ota_free ( epggrab_ota_head_t *head, epggrab_ota_mux_t *ota ); + /* ************************************************************************** * Utilities * *************************************************************************/ -static int -om_time_cmp ( epggrab_ota_mux_t *a, epggrab_ota_mux_t *b ) -{ - return (a->om_when - b->om_when); -} - static int om_id_cmp ( epggrab_ota_mux_t *a, epggrab_ota_mux_t *b ) { @@ -67,22 +72,6 @@ om_svcl_cmp ( epggrab_ota_svc_link_t *a, epggrab_ota_svc_link_t *b ) return strcmp(a->uuid, b->uuid); } -#define EPGGRAB_OTA_MIN_PERIOD 300 -#define EPGGRAB_OTA_MIN_TIMEOUT 30 - -static int -epggrab_ota_period ( int divider ) -{ - int period = 3600; - - period /= divider; - - if (period < EPGGRAB_OTA_MIN_PERIOD) - period = EPGGRAB_OTA_MIN_PERIOD; - - return period; -} - static int epggrab_ota_timeout ( void ) { @@ -95,37 +84,44 @@ epggrab_ota_timeout ( void ) } static void -epggrab_ota_done ( epggrab_ota_mux_t *ota, int timeout ) +epggrab_ota_kick ( int delay ) +{ + gtimer_arm(&epggrab_ota_kick_timer, epggrab_ota_kick_cb, NULL, delay); +} + +static void +epggrab_ota_done ( epggrab_ota_mux_t *om, int reason ) { mpegts_mux_t *mm; - LIST_REMOVE(ota, om_q_link); - ota->om_active = 0; - ota->om_when = dispatch_clock + epggrab_ota_period(1); - LIST_INSERT_SORTED(&epggrab_ota_pending, ota, om_q_link, om_time_cmp); + gtimer_disarm(&om->om_timer); + + TAILQ_REMOVE(&epggrab_ota_active, om, om_q_link); + if (reason == EPGGRAB_OTA_DONE_STOLEN) + TAILQ_INSERT_HEAD(&epggrab_ota_pending, om, om_q_link); + else if (reason == EPGGRAB_OTA_DONE_TIMEOUT) { + char name[256]; + mpegts_mux_t *mm = mpegts_mux_find(om->om_mux_uuid); + mm->mm_display_name(mm, name, sizeof(name)); + tvhlog(LOG_WARNING, "epggrab", "data completion timeout for %s", name); + } /* Remove subscriber */ - if ((mm = mpegts_mux_find(ota->om_mux_uuid))) + if ((mm = mpegts_mux_find(om->om_mux_uuid))) mpegts_mux_unsubscribe_by_name(mm, "epggrab"); - /* Re-arm */ - if (LIST_FIRST(&epggrab_ota_pending) == ota) - epggrab_ota_pending_timer_cb(NULL); - - /* Remove from active */ - if (!timeout) - epggrab_ota_active_timer_cb(NULL); + /* Kick - try start waiting muxes */ + epggrab_ota_kick(1); } static void epggrab_ota_start ( epggrab_ota_mux_t *om, int grace ) { epggrab_ota_map_t *map; - om->om_when = dispatch_clock + epggrab_ota_timeout() + grace; - om->om_active = 1; - LIST_INSERT_SORTED(&epggrab_ota_active, om, om_q_link, om_time_cmp); - if (LIST_FIRST(&epggrab_ota_active) == om) - epggrab_ota_active_timer_cb(NULL); + + TAILQ_INSERT_TAIL(&epggrab_ota_active, om, om_q_link); + gtimer_arm(&om->om_timer, epggrab_ota_timeout_cb, om, + epggrab_ota_timeout() + grace); LIST_FOREACH(map, &om->om_modules, om_link) { map->om_first = 1; map->om_complete = 0; @@ -146,7 +142,7 @@ epggrab_mux_start ( mpegts_mux_t *mm, void *p ) const char *uuid = idnode_uuid_as_str(&mm->mm_id); /* Already started */ - LIST_FOREACH(ota, &epggrab_ota_active, om_q_link) + TAILQ_FOREACH(ota, &epggrab_ota_active, om_q_link) if (!strcmp(ota->om_mux_uuid, uuid)) return; @@ -171,8 +167,13 @@ static void epggrab_mux_stop ( mpegts_mux_t *mm, void *p ) { epggrab_ota_mux_t *ota; - while ((ota = LIST_FIRST(&epggrab_ota_active))) - epggrab_ota_done(ota, 0); + const char *uuid = idnode_uuid_as_str(&mm->mm_id); + + TAILQ_FOREACH(ota, &epggrab_ota_active, om_q_link) + if (!strcmp(ota->om_mux_uuid, uuid)) { + epggrab_ota_done(ota, EPGGRAB_OTA_DONE_STOLEN); + break; + } } /* ************************************************************************** @@ -200,11 +201,9 @@ epggrab_ota_register ota = epggrab_ota_mux_skel; SKEL_USED(epggrab_ota_mux_skel); ota->om_mux_uuid = strdup(uuid); - ota->om_when = dispatch_clock + epggrab_ota_timeout(); - ota->om_active = 1; - LIST_INSERT_SORTED(&epggrab_ota_active, ota, om_q_link, om_time_cmp); - if (LIST_FIRST(&epggrab_ota_active) == ota) - epggrab_ota_active_timer_cb(NULL); + TAILQ_INSERT_TAIL(&epggrab_ota_pending, ota, om_q_link); + if (TAILQ_FIRST(&epggrab_ota_pending) == ota) + epggrab_ota_kick(1); save = 1; } } @@ -232,6 +231,7 @@ epggrab_ota_complete ( epggrab_module_ota_t *mod, epggrab_ota_mux_t *ota ) { int done = 1; + epggrab_ota_mux_t *ota2; epggrab_ota_map_t *map; lock_assert(&global_lock); tvhdebug(mod->id, "grab complete"); @@ -253,68 +253,81 @@ epggrab_ota_complete if (!done) return; /* Done */ - epggrab_ota_done(ota, 0); + TAILQ_FOREACH(ota2, &epggrab_ota_active, om_q_link) + if (ota == ota2) { + epggrab_ota_done(ota, EPGGRAB_OTA_DONE_COMPLETE); + break; + } } /* ************************************************************************** - * Timer callback + * Timer callbacks * *************************************************************************/ static void -epggrab_ota_active_timer_cb ( void *p ) +epggrab_ota_timeout_cb ( void *p ) { - epggrab_ota_mux_t *om = LIST_FIRST(&epggrab_ota_active); - gtimer_disarm(&epggrab_ota_active_timer); + epggrab_ota_mux_t *om = p; lock_assert(&global_lock); + if (!om) return; - /* Double check */ - if (om->om_when > dispatch_clock) - goto done; - /* Re-queue */ - epggrab_ota_done(om, 1); - -done: - om = LIST_FIRST(&epggrab_ota_active); - if (om) - gtimer_arm_abs(&epggrab_ota_active_timer, epggrab_ota_active_timer_cb, - NULL, om->om_when); + epggrab_ota_done(om, EPGGRAB_OTA_DONE_TIMEOUT); } static void -epggrab_ota_pending_timer_cb ( void *p ) +epggrab_ota_kick_cb ( void *p ) { + extern const idclass_t mpegts_mux_class; epggrab_ota_map_t *map; - epggrab_ota_mux_t *om = LIST_FIRST(&epggrab_ota_pending); + epggrab_ota_mux_t *om = TAILQ_FIRST(&epggrab_ota_pending); + epggrab_ota_mux_t *first = NULL; mpegts_mux_t *mm; - int extra = 0; - - gtimer_disarm(&epggrab_ota_pending_timer); + struct { + mpegts_network_t *net; + int failed; + } networks[64], *net; /* more than 64 networks? - you're a king */ + int i, r, networks_count = 0; lock_assert(&global_lock); + if (!om) return; - /* Double check */ - if (om->om_when > dispatch_clock) - goto done; next_one: - LIST_REMOVE(om, om_q_link); - /* Find the mux */ - extern const idclass_t mpegts_mux_class; mm = mpegts_mux_find(om->om_mux_uuid); if (!mm) { - RB_REMOVE(&epggrab_ota_all, om, om_global_link); - while ((map = LIST_FIRST(&om->om_modules))) { - LIST_REMOVE(map, om_link); - free(map); + epggrab_ota_free(&epggrab_ota_pending, om); + goto done; + } + + TAILQ_REMOVE(&epggrab_ota_pending, om, om_q_link); + + /* Check if this network failed before */ + for (i = 0, net = NULL; i < networks_count; i++) { + net = &networks[i]; + if (net->net == mm->mm_network) { + if (net->failed) + goto done; + break; } - free(om->om_mux_uuid); - free(om); + } + if (i >= networks_count) { + net = &networks[networks_count++]; + net->net = mm->mm_network; + net->failed = 0; + } + + if (mm->mm_is_epg(mm) <= 0) { +#if TRACE_ENABLE + char name[256]; + mm->mm_display_name(mm, name, sizeof(name)); + tvhtrace("epggrab", "epg mux %s is disabled, skipping", name); +#endif goto done; } @@ -326,33 +339,26 @@ next_one: if (!map) { char name[256]; mm->mm_display_name(mm, name, sizeof(name)); - tvhdebug("epggrab", "no modules attached to %s, check again later", name); - om->om_when = dispatch_clock + epggrab_ota_period(4); - LIST_INSERT_SORTED(&epggrab_ota_pending, om, om_q_link, om_time_cmp); + tvhdebug("epggrab", "no modules attached to %s, check again next time", name); goto done; } /* Subscribe to the mux */ - if (mm->mm_is_epg(mm) <= 0 || - mpegts_mux_subscribe(mm, "epggrab", SUBSCRIPTION_PRIO_EPG)) { - om->om_active = 0; - om->om_when = dispatch_clock + epggrab_ota_period(4) + extra; - LIST_INSERT_SORTED(&epggrab_ota_pending, om, om_q_link, om_time_cmp); + if ((r = mpegts_mux_subscribe(mm, "epggrab", SUBSCRIPTION_PRIO_EPG))) { + TAILQ_INSERT_TAIL(&epggrab_ota_pending, om, om_q_link); + if (r == SM_CODE_NO_FREE_ADAPTER) + net->failed = 1; + if (first == NULL) + first = om; } else { mpegts_mux_instance_t *mmi = mm->mm_active; epggrab_ota_start(om, mpegts_input_grace(mmi->mmi_input, mm)); } done: - om = LIST_FIRST(&epggrab_ota_pending); - if (om) { - if (om->om_when <= dispatch_clock) { - extra += 60; /* differentiate the mux busy requests */ - goto next_one; - } - gtimer_arm_abs(&epggrab_ota_pending_timer, epggrab_ota_pending_timer_cb, - NULL, om->om_when); - } + om = TAILQ_FIRST(&epggrab_ota_pending); + if (networks_count < ARRAY_SIZE(networks) && om && first != om) + goto next_one; } void @@ -441,7 +447,7 @@ epggrab_ota_load_one free(ota); return; } - LIST_INSERT_SORTED(&epggrab_ota_pending, ota, om_q_link, om_time_cmp); + TAILQ_INSERT_TAIL(&epggrab_ota_pending, ota, om_q_link); if (!(l = htsmsg_get_list(c, "modules"))) return; HTSMSG_FOREACH(f, l) { @@ -476,6 +482,9 @@ epggrab_ota_init ( void ) }; mpegts_add_listener(&ml); + TAILQ_INIT(&epggrab_ota_pending); + TAILQ_INIT(&epggrab_ota_active); + /* Delete old config */ hts_settings_buildpath(path, sizeof(path), "epggrab/otamux"); if (!lstat(path, &st)) @@ -491,19 +500,18 @@ epggrab_ota_init ( void ) htsmsg_destroy(c); } - /* Init timer (immediate call after full init) */ - if (LIST_FIRST(&epggrab_ota_pending)) - gtimer_arm_abs(&epggrab_ota_pending_timer, epggrab_ota_pending_timer_cb, - NULL, 0); + /* Init timer (call after full init - wait for network tuners) */ + if (TAILQ_FIRST(&epggrab_ota_pending)) + epggrab_ota_kick(15); } static void -epggrab_ota_free ( epggrab_ota_mux_t *ota ) +epggrab_ota_free ( epggrab_ota_head_t *head, epggrab_ota_mux_t *ota ) { epggrab_ota_map_t *map; epggrab_ota_svc_link_t *svcl; - LIST_REMOVE(ota, om_q_link); + TAILQ_REMOVE(head, ota, om_q_link); while ((map = LIST_FIRST(&ota->om_modules)) != NULL) { LIST_REMOVE(map, om_link); while ((svcl = RB_FIRST(&map->om_svcs)) != NULL) @@ -520,10 +528,10 @@ epggrab_ota_shutdown ( void ) epggrab_ota_mux_t *ota; pthread_mutex_lock(&global_lock); - while ((ota = LIST_FIRST(&epggrab_ota_active)) != NULL) - epggrab_ota_free(ota); - while ((ota = LIST_FIRST(&epggrab_ota_pending)) != NULL) - epggrab_ota_free(ota); + while ((ota = TAILQ_FIRST(&epggrab_ota_active)) != NULL) + epggrab_ota_free(&epggrab_ota_active, ota); + while ((ota = TAILQ_FIRST(&epggrab_ota_pending)) != NULL) + epggrab_ota_free(&epggrab_ota_pending, ota); pthread_mutex_unlock(&global_lock); SKEL_FREE(epggrab_ota_mux_skel); SKEL_FREE(epggrab_svc_link_skel);