summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-04-09 19:15:23 +0200
committerwm4 <wm4@nowhere>2014-04-09 19:15:23 +0200
commit5f65a5cfea020dc0e3f718da48a937f29eb1993b (patch)
treee1992642765f8a7f7d1492e796db7fd240e17860
parent6ac98c042f16d6b6a6f370905345678b42adf9e8 (diff)
downloadmpv-5f65a5cfea020dc0e3f718da48a937f29eb1993b.tar.bz2
mpv-5f65a5cfea020dc0e3f718da48a937f29eb1993b.tar.xz
cache: allow resizing at runtime
The only tricky part is keeping the cache contents, which is made simple by allocating the new cache while still keeping the old cache around, and then copying the old data. To explain the "Don't use this when playing DVD or Bluray." comment: the cache also associates timestamps to blocks of bytes, but throws away the timestamps on seek. Thus you will experience strange behavior after resizing the cache until the old cached region is exhausted.
-rw-r--r--DOCS/man/en/input.rst12
-rw-r--r--player/command.c29
-rw-r--r--stream/cache.c98
-rw-r--r--stream/stream.h1
4 files changed, 119 insertions, 21 deletions
diff --git a/DOCS/man/en/input.rst b/DOCS/man/en/input.rst
index a1e4ee45e3..e64bc7ebef 100644
--- a/DOCS/man/en/input.rst
+++ b/DOCS/man/en/input.rst
@@ -715,6 +715,18 @@ an option at runtime.
``cache``
Network cache fill state (0-100).
+``cache-size`` (RW)
+ Total network cache size in KB. This is similar to ``--cache``. This allows
+ to set the cache size at runtime. Currently, it's not possible to enable
+ or disable the cache at runtime using this property, just to resize an
+ existing cache.
+
+ Note that this tries to keep the cache contents as far as possible. To make
+ this easier, the cache resizing code will allocate the new cache while the
+ old cache is still allocated.
+
+ Don't use this when playing DVD or Bluray.
+
``pts-association-mode`` (RW)
See ``--pts-association-mode``.
diff --git a/player/command.c b/player/command.c
index 9921a055f5..a0adfb2ea3 100644
--- a/player/command.c
+++ b/player/command.c
@@ -939,6 +939,34 @@ static int mp_property_cache(m_option_t *prop, int action, void *arg,
return m_property_int_ro(prop, action, arg, cache);
}
+static int mp_property_cache_size(m_option_t *prop, int action, void *arg,
+ void *ctx)
+{
+ MPContext *mpctx = ctx;
+ if (!mpctx->stream)
+ return M_PROPERTY_UNAVAILABLE;
+ switch (action) {
+ case M_PROPERTY_GET: {
+ int64_t size = -1;
+ stream_control(mpctx->stream, STREAM_CTRL_GET_CACHE_SIZE, &size);
+ if (size <= 0)
+ break;
+ *(int *)arg = size / 1024;
+ return M_PROPERTY_OK;
+ }
+ case M_PROPERTY_SET: {
+ int64_t size = *(int *)arg * 1024LL;
+ int r = stream_control(mpctx->stream, STREAM_CTRL_SET_CACHE_SIZE, &size);
+ if (r == STREAM_UNSUPPORTED)
+ break;
+ if (r == STREAM_OK)
+ return M_PROPERTY_OK;
+ return M_PROPERTY_ERROR;
+ }
+ }
+ return M_PROPERTY_NOT_IMPLEMENTED;
+}
+
static int mp_property_clock(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
{
@@ -2150,6 +2178,7 @@ static const m_option_t mp_properties[] = {
M_PROPERTY("chapter-metadata", mp_property_chapter_metadata),
M_OPTION_PROPERTY_CUSTOM("pause", mp_property_pause),
{ "cache", mp_property_cache, CONF_TYPE_INT },
+ { "cache-size", mp_property_cache_size, CONF_TYPE_INT, M_OPT_MIN, 0 },
M_OPTION_PROPERTY("pts-association-mode"),
M_OPTION_PROPERTY("hr-seek"),
{ "clock", mp_property_clock, CONF_TYPE_STRING,
diff --git a/stream/cache.c b/stream/cache.c
index 6333eeb0e9..69d60b4fff 100644
--- a/stream/cache.c
+++ b/stream/cache.c
@@ -69,10 +69,10 @@ struct priv {
pthread_cond_t wakeup;
// Constants (as long as cache thread is running)
+ // Some of these might actually be changed by a synced cache resize.
unsigned char *buffer; // base pointer of the allocated buffer memory
int64_t buffer_size; // size of the allocated buffer memory
int64_t back_size; // keep back_size amount of old bytes for backward seek
- int64_t fill_limit; // we should fill buffer only if space>=fill_limit
int64_t seek_limit; // keep filling cache if distance is less that seek limit
struct byte_meta *bm; // additional per-byte metadata
bool seekable; // underlying stream is seekable
@@ -132,6 +132,9 @@ enum {
CACHE_CTRL_PING = -2,
};
+// we should fill buffer only if space>=FILL_LIMIT
+#define FILL_LIMIT (FFMAX(16 * 1024, BYTE_META_CHUNK_SIZE * 2))
+
static int64_t mp_clipi64(int64_t val, int64_t min, int64_t max)
{
val = FFMIN(val, max);
@@ -271,7 +274,7 @@ static bool cache_fill(struct priv *s)
if (pos >= s->buffer_size)
pos -= s->buffer_size; // wrap-around
- if (space < s->fill_limit) {
+ if (space < FILL_LIMIT) {
s->idle = true;
s->reads++; // don't stuck main thread
return false;
@@ -318,6 +321,66 @@ static bool cache_fill(struct priv *s)
return true;
}
+// This is called both during init and at runtime.
+static int resize_cache(struct priv *s, int64_t size)
+{
+ int64_t min_size = FILL_LIMIT * 4;
+ int64_t max_size = ((size_t)-1) / 4;
+ int64_t buffer_size = MPMIN(MPMAX(size, min_size), max_size);
+
+ unsigned char *buffer = malloc(buffer_size);
+ struct byte_meta *bm = calloc(buffer_size / BYTE_META_CHUNK_SIZE + 2,
+ sizeof(struct byte_meta));
+ if (!buffer || !bm) {
+ free(buffer);
+ free(bm);
+ return STREAM_ERROR;
+ }
+
+ if (s->buffer) {
+ // Copy & free the old ringbuffer data.
+ // If the buffer is too small, prefer to copy these regions:
+ // 1. Data starting from read_filepos, until cache end
+ size_t read_1 = read_buffer(s, buffer, buffer_size, s->read_filepos);
+ // 2. then data from before read_filepos until cache start
+ // (this one needs to be copied to the end of the ringbuffer)
+ size_t read_2 = 0;
+ if (s->min_filepos < s->read_filepos) {
+ size_t copy_len = buffer_size - read_1;
+ copy_len = MPMIN(copy_len, s->read_filepos - s->min_filepos);
+ assert(copy_len + read_1 <= buffer_size);
+ read_2 = read_buffer(s, buffer + buffer_size - copy_len, copy_len,
+ s->read_filepos - copy_len);
+ // This shouldn't happen, unless copy_len was computed incorrectly.
+ assert(read_2 == copy_len);
+ }
+ // Set it up such that read_1 is at buffer pos 0, and read_2 wraps
+ // around below it, so that it is located at the end of the buffer.
+ s->min_filepos = s->read_filepos - read_2;
+ s->max_filepos = s->read_filepos + read_1;
+ s->offset = s->max_filepos - read_1;
+ } else {
+ cache_drop_contents(s);
+ }
+
+ free(s->buffer);
+ free(s->bm);
+
+ s->buffer_size = buffer_size;
+ s->back_size = buffer_size / 2;
+ s->buffer = buffer;
+ s->bm = bm;
+ s->idle = false;
+ s->eof = false;
+
+ //make sure that we won't wait from cache_fill
+ //more data than it is allowed to fill
+ if (s->seek_limit > s->buffer_size - FILL_LIMIT)
+ s->seek_limit = s->buffer_size - FILL_LIMIT;
+
+ return STREAM_OK;
+}
+
static void update_cached_controls(struct priv *s)
{
unsigned int ui;
@@ -439,10 +502,16 @@ static bool control_needs_flush(int stream_ctrl)
static void cache_execute_control(struct priv *s)
{
uint64_t old_pos = stream_tell(s->stream);
-
- s->control_res = stream_control(s->stream, s->control, s->control_arg);
s->control_flush = false;
+ switch (s->control) {
+ case STREAM_CTRL_SET_CACHE_SIZE:
+ s->control_res = resize_cache(s, *(int64_t *)s->control_arg);
+ break;
+ default:
+ s->control_res = stream_control(s->stream, s->control, s->control_arg);
+ }
+
bool pos_changed = old_pos != stream_tell(s->stream);
bool ok = s->control_res == STREAM_OK;
if (pos_changed && !ok) {
@@ -609,18 +678,10 @@ int stream_cache_init(stream_t *cache, stream_t *stream, int64_t size,
struct priv *s = talloc_zero(NULL, struct priv);
s->log = cache->log;
- //64kb min_size
- s->fill_limit = FFMAX(16 * 1024, BYTE_META_CHUNK_SIZE * 2);
- s->buffer_size = FFMAX(size, s->fill_limit * 4);
- s->back_size = s->buffer_size / 2;
+ s->seek_limit = seek_limit;
- s->buffer = malloc(s->buffer_size);
- s->bm = malloc((s->buffer_size / BYTE_META_CHUNK_SIZE + 2) *
- sizeof(struct byte_meta));
- if (!s->buffer || !s->bm) {
+ if (resize_cache(s, size) != STREAM_OK) {
MP_ERR(s, "Failed to allocate cache buffer.\n");
- free(s->buffer);
- free(s->bm);
talloc_free(s);
return -1;
}
@@ -637,13 +698,8 @@ int stream_cache_init(stream_t *cache, stream_t *stream, int64_t size,
cache->control = cache_control;
cache->close = cache_uninit;
- s->seek_limit = seek_limit;
- //make sure that we won't wait from cache_fill
- //more data than it is allowed to fill
- if (s->seek_limit > s->buffer_size - s->fill_limit)
- s->seek_limit = s->buffer_size - s->fill_limit;
- if (min > s->buffer_size - s->fill_limit)
- min = s->buffer_size - s->fill_limit;
+ if (min > s->buffer_size - FILL_LIMIT)
+ min = s->buffer_size - FILL_LIMIT;
s->seekable = (stream->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK &&
stream->end_pos > 0;
diff --git a/stream/stream.h b/stream/stream.h
index c23d173165..589728d4ac 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -83,6 +83,7 @@ enum stream_ctrl {
STREAM_CTRL_GET_CURRENT_TITLE,
STREAM_CTRL_SET_CURRENT_TITLE,
STREAM_CTRL_GET_CACHE_SIZE,
+ STREAM_CTRL_SET_CACHE_SIZE,
STREAM_CTRL_GET_CACHE_FILL,
STREAM_CTRL_GET_CACHE_IDLE,
STREAM_CTRL_RESUME_CACHE,