/**
* SECTION:element-aacparse
+ * @title: aacparse
* @short_description: AAC parser
* @see_also: #GstAmrParse
*
* be determined either. However, ADTS format AAC clips can be seeked, and parser
* can also estimate playback position and clip duration.
*
- * <refsect2>
- * <title>Example launch line</title>
+ * ## Example launch line
* |[
* gst-launch-1.0 filesrc location=abc.aac ! aacparse ! faad ! audioresample ! audioconvert ! alsasink
* ]|
- * </refsect2>
+ *
*/
#ifdef HAVE_CONFIG_H
#define ADIF_MAX_SIZE 40 /* Should be enough */
#define ADTS_MAX_SIZE 10 /* Should be enough */
#define LOAS_MAX_SIZE 3 /* Should be enough */
+#define RAW_MAX_SIZE 1 /* Correct framing is required */
#define ADTS_HEADERS_LENGTH 7UL /* Total byte-length of fixed and variable
headers prepended during raw to ADTS
GstBaseParseFrame * frame, gint * skipsize);
static GstFlowReturn gst_aac_parse_pre_push_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
+static gboolean gst_aac_parse_src_event (GstBaseParse * parse,
+ GstEvent * event);
+
+static gboolean gst_aac_parse_read_audio_specific_config (GstAacParse *
+ aacparse, GstBitReader * br, gint * object_type, gint * sample_rate,
+ gint * channels, gint * frame_samples);
+
+#define gst_aac_parse_parent_class parent_class
G_DEFINE_TYPE (GstAacParse, gst_aac_parse, GST_TYPE_BASE_PARSE);
/**
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);
+ parse_class->src_event = GST_DEBUG_FUNCPTR (gst_aac_parse_src_event);
}
GST_DEBUG ("initialized");
GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (aacparse));
GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (aacparse));
+
+ aacparse->last_parsed_sample_rate = 0;
+ aacparse->last_parsed_channels = 0;
}
gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
{
GstStructure *s;
- GstCaps *src_caps = NULL, *allowed;
+ GstCaps *src_caps = NULL, *peercaps;
gboolean res = FALSE;
const gchar *stream_format;
guint8 codec_data[2];
if (stream_format)
gst_structure_set (s, "stream-format", G_TYPE_STRING, stream_format, NULL);
- allowed = gst_pad_get_allowed_caps (GST_BASE_PARSE (aacparse)->srcpad);
- if (allowed && !gst_caps_can_intersect (src_caps, allowed)) {
+ peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (aacparse), NULL);
+ if (peercaps && !gst_caps_can_intersect (src_caps, peercaps)) {
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Caps can not intersect");
if (aacparse->header_type == DSPAAC_HEADER_ADTS) {
"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)) {
+ if (gst_caps_can_intersect (src_caps, peercaps)) {
GstBuffer *codec_data_buffer;
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
gst_buffer_fill (codec_data_buffer, 0, codec_data, 2);
gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER,
codec_data_buffer, NULL);
+ gst_buffer_unref (codec_data_buffer);
}
} else if (aacparse->header_type == DSPAAC_HEADER_NONE) {
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Input is raw, trying ADTS");
gst_caps_set_simple (src_caps, "stream-format", G_TYPE_STRING, "adts",
NULL);
- if (gst_caps_can_intersect (src_caps, allowed)) {
+ if (gst_caps_can_intersect (src_caps, peercaps)) {
GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
"Caps can intersect, we will prepend ADTS headers");
aacparse->output_header_type = DSPAAC_HEADER_ADTS;
}
}
}
- if (allowed)
- gst_caps_unref (allowed);
+ if (peercaps)
+ gst_caps_unref (peercaps);
+
+ aacparse->last_parsed_channels = 0;
+ aacparse->last_parsed_sample_rate = 0;
GST_DEBUG_OBJECT (aacparse, "setting src caps: %" GST_PTR_FORMAT, src_caps);
if (value) {
GstBuffer *buf = gst_value_get_buffer (value);
- if (buf) {
+ if (buf && gst_buffer_get_size (buf) >= 2) {
GstMapInfo map;
- guint sr_idx;
-
- gst_buffer_map (buf, &map, GST_MAP_READ);
-
- sr_idx = ((map.data[0] & 0x07) << 1) | ((map.data[1] & 0x80) >> 7);
- aacparse->object_type = (map.data[0] & 0xf8) >> 3;
- aacparse->sample_rate =
- gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
- aacparse->channels = (map.data[1] & 0x78) >> 3;
- if (aacparse->channels == 7)
- aacparse->channels = 8;
- else if (aacparse->channels == 11)
- aacparse->channels = 7;
- else if (aacparse->channels == 12 || aacparse->channels == 14)
- aacparse->channels = 8;
+ GstBitReader br;
+
+ if (!gst_buffer_map (buf, &map, GST_MAP_READ))
+ return FALSE;
+ gst_bit_reader_init (&br, map.data, map.size);
+ gst_aac_parse_read_audio_specific_config (aacparse, &br,
+ &aacparse->object_type, &aacparse->sample_rate, &aacparse->channels,
+ &aacparse->frame_samples);
+
aacparse->header_type = DSPAAC_HEADER_NONE;
aacparse->mpegversion = 4;
- aacparse->frame_samples = (map.data[1] & 4) ? 960 : 1024;
gst_buffer_unmap (buf, &map);
GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, "
gst_aac_parse_set_src_caps (aacparse, caps);
if (aacparse->header_type == aacparse->output_header_type)
gst_base_parse_set_passthrough (parse, TRUE);
+
+ /* input is already correctly framed */
+ gst_base_parse_set_min_frame_size (parse, RAW_MAX_SIZE);
} else {
return FALSE;
}
gst_base_parse_set_passthrough (parse, FALSE);
}
}
-
return TRUE;
}
if (!*sample_rate)
return FALSE;
}
+ aacparse->last_parsed_sample_rate = *sample_rate;
return TRUE;
}
/* See table 1.13 in ISO/IEC 14496-3 */
static gboolean
-gst_aac_parse_read_loas_audio_specific_config (GstAacParse * aacparse,
- GstBitReader * br, gint * sample_rate, gint * channels, guint32 * bits)
+gst_aac_parse_read_audio_specific_config (GstAacParse * aacparse,
+ GstBitReader * br, gint * object_type, gint * sample_rate, gint * channels,
+ gint * frame_samples)
{
- guint8 audio_object_type, channel_configuration;
+ guint8 audio_object_type;
+ guint8 G_GNUC_UNUSED extension_audio_object_type;
+ guint8 channel_configuration, extension_channel_configuration;
+ gboolean G_GNUC_UNUSED sbr = FALSE, ps = FALSE;
if (!gst_aac_parse_get_audio_object_type (aacparse, br, &audio_object_type))
return FALSE;
+ if (object_type)
+ *object_type = audio_object_type;
if (!gst_aac_parse_get_audio_sample_rate (aacparse, br, sample_rate))
return FALSE;
if (!gst_bit_reader_get_bits_uint8 (br, &channel_configuration, 4))
return FALSE;
- GST_LOG_OBJECT (aacparse, "channel_configuration: %d", channel_configuration);
*channels = loas_channels_table[channel_configuration];
+ GST_LOG_OBJECT (aacparse, "channel_configuration: %d", channel_configuration);
if (!*channels)
return FALSE;
- if (audio_object_type == 5) {
+ if (audio_object_type == 5 || audio_object_type == 29) {
+ extension_audio_object_type = 5;
+ sbr = TRUE;
+ if (audio_object_type == 29) {
+ ps = TRUE;
+ /* Parametric stereo. If we have a one-channel configuration, we can
+ * override it to stereo */
+ if (*channels == 1)
+ *channels = 2;
+ }
+
GST_LOG_OBJECT (aacparse,
- "Audio object type 5, so rereading sampling rate...");
+ "Audio object type 5 or 29, so rereading sampling rate (was %d)...",
+ *sample_rate);
if (!gst_aac_parse_get_audio_sample_rate (aacparse, br, sample_rate))
return FALSE;
+
+ if (!gst_aac_parse_get_audio_object_type (aacparse, br, &audio_object_type))
+ return FALSE;
+
+ if (audio_object_type == 22) {
+ /* extension channel configuration */
+ if (!gst_bit_reader_get_bits_uint8 (br, &extension_channel_configuration,
+ 4))
+ return FALSE;
+ GST_LOG_OBJECT (aacparse, "extension channel_configuration: %d",
+ extension_channel_configuration);
+ *channels = loas_channels_table[extension_channel_configuration];
+ if (!*channels)
+ return FALSE;
+ }
+ } else {
+ extension_audio_object_type = 0;
}
- GST_INFO_OBJECT (aacparse, "Found LOAS config: %d Hz, %d channels",
+ GST_INFO_OBJECT (aacparse, "Parsed AudioSpecificConfig: %d Hz, %d channels",
*sample_rate, *channels);
+ if (frame_samples && audio_object_type == 23) {
+ guint8 frame_flag;
+ /* Read the Decoder Configuration (GASpecificConfig) if present */
+ /* We only care about the first bit to know what the number of samples
+ * in a frame is */
+ if (!gst_bit_reader_get_bits_uint8 (br, &frame_flag, 1))
+ return FALSE;
+ *frame_samples = frame_flag ? 960 : 1024;
+ }
+
/* There's LOTS of stuff next, but we ignore it for now as we have
what we want (sample rate and number of channels */
GST_DEBUG_OBJECT (aacparse,
"Need more code to parse humongous LOAS data, currently ignored");
- if (bits)
- *bits = 0;
+ aacparse->last_parsed_channels = *channels;
return TRUE;
}
return FALSE;
if (u8) {
GST_LOG_OBJECT (aacparse, "Frame uses previous config");
- if (!aacparse->sample_rate || !aacparse->channels) {
+ if (!aacparse->last_parsed_sample_rate || !aacparse->last_parsed_channels) {
GST_DEBUG_OBJECT (aacparse,
"No previous config to use. We'll look for more data.");
return FALSE;
}
- *sample_rate = aacparse->sample_rate;
- *channels = aacparse->channels;
+ *sample_rate = aacparse->last_parsed_sample_rate;
+ *channels = aacparse->last_parsed_channels;
return TRUE;
}
GST_DEBUG_OBJECT (aacparse, "Frame contains new config");
+ /* audioMuxVersion */
if (!gst_bit_reader_get_bits_uint8 (&br, &v, 1))
return FALSE;
if (v) {
+ /* audioMuxVersionA */
if (!gst_bit_reader_get_bits_uint8 (&br, &vA, 1))
return FALSE;
} else
guint8 same_time, subframes, num_program, prog;
if (v == 1) {
guint32 value;
+ /* taraBufferFullness */
if (!gst_aac_parse_latm_get_value (aacparse, &br, &value))
return FALSE;
}
}
if (!use_same_config) {
if (v == 0) {
- if (!gst_aac_parse_read_loas_audio_specific_config (aacparse, &br,
+ if (!gst_aac_parse_read_audio_specific_config (aacparse, &br, NULL,
sample_rate, channels, NULL))
return FALSE;
} else {
- guint32 bits, asc_len;
+ guint32 asc_len;
if (!gst_aac_parse_latm_get_value (aacparse, &br, &asc_len))
return FALSE;
- if (!gst_aac_parse_read_loas_audio_specific_config (aacparse, &br,
- sample_rate, channels, &bits))
+ if (!gst_aac_parse_read_audio_specific_config (aacparse, &br, NULL,
+ sample_rate, channels, NULL))
return FALSE;
- asc_len -= bits;
if (!gst_bit_reader_skip (&br, asc_len))
return FALSE;
}
if ((data[0] == 0x56) && ((data[1] & 0xe0) == 0xe0)) {
*framesize = gst_aac_parse_loas_get_frame_len (data);
- GST_DEBUG_OBJECT (aacparse, "Found %u byte LOAS frame", *framesize);
+ GST_DEBUG_OBJECT (aacparse, "Found possible %u byte LOAS frame",
+ *framesize);
/* In EOS mode this is enough. No need to examine the data further.
We also relax the check when we have sync, on the assumption that
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse),
nextlen + LOAS_MAX_SIZE);
return TRUE;
+ } else {
+ GST_DEBUG_OBJECT (aacparse, "That was a false positive");
}
}
return FALSE;
adts_headers[0] = 0xFFU;
adts_headers[1] = 0xF0U | (id << 3) | 0x1U;
adts_headers[2] = (profile << 6) | (sampling_frequency_index << 2) | 0x2U |
- (channel_configuration & 0x4U);
+ ((channel_configuration & 0x4U) >> 2);
adts_headers[3] = ((channel_configuration & 0x3U) << 6) | 0x30U |
(guint8) (frame_size >> 11);
adts_headers[4] = (guint8) ((frame_size >> 3) & 0x00FF);
&& aacparse->output_header_type == DSPAAC_HEADER_NONE) {
guint header_size;
GstMapInfo map;
- gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
+ frame->out_buffer = gst_buffer_make_writable (frame->buffer);
+ frame->buffer = NULL;
+ gst_buffer_map (frame->out_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);
+ gst_buffer_unmap (frame->out_buffer, &map);
+ gst_buffer_resize (frame->out_buffer, header_size,
+ gst_buffer_get_size (frame->out_buffer) - header_size);
}
+ frame->flags |= GST_BASE_PARSE_FRAME_FLAG_CLIP;
+
return GST_FLOW_OK;
}
aacparse->frame_samples = 1024;
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), ADTS_MAX_SIZE);
aacparse->sent_codec_tag = FALSE;
+ aacparse->last_parsed_channels = 0;
+ aacparse->last_parsed_sample_rate = 0;
+ aacparse->object_type = 0;
+ aacparse->bitrate = 0;
+ aacparse->header_type = DSPAAC_HEADER_NOT_PARSED;
+ aacparse->output_header_type = DSPAAC_HEADER_NOT_PARSED;
+ aacparse->channels = 0;
+ aacparse->sample_rate = 0;
return TRUE;
}
return res;
}
+
+static gboolean
+gst_aac_parse_src_event (GstBaseParse * parse, GstEvent * event)
+{
+ GstAacParse *aacparse = GST_AAC_PARSE (parse);
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
+ aacparse->last_parsed_channels = 0;
+ aacparse->last_parsed_sample_rate = 0;
+ }
+
+ return GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+}