From f268f2be92b2f983e0f45af257afd4fbbc0e5e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 8 Apr 2015 20:45:58 -0700 Subject: [PATCH] videodecoder: Try to invent default caps instead of setting none at all when getting a GAP event before CAPS Otherwise we would forward the GAP event without ever providing any caps, which then would make decodebin expose a srcpad without any caps set. That's confusing for applications and can lead to all kinds of interesting bugs. Instead do the same as already is done in GstAudioDecoder, and try to invent caps based on the sinkpad caps and the caps allowed by downstream and the srcpad template caps. https://bugzilla.gnome.org/show_bug.cgi?id=747190 --- gst-libs/gst/video/gstvideodecoder.c | 107 +++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/gst-libs/gst/video/gstvideodecoder.c b/gst-libs/gst/video/gstvideodecoder.c index 05a57cb837..775ad5fd69 100644 --- a/gst-libs/gst/video/gstvideodecoder.c +++ b/gst-libs/gst/video/gstvideodecoder.c @@ -1027,6 +1027,101 @@ _flush_events (GstPad * pad, GList * events) return NULL; } +/* Must be called holding the GST_VIDEO_DECODER_STREAM_LOCK */ +static gboolean +gst_video_decoder_negotiate_default_caps (GstVideoDecoder * decoder) +{ + GstCaps *caps; + GstVideoCodecState *state; + GstVideoInfo info; + gint i; + gint caps_size; + GstStructure *structure; + + caps = gst_pad_get_allowed_caps (decoder->srcpad); + if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) + goto caps_error; + + /* before fixating, try to use whatever upstream provided */ + caps = gst_caps_make_writable (caps); + caps_size = gst_caps_get_size (caps); + if (decoder->priv->input_state && decoder->priv->input_state->caps) { + GstCaps *sinkcaps = decoder->priv->input_state->caps; + GstStructure *structure = gst_caps_get_structure (sinkcaps, 0); + gint width, height; + gint par_n, par_d; + gint fps_n, fps_d; + + if (gst_structure_get_int (structure, "width", &width)) { + for (i = 0; i < caps_size; i++) { + gst_structure_set (gst_caps_get_structure (caps, i), "width", + G_TYPE_INT, width, NULL); + } + } + + if (gst_structure_get_int (structure, "height", &height)) { + for (i = 0; i < caps_size; i++) { + gst_structure_set (gst_caps_get_structure (caps, i), "height", + G_TYPE_INT, height, NULL); + } + } + + if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) { + for (i = 0; i < caps_size; i++) { + gst_structure_set (gst_caps_get_structure (caps, i), "framerate", + GST_TYPE_FRACTION, fps_n, fps_d, NULL); + } + } + + if (gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_n, + &par_d)) { + for (i = 0; i < caps_size; i++) { + gst_structure_set (gst_caps_get_structure (caps, i), + "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d, NULL); + } + } + } + + for (i = 0; i < caps_size; i++) { + structure = gst_caps_get_structure (caps, i); + /* Random 1280x720@30 for fixation */ + gst_structure_fixate_field_nearest_int (structure, "width", 1280); + gst_structure_fixate_field_nearest_int (structure, "height", 720); + gst_structure_fixate_field_nearest_fraction (structure, + "pixel-aspect-ratio", 1, 1); + gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1); + } + caps = gst_caps_fixate (caps); + structure = gst_caps_get_structure (caps, 0); + + if (!caps || !gst_video_info_from_caps (&info, caps)) + goto caps_error; + + GST_INFO_OBJECT (decoder, + "Chose default caps %" GST_PTR_FORMAT " for initial gap", caps); + state = + gst_video_decoder_set_output_state (decoder, info.finfo->format, + info.width, info.height, decoder->priv->input_state); + gst_video_codec_state_unref (state); + gst_caps_unref (caps); + + if (!gst_video_decoder_negotiate (decoder)) { + GST_INFO_OBJECT (decoder, + "Failed to negotiate default caps for initial gap"); + gst_pad_mark_reconfigure (decoder->srcpad); + return FALSE; + } + + return TRUE; + +caps_error: + { + if (caps) + gst_caps_unref (caps); + return FALSE; + } +} + static gboolean gst_video_decoder_sink_event_default (GstVideoDecoder * decoder, GstEvent * event) @@ -1122,6 +1217,18 @@ gst_video_decoder_sink_event_default (GstVideoDecoder * decoder, flow_ret = gst_video_decoder_drain_out (decoder, FALSE); ret = (flow_ret == GST_FLOW_OK); + /* Ensure we have caps before forwarding the event */ + GST_VIDEO_DECODER_STREAM_LOCK (decoder); + if (!decoder->priv->output_state) { + if (!gst_video_decoder_negotiate_default_caps (decoder)) { + GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); + GST_ELEMENT_ERROR (decoder, STREAM, FORMAT, (NULL), + ("Decoder output not negotiated before GAP event.")); + return FALSE; + } + } + GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); + /* Forward GAP immediately. Everything is drained after * the GAP event and we can forward this event immediately * now without having buffers out of order.