notify_by_msg("idnodeNameChanged", m);
}
-int
-idnode_load_one
- ( idnode_t *self, htsmsg_t *m )
+/*
+ * Save
+ */
+void
+idnode_save ( idnode_t *self, htsmsg_t *c )
{
- return 0;
+ const idclass_t *idc = self->in_class;
+ while (idc) {
+ prop_read_values(self, idc->ic_properties, c);
+ idc = idc->ic_super;
+ }
+}
+
+/*
+ * Load
+ */
+void idnode_load ( idnode_t *self, htsmsg_t *c )
+{
+ const idclass_t *idc = self->in_class;
+ while (idc) {
+ prop_write_values(self, idc->ic_properties, c);
+ idc = idc->ic_super;
+ }
}
void idnode_notify_title_changed(void *obj);
-void idnode_save ( idnode_t *self, const char *path );
-
-idnode_t *idnode_load ( htsmsg_field_t *cfg, void*(*create)(const char*) );
-
-void idnode_load_all ( const char *path, void *(*create)(const char*) );
-
-idnode_t *idnode_create0
- ( size_t alloc, const idclass_t *class, const char *uuid );
-
-#define idnode_create(c, uuid)\
- (struct c*)idnode_create0(sizeof(struct c), &c##_class, uuid)
-
-int idnode_load_one(idnode_t *self, htsmsg_t *cfg);
+void idnode_save ( idnode_t *self, htsmsg_t *m );
+void idnode_load ( idnode_t *self, htsmsg_t *m );
/*
* Identification
*/
-
char *mn_network_name;
/*
/*
* Functions
*/
+ void (*mn_display_name) (mpegts_network_t*, char *buf, size_t len);
+ void (*mn_config_save) (mpegts_network_t*);
mpegts_mux_t* (*mn_create_mux)
(mpegts_mux_t*, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *conf);
mpegts_service_t* (*mn_create_service)
(mpegts_mux_t*, uint16_t sid, uint16_t pmt_pid);
- void (*mn_display_name) (mpegts_network_t*, char *buf, size_t len);
- void (*mn_config_save) (mpegts_network_t*);
-
- // Note: the above are slightly odd in that they take mux instead of
- // network as initial param. This is intentional as we need to
- // know the mux and can easily get to network from there
-
-#if 0 // TODO: FIXME
- int dn_fe_type; // Frontend types for this network (FE_QPSK, etc)
-#endif
-
- uint32_t mn_nid; // limit scope of scanning
-
-#if 0 // TODO: FIXME CONFIG
- uint32_t dn_disable_pmt_monitor;
- uint32_t dn_autodiscovery;
- uint32_t dn_nitoid;
- uint32_t dn_skip_checksubscr;
-#endif
+ /*
+ * Configuration
+ */
+ uint32_t mn_nid;
+ uint32_t mn_autodiscovery;
};
/* Multiplex */
*/
void (*mm_config_save) (mpegts_mux_t *mm);
+ void (*mm_display_name) (mpegts_mux_t*, char *buf, size_t len);
+ int (*mm_is_enabled) (mpegts_mux_t *mm);
int (*mm_start) (mpegts_mux_t *mm, const char *r, int w);
void (*mm_stop) (mpegts_mux_t *mm);
void (*mm_open_table) (mpegts_mux_t*,mpegts_table_t*);
void (*mm_close_table) (mpegts_mux_t*,mpegts_table_t*);
void (*mm_create_instances) (mpegts_mux_t*);
- void (*mm_display_name) (mpegts_mux_t*, char *buf, size_t len);
/*
- * Fields
+ * Configuration
*/
- char *mm_dvb_default_authority;
+ char *mm_crid_authority;
int mm_enabled;
};
&type##_class, uuid,\
mi, mm);
-int mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid, int force );
-int mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid, int force );
-int mpegts_mux_set_default_authority ( mpegts_mux_t *mm, const char *defauth );
+int mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid );
+int mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid );
+int mpegts_mux_set_crid_authority ( mpegts_mux_t *mm, const char *defauth );
size_t mpegts_input_recv_packets
(mpegts_input_t *mi, mpegts_mux_instance_t *mmi, uint8_t *tsb, size_t len,
const char *dvb_mux_conf_load
( fe_type_t type, dvb_mux_conf_t *dmc, htsmsg_t *m );
-int dvb_mux_conf_save
- ( dvb_mux_conf_t *dmc, htsmsg_t *m );
+void dvb_mux_conf_save
+ ( fe_type_t type, dvb_mux_conf_t *dmc, htsmsg_t *m );
/* conversion routines */
const char *dvb_rolloff2str ( int rolloff );
{
int sect, last, ver;
uint16_t sid, pid, tsid;
- uint16_t nit_pid = DVB_NIT_PID;
+ uint16_t nit_pid = 0;
mpegts_mux_t *mm = mt->mt_mux;
/* Begin */
/* Multiplex */
tsid = (ptr[0] << 8) | ptr[1];
tvhtrace("pat", "tsid %04X (%d)", tsid, tsid);
- mpegts_mux_set_tsid(mm, tsid, 0);
- if (mm->mm_tsid != tsid)
- return -1;
+ mpegts_mux_set_tsid(mm, tsid);
/* Process each programme */
ptr += 5;
/* NIT PID */
if (sid == 0) {
if (pid) {
- tvhtrace("pat", " nit on pid %04X (%d)", pid, pid);
nit_pid = pid;
+ tvhtrace("pat", " nit on pid %04X (%d)", pid, pid);
}
/* Service */
ptr += 4;
len -= 4;
}
+
+ /* Install NIT handler */
+ if (nit_pid)
+ mpegts_table_add(mm, DVB_NIT_BASE, DVB_NIT_MASK, dvb_nit_callback,
+ NULL, "nit", MT_QUICKREQ | MT_CRC, nit_pid);
- /* Install NIT monitor */
- mpegts_table_add(mm, DVB_NIT_BASE, DVB_NIT_MASK, dvb_nit_callback,
- NULL, "nit", MT_CRC | MT_QUICKREQ, nit_pid);
-
+ /* End */
dvb_table_end(mt, tableid, sect, last, ver);
return mt->mt_state[0].complete ? 0 : -1;
}
return -1;
tvhtrace(mt->mt_name, " default auth [%s]", dauth);
if (mux && *dauth)
- mpegts_mux_set_default_authority(mux, dauth);
+ mpegts_mux_set_crid_authority(mux, dauth);
break;
case DVB_DESC_LOCAL_CHAN:
if (dvb_desc_local_channel(mt->mt_name, dptr, dlen, mux))
/* Find Transport Stream */
if (tableid == 0x42) {
- mpegts_mux_set_onid(mm, onid, 0);
- mpegts_mux_set_tsid(mm, tsid, 0);
- if (mm->mm_onid != onid || mm->mm_tsid != tsid)
- return -1;
+ mpegts_mux_set_onid(mm, onid);
+ mpegts_mux_set_tsid(mm, tsid);
} else {
mpegts_network_t *mn = mm->mm_network;
LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
return "Invalid FE type";
}
+static void
+dvb_mux_conf_save_dvbt ( dvb_mux_conf_t *dmc, htsmsg_t *m )
+{
+ struct dvb_ofdm_parameters *ofdm = &dmc->dmc_fe_params.u.ofdm;
+ htsmsg_add_str(m, "bandwidth",
+ dvb_bw2str(ofdm->bandwidth));
+ htsmsg_add_str(m, "constellation",
+ dvb_qam2str(ofdm->constellation));
+ htsmsg_add_str(m, "transmission_mode",
+ dvb_mode2str(ofdm->transmission_mode));
+ htsmsg_add_str(m, "guard_interval",
+ dvb_guard2str(ofdm->guard_interval));
+ htsmsg_add_str(m, "hierarchy",
+ dvb_hier2str(ofdm->hierarchy_information));
+ htsmsg_add_str(m, "fec_hi",
+ dvb_fec2str(ofdm->code_rate_HP));
+ htsmsg_add_str(m, "fec_lo",
+ dvb_fec2str(ofdm->code_rate_LP));
+}
+
+static void
+dvb_mux_conf_save_dvbc ( dvb_mux_conf_t *dmc, htsmsg_t *m )
+{
+ struct dvb_qam_parameters *qam = &dmc->dmc_fe_params.u.qam;
+ htsmsg_add_u32(m, "symbol_rate", qam->symbol_rate);
+ htsmsg_add_str(m, "constellation", dvb_qam2str(qam->modulation));
+ htsmsg_add_str(m, "fec", dvb_fec2str(qam->fec_inner));
+}
+
+static void
+dvb_mux_conf_save_dvbs ( dvb_mux_conf_t *dmc, htsmsg_t *m )
+{
+ struct dvb_qpsk_parameters *qpsk = &dmc->dmc_fe_params.u.qpsk;
+ htsmsg_add_u32(m, "symbol_rate", qpsk->symbol_rate);
+ htsmsg_add_str(m, "fec", dvb_fec2str(qpsk->fec_inner));
+ htsmsg_add_str(m, "polarisation", dvb_pol2str(dmc->dmc_fe_polarisation));
+#if DVB_API_VERSION >= 5
+ htsmsg_add_str(m, "modulation", dvb_qam2str(dmc->dmc_fe_modulation));
+ htsmsg_add_str(m, "rolloff", dvb_rolloff2str(dmc->dmc_fe_rolloff));
+#endif
+}
+
+static void
+dvb_mux_conf_save_atsc ( dvb_mux_conf_t *dmc, htsmsg_t *m )
+{
+ htsmsg_add_str(m, "constellation",
+ dvb_qam2str(dmc->dmc_fe_params.u.vsb.modulation));
+}
+
+void
+dvb_mux_conf_save ( fe_type_t type, dvb_mux_conf_t *dmc, htsmsg_t *m )
+{
+ htsmsg_add_u32(m, "frequency", dmc->dmc_fe_params.frequency);
+#if DVB_API_VERSION >= 5
+ htsmsg_add_str(m, "delsys", dvb_delsys2str(dmc->dmc_fe_delsys));
+#endif
+ if (type == FE_OFDM) dvb_mux_conf_save_dvbt(dmc, m);
+ else if (type == FE_QAM) dvb_mux_conf_save_dvbc(dmc, m);
+ else if (type == FE_QPSK) dvb_mux_conf_save_dvbs(dmc, m);
+ else if (type == FE_ATSC) dvb_mux_conf_save_atsc(dmc, m);
+}
+
#endif /* ENABLE_DVBAPI */
NULL, "pat", MT_QUICKREQ | MT_CRC, DVB_PAT_PID);
mpegts_table_add(mm, DVB_SDT_BASE, DVB_SDT_MASK, dvb_sdt_callback,
NULL, "sdt", MT_QUICKREQ | MT_CRC, DVB_SDT_PID);
+ mpegts_table_add(mm, DVB_NIT_BASE, DVB_NIT_MASK, dvb_nit_callback,
+ NULL, "nit", MT_QUICKREQ | MT_CRC, DVB_NIT_PID);
mpegts_table_add(mm, DVB_BAT_BASE, DVB_BAT_MASK, dvb_bat_callback,
NULL, "bat", MT_CRC, DVB_BAT_PID);
} else {
static void
linuxdvb_mux_config_save ( mpegts_mux_t *mm )
{
+ linuxdvb_mux_t *lm = (linuxdvb_mux_t*)mm;
+ linuxdvb_network_t *ln = (linuxdvb_network_t*)mm->mm_network;
+ htsmsg_t *c = htsmsg_create_map();
+ idnode_save(&mm->mm_id, c);
+ dvb_mux_conf_save(ln->ln_type, &lm->lm_tuning, c);
+ hts_settings_save(c, "input/linuxdvb/networks/%s/muxes/%s/config",
+ idnode_uuid_as_str(&mm->mm_network->mn_id),
+ idnode_uuid_as_str(&mm->mm_id));
+ htsmsg_destroy(c);
}
static void
linuxdvb_mux_display_name ( mpegts_mux_t *mm, char *buf, size_t len )
{
- size_t c = 0;
- const char *unit = "Hz";
linuxdvb_mux_t *lm = (linuxdvb_mux_t*)mm;
- c = snprintf(buf+c, len-c, "%d %s [%04X:%04X]",
- lm->lm_tuning.dmc_fe_params.frequency,
- unit, mm->mm_onid, mm->mm_tsid);
+ linuxdvb_network_t *ln = (linuxdvb_network_t*)mm->mm_network;
+ snprintf(buf, len, "%d %s%s [onid:%04X, tsid:%04X]",
+ lm->lm_tuning.dmc_fe_params.frequency,
+ (ln->ln_type == FE_QPSK) ? "MHz " : "Hz",
+ (ln->ln_type == FE_QPSK)
+ ? dvb_pol2str(lm->lm_tuning.dmc_fe_polarisation)
+ : "",
+ mm->mm_onid, mm->mm_tsid);
}
static void
extern const idclass_t mpegts_mux_instance_class;
mpegts_input_t *mi;
mpegts_mux_instance_t *mmi;
- tvhtrace("linuxdvb", "mm %p create instances", mm);
LIST_FOREACH(mi, &mm->mm_network->mn_inputs, mi_network_link) {
- tvhtrace("linuxdvb", " checking mi %p", mi);
LIST_FOREACH(mmi, &mi->mi_mux_instances, mmi_input_link)
if (mmi->mmi_mux == mm) break;
- if (!mmi) {
+ if (!mmi)
mmi = mpegts_mux_instance_create(mpegts_mux_instance, NULL, mi, mm);
- tvhtrace("linuxdvb", " created mmi %p", mmi);
- }
// TODO: we might eventually want to keep history!
}
}
linuxdvb_mux_create1
( linuxdvb_network_t *ln, const char *uuid, htsmsg_t *conf )
{
- uint32_t u32;
const char *str;
htsmsg_t *c, *e;
htsmsg_field_t *f;
lm = linuxdvb_mux_create0(ln, MPEGTS_ONID_NONE,
MPEGTS_TSID_NONE, &dmc, uuid);
- if (!lm) printf("OH DEAR\n");
if (!lm) return NULL;
/* No config */
return lm;
/* Config */
- // TODO: this could go in mpegts_mux
- if (!htsmsg_get_u32(conf, "enabled", &u32) && u32)
- lm->mm_enabled = 1;
- if (!htsmsg_get_u32(conf, "onid", &u32))
- lm->mm_onid = u32;
- if (!htsmsg_get_u32(conf, "tsid", &u32))
- lm->mm_tsid = u32;
- if ((str = htsmsg_get_str(conf, "default_authority")))
- lm->mm_dvb_default_authority = strdup(str);
+ idnode_load(&lm->mm_id, conf);
memcpy(&lm->lm_tuning, &dmc, sizeof(dmc));
/* Services */
htsmsg_destroy(c);
}
+ lm->mm_config_save((mpegts_mux_t*)lm);
+
return lm;
}
#include <fcntl.h>
extern const idclass_t mpegts_network_class;
+
+static const char *
+ln_type_getstr ( void *ptr )
+{
+ return dvb_type2str(((linuxdvb_network_t*)ptr)->ln_type);
+}
+
+static void
+ln_type_setstr ( void *ptr, const char *str )
+{
+ ((linuxdvb_network_t*)ptr)->ln_type = dvb_str2type(str);
+}
+
const idclass_t linuxdvb_network_class =
{
.ic_super = &mpegts_network_class,
.ic_class = "linuxdvb_network",
.ic_caption = "LinuxDVB Network",
.ic_properties = (const property_t[]){
-#if 0
{ PROPDEF2("type", "Network Type",
PT_STR, linuxdvb_network_t, ln_type, 1),
- .get_str },
-#endif
+ .str_get = ln_type_getstr,
+ .str_set = ln_type_setstr },
{}
}
};
+static void
+linuxdvb_network_config_save ( mpegts_network_t *mn )
+{
+ htsmsg_t *c = htsmsg_create_map();
+ idnode_save(&mn->mn_id, c);
+ 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_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) > 2000) continue;
+ 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;
}
static mpegts_mux_t *
linuxdvb_network_create_mux
- ( mpegts_mux_t *mm, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *conf )
+ ( mpegts_mux_t *mm, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *dmc )
{
linuxdvb_network_t *ln = (linuxdvb_network_t*)mm->mm_network;
- mm = linuxdvb_network_find_mux(ln, conf);
- if (!mm)
- mm = (mpegts_mux_t*)linuxdvb_mux_create0(ln, onid, tsid, conf, NULL);
+ mm = linuxdvb_network_find_mux(ln, dmc);
+ if (!mm) {
+ mm = (mpegts_mux_t*)linuxdvb_mux_create0(ln, onid, tsid, dmc, NULL);
+ if (mm)
+ mm->mm_config_save(mm);
+ }
return mm;
}
return NULL;
}
-static void
-linuxdvb_network_config_save ( mpegts_network_t *mn )
-{
-}
-
static linuxdvb_network_t *
linuxdvb_network_create0
( const char *uuid, htsmsg_t *conf )
{
- uint32_t u32;
- const char *str;
linuxdvb_network_t *ln;
htsmsg_t *c, *e;
htsmsg_field_t *f;
return ln;
/* Load configuration */
- if ((str = htsmsg_get_str(conf, "type")))
- ln->ln_type = dvb_str2type(str);
- if ((str = htsmsg_get_str(conf, "name")))
- ln->mn_network_name = strdup(str);
- if (!htsmsg_get_u32(conf, "nid", &u32))
- ln->mn_nid = u32;
+ idnode_load(&ln->mn_id, conf);
/* Load muxes */
if ((c = hts_settings_load_r(1, "input/linuxdvb/networks/%s/muxes", uuid))) {
}
}
+ linuxdvb_network_config_save((mpegts_network_t*)ln);
+
return ln;
}
(void)linuxdvb_network_create0(f->hmf_name, e);
}
htsmsg_destroy(c);
+ exit(1);
}
.ic_class = "mpegts_mux",
.ic_caption = "MPEGTS Multiplex",
.ic_properties = (const property_t[]){
+ { PROPDEF1("enabled", "Enabled",
+ PT_BOOL, mpegts_mux_t, mm_enabled) },
{ PROPDEF1("onid", "Original Network ID",
- PT_INT, mpegts_mux_t, mm_onid) },
+ PT_INT, mpegts_mux_t, mm_onid) },
{ PROPDEF1("tsid", "Transport Stream ID",
- PT_INT, mpegts_mux_t, mm_tsid) },
-
+ PT_INT, mpegts_mux_t, mm_tsid) },
+ { PROPDEF2("crid_authority", "CRID Authority",
+ PT_STR, mpegts_mux_t, mm_crid_authority, 1) },
+ {}
}
};
+static void
+mpegts_mux_display_name ( mpegts_mux_t *mm, char *buf, size_t len )
+{
+ snprintf(buf, len, "Multiplex [onid:%04X tsid:%04X]",
+ mm->mm_onid, mm->mm_tsid);
+}
+
+static void
+mpegts_mux_config_save ( mpegts_mux_t *mm )
+{
+}
+
+static int
+mpegts_mux_is_enabled ( mpegts_mux_t *mm )
+{
+ return mm->mm_enabled;
+}
+
int
-mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid, int force )
+mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid )
{
if (onid == mm->mm_onid)
return 0;
- if (!force && mm->mm_onid != MPEGTS_ONID_NONE)
- return 0;
mm->mm_onid = onid;
return 1;
}
int
-mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid, int force )
+mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid )
{
if (tsid == mm->mm_tsid)
return 0;
- if (!force && mm->mm_tsid != MPEGTS_TSID_NONE)
- return 0;
mm->mm_tsid = tsid;
return 1;
}
int
-mpegts_mux_set_default_authority ( mpegts_mux_t *mm, const char *defauth )
+mpegts_mux_set_crid_authority ( mpegts_mux_t *mm, const char *defauth )
{
- if (defauth && !strcmp(defauth, mm->mm_dvb_default_authority ?: ""))
+ if (defauth && !strcmp(defauth, mm->mm_crid_authority ?: ""))
return 0;
- tvh_str_update(&mm->mm_dvb_default_authority, defauth);
+ tvh_str_update(&mm->mm_crid_authority, defauth);
return 1;
}
mm->mm_network = mn;
mpegts_mux_initial_scan_link(mm);
+ /* Debug/Config */
+ mm->mm_display_name = mpegts_mux_display_name;
+ mm->mm_config_save = mpegts_mux_config_save;
+ mm->mm_is_enabled = mpegts_mux_is_enabled;
+
/* Start/stop */
mm->mm_start = mpegts_mux_start;
mm->mm_stop = mpegts_mux_stop;
.ic_class = "mpegts_network",
.ic_caption = "MPEGTS Network",
.ic_properties = (const property_t[]){
+ { PROPDEF1("networkname", "Network Name",
+ PT_STR, mpegts_network_t, mn_network_name) },
+ { PROPDEF1("nid", "Network ID (limit scanning)",
+ PT_INT, mpegts_network_t, mn_nid) },
+ { PROPDEF1("autodiscovery", "Network Discovery",
+ PT_BOOL, mpegts_network_t, mn_autodiscovery) },
+ {}
}
};
+static void
+mpegts_network_display_name
+ ( mpegts_network_t *mn, char *buf, size_t len )
+{
+ strncpy(buf, mn->mn_network_name ?: "unknown", len);
+}
+
static void
mpegts_network_config_save
( mpegts_network_t *mn )
{
+ // Nothing - leave to child classes
}
static mpegts_mux_t *
return NULL;
}
+static mpegts_service_t *
+mpegts_network_create_service
+ ( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid )
+{
+ return NULL;
+}
+
static void
mpegts_network_initial_scan(void *aux)
{
const char *netname )
{
idnode_insert(&mn->mn_id, uuid, idc);
- mn->mn_create_mux = mpegts_network_create_mux;
- mn->mn_config_save = mpegts_network_config_save;
+ mn->mn_display_name = mpegts_network_display_name;
+ mn->mn_config_save = mpegts_network_config_save;
+ mn->mn_create_mux = mpegts_network_create_mux;
+ mn->mn_create_service = mpegts_network_create_service;
if (netname) mn->mn_network_name = strdup(netname);
TAILQ_INIT(&mn->mn_initial_scan_pending_queue);
TAILQ_INIT(&mn->mn_initial_scan_current_queue);
continue;
const property_t *p = prop_find(pl, f->hmf_name);
if(p == NULL) {
- fprintf(stderr, "Property %s unmappable\n", f->hmf_name);
- continue;
- }
- if (p->rdonly) {
- tvhlog(LOG_WARNING, "prop", "field %s is read-only", p->id);
+ //fprintf(stderr, "Property %s unmappable\n", f->hmf_name);
continue;
}