]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Fix #1643 - dvb: alter the way we handle dvb tuning
authorAdam Sutton <dev@adamsutton.me.uk>
Wed, 13 Mar 2013 19:36:59 +0000 (19:36 +0000)
committerAdam Sutton <dev@adamsutton.me.uk>
Tue, 26 Mar 2013 10:04:26 +0000 (10:04 +0000)
This ensures that demux filters are not installed until after tuning
is locked. This should resolve most stale data issues.
(cherry picked from commit ddc466c1bfa7405563a68a662114f5a3659d7cb5)

src/dvb/dvb.h
src/dvb/dvb_adapter.c
src/dvb/dvb_fe.c
src/dvb/dvb_service.c

index aaccd94600bc1ae077fbdc91361eb1ba0f519e7f..335ac3c05e8874bc3fa8576b52d9e58760868e09 100644 (file)
@@ -29,6 +29,12 @@ struct service;
 struct th_dvb_table;
 struct th_dvb_mux_instance;
 
+#define TDA_OPT_FE  0x1
+#define TDA_OPT_DVR 0x2
+#define TDA_OPT_DMX 0x4
+#define TDA_OPT_PWR 0x8
+#define TDA_OPT_ALL (TDA_OPT_FE | TDA_OPT_DVR | TDA_OPT_DMX | TDA_OPT_PWR)
+
 #define DVB_VER_INT(maj,min) (((maj) << 16) + (min))
 
 #define DVB_VER_ATLEAST(maj, min) \
@@ -202,6 +208,8 @@ typedef struct th_dvb_adapter {
 
   uint32_t tda_enabled;
 
+  int tda_locked;
+
   const char *tda_rootpath;
   char *tda_identifier;
   uint32_t tda_autodiscovery;
@@ -342,11 +350,9 @@ void dvb_adapter_init(uint32_t adapter_mask, const char *rawfile);
 
 void dvb_adapter_mux_scanner(void *aux);
 
-void dvb_adapter_start (th_dvb_adapter_t *tda);
-
-void dvb_adapter_stop (th_dvb_adapter_t *tda);
+void dvb_adapter_start (th_dvb_adapter_t *tda, int opt);
 
-void dvb_adapter_stop_dvr (th_dvb_adapter_t *tda);
+void dvb_adapter_stop (th_dvb_adapter_t *tda, int opt);
 
 void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s);
 
index 8b3878dc847d1e2e4cb941fc8725d19fc7d8dc31..02b3fb8b47f48c77ee8636a8377c14e140e796e3 100644 (file)
@@ -141,7 +141,7 @@ dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on)
     gtimer_disarm(&tda->tda_mux_scanner_timer);
     if (tda->tda_mux_current)
       dvb_fe_stop(tda->tda_mux_current, 0);
-    dvb_adapter_stop(tda);
+    dvb_adapter_stop(tda, TDA_OPT_ALL);
   } else {
     tda_init(tda);
   }
@@ -692,22 +692,26 @@ static void tda_init (th_dvb_adapter_t *tda)
  *
  */
 void
-dvb_adapter_start ( th_dvb_adapter_t *tda )
+dvb_adapter_start ( th_dvb_adapter_t *tda, int opt )
 {
   if(tda->tda_enabled == 0) {
     tvhlog(LOG_INFO, "dvb", "Adapter \"%s\" cannot be started - it's disabled", tda->tda_displayname);
     return;
   }
+  
+  /* Default to ALL */
+  if (!opt)
+    opt = TDA_OPT_ALL;
 
   /* Open front end */
-  if (tda->tda_fe_fd == -1) {
+  if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd == -1)) {
     tda->tda_fe_fd = tvh_open(tda->tda_fe_path, O_RDWR | O_NONBLOCK, 0);
     if (tda->tda_fe_fd == -1) return;
     tvhlog(LOG_DEBUG, "dvb", "%s opened frontend %s", tda->tda_rootpath, tda->tda_fe_path);
   }
 
   /* Start DVR thread */
-  if (tda->tda_dvr_pipe.rd == -1) {
+  if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd == -1)) {
     int err = tvh_pipe(O_NONBLOCK, &tda->tda_dvr_pipe);
     assert(err != -1);
     pthread_create(&tda->tda_dvr_thread, NULL, dvb_adapter_input_dvr, tda);
@@ -716,10 +720,14 @@ dvb_adapter_start ( th_dvb_adapter_t *tda )
 }
 
 void
-dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda )
+dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt )
 {
+  /* Poweroff */
+  if (opt & TDA_OPT_PWR)
+    dvb_adapter_poweroff(tda);
+
   /* Stop DVR thread */
-  if (tda->tda_dvr_pipe.rd != -1) {
+  if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd != -1)) {
     tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath);
     int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1);
     assert(!err);
@@ -729,26 +737,17 @@ dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda )
     tda->tda_dvr_pipe.rd = -1;
     tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath);
   }
