Clean up the dvb.c and move stuff into separate files.

This commit is contained in:
Andreas Öman 2008-08-25 16:55:27 +00:00
parent 9a6a0b8419
commit 3ef0bc729b
10 changed files with 1217 additions and 1161 deletions

View file

@ -15,7 +15,7 @@ SRCS += epg.c epg_xmltv.c
VPATH += dvb
SRCS += dvb.c dvb_support.c dvb_dvr.c dvb_muxconfig.c dvb_fe.c dvb_tables.c \
diseqc.c
diseqc.c dvb_adapter.c dvb_multiplex.c dvb_transport.c
SRCS += iptv_input.c iptv_output.c

779
dvb/dvb.c
View file

@ -31,789 +31,12 @@
#include <string.h>
#include <dirent.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <libhts/htssettings.h>
#include "tvhead.h"
#include "dispatch.h"
#include "dvb.h"
#include "channels.h"
#include "transports.h"
#include "subscriptions.h"
#include "teletext.h"
#include "epg.h"
#include "psi.h"
#include "dvb_support.h"
#include "dvb_dvr.h"
#include "dvb_muxconfig.h"
#include "notify.h"
struct th_dvb_adapter_queue dvb_adapters;
struct th_dvb_mux_instance_tree dvb_muxes;
static void dvb_mux_scanner(void *aux, int64_t now);
static void dvb_fec_monitor(void *aux, int64_t now);
static void dvb_transport_save(th_transport_t *t);
static const char *dvb_source_name(th_transport_t *t);
static int dvb_transport_quality(th_transport_t *t);
static th_dvb_adapter_t *
tda_alloc(void)
{
th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t));
pthread_mutex_init(&tda->tda_lock, NULL);
pthread_cond_init(&tda->tda_cond, NULL);
TAILQ_INIT(&tda->tda_fe_cmd_queue);
return tda;
}
static void
dvb_add_adapter(const char *path)
{
char fname[256];
int fe, i, r;
th_dvb_adapter_t *tda;
char buf[400];
snprintf(fname, sizeof(fname), "%s/frontend0", path);
fe = open(fname, O_RDWR | O_NONBLOCK);
if(fe == -1) {
if(errno != ENOENT)
tvhlog(LOG_ALERT, "dvb",
"Unable to open %s -- %s\n", fname, strerror(errno));
return;
}
tda = tda_alloc();
tda->tda_rootpath = strdup(path);
tda->tda_demux_path = malloc(256);
snprintf(tda->tda_demux_path, 256, "%s/demux0", path);
tda->tda_dvr_path = malloc(256);
snprintf(tda->tda_dvr_path, 256, "%s/dvr0", path);
tda->tda_fe_fd = fe;
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
if(ioctl(tda->tda_fe_fd, FE_GET_INFO, tda->tda_fe_info)) {
tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter\n", fname);
close(fe);
free(tda);
return;
}
tda->tda_type = tda->tda_fe_info->type;
if(dvb_dvr_init(tda) < 0) {
close(fe);
free(tda);
return;
}
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
tda->tda_fe_info->name);
r = strlen(buf);
for(i = 0; i < r; i++)
if(!isalnum((int)buf[i]))
buf[i] = '_';
tda->tda_identifier = strdup(buf);
/* Come up with an initial displayname, user can change it and it will
be overridden by any stored settings later on */
tda->tda_displayname = strdup(tda->tda_fe_info->name);
tvhlog(LOG_INFO, "dvb",
"Found adapter %s (%s)", path, tda->tda_fe_info->name);
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
dvb_fe_start(tda);
}
void
dvb_init(void)
{
char path[200];
htsmsg_t *l, *c;
htsmsg_field_t *f;
const char *name, *s;
int i, type;
th_dvb_adapter_t *tda;
TAILQ_INIT(&dvb_adapters);
for(i = 0; i < 32; i++) {
snprintf(path, sizeof(path), "/dev/dvb/adapter%d", i);
dvb_add_adapter(path);
}
l = hts_settings_load("dvbadapters");
if(l != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_msg_by_field(f)) == NULL)
continue;
if(dvb_adapter_find_by_identifier(f->hmf_name) != NULL) {
/* Already loaded */
continue;
}
if((name = htsmsg_get_str(c, "displayname")) == NULL)
continue;
if((s = htsmsg_get_str(c, "type")) == NULL ||
(type = dvb_str_to_adaptertype(s)) < 0)
continue;
tda = tda_alloc();
tda->tda_identifier = strdup(f->hmf_name);
tda->tda_displayname = strdup(name);
tda->tda_type = type;
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
}
htsmsg_destroy(l);
}
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
dvb_tdmi_load(tda);
dvb_adapter_init();
}
/**
* Find a transport based on 'serviceid' on the given mux
*
* If it cannot be found we create it if 'pmt_pid' is also set
*/
th_transport_t *
dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
int *created)
{
th_transport_t *t;
char tmp[200];
if(created != NULL)
*created = 0;
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
if(t->tht_dvb_service_id == sid)
return t;
}
if(pmt_pid == 0)
return NULL;
if(created != NULL)
*created = 1;
snprintf(tmp, sizeof(tmp), "%s_%04x", tdmi->tdmi_identifier, sid);
t = transport_create(tmp, TRANSPORT_DVB, THT_MPEG_TS);
t->tht_dvb_service_id = sid;
t->tht_pmt = pmt_pid;
t->tht_start_feed = dvb_start_feed;
t->tht_stop_feed = dvb_stop_feed;
t->tht_config_change = dvb_transport_save;
t->tht_sourcename = dvb_source_name;
t->tht_dvb_mux_instance = tdmi;
t->tht_quality_index = dvb_transport_quality;
LIST_INSERT_HEAD(&tdmi->tdmi_transports, t, tht_mux_link);
return t;
}
/**
*
*/
static void
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "quality", 100 + tdmi->tdmi_quality * 2);
notify_by_msg("dvbmux", m);
}
/**
*
*/
static void
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_str(m, "status", tdmi->tdmi_last_status);
notify_by_msg("dvbmux", m);
}
/**
*
*/
static void
dvb_adapter_notify_reload(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tda->tda_identifier);
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("dvbadapter", m);
}
/**
*
*/
static void
dvb_fec_monitor(void *aux, int64_t now)
{
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
int i, v, vv;
const char *s;
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
tdmi = tda->tda_mux_current;
if(tdmi == NULL)
return;
if(tdmi->tdmi_status == NULL) {
v = vv = 0;
for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++) {
if(tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT)
v++;
vv += tdmi->tdmi_fec_err_histogram[i];
}
vv /= TDMI_FEC_ERR_HISTOGRAM_SIZE;
if(v == TDMI_FEC_ERR_HISTOGRAM_SIZE) {
if(LIST_FIRST(&tda->tda_transports) != NULL) {
tvhlog(LOG_ERR, "dvb",
"\"%s\": Constant rate of FEC errors (average at %d / s), "
"last %d seconds, flushing subscribers\n",
tdmi->tdmi_identifier, vv,
TDMI_FEC_ERR_HISTOGRAM_SIZE);
dvb_adapter_clean(tdmi->tdmi_adapter);
}
}
}
if(dvb_mux_status(tdmi, 1) != NULL) {
if(tdmi->tdmi_quality > -50) {
tdmi->tdmi_quality--;
dvb_notify_mux_quality(tdmi);
}
} else {
if(tdmi->tdmi_quality < 0) {
tdmi->tdmi_quality++;
dvb_notify_mux_quality(tdmi);
}
}
s = dvb_mux_status(tdmi, 0);
if(s != tdmi->tdmi_last_status) {
tdmi->tdmi_last_status = s;
dvb_notify_mux_status(tdmi);
}
}
/**
* If nobody is subscribing, cycle thru all muxes to get some stats
* and EIT updates
*/
static void
dvb_mux_scanner(void *aux, int64_t now)
{
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
dtimer_arm(&tda->tda_mux_scanner_timer, dvb_mux_scanner, tda, 10);
if(transport_compute_weight(&tda->tda_transports) > 0)
return; /* someone is here */
/* Check if we have muxes pending for quickscan, if so, choose them */
if((tdmi = RB_FIRST(&tda->tda_muxes_qscan_waiting)) != NULL) {
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_RUNNING;
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
return;
}
/* otherwise, just rotate */
tdmi = tda->tda_mux_current;
if(tdmi != NULL)
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_NONE;
tdmi = tdmi != NULL ? RB_NEXT(tdmi, tdmi_adapter_link) : NULL;
tdmi = tdmi != NULL ? tdmi : RB_FIRST(&tda->tda_muxes);
if(tdmi == NULL)
return; /* no instances */
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
}
/**
*
*/
static int
tdmi_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b)
{
if(a->tdmi_switchport != b->tdmi_switchport)
return a->tdmi_switchport - b->tdmi_switchport;
if(a->tdmi_fe_params.frequency != b->tdmi_fe_params.frequency)
return a->tdmi_fe_params.frequency - b->tdmi_fe_params.frequency;
return a->tdmi_polarisation - b->tdmi_polarisation;
}
/**
*
*/
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);
}
/**
* 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, struct dvb_frontend_parameters *fe_param,
int polarisation, int switchport,
uint16_t tsid, const char *network, const char *source)
{
th_dvb_mux_instance_t *tdmi;
static th_dvb_mux_instance_t *skel;
char buf[200];
char qpsktxt[20];
int entries_before = tda->tda_muxes.entries;
if(skel == NULL)
skel = calloc(1, sizeof(th_dvb_mux_instance_t));
skel->tdmi_polarisation = polarisation;
skel->tdmi_switchport = switchport;
skel->tdmi_fe_params.frequency = fe_param->frequency;
tdmi = RB_INSERT_SORTED(&tda->tda_muxes, skel, tdmi_adapter_link, tdmi_cmp);
if(tdmi != NULL)
return NULL;
tdmi = skel;
skel = NULL;
tdmi->tdmi_refcnt = 1;
RB_INSERT_SORTED(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link,
tdmi_cmp);
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_WAITING;
pthread_mutex_init(&tdmi->tdmi_table_lock, NULL);
tdmi->tdmi_state = TDMI_IDLE;
tdmi->tdmi_transport_stream_id = tsid;
tdmi->tdmi_adapter = tda;
tdmi->tdmi_network = network ? strdup(network) : NULL;
if(entries_before == 0 && tda->tda_rootpath != NULL) {
/* First mux on adapter with backing hardware, start scanner */
dtimer_arm(&tda->tda_mux_scanner_timer, dvb_mux_scanner, tda, 1);
}
memcpy(&tdmi->tdmi_fe_params, fe_param,
sizeof(struct dvb_frontend_parameters));
if(tda->tda_type == FE_QPSK)
snprintf(qpsktxt, sizeof(qpsktxt), "_%s_%d",
dvb_polarisation_to_str(polarisation), switchport);
else
qpsktxt[0] = 0;
snprintf(buf, sizeof(buf), "%s%d%s",
tda->tda_identifier,fe_param->frequency, qpsktxt);
tdmi->tdmi_identifier = strdup(buf);
RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp);
if(source != NULL) {
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source);
dvb_tdmi_save(tdmi);
dvb_adapter_notify_reload(tda);
}
return tdmi;
}
/**
* Unref a TDMI and optionally free it
*/
void
dvb_mux_unref(th_dvb_mux_instance_t *tdmi)
{
if(tdmi->tdmi_refcnt > 1) {
tdmi->tdmi_refcnt--;
return;
}
free(tdmi->tdmi_network);
free(tdmi->tdmi_identifier);
free(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;
char buf[400];
snprintf(buf, sizeof(buf), "%s/dvbmuxes/%s",
settings_dir, tdmi->tdmi_identifier);
unlink(buf);
while((t = LIST_FIRST(&tdmi->tdmi_transports)) != NULL)
transport_destroy(t);
if(tda->tda_mux_current == tdmi)
tdmi_stop(tda->tda_mux_current);
dtimer_disarm(&tdmi->tdmi_initial_scan_timer);
RB_REMOVE(&dvb_muxes, tdmi, tdmi_global_link);
RB_REMOVE(&tda->tda_muxes, tdmi, tdmi_adapter_link);
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_WAITING)
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
pthread_mutex_lock(&tda->tda_lock);
dvb_fe_flush(tdmi);
dvb_mux_unref(tdmi);
pthread_mutex_unlock(&tda->tda_lock);
}
#if 0
/**
* Load config for the given adapter
*/
static int
dvb_tda_load(th_dvb_adapter_t *tda)
{
struct config_head cl;
config_entry_t *ce;
char buf[400];
const char *v;
snprintf(buf, sizeof(buf), "%s/dvbadapters/%s",
settings_dir, tda->tda_identifier);
TAILQ_INIT(&cl);
if(config_read_file0(buf, &cl))
return 0;
if((v = config_get_str_sub(&cl, "type", NULL)) == NULL)
goto err;
if((tda->tda_type = dvb_str_to_adaptertype(v)) < 0)
goto err;
if((v = config_get_str_sub(&cl, "displayname", NULL)) == NULL)
goto err;
free(tda->tda_displayname);
tda->tda_displayname = strdup(v);
TAILQ_FOREACH(ce, &cl, ce_link) {
if(ce->ce_type != CFG_SUB || strcasecmp("mux", ce->ce_key))
continue;
v = dvb_mux_create_str(tda,
config_get_str_sub(&ce->ce_sub,
"transportstreamid", NULL),
config_get_str_sub(&ce->ce_sub,
"network", NULL),
config_get_str_sub(&ce->ce_sub,
"frequency", NULL),
config_get_str_sub(&ce->ce_sub,
"symbol_rate", NULL),
config_get_str_sub(&ce->ce_sub,
"constellation", NULL),
config_get_str_sub(&ce->ce_sub,
"fec", NULL),
config_get_str_sub(&ce->ce_sub,
"fec_hi", NULL),
config_get_str_sub(&ce->ce_sub,
"fec_lo", NULL),
config_get_str_sub(&ce->ce_sub,
"bandwidth", NULL),
config_get_str_sub(&ce->ce_sub,
"transmission_mode", NULL),
config_get_str_sub(&ce->ce_sub,
"guard_interval", NULL),
config_get_str_sub(&ce->ce_sub,
"hierarchy", NULL),
config_get_str_sub(&ce->ce_sub,
"polarisation", NULL),
config_get_str_sub(&ce->ce_sub,
"switchport", NULL),
0);
if(v != NULL)
tvhlog(LOG_ALERT, "dvb",
"Unable to init saved mux on %s -- %s\n",
tda->tda_identifier, v);
}
config_free0(&cl);
return 0;
err:
config_free0(&cl);
return -1;
}
#endif
static void
dvb_transport_save(th_transport_t *t)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_u32(m, "service_id", t->tht_dvb_service_id);
htsmsg_add_u32(m, "pmt", t->tht_pmt);
htsmsg_add_u32(m, "stype", t->tht_servicetype);
htsmsg_add_u32(m, "scrambled", t->tht_scrambled);
if(t->tht_provider != NULL)
htsmsg_add_str(m, "provider", t->tht_provider);
if(t->tht_svcname != NULL)
htsmsg_add_str(m, "servicename", t->tht_svcname);
if(t->tht_chname != NULL)
htsmsg_add_str(m, "channelname", t->tht_chname);
htsmsg_add_u32(m, "mapped", !!t->tht_ch);
psi_get_transport_settings(m, t);
hts_settings_save(m, "dvbtransports/%s", t->tht_identifier);
htsmsg_destroy(m);
}
/**
* Called to get quality for the given transport
*
* We keep track of this for the entire mux (if we see errors), soo..
* return that value
*/
static int
dvb_transport_quality(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
return tdmi->tdmi_quality;
}
/**
* Generate a descriptive name for the source
*/
static const char *
dvb_source_name(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi;
static char buf[200];
tdmi = t->tht_dvb_mux_instance;
snprintf(buf, sizeof(buf), "\"%s\" on \"%s\"",
tdmi->tdmi_network ?: "Unknown network",
tdmi->tdmi_adapter->tda_rootpath);
return buf;
}
/**
*
*/
void
dvb_tda_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
{
th_dvb_mux_instance_t *tdmi_src, *tdmi_dst;
th_transport_t *t_src, *t_dst;
th_stream_t *st_src, *st_dst;
while((tdmi_dst = RB_FIRST(&dst->tda_muxes)) != NULL)
dvb_mux_destroy(tdmi_dst);
RB_FOREACH(tdmi_src, &src->tda_muxes, tdmi_adapter_link) {
tdmi_dst = dvb_mux_create(dst,
&tdmi_src->tdmi_fe_params,
tdmi_src->tdmi_polarisation,
tdmi_src->tdmi_switchport,
tdmi_src->tdmi_transport_stream_id,
tdmi_src->tdmi_network,
"copy operation");
assert(tdmi_dst != NULL);
LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, tht_mux_link) {
t_dst = dvb_find_transport(tdmi_dst,
t_src->tht_dvb_service_id,
t_src->tht_pmt,
NULL);
t_dst->tht_pcr_pid = t_src->tht_pcr_pid;
t_dst->tht_disabled = t_src->tht_disabled;
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_chname != NULL)
t_dst->tht_chname = strdup(t_src->tht_chname);
if(t_src->tht_ch != NULL)
transport_map_channel(t_dst, t_src->tht_ch);
LIST_FOREACH(st_src, &t_src->tht_streams, st_link) {
st_dst = transport_add_stream(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;
}
}
dvb_tdmi_save(tdmi_dst);
}
dvb_tda_save(dst);
}
/**
*
*/
int
dvb_tda_destroy(th_dvb_adapter_t *tda)
{
th_dvb_mux_instance_t *tdmi;
char buf[400];
if(tda->tda_rootpath != NULL)
return -1;
snprintf(buf, sizeof(buf), "%s/dvbadapters/%s",
settings_dir, tda->tda_identifier);
unlink(buf);
while((tdmi = RB_FIRST(&tda->tda_muxes)) != NULL)
dvb_mux_destroy(tdmi);
TAILQ_REMOVE(&dvb_adapters, tda, tda_global_link);
free(tda->tda_identifier);
free(tda->tda_displayname);
free(tda);
return 0;
}
/**
*
*/
void
dvb_tdmi_fastswitch(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt;
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_NONE)
return;
pthread_mutex_lock(&tdmi->tdmi_table_lock);
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) {
if(tdt->tdt_quickreq && tdt->tdt_count == 0)
break;
}
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
if(tdt != NULL)
return; /* Still tables we've not seen */
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_NONE;
dvb_mux_scanner(tdmi->tdmi_adapter, 0);
}
/**
*
*/
th_dvb_mux_instance_t *
dvb_mux_find_by_identifier(const char *identifier)
{
th_dvb_mux_instance_t skel;
skel.tdmi_identifier = (char *)identifier;
return RB_FIND(&dvb_muxes, &skel, tdmi_global_link, tdmi_global_cmp);
}

