]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
linuxdvb: rewrite PID subscription (limit used PID at once)
authorJaroslav Kysela <perex@perex.cz>
Tue, 28 Apr 2015 12:43:14 +0000 (14:43 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 28 Apr 2015 12:48:32 +0000 (14:48 +0200)
src/input/mpegts.h
src/input/mpegts/linuxdvb/linuxdvb_frontend.c
src/input/mpegts/linuxdvb/linuxdvb_private.h
src/input/mpegts/mpegts_input.c
src/input/mpegts/mpegts_mux.c

index f81f0f777d1676174312cc47a90d43c4f5c74701..1ea32a6e44bc5e8c13be661a1326458fdb6e4f2a 100644 (file)
@@ -173,7 +173,6 @@ typedef struct mpegts_pid
 {
   int                      mp_pid;
   int                      mp_type; // mask for all subscribers
-  int                      mp_fd;   // linuxdvb demux fd
   int8_t                   mp_cc;
   RB_HEAD(,mpegts_pid_sub) mp_subs; // subscribers to pid
   LIST_HEAD(,mpegts_pid_sub) mp_svc_subs;
@@ -1035,15 +1034,6 @@ LIST_HEAD(,mpegts_listener) mpegts_listeners;
     if (ml->op) ml->op(t, ml->ml_opaque, arg1);\
 } (void)0
 
-/*
- * Misc
- */
-#if ENABLE_LINUXDVB
-void linuxdvb_filter_close ( int fd );
-#else
-static inline void linuxdvb_filter_close ( int fd ) { assert(0); };
-#endif
-
 #endif /* __TVH_MPEGTS_H__ */
 
 /******************************************************************************
index baac4ef3402d71fb29af9862078e5684ef8b57e3..1bdcc76e5513baa985d14cece0c3aa8f7aac4da6 100644 (file)
@@ -86,6 +86,14 @@ const idclass_t linuxdvb_frontend_class =
       .opts     = PO_RDONLY | PO_NOSAVE,
       .off      = offsetof(linuxdvb_frontend_t, lfe_number),
     },
+    {
+      .type     = PT_INT,
+      .id       = "pids_max",
+      .name     = "Maximum PIDs ",
+      .off      = offsetof(linuxdvb_frontend_t, lfe_pids_max),
+      .opts     = PO_ADVANCED,
+      .def.i    = 32
+    },
     {
       .type     = PT_BOOL,
       .id       = "powersave",
@@ -333,6 +341,7 @@ linuxdvb_frontend_stop_mux
 
   lfe->lfe_in_setup = 0;
   lfe->lfe_freq = 0;
+  mpegts_pid_done(&lfe->lfe_pids);
 }
 
 static int
@@ -353,79 +362,51 @@ linuxdvb_frontend_start_mux
   return res;
 }
 
-void
-linuxdvb_filter_close
-  ( int fd )
-{
-  close(fd);
-}
-
-static void
-linuxdvb_frontend_open_pid0
-  ( linuxdvb_frontend_t *lfe, mpegts_pid_t *mp )
+static mpegts_pid_t *
+linuxdvb_frontend_open_pid
+  ( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, int weight, void *owner )
 {
-  char name[256];
-  struct dmx_pes_filter_params dmx_param;
-  int fd;
-
-  /* Do not open internal PIDs */
-  if (mp->mp_pid > MPEGTS_FULLMUX_PID)
-    return;
-
-  /* Already opened */
-  if (mp->mp_fd != -1)
-    return;
-
-  /* Not locked */
-  if (!lfe->lfe_locked)
-    return;
-
-  lfe->mi_display_name((mpegts_input_t*)lfe, name, sizeof(name));
+  mpegts_pid_t *mp;
+  linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi;
+  int change = 0;
 
-  /* Open DMX */
-  fd = tvh_open(lfe->lfe_dmx_path, O_RDWR, 0);
-  if(fd == -1) {
-    tvherror("linuxdvb", "%s - failed to open dmx for pid %d [e=%s]",
-             name, mp->mp_pid, strerror(errno));
-    return;
+  if (pid < MPEGTS_FULLMUX_PID)
+    change = mpegts_pid_add(&lfe->lfe_pids, pid, weight) >= 0;
+  else if (pid == MPEGTS_FULLMUX_PID) {
+    change = lfe->lfe_pids.all == 0;
+    lfe->lfe_pids.all = 1;
   }
 
-  /* Install filter */
-  tvhtrace("linuxdvb", "%s - open PID %04X (%d) fd %d",
-           name, mp->mp_pid, mp->mp_pid, fd);
-  memset(&dmx_param, 0, sizeof(dmx_param));
-  dmx_param.pid      = mp->mp_pid;
-  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(fd, DMX_SET_PES_FILTER, &dmx_param)) {
-    tvherror("linuxdvb", "%s - failed to config dmx for pid %d [e=%s]",
-             name, mp->mp_pid, strerror(errno));
-    close(fd);
-    return;
-  }
+  if (!(mp = mpegts_input_open_pid(mi, mm, pid, type, weight, owner)))
+    return NULL;
 
-  /* Store */
-  mp->mp_fd = fd;
+  if (change && lfe->lfe_dvr_pipe.wr > 0)
+    tvh_write(lfe->lfe_dvr_pipe.wr, "c", 1);
 
-  return;
+  return mp;
 }
 
