]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
bouquet: freesat support
authorJaroslav Kysela <perex@perex.cz>
Mon, 3 Nov 2014 20:21:28 +0000 (21:21 +0100)
committerJaroslav Kysela <perex@perex.cz>
Sat, 8 Nov 2014 20:05:38 +0000 (21:05 +0100)
src/bouquet.c
src/bouquet.h
src/channels.c
src/input/mpegts.h
src/input/mpegts/dvb.h
src/input/mpegts/dvb_psi.c
src/input/mpegts/mpegts_table.c
src/prop.c
src/service.h

index daa7384df39fab36d8b9000d453e68a6d22d0add..a47db199a9c9385834d3ee4408084b2f4cf21183 100644 (file)
@@ -112,12 +112,17 @@ void
 bouquet_destroy_by_service(service_t *t)
 {
   bouquet_t *bq;
+  service_lcn_t *sl;
 
   lock_assert(&global_lock);
 
   RB_FOREACH(bq, &bouquets, bq_link)
     if (idnode_set_exists(bq->bq_services, &t->s_id))
       idnode_set_remove(bq->bq_services, &t->s_id);
+  while ((sl = LIST_FIRST(&t->s_lcns)) != NULL) {
+    LIST_REMOVE(sl, sl_link);
+    free(sl);
+  }
 }
 
 /**
@@ -150,8 +155,14 @@ bouquet_find_by_source(const char *name, const char *src, int create)
 
   bqs.bq_src = (char *)src;
   bq = RB_FIND(&bouquets, &bqs, bq_link, _bq_cmp);
-  if (bq)
+  if (bq) {
+    if (name && *name && strcmp(name, bq->bq_name)) {
+      tvhwarn("bouquet", "bouquet name '%s' changed to '%s'", bq->bq_name ?: "", name);
+      free(bq->bq_name);
+      bq->bq_name = strdup(name);
+    }
     return bq;
+  }
   if (create && name)
     return bouquet_create(NULL, NULL, name, src);
   return NULL;
@@ -227,13 +238,30 @@ bouquet_map_channel(bouquet_t *bq, service_t *t)
  *
  */
 void
-bouquet_add_service(bouquet_t *bq, service_t *s)
+bouquet_add_service(bouquet_t *bq, service_t *s, uint32_t lcn)
 {
+  service_lcn_t *tl;
+
   lock_assert(&global_lock);
 
   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);
+
+    LIST_FOREACH(tl, &s->s_lcns, sl_link)
+      if (tl->sl_bouquet == bq) {
+        tl->sl_lcn = lcn;
+        break;
+      }
+
+    if (!tl) {
+      tl = calloc(1, sizeof(*tl));
+      tl->sl_bouquet = bq;
+      tl->sl_lcn = lcn;
+      LIST_INSERT_HEAD(&s->s_lcns, tl, sl_link);
+    }
+    tl->sl_seen = 1;
+
     bq->bq_saveflag = 1;
     if (bq->bq_enabled && bq->bq_maptoch)
       bouquet_map_channel(bq, s);
