From 273835ff468e91fca129f1a391698962a753a5df Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sat, 17 Jan 2015 18:12:18 +0100 Subject: [PATCH] mpegts: add linked tuner feature The DvbSky S952 drivers have numerous tuning bugs and one of it is that if two tuners are not used together, streaming from one tuner can "hang". This change implements a workaround which makes the second (linked) tuner alive (tuning is joined with these two linked tuners). --- src/epggrab/otamux.c | 2 +- src/input/mpegts.h | 9 +- src/input/mpegts/mpegts_input.c | 89 +++++++++++- src/input/mpegts/mpegts_mux.c | 191 +++++++++++++++++++++---- src/input/mpegts/mpegts_mux_sched.c | 2 +- src/input/mpegts/mpegts_network_scan.c | 2 +- src/subscriptions.c | 3 +- src/subscriptions.h | 20 +-- src/webui/webui.c | 2 +- 9 files changed, 274 insertions(+), 46 deletions(-) diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index 6eb10c82..167c7787 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -540,7 +540,7 @@ next_one: /* Subscribe to the mux */ om->om_requeue = 1; - if ((r = mpegts_mux_subscribe(mm, "epggrab", SUBSCRIPTION_PRIO_EPG, + if ((r = mpegts_mux_subscribe(mm, NULL, "epggrab", SUBSCRIPTION_PRIO_EPG, SUBSCRIPTION_EPG))) { TAILQ_INSERT_TAIL(&epggrab_ota_pending, om, om_q_link); om->om_q_type = EPGGRAB_OTA_MUX_PENDING; diff --git a/src/input/mpegts.h b/src/input/mpegts.h index 8ebae796..20f25bb1 100644 --- a/src/input/mpegts.h +++ b/src/input/mpegts.h @@ -413,7 +413,7 @@ struct mpegts_mux void (*mm_config_save) (mpegts_mux_t *mm); void (*mm_display_name) (mpegts_mux_t*, char *buf, size_t len); int (*mm_is_enabled) (mpegts_mux_t *mm); - int (*mm_start) (mpegts_mux_t *mm, const char *r, int w, int flags); + int (*mm_start) (mpegts_mux_t *mm, mpegts_input_t *mi, const char *r, int w, int flags); void (*mm_stop) (mpegts_mux_t *mm, int force); void (*mm_open_table) (mpegts_mux_t*,mpegts_table_t*,int subscribe); void (*mm_close_table) (mpegts_mux_t*,mpegts_table_t*); @@ -564,6 +564,8 @@ struct mpegts_input int mi_initscan; int mi_idlescan; + char *mi_linked; + LIST_ENTRY(mpegts_input) mi_global_link; mpegts_network_link_list_t mi_networks; @@ -779,9 +781,12 @@ void mpegts_mux_open_table ( mpegts_mux_t *mm, mpegts_table_t *mt, int subscribe void mpegts_mux_close_table ( mpegts_mux_t *mm, mpegts_table_t *mt ); void mpegts_mux_remove_subscriber(mpegts_mux_t *mm, th_subscription_t *s, int reason); -int mpegts_mux_subscribe(mpegts_mux_t *mm, const char *name, int weight, int flags); +int mpegts_mux_subscribe(mpegts_mux_t *mm, mpegts_input_t *mi, + const char *name, int weight, int flags); void mpegts_mux_unsubscribe_by_name(mpegts_mux_t *mm, const char *name); +void mpegts_mux_unsubscribe_linked(mpegts_input_t *mi); + void mpegts_mux_scan_done ( mpegts_mux_t *mm, const char *buf, int res ); void mpegts_mux_bouquet_rescan ( const char *src, const char *extra ); diff --git a/src/input/mpegts/mpegts_input.c b/src/input/mpegts/mpegts_input.c index c4f15275..9fb6e220 100644 --- a/src/input/mpegts/mpegts_input.c +++ b/src/input/mpegts/mpegts_input.c @@ -140,6 +140,75 @@ mpegts_input_enabled_notify ( void *p ) mi->mi_enabled_updated(mi); } +static int +mpegts_input_class_linked_set ( void *self, const void *val ) +{ + mpegts_input_t *mi = self, *mi2; + + if (strcmp(val ?: "", mi->mi_linked ?: "")) { + mi2 = mpegts_input_find(mi->mi_linked); + free(mi->mi_linked); + mi->mi_linked = NULL; + if (mi2) { + free(mi2->mi_linked); + mi2->mi_linked = NULL; + mpegts_mux_unsubscribe_linked(mi2); + } + mpegts_mux_unsubscribe_linked(mi); + if (val) { + mi->mi_linked = strdup((char *)val); + mi2 = mpegts_input_find((char *)val); + if (mi2) { + free(mi2->mi_linked); + mi2->mi_linked = strdup(idnode_uuid_as_str(&mi->ti_id)); + idnode_changed(&mi2->ti_id); + } + } + return 1; + } + return 0; +} + +static const void * +mpegts_input_class_linked_get ( void *self ) +{ + static const char *ptr; + mpegts_input_t *mi = self; + ptr = ""; + if (mi->mi_linked) { + mi = mpegts_input_find(mi->mi_linked); + if (mi) + ptr = idnode_uuid_as_str(&mi->ti_id); + } + return &ptr; +} + +static void +mpegts_input_add_keyval(htsmsg_t *l, const char *key, const char *val) +{ + htsmsg_t *e = htsmsg_create_map(); + htsmsg_add_str(e, "key", key); + htsmsg_add_str(e, "val", val); + htsmsg_add_msg(l, NULL, e); +} + +static htsmsg_t * +mpegts_input_class_linked_enum( void * self ) +{ + mpegts_input_t *mi = self, *mi2; + tvh_input_t *ti; + htsmsg_t *m = htsmsg_create_list(); + mpegts_input_add_keyval(m, "", "Not Linked"); + TVH_INPUT_FOREACH(ti) + if (idnode_is_instance(&ti->ti_id, &mpegts_input_class)) { + mi2 = (mpegts_input_t *)ti; + if (mi2 != mi) + mpegts_input_add_keyval(m, idnode_uuid_as_str(&ti->ti_id), + idnode_get_title(&mi2->ti_id)); + } + return m; +} + const idclass_t mpegts_input_class = { .ic_class = "mpegts_input", @@ -212,6 +281,14 @@ const idclass_t mpegts_input_class = .list = mpegts_input_class_network_enum, .rend = mpegts_input_class_network_rend, }, + { + .type = PT_STR, + .id = "linked", + .name = "Linked Input", + .set = mpegts_input_class_linked_set, + .get = mpegts_input_class_linked_get, + .list = mpegts_input_class_linked_enum + }, {} } }; @@ -235,19 +312,22 @@ mpegts_input_is_enabled ( mpegts_input_t *mi, mpegts_mux_t *mm, int flags ) static void mpegts_input_display_name ( mpegts_input_t *mi, char *buf, size_t len ) { - if (mi->mi_name) - strncpy(buf, mi->mi_name, len); - else + if (mi->mi_name) { + strncpy(buf, mi->mi_name, len - 1); + buf[len - 1] = '\0'; + } else *buf = 0; } int mpegts_input_is_free ( mpegts_input_t *mi ) { - char buf[256]; mpegts_mux_instance_t *mmi = LIST_FIRST(&mi->mi_mux_active); +#if ENABLE_TRACE + char buf[256]; mi->mi_display_name(mi, buf, sizeof(buf)); tvhtrace("mpegts", "%s - is free? %d", buf, mmi == NULL); +#endif return mmi ? 0 : 1; } @@ -1345,6 +1425,7 @@ mpegts_input_delete ( mpegts_input_t *mi, int delconf ) pthread_mutex_destroy(&mi->mi_output_lock); pthread_cond_destroy(&mi->mi_table_cond); free(mi->mi_name); + free(mi->mi_linked); free(mi); } diff --git a/src/input/mpegts/mpegts_mux.c b/src/input/mpegts/mpegts_mux.c index 83251e75..1f744e94 100644 --- a/src/input/mpegts/mpegts_mux.c +++ b/src/input/mpegts/mpegts_mux.c @@ -95,6 +95,111 @@ mpegts_mux_scan_active } } +static int +mpegts_mux_keep_exists + ( mpegts_input_t *mi ) +{ + mpegts_mux_instance_t *mmi; + th_subscription_t *s; + + if (!mi) + return 0; + + mmi = LIST_FIRST(&mi->mi_mux_active); + if (mmi) + LIST_FOREACH(mmi, &mi->mi_mux_active, mmi_active_link) + LIST_FOREACH(s, &mmi->mmi_subs, ths_mmi_link) + if (!strcmp(s->ths_title, "keep")) + return 1; + + return 0; +} + +static int +mpegts_mux_subscribe_keep + ( mpegts_mux_t *mm, mpegts_input_t *mi ) +{ + char *s; + int r; + + s = mi->mi_linked; + mi->mi_linked = NULL; + r = mpegts_mux_subscribe(mm, mi, "keep", SUBSCRIPTION_PRIO_KEEP, SUBSCRIPTION_NONE); + mi->mi_linked = s; + return r; +} + +static void +mpegts_mux_subscribe_linked + ( mpegts_input_t *mi, mpegts_mux_t *mm ) +{ + mpegts_input_t *mi2 = mpegts_input_find(mi->mi_linked); + mpegts_mux_instance_t *mmi2; + mpegts_network_link_t *mnl2; + mpegts_mux_t *mm2; + char buf1[128], buf2[128]; + const char *serr = "All"; + int r = 0; + + tvhtrace("mpegts", "subscribe linked"); + + if (!mpegts_mux_keep_exists(mi) && (r = mpegts_mux_subscribe_keep(mm, mi))) { + serr = "active1"; + goto fatal; + } + + if (!mi2) + return; + + if (mpegts_mux_keep_exists(mi2)) + return; + + mmi2 = LIST_FIRST(&mi2->mi_mux_active); + if (mmi2) { + r = mpegts_mux_subscribe_keep(mmi2->mmi_mux, mi2); + if (!r) + return; + serr = "active2"; + goto fatal; + } + + /* Try muxes within same network */ + LIST_FOREACH(mnl2, &mi2->mi_networks, mnl_mi_link) + if (mnl2->mnl_network == mm->mm_network) + LIST_FOREACH(mm2, &mnl2->mnl_network->mn_muxes, mm_network_link) + if (!mm2->mm_active && mm->mm_scan_result == MM_SCAN_OK && + !LIST_EMPTY(&mm2->mm_services)) + if (!mpegts_mux_subscribe_keep(mm2, mi2)) + return; + + /* Try all other muxes */ + LIST_FOREACH(mnl2, &mi2->mi_networks, mnl_mi_link) + if (mnl2->mnl_network != mm->mm_network) + LIST_FOREACH(mm2, &mnl2->mnl_network->mn_muxes, mm_network_link) + if (!mm2->mm_active && mm->mm_scan_result == MM_SCAN_OK && + !LIST_EMPTY(&mm2->mm_services)) + if (!mpegts_mux_subscribe_keep(mm2, mi2)) + return; + +fatal: + mi ->mi_display_name(mi, buf1, sizeof(buf1)); + mi2->mi_display_name(mi2, buf2, sizeof(buf2)); + tvherror("mpegts", "%s - %s - linked input cannot be started (%s: %i)", buf1, buf2, serr, r); + abort(); +} + +void +mpegts_mux_unsubscribe_linked + ( mpegts_input_t *mi ) +{ + mpegts_mux_instance_t *mmi; + + if (mi) { + LIST_FOREACH(mmi, &mi->mi_mux_active, mmi_active_link) + mpegts_mux_unsubscribe_by_name(mmi->mmi_mux, "keep"); + } +} + int mpegts_mux_instance_start ( mpegts_mux_instance_t **mmiptr ) @@ -137,6 +242,12 @@ mpegts_mux_instance_start /* Link */ mpegts_mux_scan_active(mm, buf, mi); + if (mi->mi_linked) { + mpegts_mux_subscribe_linked(mi, mm); + if (mm->mm_active) + return 0; + } + return 0; } @@ -535,12 +646,15 @@ mpegts_mux_start1( mpegts_mux_instance_t *mmi ) static int mpegts_mux_start - ( mpegts_mux_t *mm, const char *reason, int weight, int flags ) + ( mpegts_mux_t *mm, mpegts_input_t *_mi, const char *reason, int weight, int flags ) { int havefree = 0, enabled = 0, index, index2, weight2, count, size = 0; char buf[256]; + mpegts_input_t *mi; + mpegts_mux_t *mm2; mpegts_mux_instance_t *mmi, **all; int64_t aweight, *allw; + int e, j; mpegts_mux_nice_name(mm, buf, sizeof(buf)); tvhtrace("mpegts", "%s - starting for '%s' (weight %d, flags %04X)", @@ -575,13 +689,16 @@ mpegts_mux_start /* Calculate priority+weight and sort */ count = 0; LIST_FOREACH(mmi, &mm->mm_instances, mmi_mux_link) { - int e = mmi->mmi_input->mi_is_enabled(mmi->mmi_input, mm, flags); + mi = mmi->mmi_input; + mm2 = mmi->mmi_mux; + if (_mi && mi != _mi) continue; + e = mi->mi_is_enabled(mi, mm, flags); tvhtrace("mpegts", "%s - mmi %p enabled %d", buf, mmi, e); if (!e) continue; enabled = 1; /* Already live? Try it... */ - if (e && mmi->mmi_mux->mm_active == mmi) { + if (e && mm2->mm_active == mmi) { if (!mpegts_mux_start1(mmi)) return 0; continue; @@ -591,20 +708,18 @@ mpegts_mux_start tvhtrace("mpegts", "%s - found mmi %p", buf, mmi); - aweight = ((int64_t )mmi->mmi_input->mi_get_priority(mmi->mmi_input, - mmi->mmi_mux, flags) << 32) | - mmi->mmi_input->mi_get_weight(mmi->mmi_input, flags); - for (index = 0; index < count; index++) { + aweight = ((int64_t )mi->mi_get_priority(mi, mm2, flags) << 32) | + mi->mi_get_weight(mi, flags); + + for (index = 0; index < count; index++) if (allw[index] >= aweight) break; + + for (j = count; j > index; j--) { + allw[j] = allw[j - 1]; + all [j] = all [j - 1]; } - if (index < count) { - int j; - for (j = count; j > index; j--) { - allw[j] = allw[j - 1]; - all [j] = all [j - 1]; - } - } + all [index] = mmi; allw[index] = aweight; count++; @@ -613,7 +728,7 @@ mpegts_mux_start #if ENABLE_TRACE for (index = 0; index < count; index++) { if (all[index]) { - mpegts_input_t *mi = all[index]->mmi_input; + mi = all[index]->mmi_input; char buf2[256]; mi->mi_display_name(mi, buf2, sizeof(buf2)); tvhtrace("mpegts", "%s - %i [%s]: prio %li weight %li", buf, index, buf2, @@ -624,7 +739,6 @@ mpegts_mux_start /* Try free inputs */ for (index = count - 1; index >= 0; index--) { - mpegts_input_t *mi; mmi = all[index]; mi = mmi->mmi_input; if (mi->mi_is_free(mi)) { @@ -685,11 +799,13 @@ static int mpegts_mux_has_subscribers ( mpegts_mux_t *mm, const char *name ) { mpegts_mux_instance_t *mmi = mm->mm_active; + th_subscription_t *sub; if (mmi) { - if (LIST_FIRST(&mmi->mmi_subs)) { - tvhtrace("mpegts", "%s - keeping mux (direct subscription)", name); - return 1; - } + if ((sub = LIST_FIRST(&mmi->mmi_subs)) != NULL) + if (strcmp(sub->ths_title, "keep") || LIST_NEXT(sub, ths_mmi_link)) { + tvhtrace("mpegts", "%s - keeping mux (direct subscription)", name); + return 1; + } if (mmi->mmi_input->mi_has_subscription(mmi->mmi_input, mm)) { tvhtrace("mpegts", "%s - keeping mux (service)", name); return 1; @@ -701,9 +817,9 @@ mpegts_mux_has_subscribers ( mpegts_mux_t *mm, const char *name ) static void mpegts_mux_stop ( mpegts_mux_t *mm, int force ) { - char buf[256]; - mpegts_mux_instance_t *mmi = mm->mm_active; - mpegts_input_t *mi = NULL; + char buf[256], *s; + mpegts_mux_instance_t *mmi = mm->mm_active, *mmi2; + mpegts_input_t *mi = NULL, *mi2; th_subscription_t *sub; mpegts_pid_t *mp; mpegts_pid_sub_t *mps; @@ -716,10 +832,30 @@ mpegts_mux_stop ( mpegts_mux_t *mm, int force ) /* Stop possible recursion */ if (!mmi) return; - tvhdebug("mpegts", "%s - stopping mux", buf); + tvhdebug("mpegts", "%s - stopping mux%s", buf, force ? " (forced)" : ""); mi = mmi->mmi_input; assert(mi); + + /* Linked input */ + if (mi->mi_linked) { + mi2 = mpegts_input_find(mi->mi_linked); + if (mi2 && (mmi2 = LIST_FIRST(&mi2->mi_mux_active)) != NULL) { + mpegts_mux_nice_name(mmi2->mmi_mux, buf, sizeof(buf)); + if (mmi2 && !mpegts_mux_has_subscribers(mmi2->mmi_mux, buf)) { + s = mi2->mi_linked; + mi2->mi_linked = NULL; + mpegts_mux_unsubscribe_linked(mi2); + mi2->mi_linked = s; + } else { + if (!force) { + tvhtrace("mpegts", "%s - keeping subscribed (linked tuner active)", buf); + return; + } + } + } + } + mi->mi_stopping_mux(mi, mmi); LIST_FOREACH(sub, &mmi->mmi_subs, ths_mmi_link) subscription_unlink_mux(sub, SM_CODE_SUBSCRIPTION_OVERRIDDEN); @@ -760,6 +896,7 @@ mpegts_mux_stop ( mpegts_mux_t *mm, int force ) /* Events */ mpegts_fire_event(mm, ml_mux_stop); + } void @@ -1143,14 +1280,16 @@ mpegts_mux_remove_subscriber int mpegts_mux_subscribe - ( mpegts_mux_t *mm, const char *name, int weight, int flags ) + ( mpegts_mux_t *mm, mpegts_input_t *mi, + const char *name, int weight, int flags ) { int err = 0; profile_chain_t prch; th_subscription_t *s; memset(&prch, 0, sizeof(prch)); prch.prch_id = mm; - s = subscription_create_from_mux(&prch, weight, name, + s = subscription_create_from_mux(&prch, (tvh_input_t *)mi, + weight, name, SUBSCRIPTION_NONE | flags, NULL, NULL, NULL, &err); return s ? 0 : err; diff --git a/src/input/mpegts/mpegts_mux_sched.c b/src/input/mpegts/mpegts_mux_sched.c index 029154c5..3794fad0 100644 --- a/src/input/mpegts/mpegts_mux_sched.c +++ b/src/input/mpegts/mpegts_mux_sched.c @@ -211,7 +211,7 @@ mpegts_mux_sched_timer ( void *p ) mms->mms_prch->prch_st = &mms->mms_input; mms->mms_sub - = subscription_create_from_mux(mms->mms_prch, mms->mms_weight, + = subscription_create_from_mux(mms->mms_prch, NULL, mms->mms_weight, mms->mms_creator ?: "", SUBSCRIPTION_NONE, NULL, NULL, NULL, NULL); diff --git a/src/input/mpegts/mpegts_network_scan.c b/src/input/mpegts/mpegts_network_scan.c index 646c80c7..25f47db8 100644 --- a/src/input/mpegts/mpegts_network_scan.c +++ b/src/input/mpegts/mpegts_network_scan.c @@ -53,7 +53,7 @@ mpegts_network_scan_timer_cb ( void *p ) if (mm->mm_active) continue; /* Attempt to tune */ - r = mpegts_mux_subscribe(mm, "scan", mm->mm_scan_weight, mm->mm_scan_flags); + r = mpegts_mux_subscribe(mm, NULL, "scan", mm->mm_scan_weight, mm->mm_scan_flags); /* Started */ if (!r) { diff --git a/src/subscriptions.c b/src/subscriptions.c index 7b984562..057442ee 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -758,6 +758,7 @@ mux_data_timeout ( void *aux ) th_subscription_t * subscription_create_from_mux(profile_chain_t *prch, + tvh_input_t *ti, unsigned int weight, const char *name, int flags, @@ -774,7 +775,7 @@ subscription_create_from_mux(profile_chain_t *prch, int r; /* Tune */ - r = mm->mm_start(mm, name, weight, flags); + r = mm->mm_start(mm, (mpegts_input_t *)ti, name, weight, flags); if (r) { if (err) *err = r; return NULL; diff --git a/src/subscriptions.h b/src/subscriptions.h index 40c638de..77fc7091 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -35,14 +35,15 @@ extern struct th_subscription_list subscriptions; #define SUBSCRIPTION_USERSCAN 0x080 ///< for mux subscriptions #define SUBSCRIPTION_EPG 0x100 ///< for mux subscriptions -/* Some internal prioties */ -#define SUBSCRIPTION_PRIO_SCAN_IDLE 1 ///< Idle scanning -#define SUBSCRIPTION_PRIO_SCAN_SCHED 2 ///< Scheduled scan -#define SUBSCRIPTION_PRIO_EPG 3 ///< EPG scanner -#define SUBSCRIPTION_PRIO_SCAN_INIT 4 ///< Initial scan -#define SUBSCRIPTION_PRIO_SCAN_USER 5 ///< User defined scan -#define SUBSCRIPTION_PRIO_MAPPER 6 ///< Channel mapper -#define SUBSCRIPTION_PRIO_MIN 10 ///< User defined / Normal levels +/* Some internal priorities */ +#define SUBSCRIPTION_PRIO_KEEP 1 ///< Keep input rolling +#define SUBSCRIPTION_PRIO_SCAN_IDLE 2 ///< Idle scanning +#define SUBSCRIPTION_PRIO_SCAN_SCHED 3 ///< Scheduled scan +#define SUBSCRIPTION_PRIO_EPG 4 ///< EPG scanner +#define SUBSCRIPTION_PRIO_SCAN_INIT 5 ///< Initial scan +#define SUBSCRIPTION_PRIO_SCAN_USER 6 ///< User defined scan +#define SUBSCRIPTION_PRIO_MAPPER 7 ///< Channel mapper +#define SUBSCRIPTION_PRIO_MIN 10 ///< User defined / Normal levels typedef struct th_subscription { @@ -154,9 +155,10 @@ subscription_create_from_service(struct profile_chain *prch, const char *client); #if ENABLE_MPEGTS -struct mpegts_mux; +struct tvh_input; th_subscription_t * subscription_create_from_mux(struct profile_chain *prch, + struct tvh_input *ti, unsigned int weight, const char *name, int flags, diff --git a/src/webui/webui.c b/src/webui/webui.c index b559e850..209cf0d2 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -819,7 +819,7 @@ http_stream_mux(http_connection_t *hc, mpegts_mux_t *mm, int weight) tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50); - s = subscription_create_from_mux(&prch, weight ?: 10, "HTTP", + s = subscription_create_from_mux(&prch, NULL, weight ?: 10, "HTTP", prch.prch_flags | SUBSCRIPTION_FULLMUX | SUBSCRIPTION_STREAMING,