View file

@ -33,9 +33,66 @@ extern struct th_dvb_mux_instance_tree dvb_muxes;
void dvb_init(void);
/**
* DVB Adapter
*/
void dvb_adapter_init(void);
void dvb_adapter_mux_scanner(void *aux, int64_t now);
void dvb_adapter_notify_reload(th_dvb_adapter_t *tda);
void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s);
void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src);
int dvb_adapter_destroy(th_dvb_adapter_t *tda);
/**
* DVB Multiplex
*/
void dvb_mux_save(th_dvb_mux_instance_t *tdmi);
void dvb_mux_load(th_dvb_adapter_t *tda);
void dvb_mux_unref(th_dvb_mux_instance_t *tdmi);
void dvb_mux_destroy(th_dvb_mux_instance_t *tdmi);
void dvb_mux_fastswitch(th_dvb_mux_instance_t *tdmi);
th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
struct dvb_frontend_parameters *fe_param,
int polarisation, int switchport,
uint16_t tsid, const char *network,
const char *logprefix);
void dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog,
tdmi_state_t state);
/**
* DVB Transport (aka DVB service)
*/
th_transport_t *dvb_transport_find(th_dvb_mux_instance_t *tdmi,
uint16_t sid, int pmt_pid, int *created);
void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi);
void tdmi_stop(th_dvb_mux_instance_t *tdmi);
/**
* DVB Frontend
*/
void dvb_fe_start(th_dvb_adapter_t *tda);
void dvb_fe_flush(th_dvb_mux_instance_t *tdmi);
/**
* DVB Tables
*/
void dvb_table_add_default(th_dvb_mux_instance_t *tdmi);
void dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
@ -43,32 +100,4 @@ void dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
void dvb_tdt_destroy(th_dvb_table_t *tdt);
void dvb_fe_start(th_dvb_adapter_t *tda);
void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi);
th_transport_t *dvb_find_transport(th_dvb_mux_instance_t *tdmi,
uint16_t sid, int pmt_pid, int *created);
th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
struct dvb_frontend_parameters *fe_param,
int polarisation, int switchport,
uint16_t tsid, const char *network,
const char *logprefix);
void dvb_mux_unref(th_dvb_mux_instance_t *tdmi);
void dvb_fe_flush(th_dvb_mux_instance_t *tdmi);
void dvb_mux_destroy(th_dvb_mux_instance_t *tdmi);
void tdmi_stop(th_dvb_mux_instance_t *tdmi);
void dvb_tda_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src);
int dvb_tda_destroy(th_dvb_adapter_t *tda);
void dvb_tdmi_fastswitch(th_dvb_mux_instance_t *tdmi);
#endif /* DVB_H_ */

478
dvb/dvb_adapter.c Normal file
View file

@ -0,0 +1,478 @@
/*
* 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 <libhts/htssettings.h>
#include "tvhead.h"
#include "dispatch.h"
#include "dvb.h"
#include "channels.h"
#include "transports.h"
#include "subscriptions.h"
#include "teletext.h"
#include "epg.h"
#include "psi.h"
#include "dvb_support.h"
#include "dvb_dvr.h"
#include "dvb_muxconfig.h"
#include "notify.h"
struct th_dvb_adapter_queue dvb_adapters;
struct th_dvb_mux_instance_tree dvb_muxes;
static void dvb_fec_monitor(void *aux, int64_t now);
/**
*
*/
static th_dvb_adapter_t *
tda_alloc(void)
{
th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t));
pthread_mutex_init(&tda->tda_lock, NULL);
pthread_cond_init(&tda->tda_cond, NULL);
TAILQ_INIT(&tda->tda_fe_cmd_queue);
return tda;
}
/**
* Save config for the given adapter
*/
static void
tda_save(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
htsmsg_add_str(m, "displayname", tda->tda_displayname);
hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier);
htsmsg_destroy(m);
}
/**
*
*/
void
dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tda->tda_identifier);
free(tda->tda_displayname);
tda->tda_displayname = strdup(s);
tda_save(tda);
htsmsg_add_str(m, "name", tda->tda_displayname);
notify_by_msg("dvbadapter", m);
}
/**
*
*/
static void
tda_add(const char *path)
{
char fname[256];
int fe, i, r;
th_dvb_adapter_t *tda;
char buf[400];
snprintf(fname, sizeof(fname), "%s/frontend0", path);
fe = open(fname, O_RDWR | O_NONBLOCK);
if(fe == -1) {
if(errno != ENOENT)
tvhlog(LOG_ALERT, "dvb",
"Unable to open %s -- %s\n", fname, strerror(errno));
return;
}
tda = tda_alloc();
tda->tda_rootpath = strdup(path);
tda->tda_demux_path = malloc(256);
snprintf(tda->tda_demux_path, 256, "%s/demux0", path);
tda->tda_dvr_path = malloc(256);
snprintf(tda->tda_dvr_path, 256, "%s/dvr0", path);
tda->tda_fe_fd = fe;
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
if(ioctl(tda->tda_fe_fd, FE_GET_INFO, tda->tda_fe_info)) {
tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter\n", fname);
close(fe);
free(tda);
return;
}
tda->tda_type = tda->tda_fe_info->type;
if(dvb_dvr_init(tda) < 0) {
close(fe);
free(tda);
return;
}
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
tda->tda_fe_info->name);
r = strlen(buf);
for(i = 0; i < r; i++)
if(!isalnum((int)buf[i]))
buf[i] = '_';
tda->tda_identifier = strdup(buf);
/* Come up with an initial displayname, user can change it and it will
be overridden by any stored settings later on */
tda->tda_displayname = strdup(tda->tda_fe_info->name);
tvhlog(LOG_INFO, "dvb",
"Found adapter %s (%s)", path, tda->tda_fe_info->name);
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
dvb_fe_start(tda);
}
/**
*
*/
void
dvb_adapter_init(void)
{
char path[200];
htsmsg_t *l, *c;
htsmsg_field_t *f;
const char *name, *s;
int i, type;
th_dvb_adapter_t *tda;
TAILQ_INIT(&dvb_adapters);
for(i = 0; i < 32; i++) {
snprintf(path, sizeof(path), "/dev/dvb/adapter%d", i);
tda_add(path);
}
l = hts_settings_load("dvbadapters");
if(l != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_msg_by_field(f)) == NULL)
continue;
if(dvb_adapter_find_by_identifier(f->hmf_name) != NULL) {
/* Already loaded */
continue;
}
if((name = htsmsg_get_str(c, "displayname")) == NULL)
continue;
if((s = htsmsg_get_str(c, "type")) == NULL ||
(type = dvb_str_to_adaptertype(s)) < 0)
continue;
tda = tda_alloc();
tda->tda_identifier = strdup(f->hmf_name);
tda->tda_displayname = strdup(name);
tda->tda_type = type;
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
}
htsmsg_destroy(l);
}
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
dvb_mux_load(tda);
}
/**
*
*/
static void
dvb_notify_mux_quality(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "quality", 100 + tdmi->tdmi_quality * 2);
notify_by_msg("dvbmux", m);
}
/**
*
*/
static void
dvb_notify_mux_status(th_dvb_mux_instance_t *tdmi)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_str(m, "status", tdmi->tdmi_last_status);
notify_by_msg("dvbmux", m);
}
/**
*
*/
void
dvb_adapter_notify_reload(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tda->tda_identifier);
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("dvbadapter", m);
}
/**
*
*/
static void
dvb_fec_monitor(void *aux, int64_t now)
{
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
int i, v, vv;
const char *s;
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
tdmi = tda->tda_mux_current;
if(tdmi == NULL)
return;
if(tdmi->tdmi_status == NULL) {
v = vv = 0;
for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++) {
if(tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT)
v++;
vv += tdmi->tdmi_fec_err_histogram[i];
}
vv /= TDMI_FEC_ERR_HISTOGRAM_SIZE;
if(v == TDMI_FEC_ERR_HISTOGRAM_SIZE) {
if(LIST_FIRST(&tda->tda_transports) != NULL) {
tvhlog(LOG_ERR, "dvb",
"\"%s\": Constant rate of FEC errors (average at %d / s), "
"last %d seconds, flushing subscribers\n",
tdmi->tdmi_identifier, vv,
TDMI_FEC_ERR_HISTOGRAM_SIZE);
dvb_adapter_clean(tdmi->tdmi_adapter);
}
}
}
if(dvb_mux_status(tdmi, 1) != NULL) {
if(tdmi->tdmi_quality > -50) {
tdmi->tdmi_quality--;
dvb_notify_mux_quality(tdmi);
}
} else {
if(tdmi->tdmi_quality < 0) {
tdmi->tdmi_quality++;
dvb_notify_mux_quality(tdmi);
}
}
s = dvb_mux_status(tdmi, 0);
if(s != tdmi->tdmi_last_status) {
tdmi->tdmi_last_status = s;
dvb_notify_mux_status(tdmi);
}
}
/**
* If nobody is subscribing, cycle thru all muxes to get some stats
* and EIT updates
*/
void
dvb_adapter_mux_scanner(void *aux, int64_t now)
{
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
dtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 10);
if(transport_compute_weight(&tda->tda_transports) > 0)
return; /* someone is here */
/* Check if we have muxes pending for quickscan, if so, choose them */
if((tdmi = RB_FIRST(&tda->tda_muxes_qscan_waiting)) != NULL) {
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_RUNNING;
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
return;
}
/* otherwise, just rotate */
tdmi = tda->tda_mux_current;
if(tdmi != NULL)
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_NONE;
tdmi = tdmi != NULL ? RB_NEXT(tdmi, tdmi_adapter_link) : NULL;
tdmi = tdmi != NULL ? tdmi : RB_FIRST(&tda->tda_muxes);
if(tdmi == NULL)
return; /* no instances */
dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN);
}
/**
*
*/
void
dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
{
th_dvb_mux_instance_t *tdmi_src, *tdmi_dst;
th_transport_t *t_src, *t_dst;
th_stream_t *st_src, *st_dst;
while((tdmi_dst = RB_FIRST(&dst->tda_muxes)) != NULL)
dvb_mux_destroy(tdmi_dst);
RB_FOREACH(tdmi_src, &src->tda_muxes, tdmi_adapter_link) {
tdmi_dst = dvb_mux_create(dst,
&tdmi_src->tdmi_fe_params,
tdmi_src->tdmi_polarisation,
tdmi_src->tdmi_switchport,
tdmi_src->tdmi_transport_stream_id,
tdmi_src->tdmi_network,
"copy operation");
assert(tdmi_dst != NULL);
LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, tht_mux_link) {
t_dst = dvb_transport_find(tdmi_dst,
t_src->tht_dvb_service_id,
t_src->tht_pmt,
NULL);
t_dst->tht_pcr_pid = t_src->tht_pcr_pid;
t_dst->tht_disabled = t_src->tht_disabled;
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_chname != NULL)
t_dst->tht_chname = strdup(t_src->tht_chname);
if(t_src->tht_ch != NULL)
transport_map_channel(t_dst, t_src->tht_ch);
LIST_FOREACH(st_src, &t_src->tht_streams, st_link) {
st_dst = transport_add_stream(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;
}
}
dvb_mux_save(tdmi_dst);
}
tda_save(dst);
}
/**
*
*/
int
dvb_adapter_destroy(th_dvb_adapter_t *tda)
{
th_dvb_mux_instance_t *tdmi;
char buf[400];
if(tda->tda_rootpath != NULL)
return -1;
snprintf(buf, sizeof(buf), "%s/dvbadapters/%s",
settings_dir, tda->tda_identifier);
unlink(buf);
while((tdmi = RB_FIRST(&tda->tda_muxes)) != NULL)
dvb_mux_destroy(tdmi);
TAILQ_REMOVE(&dvb_adapters, tda, tda_global_link);
free(tda->tda_identifier);
free(tda->tda_displayname);
free(tda);
return 0;
}

