Add FLAC parser, based on GstBaseParse. Also add the bit and byte reader that will...
authorSebastian Dröge <slomo@circular-chaos.org>
Mon, 29 Sep 2008 08:26:54 +0000 (08:26 +0000)
committerSebastian Dröge <slomo@circular-chaos.org>
Mon, 29 Sep 2008 08:26:54 +0000 (08:26 +0000)
Original commit message from CVS:
* configure.ac:
* gst/flacparse/Makefile.am:
* gst/flacparse/gstbaseparse.c: (gst_base_parse_get_type),
(gst_base_parse_base_init), (gst_base_parse_base_finalize),
(gst_base_parse_finalize), (gst_base_parse_class_init),
(gst_base_parse_init), (gst_base_parse_check_frame),
(gst_base_parse_parse_frame), (gst_base_parse_bytepos_to_time),
(gst_base_parse_sink_event), (gst_base_parse_sink_eventfunc),
(gst_base_parse_src_event), (gst_base_parse_src_eventfunc),
(gst_base_parse_is_seekable), (gst_base_parse_push_buffer),
(gst_base_parse_handle_and_push_buffer), (gst_base_parse_drain),
(gst_base_parse_chain), (gst_base_parse_pull_range),
(gst_base_parse_loop), (gst_base_parse_sink_activate),
(gst_base_parse_activate), (gst_base_parse_sink_activate_push),
(gst_base_parse_sink_activate_pull), (gst_base_parse_set_duration),
(gst_base_parse_set_min_frame_size),
(gst_base_parse_get_querytypes), (gst_base_parse_query),
(gst_base_parse_handle_seek), (gst_base_parse_sink_setcaps):
* gst/flacparse/gstbaseparse.h:
* gst/flacparse/gstbitreader.c: (gst_bit_reader_new),
(gst_bit_reader_new_from_buffer), (gst_bit_reader_free),
(gst_bit_reader_init), (gst_bit_reader_init_from_buffer),
(gst_bit_reader_set_pos), (gst_bit_reader_get_pos),
(gst_bit_reader_get_remaining), (gst_bit_reader_skip),
(gst_bit_reader_skip_to_byte):
* gst/flacparse/gstbitreader.h:
* gst/flacparse/gstbytereader.c: (GDOUBLE_SWAP_LE_BE),
(GFLOAT_SWAP_LE_BE), (gst_byte_reader_new),
(gst_byte_reader_new_from_buffer), (gst_byte_reader_free),
(gst_byte_reader_init), (gst_byte_reader_init_from_buffer),
(gst_byte_reader_set_pos), (gst_byte_reader_get_pos),
(gst_byte_reader_get_remaining), (gst_byte_reader_skip),
(gst_byte_reader_get_uint8), (gst_byte_reader_get_int8),
(gst_byte_reader_peek_uint8), (gst_byte_reader_peek_int8),
(gst_byte_reader_get_uint24_le), (gst_byte_reader_get_uint24_be),
(gst_byte_reader_get_int24_le), (gst_byte_reader_get_int24_be),
(gst_byte_reader_peek_uint24_le), (gst_byte_reader_peek_uint24_be),
(gst_byte_reader_peek_int24_le), (gst_byte_reader_peek_int24_be):
* gst/flacparse/gstbytereader.h:
* gst/flacparse/gstflac.c: (plugin_init):
* gst/flacparse/gstflacparse.c: (gst_flac_parse_base_init),
(gst_flac_parse_class_init), (gst_flac_parse_init),
(gst_flac_parse_finalize), (gst_flac_parse_start),
(gst_flac_parse_stop), (gst_flac_parse_get_frame_size),
(gst_flac_parse_check_valid_frame),
(gst_flac_parse_handle_streaminfo),
(gst_flac_parse_handle_vorbiscomment),
(gst_flac_parse_handle_picture), (_value_array_append_buffer),
(gst_flac_parse_handle_headers), (gst_flac_parse_generate_headers),
(gst_flac_parse_parse_frame):
* gst/flacparse/gstflacparse.h:
Add FLAC parser, based on GstBaseParse. Also add the bit and byte reader
that will be added to libgstbase later.
The FLAC parser is currently not 100% bug free and fails to get the
correct frame size for some frames in some streams.

12 files changed:
ChangeLog
configure.ac
gst/flacparse/Makefile.am [new file with mode: 0644]
gst/flacparse/gstbaseparse.c [new file with mode: 0644]
gst/flacparse/gstbaseparse.h [new file with mode: 0644]
gst/flacparse/gstbitreader.c [new file with mode: 0644]
gst/flacparse/gstbitreader.h [new file with mode: 0644]
gst/flacparse/gstbytereader.c [new file with mode: 0644]
gst/flacparse/gstbytereader.h [new file with mode: 0644]
gst/flacparse/gstflac.c [new file with mode: 0644]
gst/flacparse/gstflacparse.c [new file with mode: 0644]
gst/flacparse/gstflacparse.h [new file with mode: 0644]

index e28df05..e32924f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,62 @@
+2008-09-29  Sebastian Dröge  <sebastian.droege@collabora.co.uk>
+
+       * configure.ac:
+       * gst/flacparse/Makefile.am:
+       * gst/flacparse/gstbaseparse.c: (gst_base_parse_get_type),
+       (gst_base_parse_base_init), (gst_base_parse_base_finalize),
+       (gst_base_parse_finalize), (gst_base_parse_class_init),
+       (gst_base_parse_init), (gst_base_parse_check_frame),
+       (gst_base_parse_parse_frame), (gst_base_parse_bytepos_to_time),
+       (gst_base_parse_sink_event), (gst_base_parse_sink_eventfunc),
+       (gst_base_parse_src_event), (gst_base_parse_src_eventfunc),
+       (gst_base_parse_is_seekable), (gst_base_parse_push_buffer),
+       (gst_base_parse_handle_and_push_buffer), (gst_base_parse_drain),
+       (gst_base_parse_chain), (gst_base_parse_pull_range),
+       (gst_base_parse_loop), (gst_base_parse_sink_activate),
+       (gst_base_parse_activate), (gst_base_parse_sink_activate_push),
+       (gst_base_parse_sink_activate_pull), (gst_base_parse_set_duration),
+       (gst_base_parse_set_min_frame_size),
+       (gst_base_parse_get_querytypes), (gst_base_parse_query),
+       (gst_base_parse_handle_seek), (gst_base_parse_sink_setcaps):
+       * gst/flacparse/gstbaseparse.h:
+       * gst/flacparse/gstbitreader.c: (gst_bit_reader_new),
+       (gst_bit_reader_new_from_buffer), (gst_bit_reader_free),
+       (gst_bit_reader_init), (gst_bit_reader_init_from_buffer),
+       (gst_bit_reader_set_pos), (gst_bit_reader_get_pos),
+       (gst_bit_reader_get_remaining), (gst_bit_reader_skip),
+       (gst_bit_reader_skip_to_byte):
+       * gst/flacparse/gstbitreader.h:
+       * gst/flacparse/gstbytereader.c: (GDOUBLE_SWAP_LE_BE),
+       (GFLOAT_SWAP_LE_BE), (gst_byte_reader_new),
+       (gst_byte_reader_new_from_buffer), (gst_byte_reader_free),
+       (gst_byte_reader_init), (gst_byte_reader_init_from_buffer),
+       (gst_byte_reader_set_pos), (gst_byte_reader_get_pos),
+       (gst_byte_reader_get_remaining), (gst_byte_reader_skip),
+       (gst_byte_reader_get_uint8), (gst_byte_reader_get_int8),
+       (gst_byte_reader_peek_uint8), (gst_byte_reader_peek_int8),
+       (gst_byte_reader_get_uint24_le), (gst_byte_reader_get_uint24_be),
+       (gst_byte_reader_get_int24_le), (gst_byte_reader_get_int24_be),
+       (gst_byte_reader_peek_uint24_le), (gst_byte_reader_peek_uint24_be),
+       (gst_byte_reader_peek_int24_le), (gst_byte_reader_peek_int24_be):
+       * gst/flacparse/gstbytereader.h:
+       * gst/flacparse/gstflac.c: (plugin_init):
+       * gst/flacparse/gstflacparse.c: (gst_flac_parse_base_init),
+       (gst_flac_parse_class_init), (gst_flac_parse_init),
+       (gst_flac_parse_finalize), (gst_flac_parse_start),
+       (gst_flac_parse_stop), (gst_flac_parse_get_frame_size),
+       (gst_flac_parse_check_valid_frame),
+       (gst_flac_parse_handle_streaminfo),
+       (gst_flac_parse_handle_vorbiscomment),
+       (gst_flac_parse_handle_picture), (_value_array_append_buffer),
+       (gst_flac_parse_handle_headers), (gst_flac_parse_generate_headers),
+       (gst_flac_parse_parse_frame):
+       * gst/flacparse/gstflacparse.h:
+       Add FLAC parser, based on GstBaseParse. Also add the bit and byte reader
+       that will be added to libgstbase later.
+
+       The FLAC parser is currently not 100% bug free and fails to get the
+       correct frame size for some frames in some streams.
+
 2008-09-27  Jan Schmidt  <jan.schmidt@sun.com>
 
        * ext/resindvd/gstmpegdemux.c:
index 515bd92..414a962 100644 (file)
@@ -251,6 +251,8 @@ AG_GST_CHECK_PLUGIN(deinterlace2)
 AG_GST_CHECK_PLUGIN(dvdspu)
 AG_GST_CHECK_PLUGIN(festival)
 AG_GST_CHECK_PLUGIN(filter)
+AG_GST_CHECK_PLUGIN(flacparse)
+AG_GST_CHECK_PLUGIN(flacparse)
 AG_GST_CHECK_PLUGIN(flv)
 AG_GST_CHECK_PLUGIN(freeze)
 AG_GST_CHECK_PLUGIN(h264parse)
@@ -1403,6 +1405,7 @@ gst/deinterlace2/Makefile
 gst/dvdspu/Makefile
 gst/festival/Makefile
 gst/filter/Makefile
+gst/flacparse/Makefile
 gst/flv/Makefile
 gst/freeze/Makefile
 gst/h264parse/Makefile
