ad_ffmpeg: switch to avcodec_decode_audio4()

Switch libavcodec audio decoding from avcodec_decode_audio3() to
avcodec_decode_audio4(). Instead of decoding directly to the output
buffer, the data is now copied from the libavcodec output packet,
adding an extra memory copy (optimizing this would require some
interface changes).

After libavcodec added avcodec_decode_audio4() earlier, it dropped
support for splitting large audio packets into output chunks of size
AVCODEC_MAX_AUDIO_FRAME_SIZE or less. This caused a regression with
the previous API: audio files with huge packets could fail to decode,
as libavcodec refused to write into the AVCODEC_MAX_AUDIO_FRAME_SIZE
buffer provided by mplayer2. This occurrend mainly with some lossless
audio formats. This commit restores support for those files; there are
now no fixed limits on packet size.
This commit is contained in:
Uoti Urpala 2012-04-19 01:26:56 +03:00
parent b9fefc87c0
commit deffd15a05
1 changed files with 112 additions and 69 deletions

View File

@ -49,12 +49,15 @@ LIBAD_EXTERN(ffmpeg)
struct priv { struct priv {
AVCodecContext *avctx; AVCodecContext *avctx;
int previous_data_left; AVFrame *avframe;
char *output;
int output_left;
int unitsize;
int previous_data_left; // input demuxer packet data
}; };
static int preinit(sh_audio_t *sh) static int preinit(sh_audio_t *sh)
{ {
sh->audio_out_minsize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
return 1; return 1;
} }
@ -74,6 +77,7 @@ static int setup_format(sh_audio_t *sh_audio,
case AV_SAMPLE_FMT_FLT: sample_format = AF_FORMAT_FLOAT_NE; break; case AV_SAMPLE_FMT_FLT: sample_format = AF_FORMAT_FLOAT_NE; break;
default: default:
mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Unsupported sample format\n"); mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Unsupported sample format\n");
sample_format = AF_FORMAT_UNKNOWN;
} }
bool broken_srate = false; bool broken_srate = false;
@ -122,6 +126,7 @@ static int init(sh_audio_t *sh_audio)
sh_audio->context = ctx; sh_audio->context = ctx;
lavc_context = avcodec_alloc_context3(lavc_codec); lavc_context = avcodec_alloc_context3(lavc_codec);
ctx->avctx = lavc_context; ctx->avctx = lavc_context;
ctx->avframe = avcodec_alloc_frame();
// Always try to set - option only exists for AC3 at the moment // Always try to set - option only exists for AC3 at the moment
av_opt_set_double(lavc_context, "drc_scale", opts->drc_level, av_opt_set_double(lavc_context, "drc_scale", opts->drc_level,
@ -223,6 +228,7 @@ static void uninit(sh_audio_t *sh)
av_freep(&lavc_context->extradata); av_freep(&lavc_context->extradata);
av_freep(&lavc_context); av_freep(&lavc_context);
} }
av_free(ctx->avframe);
talloc_free(ctx); talloc_free(ctx);
sh->context = NULL; sh->context = NULL;
} }
@ -235,86 +241,123 @@ static int control(sh_audio_t *sh, int cmd, void *arg, ...)
avcodec_flush_buffers(ctx->avctx); avcodec_flush_buffers(ctx->avctx);
ds_clear_parser(sh->ds); ds_clear_parser(sh->ds);
ctx->previous_data_left = 0; ctx->previous_data_left = 0;
ctx->output_left = 0;
return CONTROL_TRUE; return CONTROL_TRUE;
} }
return CONTROL_UNKNOWN; return CONTROL_UNKNOWN;
} }
static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen, static int decode_new_packet(struct sh_audio *sh)
int maxlen)
{ {
struct priv *ctx = sh_audio->context; struct priv *priv = sh->context;
AVCodecContext *avctx = ctx->avctx; AVCodecContext *avctx = priv->avctx;
unsigned char *start = NULL;
int y, len = -1;
while (len < minlen) {
AVPacket pkt;
int len2 = maxlen;
double pts = MP_NOPTS_VALUE; double pts = MP_NOPTS_VALUE;
int x; int insize;
bool packet_already_used = ctx->previous_data_left; bool packet_already_used = priv->previous_data_left;
struct demux_packet *mpkt = ds_get_packet2(sh_audio->ds, struct demux_packet *mpkt = ds_get_packet2(sh->ds,
ctx->previous_data_left); priv->previous_data_left);
unsigned char *start;
if (!mpkt) { if (!mpkt) {
assert(!ctx->previous_data_left); assert(!priv->previous_data_left);
start = NULL; start = NULL;
x = 0; insize = 0;
ds_parse(sh_audio->ds, &start, &x, pts, 0); ds_parse(sh->ds, &start, &insize, pts, 0);
if (x <= 0) if (insize <= 0)
break; // error return -1; // error or EOF
} else { } else {
assert(mpkt->len >= ctx->previous_data_left); assert(mpkt->len >= priv->previous_data_left);
if (!ctx->previous_data_left) { if (!priv->previous_data_left) {
ctx->previous_data_left = mpkt->len; priv->previous_data_left = mpkt->len;
pts = mpkt->pts; pts = mpkt->pts;
} }
x = ctx->previous_data_left; insize = priv->previous_data_left;
start = mpkt->buffer + mpkt->len - ctx->previous_data_left; start = mpkt->buffer + mpkt->len - priv->previous_data_left;
int consumed = ds_parse(sh_audio->ds, &start, &x, pts, 0); int consumed = ds_parse(sh->ds, &start, &insize, pts, 0);
ctx->previous_data_left -= consumed; priv->previous_data_left -= consumed;
} }
AVPacket pkt;
av_init_packet(&pkt); av_init_packet(&pkt);
pkt.data = start; pkt.data = start;
pkt.size = x; pkt.size = insize;
if (mpkt && mpkt->avpacket) { if (mpkt && mpkt->avpacket) {
pkt.side_data = mpkt->avpacket->side_data; pkt.side_data = mpkt->avpacket->side_data;
pkt.side_data_elems = mpkt->avpacket->side_data_elems; pkt.side_data_elems = mpkt->avpacket->side_data_elems;
} }
if (pts != MP_NOPTS_VALUE && !packet_already_used) { if (pts != MP_NOPTS_VALUE && !packet_already_used) {
sh_audio->pts = pts; sh->pts = pts;
sh_audio->pts_bytes = 0; sh->pts_bytes = 0;
} }
y = avcodec_decode_audio3(avctx, (int16_t *)buf, &len2, &pkt); int got_frame = 0;
int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt);
// LATM may need many packets to find mux info // LATM may need many packets to find mux info
if (y == AVERROR(EAGAIN)) if (ret == AVERROR(EAGAIN))
continue; return 0;
if (y < 0) { if (ret < 0) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "lavc_audio: error\n"); mp_msg(MSGT_DECAUDIO, MSGL_V, "lavc_audio: error\n");
break; return -1;
} }
if (!sh_audio->parser) if (!sh->parser)
ctx->previous_data_left += x - y; priv->previous_data_left += insize - ret;
if (len2 > 0) { if (!got_frame)
return 0;
/* An error is reported later from output format checking, but make
* sure we don't crash by overreading first plane. */
if (av_sample_fmt_is_planar(avctx->sample_fmt) && avctx->channels > 1)
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;
priv->output = priv->avframe->data[0];
mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d \n", insize,
priv->output_left);
return 0;
}
static int decode_audio(sh_audio_t *sh_audio, unsigned char *buf, int minlen,
int maxlen)
{
struct priv *priv = sh_audio->context;
AVCodecContext *avctx = priv->avctx;
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 (avctx->channels >= 5) { if (avctx->channels >= 5) {
int samplesize = av_get_bytes_per_sample(avctx->sample_fmt); int samplesize = av_get_bytes_per_sample(avctx->sample_fmt);
reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_LAVC_DEFAULT, reorder_channel_nch(buf, AF_CHANNEL_LAYOUT_LAVC_DEFAULT,
AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT,
avctx->channels, avctx->channels,
len2 / samplesize, samplesize); size / samplesize, samplesize);
} }
if (len < 0) if (len < 0)
len = len2; len = size;
else else
len += len2; len += size;
buf += len2; buf += size;
maxlen -= len2; maxlen -= size;
sh_audio->pts_bytes += len2; sh_audio->pts_bytes += size;
}
mp_dbg(MSGT_DECAUDIO, MSGL_DBG2, "Decoded %d -> %d \n", y, len2);
if (setup_format(sh_audio, avctx))
break;
} }
return len; return len;
} }