510
dvb/dvb_multiplex.c Normal file
View file

@ -0,0 +1,510 @@
/*
* 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 <libhts/htssettings.h>
#include "tvhead.h"
#include "dispatch.h"
#include "dvb.h"
#include "channels.h"
#include "transports.h"
#include "subscriptions.h"
#include "teletext.h"
#include "epg.h"
#include "psi.h"
#include "dvb_support.h"
#include "dvb_dvr.h"
#include "dvb_muxconfig.h"
#include "notify.h"
struct th_dvb_mux_instance_tree dvb_muxes;
/**
*
*/
static int
tdmi_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b)
{
if(a->tdmi_switchport != b->tdmi_switchport)
return a->tdmi_switchport - b->tdmi_switchport;
if(a->tdmi_fe_params.frequency != b->tdmi_fe_params.frequency)
return a->tdmi_fe_params.frequency - b->tdmi_fe_params.frequency;
return a->tdmi_polarisation - b->tdmi_polarisation;
}
/**
*
*/
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);
}
/**
* 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, struct dvb_frontend_parameters *fe_param,
int polarisation, int switchport,
uint16_t tsid, const char *network, const char *source)
{
th_dvb_mux_instance_t *tdmi;
static th_dvb_mux_instance_t *skel;
char buf[200];
char qpsktxt[20];
int entries_before = tda->tda_muxes.entries;
if(skel == NULL)
skel = calloc(1, sizeof(th_dvb_mux_instance_t));
skel->tdmi_polarisation = polarisation;
skel->tdmi_switchport = switchport;
skel->tdmi_fe_params.frequency = fe_param->frequency;
tdmi = RB_INSERT_SORTED(&tda->tda_muxes, skel, tdmi_adapter_link, tdmi_cmp);
if(tdmi != NULL)
return NULL;
tdmi = skel;
skel = NULL;
tdmi->tdmi_refcnt = 1;
RB_INSERT_SORTED(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link,
tdmi_cmp);
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_WAITING;
pthread_mutex_init(&tdmi->tdmi_table_lock, NULL);
tdmi->tdmi_state = TDMI_IDLE;
tdmi->tdmi_transport_stream_id = tsid;
tdmi->tdmi_adapter = tda;
tdmi->tdmi_network = network ? strdup(network) : NULL;
if(entries_before == 0 && tda->tda_rootpath != NULL) {
/* First mux on adapter with backing hardware, start scanner */
dtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1);
}
memcpy(&tdmi->tdmi_fe_params, fe_param,
sizeof(struct dvb_frontend_parameters));
if(tda->tda_type == FE_QPSK)
snprintf(qpsktxt, sizeof(qpsktxt), "_%s_%d",
dvb_polarisation_to_str(polarisation), switchport);
else
qpsktxt[0] = 0;
snprintf(buf, sizeof(buf), "%s%d%s",
tda->tda_identifier,fe_param->frequency, qpsktxt);
tdmi->tdmi_identifier = strdup(buf);
RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp);
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_reload(tda);
}
return tdmi;
}
/**
* Unref a TDMI and optionally free it
*/
void
dvb_mux_unref(th_dvb_mux_instance_t *tdmi)
{
if(tdmi->tdmi_refcnt > 1) {
tdmi->tdmi_refcnt--;
return;
}
free(tdmi->tdmi_network);
free(tdmi->tdmi_identifier);
free(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;
char buf[400];
snprintf(buf, sizeof(buf), "%s/dvbmuxes/%s",
settings_dir, tdmi->tdmi_identifier);
unlink(buf);
while((t = LIST_FIRST(&tdmi->tdmi_transports)) != NULL)
transport_destroy(t);
if(tda->tda_mux_current == tdmi)
tdmi_stop(tda->tda_mux_current);
dtimer_disarm(&tdmi->tdmi_initial_scan_timer);
RB_REMOVE(&dvb_muxes, tdmi, tdmi_global_link);
RB_REMOVE(&tda->tda_muxes, tdmi, tdmi_adapter_link);
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_WAITING)
RB_REMOVE(&tda->tda_muxes_qscan_waiting, tdmi, tdmi_qscan_link);
hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
pthread_mutex_lock(&tda->tda_lock);
dvb_fe_flush(tdmi);
dvb_mux_unref(tdmi);
pthread_mutex_unlock(&tda->tda_lock);
}
/**
*
*/
void
dvb_mux_fastswitch(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt;
if(tdmi->tdmi_quickscan == TDMI_QUICKSCAN_NONE)
return;
pthread_mutex_lock(&tdmi->tdmi_table_lock);
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) {
if(tdt->tdt_quickreq && tdt->tdt_count == 0)
break;
}
pthread_mutex_unlock(&tdmi->tdmi_table_lock);
if(tdt != NULL)
return; /* Still tables we've not seen */
tdmi->tdmi_quickscan = TDMI_QUICKSCAN_NONE;
dvb_adapter_mux_scanner(tdmi->tdmi_adapter, 0);
}
/**
*
*/
th_dvb_mux_instance_t *
dvb_mux_find_by_identifier(const char *identifier)
{
th_dvb_mux_instance_t skel;
skel.tdmi_identifier = (char *)identifier;
return RB_FIND(&dvb_muxes, &skel, tdmi_global_link, tdmi_global_cmp);
}
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 }
};
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 }
};
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 },
};
/**
*
*/
void
dvb_mux_save(th_dvb_mux_instance_t *tdmi)
{
struct dvb_frontend_parameters *f = &tdmi->tdmi_fe_params;
htsmsg_t *m = htsmsg_create();
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_polarisation, poltab));
htsmsg_add_u32(m, "switchport", tdmi->tdmi_switchport);
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:
break;
}
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)
{
struct dvb_frontend_parameters f;
const char *s;
int r;
int polarisation = 0;
unsigned int switchport = 0;
unsigned int tsid;
memset(&f, 0, sizeof(f));
f.inversion = INVERSION_AUTO;
htsmsg_get_u32(m, "frequency", &f.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";
f.u.ofdm.bandwidth = r;
s = htsmsg_get_str(m, "constellation");
if(s == NULL || (r = str2val(s, qamtab)) < 0)
return "Invalid QAM constellation";
f.u.ofdm.constellation = r;
s = htsmsg_get_str(m, "transmission_mode");
if(s == NULL || (r = str2val(s, modetab)) < 0)
return "Invalid transmission mode";
f.u.ofdm.transmission_mode = r;
s = htsmsg_get_str(m, "guard_interval");
if(s == NULL || (r = str2val(s, guardtab)) < 0)
return "Invalid guard interval";
f.u.ofdm.guard_interval = r;
s = htsmsg_get_str(m, "hierarchy");
if(s == NULL || (r = str2val(s, hiertab)) < 0)
return "Invalid heirarchy information";
f.u.ofdm.hierarchy_information = r;
s = htsmsg_get_str(m, "fec_hi");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid hi-FEC";
f.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";
f.u.ofdm.code_rate_LP = r;
break;
case FE_QPSK:
htsmsg_get_u32(m, "symbol_rate", &f.u.qpsk.symbol_rate);
if(f.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";
f.u.qpsk.fec_inner = r;
s = htsmsg_get_str(m, "polarisation");
if(s == NULL || (r = str2val(s, poltab)) < 0)
return "Invalid polarisation";
polarisation = r;
htsmsg_get_u32(m, "switchport", &switchport);
break;
case FE_QAM:
htsmsg_get_u32(m, "symbol_rate", &f.u.qam.symbol_rate);
if(f.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";
f.u.qam.modulation = r;
s = htsmsg_get_str(m, "fec");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid FEC";
f.u.qam.fec_inner = r;
break;
case FE_ATSC:
break;
}
if(htsmsg_get_u32(m, "transportstreamid", &tsid))
tsid = 0xffff;
dvb_mux_create(tda, &f, polarisation, switchport,
tsid, htsmsg_get_str(m, "network"), NULL);
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_msg_by_field(f)) == NULL)
continue;
tdmi_create_by_msg(tda, c);
}
htsmsg_destroy(l);
}

