Add two new baseparse based parsers (aac and amr) from Bug #518857.
authorStefan Kost <ensonic@users.sourceforge.net>
Thu, 13 Nov 2008 12:59:34 +0000 (12:59 +0000)
committerStefan Kost <ensonic@users.sourceforge.net>
Thu, 13 Nov 2008 12:59:34 +0000 (12:59 +0000)
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.

12 files changed:
ChangeLog
configure.ac
gst/aacparse/Makefile.am [new file with mode: 0644]
gst/aacparse/gstaacparse.c [new file with mode: 0644]
gst/aacparse/gstaacparse.h [new file with mode: 0644]
gst/aacparse/gstbaseparse.c [new file with mode: 0644]
gst/aacparse/gstbaseparse.h [new file with mode: 0644]
gst/amrparse/Makefile.am [new file with mode: 0644]
gst/amrparse/gstamrparse.c [new file with mode: 0644]
gst/amrparse/gstamrparse.h [new file with mode: 0644]
gst/amrparse/gstbaseparse.c [new file with mode: 0644]
gst/amrparse/gstbaseparse.h [new file with mode: 0644]

index b96ae08..2eb5840 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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:
index 2a9f956..4527976 100644 (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
diff --git a/gst/aacparse/Makefile.am b/gst/aacparse/Makefile.am
new file mode 100644 (file)
index 0000000..1341e8d
--- /dev/null
@@ -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
diff --git a/gst/aacparse/gstaacparse.c b/gst/aacparse/gstaacparse.c
new file mode 100644 (file)
index 0000000..f7946f4
--- /dev/null
@@ -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);
diff --git a/gst/aacparse/gstaacparse.h b/gst/aacparse/gstaacparse.h
new file mode 100644 (file)
index 0000000..bbdbebc
--- /dev/null
@@ -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__ */
diff --git a/gst/aacparse/gstbaseparse.c b/gst/aacparse/gstbaseparse.c
new file mode 100644 (file)
index 0000000..18b396c
--- /dev/null
@@ -0,0 +1,1741 @@
+/* GStreamer
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *   Contact: Stefan Kost <stefan.kost@nokia.com>
+ * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>.
+ *
+ * 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:gstbaseparse
+ * @short_description: Base class for stream parsers
+ * @see_also: #GstBaseTransform
+ *
+ * This base class is for parser elements that process data and splits it 
+ * into separate audio/video/whatever frames.
+ *
+ * It provides for:
+ * <itemizedlist>
+ *   <listitem><para>One sinkpad and one srcpad</para></listitem>
+ *   <listitem><para>Handles state changes</para></listitem>
+ *   <listitem><para>Does flushing</para></listitem>
+ *   <listitem><para>Push mode</para></listitem>
+ *   <listitem><para>Pull mode</para></listitem>
+ *   <listitem><para>Handles events (NEWSEGMENT/EOS/FLUSH)</para></listitem>
+ *   <listitem><para>Handles seeking in both modes</para></listitem>
+ *   <listitem><para>
+ *        Handles POSITION/DURATION/SEEKING/FORMAT/CONVERT queries
+ *   </para></listitem>
+ * </itemizedlist>
+ *
+ * The purpose of this base class is to provide a basic functionality of
+ * a parser and share a lot of rather complex code.
+ *
+ * Description of the parsing mechanism:
+ * <orderedlist>
+ * <listitem>
+ *   <itemizedlist><title>Set-up phase</title>
+ *   <listitem><para>
+ *     GstBaseParse class calls @set_sink_caps to inform the subclass about
+ *     incoming sinkpad caps. Subclass should set the srcpad caps accordingly.
+ *   </para></listitem>
+ *   <listitem><para>
+ *     GstBaseParse calls @start to inform subclass that data processing is
+ *     about to start now.
+ *   </para></listitem>
+ *   <listitem><para>
+ *      At least in this point subclass needs to tell the GstBaseParse class
+ *      how big data chunks it wants to receive (min_frame_size). It can do 
+ *      this with @gst_base_parse_set_min_frame_size.
+ *   </para></listitem>
+ *   <listitem><para>
+ *      GstBaseParse class sets up appropriate data passing mode (pull/push)
+ *      and starts to process the data.
+ *   </para></listitem>
+ *   </itemizedlist>
+ * </listitem>
+ * <listitem>
+ *   <itemizedlist>
+ *   <title>Parsing phase</title>
+ *     <listitem><para>
+ *       GstBaseParse gathers at least min_frame_size bytes of data either 
+ *       by pulling it from upstream or collecting buffers into internal
+ *       #GstAdapter.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       A buffer of min_frame_size bytes is passed to subclass with
+ *       @check_valid_frame. Subclass checks the contents and returns TRUE
+ *       if the buffer contains a valid frame. It also needs to set the
+ *       @framesize according to the detected frame size. If buffer didn't
+ *       contain a valid frame, this call must return FALSE and optionally
+ *       set the @skipsize value to inform base class that how many bytes
+ *       it needs to skip in order to find a valid frame. The passed buffer
+ *       is read-only.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       After valid frame is found, it will be passed again to subclass with
+ *       @parse_frame call. Now subclass is responsible for parsing the
+ *       frame contents and setting the buffer timestamp, duration and caps.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       Finally the buffer can be pushed downstream and parsing loop starts
+ *       over again.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       During the parsing process GstBaseClass will handle both srcpad and
+ *       sinkpad events. They will be passed to subclass if @event or
+ *       @src_event callbacks have been provided.
+ *     </para></listitem>
+ *   </itemizedlist>
+ * </listitem>
+ * <listitem>
+ *   <itemizedlist><title>Shutdown phase</title>
+ *   <listitem><para>
+ *     GstBaseParse class calls @stop to inform the subclass that data
+ *     parsing will be stopped.
+ *   </para></listitem>
+ *   </itemizedlist>
+ * </listitem>
+ * </orderedlist>
+ *
+ * Subclass is responsible for providing pad template caps for
+ * source and sink pads. The pads need to be named "sink" and "src". It also 
+ * needs to set the fixed caps on srcpad, when the format is ensured (e.g. 
+ * when base class calls subclass' @set_sink_caps function).
+ *
+ * This base class uses GST_FORMAT_DEFAULT as a meaning of frames. So,
+ * subclass conversion routine needs to know that conversion from
+ * GST_FORMAT_TIME to GST_FORMAT_DEFAULT must return the
+ * frame number that can be found from the given byte position.
+ *
+ * GstBaseParse uses subclasses conversion methods also for seeking. If
+ * subclass doesn't provide @convert function, seeking will get disabled.
+ *
+ * Subclass @start and @stop functions will be called to inform the beginning
+ * and end of data processing.
+ *
+ * Things that subclass need to take care of:
+ * <itemizedlist>
+ *   <listitem><para>Provide pad templates</para></listitem>
+ *   <listitem><para>
+ *      Fixate the source pad caps when appropriate
+ *   </para></listitem>
+ *   <listitem><para>
+ *      Inform base class how big data chunks should be retrieved. This is
+ *      done with @gst_base_parse_set_min_frame_size function.
+ *   </para></listitem>
+ *   <listitem><para>
+ *      Examine data chunks passed to subclass with @check_valid_frame
+ *      and tell if they contain a valid frame
+ *   </para></listitem>
+ *   <listitem><para>
+ *      Set the caps and timestamp to frame that is passed to subclass with
+ *      @parse_frame function.
+ *   </para></listitem>
+ *   <listitem><para>Provide conversion functions</para></listitem>
+ *   <listitem><para>
+ *      Update the duration information with @gst_base_parse_set_duration
+ *   </para></listitem>
+ * </itemizedlist>
+ *
+ */
+
+/* TODO:
+ *  - Better segment handling:
+ *    - NEWSEGMENT for gaps
+ *    - Not NEWSEGMENT starting at 0 but at first frame timestamp
+ *  - GstIndex support
+ *  - Seek table generation and subclass seek entry injection
+ *  - Accurate seeking
+ *  - In push mode provide a queue of adapter-"queued" buffers for upstream
+ *    buffer metadata
+ *  - Timestamp tracking and setting
+ *  - Handle upstream timestamps
+ *  - Queue buffers/events until caps are set
+ *  - Bitrate tracking => inaccurate seeking, inaccurate duration calculation
+ *  - Let subclass decide if frames outside the segment should be dropped
+ *  - Send queries upstream
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gstbaseparse.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_base_parse_debug);
+#define GST_CAT_DEFAULT gst_base_parse_debug
+
+/* Supported formats */
+static GstFormat fmtlist[] = {
+  GST_FORMAT_DEFAULT,
+  GST_FORMAT_BYTES,
+  GST_FORMAT_TIME,
+  0
+};
+
+#define GST_BASE_PARSE_GET_PRIVATE(obj)  \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_PARSE, GstBaseParsePrivate))
+
+struct _GstBaseParsePrivate
+{
+  GstActivateMode pad_mode;
+
+  gint64 duration;
+  GstFormat duration_fmt;
+
+  guint min_frame_size;
+
+  gboolean discont;
+  gboolean flushing;
+
+  gint64 offset;
+  gint64 pending_offset;
+
+  GList *pending_events;
+
+  GstBuffer *cache;
+};
+
+struct _GstBaseParseClassPrivate
+{
+  gpointer _padding;
+};
+
+static GstElementClass *parent_class = NULL;
+
+static void gst_base_parse_base_init (gpointer g_class);
+static void gst_base_parse_base_finalize (gpointer g_class);
+static void gst_base_parse_class_init (GstBaseParseClass * klass);
+static void gst_base_parse_init (GstBaseParse * parse,
+    GstBaseParseClass * klass);
+
+GType
+gst_base_parse_get_type (void)
+{
+  static GType base_parse_type = 0;
+
+  if (!base_parse_type) {
+    static const GTypeInfo base_parse_info = {
+      sizeof (GstBaseParseClass),
+      (GBaseInitFunc) gst_base_parse_base_init,
+      (GBaseFinalizeFunc) gst_base_parse_base_finalize,
+      (GClassInitFunc) gst_base_parse_class_init,
+      NULL,
+      NULL,
+      sizeof (GstBaseParse),
+      0,
+      (GInstanceInitFunc) gst_base_parse_init,
+    };
+
+    base_parse_type = g_type_register_static (GST_TYPE_ELEMENT,
+        "GstFlacBaseParse", &base_parse_info, G_TYPE_FLAG_ABSTRACT);
+  }
+  return base_parse_type;
+}
+
+static void gst_base_parse_finalize (GObject * object);
+
+static gboolean gst_base_parse_sink_activate (GstPad * sinkpad);
+static gboolean gst_base_parse_sink_activate_push (GstPad * pad,
+    gboolean active);
+static gboolean gst_base_parse_sink_activate_pull (GstPad * pad,
+    gboolean active);
+static gboolean gst_base_parse_handle_seek (GstBaseParse * parse,
+    GstEvent * event);
+
+static gboolean gst_base_parse_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_base_parse_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_base_parse_query (GstPad * pad, GstQuery * query);
+static gboolean gst_base_parse_sink_setcaps (GstPad * pad, GstCaps * caps);
+static const GstQueryType *gst_base_parse_get_querytypes (GstPad * pad);
+
+static GstFlowReturn gst_base_parse_chain (GstPad * pad, GstBuffer * buffer);
+static void gst_base_parse_loop (GstPad * pad);
+
+static gboolean gst_base_parse_check_frame (GstBaseParse * parse,
+    GstBuffer * buffer, guint * framesize, gint * skipsize);
+
+static gboolean gst_base_parse_parse_frame (GstBaseParse * parse,
+    GstBuffer * buffer);
+
+static gboolean gst_base_parse_sink_eventfunc (GstBaseParse * parse,
+    GstEvent * event);
+
+static gboolean gst_base_parse_src_eventfunc (GstBaseParse * parse,
+    GstEvent * event);
+
+static gboolean gst_base_parse_is_seekable (GstBaseParse * parse);
+
+static void gst_base_parse_drain (GstBaseParse * parse);
+
+static void
+gst_base_parse_base_init (gpointer g_class)
+{
+  GstBaseParseClass *klass = GST_BASE_PARSE_CLASS (g_class);
+  GstBaseParseClassPrivate *priv;
+
+  GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "flacbaseparse", 0,
+      "baseparse element");
+
+  /* TODO: Remove this once GObject supports class private data */
+  priv = g_slice_new0 (GstBaseParseClassPrivate);
+  if (klass->priv)
+    memcpy (priv, klass->priv, sizeof (GstBaseParseClassPrivate));
+  klass->priv = priv;
+}
+
+static void
+gst_base_parse_base_finalize (gpointer g_class)
+{
+  GstBaseParseClass *klass = GST_BASE_PARSE_CLASS (g_class);
+
+  g_slice_free (GstBaseParseClassPrivate, klass->priv);
+  klass->priv = NULL;
+}
+
+static void
+gst_base_parse_finalize (GObject * object)
+{
+  GstBaseParse *parse = GST_BASE_PARSE (object);
+
+  g_mutex_free (parse->parse_lock);
+  g_object_unref (parse->adapter);
+
+  if (parse->pending_segment) {
+    gst_event_replace (&parse->pending_segment, NULL);
+  }
+  if (parse->close_segment) {
+    gst_event_replace (&parse->close_segment, NULL);
+  }
+
+  if (parse->priv->cache) {
+    gst_buffer_unref (parse->priv->cache);
+    parse->priv->cache = NULL;
+  }
+
+  g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref,
+      NULL);
+  g_list_free (parse->priv->pending_events);
+  parse->priv->pending_events = NULL;
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_base_parse_class_init (GstBaseParseClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  g_type_class_add_private (klass, sizeof (GstBaseParsePrivate));
+  parent_class = g_type_class_peek_parent (klass);
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_parse_finalize);
+
+  /* Default handlers */
+  klass->check_valid_frame = gst_base_parse_check_frame;
+  klass->parse_frame = gst_base_parse_parse_frame;
+  klass->event = gst_base_parse_sink_eventfunc;
+  klass->src_event = gst_base_parse_src_eventfunc;
+  klass->is_seekable = gst_base_parse_is_seekable;
+}
+
+static void
+gst_base_parse_init (GstBaseParse * parse, GstBaseParseClass * bclass)
+{
+  GstPadTemplate *pad_template;
+
+  GST_DEBUG_OBJECT (parse, "gst_base_parse_init");
+
+  parse->priv = GST_BASE_PARSE_GET_PRIVATE (parse);
+
+  pad_template =
+      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink");
+  g_return_if_fail (pad_template != NULL);
+  parse->sinkpad = gst_pad_new_from_template (pad_template, "sink");
+  gst_pad_set_event_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_event));
+  gst_pad_set_setcaps_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_setcaps));
+  gst_pad_set_chain_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_chain));
+  gst_pad_set_activate_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate));
+  gst_pad_set_activatepush_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate_push));
+  gst_pad_set_activatepull_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate_pull));
+  gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
+
+  GST_DEBUG_OBJECT (parse, "sinkpad created");
+
+  pad_template =
+      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src");
+  g_return_if_fail (pad_template != NULL);
+  parse->srcpad = gst_pad_new_from_template (pad_template, "src");
+  gst_pad_set_event_function (parse->srcpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_src_event));
+  gst_pad_set_query_type_function (parse->srcpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_get_querytypes));
+  gst_pad_set_query_function (parse->srcpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_query));
+  gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
+  GST_DEBUG_OBJECT (parse, "src created");
+
+  parse->parse_lock = g_mutex_new ();
+  parse->adapter = gst_adapter_new ();
+  parse->pending_segment = NULL;
+  parse->close_segment = NULL;
+
+  parse->priv->pad_mode = GST_ACTIVATE_NONE;
+  parse->priv->duration = -1;
+  parse->priv->min_frame_size = 1;
+  parse->priv->discont = FALSE;
+  parse->priv->flushing = FALSE;
+  parse->priv->offset = 0;
+  GST_DEBUG_OBJECT (parse, "init ok");
+}
+
+
+
+/**
+ * gst_base_parse_check_frame:
+ * @parse: #GstBaseParse.
+ * @buffer: GstBuffer.
+ * @framesize: This will be set to tell the found frame size in bytes.
+ * @skipsize: Output parameter that tells how much data needs to be skipped
+ *            in order to find the following frame header.
+ *
+ * Default callback for check_valid_frame.
+ * 
+ * Returns: Always TRUE.
+ */
+static gboolean
+gst_base_parse_check_frame (GstBaseParse * parse,
+    GstBuffer * buffer, guint * framesize, gint * skipsize)
+{
+  *framesize = GST_BUFFER_SIZE (buffer);
+  *skipsize = 0;
+  return TRUE;
+}
+
+
+/**
+ * gst_base_parse_parse_frame:
+ * @parse: #GstBaseParse.
+ * @buffer: #GstBuffer.
+ *
+ * Default callback for parse_frame.
+ */
+static gboolean
+gst_base_parse_parse_frame (GstBaseParse * parse, GstBuffer * buffer)
+{
+  /* FIXME: Could we even _try_ to do something clever here? */
+  GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+  GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+  return TRUE;
+}
+
+
+/**
+ * gst_base_parse_bytepos_to_time:
+ * @parse: #GstBaseParse.
+ * @bytepos: Position (in bytes) to be converted.
+ * @pos_in_time: #GstClockTime pointer where the result is set.
+ *
+ * Convert given byte position into #GstClockTime format.
+ * 
+ * Returns: TRUE if conversion succeeded.
+ */
+static gboolean
+gst_base_parse_bytepos_to_time (GstBaseParse * parse, gint64 bytepos,
+    GstClockTime * pos_in_time)
+{
+  GstBaseParseClass *klass;
+  gboolean res = FALSE;
+
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (klass->convert) {
+    res = klass->convert (parse, GST_FORMAT_BYTES, bytepos,
+        GST_FORMAT_TIME, (gint64 *) pos_in_time);
+  }
+  return res;
+}
+
+
+/**
+ * gst_base_parse_sink_event:
+ * @pad: #GstPad that received the event.
+ * @event: #GstEvent to be handled.
+ *
+ * Handler for sink pad events.
+ *
+ * Returns: TRUE if the event was handled.
+ */
+static gboolean
+gst_base_parse_sink_event (GstPad * pad, GstEvent * event)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *bclass;
+  gboolean handled = FALSE;
+  gboolean ret = TRUE;
+
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  GST_DEBUG_OBJECT (parse, "handling event %d", GST_EVENT_TYPE (event));
+
+  /* Cache all events except EOS, NEWSEGMENT and FLUSH_STOP if we have a
+   * pending segment */
+  if (parse->pending_segment && GST_EVENT_TYPE (event) != GST_EVENT_EOS
+      && GST_EVENT_TYPE (event) != GST_EVENT_NEWSEGMENT
+      && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_START
+      && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP) {
+    parse->priv->pending_events =
+        g_list_append (parse->priv->pending_events, event);
+    ret = TRUE;
+  } else {
+
+    if (bclass->event)
+      handled = bclass->event (parse, event);
+
+    if (!handled)
+      ret = gst_pad_event_default (pad, event);
+  }
+
+  gst_object_unref (parse);
+  GST_DEBUG_OBJECT (parse, "event handled");
+  return ret;
+}
+
+
+/**
+ * gst_base_parse_sink_eventfunc:
+ * @parse: #GstBaseParse.
+ * @event: #GstEvent to be handled.
+ *
+ * Element-level event handler function.
+ *
+ * Returns: TRUE if the event was handled and not need forwarding.
+ */
+static gboolean
+gst_base_parse_sink_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+  gboolean handled = FALSE;
+  GstEvent **eventp;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+    {
+      gdouble rate, applied_rate;
+      GstFormat format;
+      gint64 start, stop, pos;
+      gboolean update;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
+          &format, &start, &stop, &pos);
+
+
+      if (format == GST_FORMAT_BYTES) {
+        GstClockTime seg_start, seg_stop, seg_pos;
+
+        /* stop time is allowed to be open-ended, but not start & pos */
+        seg_stop = GST_CLOCK_TIME_NONE;
+        parse->priv->pending_offset = pos;
+
+        if (gst_base_parse_bytepos_to_time (parse, start, &seg_start) &&
+            gst_base_parse_bytepos_to_time (parse, pos, &seg_pos)) {
+          gst_event_unref (event);
+          event = gst_event_new_new_segment_full (update, rate, applied_rate,
+              GST_FORMAT_TIME, seg_start, seg_stop, seg_pos);
+          format = GST_FORMAT_TIME;
+          GST_DEBUG_OBJECT (parse, "Converted incoming segment to TIME. "
+              "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+              ", pos = %" GST_TIME_FORMAT, GST_TIME_ARGS (seg_start),
+              GST_TIME_ARGS (seg_stop), GST_TIME_ARGS (seg_pos));
+        }
+      }
+
+      if (format != GST_FORMAT_TIME) {
+        /* Unknown incoming segment format. Output a default open-ended 
+         * TIME segment */
+        gst_event_unref (event);
+        event = gst_event_new_new_segment_full (update, rate, applied_rate,
+            GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0);
+      }
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
+          &format, &start, &stop, &pos);
+
+      gst_segment_set_newsegment_full (&parse->segment, update, rate,
+          applied_rate, format, start, stop, pos);
+
+      GST_DEBUG_OBJECT (parse, "Created newseg rate %g, applied rate %g, "
+          "format %d, start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+          ", pos = %" GST_TIME_FORMAT, rate, applied_rate, format,
+          GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (pos));
+
+      /* save the segment for later, right before we push a new buffer so that
+       * the caps are fixed and the next linked element can receive
+       * the segment. */
+      eventp = &parse->pending_segment;
+      gst_event_replace (eventp, event);
+      gst_event_unref (event);
+      handled = TRUE;
+      break;
+    }
+
+    case GST_EVENT_FLUSH_START:
+      parse->priv->flushing = TRUE;
+      /* Wait for _chain() to exit by taking the srcpad STREAM_LOCK */
+      GST_PAD_STREAM_LOCK (parse->srcpad);
+      handled = gst_pad_push_event (parse->srcpad, event);
+      GST_PAD_STREAM_UNLOCK (parse->srcpad);
+      break;
+
+    case GST_EVENT_FLUSH_STOP:
+      gst_adapter_clear (parse->adapter);
+      parse->priv->flushing = FALSE;
+      parse->priv->discont = TRUE;
+      break;
+
+    case GST_EVENT_EOS:
+      gst_base_parse_drain (parse);
+      break;
+
+    default:
+      break;
+  }
+
+  return handled;
+}
+
+
+/**
+ * gst_base_parse_src_event:
+ * @pad: #GstPad that received the event.
+ * @event: #GstEvent that was received.
+ *
+ * Handler for source pad events.
+ *
+ * Returns: TRUE if the event was handled.
+ */
+static gboolean
+gst_base_parse_src_event (GstPad * pad, GstEvent * event)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *bclass;
+  gboolean handled = FALSE;
+  gboolean ret = TRUE;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  GST_DEBUG_OBJECT (parse, "event %d, %s", GST_EVENT_TYPE (event),
+      GST_EVENT_TYPE_NAME (event));
+
+  if (bclass->src_event)
+    handled = bclass->src_event (parse, event);
+
+  if (!handled)
+    ret = gst_pad_event_default (pad, event);
+
+  gst_object_unref (parse);
+  return ret;
+}
+
+
+/**
+ * gst_base_parse_src_eventfunc:
+ * @parse: #GstBaseParse.
+ * @event: #GstEvent that was received.
+ *
+ * Default srcpad event handler.
+ *
+ * Returns: TRUE if the event was handled and can be dropped.
+ */
+static gboolean
+gst_base_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+  gboolean handled = FALSE;
+  GstBaseParseClass *bclass;
+
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+    {
+      if (bclass->is_seekable (parse)) {
+        handled = gst_base_parse_handle_seek (parse, event);
+        gst_event_unref (event);
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return handled;
+}
+
+
+/**
+ * gst_base_parse_is_seekable:
+ * @parse: #GstBaseParse.
+ *
+ * Default handler for is_seekable.
+ *
+ * Returns: Always TRUE.
+ */
+static gboolean
+gst_base_parse_is_seekable (GstBaseParse * parse)
+{
+  return TRUE;
+}
+
+
+/**
+ * gst_base_parse_handle_and_push_buffer:
+ * @parse: #GstBaseParse.
+ * @klass: #GstBaseParseClass.
+ * @buffer: #GstBuffer.
+ *
+ * Parses the frame from given buffer and pushes it forward. Also performs
+ * timestamp handling and checks the segment limits.
+ *
+ * This is called with srcpad STREAM_LOCK held.
+ *
+ * Returns: #GstFlowReturn
+ */
+static GstFlowReturn
+gst_base_parse_handle_and_push_buffer (GstBaseParse * parse,
+    GstBaseParseClass * klass, GstBuffer * buffer)
+{
+  GstFlowReturn ret;
+  GstClockTime last_stop = GST_CLOCK_TIME_NONE;
+
+  if (parse->priv->discont) {
+    GST_DEBUG_OBJECT (parse, "marking DISCONT");
+    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+    parse->priv->discont = FALSE;
+  }
+
+  ret = klass->parse_frame (parse, buffer);
+
+  /* FIXME: Check the output buffer for any missing metadata,
+   *        keep track of timestamp and calculate everything possible
+   *        if not set already */
+
+  if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
+    last_stop = GST_BUFFER_TIMESTAMP (buffer);
+  if (last_stop != GST_CLOCK_TIME_NONE && GST_BUFFER_DURATION_IS_VALID (buffer))
+    last_stop += GST_BUFFER_DURATION (buffer);
+
+  gst_buffer_set_caps (buffer, GST_PAD_CAPS (parse->srcpad));
+
+  /* TODO: Add to seek table */
+
+  if (ret == GST_FLOW_OK) {
+    if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
+        GST_CLOCK_TIME_IS_VALID (parse->segment.stop) &&
+        GST_BUFFER_TIMESTAMP (buffer) > parse->segment.stop) {
+      GST_LOG_OBJECT (parse, "Dropped frame, after segment");
+      gst_buffer_unref (buffer);
+    } else if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
+        GST_BUFFER_DURATION_IS_VALID (buffer) &&
+        GST_CLOCK_TIME_IS_VALID (parse->segment.start) &&
+        GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION_IS_VALID (buffer)
+        < parse->segment.start) {
+      /* FIXME: subclass needs way to override the start as downstream might
+       * need frames before for proper decoding */
+      GST_LOG_OBJECT (parse, "Dropped frame, before segment");
+      gst_buffer_unref (buffer);
+    } else {
+      ret = gst_pad_push (parse->srcpad, buffer);
+      GST_LOG_OBJECT (parse, "frame (%d bytes) pushed: %d",
+          GST_BUFFER_SIZE (buffer), ret);
+    }
+  } else {
+    gst_buffer_unref (buffer);
+  }
+
+  /* Update current running segment position */
+  if (ret == GST_FLOW_OK && last_stop != GST_CLOCK_TIME_NONE)
+    gst_segment_set_last_stop (&parse->segment, GST_FORMAT_TIME, last_stop);
+
+  /* convert internal flow to OK and mark discont for the next buffer. */
+  if (ret == GST_BASE_PARSE_FLOW_DROPPED) {
+    parse->priv->discont = TRUE;
+    ret = GST_FLOW_OK;
+  }
+  return ret;
+}
+
+
+/**
+ * gst_base_parse_drain:
+ * @parse: #GstBaseParse.
+ *
+ * Drains the adapter until it is empty. It decreases the min_frame_size to
+ * match the current adapter size and calls chain method until the adapter
+ * is emptied or chain returns with error.
+ */
+static void
+gst_base_parse_drain (GstBaseParse * parse)
+{
+  guint avail;
+
+  for (;;) {
+    avail = gst_adapter_available (parse->adapter);
+    if (!avail)
+      break;
+
+    gst_base_parse_set_min_frame_size (parse, avail);
+    if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) {
+      break;
+    }
+  }
+}
+
+
+/**
+ * gst_base_parse_chain:
+ * @pad: #GstPad.
+ * @buffer: #GstBuffer.
+ *
+ * Returns: #GstFlowReturn.
+ */
+static GstFlowReturn
+gst_base_parse_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstBaseParseClass *bclass;
+  GstBaseParse *parse;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstBuffer *outbuf = NULL;
+  GstBuffer *tmpbuf = NULL;
+  guint fsize = 0;
+  gint skip = -1;
+  const guint8 *data;
+  guint min_size;
+
+  parse = GST_BASE_PARSE (GST_OBJECT_PARENT (pad));
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (parse->pending_segment) {
+    GST_DEBUG_OBJECT (parse, "chain pushing a pending segment");
+    gst_pad_push_event (parse->srcpad, parse->pending_segment);
+    parse->pending_segment = NULL;
+    parse->priv->offset = parse->priv->pending_offset;
+
+    /* Make sure that adapter doesn't have any old data after
+       newsegment has been pushed */
+
+    /* FIXME: when non-flushing seek occurs, chain is still processing the
+       data from old segment. If this processing loop is then interrupted
+       (e.g. paused), chain function exists and next time it gets called
+       all this old data gets lost and playback continues from new segment */
+    gst_adapter_clear (parse->adapter);
+  }
+
+  if (parse->priv->pending_events) {
+    GList *l;
+
+    for (l = parse->priv->pending_events; l != NULL; l = l->next) {
+      gst_pad_push_event (parse->srcpad, GST_EVENT (l->data));
+    }
+    g_list_free (parse->priv->pending_events);
+    parse->priv->pending_events = NULL;
+  }
+
+  if (buffer) {
+    GST_LOG_OBJECT (parse, "buffer size: %d, offset = %lld",
+        GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer));
+
+
+    gst_adapter_push (parse->adapter, buffer);
+  }
+
+  /* Parse and push as many frames as possible */
+  /* Stop either when adapter is empty or we are flushing */
+  while (!parse->priv->flushing) {
+    tmpbuf = gst_buffer_new ();
+
+    GST_BASE_PARSE_LOCK (parse);
+    min_size = parse->priv->min_frame_size;
+    GST_BASE_PARSE_UNLOCK (parse);
+
+    /* Synchronization loop */
+    for (;;) {
+      /* Collect at least min_frame_size bytes */
+      if (gst_adapter_available (parse->adapter) < min_size) {
+        GST_DEBUG_OBJECT (parse, "not enough data available (only %d bytes)",
+            gst_adapter_available (parse->adapter));
+        gst_buffer_unref (tmpbuf);
+        goto done;
+      }
+
+      data = gst_adapter_peek (parse->adapter, min_size);
+      GST_BUFFER_DATA (tmpbuf) = (guint8 *) data;
+      GST_BUFFER_SIZE (tmpbuf) = min_size;
+      GST_BUFFER_OFFSET (tmpbuf) = parse->priv->offset;
+      GST_BUFFER_FLAG_SET (tmpbuf, GST_MINI_OBJECT_FLAG_READONLY);
+
+      if (parse->priv->discont) {
+        GST_DEBUG_OBJECT (parse, "marking DISCONT");
+        GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+      }
+
+      skip = -1;
+      if (bclass->check_valid_frame (parse, tmpbuf, &fsize, &skip)) {
+        break;
+      }
+      if (skip > 0) {
+        GST_LOG_OBJECT (parse, "finding sync, skipping %d bytes", skip);
+        gst_adapter_flush (parse->adapter, skip);
+        parse->priv->offset += skip;
+      } else if (skip == -1) {
+        /* subclass didn't touch this value. By default we skip 1 byte */
+        GST_LOG_OBJECT (parse, "finding sync, skipping 1 byte");
+        gst_adapter_flush (parse->adapter, 1);
+        parse->priv->offset++;
+      }
+      /* There is a possibility that subclass set the skip value to zero.
+         This means that it has probably found a frame but wants to ask
+         more data (by increasing the min_size) to be sure of this. */
+    }
+    gst_buffer_unref (tmpbuf);
+    tmpbuf = NULL;
+
+    if (skip > 0) {
+      /* Subclass found the sync, but still wants to skip some data */
+      GST_LOG_OBJECT (parse, "skipping %d bytes", skip);
+      gst_adapter_flush (parse->adapter, skip);
+      parse->priv->offset += skip;
+    }
+
+    /* Grab lock to prevent a race with FLUSH_START handler */
+    GST_PAD_STREAM_LOCK (parse->srcpad);
+
+    /* FLUSH_START event causes the "flushing" flag to be set. In this
+     * case we can leave the frame pushing loop */
+    if (parse->priv->flushing) {
+      GST_PAD_STREAM_UNLOCK (parse->srcpad);
+      break;
+    }
+
+    /* FIXME: Would it be more efficient to make a subbuffer instead? */
+    outbuf = gst_adapter_take_buffer (parse->adapter, fsize);
+
+    /* Subclass may want to know the data offset */
+    GST_BUFFER_OFFSET (outbuf) = parse->priv->offset;
+    parse->priv->offset += fsize;
+
+    ret = gst_base_parse_handle_and_push_buffer (parse, bclass, outbuf);
+    GST_PAD_STREAM_UNLOCK (parse->srcpad);
+
+    if (ret != GST_FLOW_OK) {
+      GST_LOG_OBJECT (parse, "push returned %d", ret);
+      break;
+    }
+  }
+
+done:
+  GST_LOG_OBJECT (parse, "chain leaving");
+  return ret;
+}
+
+static GstFlowReturn
+gst_base_parse_pull_range (GstBaseParse * parse, guint size,
+    GstBuffer ** buffer)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+
+  /* Caching here actually makes much less difference than one would expect.
+   * We do it mainly to avoid pulling buffers of 1 byte all the time */
+  if (parse->priv->cache) {
+    guint64 cache_offset = GST_BUFFER_OFFSET (parse->priv->cache);
+    guint cache_size = GST_BUFFER_SIZE (parse->priv->cache);
+
+    if (cache_offset <= parse->priv->offset &&
+        (parse->priv->offset + size) < (cache_offset + cache_size)) {
+      *buffer = gst_buffer_create_sub (parse->priv->cache,
+          parse->priv->offset - cache_offset, size);
+      GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
+      return GST_FLOW_OK;
+    }
+    /* not enough data in the cache, free cache and get a new one */
+    gst_buffer_unref (parse->priv->cache);
+    parse->priv->cache = NULL;
+  }
+
+  /* refill the cache */
+  ret =
+      gst_pad_pull_range (parse->sinkpad, parse->priv->offset, MAX (size,
+          64 * 1024), &parse->priv->cache);
+  if (ret != GST_FLOW_OK) {
+    parse->priv->cache = NULL;
+    return ret;
+  }
+
+  if (GST_BUFFER_SIZE (parse->priv->cache) >= size) {
+    *buffer = gst_buffer_create_sub (parse->priv->cache, 0, size);
+    GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
+    return GST_FLOW_OK;
+  }
+
+  /* Not possible to get enough data, try a last time with
+   * requesting exactly the size we need */
+  gst_buffer_unref (parse->priv->cache);
+  parse->priv->cache = NULL;
+
+  ret = gst_pad_pull_range (parse->sinkpad, parse->priv->offset, size,
+      &parse->priv->cache);
+
+  if (ret != GST_FLOW_OK) {
+    GST_DEBUG_OBJECT (parse, "pull_range returned %d", ret);
+    *buffer = NULL;
+    return ret;
+  }
+
+  if (GST_BUFFER_SIZE (parse->priv->cache) < size) {
+    GST_WARNING_OBJECT (parse, "Dropping short buffer at offset %"
+        G_GUINT64_FORMAT ": wanted %u bytes, got %u bytes", parse->priv->offset,
+        size, GST_BUFFER_SIZE (parse->priv->cache));
+
+    gst_buffer_unref (parse->priv->cache);
+    parse->priv->cache = NULL;
+
+    *buffer = NULL;
+    return GST_FLOW_UNEXPECTED;
+  }
+
+  *buffer = gst_buffer_create_sub (parse->priv->cache, 0, size);
+  GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
+
+  return GST_FLOW_OK;
+}
+
+/**
+ * gst_base_parse_loop:
+ * @pad: GstPad
+ *
+ * Loop that is used in pull mode to retrieve data from upstream.
+ */
+static void
+gst_base_parse_loop (GstPad * pad)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *klass;
+  GstBuffer *buffer, *outbuf;
+  gboolean ret = FALSE;
+  guint fsize = 0, min_size;
+  gint skip = 0;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (parse->close_segment) {
+    GST_DEBUG_OBJECT (parse, "loop sending close segment");
+    gst_pad_push_event (parse->srcpad, parse->close_segment);
+    parse->close_segment = NULL;
+  }
+
+  if (parse->pending_segment) {
+    GST_DEBUG_OBJECT (parse, "loop push pending segment");
+    gst_pad_push_event (parse->srcpad, parse->pending_segment);
+    parse->pending_segment = NULL;
+  }
+
+  /* TODO: Check if we reach segment stop limit */
+
+  while (TRUE) {
+
+    GST_BASE_PARSE_LOCK (parse);
+    min_size = parse->priv->min_frame_size;
+    GST_BASE_PARSE_UNLOCK (parse);
+
+    ret = gst_base_parse_pull_range (parse, min_size, &buffer);
+
+    if (ret == GST_FLOW_UNEXPECTED)
+      goto eos;
+    else if (ret != GST_FLOW_OK)
+      goto need_pause;
+
+    if (parse->priv->discont) {
+      GST_DEBUG_OBJECT (parse, "marking DISCONT");
+      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+    }
+
+    skip = -1;
+    if (klass->check_valid_frame (parse, buffer, &fsize, &skip)) {
+      break;
+    }
+    if (skip > 0) {
+      GST_LOG_OBJECT (parse, "finding sync, skipping %d bytes", skip);
+      parse->priv->offset += skip;
+    } else if (skip == -1) {
+      GST_LOG_OBJECT (parse, "finding sync, skipping 1 byte");
+      parse->priv->offset++;
+    }
+    GST_DEBUG_OBJECT (parse, "finding sync...");
+    gst_buffer_unref (buffer);
+  }
+
+  if (fsize <= GST_BUFFER_SIZE (buffer)) {
+    outbuf = gst_buffer_create_sub (buffer, 0, fsize);
+    GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer);
+    gst_buffer_unref (buffer);
+  } else {
+    gst_buffer_unref (buffer);
+    ret = gst_base_parse_pull_range (parse, fsize, &outbuf);
+
+    if (ret == GST_FLOW_UNEXPECTED)
+      goto eos;
+    else if (ret != GST_FLOW_OK)
+      goto need_pause;
+  }
+
+  parse->priv->offset += fsize;
+
+  /* Does the subclass want to skip too? */
+  if (skip > 0)
+    parse->priv->offset += skip;
+
+  /* This always unrefs the outbuf, even if error occurs */
+  ret = gst_base_parse_handle_and_push_buffer (parse, klass, outbuf);
+
+  if (ret != GST_FLOW_OK) {
+    GST_DEBUG_OBJECT (parse, "flow: %s", gst_flow_get_name (ret));
+    if (GST_FLOW_IS_FATAL (ret)) {
+      GST_ELEMENT_ERROR (parse, STREAM, FAILED, (NULL),
+          ("streaming task paused, reason: %s", gst_flow_get_name (ret)));
+      gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
+    }
+    goto need_pause;
+  }
+
+  gst_object_unref (parse);
+  return;
+
+need_pause:
+  {
+    GST_LOG_OBJECT (parse, "pausing task");
+    gst_pad_pause_task (pad);
+    gst_object_unref (parse);
+    return;
+  }
+eos:
+  {
+    GST_LOG_OBJECT (parse, "pausing task %d", ret);
+    gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
+    gst_pad_pause_task (pad);
+    gst_object_unref (parse);
+    return;
+  }
+}
+
+
+/**
+ * gst_base_parse_sink_activate:
+ * @sinkpad: #GstPad to be activated.
+ *
+ * Returns: TRUE if activation succeeded.
+ */
+static gboolean
+gst_base_parse_sink_activate (GstPad * sinkpad)
+{
+  GstBaseParse *parse;
+  gboolean result = TRUE;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (sinkpad));
+
+  GST_DEBUG_OBJECT (parse, "sink activate");
+
+  if (gst_pad_check_pull_range (sinkpad)) {
+    GST_DEBUG_OBJECT (parse, "trying to activate in pull mode");
+    result = gst_pad_activate_pull (sinkpad, TRUE);
+  } else {
+    GST_DEBUG_OBJECT (parse, "trying to activate in push mode");
+    result = gst_pad_activate_push (sinkpad, TRUE);
+  }
+
+  GST_DEBUG_OBJECT (parse, "sink activate return %d", result);
+  gst_object_unref (parse);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_activate:
+ * @parse: #GstBaseParse.
+ * @active: TRUE if element will be activated, FALSE if disactivated.
+ *
+ * Returns: TRUE if the operation succeeded.
+ */
+static gboolean
+gst_base_parse_activate (GstBaseParse * parse, gboolean active)
+{
+  GstBaseParseClass *klass;
+  gboolean result = FALSE;
+
+  GST_DEBUG_OBJECT (parse, "activate");
+
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (active) {
+    if (parse->priv->pad_mode == GST_ACTIVATE_NONE && klass->start)
+      result = klass->start (parse);
+
+    GST_OBJECT_LOCK (parse);
+    gst_segment_init (&parse->segment, GST_FORMAT_TIME);
+    parse->priv->duration = -1;
+    parse->priv->discont = FALSE;
+    parse->priv->flushing = FALSE;
+    parse->priv->offset = 0;
+
+    if (parse->pending_segment)
+      gst_event_unref (parse->pending_segment);
+
+    parse->pending_segment =
+        gst_event_new_new_segment (FALSE, parse->segment.rate,
+        parse->segment.format,
+        parse->segment.start, parse->segment.stop, parse->segment.last_stop);
+
+    GST_OBJECT_UNLOCK (parse);
+  } else {
+    /* We must make sure streaming has finished before resetting things
+     * and calling the ::stop vfunc */
+    GST_PAD_STREAM_LOCK (parse->sinkpad);
+    GST_PAD_STREAM_UNLOCK (parse->sinkpad);
+
+    if (parse->priv->pad_mode != GST_ACTIVATE_NONE && klass->stop)
+      result = klass->stop (parse);
+
+    g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref,
+        NULL);
+    g_list_free (parse->priv->pending_events);
+    parse->priv->pending_events = NULL;
+
+    if (parse->priv->cache) {
+      gst_buffer_unref (parse->priv->cache);
+      parse->priv->cache = NULL;
+    }
+
+    parse->priv->pad_mode = GST_ACTIVATE_NONE;
+  }
+  GST_DEBUG_OBJECT (parse, "activate: %d", result);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_sink_activate_push:
+ * @pad: #GstPad to be (de)activated.
+ * @active: TRUE when activating, FALSE when deactivating.
+ *
+ * Returns: TRUE if (de)activation succeeded.
+ */
+static gboolean
+gst_base_parse_sink_activate_push (GstPad * pad, gboolean active)
+{
+  gboolean result = TRUE;
+  GstBaseParse *parse;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+
+  GST_DEBUG_OBJECT (parse, "sink activate push");
+
+  result = gst_base_parse_activate (parse, active);
+
+  if (result)
+    parse->priv->pad_mode = active ? GST_ACTIVATE_PUSH : GST_ACTIVATE_NONE;
+
+  GST_DEBUG_OBJECT (parse, "sink activate push: %d", result);
+
+  gst_object_unref (parse);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_sink_activate_pull:
+ * @sinkpad: #GstPad to be (de)activated.
+ * @active: TRUE when activating, FALSE when deactivating.
+ *
+ * Returns: TRUE if (de)activation succeeded.
+ */
+static gboolean
+gst_base_parse_sink_activate_pull (GstPad * sinkpad, gboolean active)
+{
+  gboolean result = FALSE;
+  GstBaseParse *parse;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (sinkpad));
+
+  GST_DEBUG_OBJECT (parse, "activate pull");
+
+  result = gst_base_parse_activate (parse, active);
+
+  if (result) {
+    if (active) {
+      result &= gst_pad_start_task (sinkpad,
+          (GstTaskFunction) gst_base_parse_loop, sinkpad);
+    } else {
+      result &= gst_pad_stop_task (sinkpad);
+    }
+  }
+
+  if (result)
+    parse->priv->pad_mode = active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE;
+
+  GST_DEBUG_OBJECT (parse, "sink activate pull: %d", result);
+
+  gst_object_unref (parse);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_set_duration:
+ * @parse: #GstBaseParse.
+ * @fmt: #GstFormat.
+ * @duration: duration value.
+ *
+ * Sets the duration of the currently playing media. Subclass can use this
+ * when it notices a change in the media duration.
+ */
+void
+gst_base_parse_set_duration (GstBaseParse * parse,
+    GstFormat fmt, gint64 duration)
+{
+  g_return_if_fail (parse != NULL);
+
+  GST_BASE_PARSE_LOCK (parse);
+  if (duration != parse->priv->duration) {
+    GstMessage *m;
+
+    m = gst_message_new_duration (GST_OBJECT (parse), fmt, duration);
+    gst_element_post_message (GST_ELEMENT (parse), m);
+
+    /* TODO: what about duration tag? */
+  }
+  parse->priv->duration = duration;
+  parse->priv->duration_fmt = fmt;
+  GST_DEBUG_OBJECT (parse, "set duration: %lld", duration);
+  GST_BASE_PARSE_UNLOCK (parse);
+}
+
+
+/**
+ * gst_base_parse_set_min_frame_size:
+ * @parse: #GstBaseParse.
+ * @min_size: Minimum size of the data that this base class should give to
+ *            subclass.
+ *
+ * Subclass can use this function to tell the base class that it needs to
+ * give at least #min_size buffers.
+ */
+void
+gst_base_parse_set_min_frame_size (GstBaseParse * parse, guint min_size)
+{
+  g_return_if_fail (parse != NULL);
+
+  GST_BASE_PARSE_LOCK (parse);
+  parse->priv->min_frame_size = min_size;
+  GST_LOG_OBJECT (parse, "set frame_min_size: %d", min_size);
+  GST_BASE_PARSE_UNLOCK (parse);
+}
+
+
+/**
+ * gst_base_parse_get_querytypes:
+ * @pad: GstPad
+ *
+ * Returns: A table of #GstQueryType items describing supported query types.
+ */
+static const GstQueryType *
+gst_base_parse_get_querytypes (GstPad * pad)
+{
+  static const GstQueryType list[] = {
+    GST_QUERY_POSITION,
+    GST_QUERY_DURATION,
+    GST_QUERY_FORMATS,
+    GST_QUERY_SEEKING,
+    GST_QUERY_CONVERT,
+    0
+  };
+
+  return list;
+}
+
+
+/**
+ * gst_base_parse_query:
+ * @pad: #GstPad.
+ * @query: #GstQuery.
+ *
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_base_parse_query (GstPad * pad, GstQuery * query)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *klass;
+  gboolean res = FALSE;
+
+  parse = GST_BASE_PARSE (GST_PAD_PARENT (pad));
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  /* If subclass doesn't provide conversion function we can't reply
+     to the query either */
+  if (!klass->convert) {
+    return FALSE;
+  }
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_POSITION:
+    {
+      gint64 dest_value;
+      GstFormat format;
+
+      GST_DEBUG_OBJECT (parse, "position query");
+
+      gst_query_parse_position (query, &format, NULL);
+
+      g_mutex_lock (parse->parse_lock);
+
+      if (format == GST_FORMAT_BYTES) {
+        dest_value = parse->priv->offset;
+        res = TRUE;
+      } else if (format == parse->segment.format &&
+          GST_CLOCK_TIME_IS_VALID (parse->segment.last_stop)) {
+        dest_value = parse->segment.last_stop;
+        res = TRUE;
+      } else {
+        /* priv->offset is updated in both PUSH/PULL modes */
+        res = klass->convert (parse, GST_FORMAT_BYTES, parse->priv->offset,
+            format, &dest_value);
+      }
+      g_mutex_unlock (parse->parse_lock);
+
+      if (res)
+        gst_query_set_position (query, format, dest_value);
+      else
+        res = gst_pad_query_default (pad, query);
+
+      break;
+    }
+    case GST_QUERY_DURATION:
+    {
+      GstFormat format;
+      gint64 dest_value;
+
+      GST_DEBUG_OBJECT (parse, "duration query");
+
+      gst_query_parse_duration (query, &format, NULL);
+
+      g_mutex_lock (parse->parse_lock);
+
+      if (format == GST_FORMAT_BYTES) {
+        res = gst_pad_query_peer_duration (parse->sinkpad, &format,
+            &dest_value);
+      } else if (parse->priv->duration != -1 &&
+          format == parse->priv->duration_fmt) {
+        dest_value = parse->priv->duration;
+        res = TRUE;
+      } else if (parse->priv->duration != -1) {
+        res = klass->convert (parse, parse->priv->duration_fmt,
+            parse->priv->duration, format, &dest_value);
+      }
+
+      g_mutex_unlock (parse->parse_lock);
+
+      if (res)
+        gst_query_set_duration (query, format, dest_value);
+      else
+        res = gst_pad_query_default (pad, query);
+      break;
+    }
+    case GST_QUERY_SEEKING:
+    {
+      GstFormat fmt;
+      gboolean seekable = FALSE;
+
+      GST_DEBUG_OBJECT (parse, "seeking query");
+
+      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
+
+      if (fmt != GST_FORMAT_TIME) {
+        return gst_pad_query_default (pad, query);
+      }
+
+      seekable = klass->is_seekable (parse);
+
+      /* TODO: could this duration be calculated/converted if subclass
+         hasn't given it? */
+      gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0,
+          (parse->priv->duration == -1) ?
+          GST_CLOCK_TIME_NONE : parse->priv->duration);
+
+      GST_DEBUG_OBJECT (parse, "seekable: %d", seekable);
+      res = TRUE;
+      break;
+    }
+    case GST_QUERY_FORMATS:
+      gst_query_set_formatsv (query, 3, fmtlist);
+      res = TRUE;
+      break;
+
+    case GST_QUERY_CONVERT:
+    {
+      GstFormat src_format, dest_format;
+      gint64 src_value, dest_value;
+
+      gst_query_parse_convert (query, &src_format, &src_value,
+          &dest_format, &dest_value);
+
+      /* FIXME: hm? doesn't make sense 
+       * We require all those values to be given
+       if (src_format && src_value && dest_format && dest_value ) { */
+      res = klass->convert (parse, src_format, src_value,
+          dest_format, &dest_value);
+      if (res) {
+        gst_query_set_convert (query, src_format, src_value,
+            dest_format, dest_value);
+      }
+      /*} */
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, query);
+      break;
+  }
+  return res;
+}
+
+
+/**
+ * gst_base_parse_handle_seek:
+ * @parse: #GstBaseParse.
+ * @event: #GstEvent.
+ *
+ * Returns: TRUE if seek succeeded.
+ */
+static gboolean
+gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
+{
+  GstBaseParseClass *klass;
+  gdouble rate;
+  GstFormat format;
+  GstSeekFlags flags;
+  GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
+  gboolean flush, update, res = TRUE;
+  gint64 cur, stop, seekpos;
+  GstSegment seeksegment = { 0, };
+  GstFormat dstformat;
+
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  gst_event_parse_seek (event, &rate, &format, &flags,
+      &cur_type, &cur, &stop_type, &stop);
+
+  /* no negative rates yet */
+  if (rate < 0.0)
+    goto negative_rate;
+
+  if (cur_type != GST_SEEK_TYPE_SET)
+    goto wrong_type;
+
+  /* For any format other than TIME, see if upstream handles
+   * it directly or fail. For TIME, try upstream, but do it ourselves if
+   * it fails upstream */
+  if (format != GST_FORMAT_TIME) {
+    gst_event_ref (event);
+    return gst_pad_push_event (parse->sinkpad, event);
+  } else {
+    gst_event_ref (event);
+    if (gst_pad_push_event (parse->sinkpad, event))
+      return TRUE;
+  }
+
+  /* get flush flag */
+  flush = flags & GST_SEEK_FLAG_FLUSH;
+
+  dstformat = GST_FORMAT_BYTES;
+  if (!gst_pad_query_convert (parse->srcpad, format, cur, &dstformat, &seekpos)) {
+    GST_DEBUG_OBJECT (parse, "conversion failed");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (parse, "seek position %lld in bytes: %lld", cur, seekpos);
+
+  if (parse->priv->pad_mode == GST_ACTIVATE_PULL) {
+    gint64 last_stop;
+
+    GST_DEBUG_OBJECT (parse, "seek in PULL mode");
+
+    if (flush) {
+      if (parse->srcpad) {
+        GST_DEBUG_OBJECT (parse, "sending flush start");
+        gst_pad_push_event (parse->srcpad, gst_event_new_flush_start ());
+      }
+    } else {
+      gst_pad_pause_task (parse->sinkpad);
+    }
+
+    /* we should now be able to grab the streaming thread because we stopped it
+     * with the above flush/pause code */
+    GST_PAD_STREAM_LOCK (parse->sinkpad);
+
+    /* save current position */
+    last_stop = parse->segment.last_stop;
+    GST_DEBUG_OBJECT (parse, "stopped streaming at %" G_GINT64_FORMAT,
+        last_stop);
+
+    /* copy segment, we need this because we still need the old
+     * segment when we close the current segment. */
+    memcpy (&seeksegment, &parse->segment, sizeof (GstSegment));
+
+    GST_DEBUG_OBJECT (parse, "configuring seek");
+    gst_segment_set_seek (&seeksegment, rate, format, flags,
+        cur_type, cur, stop_type, stop, &update);
+
+    /* figure out the last position we need to play. If it's configured (stop !=
+     * -1), use that, else we play until the total duration of the file */
+    if ((stop = seeksegment.stop) == -1)
+      stop = seeksegment.duration;
+
+    parse->priv->offset = seekpos;
+
+    /* prepare for streaming again */
+    if (flush) {
+      GST_DEBUG_OBJECT (parse, "sending flush stop");
+      gst_pad_push_event (parse->srcpad, gst_event_new_flush_stop ());
+    } else {
+      if (parse->close_segment)
+        gst_event_unref (parse->close_segment);
+
+      parse->close_segment = gst_event_new_new_segment (TRUE,
+          parse->segment.rate, parse->segment.format,
+          parse->segment.accum, parse->segment.last_stop, parse->segment.accum);
+
+      /* keep track of our last_stop */
+      seeksegment.accum = parse->segment.last_stop;
+
+      GST_DEBUG_OBJECT (parse, "Created close seg format %d, "
+          "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+          ", pos = %" GST_TIME_FORMAT, format,
+          GST_TIME_ARGS (parse->segment.accum),
+          GST_TIME_ARGS (parse->segment.last_stop),
+          GST_TIME_ARGS (parse->segment.accum));
+    }
+
+    memcpy (&parse->segment, &seeksegment, sizeof (GstSegment));
+
+    /* store the newsegment event so it can be sent from the streaming thread. */
+    if (parse->pending_segment)
+      gst_event_unref (parse->pending_segment);
+
+    /* This will be sent later in _loop() */
+    parse->pending_segment =
+        gst_event_new_new_segment (FALSE, parse->segment.rate,
+        parse->segment.format,
+        parse->segment.last_stop, stop, parse->segment.last_stop);
+
+    GST_DEBUG_OBJECT (parse, "Created newseg format %d, "
+        "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+        ", pos = %" GST_TIME_FORMAT, format,
+        GST_TIME_ARGS (parse->segment.last_stop),
+        GST_TIME_ARGS (stop), GST_TIME_ARGS (parse->segment.last_stop));
+
+    /* mark discont if we are going to stream from another position. */
+    if (last_stop != parse->segment.last_stop) {
+      GST_DEBUG_OBJECT (parse,
+          "mark DISCONT, we did a seek to another position");
+      parse->priv->discont = TRUE;
+    }
+
+    /* Start streaming thread if paused */
+    gst_pad_start_task (parse->sinkpad,
+        (GstTaskFunction) gst_base_parse_loop, parse->sinkpad);
+
+    GST_PAD_STREAM_UNLOCK (parse->sinkpad);
+  } else {
+    GstEvent *new_event;
+    /* The only thing we need to do in PUSH-mode is to send the
+       seek event (in bytes) to upstream. Segment / flush handling happens
+       in corresponding src event handlers */
+    GST_DEBUG_OBJECT (parse, "seek in PUSH mode");
+    new_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flush,
+        GST_SEEK_TYPE_SET, seekpos, stop_type, stop);
+
+    res = gst_pad_push_event (parse->sinkpad, new_event);
+  }
+
+done:
+  return res;
+
+  /* ERRORS */
+negative_rate:
+  {
+    GST_DEBUG_OBJECT (parse, "negative playback rates are not supported yet.");
+    res = FALSE;
+    goto done;
+  }
+wrong_type:
+  {
+    GST_DEBUG_OBJECT (parse, "unsupported seek type.");
+    res = FALSE;
+    goto done;
+  }
+}
+
+
+/**
+ * gst_base_parse_sink_setcaps:
+ * @pad: #GstPad.
+ * @caps: #GstCaps.
+ *
+ * Returns: TRUE if caps were accepted.
+ */
+static gboolean
+gst_base_parse_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *klass;
+  gboolean res = TRUE;
+
+  gchar *caps_str = gst_caps_to_string (caps);
+  g_free (caps_str);
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  GST_DEBUG_OBJECT (parse, "setcaps: %s", caps_str);
+
+  if (klass->set_sink_caps)
+    res = klass->set_sink_caps (parse, caps);
+
+  parse->negotiated = res;
+  gst_object_unref (parse);
+  return gst_pad_set_caps (pad, caps);
+}
diff --git a/gst/aacparse/gstbaseparse.h b/gst/aacparse/gstbaseparse.h
new file mode 100644 (file)
index 0000000..a2856cc
--- /dev/null
@@ -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__ */
diff --git a/gst/amrparse/Makefile.am b/gst/amrparse/Makefile.am
new file mode 100644 (file)
index 0000000..43798d5
--- /dev/null
@@ -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
diff --git a/gst/amrparse/gstamrparse.c b/gst/amrparse/gstamrparse.c
new file mode 100644 (file)
index 0000000..aeb928e
--- /dev/null
@@ -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);
diff --git a/gst/amrparse/gstamrparse.h b/gst/amrparse/gstamrparse.h
new file mode 100644 (file)
index 0000000..e776f29
--- /dev/null
@@ -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__ */
diff --git a/gst/amrparse/gstbaseparse.c b/gst/amrparse/gstbaseparse.c
new file mode 100644 (file)
index 0000000..18b396c
--- /dev/null
@@ -0,0 +1,1741 @@
+/* GStreamer
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *   Contact: Stefan Kost <stefan.kost@nokia.com>
+ * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>.
+ *
+ * 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:gstbaseparse
+ * @short_description: Base class for stream parsers
+ * @see_also: #GstBaseTransform
+ *
+ * This base class is for parser elements that process data and splits it 
+ * into separate audio/video/whatever frames.
+ *
+ * It provides for:
+ * <itemizedlist>
+ *   <listitem><para>One sinkpad and one srcpad</para></listitem>
+ *   <listitem><para>Handles state changes</para></listitem>
+ *   <listitem><para>Does flushing</para></listitem>
+ *   <listitem><para>Push mode</para></listitem>
+ *   <listitem><para>Pull mode</para></listitem>
+ *   <listitem><para>Handles events (NEWSEGMENT/EOS/FLUSH)</para></listitem>
+ *   <listitem><para>Handles seeking in both modes</para></listitem>
+ *   <listitem><para>
+ *        Handles POSITION/DURATION/SEEKING/FORMAT/CONVERT queries
+ *   </para></listitem>
+ * </itemizedlist>
+ *
+ * The purpose of this base class is to provide a basic functionality of
+ * a parser and share a lot of rather complex code.
+ *
+ * Description of the parsing mechanism:
+ * <orderedlist>
+ * <listitem>
+ *   <itemizedlist><title>Set-up phase</title>
+ *   <listitem><para>
+ *     GstBaseParse class calls @set_sink_caps to inform the subclass about
+ *     incoming sinkpad caps. Subclass should set the srcpad caps accordingly.
+ *   </para></listitem>
+ *   <listitem><para>
+ *     GstBaseParse calls @start to inform subclass that data processing is
+ *     about to start now.
+ *   </para></listitem>
+ *   <listitem><para>
+ *      At least in this point subclass needs to tell the GstBaseParse class
+ *      how big data chunks it wants to receive (min_frame_size). It can do 
+ *      this with @gst_base_parse_set_min_frame_size.
+ *   </para></listitem>
+ *   <listitem><para>
+ *      GstBaseParse class sets up appropriate data passing mode (pull/push)
+ *      and starts to process the data.
+ *   </para></listitem>
+ *   </itemizedlist>
+ * </listitem>
+ * <listitem>
+ *   <itemizedlist>
+ *   <title>Parsing phase</title>
+ *     <listitem><para>
+ *       GstBaseParse gathers at least min_frame_size bytes of data either 
+ *       by pulling it from upstream or collecting buffers into internal
+ *       #GstAdapter.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       A buffer of min_frame_size bytes is passed to subclass with
+ *       @check_valid_frame. Subclass checks the contents and returns TRUE
+ *       if the buffer contains a valid frame. It also needs to set the
+ *       @framesize according to the detected frame size. If buffer didn't
+ *       contain a valid frame, this call must return FALSE and optionally
+ *       set the @skipsize value to inform base class that how many bytes
+ *       it needs to skip in order to find a valid frame. The passed buffer
+ *       is read-only.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       After valid frame is found, it will be passed again to subclass with
+ *       @parse_frame call. Now subclass is responsible for parsing the
+ *       frame contents and setting the buffer timestamp, duration and caps.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       Finally the buffer can be pushed downstream and parsing loop starts
+ *       over again.
+ *     </para></listitem>
+ *     <listitem><para>
+ *       During the parsing process GstBaseClass will handle both srcpad and
+ *       sinkpad events. They will be passed to subclass if @event or
+ *       @src_event callbacks have been provided.
+ *     </para></listitem>
+ *   </itemizedlist>
+ * </listitem>
+ * <listitem>
+ *   <itemizedlist><title>Shutdown phase</title>
+ *   <listitem><para>
+ *     GstBaseParse class calls @stop to inform the subclass that data
+ *     parsing will be stopped.
+ *   </para></listitem>
+ *   </itemizedlist>
+ * </listitem>
+ * </orderedlist>
+ *
+ * Subclass is responsible for providing pad template caps for
+ * source and sink pads. The pads need to be named "sink" and "src". It also 
+ * needs to set the fixed caps on srcpad, when the format is ensured (e.g. 
+ * when base class calls subclass' @set_sink_caps function).
+ *
+ * This base class uses GST_FORMAT_DEFAULT as a meaning of frames. So,
+ * subclass conversion routine needs to know that conversion from
+ * GST_FORMAT_TIME to GST_FORMAT_DEFAULT must return the
+ * frame number that can be found from the given byte position.
+ *
+ * GstBaseParse uses subclasses conversion methods also for seeking. If
+ * subclass doesn't provide @convert function, seeking will get disabled.
+ *
+ * Subclass @start and @stop functions will be called to inform the beginning
+ * and end of data processing.
+ *
+ * Things that subclass need to take care of:
+ * <itemizedlist>
+ *   <listitem><para>Provide pad templates</para></listitem>
+ *   <listitem><para>
+ *      Fixate the source pad caps when appropriate
+ *   </para></listitem>
+ *   <listitem><para>
+ *      Inform base class how big data chunks should be retrieved. This is
+ *      done with @gst_base_parse_set_min_frame_size function.
+ *   </para></listitem>
+ *   <listitem><para>
+ *      Examine data chunks passed to subclass with @check_valid_frame
+ *      and tell if they contain a valid frame
+ *   </para></listitem>
+ *   <listitem><para>
+ *      Set the caps and timestamp to frame that is passed to subclass with
+ *      @parse_frame function.
+ *   </para></listitem>
+ *   <listitem><para>Provide conversion functions</para></listitem>
+ *   <listitem><para>
+ *      Update the duration information with @gst_base_parse_set_duration
+ *   </para></listitem>
+ * </itemizedlist>
+ *
+ */
+
+/* TODO:
+ *  - Better segment handling:
+ *    - NEWSEGMENT for gaps
+ *    - Not NEWSEGMENT starting at 0 but at first frame timestamp
+ *  - GstIndex support
+ *  - Seek table generation and subclass seek entry injection
+ *  - Accurate seeking
+ *  - In push mode provide a queue of adapter-"queued" buffers for upstream
+ *    buffer metadata
+ *  - Timestamp tracking and setting
+ *  - Handle upstream timestamps
+ *  - Queue buffers/events until caps are set
+ *  - Bitrate tracking => inaccurate seeking, inaccurate duration calculation
+ *  - Let subclass decide if frames outside the segment should be dropped
+ *  - Send queries upstream
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gstbaseparse.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_base_parse_debug);
+#define GST_CAT_DEFAULT gst_base_parse_debug
+
+/* Supported formats */
+static GstFormat fmtlist[] = {
+  GST_FORMAT_DEFAULT,
+  GST_FORMAT_BYTES,
+  GST_FORMAT_TIME,
+  0
+};
+
+#define GST_BASE_PARSE_GET_PRIVATE(obj)  \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_PARSE, GstBaseParsePrivate))
+
+struct _GstBaseParsePrivate
+{
+  GstActivateMode pad_mode;
+
+  gint64 duration;
+  GstFormat duration_fmt;
+
+  guint min_frame_size;
+
+  gboolean discont;
+  gboolean flushing;
+
+  gint64 offset;
+  gint64 pending_offset;
+
+  GList *pending_events;
+
+  GstBuffer *cache;
+};
+
+struct _GstBaseParseClassPrivate
+{
+  gpointer _padding;
+};
+
+static GstElementClass *parent_class = NULL;
+
+static void gst_base_parse_base_init (gpointer g_class);
+static void gst_base_parse_base_finalize (gpointer g_class);
+static void gst_base_parse_class_init (GstBaseParseClass * klass);
+static void gst_base_parse_init (GstBaseParse * parse,
+    GstBaseParseClass * klass);
+
+GType
+gst_base_parse_get_type (void)
+{
+  static GType base_parse_type = 0;
+
+  if (!base_parse_type) {
+    static const GTypeInfo base_parse_info = {
+      sizeof (GstBaseParseClass),
+      (GBaseInitFunc) gst_base_parse_base_init,
+      (GBaseFinalizeFunc) gst_base_parse_base_finalize,
+      (GClassInitFunc) gst_base_parse_class_init,
+      NULL,
+      NULL,
+      sizeof (GstBaseParse),
+      0,
+      (GInstanceInitFunc) gst_base_parse_init,
+    };
+
+    base_parse_type = g_type_register_static (GST_TYPE_ELEMENT,
+        "GstFlacBaseParse", &base_parse_info, G_TYPE_FLAG_ABSTRACT);
+  }
+  return base_parse_type;
+}
+
+static void gst_base_parse_finalize (GObject * object);
+
+static gboolean gst_base_parse_sink_activate (GstPad * sinkpad);
+static gboolean gst_base_parse_sink_activate_push (GstPad * pad,
+    gboolean active);
+static gboolean gst_base_parse_sink_activate_pull (GstPad * pad,
+    gboolean active);
+static gboolean gst_base_parse_handle_seek (GstBaseParse * parse,
+    GstEvent * event);
+
+static gboolean gst_base_parse_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_base_parse_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_base_parse_query (GstPad * pad, GstQuery * query);
+static gboolean gst_base_parse_sink_setcaps (GstPad * pad, GstCaps * caps);
+static const GstQueryType *gst_base_parse_get_querytypes (GstPad * pad);
+
+static GstFlowReturn gst_base_parse_chain (GstPad * pad, GstBuffer * buffer);
+static void gst_base_parse_loop (GstPad * pad);
+
+static gboolean gst_base_parse_check_frame (GstBaseParse * parse,
+    GstBuffer * buffer, guint * framesize, gint * skipsize);
+
+static gboolean gst_base_parse_parse_frame (GstBaseParse * parse,
+    GstBuffer * buffer);
+
+static gboolean gst_base_parse_sink_eventfunc (GstBaseParse * parse,
+    GstEvent * event);
+
+static gboolean gst_base_parse_src_eventfunc (GstBaseParse * parse,
+    GstEvent * event);
+
+static gboolean gst_base_parse_is_seekable (GstBaseParse * parse);
+
+static void gst_base_parse_drain (GstBaseParse * parse);
+
+static void
+gst_base_parse_base_init (gpointer g_class)
+{
+  GstBaseParseClass *klass = GST_BASE_PARSE_CLASS (g_class);
+  GstBaseParseClassPrivate *priv;
+
+  GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "flacbaseparse", 0,
+      "baseparse element");
+
+  /* TODO: Remove this once GObject supports class private data */
+  priv = g_slice_new0 (GstBaseParseClassPrivate);
+  if (klass->priv)
+    memcpy (priv, klass->priv, sizeof (GstBaseParseClassPrivate));
+  klass->priv = priv;
+}
+
+static void
+gst_base_parse_base_finalize (gpointer g_class)
+{
+  GstBaseParseClass *klass = GST_BASE_PARSE_CLASS (g_class);
+
+  g_slice_free (GstBaseParseClassPrivate, klass->priv);
+  klass->priv = NULL;
+}
+
+static void
+gst_base_parse_finalize (GObject * object)
+{
+  GstBaseParse *parse = GST_BASE_PARSE (object);
+
+  g_mutex_free (parse->parse_lock);
+  g_object_unref (parse->adapter);
+
+  if (parse->pending_segment) {
+    gst_event_replace (&parse->pending_segment, NULL);
+  }
+  if (parse->close_segment) {
+    gst_event_replace (&parse->close_segment, NULL);
+  }
+
+  if (parse->priv->cache) {
+    gst_buffer_unref (parse->priv->cache);
+    parse->priv->cache = NULL;
+  }
+
+  g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref,
+      NULL);
+  g_list_free (parse->priv->pending_events);
+  parse->priv->pending_events = NULL;
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_base_parse_class_init (GstBaseParseClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  g_type_class_add_private (klass, sizeof (GstBaseParsePrivate));
+  parent_class = g_type_class_peek_parent (klass);
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_parse_finalize);
+
+  /* Default handlers */
+  klass->check_valid_frame = gst_base_parse_check_frame;
+  klass->parse_frame = gst_base_parse_parse_frame;
+  klass->event = gst_base_parse_sink_eventfunc;
+  klass->src_event = gst_base_parse_src_eventfunc;
+  klass->is_seekable = gst_base_parse_is_seekable;
+}
+
+static void
+gst_base_parse_init (GstBaseParse * parse, GstBaseParseClass * bclass)
+{
+  GstPadTemplate *pad_template;
+
+  GST_DEBUG_OBJECT (parse, "gst_base_parse_init");
+
+  parse->priv = GST_BASE_PARSE_GET_PRIVATE (parse);
+
+  pad_template =
+      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink");
+  g_return_if_fail (pad_template != NULL);
+  parse->sinkpad = gst_pad_new_from_template (pad_template, "sink");
+  gst_pad_set_event_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_event));
+  gst_pad_set_setcaps_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_setcaps));
+  gst_pad_set_chain_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_chain));
+  gst_pad_set_activate_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate));
+  gst_pad_set_activatepush_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate_push));
+  gst_pad_set_activatepull_function (parse->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate_pull));
+  gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
+
+  GST_DEBUG_OBJECT (parse, "sinkpad created");
+
+  pad_template =
+      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src");
+  g_return_if_fail (pad_template != NULL);
+  parse->srcpad = gst_pad_new_from_template (pad_template, "src");
+  gst_pad_set_event_function (parse->srcpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_src_event));
+  gst_pad_set_query_type_function (parse->srcpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_get_querytypes));
+  gst_pad_set_query_function (parse->srcpad,
+      GST_DEBUG_FUNCPTR (gst_base_parse_query));
+  gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
+  GST_DEBUG_OBJECT (parse, "src created");
+
+  parse->parse_lock = g_mutex_new ();
+  parse->adapter = gst_adapter_new ();
+  parse->pending_segment = NULL;
+  parse->close_segment = NULL;
+
+  parse->priv->pad_mode = GST_ACTIVATE_NONE;
+  parse->priv->duration = -1;
+  parse->priv->min_frame_size = 1;
+  parse->priv->discont = FALSE;
+  parse->priv->flushing = FALSE;
+  parse->priv->offset = 0;
+  GST_DEBUG_OBJECT (parse, "init ok");
+}
+
+
+
+/**
+ * gst_base_parse_check_frame:
+ * @parse: #GstBaseParse.
+ * @buffer: GstBuffer.
+ * @framesize: This will be set to tell the found frame size in bytes.
+ * @skipsize: Output parameter that tells how much data needs to be skipped
+ *            in order to find the following frame header.
+ *
+ * Default callback for check_valid_frame.
+ * 
+ * Returns: Always TRUE.
+ */
+static gboolean
+gst_base_parse_check_frame (GstBaseParse * parse,
+    GstBuffer * buffer, guint * framesize, gint * skipsize)
+{
+  *framesize = GST_BUFFER_SIZE (buffer);
+  *skipsize = 0;
+  return TRUE;
+}
+
+
+/**
+ * gst_base_parse_parse_frame:
+ * @parse: #GstBaseParse.
+ * @buffer: #GstBuffer.
+ *
+ * Default callback for parse_frame.
+ */
+static gboolean
+gst_base_parse_parse_frame (GstBaseParse * parse, GstBuffer * buffer)
+{
+  /* FIXME: Could we even _try_ to do something clever here? */
+  GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+  GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+  return TRUE;
+}
+
+
+/**
+ * gst_base_parse_bytepos_to_time:
+ * @parse: #GstBaseParse.
+ * @bytepos: Position (in bytes) to be converted.
+ * @pos_in_time: #GstClockTime pointer where the result is set.
+ *
+ * Convert given byte position into #GstClockTime format.
+ * 
+ * Returns: TRUE if conversion succeeded.
+ */
+static gboolean
+gst_base_parse_bytepos_to_time (GstBaseParse * parse, gint64 bytepos,
+    GstClockTime * pos_in_time)
+{
+  GstBaseParseClass *klass;
+  gboolean res = FALSE;
+
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (klass->convert) {
+    res = klass->convert (parse, GST_FORMAT_BYTES, bytepos,
+        GST_FORMAT_TIME, (gint64 *) pos_in_time);
+  }
+  return res;
+}
+
+
+/**
+ * gst_base_parse_sink_event:
+ * @pad: #GstPad that received the event.
+ * @event: #GstEvent to be handled.
+ *
+ * Handler for sink pad events.
+ *
+ * Returns: TRUE if the event was handled.
+ */
+static gboolean
+gst_base_parse_sink_event (GstPad * pad, GstEvent * event)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *bclass;
+  gboolean handled = FALSE;
+  gboolean ret = TRUE;
+
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  GST_DEBUG_OBJECT (parse, "handling event %d", GST_EVENT_TYPE (event));
+
+  /* Cache all events except EOS, NEWSEGMENT and FLUSH_STOP if we have a
+   * pending segment */
+  if (parse->pending_segment && GST_EVENT_TYPE (event) != GST_EVENT_EOS
+      && GST_EVENT_TYPE (event) != GST_EVENT_NEWSEGMENT
+      && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_START
+      && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP) {
+    parse->priv->pending_events =
+        g_list_append (parse->priv->pending_events, event);
+    ret = TRUE;
+  } else {
+
+    if (bclass->event)
+      handled = bclass->event (parse, event);
+
+    if (!handled)
+      ret = gst_pad_event_default (pad, event);
+  }
+
+  gst_object_unref (parse);
+  GST_DEBUG_OBJECT (parse, "event handled");
+  return ret;
+}
+
+
+/**
+ * gst_base_parse_sink_eventfunc:
+ * @parse: #GstBaseParse.
+ * @event: #GstEvent to be handled.
+ *
+ * Element-level event handler function.
+ *
+ * Returns: TRUE if the event was handled and not need forwarding.
+ */
+static gboolean
+gst_base_parse_sink_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+  gboolean handled = FALSE;
+  GstEvent **eventp;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+    {
+      gdouble rate, applied_rate;
+      GstFormat format;
+      gint64 start, stop, pos;
+      gboolean update;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
+          &format, &start, &stop, &pos);
+
+
+      if (format == GST_FORMAT_BYTES) {
+        GstClockTime seg_start, seg_stop, seg_pos;
+
+        /* stop time is allowed to be open-ended, but not start & pos */
+        seg_stop = GST_CLOCK_TIME_NONE;
+        parse->priv->pending_offset = pos;
+
+        if (gst_base_parse_bytepos_to_time (parse, start, &seg_start) &&
+            gst_base_parse_bytepos_to_time (parse, pos, &seg_pos)) {
+          gst_event_unref (event);
+          event = gst_event_new_new_segment_full (update, rate, applied_rate,
+              GST_FORMAT_TIME, seg_start, seg_stop, seg_pos);
+          format = GST_FORMAT_TIME;
+          GST_DEBUG_OBJECT (parse, "Converted incoming segment to TIME. "
+              "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+              ", pos = %" GST_TIME_FORMAT, GST_TIME_ARGS (seg_start),
+              GST_TIME_ARGS (seg_stop), GST_TIME_ARGS (seg_pos));
+        }
+      }
+
+      if (format != GST_FORMAT_TIME) {
+        /* Unknown incoming segment format. Output a default open-ended 
+         * TIME segment */
+        gst_event_unref (event);
+        event = gst_event_new_new_segment_full (update, rate, applied_rate,
+            GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0);
+      }
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
+          &format, &start, &stop, &pos);
+
+      gst_segment_set_newsegment_full (&parse->segment, update, rate,
+          applied_rate, format, start, stop, pos);
+
+      GST_DEBUG_OBJECT (parse, "Created newseg rate %g, applied rate %g, "
+          "format %d, start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+          ", pos = %" GST_TIME_FORMAT, rate, applied_rate, format,
+          GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (pos));
+
+      /* save the segment for later, right before we push a new buffer so that
+       * the caps are fixed and the next linked element can receive
+       * the segment. */
+      eventp = &parse->pending_segment;
+      gst_event_replace (eventp, event);
+      gst_event_unref (event);
+      handled = TRUE;
+      break;
+    }
+
+    case GST_EVENT_FLUSH_START:
+      parse->priv->flushing = TRUE;
+      /* Wait for _chain() to exit by taking the srcpad STREAM_LOCK */
+      GST_PAD_STREAM_LOCK (parse->srcpad);
+      handled = gst_pad_push_event (parse->srcpad, event);
+      GST_PAD_STREAM_UNLOCK (parse->srcpad);
+      break;
+
+    case GST_EVENT_FLUSH_STOP:
+      gst_adapter_clear (parse->adapter);
+      parse->priv->flushing = FALSE;
+      parse->priv->discont = TRUE;
+      break;
+
+    case GST_EVENT_EOS:
+      gst_base_parse_drain (parse);
+      break;
+
+    default:
+      break;
+  }
+
+  return handled;
+}
+
+
+/**
+ * gst_base_parse_src_event:
+ * @pad: #GstPad that received the event.
+ * @event: #GstEvent that was received.
+ *
+ * Handler for source pad events.
+ *
+ * Returns: TRUE if the event was handled.
+ */
+static gboolean
+gst_base_parse_src_event (GstPad * pad, GstEvent * event)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *bclass;
+  gboolean handled = FALSE;
+  gboolean ret = TRUE;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  GST_DEBUG_OBJECT (parse, "event %d, %s", GST_EVENT_TYPE (event),
+      GST_EVENT_TYPE_NAME (event));
+
+  if (bclass->src_event)
+    handled = bclass->src_event (parse, event);
+
+  if (!handled)
+    ret = gst_pad_event_default (pad, event);
+
+  gst_object_unref (parse);
+  return ret;
+}
+
+
+/**
+ * gst_base_parse_src_eventfunc:
+ * @parse: #GstBaseParse.
+ * @event: #GstEvent that was received.
+ *
+ * Default srcpad event handler.
+ *
+ * Returns: TRUE if the event was handled and can be dropped.
+ */
+static gboolean
+gst_base_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+  gboolean handled = FALSE;
+  GstBaseParseClass *bclass;
+
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+    {
+      if (bclass->is_seekable (parse)) {
+        handled = gst_base_parse_handle_seek (parse, event);
+        gst_event_unref (event);
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return handled;
+}
+
+
+/**
+ * gst_base_parse_is_seekable:
+ * @parse: #GstBaseParse.
+ *
+ * Default handler for is_seekable.
+ *
+ * Returns: Always TRUE.
+ */
+static gboolean
+gst_base_parse_is_seekable (GstBaseParse * parse)
+{
+  return TRUE;
+}
+
+
+/**
+ * gst_base_parse_handle_and_push_buffer:
+ * @parse: #GstBaseParse.
+ * @klass: #GstBaseParseClass.
+ * @buffer: #GstBuffer.
+ *
+ * Parses the frame from given buffer and pushes it forward. Also performs
+ * timestamp handling and checks the segment limits.
+ *
+ * This is called with srcpad STREAM_LOCK held.
+ *
+ * Returns: #GstFlowReturn
+ */
+static GstFlowReturn
+gst_base_parse_handle_and_push_buffer (GstBaseParse * parse,
+    GstBaseParseClass * klass, GstBuffer * buffer)
+{
+  GstFlowReturn ret;
+  GstClockTime last_stop = GST_CLOCK_TIME_NONE;
+
+  if (parse->priv->discont) {
+    GST_DEBUG_OBJECT (parse, "marking DISCONT");
+    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+    parse->priv->discont = FALSE;
+  }
+
+  ret = klass->parse_frame (parse, buffer);
+
+  /* FIXME: Check the output buffer for any missing metadata,
+   *        keep track of timestamp and calculate everything possible
+   *        if not set already */
+
+  if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
+    last_stop = GST_BUFFER_TIMESTAMP (buffer);
+  if (last_stop != GST_CLOCK_TIME_NONE && GST_BUFFER_DURATION_IS_VALID (buffer))
+    last_stop += GST_BUFFER_DURATION (buffer);
+
+  gst_buffer_set_caps (buffer, GST_PAD_CAPS (parse->srcpad));
+
+  /* TODO: Add to seek table */
+
+  if (ret == GST_FLOW_OK) {
+    if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
+        GST_CLOCK_TIME_IS_VALID (parse->segment.stop) &&
+        GST_BUFFER_TIMESTAMP (buffer) > parse->segment.stop) {
+      GST_LOG_OBJECT (parse, "Dropped frame, after segment");
+      gst_buffer_unref (buffer);
+    } else if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) &&
+        GST_BUFFER_DURATION_IS_VALID (buffer) &&
+        GST_CLOCK_TIME_IS_VALID (parse->segment.start) &&
+        GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION_IS_VALID (buffer)
+        < parse->segment.start) {
+      /* FIXME: subclass needs way to override the start as downstream might
+       * need frames before for proper decoding */
+      GST_LOG_OBJECT (parse, "Dropped frame, before segment");
+      gst_buffer_unref (buffer);
+    } else {
+      ret = gst_pad_push (parse->srcpad, buffer);
+      GST_LOG_OBJECT (parse, "frame (%d bytes) pushed: %d",
+          GST_BUFFER_SIZE (buffer), ret);
+    }
+  } else {
+    gst_buffer_unref (buffer);
+  }
+
+  /* Update current running segment position */
+  if (ret == GST_FLOW_OK && last_stop != GST_CLOCK_TIME_NONE)
+    gst_segment_set_last_stop (&parse->segment, GST_FORMAT_TIME, last_stop);
+
+  /* convert internal flow to OK and mark discont for the next buffer. */
+  if (ret == GST_BASE_PARSE_FLOW_DROPPED) {
+    parse->priv->discont = TRUE;
+    ret = GST_FLOW_OK;
+  }
+  return ret;
+}
+
+
+/**
+ * gst_base_parse_drain:
+ * @parse: #GstBaseParse.
+ *
+ * Drains the adapter until it is empty. It decreases the min_frame_size to
+ * match the current adapter size and calls chain method until the adapter
+ * is emptied or chain returns with error.
+ */
+static void
+gst_base_parse_drain (GstBaseParse * parse)
+{
+  guint avail;
+
+  for (;;) {
+    avail = gst_adapter_available (parse->adapter);
+    if (!avail)
+      break;
+
+    gst_base_parse_set_min_frame_size (parse, avail);
+    if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) {
+      break;
+    }
+  }
+}
+
+
+/**
+ * gst_base_parse_chain:
+ * @pad: #GstPad.
+ * @buffer: #GstBuffer.
+ *
+ * Returns: #GstFlowReturn.
+ */
+static GstFlowReturn
+gst_base_parse_chain (GstPad * pad, GstBuffer * buffer)
+{
+  GstBaseParseClass *bclass;
+  GstBaseParse *parse;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstBuffer *outbuf = NULL;
+  GstBuffer *tmpbuf = NULL;
+  guint fsize = 0;
+  gint skip = -1;
+  const guint8 *data;
+  guint min_size;
+
+  parse = GST_BASE_PARSE (GST_OBJECT_PARENT (pad));
+  bclass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (parse->pending_segment) {
+    GST_DEBUG_OBJECT (parse, "chain pushing a pending segment");
+    gst_pad_push_event (parse->srcpad, parse->pending_segment);
+    parse->pending_segment = NULL;
+    parse->priv->offset = parse->priv->pending_offset;
+
+    /* Make sure that adapter doesn't have any old data after
+       newsegment has been pushed */
+
+    /* FIXME: when non-flushing seek occurs, chain is still processing the
+       data from old segment. If this processing loop is then interrupted
+       (e.g. paused), chain function exists and next time it gets called
+       all this old data gets lost and playback continues from new segment */
+    gst_adapter_clear (parse->adapter);
+  }
+
+  if (parse->priv->pending_events) {
+    GList *l;
+
+    for (l = parse->priv->pending_events; l != NULL; l = l->next) {
+      gst_pad_push_event (parse->srcpad, GST_EVENT (l->data));
+    }
+    g_list_free (parse->priv->pending_events);
+    parse->priv->pending_events = NULL;
+  }
+
+  if (buffer) {
+    GST_LOG_OBJECT (parse, "buffer size: %d, offset = %lld",
+        GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer));
+
+
+    gst_adapter_push (parse->adapter, buffer);
+  }
+
+  /* Parse and push as many frames as possible */
+  /* Stop either when adapter is empty or we are flushing */
+  while (!parse->priv->flushing) {
+    tmpbuf = gst_buffer_new ();
+
+    GST_BASE_PARSE_LOCK (parse);
+    min_size = parse->priv->min_frame_size;
+    GST_BASE_PARSE_UNLOCK (parse);
+
+    /* Synchronization loop */
+    for (;;) {
+      /* Collect at least min_frame_size bytes */
+      if (gst_adapter_available (parse->adapter) < min_size) {
+        GST_DEBUG_OBJECT (parse, "not enough data available (only %d bytes)",
+            gst_adapter_available (parse->adapter));
+        gst_buffer_unref (tmpbuf);
+        goto done;
+      }
+
+      data = gst_adapter_peek (parse->adapter, min_size);
+      GST_BUFFER_DATA (tmpbuf) = (guint8 *) data;
+      GST_BUFFER_SIZE (tmpbuf) = min_size;
+      GST_BUFFER_OFFSET (tmpbuf) = parse->priv->offset;
+      GST_BUFFER_FLAG_SET (tmpbuf, GST_MINI_OBJECT_FLAG_READONLY);
+
+      if (parse->priv->discont) {
+        GST_DEBUG_OBJECT (parse, "marking DISCONT");
+        GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+      }
+
+      skip = -1;
+      if (bclass->check_valid_frame (parse, tmpbuf, &fsize, &skip)) {
+        break;
+      }
+      if (skip > 0) {
+        GST_LOG_OBJECT (parse, "finding sync, skipping %d bytes", skip);
+        gst_adapter_flush (parse->adapter, skip);
+        parse->priv->offset += skip;
+      } else if (skip == -1) {
+        /* subclass didn't touch this value. By default we skip 1 byte */
+        GST_LOG_OBJECT (parse, "finding sync, skipping 1 byte");
+        gst_adapter_flush (parse->adapter, 1);
+        parse->priv->offset++;
+      }
+      /* There is a possibility that subclass set the skip value to zero.
+         This means that it has probably found a frame but wants to ask
+         more data (by increasing the min_size) to be sure of this. */
+    }
+    gst_buffer_unref (tmpbuf);
+    tmpbuf = NULL;
+
+    if (skip > 0) {
+      /* Subclass found the sync, but still wants to skip some data */
+      GST_LOG_OBJECT (parse, "skipping %d bytes", skip);
+      gst_adapter_flush (parse->adapter, skip);
+      parse->priv->offset += skip;
+    }
+
+    /* Grab lock to prevent a race with FLUSH_START handler */
+    GST_PAD_STREAM_LOCK (parse->srcpad);
+
+    /* FLUSH_START event causes the "flushing" flag to be set. In this
+     * case we can leave the frame pushing loop */
+    if (parse->priv->flushing) {
+      GST_PAD_STREAM_UNLOCK (parse->srcpad);
+      break;
+    }
+
+    /* FIXME: Would it be more efficient to make a subbuffer instead? */
+    outbuf = gst_adapter_take_buffer (parse->adapter, fsize);
+
+    /* Subclass may want to know the data offset */
+    GST_BUFFER_OFFSET (outbuf) = parse->priv->offset;
+    parse->priv->offset += fsize;
+
+    ret = gst_base_parse_handle_and_push_buffer (parse, bclass, outbuf);
+    GST_PAD_STREAM_UNLOCK (parse->srcpad);
+
+    if (ret != GST_FLOW_OK) {
+      GST_LOG_OBJECT (parse, "push returned %d", ret);
+      break;
+    }
+  }
+
+done:
+  GST_LOG_OBJECT (parse, "chain leaving");
+  return ret;
+}
+
+static GstFlowReturn
+gst_base_parse_pull_range (GstBaseParse * parse, guint size,
+    GstBuffer ** buffer)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+
+  /* Caching here actually makes much less difference than one would expect.
+   * We do it mainly to avoid pulling buffers of 1 byte all the time */
+  if (parse->priv->cache) {
+    guint64 cache_offset = GST_BUFFER_OFFSET (parse->priv->cache);
+    guint cache_size = GST_BUFFER_SIZE (parse->priv->cache);
+
+    if (cache_offset <= parse->priv->offset &&
+        (parse->priv->offset + size) < (cache_offset + cache_size)) {
+      *buffer = gst_buffer_create_sub (parse->priv->cache,
+          parse->priv->offset - cache_offset, size);
+      GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
+      return GST_FLOW_OK;
+    }
+    /* not enough data in the cache, free cache and get a new one */
+    gst_buffer_unref (parse->priv->cache);
+    parse->priv->cache = NULL;
+  }
+
+  /* refill the cache */
+  ret =
+      gst_pad_pull_range (parse->sinkpad, parse->priv->offset, MAX (size,
+          64 * 1024), &parse->priv->cache);
+  if (ret != GST_FLOW_OK) {
+    parse->priv->cache = NULL;
+    return ret;
+  }
+
+  if (GST_BUFFER_SIZE (parse->priv->cache) >= size) {
+    *buffer = gst_buffer_create_sub (parse->priv->cache, 0, size);
+    GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
+    return GST_FLOW_OK;
+  }
+
+  /* Not possible to get enough data, try a last time with
+   * requesting exactly the size we need */
+  gst_buffer_unref (parse->priv->cache);
+  parse->priv->cache = NULL;
+
+  ret = gst_pad_pull_range (parse->sinkpad, parse->priv->offset, size,
+      &parse->priv->cache);
+
+  if (ret != GST_FLOW_OK) {
+    GST_DEBUG_OBJECT (parse, "pull_range returned %d", ret);
+    *buffer = NULL;
+    return ret;
+  }
+
+  if (GST_BUFFER_SIZE (parse->priv->cache) < size) {
+    GST_WARNING_OBJECT (parse, "Dropping short buffer at offset %"
+        G_GUINT64_FORMAT ": wanted %u bytes, got %u bytes", parse->priv->offset,
+        size, GST_BUFFER_SIZE (parse->priv->cache));
+
+    gst_buffer_unref (parse->priv->cache);
+    parse->priv->cache = NULL;
+
+    *buffer = NULL;
+    return GST_FLOW_UNEXPECTED;
+  }
+
+  *buffer = gst_buffer_create_sub (parse->priv->cache, 0, size);
+  GST_BUFFER_OFFSET (*buffer) = parse->priv->offset;
+
+  return GST_FLOW_OK;
+}
+
+/**
+ * gst_base_parse_loop:
+ * @pad: GstPad
+ *
+ * Loop that is used in pull mode to retrieve data from upstream.
+ */
+static void
+gst_base_parse_loop (GstPad * pad)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *klass;
+  GstBuffer *buffer, *outbuf;
+  gboolean ret = FALSE;
+  guint fsize = 0, min_size;
+  gint skip = 0;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (parse->close_segment) {
+    GST_DEBUG_OBJECT (parse, "loop sending close segment");
+    gst_pad_push_event (parse->srcpad, parse->close_segment);
+    parse->close_segment = NULL;
+  }
+
+  if (parse->pending_segment) {
+    GST_DEBUG_OBJECT (parse, "loop push pending segment");
+    gst_pad_push_event (parse->srcpad, parse->pending_segment);
+    parse->pending_segment = NULL;
+  }
+
+  /* TODO: Check if we reach segment stop limit */
+
+  while (TRUE) {
+
+    GST_BASE_PARSE_LOCK (parse);
+    min_size = parse->priv->min_frame_size;
+    GST_BASE_PARSE_UNLOCK (parse);
+
+    ret = gst_base_parse_pull_range (parse, min_size, &buffer);
+
+    if (ret == GST_FLOW_UNEXPECTED)
+      goto eos;
+    else if (ret != GST_FLOW_OK)
+      goto need_pause;
+
+    if (parse->priv->discont) {
+      GST_DEBUG_OBJECT (parse, "marking DISCONT");
+      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+    }
+
+    skip = -1;
+    if (klass->check_valid_frame (parse, buffer, &fsize, &skip)) {
+      break;
+    }
+    if (skip > 0) {
+      GST_LOG_OBJECT (parse, "finding sync, skipping %d bytes", skip);
+      parse->priv->offset += skip;
+    } else if (skip == -1) {
+      GST_LOG_OBJECT (parse, "finding sync, skipping 1 byte");
+      parse->priv->offset++;
+    }
+    GST_DEBUG_OBJECT (parse, "finding sync...");
+    gst_buffer_unref (buffer);
+  }
+
+  if (fsize <= GST_BUFFER_SIZE (buffer)) {
+    outbuf = gst_buffer_create_sub (buffer, 0, fsize);
+    GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer);
+    gst_buffer_unref (buffer);
+  } else {
+    gst_buffer_unref (buffer);
+    ret = gst_base_parse_pull_range (parse, fsize, &outbuf);
+
+    if (ret == GST_FLOW_UNEXPECTED)
+      goto eos;
+    else if (ret != GST_FLOW_OK)
+      goto need_pause;
+  }
+
+  parse->priv->offset += fsize;
+
+  /* Does the subclass want to skip too? */
+  if (skip > 0)
+    parse->priv->offset += skip;
+
+  /* This always unrefs the outbuf, even if error occurs */
+  ret = gst_base_parse_handle_and_push_buffer (parse, klass, outbuf);
+
+  if (ret != GST_FLOW_OK) {
+    GST_DEBUG_OBJECT (parse, "flow: %s", gst_flow_get_name (ret));
+    if (GST_FLOW_IS_FATAL (ret)) {
+      GST_ELEMENT_ERROR (parse, STREAM, FAILED, (NULL),
+          ("streaming task paused, reason: %s", gst_flow_get_name (ret)));
+      gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
+    }
+    goto need_pause;
+  }
+
+  gst_object_unref (parse);
+  return;
+
+need_pause:
+  {
+    GST_LOG_OBJECT (parse, "pausing task");
+    gst_pad_pause_task (pad);
+    gst_object_unref (parse);
+    return;
+  }
+eos:
+  {
+    GST_LOG_OBJECT (parse, "pausing task %d", ret);
+    gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
+    gst_pad_pause_task (pad);
+    gst_object_unref (parse);
+    return;
+  }
+}
+
+
+/**
+ * gst_base_parse_sink_activate:
+ * @sinkpad: #GstPad to be activated.
+ *
+ * Returns: TRUE if activation succeeded.
+ */
+static gboolean
+gst_base_parse_sink_activate (GstPad * sinkpad)
+{
+  GstBaseParse *parse;
+  gboolean result = TRUE;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (sinkpad));
+
+  GST_DEBUG_OBJECT (parse, "sink activate");
+
+  if (gst_pad_check_pull_range (sinkpad)) {
+    GST_DEBUG_OBJECT (parse, "trying to activate in pull mode");
+    result = gst_pad_activate_pull (sinkpad, TRUE);
+  } else {
+    GST_DEBUG_OBJECT (parse, "trying to activate in push mode");
+    result = gst_pad_activate_push (sinkpad, TRUE);
+  }
+
+  GST_DEBUG_OBJECT (parse, "sink activate return %d", result);
+  gst_object_unref (parse);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_activate:
+ * @parse: #GstBaseParse.
+ * @active: TRUE if element will be activated, FALSE if disactivated.
+ *
+ * Returns: TRUE if the operation succeeded.
+ */
+static gboolean
+gst_base_parse_activate (GstBaseParse * parse, gboolean active)
+{
+  GstBaseParseClass *klass;
+  gboolean result = FALSE;
+
+  GST_DEBUG_OBJECT (parse, "activate");
+
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  if (active) {
+    if (parse->priv->pad_mode == GST_ACTIVATE_NONE && klass->start)
+      result = klass->start (parse);
+
+    GST_OBJECT_LOCK (parse);
+    gst_segment_init (&parse->segment, GST_FORMAT_TIME);
+    parse->priv->duration = -1;
+    parse->priv->discont = FALSE;
+    parse->priv->flushing = FALSE;
+    parse->priv->offset = 0;
+
+    if (parse->pending_segment)
+      gst_event_unref (parse->pending_segment);
+
+    parse->pending_segment =
+        gst_event_new_new_segment (FALSE, parse->segment.rate,
+        parse->segment.format,
+        parse->segment.start, parse->segment.stop, parse->segment.last_stop);
+
+    GST_OBJECT_UNLOCK (parse);
+  } else {
+    /* We must make sure streaming has finished before resetting things
+     * and calling the ::stop vfunc */
+    GST_PAD_STREAM_LOCK (parse->sinkpad);
+    GST_PAD_STREAM_UNLOCK (parse->sinkpad);
+
+    if (parse->priv->pad_mode != GST_ACTIVATE_NONE && klass->stop)
+      result = klass->stop (parse);
+
+    g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref,
+        NULL);
+    g_list_free (parse->priv->pending_events);
+    parse->priv->pending_events = NULL;
+
+    if (parse->priv->cache) {
+      gst_buffer_unref (parse->priv->cache);
+      parse->priv->cache = NULL;
+    }
+
+    parse->priv->pad_mode = GST_ACTIVATE_NONE;
+  }
+  GST_DEBUG_OBJECT (parse, "activate: %d", result);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_sink_activate_push:
+ * @pad: #GstPad to be (de)activated.
+ * @active: TRUE when activating, FALSE when deactivating.
+ *
+ * Returns: TRUE if (de)activation succeeded.
+ */
+static gboolean
+gst_base_parse_sink_activate_push (GstPad * pad, gboolean active)
+{
+  gboolean result = TRUE;
+  GstBaseParse *parse;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+
+  GST_DEBUG_OBJECT (parse, "sink activate push");
+
+  result = gst_base_parse_activate (parse, active);
+
+  if (result)
+    parse->priv->pad_mode = active ? GST_ACTIVATE_PUSH : GST_ACTIVATE_NONE;
+
+  GST_DEBUG_OBJECT (parse, "sink activate push: %d", result);
+
+  gst_object_unref (parse);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_sink_activate_pull:
+ * @sinkpad: #GstPad to be (de)activated.
+ * @active: TRUE when activating, FALSE when deactivating.
+ *
+ * Returns: TRUE if (de)activation succeeded.
+ */
+static gboolean
+gst_base_parse_sink_activate_pull (GstPad * sinkpad, gboolean active)
+{
+  gboolean result = FALSE;
+  GstBaseParse *parse;
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (sinkpad));
+
+  GST_DEBUG_OBJECT (parse, "activate pull");
+
+  result = gst_base_parse_activate (parse, active);
+
+  if (result) {
+    if (active) {
+      result &= gst_pad_start_task (sinkpad,
+          (GstTaskFunction) gst_base_parse_loop, sinkpad);
+    } else {
+      result &= gst_pad_stop_task (sinkpad);
+    }
+  }
+
+  if (result)
+    parse->priv->pad_mode = active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE;
+
+  GST_DEBUG_OBJECT (parse, "sink activate pull: %d", result);
+
+  gst_object_unref (parse);
+  return result;
+}
+
+
+/**
+ * gst_base_parse_set_duration:
+ * @parse: #GstBaseParse.
+ * @fmt: #GstFormat.
+ * @duration: duration value.
+ *
+ * Sets the duration of the currently playing media. Subclass can use this
+ * when it notices a change in the media duration.
+ */
+void
+gst_base_parse_set_duration (GstBaseParse * parse,
+    GstFormat fmt, gint64 duration)
+{
+  g_return_if_fail (parse != NULL);
+
+  GST_BASE_PARSE_LOCK (parse);
+  if (duration != parse->priv->duration) {
+    GstMessage *m;
+
+    m = gst_message_new_duration (GST_OBJECT (parse), fmt, duration);
+    gst_element_post_message (GST_ELEMENT (parse), m);
+
+    /* TODO: what about duration tag? */
+  }
+  parse->priv->duration = duration;
+  parse->priv->duration_fmt = fmt;
+  GST_DEBUG_OBJECT (parse, "set duration: %lld", duration);
+  GST_BASE_PARSE_UNLOCK (parse);
+}
+
+
+/**
+ * gst_base_parse_set_min_frame_size:
+ * @parse: #GstBaseParse.
+ * @min_size: Minimum size of the data that this base class should give to
+ *            subclass.
+ *
+ * Subclass can use this function to tell the base class that it needs to
+ * give at least #min_size buffers.
+ */
+void
+gst_base_parse_set_min_frame_size (GstBaseParse * parse, guint min_size)
+{
+  g_return_if_fail (parse != NULL);
+
+  GST_BASE_PARSE_LOCK (parse);
+  parse->priv->min_frame_size = min_size;
+  GST_LOG_OBJECT (parse, "set frame_min_size: %d", min_size);
+  GST_BASE_PARSE_UNLOCK (parse);
+}
+
+
+/**
+ * gst_base_parse_get_querytypes:
+ * @pad: GstPad
+ *
+ * Returns: A table of #GstQueryType items describing supported query types.
+ */
+static const GstQueryType *
+gst_base_parse_get_querytypes (GstPad * pad)
+{
+  static const GstQueryType list[] = {
+    GST_QUERY_POSITION,
+    GST_QUERY_DURATION,
+    GST_QUERY_FORMATS,
+    GST_QUERY_SEEKING,
+    GST_QUERY_CONVERT,
+    0
+  };
+
+  return list;
+}
+
+
+/**
+ * gst_base_parse_query:
+ * @pad: #GstPad.
+ * @query: #GstQuery.
+ *
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_base_parse_query (GstPad * pad, GstQuery * query)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *klass;
+  gboolean res = FALSE;
+
+  parse = GST_BASE_PARSE (GST_PAD_PARENT (pad));
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  /* If subclass doesn't provide conversion function we can't reply
+     to the query either */
+  if (!klass->convert) {
+    return FALSE;
+  }
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_POSITION:
+    {
+      gint64 dest_value;
+      GstFormat format;
+
+      GST_DEBUG_OBJECT (parse, "position query");
+
+      gst_query_parse_position (query, &format, NULL);
+
+      g_mutex_lock (parse->parse_lock);
+
+      if (format == GST_FORMAT_BYTES) {
+        dest_value = parse->priv->offset;
+        res = TRUE;
+      } else if (format == parse->segment.format &&
+          GST_CLOCK_TIME_IS_VALID (parse->segment.last_stop)) {
+        dest_value = parse->segment.last_stop;
+        res = TRUE;
+      } else {
+        /* priv->offset is updated in both PUSH/PULL modes */
+        res = klass->convert (parse, GST_FORMAT_BYTES, parse->priv->offset,
+            format, &dest_value);
+      }
+      g_mutex_unlock (parse->parse_lock);
+
+      if (res)
+        gst_query_set_position (query, format, dest_value);
+      else
+        res = gst_pad_query_default (pad, query);
+
+      break;
+    }
+    case GST_QUERY_DURATION:
+    {
+      GstFormat format;
+      gint64 dest_value;
+
+      GST_DEBUG_OBJECT (parse, "duration query");
+
+      gst_query_parse_duration (query, &format, NULL);
+
+      g_mutex_lock (parse->parse_lock);
+
+      if (format == GST_FORMAT_BYTES) {
+        res = gst_pad_query_peer_duration (parse->sinkpad, &format,
+            &dest_value);
+      } else if (parse->priv->duration != -1 &&
+          format == parse->priv->duration_fmt) {
+        dest_value = parse->priv->duration;
+        res = TRUE;
+      } else if (parse->priv->duration != -1) {
+        res = klass->convert (parse, parse->priv->duration_fmt,
+            parse->priv->duration, format, &dest_value);
+      }
+
+      g_mutex_unlock (parse->parse_lock);
+
+      if (res)
+        gst_query_set_duration (query, format, dest_value);
+      else
+        res = gst_pad_query_default (pad, query);
+      break;
+    }
+    case GST_QUERY_SEEKING:
+    {
+      GstFormat fmt;
+      gboolean seekable = FALSE;
+
+      GST_DEBUG_OBJECT (parse, "seeking query");
+
+      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
+
+      if (fmt != GST_FORMAT_TIME) {
+        return gst_pad_query_default (pad, query);
+      }
+
+      seekable = klass->is_seekable (parse);
+
+      /* TODO: could this duration be calculated/converted if subclass
+         hasn't given it? */
+      gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0,
+          (parse->priv->duration == -1) ?
+          GST_CLOCK_TIME_NONE : parse->priv->duration);
+
+      GST_DEBUG_OBJECT (parse, "seekable: %d", seekable);
+      res = TRUE;
+      break;
+    }
+    case GST_QUERY_FORMATS:
+      gst_query_set_formatsv (query, 3, fmtlist);
+      res = TRUE;
+      break;
+
+    case GST_QUERY_CONVERT:
+    {
+      GstFormat src_format, dest_format;
+      gint64 src_value, dest_value;
+
+      gst_query_parse_convert (query, &src_format, &src_value,
+          &dest_format, &dest_value);
+
+      /* FIXME: hm? doesn't make sense 
+       * We require all those values to be given
+       if (src_format && src_value && dest_format && dest_value ) { */
+      res = klass->convert (parse, src_format, src_value,
+          dest_format, &dest_value);
+      if (res) {
+        gst_query_set_convert (query, src_format, src_value,
+            dest_format, dest_value);
+      }
+      /*} */
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, query);
+      break;
+  }
+  return res;
+}
+
+
+/**
+ * gst_base_parse_handle_seek:
+ * @parse: #GstBaseParse.
+ * @event: #GstEvent.
+ *
+ * Returns: TRUE if seek succeeded.
+ */
+static gboolean
+gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event)
+{
+  GstBaseParseClass *klass;
+  gdouble rate;
+  GstFormat format;
+  GstSeekFlags flags;
+  GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
+  gboolean flush, update, res = TRUE;
+  gint64 cur, stop, seekpos;
+  GstSegment seeksegment = { 0, };
+  GstFormat dstformat;
+
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  gst_event_parse_seek (event, &rate, &format, &flags,
+      &cur_type, &cur, &stop_type, &stop);
+
+  /* no negative rates yet */
+  if (rate < 0.0)
+    goto negative_rate;
+
+  if (cur_type != GST_SEEK_TYPE_SET)
+    goto wrong_type;
+
+  /* For any format other than TIME, see if upstream handles
+   * it directly or fail. For TIME, try upstream, but do it ourselves if
+   * it fails upstream */
+  if (format != GST_FORMAT_TIME) {
+    gst_event_ref (event);
+    return gst_pad_push_event (parse->sinkpad, event);
+  } else {
+    gst_event_ref (event);
+    if (gst_pad_push_event (parse->sinkpad, event))
+      return TRUE;
+  }
+
+  /* get flush flag */
+  flush = flags & GST_SEEK_FLAG_FLUSH;
+
+  dstformat = GST_FORMAT_BYTES;
+  if (!gst_pad_query_convert (parse->srcpad, format, cur, &dstformat, &seekpos)) {
+    GST_DEBUG_OBJECT (parse, "conversion failed");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (parse, "seek position %lld in bytes: %lld", cur, seekpos);
+
+  if (parse->priv->pad_mode == GST_ACTIVATE_PULL) {
+    gint64 last_stop;
+
+    GST_DEBUG_OBJECT (parse, "seek in PULL mode");
+
+    if (flush) {
+      if (parse->srcpad) {
+        GST_DEBUG_OBJECT (parse, "sending flush start");
+        gst_pad_push_event (parse->srcpad, gst_event_new_flush_start ());
+      }
+    } else {
+      gst_pad_pause_task (parse->sinkpad);
+    }
+
+    /* we should now be able to grab the streaming thread because we stopped it
+     * with the above flush/pause code */
+    GST_PAD_STREAM_LOCK (parse->sinkpad);
+
+    /* save current position */
+    last_stop = parse->segment.last_stop;
+    GST_DEBUG_OBJECT (parse, "stopped streaming at %" G_GINT64_FORMAT,
+        last_stop);
+
+    /* copy segment, we need this because we still need the old
+     * segment when we close the current segment. */
+    memcpy (&seeksegment, &parse->segment, sizeof (GstSegment));
+
+    GST_DEBUG_OBJECT (parse, "configuring seek");
+    gst_segment_set_seek (&seeksegment, rate, format, flags,
+        cur_type, cur, stop_type, stop, &update);
+
+    /* figure out the last position we need to play. If it's configured (stop !=
+     * -1), use that, else we play until the total duration of the file */
+    if ((stop = seeksegment.stop) == -1)
+      stop = seeksegment.duration;
+
+    parse->priv->offset = seekpos;
+
+    /* prepare for streaming again */
+    if (flush) {
+      GST_DEBUG_OBJECT (parse, "sending flush stop");
+      gst_pad_push_event (parse->srcpad, gst_event_new_flush_stop ());
+    } else {
+      if (parse->close_segment)
+        gst_event_unref (parse->close_segment);
+
+      parse->close_segment = gst_event_new_new_segment (TRUE,
+          parse->segment.rate, parse->segment.format,
+          parse->segment.accum, parse->segment.last_stop, parse->segment.accum);
+
+      /* keep track of our last_stop */
+      seeksegment.accum = parse->segment.last_stop;
+
+      GST_DEBUG_OBJECT (parse, "Created close seg format %d, "
+          "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+          ", pos = %" GST_TIME_FORMAT, format,
+          GST_TIME_ARGS (parse->segment.accum),
+          GST_TIME_ARGS (parse->segment.last_stop),
+          GST_TIME_ARGS (parse->segment.accum));
+    }
+
+    memcpy (&parse->segment, &seeksegment, sizeof (GstSegment));
+
+    /* store the newsegment event so it can be sent from the streaming thread. */
+    if (parse->pending_segment)
+      gst_event_unref (parse->pending_segment);
+
+    /* This will be sent later in _loop() */
+    parse->pending_segment =
+        gst_event_new_new_segment (FALSE, parse->segment.rate,
+        parse->segment.format,
+        parse->segment.last_stop, stop, parse->segment.last_stop);
+
+    GST_DEBUG_OBJECT (parse, "Created newseg format %d, "
+        "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT
+        ", pos = %" GST_TIME_FORMAT, format,
+        GST_TIME_ARGS (parse->segment.last_stop),
+        GST_TIME_ARGS (stop), GST_TIME_ARGS (parse->segment.last_stop));
+
+    /* mark discont if we are going to stream from another position. */
+    if (last_stop != parse->segment.last_stop) {
+      GST_DEBUG_OBJECT (parse,
+          "mark DISCONT, we did a seek to another position");
+      parse->priv->discont = TRUE;
+    }
+
+    /* Start streaming thread if paused */
+    gst_pad_start_task (parse->sinkpad,
+        (GstTaskFunction) gst_base_parse_loop, parse->sinkpad);
+
+    GST_PAD_STREAM_UNLOCK (parse->sinkpad);
+  } else {
+    GstEvent *new_event;
+    /* The only thing we need to do in PUSH-mode is to send the
+       seek event (in bytes) to upstream. Segment / flush handling happens
+       in corresponding src event handlers */
+    GST_DEBUG_OBJECT (parse, "seek in PUSH mode");
+    new_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flush,
+        GST_SEEK_TYPE_SET, seekpos, stop_type, stop);
+
+    res = gst_pad_push_event (parse->sinkpad, new_event);
+  }
+
+done:
+  return res;
+
+  /* ERRORS */
+negative_rate:
+  {
+    GST_DEBUG_OBJECT (parse, "negative playback rates are not supported yet.");
+    res = FALSE;
+    goto done;
+  }
+wrong_type:
+  {
+    GST_DEBUG_OBJECT (parse, "unsupported seek type.");
+    res = FALSE;
+    goto done;
+  }
+}
+
+
+/**
+ * gst_base_parse_sink_setcaps:
+ * @pad: #GstPad.
+ * @caps: #GstCaps.
+ *
+ * Returns: TRUE if caps were accepted.
+ */
+static gboolean
+gst_base_parse_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstBaseParse *parse;
+  GstBaseParseClass *klass;
+  gboolean res = TRUE;
+
+  gchar *caps_str = gst_caps_to_string (caps);
+  g_free (caps_str);
+
+  parse = GST_BASE_PARSE (gst_pad_get_parent (pad));
+  klass = GST_BASE_PARSE_GET_CLASS (parse);
+
+  GST_DEBUG_OBJECT (parse, "setcaps: %s", caps_str);
+
+  if (klass->set_sink_caps)
+    res = klass->set_sink_caps (parse, caps);
+
+  parse->negotiated = res;
+  gst_object_unref (parse);
+  return gst_pad_set_caps (pad, caps);
+}
diff --git a/gst/amrparse/gstbaseparse.h b/gst/amrparse/gstbaseparse.h
new file mode 100644 (file)
index 0000000..a2856cc
--- /dev/null
@@ -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__ */