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
parent d8852e5788
commit 7f42b7856b
12 changed files with 5656 additions and 0 deletions

View file

@ -1,3 +1,18 @@
2008-11-13 Stefan Kost <ensonic@users.sf.net>
* 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.
2008-11-12 Thijs Vermeir <thijsvermeir@gmail.com>
* gst/librfb/rfbdecoder.c:

View file

@ -232,7 +232,9 @@ AG_GST_SET_LEVEL_DEFAULT($GST_CVS)
dnl *** plug-ins to include ***
dnl these are all the gst plug-ins, compilable without additional libs
AG_GST_CHECK_PLUGIN(aacparse)
AG_GST_CHECK_PLUGIN(aiffparse)
AG_GST_CHECK_PLUGIN(amrparse)
AG_GST_CHECK_PLUGIN(app)
AG_GST_CHECK_PLUGIN(bayer)
AG_GST_CHECK_PLUGIN(cdxaparse)
@ -1385,7 +1387,9 @@ AC_CONFIG_FILES(
Makefile
gst-plugins-bad.spec
gst/Makefile
gst/aacparse/Makefile
gst/aiffparse/Makefile
gst/amrparse/Makefile
gst/app/Makefile
gst/bayer/Makefile
gst/cdxaparse/Makefile

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__ */

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

@ -0,0 +1,11 @@
plugin_LTLIBRARIES = libgstamrparse.la
libgstamrparse_la_SOURCES = \
gstamrparse.c gstbaseparse.c
libgstamrparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
libgstamrparse_la_LIBADD = $(GST_BASE_LIBS)
libgstamrparse_la_LDFLAGS = $(PACKAGE_LIBS) $(GST_PLUGIN_LDFLAGS)
libgstamrparse_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gstamrparse.h gstbaseparse.h

597
gst/amrparse/gstamrparse.c Normal file
View file

@ -0,0 +1,597 @@
/* GStreamer Adaptive Multi-Rate parser plugin
* Copyright (C) 2006 Edgard Lima <edgard.lima@indt.org.br>
* 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:gstamrparse
* @short_description: AMR parser
* @see_also: #GstAmrnbDec, #GstAmrnbEnc
*
* <refsect2>
* <para>
* This is an AMR parser capable of handling both narrow-band and wideband
* formats.
* </para>
* <title>Example launch line</title>
* <para>
* <programlisting>
* gst-launch filesrc location=abc.amr ! amrparse ! amrdec ! audioresample ! audioconvert ! alsasink
* </programlisting>
* </para>
* </refsect2>
*/
#include <string.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstamrparse.h"
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/AMR, " "rate = (int) 8000, " "channels = (int) 1;"
"audio/AMR-WB, " "rate = (int) 16000, " "channels = (int) 1;")
);
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-amr-nb-sh; audio/x-amr-wb-sh"));
GST_DEBUG_CATEGORY_STATIC (gst_amrparse_debug);
#define GST_CAT_DEFAULT gst_amrparse_debug
static const gint block_size_nb[16] =
{ 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 };
static const gint block_size_wb[16] =
{ 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, 5, 0, 0, 0, 0, 0 };
/* AMR has a "hardcoded" framerate of 50fps */
#define AMR_FRAMES_PER_SECOND 50
#define AMR_FRAME_DURATION (GST_SECOND/AMR_FRAMES_PER_SECOND)
#define AMR_MIME_HEADER_SIZE 9
static void gst_amrparse_finalize (GObject * object);
gboolean gst_amrparse_start (GstBaseParse * parse);
gboolean gst_amrparse_stop (GstBaseParse * parse);
static gboolean gst_amrparse_sink_setcaps (GstBaseParse * parse,
GstCaps * caps);
gboolean gst_amrparse_check_valid_frame (GstBaseParse * parse,
GstBuffer * buffer, guint * framesize, gint * skipsize);
GstFlowReturn gst_amrparse_parse_frame (GstBaseParse * parse,
GstBuffer * buffer);
gboolean gst_amrparse_convert (GstBaseParse * parse,
GstFormat src_format,
gint64 src_value, GstFormat dest_format, gint64 * dest_value);
gboolean gst_amrparse_event (GstBaseParse * parse, GstEvent * event);
#define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (gst_amrparse_debug, "amrparse", 0, \
"AMR-NB audio stream parser");
GST_BOILERPLATE_FULL (GstAmrParse, gst_amrparse, GstBaseParse,
GST_TYPE_BASE_PARSE, _do_init);
/**
* gst_amrparse_base_init:
* @klass: #GstElementClass.
*
*/
static void
gst_amrparse_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstElementDetails details = GST_ELEMENT_DETAILS ("AMR audio stream parser",
"Codec/Parser/Audio",
"Adaptive Multi-Rate audio parser",
"Ronald Bultje <rbultje@ronald.bitfreak.net>");
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_amrparse_class_init:
* @klass: GstAmrParseClass.
*
*/
static void
gst_amrparse_class_init (GstAmrParseClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
object_class->finalize = gst_amrparse_finalize;
parse_class->start = GST_DEBUG_FUNCPTR (gst_amrparse_start);
parse_class->stop = GST_DEBUG_FUNCPTR (gst_amrparse_stop);
parse_class->event = GST_DEBUG_FUNCPTR (gst_amrparse_event);
parse_class->convert = GST_DEBUG_FUNCPTR (gst_amrparse_convert);
parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_amrparse_sink_setcaps);
parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_amrparse_parse_frame);
parse_class->check_valid_frame =
GST_DEBUG_FUNCPTR (gst_amrparse_check_valid_frame);
}
/**
* gst_amrparse_init:
* @amrparse: #GstAmrParse
* @klass: #GstAmrParseClass.
*
*/
static void
gst_amrparse_init (GstAmrParse * amrparse, GstAmrParseClass * klass)
{
/* init rest */
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (amrparse), 62);
amrparse->ts = 0;
GST_DEBUG ("initialized");
}
/**
* gst_amrparse_finalize:
* @object:
*
*/
static void
gst_amrparse_finalize (GObject * object)
{
GstAmrParse *amrparse;
amrparse = GST_AMRPARSE (object);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* gst_amrparse_set_src_caps:
* @amrparse: #GstAmrParse.
*
* Set source pad caps according to current knowledge about the
* audio stream.
*
* Returns: TRUE if caps were successfully set.
*/
static gboolean
gst_amrparse_set_src_caps (GstAmrParse * amrparse)
{
GstCaps *src_caps = NULL;
gboolean res = FALSE;
if (amrparse->wide) {
GST_DEBUG_OBJECT (amrparse, "setting srcpad caps to AMR-WB");
src_caps = gst_caps_new_simple ("audio/AMR-WB",
"channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, 16000, NULL);
} else {
GST_DEBUG_OBJECT (amrparse, "setting srcpad caps to AMR-NB");
/* Max. size of NB frame is 31 bytes, so we can set the min. frame
size to 32 (+1 for next frame header) */
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (amrparse), 32);
src_caps = gst_caps_new_simple ("audio/AMR",
"channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, 8000, NULL);
}
gst_pad_use_fixed_caps (GST_BASE_PARSE (amrparse)->srcpad);
res = gst_pad_set_caps (GST_BASE_PARSE (amrparse)->srcpad, src_caps);
gst_pad_fixate_caps (GST_BASE_PARSE (amrparse)->srcpad, src_caps);
gst_caps_unref (src_caps);
return res;
}
/**
* gst_amrparse_sink_setcaps:
* @sinkpad: GstPad
* @caps: GstCaps
*
* Returns: TRUE on success.
*/
static gboolean
gst_amrparse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
{
GstAmrParse *amrparse;
GstStructure *structure;
const gchar *name;
amrparse = GST_AMRPARSE (parse);
structure = gst_caps_get_structure (caps, 0);
name = gst_structure_get_name (structure);
GST_DEBUG_OBJECT (amrparse, "setcaps: %s", name);
if (!strncmp (name, "audio/x-amr-wb-sh", 17)) {
amrparse->block_size = block_size_wb;
amrparse->wide = 1;
} else if (!strncmp (name, "audio/x-amr-nb-sh", 17)) {
amrparse->block_size = block_size_nb;
amrparse->wide = 0;
} else {
GST_WARNING ("Unknown caps");
return FALSE;
}
amrparse->need_header = FALSE;
gst_amrparse_set_src_caps (amrparse);
return TRUE;
}
/**
* gst_amrparse_update_duration:
* @amrparse: #GstAmrParse.
*
* Send duration information to base class.
*/
static void
gst_amrparse_update_duration (GstAmrParse * amrparse)
{
GstPad *peer;
GstBaseParse *parse;
parse = GST_BASE_PARSE (amrparse);
/* Cannot estimate duration. No data has been passed to us yet */
if (!amrparse->framecount) {
return;
}
peer = gst_pad_get_peer (parse->sinkpad);
if (peer) {
GstFormat pformat = GST_FORMAT_BYTES;
guint64 bpf = amrparse->bytecount / amrparse->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,
AMR_FRAME_DURATION * ptot / bpf);
}
}
}
/**
* gst_amrparse_parse_header:
* @amrparse: #GstAmrParse
* @data: Header data to be parsed.
* @skipsize: Output argument where the frame size will be stored.
*
* Check if the given data contains an AMR mime header.
*
* Returns: TRUE on success.
*/
static gboolean
gst_amrparse_parse_header (GstAmrParse * amrparse,
const guint8 * data, gint * skipsize)
{
GST_DEBUG_OBJECT (amrparse, "Parsing header data");
if (!memcmp (data, "#!AMR-WB\n", 9)) {
GST_DEBUG_OBJECT (amrparse, "AMR-WB detected");
amrparse->block_size = block_size_wb;
amrparse->wide = TRUE;
*skipsize = 9;
} else if (!memcmp (data, "#!AMR\n", 6)) {
GST_DEBUG_OBJECT (amrparse, "AMR-NB detected");
amrparse->block_size = block_size_nb;
amrparse->wide = FALSE;
*skipsize = 6;
} else
return FALSE;
gst_amrparse_set_src_caps (amrparse);
return TRUE;
}
/**
* gst_amrparse_check_valid_frame:
* @parse: #GstBaseParse.
* @buffer: #GstBuffer.
* @framesize: Output variable where the found frame size is put.
* @skipsize: Output variable which tells how much data needs to be skipped
* until a frame header is found.
*
* Implementation of "check_valid_frame" vmethod in #GstBaseParse class.
*
* Returns: TRUE if the given data contains valid frame.
*/
gboolean
gst_amrparse_check_valid_frame (GstBaseParse * parse,
GstBuffer * buffer, guint * framesize, gint * skipsize)
{
const guint8 *data;
gint fsize, mode, dsize;
GstAmrParse *amrparse;
amrparse = GST_AMRPARSE (parse);
data = GST_BUFFER_DATA (buffer);
dsize = GST_BUFFER_SIZE (buffer);
GST_LOG ("buffer: %d bytes", dsize);
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
/* Discontinuous stream -> drop the sync */
amrparse->sync = FALSE;
}
if (amrparse->need_header) {
if (dsize >= AMR_MIME_HEADER_SIZE &&
gst_amrparse_parse_header (amrparse, data, skipsize)) {
amrparse->need_header = FALSE;
} else {
GST_WARNING ("media doesn't look like a AMR format");
}
/* We return FALSE, so this frame won't get pushed forward. Instead,
the "skip" value is set, so next time we will receive a valid frame. */
return FALSE;
}
/* Does this look like a possible frame header candidate? */
if ((data[0] & 0x83) == 0) {
/* Yep. Retrieve the frame size */
mode = (data[0] >> 3) & 0x0F;
fsize = amrparse->block_size[mode] + 1; /* +1 for the header byte */
/* We recognize this data as a valid frame when:
* - We are in sync. There is no need for extra checks then
* - We are in EOS. There might not be enough data to check next frame
* - Sync is lost, but the following data after this frame seem
* to contain a valid header as well (and there is enough data to
* perform this check)
*/
if (amrparse->sync || amrparse->eos ||
(dsize >= fsize && (data[fsize] & 0x83) == 0)) {
amrparse->sync = TRUE;
*framesize = fsize;
return TRUE;
}
}
GST_LOG ("sync lost");
amrparse->sync = FALSE;
return FALSE;
}
/**
* gst_amrparse_parse_frame:
* @parse: #GstBaseParse.
* @buffer: #GstBuffer.
*
* Implementation of "parse" vmethod in #GstBaseParse class.
*
* Returns: #GstFlowReturn defining the parsing status.
*/
GstFlowReturn
gst_amrparse_parse_frame (GstBaseParse * parse, GstBuffer * buffer)
{
GstAmrParse *amrparse;
amrparse = GST_AMRPARSE (parse);
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
gint64 btime;
gboolean r = gst_amrparse_convert (parse, GST_FORMAT_BYTES,
GST_BUFFER_OFFSET (buffer),
GST_FORMAT_TIME, &btime);
if (r) {
/* FIXME: What to do if the conversion fails? */
amrparse->ts = btime;
}
}
GST_BUFFER_DURATION (buffer) = AMR_FRAME_DURATION;
GST_BUFFER_TIMESTAMP (buffer) = amrparse->ts;
if (GST_CLOCK_TIME_IS_VALID (amrparse->ts)) {
amrparse->ts += AMR_FRAME_DURATION;
}
if (!(++amrparse->framecount % 50)) {
gst_amrparse_update_duration (amrparse);
}
amrparse->bytecount += GST_BUFFER_SIZE (buffer);
gst_buffer_set_caps (buffer, GST_PAD_CAPS (parse->srcpad));
return GST_FLOW_OK;
}
/**
* gst_amrparse_start:
* @parse: #GstBaseParse.
*
* Implementation of "start" vmethod in #GstBaseParse class.
*
* Returns: TRUE on success.
*/
gboolean
gst_amrparse_start (GstBaseParse * parse)
{
GstAmrParse *amrparse;
amrparse = GST_AMRPARSE (parse);
GST_DEBUG ("start");
amrparse->need_header = TRUE;
amrparse->sync = TRUE;
amrparse->eos = FALSE;
amrparse->framecount = 0;
amrparse->bytecount = 0;
amrparse->ts = 0;
return TRUE;
}
/**
* gst_amrparse_stop:
* @parse: #GstBaseParse.
*
* Implementation of "stop" vmethod in #GstBaseParse class.
*
* Returns: TRUE on success.
*/
gboolean
gst_amrparse_stop (GstBaseParse * parse)
{
GstAmrParse *amrparse;
amrparse = GST_AMRPARSE (parse);
GST_DEBUG ("stop");
amrparse->need_header = TRUE;
amrparse->ts = -1;
return TRUE;
}
/**
* gst_amrparse_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_amrparse_event (GstBaseParse * parse, GstEvent * event)
{
GstAmrParse *amrparse;
amrparse = GST_AMRPARSE (parse);
GST_DEBUG ("event");
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
amrparse->eos = TRUE;
GST_DEBUG ("EOS event");
break;
default:
break;
}
return parent_class->event (parse, event);
}
/**
* gst_amrparse_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_amrparse_convert (GstBaseParse * parse,
GstFormat src_format,
gint64 src_value, GstFormat dest_format, gint64 * dest_value)
{
gboolean ret = FALSE;
GstAmrParse *amrparse;
gfloat bpf;
amrparse = GST_AMRPARSE (parse);
/* We are not able to do any estimations until some data has been passed */
if (!amrparse->framecount)
return FALSE;
bpf = (gfloat) amrparse->bytecount / amrparse->framecount;
if (src_format == GST_FORMAT_BYTES) {
if (dest_format == GST_FORMAT_TIME) {
/* BYTES -> TIME conversion */
GST_DEBUG ("converting bytes -> time");
if (amrparse->framecount) {
*dest_value = AMR_FRAME_DURATION * src_value / bpf;
GST_DEBUG ("conversion result: %lld ms", *dest_value / GST_MSECOND);
ret = TRUE;
}
}
} else if (src_format == GST_FORMAT_TIME) {
GST_DEBUG ("converting time -> bytes");
if (dest_format == GST_FORMAT_BYTES) {
if (amrparse->framecount) {
*dest_value = bpf * src_value / AMR_FRAME_DURATION;
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) {
*dest_value = src_value * AMR_FRAME_DURATION;
ret = TRUE;
} else if (dest_format == GST_FORMAT_BYTES) {
}
}
return ret;
}
/**
* plugin_init:
* @plugin: GstPlugin
*
* Returns: TRUE on success.
*/
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "amrparse",
GST_RANK_PRIMARY + 1, GST_TYPE_AMRPARSE);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"amrparse",
"Adaptive Multi-Rate Parser",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);

