]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Add cache scheme selection for DVR to reduce system resources
authorJaroslav Kysela <perex@perex.cz>
Fri, 14 Mar 2014 22:25:36 +0000 (23:25 +0100)
committerJaroslav Kysela <perex@perex.cz>
Sat, 15 Mar 2014 17:07:14 +0000 (18:07 +0100)
12 files changed:
docs/html/config_dvr.html
src/dvr/dvr.h
src/dvr/dvr_db.c
src/dvr/dvr_rec.c
src/muxer.c
src/muxer.h
src/muxer/muxer_pass.c
src/muxer/muxer_tvh.c
src/muxer/tvh/mkmux.c
src/muxer/tvh/mkmux.h
src/webui/extjs.c
src/webui/static/app/dvr.js

index 41294452fab38da3cf785c7e3f7fb93ccf55c28b..adce28bcd75ec94b02ae1123b8df6993f93dbf69 100644 (file)
   <dt>Media container
   <dd>Select the container format used to store recordings.
 
+  <dt>Cache scheme
+  <dd>Select the cache scheme used to store recordings.
+  
+    <dl>
+
+      <dt>System
+      <dd>Standard system caching.
+      
+      <dt>Do not keep
+      <dd>Do not keep the stored data in system's cache.
+      
+      <dt>Sync
+      <dd>Sync the stored data with medium (disk).
+      
+      <dt>Sync + Do not keep
+      <dd>Combination of two above variants.
+
+    </dl>
+
   <dt>Rewrite PAT in passthrough mode
   <dd>Rewrite the original Program Association Table to only include
       the active service.  When this option is disabled, Tvheadend will
