summaryrefslogtreecommitdiffstats
path: root/mpvcore
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-11-08 20:00:58 +0100
committerwm4 <wm4@nowhere>2013-11-08 20:00:58 +0100
commit8125252399d9b02a6f143d109a92341ff7eff5a9 (patch)
tree3b9fed7c1acadca63714c29d39fda681f0dda33a /mpvcore
parent052a7d54ab4efbf9743767ea7aae95c98f5b5379 (diff)
downloadmpv-8125252399d9b02a6f143d109a92341ff7eff5a9.tar.bz2
mpv-8125252399d9b02a6f143d109a92341ff7eff5a9.tar.xz
audio: don't let ao_lavc access frontend internals, change gapless audio
ao_lavc.c accesses ao->buffer, which I consider internal. The access was done in ao_lavc.c/uninit(), which tried to get the left-over audio in order to write the last (possibly partial) audio frame. The play() function didn't accept partial frames, because the AOPLAY_FINAL_CHUNK flag was not correctly set, and handling it otherwise would require an internal FIFO. Fix this by making sure that with gapless audio (used with encoding), the AOPLAY_FINAL_CHUNK is set only once, instead when each file ends. Basically, move the hack in ao_lavc's uninit to uninit_player. One thing can not be entirely correctly handled: if gapless audio is active, we don't know really whether the AO is closed because the file ended playing (i.e. we want to send the buffered remainder of the audio to the AO), or whether the user is quitting the player. (The stop_play flag is overwritten, fixing that is perhaps not worth it.) Handle this by adding additional code to drain the AO and the buffers when playback is quit (see play_current_file() change). Test case: mpv avdevice://lavfi:sine=441 avdevice://lavfi:sine=441 -length 0.2267 -gapless-audio
Diffstat (limited to 'mpvcore')
-rw-r--r--mpvcore/player/audio.c25
-rw-r--r--mpvcore/player/loadfile.c26
-rw-r--r--mpvcore/player/playloop.c4
3 files changed, 44 insertions, 11 deletions
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;
}