summaryrefslogtreecommitdiffstats
path: root/player/audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/audio.c')
-rw-r--r--player/audio.c298
1 files changed, 162 insertions, 136 deletions
diff --git a/player/audio.c b/player/audio.c
index 4867cf5fcc..3a7d26daa7 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -19,6 +19,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <inttypes.h>
+#include <limits.h>
#include <math.h>
#include <assert.h>
@@ -103,6 +104,8 @@ void reinit_audio_chain(struct MPContext *mpctx)
mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
+ mpctx->audio_status = STATUS_SYNCING;
+
if (!(mpctx->initialized_flags & INITIALIZED_ACODEC)) {
mpctx->initialized_flags |= INITIALIZED_ACODEC;
assert(!mpctx->d_audio);
@@ -117,9 +120,19 @@ void reinit_audio_chain(struct MPContext *mpctx)
}
assert(mpctx->d_audio);
+ if (!mpctx->ao_buffer)
+ mpctx->ao_buffer = mp_audio_buffer_create(mpctx);
+
struct mp_audio in_format;
mp_audio_buffer_get_format(mpctx->d_audio->decode_buffer, &in_format);
+ if (!mp_audio_config_valid(&in_format)) {
+ // We don't know the audio format yet - so configure it later as we're
+ // resyncing. fill_audio_buffers() will call this function again.
+ mpctx->sleeptime = 0;
+ return;
+ }
+
if (mpctx->ao_decoder_fmt && (mpctx->initialized_flags & INITIALIZED_AO) &&
!mp_audio_config_equals(mpctx->ao_decoder_fmt, &in_format) &&
opts->gapless_audio < 0)
@@ -169,7 +182,6 @@ void reinit_audio_chain(struct MPContext *mpctx)
struct mp_audio fmt;
ao_get_format(ao, &fmt);
- mpctx->ao_buffer = mp_audio_buffer_create(ao);
mp_audio_buffer_reinit(mpctx->ao_buffer, &fmt);
mpctx->ao_decoder_fmt = talloc(NULL, struct mp_audio);
@@ -185,7 +197,6 @@ void reinit_audio_chain(struct MPContext *mpctx)
if (recreate_audio_filters(mpctx) < 0)
goto init_error;
- mpctx->syncing_audio = true;
return;
init_error:
@@ -206,6 +217,9 @@ double written_audio_pts(struct MPContext *mpctx)
struct mp_audio in_format;
mp_audio_buffer_get_format(d_audio->decode_buffer, &in_format);
+ if (!mp_audio_config_valid(&in_format) || !d_audio->afilter)
+ return MP_NOPTS_VALUE;;
+
// first calculate the end pts of audio that has been output by decoder
double a_pts = d_audio->pts;
if (a_pts == MP_NOPTS_VALUE)
@@ -240,7 +254,7 @@ double written_audio_pts(struct MPContext *mpctx)
double playing_audio_pts(struct MPContext *mpctx)
{
double pts = written_audio_pts(mpctx);
- if (pts == MP_NOPTS_VALUE)
+ if (pts == MP_NOPTS_VALUE || !mpctx->ao)
return pts;
return pts - mpctx->opts->playback_speed * ao_get_delay(mpctx->ao);
}
@@ -273,142 +287,109 @@ static int write_to_ao(struct MPContext *mpctx, struct mp_audio *data, int flags
return 0;
}
-static int write_silence_to_ao(struct MPContext *mpctx, int samples, int flags,
- double pts)
-{
- struct mp_audio tmp = {0};
- mp_audio_buffer_get_format(mpctx->ao_buffer, &tmp);
- tmp.samples = samples;
- char *p = talloc_size(NULL, tmp.samples * tmp.sstride);
- for (int n = 0; n < tmp.num_planes; n++)
- tmp.planes[n] = p;
- mp_audio_fill_silence(&tmp, 0, tmp.samples);
- int r = write_to_ao(mpctx, &tmp, 0, pts);
- talloc_free(p);
- return r;
-}
-
-static int audio_start_sync(struct MPContext *mpctx, int playsize)
+// Return the number of samples that must be skipped or prepended to reach the
+// target audio pts after a seek (for A/V sync or hr-seek).
+// Return value (*skip):
+// >0: skip this many samples
+// =0: don't do anything
+// <0: prepend this many samples of silence
+// Returns false if PTS is not known yet.
+static bool get_sync_samples(struct MPContext *mpctx, int *skip)
{
- struct ao *ao = mpctx->ao;
struct MPOpts *opts = mpctx->opts;
- struct dec_audio *d_audio = mpctx->d_audio;
- int res;
+ *skip = 0;
- assert(d_audio);
+ if (mpctx->audio_status != STATUS_SYNCING)
+ return true;
- struct mp_audio out_format;
- ao_get_format(ao, &out_format);
+ struct mp_audio out_format = {0};
+ ao_get_format(mpctx->ao, &out_format);
+ double play_samplerate = out_format.rate / opts->playback_speed;
- // Timing info may not be set without
- res = audio_decode(d_audio, mpctx->ao_buffer, 1);
- if (res < 0)
- return res;
-
- int samples;
- bool did_retry = false;
- double written_pts;
- double real_samplerate = out_format.rate / opts->playback_speed;
- bool hrseek = mpctx->hrseek_active; // audio only hrseek
- mpctx->hrseek_active = false;
- while (1) {
- written_pts = written_audio_pts(mpctx);
- double ptsdiff;
- if (hrseek)
- ptsdiff = written_pts - mpctx->hrseek_pts;
- else
- ptsdiff = written_pts - mpctx->video_next_pts - mpctx->delay
- + mpctx->audio_delay;
- samples = ptsdiff * real_samplerate;
-
- // ogg demuxers give packets without timing
- if (written_pts <= 1 && d_audio->pts == MP_NOPTS_VALUE) {
- if (!did_retry) {
- // Try to read more data to see packets that have pts
- res = audio_decode(d_audio, mpctx->ao_buffer, out_format.rate);
- if (res < 0)
- return res;
- did_retry = true;
- continue;
- }
- samples = 0;
- }
+ bool is_pcm = !(out_format.format & AF_FORMAT_SPECIAL_MASK); // no spdif
+ if (!opts->initial_audio_sync || !is_pcm) {
+ mpctx->audio_status = STATUS_FILLING;
+ return true;
+ }
- if (fabs(ptsdiff) > 300 || isnan(ptsdiff)) // pts reset or just broken?
- samples = 0;
-
- if (samples > 0)
- break;
-
- mpctx->syncing_audio = false;
- int skip_samples = -samples;
- int a = MPMIN(skip_samples, MPMAX(playsize, 2500));
- res = audio_decode(d_audio, mpctx->ao_buffer, a);
- if (skip_samples <= mp_audio_buffer_samples(mpctx->ao_buffer)) {
- mp_audio_buffer_skip(mpctx->ao_buffer, skip_samples);
- if (res < 0)
- return res;
- return audio_decode(d_audio, mpctx->ao_buffer, playsize);
+ double written_pts = written_audio_pts(mpctx);
+ if (written_pts == MP_NOPTS_VALUE && !mp_audio_buffer_samples(mpctx->ao_buffer))
+ return false; // no audio read yet
+
+ bool sync_to_video = mpctx->d_video && mpctx->sync_audio_to_video;
+
+ double sync_pts = MP_NOPTS_VALUE;
+ if (sync_to_video) {
+ if (mpctx->video_next_pts != MP_NOPTS_VALUE) {
+ sync_pts = mpctx->video_next_pts;
+ } else if (mpctx->video_status < STATUS_READY) {
+ return false; // wait until we know a video PTS
}
- mp_audio_buffer_clear(mpctx->ao_buffer);
- if (res < 0)
- return res;
+ } else if (mpctx->hrseek_active) {
+ sync_pts = mpctx->hrseek_pts;
}
- if (hrseek)
- // Don't add silence in audio-only case even if position is too late
- return 0;
- if (samples >= playsize) {
- /* This case could fall back to the one below with
- * samples = playsize, but then silence would keep accumulating
- * in ao_buffer if the AO accepts less data than it asks for
- * in playsize. */
- write_silence_to_ao(mpctx, playsize, 0,
- written_pts - samples / real_samplerate);
- return AD_ASYNC_PLAY_DONE;
+ if (sync_pts == MP_NOPTS_VALUE) {
+ mpctx->audio_status = STATUS_FILLING;
+ return true; // syncing disabled
}
- mpctx->syncing_audio = false;
- mp_audio_buffer_prepend_silence(mpctx->ao_buffer, samples);
- return audio_decode(d_audio, mpctx->ao_buffer, playsize);
+
+ if (sync_to_video)
+ sync_pts += mpctx->delay - mpctx->audio_delay;
+
+ double ptsdiff = written_pts - sync_pts;
+ // Missing timestamp, or PTS reset, or just broken.
+ if (written_pts == MP_NOPTS_VALUE || fabs(ptsdiff) > 300) {
+ MP_WARN(mpctx, "Failed audio resync.\n");
+ mpctx->audio_status = STATUS_FILLING;
+ return true;
+ }
+
+ *skip = -ptsdiff * play_samplerate;
+ return true;
}
int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
{
struct MPOpts *opts = mpctx->opts;
- struct ao *ao = mpctx->ao;
- int playsize;
- int playflags = 0;
- bool audio_eof = false;
- bool signal_eof = false;
- bool partial_fill = false;
struct dec_audio *d_audio = mpctx->d_audio;
- struct mp_audio out_format;
- ao_get_format(ao, &out_format);
- // Can't adjust the start of audio with spdif pass-through.
- bool modifiable_audio_format = !(out_format.format & AF_FORMAT_SPECIAL_MASK);
assert(d_audio);
- if (mpctx->paused)
- playsize = 1; // just initialize things (audio pts at least)
- else
- playsize = ao_get_space(ao);
-
- // Coming here with hrseek_active still set means audio-only
- if (!mpctx->d_video || !mpctx->sync_audio_to_video)
- mpctx->syncing_audio = false;
- if (!opts->initial_audio_sync || !modifiable_audio_format) {
- mpctx->syncing_audio = false;
- mpctx->hrseek_active = false;
+ if (!d_audio->afilter || !mpctx->ao) {
+ // Probe the initial audio format. Returns AD_OK (and does nothing) if
+ // the format is already known.
+ int r = initial_audio_decode(mpctx->d_audio);
+ if (r == AD_WAIT)
+ return -1; // continue later when new data is available
+ if (r != AD_OK) {
+ MP_ERR(mpctx, "Error initializing audio.\n");
+ struct track *track = mpctx->current_track[0][STREAM_AUDIO];
+ mp_deselect_track(mpctx, track);
+ return -2;
+ }
+ reinit_audio_chain(mpctx);
+ return -1; // try again next iteration
}
- int res;
- if (mpctx->syncing_audio || mpctx->hrseek_active)
- res = audio_start_sync(mpctx, playsize);
- else
- res = audio_decode(d_audio, mpctx->ao_buffer, playsize);
+ // if paused, just initialize things (audio format & pts)
+ int playsize = 1;
+ if (!mpctx->paused)
+ playsize = ao_get_space(mpctx->ao);
+
+ int skip = 0;
+ bool sync_known = get_sync_samples(mpctx, &skip);
+ if (skip > 0) {
+ playsize = MPMIN(skip + 1, MPMAX(playsize, 2500)); // buffer extra data
+ } else if (skip < 0) {
+ playsize = MPMAX(1, playsize + skip); // silence will be prepended
+ }
- if (res < 0) { // EOF, error or format change
- if (res == AD_NEW_FMT) {
+ int status = AD_OK;
+ if (playsize > mp_audio_buffer_samples(mpctx->ao_buffer)) {
+ status = audio_decode(d_audio, mpctx->ao_buffer, playsize);
+ if (status == AD_WAIT)
+ return -1;
+ if (status == AD_NEW_FMT) {
/* The format change isn't handled too gracefully. A more precise
* implementation would require draining buffered old-format audio
* while displaying video, then doing the output format switch.
@@ -416,16 +397,56 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
if (mpctx->opts->gapless_audio < 1)
uninit_player(mpctx, INITIALIZED_AO);
reinit_audio_chain(mpctx);
- return -1;
- } else if (res == AD_ASYNC_PLAY_DONE)
- return 0;
- else if (res == AD_EOF)
- audio_eof = true;
+ mpctx->sleeptime = 0;
+ return -1; // retry on next iteration
+ }
+ }
+
+ bool end_sync = status != AD_OK; // (on error/EOF, start playback immediately)
+ if (skip >= 0) {
+ int max = mp_audio_buffer_samples(mpctx->ao_buffer);
+ mp_audio_buffer_skip(mpctx->ao_buffer, MPMIN(skip, max));
+ // If something is left, we definitely reached the target time.
+ end_sync |= sync_known && skip < max;
+ } else if (skip < 0) {
+ if (-skip < 1000000) { // heuristic against making the buffer too large
+ mp_audio_buffer_prepend_silence(mpctx->ao_buffer, -skip);
+ } else {
+ MP_ERR(mpctx, "Audio starts too late: sync. failed.\n");
+ ao_reset(mpctx->ao);
+ }
+ end_sync = true;
+ }
+
+ if (mpctx->audio_status == STATUS_SYNCING) {
+ if (end_sync)
+ mpctx->audio_status = STATUS_FILLING;
+ mpctx->sleeptime = 0;
+ return -1; // continue on next iteration
+ }
+
+ assert(mpctx->audio_status >= STATUS_FILLING);
+
+ // Even if we're done decoding and syncing, let video start first - this is
+ // required, because sending audio to the AO already starts playback.
+ if (mpctx->audio_status == STATUS_FILLING && mpctx->sync_audio_to_video &&
+ mpctx->video_status <= STATUS_READY)
+ {
+ mpctx->audio_status = STATUS_READY;
+ return -1;
}
+ bool audio_eof = status == AD_EOF;
+ bool partial_fill = false;
+ int playflags = 0;
+
+ struct mp_audio out_format = {0};
+ ao_get_format(mpctx->ao, &out_format);
+ double play_samplerate = out_format.rate / opts->playback_speed;
+
if (endpts != MP_NOPTS_VALUE) {
double samples = (endpts - written_audio_pts(mpctx) - mpctx->audio_delay)
- * out_format.rate / opts->playback_speed;
+ * play_samplerate;
if (playsize > samples) {
playsize = MPMAX(samples, 0);
audio_eof = true;
@@ -437,17 +458,14 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
playsize = mp_audio_buffer_samples(mpctx->ao_buffer);
partial_fill = true;
}
- if (!playsize)
- return partial_fill && audio_eof ? -2 : -partial_fill;
-
- 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 {
+
+ audio_eof &= partial_fill;
+
+ if (audio_eof) {
+ // With gapless audio, delay this to ao_uninit. There must be only
+ // 1 final chunk, and that is handled when calling ao_uninit().
+ if (opts->gapless_audio)
playflags |= AOPLAY_FINAL_CHUNK;
- }
}
if (mpctx->paused)
@@ -458,10 +476,18 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
data.samples = MPMIN(data.samples, playsize);
int played = write_to_ao(mpctx, &data, playflags, written_audio_pts(mpctx));
assert(played >= 0 && played <= data.samples);
-
mp_audio_buffer_skip(mpctx->ao_buffer, played);
- return signal_eof ? -2 : -partial_fill;
+ mpctx->audio_status = STATUS_PLAYING;
+ if (audio_eof) {
+ mpctx->audio_status = STATUS_DRAINING;
+ // Wait until the AO has played all queued data. In the gapless case,
+ // we trigger EOF immediately, and let it play asynchronously.
+ if (ao_eof_reached(mpctx->ao) || opts->gapless_audio)
+ mpctx->audio_status = STATUS_EOF;
+ }
+
+ return 0;
}
// Drop data queued for output, or which the AO is currently outputting.