/* * DVB Table support * 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 "tvheadend.h" #include "dvb.h" #include "dvb_support.h" #include "epg.h" #include "channels.h" #include "psi.h" #include "notify.h" #include "cwc.h" #include "tvhtime.h" #if TDT_TRACE #define TRACE(_pre, _fmt, ...)\ tvhlog(LOG_DEBUG, "tdt-"_pre, _fmt, __VA_ARGS__) #else #define TRACE(_pre, _fmt, ...) (void)0 #endif /** * */ static void dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi) { th_dvb_table_t *tdt; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; char buf[100]; if(!tdmi->tdmi_table_initial) return; LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) if((tdt->tdt_flags & TDT_QUICKREQ) && tdt->tdt_count == 0) return; tdmi->tdmi_table_initial = 0; tda->tda_initial_num_mux--; dvb_mux_save(tdmi); dvb_mux_nicename(buf, sizeof(buf), tdmi); tvhlog(LOG_DEBUG, "dvb", "\"%s\" initial scan completed for \"%s\"", tda->tda_rootpath, buf); dvb_adapter_mux_scanner(tda); } /** * */ void dvb_table_dispatch(uint8_t *sec, int r, th_dvb_table_t *tdt) { if(tdt->tdt_destroyed) return; 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; r -= 3; tableid = sec[0]; 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(tdt->tdt_tdmi, ptr, len, tableid, tdt); else 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); } /** * */ void dvb_table_release(th_dvb_table_t *tdt) { 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); tdmi->tdmi_num_tables--; tda->tda_close_table(tdmi, tdt); free(tdt->tdt_name); tdt->tdt_destroyed = 1; dvb_table_release(tdt); } /** * Add a new DVB table */ void tdt_add(th_dvb_mux_instance_t *tdmi, int tableid, int mask, int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque), void *opaque, 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) { return; } } 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_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; tdmi->tdmi_adapter->tda_open_table(tdmi, tdt); } /** * DVB Descriptor; Service */ static int dvb_desc_service(uint8_t *ptr, int len, uint8_t *typep, char *provider, size_t providerlen, char *name, size_t namelen) { int r; if(len < 2) return -1; *typep = ptr[0]; ptr++; len--; if((r = dvb_get_string_with_len(provider, providerlen, ptr, len, NULL, NULL)) < 0) return -1; ptr += r; len -= r; if((r = dvb_get_string_with_len(name, namelen, ptr, len, NULL, NULL)) < 0) return -1; 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 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; #if TDT_TRACE uint8_t running_status; #endif int l; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; if (tableid != 0x42 && tableid != 0x46) return -1; if(len < 8) return -1; tsid = ptr[0] << 8 | ptr[1]; onid = ptr[5] << 8 | ptr[6]; if (tableid == 0x42) { dvb_mux_set_tsid(tdmi, tsid, 0); dvb_mux_set_onid(tdmi, onid, 0); if(tdmi->tdmi_transport_stream_id != tsid || tdmi->tdmi_network_id != onid) return -1; } 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 -1; } TRACE("sdt", "onid %04X tsid %04X", onid, tsid); // version = ptr[2] >> 1 & 0x1f; // section_number = ptr[3]; // last_section_number = ptr[4]; // reserved = ptr[7]; if((ptr[2] & 1) == 0) { /* current_next_indicator == next, skip this */ return -1; } len -= 8; ptr += 8; while(len >= 5) { int save = 0; service_id = ptr[0] << 8 | ptr[1]; // reserved = ptr[2]; #if TDT_TRACE running_status = (ptr[3] >> 5) & 0x7; #endif free_ca_mode = (ptr[3] >> 4) & 0x1; dllen = ((ptr[3] & 0x0f) << 8) | ptr[4]; TRACE("sdt", " sid %04X running %d free_ca %d", service_id, running_status, free_ca_mode); len -= 5; ptr += 5; if(dllen > len) break; 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; switch(dtag) { case DVB_DESC_SERVICE: if(dvb_desc_service(ptr, dlen, &stype, provider, sizeof(provider), chname0, sizeof(chname0)) == 0) { TRACE("sdt", " stype = %d, provider = %s, name = %s", stype, provider, chname0); chname = chname0; /* Some providers insert spaces. Clean up that (both heading and trailing) */ while(*chname <= 32 && *chname != 0) chname++; l = strlen(chname); while(l > 1 && chname[l - 1] <= 32) { chname[l - 1] = 0; l--; } if(l == 0) { chname = chname0; snprintf(chname0, sizeof(chname0), "noname-sid-0x%x", service_id); } } break; case DVB_DESC_DEF_AUTHORITY: dvb_desc_def_authority(ptr, dlen, crid, sizeof(crid)); break; } len -= dlen; ptr += dlen; dllen -= dlen; } if (!servicetype_is_tv(stype) && !servicetype_is_radio(stype)) continue; if (!(t = dvb_service_find(tdmi, service_id, 0, NULL))) continue; if(t->s_servicetype != stype || t->s_scrambled != free_ca_mode) { t->s_servicetype = stype; 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); save = 1; } if (*crid && strcmp(t->s_default_authority ?: "", crid)) { free(t->s_default_authority); t->s_default_authority = strdup(crid); save = 1; } 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 dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; uint16_t service, pmt, tsid; if(len < 5) return -1; if((ptr[2] & 1) == 0) { /* current_next_indicator == next, skip this */ return -1; } tsid = (ptr[0] << 8) | ptr[1]; dvb_mux_set_tsid(tdmi, tsid, 0); if (tdmi->tdmi_transport_stream_id != tsid) return -1; 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_service_find2(tdmi, service, pmt, NULL, &save); if (save || !tda->tda_disable_pmt_monitor) dvb_table_add_pmt(tdmi, pmt); } ptr += 4; len -= 4; } return 0; } /** * CA - Conditional Access */ static int dvb_ca_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { #if ENABLE_CWC cwc_emm(ptr, len, (uintptr_t)opaque, (void *)tdmi); #endif return 0; } /** * CAT - Conditional Access Table */ static int dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { int tag, tlen; uint16_t pid; uintptr_t caid; if((ptr[2] & 1) == 0) { /* current_next_indicator == next, skip this */ return -1; } ptr += 5; len -= 5; while(len > 2) { tag = *ptr++; tlen = *ptr++; len -= 2; switch(tag) { case DVB_DESC_CA: caid = ( ptr[0] << 8) | ptr[1]; pid = ((ptr[2] & 0x1f) << 8) | ptr[3]; if(pid == 0) break; tdt_add(tdmi, 0, 0, dvb_ca_callback, (void *)caid, "CA", TDT_CA, pid); break; default: break; } ptr += tlen; len -= tlen; } return 0; } /** * Tables for delivery descriptor parsing */ static const fe_code_rate_t fec_tab [16] = { FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, #if DVB_API_VERSION >= 5 FEC_3_5, #else FEC_NONE, #endif FEC_4_5, #if DVB_API_VERSION >= 5 FEC_9_10, #else FEC_NONE, #endif FEC_NONE, FEC_NONE, FEC_NONE, FEC_NONE, FEC_NONE, FEC_NONE }; static const fe_modulation_t qam_tab [6] = { QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256 }; static const fe_bandwidth_t bandwidth_tab [8] = { BANDWIDTH_8_MHZ, BANDWIDTH_7_MHZ, BANDWIDTH_6_MHZ, BANDWIDTH_AUTO, BANDWIDTH_AUTO, BANDWIDTH_AUTO, BANDWIDTH_AUTO, BANDWIDTH_AUTO }; static const fe_modulation_t constellation_tab [4] = { QPSK, QAM_16, QAM_64, QAM_AUTO }; static const fe_code_rate_t code_rate_tab [8] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_AUTO, FEC_AUTO, FEC_AUTO }; static const fe_guard_interval_t guard_interval_tab [4] = { GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4 }; static const fe_transmit_mode_t transmission_mode_tab [4] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, #if DVB_VER_ATLEAST(5,3) TRANSMISSION_MODE_4K, #else TRANSMISSION_MODE_AUTO, /* For older DVB API versions - hope the device can detect */ #endif TRANSMISSION_MODE_AUTO }; static const fe_hierarchy_t hierarchy_info_tab [8] = { HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4, HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4 }; /** * 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 onid, const char *netname) { struct dvb_mux_conf dmc; int freq, symrate; if(len < 11) return -1; memset(&dmc, 0, sizeof(dmc)); dmc.dmc_fe_params.inversion = INVERSION_AUTO; freq = bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 + bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]); if(!freq) return -1; dmc.dmc_fe_params.frequency = freq * 100; TRACE("nit", " dvb-c frequency %d", dmc.dmc_fe_params.frequency); symrate = bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 + bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4); dmc.dmc_fe_params.u.qam.symbol_rate = symrate * 100; if((ptr[6] & 0x0f) > 5) dmc.dmc_fe_params.u.qam.modulation = QAM_AUTO; 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, onid, tsid, netname, "automatic mux discovery", 1, 1, NULL, NULL, tdmi->tdmi_adapter->tda_autodiscovery); 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 onid, const char *netname) { int freq, symrate; // uint16_t orbital_pos; struct dvb_mux_conf dmc; if(len < 11) return -1; memset(&dmc, 0, sizeof(dmc)); dmc.dmc_fe_params.inversion = INVERSION_AUTO; freq = bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 + bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]); dmc.dmc_fe_params.frequency = freq * 10; TRACE("nit", " dvb-s frequency %d", dmc.dmc_fe_params.frequency); if(!freq) return -1; // orbital_pos = bcdtoint(ptr[4]) * 100 + bcdtoint(ptr[5]); symrate = bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 + bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4); dmc.dmc_fe_params.u.qam.symbol_rate = symrate * 100; dmc.dmc_fe_params.u.qam.fec_inner = fec_tab[ptr[10] & 0x0f]; dmc.dmc_polarisation = (ptr[6] >> 5) & 0x03; // Same satconf (lnb, switch, etc) dmc.dmc_satconf = tdmi->tdmi_conf.dmc_satconf; #if DVB_API_VERSION >= 5 int modulation = (ptr[6] & 0x03); if (modulation == 0x01) dmc.dmc_fe_modulation = QPSK; else if (modulation == 0x02) dmc.dmc_fe_modulation = PSK_8; else if (modulation == 0x03) dmc.dmc_fe_modulation = QAM_16; else dmc.dmc_fe_modulation = 0; if (ptr[6] & 0x04) dmc.dmc_fe_delsys = SYS_DVBS2; else dmc.dmc_fe_delsys = SYS_DVBS; switch ((ptr[6] >> 3) & 0x03) { case 0x00: dmc.dmc_fe_rolloff = ROLLOFF_35; break; case 0x01: dmc.dmc_fe_rolloff = ROLLOFF_25; break; case 0x02: dmc.dmc_fe_rolloff = ROLLOFF_20; break; default: case 0x03: dmc.dmc_fe_rolloff = ROLLOFF_AUTO; break; } if (dmc.dmc_fe_delsys == SYS_DVBS && dmc.dmc_fe_rolloff != ROLLOFF_35) { printf ("error descriptor\n"); return -1; } #endif dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, "automatic mux discovery", 1, 1, NULL, tdmi->tdmi_conf.dmc_satconf, tdmi->tdmi_adapter->tda_autodiscovery); return 0; } /** * Terrestrial delivery descriptor */ static int dvb_table_terr_delivery(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint16_t tsid, uint16_t onid, const char *netname) { struct dvb_mux_conf dmc; int freq; if(len < 11) return -1; memset(&dmc, 0, sizeof(dmc)); dmc.dmc_fe_params.inversion = INVERSION_AUTO; freq = ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]) * 10; if(!freq) return -1; dmc.dmc_fe_params.frequency = freq; TRACE("nit", " dvb-t frequency %d", dmc.dmc_fe_params.frequency); dmc.dmc_fe_params.u.ofdm.bandwidth = bandwidth_tab[(ptr[4] & 0xe0) >> 5]; dmc.dmc_fe_params.u.ofdm.constellation=constellation_tab[(ptr[5] & 0xc0) >> 6]; dmc.dmc_fe_params.u.ofdm.hierarchy_information=hierarchy_info_tab[(ptr[5] & 0x38) >> 3]; dmc.dmc_fe_params.u.ofdm.code_rate_HP=code_rate_tab[ptr[5] & 0x3]; dmc.dmc_fe_params.u.ofdm.code_rate_LP=code_rate_tab[(ptr[6] & 0xe0) >> 5]; dmc.dmc_fe_params.u.ofdm.guard_interval=guard_interval_tab[(ptr[6] & 0x18) >> 3]; dmc.dmc_fe_params.u.ofdm.transmission_mode=transmission_mode_tab[(ptr[6] & 0x06) >> 1]; dvb_mux_create(tdmi->tdmi_adapter, &dmc, onid, tsid, netname, "automatic mux discovery", 1, 1, NULL, NULL, tdmi->tdmi_adapter->tda_autodiscovery); return 0; } /** * */ static void dvb_table_local_channel(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, 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 && 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_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); service_refresh_channel(t); } } } ptr += 4; len -= 4; } } /** * NIT - Network Information Table */ static int dvb_nit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { uint8_t dtag, dlen; uint16_t llen; char netname[256]; uint16_t tsid, onid; uint16_t network_id = (ptr[0] << 8) | ptr[1]; netname[0] = '\0'; TRACE("nit", "tableid 0x%02x", tableid); #if TDT_TRACE hexdump("nit", ptr, len); #endif /* Check NID */ if(tdmi->tdmi_adapter->tda_nitoid && tdmi->tdmi_adapter->tda_nitoid != network_id) return -1; /* Ignore non-current */ if((ptr[2] & 1) == 0) return -1; /* Network descriptors */ llen = ((ptr[5] & 0xf) << 8) | ptr[6]; ptr += 7; len -= llen + 7; if (len < 0) return -1; while(llen > 2) { dtag = ptr[0]; dlen = ptr[1]; TRACE("nit", "dtag %02X dlen %d", dtag, dlen); switch(dtag) { case DVB_DESC_NETWORK_NAME: if(dvb_get_string(netname, sizeof(netname), ptr+2, dlen, NULL, NULL)) return -1; if(tableid == 0x40 && (!tdmi->tdmi_network || *tdmi->tdmi_network == '\0')) dvb_mux_set_networkname(tdmi, netname); break; } ptr += dlen + 2; llen -= dlen + 2; } TRACE("nit", "network %d/%s", network_id, netname); if (llen) return -1; /* Transport loop */ llen = ((ptr[0] & 0xf) << 8) | ptr[1]; ptr += 2; len -= 2; if (llen > len) return -1; while(len >= 6) { tsid = ( ptr[0] << 8) | ptr[1]; onid = ( ptr[2] << 8) | ptr[3]; llen = ((ptr[4] & 0xf) << 8) | ptr[5]; TRACE("nit", " onid %04X tsid %04X", onid, tsid); ptr += 6; len -= llen + 6; if(len < 0) return -1; while(llen > 2) { dtag = ptr[0]; dlen = ptr[1]; TRACE("nit", " dtag %02X dlen %d", dtag, dlen); switch(dtag) { case DVB_DESC_SAT: if(tdmi->tdmi_adapter->tda_type == FE_QPSK) dvb_table_sat_delivery(tdmi, ptr+2, dlen, tsid, onid, netname); break; case DVB_DESC_CABLE: if(tdmi->tdmi_adapter->tda_type == FE_QAM) dvb_table_cable_delivery(tdmi, ptr+2, dlen, tsid, onid, netname); break; case DVB_DESC_TERR: if(tdmi->tdmi_adapter->tda_type == FE_OFDM) dvb_table_terr_delivery(tdmi, ptr+2, dlen, tsid, onid, netname); break; case DVB_DESC_LOCAL_CHAN: dvb_table_local_channel(tdmi, ptr+2, dlen, tsid, onid); break; } llen -= dlen + 2; ptr += dlen + 2; } if (llen) return -1; } return 0; } /** * VCT - ATSC Virtual Channel Table */ static int atsc_vct_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { th_dvb_mux_instance_t *tdmi0 = tdmi; th_dvb_adapter_t *tda = tdmi->tdmi_adapter; service_t *t; int numch; char chname[256]; uint8_t atsc_stype; uint8_t stype; uint16_t service_id; uint16_t tsid, onid; int dlen, dl; uint8_t *dptr; if(tableid != 0xc8 && tableid != 0xc9) return -1; // Fail ptr += 5; // Skip generic header len -= 5; if(len < 2) return -1; numch = ptr[1]; ptr += 2; len -= 2; for(; numch > 0 && len >= 32; ptr += 32 + dlen, len -= 32 + dlen, numch--) { dl = dlen = ((ptr[30] & 3) << 8) | ptr[31]; if(dlen + 32 > len) return -1; // Corrupt table tsid = (ptr[22] << 8) | ptr[23]; onid = (ptr[24] << 8) | ptr[25]; if(tsid == 0) { tdmi = tdmi0; } else { /* Search all muxes on adapter */ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) { if(tdmi->tdmi_transport_stream_id == tsid && tdmi->tdmi_network_id == onid) break; } if(tdmi == NULL) continue; } service_id = (ptr[24] << 8) | ptr[25]; if((t = dvb_service_find(tdmi, service_id, 0, NULL)) == NULL) continue; atsc_stype = ptr[27] & 0x3f; if(atsc_stype != 0x02) { /* Not ATSC TV */ continue; } stype = ST_SDTV; atsc_utf16_to_utf8(ptr, 7, chname, sizeof(chname)); dptr = ptr + 32; while(dl > 1) { // int desclen = dptr[1]; dl -= dptr[1] + 2; dptr += dptr[1] + 2; } if(t->s_servicetype != stype || strcmp(t->s_svcname ?: "", chname)) { t->s_servicetype = stype; tvh_str_set(&t->s_svcname, chname); t->s_config_save(t); } } return 0; } /** * PMT - Program Mapping Table */ static int dvb_pmt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, uint8_t tableid, void *opaque) { int active = 0; service_t *t; th_dvb_table_t *tdt = opaque; LIST_FOREACH(t, &tdmi->tdmi_transports, s_group_link) { pthread_mutex_lock(&t->s_stream_mutex); psi_parse_pmt(t, ptr, len, 1, 1); if (t->s_pmt_pid == tdt->tdt_pid && t->s_status == SERVICE_RUNNING) active = 1; pthread_mutex_unlock(&t->s_stream_mutex); } if (tdmi->tdmi_adapter->tda_disable_pmt_monitor && !active) dvb_tdt_destroy(tdmi->tdmi_adapter, tdmi, tdt); return 0; } /* * Time Offset table handler */ static int dvb_tot_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque) { uint16_t mjd; uint8_t hour, min, sec; int year, mon, day; struct tm utc; if (tableid != 0x73) return -1; /* DVB format MJD, Hour, Min, Sec */ mjd = (buf[0] << 8) | buf[1]; hour = bcdtoint(buf[2]); min = bcdtoint(buf[3]); sec = bcdtoint(buf[4]); /* Convert MJD (using algo from EN 300 468 v1.13.1 Annex C) */ year = (int)((mjd - 15078.2) / 365.25); mon = (int)((mjd - 14956.1 - (int)(year * 365.25)) / 30.6001); day = mjd - 14956 - (int)(year * 365.25) - (int)(mon * 30.6001); if (mon == 14 || mon == 15) { year++; mon -= 12; } mon--; /* Convert to UTC time_t */ utc.tm_wday = 0; utc.tm_yday = 0; utc.tm_isdst = 0; utc.tm_year = year; utc.tm_mon = mon - 1; utc.tm_mday = day; utc.tm_hour = hour; utc.tm_min = min; utc.tm_sec = sec; tvhtime_update(&utc); return 0; } /** * Demux for default DVB tables that we want */ static void dvb_table_add_default_dvb(th_dvb_mux_instance_t *tdmi) { /* Network Information Table */ tdt_add(tdmi, 0, 0, dvb_nit_callback, NULL, "nit", TDT_QUICKREQ | TDT_CRC, 0x10); /* Service Descriptor Table and Bouqeut Allocation Table */ tdt_add(tdmi, 0, 0, dvb_pidx11_callback, NULL, "pidx11", TDT_QUICKREQ | TDT_CRC, 0x11); /* Time Offset Table */ tdt_add(tdmi, 0, 0, dvb_tot_callback, NULL, "tot", TDT_CRC, 0x14); } /** * Demux for default ATSC tables that we want */ static void dvb_table_add_default_atsc(th_dvb_mux_instance_t *tdmi) { int tableid; if(tdmi->tdmi_conf.dmc_fe_params.u.vsb.modulation == VSB_8) { tableid = 0xc8; // Terrestrial } else { tableid = 0xc9; // Cable } tdt_add(tdmi, tableid, 0xff, atsc_vct_callback, NULL, "vct", TDT_QUICKREQ | TDT_CRC, 0x1ffb); } /** * Setup FD + demux for default tables that we want */ void dvb_table_add_default(th_dvb_mux_instance_t *tdmi) { /* Program Allocation Table */ tdt_add(tdmi, 0x00, 0xff, dvb_pat_callback, NULL, "pat", TDT_QUICKREQ | TDT_CRC, 0); /* Conditional Access Table */ tdt_add(tdmi, 0x1, 0xff, dvb_cat_callback, NULL, "cat", TDT_CRC, 1); switch(tdmi->tdmi_adapter->tda_type) { case FE_QPSK: case FE_OFDM: case FE_QAM: dvb_table_add_default_dvb(tdmi); break; case FE_ATSC: dvb_table_add_default_atsc(tdmi); break; } } /** * Setup FD + demux for a services PMT */ void dvb_table_add_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid) { char pmtname[100]; snprintf(pmtname, sizeof(pmtname), "PMT(%d)", pmt_pid); tdt_add(tdmi, 0x2, 0xff, dvb_pmt_callback, NULL, pmtname, TDT_CRC | TDT_QUICKREQ | TDT_TDT, pmt_pid); } void dvb_table_rem_pmt(th_dvb_mux_instance_t *tdmi, int pmt_pid) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; th_dvb_table_t *tdt = NULL; LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) if (tdt->tdt_pid == pmt_pid && tdt->tdt_callback == dvb_pmt_callback) break; if (tdt) dvb_tdt_destroy(tda, tdmi, tdt); } /** * */ void dvb_table_flush_all(th_dvb_mux_instance_t *tdmi) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; th_dvb_table_t *tdt; while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL) dvb_tdt_destroy(tda, tdmi, tdt); }