Merge remote-tracking branch 'origin/dvbnetworks' into feature/dvb-rewrite

Conflicts:
	Makefile
	src/capmt.c
	src/dvb/dvb.h
	src/dvb/dvb_adapter.c
	src/dvb/dvb_fe.c
	src/dvb/dvb_multiplex.c
	src/dvb/dvb_preconf.c
	src/dvb/dvb_service.c
	src/dvb/dvb_tables.c
	src/epggrab/module/eit.c
	src/epggrab/otamux.c
	src/iptv_input.c
	src/main.c
	src/rawtsinput.c
	src/service.c
	src/serviceprobe.c
	src/settings.c
	src/tvheadend.h
	src/v4l.c
	src/webui/extjs.c
	src/webui/extjs_dvb.c
	src/webui/static/app/dvb.js
	src/webui/static/app/tvadapters.js
	src/webui/static/app/tvheadend.js
	src/webui/webui.c
	src/webui/webui.h
This commit is contained in:
Adam Sutton 2013-04-23 17:31:24 +01:00
commit 139b654715
68 changed files with 3578 additions and 2878 deletions

View file

@ -68,6 +68,9 @@ endif
SRCS = src/version.c \
src/main.c \
src/tvhlog.c \
src/idnode.c \
src/prop.c \
src/tvadapters.c \
src/utils.c \
src/wrappers.c \
src/access.c \
@ -160,12 +163,15 @@ SRCS-${CONFIG_TIMESHIFT} += \
# DVB
SRCS-${CONFIG_LINUXDVB} += \
src/dvb/dvb.c \
src/dvb/dvb_adapter.c \
src/dvb/dvb_support.c \
src/dvb/dvb_charset.c \
src/dvb/dvb_fe.c \
src/dvb/dvb_tables.c \
src/dvb/diseqc.c \
src/dvb/dvb_adapter.c \
src/dvb/dvb_linux.c \
src/dvb/dvb_hardware.c \
src/dvb/dvb_network.c \
src/dvb/dvb_multiplex.c \
src/dvb/dvb_service.c \
src/dvb/dvb_preconf.c \

View file

