Add cache scheme selection for DVR to reduce system resources
This commit is contained in:
parent
3f3fdc8af1
commit
ed45ab7224
12 changed files with 216 additions and 4 deletions
|
@ -16,6 +16,25 @@
|
|||
<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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -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) {
|
||||
|
|
72
src/muxer.c
72
src/muxer.c
|
@ -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;
|
||||
}
|
||||
|
|
17
src/muxer.h
17
src/muxer.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue