/*
* 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 .
*/
#include "tvheadend.h"
#include "input.h"
#include "linuxdvb_private.h"
#include "queue.h"
#include "settings.h"
#include "scanfile.h"
#include
#include
#include
#include
#include
#include
static mpegts_mux_t *
linuxdvb_network_find_mux
( linuxdvb_network_t *ln, dvb_mux_conf_t *dmc );
/* ****************************************************************************
* 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);
}
static const void *
linuxdvb_network_class_scanfile_get ( void *o )
{
static const char *s = NULL;
return &s;
}
static int
linuxdvb_network_class_scanfile_set ( void *o, const void *s )
{
dvb_mux_conf_t *dmc;
scanfile_network_t *sfn;
mpegts_mux_t *mm;
/* Find */
if (!s)
return 0;
if (!(sfn = scanfile_find(s)))
return 0;
/* Create */
LIST_FOREACH(dmc, &sfn->sfn_muxes, dmc_link) {
if (!(mm = linuxdvb_network_find_mux(o, dmc))) {
mm = (mpegts_mux_t*)linuxdvb_mux_create0(o,
MPEGTS_ONID_NONE,
MPEGTS_TSID_NONE,
dmc, NULL, NULL);
if (mm)
mm->mm_config_save(mm);
}
}
return 0;
}
static htsmsg_t *
linuxdvb_network_class_scanfile_list ( const char *type )
{
htsmsg_t *e, *m = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "linuxdvb/scanfile/list");
e = htsmsg_create_map();
htsmsg_add_str(e, "type", type);
htsmsg_add_msg(m, "params", e);
return m;
}
static htsmsg_t *
linuxdvb_network_dvbt_class_scanfile_list ( void *o )
{
return linuxdvb_network_class_scanfile_list("dvbt");
}
static htsmsg_t *
linuxdvb_network_dvbc_class_scanfile_list ( void *o )
{
return linuxdvb_network_class_scanfile_list("dvbc");
}
static htsmsg_t *
linuxdvb_network_dvbs_class_scanfile_list ( void *o )
{
return linuxdvb_network_class_scanfile_list("dvbs");
}
static htsmsg_t *
linuxdvb_network_atsc_class_scanfile_list ( void *o )
{
return linuxdvb_network_class_scanfile_list("atsc");
}
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[]) {
{
.type = PT_STR,
.id = "scanfile",
.name = "Pre-defined Muxes",
.set = linuxdvb_network_class_scanfile_set,
.get = linuxdvb_network_class_scanfile_get,
.list = linuxdvb_network_dvbt_class_scanfile_list,
.opts = PO_NOSAVE,
},
{}
}
};
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[]) {
{
.type = PT_STR,
.id = "scanfile",
.name = "Pre-defined Muxes",
.set = linuxdvb_network_class_scanfile_set,
.get = linuxdvb_network_class_scanfile_get,
.list = linuxdvb_network_dvbc_class_scanfile_list,
.opts = PO_NOSAVE,
},
{}
}
};
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[]) {
{
.type = PT_STR,
.id = "scanfile",
.name = "Pre-defined Muxes",
.set = linuxdvb_network_class_scanfile_set,
.get = linuxdvb_network_class_scanfile_get,
.list = linuxdvb_network_dvbs_class_scanfile_list,
.opts = PO_NOSAVE,
},
{}
}
};
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[]) {
{
.type = PT_STR,
.id = "scanfile",
.name = "Pre-defined Muxes",
.set = linuxdvb_network_class_scanfile_set,
.get = linuxdvb_network_class_scanfile_get,
.list = linuxdvb_network_atsc_class_scanfile_list,
.opts = PO_NOSAVE,
},
{}
}
};
/* ****************************************************************************
* Class methods
* ***************************************************************************/
static mpegts_mux_t *
linuxdvb_network_find_mux
( linuxdvb_network_t *ln, dvb_mux_conf_t *dmc )
{
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 )
{
int save = 0;
linuxdvb_network_t *ln = (linuxdvb_network_t*)mm->mm_network;
mm = linuxdvb_network_find_mux(ln, dmc);
if (!mm && ln->mn_autodiscovery) {
mm = (mpegts_mux_t*)linuxdvb_mux_create0(ln, onid, tsid, dmc, NULL, NULL);
save = 1;
} else if (mm) {
linuxdvb_mux_t *lm = (linuxdvb_mux_t*)mm;
dmc->dmc_fe_params.frequency = lm->lm_tuning.dmc_fe_params.frequency;
// Note: keep original freq, else it can bounce around if diff transponders
// report it slightly differently.
// TODO: Note: should we also leave AUTO settings as is?
if (memcmp(&lm->lm_tuning, dmc, sizeof(lm->lm_tuning))) {
memcpy(&lm->lm_tuning, dmc, sizeof(lm->lm_tuning));
save = 1;
}
}
if (save)
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;
ln = calloc(1, sizeof(linuxdvb_network_t));
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;
/* Create */
if (!(ln = (linuxdvb_network_t*)mpegts_network_create0((void*)ln,
idc, uuid, NULL, conf)))
return NULL;
/* 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);
}
htsmsg_destroy(c);
}
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
*****************************************************************************/