@ -617,7 +617,7 @@ capmt_thread(void *aux)
if (!capmt->capmt_oscam) {
bind_ok = capmt_create_udp_socket(&capmt->capmt_sock_ca0[0], capmt->capmt_port);
} else {
#if ENABLE_LINUXDVB
#if TODO_FIX_THIS //ENABLE_LINUXDVB
th_dvb_adapter_t *tda;
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
if (!tda->tda_enabled)
@ -677,7 +677,7 @@ capmt_table_input(struct th_descrambler *td, struct service *t,
{
capmt_service_t *ct = (capmt_service_t *)td;
capmt_t *capmt = ct->ct_capmt;
int adapter_num = t->s_dvb_mux_instance->tdmi_adapter->tda_adapter_num;
int adapter_num = t->s_dvb_mux->dm_current_tdmi->tdmi_adapter->tda_adapter_num;
int total_caids = 0, current_caid = 0;
caid_t *c;
@ -734,9 +734,12 @@ capmt_table_input(struct th_descrambler *td, struct service *t,
uint16_t sid = t->s_dvb_service_id;
uint16_t ecmpid = st->es_pid;
#if TODO_FIX_THIS
uint16_t transponder = t->s_dvb_mux_instance->tdmi_transport_stream_id;
uint16_t onid = t->s_dvb_mux_instance->tdmi_network_id;
#else
uint16_t transponder = 0, onid = 0;
#endif
/* don't do too much requests */
if (current_caid == total_caids && caid != ct->ct_caid_last)
@ -1020,14 +1023,15 @@ capmt_service_start(service_t *t)
if (!capmt->capmt_enabled)
continue;
#if TODO_FIX_THIS
if (!(t->s_dvb_mux_instance && t->s_dvb_mux_instance->tdmi_adapter))
continue;
#endif
tvhlog(LOG_INFO, "capmt",
"Starting capmt server for service \"%s\" on tuner %d",
t->s_svcname,
t->s_dvb_mux_instance->tdmi_adapter->tda_adapter_num);
t->s_dvb_mux->dm_current_tdmi->tdmi_adapter->tda_adapter_num);
elementary_stream_t *st;

View file

@ -25,6 +25,8 @@
void
dvb_init(uint32_t adapter_mask, const char *rawfile)
{
TAILQ_INIT(&dvb_adapters);
dvb_charset_init();
dvb_adapter_init(adapter_mask, rawfile);
dvb_network_init();
dvb_linux_init();
}

View file

@ -24,6 +24,7 @@
#include <pthread.h>
#include "htsmsg.h"
#include "psi.h"
#include "idnode.h"
struct service;
struct th_dvb_table;
@ -41,11 +42,14 @@ struct th_dvb_mux_instance;
(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);
LIST_HEAD(dvb_mux_list, dvb_mux);
TAILQ_HEAD(dvb_mux_queue, dvb_mux);
LIST_HEAD(dvb_network_list, dvb_network);
TAILQ_HEAD(dvb_hardware_queue, dvb_hardware);
/**
* Satconf
@ -59,12 +63,9 @@ typedef struct dvb_satconf {
char *sc_comment;
char *sc_lnb;
struct th_dvb_mux_instance_list sc_tdmis;
} dvb_satconf_t;
enum polarisation {
POLARISATION_HORIZONTAL = 0x00,
POLARISATION_VERTICAL = 0x01,
@ -82,7 +83,7 @@ typedef struct dvb_frontend_parameters dvb_frontend_parameters_t;
typedef struct dvb_mux_conf {
dvb_frontend_parameters_t dmc_fe_params;
int dmc_polarisation;
dvb_satconf_t *dmc_satconf;
// dvb_satconf_t *dmc_satconf;
#if DVB_API_VERSION >= 5
fe_modulation_t dmc_fe_modulation;
fe_delivery_system_t dmc_fe_delsys;
@ -91,17 +92,98 @@ typedef struct dvb_mux_conf {
} dvb_mux_conf_t;
/**
*
*/
typedef struct dvb_network {
idnode_t dn_id;
LIST_ENTRY(dvb_network) dn_global_link;
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;
int dn_fe_type; // Frontend types for this network (FE_QPSK, etc)
struct dvb_mux_list dn_muxes;
uint32_t dn_disable_pmt_monitor;
uint32_t dn_autodiscovery;
uint32_t dn_nitoid;
uint32_t dn_skip_checksubscr;
char *dn_name; // User configured name
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;
struct service_list dm_services;
dvb_mux_conf_t dm_conf;
uint32_t dm_network_id;
uint16_t dm_transport_stream_id;
char *dm_network_name; /* Name of network, from NIT table */
char *dm_default_authority;
TAILQ_HEAD(, epggrab_ota_mux) dm_epg_grab;
gtimer_t dm_initial_scan_timeout;
TAILQ_ENTRY(dvb_mux) dm_scan_link;
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;
struct th_dvb_mux_instance *dm_current_tdmi;
struct th_dvb_mux_instance_list dm_tdmis;
// Derived from dm_conf (more or less)
char *dm_local_identifier;
int dm_enabled;
} dvb_mux_t;
/**
* DVB Mux instance
*/
typedef struct th_dvb_mux_instance {
RB_ENTRY(th_dvb_mux_instance) tdmi_global_link;
LIST_ENTRY(th_dvb_mux_instance) tdmi_adapter_link;
LIST_ENTRY(th_dvb_mux_instance) tdmi_adapter_hash_link;
dvb_mux_t *tdmi_mux;
LIST_ENTRY(th_dvb_mux_instance) tdmi_mux_link;
struct th_dvb_adapter *tdmi_adapter;
LIST_ENTRY(th_dvb_mux_instance) tdmi_adapter_link;
uint16_t tdmi_signal;
uint32_t tdmi_ber, tdmi_unc;
@ -112,14 +194,6 @@ typedef struct th_dvb_mux_instance {
uint32_t tdmi_fec_err_histogram[TDMI_FEC_ERR_HISTOGRAM_SIZE];
int tdmi_fec_err_ptr;
time_t tdmi_time;
LIST_HEAD(, th_dvb_table) tdmi_tables;
int tdmi_num_tables;
TAILQ_HEAD(, th_dvb_table) tdmi_table_queue;
int tdmi_table_initial;
enum {
TDMI_FE_UNKNOWN,
@ -133,30 +207,8 @@ typedef struct th_dvb_mux_instance {
int tdmi_quality;
int tdmi_enabled;
time_t tdmi_got_adapter;
time_t tdmi_lost_adapter;
dvb_mux_conf_t tdmi_conf;
/* Linked if tdmi_conf.dmc_satconf != NULL */
LIST_ENTRY(th_dvb_mux_instance) tdmi_satconf_link;
uint32_t tdmi_network_id;
uint16_t tdmi_transport_stream_id;
char *tdmi_identifier;
char *tdmi_network; /* Name of network, from NIT table */
char *tdmi_default_authority;
struct service_list tdmi_transports; /* via s_mux_link */
TAILQ_ENTRY(th_dvb_mux_instance) tdmi_scan_link;
struct th_dvb_mux_instance_queue *tdmi_scan_queue;
TAILQ_HEAD(, epggrab_ota_mux) tdmi_epg_grab;
int tdmi_tune_failed; // Adapter failed to tune this frequency
// Don't try again
struct th_subscription_list tdmi_subscriptions;
@ -165,6 +217,7 @@ typedef struct th_dvb_mux_instance {
/**
* When in raw mode we need to enqueue raw TS packet
* to a different thread because we need to hold
@ -179,61 +232,85 @@ typedef struct dvb_table_feed {
/**
* dvb_hardware refers to any kind of DVB hardware
*
* This includes
*
* DVB Adapters (On linux: /dev/dvb/adapterX)
* DVB Frontends (On linux: /dev/dvb/adapterX/frontendX)
* DVB-S LNBs
* Diseqc equipment (Switches, motors, etc)
*
*/
typedef struct dvb_hardware {
idnode_t dh_id;
// Hierarcy
struct dvb_hardware *dh_parent;
TAILQ_ENTRY(dvb_hardware) dh_parent_link;
struct dvb_hardware_queue dh_childs;
// If attached to a network, this is set
dvb_network_t *dh_dn;
LIST_ENTRY(th_dvb_adapter) dh_network_link;
// These are created on-demand whenever this particular network
// attachment point tunes to a mux
struct th_dvb_mux_instance_list dh_tdmis;
th_dvb_mux_instance_t *dh_current_tdmi;
char *dh_name;
} dvb_hardware_t;
/**
* DVB Adapter (one of these per physical adapter)
*/
#define TDA_MUX_HASH_WIDTH 101
#define TDA_SCANQ_BAD 0 ///< Bad muxes (monitor quality)
#define TDA_SCANQ_OK 1 ///< OK muxes
#define TDA_SCANQ_NUM 2
typedef struct th_dvb_adapter {
#if 1
int tda_instance;
TAILQ_ENTRY(th_dvb_adapter) tda_global_link;
struct th_dvb_mux_instance_list tda_muxes;
dvb_network_t *tda_dn;
LIST_ENTRY(th_dvb_adapter) tda_network_link;
struct th_dvb_mux_instance_queue tda_scan_queues[TDA_SCANQ_NUM];
int tda_scan_selector;
struct th_dvb_mux_instance_list tda_tdmis;
struct th_dvb_mux_instance_queue tda_initial_scan_queue;
int tda_initial_num_mux;
th_dvb_mux_instance_t *tda_mux_current;
th_dvb_mux_instance_t *tda_mux_epg;
th_dvb_mux_instance_t *tda_current_tdmi;
char *tda_tune_reason; // Reason for last tune
int tda_table_epollfd;
uint32_t tda_enabled;
int tda_locked;
time_t tda_monitor;
time_t tda_fe_monitor;
const char *tda_rootpath;
char *tda_identifier;
uint32_t tda_autodiscovery;
uint32_t tda_idlescan;
uint32_t tda_idleclose;
uint32_t tda_skip_initialscan;
uint32_t tda_skip_checksubscr;
uint32_t tda_qmon;
uint32_t tda_poweroff;
uint32_t tda_sidtochan;
uint32_t tda_nitoid;
uint32_t tda_diseqc_version;
uint32_t tda_diseqc_repeats;
uint32_t tda_disable_pmt_monitor;
int32_t tda_full_mux_rx;
uint32_t tda_grace_period;
uint32_t tda_snr_valid;
char *tda_displayname;
int tda_fe_type;
struct dvb_frontend_info *tda_fe_info; // result of FE_GET_INFO ioctl()
char *tda_fe_path;
int tda_fe_fd;
int tda_type;
int tda_snr_valid;
struct dvb_frontend_info *tda_fe_info;
int tda_adapter_num;
@ -245,8 +322,6 @@ typedef struct th_dvb_adapter {
int tda_hostconnection;
gtimer_t tda_mux_scanner_timer;
pthread_mutex_t tda_delivery_mutex;
struct service_list tda_transports; /* Currently bound transports */
@ -257,8 +332,6 @@ typedef struct th_dvb_adapter {
struct dvb_satconf_queue tda_satconfs;
struct th_dvb_mux_instance_list tda_mux_list;
uint32_t tda_last_fec;
int tda_unc_is_delta; /* 1 if we believe FE_READ_UNCORRECTED_BLOCKS
@ -268,8 +341,8 @@ typedef struct th_dvb_adapter {
void (*tda_open_service)(struct th_dvb_adapter *tda, struct service *s);
void (*tda_close_service)(struct th_dvb_adapter *tda, struct service *s);
void (*tda_open_table)(struct th_dvb_mux_instance *tdmi, struct th_dvb_table *s);
void (*tda_close_table)(struct th_dvb_mux_instance *tdmi, struct th_dvb_table *s);
void (*tda_open_table)(struct dvb_mux *dm, struct th_dvb_table *s);
void (*tda_close_table)(struct dvb_mux *dm, struct th_dvb_table *s);
int tda_rawmode;
@ -286,6 +359,7 @@ typedef struct th_dvb_adapter {
// PIDs that needs to be requeued and processed as tables
uint8_t tda_table_filter[8192];
#endif
} th_dvb_adapter_t;
@ -318,7 +392,7 @@ typedef struct th_dvb_table {
char *tdt_name;
void *tdt_opaque;
int (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
int (*tdt_callback)(dvb_mux_t *dm, uint8_t *buf, int len,
uint8_t tableid, void *opaque);
@ -338,8 +412,8 @@ typedef struct th_dvb_table {
} th_dvb_table_t;
extern struct th_dvb_adapter_queue dvb_adapters;
extern struct th_dvb_mux_instance_tree dvb_muxes;
extern struct dvb_network_list dvb_networks;
extern struct dvb_hardware_queue dvb_adapters;
void dvb_init(uint32_t adapter_mask, const char *rawfile);
@ -348,50 +422,9 @@ void dvb_init(uint32_t adapter_mask, const char *rawfile);
*/
void dvb_adapter_init(uint32_t adapter_mask, const char *rawfile);
void dvb_adapter_mux_scanner(void *aux);
void dvb_adapter_start(th_dvb_adapter_t *tda, int opt);
void dvb_adapter_start (th_dvb_adapter_t *tda, int opt);
void dvb_adapter_stop (th_dvb_adapter_t *tda, int opt);
void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s);
void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_skip_initialscan(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_idlescan(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_skip_checksubscr(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_qmon(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_idleclose(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_poweroff(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_sidtochan(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_nitoid(th_dvb_adapter_t *tda, int nitoid);
void dvb_adapter_set_diseqc_version(th_dvb_adapter_t *tda, unsigned int v);
void dvb_adapter_set_diseqc_repeats(th_dvb_adapter_t *tda,
unsigned int repeats);
void dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int r);
void dvb_adapter_set_grace_period(th_dvb_adapter_t *tda, uint32_t p);
void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src);
void dvb_adapter_clean(th_dvb_adapter_t *tda);
int dvb_adapter_destroy(th_dvb_adapter_t *tda);
void dvb_adapter_stop(th_dvb_adapter_t *tda, int opt);
void dvb_adapter_notify(th_dvb_adapter_t *tda);
@ -424,35 +457,37 @@ int dvb_mux_str2mode(const char *str);
int dvb_mux_str2guard(const char *str);
int dvb_mux_str2hier(const char *str);
void dvb_mux_save(th_dvb_mux_instance_t *tdmi);
void dvb_mux_save(dvb_mux_t *dm);
void dvb_mux_load(th_dvb_adapter_t *tda);
void dvb_mux_load(dvb_network_t *dn);
void dvb_mux_destroy(th_dvb_mux_instance_t *tdmi);
void dvb_mux_destroy(dvb_mux_t *dm);
th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda,
const struct dvb_mux_conf *dmc,
uint16_t onid, uint16_t tsid, const char *network,
const char *logprefix, int enabled,
int initialscan, const char *identifier,
dvb_satconf_t *satconf, int create, th_dvb_mux_instance_t *src);
dvb_mux_t *dvb_mux_create(dvb_network_t *tda,
const struct dvb_mux_conf *dmc,
uint16_t onid, uint16_t tsid, const char *network,
const char *logprefix, int enabled,
int initialscan, const char *uuid,
dvb_satconf_t *satconf, int create, dvb_mux_t *src);
void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name);
int dvb_mux_tune(dvb_mux_t *dm, const char *reason, int weight);
void dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid, int force);
void dvb_mux_set_networkname(dvb_mux_t *dm, const char *name);
void dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid, int force);
void dvb_mux_set_tsid(dvb_mux_t *dm, uint16_t tsid, int force);
void dvb_mux_set_onid(dvb_mux_t *mux, uint16_t onid, int force);
void dvb_mux_set_enable(th_dvb_mux_instance_t *tdmi, int enabled);
void dvb_mux_set_satconf(th_dvb_mux_instance_t *tdmi, const char *scid,
int save);
htsmsg_t *dvb_mux_build_msg(th_dvb_mux_instance_t *tdmi);
htsmsg_t *dvb_mux_build_msg(dvb_mux_t *dm);
void dvb_mux_notify(th_dvb_mux_instance_t *tdmi);
void dvb_mux_notify(dvb_mux_t *dm);
const char *dvb_mux_add_by_params(th_dvb_adapter_t *tda,
const char *dvb_mux_add_by_params(dvb_network_t *dn,
int freq,
int symrate,
int bw,
@ -466,46 +501,54 @@ const char *dvb_mux_add_by_params(th_dvb_adapter_t *tda,
int fec,
int polarisation,
const char *satconf);
#if 0
int dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src,
dvb_satconf_t *satconf);
#endif
void dvb_mux_add_to_scan_queue (th_dvb_mux_instance_t *tdmi);
dvb_mux_t *dvb_mux_find(dvb_network_t *dn, const char *netname, uint16_t onid,
uint16_t tsid, int enabled);
void dvb_mux_stop(th_dvb_mux_instance_t *tdmi);
void dvb_mux_initial_scan_done(dvb_mux_t *dm);
int dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi, const char *reason);
void dvb_create_tdmis(dvb_mux_t *dm);
int tdmi_current_weight(const th_dvb_mux_instance_t *tdmi);
th_dvb_mux_instance_t *dvb_mux_find
(th_dvb_adapter_t *tda, const char *netname, uint16_t onid, uint16_t tsid,
int enabled );
/**
* DVB Transport (aka DVB service)
*/
void dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier);
void dvb_service_load(dvb_mux_t *dm);
struct service *dvb_service_find(th_dvb_mux_instance_t *tdmi,
uint16_t sid, int pmt_pid,
const char *identifier);
struct service *dvb_service_find(dvb_mux_t *dm,
uint16_t sid, int pmt_pid,
const char *identifier);
struct service *dvb_service_find2(th_dvb_mux_instance_t *tdmi,
uint16_t sid, int pmt_pid,
const char *identifier, int *save);
struct service *dvb_service_find2(dvb_mux_t *dm,
uint16_t sid, int pmt_pid,
const char *identifier, int *save);
struct service *dvb_service_find3
(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi,
const char *netname, uint16_t onid, uint16_t tsid, uint16_t sid,
int enabled, int epgprimary);
struct service *dvb_service_find3(dvb_network_t *dn,
dvb_mux_t *dm,
const char *netname, uint16_t onid,
uint16_t tsid, uint16_t sid,
int enabled, int epgprimary);
void dvb_service_notify(struct service *t);
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(th_dvb_mux_instance_t *tdmi, const char *reason);
void dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune);
//void dvb_fe_stop(th_dvb_adapter_t *tda, int retune);
/**
@ -513,22 +556,21 @@ void dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune);
*/
void dvb_table_init(th_dvb_adapter_t *tda);
void dvb_table_add_default(th_dvb_mux_instance_t *tdmi);
void dvb_table_add_default(dvb_mux_t *dm);
void dvb_table_flush_all(th_dvb_mux_instance_t *tdmi);
void dvb_table_flush_all(dvb_mux_t *dm);
void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid);
void dvb_table_add_pmt(dvb_mux_t *dm, int pmt_pid);
void dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid);
void dvb_table_rem_pmt(dvb_mux_t *dm, int pmt_pid);
void tdt_add(th_dvb_mux_instance_t *tdmi, int table, int mask,
int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
void tdt_add(dvb_mux_t *dm, int table, int mask,
int (*callback)(dvb_mux_t *dm, uint8_t *buf, int len,
uint8_t tableid, void *opaque), void *opaque,
const char *name, int flags, int pid);
int dvb_pidx11_callback
(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque);
int dvb_pidx11_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque);
#define TDT_CRC 0x1
#define TDT_QUICKREQ 0x2
@ -539,6 +581,21 @@ void dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt);
void dvb_table_release(th_dvb_table_t *tdt);
/**
*
*/
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_schedule_initial_scan(dvb_network_t *dn);
/**
* Satellite configuration
*/
@ -566,5 +623,18 @@ struct th_subscription *dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t
const char *username,
const char *client);
/**
* DVB Hardware
*/
idnode_t **dvb_hardware_get_childs(struct idnode *self);
const char *dvb_hardware_get_title(struct idnode *self);
void *dvb_hardware_create(const idclass_t *class, size_t size,
dvb_hardware_t *parent, const char *uuid,
const char *name);
void dvb_linux_init(void);
#endif /* DVB_H_ */

View file

@ -49,644 +49,7 @@
#include "diseqc.h"
#include "atomic.h"
struct th_dvb_adapter_queue dvb_adapters;
struct th_dvb_mux_instance_tree dvb_muxes;
static void *dvb_adapter_input_dvr(void *aux);
static void tda_init(th_dvb_adapter_t *tda);
/**
* Adapters that are known to have SNR support
*/
static const char* dvb_adapter_snr_whitelist[] = {
"Sony CXD2820R",
"stv090x",
"TurboSight",
NULL
};
/**
*
*/
static th_dvb_adapter_t *
tda_alloc(void)
{
int i;
th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t));
pthread_mutex_init(&tda->tda_delivery_mutex, NULL);
for (i = 0; i < TDA_SCANQ_NUM; i++ )
TAILQ_INIT(&tda->tda_scan_queues[i]);
TAILQ_INIT(&tda->tda_initial_scan_queue);
TAILQ_INIT(&tda->tda_satconfs);
streaming_pad_init(&tda->tda_streaming_pad);
return tda;
}
/**
* Save config for the given adapter
*/
static void
tda_save(th_dvb_adapter_t *tda)
{
htsmsg_t *m = htsmsg_create_map();
lock_assert(&global_lock);
htsmsg_add_u32(m, "enabled", tda->tda_enabled);
if (tda->tda_fe_path)
htsmsg_add_str(m, "fe_path", tda->tda_fe_path);
if (tda->tda_demux_path)
htsmsg_add_str(m, "dmx_path", tda->tda_demux_path);
if (tda->tda_dvr_path)
htsmsg_add_str(m, "dvr_path", tda->tda_dvr_path);
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
htsmsg_add_str(m, "displayname", tda->tda_displayname);
htsmsg_add_u32(m, "autodiscovery", tda->tda_autodiscovery);
htsmsg_add_u32(m, "idlescan", tda->tda_idlescan);
htsmsg_add_u32(m, "idleclose", tda->tda_idleclose);
htsmsg_add_u32(m, "skip_checksubscr", tda->tda_skip_checksubscr);
htsmsg_add_u32(m, "sidtochan", tda->tda_sidtochan);
htsmsg_add_u32(m, "qmon", tda->tda_qmon);
htsmsg_add_u32(m, "poweroff", tda->tda_poweroff);
htsmsg_add_u32(m, "nitoid", tda->tda_nitoid);
htsmsg_add_u32(m, "diseqc_version", tda->tda_diseqc_version);
htsmsg_add_u32(m, "diseqc_repeats", tda->tda_diseqc_repeats);
htsmsg_add_u32(m, "extrapriority", tda->tda_extrapriority);
htsmsg_add_u32(m, "skip_initialscan", tda->tda_skip_initialscan);
htsmsg_add_u32(m, "disable_pmt_monitor", tda->tda_disable_pmt_monitor);
htsmsg_add_s32(m, "full_mux_rx", tda->tda_full_mux_rx);
htsmsg_add_u32(m, "grace_period", tda->tda_grace_period);
hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier);
htsmsg_destroy(m);
}
/**
*
*/
void
dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_enabled == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" %s",
tda->tda_displayname, on ? "Enabled" : "Disabled");
tda->tda_enabled = on;
tda_save(tda);
if (!on) {
gtimer_disarm(&tda->tda_mux_scanner_timer);
if (tda->tda_mux_current)
dvb_fe_stop(tda->tda_mux_current, 0);
dvb_adapter_stop(tda, TDA_OPT_ALL);
} else {
tda_init(tda);
}
}
/**
*
*/
void
dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s)
{
lock_assert(&global_lock);
if(!strcmp(s, tda->tda_displayname))
return;
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" renamed to \"%s\"",
tda->tda_displayname, s);
free(tda->tda_displayname);
tda->tda_displayname = strdup(s);
tda_save(tda);
dvb_adapter_notify(tda);
}
/**
*
*/
void
dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_autodiscovery == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" mux autodiscovery set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_autodiscovery = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_skip_initialscan(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_skip_initialscan == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" skip initial scan set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_skip_initialscan = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_idlescan(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_idlescan == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" idle mux scanning set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_idlescan = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_idleclose(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_idleclose == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" idle fd close set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_idleclose = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_skip_checksubscr(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_skip_checksubscr == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" skip service availability check when mapping set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_skip_checksubscr = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_qmon(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_qmon == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" quality monitoring set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_qmon = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_sidtochan(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_sidtochan == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" use SID as channel number when mapping set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_sidtochan = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_poweroff(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_poweroff == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" idle poweroff set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_poweroff = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_nitoid(th_dvb_adapter_t *tda, int nitoid)
{
lock_assert(&global_lock);
if(tda->tda_nitoid == nitoid)
return;
tvhlog(LOG_NOTICE, "dvb", "NIT-o network id \"%d\" changed to \"%d\"",
tda->tda_nitoid, nitoid);
tda->tda_nitoid = nitoid;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_diseqc_version(th_dvb_adapter_t *tda, unsigned int v)
{
if(v > 1)
v = 1;
if(tda->tda_diseqc_version == v)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" DiSEqC version set to: %s",
tda->tda_displayname, v ? "1.1 / 2.1" : "1.0 / 2.0" );
tda->tda_diseqc_version = v;
tda_save(tda);
}
/**
* sets the number of diseqc repeats to perform
*/
void
dvb_adapter_set_diseqc_repeats(th_dvb_adapter_t *tda, unsigned int repeats)
{
if(tda->tda_diseqc_repeats == repeats)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" DiSEqC repeats set to: %i",
tda->tda_displayname, repeats);
tda->tda_diseqc_repeats = repeats;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_extrapriority(th_dvb_adapter_t *tda, int extrapriority)
{
lock_assert(&global_lock);
if(tda->tda_extrapriority == extrapriority)
return;
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" extra priority \"%d\" changed to \"%d\"",
tda->tda_displayname, tda->tda_extrapriority, extrapriority);
tda->tda_extrapriority = extrapriority;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_disable_pmt_monitor(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_disable_pmt_monitor == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" disabled PMT monitoring set to: %s",
tda->tda_displayname, on ? "On" : "Off");
tda->tda_disable_pmt_monitor = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int on)
{
const char* label[] = { "Auto", "Off", "On" };
if (on < -1) on = -1;
if (on > 1) on = 1;
if(tda->tda_full_mux_rx == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb",
"Adapter \"%s\" disabled full MUX receive set to: %s",
tda->tda_displayname, label[on+1]);
tda->tda_full_mux_rx = on;
tda_save(tda);
}
/**
*
*/
void
dvb_adapter_set_grace_period(th_dvb_adapter_t *tda, uint32_t p)
{
if (tda->tda_grace_period == p)
return;
tvhlog(LOG_NOTICE, "dvb",
"Adapter \"%s\" set grace period to %d", tda->tda_displayname, p);
tda->tda_grace_period = p;
tda_save(tda);
}
/**
*
*/
static void
dvb_adapter_checkspeed(th_dvb_adapter_t *tda)
{
char dev[64];
snprintf(dev, sizeof(dev), "dvb/dvb%d.dvr0", tda->tda_adapter_num);
tda->tda_hostconnection = get_device_connection(dev);
}
/**
* Return 1 if an adapter is capable of receiving a full mux
*/
static int
check_full_stream(th_dvb_adapter_t *tda)
{
struct dmx_pes_filter_params dmx_param;
int r;
if(tda->tda_full_mux_rx != -1)
return tda->tda_full_mux_rx;
if(tda->tda_hostconnection == HOSTCONNECTION_USB12)
return 0; // Don't even bother, device <-> host interface is too slow
if(tda->tda_hostconnection == HOSTCONNECTION_USB480)
return 0; // USB in general appears to have CPU loading issues?
int fd = tvh_open(tda->tda_demux_path, O_RDWR, 0);
if(fd == -1)
return 0;
memset(&dmx_param, 0, sizeof(dmx_param));
dmx_param.pid = 0x2000;
dmx_param.input = DMX_IN_FRONTEND;
dmx_param.output = DMX_OUT_TS_TAP;
dmx_param.pes_type = DMX_PES_OTHER;
dmx_param.flags = DMX_IMMEDIATE_START;
r = ioctl(fd, DMX_SET_PES_FILTER, &dmx_param);
close(fd);
return !r;
}
/**
*
*/
static void
tda_add(int adapter_num)
{
char buf[1024], path[200], fepath[256], dmxpath[256], dvrpath[256];
int i, j, fd;
th_dvb_adapter_t *tda;
struct dvb_frontend_info fe_info;
DIR *dirp;
const char **str;
/* Check valid adapter */
snprintf(path, sizeof(path), "/dev/dvb/adapter%d", adapter_num);
dirp = opendir(path);
if (!dirp)
return;
closedir(dirp);
/* Check each frontend */
// Note: this algo will fail if there are really exotic variations
// of tuners and demuxers etc... but you can always manually
// override the config
for (i = 0; i < 32; i++) {
/* Open/Query frontend */
snprintf(fepath, sizeof(fepath), "%s/frontend%d", path, i);
fd = tvh_open(fepath, O_RDONLY, 0);
if (fd == -1) {
if (errno != ENOENT)
tvhlog(LOG_ALERT, "dvb",
"%s: unable to open (err=%s)", fepath, strerror(errno));
continue;
}
if (ioctl(fd, FE_GET_INFO, &fe_info)) {
tvhlog(LOG_ALERT, "dvb",
"%s: unable to query (err=%s)", fepath, strerror(errno));
close(fd);
continue;
}
close(fd);
/* Find Demux */
snprintf(dmxpath, sizeof(dmxpath), "%s/demux%d", path, i);
fd = tvh_open(dmxpath, O_RDONLY, 0);
if (fd == -1) {
snprintf(dmxpath, sizeof(dmxpath), "%s/demux%d", path, 0);
fd = tvh_open(dmxpath, O_RDONLY, 0);
}
if (fd == -1) {
tvhlog(LOG_ALERT, "dvb", "%s: unable to find demux", fepath);
continue;
}
close(fd);
/* Find DVR */
snprintf(dvrpath, sizeof(dvrpath), "%s/dvr%d", path, i);
fd = tvh_open(dvrpath, O_RDONLY, 0);
if (fd == -1) {
snprintf(dvrpath, sizeof(dvrpath), "%s/dvr%d", path, 0);
fd = tvh_open(dvrpath, O_RDONLY, 0);
}
if (fd == -1) {
tvhlog(LOG_ALERT, "dvb", "%s: unable to find dvr", fepath);
continue;
}
close(fd);
/* Create entry */
tda = tda_alloc();
tda->tda_adapter_num = adapter_num;
tda->tda_rootpath = strdup(path);
tda->tda_fe_path = strdup(fepath);
tda->tda_demux_path = strdup(dmxpath);
tda->tda_dvr_path = strdup(dvrpath);
tda->tda_fe_fd = -1;
tda->tda_dvr_pipe.rd = -1;
tda->tda_full_mux_rx = -1;
tda->tda_type = fe_info.type;
tda->tda_autodiscovery = tda->tda_type != FE_QPSK;
tda->tda_idlescan = 1;
tda->tda_sat = tda->tda_type == FE_QPSK;
tda->tda_displayname = strdup(fe_info.name);
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
memcpy(tda->tda_fe_info, &fe_info, sizeof(struct dvb_frontend_info));
/* ID the device (for configuration etc..) */
// Note: would be better to include frontend dev in name, but that
// would require additional work to migrate config
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
tda->tda_fe_info->name);
for(j = 0; j < strlen(buf); j++)
if(!isalnum((int)buf[j]))
buf[j] = '_';
tda->tda_identifier = strdup(buf);
/* Check device connection */
dvb_adapter_checkspeed(tda);
/* Adapters known to provide valid SNR */
str = dvb_adapter_snr_whitelist;
while (*str) {
if (strcasestr(fe_info.name, *str)) {
tda->tda_snr_valid = 1;
break;
}
str++;
}
/* Store */
tvhlog(LOG_INFO, "dvb",
"Found adapter %s (%s) via %s%s", path, tda->tda_fe_info->name,
hostconnection2str(tda->tda_hostconnection),
tda->tda_snr_valid ? ", Reports valid SNR values" : "");
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
}
}
/**
*
*/
static void
tda_add_from_file(const char *filename)
{
int i, r;
th_dvb_adapter_t *tda;
char buf[400];
tda = tda_alloc();
tda->tda_adapter_num = -1;
tda->tda_fe_fd = -1;
tda->tda_dvr_pipe.rd = -1;
tda->tda_enabled = 1;
tda->tda_type = -1;
snprintf(buf, sizeof(buf), "%s", filename);
r = strlen(buf);
for(i = 0; i < r; i++)
if(!isalnum((int)buf[i]))
buf[i] = '_';
tda->tda_identifier = strdup(buf);
tda->tda_autodiscovery = 0;
tda->tda_idlescan = 0;
tda->tda_sat = 0;
tda->tda_full_mux_rx = 1;
/* 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(filename);
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
}
/**
* Initiliase
*/
static void tda_init (th_dvb_adapter_t *tda)
{
/* Disabled - ignore */
if (!tda->tda_enabled || !tda->tda_rootpath) return;
/* Initiliase input mode */
if(!tda->tda_open_service) {
if(tda->tda_type == -1 || check_full_stream(tda)) {
tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", tda->tda_rootpath);
dvb_input_raw_setup(tda);
} else {
tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", tda->tda_rootpath);
dvb_input_filtered_setup(tda);
}
}
/* Enable mux scanner */
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1);
}
/**
*
@ -722,6 +85,8 @@ dvb_adapter_start ( th_dvb_adapter_t *tda, int opt )
void
dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt )
{
assert(tda->tda_current_tdmi == NULL);
/* Poweroff */
if (opt & TDA_OPT_PWR)
dvb_adapter_poweroff(tda);
@ -751,234 +116,6 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt )
}
}
/**
*
*/
void
dvb_adapter_init(uint32_t adapter_mask, const char *rawfile)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
const char *s;
int i, type;
uint32_t u32;
th_dvb_adapter_t *tda;
TAILQ_INIT(&dvb_adapters);
/* Initialise hardware */
for(i = 0; i < 32; i++)
if ((1 << i) & adapter_mask)
tda_add(i);
/* Initialise rawts test file */
if(rawfile)
tda_add_from_file(rawfile);
/* Load configuration */
if ((l = hts_settings_load("dvbadapters")) != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
if((s = htsmsg_get_str(c, "type")) == NULL ||
(type = dvb_str_to_adaptertype(s)) < 0)
continue;
// TODO: do we really want to do this? useful for debug?
if((tda = dvb_adapter_find_by_identifier(f->hmf_name)) == NULL) {
/* Not discovered by hardware, create it */
tda = tda_alloc();
tda->tda_identifier = strdup(f->hmf_name);
tda->tda_type = type;
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
} else {
if(type != tda->tda_type)
continue; /* Something is wrong, ignore */
}
tvh_str_update(&tda->tda_displayname, htsmsg_get_str(c, "displayname"));
tvh_str_update(&tda->tda_dvr_path, htsmsg_get_str(c, "dvr_path"));
tvh_str_update(&tda->tda_demux_path, htsmsg_get_str(c, "dmx_path"));
if (htsmsg_get_u32(c, "enabled", &tda->tda_enabled))
tda->tda_enabled = 1;
htsmsg_get_u32(c, "autodiscovery", &tda->tda_autodiscovery);
htsmsg_get_u32(c, "idlescan", &tda->tda_idlescan);
htsmsg_get_u32(c, "idleclose", &tda->tda_idleclose);
htsmsg_get_u32(c, "skip_checksubscr", &tda->tda_skip_checksubscr);
htsmsg_get_u32(c, "sidtochan", &tda->tda_sidtochan);
htsmsg_get_u32(c, "qmon", &tda->tda_qmon);
htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff);
htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid);
htsmsg_get_u32(c, "diseqc_version", &tda->tda_diseqc_version);
htsmsg_get_u32(c, "diseqc_repeats", &tda->tda_diseqc_repeats);
htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority);
htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan);
htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor);
if (htsmsg_get_u32(c, "grace_period", &tda->tda_grace_period))
tda->tda_grace_period = 10;
if (htsmsg_get_s32(c, "full_mux_rx", &tda->tda_full_mux_rx))
if (!htsmsg_get_u32(c, "disable_full_mux_rx", &u32) && u32)
tda->tda_full_mux_rx = 0;
}
htsmsg_destroy(l);
}
/* Initialise devices */
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
tda_init(tda);
if(tda->tda_sat)
dvb_satconf_init(tda);
dvb_mux_load(tda);
}
}
/**
* If nobody is subscribing, cycle thru all muxes to get some stats
* and EIT updates
*/
void
dvb_adapter_mux_scanner(void *aux)
{
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
int i;
if(tda->tda_rootpath == NULL)
return; // No hardware
if(!tda->tda_enabled)
return; // disabled
// default period
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 20);
/* No muxes */
if(LIST_FIRST(&tda->tda_muxes) == NULL) {
dvb_adapter_poweroff(tda);
return;
}
/* Someone is actively using */
if(service_compute_weight(&tda->tda_transports) > 0)
return;
if(tda->tda_mux_current != NULL &&
LIST_FIRST(&tda->tda_mux_current->tdmi_subscriptions) != NULL)
return; // Someone is doing full mux dump
/* Check if we have muxes pending for quickscan, if so, choose them */
if((tdmi = TAILQ_FIRST(&tda->tda_initial_scan_queue)) != NULL) {
dvb_fe_tune(tdmi, "Initial autoscan");
return;
}
/* Check EPG */
if (tda->tda_mux_epg) {
epggrab_mux_stop(tda->tda_mux_epg, 1); // timeout anything not complete
tda->tda_mux_epg = NULL; // skip this time
} else {
tda->tda_mux_epg = epggrab_mux_next(tda);
}
/* EPG */
if (tda->tda_mux_epg) {
int period = epggrab_mux_period(tda->tda_mux_epg);
if (period > 20)
gtimer_arm(&tda->tda_mux_scanner_timer,
dvb_adapter_mux_scanner, tda, period);
dvb_fe_tune(tda->tda_mux_epg, "EPG scan");
return;
/* Normal */
} else if (tda->tda_idlescan) {
/* Alternate queue */
for(i = 0; i < TDA_SCANQ_NUM; i++) {
tda->tda_scan_selector++;
if (tda->tda_scan_selector == TDA_SCANQ_NUM)
tda->tda_scan_selector = 0;
tdmi = TAILQ_FIRST(&tda->tda_scan_queues[tda->tda_scan_selector]);
if (tdmi) {
dvb_fe_tune(tdmi, "Autoscan");
return;
}
}
}
/* Ensure we stop current mux and power off (if required) */
if (tda->tda_mux_current)
dvb_fe_stop(tda->tda_mux_current, 0);
}
/**
*
*/
void
dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
{
th_dvb_mux_instance_t *tdmi_src, *tdmi_dst;
lock_assert(&global_lock);
while((tdmi_dst = LIST_FIRST(&dst->tda_muxes)) != NULL)
dvb_mux_destroy(tdmi_dst);
LIST_FOREACH(tdmi_src, &src->tda_muxes, tdmi_adapter_link)
dvb_mux_copy(dst, tdmi_src, NULL);
tda_save(dst);
}
/**
*
*/
int
dvb_adapter_destroy(th_dvb_adapter_t *tda)
{
th_dvb_mux_instance_t *tdmi;
if(tda->tda_rootpath != NULL)
return -1;
lock_assert(&global_lock);
hts_settings_remove("dvbadapters/%s", tda->tda_identifier);
while((tdmi = LIST_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_adapter_clean(th_dvb_adapter_t *tda)
{
service_t *t;
lock_assert(&global_lock);
while((t = LIST_FIRST(&tda->tda_transports)) != NULL)
/* Flush all subscribers */
service_remove_subscriber(t, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN);
}
/**
* Install RAW PES filter
*/
@ -1113,8 +250,7 @@ dvb_adapter_input_dvr(void *aux)
}
} else {
LIST_FOREACH(t, &tda->tda_transports, s_active_link)
if(t->s_dvb_mux_instance == tda->tda_mux_current)
ts_recv_packet1(t, tsb + i, NULL);
ts_recv_packet1(t, tsb + i, NULL);
}
i += 188;
@ -1152,42 +288,26 @@ dvb_adapter_input_dvr(void *aux)
htsmsg_t *
dvb_adapter_build_msg(th_dvb_adapter_t *tda)
{
char buf[100];
htsmsg_t *m = htsmsg_create_map();
th_dvb_mux_instance_t *tdmi;
service_t *t;
int nummux = 0;
int numsvc = 0;
int fdiv;
htsmsg_add_str(m, "identifier", tda->tda_identifier);
htsmsg_add_str(m, "name", tda->tda_displayname);
// XXX: bad bad bad slow slow slow
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
nummux++;
LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) {
numsvc++;
}
}
htsmsg_add_str(m, "type", "dvb");
htsmsg_add_u32(m, "services", numsvc);
htsmsg_add_u32(m, "muxes", nummux);
htsmsg_add_u32(m, "initialMuxes", tda->tda_initial_num_mux);
if(tda->tda_current_tdmi != NULL) {
th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi;
if(tda->tda_mux_current != NULL) {
th_dvb_mux_instance_t *tdmi = tda->tda_mux_current;
dvb_mux_nicename(buf, sizeof(buf), tda->tda_mux_current);
htsmsg_add_str(m, "currentMux", buf);
htsmsg_add_str(m, "currentMux",
dvb_mux_nicename(tda->tda_current_tdmi->tdmi_mux));
htsmsg_add_u32(m, "signal", MIN(tdmi->tdmi_signal * 100 / 65535, 100));
htsmsg_add_u32(m, "snr", tdmi->tdmi_snr);
htsmsg_add_u32(m, "ber", tdmi->tdmi_ber);
htsmsg_add_u32(m, "unc", tdmi->tdmi_unc);
htsmsg_add_u32(m, "uncavg", tdmi->tdmi_unc_avg);
htsmsg_add_str(m, "reason", tda->tda_tune_reason);
} else {
htsmsg_add_str(m, "currentMux", "");
htsmsg_add_u32(m, "signal", 0);
@ -1195,6 +315,12 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda)
htsmsg_add_u32(m, "ber", 0);
htsmsg_add_u32(m, "unc", 0);
htsmsg_add_u32(m, "uncavg", 0);
if(tda->tda_fe_fd == -1) {
htsmsg_add_str(m, "reason", "Closed");
} else {
htsmsg_add_str(m, "reason", "Idle");
}
}
if(tda->tda_rootpath == NULL)
@ -1206,11 +332,11 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda)
htsmsg_add_str(m, "devicename", tda->tda_fe_info->name);
htsmsg_add_str(m, "deliverySystem",
dvb_adaptertype_to_str(tda->tda_type) ?: "");
dvb_adaptertype_to_str(tda->tda_fe_type) ?: "");
htsmsg_add_u32(m, "satConf", tda->tda_sat);
fdiv = tda->tda_type == FE_QPSK ? 1 : 1000;
fdiv = tda->tda_fe_type == FE_QPSK ? 1 : 1000;
htsmsg_add_u32(m, "freqMin", tda->tda_fe_info->frequency_min / fdiv);
htsmsg_add_u32(m, "freqMax", tda->tda_fe_info->frequency_max / fdiv);
@ -1369,7 +495,7 @@ dvb_adapter_poweroff(th_dvb_adapter_t *tda)
{
if (tda->tda_fe_fd == -1) return;
lock_assert(&global_lock);
if (!tda->tda_poweroff || tda->tda_type != FE_QPSK)
if (!tda->tda_poweroff || tda->tda_fe_type != FE_QPSK)
return;
diseqc_voltage_off(tda->tda_fe_fd);
tvhlog(LOG_DEBUG, "dvb", "\"%s\" is off", tda->tda_rootpath);

View file

@ -92,13 +92,11 @@ dvb_fe_monitor(void *aux)
th_dvb_adapter_t *tda = aux;
fe_status_t fe_status;
int status, v, vv, i, fec, q, bw;
th_dvb_mux_instance_t *tdmi = tda->tda_mux_current;
char buf[50];
th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi;
signal_status_t sigstat;
streaming_message_t sm;
struct service *t;
int store = 0;
int notify = 0;
if(tdmi == NULL)
@ -107,6 +105,7 @@ dvb_fe_monitor(void *aux)
/**
* Read out front end status
*/
if(ioctl(tda->tda_fe_fd, FE_READ_STATUS, &fe_status))
status = TDMI_FE_UNKNOWN;
else if(fe_status & FE_HAS_LOCK)
@ -130,15 +129,15 @@ dvb_fe_monitor(void *aux)
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
/* Install table handlers */
dvb_table_add_default(tdmi);
epggrab_mux_start(tdmi);
dvb_table_add_default(tdmi->tdmi_mux);
epggrab_mux_start(tdmi->tdmi_mux);
/* Service filters */
pthread_mutex_lock(&tda->tda_delivery_mutex);
LIST_FOREACH(t, &tda->tda_transports, s_active_link) {
if (t->s_dvb_mux_instance == tdmi) {
if (t->s_dvb_mux == tdmi->tdmi_mux) {
tda->tda_open_service(tda, t);
dvb_table_add_pmt(tdmi, t->s_pmt_pid);
dvb_table_add_pmt(tdmi->tdmi_mux, t->s_pmt_pid);
}
}
pthread_mutex_unlock(&tda->tda_delivery_mutex);
@ -148,9 +147,9 @@ dvb_fe_monitor(void *aux)
gtimer_arm_ms(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 50);
/* Monitor (1 per sec) */
if (dispatch_clock < tda->tda_monitor)
if (dispatch_clock < tda->tda_fe_monitor)
return;
tda->tda_monitor = dispatch_clock + 1;
tda->tda_fe_monitor = dispatch_clock + 1;
}
} else {
@ -224,11 +223,10 @@ dvb_fe_monitor(void *aux)
if(status != tdmi->tdmi_fe_status) {
tdmi->tdmi_fe_status = status;
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_DEBUG,
"dvb", "\"%s\" on adapter \"%s\", status changed to %s",
buf, tda->tda_displayname, dvb_mux_status(tdmi));
store = 1;
dvb_mux_nicename(tdmi->tdmi_mux), tda->tda_displayname,
dvb_mux_status(tdmi));
notify = 1;
}
@ -241,7 +239,6 @@ dvb_fe_monitor(void *aux)
}
if(q != tdmi->tdmi_quality) {
tdmi->tdmi_quality = q;
store = 1;
notify = 1;
}
}
@ -249,6 +246,8 @@ dvb_fe_monitor(void *aux)
bw = atomic_exchange(&tda->tda_bytes, 0);
if(notify) {
#if 0 // XXX(dvbreorg)
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
@ -259,8 +258,9 @@ dvb_fe_monitor(void *aux)
htsmsg_add_u32(m, "ber", tdmi->tdmi_ber);
htsmsg_add_u32(m, "unc", tdmi->tdmi_unc);
notify_by_msg("dvbMux", m);
#endif
m = htsmsg_create_map();
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "identifier", tda->tda_identifier);
htsmsg_add_u32(m, "signal", MIN(tdmi->tdmi_signal * 100 / 65535, 100));
if(tda->tda_snr_valid)
@ -272,8 +272,10 @@ dvb_fe_monitor(void *aux)
notify_by_msg("tvAdapter", m);
}
#if 0 // XXX(dvbreorg)
if(store)
dvb_mux_save(tdmi);
#endif
/* Streaming message */
sigstat.status_text = dvb_mux_status(tdmi);
@ -283,57 +285,10 @@ dvb_fe_monitor(void *aux)
sigstat.unc = tdmi->tdmi_unc;
sm.sm_type = SMT_SIGNAL_STATUS;
sm.sm_data = &sigstat;
LIST_FOREACH(t, &tda->tda_transports, s_active_link)
if(t->s_dvb_mux_instance == tda->tda_mux_current && t->s_status == SERVICE_RUNNING ) {
pthread_mutex_lock(&t->s_stream_mutex);
streaming_pad_deliver(&t->s_streaming_pad, &sm);
pthread_mutex_unlock(&t->s_stream_mutex);
}
}
/**
* Stop the given TDMI
*/
void
dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
dvb_table_feed_t *dtf;
lock_assert(&global_lock);
assert(tdmi == tda->tda_mux_current);
tda->tda_mux_current = NULL;
if(tdmi->tdmi_table_initial) {
tdmi->tdmi_table_initial = 0;
tda->tda_initial_num_mux--;
dvb_mux_save(tdmi);
}
dvb_adapter_stop(tda, TDA_OPT_DVR);
pthread_mutex_lock(&tda->tda_delivery_mutex);
while((dtf = TAILQ_FIRST(&tda->tda_table_feed)))
TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
dvb_table_flush_all(tdmi);
tda->tda_locked = 0;
assert(tdmi->tdmi_scan_queue == NULL);
if(tdmi->tdmi_enabled) {
dvb_mux_add_to_scan_queue(tdmi);
}
epggrab_mux_stop(tdmi, 0);
time(&tdmi->tdmi_lost_adapter);
if (!retune) {
gtimer_disarm(&tda->tda_fe_monitor_timer);
dvb_adapter_stop(tda, TDA_OPT_ALL);
LIST_FOREACH(t, &tda->tda_transports, s_active_link) {
pthread_mutex_lock(&t->s_stream_mutex);
streaming_pad_deliver(&t->s_streaming_pad, &sm);
pthread_mutex_unlock(&t->s_stream_mutex);
}
}
@ -352,9 +307,8 @@ static struct dtv_properties clear_cmdseq = {
*
*/
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;
@ -396,43 +350,50 @@ dvb_fe_tune_s2(th_dvb_mux_instance_t *tdmi, dvb_mux_conf_t *dmc)
#endif
/**
*
*/
int
dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi, const char *reason)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
dvb_mux_t *dm = tdmi->tdmi_mux;
// copy dmc, cause frequency may be change with FE_QPSK
dvb_mux_conf_t dmc = tdmi->tdmi_conf;
dvb_mux_conf_t dmc = dm->dm_conf;
dvb_frontend_parameters_t* p = &dmc.dmc_fe_params;
char buf[256];
int r;
lock_assert(&global_lock);
if(tda->tda_enabled == 0)
return SM_CODE_TUNING_FAILED;
if(tda->tda_mux_current == tdmi)
return 0;
if(tdmi->tdmi_scan_queue != NULL) {
TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
tdmi->tdmi_scan_queue = NULL;
if(tda->tda_current_tdmi != NULL) {
if(tda->tda_current_tdmi == tdmi)
return 0; // Already currently tuned
/*
* Adapter tuned to something else.
* But at this stage we're no longer caring about such things.
* That should have been sorted out by our callers.
* So let's just kick it out the current occupant
*/
dvb_mux_stop(tda->tda_current_tdmi);
}
if(tda->tda_mux_current != NULL)
dvb_fe_stop(tda->tda_mux_current, 1);
free(tda->tda_tune_reason);
tda->tda_tune_reason = strdup(reason);
//abort();
dvb_adapter_start(tda, TDA_OPT_FE | TDA_OPT_PWR);
if(tda->tda_type == FE_QPSK) {
// Make sure dvb_mux_stop() did the right thing
assert(tda->tda_current_tdmi == NULL);
if(tda->tda_fe_type == FE_QPSK) {
/* DVB-S */
dvb_satconf_t *sc;
int port, lowfreq, hifreq, switchfreq, hiband, pol, dbsbs;
lowfreq = 9750000;
@ -440,8 +401,9 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
switchfreq = 11700000;
port = 0;
dbsbs = 0;
if((sc = tdmi->tdmi_conf.dmc_satconf) != NULL) {
#if 0
dvb_satconf_t *sc;
if((sc = dm->dm_conf.dmc_satconf) != NULL) {
port = sc->sc_port;
if(sc->sc_lnb != NULL)
@ -449,18 +411,19 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
if(!strcmp(sc->sc_id ?: "", "DBS Bandstacked"))
dbsbs = 1;
}
#endif
if(dbsbs) {
hiband = 0;
if(tdmi->tdmi_conf.dmc_polarisation == POLARISATION_HORIZONTAL ||
tdmi->tdmi_conf.dmc_polarisation == POLARISATION_CIRCULAR_LEFT)
if(dm->dm_conf.dmc_polarisation == POLARISATION_HORIZONTAL ||
dm->dm_conf.dmc_polarisation == POLARISATION_CIRCULAR_LEFT)
p->frequency = abs(p->frequency - hifreq);
else
p->frequency = abs(p->frequency - lowfreq);
pol = POLARISATION_CIRCULAR_LEFT;
} else {
hiband = switchfreq && p->frequency > switchfreq;
pol = tdmi->tdmi_conf.dmc_polarisation;
pol = dm->dm_conf.dmc_polarisation;
if(hiband)
p->frequency = abs(p->frequency - hifreq);
else
@ -475,48 +438,37 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
tvhlog(LOG_ERR, "dvb", "diseqc setup failed %d\n", r);
}
dvb_mux_nicename(buf, sizeof(buf), tdmi);
#if DVB_API_VERSION >= 5
if (tda->tda_type == FE_QPSK) {
if (tda->tda_fe_type == FE_QPSK) {
tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning via s2api to \"%s\" (%d, %d Baud, "
"%s, %s, %s) for %s", tda->tda_rootpath, buf, p->frequency, p->u.qpsk.symbol_rate,
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);
"%s, %s, %s)", tda->tda_rootpath, dvb_mux_nicename(dm), p->frequency, p->u.qpsk.symbol_rate,
dvb_mux_fec2str(p->u.qpsk.fec_inner), dvb_mux_delsys2str(dmc.dmc_fe_delsys),
dvb_mux_qam2str(dmc.dmc_fe_modulation));
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\"",
tda->tda_rootpath, dvb_mux_nicename(dm));
r = ioctl(tda->tda_fe_fd, FE_SET_FRONTEND, p);
}
if(r != 0) {
tvhlog(LOG_ERR, "dvb", "\"%s\" tuning to \"%s\""
" -- Front configuration failed -- %s, frequency: %u",
tda->tda_rootpath, buf, strerror(errno), p->frequency);
/* Remove from initial scan set */
if(tdmi->tdmi_table_initial) {
tdmi->tdmi_table_initial = 0;
tda->tda_initial_num_mux--;
}
/* Mark as bad */
tda->tda_rootpath, dvb_mux_nicename(dm), strerror(errno), p->frequency);
if (errno == EINVAL)
dvb_mux_set_enable(tdmi, 0);
dvb_adapter_stop(tda, TDA_OPT_ALL);
tdmi->tdmi_tune_failed = 1;
return SM_CODE_TUNING_FAILED;
}
tda->tda_mux_current = tdmi;
dm->dm_current_tdmi = tdmi;
time(&tda->tda_monitor);
tda->tda_monitor += 4; // wait a few secs before monitoring (unlocked)
time(&tda->tda_fe_monitor);
tda->tda_fe_monitor += 4; // wait a few secs before monitoring (unlocked)
gtimer_arm_ms(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 50);
dvb_adapter_notify(tda);
return 0;
return r;
}

82
src/dvb/dvb_hardware.c Normal file
View file

@ -0,0 +1,82 @@
/*
* Generic DVB hardware stuff
* Copyright (C) 2013 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 "tvheadend.h"
#include "dvb.h"
struct dvb_hardware_queue dvb_adapters;
/**
*
*/
idnode_t **
dvb_hardware_get_childs(struct idnode *self)
{
dvb_hardware_t *dh = (dvb_hardware_t *)self;
dvb_hardware_t *c;
int cnt = 1;
TAILQ_FOREACH(c, &dh->dh_childs, dh_parent_link)
cnt++;
idnode_t **v = malloc(sizeof(idnode_t *) * cnt);
cnt = 0;
TAILQ_FOREACH(c, &dh->dh_childs, dh_parent_link)
v[cnt++] = &c->dh_id;
// qsort(v, cnt, sizeof(idnode_t *), svcsortcmp);
v[cnt] = NULL;
return v;
}
/**
*
*/
const char *
dvb_hardware_get_title(struct idnode *self)
{
dvb_hardware_t *dh = (dvb_hardware_t *)self;
return dh->dh_name;
}
/**
*
*/
void *
dvb_hardware_create(const idclass_t *class, size_t size,
dvb_hardware_t *parent, const char *uuid,
const char *name)
{
dvb_hardware_t *dh = calloc(1, size);
idnode_insert(&dh->dh_id, uuid, class);
TAILQ_INIT(&dh->dh_childs);
if(parent == NULL) {
TAILQ_INSERT_TAIL(&dvb_adapters, dh, dh_parent_link);
} else {
TAILQ_INSERT_TAIL(&parent->dh_childs, dh, dh_parent_link);
}
dh->dh_name = strdup(name);
return dh;
}

