diff --git a/Makefile b/Makefile index 00ee4147..0be7119d 100644 --- a/Makefile +++ b/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 \ diff --git a/src/capmt.c b/src/capmt.c index 341abce5..3977efd0 100644 --- a/src/capmt.c +++ b/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; diff --git a/src/dvb/dvb.c b/src/dvb/dvb.c index 9e3775b3..533e9450 100644 --- a/src/dvb/dvb.c +++ b/src/dvb/dvb.c @@ -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(); } diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 127d676f..0f3defbd 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -24,6 +24,7 @@ #include #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_ */ diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 00a53704..b09468e8 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -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); diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 99c431d9..3b858a70 100644 --- a/src/dvb/dvb_fe.c +++ b/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; } diff --git a/src/dvb/dvb_hardware.c b/src/dvb/dvb_hardware.c new file mode 100644 index 00000000..0c8bed61 --- /dev/null +++ b/src/dvb/dvb_hardware.c @@ -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 . + */ + + +#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; +} diff --git a/src/dvb/dvb_input_filtered.c b/src/dvb/dvb_input_filtered.c index a4dddf26..9040dcd7 100644 --- a/src/dvb/dvb_input_filtered.c +++ b/src/dvb/dvb_input_filtered.c @@ -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); } diff --git a/src/dvb/dvb_input_raw.c b/src/dvb/dvb_input_raw.c index 0c3f2a56..7176e0ef 100644 --- a/src/dvb/dvb_input_raw.c +++ b/src/dvb/dvb_input_raw.c @@ -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); diff --git a/src/dvb/dvb_linux.c b/src/dvb/dvb_linux.c new file mode 100644 index 00000000..8727f463 --- /dev/null +++ b/src/dvb/dvb_linux.c @@ -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 . + */ + +#include +#include +#include +#include +#include +#include + +#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); +} + + diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index ce0f1909..e0489044 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -46,8 +46,6 @@ #include "subscriptions.h" #include "epggrab.h" -struct th_dvb_mux_instance_tree dvb_muxes; - static struct strtab muxfestatustab[] = { { "Unknown", TDMI_FE_UNKNOWN }, { "No signal", TDMI_FE_NO_SIGNAL }, @@ -58,22 +56,53 @@ static struct strtab muxfestatustab[] = { { "OK", TDMI_FE_OK }, }; +static idnode_t **dvb_mux_get_childs(struct idnode *self); +static const char *dvb_mux_get_title(struct idnode *self); + +static const idclass_t dvb_mux_class = { + .ic_class = "dvbmux", + .ic_get_title = dvb_mux_get_title, + .ic_get_childs = dvb_mux_get_childs, + .ic_save = (void *)dvb_mux_save, + .ic_properties = (const property_t[]){ + { + "enabled", "Enabled", PT_BOOL, + offsetof(dvb_mux_t, dm_enabled) + }, {}} +}; + /** * */ static void -mux_link_initial(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi) +mux_link_initial(dvb_network_t *dn, dvb_mux_t *dm) { - int was_empty = TAILQ_FIRST(&tda->tda_initial_scan_queue) == NULL; + assert(dm->dm_scan_status == DM_SCAN_DONE); - tdmi->tdmi_scan_queue = &tda->tda_initial_scan_queue; - TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); - - if(was_empty && (tda->tda_mux_current == NULL || - tda->tda_mux_current->tdmi_table_initial == 0)) - gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 0); + dm->dm_scan_status = DM_SCAN_PENDING; + TAILQ_INSERT_TAIL(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link); + dn->dn_initial_scan_num_mux++; + dvb_network_schedule_initial_scan(dn); } + +/** + * + */ +void +dvb_mux_initial_scan_done(dvb_mux_t *dm) +{ + dvb_network_t *dn = dm->dm_dn; + gtimer_disarm(&dm->dm_initial_scan_timeout); + assert(dm->dm_scan_status == DM_SCAN_CURRENT); + dn->dn_initial_scan_num_mux--; + dm->dm_scan_status = DM_SCAN_DONE; + TAILQ_REMOVE(&dn->dn_initial_scan_current_queue, dm, dm_scan_link); + dvb_network_schedule_initial_scan(dn); + dvb_mux_save(dm); // Save to dm_scan_status is persisted +} + + /** * Return a readable status text for the given mux */ @@ -88,28 +117,14 @@ dvb_mux_status(th_dvb_mux_instance_t *tdmi) * */ static int -tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b) -{ - return strcmp(a->tdmi_identifier, b->tdmi_identifier); -} - - -/** - * - */ -static int -tdmi_compare_key(const struct dvb_mux_conf *a, - const struct dvb_mux_conf *b, - const dvb_satconf_t *satconf) +dmc_compare_key(const struct dvb_mux_conf *a, + const struct dvb_mux_conf *b) { int32_t fd = (int32_t)a->dmc_fe_params.frequency - (int32_t)b->dmc_fe_params.frequency; - if (!satconf) - satconf = b->dmc_satconf; fd = labs(fd); return fd < 2000 && - a->dmc_polarisation == b->dmc_polarisation && - a->dmc_satconf == satconf; + a->dmc_polarisation == b->dmc_polarisation; } @@ -117,9 +132,9 @@ tdmi_compare_key(const struct dvb_mux_conf *a, * Return 0 if configuration does not differ. return 1 if it does */ static int -tdmi_compare_conf(int adapter_type, - const struct dvb_mux_conf *a, - const struct dvb_mux_conf *b) +dcm_compare_conf(int adapter_type, + const struct dvb_mux_conf *a, + const struct dvb_mux_conf *b) { switch(adapter_type) { case FE_OFDM: @@ -151,28 +166,29 @@ tdmi_compare_conf(int adapter_type, /** * Create a new mux on the given adapter, return NULL if it already exists */ -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 *source, - 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 *dn, const struct dvb_mux_conf *dmc, + uint16_t onid, uint16_t tsid, const char *network, + const char *source, int enabled, int needscan, + const char *uuid, dvb_satconf_t *satconf, + int create, dvb_mux_t *src) { - th_dvb_mux_instance_t *tdmi, *c; - char buf[200]; + dvb_mux_t *dm; lock_assert(&global_lock); +#if TODO_FIX_THIS if (!satconf) satconf = dmc->dmc_satconf; +#endif /* HACK - we hash/compare based on 2KHz spacing and compare on +/-500Hz */ - LIST_FOREACH(tdmi, &tda->tda_mux_list, tdmi_adapter_hash_link) { - if(tdmi_compare_key(&tdmi->tdmi_conf, dmc, satconf)) + LIST_FOREACH(dm, &dn->dn_muxes, dm_network_link) { + if(dmc_compare_key(&dm->dm_conf, dmc)) break; /* Mux already exist */ } - if(tdmi != NULL) { - + if(dm != NULL) { /* Update stuff ... */ int save = 0; char buf2[1024]; @@ -180,70 +196,61 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, int master = 0; if (!src) master = 1; - else if (src->tdmi_network_id == tdmi->tdmi_network_id) + else if (src->dm_network_id == dm->dm_network_id) master = 1; /* Network ID */ - if(tsid != 0xFFFF && tdmi->tdmi_transport_stream_id != tsid) { - if (tdmi->tdmi_transport_stream_id == 0xFFFF || master) { - tdmi->tdmi_transport_stream_id = tsid; + if(tsid != 0xFFFF && dm->dm_transport_stream_id != tsid) { + if (dm->dm_transport_stream_id == 0xFFFF || master) { + dm->dm_transport_stream_id = tsid; save = 1; } } - if(onid && tdmi->tdmi_network_id != onid) { - if (!tdmi->tdmi_network_id || master) { - tdmi->tdmi_network_id = onid; + if(onid && dm->dm_network_id != onid) { + if (!dm->dm_network_id || master) { + dm->dm_network_id = onid; save = 1; } } - if(network && *network && strcmp(tdmi->tdmi_network ?: "", network)) { - if (!tdmi->tdmi_network || master) { - free(tdmi->tdmi_network); - tdmi->tdmi_network = strdup(network); + if(network && *network && strcmp(dm->dm_network_name ?: "", network)) { + if (!dm->dm_network_name || master) { + free(dm->dm_network_name); + dm->dm_network_name = strdup(network); save = 1; } } /* Tuning Info */ // TODO: same protection here? - if(tdmi->tdmi_adapter->tda_autodiscovery && - tdmi_compare_conf(tda->tda_type, &tdmi->tdmi_conf, dmc)) { + if(dm->dm_dn->dn_autodiscovery && + dcm_compare_conf(dn->dn_fe_type, &dm->dm_conf, dmc)) { #if DVB_API_VERSION >= 5 snprintf(buf2, sizeof(buf2), " ("); - if (tdmi->tdmi_conf.dmc_fe_modulation != dmc->dmc_fe_modulation) + if (dm->dm_conf.dmc_fe_modulation != dmc->dmc_fe_modulation) sprintf(buf2, "%s %s->%s, ", buf2, - dvb_mux_qam2str(tdmi->tdmi_conf.dmc_fe_modulation), + dvb_mux_qam2str(dm->dm_conf.dmc_fe_modulation), dvb_mux_qam2str(dmc->dmc_fe_modulation)); - if (tdmi->tdmi_conf.dmc_fe_delsys != dmc->dmc_fe_delsys) + if (dm->dm_conf.dmc_fe_delsys != dmc->dmc_fe_delsys) sprintf(buf2, "%s %s->%s, ", buf2, - dvb_mux_delsys2str(tdmi->tdmi_conf.dmc_fe_delsys), + dvb_mux_delsys2str(dm->dm_conf.dmc_fe_delsys), dvb_mux_delsys2str(dmc->dmc_fe_delsys)); - if (tdmi->tdmi_conf.dmc_fe_rolloff != dmc->dmc_fe_rolloff) + if (dm->dm_conf.dmc_fe_rolloff != dmc->dmc_fe_rolloff) sprintf(buf2, "%s %s->%s, ", buf2, - dvb_mux_rolloff2str(tdmi->tdmi_conf.dmc_fe_rolloff), + dvb_mux_rolloff2str(dm->dm_conf.dmc_fe_rolloff), dvb_mux_rolloff2str(dmc->dmc_fe_rolloff)); sprintf(buf2, "%s)", buf2); #endif - memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf)); + memcpy(&dm->dm_conf, dmc, sizeof(struct dvb_mux_conf)); save = 1; } - /* HACK - load old transports and remove old mux config */ - if(identifier) { - save = 1; - dvb_service_load(tdmi, identifier); - hts_settings_remove("dvbmuxes/%s/%s", - tda->tda_identifier, identifier); - } - if(save) { - dvb_mux_save(tdmi); - dvb_mux_nicename(buf, sizeof(buf), tdmi); - tvhlog(LOG_DEBUG, "dvb", - "Configuration for mux \"%s\" updated by %s%s", - buf, source, buf2); - dvb_mux_notify(tdmi); + dvb_mux_save(dm); + tvhlog(LOG_DEBUG, "dvb", + "Configuration for mux \"%s\" updated by %s%s", + dvb_mux_nicename(dm), source, buf2); + dvb_mux_notify(dm); } return NULL; @@ -252,150 +259,99 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, if (!create) return NULL; - tdmi = calloc(1, sizeof(th_dvb_mux_instance_t)); + dm = calloc(1, sizeof(dvb_mux_t)); - if(identifier == NULL) { - char qpsktxt[20]; - - if(tda->tda_sat) - snprintf(qpsktxt, sizeof(qpsktxt), "_%s", - dvb_polarisation_to_str(dmc->dmc_polarisation)); - else - qpsktxt[0] = 0; - - snprintf(buf, sizeof(buf), "%s%d%s%s%s", - tda->tda_identifier, dmc->dmc_fe_params.frequency, qpsktxt, - satconf ? "_satconf_" : "", - satconf ? satconf->sc_id : ""); - - tdmi->tdmi_identifier = strdup(buf); - } else { - tdmi->tdmi_identifier = strdup(identifier); - } + dm->dm_network_id = onid; + dm->dm_transport_stream_id = tsid; + dm->dm_network_name = network ? strdup(network) : NULL; + TAILQ_INIT(&dm->dm_epg_grab); + TAILQ_INIT(&dm->dm_table_queue); - c = RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp); + dm->dm_dn = dn; + LIST_INSERT_HEAD(&dn->dn_muxes, dm, dm_network_link); - if(c != NULL) { - /* Global identifier collision, not good, not good at all */ + idnode_insert(&dm->dm_id, uuid, &dvb_mux_class); - tvhlog(LOG_ERR, "dvb", - "Multiple DVB multiplexes with same identifier \"%s\" " - "one is skipped", tdmi->tdmi_identifier); - free(tdmi->tdmi_identifier); - free(tdmi); - return NULL; - } + char identifier[128]; + snprintf(identifier, sizeof(identifier), + "%d%s", dmc->dmc_fe_params.frequency, + dn->dn_fe_type == FE_QPSK ? + dvb_polarisation_to_str(dmc->dmc_polarisation) : ""); + dm->dm_local_identifier = strdup(identifier); - TAILQ_INIT(&tdmi->tdmi_epg_grab); + dm->dm_enabled = enabled; - tdmi->tdmi_enabled = enabled; - - TAILQ_INIT(&tdmi->tdmi_table_queue); - - tdmi->tdmi_network_id = onid; - tdmi->tdmi_transport_stream_id = tsid; - tdmi->tdmi_adapter = tda; - tdmi->tdmi_network = network ? strdup(network) : NULL; - tdmi->tdmi_quality = 100; - - memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf)); - if(satconf) - tdmi->tdmi_conf.dmc_satconf = satconf; - if(tdmi->tdmi_conf.dmc_satconf != NULL) { - LIST_INSERT_HEAD(&tdmi->tdmi_conf.dmc_satconf->sc_tdmis, - tdmi, tdmi_satconf_link); - } - - LIST_INSERT_HEAD(&tda->tda_mux_list, tdmi, tdmi_adapter_hash_link); - LIST_INSERT_HEAD(&tda->tda_muxes, tdmi, tdmi_adapter_link); + memcpy(&dm->dm_conf, dmc, sizeof(struct dvb_mux_conf)); if(source != NULL) { - dvb_mux_nicename(buf, sizeof(buf), tdmi); - tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source); + tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", + dvb_mux_nicename(dm), source); - dvb_mux_save(tdmi); + dvb_mux_save(dm); } - dvb_service_load(tdmi, identifier); - dvb_mux_notify(tdmi); + dvb_service_load(dm); - if(enabled) { - if(initialscan) { - tda->tda_initial_num_mux++; - tdmi->tdmi_table_initial = 1; - mux_link_initial(tda, tdmi); - } else { - dvb_mux_add_to_scan_queue(tdmi); - } + if(enabled && needscan) { + dn->dn_initial_scan_num_mux++; + mux_link_initial(dn, dm); } +#if TODO_FIX_THIS dvb_adapter_notify(tda); +#endif - return tdmi; -} - -/** - * Destroy a DVB mux (it might come back by itself very soon though :) - */ -void -dvb_mux_destroy(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - service_t *t; - - lock_assert(&global_lock); - - hts_settings_remove("dvbmuxes/%s/%s", - tda->tda_identifier, tdmi->tdmi_identifier); - - while((t = LIST_FIRST(&tdmi->tdmi_transports)) != NULL) { - hts_settings_remove("dvbtransports/%s/%s", - t->s_dvb_mux_instance->tdmi_identifier, - t->s_identifier); - service_destroy(t); - } - - dvb_service_notify_by_adapter(tda); - - if(tda->tda_mux_current == tdmi) - dvb_fe_stop(tda->tda_mux_current, 0); - - if(tdmi->tdmi_conf.dmc_satconf != NULL) - LIST_REMOVE(tdmi, tdmi_satconf_link); - - RB_REMOVE(&dvb_muxes, tdmi, tdmi_global_link); - LIST_REMOVE(tdmi, tdmi_adapter_link); - LIST_REMOVE(tdmi, tdmi_adapter_hash_link); - - if(tdmi->tdmi_scan_queue != NULL) - TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); - - if(tdmi->tdmi_table_initial) - tda->tda_initial_num_mux--; - - epggrab_mux_delete(tdmi); - - hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier); - - free(tdmi->tdmi_network); - free(tdmi->tdmi_identifier); - free(tdmi); - - dvb_adapter_notify(tda); + return dm; } /** * */ -th_dvb_mux_instance_t * -dvb_mux_find_by_identifier(const char *identifier) +static void +dvb_tdmi_destroy(th_dvb_mux_instance_t *tdmi) { - th_dvb_mux_instance_t skel; + LIST_REMOVE(tdmi, tdmi_mux_link); + LIST_REMOVE(tdmi, tdmi_adapter_link); + free(tdmi); +} + + +/** + * Destroy a DVB mux (it might come back by itself very soon though :) + */ +void +dvb_mux_destroy(dvb_mux_t *dm) +{ + th_dvb_mux_instance_t *tdmi; + dvb_network_t *dn = dm->dm_dn; lock_assert(&global_lock); - skel.tdmi_identifier = (char *)identifier; - return RB_FIND(&dvb_muxes, &skel, tdmi_global_link, tdmi_global_cmp); + LIST_REMOVE(dm, dm_network_link); + + while((tdmi = LIST_FIRST(&dm->dm_tdmis)) != NULL) + dvb_tdmi_destroy(tdmi); + + switch(dm->dm_scan_status) { + case DM_SCAN_DONE: + break; + case DM_SCAN_CURRENT: + TAILQ_REMOVE(&dn->dn_initial_scan_current_queue, dm, dm_scan_link); + gtimer_disarm(&dm->dm_initial_scan_timeout); + if(0) // Sorry but i can't resist these whenever i get an oppertunity // andoma + case DM_SCAN_PENDING: + TAILQ_REMOVE(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link); + dn->dn_initial_scan_num_mux--; + dvb_network_schedule_initial_scan(dn); + break; + } + + + epggrab_mux_delete(dm); + + free(dm->dm_local_identifier); + idnode_unlink(&dm->dm_id); + free(dm); } @@ -582,29 +538,29 @@ int dvb_mux_str2hier(const char *str) * */ void -dvb_mux_save(th_dvb_mux_instance_t *tdmi) +dvb_mux_save(dvb_mux_t *dm) { - struct dvb_frontend_parameters *f = &tdmi->tdmi_conf.dmc_fe_params; + const dvb_mux_conf_t *dmc = &dm->dm_conf; + const struct dvb_frontend_parameters *f = &dmc->dmc_fe_params; htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_u32(m, "quality", tdmi->tdmi_adapter->tda_qmon ? tdmi->tdmi_quality : 100); - htsmsg_add_u32(m, "enabled", tdmi->tdmi_enabled); - htsmsg_add_str(m, "status", dvb_mux_status(tdmi)); + htsmsg_add_u32(m, "enabled", dm->dm_enabled); + htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id)); - htsmsg_add_u32(m, "transportstreamid", tdmi->tdmi_transport_stream_id); - htsmsg_add_u32(m, "originalnetworkid", tdmi->tdmi_network_id); - if(tdmi->tdmi_network != NULL) - htsmsg_add_str(m, "network", tdmi->tdmi_network); + htsmsg_add_u32(m, "transportstreamid", dm->dm_transport_stream_id); + htsmsg_add_u32(m, "originalnetworkid", dm->dm_network_id); + if(dm->dm_network_name != NULL) + htsmsg_add_str(m, "network", dm->dm_network_name); htsmsg_add_u32(m, "frequency", f->frequency); - htsmsg_add_u32(m, "initialscan", tdmi->tdmi_table_initial); + htsmsg_add_u32(m, "needscan", dm->dm_scan_status != DM_SCAN_DONE); - if(tdmi->tdmi_default_authority) - htsmsg_add_str(m, "default_authority", tdmi->tdmi_default_authority); + if(dm->dm_default_authority) + htsmsg_add_str(m, "default_authority", dm->dm_default_authority); - switch(tdmi->tdmi_adapter->tda_type) { + switch(dm->dm_dn->dn_fe_type) { case FE_OFDM: htsmsg_add_str(m, "bandwidth", val2str(f->u.ofdm.bandwidth, bwtab)); @@ -635,17 +591,17 @@ dvb_mux_save(th_dvb_mux_instance_t *tdmi) val2str(f->u.qpsk.fec_inner, fectab)); htsmsg_add_str(m, "polarisation", - val2str(tdmi->tdmi_conf.dmc_polarisation, poltab)); + val2str(dmc->dmc_polarisation, poltab)); #if DVB_API_VERSION >= 5 htsmsg_add_str(m, "modulation", - val2str(tdmi->tdmi_conf.dmc_fe_modulation, qamtab)); + val2str(dmc->dmc_fe_modulation, qamtab)); htsmsg_add_str(m, "delivery_system", - val2str(tdmi->tdmi_conf.dmc_fe_delsys, delsystab)); + val2str(dmc->dmc_fe_delsys, delsystab)); htsmsg_add_str(m, "rolloff", - val2str(tdmi->tdmi_conf.dmc_fe_rolloff, rollofftab)); + val2str(dmc->dmc_fe_rolloff, rollofftab)); #endif break; @@ -665,34 +621,30 @@ dvb_mux_save(th_dvb_mux_instance_t *tdmi) break; } - if(tdmi->tdmi_conf.dmc_satconf != NULL) - htsmsg_add_str(m, "satconf", tdmi->tdmi_conf.dmc_satconf->sc_id); + hts_settings_save(m, "dvb/networks/%s/muxes/%s/config", + idnode_uuid_as_str(&dm->dm_dn->dn_id), + dm->dm_local_identifier); - hts_settings_save(m, "dvbmuxes/%s/%s", - tdmi->tdmi_adapter->tda_identifier, tdmi->tdmi_identifier); htsmsg_destroy(m); } - /** * */ static const char * -tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) +dvb_mux_create_by_msg(dvb_network_t *dn, htsmsg_t *m, const char *fname) { - th_dvb_mux_instance_t *tdmi; + dvb_mux_t *dm; struct dvb_mux_conf dmc; const char *s; int r; - unsigned int onid, tsid, u32, enabled, initscan; + unsigned int onid, tsid, enabled; memset(&dmc, 0, sizeof(dmc)); - dmc.dmc_fe_params.inversion = INVERSION_AUTO; htsmsg_get_u32(m, "frequency", &dmc.dmc_fe_params.frequency); - - switch(tda->tda_type) { + switch(dn->dn_fe_type) { case FE_OFDM: s = htsmsg_get_str(m, "bandwidth"); if(s == NULL || (r = str2val(s, bwtab)) < 0) @@ -805,78 +757,73 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) if(htsmsg_get_u32(m, "enabled", &enabled)) enabled = 1; - if((s = htsmsg_get_str(m, "satconf")) != NULL) - dmc.dmc_satconf = dvb_satconf_entry_find(tda, s, 0); - else - dmc.dmc_satconf = NULL; - - initscan = htsmsg_get_u32_or_default(m, "initialscan", 1); - if (!initscan && !tda->tda_skip_initialscan) - initscan = 1; - - tdmi = dvb_mux_create(tda, &dmc, - onid, tsid, htsmsg_get_str(m, "network"), NULL, enabled, - initscan, - identifier, NULL, 1, NULL); - if(tdmi != NULL) { - - if((s = htsmsg_get_str(m, "status")) != NULL) - tdmi->tdmi_fe_status = str2val(s, muxfestatustab); - - if(tda->tda_qmon && !htsmsg_get_u32(m, "quality", &u32)) - tdmi->tdmi_quality = u32; + const char *uuid = htsmsg_get_str(m, "uuid"); + dm = dvb_mux_create(dn, &dmc, + onid, tsid, htsmsg_get_str(m, "network"), NULL, enabled, + htsmsg_get_u32_or_default(m, "needscan", 1), + uuid, NULL, 1, NULL); + if(dm != NULL) { if((s = htsmsg_get_str(m, "default_authority"))) - tdmi->tdmi_default_authority = strdup(s); + dm->dm_default_authority = strdup(s); + + if(uuid == NULL) + // If mux didn't have UUID, write it to make sure UUID is stable + dvb_mux_save(dm); } return NULL; } - /** * */ void -dvb_mux_load(th_dvb_adapter_t *tda) +dvb_mux_load(dvb_network_t *dn) { htsmsg_t *l, *c; htsmsg_field_t *f; - if((l = hts_settings_load("dvbmuxes/%s", tda->tda_identifier)) == NULL) + if((l = hts_settings_load_r(1, "dvb/networks/%s/muxes", + idnode_uuid_as_str(&dn->dn_id))) == NULL) return; - + HTSMSG_FOREACH(f, l) { if((c = htsmsg_get_map_by_field(f)) == NULL) continue; - - tdmi_create_by_msg(tda, c, f->hmf_name); + + if((c = htsmsg_get_map(c, "config")) == NULL) + continue; + + dvb_mux_create_by_msg(dn, c, f->hmf_name); } htsmsg_destroy(l); } + /** * */ void -dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname) +dvb_mux_set_networkname(dvb_mux_t *dm, const char *networkname) { htsmsg_t *m; if (!networkname || !*networkname) return; - if (!strcmp(tdmi->tdmi_network ?: "", networkname)) + if (!strcmp(dm->dm_network_name ?: "", networkname)) return; - free(tdmi->tdmi_network); - tdmi->tdmi_network = strdup(networkname); - dvb_mux_save(tdmi); + free(dm->dm_network_name); + dm->dm_network_name = strdup(networkname); + + dvb_mux_save(dm); m = htsmsg_create_map(); - htsmsg_add_str(m, "id", tdmi->tdmi_identifier); - htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); + htsmsg_add_str(m, "id", dm->dm_local_identifier); + htsmsg_add_str(m, "network", dm->dm_network_name ?: ""); notify_by_msg("dvbMux", m); } @@ -885,24 +832,24 @@ dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname) * */ void -dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid, int force) +dvb_mux_set_tsid(dvb_mux_t *dm, uint16_t tsid, int force) { htsmsg_t *m; if (!force) - if (tdmi->tdmi_transport_stream_id != 0xFFFF || tsid == 0xFFFF) + if (dm->dm_transport_stream_id != 0xFFFF || tsid == 0xFFFF) return; - if (tdmi->tdmi_transport_stream_id == tsid) + if (dm->dm_transport_stream_id == tsid) return; - tdmi->tdmi_transport_stream_id = tsid; + dm->dm_transport_stream_id = tsid; - dvb_mux_save(tdmi); + dvb_mux_save(dm); m = htsmsg_create_map(); - htsmsg_add_str(m, "id", tdmi->tdmi_identifier); - htsmsg_add_u32(m, "muxid", tdmi->tdmi_transport_stream_id); + htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id)); + htsmsg_add_u32(m, "muxid", dm->dm_transport_stream_id); notify_by_msg("dvbMux", m); } @@ -910,111 +857,37 @@ dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid, int force) * */ void -dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid, int force) +dvb_mux_set_onid(dvb_mux_t *dm, uint16_t onid, int force) { htsmsg_t *m; if (!force) - if (tdmi->tdmi_network_id != 0 || onid == 0) + if (dm->dm_network_id != 0 || onid == 0) return; - if (tdmi->tdmi_network_id == onid) + if (dm->dm_network_id == onid) return; - tdmi->tdmi_network_id = onid; + dm->dm_network_id = onid; - dvb_mux_save(tdmi); + dvb_mux_save(dm); m = htsmsg_create_map(); - htsmsg_add_str(m, "id", tdmi->tdmi_identifier); - htsmsg_add_u32(m, "onid", tdmi->tdmi_network_id); + htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id)); + htsmsg_add_u32(m, "onid", dm->dm_network_id); notify_by_msg("dvbMux", m); } - -/** - * - */ -static int -tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - - if(tdmi->tdmi_enabled == enabled) - return 0; - - if(tdmi->tdmi_enabled) { - - if(tda->tda_mux_current == tdmi) - dvb_fe_stop(tdmi, 0); - - if(tdmi->tdmi_scan_queue != NULL) { - TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); - tdmi->tdmi_scan_queue = NULL; - } - } - - tdmi->tdmi_enabled = enabled; - - if(enabled) - mux_link_initial(tda, tdmi); - - subscription_reschedule(); - return 1; -} - -/** - * Configurable by user - */ -void -dvb_mux_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) -{ - if (tdmi_set_enable(tdmi, enabled)) - dvb_mux_save(tdmi); -} - - /** * */ static void -dvb_mux_fe_status(char *buf, size_t size, th_dvb_mux_instance_t *tdmi) +dvb_mux_modulation(char *buf, size_t size, const dvb_mux_t *dm) { - switch (tdmi->tdmi_fe_status) { - case TDMI_FE_UNKNOWN: - default: - snprintf(buf, size, "Unknown"); - break; - case TDMI_FE_NO_SIGNAL: - snprintf(buf, size, "No Signal"); - break; - case TDMI_FE_FAINT_SIGNAL: - snprintf(buf, size, "Faint Signal"); - break; - case TDMI_FE_BAD_SIGNAL: - snprintf(buf, size, "Bad Signal"); - break; - case TDMI_FE_CONSTANT_FEC: - snprintf(buf, size, "Constant FEC"); - break; - case TDMI_FE_BURSTY_FEC: - snprintf(buf, size, "Bursty FEC"); - break; - case TDMI_FE_OK: - snprintf(buf, size, "Ok"); - break; - } -} + const dvb_mux_conf_t *dmc = &dm->dm_conf; + const struct dvb_frontend_parameters *f = &dmc->dmc_fe_params; -/** - * - */ -static void -dvb_mux_modulation(char *buf, size_t size, th_dvb_mux_instance_t *tdmi) -{ - struct dvb_frontend_parameters *f = &tdmi->tdmi_conf.dmc_fe_params; - - switch(tdmi->tdmi_adapter->tda_type) { + switch(dm->dm_dn->dn_fe_type) { case FE_OFDM: snprintf(buf, size, "%s, %s, %s-mode", val2str(f->u.ofdm.constellation, qamtab), @@ -1025,8 +898,8 @@ dvb_mux_modulation(char *buf, size_t size, th_dvb_mux_instance_t *tdmi) case FE_QPSK: #if DVB_API_VERSION >= 5 snprintf(buf, size, "%d kBaud, %s, %s", f->u.qpsk.symbol_rate / 1000, - val2str(tdmi->tdmi_conf.dmc_fe_delsys, delsystab), - val2str(tdmi->tdmi_conf.dmc_fe_modulation, qamtab)); + val2str(dmc->dmc_fe_delsys, delsystab), + val2str(dmc->dmc_fe_modulation, qamtab)); #else snprintf(buf, size, "%d kBaud", f->u.qpsk.symbol_rate / 1000); #endif @@ -1052,39 +925,35 @@ dvb_mux_modulation(char *buf, size_t size, th_dvb_mux_instance_t *tdmi) * */ htsmsg_t * -dvb_mux_build_msg(th_dvb_mux_instance_t *tdmi) +dvb_mux_build_msg(dvb_mux_t *dm) { htsmsg_t *m = htsmsg_create_map(); char buf[100]; +#if TODO_FIX_THIS htsmsg_add_str(m, "adapterId", tdmi->tdmi_adapter->tda_identifier); +#endif - htsmsg_add_str(m, "id", tdmi->tdmi_identifier); - htsmsg_add_u32(m, "enabled", tdmi->tdmi_enabled); - htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); + htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&dm->dm_id)); + htsmsg_add_u32(m, "enabled", dm->dm_enabled); + htsmsg_add_str(m, "network", dm->dm_network_name ?: ""); - dvb_mux_nicefreq(buf, sizeof(buf), tdmi); - htsmsg_add_str(m, "freq", buf); + htsmsg_add_str(m, "freq", dvb_mux_nicefreq(dm)); - dvb_mux_modulation(buf, sizeof(buf), tdmi); + dvb_mux_modulation(buf, sizeof(buf), dm); htsmsg_add_str(m, "mod", buf); - dvb_mux_fe_status(buf, sizeof(buf), tdmi); - htsmsg_add_str(m, "fe_status", buf); + const dvb_mux_conf_t *dmc = &dm->dm_conf; - htsmsg_add_str(m, "pol", - dvb_polarisation_to_str_long(tdmi->tdmi_conf.dmc_polarisation)); + htsmsg_add_str(m, "pol", + dvb_polarisation_to_str_long(dmc->dmc_polarisation)); - if(tdmi->tdmi_conf.dmc_satconf != NULL) - htsmsg_add_str(m, "satconf", tdmi->tdmi_conf.dmc_satconf->sc_id); + if(dm->dm_transport_stream_id != 0xffff) + htsmsg_add_u32(m, "muxid", dm->dm_transport_stream_id); - if(tdmi->tdmi_transport_stream_id != 0xffff) - htsmsg_add_u32(m, "muxid", tdmi->tdmi_transport_stream_id); + if(dm->dm_network_id) + htsmsg_add_u32(m, "onid", dm->dm_network_id); - if(tdmi->tdmi_network_id) - htsmsg_add_u32(m, "onid", tdmi->tdmi_network_id); - - htsmsg_add_u32(m, "quality", tdmi->tdmi_quality); return m; } @@ -1093,9 +962,9 @@ dvb_mux_build_msg(th_dvb_mux_instance_t *tdmi) * */ void -dvb_mux_notify(th_dvb_mux_instance_t *tdmi) +dvb_mux_notify(dvb_mux_t *dm) { - notify_by_msg("dvbMux", dvb_mux_build_msg(tdmi)); + // notify_by_msg("dvbMux", dvb_mux_build_msg(tdmi)); } @@ -1103,7 +972,7 @@ dvb_mux_notify(th_dvb_mux_instance_t *tdmi) * */ const char * -dvb_mux_add_by_params(th_dvb_adapter_t *tda, +dvb_mux_add_by_params(dvb_network_t *dn, int freq, int symrate, int bw, @@ -1118,13 +987,12 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, int polarisation, const char *satconf) { - th_dvb_mux_instance_t *tdmi; struct dvb_mux_conf dmc; memset(&dmc, 0, sizeof(dmc)); dmc.dmc_fe_params.inversion = INVERSION_AUTO; - switch(tda->tda_type) { + switch(dn->dn_fe_type) { case FE_OFDM: dmc.dmc_fe_params.frequency = freq * 1000; if(!val2str(bw, bwtab)) @@ -1208,24 +1076,17 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, } - if(satconf != NULL) { - dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0); - if(dmc.dmc_satconf == NULL) - return "Satellite configuration not found"; - } else { - dmc.dmc_satconf = NULL; - } dmc.dmc_polarisation = polarisation; - tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL, 1, NULL); + dvb_mux_t *dm = dvb_mux_create(dn, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL, 1, NULL); - if(tdmi == NULL) + if(dm == NULL) return "Mux already exist"; return NULL; } - +#if 0 /** * */ @@ -1249,7 +1110,7 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, if(tdmi_dst == NULL) return -1; // Already exist - LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, s_group_link) { + LIST_FOREACH(t_src, &tdmi_src->tdmi_mux->dm_services, s_group_link) { t_dst = dvb_service_find(tdmi_dst, t_src->s_dvb_service_id, t_src->s_pmt_pid, NULL); @@ -1303,34 +1164,35 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, dvb_mux_save(tdmi_dst); return 0; } +#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) { - int ti; - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - ti = tdmi->tdmi_quality == 100 ? TDA_SCANQ_OK - : TDA_SCANQ_BAD; - tdmi->tdmi_scan_queue = &tda->tda_scan_queues[ti]; - TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); -} + dvb_mux_t *dm; -th_dvb_mux_instance_t *dvb_mux_find - ( th_dvb_adapter_t *tda, const char *netname, uint16_t onid, uint16_t tsid, - int enabled ) -{ - th_dvb_mux_instance_t *tdmi; - 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; - return tdmi; + if(dn != NULL) { + 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; + return dm; } } else { - TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) - if ((tdmi = dvb_mux_find(tda, netname, onid, tsid, enabled))) - return tdmi; + dvb_network_t *dn; + LIST_FOREACH(dn, &dvb_networks, dn_global_link) + if ((dm = dvb_mux_find(dn, netname, onid, tsid, enabled))) + return dm; } return NULL; } @@ -1348,11 +1210,14 @@ dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, const char *client) { th_subscription_t *s; +#if 0 streaming_message_t *sm; streaming_start_t *ss; int r; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; char buf[100]; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; +#endif s = subscription_create(INT32_MAX, name, st, SUBSCRIPTION_RAW_MPEGTS, NULL, hostname, username, client); @@ -1361,8 +1226,10 @@ dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, s->ths_tdmi = tdmi; LIST_INSERT_HEAD(&tdmi->tdmi_subscriptions, s, ths_tdmi_link); - r = dvb_fe_tune(tdmi, "Full mux subscription"); - + dvb_mux_tune(tdmi->tdmi_mux, "Full mux subscription", 99999); + abort(); +#if 0 +>>>>>>> origin/dvbnetworks pthread_mutex_lock(&tda->tda_delivery_mutex); streaming_target_connect(&tda->tda_streaming_pad, &s->ths_input); @@ -1392,7 +1259,220 @@ dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, } pthread_mutex_unlock(&tda->tda_delivery_mutex); - +#endif notify_reload("subscriptions"); return s; } + + + +/** + * + */ +static const char * +dvb_mux_get_title(struct idnode *self) +{ + return dvb_mux_nicename((dvb_mux_t *)self); +} + +/** + * + */ +static int +svcsortcmp(const void *A, const void *B) +{ + const service_t *a = *(service_t **)A; + const service_t *b = *(service_t **)B; + return (int)a->s_dvb_service_id - (int)b->s_dvb_service_id; +} + + +/** + * + */ +static idnode_t ** +dvb_mux_get_childs(struct idnode *self) +{ + dvb_mux_t *dm = (dvb_mux_t *)self; + service_t *s; + int cnt = 1; + + LIST_FOREACH(s, &dm->dm_services, s_group_link) + cnt++; + + idnode_t **v = malloc(sizeof(idnode_t *) * cnt); + cnt = 0; + LIST_FOREACH(s, &dm->dm_services, s_group_link) + v[cnt++] = (idnode_t *)s; + qsort(v, cnt, sizeof(idnode_t *), svcsortcmp); + v[cnt] = NULL; + return v; +} + + + +/** + * These are created on the fly + */ +void +dvb_create_tdmis(dvb_mux_t *dm) +{ + th_dvb_mux_instance_t *tdmi; + dvb_network_t *dn = dm->dm_dn; + th_dvb_adapter_t *tda; + + LIST_FOREACH(tda, &dn->dn_adapters, tda_network_link) { + + LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) { + if(tdmi->tdmi_adapter != NULL) + break; + } + + if(tdmi == NULL) { + tdmi = calloc(1, sizeof(th_dvb_mux_instance_t)); + tdmi->tdmi_adapter = tda; + tdmi->tdmi_mux = dm; + LIST_INSERT_HEAD(&tda->tda_tdmis, tdmi, tdmi_adapter_link); + LIST_INSERT_HEAD(&dm->dm_tdmis, tdmi, tdmi_mux_link); + } + } +} + + + +/** + * + */ +void +dvb_mux_stop(th_dvb_mux_instance_t *tdmi) +{ + dvb_mux_t *dm = tdmi->tdmi_mux; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + service_t *s; + + assert(dm->dm_current_tdmi == tdmi); + assert(tda->tda_current_tdmi == tdmi); + + lock_assert(&global_lock); + + /* Flush all subscribers */ + while((s = LIST_FIRST(&tda->tda_transports)) != NULL) + service_remove_subscriber(s, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN); + + dvb_table_flush_all(dm); + epggrab_mux_stop(dm, 0); + + if(dm->dm_scan_status == DM_SCAN_CURRENT) { + /* + * If we were currently doing initial scan but lost the adapter + * before finishing, put us back on the pending queue + */ + dvb_network_t *dn = dm->dm_dn; + TAILQ_REMOVE(&dn->dn_initial_scan_current_queue, dm, dm_scan_link); + dm->dm_scan_status = DM_SCAN_PENDING; + TAILQ_INSERT_TAIL(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link); + dvb_network_schedule_initial_scan(dn); + } + + dm->dm_current_tdmi = NULL; + tda->tda_current_tdmi = NULL; +} + + +/** + * + */ +int +tdmi_current_weight(const th_dvb_mux_instance_t *tdmi) +{ + int w = 0; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + const service_t *s; + + + // This max weight of all subscriptions could be kept in the adapter struct + pthread_mutex_lock(&tda->tda_delivery_mutex); + LIST_FOREACH(s, &tda->tda_transports, s_active_link) { + const th_subscription_t *ths; + LIST_FOREACH(ths, &s->s_subscriptions, ths_service_link) + w = MAX(w, ths->ths_weight); + } + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + w = MAX(w, tdmi->tdmi_mux->dm_scan_status == DM_SCAN_CURRENT); + return w; +} + + +/** + * + */ +static void +dvb_mux_initial_scan_timeout(void *aux) +{ + dvb_mux_t *dm = aux; + tvhlog(LOG_DEBUG, "dvb", "Initial scan timed out for \"%s\"", + dvb_mux_nicename(dm)); + + dvb_mux_initial_scan_done(dm); +} + + +/** + * + */ +int +dvb_mux_tune(dvb_mux_t *dm, const char *reason, int weight) +{ + dvb_network_t *dn = dm->dm_dn; + th_dvb_mux_instance_t *tdmi; + int r; + + lock_assert(&global_lock); + + if(dm->dm_current_tdmi == NULL) { + + dvb_create_tdmis(dm); + + while(1) { + // Figure which adapter to use + LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) + if(!tdmi->tdmi_tune_failed && tdmi->tdmi_adapter->tda_current_tdmi == NULL) + break; + + if(tdmi == NULL) { + // None available, need to strike one out + LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) { + if(tdmi->tdmi_tune_failed) + continue; + + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + th_dvb_mux_instance_t *t2 = tda->tda_current_tdmi; + assert(t2 != NULL); + assert(t2 != tdmi); + + if(tdmi_current_weight(t2) < weight) { + break; + } + } + + if(tdmi == NULL) + return SM_CODE_NO_FREE_ADAPTER; + } + + r = dvb_fe_tune_tdmi(tdmi, reason); + if(!r) + break; + } + } + + if(dm->dm_scan_status == DM_SCAN_PENDING) { + TAILQ_REMOVE(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link); + dm->dm_scan_status = DM_SCAN_CURRENT; + TAILQ_INSERT_TAIL(&dn->dn_initial_scan_current_queue, dm, dm_scan_link); + + gtimer_arm(&dm->dm_initial_scan_timeout, dvb_mux_initial_scan_timeout, dm, 10); + } + + return 0; +} diff --git a/src/dvb/dvb_network.c b/src/dvb/dvb_network.c new file mode 100644 index 00000000..74c8a14b --- /dev/null +++ b/src/dvb/dvb_network.c @@ -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 . + */ + +#include + +#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; +} diff --git a/src/dvb/dvb_preconf.c b/src/dvb/dvb_preconf.c index bf9e8e34..f6e2ca23 100644 --- a/src/dvb/dvb_preconf.c +++ b/src/dvb/dvb_preconf.c @@ -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; } } diff --git a/src/dvb/dvb_preconf.h b/src/dvb/dvb_preconf.h index 71dbce28..a3c14da3 100644 --- a/src/dvb/dvb_preconf.h +++ b/src/dvb/dvb_preconf.h @@ -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 */ diff --git a/src/dvb/dvb_satconf.c b/src/dvb/dvb_satconf.c index ba607ef5..b0d609de 100644 --- a/src/dvb/dvb_satconf.c +++ b/src/dvb/dvb_satconf.c @@ -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); diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index f8f8def6..b79d3e52 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -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); -} diff --git a/src/dvb/dvb_support.c b/src/dvb/dvb_support.c index 6d883e00..fc58cfc7 100644 --- a/src/dvb/dvb_support.c +++ b/src/dvb/dvb_support.c @@ -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; } diff --git a/src/dvb/dvb_support.h b/src/dvb/dvb_support.h index e9baf7be..f11f2db3 100644 --- a/src/dvb/dvb_support.h +++ b/src/dvb/dvb_support.h @@ -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); diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index 2cce820b..938d10a6 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -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); - } diff --git a/src/epggrab.h b/src/epggrab.h index d9496f58..82ade5ba 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -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 diff --git a/src/epggrab/module.c b/src/epggrab/module.c index b920eea1..788dd8ab 100644 --- a/src/epggrab/module.c +++ b/src/epggrab/module.c @@ -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 ) { diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index c20c32d5..9c7bbebc 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -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"); } diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index 0d245d3a..e303735a 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -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++); } } diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index 3669664c..60fd3f0e 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -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 } } diff --git a/src/epggrab/private.h b/src/epggrab/private.h index a9625862..7ce58ea1 100644 --- a/src/epggrab/private.h +++ b/src/epggrab/private.h @@ -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 diff --git a/src/filebundle.c b/src/filebundle.c index 295ed00f..ba3190a5 100644 --- a/src/filebundle.c +++ b/src/filebundle.c @@ -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); diff --git a/src/hts_strtab.h b/src/hts_strtab.h index f85f7fa6..38d47bcc 100644 --- a/src/hts_strtab.h +++ b/src/hts_strtab.h @@ -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++) diff --git a/src/htsmsg.c b/src/htsmsg.c index 62307dac..5c91c03d 100644 --- a/src/htsmsg.c +++ b/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; diff --git a/src/htsmsg.h b/src/htsmsg.h index 05beb2cf..19ee2726 100644 --- a/src/htsmsg.h +++ b/src/htsmsg.h @@ -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. diff --git a/src/htsmsg_json.c b/src/htsmsg_json.c index 3fda47c1..19c85a71 100644 --- a/src/htsmsg_json.c +++ b/src/htsmsg_json.c @@ -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 diff --git a/src/http.c b/src/http.c index 98562d74..0c89da94 100644 --- a/src/http.c +++ b/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) { diff --git a/src/idnode.c b/src/idnode.c new file mode 100644 index 00000000..8894affe --- /dev/null +++ b/src/idnode.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include + +#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); +} diff --git a/src/idnode.h b/src/idnode.h new file mode 100644 index 00000000..01d06ead --- /dev/null +++ b/src/idnode.h @@ -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); + + diff --git a/src/iptv_input.c b/src/iptv_input.c index 6c39a866..171fe5ae 100644 --- a/src/iptv_input.c +++ b/src/iptv_input.c @@ -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; diff --git a/src/main.c b/src/main.c index 8e2d7ec8..99a57a7b 100644 --- a/src/main.c +++ b/src/main.c @@ -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 diff --git a/src/prop.c b/src/prop.c new file mode 100644 index 00000000..ac7a92c7 --- /dev/null +++ b/src/prop.c @@ -0,0 +1,224 @@ +#include +#include + +#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; +} diff --git a/src/prop.h b/src/prop.h new file mode 100644 index 00000000..e7bc63e9 --- /dev/null +++ b/src/prop.h @@ -0,0 +1,37 @@ +#pragma once +#include + +#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); diff --git a/src/rawtsinput.c b/src/rawtsinput.c index 134fcad5..905cdcaa 100644 --- a/src/rawtsinput.c +++ b/src/rawtsinput.c @@ -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); diff --git a/src/service.c b/src/service.c index 7851f5c2..8f050311 100644 --- a/src/service.c +++ b/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); diff --git a/src/service.h b/src/service.h index d2f8b03b..474718a7 100644 --- a/src/service.h +++ b/src/service.h @@ -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); diff --git a/src/serviceprobe.c b/src/serviceprobe.c index f542ca33..16ca06c4 100644 --- a/src/serviceprobe.c +++ b/src/serviceprobe.c @@ -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); diff --git a/src/settings.c b/src/settings.c index da46c455..5623155f 100644 --- a/src/settings.c +++ b/src/settings.c @@ -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; +} + /** * */ diff --git a/src/settings.h b/src/settings.h index a1db839c..8d29d54b 100644 --- a/src/settings.h +++ b/src/settings.h @@ -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); diff --git a/src/streaming.c b/src/streaming.c index 2e0cace3..381e91c4 100644 --- a/src/streaming.c +++ b/src/streaming.c @@ -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: diff --git a/src/subscriptions.c b/src/subscriptions.c index 548fa7e9..37f39a7e 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -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 ?: "", si.si_network ?: "", si.si_mux ?: "", si.si_provider ?: "", - si.si_service ?: "", - s->ths_service->s_quality_index(s->ths_service)); + si.si_service ?: ""); 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 ?: "", si.si_network ?: "", si.si_mux ?: "", si.si_provider ?: "", - si.si_service ?: "", - t->s_quality_index(t)); + si.si_service ?: ""); service_source_info_free(&si); subscription_link_service(s, t); notify_reload("subscriptions"); return s; +#endif + abort(); } diff --git a/src/subscriptions.h b/src/subscriptions.h index 28b84af8..e80c955d 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -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; diff --git a/src/tvadapters.c b/src/tvadapters.c new file mode 100644 index 00000000..632cf55d --- /dev/null +++ b/src/tvadapters.c @@ -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 . + */ + +#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; +} diff --git a/src/tvadapters.h b/src/tvadapters.h new file mode 100644 index 00000000..56b70525 --- /dev/null +++ b/src/tvadapters.h @@ -0,0 +1,5 @@ +#pragma once + +#include "idnode.h" + +idnode_t **tv_adapters_root(void); diff --git a/src/tvheadend.h b/src/tvheadend.h index 55cdd517..801186fb 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -27,6 +27,7 @@ #include #include #include +#include #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); diff --git a/src/v4l.c b/src/v4l.c index 1a48fed8..63d9ea4a 100644 --- a/src/v4l.c +++ b/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; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index c7a6dfcb..c25f07f5 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -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(); diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c index 4aefa6b7..16aa0964 100644 --- a/src/webui/extjs_dvb.c +++ b/src/webui/extjs_dvb.c @@ -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 } diff --git a/src/webui/extjs_v4l.c b/src/webui/extjs_v4l.c index f7e02630..10556124 100644 --- a/src/webui/extjs_v4l.c +++ b/src/webui/extjs_v4l.c @@ -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 /** diff --git a/src/webui/statedump.c b/src/webui/statedump.c index 603fd158..8517a6ae 100644 --- a/src/webui/statedump.c +++ b/src/webui/statedump.c @@ -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; diff --git a/src/webui/static/app/acleditor.js b/src/webui/static/app/acleditor.js index f1dee2a6..78c05363 100644 --- a/src/webui/static/app/acleditor.js +++ b/src/webui/static/app/acleditor.js @@ -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 'Hidden'; + }, + 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 'Hidden'; - }, - 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'); } diff --git a/src/webui/static/app/capmteditor.js b/src/webui/static/app/capmteditor.js index 6f0c73ea..a5928647 100644 --- a/src/webui/static/app/capmteditor.js +++ b/src/webui/static/app/capmteditor.js @@ -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'); } diff --git a/src/webui/static/app/cteditor.js b/src/webui/static/app/cteditor.js index 414730ba..7cbda3d4 100644 --- a/src/webui/static/app/cteditor.js +++ b/src/webui/static/app/cteditor.js @@ -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'); } diff --git a/src/webui/static/app/cwceditor.js b/src/webui/static/app/cwceditor.js index 52d82455..1a96ee2d 100644 --- a/src/webui/static/app/cwceditor.js +++ b/src/webui/static/app/cwceditor.js @@ -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); diff --git a/src/webui/static/app/dvb.js b/src/webui/static/app/dvb.js index 4d423d3f..13d75fc4 100644 --- a/src/webui/static/app/dvb.js +++ b/src/webui/static/app/dvb.js @@ -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 'Play' - } - }, { + }, { 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) { + '

