Commit ef8a1bdd authored by Tim-Philipp Müller's avatar Tim-Philipp Müller 🐙

avauddec: fix decoding of APE and Cook audio

.. and other formats where ffmpeg gives us multiple
subframes per input frame.

Since we now support non-interleaved audio, we can't
just concat buffers any more. Also, audio metas won't
be combined when buffers are merged, so when we push
out the combined buffer we'll look at the meta describing
only the first subframe and think it covers the whole
frame leading to stutter/gaps in the output.

We could fix this by copying the output data into a new
buffer when we merge buffers, but that's suboptimal, so
let's add some API to GstAudioDecoder to push out subframes
and use that instead.

parent caf953bd
Pipeline #22546 passed with stages
in 17 minutes and 10 seconds
......@@ -188,7 +188,6 @@ gst_ffmpegauddec_close (GstFFMpegAudDec * ffmpegdec, gboolean reset)
GST_LOG_OBJECT (ffmpegdec, "closing libav codec");
gst_caps_replace (&ffmpegdec->last_caps, NULL);
gst_buffer_replace (&ffmpegdec->outbuf, NULL);
gst_ffmpeg_avcodec_close (ffmpegdec->context);
ffmpegdec->opened = FALSE;
......@@ -576,12 +575,10 @@ gst_ffmpegauddec_frame (GstFFMpegAudDec * ffmpegdec, GstFlowReturn * ret)
gst_ffmpegauddec_audio_frame (ffmpegdec, oclass->in_plugin, &outbuf, ret);
if (outbuf) {
GST_LOG_OBJECT (ffmpegdec, "Decoded data, now storing buffer %p", outbuf);
if (ffmpegdec->outbuf)
ffmpegdec->outbuf = gst_buffer_append (ffmpegdec->outbuf, outbuf);
ffmpegdec->outbuf = outbuf;
GST_LOG_OBJECT (ffmpegdec, "Decoded data, buffer %" GST_PTR_FORMAT, outbuf);
*ret =
gst_audio_decoder_finish_subframe (GST_AUDIO_DECODER_CAST (ffmpegdec),
} else {
GST_DEBUG_OBJECT (ffmpegdec, "We didn't get a decoded buffer");
......@@ -601,6 +598,7 @@ static void
gst_ffmpegauddec_drain (GstFFMpegAudDec * ffmpegdec)
GstFFMpegAudDecClass *oclass;
gboolean got_any_frames = FALSE;
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
......@@ -617,14 +615,14 @@ gst_ffmpegauddec_drain (GstFFMpegAudDec * ffmpegdec)
GstFlowReturn ret;
got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret);
if (got_frame)
got_any_frames = TRUE;
} while (got_frame);
avcodec_flush_buffers (ffmpegdec->context);
if (ffmpegdec->outbuf)
gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (ffmpegdec),
ffmpegdec->outbuf, 1);
ffmpegdec->outbuf = NULL;
if (got_any_frames)
gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (ffmpegdec), NULL, 1);
GST_WARNING_OBJECT (ffmpegdec, "send packet failed, could not drain decoder");
......@@ -648,6 +646,7 @@ gst_ffmpegauddec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf)
guint8 *data;
GstMapInfo map;
gint size;
gboolean got_any_frames = FALSE;
gboolean got_frame;
GstFlowReturn ret = GST_FLOW_OK;
gboolean is_header;
......@@ -719,6 +718,9 @@ gst_ffmpegauddec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf)
/* decode a frame of audio now */
got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret);
if (got_frame)
got_any_frames = TRUE;
if (ret != GST_FLOW_OK) {
GST_LOG_OBJECT (ffmpegdec, "breaking because of flow ret %s",
gst_flow_get_name (ret));
......@@ -730,14 +732,10 @@ gst_ffmpegauddec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf)
gst_buffer_unmap (inbuf, &map);
gst_buffer_unref (inbuf);
if (ffmpegdec->outbuf)
ret =
gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (ffmpegdec),
ffmpegdec->outbuf, 1);
else if (is_header)
if (is_header || got_any_frames) {
ret =
gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (ffmpegdec), NULL, 1);
ffmpegdec->outbuf = NULL;
return ret;
......@@ -44,9 +44,6 @@ struct _GstFFMpegAudDec
/* prevent reopening the decoder on GST_EVENT_CAPS when caps are same as last time. */
GstCaps *last_caps;
/* Stores current buffers to push as GstAudioDecoder wants 1:1 mapping for input/output buffers */
GstBuffer *outbuf;
/* current output format */
GstAudioInfo info;
GstAudioChannelPosition ffmpeg_layout[64];
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment