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.)
This commit is contained in:
wm4 2018-04-12 18:47:25 +02:00 committed by Jan Ekström
parent 9ee9313465
commit 4e7cbb7606
2 changed files with 32 additions and 4 deletions

View File

@ -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.
.. note::

View File

@ -255,6 +255,33 @@ static char *audio_config_to_str_buf(char *buf, size_t buf_sz, int rate,
return buf;
}
// 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)
abort();
// 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);
done:
talloc_free(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))
{
mp_output_chain_set_ao(ao_c->filter, mpctx->ao);