]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
dvbreorg: Service subscription now possible
authorAndreas Öman <andreas@lonelycoder.com>
Wed, 6 Feb 2013 10:34:49 +0000 (11:34 +0100)
committerAndreas Öman <andreas@lonelycoder.com>
Wed, 6 Feb 2013 20:46:27 +0000 (21:46 +0100)
14 files changed:
src/dvb/dvb.h
src/dvb/dvb_adapter.c
src/dvb/dvb_fe.c
src/dvb/dvb_multiplex.c
src/dvb/dvb_service.c
src/iptv_input.c
src/service.c
src/service.h
src/subscriptions.c
src/subscriptions.h
src/v4l.c
src/webui/extjs_dvb.c
src/webui/static/app/dvb.js
src/webui/static/app/dvb_networks.js

index 9b528554b9862dae52a78abf0387ea346e1d92fb..cfb42e1a0d357cdd618ffed65a02f54f6d228157 100644 (file)
@@ -199,8 +199,6 @@ typedef struct th_dvb_mux_instance {
   int tdmi_tune_failed; // Adapter failed to tune this frequency
                         // Don't try again
 
-  int tdmi_weight;
-
   struct th_subscription_list tdmi_subscriptions;
 
 } th_dvb_mux_instance_t;
@@ -231,6 +229,9 @@ typedef struct dvb_table_feed {
 
 typedef struct th_dvb_adapter {
 
+  int tda_instance;
+
+
   TAILQ_ENTRY(th_dvb_adapter) tda_global_link;
 
   dvb_network_t *tda_dn;
@@ -403,8 +404,6 @@ void dvb_adapter_set_full_mux_rx(th_dvb_adapter_t *tda, int r);
 
 void dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src);
 
-void dvb_adapter_clean(th_dvb_adapter_t *tda);
-
 int dvb_adapter_destroy(th_dvb_adapter_t *tda);
 
 void dvb_adapter_notify(th_dvb_adapter_t *tda);
@@ -490,9 +489,15 @@ dvb_mux_t *dvb_mux_find(dvb_network_t *dn, const char *netname, uint16_t onid,
                         uint16_t tsid, int enabled);
 
 
+void dvb_mux_stop(th_dvb_mux_instance_t *tdmi);
+
 void dvb_mux_initial_scan_done(dvb_mux_t *dm);
 
-int dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi);
+int dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi, const char *reason);
+
+void dvb_create_tdmis(dvb_mux_t *dm);
+
+int tdmi_current_weight(const th_dvb_mux_instance_t *tdmi);
 
 
 /**
index d43938db1724bb961d2a7a29a422d2a47814b182..03cc15a2f31e3bb528a70cfadc16ef435b0a19c6 100644 (file)
@@ -47,7 +47,7 @@
 
 struct th_dvb_adapter_queue dvb_adapters;
 static void *dvb_adapter_input_dvr(void *aux);
-
+static int dvb_adapter_instance_generator;
 
 /**
  *
@@ -59,6 +59,7 @@ tda_alloc(void)
   pthread_mutex_init(&tda->tda_delivery_mutex, NULL);
   TAILQ_INIT(&tda->tda_satconfs);
   streaming_pad_init(&tda->tda_streaming_pad);
+  tda->tda_instance = ++dvb_adapter_instance_generator;
   return tda;
 }
 
@@ -631,87 +632,6 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile)
   }
 }
 
-#if 0
-/**
- * If nobody is subscribing, cycle thru all muxes to get some stats
- * and EIT updates
- */
-void
-dvb_adapter_mux_scanner(void *aux)
-{
-  th_dvb_adapter_t *tda = aux;
-  th_dvb_mux_instance_t *tdmi;
-
-  if(tda->tda_rootpath == NULL)
-    return; // No hardware
-
-  // default period
-  gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 20);
-
-  /* No muxes */
-  if(LIST_FIRST(&tda->tda_dn->dn_mux_instances) == NULL) {
-    dvb_adapter_poweroff(tda);
-    return;
-  }
-
-  /* Someone is actively using */
-  if(service_compute_weight(&tda->tda_transports) > 0)
-    return;
-
-  if(tda->tda_mux_current != NULL &&
-     LIST_FIRST(&tda->tda_mux_current->tdmi_subscriptions) != NULL)
-    return; // Someone is doing full mux dump
-
-  /* Check if we have muxes pending for quickscan, if so, choose them */
-  if((tdmi = TAILQ_FIRST(&tda->tda_dn->dn_initial_scan_queue)) != NULL) {
-    dvb_fe_tune(tdmi, "Initial autoscan");
-    return;
-  }
-
-  /* Check EPG */
-  if (tda->tda_dn->dn_mux_epg) {
-    // timeout anything not complete
-    epggrab_mux_stop(tda->tda_dn->dn_mux_epg, 1);
-    tda->tda_dn->dn_mux_epg = NULL; // skip this time
-  } else {
-    tda->tda_dn->dn_mux_epg = epggrab_mux_next(tda);
-  }
-
-  /* EPG */
-  if (tda->tda_dn->dn_mux_epg) {
-    int period = epggrab_mux_period(tda->tda_dn->dn_mux_epg);
-    if (period > 20)
-      gtimer_arm(&tda->tda_mux_scanner_timer,
-                 dvb_adapter_mux_scanner, tda, period);
-    dvb_fe_tune(tda->tda_dn->dn_mux_epg->dm_tdmi, "EPG scan");
-    return;
-
-  }
-
-  /* Ensure we stop current mux and power off (if required) */
-  if (tda->tda_mux_current)
-    dvb_fe_stop(tda->tda_mux_current, 0);
-}
-#endif
-
-/**
- * 
- */
-void
-dvb_adapter_clone(th_dvb_adapter_t *dst, th_dvb_adapter_t *src)
-{
-#if 0
-  th_dvb_mux_instance_t *tdmi_src, *tdmi_dst;
-
-  lock_assert(&global_lock);
-
-  while((tdmi_dst = LIST_FIRST(&dst->tda_dn->dn_mux_instances)) != NULL)
-    dvb_mux_destroy(tdmi_dst);
-
-  tda_save(dst);
-#endif
-  abort(); // XXX(dvbreorg)
-}
 
 
 /**
@@ -746,24 +666,6 @@ dvb_adapter_destroy(th_dvb_adapter_t *tda)
 #endif
 
 
-/**
- *
- */
-void
-dvb_adapter_clean(th_dvb_adapter_t *tda)
-{
-  service_t *t;
-  
-  lock_assert(&global_lock);
-
-  while((t = LIST_FIRST(&tda->tda_transports)) != NULL)
-    /* Flush all subscribers */
-    service_remove_subscriber(t, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN);
-}
-
-
-
-
 /**
  *
  */
