diff options
-rw-r--r-- | DOCS/man/en/input.rst | 12 | ||||
-rw-r--r-- | player/command.c | 29 | ||||
-rw-r--r-- | stream/cache.c | 98 | ||||
-rw-r--r-- | stream/stream.h | 1 |
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, |