vc1parse: Add VC1 parser plugin
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 31 Jan 2012 09:58:23 +0000 (10:58 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Thu, 25 Oct 2012 12:05:48 +0000 (14:05 +0200)
gst/videoparsers/Makefile.am
gst/videoparsers/gstvc1parse.c [new file with mode: 0644]
gst/videoparsers/gstvc1parse.h [new file with mode: 0644]
gst/videoparsers/plugin.c

index 2e2a38b..0e88f73 100644 (file)
@@ -4,7 +4,8 @@ libgstvideoparsersbad_la_SOURCES = plugin.c \
        h263parse.c gsth263parse.c \
        gstdiracparse.c dirac_parse.c \
        gsth264parse.c gstmpegvideoparse.c \
-       gstmpeg4videoparse.c
+       gstmpeg4videoparse.c \
+       gstvc1parse.c
 
 libgstvideoparsersbad_la_CFLAGS = \
        $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
@@ -25,7 +26,8 @@ libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static
 noinst_HEADERS = gsth263parse.h h263parse.h \
        gstdiracparse.h dirac_parse.h \
        gsth264parse.h gstmpegvideoparse.h \
-       gstmpeg4videoparse.h
+       gstmpeg4videoparse.h \
+       gstvc1parse.h
 
 Android.mk: Makefile.am $(BUILT_SOURCES)
        androgenizer \
diff --git a/gst/videoparsers/gstvc1parse.c b/gst/videoparsers/gstvc1parse.c
new file mode 100644 (file)
index 0000000..33400af
--- /dev/null
@@ -0,0 +1,1639 @@
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+/*
+ * Information about the caps fields:
+ *
+ * header-format:
+ *   none: No codec_data and only in-stream headers
+ *
+ *   asf: codec_data as specified in the ASF specification
+ *       Simple/Main profile: 4 byte sequence header without startcode
+ *       Advanced profile: Sequence header and entrypoint with startcodes
+ *
+ *   sequence-layer: codec_data as specified in SMPTE 421M Annex L.2
+ *
+ *
+ * stream-format:
+ *   bdu: BDUs with startcodes
+ *
+ *   bdu-frame: BDUs with startcodes, everything up to and including a frame
+ *       per buffer. This also means everything needed to decode a frame, i.e.
+ *       field and slice BDUs
+ *
+ *   sequence-layer-bdu: Sequence layer in first buffer, then BDUs with startcodes
+ *
+ *   sequence-layer-bdu-frame: Sequence layer in first buffer, then only frame
+ *       BDUs with startcodes, i.e. everything up to and including a frame.
+ *
+ *   sequence-layer-raw-frame: Sequence layer in first buffer, then only frame
+ *       BDUs without startcodes. Only for simple/main profile.
+ *   
+ *   sequence-layer-frame-layer: As specified in SMPTE 421M Annex L, sequence-layer
+ *       first, then BDUs inside frame-layer
+ *
+ *   asf: As specified in the ASF specification.
+ *       For simple/main profile a single frame BDU without startcodes per buffer
+ *       For advanced profile one or many BDUs with/without startcodes:
+ *           Startcodes required if non-frame BDU or multiple BDUs per buffer
+ *           unless frame BDU followed by field BDU. In that case only second (field)
+ *           startcode required.
+ *
+ *   frame-layer: As specified in SMPTE 421M Annex L.2
+ *
+ *
+ * If no stream-format is given in the caps we do the following:
+ *   
+ *   0) If header-format=asf we assume stream-format=asf
+ *   1) If first buffer starts with sequence header startcode
+ *      we assume stream-format=bdu (or bdu-frame, doesn't matter
+ *      for the input because we're parsing anyway)
+ *   2) If first buffer starts with sequence layer startcode
+ *      1) If followed by sequence header or frame startcode
+ *         we assume stream-format=sequence-layer-bdu (or -bdu-frame,
+ *         doesn't matter for the input because we're parsing anyway)
+ *      2) Otherwise we assume stream-format=sequence-layer-frame-layer
+ *   3) Otherwise
+ *      1) If header-format=sequence-layer we assume stream-format=frame-layer
+ *      2) If header-format=none we error out
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvc1parse.h"
+
+#include <gst/base/gstbytereader.h>
+#include <string.h>
+
+GST_DEBUG_CATEGORY (vc1_parse_debug);
+#define GST_CAT_DEFAULT vc1_parse_debug
+
+static const struct
+{
+  gchar str[15];
+  VC1HeaderFormat en;
+} header_formats[] = {
+  {
+  "none", VC1_HEADER_FORMAT_NONE}, {
+  "asf", VC1_HEADER_FORMAT_ASF}, {
+  "sequence-layer", VC1_HEADER_FORMAT_SEQUENCE_LAYER}
+};
+
+static const struct
+{
+  gchar str[27];
+  VC1StreamFormat en;
+} stream_formats[] = {
+  {
+  "bdu", VC1_STREAM_FORMAT_BDU}, {
+  "bdu-frame", VC1_STREAM_FORMAT_BDU_FRAME}, {
+  "sequence-layer-bdu", VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU}, {
+  "sequence-layer-bdu-frame", VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME}, {
+  "sequence-layer-raw-frame", VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME}, {
+  "sequence-layer-frame-layer", VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER}, {
+  "asf", VC1_STREAM_FORMAT_ASF}, {
+  "frame-layer", VC1_STREAM_FORMAT_FRAME_LAYER}
+};
+
+static const gchar *
+stream_format_to_string (VC1StreamFormat stream_format)
+{
+  return stream_formats[stream_format].str;
+}
+
+static VC1StreamFormat
+stream_format_from_string (const gchar * stream_format)
+{
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (stream_formats); i++) {
+    if (strcmp (stream_formats[i].str, stream_format) == 0)
+      return stream_formats[i].en;
+  }
+  return -1;
+}
+
+static const gchar *
+header_format_to_string (VC1HeaderFormat header_format)
+{
+  return header_formats[header_format].str;
+}
+
+static VC1HeaderFormat
+header_format_from_string (const gchar * header_format)
+{
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (header_formats); i++) {
+    if (strcmp (header_formats[i].str, header_format) == 0)
+      return header_formats[i].en;
+  }
+  return -1;
+}
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-wmv, wmvversion=(int) 3, "
+        "format=(fourcc) {WVC1, WMV3}"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-wmv, wmvversion=(int) 3, "
+        "format=(fourcc) {WVC1, WMV3}, "
+        "stream-format=(string) {bdu, bdu-frame, sequence-layer-bdu, "
+        "sequence-layer-bdu-frame, sequence-layer-raw-frame, "
+        "sequence-layer-frame-layer, asf, frame-layer}, "
+        "header-format=(string) {none, asf, sequence-layer}"));
+
+
+GST_BOILERPLATE (GstVC1Parse, gst_vc1_parse, GstBaseParse, GST_TYPE_BASE_PARSE);
+
+static void gst_vc1_parse_finalize (GObject * object);
+
+static gboolean gst_vc1_parse_start (GstBaseParse * parse);
+static gboolean gst_vc1_parse_stop (GstBaseParse * parse);
+static gboolean gst_vc1_parse_check_valid_frame (GstBaseParse * parse,
+    GstBaseParseFrame * frame, guint * framesize, gint * skipsize);
+static GstFlowReturn gst_vc1_parse_parse_frame (GstBaseParse * parse,
+    GstBaseParseFrame * frame);
+static GstFlowReturn gst_vc1_parse_pre_push_frame (GstBaseParse * parse,
+    GstBaseParseFrame * frame);
+static gboolean gst_vc1_parse_set_caps (GstBaseParse * parse, GstCaps * caps);
+static GstCaps *gst_vc1_parse_get_sink_caps (GstBaseParse * parse);
+static GstFlowReturn gst_vc1_parse_detect (GstBaseParse * parse,
+    GstBuffer * buffer);
+
+static void gst_vc1_parse_reset (GstVC1Parse * vc1parse);
+static gboolean gst_vc1_parse_handle_seq_layer (GstVC1Parse * vc1parse,
+    GstBuffer * buf, guint offset, guint size);
+static gboolean gst_vc1_parse_handle_seq_hdr (GstVC1Parse * vc1parse,
+    GstBuffer * buf, guint offset, guint size);
+static gboolean gst_vc1_parse_handle_entrypoint (GstVC1Parse * vc1parse,
+    GstBuffer * buf, guint offset, guint size);
+static void gst_vc1_parse_update_stream_format_properties (GstVC1Parse *
+    vc1parse);
+
+static void
+gst_vc1_parse_base_init (gpointer g_class)
+{
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&srctemplate));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&sinktemplate));
+
+  gst_element_class_set_details_simple (gstelement_class, "VC1 parser",
+      "Codec/Parser/Converter/Video",
+      "Parses VC1 streams",
+      "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
+
+  GST_DEBUG_CATEGORY_INIT (vc1_parse_debug, "vc1parse", 0, "vc1 parser");
+}
+
+static void
+gst_vc1_parse_class_init (GstVC1ParseClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
+
+  gobject_class->finalize = gst_vc1_parse_finalize;
+
+  parse_class->start = GST_DEBUG_FUNCPTR (gst_vc1_parse_start);
+  parse_class->stop = GST_DEBUG_FUNCPTR (gst_vc1_parse_stop);
+  parse_class->check_valid_frame =
+      GST_DEBUG_FUNCPTR (gst_vc1_parse_check_valid_frame);
+  parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_vc1_parse_parse_frame);
+  parse_class->pre_push_frame =
+      GST_DEBUG_FUNCPTR (gst_vc1_parse_pre_push_frame);
+  parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_vc1_parse_set_caps);
+  parse_class->get_sink_caps = GST_DEBUG_FUNCPTR (gst_vc1_parse_get_sink_caps);
+  parse_class->detect = GST_DEBUG_FUNCPTR (gst_vc1_parse_detect);
+}
+
+static void
+gst_vc1_parse_init (GstVC1Parse * vc1parse, GstVC1ParseClass * g_class)
+{
+  /* Default values for stream-format=raw, i.e.
+   * raw VC1 frames with startcodes */
+  gst_base_parse_set_syncable (GST_BASE_PARSE (vc1parse), TRUE);
+  gst_base_parse_set_has_timing_info (GST_BASE_PARSE (vc1parse), FALSE);
+
+  gst_vc1_parse_reset (vc1parse);
+}
+
+static void
+gst_vc1_parse_finalize (GObject * object)
+{
+  /*GstVC1Parse *vc1parse = GST_VC1_PARSE (object); */
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_vc1_parse_reset (GstVC1Parse * vc1parse)
+{
+  vc1parse->profile = -1;
+  vc1parse->level = -1;
+  vc1parse->fourcc = 0;
+  vc1parse->width = 0;
+  vc1parse->height = 0;
+  vc1parse->fps_n = vc1parse->fps_d = 0;
+  vc1parse->fps_from_caps = FALSE;
+  vc1parse->par_n = vc1parse->par_d = 0;
+  vc1parse->par_from_caps = FALSE;
+
+  vc1parse->renegotiate = TRUE;
+  vc1parse->update_caps = TRUE;
+
+  vc1parse->input_header_format = VC1_HEADER_FORMAT_NONE;
+  vc1parse->input_stream_format = VC1_STREAM_FORMAT_BDU;
+  vc1parse->output_header_format = VC1_HEADER_FORMAT_NONE;
+  vc1parse->output_stream_format = VC1_STREAM_FORMAT_BDU;
+  gst_buffer_replace (&vc1parse->seq_layer_buffer, NULL);
+  gst_buffer_replace (&vc1parse->seq_hdr_buffer, NULL);
+  gst_buffer_replace (&vc1parse->entrypoint_buffer, NULL);
+}
+
+static gboolean
+gst_vc1_parse_start (GstBaseParse * parse)
+{
+  GstVC1Parse *vc1parse = GST_VC1_PARSE (parse);
+
+  GST_DEBUG_OBJECT (parse, "start");
+  gst_vc1_parse_reset (vc1parse);
+
+  vc1parse->detecting_stream_format = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+gst_vc1_parse_stop (GstBaseParse * parse)
+{
+  GstVC1Parse *vc1parse = GST_VC1_PARSE (parse);
+
+  GST_DEBUG_OBJECT (parse, "stop");
+  gst_vc1_parse_reset (vc1parse);
+
+  return TRUE;
+}
+
+static gboolean
+gst_vc1_parse_renegotiate (GstVC1Parse * vc1parse)
+{
+  GstCaps *allowed_caps;
+
+  if (!vc1parse->renegotiate)
+    return TRUE;
+
+  /* Negotiate with downstream here */
+  GST_DEBUG_OBJECT (vc1parse, "Renegotiating");
+
+  allowed_caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (vc1parse));
+  if (allowed_caps && !gst_caps_is_empty (allowed_caps)
+      && !gst_caps_is_any (allowed_caps)) {
+    GstStructure *s;
+    const gchar *stream_format, *header_format;
+
+    GST_DEBUG_OBJECT (vc1parse, "Downstream allowed caps: %" GST_PTR_FORMAT,
+        allowed_caps);
+
+    allowed_caps = gst_caps_make_writable (allowed_caps);
+    gst_caps_truncate (allowed_caps);
+    s = gst_caps_get_structure (allowed_caps, 0);
+
+    /* If already fixed this does nothing */
+    gst_structure_fixate_field_string (s, "header-format", "asf");
+    header_format = gst_structure_get_string (s, "header-format");
+    if (!header_format) {
+      vc1parse->output_header_format = vc1parse->input_header_format;
+      header_format = header_format_to_string (vc1parse->output_header_format);
+      gst_structure_set (s, "header-format", G_TYPE_STRING, header_format,
+          NULL);
+    } else {
+      vc1parse->output_header_format =
+          header_format_from_string (header_format);
+    }
+
+    /* If already fixed this does nothing */
+    gst_structure_fixate_field_string (s, "stream-format", "asf");
+    stream_format = gst_structure_get_string (s, "stream-format");
+    if (!stream_format) {
+      vc1parse->output_stream_format = vc1parse->input_stream_format;
+      stream_format = stream_format_to_string (vc1parse->output_stream_format);
+      gst_structure_set (s, "stream-format", G_TYPE_STRING, stream_format,
+          NULL);
+    } else {
+      vc1parse->output_stream_format =
+          stream_format_from_string (stream_format);
+    }
+  } else if (gst_caps_is_empty (allowed_caps)) {
+    GST_ERROR_OBJECT (vc1parse, "Empty caps");
+    return FALSE;
+  } else {
+    GST_DEBUG_OBJECT (vc1parse, "Using input header/stream format");
+    vc1parse->output_header_format = vc1parse->input_header_format;
+    vc1parse->output_stream_format = vc1parse->input_stream_format;
+  }
+
+  if (allowed_caps)
+    gst_caps_unref (allowed_caps);
+  vc1parse->renegotiate = FALSE;
+  vc1parse->update_caps = TRUE;
+
+  GST_DEBUG_OBJECT (vc1parse, "Negotiated %s/%s with downstream",
+      header_format_to_string (vc1parse->output_header_format),
+      stream_format_to_string (vc1parse->output_stream_format));
+
+  return TRUE;
+}
+
+static GstCaps *
+gst_vc1_parse_get_sink_caps (GstBaseParse * parse)
+{
+  GstCaps *peercaps;
+  const GstCaps *templ;
+  GstCaps *ret;
+
+  templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse));
+  peercaps = gst_pad_peer_get_caps (GST_BASE_PARSE_SRC_PAD (parse));
+  if (peercaps) {
+    guint i, n;
+    GstStructure *s;
+
+    /* Remove the stream-format and header-format fields
+     * and add the generic ones again by intersecting
+     * with our template */
+    peercaps = gst_caps_make_writable (peercaps);
+    n = gst_caps_get_size (peercaps);
+    for (i = 0; i < n; i++) {
+      s = gst_caps_get_structure (peercaps, i);
+
+      gst_structure_remove_field (s, "stream-format");
+      gst_structure_remove_field (s, "header-format");
+    }
+
+    ret = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (peercaps);
+  } else {
+    ret = gst_caps_copy (templ);
+  }
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_vc1_parse_detect (GstBaseParse * parse, GstBuffer * buffer)
+{
+  GstVC1Parse *vc1parse = GST_VC1_PARSE (parse);
+  guint8 *data;
+  gint size;
+
+  if (!vc1parse->detecting_stream_format)
+    return GST_FLOW_OK;
+
+  data = GST_BUFFER_DATA (buffer);
+  size = GST_BUFFER_SIZE (buffer);
+
+  while (size >= 4) {
+    guint32 startcode = GST_READ_UINT32_BE (data);
+
+    if ((startcode & 0xffffff00) == 0x00000100) {
+      GST_DEBUG_OBJECT (vc1parse, "Found BDU startcode");
+      vc1parse->input_stream_format = VC1_STREAM_FORMAT_BDU_FRAME;
+      goto detected;
+    }
+
+    data += 4;
+    size -= 4;
+  }
+
+  data = GST_BUFFER_DATA (buffer);
+  size = GST_BUFFER_SIZE (buffer);
+  while (size >= 40) {
+    if (data[3] == 0xc5 && GST_READ_UINT32_BE (data + 4) == 0x00000004 &&
+        GST_READ_UINT32_BE (data + 20) == 0x0000000c) {
+      guint32 startcode;
+
+      GST_DEBUG_OBJECT (vc1parse, "Found sequence layer");
+      startcode = GST_READ_UINT32_BE (data + 36);
+      if ((startcode & 0xffffff00) == 0x00000100) {
+        GST_DEBUG_OBJECT (vc1parse, "Found BDU startcode after sequence layer");
+        vc1parse->input_stream_format =
+            VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME;
+        goto detected;
+      } else {
+        GST_DEBUG_OBJECT (vc1parse,
+            "Assuming sequence-layer-frame-layer stream format");
+        vc1parse->input_stream_format =
+            VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER;
+        goto detected;
+      }
+    }
+    data += 4;
+    size -= 4;
+  }
+
+  if (GST_BUFFER_SIZE (buffer) <= 128) {
+    GST_DEBUG_OBJECT (vc1parse, "Requesting more data");
+    return GST_FLOW_NOT_NEGOTIATED;
+  }
+
+  if (GST_BASE_PARSE_DRAINING (vc1parse)) {
+    GST_ERROR_OBJECT (vc1parse, "Failed to detect or assume a stream format "
+        "and draining now");
+    return GST_FLOW_ERROR;
+  }
+
+  /* Otherwise we try some heuristics */
+  if (vc1parse->input_header_format == VC1_HEADER_FORMAT_ASF) {
+    GST_DEBUG_OBJECT (vc1parse, "Assuming ASF stream format");
+    vc1parse->input_stream_format = VC1_STREAM_FORMAT_ASF;
+    goto detected;
+  } else if (vc1parse->input_header_format == VC1_HEADER_FORMAT_SEQUENCE_LAYER) {
+    GST_DEBUG_OBJECT (vc1parse, "Assuming frame-layer stream format");
+    vc1parse->input_stream_format = VC1_STREAM_FORMAT_FRAME_LAYER;
+    goto detected;
+  } else {
+    GST_ERROR_OBJECT (vc1parse, "Can't detect or assume a stream format");
+    return GST_FLOW_ERROR;
+  }
+
+  g_assert_not_reached ();
+  return GST_FLOW_ERROR;
+
+detected:
+  vc1parse->detecting_stream_format = FALSE;
+  gst_vc1_parse_update_stream_format_properties (vc1parse);
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_vc1_parse_check_valid_frame (GstBaseParse * parse,
+    GstBaseParseFrame * frame, guint * framesize, gint * skipsize)
+{
+  GstVC1Parse *vc1parse = GST_VC1_PARSE (parse);
+  GstBuffer *buffer = frame->buffer;
+  guint8 *data = GST_BUFFER_DATA (buffer);
+  guint size = GST_BUFFER_SIZE (buffer);
+
+  if (vc1parse->renegotiate) {
+    if (!gst_vc1_parse_renegotiate (vc1parse)) {
+      GST_ERROR_OBJECT (vc1parse, "Failed to negotiate with downstream");
+      return FALSE;
+    }
+  }
+
+  if (!vc1parse->seq_layer_buffer
+      && (vc1parse->input_stream_format == VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU
+          || vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME
+          || vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME
+          || vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER)) {
+    if (data[3] == 0xc5 && GST_READ_UINT32_BE (data + 4) == 0x00000004
+        && GST_READ_UINT32_BE (data + 20) == 0x0000000c) {
+      *framesize = 36;
+      return TRUE;
+    }
+    return FALSE;
+  } else if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU ||
+      vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU_FRAME ||
+      (vc1parse->seq_layer_buffer
+          && (vc1parse->input_stream_format ==
+              VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU
+              || vc1parse->input_stream_format ==
+              VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME))) {
+    GstVC1ParserResult pres;
+    GstVC1BDU bdu;
+    g_assert (size >= 4);
+    memset (&bdu, 0, sizeof (bdu));
+    GST_DEBUG_OBJECT (vc1parse,
+        "Handling buffer of size %u at offset %" G_GUINT64_FORMAT, size,
+        GST_BUFFER_OFFSET (buffer));
+    pres = gst_vc1_identify_next_bdu (data, size, &bdu);
+    switch (pres) {
+      case GST_VC1_PARSER_OK:
+        GST_DEBUG_OBJECT (vc1parse, "Have complete BDU");
+        if (bdu.sc_offset > 4) {
+          *skipsize = bdu.sc_offset;
+          return FALSE;
+        } else {
+          *framesize = bdu.offset + bdu.size;
+          return TRUE;
+        }
+        break;
+      case GST_VC1_PARSER_BROKEN_DATA:
+        GST_ERROR_OBJECT (vc1parse, "Broken data");
+        return FALSE;
+        break;
+      case GST_VC1_PARSER_NO_BDU:
+        GST_DEBUG_OBJECT (vc1parse, "Found no BDU startcode");
+        *skipsize = size - 3;
+        return FALSE;
+        break;
+      case GST_VC1_PARSER_NO_BDU_END:
+        GST_DEBUG_OBJECT (vc1parse, "Found no BDU end");
+        if (G_UNLIKELY (GST_BASE_PARSE_DRAINING (vc1parse))) {
+          GST_DEBUG_OBJECT (vc1parse, "Draining - assuming complete frame");
+          *framesize = size;
+          return TRUE;
+        } else {
+          *skipsize = 0;
+          /* Request all that is available */
+          *framesize = G_MAXUINT;
+          return FALSE;
+        }
+        break;
+      case GST_VC1_PARSER_ERROR:
+        GST_ERROR_OBJECT (vc1parse, "Parsing error");
+        return FALSE;
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+  } else if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_ASF ||
+      (vc1parse->seq_layer_buffer
+          && vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME)) {
+    /* Must be packetized already */
+    *framesize = size;
+    return TRUE;
+  } else {
+    /* frame-layer or sequence-layer-frame-layer */
+    g_assert (size >= 8);
+    /* Parse frame layer size */
+    *framesize = GST_READ_UINT24_BE (data + 1) + 8;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static gboolean
+gst_vc1_parse_update_caps (GstVC1Parse * vc1parse)
+{
+  GstCaps *caps;
+  GstVC1Profile profile;
+  if (GST_PAD_CAPS (GST_BASE_PARSE_SRC_PAD (vc1parse))
+      && !vc1parse->update_caps)
+    return TRUE;
+  caps = gst_caps_new_simple ("video/x-wmv", "wmvversion", G_TYPE_INT, 3, NULL);
+  /* Must have this here from somewhere */
+  g_assert (vc1parse->width != 0 && vc1parse->height != 0);
+  gst_caps_set_simple (caps, "width", G_TYPE_INT, vc1parse->width, "height",
+      G_TYPE_INT, vc1parse->height, NULL);
+  if (vc1parse->fps_d != 0)
+    gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, vc1parse->fps_n,
+        vc1parse->fps_d, NULL);
+  if (vc1parse->par_n != 0 && vc1parse->par_d != 0)
+    gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+        vc1parse->par_n, vc1parse->par_d, NULL);
+  if (vc1parse->seq_hdr_buffer)
+    profile = vc1parse->seq_hdr.profile;
+  else if (vc1parse->seq_layer_buffer)
+    profile = vc1parse->seq_layer.struct_c.profile;
+  else
+    g_assert_not_reached ();
+  if (profile == GST_VC1_PROFILE_ADVANCED) {
+    const gchar *level;
+    /* Caller must make sure this is valid here */
+    g_assert (vc1parse->seq_hdr_buffer);
+    switch ((GstVC1Level) vc1parse->seq_hdr.advanced.level) {
+      case GST_VC1_LEVEL_L0:
+        level = "0";
+        break;
+      case GST_VC1_LEVEL_L1:
+        level = "1";
+        break;
+      case GST_VC1_LEVEL_L2:
+        level = "2";
+        break;
+      case GST_VC1_LEVEL_L3:
+        level = "3";
+        break;
+      case GST_VC1_LEVEL_L4:
+        level = "4";
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+
+    gst_caps_set_simple (caps, "format", GST_TYPE_FOURCC,
+        GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
+        "profile", G_TYPE_STRING, "advanced",
+        "level", G_TYPE_STRING, level, NULL);
+  } else
+      if (profile == GST_VC1_PROFILE_SIMPLE
+      || profile == GST_VC1_PROFILE_MAIN) {
+    const gchar *profile_str;
+    if (profile == GST_VC1_PROFILE_SIMPLE)
+      profile_str = "simple";
+    else
+      profile_str = "main";
+    gst_caps_set_simple (caps, "format", GST_TYPE_FOURCC,
+        GST_MAKE_FOURCC ('W', 'M', 'V', '3'),
+        "profile", G_TYPE_STRING, profile_str, NULL);
+    if (vc1parse->seq_layer_buffer) {
+      const gchar *level;
+      switch (vc1parse->seq_layer.struct_b.level) {
+        case GST_VC1_LEVEL_LOW:
+          level = "low";
+          break;
+        case GST_VC1_LEVEL_MEDIUM:
+          level = "medium";
+          break;
+        case GST_VC1_LEVEL_HIGH:
+          level = "high";
+          break;
+        default:
+          g_assert_not_reached ();
+          break;
+      }
+
+      gst_caps_set_simple (caps, "level", G_TYPE_STRING, level, NULL);
+    }
+  } else {
+    g_assert_not_reached ();
+  }
+
+  switch (vc1parse->output_header_format) {
+    case VC1_HEADER_FORMAT_ASF:
+      if (vc1parse->profile != GST_VC1_PROFILE_ADVANCED) {
+        GstBuffer *codec_data;
+        if (vc1parse->seq_hdr_buffer) {
+          codec_data = gst_buffer_create_sub (vc1parse->seq_hdr_buffer, 0, 4);
+        } else {
+          guint32 seq_hdr = 0;
+          /* Build simple/main sequence header from sequence layer */
+          seq_hdr |= (vc1parse->profile << 30);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.wmvp << 28);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.frmrtq_postproc << 25);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.bitrtq_postproc << 20);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.loop_filter << 19);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.multires << 17);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.fastuvmc << 15);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.extended_mv << 14);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.dquant << 12);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.vstransform << 11);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.overlap << 9);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.syncmarker << 8);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.rangered << 7);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.maxbframes << 4);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.quantizer << 2);
+          seq_hdr |= (vc1parse->seq_layer.struct_c.finterpflag << 1);
+          codec_data = gst_buffer_new_and_alloc (4);
+          GST_WRITE_UINT32_BE (GST_BUFFER_DATA (codec_data), seq_hdr);
+        }
+
+        gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data,
+            NULL);
+        gst_buffer_unref (codec_data);
+      } else {
+        GstBuffer *codec_data;
+        /* Should have seqhdr and entrypoint for the advanced profile here */
+        g_assert (vc1parse->seq_hdr_buffer && vc1parse->entrypoint_buffer);
+        codec_data =
+            gst_buffer_new_and_alloc (1 + 4 +
+            GST_BUFFER_SIZE (vc1parse->seq_hdr_buffer) + 4 +
+            GST_BUFFER_SIZE (vc1parse->entrypoint_buffer));
+        if (vc1parse->profile == GST_VC1_PROFILE_SIMPLE)
+          GST_WRITE_UINT8 (GST_BUFFER_DATA (codec_data), 0x29);
+        else
+          GST_WRITE_UINT8 (GST_BUFFER_DATA (codec_data), 0x2b);
+        GST_WRITE_UINT32_BE (GST_BUFFER_DATA (codec_data) + 1, 0x0000010f);
+        memcpy (GST_BUFFER_DATA (codec_data) + 1 + 4,
+            GST_BUFFER_DATA (vc1parse->seq_hdr_buffer),
+            GST_BUFFER_SIZE (vc1parse->seq_hdr_buffer));
+        GST_WRITE_UINT32_BE (GST_BUFFER_DATA (codec_data) + 1 + 4 +
+            GST_BUFFER_SIZE (vc1parse->seq_hdr_buffer), 0x0000010e);
+        memcpy (GST_BUFFER_DATA (codec_data) + 1 + 4 +
+            GST_BUFFER_SIZE (vc1parse->seq_hdr_buffer) + 4,
+            GST_BUFFER_DATA (vc1parse->entrypoint_buffer),
+            GST_BUFFER_SIZE (vc1parse->entrypoint_buffer));
+        gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data,
+            NULL);
+        gst_buffer_unref (codec_data);
+      }
+      break;
+    case VC1_HEADER_FORMAT_SEQUENCE_LAYER:
+      if (vc1parse->seq_layer_buffer) {
+        gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
+            vc1parse->seq_layer_buffer, NULL);
+      } else {
+        GstBuffer *codec_data = gst_buffer_new_and_alloc (36);
+        guint8 *data = GST_BUFFER_DATA (codec_data);
+        guint32 structC = 0;
+        /* Unknown number of frames and start code */
+        data[0] = 0xff;
+        data[1] = 0xff;
+        data[2] = 0xff;
+        data[3] = 0xc5;
+        /* structC */
+        structC |= (vc1parse->profile << 30);
+        if (vc1parse->profile != GST_VC1_PROFILE_ADVANCED) {
+          structC |= (vc1parse->seq_layer.struct_c.wmvp << 28);
+          structC |= (vc1parse->seq_layer.struct_c.frmrtq_postproc << 25);
+          structC |= (vc1parse->seq_layer.struct_c.bitrtq_postproc << 20);
+          structC |= (vc1parse->seq_layer.struct_c.loop_filter << 19);
+          structC |= (vc1parse->seq_layer.struct_c.multires << 17);
+          structC |= (vc1parse->seq_layer.struct_c.fastuvmc << 15);
+          structC |= (vc1parse->seq_layer.struct_c.extended_mv << 14);
+          structC |= (vc1parse->seq_layer.struct_c.dquant << 12);
+          structC |= (vc1parse->seq_layer.struct_c.vstransform << 11);
+          structC |= (vc1parse->seq_layer.struct_c.overlap << 9);
+          structC |= (vc1parse->seq_layer.struct_c.syncmarker << 8);
+          structC |= (vc1parse->seq_layer.struct_c.rangered << 7);
+          structC |= (vc1parse->seq_layer.struct_c.maxbframes << 4);
+          structC |= (vc1parse->seq_layer.struct_c.quantizer << 2);
+          structC |= (vc1parse->seq_layer.struct_c.finterpflag << 1);
+        }
+        GST_WRITE_UINT32_BE (data + 4, structC);
+        /* 0x00000004 */
+        GST_WRITE_UINT32_BE (data + 8, 4);
+        /* structA */
+        if (vc1parse->profile != GST_VC1_PROFILE_ADVANCED) {
+          GST_WRITE_UINT32_BE (data + 12, vc1parse->height);
+          GST_WRITE_UINT32_BE (data + 16, vc1parse->width);
+        } else {
+          GST_WRITE_UINT32_BE (data + 12, 0);
+          GST_WRITE_UINT32_BE (data + 16, 0);
+        }
+
+        /* 0x0000000c */
+        GST_WRITE_UINT32_BE (data + 20, 0x0000000c);
+        /* structB */
+        if (vc1parse->level != -1)
+          data[24] = (vc1parse->level << 5);
+        else
+          data[24] = 0x40;      /* Use HIGH level */
+        /* Unknown HRD_BUFFER */
+        GST_WRITE_UINT24_BE (data + 25, 0);
+        /* Unknown HRD_RATE */
+        GST_WRITE_UINT32_BE (data + 28, 0);
+        /* Framerate */
+        GST_WRITE_UINT32_BE (data + 32,
+            ((guint32) (((gdouble) vc1parse->fps_n) /
+                    ((gdouble) vc1parse->fps_d) + 0.5)));
+        gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data,
+            NULL);
+        gst_buffer_unref (codec_data);
+      }
+      break;
+    case VC1_HEADER_FORMAT_NONE:
+    default:
+      /* Nothing here */
+      break;
+  }
+
+  GST_DEBUG_OBJECT (vc1parse, "Setting caps %" GST_PTR_FORMAT, caps);
+  gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (vc1parse), caps);
+  gst_caps_unref (caps);
+  vc1parse->update_caps = FALSE;
+  return TRUE;
+}
+
+static gboolean
+gst_vc1_parse_handle_bdu (GstVC1Parse * vc1parse, GstVC1StartCode startcode,
+    GstBuffer * buffer, guint offset, guint size)
+{
+  GST_DEBUG_OBJECT (vc1parse, "Handling BDU with startcode 0x%02x", startcode);
+
+  switch (startcode) {
+    case GST_VC1_SEQUENCE:{
+      GST_DEBUG_OBJECT (vc1parse, "Have new SequenceHeader header");
+      if (!gst_vc1_parse_handle_seq_hdr (vc1parse, buffer, offset, size)) {
+        GST_ERROR_OBJECT (vc1parse, "Invalid VC1 sequence header");
+        return FALSE;
+      }
+      break;
+    }
+    case GST_VC1_ENTRYPOINT:
+      GST_DEBUG_OBJECT (vc1parse, "Have new EntryPoint header");
+      if (!gst_vc1_parse_handle_entrypoint (vc1parse, buffer, offset, size)) {
+        GST_ERROR_OBJECT (vc1parse, "Invalid VC1 entrypoint");
+        return FALSE;
+      }
+      break;
+    case GST_VC1_FRAME:
+      /* TODO: Check if keyframe */
+      break;
+    default:
+      break;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_vc1_parse_handle_bdus (GstVC1Parse * vc1parse, GstBuffer * buffer,
+    guint offset, guint size)
+{
+  GstVC1BDU bdu;
+  GstVC1ParserResult pres;
+  guint8 *data = GST_BUFFER_DATA (buffer) + offset;
+
+  do {
+    memset (&bdu, 0, sizeof (bdu));
+    pres = gst_vc1_identify_next_bdu (data, size, &bdu);
+    if (pres == GST_VC1_PARSER_OK || pres == GST_VC1_PARSER_NO_BDU_END) {
+      if (pres == GST_VC1_PARSER_NO_BDU_END) {
+        pres = GST_VC1_PARSER_OK;
+        bdu.size = size - bdu.offset;
+      }
+
+      data += bdu.offset;
+      size -= bdu.offset;
+
+      if (!gst_vc1_parse_handle_bdu (vc1parse, bdu.type, buffer,
+              data - GST_BUFFER_DATA (buffer), bdu.size))
+        return FALSE;
+
+      data += bdu.size;
+      size -= bdu.size;
+    }
+  } while (pres == GST_VC1_PARSER_OK && size > 0);
+
+  if (pres != GST_VC1_PARSER_OK) {
+    GST_DEBUG_OBJECT (vc1parse, "Failed to parse BDUs");
+    return FALSE;
+  }
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_vc1_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
+{
+  GstVC1Parse *vc1parse = GST_VC1_PARSE (parse);
+  GstBuffer *buffer = frame->buffer;
+  guint8 *data = GST_BUFFER_DATA (buffer);
+  guint size = GST_BUFFER_SIZE (buffer);
+
+  if (!vc1parse->seq_layer_buffer
+      && (vc1parse->input_stream_format == VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU
+          || vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME
+          || vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME
+          || vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER)) {
+    g_assert (size >= 36);
+    if (!gst_vc1_parse_handle_seq_layer (vc1parse, buffer, 0, size)) {
+      GST_ERROR_OBJECT (vc1parse, "Invalid sequence layer");
+      return GST_FLOW_ERROR;
+    }
+
+    frame->flags |= GST_BASE_PARSE_FRAME_FLAG_NO_FRAME;
+
+    if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU
+        || vc1parse->input_stream_format ==
+        VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME) {
+      gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 4);
+    } else if (vc1parse->input_stream_format ==
+        VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME) {
+      gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 1);
+    } else {
+      /* frame-layer */
+      gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 8);
+    }
+  } else if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU ||
+      vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU_FRAME ||
+      (vc1parse->seq_layer_buffer
+          && (vc1parse->input_stream_format ==
+              VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU
+              || vc1parse->input_stream_format ==
+              VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME))) {
+    GstVC1StartCode startcode;
+
+    /* Is already a complete BDU, should have at least the startcode */
+    g_assert (size >= 4);
+    startcode = data[3];
+
+    if (startcode != GST_VC1_SEQUENCE) {
+      if (!vc1parse->seq_hdr_buffer && !vc1parse->seq_layer_buffer) {
+        GST_ERROR_OBJECT (vc1parse,
+            "Need sequence header/layer before anything else");
+        return GST_FLOW_ERROR;
+      }
+    } else if (startcode != GST_VC1_ENTRYPOINT
+        && vc1parse->profile == GST_VC1_PROFILE_ADVANCED) {
+      if (vc1parse->seq_hdr_buffer && !vc1parse->entrypoint_buffer) {
+        GST_ERROR_OBJECT (vc1parse,
+            "Need entrypoint header after the sequence header for the "
+            "advanced profile");
+        return GST_FLOW_ERROR;
+      }
+    }
+
+    if (!gst_vc1_parse_handle_bdu (vc1parse, startcode, buffer, 4, size - 4))
+      return GST_FLOW_ERROR;
+  } else if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_ASF ||
+      (vc1parse->seq_layer_buffer
+          && vc1parse->input_stream_format ==
+          VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME)) {
+    GST_LOG_OBJECT (vc1parse, "Have new ASF or RAW data unit");
+
+    if (!vc1parse->seq_hdr_buffer && !vc1parse->seq_layer_buffer) {
+      GST_ERROR_OBJECT (vc1parse, "Need a sequence header or sequence layer");
+      return GST_FLOW_ERROR;
+    }
+
+    /* Might be multiple BDUs here, complex... */
+    if (vc1parse->profile == GST_VC1_PROFILE_ADVANCED) {
+      gboolean startcodes = FALSE;
+
+      if (size >= 4) {
+        guint32 startcode = GST_READ_UINT32_BE (data);
+        startcodes = ((startcode & 0xffffff00) == 0x00000100);
+      }
+
+      if (startcodes) {
+        if (!gst_vc1_parse_handle_bdus (vc1parse, buffer, 0, size))
+          return GST_FLOW_ERROR;
+
+        /* For the advanced profile we need a sequence header here */
+        if (!vc1parse->seq_hdr_buffer) {
+          GST_ERROR_OBJECT (vc1parse, "Need sequence header");
+          return GST_FLOW_ERROR;
+        }
+      } else {
+        /* Must be a frame or a frame + field */
+        /* TODO: Check if keyframe */
+      }
+    }
+  } else {
+    GstVC1ParserResult pres;
+    GstVC1FrameLayer flayer;
+    gboolean startcodes = FALSE;
+
+    /* frame-layer or sequence-layer-frame-layer */
+
+    /* Check if the frame-layer data contains BDUs with startcodes.
+     * Startcodes are not allowed in raw WMV9/VC1 streams
+     */
+    if (size >= 8 + 4) {
+      guint32 startcode = GST_READ_UINT32_BE (data + 8);
+      startcodes = ((startcode & 0xffffff00) == 0x00000100);
+    }
+
+    /* We either need a sequence layer or sequence header here
+     * or this has to be an advanced profile stream.
+     *
+     * For the advanced profile the frame-layer data contains
+     * BDUs with startcodes and includes the sequence header
+     */
+    if (!vc1parse->seq_layer_buffer && !vc1parse->seq_hdr_buffer && !startcodes) {
+      GST_ERROR_OBJECT (vc1parse, "Need a sequence header or sequence layer");
+      return GST_FLOW_ERROR;
+    }
+
+    if ((vc1parse->seq_layer_buffer || vc1parse->seq_hdr_buffer)
+        && vc1parse->profile == GST_VC1_PROFILE_ADVANCED && !startcodes) {
+      GST_ERROR_OBJECT (vc1parse,
+          "Advanced profile frame-layer data must start with startcodes");
+      return GST_FLOW_ERROR;
+    }
+
+    memset (&flayer, 0, sizeof (flayer));
+    pres = gst_vc1_parse_frame_layer (data, size, &flayer);
+    if (pres != GST_VC1_PARSER_OK) {
+      GST_ERROR_OBJECT (vc1parse, "Invalid VC1 frame layer");
+      return GST_FLOW_ERROR;
+    }
+
+    frame->buffer = buffer = gst_buffer_make_metadata_writable (buffer);
+    GST_BUFFER_TIMESTAMP (buffer) =
+        gst_util_uint64_scale (flayer.timestamp, GST_MSECOND, 1);
+    if (!flayer.key)
+      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
+    else
+      GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
+
+    /* For the simple/main profile this contains a single frame BDU without
+     * startcodes and for the advanced profile this contains BDUs with
+     * startcodes. In the case of the advanced profile parse them.
+     *
+     * Also for wrongly muxed simple/main profile streams with startcodes
+     * we do the same.
+     */
+    if (startcodes) {
+      /* skip frame layer header */
+      if (!gst_vc1_parse_handle_bdus (vc1parse, buffer, 8, size - 8))
+        return GST_FLOW_ERROR;
+
+      /* For the advanced profile we need a sequence header here */
+      if (!vc1parse->seq_hdr_buffer) {
+        GST_ERROR_OBJECT (vc1parse, "Need sequence header");
+        return GST_FLOW_ERROR;
+      }
+    }
+  }
+
+  /* Need sequence header or sequence layer here, above code
+   * checks this already */
+  g_assert (vc1parse->seq_layer_buffer || vc1parse->seq_hdr_buffer);
+  /* We need the entrypoint BDU for the advanced profile before we can set
+   * the caps. For the ASF header format it will already be in the codec_data,
+   * for the frame-layer stream format it will be in the first frame already.
+   *
+   * The only case where we wait another frame is the raw stream format, where
+   * it will be the second BDU
+   */
+  if (vc1parse->profile == GST_VC1_PROFILE_ADVANCED
+      && !vc1parse->entrypoint_buffer) {
+    if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU
+        || vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU_FRAME
+        || vc1parse->input_stream_format == VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU
+        || vc1parse->input_stream_format ==
+        VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME) {
+      return GST_BASE_PARSE_FLOW_QUEUED;
+    } else {
+      GST_ERROR_OBJECT (vc1parse, "Need entrypoint for the advanced profile");
+      return GST_FLOW_ERROR;
+    }
+  }
+
+  if (!gst_vc1_parse_update_caps (vc1parse))
+    return GST_FLOW_NOT_NEGOTIATED;
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_vc1_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
+{
+  GstVC1Parse *vc1parse = GST_VC1_PARSE (parse);
+
+  /* Nothing to do here */
+  if (vc1parse->input_stream_format == vc1parse->output_stream_format)
+    return GST_FLOW_OK;
+
+  switch (vc1parse->output_stream_format) {
+    case VC1_STREAM_FORMAT_BDU:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    case VC1_STREAM_FORMAT_BDU_FRAME:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    case VC1_STREAM_FORMAT_ASF:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    case VC1_STREAM_FORMAT_FRAME_LAYER:
+      switch (vc1parse->input_stream_format) {
+        case VC1_STREAM_FORMAT_BDU:
+        case VC1_STREAM_FORMAT_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME:
+        case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER:
+        case VC1_STREAM_FORMAT_ASF:
+        case VC1_STREAM_FORMAT_FRAME_LAYER:
+          break;
+        default:
+          g_assert_not_reached ();
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  return GST_FLOW_OK;
+}
+
+/* SMPTE 421M Table 7 */
+static const struct
+{
+  gint par_n, par_d;
+} aspect_ratios[] = {
+  {
+  0, 0}, {
+  1, 1}, {
+  12, 11}, {
+  10, 11}, {
+  16, 11}, {
+  40, 33}, {
+  24, 11}, {
+  20, 11}, {
+  32, 11}, {
+  80, 33}, {
+  18, 11}, {
+  15, 11}, {
+  64, 33}, {
+  160, 99}, {
+  0, 0}, {
+  0, 0}
+};
+
+/* SMPTE 421M Table 8 */
+static const guint framerates_n[] = {
+  0,
+  24 * 1000,
+  25 * 1000, 30 * 1000, 50 * 1000, 60 * 1000, 48 * 1000, 72 * 1000
+};
+
+/* SMPTE 421M Table 9 */
+static const guint framerates_d[] = {
+  0,
+  1000,
+  1001
+};
+
+static gboolean
+gst_vc1_parse_handle_seq_hdr (GstVC1Parse * vc1parse,
+    GstBuffer * buf, guint offset, guint size)
+{
+  GstVC1ParserResult pres;
+  GstVC1Profile profile;
+  g_assert (GST_BUFFER_SIZE (buf) >= offset + size);
+  gst_buffer_replace (&vc1parse->seq_hdr_buffer, NULL);
+  memset (&vc1parse->seq_hdr, 0, sizeof (vc1parse->seq_hdr));
+  pres =
+      gst_vc1_parse_sequence_header (GST_BUFFER_DATA (buf) + offset,
+      size, &vc1parse->seq_hdr);
+  if (pres != GST_VC1_PARSER_OK) {
+    GST_ERROR_OBJECT (vc1parse, "Invalid VC1 sequence header");
+    return FALSE;
+  }
+  vc1parse->seq_hdr_buffer = gst_buffer_create_sub (buf, offset, size);
+  profile = vc1parse->seq_hdr.profile;
+  if (vc1parse->profile != profile) {
+    vc1parse->update_caps = TRUE;
+    vc1parse->profile = vc1parse->seq_hdr.profile;
+  }
+
+  /* Only update fps if not from caps */
+  if (!vc1parse->fps_from_caps && profile != GST_VC1_PROFILE_ADVANCED) {
+    gint fps;
+    /* This is only an estimate but better than nothing */
+    fps = vc1parse->seq_hdr.struct_c.framerate;
+    if (fps != 0 && (vc1parse->fps_d == 0 ||
+            gst_util_fraction_compare (fps, 1, vc1parse->fps_n,
+                vc1parse->fps_d) != 0)) {
+      vc1parse->update_caps = TRUE;
+      vc1parse->fps_n = fps;
+      vc1parse->fps_d = 1;
+    }
+  }
+
+  if (profile == GST_VC1_PROFILE_ADVANCED) {
+    GstVC1Level level;
+    gint width, height;
+    level = vc1parse->seq_hdr.advanced.level;
+    if (vc1parse->level != level) {
+      vc1parse->update_caps = TRUE;
+      vc1parse->level = level;
+    }
+
+    width = vc1parse->seq_hdr.advanced.max_coded_width;
+    height = vc1parse->seq_hdr.advanced.max_coded_height;
+    if (vc1parse->width != width || vc1parse->height != height) {
+      vc1parse->update_caps = TRUE;
+      vc1parse->width = width;
+      vc1parse->height = height;
+    }
+
+    /* Only update fps if not from caps */
+    if (!vc1parse->fps_from_caps) {
+      gint fps;
+      /* This is only an estimate but better than nothing */
+      fps = vc1parse->seq_hdr.advanced.framerate;
+      if (fps != 0 && (vc1parse->fps_d == 0 ||
+              gst_util_fraction_compare (fps, 1, vc1parse->fps_n,
+                  vc1parse->fps_d) != 0)) {
+        vc1parse->update_caps = TRUE;
+        vc1parse->fps_n = fps;
+        vc1parse->fps_d = 1;
+      }
+    }
+
+    if (vc1parse->seq_hdr.advanced.display_ext) {
+      /* Only update PAR if not from input caps */
+      if (!vc1parse->par_from_caps
+          && vc1parse->seq_hdr.advanced.aspect_ratio_flag) {
+        gint par_n, par_d;
+        if (vc1parse->seq_hdr.advanced.aspect_ratio == 15) {
+          par_n = vc1parse->seq_hdr.advanced.aspect_horiz_size;
+          par_d = vc1parse->seq_hdr.advanced.aspect_vert_size;
+        } else {
+          par_n = aspect_ratios[vc1parse->seq_hdr.advanced.aspect_ratio].par_n;
+          par_d = aspect_ratios[vc1parse->seq_hdr.advanced.aspect_ratio].par_d;
+        }
+
+        if (par_n != 0 && par_d != 0 &&
+            (vc1parse->par_d == 0
+                || gst_util_fraction_compare (par_n, par_d,
+                    vc1parse->par_n, vc1parse->par_d) != 0)) {
+          vc1parse->update_caps = TRUE;
+          vc1parse->par_n = par_n;
+          vc1parse->par_d = par_d;
+        }
+      }
+
+      /* Only update fps if not from caps, better value than above */
+      if (!vc1parse->fps_from_caps && vc1parse->seq_hdr.advanced.framerate_flag) {
+        gint fps_n = 0, fps_d = 0;
+        if (!vc1parse->seq_hdr.advanced.framerateind) {
+          if (vc1parse->seq_hdr.advanced.frameratenr > 0
+              && vc1parse->seq_hdr.advanced.frameratenr < 8
+              && vc1parse->seq_hdr.advanced.frameratedr > 0
+              && vc1parse->seq_hdr.advanced.frameratedr < 3) {
+            fps_n = framerates_n[vc1parse->seq_hdr.advanced.frameratenr];
+            fps_d = framerates_d[vc1parse->seq_hdr.advanced.frameratedr];
+          }
+        } else {
+          fps_n = vc1parse->seq_hdr.advanced.framerateexp + 1;
+          fps_d = 32;
+        }
+
+        if (fps_n != 0 && fps_d != 0 &&
+            (vc1parse->fps_d == 0
+                || gst_util_fraction_compare (fps_n, fps_d,
+                    vc1parse->fps_n, vc1parse->fps_d) != 0)) {
+          vc1parse->update_caps = TRUE;
+          vc1parse->fps_n = fps_n;
+          vc1parse->fps_d = fps_d;
+        }
+      }
+    }
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_vc1_parse_handle_seq_layer (GstVC1Parse * vc1parse,
+    GstBuffer * buf, guint offset, guint size)
+{
+  GstVC1ParserResult pres;
+  GstVC1Profile profile;
+  GstVC1Level level;
+  gint width, height;
+  g_assert (GST_BUFFER_SIZE (buf) >= offset + size);
+  gst_buffer_replace (&vc1parse->seq_layer_buffer, NULL);
+  memset (&vc1parse->seq_layer, 0, sizeof (vc1parse->seq_layer));
+  pres =
+      gst_vc1_parse_sequence_layer (GST_BUFFER_DATA (buf) + offset,
+      size, &vc1parse->seq_layer);
+  if (pres != GST_VC1_PARSER_OK) {
+    GST_ERROR_OBJECT (vc1parse, "Invalid VC1 sequence layer");
+    return FALSE;
+  }
+  vc1parse->seq_layer_buffer = gst_buffer_create_sub (buf, offset, size);
+  profile = vc1parse->seq_layer.struct_c.profile;
+  if (vc1parse->profile != profile) {
+    vc1parse->update_caps = TRUE;
+    vc1parse->profile = vc1parse->seq_layer.struct_c.profile;
+  }
+
+  width = vc1parse->seq_layer.struct_a.vert_size;
+  height = vc1parse->seq_layer.struct_a.horiz_size;
+  if (vc1parse->width != width || vc1parse->height != height) {
+    vc1parse->update_caps = TRUE;
+    vc1parse->width = width;
+    vc1parse->height = height;
+  }
+
+  level = vc1parse->seq_layer.struct_b.level;
+  if (vc1parse->level != level) {
+    vc1parse->update_caps = TRUE;
+    vc1parse->level = level;
+  }
+
+  if (!vc1parse->fps_from_caps) {
+    gint fps;
+    fps = vc1parse->seq_layer.struct_c.framerate;
+    if (fps == 0 || fps == -1)
+      fps = vc1parse->seq_layer.struct_b.framerate;
+    if (fps != 0 && fps != -1 && (vc1parse->fps_d == 0 ||
+            gst_util_fraction_compare (fps, 1, vc1parse->fps_n,
+                vc1parse->fps_d) != 0)) {
+      vc1parse->update_caps = TRUE;
+      vc1parse->fps_n = fps;
+      vc1parse->fps_d = 1;
+    }
+  }
+
+  /* And now update the duration */
+  if (vc1parse->seq_layer.numframes != 0 && vc1parse->seq_layer.numframes != -1)
+    gst_base_parse_set_duration (GST_BASE_PARSE (vc1parse),
+        GST_FORMAT_DEFAULT, vc1parse->seq_layer.numframes, 50);
+  return TRUE;
+}
+
+static gboolean
+gst_vc1_parse_handle_entrypoint (GstVC1Parse * vc1parse,
+    GstBuffer * buf, guint offset, guint size)
+{
+  g_assert (GST_BUFFER_SIZE (buf) >= offset + size);
+  gst_buffer_replace (&vc1parse->entrypoint_buffer, NULL);
+  vc1parse->entrypoint_buffer = gst_buffer_create_sub (buf, offset, size);
+  return TRUE;
+}
+
+static void
+gst_vc1_parse_update_stream_format_properties (GstVC1Parse * vc1parse)
+{
+  if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU
+      || vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU_FRAME) {
+    /* Need at least the 4 bytes start code */
+    gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 4);
+    gst_base_parse_set_syncable (GST_BASE_PARSE (vc1parse), TRUE);
+  } else
+      if (vc1parse->input_stream_format ==
+      VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU
+      || vc1parse->input_stream_format ==
+      VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME) {
+    /* Need at least the 36 bytes sequence layer */
+    gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 36);
+    gst_base_parse_set_syncable (GST_BASE_PARSE (vc1parse), TRUE);
+  } else
+      if (vc1parse->input_stream_format ==
+      VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME
+      || vc1parse->input_stream_format ==
+      VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER) {
+    /* Need at least the 36 bytes sequence layer */
+    gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 36);
+    gst_base_parse_set_syncable (GST_BASE_PARSE (vc1parse), FALSE);
+  } else if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_ASF) {
+    vc1parse->input_stream_format = VC1_STREAM_FORMAT_ASF;
+    /* Need something, assume this is already packetized */
+    gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 1);
+    gst_base_parse_set_syncable (GST_BASE_PARSE (vc1parse), FALSE);
+  } else if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_FRAME_LAYER) {
+    /* Need at least the frame layer header */
+    gst_base_parse_set_min_frame_size (GST_BASE_PARSE (vc1parse), 8);
+    gst_base_parse_set_syncable (GST_BASE_PARSE (vc1parse), FALSE);
+  } else {
+    g_assert_not_reached ();
+  }
+}
+
+static gboolean
+gst_vc1_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
+{
+  GstVC1Parse *vc1parse = GST_VC1_PARSE (parse);
+  GstStructure *s;
+  const GValue *value;
+  GstBuffer *codec_data = NULL;
+  const gchar *stream_format = NULL;
+  const gchar *header_format = NULL;
+  const gchar *profile = NULL;
+  GST_DEBUG_OBJECT (parse, "caps %" GST_PTR_FORMAT, caps);
+  /* Parse the caps to get as much information as possible */
+  s = gst_caps_get_structure (caps, 0);
+  vc1parse->width = 0;
+  gst_structure_get_int (s, "width", &vc1parse->width);
+  vc1parse->height = 0;
+  gst_structure_get_int (s, "height", &vc1parse->height);
+  vc1parse->fps_n = vc1parse->fps_d = 0;
+  vc1parse->fps_from_caps = FALSE;
+  gst_structure_get_fraction (s, "framerate", &vc1parse->fps_n,
+      &vc1parse->fps_d);
+  if (vc1parse->fps_d != 0)
+    vc1parse->fps_from_caps = TRUE;
+  gst_structure_get_fraction (s, "pixel-aspect-ratio",
+      &vc1parse->par_n, &vc1parse->par_d);
+  if (vc1parse->par_n != 0 && vc1parse->par_d != 0)
+    vc1parse->par_from_caps = TRUE;
+  vc1parse->fourcc = 0;
+  gst_structure_get_fourcc (s, "format", &vc1parse->fourcc);
+  vc1parse->profile = -1;
+  profile = gst_structure_get_string (s, "profile");
+  if (profile && strcmp (profile, "simple"))
+    vc1parse->profile = GST_VC1_PROFILE_SIMPLE;
+  else if (profile && strcmp (profile, "main"))
+    vc1parse->profile = GST_VC1_PROFILE_MAIN;
+  else if (profile && strcmp (profile, "advanced"))
+    vc1parse->profile = GST_VC1_PROFILE_ADVANCED;
+  else if (vc1parse->fourcc == GST_MAKE_FOURCC ('W', 'V', 'C', '1'))
+    vc1parse->profile = GST_VC1_PROFILE_ADVANCED;
+  else if (vc1parse->fourcc == GST_MAKE_FOURCC ('W', 'M', 'V', '3'))
+    vc1parse->profile = GST_VC1_PROFILE_MAIN;   /* or SIMPLE */
+  vc1parse->level = -1;
+  vc1parse->detecting_stream_format = FALSE;
+  header_format = gst_structure_get_string (s, "header-format");
+  stream_format = gst_structure_get_string (s, "stream-format");
+  /* Now parse the codec_data */
+  gst_buffer_replace (&vc1parse->seq_layer_buffer, NULL);
+  gst_buffer_replace (&vc1parse->seq_hdr_buffer, NULL);
+  gst_buffer_replace (&vc1parse->entrypoint_buffer, NULL);
+  memset (&vc1parse->seq_layer, 0, sizeof (vc1parse->seq_layer));
+  memset (&vc1parse->seq_hdr, 0, sizeof (vc1parse->seq_hdr));
+  value = gst_structure_get_value (s, "codec_data");
+  if (value != NULL) {
+    codec_data = gst_value_get_buffer (value);
+    if (GST_BUFFER_SIZE (codec_data) == 4) {
+      /* ASF, VC1/WMV3 simple/main profile
+       * This is the sequence header without start codes
+       */
+      if (!gst_vc1_parse_handle_seq_hdr (vc1parse, codec_data,
+              0, GST_BUFFER_SIZE (codec_data)))
+        return FALSE;
+      if (header_format && strcmp (header_format, "asf") != 0)
+        GST_WARNING_OBJECT (vc1parse,
+            "Upstream claimed '%s' header format but 'asf' detected",
+            header_format);
+      vc1parse->input_header_format = VC1_HEADER_FORMAT_ASF;
+    } else
+        if (GST_BUFFER_SIZE (codec_data) == 36 &&
+        GST_BUFFER_DATA (codec_data)[3] == 0xc5) {
+      /* Sequence Layer, SMPTE S421M-2006 Annex L.3 */
+      if (!gst_vc1_parse_handle_seq_layer (vc1parse, codec_data, 0,
+              GST_BUFFER_SIZE (codec_data))) {
+        GST_ERROR_OBJECT (vc1parse, "Invalid VC1 sequence layer");
+        return FALSE;
+      }
+
+      if (header_format && strcmp (header_format, "sequence-layer") != 0)
+        GST_WARNING_OBJECT (vc1parse,
+            "Upstream claimed '%s' header format but 'sequence-layer' detected",
+            header_format);
+      vc1parse->input_header_format = VC1_HEADER_FORMAT_SEQUENCE_LAYER;
+    } else {
+      guint32 start_code;
+      /* ASF, VC1 advanced profile
+       * This should be the
+       * 1) ASF binding byte
+       * 2) Sequence Header with startcode
+       * 3) EntryPoint Header with startcode
+       */
+      if (GST_BUFFER_SIZE (codec_data) < 1 + 4 + 4 + 4 + 2) {
+        GST_ERROR_OBJECT (vc1parse,
+            "Too small for VC1 advanced profile ASF header");
+        return FALSE;
+      }
+
+      /* Some sanity checking */
+      if ((GST_BUFFER_DATA (codec_data)[0] & 0x01) != 0x01) {
+        GST_ERROR_OBJECT (vc1parse,
+            "Invalid binding byte for VC1 advanced profile ASF header");
+        return FALSE;
+      }
+
+      start_code = GST_READ_UINT32_BE (GST_BUFFER_DATA (codec_data) + 1);
+      if (start_code != 0x000010f) {
+        GST_ERROR_OBJECT (vc1parse,
+            "VC1 advanced profile ASF header does not start with SequenceHeader startcode");
+        return FALSE;
+      }
+
+      if (!gst_vc1_parse_handle_bdus (vc1parse, codec_data, 1,
+              GST_BUFFER_SIZE (codec_data) - 1))
+        return FALSE;
+
+      if (!vc1parse->seq_hdr_buffer || !vc1parse->entrypoint_buffer) {
+        GST_ERROR_OBJECT (vc1parse,
+            "Need sequence header and entrypoint header in the codec_data");
+        return FALSE;
+      }
+
+      if (header_format && strcmp (header_format, "asf") != 0)
+        GST_WARNING_OBJECT (vc1parse,
+            "Upstream claimed '%s' header format but 'asf' detected",
+            header_format);
+
+      vc1parse->input_header_format = VC1_HEADER_FORMAT_ASF;
+    }
+  } else {
+    vc1parse->input_header_format = VC1_HEADER_FORMAT_NONE;
+    if (header_format && strcmp (header_format, "sequence-layer") != 0)
+      vc1parse->input_header_format = VC1_HEADER_FORMAT_SEQUENCE_LAYER;
+    else if (header_format && strcmp (header_format, "none") != 0)
+      GST_WARNING_OBJECT (vc1parse,
+          "Upstream claimed '%s' header format but 'none' detected",
+          header_format);
+  }
+
+  /* If no stream-format was set we try to detect it */
+  if (!stream_format) {
+    vc1parse->detecting_stream_format = TRUE;
+  } else {
+    vc1parse->input_stream_format = stream_format_from_string (stream_format);
+    gst_vc1_parse_update_stream_format_properties (vc1parse);
+  }
+
+  vc1parse->renegotiate = TRUE;
+  vc1parse->update_caps = TRUE;
+  return TRUE;
+}
diff --git a/gst/videoparsers/gstvc1parse.h b/gst/videoparsers/gstvc1parse.h
new file mode 100644 (file)
index 0000000..73e3b3f
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#ifndef __GST_VC1_PARSE_H__
+#define __GST_VC1_PARSE_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbaseparse.h>
+#include <gst/codecparsers/gstvc1parser.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VC1_PARSE \
+  (gst_vc1_parse_get_type())
+#define GST_VC1_PARSE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VC1_PARSE,GstVC1Parse))
+#define GST_VC1_PARSE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VC1_PARSE,GstVC1ParseClass))
+#define GST_IS_VC1_PARSE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VC1_PARSE))
+#define GST_IS_VC1_PARSE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VC1_PARSE))
+
+typedef enum {
+  VC1_HEADER_FORMAT_NONE = 0,
+  VC1_HEADER_FORMAT_ASF,
+  VC1_HEADER_FORMAT_SEQUENCE_LAYER
+} VC1HeaderFormat;
+
+typedef enum {
+  VC1_STREAM_FORMAT_BDU = 0,
+  VC1_STREAM_FORMAT_BDU_FRAME,
+  VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU,
+  VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME,
+  VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME,
+  VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER,
+  VC1_STREAM_FORMAT_ASF,
+  VC1_STREAM_FORMAT_FRAME_LAYER
+} VC1StreamFormat;
+
+/* FIXME move into baseparse, or anything equivalent;
+ * see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */
+#define GST_BASE_PARSE_FRAME_FLAG_PARSING   0x10000
+
+typedef struct _GstVC1Parse GstVC1Parse;
+typedef struct _GstVC1ParseClass GstVC1ParseClass;
+
+struct _GstVC1Parse
+{
+  GstBaseParse baseparse;
+
+  /* Caps */
+  GstVC1Profile profile;
+  GstVC1Level level;
+  guint32 fourcc;
+  gint width, height;
+
+  gint fps_n, fps_d;
+  gboolean fps_from_caps;
+  gint par_n, par_d;
+  gboolean par_from_caps;
+
+  /* TRUE if we should negotiate with downstream */
+  gboolean renegotiate;
+  /* TRUE if the srcpads should be updated */
+  gboolean update_caps;
+
+  VC1HeaderFormat input_header_format;
+  VC1HeaderFormat output_header_format;
+  VC1StreamFormat input_stream_format;
+  VC1StreamFormat output_stream_format;
+  gboolean detecting_stream_format;
+
+  GstVC1SeqHdr seq_hdr;
+  GstBuffer *seq_hdr_buffer;
+  GstBuffer *entrypoint_buffer;
+
+  GstVC1SeqLayer seq_layer;
+  GstBuffer *seq_layer_buffer;
+
+  /* Metadata about the currently parsed frame, only
+   * valid if the GstBaseParseFrame has the
+   * GST_BASE_PARSE_FRAME_FLAG_PARSING flag */
+  GstVC1StartCode startcode;
+};
+
+struct _GstVC1ParseClass
+{
+  GstBaseParseClass parent_class;
+};
+
+G_END_DECLS
+
+GType gst_vc1_parse_get_type (void);
+
+#endif /* __GST_VC1_PARSE_H__ */
index 86a4604..29afcbc 100644 (file)
@@ -27,6 +27,7 @@
 #include "gstdiracparse.h"
 #include "gstmpegvideoparse.h"
 #include "gstmpeg4videoparse.h"
+#include "gstvc1parse.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
@@ -43,6 +44,8 @@ plugin_init (GstPlugin * plugin)
       GST_RANK_PRIMARY + 1, GST_TYPE_MPEGVIDEO_PARSE);
   ret |= gst_element_register (plugin, "mpeg4videoparse",
       GST_RANK_PRIMARY + 1, GST_TYPE_MPEG4VIDEO_PARSE);
+  ret |= gst_element_register (plugin, "vc1parse",
+      GST_RANK_PRIMARY + 1, GST_TYPE_VC1_PARSE);
 
   return ret;
 }