summaryrefslogtreecommitdiff
path: root/gst/audioparsers/gstaacparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/audioparsers/gstaacparse.c')
-rw-r--r--gst/audioparsers/gstaacparse.c717
1 files changed, 717 insertions, 0 deletions
diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c
new file mode 100644
index 000000000..df7c401ab
--- /dev/null
+++ b/gst/audioparsers/gstaacparse.c
@@ -0,0 +1,717 @@
+/* GStreamer AAC parser plugin
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-aacparse
+ * @short_description: AAC parser
+ * @see_also: #GstAmrParse
+ *
+ * This is an AAC parser which handles both ADIF and ADTS stream formats.
+ *
+ * As ADIF format is not framed, it is not seekable and stream duration cannot
+ * 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>
+ * |[
+ * gst-launch filesrc location=abc.aac ! aacparse ! faad ! audioresample ! audioconvert ! alsasink
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "gstaacparse.h"
+
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/mpeg, "
+ "framed = (boolean) true, " "mpegversion = (int) { 2, 4 }, "
+ "stream-format = (string) { raw, adts, adif };"));
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/mpeg, "
+ "framed = (boolean) false, " "mpegversion = (int) { 2, 4 };"));
+
+GST_DEBUG_CATEGORY_STATIC (aacparse_debug);
+#define GST_CAT_DEFAULT aacparse_debug
+
+
+#define ADIF_MAX_SIZE 40 /* Should be enough */
+#define ADTS_MAX_SIZE 10 /* Should be enough */
+
+
+#define AAC_FRAME_DURATION(parse) (GST_SECOND/parse->frames_per_sec)
+
+gboolean gst_aac_parse_start (GstBaseParse * parse);
+gboolean gst_aac_parse_stop (GstBaseParse * parse);
+
+static gboolean gst_aac_parse_sink_setcaps (GstBaseParse * parse,
+ GstCaps * caps);
+
+gboolean gst_aac_parse_check_valid_frame (GstBaseParse * parse,
+ GstBaseParseFrame * frame, guint * size, gint * skipsize);
+
+GstFlowReturn gst_aac_parse_parse_frame (GstBaseParse * parse,
+ GstBaseParseFrame * frame);
+
+gboolean gst_aac_parse_convert (GstBaseParse * parse,
+ GstFormat src_format,
+ gint64 src_value, GstFormat dest_format, gint64 * dest_value);
+
+gint gst_aac_parse_get_frame_overhead (GstBaseParse * parse,
+ GstBuffer * buffer);
+
+gboolean gst_aac_parse_event (GstBaseParse * parse, GstEvent * event);
+
+#define _do_init(bla) \
+ GST_DEBUG_CATEGORY_INIT (aacparse_debug, "aacparse", 0, \
+ "AAC audio stream parser");
+
+GST_BOILERPLATE_FULL (GstAacParse, gst_aac_parse, GstBaseParse,
+ GST_TYPE_BASE_PARSE, _do_init);
+
+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_base_init:
+ * @klass: #GstElementClass.
+ *
+ */
+static void
+gst_aac_parse_base_init (gpointer klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_template));
+
+ gst_element_class_set_details_simple (element_class,
+ "AAC audio stream parser", "Codec/Parser/Audio",
+ "Advanced Audio Coding parser", "Stefan Kost <stefan.kost@nokia.com>");
+}
+
+
+/**
+ * gst_aac_parse_class_init:
+ * @klass: #GstAacParseClass.
+ *
+ */
+static void
+gst_aac_parse_class_init (GstAacParseClass * klass)
+{
+ GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
+
+ parse_class->start = GST_DEBUG_FUNCPTR (gst_aac_parse_start);
+ parse_class->stop = GST_DEBUG_FUNCPTR (gst_aac_parse_stop);
+ parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_aac_parse_sink_setcaps);
+ parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_aac_parse_parse_frame);
+ parse_class->check_valid_frame =
+ GST_DEBUG_FUNCPTR (gst_aac_parse_check_valid_frame);
+}
+
+
+/**
+ * gst_aac_parse_init:
+ * @aacparse: #GstAacParse.
+ * @klass: #GstAacParseClass.
+ *
+ */
+static void
+gst_aac_parse_init (GstAacParse * aacparse, GstAacParseClass * klass)
+{
+ GST_DEBUG ("initialized");
+}
+
+
+/**
+ * gst_aac_parse_set_src_caps:
+ * @aacparse: #GstAacParse.
+ * @sink_caps: (proposed) caps of sink pad
+ *
+ * Set source pad caps according to current knowledge about the
+ * audio stream.
+ *
+ * Returns: TRUE if caps were successfully set.
+ */
+static gboolean
+gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
+{
+ GstStructure *s;
+ GstCaps *src_caps = NULL;
+ gboolean res = FALSE;
+ const gchar *stream_format;
+
+ GST_DEBUG_OBJECT (aacparse, "sink caps: %" GST_PTR_FORMAT, sink_caps);
+ if (sink_caps)
+ src_caps = gst_caps_copy (sink_caps);
+ else
+ src_caps = gst_caps_new_simple ("audio/mpeg", NULL);
+
+ gst_caps_set_simple (src_caps, "framed", G_TYPE_BOOLEAN, TRUE,
+ "mpegversion", G_TYPE_INT, aacparse->mpegversion, NULL);
+
+ switch (aacparse->header_type) {
+ case DSPAAC_HEADER_NONE:
+ stream_format = "raw";
+ break;
+ case DSPAAC_HEADER_ADTS:
+ stream_format = "adts";
+ break;
+ case DSPAAC_HEADER_ADIF:
+ stream_format = "adif";
+ break;
+ default:
+ stream_format = NULL;
+ }
+
+ s = gst_caps_get_structure (src_caps, 0);
+ if (aacparse->sample_rate > 0)
+ gst_structure_set (s, "rate", G_TYPE_INT, aacparse->sample_rate, NULL);
+ if (aacparse->channels > 0)
+ gst_structure_set (s, "channels", G_TYPE_INT, aacparse->channels, NULL);
+ if (stream_format)
+ gst_structure_set (s, "stream-format", G_TYPE_STRING, stream_format, NULL);
+
+ GST_DEBUG_OBJECT (aacparse, "setting src caps: %" GST_PTR_FORMAT, src_caps);
+
+ res = gst_pad_set_caps (GST_BASE_PARSE (aacparse)->srcpad, src_caps);
+ gst_caps_unref (src_caps);
+ return res;
+}
+
+
+/**
+ * gst_aac_parse_sink_setcaps:
+ * @sinkpad: GstPad
+ * @caps: GstCaps
+ *
+ * Implementation of "set_sink_caps" vmethod in #GstBaseParse class.
+ *
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
+{
+ GstAacParse *aacparse;
+ GstStructure *structure;
+ gchar *caps_str;
+ const GValue *value;
+
+ aacparse = GST_AAC_PARSE (parse);
+ structure = gst_caps_get_structure (caps, 0);
+ caps_str = gst_caps_to_string (caps);
+
+ GST_DEBUG_OBJECT (aacparse, "setcaps: %s", caps_str);
+ g_free (caps_str);
+
+ /* This is needed at least in case of RTP
+ * Parses the codec_data information to get ObjectType,
+ * number of channels and samplerate */
+ value = gst_structure_get_value (structure, "codec_data");
+ if (value) {
+ GstBuffer *buf = gst_value_get_buffer (value);
+
+ if (buf) {
+ const guint8 *buffer = GST_BUFFER_DATA (buf);
+ guint sr_idx;
+
+ sr_idx = ((buffer[0] & 0x07) << 1) | ((buffer[1] & 0x80) >> 7);
+ aacparse->object_type = (buffer[0] & 0xf8) >> 3;
+ aacparse->sample_rate = gst_aac_parse_get_sample_rate_from_index (sr_idx);
+ aacparse->channels = (buffer[1] & 0x78) >> 3;
+ aacparse->header_type = DSPAAC_HEADER_NONE;
+ aacparse->mpegversion = 4;
+
+ GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d",
+ aacparse->object_type, aacparse->sample_rate, aacparse->channels);
+
+ /* arrange for metadata and get out of the way */
+ gst_aac_parse_set_src_caps (aacparse, caps);
+ gst_base_parse_set_passthrough (parse, TRUE);
+ } else
+ return FALSE;
+
+ /* caps info overrides */
+ gst_structure_get_int (structure, "rate", &aacparse->sample_rate);
+ gst_structure_get_int (structure, "channels", &aacparse->channels);
+ } else {
+ gst_base_parse_set_passthrough (parse, FALSE);
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * gst_aac_parse_adts_get_frame_len:
+ * @data: block of data containing an ADTS header.
+ *
+ * This function calculates ADTS frame length from the given header.
+ *
+ * Returns: size of the ADTS frame.
+ */
+static inline guint
+gst_aac_parse_adts_get_frame_len (const guint8 * data)
+{
+ return ((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] & 0xe0) >> 5);
+}
+
+
+/**
+ * gst_aac_parse_check_adts_frame:
+ * @aacparse: #GstAacParse.
+ * @data: Data to be checked.
+ * @avail: Amount of data passed.
+ * @framesize: If valid ADTS frame was found, this will be set to tell the
+ * found frame size in bytes.
+ * @needed_data: If frame was not found, this may be set to tell how much
+ * more data is needed in the next round to detect the frame
+ * reliably. This may happen when a frame header candidate
+ * is found but it cannot be guaranteed to be the header without
+ * peeking the following data.
+ *
+ * Check if the given data contains contains ADTS frame. The algorithm
+ * will examine ADTS frame header and calculate the frame size. Also, another
+ * consecutive ADTS frame header need to be present after the found frame.
+ * Otherwise the data is not considered as a valid ADTS frame. However, this
+ * "extra check" is omitted when EOS has been received. In this case it is
+ * enough when data[0] contains a valid ADTS header.
+ *
+ * This function may set the #needed_data to indicate that a possible frame
+ * candidate has been found, but more data (#needed_data bytes) is needed to
+ * be absolutely sure. When this situation occurs, FALSE will be returned.
+ *
+ * When a valid frame is detected, this function will use
+ * gst_base_parse_set_min_frame_size() function from #GstBaseParse class
+ * to set the needed bytes for next frame.This way next data chunk is already
+ * of correct size.
+ *
+ * Returns: TRUE if the given data contains a valid ADTS header.
+ */
+static gboolean
+gst_aac_parse_check_adts_frame (GstAacParse * aacparse,
+ const guint8 * data, const guint avail, gboolean drain,
+ guint * framesize, guint * needed_data)
+{
+ if (G_UNLIKELY (avail < 2))
+ return FALSE;
+
+ if ((data[0] == 0xff) && ((data[1] & 0xf6) == 0xf0)) {
+ *framesize = gst_aac_parse_adts_get_frame_len (data);
+
+ /* In EOS mode this is enough. No need to examine the data further */
+ if (drain) {
+ return TRUE;
+ }
+
+ if (*framesize + ADTS_MAX_SIZE > avail) {
+ /* We have found a possible frame header candidate, but can't be
+ sure since we don't have enough data to check the next frame */
+ GST_DEBUG ("NEED MORE DATA: we need %d, available %d",
+ *framesize + ADTS_MAX_SIZE, avail);
+ *needed_data = *framesize + ADTS_MAX_SIZE;
+ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse),
+ *framesize + ADTS_MAX_SIZE);
+ return FALSE;
+ }
+
+ if ((data[*framesize] == 0xff) && ((data[*framesize + 1] & 0xf6) == 0xf0)) {
+ guint nextlen = gst_aac_parse_adts_get_frame_len (data + (*framesize));
+
+ GST_LOG ("ADTS frame found, len: %d bytes", *framesize);
+ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse),
+ nextlen + ADTS_MAX_SIZE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* caller ensure sufficient data */
+static inline void
+gst_aac_parse_parse_adts_header (GstAacParse * aacparse, const guint8 * data,
+ gint * rate, gint * channels, gint * object, gint * version)
+{
+
+ if (rate) {
+ gint sr_idx = (data[2] & 0x3c) >> 2;
+
+ *rate = gst_aac_parse_get_sample_rate_from_index (sr_idx);
+ }
+ if (channels)
+ *channels = ((data[2] & 0x01) << 2) | ((data[3] & 0xc0) >> 6);
+
+ if (version)
+ *version = (data[1] & 0x08) ? 2 : 4;
+ if (object)
+ *object = (data[2] & 0xc0) >> 6;
+}
+
+/**
+ * gst_aac_parse_detect_stream:
+ * @aacparse: #GstAacParse.
+ * @data: A block of data that needs to be examined for stream characteristics.
+ * @avail: Size of the given datablock.
+ * @framesize: If valid stream was found, this will be set to tell the
+ * first frame size in bytes.
+ * @skipsize: If valid stream was found, this will be set to tell the first
+ * audio frame position within the given data.
+ *
+ * Examines the given piece of data and try to detect the format of it. It
+ * checks for "ADIF" header (in the beginning of the clip) and ADTS frame
+ * header. If the stream is detected, TRUE will be returned and #framesize
+ * is set to indicate the found frame size. Additionally, #skipsize might
+ * be set to indicate the number of bytes that need to be skipped, a.k.a. the
+ * position of the frame inside given data chunk.
+ *
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_aac_parse_detect_stream (GstAacParse * aacparse,
+ const guint8 * data, const guint avail, gboolean drain,
+ guint * framesize, gint * skipsize)
+{
+ gboolean found = FALSE;
+ guint need_data = 0;
+ guint i = 0;
+
+ GST_DEBUG_OBJECT (aacparse, "Parsing header data");
+
+ /* FIXME: No need to check for ADIF if we are not in the beginning of the
+ stream */
+
+ /* Can we even parse the header? */
+ if (avail < ADTS_MAX_SIZE)
+ return FALSE;
+
+ for (i = 0; i < avail - 4; i++) {
+ if (((data[i] == 0xff) && ((data[i + 1] & 0xf6) == 0xf0)) ||
+ strncmp ((char *) data + i, "ADIF", 4) == 0) {
+ found = TRUE;
+
+ if (i) {
+ /* Trick: tell the parent class that we didn't find the frame yet,
+ but make it skip 'i' amount of bytes. Next time we arrive
+ here we have full frame in the beginning of the data. */
+ *skipsize = i;
+ return FALSE;
+ }
+ break;
+ }
+ }
+ if (!found) {
+ if (i)
+ *skipsize = i;
+ return FALSE;
+ }
+
+ if (gst_aac_parse_check_adts_frame (aacparse, data, avail, drain,
+ framesize, &need_data)) {
+ gint rate, channels;
+
+ GST_INFO ("ADTS ID: %d, framesize: %d", (data[1] & 0x08) >> 3, *framesize);
+
+ aacparse->header_type = DSPAAC_HEADER_ADTS;
+ gst_aac_parse_parse_adts_header (aacparse, data, &rate, &channels,
+ &aacparse->object_type, &aacparse->mpegversion);
+
+ gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), rate, 1024, 2, 2);
+
+ GST_DEBUG ("ADTS: samplerate %d, channels %d, objtype %d, version %d",
+ rate, channels, aacparse->object_type, aacparse->mpegversion);
+
+ gst_base_parse_set_syncable (GST_BASE_PARSE (aacparse), TRUE);
+
+ return TRUE;
+ } else if (need_data) {
+ /* This tells the parent class not to skip any data */
+ *skipsize = 0;
+ return FALSE;
+ }
+
+ if (avail < ADIF_MAX_SIZE)
+ return FALSE;
+
+ if (memcmp (data + i, "ADIF", 4) == 0) {
+ const guint8 *adif;
+ int skip_size = 0;
+ int bitstream_type;
+ int sr_idx;
+
+ aacparse->header_type = DSPAAC_HEADER_ADIF;
+ aacparse->mpegversion = 4;
+
+ /* Skip the "ADIF" bytes */
+ adif = data + i + 4;
+
+ /* copyright string */
+ if (adif[0] & 0x80)
+ skip_size += 9; /* skip 9 bytes */
+
+ bitstream_type = adif[0 + skip_size] & 0x10;
+ aacparse->bitrate =
+ ((unsigned int) (adif[0 + skip_size] & 0x0f) << 19) |
+ ((unsigned int) adif[1 + skip_size] << 11) |
+ ((unsigned int) adif[2 + skip_size] << 3) |
+ ((unsigned int) adif[3 + skip_size] & 0xe0);
+
+ /* CBR */
+ if (bitstream_type == 0) {
+#if 0
+ /* Buffer fullness parsing. Currently not needed... */
+ guint num_elems = 0;
+ guint fullness = 0;
+
+ num_elems = (adif[3 + skip_size] & 0x1e);
+ GST_INFO ("ADIF num_config_elems: %d", num_elems);
+
+ fullness = ((unsigned int) (adif[3 + skip_size] & 0x01) << 19) |
+ ((unsigned int) adif[4 + skip_size] << 11) |
+ ((unsigned int) adif[5 + skip_size] << 3) |
+ ((unsigned int) (adif[6 + skip_size] & 0xe0) >> 5);
+
+ GST_INFO ("ADIF buffer fullness: %d", fullness);
+#endif
+ aacparse->object_type = ((adif[6 + skip_size] & 0x01) << 1) |
+ ((adif[7 + skip_size] & 0x80) >> 7);
+ sr_idx = (adif[7 + skip_size] & 0x78) >> 3;
+ }
+ /* VBR */
+ else {
+ aacparse->object_type = (adif[4 + skip_size] & 0x18) >> 3;
+ sr_idx = ((adif[4 + skip_size] & 0x07) << 1) |
+ ((adif[5 + skip_size] & 0x80) >> 7);
+ }
+
+ /* FIXME: This gives totally wrong results. Duration calculation cannot
+ be based on this */
+ aacparse->sample_rate = gst_aac_parse_get_sample_rate_from_index (sr_idx);
+
+ /* baseparse is not given any fps,
+ * so it will give up on timestamps, seeking, etc */
+
+ /* FIXME: Can we assume this? */
+ aacparse->channels = 2;
+
+ GST_INFO ("ADIF: br=%d, samplerate=%d, objtype=%d",
+ aacparse->bitrate, aacparse->sample_rate, aacparse->object_type);
+
+ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), 512);
+
+ /* arrange for metadata and get out of the way */
+ gst_aac_parse_set_src_caps (aacparse,
+ GST_PAD_CAPS (GST_BASE_PARSE_SINK_PAD (aacparse)));
+
+ /* not syncable, not easily seekable (unless we push data from start */
+ gst_base_parse_set_syncable (GST_BASE_PARSE_CAST (aacparse), FALSE);
+ gst_base_parse_set_passthrough (GST_BASE_PARSE_CAST (aacparse), TRUE);
+ gst_base_parse_set_average_bitrate (GST_BASE_PARSE_CAST (aacparse), 0);
+
+ *framesize = avail;
+ return TRUE;
+ }
+
+ /* This should never happen */
+ return FALSE;
+}
+
+
+/**
+ * gst_aac_parse_check_valid_frame:
+ * @parse: #GstBaseParse.
+ * @buffer: #GstBuffer.
+ * @framesize: If the buffer contains a valid frame, its size will be put here
+ * @skipsize: How much data parent class should skip in order to find the
+ * frame header.
+ *
+ * Implementation of "check_valid_frame" vmethod in #GstBaseParse class.
+ *
+ * Returns: TRUE if buffer contains a valid frame.
+ */
+gboolean
+gst_aac_parse_check_valid_frame (GstBaseParse * parse,
+ GstBaseParseFrame * frame, guint * framesize, gint * skipsize)
+{
+ const guint8 *data;
+ GstAacParse *aacparse;
+ gboolean ret = FALSE;
+ gboolean lost_sync;
+ GstBuffer *buffer;
+
+ aacparse = GST_AAC_PARSE (parse);
+ buffer = frame->buffer;
+ data = GST_BUFFER_DATA (buffer);
+
+ lost_sync = GST_BASE_PARSE_LOST_SYNC (parse);
+
+ if (aacparse->header_type == DSPAAC_HEADER_ADIF ||
+ aacparse->header_type == DSPAAC_HEADER_NONE) {
+ /* There is nothing to parse */
+ *framesize = GST_BUFFER_SIZE (buffer);
+ ret = TRUE;
+
+ } else if (aacparse->header_type == DSPAAC_HEADER_NOT_PARSED || lost_sync) {
+
+ ret = gst_aac_parse_detect_stream (aacparse, data, GST_BUFFER_SIZE (buffer),
+ GST_BASE_PARSE_DRAINING (parse), framesize, skipsize);
+
+ } else if (aacparse->header_type == DSPAAC_HEADER_ADTS) {
+ guint needed_data = 1024;
+
+ ret = gst_aac_parse_check_adts_frame (aacparse, data,
+ GST_BUFFER_SIZE (buffer), GST_BASE_PARSE_DRAINING (parse),
+ framesize, &needed_data);
+
+ if (!ret) {
+ GST_DEBUG ("buffer didn't contain valid frame");
+ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse),
+ needed_data);
+ }
+
+ } else {
+ GST_DEBUG ("buffer didn't contain valid frame");
+ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), 1024);
+ }
+
+ return ret;
+}
+
+
+/**
+ * gst_aac_parse_parse_frame:
+ * @parse: #GstBaseParse.
+ * @buffer: #GstBuffer.
+ *
+ * Implementation of "parse_frame" vmethod in #GstBaseParse class.
+ *
+ * Also determines frame overhead.
+ * ADTS streams have a 7 byte header in each frame. MP4 and ADIF streams don't have
+ * a per-frame header.
+ *
+ * We're making a couple of simplifying assumptions:
+ *
+ * 1. We count Program Configuration Elements rather than searching for them
+ * in the streams to discount them - the overhead is negligible.
+ *
+ * 2. We ignore CRC. This has a worst-case impact of (num_raw_blocks + 1)*16
+ * bits, which should still not be significant enough to warrant the
+ * additional parsing through the headers
+ *
+ * Returns: GST_FLOW_OK if frame was successfully parsed and can be pushed
+ * forward. Otherwise appropriate error is returned.
+ */
+GstFlowReturn
+gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
+{
+ GstAacParse *aacparse;
+ GstBuffer *buffer;
+ GstFlowReturn ret = GST_FLOW_OK;
+ gint rate, channels;
+
+ aacparse = GST_AAC_PARSE (parse);
+ buffer = frame->buffer;
+
+ if (G_UNLIKELY (aacparse->header_type != DSPAAC_HEADER_ADTS))
+ return ret;
+
+ /* see above */
+ frame->overhead = 7;
+
+ gst_aac_parse_parse_adts_header (aacparse, GST_BUFFER_DATA (buffer),
+ &rate, &channels, NULL, NULL);
+ GST_LOG_OBJECT (aacparse, "rate: %d, chans: %d", rate, channels);
+
+ if (G_UNLIKELY (rate != aacparse->sample_rate
+ || channels != aacparse->channels)) {
+ aacparse->sample_rate = rate;
+ aacparse->channels = channels;
+
+ if (!gst_aac_parse_set_src_caps (aacparse,
+ GST_PAD_CAPS (GST_BASE_PARSE (aacparse)->sinkpad))) {
+ /* If linking fails, we need to return appropriate error */
+ ret = GST_FLOW_NOT_LINKED;
+ }
+
+ gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse),
+ aacparse->sample_rate, 1024, 2, 2);
+ }
+
+ return ret;
+}
+
+
+/**
+ * gst_aac_parse_start:
+ * @parse: #GstBaseParse.
+ *
+ * Implementation of "start" vmethod in #GstBaseParse class.
+ *
+ * Returns: TRUE if startup succeeded.
+ */
+gboolean
+gst_aac_parse_start (GstBaseParse * parse)
+{
+ GstAacParse *aacparse;
+
+ aacparse = GST_AAC_PARSE (parse);
+ GST_DEBUG ("start");
+ gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), 1024);
+ return TRUE;
+}
+
+
+/**
+ * gst_aac_parse_stop:
+ * @parse: #GstBaseParse.
+ *
+ * Implementation of "stop" vmethod in #GstBaseParse class.
+ *
+ * Returns: TRUE is stopping succeeded.
+ */
+gboolean
+gst_aac_parse_stop (GstBaseParse * parse)
+{
+ GST_DEBUG ("stop");
+ return TRUE;
+}