diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 9b528554..cfb42e1a 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -199,8 +199,6 @@ typedef struct th_dvb_mux_instance { int tdmi_tune_failed; // Adapter failed to tune this frequency // Don't try again - int tdmi_weight; - struct th_subscription_list tdmi_subscriptions; } th_dvb_mux_instance_t; @@ -231,6 +229,9 @@ typedef struct dvb_table_feed { typedef struct th_dvb_adapter { + int tda_instance; + + TAILQ_ENTRY(th_dvb_adapter) tda_global_link; dvb_network_t *tda_dn; @@ -403,8 +404,6 @@ void dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int r); void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src); -void dvb_adapter_clean(th_dvb_adapter_t *tda); - int dvb_adapter_destroy(th_dvb_adapter_t *tda); void dvb_adapter_notify(th_dvb_adapter_t *tda); @@ -490,9 +489,15 @@ dvb_mux_t *dvb_mux_find(dvb_network_t *dn, const char *netname, uint16_t onid, uint16_t tsid, int enabled); +void dvb_mux_stop(th_dvb_mux_instance_t *tdmi); + void dvb_mux_initial_scan_done(dvb_mux_t *dm); -int dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi); +int dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi, const char *reason); + +void dvb_create_tdmis(dvb_mux_t *dm); + +int tdmi_current_weight(const th_dvb_mux_instance_t *tdmi); /** diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index d43938db..03cc15a2 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -47,7 +47,7 @@ struct th_dvb_adapter_queue dvb_adapters; static void *dvb_adapter_input_dvr(void *aux); - +static int dvb_adapter_instance_generator; /** * @@ -59,6 +59,7 @@ tda_alloc(void) pthread_mutex_init(&tda->tda_delivery_mutex, NULL); TAILQ_INIT(&tda->tda_satconfs); streaming_pad_init(&tda->tda_streaming_pad); + tda->tda_instance = ++dvb_adapter_instance_generator; return tda; } @@ -631,87 +632,6 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) } } -#if 0 -/** - * If nobody is subscribing, cycle thru all muxes to get some stats - * and EIT updates - */ -void -dvb_adapter_mux_scanner(void *aux) -{ - th_dvb_adapter_t *tda = aux; - th_dvb_mux_instance_t *tdmi; - - if(tda->tda_rootpath == NULL) - return; // No hardware - - // default period - gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 20); - - /* No muxes */ - if(LIST_FIRST(&tda->tda_dn->dn_mux_instances) == NULL) { - dvb_adapter_poweroff(tda); - return; - } - - /* Someone is actively using */ - if(service_compute_weight(&tda->tda_transports) > 0) - return; - - if(tda->tda_mux_current != NULL && - LIST_FIRST(&tda->tda_mux_current->tdmi_subscriptions) != NULL) - return; // Someone is doing full mux dump - - /* Check if we have muxes pending for quickscan, if so, choose them */ - if((tdmi = TAILQ_FIRST(&tda->tda_dn->dn_initial_scan_queue)) != NULL) { - dvb_fe_tune(tdmi, "Initial autoscan"); - return; - } - - /* Check EPG */ - if (tda->tda_dn->dn_mux_epg) { - // timeout anything not complete - epggrab_mux_stop(tda->tda_dn->dn_mux_epg, 1); - tda->tda_dn->dn_mux_epg = NULL; // skip this time - } else { - tda->tda_dn->dn_mux_epg = epggrab_mux_next(tda); - } - - /* EPG */ - if (tda->tda_dn->dn_mux_epg) { - int period = epggrab_mux_period(tda->tda_dn->dn_mux_epg); - if (period > 20) - gtimer_arm(&tda->tda_mux_scanner_timer, - dvb_adapter_mux_scanner, tda, period); - dvb_fe_tune(tda->tda_dn->dn_mux_epg->dm_tdmi, "EPG scan"); - return; - - } - - /* Ensure we stop current mux and power off (if required) */ - if (tda->tda_mux_current) - dvb_fe_stop(tda->tda_mux_current, 0); -} -#endif - -/** - * - */ -void -dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src) -{ -#if 0 - th_dvb_mux_instance_t *tdmi_src, *tdmi_dst; - - lock_assert(&global_lock); - - while((tdmi_dst = LIST_FIRST(&dst->tda_dn->dn_mux_instances)) != NULL) - dvb_mux_destroy(tdmi_dst); - - tda_save(dst); -#endif - abort(); // XXX(dvbreorg) -} /** @@ -746,24 +666,6 @@ dvb_adapter_destroy(th_dvb_adapter_t *tda) #endif -/** - * - */ -void -dvb_adapter_clean(th_dvb_adapter_t *tda) -{ - service_t *t; - - lock_assert(&global_lock); - - while((t = LIST_FIRST(&tda->tda_transports)) != NULL) - /* Flush all subscribers */ - service_remove_subscriber(t, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN); -} - - - - /** * */ @@ -921,7 +823,6 @@ dvb_adapter_input_dvr(void *aux) htsmsg_t * dvb_adapter_build_msg(th_dvb_adapter_t *tda) { - char buf[100]; htsmsg_t *m = htsmsg_create_map(); int fdiv; @@ -933,8 +834,8 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda) if(tda->tda_current_tdmi != NULL) { th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi; - dvb_mux_nicename(buf, sizeof(buf), tda->tda_current_tdmi->tdmi_mux); - htsmsg_add_str(m, "currentMux", buf); + htsmsg_add_str(m, "currentMux", + dvb_mux_nicename(tda->tda_current_tdmi->tdmi_mux)); htsmsg_add_u32(m, "signal", MIN(tdmi->tdmi_signal * 100 / 65535, 100)); htsmsg_add_u32(m, "snr", tdmi->tdmi_snr); diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index a01fb598..916fe523 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -254,32 +254,6 @@ dvb_fe_monitor(void *aux) } -#if 0 -/** - * Stop the given TDMI - */ -void -dvb_fe_stop(th_dvb_adapter_t *tda, int retune) -{ - lock_assert(&global_lock); - - th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi; - assert(tdmi != NULL); - dvb_mux_t *dm = tdmi->tdmi_mux; - - dvb_table_flush_all(dm); - - epggrab_mux_stop(dm, 0); - -#if 0 /// XXX(dvbreorg) - if(!retune) { - gtimer_disarm(&tda->tda_fe_monitor_timer); - dvb_adapter_stop(tda); - } -#endif -} -#endif - #if DVB_API_VERSION >= 5 @@ -385,23 +359,42 @@ dvb_fe_tune_s2(th_dvb_adapter_t *tda, dvb_mux_conf_t *dmc) +/** + * + */ int -dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi) +dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi, const char *reason) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - const dvb_mux_t *dm = tdmi->tdmi_mux; + dvb_mux_t *dm = tdmi->tdmi_mux; // copy dmc, cause frequency may be change with FE_QPSK dvb_mux_conf_t dmc = dm->dm_conf; dvb_frontend_parameters_t* p = &dmc.dmc_fe_params; int r; char buf[256]; + if(tda->tda_current_tdmi != NULL) { + + if(tda->tda_current_tdmi == tdmi) + return 0; // Already currently tuned + + /* + * Adapter tuned to something else. + * But at this stage we're no longer caring about such things. + * That should have been sorted out by our callers. + * So let's just kick it out the current occupant + */ + dvb_mux_stop(tda->tda_current_tdmi); + } + + free(tda->tda_tune_reason); + tda->tda_tune_reason = strdup(reason); + dvb_adapter_start(tda); + // Make sure dvb_mux_stop() did the right thing assert(tda->tda_current_tdmi == NULL); - - if(tda->tda_fe_type == FE_QPSK) { /* DVB-S */ @@ -467,8 +460,23 @@ dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi) } - if(!r) + if(!r) { gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); + tda->tda_current_tdmi = tdmi; + dm->dm_current_tdmi = tdmi; + + dvb_table_add_default(dm); + epggrab_mux_start(dm); + dvb_adapter_notify(tda); + + } else { + + tvhlog(LOG_ERR, "dvb", "\"%s\" tuning to \"%s\"" + " -- Front configuration failed -- %s", + tda->tda_rootpath, dvb_mux_nicename(dm), strerror(errno)); + /* Mark as bad */ + tdmi->tdmi_tune_failed = 1; + } return r; } diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index 00b54484..b7f11589 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -1293,7 +1293,7 @@ dvb_mux_get_childs(struct idnode *self) /** * These are created on the fly */ -static void +void dvb_create_tdmis(dvb_mux_t *dm) { th_dvb_mux_instance_t *tdmi; @@ -1322,36 +1322,64 @@ dvb_create_tdmis(dvb_mux_t *dm) /** * */ -static void +void dvb_mux_stop(th_dvb_mux_instance_t *tdmi) { dvb_mux_t *dm = tdmi->tdmi_mux; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + service_t *s; + assert(dm->dm_current_tdmi == tdmi); assert(tda->tda_current_tdmi == tdmi); + lock_assert(&global_lock); + + /* Flush all subscribers */ + while((s = LIST_FIRST(&tda->tda_transports)) != NULL) + service_remove_subscriber(s, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN); + dvb_table_flush_all(dm); epggrab_mux_stop(dm, 0); - assert(dm->dm_scan_status == DM_SCAN_DONE); + if(dm->dm_scan_status == DM_SCAN_CURRENT) { + /* + * If we were currently doing initial scan but lost the adapter + * before finishing, put us back on the pending queue + */ + dvb_network_t *dn = dm->dm_dn; + TAILQ_REMOVE(&dn->dn_initial_scan_current_queue, dm, dm_scan_link); + dm->dm_scan_status = DM_SCAN_PENDING; + TAILQ_INSERT_TAIL(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link); + dvb_network_schedule_initial_scan(dn); + } dm->dm_current_tdmi = NULL; tda->tda_current_tdmi = NULL; - - printf("NEED TO TAKE CARE OF SERVICES in dvb_mux_stop\n"); } /** * */ -static int -tdmi_compute_weight(const th_dvb_mux_instance_t *tdmi) +int +tdmi_current_weight(const th_dvb_mux_instance_t *tdmi) { - const dvb_mux_t *dm = tdmi->tdmi_mux; - if(dm->dm_scan_status == DM_SCAN_CURRENT) - return 1; - return 0; + int w = 0; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + const service_t *s; + + + // This max weight of all subscriptions could be kept in the adapter struct + pthread_mutex_lock(&tda->tda_delivery_mutex); + LIST_FOREACH(s, &tda->tda_transports, s_active_link) { + const th_subscription_t *ths; + LIST_FOREACH(ths, &s->s_subscriptions, ths_service_link) + w = MAX(w, ths->ths_weight); + } + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + w = MAX(w, tdmi->tdmi_mux->dm_scan_status == DM_SCAN_CURRENT); + return w; } @@ -1379,61 +1407,44 @@ dvb_mux_tune(dvb_mux_t *dm, const char *reason, int weight) th_dvb_mux_instance_t *tdmi; int r; - assert(dm->dm_current_tdmi == NULL); - lock_assert(&global_lock); - dvb_create_tdmis(dm); + if(dm->dm_current_tdmi == NULL) { - retry: - // Figure which adapter to use - LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) - if(!tdmi->tdmi_tune_failed && tdmi->tdmi_adapter->tda_current_tdmi == NULL) - break; + dvb_create_tdmis(dm); - if(tdmi == NULL) { - // None available, need to strike one out - LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) { - if(tdmi->tdmi_tune_failed) - continue; + while(1) { + // Figure which adapter to use + LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) + if(!tdmi->tdmi_tune_failed && tdmi->tdmi_adapter->tda_current_tdmi == NULL) + break; - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - th_dvb_mux_instance_t *t2 = tda->tda_current_tdmi; - assert(t2 != NULL); - assert(t2 != tdmi); + if(tdmi == NULL) { + // None available, need to strike one out + LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) { + if(tdmi->tdmi_tune_failed) + continue; - if(tdmi_compute_weight(t2) < weight) { - dvb_mux_stop(t2); - break; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + th_dvb_mux_instance_t *t2 = tda->tda_current_tdmi; + assert(t2 != NULL); + assert(t2 != tdmi); + + if(tdmi_current_weight(t2) < weight) { + break; + } + } + + if(tdmi == NULL) + return SM_CODE_NO_FREE_ADAPTER; } + + r = dvb_fe_tune_tdmi(tdmi, reason); + if(!r) + break; } - - if(tdmi == NULL) - return SM_CODE_NO_FREE_ADAPTER; - } - tdmi->tdmi_weight = weight; - - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - - free(tda->tda_tune_reason); - tda->tda_tune_reason = strdup(reason); - - r = dvb_fe_tune_tdmi(tdmi); - - if(r != 0) { - tvhlog(LOG_ERR, "dvb", "\"%s\" tuning to \"%s\"" - " -- Front configuration failed -- %s", - tda->tda_rootpath, dvb_mux_nicename(dm), strerror(errno)); - - /* Mark as bad */ - tdmi->tdmi_tune_failed = 1; - goto retry; - } - - tda->tda_current_tdmi = tdmi; - dm->dm_current_tdmi = tdmi; if(dm->dm_scan_status == DM_SCAN_PENDING) { TAILQ_REMOVE(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link); dm->dm_scan_status = DM_SCAN_CURRENT; @@ -1442,9 +1453,5 @@ dvb_mux_tune(dvb_mux_t *dm, const char *reason, int weight) gtimer_arm(&dm->dm_initial_scan_timeout, dvb_mux_initial_scan_timeout, dm, 10); } - dvb_table_add_default(dm); - epggrab_mux_start(dm); - - dvb_adapter_notify(tda); return 0; } diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 98d14650..8b8e1ffb 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -55,54 +55,48 @@ static htsmsg_t *dvb_service_serialize(service_t *s, int full); * transports that is subscribing to the adapter */ static int -dvb_service_start(service_t *t, unsigned int weight, int force_start) +dvb_service_start(service_t *s, int instance) { -#if 0 - int w, r; - th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; - th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi; + int r; + dvb_mux_t *dm = s->s_dvb_mux; + th_dvb_mux_instance_t *tdmi; + + assert(s->s_status == SERVICE_IDLE); lock_assert(&global_lock); - if(tda->tda_rootpath == NULL) - return SM_CODE_NO_HW_ATTACHED; + LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) + if(tdmi->tdmi_adapter->tda_instance == instance) + break; - if(t->s_dvb_mux_instance && !t->s_dvb_mux_instance->tdmi_enabled) - return SM_CODE_MUX_NOT_ENABLED; /* Mux is disabled */ + assert(tdmi != NULL); // We should always find this instance - /* Check if adapter is idle, or already tuned */ + r = dvb_fe_tune_tdmi(tdmi, "service start"); - if(tdmi != NULL && - (tdmi != t->s_dvb_mux_instance || - tda->tda_hostconnection == HOSTCONNECTION_USB12)) { - - w = service_compute_weight(&tda->tda_transports); - if(w && w >= weight && !force_start) - /* We are outranked by weight, cant use it */ - return SM_CODE_NOT_FREE; - - if(LIST_FIRST(&tdmi->tdmi_subscriptions) != NULL) - return SM_CODE_NOT_FREE; - - dvb_adapter_clean(tda); + if(!r) { + th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter; + pthread_mutex_lock(&tda->tda_delivery_mutex); + LIST_INSERT_HEAD(&tda->tda_transports, s, s_active_link); + pthread_mutex_unlock(&tda->tda_delivery_mutex); + tda->tda_open_service(tda, s); + dvb_table_add_pmt(dm, s->s_pmt_pid); } - pthread_mutex_lock(&tda->tda_delivery_mutex); - - r = dvb_fe_tune(t->s_dvb_mux_instance->tdmi_mux, "Transport start"); - if(!r) - LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link); - - pthread_mutex_unlock(&tda->tda_delivery_mutex); - - if(!r) - tda->tda_open_service(tda, t); - - dvb_table_add_pmt(t->s_dvb_mux_instance->tdmi_mux, t->s_pmt_pid); - return r; -#endif - return SM_CODE_NO_FREE_ADAPTER; +} + + +/** + * + */ +static th_dvb_adapter_t * +dvb_service_get_tda(service_t *s) +{ + dvb_mux_t *dm = s->s_dvb_mux; + assert(dm->dm_current_tdmi != NULL); + th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter; + assert(tda != NULL); + return tda; } @@ -110,21 +104,18 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start) * */ static void -dvb_service_stop(service_t *t) +dvb_service_stop(service_t *s) { -#if 0 - th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; - + th_dvb_adapter_t *tda = dvb_service_get_tda(s); lock_assert(&global_lock); pthread_mutex_lock(&tda->tda_delivery_mutex); - LIST_REMOVE(t, s_active_link); + LIST_REMOVE(s, s_active_link); pthread_mutex_unlock(&tda->tda_delivery_mutex); - tda->tda_close_service(tda, t); + tda->tda_close_service(tda, s); - t->s_status = SERVICE_IDLE; -#endif + s->s_status = SERVICE_IDLE; } @@ -132,14 +123,32 @@ dvb_service_stop(service_t *t) * */ static void -dvb_service_refresh(service_t *t) +dvb_service_refresh(service_t *s) { -#if 0 - th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; - + th_dvb_adapter_t *tda = dvb_service_get_tda(s); lock_assert(&global_lock); - tda->tda_open_service(tda, t); -#endif + tda->tda_open_service(tda, s); +} + + +/** + * + */ +static void +dvb_service_enlist(struct service *s, struct service_instance_list *sil) +{ + dvb_mux_t *dm = s->s_dvb_mux; + th_dvb_mux_instance_t *tdmi; + + dvb_create_tdmis(dm); + + LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) { + if(tdmi->tdmi_tune_failed) + continue; // The hardware is not able to tune to this frequency, never consider it + + service_instance_add(sil, s, tdmi->tdmi_adapter->tda_instance, 100, + tdmi_current_weight(tdmi)); + } } @@ -389,6 +398,7 @@ dvb_service_find2(dvb_mux_t *dm, uint16_t sid, int pmt_pid, t->s_setsourceinfo = dvb_service_setsourceinfo; t->s_grace_period = dvb_grace_period; t->s_serialize = dvb_service_serialize; + t->s_enlist = dvb_service_enlist; t->s_dvb_mux = dm; LIST_INSERT_HEAD(&dm->dm_services, t, s_group_link); diff --git a/src/iptv_input.c b/src/iptv_input.c index 5abc4ef3..57c994e9 100644 --- a/src/iptv_input.c +++ b/src/iptv_input.c @@ -198,7 +198,7 @@ iptv_thread(void *aux) * */ static int -iptv_service_start(service_t *t, unsigned int weight, int force_start) +iptv_service_start(service_t *t, int instance) { pthread_t tid; int fd; diff --git a/src/service.c b/src/service.c index c625148d..220f4cbc 100644 --- a/src/service.c +++ b/src/service.c @@ -176,7 +176,7 @@ service_stop(service_t *t) */ void service_remove_subscriber(service_t *t, th_subscription_t *s, - int reason) + int reason) { lock_assert(&global_lock); @@ -197,7 +197,7 @@ service_remove_subscriber(service_t *t, th_subscription_t *s, * */ int -service_start(service_t *t, unsigned int weight, int force_start) +service_start(service_t *t, int instance) { elementary_stream_t *st; int r, timeout = 2; @@ -208,7 +208,7 @@ service_start(service_t *t, unsigned int weight, int force_start) t->s_streaming_status = 0; t->s_pcr_drift = 0; - if((r = t->s_start_feed(t, weight, force_start))) + if((r = t->s_start_feed(t, instance))) return r; cwc_service_start(t); @@ -234,12 +234,73 @@ service_start(service_t *t, unsigned int weight, int force_start) return 0; } + +/** + * Main entry point for starting a service based on a channel + */ +service_instance_t * +service_find_instance(channel_t *ch, struct service_instance_list *sil, + int *error, int weight) +{ + service_t *s; + service_instance_t *si, *next; + + lock_assert(&global_lock); + + // First, update list of candidates + + LIST_FOREACH(si, sil, si_link) + si->si_mark = 1; + + LIST_FOREACH(s, &ch->ch_services, s_ch_link) + s->s_enlist(s, sil); + + for(si = LIST_FIRST(sil); si != NULL; si = next) { + next = LIST_NEXT(si, si_link); + if(si->si_mark) + service_instance_destroy(si); + } + + printf("Service start, enlisted candidates\n"); + LIST_FOREACH(si, sil, si_link) + printf(" %s i:%d w:%d p:%d\n", si->si_s->s_nicename, si->si_instance, si->si_weight, si->si_prio); + + + // Check if any service is already running, if so, use that + LIST_FOREACH(si, sil, si_link) + if(si->si_s->s_status == SERVICE_RUNNING) + return si; + + // Check if any is idle + LIST_FOREACH(si, sil, si_link) + if(si->si_weight == 0 && si->si_error == 0) + break; + + // Check if to kick someone out + if(si == NULL) { + LIST_FOREACH(si, sil, si_link) { + if(si->si_weight < weight && si->si_error == 0) + break; + } + } + + if(si == NULL) { + *error = SM_CODE_NO_FREE_ADAPTER; + return NULL; + } + + service_start(si->si_s, si->si_instance); + return NULL; +} + + + +#if 0 /** * */ service_t * -service_find(channel_t *ch, unsigned int weight, const char *loginfo, - int *errorp, service_t *skip) +service_enlist(channel_t *ch) { service_t *t, **vec; int cnt = 0, i, r, off; @@ -321,6 +382,7 @@ service_find(channel_t *ch, unsigned int weight, const char *loginfo, *errorp = SM_CODE_NO_SERVICE; return NULL; } +#endif /** @@ -1031,38 +1093,66 @@ service_refresh_channel(service_t *t) * */ static int -ssc_cmp(const service_start_cand_t *a, - const service_start_cand_t *b) +si_cmp(const service_instance_t *a, const service_instance_t *b) { - return a->ssc_prio - b->ssc_prio; + return a->si_prio - b->si_prio; } /** * */ -service_start_cand_t * -service_find_cand(struct service_start_cand_list *sscl, - struct service *s, int instance, int prio) +service_instance_t * +service_instance_add(struct service_instance_list *sil, + struct service *s, int instance, int prio, + int weight) { - service_start_cand_t *ssc; - LIST_FOREACH(ssc, sscl, ssc_link) { - if(ssc->ssc_s == s && ssc->ssc_instance == instance) + service_instance_t *si; + LIST_FOREACH(si, sil, si_link) + if(si->si_s == s && si->si_instance == instance) break; - } - if(ssc != NULL) { - ssc = calloc(1, sizeof(service_start_cand_t)); - ssc->ssc_s = s; + if(si == NULL) { + si = calloc(1, sizeof(service_instance_t)); + si->si_s = s; service_ref(s); - ssc->ssc_instance = instance; + si->si_instance = instance; + si->si_weight = weight; } else { - if(ssc->ssc_prio == prio) - return ssc; - LIST_REMOVE(ssc, ssc_link); + si->si_mark = 0; + if(si->si_prio == prio && si->si_weight == weight) + return si; + LIST_REMOVE(si, si_link); } - ssc->ssc_prio = prio; - LIST_INSERT_SORTED(sscl, ssc, ssc_link, ssc_cmp); - return ssc; + si->si_weight = weight; + si->si_prio = prio; + LIST_INSERT_SORTED(sil, si, si_link, si_cmp); + return si; +} + + +/** + * + */ +void +service_instance_destroy(service_instance_t *si) +{ + LIST_REMOVE(si, si_link); + service_unref(si->si_s); + free(si); +} + + +/** + * + */ +void +service_instance_list_clear(struct service_instance_list *sil) +{ + lock_assert(&global_lock); + + service_instance_t *si; + while((si = LIST_FIRST(sil)) != NULL) + service_instance_destroy(si); } diff --git a/src/service.h b/src/service.h index 294ef695..d045231a 100644 --- a/src/service.h +++ b/src/service.h @@ -167,35 +167,49 @@ typedef struct elementary_stream { } elementary_stream_t; -LIST_HEAD(service_start_cand_list, service_start_cand); +LIST_HEAD(service_instance_list, service_instance); /** * */ -typedef struct service_start_cand { +typedef struct service_instance { - LIST_ENTRY(service_start_cand) ssc_link; + LIST_ENTRY(service_instance) si_link; - int ssc_prio; + int si_prio; - struct service *ssc_s; // A reference is held - int ssc_instance; // Discriminator when having multiple adapters, etc + struct service *si_s; // A reference is held + int si_instance; // Discriminator when having multiple adapters, etc - int ssc_error_code; // Set if we deem this cand to be broken - time_t ssc_err_time; // Time we detected it was broken + int si_error; /* Set if subscription layer deem this cand + to be broken. We typically set this if we + have not seen any demuxed packets after + the grace period has expired. + The actual value is current time + */ - int ssc_weight; // Highest weight that holds this cand + time_t si_error_time; -} service_start_cand_t; + + int si_weight; // Highest weight that holds this cand + + int si_mark; // For mark & sweep + +} service_instance_t; /** * */ -service_start_cand_t *service_find_cand(struct service_start_cand_list *sscl, - struct service *s, - int instance, - int prio); +service_instance_t *service_instance_add(struct service_instance_list *sil, + struct service *s, + int instance, + int prio, + int weight); + +void service_instance_destroy(service_instance_t *si); + +void service_instance_list_clear(struct service_instance_list *sil); /** * @@ -285,12 +299,9 @@ typedef struct service { LIST_HEAD(, th_subscription) s_subscriptions; - void (*s_enlist)(struct service *s, - struct service_start_cand_list *sscl); + void (*s_enlist)(struct service *s, struct service_instance_list *sil); - - int (*s_start_feed)(struct service *t, unsigned int weight, - int force_start); + int (*s_start_feed)(struct service *s, int instance); void (*s_refresh_feed)(struct service *t); @@ -540,7 +551,7 @@ void service_init(void); unsigned int service_compute_weight(struct service_list *head); -int service_start(service_t *t, unsigned int weight, int force_start); +int service_start(service_t *t, int instance); service_t *service_create(const char *uuid, int source_type); @@ -552,9 +563,10 @@ service_t *service_find_by_identifier(const char *identifier); void service_map_channel(service_t *t, struct channel *ch, int save); -service_t *service_find(struct channel *ch, unsigned int weight, - const char *loginfo, int *errorp, - service_t *skip); +service_instance_t *service_find_instance(struct channel *ch, + struct service_instance_list *sil, + int *error, + int weight); elementary_stream_t *service_stream_find(service_t *t, int pid); diff --git a/src/subscriptions.c b/src/subscriptions.c index e7026080..28a6cf6a 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -155,9 +155,8 @@ void subscription_reschedule(void) { th_subscription_t *s; - service_t *t, *skip; + service_instance_t *si; streaming_message_t *sm; - char buf[128]; int error; lock_assert(&global_lock); @@ -173,19 +172,20 @@ subscription_reschedule(void) /* Already got a service */ if(s->ths_state != SUBSCRIPTION_BAD_SERVICE) - continue; /* And it seems to work ok, so we're happy */ - skip = s->ths_service; - error = s->ths_testing_error; - service_remove_subscriber(s->ths_service, s, s->ths_testing_error); - } else { - error = 0; - skip = NULL; + continue; /* And it not bad, so we're happy */ + + si = s->ths_current_instance; + + assert(si != NULL); + si->si_error = s->ths_testing_error; + time(&si->si_error_time); } - snprintf(buf, sizeof(buf), "Subscription \"%s\"", s->ths_title); - t = service_find(s->ths_channel, s->ths_weight, buf, &error, skip); + si = service_find_instance(s->ths_channel, &s->ths_instances, &error, + s->ths_weight); + s->ths_current_instance = si; - if(t == NULL) { + if(si == NULL) { /* No service available */ sm = streaming_msg_create_code(SMT_NOSTART, error); @@ -193,7 +193,7 @@ subscription_reschedule(void) continue; } - subscription_link_service(s, t); + subscription_link_service(s, si->si_s); } } @@ -207,6 +207,8 @@ subscription_unsubscribe(th_subscription_t *s) lock_assert(&global_lock); + service_instance_list_clear(&s->ths_instances); + LIST_REMOVE(s, ths_global_link); if(s->ths_channel != NULL) { @@ -407,6 +409,7 @@ th_subscription_t * subscription_create_from_service(service_t *t, const char *name, streaming_target_t *st, int flags) { +#if 0 th_subscription_t *s; source_info_t si; int r; @@ -443,6 +446,8 @@ subscription_create_from_service(service_t *t, const char *name, subscription_link_service(s, t); notify_reload("subscriptions"); return s; +#endif + abort(); } diff --git a/src/subscriptions.h b/src/subscriptions.h index 0a88e5f5..f5166f7a 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -19,6 +19,8 @@ #ifndef SUBSCRIPTIONS_H #define SUBSCRIPTIONS_H +#include "service.h" + extern struct th_subscription_list subscriptions; #define SUBSCRIPTION_RAW_MPEGTS 0x1 @@ -65,6 +67,12 @@ typedef struct th_subscription { char *ths_username; char *ths_client; + /** + * This is the list of service candidates we have + */ + struct service_instance_list ths_instances; + struct service_instance *ths_current_instance; + // Ugly ugly ugly to refer DVB code here LIST_ENTRY(th_subscription) ths_tdmi_link; diff --git a/src/v4l.c b/src/v4l.c index 6fae6750..1e76ba1a 100644 --- a/src/v4l.c +++ b/src/v4l.c @@ -170,7 +170,7 @@ v4l_thread(void *aux) * */ static int -v4l_service_start(service_t *t, unsigned int weight, int force_start) +v4l_service_start(service_t *t, int instance) { v4l_adapter_t *va = t->s_v4l_adapter; int frequency = t->s_v4l_frequency; diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index f321200a..408e2d3b 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -221,23 +221,25 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque) out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); -#if 0 + } else if(!strcmp(op, "serviceprobe")) { tvhlog(LOG_NOTICE, "web interface", "Service probe started on \"%s\"", tda->tda_displayname); - LIST_FOREACH(tdmi, &tda->tda_dn->dn_mux_instances, tdmi_adapter_link) { - LIST_FOREACH(t, &tdmi->tdmi_mux->dm_services, s_group_link) { - if(t->s_enabled) - serviceprobe_enqueue(t); + dvb_mux_t *dm; + service_t *s; + + LIST_FOREACH(dm, &tda->tda_dn->dn_muxes, dm_network_link) { + LIST_FOREACH(s, &dm->dm_services, s_group_link) { + if(s->s_enabled) + serviceprobe_enqueue(s); } } out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); -#endif } else { pthread_mutex_unlock(&global_lock); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index bdbc022b..89e6e4cb 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -1266,10 +1266,13 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) { if (r.data.identifier != adapterId) return; infoTemplate.overwrite(infoPanel.body, r.data); + serviceScanBtn.enable(); + if (r.data.services > 0 && r.data.initialMuxes == 0) serviceScanBtn .enable(); else serviceScanBtn.disable(); }); + serviceScanBtn.enable(); return panel; } diff --git a/src/webui/static/app/dvb_networks.js b/src/webui/static/app/dvb_networks.js index 44b68687..378df82b 100644 --- a/src/webui/static/app/dvb_networks.js +++ b/src/webui/static/app/dvb_networks.js @@ -13,7 +13,12 @@ tvheadend.dvb_networks = function() { root : new Ext.tree.AsyncTreeNode({ id : 'root', text: 'DVB Networks' - }) + }), + listeners: { + click: function(n) { + Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"'); + } + } });