Add two new baseparse based parsers (aac and amr) from Bug #518857.

Original commit message from CVS:
* configure.ac:
* gst/aacparse/Makefile.am:
* gst/aacparse/gstaacparse.c:
* gst/aacparse/gstaacparse.h:
* gst/aacparse/gstbaseparse.c:
* gst/aacparse/gstbaseparse.h:
* gst/amrparse/Makefile.am:
* gst/amrparse/gstamrparse.c:
* gst/amrparse/gstamrparse.h:
* gst/amrparse/gstbaseparse.c:
* gst/amrparse/gstbaseparse.h:
Add two new baseparse based parsers (aac and amr) from Bug #518857.
This commit is contained in:
Stefan Kost 2008-11-13 12:59:34 +00:00 committed by Tim-Philipp Müller
parent e71a908d96
commit fe9e6d3469
5 changed files with 2965 additions and 0 deletions

11
gst/aacparse/Makefile.am Normal file
View file

@ -0,0 +1,11 @@
plugin_LTLIBRARIES = libgstaacparse.la
libgstaacparse_la_SOURCES = \
gstaacparse.c gstbaseparse.c
libgstaacparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
libgstaacparse_la_LIBADD = $(GST_BASE_LIBS)
libgstaacparse_la_LDFLAGS = $(PACKAGE_LIBS) $(GST_PLUGIN_LDFLAGS)
libgstaacparse_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gstaacparse.h gstbaseparse.h

857
gst/aacparse/gstaacparse.c Normal file
View file