index f1d67a64842708758c702cf84a1c427762245b6b..73b031f93f45398c7d4249e04745c8ffba6940d3 100644 (file)
@@ -37,6 +37,7 @@ typedef struct dvr_config {
   int dvr_extra_time_post;
 
   muxer_container_type_t dvr_mc;
+  muxer_cache_type_t dvr_mux_cache;
 
   /* Series link support */
   int dvr_sl_brand_lock;
@@ -332,6 +333,8 @@ void dvr_storage_set(dvr_config_t *cfg, const char *storage);
 
 void dvr_container_set(dvr_config_t *cfg, const char *container);
 
+void dvr_mux_cache_set(dvr_config_t *cfg, int mcache);
+
 void dvr_postproc_set(dvr_config_t *cfg, const char *postproc);
 
 void dvr_retention_set(dvr_config_t *cfg, int days);
index e80dae062cad387872df90769f51f95591afd42e..536e55a240904e50533251eae7d345d687c41ac0 100644 (file)
@@ -1113,6 +1113,7 @@ dvr_init(void)
         cfg = dvr_config_create(s);
 
       cfg->dvr_mc = htsmsg_get_u32_or_default(m, "container", MC_MATROSKA);
+      cfg->dvr_mux_cache = htsmsg_get_u32_or_default(m, "cache", MC_CACHE_DONTKEEP);
 
       if(!htsmsg_get_u32(m, "rewrite-pat", &u32)) {
         if (u32)
@@ -1294,6 +1295,7 @@ dvr_config_create(const char *name)
   cfg->dvr_config_name = strdup(name);
   cfg->dvr_retention_days = 31;
   cfg->dvr_mc = MC_MATROSKA;
+  cfg->dvr_mux_cache = MC_CACHE_DONTKEEP;
   cfg->dvr_flags = DVR_TAG_FILES | DVR_SKIP_COMMERCIALS;
 
   /* series link support */
@@ -1352,6 +1354,7 @@ dvr_save(dvr_config_t *cfg)
     htsmsg_add_str(m, "config_name", cfg->dvr_config_name);
   htsmsg_add_str(m, "storage", cfg->dvr_storage);
   htsmsg_add_u32(m, "container", cfg->dvr_mc);
+  htsmsg_add_u32(m, "cache", cfg->dvr_mux_cache);
   htsmsg_add_u32(m, "rewrite-pat", !!(cfg->dvr_mux_flags & MUX_REWRITE_PAT));
   htsmsg_add_u32(m, "rewrite-pmt", !!(cfg->dvr_mux_flags & MUX_REWRITE_PMT));
   htsmsg_add_u32(m, "retention-days", cfg->dvr_retention_days);
@@ -1412,6 +1415,23 @@ dvr_container_set(dvr_config_t *cfg, const char *container)
   dvr_save(cfg);
 }
 
+/**
+ *
+ */
+void
+dvr_mux_cache_set(dvr_config_t *cfg, int mcache)
+{
+  if (mcache < MC_CACHE_UNKNOWN || mcache > MC_CACHE_LAST)
+    mcache = MC_CACHE_UNKNOWN;
+
+  if(cfg->dvr_mux_cache == mcache)
+    return;
+
+  cfg->dvr_mux_cache = mcache;
+
+  dvr_save(cfg);
+}
+
 
 /**
  *
index 37fd052034a6ac59f624cdb2db8fbf501b723532..4b1b1132ffc360cecdbf1a4aa4838bd469ce7037 100644 (file)
@@ -293,6 +293,7 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
 
   mc = de->de_mc;
   m_cfg.dvr_flags = cfg->dvr_mux_flags;
+  m_cfg.dvr_cache = cfg->dvr_mux_cache;
 
   de->de_mux = muxer_create(mc, &m_cfg);
   if(!de->de_mux) {
index f40e5144b8800608270e5d5d197aa6b5d5869505..2d737327565db946bd923fb8b0567fb524f3626c 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <string.h>
+#include <fcntl.h>
 
 #include "tvheadend.h"
 #include "service.h"
@@ -253,6 +254,8 @@ muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg)
   if(!m)
     tvhlog(LOG_ERR, "mux", "Can't find a muxer that supports '%s' container",
           muxer_container_type2txt(mc));
+  else
+    m->m_cache = m_cfg->dvr_cache;
 
   return m;
 }
@@ -408,4 +411,73 @@ muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data)
   return m->m_write_pkt(m, smt, data);
 }
 
+/**
+ * cache type conversions
+ */
+static struct strtab cache_types[] = {
+  { "Unknown",            MC_CACHE_UNKNOWN },
+  { "System",             MC_CACHE_SYSTEM },
+  { "Do not keep",        MC_CACHE_DONTKEEP },
+  { "Sync",               MC_CACHE_SYNC },
+  { "Sync + Do not keep", MC_CACHE_SYNCDONTKEEP }
+};
 
+const char*
+muxer_cache_type2txt(muxer_cache_type_t c)
+{
+  return val2str(c, cache_types);
+}
+
+muxer_cache_type_t
+muxer_cache_txt2type(const char *str)
+{
+  int r = str2val(str, cache_types);
+  if (r < 0)
+    r = MC_CACHE_UNKNOWN;
+  return r;
+}
+
+/**
+ * cache scheme
+ */
+void
+muxer_cache_update(muxer_t *m, int fd, off_t pos, size_t size)
+{
+  switch (m->m_cache) {
+  case MC_CACHE_UNKNOWN:
+  case MC_CACHE_SYSTEM:
+    break;
+  case MC_CACHE_SYNC:
+    fsync(fd);
+    break;
+  case MC_CACHE_SYNCDONTKEEP:
+    fsync(fd);
+    /* fall through */
+  case MC_CACHE_DONTKEEP:
+    posix_fadvise(fd, pos, size, POSIX_FADV_DONTNEED);
+    break;
+  default:
+    abort();
+  }
+}
+
+/**
+ * Get a list of supported cache schemes
+ */
+int
+muxer_cache_list(htsmsg_t *array)
+{
+  htsmsg_t *mc;
+  int c;
+  const char *s;
+
+  for (c = 0; c <= MC_CACHE_LAST; c++) {
+    mc = htsmsg_create_map();
+    s = muxer_cache_type2txt(c);
+    htsmsg_add_u32(mc, "index",       c);
+    htsmsg_add_str(mc, "description", s);
+    htsmsg_add_msg(array, NULL, mc);
+  }
+
+  return c;
+}
index 8215df690aa6f1a7fd49be0bc6d249e7112521ca..c6518c73e9f62f90f7c17f76c8ed8f555c130665 100644 (file)
@@ -34,9 +34,19 @@ typedef enum {
   MC_WEBM        = 6,
 } muxer_container_type_t;
 
+typedef enum {
+  MC_CACHE_UNKNOWN      = 0,
+  MC_CACHE_SYSTEM       = 1,
+  MC_CACHE_DONTKEEP     = 2,
+  MC_CACHE_SYNC         = 3,
+  MC_CACHE_SYNCDONTKEEP = 4,
+  MC_CACHE_LAST         = MC_CACHE_SYNCDONTKEEP
+} muxer_cache_type_t;
+
 /* Muxer configuration used when creating a muxer. */
 typedef struct muxer_config {
   int dvr_flags;
+  muxer_cache_type_t dvr_cache;
 } muxer_config_t;
 
 struct muxer;
@@ -65,6 +75,7 @@ typedef struct muxer {
 
   int                    m_errors;     // Number of errors
   muxer_container_type_t m_container;  // The type of the container
+  muxer_cache_type_t     m_cache;      // Caching scheme
 } muxer_t;
 
 
@@ -95,4 +106,10 @@ int         muxer_write_pkt   (muxer_t *m, streaming_message_type_t smt, void *d
 const char* muxer_mime        (muxer_t *m, const struct streaming_start *ss);
 const char* muxer_suffix      (muxer_t *m, const struct streaming_start *ss);
 
+// Cache
+const char *       muxer_cache_type2txt(muxer_cache_type_t t);
+muxer_cache_type_t muxer_cache_txt2type(const char *str);
+void               muxer_cache_update(muxer_t *m, int fd, off_t off, size_t size);
+int                muxer_cache_list(htsmsg_t *array);
+
 #endif
index ae9b781b3f094e2328572356bc04786c5bae3de3..c31c8ec79cbeb7bcf08dc0edff9c07b5209e4752 100644 (file)
@@ -33,6 +33,7 @@ typedef struct pass_muxer {
   muxer_t;
 
   /* File descriptor stuff */
+  off_t pm_off;
   int   pm_fd;
   int   pm_seekable;
   int   pm_error;
@@ -360,6 +361,7 @@ pass_muxer_open_stream(muxer_t *m, int fd)
 {
   pass_muxer_t *pm = (pass_muxer_t*)m;
 
+  pm->pm_off      = 0;
   pm->pm_fd       = fd;
   pm->pm_seekable = 0;
   pm->pm_filename = strdup("Live stream");
@@ -386,6 +388,7 @@ pass_muxer_open_file(muxer_t *m, const char *filename)
     return -1;
   }
 
+  pm->pm_off      = 0;
   pm->pm_seekable = 1;
   pm->pm_fd       = fd;
   pm->pm_filename = strdup(filename);
@@ -408,6 +411,11 @@ pass_muxer_write(muxer_t *m, const void *data, size_t size)
     tvhlog(LOG_ERR, "pass", "%s: Write failed -- %s", pm->pm_filename, 
           strerror(errno));
     m->m_errors++;
+    muxer_cache_update(m, pm->pm_fd, pm->pm_off, 0);
+    pm->pm_off = lseek(pm->pm_fd, 0, SEEK_CUR);
+  } else {
+    muxer_cache_update(m, pm->pm_fd, pm->pm_off, 0);
+    pm->pm_off += size;
   }
 }
 
index 1049c847c9813156eb65e2b4ccc2b5667a98b304..f87fa192c217689e58b7f666b0b2befc06cac05d 100644 (file)
@@ -239,7 +239,7 @@ tvh_muxer_create(muxer_container_type_t mc, muxer_config_t *m_cfg)
   tm->m_close        = tvh_muxer_close;
   tm->m_destroy      = tvh_muxer_destroy;
   tm->m_container    = mc;
-  tm->tm_ref         = mk_mux_create(mc == MC_WEBM);
+  tm->tm_ref         = mk_mux_create((muxer_t *)tm, mc == MC_WEBM);
 
   return (muxer_t*)tm;
 }
