src/epggrab.c\
src/spawn.c \
src/packet.c \
+ src/esstream.c \
src/streaming.c \
src/channels.c \
src/subscriptions.c \
htsmsg_add_str(e, "type", "PMT");
htsmsg_add_msg(st, NULL, e);
}
- TAILQ_FOREACH(es, &s->s_components, es_link) {
+ TAILQ_FOREACH(es, &s->s_components.set_all, es_link) {
if (es->es_type == SCT_PCR) continue;
htsmsg_add_msg(st, NULL, api_service_streams_get_one(es, 0));
}
- if (TAILQ_FIRST(&s->s_filt_components) == NULL ||
- s->s_status == SERVICE_IDLE)
- service_build_filter(s);
- TAILQ_FOREACH(es, &s->s_filt_components, es_filt_link) {
+ if (elementary_set_has_streams(&s->s_components, 1) || s->s_status == SERVICE_IDLE)
+ elementary_set_filter_build(&s->s_components);
+ TAILQ_FOREACH(es, &s->s_components.set_filter, es_filter_link) {
if (es->es_type == SCT_PCR) continue;
htsmsg_add_msg(stf, NULL, api_service_streams_get_one(es, 1));
}
/* add missing A/V PIDs and ECM PIDs */
i = 0;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
if (i < MAX_PIDS && capmt_include_elementary_stream(st->es_type)) {
if (capmt_update_elementary_stream(ct, &i, st))
change = 1;
cce->cce_ecmpid != t->s_dvb_prefcapid) {
st = NULL;
} else {
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
LIST_FOREACH(c, &st->es_caids, link)
if (c->use && cce->cce_caid == c->caid &&
cce->cce_providerid == c->providerid &&
ct->ct_multipid = descrambler_multi_pid((th_descrambler_t *)ct);
i = 0;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
if (i < MAX_PIDS && capmt_include_elementary_stream(st->es_type))
capmt_update_elementary_stream(ct, &i, st);
if (t->s_dvb_prefcapid_lock == PREFCAPID_FORCE &&
tvhdebug(cc->cc_subsys, "%s: ECM state INIT", cc->cc_name);
if(t->s_dvb_prefcapid_lock != PREFCAPID_OFF) {
- st = service_stream_find((service_t*)t, t->s_dvb_prefcapid);
+ st = elementary_stream_find(&t->s_components, t->s_dvb_prefcapid);
if (st && st->es_type == SCT_CA)
LIST_FOREACH(c, &st->es_caids, link)
LIST_FOREACH(pcard, &cc->cc_cards, cs_card)
goto end;
}
- st = service_stream_find((service_t *)t, pid);
+ st = elementary_stream_find(&t->s_components, pid);
if (st) {
LIST_FOREACH(c, &st->es_caids, link)
LIST_FOREACH(pcard, &cc->cc_cards, cs_card)
LIST_FOREACH(pcard, &cc->cc_cards, cs_card) {
if (!pcard->cs_running) continue;
if (pcard->cs_ra.caid == 0) continue;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
if (prefpid_lock == PREFCAPID_FORCE && prefpid != st->es_pid)
continue;
LIST_FOREACH(c, &st->es_caids, link) {
if (ct) {
reuse = 1;
for (i = 0; i < ct->cs_epids.count; i++) {
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
if (st->es_pid != ct->cs_epids.pids[i].pid) continue;
LIST_FOREACH(c, &st->es_caids, link)
if (c->use && c->caid == pcard->cs_ra.caid)
add:
i = 0;
mpegts_pid_init(&epids);
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
LIST_FOREACH(c, &st->es_caids, link)
if (c->use && c->caid == pcard->cs_ra.caid)
mpegts_pid_add(&epids, st->es_pid, 0);
if (!mt->s_dvb_forcecaid) {
pthread_mutex_lock(&t->s_stream_mutex);
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
LIST_FOREACH(c, &st->es_caids, link) {
if (c->use && c->caid == ccw->ccw_caid &&
c->providerid == ccw->ccw_providerid)
if (!ca_hints_quickecm)
return 0;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link) {
if (st->es_pid != pid) continue;
TAILQ_FOREACH(hint, &ca_hints, dh_link) {
if (!hint->dh_quickecm) continue;
if (!t->s_dvb_forcecaid) {
count = 0;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link)
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_filter_link)
LIST_FOREACH(ca, &st->es_caids, link) {
if (ca->use == 0) continue;
TAILQ_FOREACH(hint, &ca_hints, dh_link) {
if (i > 0)
break;
}
- TAILQ_FOREACH(st, &t->s_filt_components, es_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link) {
if (st->es_type != SCT_CA) continue;
LIST_FOREACH(c, &st->es_caids, link) {
if (!c->use) continue;
if (i > 0)
break;
}
- TAILQ_FOREACH(st, &t->s_filt_components, es_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_filter, es_link) {
if (st->es_type != SCT_CA) continue;
LIST_FOREACH(c, &st->es_caids, link) {
if (i >= ARRAY_SIZE(as->caid_list)) {
update_pid:
#if ENABLE_DDCI
/* open selected ECM PIDs */
- TAILQ_FOREACH(st, &t->s_filt_components, es_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link) {
if (st->es_type != SCT_CA) continue;
LIST_FOREACH(c, &st->es_caids, link) {
if (!c->use) continue;
lock_assert(&global_lock);
TAILQ_FOREACH(s, &service_all, s_all_link) {
pthread_mutex_lock(&s->s_stream_mutex);
- TAILQ_FOREACH(es, &s->s_components, es_link) {
+ TAILQ_FOREACH(es, &s->s_components.set_all, es_link) {
LIST_FOREACH(ca, &es->es_caids, link) {
v = provider ? ca->providerid : ca->caid;
for (i = 0; i < count; i++)
--- /dev/null
+/*
+ * Elementary streams
+ * Copyright (C) 2010 Andreas Ă–man
+ * 2018 Jaroslav Kysela
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "service.h"
+#include "streaming.h"
+#include "esfilter.h"
+#include "lang_codes.h"
+
+/**
+ *
+ */
+static void
+elementary_stream_make_nicename(elementary_stream_t *st, const char *nicename)
+{
+ char buf[256];
+ if(st->es_pid != -1)
+ snprintf(buf, sizeof(buf), "%s: %s @ #%d",
+ nicename,
+ streaming_component_type2txt(st->es_type), st->es_pid);
+ else
+ snprintf(buf, sizeof(buf), "%s: %s",
+ nicename,
+ streaming_component_type2txt(st->es_type));
+
+ free(st->es_nicename);
+ st->es_nicename = strdup(buf);
+}
+
+/**
+ *
+ */
+void elementary_set_init
+ (elementary_set_t *set, int subsys, const char *nicename, service_t *t)
+{
+ TAILQ_INIT(&set->set_all);
+ TAILQ_INIT(&set->set_filter);
+ set->set_nicename = NULL;
+ set->set_last_pid = -1;
+ set->set_last_es = NULL;
+ set->set_service = t;
+ elementary_set_update_nicename(set, nicename);
+}
+
+/**
+ *
+ */
+void elementary_set_clean(elementary_set_t *set)
+{
+ elementary_stream_t *st;
+
+ TAILQ_INIT(&set->set_filter);
+ while ((st = TAILQ_FIRST(&set->set_all)) != NULL)
+ elementary_set_stream_destroy(set, st);
+ free(set->set_nicename);
+ set->set_nicename = NULL;
+ set->set_service = 0;
+}
+
+/**
+ *
+ */
+void elementary_set_update_nicename(elementary_set_t *set, const char *nicename)
+{
+ elementary_stream_t *st;
+
+ free(set->set_nicename);
+ set->set_nicename = nicename ? strdup(nicename) : NULL;
+ TAILQ_FOREACH(st, &set->set_all, es_link)
+ elementary_stream_make_nicename(st, nicename);
+}
+
+/**
+ *
+ */
+static void
+elementary_stream_init(elementary_stream_t *es)
+{
+ es->es_cc = -1;
+
+ if (es->es_type == SCT_HBBTV && es->es_psi.mt_name == NULL)
+ dvb_table_parse_init(&es->es_psi, "hbbtv", LS_TS, es->es_pid,
+ DVB_HBBTV_BASE, DVB_HBBTV_MASK, es);
+}
+
+/**
+ *
+ */
+static void
+elementary_stream_clean(elementary_stream_t *es)
+{
+ tvhlog_limit_reset(&es->es_cc_log);
+
+ if (es->es_psi.mt_name)
+ dvb_table_reset(&es->es_psi);
+}
+
+/**
+ *
+ */
+void
+elementary_set_clean_streams(elementary_set_t *set)
+{
+ elementary_stream_t *st;
+
+ TAILQ_FOREACH(st, &set->set_all, es_link)
+ elementary_stream_clean(st);
+}
+
+/**
+ *
+ */
+void
+elementary_set_init_filter_streams(elementary_set_t *set)
+{
+ elementary_stream_t *st;
+
+ TAILQ_FOREACH(st, &set->set_filter, es_filter_link)
+ elementary_stream_init(st);
+}
+
+/**
+ *
+ */
+int
+elementary_set_has_streams(elementary_set_t *set, int filtered)
+{
+ return filtered ? TAILQ_FIRST(&set->set_filter) != NULL :
+ TAILQ_FIRST(&set->set_all) != NULL;
+}
+
+/**
+ *
+ */
+void
+elementary_set_stream_destroy(elementary_set_t *set, elementary_stream_t *es)
+{
+ elementary_stream_t *es1;
+ caid_t *c;
+
+ if (es->es_psi.mt_name)
+ dvb_table_parse_done(&es->es_psi);
+
+ if (set->set_last_es == es) {
+ set->set_last_pid = -1;
+ set->set_last_es = NULL;
+ }
+
+ TAILQ_REMOVE(&set->set_all, es, es_link);
+ TAILQ_FOREACH(es1, &set->set_filter, es_filter_link)
+ if (es1 == es) {
+ TAILQ_REMOVE(&set->set_filter, es, es_filter_link);
+ break;
+ }
+
+ while ((c = LIST_FIRST(&es->es_caids)) != NULL) {
+ LIST_REMOVE(c, link);
+ free(c);
+ }
+
+ free(es->es_nicename);
+ free(es);
+}
+
+/**
+ *
+ */
+#define ESFM_USED (1<<0)
+#define ESFM_IGNORE (1<<1)
+
+static void
+elementary_set_filter_build_add
+ (elementary_set_t *set, elementary_stream_t *st,
+ elementary_stream_t **sta, int *p)
+{
+ /* only once */
+ if (st->es_filter & ESFM_USED)
+ return;
+ st->es_filter |= ESFM_USED;
+ TAILQ_INSERT_TAIL(&set->set_filter, st, es_filter_link);
+ sta[*p] = st;
+ (*p)++;
+}
+
+/**
+ *
+ */
+static void
+elementary_set_filter_print
+ (elementary_set_t *set)
+{
+ elementary_stream_t *st;
+ caid_t *ca;
+
+ if (!tvhtrace_enabled())
+ return;
+ TAILQ_FOREACH(st, &set->set_filter, es_filter_link) {
+ if (LIST_EMPTY(&st->es_caids)) {
+ tvhtrace(set->set_subsys, "esfilter: \"%s\" %03d %05d %s %s",
+ set->set_nicename, st->es_index, st->es_pid,
+ streaming_component_type2txt(st->es_type),
+ lang_code_get(st->es_lang));
+ } else {
+ LIST_FOREACH(ca, &st->es_caids, link)
+ if (ca->use)
+ tvhtrace(set->set_subsys, "esfilter: \"%s\" %03d %05d %s %04x %06x",
+ set->set_nicename, st->es_index, st->es_pid,
+ streaming_component_type2txt(st->es_type),
+ ca->caid, ca->providerid);
+ }
+ }
+}
+
+/**
+ *
+ */
+void
+elementary_set_filter_build(elementary_set_t *set)
+{
+ service_t *t;
+ elementary_stream_t *st, *st2, **sta;
+ esfilter_t *esf;
+ caid_t *ca, *ca2;
+ int i, n, p, o, exclusive, sindex;
+ uint32_t mask;
+ char service_ubuf[UUID_HEX_SIZE];
+
+ t = set->set_service;
+ idnode_uuid_as_str(&t->s_id, service_ubuf);
+
+ /* rebuild the filtered and ordered components */
+ TAILQ_INIT(&set->set_filter);
+
+ for (i = ESF_CLASS_VIDEO; i <= ESF_CLASS_LAST; i++)
+ if (!TAILQ_EMPTY(&esfilters[i]))
+ goto filter;
+
+ TAILQ_FOREACH(st, &set->set_all, es_link) {
+ TAILQ_INSERT_TAIL(&set->set_filter, st, es_filter_link);
+ LIST_FOREACH(ca, &st->es_caids, link)
+ ca->use = 1;
+ }
+ elementary_set_filter_print(set);
+ return;
+
+filter:
+ n = 0;
+ TAILQ_FOREACH(st, &set->set_all, es_link) {
+ st->es_filter = 0;
+ LIST_FOREACH(ca, &st->es_caids, link) {
+ ca->use = 0;
+ ca->filter = 0;
+ }
+ n++;
+ }
+
+ sta = alloca(sizeof(elementary_stream_t *) * n);
+
+ for (i = ESF_CLASS_VIDEO, p = 0; i <= ESF_CLASS_LAST; i++) {
+ o = p;
+ mask = esfilterclsmask[i];
+ if (TAILQ_EMPTY(&esfilters[i])) {
+ TAILQ_FOREACH(st, &set->set_all, es_link) {
+ if ((mask & SCT_MASK(st->es_type)) != 0) {
+ elementary_set_filter_build_add(set, st, sta, &p);
+ LIST_FOREACH(ca, &st->es_caids, link)
+ ca->use = 1;
+ }
+ }
+ continue;
+ }
+ exclusive = 0;
+ TAILQ_FOREACH(esf, &esfilters[i], esf_link) {
+ if (!esf->esf_enabled)
+ continue;
+ sindex = 0;
+ TAILQ_FOREACH(st, &set->set_all, es_link) {
+ if ((mask & SCT_MASK(st->es_type)) == 0)
+ continue;
+ if (esf->esf_type && (esf->esf_type & SCT_MASK(st->es_type)) == 0)
+ continue;
+ if (esf->esf_language[0] &&
+ strncmp(esf->esf_language, st->es_lang, 4))
+ continue;
+ if (esf->esf_service[0]) {
+ if (strcmp(esf->esf_service, service_ubuf))
+ continue;
+ if (esf->esf_pid && esf->esf_pid != st->es_pid)
+ continue;
+ }
+ if (i == ESF_CLASS_CA) {
+ if (esf->esf_pid && esf->esf_pid != st->es_pid)
+ continue;
+ ca = NULL;
+ if ((esf->esf_caid != (uint16_t)-1 || esf->esf_caprovider != -1)) {
+ LIST_FOREACH(ca, &st->es_caids, link) {
+ if (esf->esf_caid != (uint16_t)-1 && ca->caid != esf->esf_caid)
+ continue;
+ if (esf->esf_caprovider != (uint32_t)-1 && ca->providerid != esf->esf_caprovider)
+ continue;
+ break;
+ }
+ if (ca == NULL)
+ continue;
+ }
+ sindex++;
+ if (esf->esf_sindex && esf->esf_sindex != sindex)
+ continue;
+ if (esf->esf_log)
+ tvhinfo(LS_SERVICE, "esfilter: \"%s\" %s %03d %03d %05d %04x %06x %s",
+ t->s_nicename, esfilter_class2txt(i), st->es_index,
+ esf->esf_index, st->es_pid, esf->esf_caid, esf->esf_caprovider,
+ esfilter_action2txt(esf->esf_action));
+ switch (esf->esf_action) {
+ case ESFA_NONE:
+ break;
+ case ESFA_IGNORE:
+ca_ignore:
+ if (ca == NULL)
+ LIST_FOREACH(ca, &st->es_caids, link)
+ ca->filter |= ESFM_IGNORE;
+ else
+ ca->filter |= ESFM_IGNORE;
+ st->es_filter |= ESFM_IGNORE;
+ break;
+ case ESFA_ONE_TIME:
+ TAILQ_FOREACH(st2, &set->set_all, es_link)
+ if (st2->es_type == SCT_CA && (st2->es_filter & ESFM_USED) != 0)
+ break;
+ if (st2 != NULL) goto ca_ignore;
+ /* fall through */
+ case ESFA_USE:
+ if (ca == NULL)
+ LIST_FOREACH(ca, &st->es_caids, link)
+ ca->filter |= ESFM_USED;
+ else
+ ca->filter |= ESFM_USED;
+ elementary_set_filter_build_add(set, st, sta, &p);
+ break;
+ case ESFA_EXCLUSIVE:
+ if (ca == NULL)
+ LIST_FOREACH(ca, &st->es_caids, link)
+ ca->use = 1;
+ else {
+ LIST_FOREACH(ca2, &st->es_caids, link)
+ ca2->use = 0;
+ ca->use = 1;
+ }
+ break;
+ case ESFA_EMPTY:
+ if (p == o)
+ elementary_set_filter_build_add(set, st, sta, &p);
+ break;
+ default:
+ tvhdebug(LS_SERVICE, "Unknown esfilter action %d", esf->esf_action);
+ break;
+ }
+ } else {
+ sindex++;
+ if (esf->esf_sindex && esf->esf_sindex != sindex)
+ continue;
+ if (esf->esf_log)
+ tvhinfo(LS_SERVICE, "esfilter: \"%s\" %s %03d %03d %05d %s %s %s",
+ t->s_nicename, esfilter_class2txt(i), st->es_index, esf->esf_index,
+ st->es_pid, streaming_component_type2txt(st->es_type),
+ lang_code_get(st->es_lang), esfilter_action2txt(esf->esf_action));
+ switch (esf->esf_action) {
+ case ESFA_NONE:
+ break;
+ case ESFA_IGNORE:
+ignore:
+ st->es_filter |= ESFM_IGNORE;
+ break;
+ case ESFA_ONE_TIME:
+ TAILQ_FOREACH(st2, &set->set_all, es_link) {
+ if (st == st2)
+ continue;
+ if ((st2->es_filter & ESFM_USED) == 0)
+ continue;
+ if ((mask & SCT_MASK(st2->es_type)) == 0)
+ continue;
+ if (esf->esf_language[0] != '\0' && strcmp(st2->es_lang, st->es_lang))
+ continue;
+ break;
+ }
+ if (st2 != NULL) goto ignore;
+ /* fall through */
+ case ESFA_USE:
+ elementary_set_filter_build_add(set, st, sta, &p);
+ break;
+ case ESFA_EXCLUSIVE:
+ break;
+ case ESFA_EMPTY:
+ if (p == o)
+ elementary_set_filter_build_add(set, st, sta, &p);
+ break;
+ default:
+ tvhdebug(LS_SERVICE, "Unknown esfilter action %d", esf->esf_action);
+ break;
+ }
+ }
+ if (esf->esf_action == ESFA_EXCLUSIVE) {
+ /* forget previous work */
+ while (p > o) {
+ p--;
+ LIST_FOREACH(ca, &sta[p]->es_caids, link)
+ ca->use = 0;
+ TAILQ_REMOVE(&set->set_filter, sta[p], es_filter_link);
+ }
+ st->es_filter = 0;
+ elementary_set_filter_build_add(set, st, sta, &p);
+ exclusive = 1;
+ break;
+ }
+ }
+ if (exclusive) break;
+ }
+ if (!exclusive) {
+ TAILQ_FOREACH(st, &set->set_all, es_link) {
+ if ((mask & SCT_MASK(st->es_type)) != 0 &&
+ (st->es_filter & (ESFM_USED|ESFM_IGNORE)) == 0) {
+ elementary_set_filter_build_add(set, st, sta, &p);
+ LIST_FOREACH(ca, &st->es_caids, link)
+ ca->use = 1;
+ } else {
+ LIST_FOREACH(ca, &st->es_caids, link)
+ if (ca->filter & ESFM_USED)
+ ca->use = 1;
+ }
+ }
+ }
+ }
+
+ elementary_set_filter_print(set);
+}
+
+/**
+ * Add a new stream to a service
+ */
+elementary_stream_t *
+elementary_stream_create
+ (elementary_set_t *set, int pid, streaming_component_type_t type, int running)
+{
+ elementary_stream_t *st, *st2;
+ int i = 0;
+ int idx = 0;
+
+ TAILQ_FOREACH(st, &set->set_all, es_link) {
+ if(st->es_index > idx)
+ idx = st->es_index;
+ i++;
+ if(pid != -1 && st->es_pid == pid)
+ return st;
+ }
+
+ st = calloc(1, sizeof(elementary_stream_t));
+ st->es_index = idx + 1;
+
+ st->es_type = type;
+
+ TAILQ_INSERT_TAIL(&set->set_all, st, es_link);
+ st->es_service = set->set_service;
+
+ st->es_pid = pid;
+
+ elementary_stream_make_nicename(st, set->set_nicename);
+
+ if (running) {
+ elementary_set_filter_build(set);
+ TAILQ_FOREACH(st2, &set->set_filter, es_filter_link)
+ if (st2 == st) {
+ elementary_stream_init(st);
+ break;
+ }
+ }
+
+ return st;
+}
+
+/**
+ * Find an elementary stream in a service
+ */
+elementary_stream_t *
+elementary_stream_find_(elementary_set_t *set, int pid)
+{
+ elementary_stream_t *st;
+
+ TAILQ_FOREACH(st, &set->set_all, es_link) {
+ if(st->es_pid == pid) {
+ set->set_last_es = st;
+ set->set_last_pid = pid;
+ return st;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Find a first elementary stream in a service (by type)
+ */
+elementary_stream_t *
+elementary_stream_type_find
+ (elementary_set_t *set, streaming_component_type_t type)
+{
+ elementary_stream_t *st;
+
+ TAILQ_FOREACH(st, &set->set_all, es_link)
+ if(st->es_type == type)
+ return st;
+ return NULL;
+}
+
+/**
+ *
+ */
+elementary_stream_t *
+elementary_stream_type_modify(elementary_set_t *set, int pid,
+ streaming_component_type_t type,
+ int running)
+{
+ elementary_stream_t *es = elementary_stream_type_find(set, type);
+ if (!es)
+ return elementary_stream_create(set, pid, type, running);
+ if (es->es_pid != pid)
+ es->es_pid = pid;
+ return es;
+}
+
+/**
+ *
+ */
+void
+elementary_stream_type_destroy
+ (elementary_set_t *set, streaming_component_type_t type)
+{
+ elementary_stream_t *es = elementary_stream_type_find(set, type);
+ if (es)
+ elementary_set_stream_destroy(set, es);
+}
+
+/**
+ *
+ */
+int
+elementary_stream_has_audio_or_video(elementary_set_t *set)
+{
+ elementary_stream_t *st;
+ TAILQ_FOREACH(st, &set->set_all, es_link)
+ if (SCT_ISVIDEO(st->es_type) || SCT_ISAUDIO(st->es_type))
+ return 1;
+ return 0;
+}
+
+int
+elementary_stream_has_no_audio(elementary_set_t *set, int filtered)
+{
+ elementary_stream_t *st;
+ if (filtered) {
+ TAILQ_FOREACH(st, &set->set_filter, es_filter_link)
+ if (SCT_ISAUDIO(st->es_type))
+ return 0;
+ } else {
+ TAILQ_FOREACH(st, &set->set_all, es_link)
+ if (SCT_ISAUDIO(st->es_type))
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ *
+ */
+static int
+escmp(const void *A, const void *B)
+{
+ elementary_stream_t *a = *(elementary_stream_t **)A;
+ elementary_stream_t *b = *(elementary_stream_t **)B;
+ return a->es_position - b->es_position;
+}
+
+/**
+ *
+ */
+void
+elementary_set_sort_streams(elementary_set_t *set)
+{
+ elementary_stream_t *st, **v;
+ int num = 0, i = 0;
+
+ TAILQ_FOREACH(st, &set->set_all, es_link)
+ num++;
+
+ v = alloca(num * sizeof(elementary_stream_t *));
+ TAILQ_FOREACH(st, &set->set_all, es_link)
+ v[i++] = st;
+
+ qsort(v, num, sizeof(elementary_stream_t *), escmp);
+
+ TAILQ_INIT(&set->set_all);
+ for(i = 0; i < num; i++)
+ TAILQ_INSERT_TAIL(&set->set_all, v[i], es_link);
+}
+
+/**
+ * Generate a message containing info about all components
+ */
+streaming_start_t *
+elementary_stream_build_start(elementary_set_t *set)
+{
+ elementary_stream_t *st;
+ int n = 0;
+ streaming_start_t *ss;
+
+ TAILQ_FOREACH(st, &set->set_filter, es_filter_link)
+ n++;
+
+ ss = calloc(1, sizeof(streaming_start_t) +
+ sizeof(streaming_start_component_t) * n);
+
+ ss->ss_num_components = n;
+
+ n = 0;
+ TAILQ_FOREACH(st, &set->set_filter, es_filter_link) {
+ streaming_start_component_t *ssc = &ss->ss_components[n++];
+ ssc->ssc_index = st->es_index;
+ ssc->ssc_type = st->es_type;
+
+ memcpy(ssc->ssc_lang, st->es_lang, 4);
+ ssc->ssc_audio_type = st->es_audio_type;
+ ssc->ssc_audio_version = st->es_audio_version;
+ ssc->ssc_composition_id = st->es_composition_id;
+ ssc->ssc_ancillary_id = st->es_ancillary_id;
+ ssc->ssc_pid = st->es_pid;
+ ssc->ssc_width = st->es_width;
+ ssc->ssc_height = st->es_height;
+ ssc->ssc_frameduration = st->es_frame_duration;
+ }
+
+ ss->ss_refcount = 1;
+ return ss;
+}
--- /dev/null
+/*
+ * Tvheadend
+ * Copyright (C) 2010 Andreas Ă–man
+ * 2018 Jaroslav Kysela
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ESSTREAM_H__
+#define ESSTREAM_H__
+
+#include "descrambler.h"
+#include "input/mpegts/dvb.h"
+
+/**
+ *
+ */
+
+typedef struct service service_t;
+typedef struct streaming_start streaming_start_t;
+
+typedef struct elementary_stream elementary_stream_t;
+typedef struct elementary_set elementary_set_t;
+
+/**
+ * Stream, one media component for a service.
+ */
+struct elementary_stream {
+ TAILQ_ENTRY(elementary_stream) es_link;
+ TAILQ_ENTRY(elementary_stream) es_filter_link;
+
+ uint32_t es_position;
+ struct service *es_service;
+
+ streaming_component_type_t es_type;
+ int es_index;
+
+ char *es_nicename;
+
+ /* PID related */
+ int16_t es_pid;
+ uint16_t es_parent_pid; /* For subtitle streams originating from
+ a teletext stream. this is the pid
+ of the teletext stream */
+ int8_t es_pid_opened; /* PID is opened */
+ int8_t es_cc; /* Last CC */
+
+ /* CA ID's on this stream */
+ struct caid_list es_caids;
+
+ /* */
+ int es_delete_me; /* Temporary flag for deleting streams */
+
+ /* Stream info */
+ int es_frame_duration;
+
+ int es_width;
+ int es_height;
+
+ uint16_t es_aspect_num;
+ uint16_t es_aspect_den;
+
+ char es_lang[4]; /* ISO 639 2B 3-letter language code */
+ uint8_t es_audio_type; /* Audio type */
+ uint8_t es_audio_version; /* Audio version/layer */
+
+ uint16_t es_composition_id;
+ uint16_t es_ancillary_id;
+
+ /* Error log limiters */
+ tvhlog_limit_t es_cc_log;
+ /* Filter temporary variable */
+ uint32_t es_filter;
+
+ /* HBBTV PSI table (AIT) */
+ mpegts_psi_table_t es_psi;
+};
+
+/*
+ * Group of elementary streams
+ */
+struct elementary_set {
+ TAILQ_HEAD(, elementary_stream) set_all;
+ TAILQ_HEAD(, elementary_stream) set_filter;
+ int set_subsys;
+ char *set_nicename;
+ uint16_t set_last_pid;
+ elementary_stream_t *set_last_es;
+ service_t *set_service;
+};
+
+/*
+ * Prototypes
+ */
+void elementary_set_init
+ (elementary_set_t *set, int subsys, const char *nicename, service_t *t);
+void elementary_set_clean(elementary_set_t *set);
+void elementary_set_update_nicename(elementary_set_t *set, const char *nicename);
+void elementary_set_clean_streams(elementary_set_t *set);
+void elementary_set_stream_destroy(elementary_set_t *set, elementary_stream_t *es);
+void elementary_set_init_filter_streams(elementary_set_t *set);
+void elementary_set_filter_build(elementary_set_t *set);
+elementary_stream_t *elementary_stream_create
+ (elementary_set_t *set, int pid, streaming_component_type_t type, int running);
+elementary_stream_t *elementary_stream_find_(elementary_set_t *set, int pid);
+elementary_stream_t *elementary_stream_type_find
+ (elementary_set_t *set, streaming_component_type_t type);
+static inline elementary_stream_t *elementary_stream_find
+ (elementary_set_t *set, int pid)
+ {
+ if (set->set_last_pid != (pid))
+ return elementary_stream_find_(set, pid);
+ else
+ return set->set_last_es;
+ }
+elementary_stream_t *elementary_stream_type_modify
+ (elementary_set_t *set, int pid, streaming_component_type_t type, int running);
+void elementary_stream_type_destroy
+ (elementary_set_t *set, streaming_component_type_t type);
+int elementary_stream_has_audio_or_video(elementary_set_t *set);
+int elementary_stream_has_no_audio(elementary_set_t *set, int filtered);
+int elementary_set_has_streams(elementary_set_t *set, int filtered);
+void elementary_set_sort_streams(elementary_set_t *set);
+streaming_start_t *elementary_stream_build_start(elementary_set_t *set);
+
+#endif // ESSTREAM_H__
tvhdebug(mt->mt_subsys, "%s: caid %04X (%s) provider %08X pid %04X",
mt->mt_name, caid, caid2name(caid), provid, pid);
- st = service_stream_find((service_t*)t, pid);
+ st = elementary_stream_find(&t->s_components, pid);
if (st == NULL || st->es_type != SCT_CA) {
- st = service_stream_create((service_t*)t, pid, SCT_CA);
+ st = elementary_stream_create(&t->s_components, pid, SCT_CA,
+ t->s_status == SERVICE_RUNNING);
r |= PMT_UPDATE_NEW_CA_STREAM;
}
// higher than normal MPEG TS (0x2000 ++)
int pid = DVB_TELETEXT_BASE + page;
- st = service_stream_find((service_t*)t, pid);
+ st = elementary_stream_find(&t->s_components, pid);
if (st == NULL || st->es_type != SCT_TEXTSUB) {
r |= PMT_UPDATE_NEW_STREAM;
- st = service_stream_create((service_t*)t, pid, SCT_TEXTSUB);
+ st = elementary_stream_create(&t->s_components, pid, SCT_TEXTSUB,
+ t->s_status == SERVICE_RUNNING);
}
lang = lang_code_get2((const char*)ptr, 3);
len -= 9;
/* Mark all streams for deletion */
- TAILQ_FOREACH(st, &t->s_components, es_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link) {
st->es_delete_me = 1;
LIST_FOREACH(c, &st->es_caids, link)
if (hts_stream_type != SCT_UNKNOWN) {
- st = service_stream_find((service_t*)t, pid);
+ st = elementary_stream_find(&t->s_components, pid);
if (st == NULL || st->es_type != hts_stream_type) {
update |= PMT_UPDATE_NEW_STREAM;
- st = service_stream_create((service_t*)t, pid, hts_stream_type);
+ st = elementary_stream_create(&t->s_components, pid, hts_stream_type,
+ t->s_status == SERVICE_RUNNING);
}
if (st->es_type != hts_stream_type) {
/* Handle PCR 'elementary stream' */
if (!pcr_shared) {
- st = service_stream_type_modify((service_t *)t, t->s_pcr_pid, SCT_PCR);
+ st = elementary_stream_type_modify(&t->s_components, t->s_pcr_pid, SCT_PCR,
+ t->s_status == SERVICE_RUNNING);
st->es_delete_me = 0;
}
/* Scan again to see if any streams should be deleted */
- for(st = TAILQ_FIRST(&t->s_components); st != NULL; st = next) {
+ for(st = TAILQ_FIRST(&t->s_components.set_all); st != NULL; st = next) {
next = TAILQ_NEXT(st, es_link);
for(c = LIST_FIRST(&st->es_caids); c != NULL; c = cn) {
}
if(st->es_delete_me) {
- service_stream_destroy((service_t*)t, st);
+ elementary_set_stream_destroy(&t->s_components, st);
update |= PMT_UPDATE_STREAM_DELETED;
}
}
if(update & PMT_REORDERED)
- sort_elementary_streams((service_t*)t);
+ elementary_set_sort_streams(&t->s_components);
if(update) {
tvhdebug(mt->mt_subsys, "%s: Service \"%s\" PMT (version %d) updated"
}
}
- if (service_has_audio_or_video((service_t *)t)) {
+ if (elementary_stream_has_audio_or_video(&t->s_components)) {
dvb_service_autoenable(t, "PAT and PMT");
t->s_verified = 1;
}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "tvheadend.h"
#include "en50221_capmt.h"
+#include "esstream.h"
#include "input.h"
#define EN50221_CAPMT_CMD_OK 1
{
elementary_stream_t *st;
- TAILQ_FOREACH(st, &s->s_filt_components, es_link)
+ TAILQ_FOREACH(st, &s->s_components.set_filter, es_filter_link)
if (st->es_pid == pid) return 1;
return 0;
}
if (caids_count == 0)
return 0;
- TAILQ_FOREACH(st, &s->s_filt_components, es_link) {
+ TAILQ_FOREACH(st, &s->s_components.set_filter, es_filter_link) {
if (st->es_type != SCT_CA) continue;
LIST_FOREACH(c, &st->es_caids, link) {
if (!c->use) continue;
lock_assert(&s->s_stream_mutex);
es = NULL;
- if (service_stream_find((service_t *)s, pid) == NULL) {
+ if (elementary_stream_find(&s->s_components, pid) == NULL) {
if (!create)
return NULL;
- es = service_stream_create(s, pid, stype);
+ es = elementary_stream_create(&s->s_components, pid, stype,
+ s->s_status == SERVICE_RUNNING);
es->es_pid_opened = 1;
}
if (es && mm->mm_active) {
pthread_mutex_lock(&mi->mi_output_lock);
pthread_mutex_lock(&s->s_stream_mutex);
- TAILQ_FOREACH(es, &s->s_components, es_link) {
+ TAILQ_FOREACH(es, &s->s_components.set_all, es_link) {
if (es->es_type != SCT_CAT) continue;
es->es_delete_me = 1;
LIST_FOREACH(c, &es->es_caids, link)
len -= dlen;
}
- for (es = TAILQ_FIRST(&s->s_components); es != NULL; es = next) {
+ for (es = TAILQ_FIRST(&s->s_components.set_all); es != NULL; es = next) {
next = TAILQ_NEXT(es, es_link);
if (es->es_type != SCT_CAT) continue;
for (c = LIST_FIRST(&es->es_caids); c != NULL; c = cn) {
}
}
if (es->es_delete_me)
- service_stream_destroy(s, es);
+ elementary_set_stream_destroy(&s->s_components, es);
}
pthread_mutex_unlock(&s->s_stream_mutex);
s->s_cat_opened = 1;
}
/* Open only filtered components here */
- TAILQ_FOREACH(st, &s->s_filt_components, es_filt_link)
+ TAILQ_FOREACH(st, &s->s_components.set_filter, es_filter_link)
if ((s->s_scrambled_pass || st->es_type != SCT_CA) &&
st->es_pid != s->s_pmt_pid && st->es_pid != s->s_pcr_pid) {
st->es_pid_opened = 1;
s->s_cat_opened = 0;
}
/* Close all opened PIDs (the component filter may be changed at runtime) */
- TAILQ_FOREACH(st, &s->s_components, es_link) {
+ TAILQ_FOREACH(st, &s->s_components.set_all, es_link) {
if (st->es_pid_opened) {
st->es_pid_opened = 0;
mpegts_input_close_pid(mi, mm, st->es_pid, MPS_SERVICE, s);
if (mpkt->mp_cc_restart) {
LIST_FOREACH(s, &mm->mm_transports, s_active_link)
- TAILQ_FOREACH(st, &s->s_components, es_link)
+ TAILQ_FOREACH(st, &s->s_components.set_all, es_link)
st->es_cc = -1;
}
LIST_FOREACH(mps, &mp->mp_svc_subs, mps_svcraw_link) {
s = mps->mps_owner;
pthread_mutex_lock(&s->s_stream_mutex);
- st = service_stream_find(s, pid);
+ st = elementary_stream_find(&s->s_components, pid);
ts_recv_packet0((mpegts_service_t*)s, st, tsb, llen);
pthread_mutex_unlock(&s->s_stream_mutex);
}
#include <assert.h>
+#include "tvheadend.h"
#include "service.h"
#include "channels.h"
#include "input.h"
pthread_mutex_lock(&s->s_stream_mutex);
if (pid == s->s_pmt_pid || pid == s->s_pcr_pid)
goto ok;
- if (service_stream_find((service_t *)s, pid))
+ if (elementary_stream_find(&s->s_components, pid))
goto ok;
pthread_mutex_unlock(&s->s_stream_mutex);
}
mpegts_pid_add(pids, s->s_pcr_pid, MPS_WEIGHT_PCR);
/* Ensure that filtered PIDs are not send in ts_recv_raw */
- TAILQ_FOREACH(st, &s->s_filt_components, es_filt_link)
+ TAILQ_FOREACH(st, &s->s_components.set_filter, es_filter_link)
if ((is_ddci || s->s_scrambled_pass || st->es_type != SCT_CA) &&
st->es_pid >= 0 && st->es_pid < 8192)
mpegts_pid_add(pids, st->es_pid, mpegts_mps_weight(st));
LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link) {
mpegts_pid_add(&tr->sf_pids, s->s_pmt_pid, w);
mpegts_pid_add(&tr->sf_pids, s->s_pcr_pid, w);
- TAILQ_FOREACH(st, &s->s_components, es_link)
+ TAILQ_FOREACH(st, &s->s_components.set_all, es_link)
mpegts_pid_add(&tr->sf_pids, st->es_pid, w);
}
}
if (t->s_start_time + sec2mono(1) < mclk() &&
tvhlog_limit(&st->es_cc_log, 10))
tvhwarn(LS_TS, "%s Continuity counter error (total %zi)",
- service_component_nicename(st), st->es_cc_log.count);
+ st->es_nicename, st->es_cc_log.count);
if (!error)
errors++;
error |= 2;
if (t->s_start_time + sec2mono(1) < mclk() &&
tvhlog_limit(&st->es_cc_log, 10))
tvhwarn(LS_TS, "%s Continuity counter error (total %zi)",
- service_component_nicename(st), st->es_cc_log.count);
+ st->es_nicename, st->es_cc_log.count);
}
st->es_cc = (cc + 1) & 0xf;
}
pid = (tsb[1] & 0x1f) << 8 | tsb[2];
- st = service_stream_find((service_t*)t, pid);
+ st = elementary_stream_find(&t->s_components, pid);
if((st == NULL) && (pid != t->s_pcr_pid) && !table) {
pthread_mutex_unlock(&t->s_stream_mutex);
for ( ; len > 0; tsb += len2, len -= len2 ) {
len2 = mpegts_word_count(tsb, len, 0xFF9FFFD0);
pid = (tsb[1] & 0x1f) << 8 | tsb[2];
- st = service_stream_find((service_t*)t, pid);
+ st = elementary_stream_find(&t->s_components, pid);
ts_recv_packet0(t, st, tsb, len2);
}
}
for ( ; len > 0; tsb += len2, len -= len2 ) {
len2 = mpegts_word_count(tsb, len, 0xFF9FFFD0);
pid = (tsb[1] & 0x1f) << 8 | tsb[2];
- if((st = service_stream_find((service_t*)t, pid)) != NULL)
+ if((st = elementary_stream_find(&t->s_components, pid)) != NULL)
ts_recv_skipped0(t, st, tsb, len2);
}
}
prs->prs_candidate_pcr = PTS_UNSET;
prs->prs_current_pcr_guess = 0;
prs->prs_pcr_boundary = 90000;
- if (service_has_no_audio(prs->prs_service, 1))
+ if (elementary_stream_has_no_audio(&prs->prs_service->s_components, 1))
prs->prs_pcr_boundary = 6*90000;
streaming_target_deliver2(prs->prs_output, sm);
pthread_mutex_unlock(&s->s_stream_mutex);
return 0;
}
- TAILQ_FOREACH(st, &s->s_components, es_link) {
+ TAILQ_FOREACH(st, &s->s_components.set_all, es_link) {
if (st->es_type == SCT_CA)
enc = 1;
if (st->es_pid > 0)
size_t l;
buf[0] = '\0';
- TAILQ_FOREACH(st, &svc->s_components, es_link) {
+ TAILQ_FOREACH(st, &svc->s_components.set_all, es_link) {
switch(st->es_type) {
case SCT_CA:
LIST_FOREACH(c, &st->es_caids, link) {
.ic_properties = NULL
};
-/**
- *
- */
-static void
-stream_init(elementary_stream_t *st)
-{
- st->es_cc = -1;
-
- if (st->es_type == SCT_HBBTV && st->es_psi.mt_name == NULL)
- dvb_table_parse_init(&st->es_psi, "hbbtv", LS_TS, st->es_pid,
- DVB_HBBTV_BASE, DVB_HBBTV_MASK, st);
-}
-
-
-/**
- *
- */
-static void
-stream_clean(elementary_stream_t *st)
-{
- tvhlog_limit_reset(&st->es_cc_log);
-
- if (st->es_psi.mt_name)
- dvb_table_reset(&st->es_psi);
-}
-
-/**
- *
- */
-void
-service_stream_destroy(service_t *t, elementary_stream_t *es)
-{
- elementary_stream_t *es1;
- caid_t *c;
-
- if(t->s_status == SERVICE_RUNNING)
- stream_clean(es);
-
- if (es->es_psi.mt_name)
- dvb_table_parse_done(&es->es_psi);
-
- if (t->s_last_es == es) {
- t->s_last_pid = -1;
- t->s_last_es = NULL;
- }
-
- TAILQ_REMOVE(&t->s_components, es, es_link);
- TAILQ_FOREACH(es1, &t->s_filt_components, es_filt_link)
- if (es1 == es) {
- TAILQ_REMOVE(&t->s_filt_components, es, es_filt_link);
- break;
- }
-
- while ((c = LIST_FIRST(&es->es_caids)) != NULL) {
- LIST_REMOVE(c, link);
- free(c);
- }
-
- free(es->es_nicename);
- free(es);
-}
-
/**
* Service lock must be held
*/
void
service_stop(service_t *t)
{
- elementary_stream_t *st;
-
mtimer_disarm(&t->s_receive_timer);
t->s_stop_feed(t);
/**
* Clean up each stream
*/
- TAILQ_FOREACH(st, &t->s_components, es_link)
- stream_clean(st);
+ elementary_set_clean_streams(&t->s_components);
t->s_status = SERVICE_IDLE;
tvhlog_limit_reset(&t->s_tei_log);
}
}
-
-/**
- *
- */
-#define ESFM_USED (1<<0)
-#define ESFM_IGNORE (1<<1)
-
-static void
-service_build_filter_add(service_t *t, elementary_stream_t *st,
- elementary_stream_t **sta, int *p)
-{
- /* only once */
- if (st->es_filter & ESFM_USED)
- return;
- st->es_filter |= ESFM_USED;
- TAILQ_INSERT_TAIL(&t->s_filt_components, st, es_filt_link);
- sta[*p] = st;
- (*p)++;
-}
-
-/**
- *
- */
-static void
-service_print_filter(service_t *t)
-{
- elementary_stream_t *st;
- caid_t *ca;
-
- if (!tvhtrace_enabled())
- return;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
- if (LIST_EMPTY(&st->es_caids)) {
- tvhtrace(LS_SERVICE, "esfilter: \"%s\" %03d %05d %s %s",
- t->s_nicename, st->es_index, st->es_pid,
- streaming_component_type2txt(st->es_type),
- lang_code_get(st->es_lang));
- } else {
- LIST_FOREACH(ca, &st->es_caids, link)
- if (ca->use)
- tvhtrace(LS_SERVICE, "esfilter: \"%s\" %03d %05d %s %04x %06x",
- t->s_nicename, st->es_index, st->es_pid,
- streaming_component_type2txt(st->es_type),
- ca->caid, ca->providerid);
- }
- }
-}
-
-/**
- *
- */
-void
-service_build_filter(service_t *t)
-{
- elementary_stream_t *st, *st2, **sta;
- esfilter_t *esf;
- caid_t *ca, *ca2;
- int i, n, p, o, exclusive, sindex;
- uint32_t mask;
- char ubuf[UUID_HEX_SIZE];
-
- /* rebuild the filtered and ordered components */
- TAILQ_INIT(&t->s_filt_components);
-
- for (i = ESF_CLASS_VIDEO; i <= ESF_CLASS_LAST; i++)
- if (!TAILQ_EMPTY(&esfilters[i]))
- goto filter;
-
- TAILQ_FOREACH(st, &t->s_components, es_link) {
- TAILQ_INSERT_TAIL(&t->s_filt_components, st, es_filt_link);
- LIST_FOREACH(ca, &st->es_caids, link)
- ca->use = 1;
- }
- service_print_filter(t);
- return;
-
-filter:
- n = 0;
- TAILQ_FOREACH(st, &t->s_components, es_link) {
- st->es_filter = 0;
- LIST_FOREACH(ca, &st->es_caids, link) {
- ca->use = 0;
- ca->filter = 0;
- }
- n++;
- }
-
- sta = alloca(sizeof(elementary_stream_t *) * n);
-
- for (i = ESF_CLASS_VIDEO, p = 0; i <= ESF_CLASS_LAST; i++) {
- o = p;
- mask = esfilterclsmask[i];
- if (TAILQ_EMPTY(&esfilters[i])) {
- TAILQ_FOREACH(st, &t->s_components, es_link) {
- if ((mask & SCT_MASK(st->es_type)) != 0) {
- service_build_filter_add(t, st, sta, &p);
- LIST_FOREACH(ca, &st->es_caids, link)
- ca->use = 1;
- }
- }
- continue;
- }
- exclusive = 0;
- TAILQ_FOREACH(esf, &esfilters[i], esf_link) {
- if (!esf->esf_enabled)
- continue;
- sindex = 0;
- TAILQ_FOREACH(st, &t->s_components, es_link) {
- if ((mask & SCT_MASK(st->es_type)) == 0)
- continue;
- if (esf->esf_type && (esf->esf_type & SCT_MASK(st->es_type)) == 0)
- continue;
- if (esf->esf_language[0] &&
- strncmp(esf->esf_language, st->es_lang, 4))
- continue;
- if (esf->esf_service[0]) {
- if (strcmp(esf->esf_service, idnode_uuid_as_str(&t->s_id, ubuf)))
- continue;
- if (esf->esf_pid && esf->esf_pid != st->es_pid)
- continue;
- }
- if (i == ESF_CLASS_CA) {
- if (esf->esf_pid && esf->esf_pid != st->es_pid)
- continue;
- ca = NULL;
- if ((esf->esf_caid != (uint16_t)-1 || esf->esf_caprovider != -1)) {
- LIST_FOREACH(ca, &st->es_caids, link) {
- if (esf->esf_caid != (uint16_t)-1 && ca->caid != esf->esf_caid)
- continue;
- if (esf->esf_caprovider != (uint32_t)-1 && ca->providerid != esf->esf_caprovider)
- continue;
- break;
- }
- if (ca == NULL)
- continue;
- }
- sindex++;
- if (esf->esf_sindex && esf->esf_sindex != sindex)
- continue;
- if (esf->esf_log)
- tvhinfo(LS_SERVICE, "esfilter: \"%s\" %s %03d %03d %05d %04x %06x %s",
- t->s_nicename, esfilter_class2txt(i), st->es_index,
- esf->esf_index, st->es_pid, esf->esf_caid, esf->esf_caprovider,
- esfilter_action2txt(esf->esf_action));
- switch (esf->esf_action) {
- case ESFA_NONE:
- break;
- case ESFA_IGNORE:
-ca_ignore:
- if (ca == NULL)
- LIST_FOREACH(ca, &st->es_caids, link)
- ca->filter |= ESFM_IGNORE;
- else
- ca->filter |= ESFM_IGNORE;
- st->es_filter |= ESFM_IGNORE;
- break;
- case ESFA_ONE_TIME:
- TAILQ_FOREACH(st2, &t->s_components, es_link)
- if (st2->es_type == SCT_CA && (st2->es_filter & ESFM_USED) != 0)
- break;
- if (st2 != NULL) goto ca_ignore;
- /* fall through */
- case ESFA_USE:
- if (ca == NULL)
- LIST_FOREACH(ca, &st->es_caids, link)
- ca->filter |= ESFM_USED;
- else
- ca->filter |= ESFM_USED;
- service_build_filter_add(t, st, sta, &p);
- break;
- case ESFA_EXCLUSIVE:
- if (ca == NULL)
- LIST_FOREACH(ca, &st->es_caids, link)
- ca->use = 1;
- else {
- LIST_FOREACH(ca2, &st->es_caids, link)
- ca2->use = 0;
- ca->use = 1;
- }
- break;
- case ESFA_EMPTY:
- if (p == o)
- service_build_filter_add(t, st, sta, &p);
- break;
- default:
- tvhdebug(LS_SERVICE, "Unknown esfilter action %d", esf->esf_action);
- break;
- }
- } else {
- sindex++;
- if (esf->esf_sindex && esf->esf_sindex != sindex)
- continue;
- if (esf->esf_log)
- tvhinfo(LS_SERVICE, "esfilter: \"%s\" %s %03d %03d %05d %s %s %s",
- t->s_nicename, esfilter_class2txt(i), st->es_index, esf->esf_index,
- st->es_pid, streaming_component_type2txt(st->es_type),
- lang_code_get(st->es_lang), esfilter_action2txt(esf->esf_action));
- switch (esf->esf_action) {
- case ESFA_NONE:
- break;
- case ESFA_IGNORE:
-ignore:
- st->es_filter |= ESFM_IGNORE;
- break;
- case ESFA_ONE_TIME:
- TAILQ_FOREACH(st2, &t->s_components, es_link) {
- if (st == st2)
- continue;
- if ((st2->es_filter & ESFM_USED) == 0)
- continue;
- if ((mask & SCT_MASK(st2->es_type)) == 0)
- continue;
- if (esf->esf_language[0] != '\0' && strcmp(st2->es_lang, st->es_lang))
- continue;
- break;
- }
- if (st2 != NULL) goto ignore;
- /* fall through */
- case ESFA_USE:
- service_build_filter_add(t, st, sta, &p);
- break;
- case ESFA_EXCLUSIVE:
- break;
- case ESFA_EMPTY:
- if (p == o)
- service_build_filter_add(t, st, sta, &p);
- break;
- default:
- tvhdebug(LS_SERVICE, "Unknown esfilter action %d", esf->esf_action);
- break;
- }
- }
- if (esf->esf_action == ESFA_EXCLUSIVE) {
- /* forget previous work */
- while (p > o) {
- p--;
- LIST_FOREACH(ca, &sta[p]->es_caids, link)
- ca->use = 0;
- TAILQ_REMOVE(&t->s_filt_components, sta[p], es_filt_link);
- }
- st->es_filter = 0;
- service_build_filter_add(t, st, sta, &p);
- exclusive = 1;
- break;
- }
- }
- if (exclusive) break;
- }
- if (!exclusive) {
- TAILQ_FOREACH(st, &t->s_components, es_link) {
- if ((mask & SCT_MASK(st->es_type)) != 0 &&
- (st->es_filter & (ESFM_USED|ESFM_IGNORE)) == 0) {
- service_build_filter_add(t, st, sta, &p);
- LIST_FOREACH(ca, &st->es_caids, link)
- ca->use = 1;
- } else {
- LIST_FOREACH(ca, &st->es_caids, link)
- if (ca->filter & ESFM_USED)
- ca->use = 1;
- }
- }
- }
- }
-
- service_print_filter(t);
-}
-
/**
*
*/
service_start(service_t *t, int instance, int weight, int flags,
int timeout, int postpone)
{
- elementary_stream_t *st;
int r, stimeout = 10;
lock_assert(&global_lock);
t->s_start_time = mclk();
pthread_mutex_lock(&t->s_stream_mutex);
- service_build_filter(t);
+ elementary_set_filter_build(&t->s_components);
pthread_mutex_unlock(&t->s_stream_mutex);
descrambler_caid_changed(t);
/**
* Initialize stream
*/
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link)
- stream_init(st);
+ elementary_set_init_filter_streams(&t->s_components);
pthread_mutex_unlock(&t->s_stream_mutex);
void
service_destroy(service_t *t, int delconf)
{
- elementary_stream_t *st;
th_subscription_t *s;
idnode_list_mapping_t *ilm;
t->s_status = SERVICE_ZOMBIE;
- TAILQ_INIT(&t->s_filt_components);
- while((st = TAILQ_FIRST(&t->s_components)) != NULL)
- service_stream_destroy(t, st);
+ elementary_set_clean(&t->s_components);
if (t->s_hbbtv) {
htsmsg_destroy(t->s_hbbtv);
t->s_channel_name = service_channel_name;
t->s_provider_name = service_provider_name;
t->s_memoryinfo = service_memoryinfo;
- TAILQ_INIT(&t->s_components);
- TAILQ_INIT(&t->s_filt_components);
- t->s_last_pid = -1;
+ elementary_set_init(&t->s_components, LS_SERVICE, NULL, t);
streaming_pad_init(&t->s_streaming_pad);
return t;
}
-
-/**
- *
- */
-static void
-service_stream_make_nicename(service_t *t, elementary_stream_t *st)
-{
- char buf[256];
- if(st->es_pid != -1)
- snprintf(buf, sizeof(buf), "%s: %s @ #%d",
- service_nicename(t),
- streaming_component_type2txt(st->es_type), st->es_pid);
- else
- snprintf(buf, sizeof(buf), "%s: %s",
- service_nicename(t),
- streaming_component_type2txt(st->es_type));
-
- free(st->es_nicename);
- st->es_nicename = strdup(buf);
-}
-
-
/**
*
*/
/**
*
*/
-void
+const char *
service_make_nicename(service_t *t)
{
int prefidx;
char buf[256];
- elementary_stream_t *st;
prefidx = service_make_nicename0(t, buf, sizeof(buf), 0);
t->s_nicename = strdup(buf);
t->s_nicename_prefidx = prefidx;
- TAILQ_FOREACH(st, &t->s_components, es_link)
- service_stream_make_nicename(t, st);
-}
-
-/**
- * Add a new stream to a service
- */
-elementary_stream_t *
-service_stream_create(service_t *t, int pid,
- streaming_component_type_t type)
-{
- elementary_stream_t *st, *st2;
- int i = 0;
- int idx = 0;
- lock_assert(&t->s_stream_mutex);
-
- TAILQ_FOREACH(st, &t->s_components, es_link) {
- if(st->es_index > idx)
- idx = st->es_index;
- i++;
- if(pid != -1 && st->es_pid == pid)
- return st;
- }
-
- st = calloc(1, sizeof(elementary_stream_t));
- st->es_index = idx + 1;
-
- st->es_type = type;
-
- TAILQ_INSERT_TAIL(&t->s_components, st, es_link);
- st->es_service = t;
-
- st->es_pid = pid;
-
- service_stream_make_nicename(t, st);
-
- if(t->s_status == SERVICE_RUNNING) {
- service_build_filter(t);
- TAILQ_FOREACH(st2, &t->s_filt_components, es_filt_link)
- if (st2 == st) {
- stream_init(st);
- break;
- }
- }
-
- return st;
-}
-
-/**
- * Find an elementary stream in a service
- */
-elementary_stream_t *
-service_stream_find_(service_t *t, int pid)
-{
- elementary_stream_t *st;
-
- lock_assert(&t->s_stream_mutex);
-
- TAILQ_FOREACH(st, &t->s_components, es_link) {
- if(st->es_pid == pid) {
- t->s_last_es = st;
- t->s_last_pid = pid;
- return st;
- }
- }
- return NULL;
-}
+ elementary_set_update_nicename(&t->s_components, t->s_nicename);
-/**
- * Find a first elementary stream in a service (by type)
- */
-elementary_stream_t *
-service_stream_type_find(service_t *t, streaming_component_type_t type)
-{
- elementary_stream_t *st;
-
- lock_assert(&t->s_stream_mutex);
-
- TAILQ_FOREACH(st, &t->s_components, es_link)
- if(st->es_type == type)
- return st;
- return NULL;
-}
-
-/**
- *
- */
-elementary_stream_t *
-service_stream_type_modify(service_t *t, int pid,
- streaming_component_type_t type)
-{
- elementary_stream_t *es = service_stream_type_find(t, type);
- if (!es)
- return service_stream_create(t, pid, type);
- if (es->es_pid != pid)
- es->es_pid = pid;
- return es;
-}
-
-/**
- *
- */
-void
-service_stream_type_destroy(service_t *t, streaming_component_type_t type)
-{
- elementary_stream_t *es = service_stream_type_find(t, type);
- if (es)
- service_stream_destroy(t, es);
+ return t->s_nicename;
}
/**
sec2mono(t->s_timeout));
}
-/**
- *
- */
-int
-service_has_audio_or_video(service_t *t)
-{
- elementary_stream_t *st;
- TAILQ_FOREACH(st, &t->s_components, es_link)
- if (SCT_ISVIDEO(st->es_type) || SCT_ISAUDIO(st->es_type))
- return 1;
- return 0;
-}
-
-int
-service_has_no_audio(service_t *t, int filtered)
-{
- elementary_stream_t *st;
- TAILQ_FOREACH(st, filtered ? &t->s_filt_components :
- &t->s_components, es_link)
- if (SCT_ISAUDIO(st->es_type))
- return 0;
- return 1;
-}
-
int
service_is_sdtv(service_t *t)
{
return 1;
else if (s_type == ST_NONE) {
elementary_stream_t *st;
- TAILQ_FOREACH(st, &t->s_components, es_link)
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link)
if (SCT_ISVIDEO(st->es_type) && st->es_height < 720)
return 1;
}
return 1;
else if (s_type == ST_NONE) {
elementary_stream_t *st;
- TAILQ_FOREACH(st, &t->s_components, es_link)
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link)
if (SCT_ISVIDEO(st->es_type) &&
st->es_height >= 720 && st->es_height <= 1080)
return 1;
return 1;
else if (s_type == ST_NONE) {
elementary_stream_t *st;
- TAILQ_FOREACH(st, &t->s_components, es_link)
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link)
if (SCT_ISVIDEO(st->es_type) && st->es_height > 1080)
return 1;
}
return 1;
else if (s_type == ST_NONE) {
elementary_stream_t *st;
- TAILQ_FOREACH(st, &t->s_components, es_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link) {
if (SCT_ISVIDEO(st->es_type))
return 0;
else if (SCT_ISAUDIO(st->es_type))
return 0;
if (((mpegts_service_t *)t)->s_dvb_forcecaid)
return 1;
- TAILQ_FOREACH(st, &t->s_components, es_link)
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link)
if (st->es_type == SCT_CA)
return 1;
return 0;
service_restart_streams(service_t *t)
{
streaming_message_t *sm;
- int had_components = TAILQ_FIRST(&t->s_filt_components) != NULL &&
- t->s_running;
+ const int had_streams = elementary_set_has_streams(&t->s_components, 1);
+ const int had_components = had_streams && t->s_running;
- service_build_filter(t);
+ elementary_set_filter_build(&t->s_components);
- if(TAILQ_FIRST(&t->s_filt_components) != NULL) {
+ if(had_streams) {
if (had_components) {
sm = streaming_msg_create_code(SMT_STOP, SM_CODE_SOURCE_RECONFIGURED);
streaming_service_deliver(t, sm);
service_build_stream_start(service_t *t)
{
extern const idclass_t mpegts_service_class;
- elementary_stream_t *st;
- int n = 0;
streaming_start_t *ss;
- lock_assert(&t->s_stream_mutex);
-
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link)
- n++;
-
- ss = calloc(1, sizeof(streaming_start_t) +
- sizeof(streaming_start_component_t) * n);
-
- ss->ss_num_components = n;
-
- n = 0;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
- streaming_start_component_t *ssc = &ss->ss_components[n++];
- ssc->ssc_index = st->es_index;
- ssc->ssc_type = st->es_type;
-
- memcpy(ssc->ssc_lang, st->es_lang, 4);
- ssc->ssc_audio_type = st->es_audio_type;
- ssc->ssc_audio_version = st->es_audio_version;
- ssc->ssc_composition_id = st->es_composition_id;
- ssc->ssc_ancillary_id = st->es_ancillary_id;
- ssc->ssc_pid = st->es_pid;
- ssc->ssc_width = st->es_width;
- ssc->ssc_height = st->es_height;
- ssc->ssc_frameduration = st->es_frame_duration;
- }
-
- t->s_setsourceinfo(t, &ss->ss_si);
-
- ss->ss_refcount = 1;
+ ss = elementary_stream_build_start(&t->s_components);
ss->ss_pcr_pid = t->s_pcr_pid;
ss->ss_pmt_pid = t->s_pmt_pid;
if (idnode_is_instance(&t->s_id, &mpegts_service_class)) {
return ss;
}
-
/**
*
*/
return t->s_nicename;
}
-const char *
-service_component_nicename(elementary_stream_t *st)
-{
- return st->es_nicename;
-}
-
const char *
service_adapter_nicename(service_t *t, char *buf, size_t len)
{
pthread_mutex_lock(&t->s_stream_mutex);
list = htsmsg_create_list();
- TAILQ_FOREACH(st, &t->s_components, es_link) {
+ TAILQ_FOREACH(st, &t->s_components.set_all, es_link) {
if (st->es_type == SCT_PCR)
continue;
}
}
-/**
- *
- */
-static int
-escmp(const void *A, const void *B)
-{
- elementary_stream_t *a = *(elementary_stream_t **)A;
- elementary_stream_t *b = *(elementary_stream_t **)B;
- return a->es_position - b->es_position;
-}
-
-/**
- *
- */
-void
-sort_elementary_streams(service_t *t)
-{
- elementary_stream_t *st, **v;
- int num = 0, i = 0;
-
- TAILQ_FOREACH(st, &t->s_components, es_link)
- num++;
-
- v = alloca(num * sizeof(elementary_stream_t *));
- TAILQ_FOREACH(st, &t->s_components, es_link)
- v[i++] = st;
-
- qsort(v, num, sizeof(elementary_stream_t *), escmp);
-
- TAILQ_INIT(&t->s_components);
- for(i = 0; i < num; i++)
- TAILQ_INSERT_TAIL(&t->s_components, v[i], es_link);
-}
-
/**
*
*/
LIST_INSERT_HEAD(&st->es_caids, c, link);
}
-
/**
*
*/
add_caid(st, a, b);
}
-
/**
*
*/
if(pid > 0 && t->s_pcr_pid > 0 && pid == t->s_pcr_pid)
shared_pcr = 1;
- st = service_stream_create(t, pid, type);
+ st = elementary_stream_create(&t->s_components, pid, type, 0);
if((v = htsmsg_get_str(c, "language")) != NULL)
strncpy(st->es_lang, lang_code_get(v), 3);
}
}
if (!shared_pcr)
- service_stream_type_modify(t, t->s_pcr_pid, SCT_PCR);
+ elementary_stream_type_modify(&t->s_components, t->s_pcr_pid, SCT_PCR, 0);
else
- service_stream_type_destroy(t, SCT_PCR);
- sort_elementary_streams(t);
+ elementary_stream_type_destroy(&t->s_components, SCT_PCR);
+ elementary_set_sort_streams(&t->s_components);
pthread_mutex_unlock(&t->s_stream_mutex);
}
#ifndef SERVICE_H__
#define SERVICE_H__
+#include "esstream.h"
#include "sbuf.h"
#include "htsmsg.h"
#include "idnode.h"
#include "profile.h"
-#include "descrambler.h"
-#include "input/mpegts/dvb.h"
extern const idclass_t service_class;
extern const idclass_t service_raw_class;
int si_type;
} source_info_t;
-/**
- * Stream, one media component for a service.
- */
-typedef struct elementary_stream {
-
- TAILQ_ENTRY(elementary_stream) es_link;
- TAILQ_ENTRY(elementary_stream) es_filt_link;
-
- uint32_t es_position;
- struct service *es_service;
-
- streaming_component_type_t es_type;
- int es_index;
-
- char *es_nicename;
-
- /* PID related */
- int16_t es_pid;
- uint16_t es_parent_pid; /* For subtitle streams originating from
- a teletext stream. this is the pid
- of the teletext stream */
- int8_t es_pid_opened; /* PID is opened */
- int8_t es_cc; /* Last CC */
-
- /* CA ID's on this stream */
- struct caid_list es_caids;
-
- /* */
- int es_delete_me; /* Temporary flag for deleting streams */
-
- /* Stream info */
- int es_frame_duration;
-
- int es_width;
- int es_height;
-
- uint16_t es_aspect_num;
- uint16_t es_aspect_den;
-
- char es_lang[4]; /* ISO 639 2B 3-letter language code */
- uint8_t es_audio_type; /* Audio type */
- uint8_t es_audio_version; /* Audio version/layer */
-
- uint16_t es_composition_id;
- uint16_t es_ancillary_id;
-
- /* Error log limiters */
- tvhlog_limit_t es_cc_log;
-
- /* Filter temporary variable */
- uint32_t es_filter;
-
- /* HBBTV PSI table (AIT) */
- mpegts_psi_table_t es_psi;
-
-} elementary_stream_t;
-
-
typedef TAILQ_HEAD(service_instance_list, service_instance) service_instance_list_t;
/**
descramble_info_t *s_descramble_info;
/**
- * List of all and filtered components.
+ * Set of all and filtered components.
*/
- struct elementary_stream_queue s_components;
- struct elementary_stream_queue s_filt_components;
- short s_last_pid;
- elementary_stream_t *s_last_es;
+ elementary_set_t s_components;
/**
* Delivery pad, this is were we finally deliver all streaming output
int timeout, int postpone);
void service_stop(service_t *t);
-void service_build_filter(service_t *t);
-
service_t *service_create0(service_t *t, int service_type, const idclass_t *idc,
const char *uuid, int source_type, htsmsg_t *conf);
int flags, int timeout,
int postpone);
-elementary_stream_t *service_stream_find_(service_t *t, int pid);
-
-static inline elementary_stream_t *
-service_stream_find(service_t *t, int pid)
-{
- if (t->s_last_pid != (pid))
- return service_stream_find_(t, pid);
- else
- return t->s_last_es;
-}
-
-elementary_stream_t *
-service_stream_type_find(service_t *t, streaming_component_type_t type);
-
-elementary_stream_t *service_stream_create(service_t *t, int pid,
- streaming_component_type_t type);
-
-elementary_stream_t *
-service_stream_type_modify(service_t *t, int pid,
- streaming_component_type_t type);
-
void service_settings_write(service_t *t);
const char *service_servicetype_txt(service_t *t);
-int service_has_audio_or_video(service_t *t);
-int service_has_no_audio(service_t *t, int filtered);
int service_is_sdtv(service_t *t);
int service_is_uhdtv(service_t *t);
int service_is_hdtv(service_t *t);
service_set_streaming_status_flags_(t, n & ~flag);
}
-
struct streaming_start;
struct streaming_start *service_build_stream_start(service_t *t);
void service_restart_streams(service_t *t);
-void service_stream_destroy(service_t *t, elementary_stream_t *st);
-
-void service_stream_type_destroy(service_t *t, streaming_component_type_t type);
-
void service_request_save(service_t *t);
void service_source_info_free(source_info_t *si);
void service_source_info_copy(source_info_t *dst, const source_info_t *src);
-void service_make_nicename(service_t *t);
+const char *service_make_nicename(service_t *t);
const char *service_nicename(service_t *t);
-const char *service_component_nicename(elementary_stream_t *st);
-
const char *service_adapter_nicename(service_t *t, char *buf, size_t len);
const char *service_tss2text(int flags);
pthread_mutex_lock(&t->s_stream_mutex);
- if(TAILQ_FIRST(&t->s_filt_components) != NULL ||
- t->s_type != STYPE_STD) {
+ if(elementary_set_has_streams(&t->s_components, 1) || t->s_type != STYPE_STD) {
streaming_msg_free(s->ths_start_message);
LIST_HEAD(service_list, service);
RB_HEAD(service_tree, service);
TAILQ_HEAD(service_queue, service);
-LIST_HEAD(elementary_stream_list, elementary_stream);
-TAILQ_HEAD(elementary_stream_queue, elementary_stream);
LIST_HEAD(th_muxer_list, th_muxer);
LIST_HEAD(th_muxstream_list, th_muxstream);
LIST_HEAD(th_descrambler_list, th_descrambler);