diff options
-rw-r--r-- | DOCS/man/input.rst | 32 | ||||
-rw-r--r-- | DOCS/man/options.rst | 100 | ||||
-rw-r--r-- | demux/demux.c | 54 | ||||
-rw-r--r-- | demux/demux.h | 3 | ||||
-rw-r--r-- | demux/demux_disc.c | 7 | ||||
-rw-r--r-- | demux/demux_mkv_timeline.c | 10 | ||||
-rw-r--r-- | options/options.c | 3 | ||||
-rw-r--r-- | options/options.h | 12 | ||||
-rw-r--r-- | player/command.c | 134 | ||||
-rw-r--r-- | player/core.h | 2 | ||||
-rw-r--r-- | player/loadfile.c | 1 | ||||
-rw-r--r-- | player/misc.c | 18 | ||||
-rw-r--r-- | player/osd.c | 32 | ||||
-rw-r--r-- | player/playloop.c | 7 | ||||
-rw-r--r-- | stream/cache.c | 809 | ||||
-rw-r--r-- | stream/cache_file.c | 158 | ||||
-rw-r--r-- | stream/stream.c | 97 | ||||
-rw-r--r-- | stream/stream.h | 27 | ||||
-rw-r--r-- | stream/stream_avdevice.c | 1 | ||||
-rw-r--r-- | stream/stream_dvb.c | 1 | ||||
-rw-r--r-- | stream/stream_dvdnav.c | 1 | ||||
-rw-r--r-- | stream/stream_edl.c | 1 | ||||
-rw-r--r-- | stream/stream_file.c | 1 | ||||
-rw-r--r-- | stream/stream_memory.c | 1 | ||||
-rw-r--r-- | stream/stream_mf.c | 1 | ||||
-rw-r--r-- | stream/stream_tv.c | 1 | ||||
-rw-r--r-- | wscript_build.py | 2 |
27 files changed, 49 insertions, 1467 deletions
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index b577b688d0..5413ad79be 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -1455,38 +1455,6 @@ Property list playing at all. In other words, it's only ``no`` if there's actually video playing. (Behavior since mpv 0.7.0.) -``cache`` - Network cache fill state (0-100.0). - -``cache-size`` (RW) - Network cache size in KB. This is similar to ``--cache``. This allows - setting 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. - - This does not include the backbuffer size (changed after mpv 0.10.0). - - 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 Blu-ray. - -``cache-free`` (R) - Total free cache size in KB. - -``cache-used`` (R) - Total used cache size in KB. - -``cache-speed`` (R) - Current I/O read speed between the cache and the lower layer (like network). - This gives the number bytes per seconds over a 1 second window (using - the type ``MPV_FORMAT_INT64`` for the client API). - -``cache-idle`` (R) - Returns ``yes`` if the cache is idle, which means the cache is filled as - much as possible, and is currently not reading more data. - ``demuxer-cache-duration`` Approximate duration of video buffered in the demuxer, in seconds. The guess is very unreliable, and often the property will not be available diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 9c66ca35cb..c8e75556c0 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -3836,97 +3836,15 @@ TV Cache ----- -``--cache=<kBytes|yes|no|auto>`` - Set the size of the cache in kilobytes, disable it with ``no``, or - automatically enable it if needed with ``auto`` (default: ``auto``). - With ``auto``, the cache will usually be enabled for network streams, - using the size set by ``--cache-default``. With ``yes``, the cache will - always be enabled with the size set by ``--cache-default`` (unless the - stream cannot be cached, or ``--cache-default`` disables caching). - - May be useful when playing files from slow media, but can also have - negative effects, especially with file formats that require a lot of - seeking, such as MP4. - - Note that half the cache size will be used to allow fast seeking back. This - is also the reason why a full cache is usually not reported as 100% full. - The cache fill display does not include the part of the cache reserved for - seeking back. The actual maximum percentage will usually be the ratio - between readahead and backbuffer sizes. - -``--cache-default=<kBytes|no>`` - Set the size of the cache in kilobytes (default: 10000 KB). Using ``no`` - will not automatically enable the cache e.g. when playing from a network - stream. Note that using ``--cache`` will always override this option. - -``--cache-initial=<kBytes>`` - Playback will start when the cache has been filled up with this many - kilobytes of data (default: 0). - -``--cache-seek-min=<kBytes>`` - If a seek is to be made to a position within ``<kBytes>`` of the cache - size from the current position, mpv will wait for the cache to be - filled to this position rather than performing a stream seek (default: - 500). - - This matters for small forward seeks. With slow streams (especially HTTP - streams) there is a tradeoff between skipping the data between current - position and seek destination, or performing an actual seek. Depending - on the situation, either of these might be slower than the other method. - This option allows control over this. - -``--cache-backbuffer=<kBytes>`` - Size of the cache back buffer (default: 10000 KB). This will add to the total - cache size, and reserved the amount for seeking back. The reserved amount - will not be used for readahead, and instead preserves already read data to - enable fast seeking back. - -``--cache-file=<TMP|path>`` - Create a cache file on the filesystem. - - There are two ways of using this: - - 1. Passing a path (a filename). The file will always be 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. - - .. warning:: Causes random corruption when used with ordered chapters or - with ``--audio-file``. - - 2. Passing the string ``TMP``. This will not be interpreted as filename. - Instead, an invisible temporary file is created. It depends on your - C library where this file is created (usually ``/tmp/``), and whether - filename is visible (the ``tmpfile()`` function is used). On some - systems, automatic deletion of the cache file might not be guaranteed. - - If you want to use a file cache, this mode is recommended, because it - doesn't break ordered chapters or ``--audio-file``. These modes open - multiple cache streams, and using the same file for them obviously - clashes. - - See also: ``--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. - - Keep in mind that some use-cases, like playing ordered chapters with cache - enabled, will actually create multiple cache files, each of which will - use up to this much disk space. - - (Default: 1048576, 1 GB.) +``--cache=<yes|no|auto>`` + Decide whether to use network cache settings (default: auto). + + If enabled, use the maximum of ``--cache-secs`` and ``--demuxer-max-bytes`` + for the cache size. If disabled, ``--cache-pause`` and related are + implicitly disabled. + + The ``auto`` choice sets this depending on whether the stream is thought to + involve network accesses (this is an imperfect heuristic). ``--no-cache`` Turn off input stream caching. See ``--cache``. diff --git a/demux/demux.c b/demux/demux.c index 3e9d897bb2..45f472cfc9 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -86,6 +86,7 @@ const demuxer_desc_t *const demuxer_list[] = { }; struct demux_opts { + int enable_cache; int64_t max_bytes; int64_t max_bytes_bw; double min_secs; @@ -102,6 +103,8 @@ struct demux_opts { const struct m_sub_options demux_conf = { .opts = (const struct m_option[]){ + OPT_CHOICE("cache", enable_cache, 0, + ({"no", 0}, {"auto", -1}, {"yes", 1})), OPT_DOUBLE("demuxer-readahead-secs", min_secs, M_OPT_MIN, .min = 0), // (The MAX_BYTES sizes may not be accurate because the max field is // of double type.) @@ -117,6 +120,7 @@ const struct m_sub_options demux_conf = { }, .size = sizeof(struct demux_opts), .defaults = &(const struct demux_opts){ + .enable_cache = -1, // auto .max_bytes = 150 * 1024 * 1024, .max_bytes_bw = 50 * 1024 * 1024, .min_secs = 1.0, @@ -129,6 +133,8 @@ const struct m_sub_options demux_conf = { struct demux_internal { struct mp_log *log; + struct demux_opts *opts; + // The demuxer runs potentially in another thread, so we keep two demuxer // structs; the real demuxer can access the shadow struct only. struct demuxer *d_thread; // accessed by demuxer impl. (producer) @@ -215,8 +221,6 @@ struct demux_internal { // Transient state. double duration; // Cached state. - bool force_cache_update; - struct stream_cache_info stream_cache_info; int64_t stream_size; // Updated during init only. char *stream_base_filename; @@ -1695,9 +1699,6 @@ static void execute_trackswitch(struct demux_internal *in) if (in->d_thread->desc->control) in->d_thread->desc->control(in->d_thread, DEMUXER_CTRL_SWITCHED_TRACKS, 0); - stream_control(in->d_thread->stream, STREAM_CTRL_SET_READAHEAD, - &(int){any_selected}); - pthread_mutex_lock(&in->lock); } @@ -1746,13 +1747,6 @@ static bool thread_work(struct demux_internal *in) if (read_packet(in)) return true; // read_packet unlocked, so recheck conditions } - if (in->force_cache_update) { - pthread_mutex_unlock(&in->lock); - update_cache(in); - pthread_mutex_lock(&in->lock); - in->force_cache_update = false; - return true; - } return false; } @@ -2276,6 +2270,19 @@ static void fixup_metadata(struct demux_internal *in) } } +// Return whether "heavy" caching on this stream is enabled. By default, this +// corresponds to whether the source stream is considered in the network. The +// only effect should be adjusting display behavior (of cache stats etc.), and +// possibly switching between which set of options influence cache settings. +bool demux_is_network_cached(demuxer_t *demuxer) +{ + struct demux_internal *in = demuxer->in; + bool use_cache = demuxer->is_network; + if (in->opts->enable_cache >= 0) + use_cache = in->opts->enable_cache == 1; + return use_cache; +} + static struct demuxer *open_given_type(struct mpv_global *global, struct mp_log *log, const struct demuxer_desc *desc, @@ -2305,12 +2312,11 @@ static struct demuxer *open_given_type(struct mpv_global *global, .extended_ctrls = stream->extended_ctrls, }; demuxer->seekable = stream->seekable; - if (demuxer->stream->underlying && !demuxer->stream->underlying->seekable) - demuxer->seekable = false; struct demux_internal *in = demuxer->in = talloc_ptrtype(demuxer, in); *in = (struct demux_internal){ .log = demuxer->log, + .opts = opts, .d_thread = talloc(demuxer, struct demuxer), .d_user = demuxer, .min_secs = opts->min_secs, @@ -2371,10 +2377,8 @@ static struct demuxer *open_given_type(struct mpv_global *global, fixup_metadata(in); in->events = DEMUX_EVENT_ALL; demux_update(demuxer); - stream_control(demuxer->stream, STREAM_CTRL_SET_READAHEAD, - &(int){params ? params->initial_readahead : false}); int seekable = opts->seekable_cache; - if (demuxer->is_network || stream->caching) { + if (demux_is_network_cached(demuxer)) { in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache); if (seekable < 0) seekable = 1; @@ -2486,8 +2490,6 @@ struct demuxer *demux_open_url(const char *url, talloc_free(priv_cancel); return NULL; } - if (!params->disable_cache) - stream_enable_cache_defaults(&s); struct demuxer *d = demux_open(s, params, global); if (d) { talloc_steal(d->in, priv_cancel); @@ -3042,15 +3044,12 @@ static void update_cache(struct demux_internal *in) // Don't lock while querying the stream. struct mp_tags *stream_metadata = NULL; - struct stream_cache_info stream_cache_info = {.size = -1}; int64_t stream_size = stream_get_size(stream); stream_control(stream, STREAM_CTRL_GET_METADATA, &stream_metadata); - stream_control(stream, STREAM_CTRL_GET_CACHE_INFO, &stream_cache_info); pthread_mutex_lock(&in->lock); in->stream_size = stream_size; - in->stream_cache_info = stream_cache_info; if (stream_metadata) { for (int n = 0; n < in->num_streams; n++) { struct demux_stream *ds = in->streams[n]->ds; @@ -3065,18 +3064,7 @@ static void update_cache(struct demux_internal *in) // must be called locked static int cached_stream_control(struct demux_internal *in, int cmd, void *arg) { - // If the cache is active, wake up the thread to possibly update cache state. - if (in->stream_cache_info.size >= 0) { - in->force_cache_update = true; - pthread_cond_signal(&in->wakeup); - } - switch (cmd) { - case STREAM_CTRL_GET_CACHE_INFO: - if (in->stream_cache_info.size < 0) - return STREAM_UNSUPPORTED; - *(struct stream_cache_info *)arg = in->stream_cache_info; - return STREAM_OK; case STREAM_CTRL_GET_SIZE: if (in->stream_size < 0) return STREAM_UNSUPPORTED; diff --git a/demux/demux.h b/demux/demux.h index 7d2924000a..5b92e97f49 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -174,13 +174,11 @@ struct demuxer_params { bool *matroska_was_valid; struct timeline *timeline; bool disable_timeline; - bool initial_readahead; bstr init_fragment; bool skip_lavf_probing; bool does_not_own_stream; // if false, stream is free'd on demux_free() // -- demux_open_url() only int stream_flags; - bool disable_cache; // result bool demuxer_failed; }; @@ -317,6 +315,7 @@ void demux_metadata_changed(demuxer_t *demuxer); void demux_update(demuxer_t *demuxer); void demux_disable_cache(demuxer_t *demuxer); +bool demux_is_network_cached(demuxer_t *demuxer); struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d, enum stream_type t, int id); diff --git a/demux/demux_disc.c b/demux/demux_disc.c index 15ccb2313d..e5c63cea17 100644 --- a/demux/demux_disc.c +++ b/demux/demux_disc.c @@ -292,11 +292,8 @@ static int d_open(demuxer_t *demuxer, enum demux_check check) struct stream *cur = demuxer->stream; const char *sname = ""; - while (cur) { - if (cur->info) - sname = cur->info->name; - cur = cur->underlying; // down the caching chain - } + if (cur->info) + sname = cur->info->name; p->is_cdda = strcmp(sname, "cdda") == 0; p->is_dvd = strcmp(sname, "dvd") == 0 || diff --git a/demux/demux_mkv_timeline.c b/demux/demux_mkv_timeline.c index 32eab2ca8f..69cf26e681 100644 --- a/demux/demux_mkv_timeline.c +++ b/demux/demux_mkv_timeline.c @@ -172,7 +172,6 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment) .matroska_wanted_segment = segment, .matroska_was_valid = &was_valid, .disable_timeline = true, - .disable_cache = true, }; struct mp_cancel *cancel = ctx->tl->cancel; if (mp_cancel_test(cancel)) @@ -216,15 +215,6 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment) } } - if (stream_wants_cache(d->stream, ctx->opts->stream_cache)) { - demux_free(d); - params.disable_cache = false; - params.matroska_wanted_uids = ctx->uids; // potentially reallocated, same data - d = demux_open_url(filename, ¶ms, cancel, ctx->global); - if (!d) - return false; - } - ctx->sources[i] = d; return true; } diff --git a/options/options.c b/options/options.c index 5a77e9973d..2548759ddb 100644 --- a/options/options.c +++ b/options/options.c @@ -59,7 +59,6 @@ extern const struct m_sub_options tv_params_conf; extern const struct m_sub_options stream_cdda_conf; extern const struct m_sub_options stream_dvb_conf; extern const struct m_sub_options stream_lavf_conf; -extern const struct m_sub_options stream_cache_conf; extern const struct m_sub_options sws_conf; extern const struct m_sub_options drm_conf; extern const struct m_sub_options demux_rawaudio_conf; @@ -387,8 +386,6 @@ const m_option_t mp_opts[] = { // ------------------------- stream options -------------------- - OPT_SUBSTRUCT("", stream_cache, stream_cache_conf, 0), - #if HAVE_DVDREAD || HAVE_DVDNAV OPT_SUBSTRUCT("", dvd_opts, dvd_conf, 0), #endif /* HAVE_DVDREAD */ diff --git a/options/options.h b/options/options.h index 0eb5451794..a6532b4458 100644 --- a/options/options.h +++ b/options/options.h @@ -59,16 +59,6 @@ typedef struct mp_vo_opts { struct drm_opts *drm_opts; } mp_vo_opts; -struct mp_cache_opts { - int size; - int def_size; - int initial; - int seek_min; - int back_buffer; - char *file; - int file_max; -}; - // Subtitle options needed by the subtitle decoders/renderers. struct mp_subtitle_opts { int sub_visibility; @@ -204,7 +194,6 @@ typedef struct MPOpts { char *force_configdir; int use_filedir_conf; int hls_bitrate; - struct mp_cache_opts *stream_cache; int chapterrange[2]; int edition_id; int correct_pts; @@ -358,7 +347,6 @@ struct filter_opts { extern const m_option_t mp_opts[]; extern const struct MPOpts mp_default_opts; extern const struct m_sub_options vo_sub_opts; -extern const struct m_sub_options stream_cache_conf; extern const struct m_sub_options dvd_conf; extern const struct m_sub_options mp_subtitle_sub_opts; extern const struct m_sub_options mp_osd_render_sub_opts; diff --git a/player/command.c b/player/command.c index f556e7211d..ab9a09fcac 100644 --- a/player/command.c +++ b/player/command.c @@ -1559,134 +1559,6 @@ static int mp_property_playback_abort(void *ctx, struct m_property *prop, return m_property_flag_ro(action, arg, !mpctx->playing || mpctx->stop_play); } -static int mp_property_cache(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - float cache = mp_get_cache_percent(mpctx); - if (cache < 0) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%d", (int)cache); - return M_PROPERTY_OK; - } - - return m_property_float_ro(action, arg, cache); -} - -static int property_int_kb_size(int kb_size, int action, void *arg) -{ - switch (action) { - case M_PROPERTY_GET: - *(int *)arg = kb_size; - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: - *(char **)arg = format_file_size(kb_size * 1024LL); - return M_PROPERTY_OK; - case M_PROPERTY_GET_TYPE: - *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_INT}; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_cache_size(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct demuxer *demuxer = mpctx->demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_GET: - case M_PROPERTY_PRINT: { - struct stream_cache_info info = {0}; - demux_stream_control(demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - break; - return property_int_kb_size(info.size / 1024, action, arg); - } - case M_PROPERTY_GET_TYPE: - *(struct m_option *)arg = (struct m_option){ - .type = CONF_TYPE_INT, - .flags = M_OPT_MIN, - .min = 0, - }; - return M_PROPERTY_OK; - case M_PROPERTY_SET: { - int64_t size = *(int *)arg * 1024LL; - int r = demux_stream_control(demuxer, 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_cache_used(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - if (!mpctx->demuxer) - return M_PROPERTY_UNAVAILABLE; - - struct stream_cache_info info = {0}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - return property_int_kb_size(info.fill / 1024, action, arg); -} - -static int mp_property_cache_free(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - if (!mpctx->demuxer) - return M_PROPERTY_UNAVAILABLE; - - struct stream_cache_info info = {0}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - - return property_int_kb_size((info.size - info.fill) / 1024, action, arg); -} - -static int mp_property_cache_speed(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - if (!mpctx->demuxer) - return M_PROPERTY_UNAVAILABLE; - - struct stream_cache_info info = {0}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_strdup_append(format_file_size(info.speed), "/s"); - return M_PROPERTY_OK; - } - return m_property_int64_ro(action, arg, info.speed); -} - -static int mp_property_cache_idle(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct stream_cache_info info = {0}; - if (mpctx->demuxer) - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - return m_property_flag_ro(action, arg, info.idle); -} - static int mp_property_demuxer_cache_duration(void *ctx, struct m_property *prop, int action, void *arg) { @@ -3897,12 +3769,6 @@ static const struct m_property mp_properties_base[] = { {"eof-reached", mp_property_eof_reached}, {"seeking", mp_property_seeking}, {"playback-abort", mp_property_playback_abort}, - {"cache-percent", mp_property_cache}, - {"cache-free", mp_property_cache_free}, - {"cache-used", mp_property_cache_used}, - {"cache-size", mp_property_cache_size}, - {"cache-idle", mp_property_cache_idle}, - {"cache-speed", mp_property_cache_speed}, {"demuxer-cache-duration", mp_property_demuxer_cache_duration}, {"demuxer-cache-time", mp_property_demuxer_cache_time}, {"demuxer-cache-idle", mp_property_demuxer_cache_idle}, diff --git a/player/core.h b/player/core.h index 193537652b..7fab8776d7 100644 --- a/player/core.h +++ b/player/core.h @@ -554,8 +554,6 @@ double get_play_end_pts(struct MPContext *mpctx); double get_play_start_pts(struct MPContext *mpctx); double get_ab_loop_start_time(struct MPContext *mpctx); void merge_playlist_files(struct playlist *pl); -float mp_get_cache_percent(struct MPContext *mpctx); -bool mp_get_cache_idle(struct MPContext *mpctx); void update_vo_playback_state(struct MPContext *mpctx); void update_window_title(struct MPContext *mpctx, bool force); void error_on_track(struct MPContext *mpctx, struct track *track); diff --git a/player/loadfile.c b/player/loadfile.c index d854ab04a9..2fb69eb635 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -971,7 +971,6 @@ static void *open_demux_thread(void *ctx) struct demuxer_params p = { .force_format = mpctx->open_format, .stream_flags = mpctx->open_url_flags, - .initial_readahead = true, }; mpctx->open_res_demuxer = demux_open_url(mpctx->open_url, &p, mpctx->open_cancel, mpctx->global); diff --git a/player/misc.c b/player/misc.c index f2e6f05173..ce353b9590 100644 --- a/player/misc.c +++ b/player/misc.c @@ -203,24 +203,6 @@ void issue_refresh_seek(struct MPContext *mpctx, enum seek_precision min_prec) queue_seek(mpctx, MPSEEK_ABSOLUTE, get_current_time(mpctx), min_prec, 0); } -float mp_get_cache_percent(struct MPContext *mpctx) -{ - struct stream_cache_info info = {0}; - if (mpctx->demuxer) - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size > 0 && info.fill >= 0) - return info.fill / (info.size / 100.0); - return -1; -} - -bool mp_get_cache_idle(struct MPContext *mpctx) -{ - struct stream_cache_info info = {0}; - if (mpctx->demuxer) - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - return info.idle; -} - void update_vo_playback_state(struct MPContext *mpctx) { if (mpctx->video_out && mpctx->video_out->config_ok) { diff --git a/player/osd.c b/player/osd.c index 9faf6eb6a5..7d24c01619 100644 --- a/player/osd.c +++ b/player/osd.c @@ -229,27 +229,23 @@ static char *get_term_status_msg(struct MPContext *mpctx) } } - if (mpctx->demuxer) { - struct stream_cache_info info = {0}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size > 0 || mpctx->demuxer->is_network) { - saddf(&line, " Cache: "); + if (mpctx->demuxer && demux_is_network_cached(mpctx->demuxer)) { + saddf(&line, " Cache: "); - struct demux_ctrl_reader_state s = {.ts_duration = -1}; - demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_READER_STATE, &s); + struct demux_ctrl_reader_state s = {.ts_duration = -1}; + demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_READER_STATE, &s); - if (s.ts_duration < 0) { - saddf(&line, "???"); + if (s.ts_duration < 0) { + saddf(&line, "???"); + } else { + saddf(&line, "%2ds", (int)s.ts_duration); + } + int64_t cache_size = s.fw_bytes; + if (cache_size > 0) { + if (cache_size >= 1024 * 1024) { + saddf(&line, "+%lldMB", (long long)(cache_size / 1024 / 1024)); } else { - saddf(&line, "%2ds", (int)s.ts_duration); - } - int64_t cache_size = s.fw_bytes + info.fill; - if (cache_size > 0) { - if (cache_size >= 1024 * 1024) { - saddf(&line, "+%lldMB", (long long)(cache_size / 1024 / 1024)); - } else { - saddf(&line, "+%lldKB", (long long)(cache_size / 1024)); - } + saddf(&line, "+%lldKB", (long long)(cache_size / 1024)); } } } diff --git a/player/playloop.c b/player/playloop.c index fa1cc544cb..59e3211df9 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -627,14 +627,11 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx) double now = mp_time_sec(); - struct stream_cache_info c = {.idle = true}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &c); - struct demux_ctrl_reader_state s = {.idle = true, .ts_duration = -1}; demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_READER_STATE, &s); int cache_buffer = 100; - bool use_pause_on_low_cache = (c.size > 0 || mpctx->demuxer->is_network) && + bool use_pause_on_low_cache = demux_is_network_cached(mpctx->demuxer) && opts->cache_pause; if (!mpctx->restart_complete) { @@ -669,7 +666,7 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx) } // Also update cache properties. - bool busy = !s.idle || !c.idle; + bool busy = !s.idle; if (busy || mpctx->next_cache_update > 0) { if (mpctx->next_cache_update <= now) { mpctx->next_cache_update = busy ? now + 0.25 : 0; diff --git a/stream/cache.c b/stream/cache.c deleted file mode 100644 index 424f27910f..0000000000 --- a/stream/cache.c +++ /dev/null @@ -1,809 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -// Time in seconds the main thread waits for the cache thread. On wakeups, the -// code checks for user requested aborts and also prints warnings that the -// cache is being slow. -#define CACHE_WAIT_TIME 1.0 - -// The time the cache sleeps in idle mode. This controls how often the cache -// retries reading from the stream after EOF has reached (in case the stream is -// actually readable again, for example if data has been appended to a file). -// Note that if this timeout is too low, the player will waste too much CPU -// when player is paused. -#define CACHE_IDLE_SLEEP_TIME 1.0 - -// Time in seconds the cache updates "cached" controls. Note that idle mode -// will block the cache from doing this, and this timeout is honored only if -// the cache is active. -#define CACHE_UPDATE_CONTROLS_TIME 2.0 - - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <unistd.h> -#include <errno.h> -#include <assert.h> -#include <pthread.h> -#include <time.h> -#include <math.h> -#include <sys/time.h> - -#include <libavutil/common.h> - -#include "config.h" - -#include "osdep/timer.h" -#include "osdep/threads.h" - -#include "common/msg.h" -#include "common/tags.h" -#include "misc/thread_tools.h" -#include "options/options.h" - -#include "stream.h" -#include "common/common.h" - -#define OPT_BASE_STRUCT struct mp_cache_opts - -const struct m_sub_options stream_cache_conf = { - .opts = (const struct m_option[]){ - OPT_CHOICE_OR_INT("cache", size, 0, 32, 0x7fffffff, - ({"no", 0}, - {"auto", -1}, - {"yes", -2})), - OPT_CHOICE_OR_INT("cache-default", def_size, 0, 32, 0x7fffffff, - ({"no", 0})), - OPT_INTRANGE("cache-initial", initial, 0, 0, 0x7fffffff), - OPT_INTRANGE("cache-seek-min", seek_min, 0, 0, 0x7fffffff), - OPT_INTRANGE("cache-backbuffer", back_buffer, 0, 0, 0x7fffffff), - OPT_STRING("cache-file", file, M_OPT_FILE), - OPT_INTRANGE("cache-file-size", file_max, 0, 0, 0x7fffffff), - {0} - }, - .size = sizeof(struct mp_cache_opts), - .defaults = &(const struct mp_cache_opts){ - .size = -1, - .def_size = 10000, - .initial = 0, - .seek_min = 500, - .back_buffer = 10000, - .file_max = 1024 * 1024, - }, -}; - -// Note: (struct priv*)(cache->priv)->cache == cache -struct priv { - pthread_t cache_thread; - bool cache_thread_running; - pthread_mutex_t mutex; - 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 seek_limit; // keep filling cache if distance is less that seek limit - bool seekable; // underlying stream is seekable - - struct mp_log *log; - - // Owned by the main thread - stream_t *cache; // wrapper stream, used by demuxer etc. - - // Owned by the cache thread - stream_t *stream; // "real" stream, used to read from the source media - int64_t bytes_until_wakeup; // wakeup cache thread after this many bytes - - // All the following members are shared between the threads. - // You must lock the mutex to access them. - - // Ringbuffer - int64_t min_filepos; // range of file that is cached in the buffer - int64_t max_filepos; // ... max_filepos being the last read position - bool eof; // true if max_filepos = EOF - int64_t offset; // buffer[WRAP(s->max_filepos - offset)] corresponds - // to the byte at max_filepos (must be wrapped by - // buffer_size) - - bool idle; // cache thread has stopped reading - int64_t reads; // number of actual read attempts performed - int64_t speed_start; // start time (us) for calculating download speed - int64_t speed_amount; // bytes read since speed_start - double speed; - - bool enable_readahead; // actively read beyond read() position - int64_t read_filepos; // client read position (mirrors cache->pos) - int64_t read_min; // file position until which the thread should - // read even if readahead is disabled - - int64_t eof_pos; - - bool read_seek_failed; // let a read fail because an async seek failed - - int control; // requested STREAM_CTRL_... or CACHE_CTRL_... - void *control_arg; // temporary for executing STREAM_CTRLs - int control_res; - bool control_flush; - - // Cached STREAM_CTRLs - double stream_time_length; - int64_t stream_size; - struct mp_tags *stream_metadata; - double start_pts; - bool has_avseek; -}; - -enum { - CACHE_CTRL_NONE = 0, - CACHE_CTRL_QUIT = -1, - CACHE_CTRL_PING = -2, - CACHE_CTRL_SEEK = -3, - - // we should fill buffer only if space>=FILL_LIMIT - FILL_LIMIT = 16 * 1024, -}; - -// Used by the main thread to wakeup the cache thread, and to wait for the -// cache thread. The cache mutex has to be locked when calling this function. -// *retry_time should be set to 0 on the first call. -// Return false if the stream has been aborted. -static bool cache_wakeup_and_wait(struct priv *s, double *retry_time) -{ - double start = mp_time_sec(); - if (*retry_time >= CACHE_WAIT_TIME) { - MP_VERBOSE(s, "Cache is not responding - slow/stuck network connection?\n"); - *retry_time = -1; // do not warn again for this call - } - - pthread_cond_signal(&s->wakeup); - struct timespec ts = mp_rel_time_to_timespec(CACHE_WAIT_TIME); - pthread_cond_timedwait(&s->wakeup, &s->mutex, &ts); - - if (*retry_time >= 0) - *retry_time += mp_time_sec() - start; - - return !mp_cancel_test(s->cache->cancel); -} - -// Runs in the cache thread -static void cache_drop_contents(struct priv *s) -{ - s->offset = s->min_filepos = s->max_filepos = s->read_filepos; - s->eof = false; - s->start_pts = MP_NOPTS_VALUE; -} - -static void update_speed(struct priv *s) -{ - int64_t now = mp_time_us(); - if (s->speed_start + 1000000 <= now) { - s->speed = s->speed_amount * 1e6 / (now - s->speed_start); - s->speed_amount = 0; - s->speed_start = now; - } -} - -// Copy at most dst_size from the cache at the given absolute file position pos. -// Return number of bytes that could actually be read. -// Does not advance the file position, or change anything else. -// Can be called from anywhere, as long as the mutex is held. -static size_t read_buffer(struct priv *s, unsigned char *dst, - size_t dst_size, int64_t pos) -{ - size_t read = 0; - while (read < dst_size) { - if (pos >= s->max_filepos || pos < s->min_filepos) - break; - int64_t newb = s->max_filepos - pos; // new bytes in the buffer - - int64_t bpos = pos - s->offset; // file pos to buffer memory pos - if (bpos < 0) { - bpos += s->buffer_size; - } else if (bpos >= s->buffer_size) { - bpos -= s->buffer_size; - } - - if (newb > s->buffer_size - bpos) - newb = s->buffer_size - bpos; // handle wrap... - - newb = MPMIN(newb, dst_size - read); - - assert(newb >= 0 && read + newb <= dst_size); - assert(bpos >= 0 && bpos + newb <= s->buffer_size); - memcpy(&dst[read], &s->buffer[bpos], newb); - read += newb; - pos += newb; - } - return read; -} - -// Whether a seek will be needed to get to the position. This honors seek_limit, -// which is a heuristic to prevent dropping the cache with small forward seeks. -// This helps in situations where waiting for network a bit longer would quickly -// reach the target position. Especially if the demuxer seeks back and forth, -// not dropping the backwards cache will be a major performance win. -static bool needs_seek(struct priv *s, int64_t pos) -{ - return pos < s->min_filepos || pos > s->max_filepos + s->seek_limit; -} - -static bool cache_update_stream_position(struct priv *s) -{ - int64_t read = s->read_filepos; - - s->read_seek_failed = false; - - if (needs_seek(s, read)) { - MP_VERBOSE(s, "Dropping cache at pos %"PRId64", " - "cached range: %"PRId64"-%"PRId64".\n", read, - s->min_filepos, s->max_filepos); - cache_drop_contents(s); - } - - if (stream_tell(s->stream) != s->max_filepos && s->seekable) { - MP_VERBOSE(s, "Seeking underlying stream: %"PRId64" -> %"PRId64"\n", - stream_tell(s->stream), s->max_filepos); - if (!stream_seek(s->stream, s->max_filepos)) { - s->read_seek_failed = true; - return false; - } - } - - return stream_tell(s->stream) == s->max_filepos; -} - -// Runs in the cache thread. -static void cache_fill(struct priv *s) -{ - int64_t read = s->read_filepos; - bool read_attempted = false; - int len = 0; - - if (!cache_update_stream_position(s)) - goto done; - - if (!s->enable_readahead && s->read_min <= s->max_filepos) - goto done; - - if (mp_cancel_test(s->cache->cancel)) - goto done; - - // number of buffer bytes which should be preserved in backwards direction - int64_t back = MPCLAMP(read - s->min_filepos, 0, s->back_size); - - // limit maximum readahead so that the backbuffer space is reserved, even - // if the backbuffer is not used. limit it to ensure that we don't stall the - // network when starting a file, or we wouldn't download new data until we - // get new free space again. (unless everything fits in the cache.) - if (s->stream_size > s->buffer_size) - back = MPMAX(back, s->back_size); - - // number of buffer bytes that are valid and can be read - int64_t newb = FFMAX(s->max_filepos - read, 0); - - // max. number of bytes that can be written (starting from max_filepos) - int64_t space = s->buffer_size - (newb + back); - - // offset into the buffer that maps to max_filepos - int64_t pos = s->max_filepos - s->offset; - if (pos >= s->buffer_size) - pos -= s->buffer_size; // wrap-around - - if (space < FILL_LIMIT) - goto done; - - // limit to end of buffer (without wrapping) - if (pos + space >= s->buffer_size) - space = s->buffer_size - pos; - - // limit read size (or else would block and read the entire buffer in 1 call) - space = FFMIN(space, s->stream->read_chunk); - - // back+newb+space <= buffer_size - int64_t back2 = s->buffer_size - (space + newb); // max back size - if (s->min_filepos < (read - back2)) - s->min_filepos = read - back2; - - // The read call might take a long time and block, so drop the lock. - pthread_mutex_unlock(&s->mutex); - len = stream_read_partial(s->stream, &s->buffer[pos], space); - pthread_mutex_lock(&s->mutex); - - // Do this after reading a block, because at least libdvdnav updates the - // stream position only after actually reading something after a seek. - if (s->start_pts == MP_NOPTS_VALUE) { - double pts; - if (stream_control(s->stream, STREAM_CTRL_GET_CURRENT_TIME, &pts) > 0) - s->start_pts = pts; - } - - s->max_filepos += len; - if (pos + len == s->buffer_size) - s->offset += s->buffer_size; // wrap... - s->speed_amount += len; - - read_attempted = true; - -done: ; - - bool prev_eof = s->eof; - if (read_attempted) - s->eof = len <= 0; - if (!prev_eof && s->eof) { - s->eof_pos = stream_tell(s->stream); - MP_VERBOSE(s, "EOF reached.\n"); - } - s->idle = s->eof || !read_attempted; - s->reads++; - - update_speed(s); - - pthread_cond_signal(&s->wakeup); -} - -// This is called both during init and at runtime. -// The size argument is the readahead half only; s->back_size is the backbuffer. -static int resize_cache(struct priv *s, int64_t size) -{ - int64_t min_size = FILL_LIMIT * 2; - int64_t max_size = ((size_t)-1) / 8; - - if (s->stream_size > 0) { - size = MPMIN(size, s->stream_size); - if (size >= s->stream_size) { - MP_VERBOSE(s, "no backbuffer needed\n"); - s->back_size = 0; - } - } - - int64_t buffer_size = MPCLAMP(size, min_size, max_size); - s->back_size = MPCLAMP(s->back_size, min_size, max_size); - buffer_size += s->back_size; - - unsigned char *buffer = malloc(buffer_size); - if (!buffer) - 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); - - s->buffer_size = buffer_size; - s->buffer = buffer; - 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; - - MP_VERBOSE(s, "Cache size set to %lld KiB (%lld KiB backbuffer)\n", - (long long)(s->buffer_size / 1024), - (long long)(s->back_size / 1024)); - - assert(s->back_size < s->buffer_size); - - return STREAM_OK; -} - -static void update_cached_controls(struct priv *s) -{ - int64_t i64; - double d; - struct mp_tags *tags; - s->stream_time_length = 0; - if (stream_control(s->stream, STREAM_CTRL_GET_TIME_LENGTH, &d) == STREAM_OK) - s->stream_time_length = d; - if (stream_control(s->stream, STREAM_CTRL_GET_METADATA, &tags) == STREAM_OK) { - talloc_free(s->stream_metadata); - s->stream_metadata = talloc_steal(s, tags); - } - s->stream_size = s->eof_pos; - i64 = stream_get_size(s->stream); - if (i64 >= 0) - s->stream_size = i64; - s->has_avseek = stream_control(s->stream, STREAM_CTRL_HAS_AVSEEK, NULL) > 0; -} - -// the core might call these every frame, so cache them... -static int cache_get_cached_control(stream_t *cache, int cmd, void *arg) -{ - struct priv *s = cache->priv; - switch (cmd) { - case STREAM_CTRL_GET_CACHE_INFO: - *(struct stream_cache_info *)arg = (struct stream_cache_info) { - .size = s->buffer_size - s->back_size, - .fill = s->max_filepos - s->read_filepos, - .idle = s->idle, - .speed = llrint(s->speed), - }; - return STREAM_OK; - case STREAM_CTRL_SET_READAHEAD: - s->enable_readahead = *(int *)arg; - pthread_cond_signal(&s->wakeup); - return STREAM_OK; - case STREAM_CTRL_GET_TIME_LENGTH: - *(double *)arg = s->stream_time_length; - return s->stream_time_length ? STREAM_OK : STREAM_UNSUPPORTED; - case STREAM_CTRL_GET_SIZE: - if (s->stream_size < 0) - return STREAM_UNSUPPORTED; - *(int64_t *)arg = s->stream_size; - return STREAM_OK; - case STREAM_CTRL_GET_CURRENT_TIME: { - if (s->start_pts == MP_NOPTS_VALUE) - return STREAM_UNSUPPORTED; - *(double *)arg = s->start_pts; - return STREAM_OK; - } - case STREAM_CTRL_HAS_AVSEEK: - return s->has_avseek ? STREAM_OK : STREAM_UNSUPPORTED; - case STREAM_CTRL_GET_METADATA: { - if (s->stream_metadata) { - ta_set_parent(s->stream_metadata, NULL); - *(struct mp_tags **)arg = s->stream_metadata; - s->stream_metadata = NULL; - return STREAM_OK; - } - return STREAM_UNSUPPORTED; - } - case STREAM_CTRL_AVSEEK: - if (!s->has_avseek) - return STREAM_UNSUPPORTED; - break; - } - return STREAM_ERROR; -} - -static bool control_needs_flush(int stream_ctrl) -{ - switch (stream_ctrl) { - case STREAM_CTRL_SEEK_TO_TIME: - case STREAM_CTRL_AVSEEK: - case STREAM_CTRL_SET_ANGLE: - case STREAM_CTRL_SET_CURRENT_TITLE: - case STREAM_CTRL_DVB_SET_CHANNEL: - case STREAM_CTRL_DVB_SET_CHANNEL_NAME: - case STREAM_CTRL_DVB_STEP_CHANNEL: - return true; - } - return false; -} - -// Runs in the cache thread -static void cache_execute_control(struct priv *s) -{ - uint64_t old_pos = stream_tell(s->stream); - 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) { - MP_ERR(s, "STREAM_CTRL changed stream pos but " - "returned error, this is not allowed!\n"); - } else if (pos_changed || (ok && control_needs_flush(s->control))) { - MP_VERBOSE(s, "Dropping cache due to control()\n"); - s->read_filepos = stream_tell(s->stream); - s->read_min = s->read_filepos; - s->control_flush = true; - cache_drop_contents(s); - } - - update_cached_controls(s); - s->control = CACHE_CTRL_NONE; - pthread_cond_signal(&s->wakeup); -} - -static void *cache_thread(void *arg) -{ - struct priv *s = arg; - mpthread_set_name("cache"); - pthread_mutex_lock(&s->mutex); - update_cached_controls(s); - double last = mp_time_sec(); - while (s->control != CACHE_CTRL_QUIT) { - if (mp_time_sec() - last > CACHE_UPDATE_CONTROLS_TIME) { - update_cached_controls(s); - last = mp_time_sec(); - } - if (s->control > 0) { - cache_execute_control(s); - } else if (s->control == CACHE_CTRL_SEEK) { - s->control_res = cache_update_stream_position(s); - s->control = CACHE_CTRL_NONE; - pthread_cond_signal(&s->wakeup); - } else { - cache_fill(s); - } - if (s->control == CACHE_CTRL_PING) { - pthread_cond_signal(&s->wakeup); - s->control = CACHE_CTRL_NONE; - } - if (s->idle && s->control == CACHE_CTRL_NONE) { - struct timespec ts = mp_rel_time_to_timespec(CACHE_IDLE_SLEEP_TIME); - pthread_cond_timedwait(&s->wakeup, &s->mutex, &ts); - } - } - pthread_cond_signal(&s->wakeup); - pthread_mutex_unlock(&s->mutex); - MP_VERBOSE(s, "Cache exiting...\n"); - return NULL; -} - -static int cache_fill_buffer(struct stream *cache, char *buffer, int max_len) -{ - struct priv *s = cache->priv; - assert(s->cache_thread_running); - - pthread_mutex_lock(&s->mutex); - - if (cache->pos != s->read_filepos) - MP_ERR(s, "!!! read_filepos differs !!! report this bug...\n"); - - int readb = 0; - if (max_len > 0) { - double retry_time = 0; - int64_t retry = s->reads - 1; // try at least 1 read on EOF - while (1) { - s->read_min = s->read_filepos + max_len + 64 * 1024; - readb = read_buffer(s, buffer, max_len, s->read_filepos); - s->read_filepos += readb; - if (readb > 0) - break; - if (s->eof && s->read_filepos >= s->max_filepos && s->reads >= retry) - break; - s->idle = false; - if (!cache_wakeup_and_wait(s, &retry_time)) - break; - if (s->read_seek_failed) { - MP_ERR(s, "error reading after async seek failed\n"); - s->read_seek_failed = false; - break; - } - } - } - - if (!s->eof) { - // wakeup the cache thread, possibly make it read more data ahead - // this is throttled to reduce excessive wakeups during normal reading - // (using the amount of bytes after which the cache thread most likely - // can actually read new data) - s->bytes_until_wakeup -= readb; - if (s->bytes_until_wakeup <= 0) { - s->bytes_until_wakeup = MPMAX(FILL_LIMIT, s->stream->read_chunk); - pthread_cond_signal(&s->wakeup); - } - } - pthread_mutex_unlock(&s->mutex); - return readb; -} - -static int cache_seek(stream_t *cache, int64_t pos) -{ - struct priv *s = cache->priv; - assert(s->cache_thread_running); - int r = 1; - - pthread_mutex_lock(&s->mutex); - - MP_DBG(s, "request seek: %" PRId64 " <= to=%" PRId64 - " (cur=%" PRId64 ") <= %" PRId64 " \n", - s->min_filepos, pos, s->read_filepos, s->max_filepos); - - if (!s->seekable && pos > s->max_filepos) { - MP_ERR(s, "Attempting to seek past cached data in unseekable stream.\n"); - r = 0; - } else if (!s->seekable && pos < s->min_filepos) { - MP_ERR(s, "Attempting to seek before cached data in unseekable stream.\n"); - r = 0; - } else { - cache->pos = s->read_filepos = s->read_min = pos; - // Is this seek likely to cause a stream-level seek? - // If it is, wait until that is complete and return its result. - // This check is not quite exact - if the reader thread is blocked in - // a read, the read might advance file position enough that a seek - // forward is no longer needed. - if (needs_seek(s, pos)) { - s->eof = false; - s->control = CACHE_CTRL_SEEK; - s->control_res = 0; - double retry = 0; - while (s->control != CACHE_CTRL_NONE) { - if (!cache_wakeup_and_wait(s, &retry)) - break; - } - r = s->control_res; - } else { - pthread_cond_signal(&s->wakeup); - r = 1; - } - } - - s->bytes_until_wakeup = 0; - - pthread_mutex_unlock(&s->mutex); - - return r; -} - -static int cache_control(stream_t *cache, int cmd, void *arg) -{ - struct priv *s = cache->priv; - int r = STREAM_ERROR; - - assert(cmd > 0); - - pthread_mutex_lock(&s->mutex); - - r = cache_get_cached_control(cache, cmd, arg); - if (r != STREAM_ERROR) - goto done; - - MP_VERBOSE(s, "blocking for STREAM_CTRL %d\n", cmd); - - s->control = cmd; - s->control_arg = arg; - double retry = 0; - while (s->control != CACHE_CTRL_NONE) { - if (!cache_wakeup_and_wait(s, &retry)) { - s->eof = 1; - r = STREAM_UNSUPPORTED; - goto done; - } - } - r = s->control_res; - if (s->control_flush) { - stream_drop_buffers(cache); - cache->pos = s->read_filepos; - } - -done: - pthread_mutex_unlock(&s->mutex); - return r; -} - -static void cache_uninit(stream_t *cache) -{ - struct priv *s = cache->priv; - if (s->cache_thread_running) { - MP_VERBOSE(s, "Terminating cache...\n"); - pthread_mutex_lock(&s->mutex); - s->control = CACHE_CTRL_QUIT; - pthread_cond_signal(&s->wakeup); - pthread_mutex_unlock(&s->mutex); - pthread_join(s->cache_thread, NULL); - } - pthread_mutex_destroy(&s->mutex); - pthread_cond_destroy(&s->wakeup); - free(s->buffer); - talloc_free(s); -} - -// return 1 on success, 0 if the cache is disabled/not needed, and -1 on error -// or if the cache is disabled -int stream_cache_init(stream_t *cache, stream_t *stream, - struct mp_cache_opts *opts) -{ - if (opts->size < 1) - return 0; - - struct priv *s = talloc_zero(NULL, struct priv); - s->log = cache->log; - s->eof_pos = -1; - s->enable_readahead = true; - - cache_drop_contents(s); - - s->speed_start = mp_time_us(); - - s->seek_limit = opts->seek_min * 1024ULL; - s->back_size = opts->back_buffer * 1024ULL; - - s->stream_size = stream_get_size(stream); - - if (resize_cache(s, opts->size * 1024ULL) != STREAM_OK) { - MP_ERR(s, "Failed to allocate cache buffer.\n"); - talloc_free(s); - return -1; - } - - pthread_mutex_init(&s->mutex, NULL); - pthread_cond_init(&s->wakeup, NULL); - - cache->priv = s; - s->cache = cache; - s->stream = stream; - - cache->seek = cache_seek; - cache->fill_buffer = cache_fill_buffer; - cache->control = cache_control; - cache->close = cache_uninit; - - int64_t min = opts->initial * 1024ULL; - if (min > s->buffer_size - FILL_LIMIT) - min = s->buffer_size - FILL_LIMIT; - - s->seekable = stream->seekable; - - if (pthread_create(&s->cache_thread, NULL, cache_thread, s) != 0) { - MP_ERR(s, "Starting cache thread failed.\n"); - return -1; - } - s->cache_thread_running = true; - - // wait until cache is filled with at least min bytes - if (min < 1) - return 1; - for (;;) { - if (mp_cancel_test(cache->cancel)) - return -1; - struct stream_cache_info info; - if (stream_control(s->cache, STREAM_CTRL_GET_CACHE_INFO, &info) < 0) - break; - mp_msg(s->log, MSGL_STATUS, "Cache fill: %5.2f%% " - "(%" PRId64 " bytes)", 100.0 * info.fill / s->buffer_size, - info.fill); - if (info.fill >= min) - break; - if (info.idle) - break; // file is smaller than prefill size - // Wake up if the cache is done reading some data (or on timeout/abort) - pthread_mutex_lock(&s->mutex); - s->control = CACHE_CTRL_PING; - pthread_cond_signal(&s->wakeup); - cache_wakeup_and_wait(s, &(double){0}); - pthread_mutex_unlock(&s->mutex); - } - return 1; -} diff --git a/stream/cache_file.c b/stream/cache_file.c deleted file mode 100644 index 4cf7060e83..0000000000 --- a/stream/cache_file.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdint.h> - -#include "osdep/io.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 = stream_get_size(s); - if (p->size >= 0 && 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 - if (p->size >= 0) - 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; - - if (!stream->seekable) { - MP_ERR(cache, "can't cache unseekable stream\n"); - return -1; - } - - bool use_anon_file = strcmp(opts->file, "TMP") == 0; - FILE *file = use_anon_file ? tmpfile() : 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 072672d1ed..7d093e1913 100644 --- a/stream/stream.c +++ b/stream/stream.c @@ -229,7 +229,6 @@ static int open_internal(const stream_info_t *sinfo, const char *url, int flags, s->global = global; s->url = talloc_strdup(s, url); s->path = talloc_strdup(s, path); - s->allow_caching = true; s->is_network = sinfo->is_network; s->mode = flags & (STREAM_READ | STREAM_WRITE); @@ -256,9 +255,6 @@ static int open_internal(const stream_info_t *sinfo, const char *url, int flags, if (!s->read_chunk) s->read_chunk = 4 * (s->sector_size ? s->sector_size : STREAM_BUFFER_SIZE); - if (!s->fill_buffer) - s->allow_caching = false; - assert(s->seekable == !!s->seek); if (s->mime_type) @@ -581,7 +577,6 @@ void free_stream(stream_t *s) if (s->close) s->close(s); - free_stream(s->underlying); talloc_free(s); } @@ -597,98 +592,6 @@ 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->underlying = orig; - cache->caching = true; - 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->streaming = orig->streaming, - cache->is_network = orig->is_network; - cache->is_local_file = orig->is_local_file; - cache->is_directory = orig->is_directory; - cache->cancel = orig->cancel; - 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) -{ - struct mp_cache_opts use_opts = *opts; - if (use_opts.size == -1) - use_opts.size = stream->streaming ? use_opts.def_size : 0; - if (use_opts.size == -2) - use_opts.size = use_opts.def_size; - - if (stream->mode != STREAM_READ || !stream->allow_caching || use_opts.size < 1) - use_opts.size = 0; - return use_opts; -} - -bool stream_wants_cache(stream_t *stream, struct mp_cache_opts *opts) -{ - struct mp_cache_opts use_opts = check_cache_opts(stream, opts); - return use_opts.size > 0; -} - -// return 1 on success, 0 if the cache is disabled/not needed, and -1 on error -// or if the cache is disabled -static int stream_enable_cache(stream_t **stream, struct mp_cache_opts *opts) -{ - stream_t *orig = *stream; - struct mp_cache_opts use_opts = check_cache_opts(*stream, opts); - - if (use_opts.size < 1) - return 0; - - stream_t *fcache = open_cache(orig, "file-cache"); - if (stream_file_cache_init(fcache, orig, &use_opts) <= 0) { - fcache->underlying = NULL; // don't free original stream - free_stream(fcache); - fcache = orig; - } - - stream_t *cache = open_cache(fcache, "cache"); - - int res = stream_cache_init(cache, fcache, &use_opts); - if (res <= 0) { - cache->underlying = NULL; // don't free original stream - free_stream(cache); - if (fcache != orig) { - fcache->underlying = NULL; - free_stream(fcache); - } - } else { - *stream = cache; - } - return res; -} - -// Do some crazy stuff to call stream_enable_cache() with the global options. -int stream_enable_cache_defaults(stream_t **stream) -{ - struct mpv_global *global = (*stream)->global; - if (!global) - return 0; - void *tmp = talloc_new(NULL); - struct mp_cache_opts *opts = - mp_get_config_group(tmp, global, &stream_cache_conf); - int r = stream_enable_cache(stream, opts); - talloc_free(tmp); - return r; -} - static uint16_t stream_read_word_endian(stream_t *s, bool big_endian) { unsigned int y = stream_read_char(s); diff --git a/stream/stream.h b/stream/stream.h index 2f59e5318f..c91843de8a 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -52,11 +52,6 @@ enum stream_ctrl { STREAM_CTRL_GET_SIZE = 1, - // Cache - STREAM_CTRL_GET_CACHE_INFO, - STREAM_CTRL_SET_CACHE_SIZE, - STREAM_CTRL_SET_READAHEAD, - // stream_memory.c STREAM_CTRL_SET_CONTENTS, @@ -104,14 +99,6 @@ enum stream_ctrl { STREAM_CTRL_SET_CURRENT_TITLE, }; -// for STREAM_CTRL_GET_CACHE_INFO -struct stream_cache_info { - int64_t size; - int64_t fill; - bool idle; - int64_t speed; -}; - struct stream_lang_req { int type; // STREAM_AUDIO, STREAM_SUB int id; @@ -179,8 +166,6 @@ typedef struct stream { bool seekable : 1; // presence of general byte seeking support bool fast_skip : 1; // consider stream fast enough to fw-seek by skipping bool is_network : 1; // original stream_info_t.is_network flag - bool allow_caching : 1; // stream cache makes sense - bool caching : 1; // is a cache, or accesses a cache bool is_local_file : 1; // from the filesystem bool is_directory : 1; // directory on the filesystem bool access_references : 1; // open other streams @@ -190,24 +175,12 @@ typedef struct stream { struct mp_cancel *cancel; // cancellation notification - struct stream *underlying; // e.g. cache wrapper - // Includes additional padding in case sizes get rounded up by sector size. unsigned char buffer[]; } stream_t; int stream_fill_buffer(stream_t *s); -struct mp_cache_opts; -bool stream_wants_cache(stream_t *stream, struct mp_cache_opts *opts); -int stream_enable_cache_defaults(stream_t **stream); - -// 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); inline static int stream_read_char(stream_t *s) diff --git a/stream/stream_avdevice.c b/stream/stream_avdevice.c index 2b132cd1a9..5185b7a844 100644 --- a/stream/stream_avdevice.c +++ b/stream/stream_avdevice.c @@ -22,7 +22,6 @@ static int open_f(stream_t *stream) { stream->demuxer = "lavf"; - stream->allow_caching = false; return STREAM_OK; } diff --git a/stream/stream_dvb.c b/stream/stream_dvb.c index 9821119cac..fa58f633ec 100644 --- a/stream/stream_dvb.c +++ b/stream/stream_dvb.c @@ -1117,7 +1117,6 @@ static int dvb_open(stream_t *stream) stream->close = dvbin_close; stream->control = dvbin_stream_control; stream->streaming = true; - stream->allow_caching = true; stream->demuxer = "lavf"; stream->lavf_type = "mpegts"; stream->extended_ctrls = true; diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c index 16a3d11d70..55ce771896 100644 --- a/stream/stream_dvdnav.c +++ b/stream/stream_dvdnav.c @@ -520,7 +520,6 @@ static int open_s_internal(stream_t *stream) stream->close = stream_dvdnav_close; stream->demuxer = "+disc"; stream->lavf_type = "mpeg"; - stream->allow_caching = false; return STREAM_OK; } diff --git a/stream/stream_edl.c b/stream/stream_edl.c index 11c149b3ab..94bbe58c88 100644 --- a/stream/stream_edl.c +++ b/stream/stream_edl.c @@ -6,7 +6,6 @@ static int s_open (struct stream *stream) { stream->demuxer = "edl"; - stream->allow_caching = false; return STREAM_OK; } diff --git a/stream/stream_file.c b/stream/stream_file.c index 20b4d7c0c3..319a92f08b 100644 --- a/stream/stream_file.c +++ b/stream/stream_file.c @@ -326,7 +326,6 @@ static int open_f(stream_t *stream) if (fstat(p->fd, &st) == 0) { if (S_ISDIR(st.st_mode)) { stream->is_directory = true; - stream->allow_caching = false; MP_INFO(stream, "This is a directory - adding to playlist.\n"); } else if (S_ISREG(st.st_mode)) { p->regular_file = true; diff --git a/stream/stream_memory.c b/stream/stream_memory.c index e0d01ff2d8..8df043201e 100644 --- a/stream/stream_memory.c +++ b/stream/stream_memory.c @@ -62,7 +62,6 @@ static int open_f(stream_t *stream) stream->seekable = true; stream->control = control; stream->read_chunk = 1024 * 1024; - stream->allow_caching = false; struct priv *p = talloc_zero(stream, struct priv); stream->priv = p; diff --git a/stream/stream_mf.c b/stream/stream_mf.c index fbee82924a..69a6dce58d 100644 --- a/stream/stream_mf.c +++ b/stream/stream_mf.c @@ -31,7 +31,6 @@ static int mf_stream_open (stream_t *stream) { stream->demuxer = "mf"; - stream->allow_caching = false; return STREAM_OK; } diff --git a/stream/stream_tv.c b/stream/stream_tv.c index d9acbe4cf4..bb3c48f269 100644 --- a/stream/stream_tv.c +++ b/stream/stream_tv.c @@ -41,7 +41,6 @@ tv_stream_open (stream_t *stream) stream->close=tv_stream_close; stream->demuxer = "tv"; - stream->allow_caching = false; return STREAM_OK; } diff --git a/wscript_build.py b/wscript_build.py index e37eb7e775..066ede1274 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -357,8 +357,6 @@ def build(ctx): ( "stream/ai_oss.c", "oss-audio && audio-input" ), ( "stream/ai_sndio.c", "sndio && audio-input" ), ( "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" ), |