1
0
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:
Sultan Alsawaf 2022-12-28 22:01:07 -08:00 committed by sfan5
parent b6c9846112
commit eb29aa4839
3 changed files with 42 additions and 3 deletions

View File

@ -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`,

View File

@ -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).

View File

@ -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;