-static mpegts_pid_t *
-linuxdvb_frontend_open_pid
+static int
+linuxdvb_frontend_close_pid
   ( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, int weight, void *owner )
 {
-  mpegts_pid_t *mp;
   linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi;
+  int change = 0, r;
 
-  if (!(mp = mpegts_input_open_pid(mi, mm, pid, type, weight, owner)))
-    return NULL;
+  if (pid < MPEGTS_FULLMUX_PID)
+    change = mpegts_pid_del(&lfe->lfe_pids, pid, weight) >= 0;
+  else if (pid == MPEGTS_FULLMUX_PID) {
+    change = lfe->lfe_pids.all != 0;
+    lfe->lfe_pids.all = 0;
+  }
 
-  linuxdvb_frontend_open_pid0(lfe, mp);
+  if ((r = mpegts_input_close_pid(mi, mm, pid, type, weight, owner)) <= 0)
+    return r;
 
-  return mp;
+  if (change)
+    tvh_write(lfe->lfe_dvr_pipe.wr, "c", 1);
+
+  return r;
 }
 
 static idnode_set_t *
@@ -497,7 +478,6 @@ linuxdvb_frontend_monitor ( void *aux )
   linuxdvb_frontend_t *lfe = aux;
   mpegts_mux_instance_t *mmi = LIST_FIRST(&lfe->mi_mux_active);
   mpegts_mux_t *mm;
-  mpegts_pid_t *mp;
   fe_status_t fe_status;
   signal_state_t status;
   signal_status_t sigstat;
@@ -607,12 +587,6 @@ linuxdvb_frontend_monitor ( void *aux )
       /* Table handlers */
       linuxdvb_frontend_default_tables(lfe, (dvb_mux_t*)mm);
 
-      /* Locked - ensure everything is open */
-      pthread_mutex_lock(&lfe->mi_output_lock);
-      RB_FOREACH(mp, &mm->mm_pids, mp_link)
-        linuxdvb_frontend_open_pid0(lfe, mp);
-      pthread_mutex_unlock(&lfe->mi_output_lock);
-
     /* Re-arm (quick) */
     } else {
       gtimer_arm_ms(&lfe->lfe_monitor_timer, linuxdvb_frontend_monitor,
@@ -867,25 +841,138 @@ linuxdvb_frontend_monitor ( void *aux )
   }
 }
 
+typedef struct linuxdvb_pid {
+  int fd;
+  int pid;
+} linuxdvb_pid_t;
+
+static void
+linuxdvb_frontend_open_pid0
+  ( linuxdvb_frontend_t *lfe, const char *name,
+    linuxdvb_pid_t *pids, int pids_size, int pid )
+{
+  struct dmx_pes_filter_params dmx_param;
+  int fd;
+
+  for ( ; pids_size > 0 && pids->fd >= 0; pids_size--, pids++);
+  if (pids_size == 0) {
+    tvherror("linuxdvb", "%s - maximum PID count reached, pid %d ignored",
+             name, pid);
+    return;
+  }
+
+  /* Open DMX */
+  fd = tvh_open(lfe->lfe_dmx_path, O_RDWR, 0);
+  if(fd == -1) {
+    tvherror("linuxdvb", "%s - failed to open dmx for pid %d [e=%s]",
+             name, pid, strerror(errno));
+    return;
+  }
+
+  /* Install filter */
+  tvhtrace("linuxdvb", "%s - open PID %04X (%d) fd %d", name, pid, pid, fd);
+  memset(&dmx_param, 0, sizeof(dmx_param));
+  dmx_param.pid      = pid;
+  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(fd, DMX_SET_PES_FILTER, &dmx_param)) {
+    tvherror("linuxdvb", "%s - failed to config dmx for pid %d [e=%s]",
+             name, pid, strerror(errno));
+    close(fd);
+    return;
+  }
+
+  /* Store */
+  pids->fd = fd;
+  pids->pid = pid;
+}
+
+static void
+linuxdvb_frontend_close_pid0
+  ( linuxdvb_frontend_t *lfe, const char *name,
+    linuxdvb_pid_t *pids, int pids_size, int pid )
+{
+  for ( ; pids_size > 0 && pids->pid != pid;
+          pids_size--, pids++);
+  if (pids_size == 0)
+    return;
+  close(pids->fd);
+  pids->fd = -1;
+  pids->pid = -1;
+}
+
+static int
+linuxdvb_pid_exists ( linuxdvb_pid_t *pids, int pids_size, int pid )
+{
+  int i;
+  for (i = 0; i < pids_size; i++)
+    if (pids[i].fd >= 0 && pids[i].pid == pid)
+      return 1;
+  return 0;
+}
+
+static void
+linuxdvb_update_pids ( linuxdvb_frontend_t *lfe, const char *name,
+                       mpegts_apids_t *tuned, linuxdvb_pid_t *pids,
+                       int pids_size )
+{
+  mpegts_apids_t wpid, padd, pdel;
+  int i, max = MAX(16, lfe->lfe_pids_max);
+
+  pthread_mutex_lock(&lfe->lfe_dvr_lock);
+
+  if (!lfe->lfe_pids.all) {
+    mpegts_pid_weighted(&wpid, &lfe->lfe_pids, max);
+    mpegts_pid_compare(&wpid, tuned, &padd, &pdel);
+    mpegts_pid_done(&wpid);
+    for (i = 0; i < pdel.count; i++)
+      linuxdvb_frontend_close_pid0(lfe, name, pids, pids_size, pdel.pids[i].pid);
+    for (i = 0; i < padd.count; i++)
+      linuxdvb_frontend_open_pid0(lfe, name, pids, pids_size, padd.pids[i].pid);
+    mpegts_pid_done(&padd);
+    mpegts_pid_done(&pdel);
+  }
+
+  if (lfe->lfe_pids.all && !linuxdvb_pid_exists(pids, pids_size, MPEGTS_FULLMUX_PID))
+    linuxdvb_frontend_open_pid0(lfe, name, pids, pids_size, MPEGTS_FULLMUX_PID);
+  else if (!lfe->lfe_pids.all && linuxdvb_pid_exists(pids, pids_size, MPEGTS_FULLMUX_PID))
+    linuxdvb_frontend_close_pid0(lfe, name, pids, pids_size, MPEGTS_FULLMUX_PID);
+
+  mpegts_pid_done(tuned);
+  mpegts_pid_weighted(tuned, &lfe->lfe_pids, max);
+  tuned->all = lfe->lfe_pids.all;
+
+  pthread_mutex_unlock(&lfe->lfe_dvr_lock);
+}
+
 static void *
 linuxdvb_frontend_input_thread ( void *aux )
 {
   linuxdvb_frontend_t *lfe = aux;
   mpegts_mux_instance_t *mmi;
-  int dvr = -1;
-  char buf[256];
-  int nfds;
+  char name[256], b;
   tvhpoll_event_t ev[2];
   tvhpoll_t *efd;
   ssize_t n;
   size_t skip = (MIN(lfe->lfe_skip_bytes, 1024*1024) / 188) * 188;
   size_t counter = 0;
+  linuxdvb_pid_t pids[128];
+  mpegts_apids_t tuned;
   sbuf_t sb;
-  int nodata = 4;
+  int i, dvr = -1, nfds, nodata = 4;
+
+  mpegts_pid_init(&tuned);
+  for (i = 0; i < ARRAY_SIZE(pids); i++) {
+    pids[i].fd = -1;
+    pids[i].pid = -1;
+  }
 
   /* Get MMI */
   pthread_mutex_lock(&lfe->lfe_dvr_lock);
-  lfe->mi_display_name((mpegts_input_t*)lfe, buf, sizeof(buf));
+  lfe->mi_display_name((mpegts_input_t*)lfe, name, sizeof(name));
   mmi = LIST_FIRST(&lfe->mi_mux_active);
   pthread_cond_signal(&lfe->lfe_dvr_cond);
   pthread_mutex_unlock(&lfe->lfe_dvr_lock);
@@ -894,7 +981,7 @@ linuxdvb_frontend_input_thread ( void *aux )
   /* Open DVR */
   dvr = tvh_open(lfe->lfe_dvr_path, O_RDONLY | O_NONBLOCK, 0);
   if (dvr < 0) {
-    tvherror("linuxdvb", "%s - failed to open %s", buf, lfe->lfe_dvr_path);
+    tvherror("linuxdvb", "%s - failed to open %s", name, lfe->lfe_dvr_path);
     return NULL;
   }
 
@@ -910,12 +997,15 @@ linuxdvb_frontend_input_thread ( void *aux )
   /* Allocate memory */
   sbuf_init_fixed(&sb, MIN(MAX(18800, lfe->lfe_ibuf_size), 1880000));
 
+  /* Subscribe PIDs */
+  linuxdvb_update_pids(lfe, name, &tuned, pids, ARRAY_SIZE(pids));
+
   /* Read */
   while (tvheadend_running) {
     nfds = tvhpoll_wait(efd, ev, 1, 150);
     if (nfds == 0) { /* timeout */
       if (nodata == 0) {
-        tvhlog(LOG_WARNING, "linuxdvb", "%s - poll TIMEOUT", buf);
+        tvhlog(LOG_WARNING, "linuxdvb", "%s - poll TIMEOUT", name);
         nodata = 50;
         lfe->lfe_nodata = 1;
       } else {
@@ -923,6 +1013,15 @@ linuxdvb_frontend_input_thread ( void *aux )
       }
     }
     if (nfds < 1) continue;
+    if (ev[0].data.fd == lfe->lfe_dvr_pipe.rd) {
+      if (read(lfe->lfe_dvr_pipe.rd, &b, 1) > 0) {
+        if (b == 'c')
+          linuxdvb_update_pids(lfe, name, &tuned, pids, ARRAY_SIZE(pids));
+        else
+          break;
+      }
+      continue;
+    }
     if (ev[0].data.fd != dvr) break;
 
     nodata = 50;
@@ -933,11 +1032,11 @@ linuxdvb_frontend_input_thread ( void *aux )
       if (ERRNO_AGAIN(errno))
         continue;
       if (errno == EOVERFLOW) {
-        tvhlog(LOG_WARNING, "linuxdvb", "%s - read() EOVERFLOW", buf);
+        tvhlog(LOG_WARNING, "linuxdvb", "%s - read() EOVERFLOW", name);
         continue;
       }
       tvhlog(LOG_ERR, "linuxdvb", "%s - read() error %d (%s)",
-             buf, errno, strerror(errno));
+             name, errno, strerror(errno));
       break;
     }
 
@@ -957,6 +1056,10 @@ linuxdvb_frontend_input_thread ( void *aux )
 
   sbuf_free(&sb);
   tvhpoll_destroy(efd);
+  for (i = 0; i < ARRAY_SIZE(pids); i++)
+    if (pids[i].fd >= 0)
+      close(pids[i].fd);
+  mpegts_pid_done(&tuned);
   close(dvr);
   return NULL;
 }
@@ -1477,6 +1580,7 @@ linuxdvb_frontend_create
   lfe->lfe_name[sizeof(lfe->lfe_name)-1] = '\0';
   lfe->lfe_ibuf_size = 18800;
   lfe->lfe_status_period = 1000;
+  lfe->lfe_pids_max = 32;
   lfe = (linuxdvb_frontend_t*)mpegts_input_create0((mpegts_input_t*)lfe, idc, uuid, conf);
   if (!lfe) return NULL;
 
@@ -1502,6 +1606,7 @@ linuxdvb_frontend_create
   lfe->mi_stop_mux        = linuxdvb_frontend_stop_mux;
   lfe->mi_network_list    = linuxdvb_frontend_network_list;
   lfe->mi_open_pid        = linuxdvb_frontend_open_pid;
+  lfe->mi_close_pid       = linuxdvb_frontend_close_pid;
   lfe->mi_enabled_updated = linuxdvb_frontend_enabled_updated;
 
   /* Adapter link */