@@ -921,7 +823,6 @@ dvb_adapter_input_dvr(void *aux)
 htsmsg_t *
 dvb_adapter_build_msg(th_dvb_adapter_t *tda)
 {
-  char buf[100];
   htsmsg_t *m = htsmsg_create_map();
   int fdiv;
 
@@ -933,8 +834,8 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda)
   if(tda->tda_current_tdmi != NULL) {
     th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi;
 
-    dvb_mux_nicename(buf, sizeof(buf), tda->tda_current_tdmi->tdmi_mux);
-    htsmsg_add_str(m, "currentMux", buf);
+    htsmsg_add_str(m, "currentMux",
+                   dvb_mux_nicename(tda->tda_current_tdmi->tdmi_mux));
 
     htsmsg_add_u32(m, "signal", MIN(tdmi->tdmi_signal * 100 / 65535, 100));
     htsmsg_add_u32(m, "snr", tdmi->tdmi_snr);
index a01fb59842903e9b3d2d160d4a663844199888e2..916fe523addb723cc6df16ec47c5d020c847c792 100644 (file)
@@ -254,32 +254,6 @@ dvb_fe_monitor(void *aux)
 }
 
 
-#if 0
-/**
- * Stop the given TDMI
- */
-void
-dvb_fe_stop(th_dvb_adapter_t *tda, int retune)
-{
-  lock_assert(&global_lock);
-
-  th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi;
-  assert(tdmi != NULL);
-  dvb_mux_t *dm = tdmi->tdmi_mux;
-
-  dvb_table_flush_all(dm);
-
-  epggrab_mux_stop(dm, 0);
-
-#if 0  /// XXX(dvbreorg)
-  if(!retune) {
-    gtimer_disarm(&tda->tda_fe_monitor_timer);
-    dvb_adapter_stop(tda);
-  }
-#endif
-}
-#endif
-
 
 
 #if DVB_API_VERSION >= 5
@@ -385,22 +359,41 @@ dvb_fe_tune_s2(th_dvb_adapter_t *tda, dvb_mux_conf_t *dmc)
 
 
 
+/**
+ *
+ */
 int
-dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi)
+dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi, const char *reason)
 {
   th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
-  const dvb_mux_t *dm = tdmi->tdmi_mux;
+  dvb_mux_t *dm = tdmi->tdmi_mux;
   // copy dmc, cause frequency may be change with FE_QPSK
   dvb_mux_conf_t dmc = dm->dm_conf;
   dvb_frontend_parameters_t* p = &dmc.dmc_fe_params;
   int r;
   char buf[256];
 
-  dvb_adapter_start(tda);
+  if(tda->tda_current_tdmi != NULL) {
 
-  assert(tda->tda_current_tdmi == NULL);
+    if(tda->tda_current_tdmi == tdmi)
+      return 0; // Already currently tuned
 
+    /*
+     * Adapter tuned to something else.
+     * But at this stage we're no longer caring about such things.
+     * That should have been sorted out by our callers.
+     * So let's just kick it out the current occupant
+      */
+    dvb_mux_stop(tda->tda_current_tdmi);
+  }
 
+  free(tda->tda_tune_reason);
+  tda->tda_tune_reason = strdup(reason);
+
+  dvb_adapter_start(tda);
+
+  // Make sure dvb_mux_stop() did the right thing
+  assert(tda->tda_current_tdmi == NULL);
 
   if(tda->tda_fe_type == FE_QPSK) {
 
@@ -467,8 +460,23 @@ dvb_fe_tune_tdmi(th_dvb_mux_instance_t *tdmi)
   }
 
 
-  if(!r)
+  if(!r) {
     gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
+    tda->tda_current_tdmi = tdmi;
+    dm->dm_current_tdmi = tdmi;
+
+    dvb_table_add_default(dm);
+    epggrab_mux_start(dm);
+    dvb_adapter_notify(tda);
+
+  } else {
+
+    tvhlog(LOG_ERR, "dvb", "\"%s\" tuning to \"%s\""
+           " -- Front configuration failed -- %s",
+           tda->tda_rootpath, dvb_mux_nicename(dm), strerror(errno));
+    /* Mark as bad */
+    tdmi->tdmi_tune_failed = 1;
+  }
 
   return r;
 }
index 00b54484dffd957659c194d493c0fd6b3ed9dffe..b7f115895cf62e5c0f2e2051a6323aefd9c5edb0 100644 (file)
@@ -1293,7 +1293,7 @@ dvb_mux_get_childs(struct idnode *self)
 /**
  * These are created on the fly
  */
-static void
+void
 dvb_create_tdmis(dvb_mux_t *dm)
 {
   th_dvb_mux_instance_t *tdmi;
@@ -1322,36 +1322,64 @@ dvb_create_tdmis(dvb_mux_t *dm)
 /**
  *
  */
-static void
+void
 dvb_mux_stop(th_dvb_mux_instance_t *tdmi)
 {
   dvb_mux_t *dm = tdmi->tdmi_mux;
   th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+  service_t *s;
+
   assert(dm->dm_current_tdmi == tdmi);
   assert(tda->tda_current_tdmi == tdmi);
 
+  lock_assert(&global_lock);
+
+  /* Flush all subscribers */
+  while((s = LIST_FIRST(&tda->tda_transports)) != NULL)
+    service_remove_subscriber(s, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN);
+
   dvb_table_flush_all(dm);
   epggrab_mux_stop(dm, 0);
 
-  assert(dm->dm_scan_status == DM_SCAN_DONE);
+  if(dm->dm_scan_status == DM_SCAN_CURRENT) {
+    /*
+     * If we were currently doing initial scan but lost the adapter
+     * before finishing, put us back on the pending queue
+     */
+    dvb_network_t *dn = dm->dm_dn;
+    TAILQ_REMOVE(&dn->dn_initial_scan_current_queue, dm, dm_scan_link);
+    dm->dm_scan_status = DM_SCAN_PENDING;
+    TAILQ_INSERT_TAIL(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link);
+    dvb_network_schedule_initial_scan(dn);
+  }
 
   dm->dm_current_tdmi = NULL;
   tda->tda_current_tdmi = NULL;
-
-  printf("NEED TO TAKE CARE OF SERVICES in dvb_mux_stop\n");
 }
 
 
 /**
  *
  */
-static int
-tdmi_compute_weight(const th_dvb_mux_instance_t *tdmi)
+int
+tdmi_current_weight(const th_dvb_mux_instance_t *tdmi)
 {
-  const dvb_mux_t *dm = tdmi->tdmi_mux;
-  if(dm->dm_scan_status == DM_SCAN_CURRENT)
-    return 1;
-  return 0;
+  int w = 0;
+  th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+  const service_t *s;
+
+
+  // This max weight of all subscriptions could be kept in the adapter struct
+  pthread_mutex_lock(&tda->tda_delivery_mutex);
+  LIST_FOREACH(s, &tda->tda_transports, s_active_link) {
+    const th_subscription_t *ths;
+    LIST_FOREACH(ths, &s->s_subscriptions, ths_service_link)
+      w = MAX(w, ths->ths_weight);
+  }
+  pthread_mutex_unlock(&tda->tda_delivery_mutex);
+
+  w = MAX(w, tdmi->tdmi_mux->dm_scan_status == DM_SCAN_CURRENT);
+  return w;
 }
 
 
@@ -1379,61 +1407,44 @@ dvb_mux_tune(dvb_mux_t *dm, const char *reason, int weight)
   th_dvb_mux_instance_t *tdmi;
   int r;
 
-  assert(dm->dm_current_tdmi == NULL);
-
   lock_assert(&global_lock);
 
-  dvb_create_tdmis(dm);
+  if(dm->dm_current_tdmi == NULL) {
 
-  retry:
-  // Figure which adapter to use
-  LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link)
-    if(!tdmi->tdmi_tune_failed && tdmi->tdmi_adapter->tda_current_tdmi == NULL)
-      break;
+    dvb_create_tdmis(dm);
 
-  if(tdmi == NULL) {
-    // None available, need to strike one out
-    LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) {
-      if(tdmi->tdmi_tune_failed)
-        continue;
+    while(1) {
+      // Figure which adapter to use
+      LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link)
+        if(!tdmi->tdmi_tune_failed && tdmi->tdmi_adapter->tda_current_tdmi == NULL)
+          break;
 
-      th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
-      th_dvb_mux_instance_t *t2 = tda->tda_current_tdmi;
-      assert(t2 != NULL);
-      assert(t2 != tdmi);
+      if(tdmi == NULL) {
+        // None available, need to strike one out
+        LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) {
+          if(tdmi->tdmi_tune_failed)
+            continue;
 
-      if(tdmi_compute_weight(t2) < weight) {
-        dvb_mux_stop(t2);
-        break;
-      }
-    }
+          th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+          th_dvb_mux_instance_t *t2 = tda->tda_current_tdmi;
+          assert(t2 != NULL);
+          assert(t2 != tdmi);
 
-    if(tdmi == NULL)
-      return SM_CODE_NO_FREE_ADAPTER;
+          if(tdmi_current_weight(t2) < weight) {
+            break;
+          }
+        }
 
-  }
-
-  tdmi->tdmi_weight = weight;
-
-  th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
-
-  free(tda->tda_tune_reason);
-  tda->tda_tune_reason = strdup(reason);
-
-  r = dvb_fe_tune_tdmi(tdmi);
-
-  if(r != 0) {
-    tvhlog(LOG_ERR, "dvb", "\"%s\" tuning to \"%s\""
-           " -- Front configuration failed -- %s",
-           tda->tda_rootpath, dvb_mux_nicename(dm), strerror(errno));
+        if(tdmi == NULL)
+          return SM_CODE_NO_FREE_ADAPTER;
+      }
 
