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;