View file

@ -21,332 +21,14 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <libhts/htssettings.h>
#include <linux/dvb/frontend.h>
#include "tvhead.h"
#include "channels.h"
#include "dvb.h"
#include "dvb_dvr.h"
#include "dvb_muxconfig.h"
#include "dvb_support.h"
#include "transports.h"
#include "notify.h"
/**
* Save config for the given adapter
*/
void
dvb_tda_save(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
htsmsg_add_str(m, "displayname", tda->tda_displayname);
hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier);
htsmsg_destroy(m);
}
/**
*
*/
static htsmsg_t *
dvb_tda_createmsg(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_str(m, "id", tda->tda_identifier);
return m;
}
/**
*
*/
void
dvb_tda_set_displayname(th_dvb_adapter_t *tda, const char *s)
{
htsmsg_t *m = dvb_tda_createmsg(tda);
free(tda->tda_displayname);
tda->tda_displayname = strdup(s);
dvb_tda_save(tda);
htsmsg_add_str(m, "name", tda->tda_displayname);
notify_by_msg("dvbadapter", m);
}
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 }
};
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 }
};
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 },
};
/**
*
*/
void
dvb_tdmi_save(th_dvb_mux_instance_t *tdmi)
{
struct dvb_frontend_parameters *f = &tdmi->tdmi_fe_params;
htsmsg_t *m = htsmsg_create();
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_polarisation, poltab));
htsmsg_add_u32(m, "switchport", tdmi->tdmi_switchport);
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:
break;
}
hts_settings_save(m, "dvbmuxes/%s/%s",
tdmi->tdmi_adapter->tda_identifier, tdmi->tdmi_identifier);
htsmsg_destroy(m);
}
/**
*
*/
static const char *
dvb_tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m)
{
struct dvb_frontend_parameters f;
const char *s;
int r;
int polarisation = 0;
unsigned int switchport = 0;
unsigned int tsid;
memset(&f, 0, sizeof(f));
f.inversion = INVERSION_AUTO;
htsmsg_get_u32(m, "frequency", &f.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";
f.u.ofdm.bandwidth = r;
s = htsmsg_get_str(m, "constellation");
if(s == NULL || (r = str2val(s, qamtab)) < 0)
return "Invalid QAM constellation";
f.u.ofdm.constellation = r;
s = htsmsg_get_str(m, "transmission_mode");
if(s == NULL || (r = str2val(s, modetab)) < 0)
return "Invalid transmission mode";
f.u.ofdm.transmission_mode = r;
s = htsmsg_get_str(m, "guard_interval");
if(s == NULL || (r = str2val(s, guardtab)) < 0)
return "Invalid guard interval";
f.u.ofdm.guard_interval = r;
s = htsmsg_get_str(m, "hierarchy");
if(s == NULL || (r = str2val(s, hiertab)) < 0)
return "Invalid heirarchy information";
f.u.ofdm.hierarchy_information = r;
s = htsmsg_get_str(m, "fec_hi");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid hi-FEC";
f.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";
f.u.ofdm.code_rate_LP = r;
break;
case FE_QPSK:
htsmsg_get_u32(m, "symbol_rate", &f.u.qpsk.symbol_rate);
if(f.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";
f.u.qpsk.fec_inner = r;
s = htsmsg_get_str(m, "polarisation");
if(s == NULL || (r = str2val(s, poltab)) < 0)
return "Invalid polarisation";
polarisation = r;
htsmsg_get_u32(m, "switchport", &switchport);
break;
case FE_QAM:
htsmsg_get_u32(m, "symbol_rate", &f.u.qam.symbol_rate);
if(f.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";
f.u.qam.modulation = r;
s = htsmsg_get_str(m, "fec");
if(s == NULL || (r = str2val(s, fectab)) < 0)
return "Invalid FEC";
f.u.qam.fec_inner = r;
break;
case FE_ATSC:
break;
}
if(htsmsg_get_u32(m, "transportstreamid", &tsid))
tsid = 0xffff;
dvb_mux_create(tda, &f, polarisation, switchport,
tsid, htsmsg_get_str(m, "network"), NULL);
return NULL;
}
/**
*
*/
void
dvb_tdmi_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_msg_by_field(f)) == NULL)
continue;
dvb_tdmi_create_by_msg(tda, c);
}
htsmsg_destroy(l);
}
/**
* A big list of all known DVB networks (from linuxtv.org)
@ -365,8 +47,6 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const struct mux *m, int num,
int polarisation;
int switchport;
printf("m = %p, num = %d\n", m, num);
for(i = 0; i < num; i++) {
polarisation = 0;

View file

@ -21,33 +21,8 @@
#include <libhts/htsmsg.h>
void dvb_tda_save(th_dvb_adapter_t *tda);
void dvb_tda_set_displayname(th_dvb_adapter_t *tda, const char *s);
void dvb_tdmi_save(th_dvb_mux_instance_t *tdmi);
const char *dvb_mux_create_str(th_dvb_adapter_t *tda,
const char *tsidstr,
const char *network,
const char *freqstr,
const char *symratestr,
const char *qamstr,
const char *fecstr,
const char *fechistr,
const char *feclostr,
const char *bwstr,
const char *tmodestr,
const char *guardstr,
const char *hierstr,
const char *polstr,
const char *switchportstr,
int save);
htsmsg_t *dvb_mux_preconf_get_node(int fetype, const char *node);
int dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id);
void dvb_tdmi_load(th_dvb_adapter_t *tda);
#endif /* DVB_MUXCONFIG_H */

