]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
mpegts: new feature for scheduling mux tuning events
authorAdam Sutton <dev@adamsutton.me.uk>
Mon, 21 Apr 2014 18:33:17 +0000 (19:33 +0100)
committerAdam Sutton <dev@adamsutton.me.uk>
Mon, 21 Apr 2014 18:39:04 +0000 (19:39 +0100)
Could be useful for requesting periodic tuning of mux for EMM harvesting.
Will eventually be used to replace the current OTA EPG scheduling.

Makefile
src/api/api_mpegts.c
src/input.h
src/input/mpegts/mpegts_mux_sched.c [new file with mode: 0644]
src/input/mpegts/mpegts_mux_sched.h [new file with mode: 0644]
src/main.c
src/webui/static/app/mpegts.js
src/webui/static/app/tvheadend.js

index 2f1d995e210219b7d8557e0325bdae11651bf071..d93c70bb204fc5d3d9a78d706b1567740223cd63 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -178,6 +178,7 @@ SRCS-$(CONFIG_MPEGTS) += \
        src/input/mpegts/dvb_charset.c \
        src/input/mpegts/dvb_psi.c \
        src/input/mpegts/tsdemux.c \
+       src/input/mpegts/mpegts_mux_sched.c \
 
 # MPEGTS DVB
 SRCS-${CONFIG_MPEGTS_DVB} += \
index 9409319e7dfa2746b77fc4657ae31ff1971472b8..c5084aa13bd6763b68d7dc8f7643497d149fad41 100644 (file)
@@ -251,6 +251,43 @@ api_mpegts_service_grid
   }
 }
 
+/*
+ * Mux scheduler
+ */
+static void
+api_mpegts_mux_sched_grid
+  ( idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
+{
+  mpegts_mux_sched_t *mms;
+  LIST_FOREACH(mms, &mpegts_mux_sched_all, mms_link)
+    idnode_set_add(ins, (idnode_t*)mms, &conf->filter);
+}
+
+static int
+api_mpegts_mux_sched_create
+  ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  int err;
+  htsmsg_t *conf;
+  mpegts_mux_sched_t *mms;
+
+  if (!(conf  = htsmsg_get_map(args, "conf")))
+    return EINVAL;
+
+  pthread_mutex_lock(&global_lock);
+  mms = mpegts_mux_sched_create(NULL, conf);
+  if (mms) {
+    err = 0;
+    *resp = htsmsg_create_map();
+    mpegts_mux_sched_save(mms);
+  } else {
+    err = EINVAL;
+  }
+  pthread_mutex_unlock(&global_lock);
+
+  return err;
+}
+
 #if ENABLE_MPEGTS_DVB
 static int
 api_dvb_scanfile_list
@@ -321,6 +358,9 @@ api_mpegts_init ( void )
     { "mpegts/mux/class",          ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_mux_class },
     { "mpegts/service/grid",       ACCESS_ANONYMOUS, api_idnode_grid,  api_mpegts_service_grid },
     { "mpegts/service/class",      ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_service_class },
+    { "mpegts/mux_sched/class",    ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_mux_sched_class },
+    { "mpegts/mux_sched/grid",     ACCESS_ANONYMOUS, api_idnode_grid, api_mpegts_mux_sched_grid },
+    { "mpegts/mux_sched/create",   ACCESS_ANONYMOUS, api_mpegts_mux_sched_create, NULL },
 #if ENABLE_MPEGTS_DVB
     { "dvb/scanfile/list",         ACCESS_ANONYMOUS, api_dvb_scanfile_list, NULL },
 #endif
index 2b28884df3ee3dc5ecce6f384f9ca83fadcaa5e4..6de228f9c761d88da4b287aefd626eea3f11ae7a 100644 (file)
@@ -115,6 +115,7 @@ void tvh_input_stream_destroy ( tvh_input_stream_t *st );
 
 #if ENABLE_MPEGTS
 #include "input/mpegts.h"
+#include "input/mpegts/mpegts_mux_sched.h"
 #if ENABLE_MPEGTS_DVB
 #include "input/mpegts/mpegts_dvb.h"
 #endif
diff --git a/src/input/mpegts/mpegts_mux_sched.c b/src/input/mpegts/mpegts_mux_sched.c
new file mode 100644 (file)
index 0000000..13e6762
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ *  Tvheadend - TS file input system
+ *
+ *  Copyright (C) 2014 Adam Sutton
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "input.h"
+#include "input/mpegts/mpegts_mux_sched.h"
+#include "streaming.h"
+#include "settings.h"
+
+static void mpegts_mux_sched_timer ( void *p );
+static void mpegts_mux_sched_input ( void *p, streaming_message_t *sm );
+
+mpegts_mux_sched_list_t mpegts_mux_sched_all;
+
+/******************************************************************************
+ * Class
+ *****************************************************************************/
+
+static void
+mpegts_mux_sched_set_timer ( mpegts_mux_sched_t *mms )
+{
+  /* Upate timer */
+  if (!mms->mms_enabled) {
+    if (mms->mms_sub)
+      subscription_unsubscribe(mms->mms_sub);
+    mms->mms_sub    = NULL;
+    mms->mms_active = 0;
+    gtimer_disarm(&mms->mms_timer);
+  } else if (mms->mms_active) {
+    if (mms->mms_timeout <= 0)
+      gtimer_disarm(&mms->mms_timer);
+    else {
+      gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms,
+                 mms->mms_timeout);
+    }
+  } else {
+    time_t now, nxt;
+    time(&now);
+    if (!cron_next(&mms->mms_cronjob, now, &nxt)) {
+      gtimer_arm_abs(&mms->mms_timer, mpegts_mux_sched_timer, mms, nxt);
+    }
+  }
+}
+
+static void
+mpegts_mux_sched_class_save ( idnode_t *in )
+{
+  mpegts_mux_sched_t *mms = (mpegts_mux_sched_t*)in;
+
+  /* Update timer */
+  mpegts_mux_sched_set_timer(mms);
+
+  /* Save */
+  mpegts_mux_sched_save(mms);
+}
+
+static void
+mpegts_mux_sched_class_delete ( idnode_t *in )
+{
+  mpegts_mux_sched_delete((mpegts_mux_sched_t*)in, 1);
+}
+
+static htsmsg_t *
+mpegts_mux_sched_class_mux_list ( void *o )
+{
+  htsmsg_t *m, *p;
+
+  p = htsmsg_create_map();
+  htsmsg_add_str (p, "class", "mpegts_mux");
+  htsmsg_add_bool(p, "enum",  1);
+
+  m = htsmsg_create_map();
+  htsmsg_add_str (m, "type",  "api");
+  htsmsg_add_str (m, "uri",   "idnode/load");
+  htsmsg_add_str (m, "event", "mpegts_mux");
+  htsmsg_add_msg (m, "params", p);
+  
+  return m;
+}
+
+static int
+mpegts_mux_sched_class_cron_set ( void *p, const void *v )
+{
+  mpegts_mux_sched_t *mms = p;
+  const char *str = v;
+  if (strcmp(str, mms->mms_cronstr ?: "")) {
+    if (!cron_set(&mms->mms_cronjob, str)) {
+      free(mms->mms_cronstr);
+      mms->mms_cronstr = strdup(str);
+      return 1;
+    } else {
+      tvhwarn("muxsched", "invalid cronjob spec (%s)", str);
+    }
+  }
+  return 0;
+}
+
+const idclass_t mpegts_mux_sched_class =
+{
+  .ic_class      = "mpegts_mux_sched",
+  .ic_caption    = "Mux Sched Entry",
+  .ic_event      = "mpegts_mux_sched",
+  .ic_save       = mpegts_mux_sched_class_save,
+  .ic_delete     = mpegts_mux_sched_class_delete,
+  .ic_properties = (const property_t[]){
+    {
+      .type     = PT_BOOL,
+      .id       = "enabled",
+      .name     = "Enabled",
+      .off      = offsetof(mpegts_mux_sched_t, mms_enabled),
+      .def.i    = 1,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "mux",
+      .name     = "Mux",
+      .off      = offsetof(mpegts_mux_sched_t, mms_mux),
+      .list     = mpegts_mux_sched_class_mux_list,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "cron",
+      .name     = "Cron",
+      .off      = offsetof(mpegts_mux_sched_t, mms_cronstr),
+      .set      = mpegts_mux_sched_class_cron_set,
+    },
+    {
+      .type     = PT_INT,
+      .id       = "timeout",
+      .name     = "Timout (secs)",
+      .off      = offsetof(mpegts_mux_sched_t, mms_timeout),
+    },
+    {
+    },
+  }
+};
+
+/******************************************************************************
+ * Input
+ *****************************************************************************/
+
+static void
+mpegts_mux_sched_input ( void *p, streaming_message_t *sm )
+{
+  mpegts_mux_sched_t *mms = p;
+
+  switch (sm->sm_type) {
+    case SMT_STOP:
+      gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms, 0);
+      break;
+    default:
+      // ignore
+      break;
+  }
+  streaming_msg_free(sm);
+}
+
+/******************************************************************************
+ * Timer
+ *****************************************************************************/
+
+static void
+mpegts_mux_sched_timer ( void *p )
+{
+  mpegts_mux_t *mm;
+  mpegts_mux_sched_t *mms = p;
+  time_t now, nxt;
+
+  /* Not enabled (shouldn't be running) */
+  if (!mms->mms_enabled)
+    return;
+
+  /* Invalid config (creating?) */
+  if (!mms->mms_mux)
+    return;
+  
+  /* Find mux */
+  if (!(mm = mpegts_mux_find(mms->mms_mux))) {
+    tvhdebug("muxsched", "mux has been removed, delete sched entry");
+    mpegts_mux_sched_delete(mms, 1);
+    return;
+  }
+
+  /* Current time */
+  time(&now);
+
+  /* Start sub */
+  if (!mms->mms_active) {
+    assert(mms->mms_sub == NULL);
+
+    mms->mms_sub
+      = subscription_create_from_mux(mm, mms->mms_weight,
+                                     mms->mms_creator ?: "",
+                                     &mms->mms_input,
+                                     SUBSCRIPTION_NONE,
+                                     NULL, NULL, NULL, NULL);
+
+    /* Failed (try-again soon) */
+    if (!mms->mms_sub) {
+      gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms, 60);
+
+    /* OK */
+    } else {
+      mms->mms_active = 1;
+      if (mms->mms_timeout > 0) {
+        gtimer_arm(&mms->mms_timer, mpegts_mux_sched_timer, mms,
+                   mms->mms_timeout);
+      } 
+    }
+
+  /* Cancel sub */
+  } else {
+    if (mms->mms_sub) {
+      subscription_unsubscribe(mms->mms_sub);
+      mms->mms_sub = NULL;
+    }
+    mms->mms_active = 0;
+
+    /* Find next */
+    if (cron_next(&mms->mms_cronjob, now, &nxt)) {
+      tvherror("muxsched", "failed to find next event");
+      return;
+    }
+
+    /* Timer */
+    gtimer_arm_abs(&mms->mms_timer, mpegts_mux_sched_timer, mms, nxt);
+  }
+}
+
+/******************************************************************************
+ * Init / Create
+ *****************************************************************************/
+
+mpegts_mux_sched_t *
+mpegts_mux_sched_create ( const char *uuid, htsmsg_t *conf )
+{
+  mpegts_mux_sched_t *mms;
+
+  if (!(mms = calloc(1, sizeof(mpegts_mux_sched_t)))) {
+    tvherror("muxsched", "calloc() failed");
+    assert(0);
+    return NULL;
+  }
+
+  /* Insert node */
+  idnode_insert(&mms->mms_id, uuid, &mpegts_mux_sched_class);
+
+  /* Add to list */
+  LIST_INSERT_HEAD(&mpegts_mux_sched_all, mms, mms_link);
+
+  /* Initialise */
+  streaming_target_init(&mms->mms_input, mpegts_mux_sched_input, mms, 0);
+
+  /* Load conf */
+  if (conf)
+    idnode_load(&mms->mms_id, conf);
+
+  /* Validate */
+  if (!mpegts_mux_find(mms->mms_mux ?: "") ||
+      !mms->mms_cronstr) {
+    mpegts_mux_sched_delete(mms, 1);
+    return NULL;
+  }
+
+  /* Set timer */
+  mpegts_mux_sched_set_timer(mms);
+
+  return mms;
+}
+
+void
+mpegts_mux_sched_save ( mpegts_mux_sched_t *mms )
+{
+  htsmsg_t *c = htsmsg_create_map();
+  idnode_save(&mms->mms_id, c);
+  hts_settings_save(c, "muxsched/%s", idnode_uuid_as_str(&mms->mms_id));
+  htsmsg_destroy(c);
+}
+
+void
+mpegts_mux_sched_delete ( mpegts_mux_sched_t *mms, int delconf )
+{
+  LIST_REMOVE(mms, mms_link);
+  if (delconf)
+    hts_settings_remove("muxsched/%s", idnode_uuid_as_str(&mms->mms_id));
+  if (mms->mms_sub)
+    subscription_unsubscribe(mms->mms_sub);
+  gtimer_disarm(&mms->mms_timer);
+  idnode_unlink(&mms->mms_id);
+  free(mms->mms_cronstr);
+  free(mms->mms_mux);
+  free(mms->mms_creator);
+  free(mms);
+}
+
+void
+mpegts_mux_sched_init ( void )
+{
+  htsmsg_t *c, *e;
+  htsmsg_field_t *f;
+
+  /* Load settings */
+  if ((c = hts_settings_load_r(1, "muxsched"))) {
+    HTSMSG_FOREACH(f, c) {
+      if (!(e = htsmsg_field_get_map(f))) continue;
+      mpegts_mux_sched_create(f->hmf_name, e);
+    }
+    htsmsg_destroy(c);
+  }
+}
+
+void
+mpegts_mux_sched_done ( void )
+{
+  mpegts_mux_sched_t *mms;
+  pthread_mutex_lock(&global_lock);
+  while ((mms = LIST_FIRST(&mpegts_mux_sched_all)))
+    mpegts_mux_sched_delete(mms, 0);
+  pthread_mutex_unlock(&global_lock);
+}
+
+/******************************************************************************
+ * Editor Configuration
+ *
+ * vim:sts=2:ts=2:sw=2:et
+ *****************************************************************************/
diff --git a/src/input/mpegts/mpegts_mux_sched.h b/src/input/mpegts/mpegts_mux_sched.h
new file mode 100644 (file)
index 0000000..f5dfa12
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Tvheadend - TS file input system
+ *
+ *  Copyright (C) 2014 Adam Sutton
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TVH_MPEGTS_MUX_SCHED_H__
+#define __TVH_MPEGTS_MUX_SCHED_H__
+
+#include "tvheadend.h"
+#include "cron.h"
+#include "idnode.h"
+#include "subscriptions.h"
+
+typedef LIST_HEAD(,mpegts_mux_sched) mpegts_mux_sched_list_t;
+
+extern mpegts_mux_sched_list_t mpegts_mux_sched_all;
+
+extern const idclass_t mpegts_mux_sched_class;
+
+typedef struct mpegts_mux_sched
+{
+  idnode_t mms_id;
+
+  LIST_ENTRY(mpegts_mux_sched) mms_link;
+
+  /*
+   * Configuration
+   */
+  int             mms_enabled;  ///< Enabled
+  char           *mms_cronstr;  ///< Cron configuration string
+  char           *mms_mux;      ///< Mux UUID
+  char           *mms_creator;  ///< Creator of entry
+  int             mms_timeout;  ///< Timeout (in seconds)
+  int             mms_weight;   ///< Weighting
+
+  /*
+   * Cron handling
+   */
+  int             mms_active;   ///< Subscription is active
+  gtimer_t        mms_timer;    ///< Timer for start/end
+  cron_t          mms_cronjob;  ///< Cron spec
+  
+  /*
+   * Subscription
+   */
+  th_subscription_t  *mms_sub;      ///< Subscription handler
+  streaming_target_t  mms_input;    ///< Streaming input
+
+} mpegts_mux_sched_t;
+
+mpegts_mux_sched_t *mpegts_mux_sched_create ( const char *uuid, htsmsg_t *c );
+void mpegts_mux_sched_delete ( mpegts_mux_sched_t *mms, int delconf );
+void mpegts_mux_sched_save   ( mpegts_mux_sched_t *mms );
+
+void mpegts_mux_sched_init ( void );
+void mpegts_mux_sched_done ( void );
+
+
+#endif /* __TVH_MPEGTS_H__ */
+
+/******************************************************************************
+ * Editor Configuration
+ *
+ * vim:sts=2:ts=2:sw=2:et
+ *****************************************************************************/
index 93b0beb5ab0ca003c45b3ef8f3d6f1b5d4e66f3d..f23bec0e250ca7f57c0c22fe21495e208927caa9 100644 (file)
@@ -771,6 +771,9 @@ main(int argc, char **argv)
 #if ENABLE_LINUXDVB
   linuxdvb_init(adapter_mask);
 #endif
