summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/options.rst25
-rw-r--r--old-makefile1
-rw-r--r--options/options.c3
-rw-r--r--options/options.h2
-rw-r--r--stream/cache_file.c149
-rw-r--r--stream/stream.c48
-rw-r--r--stream/stream.h2
-rw-r--r--wscript_build.py1
8 files changed, 215 insertions, 16 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index b6b08d7d6e..d67d93c592 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -428,6 +428,31 @@ OPTIONS
on the situation, either of these might be slower than the other method.
This option allows control over this.
+``--cache-file=<path>``
+ Create a cache file on the filesystem with the given name. The file is
+ always overwritten. When the general cache is enabled, this file cache
+ will be used to store whatever is read from the source stream.
+
+ This will always overwrite the cache file, and you can't use an existing
+ cache file to resume playback of a stream. (Technically, mpv wouldn't
+ even know which blocks in the file are valid and which not.)
+
+ The resulting file will not necessarily contain all data of the source
+ stream. For example, if you seek, the parts that were skipped over are
+ never read and consequently are not written to the cache. The skipped over
+ parts are filled with zeros. This means that the cache file doesn't
+ necessarily correspond to a full download of the source stream.
+
+ Both of these issues could be improved if there is any user interest.
+
+ Also see ``--cache-file-size``.
+
+``--cache-file-size=<kBytes>``
+ Maximum size of the file created with ``--cache-file``. For read accesses
+ above this size, the cache is simply not used.
+
+ (Default: 1048576, 1 GB.)
+
``--cdda-...``
These options can be used to tune the CD Audio reading feature of mpv.
diff --git a/old-makefile b/old-makefile
index 6649f583ce..986c1df828 100644
--- a/old-makefile
+++ b/old-makefile
@@ -206,6 +206,7 @@ SOURCES = audio/audio.c \
player/timeline/tl_mpv_edl.c \
player/timeline/tl_cue.c \
stream/cache.c \
+ stream/cache_file.c \
stream/cookies.c \
stream/rar.c \
stream/stream.c \
diff --git a/options/options.c b/options/options.c
index e2ce57955e..c6f758a647 100644
--- a/options/options.c
+++ b/options/options.c
@@ -139,6 +139,8 @@ const m_option_t mp_opts[] = {
({"no", 0})),
OPT_INTRANGE("cache-initial", stream_cache.initial, 0, 0, 0x7fffffff),
OPT_INTRANGE("cache-seek-min", stream_cache.seek_min, 0, 0, 0x7fffffff),
+ OPT_STRING("cache-file", stream_cache.file, 0),
+ OPT_INTRANGE("cache-file-size", stream_cache.file_max, 0, 0, 0x7fffffff),
OPT_CHOICE_OR_INT("cache-pause-below", stream_cache_pause, 0, 0, 0x7fffffff,
({"no", 0})),
OPT_INTRANGE("cache-pause-restart", stream_cache_unpause, 0, 0, 0x7fffffff),
@@ -573,6 +575,7 @@ const struct MPOpts mp_default_opts = {
.def_size = 25000,
.initial = 0,
.seek_min = 500,
+ .file_max = 1024 * 1024,
},
.stream_cache_pause = 500,
.stream_cache_unpause = 1000,
diff --git a/options/options.h b/options/options.h
index 110a16701c..83b44c1e3d 100644
--- a/options/options.h
+++ b/options/options.h
@@ -47,6 +47,8 @@ struct mp_cache_opts {
int def_size;
int initial;
int seek_min;
+ char *file;
+ int file_max;
};
typedef struct MPOpts {
diff --git a/stream/cache_file.c b/stream/cache_file.c
new file mode 100644
index 0000000000..dfdd8390dc
--- /dev/null
+++ b/stream/cache_file.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <stdint.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+
+#include "options/options.h"
+
+#include "stream.h"
+
+#define BLOCK_SIZE 1024LL
+#define BLOCK_ALIGN(p) ((p) & ~(BLOCK_SIZE - 1))
+
+struct priv {
+ struct stream *original;
+ FILE *cache_file;
+ uint8_t *block_bits; // 1 bit for each BLOCK_SIZE, whether block was read
+ int64_t size; // currently known size
+ int64_t max_size; // max. size for block_bits and cache_file
+};
+
+static bool test_bit(struct priv *p, int64_t pos)
+{
+ if (pos < 0 || pos >= p->size)
+ return false;
+ size_t block = pos / BLOCK_SIZE;
+ return p->block_bits[block / 8] & (1 << (block % 8));
+}
+
+static void set_bit(struct priv *p, int64_t pos, bool bit)
+{
+ if (pos < 0 || pos >= p->size)
+ return;
+ size_t block = pos / BLOCK_SIZE;
+ unsigned int m = (1 << (block % 8));
+ p->block_bits[block / 8] = (p->block_bits[block / 8] & ~m) | (bit ? m : 0);
+}
+
+static int fill_buffer(stream_t *s, char *buffer, int max_len)
+{
+ struct priv *p = s->priv;
+ if (s->pos < 0)
+ return -1;
+ if (s->pos >= p->max_size) {
+ if (stream_seek(p->original, s->pos) < 1)
+ return -1;
+ return stream_read(p->original, buffer, max_len);
+ }
+ // Size of file changes -> invalidate last block
+ if (s->pos >= p->size - BLOCK_SIZE) {
+ int64_t new_size = -1;
+ stream_control(s, STREAM_CTRL_GET_SIZE, &new_size);
+ if (new_size != p->size)
+ set_bit(p, BLOCK_ALIGN(p->size), 0);
+ p->size = MPMIN(p->max_size, new_size);
+ }
+ int64_t aligned = BLOCK_ALIGN(s->pos);
+ if (!test_bit(p, aligned)) {
+ char tmp[BLOCK_SIZE];
+ stream_seek(p->original, aligned);
+ int r = stream_read(p->original, tmp, BLOCK_SIZE);
+ if (r < BLOCK_SIZE) {
+ if (p->size < 0) {
+ MP_WARN(s, "suspected EOF\n");
+ } else if (aligned + r < p->size) {
+ MP_ERR(s, "unexpected EOF\n");
+ return -1;
+ }
+ }
+ if (fseeko(p->cache_file, aligned, SEEK_SET))
+ return -1;
+ if (fwrite(tmp, r, 1, p->cache_file) != 1)
+ return -1;
+ set_bit(p, aligned, 1);
+ }
+ if (fseeko(p->cache_file, s->pos, SEEK_SET))
+ return -1;
+ // align/limit to blocks
+ max_len = MPMIN(max_len, BLOCK_SIZE - (s->pos % BLOCK_SIZE));
+ // Limit to max. known file size
+ max_len = MPMIN(max_len, p->size - s->pos);
+ return fread(buffer, 1, max_len, p->cache_file);
+}
+
+static int seek(stream_t *s, int64_t newpos)
+{
+ return 1;
+}
+
+static int control(stream_t *s, int cmd, void *arg)
+{
+ struct priv *p = s->priv;
+ return stream_control(p->original, cmd, arg);
+}
+
+static void s_close(stream_t *s)
+{
+ struct priv *p = s->priv;
+ if (p->cache_file)
+ fclose(p->cache_file);
+ talloc_free(p);
+}
+
+// return 1 on success, 0 if disabled, -1 on error
+int stream_file_cache_init(stream_t *cache, stream_t *stream,
+ struct mp_cache_opts *opts)
+{
+ if (!opts->file || !opts->file[0] || opts->file_max < 1)
+ return 0;
+
+ FILE *file = fopen(opts->file, "wb+");
+ if (!file) {
+ MP_ERR(cache, "can't open cache file '%s'\n", opts->file);
+ return -1;
+ }
+
+ struct priv *p = talloc_zero(NULL, struct priv);
+
+ cache->priv = p;
+ p->original = stream;
+ p->cache_file = file;
+ p->max_size = opts->file_max * 1024LL;
+
+ // file_max can be INT_MAX, so this is at most about 256MB
+ p->block_bits = talloc_zero_size(p, (p->max_size / BLOCK_SIZE + 1) / 8 + 1);
+
+ cache->seek = seek;
+ cache->fill_buffer = fill_buffer;
+ cache->control = control;
+ cache->close = s_close;
+
+ return 1;
+}
diff --git a/stream/stream.c b/stream/stream.c
index 2a6136f036..6e903bc8ec 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -766,6 +766,28 @@ stream_t *open_memory_stream(void *data, int len)
return s;
}
+static stream_t *open_cache(stream_t *orig, const char *name)
+{
+ stream_t *cache = new_stream();
+ cache->uncached_type = orig->type;
+ cache->uncached_stream = orig;
+ cache->seekable = true;
+ cache->mode = STREAM_READ;
+ cache->read_chunk = 4 * STREAM_BUFFER_SIZE;
+
+ cache->url = talloc_strdup(cache, orig->url);
+ cache->mime_type = talloc_strdup(cache, orig->mime_type);
+ cache->demuxer = talloc_strdup(cache, orig->demuxer);
+ cache->lavf_type = talloc_strdup(cache, orig->lavf_type);
+ cache->safe_origin = orig->safe_origin;
+ cache->opts = orig->opts;
+ cache->global = orig->global;
+
+ cache->log = mp_log_new(cache, cache->global->log, name);
+
+ return cache;
+}
+
static struct mp_cache_opts check_cache_opts(stream_t *stream,
struct mp_cache_opts *opts)
{
@@ -794,27 +816,21 @@ int stream_enable_cache(stream_t **stream, struct mp_cache_opts *opts)
if (use_opts.size < 1)
return -1;
- stream_t *cache = new_stream();
- cache->uncached_type = orig->type;
- cache->uncached_stream = orig;
- cache->seekable = true;
- cache->mode = STREAM_READ;
- cache->read_chunk = 4 * STREAM_BUFFER_SIZE;
-
- cache->url = talloc_strdup(cache, orig->url);
- cache->mime_type = talloc_strdup(cache, orig->mime_type);
- cache->demuxer = talloc_strdup(cache, orig->demuxer);
- cache->lavf_type = talloc_strdup(cache, orig->lavf_type);
- cache->safe_origin = orig->safe_origin;
- cache->opts = orig->opts;
- cache->global = orig->global;
+ stream_t *fcache = open_cache(orig, "file-cache");
+ if (stream_file_cache_init(fcache, orig, &use_opts) <= 0) {
+ fcache->uncached_stream = NULL; // don't free original stream
+ free_stream(fcache);
+ fcache = orig;
+ }
- cache->log = mp_log_new(cache, cache->global->log, "cache");
+ stream_t *cache = open_cache(fcache, "cache");
- int res = stream_cache_init(cache, orig, &use_opts);
+ int res = stream_cache_init(cache, fcache, &use_opts);
if (res <= 0) {
cache->uncached_stream = NULL; // don't free original stream
free_stream(cache);
+ if (fcache != orig)
+ free_stream(fcache);
} else {
*stream = cache;
}
diff --git a/stream/stream.h b/stream/stream.h
index 267f8e753a..4f8ca6ddae 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -198,6 +198,8 @@ int stream_enable_cache(stream_t **stream, struct mp_cache_opts *opts);
// Internal
int stream_cache_init(stream_t *cache, stream_t *stream,
struct mp_cache_opts *opts);
+int stream_file_cache_init(stream_t *cache, stream_t *stream,
+ struct mp_cache_opts *opts);
int stream_write_buffer(stream_t *s, unsigned char *buf, int len);
diff --git a/wscript_build.py b/wscript_build.py
index 4f7efdfbb9..ef480ab953 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -233,6 +233,7 @@ def build(ctx):
( "stream/ai_sndio.c", "sndio" ),
( "stream/audio_in.c", "audio-input" ),
( "stream/cache.c" ),
+ ( "stream/cache_file.c" ),
( "stream/cookies.c" ),
( "stream/dvb_tune.c", "dvbin" ),
( "stream/frequencies.c", "tv" ),