View file

@ -59,7 +59,7 @@ open_service(th_dvb_adapter_t *tda, service_t *s)
st->es_demuxer_fd = -1;
tvhlog(LOG_ERR, "dvb",
"\"%s\" unable to open demuxer \"%s\" for pid %d -- %s",
s->s_identifier, tda->tda_demux_path,
s->s_nicename, tda->tda_demux_path,
st->es_pid, strerror(errno));
continue;
}
@ -74,7 +74,7 @@ open_service(th_dvb_adapter_t *tda, service_t *s)
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_identifier, tda->tda_demux_path,
s->s_nicename, tda->tda_demux_path,
st->es_pid, strerror(errno));
close(fd);
fd = -1;
@ -110,9 +110,9 @@ close_service(th_dvb_adapter_t *tda, service_t *s)
*
*/
static void
open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
open_table(dvb_mux_t *dm, th_dvb_table_t *tdt)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter;
struct epoll_event e;
static int tdt_id_tally;
@ -148,7 +148,7 @@ open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
}
if(tdt->tdt_fd == -1)
TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
TAILQ_INSERT_TAIL(&dm->dm_table_queue, tdt, tdt_pending_link);
}
@ -156,9 +156,9 @@ open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
* Close FD for the given table and put table on the pending list
*/
static void
tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
tdt_close_fd(dvb_mux_t *dm, th_dvb_table_t *tdt)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter;
assert(tdt->tdt_fd != -1);
@ -166,7 +166,7 @@ tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
close(tdt->tdt_fd);
tdt->tdt_fd = -1;
TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
TAILQ_INSERT_TAIL(&dm->dm_table_queue, tdt, tdt_pending_link);
}
@ -181,7 +181,7 @@ dvb_table_input(void *aux)
int r, i, tid, fd, x;
struct epoll_event ev[1];
uint8_t sec[4096];
th_dvb_mux_instance_t *tdmi;
dvb_mux_t *dm;
th_dvb_table_t *tdt;
int64_t cycle_barrier = 0;
@ -200,8 +200,8 @@ dvb_table_input(void *aux)
continue;
pthread_mutex_lock(&global_lock);
if((tdmi = tda->tda_mux_current) != NULL) {
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
if((dm = tda->tda_current_tdmi->tdmi_mux) != NULL) {
LIST_FOREACH(tdt, &dm->dm_tables, tdt_link)
if(tdt->tdt_id == tid)
break;
@ -209,15 +209,15 @@ dvb_table_input(void *aux)
dvb_table_dispatch(sec, r, tdt);
/* Any tables pending (that wants a filter/fd), close this one */
if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL &&
if(TAILQ_FIRST(&dm->dm_table_queue) != NULL &&
cycle_barrier < getmonoclock()) {
tdt_close_fd(tdmi, tdt);
tdt_close_fd(dm, tdt);
cycle_barrier = getmonoclock() + 100000;
tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue);
tdt = TAILQ_FIRST(&dm->dm_table_queue);
assert(tdt != NULL);
TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
TAILQ_REMOVE(&dm->dm_table_queue, tdt, tdt_pending_link);
open_table(tdmi, tdt);
open_table(dm, tdt);
}
}
}
@ -229,13 +229,15 @@ dvb_table_input(void *aux)
static void
close_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
close_table(dvb_mux_t *dm, th_dvb_table_t *tdt)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
if(tdt->tdt_fd == -1) {
TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
TAILQ_REMOVE(&dm->dm_table_queue, tdt, tdt_pending_link);
} else {
th_dvb_mux_instance_t *tdmi = dm->dm_current_tdmi;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
close(tdt->tdt_fd);
}

View file

@ -62,8 +62,9 @@ close_service(th_dvb_adapter_t *tda, service_t *s)
*
*/
static void
open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
open_table(dvb_mux_t *dm, th_dvb_table_t *tdt)
{
th_dvb_mux_instance_t *tdmi = dm->dm_current_tdmi;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
assert(tdt->tdt_pid < 0x2000);
tda->tda_table_filter[tdt->tdt_pid] = 1;
@ -74,8 +75,9 @@ open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
*
*/
static void
close_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
close_table(dvb_mux_t *dm, th_dvb_table_t *tdt)
{
th_dvb_mux_instance_t *tdmi = dm->dm_current_tdmi;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
assert(tdt->tdt_pid < 0x2000);
tda->tda_table_filter[tdt->tdt_pid] = 0;
@ -99,18 +101,17 @@ got_section(const uint8_t *data, size_t len, void *opaque)
* so we need to be a bit careful here
*/
static void
dvb_table_raw_dispatch(th_dvb_mux_instance_t *tdmi,
const dvb_table_feed_t *dtf)
dvb_table_raw_dispatch(dvb_mux_t *dm, const dvb_table_feed_t *dtf)
{
int pid = (dtf->dtf_tsb[1] & 0x1f) << 8 | dtf->dtf_tsb[2];
th_dvb_table_t *vec[tdmi->tdmi_num_tables], *tdt;
th_dvb_table_t *vec[dm->dm_num_tables], *tdt;
int i = 0;
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) {
LIST_FOREACH(tdt, &dm->dm_tables, tdt_link) {
vec[i++] = tdt;
tdt->tdt_refcount++;
}
assert(i == tdmi->tdmi_num_tables);
int len = tdmi->tdmi_num_tables; // can change during callbacks
assert(i == dm->dm_num_tables);
int len = dm->dm_num_tables; // can change during callbacks
for(i = 0; i < len; i++) {
tdt = vec[i];
if(!tdt->tdt_destroyed) {
@ -144,8 +145,8 @@ dvb_table_input(void *aux)
pthread_mutex_lock(&global_lock);
if((tdmi = tda->tda_mux_current) != NULL)
dvb_table_raw_dispatch(tdmi, dtf);
if((tdmi = tda->tda_current_tdmi) != NULL)
dvb_table_raw_dispatch(tdmi->tdmi_mux, dtf);
pthread_mutex_unlock(&global_lock);
free(dtf);

218
src/dvb/dvb_linux.c Normal file
View file

@ -0,0 +1,218 @@
/*
* DVB for Linux
* Copyright (C) 2013 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include "tvheadend.h"
#include "dvb.h"
#include "dvb_support.h"
typedef struct linux_dvb_frontend {
dvb_hardware_t ldf_dh;
int ldf_adapterid;
int ldf_frontend;
int ldf_fd; // if -1, frontend is closed
} linux_dvb_frontend_t;
static const idclass_t linux_dvb_hardware_class = {
.ic_class = "linux_dvb_hardware",
.ic_get_childs = dvb_hardware_get_childs,
.ic_get_title = dvb_hardware_get_title,
.ic_properties = (const property_t[]){
{
"name", "Name", PT_STR,
offsetof(dvb_hardware_t, dh_name),
.notify = &idnode_notify_title_changed,
}, {}},
};
static const idclass_t linux_dvb_adapter_class = {
.ic_class = "linux_dvb_adapter",
.ic_super = &linux_dvb_hardware_class,
};
static const idclass_t linux_dvb_frontend_class = {
.ic_class = "linux_dvb_frontend",
.ic_super = &linux_dvb_hardware_class,
};
static const idclass_t linux_dvbc_frontend_class = {
.ic_leaf = 1,
.ic_class = "linux_dvbc_frontend",
.ic_super = &linux_dvb_frontend_class,
};
static const idclass_t linux_dvbt_frontend_class = {
.ic_leaf = 1,
.ic_class = "linux_dvbt_frontend",
.ic_super = &linux_dvb_frontend_class,
};
static const idclass_t linux_atsc_frontend_class = {
.ic_leaf = 1,
.ic_class = "linux_atsc_frontend",
.ic_super = &linux_dvb_frontend_class,
};
static const idclass_t linux_dvbs_frontend_class = {
.ic_class = "linux_dvbs_frontend",
.ic_super = &linux_dvb_frontend_class,
};
/**
*
*/
static void
linux_dvb_frontend_create(dvb_hardware_t *parent, int adapterid,
int frontendid, const struct dvb_frontend_info *dfi)
{
const idclass_t *class;
switch(dfi->type) {
case FE_OFDM:
class = &linux_dvbt_frontend_class;
break;
case FE_QAM:
class = &linux_dvbc_frontend_class;
break;
case FE_QPSK:
class = &linux_dvbs_frontend_class;
break;
case FE_ATSC:
class = &linux_atsc_frontend_class;
break;
default:
return;
}
// dvb_hardware_t *fe =
dvb_hardware_create(class,
sizeof(linux_dvb_frontend_t), parent, NULL, dfi->name);
}
/**
*
*/
static void
add_adapter(int aid)
{
int frontends;
int demuxers;
int dvrs;
int i;
char path[PATH_MAX];
dvb_hardware_t *a = NULL;
for(frontends = 0; frontends < 32; frontends++) {
snprintf(path, sizeof(path), "/dev/dvb/adapter%d/frontend%d",
aid, frontends);
if(access(path, R_OK | W_OK))
break;
}
if(frontends == 0)
return; // Nothing here
for(demuxers = 0; demuxers < 32; demuxers++) {
snprintf(path, sizeof(path), "/dev/dvb/adapter%d/demux%d",
aid, demuxers);
if(access(path, R_OK | W_OK))
break;
}
for(dvrs = 0; dvrs < 32; dvrs++) {
snprintf(path, sizeof(path), "/dev/dvb/adapter%d/dvr%d",
aid, dvrs);
if(access(path, R_OK | W_OK))
break;
}
tvhlog(LOG_INFO, "DVB",
"Linux DVB adapter %d: %d frontends, %d demuxers, %d DVRs",
aid, frontends, demuxers, dvrs);
for(i = 0; i < frontends; i++) {
snprintf(path, sizeof(path), "/dev/dvb/adapter%d/frontend%d", aid, i);
int fd = tvh_open(path, O_RDWR | O_NONBLOCK, 0);
if(fd == -1) {
tvhlog(LOG_ALERT, "DVB",
"Unable to open %s -- %s", path, strerror(errno));
continue;
}
struct dvb_frontend_info dfi;
int r = ioctl(fd, FE_GET_INFO, &dfi);
close(fd);
if(r) {
tvhlog(LOG_ALERT, "DVB", "%s: Unable to query adapter", path);
continue;
}
if(a == NULL) {
char name[512];
snprintf(name, sizeof(name), "/dev/dvb/adapter%d", aid);
a = dvb_hardware_create(&linux_dvb_adapter_class, sizeof(dvb_hardware_t),
NULL, NULL, name);
}
linux_dvb_frontend_create(a, aid, i, &dfi);
}
}
/**
*
*/
void
dvb_linux_init(void)
{
int i;
for(i = 0 ; i < 32; i++)
add_adapter(i);
}

File diff suppressed because it is too large Load diff

255
src/dvb/dvb_network.c Normal file
View file

@ -0,0 +1,255 @@
/*
* TV Input - Linux DVB interface
* Copyright (C) 2007 - 2012 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 <assert.h>
#include "tvheadend.h"
#include "packet.h"
#include "dvb.h"
#include "epggrab.h"
#include "settings.h"
#include "dvb_support.h"
const static struct strtab typetab[] = {
{"DVB-T", FE_OFDM},
{"DVB-C", FE_QAM},
{"DVB-S", FE_QPSK},
{"ATSC", FE_ATSC},
};
struct dvb_network_list dvb_networks;
static idnode_t **dvb_network_get_childs(struct idnode *self);
static const char *dvb_network_get_title(struct idnode *self);
static void dvb_network_save(idnode_t *in);
static const idclass_t dvb_network_class = {
.ic_class = "dvbnetwork",
.ic_get_childs = dvb_network_get_childs,
.ic_get_title = dvb_network_get_title,
.ic_save = dvb_network_save,
.ic_properties = (const property_t[]){
{
"name", "Name", PT_STR,
offsetof(dvb_network_t, dn_name),
.notify = &idnode_notify_title_changed,
}, {
"autodiscovery", "Auto discovery", PT_BOOL,
offsetof(dvb_network_t, dn_autodiscovery)
}, {
"nitoid", "NIT OID", PT_INT,
offsetof(dvb_network_t, dn_nitoid)
}, {
"disable_pmt_monitor", "Disable PMT monitor", PT_BOOL,
offsetof(dvb_network_t, dn_disable_pmt_monitor)
}, {}},
};
/**
*
*/
dvb_network_t *
dvb_network_create(int fe_type, const char *uuid)
{
char defname[64];
dvb_network_t *dn = calloc(1, sizeof(dvb_network_t));
if(idnode_insert(&dn->dn_id, uuid, &dvb_network_class)) {
free(dn);
return NULL;
}
dn->dn_fe_type = fe_type;
TAILQ_INIT(&dn->dn_initial_scan_pending_queue);
TAILQ_INIT(&dn->dn_initial_scan_current_queue);
dn->dn_autodiscovery = fe_type != FE_QPSK;
snprintf(defname, sizeof(defname), "%s network", val2str(fe_type, typetab));
dn->dn_name = strdup(defname);
LIST_INSERT_HEAD(&dvb_networks, dn, dn_global_link);
return dn;
}
/**
*
*/
static const char *
dvb_network_get_title(struct idnode *self)
{
dvb_network_t *dn = (dvb_network_t *)self;
return dn->dn_name;
}
/**
*
*/
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)
{
const char *s = htsmsg_get_str(m, "type");
if(s == NULL)
return;
int fetype = str2val(s, typetab);
if(fetype == -1)
return;
dvb_network_t *dn = dvb_network_create(fetype, uuid);
if(dn == NULL)
return;
htsmsg_delete_field(m, "type");
prop_write_values(dn, dvb_network_class.ic_properties, m);
dvb_mux_load(dn);
dvb_network_schedule_initial_scan(dn);
}
/**
*
*/
static void
dvb_network_save(idnode_t *in)
{
dvb_network_t *dn = (dvb_network_t *)in;
htsmsg_t *m = htsmsg_create_map();
lock_assert(&global_lock);
htsmsg_add_str(m, "type", val2str(dn->dn_fe_type, typetab));
prop_read_values(in, dvb_network_class.ic_properties, m);
hts_settings_save(m, "dvb/networks/%s/config",
idnode_uuid_as_str(in));
htsmsg_destroy(m);
}
/**
*
*/
static void
dvb_network_initial_scan(void *aux)
{
dvb_network_t *dn = aux;
dvb_mux_t *dm;
while((dm = TAILQ_FIRST(&dn->dn_initial_scan_pending_queue)) != NULL) {
assert(dm->dm_scan_status == DM_SCAN_PENDING);
if(dvb_mux_tune(dm, "initial scan", 1))
break;
assert(dm->dm_scan_status == DM_SCAN_CURRENT);
}
gtimer_arm(&dn->dn_initial_scan_timer, dvb_network_initial_scan, dn, 10);
}
/**
*
*/
void
dvb_network_schedule_initial_scan(dvb_network_t *dn)
{
gtimer_arm(&dn->dn_initial_scan_timer, dvb_network_initial_scan, dn, 0);
}
/**
*
*/
void
dvb_network_init(void)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
if((l = hts_settings_load_r(1, "dvb/networks")) == 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_network_load(c, f->hmf_name);
}
htsmsg_destroy(l);
}
/**
*
*/
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;
}

View file

@ -35,8 +35,8 @@
*
*/
static void
dvb_mux_preconf_add(th_dvb_adapter_t *tda, const network_t *net,
const char *source, const char *satconf)
dvb_mux_preconf_add(dvb_network_t *dn, const network_t *net,
const char *source)
{
const mux_t *m;
struct dvb_mux_conf dmc;
@ -48,7 +48,7 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const network_t *net,
dmc.dmc_fe_params.inversion = INVERSION_AUTO;
dmc.dmc_fe_params.frequency = m->freq;
switch(tda->tda_type) {
switch(dn->dn_fe_type) {
case FE_OFDM:
dmc.dmc_fe_params.u.ofdm.bandwidth = m->bw;
dmc.dmc_fe_params.u.ofdm.constellation = m->constellation;
@ -96,9 +96,11 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const network_t *net,
break;
}
#if TODO_FIX_THIS
dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0);
#endif
dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, source, 1, 1, NULL, NULL, 1, NULL);
dvb_mux_create(dn, &dmc, 0, 0xffff, NULL, source, 1, 1, NULL, NULL, 1, NULL);
}
}
@ -107,8 +109,7 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const network_t *net,
*
*/
int
dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id,
const char *satconf)
dvb_mux_preconf_add_network(dvb_network_t *dn, const char *id)
{
region_list_t *list;
const region_t *r;
@ -117,7 +118,7 @@ dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id,
snprintf(source, sizeof(source), "built-in configuration from \"%s\"", id);
switch(tda->tda_type) {
switch(dn->dn_fe_type) {
case FE_QAM:
list = &regions_DVBC;
break;
@ -137,7 +138,7 @@ dvb_mux_preconf_add_network(th_dvb_adapter_t *tda, const char *id,
LIST_FOREACH(r, list, link) {
LIST_FOREACH(n, &r->networks, link) {
if(!strcmp(n->id, id)) {
dvb_mux_preconf_add(tda, n, source, satconf);
dvb_mux_preconf_add(dn, n, source);
break;
}
}

View file

@ -23,7 +23,6 @@
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,
const char *satconf);
int dvb_mux_preconf_add_network(dvb_network_t *dn, const char *id);
#endif /* DVB_MUXCONFIG_H */

View file

@ -87,14 +87,7 @@ dvb_satconf_entry_find(th_dvb_adapter_t *tda, const char *id, int create)
static void
satconf_destroy(th_dvb_adapter_t *tda, dvb_satconf_t *sc)
{
th_dvb_mux_instance_t *tdmi;
while((tdmi = LIST_FIRST(&sc->sc_tdmis)) != NULL) {
tdmi->tdmi_conf.dmc_satconf = NULL;
LIST_REMOVE(tdmi, tdmi_satconf_link);
}
TAILQ_REMOVE(&tda->tda_satconfs, sc, sc_adapter_link);
TAILQ_REMOVE(&tda->tda_satconfs, sc, sc_adapter_link);
free(sc->sc_id);
free(sc->sc_name);
free(sc->sc_comment);

View file

@ -44,7 +44,20 @@
#include "dvb_support.h"
#include "notify.h"
static const char *dvb_service_get_title(struct idnode *self);
const idclass_t dvb_service_class = {
.ic_super = &service_class,
.ic_class = "dvbservice",
.ic_get_title = dvb_service_get_title,
// .ic_get_childs = dvb_service_get_childs,
.ic_properties = (const property_t[]){
{
"dvb_eit_enable", "Use EPG", PT_BOOL,
offsetof(service_t, s_dvb_eit_enable)
}, {
}}
};
/**
@ -55,74 +68,49 @@
* transports that is subscribing to the adapter
*/
static int
dvb_service_start(service_t *t, unsigned int weight, int force_start)
dvb_service_start(service_t *s, int instance)
{
int w, r;
th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter;
th_dvb_mux_instance_t *tdmi = tda->tda_mux_current;
int r;
dvb_mux_t *dm = s->s_dvb_mux;
th_dvb_mux_instance_t *tdmi;
assert(s->s_status == SERVICE_IDLE);
lock_assert(&global_lock);
if(tda->tda_rootpath == NULL)
return SM_CODE_NO_HW_ATTACHED;
LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link)
if(tdmi->tdmi_adapter->tda_instance == instance)
break;
if(t->s_dvb_mux_instance && !t->s_dvb_mux_instance->tdmi_enabled)
return SM_CODE_MUX_NOT_ENABLED; /* Mux is disabled */
assert(tdmi != NULL); // We should always find this instance
/* Check if adapter is idle, or already tuned */
r = dvb_fe_tune_tdmi(tdmi, "service start");
if(tdmi != NULL &&
(tdmi != t->s_dvb_mux_instance ||
tda->tda_hostconnection == HOSTCONNECTION_USB12)) {
w = service_compute_weight(&tda->tda_transports);
if(w && w >= weight && !force_start)
/* We are outranked by weight, cant use it */
return SM_CODE_NOT_FREE;
if(LIST_FIRST(&tdmi->tdmi_subscriptions) != NULL)
return SM_CODE_NOT_FREE;
dvb_adapter_clean(tda);
}
r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start");
pthread_mutex_lock(&tda->tda_delivery_mutex);
if(!r)
LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
if (tda->tda_locked) {
if(!r)
tda->tda_open_service(tda, t);
dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid);
if(!r) {
th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter;
pthread_mutex_lock(&tda->tda_delivery_mutex);
LIST_INSERT_HEAD(&tda->tda_transports, s, s_active_link);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
if (tda->tda_locked) {
tda->tda_open_service(tda, s);
dvb_table_add_pmt(dm, s->s_pmt_pid);
}
}
return r;
}
/**
*
*/
static void
dvb_service_stop(service_t *t)
static th_dvb_adapter_t *
dvb_service_get_tda(service_t *s)
{
th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter;
lock_assert(&global_lock);
pthread_mutex_lock(&tda->tda_delivery_mutex);
LIST_REMOVE(t, s_active_link);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
tda->tda_close_service(tda, t);
t->s_status = SERVICE_IDLE;
dvb_mux_t *dm = s->s_dvb_mux;
assert(dm->dm_current_tdmi != NULL);
th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter;
assert(tda != NULL);
return tda;
}
@ -130,12 +118,51 @@ dvb_service_stop(service_t *t)
*
*/
static void
dvb_service_refresh(service_t *t)
dvb_service_stop(service_t *s)
{
th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter;
th_dvb_adapter_t *tda = dvb_service_get_tda(s);
lock_assert(&global_lock);
tda->tda_open_service(tda, t);
pthread_mutex_lock(&tda->tda_delivery_mutex);
LIST_REMOVE(s, s_active_link);
pthread_mutex_unlock(&tda->tda_delivery_mutex);
tda->tda_close_service(tda, s);
s->s_status = SERVICE_IDLE;
}
/**
*
*/
static void
dvb_service_refresh(service_t *s)
{
th_dvb_adapter_t *tda = dvb_service_get_tda(s);
lock_assert(&global_lock);
tda->tda_open_service(tda, s);
}
/**
*
*/
static void
dvb_service_enlist(struct service *s, struct service_instance_list *sil)
{
dvb_mux_t *dm = s->s_dvb_mux;
th_dvb_mux_instance_t *tdmi;
dvb_create_tdmis(dm);
LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) {
if(tdmi->tdmi_tune_failed)
continue; // The hardware is not able to tune to this frequency, never consider it
service_instance_add(sil, s, tdmi->tdmi_adapter->tda_instance, 100,
tdmi_current_weight(tdmi));
}
}
/**
@ -144,9 +171,11 @@ dvb_service_refresh(service_t *t)
static int
dvb_service_is_enabled(service_t *t)
{
th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance;
return t->s_dvb_mux->dm_enabled;
#if TODO_FIX_THIS
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
return tda->tda_enabled && tdmi->tdmi_enabled && t->s_enabled && t->s_pmt_pid;
#endif
}
@ -160,6 +189,7 @@ dvb_service_save(service_t *t)
lock_assert(&global_lock);
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&t->s_id));
htsmsg_add_u32(m, "service_id", t->s_dvb_service_id);
htsmsg_add_u32(m, "pmt", t->s_pmt_pid);
htsmsg_add_u32(m, "stype", t->s_servicetype);
@ -179,7 +209,7 @@ dvb_service_save(service_t *t)
if(t->s_dvb_charset != NULL)
htsmsg_add_str(m, "dvb_charset", t->s_dvb_charset);
htsmsg_add_u32(m, "dvb_eit_enable", t->s_dvb_eit_enable);
if(t->s_default_authority)
@ -191,13 +221,15 @@ dvb_service_save(service_t *t)
pthread_mutex_lock(&t->s_stream_mutex);
psi_save_service_settings(m, t);
pthread_mutex_unlock(&t->s_stream_mutex);
hts_settings_save(m, "dvbtransports/%s/%s",
t->s_dvb_mux_instance->tdmi_identifier,
t->s_identifier);
dvb_mux_t *dm = t->s_dvb_mux;
hts_settings_save(m, "dvb/networks/%s/muxes/%s/services/%04x",
idnode_uuid_as_str(&dm->dm_dn->dn_id),
dm->dm_local_identifier,
t->s_dvb_service_id);
htsmsg_destroy(m);
dvb_service_notify(t);
}
@ -205,7 +237,7 @@ dvb_service_save(service_t *t)
* Load config for the given mux
*/
void
dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier)
dvb_service_load(dvb_mux_t *dm)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
@ -213,19 +245,15 @@ dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier)
const char *s;
unsigned int u32;
service_t *t;
int old;
lock_assert(&global_lock);
/* HACK - use provided identifier to load config incase we've migrated
* mux freq */
if (!tdmi_identifier)
tdmi_identifier = tdmi->tdmi_identifier;
old = strcmp(tdmi_identifier, tdmi->tdmi_identifier);
if((l = hts_settings_load("dvbtransports/%s", tdmi_identifier)) == NULL)
l = hts_settings_load("dvb/networks/%s/muxes/%s/services",
idnode_uuid_as_str(&dm->dm_dn->dn_id),
dm->dm_local_identifier);
if(l == NULL)
return;
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
@ -235,8 +263,9 @@ dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier)
if(htsmsg_get_u32(c, "pmt", &pmt))
continue;
t = dvb_service_find(tdmi, sid, pmt, f->hmf_name);
const char *uuid = htsmsg_get_str(c, "uuid");
t = dvb_service_find(dm, sid, pmt, uuid);
htsmsg_get_u32(c, "stype", &t->s_servicetype);
if(htsmsg_get_u32(c, "scrambled", &u32))
@ -281,37 +310,16 @@ dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier)
t->s_prefcapid = u32;
/* HACK - force save for old config */
if(old)
if(uuid == NULL) {
// If service config on disk lacked UUID (for whatever reason),
// write it back
dvb_service_save(t);
}
}
/* HACK - remove old settings */
if(old) {
HTSMSG_FOREACH(f, l)
hts_settings_remove("dvbtransports/%s/%s", tdmi_identifier, f->hmf_name);
hts_settings_remove("dvbtransports/%s", tdmi_identifier);
}
htsmsg_destroy(l);
}
/**
* 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_service_quality(service_t *t)
{
th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance;
lock_assert(&global_lock);
return tdmi->tdmi_adapter->tda_qmon ? tdmi->tdmi_quality : 100;
}
/**
* Generate a descriptive name for the source
@ -319,8 +327,7 @@ dvb_service_quality(service_t *t)
static void
dvb_service_setsourceinfo(service_t *t, struct source_info *si)
{
th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance;
char buf[100];
dvb_mux_t *dm = t->s_dvb_mux;
memset(si, 0, sizeof(struct source_info));
@ -328,16 +335,10 @@ dvb_service_setsourceinfo(service_t *t, struct source_info *si)
si->si_type = S_MPEG_TS;
if(tdmi->tdmi_adapter->tda_rootpath != NULL)
si->si_device = strdup(tdmi->tdmi_adapter->tda_rootpath);
if(dm->dm_network_name != NULL)
si->si_network = strdup(dm->dm_network_name);
si->si_adapter = strdup(tdmi->tdmi_adapter->tda_displayname);
if(tdmi->tdmi_network != NULL)
si->si_network = strdup(tdmi->tdmi_network);
dvb_mux_nicename(buf, sizeof(buf), tdmi);
si->si_mux = strdup(buf);
si->si_mux = strdup(dvb_mux_nicename(dm));
if(t->s_provider != NULL)
si->si_provider = strdup(t->s_provider);
@ -353,42 +354,45 @@ dvb_service_setsourceinfo(service_t *t, struct source_info *si)
static int
dvb_grace_period(service_t *t)
{
#if TODO_FIX_THIS
if (t->s_dvb_mux_instance && t->s_dvb_mux_instance->tdmi_adapter)
return t->s_dvb_mux_instance->tdmi_adapter->tda_grace_period ?: 10;
#endif
return 10;
}
/*
* Find transport based on the DVB identification
*/
service_t *
dvb_service_find3
(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi,
const char *netname, uint16_t onid, uint16_t tsid, uint16_t sid,
int enabled, int epgprimary)
dvb_service_find3(dvb_network_t *dn, dvb_mux_t *dm,
const char *netname, uint16_t onid, uint16_t tsid,
uint16_t sid, int enabled, int epgprimary)
{
service_t *svc;
if (tdmi) {
LIST_FOREACH(svc, &tdmi->tdmi_transports, s_group_link) {
if (dm != NULL) {
LIST_FOREACH(svc, &dm->dm_services, s_group_link) {
if (sid != svc->s_dvb_service_id) continue;
if (enabled && !svc->s_enabled) continue;
if (epgprimary && !service_is_primary_epg(svc)) continue;
return svc;
}
} else if (tda) {
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
if (enabled && !tdmi->tdmi_enabled) continue;
if (onid && onid != tdmi->tdmi_network_id) continue;
if (tsid && tsid != tdmi->tdmi_transport_stream_id) continue;
if (netname && strcmp(netname, tdmi->tdmi_network ?: "")) continue;
if ((svc = dvb_service_find3(tda, tdmi, NULL, 0, 0, sid,
enabled, epgprimary)))
} else if (dn) {
LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link) {
if (enabled && !dm->dm_enabled) continue;
if (onid && onid != dm->dm_network_id) continue;
if (tsid && tsid != dm->dm_transport_stream_id) continue;
if (netname && strcmp(netname, dm->dm_network_name ?: "")) continue;
if ((svc = dvb_service_find3(dn, dm, NULL, 0, 0, sid,
enabled, epgprimary)))
return svc;
}
} else {
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
if ((svc = dvb_service_find3(tda, NULL, netname, onid, tsid,
sid, enabled, epgprimary)))
LIST_FOREACH(dn, &dvb_networks, dn_global_link)
if ((svc = dvb_service_find3(dn, NULL, netname, onid, tsid,
sid, enabled, epgprimary)))
return svc;
}
return NULL;
@ -401,23 +405,24 @@ dvb_service_find3
* If it cannot be found we create it if 'pmt_pid' is also set
*/
service_t *
dvb_service_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
const char *identifier)
dvb_service_find(dvb_mux_t *dm, uint16_t sid, int pmt_pid, const char *uuid)
{
return dvb_service_find2(tdmi, sid, pmt_pid, identifier, NULL);
return dvb_service_find2(dm, sid, pmt_pid, uuid, NULL);
}
/**
*
*/
service_t *
dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
const char *identifier, int *save)
dvb_service_find2(dvb_mux_t *dm, uint16_t sid, int pmt_pid,
const char *uuid, int *save)
{
service_t *t;
char tmp[200];
char buf[200];
lock_assert(&global_lock);
LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) {
LIST_FOREACH(t, &dm->dm_services, s_group_link) {
if(t->s_dvb_service_id == sid)
break;
}
@ -431,15 +436,14 @@ dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
return t;
}
if(identifier == NULL) {
snprintf(tmp, sizeof(tmp), "%s_%04x", tdmi->tdmi_identifier, sid);
identifier = tmp;
}
if(pmt_pid == 0)
return NULL;
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_DEBUG, "dvb", "Add service \"%s\" on \"%s\"", identifier, buf);
tvhlog(LOG_DEBUG, "dvb", "Add service \"0x%x\" on \"%s\"", sid,
dvb_mux_nicename(dm));
t = service_create(uuid, S_MPEG_TS, &dvb_service_class);
t = service_create(identifier, SERVICE_TYPE_DVB, S_MPEG_TS);
if (save) *save = 1;
t->s_dvb_service_id = sid;
@ -450,18 +454,16 @@ dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
t->s_stop_feed = dvb_service_stop;
t->s_config_save = dvb_service_save;
t->s_setsourceinfo = dvb_service_setsourceinfo;
t->s_quality_index = dvb_service_quality;
t->s_grace_period = dvb_grace_period;
t->s_is_enabled = dvb_service_is_enabled;
t->s_enlist = dvb_service_enlist;
t->s_dvb_mux_instance = tdmi;
LIST_INSERT_HEAD(&tdmi->tdmi_transports, t, s_group_link);
t->s_dvb_mux = dm;
LIST_INSERT_HEAD(&dm->dm_services, t, s_group_link);
pthread_mutex_lock(&t->s_stream_mutex);
pthread_mutex_lock(&t->s_stream_mutex);
service_make_nicename(t);
pthread_mutex_unlock(&t->s_stream_mutex);
dvb_adapter_notify(tdmi->tdmi_adapter);
pthread_mutex_unlock(&t->s_stream_mutex);
return t;
}
@ -469,52 +471,18 @@ dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
/**
*
*/
htsmsg_t *
dvb_service_build_msg(service_t *t)
static const char *
dvb_service_get_title(struct idnode *self)
{
th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance;
htsmsg_t *m = htsmsg_create_map();
uint16_t caid;
char buf[100];
htsmsg_add_str(m, "id", t->s_identifier);
htsmsg_add_u32(m, "enabled", t->s_enabled);
htsmsg_add_u32(m, "channel", t->s_channel_number);
service_t *s = (service_t *)self;
static char buf[100];
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);
snprintf(buf, sizeof(buf), "%s (0x%04X)", service_servicetype_txt(t), t->s_servicetype);
htsmsg_add_str(m, "type", buf);
htsmsg_add_str(m, "typestr", service_servicetype_txt(t));
htsmsg_add_u32(m, "typenum", t->s_servicetype);
htsmsg_add_str(m, "svcname", t->s_svcname ?: "");
htsmsg_add_str(m, "provider", t->s_provider ?: "");
htsmsg_add_str(m, "network", tdmi->tdmi_network ?: "");
if((caid = service_get_encryption(t)) != 0)
htsmsg_add_str(m, "encryption", psi_caid2name(caid));
dvb_mux_nicefreq(buf, sizeof(buf), tdmi);
htsmsg_add_str(m, "mux", buf);
if(tdmi->tdmi_conf.dmc_satconf != NULL)
htsmsg_add_str(m, "satconf", tdmi->tdmi_conf.dmc_satconf->sc_id);
if(t->s_ch != NULL)
htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
if(t->s_dvb_charset != NULL)
htsmsg_add_str(m, "dvb_charset", t->s_dvb_charset);
htsmsg_add_u32(m, "dvb_eit_enable", t->s_dvb_eit_enable);
htsmsg_add_u32(m, "prefcapid", t->s_prefcapid);
return m;
if(s->s_svcname) {
return s->s_svcname;
} else {
snprintf(buf, sizeof(buf), "Service-0x%04x", s->s_dvb_service_id);
return buf;
}
}
@ -529,16 +497,3 @@ dvb_service_notify_by_adapter(th_dvb_adapter_t *tda)
notify_by_msg("dvbService", m);
}
/**
*
*/
void
dvb_service_notify(service_t *t)
{
th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance;
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "adapterId", tdmi->tdmi_adapter->tda_identifier);
notify_by_msg("dvbService", m);
}

View file

@ -423,77 +423,57 @@ dvb_polarisation_to_str_long(int pol)
}
th_dvb_adapter_t *
dvb_adapter_find_by_identifier(const char *identifier)
/**
*
*/
static void
nicenum(char *x, size_t siz, unsigned int v, const char *postfix)
{
th_dvb_adapter_t *tda;
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
if(!strcmp(identifier, tda->tda_identifier))
return tda;
return NULL;
if(v < 1000)
snprintf(x, siz, "%d%s", v, postfix);
else if(v < 1000000)
snprintf(x, siz, "%d,%03d%s", v / 1000, v % 1000, postfix);
else if(v < 1000000000)
snprintf(x, siz, "%d,%03d,%03d%s",
v / 1000000, (v % 1000000) / 1000, v % 1000, postfix);
else
snprintf(x, siz, "%d,%03d,%03d,%03d%s",
v / 1000000000, (v % 1000000000) / 1000000,
(v % 1000000) / 1000, v % 1000, postfix);
}
/**
*
*/
static const char *
nicenum(char *x, size_t siz, unsigned int v)
const char *
dvb_mux_nicefreq(const dvb_mux_t *dm)
{
if(v < 1000)
snprintf(x, siz, "%d", v);
else if(v < 1000000)
snprintf(x, siz, "%d,%03d", v / 1000, v % 1000);
else if(v < 1000000000)
snprintf(x, siz, "%d,%03d,%03d",
v / 1000000, (v % 1000000) / 1000, v % 1000);
else
snprintf(x, siz, "%d,%03d,%03d,%03d",
v / 1000000000, (v % 1000000000) / 1000000,
(v % 1000000) / 1000, v % 1000);
return x;
static char ret[100];
int f = dm->dm_conf.dmc_fe_params.frequency;
nicenum(ret, sizeof(ret), dm->dm_dn->dn_fe_type == FE_QPSK ? f : f / 1000,
" kHz");
return ret;
}
/**
*
*
*/
void
dvb_mux_nicefreq(char *buf, size_t size, th_dvb_mux_instance_t *tdmi)
const char *
dvb_mux_nicename(const dvb_mux_t *dm)
{
char freq[50];
static char ret[100];
const char *n = dm->dm_network_name;
if(tdmi->tdmi_adapter->tda_type == FE_QPSK) {
nicenum(freq, sizeof(freq), tdmi->tdmi_conf.dmc_fe_params.frequency);
snprintf(buf, size, "%s kHz", freq);
} else {
nicenum(freq, sizeof(freq),
tdmi->tdmi_conf.dmc_fe_params.frequency / 1000);
snprintf(buf, size, "%s kHz", freq);
}
}
/**
*
*/
void
dvb_mux_nicename(char *buf, size_t size, th_dvb_mux_instance_t *tdmi)
{
char freq[50];
const char *n = tdmi->tdmi_network;
if(tdmi->tdmi_adapter->tda_type == FE_QPSK) {
nicenum(freq, sizeof(freq), tdmi->tdmi_conf.dmc_fe_params.frequency);
snprintf(buf, size, "%s%s%s kHz %s (%s)",
n?:"", n ? ": ":"", freq,
dvb_polarisation_to_str_long(tdmi->tdmi_conf.dmc_polarisation),
tdmi->tdmi_conf.dmc_satconf ? tdmi->tdmi_conf.dmc_satconf->sc_name : "No satconf");
} else {
nicenum(freq, sizeof(freq), tdmi->tdmi_conf.dmc_fe_params.frequency / 1000);
snprintf(buf, size, "%s%s%s kHz", n?:"", n ? ": ":"", freq);
}
snprintf(ret, sizeof(ret), "%s%s%s%s%s",
n ?: "",
n ? ": " : "",
dvb_mux_nicefreq(dm),
dm->dm_dn->dn_fe_type == FE_QPSK ? " " : "",
dm->dm_dn->dn_fe_type == FE_QPSK ?
dvb_polarisation_to_str_long(dm->dm_conf.dmc_polarisation) :
"");
return ret;
}

View file

@ -80,10 +80,11 @@ const char *dvb_polarisation_to_str(int pol);
const char *dvb_polarisation_to_str_long(int pol);
th_dvb_adapter_t *dvb_adapter_find_by_identifier(const char *identifier);
th_dvb_mux_instance_t *dvb_mux_find_by_identifier(const char *identifier);
void dvb_mux_nicename(char *buf, size_t size, th_dvb_mux_instance_t *tdmi);
int dvb_mux_badness(th_dvb_mux_instance_t *tdmi);
const char *dvb_mux_status(th_dvb_mux_instance_t *tdmi);
void dvb_mux_nicefreq(char *buf, size_t size, th_dvb_mux_instance_t *tdmi);
const char *dvb_mux_nicefreq(const dvb_mux_t *dm);
const char *dvb_mux_nicename(const dvb_mux_t *dm);
void atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen);

View file

@ -52,24 +52,20 @@ dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi)
{
th_dvb_table_t *tdt;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
char buf[100];
dvb_mux_t *dm = tdmi->tdmi_mux;
if(!tdmi->tdmi_table_initial)
if(dm->dm_scan_status == DM_SCAN_DONE)
return;
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
LIST_FOREACH(tdt, &dm->dm_tables, tdt_link)
if((tdt->tdt_flags & TDT_QUICKREQ) && tdt->tdt_count == 0)
return;
tdmi->tdmi_table_initial = 0;
tda->tda_initial_num_mux--;
dvb_mux_save(tdmi);
dvb_mux_save(dm);
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tvhlog(LOG_DEBUG, "dvb", "\"%s\" initial scan completed for \"%s\"",
tda->tda_rootpath, buf);
dvb_adapter_mux_scanner(tda);
tda->tda_rootpath, dvb_mux_nicename(dm));
dvb_mux_initial_scan_done(dm);
}
@ -87,6 +83,7 @@ dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt)
uint8_t *ptr;
int ret;
th_dvb_mux_instance_t *tdmi = tdt->tdt_tdmi;
dvb_mux_t *dm = tdmi->tdmi_mux;
/* It seems some hardware (or is it the dvb API?) does not
honour the DMX_CHECK_CRC flag, so we check it again */
@ -107,12 +104,12 @@ dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt)
if(chkcrc) len -= 4; /* Strip trailing CRC */
if(tdt->tdt_flags & TDT_CA)
ret = tdt->tdt_callback((th_dvb_mux_instance_t *)tdt,
sec, len + 3, tableid, tdt->tdt_opaque);
ret = tdt->tdt_callback((dvb_mux_t *)tdt,
sec, len + 3, tableid, tdt->tdt_opaque);
else if(tdt->tdt_flags & TDT_TDT)
ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt);
ret = tdt->tdt_callback(dm, ptr, len, tableid, tdt);
else
ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque);
ret = tdt->tdt_callback(dm, ptr, len, tableid, tdt->tdt_opaque);
if(ret == 0)
tdt->tdt_count++;
@ -140,11 +137,12 @@ static void
dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi,
th_dvb_table_t *tdt)
{
dvb_mux_t *dm = tdmi->tdmi_mux;
lock_assert(&global_lock);
assert(tdt->tdt_tdmi == tdmi);
LIST_REMOVE(tdt, tdt_link);
tdmi->tdmi_num_tables--;
tda->tda_close_table(tdmi, tdt);
dm->dm_num_tables--;
tda->tda_close_table(dm, tdt);
free(tdt->tdt_name);
tdt->tdt_destroyed = 1;
dvb_table_release(tdt);
@ -155,18 +153,20 @@ dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi,
* Add a new DVB table
*/
void
tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask,
int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
tdt_add(dvb_mux_t *dm, int tableid, int mask,
int (*callback)(dvb_mux_t *dm, uint8_t *buf, int len,
uint8_t tableid, void *opaque), void *opaque,
const char *name, int flags, int pid)
{
th_dvb_table_t *t;
th_dvb_mux_instance_t *tdmi = dm->dm_current_tdmi;
assert(tdmi != NULL);
// Allow multiple entries per PID, but only one per callback/opaque instance
// TODO: this could mean reading the same data multiple times, and not
// sure how well this will work! I know Andreas has some thoughts on
// this
LIST_FOREACH(t, &tdmi->tdmi_tables, tdt_link) {
LIST_FOREACH(t, &dm->dm_tables, tdt_link) {
if(pid == t->tdt_pid &&
t->tdt_callback == callback && t->tdt_opaque == opaque) {
return;
@ -183,11 +183,11 @@ tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask,
tdt->tdt_table = tableid;
tdt->tdt_mask = mask;
tdt->tdt_tdmi = tdmi;
LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link);
tdmi->tdmi_num_tables++;
LIST_INSERT_HEAD(&dm->dm_tables, tdt, tdt_link);
dm->dm_num_tables++;
tdt->tdt_fd = -1;
tdmi->tdmi_adapter->tda_open_table(tdmi, tdt);
tdmi->tdmi_adapter->tda_open_table(dm, tdt);
}
/**
@ -231,14 +231,14 @@ dvb_desc_def_authority(uint8_t *ptr, int len, char *defauth, size_t dalen)
}
static int
dvb_bat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
dvb_bat_callback(dvb_mux_t *dm, uint8_t *buf, int len,
uint8_t tableid, void *opaque)
{
int i, j, bdlen, tslen, tdlen;
uint8_t dtag, dlen;
uint16_t tsid, onid;
char crid[257];
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
dvb_network_t *dn = dm->dm_dn;
if (tableid != 0x4a) return -1;
bdlen = ((buf[5] & 0xf) << 8) | buf[6];
@ -267,14 +267,13 @@ dvb_bat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
i += 6;
j = 0;
/* Find TDMI */
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
if(tdmi->tdmi_transport_stream_id == tsid &&
tdmi->tdmi_network_id == onid)
/* Find mux */
LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link)
if(dm->dm_transport_stream_id == tsid && dm->dm_network_id == onid)
break;
/* Descriptors */
if (tdmi) {
if (dm != NULL) {
int save = 0;
*crid = 0;
while (j+2 < tdlen) {
@ -289,13 +288,13 @@ dvb_bat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
}
j += dlen;
}
if (*crid && strcmp(tdmi->tdmi_default_authority ?: "", crid)) {
free(tdmi->tdmi_default_authority);
tdmi->tdmi_default_authority = strdup(crid);
if (*crid && strcmp(dm->dm_default_authority ?: "", crid)) {
free(dm->dm_default_authority);
dm->dm_default_authority = strdup(crid);
save = 1;
}
if (save)
dvb_mux_save(tdmi);
dvb_mux_save(dm);
}
i += tdlen;
@ -308,7 +307,7 @@ dvb_bat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
* DVB SDT (Service Description Table)
*/
static int
dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_sdt_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
service_t *t;
@ -328,7 +327,7 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
int l;
uint8_t *dlptr, *dptr;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
dvb_network_t *dn = dm->dm_dn;
if (tableid != 0x42 && tableid != 0x46) return -1;
@ -342,16 +341,16 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
/* Find Transport Stream */
if (tableid == 0x42) {
dvb_mux_set_tsid(tdmi, tsid, 0);
dvb_mux_set_onid(tdmi, onid, 0);
if(tdmi->tdmi_transport_stream_id != tsid || tdmi->tdmi_network_id != onid)
dvb_mux_set_tsid(dm, tsid, 0);
dvb_mux_set_onid(dm, onid, 0);
if(dm->dm_transport_stream_id != tsid || dm->dm_network_id != onid)
return -1;
} else {
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
if(tdmi->tdmi_transport_stream_id == tsid &&
tdmi->tdmi_network_id == onid)
LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link)
if(dm->dm_transport_stream_id == tsid &&
dm->dm_network_id != onid)
break;
if (!tdmi) return -1;
if (!dm) return 0;
}
// version = ptr[2] >> 1 & 0x1f;
@ -385,6 +384,9 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if (len < 0)
break;
if (!(t = dvb_service_find(dm, service_id, 0, NULL)))
continue;
stype = 0;
chname = NULL;
*crid = 0;
@ -430,9 +432,6 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
}
}
if (!(t = dvb_service_find(tdmi, service_id, 0, NULL)))
continue;
if(t->s_servicetype != stype ||
t->s_scrambled != free_ca_mode) {
t->s_servicetype = stype;
@ -444,8 +443,8 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
strcmp(t->s_svcname ?: "", chname))) {
int save2 = 0;
int master = 0;
if (t->s_dvb_mux_instance && t->s_dvb_mux_instance->tdmi_network_id &&
t->s_dvb_mux_instance->tdmi_network_id == tdmi->tdmi_network_id)
if (t->s_dvb_mux && t->s_dvb_mux->dm_network_id &&
t->s_dvb_mux->dm_network_id == dm->dm_network_id)
master = 1;
if (!t->s_provider || master) {
@ -485,14 +484,14 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
/*
* Combined PID 0x11 callback, for stuff commonly found on that PID
*/
int dvb_pidx11_callback
(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
int
dvb_pidx11_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
if (tableid == 0x42 || tableid == 0x46)
return dvb_sdt_callback(tdmi, ptr, len, tableid, opaque);
return dvb_sdt_callback(dm, ptr, len, tableid, opaque);
else if (tableid == 0x4a)
return dvb_bat_callback(tdmi, ptr, len, tableid, opaque);
return dvb_bat_callback(dm, ptr, len, tableid, opaque);
return -1;
}
@ -500,10 +499,9 @@ int dvb_pidx11_callback
* PAT - Program Allocation table
*/
static int
dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_pat_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
uint16_t service, pmt, tsid;
if(len < 5)
@ -515,8 +513,8 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
}
tsid = (ptr[0] << 8) | ptr[1];
dvb_mux_set_tsid(tdmi, tsid, 0);
if (tdmi->tdmi_transport_stream_id != tsid)
dvb_mux_set_tsid(dm, tsid, 0);
if (dm->dm_transport_stream_id != tsid)
return -1;
ptr += 5;
@ -528,9 +526,9 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(service != 0 && pmt != 0) {
int save = 0;
dvb_service_find2(tdmi, service, pmt, NULL, &save);
if (save || !tda->tda_disable_pmt_monitor)
dvb_table_add_pmt(tdmi, pmt);
dvb_service_find2(dm, service, pmt, NULL, &save);
if (save || ! dm->dm_dn->dn_disable_pmt_monitor)
dvb_table_add_pmt(dm, pmt);
}
ptr += 4;
len -= 4;
@ -543,11 +541,11 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* CA - Conditional Access
*/
static int
dvb_ca_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_ca_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
#if ENABLE_CWC
cwc_emm(ptr, len, (uintptr_t)opaque, (void *)tdmi);
cwc_emm(ptr, len, (uintptr_t)opaque, (void *)dm);
#endif
return 0;
}
@ -556,8 +554,8 @@ dvb_ca_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* CAT - Conditional Access Table
*/
static int
dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
dvb_cat_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
int tag, tlen;
uint16_t pid;
@ -583,7 +581,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(pid == 0)
break;
tdt_add(tdmi, 0, 0, dvb_ca_callback, (void *)caid, "CA",
tdt_add(dm, 0, 0, dvb_ca_callback, (void *)caid, "CA",
TDT_CA, pid);
break;
@ -661,7 +659,7 @@ static const fe_hierarchy_t hierarchy_info_tab [8] = {
* Cable delivery descriptor
*/
static int
dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_table_cable_delivery(dvb_mux_t *dm, uint8_t *ptr, int len,
uint16_t tsid, uint16_t onid, const char *netname)
{
struct dvb_mux_conf dmc;
@ -697,10 +695,9 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dmc.dmc_fe_params.u.qam.fec_inner = fec_tab[ptr[10] & 0x07];
dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname,
dvb_mux_create(dm->dm_dn, &dmc, onid, tsid, netname,
"automatic mux discovery", 1, 1, NULL, NULL,
tdmi->tdmi_adapter->tda_autodiscovery, tdmi);
dm->dm_dn->dn_autodiscovery, dm);
return 0;
}
@ -708,7 +705,7 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* Satellite delivery descriptor
*/
static int
dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_table_sat_delivery(dvb_mux_t *dm, uint8_t *ptr, int len,
uint16_t tsid, uint16_t onid, const char *netname)
{
int freq, symrate;
@ -747,8 +744,6 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dmc.dmc_fe_params.u.qam.fec_inner = fec_tab[ptr[10] & 0x0f];
dmc.dmc_polarisation = (ptr[6] >> 5) & 0x03;
// Same satconf (lnb, switch, etc)
dmc.dmc_satconf = tdmi->tdmi_conf.dmc_satconf;
#if DVB_API_VERSION >= 5
int modulation = (ptr[6] & 0x03);
@ -790,9 +785,11 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
#endif
dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname,
"automatic mux discovery", 1, 1, NULL, tdmi->tdmi_conf.dmc_satconf,
tdmi->tdmi_adapter->tda_autodiscovery, tdmi);
#if TODO_FIX_THIS
dvb_mux_create(dm->dm_dn, &dmc, onid, tsid, netname,
"automatic mux discovery", 1, 1, NULL, dm->dm_conf.dmc_satconf,
dm->dm_dn->dn_autodiscovery, dm);
#endif
return 0;
}
@ -802,7 +799,7 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* Terrestrial delivery descriptor
*/
static int
dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_table_terr_delivery(dvb_mux_t *dm, uint8_t *ptr, int len,
uint16_t tsid, uint16_t onid, const char *netname)
{
struct dvb_mux_conf dmc;
@ -829,10 +826,9 @@ dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dmc.dmc_fe_params.u.ofdm.guard_interval=guard_interval_tab[(ptr[6] & 0x18) >> 3];
dmc.dmc_fe_params.u.ofdm.transmission_mode=transmission_mode_tab[(ptr[6] & 0x06) >> 1];
dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname,
dvb_mux_create(dm->dm_dn, &dmc, onid, tsid, netname,
"automatic mux discovery", 1, 1, NULL, NULL,
tdmi->tdmi_adapter->tda_autodiscovery, tdmi);
dm->dm_dn->dn_autodiscovery, dm);
return 0;
}
@ -841,18 +837,18 @@ dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
*
*/
static void
dvb_table_local_channel(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_table_local_channel(dvb_mux_t *dm, uint8_t *ptr, int len,
uint16_t tsid, uint16_t onid)
{
uint16_t sid, chan;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
dvb_network_t *dn = dm->dm_dn;
service_t *t;
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id == onid)
LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link)
if(dm->dm_transport_stream_id == tsid && dm->dm_network_id == onid)
break;
if(tdmi == NULL)
if(dm == NULL)
return;
while(len >= 4) {
@ -860,7 +856,7 @@ dvb_table_local_channel(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
chan = ((ptr[2] & 3) << 8) | ptr[3];
if(chan != 0) {
t = dvb_service_find(tdmi, sid, 0, NULL);
t = dvb_service_find(dm, sid, 0, NULL);
if(t != NULL) {
if(t->s_channel_number != chan) {
@ -881,7 +877,7 @@ dvb_table_local_channel(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* NIT - Network Information Table
*/
static int
dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_nit_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
uint8_t dtag, dlen;
@ -898,8 +894,8 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
if(tableid != 0x40) return -1;
/* Check NID */
if(tdmi->tdmi_adapter->tda_nitoid &&
tdmi->tdmi_adapter->tda_nitoid != network_id)
if(dm->dm_dn->dn_nitoid &&
dm->dm_dn->dn_nitoid != network_id)
return -1;
/* Ignore non-current */
@ -923,8 +919,8 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
case DVB_DESC_NETWORK_NAME:
if(dvb_get_string(netname, sizeof(netname), ptr+2, dlen, NULL, NULL))
return -1;
if((tableid == 0x40) && (!tdmi->tdmi_network || *tdmi->tdmi_network == '\0'))
dvb_mux_set_networkname(tdmi, netname);
if(strcmp(dm->dm_network_name ?: "", netname))
dvb_mux_set_networkname(dm, netname);
break;
}
@ -961,19 +957,19 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
switch(dtag) {
case DVB_DESC_SAT:
if(tdmi->tdmi_adapter->tda_type == FE_QPSK)
dvb_table_sat_delivery(tdmi, ptr+2, dlen, tsid, onid, netname);
if(dm->dm_dn->dn_fe_type == FE_QPSK)
dvb_table_sat_delivery(dm, ptr+2, dlen, tsid, onid, netname);
break;
case DVB_DESC_CABLE:
if(tdmi->tdmi_adapter->tda_type == FE_QAM)
dvb_table_cable_delivery(tdmi, ptr+2, dlen, tsid, onid, netname);
if(dm->dm_dn->dn_fe_type == FE_QAM)
dvb_table_cable_delivery(dm, ptr+2, dlen, tsid, onid, netname);
break;
case DVB_DESC_TERR:
if(tdmi->tdmi_adapter->tda_type == FE_OFDM)
dvb_table_terr_delivery(tdmi, ptr+2, dlen, tsid, onid, netname);
if(dm->dm_dn->dn_fe_type == FE_OFDM)
dvb_table_terr_delivery(dm, ptr+2, dlen, tsid, onid, netname);
break;
case DVB_DESC_LOCAL_CHAN:
dvb_table_local_channel(tdmi, ptr+2, dlen, tsid, onid);
dvb_table_local_channel(dm, ptr+2, dlen, tsid, onid);
break;
}
@ -991,11 +987,10 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* VCT - ATSC Virtual Channel Table
*/
static int
atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
atsc_vct_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
th_dvb_mux_instance_t *tdmi0 = tdmi;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
dvb_network_t *dn = dm->dm_dn;
service_t *t;
int numch;
char chname[256];
@ -1029,20 +1024,18 @@ atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
tsid = (ptr[22] << 8) | ptr[23];
onid = (ptr[24] << 8) | ptr[25];
if(tsid == 0) {
tdmi = tdmi0;
} else {
/* Search all muxes on adapter */
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id == onid)
break;
}
if(tdmi == NULL)
continue;
if(tsid != 0) {
/* Search all muxes on adapter */
LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link)
if(dm->dm_transport_stream_id == tsid && dm->dm_network_id == onid);
break;
}
if(dm == NULL)
continue;
service_id = (ptr[24] << 8) | ptr[25];
if((t = dvb_service_find(tdmi, service_id, 0, NULL)) == NULL)
if((t = dvb_service_find(dm, service_id, 0, NULL)) == NULL)
continue;
atsc_stype = ptr[27] & 0x3f;
@ -1080,23 +1073,24 @@ atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* PMT - Program Mapping Table
*/
static int
dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
dvb_pmt_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
int active = 0;
service_t *t;
th_dvb_table_t *tdt = opaque;
LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) {
LIST_FOREACH(t, &dm->dm_services, s_group_link) {
pthread_mutex_lock(&t->s_stream_mutex);
psi_parse_pmt(t, ptr, len, 1, 1);
if (t->s_pmt_pid == tdt->tdt_pid && t->s_status == SERVICE_RUNNING)
active = 1;
pthread_mutex_unlock(&t->s_stream_mutex);
}
if (tdmi->tdmi_adapter->tda_disable_pmt_monitor && !active)
dvb_tdt_destroy(tdmi->tdmi_adapter, tdmi, tdt);
if (dm->dm_dn->dn_disable_pmt_monitor && !active)
dvb_tdt_destroy(dm->dm_current_tdmi->tdmi_adapter,
dm->dm_current_tdmi, tdt);
return 0;
}
@ -1105,7 +1099,7 @@ dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
* Time Offset table handler
*/
static int
dvb_tot_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
dvb_tot_callback(dvb_mux_t *dm, uint8_t *buf, int len,
uint8_t tableid, void *opaque)
{
uint16_t mjd;
@ -1151,21 +1145,21 @@ dvb_tot_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
* Demux for default DVB tables that we want
*/
static void
dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi)
dvb_table_add_default_dvb(dvb_mux_t *dm)
{
/* Network Information Table */
tdt_add(tdmi, 0, 0, dvb_nit_callback, NULL, "nit",
tdt_add(dm, 0, 0, dvb_nit_callback, NULL, "nit",
TDT_QUICKREQ | TDT_CRC, 0x10);
/* Service Descriptor Table and Bouqeut Allocation Table */
tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11",
tdt_add(dm, 0, 0, dvb_pidx11_callback, NULL, "pidx11",
TDT_QUICKREQ | TDT_CRC, 0x11);
/* Time Offset Table */
tdt_add(tdmi, 0, 0, dvb_tot_callback, NULL, "tot", TDT_CRC, 0x14);
tdt_add(dm, 0, 0, dvb_tot_callback, NULL, "tot", TDT_CRC, 0x14);
}
@ -1173,17 +1167,17 @@ dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi)
* Demux for default ATSC tables that we want
*/
static void
dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi)
dvb_table_add_default_atsc(dvb_mux_t *dm)
{
int tableid;
if(tdmi->tdmi_conf.dmc_fe_params.u.vsb.modulation == VSB_8) {
if(dm->dm_conf.dmc_fe_params.u.vsb.modulation == VSB_8) {
tableid = 0xc8; // Terrestrial
} else {
tableid = 0xc9; // Cable
}
tdt_add(tdmi, tableid, 0xff, atsc_vct_callback, NULL, "vct",
tdt_add(dm, tableid, 0xff, atsc_vct_callback, NULL, "vct",
TDT_QUICKREQ | TDT_CRC, 0x1ffb);
}
@ -1194,26 +1188,26 @@ dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi)
* Setup FD + demux for default tables that we want
*/
void
dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
dvb_table_add_default(dvb_mux_t *dm)
{
/* Program Allocation Table */
tdt_add(tdmi, 0x00, 0xff, dvb_pat_callback, NULL, "pat",
tdt_add(dm, 0x00, 0xff, dvb_pat_callback, NULL, "pat",
TDT_QUICKREQ | TDT_CRC, 0);
/* Conditional Access Table */
tdt_add(tdmi, 0x1, 0xff, dvb_cat_callback, NULL, "cat",
tdt_add(dm, 0x1, 0xff, dvb_cat_callback, NULL, "cat",
TDT_CRC, 1);
switch(tdmi->tdmi_adapter->tda_type) {
switch(dm->dm_dn->dn_fe_type) {
case FE_QPSK:
case FE_OFDM:
case FE_QAM:
dvb_table_add_default_dvb(tdmi);
dvb_table_add_default_dvb(dm);
break;
case FE_ATSC:
dvb_table_add_default_atsc(tdmi);
dvb_table_add_default_atsc(dm);
break;
}
}
@ -1223,24 +1217,26 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
* Setup FD + demux for a services PMT
*/
void
dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid)
dvb_table_add_pmt(dvb_mux_t *dm, int pmt_pid)
{
char pmtname[100];
snprintf(pmtname, sizeof(pmtname), "PMT(%d)", pmt_pid);
tdt_add(tdmi, 0x2, 0xff, dvb_pmt_callback, NULL, pmtname,
tdt_add(dm, 0x2, 0xff, dvb_pmt_callback, NULL, pmtname,
TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid);
}
void
dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid)
dvb_table_rem_pmt(dvb_mux_t *dm, int pmt_pid)
{
th_dvb_mux_instance_t *tdmi = dm->dm_current_tdmi;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_dvb_table_t *tdt = NULL;
LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
LIST_FOREACH(tdt, &dm->dm_tables, tdt_link)
if (tdt->tdt_pid == pmt_pid && tdt->tdt_callback == dvb_pmt_callback)
break;
if (tdt) dvb_tdt_destroy(tda, tdmi, tdt);
if (tdt)
dvb_tdt_destroy(tda, tdmi, tdt);
}
@ -1248,12 +1244,12 @@ dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid)
*
*/
void
dvb_table_flush_all(th_dvb_mux_instance_t *tdmi)
dvb_table_flush_all(dvb_mux_t *dm)
{
th_dvb_mux_instance_t *tdmi = dm->dm_current_tdmi;
th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
th_dvb_table_t *tdt;
while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL)
while((tdt = LIST_FIRST(&dm->dm_tables)) != NULL)
dvb_tdt_destroy(tda, tdmi, tdt);
}

View file

@ -26,7 +26,7 @@
* *************************************************************************/
struct th_dvb_mux_instance;
struct th_dvb_adapter;
struct dvb_network;
typedef struct epggrab_module epggrab_module_t;
typedef struct epggrab_module_int epggrab_module_int_t;
@ -169,10 +169,10 @@ struct epggrab_module_ext
*/
struct epggrab_ota_mux
{
TAILQ_ENTRY(epggrab_ota_mux) glob_link; ///< Grabber link
TAILQ_ENTRY(epggrab_ota_mux) tdmi_link; ///< Link to mux
TAILQ_ENTRY(epggrab_ota_mux) grab_link; ///< Link to grabber
struct th_dvb_mux_instance *tdmi; ///< Mux instance
TAILQ_ENTRY(epggrab_ota_mux) glob_link;///< Grabber link
TAILQ_ENTRY(epggrab_ota_mux) dm_link; ///< Link to dvb mux
TAILQ_ENTRY(epggrab_ota_mux) grab_link;///< Link to grabber
struct dvb_mux *dm; ///< Mux instance
epggrab_module_ota_t *grab; ///< Grab instance
int timeout; ///< Time out if this long
@ -203,7 +203,7 @@ struct epggrab_module_ota
TAILQ_HEAD(, epggrab_ota_mux) muxes; ///< List of related muxes
/* Transponder tuning */
void (*start) ( epggrab_module_ota_t *m, struct th_dvb_mux_instance *tdmi );
void (*start) ( epggrab_module_ota_t *m, struct dvb_mux *dm );
};
/*
@ -261,11 +261,11 @@ void epggrab_channel_mod ( struct channel *ch );
/*
* Transport handling
*/
void epggrab_mux_start ( struct th_dvb_mux_instance *tdmi );
void epggrab_mux_stop ( struct th_dvb_mux_instance *tdmi, int timeout );
void epggrab_mux_delete ( struct th_dvb_mux_instance *tdmi );
int epggrab_mux_period ( struct th_dvb_mux_instance *tdmi );
struct th_dvb_mux_instance *epggrab_mux_next ( struct th_dvb_adapter *tda );
void epggrab_mux_start ( struct dvb_mux *tdmi );
void epggrab_mux_stop ( struct dvb_mux *tdmi, int timeout );
void epggrab_mux_delete ( struct dvb_mux *tdmi );
int epggrab_mux_period ( struct dvb_mux *tdmi );
struct dvb_mux *epggrab_mux_next ( struct dvb_network *dn );
/*
* Re-schedule

View file

@ -457,7 +457,7 @@ epggrab_module_ota_t *epggrab_module_ota_create
( epggrab_module_ota_t *skel,
const char *id, const char *name, int priority,
void (*start) (epggrab_module_ota_t*m,
struct th_dvb_mux_instance *tdmi),
struct dvb_mux *dm),
int (*enable) (void *m, uint8_t e ),
epggrab_channel_tree_t *channels )
{

View file

@ -481,7 +481,7 @@ static int _eit_desc_crid
} else {
char *defauth = svc->s_default_authority;
if (!defauth)
defauth = svc->s_dvb_mux_instance->tdmi_default_authority;
defauth = svc->s_dvb_mux->dm_default_authority;
if (defauth)
snprintf(crid, clen, "crid://%s%s", defauth, buf);
}
@ -548,8 +548,8 @@ static int _eit_process_event
/* Override */
if (!ev.default_charset) {
ev.default_charset
= dvb_charset_find(svc->s_dvb_mux_instance->tdmi_network_id,
svc->s_dvb_mux_instance->tdmi_transport_stream_id,
= dvb_charset_find(svc->s_dvb_mux->dm_network_id,
svc->s_dvb_mux->dm_transport_stream_id,
svc->s_dvb_service_id);
}
@ -658,13 +658,12 @@ static int _eit_process_event
return ret;
}
static int _eit_callback
( th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
uint8_t tableid, void *opaque )
static int
_eit_callback(dvb_mux_t *dm, uint8_t *ptr, int len,
uint8_t tableid, void *opaque)
{
epggrab_module_t *mod = opaque;
epggrab_ota_mux_t *ota;
th_dvb_adapter_t *tda;
service_t *svc;
eit_status_t *sta;
eit_table_status_t *tsta;
@ -676,7 +675,7 @@ static int _eit_callback
if(tableid < 0x4e || tableid > 0x6f || len < 11) return -1;
/* Get OTA */
ota = epggrab_ota_find((epggrab_module_ota_t*)mod, tdmi);
ota = epggrab_ota_find((epggrab_module_ota_t*)mod, dm);
if (!ota || !ota->status) return -1;
sta = ota->status;
@ -714,22 +713,22 @@ static int _eit_callback
// Note: tableid=0x4f,0x60-0x6f is other TS
// so must find the tdmi
if(tableid == 0x4f || tableid >= 0x60) {
tda = tdmi->tdmi_adapter;
tdmi = dvb_mux_find(tda, NULL, onid, tsid, 1);
dm = dvb_mux_find(dm->dm_dn, NULL, onid, tsid, 1);
} else {
if (tdmi->tdmi_transport_stream_id != tsid ||
tdmi->tdmi_network_id != onid) {
if (dm->dm_transport_stream_id != tsid ||
dm->dm_network_id != onid) {
tvhtrace("eit",
"invalid tsid found tid 0x%02X, onid:tsid %d:%d != %d:%d",
tableid, tdmi->tdmi_network_id, tdmi->tdmi_transport_stream_id,
tableid, dm->dm_network_id, dm->dm_transport_stream_id,
onid, tsid);
tdmi = NULL;
dm = NULL;
}
}
if(!tdmi) goto done;
if(!dm) goto done;
/* Get service */
svc = dvb_service_find3(NULL, tdmi, NULL, 0, 0, sid, 1, 1);
svc = dvb_service_find3(NULL, dm, NULL, 0, 0, sid, 1, 1);
if (!svc || !svc->s_ch) goto done;
/* Register as interesting */
@ -815,10 +814,9 @@ static void _eit_ota_destroy ( epggrab_ota_mux_t *ota )
}
static void _eit_start
( epggrab_module_ota_t *m, th_dvb_mux_instance_t *tdmi )
( epggrab_module_ota_t *m, dvb_mux_t *dm )
{
epggrab_ota_mux_t *ota;
/* Disabled */
if (!m->enabled) return;
@ -830,7 +828,7 @@ static void _eit_start
}
/* Register */
if (!(ota = epggrab_ota_create(m, tdmi))) return;
if (!(ota = epggrab_ota_create(m, dm))) return;
if (!ota->status) {
ota->status = calloc(1, sizeof(eit_status_t));
ota->destroy = _eit_ota_destroy;
@ -842,16 +840,16 @@ static void _eit_start
tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3840, NULL);
tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3841, NULL);
#endif
tdt_add(tdmi, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002);
tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003);
tdt_add(dm, 0, 0, dvb_pidx11_callback, m, m->id, TDT_CRC, 3002);
tdt_add(dm, 0, 0, _eit_callback, m, m->id, TDT_CRC, 3003);
/* Viasat Baltic (0x39) */
} else if (!strcmp("viasat_baltic", m->id)) {
tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39);
tdt_add(dm, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x39);
/* Standard (0x12) */
} else {
tdt_add(tdmi, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12);
tdt_add(dm, 0, 0, _eit_callback, m, m->id, TDT_CRC, 0x12);
}
tvhlog(LOG_DEBUG, m->id, "install table handlers");
}

