tvheadend/src/input/mpegts/linuxdvb/linuxdvb_network.c

293 lines
8.3 KiB
C

/*
* Tvheadend - Linux DVB Network
*
* Copyright (C) 2013 Adam Sutton
*
* 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 "input.h"
#include "linuxdvb_private.h"
#include "queue.h"
#include "settings.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
/* ****************************************************************************
* Class definition
* ***************************************************************************/
extern const idclass_t mpegts_network_class;
static void
linuxdvb_network_class_delete ( idnode_t *in )
{
mpegts_network_t *mn = (mpegts_network_t*)in;
/* remove config */
hts_settings_remove("input/linuxdvb/networks/%s",
idnode_uuid_as_str(in));
/* Parent delete */
mpegts_network_delete(mn);
}
const idclass_t linuxdvb_network_class =
{
.ic_super = &mpegts_network_class,
.ic_class = "linuxdvb_network",
.ic_caption = "LinuxDVB Network",
.ic_delete = linuxdvb_network_class_delete,
.ic_properties = (const property_t[]){
{}
}
};
const idclass_t linuxdvb_network_dvbt_class =
{
.ic_super = &linuxdvb_network_class,
.ic_class = "linuxdvb_network_dvbt",
.ic_caption = "DVB-T Network",
.ic_properties = (const property_t[]) {
{}
}
};
const idclass_t linuxdvb_network_dvbc_class =
{
.ic_super = &linuxdvb_network_class,
.ic_class = "linuxdvb_network_dvbc",
.ic_caption = "DVB-C Network",
.ic_properties = (const property_t[]) {
{}
}
};
const idclass_t linuxdvb_network_dvbs_class =
{
.ic_super = &linuxdvb_network_class,
.ic_class = "linuxdvb_network_dvbs",
.ic_caption = "DVB-S Network",
.ic_properties = (const property_t[]) {
{}
}
};
const idclass_t linuxdvb_network_atsc_class =
{
.ic_super = &linuxdvb_network_class,
.ic_class = "linuxdvb_network_atsc",
.ic_caption = "ATSC Network",
.ic_properties = (const property_t[]) {
{}
}
};
/* ****************************************************************************
* Class methods
* ***************************************************************************/
static mpegts_mux_t *
linuxdvb_network_find_mux
( linuxdvb_network_t *ln, dvb_mux_conf_t *dmc )
{
#define LINUXDVB_FREQ_TOL 2000 // TODO: fix this!
mpegts_mux_t *mm;
LIST_FOREACH(mm, &ln->mn_muxes, mm_network_link) {
linuxdvb_mux_t *lm = (linuxdvb_mux_t*)mm;
if (abs(lm->lm_tuning.dmc_fe_params.frequency
- dmc->dmc_fe_params.frequency) > LINUXDVB_FREQ_TOL) continue;
if (lm->lm_tuning.dmc_fe_polarisation != dmc->dmc_fe_polarisation) continue;
break;
}
return mm;
}
static void
linuxdvb_network_config_save ( mpegts_network_t *mn )
{
htsmsg_t *c = htsmsg_create_map();
idnode_save(&mn->mn_id, c);
htsmsg_add_str(c, "class", mn->mn_id.in_class->ic_class);
hts_settings_save(c, "input/linuxdvb/networks/%s/config",
idnode_uuid_as_str(&mn->mn_id));
htsmsg_destroy(c);
}
static mpegts_mux_t *
linuxdvb_network_create_mux
( mpegts_mux_t *mm, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *dmc )
{
// TODO: should we have a mux_find wrapper?
linuxdvb_network_t *ln = (linuxdvb_network_t*)mm->mm_network;
mm = linuxdvb_network_find_mux(ln, dmc);
if (!mm) {
mm = (mpegts_mux_t*)linuxdvb_mux_create0(ln, onid, tsid, dmc, NULL, NULL);
if (mm)
mm->mm_config_save(mm);
}
return mm;
}
static mpegts_service_t *
linuxdvb_network_create_service
( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid )
{
return linuxdvb_service_create0((linuxdvb_mux_t*)mm, sid,
pmt_pid, NULL, NULL);
}
static const idclass_t *
linuxdvb_network_mux_class
( mpegts_network_t *mn )
{
extern const idclass_t linuxdvb_mux_dvbt_class;
extern const idclass_t linuxdvb_mux_dvbc_class;
extern const idclass_t linuxdvb_mux_dvbs_class;
extern const idclass_t linuxdvb_mux_atsc_class;
if (idnode_is_instance(&mn->mn_id, &linuxdvb_network_dvbt_class))
return &linuxdvb_mux_dvbt_class;
if (idnode_is_instance(&mn->mn_id, &linuxdvb_network_dvbc_class))
return &linuxdvb_mux_dvbc_class;
if (idnode_is_instance(&mn->mn_id, &linuxdvb_network_dvbs_class))
return &linuxdvb_mux_dvbs_class;
if (idnode_is_instance(&mn->mn_id, &linuxdvb_network_atsc_class))
return &linuxdvb_mux_atsc_class;
return NULL;
}
static mpegts_mux_t *
linuxdvb_network_mux_create2
( mpegts_network_t *mn, htsmsg_t *conf )
{
linuxdvb_network_t *ln = (linuxdvb_network_t*)mn;
return (mpegts_mux_t*)
linuxdvb_mux_create0(ln, MPEGTS_ONID_NONE, MPEGTS_TSID_NONE,
NULL, NULL, conf);
}
/* ****************************************************************************
* Creation/Config
* ***************************************************************************/
linuxdvb_network_t *
linuxdvb_network_create0
( const char *uuid, const idclass_t *idc, htsmsg_t *conf )
{
linuxdvb_network_t *ln;
htsmsg_t *c, *e;
htsmsg_field_t *f;
/* Create */
if (!(ln = (linuxdvb_network_t*)mpegts_network_create0(calloc(1, sizeof(linuxdvb_network_t)),
idc, uuid, NULL, conf)))
return NULL;
if (idc == &linuxdvb_network_dvbt_class)
ln->ln_type = FE_OFDM;
else if (idc == &linuxdvb_network_dvbc_class)
ln->ln_type = FE_QAM;
else if (idc == &linuxdvb_network_dvbs_class)
ln->ln_type = FE_QPSK;
else
ln->ln_type = FE_ATSC;
/* Callbacks */
ln->mn_create_mux = linuxdvb_network_create_mux;
ln->mn_create_service = linuxdvb_network_create_service;
ln->mn_config_save = linuxdvb_network_config_save;
ln->mn_mux_class = linuxdvb_network_mux_class;
ln->mn_mux_create2 = linuxdvb_network_mux_create2;
/* No config */
if (!conf)
return ln;
/* Load muxes */
if ((c = hts_settings_load_r(1, "input/linuxdvb/networks/%s/muxes", uuid))) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_get_map_by_field(f))) continue;
if (!(e = htsmsg_get_map(e, "config"))) continue;
(void)linuxdvb_mux_create1(ln, f->hmf_name, e);
}
}
return ln;
}
static mpegts_network_t *
linuxdvb_network_builder
( const idclass_t *idc, htsmsg_t *conf )
{
return (mpegts_network_t*)linuxdvb_network_create0(NULL, idc, conf);
}
void linuxdvb_network_init ( void )
{
htsmsg_t *c, *e;
htsmsg_field_t *f;
const char *s;
int i;
const idclass_t* classes[] = {
&linuxdvb_network_dvbt_class,
&linuxdvb_network_dvbc_class,
&linuxdvb_network_dvbs_class,
&linuxdvb_network_atsc_class,
};
/* Register class builders */
for (i = 0; i < ARRAY_SIZE(classes); i++)
mpegts_network_register_builder(classes[i], linuxdvb_network_builder);
/* Load settings */
if (!(c = hts_settings_load_r(1, "input/linuxdvb/networks")))
return;
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_get_map_by_field(f))) continue;
if (!(e = htsmsg_get_map(e, "config"))) continue;
if (!(s = htsmsg_get_str(e, "class"))) continue;
for (i = 0; i < ARRAY_SIZE(classes); i++) {
if(!strcmp(classes[i]->ic_class, s)) {
(void)linuxdvb_network_create0(f->hmf_name, classes[i], e);
break;
}
}
}
htsmsg_destroy(c);
}
/* ****************************************************************************
* Search
* ***************************************************************************/
linuxdvb_network_t*
linuxdvb_network_find_by_uuid(const char *uuid)
{
idnode_t *in = idnode_find(uuid, &linuxdvb_network_class);
return (linuxdvb_network_t*)in;
}
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/