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).
This commit is contained in:
Jaroslav Kysela 2015-01-17 18:12:18 +01:00
parent 37eb9d2cfd
commit 273835ff46
9 changed files with 274 additions and 46 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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