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
This commit is contained in:
Sebastian Dröge 2015-04-08 20:45:58 -07:00
parent 3570100b66
commit f268f2be92

View file

@ -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.