From fef8b7984be5a6244612d346bd60d2badd4a2e63 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 21 Jan 2016 22:10:15 +0100 Subject: audio: refactor: work towards unentangling audio decoding and filtering Similar to the video path. dec_audio.c now handles decoding only. It also looks very similar to dec_video.c, and actually contains some of the rewritten code from it. (A further goal might be unifying the decoders, I guess.) High potential for regressions. --- audio/decode/ad_lavc.c | 8 +- audio/decode/ad_spdif.c | 8 +- audio/decode/dec_audio.c | 193 ++++++++++++++++++---------------------------- audio/decode/dec_audio.h | 41 +++++----- player/audio.c | 195 +++++++++++++++++++++++++++++++++++------------ player/command.c | 8 +- player/core.h | 21 +++++ player/video.c | 4 +- 8 files changed, 277 insertions(+), 201 deletions(-) diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c index f7304353af..3c17d0d9bb 100644 --- a/audio/decode/ad_lavc.c +++ b/audio/decode/ad_lavc.c @@ -196,21 +196,21 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt, // LATM may need many packets to find mux info if (ret == AVERROR(EAGAIN)) { mpkt->len = 0; - return AD_OK; + return 0; } } if (ret < 0) { MP_ERR(da, "Error decoding audio.\n"); - return AD_ERR; + return -1; } if (!got_frame) - return mpkt ? AD_OK : AD_EOF; + return 0; double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL); struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe); if (!mpframe) - return AD_ERR; + return -1; struct mp_chmap lavc_chmap = mpframe->channels; if (lavc_chmap.num != avctx->channels) diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c index 54e52a9566..7298d9e7d7 100644 --- a/audio/decode/ad_spdif.c +++ b/audio/decode/ad_spdif.c @@ -251,7 +251,7 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt, spdif_ctx->out_buffer_len = 0; if (!mpkt) - return AD_EOF; + return 0; double pts = mpkt->pts; @@ -261,17 +261,17 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt, pkt.pts = pkt.dts = 0; if (!spdif_ctx->lavf_ctx) { if (init_filter(da, &pkt) < 0) - return AD_ERR; + return -1; } int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt); avio_flush(spdif_ctx->lavf_ctx->pb); if (ret < 0) - return AD_ERR; + return -1; int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride; *out = mp_audio_pool_get(spdif_ctx->pool, &spdif_ctx->fmt, samples); if (!*out) - return AD_ERR; + return -1; memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len); (*out)->pts = pts; diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c index f97636e475..9fe09ae89f 100644 --- a/audio/decode/dec_audio.c +++ b/audio/decode/dec_audio.c @@ -61,8 +61,6 @@ static void uninit_decoder(struct dec_audio *d_audio) d_audio->ad_driver = NULL; talloc_free(d_audio->priv); d_audio->priv = NULL; - d_audio->afilter->initialized = -1; - d_audio->decode_format = (struct mp_audio){0}; } static int init_audio_codec(struct dec_audio *d_audio, const char *decoder) @@ -93,7 +91,7 @@ static struct mp_decoder_list *audio_select_decoders(struct dec_audio *d_audio) struct mp_decoder_list *list = audio_decoder_list(); struct mp_decoder_list *new = mp_select_decoders(list, codec, opts->audio_decoders); - if (d_audio->spdif_passthrough) { + if (d_audio->try_spdif) { struct mp_decoder_list *spdif = mp_select_decoder_list(list, codec, "spdif", opts->audio_spdif); mp_append_decoders(spdif, new); @@ -159,144 +157,101 @@ void audio_uninit(struct dec_audio *d_audio) return; MP_VERBOSE(d_audio, "Uninit audio filters...\n"); uninit_decoder(d_audio); - af_destroy(d_audio->afilter); - talloc_free(d_audio->waiting); + talloc_free(d_audio->current_frame); talloc_free(d_audio->packet); talloc_free(d_audio); } -static int decode_new_frame(struct dec_audio *da) +void audio_reset_decoding(struct dec_audio *d_audio) { - while (!da->waiting) { - if (!da->packet) { - if (demux_read_packet_async(da->header, &da->packet) == 0) - return AD_WAIT; - } + if (d_audio->ad_driver) + d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL); + d_audio->pts = MP_NOPTS_VALUE; + d_audio->pts_reset = false; + talloc_free(d_audio->current_frame); + d_audio->current_frame = NULL; + talloc_free(d_audio->packet); + d_audio->packet = NULL; +} - int ret = da->ad_driver->decode_packet(da, da->packet, &da->waiting); - if (ret < 0 || (da->packet && da->packet->len == 0)) { - talloc_free(da->packet); - da->packet = NULL; - } - if (ret < 0) - return ret; - - if (da->waiting) { - if (da->waiting->pts != MP_NOPTS_VALUE) { - if (da->pts != MP_NOPTS_VALUE) { - da->pts += da->pts_offset / (double)da->waiting->rate; - da->pts_offset = 0; - } - double newpts = da->waiting->pts; - // Keep the interpolated timestamp if it doesn't deviate more - // than 1 ms from the real one. (MKV rounded timestamps.) - if (da->pts == MP_NOPTS_VALUE || da->pts_offset != 0 || - fabs(da->pts - newpts) > 0.001) - { - // Attempt to detect jumps in PTS. Even for the lowest - // sample rates and with worst container rounded timestamp, - // this should be a margin more than enough. - if (da->pts != MP_NOPTS_VALUE && fabs(newpts - da->pts) > 0.1) - { - MP_WARN(da, "Invalid audio PTS: %f -> %f\n", - da->pts, newpts); - da->pts_reset = true; - } - da->pts = da->waiting->pts; - da->pts_offset = 0; - } +static void fix_audio_pts(struct dec_audio *da) +{ + if (!da->current_frame) + return; + + if (da->current_frame->pts != MP_NOPTS_VALUE) { + double newpts = da->current_frame->pts; + // Keep the interpolated timestamp if it doesn't deviate more + // than 1 ms from the real one. (MKV rounded timestamps.) + if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001) { + // Attempt to detect jumps in PTS. Even for the lowest + // sample rates and with worst container rounded timestamp, + // this should be a margin more than enough. + if (da->pts != MP_NOPTS_VALUE && fabs(newpts - da->pts) > 0.1) { + MP_WARN(da, "Invalid audio PTS: %f -> %f\n", + da->pts, newpts); + da->pts_reset = true; } - da->pts_offset += da->waiting->samples; - da->decode_format = *da->waiting; - mp_audio_set_null_data(&da->decode_format); + da->pts = da->current_frame->pts; } - - if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps) - da->pts = 0; } - return mp_audio_config_valid(da->waiting) ? AD_OK : AD_ERR; -} -/* Decode packets until we know the audio format. Then reinit the buffer. - * Returns AD_OK on success, negative AD_* code otherwise. - * Also returns AD_OK if already initialized (and does nothing). - */ -int initial_audio_decode(struct dec_audio *da) -{ - return decode_new_frame(da); -} + if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps) + da->pts = 0; -static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf, - int minsamples, bool eof) -{ - while (mp_audio_buffer_samples(outbuf) < minsamples) { - if (af_output_frame(afs, eof) < 0) - return true; // error, stop doing stuff - struct mp_audio *mpa = af_read_output_frame(afs); - if (!mpa) - return false; // out of data - mp_audio_buffer_append(outbuf, mpa); - talloc_free(mpa); - } - return true; + da->current_frame->pts = da->pts; + + if (da->pts != MP_NOPTS_VALUE) + da->pts += da->current_frame->samples / (double)da->current_frame->rate; } -/* Try to get at least minsamples decoded+filtered samples in outbuf - * (total length including possible existing data). - * Return 0 on success, or negative AD_* error code. - * In the former case outbuf has at least minsamples buffered on return. - * In case of EOF/error it might or might not be. */ -int audio_decode(struct dec_audio *da, struct mp_audio_buffer *outbuf, - int minsamples) +void audio_work(struct dec_audio *da) { - struct af_stream *afs = da->afilter; - if (afs->initialized < 1) - return AD_ERR; - - MP_STATS(da, "start audio"); - - int res; - while (1) { - res = 0; + if (da->current_frame) + return; - if (copy_output(afs, outbuf, minsamples, false)) - break; + if (!da->packet && demux_read_packet_async(da->header, &da->packet) == 0) { + da->current_state = AUDIO_WAIT; + return; + } - res = decode_new_frame(da); - if (res < 0) { - // drain filters first (especially for true EOF case) - copy_output(afs, outbuf, minsamples, true); - break; - } + bool had_packet = !!da->packet; - // On format change, make sure to drain the filter chain. - if (!mp_audio_config_equals(&afs->input, da->waiting)) { - copy_output(afs, outbuf, minsamples, true); - res = AD_NEW_FMT; - break; - } + int ret = da->ad_driver->decode_packet(da, da->packet, &da->current_frame); + if (ret < 0 || (da->packet && da->packet->len == 0)) { + talloc_free(da->packet); + da->packet = NULL; + } - struct mp_audio *mpa = da->waiting; - da->waiting = NULL; - if (af_filter_frame(afs, mpa) < 0) - return AD_ERR; + if (da->current_frame && !mp_audio_config_valid(da->current_frame)) { + talloc_free(da->current_frame); + da->current_frame = NULL; } - MP_STATS(da, "end audio"); + da->current_state = AUDIO_OK; + if (!da->current_frame) { + da->current_state = AUDIO_EOF; + if (had_packet) + da->current_state = AUDIO_SKIP; + } - return res; + fix_audio_pts(da); } -void audio_reset_decoding(struct dec_audio *d_audio) +// Fetch an audio frame decoded with audio_work(). Returns one of: +// AUDIO_OK: *out_frame is set to a new image +// AUDIO_WAIT: waiting for demuxer; will receive a wakeup signal +// AUDIO_EOF: end of file, no more frames to be expected +// AUDIO_SKIP: dropped frame or something similar +int audio_get_frame(struct dec_audio *da, struct mp_audio **out_frame) { - if (d_audio->ad_driver) - d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL); - af_seek_reset(d_audio->afilter); - d_audio->pts = MP_NOPTS_VALUE; - d_audio->pts_offset = 0; - d_audio->pts_reset = false; - talloc_free(d_audio->waiting); - d_audio->waiting = NULL; - talloc_free(d_audio->packet); - d_audio->packet = NULL; + *out_frame = NULL; + if (da->current_frame) { + *out_frame = da->current_frame; + da->current_frame = NULL; + return AUDIO_OK; + } + if (da->current_state == AUDIO_OK) + return AUDIO_SKIP; + return da->current_state; } diff --git a/audio/decode/dec_audio.h b/audio/decode/dec_audio.h index d88d8bfaea..27752f7be1 100644 --- a/audio/decode/dec_audio.h +++ b/audio/decode/dec_audio.h @@ -30,39 +30,38 @@ struct dec_audio { struct mp_log *log; struct MPOpts *opts; struct mpv_global *global; - bool spdif_passthrough, spdif_failed; const struct ad_functions *ad_driver; struct sh_stream *header; - struct af_stream *afilter; char *decoder_desc; - struct mp_audio decode_format; - struct mp_audio *waiting; // used on format-change - // last known pts value in output from decoder - double pts; - // number of samples output by decoder after last known pts - int pts_offset; + + bool try_spdif; + // set every time a jump in timestamps is encountered bool pts_reset; + // For free use by the ad_driver void *priv; - // Strictly internal to dec_audio.c - struct demux_packet *packet; -}; -enum { - AD_OK = 0, - AD_ERR = -1, - AD_EOF = -2, - AD_NEW_FMT = -3, - AD_WAIT = -4, + // Strictly internal (dec_audio.c). + + double pts; // endpts of previous frame + struct demux_packet *packet; + struct mp_audio *current_frame; + int current_state; }; struct mp_decoder_list *audio_decoder_list(void); int audio_init_best_codec(struct dec_audio *d_audio); -int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf, - int minsamples); -int initial_audio_decode(struct dec_audio *d_audio); -void audio_reset_decoding(struct dec_audio *d_audio); void audio_uninit(struct dec_audio *d_audio); +void audio_work(struct dec_audio *d_audio); + +#define AUDIO_OK 1 +#define AUDIO_WAIT 0 +#define AUDIO_EOF -1 +#define AUDIO_SKIP -2 +int audio_get_frame(struct dec_audio *d_audio, struct mp_audio **out_frame); + +void audio_reset_decoding(struct dec_audio *d_audio); + #endif /* MPLAYER_DEC_AUDIO_H */ diff --git a/player/audio.c b/player/audio.c index 5a62a69d1a..fb1398c597 100644 --- a/player/audio.c +++ b/player/audio.c @@ -43,6 +43,14 @@ #include "core.h" #include "command.h" +enum { + AD_OK = 0, + AD_ERR = -1, + AD_EOF = -2, + AD_NEW_FMT = -3, + AD_WAIT = -4, +}; + // Use pitch correction only for speed adjustments by the user, not minor sync // correction ones. static int get_speed_method(struct MPContext *mpctx) @@ -55,7 +63,7 @@ static int get_speed_method(struct MPContext *mpctx) // return true; if filter recreation is needed, return false. static bool update_speed_filters(struct MPContext *mpctx) { - struct af_stream *afs = mpctx->d_audio->afilter; + struct af_stream *afs = mpctx->ao_chain->af; double speed = mpctx->audio_speed; if (afs->initialized < 1) @@ -81,7 +89,7 @@ static bool update_speed_filters(struct MPContext *mpctx) // Update speed, and insert/remove filters if necessary. static void recreate_speed_filters(struct MPContext *mpctx) { - struct af_stream *afs = mpctx->d_audio->afilter; + struct af_stream *afs = mpctx->ao_chain->af; if (update_speed_filters(mpctx)) return; @@ -113,9 +121,9 @@ fail: static int recreate_audio_filters(struct MPContext *mpctx) { - assert(mpctx->d_audio); + assert(mpctx->ao_chain); - struct af_stream *afs = mpctx->d_audio->afilter; + struct af_stream *afs = mpctx->ao_chain->af; if (afs->initialized < 1 && af_init(afs) < 0) goto fail; @@ -134,11 +142,11 @@ fail: int reinit_audio_filters(struct MPContext *mpctx) { - struct dec_audio *d_audio = mpctx->d_audio; - if (!d_audio) + struct ao_chain *ao_c = mpctx->ao_chain; + if (!ao_c) return 0; - af_uninit(mpctx->d_audio->afilter); + af_uninit(ao_c->af); return recreate_audio_filters(mpctx) < 0 ? -1 : 1; } @@ -148,7 +156,7 @@ void update_playback_speed(struct MPContext *mpctx) mpctx->audio_speed = mpctx->opts->playback_speed * mpctx->speed_factor_a; mpctx->video_speed = mpctx->opts->playback_speed * mpctx->speed_factor_v; - if (!mpctx->d_audio || mpctx->d_audio->afilter->initialized < 1) + if (!mpctx->ao_chain || mpctx->ao_chain->af->initialized < 1) return; if (!update_speed_filters(mpctx)) @@ -161,7 +169,11 @@ void reset_audio_state(struct MPContext *mpctx) audio_reset_decoding(mpctx->d_audio); if (mpctx->ao_buffer) mp_audio_buffer_clear(mpctx->ao_buffer); - mpctx->audio_status = mpctx->d_audio ? STATUS_SYNCING : STATUS_EOF; + if (mpctx->ao_chain) { + mpctx->ao_chain->pts = MP_NOPTS_VALUE; + af_seek_reset(mpctx->ao_chain->af); + } + mpctx->audio_status = mpctx->ao_chain ? STATUS_SYNCING : STATUS_EOF; mpctx->delay = 0; mpctx->audio_drop_throttle = 0; mpctx->audio_stat_start = 0; @@ -185,12 +197,15 @@ void uninit_audio_out(struct MPContext *mpctx) void uninit_audio_chain(struct MPContext *mpctx) { - if (mpctx->d_audio) { + if (mpctx->ao_chain) { mixer_uninit_audio(mpctx->mixer); audio_uninit(mpctx->d_audio); mpctx->d_audio = NULL; talloc_free(mpctx->ao_buffer); mpctx->ao_buffer = NULL; + af_destroy(mpctx->ao_chain->af); + talloc_free(mpctx->ao_chain); + mpctx->ao_chain = NULL; mpctx->audio_status = STATUS_EOF; reselect_demux_streams(mpctx); @@ -210,15 +225,25 @@ void reinit_audio_chain(struct MPContext *mpctx) mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL); + struct ao_chain *ao_c = mpctx->ao_chain; + if (!mpctx->d_audio) { + assert(!ao_c); mpctx->d_audio = talloc_zero(NULL, struct dec_audio); mpctx->d_audio->log = mp_log_new(mpctx->d_audio, mpctx->log, "!ad"); mpctx->d_audio->global = mpctx->global; mpctx->d_audio->opts = opts; mpctx->d_audio->header = sh; - mpctx->d_audio->afilter = af_new(mpctx->global); - mpctx->d_audio->afilter->replaygain_data = sh->codec->replaygain_data; - mpctx->d_audio->spdif_passthrough = true; + ao_c = talloc_zero(NULL, struct ao_chain); + mpctx->ao_chain = ao_c; + ao_c->log = mpctx->d_audio->log; + ao_c->af = af_new(mpctx->global); + ao_c->af->replaygain_data = sh->codec->replaygain_data; + ao_c->spdif_passthrough = true; + ao_c->pts = MP_NOPTS_VALUE; + ao_c->ao = mpctx->ao; + ao_c->audio_src = mpctx->d_audio; + mpctx->d_audio->try_spdif = ao_c->spdif_passthrough; mpctx->ao_buffer = mp_audio_buffer_create(NULL); if (!audio_init_best_codec(mpctx->d_audio)) goto init_error; @@ -232,7 +257,7 @@ void reinit_audio_chain(struct MPContext *mpctx) } assert(mpctx->d_audio); - struct mp_audio in_format = mpctx->d_audio->decode_format; + struct mp_audio in_format = ao_c->input_format; if (!mp_audio_config_valid(&in_format)) { // We don't know the audio format yet - so configure it later as we're @@ -248,7 +273,7 @@ void reinit_audio_chain(struct MPContext *mpctx) uninit_audio_out(mpctx); } - struct af_stream *afs = mpctx->d_audio->afilter; + struct af_stream *afs = ao_c->af; afs->output = (struct mp_audio){0}; if (mpctx->ao) { @@ -272,7 +297,7 @@ void reinit_audio_chain(struct MPContext *mpctx) if (!mpctx->ao) { bool spdif_fallback = af_fmt_is_spdif(afs->output.format) && - mpctx->d_audio->spdif_passthrough; + ao_c->spdif_passthrough; bool ao_null_fallback = opts->ao_null_fallback && !spdif_fallback; mp_chmap_remove_useless_channels(&afs->output.channels, @@ -282,6 +307,7 @@ void reinit_audio_chain(struct MPContext *mpctx) mpctx->ao = ao_init_best(mpctx->global, ao_null_fallback, mpctx->input, mpctx->encode_lavc_ctx, afs->output.rate, afs->output.format, afs->output.channels); + ao_c->ao = mpctx->ao; struct mp_audio fmt = {0}; if (mpctx->ao) @@ -293,14 +319,16 @@ void reinit_audio_chain(struct MPContext *mpctx) MP_ERR(mpctx, "Passthrough format unsupported.\n"); ao_uninit(mpctx->ao); mpctx->ao = NULL; + ao_c->ao = NULL; } } if (!mpctx->ao) { // If spdif was used, try to fallback to PCM. if (spdif_fallback) { - mpctx->d_audio->spdif_passthrough = false; - mpctx->d_audio->spdif_failed = true; + ao_c->spdif_passthrough = false; + ao_c->spdif_failed = true; + mpctx->d_audio->try_spdif = false; if (!audio_init_best_codec(mpctx->d_audio)) goto init_error; reset_audio_state(mpctx); @@ -346,35 +374,22 @@ no_audio: // ao so far. double written_audio_pts(struct MPContext *mpctx) { - struct dec_audio *d_audio = mpctx->d_audio; - if (!d_audio) + struct ao_chain *ao_c = mpctx->ao_chain; + if (!ao_c) return MP_NOPTS_VALUE; - struct mp_audio in_format = d_audio->decode_format; + struct mp_audio in_format = ao_c->input_format; - if (!mp_audio_config_valid(&in_format) || d_audio->afilter->initialized < 1) + if (!mp_audio_config_valid(&in_format) || ao_c->af->initialized < 1) return MP_NOPTS_VALUE; // first calculate the end pts of audio that has been output by decoder - double a_pts = d_audio->pts; + double a_pts = ao_c->pts; if (a_pts == MP_NOPTS_VALUE) return MP_NOPTS_VALUE; - // d_audio->pts is the timestamp of the first sample of the latest frame - // the with a known pts that the decoder has returned. d_audio->pts_offset - // is the amount of samples the decoder has returned after that timestamp - // (includes the frame size). - a_pts += d_audio->pts_offset / (double)in_format.rate; - - // Now a_pts hopefully holds the pts for end of audio from decoder. - // Subtract data in buffers between decoder and audio out. - - // Decoded but not filtered - if (d_audio->waiting) - a_pts -= d_audio->waiting->samples / (double)in_format.rate; - // Data buffered in audio filters, measured in seconds of "missing" output - double buffered_output = af_calc_delay(d_audio->afilter); + double buffered_output = af_calc_delay(ao_c->af); // Data that was ready for ao but was buffered because ao didn't fully // accept everything to internal buffers yet @@ -498,20 +513,106 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip) return true; } + +static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf, + int minsamples, bool eof) +{ + while (mp_audio_buffer_samples(outbuf) < minsamples) { + if (af_output_frame(afs, eof) < 0) + return true; // error, stop doing stuff + struct mp_audio *mpa = af_read_output_frame(afs); + if (!mpa) + return false; // out of data + mp_audio_buffer_append(outbuf, mpa); + talloc_free(mpa); + } + return true; +} + +static int decode_new_frame(struct ao_chain *ao_c) +{ + if (ao_c->input_frame) + return AD_OK; + + int res = AUDIO_SKIP; + while (res == AUDIO_SKIP) { + audio_work(ao_c->audio_src); + res = audio_get_frame(ao_c->audio_src, &ao_c->input_frame); + } + + if (ao_c->input_frame) + mp_audio_copy_config(&ao_c->input_format, ao_c->input_frame); + + switch (res) { + case AUDIO_OK: return AD_OK; + case AUDIO_WAIT: return AD_WAIT; + case AUDIO_EOF: return AD_EOF; + default: abort(); + } +} + +/* Try to get at least minsamples decoded+filtered samples in outbuf + * (total length including possible existing data). + * Return 0 on success, or negative AD_* error code. + * In the former case outbuf has at least minsamples buffered on return. + * In case of EOF/error it might or might not be. */ +static int filter_audio(struct ao_chain *ao_c, struct mp_audio_buffer *outbuf, + int minsamples) +{ + struct af_stream *afs = ao_c->af; + if (afs->initialized < 1) + return AD_ERR; + + MP_STATS(ao_c, "start audio"); + + int res; + while (1) { + res = 0; + + if (copy_output(afs, outbuf, minsamples, false)) + break; + + res = decode_new_frame(ao_c); + if (res < 0) { + // drain filters first (especially for true EOF case) + copy_output(afs, outbuf, minsamples, true); + break; + } + + // On format change, make sure to drain the filter chain. + if (!mp_audio_config_equals(&afs->input, ao_c->input_frame)) { + copy_output(afs, outbuf, minsamples, true); + res = AD_NEW_FMT; + break; + } + + struct mp_audio *mpa = ao_c->input_frame; + ao_c->input_frame = NULL; + ao_c->pts = mpa->pts + mpa->samples / (double)mpa->rate; + if (af_filter_frame(afs, mpa) < 0) + return AD_ERR; + } + + MP_STATS(ao_c, "end audio"); + + return res; +} + void fill_audio_out_buffers(struct MPContext *mpctx, double endpts) { struct MPOpts *opts = mpctx->opts; - struct dec_audio *d_audio = mpctx->d_audio; + struct ao_chain *ao_c = mpctx->ao_chain; dump_audio_stats(mpctx); if (mpctx->ao && ao_query_and_reset_events(mpctx->ao, AO_EVENT_RELOAD)) { ao_reset(mpctx->ao); uninit_audio_out(mpctx); - if (d_audio) { - if (mpctx->d_audio->spdif_failed) { - mpctx->d_audio->spdif_failed = false; - mpctx->d_audio->spdif_passthrough = true; + if (ao_c) { + struct dec_audio *d_audio = ao_c->audio_src; + if (d_audio && ao_c->spdif_failed) { + ao_c->spdif_failed = false; + mpctx->d_audio->try_spdif = true; if (!audio_init_best_codec(mpctx->d_audio)) { MP_ERR(mpctx, "Error reinitializing audio.\n"); error_on_track(mpctx, mpctx->current_track[0][STREAM_AUDIO]); @@ -522,13 +623,13 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts) } } - if (!d_audio) + if (!ao_c) return; - if (d_audio->afilter->initialized < 1 || !mpctx->ao) { + if (ao_c->af->initialized < 1 || !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); + int r = decode_new_frame(mpctx->ao_chain); if (r == AD_WAIT) return; // continue later when new data is available reinit_audio_chain(mpctx); @@ -536,7 +637,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts) return; // try again next iteration } - if (mpctx->vo_chain && d_audio->pts_reset) { + if (mpctx->vo_chain && ao_c->audio_src->pts_reset) { MP_VERBOSE(mpctx, "Reset playback due to audio timestamp reset.\n"); reset_playback_state(mpctx); mpctx->sleeptime = 0; @@ -586,7 +687,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts) int status = AD_OK; bool working = false; if (playsize > mp_audio_buffer_samples(mpctx->ao_buffer)) { - status = audio_decode(d_audio, mpctx->ao_buffer, playsize); + status = filter_audio(mpctx->ao_chain, mpctx->ao_buffer, playsize); if (status == AD_WAIT) return; if (status == AD_NEW_FMT) { diff --git a/player/command.c b/player/command.c index 413349dce3..52f0b1830f 100644 --- a/player/command.c +++ b/player/command.c @@ -1242,9 +1242,9 @@ static int mp_property_filter_metadata(void *ctx, struct m_property *prop, struct vf_chain *vf = mpctx->vo_chain->vf; res = vf_control_by_label(vf, VFCTRL_GET_METADATA, &metadata, key); } else if (strcmp(type, "af") == 0) { - if (!(mpctx->d_audio && mpctx->d_audio->afilter)) + if (!(mpctx->ao_chain && mpctx->ao_chain->af)) return M_PROPERTY_UNAVAILABLE; - struct af_stream *af = mpctx->d_audio->afilter; + struct af_stream *af = mpctx->ao_chain->af; res = af_control_by_label(af, AF_CONTROL_GET_METADATA, &metadata, key); } switch (res) { @@ -1751,8 +1751,8 @@ static int mp_property_audio_params(void *ctx, struct m_property *prop, { MPContext *mpctx = ctx; struct mp_audio fmt = {0}; - if (mpctx->d_audio) - fmt = mpctx->d_audio->decode_format; + if (mpctx->ao_chain) + fmt = mpctx->ao_chain->input_format; return property_audiofmt(fmt, action, arg); } diff --git a/player/core.h b/player/core.h index 89502d4e1b..00d1c81460 100644 --- a/player/core.h +++ b/player/core.h @@ -27,6 +27,7 @@ #include "options/options.h" #include "sub/osd.h" #include "demux/timeline.h" +#include "audio/audio.h" #include "video/mp_image.h" #include "video/out/vo.h" @@ -171,6 +172,25 @@ struct vo_chain { struct dec_video *video_src; }; +// Like vo_chain, for audio. +struct ao_chain { + struct mp_log *log; + + double pts; // timestamp of first sample output by decoder + bool spdif_passthrough, spdif_failed; + + struct af_stream *af; + struct ao *ao; + + // 1-element input frame queue. + struct mp_audio *input_frame; + + // Last known input_mpi format (so vf can be reinitialized any time). + struct mp_audio input_format; + + struct dec_audio *audio_src; +}; + /* Note that playback can be paused, stopped, etc. at any time. While paused, * playback restart is still active, because you want seeking to work even * if paused. @@ -278,6 +298,7 @@ typedef struct MPContext { struct ao *ao; struct mp_audio *ao_decoder_fmt; // for weak gapless audio check struct mp_audio_buffer *ao_buffer; // queued audio; passed to ao_play() later + struct ao_chain *ao_chain; struct vo_chain *vo_chain; diff --git a/player/video.c b/player/video.c index faa8a652bb..18a7ef53ec 100644 --- a/player/video.c +++ b/player/video.c @@ -889,8 +889,8 @@ static double find_best_speed(struct MPContext *mpctx, double vsync) static bool using_spdif_passthrough(struct MPContext *mpctx) { - if (mpctx->d_audio && mpctx->d_audio->afilter) - return !af_fmt_is_pcm(mpctx->d_audio->afilter->input.format); + if (mpctx->ao_chain) + return !af_fmt_is_pcm(mpctx->ao_chain->input_format.format); return false; } -- cgit v1.2.3