| /* |
| * 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/base.h> |
| #include <gst/pbutils/pbutils.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 struct |
| { |
| gchar str[5]; |
| GstVC1ParseFormat en; |
| } parse_formats[] = { |
| { |
| "WMV3", GST_VC1_PARSE_FORMAT_WMV3}, { |
| "WVC1", GST_VC1_PARSE_FORMAT_WVC1} |
| }; |
| |
| 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 const gchar * |
| parse_format_to_string (GstVC1ParseFormat format) |
| { |
| return parse_formats[format].str; |
| } |
| |
| static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("video/x-wmv, wmvversion=(int) 3, " |
| "format=(string) {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=(string) {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}")); |
| |
| |
| #define parent_class gst_vc1_parse_parent_class |
| G_DEFINE_TYPE (GstVC1Parse, gst_vc1_parse, 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 GstFlowReturn gst_vc1_parse_handle_frame (GstBaseParse * parse, |
| GstBaseParseFrame * frame, gint * skipsize); |
| 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, |
| GstCaps * filter); |
| 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_class_init (GstVC1ParseClass * klass) |
| { |
| GObjectClass *gobject_class = (GObjectClass *) klass; |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass); |
| |
| GST_DEBUG_CATEGORY_INIT (vc1_parse_debug, "vc1parse", 0, "vc1 parser"); |
| |
| gobject_class->finalize = gst_vc1_parse_finalize; |
| |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&srctemplate)); |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&sinktemplate)); |
| |
| gst_element_class_set_static_metadata (element_class, "VC1 parser", |
| "Codec/Parser/Converter/Video", |
| "Parses VC1 streams", |
| "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); |
| |
| parse_class->start = GST_DEBUG_FUNCPTR (gst_vc1_parse_start); |
| parse_class->stop = GST_DEBUG_FUNCPTR (gst_vc1_parse_stop); |
| parse_class->handle_frame = GST_DEBUG_FUNCPTR (gst_vc1_parse_handle_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) |
| { |
| /* 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); |
| GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (vc1parse)); |
| GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (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->format = 0; |
| vc1parse->width = 0; |
| vc1parse->height = 0; |
| vc1parse->fps_n = vc1parse->fps_d = 0; |
| vc1parse->frame_duration = GST_CLOCK_TIME_NONE; |
| 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->sent_codec_tag = FALSE; |
| |
| 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); |
| |
| vc1parse->seq_layer_sent = FALSE; |
| vc1parse->frame_layer_first_frame_sent = FALSE; |
| } |
| |
| 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_is_format_allowed (GstVC1Parse * vc1parse) |
| { |
| if (vc1parse->profile == GST_VC1_PROFILE_ADVANCED && |
| vc1parse->output_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME) { |
| GST_ERROR_OBJECT (vc1parse, |
| "sequence-layer-raw-frame is not allowed in advanced profile"); |
| return FALSE; |
| } else if (vc1parse->profile == GST_VC1_PROFILE_SIMPLE && |
| (vc1parse->output_stream_format == VC1_STREAM_FORMAT_BDU || |
| vc1parse->output_stream_format == VC1_STREAM_FORMAT_BDU_FRAME || |
| vc1parse->output_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU || |
| vc1parse->output_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME)) { |
| GST_ERROR_OBJECT (vc1parse, |
| "output stream-format not allowed in simple profile"); |
| return FALSE; |
| } |
| |
| GST_DEBUG_OBJECT (vc1parse, "check output header-format"); |
| switch (vc1parse->output_header_format) { |
| case VC1_HEADER_FORMAT_ASF: |
| case VC1_HEADER_FORMAT_SEQUENCE_LAYER: |
| /* Doesn't make sense to have sequence-layer-* stream-format */ |
| if (vc1parse->output_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU || |
| vc1parse->output_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME || |
| vc1parse->output_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME || |
| vc1parse->output_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER) |
| return FALSE; |
| break; |
| case VC1_HEADER_FORMAT_NONE: |
| /* In simple/main profile, there is no sequence header BDU */ |
| if (vc1parse->profile != GST_VC1_PROFILE_ADVANCED && |
| (vc1parse->output_stream_format == VC1_STREAM_FORMAT_BDU || |
| vc1parse->output_stream_format == VC1_STREAM_FORMAT_BDU_FRAME || |
| vc1parse->output_stream_format == VC1_STREAM_FORMAT_FRAME_LAYER)) |
| return FALSE; |
| |
| /* ASF stream-format doesn't carry sequence header */ |
| if (vc1parse->output_stream_format == VC1_STREAM_FORMAT_ASF) |
| return FALSE; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| |
| if (vc1parse->output_stream_format == vc1parse->input_stream_format) |
| return TRUE; |
| |
| GST_DEBUG_OBJECT (vc1parse, "check stream-format conversion"); |
| switch (vc1parse->output_stream_format) { |
| case VC1_STREAM_FORMAT_BDU: |
| if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU_FRAME || |
| 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 || |
| vc1parse->input_stream_format == VC1_STREAM_FORMAT_FRAME_LAYER) |
| goto conversion_not_supported; |
| break; |
| |
| case VC1_STREAM_FORMAT_BDU_FRAME: |
| if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU || |
| vc1parse->input_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU || |
| vc1parse->input_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER || |
| vc1parse->input_stream_format == VC1_STREAM_FORMAT_ASF || |
| vc1parse->input_stream_format == VC1_STREAM_FORMAT_FRAME_LAYER) |
| goto conversion_not_supported; |
| |
| if (vc1parse->input_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME) |
| return FALSE; |
| break; |
| |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU: |
| if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU_FRAME || |
| 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 || |
| vc1parse->input_stream_format == VC1_STREAM_FORMAT_FRAME_LAYER) |
| goto conversion_not_supported; |
| break; |
| |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME: |
| if (vc1parse->input_stream_format == VC1_STREAM_FORMAT_BDU || |
| vc1parse->input_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU || |
| vc1parse->input_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER || |
| vc1parse->input_stream_format == VC1_STREAM_FORMAT_ASF || |
| vc1parse->input_stream_format == VC1_STREAM_FORMAT_FRAME_LAYER) |
| goto conversion_not_supported; |
| |
| if (vc1parse->input_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME) |
| return FALSE; |
| break; |
| |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME: |
| 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 || |
| vc1parse->input_stream_format == |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER || |
| vc1parse->input_stream_format == VC1_STREAM_FORMAT_FRAME_LAYER) |
| goto conversion_not_supported; |
| break; |
| |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER: |
| if (vc1parse->input_stream_format != VC1_STREAM_FORMAT_FRAME_LAYER && |
| vc1parse->input_stream_format != VC1_STREAM_FORMAT_ASF) |
| goto conversion_not_supported; |
| break; |
| |
| case VC1_STREAM_FORMAT_ASF: |
| goto conversion_not_supported; |
| break; |
| |
| case VC1_STREAM_FORMAT_FRAME_LAYER: |
| if (vc1parse->input_stream_format != |
| VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER && |
| vc1parse->input_stream_format != VC1_STREAM_FORMAT_ASF) |
| goto conversion_not_supported; |
| break; |
| |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| |
| return TRUE; |
| |
| conversion_not_supported: |
| GST_ERROR_OBJECT (vc1parse, "stream conversion not implemented yet"); |
| return FALSE; |
| } |
| |
| static gboolean |
| gst_vc1_parse_renegotiate (GstVC1Parse * vc1parse) |
| { |
| GstCaps *in_caps; |
| GstCaps *allowed_caps; |
| GstCaps *tmp; |
| |
| /* 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); |
| |
| /* Downstream element can have differents caps according to wmv format |
| * so intersect to select the good caps */ |
| in_caps = gst_caps_new_simple ("video/x-wmv", |
| "format", G_TYPE_STRING, parse_format_to_string (vc1parse->format), |
| NULL); |
| |
| tmp = gst_caps_intersect_full (allowed_caps, in_caps, |
| GST_CAPS_INTERSECT_FIRST); |
| gst_caps_unref (in_caps); |
| |
| if (gst_caps_is_empty (tmp)) { |
| GST_ERROR_OBJECT (vc1parse, "Empty caps, downstream doesn't support %s", |
| parse_format_to_string (vc1parse->format)); |
| gst_caps_unref (tmp); |
| return FALSE; |
| } |
| |
| tmp = gst_caps_make_writable (tmp); |
| s = gst_caps_get_structure (tmp, 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); |
| } |
| gst_caps_unref (tmp); |
| } else if (gst_caps_is_empty (allowed_caps)) { |
| GST_ERROR_OBJECT (vc1parse, "Empty caps"); |
| gst_caps_unref (allowed_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); |
| |
| if (!gst_vc1_parse_is_format_allowed (vc1parse)) |
| return FALSE; |
| |
| vc1parse->renegotiate = FALSE; |
| vc1parse->update_caps = TRUE; |
| |
| GST_INFO_OBJECT (vc1parse, "input %s/%s, negotiated %s/%s with downstream", |
| header_format_to_string (vc1parse->input_header_format), |
| stream_format_to_string (vc1parse->input_stream_format), |
| header_format_to_string (vc1parse->output_header_format), |
| stream_format_to_string (vc1parse->output_stream_format)); |
| |
| return TRUE; |
| } |
| |
| static void |
| remove_fields (GstCaps * caps) |
| { |
| guint i, n; |
| |
| n = gst_caps_get_size (caps); |
| for (i = 0; i < n; i++) { |
| GstStructure *s = gst_caps_get_structure (caps, i); |
| |
| gst_structure_remove_field (s, "stream-format"); |
| gst_structure_remove_field (s, "header-format"); |
| } |
| } |
| |
| static GstCaps * |
| gst_vc1_parse_get_sink_caps (GstBaseParse * parse, GstCaps * filter) |
| { |
| GstCaps *peercaps; |
| GstCaps *templ; |
| GstCaps *ret; |
| |
| templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse)); |
| if (filter) { |
| GstCaps *fcopy = gst_caps_copy (filter); |
| /* Remove the fields we convert */ |
| remove_fields (fcopy); |
| peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy); |
| gst_caps_unref (fcopy); |
| } else |
| peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), NULL); |
| |
| if (peercaps) { |
| /* 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); |
| remove_fields (peercaps); |
| |
| ret = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST); |
| gst_caps_unref (peercaps); |
| gst_caps_unref (templ); |
| } else { |
| ret = templ; |
| } |
| |
| if (filter) { |
| GstCaps *tmp = |
| gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST); |
| gst_caps_unref (ret); |
| ret = tmp; |
| } |
| |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_vc1_parse_detect (GstBaseParse * parse, GstBuffer * buffer) |
| { |
| GstVC1Parse *vc1parse = GST_VC1_PARSE (parse); |
| GstMapInfo minfo; |
| guint8 *data; |
| gint size; |
| |
| if (!vc1parse->detecting_stream_format) |
| return GST_FLOW_OK; |
| |
| if (!gst_buffer_map (buffer, &minfo, GST_MAP_READ)) |
| return GST_FLOW_ERROR; |
| |
| data = minfo.data; |
| size = minfo.size; |
| |
| #if 0 |
| /* FIXME: disable BDU check for now as BDU parsing needs more work. |
| */ |
| 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; |
| } |
| #endif |
| |
| while (size >= 40) { |
| if (data[3] == 0xc5 && GST_READ_UINT32_LE (data + 4) == 0x00000004 && |
| GST_READ_UINT32_LE (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_get_size (buffer) <= 128) { |
| GST_DEBUG_OBJECT (vc1parse, "Requesting more data"); |
| gst_buffer_unmap (buffer, &minfo); |
| 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"); |
| gst_buffer_unmap (buffer, &minfo); |
| 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"); |
| gst_buffer_unmap (buffer, &minfo); |
| return GST_FLOW_ERROR; |
| } |
| |
| g_assert_not_reached (); |
| return GST_FLOW_ERROR; |
| |
| detected: |
| |
| gst_buffer_unmap (buffer, &minfo); |
| vc1parse->detecting_stream_format = FALSE; |
| gst_vc1_parse_update_stream_format_properties (vc1parse); |
| return GST_FLOW_OK; |
| } |
| |
| static int |
| gst_vc1_parse_get_max_framerate (GstVC1Parse * vc1parse) |
| { |
| /* http://wiki.multimedia.cx/index.php?title=VC-1#Setup_Data_.2F_Sequence_Layer */ |
| switch (vc1parse->profile) { |
| case GST_VC1_PROFILE_SIMPLE: |
| switch (vc1parse->level) { |
| case GST_VC1_LEVEL_LOW: |
| return 15; |
| case GST_VC1_LEVEL_MEDIUM: |
| return 30; |
| default: |
| g_assert_not_reached (); |
| return 0; |
| } |
| break; |
| case GST_VC1_PROFILE_MAIN: |
| switch (vc1parse->level) { |
| case GST_VC1_LEVEL_LOW: |
| return 24; |
| case GST_VC1_LEVEL_MEDIUM: |
| return 30; |
| case GST_VC1_LEVEL_HIGH: |
| return 30; |
| default: |
| g_assert_not_reached (); |
| return 0; |
| } |
| break; |
| case GST_VC1_PROFILE_ADVANCED: |
| switch (vc1parse->level) { |
| case GST_VC1_LEVEL_L0: |
| return 30; |
| case GST_VC1_LEVEL_L1: |
| return 30; |
| case GST_VC1_LEVEL_L2: |
| return 60; |
| case GST_VC1_LEVEL_L3: |
| return 60; |
| case GST_VC1_LEVEL_L4: |
| return 60; |
| default: |
| g_assert_not_reached (); |
| return 0; |
| } |
| break; |
| default: |
| g_assert_not_reached (); |
| return 0; |
| } |
| } |
| |
| static GstBuffer * |
| gst_vc1_parse_make_sequence_layer (GstVC1Parse * vc1parse) |
| { |
| GstBuffer *seq_layer_buffer; |
| guint8 *data; |
| guint32 structC = 0; |
| GstMapInfo minfo; |
| |
| seq_layer_buffer = gst_buffer_new_and_alloc (36); |
| gst_buffer_map (seq_layer_buffer, &minfo, GST_MAP_WRITE); |
| |
| data = minfo.data; |
| /* According to SMPTE 421M Annex L, the sequence layer shall be |
| * represented as a sequence of 32 bit unsigned integers and each |
| * integers should be serialized in little-endian byte-order except for |
| * STRUCT_C which should be serialized in big-endian byte-order. */ |
| |
| /* Unknown number of frames and start code */ |
| data[0] = 0xff; |
| data[1] = 0xff; |
| data[2] = 0xff; |
| data[3] = 0xc5; |
| |
| /* 0x00000004 */ |
| GST_WRITE_UINT32_LE (data + 4, 4); |
| |
| /* structC */ |
| structC |= (vc1parse->profile << 30); |
| if (vc1parse->profile != GST_VC1_PROFILE_ADVANCED) { |
| /* Build simple/main structC from sequence header */ |
| structC |= (vc1parse->seq_hdr.struct_c.wmvp << 28); |
| structC |= (vc1parse->seq_hdr.struct_c.frmrtq_postproc << 25); |
| structC |= (vc1parse->seq_hdr.struct_c.bitrtq_postproc << 20); |
| structC |= (vc1parse->seq_hdr.struct_c.loop_filter << 19); |
| /* Reserved3 shall be set to zero */ |
| structC |= (vc1parse->seq_hdr.struct_c.multires << 17); |
| /* Reserved4 shall be set to one */ |
| structC |= (1 << 16); |
| structC |= (vc1parse->seq_hdr.struct_c.fastuvmc << 15); |
| structC |= (vc1parse->seq_hdr.struct_c.extended_mv << 14); |
| structC |= (vc1parse->seq_hdr.struct_c.dquant << 12); |
| structC |= (vc1parse->seq_hdr.struct_c.vstransform << 11); |
| /* Reserved5 shall be set to zero */ |
| structC |= (vc1parse->seq_hdr.struct_c.overlap << 9); |
| structC |= (vc1parse->seq_hdr.struct_c.syncmarker << 8); |
| structC |= (vc1parse->seq_hdr.struct_c.rangered << 7); |
| structC |= (vc1parse->seq_hdr.struct_c.maxbframes << 4); |
| structC |= (vc1parse->seq_hdr.struct_c.quantizer << 2); |
| structC |= (vc1parse->seq_hdr.struct_c.finterpflag << 1); |
| /* Reserved6 shall be set to one */ |
| structC |= 1; |
| } |
| GST_WRITE_UINT32_BE (data + 8, structC); |
| |
| /* structA */ |
| if (vc1parse->profile != GST_VC1_PROFILE_ADVANCED) { |
| GST_WRITE_UINT32_LE (data + 12, vc1parse->height); |
| GST_WRITE_UINT32_LE (data + 16, vc1parse->width); |
| } else { |
| GST_WRITE_UINT32_LE (data + 12, 0); |
| GST_WRITE_UINT32_LE (data + 16, 0); |
| } |
| |
| /* 0x0000000c */ |
| GST_WRITE_UINT32_LE (data + 20, 0x0000000c); |
| |
| /* structB */ |
| /* Unknown HRD_BUFFER */ |
| GST_WRITE_UINT24_LE (data + 24, 0); |
| if ((gint) vc1parse->level != -1) |
| data[27] = (vc1parse->level << 5); |
| else |
| data[27] = (0x4 << 5); /* Use HIGH level */ |
| /* Unknown HRD_RATE */ |
| GST_WRITE_UINT32_LE (data + 28, 0); |
| /* Framerate */ |
| if (vc1parse->fps_d == 0) { |
| /* If not known, it seems we need to put in the maximum framerate |
| possible for the profile/level used (this is for RTP |
| (https://tools.ietf.org/html/draft-ietf-avt-rtp-vc1-06#section-6.1), |
| so likely elsewhere too */ |
| GST_WRITE_UINT32_LE (data + 32, gst_vc1_parse_get_max_framerate (vc1parse)); |
| } else { |
| GST_WRITE_UINT32_LE (data + 32, |
| ((guint32) (((gdouble) vc1parse->fps_n) / |
| ((gdouble) vc1parse->fps_d) + 0.5))); |
| } |
| |
| gst_buffer_unmap (seq_layer_buffer, &minfo); |
| |
| return seq_layer_buffer; |
| } |
| |
| static gboolean |
| gst_vc1_parse_update_caps (GstVC1Parse * vc1parse) |
| { |
| GstCaps *caps; |
| GstVC1Profile profile = -1; |
| const gchar *stream_format, *header_format; |
| |
| if (gst_pad_has_current_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); |
| |
| header_format = header_format_to_string (vc1parse->output_header_format); |
| stream_format = stream_format_to_string (vc1parse->output_stream_format); |
| gst_caps_set_simple (caps, "header-format", G_TYPE_STRING, header_format, |
| "stream-format", G_TYPE_STRING, stream_format, 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); |
| |
| vc1parse->frame_duration = gst_util_uint64_scale (GST_SECOND, |
| vc1parse->fps_d, vc1parse->fps_n); |
| } |
| |
| 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 = NULL; |
| /* 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", G_TYPE_STRING, "WVC1", |
| "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", G_TYPE_STRING, "WMV3", |
| "profile", G_TYPE_STRING, profile_str, NULL); |
| |
| if (vc1parse->seq_layer_buffer) { |
| const gchar *level = NULL; |
| 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_copy_region (vc1parse->seq_hdr_buffer, |
| GST_BUFFER_COPY_ALL, 0, 4); |
| } else { |
| GstMapInfo minfo; |
| 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); |
| /* Reserved3 shall be set to zero */ |
| seq_hdr |= (vc1parse->seq_layer.struct_c.multires << 17); |
| /* Reserved4 shall be set to one */ |
| seq_hdr |= (1 << 16); |
| 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); |
| /* Reserved5 shall be set to zero */ |
| 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); |
| /* Reserved6 shall be set to one */ |
| seq_hdr |= 1; |
| codec_data = gst_buffer_new_and_alloc (4); |
| |
| gst_buffer_map (codec_data, &minfo, GST_MAP_WRITE); |
| GST_WRITE_UINT32_BE (minfo.data, seq_hdr); |
| gst_buffer_unmap (codec_data, &minfo); |
| } |
| |
| gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data, |
| NULL); |
| gst_buffer_unref (codec_data); |
| } else { |
| GstBuffer *codec_data; |
| GstMapInfo minfo, sminfo, eminfo; |
| |
| /* 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_get_size (vc1parse->seq_hdr_buffer) + 4 + |
| gst_buffer_get_size (vc1parse->entrypoint_buffer)); |
| |
| gst_buffer_map (codec_data, &minfo, GST_MAP_WRITE); |
| gst_buffer_map (vc1parse->seq_hdr_buffer, &sminfo, GST_MAP_READ); |
| gst_buffer_map (vc1parse->entrypoint_buffer, &eminfo, GST_MAP_READ); |
| |
| if (vc1parse->profile == GST_VC1_PROFILE_SIMPLE) |
| GST_WRITE_UINT8 (minfo.data, 0x29); |
| else |
| GST_WRITE_UINT8 (minfo.data, 0x2b); |
| |
| GST_WRITE_UINT32_BE (minfo.data + 1, 0x0000010f); |
| memcpy (minfo.data + 1 + 4, sminfo.data, sminfo.size); |
| GST_WRITE_UINT32_BE (minfo.data + 1 + 4 + |
| gst_buffer_get_size (vc1parse->seq_hdr_buffer), 0x0000010e); |
| memcpy (minfo.data + 1 + 4 + sminfo.size + 4, eminfo.data, eminfo.size); |
| gst_buffer_unmap (codec_data, &minfo); |
| gst_buffer_unmap (vc1parse->seq_hdr_buffer, &sminfo); |
| gst_buffer_unmap (vc1parse->entrypoint_buffer, &eminfo); |
| |
| 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; |
| |
| codec_data = gst_vc1_parse_make_sequence_layer (vc1parse); |
| |
| 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 inline void |
| calculate_mb_size (GstVC1SeqHdr * seqhdr, guint width, guint height) |
| { |
| seqhdr->mb_width = (width + 15) >> 4; |
| seqhdr->mb_height = (height + 15) >> 4; |
| seqhdr->mb_stride = seqhdr->mb_width + 1; |
| } |
| |
| 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; |
| GstMapInfo minfo; |
| |
| gst_buffer_map (buffer, &minfo, GST_MAP_READ); |
| |
| data = minfo.data + 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 - minfo.data, bdu.size)) { |
| gst_buffer_unmap (buffer, &minfo); |
| return FALSE; |
| } |
| |
| data += bdu.size; |
| size -= bdu.size; |
| } |
| } while (pres == GST_VC1_PARSER_OK && size > 0); |
| |
| gst_buffer_unmap (buffer, &minfo); |
| |
| if (pres != GST_VC1_PARSER_OK) { |
| GST_DEBUG_OBJECT (vc1parse, "Failed to parse BDUs"); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| static GstFlowReturn |
| gst_vc1_parse_handle_frame (GstBaseParse * parse, GstBaseParseFrame * frame, |
| gint * skipsize) |
| { |
| GstVC1Parse *vc1parse = GST_VC1_PARSE (parse); |
| GstBuffer *buffer = frame->buffer; |
| guint8 *data; |
| gsize size; |
| gsize framesize = -1; |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstMapInfo minfo; |
| |
| memset (&minfo, 0, sizeof (minfo)); |
| |
| *skipsize = 0; |
| |
| if (vc1parse->renegotiate |
| || gst_pad_check_reconfigure (GST_BASE_PARSE_SRC_PAD (parse))) { |
| if (!gst_vc1_parse_renegotiate (vc1parse)) { |
| GST_ERROR_OBJECT (vc1parse, "Failed to negotiate with downstream"); |
| ret = GST_FLOW_NOT_NEGOTIATED; |
| goto done; |
| } |
| } |
| |
| if (!gst_buffer_map (buffer, &minfo, GST_MAP_READ)) { |
| GST_ERROR_OBJECT (vc1parse, "Failed to map buffer"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| data = minfo.data; |
| size = minfo.size; |
| |
| /* First check if we have a valid, complete frame here */ |
| 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_LE (data + 4) == 0x00000004 |
| && GST_READ_UINT32_LE (data + 20) == 0x0000000c) { |
| framesize = 36; |
| } else { |
| *skipsize = 1; |
| } |
| } 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 %" G_GSIZE_FORMAT " at offset %" |
| G_GUINT64_FORMAT, size, GST_BUFFER_OFFSET (buffer)); |
| /* XXX: when a buffer contains multiple BDUs, does the first one start with |
| * a startcode? |
| */ |
| 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; |
| } else { |
| framesize = bdu.offset + bdu.size; |
| } |
| break; |
| case GST_VC1_PARSER_BROKEN_DATA: |
| GST_ERROR_OBJECT (vc1parse, "Broken data"); |
| *skipsize = 1; |
| break; |
| case GST_VC1_PARSER_NO_BDU: |
| GST_DEBUG_OBJECT (vc1parse, "Found no BDU startcode"); |
| *skipsize = size - 3; |
| 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; |
| } else { |
| /* Need more data */ |
| *skipsize = 0; |
| } |
| break; |
| case GST_VC1_PARSER_ERROR: |
| GST_ERROR_OBJECT (vc1parse, "Parsing error"); |
| 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; |
| } else { |
| /* frame-layer or sequence-layer-frame-layer */ |
| g_assert (size >= 8); |
| /* Parse frame layer size */ |
| framesize = GST_READ_UINT24_LE (data) + 8; |
| } |
| |
| |
| if (framesize == -1) { |
| GST_DEBUG_OBJECT (vc1parse, "Not a complete frame, skipping %d", *skipsize); |
| ret = GST_FLOW_OK; |
| goto done; |
| } |
| g_assert (*skipsize == 0); |
| |
| /* We have a complete frame at this point */ |
| |
| 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"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| 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"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| } 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"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| } |
| |
| if (!gst_vc1_parse_handle_bdu (vc1parse, startcode, buffer, 4, size - 4)) { |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| } 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"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| if (GST_CLOCK_TIME_IS_VALID (vc1parse->frame_duration)) |
| GST_BUFFER_DURATION (buffer) = vc1parse->frame_duration; |
| |
| /* 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 + 4); |
| |
| startcodes = ((startcode & 0xffffff00) == 0x00000100); |
| } |
| |
| if (startcodes) { |
| if (!gst_vc1_parse_handle_bdus (vc1parse, buffer, 0, size)) { |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| /* For the advanced profile we need a sequence header here */ |
| if (!vc1parse->seq_hdr_buffer) { |
| GST_ERROR_OBJECT (vc1parse, "Need sequence header"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| } else { |
| /* Must be a frame or a frame + field */ |
| /* TODO: Check if keyframe */ |
| } |
| } else { |
| /* In simple/main, we basically have a raw frame, so parse it */ |
| GstVC1ParserResult pres; |
| GstVC1FrameHdr frame_hdr; |
| GstVC1SeqHdr seq_hdr; |
| |
| if (!vc1parse->seq_hdr_buffer) { |
| /* Build seq_hdr from sequence-layer to be able to parse frame */ |
| seq_hdr.profile = vc1parse->profile; |
| seq_hdr.struct_c = vc1parse->seq_layer.struct_c; |
| calculate_mb_size (&seq_hdr, vc1parse->seq_layer.struct_a.horiz_size, |
| vc1parse->seq_layer.struct_a.vert_size); |
| } else { |
| seq_hdr = vc1parse->seq_hdr; |
| } |
| |
| pres = gst_vc1_parse_frame_header (data, size, &frame_hdr, |
| &seq_hdr, NULL); |
| if (pres != GST_VC1_PARSER_OK) { |
| GST_ERROR_OBJECT (vc1parse, "Invalid VC1 frame header"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| if (frame_hdr.ptype == GST_VC1_PICTURE_TYPE_I) |
| GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); |
| else |
| GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); |
| } |
| } 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"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| 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"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| 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"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| 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)) { |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| |
| /* For the advanced profile we need a sequence header here */ |
| if (!vc1parse->seq_hdr_buffer) { |
| GST_ERROR_OBJECT (vc1parse, "Need sequence header"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| } |
| } |
| |
| /* 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) { |
| frame->flags |= GST_BASE_PARSE_FRAME_FLAG_QUEUE; |
| } else { |
| GST_ERROR_OBJECT (vc1parse, "Need entrypoint for the advanced profile"); |
| ret = GST_FLOW_ERROR; |
| goto done; |
| } |
| } |
| |
| if (!gst_vc1_parse_update_caps (vc1parse)) { |
| ret = GST_FLOW_NOT_NEGOTIATED; |
| goto done; |
| } |
| |
| gst_buffer_unmap (buffer, &minfo); |
| memset (&minfo, 0, sizeof (minfo)); |
| GST_DEBUG_OBJECT (vc1parse, "Finishing frame of size %" G_GSIZE_FORMAT, |
| framesize); |
| ret = gst_base_parse_finish_frame (parse, frame, framesize); |
| |
| done: |
| if (minfo.data) |
| gst_buffer_unmap (buffer, &minfo); |
| |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_vc1_parse_push_sequence_layer (GstVC1Parse * vc1parse) |
| { |
| GstBuffer *seq_layer; |
| |
| if ((seq_layer = vc1parse->seq_layer_buffer)) |
| gst_buffer_ref (seq_layer); |
| else |
| seq_layer = gst_vc1_parse_make_sequence_layer (vc1parse); |
| |
| return gst_pad_push (GST_BASE_PARSE_SRC_PAD (vc1parse), seq_layer); |
| } |
| |
| static GstFlowReturn |
| gst_vc1_parse_convert_asf_to_bdu (GstVC1Parse * vc1parse, |
| GstBaseParseFrame * frame) |
| { |
| GstByteWriter bw; |
| GstBuffer *buffer; |
| GstBuffer *tmp; |
| GstMemory *mem; |
| guint8 sc_data[4]; |
| guint32 startcode; |
| gboolean ok; |
| GstFlowReturn ret = GST_FLOW_OK; |
| |
| buffer = frame->buffer; |
| |
| /* Simple profile doesn't have start codes so bdu format is not possible */ |
| if (vc1parse->profile == GST_VC1_PROFILE_SIMPLE) { |
| GST_ERROR_OBJECT (vc1parse, "can't convert to bdu in simple profile"); |
| ret = GST_FLOW_NOT_NEGOTIATED; |
| goto done; |
| } |
| |
| /* ASF frame could have a start code at the beginning or not. So we first |
| * check for a start code if we have at least 4 bytes in the buffer. */ |
| if (gst_buffer_extract (buffer, 0, sc_data, 4) == 4) { |
| startcode = GST_READ_UINT32_BE (sc_data); |
| if (((startcode & 0xffffff00) == 0x00000100)) { |
| /* Start code found */ |
| goto done; |
| } |
| } |
| |
| /* Yes, a frame could be smaller than 4 bytes and valid, for instance |
| * black video. */ |
| |
| /* We will prepend 4 bytes to buffer */ |
| gst_byte_writer_init_with_size (&bw, 4, TRUE); |
| |
| /* Set start code and suffixe, we assume raw asf data is a frame */ |
| ok = gst_byte_writer_put_uint24_be (&bw, 0x000001); |
| ok &= gst_byte_writer_put_uint8 (&bw, 0x0D); |
| tmp = gst_byte_writer_reset_and_get_buffer (&bw); |
| |
| /* Prepend startcode buffer to frame buffer */ |
| mem = gst_buffer_get_all_memory (tmp); |
| gst_buffer_prepend_memory (buffer, mem); |
| gst_buffer_unref (tmp); |
| |
| if (G_UNLIKELY (!ok)) { |
| GST_ERROR_OBJECT (vc1parse, "convert asf to bdu failed"); |
| ret = GST_FLOW_ERROR; |
| } |
| |
| done: |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_vc1_parse_convert_to_frame_layer (GstVC1Parse * vc1parse, |
| GstBaseParseFrame * frame) |
| { |
| GstByteWriter bw; |
| GstBuffer *buffer; |
| GstBuffer *frame_layer; |
| gsize frame_layer_size; |
| GstMemory *mem; |
| gboolean ok; |
| gboolean keyframe; |
| guint8 sc_data[4]; |
| guint32 startcode; |
| |
| buffer = frame->buffer; |
| keyframe = !(GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)); |
| |
| /* We need 8 bytes for frame-layer header */ |
| frame_layer_size = 8; |
| if (vc1parse->profile == GST_VC1_PROFILE_ADVANCED) { |
| if (!vc1parse->frame_layer_first_frame_sent) { |
| /* First frame should contain sequence-header, entry-point and frame */ |
| frame_layer_size += 4 + gst_buffer_get_size (vc1parse->seq_hdr_buffer) |
| + 4 + gst_buffer_get_size (vc1parse->entrypoint_buffer) + 4; |
| } else if (keyframe) { |
| /* Keyframe should contain entry point */ |
| frame_layer_size += 4 + |
| gst_buffer_get_size (vc1parse->entrypoint_buffer) + 4; |
| } |
| } |
| |
| gst_byte_writer_init_with_size (&bw, frame_layer_size, TRUE); |
| |
| /* frame-layer header shall be serialized in little-endian byte order */ |
| ok = gst_byte_writer_put_uint24_le (&bw, gst_buffer_get_size (buffer)); |
| |
| if (keyframe) |
| ok &= gst_byte_writer_put_uint8 (&bw, 0x80); /* keyframe */ |
| else |
| ok &= gst_byte_writer_put_uint8 (&bw, 0x00); |
| |
| ok &= gst_byte_writer_put_uint32_le (&bw, GST_BUFFER_PTS (buffer)); |
| |
| if (vc1parse->profile != GST_VC1_PROFILE_ADVANCED) |
| goto headers_done; |
| |
| if (!vc1parse->frame_layer_first_frame_sent) { |
| /* Write sequence-header start code, sequence-header entrypoint startcode |
| * and entrypoint */ |
| ok &= gst_byte_writer_put_uint32_be (&bw, 0x0000010f); |
| ok &= gst_byte_writer_put_buffer (&bw, vc1parse->seq_hdr_buffer, 0, -1); |
| ok &= gst_byte_writer_put_uint32_be (&bw, 0x0000010e); |
| ok &= gst_byte_writer_put_buffer (&bw, vc1parse->entrypoint_buffer, 0, -1); |
| } else if (keyframe) { |
| /* Write entrypoint startcode and entrypoint */ |
| ok &= gst_byte_writer_put_uint32_be (&bw, 0x0000010e); |
| ok &= gst_byte_writer_put_buffer (&bw, vc1parse->entrypoint_buffer, 0, -1); |
| } |
| |
| /* frame can begin with startcode, in this case, don't prepend it */ |
| if (gst_buffer_extract (buffer, 0, sc_data, 4) == 4) { |
| startcode = GST_READ_UINT32_BE (sc_data); |
| if (((startcode & 0xffffff00) == 0x00000100)) { |
| /* Start code found */ |
| goto headers_done; |
| } |
| } |
| |
| ok &= gst_byte_writer_put_uint32_be (&bw, 0x0000010d); |
| |
| headers_done: |
| frame_layer = gst_byte_writer_reset_and_get_buffer (&bw); |
| mem = gst_buffer_get_all_memory (frame_layer); |
| gst_buffer_prepend_memory (buffer, mem); |
| gst_buffer_unref (frame_layer); |
| |
| if (G_UNLIKELY (!ok)) { |
| GST_ERROR_OBJECT (vc1parse, "failed to convert to frame layer"); |
| return GST_FLOW_ERROR; |
| } |
| |
| vc1parse->frame_layer_first_frame_sent = TRUE; |
| return GST_FLOW_OK; |
| } |
| |
| static GstFlowReturn |
| gst_vc1_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) |
| { |
| GstVC1Parse *vc1parse = GST_VC1_PARSE (parse); |
| GstFlowReturn ret = GST_FLOW_OK; |
| |
| if (!vc1parse->sent_codec_tag) { |
| GstTagList *taglist; |
| GstCaps *caps; |
| |
| /* codec tag */ |
| caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse)); |
| if (G_UNLIKELY (caps == NULL)) { |
| if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) { |
| GST_INFO_OBJECT (parse, "Src pad is flushing"); |
| return GST_FLOW_FLUSHING; |
| } else { |
| GST_INFO_OBJECT (parse, "Src pad is not negotiated!"); |
| return GST_FLOW_NOT_NEGOTIATED; |
| } |
| } |
| |
| taglist = gst_tag_list_new_empty (); |
| gst_pb_utils_add_codec_description_to_tag_list (taglist, |
| GST_TAG_VIDEO_CODEC, caps); |
| gst_caps_unref (caps); |
| |
| gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE); |
| gst_tag_list_unref (taglist); |
| |
| /* also signals the end of first-frame processing */ |
| vc1parse->sent_codec_tag = TRUE; |
| } |
| |
| /* 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: |
| g_assert_not_reached (); |
| break; |
| case VC1_STREAM_FORMAT_BDU_FRAME: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU: |
| /* We just need to drop sequence-layer buffer */ |
| if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_NO_FRAME) { |
| ret = GST_BASE_PARSE_FLOW_DROPPED; |
| } |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME: |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME: |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_ASF: |
| ret = gst_vc1_parse_convert_asf_to_bdu (vc1parse, frame); |
| break; |
| case VC1_STREAM_FORMAT_FRAME_LAYER: |
| goto conversion_not_supported; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| break; |
| |
| case VC1_STREAM_FORMAT_BDU_FRAME: |
| switch (vc1parse->input_stream_format) { |
| case VC1_STREAM_FORMAT_BDU: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_BDU_FRAME: |
| g_assert_not_reached (); |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME: |
| /* We just need to drop sequence-layer buffer */ |
| if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_NO_FRAME) { |
| ret = GST_BASE_PARSE_FLOW_DROPPED; |
| } |
| break; |
| 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: |
| goto conversion_not_supported; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| break; |
| |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU: |
| switch (vc1parse->input_stream_format) { |
| case VC1_STREAM_FORMAT_BDU: |
| /* We just need to send the sequence-layer first */ |
| if (!vc1parse->seq_layer_sent) { |
| ret = gst_vc1_parse_push_sequence_layer (vc1parse); |
| if (ret != GST_FLOW_OK) { |
| GST_ERROR_OBJECT (vc1parse, "push sequence layer failed"); |
| break; |
| } |
| vc1parse->seq_layer_sent = TRUE; |
| } |
| break; |
| case VC1_STREAM_FORMAT_BDU_FRAME: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU: |
| g_assert_not_reached (); |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME: |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME: |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_ASF: |
| /* We just need to send the sequence-layer first */ |
| if (!vc1parse->seq_layer_sent) { |
| ret = gst_vc1_parse_push_sequence_layer (vc1parse); |
| if (ret != GST_FLOW_OK) { |
| GST_ERROR_OBJECT (vc1parse, "push sequence layer failed"); |
| break; |
| } |
| vc1parse->seq_layer_sent = TRUE; |
| } |
| /* FIXME: We may only authorize this when header-format is set to |
| * none and we should add the entrypoint for advanced profile. */ |
| ret = gst_vc1_parse_convert_asf_to_bdu (vc1parse, frame); |
| break; |
| case VC1_STREAM_FORMAT_FRAME_LAYER: |
| goto conversion_not_supported; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| break; |
| |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME: |
| switch (vc1parse->input_stream_format) { |
| case VC1_STREAM_FORMAT_BDU: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_BDU_FRAME: |
| /* We just need to send the sequence-layer first */ |
| if (!vc1parse->seq_layer_sent) { |
| ret = gst_vc1_parse_push_sequence_layer (vc1parse); |
| if (ret != GST_FLOW_OK) { |
| GST_ERROR_OBJECT (vc1parse, "push sequence layer failed"); |
| break; |
| } |
| vc1parse->seq_layer_sent = TRUE; |
| } |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_BDU_FRAME: |
| g_assert_not_reached (); |
| break; |
| 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: |
| goto conversion_not_supported; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| break; |
| |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME: |
| if (vc1parse->profile != GST_VC1_PROFILE_SIMPLE && |
| vc1parse->profile != GST_VC1_PROFILE_MAIN) { |
| GST_ERROR_OBJECT (vc1parse, |
| "sequence-layer-raw-frame is only for simple/main profile"); |
| goto conversion_not_supported; |
| } |
| 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: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_RAW_FRAME: |
| g_assert_not_reached (); |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER: |
| goto conversion_not_supported; |
| case VC1_STREAM_FORMAT_ASF: |
| /* ASF contains raw frame for simple/main profile, so we just |
| * have to send sequence-layer before frames */ |
| if (!vc1parse->seq_layer_sent) { |
| ret = gst_vc1_parse_push_sequence_layer (vc1parse); |
| if (ret != GST_FLOW_OK) { |
| GST_ERROR_OBJECT (vc1parse, "push sequence layer failed"); |
| break; |
| } |
| vc1parse->seq_layer_sent = TRUE; |
| } |
| break; |
| case VC1_STREAM_FORMAT_FRAME_LAYER: |
| goto conversion_not_supported; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| 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: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER: |
| g_assert_not_reached (); |
| break; |
| case VC1_STREAM_FORMAT_ASF: |
| /* Make sure we push the sequence layer */ |
| if (!vc1parse->seq_layer_sent) { |
| ret = gst_vc1_parse_push_sequence_layer (vc1parse); |
| if (ret != GST_FLOW_OK) { |
| GST_ERROR_OBJECT (vc1parse, "push sequence layer failed"); |
| break; |
| } |
| vc1parse->seq_layer_sent = TRUE; |
| } |
| ret = gst_vc1_parse_convert_to_frame_layer (vc1parse, frame); |
| break; |
| case VC1_STREAM_FORMAT_FRAME_LAYER: |
| /* We just need to send the sequence-layer first */ |
| if (!vc1parse->seq_layer_sent) { |
| ret = gst_vc1_parse_push_sequence_layer (vc1parse); |
| if (ret != GST_FLOW_OK) { |
| GST_ERROR_OBJECT (vc1parse, "push sequence layer failed"); |
| break; |
| } |
| vc1parse->seq_layer_sent = TRUE; |
| } |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| 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: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_ASF: |
| g_assert_not_reached (); |
| break; |
| case VC1_STREAM_FORMAT_FRAME_LAYER: |
| goto conversion_not_supported; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| 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: |
| goto conversion_not_supported; |
| break; |
| case VC1_STREAM_FORMAT_SEQUENCE_LAYER_FRAME_LAYER: |
| /* We just need to drop sequence-layer buffer */ |
| if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_NO_FRAME) { |
| ret = GST_BASE_PARSE_FLOW_DROPPED; |
| } |
| break; |
| case VC1_STREAM_FORMAT_ASF: |
| ret = gst_vc1_parse_convert_to_frame_layer (vc1parse, frame); |
| break; |
| case VC1_STREAM_FORMAT_FRAME_LAYER: |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| break; |
| |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| |
| return ret; |
| |
| conversion_not_supported: |
| GST_WARNING_OBJECT (vc1parse, "stream conversion not implemented yet"); |
| return GST_FLOW_NOT_NEGOTIATED; |
| } |
| |
| /* 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; |
| GstMapInfo minfo; |
| |
| g_assert (gst_buffer_get_size (buf) >= offset + size); |
| gst_buffer_replace (&vc1parse->seq_hdr_buffer, NULL); |
| memset (&vc1parse->seq_hdr, 0, sizeof (vc1parse->seq_hdr)); |
| |
| gst_buffer_map (buf, &minfo, GST_MAP_READ); |
| pres = |
| gst_vc1_parse_sequence_header (minfo.data + offset, |
| size, &vc1parse->seq_hdr); |
| gst_buffer_unmap (buf, &minfo); |
| |
| if (pres != GST_VC1_PARSER_OK) { |
| GST_ERROR_OBJECT (vc1parse, "Invalid VC1 sequence header"); |
| return FALSE; |
| } |
| vc1parse->seq_hdr_buffer = |
| gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, 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; |
| GstMapInfo minfo; |
| |
| g_assert (gst_buffer_get_size (buf) >= offset + size); |
| |
| gst_buffer_replace (&vc1parse->seq_layer_buffer, NULL); |
| memset (&vc1parse->seq_layer, 0, sizeof (vc1parse->seq_layer)); |
| |
| gst_buffer_map (buf, &minfo, GST_MAP_READ); |
| pres = |
| gst_vc1_parse_sequence_layer (minfo.data + offset, |
| size, &vc1parse->seq_layer); |
| gst_buffer_unmap (buf, &minfo); |
| |
| if (pres != GST_VC1_PARSER_OK) { |
| GST_ERROR_OBJECT (vc1parse, "Invalid VC1 sequence layer"); |
| return FALSE; |
| } |
| vc1parse->seq_layer_buffer = |
| gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, 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 (width > 0 && height > 0 |
| && (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 && profile != GST_VC1_PROFILE_ADVANCED) { |
| 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_get_size (buf) >= offset + size); |
| |
| gst_buffer_replace (&vc1parse->entrypoint_buffer, NULL); |
| vc1parse->entrypoint_buffer = |
| gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, 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; |
| const gchar *format; |
| |
| 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->format = 0; |
| format = gst_structure_get_string (s, "format"); |
| if (format && strcmp (format, "WVC1") == 0) |
| vc1parse->format = GST_VC1_PARSE_FORMAT_WVC1; |
| else |
| vc1parse->format = GST_VC1_PARSE_FORMAT_WMV3; |
| |
| 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->format == GST_VC1_PARSE_FORMAT_WVC1) |
| vc1parse->profile = GST_VC1_PROFILE_ADVANCED; |
| else if (vc1parse->format == GST_VC1_PARSE_FORMAT_WMV3) |
| 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) { |
| gsize codec_data_size; |
| GstMapInfo minfo; |
| |
| codec_data = gst_value_get_buffer (value); |
| gst_buffer_map (codec_data, &minfo, GST_MAP_READ); |
| codec_data_size = gst_buffer_get_size (codec_data); |
| if ((codec_data_size == 4 || codec_data_size == 5)) { |
| /* 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, codec_data_size)) { |
| gst_buffer_unmap (codec_data, &minfo); |
| 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 (codec_data_size == 36 && minfo.data[3] == 0xc5) { |
| /* Sequence Layer, SMPTE S421M-2006 Annex L.3 */ |
| if (!gst_vc1_parse_handle_seq_layer (vc1parse, codec_data, 0, |
| codec_data_size)) { |
| GST_ERROR_OBJECT (vc1parse, "Invalid VC1 sequence layer"); |
| gst_buffer_unmap (codec_data, &minfo); |
| 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 (codec_data_size < 1 + 4 + 4 + 4 + 2) { |
| GST_ERROR_OBJECT (vc1parse, |
| "Too small for VC1 advanced profile ASF header"); |
| gst_buffer_unmap (codec_data, &minfo); |
| return FALSE; |
| } |
| |
| /* Some sanity checking */ |
| if ((minfo.data[0] & 0x01) != 0x01) { |
| GST_ERROR_OBJECT (vc1parse, |
| "Invalid binding byte for VC1 advanced profile ASF header"); |
| gst_buffer_unmap (codec_data, &minfo); |
| return FALSE; |
| } |
| |
| start_code = GST_READ_UINT32_BE (minfo.data + 1); |
| if (start_code != 0x000010f) { |
| GST_ERROR_OBJECT (vc1parse, |
| "VC1 advanced profile ASF header does not start with SequenceHeader startcode"); |
| gst_buffer_unmap (codec_data, &minfo); |
| return FALSE; |
| } |
| |
| if (!gst_vc1_parse_handle_bdus (vc1parse, codec_data, 1, |
| codec_data_size - 1)) { |
| gst_buffer_unmap (codec_data, &minfo); |
| return FALSE; |
| } |
| |
| if (!vc1parse->seq_hdr_buffer || !vc1parse->entrypoint_buffer) { |
| GST_ERROR_OBJECT (vc1parse, |
| "Need sequence header and entrypoint header in the codec_data"); |
| gst_buffer_unmap (codec_data, &minfo); |
| 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; |
| } |
| gst_buffer_unmap (codec_data, &minfo); |
| } else { |
| vc1parse->input_header_format = VC1_HEADER_FORMAT_NONE; |
| 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; |
| } |