View file

@ -539,11 +539,11 @@ static int _opentv_bat_section
* ***********************************************************************/
static epggrab_ota_mux_t *_opentv_event_callback
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
( dvb_mux_t *dm, uint8_t *buf, int len, uint8_t tid, void *p )
{
th_dvb_table_t *tdt = p;
opentv_module_t *mod = tdt->tdt_opaque;
epggrab_ota_mux_t *ota = epggrab_ota_find((epggrab_module_ota_t*)mod, tdmi);
epggrab_ota_mux_t *ota = epggrab_ota_find((epggrab_module_ota_t*)mod, dm);
opentv_status_t *sta;
opentv_pid_t *pid;
@ -604,9 +604,9 @@ static epggrab_ota_mux_t *_opentv_event_callback
}
static int _opentv_title_callback
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
( dvb_mux_t *dm, uint8_t *buf, int len, uint8_t tid, void *p )
{
epggrab_ota_mux_t *ota = _opentv_event_callback(tdmi, buf, len, tid, p);
epggrab_ota_mux_t *ota = _opentv_event_callback(dm, buf, len, tid, p);
if (ota)
return _opentv_parse_event_section((opentv_module_t*)ota->grab,
(opentv_status_t*)ota->status,
@ -615,9 +615,9 @@ static int _opentv_title_callback
}
static int _opentv_summary_callback
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
( dvb_mux_t *dm, uint8_t *buf, int len, uint8_t tid, void *p )
{
epggrab_ota_mux_t *ota = _opentv_event_callback(tdmi, buf, len, tid, p);
epggrab_ota_mux_t *ota = _opentv_event_callback(dm, buf, len, tid, p);
if (ota)
return _opentv_parse_event_section((opentv_module_t*)ota->grab,
(opentv_status_t*)ota->status,
@ -626,10 +626,10 @@ static int _opentv_summary_callback
}
static int _opentv_channel_callback
( th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tid, void *p )
( dvb_mux_t *dm, uint8_t *buf, int len, uint8_t tid, void *p )
{
opentv_module_t *mod = p;
epggrab_ota_mux_t *ota = epggrab_ota_find((epggrab_module_ota_t*)mod, tdmi);
epggrab_ota_mux_t *ota = epggrab_ota_find((epggrab_module_ota_t*)mod, dm);
opentv_status_t *sta;
if (!ota || !ota->status) return 0;
sta = ota->status;
@ -662,7 +662,7 @@ static void _opentv_ota_destroy ( epggrab_ota_mux_t *ota )
}
static void _opentv_start
( epggrab_module_ota_t *m, th_dvb_mux_instance_t *tdmi )
( epggrab_module_ota_t *m, dvb_mux_t *dm )
{
int *t;
epggrab_ota_mux_t *ota;
@ -671,10 +671,10 @@ static void _opentv_start
/* Ignore */
if (!m->enabled) return;
if (mod->tsid != tdmi->tdmi_transport_stream_id) return;
if (mod->tsid != dm->dm_transport_stream_id) return;
/* Create link */
if (!(ota = epggrab_ota_create(m, tdmi))) return;
if (!(ota = epggrab_ota_create(m, dm))) return;
if (!ota->status) {
ota->status = calloc(1, sizeof(opentv_status_t));
ota->destroy = _opentv_ota_destroy;
@ -692,7 +692,7 @@ static void _opentv_start
t = mod->channel;
while (*t) {
// TODO: what about 0x46 (service description)
tdt_add(tdmi, 0x4a, 0xff, _opentv_channel_callback, m,
tdt_add(dm, 0x4a, 0xff, _opentv_channel_callback, m,
m->id, TDT_CRC, *t++);
}
@ -700,7 +700,7 @@ static void _opentv_start
t = mod->title;
while (*t) {
_opentv_status_get_pid(sta, *t);
tdt_add(tdmi, 0xa0, 0xfc, _opentv_title_callback, m,
tdt_add(dm, 0xa0, 0xfc, _opentv_title_callback, m,
m->id, TDT_CRC | TDT_TDT, *t++);
}
@ -708,7 +708,7 @@ static void _opentv_start
t = mod->summary;
while (*t) {
_opentv_status_get_pid(sta, *t);
tdt_add(tdmi, 0xa8, 0xfc, _opentv_summary_callback, m,
tdt_add(dm, 0xa8, 0xfc, _opentv_summary_callback, m,
m->id, TDT_CRC | TDT_TDT, *t++);
}
}

View file

@ -36,27 +36,27 @@ TAILQ_HEAD(, epggrab_ota_mux) ota_mux_all;
* Global functions (called from DVB code)
* *************************************************************************/
void epggrab_mux_start ( th_dvb_mux_instance_t *tdmi )
void epggrab_mux_start ( dvb_mux_t *dm )
{
epggrab_module_t *m;
epggrab_module_ota_t *om;
LIST_FOREACH(m, &epggrab_modules, link) {
if (m->type == EPGGRAB_OTA) {
om = (epggrab_module_ota_t*)m;
if (om->start) om->start(om, tdmi);
if (om->start) om->start(om, dm);
}
}
}
void epggrab_mux_stop ( th_dvb_mux_instance_t *tdmi, int timeout )
void epggrab_mux_stop ( dvb_mux_t *dm, int timeout )
{
// Note: the slightly akward list iteration here is because
// _ota_cancel/delete can remove the object and free() it
epggrab_ota_mux_t *a, *b;
a = TAILQ_FIRST(&tdmi->tdmi_epg_grab);
a = TAILQ_FIRST(&dm->dm_epg_grab);
while (a) {
b = TAILQ_NEXT(a, tdmi_link);
if (a->tdmi == tdmi) {
b = TAILQ_NEXT(a, dm_link);
if (a->dm == dm) {
if (timeout)
epggrab_ota_timeout(a);
else
@ -66,16 +66,16 @@ void epggrab_mux_stop ( th_dvb_mux_instance_t *tdmi, int timeout )
}
}
void epggrab_mux_delete ( th_dvb_mux_instance_t *tdmi )
void epggrab_mux_delete ( dvb_mux_t *dm )
{
epggrab_ota_destroy_by_tdmi(tdmi);
epggrab_ota_destroy_by_dm(dm);
}
int epggrab_mux_period ( th_dvb_mux_instance_t *tdmi )
int epggrab_mux_period ( dvb_mux_t *dm )
{
int period = 0;
epggrab_ota_mux_t *ota;
TAILQ_FOREACH(ota, &tdmi->tdmi_epg_grab, tdmi_link) {
TAILQ_FOREACH(ota, &dm->dm_epg_grab, dm_link) {
if (!ota->is_reg) continue;
if (ota->timeout > period)
period = ota->timeout;
@ -83,18 +83,20 @@ int epggrab_mux_period ( th_dvb_mux_instance_t *tdmi )
return period;
}
th_dvb_mux_instance_t *epggrab_mux_next ( th_dvb_adapter_t *tda )
dvb_mux_t *epggrab_mux_next ( dvb_network_t *dn )
{
time_t now;
epggrab_ota_mux_t *ota;
time(&now);
TAILQ_FOREACH(ota, &ota_mux_all, glob_link) {
#if TODO_FIX_THIS
if (ota->tdmi->tdmi_adapter != tda) continue;
#endif
if (ota->interval + ota->completed > now) return NULL;
if (!ota->is_reg) return NULL;
break;
if (ota->dm->dm_dn == dn) break;
}
return ota ? ota->tdmi : NULL;
return ota ? ota->dm : NULL;
}
/* **************************************************************************
@ -148,12 +150,15 @@ static void _epggrab_ota_save_one ( htsmsg_t *m, epggrab_module_ota_t *mod )
TAILQ_FOREACH(ota, &mod->muxes, grab_link) {
if (!l) l = htsmsg_create_list();
e = htsmsg_create_map();
htsmsg_add_u32(e, "onid", ota->tdmi->tdmi_network_id);
htsmsg_add_u32(e, "tsid", ota->tdmi->tdmi_transport_stream_id);
const dvb_mux_t *dm = ota->dm;
htsmsg_add_u32(e, "onid", dm->dm_network_id);
htsmsg_add_u32(e, "tsid", dm->dm_transport_stream_id);
htsmsg_add_u32(e, "period", ota->timeout);
htsmsg_add_u32(e, "interval", ota->interval);
if (ota->tdmi->tdmi_network)
htsmsg_add_str(e, "networkname", ota->tdmi->tdmi_network);
if (dm->dm_network_name)
htsmsg_add_str(e, "networkname", dm->dm_network_name);
htsmsg_add_msg(l, NULL, e);
}
if (l) htsmsg_add_msg(m, mod->id, l);
@ -210,11 +215,11 @@ static int _ota_time_cmp ( void *_a, void *_b )
* Find existing link
*/
epggrab_ota_mux_t *epggrab_ota_find
( epggrab_module_ota_t *mod, th_dvb_mux_instance_t *tdmi )
( epggrab_module_ota_t *mod, dvb_mux_t *dm )
{
epggrab_ota_mux_t *ota;
TAILQ_FOREACH(ota, &mod->muxes, grab_link) {
if (ota->tdmi == tdmi) break;
if (ota->dm == dm) break;
}
return ota;
}
@ -223,18 +228,18 @@ epggrab_ota_mux_t *epggrab_ota_find
* Create (temporary) or Find (existing) link
*/
epggrab_ota_mux_t *epggrab_ota_create
( epggrab_module_ota_t *mod, th_dvb_mux_instance_t *tdmi )
( epggrab_module_ota_t *mod, dvb_mux_t *dm)
{
/* Search for existing */
epggrab_ota_mux_t *ota = epggrab_ota_find(mod, tdmi);
epggrab_ota_mux_t *ota = epggrab_ota_find(mod, dm);
/* Create new */
if (!ota) {
ota = calloc(1, sizeof(epggrab_ota_mux_t));
ota->grab = mod;
ota->tdmi = tdmi;
ota->dm = dm;
TAILQ_INSERT_TAIL(&ota_mux_all, ota, glob_link);
TAILQ_INSERT_TAIL(&tdmi->tdmi_epg_grab, ota, tdmi_link);
TAILQ_INSERT_TAIL(&dm->dm_epg_grab, ota, dm_link);
TAILQ_INSERT_TAIL(&mod->muxes, ota, grab_link);
} else {
@ -252,15 +257,16 @@ void epggrab_ota_create_and_register_by_id
( epggrab_module_ota_t *mod, int onid, int tsid, int period, int interval,
const char *networkname )
{
th_dvb_adapter_t *tda;
th_dvb_mux_instance_t *tdmi;
dvb_network_t *dn;
dvb_mux_t *dm;
epggrab_ota_mux_t *ota;
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
if (tdmi->tdmi_transport_stream_id != tsid) continue;
if (onid && tdmi->tdmi_network_id != onid) continue;
if (networkname && (!tdmi->tdmi_network || strcmp(networkname, tdmi->tdmi_network))) continue;
ota = epggrab_ota_create(mod, tdmi);
LIST_FOREACH(dn, &dvb_networks, dn_global_link) {
LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link) {
if (dm->dm_transport_stream_id != tsid) continue;
if (onid && dm->dm_network_id != onid) continue;
if (networkname && (!dm->dm_network_name ||
strcmp(networkname, dm->dm_network_name))) continue;
ota = epggrab_ota_create(mod, dm);
epggrab_ota_register(ota, period, interval);
}
}
@ -272,7 +278,7 @@ void epggrab_ota_create_and_register_by_id
void epggrab_ota_destroy ( epggrab_ota_mux_t *ota )
{
TAILQ_REMOVE(&ota_mux_all, ota, glob_link);
TAILQ_REMOVE(&ota->tdmi->tdmi_epg_grab, ota, tdmi_link);
TAILQ_REMOVE(&ota->dm->dm_epg_grab, ota, dm_link);
TAILQ_REMOVE(&ota->grab->muxes, ota, grab_link);
if (ota->destroy) ota->destroy(ota);
else {
@ -284,10 +290,10 @@ void epggrab_ota_destroy ( epggrab_ota_mux_t *ota )
/*
* Destroy by tdmi
*/
void epggrab_ota_destroy_by_tdmi ( th_dvb_mux_instance_t *tdmi )
void epggrab_ota_destroy_by_dm ( dvb_mux_t *dm )
{
epggrab_ota_mux_t *ota;
while ((ota = TAILQ_FIRST(&tdmi->tdmi_epg_grab)))
while ((ota = TAILQ_FIRST(&dm->dm_epg_grab)))
epggrab_ota_destroy(ota);
}
@ -369,7 +375,7 @@ int epggrab_ota_begin ( epggrab_ota_mux_t *ota )
void epggrab_ota_complete ( epggrab_ota_mux_t *ota )
{
th_dvb_mux_instance_t *tdmi = ota->tdmi;
dvb_mux_t *dm = ota->dm;
if (ota->state != EPGGRAB_OTA_MUX_COMPLETE) {
tvhlog(LOG_DEBUG, ota->grab->id, "processing complete");
@ -377,15 +383,17 @@ void epggrab_ota_complete ( epggrab_ota_mux_t *ota )
time(&ota->completed);
/* Check others */
TAILQ_FOREACH(ota, &tdmi->tdmi_epg_grab, tdmi_link) {
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) {
gtimer_arm(&tdmi->tdmi_adapter->tda_mux_scanner_timer,
dvb_adapter_mux_scanner, tdmi->tdmi_adapter, 20);
dvb_network_t *dn = dm->dm_dn;
gtimer_arm(&dn->dn_mux_scanner_timer,
dvb_network_mux_scanner, dn, 20);
}
#endif
}
}

View file

@ -88,7 +88,7 @@ epggrab_module_ota_t *epggrab_module_ota_create
( epggrab_module_ota_t *skel,
const char *id, const char *name, int priority,
void (*start) (epggrab_module_ota_t*m,
struct th_dvb_mux_instance *tdmi),
struct dvb_mux *dm),
int (*enable) (void *m, uint8_t e ),
epggrab_channel_tree_t *channels );
@ -109,9 +109,9 @@ void epggrab_ota_save ( void );
* blocked (i.e. has completed within interval period)
*/
epggrab_ota_mux_t *epggrab_ota_find
( epggrab_module_ota_t *mod, struct th_dvb_mux_instance *tdmi );
( epggrab_module_ota_t *mod, struct dvb_mux *dm );
epggrab_ota_mux_t *epggrab_ota_create
( epggrab_module_ota_t *mod, struct th_dvb_mux_instance *tdmi );
( epggrab_module_ota_t *mod, struct dvb_mux *dm );
void epggrab_ota_create_and_register_by_id
( epggrab_module_ota_t *mod, int nid, int tsid,
int period, int interval, const char *name );
@ -121,7 +121,7 @@ void epggrab_ota_create_and_register_by_id
*/
void epggrab_ota_destroy ( epggrab_ota_mux_t *ota );
void epggrab_ota_destroy_by_module ( epggrab_module_ota_t *mod );
void epggrab_ota_destroy_by_tdmi ( struct th_dvb_mux_instance *tdmi );
void epggrab_ota_destroy_by_dm ( struct dvb_mux *dm );
/*
* Register interest

View file

@ -306,7 +306,7 @@ int fb_scandir ( const char *path, fb_dirent ***list )
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);

View file

@ -26,11 +26,11 @@ struct strtab {
int val;
};
static int str2val0(const char *str, struct strtab tab[], int l)
static int str2val0(const char *str, const struct strtab tab[], int l)
__attribute((unused));
static int
str2val0(const char *str, struct strtab tab[], int l)
str2val0(const char *str, const struct strtab tab[], int l)
{
int i;
for(i = 0; i < l; i++)
@ -62,11 +62,11 @@ str2val0_def(const char *str, struct strtab tab[], int l, int def)
str2val0_def(str, tab, sizeof(tab) / sizeof(tab[0]), def)
static const char * val2str0(int val, struct strtab tab[], int l)
static const char * val2str0(int val, const struct strtab tab[], int l)
__attribute__((unused));
static const char *
val2str0(int val, struct strtab tab[], int l)
val2str0(int val, const struct strtab tab[], int l)
{
int i;
for(i = 0; i < l; i++)

View file

@ -176,6 +176,16 @@ htsmsg_destroy(htsmsg_t *msg)
free(msg);
}
/*
*
*/
void
htsmsg_add_bool(htsmsg_t *msg, const char *name, int b)
{
htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_BOOL, HMF_NAME_ALLOCED);
f->hmf_bool = !!b;
}
/*
*
*/
@ -328,6 +338,9 @@ htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p)
case HMF_S64:
*s64p = f->hmf_s64;
break;
case HMF_BOOL:
*s64p = f->hmf_bool;
break;
case HMF_DBL:
*s64p = f->hmf_dbl;
break;
@ -336,6 +349,17 @@ htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p)
}
/**
*
*/
int
htsmsg_get_bool_or_default(htsmsg_t *msg, const char *name, int def)
{
int64_t s64;
return htsmsg_get_s64(msg, name, &s64) ? def : s64;
}
/**
*
*/
@ -613,6 +637,10 @@ htsmsg_print0(htsmsg_t *msg, int indent)
printf("S64) = %" PRId64 "\n", f->hmf_s64);
break;
case HMF_BOOL:
printf("BOOL) = %s\n", f->hmf_bool ? "true" : "false");
break;
case HMF_DBL:
printf("DBL) = %f\n", f->hmf_dbl);
break;
@ -659,6 +687,10 @@ htsmsg_copy_i(htsmsg_t *src, htsmsg_t *dst)
htsmsg_add_s64(dst, f->hmf_name, f->hmf_s64);
break;
case HMF_BOOL:
htsmsg_add_bool(dst, f->hmf_name, f->hmf_bool);
break;
case HMF_BIN:
htsmsg_add_bin(dst, f->hmf_name, f->hmf_bin, f->hmf_binsize);
break;

View file

@ -50,6 +50,7 @@ typedef struct htsmsg {
#define HMF_BIN 4
#define HMF_LIST 5
#define HMF_DBL 6
#define HMF_BOOL 7
typedef struct htsmsg_field {
TAILQ_ENTRY(htsmsg_field) hmf_link;
@ -69,6 +70,7 @@ typedef struct htsmsg_field {
} bin;
htsmsg_t msg;
double dbl;
int bool;
} u;
} htsmsg_field_t;
@ -78,6 +80,7 @@ typedef struct htsmsg_field {
#define hmf_bin u.bin.data
#define hmf_binsize u.bin.len
#define hmf_dbl u.dbl
#define hmf_bool u.bool
#define htsmsg_get_map_by_field(f) \
((f)->hmf_type == HMF_MAP ? &(f)->hmf_msg : NULL)
@ -106,6 +109,8 @@ void htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f);
*/
void htsmsg_destroy(htsmsg_t *msg);
void htsmsg_add_bool(htsmsg_t *msg, const char *name, int b);
/**
* Add an integer field where source is unsigned 32 bit.
*/
@ -195,6 +200,8 @@ int htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p);
*/
int64_t htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def);
int htsmsg_get_bool_or_default(htsmsg_t *msg, const char *name, int def);
/**
* Get pointer to a binary field. No copying of data is performed.

View file

@ -39,6 +39,7 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
htsmsg_field_t *f;
char buf[100];
static const char *indentor = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
const char *s;
htsbuf_append(hq, isarray ? "[" : "{", 1);
@ -69,6 +70,11 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
htsbuf_append_and_escape_jsonstr(hq, "binary");
break;
case HMF_BOOL:
s = f->hmf_bool ? "true" : "false";
htsbuf_append(hq, s, strlen(s));
break;
case HMF_S64:
snprintf(buf, sizeof(buf), "%" PRId64, f->hmf_s64);
htsbuf_append(hq, buf, strlen(buf));
@ -169,7 +175,7 @@ add_double(void *opaque, void *parent, const char *name, double v)
static void
add_bool(void *opaque, void *parent, const char *name, int v)
{
htsmsg_add_u32(parent, name, v);
htsmsg_add_bool(parent, name, v);
}
static void

View file

@ -393,8 +393,7 @@ static int
http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
{
http_path_t *hp;
char *remain, *args, *v, *argv[2];
int n;
char *remain, *args, *v;
/* Set keep-alive status */
v = http_arg_get(&hc->hc_args, "Content-Length");
@ -421,18 +420,17 @@ http_cmd_post(http_connection_t *hc, htsbuf_queue_t *spill)
/* Parse content-type */
v = http_arg_get(&hc->hc_args, "Content-Type");
if(v == NULL) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return 0;
}
n = http_tokenize(v, argv, 2, ';');
if(n == 0) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return 0;
}
if(v != NULL) {
char *argv[2];
int n = http_tokenize(v, argv, 2, ';');
if(n == 0) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return 0;
}
if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
http_parse_get_args(hc, hc->hc_post_data);
if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
http_parse_get_args(hc, hc->hc_post_data);
}
hp = http_resolve(hc, &remain, &args);
if(hp == NULL) {

342
src/idnode.c Normal file
View file

@ -0,0 +1,342 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "idnode.h"
#include "notify.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;
}
/**
*
*/
void *
idnode_find(const char *uuid, const idclass_t *idc)
{
idnode_t skel, *r;
if(hex2bin(skel.in_uuid, 16, uuid))
return NULL;
r = RB_FIND(&idnodes, &skel, in_link, in_cmp);
if(r != NULL && idc != NULL) {
const idclass_t *c = r->in_class;
for(;c != NULL; c = c->ic_super) {
if(idc == c)
return r;
}
return NULL;
}
return r;
}
/**
*
*/
void
idnode_unlink(idnode_t *in)
{
RB_REMOVE(&idnodes, in, in_link);
}
/**
* Recursive to get superclass nodes first
*/
static void
add_params(struct idnode *self, const idclass_t *ic, htsmsg_t *p)
{
if(ic->ic_super != NULL)
add_params(self, ic->ic_super, p);
if(TAILQ_FIRST(&p->hm_fields) != NULL) {
// Only add separator if not empty
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "caption", ic->ic_caption ?: ic->ic_class);
htsmsg_add_str(m, "type", "separator");
htsmsg_add_msg(p, NULL, m);
}
prop_add_params_to_msg(self, ic->ic_properties, p);
}
/**
*
*/
static const char *
idnode_get_title(idnode_t *in)
{
const idclass_t *ic = in->in_class;
for(; ic != NULL; ic = ic->ic_super) {
if(ic->ic_get_title != NULL)
return ic->ic_get_title(in);
}
return idnode_uuid_as_str(in);
}
/**
*
*/
idnode_t **
idnode_get_childs(idnode_t *in)
{
if(in == NULL)
return NULL;
const idclass_t *ic = in->in_class;
for(; ic != NULL; ic = ic->ic_super) {
if(ic->ic_get_childs != NULL)
return ic->ic_get_childs(in);
}
return NULL;
}
/**
*
*/
int
idnode_is_leaf(idnode_t *in)
{
const idclass_t *ic = in->in_class;
if(ic->ic_leaf)
return 1;
for(; ic != NULL; ic = ic->ic_super) {
if(ic->ic_get_childs != NULL)
return 0;
}
return 1;
}
/**
*
*/
htsmsg_t *
idnode_serialize(struct idnode *self)
{
const idclass_t *c = self->in_class;
htsmsg_t *m;
if(c->ic_serialize != NULL) {
m = c->ic_serialize(self);
} else {
m = htsmsg_create_map();
htsmsg_add_str(m, "text", idnode_get_title(self));
htsmsg_t *p = htsmsg_create_list();
add_params(self, c, p);
htsmsg_add_msg(m, "params", p);
htsmsg_add_str(m, "id", idnode_uuid_as_str(self));
}
return m;
}
/**
*
*/
static void
idnode_save(idnode_t *in)
{
const idclass_t *ic = in->in_class;
for(; ic != NULL; ic = ic->ic_super) {
if(ic->ic_save != NULL) {
ic->ic_save(in);
break;
}
}
// Tell about updated parameters
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "id", idnode_uuid_as_str(in));
htsmsg_t *p = htsmsg_create_list();
add_params(in, in->in_class, p);
htsmsg_add_msg(m, "params", p);
notify_by_msg("idnodeParamsChanged", m);
}
/**
*
*/
void
idnode_set_prop(idnode_t *in, const char *key, const char *value)
{
const idclass_t *ic = in->in_class;
int do_save = 0;
for(;ic != NULL; ic = ic->ic_super) {
int x = prop_set(in, ic->ic_properties, key, value);
if(x == -1)
continue;
do_save |= x;
break;
}
if(do_save)
idnode_save(in);
}
/**
*
*/
void
idnode_update_all_props(idnode_t *in,
const char *(*getvalue)(void *opaque, const char *key),
void *opaque)
{
const idclass_t *ic = in->in_class;
int do_save = 0;
for(;ic != NULL; ic = ic->ic_super)
do_save |= prop_update_all(in, ic->ic_properties, getvalue, opaque);
if(do_save)
idnode_save(in);
}
/**
*
*/
void
idnode_notify_title_changed(void *obj)
{
idnode_t *in = obj;
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "id", idnode_uuid_as_str(in));
htsmsg_add_str(m, "text", idnode_get_title(in));
notify_by_msg("idnodeNameChanged", m);
}

53
src/idnode.h Normal file
View file

@ -0,0 +1,53 @@
#pragma once
#include "tvheadend.h"
#include "prop.h"
struct htsmsg;
struct idnode;
typedef struct idclass {
const struct idclass *ic_super;
const char *ic_class;
const char *ic_caption;
int ic_leaf;
struct htsmsg *(*ic_serialize)(struct idnode *self);
struct idnode **(*ic_get_childs)(struct idnode *self);
const char *(*ic_get_title)(struct idnode *self);
void (*ic_save)(struct idnode *self);
const property_t *ic_properties;
} 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);
void *idnode_find(const char *uuid, const idclass_t *class);
idnode_t **idnode_get_childs(idnode_t *in);
int idnode_is_leaf(idnode_t *in);
void idnode_unlink(idnode_t *in);
htsmsg_t *idnode_serialize(struct idnode *self);
void idnode_set_prop(idnode_t *in, const char *key, const char *value);
void idnode_update_all_props(idnode_t *in,
const char *(*getvalue)(void *opaque,
const char *key),
void *opaque);
void idnode_notify_title_changed(void *obj);

