]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
bouquet: do not keep services for not enabled bouquets, fix bouquet delete
authorJaroslav Kysela <perex@perex.cz>
Thu, 6 Nov 2014 13:29:51 +0000 (14:29 +0100)
committerJaroslav Kysela <perex@perex.cz>
Sat, 8 Nov 2014 20:05:39 +0000 (21:05 +0100)
src/bouquet.c
src/bouquet.h
src/input/mpegts.h
src/input/mpegts/dvb.h
src/input/mpegts/dvb_psi.c
src/input/mpegts/dvb_support.c
src/input/mpegts/mpegts_mux.c
src/input/mpegts/mpegts_network_scan.c
src/webui/static/app/cteditor.js

index 9bd677b52778d38d8a2e2a0bf9f7c6469edc3aa0..7cca266b13412f6e85c4755698ddb3d2f9f07132 100644 (file)
@@ -251,6 +251,9 @@ bouquet_add_service(bouquet_t *bq, service_t *s, uint64_t lcn)
 
   lock_assert(&global_lock);
 
+  if (!bq->bq_enabled)
+    return;
+
   if (!idnode_set_exists(bq->bq_services, &s->s_id)) {
     tvhtrace("bouquet", "add service %s to %s", s->s_nicename, bq->bq_name ?: "<unknown>");
     idnode_set_add(bq->bq_services, &s->s_id, NULL);
@@ -317,7 +320,7 @@ bouquet_remove_service(bouquet_t *bq, service_t *s)
  *
  */
 void
-bouquet_completed(bouquet_t *bq)
+bouquet_completed(bouquet_t *bq, uint32_t seen)
 {
   idnode_set_t *remove;
   service_t *s;
@@ -327,9 +330,17 @@ bouquet_completed(bouquet_t *bq)
   if (!bq)
     return;
 
-  tvhtrace("bouquet", "%s: completed: active=%zi old=%zi",
-            bq->bq_name ?: "", bq->bq_active_services->is_count,
-            bq->bq_services->is_count);
+  if (seen != bq->bq_services_seen) {
+    bq->bq_services_seen = seen;
+    bq->bq_saveflag = 1;
+  }
+
+  tvhtrace("bouquet", "%s: completed: enabled=%d active=%zi old=%zi seen=%u",
+            bq->bq_name ?: "", bq->bq_enabled, bq->bq_active_services->is_count,
+            bq->bq_services->is_count, seen);
+
+  if (!bq->bq_enabled)
+    goto save;
 
   /* Add/Remove services */
   remove = idnode_set_create(0);
@@ -359,6 +370,7 @@ bouquet_completed(bouquet_t *bq)
   idnode_set_free(bq->bq_active_services);
   bq->bq_active_services = idnode_set_create(1);
 
+save:
   if (bq->bq_saveflag)
     bouquet_save(bq, 1);
 }
@@ -380,6 +392,18 @@ bouquet_map_to_channels(bouquet_t *bq)
       bouquet_unmap_channel(bq, t);
     }
   }