@@ -281,11 +309,17 @@ void
 bouquet_completed(bouquet_t *bq)
 {
   idnode_set_t *remove;
+  service_t *s;
+  service_lcn_t *lcn, *lcn_next;
   size_t z;
 
+  if (!bq)
+    return;
+
   tvhtrace("bouquet", "completed: active=%zi old=%zi",
             bq->bq_active_services->is_count, bq->bq_services->is_count);
 
+  /* Add/Remove services */
   remove = idnode_set_create();
   for (z = 0; z < bq->bq_services->is_count; z++)
     if (!idnode_set_exists(bq->bq_active_services, bq->bq_services->is_array[z]))
@@ -294,8 +328,27 @@ bouquet_completed(bouquet_t *bq)
     bouquet_remove_service(bq, (service_t *)remove->is_array[z]);
   idnode_set_free(remove);
 
+  /* Remove no longer used LCNs */
+  for (z = 0; z < bq->bq_services->is_count; z++) {
+    s = (service_t *)bq->bq_services->is_array[z];
+    for (lcn = LIST_FIRST(&s->s_lcns); lcn; lcn = lcn_next) {
+      lcn_next = LIST_NEXT(lcn, sl_link);
+      if (lcn->sl_bouquet != bq) continue;
+      if (!lcn->sl_seen) {
+        LIST_REMOVE(lcn, sl_link);
+        free(lcn);
+      } else {
+        lcn->sl_seen = 0;
+      }
+    }
+  }
+
+
   idnode_set_free(bq->bq_active_services);
   bq->bq_active_services = idnode_set_create();
+
+  if (bq->bq_saveflag)
+    bouquet_save(bq, 1);
 }
 
 /*
@@ -335,6 +388,20 @@ bouquet_notify_channels(bouquet_t *bq)
   }
 }
 
+/*
+ *
+ */
+uint64_t
+bouquet_get_channel_number(bouquet_t *bq, service_t *t)
+{
+  service_lcn_t *tl;
+
+  LIST_FOREACH(tl, &t->s_lcns, sl_link)
+    if (tl->sl_bouquet == bq)
+      return (int64_t)tl->sl_lcn * CHANNEL_SPLIT;
+  return 0;
+}
+
 /**
  *
  */
@@ -524,15 +591,19 @@ bouquet_class_chtag_ref_set ( void *obj, const void *p )
 static const void *
 bouquet_class_services_get ( void *obj )
 {
-  htsmsg_t *l = htsmsg_create_list();
+  htsmsg_t *m = htsmsg_create_map();
   bouquet_t *bq = obj;
+  service_t *t;
   size_t z;
 
   /* Add all */
-  for (z = 0; z < bq->bq_services->is_count; z++)
-    htsmsg_add_str(l, NULL, idnode_uuid_as_str(bq->bq_services->is_array[z]));
+  for (z = 0; z < bq->bq_services->is_count; z++) {
+    t = (service_t *)bq->bq_services->is_array[z];
+    htsmsg_add_u32(m, idnode_uuid_as_str(&t->s_id),
+                   bouquet_get_channel_number(bq, t));
+  }
 
-  return l;
+  return m;
 }
 
 static char *
@@ -696,7 +767,7 @@ bouquet_service_resolve(void)
   bouquet_t *bq;
   htsmsg_field_t *f;
   service_t *s;
-  const char *str;
+  uint32_t lcn;
   int saveflag;
 
   lock_assert(&global_lock);
@@ -706,11 +777,10 @@ bouquet_service_resolve(void)
       continue;
     saveflag = bq->bq_saveflag;
     HTSMSG_FOREACH(f, bq->bq_services_waiting) {
-      if ((str = htsmsg_field_get_str(f))) {
-        s = service_find_by_identifier(str);
-        if (s)
-          bouquet_add_service(bq, s);
-      }
+      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 fe34571f4989171589ffac055305231611d7ee51..293a97d6b7fc503bef069aa9e4aa91da03c54cac 100644 (file)
@@ -76,9 +76,11 @@ 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);
+void bouquet_add_service(bouquet_t *bq, service_t *s, uint32_t lcn);
 void bouquet_completed(bouquet_t *bq);
 
