]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Split dvb.c into table specific code and front-end specific code.
authorAndreas Öman <andreas@lonelycoder.com>
Mon, 10 Dec 2007 12:24:47 +0000 (12:24 +0000)
committerAndreas Öman <andreas@lonelycoder.com>
Mon, 10 Dec 2007 12:24:47 +0000 (12:24 +0000)
Revamp DVB fe manager

Makefile
dvb.c
dvb.h
dvb_fe.c [new file with mode: 0644]
dvb_muxconfig.c
dvb_support.c
dvb_support.h
dvb_tables.c [new file with mode: 0644]
tvhead.h

index f3e1abe2dc83b1dffc388585b999b0f5cf2888cd..9e0af3c4d8a14e5d71466fe545d9689203c920c4 100644 (file)
--- 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 fdff939634d5757aab7ae4aeea6065d7ed7070b9..97518379949d0fac18e0d9c4e5acba740e3a3a95 100644 (file)
--- 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;
-}
-
-
-
-
-
-/*
- *
- */
-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);
-}
-
-
-/*
- *
+/**
+ * Based on the gived transport id and service id, try to locate the transport
  */
-
 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);
-  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;
+  dvb_table_add_transport(tdmi, t, pmt_pid);
+  return t;
 }
 
 
-
-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 bcd47b9ce2c147a4128efb2cd0eb99044297e3ad..4ebdf8568f73fa824511de38f18195c4814cf6cf 100644 (file)
--- 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 (file)
index 0000000..3adf830
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+
+#include <libhts/htscfg.h>
+#include <ffmpeg/avstring.h>
+
+#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);
+}
index db37ac701b28766091ce271944aca2312a59941c..6c3970549058e521dbb3740e907bc226d794e830 100644 (file)
@@ -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;
index ccbaca09776409903668e5d84784b24e7f619dd5..80bf523e158972799a0e867a0694bf8237386786 100644 (file)
@@ -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));
+}
index bef1ca9ad13b477fa907f3892a9056e7c828dcd8..cbf4264bc4fb2edfd79172eb99ed6669826f77a4 100644 (file)
@@ -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 (file)
index 0000000..45bacd6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+
+#include <libhts/htscfg.h>
+#include <ffmpeg/avstring.h>
+
+#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);
+}
index 43b89d5dff88460e219a8defa7a30db6c6c00c01..ffa5cf9ce1c0610e42cac6d24831197d08c1610d 100644 (file)
--- 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;