-    /* Mark as bad */
-    tdmi->tdmi_tune_failed = 1;
-    goto retry;
+      r = dvb_fe_tune_tdmi(tdmi, reason);
+      if(!r)
+        break;
+    }
   }
 
-  tda->tda_current_tdmi = tdmi;
-  dm->dm_current_tdmi = tdmi;
   if(dm->dm_scan_status == DM_SCAN_PENDING) {
     TAILQ_REMOVE(&dn->dn_initial_scan_pending_queue, dm, dm_scan_link);
     dm->dm_scan_status = DM_SCAN_CURRENT;
@@ -1442,9 +1453,5 @@ dvb_mux_tune(dvb_mux_t *dm, const char *reason, int weight)
     gtimer_arm(&dm->dm_initial_scan_timeout, dvb_mux_initial_scan_timeout, dm, 10);
   }
 
-  dvb_table_add_default(dm);
-  epggrab_mux_start(dm);
-
-  dvb_adapter_notify(tda);
   return 0;
 }
index 98d14650fe096c450b703359e4dda5cdb11b001e..8b8e1ffb8b60f14b05449662ed99186194dfc37c 100644 (file)
@@ -55,54 +55,48 @@ static htsmsg_t *dvb_service_serialize(service_t *s, int full);
  * transports that is subscribing to the adapter
  */
 static int
-dvb_service_start(service_t *t, unsigned int weight, int force_start)
+dvb_service_start(service_t *s, int instance)
 {
-#if 0
-  int w, r;
-  th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter;
-  th_dvb_mux_instance_t *tdmi = tda->tda_current_tdmi;
-
-  lock_assert(&global_lock);
-
-  if(tda->tda_rootpath == NULL)
-    return SM_CODE_NO_HW_ATTACHED;
+  int r;
+  dvb_mux_t *dm = s->s_dvb_mux;
+  th_dvb_mux_instance_t *tdmi;
 
-  if(t->s_dvb_mux_instance && !t->s_dvb_mux_instance->tdmi_enabled)
-    return SM_CODE_MUX_NOT_ENABLED; /* Mux is disabled */
+  assert(s->s_status == SERVICE_IDLE);
 
-  /* Check if adapter is idle, or already tuned */
+  lock_assert(&global_lock);
 
-  if(tdmi != NULL && 
-     (tdmi != t->s_dvb_mux_instance ||
-      tda->tda_hostconnection == HOSTCONNECTION_USB12)) {
+  LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link)
+    if(tdmi->tdmi_adapter->tda_instance == instance)
+      break;
 
-    w = service_compute_weight(&tda->tda_transports);
-    if(w && w >= weight && !force_start)
-      /* We are outranked by weight, cant use it */
-      return SM_CODE_NOT_FREE;
+  assert(tdmi != NULL); // We should always find this instance
 
-    if(LIST_FIRST(&tdmi->tdmi_subscriptions) != NULL)
-      return SM_CODE_NOT_FREE;
+  r = dvb_fe_tune_tdmi(tdmi, "service start");
 
-    dvb_adapter_clean(tda);
+  if(!r) {
+    th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter;
+    pthread_mutex_lock(&tda->tda_delivery_mutex);
+    LIST_INSERT_HEAD(&tda->tda_transports, s, s_active_link);
+    pthread_mutex_unlock(&tda->tda_delivery_mutex);
+    tda->tda_open_service(tda, s);
+    dvb_table_add_pmt(dm, s->s_pmt_pid);
   }
 
-  pthread_mutex_lock(&tda->tda_delivery_mutex);
-
-  r = dvb_fe_tune(t->s_dvb_mux_instance->tdmi_mux, "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);
+  return r;
+}
 
-  dvb_table_add_pmt(t->s_dvb_mux_instance->tdmi_mux, t->s_pmt_pid);
 
-  return r;
-#endif
-  return SM_CODE_NO_FREE_ADAPTER;
+/**
+ *
+ */
+static th_dvb_adapter_t *
+dvb_service_get_tda(service_t *s)
+{
+  dvb_mux_t *dm = s->s_dvb_mux;
+  assert(dm->dm_current_tdmi != NULL);
+  th_dvb_adapter_t *tda = dm->dm_current_tdmi->tdmi_adapter;
+  assert(tda != NULL);
+  return tda;
 }
 
 
@@ -110,21 +104,18 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start)
  *
  */
 static void