@@ -1511,6 +1616,7 @@ linuxdvb_frontend_create
   /* DVR lock/cond */
   pthread_mutex_init(&lfe->lfe_dvr_lock, NULL);
   pthread_cond_init(&lfe->lfe_dvr_cond, NULL);
+  mpegts_pid_init(&lfe->lfe_pids);
  
   /* Satconf */
   if (conf) {
index 559eaa03a79fa67ff7282b7e8a42f46b96413e1d..0522d15f55a2812812a9277bd8e5783004ded7df 100644 (file)
@@ -120,6 +120,8 @@ struct linuxdvb_frontend
   th_pipe_t                 lfe_dvr_pipe;
   pthread_mutex_t           lfe_dvr_lock;
   pthread_cond_t            lfe_dvr_cond;
+  mpegts_apids_t            lfe_pids;
+  int                       lfe_pids_max;
  
   /*
    * Tuning
index 2baa3f1ce532e9c1e32b7072445c2b2c25d7ea3f..e2e9ad6d3f825269741545615c71a768bdd3f637 100644 (file)
@@ -517,8 +517,6 @@ mpegts_input_close_pid
   }
   if (!RB_FIRST(&mp->mp_subs)) {
     RB_REMOVE(&mm->mm_pids, mp, mp_link);
-    if (mp->mp_fd != -1)
-      linuxdvb_filter_close(mp->mp_fd);
     free(mp);
     return 1;
   } else {
index b6bc532d470f2962839006c3e9a696eba0abba95..b4fc44fe74bba4cb88dff84d18e3d92a808b67c7 100644 (file)
@@ -761,8 +761,6 @@ mpegts_mux_stop ( mpegts_mux_t *mm, int force, int reason )
       }
     }
     RB_REMOVE(&mm->mm_pids, mp, mp_link);
-    if (mp->mp_fd != -1)
-      linuxdvb_filter_close(mp->mp_fd);
     free(mp);
   }
   pthread_mutex_unlock(&mi->mi_output_lock);
@@ -1257,7 +1255,6 @@ mpegts_mux_find_pid_ ( mpegts_mux_t *mm, int pid, int create )
       mp = calloc(1, sizeof(*mp));
       mp->mp_pid = pid;
       if (!RB_INSERT_SORTED(&mm->mm_pids, mp, mp_link, mp_cmp)) {
-        mp->mp_fd = -1;
         mp->mp_cc = -1;
       } else {
         free(mp);