tvheadend/src/dvb/dvb_multiplex.c
2009-11-17 22:00:23 +00:00

1047 lines
26 KiB
C

/*
* TV Input - Linux DVB interface
* Copyright (C) 2007 Andreas Öman
*
* 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 <pthread.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include "settings.h"
#include "tvhead.h"
#include "dvb.h"
#include "channels.h"
#include "transports.h"
#include "teletext.h"
#include "psi.h"
#include "dvb_support.h"
#include "notify.h"
struct th_dvb_mux_instance_tree dvb_muxes;
static struct strtab muxfestatustab[] = {
{ "Unknown", TDMI_FE_UNKNOWN },
{ "No signal", TDMI_FE_NO_SIGNAL },
{ "Faint signal", TDMI_FE_FAINT_SIGNAL },
{ "Bad signal", TDMI_FE_BAD_SIGNAL },
{ "Constant FEC", TDMI_FE_CONSTANT_FEC },
{ "Bursty FEC", TDMI_FE_BURSTY_FEC },
{ "OK", TDMI_FE_OK },
};
static void tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled);
/**
*
*/
static void
mux_link_initial(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi)
{
int was_empty = TAILQ_FIRST(&tda->tda_initial_scan_queue) == NULL;
tdmi->tdmi_scan_queue = &tda->tda_initial_scan_queue;
TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
if(was_empty && (tda->tda_mux_current == NULL ||
tda->tda_mux_current->tdmi_table_initial == 0))
dvb_adapter_mux_scanner(tda);
}
/**
* Return a readable status text for the given mux
*/
const char *
dvb_mux_status(th_dvb_mux_instance_t *tdmi)
{
return val2str(tdmi->tdmi_fe_status, muxfestatustab) ?: "Invalid";
}
/**
*
*/
static int
tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b)
{
return strcmp(a->tdmi_identifier, b->tdmi_identifier);
}
/**
*
*/
static int
tdmi_compare_key(const struct dvb_mux_conf *a,
const struct dvb_mux_conf *b)
{
return a->dmc_fe_params.frequency == b->dmc_fe_params.frequency &&
a->dmc_polarisation == b->dmc_polarisation &&
a->dmc_satconf == b->dmc_satconf;
}
/**
* Return 0 if configuration does not differ. return 1 if it does
*/
static int
tdmi_compare_conf(int adapter_type,
const struct dvb_mux_conf *a,
const struct dvb_mux_conf *b)
{
switch(adapter_type) {
case FE_OFDM:
return memcmp(&a->dmc_fe_params.u.ofdm,
&b->dmc_fe_params.u.ofdm,
sizeof(a->dmc_fe_params.u.ofdm));
case FE_QAM:
return memcmp(&a->dmc_fe_params.u.qam,
&b->dmc_fe_params.u.qam,
sizeof(a->dmc_fe_params.u.qam));
case FE_ATSC:
return memcmp(&a->dmc_fe_params.u.vsb,
&b->dmc_fe_params.u.vsb,
sizeof(a->dmc_fe_params.u.vsb));
case FE_QPSK:
return memcmp(&a->dmc_fe_params.u.qpsk,
&b->dmc_fe_params.u.qpsk,
sizeof(a->dmc_fe_params.u.qpsk))
#if DVB_API_VERSION >= 5
|| a->dmc_fe_modulation != b->dmc_fe_modulation
|| a->dmc_fe_delsys != b->dmc_fe_delsys
|| a->dmc_fe_rolloff != b->dmc_fe_rolloff
#endif
;
}
return 0;
}
/**
* Create a new mux on the given adapter, return NULL if it already exists
*/
th_dvb_mux_instance_t *
dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc,
uint16_t tsid, const char *network, const char *source,
int enabled, const char *identifier)
{
th_dvb_mux_instance_t *tdmi, *c;
unsigned int hash;
char buf[200];
lock_assert(&global_lock);
hash = (dmc->dmc_fe_params.frequency +
dmc->dmc_polarisation) % TDA_MUX_HASH_WIDTH;
LIST_FOREACH(tdmi, &tda->tda_mux_hash[hash], tdmi_adapter_hash_link) {
if(tdmi_compare_key(&tdmi->tdmi_conf, dmc))
break; /* Mux already exist */
}
if(tdmi != NULL) {
/* Update stuff ... */
int save = 0;
if(tdmi_compare_conf(tda->tda_type, &tdmi->tdmi_conf, dmc)) {
memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf));
save = 1;
}
if(tdmi->tdmi_transport_stream_id != tsid) {
tdmi->tdmi_transport_stream_id = tsid;
save = 1;
}
if(save) {
dvb_mux_save(tdmi);
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_DEBUG, "dvb",
"Configuration for mux \"%s\" updated by %s", buf, source);
dvb_mux_notify(tdmi);
}
return NULL;
}
tdmi = calloc(1, sizeof(th_dvb_mux_instance_t));
if(identifier == NULL) {
char qpsktxt[20];
if(tda->tda_sat)
snprintf(qpsktxt, sizeof(qpsktxt), "_%s",
dvb_polarisation_to_str(dmc->dmc_polarisation));
else
qpsktxt[0] = 0;
snprintf(buf, sizeof(buf), "%s%d%s%s%s",
tda->tda_identifier, dmc->dmc_fe_params.frequency, qpsktxt,
dmc->dmc_satconf ? "_satconf_" : "",
dmc->dmc_satconf ? dmc->dmc_satconf->sc_id : "");
tdmi->tdmi_identifier = strdup(buf);
} else {
tdmi->tdmi_identifier = strdup(identifier);
}
c = RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp);
if(c != NULL) {
/* Global identifier collision, not good, not good at all */
tvhlog(LOG_ERR, "dvb",
"Multiple DVB multiplexes with same identifier \"%s\" "
"one is skipped", tdmi->tdmi_identifier);
free(tdmi->tdmi_identifier);
free(tdmi);
return NULL;
}
tdmi->tdmi_enabled = enabled;
TAILQ_INIT(&tdmi->tdmi_table_queue);
tdmi->tdmi_transport_stream_id = tsid;
tdmi->tdmi_adapter = tda;
tdmi->tdmi_network = network ? strdup(network) : NULL;
tdmi->tdmi_quality = 100;
memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf));
if(tdmi->tdmi_conf.dmc_satconf != NULL) {
LIST_INSERT_HEAD(&tdmi->tdmi_conf.dmc_satconf->sc_tdmis,
tdmi, tdmi_satconf_link);
}
LIST_INSERT_HEAD(&tda->tda_mux_hash[hash], tdmi, tdmi_adapter_hash_link);
LIST_INSERT_HEAD(&tda->tda_muxes, tdmi, tdmi_adapter_link);
if(source != NULL) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source);
dvb_mux_save(tdmi);
dvb_adapter_notify(tda);
}
dvb_transport_load(tdmi);
dvb_mux_notify(tdmi);
if(enabled) {
tda->tda_initial_num_mux++;
tdmi->tdmi_table_initial = 1;
mux_link_initial(tda, tdmi);
}
return tdmi;
}
/**
* Destroy a DVB mux (it might come back by itself very soon though :)
*/
void
dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_transport_t *t;
lock_assert(&global_lock);
hts_settings_remove("dvbmuxes/%s/%s",
tda->tda_identifier, tdmi->tdmi_identifier);
while((t = LIST_FIRST(&tdmi->tdmi_transports)) != NULL) {
hts_settings_remove("dvbtransports/%s/%s",
t->tht_dvb_mux_instance->tdmi_identifier,
t->tht_identifier);
transport_destroy(t);
}
dvb_transport_notify_by_adapter(tda);
if(tda->tda_mux_current == tdmi)
dvb_fe_stop(tda->tda_mux_current);
if(tdmi->tdmi_conf.dmc_satconf != NULL)
LIST_REMOVE(tdmi, tdmi_satconf_link);
RB_REMOVE(&dvb_muxes, tdmi, tdmi_global_link);
LIST_REMOVE(tdmi, tdmi_adapter_link);
LIST_REMOVE(tdmi, tdmi_adapter_hash_link);
if(tdmi->tdmi_scan_queue != NULL)
TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
if(tdmi->tdmi_table_initial)
tda->tda_initial_num_mux--;
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
free(tdmi->tdmi_network);
free(tdmi->tdmi_identifier);
free(tdmi);
dvb_adapter_notify(tda);
}
/**
*
*/
th_dvb_mux_instance_t *
dvb_mux_find_by_identifier(const char *identifier)
{
th_dvb_mux_instance_t skel;
lock_assert(&global_lock);
skel.tdmi_identifier = (char *)identifier;
return RB_FIND(&dvb_muxes, &skel, tdmi_global_link, tdmi_global_cmp);
}
#if DVB_API_VERSION >= 5
static struct strtab rollofftab[] = {
{ "ROLLOFF_35", ROLLOFF_35 },
{ "ROLLOFF_20", ROLLOFF_20 },
{ "ROLLOFF_25", ROLLOFF_25 },
{ "ROLLOFF_AUTO", ROLLOFF_AUTO }
};
static struct strtab delsystab[] = {
{ "SYS_UNDEFINED", SYS_UNDEFINED },
{ "SYS_DVBC_ANNEX_AC", SYS_DVBC_ANNEX_AC },
{ "SYS_DVBC_ANNEX_B", SYS_DVBC_ANNEX_B },
{ "SYS_DVBT", SYS_DVBT },
{ "SYS_DSS", SYS_DSS },
{ "SYS_DVBS", SYS_DVBS },
{ "SYS_DVBS2", SYS_DVBS2 },
{ "SYS_DVBH", SYS_DVBH },
{ "SYS_ISDBT", SYS_ISDBT },
{ "SYS_ISDBS", SYS_ISDBS },
{ "SYS_ISDBC", SYS_ISDBC },
{ "SYS_ATSC", SYS_ATSC },
{ "SYS_ATSCMH", SYS_ATSCMH },
{ "SYS_DMBTH", SYS_DMBTH },
{ "SYS_CMMB", SYS_CMMB },
{ "SYS_DAB", SYS_DAB }
};
#endif
static struct strtab fectab[] = {
{ "NONE", FEC_NONE },
{ "1/2", FEC_1_2 },
{ "2/3", FEC_2_3 },
{ "3/4", FEC_3_4 },
{ "4/5", FEC_4_5 },
{ "5/6", FEC_5_6 },
{ "6/7", FEC_6_7 },
{ "7/8", FEC_7_8 },
{ "8/9", FEC_8_9 },
{ "AUTO", FEC_AUTO },
#if DVB_API_VERSION >= 5
{ "3/5", FEC_3_5 },
{ "9/10", FEC_9_10 }
#endif
};
static struct strtab qamtab[] = {
{ "QPSK", QPSK },
{ "QAM16", QAM_16 },
{ "QAM32", QAM_32 },
{ "QAM64", QAM_64 },
{ "QAM128", QAM_128 },
{ "QAM256", QAM_256 },
{ "AUTO", QAM_AUTO },
{ "8VSB", VSB_8 },
{ "16VSB", VSB_16 },
#if DVB_API_VERSION >= 5
{ "PSK_8", PSK_8 },
{ "APSK_16", APSK_16 },
{ "APSK_32", APSK_32 },
{ "DQPSK", DQPSK }
#endif
};
static struct strtab bwtab[] = {
{ "8MHz", BANDWIDTH_8_MHZ },
{ "7MHz", BANDWIDTH_7_MHZ },
{ "6MHz", BANDWIDTH_6_MHZ },
{ "AUTO", BANDWIDTH_AUTO }
};
static struct strtab modetab[] = {
{ "2k", TRANSMISSION_MODE_2K },
{ "8k", TRANSMISSION_MODE_8K },
{ "AUTO", TRANSMISSION_MODE_AUTO }
};
static struct strtab guardtab[] = {
{ "1/32", GUARD_INTERVAL_1_32 },
{ "1/16", GUARD_INTERVAL_1_16 },
{ "1/8", GUARD_INTERVAL_1_8 },
{ "1/4", GUARD_INTERVAL_1_4 },
{ "AUTO", GUARD_INTERVAL_AUTO },
};
static struct strtab hiertab[] = {
{ "NONE", HIERARCHY_NONE },
{ "1", HIERARCHY_1 },
{ "2", HIERARCHY_2 },
{ "4", HIERARCHY_4 },
{ "AUTO", HIERARCHY_AUTO }
};
static struct strtab poltab[] = {
{ "Vertical", POLARISATION_VERTICAL },
{ "Horizontal", POLARISATION_HORIZONTAL },
{ "Left", POLARISATION_CIRCULAR_LEFT },
{ "Right", POLARISATION_CIRCULAR_RIGHT },
};
/**
*
*/
void
dvb_mux_save(th_dvb_mux_instance_t *tdmi)
{
struct dvb_frontend_parameters *f = &tdmi->tdmi_conf.dmc_fe_params;
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
htsmsg_add_u32(m, "enabled", tdmi->tdmi_enabled);
htsmsg_add_str(m, "status", dvb_mux_status(tdmi));
htsmsg_add_u32(m, "transportstreamid", tdmi->tdmi_transport_stream_id);
if(tdmi->tdmi_network != NULL)
htsmsg_add_str(m, "network", tdmi->tdmi_network);
htsmsg_add_u32(m, "frequency", f->frequency);
switch(tdmi->tdmi_adapter->tda_type) {
case FE_OFDM:
htsmsg_add_str(m, "bandwidth",
val2str(f->u.ofdm.bandwidth, bwtab));
htsmsg_add_str(m, "constellation",
val2str(f->u.ofdm.constellation, qamtab));
htsmsg_add_str(m, "transmission_mode",
val2str(f->u.ofdm.transmission_mode, modetab));
htsmsg_add_str(m, "guard_interval",
val2str(f->u.ofdm.guard_interval, guardtab));
htsmsg_add_str(m, "hierarchy",
val2str(f->u.ofdm.hierarchy_information, hiertab));
htsmsg_add_str(m, "fec_hi",
val2str(f->u.ofdm.code_rate_HP, fectab));
htsmsg_add_str(m, "fec_lo",
val2str(f->u.ofdm.code_rate_LP, fectab));
break;
case FE_QPSK:
htsmsg_add_u32(m, "symbol_rate", f->u.qpsk.symbol_rate);
htsmsg_add_str(m, "fec",
val2str(f->u.qpsk.fec_inner, fectab));
htsmsg_add_str(m, "polarisation",
val2str(tdmi->tdmi_conf.dmc_polarisation, poltab));
#if DVB_API_VERSION >= 5
htsmsg_add_str(m, "modulation",
val2str(tdmi->tdmi_conf.dmc_fe_modulation, qamtab));
htsmsg_add_str(m, "delivery_system",
val2str(tdmi->tdmi_conf.dmc_fe_delsys, delsystab));
htsmsg_add_str(m, "rolloff",
val2str(tdmi->tdmi_conf.dmc_fe_rolloff, rollofftab));
#endif
break;
case FE_QAM:
htsmsg_add_u32(m, "symbol_rate", f->u.qam.symbol_rate);
htsmsg_add_str(m, "fec",
val2str(f->u.qam.fec_inner, fectab));
htsmsg_add_str(m, "constellation",
val2str(f->u.qam.modulation, qamtab));
break;
case FE_ATSC:
htsmsg_add_str(m, "constellation",
val2str(f->u.vsb.modulation, qamtab));
break;
}
if(tdmi->tdmi_conf.dmc_satconf != NULL)
htsmsg_add_str(m, "satconf", tdmi->tdmi_conf.dmc_satconf->sc_id);
hts_settings_save(m, "dvbmuxes/%s/%s",
tdmi->tdmi_adapter->tda_identifier, tdmi->tdmi_identifier);
htsmsg_destroy(m);
}
/**
*
*/
static const char *
tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier)
{
th_dvb_mux_instance_t *tdmi;
struct dvb_mux_conf dmc;
const char *s;
int r;
unsigned int tsid, u32, enabled;
memset(&dmc, 0, sizeof(dmc));
dmc.dmc_fe_params.inversion = INVERSION_AUTO;
htsmsg_get_u32(m, "frequency", &dmc.dmc_fe_params.frequency);
switch(tda->tda_type) {
case FE_OFDM:
s = htsmsg_get_str(m, "bandwidth");
if(s == NULL || (r = str2val(s, bwtab)) < 0)
return "Invalid bandwidth";
dmc.dmc_fe_params.u.ofdm.bandwidth = r;
s = htsmsg_get_str(m, "constellation");
if(s == NULL || (r = str2val(s, qamtab)) < 0)
return "Invalid QAM constellation";
dmc.dmc_fe_params.u.ofdm.constellation = r;
s = htsmsg_get_str(m, "transmission_mode");
if(s == NULL || (r = str2val(s, modetab)) < 0)
return "Invalid transmission mode";
dmc.dmc_fe_params.u.ofdm.transmission_mode = r;
s = htsmsg_get_str(m, "guard_interval");
if(s == NULL || (r = str2val(s, guardtab)) < 0)
return "Invalid guard interval";
dmc.dmc_fe_params.u.ofdm.guard_interval = r;
s = htsmsg_get_str(m, "hierarchy");
if(s == NULL || (r = str2val(s, hiertab)) < 0)
return "Invalid heirarchy information";
dmc.dmc_fe_params.u.ofdm.hierarchy_information = r;
s = htsmsg_get_str(m, "fec_hi");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid hi-FEC";
dmc.dmc_fe_params.u.ofdm.code_rate_HP = r;
s = htsmsg_get_str(m, "fec_lo");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid lo-FEC";
dmc.dmc_fe_params.u.ofdm.code_rate_LP = r;
break;
case FE_QPSK:
htsmsg_get_u32(m, "symbol_rate", &dmc.dmc_fe_params.u.qpsk.symbol_rate);
if(dmc.dmc_fe_params.u.qpsk.symbol_rate == 0)
return "Invalid symbol rate";
s = htsmsg_get_str(m, "fec");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid FEC";
dmc.dmc_fe_params.u.qpsk.fec_inner = r;
s = htsmsg_get_str(m, "polarisation");
if(s == NULL || (r = str2val(s, poltab)) < 0)
return "Invalid polarisation";
dmc.dmc_polarisation = r;
#if DVB_API_VERSION >= 5
s = htsmsg_get_str(m, "modulation");
if(s == NULL || (r = str2val(s, qamtab)) < 0) {
r = str2val("QPSK", qamtab);
tvhlog(LOG_INFO,
"dvb", "no modulation for mux found, defaulting to QPSK");
}
dmc.dmc_fe_modulation = r;
s = htsmsg_get_str(m, "delivery_system");
if(s == NULL || (r = str2val(s, delsystab)) < 0) {
r = str2val("SYS_DVBS", delsystab);
tvhlog(LOG_INFO,
"dvb", "no delivery system for mux found, defaulting to SYS_DVBS");
}
dmc.dmc_fe_delsys = r;
s = htsmsg_get_str(m, "rolloff");
if(s == NULL || (r = str2val(s, rollofftab)) < 0) {
r = str2val("ROLLOFF_35", rollofftab);
tvhlog(LOG_INFO,
"dvb", "no rolloff for mux found, defaulting to ROLLOFF_35");
}
dmc.dmc_fe_rolloff = r;
#endif
break;
case FE_QAM:
htsmsg_get_u32(m, "symbol_rate", &dmc.dmc_fe_params.u.qam.symbol_rate);
if(dmc.dmc_fe_params.u.qam.symbol_rate == 0)
return "Invalid symbol rate";
s = htsmsg_get_str(m, "constellation");
if(s == NULL || (r = str2val(s, qamtab)) < 0)
return "Invalid QAM constellation";
dmc.dmc_fe_params.u.qam.modulation = r;
s = htsmsg_get_str(m, "fec");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid FEC";
dmc.dmc_fe_params.u.qam.fec_inner = r;
break;
case FE_ATSC:
s = htsmsg_get_str(m, "constellation");
if(s == NULL || (r = str2val(s, qamtab)) < 0)
return "Invalid VSB constellation";
dmc.dmc_fe_params.u.vsb.modulation = r;
break;
}
if(htsmsg_get_u32(m, "transportstreamid", &tsid))
tsid = 0xffff;
if(htsmsg_get_u32(m, "enabled", &enabled))
enabled = 1;
if((s = htsmsg_get_str(m, "satconf")) != NULL)
dmc.dmc_satconf = dvb_satconf_entry_find(tda, s, 0);
else
dmc.dmc_satconf = NULL;
tdmi = dvb_mux_create(tda, &dmc,
tsid, htsmsg_get_str(m, "network"), NULL, enabled,
identifier);
if(tdmi != NULL) {
if((s = htsmsg_get_str(m, "status")) != NULL)
tdmi->tdmi_fe_status = str2val(s, muxfestatustab);
if(!htsmsg_get_u32(m, "quality", &u32))
tdmi->tdmi_quality = u32;
}
return NULL;
}
/**
*
*/
void
dvb_mux_load(th_dvb_adapter_t *tda)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
if((l = hts_settings_load("dvbmuxes/%s", tda->tda_identifier)) == NULL)
return;
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
tdmi_create_by_msg(tda, c, f->hmf_name);
}
htsmsg_destroy(l);
}
/**
*
*/
void
dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname)
{
htsmsg_t *m;
free(tdmi->tdmi_network);
tdmi->tdmi_network = strdup(networkname);
dvb_mux_save(tdmi);
m = htsmsg_create_map();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_str(m, "network", tdmi->tdmi_network ?: "");
notify_by_msg("dvbMux", m);
}
/**
*
*/
void
dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid)
{
htsmsg_t *m;
tdmi->tdmi_transport_stream_id = tsid;
dvb_mux_save(tdmi);
m = htsmsg_create_map();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "muxid", tdmi->tdmi_transport_stream_id);
notify_by_msg("dvbMux", m);
}
/**
*
*/
static void
tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
if(tdmi->tdmi_enabled == enabled)
return;
if(tdmi->tdmi_enabled) {
if(tdmi->tdmi_scan_queue != NULL) {
TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
tdmi->tdmi_scan_queue = NULL;
}
}
tdmi->tdmi_enabled = enabled;
if(enabled)
mux_link_initial(tda, tdmi);
}
/**
* Configurable by user
*/
void
dvb_mux_set_enable(th_dvb_mux_instance_t *tdmi, int enabled)
{
tdmi_set_enable(tdmi, enabled);
dvb_mux_save(tdmi);
}
/**
*
*/
static void
dvb_mux_modulation(char *buf, size_t size, th_dvb_mux_instance_t *tdmi)
{
struct dvb_frontend_parameters *f = &tdmi->tdmi_conf.dmc_fe_params;
switch(tdmi->tdmi_adapter->tda_type) {
case FE_OFDM:
snprintf(buf, size, "%s, %s, %s-mode",
val2str(f->u.ofdm.constellation, qamtab),
val2str(f->u.ofdm.bandwidth, bwtab),
val2str(f->u.ofdm.transmission_mode, modetab));
break;
case FE_QPSK:
#if DVB_API_VERSION >= 5
snprintf(buf, size, "%d kBaud, %s, %s", f->u.qpsk.symbol_rate / 1000,
val2str(tdmi->tdmi_conf.dmc_fe_delsys, delsystab),
val2str(tdmi->tdmi_conf.dmc_fe_modulation, qamtab));
#else
snprintf(buf, size, "%d kBaud", f->u.qpsk.symbol_rate / 1000);
#endif
break;
case FE_QAM:
snprintf(buf, size, "%s, %d kBaud",
val2str(f->u.qam.modulation, qamtab),
f->u.qpsk.symbol_rate / 1000);
break;
case FE_ATSC:
snprintf(buf, size, "%s", val2str(f->u.vsb.modulation, qamtab));
break;
default:
snprintf(buf, size, "Unknown");
break;
}
}
/**
*
*/
htsmsg_t *
dvb_mux_build_msg(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create_map();
char buf[100];
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "enabled", tdmi->tdmi_enabled);
htsmsg_add_str(m, "network", tdmi->tdmi_network ?: "");
dvb_mux_nicefreq(buf, sizeof(buf), tdmi);
htsmsg_add_str(m, "freq", buf);
dvb_mux_modulation(buf, sizeof(buf), tdmi);
htsmsg_add_str(m, "mod", buf);
htsmsg_add_str(m, "pol",
dvb_polarisation_to_str_long(tdmi->tdmi_conf.dmc_polarisation));
if(tdmi->tdmi_conf.dmc_satconf != NULL)
htsmsg_add_str(m, "satconf", tdmi->tdmi_conf.dmc_satconf->sc_id);
if(tdmi->tdmi_transport_stream_id != 0xffff)
htsmsg_add_u32(m, "muxid", tdmi->tdmi_transport_stream_id);
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
return m;
}
/**
*
*/
void
dvb_mux_notify(th_dvb_mux_instance_t *tdmi)
{
notify_by_msg("dvbMux", dvb_mux_build_msg(tdmi));
}
/**
*
*/
const char *
dvb_mux_add_by_params(th_dvb_adapter_t *tda,
int freq,
int symrate,
int bw,
int constellation,
int delsys,
int tmode,
int guard,
int hier,
int fechi,
int feclo,
int fec,
int polarisation,
const char *satconf)
{
th_dvb_mux_instance_t *tdmi;
struct dvb_mux_conf dmc;
memset(&dmc, 0, sizeof(dmc));
dmc.dmc_fe_params.inversion = INVERSION_AUTO;
switch(tda->tda_type) {
case FE_OFDM:
dmc.dmc_fe_params.frequency = freq * 1000;
if(!val2str(bw, bwtab))
return "Invalid bandwidth";
if(!val2str(constellation, qamtab))
return "Invalid QAM constellation";
if(!val2str(tmode, modetab))
return "Invalid transmission mode";
if(!val2str(guard, guardtab))
return "Invalid guard interval";
if(!val2str(hier, hiertab))
return "Invalid hierarchy";
if(!val2str(fechi, fectab))
return "Invalid FEC Hi";
if(!val2str(feclo, fectab))
return "Invalid FEC Lo";
dmc.dmc_fe_params.u.ofdm.bandwidth = bw;
dmc.dmc_fe_params.u.ofdm.constellation = constellation;
dmc.dmc_fe_params.u.ofdm.transmission_mode = tmode;
dmc.dmc_fe_params.u.ofdm.guard_interval = guard;
dmc.dmc_fe_params.u.ofdm.hierarchy_information = hier;
dmc.dmc_fe_params.u.ofdm.code_rate_HP = fechi;
dmc.dmc_fe_params.u.ofdm.code_rate_LP = feclo;
polarisation = 0;
break;
case FE_QAM:
dmc.dmc_fe_params.frequency = freq * 1000;
if(!val2str(constellation, qamtab))
return "Invalid QAM constellation";
if(!val2str(fec, fectab))
return "Invalid FEC";
dmc.dmc_fe_params.u.qam.symbol_rate = symrate;
dmc.dmc_fe_params.u.qam.modulation = constellation;
dmc.dmc_fe_params.u.qam.fec_inner = fec;
polarisation = 0;
break;
case FE_QPSK:
dmc.dmc_fe_params.frequency = freq;
if(!val2str(fec, fectab))
return "Invalid FEC";
if(!val2str(polarisation, poltab))
return "Invalid polarisation";
dmc.dmc_fe_params.u.qpsk.symbol_rate = symrate;
dmc.dmc_fe_params.u.qpsk.fec_inner = fec;
#if DVB_API_VERSION >= 5
if(!val2str(constellation, qamtab))
return "Invalid QPSK constellation";
if(!val2str(delsys, delsystab))
return "Invalid delivery system";
dmc.dmc_fe_delsys = delsys;
dmc.dmc_fe_modulation = constellation;
#endif
break;
case FE_ATSC:
dmc.dmc_fe_params.frequency = freq;
if(!val2str(constellation, qamtab))
return "Invalid VSB constellation";
dmc.dmc_fe_params.u.vsb.modulation = constellation;
break;
}
if(satconf != NULL) {
dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0);
if(dmc.dmc_satconf == NULL)
return "Satellite configuration not found";
} else {
dmc.dmc_satconf = NULL;
}
dmc.dmc_polarisation = polarisation;
tdmi = dvb_mux_create(tda, &dmc, 0xffff, NULL, NULL, 1, NULL);
if(tdmi == NULL)
return "Mux already exist";
return NULL;
}
/**
*
*/
int
dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src)
{
th_dvb_mux_instance_t *tdmi_dst;
th_transport_t *t_src, *t_dst;
th_stream_t *st_src, *st_dst;
tdmi_dst = dvb_mux_create(dst,
&tdmi_src->tdmi_conf,
tdmi_src->tdmi_transport_stream_id,
tdmi_src->tdmi_network,
"copy operation", tdmi_src->tdmi_enabled,
NULL);
if(tdmi_dst == NULL)
return -1; // Already exist
LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, tht_group_link) {
t_dst = dvb_transport_find(tdmi_dst,
t_src->tht_dvb_service_id,
t_src->tht_pmt_pid, NULL);
t_dst->tht_pcr_pid = t_src->tht_pcr_pid;
t_dst->tht_enabled = t_src->tht_enabled;
t_dst->tht_servicetype = t_src->tht_servicetype;
t_dst->tht_scrambled = t_src->tht_scrambled;
if(t_src->tht_provider != NULL)
t_dst->tht_provider = strdup(t_src->tht_provider);
if(t_src->tht_svcname != NULL)
t_dst->tht_svcname = strdup(t_src->tht_svcname);
if(t_src->tht_ch != NULL)
transport_map_channel(t_dst, t_src->tht_ch, 0);
pthread_mutex_lock(&t_src->tht_stream_mutex);
pthread_mutex_lock(&t_dst->tht_stream_mutex);
LIST_FOREACH(st_src, &t_src->tht_components, st_link) {
st_dst = transport_stream_create(t_dst,
st_src->st_pid,
st_src->st_type);
st_dst->st_tb = (AVRational){1, 90000};
memcpy(st_dst->st_lang, st_src->st_lang, 4);
st_dst->st_frame_duration = st_src->st_frame_duration;
st_dst->st_caid = st_src->st_caid;
}
pthread_mutex_unlock(&t_dst->tht_stream_mutex);
pthread_mutex_unlock(&t_src->tht_stream_mutex);
t_dst->tht_config_save(t_dst); // Save config
}
dvb_mux_save(tdmi_dst);
return 0;
}