index 4925039d8e15ac9f8ea62b047a655ae505a77c6e..718704b26c0e339e4406178868639cb2ffb52ea5 100644 (file)
@@ -85,6 +85,7 @@ typedef struct mk_chapter {
  *
  */
 struct mk_mux {
+  muxer_t *m;
   int fd;
   char *filename;
   int error;
@@ -424,6 +425,7 @@ mk_write_to_fd(mk_mux_t *mkm, htsbuf_queue_t *hq)
 {
   htsbuf_data_t *hd;
   int i = 0;
+  off_t oldpos = mkm->fdpos;
 
   TAILQ_FOREACH(hd, &hq->hq_q, hd_link)
     i++;
@@ -448,6 +450,8 @@ mk_write_to_fd(mk_mux_t *mkm, htsbuf_queue_t *hq)
     iov += iovcnt;
   } while(i);
 
+  muxer_cache_update(mkm->m, mkm->fd, oldpos, 0);
+
   return 0;
 }
 
@@ -785,6 +789,7 @@ mk_write_metaseek(mk_mux_t *mkm, int first)
     mk_write_to_fd(mkm, &q);
   } else if(mkm->seekable) {
     off_t prev = mkm->fdpos;
+    mkm->fdpos = mkm->segment_pos;
     if(lseek(mkm->fd, mkm->segment_pos, SEEK_SET) == (off_t) -1)
       mkm->error = errno;
 
@@ -1008,10 +1013,11 @@ mk_write_cues(mk_mux_t *mkm)
 /**
  *
  */
-mk_mux_t *mk_mux_create(int webm)
+mk_mux_t *mk_mux_create(muxer_t *m, int webm)
 {
   mk_mux_t *mkm = calloc(1, sizeof(mk_mux_t));
 
+  mkm->m = m;
   mkm->webm = webm;
   mkm->fd   = -1;
 
index 44a4de4943ed399b0be7dcf1f4af42c86f2b315d..6840d3f1eacf5a4a7a006ed550b7612e31a14f61 100644 (file)
@@ -28,7 +28,7 @@ struct th_pkt;
 struct channel;
 struct event;
 
-mk_mux_t *mk_mux_create(int webm);
+mk_mux_t *mk_mux_create(muxer_t *m, int webm);
 
 int mk_mux_open_file  (mk_mux_t *mkm, const char *filename);
 int mk_mux_open_stream(mk_mux_t *mkm, int fd);
index 443769dbfebf674471b3fba926b4e60b31fadaef..e9af647c02652d762221420be59729dcbc7198ca 100755 (executable)
@@ -622,6 +622,46 @@ skip:
 }
 
 
+/**
+ *
+ */
+static int
+extjs_dvr_caches(http_connection_t *hc, const char *remain, void *opaque)
+{
+  htsbuf_queue_t *hq = &hc->hc_reply;
+  const char *op = http_arg_get(&hc->hc_req_args, "op");
+  htsmsg_t *out, *array;
+
+  pthread_mutex_lock(&global_lock);
+
+  if(op != NULL && !strcmp(op, "list")) {
+
+    out = htsmsg_create_map();
+    array = htsmsg_create_list();
+
+    if (http_access_verify(hc, ACCESS_RECORDER_ALL))
+      goto skip;
+
+    muxer_cache_list(array);
+
+skip:
+    htsmsg_add_msg(out, "entries", array);
+
+  } else {
+    pthread_mutex_unlock(&global_lock);
+    return HTTP_STATUS_BAD_REQUEST;
+  }
+
+  pthread_mutex_unlock(&global_lock);
+
+  htsmsg_json_serialize(out, hq, 0);
+  htsmsg_destroy(out);
+  http_output_content(hc, "text/x-json; charset=UTF-8");
+  return 0;
+
+}
+
+
 /**
  *
  */
@@ -1086,6 +1126,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
     r = htsmsg_create_map();
     htsmsg_add_str(r, "storage", cfg->dvr_storage);
     htsmsg_add_str(r, "container", muxer_container_type2txt(cfg->dvr_mc));
+    htsmsg_add_u32(r, "cache",     cfg->dvr_mux_cache);
     htsmsg_add_u32(r, "rewritePAT", !!(cfg->dvr_mux_flags & MUX_REWRITE_PAT));
     htsmsg_add_u32(r, "rewritePMT", !!(cfg->dvr_mux_flags & MUX_REWRITE_PMT));
     if(cfg->dvr_postproc != NULL)
@@ -1124,6 +1165,9 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
    if((s = http_arg_get(&hc->hc_req_args, "container")) != NULL)
       dvr_container_set(cfg,s);
 
+   if((s = http_arg_get(&hc->hc_req_args, "cache")) != NULL)
+      dvr_mux_cache_set(cfg,atoi(s));
+
     if((s = http_arg_get(&hc->hc_req_args, "postproc")) != NULL)
       dvr_postproc_set(cfg,s);
 
@@ -1750,6 +1794,7 @@ extjs_start(void)
   http_path_add("/dvrlist_finished", NULL, extjs_dvrlist_finished, ACCESS_WEB_INTERFACE);
   http_path_add("/dvrlist_failed",   NULL, extjs_dvrlist_failed,   ACCESS_WEB_INTERFACE);
   http_path_add("/dvr_containers",   NULL, extjs_dvr_containers,   ACCESS_WEB_INTERFACE);
+  http_path_add("/dvr_caches",       NULL, extjs_dvr_caches,       ACCESS_WEB_INTERFACE);
   http_path_add("/ecglist",          NULL, extjs_ecglist,          ACCESS_WEB_INTERFACE);
   http_path_add("/config",           NULL, extjs_config,           ACCESS_WEB_INTERFACE);
   http_path_add("/languages",        NULL, extjs_languages,        ACCESS_WEB_INTERFACE);
index c01312135f912096645df39d6dc87816f2fd3e00..7a97a1832b9dc90667d40e6b2ff758f729e582dd 100644 (file)
@@ -27,6 +27,18 @@ tvheadend.containers = new Ext.data.JsonStore({
        }
 });
 
