diff --git a/gst/videoparsers/Makefile.am b/gst/videoparsers/Makefile.am index 2e2a38b385..0e88f739ff 100644 --- a/gst/videoparsers/Makefile.am +++ b/gst/videoparsers/Makefile.am @@ -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 index 0000000000..33400af4a4 --- /dev/null +++ b/gst/videoparsers/gstvc1parse.c @@ -0,0 +1,1639 @@ +/* + * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. + * Author: Sebastian Dröge , 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 +#include + +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 "); + + 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 index 0000000000..73e3b3f8a6 --- /dev/null +++ b/gst/videoparsers/gstvc1parse.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. + * Author: Sebastian Dröge , 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 +#include +#include + +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__ */ diff --git a/gst/videoparsers/plugin.c b/gst/videoparsers/plugin.c index 86a46041a9..29afcbcc25 100644 --- a/gst/videoparsers/plugin.c +++ b/gst/videoparsers/plugin.c @@ -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; }