@ -0,0 +1,857 @@
/* 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:gstaacparse
* @short_description: AAC parser
* @see_also: #GstAmrParse
*
* <refsect2>
* <para>
* This is an AAC parser. It can handle both ADIF and ADTS stream formats.
* The parser inherits from #GstBaseParse and therefore in only needs to
* implement AAC-specific functionality.
* </para>
* <para>
* As ADIF format is not framed, it is not seekable. From the same reason
* stream duration cannot be calculated either. Instead, AAC clips that are
* in ADTS format can be seeked, and parser also is able to calculate their
* playback position and clip duration.
* </para>
* <title>Example launch line</title>
* <para>
* <programlisting>
* gst-launch filesrc location=abc.aac ! aacparse ! faad ! audioresample ! audioconvert ! alsasink
* </programlisting>
* </para>
* </refsect2>
*/
#include <string.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#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 };"));
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 (gst_aacparse_debug);
#define GST_CAT_DEFAULT gst_aacparse_debug
static const guint aac_sample_rates[] = {
96000,
88200,
64000,
48000,
44100,
32000,
24000,
22050,
16000,
12000,
11025,
8000
};
#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)
static void gst_aacparse_finalize (GObject * object);
gboolean gst_aacparse_start (GstBaseParse * parse);
gboolean gst_aacparse_stop (GstBaseParse * parse);
static gboolean gst_aacparse_sink_setcaps (GstBaseParse * parse,
GstCaps * caps);
gboolean gst_aacparse_check_valid_frame (GstBaseParse * parse,
GstBuffer * buffer, guint * size, gint * skipsize);
GstFlowReturn gst_aacparse_parse_frame (GstBaseParse * parse,
GstBuffer * buffer);
gboolean gst_aacparse_convert (GstBaseParse * parse,
GstFormat src_format,
gint64 src_value, GstFormat dest_format, gint64 * dest_value);
gboolean gst_aacparse_is_seekable (GstBaseParse * parse);
gboolean gst_aacparse_event (GstBaseParse * parse, GstEvent * event);
#define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (gst_aacparse_debug, "aacparse", 0, \
"AAC audio stream parser");
GST_BOILERPLATE_FULL (GstAacParse, gst_aacparse, GstBaseParse,
GST_TYPE_BASE_PARSE, _do_init);
/**
* gst_aacparse_base_init:
* @klass: #GstElementClass.
*
*/
static void
gst_aacparse_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstElementDetails details = GST_ELEMENT_DETAILS ("AAC audio stream parser",
"Codec/Parser/Audio",
"Advanced Audio Coding parser",
"Stefan Kost <stefan.kost@nokia.com>");
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 (element_class, &details);
}
/**
* gst_aacparse_class_init:
* @klass: #GstAacParseClass.
*
*/
static void
gst_aacparse_class_init (GstAacParseClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
object_class->finalize = gst_aacparse_finalize;
parse_class->start = GST_DEBUG_FUNCPTR (gst_aacparse_start);
parse_class->stop = GST_DEBUG_FUNCPTR (gst_aacparse_stop);
parse_class->event = GST_DEBUG_FUNCPTR (gst_aacparse_event);
parse_class->convert = GST_DEBUG_FUNCPTR (gst_aacparse_convert);
parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_aacparse_sink_setcaps);
parse_class->is_seekable = GST_DEBUG_FUNCPTR (gst_aacparse_is_seekable);
parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_aacparse_parse_frame);
parse_class->check_valid_frame =
GST_DEBUG_FUNCPTR (gst_aacparse_check_valid_frame);
}
/**
* gst_aacparse_init:
* @aacparse: #GstAacParse.
* @klass: #GstAacParseClass.
*
*/
static void
gst_aacparse_init (GstAacParse * aacparse, GstAacParseClass * klass)
{
/* init rest */
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), 1024);
aacparse->ts = 0;
GST_DEBUG ("initialized");
}
/**
* gst_aacparse_finalize:
* @object:
*
*/
static void
gst_aacparse_finalize (GObject * object)
{
GstAacParse *aacparse;
aacparse = GST_AACPARSE (object);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* gst_aacparse_set_src_caps:
* @aacparse: #GstAacParse.
*
* Set source pad caps according to current knowledge about the
* audio stream.
*
* Returns: TRUE if caps were successfully set.
*/
static gboolean
gst_aacparse_set_src_caps (GstAacParse * aacparse)
{
GstCaps *src_caps = NULL;
gchar *caps_str = NULL;
gboolean res = FALSE;
src_caps = gst_caps_new_simple ("audio/mpeg",
"framed", G_TYPE_BOOLEAN, TRUE,
"mpegversion", G_TYPE_INT, aacparse->mpegversion, NULL);
caps_str = gst_caps_to_string (src_caps);
GST_DEBUG_OBJECT (aacparse, "setting srcpad caps: %s", caps_str);
g_free (caps_str);
gst_pad_use_fixed_caps (GST_BASE_PARSE (aacparse)->srcpad);
res = gst_pad_set_caps (GST_BASE_PARSE (aacparse)->srcpad, src_caps);
gst_pad_fixate_caps (GST_BASE_PARSE (aacparse)->srcpad, src_caps);
gst_caps_unref (src_caps);
return res;
}
/**
* gst_aacparse_sink_setcaps:
* @sinkpad: GstPad
* @caps: GstCaps
*
* Implementation of "set_sink_caps" vmethod in #GstBaseParse class.
*
* Returns: TRUE on success.
*/
static gboolean
gst_aacparse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
{
GstAacParse *aacparse;
GstStructure *structure;
gchar *caps_str;
aacparse = GST_AACPARSE (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
if (gst_structure_has_field (structure, "codec_data")) {
const GValue *value = gst_structure_get_value (structure, "codec_data");
if (value) {
GstBuffer *buf = gst_value_get_buffer (value);
const guint8 *buffer = GST_BUFFER_DATA (buf);
aacparse->object_type = (buffer[0] & 0xf8) >> 3;
aacparse->sample_rate = ((buffer[0] & 0x07) << 1) |
((buffer[1] & 0x80) >> 7);
aacparse->channels = (buffer[1] & 0x78) >> 3;
aacparse->header_type = DSPAAC_HEADER_NONE;
aacparse->mpegversion = 4;
} else
return FALSE;
}
return TRUE;
}
/**
* gst_aacparse_update_duration:
* @aacparse: #GstAacParse.
*
*/
static void
gst_aacparse_update_duration (GstAacParse * aacparse)
{
GstPad *peer;
GstBaseParse *parse;
parse = GST_BASE_PARSE (aacparse);
/* Cannot estimate duration. No data has been passed to us yet */
if (!aacparse->framecount || !aacparse->frames_per_sec) {
return;
}
// info->length = (int)((filelength_filestream(file)/(((info->bitrate*8)/1024)*16))*1000);
peer = gst_pad_get_peer (parse->sinkpad);
if (peer) {
GstFormat pformat = GST_FORMAT_BYTES;
guint64 bpf = aacparse->bytecount / aacparse->framecount;
gboolean qres = FALSE;
gint64 ptot;
qres = gst_pad_query_duration (peer, &pformat, &ptot);
gst_object_unref (GST_OBJECT (peer));
if (qres && bpf) {
gst_base_parse_set_duration (parse, GST_FORMAT_TIME,
AAC_FRAME_DURATION (aacparse) * ptot / bpf);
}
}
}
/**
* gst_aacparse_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_aacparse_adts_get_frame_len (const guint8 * data)
{
return ((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] & 0xe0) >> 5);
}
/**
* gst_aacparse_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_aacparse_check_adts_frame (GstAacParse * aacparse,
const guint8 * data,
const guint avail, guint * framesize, guint * needed_data)
{
if ((data[0] == 0xff) && ((data[1] & 0xf6) == 0xf0)) {
*framesize = gst_aacparse_adts_get_frame_len (data);
/* In EOS mode this is enough. No need to examine the data further */
if (aacparse->eos) {
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_aacparse_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;
}
}
aacparse->sync = FALSE;
return FALSE;
}
/**
* gst_aacparse_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_aacparse_detect_stream (GstAacParse * aacparse,
const guint8 * data, const guint avail, 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_aacparse_check_adts_frame (aacparse, data, avail,
framesize, &need_data)) {
gint sr_idx;
GST_INFO ("ADTS ID: %d, framesize: %d", (data[1] & 0x08) >> 3, *framesize);
aacparse->header_type = DSPAAC_HEADER_ADTS;
sr_idx = (data[2] & 0x3c) >> 2;
aacparse->sample_rate = aac_sample_rates[sr_idx];
aacparse->mpegversion = (data[1] & 0x08) ? 2 : 4;
aacparse->object_type = (data[2] & 0xc0) >> 6;
aacparse->channels = ((data[2] & 0x01) << 2) | ((data[3] & 0xc0) >> 6);
aacparse->bitrate = ((data[5] & 0x1f) << 6) | ((data[6] & 0xfc) >> 2);
aacparse->frames_per_sec = aac_sample_rates[sr_idx] / 1024.f;
GST_DEBUG ("ADTS: samplerate %d, channels %d, bitrate %d, objtype %d, "
"fps %f", aacparse->sample_rate, aacparse->channels,
aacparse->bitrate, aacparse->object_type, aacparse->frames_per_sec);
aacparse->sync = 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 = aac_sample_rates[sr_idx];
aacparse->frames_per_sec = aac_sample_rates[sr_idx] / 1024.f;
GST_INFO ("ADIF fps: %f", aacparse->frames_per_sec);
// 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);
*framesize = avail;
aacparse->sync = TRUE;
return TRUE;
}
/* This should never happen */
return FALSE;
}
/**
* gst_aacparse_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_aacparse_check_valid_frame (GstBaseParse * parse,
GstBuffer * buffer, guint * framesize, gint * skipsize)
{
const guint8 *data;
GstAacParse *aacparse;
guint needed_data = 1024;
gboolean ret = FALSE;
aacparse = GST_AACPARSE (parse);
data = GST_BUFFER_DATA (buffer);
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
/* Discontinuous stream -> drop the sync */
aacparse->sync = FALSE;
}
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 ||
aacparse->sync == FALSE) {
ret = gst_aacparse_detect_stream (aacparse, data, GST_BUFFER_SIZE (buffer),
framesize, skipsize);
} else if (aacparse->header_type == DSPAAC_HEADER_ADTS) {
ret = gst_aacparse_check_adts_frame (aacparse, data,
GST_BUFFER_SIZE (buffer), framesize, &needed_data);
}
if (!ret) {
/* Increase the block size, we want to find the header by ourselves */
GST_DEBUG ("buffer didn't contain valid frame, skip = %d", *skipsize);
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse), needed_data);
}
return ret;
}
/**
* gst_aacparse_parse_frame:
* @parse: #GstBaseParse.
* @buffer: #GstBuffer.
*
* Implementation of "parse_frame" vmethod in #GstBaseParse class.
*
* Returns: GST_FLOW_OK if frame was successfully parsed and can be pushed
* forward. Otherwise appropriate error is returned.
*/
GstFlowReturn
gst_aacparse_parse_frame (GstBaseParse * parse, GstBuffer * buffer)
{
GstAacParse *aacparse;
GstFlowReturn ret = GST_FLOW_OK;
aacparse = GST_AACPARSE (parse);
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
gint64 btime;
gboolean r = gst_aacparse_convert (parse, GST_FORMAT_BYTES,
GST_BUFFER_OFFSET (buffer),
GST_FORMAT_TIME, &btime);
if (r) {
/* FIXME: What to do if the conversion fails? */
aacparse->ts = btime;
}
}
GST_BUFFER_DURATION (buffer) = AAC_FRAME_DURATION (aacparse);
GST_BUFFER_TIMESTAMP (buffer) = aacparse->ts;
if (GST_CLOCK_TIME_IS_VALID (aacparse->ts))
aacparse->ts += GST_BUFFER_DURATION (buffer);
if (!(++aacparse->framecount % 50)) {
gst_aacparse_update_duration (aacparse);
}
aacparse->bytecount += GST_BUFFER_SIZE (buffer);
if (!aacparse->src_caps_set) {
if (!gst_aacparse_set_src_caps (aacparse)) {
/* If linking fails, we need to return appropriate error */
ret = GST_FLOW_NOT_LINKED;
}
aacparse->src_caps_set = TRUE;
}
gst_buffer_set_caps (buffer, GST_PAD_CAPS (parse->srcpad));
return ret;
}
/**
* gst_aacparse_start:
* @parse: #GstBaseParse.
*
* Implementation of "start" vmethod in #GstBaseParse class.
*
* Returns: TRUE if startup succeeded.
*/
gboolean
gst_aacparse_start (GstBaseParse * parse)
{
GstAacParse *aacparse;
aacparse = GST_AACPARSE (parse);
GST_DEBUG ("start");
aacparse->src_caps_set = FALSE;
aacparse->framecount = 0;
aacparse->bytecount = 0;
aacparse->ts = 0;
aacparse->sync = FALSE;
aacparse->eos = FALSE;
return TRUE;
}
/**
* gst_aacparse_stop:
* @parse: #GstBaseParse.
*
* Implementation of "stop" vmethod in #GstBaseParse class.
*
* Returns: TRUE is stopping succeeded.
*/
gboolean
gst_aacparse_stop (GstBaseParse * parse)
{
GstAacParse *aacparse;
aacparse = GST_AACPARSE (parse);
GST_DEBUG ("stop");
aacparse->ts = -1;
return TRUE;
}
/**
* gst_aacparse_event:
* @parse: #GstBaseParse.
* @event: #GstEvent.
*
* Implementation of "event" vmethod in #GstBaseParse class.
*
* Returns: TRUE if the event was handled and can be dropped.
*/
gboolean
gst_aacparse_event (GstBaseParse * parse, GstEvent * event)
{
GstAacParse *aacparse;
aacparse = GST_AACPARSE (parse);
GST_DEBUG ("event");
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
aacparse->eos = TRUE;
GST_DEBUG ("EOS event");
break;
default:
break;
}
return parent_class->event (parse, event);
}
/**
* gst_aacparse_convert:
* @parse: #GstBaseParse.
* @src_format: #GstFormat describing the source format.
* @src_value: Source value to be converted.
* @dest_format: #GstFormat defining the converted format.
* @dest_value: Pointer where the conversion result will be put.
*
* Implementation of "convert" vmethod in #GstBaseParse class.
*
* Returns: TRUE if conversion was successful.
*/
gboolean
gst_aacparse_convert (GstBaseParse * parse,
GstFormat src_format,
gint64 src_value, GstFormat dest_format, gint64 * dest_value)
{
gboolean ret = FALSE;
GstAacParse *aacparse;
gfloat bpf;
aacparse = GST_AACPARSE (parse);
/* We are not able to do any estimations until some data has been passed */
if (!aacparse->framecount)
return FALSE;
bpf = (gfloat) aacparse->bytecount / aacparse->framecount;
if (src_format == GST_FORMAT_BYTES) {
if (dest_format == GST_FORMAT_TIME) {
/* BYTES -> TIME conversion */
GST_DEBUG ("converting bytes -> time");
if (aacparse->framecount && aacparse->frames_per_sec) {
*dest_value = AAC_FRAME_DURATION (aacparse) * src_value / bpf;
GST_DEBUG ("conversion result: %lld ms", *dest_value / GST_MSECOND);
ret = TRUE;
}
} else if (dest_format == GST_FORMAT_BYTES) {
/* Parent class may ask us to convert from BYTES to BYTES */
*dest_value = src_value;
ret = TRUE;
}
} else if (src_format == GST_FORMAT_TIME) {
GST_DEBUG ("converting time -> bytes");
if (dest_format == GST_FORMAT_BYTES) {
if (aacparse->framecount && aacparse->frames_per_sec) {
*dest_value = bpf * src_value / AAC_FRAME_DURATION (aacparse);
GST_DEBUG ("time %lld ms in bytes = %lld", src_value / GST_MSECOND,
*dest_value);
ret = TRUE;
}
}
} else if (src_format == GST_FORMAT_DEFAULT) {
/* DEFAULT == frame-based */
if (dest_format == GST_FORMAT_TIME && aacparse->frames_per_sec) {
*dest_value = src_value * AAC_FRAME_DURATION (aacparse);
ret = TRUE;
} else if (dest_format == GST_FORMAT_BYTES) {
}
}
return ret;
}
/**
* gst_aacparse_is_seekable:
* @parse: #GstBaseParse.
*
* Implementation of "is_seekable" vmethod in #GstBaseParse class.
*
* Returns: TRUE if the current stream is seekable.
*/
gboolean
gst_aacparse_is_seekable (GstBaseParse * parse)
{
GstAacParse *aacparse;
aacparse = GST_AACPARSE (parse);
GST_DEBUG_OBJECT (aacparse, "IS_SEEKABLE: %d",
aacparse->header_type != DSPAAC_HEADER_ADIF);
/* Not seekable if ADIF header is found */
return (aacparse->header_type != DSPAAC_HEADER_ADIF);
}
/**
* plugin_init:
* @plugin: GstPlugin
*
* Returns: TRUE on success.
*/
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "aacparse",
GST_RANK_PRIMARY + 1, GST_TYPE_AACPARSE);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"aacparse",
"Advanced Audio Coding Parser",
plugin_init, VERSION, GST_LICENSE_UNKNOWN, GST_PACKAGE_NAME,
GST_PACKAGE_ORIGIN);