+//For the cache configuration
+tvheadend.caches = new Ext.data.JsonStore({
+       autoLoad : true,
+       root : 'entries',
+       fields : [ 'index', 'description' ],
+       id : 'name',
+       url : 'dvr_caches',
+       baseParams : {
+               op : 'list'
+       }
+});
+
 
 /**
  * Configuration names
@@ -733,7 +745,7 @@ tvheadend.dvrsettings = function() {
        var confreader = new Ext.data.JsonReader({
                root : 'dvrSettings'
        }, [ 'storage', 'postproc', 'retention', 'dayDirs', 'channelDirs',
-               'channelInTitle', 'container', 'dateInTitle', 'timeInTitle',
+               'channelInTitle', 'container', 'cache', 'dateInTitle', 'timeInTitle',
                'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs',
                'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle', 'episodeBeforeDate', 'rewritePAT', 'rewritePMT' ]);
 
@@ -781,6 +793,15 @@ tvheadend.dvrsettings = function() {
                        editable : false,
                        width : 200,
                        hiddenName : 'container'
+               }), new Ext.form.ComboBox({
+                       store : tvheadend.caches,
+                       fieldLabel : 'Cache scheme',
+                       triggerAction : 'all',
+                       displayField : 'description',
+                       valueField : 'index',
+                       editable : false,
+                       width : 200,
+                       hiddenName : 'cache'
                }), new Ext.form.Checkbox({
                        fieldLabel : 'Rewrite PAT in passthrough mode',
                        name : 'rewritePAT'