Symbolrate range:

' + '{symrateMin} Baud - {symrateMax} Baud' + '

Status

' - + '

Currently tuned to:

{currentMux} ' + + '

Currently tuned to:

{currentMux} ({reason})' + '

Services:

{services}' + '

Muxes:

{muxes}' + '

Muxes awaiting initial scan:

{initialMuxes}' + '

Signal Strength:

{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; } diff --git a/src/webui/static/app/dvb_networks.js b/src/webui/static/app/dvb_networks.js new file mode 100644 index 00000000..dea3b169 --- /dev/null +++ b/src/webui/static/app/dvb_networks.js @@ -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'); +} + diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index 02021eb3..1bce7c7d 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -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'); } /** diff --git a/src/webui/static/app/extensions.js b/src/webui/static/app/extensions.js index 281a98e4..5a14d9f8 100644 --- a/src/webui/static/app/extensions.js +++ b/src/webui/static/app/extensions.js @@ -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 '
 
'; - } -}; - - -/** - * 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 '
 
'; - }, - - 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); - } - } -}); diff --git a/src/webui/static/app/iptv.js b/src/webui/static/app/iptv.js index e1c9a41b..1aab499c 100644 --- a/src/webui/static/app/iptv.js +++ b/src/webui/static/app/iptv.js @@ -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, diff --git a/src/webui/static/app/status.js b/src/webui/static/app/status.js index 5aacae41..2caa8fb7 100644 --- a/src/webui/static/app/status.js +++ b/src/webui/static/app/status.js @@ -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", diff --git a/src/webui/static/app/tvadapters.js b/src/webui/static/app/tvadapters.js index 3661fabd..ef2fcba6 100644 --- a/src/webui/static/app/tvadapters.js +++ b/src/webui/static/app/tvadapters.js @@ -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'); +} diff --git a/src/webui/static/checkcolumn/CheckColumn.js b/src/webui/static/checkcolumn/CheckColumn.js new file mode 100644 index 00000000..8ebf47bf --- /dev/null +++ b/src/webui/static/checkcolumn/CheckColumn.js @@ -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 + *

A Column subclass which renders a checkbox in each column cell which toggles the truthiness of the associated data field on click.

+ *

Note. As of ExtJS 3.3 this no longer has to be configured as a plugin of the GridPanel.

+ *

Example usage:

+ *

+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,
+    ...
+});
+ * 
+ * In addition to toggling a Boolean value within the record data, this + * class toggles a css class between 'x-grid3-check-col' and + * 'x-grid3-check-col-on' 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('
 
', 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; \ No newline at end of file diff --git a/src/webui/webui.c b/src/webui/webui.c index bc7d5989..d97aedec 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -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); diff --git a/src/webui/webui.h b/src/webui/webui.h index 0d50b554..9605c3c5 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -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)); + /** * */