119
gst/aacparse/gstaacparse.h Normal file
View file

@ -0,0 +1,119 @@
/* GStreamer AAC parser
* 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.
*/
#ifndef __GST_AACPARSE_H__
#define __GST_AACPARSE_H__
#include <gst/gst.h>
#include "gstbaseparse.h"
G_BEGIN_DECLS
#define GST_TYPE_AACPARSE \
(gst_aacparse_get_type())
#define GST_AACPARSE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AACPARSE, GstAacParse))
#define GST_AACPARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AACPARSE, GstAacParseClass))
#define GST_IS_AACPARSE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AACPARSE))
#define GST_IS_AACPARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AACPARSE))
/**
* GstAacHeaderType:
* @DSPAAC_HEADER_NOT_PARSED: Header not parsed yet.
* @DSPAAC_HEADER_UNKNOWN: Unknown (not recognized) header.
* @DSPAAC_HEADER_ADIF: ADIF header found.
* @DSPAAC_HEADER_ADTS: ADTS header found.
* @DSPAAC_HEADER_NONE: Raw stream, no header.
*
* Type header enumeration set in #header_type.
*/
typedef enum {
DSPAAC_HEADER_NOT_PARSED,
DSPAAC_HEADER_UNKNOWN,
DSPAAC_HEADER_ADIF,
DSPAAC_HEADER_ADTS,
DSPAAC_HEADER_NONE
} GstAacHeaderType;
typedef struct _GstAacParse GstAacParse;
typedef struct _GstAacParseClass GstAacParseClass;
/**
* GstAacParse:
* @element: the parent element.
* @object_type: AAC object type of the stream.
* @bitrate: Current media bitrate.
* @sample_rate: Current media samplerate.
* @channels: Current media channel count.
* @frames_per_sec: FPS value of the current stream.
* @header_type: #GstAacHeaderType indicating the current stream type.
* @framecount: The amount of frames that has been processed this far.
* @bytecount: The amount of bytes that has been processed this far.
* @sync: Tells whether the parser is in sync (a.k.a. not searching for header)
* @eos: End-of-Stream indicator. Set when EOS event arrives.
* @duration: Duration of the current stream.
* @ts: Current stream timestamp.
*
* The opaque GstAacParse data structure.
*/
struct _GstAacParse {
GstBaseParse element;
/* Stream type -related info */
gint object_type;
gint bitrate;
gint sample_rate;
gint channels;
gint mpegversion;
gfloat frames_per_sec;
GstAacHeaderType header_type;
guint64 framecount;
guint64 bytecount;
gboolean src_caps_set;
gboolean sync;
gboolean eos;
GstClockTime duration;
GstClockTime ts;
};
/**
* GstAacParseClass:
* @parent_class: Element parent class.
*
* The opaque GstAacParseClass data structure.
*/
struct _GstAacParseClass {
GstBaseParseClass parent_class;
};
GType gst_aacparse_get_type (void);
G_END_DECLS
#endif /* __GST_AACPARSE_H__ */