+
+  if (!bq->bq_enabled) {
+    if (bq->bq_services->is_count) {
+      idnode_set_free(bq->bq_services);
+      bq->bq_services = idnode_set_create(1);
+      bq->bq_saveflag = 1;
+    }
+    if (bq->bq_active_services->is_count) {
+      idnode_set_free(bq->bq_active_services);
+      bq->bq_active_services = idnode_set_create(1);
+    }
+  }
 }
 
 /*
@@ -446,6 +470,8 @@ bouquet_class_delete(idnode_t *self)
 {
   bouquet_t *bq = (bouquet_t *)self;
 
+  bq->bq_enabled = 0;
+  bouquet_map_to_channels(bq);
   if (!bq->bq_shield) {
     hts_settings_remove("bouquet/%s", idnode_uuid_as_str(&bq->bq_id));
     bouquet_destroy(bq);
@@ -477,10 +503,25 @@ bouquet_class_get_list(void *o)
   return m;
 }
 
+static void
+bouquet_class_rescan_notify ( void *obj )
+{
+  void mpegts_mux_bouquet_rescan ( const char *src, const char *extra );
+  bouquet_t *bq = obj;
+
+  if (bq->bq_rescan)
+    mpegts_mux_bouquet_rescan(bq->bq_src, bq->bq_comment);
+  bq->bq_rescan = 0;
+}
+
 static void
 bouquet_class_enabled_notify ( void *obj )
 {
-  bouquet_map_to_channels((bouquet_t *)obj);
+  bouquet_t *bq = obj;
+
+  if (bq->bq_enabled)
+    bouquet_class_rescan_notify(obj);
+  bouquet_map_to_channels(bq);
 }
 
 static void
@@ -687,6 +728,14 @@ const idclass_t bouquet_class = {
       .off      = offsetof(bouquet_t, bq_enabled),
       .notify   = bouquet_class_enabled_notify,
     },
+    {
+      .type     = PT_BOOL,
+      .id       = "rescan",
+      .name     = "Rescan",
+      .off      = offsetof(bouquet_t, bq_rescan),
+      .notify   = bouquet_class_rescan_notify,
+      .opts     = PO_NOSAVE,
+    },
     {
       .type     = PT_BOOL,
       .id       = "maptoch",
@@ -754,6 +803,13 @@ const idclass_t bouquet_class = {
       .rend     = bouquet_class_services_rend,
       .opts     = PO_RDONLY | PO_HIDDEN,
     },
+    {
+      .type     = PT_U32,
+      .id       = "services_seen",
+      .name     = "# Seen Services",
+      .off      = offsetof(bouquet_t, bq_services_seen),
+      .opts     = PO_RDONLY,
+    },
     {
       .type     = PT_U32,
       .id       = "services_count",
@@ -816,11 +872,13 @@ bouquet_service_resolve(void)
     if (!bq->bq_services_waiting)
       continue;
     saveflag = bq->bq_saveflag;
-    HTSMSG_FOREACH(f, bq->bq_services_waiting) {
-      if (htsmsg_field_get_u32(f, &lcn)) continue;
-      s = service_find_by_identifier(f->hmf_name);
-      if (s)
-        bouquet_add_service(bq, s, lcn);
+    if (bq->bq_enabled) {
+      HTSMSG_FOREACH(f, bq->bq_services_waiting) {
+        if (htsmsg_field_get_u32(f, &lcn)) continue;
+        s = service_find_by_identifier(f->hmf_name);
+        if (s)
+          bouquet_add_service(bq, s, lcn);
+      }
     }
     htsmsg_destroy(bq->bq_services_waiting);
     bq->bq_services_waiting = NULL;
index 1e2a59bd3bb02532f2972e8f2ea2d7369032093e..dcea306a9c021e0f5d565ca23c3fbbd613dd1c1a 100644 (file)
@@ -34,6 +34,7 @@ typedef struct bouquet {
 
   int           bq_shield;
   int           bq_enabled;
+  int           bq_rescan;
   int           bq_maptoch;
   int           bq_mapnolcn;
   int           bq_mapnoname;
@@ -47,6 +48,8 @@ typedef struct bouquet {
   idnode_set_t *bq_services;
   idnode_set_t *bq_active_services;
   htsmsg_t     *bq_services_waiting;
+  uint32_t      bq_services_seen;
+  uint32_t      bq_services_tmp; /* for fastscan tables */
   uint32_t      bq_lcn_offset;
 
 } bouquet_t;