-dvb_service_stop(service_t *t)
+dvb_service_stop(service_t *s)
 {
-#if 0
-  th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter;
-
+  th_dvb_adapter_t *tda = dvb_service_get_tda(s);
   lock_assert(&global_lock);
 
   pthread_mutex_lock(&tda->tda_delivery_mutex);
-  LIST_REMOVE(t, s_active_link);
+  LIST_REMOVE(s, s_active_link);
   pthread_mutex_unlock(&tda->tda_delivery_mutex);
 
-  tda->tda_close_service(tda, t);
+  tda->tda_close_service(tda, s);
 
-  t->s_status = SERVICE_IDLE;
-#endif
+  s->s_status = SERVICE_IDLE;
 }
 
 
@@ -132,14 +123,32 @@ dvb_service_stop(service_t *t)
  *
  */
 static void
-dvb_service_refresh(service_t *t)
+dvb_service_refresh(service_t *s)
 {
-#if 0
-  th_dvb_adapter_t *tda = t->s_dvb_mux_instance->tdmi_adapter;
-
+  th_dvb_adapter_t *tda = dvb_service_get_tda(s);
   lock_assert(&global_lock);
-  tda->tda_open_service(tda, t);
-#endif
+  tda->tda_open_service(tda, s);
+}
+
+
+/**
+ *
+ */
+static void
+dvb_service_enlist(struct service *s, struct service_instance_list *sil)
+{
+  dvb_mux_t *dm = s->s_dvb_mux;
+  th_dvb_mux_instance_t *tdmi;
+
+  dvb_create_tdmis(dm);
+
+  LIST_FOREACH(tdmi, &dm->dm_tdmis, tdmi_mux_link) {
+    if(tdmi->tdmi_tune_failed)
+      continue; // The hardware is not able to tune to this frequency, never consider it
+
+    service_instance_add(sil, s, tdmi->tdmi_adapter->tda_instance, 100,
+                         tdmi_current_weight(tdmi));
+  }
 }
 
 