View file

@ -48,6 +48,11 @@ static pthread_mutex_t iptv_recvmutex;
struct service_list iptv_all_services; /* All IPTV services */
static struct service_list iptv_active_services; /* Currently enabled */
const idclass_t iptv_class = {
.ic_super = &service_class,
.ic_class = "iptv",
};
/**
* PAT parser. We only parse a single program. CRC has already been verified
*/
@ -198,7 +203,7 @@ iptv_thread(void *aux)
*
*/
static int
iptv_service_start(service_t *t, unsigned int weight, int force_start)
iptv_service_start(service_t *t, int instance)
{
pthread_t tid;
int fd;
@ -227,7 +232,7 @@ iptv_service_start(service_t *t, unsigned int weight, int force_start)
fd = tvh_socket(AF_INET6, SOCK_DGRAM, 0);
}
if(fd == -1) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_identifier);
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_nicename);
return -1;
}
@ -237,7 +242,7 @@ iptv_service_start(service_t *t, unsigned int weight, int force_start)
ifr.ifr_name[IFNAMSIZ - 1] = 0;
if(ioctl(fd, SIOCGIFINDEX, &ifr)) {
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s",
t->s_identifier, t->s_iptv_iface);
t->s_nicename, t->s_iptv_iface);
close(fd);
return -1;
}
@ -251,7 +256,7 @@ iptv_service_start(service_t *t, unsigned int weight, int force_start)
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_identifier, 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;
@ -265,7 +270,7 @@ iptv_service_start(service_t *t, unsigned int weight, int force_start)
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_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno));
t->s_nicename, inet_ntoa(m.imr_multiaddr), strerror(errno));
close(fd);
return -1;
}
@ -279,7 +284,7 @@ iptv_service_start(service_t *t, unsigned int weight, int force_start)
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_identifier, straddr, t->s_iptv_port,
t->s_nicename, straddr, t->s_iptv_port,
strerror(errno));
close(fd);
return -1;
@ -294,7 +299,7 @@ iptv_service_start(service_t *t, unsigned int weight, int force_start)
inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr,
straddr, sizeof(straddr));
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s",
t->s_identifier, straddr, strerror(errno));
t->s_nicename, straddr, strerror(errno));
close(fd);
return -1;
}
@ -312,7 +317,7 @@ iptv_service_start(service_t *t, unsigned int weight, int force_start)
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_identifier, strerror(errno));
t->s_nicename, strerror(errno));
close(fd);
return -1;
}
@ -356,7 +361,7 @@ iptv_service_stop(service_t *t)
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_identifier, t->s_iptv_iface);
t->s_nicename, t->s_iptv_iface);
}
if(t->s_iptv_group.s_addr != 0) {
@ -371,7 +376,7 @@ iptv_service_stop(service_t *t)
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_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno));
t->s_nicename, inet_ntoa(m.imr_multiaddr), strerror(errno));
}
} else {
char straddr[INET6_ADDRSTRLEN];
@ -388,7 +393,7 @@ iptv_service_stop(service_t *t)
straddr, sizeof(straddr));
tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s",
t->s_identifier, straddr, strerror(errno));
t->s_nicename, straddr, strerror(errno));
}
@ -440,27 +445,13 @@ iptv_service_save(service_t *t)
psi_save_service_settings(m, t);
pthread_mutex_unlock(&t->s_stream_mutex);
hts_settings_save(m, "iptvservices/%s",
t->s_identifier);
abort(); // XXX(dvbreorg);
// hts_settings_save(m, "iptvservices/%s", t->s_uuid);
htsmsg_destroy(m);
}
/**
*
*/
static int
iptv_service_quality(service_t *t)
{
if(t->s_iptv_iface == NULL ||
(t->s_iptv_group.s_addr == 0 && t->s_iptv_group6.s6_addr == 0) ||
t->s_iptv_port == 0)
return 0;
return 100;
}
/**
*
*/
@ -507,7 +498,8 @@ iptv_grace_period(service_t *t)
static void
iptv_service_dtor(service_t *t)
{
hts_settings_remove("iptvservices/%s", t->s_identifier);
abort(); // XXX(dvbreorg);
// hts_settings_remove("iptvservices/%s", t->s_uuid);
}
@ -517,32 +509,19 @@ iptv_service_dtor(service_t *t)
service_t *
iptv_service_find(const char *id, int create)
{
static int tally;
service_t *t;
char buf[20];
if(id != NULL) {
if(strncmp(id, "iptv_", 5))
return NULL;
LIST_FOREACH(t, &iptv_all_services, s_group_link)
if(!strcmp(t->s_identifier, id))
return t;
t = idnode_find(id, &iptv_class);
if(t != NULL)
return t;
}
if(create == 0)
return NULL;
if(id == NULL) {
tally++;
snprintf(buf, sizeof(buf), "iptv_%d", tally);
id = buf;
} else {
tally = MAX(atoi(id + 5), tally);
}
t = service_create(id, SERVICE_TYPE_IPTV, S_MPEG_TS);
t = service_create(id, S_MPEG_TS, &iptv_class);
t->s_servicetype = ST_SDTV;
t->s_start_feed = iptv_service_start;
@ -550,7 +529,6 @@ iptv_service_find(const char *id, int create)
t->s_stop_feed = iptv_service_stop;
t->s_config_save = iptv_service_save;
t->s_setsourceinfo = iptv_service_setsourceinfo;
t->s_quality_index = iptv_service_quality;
t->s_is_enabled = iptv_service_is_enabled;
t->s_grace_period = iptv_grace_period;
t->s_dtor = iptv_service_dtor;

View file

@ -60,6 +60,7 @@
#include "ffdecsa/FFdecsa.h"
#include "muxes.h"
#include "config2.h"
#include "idnode.h"
#include "imagecache.h"
#include "timeshift.h"
#if ENABLE_LIBAV

224
src/prop.c Normal file
View file

@ -0,0 +1,224 @@
#include <stdio.h>
#include <string.h>
#include "tvheadend.h"
#include "prop.h"
/**
*
*/
static int
str_to_bool(const char *s)
{
if(s == NULL)
return 0;
int v = atoi(s);
if(v)
return 1;
if(!strcasecmp(s, "on") ||
!strcasecmp(s, "true") ||
!strcasecmp(s, "yes"))
return 1;
return 0;
}
static const property_t *
prop_find(const property_t *p, const char *id)
{
int i = 0;
for(;p[i].id; i++)
if(!strcmp(id, p[i].id))
return p + i;
return NULL;
}
#define TO_FROM(x, y) ((x) << 16 | (y))
/**
*
*/
void
prop_write_values(void *obj, const property_t *pl, htsmsg_t *m)
{
htsmsg_field_t *f;
HTSMSG_FOREACH(f, m) {
if(f->hmf_name == NULL)
continue;
const property_t *p = prop_find(pl, f->hmf_name);
if(p == NULL) {
fprintf(stderr, "Property %s unmappable\n", f->hmf_name);
continue;
}
void *val = obj + p->off;
switch(TO_FROM(p->type, f->hmf_type)) {
case TO_FROM(PT_BOOL, HMF_BOOL):
*(int *)val = f->hmf_bool;
break;
case TO_FROM(PT_BOOL, HMF_S64):
*(int *)val = !!f->hmf_s64;
break;
case TO_FROM(PT_INT, HMF_S64):
*(int *)val = f->hmf_s64;
break;
case TO_FROM(PT_STR, HMF_STR):
if(p->str_set != NULL)
p->str_set(obj, f->hmf_str);
else
mystrset(val, f->hmf_str);
break;
}
}
}
/**
*
*/
static void
prop_read_value(void *obj, const property_t *p, htsmsg_t *m, const char *name)
{
const char *s;
const void *val = obj + p->off;
switch(p->type) {
case PT_BOOL:
htsmsg_add_bool(m, name, *(int *)val);
break;
case PT_INT:
htsmsg_add_s32(m, name, *(int *)val);
break;
case PT_STR:
if(p->str_get != NULL)
s = p->str_get(obj);
else
s = *(const char **)val;
if(s != NULL)
htsmsg_add_str(m, name, s);
break;
}
}
/**
*
*/
void
prop_read_values(void *obj, const property_t *p, htsmsg_t *m)
{
if(p == NULL)
return;
int i = 0;
for(;p[i].id; i++)
prop_read_value(obj, p+i, m, p[i].id);
}
/**
*
*/
const static struct strtab typetab[] = {
{ "bool", PT_BOOL },
{ "int", PT_INT },
{ "str", PT_STR },
};
/**
*
*/
void
prop_add_params_to_msg(void *obj, const property_t *p, htsmsg_t *msg)
{
if(p == NULL)
return;
int i = 0;
for(;p[i].id; i++) {
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "id", p[i].id);
htsmsg_add_str(m, "caption", p[i].name);
htsmsg_add_str(m, "type", val2str(p[i].type, typetab) ?: "unknown");
prop_read_value(obj, p+i, m, "value");
htsmsg_add_msg(msg, NULL, m);
}
}
/**
* value can be NULL
*
* Return 1 if we actually changed something
*/
static int
prop_seti(void *obj, const property_t *p, const char *value)
{
int i32;
const char *s;
void *val = obj + p->off;
switch(p->type) {
case PT_BOOL:
i32 = str_to_bool(value);
if(0)
case PT_INT:
i32 = value ? atoi(value) : 0;
if(*(int *)val == i32)
return 0; // Already set
*(int *)val = i32;
break;
case PT_STR:
if(p->str_get != NULL)
s = p->str_get(obj);
else
s = *(const char **)val;
if(!strcmp(s ?: "", value ?: ""))
return 0;
if(p->str_set != NULL)
p->str_set(obj, value);
else
mystrset(val, value);
break;
}
if(p->notify)
p->notify(obj);
return 1;
}
/**
* Return 1 if something changed
*/
int
prop_set(void *obj, const property_t *p, const char *key, const char *value)
{
if((p = prop_find(p, key)) == NULL)
return -1;
return prop_seti(obj, p, value);
}
/**
*
*/
int
prop_update_all(void *obj, const property_t *p,
const char *(*getvalue)(void *opaque, const char *key),
void *opaque)
{
int i = 0;
int r = 0;
for(; p[i].id; i++)
r |= prop_seti(obj, p+i, getvalue(opaque, p[i].id));
return r;
}

37
src/prop.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include <stddef.h>
#include "htsmsg.h"
typedef enum {
PT_BOOL,
PT_INT,
PT_STR,
} prop_type_t;
typedef struct property {
const char *id;
const char *name;
prop_type_t type;
size_t off;
const char *(*str_get)(void *ptr);
void (*str_set)(void *ptr, const char *str);
void (*notify)(void *ptr);
} property_t;
void prop_add_params_to_msg(void *obj, const property_t *p, htsmsg_t *msg);
void prop_write_values(void *ptr, const property_t *pl, htsmsg_t *m);
void prop_read_values(void *obj, const property_t *p, htsmsg_t *m);
int prop_set(void *obj, const property_t *p, const char *key, const char *val);
int prop_update_all(void *obj, const property_t *p,
const char *(*getvalue)(void *opaque, const char *key),
void *opaque);

View file

@ -48,11 +48,16 @@ typedef struct rawts {
} rawts_t;
const idclass_t rawts_class = {
.ic_super = &service_class,
.ic_class = "rawts",
};
/**
*
*/
static int
rawts_service_start(service_t *t, unsigned int weight, int force_start)
rawts_service_start(service_t *t, int id)
{
return 0; // Always ok
}
@ -82,16 +87,6 @@ rawts_service_save(service_t *t)
}
/**
*
*/
static int
rawts_service_quality(service_t *t)
{
return 100;
}
/**
*
*/
@ -131,7 +126,7 @@ rawts_service_add(rawts_t *rt, uint16_t sid, int pmt_pid)
snprintf(tmp, sizeof(tmp), "%s_%04x", rt->rt_identifier, sid);
t = service_create(tmp, SERVICE_TYPE_DVB, S_MPEG_TS);
t = service_create(NULL, S_MPEG_TS, &rawts_class);
t->s_flags |= S_DEBUG;
t->s_dvb_service_id = sid;
@ -141,7 +136,6 @@ rawts_service_add(rawts_t *rt, uint16_t sid, int pmt_pid)
t->s_stop_feed = rawts_service_stop;
t->s_config_save = rawts_service_save;
t->s_setsourceinfo = rawts_service_setsourceinfo;
t->s_quality_index = rawts_service_quality;
t->s_is_enabled = rawts_service_is_enabled;
t->s_svcname = strdup(tmp);

View file