1741
gst/aacparse/gstbaseparse.c Normal file

File diff suppressed because it is too large Load diff

237
gst/aacparse/gstbaseparse.h Normal file
View file

@ -0,0 +1,237 @@
/* GStreamer
* 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.
*/
#ifndef __GST_BASE_PARSE_H__
#define __GST_BASE_PARSE_H__
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
#define GST_TYPE_BASE_PARSE (gst_base_parse_get_type())
#define GST_BASE_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_PARSE,GstBaseParse))
#define GST_BASE_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_PARSE,GstBaseParseClass))
#define GST_BASE_PARSE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_PARSE,GstBaseParseClass))
#define GST_IS_BASE_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_PARSE))
#define GST_IS_BASE_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_PARSE))
#define GST_BASE_PARSE_CAST(obj) ((GstBaseParse *)(obj))
/**
* GST_BASE_PARSE_SINK_NAME:
*
* the name of the templates for the sink pad
*/
#define GST_BASE_PARSE_SINK_NAME "sink"
/**
* GST_BASE_PARSE_SRC_NAME:
*
* the name of the templates for the source pad
*/
#define GST_BASE_PARSE_SRC_NAME "src"
/**
* GST_BASE_PARSE_SRC_PAD:
* @obj: base parse instance
*
* Gives the pointer to the source #GstPad object of the element.
*
* Since: 0.10.x
*/
#define GST_BASE_PARSE_SRC_PAD(obj) (GST_BASE_PARSE_CAST (obj)->srcpad)
/**
* GST_BASE_PARSE_SINK_PAD:
* @obj: base parse instance
*
* Gives the pointer to the sink #GstPad object of the element.
*
* Since: 0.10.x
*/
#define GST_BASE_PARSE_SINK_PAD(obj) (GST_BASE_PARSE_CAST (obj)->sinkpad)
/**
* GST_BASE_PARSE_FLOW_DROPPED:
*
* A #GstFlowReturn that can be returned from parse_frame to
* indicate that no output buffer was generated.
*
* Since: 0.10.x
*/
#define GST_BASE_PARSE_FLOW_DROPPED GST_FLOW_CUSTOM_SUCCESS
/**
* GST_BASE_PARSE_LOCK:
* @obj: base parse instance
*
* Obtain a lock to protect the parse function from concurrent access.
*
* Since: 0.10.x
*/
#define GST_BASE_PARSE_LOCK(obj) g_mutex_lock (GST_BASE_PARSE_CAST (obj)->parse_lock)
/**
* GST_BASE_PARSE_UNLOCK:
* @obj: base parse instance
*
* Release the lock that protects the parse function from concurrent access.
*
* Since: 0.10.x
*/
#define GST_BASE_PARSE_UNLOCK(obj) g_mutex_unlock (GST_BASE_PARSE_CAST (obj)->parse_lock)
typedef struct _GstBaseParse GstBaseParse;
typedef struct _GstBaseParseClass GstBaseParseClass;
typedef struct _GstBaseParsePrivate GstBaseParsePrivate;
typedef struct _GstBaseParseClassPrivate GstBaseParseClassPrivate;
/**
* GstBaseParse:
* @element: the parent element.
*
* The opaque #GstBaseParse data structure.
*/
struct _GstBaseParse {
GstElement element;
GstAdapter *adapter;
/*< protected >*/
/* source and sink pads */
GstPad *sinkpad;
GstPad *srcpad;
/* MT-protected (with STREAM_LOCK) */
GstSegment segment;
/* Newsegment event to be sent after SEEK */
GstEvent *pending_segment;
/* Segment event that closes the running segment prior to SEEK */
GstEvent *close_segment;
/* Caps nego done already? */
gboolean negotiated;
GMutex *parse_lock;
/*< private >*/
gpointer _gst_reserved[GST_PADDING_LARGE];
GstBaseParsePrivate *priv;
};
/**
* GstBaseParseClass:
* @start: Optional.
* Called when the element starts processing.
* Allows opening external resources.
* @stop: Optional.
* Called when the element stops processing.
* Allows closing external resources.
* @set_sink_caps: allows the subclass to be notified of the actual caps set.
* @check_valid_frame: Check if the given piece of data contains a valid
* frame.
* @parse_frame: Parse the already checked frame. Subclass need to
* set the buffer timestamp, duration, caps and possibly
* other necessary metadata. This is called with srcpad's
* STREAM_LOCK held.
* @convert: Optional.
* Convert between formats.
* @find_frame: Optional.
* Finds a frame. Gets a position passed and should return
* TRUE and the offset in bytes where this position is.
* Will only be called in pull mode and the subclass can pull
* whatever it wants from upstream. If not implemented,
* the base class will implement it by calling
* @check_valid_frame and @parse_frame to find the wanted
* frame and build a seek table.
* @event: Optional.
* Event handler on the sink pad. This function should return
* TRUE if the event was handled and can be dropped.
* @src_event: Optional.
* Event handler on the source pad. Should return TRUE
* if the event was handled and can be dropped.
* @is_seekable: Optional.
* Subclass can override this if it wants to control the
* seekability of the stream. Otherwise the element assumes
* that stream is always seekable.
*
* Subclasses can override any of the available virtual methods or not, as
* needed. At minimum @check_valid_frame and @parse_frame needs to be
* overridden.
*/
struct _GstBaseParseClass {
GstElementClass parent_class;
/*< public >*/
/* virtual methods for subclasses */
gboolean (*start) (GstBaseParse *parse);
gboolean (*stop) (GstBaseParse *parse);
gboolean (*set_sink_caps) (GstBaseParse *parse,
GstCaps *caps);
gboolean (*check_valid_frame) (GstBaseParse *parse,
GstBuffer *buffer,
guint *framesize,
gint *skipsize);
GstFlowReturn (*parse_frame) (GstBaseParse *parse,
GstBuffer *buffer);
gboolean (*convert) (GstBaseParse * parse,
GstFormat src_format,
gint64 src_value,
GstFormat dest_format,
gint64 * dest_value);
gboolean (*find_frame) (GstBaseParse *parse,
GstFormat src_format,
gint64 src_value,
gint64 * dest_value);
gboolean (*event) (GstBaseParse *parse,
GstEvent *event);
gboolean (*src_event) (GstBaseParse *parse,
GstEvent *event);
gboolean (*is_seekable) (GstBaseParse *parse);
/*< private >*/
gpointer _gst_reserved[GST_PADDING_LARGE];
GstBaseParseClassPrivate *priv;
};
GType gst_base_parse_get_type (void);
void gst_base_parse_set_duration (GstBaseParse *parse,
GstFormat fmt,
gint64 duration);
void gst_base_parse_set_min_frame_size (GstBaseParse *parse,
guint min_size);
G_END_DECLS
#endif /* __GST_BASE_PARSE_H__ */