@@ -389,6 +398,7 @@ dvb_service_find2(dvb_mux_t *dm, uint16_t sid, int pmt_pid,
   t->s_setsourceinfo = dvb_service_setsourceinfo;
   t->s_grace_period  = dvb_grace_period;
   t->s_serialize     = dvb_service_serialize;
+  t->s_enlist        = dvb_service_enlist;
 
   t->s_dvb_mux = dm;
   LIST_INSERT_HEAD(&dm->dm_services, t, s_group_link);
index 5abc4ef38d85050243e45145c05c8a921c95d0c7..57c994e970dec5468245ae5f81d966c43c79538e 100644 (file)
@@ -198,7 +198,7 @@ iptv_thread(void *aux)
  *
  */
 static int
-iptv_service_start(service_t *t, unsigned int weight, int force_start)
+iptv_service_start(service_t *t, int instance)
 {
   pthread_t tid;
   int fd;
index c625148d98aed61b9fb6e998319f2d538920f070..220f4cbc03f20b01813e94d5dcac99f4c8336990 100644 (file)
@@ -176,7 +176,7 @@ service_stop(service_t *t)
  */
 void
 service_remove_subscriber(service_t *t, th_subscription_t *s,
-                           int reason)
+                          int reason)
 {
   lock_assert(&global_lock);
 
@@ -197,7 +197,7 @@ service_remove_subscriber(service_t *t, th_subscription_t *s,
  *
  */
 int
-service_start(service_t *t, unsigned int weight, int force_start)
+service_start(service_t *t, int instance)
 {
   elementary_stream_t *st;
   int r, timeout = 2;
@@ -208,7 +208,7 @@ service_start(service_t *t, unsigned int weight, int force_start)
   t->s_streaming_status = 0;
   t->s_pcr_drift = 0;
 
-  if((r = t->s_start_feed(t, weight, force_start)))
+  if((r = t->s_start_feed(t, instance)))
     return r;
 
   cwc_service_start(t);
@@ -234,12 +234,73 @@ service_start(service_t *t, unsigned int weight, int force_start)
   return 0;
 }
 
+
+/**
+ * Main entry point for starting a service based on a channel
+ */
+service_instance_t *
+service_find_instance(channel_t *ch, struct service_instance_list *sil,
+                      int *error, int weight)
+{
+  service_t *s;
+  service_instance_t *si, *next;
+
+  lock_assert(&global_lock);
+
+  // First, update list of candidates
+
+  LIST_FOREACH(si, sil, si_link)
+    si->si_mark = 1;
+
+  LIST_FOREACH(s, &ch->ch_services, s_ch_link)
+    s->s_enlist(s, sil);
+
+  for(si = LIST_FIRST(sil); si != NULL; si = next) {
+    next = LIST_NEXT(si, si_link);
+    if(si->si_mark)
+      service_instance_destroy(si);
+  }
+
+  printf("Service start, enlisted candidates\n");
+  LIST_FOREACH(si, sil, si_link)
+    printf("  %s i:%d w:%d p:%d\n", si->si_s->s_nicename, si->si_instance, si->si_weight, si->si_prio);
+
+
+  // Check if any service is already running, if so, use that
+  LIST_FOREACH(si, sil, si_link)
+    if(si->si_s->s_status == SERVICE_RUNNING)
+      return si;
+
+  // Check if any is idle
+  LIST_FOREACH(si, sil, si_link)
+    if(si->si_weight == 0 && si->si_error == 0)
+      break;
+
+  // Check if to kick someone out
+  if(si == NULL) {
+    LIST_FOREACH(si, sil, si_link) {
+      if(si->si_weight < weight && si->si_error == 0)
+        break;
+    }
+  }
+
+  if(si == NULL) {
+    *error = SM_CODE_NO_FREE_ADAPTER;
+    return NULL;
+  }
+
+  service_start(si->si_s, si->si_instance);
+  return NULL;
+}
+
+
+
+#if 0
 /**
  *
  */
 service_t *
-service_find(channel_t *ch, unsigned int weight, const char *loginfo,
-              int *errorp, service_t *skip)
+service_enlist(channel_t *ch)
 {
   service_t *t, **vec;
   int cnt = 0, i, r, off;
@@ -321,6 +382,7 @@ service_find(channel_t *ch, unsigned int weight, const char *loginfo,
     *errorp = SM_CODE_NO_SERVICE;
   return NULL;
 }
+#endif
 
 
 /**
@@ -1031,38 +1093,66 @@ service_refresh_channel(service_t *t)
  *
  */
 static int
-ssc_cmp(const service_start_cand_t *a,
-        const service_start_cand_t *b)
+si_cmp(const service_instance_t *a, const service_instance_t *b)
 {
-  return a->ssc_prio - b->ssc_prio;
+  return a->si_prio - b->si_prio;
 }
 
 /**
  *
  */
-service_start_cand_t *
-service_find_cand(struct service_start_cand_list *sscl,
-                  struct service *s, int instance, int prio)
+service_instance_t *
+service_instance_add(struct service_instance_list *sil,
+                     struct service *s, int instance, int prio,
+                     int weight)
 {
-  service_start_cand_t *ssc;
-  LIST_FOREACH(ssc, sscl, ssc_link) {
-    if(ssc->ssc_s == s && ssc->ssc_instance == instance)
+  service_instance_t *si;
+  LIST_FOREACH(si, sil, si_link)
+    if(si->si_s == s && si->si_instance == instance)
       break;
-  }
 
-  if(ssc != NULL) {
-    ssc = calloc(1, sizeof(service_start_cand_t));
-    ssc->ssc_s = s;
+  if(si == NULL) {
+    si = calloc(1, sizeof(service_instance_t));
+    si->si_s = s;
     service_ref(s);
-    ssc->ssc_instance = instance;
+    si->si_instance = instance;
+    si->si_weight = weight;
   } else {
-    if(ssc->ssc_prio == prio)
-      return ssc;
-    LIST_REMOVE(ssc, ssc_link);
+    si->si_mark = 0;
+    if(si->si_prio == prio && si->si_weight == weight)
+      return si;
+    LIST_REMOVE(si, si_link);
   }
-  ssc->ssc_prio = prio;
-  LIST_INSERT_SORTED(sscl, ssc, ssc_link, ssc_cmp);
-  return ssc;
+  si->si_weight = weight;
+  si->si_prio = prio;
+  LIST_INSERT_SORTED(sil, si, si_link, si_cmp);
+  return si;
+}
+
+
+/**
+ *
+ */
+void
+service_instance_destroy(service_instance_t *si)
+{
+  LIST_REMOVE(si, si_link);
+  service_unref(si->si_s);
+  free(si);
+}
+
+
+/**
+ *
+ */
+void
+service_instance_list_clear(struct service_instance_list *sil)
+{
+  lock_assert(&global_lock);
+
+  service_instance_t *si;
+  while((si = LIST_FIRST(sil)) != NULL)
+    service_instance_destroy(si);
 }
 
 
index 294ef695ebdfe1f307c4f419b333adb0a997be42..d045231ad9e168ad52ace19c203dfa0885770298 100644 (file)
@@ -167,35 +167,49 @@ typedef struct elementary_stream {
 } elementary_stream_t;
 
 
-LIST_HEAD(service_start_cand_list, service_start_cand);
+LIST_HEAD(service_instance_list, service_instance);
 
 /**
  *
  */
-typedef struct service_start_cand {
+typedef struct service_instance {
 
-  LIST_ENTRY(service_start_cand) ssc_link;
+  LIST_ENTRY(service_instance) si_link;
 
-  int ssc_prio;
+  int si_prio;
 
-  struct service *ssc_s;  // A reference is held
-  int ssc_instance;       // Discriminator when having multiple adapters, etc
+  struct service *si_s; // A reference is held
+  int si_instance;       // Discriminator when having multiple adapters, etc
 
-  int ssc_error_code;     // Set if we deem this cand to be broken
-  time_t ssc_err_time;    // Time we detected it was broken
+  int si_error;        /* Set if subscription layer deem this cand
+                          to be broken. We typically set this if we
+                          have not seen any demuxed packets after
+                          the grace period has expired.
+                          The actual value is current time
+                       */
 
-  int ssc_weight;         // Highest weight that holds this cand
+  time_t si_error_time;
 
-} service_start_cand_t;
+
+  int si_weight;         // Highest weight that holds this cand
+
+  int si_mark;           // For mark & sweep
+
+} service_instance_t;
 
 
 /**
  *
  */
-service_start_cand_t *service_find_cand(struct service_start_cand_list *sscl,
-                                        struct service *s,
-                                        int instance,
-                                        int prio);
+service_instance_t *service_instance_add(struct service_instance_list *sil,
+                                         struct service *s,
+                                         int instance,
+                                         int prio,
+                                         int weight);
+
+void service_instance_destroy(service_instance_t *si);
+
+void service_instance_list_clear(struct service_instance_list *sil);
 
 /**
  *
@@ -285,12 +299,9 @@ typedef struct service {
 
   LIST_HEAD(, th_subscription) s_subscriptions;
 
-  void (*s_enlist)(struct service *s,
-                   struct service_start_cand_list *sscl);
-
+  void (*s_enlist)(struct service *s, struct service_instance_list *sil);
 
-  int (*s_start_feed)(struct service *t, unsigned int weight,
-                       int force_start);
+  int (*s_start_feed)(struct service *s, int instance);
 
   void (*s_refresh_feed)(struct service *t);
 
@@ -540,7 +551,7 @@ void service_init(void);
 
 unsigned int service_compute_weight(struct service_list *head);
 
-int service_start(service_t *t, unsigned int weight, int force_start);
+int service_start(service_t *t, int instance);
 
 service_t *service_create(const char *uuid, int source_type);
 
@@ -552,9 +563,10 @@ service_t *service_find_by_identifier(const char *identifier);
 
 void service_map_channel(service_t *t, struct channel *ch, int save);
 
-service_t *service_find(struct channel *ch, unsigned int weight,
-                       const char *loginfo, int *errorp,
-                       service_t *skip);
+service_instance_t *service_find_instance(struct channel *ch,
+                                          struct service_instance_list *sil,
+                                          int *error,
+                                          int weight);
 
 elementary_stream_t *service_stream_find(service_t *t, int pid);
 
index e70260807072827b4d42e6aeed2680b553843a90..28a6cf6a6bad05491c246197a45fa1c053aacbca 100644 (file)
@@ -155,9 +155,8 @@ void
 subscription_reschedule(void)
 {
   th_subscription_t *s;
-  service_t *t, *skip;
+  service_instance_t *si;
   streaming_message_t *sm;
-  char buf[128];
   int error;
 
   lock_assert(&global_lock);
@@ -173,19 +172,20 @@ subscription_reschedule(void)
       /* Already got a service */
 
       if(s->ths_state != SUBSCRIPTION_BAD_SERVICE)
-       continue; /* And it seems to work ok, so we're happy */
-      skip = s->ths_service;
-      error = s->ths_testing_error;
-      service_remove_subscriber(s->ths_service, s, s->ths_testing_error);
-    } else {
-      error = 0;
-      skip = NULL;
+       continue; /* And it not bad, so we're happy */
+
+      si = s->ths_current_instance;
+
+      assert(si != NULL);
+      si->si_error = s->ths_testing_error;
+      time(&si->si_error_time);
     }
 
-    snprintf(buf, sizeof(buf), "Subscription \"%s\"", s->ths_title);
-    t = service_find(s->ths_channel, s->ths_weight, buf, &error, skip);
+    si = service_find_instance(s->ths_channel, &s->ths_instances, &error,
+                               s->ths_weight);
+    s->ths_current_instance = si;
 
-    if(t == NULL) {
+    if(si == NULL) {
       /* No service available */
 
       sm = streaming_msg_create_code(SMT_NOSTART, error);
@@ -193,7 +193,7 @@ subscription_reschedule(void)
       continue;
     }
 
-    subscription_link_service(s, t);
+    subscription_link_service(s, si->si_s);
   }
 }
 
@@ -207,6 +207,8 @@ subscription_unsubscribe(th_subscription_t *s)
 
   lock_assert(&global_lock);
 
+  service_instance_list_clear(&s->ths_instances);
+
   LIST_REMOVE(s, ths_global_link);
 
   if(s->ths_channel != NULL) {
@@ -407,6 +409,7 @@ th_subscription_t *
 subscription_create_from_service(service_t *t, const char *name,
                                 streaming_target_t *st, int flags)
 {
+#if 0
   th_subscription_t *s;
   source_info_t si;
   int r;
@@ -443,6 +446,8 @@ subscription_create_from_service(service_t *t, const char *name,
   subscription_link_service(s, t);
   notify_reload("subscriptions");
   return s;
+#endif
+  abort();
 }
 
 
index 0a88e5f51a4e06e3d359b0b8b1720c2a91948b6b..f5166f7aed989c975331ce3cc2274fc0213e6005 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef SUBSCRIPTIONS_H
 #define SUBSCRIPTIONS_H
 
+#include "service.h"
+
 extern struct th_subscription_list subscriptions;
 
 #define SUBSCRIPTION_RAW_MPEGTS 0x1
@@ -65,6 +67,12 @@ typedef struct th_subscription {
   char *ths_username;
   char *ths_client;
 
+  /**
+   * This is the list of service candidates we have
+   */
+  struct service_instance_list ths_instances;
+  struct service_instance *ths_current_instance;
+
   // Ugly ugly ugly to refer DVB code here
 
   LIST_ENTRY(th_subscription) ths_tdmi_link;
index 6fae675011167070b0638e39865f2f1440642586..1e76ba1a8f59caa64c7500f6ac46323d2890e006 100644 (file)
--- a/src/v4l.c
+++ b/src/v4l.c
@@ -170,7 +170,7 @@ v4l_thread(void *aux)
  *
  */
 static int
-v4l_service_start(service_t *t, unsigned int weight, int force_start)
+v4l_service_start(service_t *t, int instance)
 {
   v4l_adapter_t *va = t->s_v4l_adapter;
   int frequency = t->s_v4l_frequency;
index f321200ae4f480bfeb64def88af1a4ccec1c7eb9..408e2d3bd096b41d3e50f11aaf401a7130a17b97 100644 (file)
@@ -221,23 +221,25 @@ extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
 
     out = htsmsg_create_map();
     htsmsg_add_u32(out, "success", 1);
-#if 0
+
   } else if(!strcmp(op, "serviceprobe")) {
 
     tvhlog(LOG_NOTICE, "web interface",
           "Service probe started on \"%s\"", tda->tda_displayname);
 
-    LIST_FOREACH(tdmi, &tda->tda_dn->dn_mux_instances, tdmi_adapter_link) {
-      LIST_FOREACH(t, &tdmi->tdmi_mux->dm_services, s_group_link) {
-       if(t->s_enabled)
-         serviceprobe_enqueue(t);
+    dvb_mux_t *dm;
+    service_t *s;
+
+    LIST_FOREACH(dm, &tda->tda_dn->dn_muxes, dm_network_link) {
+      LIST_FOREACH(s, &dm->dm_services, s_group_link) {
+        if(s->s_enabled)
+         serviceprobe_enqueue(s);
       }
     }
 
 
     out = htsmsg_create_map();
     htsmsg_add_u32(out, "success", 1);
-#endif
 
   } else {
     pthread_mutex_unlock(&global_lock);
index bdbc022b3ed21e5f7dc75b3c90c9b6c97cd35d50..89e6e4cbfcb7a6cdcafc572265650c1430306d3e 100644 (file)
@@ -1266,10 +1266,13 @@ tvheadend.dvb_adapter_general = function(adapterData, satConfStore) {
                if (r.data.identifier != adapterId) return;
                infoTemplate.overwrite(infoPanel.body, r.data);
 
+          serviceScanBtn.enable();
+
                if (r.data.services > 0 && r.data.initialMuxes == 0) serviceScanBtn
                        .enable();
                else serviceScanBtn.disable();
        });
+  serviceScanBtn.enable();
 
        return panel;
 }
index 44b6868758b2556f200082375b71e04726f35a2b..378df82bf622dd3eb6b8111f05d9099c862e9d16 100644 (file)
@@ -13,7 +13,12 @@ tvheadend.dvb_networks = function() {
     root : new Ext.tree.AsyncTreeNode({
       id : 'root',
       text: 'DVB Networks'
-    })
+    }),
+    listeners: {
+      click: function(n) {
+        Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');
+      }
+    }
   });