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,