summaryrefslogtreecommitdiffstats
path: root/audio/decode/ad_lavc.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-11-12 22:27:44 +0100
committerwm4 <wm4@nowhere>2013-11-12 23:39:09 +0100
commit22b3f522cacfbdba76d311c86efd6091512eb089 (patch)
tree1105af44a9403bde554cd4b6041d05ceea4fb39a /audio/decode/ad_lavc.c
parent5388a0cd4062ba24f5382f025552422fb6430906 (diff)
downloadmpv-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.c161
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)