# Core
#
SRCS = src/main.c \
+ src/idnode.c \
src/utils.c \
src/wrappers.c \
src/version.c \
dvb_init(uint32_t adapter_mask, const char *rawfile)
{
dvb_charset_init();
+ dvb_network_init();
dvb_adapter_init(adapter_mask, rawfile);
}
#include <pthread.h>
#include "htsmsg.h"
#include "psi.h"
+#include "idnode.h"
struct service;
struct th_dvb_table;
(DVB_VER_INT(DVB_API_VERSION, DVB_API_VERSION_MINOR) >= DVB_VER_INT(maj, min))
TAILQ_HEAD(th_dvb_adapter_queue, th_dvb_adapter);
-RB_HEAD(th_dvb_mux_instance_tree, th_dvb_mux_instance);
+LIST_HEAD(th_dvb_adapter_list, th_dvb_adapter);
TAILQ_HEAD(th_dvb_mux_instance_queue, th_dvb_mux_instance);
LIST_HEAD(th_dvb_mux_instance_list, th_dvb_mux_instance);
TAILQ_HEAD(dvb_satconf_queue, dvb_satconf);
*
*/
typedef struct dvb_network {
+ idnode_t dn_id;
LIST_ENTRY(dvb_network) dn_global_link;
- struct dvb_mux_queue dn_initial_scan_queue;
- int dn_initial_num_mux;
+ struct dvb_mux_queue dn_initial_scan_pending_queue;
+ struct dvb_mux_queue dn_initial_scan_current_queue;
+ int dn_initial_scan_num_mux;
+ gtimer_t dn_initial_scan_timer;
struct dvb_mux *dn_mux_epg;
struct dvb_mux_list dn_muxes;
- gtimer_t dn_mux_scanner_timer;
-
uint32_t dn_disable_pmt_monitor;
uint32_t dn_autodiscovery;
uint32_t dn_nitoid;
- char *dn_uuid;
+ struct th_dvb_adapter_list dn_adapters;
} dvb_network_t;
*
*/
typedef struct dvb_mux {
-
+ idnode_t dm_id;
LIST_ENTRY(dvb_mux) dm_network_link;
dvb_network_t *dm_dn;
TAILQ_HEAD(, epggrab_ota_mux) dm_epg_grab;
+ gtimer_t dm_initial_scan_timeout;
+
TAILQ_ENTRY(dvb_mux) dm_scan_link;
- struct dvb_mux_queue *dm_scan_queue;
+ enum {
+ DM_SCAN_DONE, // All done
+ DM_SCAN_PENDING, // Waiting to be tuned for initial scan
+ DM_SCAN_CURRENT, // Currently tuned for initial scan
+ } dm_scan_status;
LIST_HEAD(, th_dvb_table) dm_tables;
int dm_num_tables;
TAILQ_HEAD(, th_dvb_table) dm_table_queue;
- int dm_table_initial;
+ // int dm_table_initial;
struct th_dvb_mux_instance *dm_current_tdmi;
// Derived from dm_conf (more or less)
char *dm_local_identifier;
- char *dm_uuid;
-
int dm_enabled;
} dvb_mux_t;
int tdmi_tune_failed; // Adapter failed to tune this frequency
// Don't try again
+ int tdmi_weight;
+
struct th_subscription_list tdmi_subscriptions;
} th_dvb_mux_instance_t;
TAILQ_ENTRY(th_dvb_adapter) tda_global_link;
dvb_network_t *tda_dn;
+ LIST_ENTRY(th_dvb_adapter) tda_network_link;
struct th_dvb_mux_instance_list tda_tdmis;
uint16_t tsid, int enabled);
+void dvb_mux_initial_scan_done(dvb_mux_t *dm);
+
/**
* DVB Transport (aka DVB service)
*/
void dvb_service_notify_by_adapter(th_dvb_adapter_t *tda);
-htsmsg_t *dvb_service_build_msg(struct service *t);
-
/**
* DVB Frontend
*/
-int dvb_fe_tune(dvb_mux_t *dm, const char *reason);
+int dvb_fe_tune(dvb_mux_t *dm, const char *reason, int weight);
-void dvb_fe_stop(th_dvb_adapter_t *tda, int retune);
+//void dvb_fe_stop(th_dvb_adapter_t *tda, int retune);
/**
/**
*
*/
-dvb_network_t *dvb_network_create(int fe_type);
+dvb_network_t *dvb_network_create(int fe_type, const char *uuid);
+
+//void dvb_network_mux_scanner(void *aux);
+
+void dvb_network_init(void);
+
+idnode_t **dvb_network_root(void);
-void dvb_network_mux_scanner(void *aux);
+void dvb_network_schedule_initial_scan(dvb_network_t *dn);
/**
#include "diseqc.h"
struct th_dvb_adapter_queue dvb_adapters;
-struct th_dvb_mux_instance_tree dvb_muxes;
static void *dvb_adapter_input_dvr(void *aux);
tda_save(tda);
}
+
+/**
+ *
+ */
+static void
+dvb_adapter_set_network(th_dvb_adapter_t *tda, const char *uuid)
+{
+ dvb_network_t *dn = LIST_FIRST(&dvb_networks);
+ tda->tda_dn = dn;
+ LIST_INSERT_HEAD(&dn->dn_adapters, tda, tda_network_link);
+}
+
+
+
/**
*
*/
close(fe);
tda->tda_fe_type = tda->tda_fe_info->type;
- tda->tda_dn = dvb_network_create(tda->tda_fe_type);
+ dvb_adapter_set_network(tda, NULL);
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
tda->tda_fe_info->name);
/**
* Read out front end status
*/
+
if(ioctl(tda->tda_fe_fd, FE_READ_STATUS, &fe_status))
fe_status = 0;
}
+#if 0
/**
* Stop the given TDMI
*/
assert(tdmi != NULL);
dvb_mux_t *dm = tdmi->tdmi_mux;
- if(dm->dm_table_initial) {
- dm->dm_table_initial = 0;
- dm->dm_dn->dn_initial_num_mux--;
- dvb_mux_save(dm);
- }
-
dvb_table_flush_all(dm);
- assert(dm->dm_scan_queue == NULL);
-
epggrab_mux_stop(dm, 0);
#if 0 /// XXX(dvbreorg)
}
#endif
}
+#endif
+
+
#if DVB_API_VERSION >= 5
*
*/
static int
-dvb_fe_tune_s2(th_dvb_mux_instance_t *tdmi, dvb_mux_conf_t *dmc)
+dvb_fe_tune_s2(th_dvb_adapter_t *tda, dvb_mux_conf_t *dmc)
{
- th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
struct dvb_frontend_parameters *p = &dmc->dmc_fe_params;
int r;
#endif
+
/**
- *
+ * These are created on the fly
*/
-int
-dvb_fe_tune(dvb_mux_t *dm, const char *reason)
+static void
+dvb_create_tdmis(dvb_mux_t *dm)
{
+ th_dvb_mux_instance_t *tdmi;
dvb_network_t *dn = dm->dm_dn;
+ th_dvb_adapter_t *tda;
+
+ LIST_FOREACH(tda, &dn->dn_adapters, tda_network_link) {
+
+ LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) {
+ if(tdmi->tdmi_adapter != NULL)
+ break;
+ }
- th_dvb_mux_instance_t *tdmi = NULL; // dm->dm_tdmi;
+ if(tdmi == NULL) {
+ tdmi = calloc(1, sizeof(th_dvb_mux_instance_t));
+ tdmi->tdmi_adapter = tda;
+ tdmi->tdmi_mux = dm;
+ LIST_INSERT_HEAD(&tda->tda_tdmis, tdmi, tdmi_adapter_link);
+ LIST_INSERT_HEAD(&dm->dm_tdmis, tdmi, tdmi_mux_link);
+ }
+ }
+}
+
+
+
+/**
+ *
+ */
+static void
+dvb_mux_stop(th_dvb_mux_instance_t *tdmi)
+{
+ dvb_mux_t *dm = tdmi->tdmi_mux;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+ assert(dm->dm_current_tdmi == tdmi);
+ assert(tda->tda_current_tdmi == tdmi);
+
+ dvb_table_flush_all(dm);
+ epggrab_mux_stop(dm, 0);
+
+ assert(dm->dm_scan_status == DM_SCAN_DONE);
+
+ dm->dm_current_tdmi = NULL;
+ tda->tda_current_tdmi = NULL;
+
+ printf("NEED TO TAKE CARE OF SERVICES in dvb_mux_stop\n");
+}
+
+
+/**
+ *
+ */
+static int
+tdmi_compute_weight(const th_dvb_mux_instance_t *tdmi)
+{
+ const dvb_mux_t *dm = tdmi->tdmi_mux;
+ if(dm->dm_scan_status == DM_SCAN_CURRENT)
+ return 1;
+ return 0;
+}
+
+
+/**
+ *
+ */
+static void
+dvb_mux_initial_scan_timeout(void *aux)
+{
+ dvb_mux_t *dm = aux;
+ char buf[100];
+
+ dvb_mux_nicename(buf, sizeof(buf), dm);
+ tvhlog(LOG_DEBUG, "dvb", "Initial scan timed out for \"%s\"", buf);
+
+ dvb_mux_initial_scan_done(dm);
+}
+
+
+/**
+ *
+ */
+int
+dvb_fe_tune(dvb_mux_t *dm, const char *reason, int weight)
+{
+ dvb_network_t *dn = dm->dm_dn;
// copy dmc, cause frequency may be change with FE_QPSK
dvb_mux_conf_t dmc = dm->dm_conf;
dvb_frontend_parameters_t* p = &dmc.dmc_fe_params;
-
+ th_dvb_mux_instance_t *tdmi;
+
char buf[256];
int r;
-
+
+ assert(dm->dm_current_tdmi == NULL);
lock_assert(&global_lock);
- free(tda->tda_tune_reason);
- tda->tda_tune_reason = strdup(reason);
+ dvb_create_tdmis(dm);
- if(tda->tda_current_tdmi == tdmi) {
- dvb_adapter_notify(tda);
- return 0;
- }
+ retry:
+ // Figure which adapter to use
+ LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link)
+ if(!tdmi->tdmi_tune_failed && tdmi->tdmi_adapter->tda_current_tdmi == NULL)
+ break;
+
+ if(tdmi == NULL) {
+ // None available, need to strike one out
+ LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) {
+ if(tdmi->tdmi_tune_failed)
+ continue;
+
+ th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+ th_dvb_mux_instance_t *t2 = tda->tda_current_tdmi;
+ assert(t2 != NULL);
+ assert(t2 != tdmi);
+
+ if(tdmi_compute_weight(t2) < weight) {
+ dvb_mux_stop(t2);
+ break;
+ }
+ }
+
+ if(tdmi == NULL)
+ return SM_CODE_NO_FREE_ADAPTER;
- if(dm->dm_scan_queue != NULL) {
- TAILQ_REMOVE(dm->dm_scan_queue, dm, dm_scan_link);
- dm->dm_scan_queue = NULL;
}
- if(tda->tda_current_tdmi != NULL)
- dvb_fe_stop(tda, 1);
- else
- dvb_adapter_start(tda);
-
+ th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+
+ dvb_adapter_start(tda);
+
+ assert(tda->tda_current_tdmi == NULL);
+
+ free(tda->tda_tune_reason);
+ tda->tda_tune_reason = strdup(reason);
+
+ tdmi->tdmi_weight = weight;
+
if(tda->tda_fe_type == FE_QPSK) {
-
+
/* DVB-S */
int port, lowfreq, hifreq, switchfreq, hiband, pol, dbsbs;
dvb_mux_fec2str(p->u.qpsk.fec_inner), dvb_mux_delsys2str(dmc.dmc_fe_delsys),
dvb_mux_qam2str(dmc.dmc_fe_modulation), reason);
- r = dvb_fe_tune_s2(tdmi, &dmc);
+ r = dvb_fe_tune_s2(tda, &dmc);
} else
#endif
{
- tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning to \"%s\" (%s)", tda->tda_rootpath, buf, reason);
+ tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning to \"%s\" (%s) fd:%d", tda->tda_rootpath, buf, reason, tda->tda_fe_fd);
r = ioctl(tda->tda_fe_fd, FE_SET_FRONTEND, p);
}
" -- Front configuration failed -- %s, frequency: %u",
tda->tda_rootpath, buf, strerror(errno), p->frequency);
- /* Remove from initial scan set */
- if(dm->dm_table_initial) {
- dm->dm_table_initial = 0;
- dn->dn_initial_num_mux--;
- }
-
/* Mark as bad */
tdmi->tdmi_tune_failed = 1;
- return SM_CODE_TUNING_FAILED;
+ goto retry;
}
tda->tda_current_tdmi = tdmi;
+ dm->dm_current_tdmi = tdmi;
+ if(dm->dm_scan_status == DM_SCAN_PENDING) {
+ TAILQ_REMOVE(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link);
+ dm->dm_scan_status = DM_SCAN_CURRENT;
+ TAILQ_INSERT_TAIL(&dn->dn_initial_scan_current_queue, dm, dm_scan_link);
- gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
+ gtimer_arm(&dm->dm_initial_scan_timeout, dvb_mux_initial_scan_timeout, dm, 10);
+ }
- dvb_table_add_default(tdmi->tdmi_mux);
- epggrab_mux_start(tdmi->tdmi_mux);
+ gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
+
+ dvb_table_add_default(dm);
+ epggrab_mux_start(dm);
dvb_adapter_notify(tda);
return 0;
st->es_demuxer_fd = -1;
tvhlog(LOG_ERR, "dvb",
"\"%s\" unable to open demuxer \"%s\" for pid %d -- %s",
- s->s_uuid, tda->tda_demux_path,
+ s->s_nicename, tda->tda_demux_path,
st->es_pid, strerror(errno));
continue;
}
if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) {
tvhlog(LOG_ERR, "dvb",
"\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s",
- s->s_uuid, tda->tda_demux_path,
+ s->s_nicename, tda->tda_demux_path,
st->es_pid, strerror(errno));
close(fd);
fd = -1;
#include "subscriptions.h"
#include "epggrab.h"
-struct th_dvb_mux_instance_tree dvb_muxes;
-
static struct strtab muxfestatustab[] = {
{ "Unknown", TDMI_FE_UNKNOWN },
{ "No signal", TDMI_FE_NO_SIGNAL },
{ "OK", TDMI_FE_OK },
};
+
+
+static htsmsg_t *dvb_mux_serialize(struct idnode *self, int full);
+static idnode_t **dvb_mux_get_childs(struct idnode *self);
+
+static const idclass_t dvb_mux_class = {
+ .ic_class = "dvbmux",
+ .ic_serialize = dvb_mux_serialize,
+ .ic_get_childs = dvb_mux_get_childs,
+};
+
+
+
/**
*
*/
static void
mux_link_initial(dvb_network_t *dn, dvb_mux_t *dm)
{
- dm->dm_scan_queue = &dn->dn_initial_scan_queue;
- TAILQ_INSERT_TAIL(dm->dm_scan_queue, dm, dm_scan_link);
+ assert(dm->dm_scan_status == DM_SCAN_DONE);
- gtimer_arm(&dn->dn_mux_scanner_timer, dvb_network_mux_scanner, dn, 0);
+ dm->dm_scan_status = DM_SCAN_PENDING;
+ TAILQ_INSERT_TAIL(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link);
+ dn->dn_initial_scan_num_mux++;
+ dvb_network_schedule_initial_scan(dn);
}
+
+/**
+ *
+ */
+void
+dvb_mux_initial_scan_done(dvb_mux_t *dm)
+{
+ dvb_network_t *dn = dm->dm_dn;
+ gtimer_disarm(&dm->dm_initial_scan_timeout);
+ assert(dm->dm_scan_status == DM_SCAN_CURRENT);
+ dn->dn_initial_scan_num_mux--;
+ dm->dm_scan_status = DM_SCAN_DONE;
+ TAILQ_REMOVE(&dn->dn_initial_scan_current_queue, dm, dm_scan_link);
+ dvb_network_schedule_initial_scan(dn);
+ dvb_mux_save(dm); // Save to dm_scan_status is persisted
+}
+
+
/**
* Return a readable status text for the given mux
*/
dvb_mux_t *
dvb_mux_create(dvb_network_t *dn, const struct dvb_mux_conf *dmc,
uint16_t onid, uint16_t tsid, const char *network,
- const char *source, int enabled, int initialscan,
+ const char *source, int enabled, int needscan,
const char *uuid)
{
dvb_mux_t *dm;
save = 1;
}
-#if 0 // XXX(dvbreorg)
- /* HACK - load old transports and remove old mux config */
- if(identifier) {
- save = 1;
- dvb_service_load(tdmi, identifier);
- hts_settings_remove("dvbmuxes/%s/%s",
- tda->tda_identifier, identifier);
- }
-#endif
-
if(save) {
char buf[128];
dvb_mux_save(dm);
dm->dm_dn = dn;
LIST_INSERT_HEAD(&dn->dn_muxes, dm, dm_network_link);
+ idnode_insert(&dm->dm_id, uuid, &dvb_mux_class);
+
char identifier[128];
snprintf(identifier, sizeof(identifier),
"%d%s", dmc->dmc_fe_params.frequency,
memcpy(&dm->dm_conf, dmc, sizeof(struct dvb_mux_conf));
- dm->dm_table_initial = initialscan;
-
if(source != NULL) {
char buf[128];
dvb_mux_nicename(buf, sizeof(buf), dm);
dvb_service_load(dm);
- if(enabled && dm->dm_table_initial) {
- dn->dn_initial_num_mux++;
+ if(enabled && needscan) {
+ dn->dn_initial_scan_num_mux++;
mux_link_initial(dn, dm);
}
dvb_mux_destroy(dvb_mux_t *dm)
{
th_dvb_mux_instance_t *tdmi;
+ dvb_network_t *dn = dm->dm_dn;
lock_assert(&global_lock);
while((tdmi = LIST_FIRST(&dm->dm_tdmis)) != NULL)
dvb_tdmi_destroy(tdmi);
- if(dm->dm_scan_queue != NULL)
- TAILQ_REMOVE(dm->dm_scan_queue, dm, dm_scan_link);
+ switch(dm->dm_scan_status) {
+ case DM_SCAN_DONE:
+ break;
+ case DM_SCAN_CURRENT:
+ TAILQ_REMOVE(&dn->dn_initial_scan_current_queue, dm, dm_scan_link);
+ gtimer_disarm(&dm->dm_initial_scan_timeout);
+ if(0) // Sorry but i can't resist these whenever i get an oppertunity // andoma
+ case DM_SCAN_PENDING:
+ TAILQ_REMOVE(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link);
+ dn->dn_initial_scan_num_mux--;
+ dvb_network_schedule_initial_scan(dn);
+ break;
+ }
- if(dm->dm_table_initial)
- dm->dm_dn->dn_initial_num_mux--;
epggrab_mux_delete(dm);
free(dm->dm_local_identifier);
- free(dm->dm_uuid);
+ idnode_unlink(&dm->dm_id);
free(dm);
}
htsmsg_add_u32(m, "frequency", f->frequency);
- htsmsg_add_u32(m, "initialscan", dm->dm_table_initial);
+ htsmsg_add_u32(m, "needscan", dm->dm_scan_status != DM_SCAN_DONE);
if(dm->dm_default_authority)
htsmsg_add_str(m, "default_authority", dm->dm_default_authority);
}
hts_settings_save(m, "dvb/networks/%s/muxes/%s/config",
- dm->dm_dn->dn_uuid,
+ idnode_uuid_as_str(&dm->dm_dn->dn_id),
dm->dm_local_identifier);
htsmsg_destroy(m);
struct dvb_mux_conf dmc;
const char *s;
int r;
- unsigned int onid, tsid, enabled, initscan;
+ unsigned int onid, tsid, enabled;
memset(&dmc, 0, sizeof(dmc));
dmc.dmc_fe_params.inversion = INVERSION_AUTO;
if(htsmsg_get_u32(m, "enabled", &enabled))
enabled = 1;
- initscan = htsmsg_get_u32_or_default(m, "initialscan", 0);
dm = dvb_mux_create(dn, &dmc,
onid, tsid, htsmsg_get_str(m, "network"), NULL, enabled,
- initscan,
+ htsmsg_get_u32_or_default(m, "needscan", 1),
htsmsg_get_str(m, "uuid"));
if(dm != NULL) {
htsmsg_field_t *f;
if((l = hts_settings_load_r(1, "dvb/networks/%s/muxes",
- dn->dn_uuid)) == NULL)
+ idnode_uuid_as_str(&dn->dn_id))) == NULL)
return;
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
+ if((c = htsmsg_get_map(c, "config")) == NULL)
+ continue;
+
dvb_mux_create_by_msg(dn, c, f->hmf_name);
}
htsmsg_destroy(l);
dvb_mux_save(dm);
m = htsmsg_create_map();
- htsmsg_add_str(m, "uuid", dm->dm_uuid);
+ htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id));
htsmsg_add_u32(m, "muxid", dm->dm_transport_stream_id);
notify_by_msg("dvbMux", m);
}
dvb_mux_save(dm);
m = htsmsg_create_map();
- htsmsg_add_str(m, "uuid", dm->dm_uuid);
+ htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id));
htsmsg_add_u32(m, "onid", dm->dm_network_id);
notify_by_msg("dvbMux", m);
}
htsmsg_t *m = htsmsg_create_map();
char buf[100];
- htsmsg_add_str(m, "uuid", dm->dm_uuid);
+ htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id));
htsmsg_add_u32(m, "enabled", dm->dm_enabled);
htsmsg_add_str(m, "network", dm->dm_network_name ?: "");
s->ths_tdmi = tdmi;
LIST_INSERT_HEAD(&tdmi->tdmi_subscriptions, s, ths_tdmi_link);
- dvb_fe_tune(tdmi->tdmi_mux, "Full mux subscription");
-
+ dvb_fe_tune(tdmi->tdmi_mux, "Full mux subscription", 99999);
+ abort();
pthread_mutex_lock(&tda->tda_delivery_mutex);
streaming_target_connect(&tda->tda_streaming_pad, &s->ths_input);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
notify_reload("subscriptions");
return s;
}
+
+
+
+/**
+ *
+ */
+static htsmsg_t *
+dvb_mux_serialize(struct idnode *self, int full)
+{
+ dvb_mux_t *dm = (dvb_mux_t *)self;
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_str(m, "id", idnode_uuid_as_str(&dm->dm_id));
+
+ char buf[256];
+ dvb_mux_nicename(buf, sizeof(buf), dm);
+
+ htsmsg_add_str(m, "text", buf);
+ return m;
+}
+
+/**
+ *
+ */
+static int
+svcsortcmp(const void *A, const void *B)
+{
+ const service_t *a = *(service_t **)A;
+ const service_t *b = *(service_t **)B;
+ return (int)a->s_dvb_service_id - (int)b->s_dvb_service_id;
+}
+
+
+/**
+ *
+ */
+static idnode_t **
+dvb_mux_get_childs(struct idnode *self)
+{
+ dvb_mux_t *dm = (dvb_mux_t *)self;
+ service_t *s;
+ int cnt = 1;
+
+ LIST_FOREACH(s, &dm->dm_services, s_group_link)
+ cnt++;
+
+ idnode_t **v = malloc(sizeof(idnode_t *) * cnt);
+ cnt = 0;
+ LIST_FOREACH(s, &dm->dm_services, s_group_link)
+ v[cnt++] = (idnode_t *)s;
+ qsort(v, cnt, sizeof(idnode_t *), svcsortcmp);
+ v[cnt] = NULL;
+ return v;
+}
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <assert.h>
+
#include "tvheadend.h"
#include "packet.h"
#include "dvb.h"
#include "epggrab.h"
+#include "settings.h"
+#include "dvb_support.h"
struct dvb_network_list dvb_networks;
+static htsmsg_t *dvb_network_serialize(struct idnode *self, int full);
+static idnode_t **dvb_network_get_childs(struct idnode *self);
+
+static const idclass_t dvb_network_class = {
+ .ic_class = "dvbnetwork",
+ .ic_serialize = dvb_network_serialize,
+ .ic_get_childs = dvb_network_get_childs,
+};
+
/**
*
*/
dvb_network_t *
-dvb_network_create(int fe_type)
+dvb_network_create(int fe_type, const char *uuid)
{
+ printf("Creating network %s\n", uuid);
dvb_network_t *dn = calloc(1, sizeof(dvb_network_t));
+ if(idnode_insert(&dn->dn_id, uuid, &dvb_network_class)) {
+ free(dn);
+ return NULL;
+ }
+
+ printf("Added network %s\n", idnode_uuid_as_str(&dn->dn_id));
+
dn->dn_fe_type = fe_type;
- TAILQ_INIT(&dn->dn_initial_scan_queue);
- gtimer_arm(&dn->dn_mux_scanner_timer, dvb_network_mux_scanner, dn, 1);
+ TAILQ_INIT(&dn->dn_initial_scan_pending_queue);
+ TAILQ_INIT(&dn->dn_initial_scan_current_queue);
dn->dn_autodiscovery = fe_type != FE_QPSK;
LIST_INSERT_HEAD(&dvb_networks, dn, dn_global_link);
return dn;
}
-#if 0
- htsmsg_get_u32(c, "autodiscovery", &tda->tda_autodiscovery);
- htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid);
- htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor);
-#endif
-#if 0
+
+/**
+ *
+ */
+static htsmsg_t *
+dvb_network_serialize(struct idnode *self, int full)
+{
+ dvb_network_t *dn = (dvb_network_t *)self;
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_str(m, "id", idnode_uuid_as_str(&dn->dn_id));
+ htsmsg_add_str(m, "text", idnode_uuid_as_str(&dn->dn_id));
+ return m;
+}
+
+
+/**
+ *
+ */
+static int
+muxsortcmp(const void *A, const void *B)
+{
+ const dvb_mux_t *a = *(dvb_mux_t **)A;
+ const dvb_mux_t *b = *(dvb_mux_t **)B;
+ if(a->dm_conf.dmc_fe_params.frequency < b->dm_conf.dmc_fe_params.frequency)
+ return -1;
+ if(a->dm_conf.dmc_fe_params.frequency > b->dm_conf.dmc_fe_params.frequency)
+ return 1;
+ return a->dm_conf.dmc_polarisation - b->dm_conf.dmc_polarisation;
+}
+
+
+/**
+ *
+ */
+static idnode_t **
+dvb_network_get_childs(struct idnode *self)
+{
+ dvb_network_t *dn = (dvb_network_t *)self;
+ dvb_mux_t *dm;
+ int cnt = 1;
+ LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link)
+ cnt++;
+
+ idnode_t **v = malloc(sizeof(idnode_t *) * cnt);
+ cnt = 0;
+ LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link)
+ v[cnt++] = (idnode_t *)dm;
+ qsort(v, cnt, sizeof(idnode_t *), muxsortcmp);
+ v[cnt] = NULL;
+ return v;
+}
+
+
+/**
+ *
+ */
+static void
+dvb_network_load(htsmsg_t *m, const char *uuid)
+{
+ uint32_t fetype;
+
+ if(htsmsg_get_u32(m, "fetype", &fetype))
+ return;
+
+ dvb_network_t *dn = dvb_network_create(fetype, uuid);
+ if(dn == NULL)
+ return;
+
+ htsmsg_get_u32(m, "autodiscovery", &dn->dn_autodiscovery);
+ htsmsg_get_u32(m, "nitoid", &dn->dn_nitoid);
+ htsmsg_get_u32(m, "disable_pmt_monitor", &dn->dn_disable_pmt_monitor);
+
+ dvb_mux_load(dn);
+
+ dvb_network_schedule_initial_scan(dn);
+}
+
+#if 1
/**
*
*/
lock_assert(&global_lock);
+ htsmsg_add_u32(m, "fetype", dn->dn_fe_type);
htsmsg_add_u32(m, "autodiscovery", dn->dn_autodiscovery);
htsmsg_add_u32(m, "nitoid", dn->dn_nitoid);
htsmsg_add_u32(m, "disable_pmt_monitor", dn->dn_disable_pmt_monitor);
- abort();
+
+ hts_settings_save(m, "dvb/networks/%s/config",
+ idnode_uuid_as_str(&dn->dn_id));
+ htsmsg_destroy(m);
}
#endif
+
#if 0
/**
*
#endif
+
/**
*
*/
-void
-dvb_network_mux_scanner(void *aux)
+static void
+dvb_network_initial_scan(void *aux)
{
dvb_network_t *dn = aux;
dvb_mux_t *dm;
- // default period
- gtimer_arm(&dn->dn_mux_scanner_timer, dvb_network_mux_scanner, dn, 20);
-
-#if 0
- /* No muxes */
- if(LIST_FIRST(&dn->dn_mux_instances) == NULL) {
- dvb_adapter_poweroff(tda);
- return;
+ while((dm = TAILQ_FIRST(&dn->dn_initial_scan_pending_queue)) != NULL) {
+ assert(dm->dm_scan_status == DM_SCAN_PENDING);
+ if(dvb_fe_tune(dm, "initial scan", 1))
+ break;
+ assert(dm->dm_scan_status == DM_SCAN_CURRENT);
}
-#endif
-#if 0
- /* Someone is actively using */
- if(service_compute_weight(&tda->tda_transports) > 0)
- return;
-#endif
-#if 0
- if(tda->tda_mux_current != NULL &&
- LIST_FIRST(&tda->tda_mux_current->tdmi_subscriptions) != NULL)
- return; // Someone is doing full mux dump
-#endif
+ gtimer_arm(&dn->dn_initial_scan_timer, dvb_network_initial_scan, dn, 10);
+}
- /* Check if we have muxes pending for quickscan, if so, choose them */
- if((dm = TAILQ_FIRST(&dn->dn_initial_scan_queue)) != NULL) {
- dvb_fe_tune(dm, "Initial autoscan");
- return;
- }
+/**
+ *
+ */
+void
+dvb_network_schedule_initial_scan(dvb_network_t *dn)
+{
+ gtimer_arm(&dn->dn_initial_scan_timer, dvb_network_initial_scan, dn, 0);
+}
- /* Check EPG */
- if (dn->dn_mux_epg) {
- // timeout anything not complete
- epggrab_mux_stop(dn->dn_mux_epg, 1);
- dn->dn_mux_epg = NULL; // skip this time
- } else {
- dn->dn_mux_epg = epggrab_mux_next(dn);
+/**
+ *
+ */
+void
+dvb_network_init(void)
+{
+ htsmsg_t *l, *c;
+ htsmsg_field_t *f;
+
+ if(0) {
+ dvb_network_save(dvb_network_create(FE_QAM, NULL));
+ exit(0);
}
- /* EPG */
- if (dn->dn_mux_epg) {
- int period = epggrab_mux_period(dn->dn_mux_epg);
- if (period > 20)
- gtimer_arm(&dn->dn_mux_scanner_timer,
- dvb_network_mux_scanner, dn, period);
- dvb_fe_tune(dn->dn_mux_epg, "EPG scan");
+ if((l = hts_settings_load_r(1, "dvb/networks")) == NULL)
return;
+
+ htsmsg_print(l);
+
+ HTSMSG_FOREACH(f, l) {
+ if((c = htsmsg_get_map_by_field(f)) == NULL)
+ continue;
+
+ if((c = htsmsg_get_map(c, "config")) == NULL)
+ continue;
+
+ dvb_network_load(c, f->hmf_name);
}
+ htsmsg_destroy(l);
+}
-#if 0
- /* Ensure we stop current mux and power off (if required) */
- if (tda->tda_mux_current)
- dvb_fe_stop(tda->tda_mux_current, 0);
-#endif
+
+/**
+ *
+ */
+idnode_t **
+dvb_network_root(void)
+{
+ dvb_network_t *dn;
+ int cnt = 1;
+ LIST_FOREACH(dn, &dvb_networks, dn_global_link)
+ cnt++;
+
+ idnode_t **v = malloc(sizeof(idnode_t *) * cnt);
+ cnt = 0;
+ LIST_FOREACH(dn, &dvb_networks, dn_global_link)
+ v[cnt++] = &dn->dn_id;
+ v[cnt] = NULL;
+ return v;
}
#include "dvb_support.h"
#include "notify.h"
-
-
-/**
- *
- */
-
-
-
-
+static htsmsg_t *dvb_service_serialize(service_t *s, int full);
/**
return r;
#endif
- return SM_CODE_NO_HW_ATTACHED;
+ return SM_CODE_NO_FREE_ADAPTER;
}
dvb_mux_t *dm = t->s_dvb_mux;
hts_settings_save(m, "dvb/networks/%s/muxes/%s/services/%04x",
- dm->dm_dn->dn_uuid,
+ idnode_uuid_as_str(&dm->dm_dn->dn_id),
dm->dm_local_identifier,
t->s_dvb_service_id);
lock_assert(&global_lock);
l = hts_settings_load("dvb/networks/%s/muxes/%s/services",
- dm->dm_dn->dn_uuid, dm->dm_local_identifier);
+ idnode_uuid_as_str(&dm->dm_dn->dn_id),
+ dm->dm_local_identifier);
if(l == NULL)
return;
t->s_config_save = dvb_service_save;
t->s_setsourceinfo = dvb_service_setsourceinfo;
t->s_grace_period = dvb_grace_period;
+ t->s_serialize = dvb_service_serialize;
t->s_dvb_mux = dm;
LIST_INSERT_HEAD(&dm->dm_services, t, s_group_link);
/**
*
*/
-htsmsg_t *
-dvb_service_build_msg(service_t *t)
+static htsmsg_t *
+dvb_service_serialize(service_t *s, int full)
{
- dvb_mux_t *dm = t->s_dvb_mux;
+ dvb_mux_t *dm = s->s_dvb_mux;
htsmsg_t *m = htsmsg_create_map();
char buf[100];
-
- htsmsg_add_str(m, "uuid", t->s_uuid);
- htsmsg_add_u32(m, "enabled", t->s_enabled);
- htsmsg_add_u32(m, "channel", t->s_channel_number);
- htsmsg_add_u32(m, "sid", t->s_dvb_service_id);
- htsmsg_add_u32(m, "pmt", t->s_pmt_pid);
- htsmsg_add_u32(m, "pcr", t->s_pcr_pid);
-
- htsmsg_add_str(m, "type", service_servicetype_txt(t));
+ htsmsg_add_str(m, "id", idnode_uuid_as_str(&s->s_id));
+
+ snprintf(buf, sizeof(buf), "%s (0x%04x)",
+ s->s_svcname ?: "<noname>", s->s_dvb_service_id);
+ htsmsg_add_str(m, "text", buf);
+
+
+ htsmsg_add_u32(m, "enabled", s->s_enabled);
+ htsmsg_add_u32(m, "channel", s->s_channel_number);
+
+ htsmsg_add_u32(m, "sid", s->s_dvb_service_id);
+ htsmsg_add_u32(m, "pmt", s->s_pmt_pid);
+ htsmsg_add_u32(m, "pcr", s->s_pcr_pid);
- htsmsg_add_str(m, "svcname", t->s_svcname ?: "");
- htsmsg_add_str(m, "provider", t->s_provider ?: "");
+ htsmsg_add_str(m, "type", service_servicetype_txt(s));
+
+ htsmsg_add_str(m, "svcname", s->s_svcname ?: "");
+ htsmsg_add_str(m, "provider", s->s_provider ?: "");
htsmsg_add_str(m, "network", dm->dm_network_name ?: "");
dvb_mux_nicefreq(buf, sizeof(buf), dm);
htsmsg_add_str(m, "mux", buf);
- if(t->s_ch != NULL)
- htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
+ if(s->s_ch != NULL)
+ htsmsg_add_str(m, "channelname", s->s_ch->ch_name);
- if(t->s_dvb_charset != NULL)
- htsmsg_add_str(m, "dvb_charset", t->s_dvb_charset);
+ if(s->s_dvb_charset != NULL)
+ htsmsg_add_str(m, "dvb_charset", s->s_dvb_charset);
- htsmsg_add_u32(m, "dvb_eit_enable", t->s_dvb_eit_enable);
+ htsmsg_add_u32(m, "dvb_eit_enable", s->s_dvb_eit_enable);
return m;
}
th_dvb_table_t *tdt;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
dvb_mux_t *dm = tdmi->tdmi_mux;
- dvb_network_t *dn = dm->dm_dn;
char buf[100];
- if(!dm->dm_table_initial)
+ if(dm->dm_scan_status == DM_SCAN_DONE)
return;
LIST_FOREACH(tdt, &dm->dm_tables, tdt_link)
if((tdt->tdt_flags & TDT_QUICKREQ) && tdt->tdt_count == 0)
return;
- dm->dm_table_initial = 0;
- dn->dn_initial_num_mux--;
dvb_mux_save(dm);
-
dvb_mux_nicename(buf, sizeof(buf), dm);
tvhlog(LOG_DEBUG, "dvb", "\"%s\" initial scan completed for \"%s\"",
tda->tda_rootpath, buf);
- dvb_network_mux_scanner(dn);
+ dvb_mux_initial_scan_done(dm);
}
TAILQ_FOREACH(ota, &dm->dm_epg_grab, dm_link) {
if (ota->is_reg && ota->state == EPGGRAB_OTA_MUX_RUNNING) break;
}
-
+#if 0 // XXX(dvbreorg)
/* All complete (bring timer forward) */
if (!ota) {
dvb_network_t *dn = dm->dm_dn;
gtimer_arm(&dn->dn_mux_scanner_timer,
dvb_network_mux_scanner, dn, 20);
}
+#endif
}
}
for (i = 0; i < ret; i++) {
(*list)[i] = calloc(1, sizeof(fb_dirent));
strcpy((*list)[i]->name, de[i]->d_name);
- (*list)[i]->type = FB_DIRECT;
+ (*list)[i]->type = de[i]->d_type == DT_DIR ? FB_DIR : FB_FILE;
free(de[i]);
}
free(de);
--- /dev/null
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "idnode.h"
+
+static int randfd = 0;
+
+RB_HEAD(idnode_tree, idnode);
+
+static struct idnode_tree idnodes;
+
+/**
+ *
+ */
+static int
+hexnibble(char c)
+{
+ switch(c) {
+ case '0' ... '9': return c - '0';
+ case 'a' ... 'f': return c - 'a' + 10;
+ case 'A' ... 'F': return c - 'A' + 10;
+ default:
+ return -1;
+ }
+}
+
+
+/**
+ *
+ */
+static int
+hex2bin(uint8_t *buf, size_t buflen, const char *str)
+{
+ int hi, lo;
+
+ while(*str) {
+ if(buflen == 0)
+ return -1;
+ if((hi = hexnibble(*str++)) == -1)
+ return -1;
+ if((lo = hexnibble(*str++)) == -1)
+ return -1;
+
+ *buf++ = hi << 4 | lo;
+ buflen--;
+ }
+ return 0;
+}
+
+
+/**
+ *
+ */
+static void
+bin2hex(char *dst, size_t dstlen, const uint8_t *src, size_t srclen)
+{
+ while(dstlen > 2 && srclen > 0) {
+ *dst++ = "0123456789abcdef"[*src >> 4];
+ *dst++ = "0123456789abcdef"[*src & 0xf];
+ src++;
+ srclen--;
+ dstlen -= 2;
+ }
+ *dst = 0;
+}
+
+
+/**
+ *
+ */
+void
+idnode_init(void)
+{
+ randfd = open("/dev/urandom", O_RDONLY);
+ if(randfd == -1)
+ exit(1);
+}
+
+
+/**
+ *
+ */
+static int
+in_cmp(const idnode_t *a, const idnode_t *b)
+{
+ return memcmp(a->in_uuid, b->in_uuid, 16);
+}
+
+
+/**
+ *
+ */
+int
+idnode_insert(idnode_t *in, const char *uuid, const idclass_t *class)
+{
+ idnode_t *c;
+ if(uuid == NULL) {
+ if(read(randfd, in->in_uuid, 16) != 16) {
+ perror("read(random for uuid)");
+ exit(1);
+ }
+ } else {
+ if(hex2bin(in->in_uuid, 16, uuid))
+ return -1;
+ }
+
+ in->in_class = class;
+
+ c = RB_INSERT_SORTED(&idnodes, in, in_link, in_cmp);
+ if(c != NULL) {
+ fprintf(stderr, "Id node collision\n");
+ abort();
+ }
+ return 0;
+}
+
+
+/**
+ *
+ */
+const char *
+idnode_uuid_as_str(const idnode_t *in)
+{
+ static char ret[16][33];
+ static int p;
+ char *b = ret[p];
+ bin2hex(b, 33, in->in_uuid, 16);
+ p = (p + 1) & 15;
+ return b;
+}
+
+
+/**
+ *
+ */
+idnode_t *
+idnode_find(const char *uuid)
+{
+ idnode_t skel, *r;
+
+ if(hex2bin(skel.in_uuid, 16, uuid))
+ return NULL;
+ r = RB_FIND(&idnodes, &skel, in_link, in_cmp);
+ return r;
+}
+
+
+/**
+ *
+ */
+void
+idnode_unlink(idnode_t *in)
+{
+ RB_REMOVE(&idnodes, in, in_link);
+}
--- /dev/null
+#pragma once
+
+#include "tvheadend.h"
+
+struct htsmsg;
+struct idnode;
+
+typedef struct idclass {
+ const char *ic_class;
+ struct htsmsg *(*ic_serialize)(struct idnode *self, int full);
+ struct idnode **(*ic_get_childs)(struct idnode *self);
+} idclass_t;
+
+
+typedef struct idnode {
+ uint8_t in_uuid[16];
+ RB_ENTRY(idnode) in_link;
+ const idclass_t *in_class;
+} idnode_t;
+
+void idnode_init(void);
+
+int idnode_insert(idnode_t *in, const char *uuid, const idclass_t *class);
+
+const char *idnode_uuid_as_str(const idnode_t *in);
+
+idnode_t *idnode_find(const char *uuid);
+
+void idnode_unlink(idnode_t *in);
fd = tvh_socket(AF_INET6, SOCK_DGRAM, 0);
}
if(fd == -1) {
- tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_uuid);
+ tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_nicename);
return -1;
}
ifr.ifr_name[IFNAMSIZ - 1] = 0;
if(ioctl(fd, SIOCGIFINDEX, &ifr)) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s",
- t->s_uuid, t->s_iptv_iface);
+ t->s_nicename, t->s_iptv_iface);
close(fd);
return -1;
}
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m, sizeof(struct ip_mreqn));
if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s",
- t->s_uuid, inet_ntoa(sin.sin_addr), t->s_iptv_port,
+ t->s_nicename, inet_ntoa(sin.sin_addr), t->s_iptv_port,
strerror(errno));
close(fd);
return -1;
if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &m,
sizeof(struct ip_mreqn)) == -1) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s",
- t->s_uuid, inet_ntoa(m.imr_multiaddr), strerror(errno));
+ t->s_nicename, inet_ntoa(m.imr_multiaddr), strerror(errno));
close(fd);
return -1;
}
if(bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) {
inet_ntop(AF_INET6, &sin6.sin6_addr, straddr, sizeof(straddr));
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s",
- t->s_uuid, straddr, t->s_iptv_port,
+ t->s_nicename, straddr, t->s_iptv_port,
strerror(errno));
close(fd);
return -1;
inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr,
straddr, sizeof(straddr));
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s",
- t->s_uuid, straddr, strerror(errno));
+ t->s_nicename, straddr, strerror(errno));
close(fd);
return -1;
}
ev.data.fd = fd;
if(epoll_ctl(iptv_epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot add to epoll set -- %s",
- t->s_uuid, strerror(errno));
+ t->s_nicename, strerror(errno));
close(fd);
return -1;
}
ifr.ifr_name[IFNAMSIZ - 1] = 0;
if(ioctl(t->s_iptv_fd, SIOCGIFINDEX, &ifr)) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s",
- t->s_uuid, t->s_iptv_iface);
+ t->s_nicename, t->s_iptv_iface);
}
if(t->s_iptv_group.s_addr != 0) {
if(setsockopt(t->s_iptv_fd, SOL_IP, IP_DROP_MEMBERSHIP, &m,
sizeof(struct ip_mreqn)) == -1) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s",
- t->s_uuid, inet_ntoa(m.imr_multiaddr), strerror(errno));
+ t->s_nicename, inet_ntoa(m.imr_multiaddr), strerror(errno));
}
} else {
char straddr[INET6_ADDRSTRLEN];
straddr, sizeof(straddr));
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s",
- t->s_uuid, straddr, strerror(errno));
+ t->s_nicename, straddr, strerror(errno));
}
psi_save_service_settings(m, t);
pthread_mutex_unlock(&t->s_stream_mutex);
- hts_settings_save(m, "iptvservices/%s",
- t->s_uuid);
+ abort(); // XXX(dvbreorg);
+
+ // hts_settings_save(m, "iptvservices/%s", t->s_uuid);
htsmsg_destroy(m);
}
static void
iptv_service_dtor(service_t *t)
{
- hts_settings_remove("iptvservices/%s", t->s_uuid);
+ abort(); // XXX(dvbreorg);
+ // hts_settings_remove("iptvservices/%s", t->s_uuid);
}
return NULL;
LIST_FOREACH(t, &iptv_all_services, s_group_link)
- if(!strcmp(t->s_uuid, id))
+ if(!strcmp(t->s_nicename, id)) // XXX(dvbreorg)
return t;
}
#include "ffdecsa/FFdecsa.h"
#include "muxes.h"
#include "config2.h"
+#include "idnode.h"
int running;
time_t dispatch_clock;
access_init(createdefault);
tcp_server_init();
+
+ idnode_init();
+
#if ENABLE_LINUXDVB
dvb_init(adapter_mask, dvb_rawts_input);
#endif
#include "htsp_server.h"
#include "lang_codes.h"
-#define SERVICE_HASH_WIDTH 101
+static void service_data_timeout(void *aux);
-static struct service_list servicehash[SERVICE_HASH_WIDTH];
+static htsmsg_t *service_serialize(struct idnode *self, int full);
-static void service_data_timeout(void *aux);
+static const idclass_t service_class = {
+ .ic_class = "service",
+ .ic_serialize = service_serialize,
+};
/**
*
}
LIST_REMOVE(t, s_group_link);
- LIST_REMOVE(t, s_hash_link);
-
+
+ idnode_unlink(&t->s_id);
+
if(t->s_status != SERVICE_IDLE)
service_stop(t);
t->s_status = SERVICE_ZOMBIE;
- free(t->s_uuid);
free(t->s_svcname);
free(t->s_provider);
free(t->s_dvb_charset);
service_t *
service_create(const char *uuid, int source_type)
{
- unsigned int hash = tvh_strhash(uuid, SERVICE_HASH_WIDTH);
service_t *t = calloc(1, sizeof(service_t));
lock_assert(&global_lock);
pthread_mutex_init(&t->s_stream_mutex, NULL);
pthread_cond_init(&t->s_tss_cond, NULL);
- t->s_uuid = strdup(uuid);
t->s_source_type = source_type;
t->s_refcount = 1;
t->s_enabled = 1;
streaming_pad_init(&t->s_streaming_pad);
- LIST_INSERT_HEAD(&servicehash[hash], t, s_hash_link);
+ idnode_insert(&t->s_id, uuid, &service_class);
+
return t;
}
service_t *
service_find_by_identifier(const char *identifier)
{
- service_t *t;
- unsigned int hash = tvh_strhash(identifier, SERVICE_HASH_WIDTH);
-
- lock_assert(&global_lock);
-
- LIST_FOREACH(t, &servicehash[hash], s_hash_link)
- if(!strcmp(t->s_uuid, identifier))
- break;
- return t;
+ idnode_t *id = idnode_find(identifier);
+ return id->in_class == &service_class ? (service_t *)id : NULL;
}
}
return ret;
}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+service_serialize(struct idnode *self, int full)
+{
+ service_t *s = (service_t *)self;
+ return s->s_serialize(s, full);
+}
#define PID_TELETEXT_BASE 0x2000
#include "htsmsg.h"
-
+#include "idnode.h"
/**
*
*/
typedef struct service {
-
- LIST_ENTRY(service) s_hash_link;
+ idnode_t s_id;
enum {
/**
void (*s_dtor)(struct service *t);
+ htsmsg_t *(*s_serialize)(struct service *s, int full);
+
/*
* Per source type structs
*/
struct dvb_mux *s_dvb_mux;
- /**
- * Unique identifer
- */
- char *s_uuid;
-
/**
* Name usable for displaying to user
*/
case SM_CODE_SUBSCRIPTION_OVERRIDDEN:
return "Subscription overridden";
- case SM_CODE_NO_HW_ATTACHED:
- return "No hardware present";
+ case SM_CODE_NO_FREE_ADAPTER:
+ return "No free adapter";
case SM_CODE_MUX_NOT_ENABLED:
return "Mux not enabled";
case SM_CODE_NOT_FREE:
#define SM_CODE_SOURCE_DELETED 102
#define SM_CODE_SUBSCRIPTION_OVERRIDDEN 103
-#define SM_CODE_NO_HW_ATTACHED 200
+#define SM_CODE_NO_FREE_ADAPTER 200
#define SM_CODE_MUX_NOT_ENABLED 201
#define SM_CODE_NOT_FREE 202
#define SM_CODE_TUNING_FAILED 203
htsmsg_t *r = htsmsg_create_map();
char abuf[INET_ADDRSTRLEN];
char abuf6[INET6_ADDRSTRLEN];
- htsmsg_add_str(r, "id", t->s_uuid);
+ // htsmsg_add_str(r, "id", t->s_uuid); // XXX(dvbreorg)
htsmsg_add_str(r, "channelname", t->s_ch ? t->s_ch->ch_name : "");
htsmsg_add_str(r, "interface", t->s_iptv_iface ?: "");
array = htsmsg_create_list();
#if ENABLE_LINUXDVB
- // extjs_list_dvb_adapters(array);
+ extjs_list_dvb_adapters(array);
#endif
#if ENABLE_V4L
#include "dvb/dvb_preconf.h"
#include "dvr/dvr.h"
-#if 0
if(s == NULL || a == NULL)
return HTTP_STATUS_BAD_REQUEST;
-
+
pthread_mutex_lock(&global_lock);
if(http_access_verify(hc, ACCESS_ADMIN)) {
return 0;
}
+
/**
*
*/
const char *op = http_arg_get(&hc->hc_req_args, "op");
const char *sibling = http_arg_get(&hc->hc_req_args, "sibling");
const char *s;
- th_dvb_mux_instance_t *tdmi;
- service_t *t;
pthread_mutex_lock(&global_lock);
htsmsg_add_str(r, "id", tda->tda_identifier);
htsmsg_add_str(r, "device", tda->tda_rootpath ?: "No hardware attached");
htsmsg_add_str(r, "name", tda->tda_displayname);
- htsmsg_add_u32(r, "automux", tda->tda_autodiscovery);
htsmsg_add_u32(r, "skip_initialscan", tda->tda_skip_initialscan);
htsmsg_add_u32(r, "idleclose", tda->tda_idleclose);
htsmsg_add_u32(r, "skip_checksubscr", tda->tda_skip_checksubscr);
htsmsg_add_u32(r, "qmon", tda->tda_qmon);
htsmsg_add_u32(r, "poweroff", tda->tda_poweroff);
htsmsg_add_u32(r, "sidtochan", tda->tda_sidtochan);
- htsmsg_add_u32(r, "nitoid", tda->tda_nitoid);
- htsmsg_add_u32(r, "disable_pmt_monitor", tda->tda_disable_pmt_monitor);
htsmsg_add_u32(r, "full_mux_rx", tda->tda_full_mux_rx+1);
htsmsg_add_str(r, "diseqcversion",
((const char *[]){"DiSEqC 1.0 / 2.0",
if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL)
dvb_adapter_set_displayname(tda, s);
- s = http_arg_get(&hc->hc_req_args, "automux");
- dvb_adapter_set_auto_discovery(tda, !!s);
-
s = http_arg_get(&hc->hc_req_args, "skip_initialscan");
dvb_adapter_set_skip_initialscan(tda, !!s);
s = http_arg_get(&hc->hc_req_args, "sidtochan");
dvb_adapter_set_sidtochan(tda, !!s);
- s = http_arg_get(&hc->hc_req_args, "disable_pmt_monitor");
- dvb_adapter_set_disable_pmt_monitor(tda, !!s);
-
s = http_arg_get(&hc->hc_req_args, "full_mux_rx");
dvb_adapter_set_full_mux_rx(tda, atoi(s)-1);
- if((s = http_arg_get(&hc->hc_req_args, "nitoid")) != NULL)
- dvb_adapter_set_nitoid(tda, atoi(s));
-
if((s = http_arg_get(&hc->hc_req_args, "diseqcversion")) != NULL) {
if(!strcmp(s, "DiSEqC 1.0 / 2.0"))
dvb_adapter_set_diseqc_version(tda, 0);
// sc = http_arg_get(&hc->hc_req_args, "satconf");
if((s = http_arg_get(&hc->hc_req_args, "network")) != NULL)
- dvb_mux_preconf_add_network(tda, s);
+ dvb_mux_preconf_add_network(tda->tda_dn, s);
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
-
+#if 0
} else if(!strcmp(op, "serviceprobe")) {
tvhlog(LOG_NOTICE, "web interface",
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
+#endif
} else {
pthread_mutex_unlock(&global_lock);
return 0;
}
+#if 0
/**
*
}
#endif
+#endif
+
/**
*
*/
htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda));
}
-#endif
/**
*
return HTTP_STATUS_UNAUTHORIZED;
}
- printf("s=%s\n", s);
-
out = htsmsg_create_list();
+ idnode_t **v;
+
if(!strcmp(s, "root")) {
- htsmsg_t *n = htsmsg_create_map();
- htsmsg_add_str(n, "text", "Network1");
- htsmsg_add_str(n, "id", "net/1");
- htsmsg_add_str(n, "cls", "folder");
- htsmsg_add_str(n, "iconCls", "iptv");
- htsmsg_add_msg(out, NULL, n);
-
- n = htsmsg_create_map();
- htsmsg_add_str(n, "text", "Network2");
- htsmsg_add_str(n, "id", "net/2");
- htsmsg_add_str(n, "cls", "folder");
- htsmsg_add_msg(out, NULL, n);
+ v = dvb_network_root();
+ } else {
+ idnode_t *n = idnode_find(s);
+ v = n != NULL && n->in_class->ic_get_childs != NULL ?
+ n->in_class->ic_get_childs(n) : NULL;
}
+ int i;
+ for(i = 0; v[i] != NULL; i++) {
+ htsmsg_t *m = v[i]->in_class->ic_serialize(v[i], 0);
+ if(v[i]->in_class->ic_get_childs == NULL)
+ htsmsg_add_u32(m, "leaf", 1);
+ htsmsg_add_msg(out, NULL, m);
+ }
pthread_mutex_unlock(&global_lock);
+ free(v);
+
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");
{
http_path_add("/dvb/networks",
NULL, extjs_dvbnetworks, ACCESS_WEB_INTERFACE);
-#if 0
http_path_add("/dvb/locations",
NULL, extjs_dvblocations, ACCESS_WEB_INTERFACE);
-
http_path_add("/dvb/adapter",
NULL, extjs_dvbadapter, ACCESS_ADMIN);
+#if 0
+
http_path_add("/dvb/muxes",
NULL, extjs_dvbmuxes, ACCESS_ADMIN);
--- /dev/null
+/**
+ *
+ */
+tvheadend.dvb_networks = function() {
+
+ var loader = new Ext.tree.TreeLoader({
+ dataUrl: 'dvb/networks'
+ });
+
+ var tree = new Ext.tree.TreePanel({
+ title: 'DVB Networks',
+ loader: loader,
+ root : new Ext.tree.AsyncTreeNode({
+ id : 'root',
+ text: 'DVB Networks'
+ })
+ });
+
+
+ tree.on('render', function() {
+ tree.getRootNode().expand();
+ });
+
+ return tree;
+}
streaming_queue_init(&sq, SMT_PACKET);
s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st);
- name = strdupa(tdmi->tdmi_mux->dm_uuid);
+ name = "foo"; // strdupa(tdmi->tdmi_mux->dm_uuid); XXX(dvbreorg)
pthread_mutex_unlock(&global_lock);
http_stream_run(hc, &sq, name, MC_PASS);
pthread_mutex_lock(&global_lock);