diff --git a/gst/flacparse/Makefile.am b/gst/flacparse/Makefile.am
new file mode 100644 (file)
index 0000000..21e2f74
--- /dev/null
@@ -0,0 +1,11 @@
+plugin_LTLIBRARIES = libgstflacparse.la
+
+libgstflacparse_la_SOURCES = gstflac.c gstbaseparse.c gstflacparse.c gstbitreader.c gstbytereader.c
+libgstflacparse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
+libgstflacparse_la_LIBADD = \
+       $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \
+       -lgstaudio-$(GST_MAJORMINOR) \
+       $(GST_BASE_LIBS) $(GST_LIBS)
+libgstflacparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = gstbitreader.h gstbytereader.h gstbaseparse.h gstflacparse.h
diff --git a/gst/flacparse/gstbaseparse.c b/gst/flacparse/gstbaseparse.c
new file mode 100644 (file)
index 0000000..dd990ab
--- /dev/null
@@ -0,0 +1,1763 @@
+/* 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
+ *  - Handle upstream timestamps
+ *  - Bitrate tracking => inaccurate seeking, inaccurate duration calculation
+ *  - Let subclass decide if frames outside the segment should be dropped
+ */
+
+#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;
+
+  GstAdapter *adapter;
+
+  guint64 bitrate_sum;
+  guint64 avg_bitrate;
+};
+
+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->priv->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->priv->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->priv->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_push_buffer:
+ * @parse: #GstBaseParse.
+ * @buffer: #GstBuffer.
+ *
+ * Pushes @buffer on the srcpad and updates the internal state.
+ *
+ * This must be called with srcpad STREAM_LOCK held.
+ *
+ * Returns: #GstFlowReturn
+ */
+GstFlowReturn
+gst_base_parse_push_buffer (GstBaseParse * parse, GstBuffer * buffer)
+{
+  GstClockTime last_stop = GST_CLOCK_TIME_NONE;
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  g_return_val_if_fail (GST_PAD_CAPS (parse->srcpad), GST_FLOW_ERROR);
+
+  if (parse->priv->pad_mode == GST_ACTIVATE_PULL) {
+    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;
+    }
+  } else {
+    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;
+    }
+  }
+
+  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 (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 (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 {
+    guint size = GST_BUFFER_SIZE (buffer);
+
+    ret = gst_pad_push (parse->srcpad, buffer);
+
+    GST_LOG_OBJECT (parse, "frame (%d bytes) pushed: %s", size,
+        gst_flow_get_name (ret));
+  }
+
+  /* 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);
+
+  return ret;
+}
+
+/**
+ * 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;
+
+  if (parse->priv->discont) {
+    GST_DEBUG_OBJECT (parse, "marking DISCONT");
+    GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+    parse->priv->discont = FALSE;
+  }
+
+  /* FIXME: Set upstream timestamp if possible */
+
+  ret = klass->parse_frame (parse, buffer);
+
+  /* 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;
+  }
+
+  g_return_val_if_fail (GST_PAD_CAPS (parse->srcpad), GST_FLOW_ERROR);
+
+  ret = gst_base_parse_push_buffer (parse, buffer);
+
+  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->priv->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);
+
+  /* Make sure that adapter doesn't have any old data after
+     newsegment has been received and update our offset */
+  if (parse->pending_segment) {
+    gst_adapter_clear (parse->priv->adapter);
+    parse->priv->offset = parse->priv->pending_offset;
+  }
+
+  if (buffer) {
+    GST_LOG_OBJECT (parse, "buffer size: %d, offset = %lld",
+        GST_BUFFER_SIZE (buffer), GST_BUFFER_OFFSET (buffer));
+
+
+    gst_adapter_push (parse->priv->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 ();
+
+    /* Synchronization loop */
+    for (;;) {
+
+      GST_BASE_PARSE_LOCK (parse);
+      min_size = parse->priv->min_frame_size;
+      GST_BASE_PARSE_UNLOCK (parse);
+
+      /* Collect at least min_frame_size bytes */
+      if (gst_adapter_available (parse->priv->adapter) < min_size) {
+        GST_DEBUG_OBJECT (parse, "not enough data available (only %d bytes)",
+            gst_adapter_available (parse->priv->adapter));
+        gst_buffer_unref (tmpbuf);
+        goto done;
+      }
+
+      data = gst_adapter_peek (parse->priv->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->priv->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->priv->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->priv->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->priv->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);
+
+  /* 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);
+
+      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/flacparse/gstbaseparse.h b/gst/flacparse/gstbaseparse.h
new file mode 100644 (file)
index 0000000..b4b44c2
--- /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;
+
+  /*< 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);
+
+GstFlowReturn gst_base_parse_push_buffer (GstBaseParse *parse, GstBuffer *buffer);
+
+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/flacparse/gstbitreader.c b/gst/flacparse/gstbitreader.c
new file mode 100644 (file)
index 0000000..7647ab9
--- /dev/null
@@ -0,0 +1,400 @@
+/* GStreamer
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstbitreader.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gstbitreader
+ * @short_description: Reads any number of bits from a memory buffer
+ *
+ * #GstBitReader provides a bit reader that can read any number of bits
+ * from a memory buffer. It provides functions for reading any number of bits
+ * into 8, 16, 32 and 64 bit variables.
+ */
+
+/**
+ * gst_bit_reader_new:
+ * @data: Data from which the #GstBitReader should read
+ * @size: Size of @data in bytes
+ *
+ * Create a new #GstBitReader instance, which will read from @data.
+ *
+ * Returns: a new #GstBitReader instance
+ *
+ * Since: 0.10.22
+ */
+GstBitReader *
+gst_bit_reader_new (const guint8 * data, guint size)
+{
+  GstBitReader *ret = g_slice_new0 (GstBitReader);
+
+  ret->data = data;
+  ret->size = size;
+
+  return ret;
+}
+
+/**
+ * gst_bit_reader_new_from_buffer:
+ * @buffer: Buffer from which the #GstBitReader should read
+ *
+ * Create a new #GstBitReader instance, which will read from the
+ * #GstBuffer @buffer.
+ *
+ * Returns: a new #GstBitReader instance
+ *
+ * Since: 0.10.22
+ */
+GstBitReader *
+gst_bit_reader_new_from_buffer (const GstBuffer * buffer)
+{
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+
+  return gst_bit_reader_new (GST_BUFFER_DATA (buffer),
+      GST_BUFFER_SIZE (buffer));
+}
+
+/**
+ * gst_bit_reader_free:
+ * @reader: a #GstBitReader instance
+ *
+ * Frees a #GstBitReader instance, which was previously allocated by
+ * gst_bit_reader_new() or gst_bit_reader_new_from_buffer().
+ * 
+ * Since: 0.10.22
+ */
+void
+gst_bit_reader_free (GstBitReader * reader)
+{
+  g_return_if_fail (reader != NULL);
+
+  g_slice_free (GstBitReader, reader);
+}
+
+/**
+ * gst_bit_reader_init:
+ * @reader: a #GstBitReader instance
+ * @data: Data from which the #GstBitReader should read
+ * @size: Size of @data in bytes
+ *
+ * Initializes a #GstBitReader instance to read from @data. This function
+ * can be called on already initialized instances.
+ * 
+ * Since: 0.10.22
+ */
+void
+gst_bit_reader_init (GstBitReader * reader, const guint8 * data, guint size)
+{
+  g_return_if_fail (reader != NULL);
+
+  reader->data = data;
+  reader->size = size;
+  reader->byte = reader->bit = 0;
+}
+
+/**
+ * gst_bit_reader_init:
+ * @reader: a #GstBitReader instance
+ * @buffer: Buffer from which the #GstBitReader should read
+ *
+ * Initializes a #GstBitReader instance to read from @buffer. This function
+ * can be called on already initialized instances.
+ * 
+ * Since: 0.10.22
+ */
+void
+gst_bit_reader_init_from_buffer (GstBitReader * reader,
+    const GstBuffer * buffer)
+{
+  g_return_if_fail (GST_IS_BUFFER (buffer));
+
+  gst_bit_reader_init (reader, GST_BUFFER_DATA (buffer),
+      GST_BUFFER_SIZE (buffer));
+}
+
+/**
+ * gst_bit_reader_set_pos:
+ * @reader: a #GstBitReader instance
+ * @pos: The new position in bits
+ *
+ * Sets the new position of a #GstBitReader instance to @pos in bits.
+ *
+ * Returns: %TRUE if the position could be set successfully, %FALSE
+ * otherwise.
+ * 
+ * Since: 0.10.22
+ */
+gboolean
+gst_bit_reader_set_pos (GstBitReader * reader, guint pos)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+
+  if (pos > reader->size * 8)
+    return FALSE;
+
+  reader->byte = pos / 8;
+  reader->bit = pos % 8;
+
+  return TRUE;
+}
+
+/**
+ * gst_bit_reader_get_pos:
+ * @reader: a #GstBitReader instance
+ *
+ * Returns the current position of a #GstBitReader instance in bits.
+ *
+ * Returns: The current position of @reader in bits.
+ * 
+ * Since: 0.10.22
+ */
+guint
+gst_bit_reader_get_pos (const GstBitReader * reader)
+{
+  g_return_val_if_fail (reader != NULL, 0);
+
+  return reader->byte * 8 + reader->bit;
+}
+
+/**
+ * gst_bit_reader_get_remaining:
+ * @reader: a #GstBitReader instance
+ *
+ * Returns the remaining number of bits of a #GstBitReader instance.
+ *
+ * Returns: The remaining number of bits of @reader instance.
+ * 
+ * Since: 0.10.22
+ */
+guint
+gst_bit_reader_get_remaining (const GstBitReader * reader)
+{
+  g_return_val_if_fail (reader != NULL, 0);
+
+  return reader->size * 8 - (reader->byte * 8 + reader->bit);
+}
+
+/**
+ * gst_bit_reader_skip:
+ * @reader: a #GstBitReader instance
+ * @nbits: the number of bits to skip
+ *
+ * Skips @nbits bits of the #GstBitReader instance.
+ *
+ * Returns: %TRUE if @nbits bits could be skipped, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+gboolean
+gst_bit_reader_skip (GstBitReader * reader, guint nbits)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+
+  if (gst_bit_reader_get_remaining (reader) < nbits)
+    return FALSE;
+
+  reader->bit += nbits;
+  reader->byte += reader->bit / 8;
+  reader->bit = reader->bit % 8;
+
+  return TRUE;
+}
+
+/**
+ * gst_bit_reader_skip_to_byte:
+ * @reader: a #GstBitReader instance
+ *
+ * Skips until the next byte.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+gboolean
+gst_bit_reader_skip_to_byte (GstBitReader * reader)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+
+  if (reader->byte > reader->size)
+    return FALSE;
+
+  if (reader->bit) {
+    reader->bit = 0;
+    reader->byte++;
+  }
+
+  return TRUE;
+}
+
+/**
+ * gst_bit_reader_get_bits_uint8:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint8 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_bit_reader_get_bits_uint16:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint16 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_bit_reader_get_bits_uint32:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint32 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_bit_reader_get_bits_uint64:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint64 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_bit_reader_peek_bits_uint8:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint8 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_bit_reader_peek_bits_uint16:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint16 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_bit_reader_peek_bits_uint32:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint32 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_bit_reader_peek_bits_uint64:
+ * @reader: a #GstBitReader instance
+ * @val: Pointer to a #guint64 to store the result
+ * @nbits: number of bits to read
+ *
+ * Read @nbits bits into @val but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+#define GST_BIT_READER_READ_BITS(bits) \
+gboolean \
+gst_bit_reader_get_bits_uint##bits (GstBitReader *reader, guint##bits *val, guint nbits) \
+{ \
+  guint##bits ret = 0; \
+  \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  g_return_val_if_fail (nbits <= bits, FALSE); \
+  \
+  if (reader->byte * 8 + reader->bit + nbits > reader->size * 8) \
+    return FALSE; \
+  \
+  while (nbits > 0) { \
+    guint toread = MIN (nbits, 8 - reader->bit); \
+    \
+    ret <<= toread; \
+    ret |= (reader->data[reader->byte] & (0xff >> reader->bit)) >> (8 - toread - reader->bit); \
+    \
+    reader->bit += toread; \
+    if (reader->bit >= 8) { \
+      reader->byte++; \
+      reader->bit = 0; \
+    } \
+    nbits -= toread; \
+  } \
+  \
+  *val = ret; \
+  return TRUE; \
+} \
+\
+gboolean \
+gst_bit_reader_peek_bits_uint##bits (const GstBitReader *reader, guint##bits *val, guint nbits) \
+{ \
+  GstBitReader tmp; \
+  \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  tmp = *reader; \
+  return gst_bit_reader_get_bits_uint##bits (&tmp, val, nbits); \
+}
+
+GST_BIT_READER_READ_BITS (8);
+GST_BIT_READER_READ_BITS (16);
+GST_BIT_READER_READ_BITS (32);
+GST_BIT_READER_READ_BITS (64);
diff --git a/gst/flacparse/gstbitreader.h b/gst/flacparse/gstbitreader.h
new file mode 100644 (file)
index 0000000..5dc16a3
--- /dev/null
@@ -0,0 +1,99 @@
+/* GStreamer
+ *
+ * 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.
+ */
+
+#ifndef __GST_BIT_READER_H__
+#define __GST_BIT_READER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstBitReader:
+ * @data: Data from which the bit reader will read
+ * @size: Size of @data in bytes
+ * @byte: Current byte position
+ * @bit: Bit position in the current byte
+ *
+ * A bit reader instance.
+ */
+typedef struct {
+  const guint8 *data;
+  guint size;
+
+  guint byte;  /* Byte position */
+  guint bit;   /* Bit position in the current byte */
+} GstBitReader;
+
+GstBitReader * gst_bit_reader_new (const guint8 *data, guint size);
+GstBitReader * gst_bit_reader_new_from_buffer (const GstBuffer *buffer);
+void gst_bit_reader_free (GstBitReader *reader);
+
+void gst_bit_reader_init (GstBitReader *reader, const guint8 *data, guint size);
+void gst_bit_reader_init_from_buffer (GstBitReader *reader, const GstBuffer *buffer);
+
+gboolean gst_bit_reader_set_pos (GstBitReader *reader, guint pos);
+
+guint gst_bit_reader_get_pos (const GstBitReader *reader);
+guint gst_bit_reader_get_remaining (const GstBitReader *reader);
+
+gboolean gst_bit_reader_skip (GstBitReader *reader, guint nbits);
+gboolean gst_bit_reader_skip_to_byte (GstBitReader *reader);
+
+gboolean gst_bit_reader_get_bits_uint8 (GstBitReader *reader, guint8 *val, guint nbits);
+gboolean gst_bit_reader_get_bits_uint16 (GstBitReader *reader, guint16 *val, guint nbits);
+gboolean gst_bit_reader_get_bits_uint32 (GstBitReader *reader, guint32 *val, guint nbits);
+gboolean gst_bit_reader_get_bits_uint64 (GstBitReader *reader, guint64 *val, guint nbits);
+
+gboolean gst_bit_reader_peek_bits_uint8 (const GstBitReader *reader, guint8 *val, guint nbits);
+gboolean gst_bit_reader_peek_bits_uint16 (const GstBitReader *reader, guint16 *val, guint nbits);
+gboolean gst_bit_reader_peek_bits_uint32 (const GstBitReader *reader, guint32 *val, guint nbits);
+gboolean gst_bit_reader_peek_bits_uint64 (const GstBitReader *reader, guint64 *val, guint nbits);
+
+/**
+ * GST_BIT_READER_INIT:
+ * @data: Data from which the #GstBitReader should read
+ * @size: Size of @data in bytes
+ *
+ * A #GstBitReader must be initialized with this macro, before it can be
+ * used. This macro can used be to initialize a variable, but it cannot
+ * be assigned to a variable. In that case you have to use
+ * gst_bit_reader_init().
+ *
+ * Since: 0.10.22
+ */
+#define GST_BIT_READER_INIT(data, size) {data, size, 0, 0}
+
+/**
+ * GST_BIT_READER_INIT_FROM_BUFFER:
+ * @buffer: Buffer from which the #GstBitReader should read
+ *
+ * A #GstBitReader must be initialized with this macro, before it can be
+ * used. This macro can used be to initialize a variable, but it cannot
+ * be assigned to a variable. In that case you have to use
+ * gst_bit_reader_init().
+ *
+ * Since: 0.10.22
+ */
+#define GST_BIT_READER_INIT_FROM_BUFFER(buffer) {GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 0, 0}
+
+G_END_DECLS
+
+#endif /* __GST_BIT_READER_H__ */
diff --git a/gst/flacparse/gstbytereader.c b/gst/flacparse/gstbytereader.c
new file mode 100644 (file)
index 0000000..cb91ea7
--- /dev/null
@@ -0,0 +1,1231 @@
+/* GStreamer
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstbytereader.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gstbytereader
+ * @short_description: Reads different integer and floating point types from a memory buffer
+ *
+ * #GstByteReader provides a byte reader that can read different integer and
+ * floating point types from a memory buffer. It provides functions for reading
+ * signed/unsigned, little/big endian integers of 8, 16, 24, 32 and 64 bits
+ * and functions for reading little/big endian floating points numbers of
+ * 32 and 64 bits.
+ */
+
+
+/* Copied from gst/floatcast/floatcast.h as this is in gst-plugins-base */
+
+inline static gdouble
+GDOUBLE_SWAP_LE_BE (gdouble in)
+{
+  union
+  {
+    guint64 i;
+    gdouble d;
+  } u;
+
+  u.d = in;
+  u.i = GUINT64_SWAP_LE_BE (u.i);
+  return u.d;
+}
+
+inline static gfloat
+GFLOAT_SWAP_LE_BE (gfloat in)
+{
+  union
+  {
+    guint32 i;
+    gfloat f;
+  } u;
+
+  u.f = in;
+  u.i = GUINT32_SWAP_LE_BE (u.i);
+  return u.f;
+}
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GFLOAT_TO_LE(val)    ((gfloat) (val))
+#define GFLOAT_TO_BE(val)    (GFLOAT_SWAP_LE_BE (val))
+#define GDOUBLE_TO_LE(val)   ((gdouble) (val))
+#define GDOUBLE_TO_BE(val)   (GDOUBLE_SWAP_LE_BE (val))
+
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+#define GFLOAT_TO_LE(val)    (GFLOAT_SWAP_LE_BE (val))
+#define GFLOAT_TO_BE(val)    ((gfloat) (val))
+#define GDOUBLE_TO_LE(val)   (GDOUBLE_SWAP_LE_BE (val))
+#define GDOUBLE_TO_BE(val)   ((gdouble) (val))
+
+#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+#error unknown ENDIAN type
+#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+
+#define GFLOAT_FROM_LE(val)  (GFLOAT_TO_LE (val))
+#define GFLOAT_FROM_BE(val)  (GFLOAT_TO_BE (val))
+#define GDOUBLE_FROM_LE(val) (GDOUBLE_TO_LE (val))
+#define GDOUBLE_FROM_BE(val) (GDOUBLE_TO_BE (val))
+
+
+/**
+ * gst_byte_reader_new:
+ * @data: Data from which the #GstByteReader should read
+ * @size: Size of @data in bytes
+ *
+ * Create a new #GstByteReader instance, which will read from @data.
+ *
+ * Returns: a new #GstByteReader instance
+ *
+ * Since: 0.10.22
+ */
+GstByteReader *
+gst_byte_reader_new (const guint8 * data, guint size)
+{
+  GstByteReader *ret = g_slice_new0 (GstByteReader);
+
+  ret->data = data;
+  ret->size = size;
+
+  return ret;
+}
+
+/**
+ * gst_byte_reader_new_from_buffer:
+ * @buffer: Buffer from which the #GstByteReader should read
+ *
+ * Create a new #GstByteReader instance, which will read from the
+ * #GstBuffer @buffer.
+ *
+ * Returns: a new #GstByteReader instance
+ *
+ * Since: 0.10.22
+ */
+GstByteReader *
+gst_byte_reader_new_from_buffer (const GstBuffer * buffer)
+{
+  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+
+  return gst_byte_reader_new (GST_BUFFER_DATA (buffer),
+      GST_BUFFER_SIZE (buffer));
+}
+
+/**
+ * gst_byte_reader_free:
+ * @reader: a #GstByteReader instance
+ *
+ * Frees a #GstByteReader instance, which was previously allocated by
+ * gst_byte_reader_new() or gst_byte_reader_new_from_buffer().
+ * 
+ * Since: 0.10.22
+ */
+void
+gst_byte_reader_free (GstByteReader * reader)
+{
+  g_return_if_fail (reader != NULL);
+
+  g_slice_free (GstByteReader, reader);
+}
+
+/**
+ * gst_byte_reader_init:
+ * @reader: a #GstByteReader instance
+ * @data: Data from which the #GstByteReader should read
+ * @size: Size of @data in bytes
+ *
+ * Initializes a #GstByteReader instance to read from @data. This function
+ * can be called on already initialized instances.
+ * 
+ * Since: 0.10.22
+ */
+void
+gst_byte_reader_init (GstByteReader * reader, const guint8 * data, guint size)
+{
+  g_return_if_fail (reader != NULL);
+
+  reader->data = data;
+  reader->size = size;
+  reader->byte = 0;
+}
+
+/**
+ * gst_byte_reader_init:
+ * @reader: a #GstByteReader instance
+ * @buffer: Buffer from which the #GstByteReader should read
+ *
+ * Initializes a #GstByteReader instance to read from @buffer. This function
+ * can be called on already initialized instances.
+ * 
+ * Since: 0.10.22
+ */
+void
+gst_byte_reader_init_from_buffer (GstByteReader * reader,
+    const GstBuffer * buffer)
+{
+  g_return_if_fail (GST_IS_BUFFER (buffer));
+
+  gst_byte_reader_init (reader, GST_BUFFER_DATA (buffer),
+      GST_BUFFER_SIZE (buffer));
+}
+
+/**
+ * gst_byte_reader_set_pos:
+ * @reader: a #GstByteReader instance
+ * @pos: The new position in bytes
+ *
+ * Sets the new position of a #GstByteReader instance to @pos in bytes.
+ *
+ * Returns: %TRUE if the position could be set successfully, %FALSE
+ * otherwise.
+ * 
+ * Since: 0.10.22
+ */
+gboolean
+gst_byte_reader_set_pos (GstByteReader * reader, guint pos)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+
+  if (pos > reader->size)
+    return FALSE;
+
+  reader->byte = pos;
+
+  return TRUE;
+}
+
+/**
+ * gst_byte_reader_get_pos:
+ * @reader: a #GstByteReader instance
+ *
+ * Returns the current position of a #GstByteReader instance in bytes.
+ *
+ * Returns: The current position of @reader in bytes.
+ * 
+ * Since: 0.10.22
+ */
+guint
+gst_byte_reader_get_pos (const GstByteReader * reader)
+{
+  g_return_val_if_fail (reader != NULL, 0);
+
+  return reader->byte;
+}
+
+/**
+ * gst_byte_reader_get_remaining:
+ * @reader: a #GstByteReader instance
+ *
+ * Returns the remaining number of bytes of a #GstByteReader instance.
+ *
+ * Returns: The remaining number of bytes of @reader instance.
+ * 
+ * Since: 0.10.22
+ */
+guint
+gst_byte_reader_get_remaining (const GstByteReader * reader)
+{
+  g_return_val_if_fail (reader != NULL, 0);
+
+  return reader->size - reader->byte;
+}
+
+/**
+ * gst_byte_reader_skip:
+ * @reader: a #GstByteReader instance
+ * @nbytes: the number of bytes to skip
+ *
+ * Skips @nbytes bytes of the #GstByteReader instance.
+ *
+ * Returns: %TRUE if @nbytes bytes could be skipped, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+gboolean
+gst_byte_reader_skip (GstByteReader * reader, guint nbytes)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+
+  if (gst_byte_reader_get_remaining (reader) < nbytes)
+    return FALSE;
+
+  reader->byte += nbytes;
+
+  return TRUE;
+}
+
+/**
+ * gst_byte_reader_get_uint8:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint8 to store the result
+ *
+ * Read an unsigned 8 bit integer into @val and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int8:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint8 to store the result
+ *
+ * Read a signed 8 bit integer into @val and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint8:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint8 to store the result
+ *
+ * Read a signed 8 bit integer into @val but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int8:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint8 to store the result
+ *
+ * Read a signed 8 bit integer into @val but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_uint16_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint16 to store the result
+ *
+ * Read an unsigned 16 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int16_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint16 to store the result
+ *
+ * Read a signed 16 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint16_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint16 to store the result
+ *
+ * Read a signed 16 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int16_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint16 to store the result
+ *
+ * Read a signed 16 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_uint16_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint16 to store the result
+ *
+ * Read an unsigned 16 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int16_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint16 to store the result
+ *
+ * Read a signed 16 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint16_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint16 to store the result
+ *
+ * Read a signed 16 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int16_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint16 to store the result
+ *
+ * Read a signed 16 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_uint24_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read an unsigned 24 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int24_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 24 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint24_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read a signed 24 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int24_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 24 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_uint24_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read an unsigned 24 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int24_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 24 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint24_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read a signed 24 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int24_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 24 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+
+/**
+ * gst_byte_reader_get_uint32_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read an unsigned 32 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int32_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 32 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint32_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read a signed 32 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int32_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 32 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_uint32_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read an unsigned 32 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int32_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 32 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint32_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint32 to store the result
+ *
+ * Read a signed 32 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int32_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint32 to store the result
+ *
+ * Read a signed 32 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_uint64_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint64 to store the result
+ *
+ * Read an unsigned 64 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int64_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint64 to store the result
+ *
+ * Read a signed 64 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint64_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint64 to store the result
+ *
+ * Read a signed 64 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int64_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint64 to store the result
+ *
+ * Read a signed 64 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_uint64_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint64 to store the result
+ *
+ * Read an unsigned 64 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_int64_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint64 to store the result
+ *
+ * Read a signed 64 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_uint64_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #guint64 to store the result
+ *
+ * Read a signed 64 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_int64_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gint64 to store the result
+ *
+ * Read a signed 64 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+#define GST_BYTE_READER_READ_INTS(bits) \
+gboolean \
+gst_byte_reader_get_uint##bits##_le (GstByteReader *reader, guint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \
+  reader->byte += bits / 8; \
+  return TRUE; \
+} \
+\
+gboolean \
+gst_byte_reader_get_uint##bits##_be (GstByteReader *reader, guint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \
+  reader->byte += bits / 8; \
+  return TRUE; \
+} \
+\
+gboolean \
+gst_byte_reader_get_int##bits##_le (GstByteReader *reader, gint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \
+  reader->byte += bits / 8; \
+  return TRUE; \
+} \
+\
+gboolean \
+gst_byte_reader_get_int##bits##_be (GstByteReader *reader, gint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \
+  reader->byte += bits / 8; \
+  return TRUE; \
+} \
+gboolean \
+gst_byte_reader_peek_uint##bits##_le (GstByteReader *reader, guint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \
+  return TRUE; \
+} \
+\
+gboolean \
+gst_byte_reader_peek_uint##bits##_be (GstByteReader *reader, guint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \
+  return TRUE; \
+} \
+\
+gboolean \
+gst_byte_reader_peek_int##bits##_le (GstByteReader *reader, gint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \
+  return TRUE; \
+} \
+\
+gboolean \
+gst_byte_reader_peek_int##bits##_be (GstByteReader *reader, gint##bits *val) \
+{ \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \
+  return TRUE; \
+}
+
+
+GST_BYTE_READER_READ_INTS (16);
+GST_BYTE_READER_READ_INTS (32);
+GST_BYTE_READER_READ_INTS (64);
+
+gboolean
+gst_byte_reader_get_uint8 (GstByteReader * reader, guint8 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 1 > reader->size)
+    return FALSE;
+
+  *val = GST_READ_UINT8 (&reader->data[reader->byte]);
+  reader->byte++;
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_get_int8 (GstByteReader * reader, gint8 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 1 > reader->size)
+    return FALSE;
+
+  *val = GST_READ_UINT8 (&reader->data[reader->byte]);
+  reader->byte++;
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_peek_uint8 (GstByteReader * reader, guint8 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 1 > reader->size)
+    return FALSE;
+
+  *val = GST_READ_UINT8 (&reader->data[reader->byte]);
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_peek_int8 (GstByteReader * reader, gint8 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 1 > reader->size)
+    return FALSE;
+
+  *val = GST_READ_UINT8 (&reader->data[reader->byte]);
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_get_uint24_le (GstByteReader * reader, guint32 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  *val =
+      (reader->data[reader->byte] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte + 2] << 16));
+  reader->byte += 3;
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_get_uint24_be (GstByteReader * reader, guint32 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  *val =
+      (reader->data[reader->byte + 2] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte] << 16));
+  reader->byte += 3;
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_get_int24_le (GstByteReader * reader, gint32 * val)
+{
+  guint32 ret;
+
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  ret =
+      (reader->data[reader->byte] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte + 2] << 16));
+
+  if (ret & 0x00800000)
+    ret |= 0xff000000;
+
+  reader->byte += 3;
+
+  *val = ret;
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_get_int24_be (GstByteReader * reader, gint32 * val)
+{
+  guint32 ret;
+
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  ret =
+      (reader->data[reader->byte + 2] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte] << 16));
+  if (ret & 0x00800000)
+    ret |= 0xff000000;
+
+  reader->byte += 3;
+
+  *val = ret;
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_peek_uint24_le (GstByteReader * reader, guint32 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  *val =
+      (reader->data[reader->byte] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte + 2] << 16));
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_peek_uint24_be (GstByteReader * reader, guint32 * val)
+{
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  *val =
+      (reader->data[reader->byte + 2] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte] << 16));
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_peek_int24_le (GstByteReader * reader, gint32 * val)
+{
+  guint32 ret;
+
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  ret =
+      (reader->data[reader->byte] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte + 2] << 16));
+
+  if (ret & 0x00800000)
+    ret |= 0xff000000;
+
+  *val = ret;
+  return TRUE;
+}
+
+gboolean
+gst_byte_reader_peek_int24_be (GstByteReader * reader, gint32 * val)
+{
+  guint32 ret;
+
+  g_return_val_if_fail (reader != NULL, FALSE);
+  g_return_val_if_fail (val != NULL, FALSE);
+
+  if (reader->byte + 3 > reader->size)
+    return FALSE;
+
+  ret =
+      (reader->data[reader->byte + 2] | (reader->data[reader->byte +
+              1] << 8) | (reader->data[reader->byte] << 16));
+  if (ret & 0x00800000)
+    ret |= 0xff000000;
+
+  *val = ret;
+  return TRUE;
+}
+
+/**
+ * gst_byte_reader_get_float32_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gfloat to store the result
+ *
+ * Read a 32 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_float32_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gfloat to store the result
+ *
+ * Read a 32 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_float32_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gfloat to store the result
+ *
+ * Read a 32 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_float32_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gfloat to store the result
+ *
+ * Read a 32 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_float64_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gdouble to store the result
+ *
+ * Read a 64 bit little endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_float64_le:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gdouble to store the result
+ *
+ * Read a 64 bit little endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_get_float64_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gdouble to store the result
+ *
+ * Read a 64 bit big endian integer into @val
+ * and update the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+/**
+ * gst_byte_reader_peek_float64_be:
+ * @reader: a #GstByteReader instance
+ * @val: Pointer to a #gdouble to store the result
+ *
+ * Read a 64 bit big endian integer into @val
+ * but keep the current position.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ * 
+ * Since: 0.10.22
+ */
+
+#define GST_BYTE_READER_READ_FLOATS(bits, type, TYPE) \
+gboolean \
+gst_byte_reader_get_float##bits##_le (GstByteReader *reader, g##type *val) \
+{ \
+  g##type ret; \
+  \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  memcpy (&ret, &reader->data[reader->byte], bits / 8); \
+  *val = G##TYPE##_FROM_LE (ret); \
+  reader->byte += bits / 8; \
+  return TRUE; \
+} \
+gboolean \
+gst_byte_reader_get_float##bits##_be (GstByteReader *reader, g##type *val) \
+{ \
+  g##type ret; \
+  \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  memcpy (&ret, &reader->data[reader->byte], bits / 8); \
+  *val = G##TYPE##_FROM_BE (ret); \
+  reader->byte += bits / 8; \
+  return TRUE; \
+} \
+gboolean \
+gst_byte_reader_peek_float##bits##_le (GstByteReader *reader, g##type *val) \
+{ \
+  g##type ret; \
+  \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  memcpy (&ret, &reader->data[reader->byte], bits / 8); \
+  *val = G##TYPE##_FROM_LE (ret); \
+  return TRUE; \
+} \
+gboolean \
+gst_byte_reader_peek_float##bits##_be (GstByteReader *reader, g##type *val) \
+{ \
+  g##type ret; \
+  \
+  g_return_val_if_fail (reader != NULL, FALSE); \
+  g_return_val_if_fail (val != NULL, FALSE); \
+  \
+  if (reader->byte + bits / 8 > reader->size) \
+    return FALSE; \
+  \
+  memcpy (&ret, &reader->data[reader->byte], bits / 8); \
+  *val = G##TYPE##_FROM_BE (ret); \
+  return TRUE; \
+}
+
+GST_BYTE_READER_READ_FLOATS (32, float, FLOAT);
+GST_BYTE_READER_READ_FLOATS (64, double, DOUBLE);
diff --git a/gst/flacparse/gstbytereader.h b/gst/flacparse/gstbytereader.h
new file mode 100644 (file)
index 0000000..28534c0
--- /dev/null
@@ -0,0 +1,134 @@
+/* GStreamer
+ *
+ * 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.
+ */
+
+#ifndef __GST_BYTE_READER_H__
+#define __GST_BYTE_READER_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstByteReader:
+ * @data: Data from which the bit reader will read
+ * @size: Size of @data in bytes
+ * @byte: Current byte position
+ *
+ * A byte reader instance.
+ */
+typedef struct {
+  const guint8 *data;
+  guint size;
+
+  guint byte;  /* Byte position */
+} GstByteReader;
+
+GstByteReader * gst_byte_reader_new (const guint8 *data, guint size);
+GstByteReader * gst_byte_reader_new_from_buffer (const GstBuffer *buffer);
+void gst_byte_reader_free (GstByteReader *reader);
+
+void gst_byte_reader_init (GstByteReader *reader, const guint8 *data, guint size);
+void gst_byte_reader_init_from_buffer (GstByteReader *reader, const GstBuffer *buffer);
+
+gboolean gst_byte_reader_set_pos (GstByteReader *reader, guint pos);
+
+guint gst_byte_reader_get_pos (const GstByteReader *reader);
+guint gst_byte_reader_get_remaining (const GstByteReader *reader);
+
+gboolean gst_byte_reader_skip (GstByteReader *reader, guint nbytes);
+
+gboolean gst_byte_reader_get_uint8 (GstByteReader *reader, guint8 *val);
+gboolean gst_byte_reader_get_int8 (GstByteReader *reader, gint8 *val);
+gboolean gst_byte_reader_get_uint16_le (GstByteReader *reader, guint16 *val);
+gboolean gst_byte_reader_get_int16_le (GstByteReader *reader, gint16 *val);
+gboolean gst_byte_reader_get_uint16_be (GstByteReader *reader, guint16 *val);
+gboolean gst_byte_reader_get_int16_be (GstByteReader *reader, gint16 *val);
+gboolean gst_byte_reader_get_uint24_le (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_get_int24_le (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_get_uint24_be (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_get_int24_be (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_get_uint32_le (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_get_int32_le (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_get_uint32_be (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_get_int32_be (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_get_uint64_le (GstByteReader *reader, guint64 *val);
+gboolean gst_byte_reader_get_int64_le (GstByteReader *reader, gint64 *val);
+gboolean gst_byte_reader_get_uint64_be (GstByteReader *reader, guint64 *val);
+gboolean gst_byte_reader_get_int64_be (GstByteReader *reader, gint64 *val);
+
+gboolean gst_byte_reader_peek_uint8 (GstByteReader *reader, guint8 *val);
+gboolean gst_byte_reader_peek_int8 (GstByteReader *reader, gint8 *val);
+gboolean gst_byte_reader_peek_uint16_le (GstByteReader *reader, guint16 *val);
+gboolean gst_byte_reader_peek_int16_le (GstByteReader *reader, gint16 *val);
+gboolean gst_byte_reader_peek_uint16_be (GstByteReader *reader, guint16 *val);
+gboolean gst_byte_reader_peek_int16_be (GstByteReader *reader, gint16 *val);
+gboolean gst_byte_reader_peek_uint24_le (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_peek_int24_le (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_peek_uint24_be (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_peek_int24_be (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_peek_uint32_le (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_peek_int32_le (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_peek_uint32_be (GstByteReader *reader, guint32 *val);
+gboolean gst_byte_reader_peek_int32_be (GstByteReader *reader, gint32 *val);
+gboolean gst_byte_reader_peek_uint64_le (GstByteReader *reader, guint64 *val);
+gboolean gst_byte_reader_peek_int64_le (GstByteReader *reader, gint64 *val);
+gboolean gst_byte_reader_peek_uint64_be (GstByteReader *reader, guint64 *val);
+gboolean gst_byte_reader_peek_int64_be (GstByteReader *reader, gint64 *val);
+
+gboolean gst_byte_reader_get_float32_le (GstByteReader *reader, gfloat *val);
+gboolean gst_byte_reader_get_float32_be (GstByteReader *reader, gfloat *val);
+gboolean gst_byte_reader_get_float64_le (GstByteReader *reader, gdouble *val);
+gboolean gst_byte_reader_get_float64_be (GstByteReader *reader, gdouble *val);
+
+gboolean gst_byte_reader_peek_float32_le (GstByteReader *reader, gfloat *val);
+gboolean gst_byte_reader_peek_float32_be (GstByteReader *reader, gfloat *val);
+gboolean gst_byte_reader_peek_float64_le (GstByteReader *reader, gdouble *val);
+gboolean gst_byte_reader_peek_float64_be (GstByteReader *reader, gdouble *val);
+
+/**
+ * GST_BYTE_READER_INIT:
+ * @data: Data from which the #GstByteReader should read
+ * @size: Size of @data in bytes
+ *
+ * A #GstByteReader must be initialized with this macro, before it can be
+ * used. This macro can used be to initialize a variable, but it cannot
+ * be assigned to a variable. In that case you have to use
+ * gst_byte_reader_init().
+ *
+ * Since: 0.10.22
+ */
+#define GST_BYTE_READER_INIT(data, size) {data, size, 0}
+
+/**
+ * GST_BYTE_READER_INIT_FROM_BUFFER:
+ * @buffer: Buffer from which the #GstByteReader should read
+ *
+ * A #GstByteReader must be initialized with this macro, before it can be
+ * used. This macro can used be to initialize a variable, but it cannot
+ * be assigned to a variable. In that case you have to use
+ * gst_byte_reader_init().
+ *
+ * Since: 0.10.22
+ */
+#define GST_BYTE_READER_INIT_FROM_BUFFER(buffer) {GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 0}
+
+G_END_DECLS
+
+#endif /* __GST_BYTE_READER_H__ */
diff --git a/gst/flacparse/gstflac.c b/gst/flacparse/gstflac.c
new file mode 100644 (file)
index 0000000..862426f
--- /dev/null
@@ -0,0 +1,52 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstflacparse.h"
+
+#include <gst/tag/tag.h>
+#include <gst/gst-i18n-plugin.h>
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+#if ENABLE_NLS
+  GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
+      LOCALEDIR);
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+  if (!gst_element_register (plugin, "flacparse", GST_RANK_NONE,
+          gst_flac_parse_get_type ()))
+    return FALSE;
+
+  gst_tag_register_musicbrainz_tags ();
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "flacparse",
+    "The FLAC Lossless compressor Codec parser",
+    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/flacparse/gstflacparse.c b/gst/flacparse/gstflacparse.c
new file mode 100644 (file)
index 0000000..f25e34b
--- /dev/null
@@ -0,0 +1,1233 @@
+/* GStreamer
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstflacparse.h"
+
+#include <string.h>
+#include <gst/tag/tag.h>
+#include <gst/audio/audio.h>
+
+#include "gstbitreader.h"
+#include "gstbytereader.h"
+
+GST_DEBUG_CATEGORY_STATIC (flacparse_debug);
+#define GST_CAT_DEFAULT flacparse_debug
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-flac, "
+        "channels = (int) [ 1, 8 ], " "rate = (int) [ 1, 655350 ]")
+    );
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-flac")
+    );
+
+static void gst_flac_parse_finalize (GObject * object);
+
+static gboolean gst_flac_parse_start (GstBaseParse * parse);
+static gboolean gst_flac_parse_stop (GstBaseParse * parse);
+static gboolean gst_flac_parse_check_valid_frame (GstBaseParse * parse,
+    GstBuffer * buffer, guint * framesize, gint * skipsize);
+static GstFlowReturn gst_flac_parse_parse_frame (GstBaseParse * parse,
+    GstBuffer * buffer);
+
+GST_BOILERPLATE (GstFlacParse, gst_flac_parse, GstBaseParse,
+    GST_TYPE_BASE_PARSE);
+
+static void
+gst_flac_parse_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_factory));
+
+  gst_element_class_set_details_simple (element_class, "FLAC audio parser",
+      "Codec/Parser/Audio",
+      "Parses audio with the FLAC lossless audio codec",
+      "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
+
+  GST_DEBUG_CATEGORY_INIT (flacparse_debug, "flacparse", 0,
+      "Flac parser element");
+}
+
+static void
+gst_flac_parse_class_init (GstFlacParseClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstBaseParseClass *baseparse_class = GST_BASE_PARSE_CLASS (klass);
+
+  gobject_class->finalize = gst_flac_parse_finalize;
+
+  baseparse_class->start = GST_DEBUG_FUNCPTR (gst_flac_parse_start);
+  baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_flac_parse_stop);
+  baseparse_class->check_valid_frame =
+      GST_DEBUG_FUNCPTR (gst_flac_parse_check_valid_frame);
+  baseparse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_flac_parse_parse_frame);
+}
+
+static void
+gst_flac_parse_init (GstFlacParse * flacparse, GstFlacParseClass * klass)
+{
+}
+
+static void
+gst_flac_parse_finalize (GObject * object)
+{
+  GstFlacParse *flacparse = GST_FLAC_PARSE (object);
+
+  if (flacparse->tags) {
+    gst_tag_list_free (flacparse->tags);
+    flacparse->tags = NULL;
+  }
+
+
+  g_list_foreach (flacparse->headers, (GFunc) gst_mini_object_unref, NULL);
+  g_list_free (flacparse->headers);
+  flacparse->headers = NULL;
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_flac_parse_start (GstBaseParse * parse)
+{
+  GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
+
+  flacparse->state = GST_FLAC_PARSE_STATE_INIT;
+  flacparse->min_blocksize = 0;
+  flacparse->max_blocksize = 0;
+  flacparse->min_framesize = 0;
+  flacparse->max_framesize = 0;
+
+  flacparse->upstream_length = -1;
+
+  flacparse->samplerate = 0;
+  flacparse->channels = 0;
+  flacparse->bps = 0;
+  flacparse->total_samples = 0;
+
+  flacparse->requested_frame_size = 0;
+  flacparse->offset = GST_CLOCK_TIME_NONE;
+  flacparse->blocking_strategy = 0;
+  flacparse->block_size = 0;
+  flacparse->sample_number = 0;
+
+  /* "fLaC" marker */
+  gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 4);
+
+  return TRUE;
+}
+
+static gboolean
+gst_flac_parse_stop (GstBaseParse * parse)
+{
+  GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
+
+  if (flacparse->tags) {
+    gst_tag_list_free (flacparse->tags);
+    flacparse->tags = NULL;
+  }
+
+  g_list_foreach (flacparse->headers, (GFunc) gst_mini_object_unref, NULL);
+  g_list_free (flacparse->headers);
+  flacparse->headers = NULL;
+
+  return TRUE;
+}
+
+static gint
+gst_flac_parse_get_frame_size (GstFlacParse * flacparse, GstBuffer * buffer,
+    guint * framesize_ret)
+{
+  GstBitReader reader = GST_BIT_READER_INIT_FROM_BUFFER (buffer);
+  guint16 samplerate;
+  guint8 tmp;
+  gint i;
+  guint8 channel_assignment = 0;
+
+  /* Skip 14 bit sync code */
+  if (!gst_bit_reader_skip (&reader, 14))
+    goto need_more_data;
+
+  /* Must be 0 */
+  if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1))
+    goto need_more_data;
+  else if (tmp != 0)
+    goto error;
+
+  /* 0 == fixed block size, 1 == variable block size */
+  if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->blocking_strategy,
+          1))
+    goto need_more_data;
+
+  /* block size index, calculation of the real blocksize below */
+  if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->block_size, 4))
+    goto need_more_data;
+  else if (flacparse->block_size == 0)
+    goto error;
+
+  /* sample rate index, calculation of the real samplerate below */
+  if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 4))
+    goto need_more_data;
+  else if (samplerate == 0x0f)
+    goto error;
+
+  /* channel assignment */
+  if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 4)) {
+    goto need_more_data;
+  } else if (tmp < 8) {
+    if (flacparse->channels && tmp + 1 != flacparse->channels)
+      goto error;
+    else
+      flacparse->channels = tmp + 1;
+  } else if (tmp <= 10 && flacparse->channels != 2) {
+    if (flacparse->channels && 2 != flacparse->channels)
+      goto error;
+    else
+      flacparse->channels = 2;
+    if (tmp == 8)
+      channel_assignment = 1;   /* left-side */
+    else if (tmp == 9)
+      channel_assignment = 2;   /* right-side */
+    else
+      channel_assignment = 3;   /* mid-side */
+  } else if (tmp > 10) {
+    goto error;
+  }
+
+  /* bits per sample */
+  if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 3)) {
+    goto need_more_data;
+  } else if (tmp == 0x03 || tmp == 0x07) {
+    goto error;
+  } else if (tmp == 0 && flacparse->bps == 0) {
+    goto need_streaminfo;
+  } else if (tmp == 0x01 && flacparse->bps != 8) {
+    if (flacparse->bps && flacparse->bps != 8)
+      goto error;
+    else
+      flacparse->bps = 8;
+  } else if (tmp == 0x02 && flacparse->bps != 12) {
+    if (flacparse->bps && flacparse->bps != 12)
+      goto error;
+    else
+      flacparse->bps = 12;
+  } else if (tmp == 0x04 && flacparse->bps != 16) {
+    if (flacparse->bps && flacparse->bps != 16)
+      goto error;
+    else
+      flacparse->bps = 16;
+  } else if (tmp == 0x05 && flacparse->bps != 20) {
+    if (flacparse->bps && flacparse->bps != 20)
+      goto error;
+    else
+      flacparse->bps = 20;
+  } else if (tmp == 0x06 && flacparse->bps != 24) {
+    if (flacparse->bps && flacparse->bps != 24)
+      goto error;
+    else
+      flacparse->bps = 24;
+  }
+
+  /* reserved, must be 0 */
+  if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1))
+    goto need_more_data;
+  else if (tmp != 0)
+    goto error;
+
+  /* read "utf8" encoded sample/frame number */
+  {
+    guint len = 0;
+
+    tmp = 1;
+    while (tmp != 0) {
+      if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1))
+        goto need_more_data;
+      else if (tmp == 1)
+        len++;
+    }
+    if (len == 1)
+      goto error;
+
+    flacparse->sample_number = 0;
+    if (len == 0) {
+      if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 7))
+        goto need_more_data;
+      flacparse->sample_number = tmp;
+    } else if ((flacparse->blocking_strategy == 0 && len > 6) ||
+        (flacparse->blocking_strategy == 1 && len > 7)) {
+      goto error;
+    } else {
+      if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 8 - len - 1))
+        goto need_more_data;
+
+      flacparse->sample_number = tmp;
+      len -= 1;
+
+      while (len > 0) {
+        if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 2))
+          goto need_more_data;
+        else if (tmp != 0x02)
+          goto error;
+
+        if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 6))
+          goto need_more_data;
+        flacparse->sample_number <<= 6;
+        flacparse->sample_number |= tmp;
+        len--;
+      }
+    }
+  }
+
+  /* calculate real blocksize from the blocksize index */
+  if (flacparse->block_size == 1)
+    flacparse->block_size = 192;
+  else if (flacparse->block_size <= 5)
+    flacparse->block_size = 576 * (1 << (flacparse->block_size - 2));
+  else if (flacparse->block_size <= 15)
+    flacparse->block_size = 256 * (1 << (flacparse->block_size - 8));
+  else if (flacparse->block_size == 6) {
+    if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->block_size, 8))
+      goto need_more_data;
+    flacparse->block_size++;
+  } else if (flacparse->block_size == 7) {
+    if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->block_size, 16))
+      goto need_more_data;
+    flacparse->block_size++;
+  }
+
+  /* calculate the real samplerate from the samplerate index */
+  if (samplerate == 0 && flacparse->samplerate == 0) {
+    goto need_streaminfo;
+  } else if (samplerate == 1) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 88200;
+    else if (flacparse->samplerate != 88200)
+      goto error;
+  } else if (samplerate == 2) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 176400;
+    else if (flacparse->samplerate != 176400)
+      goto error;
+  } else if (samplerate == 3) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 192000;
+    else if (flacparse->samplerate != 192000)
+      goto error;
+  } else if (samplerate == 4) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 8000;
+    else if (flacparse->samplerate != 8000)
+      goto error;
+  } else if (samplerate == 5) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 16000;
+    else if (flacparse->samplerate != 16000)
+      goto error;
+  } else if (samplerate == 6) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 22050;
+    else if (flacparse->samplerate != 22050)
+      goto error;
+  } else if (samplerate == 7) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 24000;
+    else if (flacparse->samplerate != 24000)
+      goto error;
+  } else if (samplerate == 8) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 32000;
+    else if (flacparse->samplerate != 32000)
+      goto error;
+  } else if (samplerate == 9) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 44100;
+    else if (flacparse->samplerate != 44100)
+      goto error;
+  } else if (samplerate == 10) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 48000;
+    else if (flacparse->samplerate != 48000)
+      goto error;
+  } else if (samplerate == 11) {
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = 96000;
+    else if (flacparse->samplerate != 96000)
+      goto error;
+  } else if (samplerate == 12) {
+    if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 8))
+      goto need_more_data;
+    samplerate *= 1000;
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = samplerate;
+    else if (flacparse->samplerate != samplerate)
+      goto error;
+  } else if (samplerate == 13) {
+    if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 16))
+      goto need_more_data;
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = samplerate;
+    else if (flacparse->samplerate != samplerate)
+      goto error;
+  } else if (samplerate == 14) {
+    if (!gst_bit_reader_get_bits_uint16 (&reader, &samplerate, 16))
+      goto need_more_data;
+    samplerate *= 10;
+    if (flacparse->samplerate == 0)
+      flacparse->samplerate = samplerate;
+    else if (flacparse->samplerate != samplerate)
+      goto error;
+  }
+
+  /* skip crc-8 for the header */
+  if (!gst_bit_reader_skip (&reader, 8))
+    goto need_more_data;
+
+  /* parse subframes, one subframe per channel */
+  for (i = 0; i < flacparse->channels; i++) {
+    guint8 sf_type;
+    guint8 cur_bps;
+
+    cur_bps = flacparse->bps;
+
+    /* for mid/side, left/side, right/side the "difference" channel
+     * needs and additional bit */
+    if (i == 0 && channel_assignment == 2)
+      cur_bps++;
+    else if (i == 1 && (channel_assignment == 1 || channel_assignment == 3))
+      cur_bps++;
+
+    /* must be 0 */
+    if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1))
+      goto need_more_data;
+    else if (tmp != 0)
+      goto error;
+
+    /* sub frame type */
+    if (!gst_bit_reader_get_bits_uint8 (&reader, &sf_type, 6))
+      goto need_more_data;
+    else if (((sf_type & 0xfe) == 0x02) ||
+        ((sf_type & 0xfc) == 0x04) ||
+        ((sf_type & 0xf8) == 0x08 && (sf_type & 0x07) > 4) ||
+        ((sf_type & 0xf0) == 0x10))
+      goto error;
+
+    /* wasted bits per sample, if 1 the value follows unary coded */
+    if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1)) {
+      goto need_more_data;
+    } else if (tmp != 0) {
+      guint wasted = 1;
+
+      tmp = 0;
+      while (tmp == 0) {
+        if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1))
+          goto need_more_data;
+        else
+          wasted++;
+      }
+      cur_bps -= wasted;
+    }
+
+    /* subframe type: constant */
+    if (sf_type == 0x00) {
+      if (!gst_bit_reader_skip (&reader, cur_bps))
+        goto need_more_data;
+      /* subframe type: verbatim */
+    } else if (sf_type == 0x01) {
+      if (!gst_bit_reader_skip (&reader, cur_bps * flacparse->block_size))
+        goto need_more_data;
+      /* subframe type: LPC or fixed */
+    } else {
+      guint8 residual_type;
+      guint order = 0;
+      guint16 partition_order;
+      guint j;
+
+      /* Skip warm-up samples for fixed subframe and calculate order */
+      if ((sf_type & 0xf8) == 0x08) {
+        order = sf_type & 0x07;
+
+        g_assert (order <= 4);
+
+        if (!gst_bit_reader_skip (&reader, cur_bps * order))
+          goto need_more_data;
+        /* Skip warm-up samples for LPC subframe, get parameters and calculate order */
+      } else if ((sf_type & 0xe0) == 0x20) {
+        guint8 prec;
+
+        order = (sf_type & 0x1f) + 1;
+
+        /* warm-up samples */
+        if (!gst_bit_reader_skip (&reader, cur_bps * order))
+          goto need_more_data;
+
+        /* LPC coefficient precision */
+        if (!gst_bit_reader_get_bits_uint8 (&reader, &prec, 4))
+          goto need_more_data;
+        else if (prec == 0x0f)
+          goto error;
+        prec++;
+
+        /* LPC coefficient shift */
+        if (!gst_bit_reader_skip (&reader, 5))
+          goto need_more_data;
+
+        /* LPC coefficients */
+        if (!gst_bit_reader_skip (&reader, order * prec))
+          goto need_more_data;
+      } else {
+        g_assert_not_reached ();
+      }
+
+      /* residual type: 0 == rice, 1 == rice2 */
+      if (!gst_bit_reader_get_bits_uint8 (&reader, &residual_type, 2))
+        goto need_more_data;
+
+      if (residual_type & 0x02)
+        goto error;
+
+      /* partition order */
+      if (!gst_bit_reader_get_bits_uint16 (&reader, &partition_order, 4))
+        goto need_more_data;
+
+      partition_order = 1 << partition_order;
+
+      /* 2^partition_order partitions */
+      for (j = 0; j < partition_order; j++) {
+        guint samples;
+        guint8 rice_parameter;
+
+        /* calculate number of samples for the current partition */
+        if (partition_order == 1) {
+          samples = flacparse->block_size - order;
+        } else if (j != 0) {
+          samples = flacparse->block_size / partition_order;
+        } else {
+          samples = flacparse->block_size / partition_order - order;
+        }
+
+        /* rice parameter */
+        if (!gst_bit_reader_get_bits_uint8 (&reader, &rice_parameter,
+                (residual_type == 0) ? 4 : 5))
+          goto need_more_data;
+
+        /* if rice parameter has all bits set the samples follow unencoded with the number of bits
+         * per sample in the following 5 bits */
+        if ((residual_type == 0 && rice_parameter == 0x0f)
+            || (residual_type == 1 && rice_parameter == 0x1f)) {
+          if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 5))
+            goto need_more_data;
+          if (!gst_bit_reader_skip (&reader, tmp * samples))
+            goto need_more_data;
+        } else {
+          guint k;
+
+          /* read the rice encoded samples */
+          for (k = 0; k < samples; k++) {
+            tmp = 0;
+            while (tmp == 0)
+              if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp, 1))
+                goto need_more_data;
+
+            if (!gst_bit_reader_skip (&reader, rice_parameter))
+              goto need_more_data;
+          }
+        }
+      }
+    }
+  }
+
+  /* zero padding to byte alignment */
+  gst_bit_reader_skip_to_byte (&reader);
+
+  /* Skip crc-16 for the complete frame */
+  if (!gst_bit_reader_skip (&reader, 16))
+    goto need_more_data;
+
+  *framesize_ret = gst_bit_reader_get_pos (&reader) / 8;
+
+  GST_DEBUG_OBJECT (flacparse, "Parsed frame at offset %" G_GUINT64_FORMAT ":\n"
+      "Frame size: %u\n"
+      "Block size: %u\n"
+      "Sample/Frame number: %" G_GUINT64_FORMAT,
+      flacparse->offset, *framesize_ret,
+      flacparse->block_size, flacparse->sample_number);
+
+  return 0;
+
+need_more_data:
+  {
+    gint64 upstream_len = flacparse->upstream_length;
+    GstFormat fmt;
+    gboolean first_try = TRUE;
+
+    GST_DEBUG_OBJECT (flacparse, "Need more data");
+
+  retry:
+
+    fmt = GST_FORMAT_BYTES;
+    if (upstream_len != -1 ||
+        (gst_pad_query_peer_duration (GST_BASE_PARSE_SINK_PAD (GST_BASE_PARSE
+                    (flacparse)), &fmt, &upstream_len)
+            && fmt == GST_FORMAT_BYTES)) {
+      flacparse->upstream_length = upstream_len;
+      upstream_len -= GST_BUFFER_OFFSET (buffer);
+
+      if (flacparse->max_framesize
+          && flacparse->max_framesize > flacparse->requested_frame_size
+          && flacparse->max_framesize <= upstream_len) {
+        flacparse->requested_frame_size = flacparse->max_framesize;
+      } else if (flacparse->requested_frame_size + 4096 <= upstream_len) {
+        flacparse->requested_frame_size += 4096;
+      } else if (flacparse->requested_frame_size <= upstream_len) {
+        flacparse->requested_frame_size = upstream_len;
+      } else {
+        if (first_try) {
+          upstream_len = -1;
+          first_try = FALSE;
+          goto retry;
+        } else {
+          goto eos;
+        }
+      }
+      GST_DEBUG_OBJECT (flacparse, "Requesting %u bytes",
+          flacparse->requested_frame_size);
+      return flacparse->requested_frame_size;
+    } else {
+      flacparse->requested_frame_size++;
+      GST_DEBUG_OBJECT (flacparse, "Requesting %u bytes",
+          flacparse->requested_frame_size);
+      return flacparse->requested_frame_size;
+    }
+  }
+
+need_streaminfo:
+  {
+    GST_ERROR_OBJECT (flacparse, "Need STREAMINFO");
+    return -2;
+  }
+
+error:
+  {
+    GST_WARNING_OBJECT (flacparse, "Invalid frame");
+    return -1;
+  }
+eos:
+  {
+    GST_WARNING_OBJECT (flacparse, "EOS");
+    return -1;
+  }
+}
+
+static gboolean
+gst_flac_parse_check_valid_frame (GstBaseParse * parse, GstBuffer * buffer,
+    guint * framesize, gint * skipsize)
+{
+  GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
+  const guint8 *data = GST_BUFFER_DATA (buffer);
+
+  if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) {
+    if (memcmp (GST_BUFFER_DATA (buffer), "fLaC", 4) == 0) {
+      GST_DEBUG_OBJECT (flacparse, "fLaC marker found");
+      *framesize = 4;
+      return TRUE;
+    } else if (data[0] == 0xff && (data[1] >> 2) == 0x3e) {
+      GST_DEBUG_OBJECT (flacparse, "Found headerless FLAC");
+      /* Minimal size of a frame header */
+      gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 16);
+      flacparse->requested_frame_size = 16;
+      flacparse->state = GST_FLAC_PARSE_STATE_GENERATE_HEADERS;
+      *skipsize = 0;
+      return FALSE;
+    } else {
+      GST_DEBUG_OBJECT (flacparse, "fLaC marker not found");
+      return FALSE;
+    }
+  } else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) {
+    guint size = 4 + ((data[1] << 16) | (data[2] << 8) | (data[3]));
+
+    GST_DEBUG_OBJECT (flacparse, "Found metadata block of size %u", size);
+    *framesize = size;
+    return TRUE;
+  } else {
+    if (data[0] == 0xff && (data[1] >> 2) == 0x3e) {
+      gint ret = 0;
+
+      flacparse->offset = GST_BUFFER_OFFSET (buffer);
+      flacparse->blocking_strategy = 0;
+      flacparse->block_size = 0;
+      flacparse->sample_number = 0;
+
+      GST_DEBUG_OBJECT (flacparse, "Found sync code");
+      ret = gst_flac_parse_get_frame_size (flacparse, buffer, framesize);
+      if (ret == 0) {
+        return TRUE;
+      } else if (ret == -1) {
+        return FALSE;
+      } else if (ret == -2) {
+        GST_ELEMENT_ERROR (flacparse, STREAM, FORMAT, (NULL),
+            ("Need STREAMINFO for parsing"));
+        return FALSE;
+      } else if (ret > 0) {
+        *skipsize = 0;
+        gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), ret);
+        flacparse->requested_frame_size = ret;
+        return FALSE;
+      }
+    } else {
+      GST_DEBUG_OBJECT (flacparse, "Sync code not found");
+      return FALSE;
+    }
+  }
+
+  return FALSE;
+}
+
+static gboolean
+gst_flac_parse_handle_streaminfo (GstFlacParse * flacparse, GstBuffer * buffer)
+{
+  GstBitReader reader = GST_BIT_READER_INIT_FROM_BUFFER (buffer);
+
+  if (GST_BUFFER_SIZE (buffer) != 4 + 34) {
+    GST_ERROR_OBJECT (flacparse, "Invalid metablock size for STREAMINFO: %u",
+        GST_BUFFER_SIZE (buffer));
+    return FALSE;
+  }
+
+  /* Skip metadata block header */
+  gst_bit_reader_skip (&reader, 32);
+
+  if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->min_blocksize, 16))
+    goto error;
+  if (flacparse->min_blocksize < 16) {
+    GST_ERROR_OBJECT (flacparse, "Invalid minimum block size: %u",
+        flacparse->min_blocksize);
+    return FALSE;
+  }
+
+  if (!gst_bit_reader_get_bits_uint16 (&reader, &flacparse->max_blocksize, 16))
+    goto error;
+  if (flacparse->max_blocksize < 16) {
+    GST_ERROR_OBJECT (flacparse, "Invalid maximum block size: %u",
+        flacparse->max_blocksize);
+    return FALSE;
+  }
+
+  if (!gst_bit_reader_get_bits_uint32 (&reader, &flacparse->min_framesize, 24))
+    goto error;
+  if (!gst_bit_reader_get_bits_uint32 (&reader, &flacparse->max_framesize, 24))
+    goto error;
+
+  if (!gst_bit_reader_get_bits_uint32 (&reader, &flacparse->samplerate, 20))
+    goto error;
+  if (flacparse->samplerate == 0) {
+    GST_ERROR_OBJECT (flacparse, "Invalid sample rate 0");
+    return FALSE;
+  }
+
+  if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->channels, 3))
+    goto error;
+  flacparse->channels++;
+  if (flacparse->channels > 8) {
+    GST_ERROR_OBJECT (flacparse, "Invalid number of channels %u",
+        flacparse->channels);
+    return FALSE;
+  }
+
+  if (!gst_bit_reader_get_bits_uint8 (&reader, &flacparse->bps, 5))
+    goto error;
+  flacparse->bps++;
+
+  if (!gst_bit_reader_get_bits_uint64 (&reader, &flacparse->total_samples, 36))
+    goto error;
+  if (flacparse->total_samples)
+    gst_base_parse_set_duration (GST_BASE_PARSE (flacparse), GST_FORMAT_TIME,
+        GST_FRAMES_TO_CLOCK_TIME (flacparse->total_samples,
+            flacparse->samplerate));
+
+  GST_DEBUG_OBJECT (flacparse, "STREAMINFO:\n"
+      "\tmin/max blocksize: %u/%u,\n"
+      "\tmin/max framesize: %u/%u,\n"
+      "\tsamplerate: %u,\n"
+      "\tchannels: %u,\n"
+      "\tbits per sample: %u,\n"
+      "\ttotal samples: %" G_GUINT64_FORMAT,
+      flacparse->min_blocksize, flacparse->max_blocksize,
+      flacparse->min_framesize, flacparse->max_framesize,
+      flacparse->samplerate,
+      flacparse->channels, flacparse->bps, flacparse->total_samples);
+
+  return TRUE;
+
+error:
+  GST_ERROR_OBJECT (flacparse, "Failed to read data");
+  return FALSE;
+}
+
+static gboolean
+gst_flac_parse_handle_vorbiscomment (GstFlacParse * flacparse,
+    GstBuffer * buffer)
+{
+  flacparse->tags = gst_tag_list_from_vorbiscomment_buffer (buffer,
+      GST_BUFFER_DATA (buffer), 4, NULL);
+
+  if (flacparse->tags == NULL) {
+    GST_ERROR_OBJECT (flacparse, "Invalid vorbiscomment block");
+  } else if (gst_tag_list_is_empty (flacparse->tags)) {
+    gst_tag_list_free (flacparse->tags);
+    flacparse->tags = NULL;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_flac_parse_handle_picture (GstFlacParse * flacparse, GstBuffer * buffer)
+{
+  GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer);
+  const guint8 *data = GST_BUFFER_DATA (buffer);
+  guint32 img_len, img_type;
+  guint32 img_mimetype_len, img_description_len;
+
+  if (!gst_byte_reader_get_uint32_be (&reader, &img_type))
+    goto error;
+
+  if (!gst_byte_reader_get_uint32_be (&reader, &img_mimetype_len))
+    goto error;
+  if (!gst_byte_reader_skip (&reader, img_mimetype_len))
+    goto error;
+
+  if (!gst_byte_reader_get_uint32_be (&reader, &img_description_len))
+    goto error;
+  if (!gst_byte_reader_skip (&reader, img_description_len))
+    goto error;
+
+  if (!gst_byte_reader_skip (&reader, 4 * 4))
+    goto error;
+
+  if (!gst_byte_reader_get_uint32_be (&reader, &img_len))
+    goto error;
+
+  if (!flacparse->tags)
+    flacparse->tags = gst_tag_list_new ();
+
+  gst_tag_list_add_id3_image (flacparse->tags,
+      data + gst_byte_reader_get_pos (&reader), img_len, img_type);
+
+  if (gst_tag_list_is_empty (flacparse->tags)) {
+    gst_tag_list_free (flacparse->tags);
+    flacparse->tags = NULL;
+  }
+
+  return TRUE;
+
+error:
+  GST_ERROR_OBJECT (flacparse, "Error reading data");
+  return FALSE;
+}
+
+static void
+_value_array_append_buffer (GValue * array_val, GstBuffer * buf)
+{
+  GValue value = { 0, };
+
+  g_value_init (&value, GST_TYPE_BUFFER);
+  /* copy buffer to avoid problems with circular refcounts */
+  buf = gst_buffer_copy (buf);
+  /* again, for good measure */
+  GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
+  gst_value_set_buffer (&value, buf);
+  gst_buffer_unref (buf);
+  gst_value_array_append_value (array_val, &value);
+  g_value_unset (&value);
+}
+
+static gboolean
+gst_flac_parse_handle_headers (GstFlacParse * flacparse)
+{
+  GstBuffer *vorbiscomment = NULL;
+  GstBuffer *streaminfo = NULL;
+  GstBuffer *marker = NULL;
+  GValue array = { 0, };
+  GstCaps *caps;
+  GList *l;
+
+  caps = gst_caps_new_simple ("audio/x-flac",
+      "channels", G_TYPE_INT, flacparse->channels,
+      "rate", G_TYPE_INT, flacparse->samplerate, NULL);
+
+  if (!flacparse->headers)
+    goto push_headers;
+
+  for (l = flacparse->headers; l; l = l->next) {
+    GstBuffer *header = l->data;
+    const guint8 *data = GST_BUFFER_DATA (header);
+    guint size = GST_BUFFER_SIZE (header);
+
+    GST_BUFFER_FLAG_SET (header, GST_BUFFER_FLAG_IN_CAPS);
+
+    if (size == 4 && memcmp (data, "fLaC", 4) == 0) {
+      marker = header;
+    } else if (size > 1 && (data[0] & 0x7f) == 0) {
+      streaminfo = header;
+    } else if (size > 1 && (data[0] & 0x7f) == 4) {
+      vorbiscomment = header;
+    }
+  }
+
+  if (marker == NULL || streaminfo == NULL || vorbiscomment == NULL) {
+    GST_WARNING_OBJECT (flacparse,
+        "missing header %p %p %p, muxing into container "
+        "formats may be broken", marker, streaminfo, vorbiscomment);
+    goto push_headers;
+  }
+
+  g_value_init (&array, GST_TYPE_ARRAY);
+
+  /* add marker including STREAMINFO header */
+  {
+    GstBuffer *buf;
+    guint16 num;
+
+    /* minus one for the marker that is merged with streaminfo here */
+    num = g_list_length (flacparse->headers) - 1;
+
+    buf = gst_buffer_new_and_alloc (13 + GST_BUFFER_SIZE (streaminfo));
+    GST_BUFFER_DATA (buf)[0] = 0x7f;
+    memcpy (GST_BUFFER_DATA (buf) + 1, "FLAC", 4);
+    GST_BUFFER_DATA (buf)[5] = 0x01;    /* mapping version major */
+    GST_BUFFER_DATA (buf)[6] = 0x00;    /* mapping version minor */
+    GST_BUFFER_DATA (buf)[7] = (num & 0xFF00) >> 8;
+    GST_BUFFER_DATA (buf)[8] = (num & 0x00FF) >> 0;
+    memcpy (GST_BUFFER_DATA (buf) + 9, "fLaC", 4);
+    memcpy (GST_BUFFER_DATA (buf) + 13, GST_BUFFER_DATA (streaminfo),
+        GST_BUFFER_SIZE (streaminfo));
+    _value_array_append_buffer (&array, buf);
+    gst_buffer_unref (buf);
+  }
+
+  /* add VORBISCOMMENT header */
+  _value_array_append_buffer (&array, vorbiscomment);
+
+  /* add other headers, if there are any */
+  for (l = flacparse->headers; l; l = l->next) {
+    if (GST_BUFFER_CAST (l->data) != marker &&
+        GST_BUFFER_CAST (l->data) != streaminfo &&
+        GST_BUFFER_CAST (l->data) != vorbiscomment) {
+      _value_array_append_buffer (&array, GST_BUFFER_CAST (l->data));
+    }
+  }
+
+  gst_structure_set_value (gst_caps_get_structure (caps, 0),
+      "streamheader", &array);
+  g_value_unset (&array);
+
+push_headers:
+
+  gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (GST_BASE_PARSE (flacparse)), caps);
+  gst_caps_unref (caps);
+
+  /* push header buffers; update caps, so when we push the first buffer the
+   * negotiated caps will change to caps that include the streamheader field */
+  for (l = flacparse->headers; l != NULL; l = l->next) {
+    GstBuffer *buf = GST_BUFFER (l->data);
+    GstFlowReturn ret;
+
+    l->data = NULL;
+    gst_buffer_set_caps (buf,
+        GST_PAD_CAPS (GST_BASE_PARSE_SRC_PAD (GST_BASE_PARSE (flacparse))));
+
+    ret = gst_base_parse_push_buffer (GST_BASE_PARSE (flacparse), buf);
+    if (ret != GST_FLOW_OK)
+      return FALSE;
+  }
+  g_list_free (flacparse->headers);
+  flacparse->headers = NULL;
+
+  /* Push tags */
+  if (flacparse->tags)
+    gst_element_found_tags (GST_ELEMENT (flacparse),
+        gst_tag_list_copy (flacparse->tags));
+
+  return TRUE;
+}
+
+static gboolean
+gst_flac_parse_generate_headers (GstFlacParse * flacparse)
+{
+  GstBuffer *marker, *streaminfo, *vorbiscomment;
+  guint8 *data;
+
+  marker = gst_buffer_new_and_alloc (4);
+  memcpy (GST_BUFFER_DATA (marker), "fLaC", 4);
+  GST_BUFFER_TIMESTAMP (marker) = GST_CLOCK_TIME_NONE;
+  GST_BUFFER_DURATION (marker) = GST_CLOCK_TIME_NONE;
+  GST_BUFFER_OFFSET (marker) = 0;
+  GST_BUFFER_OFFSET_END (marker) = 0;
+  flacparse->headers = g_list_append (flacparse->headers, marker);
+
+  streaminfo = gst_buffer_new_and_alloc (4 + 34);
+  data = GST_BUFFER_DATA (streaminfo);
+  memset (data, 0, 4 + 34);
+
+  /* metadata block header */
+  data[0] = 0x00;               /* is_last = 0; type = 0; */
+  data[1] = 0x00;               /* length = 34; */
+  data[2] = 0x00;
+  data[3] = 0x22;
+
+  /* streaminfo */
+
+  data[4] = (flacparse->block_size >> 8) & 0xff;        /* min blocksize = blocksize; */
+  data[5] = (flacparse->block_size) & 0xff;
+  data[6] = (flacparse->block_size >> 8) & 0xff;        /* max blocksize = blocksize; */
+  data[7] = (flacparse->block_size) & 0xff;
+
+  data[8] = 0x00;               /* min framesize = 0; */
+  data[9] = 0x00;
+  data[10] = 0x00;
+  data[11] = 0x00;              /* max framesize = 0; */
+  data[12] = 0x00;
+  data[13] = 0x00;
+
+  data[14] = (flacparse->samplerate >> 12) & 0xff;
+  data[15] = (flacparse->samplerate >> 4) & 0xff;
+  data[16] = (flacparse->samplerate >> 0) & 0xf0;
+
+  data[16] |= (flacparse->channels - 1) << 1;
+
+  data[16] |= ((flacparse->bps - 1) >> 4) & 0x01;
+  data[17] = (((flacparse->bps - 1)) & 0x0f) << 4;
+
+  {
+    gint64 duration;
+    GstFormat fmt = GST_FORMAT_TIME;
+
+    if (gst_pad_query_peer_duration (GST_BASE_PARSE_SINK_PAD (GST_BASE_PARSE
+                (flacparse)), &fmt, &duration) && fmt == GST_FORMAT_TIME) {
+      duration = GST_CLOCK_TIME_TO_FRAMES (duration, flacparse->samplerate);
+
+      data[17] |= (duration >> 32) & 0xff;
+      data[18] |= (duration >> 24) & 0xff;
+      data[19] |= (duration >> 16) & 0xff;
+      data[20] |= (duration >> 8) & 0xff;
+      data[21] |= (duration >> 0) & 0xff;
+    }
+  }
+  /* MD5 = 0; */
+
+  GST_BUFFER_TIMESTAMP (streaminfo) = GST_CLOCK_TIME_NONE;
+  GST_BUFFER_DURATION (streaminfo) = GST_CLOCK_TIME_NONE;
+  GST_BUFFER_OFFSET (streaminfo) = 0;
+  GST_BUFFER_OFFSET_END (streaminfo) = 0;
+  flacparse->headers = g_list_append (flacparse->headers, streaminfo);
+
+  /* empty vorbiscomment */
+  {
+    GstTagList *taglist = gst_tag_list_new ();
+    guchar header[4];
+    guint size;
+
+    header[0] = 0x84;           /* is_last = 1; type = 4; */
+
+    vorbiscomment =
+        gst_tag_list_to_vorbiscomment_buffer (taglist, header, sizeof (header),
+        NULL);
+    gst_tag_list_free (taglist);
+
+    /* Get rid of framing bit */
+    if (GST_BUFFER_DATA (vorbiscomment)[GST_BUFFER_SIZE (vorbiscomment) - 1] ==
+        1) {
+      GstBuffer *sub;
+
+      sub =
+          gst_buffer_create_sub (vorbiscomment, 0,
+          GST_BUFFER_SIZE (vorbiscomment) - 1);
+      gst_buffer_unref (vorbiscomment);
+      vorbiscomment = sub;
+    }
+
+    size = GST_BUFFER_SIZE (vorbiscomment) - 4;
+    GST_BUFFER_DATA (vorbiscomment)[1] = ((size & 0xFF0000) >> 16);
+    GST_BUFFER_DATA (vorbiscomment)[2] = ((size & 0x00FF00) >> 8);
+    GST_BUFFER_DATA (vorbiscomment)[3] = (size & 0x0000FF);
+
+    GST_BUFFER_TIMESTAMP (vorbiscomment) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DURATION (vorbiscomment) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_OFFSET (vorbiscomment) = 0;
+    GST_BUFFER_OFFSET_END (vorbiscomment) = 0;
+    flacparse->headers = g_list_append (flacparse->headers, vorbiscomment);
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_flac_parse_parse_frame (GstBaseParse * parse, GstBuffer * buffer)
+{
+  GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
+  const guint8 *data = GST_BUFFER_DATA (buffer);
+
+  if (flacparse->state == GST_FLAC_PARSE_STATE_INIT) {
+    GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_OFFSET (buffer) = 0;
+    GST_BUFFER_OFFSET_END (buffer) = 0;
+
+    /* 32 bits metadata block */
+    gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), 4);
+    flacparse->state = GST_FLAC_PARSE_STATE_HEADERS;
+
+    flacparse->headers =
+        g_list_append (flacparse->headers, gst_buffer_ref (buffer));
+
+    return GST_BASE_PARSE_FLOW_DROPPED;
+  } else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) {
+    gboolean is_last = ((data[0] & 0x80) == 0x80);
+    guint type = (data[0] & 0x7F);
+
+    if (type == 127) {
+      GST_WARNING_OBJECT (flacparse, "Invalid metadata block type");
+      return GST_BASE_PARSE_FLOW_DROPPED;
+    }
+
+    GST_DEBUG_OBJECT (flacparse, "Handling metadata block of type %u", type);
+
+    switch (type) {
+      case 0:                  /* STREAMINFO */
+        if (!gst_flac_parse_handle_streaminfo (flacparse, buffer))
+          return GST_FLOW_ERROR;
+        break;
+      case 3:                  /* SEEKTABLE */
+        /* TODO: handle seektables */
+        break;
+      case 4:                  /* VORBIS_COMMENT */
+        if (!gst_flac_parse_handle_vorbiscomment (flacparse, buffer))
+          return GST_FLOW_ERROR;
+        break;
+      case 6:                  /* PICTURE */
+        if (!gst_flac_parse_handle_picture (flacparse, buffer))
+          return GST_FLOW_ERROR;
+        break;
+      case 1:                  /* PADDING */
+      case 2:                  /* APPLICATION */
+      case 5:                  /* CUESHEET */
+      default:                 /* RESERVED */
+        break;
+    }
+
+    GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+    GST_BUFFER_OFFSET (buffer) = 0;
+    GST_BUFFER_OFFSET_END (buffer) = 0;
+
+    if (is_last) {
+      flacparse->headers =
+          g_list_append (flacparse->headers, gst_buffer_ref (buffer));
+
+      if (!gst_flac_parse_handle_headers (flacparse))
+        return GST_FLOW_ERROR;
+
+      /* Minimal size of a frame header */
+      gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), MAX (16,
+              flacparse->min_framesize));
+      flacparse->requested_frame_size = MAX (16, flacparse->min_framesize);
+      flacparse->state = GST_FLAC_PARSE_STATE_DATA;
+
+      /* DROPPED because we pushed all headers manually already */
+      return GST_BASE_PARSE_FLOW_DROPPED;
+    } else {
+      flacparse->headers =
+          g_list_append (flacparse->headers, gst_buffer_ref (buffer));
+      return GST_BASE_PARSE_FLOW_DROPPED;
+    }
+  } else {
+    if (flacparse->offset != GST_BUFFER_OFFSET (buffer)) {
+      gint ret;
+      guint framesize;
+
+      flacparse->offset = GST_BUFFER_OFFSET (buffer);
+      ret = gst_flac_parse_get_frame_size (flacparse, buffer, &framesize);
+      if (ret != 0) {
+        GST_ERROR_OBJECT (flacparse,
+            "Baseclass didn't provide a complete frame");
+        return GST_FLOW_ERROR;
+      }
+    }
+
+    if (flacparse->block_size == 0) {
+      GST_ERROR_OBJECT (flacparse, "Unparsed frame");
+      return GST_FLOW_ERROR;
+    }
+
+    if (flacparse->state == GST_FLAC_PARSE_STATE_GENERATE_HEADERS) {
+      if (flacparse->blocking_strategy == 1) {
+        GST_WARNING_OBJECT (flacparse,
+            "Generating headers for variable blocksize streams not supported");
+
+        if (!gst_flac_parse_handle_headers (flacparse))
+          return GST_FLOW_ERROR;
+      } else {
+        GST_DEBUG_OBJECT (flacparse, "Generating headers");
+
+        if (!gst_flac_parse_generate_headers (flacparse))
+          return GST_FLOW_ERROR;
+
+        if (!gst_flac_parse_handle_headers (flacparse))
+          return GST_FLOW_ERROR;
+      }
+      flacparse->state = GST_FLAC_PARSE_STATE_DATA;
+    }
+
+    if (flacparse->blocking_strategy == 0) {
+      GST_BUFFER_TIMESTAMP (buffer) =
+          gst_util_uint64_scale (flacparse->sample_number,
+          flacparse->block_size * GST_SECOND, flacparse->samplerate);
+    } else {
+      GST_BUFFER_TIMESTAMP (buffer) =
+          gst_util_uint64_scale (flacparse->sample_number, GST_SECOND,
+          flacparse->samplerate);
+    }
+    GST_BUFFER_DURATION (buffer) =
+        GST_FRAMES_TO_CLOCK_TIME (flacparse->block_size, flacparse->samplerate);
+
+    /* Minimal size of a frame header */
+    gst_base_parse_set_min_frame_size (GST_BASE_PARSE (flacparse), MAX (16,
+            flacparse->min_framesize));
+    flacparse->requested_frame_size = MAX (16, flacparse->min_framesize);
+
+    flacparse->offset = -1;
+    flacparse->blocking_strategy = 0;
+    flacparse->block_size = 0;
+    flacparse->sample_number = 0;
+    return GST_FLOW_OK;
+  }
+}
diff --git a/gst/flacparse/gstflacparse.h b/gst/flacparse/gstflacparse.h
new file mode 100644 (file)
index 0000000..66171dc
--- /dev/null
@@ -0,0 +1,87 @@
+/* GStreamer
+ *
+ * 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.
+ */
+
+#ifndef __GST_FLAC_PARSE_H__
+#define __GST_FLAC_PARSE_H__
+
+#include <gst/gst.h>
+#include "gstbaseparse.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_FLAC_PARSE               (gst_base_parse_get_type())
+#define GST_FLAC_PARSE(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLAC_PARSE,GstFlacParse))
+#define GST_FLAC_PARSE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLAC_PARSE,GstFlacParseClass))
+#define GST_FLAC_PARSE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_FLAC_PARSE,GstFlacParseClass))
+#define GST_IS_FLAC_PARSE(obj)    (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLAC_PARSE))
+#define GST_IS_FLAC_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLAC_PARSE))
+#define GST_FLAC_PARSE_CAST(obj)       ((GstFlacParse *)(obj))
+
+typedef struct _GstFlacParse GstFlacParse;
+typedef struct _GstFlacParseClass GstFlacParseClass;
+
+typedef enum {
+  GST_FLAC_PARSE_STATE_INIT,
+  GST_FLAC_PARSE_STATE_HEADERS,
+  GST_FLAC_PARSE_STATE_GENERATE_HEADERS,
+  GST_FLAC_PARSE_STATE_DATA
+} GstFlacParseState;
+
+typedef struct {
+  guint8 type;
+} GstFlacParseSubFrame;
+
+struct _GstFlacParse {
+  GstBaseParse parent;
+
+  GstFlacParseState state;
+
+  gint64 upstream_length;
+
+  /* STREAMINFO content */
+  guint16 min_blocksize, max_blocksize;
+  guint32 min_framesize, max_framesize;
+  guint32 samplerate;
+  guint8 channels;
+  guint8 bps;
+  guint64 total_samples;
+
+  guint requested_frame_size;
+
+  /* Current frame */
+  guint64 offset;
+  guint8 blocking_strategy;
+  guint16 block_size;
+  guint64 sample_number;
+
+  GstTagList *tags;
+
+  GList *headers;
+};
+
+struct _GstFlacParseClass {
+  GstBaseParseClass parent_class;
+};
+
+GType gst_flac_parse_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_FLAC_PARSE_H__ */