summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-10-11 19:34:04 +0200
committerwm4 <wm4@nowhere>2019-10-11 20:01:51 +0200
commitf26dfb6e4d4ecc40c157382f6fcc5a1167334afc (patch)
treed4fba9b1e256c2008551e3d731576f550107143f
parentc84ec021287d72d122071d5c211f9bfbeb13a3fe (diff)
downloadmpv-f26dfb6e4d4ecc40c157382f6fcc5a1167334afc.tar.bz2
mpv-f26dfb6e4d4ecc40c157382f6fcc5a1167334afc.tar.xz
player: partially rework --cache-pause
The --cache-pause feature (enabled by default) will pause playback for a while if network runs out of data. If this is not done, then playback will go on frame-wise (as packets are slowly read from the network and then instantly decoded and displayed). This feature is actually useless, as you won't get nice playback no matter what if network is too slow, but I guess I still prefer this behavior for some reason. This commit changes this behavior from using the demuxer cache state only, to trying to use underrun information from the AO/VO. This means if you have a very large audio buffer, then cache-pausing will trigger once that buffer is depleted, which will be some time _after_ the demuxer cache has run out. This requires explicit support from the AO. Otherwise, the behavior should be mostly the same as before this commit. This does not care about the AO buffer. In theory, the AO may underrun, then the player will write some data to the AO buffer, then the AO will recover and play this bit of data, then the player will probably trigger the cache-pause behavior. The probability of this happening should be pretty low, so I will hold off fixing this until the next refactor of the AO chain (if ever). The VO underflow detection was devised and tested in 5 minutes, and may not be correct. At least I'm fairly sure that the combination of all the factors should make incorrect behavior relatively unlikely, but problems are possible. Also, the demux_reader_state.underrun field may be inaccurate. It's only the present state at the time demux_get_reader_state() was called, and may exclude past underruns. In theory, this could cause "close" cases to be missed. Then you might get an audio underrun without cache-pausing acting on it. If the stars align, this could happen multiple times in the row, effectively making this feature not work. The most user-visible consequence of this change is that the user will now see an AO underrun warning every time the cache runs out. Maybe this cache-pause feature should just be removed...
-rw-r--r--player/audio.c10
-rw-r--r--player/core.h5
-rw-r--r--player/playloop.c56
-rw-r--r--player/video.c8
4 files changed, 71 insertions, 8 deletions
diff --git a/player/audio.c b/player/audio.c
index 20f80256b2..eb6081e26d 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -181,6 +181,7 @@ static void ao_chain_reset_state(struct ao_chain *ao_c)
ao_c->last_out_pts = MP_NOPTS_VALUE;
TA_FREEP(&ao_c->output_frame);
ao_c->out_eof = false;
+ ao_c->underrun = false;
mp_audio_buffer_clear(ao_c->ao_buffer);
}
@@ -855,7 +856,14 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
int playsize = ao_get_space(mpctx->ao);
- ao_query_and_reset_events(mpctx->ao, AO_EVENT_UNDERRUN);
+ if (ao_query_and_reset_events(mpctx->ao, AO_EVENT_UNDERRUN))
+ ao_c->underrun = true;
+
+ // Stop feeding data if an underrun happened. Something else needs to
+ // "unblock" audio after underrun. handle_update_cache() does this and can
+ // take the network state into account.
+ if (ao_c->underrun)
+ return;
int skip = 0;
bool sync_known = get_sync_samples(mpctx, &skip);
diff --git a/player/core.h b/player/core.h
index 11e741bb78..0edf4fa31e 100644
--- a/player/core.h
+++ b/player/core.h
@@ -183,6 +183,8 @@ struct vo_chain {
bool is_coverart;
// - video consists of sparse still images
bool is_sparse;
+
+ bool underrun;
};
// Like vo_chain, for audio.
@@ -206,6 +208,8 @@ struct ao_chain {
struct track *track;
struct mp_pin *filter_src;
struct mp_pin *dec_src;
+
+ bool underrun;
};
/* Note that playback can be paused, stopped, etc. at any time. While paused,
@@ -423,6 +427,7 @@ typedef struct MPContext {
bool playing_msg_shown;
bool paused_for_cache;
+ bool demux_underrun;
double cache_stop_time;
int cache_buffer;
diff --git a/player/playloop.c b/player/playloop.c
index e0d0056d7d..99069401de 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -660,18 +660,36 @@ static void handle_osd_redraw(struct MPContext *mpctx)
vo_redraw(mpctx->video_out);
}
+static void clear_underruns(struct MPContext *mpctx)
+{
+ if (mpctx->ao_chain && mpctx->ao_chain->underrun) {
+ mpctx->ao_chain->underrun = false;
+ mp_wakeup_core(mpctx);
+ }
+
+ if (mpctx->vo_chain && mpctx->vo_chain->underrun) {
+ mpctx->vo_chain->underrun = false;
+ mp_wakeup_core(mpctx);
+ }
+}
+
static void handle_update_cache(struct MPContext *mpctx)
{
bool force_update = false;
struct MPOpts *opts = mpctx->opts;
- if (!mpctx->demuxer)
+
+ if (!mpctx->demuxer) {
+ clear_underruns(mpctx);
return;
+ }
double now = mp_time_sec();
struct demux_reader_state s;
demux_get_reader_state(mpctx->demuxer, &s);
+ mpctx->demux_underrun |= s.underrun;
+
int cache_buffer = 100;
bool use_pause_on_low_cache = demux_is_network_cached(mpctx->demuxer) &&
opts->cache_pause && mpctx->play_dir > 0;
@@ -690,17 +708,43 @@ static void handle_update_cache(struct MPContext *mpctx)
// Enter buffering state only if there actually was an underrun (or if
// initial caching before playback restart is used).
- if (is_low && !mpctx->paused_for_cache && mpctx->restart_complete)
- is_low = s.underrun;
+ bool need_wait = is_low;
+ if (is_low && !mpctx->paused_for_cache && mpctx->restart_complete) {
+ // Wait only if an output underrun was registered. (Or if there is no
+ // underrun detection.)
+ bool output_underrun = false;
+
+ if (mpctx->ao_chain) {
+ output_underrun |=
+ !(mpctx->ao && ao_get_reports_underruns(mpctx->ao)) ||
+ mpctx->ao_chain->underrun;
+ }
+ if (mpctx->vo_chain)
+ output_underrun |= mpctx->vo_chain->underrun;
+
+ // Output underruns could be sporadic (unrelated to demuxer buffer state
+ // and for example caused by slow decoding), so use a past demuxer
+ // underrun as indication that the underrun was possibly due to a
+ // demuxer underrun.
+ need_wait = mpctx->demux_underrun && output_underrun;
+ }
+
+ // Let the underrun flag "stick" around until the cache has fully recovered.
+ // See logic where demux_underrun is used.
+ if (!is_low)
+ mpctx->demux_underrun = false;
- if (mpctx->paused_for_cache != is_low) {
- mpctx->paused_for_cache = is_low;
+ if (mpctx->paused_for_cache != need_wait) {
+ mpctx->paused_for_cache = need_wait;
update_internal_pause_state(mpctx);
force_update = true;
- if (is_low)
+ if (mpctx->paused_for_cache)
mpctx->cache_stop_time = now;
}
+ if (!mpctx->paused_for_cache)
+ clear_underruns(mpctx);
+
if (mpctx->paused_for_cache) {
cache_buffer =
100 * MPCLAMP(s.ts_duration / opts->cache_pause_wait, 0, 0.99);
diff --git a/player/video.c b/player/video.c
index 1d800a96d3..680636b075 100644
--- a/player/video.c
+++ b/player/video.c
@@ -91,6 +91,7 @@ int reinit_video_filters(struct MPContext *mpctx)
static void vo_chain_reset_state(struct vo_chain *vo_c)
{
vo_seek_reset(vo_c->vo);
+ vo_c->underrun = false;
}
void reset_video_state(struct MPContext *mpctx)
@@ -1003,8 +1004,13 @@ void write_video(struct MPContext *mpctx)
if (r < 0)
goto error;
- if (r == VD_WAIT) // Demuxer will wake us up for more packets to decode.
+ if (r == VD_WAIT) {
+ // Heuristic to detect underruns.
+ if (mpctx->video_status == STATUS_PLAYING && !vo_still_displaying(vo))
+ vo_c->underrun = true;
+ // Demuxer will wake us up for more packets to decode.
return;
+ }
if (r == VD_EOF) {
if (check_for_hwdec_fallback(mpctx))