mirror of
https://github.com/mpv-player/mpv
synced 2025-02-16 12:17:12 +00:00
demux: add --demuxer-hysteresis-secs option to save power with caching
Buffering ahead nonstop into the cache results in nonstop disk or network activity to read stream data from wherever it may originate. Currently, there's no way to configure the demuxer to back off once it's buffered ahead enough data, since the cache limit will be perpetually not-reached as a stream continues to play, until the entire stream is eventually buffered. On a laptop with an i9-12900H with decoding performed by the iGPU, watching a locally-saved 1080p video which hasn't been buffered into the page cache consumes approximately 15 W even with caching enabled. When configuring a hysteresis to make the demuxer back off, power consumption drops to 9 W when watching the same video, resulting in a whopping 6 W of power savings. To make it possible to attain significant power savings via caching, add a --demuxer-hysteresis-secs option to configure a hysteresis to make the demuxer back off until there's only the configured number of seconds remaining in the cache from the current playback position. This feature is disabled by default.
This commit is contained in:
parent
b6c9846112
commit
eb29aa4839
@ -39,6 +39,7 @@ Interface changes
|
||||
- deprecate `--vo-sixel-exit-clear` and alias it to
|
||||
`--vo-sixel-alt-screen`
|
||||
- deprecate `--drm-atomic`
|
||||
- add `--demuxer-hysteresis-secs`
|
||||
--- mpv 0.35.0 ---
|
||||
- add the `--vo=gpu-next` video output driver, as well as the options
|
||||
`--allow-delayed-peak-detect`, `--builtin-scalers`,
|
||||
|
@ -3819,6 +3819,33 @@ Demuxer
|
||||
(This value tends to be fuzzy, because many file formats don't store linear
|
||||
timestamps.)
|
||||
|
||||
``--demuxer-hysteresis-secs=<seconds>``
|
||||
Once the ``--demuxer-max-bytes`` limit is reached, this value can be used
|
||||
to specify a hysteresis before the demuxer will buffer ahead again. This
|
||||
specifies the maximum number of seconds from the current playback position
|
||||
that needs to be remaining in the cache before the demuxer will continue
|
||||
buffering ahead.
|
||||
|
||||
For example, with a value of 10 seconds specified, the demuxer will buffer
|
||||
ahead up to ``--demuxer-max-bytes`` and won't start buffering ahead again
|
||||
until there is only 10 seconds of content left in the cache. When the
|
||||
demuxer starts buffering ahead again, it will buffer ahead up to
|
||||
``--demuxer-max-bytes`` and stop until there's only 10 seconds of content
|
||||
remaining in the cache, and so on.
|
||||
|
||||
This can provide significant power savings and reduce load by making the
|
||||
demuxer only buffer ahead in chunks at a time rather than buffering ahead
|
||||
nonstop to keep the cache filled.
|
||||
|
||||
If you want to save power and reduce load, configure this to a small number
|
||||
that's much lower than ``--cache-secs`` or ``--demuxer-readahead-secs``.
|
||||
If it takes a long time to buffer anything at all for a given stream (like
|
||||
when reading from a very slow disk is involved), then the hysteresis value
|
||||
should be larger to compensate.
|
||||
|
||||
The default value is 0 seconds, which disables the caching hysteresis. A
|
||||
value of 10 seconds probably works well for most usecases.
|
||||
|
||||
``--prefetch-playlist=<yes|no>``
|
||||
Prefetch next playlist entry while playback of the current entry is ending
|
||||
(default: no).
|
||||
|
@ -91,6 +91,7 @@ struct demux_opts {
|
||||
int64_t max_bytes_bw;
|
||||
int donate_fw;
|
||||
double min_secs;
|
||||
double hyst_secs;
|
||||
int force_seekable;
|
||||
double min_secs_cache;
|
||||
int access_references;
|
||||
@ -115,6 +116,7 @@ const struct m_sub_options demux_conf = {
|
||||
{"no", 0}, {"auto", -1}, {"yes", 1})},
|
||||
{"cache-on-disk", OPT_FLAG(disk_cache)},
|
||||
{"demuxer-readahead-secs", OPT_DOUBLE(min_secs), M_RANGE(0, DBL_MAX)},
|
||||
{"demuxer-hysteresis-secs", OPT_DOUBLE(hyst_secs), M_RANGE(0, DBL_MAX)},
|
||||
{"demuxer-max-bytes", OPT_BYTE_SIZE(max_bytes),
|
||||
M_RANGE(0, M_MAX_MEM_BYTES)},
|
||||
{"demuxer-max-back-bytes", OPT_BYTE_SIZE(max_bytes_bw),
|
||||
@ -210,6 +212,8 @@ struct demux_internal {
|
||||
bool warned_queue_overflow;
|
||||
bool eof; // whether we're in EOF state
|
||||
double min_secs;
|
||||
double hyst_secs; // stop reading till there's hyst_secs remaining
|
||||
bool hyst_active;
|
||||
size_t max_bytes;
|
||||
size_t max_bytes_bw;
|
||||
bool seekable_cache;
|
||||
@ -2205,8 +2209,12 @@ static bool read_packet(struct demux_internal *in)
|
||||
if (ds->eager && ds->queue->last_ts != MP_NOPTS_VALUE &&
|
||||
in->min_secs > 0 && ds->base_ts != MP_NOPTS_VALUE &&
|
||||
ds->queue->last_ts >= ds->base_ts &&
|
||||
!in->back_demuxing)
|
||||
prefetch_more |= ds->queue->last_ts - ds->base_ts < in->min_secs;
|
||||
!in->back_demuxing) {
|
||||
if (ds->queue->last_ts - ds->base_ts <= in->hyst_secs)
|
||||
in->hyst_active = false;
|
||||
if (!in->hyst_active)
|
||||
prefetch_more |= ds->queue->last_ts - ds->base_ts < in->min_secs;
|
||||
}
|
||||
total_fw_bytes += get_foward_buffered_bytes(ds);
|
||||
}
|
||||
|
||||
@ -2214,8 +2222,10 @@ static bool read_packet(struct demux_internal *in)
|
||||
(size_t)total_fw_bytes, read_more, prefetch_more, refresh_more);
|
||||
if (total_fw_bytes >= in->max_bytes) {
|
||||
// if we hit the limit just by prefetching, simply stop prefetching
|
||||
if (!read_more)
|
||||
if (!read_more) {
|
||||
in->hyst_active = !!in->hyst_secs;
|
||||
return false;
|
||||
}
|
||||
if (!in->warned_queue_overflow) {
|
||||
in->warned_queue_overflow = true;
|
||||
MP_WARN(in, "Too many packets in the demuxer packet queues:\n");
|
||||
@ -2466,6 +2476,7 @@ static void update_opts(struct demux_internal *in)
|
||||
struct demux_opts *opts = in->opts;
|
||||
|
||||
in->min_secs = opts->min_secs;
|
||||
in->hyst_secs = opts->hyst_secs;
|
||||
in->max_bytes = opts->max_bytes;
|
||||
in->max_bytes_bw = opts->max_bytes_bw;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user