@@ -78,7 +81,7 @@ bouquet_t * bouquet_find_by_source(const char *name, const char *src, int create
 void bouquet_map_to_channels(bouquet_t *bq);
 void bouquet_notify_channels(bouquet_t *bq);
 void bouquet_add_service(bouquet_t *bq, service_t *s, uint64_t lcn);
-void bouquet_completed(bouquet_t *bq);
+void bouquet_completed(bouquet_t *bq, uint32_t seen);
 
 uint64_t bouquet_get_channel_number(bouquet_t *bq, service_t *t);
 
index eb39160dba01142f6af927824507b3059710e506..38154ed9bcfb35a975034337d93ad7468cbd8ec7 100644 (file)
@@ -752,8 +752,15 @@ void mpegts_mux_unsubscribe_by_name(mpegts_mux_t *mm, const char *name);
 
 void mpegts_mux_scan_done ( mpegts_mux_t *mm, const char *buf, int res );
 
+void mpegts_mux_bouquet_rescan ( const char *src, const char *extra );
+
 void mpegts_mux_nice_name( mpegts_mux_t *mm, char *buf, size_t len );
 
+int mpegts_mux_class_scan_state_set ( void *, const void * );
+
+static inline int mpegts_mux_scan_state_set ( mpegts_mux_t *m, int state )
+  { return mpegts_mux_class_scan_state_set ( m, &state ); }
+
 mpegts_pid_t *mpegts_mux_find_pid_(mpegts_mux_t *mm, int pid, int create);
 
 static inline mpegts_pid_t *
index 01b5c3a8dc30894a3d0483f2efe4604f8898dcef..fcc5c0f248da0b8673abf6b63f43cecbf380c657 100644 (file)
@@ -505,6 +505,8 @@ int dvb_sat_position( const dvb_mux_conf_t *mc );
 
 const char *dvb_sat_position_to_str( int position, char *buf, size_t buflen );
 
+const int dvb_sat_position_from_str( const char *buf );
+
 #endif /* ENABLE_MPEGTS_DVB */
 
 void dvb_done       ( void );
index d8eacf8a2034c1428451487dd6a4838417c2b811..ef2a067996223ef4c3d0c8e25d471e2746a72a7b 100644 (file)
@@ -63,6 +63,7 @@ typedef struct dvb_bat_id {
   uint32_t freesat:1;
   uint32_t bskyb:1;
   uint16_t nbid;
+  uint32_t services_count;
   char name[32];
   mpegts_mux_t *mm;
   TAILQ_HEAD(,dvb_bat_svc) services;
@@ -389,6 +390,8 @@ dvb_desc_service_list
     sid   = (ptr[i] << 8) | ptr[i+1];
     stype = ptr[i+2];
     tvhdebug(dstr, "    service %04X (%d) type %d", sid, sid, stype);
+    if (bi)
+      bi->services_count++;
     if (mm) {
       int save = 0;
       s = mpegts_service_find(mm, sid, 0, 1, &save);
@@ -524,7 +527,7 @@ dvb_freesat_add_service
     snprintf(name, sizeof(name), "%s: %s", bi->name, fr->name);
     fr->bouquet = bouquet_find_by_source(name, src, 1);
   }
-  bouquet_add_service(fr->bouquet, (service_t *)s, lcn * CHANNEL_SPLIT);
+  bouquet_add_service(fr->bouquet, (service_t *)s, (int64_t)lcn * CHANNEL_SPLIT);
 }
 
 static void
@@ -590,7 +593,7 @@ dvb_freesat_completed
       TAILQ_REMOVE(&fr->services, fs, region_link);
     if (fr->bouquet) {
       dvb_bouquet_comment(fr->bouquet, bi->mm);
-      bouquet_completed(fr->bouquet);
+      bouquet_completed(fr->bouquet, total);
       fr->bouquet = NULL;
     }
   }
@@ -653,9 +656,9 @@ dvb_bskyb_local_channels
   if (len < 2)
     return;
 
-  regionid = ptr[1];
+  regionid = (ptr[1] != 0xff) ? ptr[1] : 0xffff;
 
