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 +++++----- 4 files changed, 102 insertions(+), 148 deletions(-) (limited to 'audio') 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 */ -- cgit v1.2.3