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:
commit
139b654715
68 changed files with 3578 additions and 2878 deletions
8
Makefile
8
Makefile
|
@ -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 \
|
||||
|
|
14
src/capmt.c
14
src/capmt.c
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
394
src/dvb/dvb.h
394
src/dvb/dvb.h
|
@ -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_ */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
184
src/dvb/dvb_fe.c
184
src/dvb/dvb_fe.c
|
@ -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
82
src/dvb/dvb_hardware.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
218
src/dvb/dvb_linux.c
Normal 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
255
src/dvb/dvb_network.c
Normal 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;
|
||||
}
|
|
@ -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 = ®ions_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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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++);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++)
|
||||
|
|
32
src/htsmsg.c
32
src/htsmsg.c
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
24
src/http.c
24
src/http.c
|
@ -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
342
src/idnode.c
Normal 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
53
src/idnode.h
Normal 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);
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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
224
src/prop.c
Normal 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
37
src/prop.h
Normal 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);
|
|
@ -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);
|
||||
|
|
321
src/service.c
321
src/service.c
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
40
src/tvadapters.c
Normal 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
5
src/tvadapters.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "idnode.h"
|
||||
|
||||
idnode_t **tv_adapters_root(void);
|
|
@ -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);
|
||||
|
|
22
src/v4l.c
22
src/v4l.c
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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} '
|
||||
+ '<h3>Currently tuned to:</h3>{currentMux} ({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;
|
||||
}
|
||||
|
|
142
src/webui/static/app/dvb_networks.js
Normal file
142
src/webui/static/app/dvb_networks.js
Normal 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');
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
/**
|
||||
|
|
|
@ -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+'"> </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"> </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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
71
src/webui/static/checkcolumn/CheckColumn.js
Normal file
71
src/webui/static/checkcolumn/CheckColumn.js
Normal 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}"> </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;
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue