diff --git a/audio/out/ao.c b/audio/out/ao.c index 3f0865af22..ccbcc16e88 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -198,11 +198,7 @@ autoprobe: void ao_uninit(struct ao *ao, bool cut_audio) { - assert(ao->buffer.len >= ao->buffer_playable_size); - ao->buffer.len = ao->buffer_playable_size; ao->driver->uninit(ao, cut_audio); - if (!cut_audio && ao->buffer.len) - mp_msg(MSGT_AO, MSGL_WARN, "Audio output truncated at end.\n"); talloc_free(ao); } @@ -234,8 +230,6 @@ int ao_get_space(struct ao *ao) void ao_reset(struct ao *ao) { - ao->buffer.len = 0; - ao->buffer_playable_size = 0; if (ao->driver->reset) ao->driver->reset(ao); } diff --git a/audio/out/ao.h b/audio/out/ao.h index 3d64102ada..7e7b6968bb 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -73,7 +73,8 @@ struct ao { int bps; // bytes per second double pts; // some mplayer.c state (why is this here?) struct bstr buffer; - int buffer_playable_size; + int buffer_playable_size; // part of the part of the buffer the AO hasn't + // accepted yet with play() bool probing; // if true, don't fail loudly on init bool untimed; bool no_persistent_volume; // the AO does the equivalent of af_volume diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index eb41710b82..4364b9054d 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -299,7 +299,6 @@ static int encode(struct ao *ao, double apts, void *data); static int play(struct ao *ao, void *data, int len, int flags); static void uninit(struct ao *ao, bool cut_audio) { - struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; if (!encode_lavc_start(ectx)) { @@ -307,33 +306,6 @@ static void uninit(struct ao *ao, bool cut_audio) return; } - if (ac->buffer) { - if (ao->buffer.len > 0) { - // TRICK: append aframesize-1 samples to the end, then play() will - // encode all it can - size_t extralen = - (ac->aframesize - 1) * ao->channels.num * ac->sample_size; - void *paddingbuf = talloc_size(ao, ao->buffer.len + extralen); - memcpy(paddingbuf, ao->buffer.start, ao->buffer.len); - fill_with_padding((char *) paddingbuf + ao->buffer.len, - extralen / ac->sample_size, - ac->sample_size, ac->sample_padding); - int written = play(ao, paddingbuf, ao->buffer.len + extralen, 0); - if (written < ao->buffer.len) { - MP_ERR(ao, "did not write enough data at the end\n"); - } - talloc_free(paddingbuf); - ao->buffer.len = 0; - } - - double outpts = ac->expected_next_pts; - if (!ectx->options->rawts && ectx->options->copyts) - outpts += ectx->discontinuity_pts_offset; - outpts += encode_lavc_getoffset(ectx, ac->stream); - - while (encode(ao, outpts, NULL) > 0) ; - } - ao->priv = NULL; } @@ -484,6 +456,7 @@ static int play(struct ao *ao, void *data, int len, int flags) double nextpts; double pts = ao->pts; double outpts; + int bytelen = len; len /= ac->sample_size * ao->channels.num; @@ -491,6 +464,36 @@ static int play(struct ao *ao, void *data, int len, int flags) MP_WARN(ao, "not ready yet for encoding audio\n"); return 0; } + + if (flags & AOPLAY_FINAL_CHUNK) { + int written = 0; + if (len > 0) { + size_t extralen = + (ac->aframesize - 1) * ao->channels.num * ac->sample_size; + paddingbuf = talloc_size(NULL, bytelen + extralen); + memcpy(paddingbuf, data, bytelen); + fill_with_padding((char *) paddingbuf + bytelen, + extralen / ac->sample_size, + ac->sample_size, ac->sample_padding); + // No danger of recursion, because AOPLAY_FINAL_CHUNK not set + written = play(ao, paddingbuf, bytelen + extralen, 0); + if (written < bytelen) { + MP_ERR(ao, "did not write enough data at the end\n"); + } + talloc_free(paddingbuf); + paddingbuf = NULL; + } + + outpts = ac->expected_next_pts; + if (!ectx->options->rawts && ectx->options->copyts) + outpts += ectx->discontinuity_pts_offset; + outpts += encode_lavc_getoffset(ectx, ac->stream); + + while (encode(ao, outpts, NULL) > 0) ; + + return FFMIN(written, bytelen); + } + if (pts == MP_NOPTS_VALUE) { MP_WARN(ao, "frame without pts, please report; synthesizing pts instead\n"); // synthesize pts from previous expected next pts diff --git a/mpvcore/player/audio.c b/mpvcore/player/audio.c index 443a1eed90..2c3d044c41 100644 --- a/mpvcore/player/audio.c +++ b/mpvcore/player/audio.c @@ -225,10 +225,13 @@ static int write_to_ao(struct MPContext *mpctx, void *data, int len, int flags, return 0; struct ao *ao = mpctx->ao; double bps = ao->bps / mpctx->opts->playback_speed; + int unitsize = ao->channels.num * af_fmt2bits(ao->format) / 8; ao->pts = pts; int played = ao_play(mpctx->ao, data, len, flags); + assert(played <= len); + assert(played % unitsize == 0); if (played > 0) { - mpctx->shown_aframes += played / (af_fmt2bits(ao->format) / 8); + mpctx->shown_aframes += played / unitsize; mpctx->delay += played / bps; // Keep correct pts for remaining data - could be used to flush // remaining buffer when closing ao. @@ -332,6 +335,7 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) int playsize; int playflags = 0; bool audio_eof = false; + bool signal_eof = false; bool partial_fill = false; sh_audio_t * const sh_audio = mpctx->sh_audio; bool modifiable_audio_format = !(ao->format & AF_FORMAT_SPECIAL_MASK); @@ -377,7 +381,6 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) * ao->bps / opts->playback_speed; if (playsize > bytes) { playsize = MPMAX(bytes, 0); - playflags |= AOPLAY_FINAL_CHUNK; audio_eof = true; partial_fill = true; } @@ -387,18 +390,24 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) if (playsize > ao->buffer.len) { partial_fill = true; playsize = ao->buffer.len; - if (audio_eof) - playflags |= AOPLAY_FINAL_CHUNK; } playsize -= playsize % unitsize; if (!playsize) return partial_fill && audio_eof ? -2 : -partial_fill; - // play audio: + if (audio_eof && partial_fill) { + if (opts->gapless_audio) { + // With gapless audio, delay this to ao_uninit. There must be only + // 1 final chunk, and that is handled when calling ao_uninit(). + signal_eof = true; + } else { + playflags |= AOPLAY_FINAL_CHUNK; + } + } + assert(ao->buffer_playable_size <= ao->buffer.len); int played = write_to_ao(mpctx, ao->buffer.start, playsize, playflags, written_audio_pts(mpctx)); - assert(played % unitsize == 0); ao->buffer_playable_size = playsize - played; if (played > 0) { @@ -407,8 +416,8 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) } else if (!mpctx->paused && audio_eof && ao_get_delay(ao) < .04) { // Sanity check to avoid hanging in case current ao doesn't output // partial chunks and doesn't check for AOPLAY_FINAL_CHUNK - return -2; + signal_eof = true; } - return -partial_fill; + return signal_eof ? -2 : -partial_fill; } diff --git a/mpvcore/player/loadfile.c b/mpvcore/player/loadfile.c index baa49f0db5..569532840c 100644 --- a/mpvcore/player/loadfile.c +++ b/mpvcore/player/loadfile.c @@ -62,6 +62,8 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) { + struct MPOpts *opts = mpctx->opts; + mask &= mpctx->initialized_flags; MP_DBG(mpctx, "\n*** uninit(0x%X)\n", mask); @@ -154,9 +156,22 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) } if (mask & INITIALIZED_AO) { + struct ao *ao = mpctx->ao; mpctx->initialized_flags &= ~INITIALIZED_AO; - if (mpctx->ao) - ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE); + if (ao) { + bool drain = false; + // Note: with gapless_audio, stop_play is not correctly set + if (opts->gapless_audio || mpctx->stop_play == AT_END_OF_FILE) { + drain = true; + int len = ao->buffer_playable_size; + assert(len <= ao->buffer.len); + int played = ao_play(ao, ao->buffer.start, len, + AOPLAY_FINAL_CHUNK); + if (played < len) + MP_WARN(ao, "Audio output truncated at end.\n"); + } + ao_uninit(ao, drain); + } mpctx->ao = NULL; } @@ -1277,6 +1292,13 @@ terminate_playback: // don't jump here after ao/vo/getch initialization! if (mpctx->stop_play == KEEP_PLAYING) mpctx->stop_play = AT_END_OF_FILE; + // Drop audio left in the buffers + if (mpctx->stop_play != AT_END_OF_FILE && mpctx->ao) { + mpctx->ao->buffer_playable_size = 0; + mpctx->ao->buffer.len = 0; + ao_reset(mpctx->ao); + } + if (opts->position_save_on_quit && mpctx->stop_play == PT_QUIT) mp_write_watch_later_conf(mpctx); diff --git a/mpvcore/player/playloop.c b/mpvcore/player/playloop.c index d4db03c446..6cd53214a2 100644 --- a/mpvcore/player/playloop.c +++ b/mpvcore/player/playloop.c @@ -254,10 +254,12 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, if (demuxer_amount == -1) { assert(!need_reset); mpctx->stop_play = AT_END_OF_FILE; - // Clear audio from current position if (mpctx->sh_audio && !timeline_fallthrough) { + // Seek outside of the file -> clear audio from current position ao_reset(mpctx->ao); mpctx->sh_audio->a_buffer_len = 0; + mpctx->ao->buffer.len = 0; + mpctx->ao->buffer_playable_size = 0; } return -1; }