aacparse: allow conversion from ADTS to raw AAC

Some muxers (eg, qtmux) only support raw AAC, so this allows linking
an encoder that outputs ADTS only to those muxers.

The conversion is simple (omit the first 7 or 9 bytes of the frame),
but has to be done in pre_push instead of handle_frame as 1.0 does
not seem to allow skipping bytes there as 0.10 used to.

Other conversions are not supported (yet).
This commit is contained in:
Vincent Penquerc'h 2013-07-15 17:11:45 +01:00
parent 55e9338846
commit 91d4abceaa
3 changed files with 80 additions and 17 deletions

View file

@ -8,6 +8,7 @@ libgstaudioparsers_la_SOURCES = \
libgstaudioparsers_la_CFLAGS = \ libgstaudioparsers_la_CFLAGS = \
$(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
libgstaudioparsers_la_LIBADD = \ libgstaudioparsers_la_LIBADD = \
-lgstpbutils-$(GST_API_VERSION) \
$(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_API_VERSION) \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_API_VERSION) \
-lgstaudio-$(GST_API_VERSION) \ -lgstaudio-$(GST_API_VERSION) \
$(GST_BASE_LIBS) $(GST_LIBS) $(GST_BASE_LIBS) $(GST_LIBS)

View file

@ -45,6 +45,7 @@
#include <string.h> #include <string.h>
#include <gst/base/gstbitreader.h> #include <gst/base/gstbitreader.h>
#include <gst/pbutils/codec-utils.h>
#include "gstaacparse.h" #include "gstaacparse.h"
@ -91,22 +92,11 @@ static GstCaps *gst_aac_parse_sink_getcaps (GstBaseParse * parse,
static GstFlowReturn gst_aac_parse_handle_frame (GstBaseParse * parse, static GstFlowReturn gst_aac_parse_handle_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, gint * skipsize); GstBaseParseFrame * frame, gint * skipsize);
static GstFlowReturn gst_aac_parse_pre_push_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
G_DEFINE_TYPE (GstAacParse, gst_aac_parse, GST_TYPE_BASE_PARSE); G_DEFINE_TYPE (GstAacParse, gst_aac_parse, GST_TYPE_BASE_PARSE);
static inline gint
gst_aac_parse_get_sample_rate_from_index (guint sr_idx)
{
static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100,
32000, 24000, 22050, 16000, 12000, 11025, 8000
};
if (sr_idx < G_N_ELEMENTS (aac_sample_rates))
return aac_sample_rates[sr_idx];
GST_WARNING ("Invalid sample rate index %u", sr_idx);
return 0;
}
/** /**
* gst_aac_parse_class_init: * gst_aac_parse_class_init:
* @klass: #GstAacParseClass. * @klass: #GstAacParseClass.
@ -135,6 +125,8 @@ gst_aac_parse_class_init (GstAacParseClass * klass)
parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_aac_parse_sink_setcaps); parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_aac_parse_sink_setcaps);
parse_class->get_sink_caps = GST_DEBUG_FUNCPTR (gst_aac_parse_sink_getcaps); parse_class->get_sink_caps = GST_DEBUG_FUNCPTR (gst_aac_parse_sink_getcaps);
parse_class->handle_frame = GST_DEBUG_FUNCPTR (gst_aac_parse_handle_frame); parse_class->handle_frame = GST_DEBUG_FUNCPTR (gst_aac_parse_handle_frame);
parse_class->pre_push_frame =
GST_DEBUG_FUNCPTR (gst_aac_parse_pre_push_frame);
} }
@ -165,9 +157,11 @@ static gboolean
gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps) gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
{ {
GstStructure *s; GstStructure *s;
GstCaps *src_caps = NULL; GstCaps *src_caps = NULL, *allowed;
gboolean res = FALSE; gboolean res = FALSE;
const gchar *stream_format; const gchar *stream_format;
GstBuffer *codec_data;
guint16 codec_data_data;
GST_DEBUG_OBJECT (aacparse, "sink caps: %" GST_PTR_FORMAT, sink_caps); GST_DEBUG_OBJECT (aacparse, "sink caps: %" GST_PTR_FORMAT, sink_caps);
if (sink_caps) if (sink_caps)
@ -178,6 +172,7 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
gst_caps_set_simple (src_caps, "framed", G_TYPE_BOOLEAN, TRUE, gst_caps_set_simple (src_caps, "framed", G_TYPE_BOOLEAN, TRUE,
"mpegversion", G_TYPE_INT, aacparse->mpegversion, NULL); "mpegversion", G_TYPE_INT, aacparse->mpegversion, NULL);
aacparse->output_header_type = aacparse->header_type;
switch (aacparse->header_type) { switch (aacparse->header_type) {
case DSPAAC_HEADER_NONE: case DSPAAC_HEADER_NONE:
stream_format = "raw"; stream_format = "raw";
@ -203,11 +198,55 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
if (stream_format) if (stream_format)
gst_structure_set (s, "stream-format", G_TYPE_STRING, stream_format, NULL); gst_structure_set (s, "stream-format", G_TYPE_STRING, stream_format, NULL);
allowed = gst_pad_get_allowed_caps (GST_BASE_PARSE (aacparse)->srcpad);
if (!gst_caps_can_intersect (src_caps, allowed)) {
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Caps can not intersect");
if (aacparse->header_type == DSPAAC_HEADER_ADTS) {
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Input is ADTS, trying raw");
gst_caps_set_simple (src_caps, "stream-format", G_TYPE_STRING, "raw",
NULL);
if (gst_caps_can_intersect (src_caps, allowed)) {
GstMapInfo map;
int idx;
idx =
gst_codec_utils_aac_get_index_from_sample_rate
(aacparse->sample_rate);
if (idx < 0)
goto not_a_known_rate;
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Caps can intersect, we will drop the ADTS layer");
aacparse->output_header_type = DSPAAC_HEADER_NONE;
/* The codec_data data is according to AudioSpecificConfig,
ISO/IEC 14496-3, 1.6.2.1 */
codec_data = gst_buffer_new_and_alloc (2);
gst_buffer_map (codec_data, &map, GST_MAP_WRITE);
codec_data_data =
(aacparse->object_type << 11) |
(idx << 7) | (aacparse->channels << 3);
GST_WRITE_UINT16_BE (map.data, codec_data_data);
gst_buffer_unmap (codec_data, &map);
gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER,
codec_data, NULL);
}
}
}
gst_caps_unref (allowed);
GST_DEBUG_OBJECT (aacparse, "setting src caps: %" GST_PTR_FORMAT, src_caps); GST_DEBUG_OBJECT (aacparse, "setting src caps: %" GST_PTR_FORMAT, src_caps);
res = gst_pad_set_caps (GST_BASE_PARSE (aacparse)->srcpad, src_caps); res = gst_pad_set_caps (GST_BASE_PARSE (aacparse)->srcpad, src_caps);
gst_caps_unref (src_caps); gst_caps_unref (src_caps);
return res; return res;
not_a_known_rate:
gst_caps_unref (allowed);
gst_caps_unref (src_caps);
return FALSE;
} }
@ -250,7 +289,8 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
sr_idx = ((map.data[0] & 0x07) << 1) | ((map.data[1] & 0x80) >> 7); sr_idx = ((map.data[0] & 0x07) << 1) | ((map.data[1] & 0x80) >> 7);
aacparse->object_type = (map.data[0] & 0xf8) >> 3; aacparse->object_type = (map.data[0] & 0xf8) >> 3;
aacparse->sample_rate = gst_aac_parse_get_sample_rate_from_index (sr_idx); aacparse->sample_rate =
gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
aacparse->channels = (map.data[1] & 0x78) >> 3; aacparse->channels = (map.data[1] & 0x78) >> 3;
aacparse->header_type = DSPAAC_HEADER_NONE; aacparse->header_type = DSPAAC_HEADER_NONE;
aacparse->mpegversion = 4; aacparse->mpegversion = 4;
@ -669,7 +709,7 @@ gst_aac_parse_parse_adts_header (GstAacParse * aacparse, const guint8 * data,
if (rate) { if (rate) {
gint sr_idx = (data[2] & 0x3c) >> 2; gint sr_idx = (data[2] & 0x3c) >> 2;
*rate = gst_aac_parse_get_sample_rate_from_index (sr_idx); *rate = gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
} }
if (channels) if (channels)
*channels = ((data[2] & 0x01) << 2) | ((data[3] & 0xc0) >> 6); *channels = ((data[2] & 0x01) << 2) | ((data[3] & 0xc0) >> 6);
@ -860,7 +900,8 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse,
/* FIXME: This gives totally wrong results. Duration calculation cannot /* FIXME: This gives totally wrong results. Duration calculation cannot
be based on this */ be based on this */
aacparse->sample_rate = gst_aac_parse_get_sample_rate_from_index (sr_idx); aacparse->sample_rate =
gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
/* baseparse is not given any fps, /* baseparse is not given any fps,
* so it will give up on timestamps, seeking, etc */ * so it will give up on timestamps, seeking, etc */
@ -1056,6 +1097,26 @@ exit:
return GST_FLOW_OK; return GST_FLOW_OK;
} }
static GstFlowReturn
gst_aac_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
{
GstAacParse *aacparse = GST_AAC_PARSE (parse);
/* As a special case, we can remove the ADTS framing and output raw AAC. */
if (aacparse->header_type == DSPAAC_HEADER_ADTS
&& aacparse->output_header_type == DSPAAC_HEADER_NONE) {
guint header_size;
GstMapInfo map;
gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
header_size = (map.data[1] & 1) ? 7 : 9; /* optional CRC */
gst_buffer_unmap (frame->buffer, &map);
gst_buffer_resize (frame->buffer, header_size,
gst_buffer_get_size (frame->buffer) - header_size);
}
return GST_FLOW_OK;
}
/** /**
* gst_aac_parse_start: * gst_aac_parse_start:

View file

@ -80,6 +80,7 @@ struct _GstAacParse {
gint frame_samples; gint frame_samples;
GstAacHeaderType header_type; GstAacHeaderType header_type;
GstAacHeaderType output_header_type;
}; };
/** /**