audioparsers: move 'flacparse' into it
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Thu, 17 Dec 2009 11:44:20 +0000 (12:44 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Fri, 8 Apr 2011 17:07:02 +0000 (18:07 +0100)
gst/audioparsers/Makefile.am
gst/audioparsers/gstflacparse.c [new file with mode: 0644]
gst/audioparsers/gstflacparse.h [new file with mode: 0644]
gst/audioparsers/plugin.c

index 16c9d2d..6beb07c 100644 (file)
@@ -1,11 +1,16 @@
 plugin_LTLIBRARIES = libgstaudioparsersbad.la
 
 libgstaudioparsersbad_la_SOURCES = \
-       gstaacparse.c gstamrparse.c gstac3parse.c gstbaseparse.c plugin.c
+       gstaacparse.c gstamrparse.c gstac3parse.c gstflacparse.c \
+       gstbaseparse.c plugin.c
 
-libgstaudioparsersbad_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
-libgstaudioparsersbad_la_LIBADD = $(GST_BASE_LIBS)
+libgstaudioparsersbad_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
+libgstaudioparsersbad_la_LIBADD = \
+       $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \
+       -lgstaudio-$(GST_MAJORMINOR) \
+       $(GST_BASE_LIBS) $(GST_LIBS)
 libgstaudioparsersbad_la_LDFLAGS = $(PACKAGE_LIBS) $(GST_PLUGIN_LDFLAGS)
 libgstaudioparsersbad_la_LIBTOOLFLAGS = --tag=disable-static
 
-noinst_HEADERS = gstaacparse.h gstamrparse.h gstac3parse.h gstbaseparse.h
+noinst_HEADERS = gstaacparse.h gstamrparse.h gstac3parse.h gstflacparse.h \
+       gstbaseparse.h
diff --git a/gst/audioparsers/gstflacparse.c b/gst/audioparsers/gstflacparse.c
new file mode 100644 (file)
index 0000000..d65ba06
--- /dev/null
@@ -0,0 +1,1234 @@
+/* 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 <gst/base/gstbitreader.h>
+#include <gst/base/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 && upstream_len != -1)) {
+      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;
+  }
+
+eos:
+  {
+    GST_WARNING_OBJECT (flacparse, "EOS");
+    return -1;
+  }
+
+error:
+  {
+    GST_WARNING_OBJECT (flacparse, "Invalid frame");
+    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/audioparsers/gstflacparse.h b/gst/audioparsers/gstflacparse.h
new file mode 100644 (file)
index 0000000..522cc78
--- /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_flac_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__ */
index 2e3bed5..7da4fc1 100644 (file)
@@ -24,6 +24,7 @@
 #include "gstaacparse.h"
 #include "gstamrparse.h"
 #include "gstac3parse.h"
+#include "gstflacparse.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
@@ -36,6 +37,8 @@ plugin_init (GstPlugin * plugin)
       GST_RANK_PRIMARY + 1, GST_TYPE_AMRPARSE);
   ret &= gst_element_register (plugin, "ac3parse",
       GST_RANK_MARGINAL, GST_TYPE_AC3_PARSE);
+  ret &= gst_element_register (plugin, "flacparse",
+      GST_RANK_NONE, GST_TYPE_FLAC_PARSE);
 
   return ret;
 }