diff options
author | wm4 <wm4@nowhere> | 2013-11-12 22:27:44 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2013-11-12 23:39:09 +0100 |
commit | 22b3f522cacfbdba76d311c86efd6091512eb089 (patch) | |
tree | 1105af44a9403bde554cd4b6041d05ceea4fb39a /audio/decode/ad_lavc.c | |
parent | 5388a0cd4062ba24f5382f025552422fb6430906 (diff) | |
download | mpv-22b3f522cacfbdba76d311c86efd6091512eb089.tar.bz2 mpv-22b3f522cacfbdba76d311c86efd6091512eb089.tar.xz |
audio: add support for using non-interleaved audio from decoders directly
Most libavcodec decoders output non-interleaved audio. Add direct
support for this, and remove the hack that repacked non-interleaved
audio back to packed audio.
Remove the minlen argument from the decoder callback. Instead of
forcing every decoder to have its own decode loop to fill the buffer
until minlen is reached, leave this to the caller. So if a decoder
doesn't return enough data, it's simply called again. (In future, I
even want to change it so that decoders don't read packets directly,
but instead the caller has to pass packets to the decoders. This fits
well with this change, because now the decoder callback typically
decodes at most one packet.)
ad_mpg123.c receives some heavy refactoring. The main problem is that
it wanted to handle format changes when there was no data in the decode
output buffer yet. This sounds reasonable, but actually it would write
data into a buffer prepared for old data, since the caller doesn't know
about the format change yet. (I.e. the best place for a format change
would be _after_ writing the last sample to the output buffer.) It's
possible that this code was not perfectly sane before this commit,
and perhaps lost one frame of data after a format change, but I didn't
confirm this. Trying to fix this, I ended up rewriting the decoding
and also the probing.
Diffstat (limited to 'audio/decode/ad_lavc.c')
-rw-r--r-- | audio/decode/ad_lavc.c | 161 |
1 files changed, 53 insertions, 108 deletions
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c index 1e63f0c3f2..c42c430850 100644 --- a/audio/decode/ad_lavc.c +++ b/audio/decode/ad_lavc.c @@ -36,25 +36,20 @@ #include "mpvcore/av_opts.h" #include "ad.h" -#include "audio/reorder_ch.h" #include "audio/fmt-conversion.h" -#include "compat/mpbswap.h" #include "compat/libav.h" struct priv { AVCodecContext *avctx; AVFrame *avframe; - uint8_t *output; - uint8_t *output_packed; // used by deplanarize to store packed audio samples - int output_left; - int unitsize; + struct mp_audio frame; bool force_channel_map; struct demux_packet *packet; }; static void uninit(sh_audio_t *sh); -static int decode_audio(sh_audio_t *sh,unsigned char *buffer,int minlen,int maxlen); +static int decode_new_packet(struct sh_audio *sh); #define OPT_BASE_STRUCT struct MPOpts @@ -150,22 +145,21 @@ static int preinit(sh_audio_t *sh) return 1; } -/* Prefer playing audio with the samplerate given in container data - * if available, but take number the number of channels and sample format - * from the codec, since if the codec isn't using the correct values for - * those everything breaks anyway. - */ -static int setup_format(sh_audio_t *sh_audio, - const AVCodecContext *lavc_context) +static int setup_format(sh_audio_t *sh_audio) { struct priv *priv = sh_audio->context; - int sample_format = - af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt)); - int samplerate = lavc_context->sample_rate; - // If not set, try container samplerate + AVCodecContext *lavc_context = priv->avctx; + + int sample_format = af_from_avformat(lavc_context->sample_fmt); + if (!sample_format) + return -1; + + int samplerate = lavc_context->sample_rate; if (!samplerate && sh_audio->wf) { + // If not set, try container samplerate. + // (Maybe this can't happen, and it's an artifact from the past.) samplerate = sh_audio->wf->nSamplesPerSec; - mp_tmsg(MSGT_DECAUDIO, MSGL_V, "ad_lavc: using container rate.\n"); + mp_tmsg(MSGT_DECAUDIO, MSGL_WARN, "ad_lavc: using container rate.\n"); } struct mp_chmap lavc_chmap; @@ -178,14 +172,9 @@ static int setup_format(sh_audio_t *sh_audio, lavc_chmap = sh_audio->channels; } - if (!mp_chmap_equals(&lavc_chmap, &sh_audio->channels) || - samplerate != sh_audio->samplerate || - sample_format != sh_audio->sample_format) { - sh_audio->channels = lavc_chmap; - sh_audio->samplerate = samplerate; - sh_audio->sample_format = sample_format; - return 1; - } + sh_audio->channels = lavc_chmap; + sh_audio->samplerate = samplerate; + sh_audio->sample_format = sample_format; return 0; } @@ -285,15 +274,12 @@ static int init(sh_audio_t *sh_audio, const char *decoder) mp_msg(MSGT_DECAUDIO, MSGL_V, "INFO: libavcodec \"%s\" init OK!\n", lavc_codec->name); - // Decode at least 1 byte: (to get header filled) - for (int tries = 0;;) { - int x = decode_audio(sh_audio, sh_audio->a_buffer, 1, - sh_audio->a_buffer_size); - if (x > 0) { - sh_audio->a_buffer_len = x; + // Decode at least 1 sample: (to get header filled) + for (int tries = 1; ; tries++) { + int x = decode_new_packet(sh_audio); + if (x >= 0 && ctx->frame.samples > 0) break; - } - if (++tries >= 5) { + if (tries >= 5) { mp_msg(MSGT_DECAUDIO, MSGL_ERR, "ad_lavc: initial decode failed\n"); uninit(sh_audio); @@ -305,12 +291,6 @@ static int init(sh_audio_t *sh_audio, const char *decoder) if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec) sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec; - int af_sample_fmt = - af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt)); - if (af_sample_fmt == AF_FORMAT_UNKNOWN) { - uninit(sh_audio); - return 0; - } return 1; } @@ -338,7 +318,7 @@ static int control(sh_audio_t *sh, int cmd, void *arg) switch (cmd) { case ADCTRL_RESYNC_STREAM: avcodec_flush_buffers(ctx->avctx); - ctx->output_left = 0; + ctx->frame.samples = 0; talloc_free(ctx->packet); ctx->packet = NULL; return CONTROL_TRUE; @@ -346,29 +326,13 @@ static int control(sh_audio_t *sh, int cmd, void *arg) return CONTROL_UNKNOWN; } -static av_always_inline void deplanarize(struct sh_audio *sh) -{ - struct priv *priv = sh->context; - - uint8_t **planes = priv->avframe->extended_data; - size_t bps = av_get_bytes_per_sample(priv->avctx->sample_fmt); - size_t nb_samples = priv->avframe->nb_samples; - size_t channels = priv->avctx->channels; - size_t size = bps * nb_samples * channels; - - if (talloc_get_size(priv->output_packed) != size) - priv->output_packed = - talloc_realloc_size(priv, priv->output_packed, size); - - reorder_to_packed(priv->output_packed, planes, bps, channels, nb_samples); - - priv->output = priv->output_packed; -} - static int decode_new_packet(struct sh_audio *sh) { struct priv *priv = sh->context; AVCodecContext *avctx = priv->avctx; + + priv->frame.samples = 0; + struct demux_packet *mpkt = priv->packet; if (!mpkt) mpkt = demux_read_packet(sh->gsh); @@ -384,7 +348,7 @@ static int decode_new_packet(struct sh_audio *sh) if (mpkt->pts != MP_NOPTS_VALUE) { sh->pts = mpkt->pts; - sh->pts_bytes = 0; + sh->pts_offset = 0; } int got_frame = 0; int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt); @@ -409,58 +373,39 @@ static int decode_new_packet(struct sh_audio *sh) } if (!got_frame) return 0; - uint64_t unitsize = (uint64_t)av_get_bytes_per_sample(avctx->sample_fmt) * - avctx->channels; - if (unitsize > 100000) - abort(); - priv->unitsize = unitsize; - uint64_t output_left = unitsize * priv->avframe->nb_samples; - if (output_left > 500000000) - abort(); - priv->output_left = output_left; - if (av_sample_fmt_is_planar(avctx->sample_fmt) && avctx->channels > 1) { - deplanarize(sh); - } else { - priv->output = priv->avframe->data[0]; - } - mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d \n", in_len, - priv->output_left); + + if (setup_format(sh) < 0) + return -1; + + priv->frame.samples = priv->avframe->nb_samples; + mp_audio_set_format(&priv->frame, sh->sample_format); + mp_audio_set_channels(&priv->frame, &sh->channels); + priv->frame.rate = sh->samplerate; + for (int n = 0; n < priv->frame.num_planes; n++) + priv->frame.planes[n] = priv->avframe->data[n]; + + mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d samples\n", in_len, + priv->frame.samples); return 0; } - -static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen, - int maxlen) +static int decode_audio(sh_audio_t *sh, struct mp_audio *buffer, int maxlen) { - struct priv *priv = sh_audio->context; - AVCodecContext *avctx = priv->avctx; + struct priv *priv = sh->context; - int len = -1; - while (len < minlen) { - if (!priv->output_left) { - if (decode_new_packet(sh_audio) < 0) - break; - continue; - } - if (setup_format(sh_audio, avctx)) - return len; - int size = (minlen - len + priv->unitsize - 1); - size -= size % priv->unitsize; - size = FFMIN(size, priv->output_left); - if (size > maxlen) - abort(); - memcpy(buf, priv->output, size); - priv->output += size; - priv->output_left -= size; - if (len < 0) - len = size; - else - len += size; - buf += size; - maxlen -= size; - sh_audio->pts_bytes += size; + if (!priv->frame.samples) { + if (decode_new_packet(sh) < 0) + return -1; } - return len; + + if (!mp_audio_config_equals(buffer, &priv->frame)) + return 0; + + buffer->samples = MPMIN(priv->frame.samples, maxlen); + mp_audio_copy(buffer, 0, &priv->frame, 0, buffer->samples); + mp_audio_skip_samples(&priv->frame, buffer->samples); + sh->pts_offset += buffer->samples; + return 0; } static void add_decoders(struct mp_decoder_list *list) |