dvbreorg: Service subscription now possible

This commit is contained in:
Andreas Öman 2013-02-06 11:34:49 +01:00
parent 2cd41e90e5
commit 6f6f422bdf
14 changed files with 377 additions and 321 deletions

View file

@ -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);
/**

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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);
}

View file

@ -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);

View file

@ -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();
}

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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 + '"');
}
}
});