+uint64_t bouquet_get_channel_number(bouquet_t *bq, service_t *t);
+
 void bouquet_save(bouquet_t *bq, int notify);
 
 /**
index 227bdfbaf27b9b7d949984922f970d8df5868743..2d29c1d4765141de8898fae6ccdf5b1031e46354 100644 (file)
@@ -569,9 +569,13 @@ channel_get_number ( channel_t *ch )
   if (ch->ch_number) {
     n = ch->ch_number;
   } else {
-    LIST_FOREACH(csm, &ch->ch_services, csm_chn_link)
+    LIST_FOREACH(csm, &ch->ch_services, csm_chn_link) {
+      if (ch->ch_bouquet &&
+          (n = bouquet_get_channel_number(ch->ch_bouquet, csm->csm_svc)))
+        break;
       if ((n = service_get_channel_number(csm->csm_svc)))
         break;
+    }
   }
   if (n) {
     if (ch->ch_bouquet)
@@ -661,6 +665,7 @@ channel_get_icon ( channel_t *ch )
           ch->ch_icon = strdup(icn);
           channel_save(ch);
           idnode_notify_simple(&ch->ch_id);
+          break;
         }
       }
     }
index bdc9114aa095e9212ef19db3a9ff30bc171e89ef..c898a1a5ea9ab4fbd8dc17149a85c70bda36f514 100644 (file)
@@ -111,7 +111,6 @@ typedef struct mpegts_table_state
   int      version;
   int      complete;
   uint32_t sections[8];
-  void    *bouquet;
   RB_ENTRY(mpegts_table_state)   link;
 } mpegts_table_state_t;
 
@@ -172,6 +171,7 @@ struct mpegts_table
   char *mt_name;
 
   void *mt_opaque;
+  void *mt_bat;
   mpegts_table_callback_t mt_callback;
 
   RB_HEAD(,mpegts_table_state) mt_state;
index fc52004f590f9274e8c175bd8563e38fe6b8c4f9..e2aeecbb1f6279971e80abcc70cf5ee3aaa0e7ee 100644 (file)
@@ -131,6 +131,9 @@ struct mpegts_mux;
 #define DVB_DESC_AAC                  0x7C
 #define DVB_DESC_LOCAL_CHAN           0x83
 
+#define DVB_DESC_FREESAT_LCN          0xD3
+#define DVB_DESC_FREESAT_REGIONS      0xD4
+
 /* Service type lookup */
 
 int dvb_servicetype_lookup ( int t );
@@ -202,7 +205,9 @@ int dvb_table_begin
    int tableid, uint64_t extraid, int minlen,
    struct mpegts_table_state **st, int *sect, int *last, int *ver);
 void dvb_table_reset
-  (struct mpegts_table *mt );
+  (struct mpegts_table *mt);
+void dvb_bat_destroy
+  (struct mpegts_table *mt);
 
 int dvb_pat_callback
   (struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
index 5431c3c1dc4039f12338666ec851b14b63726669..79dcebf6e9669c0310fe2b12ca83aba71a66af93 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+typedef struct dvb_freesat_svc {
+  TAILQ_ENTRY(dvb_freesat_svc) link;
+  TAILQ_ENTRY(dvb_freesat_svc) region_link;
+  uint16_t sid;
+  uint16_t regionid;
+  uint16_t lcn;
+  mpegts_service_t *svc;
+} dvb_freesat_svc_t;
+
+typedef struct dvb_freesat_region {
+  LIST_ENTRY(dvb_freesat_region) link;
+  TAILQ_HEAD(,dvb_freesat_svc) services;
+  uint16_t regionid;
+  char name[32];
+  bouquet_t *bouquet;
+} dvb_freesat_region_t;
+
+typedef struct dvb_bat_svc {
+  TAILQ_ENTRY(dvb_bat_svc) link;
+  mpegts_service_t *svc;
+  dvb_freesat_svc_t *fallback;
+} dvb_bat_svc_t;
+
+typedef struct dvb_bat_id {
+  LIST_ENTRY(dvb_bat_id) link;
+  uint32_t complete:1;
+  uint32_t freesat:1;
+  uint16_t nbid;
+  char name[32];
+  TAILQ_HEAD(,dvb_bat_svc) services;
+} dvb_bat_id_t;
+
+typedef struct dvb_bat {
+  int complete;
+  LIST_HEAD(,dvb_bat_id)          bats;
+  LIST_HEAD(,dvb_freesat_region)  fregions;
+  TAILQ_HEAD(,dvb_freesat_svc)    fservices;
+} dvb_bat_t;
+
 SKEL_DECLARE(mpegts_table_state_skel, struct mpegts_table_state);
 
 static int
@@ -323,10 +362,11 @@ dvb_desc_service
 
 static int
 dvb_desc_service_list
-  ( const char *dstr, const uint8_t *ptr, int len, mpegts_mux_t *mm, bouquet_t *bq )
+  ( const char *dstr, const uint8_t *ptr, int len, mpegts_mux_t *mm, dvb_bat_id_t *bi )
 {
   uint16_t stype, sid;
   int i;
+  dvb_bat_svc_t *bs;
   mpegts_service_t *s;
   for (i = 0; i < len; i += 3) {
     sid   = (ptr[i] << 8) | ptr[i+1];
@@ -335,8 +375,11 @@ dvb_desc_service_list
     if (mm) {
       int save = 0;
       s = mpegts_service_find(mm, sid, 0, 1, &save);
-      if (bq)
-        bouquet_add_service(bq, (service_t *)s);
+      if (bi) {
+        bs = calloc(1, sizeof(*bs));
+        bs->svc = s;
+        TAILQ_INSERT_TAIL(&bi->services, bs, link);
+      }
       if (save)
         s->s_config_save((service_t*)s);
     }
@@ -371,7 +414,150 @@ dvb_desc_local_channel
   return 0;
 }
 
+/*
+ * UK FreeSat
+ */
+
+static void
+dvb_freesat_local_channels
+  ( dvb_bat_t *b, const char *dstr, const uint8_t *ptr, int len, uint16_t nbid )
+{
+  uint16_t sid, unk, lcn, regionid;
+  dvb_freesat_svc_t *fs;
+  int len2;
+
+  while (len > 4) {
+    sid = (ptr[0] << 8) | ptr[1];
+    unk = (ptr[2] << 8) | ptr[3];
+    len2 = ptr[4];
+    ptr += 5;
+    len -= 5;
+    if (len2 > len)
+      break;
+    tvhtrace(dstr, "      sid %04X (%d) uknown %04X (%d)\n", sid, sid, unk, unk);
+    while (len2 > 3) {
+      lcn = ((ptr[0] & 0x0f) << 8) | ptr[1];
+      regionid = (ptr[2] << 8) | ptr[3];
+      tvhtrace(dstr, "        lcn %d region %d\n", lcn, regionid);
+
+      TAILQ_FOREACH(fs, &b->fservices, link)
+        if (fs->sid == sid && fs->regionid == regionid)
+          break;
+      if (!fs) {
+        fs = calloc(1, sizeof(*fs));
+        fs->sid = sid;
+        fs->regionid = regionid;
+        fs->lcn = lcn;
+        TAILQ_INSERT_TAIL(&b->fservices, fs, link);
+      }
+      ptr += 4;
+      len -= 4;
+      len2 -= 4;
+    }
+  }
+}
+
+static void
+dvb_freesat_regions
+  ( dvb_bat_t *b, const char *dstr, const uint8_t *ptr, int len, uint16_t nbid )
+{
+  uint16_t id;
+  char name[32];
+  dvb_freesat_region_t *fr;
+  int r;
+
+  while (len > 5) {
+    id = (ptr[0] << 8) | ptr[1];
+    /* language: ptr[2-4]: 'eng' */
+    if ((r = dvb_get_string_with_len(name, sizeof(name), ptr + 5, len - 5, NULL, NULL)) < 0)
+      break;
+    tvhtrace(dstr, "    region %u - '%s'\n", id, name);
+
+    LIST_FOREACH(fr, &b->fregions, link)
+      if (fr->regionid == id)
+        break;
+    if (!fr) {
+      fr = calloc(1, sizeof(*fr));
+      fr->regionid = id;
+      strncpy(fr->name, name, sizeof(fr->name)-1);
+      fr->name[sizeof(fr->name)-1] = '\0';
+      TAILQ_INIT(&fr->services);
+      LIST_INSERT_HEAD(&b->fregions, fr, link);
+    }
+
+    ptr += 5 + r;
+    len -= 5 + r;
+  }
+}
+
+static void
+dvb_freesat_add_service
+  ( dvb_bat_id_t *bi, dvb_freesat_region_t *fr, mpegts_service_t *s, uint32_t lcn )
+{
+  char name[64], src[64];
+  if (!fr->bouquet) {
+    snprintf(name, sizeof(name), "%s: %s", bi->name, fr->name);
+    snprintf(src, sizeof(src), "dvb-freesat://28.2E,%04X,%u", bi->nbid, fr->regionid);
+    fr->bouquet = bouquet_find_by_source(name, src, 1);
+  }
+  bouquet_add_service(fr->bouquet, (service_t *)s, lcn);
+}
 
+static void
+dvb_freesat_completed
+  ( dvb_bat_t *b, dvb_bat_id_t *bi, const char *dstr, int nbid )
+{
+  dvb_bat_svc_t *bs;
+  dvb_freesat_svc_t *fs;
+  dvb_freesat_region_t *fr;
+  uint16_t sid;
+
+  /* Find all "fallback" services and region specific */
+  TAILQ_FOREACH(bs, &bi->services, link) {
+    sid = bs->svc->s_dvb_service_id;
+    TAILQ_FOREACH(fs, &b->fservices, link)
+      if (fs->sid == sid) {
+        fs->svc = bs->svc;
+        if ((fs->regionid == 0 && !bs->fallback) || fs->regionid == 65535) {
+          bs->fallback = fs;
+          continue;
+        }
+        LIST_FOREACH(fr, &b->fregions, link)
+          if (fr->regionid == fs->regionid)
+            break;
+        if (!fr)
+          tvhtrace(dstr, "cannot find freesat region id %u", fs->regionid);
+        else
+          TAILQ_INSERT_TAIL(&fr->services, fs, region_link);
+      }
+  }
+
+  /* create bouquets, one per region */
+  LIST_FOREACH(fr, &b->fregions, link) {
+    if (TAILQ_EMPTY(&fr->services)) continue;
+    TAILQ_FOREACH(fs, &fr->services, region_link)
+      dvb_freesat_add_service(bi, fr, fs->svc, fs->lcn);
+    TAILQ_FOREACH(bs, &bi->services, link)
+      if ((fs = bs->fallback) != NULL)
+        dvb_freesat_add_service(bi, fr, bs->svc, fs->lcn);
+      else
+        dvb_freesat_add_service(bi, fr, bs->svc, 0);
+  }
+
+  /* Remove all services associated to region, notify the completed status */
+  LIST_FOREACH(fr, &b->fregions, link) {
+    while ((fs = TAILQ_FIRST(&fr->services)) != NULL)
+      TAILQ_REMOVE(&fr->services, fs, region_link);
+    if (fr->bouquet) {
+      bouquet_completed(fr->bouquet);
+      fr->bouquet = NULL;
+    }
+  }
+
+  /* Clear all "fallback/default" services */
+  TAILQ_FOREACH(bs, &bi->services, link)
+    bs->fallback = NULL;
+}
 
 /* **************************************************************************
  * Tables
@@ -711,12 +897,105 @@ dvb_pmt_callback
 /*
  * NIT/BAT processing (because its near identical)
  */
+
+static void
+dvb_bat_destroy_lists( mpegts_table_t *mt )
+{
+  dvb_bat_t *b = mt->mt_bat;
+  dvb_bat_id_t *bi;
+  dvb_bat_svc_t *bs;
+  dvb_freesat_region_t *fr;
+  dvb_freesat_svc_t *fs;
+  
+  while ((bi = LIST_FIRST(&b->bats)) != NULL) {
+    while ((bs = TAILQ_FIRST(&bi->services)) != NULL) {
+      TAILQ_REMOVE(&bi->services, bs, link);
+      free(bs);
+    }
+    LIST_REMOVE(bi, link);
+    free(bi);
+  }
+  while ((fr = LIST_FIRST(&b->fregions)) != NULL) {
+    LIST_REMOVE(fr, link);
+    free(fr);
+  }
+  while ((fs = TAILQ_FIRST(&b->fservices)) != NULL) {
+    TAILQ_REMOVE(&b->fservices, fs, link);
+    free(fs);
+  }
+}
+
+void
+dvb_bat_destroy( mpegts_table_t *mt )
+{
+  dvb_bat_destroy_lists(mt);
+  free(mt->mt_bat);
+  mt->mt_bat = NULL;
+}
+
+static void
+dvb_bat_completed
+  ( dvb_bat_t *b, const char *dstr, int tableid, int nbid, mpegts_mux_t *mux )
+{
+  dvb_bat_id_t *bi;
+  dvb_bat_svc_t *bs;
+  char src[64];
+  bouquet_t *bq;
+
+  b->complete = 1;
+
+  LIST_FOREACH(bi, &b->bats, link) {
+
+    if (bi->nbid != nbid) {
+      if (!bi->complete)
+        b->complete = 0;
+      continue;
+    }
+
+    if (bi->freesat) {
+      dvb_freesat_completed(b, bi, dstr, nbid);
+      goto complete;
+    }
+
+    bq = NULL;
+
+#if ENABLE_MPEGTS_DVB
+    if (tableid == 0x4A /* BAT */) {
+      if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbs_class)) {
+        dvb_mux_conf_t *mc = &((dvb_mux_t *)mux)->lm_tuning;
+        if (mc->u.dmc_fe_qpsk.orbital_dir) {
+          char buf[16];
+          dvb_sat_position_to_str(dvb_sat_position(mc), buf, sizeof(buf));
+          snprintf(src, sizeof(src), "dvb-bouquet://dvbs,%s,%04X", buf, bi->nbid);
+        }
+      } else if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbt_class)) {
+        snprintf(src, sizeof(src), "dvb-bouquet://dvbt,%04X", bi->nbid);
+      } else if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbc_class)) {
+        snprintf(src, sizeof(src), "dvb-bouquet://dvbc,%04X", bi->nbid);
+      }
+      if (src[0])
+        bq = bouquet_find_by_source(bi->name, src, !TAILQ_EMPTY(&bi->services));
+    }
+#endif
+
+    if (!bq) continue;
+
+    TAILQ_FOREACH(bs, &bi->services, link)
+      bouquet_add_service(bq, (service_t *)bs->svc, 0);
+
+    bouquet_completed(bq);
+
+complete:
+    bi->complete = 1;
+  }
+}
+
 int
 dvb_nit_callback
   (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
 {
   int save = 0;
-  int r, sect, last, ver;
+  int r, sect, last, ver, fsat = 0;
   uint8_t  dtag;
   int llen, dllen, dlen;
   const uint8_t *lptr, *dlptr, *dptr;
@@ -726,26 +1005,33 @@ dvb_nit_callback
   char name[256], dauth[256];
   mpegts_table_state_t  *st  = NULL;
   bouquet_t *bq = NULL;
+  dvb_bat_t *b = NULL;
+  dvb_bat_id_t *bi = NULL;
   const char *charset;
 
   /* Net/Bat ID */
   nbid = (ptr[0] << 8) | ptr[1];
 
   /* Begin */
-  if (tableid != 0x40 && tableid != 0x41 && tableid != 0x4A && tableid != 0xBC)
+  if (tableid != 0x40 && tableid != 0x41 && tableid != 0x4A &&
+      tableid != DVB_FASTSCAN_NIT_BASE)
     return -1;
+
   r = dvb_table_begin(mt, ptr, len, tableid, nbid, 7, &st, &sect, &last, &ver);
   if (r == 0) {
-    if (tableid != 0xBC /* fastscan */) {
-      RB_FOREACH(st, &mt->mt_state, link)
-        if (st->bouquet)
-          bouquet_completed((bouquet_t *)st->bouquet);
+    if (tableid == 0x4A) {
+      if ((b = mt->mt_bat) != NULL) {
+        if (!b->complete)
+          dvb_bat_completed(b, mt->mt_name, tableid, nbid, mm);
+        if (b->complete)
+          dvb_bat_destroy_lists(mt);
+      }
     }
   }
   if (r != 1) return r;
 
   /* NIT */
-  if (tableid != 0x4A && tableid != 0xBC /* fastscan */) {
+  if (tableid != 0x4A && tableid != DVB_FASTSCAN_NIT_BASE) {
 
     /* Specific NID */
     if (mn->mn_nid) {
@@ -759,6 +1045,24 @@ dvb_nit_callback
     }
   }
 
+  /* BAT ID lookup */
+  if (tableid == 0x4A) {
+    if ((b = mt->mt_bat) == NULL) {
+      b = calloc(1, sizeof(*b));
+      TAILQ_INIT(&b->fservices);
+      mt->mt_bat = b;
+    }
+    LIST_FOREACH(bi, &b->bats, link)
+      if (bi->nbid == nbid)
+        break;
+    if (!bi) {
+      bi = calloc(1, sizeof(*bi));
+      bi->nbid = nbid;
+      TAILQ_INIT(&bi->services);
+      LIST_INSERT_HEAD(&b->bats, bi, link);
+    }
+  }
+
   /* Network Descriptors */
   *name   = 0;
   charset = dvb_charset_find(mn, NULL, NULL);
@@ -774,17 +1078,29 @@ dvb_nit_callback
       case DVB_DESC_MULTI_NETWORK_NAME:
         // TODO: implement this?
         break;
+      case DVB_DESC_PRIVATE_DATA:
+        if (tableid == 0x4A && dlen == 4 && !memcmp(dptr, "FSAT", 4))
+          fsat = 1;
+        break;
+      case DVB_DESC_FREESAT_REGIONS:
+        if (fsat)
+          dvb_freesat_regions(b, mt->mt_name, dptr, dlen, nbid);
+        break;
     }
   }
 
   /* Fastscan */
-  if (tableid == 0xBC) {
+  if (tableid == DVB_FASTSCAN_NIT_BASE) {
     tvhdebug(mt->mt_name, "fastscan %04X (%d) [%s]", nbid, nbid, name);
     bq = mt->mt_opaque;
 
   /* BAT */
   } else if (tableid == 0x4A) {
     tvhdebug(mt->mt_name, "bouquet %04X (%d) [%s]", nbid, nbid, name);
+    if (bi && *name) {
+      strncpy(bi->name, name, sizeof(bi->name)-1);
+      bi->name[sizeof(bi->name)-1] = '\0';
+    }
 
   /* NIT */
   } else {
@@ -805,30 +1121,6 @@ dvb_nit_callback
         break;
     charset = dvb_charset_find(mn, mux, NULL);
 
-#if ENABLE_MPEGTS_DVB
-    dauth[0] = 0;
-    if (!bq && *name && tableid == 0x4A /* BAT */) {
-      if (idnode_is_instance(&mm->mm_id, &dvb_mux_dvbs_class)) {
-        dvb_mux_conf_t *mc = &((dvb_mux_t *)mm)->lm_tuning;
-        if (mc->u.dmc_fe_qpsk.orbital_dir) {
-          char buf[16];
-          dvb_sat_position_to_str(dvb_sat_position(mc), buf, sizeof(buf));
-          snprintf(dauth, sizeof(dauth), "dvb-bouquet://dvbs,%s/%s", buf, name);
-        }
-      } else if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbt_class)) {
-        snprintf(dauth, sizeof(dauth), "dvb-bouquet://dvbt/%s", name);
-      } else if (idnode_is_instance(&mux->mm_id, &dvb_mux_dvbc_class)) {
-        snprintf(dauth, sizeof(dauth), "dvb-bouquet://dvbc/%s", name);
-      }
-    }
-    if (dauth[0]) {
-      bouquet_t *bq2 = bouquet_find_by_source(name, dauth, 1);
-      if (bq2 != bq && bq && bq->bq_saveflag)
-        bouquet_save(bq, 1);
-      bq = bq2;
-      st->bouquet = bq;
-    }
-#endif
 
     tvhdebug(mt->mt_name, "  onid %04X (%d) tsid %04X (%d) mux %p bq %p", onid, onid, tsid, tsid, mux, bq);
 
@@ -887,9 +1179,19 @@ dvb_nit_callback
             return -1;
           break;
         case DVB_DESC_SERVICE_LIST:
-          if (dvb_desc_service_list(mt->mt_name, dptr, dlen, mux, bq))
+          if (dvb_desc_service_list(mt->mt_name, dptr, dlen, mux, bi))
             return -1;
           break;
+        case DVB_DESC_PRIVATE_DATA:
+          if (dlen == 4 && !memcmp(dptr, "FSAT", 4))
+            fsat = 1;
+          break;
+        case DVB_DESC_FREESAT_LCN:
+          if (tableid == 0x4A && fsat) {
+            dvb_freesat_local_channels(b, mt->mt_name, dptr, dlen, nbid);
+            bi->freesat = 1;
+          }
+          break;
       }
     }
   }
@@ -1163,7 +1465,7 @@ dvb_fs_sdt_callback
   mpegts_mux_t     *mm = mt->mt_mux, *mux;
   mpegts_network_t *mn = mm->mm_network;
   mpegts_table_state_t  *st  = NULL;
-  bouquet_t *bq = mt->mt_opaque;
+  bouquet_t *bq = mt->mt_bat;
 
   /* Fastscan ID */
   nbid = (ptr[0] << 8) | ptr[1];
@@ -1203,7 +1505,7 @@ dvb_fs_sdt_callback
     s       = mpegts_service_find(mm, service_id, 0, 1, &save);
     charset = dvb_charset_find(mn, mm, s);
     if (bq && s)
-      bouquet_add_service(bq, (service_t *)s);
+      bouquet_add_service(bq, (service_t *)s, 0);
 
     /* Descriptor loop */
     DVB_DESC_EACH(lptr, llen, dtag, dlen, dptr) {
index 3628dfe7d22ee672e7252759d264253875fbe907..3da63d1237734f1e25ad52c5669dd4580004ce6b 100644 (file)
@@ -142,6 +142,8 @@ mpegts_table_release_ ( mpegts_table_t *mt )
   tvhtrace("mpegts", "table: mux %p free %s %02X/%02X (%d) pid %04X (%d)",
            mt->mt_mux, mt->mt_name, mt->mt_table, mt->mt_mask, mt->mt_table,
            mt->mt_pid, mt->mt_pid);
+  if (mt->mt_bat)
+    dvb_bat_destroy(mt);
   if (mt->mt_destroy)
     mt->mt_destroy(mt);
   free(mt->mt_name);
index 723ae41090ec7966fb0f818cbed153384a37cedb..c568fb3a6e5a578efdfe4400135ee202140cc69c 100644 (file)
@@ -107,7 +107,9 @@ prop_write_values
 
     /* List */
     if (p->islist)
-      new = htsmsg_field_get_list(f);
+      new = (f->hmf_type == HMF_MAP) ?
+              htsmsg_field_get_map(f) :
+              htsmsg_field_get_list(f);
 
     /* Singular */
     else {
index 3097cb305cc2305b43c2d5167a250de18f33e52c..dd8faab71b8d32a7ed08422e554e9234834e571f 100644 (file)
@@ -180,6 +180,17 @@ void service_instance_destroy
 
 void service_instance_list_clear(service_instance_list_t *sil);
 
+/**
+ *
+ */
+typedef struct service_lcn {
+  LIST_ENTRY(service_lcn) sl_link;
+  void     *sl_bouquet;
+  uint32_t  sl_lcn;
+  uint8_t   sl_seen;
+} service_lcn_t;
+
+
 /**
  *
  */
@@ -443,6 +454,11 @@ typedef struct service {
 
   int64_t s_current_pts;
 
+  /*
+   * Local channel numbers per bouquet
+   */
+  LIST_HEAD(,service_lcn) s_lcns;
+
 } service_t;