|author||wm4 <wm4@nowhere>||2018-04-12 18:47:25 +0200|
|committer||Jan Ekström <firstname.lastname@example.org>||2018-04-15 23:11:33 +0300|
audio: don't recreate AO if a filter changes the output format
Until recently, the AO was reinitialized strictly only on decoder format changes. But the commit for simplifying audio format negotiation removed this. Now the AO is recreated for any format change. This is sort of annoying if you change playback speed. The insertion/removal of af_scaletempo can change the sample format. For example, the acompressor filter will convert output to double, so toggling scaletempo will force the format back to float. This recreates the AO under the --gapless-audio=weak default. This likely affects a lot of other filters too. Work this around by allowing sample format changes, and keeping the current AO format in these cases. This is probably not a big problem. Most audio APIs force the output format to float anyway. This means you actually have to worry about what the default gapless mode does to your audio. If you start with a file that uses 8 bit per sample, and then continue playing a 24 bit FLAC, it will be converted down to 8 bit per sample. (Assuming they are played in a way that uses the gapless logic.)
2 files changed, 32 insertions, 4 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index a5d146690a..2776bf7b10 100644
@@ -1541,9 +1541,10 @@ Audio
changes, the audio device is closed and reopened. This means that
you will normally get gapless audio with files that were encoded
using the same settings, but might not be gapless in other cases.
- (Unlike with ``yes``, you don't have to worry about corner cases
- like the first file setting a very low quality output format, and
- ruining the playback of higher quality files that follow.)
+ The exact conditions under which the audio device is kept open is
+ an implementation detail, and can change from version to version.
+ Currently, the device is kept even if the sample format changes,
+ but the sample formats are convertible.
diff --git a/player/audio.c b/player/audio.c
index a6b7601df2..842d965d49 100644
@@ -255,6 +255,33 @@ static char *audio_config_to_str_buf(char *buf, size_t buf_sz, int rate,
+// Decide whether on a format change, we should reinit the AO.
+static bool keep_weak_gapless_format(struct mp_aframe *old, struct mp_aframe* new)
+ bool res = false;
+ struct mp_aframe *new_mod = mp_aframe_new_ref(new);
+ if (!new_mod)
+ // If the sample formats are compatible (== libswresample generally can
+ // convert them), keep the AO. On other changes, recreate it.
+ int old_fmt = mp_aframe_get_format(old);
+ int new_fmt = mp_aframe_get_format(new);
+ if (af_format_conversion_score(old_fmt, new_fmt) == INT_MIN)
+ goto done; // completely incompatible formats
+ if (!mp_aframe_set_format(new_mod, old_fmt))
+ goto done;
+ res = mp_aframe_config_equals(old, new_mod);
+ return res;
static void reinit_audio_filters_and_output(struct MPContext *mpctx)
struct MPOpts *opts = mpctx->opts;
@@ -290,7 +317,7 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
// previous one, keep the AO and don't reinit anything.
// Strong gapless: always keep the AO
if ((mpctx->ao_filter_fmt && mpctx->ao && opts->gapless_audio < 0 &&
- mp_aframe_config_equals(mpctx->ao_filter_fmt, out_fmt)) ||
+ keep_weak_gapless_format(mpctx->ao_filter_fmt, out_fmt)) ||
(mpctx->ao && opts->gapless_audio > 0))