View file

@ -98,7 +98,7 @@ dvb_table_recv(int events, void *opaque, int fd)
tdt->tdt_count++;
tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque);
dvb_tdmi_fastswitch(tdt->tdt_tdmi);
dvb_mux_fastswitch(tdt->tdt_tdmi);
}
@ -258,7 +258,7 @@ dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(tdmi == NULL)
return;
t = dvb_find_transport(tdmi, serviceid, 0, NULL);
t = dvb_transport_find(tdmi, serviceid, 0, NULL);
if(t == NULL)
return;
@ -409,7 +409,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
snprintf(chname0, sizeof(chname0), "noname-sid-0x%x", service_id);
}
t = dvb_find_transport(tdmi, service_id, 0, NULL);
t = dvb_transport_find(tdmi, service_id, 0, NULL);
if(t == NULL)
break;
@ -438,7 +438,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
}
}
if(change) {
dvb_tdmi_save(tdmi);
dvb_mux_save(tdmi);
// notify_tdmi_services_change(tdmi);
}
}
@ -461,7 +461,7 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(tdmi->tdmi_transport_stream_id != tid) {
tdmi->tdmi_transport_stream_id = tid;
dvb_tda_save(tdmi->tdmi_adapter);
dvb_mux_save(tdmi);
}
ptr += 5;
@ -472,7 +472,7 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
pmt = (ptr[2] & 0x1f) << 8 | ptr[3];
if(service != 0) {
t = dvb_find_transport(tdmi, service, pmt, &created);
t = dvb_transport_find(tdmi, service, pmt, &created);
if(created) { /* Add PMT to our table scanner */
dvb_table_add_transport(tdmi, t, pmt);
}
@ -647,7 +647,7 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
free((void *)tdmi->tdmi_network);
tdmi->tdmi_network = strdup(networkname);
//notify_tdmi_name_change(tdmi);
dvb_tda_save(tdmi->tdmi_adapter);
dvb_mux_save(tdmi);
}
break;
}
@ -713,7 +713,7 @@ dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
psi_parse_pmt(t, ptr, len, 1);
v ^= t->tht_pmt_seen;
if(v) {
dvb_tdmi_save(tdmi);
dvb_mux_save(tdmi);
//notify_tdmi_services_change(tdmi);
}
return;

161
dvb/dvb_transport.c Normal file
View file

@ -0,0 +1,161 @@
/*
* 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 <libhts/htssettings.h>
#include "tvhead.h"
#include "dispatch.h"
#include "dvb.h"
#include "channels.h"
#include "transports.h"
#include "subscriptions.h"
#include "psi.h"
#include "dvb_support.h"
#include "dvb_dvr.h"
#include "dvb_muxconfig.h"
#include "notify.h"
/**
*
*/
static void
dvb_transport_save(th_transport_t *t)
{
htsmsg_t *m = htsmsg_create();
htsmsg_add_u32(m, "service_id", t->tht_dvb_service_id);
htsmsg_add_u32(m, "pmt", t->tht_pmt);
htsmsg_add_u32(m, "stype", t->tht_servicetype);
htsmsg_add_u32(m, "scrambled", t->tht_scrambled);
if(t->tht_provider != NULL)
htsmsg_add_str(m, "provider", t->tht_provider);
if(t->tht_svcname != NULL)
htsmsg_add_str(m, "servicename", t->tht_svcname);
if(t->tht_chname != NULL)
htsmsg_add_str(m, "channelname", t->tht_chname);
htsmsg_add_u32(m, "mapped", !!t->tht_ch);
psi_get_transport_settings(m, t);
hts_settings_save(m, "dvbtransports/%s", t->tht_identifier);
htsmsg_destroy(m);
}
/**
* Called to get quality for the given transport
*
* We keep track of this for the entire mux (if we see errors), soo..
* return that value
*/
static int
dvb_transport_quality(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
return tdmi->tdmi_quality;
}
/**
* Generate a descriptive name for the source
*/
static const char *
dvb_transport_name(th_transport_t *t)
{
th_dvb_mux_instance_t *tdmi;
static char buf[200];
tdmi = t->tht_dvb_mux_instance;
snprintf(buf, sizeof(buf), "\"%s\" on \"%s\"",
tdmi->tdmi_network ?: "Unknown network",
tdmi->tdmi_adapter->tda_rootpath);
return buf;
}
/**
* Find a transport based on 'serviceid' on the given mux
*
* If it cannot be found we create it if 'pmt_pid' is also set
*/
th_transport_t *
dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
int *created)
{
th_transport_t *t;
char tmp[200];
if(created != NULL)
*created = 0;
LIST_FOREACH(t, &tdmi->tdmi_transports, tht_mux_link) {
if(t->tht_dvb_service_id == sid)
return t;
}
if(pmt_pid == 0)
return NULL;
if(created != NULL)
*created = 1;
snprintf(tmp, sizeof(tmp), "%s_%04x", tdmi->tdmi_identifier, sid);
t = transport_create(tmp, TRANSPORT_DVB, THT_MPEG_TS);
t->tht_dvb_service_id = sid;
t->tht_pmt = pmt_pid;
t->tht_start_feed = dvb_start_feed;
t->tht_stop_feed = dvb_stop_feed;
t->tht_config_change = dvb_transport_save;
t->tht_sourcename = dvb_transport_name;
t->tht_dvb_mux_instance = tdmi;
t->tht_quality_index = dvb_transport_quality;
LIST_INSERT_HEAD(&tdmi->tdmi_transports, t, tht_mux_link);
return t;
}

View file

@ -368,7 +368,7 @@ extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
} else if(!strcmp(op, "save")) {
if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL)
dvb_tda_set_displayname(tda, s);
dvb_adapter_set_displayname(tda, s);
out = htsmsg_create();
htsmsg_add_u32(out, "success", 1);