-  if (regionid != 0xff && regionid != 0 && regionid != 1) {
+  if (regionid != 0xffff && regionid != 0 && regionid != 1) {
     if ((str = getenv("TVHEADEND_BSKYB_REGIONID")) != NULL) {
       if (regionid != atoi(str))
         return;
@@ -667,7 +670,7 @@ dvb_bskyb_local_channels
   len -= 2;
   ptr += 2;
 
-  tvhtrace(dstr, "      region id %02X (%d) unknown %02X (%d)",
+  tvhtrace(dstr, "      region id %04X (%d) unknown %02X (%d)",
            regionid, regionid, ptr[0], ptr[0]);
 
   while (len > 8) {
@@ -687,7 +690,7 @@ dvb_bskyb_local_channels
     if (!fs) {
       fs = calloc(1, sizeof(*fs));
       fs->sid = sid;
-      fs->regionid = regionid != 0xff ? regionid : 0xffff;
+      fs->regionid = regionid;
       fs->lcn = lcn != 0xffff ? lcn : 0;
       TAILQ_INSERT_TAIL(&b->fservices, fs, link);
     }
@@ -704,7 +707,7 @@ dvb_bskyb_local_channels
       }
     }
 
-    if (regionid && regionid != 0xff) {
+    if (regionid && regionid != 0xffff) {
       LIST_FOREACH(fr, &b->fregions, link)
         if (fr->regionid == regionid)
           break;
@@ -1153,7 +1156,7 @@ dvb_bat_completed
     TAILQ_FOREACH(bs, &bi->services, link)
       bouquet_add_service(bq, (service_t *)bs->svc, 0);
 
-    bouquet_completed(bq);
+    bouquet_completed(bq, bi->services_count);
 
 complete:
     bi->complete = 1;
@@ -1678,7 +1681,7 @@ dvb_fs_sdt_callback
   if (r == 0) {
     mt->mt_working -= st->working;
     st->working = 0;
-    bouquet_completed(bq);
+    bouquet_completed(bq, bq->bq_services_tmp);
   }
   if (r != 1) return r;
   if (len < 5) return -1;
@@ -1730,6 +1733,7 @@ dvb_fs_sdt_callback
       tvhtrace(mt->mt_name, "    dtag %02X dlen %d", dtag, dlen);
       switch (dtag) {
         case DVB_DESC_SERVICE:
+          bq->bq_services_tmp++;
           if (dvb_desc_service(dptr, dlen, &stype, sprov,
                                sizeof(sprov), sname, sizeof(sname), charset))
             return -1;
@@ -2301,6 +2305,7 @@ static void
 psi_tables_dvb_fastscan( void *aux, bouquet_t *bq, const char *name, int pid )
 {
   tvhtrace("fastscan", "adding table %04X (%i) for '%s'", pid, pid, name);
+  bq->bq_services_tmp = 0;
   mpegts_table_add(aux, DVB_FASTSCAN_NIT_BASE, DVB_FASTSCAN_MASK,
                    dvb_nit_callback, bq, "fs_nit", MT_CRC, pid);
   mpegts_table_add(aux, DVB_FASTSCAN_SDT_BASE, DVB_FASTSCAN_MASK,
index 7689eb0a8b52b0db4941fc49eed4a1efac21c9da..5d127fe24f4ebcb01a383f80b819477d60327250 100644 (file)
@@ -868,6 +868,31 @@ dvb_sat_position_to_str(int position, char *buf, size_t buflen)
   return buf;
 }
 
+const int
+dvb_sat_position_from_str( const char *buf )
+{
+  const char *s = buf;
+  int min, maj;
+  char c;
+
+  if (!buf)
+    return INT_MAX;
+  maj = atoi(s);
+  while (*s && *s != '.')
+    s++;
+  min = *s == '.' ? atoi(s + 1) : 0;
+  do {
+    c = *s++;
+  } while (c && c != 'W' && c != 'E');
+  if (!c)
+    return INT_MAX;
+  if (maj > 180 || maj < 0)
+    return INT_MAX;
+  if (min > 9 || min < 0)
+    return INT_MAX;
+  return (maj * 10 + min) * (c == 'W' ? -1 : 1);
+}
+
 #endif /* ENABLE_MPEGTS_DVB */
 
 /**
index e1e4140458da1e49c472f4863ab590204a14b1ec..d260b99a35fae59c588187ba6e4d7ad11894b235 100644 (file)
@@ -257,7 +257,7 @@ scan_result_tab[] = {
  { "FAIL",    MM_SCAN_FAIL },
 };
 
-static int
+int
 mpegts_mux_class_scan_state_set ( void *o, const void *p )
 {
   mpegts_mux_t *mm = o;
index 94646ecd4e5521c9a24d8b1a6c71730d5aa09543..1c86b262cd99a19d519c097f473d30cd57d4c8c6 100644 (file)
@@ -213,6 +213,102 @@ mpegts_network_scan_queue_add ( mpegts_mux_t *mm, int weight )
   mpegts_network_scan_notify(mm);
 }
 
+/******************************************************************************
+ * Bouquet helper
+ *****************************************************************************/
+
+static ssize_t
+startswith( const char *str, const char *start )
+{
+  size_t len = strlen(start);
+  if (!strncmp(str, start, len))
+    return len;
+  return -1;
+}
+
+void
+mpegts_mux_bouquet_rescan ( const char *src, const char *extra )
+{
+  mpegts_network_t *mn;
+  mpegts_mux_t *mm;
+  ssize_t l;
+#if ENABLE_MPEGTS_DVB
+  const idclass_t *ic;
+  uint32_t freq;
+  int satpos;
+#endif
+
+  if (!src)
+    return;
+#if ENABLE_MPEGTS_DVB
+  if ((l = startswith(src, "dvb-bouquet://dvbs,")) > 0) {
+    uint32_t tsid, nbid;
+    src += l;
+    if ((satpos = dvb_sat_position_from_str(src)) == INT_MAX)
+      return;
+    while (*src && *src != ',')
+      src++;
+    if (sscanf(src, ",%x,%x", &tsid, &nbid) != 2)
+      return;
+    LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
+      LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
+        if (idnode_is_instance(&mm->mm_id, &dvb_mux_dvbs_class) &&
+            mm->mm_tsid == tsid &&
+            dvb_sat_position(&((dvb_mux_t *)mm)->lm_tuning) == satpos)
+          mpegts_mux_scan_state_set(mm, MM_SCAN_STATE_PEND);
+    return;
+  }
+  if ((l = startswith(src, "dvb-bouquet://dvbt,")) > 0) {
+    uint32_t tsid, nbid;
+    if (sscanf(src, "%x,%x", &tsid, &nbid) != 2)
+      return;
+    ic = &dvb_mux_dvbt_class;
+    goto tsid_lookup;
+  }
+  if ((l = startswith(src, "dvb-bouquet://dvbc,")) > 0) {
+    uint32_t tsid, nbid;
+    if (sscanf(src, "%x,%x", &tsid, &nbid) != 2)
+      return;
+    ic = &dvb_mux_dvbc_class;
+tsid_lookup:
+    LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
+      LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
+        if (idnode_is_instance(&mm->mm_id, ic) &&
+            mm->mm_tsid == tsid)
+          mpegts_mux_scan_state_set(mm, MM_SCAN_STATE_PEND);
+    return;
+  }
+  if ((l = startswith(src, "dvb-bskyb://dvbs,")) > 0 ||
+      (l = startswith(src, "dvb-freesat://dvbs,")) > 0) {
+    if ((satpos = dvb_sat_position_from_str(src + l)) == INT_MAX)
+      return;
+    /* a bit tricky, but we don't have other info */
+    if (!extra)
+      return;
+    freq = strtod(extra, NULL) * 1000;
+    goto freq;
+  }
+  if ((l = startswith(src, "dvb-fastscan://dvbs,")) > 0) {
+    uint32_t pid;
+    src += l;
+    if ((satpos = dvb_sat_position_from_str(src)) == INT_MAX)
+      return;
+    while (*src && *src != ',')
+      src++;
+    if (sscanf(src, ",%u,%u", &freq, &pid) != 2)
+      return;
+freq:
+    LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
+      LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
+        if (idnode_is_instance(&mm->mm_id, &dvb_mux_dvbs_class) &&
+            ((dvb_mux_t *)mm)->lm_tuning.dmc_fe_freq == freq &&
+            dvb_sat_position(&((dvb_mux_t *)mm)->lm_tuning) == satpos)
+          mpegts_mux_scan_state_set(mm, MM_SCAN_STATE_PEND);
+    return;
+  }
+#endif
+}
+
 /******************************************************************************
  * Subsystem setup / tear down
  *****************************************************************************/
index 389623cb75b3e48ef9b2ce80932c5f4e995cbeb9..3f94550aa71c4a1dd903913a9cd1f46fd7e68829 100644 (file)
@@ -32,8 +32,8 @@ tvheadend.cteditor = function(panel, index)
  */
 tvheadend.bouquet = function(panel, index)
 {
-    var list = 'enabled,name,maptoch,mapnolcn,lcn_off,mapnoname,mapradio,' +
-               'chtag,source,services_count,comment';
+    var list = 'enabled,rescan,name,maptoch,mapnolcn,lcn_off,mapnoname,mapradio,' +
+               'chtag,source,services_count,services_seen,comment';
 
     tvheadend.idnode_grid(panel, {
         url: 'api/bouquet',
@@ -43,6 +43,7 @@ tvheadend.bouquet = function(panel, index)
         tabIndex: index,
         columns: {
             enabled:        { width: 50 },
+            rescan:         { width: 50 },
             name:           { width: 200 },
             maptoch:        { width: 100 },
             mapnolcn:       { width: 100 },
@@ -52,6 +53,7 @@ tvheadend.bouquet = function(panel, index)
             chtag:          { width: 100 },
             source:         { width: 200 },
             services_count: { width: 100 },
+            services_seen:  { width: 100 },
             comment:        { width: 200 },
         },
         list: list,