View file

@ -0,0 +1,86 @@
/* GStreamer Adaptive Multi-Rate parser
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
* 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_AMRPARSE_H__
#define __GST_AMRPARSE_H__
#include <gst/gst.h>
#include "gstbaseparse.h"
G_BEGIN_DECLS
#define GST_TYPE_AMRPARSE \
(gst_amrparse_get_type())
#define GST_AMRPARSE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_AMRPARSE, GstAmrParse))
#define GST_AMRPARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AMRPARSE, GstAmrParseClass))
#define GST_IS_AMRPARSE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_AMRPARSE))
#define GST_IS_AMRPARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AMRPARSE))
typedef struct _GstAmrParse GstAmrParse;
typedef struct _GstAmrParseClass GstAmrParseClass;
/**
* GstAmrParse:
* @element: the parent element.
* @block_size: Pointer to frame size lookup table.
* @need_header: Tells whether the MIME header should be read in the beginning.
* @wide: Wideband mode.
* @eos: Indicates the EOS situation. Set when EOS event is received.
* @sync: Tells whether the parser is in sync.
* @framecount: Total amount of frames handled.
* @bytecount: Total amount of bytes handled.
* @ts: Timestamp of the current media.
*
* The opaque GstAacParse data structure.
*/
struct _GstAmrParse {
GstBaseParse element;
const gint *block_size;
gboolean need_header;
gboolean wide;
gboolean eos;
gboolean sync;
guint64 framecount;
guint64 bytecount;
GstClockTime ts;
};
/**
* GstAmrParseClass:
* @parent_class: Element parent class.
*
* The opaque GstAmrParseClass data structure.
*/
struct _GstAmrParseClass {
GstBaseParseClass parent_class;
};
GType gst_amrparse_get_type (void);
G_END_DECLS
#endif /* __GST_AMRPARSE_H__ */

1741
gst/amrparse/gstbaseparse.c Normal file

File diff suppressed because it is too large Load diff

237
gst/amrparse/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__ */