/* * 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 "tvhead.h" #include "dvb.h" #include "channels.h" #include "transports.h" #include "teletext.h" #include "psi.h" #include "dvb_support.h" #include "notify.h" struct th_dvb_mux_instance_tree dvb_muxes; static struct strtab muxfestatustab[] = { { "Unknown", TDMI_FE_UNKNOWN }, { "No signal", TDMI_FE_NO_SIGNAL }, { "Faint signal", TDMI_FE_FAINT_SIGNAL }, { "Bad signal", TDMI_FE_BAD_SIGNAL }, { "Constant FEC", TDMI_FE_CONSTANT_FEC }, { "Bursty FEC", TDMI_FE_BURSTY_FEC }, { "OK", TDMI_FE_OK }, }; static void tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled); /** * */ static void mux_link_initial(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi) { int was_empty = TAILQ_FIRST(&tda->tda_initial_scan_queue) == NULL; tdmi->tdmi_scan_queue = &tda->tda_initial_scan_queue; TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); if(was_empty && (tda->tda_mux_current == NULL || tda->tda_mux_current->tdmi_table_initial == 0)) dvb_adapter_mux_scanner(tda); } /** * Return a readable status text for the given mux */ const char * dvb_mux_status(th_dvb_mux_instance_t *tdmi) { return val2str(tdmi->tdmi_fe_status, muxfestatustab) ?: "Invalid"; } /** * */ static int tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b) { return strcmp(a->tdmi_identifier, b->tdmi_identifier); } /** * */ static int tdmi_compare_key(const struct dvb_mux_conf *a, const struct dvb_mux_conf *b) { return a->dmc_fe_params.frequency == b->dmc_fe_params.frequency && a->dmc_polarisation == b->dmc_polarisation && a->dmc_satconf == b->dmc_satconf; } /** * Return 0 if configuration does not differ. return 1 if it does */ static int tdmi_compare_conf(int adapter_type, const struct dvb_mux_conf *a, const struct dvb_mux_conf *b) { switch(adapter_type) { case FE_OFDM: return memcmp(&a->dmc_fe_params.u.ofdm, &b->dmc_fe_params.u.ofdm, sizeof(a->dmc_fe_params.u.ofdm)); case FE_QAM: return memcmp(&a->dmc_fe_params.u.qam, &b->dmc_fe_params.u.qam, sizeof(a->dmc_fe_params.u.qam)); case FE_ATSC: return memcmp(&a->dmc_fe_params.u.vsb, &b->dmc_fe_params.u.vsb, sizeof(a->dmc_fe_params.u.vsb)); case FE_QPSK: return memcmp(&a->dmc_fe_params.u.qpsk, &b->dmc_fe_params.u.qpsk, sizeof(a->dmc_fe_params.u.qpsk)) #if DVB_API_VERSION >= 5 || a->dmc_fe_modulation != b->dmc_fe_modulation || a->dmc_fe_delsys != b->dmc_fe_delsys || a->dmc_fe_rolloff != b->dmc_fe_rolloff #endif ; } return 0; } /** * 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, int enabled, const char *identifier) { 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) { if(tdmi_compare_key(&tdmi->tdmi_conf, dmc)) break; /* Mux already exist */ } if(tdmi != NULL) { /* Update stuff ... */ int save = 0; if(tdmi_compare_conf(tda->tda_type, &tdmi->tdmi_conf, dmc)) { memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf)); save = 1; } if(tdmi->tdmi_transport_stream_id != tsid) { tdmi->tdmi_transport_stream_id = tsid; save = 1; } if(save) { dvb_mux_save(tdmi); dvb_mux_nicename(buf, sizeof(buf), tdmi); tvhlog(LOG_DEBUG, "dvb", "Configuration for mux \"%s\" updated by %s", buf, source); dvb_mux_notify(tdmi); } return NULL; } tdmi = calloc(1, sizeof(th_dvb_mux_instance_t)); if(identifier == NULL) { char qpsktxt[20]; if(tda->tda_sat) snprintf(qpsktxt, sizeof(qpsktxt), "_%s", dvb_polarisation_to_str(dmc->dmc_polarisation)); else qpsktxt[0] = 0; snprintf(buf, sizeof(buf), "%s%d%s%s%s", tda->tda_identifier, dmc->dmc_fe_params.frequency, qpsktxt, dmc->dmc_satconf ? "_satconf_" : "", dmc->dmc_satconf ? dmc->dmc_satconf->sc_id : ""); tdmi->tdmi_identifier = strdup(buf); } else { tdmi->tdmi_identifier = strdup(identifier); } c = RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp); if(c != NULL) { /* Global identifier collision, not good, not good at all */ tvhlog(LOG_ERR, "dvb", "Multiple DVB multiplexes with same identifier \"%s\" " "one is skipped", tdmi->tdmi_identifier); free(tdmi->tdmi_identifier); free(tdmi); return NULL; } tdmi->tdmi_enabled = enabled; TAILQ_INIT(&tdmi->tdmi_table_queue); tdmi->tdmi_transport_stream_id = tsid; tdmi->tdmi_adapter = tda; tdmi->tdmi_network = network ? strdup(network) : NULL; tdmi->tdmi_quality = 100; memcpy(&tdmi->tdmi_conf, dmc, sizeof(struct dvb_mux_conf)); if(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_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_mux_notify(tdmi); if(enabled) { tda->tda_initial_num_mux++; tdmi->tdmi_table_initial = 1; mux_link_initial(tda, tdmi); } return tdmi; } /** * Destroy a DVB mux (it might come back by itself very soon though :) */ void dvb_mux_destroy(th_dvb_mux_instance_t *tdmi) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; th_transport_t *t; lock_assert(&global_lock); hts_settings_remove("dvbmuxes/%s/%s", tda->tda_identifier, tdmi->tdmi_identifier); while((t = LIST_FIRST(&tdmi->tdmi_transports)) != NULL) { hts_settings_remove("dvbtransports/%s/%s", t->tht_dvb_mux_instance->tdmi_identifier, t->tht_identifier); transport_destroy(t); } dvb_transport_notify_by_adapter(tda); if(tda->tda_mux_current == tdmi) dvb_fe_stop(tda->tda_mux_current); if(tdmi->tdmi_conf.dmc_satconf != NULL) LIST_REMOVE(tdmi, tdmi_satconf_link); RB_REMOVE(&dvb_muxes, tdmi, tdmi_global_link); LIST_REMOVE(tdmi, tdmi_adapter_link); LIST_REMOVE(tdmi, tdmi_adapter_hash_link); if(tdmi->tdmi_scan_queue != NULL) TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); if(tdmi->tdmi_table_initial) tda->tda_initial_num_mux--; hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier); free(tdmi->tdmi_network); free(tdmi->tdmi_identifier); free(tdmi); dvb_adapter_notify(tda); } /** * */ th_dvb_mux_instance_t * dvb_mux_find_by_identifier(const char *identifier) { th_dvb_mux_instance_t skel; lock_assert(&global_lock); skel.tdmi_identifier = (char *)identifier; return RB_FIND(&dvb_muxes, &skel, tdmi_global_link, tdmi_global_cmp); } #if DVB_API_VERSION >= 5 static struct strtab rollofftab[] = { { "ROLLOFF_35", ROLLOFF_35 }, { "ROLLOFF_20", ROLLOFF_20 }, { "ROLLOFF_25", ROLLOFF_25 }, { "ROLLOFF_AUTO", ROLLOFF_AUTO } }; static struct strtab delsystab[] = { { "SYS_UNDEFINED", SYS_UNDEFINED }, { "SYS_DVBC_ANNEX_AC", SYS_DVBC_ANNEX_AC }, { "SYS_DVBC_ANNEX_B", SYS_DVBC_ANNEX_B }, { "SYS_DVBT", SYS_DVBT }, { "SYS_DSS", SYS_DSS }, { "SYS_DVBS", SYS_DVBS }, { "SYS_DVBS2", SYS_DVBS2 }, { "SYS_DVBH", SYS_DVBH }, { "SYS_ISDBT", SYS_ISDBT }, { "SYS_ISDBS", SYS_ISDBS }, { "SYS_ISDBC", SYS_ISDBC }, { "SYS_ATSC", SYS_ATSC }, { "SYS_ATSCMH", SYS_ATSCMH }, { "SYS_DMBTH", SYS_DMBTH }, { "SYS_CMMB", SYS_CMMB }, { "SYS_DAB", SYS_DAB } }; #endif static struct strtab fectab[] = { { "NONE", FEC_NONE }, { "1/2", FEC_1_2 }, { "2/3", FEC_2_3 }, { "3/4", FEC_3_4 }, { "4/5", FEC_4_5 }, { "5/6", FEC_5_6 }, { "6/7", FEC_6_7 }, { "7/8", FEC_7_8 }, { "8/9", FEC_8_9 }, { "AUTO", FEC_AUTO }, #if DVB_API_VERSION >= 5 { "3/5", FEC_3_5 }, { "9/10", FEC_9_10 } #endif }; static struct strtab qamtab[] = { { "QPSK", QPSK }, { "QAM16", QAM_16 }, { "QAM32", QAM_32 }, { "QAM64", QAM_64 }, { "QAM128", QAM_128 }, { "QAM256", QAM_256 }, { "AUTO", QAM_AUTO }, { "8VSB", VSB_8 }, { "16VSB", VSB_16 }, #if DVB_API_VERSION >= 5 { "PSK_8", PSK_8 }, { "APSK_16", APSK_16 }, { "APSK_32", APSK_32 }, { "DQPSK", DQPSK } #endif }; static struct strtab bwtab[] = { { "8MHz", BANDWIDTH_8_MHZ }, { "7MHz", BANDWIDTH_7_MHZ }, { "6MHz", BANDWIDTH_6_MHZ }, { "AUTO", BANDWIDTH_AUTO } }; static struct strtab modetab[] = { { "2k", TRANSMISSION_MODE_2K }, { "8k", TRANSMISSION_MODE_8K }, { "AUTO", TRANSMISSION_MODE_AUTO } }; static struct strtab guardtab[] = { { "1/32", GUARD_INTERVAL_1_32 }, { "1/16", GUARD_INTERVAL_1_16 }, { "1/8", GUARD_INTERVAL_1_8 }, { "1/4", GUARD_INTERVAL_1_4 }, { "AUTO", GUARD_INTERVAL_AUTO }, }; static struct strtab hiertab[] = { { "NONE", HIERARCHY_NONE }, { "1", HIERARCHY_1 }, { "2", HIERARCHY_2 }, { "4", HIERARCHY_4 }, { "AUTO", HIERARCHY_AUTO } }; static struct strtab poltab[] = { { "Vertical", POLARISATION_VERTICAL }, { "Horizontal", POLARISATION_HORIZONTAL }, { "Left", POLARISATION_CIRCULAR_LEFT }, { "Right", POLARISATION_CIRCULAR_RIGHT }, }; /** * */ void 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, "enabled", tdmi->tdmi_enabled); htsmsg_add_str(m, "status", dvb_mux_status(tdmi)); htsmsg_add_u32(m, "transportstreamid", tdmi->tdmi_transport_stream_id); if(tdmi->tdmi_network != NULL) htsmsg_add_str(m, "network", tdmi->tdmi_network); htsmsg_add_u32(m, "frequency", f->frequency); switch(tdmi->tdmi_adapter->tda_type) { case FE_OFDM: htsmsg_add_str(m, "bandwidth", val2str(f->u.ofdm.bandwidth, bwtab)); htsmsg_add_str(m, "constellation", val2str(f->u.ofdm.constellation, qamtab)); htsmsg_add_str(m, "transmission_mode", val2str(f->u.ofdm.transmission_mode, modetab)); htsmsg_add_str(m, "guard_interval", val2str(f->u.ofdm.guard_interval, guardtab)); htsmsg_add_str(m, "hierarchy", val2str(f->u.ofdm.hierarchy_information, hiertab)); htsmsg_add_str(m, "fec_hi", val2str(f->u.ofdm.code_rate_HP, fectab)); htsmsg_add_str(m, "fec_lo", val2str(f->u.ofdm.code_rate_LP, fectab)); break; case FE_QPSK: htsmsg_add_u32(m, "symbol_rate", f->u.qpsk.symbol_rate); htsmsg_add_str(m, "fec", val2str(f->u.qpsk.fec_inner, fectab)); htsmsg_add_str(m, "polarisation", val2str(tdmi->tdmi_conf.dmc_polarisation, poltab)); #if DVB_API_VERSION >= 5 htsmsg_add_str(m, "modulation", val2str(tdmi->tdmi_conf.dmc_fe_modulation, qamtab)); htsmsg_add_str(m, "delivery_system", val2str(tdmi->tdmi_conf.dmc_fe_delsys, delsystab)); htsmsg_add_str(m, "rolloff", val2str(tdmi->tdmi_conf.dmc_fe_rolloff, rollofftab)); #endif break; case FE_QAM: htsmsg_add_u32(m, "symbol_rate", f->u.qam.symbol_rate); htsmsg_add_str(m, "fec", val2str(f->u.qam.fec_inner, fectab)); htsmsg_add_str(m, "constellation", val2str(f->u.qam.modulation, qamtab)); break; case FE_ATSC: htsmsg_add_str(m, "constellation", val2str(f->u.vsb.modulation, qamtab)); break; } if(tdmi->tdmi_conf.dmc_satconf != NULL) htsmsg_add_str(m, "satconf", tdmi->tdmi_conf.dmc_satconf->sc_id); hts_settings_save(m, "dvbmuxes/%s/%s", tdmi->tdmi_adapter->tda_identifier, tdmi->tdmi_identifier); htsmsg_destroy(m); } /** * */ static const char * tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m, const char *identifier) { th_dvb_mux_instance_t *tdmi; struct dvb_mux_conf dmc; const char *s; int r; unsigned int tsid, u32, enabled; memset(&dmc, 0, sizeof(dmc)); dmc.dmc_fe_params.inversion = INVERSION_AUTO; htsmsg_get_u32(m, "frequency", &dmc.dmc_fe_params.frequency); switch(tda->tda_type) { case FE_OFDM: s = htsmsg_get_str(m, "bandwidth"); if(s == NULL || (r = str2val(s, bwtab)) < 0) return "Invalid bandwidth"; dmc.dmc_fe_params.u.ofdm.bandwidth = r; s = htsmsg_get_str(m, "constellation"); if(s == NULL || (r = str2val(s, qamtab)) < 0) return "Invalid QAM constellation"; dmc.dmc_fe_params.u.ofdm.constellation = r; s = htsmsg_get_str(m, "transmission_mode"); if(s == NULL || (r = str2val(s, modetab)) < 0) return "Invalid transmission mode"; dmc.dmc_fe_params.u.ofdm.transmission_mode = r; s = htsmsg_get_str(m, "guard_interval"); if(s == NULL || (r = str2val(s, guardtab)) < 0) return "Invalid guard interval"; dmc.dmc_fe_params.u.ofdm.guard_interval = r; s = htsmsg_get_str(m, "hierarchy"); if(s == NULL || (r = str2val(s, hiertab)) < 0) return "Invalid heirarchy information"; dmc.dmc_fe_params.u.ofdm.hierarchy_information = r; s = htsmsg_get_str(m, "fec_hi"); if(s == NULL || (r = str2val(s, fectab)) < 0) return "Invalid hi-FEC"; dmc.dmc_fe_params.u.ofdm.code_rate_HP = r; s = htsmsg_get_str(m, "fec_lo"); if(s == NULL || (r = str2val(s, fectab)) < 0) return "Invalid lo-FEC"; dmc.dmc_fe_params.u.ofdm.code_rate_LP = r; break; case FE_QPSK: htsmsg_get_u32(m, "symbol_rate", &dmc.dmc_fe_params.u.qpsk.symbol_rate); if(dmc.dmc_fe_params.u.qpsk.symbol_rate == 0) return "Invalid symbol rate"; s = htsmsg_get_str(m, "fec"); if(s == NULL || (r = str2val(s, fectab)) < 0) return "Invalid FEC"; dmc.dmc_fe_params.u.qpsk.fec_inner = r; s = htsmsg_get_str(m, "polarisation"); if(s == NULL || (r = str2val(s, poltab)) < 0) return "Invalid polarisation"; dmc.dmc_polarisation = r; #if DVB_API_VERSION >= 5 s = htsmsg_get_str(m, "modulation"); if(s == NULL || (r = str2val(s, qamtab)) < 0) { r = str2val("QPSK", qamtab); tvhlog(LOG_INFO, "dvb", "no modulation for mux found, defaulting to QPSK"); } dmc.dmc_fe_modulation = r; s = htsmsg_get_str(m, "delivery_system"); if(s == NULL || (r = str2val(s, delsystab)) < 0) { r = str2val("SYS_DVBS", delsystab); tvhlog(LOG_INFO, "dvb", "no delivery system for mux found, defaulting to SYS_DVBS"); } dmc.dmc_fe_delsys = r; s = htsmsg_get_str(m, "rolloff"); if(s == NULL || (r = str2val(s, rollofftab)) < 0) { r = str2val("ROLLOFF_35", rollofftab); tvhlog(LOG_INFO, "dvb", "no rolloff for mux found, defaulting to ROLLOFF_35"); } dmc.dmc_fe_rolloff = r; #endif break; case FE_QAM: htsmsg_get_u32(m, "symbol_rate", &dmc.dmc_fe_params.u.qam.symbol_rate); if(dmc.dmc_fe_params.u.qam.symbol_rate == 0) return "Invalid symbol rate"; s = htsmsg_get_str(m, "constellation"); if(s == NULL || (r = str2val(s, qamtab)) < 0) return "Invalid QAM constellation"; dmc.dmc_fe_params.u.qam.modulation = r; s = htsmsg_get_str(m, "fec"); if(s == NULL || (r = str2val(s, fectab)) < 0) return "Invalid FEC"; dmc.dmc_fe_params.u.qam.fec_inner = r; break; case FE_ATSC: s = htsmsg_get_str(m, "constellation"); if(s == NULL || (r = str2val(s, qamtab)) < 0) return "Invalid VSB constellation"; dmc.dmc_fe_params.u.vsb.modulation = r; break; } if(htsmsg_get_u32(m, "transportstreamid", &tsid)) tsid = 0xffff; if(htsmsg_get_u32(m, "enabled", &enabled)) enabled = 1; if((s = htsmsg_get_str(m, "satconf")) != NULL) dmc.dmc_satconf = dvb_satconf_entry_find(tda, s, 0); else dmc.dmc_satconf = NULL; tdmi = dvb_mux_create(tda, &dmc, tsid, htsmsg_get_str(m, "network"), NULL, enabled, identifier); 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)) tdmi->tdmi_quality = u32; } return NULL; } /** * */ void dvb_mux_load(th_dvb_adapter_t *tda) { htsmsg_t *l, *c; htsmsg_field_t *f; if((l = hts_settings_load("dvbmuxes/%s", tda->tda_identifier)) == NULL) return; HTSMSG_FOREACH(f, l) { if((c = htsmsg_get_map_by_field(f)) == NULL) continue; tdmi_create_by_msg(tda, c, f->hmf_name); } htsmsg_destroy(l); } /** * */ void dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname) { htsmsg_t *m; free(tdmi->tdmi_network); tdmi->tdmi_network = strdup(networkname); dvb_mux_save(tdmi); m = htsmsg_create_map(); htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); notify_by_msg("dvbMux", m); } /** * */ void dvb_mux_set_tsid(th_dvb_mux_instance_t *tdmi, uint16_t tsid) { htsmsg_t *m; tdmi->tdmi_transport_stream_id = tsid; dvb_mux_save(tdmi); m = htsmsg_create_map(); htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_u32(m, "muxid", tdmi->tdmi_transport_stream_id); notify_by_msg("dvbMux", m); } /** * */ static void tdmi_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; if(tdmi->tdmi_enabled == enabled) return; if(tdmi->tdmi_enabled) { if(tdmi->tdmi_scan_queue != NULL) { TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link); tdmi->tdmi_scan_queue = NULL; } } tdmi->tdmi_enabled = enabled; if(enabled) mux_link_initial(tda, tdmi); } /** * Configurable by user */ void dvb_mux_set_enable(th_dvb_mux_instance_t *tdmi, int enabled) { tdmi_set_enable(tdmi, enabled); dvb_mux_save(tdmi); } /** * */ static void dvb_mux_modulation(char *buf, size_t size, th_dvb_mux_instance_t *tdmi) { struct dvb_frontend_parameters *f = &tdmi->tdmi_conf.dmc_fe_params; switch(tdmi->tdmi_adapter->tda_type) { case FE_OFDM: snprintf(buf, size, "%s, %s, %s-mode", val2str(f->u.ofdm.constellation, qamtab), val2str(f->u.ofdm.bandwidth, bwtab), val2str(f->u.ofdm.transmission_mode, modetab)); break; case FE_QPSK: #if DVB_API_VERSION >= 5 snprintf(buf, size, "%d kBaud, %s, %s", f->u.qpsk.symbol_rate / 1000, val2str(tdmi->tdmi_conf.dmc_fe_delsys, delsystab), val2str(tdmi->tdmi_conf.dmc_fe_modulation, qamtab)); #else snprintf(buf, size, "%d kBaud", f->u.qpsk.symbol_rate / 1000); #endif break; case FE_QAM: snprintf(buf, size, "%s, %d kBaud", val2str(f->u.qam.modulation, qamtab), f->u.qpsk.symbol_rate / 1000); break; case FE_ATSC: snprintf(buf, size, "%s", val2str(f->u.vsb.modulation, qamtab)); break; default: snprintf(buf, size, "Unknown"); break; } } /** * */ htsmsg_t * dvb_mux_build_msg(th_dvb_mux_instance_t *tdmi) { htsmsg_t *m = htsmsg_create_map(); char buf[100]; htsmsg_add_str(m, "id", tdmi->tdmi_identifier); htsmsg_add_u32(m, "enabled", tdmi->tdmi_enabled); htsmsg_add_str(m, "network", tdmi->tdmi_network ?: ""); dvb_mux_nicefreq(buf, sizeof(buf), tdmi); htsmsg_add_str(m, "freq", buf); dvb_mux_modulation(buf, sizeof(buf), tdmi); htsmsg_add_str(m, "mod", buf); htsmsg_add_str(m, "pol", dvb_polarisation_to_str_long(tdmi->tdmi_conf.dmc_polarisation)); if(tdmi->tdmi_conf.dmc_satconf != NULL) 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); htsmsg_add_u32(m, "quality", tdmi->tdmi_quality); return m; } /** * */ void dvb_mux_notify(th_dvb_mux_instance_t *tdmi) { notify_by_msg("dvbMux", dvb_mux_build_msg(tdmi)); } /** * */ const char * dvb_mux_add_by_params(th_dvb_adapter_t *tda, int freq, int symrate, int bw, int constellation, int delsys, int tmode, int guard, int hier, int fechi, int feclo, int fec, int polarisation, const char *satconf) { th_dvb_mux_instance_t *tdmi; struct dvb_mux_conf dmc; memset(&dmc, 0, sizeof(dmc)); dmc.dmc_fe_params.inversion = INVERSION_AUTO; switch(tda->tda_type) { case FE_OFDM: dmc.dmc_fe_params.frequency = freq * 1000; if(!val2str(bw, bwtab)) return "Invalid bandwidth"; if(!val2str(constellation, qamtab)) return "Invalid QAM constellation"; if(!val2str(tmode, modetab)) return "Invalid transmission mode"; if(!val2str(guard, guardtab)) return "Invalid guard interval"; if(!val2str(hier, hiertab)) return "Invalid hierarchy"; if(!val2str(fechi, fectab)) return "Invalid FEC Hi"; if(!val2str(feclo, fectab)) return "Invalid FEC Lo"; dmc.dmc_fe_params.u.ofdm.bandwidth = bw; dmc.dmc_fe_params.u.ofdm.constellation = constellation; dmc.dmc_fe_params.u.ofdm.transmission_mode = tmode; dmc.dmc_fe_params.u.ofdm.guard_interval = guard; dmc.dmc_fe_params.u.ofdm.hierarchy_information = hier; dmc.dmc_fe_params.u.ofdm.code_rate_HP = fechi; dmc.dmc_fe_params.u.ofdm.code_rate_LP = feclo; polarisation = 0; break; case FE_QAM: dmc.dmc_fe_params.frequency = freq * 1000; if(!val2str(constellation, qamtab)) return "Invalid QAM constellation"; if(!val2str(fec, fectab)) return "Invalid FEC"; dmc.dmc_fe_params.u.qam.symbol_rate = symrate; dmc.dmc_fe_params.u.qam.modulation = constellation; dmc.dmc_fe_params.u.qam.fec_inner = fec; polarisation = 0; break; case FE_QPSK: dmc.dmc_fe_params.frequency = freq; if(!val2str(fec, fectab)) return "Invalid FEC"; if(!val2str(polarisation, poltab)) return "Invalid polarisation"; dmc.dmc_fe_params.u.qpsk.symbol_rate = symrate; dmc.dmc_fe_params.u.qpsk.fec_inner = fec; #if DVB_API_VERSION >= 5 if(!val2str(constellation, qamtab)) return "Invalid QPSK constellation"; if(!val2str(delsys, delsystab)) return "Invalid delivery system"; dmc.dmc_fe_delsys = delsys; dmc.dmc_fe_modulation = constellation; #endif break; case FE_ATSC: dmc.dmc_fe_params.frequency = freq; if(!val2str(constellation, qamtab)) return "Invalid VSB constellation"; dmc.dmc_fe_params.u.vsb.modulation = constellation; break; } if(satconf != NULL) { dmc.dmc_satconf = dvb_satconf_entry_find(tda, satconf, 0); if(dmc.dmc_satconf == NULL) return "Satellite configuration not found"; } else { dmc.dmc_satconf = NULL; } dmc.dmc_polarisation = polarisation; tdmi = dvb_mux_create(tda, &dmc, 0xffff, NULL, NULL, 1, NULL); if(tdmi == NULL) return "Mux already exist"; return NULL; } /** * */ int dvb_mux_copy(th_dvb_adapter_t *dst, th_dvb_mux_instance_t *tdmi_src) { th_dvb_mux_instance_t *tdmi_dst; th_transport_t *t_src, *t_dst; th_stream_t *st_src, *st_dst; tdmi_dst = dvb_mux_create(dst, &tdmi_src->tdmi_conf, tdmi_src->tdmi_transport_stream_id, tdmi_src->tdmi_network, "copy operation", tdmi_src->tdmi_enabled, NULL); if(tdmi_dst == NULL) return -1; // Already exist LIST_FOREACH(t_src, &tdmi_src->tdmi_transports, tht_group_link) { t_dst = dvb_transport_find(tdmi_dst, t_src->tht_dvb_service_id, t_src->tht_pmt_pid, NULL); t_dst->tht_pcr_pid = t_src->tht_pcr_pid; t_dst->tht_enabled = t_src->tht_enabled; t_dst->tht_servicetype = t_src->tht_servicetype; t_dst->tht_scrambled = t_src->tht_scrambled; if(t_src->tht_provider != NULL) t_dst->tht_provider = strdup(t_src->tht_provider); if(t_src->tht_svcname != NULL) t_dst->tht_svcname = strdup(t_src->tht_svcname); if(t_src->tht_ch != NULL) transport_map_channel(t_dst, t_src->tht_ch, 0); pthread_mutex_lock(&t_src->tht_stream_mutex); pthread_mutex_lock(&t_dst->tht_stream_mutex); LIST_FOREACH(st_src, &t_src->tht_components, st_link) { st_dst = transport_stream_create(t_dst, st_src->st_pid, st_src->st_type); st_dst->st_tb = (AVRational){1, 90000}; memcpy(st_dst->st_lang, st_src->st_lang, 4); st_dst->st_frame_duration = st_src->st_frame_duration; st_dst->st_caid = st_src->st_caid; } pthread_mutex_unlock(&t_dst->tht_stream_mutex); pthread_mutex_unlock(&t_src->tht_stream_mutex); t_dst->tht_config_save(t_dst); // Save config } dvb_mux_save(tdmi_dst); return 0; }