-}
-
-void
-dvb_adapter_stop ( th_dvb_adapter_t *tda )
-{
-  /* Poweroff */
-  dvb_adapter_poweroff(tda);
 
-  /* Don't stop/close */
+  /* Don't close FE */
   if (!tda->tda_idleclose && tda->tda_enabled) return;
 
   /* Close front end */
-  if (tda->tda_fe_fd != -1) {
+  if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd != -1)) {
     tvhlog(LOG_DEBUG, "dvb", "%s closing frontend", tda->tda_rootpath);
     close(tda->tda_fe_fd);
     tda->tda_fe_fd = -1;
   }
 
-  dvb_adapter_stop_dvr(tda);
-  
   dvb_adapter_notify(tda);
 }
 
@@ -980,8 +979,39 @@ dvb_adapter_clean(th_dvb_adapter_t *tda)
     service_remove_subscriber(t, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN);
 }
 
+/**
+ * Install RAW PES filter
+ */
+static int
+dvb_adapter_raw_filter(th_dvb_adapter_t *tda)
+{
+  int dmx = -1;
+  struct dmx_pes_filter_params dmx_param;
 
+  dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0);
+  if(dmx == -1) {
+    tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s",
+           tda->tda_demux_path, strerror(errno));
+    return -1;
+  }
 
+  memset(&dmx_param, 0, sizeof(dmx_param));
+  dmx_param.pid      = 0x2000;
+  dmx_param.input    = DMX_IN_FRONTEND;
+  dmx_param.output   = DMX_OUT_TS_TAP;
+  dmx_param.pes_type = DMX_PES_OTHER;
+  dmx_param.flags    = DMX_IMMEDIATE_START;
+
+  if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param) == -1) {
+    tvhlog(LOG_ERR, "dvb",
+    "Unable to configure demuxer \"%s\" for all PIDs -- %s",
+    tda->tda_demux_path, strerror(errno));
+    close(dmx);
+    return -1;
+  }
+
+  return dmx;
+}
 
 /**
  *
@@ -990,54 +1020,18 @@ static void *
 dvb_adapter_input_dvr(void *aux)
 {
   th_dvb_adapter_t *tda = aux;
-  int fd, i, r, c, efd, nfds, dmx = -1;
+  th_dvb_mux_instance_t *tdmi;
+  int fd = -1, i, r, c, efd, nfds, dmx = -1;
   uint8_t tsb[188 * 10];
   service_t *t;
   struct epoll_event ev;
-
-  fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0);
-  if(fd == -1) {
-    tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", tda->tda_dvr_path, strerror(errno));
-    return NULL;
-  }
-
-  if(tda->tda_rawmode) {
-
-    // Receive unfiltered raw transport stream
-
-    dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0);
-    if(dmx == -1) {
-      tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", 
-            tda->tda_demux_path, strerror(errno));
-      close(fd);
-      return NULL;
-    }
-
-    struct dmx_pes_filter_params dmx_param;
-
-    memset(&dmx_param, 0, sizeof(dmx_param));
-    dmx_param.pid = 0x2000;
-    dmx_param.input = DMX_IN_FRONTEND;
-    dmx_param.output = DMX_OUT_TS_TAP;
-    dmx_param.pes_type = DMX_PES_OTHER;
-    dmx_param.flags = DMX_IMMEDIATE_START;
-  
-    if(ioctl(dmx, DMX_SET_PES_FILTER, &dmx_param)) {
-      tvhlog(LOG_ERR, "dvb",
-            "Unable to configure demuxer \"%s\" for all PIDs -- %s",
-            tda->tda_demux_path, strerror(errno));
-      close(dmx);
-      close(fd);
-      return NULL;
-    }
-  }
+  int delay = 10, locked = 0;
+  fe_status_t festat;
 
   /* Create poll */
   efd = epoll_create(2);
   memset(&ev, 0, sizeof(ev));
   ev.events  = EPOLLIN;
-  ev.data.fd = fd;
-  epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
   ev.data.fd = tda->tda_dvr_pipe.rd;
   epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe.rd, &ev);
 
@@ -1045,10 +1039,65 @@ dvb_adapter_input_dvr(void *aux)
   while(1) {
 
     /* Wait for input */
-    nfds = epoll_wait(efd, &ev, 1, -1);
+    nfds = epoll_wait(efd, &ev, 1, delay);
+
+    /* Exit */
+    if ((nfds > 0) && (ev.data.fd != fd)) break;
+
+    /* Check for lock */
+    if (!locked) {
+      if (ioctl(tda->tda_fe_fd, FE_READ_STATUS, &festat))
+        continue;
+      if (!(festat & FE_HAS_LOCK))
+        continue;
+
+      /* Open DVR */
+      fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0);
+      if (fd == -1) {
+        tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s",
+               tda->tda_dvr_path, strerror(errno));
+        break;
+      }
+      ev.data.fd = fd;
+      epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
+
+      /* Note: table handlers must be installed with global lock */
+      pthread_mutex_lock(&global_lock);
+      tda->tda_locked = locked = 1;
+      delay           = -1;
+      if ((tdmi = tda->tda_mux_current)) {
+
+        /* Install table handlers */
+        dvb_table_add_default(tdmi);
+        epggrab_mux_start(tdmi);
+
+        /* Raw filter */
+        if(tda->tda_rawmode)
+          dmx = dvb_adapter_raw_filter(tda);
+
+        /* Service filters */
+        pthread_mutex_lock(&tda->tda_delivery_mutex);
+        LIST_FOREACH(t, &tda->tda_transports, s_active_link) {
+          if (t->s_dvb_mux_instance == tdmi) {
+            tda->tda_open_service(tda, t);
+            dvb_table_add_pmt(tdmi, t->s_pmt_pid);
+          }
+        }
+        pthread_mutex_unlock(&tda->tda_delivery_mutex);
+      }
+      pthread_mutex_unlock(&global_lock);
+
+      /* Error */
+      if (tda->tda_rawmode && (dmx == -1)) {
+        tvhlog(LOG_ALERT, "dvb", "Unable to install raw mux filter");
+        break;
+      }
+    }
+
+    /* No data */
     if (nfds < 1) continue;
-    if (ev.data.fd != fd) break;
 
+    /* Read data */
     c = read(fd, tsb+r, sizeof(tsb)-r);
     if (c < 0) {
       if (errno == EAGAIN || errno == EINTR)
index 77f7621731266587b827c1e4b03369f721048511..d0924c691ea36dc5667a3d5676d9a31fa412205b 100644 (file)
@@ -273,6 +273,7 @@ void
 dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune)
 {
   th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+  dvb_table_feed_t *dtf;
 
   lock_assert(&global_lock);
 
@@ -285,8 +286,13 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune)
     dvb_mux_save(tdmi);
   }
 
-  dvb_adapter_stop_dvr(tda);
+  dvb_adapter_stop(tda, TDA_OPT_DVR);
+  pthread_mutex_lock(&tda->tda_delivery_mutex);
+  while((dtf = TAILQ_FIRST(&tda->tda_table_feed)))
+    TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link);
+  pthread_mutex_unlock(&tda->tda_delivery_mutex);
   dvb_table_flush_all(tdmi);
+  tda->tda_locked      = 0;
 
   assert(tdmi->tdmi_scan_queue == NULL);
 
@@ -300,7 +306,7 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune)
 
   if (!retune) {
     gtimer_disarm(&tda->tda_fe_monitor_timer);
-    dvb_adapter_stop(tda);
+    dvb_adapter_stop(tda, TDA_OPT_ALL);
   }
 }
 
@@ -421,9 +427,11 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
   char buf[256];
   int r;
  
-
   lock_assert(&global_lock);
 
+  if(tda->tda_enabled == 0)
+    return SM_CODE_TUNING_FAILED;
+
   if(tda->tda_mux_current == tdmi)
     return 0;
   
@@ -434,7 +442,8 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
 
   if(tda->tda_mux_current != NULL)
     dvb_fe_stop(tda->tda_mux_current, 1);
-  dvb_adapter_start(tda);
+
+  dvb_adapter_start(tda, TDA_OPT_FE | TDA_OPT_PWR);
       
   if(tda->tda_type == FE_QPSK) {
        
@@ -486,7 +495,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
 
   tda->tda_fe_monitor_hold = 4;
 
-
 #if DVB_API_VERSION >= 5
   if (tda->tda_type == FE_QPSK) {
     tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning via s2api to \"%s\" (%d, %d Baud, "
@@ -517,15 +525,18 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
     if (errno == EINVAL)
       dvb_mux_set_enable(tdmi, 0);
     return SM_CODE_TUNING_FAILED;
-  }   
+  }
 
   tda->tda_mux_current = tdmi;
 
-  gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
+  dvb_adapter_start(tda, TDA_OPT_ALL);
 
+  gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
 
+#if 0
   dvb_table_add_default(tdmi);
   epggrab_mux_start(tdmi);
+#endif
 
   dvb_adapter_notify(tda);
   return 0;
index 61242beff06f316cc370de19035d860be09f0162..31e25e64cdaed9c6c38909b3b1a44190b4178f62 100644 (file)
@@ -86,18 +86,21 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start)
     dvb_adapter_clean(tda);
   }
 
+  r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start");
+
   pthread_mutex_lock(&tda->tda_delivery_mutex);
 
-  r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start");
   if(!r)
     LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link);
 
   pthread_mutex_unlock(&tda->tda_delivery_mutex);
 
-  if(!r)
-    tda->tda_open_service(tda, t);
+  if (tda->tda_locked) {
+    if(!r)
+      tda->tda_open_service(tda, t);
 
-  dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid);
+    dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid);
+  }
 
   return r;
 }