diff --git a/src/dvb/diseqc.c b/src/dvb/diseqc.c index 404c423..1c49ff2 100644 --- a/src/dvb/diseqc.c +++ b/src/dvb/diseqc.c @@ -1,124 +1,104 @@ #include #include #include -//#include "scan.h" +#include "tvheadend.h" #include "diseqc.h" - -struct diseqc_cmd switch_commited_cmds[] = { - { { { 0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf2, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf1, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf3, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf4, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf6, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf5, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf7, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf8, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xfa, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xf9, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xfb, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xfc, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xfe, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xfd, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x38, 0xff, 0x00, 0x00 }, 4 }, 0 } -}; - -struct diseqc_cmd switch_uncommited_cmds[] = { - { { { 0xe0, 0x10, 0x39, 0xf0, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf1, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf2, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf3, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf4, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf5, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf6, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf7, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf8, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xf9, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xfa, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xfb, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xfc, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xfd, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xfe, 0x00, 0x00 }, 4 }, 0 }, - { { { 0xe0, 0x10, 0x39, 0xff, 0x00, 0x00 }, 4 }, 0 } -}; - - /*--------------------------------------------------------------------------*/ -static inline -void msleep(uint32_t msec) +static inline void +msleep(uint32_t msec) { struct timespec req = { msec / 1000, 1000000 * (msec % 1000) }; while (nanosleep(&req, &req)) ; } -int diseqc_send_msg (int fd, fe_sec_voltage_t v, struct diseqc_cmd **cmd, - fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b) +int +diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, + __u8 data_1, __u8 data_2, __u8 data_3, __u8 msg_len) { - int err; + struct dvb_diseqc_master_cmd message; + +#if DISEQC_TRACE + tvhlog(LOG_DEBUG, "diseqc", "sending %X %X %X %X %X %X", + framing_byte, address, cmd, data_1, data_2, data_3); +#endif + + message.msg[0] = framing_byte; + message.msg[1] = address; + message.msg[2] = cmd; + message.msg[3] = data_1; + message.msg[4] = data_2; + message.msg[5] = data_3; + message.msg_len = msg_len; + return ioctl(fe_fd, FE_DISEQC_SEND_MASTER_CMD, &message); +} + +int +diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, + uint32_t version, uint32_t repeats) +{ + int i = (lnb_num % 4) * 4 + voltage * 2 + (band ? 1 : 0); + int j = lnb_num / 4; + int k, err; + +#if DISEQC_TRACE + tvhlog(LOG_DEBUG, "diseqc", + "fe_fd %i, lnb_num %i, voltage %i, band %i, version %i, repeats %i", + fe_fd, lnb_num, voltage, band, version, repeats); +#endif + + /* verify lnb number and diseqc data */ + if(lnb_num < 0 || lnb_num >=64 || i < 0 || i >= 16 || j < 0 || j >= 16) + return -1; - if ((err = ioctl(fd, FE_SET_TONE, SEC_TONE_OFF))) + /* turn off continuous tone */ + if ((err = ioctl(fe_fd, FE_SET_TONE, SEC_TONE_OFF))) return err; - if ((err = ioctl(fd, FE_SET_VOLTAGE, v))) + /* set lnb voltage */ + if ((err = ioctl(fe_fd, FE_SET_VOLTAGE, + (i/2) % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13))) return err; + msleep(15); + if (repeats == 0) { /* uncommited msg, wait 15ms, commited msg */ + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x39, 0xF0 | j, 0, 0, 4))) + return err; msleep(15); - while (*cmd) { - if ((err = ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &(*cmd)->cmd))) + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) + return err; + } else { /* commited msg, 25ms, uncommited msg, 25ms, commited msg, etc */ + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) + return err; + for (k = 0; k < repeats; k++) { + msleep(25); + if ((err = diseqc_send_msg(fe_fd, 0xE0, 0x10, 0x39, 0xF0 | j, 0, 0, 4))) + return err; + msleep(25); + if ((err = diseqc_send_msg(fe_fd, 0xE1, 0x10, 0x38, 0xF0 | i, 0, 0, 4))) return err; - - msleep((*cmd)->wait); - cmd++; } - + } msleep(15); - if ((err = ioctl(fd, FE_DISEQC_SEND_BURST, b))) + /* set toneburst */ + if ((err = ioctl(fe_fd, FE_DISEQC_SEND_BURST, + (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A))) return err; - msleep(15); - return ioctl(fd, FE_SET_TONE, t); + /* set continuous tone */ + if ((err = ioctl(fe_fd, FE_SET_TONE, i % 2 ? SEC_TONE_ON : SEC_TONE_OFF))) + return err; + return 0; } - int -diseqc_setup(int frontend_fd, int switch_pos, int voltage_18, int hiband, - int diseqc_ver) +diseqc_voltage_off(int fe_fd) { - struct diseqc_cmd *cmd[2] = { NULL, NULL }; - int i = 4 * switch_pos + 2 * hiband + (voltage_18 ? 1 : 0); - - if (diseqc_ver == 1) { - if(switch_pos < 0 || switch_pos >= (int) (sizeof(switch_uncommited_cmds)/sizeof(struct diseqc_cmd))) - return -1; - cmd[0] = &switch_uncommited_cmds[switch_pos]; - } else { - if(i < 0 || i >= (int) (sizeof(switch_commited_cmds)/sizeof(struct diseqc_cmd))) - return -1; - cmd[0] = &switch_commited_cmds[i]; - } - - return diseqc_send_msg (frontend_fd, - i % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13, - cmd, - (i/2) % 2 ? SEC_TONE_ON : SEC_TONE_OFF, - (i/4) % 2 ? SEC_MINI_B : SEC_MINI_A); -} - - -int diseqc_voltage_off(int frontend_fd) -{ - fe_sec_voltage_t v = SEC_VOLTAGE_OFF; - int err; - - if ((err = ioctl(frontend_fd, FE_SET_VOLTAGE, v))) - return err; - - return 0; + return ioctl(fe_fd, FE_SET_VOLTAGE, SEC_VOLTAGE_OFF); } diff --git a/src/dvb/diseqc.h b/src/dvb/diseqc.h index 5daeb88..852a794 100644 --- a/src/dvb/diseqc.h +++ b/src/dvb/diseqc.h @@ -2,25 +2,15 @@ #define __DISEQC_H__ #include #include - -struct diseqc_cmd { - struct dvb_diseqc_master_cmd cmd; - uint32_t wait; -}; - - -extern int diseqc_send_msg(int fd, fe_sec_voltage_t v, struct diseqc_cmd **cmd, - fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b); - - /** * set up the switch to position/voltage/tone */ -int diseqc_setup(int frontend_fd, int switch_pos, int voltage_18, int hiband, - int diseqc_ver); - -int diseqc_voltage_off(int frontend_fd); +int diseqc_send_msg(int fe_fd, __u8 framing_byte, __u8 address, __u8 cmd, + __u8 data_1, __u8 data_2, __u8 data_3, __u8 msg_len); +int diseqc_setup(int fe_fd, int lnb_num, int voltage, int band, + uint32_t version, uint32_t repeats); +int diseqc_voltage_off(int fe_fd); #endif diff --git a/src/dvb/dvb.c b/src/dvb/dvb.c index 0b0d8e7..9e3775b 100644 --- a/src/dvb/dvb.c +++ b/src/dvb/dvb.c @@ -18,11 +18,13 @@ #include "tvheadend.h" #include "dvb.h" #include "dvb_support.h" +#include "dvb_charset.h" void -dvb_init(uint32_t adapter_mask) +dvb_init(uint32_t adapter_mask, const char *rawfile) { - dvb_adapter_init(adapter_mask); + dvb_charset_init(); + dvb_adapter_init(adapter_mask, rawfile); } diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index 3d8b065..5ae0d87 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -21,11 +21,15 @@ #include #include #include #include "htsmsg.h" +#include "psi.h" +struct service; +struct th_dvb_table; +struct th_dvb_mux_instance; #define DVB_VER_INT(maj,min) (((maj) << 16) + (min)) #define DVB_VER_ATLEAST(maj, min) \ (DVB_VER_INT(DVB_API_VERSION, DVB_API_VERSION_MINOR) >= DVB_VER_INT(maj, min)) @@ -41,11 +45,11 @@ TAILQ_HEAD(dvb_satconf_queue, dvb_satconf); * Satconf */ typedef struct dvb_satconf { char *sc_id; TAILQ_ENTRY(dvb_satconf) sc_adapter_link; - int sc_port; // diseqc switchport (0 - 15) + int sc_port; // diseqc switchport (0 - 63) char *sc_name; char *sc_comment; char *sc_lnb; @@ -91,21 +95,25 @@ typedef struct th_dvb_mux_instance { LIST_ENTRY(th_dvb_mux_instance) tdmi_adapter_link; LIST_ENTRY(th_dvb_mux_instance) tdmi_adapter_hash_link; struct th_dvb_adapter *tdmi_adapter; - uint16_t tdmi_snr, tdmi_signal; - uint32_t tdmi_ber, tdmi_uncorrected_blocks; + uint16_t tdmi_signal; + uint32_t tdmi_ber, tdmi_unc; + float tdmi_unc_avg; + float tdmi_snr; #define TDMI_FEC_ERR_HISTOGRAM_SIZE 10 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, @@ -127,25 +135,46 @@ typedef struct th_dvb_mux_instance { 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; + struct th_subscription_list tdmi_subscriptions; + } th_dvb_mux_instance_t; + + +/** + * When in raw mode we need to enqueue raw TS packet + * to a different thread because we need to hold + * global_lock when doing delivery of the tables + */ +TAILQ_HEAD(dvb_table_feed_queue, dvb_table_feed); + +typedef struct dvb_table_feed { + TAILQ_ENTRY(dvb_table_feed) dtf_link; + uint8_t dtf_tsb[188]; +} dvb_table_feed_t; + + + /** * DVB Adapter (one of these per physical adapter) */ #define TDA_MUX_HASH_WIDTH 101 @@ -173,30 +202,33 @@ typedef struct th_dvb_adapter { 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; char *tda_displayname; 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; char *tda_demux_path; - char *tda_dvr_path; pthread_t tda_dvr_thread; int tda_dvr_pipe[2]; int tda_hostconnection; @@ -211,24 +243,38 @@ typedef struct th_dvb_adapter { int tda_sat; // Set if this adapter is a satellite receiver (DVB-S, etc) struct dvb_satconf_queue tda_satconfs; - struct th_dvb_mux_instance_list tda_mux_hash[TDA_MUX_HASH_WIDTH]; - - uint32_t tda_dump_muxes; - - int tda_allpids_dmx_fd; - int tda_dump_fd; + 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 * return dela values */ uint32_t tda_extrapriority; // extra priority for choosing the best adapter/service + 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); + + int tda_rawmode; + + // Full mux streaming, protected via the delivery mutex + + streaming_pad_t tda_streaming_pad; + + + struct dvb_table_feed_queue tda_table_feed; + pthread_cond_t tda_table_feed_cond; // Bound to tda_delivery_mutex + + // PIDs that needs to be requeued and processed as tables + uint8_t tda_table_filter[8192]; + + } th_dvb_adapter_t; /** * DVB table */ @@ -251,10 +297,11 @@ typedef struct th_dvb_table { * File descriptor for filter */ int tdt_fd; LIST_ENTRY(th_dvb_table) tdt_link; + th_dvb_mux_instance_t *tdt_tdmi; char *tdt_name; void *tdt_opaque; int (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, @@ -262,26 +309,32 @@ typedef struct th_dvb_table { int tdt_count; int tdt_pid; - struct dmx_sct_filter_params *tdt_fparams; - int tdt_id; + int tdt_table; + int tdt_mask; + + int tdt_destroyed; + int tdt_refcount; + + psi_section_t tdt_sect; // Manual reassembly + } th_dvb_table_t; extern struct th_dvb_adapter_queue dvb_adapters; extern struct th_dvb_mux_instance_tree dvb_muxes; -void dvb_init(uint32_t adapter_mask); +void dvb_init(uint32_t adapter_mask, const char *rawfile); /** * DVB Adapter */ -void dvb_adapter_init(uint32_t adapter_mask); +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); @@ -297,22 +350,27 @@ 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_dump_muxes(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_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); @@ -325,10 +383,16 @@ htsmsg_t *dvb_fe_opts(th_dvb_adapter_t *tda, const char *which); void dvb_adapter_set_extrapriority(th_dvb_adapter_t *tda, int extrapriority); void dvb_adapter_poweroff(th_dvb_adapter_t *tda); +void dvb_input_filtered_setup(th_dvb_adapter_t *tda); + +void dvb_input_raw_setup(th_dvb_adapter_t *tda); + + + /** * DVB Multiplex */ const char* dvb_mux_fec2str(int fec); const char* dvb_mux_delsys2str(int delsys); @@ -348,19 +412,21 @@ void dvb_mux_load(th_dvb_adapter_t *tda); void dvb_mux_destroy(th_dvb_mux_instance_t *tdmi); th_dvb_mux_instance_t *dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, - uint16_t tsid, const char *network, + uint16_t onid, uint16_t tsid, const char *network, const char *logprefix, int enabled, int initialscan, const char *identifier, dvb_satconf_t *satconf); void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *name); void dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid); +void dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid); + 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); @@ -386,28 +452,37 @@ const char *dvb_mux_add_by_params(th_dvb_adapter_t *tda, int dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, dvb_satconf_t *satconf); void dvb_mux_add_to_scan_queue (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_transport_load(th_dvb_mux_instance_t *tdmi); +void dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier); -struct service *dvb_transport_find(th_dvb_mux_instance_t *tdmi, +struct service *dvb_service_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, const char *identifier); -struct service *dvb_transport_find2(th_dvb_mux_instance_t *tdmi, +struct service *dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, const char *identifier, int *save); -void dvb_transport_notify(struct service *t); +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); -void dvb_transport_notify_by_adapter(th_dvb_adapter_t *tda); +void dvb_service_notify(struct service *t); -htsmsg_t *dvb_transport_build_msg(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); @@ -426,23 +501,28 @@ void dvb_table_flush_all(th_dvb_mux_instance_t *tdmi); void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); void dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid); -struct dmx_sct_filter_params *dvb_fparams_alloc(void); - -void -tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, +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, uint8_t tableid, void *opaque), void *opaque, - const char *name, int flags, int pid, th_dvb_table_t *tdt); + 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); #define TDT_CRC 0x1 #define TDT_QUICKREQ 0x2 #define TDT_CA 0x4 #define TDT_TDT 0x8 +void dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt); + +void dvb_table_release(th_dvb_table_t *tdt); + /** * Satellite configuration */ void dvb_satconf_init(th_dvb_adapter_t *tda); @@ -454,6 +534,16 @@ dvb_satconf_t *dvb_satconf_entry_find(th_dvb_adapter_t *tda, const char *id, int create); void dvb_lnb_get_frequencies(const char *id, int *f_low, int *f_hi, int *f_switch); + +/** + * Raw demux + */ +struct th_subscription; +struct th_subscription *dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, + const char *name, + streaming_target_t *st); + #endif /* DVB_H_ */ + diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 9e714e9..8e35dfe 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -14,11 +14,10 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#define _GNU_SOURCE #include #include #include #include @@ -63,14 +62,11 @@ tda_alloc(void) 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); - - tda->tda_allpids_dmx_fd = -1; - tda->tda_dump_fd = -1; - + streaming_pad_init(&tda->tda_streaming_pad); return tda; } /** @@ -85,19 +81,22 @@ tda_save(th_dvb_adapter_t *tda) 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, "dump_muxes", tda->tda_dump_muxes); 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); hts_settings_save(m, "dvbadapters/%s", tda->tda_identifier); htsmsg_destroy(m); } @@ -182,10 +181,29 @@ dvb_adapter_set_idlescan(th_dvb_adapter_t *tda, int on) /** * */ 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; @@ -255,29 +273,10 @@ dvb_adapter_set_poweroff(th_dvb_adapter_t *tda, int on) /** * */ void -dvb_adapter_set_dump_muxes(th_dvb_adapter_t *tda, int on) -{ - if(tda->tda_dump_muxes == on) - return; - - lock_assert(&global_lock); - - tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" dump of DVB mux input set to: %s", - tda->tda_displayname, on ? "On" : "Off"); - - tda->tda_dump_muxes = 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) @@ -313,10 +312,25 @@ dvb_adapter_set_diseqc_version(th_dvb_adapter_t *tda, unsigned int v) 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) { @@ -352,20 +366,82 @@ 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 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); +} + + +/** + * + */ 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) @@ -390,25 +466,26 @@ tda_add(int adapter_num) tda->tda_adapter_num = adapter_num; tda->tda_rootpath = strdup(path); tda->tda_demux_path = malloc(256); snprintf(tda->tda_demux_path, 256, "%s/demux0", path); - tda->tda_dvr_path = malloc(256); - snprintf(tda->tda_dvr_path, 256, "%s/dvr0", path); tda->tda_fe_path = strdup(fname); - tda->tda_fe_fd = -1; tda->tda_dvr_pipe[0] = -1; + tda->tda_full_mux_rx = -1; tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info)); if(ioctl(fe, FE_GET_INFO, tda->tda_fe_info)) { tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter", fname); close(fe); free(tda); return; } + if (tda->tda_idlescan || !tda->tda_idleclose) + tda->tda_fe_fd = fe; + else close(fe); tda->tda_type = tda->tda_fe_info->type; snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath, @@ -431,25 +508,87 @@ tda_add(int adapter_num) tda->tda_displayname = strdup(tda->tda_fe_info->name); dvb_adapter_checkspeed(tda); + + if(!strcmp(tda->tda_fe_info->name, "Sony CXD2820R (DVB-T/T2)")) + tda->tda_snr_valid = 1; + tvhlog(LOG_INFO, "dvb", - "Found adapter %s (%s) via %s", path, tda->tda_fe_info->name, - hostconnection2str(tda->tda_hostconnection)); + "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); + gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1); +} - dvb_table_init(tda); - if(tda->tda_sat) - dvb_satconf_init(tda); - gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1); +/** + * + */ +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[0] = -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 input + */ +static void tda_init_input (th_dvb_adapter_t *tda) +{ + 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); + } +} + + + +/** + * + */ void dvb_adapter_start ( th_dvb_adapter_t *tda ) { /* Open front end */ if (tda->tda_fe_fd == -1) { @@ -458,11 +597,16 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) tvhlog(LOG_DEBUG, "dvb", "%s opened frontend %s", tda->tda_rootpath, tda->tda_fe_path); } /* Start DVR thread */ if (tda->tda_dvr_pipe[0] == -1) { - assert(pipe2(tda->tda_dvr_pipe, O_NONBLOCK | O_CLOEXEC) != -1); + int err = pipe(tda->tda_dvr_pipe); + assert(err != -1); + + fcntl(tda->tda_dvr_pipe[0], F_SETFD, fcntl(tda->tda_dvr_pipe[0], F_GETFD) | FD_CLOEXEC); + fcntl(tda->tda_dvr_pipe[0], F_SETFL, fcntl(tda->tda_dvr_pipe[0], F_GETFL) | O_NONBLOCK); + fcntl(tda->tda_dvr_pipe[1], F_SETFD, fcntl(tda->tda_dvr_pipe[1], F_GETFD) | FD_CLOEXEC); pthread_create(&tda->tda_dvr_thread, NULL, dvb_adapter_input_dvr, tda); tvhlog(LOG_DEBUG, "dvb", "%s started dvr thread", tda->tda_rootpath); } } @@ -470,21 +614,25 @@ void dvb_adapter_stop ( th_dvb_adapter_t *tda ) { /* Poweroff */ dvb_adapter_poweroff(tda); + /* Don't stop/close */ + if (!tda->tda_idleclose) return; + /* Close front end */ if (tda->tda_fe_fd != -1) { tvhlog(LOG_DEBUG, "dvb", "%s closing frontend", tda->tda_rootpath); close(tda->tda_fe_fd); tda->tda_fe_fd = -1; } /* Stop DVR thread */ if (tda->tda_dvr_pipe[0] != -1) { tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); - assert(write(tda->tda_dvr_pipe[1], "", 1) == 1); + int err = write(tda->tda_dvr_pipe[1], "", 1); + assert(err != -1); pthread_join(tda->tda_dvr_thread, NULL); close(tda->tda_dvr_pipe[0]); close(tda->tda_dvr_pipe[1]); tda->tda_dvr_pipe[0] = -1; tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); @@ -493,24 +641,31 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda ) /** * */ void -dvb_adapter_init(uint32_t adapter_mask) +dvb_adapter_init(uint32_t adapter_mask, const char *rawfile) { htsmsg_t *l, *c; htsmsg_field_t *f; const char *name, *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 */ l = hts_settings_load("dvbadapters"); if(l != NULL) { HTSMSG_FOREACH(f, l) { if((c = htsmsg_get_map_by_field(f)) == NULL) continue; @@ -521,11 +676,10 @@ dvb_adapter_init(uint32_t adapter_mask) (type = dvb_str_to_adaptertype(s)) < 0) continue; 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 { @@ -536,26 +690,37 @@ dvb_adapter_init(uint32_t adapter_mask) free(tda->tda_displayname); tda->tda_displayname = strdup(name); 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, "dump_muxes", &tda->tda_dump_muxes); 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_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); } - TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) { + tda_init_input(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 @@ -581,10 +746,14 @@ dvb_adapter_mux_scanner(void *aux) /* 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; } @@ -699,59 +868,149 @@ dvb_adapter_clean(th_dvb_adapter_t *tda) */ static void * dvb_adapter_input_dvr(void *aux) { th_dvb_adapter_t *tda = aux; - int fd, i, r, efd, nfds; + int fd, i, r, c, efd, nfds, dmx = -1; uint8_t tsb[188 * 10]; service_t *t; struct epoll_event ev; + char path[256]; - fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0); + snprintf(path, sizeof(path), "%s/dvr0", tda->tda_rootpath); + + fd = tvh_open(path, O_RDONLY | O_NONBLOCK, 0); if(fd == -1) { - tvhlog(LOG_ALERT, "dvb", "%s: unable to open dvr", tda->tda_dvr_path); + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", path, strerror(errno)); return NULL; } + if(tda->tda_rawmode) { + + // Receive unfiltered raw transport stream + + dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); + if(dmx == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", + tda->tda_demux_path, strerror(errno)); + close(fd); + return NULL; + } + + struct dmx_pes_filter_params dmx_param; + + 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; + + if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param)) { + tvhlog(LOG_ERR, "dvb", + "Unable to configure demuxer \"%s\" for all PIDs -- %s", + tda->tda_demux_path, strerror(errno)); + close(dmx); + close(fd); + return NULL; + } + } + /* Create poll */ efd = epoll_create(2); + memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; ev.data.fd = fd; epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); ev.data.fd = tda->tda_dvr_pipe[0]; epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe[0], &ev); + r = i = 0; while(1){ /* Wait for input */ nfds = epoll_wait(efd, &ev, 1, -1); if (nfds < 1) continue; if (ev.data.fd != fd) break; - r = read(fd, tsb, sizeof(tsb)); + c = read(fd, tsb+r, sizeof(tsb)-r); + if (c < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + else if (errno == EOVERFLOW) { + tvhlog(LOG_WARNING, "dvb", "\"%s\" read() EOVERFLOW", + tda->tda_identifier); + continue; + } else { + // TODO: should we try to recover? + tvhlog(LOG_ERR, "dvb", "\"%s\" read() error %d", + tda->tda_identifier, errno); + break; + } + } + r += c; + + /* not enough data */ + if (r < 188) continue; + + int wakeup_table_feed = 0; // Just wanna wakeup once pthread_mutex_lock(&tda->tda_delivery_mutex); - for(i = 0; i < r; i += 188) { + if(LIST_FIRST(&tda->tda_streaming_pad.sp_targets) != NULL) { + streaming_message_t sm; + pktbuf_t *pb = pktbuf_alloc(tsb, r); + memset(&sm, 0, sizeof(sm)); + sm.sm_type = SMT_MPEGTS; + sm.sm_data = pb; + streaming_pad_deliver(&tda->tda_streaming_pad, &sm); + pktbuf_ref_dec(pb); + } + + /* Process */ + while (r >= 188) { + + /* sync */ + if (tsb[i] == 0x47) { + int pid = (tsb[i+1] & 0x1f) << 8 | tsb[i+2]; + + if(tda->tda_table_filter[pid]) { + if(!(tsb[i+1] & 0x80)) { // Only dispatch to table parser if not error + dvb_table_feed_t *dtf = malloc(sizeof(dvb_table_feed_t)); + memcpy(dtf->dtf_tsb, tsb + i, 188); + TAILQ_INSERT_TAIL(&tda->tda_table_feed, dtf, dtf_link); + wakeup_table_feed = 1; + } + } 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); } - if(tda->tda_dump_fd != -1) { - if(write(tda->tda_dump_fd, tsb, r) != r) { - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to write to mux dump file -- %s", - tda->tda_identifier, strerror(errno)); - close(tda->tda_dump_fd); - tda->tda_dump_fd = -1; + i += 188; + r -= 188; + + /* no sync */ + } else { + tvhlog(LOG_DEBUG, "dvb", "\"%s\" ts sync lost", tda->tda_identifier); + if (ts_resync(tsb, &r, &i)) break; + tvhlog(LOG_DEBUG, "dvb", "\"%s\" ts sync found", tda->tda_identifier); } } + if(wakeup_table_feed) + pthread_cond_signal(&tda->tda_table_feed_cond); + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + /* reset buffer */ + if (r) memmove(tsb, tsb+i, r); + i = 0; } + if(dmx != -1) + close(dmx); close(efd); close(fd); return NULL; } @@ -786,12 +1045,20 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda) 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_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_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); } if(tda->tda_rootpath == NULL) return m; @@ -959,10 +1226,11 @@ dvb_fe_opts(th_dvb_adapter_t *tda, const char *which) * Turn off the adapter */ void 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) return; diseqc_voltage_off(tda->tda_fe_fd); tvhlog(LOG_DEBUG, "dvb", "\"%s\" is off", tda->tda_rootpath); diff --git a/src/dvb/dvb_charset.c b/src/dvb/dvb_charset.c new file mode 100644 index 0000000..85bdb69 --- /dev/null +++ b/src/dvb/dvb_charset.c @@ -0,0 +1,96 @@ +/* + * tvheadend, charset list + * Copyright (C) 2012 Mariusz BiaÅ‚oÅ„czyk + * + * 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 "settings.h" +#include "dvb/dvb_charset.h" + +/* + * Process a file + */ +static void _charset_load_file() +{ + htsmsg_t *l, *e; + htsmsg_field_t *f; + + dvb_charset_t *enc; + const char *charset; + uint32_t tsid, onid, sid; + int i = 0; + + l = hts_settings_load("charset"); + if (l) + { + HTSMSG_FOREACH(f, l) { + if ((e = htsmsg_get_map_by_field(f))) { + tsid = onid = sid = 0; + htsmsg_get_u32(e, "onid", &onid); + htsmsg_get_u32(e, "tsid", &tsid); + htsmsg_get_u32(e, "sid", &sid); + charset = htsmsg_get_str(e, "charset"); + + if (tsid == 0 || onid == 0 || !charset) + continue; + + enc = calloc(1, sizeof(dvb_charset_t)); + if (enc) + { + enc->onid = onid; + enc->tsid = tsid; + enc->sid = sid; + enc->charset = strdup(charset); + LIST_INSERT_HEAD(&dvb_charset_list, enc, link); + i++; + } + } + }; + htsmsg_destroy(l); + }; + + if (i > 0) + tvhlog(LOG_INFO, "charset", "%d entries loaded", i); +} + +/* + * Initialise the charset list + */ +void dvb_charset_init ( void ) +{ + _charset_load_file(); +} + +/* + * Find default charset + */ +const char *dvb_charset_find + ( uint16_t onid, uint16_t tsid, uint16_t sid ) +{ + dvb_charset_t *ret = NULL, *enc; + LIST_FOREACH(enc, &dvb_charset_list, link) { + if (onid == enc->onid && tsid == enc->tsid) { + if (sid == enc->sid) { + ret = enc; + break; + } else if (!enc->sid) { + ret = enc; + } + } + } + return ret ? ret->charset : NULL; +} diff --git a/src/dvb/dvb_charset.h b/src/dvb/dvb_charset.h new file mode 100644 index 0000000..9ea8302 --- /dev/null +++ b/src/dvb/dvb_charset.h @@ -0,0 +1,37 @@ +/* + * tvheadend, dvb charset config + * Copyright (C) 2012 Mariusz BiaÅ‚oÅ„czyk + * + * 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 . + */ + +#ifndef __TVH_DVB_CHARSET_H__ +#define __TVH_DVB_CHARSET_H__ + +typedef struct dvb_charset { + LIST_ENTRY(dvb_charset) link; + uint16_t onid; + uint16_t tsid; + uint16_t sid; + const char *charset; +} dvb_charset_t; + +LIST_HEAD(,dvb_charset) dvb_charset_list; + +void dvb_charset_init ( void ); + +const char *dvb_charset_find + (uint16_t onid, uint16_t tsid, uint16_t sid); + +#endif /* __TVH_DVB_CHARSET_H__ */ diff --git a/src/dvb/dvb_charset_tables.h b/src/dvb/dvb_charset_tables.h index 2d78152..b106ebe 100644 --- a/src/dvb/dvb_charset_tables.h +++ b/src/dvb/dvb_charset_tables.h @@ -225,141 +225,137 @@ static const uint16_t iso6937_lone_accents[16] = { // Code tables for ISO6937 two-byte sequences combining an accent byte. // in the range 0xc1 to 0xcf with a letter in one of the ranges // 0x41 to 0x5a (A-Z) or 0x61 to 0x7a (a-z) static const uint16_t iso6937_multi_byte[16][52] = { - /* 0xC0 */ { - 0x00, + /* 0xC0 */ + { + 0x0000, }, - /* 0xC1 */ { + /* 0xC1 */ + { /* A-Z */ - 0xc0, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00c0, 0x0000, 0x0000, 0x0000, 0x00c8, 0x0000, 0x0000, 0x0000, 0x00cc, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00d2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0xe0, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00e0, 0x0000, 0x0000, 0x0000, 0x00e8, 0x0000, 0x0000, 0x0000, 0x00ec, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00f2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }, /* 0xC2 */ { /* A-Z */ - 0xc1, 0x00, 0x0106, 0x00, 0xc9, 0x00, 0x00, 0x00, 0xcd, 0x00, 0x00, 0x0139, - 0x00, 0x0143, 0xd3, 0x00, 0x00, 0x0154, 0x015a, 0x00, 0xda, 0x00, 0x00, 0x00, - 0xdd, 0x0179, + 0x00c1, 0x0000, 0x0106, 0x0000, 0x00c9, 0x0000, 0x0000, 0x0000, 0x00cd, 0x0000, 0x0000, 0x0139, 0x0000, + 0x0143, 0x00d3, 0x0000, 0x0000, 0x0154, 0x015a, 0x0000, 0x00da, 0x0000, 0x0000, 0x0000, 0x00dd, 0x0179, /* a-z */ - 0xe1, 0x00, 0x0107, 0x00, 0xe9, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x013a, - 0x00, 0x0144, 0xf3, 0x00, 0x00, 0x0155, 0x015b, 0x00, 0xfa, 0x00, 0x00, 0x00, - 0xfd, 0x017a + 0x00e1, 0x0000, 0x0107, 0x0000, 0x00e9, 0x0000, 0x0000, 0x0000, 0x00ed, 0x0000, 0x0000, 0x013a, 0x0000, + 0x0144, 0x00f3, 0x0000, 0x0000, 0x0155, 0x015b, 0x0000, 0x00fa, 0x0000, 0x0000, 0x0000, 0x00fd, 0x017a }, /* 0xC3 */ { /* A-Z */ - 0xc2, 0x00, 0x0108, 0x00, 0xca, 0x00, 0x011c, 0x0124, 0xce, 0x0134, 0x00, - 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x015c, 0x00, 0xdb, 0x00, 0x0174, - 0x00, 0x0176, 0x00, + 0x00c2, 0x0000, 0x0108, 0x0000, 0x00ca, 0x0000, 0x011c, 0x0124, 0x00ce, 0x0134, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00d4, 0x0000, 0x0000, 0x0000, 0x015c, 0x0000, 0x00db, 0x0000, 0x0174, 0x0000, 0x0176, 0x0000, /* a-z */ - 0xe2, 0x00, 0x0109, 0x00, 0xea, 0x00, 0x011d, 0x0125, 0xee, 0x0135, 0x00, - 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x015d, 0x00, 0xfb, 0x00, 0x0175, - 0x00, 0x0177, 0x00 + 0x00e2, 0x0000, 0x0109, 0x0000, 0x00ea, 0x0000, 0x011d, 0x0125, 0x00ee, 0x0135, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00f4, 0x0000, 0x0000, 0x0000, 0x015d, 0x0000, 0x00fb, 0x0000, 0x0175, 0x0000, 0x0177, 0x0000 }, /* 0xC4 */ { /* A-Z */ - 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0128, 0x00, 0x00, 0x00, 0x00, - 0xd1, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0168, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00c3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0128, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00d1, 0x00d5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0168, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0129, 0x00, 0x00, 0x00, 0x00, - 0xf1, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0169, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00e3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0129, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00f1, 0x00f5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0169, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0xC5 */ { /* A-Z */ - 0x0100, 0x00, 0x00, 0x00, 0x0112, 0x00, 0x00, 0x00, 0x012a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x014c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x016a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0100, 0x0000, 0x0000, 0x0000, 0x0112, 0x0000, 0x0000, 0x0000, 0x012a, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x014c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0x0101, 0x00, 0x00, 0x00, 0x0113, 0x00, 0x00, 0x00, 0x012b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x014d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x016b, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x0101, 0x0000, 0x0000, 0x0000, 0x0113, 0x0000, 0x0000, 0x0000, 0x012b, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x014d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0xC6 */ { /* A-Z */ - 0x0102, 0x00, 0x00, 0x00, 0x00, 0x00, 0x011e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x016c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0102, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x011e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0x0103, 0x00, 0x00, 0x00, 0x00, 0x00, 0x011f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x016d, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x0103, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x011f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0xC7 */ { /* A-Z */ - 0x00, 0x010a, 0x00, 0x0116, 0x00, 0x0120, 0x00, 0x0130, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x017b, + 0x0000, 0x0000, 0x010a, 0x0000, 0x0116, 0x0000, 0x0120, 0x0000, 0x0130, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017b, /* a-z */ - 0x00, 0x010b, 0x00, 0x0117, 0x00, 0x0121, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x017c + 0x0000, 0x0000, 0x010b, 0x0000, 0x0117, 0x0000, 0x0121, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017c }, /* 0xC8 */ { /* A-Z */ - 0xc4, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x0178, 0x00, + 0x00c4, 0x0000, 0x0000, 0x0000, 0x00cb, 0x0000, 0x0000, 0x0000, 0x00cf, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00d6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00dc, 0x0000, 0x0000, 0x0000, 0x0178, 0x0000, /* a-z */ - 0xe4, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xff, 0x00 + 0x00e4, 0x0000, 0x0000, 0x0000, 0x00eb, 0x0000, 0x0000, 0x0000, 0x00ef, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00f6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fc, 0x0000, 0x0000, 0x0000, 0x00ff, 0x0000 }, /* 0xC9 */ { - 0x00, + 0x0000, }, /* 0xCA */ { /* A-Z */ - 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x016e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00c5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x016f, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00e5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0xCB */ { /* A-Z */ - 0x00, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x0122, 0x00, 0x00, 0x00, 0x0136, 0x013b, 0x00, - 0x0145, 0x00, 0x00, 0x00, 0x0156, 0x015e, 0x0162, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0000, 0x0000, 0x00c7, 0x0000, 0x0000, 0x0000, 0x0122, 0x0000, 0x0000, 0x0000, 0x0136, 0x013b, 0x0000, + 0x0145, 0x0000, 0x0000, 0x0000, 0x0156, 0x015e, 0x0162, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x0123, 0x00, 0x00, 0x00, 0x0137, 0x013c, 0x00, - 0x0146, 0x00, 0x00, 0x00, 0x0157, 0x015f, 0x0163, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x0000, 0x0000, 0x00e7, 0x0000, 0x0000, 0x0000, 0x0123, 0x0000, 0x0000, 0x0000, 0x0137, 0x013c, 0x0000, + 0x0146, 0x0000, 0x0000, 0x0000, 0x0157, 0x015f, 0x0163, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0xCC */ { - 0x00, + 0x0000, }, /* 0xCD */ { /* A-Z */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0150, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0170, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0150, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0170, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0151, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0171, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0151, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0171, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0xCE */ { /* A-Z */ - 0x0104, 0x00, 0x00, 0x00, 0x0118, 0x00, 0x00, 0x00, 0x012e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0172, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0104, 0x0000, 0x0000, 0x0000, 0x0118, 0x0000, 0x0000, 0x0000, 0x012e, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0172, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* a-z */ - 0x0105, 0x00, 0x00, 0x00, 0x0119, 0x00, 0x00, 0x00, 0x012f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0173, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x0105, 0x0000, 0x0000, 0x0000, 0x0119, 0x0000, 0x0000, 0x0000, 0x012f, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0173, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, /* 0xCF */ { /* A-Z */ - 0x00, 0x00, 0x010c, 0x010e, 0x011a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x013d, - 0x00, 0x0147, 0x00, 0x00, 0x00, 0x0158, 0x0160, 0x0164, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x017d, + 0x0000, 0x0000, 0x010c, 0x010e, 0x011a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x013d, 0x0000, + 0x0147, 0x0000, 0x0000, 0x0000, 0x0158, 0x0160, 0x0164, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017d, /* a-z */ - 0x00, 0x00, 0x010d, 0x010f, 0x011b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x013e, - 0x00, 0x0148, 0x00, 0x00, 0x00, 0x0159, 0x0161, 0x0165, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x017e + 0x0000, 0x0000, 0x010d, 0x010f, 0x011b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x013e, 0x0000, + 0x0148, 0x0000, 0x0000, 0x0000, 0x0159, 0x0161, 0x0165, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017e } }; #endif diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 34a379f..7120ab5 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -37,10 +37,12 @@ #include "dvb.h" #include "dvb_support.h" #include "diseqc.h" #include "notify.h" #include "dvr/dvr.h" +#include "service.h" +#include "streaming.h" #include "epggrab.h" /** * Return uncorrected block (since last read) @@ -86,13 +88,19 @@ dvb_fe_get_unc(th_dvb_adapter_t *tda) static void dvb_fe_monitor(void *aux) { th_dvb_adapter_t *tda = aux; fe_status_t fe_status; - int status, v, update = 0, vv, i, fec, q; + int status, v, vv, i, fec, q; th_dvb_mux_instance_t *tdmi = tda->tda_mux_current; char buf[50]; + signal_status_t sigstat; + streaming_message_t sm; + struct service *t; + + int store = 0; + int notify = 0; gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); if(tdmi == NULL) return; @@ -127,51 +135,75 @@ dvb_fe_monitor(void *aux) if(status == -1) { /* Read FEC counter (delta) */ fec = dvb_fe_get_unc(tda); + if(tdmi->tdmi_unc != fec) { + tdmi->tdmi_unc = fec; + notify = 1; + } + tdmi->tdmi_fec_err_histogram[tdmi->tdmi_fec_err_ptr++] = fec; if(tdmi->tdmi_fec_err_ptr == TDMI_FEC_ERR_HISTOGRAM_SIZE) tdmi->tdmi_fec_err_ptr = 0; v = vv = 0; for(i = 0; i < TDMI_FEC_ERR_HISTOGRAM_SIZE; i++) { if(tdmi->tdmi_fec_err_histogram[i] > DVB_FEC_ERROR_LIMIT) v++; vv += tdmi->tdmi_fec_err_histogram[i]; } - vv = vv / TDMI_FEC_ERR_HISTOGRAM_SIZE; + + float avg = (float)vv / TDMI_FEC_ERR_HISTOGRAM_SIZE; + + if(tdmi->tdmi_unc_avg != avg) { + tdmi->tdmi_unc_avg = avg; + notify = 1; + } if(v == 0) { status = TDMI_FE_OK; } else if(v == 1) { status = TDMI_FE_BURSTY_FEC; } else { status = TDMI_FE_CONSTANT_FEC; } + int v; /* bit error rate */ - if(ioctl(tda->tda_fe_fd, FE_READ_BER, &tdmi->tdmi_ber) == -1) - tdmi->tdmi_ber = -2; + if(ioctl(tda->tda_fe_fd, FE_READ_BER, &v) != -1 && v != tdmi->tdmi_ber) { + tdmi->tdmi_ber = v; + notify = 1; + } /* signal strength */ - if(ioctl(tda->tda_fe_fd, FE_READ_SIGNAL_STRENGTH, &tdmi->tdmi_signal) == -1) - tdmi->tdmi_signal = -2; + if(ioctl(tda->tda_fe_fd, FE_READ_SIGNAL_STRENGTH, &v) != -1 && v != tdmi->tdmi_signal) { + tdmi->tdmi_signal = v; + notify = 1; + } /* signal/noise ratio */ - if(ioctl(tda->tda_fe_fd, FE_READ_SNR, &tdmi->tdmi_snr) == -1) - tdmi->tdmi_snr = -2; + if(tda->tda_snr_valid) { + if(ioctl(tda->tda_fe_fd, FE_READ_SNR, &v) != -1) { + float snr = v / 10.0; + if(tdmi->tdmi_snr != snr) { + tdmi->tdmi_snr = snr; + notify = 1; + } + } + } } 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)); - update = 1; + store = 1; + notify = 1; } if(status != TDMI_FE_UNKNOWN) { if(tda->tda_qmon) { q = tdmi->tdmi_quality + (status - TDMI_FE_OK + 1); @@ -179,23 +211,56 @@ dvb_fe_monitor(void *aux) } else { q = 100; } if(q != tdmi->tdmi_quality) { tdmi->tdmi_quality = q; - update = 1; + store = 1; + notify = 1; } } - if(update) { + if(notify) { htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_u32(m, "quality", tdmi->tdmi_quality); + htsmsg_add_u32(m, "signal", tdmi->tdmi_signal); + + if(tda->tda_snr_valid) + htsmsg_add_dbl(m, "snr", tdmi->tdmi_snr); + htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); + htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); notify_by_msg("dvbMux", m); + 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) + htsmsg_add_dbl(m, "snr", tdmi->tdmi_snr); + htsmsg_add_u32(m, "ber", tdmi->tdmi_ber); + htsmsg_add_u32(m, "unc", tdmi->tdmi_unc); + htsmsg_add_dbl(m, "uncavg", tdmi->tdmi_unc_avg); + notify_by_msg("tvAdapter", m); + } + + if(store) dvb_mux_save(tdmi); + + /* Streaming message */ + sigstat.status_text = dvb_mux_status(tdmi); + sigstat.snr = tdmi->tdmi_snr; + sigstat.signal = tdmi->tdmi_signal; + sigstat.ber = tdmi->tdmi_ber; + 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 @@ -203,23 +268,15 @@ dvb_fe_monitor(void *aux) void dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + lock_assert(&global_lock); + assert(tdmi == tda->tda_mux_current); tda->tda_mux_current = NULL; - if(tda->tda_allpids_dmx_fd != -1) { - close(tda->tda_allpids_dmx_fd); - tda->tda_allpids_dmx_fd = -1; - } - - if(tda->tda_dump_fd != -1) { - close(tda->tda_dump_fd); - tda->tda_dump_fd = -1; - } - if(tdmi->tdmi_table_initial) { tdmi->tdmi_table_initial = 0; tda->tda_initial_num_mux--; dvb_mux_save(tdmi); } @@ -240,83 +297,10 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) gtimer_disarm(&tda->tda_fe_monitor_timer); dvb_adapter_stop(tda); } } - -/** - * Open a dump file which we write the entire mux output to - */ -static void -dvb_adapter_open_dump_file(th_dvb_adapter_t *tda) -{ - struct dmx_pes_filter_params dmx_param; - char fullname[1000]; - char path[500]; - const char *fname = tda->tda_mux_current->tdmi_identifier; - - int fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); - if(fd == -1) - return; - - 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; - - if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to configure demuxer \"%s\" for all PIDs -- %s", - fname, tda->tda_demux_path, - strerror(errno)); - close(fd); - return; - } - - snprintf(path, sizeof(path), "%s/muxdumps", - dvr_config_find_by_name_default("")->dvr_storage); - - if(mkdir(path, 0777) && errno != EEXIST) { - tvhlog(LOG_ERR, "dvb", "\"%s\" unable to create mux dump dir %s -- %s", - fname, path, strerror(errno)); - close(fd); - return; - } - - int attempt = 1; - - while(1) { - struct stat st; - snprintf(fullname, sizeof(fullname), "%s/%s.dump%d.ts", - path, fname, attempt); - - if(stat(fullname, &st) == -1) - break; - - attempt++; - } - - int f = open(fullname, O_CREAT | O_TRUNC | O_WRONLY, 0777); - - if(f == -1) { - tvhlog(LOG_ERR, "dvb", "\"%s\" unable to create mux dump file %s -- %s", - fname, fullname, strerror(errno)); - close(fd); - return; - } - - tvhlog(LOG_WARNING, "dvb", "\"%s\" writing to mux dump file %s", - fname, fullname); - - tda->tda_allpids_dmx_fd = fd; - tda->tda_dump_fd = f; -} - - - #if DVB_API_VERSION >= 5 static int check_frontend (int fe_fd, int dvr, int human_readable) { (void)dvr; fe_status_t status; @@ -450,40 +434,52 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if(tda->tda_type == FE_QPSK) { /* DVB-S */ dvb_satconf_t *sc; - int port, lowfreq, hifreq, switchfreq, hiband, pol; + int port, lowfreq, hifreq, switchfreq, hiband, pol, dbsbs; lowfreq = 9750000; hifreq = 10600000; switchfreq = 11700000; port = 0; + dbsbs = 0; if((sc = tdmi->tdmi_conf.dmc_satconf) != NULL) { port = sc->sc_port; if(sc->sc_lnb != NULL) dvb_lnb_get_frequencies(sc->sc_lnb, &lowfreq, &hifreq, &switchfreq); + if(!strcmp(sc->sc_id ?: "", "DBS Bandstacked")) + dbsbs = 1; } + if(dbsbs) { + hiband = 0; + if(tdmi->tdmi_conf.dmc_polarisation == POLARISATION_HORIZONTAL || + tdmi->tdmi_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; - if ((r = diseqc_setup(tda->tda_fe_fd, - port, - pol == POLARISATION_HORIZONTAL || - pol == POLARISATION_CIRCULAR_LEFT, - hiband, tda->tda_diseqc_version)) != 0) - tvhlog(LOG_ERR, "dvb", "diseqc setup failed %d\n", r); - if(hiband) p->frequency = abs(p->frequency - hifreq); else p->frequency = abs(p->frequency - lowfreq); } + if ((r = diseqc_setup(tda->tda_fe_fd, port, + pol == POLARISATION_HORIZONTAL || + pol == POLARISATION_CIRCULAR_LEFT, + hiband, tda->tda_diseqc_version, + tda->tda_diseqc_repeats)) != 0) + tvhlog(LOG_ERR, "dvb", "diseqc setup failed %d\n", r); + } + dvb_mux_nicename(buf, sizeof(buf), tdmi); tda->tda_fe_monitor_hold = 4; @@ -518,13 +514,10 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) return SM_CODE_TUNING_FAILED; } tda->tda_mux_current = tdmi; - if(tda->tda_dump_muxes) - dvb_adapter_open_dump_file(tda); - gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1); dvb_table_add_default(tdmi); epggrab_mux_start(tdmi); diff --git a/src/dvb/dvb_input_filtered.c b/src/dvb/dvb_input_filtered.c new file mode 100644 index 0000000..a4dddf2 --- /dev/null +++ b/src/dvb/dvb_input_filtered.c @@ -0,0 +1,259 @@ +/* + * TV Input - Linux DVB interface + * Copyright (C) 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 . + */ + +/** + * DVB input using hardware filters + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "dvb.h" +#include "service.h" + +/** + * Install filters for a service + * + * global_lock must be held + */ +static void +open_service(th_dvb_adapter_t *tda, service_t *s) +{ + struct dmx_pes_filter_params dmx_param; + int fd; + elementary_stream_t *st; + + TAILQ_FOREACH(st, &s->s_components, es_link) { + if(st->es_pid >= 0x2000) + continue; + + if(st->es_demuxer_fd != -1) + continue; + + fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); + st->es_cc_valid = 0; + + if(fd == -1) { + 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, + st->es_pid, strerror(errno)); + continue; + } + + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = st->es_pid; + 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; + + 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, + st->es_pid, strerror(errno)); + close(fd); + fd = -1; + } + + st->es_demuxer_fd = fd; + } +} + + +/** + * Remove filters for a service + * + * global_lock must be held + */ +static void +close_service(th_dvb_adapter_t *tda, service_t *s) +{ + elementary_stream_t *es; + + TAILQ_FOREACH(es, &s->s_components, es_link) { + if(es->es_demuxer_fd != -1) { + close(es->es_demuxer_fd); + es->es_demuxer_fd = -1; + } + } +} + + + + +/** + * + */ +static void +open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + struct epoll_event e; + static int tdt_id_tally; + + tdt->tdt_fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); + + if(tdt->tdt_fd != -1) { + + tdt->tdt_id = ++tdt_id_tally; + + e.events = EPOLLIN; + e.data.u64 = ((uint64_t)tdt->tdt_fd << 32) | tdt->tdt_id; + + if(epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, tdt->tdt_fd, &e)) { + close(tdt->tdt_fd); + tdt->tdt_fd = -1; + } else { + + struct dmx_sct_filter_params fp = {0}; + + fp.filter.filter[0] = tdt->tdt_table; + fp.filter.mask[0] = tdt->tdt_mask; + + if(tdt->tdt_flags & TDT_CRC) + fp.flags |= DMX_CHECK_CRC; + fp.flags |= DMX_IMMEDIATE_START; + fp.pid = tdt->tdt_pid; + + if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, &fp)) { + close(tdt->tdt_fd); + tdt->tdt_fd = -1; + } + } + } + + if(tdt->tdt_fd == -1) + TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); +} + + +/** + * 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) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + + assert(tdt->tdt_fd != -1); + + epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); + close(tdt->tdt_fd); + + tdt->tdt_fd = -1; + TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); +} + + + +/** + * + */ +static void * +dvb_table_input(void *aux) +{ + th_dvb_adapter_t *tda = aux; + int r, i, tid, fd, x; + struct epoll_event ev[1]; + uint8_t sec[4096]; + th_dvb_mux_instance_t *tdmi; + th_dvb_table_t *tdt; + int64_t cycle_barrier = 0; + + while(1) { + x = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1); + + for(i = 0; i < x; i++) { + + tid = ev[i].data.u64 & 0xffffffff; + fd = ev[i].data.u64 >> 32; + + if(!(ev[i].events & EPOLLIN)) + continue; + + if((r = read(fd, sec, sizeof(sec))) < 3) + continue; + + pthread_mutex_lock(&global_lock); + if((tdmi = tda->tda_mux_current) != NULL) { + LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) + if(tdt->tdt_id == tid) + break; + + if(tdt != NULL) { + 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 && + cycle_barrier < getmonoclock()) { + tdt_close_fd(tdmi, tdt); + cycle_barrier = getmonoclock() + 100000; + tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue); + assert(tdt != NULL); + TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); + + open_table(tdmi, tdt); + } + } + } + pthread_mutex_unlock(&global_lock); + } + } + return NULL; +} + + +static void +close_table(th_dvb_mux_instance_t *tdmi, 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); + } else { + epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); + close(tdt->tdt_fd); + } +} + +/** + * + */ +void +dvb_input_filtered_setup(th_dvb_adapter_t *tda) +{ + tda->tda_open_service = open_service; + tda->tda_close_service = close_service; + tda->tda_open_table = open_table; + tda->tda_close_table = close_table; + + pthread_t ptid; + tda->tda_table_epollfd = epoll_create(50); + pthread_create(&ptid, NULL, dvb_table_input, tda); +} + diff --git a/src/dvb/dvb_input_raw.c b/src/dvb/dvb_input_raw.c new file mode 100644 index 0000000..0c3f2a5 --- /dev/null +++ b/src/dvb/dvb_input_raw.c @@ -0,0 +1,176 @@ +/* + * TV Input - Linux DVB interface + * Copyright (C) 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 . + */ + +/** + * DVB input from a raw transport stream + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tvheadend.h" +#include "dvb.h" +#include "service.h" + +/** + * Install filters for a service + * + * global_lock must be held + */ +static void +open_service(th_dvb_adapter_t *tda, service_t *s) +{ + // NOP -- We receive all PIDs anyway +} + + +/** + * Remove filters for a service + * + * global_lock must be held + */ +static void +close_service(th_dvb_adapter_t *tda, service_t *s) +{ + // NOP -- open_service() is a NOP +} + + +/** + * + */ +static void +open_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + assert(tdt->tdt_pid < 0x2000); + tda->tda_table_filter[tdt->tdt_pid] = 1; +} + + +/** + * + */ +static void +close_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + assert(tdt->tdt_pid < 0x2000); + tda->tda_table_filter[tdt->tdt_pid] = 0; +} + + +/** + * + */ +static void +got_section(const uint8_t *data, size_t len, void *opaque) +{ + th_dvb_table_t *tdt = opaque; + dvb_table_dispatch((uint8_t *)data, len, tdt); +} + + + +/** + * All the tables can be destroyed from any of the callbacks + * 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) +{ + int pid = (dtf->dtf_tsb[1] & 0x1f) << 8 | dtf->dtf_tsb[2]; + th_dvb_table_t *vec[tdmi->tdmi_num_tables], *tdt; + int i = 0; + LIST_FOREACH(tdt, &tdmi->tdmi_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 + for(i = 0; i < len; i++) { + tdt = vec[i]; + if(!tdt->tdt_destroyed) { + if(tdt->tdt_pid == pid) + psi_section_reassemble(&tdt->tdt_sect, dtf->dtf_tsb, + 0, got_section, tdt); + } + dvb_table_release(tdt); + } +} + +/** + * + */ +static void * +dvb_table_input(void *aux) +{ + th_dvb_adapter_t *tda = aux; + th_dvb_mux_instance_t *tdmi; + dvb_table_feed_t *dtf; + + while(1) { + + pthread_mutex_lock(&tda->tda_delivery_mutex); + + while((dtf = TAILQ_FIRST(&tda->tda_table_feed)) == NULL) + pthread_cond_wait(&tda->tda_table_feed_cond, &tda->tda_delivery_mutex); + TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link); + + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + pthread_mutex_lock(&global_lock); + + if((tdmi = tda->tda_mux_current) != NULL) + dvb_table_raw_dispatch(tdmi, dtf); + + pthread_mutex_unlock(&global_lock); + free(dtf); + } + return NULL; +} + + +/** + * + */ +void +dvb_input_raw_setup(th_dvb_adapter_t *tda) +{ + tda->tda_rawmode = 1; + tda->tda_open_service = open_service; + tda->tda_close_service = close_service; + tda->tda_open_table = open_table; + tda->tda_close_table = close_table; + + TAILQ_INIT(&tda->tda_table_feed); + pthread_cond_init(&tda->tda_table_feed_cond, NULL); + + pthread_t ptid; + pthread_create(&ptid, NULL, dvb_table_input, tda); + +} + diff --git a/src/dvb/dvb_multiplex.c b/src/dvb/dvb_multiplex.c index a3d7747..1ac5caf 100644 --- a/src/dvb/dvb_multiplex.c +++ b/src/dvb/dvb_multiplex.c @@ -102,11 +102,14 @@ tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b) */ static int tdmi_compare_key(const struct dvb_mux_conf *a, const struct dvb_mux_conf *b) { - return a->dmc_fe_params.frequency == b->dmc_fe_params.frequency && + int32_t fd = (int32_t)a->dmc_fe_params.frequency + - (int32_t)b->dmc_fe_params.frequency; + fd = labs(fd); + return fd < 2000 && a->dmc_polarisation == b->dmc_polarisation && a->dmc_satconf == b->dmc_satconf; } @@ -148,32 +151,30 @@ 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 tsid, const char *network, const char *source, + uint16_t onid, uint16_t tsid, const char *network, const char *source, int enabled, int initialscan, const char *identifier, dvb_satconf_t *satconf) { th_dvb_mux_instance_t *tdmi, *c; - unsigned int hash; char buf[200]; lock_assert(&global_lock); - hash = (dmc->dmc_fe_params.frequency + - dmc->dmc_polarisation) % TDA_MUX_HASH_WIDTH; - - LIST_FOREACH(tdmi, &tda->tda_mux_hash[hash], tdmi_adapter_hash_link) { + /* 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)) break; /* Mux already exist */ } if(tdmi != NULL) { /* Update stuff ... */ int save = 0; char buf2[1024]; + buf2[0] = 0; if(tdmi_compare_conf(tda->tda_type, &tdmi->tdmi_conf, dmc)) { #if DVB_API_VERSION >= 5 snprintf(buf2, sizeof(buf2), " ("); if (tdmi->tdmi_conf.dmc_fe_modulation != dmc->dmc_fe_modulation) @@ -187,22 +188,32 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, if (tdmi->tdmi_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(dmc->dmc_fe_rolloff)); sprintf(buf2, "%s)", buf2); -#else - buf2[0] = 0; #endif memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf)); save = 1; } - if(tdmi->tdmi_transport_stream_id != tsid) { + if(tsid != 0xFFFF && tdmi->tdmi_transport_stream_id != tsid) { tdmi->tdmi_transport_stream_id = tsid; save = 1; } + if(onid && tdmi->tdmi_network_id != onid) { + tdmi->tdmi_network_id = onid; + 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", @@ -253,10 +264,11 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, 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; @@ -266,31 +278,33 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc, 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_hash[hash], tdmi, tdmi_adapter_hash_link); + LIST_INSERT_HEAD(&tda->tda_mux_list, tdmi, tdmi_adapter_hash_link); LIST_INSERT_HEAD(&tda->tda_muxes, tdmi, tdmi_adapter_link); if(source != NULL) { dvb_mux_nicename(buf, sizeof(buf), tdmi); tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source); dvb_mux_save(tdmi); dvb_adapter_notify(tda); } - dvb_transport_load(tdmi); + dvb_service_load(tdmi, identifier); dvb_mux_notify(tdmi); - if(enabled && initialscan) { + 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); } + } return tdmi; } /** @@ -312,11 +326,11 @@ dvb_mux_destroy(th_dvb_mux_instance_t *tdmi) t->s_dvb_mux_instance->tdmi_identifier, t->s_identifier); service_destroy(t); } - dvb_transport_notify_by_adapter(tda); + 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) @@ -546,22 +560,26 @@ dvb_mux_save(th_dvb_mux_instance_t *tdmi) { struct dvb_frontend_parameters *f = &tdmi->tdmi_conf.dmc_fe_params; htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_u32(m, "quality", tdmi->tdmi_quality); + 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, "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, "frequency", f->frequency); htsmsg_add_u32(m, "initialscan", tdmi->tdmi_table_initial); + if(tdmi->tdmi_default_authority) + htsmsg_add_str(m, "default_authority", tdmi->tdmi_default_authority); + switch(tdmi->tdmi_adapter->tda_type) { case FE_OFDM: htsmsg_add_str(m, "bandwidth", val2str(f->u.ofdm.bandwidth, bwtab)); @@ -638,11 +656,11 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) { th_dvb_mux_instance_t *tdmi; struct dvb_mux_conf dmc; const char *s; int r; - unsigned int tsid, u32, enabled, initscan; + unsigned int onid, tsid, u32, enabled, initscan; memset(&dmc, 0, sizeof(dmc)); dmc.dmc_fe_params.inversion = INVERSION_AUTO; htsmsg_get_u32(m, "frequency", &dmc.dmc_fe_params.frequency); @@ -753,10 +771,12 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) break; } if(htsmsg_get_u32(m, "transportstreamid", &tsid)) tsid = 0xffff; + if(htsmsg_get_u32(m, "originalnetworkid", &onid)) + onid = 0; if(htsmsg_get_u32(m, "enabled", &enabled)) enabled = 1; if((s = htsmsg_get_str(m, "satconf")) != NULL) @@ -767,20 +787,23 @@ tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) initscan = htsmsg_get_u32_or_default(m, "initialscan", 1); if (!initscan && !tda->tda_skip_initialscan) initscan = 1; tdmi = dvb_mux_create(tda, &dmc, - tsid, htsmsg_get_str(m, "network"), NULL, enabled, + onid, tsid, htsmsg_get_str(m, "network"), NULL, enabled, initscan, identifier, NULL); if(tdmi != NULL) { if((s = htsmsg_get_str(m, "status")) != NULL) tdmi->tdmi_fe_status = str2val(s, muxfestatustab); - if(!htsmsg_get_u32(m, "quality", &u32)) + if(tda->tda_qmon && !htsmsg_get_u32(m, "quality", &u32)) tdmi->tdmi_quality = u32; + + if((s = htsmsg_get_str(m, "default_authority"))) + tdmi->tdmi_default_authority = strdup(s); } return NULL; } @@ -842,10 +865,28 @@ dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid) htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_u32(m, "muxid", tdmi->tdmi_transport_stream_id); notify_by_msg("dvbMux", m); } +/** + * + */ +void +dvb_mux_set_onid(th_dvb_mux_instance_t *tdmi, uint16_t onid) +{ + htsmsg_t *m; + + tdmi->tdmi_network_id = onid; + + dvb_mux_save(tdmi); + + m = htsmsg_create_map(); + htsmsg_add_str(m, "id", tdmi->tdmi_identifier); + htsmsg_add_u32(m, "onid", tdmi->tdmi_network_id); + notify_by_msg("dvbMux", m); +} + /** * */ static void @@ -856,10 +897,13 @@ tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) if(tdmi->tdmi_enabled == enabled) return; 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; } } @@ -986,10 +1030,13 @@ dvb_mux_build_msg(th_dvb_mux_instance_t *tdmi) htsmsg_add_str(m, "satconf", tdmi->tdmi_conf.dmc_satconf->sc_id); if(tdmi->tdmi_transport_stream_id != 0xffff) htsmsg_add_u32(m, "muxid", tdmi->tdmi_transport_stream_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; } @@ -1119,11 +1166,11 @@ dvb_mux_add_by_params(th_dvb_adapter_t *tda, } else { dmc.dmc_satconf = NULL; } dmc.dmc_polarisation = polarisation; - tdmi = dvb_mux_create(tda, &dmc, 0xffff, NULL, NULL, 1, 1, NULL, NULL); + tdmi = dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, NULL, 1, 1, NULL, NULL); if(tdmi == NULL) return "Mux already exist"; return NULL; @@ -1142,20 +1189,21 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, elementary_stream_t *st_src, *st_dst; caid_t *caid_src, *caid_dst; tdmi_dst = dvb_mux_create(dst, &tdmi_src->tdmi_conf, + tdmi_src->tdmi_network_id, tdmi_src->tdmi_transport_stream_id, tdmi_src->tdmi_network, "copy operation", tdmi_src->tdmi_enabled, 1, NULL, satconf); if(tdmi_dst == NULL) return -1; // Already exist LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, s_group_link) { - t_dst = dvb_transport_find(tdmi_dst, + t_dst = dvb_service_find(tdmi_dst, t_src->s_dvb_service_id, t_src->s_pmt_pid, NULL); t_dst->s_pcr_pid = t_src->s_pcr_pid; t_dst->s_enabled = t_src->s_enabled; @@ -1166,12 +1214,12 @@ dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src, t_dst->s_provider = strdup(t_src->s_provider); if(t_src->s_svcname != NULL) t_dst->s_svcname = strdup(t_src->s_svcname); - if(t_src->s_dvb_default_charset != NULL) - t_dst->s_dvb_default_charset = strdup(t_src->s_dvb_default_charset); + if(t_src->s_dvb_charset != NULL) + t_dst->s_dvb_charset = strdup(t_src->s_dvb_charset); if(t_src->s_ch != NULL) service_map_channel(t_dst, t_src->s_ch, 0); pthread_mutex_lock(&t_src->s_stream_mutex); @@ -1214,5 +1262,55 @@ void dvb_mux_add_to_scan_queue ( th_dvb_mux_instance_t *tdmi ) 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); } + +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; + } + } else { + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) + if ((tdmi = dvb_mux_find(tda, netname, onid, tsid, enabled))) + return tdmi; + } + return NULL; +} + + +/** + * + */ +th_subscription_t * +dvb_subscription_create_from_tdmi(th_dvb_mux_instance_t *tdmi, + const char *name, + streaming_target_t *st) +{ + th_subscription_t *s; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + + s = subscription_create(INT32_MAX, name, st, SUBSCRIPTION_RAW_MPEGTS, + NULL, NULL, NULL, NULL); + + + s->ths_tdmi = tdmi; + LIST_INSERT_HEAD(&tdmi->tdmi_subscriptions, s, ths_tdmi_link); + + dvb_fe_tune(tdmi, "Full mux subscription"); + + pthread_mutex_lock(&tda->tda_delivery_mutex); + streaming_target_connect(&tda->tda_streaming_pad, &s->ths_input); + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + notify_reload("subscriptions"); + return s; +} diff --git a/src/dvb/dvb_preconf.c b/src/dvb/dvb_preconf.c index 5219a15..755340d 100644 --- a/src/dvb/dvb_preconf.c +++ b/src/dvb/dvb_preconf.c @@ -96,11 +96,11 @@ dvb_mux_preconf_add(th_dvb_adapter_t *tda, const network_t *net, break; } dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0); - dvb_mux_create(tda, &dmc, 0xffff, NULL, source, 1, 1, NULL, NULL); + dvb_mux_create(tda, &dmc, 0, 0xffff, NULL, source, 1, 1, NULL, NULL); } } /** diff --git a/src/dvb/dvb_satconf.c b/src/dvb/dvb_satconf.c index 6a41e35..ba607ef 100644 --- a/src/dvb/dvb_satconf.c +++ b/src/dvb/dvb_satconf.c @@ -259,11 +259,11 @@ dvb_satconf_list(th_dvb_adapter_t *tda) htsmsg_t *m; TAILQ_FOREACH(sc, &tda->tda_satconfs, sc_adapter_link) { m = htsmsg_create_map(); htsmsg_add_str(m, "identifier", sc->sc_id); - htsmsg_add_str(m, "name", sc->sc_name); + htsmsg_add_str(m, "name", sc->sc_name ?: ""); htsmsg_add_msg(array, NULL, m); } return array; } @@ -288,15 +288,17 @@ dvb_lnblist_get(void) { htsmsg_t *array = htsmsg_create_list(); add_to_lnblist(array, "Universal"); add_to_lnblist(array, "DBS"); + add_to_lnblist(array, "DBS Bandstacked"); add_to_lnblist(array, "Standard"); add_to_lnblist(array, "Enhanced"); add_to_lnblist(array, "C-Band"); add_to_lnblist(array, "C-Multi"); add_to_lnblist(array, "Circular 10750"); + add_to_lnblist(array, "Ku 11300"); return array; } /** @@ -311,10 +313,14 @@ dvb_lnb_get_frequencies(const char *id, int *f_low, int *f_hi, int *f_switch) *f_switch = 11700000; } else if(!strcmp(id, "DBS")) { *f_low = 11250000; *f_hi = 0; *f_switch = 0; + } else if(!strcmp(id, "DBS Bandstacked")) { + *f_low = 11250000; + *f_hi = 14350000; + *f_switch = 0; } else if(!strcmp(id, "Standard")) { *f_low = 10000000; *f_hi = 0; *f_switch = 0; } else if(!strcmp(id, "Enhanced")) { @@ -331,7 +337,11 @@ dvb_lnb_get_frequencies(const char *id, int *f_low, int *f_hi, int *f_switch) *f_switch = 0; } else if(!strcmp(id, "Circular 10750")) { *f_low = 10750000; *f_hi = 0; *f_switch = 0; + } else if(!strcmp(id, "Ku 11300")) { + *f_low = 11300000; + *f_hi = 0; + *f_switch = 0; } } diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c new file mode 100644 index 0000000..8262122 --- /dev/null +++ b/src/dvb/dvb_service.c @@ -0,0 +1,501 @@ +/* + * TV Input - Linux DVB interface + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "settings.h" + +#include "tvheadend.h" +#include "dvb.h" +#include "channels.h" +#include "subscriptions.h" +#include "psi.h" +#include "dvb_support.h" +#include "notify.h" + + + + +/** + * Switch the adapter (which is implicitly tied to our transport) + * to receive the given transport. + * + * But we only do this if 'weight' is higher than all of the current + * transports that is subscribing to the adapter + */ +static int +dvb_service_start(service_t *t, unsigned int weight, int force_start) +{ + 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; + + lock_assert(&global_lock); + + if(tda->tda_rootpath == NULL) + return SM_CODE_NO_HW_ATTACHED; + + if(t->s_dvb_mux_instance && !t->s_dvb_mux_instance->tdmi_enabled) + return SM_CODE_MUX_NOT_ENABLED; /* Mux is disabled */ + + /* Check if adapter is idle, or already tuned */ + + 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); + } + + pthread_mutex_lock(&tda->tda_delivery_mutex); + + r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); + if(!r) + LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link); + + pthread_mutex_unlock(&tda->tda_delivery_mutex); + + if(!r) + tda->tda_open_service(tda, t); + + dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); + + return r; +} + + +/** + * + */ +static void +dvb_service_stop(service_t *t) +{ + 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; +} + + +/** + * + */ +static void +dvb_service_refresh(service_t *t) +{ + th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; + + lock_assert(&global_lock); + tda->tda_open_service(tda, t); +} + + +/** + * + */ +static void +dvb_service_save(service_t *t) +{ + htsmsg_t *m = htsmsg_create_map(); + + lock_assert(&global_lock); + + 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); + htsmsg_add_u32(m, "scrambled", t->s_scrambled); + htsmsg_add_u32(m, "channel", t->s_channel_number); + + if(t->s_provider != NULL) + htsmsg_add_str(m, "provider", t->s_provider); + + if(t->s_svcname != NULL) + htsmsg_add_str(m, "servicename", t->s_svcname); + + if(t->s_ch != NULL) { + htsmsg_add_str(m, "channelname", t->s_ch->ch_name); + htsmsg_add_u32(m, "mapped", 1); + } + + 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) + htsmsg_add_str(m, "default_authority", t->s_default_authority); + + 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); + + htsmsg_destroy(m); + dvb_service_notify(t); +} + + +/** + * Load config for the given mux + */ +void +dvb_service_load(th_dvb_mux_instance_t *tdmi, const char *tdmi_identifier) +{ + htsmsg_t *l, *c; + htsmsg_field_t *f; + uint32_t sid, pmt; + 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) + return; + + HTSMSG_FOREACH(f, l) { + if((c = htsmsg_get_map_by_field(f)) == NULL) + continue; + + if(htsmsg_get_u32(c, "service_id", &sid)) + continue; + + if(htsmsg_get_u32(c, "pmt", &pmt)) + continue; + + t = dvb_service_find(tdmi, sid, pmt, f->hmf_name); + + htsmsg_get_u32(c, "stype", &t->s_servicetype); + if(htsmsg_get_u32(c, "scrambled", &u32)) + u32 = 0; + t->s_scrambled = u32; + + if(htsmsg_get_u32(c, "channel", &u32)) + u32 = 0; + t->s_channel_number = u32; + + s = htsmsg_get_str(c, "provider"); + t->s_provider = s ? strdup(s) : NULL; + + s = htsmsg_get_str(c, "servicename"); + t->s_svcname = s ? strdup(s) : NULL; + + pthread_mutex_lock(&t->s_stream_mutex); + service_make_nicename(t); + psi_load_service_settings(c, t); + pthread_mutex_unlock(&t->s_stream_mutex); + + if (!(s = htsmsg_get_str(c, "dvb_charset"))) + s = htsmsg_get_str(c, "dvb_default_charset"); + t->s_dvb_charset = s ? strdup(s) : NULL; + + s = htsmsg_get_str(c, "default_authority"); + t->s_default_authority = s ? strdup(s) : NULL; + + if(htsmsg_get_u32(c, "dvb_eit_enable", &u32)) + u32 = 1; + t->s_dvb_eit_enable = u32; + + s = htsmsg_get_str(c, "channelname"); + if(htsmsg_get_u32(c, "mapped", &u32)) + u32 = 0; + + if(s && u32) + service_map_channel(t, channel_find_by_name(s, 1, 0), 0); + + /* HACK - force save for old config */ + if(old) + 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 + */ +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]; + + memset(si, 0, sizeof(struct source_info)); + + lock_assert(&global_lock); + + si->si_type = S_MPEG_TS; + + if(tdmi->tdmi_adapter->tda_rootpath != NULL) + si->si_device = strdup(tdmi->tdmi_adapter->tda_rootpath); + + 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); + + if(t->s_provider != NULL) + si->si_provider = strdup(t->s_provider); + + if(t->s_svcname != NULL) + si->si_service = strdup(t->s_svcname); +} + + +/** + * + */ +static int +dvb_grace_period(service_t *t) +{ + 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) +{ + service_t *svc; + if (tdmi) { + LIST_FOREACH(svc, &tdmi->tdmi_transports, s_group_link) { + if (enabled && !svc->s_enabled) continue; + if (epgprimary && !service_is_primary_epg(svc)) continue; + if (sid == svc->s_dvb_service_id) 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))) + return svc; + } + } else { + TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) + if ((svc = dvb_service_find3(tda, NULL, netname, onid, tsid, + sid, enabled, epgprimary))) + return svc; + } + return NULL; +} + + +/** + * Find a transport based on 'serviceid' on the given mux + * + * 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) +{ + return dvb_service_find2(tdmi, sid, pmt_pid, identifier, NULL); +} + +service_t * +dvb_service_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, + const char *identifier, int *save) +{ + service_t *t; + char tmp[200]; + char buf[200]; + + lock_assert(&global_lock); + + LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) { + if(t->s_dvb_service_id == sid) + return t; + } + + if(pmt_pid == 0) + return NULL; + + if(identifier == NULL) { + snprintf(tmp, sizeof(tmp), "%s_%04x", tdmi->tdmi_identifier, sid); + identifier = tmp; + } + + dvb_mux_nicename(buf, sizeof(buf), tdmi); + tvhlog(LOG_DEBUG, "dvb", "Add service \"%s\" on \"%s\"", identifier, buf); + + t = service_create(identifier, SERVICE_TYPE_DVB, S_MPEG_TS); + if (save) *save = 1; + + t->s_dvb_service_id = sid; + t->s_pmt_pid = pmt_pid; + + t->s_start_feed = dvb_service_start; + t->s_refresh_feed = dvb_service_refresh; + 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_dvb_mux_instance = tdmi; + LIST_INSERT_HEAD(&tdmi->tdmi_transports, t, s_group_link); + + pthread_mutex_lock(&t->s_stream_mutex); + service_make_nicename(t); + pthread_mutex_unlock(&t->s_stream_mutex); + + dvb_adapter_notify(tdmi->tdmi_adapter); + return t; +} + + +/** + * + */ +htsmsg_t * +dvb_service_build_msg(service_t *t) +{ + th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; + htsmsg_t *m = htsmsg_create_map(); + 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); + + htsmsg_add_u32(m, "sid", t->s_dvb_service_id); + htsmsg_add_u32(m, "pmt", t->s_pmt_pid); + htsmsg_add_u32(m, "pcr", t->s_pcr_pid); + + htsmsg_add_str(m, "type", service_servicetype_txt(t)); + + htsmsg_add_str(m, "svcname", t->s_svcname ?: ""); + htsmsg_add_str(m, "provider", t->s_provider ?: ""); + + htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); + + dvb_mux_nicefreq(buf, sizeof(buf), tdmi); + htsmsg_add_str(m, "mux", buf); + + 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); + + return m; +} + + +/** + * + */ +void +dvb_service_notify_by_adapter(th_dvb_adapter_t *tda) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_str(m, "adapterId", tda->tda_identifier); + 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 d990287..64b35b2 100644 --- a/src/dvb/dvb_support.c +++ b/src/dvb/dvb_support.c @@ -139,11 +139,11 @@ static inline size_t conv_6937(const uint8_t *src, size_t srclen, *dst = ' '; (*dstlen)--; dst++; } else { uint16_t uc; - if (c <= 0x9f) { + if (c >= 0xc0 && c <= 0xcf) { // map two-byte sequence, skipping illegal combinations. if (srclen<2) { errno = EINVAL; return -1; } @@ -153,11 +153,11 @@ static inline size_t conv_6937(const uint8_t *src, size_t srclen, if (c2 == 0x20) { uc = iso6937_lone_accents[c-0xc0]; } else if (c2 >= 0x41 && c2 <= 0x5a) { uc = iso6937_multi_byte[c-0xc0][c2-0x41]; } else if (c2 >= 0x61 && c2 <= 0x7a) { - uc = iso6937_multi_byte[c-0xc0][c2-0x61]; + uc = iso6937_multi_byte[c-0xc0][c2-0x61+26]; } else { uc = 0; } } else { // map according to single character table, skipping @@ -200,15 +200,15 @@ static inline size_t dvb_convert(int conv, * DVB String conversion according to EN 300 468, Annex A * Not all character sets are supported, but it should cover most of them */ int -dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen, char *dvb_default_charset, dvb_string_conv_t *conv) +dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen, const char *dvb_charset, dvb_string_conv_t *conv) { int ic; size_t len, outlen; - int i; + int i, auto_pl_charset = 0; if(srclen < 1) { *dst = 0; return 0; } @@ -218,15 +218,25 @@ dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen, char if (conv->type == src[0]) return conv->func(dst, &dstlen, src, srclen); conv++; } + // check for automatic polish charset detection + if (dvb_charset && strcmp("PL_AUTO", dvb_charset) == 0) { + auto_pl_charset = 1; + dvb_charset = NULL; + } + + // automatic charset detection switch(src[0]) { case 0: return -1; case 0x01 ... 0x0b: + if (auto_pl_charset && (src[0] + 4) == 5) + ic = convert_iso6937; + else ic = convert_iso_8859[src[0] + 4]; src++; srclen--; break; case 0x0c ... 0x0f: @@ -248,20 +258,24 @@ dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen, char break; case 0x16 ... 0x1f: return -1; default: - if (dvb_default_charset != NULL && sscanf(dvb_default_charset, "ISO8859-%d", &i) > 0) { - if (i > 0 && i < 16) { - ic = convert_iso_8859[i]; - } else { + if (auto_pl_charset) + ic = convert_iso_8859[2]; + else ic = convert_iso6937; + break; } + + // manual charset override + if (dvb_charset != NULL && dvb_charset[0] != 0) { + if (sscanf(dvb_charset, "ISO8859-%d", &i) > 0 && i > 0 && i < 16) { + ic = convert_iso_8859[i]; } else { ic = convert_iso6937; } - break; } if(srclen < 1) { *dst = 0; return 0; @@ -282,19 +296,19 @@ dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen, char } int dvb_get_string_with_len(char *dst, size_t dstlen, - const uint8_t *buf, size_t buflen, char *dvb_default_charset, + const uint8_t *buf, size_t buflen, const char *dvb_charset, dvb_string_conv_t *conv) { int l = buf[0]; if(l + 1 > buflen) return -1; - if(dvb_get_string(dst, dstlen, buf + 1, l, dvb_default_charset, conv)) + if(dvb_get_string(dst, dstlen, buf + 1, l, dvb_charset, conv)) return -1; return l + 1; } diff --git a/src/dvb/dvb_support.h b/src/dvb/dvb_support.h index e9b3166..22e81ec 100644 --- a/src/dvb/dvb_support.h +++ b/src/dvb/dvb_support.h @@ -46,10 +46,11 @@ #define DVB_DESC_CONTENT 0x54 #define DVB_DESC_PARENTAL_RAT 0x55 #define DVB_DESC_TELETEXT 0x56 #define DVB_DESC_SUBTITLE 0x59 #define DVB_DESC_AC3 0x6a +#define DVB_DESC_DEF_AUTHORITY 0x73 #define DVB_DESC_CRID 0x76 #define DVB_DESC_EAC3 0x7a #define DVB_DESC_AAC 0x7c #define DVB_DESC_LOCAL_CHAN 0x83 @@ -59,15 +60,15 @@ typedef struct dvb_string_conv size_t (*func) ( char *dst, size_t *dstlen, const uint8_t* src, size_t srclen ); } dvb_string_conv_t; int dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, - const size_t srclen, char *dvb_default_charset, + const size_t srclen, const char *dvb_charset, dvb_string_conv_t *conv); int dvb_get_string_with_len(char *dst, size_t dstlen, - const uint8_t *buf, size_t buflen, char *dvb_default_charset, + const uint8_t *buf, size_t buflen, const char *dvb_charset, dvb_string_conv_t *conv); #define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f)) time_t dvb_convert_date(uint8_t *dvb_buf); diff --git a/src/dvb/dvb_tables.c b/src/dvb/dvb_tables.c index cee8bae..529f5c5 100644 --- a/src/dvb/dvb_tables.c +++ b/src/dvb/dvb_tables.c @@ -40,20 +40,10 @@ #include "channels.h" #include "psi.h" #include "notify.h" #include "cwc.h" -static int tdt_id_tally; - -/** - * Helper for preparing a section filter parameter struct - */ -struct dmx_sct_filter_params * -dvb_fparams_alloc(void) -{ - return calloc(1, sizeof(struct dmx_sct_filter_params)); -} /** * */ static void @@ -83,73 +73,21 @@ dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) /** * */ -static void -tdt_open_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct epoll_event e; - - assert(tdt->tdt_fd == -1); - TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); - - tdt->tdt_fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); - - if(tdt->tdt_fd != -1) { - - tdt->tdt_id = ++tdt_id_tally; - - e.events = EPOLLIN; - e.data.u64 = ((uint64_t)tdt->tdt_fd << 32) | tdt->tdt_id; - - if(epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, tdt->tdt_fd, &e)) { - close(tdt->tdt_fd); - tdt->tdt_fd = -1; - } else { - if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, tdt->tdt_fparams)) { - close(tdt->tdt_fd); - tdt->tdt_fd = -1; - } - } - } - - if(tdt->tdt_fd == -1) - TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); -} - - -/** - * 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) +void +dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt) { - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - - assert(tdt->tdt_fd != -1); - - epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); - close(tdt->tdt_fd); - - tdt->tdt_fd = -1; - TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); -} - + if(tdt->tdt_destroyed) + return; -/** - * - */ -static void -dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec, - int r) -{ int chkcrc = tdt->tdt_flags & TDT_CRC; int tableid, len; uint8_t *ptr; int ret; + th_dvb_mux_instance_t *tdmi = tdt->tdt_tdmi; /* It seems some hardware (or is it the dvb API?) does not honour the DMX_CHECK_CRC flag, so we check it again */ if(chkcrc && tvh_crc32(sec, r, 0xffffffff)) return; @@ -159,167 +97,98 @@ dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec, len = ((sec[1] & 0x0f) << 8) | sec[2]; if(len < r) return; + if((tableid & tdt->tdt_mask) != tdt->tdt_table) + return; + ptr = &sec[3]; 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); else if(tdt->tdt_flags & TDT_TDT) - ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt); + ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt); else - ret = tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque); + ret = tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque); if(ret == 0) tdt->tdt_count++; if(tdt->tdt_flags & TDT_QUICKREQ) dvb_table_fastswitch(tdmi); } -/** - * - */ -static void * -dvb_table_input(void *aux) -{ - th_dvb_adapter_t *tda = aux; - int r, i, tid, fd, x; - struct epoll_event ev[1]; - uint8_t sec[4096]; - th_dvb_mux_instance_t *tdmi; - th_dvb_table_t *tdt; - int64_t cycle_barrier = 0; - - while(1) { - x = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1); - - for(i = 0; i < x; i++) { - - tid = ev[i].data.u64 & 0xffffffff; - fd = ev[i].data.u64 >> 32; - - if(!(ev[i].events & EPOLLIN)) - continue; - - if((r = read(fd, sec, sizeof(sec))) < 3) - continue; - - pthread_mutex_lock(&global_lock); - if((tdmi = tda->tda_mux_current) != NULL) { - LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) - if(tdt->tdt_id == tid) - break; - - if(tdt != NULL) { - dvb_proc_table(tdmi, tdt, sec, r); - - /* Any tables pending (that wants a filter/fd), close this one */ - if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL && - cycle_barrier < getmonoclock()) { - tdt_close_fd(tdmi, tdt); - cycle_barrier = getmonoclock() + 100000; - tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue); - assert(tdt != NULL); - - tdt_open_fd(tdmi, tdt); - } - } - } - pthread_mutex_unlock(&global_lock); - } - } - return NULL; -} - - /** * */ void -dvb_table_init(th_dvb_adapter_t *tda) +dvb_table_release(th_dvb_table_t *tdt) { - pthread_t ptid; - tda->tda_table_epollfd = epoll_create(50); - pthread_create(&ptid, NULL, dvb_table_input, tda); + if(--tdt->tdt_refcount == 0) + free(tdt); } /** * */ static void dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt) { + lock_assert(&global_lock); + assert(tdt->tdt_tdmi == tdmi); LIST_REMOVE(tdt, tdt_link); - - if(tdt->tdt_fd == -1) { - TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); - } else { - epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL); - close(tdt->tdt_fd); - } - + tdmi->tdmi_num_tables--; + tda->tda_close_table(tdmi, tdt); free(tdt->tdt_name); - free(tdt->tdt_fparams); - free(tdt); + tdt->tdt_destroyed = 1; + dvb_table_release(tdt); } - - /** * Add a new DVB table */ void -tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, +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, uint8_t tableid, void *opaque), void *opaque, - const char *name, int flags, int pid, th_dvb_table_t *tdt) + const char *name, int flags, int pid) { th_dvb_table_t *t; // 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) { if(pid == t->tdt_pid && t->tdt_callback == callback && t->tdt_opaque == opaque) { - free(tdt); - free(fparams); return; } } - if(fparams == NULL) - fparams = dvb_fparams_alloc(); - - if(flags & TDT_CRC) fparams->flags |= DMX_CHECK_CRC; - fparams->flags |= DMX_IMMEDIATE_START; - fparams->pid = pid; - - - if(tdt == NULL) - tdt = calloc(1, sizeof(th_dvb_table_t)); - + th_dvb_table_t *tdt = calloc(1, sizeof(th_dvb_table_t)); + tdt->tdt_refcount = 1; tdt->tdt_name = strdup(name); tdt->tdt_callback = callback; tdt->tdt_opaque = opaque; tdt->tdt_pid = pid; tdt->tdt_flags = flags; - tdt->tdt_fparams = fparams; + 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++; tdt->tdt_fd = -1; - TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link); - tdt_open_fd(tdmi, tdt); + tdmi->tdmi_adapter->tda_open_table(tdmi, tdt); } /** * DVB Descriptor; Service */ @@ -347,35 +216,135 @@ dvb_desc_service(uint8_t *ptr, int len, uint8_t *typep, ptr += r; len -= r; return 0; } /** + * DVB Descriptor: Default authority + */ +static int +dvb_desc_def_authority(uint8_t *ptr, int len, char *defauth, size_t dalen) +{ + int r; + if ((r = dvb_get_string(defauth, dalen, ptr, len, NULL, NULL)) < 0) + return -1; + return 0; +} + +static int +dvb_bat_callback(th_dvb_mux_instance_t *tdmi, 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; + + if (tableid != 0x4a) return -1; + bdlen = ((buf[5] & 0xf) << 8) | buf[6]; + if (bdlen+7 > len) return -1; + buf += 7; + len -= 7; + + /* Bouquet Desc */ + i = 0; + // TODO: parse top level descriptors? + buf += bdlen; + len -= bdlen; + + tslen = ((buf[0] & 0xf) << 8) | buf[1]; + if (tslen+2 > len) return -1; + buf += 2; + len -= 2; + + /* Transport Loop */ + i = 0; + while (i+6 < tslen) { + tsid = buf[i] << 8 | buf[i+1]; + onid = buf[i+2] << 8 | buf[i+3]; + tdlen = ((buf[i+4] & 0xf) << 8) | buf[i+5]; + if (tdlen+i+6 > tslen) break; + 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) + break; + + /* Descriptors */ + if (tdmi) { + int save = 0; + *crid = 0; + while (j+2 < tdlen) { + dtag = buf[i+j]; + dlen = buf[i+j+1]; + if (dlen+j+2 > tdlen) break; + j += 2; + switch (dtag) { + case DVB_DESC_DEF_AUTHORITY: + dvb_desc_def_authority(buf+i+j, dlen, crid, sizeof(crid)); + break; + } + j += dlen; + } + if (*crid && strcmp(tdmi->tdmi_default_authority ?: "", crid)) { + free(tdmi->tdmi_default_authority); + tdmi->tdmi_default_authority = strdup(crid); + save = 1; + } + if (save) + dvb_mux_save(tdmi); + } + + i += tdlen; + } + + return 0; +} + +/** * DVB SDT (Service Description Table) */ static int dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { service_t *t; uint16_t service_id; - uint16_t transport_stream_id; + uint16_t tsid, onid; int free_ca_mode; int dllen; uint8_t dtag, dlen; + char crid[257]; char provider[256]; char chname0[256], *chname; uint8_t stype; int l; - if(len < 8) - return -1; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - transport_stream_id = ptr[0] << 8 | ptr[1]; + if (tableid != 0x42 && tableid != 0x46) return -1; - if(tdmi->tdmi_transport_stream_id != transport_stream_id) + if(len < 8) return -1; + + tsid = ptr[0] << 8 | ptr[1]; + onid = ptr[5] << 8 | ptr[6]; + if (tableid == 0x42) { + if(tdmi->tdmi_transport_stream_id != tsid) return -1; + if(!tdmi->tdmi_network_id) + dvb_mux_set_onid(tdmi, onid); + } else { + LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) + if(tdmi->tdmi_transport_stream_id == tsid && + tdmi->tdmi_network_id != onid) + break; + if (!tdmi) return 0; + } // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; // last_section_number = ptr[4]; // original_network_id = ptr[5] << 8 | ptr[6]; @@ -389,10 +358,11 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, len -= 8; ptr += 8; while(len >= 5) { + int save = 0; service_id = ptr[0] << 8 | ptr[1]; // reserved = ptr[2]; // running_status = (ptr[3] >> 5) & 0x7; free_ca_mode = (ptr[3] >> 4) & 0x1; dllen = ((ptr[3] & 0x0f) << 8) | ptr[4]; @@ -401,22 +371,27 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, ptr += 5; if(dllen > len) break; - stype = 0; + if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) { + len -= dllen; + ptr += dllen; + continue; + } + stype = 0; chname = NULL; + *crid = 0; while(dllen > 2) { dtag = ptr[0]; dlen = ptr[1]; len -= 2; ptr += 2; dllen -= 2; - if(dlen > len) - break; + if(dlen > len) break; switch(dtag) { case DVB_DESC_SERVICE: if(dvb_desc_service(ptr, dlen, &stype, provider, sizeof(provider), @@ -435,46 +410,68 @@ dvb_sdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(l == 0) { chname = chname0; snprintf(chname0, sizeof(chname0), "noname-sid-0x%x", service_id); } - - t = dvb_transport_find(tdmi, service_id, 0, NULL); - if(t == NULL) + } + break; + case DVB_DESC_DEF_AUTHORITY: + dvb_desc_def_authority(ptr, dlen, crid, sizeof(crid)); break; + } + len -= dlen; ptr += dlen; dllen -= dlen; + } if(t->s_servicetype != stype || - t->s_scrambled != free_ca_mode || - strcmp(t->s_provider ?: "", provider) || - strcmp(t->s_svcname ?: "", chname)) { - + t->s_scrambled != free_ca_mode) { t->s_servicetype = stype; t->s_scrambled = free_ca_mode; + save = 1; + } + if (chname && (strcmp(t->s_provider ?: "", provider) || + strcmp(t->s_svcname ?: "", chname))) { free(t->s_provider); t->s_provider = strdup(provider); free(t->s_svcname); t->s_svcname = strdup(chname); pthread_mutex_lock(&t->s_stream_mutex); service_make_nicename(t); pthread_mutex_unlock(&t->s_stream_mutex); - t->s_config_save(t); - service_refresh_channel(t); + save = 1; } - } - break; + + if (*crid && strcmp(t->s_default_authority ?: "", crid)) { + free(t->s_default_authority); + t->s_default_authority = strdup(crid); + save = 1; } - len -= dlen; ptr += dlen; dllen -= dlen; + if (save) { + t->s_config_save(t); + service_refresh_channel(t); } } return 0; } +/* + * 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) +{ + if (tableid == 0x42 || tableid == 0x46) + return dvb_sdt_callback(tdmi, ptr, len, tableid, opaque); + else if (tableid == 0x4a) + return dvb_bat_callback(tdmi, ptr, len, tableid, opaque); + return -1; +} /** * PAT - Program Allocation table */ static int @@ -499,29 +496,29 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, // That might indicate that we have accedentally received a PAT // from another mux LIST_FOREACH(other, &tda->tda_muxes, tdmi_adapter_link) if(other != tdmi && other->tdmi_conf.dmc_satconf == tdmi->tdmi_conf.dmc_satconf && - other->tdmi_transport_stream_id == tsid) + other->tdmi_transport_stream_id == tsid && + other->tdmi_network_id == tdmi->tdmi_network_id) return -1; if(tdmi->tdmi_transport_stream_id == 0xffff) dvb_mux_set_tsid(tdmi, tsid); - else if(tdmi->tdmi_transport_stream_id != tsid) { + else if (tdmi->tdmi_transport_stream_id != tsid) return -1; // TSID mismatches, skip packet, may be from another mux - } ptr += 5; len -= 5; while(len >= 4) { service = ptr[0] << 8 | ptr[1]; pmt = (ptr[2] & 0x1f) << 8 | ptr[3]; if(service != 0 && pmt != 0) { int save = 0; - dvb_transport_find2(tdmi, service, pmt, NULL, &save); + dvb_service_find2(tdmi, service, pmt, NULL, &save); if (save || !tda->tda_disable_pmt_monitor) dvb_table_add_pmt(tdmi, pmt); } ptr += 4; len -= 4; @@ -570,12 +567,12 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, pid = ((ptr[2] & 0x1f) << 8) | ptr[3]; if(pid == 0) break; - tdt_add(tdmi, NULL, dvb_ca_callback, (void *)caid, "CA", - TDT_CA, pid, NULL); + tdt_add(tdmi, 0, 0, dvb_ca_callback, (void *)caid, "CA", + TDT_CA, pid); break; default: break; } @@ -616,11 +613,11 @@ static const fe_modulation_t qam_tab [6] = { /** * Cable delivery descriptor */ static int dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint16_t tsid) + uint16_t tsid, uint16_t onid) { struct dvb_mux_conf dmc; int freq, symrate; if(!tdmi->tdmi_adapter->tda_autodiscovery) @@ -653,21 +650,21 @@ dvb_table_cable_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, else dmc.dmc_fe_params.u.qam.modulation = qam_tab[ptr[6] & 0x0f]; dmc.dmc_fe_params.u.qam.fec_inner = fec_tab[ptr[10] & 0x07]; - dvb_mux_create(tdmi->tdmi_adapter, &dmc, tsid, NULL, + dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, NULL, "automatic mux discovery", 1, 1, NULL, NULL); return 0; } /** * Satellite delivery descriptor */ static int dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint16_t tsid) + uint16_t tsid, uint16_t onid) { int freq, symrate; // uint16_t orbital_pos; struct dvb_mux_conf dmc; @@ -738,41 +735,41 @@ dvb_table_sat_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, printf ("error descriptor\n"); return -1; } #endif - dvb_mux_create(tdmi->tdmi_adapter, &dmc, tsid, NULL, - "automatic mux discovery", 1, 1, NULL, NULL); + dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, NULL, + "automatic mux discovery", 1, 1, NULL, tdmi->tdmi_conf.dmc_satconf); return 0; } /** * */ static void dvb_table_local_channel(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint16_t tsid) + uint16_t tsid, uint16_t onid) { uint16_t sid, chan; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; service_t *t; LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) - if(tdmi->tdmi_transport_stream_id == tsid) + if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id == onid) break; if(tdmi == NULL) return; while(len >= 4) { sid = (ptr[0] << 8) | ptr[1]; chan = ((ptr[2] & 3) << 8) | ptr[3]; if(chan != 0) { - t = dvb_transport_find(tdmi, sid, 0, NULL); + t = dvb_service_find(tdmi, sid, 0, NULL); if(t != NULL) { if(t->s_channel_number != chan) { t->s_channel_number = chan; t->s_config_save(t); @@ -795,11 +792,11 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { uint8_t tag, tlen; int ntl; char networkname[256]; - uint16_t tsid; + uint16_t tsid, onid; uint16_t network_id = (ptr[0] << 8) | ptr[1]; if(tdmi->tdmi_adapter->tda_nitoid) { if(tableid != 0x41) return -1; @@ -857,10 +854,11 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, if(len < ntl) return -1; while(len >= 6) { tsid = ( ptr[0] << 8) | ptr[1]; + onid = ( ptr[2] << 8) | ptr[3]; ntl = ((ptr[4] & 0xf) << 8) | ptr[5]; ptr += 6; len -= 6; if(ntl > len) @@ -873,18 +871,18 @@ dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, ntl -= 2; switch(tag) { case DVB_DESC_SAT: if(tdmi->tdmi_adapter->tda_type == FE_QPSK) - dvb_table_sat_delivery(tdmi, ptr, tlen, tsid); + dvb_table_sat_delivery(tdmi, ptr, tlen, tsid, onid); break; case DVB_DESC_CABLE: if(tdmi->tdmi_adapter->tda_type == FE_QAM) - dvb_table_cable_delivery(tdmi, ptr, tlen, tsid); + dvb_table_cable_delivery(tdmi, ptr, tlen, tsid, onid); break; case DVB_DESC_LOCAL_CHAN: - dvb_table_local_channel(tdmi, ptr, tlen, tsid); + dvb_table_local_channel(tdmi, ptr, tlen, tsid, onid); break; } ptr += tlen; len -= tlen; @@ -907,11 +905,11 @@ atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, int numch; char chname[256]; uint8_t atsc_stype; uint8_t stype; uint16_t service_id; - uint16_t transport_stream_id; + uint16_t tsid, onid; int dlen, dl; uint8_t *dptr; if(tableid != 0xc8 && tableid != 0xc9) return -1; // Fail @@ -931,22 +929,23 @@ atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, dl = dlen = ((ptr[30] & 3) << 8) | ptr[31]; if(dlen + 32 > len) return -1; // Corrupt table - transport_stream_id = (ptr[22] << 8) | ptr[23]; + tsid = (ptr[22] << 8) | ptr[23]; + onid = (ptr[24] << 8) | ptr[25]; /* Search all muxes on adapter */ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) - if(tdmi->tdmi_transport_stream_id == transport_stream_id) + if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id == onid); break; if(tdmi == NULL) continue; service_id = (ptr[24] << 8) | ptr[25]; - if((t = dvb_transport_find(tdmi, service_id, 0, NULL)) == NULL) + if((t = dvb_service_find(tdmi, service_id, 0, NULL)) == NULL) continue; atsc_stype = ptr[27] & 0x3f; if(atsc_stype != 0x02) { /* Not ATSC TV */ @@ -1008,62 +1007,45 @@ dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, * Demux for default DVB tables that we want */ static void dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) { - struct dmx_sct_filter_params *fp; - /* Network Information Table */ - fp = dvb_fparams_alloc(); + int table; if(tdmi->tdmi_adapter->tda_nitoid) { - fp->filter.filter[0] = 0x41; + table = 0x41; } else { - fp->filter.filter[0] = 0x40; + table = 0x40; } - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_nit_callback, NULL, "nit", - TDT_QUICKREQ | TDT_CRC, 0x10, NULL); + tdt_add(tdmi, table, 0xff, dvb_nit_callback, NULL, "nit", + TDT_QUICKREQ | TDT_CRC, 0x10); - /* Service Descriptor Table */ + /* Service Descriptor Table and Bouqeut Allocation Table */ - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x42; - // Note: DishNet (EchoStar) send SDT on 0x46 rather than 0x42 - if (tdmi->tdmi_network != NULL) { - if(strstr(tdmi->tdmi_network,"EchoStar")!=NULL) { - fp->filter.filter[0] = 0x46; - } - } - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_sdt_callback, NULL, "sdt", - TDT_QUICKREQ | TDT_CRC, 0x11, NULL); + tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11", + TDT_QUICKREQ | TDT_CRC, 0x11); } /** * Demux for default ATSC tables that we want */ static void dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi) { - struct dmx_sct_filter_params *fp; int tableid; if(tdmi->tdmi_conf.dmc_fe_params.u.vsb.modulation == VSB_8) { tableid = 0xc8; // Terrestrial } else { tableid = 0xc9; // Cable } - /* Virtual Channel Table */ - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = tableid; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, atsc_vct_callback, NULL, "vct", - TDT_QUICKREQ | TDT_CRC, 0x1ffb, NULL); + tdt_add(tdmi, tableid, 0xff, atsc_vct_callback, NULL, "vct", + TDT_QUICKREQ | TDT_CRC, 0x1ffb); } @@ -1071,27 +1053,17 @@ 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) { - struct dmx_sct_filter_params *fp; - /* Program Allocation Table */ - - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x00; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_pat_callback, NULL, "pat", - TDT_QUICKREQ | TDT_CRC, 0, NULL); + tdt_add(tdmi, 0x00, 0xff, dvb_pat_callback, NULL, "pat", + TDT_QUICKREQ | TDT_CRC, 0); /* Conditional Access Table */ - - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x1; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_cat_callback, NULL, "cat", - TDT_CRC, 1, NULL); + tdt_add(tdmi, 0x1, 0xff, dvb_cat_callback, NULL, "cat", + TDT_CRC, 1); switch(tdmi->tdmi_adapter->tda_type) { case FE_QPSK: case FE_OFDM: @@ -1110,19 +1082,15 @@ 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) { - struct dmx_sct_filter_params *fp; char pmtname[100]; snprintf(pmtname, sizeof(pmtname), "PMT(%d)", pmt_pid); - fp = dvb_fparams_alloc(); - fp->filter.filter[0] = 0x02; - fp->filter.mask[0] = 0xff; - tdt_add(tdmi, fp, dvb_pmt_callback, NULL, pmtname, - TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid, NULL); + tdt_add(tdmi, 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) { diff --git a/src/dvb/dvb_transport.c b/src/dvb/dvb_transport.c deleted file mode 100644 index 1835d48..0000000 --- a/src/dvb/dvb_transport.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * TV Input - Linux DVB interface - * Copyright (C) 2007 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 -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "settings.h" - -#include "tvheadend.h" -#include "dvb.h" -#include "channels.h" -#include "subscriptions.h" -#include "psi.h" -#include "dvb_support.h" -#include "notify.h" - -/** - * - */ -static void -dvb_transport_open_demuxers(th_dvb_adapter_t *tda, service_t *t) -{ - struct dmx_pes_filter_params dmx_param; - int fd; - elementary_stream_t *st; - - TAILQ_FOREACH(st, &t->s_components, es_link) { - if(st->es_pid >= 0x2000) - continue; - - if(st->es_demuxer_fd != -1) - continue; - - fd = tvh_open(tda->tda_demux_path, O_RDWR, 0); - st->es_cc_valid = 0; - - if(fd == -1) { - st->es_demuxer_fd = -1; - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to open demuxer \"%s\" for pid %d -- %s", - t->s_identifier, tda->tda_demux_path, - st->es_pid, strerror(errno)); - continue; - } - - memset(&dmx_param, 0, sizeof(dmx_param)); - dmx_param.pid = st->es_pid; - 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; - - if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "\"%s\" unable to configure demuxer \"%s\" for pid %d -- %s", - t->s_identifier, tda->tda_demux_path, - st->es_pid, strerror(errno)); - close(fd); - fd = -1; - } - - st->es_demuxer_fd = fd; - } -} - - - -/** - * Switch the adapter (which is implicitly tied to our transport) - * to receive the given transport. - * - * But we only do this if 'weight' is higher than all of the current - * transports that is subscribing to the adapter - */ -static int -dvb_transport_start(service_t *t, unsigned int weight, int force_start) -{ - 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; - - lock_assert(&global_lock); - - if(tda->tda_rootpath == NULL) - return SM_CODE_NO_HW_ATTACHED; - - if(t->s_dvb_mux_instance && !t->s_dvb_mux_instance->tdmi_enabled) - return SM_CODE_MUX_NOT_ENABLED; /* Mux is disabled */ - - /* Check if adapter is idle, or already tuned */ - - 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; - - dvb_adapter_clean(tda); - } - - pthread_mutex_lock(&tda->tda_delivery_mutex); - - r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); - if(!r) - LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link); - - pthread_mutex_unlock(&tda->tda_delivery_mutex); - - if(!r) - dvb_transport_open_demuxers(tda, t); - - dvb_table_add_pmt(tdmi, t->s_pmt_pid); - - return r; -} - - -/** - * - */ -static void -dvb_transport_stop(service_t *t) -{ - th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; - elementary_stream_t *st; - - lock_assert(&global_lock); - - pthread_mutex_lock(&tda->tda_delivery_mutex); - LIST_REMOVE(t, s_active_link); - pthread_mutex_unlock(&tda->tda_delivery_mutex); - - TAILQ_FOREACH(st, &t->s_components, es_link) { - if(st->es_demuxer_fd != -1) { - close(st->es_demuxer_fd); - st->es_demuxer_fd = -1; - } - } - t->s_status = SERVICE_IDLE; -} - - -/** - * - */ -static void -dvb_transport_refresh(service_t *t) -{ - th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter; - - lock_assert(&global_lock); - dvb_transport_open_demuxers(tda, t); -} - - - -/** - * Load config for the given mux - */ -void -dvb_transport_load(th_dvb_mux_instance_t *tdmi) -{ - htsmsg_t *l, *c; - htsmsg_field_t *f; - uint32_t sid, pmt; - const char *s; - unsigned int u32; - service_t *t; - - lock_assert(&global_lock); - - if((l = hts_settings_load("dvbtransports/%s", tdmi->tdmi_identifier)) == NULL) - return; - - HTSMSG_FOREACH(f, l) { - if((c = htsmsg_get_map_by_field(f)) == NULL) - continue; - - if(htsmsg_get_u32(c, "service_id", &sid)) - continue; - - if(htsmsg_get_u32(c, "pmt", &pmt)) - continue; - - t = dvb_transport_find(tdmi, sid, pmt, f->hmf_name); - - htsmsg_get_u32(c, "stype", &t->s_servicetype); - if(htsmsg_get_u32(c, "scrambled", &u32)) - u32 = 0; - t->s_scrambled = u32; - - if(htsmsg_get_u32(c, "channel", &u32)) - u32 = 0; - t->s_channel_number = u32; - - s = htsmsg_get_str(c, "provider"); - t->s_provider = s ? strdup(s) : NULL; - - s = htsmsg_get_str(c, "servicename"); - t->s_svcname = s ? strdup(s) : NULL; - - pthread_mutex_lock(&t->s_stream_mutex); - service_make_nicename(t); - psi_load_service_settings(c, t); - pthread_mutex_unlock(&t->s_stream_mutex); - - s = htsmsg_get_str(c, "dvb_default_charset"); - t->s_dvb_default_charset = s ? strdup(s) : NULL; - - if(htsmsg_get_u32(c, "dvb_eit_enable", &u32)) - u32 = 1; - t->s_dvb_eit_enable = u32; - - s = htsmsg_get_str(c, "channelname"); - if(htsmsg_get_u32(c, "mapped", &u32)) - u32 = 0; - - if(s && u32) - service_map_channel(t, channel_find_by_name(s, 1, 0), 0); - } - htsmsg_destroy(l); -} - -/** - * - */ -static void -dvb_transport_save(service_t *t) -{ - htsmsg_t *m = htsmsg_create_map(); - - lock_assert(&global_lock); - - 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); - htsmsg_add_u32(m, "scrambled", t->s_scrambled); - htsmsg_add_u32(m, "channel", t->s_channel_number); - - if(t->s_provider != NULL) - htsmsg_add_str(m, "provider", t->s_provider); - - if(t->s_svcname != NULL) - htsmsg_add_str(m, "servicename", t->s_svcname); - - if(t->s_ch != NULL) { - htsmsg_add_str(m, "channelname", t->s_ch->ch_name); - htsmsg_add_u32(m, "mapped", 1); - } - - if(t->s_dvb_default_charset != NULL) - htsmsg_add_str(m, "dvb_default_charset", t->s_dvb_default_charset); - - htsmsg_add_u32(m, "dvb_eit_enable", t->s_dvb_eit_enable); - - 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); - - htsmsg_destroy(m); - dvb_transport_notify(t); -} - - -/** - * 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_transport_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 - */ -static void -dvb_transport_setsourceinfo(service_t *t, struct source_info *si) -{ - th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; - char buf[100]; - - memset(si, 0, sizeof(struct source_info)); - - lock_assert(&global_lock); - - if(tdmi->tdmi_adapter->tda_rootpath != NULL) - si->si_device = strdup(tdmi->tdmi_adapter->tda_rootpath); - - 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); - - if(t->s_provider != NULL) - si->si_provider = strdup(t->s_provider); - - if(t->s_svcname != NULL) - si->si_service = strdup(t->s_svcname); -} - - -/** - * - */ -static int -dvb_grace_period(service_t *t) -{ - return 10; -} - - -/** - * Find a transport based on 'serviceid' on the given mux - * - * If it cannot be found we create it if 'pmt_pid' is also set - */ -service_t * -dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, - const char *identifier) -{ - return dvb_transport_find2(tdmi, sid, pmt_pid, identifier, NULL); -} - -service_t * -dvb_transport_find2(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid, - const char *identifier, int *save) -{ - service_t *t; - char tmp[200]; - char buf[200]; - - lock_assert(&global_lock); - - LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) { - if(t->s_dvb_service_id == sid) - return t; - } - - if(pmt_pid == 0) - return NULL; - - if(identifier == NULL) { - snprintf(tmp, sizeof(tmp), "%s_%04x", tdmi->tdmi_identifier, sid); - identifier = tmp; - } - - dvb_mux_nicename(buf, sizeof(buf), tdmi); - tvhlog(LOG_DEBUG, "dvb", "Add service \"%s\" on \"%s\"", identifier, buf); - - t = service_create(identifier, SERVICE_TYPE_DVB, S_MPEG_TS); - if (save) *save = 1; - - t->s_dvb_service_id = sid; - t->s_pmt_pid = pmt_pid; - - t->s_start_feed = dvb_transport_start; - t->s_refresh_feed = dvb_transport_refresh; - t->s_stop_feed = dvb_transport_stop; - t->s_config_save = dvb_transport_save; - t->s_setsourceinfo = dvb_transport_setsourceinfo; - t->s_quality_index = dvb_transport_quality; - t->s_grace_period = dvb_grace_period; - - t->s_dvb_mux_instance = tdmi; - LIST_INSERT_HEAD(&tdmi->tdmi_transports, t, s_group_link); - - pthread_mutex_lock(&t->s_stream_mutex); - service_make_nicename(t); - pthread_mutex_unlock(&t->s_stream_mutex); - - dvb_adapter_notify(tdmi->tdmi_adapter); - return t; -} - - -/** - * - */ -htsmsg_t * -dvb_transport_build_msg(service_t *t) -{ - th_dvb_mux_instance_t *tdmi = t->s_dvb_mux_instance; - htsmsg_t *m = htsmsg_create_map(); - 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); - - htsmsg_add_u32(m, "sid", t->s_dvb_service_id); - htsmsg_add_u32(m, "pmt", t->s_pmt_pid); - htsmsg_add_u32(m, "pcr", t->s_pcr_pid); - - htsmsg_add_str(m, "type", service_servicetype_txt(t)); - - htsmsg_add_str(m, "svcname", t->s_svcname ?: ""); - htsmsg_add_str(m, "provider", t->s_provider ?: ""); - - htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); - - dvb_mux_nicefreq(buf, sizeof(buf), tdmi); - htsmsg_add_str(m, "mux", buf); - - if(t->s_ch != NULL) - htsmsg_add_str(m, "channelname", t->s_ch->ch_name); - - if(t->s_dvb_default_charset != NULL) - htsmsg_add_str(m, "dvb_default_charset", t->s_dvb_default_charset); - - htsmsg_add_u32(m, "dvb_eit_enable", t->s_dvb_eit_enable); - - return m; -} - - -/** - * - */ -void -dvb_transport_notify_by_adapter(th_dvb_adapter_t *tda) -{ - htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_str(m, "adapterId", tda->tda_identifier); - notify_by_msg("dvbService", m); -} - - -/** - * - */ -void -dvb_transport_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); -}