+#if ENABLE_MPEGTS
+  mpegts_mux_sched_init();
+#endif
 
   channel_init();
 
@@ -837,6 +840,9 @@ main(int argc, char **argv)
   tvhftrace("main", webui_done);
   tvhftrace("main", http_client_done);
   tvhftrace("main", fsmonitor_done);
+#if ENABLE_MPEGTS
+  tvhftrace("main", mpegts_mux_sched_done);
+#endif
 #if ENABLE_MPEGTS_DVB
   tvhftrace("main", dvb_network_done);
 #endif
index 2113db0659210410f7d7731e8ed4124583aab053..1e5f007cd8b258d9b7625cacc021c2bd975aba71 100644 (file)
@@ -137,3 +137,23 @@ tvheadend.services = function(panel)
     }
   });
 }
+
+tvheadend.mux_sched = function(panel)
+{
+  tvheadend.idnode_grid(panel, {
+    url      : 'api/mpegts/mux_sched',
+    comet    : 'mpegts_mux_sched',
+    titleS   : 'Mux Scheduler',
+    titleP   : 'Mux Schedulers',
+    tabIndex : 4,
+    hidemode : true,
+    add      : {
+      url    : 'api/mpegts/mux_sched',
+      titleS : 'Mux Scheduler',
+      create : {
+        url          : 'api/mpegts/mux_sched/create'
+      }
+    },
+    del      : true
+  });
+}
index 4649c17363d9ac8cbd7d2e54747532a32fc5e455..36b188bf5f25c1059774732745ef447c8b4b0335 100644 (file)
@@ -249,6 +249,7 @@ function accessUpdate(o) {
     tvheadend.networks(tvheadend.conf_dvbin);
     tvheadend.muxes(tvheadend.conf_dvbin);
     tvheadend.services(tvheadend.conf_dvbin);
+    tvheadend.mux_sched(tvheadend.conf_dvbin);
     tabs1.push(tvheadend.conf_dvbin);
 
     /* Channel / EPG */