mirror of https://github.com/mpv-player/mpv
player: redo how stream caching and pausing on low cache works
Add the --cache-secs option, which literally overrides the value of --demuxer-readahead-secs if the stream cache is active. The default value is very high (10 seconds), which means it can act as network cache. Remove the old behavior of trying to pause once the byte cache runs low. Instead, do something similar wit the demuxer cache. The nice thing is that we can guess how many seconds of video it has cached, and we can make better decisions. But for now, apply a relatively naive heuristic: if the cache is below 0.5 secs, pause, and wait until at least 2 secs are available. Note that due to timestamp reordering, the estimated cached duration of video might be inaccurate, depending on the file format. If the file format has DTS, it's easy, otherwise the duration will seemingly jump back and forth.
This commit is contained in:
parent
a8513f8b37
commit
0b428e4482
|
@ -2751,20 +2751,6 @@ Cache
|
|||
will not automatically enable the cache e.g. when playing from a network
|
||||
stream. Note that using ``--cache`` will always override this option.
|
||||
|
||||
``--cache-pause-below=<kBytes|no>``
|
||||
If the cache size goes below the specified value (in KB), pause and wait
|
||||
until the size set by ``--cache-pause-restart`` is reached, then resume
|
||||
playback (default: 50). If ``no`` is specified, this behavior is disabled.
|
||||
|
||||
When the player is paused this way, the status line shows ``Buffering``
|
||||
instead of ``Paused``, and the OSD uses a clock symbol instead of the
|
||||
normal paused symbol.
|
||||
|
||||
``--cache-pause-restart=<kBytes>``
|
||||
If the cache is paused due to the ``--cache-pause-below`` functionality,
|
||||
then the player unpauses as soon as the cache has this much data (in KB).
|
||||
(Default: 100)
|
||||
|
||||
``--cache-initial=<kBytes>``
|
||||
Playback will start when the cache has been filled up with this many
|
||||
kilobytes of data (default: 0).
|
||||
|
@ -2809,6 +2795,15 @@ Cache
|
|||
``--no-cache``
|
||||
Turn off input stream caching. See ``--cache``.
|
||||
|
||||
``--cache-secs=<seconds>``
|
||||
How many seconds of audio/video to prefetch if the cache is active. This
|
||||
overrides the ``--demuxer-readahead-secs`` option if and only if the cache
|
||||
is enabled. (Default: 10.)
|
||||
|
||||
``--cache-pause``, ``--no-cache-pause``
|
||||
Whether the player should automatically pause when the cache runs low,
|
||||
and unpause once more data is available ("buffering").
|
||||
|
||||
|
||||
Network
|
||||
-------
|
||||
|
|
|
@ -109,6 +109,7 @@ struct demux_internal {
|
|||
bool warned_queue_overflow;
|
||||
bool last_eof; // last actual global EOF status
|
||||
bool eof; // whether we're in EOF state (reset for retry)
|
||||
bool idle;
|
||||
bool autoselect;
|
||||
double min_secs;
|
||||
int min_packs;
|
||||
|
@ -144,6 +145,10 @@ struct demux_stream {
|
|||
struct demux_packet *tail;
|
||||
};
|
||||
|
||||
// If one of the values is NOPTS, always pick the other one.
|
||||
#define MP_PTS_MIN(a, b) ((a) == MP_NOPTS_VALUE || ((a) > (b)) ? (b) : (a))
|
||||
#define MP_PTS_MAX(a, b) ((a) == MP_NOPTS_VALUE || ((a) < (b)) ? (b) : (a))
|
||||
|
||||
static void demuxer_sort_chapters(demuxer_t *demuxer);
|
||||
static void *demux_thread(void *pctx);
|
||||
static void update_cache(struct demux_internal *in);
|
||||
|
@ -329,6 +334,7 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
|||
static bool read_packet(struct demux_internal *in)
|
||||
{
|
||||
in->eof = false;
|
||||
in->idle = true;
|
||||
|
||||
// Check if we need to read a new packet. We do this if all queues are below
|
||||
// the minimum, or if a stream explicitly needs new packets. Also includes
|
||||
|
@ -373,6 +379,7 @@ static bool read_packet(struct demux_internal *in)
|
|||
|
||||
// Actually read a packet. Drop the lock while doing so, because waiting
|
||||
// for disk or network I/O can take time.
|
||||
in->idle = false;
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
struct demuxer *demux = in->d_thread;
|
||||
bool eof = !demux->desc->fill_buffer || demux->desc->fill_buffer(demux) <= 0;
|
||||
|
@ -808,7 +815,8 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
|||
.d_thread = talloc(demuxer, struct demuxer),
|
||||
.d_buffer = talloc(demuxer, struct demuxer),
|
||||
.d_user = demuxer,
|
||||
.min_secs = demuxer->opts->demuxer_min_secs,
|
||||
.min_secs = stream->uncached_stream ? demuxer->opts->demuxer_min_secs_cache
|
||||
: demuxer->opts->demuxer_min_secs,
|
||||
.min_packs = demuxer->opts->demuxer_min_packs,
|
||||
.min_bytes = demuxer->opts->demuxer_min_bytes,
|
||||
};
|
||||
|
@ -1162,6 +1170,24 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
|
|||
in->tracks_switched = true;
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
return DEMUXER_CTRL_OK;
|
||||
case DEMUXER_CTRL_GET_READER_STATE: {
|
||||
struct demux_ctrl_reader_state *r = arg;
|
||||
*r = (struct demux_ctrl_reader_state){
|
||||
.eof = in->last_eof,
|
||||
.idle = in->idle,
|
||||
.ts_range = {MP_NOPTS_VALUE, MP_NOPTS_VALUE},
|
||||
};
|
||||
for (int n = 0; n < in->d_user->num_streams; n++) {
|
||||
struct demux_stream *ds = in->d_user->streams[n]->ds;
|
||||
if (ds->active) {
|
||||
r->underrun |= !ds->head;
|
||||
r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
|
||||
r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
|
||||
}
|
||||
}
|
||||
r->idle &= !r->underrun;
|
||||
return DEMUXER_CTRL_OK;
|
||||
}
|
||||
}
|
||||
return DEMUXER_CTRL_DONTKNOW;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,12 @@ enum demux_ctrl {
|
|||
DEMUXER_CTRL_RESYNC,
|
||||
DEMUXER_CTRL_IDENTIFY_PROGRAM,
|
||||
DEMUXER_CTRL_STREAM_CTRL,
|
||||
DEMUXER_CTRL_GET_READER_STATE,
|
||||
};
|
||||
|
||||
struct demux_ctrl_reader_state {
|
||||
bool eof, underrun, idle;
|
||||
double ts_range[2]; // start, end
|
||||
};
|
||||
|
||||
struct demux_ctrl_stream_ctrl {
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
#
|
||||
# Disable the behavior that the player will pause if the cache goes below a
|
||||
# certain fill size.
|
||||
#cache-pause-below=no
|
||||
#cache-pause=no
|
||||
#
|
||||
# Read ahead about 5 seconds of audio and video packets.
|
||||
#demuxer-readahead-secs=5.0
|
||||
|
|
|
@ -974,5 +974,7 @@ static const char *const replaced_opts =
|
|||
"|status-msg#--term-status-msg"
|
||||
"|idx#--index"
|
||||
"|forceidx#--index"
|
||||
"|cache-pause-below#for 'no', use --no-cache-pause"
|
||||
"|no-cache-pause-below#--no-cache-pause"
|
||||
"|"
|
||||
;
|
||||
|
|
|
@ -144,9 +144,6 @@ const m_option_t mp_opts[] = {
|
|||
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),
|
||||
|
||||
#if HAVE_DVDREAD || HAVE_DVDNAV
|
||||
OPT_STRING("dvd-device", dvd_device, 0),
|
||||
|
@ -220,6 +217,9 @@ const m_option_t mp_opts[] = {
|
|||
OPT_INTRANGE("demuxer-readahead-packets", demuxer_min_packs, 0, 0, MAX_PACKS),
|
||||
OPT_INTRANGE("demuxer-readahead-bytes", demuxer_min_bytes, 0, 0, MAX_PACK_BYTES),
|
||||
|
||||
OPT_DOUBLE("cache-secs", demuxer_min_secs_cache, M_OPT_MIN, .min = 0),
|
||||
OPT_FLAG("cache-pause", cache_pausing, 0),
|
||||
|
||||
OPT_DOUBLE("mf-fps", mf_fps, 0),
|
||||
OPT_STRING("mf-type", mf_type, 0),
|
||||
#if HAVE_TV
|
||||
|
@ -598,13 +598,13 @@ const struct MPOpts mp_default_opts = {
|
|||
.seek_min = 500,
|
||||
.file_max = 1024 * 1024,
|
||||
},
|
||||
.stream_cache_pause = 50,
|
||||
.stream_cache_unpause = 100,
|
||||
.demuxer_thread = 1,
|
||||
.demuxer_min_packs = 0,
|
||||
.demuxer_min_bytes = 0,
|
||||
.demuxer_min_secs = 0.2,
|
||||
.network_rtsp_transport = 2,
|
||||
.demuxer_min_secs_cache = 10,
|
||||
.cache_pausing = 1,
|
||||
.chapterrange = {-1, -1},
|
||||
.edition_id = -1,
|
||||
.default_max_pts_correction = -1,
|
||||
|
|
|
@ -123,8 +123,6 @@ typedef struct MPOpts {
|
|||
int use_filedir_conf;
|
||||
int network_rtsp_transport;
|
||||
struct mp_cache_opts stream_cache;
|
||||
int stream_cache_pause;
|
||||
int stream_cache_unpause;
|
||||
int chapterrange[2];
|
||||
int edition_id;
|
||||
int correct_pts;
|
||||
|
@ -189,6 +187,9 @@ typedef struct MPOpts {
|
|||
char *sub_demuxer_name;
|
||||
int mkv_subtitle_preroll;
|
||||
|
||||
double demuxer_min_secs_cache;
|
||||
int cache_pausing;
|
||||
|
||||
struct image_writer_opts *screenshot_image_opts;
|
||||
char *screenshot_template;
|
||||
|
||||
|
|
|
@ -520,32 +520,44 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx)
|
|||
struct MPOpts *opts = mpctx->opts;
|
||||
if (!mpctx->demuxer)
|
||||
return;
|
||||
int64_t fill = -1;
|
||||
demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_FILL, &fill);
|
||||
int cache_kb = fill > 0 ? (fill + 1023) / 1024 : -1;
|
||||
bool idle = mp_get_cache_idle(mpctx);
|
||||
if (mpctx->paused && mpctx->paused_for_cache) {
|
||||
if (cache_kb < 0 || cache_kb >= opts->stream_cache_unpause || idle) {
|
||||
mpctx->paused_for_cache = false;
|
||||
if (!opts->pause)
|
||||
unpause_player(mpctx);
|
||||
}
|
||||
mpctx->sleeptime = MPMIN(mpctx->sleeptime, 0.2);
|
||||
} else {
|
||||
if (cache_kb >= 0 && cache_kb <= opts->stream_cache_pause && !idle &&
|
||||
opts->stream_cache_pause < opts->stream_cache_unpause)
|
||||
{
|
||||
bool prev_paused_user = opts->pause;
|
||||
pause_player(mpctx);
|
||||
mpctx->paused_for_cache = true;
|
||||
opts->pause = prev_paused_user;
|
||||
|
||||
struct demux_ctrl_reader_state s =
|
||||
{.idle = true, .ts_range = {MP_NOPTS_VALUE, MP_NOPTS_VALUE}};
|
||||
demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_READER_STATE, &s);
|
||||
|
||||
double range = -1;
|
||||
if (s.ts_range[0] != MP_NOPTS_VALUE && s.ts_range[1] != MP_NOPTS_VALUE)
|
||||
range = s.ts_range[1] - s.ts_range[0];
|
||||
if (range < 0)
|
||||
range = 1e20; // unknown/broken timestamps; disable
|
||||
|
||||
if (mpctx->restart_complete) {
|
||||
if (mpctx->paused && mpctx->paused_for_cache) {
|
||||
if (!opts->cache_pausing || range >= 2.0 || s.eof) {
|
||||
mpctx->paused_for_cache = false;
|
||||
if (!opts->pause)
|
||||
unpause_player(mpctx);
|
||||
}
|
||||
mpctx->sleeptime = MPMIN(mpctx->sleeptime, 0.2);
|
||||
} else {
|
||||
if (opts->cache_pausing && range < 0.5 && !s.eof) {
|
||||
bool prev_paused_user = opts->pause;
|
||||
pause_player(mpctx);
|
||||
mpctx->paused_for_cache = true;
|
||||
opts->pause = prev_paused_user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int idle = 1;
|
||||
demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_IDLE, &idle);
|
||||
|
||||
// Also update cache properties.
|
||||
if (cache_kb > 0 || mpctx->next_cache_update > 0) {
|
||||
bool busy = idle == 0 || !s.idle;
|
||||
if (busy || mpctx->next_cache_update > 0) {
|
||||
double now = mp_time_sec();
|
||||
if (mpctx->next_cache_update <= now) {
|
||||
mpctx->next_cache_update = cache_kb > 0 ? now + 0.25 : 0;
|
||||
mpctx->next_cache_update = busy ? now + 0.25 : 0;
|
||||
mp_notify(mpctx, MP_EVENT_CACHE_UPDATE, NULL);
|
||||
}
|
||||
if (mpctx->next_cache_update > 0) {
|
||||
|
|
|
@ -782,6 +782,7 @@ static stream_t *open_cache(stream_t *orig, const char *name)
|
|||
cache->demuxer = talloc_strdup(cache, orig->demuxer);
|
||||
cache->lavf_type = talloc_strdup(cache, orig->lavf_type);
|
||||
cache->safe_origin = orig->safe_origin;
|
||||
cache->streaming = orig->streaming,
|
||||
cache->opts = orig->opts;
|
||||
cache->global = orig->global;
|
||||
|
||||
|
|
Loading…
Reference in New Issue