diff --git a/Makefile b/Makefile index f3e1abe2..9e0af3c4 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ SRCS += pvr.c SRCS += epg.c epg_xmltv.c -SRCS += dvb.c dvb_support.c dvb_dvr.c dvb_muxconfig.c +SRCS += dvb.c dvb_support.c dvb_dvr.c dvb_muxconfig.c dvb_fe.c dvb_tables.c SRCS += iptv_input.c iptv_output.c diff --git a/dvb.c b/dvb.c index fdff9396..97518379 100644 --- a/dvb.c +++ b/dvb.c @@ -52,113 +52,11 @@ struct th_dvb_mux_list dvb_muxes; struct th_dvb_adapter_list dvb_adapters_probing; struct th_dvb_adapter_list dvb_adapters_running; -static void dvb_tdt_add_demux(th_dvb_mux_instance_t *tdmi); -static void dvb_eit_add_demux(th_dvb_mux_instance_t *tdmi); -static void dvb_sdt_add_demux(th_dvb_mux_instance_t *tdmi); -static void dvb_pat_add_demux(th_dvb_mux_instance_t *tdmi); -static void dvb_cat_add_demux(th_dvb_mux_instance_t *tdmi); -static void dvb_nit_add_demux(th_dvb_mux_instance_t *tdmi); - -static void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi); - static void dvb_start_initial_scan(th_dvb_mux_instance_t *tdmi); - static void tdmi_activate(th_dvb_mux_instance_t *tdmi); - static void dvb_mux_scanner(void *aux, int64_t now); - static void dvb_fec_monitor(void *aux, int64_t now); -typedef struct dvb_fe_cmd { - TAILQ_ENTRY(dvb_fe_cmd) link; - struct dvb_frontend_parameters *feparams; -} dvb_fe_cmd_t; - - - - - -static void * -tda_monitor1(void *aux) -{ - th_dvb_adapter_t *tda = aux; - struct timespec ts; - dvb_fe_cmd_t *c; - int i, v; - - while(1) { - ts.tv_sec = time(NULL) + 1; - ts.tv_nsec = 0; - - pthread_mutex_lock(&tda->tda_lock); - pthread_cond_timedwait(&tda->tda_cond, &tda->tda_lock, &ts); - c = TAILQ_FIRST(&tda->tda_fe_cmd_queue); - if(c != NULL) - TAILQ_REMOVE(&tda->tda_fe_cmd_queue, c, link); - - pthread_mutex_unlock(&tda->tda_lock); - - if(c != NULL) { - i = ioctl(tda->tda_fe_fd, FE_SET_FRONTEND, c->feparams); - if(i != 0) { - syslog(LOG_ERR, "\"%s\" tuning to %dHz" - " -- Front configuration failed -- %s", - tda->tda_path, c->feparams->frequency, - strerror(errno)); - } - free(c); - } - ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &v); - tda->tda_fe_errors = v; - } -} - - - -static void -tda_parse_status(th_dvb_adapter_t *tda, fe_status_t fe_status) -{ - th_dvb_mux_instance_t *tdmi; - - tdmi = tda->tda_mux_current; - if(tdmi != NULL) { - if(fe_status & FE_HAS_LOCK) { - tdmi->tdmi_status = NULL; - } else if(fe_status & FE_HAS_SYNC) - tdmi->tdmi_status = "No lock, but sync ok"; - else if(fe_status & FE_HAS_VITERBI) - tdmi->tdmi_status = "No lock, but FEC stable"; - else if(fe_status & FE_HAS_CARRIER) - tdmi->tdmi_status = "No lock, but carrier present"; - else if(fe_status & FE_HAS_SIGNAL) - tdmi->tdmi_status = "No lock, but faint signal present"; - else - tdmi->tdmi_status = "No signal"; - } -} - - -static void * -tda_monitor2(void *aux) -{ - th_dvb_adapter_t *tda = aux; - struct dvb_frontend_event ev; - int r, v; - - while(1) { - r = ioctl(tda->tda_fe_fd, FE_GET_EVENT, &ev); - if(r == 0) { - tda_parse_status(tda, ev.status); - - /* Read to reset */ - ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &v); - } - } -} - - - - static void @@ -167,7 +65,6 @@ dvb_add_adapter(const char *path) char fname[256]; int fe; th_dvb_adapter_t *tda; - pthread_t ptid; snprintf(fname, sizeof(fname), "%s/frontend0", path); @@ -214,8 +111,7 @@ dvb_add_adapter(const char *path) syslog(LOG_INFO, "Adding adapter %s (%s)", path, tda->tda_fe_info->name); dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1); - pthread_create(&ptid, NULL, tda_monitor1, tda); - pthread_create(&ptid, NULL, tda_monitor2, tda); + dvb_fe_start(tda); } @@ -251,157 +147,16 @@ dvb_init(void) - - - -static void -tdt_destroy(th_dvb_table_t *tdt) -{ - LIST_REMOVE(tdt, tdt_link); - close(dispatch_delfd(tdt->tdt_handle)); - free(tdt->tdt_name); - free(tdt); -} - - -static void -tdmi_stop(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_table_t *tdt; - while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL) - tdt_destroy(tdt); - - tdmi->tdmi_state = TDMI_IDLE; - time(&tdmi->tdmi_lost_adapter); -} - - -static void -dvb_table_recv(int events, void *opaque, int fd) -{ - th_dvb_table_t *tdt = opaque; - uint8_t sec[4096], *ptr; - int r, len; - uint8_t tableid; - - if(!(events & DISPATCH_READ)) - return; - - r = read(fd, sec, sizeof(sec)); - if(r < 3) - return; - - r -= 3; - - tableid = sec[0]; - len = ((sec[1] & 0x0f) << 8) | sec[2]; - - if(len < r) - return; - - ptr = &sec[3]; - len -= 3; - if(!tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque)) { - tdt->tdt_count++; - } - tdmi_check_scan_status(tdt->tdt_tdmi); -} - - - - - -static void -tdt_add(th_dvb_mux_instance_t *tdmi, int fd, - int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, - uint8_t tableid, void *opaque), void *opaque, - int initial_count, char *name) -{ - th_dvb_table_t *tdt = malloc(sizeof(th_dvb_table_t)); - - LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link); - tdt->tdt_name = strdup(name); - tdt->tdt_callback = callback; - tdt->tdt_opaque = opaque; - tdt->tdt_tdmi = tdmi; - tdt->tdt_handle = dispatch_addfd(fd, dvb_table_recv, tdt, DISPATCH_READ); - tdt->tdt_count = initial_count; -} - - - - - -int -dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - th_dvb_mux_t *tdm = tdmi->tdmi_mux; - dvb_fe_cmd_t *c; - - tdmi->tdmi_state = state; - - if(tda->tda_mux_current == tdmi) - return 0; - - if(tda->tda_mux_current != NULL) - tdmi_stop(tda->tda_mux_current); - - tda->tda_mux_current = tdmi; - - if(maylog) - syslog(LOG_DEBUG, "\"%s\" tuning to mux \"%s\"", - tda->tda_path, tdmi->tdmi_mux->tdm_title); - - c = malloc(sizeof(dvb_fe_cmd_t)); - c->feparams = tdm->tdm_fe_params; - - pthread_mutex_lock(&tda->tda_lock); - TAILQ_INSERT_TAIL(&tda->tda_fe_cmd_queue, c, link); - pthread_cond_signal(&tda->tda_cond); - pthread_mutex_unlock(&tda->tda_lock); - - dvb_tdt_add_demux(tdmi); - dvb_eit_add_demux(tdmi); - dvb_sdt_add_demux(tdmi); - dvb_pat_add_demux(tdmi); - dvb_cat_add_demux(tdmi); - dvb_nit_add_demux(tdmi); - - time(&tdmi->tdmi_got_adapter); - return 0; -} - - - - - -/* - * +/** + * Based on the gived transport id and service id, try to locate the transport */ -static int -dvb_service_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint8_t tableid, void *opaque) -{ - th_transport_t *t = opaque; - - return psi_parse_pmt(t, ptr, len, 1); -} - - -/* - * - */ - th_transport_t * dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t tid, uint16_t sid, int pmt_pid) { th_transport_t *t; th_dvb_mux_t *tdm = tdmi->tdmi_mux; - struct dmx_sct_filter_params fparams; - char pmtname[50], tmp[100]; - int fd; + char tmp[100]; LIST_FOREACH(t, &all_transports, tht_global_link) { if(t->tht_dvb_transport_id == tid && @@ -427,725 +182,17 @@ dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t tid, free((void *)t->tht_uniquename); t->tht_uniquename = strdup(tmp); - - fd = open(tdmi->tdmi_adapter->tda_demux_path, O_RDWR); - if(fd == -1) { - free(t); - return NULL; - } - - memset(&fparams, 0, sizeof(fparams)); - fparams.pid = pmt_pid; - fparams.timeout = 0; - fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; - fparams.filter.filter[0] = 0x02; - fparams.filter.mask[0] = 0xff; - - if(ioctl(fd, DMX_SET_FILTER, &fparams) < 0) { - close(fd); - free(t); - return NULL; - } - - snprintf(pmtname, sizeof(pmtname), "PMT(%d), service:%d", - pmt_pid, sid); - - - tdt_add(tdmi, fd, dvb_service_callback, t, 0, pmtname); t->tht_name = strdup(tdm->tdm_title); LIST_INSERT_HEAD(&all_transports, t, tht_global_link); + + dvb_table_add_transport(tdmi, t, pmt_pid); return t; } - - - - -/* - * DVB time and date functions - */ - -#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f)) - -static time_t -convert_date(uint8_t *dvb_buf) -{ - int i; - int year, month, day, hour, min, sec; - long int mjd; - struct tm dvb_time; - - mjd = (dvb_buf[0] & 0xff) << 8; - mjd += (dvb_buf[1] & 0xff); - hour = bcdtoint(dvb_buf[2] & 0xff); - min = bcdtoint(dvb_buf[3] & 0xff); - sec = bcdtoint(dvb_buf[4] & 0xff); - /* - * Use the routine specified in ETSI EN 300 468 V1.4.1, - * "Specification for Service Information in Digital Video Broadcasting" - * to convert from Modified Julian Date to Year, Month, Day. - */ - year = (int) ((mjd - 15078.2) / 365.25); - month = (int) ((mjd - 14956.1 - (int) (year * 365.25)) / 30.6001); - day = mjd - 14956 - (int) (year * 365.25) - (int) (month * 30.6001); - if (month == 14 || month == 15) - i = 1; - else - i = 0; - year += i; - month = month - 1 - i * 12; - - dvb_time.tm_sec = sec; - dvb_time.tm_min = min; - dvb_time.tm_hour = hour; - dvb_time.tm_mday = day; - dvb_time.tm_mon = month - 1; - dvb_time.tm_year = year; - dvb_time.tm_isdst = -1; - dvb_time.tm_wday = 0; - dvb_time.tm_yday = 0; - return (timegm(&dvb_time)); -} - - -static int -dvb_tdt_callback(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, - uint8_t tableid, void *opaque) -{ - time_t t; - t = convert_date(buf); - tdmi->tdmi_time = t; - time(&t); - - t = tdmi->tdmi_time - t; - - if(abs(t) > 5) { - syslog(LOG_NOTICE, - "\"%s\" DVB network clock is %lds off from system clock", - tdmi->tdmi_mux->tdm_name, t); - } - return 0; -} - - -static void -dvb_tdt_add_demux(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct dmx_sct_filter_params fparams; - int fd; - - fd = open(tda->tda_demux_path, O_RDWR); - if(fd == -1) - return; - - memset(&fparams, 0, sizeof(fparams)); - fparams.pid = 0x14; - fparams.timeout = 0; - fparams.flags = DMX_IMMEDIATE_START; - fparams.filter.filter[0] = 0x70; - fparams.filter.mask[0] = 0xff; - - if(ioctl(fd, DMX_SET_FILTER, &fparams) < 0) { - close(fd); - return; - } - tdt_add(tdmi, fd, dvb_tdt_callback, NULL, 1, "tdt"); -} - - - - - -/* - * DVB Descriptor; Short Event - */ - -static int -dvb_desc_short_event(uint8_t *ptr, int len, - char *title, size_t titlelen, - char *desc, size_t desclen) -{ - int r; - - if(len < 4) - return -1; - ptr += 3; len -= 3; - - if((r = dvb_get_string_with_len(title, titlelen, ptr, len, "UTF8")) < 0) - return -1; - ptr += r; len -= r; - - if((r = dvb_get_string_with_len(desc, desclen, ptr, len, "UTF8")) < 0) - return -1; - - return 0; -} - - -/* - * 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, - "UTF8")) < 0) - return -1; - ptr += r; len -= r; - - if((r = dvb_get_string_with_len(name, namelen, ptr, len, - "UTF8")) < 0) - return -1; - ptr += r; len -= r; - return 0; -} - - - - - - - -/* - * DVB EIT (Event Information Table) - */ - - -static int -dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint8_t tableid, void *opaque) -{ - th_transport_t *t; - th_channel_t *ch; - - uint16_t serviceid; - int version; - int current_next_indicator; - uint8_t section_number; - uint8_t last_section_number; - uint16_t transport_stream_id; - uint16_t original_network_id; - uint8_t segment_last_section_number; - uint8_t last_table_id; - - uint16_t event_id; - time_t start_time; - - int duration; - int dllen; - uint8_t dtag, dlen; - - char title[256]; - char desc[5000]; - - if(tableid < 0x4e || tableid > 0x6f) - return -1; - - if(len < 11) - return -1; - - serviceid = ptr[0] << 8 | ptr[1]; - version = ptr[2] >> 1 & 0x1f; - current_next_indicator = ptr[2] & 1; - section_number = ptr[3]; - last_section_number = ptr[4]; - transport_stream_id = ptr[5] << 8 | ptr[6]; - original_network_id = ptr[7] << 8 | ptr[8]; - segment_last_section_number = ptr[9]; - last_table_id = ptr[10]; - - len -= 11; - ptr += 11; - - t = dvb_find_transport(tdmi, transport_stream_id, serviceid, 0); - if(t == NULL) - return -1; - ch = t->tht_channel; - if(ch == NULL) - return -1; - - epg_lock(); - - while(len >= 12) { - event_id = ptr[0] << 8 | ptr[1]; - start_time = convert_date(&ptr[2]); - duration = bcdtoint(ptr[7] & 0xff) * 3600 + - bcdtoint(ptr[8] & 0xff) * 60 + - bcdtoint(ptr[9] & 0xff); - dllen = ((ptr[10] & 0x0f) << 8) | ptr[11]; - - len -= 12; - ptr += 12; - - - - if(dllen > len) - break; - - while(dllen > 0) { - dtag = ptr[0]; - dlen = ptr[1]; - - len -= 2; ptr += 2; dllen -= 2; - - if(dlen > len) - break; - - switch(dtag) { - case DVB_DESC_SHORT_EVENT: - if(dvb_desc_short_event(ptr, dlen, - title, sizeof(title), - desc, sizeof(desc)) < 0) - duration = 0; - break; - } - - len -= dlen; ptr += dlen; dllen -= dlen; - } - - if(duration > 0) { - epg_update_event_by_id(ch, event_id, start_time, duration, - title, desc); - - } - } - - epg_unlock(); - return 0; -} - - - -static void -dvb_eit_add_demux(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct dmx_sct_filter_params fparams; - int fd; - - fd = open(tda->tda_demux_path, O_RDWR); - if(fd == -1) - return; - - memset(&fparams, 0, sizeof(fparams)); - fparams.pid = 0x12; - fparams.timeout = 0; - fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; - - if(ioctl(fd, DMX_SET_FILTER, &fparams) < 0) { - close(fd); - return; - } - - tdt_add(tdmi, fd, dvb_eit_callback, NULL, 1, "eit"); -} - - - - - -/* - * 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) -{ - th_dvb_mux_t *tdm = tdmi->tdmi_mux; - th_transport_t *t; - int version; - int current_next_indicator; - uint8_t section_number; - uint8_t last_section_number; - uint16_t service_id; - uint16_t transport_stream_id; - uint16_t original_network_id; - - int reserved; - int running_status, free_ca_mode; - int dllen; - uint8_t dtag, dlen; - - char provider[256]; - char chname0[256], *chname; - uint8_t stype; - int ret = 0, l; - - if(tdm->tdm_network == NULL) - return -1; - - if(len < 8) - return -1; - - transport_stream_id = ptr[0] << 8 | ptr[1]; - version = ptr[2] >> 1 & 0x1f; - current_next_indicator = ptr[2] & 1; - section_number = ptr[3]; - last_section_number = ptr[4]; - original_network_id = ptr[5] << 8 | ptr[6]; - reserved = ptr[7]; - - len -= 8; - ptr += 8; - - - while(len >= 5) { - 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]; - - len -= 5; - ptr += 5; - - if(dllen > len) - break; - - stype = 0; - - chname = NULL; - - 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) { - stype = 0; - } else { - chname = chname0; - /* Some providers insert spaces */ - while(*chname <= 32 && *chname != 0) - chname++; - - l = strlen(chname); - while(l > 1 && chname[l - 1] <= 32) { - chname[l - 1] = 0; - l--; - } - - } - break; - } - - len -= dlen; ptr += dlen; dllen -= dlen; - } - - - if(chname != NULL) switch(stype) { - - case DVB_ST_SDTV: - case DVB_ST_HDTV: - case DVB_ST_AC_SDTV: - case DVB_ST_AC_HDTV: - /* TV service */ - - t = dvb_find_transport(tdmi, transport_stream_id, service_id, 0); - - if(t == NULL) { - ret |= 1; - } else { - t->tht_scrambled = free_ca_mode; - - free((void *)t->tht_provider); - t->tht_provider = strdup(provider); - - free((void *)t->tht_network); - t->tht_network = strdup(tdm->tdm_network); - - if(t->tht_channel == NULL) { - /* Not yet mapped to a channel */ - if(LIST_FIRST(&t->tht_streams) != NULL) { - /* We have streams, map it */ - if(t->tht_scrambled) - t->tht_prio = 75; - else - t->tht_prio = 25; - - transport_set_channel(t, channel_find(chname, 1, NULL)); - } else { - if(t->tht_pmt_seen == 0) - ret |= 1; /* Return error (so scanning wont continue yet) */ - } - } - } - break; - - } - } - return ret; -} - - - -static void -dvb_sdt_add_demux(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct dmx_sct_filter_params fparams; - int fd; - - fd = open(tda->tda_demux_path, O_RDWR); - if(fd == -1) - return; - - memset(&fparams, 0, sizeof(fparams)); - fparams.pid = 0x11; - fparams.timeout = 0; - fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; - fparams.filter.filter[0] = 0x42; - fparams.filter.mask[0] = 0xff; - - if(ioctl(fd, DMX_SET_FILTER, &fparams) < 0) { - close(fd); - return; - } - - tdt_add(tdmi, fd, dvb_sdt_callback, NULL, 0, "sdt"); -} - /** * */ - - -static int -dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, - uint8_t tableid, void *opaque) -{ - uint16_t service, pmt, tid; - - if(len < 5) - return -1; - - tid = (ptr[0] << 8) | ptr[1]; - - ptr += 5; - len -= 5; - - while(len >= 4) { - service = ptr[0] << 8 | ptr[1]; - pmt = (ptr[2] & 0x1f) << 8 | ptr[3]; - - if(service != 0) - dvb_find_transport(tdmi, tid, service, pmt); - - ptr += 4; - len -= 4; - } - return 0; -} - - - -static void -dvb_pat_add_demux(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct dmx_sct_filter_params fparams; - int fd; - - fd = open(tda->tda_demux_path, O_RDWR); - if(fd == -1) - return; - - memset(&fparams, 0, sizeof(fparams)); - fparams.pid = 0; - fparams.timeout = 0; - fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; - fparams.filter.filter[0] = 0x00; - fparams.filter.mask[0] = 0xff; - - if(ioctl(fd, DMX_SET_FILTER, &fparams) < 0) { - close(fd); - return; - } - - tdt_add(tdmi, fd, dvb_pat_callback, NULL, 0, "pat"); -} - - -/** - * CAT - Condition 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 caid; - uint16_t pid; - - 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]; - break; - - default: - break; - } - - ptr += tlen; - len -= tlen; - } - - - return 0; -} - - - -static void -dvb_cat_add_demux(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct dmx_sct_filter_params fparams; - int fd; - - fd = open(tda->tda_demux_path, O_RDWR); - if(fd == -1) - return; - - memset(&fparams, 0, sizeof(fparams)); - fparams.pid = 1; - fparams.timeout = 0; - fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; - fparams.filter.filter[0] = 0x01; - fparams.filter.mask[0] = 0xff; - - if(ioctl(fd, DMX_SET_FILTER, &fparams) < 0) { - close(fd); - return; - } - - tdt_add(tdmi, fd, dvb_cat_callback, NULL, 1, "cat"); -} - -/** - * 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 tag, tlen; - int ntl; - char networkname[256]; - th_dvb_mux_t *tdm = tdmi->tdmi_mux; - - - ptr += 5; - len -= 5; - - if(tableid != 0x40) - return -1; - - ntl = ((ptr[0] & 0xf) << 8) | ptr[1]; - ptr += 2; - len -= 2; - if(ntl > len) - return 0; - - while(ntl > 2) { - tag = *ptr++; - tlen = *ptr++; - len -= 2; - ntl -= 2; - - switch(tag) { - case DVB_DESC_NETWORK_NAME: - if(dvb_get_string(networkname, sizeof(networkname), ptr, tlen, "UTF8")) - return 0; - - if(tdm->tdm_network != NULL) { - if(strcmp(tdm->tdm_network, networkname)) { - syslog(LOG_ALERT, - "DVB Mux conflict \"%s\" on \"%s\" says it's \"%s\", " - "but is has alrady been reported as \"%s\"", - tdm->tdm_name, - tdmi->tdmi_adapter->tda_path, - networkname, - tdm->tdm_network); - } - } else { - tdm->tdm_network = strdup(networkname); - } - break; - } - - ptr += tlen; - len -= tlen; - ntl -= tlen; - } - - return 0; -} - - - -static void -dvb_nit_add_demux(th_dvb_mux_instance_t *tdmi) -{ - th_dvb_adapter_t *tda = tdmi->tdmi_adapter; - struct dmx_sct_filter_params fparams; - int fd; - - fd = open(tda->tda_demux_path, O_RDWR); - if(fd == -1) - return; - - memset(&fparams, 0, sizeof(fparams)); - fparams.pid = 0x10; - fparams.timeout = 0; - fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; - fparams.filter.filter[0] = 0x00; - fparams.filter.mask[0] = 0x00; - - if(ioctl(fd, DMX_SET_FILTER, &fparams) < 0) { - close(fd); - return; - } - - tdt_add(tdmi, fd, dvb_nit_callback, NULL, 0, "nit"); -} - - static void tdmi_activate(th_dvb_mux_instance_t *tdmi) { @@ -1177,6 +224,9 @@ tdmi_activate(th_dvb_mux_instance_t *tdmi) } +/** + * + */ static void tdmi_initial_scan_timeout(void *aux, int64_t now) { @@ -1203,8 +253,10 @@ tdmi_initial_scan_timeout(void *aux, int64_t now) } - -static void +/** + * + */ +void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi) { th_dvb_table_t *tdt; @@ -1227,7 +279,9 @@ tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi) } - +/** + * + */ static void dvb_start_initial_scan(th_dvb_mux_instance_t *tdmi) { @@ -1238,11 +292,10 @@ dvb_start_initial_scan(th_dvb_mux_instance_t *tdmi) } + /** - * * */ - static int mux_sort_quality(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b) { @@ -1250,13 +303,15 @@ mux_sort_quality(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b) } +/** + * + */ static void dvb_fec_monitor(void *aux, int64_t now) { th_dvb_adapter_t *tda = aux; th_dvb_mux_instance_t *tdmi; th_dvb_mux_t *tdm; - int v; dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1); @@ -1264,10 +319,6 @@ dvb_fec_monitor(void *aux, int64_t now) if(tdmi != NULL && tdmi->tdmi_status == NULL) { - v = tda->tda_fe_errors; - - tdmi->tdmi_fec_err_per_sec = (tdmi->tdmi_fec_err_per_sec * 7 + v) / 8; - if(tdmi->tdmi_fec_err_per_sec > DVB_FEC_ERROR_LIMIT) { if(LIST_FIRST(&tda->tda_transports) != NULL) { @@ -1290,7 +341,6 @@ dvb_fec_monitor(void *aux, int64_t now) * If nobody is subscribing, cycle thru all muxes to get some stats * and EIT updates */ - static void dvb_mux_scanner(void *aux, int64_t now) { @@ -1310,5 +360,4 @@ dvb_mux_scanner(void *aux, int64_t now) return; /* no instances */ dvb_tune_tdmi(tdmi, 0, TDMI_IDLESCAN); - } diff --git a/dvb.h b/dvb.h index bcd47b9c..4ebdf856 100644 --- a/dvb.h +++ b/dvb.h @@ -30,6 +30,21 @@ void dvb_init(void); int dvb_configure_transport(th_transport_t *t, const char *muxname, const char *channel_name); -int dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state); +void dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, + tdmi_state_t state); + +void dvb_table_add_default(th_dvb_mux_instance_t *tdmi); + +void dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t, + int pmt_pid); + +void dvb_tdt_destroy(th_dvb_table_t *tdt); + +void dvb_fe_start(th_dvb_adapter_t *tda); + +void tdmi_check_scan_status(th_dvb_mux_instance_t *tdmi); + +th_transport_t *dvb_find_transport(th_dvb_mux_instance_t *tdmi, uint16_t tid, + uint16_t sid, int pmt_pid); #endif /* DVB_H_ */ diff --git a/dvb_fe.c b/dvb_fe.c new file mode 100644 index 00000000..3adf8302 --- /dev/null +++ b/dvb_fe.c @@ -0,0 +1,207 @@ +/* + * 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 "tvhead.h" +#include "dispatch.h" +#include "dvb.h" +#include "dvb_support.h" + + +typedef struct dvb_fe_cmd { + TAILQ_ENTRY(dvb_fe_cmd) link; + th_dvb_mux_instance_t *tdmi; +} dvb_fe_cmd_t; + + +/** + * On some cards the FEC readout, tuning and such things takes a very long + * time (~0.5 s). Therefore we need to do the tuning and monitoring in a + * separate thread + */ +static void * +dvb_fe_manager(void *aux) +{ + th_dvb_adapter_t *tda = aux; + struct timespec ts; + dvb_fe_cmd_t *c; + int i, v; + th_dvb_mux_instance_t *tdmi = NULL; + fe_status_t fe_status; + th_dvb_table_t *tdt; + + while(1) { + ts.tv_sec = time(NULL) + 1; + ts.tv_nsec = 0; + + pthread_mutex_lock(&tda->tda_lock); + pthread_cond_timedwait(&tda->tda_cond, &tda->tda_lock, &ts); + c = TAILQ_FIRST(&tda->tda_fe_cmd_queue); + if(c != NULL) + TAILQ_REMOVE(&tda->tda_fe_cmd_queue, c, link); + + pthread_mutex_unlock(&tda->tda_lock); + + if(c != NULL) { + + /* Switch to a new mux */ + + tdmi = c->tdmi; + + i = ioctl(tda->tda_fe_fd, FE_SET_FRONTEND, + tdmi->tdmi_mux->tdm_fe_params); + + if(i != 0) { + syslog(LOG_ERR, "\"%s\" tuning to %dHz" + " -- Front configuration failed -- %s", + tda->tda_path, tdmi->tdmi_mux->tdm_fe_params->frequency, + strerror(errno)); + } + free(c); + + time(&tdmi->tdmi_got_adapter); + + /* Reset FEC counter */ + + ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &v); + + /* Now that we have tuned, start demuxing of tables */ + + pthread_mutex_lock(&tdmi->tdmi_table_lock); + LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link) { + if(tdt->tdt_fparams == NULL) + continue; + + ioctl(tdt->tdt_fd, DMX_SET_FILTER, tdt->tdt_fparams); + free(tdt->tdt_fparams); + tdt->tdt_fparams = NULL; + } + + pthread_mutex_unlock(&tdmi->tdmi_table_lock); + } + + if(tdmi == NULL) + continue; + + fe_status = 0; + ioctl(tda->tda_fe_fd, FE_READ_STATUS, &fe_status); + + if(fe_status & FE_HAS_LOCK) { + tdmi->tdmi_status = NULL; + } else if(fe_status & FE_HAS_SYNC) + tdmi->tdmi_status = "No lock, but sync ok"; + else if(fe_status & FE_HAS_VITERBI) + tdmi->tdmi_status = "No lock, but FEC stable"; + else if(fe_status & FE_HAS_CARRIER) + tdmi->tdmi_status = "No lock, but carrier present"; + else if(fe_status & FE_HAS_SIGNAL) + tdmi->tdmi_status = "No lock, but faint signal present"; + else + tdmi->tdmi_status = "No signal"; + + ioctl(tda->tda_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &v); + tdmi->tdmi_fec_err_per_sec = (tdmi->tdmi_fec_err_per_sec * 7 + v) / 8; + } +} + + +/** + * Startup the FE management thread + */ +void +dvb_fe_start(th_dvb_adapter_t *tda) +{ + pthread_t ptid; + pthread_create(&ptid, NULL, dvb_fe_manager, tda); +} + + +/** + * Stop the given TDMI + */ +static void +tdmi_stop(th_dvb_mux_instance_t *tdmi) +{ + th_dvb_table_t *tdt; + + pthread_mutex_lock(&tdmi->tdmi_table_lock); + + while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL) + dvb_tdt_destroy(tdt); + + pthread_mutex_unlock(&tdmi->tdmi_table_lock); + + tdmi->tdmi_state = TDMI_IDLE; + time(&tdmi->tdmi_lost_adapter); +} + + +/** + * Tune an adapter to a mux instance (but only if needed) + */ +void +dvb_tune_tdmi(th_dvb_mux_instance_t *tdmi, int maylog, tdmi_state_t state) +{ + dvb_fe_cmd_t *c; + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + + tdmi->tdmi_state = state; + + if(tda->tda_mux_current == tdmi) + return; + + if(tda->tda_mux_current != NULL) + tdmi_stop(tda->tda_mux_current); + + tda->tda_mux_current = tdmi; + + if(maylog) + syslog(LOG_DEBUG, "\"%s\" tuning to mux \"%s\"", + tda->tda_path, tdmi->tdmi_mux->tdm_title); + + /* Add tables which will be activated once the tuning is completed */ + + dvb_table_add_default(tdmi); + + /* Send command to the thread */ + + c = malloc(sizeof(dvb_fe_cmd_t)); + c->tdmi = tdmi; + pthread_mutex_lock(&tda->tda_lock); + TAILQ_INSERT_TAIL(&tda->tda_fe_cmd_queue, c, link); + pthread_cond_signal(&tda->tda_cond); + pthread_mutex_unlock(&tda->tda_lock); +} diff --git a/dvb_muxconfig.c b/dvb_muxconfig.c index db37ac70..6c397054 100644 --- a/dvb_muxconfig.c +++ b/dvb_muxconfig.c @@ -40,6 +40,8 @@ dvb_add_mux_instance(th_dvb_adapter_t *tda, th_dvb_mux_t *tdm) tdmi = calloc(1, sizeof(th_dvb_mux_instance_t)); + pthread_mutex_init(&tdmi->tdmi_table_lock, NULL); + tdmi->tdmi_status = TDMI_CONFIGURED; tdmi->tdmi_mux = tdm; diff --git a/dvb_support.c b/dvb_support.c index ccbaca09..80bf523e 100644 --- a/dvb_support.c +++ b/dvb_support.c @@ -160,3 +160,48 @@ dvb_get_string_with_len(char *dst, size_t dstlen, return l + 1; } + + +/* + * DVB time and date functions + */ + +time_t +dvb_convert_date(uint8_t *dvb_buf) +{ + int i; + int year, month, day, hour, min, sec; + long int mjd; + struct tm dvb_time; + + mjd = (dvb_buf[0] & 0xff) << 8; + mjd += (dvb_buf[1] & 0xff); + hour = bcdtoint(dvb_buf[2] & 0xff); + min = bcdtoint(dvb_buf[3] & 0xff); + sec = bcdtoint(dvb_buf[4] & 0xff); + /* + * Use the routine specified in ETSI EN 300 468 V1.4.1, + * "Specification for Service Information in Digital Video Broadcasting" + * to convert from Modified Julian Date to Year, Month, Day. + */ + year = (int) ((mjd - 15078.2) / 365.25); + month = (int) ((mjd - 14956.1 - (int) (year * 365.25)) / 30.6001); + day = mjd - 14956 - (int) (year * 365.25) - (int) (month * 30.6001); + if (month == 14 || month == 15) + i = 1; + else + i = 0; + year += i; + month = month - 1 - i * 12; + + dvb_time.tm_sec = sec; + dvb_time.tm_min = min; + dvb_time.tm_hour = hour; + dvb_time.tm_mday = day; + dvb_time.tm_mon = month - 1; + dvb_time.tm_year = year; + dvb_time.tm_isdst = -1; + dvb_time.tm_wday = 0; + dvb_time.tm_yday = 0; + return (timegm(&dvb_time)); +} diff --git a/dvb_support.h b/dvb_support.h index bef1ca9a..cbf4264b 100644 --- a/dvb_support.h +++ b/dvb_support.h @@ -55,4 +55,8 @@ int dvb_get_string_with_len(char *dst, size_t dstlen, const uint8_t *buf, size_t buflen, const char *target_encoding); +#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f)) + +time_t dvb_convert_date(uint8_t *dvb_buf); + #endif /* DVB_SUPPORT_H */ diff --git a/dvb_tables.c b/dvb_tables.c new file mode 100644 index 00000000..45bacd69 --- /dev/null +++ b/dvb_tables.c @@ -0,0 +1,659 @@ +/* + * 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 "tvhead.h" +#include "dispatch.h" +#include "dvb.h" +#include "dvb_support.h" +#include "epg.h" +#include "transports.h" +#include "channels.h" +#include "psi.h" + +#define TDT_NOW 0x1 + +/** + * + */ +void +dvb_tdt_destroy(th_dvb_table_t *tdt) +{ + free(tdt->tdt_fparams); + LIST_REMOVE(tdt, tdt_link); + close(dispatch_delfd(tdt->tdt_handle)); + free(tdt->tdt_name); + free(tdt); +} + + +/** + * + */ +static void +dvb_table_recv(int events, void *opaque, int fd) +{ + th_dvb_table_t *tdt = opaque; + uint8_t sec[4096], *ptr; + int r, len; + uint8_t tableid; + + if(!(events & DISPATCH_READ)) + return; + + r = read(fd, sec, sizeof(sec)); + if(r < 3) + return; + + r -= 3; + + tableid = sec[0]; + len = ((sec[1] & 0x0f) << 8) | sec[2]; + + if(len < r) + return; + + ptr = &sec[3]; + len -= 3; + if(!tdt->tdt_callback(tdt->tdt_tdmi, ptr, len, tableid, tdt->tdt_opaque)) + tdt->tdt_count++; + + tdmi_check_scan_status(tdt->tdt_tdmi); +} + + + + + +/** + * Add a new DVB table + */ +static void +tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams, + int (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, + uint8_t tableid, void *opaque), void *opaque, + int initial_count, char *name, int flags) +{ + th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + th_dvb_table_t *tdt; + int fd; + + if((fd = open(tda->tda_demux_path, O_RDWR)) == -1) + return; + + tdt = calloc(1, sizeof(th_dvb_table_t)); + tdt->tdt_fd = fd; + tdt->tdt_name = strdup(name); + tdt->tdt_callback = callback; + tdt->tdt_opaque = opaque; + tdt->tdt_tdmi = tdmi; + tdt->tdt_handle = dispatch_addfd(fd, dvb_table_recv, tdt, DISPATCH_READ); + tdt->tdt_count = initial_count; + + if(flags & TDT_NOW) { + ioctl(fd, DMX_SET_FILTER, fparams); + free(fparams); + } else { + tdt->tdt_fparams = fparams; + } + + pthread_mutex_lock(&tdmi->tdmi_table_lock); + LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link); + pthread_mutex_unlock(&tdmi->tdmi_table_lock); +} + + +/** + * DVB Descriptor; Short Event + */ +static int +dvb_desc_short_event(uint8_t *ptr, int len, + char *title, size_t titlelen, + char *desc, size_t desclen) +{ + int r; + + if(len < 4) + return -1; + ptr += 3; len -= 3; + + if((r = dvb_get_string_with_len(title, titlelen, ptr, len, "UTF8")) < 0) + return -1; + ptr += r; len -= r; + + if((r = dvb_get_string_with_len(desc, desclen, ptr, len, "UTF8")) < 0) + return -1; + + return 0; +} + + +/** + * 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, + "UTF8")) < 0) + return -1; + ptr += r; len -= r; + + if((r = dvb_get_string_with_len(name, namelen, ptr, len, + "UTF8")) < 0) + return -1; + ptr += r; len -= r; + return 0; +} + + +/** + * DVB EIT (Event Information Table) + */ +static int +dvb_eit_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len, + uint8_t tableid, void *opaque) +{ + th_transport_t *t; + th_channel_t *ch; + + uint16_t serviceid; + int version; + int current_next_indicator; + uint8_t section_number; + uint8_t last_section_number; + uint16_t transport_stream_id; + uint16_t original_network_id; + uint8_t segment_last_section_number; + uint8_t last_table_id; + + uint16_t event_id; + time_t start_time; + + int duration; + int dllen; + uint8_t dtag, dlen; + + char title[256]; + char desc[5000]; + + if(tableid < 0x4e || tableid > 0x6f) + return -1; + + if(len < 11) + return -1; + + serviceid = ptr[0] << 8 | ptr[1]; + version = ptr[2] >> 1 & 0x1f; + current_next_indicator = ptr[2] & 1; + section_number = ptr[3]; + last_section_number = ptr[4]; + transport_stream_id = ptr[5] << 8 | ptr[6]; + original_network_id = ptr[7] << 8 | ptr[8]; + segment_last_section_number = ptr[9]; + last_table_id = ptr[10]; + + len -= 11; + ptr += 11; + + t = dvb_find_transport(tdmi, transport_stream_id, serviceid, 0); + if(t == NULL) + return -1; + ch = t->tht_channel; + if(ch == NULL) + return -1; + + epg_lock(); + + while(len >= 12) { + event_id = ptr[0] << 8 | ptr[1]; + start_time = dvb_convert_date(&ptr[2]); + duration = bcdtoint(ptr[7] & 0xff) * 3600 + + bcdtoint(ptr[8] & 0xff) * 60 + + bcdtoint(ptr[9] & 0xff); + dllen = ((ptr[10] & 0x0f) << 8) | ptr[11]; + + len -= 12; + ptr += 12; + + if(dllen > len) + break; + + while(dllen > 0) { + dtag = ptr[0]; + dlen = ptr[1]; + + len -= 2; ptr += 2; dllen -= 2; + + if(dlen > len) + break; + + switch(dtag) { + case DVB_DESC_SHORT_EVENT: + if(dvb_desc_short_event(ptr, dlen, + title, sizeof(title), + desc, sizeof(desc)) < 0) + duration = 0; + break; + } + + len -= dlen; ptr += dlen; dllen -= dlen; + } + + if(duration > 0) { + epg_update_event_by_id(ch, event_id, start_time, duration, + title, desc); + + } + } + + epg_unlock(); + 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) +{ + th_dvb_mux_t *tdm = tdmi->tdmi_mux; + th_transport_t *t; + int version; + int current_next_indicator; + uint8_t section_number; + uint8_t last_section_number; + uint16_t service_id; + uint16_t transport_stream_id; + uint16_t original_network_id; + + int reserved; + int running_status, free_ca_mode; + int dllen; + uint8_t dtag, dlen; + + char provider[256]; + char chname0[256], *chname; + uint8_t stype; + int ret = 0, l; + + if(tdm->tdm_network == NULL) + return -1; + + if(len < 8) + return -1; + + transport_stream_id = ptr[0] << 8 | ptr[1]; + version = ptr[2] >> 1 & 0x1f; + current_next_indicator = ptr[2] & 1; + section_number = ptr[3]; + last_section_number = ptr[4]; + original_network_id = ptr[5] << 8 | ptr[6]; + reserved = ptr[7]; + + len -= 8; + ptr += 8; + + + while(len >= 5) { + 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]; + + len -= 5; + ptr += 5; + + if(dllen > len) + break; + + stype = 0; + + chname = NULL; + + 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) { + stype = 0; + } else { + chname = chname0; + /* Some providers insert spaces */ + while(*chname <= 32 && *chname != 0) + chname++; + + l = strlen(chname); + while(l > 1 && chname[l - 1] <= 32) { + chname[l - 1] = 0; + l--; + } + + } + break; + } + + len -= dlen; ptr += dlen; dllen -= dlen; + } + + + if(chname != NULL) switch(stype) { + + case DVB_ST_SDTV: + case DVB_ST_HDTV: + case DVB_ST_AC_SDTV: + case DVB_ST_AC_HDTV: + /* TV service */ + + t = dvb_find_transport(tdmi, transport_stream_id, service_id, 0); + + if(t == NULL) { + ret |= 1; + } else { + t->tht_scrambled = free_ca_mode; + + free((void *)t->tht_provider); + t->tht_provider = strdup(provider); + + free((void *)t->tht_network); + t->tht_network = strdup(tdm->tdm_network); + + if(t->tht_channel == NULL) { + /* Not yet mapped to a channel */ + if(LIST_FIRST(&t->tht_streams) != NULL) { + /* We have streams, map it */ + if(t->tht_scrambled) + t->tht_prio = 75; + else + t->tht_prio = 25; + + transport_set_channel(t, channel_find(chname, 1, NULL)); + } else { + if(t->tht_pmt_seen == 0) + ret |= 1; /* Return error (so scanning wont continue yet) */ + } + } + } + break; + + } + } + return ret; +} + + +/** + * 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) +{ + uint16_t service, pmt, tid; + + if(len < 5) + return -1; + + tid = (ptr[0] << 8) | ptr[1]; + + ptr += 5; + len -= 5; + + while(len >= 4) { + service = ptr[0] << 8 | ptr[1]; + pmt = (ptr[2] & 0x1f) << 8 | ptr[3]; + + if(service != 0) + dvb_find_transport(tdmi, tid, service, pmt); + + ptr += 4; + len -= 4; + } + return 0; +} + + +/** + * CAT - Condition 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 caid; + uint16_t pid; + + 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]; + break; + + default: + break; + } + + ptr += tlen; + len -= tlen; + } + return 0; +} + + +/** + * 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 tag, tlen; + int ntl; + char networkname[256]; + th_dvb_mux_t *tdm = tdmi->tdmi_mux; + + + ptr += 5; + len -= 5; + + if(tableid != 0x40) + return -1; + + ntl = ((ptr[0] & 0xf) << 8) | ptr[1]; + ptr += 2; + len -= 2; + if(ntl > len) + return 0; + + while(ntl > 2) { + tag = *ptr++; + tlen = *ptr++; + len -= 2; + ntl -= 2; + + switch(tag) { + case DVB_DESC_NETWORK_NAME: + if(dvb_get_string(networkname, sizeof(networkname), ptr, tlen, "UTF8")) + return 0; + + if(tdm->tdm_network != NULL) { + if(strcmp(tdm->tdm_network, networkname)) { + syslog(LOG_ALERT, + "DVB Mux conflict \"%s\" on \"%s\" says it's \"%s\", " + "but is has alrady been reported as \"%s\"", + tdm->tdm_name, + tdmi->tdmi_adapter->tda_path, + networkname, + tdm->tdm_network); + } + } else { + tdm->tdm_network = strdup(networkname); + } + break; + } + + ptr += tlen; + len -= tlen; + ntl -= tlen; + } + + 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) +{ + th_transport_t *t = opaque; + + return psi_parse_pmt(t, ptr, len, 1); +} + + + +/** + * Helper for preparing a section filter parameter struct + */ +struct dmx_sct_filter_params * +dvb_fparams_alloc(int pid, int flags) +{ + struct dmx_sct_filter_params *p; + + p = calloc(1, sizeof(struct dmx_sct_filter_params)); + p->pid = pid; + p->timeout = 0; + p->flags = flags; + return p; +} + + + +/** + * Setup FD + demux for default DVB 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(0x0, DMX_IMMEDIATE_START | DMX_CHECK_CRC); + fp->filter.filter[0] = 0x00; + fp->filter.mask[0] = 0xff; + tdt_add(tdmi, fp, dvb_pat_callback, NULL, 0, "pat", 0); + + /* Conditional Access Table */ + + fp = dvb_fparams_alloc(0x1, DMX_IMMEDIATE_START | DMX_CHECK_CRC); + fp->filter.filter[0] = 0x1; + fp->filter.mask[0] = 0xff; + tdt_add(tdmi, fp, dvb_cat_callback, NULL, 1, "cat", 0); + + /* Network Information Table */ + + fp = dvb_fparams_alloc(0x10, DMX_IMMEDIATE_START | DMX_CHECK_CRC); + tdt_add(tdmi, fp, dvb_nit_callback, NULL, 0, "nit", 0); + + /* Service Descriptor Table */ + + fp = dvb_fparams_alloc(0x11, DMX_IMMEDIATE_START | DMX_CHECK_CRC); + fp->filter.filter[0] = 0x42; + fp->filter.mask[0] = 0xff; + tdt_add(tdmi, fp, dvb_sdt_callback, NULL, 0, "sdt", 0); + + /* Event Information table */ + + fp = dvb_fparams_alloc(0x12, DMX_IMMEDIATE_START | DMX_CHECK_CRC); + tdt_add(tdmi, fp, dvb_eit_callback, NULL, 1, "eit", 0); +} + + +/** + * Setup FD + demux for a services PMT + */ +void +dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t, + int pmt_pid) +{ + struct dmx_sct_filter_params *fp; + char pmtname[100]; + + snprintf(pmtname, sizeof(pmtname), "PMT(%d), service:%d", + pmt_pid, t->tht_dvb_service_id); + + fp = dvb_fparams_alloc(pmt_pid, DMX_IMMEDIATE_START | DMX_CHECK_CRC); + fp->filter.filter[0] = 0x02; + fp->filter.mask[0] = 0xff; + tdt_add(tdmi, fp, dvb_pmt_callback, t, 0, pmtname, TDT_NOW); +} diff --git a/tvhead.h b/tvhead.h index 43b89d5d..ffa5cf9c 100644 --- a/tvhead.h +++ b/tvhead.h @@ -159,6 +159,8 @@ typedef struct th_dvb_mux_instance { time_t tdmi_time; LIST_HEAD(, th_dvb_table) tdmi_tables; + pthread_mutex_t tdmi_table_lock; + tdmi_state_t tdmi_state; dtimer_t tdmi_initial_scan_timer; @@ -181,6 +183,9 @@ typedef struct th_dvb_table { void *tdt_opaque; int (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len, uint8_t tableid, void *opaque); + + int tdt_fd; + struct dmx_sct_filter_params *tdt_fparams; } th_dvb_table_t;