gstreamer/ext/fdkaac/gstfdkaacdec.c
Vincent Penquerc'h ce59031b10 fdkaacenc: fix accessing freed memory
The buffer data is not always copied in _Fill, and will be
read in _DecodeFrame. We unmap at the end of the function,
whether we get there via failure or early out, and keep a
ref to the buffer to ensure we can use it to unmap the
memory even after _finish_frame is called, as it unrefs
the buffer.

Note that there is an access beyond the allocated buffer,
which is only apparent when playing from souphttpsrc (ie,
not from filesrc). This appears to be a bug in the bit
reading code in libfdkaac AFAICT.

https://bugzilla.gnome.org/show_bug.cgi?id=772186
2016-09-29 15:13:07 +01:00

451 lines
14 KiB
C

/*
* Copyright (C) 2016 Sebastian Dröge <sebastian@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstfdkaacdec.h"
#include <gst/pbutils/pbutils.h>
#include <string.h>
/* TODO:
* - LOAS / LATM support
* - Error concealment
*/
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, "
"mpegversion = (int) 4, "
"stream-format = (string) { adts, adif, raw }")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " GST_AUDIO_NE (S16) ", "
"layout = (string) interleaved, "
"rate = (int) [8000, 96000], " "channels = (int) [1, 8]")
);
GST_DEBUG_CATEGORY_STATIC (gst_fdkaacdec_debug);
#define GST_CAT_DEFAULT gst_fdkaacdec_debug
static gboolean gst_fdkaacdec_start (GstAudioDecoder * dec);
static gboolean gst_fdkaacdec_stop (GstAudioDecoder * dec);
static gboolean gst_fdkaacdec_set_format (GstAudioDecoder * dec,
GstCaps * caps);
static GstFlowReturn gst_fdkaacdec_handle_frame (GstAudioDecoder * dec,
GstBuffer * in_buf);
static void gst_fdkaacdec_flush (GstAudioDecoder * dec, gboolean hard);
G_DEFINE_TYPE (GstFdkAacDec, gst_fdkaacdec, GST_TYPE_AUDIO_DECODER);
static gboolean
gst_fdkaacdec_start (GstAudioDecoder * dec)
{
GstFdkAacDec *self = GST_FDKAACDEC (dec);
GST_DEBUG_OBJECT (self, "start");
return TRUE;
}
static gboolean
gst_fdkaacdec_stop (GstAudioDecoder * dec)
{
GstFdkAacDec *self = GST_FDKAACDEC (dec);
GST_DEBUG_OBJECT (self, "stop");
g_free (self->decode_buffer);
self->decode_buffer = NULL;
if (self->dec)
aacDecoder_Close (self->dec);
self->dec = NULL;
return TRUE;
}
static gboolean
gst_fdkaacdec_set_format (GstAudioDecoder * dec, GstCaps * caps)
{
GstFdkAacDec *self = GST_FDKAACDEC (dec);
TRANSPORT_TYPE transport_format;
GstStructure *s;
const gchar *stream_format;
AAC_DECODER_ERROR err;
if (self->dec) {
/* drain */
gst_fdkaacdec_handle_frame (dec, NULL);
aacDecoder_Close (self->dec);
self->dec = NULL;
}
s = gst_caps_get_structure (caps, 0);
stream_format = gst_structure_get_string (s, "stream-format");
if (strcmp (stream_format, "raw") == 0) {
transport_format = TT_MP4_RAW;
} else if (strcmp (stream_format, "adif") == 0) {
transport_format = TT_MP4_ADIF;
} else if (strcmp (stream_format, "adts") == 0) {
transport_format = TT_MP4_ADTS;
} else {
g_assert_not_reached ();
}
self->dec = aacDecoder_Open (transport_format, 1);
if (!self->dec) {
GST_ERROR_OBJECT (self, "Failed to open decoder");
return FALSE;
}
if (transport_format == TT_MP4_RAW) {
GstBuffer *codec_data = NULL;
GstMapInfo map;
guint8 *data;
guint size;
gst_structure_get (s, "codec_data", GST_TYPE_BUFFER, &codec_data, NULL);
if (!codec_data) {
GST_ERROR_OBJECT (self, "Raw AAC without codec_data not supported");
return FALSE;
}
gst_buffer_map (codec_data, &map, GST_MAP_READ);
data = map.data;
size = map.size;
if ((err = aacDecoder_ConfigRaw (self->dec, &data, &size)) != AAC_DEC_OK) {
gst_buffer_unmap (codec_data, &map);
gst_buffer_unref (codec_data);
GST_ERROR_OBJECT (self, "Invalid codec_data: %d", err);
return FALSE;
}
gst_buffer_unmap (codec_data, &map);
gst_buffer_unref (codec_data);
}
if ((err =
aacDecoder_SetParam (self->dec, AAC_PCM_OUTPUT_CHANNEL_MAPPING,
0)) != AAC_DEC_OK) {
GST_ERROR_OBJECT (self, "Failed to set output channel mapping: %d", err);
return FALSE;
}
if ((err =
aacDecoder_SetParam (self->dec, AAC_PCM_OUTPUT_INTERLEAVED,
1)) != AAC_DEC_OK) {
GST_ERROR_OBJECT (self, "Failed to set interleaved output: %d", err);
return FALSE;
}
/* 8 channels * 2 bytes per sample * 2048 samples */
if (!self->decode_buffer) {
self->decode_buffer_size = 8 * 2048;
self->decode_buffer = g_new (gint16, self->decode_buffer_size);
}
return TRUE;
}
static GstFlowReturn
gst_fdkaacdec_handle_frame (GstAudioDecoder * dec, GstBuffer * inbuf)
{
GstFdkAacDec *self = GST_FDKAACDEC (dec);
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *outbuf;
GstMapInfo imap;
AAC_DECODER_ERROR err;
guint size, valid;
CStreamInfo *stream_info;
GstAudioInfo info;
guint flags = 0, i;
GstAudioChannelPosition pos[64], gst_pos[64];
gboolean need_reorder;
if (inbuf) {
gst_buffer_ref (inbuf);
gst_buffer_map (inbuf, &imap, GST_MAP_READ);
valid = size = imap.size;
if ((err =
aacDecoder_Fill (self->dec, (guint8 **) & imap.data, &size,
&valid)) != AAC_DEC_OK) {
GST_AUDIO_DECODER_ERROR (self, 1, STREAM, DECODE, (NULL),
("filling error: %d", err), ret);
goto out;
}
if (GST_BUFFER_IS_DISCONT (inbuf))
flags |= AACDEC_INTR;
} else {
flags |= AACDEC_FLUSH;
}
if ((err =
aacDecoder_DecodeFrame (self->dec, self->decode_buffer,
self->decode_buffer_size, flags)) != AAC_DEC_OK) {
if (err == AAC_DEC_TRANSPORT_SYNC_ERROR) {
ret = GST_FLOW_OK;
outbuf = NULL;
goto finish;
}
GST_AUDIO_DECODER_ERROR (self, 1, STREAM, DECODE, (NULL),
("decoding error: %d", err), ret);
goto out;
}
stream_info = aacDecoder_GetStreamInfo (self->dec);
if (!stream_info) {
GST_AUDIO_DECODER_ERROR (self, 1, STREAM, DECODE, (NULL),
("failed to get stream info"), ret);
goto out;
}
/* FIXME: Don't recalculate this on every buffer */
if (stream_info->numChannels == 1) {
pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
} else {
gint n_front = 0, n_side = 0, n_back = 0, n_lfe = 0;
/* FIXME: Can this be simplified somehow? */
for (i = 0; i < stream_info->numChannels; i++) {
if (stream_info->pChannelType[i] == ACT_FRONT) {
n_front++;
} else if (stream_info->pChannelType[i] == ACT_SIDE) {
n_side++;
} else if (stream_info->pChannelType[i] == ACT_BACK) {
n_back++;
} else if (stream_info->pChannelType[i] == ACT_LFE) {
n_lfe++;
} else {
GST_ERROR_OBJECT (self, "Channel type %d not supported",
stream_info->pChannelType[i]);
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
}
for (i = 0; i < stream_info->numChannels; i++) {
if (stream_info->pChannelType[i] == ACT_FRONT) {
if (stream_info->pChannelIndices[i] == 0) {
if (n_front & 1)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
else if (n_front > 2)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
else
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
} else if (stream_info->pChannelIndices[i] == 1) {
if ((n_front & 1) && n_front > 3)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
else if (n_front & 1)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
else if (n_front > 2)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
else
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
} else if (stream_info->pChannelIndices[i] == 2) {
if ((n_front & 1) && n_front > 3)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
else if (n_front & 1)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
else if (n_front > 2)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
else
g_assert_not_reached ();
} else if (stream_info->pChannelIndices[i] == 3) {
if ((n_front & 1) && n_front > 3)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
else if (n_front & 1)
g_assert_not_reached ();
else if (n_front > 2)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
else
g_assert_not_reached ();
} else if (stream_info->pChannelIndices[i] == 4) {
if ((n_front & 1) && n_front > 2)
pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
else if (n_front & 1)
g_assert_not_reached ();
else if (n_front > 2)
g_assert_not_reached ();
else
g_assert_not_reached ();
} else {
GST_ERROR_OBJECT (self, "Front channel index %d not supported",
stream_info->pChannelIndices[i]);
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
} else if (stream_info->pChannelType[i] == ACT_SIDE) {
if (n_side & 1) {
GST_ERROR_OBJECT (self, "Odd number of side channels not supported");
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
} else if (stream_info->pChannelIndices[i] == 0) {
pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
} else if (stream_info->pChannelIndices[i] == 1) {
pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
} else {
GST_ERROR_OBJECT (self, "Side channel index %d not supported",
stream_info->pChannelIndices[i]);
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
} else if (stream_info->pChannelType[i] == ACT_BACK) {
if (stream_info->pChannelIndices[i] == 0) {
if (n_back & 1)
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
else
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
} else if (stream_info->pChannelIndices[i] == 1) {
if (n_back & 1)
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
else
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
} else if (stream_info->pChannelIndices[i] == 2) {
if (n_back & 1)
pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
else
g_assert_not_reached ();
} else {
GST_ERROR_OBJECT (self, "Side channel index %d not supported",
stream_info->pChannelIndices[i]);
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
} else if (stream_info->pChannelType[i] == ACT_LFE) {
if (stream_info->pChannelIndices[i] == 0) {
pos[i] = GST_AUDIO_CHANNEL_POSITION_LFE1;
} else {
GST_ERROR_OBJECT (self, "LFE channel index %d not supported",
stream_info->pChannelIndices[i]);
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
} else {
GST_ERROR_OBJECT (self, "Channel type %d not supported",
stream_info->pChannelType[i]);
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
}
}
memcpy (gst_pos, pos,
sizeof (GstAudioChannelPosition) * stream_info->numChannels);
if (!gst_audio_channel_positions_to_valid_order (gst_pos,
stream_info->numChannels)) {
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
need_reorder =
memcmp (pos, gst_pos,
sizeof (GstAudioChannelPosition) * stream_info->numChannels) != 0;
gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S16,
stream_info->sampleRate, stream_info->numChannels, gst_pos);
if (!gst_audio_decoder_set_output_format (dec, &info)) {
GST_ERROR_OBJECT (self, "Failed to set output format");
ret = GST_FLOW_NOT_NEGOTIATED;
goto out;
}
outbuf =
gst_audio_decoder_allocate_output_buffer (dec,
stream_info->frameSize * GST_AUDIO_INFO_BPF (&info));
gst_buffer_fill (outbuf, 0, self->decode_buffer,
gst_buffer_get_size (outbuf));
if (need_reorder) {
gst_audio_buffer_reorder_channels (outbuf, GST_AUDIO_INFO_FORMAT (&info),
GST_AUDIO_INFO_CHANNELS (&info), pos, gst_pos);
}
finish:
ret = gst_audio_decoder_finish_frame (dec, outbuf, 1);
out:
if (inbuf) {
gst_buffer_unmap (inbuf, &imap);
gst_buffer_unref (inbuf);
}
return ret;
}
static void
gst_fdkaacdec_flush (GstAudioDecoder * dec, gboolean hard)
{
GstFdkAacDec *self = GST_FDKAACDEC (dec);
if (self->dec) {
AAC_DECODER_ERROR err;
if ((err =
aacDecoder_DecodeFrame (self->dec, self->decode_buffer,
self->decode_buffer_size, AACDEC_FLUSH)) != AAC_DEC_OK) {
GST_ERROR_OBJECT (self, "flushing error: %d", err);
}
}
}
static void
gst_fdkaacdec_init (GstFdkAacDec * self)
{
self->dec = NULL;
gst_audio_decoder_set_drainable (GST_AUDIO_DECODER (self), TRUE);
gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (self), TRUE);
}
static void
gst_fdkaacdec_class_init (GstFdkAacDecClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
base_class->start = GST_DEBUG_FUNCPTR (gst_fdkaacdec_start);
base_class->stop = GST_DEBUG_FUNCPTR (gst_fdkaacdec_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (gst_fdkaacdec_set_format);
base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_fdkaacdec_handle_frame);
base_class->flush = GST_DEBUG_FUNCPTR (gst_fdkaacdec_flush);
gst_element_class_add_static_pad_template (element_class, &sink_template);
gst_element_class_add_static_pad_template (element_class, &src_template);
gst_element_class_set_static_metadata (element_class, "FDK AAC audio decoder",
"Codec/Decoder/Audio", "FDK AAC audio decoder",
"Sebastian Dröge <sebastian@centricular.com>");
GST_DEBUG_CATEGORY_INIT (gst_fdkaacdec_debug, "fdkaacdec", 0,
"fdkaac decoder");
}