@ -49,11 +49,27 @@
#include "htsp_server.h"
#include "lang_codes.h"
#define SERVICE_HASH_WIDTH 101
static struct service_list servicehash[SERVICE_HASH_WIDTH];
static void service_data_timeout(void *aux);
static const char *service_channel_get(void *obj);
static void service_channel_set(void *obj, const char *str);
static void service_save(struct idnode *self);
const idclass_t service_class = {
.ic_class = "service",
.ic_caption = "Service",
.ic_save = service_save,
.ic_properties = (const property_t[]){
{
"channel", "Channel", PT_STR,
.str_get = service_channel_get,
.str_set = service_channel_set,
}, {
"enabled", "Enabled", PT_BOOL,
offsetof(service_t, s_enabled)
}, {
}}
};
/**
*
@ -67,7 +83,7 @@ stream_init(elementary_stream_t *st)
st->es_curdts = PTS_UNSET;
st->es_curpts = PTS_UNSET;
st->es_prevdts = PTS_UNSET;
st->es_pcr_real_last = PTS_UNSET;
st->es_pcr_last = PTS_UNSET;
st->es_pcr_drift = 0;
@ -173,7 +189,7 @@ service_stop(service_t *t)
*/
void
service_remove_subscriber(service_t *t, th_subscription_t *s,
int reason)
int reason)
{
lock_assert(&global_lock);
@ -194,7 +210,7 @@ service_remove_subscriber(service_t *t, th_subscription_t *s,
*
*/
int
service_start(service_t *t, unsigned int weight, int force_start)
service_start(service_t *t, int instance)
{
elementary_stream_t *st;
int r, timeout = 2;
@ -205,7 +221,7 @@ service_start(service_t *t, unsigned int weight, int force_start)
t->s_streaming_status = 0;
t->s_pcr_drift = 0;
if((r = t->s_start_feed(t, weight, force_start)))
if((r = t->s_start_feed(t, instance)))
return r;
#if ENABLE_CWC
@ -233,92 +249,68 @@ service_start(service_t *t, unsigned int weight, int force_start)
return 0;
}
/**
*
*/
static int
dvb_extra_prio(th_dvb_adapter_t *tda)
{
return tda->tda_extrapriority + tda->tda_hostconnection * 10;
}
/**
* Return prio for the given service
* Main entry point for starting a service based on a channel
*/
static int
service_get_prio(service_t *t)
service_instance_t *
service_find_instance(channel_t *ch, struct service_instance_list *sil,
int *error, int weight)
{
switch(t->s_type) {
case SERVICE_TYPE_DVB:
return (t->s_scrambled ? 300 : 100) +
dvb_extra_prio(t->s_dvb_mux_instance->tdmi_adapter);
service_t *s;
service_instance_t *si, *next;
case SERVICE_TYPE_IPTV:
return 200;
lock_assert(&global_lock);
case SERVICE_TYPE_V4L:
return 400;
// First, update list of candidates
default:
return 500;
}
}
LIST_FOREACH(si, sil, si_link)
si->si_mark = 1;
/**
* Return quality index for given service
*
* We invert the result (providers say that negative numbers are worse)
*
* But for sorting, we want low numbers first
*
* Also, we bias and trim with an offset of two to avoid counting any
* transient errors.
*/
LIST_FOREACH(s, &ch->ch_services, s_ch_link)
s->s_enlist(s, sil);
static int
service_get_quality(service_t *t)
{
return t->s_quality_index ? -MIN(t->s_quality_index(t) + 2, 0) : 0;
}
/**
* a - b -> lowest number first
*/
static int
servicecmp(const void *A, const void *B)
{
service_t *a = *(service_t **)A;
service_t *b = *(service_t **)B;
/* only check quality if both adapters have the same prio
*
* there needs to be a much more sophisticated algorithm to take priority and quality into account
* additional, it may be problematic, since a higher priority value lowers the ranking
*
*/
int prio_a = service_get_prio(a);
int prio_b = service_get_prio(b);
if (prio_a == prio_b) {
int q = service_get_quality(a) - service_get_quality(b);
if(q != 0)
return q; /* Quality precedes priority */
for(si = LIST_FIRST(sil); si != NULL; si = next) {
next = LIST_NEXT(si, si_link);
if(si->si_mark)
service_instance_destroy(si);
}
return prio_a - prio_b;
// Check if any service is already running, if so, use that
LIST_FOREACH(si, sil, si_link)
if(si->si_s->s_status == SERVICE_RUNNING)
return si;
// Check if any is idle
LIST_FOREACH(si, sil, si_link)
if(si->si_weight == 0 && si->si_error == 0)
break;
// Check if to kick someone out
if(si == NULL) {
LIST_FOREACH(si, sil, si_link) {
if(si->si_weight < weight && si->si_error == 0)
break;
}
}
if(si == NULL) {
*error = SM_CODE_NO_FREE_ADAPTER;
return NULL;
}
service_start(si->si_s, si->si_instance);
return NULL;
}
#if 0
/**
*
*/
service_t *
service_find(channel_t *ch, unsigned int weight, const char *loginfo,
int *errorp, service_t *skip)
service_enlist(channel_t *ch)
{
service_t *t, **vec;
int cnt = 0, i, r, off;
@ -353,7 +345,7 @@ service_find(channel_t *ch, unsigned int weight, const char *loginfo,
/* Sort services, lower priority should come come earlier in the vector
(i.e. it will be more favoured when selecting a service */
qsort(vec, cnt, sizeof(service_t *), servicecmp);
// qsort(vec, cnt, sizeof(service_t *), servicecmp);
// Skip up to the service that the caller didn't want
// If the sorting above is not stable that might mess up things
@ -373,15 +365,6 @@ service_find(channel_t *ch, unsigned int weight, const char *loginfo,
t = vec[i];
if(t->s_status == SERVICE_RUNNING)
return t;
if(t->s_quality_index(t) < 10) {
if(loginfo != NULL) {
tvhlog(LOG_NOTICE, "Service",
"%s: Skipping \"%s\" -- Quality below 10%%",
loginfo, service_nicename(t));
err = SM_CODE_BAD_SIGNAL;
}
continue;
}
tvhlog(LOG_DEBUG, "Service", "%s: Probing adapter \"%s\" without stealing for service \"%s\"",
loginfo, service_adapter_nicename(t), service_nicename(t));
if((r = service_start(t, 0, 0)) == 0)
@ -409,29 +392,9 @@ service_find(channel_t *ch, unsigned int weight, const char *loginfo,
*errorp = SM_CODE_NO_SERVICE;
return NULL;
}
#endif
/**
*
*/
unsigned int
service_compute_weight(struct service_list *head)
{
service_t *t;
th_subscription_t *s;
int w = 0;
lock_assert(&global_lock);
LIST_FOREACH(t, head, s_active_link) {
LIST_FOREACH(s, &t->s_subscriptions, ths_service_link) {
if(s->ths_weight > w)
w = s->ths_weight;
}
}
return w;
}
/**
*
@ -481,14 +444,14 @@ service_destroy(service_t *t)
}
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_identifier);
free(t->s_svcname);
free(t->s_provider);
free(t->s_dvb_charset);
@ -512,17 +475,14 @@ service_destroy(service_t *t)
* Create and initialize a new service struct
*/
service_t *
service_create(const char *identifier, int type, int source_type)
service_create(const char *uuid, int source_type, const idclass_t *idc)
{
unsigned int hash = tvh_strhash(identifier, 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_identifier = strdup(identifier);
t->s_type = type;
t->s_source_type = source_type;
t->s_refcount = 1;
t->s_enabled = 1;
@ -535,7 +495,8 @@ service_create(const char *identifier, int type, int source_type)
streaming_pad_init(&t->s_streaming_pad);
LIST_INSERT_HEAD(&servicehash[hash], t, s_hash_link);
idnode_insert(&t->s_id, uuid, idc);
return t;
}
@ -545,15 +506,7 @@ service_create(const char *identifier, int type, int source_type)
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_identifier, identifier))
break;
return t;
return idnode_find(identifier, &service_class);
}
@ -703,6 +656,27 @@ service_map_channel(service_t *t, channel_t *ch, int save)
t->s_config_save(t);
}
/**
*
*/
static const char *service_channel_get(void *obj)
{
service_t *s = obj;
return s->s_ch ? s->s_ch->ch_name : NULL;
}
/**
*
*/
static void
service_channel_set(void *obj, const char *str)
{
service_map_channel(obj, str ? channel_find_by_name(str, 1, 0) : NULL, 0);
}
/**
*
*/
@ -974,6 +948,16 @@ service_request_save(service_t *t, int restart)
}
/**
*
*/
static void
service_save(struct idnode *self)
{
service_t *s = (service_t *)self;
s->s_config_save(s);
}
/**
*
*/
@ -1076,25 +1060,7 @@ service_component_nicename(elementary_stream_t *st)
const char *
service_adapter_nicename(service_t *t)
{
switch(t->s_type) {
case SERVICE_TYPE_DVB:
if(t->s_dvb_mux_instance)
return t->s_dvb_mux_instance->tdmi_identifier;
else
return "Unknown adapter";
case SERVICE_TYPE_IPTV:
return t->s_iptv_iface;
case SERVICE_TYPE_V4L:
if(t->s_v4l_adapter)
return t->s_v4l_adapter->va_displayname;
else
return "Unknown adapter";
default:
return "Unknown adapter";
}
return "Adapter";
}
const char *
@ -1155,6 +1121,73 @@ service_refresh_channel(service_t *t)
}
/**
*
*/
static int
si_cmp(const service_instance_t *a, const service_instance_t *b)
{
return a->si_prio - b->si_prio;
}
/**
*
*/
service_instance_t *
service_instance_add(struct service_instance_list *sil,
struct service *s, int instance, int prio,
int weight)
{
service_instance_t *si;
LIST_FOREACH(si, sil, si_link)
if(si->si_s == s && si->si_instance == instance)
break;
if(si == NULL) {
si = calloc(1, sizeof(service_instance_t));
si->si_s = s;
service_ref(s);
si->si_instance = instance;
si->si_weight = weight;
} else {
si->si_mark = 0;
if(si->si_prio == prio && si->si_weight == weight)
return si;
LIST_REMOVE(si, si_link);
}
si->si_weight = weight;
si->si_prio = prio;
LIST_INSERT_SORTED(sil, si, si_link, si_cmp);
return si;
}
/**
*
*/
void
service_instance_destroy(service_instance_t *si)
{
LIST_REMOVE(si, si_link);
service_unref(si->si_s);
free(si);
}
/**
*
*/
void
service_instance_list_clear(struct service_instance_list *sil)
{
lock_assert(&global_lock);
service_instance_t *si;
while((si = LIST_FIRST(sil)) != NULL)
service_instance_destroy(si);
}
/**
* Get the encryption CAID from a service
* only the first CA stream in a service is returned
@ -1190,7 +1223,7 @@ service_is_primary_epg(service_t *svc)
if (!svc || !svc->s_ch) return 0;
LIST_FOREACH(t, &svc->s_ch->ch_services, s_ch_link) {
if (!t->s_is_enabled(t) || !t->s_dvb_eit_enable) continue;
if (!ret || service_get_prio(t) < service_get_prio(ret))
if (!ret)
ret = t;
}
return !ret ? 0 : (ret->s_dvb_service_id == svc->s_dvb_service_id);

View file

@ -22,8 +22,10 @@
#define PID_TELETEXT_BASE 0x2000
#include "htsmsg.h"
#include "idnode.h"
extern const idclass_t service_class;
/**
* Descrambler superclass
@ -167,18 +169,55 @@ typedef struct elementary_stream {
} elementary_stream_t;
LIST_HEAD(service_instance_list, service_instance);
/**
*
*/
typedef struct service_instance {
LIST_ENTRY(service_instance) si_link;
int si_prio;
struct service *si_s; // A reference is held
int si_instance; // Discriminator when having multiple adapters, etc
int si_error; /* Set if subscription layer deem this cand
to be broken. We typically set this if we
have not seen any demuxed packets after
the grace period has expired.
The actual value is current time
*/
time_t si_error_time;
int si_weight; // Highest weight that holds this cand
int si_mark; // For mark & sweep
} service_instance_t;
/**
*
*/
service_instance_t *service_instance_add(struct service_instance_list *sil,
struct service *s,
int instance,
int prio,
int weight);
void service_instance_destroy(service_instance_t *si);
void service_instance_list_clear(struct service_instance_list *sil);
/**
*
*/
typedef struct service {
LIST_ENTRY(service) s_hash_link;
enum {
SERVICE_TYPE_DVB,
SERVICE_TYPE_IPTV,
SERVICE_TYPE_V4L,
} s_type;
idnode_t s_id;
enum {
/**
@ -263,8 +302,9 @@ typedef struct service {
LIST_HEAD(, th_subscription) s_subscriptions;
int (*s_start_feed)(struct service *t, unsigned int weight,
int force_start);
void (*s_enlist)(struct service *s, struct service_instance_list *sil);
int (*s_start_feed)(struct service *s, int instance);
void (*s_refresh_feed)(struct service *t);
@ -274,8 +314,6 @@ typedef struct service {
void (*s_setsourceinfo)(struct service *t, struct source_info *si);
int (*s_quality_index)(struct service *t);
int (*s_grace_period)(struct service *t);
void (*s_dtor)(struct service *t);
@ -283,12 +321,7 @@ typedef struct service {
/*
* Per source type structs
*/
struct th_dvb_mux_instance *s_dvb_mux_instance;
/**
* Unique identifer (used for storing on disk, etc)
*/
char *s_identifier;
struct dvb_mux *s_dvb_mux;
/**
* Name usable for displaying to user
@ -517,12 +550,9 @@ typedef struct service {
void service_init(void);
unsigned int service_compute_weight(struct service_list *head);
int service_start(service_t *t, int instance);
int service_start(service_t *t, unsigned int weight, int force_start);
service_t *service_create(const char *identifier, int type,
int source_type);
service_t *service_create(const char *uuid, int source_type, const idclass_t *idc);
void service_unref(service_t *t);
@ -532,9 +562,10 @@ service_t *service_find_by_identifier(const char *identifier);
void service_map_channel(service_t *t, struct channel *ch, int save);
service_t *service_find(struct channel *ch, unsigned int weight,
const char *loginfo, int *errorp,
service_t *skip);
service_instance_t *service_find_instance(struct channel *ch,
struct service_instance_list *sil,
int *error,
int weight);
elementary_stream_t *service_stream_find(service_t *t, int pid);

View file

@ -108,8 +108,8 @@ serviceprobe_thread(void *aux)
was_doing_work = 1;
}
if (t->s_dvb_mux_instance)
checksubscr = !t->s_dvb_mux_instance->tdmi_adapter->tda_skip_checksubscr;
if (t->s_dvb_mux->dm_dn)
checksubscr = !t->s_dvb_mux->dm_dn->dn_skip_checksubscr;
else
checksubscr = 1;
@ -180,10 +180,10 @@ serviceprobe_thread(void *aux)
} else if(t->s_ch == NULL) {
int channum = t->s_channel_number;
const char *str;
#if 0 // XXX(dvbreorg)
if (!channum && t->s_dvb_mux_instance->tdmi_adapter->tda_sidtochan)
channum = t->s_dvb_service_id;
#endif
ch = channel_find_by_name(t->s_svcname, 1, channum);
service_map_channel(t, ch, 1);

View file

@ -222,7 +222,7 @@ hts_settings_load_one(const char *filename)
*
*/
static htsmsg_t *
_hts_settings_load(const char *fullpath)
hts_settings_load_path(const char *fullpath, int depth)
{
char child[256];
struct filebundle_stat st;
@ -245,9 +245,16 @@ _hts_settings_load(const char *fullpath)
for(i = 0; i < n; i++) {
d = namelist[i];
if(d->name[0] != '.') {
snprintf(child, sizeof(child), "%s/%s", fullpath, d->name);
if ((c = hts_settings_load_one(child)))
snprintf(child, sizeof(child), "%s/%s", fullpath, d->name);
if(d->type == FB_DIR && depth > 0) {
c = hts_settings_load_path(child, depth - 1);
} else {
c = hts_settings_load_one(child);
}
if(c != NULL)
htsmsg_add_msg(r, d->name, c);
}
free(d);
}
@ -264,32 +271,57 @@ _hts_settings_load(const char *fullpath)
/**
*
*/
htsmsg_t *
hts_settings_load(const char *pathfmt, ...)
static htsmsg_t *
hts_settings_vload(const char *pathfmt, va_list ap, int depth)
{
htsmsg_t *ret = NULL;
char fullpath[256];
va_list ap;
va_list ap2;
va_copy(ap2, ap);
/* Try normal path */
va_start(ap, pathfmt);
_hts_settings_buildpath(fullpath, sizeof(fullpath),
pathfmt, ap, settingspath);
va_end(ap);
ret = _hts_settings_load(fullpath);
ret = hts_settings_load_path(fullpath, depth);
/* Try bundle path */
if (!ret && *pathfmt != '/') {
va_start(ap, pathfmt);
_hts_settings_buildpath(fullpath, sizeof(fullpath),
pathfmt, ap, "data/conf");
va_end(ap);
ret = _hts_settings_load(fullpath);
pathfmt, ap2, "data/conf");
ret = hts_settings_load_path(fullpath, depth);
}
return ret;
}
/**
*
*/
htsmsg_t *
hts_settings_load(const char *pathfmt, ...)
{
va_list ap;
va_start(ap, pathfmt);
htsmsg_t *r = hts_settings_vload(pathfmt, ap, 0);
va_end(ap);
return r;
}
/**
*
*/
htsmsg_t *
hts_settings_load_r(int depth, const char *pathfmt, ...)
{
va_list ap;
va_start(ap, pathfmt);
htsmsg_t *r = hts_settings_vload(pathfmt, ap, depth);
va_end(ap);
return r;
}
/**
*
*/

View file

@ -28,6 +28,8 @@ void hts_settings_save(htsmsg_t *record, const char *pathfmt, ...);
htsmsg_t *hts_settings_load(const char *pathfmt, ...);
htsmsg_t *hts_settings_load_r(int depth, const char *pathfmt, ...);
void hts_settings_remove(const char *pathfmt, ...);
const char *hts_settings_get_root(void);

View file

@ -418,8 +418,8 @@ streaming_code2txt(int code)
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:

View file

@ -155,9 +155,8 @@ void
subscription_reschedule(void)
{
th_subscription_t *s;
service_t *t, *skip;
service_instance_t *si;
streaming_message_t *sm;
char buf[128];
int error;
lock_assert(&global_lock);
@ -173,19 +172,20 @@ subscription_reschedule(void)
/* Already got a service */
if(s->ths_state != SUBSCRIPTION_BAD_SERVICE)
continue; /* And it seems to work ok, so we're happy */
skip = s->ths_service;
error = s->ths_testing_error;
service_remove_subscriber(s->ths_service, s, s->ths_testing_error);
} else {
error = 0;
skip = NULL;
continue; /* And it not bad, so we're happy */
si = s->ths_current_instance;
assert(si != NULL);
si->si_error = s->ths_testing_error;
time(&si->si_error_time);
}
snprintf(buf, sizeof(buf), "Subscription \"%s\"", s->ths_title);
t = service_find(s->ths_channel, s->ths_weight, buf, &error, skip);
si = service_find_instance(s->ths_channel, &s->ths_instances, &error,
s->ths_weight);
s->ths_current_instance = si;
if(t == NULL) {
if(si == NULL) {
/* No service available */
sm = streaming_msg_create_code(SMT_NOSTART, error);
@ -193,7 +193,7 @@ subscription_reschedule(void)
continue;
}
subscription_link_service(s, t);
subscription_link_service(s, si->si_s);
}
}
@ -207,6 +207,8 @@ subscription_unsubscribe(th_subscription_t *s)
lock_assert(&global_lock);
service_instance_list_clear(&s->ths_instances);
LIST_REMOVE(s, ths_global_link);
if(s->ths_channel != NULL) {
@ -396,14 +398,13 @@ subscription_create_from_channel(channel_t *ch, unsigned int weight,
tvhlog(LOG_INFO, "subscription",
"\"%s\" subscribing on \"%s\", weight: %d, adapter: \"%s\", "
"network: \"%s\", mux: \"%s\", provider: \"%s\", "
"service: \"%s\", quality: %d",
"service: \"%s\"",
s->ths_title, ch->ch_name, weight,
si.si_adapter ?: "<N/A>",
si.si_network ?: "<N/A>",
si.si_mux ?: "<N/A>",
si.si_provider ?: "<N/A>",
si.si_service ?: "<N/A>",
s->ths_service->s_quality_index(s->ths_service));
si.si_service ?: "<N/A>");
service_source_info_free(&si);
}
@ -421,6 +422,7 @@ subscription_create_from_service(service_t *t, const char *name,
const char *hostname, const char *username,
const char *client)
{
#if 0
th_subscription_t *s;
source_info_t si;
int r;
@ -445,19 +447,20 @@ subscription_create_from_service(service_t *t, const char *name,
tvhlog(LOG_INFO, "subscription",
"\"%s\" direct subscription to adapter: \"%s\", "
"network: \"%s\", mux: \"%s\", provider: \"%s\", "
"service: \"%s\", quality: %d",
"service: \"%s\"",
s->ths_title,
si.si_adapter ?: "<N/A>",
si.si_network ?: "<N/A>",
si.si_mux ?: "<N/A>",
si.si_provider ?: "<N/A>",
si.si_service ?: "<N/A>",
t->s_quality_index(t));
si.si_service ?: "<N/A>");
service_source_info_free(&si);
subscription_link_service(s, t);
notify_reload("subscriptions");
return s;
#endif
abort();
}

View file

@ -19,6 +19,8 @@
#ifndef SUBSCRIPTIONS_H
#define SUBSCRIPTIONS_H
#include "service.h"
extern struct th_subscription_list subscriptions;
#define SUBSCRIPTION_RAW_MPEGTS 0x1
@ -65,6 +67,12 @@ typedef struct th_subscription {
char *ths_username;
char *ths_client;
/**
* This is the list of service candidates we have
*/
struct service_instance_list ths_instances;
struct service_instance *ths_current_instance;
// Ugly ugly ugly to refer DVB code here
LIST_ENTRY(th_subscription) ths_tdmi_link;

40
src/tvadapters.c Normal file
View file

@ -0,0 +1,40 @@
/*
* TV Adapters
* Copyright (C) 2013 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 "tvheadend.h"
#include "tvadapters.h"
#include "dvb/dvb.h"
/**
*
*/
idnode_t **
tv_adapters_root(void)
{
dvb_hardware_t *dh;
int cnt = 1;
TAILQ_FOREACH(dh, &dvb_adapters, dh_parent_link)
cnt++;
idnode_t **v = malloc(sizeof(idnode_t *) * cnt);
cnt = 0;
TAILQ_FOREACH(dh, &dvb_adapters, dh_parent_link)
v[cnt++] = &dh->dh_id;
v[cnt] = NULL;
return v;
}

5
src/tvadapters.h Normal file
View file

@ -0,0 +1,5 @@
#pragma once
#include "idnode.h"
idnode_t **tv_adapters_root(void);

View file

@ -27,6 +27,7 @@
#include <netinet/in.h>
#include <sys/time.h>
#include <libgen.h>
#include <string.h>
#include "queue.h"
#include "avg.h"
@ -80,6 +81,7 @@ typedef struct source_info {
int si_type;
} source_info_t;
static inline void
lock_assert0(pthread_mutex_t *l, const char *file, int line)
{
@ -349,7 +351,7 @@ typedef enum {
#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
@ -493,6 +495,12 @@ typedef struct th_pipe
int wr;
} th_pipe_t;
static inline void mystrset(char **p, const char *s)
{
free(*p);
*p = s ? strdup(s) : NULL;
}
int tvh_open(const char *pathname, int flags, mode_t mode);
int tvh_socket(int domain, int type, int protocol);

View file

@ -45,6 +45,10 @@ struct v4l_adapter_queue v4l_adapters;
static void v4l_adapter_notify(v4l_adapter_t *va);
const idclass_t v4l_class = {
.ic_super = &service_class,
.ic_class = "v4l",
};
/**
*
@ -170,7 +174,7 @@ v4l_thread(void *aux)
*
*/
static int
v4l_service_start(service_t *t, unsigned int weight, int force_start)
v4l_service_start(service_t *t, int instance)
{
v4l_adapter_t *va = t->s_v4l_adapter;
int frequency = t->s_v4l_frequency;
@ -289,21 +293,12 @@ v4l_service_save(service_t *t)
pthread_mutex_unlock(&t->s_stream_mutex);
hts_settings_save(m, "v4lservices/%s/%s",
va->va_identifier, t->s_identifier);
va->va_identifier, idnode_uuid_as_str(&t->s_id));
htsmsg_destroy(m);
}
/**
*
*/
static int
v4l_service_quality(service_t *t)
{
return 100;
}
/**
*
*/
@ -358,7 +353,7 @@ v4l_service_find(v4l_adapter_t *va, const char *id, int create)
return NULL;
LIST_FOREACH(t, &va->va_services, s_group_link)
if(!strcmp(t->s_identifier, id))
if(!strcmp(idnode_uuid_as_str(&t->s_id), id))
return t;
}
@ -373,14 +368,13 @@ v4l_service_find(v4l_adapter_t *va, const char *id, int create)
va->va_tally = MAX(atoi(id + vaidlen + 1), va->va_tally);
}
t = service_create(id, SERVICE_TYPE_V4L, 0);
t = service_create(id, 0, &v4l_class);
t->s_start_feed = v4l_service_start;
t->s_refresh_feed = v4l_service_refresh;
t->s_stop_feed = v4l_service_stop;
t->s_config_save = v4l_service_save;
t->s_setsourceinfo = v4l_service_setsourceinfo;
t->s_quality_index = v4l_service_quality;
t->s_is_enabled = v4l_service_is_enabled;
t->s_grace_period = v4l_grace_period;
t->s_iptv_fd = -1;

View file

@ -50,6 +50,7 @@
#include "imagecache.h"
#include "timeshift.h"
#include "tvhtime.h"
#include "tvadapters.h"
/**
*
@ -115,6 +116,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
extjs_load(hq, "static/lovcombo/lovcombo-all.js");
extjs_load(hq, "static/multiselect/multiselect.js");
extjs_load(hq, "static/multiselect/ddview.js");
extjs_load(hq, "static/checkcolumn/CheckColumn.js");
/**
* Create a namespace for our app
@ -133,6 +135,7 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque)
extjs_load(hq, "static/app/tvadapters.js");
#if ENABLE_LINUXDVB
extjs_load(hq, "static/app/dvb.js");
extjs_load(hq, "static/app/dvb_networks.js");
#endif
extjs_load(hq, "static/app/iptv.js");
#if ENABLE_V4L
@ -1773,7 +1776,7 @@ build_record_iptv(service_t *t)
htsmsg_t *r = htsmsg_create_map();
char abuf[INET_ADDRSTRLEN];
char abuf6[INET6_ADDRSTRLEN];
htsmsg_add_str(r, "id", t->s_identifier);
// 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 ?: "");
@ -1920,6 +1923,7 @@ extjs_service_update(htsmsg_t *in)
}
}
#if 0
/**
*
*/
@ -1951,6 +1955,7 @@ extjs_tvadapter(http_connection_t *hc, const char *remain, void *opaque)
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
#endif
/**
*
@ -2138,6 +2143,109 @@ extjs_tvhlog(http_connection_t *hc, const char *remain, void *opaque)
return 0;
}
/**
*
*/
int
extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque,
idnode_t **(*rootfn)(void))
{
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "node");
htsmsg_t *out = NULL;
if(s == NULL)
return HTTP_STATUS_BAD_REQUEST;
pthread_mutex_lock(&global_lock);
if(http_access_verify(hc, ACCESS_ADMIN)) {
pthread_mutex_unlock(&global_lock);
return HTTP_STATUS_UNAUTHORIZED;
}
out = htsmsg_create_list();
idnode_t **v;
if(!strcmp(s, "root")) {
v = rootfn();
} else {
v = idnode_get_childs(idnode_find(s, NULL));
}
if(v != NULL) {
int i;
for(i = 0; v[i] != NULL; i++) {
htsmsg_t *m = idnode_serialize(v[i]);
htsmsg_add_u32(m, "leaf", idnode_is_leaf(v[i]));
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");
return 0;
}
static const char *
get_prop_value(void *opaque, const char *key)
{
http_connection_t *hc = opaque;
return http_arg_get(&hc->hc_req_args, key);
}
/**
*
*/
static int
extjs_item_update(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
htsmsg_t *out = NULL;
if(remain == NULL)
return HTTP_STATUS_BAD_REQUEST;
pthread_mutex_lock(&global_lock);
idnode_t *n = idnode_find(remain, NULL);
if(n == NULL) {
pthread_mutex_unlock(&global_lock);
return 404;
}
idnode_update_all_props(n, get_prop_value, hc);
pthread_mutex_unlock(&global_lock);
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
/**
*
*/
static int
extjs_tvadapters(http_connection_t *hc, const char *remain, void *opaque)
{
return extjs_get_idnode(hc, remain, opaque, &tv_adapters_root);
}
/**
* Capability check
*/
@ -2254,11 +2362,15 @@ extjs_start(void)
http_path_add("/mergechannel", NULL, extjs_mergechannel, ACCESS_ADMIN);
http_path_add("/iptv/services", NULL, extjs_iptvservices, ACCESS_ADMIN);
http_path_add("/servicedetails", NULL, extjs_servicedetails, ACCESS_ADMIN);
http_path_add("/tv/adapter", NULL, extjs_tvadapter, ACCESS_ADMIN);
// http_path_add("/tv/adapter", NULL, extjs_tvadapter, ACCESS_ADMIN);
#if ENABLE_TIMESHIFT
http_path_add("/timeshift", NULL, extjs_timeshift, ACCESS_ADMIN);
#endif
http_path_add("/tvhlog", NULL, extjs_tvhlog, ACCESS_ADMIN);
http_path_add("/item/update", NULL, extjs_item_update, ACCESS_ADMIN);
http_path_add("/tvadapters",
NULL, extjs_tvadapters, ACCESS_ADMIN);
#if ENABLE_LINUXDVB
extjs_start_dvb();

View file

@ -43,11 +43,14 @@
#include "dvb/dvb_preconf.h"
#include "dvr/dvr.h"
#if 0
/**
*
*/
static int
extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
extjs_dvblocations(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
const char *s = http_arg_get(&hc->hc_req_args, "node");
@ -57,7 +60,7 @@ extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
if(s == NULL || a == NULL)
return HTTP_STATUS_BAD_REQUEST;
pthread_mutex_lock(&global_lock);
if(http_access_verify(hc, ACCESS_ADMIN)) {
@ -72,7 +75,7 @@ extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
pthread_mutex_unlock(&global_lock);
if((out = dvb_mux_preconf_get_node(tda->tda_type, s)) == NULL)
if((out = dvb_mux_preconf_get_node(tda->tda_fe_type, s)) == NULL)
return 404;
htsmsg_json_serialize(out, hq, 0);
@ -80,7 +83,10 @@ extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
#endif
#if 0
/**
*
*/
@ -98,7 +104,6 @@ json_single_record(htsmsg_t *rec, const char *root)
}
/**
*
*/
@ -110,9 +115,7 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
htsmsg_t *out, *array, *r;
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, *sc;
th_dvb_mux_instance_t *tdmi;
service_t *t;
const char *s;
pthread_mutex_lock(&global_lock);
@ -124,7 +127,7 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
array = htsmsg_create_list();
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
if(ref == NULL || (ref != tda && ref->tda_type == tda->tda_type))
if(ref == NULL || (ref != tda && ref->tda_fe_type == tda->tda_fe_type))
htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda));
}
pthread_mutex_unlock(&global_lock);
@ -148,16 +151,11 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
htsmsg_add_u32(r, "enabled", tda->tda_enabled);
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, "idlescan", tda->tda_idlescan);
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_u32(r, "grace_period", tda->tda_grace_period);
htsmsg_add_str(r, "diseqcversion",
@ -184,15 +182,9 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
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, "idlescan");
dvb_adapter_set_idlescan(tda, !!s);
s = http_arg_get(&hc->hc_req_args, "idleclose");
dvb_adapter_set_idleclose(tda, !!s);
s = http_arg_get(&hc->hc_req_args, "skip_checksubscr");
dvb_adapter_set_skip_checksubscr(tda, !!s);
s = http_arg_get(&hc->hc_req_args, "qmon");
dvb_adapter_set_qmon(tda, !!s);
@ -202,9 +194,6 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
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);
@ -236,10 +225,10 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
htsmsg_add_u32(out, "success", 1);
} else if(!strcmp(op, "addnetwork")) {
sc = http_arg_get(&hc->hc_req_args, "satconf");
// 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, sc);
dvb_mux_preconf_add_network(tda->tda_dn, s);
out = htsmsg_create_map();
htsmsg_add_u32(out, "success", 1);
@ -249,10 +238,13 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
tvhlog(LOG_NOTICE, "web interface",
"Service probe started on \"%s\"", tda->tda_displayname);
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) {
if(t->s_enabled)
serviceprobe_enqueue(t);
dvb_mux_t *dm;
service_t *s;
LIST_FOREACH(dm, &tda->tda_dn->dn_muxes, dm_network_link) {
LIST_FOREACH(s, &dm->dm_services, s_group_link) {
if(s->s_enabled)
serviceprobe_enqueue(s);
}
}
@ -272,7 +264,9 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
#endif
#if 0
/**
*
@ -346,7 +340,7 @@ extjs_dvbmuxes(http_connection_t *hc, const char *remain, void *opaque)
if(!strcmp(op, "get")) {
array = htsmsg_create_list();
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
LIST_FOREACH(tdmi, &tda->tda_dn->dn_mux_instances, tdmi_adapter_link)
htsmsg_add_msg(array, NULL, dvb_mux_build_msg(tdmi));
htsmsg_add_msg(out, "entries", array);
@ -427,16 +421,16 @@ extjs_dvbservices(http_connection_t *hc, const char *remain, void *opaque)
out = htsmsg_create_map();
array = htsmsg_create_list();
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) {
LIST_FOREACH(tdmi, &tda->tda_dn->dn_mux_instances, tdmi_adapter_link) {
LIST_FOREACH(t, &tdmi->tdmi_mux->dm_services, s_group_link) {
count++;
}
}
tvec = alloca(sizeof(service_t *) * count);
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) {
LIST_FOREACH(tdmi, &tda->tda_dn->dn_mux_instances, tdmi_adapter_link) {
LIST_FOREACH(t, &tdmi->tdmi_mux->dm_services, s_group_link) {
tvec[i++] = t;
}
}
@ -620,7 +614,7 @@ extjs_dvb_addmux(http_connection_t *hc, const char *remain, void *opaque)
return 0;
}
#if 0
/**
*
*/
@ -684,16 +678,17 @@ extjs_dvb_copymux(http_connection_t *hc, const char *remain, void *opaque)
return 0;
}
#endif
#endif
/**
*
*/
void
extjs_list_dvb_adapters(htsmsg_t *array)
static int
extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
{
th_dvb_adapter_t *tda;
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda));
return extjs_get_idnode(hc, remain, opaque, &dvb_network_root);
}
@ -703,12 +698,16 @@ extjs_list_dvb_adapters(htsmsg_t *array)
void
extjs_start_dvb(void)
{
http_path_add("/dvbnetworks",
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);
http_path_add("/dvb/muxes",
NULL, extjs_dvbmuxes, ACCESS_ADMIN);
@ -726,8 +725,8 @@ extjs_start_dvb(void)
http_path_add("/dvb/addmux",
NULL, extjs_dvb_addmux, ACCESS_ADMIN);
http_path_add("/dvb/copymux",
NULL, extjs_dvb_copymux, ACCESS_ADMIN);
#endif
}

View file

@ -170,7 +170,7 @@ build_record_v4l(service_t *t)
{
htsmsg_t *r = htsmsg_create_map();
htsmsg_add_str(r, "id", t->s_identifier);
// htsmsg_add_str(r, "id", t->s_identifier); // XXX(dvbreorg)
htsmsg_add_str(r, "channelname", t->s_ch ? t->s_ch->ch_name : "");
htsmsg_add_u32(r, "frequency", t->s_v4l_frequency);
@ -265,6 +265,7 @@ extjs_v4lservices(http_connection_t *hc, const char *remain, void *opaque)
return 0;
}
#if 0
/**
*
@ -277,6 +278,7 @@ extjs_list_v4l_adapters(htsmsg_t *array)
TAILQ_FOREACH(va, &v4l_adapters, va_global_link)
htsmsg_add_msg(array, NULL, v4l_adapter_build_msg(va));
}
#endif
/**

View file

@ -76,7 +76,7 @@ dumpchannels(htsbuf_queue_t *hq)
}
}
#if ENABLE_LINUXDVB
#if 0
static void
dumptransports(htsbuf_queue_t *hq, struct service_list *l, int indent)
{
@ -127,23 +127,11 @@ static void
dumpdvbadapters(htsbuf_queue_t *hq)
{
th_dvb_adapter_t *tda;
th_dvb_mux_instance_t *tdmi;
outputtitle(hq, 0, "DVB Adapters");
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
htsbuf_qprintf(hq, "%s (%s)\n", tda->tda_displayname, tda->tda_identifier);
outputtitle(hq, 4, "Multiplexes");
LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
char tdminame[64];
dvb_mux_nicename(tdminame, sizeof(tdminame), tdmi);
htsbuf_qprintf(hq, " %s (%s)\n",
tdminame, tdmi->tdmi_identifier);
htsbuf_qprintf(hq, "\n");
dumptransports(hq, &tdmi->tdmi_transports, 8);
}
}
}
#endif
@ -182,10 +170,6 @@ page_statedump(http_connection_t *hc, const char *remain, void *opaque)
tvh_binshasum[19]);
dumpchannels(hq);
#if ENABLE_LINUXDVB
dumpdvbadapters(hq);
#endif
http_output_content(hc, "text/plain; charset=UTF-8");
return 0;

View file

@ -1,77 +1,73 @@
tvheadend.acleditor = function() {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 60
});
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
var streamingColumn = new Ext.grid.CheckColumn({
header : "Streaming",
dataIndex : 'streaming',
width : 100
});
columns : [{
xtype: 'checkcolumn',
header : "Enabled",
dataIndex : 'enabled',
width : 60
}, {
header : "Username",
dataIndex : 'username',
editor : new fm.TextField({
allowBlank : false
})
}, {
header : "Password",
dataIndex : 'password',
renderer : function(value, metadata, record, row, col, store) {
return '<span class="tvh-grid-unset">Hidden</span>';
},
editor : new fm.TextField({
allowBlank : false
})
}, {
header : "Prefix",
dataIndex : 'prefix',
editor : new fm.TextField({
allowBlank : false
})
}, {
xtype: 'checkcolumn',
header : "Streaming",
dataIndex : 'streaming',
width : 100
}, {
xtype: 'checkcolumn',
header : "Video Recorder",
dataIndex : 'dvr',
width : 100
}, {
xtype: 'checkcolumn',
header : "All Configs (VR)",
dataIndex : 'dvrallcfg',
width : 100
}, {
xtype: 'checkcolumn',
header : "Web Interface",
dataIndex : 'webui',
width : 100
}, {
xtype: 'checkcolumn',
header : "Admin",
dataIndex : 'admin',
width : 100
}, {
header : "Comment",
dataIndex : 'comment',
width : 400,
editor : new fm.TextField({})
}]});
var dvrColumn = new Ext.grid.CheckColumn({
header : "Video Recorder",
dataIndex : 'dvr',
width : 100
});
var UserRecord = Ext.data.Record.create(
[ 'enabled', 'streaming', 'dvr', 'dvrallcfg', 'admin', 'webui', 'username',
'prefix', 'password', 'comment'
]);
var dvrallcfgColumn = new Ext.grid.CheckColumn({
header : "All Configs (VR)",
dataIndex : 'dvrallcfg',
width : 100
});
var webuiColumn = new Ext.grid.CheckColumn({
header : "Web Interface",
dataIndex : 'webui',
width : 100
});
var adminColumn = new Ext.grid.CheckColumn({
header : "Admin",
dataIndex : 'admin',
width : 100
});
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns : [ enabledColumn, {
header : "Username",
dataIndex : 'username',
editor : new fm.TextField({
allowBlank : false
})
}, {
header : "Password",
dataIndex : 'password',
renderer : function(value, metadata, record, row, col, store) {
return '<span class="tvh-grid-unset">Hidden</span>';
},
editor : new fm.TextField({
allowBlank : false
})
}, {
header : "Prefix",
dataIndex : 'prefix',
editor : new fm.TextField({
allowBlank : false
})
}, streamingColumn, dvrColumn, dvrallcfgColumn, webuiColumn, adminColumn, {
header : "Comment",
dataIndex : 'comment',
width : 400,
editor : new fm.TextField({})
} ]});
var UserRecord = Ext.data.Record.create([ 'enabled', 'streaming', 'dvr',
'dvrallcfg', 'admin', 'webui', 'username', 'prefix', 'password',
'comment' ]);
return new tvheadend.tableEditor('Access control', 'accesscontrol', cm,
UserRecord, [ enabledColumn, streamingColumn, dvrColumn, dvrallcfgColumn,
webuiColumn, adminColumn ], null, 'config_access.html', 'group');
return new tvheadend.tableEditor('Access control', 'accesscontrol', cm,
UserRecord, [], null, 'config_access.html',
'group');
}

View file

@ -1,18 +1,6 @@
tvheadend.capmteditor = function() {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 60
});
var oscamColumn = new Ext.grid.CheckColumn({
header : "OSCam mode",
dataIndex : 'oscam',
width : 60
});
function setMetaAttr(meta, record) {
var enabled = record.get('enabled');
if (!enabled) return;
@ -28,7 +16,12 @@ tvheadend.capmteditor = function() {
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns: [ enabledColumn, {
columns: [ {
xtype: 'checkcolumn',
header : "Enabled",
dataIndex : 'enabled',
width : 60
}, {
header : "Camd.socket Filename",
dataIndex : 'camdfilename',
width : 200,
@ -49,7 +42,13 @@ tvheadend.capmteditor = function() {
editor : new fm.TextField({
allowBlank : false
})
}, oscamColumn, {
}, {
xtype: 'checkcolumn',
header : "OSCam mode",
dataIndex : 'oscam',
width : 60
} , {
header : "Comment",
dataIndex : 'comment',
width : 400,
@ -83,5 +82,5 @@ tvheadend.capmteditor = function() {
});
return new tvheadend.tableEditor('Capmt Connections', 'capmt', cm, rec,
[ enabledColumn, oscamColumn ], store, 'config_capmt.html', 'key');
[ ], store, 'config_capmt.html', 'key');
}

View file

@ -1,51 +1,49 @@
tvheadend.cteditor = function() {
var fm = Ext.form;
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 60
});
var internalColumn = new Ext.grid.CheckColumn({
header : "Internal",
dataIndex : 'internal',
width : 100
});
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns : [{
xtype: 'checkcolumn',
header : "Enabled",
dataIndex : 'enabled',
width : 60
} , {
header : "Name",
dataIndex : 'name',
editor : new fm.TextField({
allowBlank : false
})
}, {
xtype: 'checkcolumn',
header : "Internal",
dataIndex : 'internal',
width : 100
}, {
header : "Icon (full URL)",
dataIndex : 'icon',
width : 400,
editor : new fm.TextField({})
}, {
xtype: 'checkcolumn',
header : "Icon has title",
dataIndex : 'titledIcon',
width : 100,
tooltip : 'Set this if the supplied icon has a title embedded. '
+ 'This will tell displaying application not to superimpose title '
+ 'on top of logo.'
}, {
header : "Comment",
dataIndex : 'comment',
width : 400,
editor : new fm.TextField({})
} ]});
var titledIconColumn = new Ext.grid.CheckColumn({
header : "Icon has title",
dataIndex : 'titledIcon',
width : 100,
tooltip : 'Set this if the supplied icon has a title embedded. '
+ 'This will tell displaying application not to superimpose title '
+ 'on top of logo.'
});
var ChannelTagRecord = Ext.data.Record.create([
'enabled', 'name', 'internal', 'icon', 'comment', 'titledIcon' ]);
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns : [ enabledColumn, {
header : "Name",
dataIndex : 'name',
editor : new fm.TextField({
allowBlank : false
})
}, internalColumn, {
header : "Icon (full URL)",
dataIndex : 'icon',
width : 400,
editor : new fm.TextField({})
}, titledIconColumn, {
header : "Comment",
dataIndex : 'comment',
width : 400,
editor : new fm.TextField({})
} ]});
var ChannelTagRecord = Ext.data.Record.create([ 'enabled', 'name',
'internal', 'icon', 'comment', 'titledIcon' ]);
return new tvheadend.tableEditor('Channel Tags', 'channeltags', cm,
ChannelTagRecord, [ enabledColumn, internalColumn, titledIconColumn ],
null, 'config_tags.html', 'tags');
return new tvheadend.tableEditor('Channel Tags', 'channeltags', cm,
ChannelTagRecord, [],
null, 'config_tags.html', 'tags');
}

View file

@ -1,24 +1,6 @@
tvheadend.cwceditor = function() {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 60
});
var emmColumn = new Ext.grid.CheckColumn({
header : "Update Card",
dataIndex : 'emm',
width : 100
});
var emmexColumn = new Ext.grid.CheckColumn({
header : "Update One",
dataIndex : 'emmex',
width : 100
});
function setMetaAttr(meta, record) {
var enabled = record.get('enabled');
if (!enabled) return;
@ -34,7 +16,12 @@ tvheadend.cwceditor = function() {
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns : [ enabledColumn, {
columns : [ {
xtype: 'checkcolumn',
header : "Enabled",
dataIndex : 'enabled',
width : 60
}, {
header : "Hostname",
dataIndex : 'hostname',
width : 200,
@ -86,7 +73,17 @@ tvheadend.cwceditor = function() {
editor : new fm.TextField({
allowBlank : false
})
}, emmColumn, emmexColumn, {
}, {
xtype: 'checkcolumn',
header : "Update Card",
dataIndex : 'emm',
width : 100
}, {
xtype: 'checkcolumn',
header : "Update One",
dataIndex : 'emmex',
width : 100
}, {
header : "Comment",
dataIndex : 'comment',
width : 400,
@ -112,8 +109,8 @@ tvheadend.cwceditor = function() {
}
});
var grid = new tvheadend.tableEditor('Code Word Client', 'cwc', cm, rec, [
enabledColumn, emmColumn, emmexColumn ], store, 'config_cwc.html', 'key');
var grid = new tvheadend.tableEditor('Code Word Client', 'cwc', cm, rec, [],
store, 'config_cwc.html', 'key');
tvheadend.comet.on('cwcStatus', function(msg) {
var rec = store.getById(msg.id);

View file

@ -7,12 +7,6 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 40
});
var qualityColumn = new Ext.ux.grid.ProgressColumn({
header : "Quality",
dataIndex : 'quality',
@ -23,15 +17,19 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) {
var cmlist = Array();
cmlist.push(enabledColumn, {
cmlist.push({
xtype: 'checkcolumn',
header : "Enabled",
dataIndex : 'enabled',
width : 40
}, {
header : "Play",
dataIndex : 'id',
width : 50,
renderer : function(value, metadata, record, row, col, store) {
url = 'stream/mux/' + value
return '<a href="' + url + '">Play</a>'
}
}, {
}, {
header : "Network",
dataIndex : 'network',
width : 200
@ -363,7 +361,7 @@ tvheadend.dvb_muxes = function(adapterData, satConfStore) {
var grid = new Ext.grid.EditorGridPanel({
stripeRows : true,
title : 'Multiplexes',
plugins : [ enabledColumn, qualityColumn ],
plugins : [ qualityColumn ],
store : store,
clicksToEdit : 2,
cm : cm,
@ -392,17 +390,6 @@ tvheadend.dvb_services = function(adapterData, satConfStore) {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 45
});
var eitColumn = new Ext.grid.CheckColumn({
header : "EPG",
dataIndex : 'dvb_eit_enable',
width : 45
});
var actions = new Ext.ux.grid.RowActions({
header : '',
@ -423,10 +410,15 @@ tvheadend.dvb_services = function(adapterData, satConfStore) {
} ]
});
var cmlist = Array();
cmlist.push(enabledColumn,
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns: [
{
xtype: 'checkcolumn',
header : "Enabled",
dataIndex : 'enabled',
width : 45
},
{
header : "Service name",
dataIndex : 'svcname',
@ -498,7 +490,12 @@ tvheadend.dvb_services = function(adapterData, satConfStore) {
displayField : 'value',
valueField : 'key'
})
}, eitColumn, {
}, {
xtype: 'checkcolumn',
header : "EPG",
dataIndex : 'dvb_eit_enable',
width : 45
} , {
header : "Type",
dataIndex : 'type',
width : 50
@ -684,7 +681,7 @@ tvheadend.dvb_services = function(adapterData, satConfStore) {
var grid = new Ext.grid.EditorGridPanel({
stripeRows : true,
title : 'Services',
plugins : [ enabledColumn, eitColumn, actions ],
plugins : [ actions ],
store : store,
clicksToEdit : 2,
cm : cm,
@ -745,7 +742,7 @@ tvheadend.addMuxByLocation = function(adapterData, satConfStore) {
baseParams : {
adapter : adapterData.identifier
},
dataUrl : 'dvbnetworks'
dataUrl : 'dvb/locations'
}),
root : new Ext.tree.AsyncTreeNode({
id : 'root'
@ -1192,7 +1189,7 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) {
var confreader = new Ext.data.JsonReader({
root : 'dvbadapters'
}, [ 'name', 'enabled', 'automux', 'skip_initialscan', 'idlescan', 'diseqcversion',
}, [ 'name', 'enabled', 'automux', 'skip_initialscan', 'diseqcversion',
'diseqcrepeats', 'qmon', 'skip_checksubscr',
'poweroff', 'sidtochan', 'nitoid', 'extrapriority',
,'disable_pmt_monitor', 'full_mux_rx', 'idleclose', 'grace_period' ]);
@ -1225,10 +1222,6 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) {
fieldLabel : 'Skip initial scan',
name : 'skip_initialscan'
}),
new Ext.form.Checkbox({
fieldLabel : 'Idle scanning',
name : 'idlescan'
}),
new Ext.form.Checkbox({
fieldLabel : 'Close device handle when idle',
name : 'idleclose'
@ -1349,7 +1342,7 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) {
+ '<h3>Symbolrate range:</h3>'
+ '{symrateMin} Baud - {symrateMax} Baud</tpl>'
+ '<h2 style="font-size: 150%">Status</h2>'
+ '<h3>Currently tuned to:</h3>{currentMux}&nbsp'
+ '<h3>Currently tuned to:</h3>{currentMux}&nbsp({reason})'
+ '<h3>Services:</h3>{services}' + '<h3>Muxes:</h3>{muxes}'
+ '<h3>Muxes awaiting initial scan:</h3>{initialMuxes}'
+ '<h3>Signal Strength:</h3>{signal}%'
@ -1385,10 +1378,13 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) {
if (r.data.identifier != adapterId) return;
infoTemplate.overwrite(infoPanel.body, r.data);
serviceScanBtn.enable();
if (r.data.services > 0 && r.data.initialMuxes == 0) serviceScanBtn
.enable();
else serviceScanBtn.disable();
});
serviceScanBtn.enable();
return panel;
}

View file

@ -0,0 +1,142 @@
tvheadend.item_editor = function(item) {
var fields = []
for (var idx in item.params) {
var f = item.params[idx];
switch(f.type) {
case 'str':
fields.push({
fieldLabel: f.caption,
name: f.id,
value: f.value
});
break;
case 'bool':
fields.push({
xtype: 'checkbox',
fieldLabel: f.caption,
name: f.id,
checked: f.value
});
break;
case 'separator':
fields.push({
xtype: 'label',
fieldLabel: f.caption,
});
break;
}
}
var panel = new Ext.FormPanel({
labelWidth: 150, // label settings here cascade unless overridden
frame:true,
title: 'Parameters',
bodyStyle:'padding:5px 5px 0',
width: 500,
defaults: {width: 330},
defaultType: 'textfield',
items: fields,
buttons: [{
text: 'Save',
handler: function(){
if(panel.getForm().isValid()){
panel.getForm().submit({
url: 'item/update/' + item.id,
waitMsg : 'Saving Data...'
});
}
}
},{
text: 'Reset',
handler: function(){
panel.getForm().reset();
}
}]
});
return panel;
}
/**
*
*/
tvheadend.item_browser = function(url, title) {
var current = null;
var loader = new Ext.tree.TreeLoader({
dataUrl: url
});
var tree = new Ext.tree.TreePanel({
loader: loader,
flex:1,
border: false,
root : new Ext.tree.AsyncTreeNode({
id : 'root',
text: title
}),
listeners: {
click: function(n) {
if(current)
panel.remove(current);
current = panel.add(new tvheadend.item_editor(n.attributes));
panel.doLayout();
}
}
});
tvheadend.comet.on('idnodeNameChanged', function(o) {
var n = tree.getNodeById(o.id);
if(n) {
n.setText(o.text);
}
});
tvheadend.comet.on('idnodeParamsChanged', function(o) {
var n = tree.getNodeById(o.id);
if(n) {
n.attributes.params = o.params;
}
});
var panel = new Ext.Panel({
title: title,
layout: 'hbox',
flex: 1,
padding: 5,
border: false,
layoutConfig: {
align:'stretch'
},
items: [tree]
});
tree.on('render', function() {
tree.getRootNode().expand();
});
return panel;
}
/**
*
*/
tvheadend.dvb_networks = function() {
return tvheadend.item_browser('/dvb/networks', 'DVB Networks');
}

View file

@ -427,17 +427,18 @@ tvheadend.dvrschedule = function(title, iconCls, dvrStore) {
tvheadend.autoreceditor = function() {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 30
});
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns :
[
enabledColumn,
[
{
header: 'Enabled',
dataIndex: 'enabled',
width: 30,
xtype: 'checkcolumn'
},
{
header : "Title (Regexp)",
dataIndex : 'title',
@ -585,7 +586,7 @@ tvheadend.autoreceditor = function() {
} ]});
return new tvheadend.tableEditor('Automatic Recorder', 'autorec', cm,
tvheadend.autorecRecord, [ enabledColumn ], tvheadend.autorecStore,
tvheadend.autorecRecord, [], tvheadend.autorecStore,
'autorec.html', 'wand');
}
/**

View file

@ -6,173 +6,6 @@
* http://extjs.com/license
*/
/**
* CheckedColumn
*/
Ext.grid.CheckColumn = function(config){
Ext.apply(this, config);
if(!this.id){
this.id = Ext.id();
}
this.renderer = this.renderer.createDelegate(this);
};
Ext.grid.CheckColumn.prototype ={
init : function(grid){
this.grid = grid;
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},
onMouseDown : function(e, t){
if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
e.stopEvent();
var index = this.grid.getView().findRowIndex(t);
var record = this.grid.store.getAt(index);
record.set(this.dataIndex, !record.data[this.dataIndex]);
}
},
renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
}
};
/**
* Rowexpander
*/
Ext.grid.RowExpander = function(config){
Ext.apply(this, config);
this.addEvents({
beforeexpand : true,
expand: true,
beforecollapse: true,
collapse: true
});
Ext.grid.RowExpander.superclass.constructor.call(this);
if(this.tpl){
if(typeof this.tpl == 'string'){
this.tpl = new Ext.Template(this.tpl);
}
this.tpl.compile();
}
this.state = {};
this.bodyContent = {};
};
Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
header: "",
width: 20,
sortable: false,
fixed:true,
menuDisabled:true,
dataIndex: '',
id: 'expander',
lazyRender : true,
enableCaching: true,
getRowClass : function(record, rowIndex, p, ds){
p.cols = p.cols-1;
var content = this.bodyContent[record.id];
if(!content && !this.lazyRender){
content = this.getBodyContent(record, rowIndex);
}
if(content){
p.body = content;
}
return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
},
init : function(grid){
this.grid = grid;
var view = grid.getView();
view.getRowClass = this.getRowClass.createDelegate(this);
view.enableRowBody = true;
grid.on('render', function(){
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},
getBodyContent : function(record, index){
if(!this.enableCaching){
return this.tpl.apply(record.data);
}
var content = this.bodyContent[record.id];
if(!content){
content = this.tpl.apply(record.data);
this.bodyContent[record.id] = content;
}
return content;
},
onMouseDown : function(e, t){
if(t.className == 'x-grid3-row-expander'){
e.stopEvent();
var row = e.getTarget('.x-grid3-row');
this.toggleRow(row);
}
},
renderer : function(v, p, record){
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander">&#160;</div>';
},
beforeExpand : function(record, body, rowIndex){
if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
if(this.tpl && this.lazyRender){
body.innerHTML = this.getBodyContent(record, rowIndex);
}
return true;
}else{
return false;
}
},
toggleRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
},
expandRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
if(this.beforeExpand(record, body, row.rowIndex)){
this.state[record.id] = true;
Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
this.fireEvent('expand', this, record, body, row.rowIndex);
}
},
collapseRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.fireEvent('collapse', this, record, body, row.rowIndex);
}
}
});

View file

@ -16,11 +16,6 @@ tvheadend.iptv = function(adapterId) {
var fm = Ext.form;
var enabledColumn = new Ext.grid.CheckColumn({
header : "Enabled",
dataIndex : 'enabled',
width : 45
});
var actions = new Ext.ux.grid.RowActions({
header : '',
@ -44,7 +39,12 @@ tvheadend.iptv = function(adapterId) {
var cm = new Ext.grid.ColumnModel({
defaultSortable: true,
columns : [
enabledColumn,
{
xtype: 'checkcolumn',
header : "Enabled",
dataIndex : 'enabled',
width : 45
},
{
header : "Channel name",
dataIndex : 'channelname',
@ -275,7 +275,7 @@ tvheadend.iptv = function(adapterId) {
stripeRows : true,
title : 'IPTV',
iconCls : 'iptv',
plugins : [ enabledColumn, actions ],
plugins : [ actions ],
store : store,
clicksToEdit : 2,
cm : cm,

View file

@ -169,6 +169,10 @@ tvheadend.status_adapters = function() {
header : "Bandwidth (kb/s)",
dataIndex : 'bw',
renderer: renderBw
},{
width : 50,
header : "Used for",
dataIndex : 'reason'
},{
width : 50,
header : "Bit error rate",

View file

@ -1,13 +1,15 @@
/**
* Datastore for adapters
*/
/*
tvheadend.tvAdapterStore = new Ext.data.JsonStore({
root : 'entries',
id : 'identifier',
fields : [ 'identifier', 'type', 'name', 'path', 'devicename',
'hostconnection', 'currentMux', 'services', 'muxes', 'initialMuxes',
'satConf', 'deliverySystem', 'freqMin', 'freqMax', 'freqStep',
'symrateMin', 'symrateMax', 'signal', 'snr', 'ber', 'unc', 'uncavg', 'bw'],
'symrateMin', 'symrateMax', 'signal', 'snr', 'ber', 'unc', 'uncavg', 'bw', 'reason'],
autoLoad : true,
url : 'tv/adapter'
});
@ -72,10 +74,11 @@ tvheadend.tvadapters = function() {
return panel;
}
*/
/**
*
*/
/*
tvheadend.showTransportDetails = function(data) {
html = '';
@ -107,3 +110,8 @@ tvheadend.showTransportDetails = function(data) {
});
win.show();
}
*/
tvheadend.tvadapters = function() {
return tvheadend.item_browser('/tvadapters', 'TV Adapters');
}

View file

@ -0,0 +1,71 @@
/*!
* Ext JS Library 3.4.0
* Copyright(c) 2006-2011 Sencha Inc.
* licensing@sencha.com
* http://www.sencha.com/license
*/
Ext.ns('Ext.ux.grid');
/**
* @class Ext.ux.grid.CheckColumn
* @extends Ext.grid.Column
* <p>A Column subclass which renders a checkbox in each column cell which toggles the truthiness of the associated data field on click.</p>
* <p><b>Note. As of ExtJS 3.3 this no longer has to be configured as a plugin of the GridPanel.</b></p>
* <p>Example usage:</p>
* <pre><code>
var cm = new Ext.grid.ColumnModel([{
header: 'Foo',
...
},{
xtype: 'checkcolumn',
header: 'Indoor?',
dataIndex: 'indoor',
width: 55
}
]);
// create the grid
var grid = new Ext.grid.EditorGridPanel({
...
colModel: cm,
...
});
* </code></pre>
* In addition to toggling a Boolean value within the record data, this
* class toggles a css class between <tt>'x-grid3-check-col'</tt> and
* <tt>'x-grid3-check-col-on'</tt> to alter the background image used for
* a column.
*/
Ext.ux.grid.CheckColumn = Ext.extend(Ext.grid.Column, {
/**
* @private
* Process and refire events routed from the GridView's processEvent method.
*/
processEvent : function(name, e, grid, rowIndex, colIndex){
if (name == 'mousedown') {
var record = grid.store.getAt(rowIndex);
record.set(this.dataIndex, !record.data[this.dataIndex]);
return false; // Cancel row selection.
} else {
return Ext.grid.ActionColumn.superclass.processEvent.apply(this, arguments);
}
},
renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
return String.format('<div class="x-grid3-check-col{0}">&#160;</div>', v ? '-on' : '');
},
// Deprecate use as a plugin. Remove in 4.0
init: Ext.emptyFn
});
// register ptype. Deprecate. Remove in 4.0
Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn);
// backwards compat. Remove in 4.0
Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn;
// register Column xtype
Ext.grid.Column.types.checkcolumn = Ext.ux.grid.CheckColumn;

View file

@ -621,24 +621,23 @@ http_stream_service(http_connection_t *hc, service_t *service)
/**
* Subscribes to a service and starts the streaming loop
*/
#if ENABLE_LINUXDVB
#if 0//ENABLE_LINUXDVB
static int
http_stream_tdmi(http_connection_t *hc, th_dvb_mux_instance_t *tdmi)
http_stream_tdmi(http_connection_t *hc, dvb_mux_t *dm)
{
th_subscription_t *s;
streaming_queue_t sq;
const char *name;
char addrbuf[50];
streaming_queue_init(&sq, SMT_PACKET);
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
s = dvb_subscription_create_from_tdmi(tdmi, "HTTP", &sq.sq_st,
s = dvb_subscription_create_from_tdmi(dm, "HTTP", &sq.sq_st,
addrbuf,
hc->hc_username,
http_arg_get(&hc->hc_args, "User-Agent"));
name = strdupa(tdmi->tdmi_identifier);
//name = strdupa(dm->dm_id.in_uuid);
pthread_mutex_unlock(&global_lock);
http_stream_run(hc, &sq, name, MC_RAW);
http_stream_run(hc, &sq, "foobar", MC_RAW);
pthread_mutex_lock(&global_lock);
subscription_unsubscribe(s);
@ -731,8 +730,8 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
char *components[2];
channel_t *ch = NULL;
service_t *service = NULL;
#if ENABLE_LINUXDVB
th_dvb_mux_instance_t *tdmi = NULL;
#if 0//ENABLE_LINUXDVB
dvb_mux_t *dm = NULL;
#endif
hc->hc_keep_alive = 0;
@ -757,9 +756,9 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
ch = channel_find_by_name(components[1], 0, 0);
} else if(!strcmp(components[0], "service")) {
service = service_find_by_identifier(components[1]);
#if ENABLE_LINUXDVB
#if 0//ENABLE_LINUXDVB
} else if(!strcmp(components[0], "mux")) {
tdmi = dvb_mux_find_by_identifier(components[1]);
dm = dvb_mux_find_by_identifier(components[1]);
#endif
}
@ -767,9 +766,9 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
return http_stream_channel(hc, ch);
} else if(service != NULL) {
return http_stream_service(hc, service);
#if ENABLE_LINUXDVB
} else if(tdmi != NULL) {
return http_stream_tdmi(hc, tdmi);
#if 0//ENABLE_LINUXDVB
} else if(dm != NULL) {
return http_stream_tdmi(hc, dm);
#endif
} else {
http_error(hc, HTTP_STATUS_BAD_REQUEST);

View file

@ -20,6 +20,7 @@
#define WEBUI_H_
#include "htsmsg.h"
#include "idnode.h"
#include "http.h"
void webui_init(void);
@ -34,12 +35,10 @@ const char* html_escape(char *dst, const char *src, size_t len);
int page_static_file(http_connection_t *hc, const char *remain, void *opaque);
#if ENABLE_LINUXDVB
void extjs_list_dvb_adapters(htsmsg_t *array);
void extjs_start_dvb(void);
#endif
#if ENABLE_V4L
void extjs_list_v4l_adapters(htsmsg_t *array);
void extjs_start_v4l(void);
#endif
@ -48,6 +47,9 @@ void extjs_service_update(htsmsg_t *in);
void extjs_service_delete(htsmsg_t *in);
int extjs_get_idnode(http_connection_t *hc, const char *remain, void *opaque,
idnode_t **(*rootfn)(void));
/**
*
*/