audiodecoder: Choose a default initial caps before sending GAP
If there are no caps from the audio decoder when handling a GAP event - as when one is received right at the start on a DVD without initial audio - then choose any default caps for downstream and then send the GAP, so the audio sink has a configured format in which to start the ringbuffer. Also, make the audio sink reject a GAP without caps with a clearer error message. Fixes bug https://bugzilla.gnome.org/show_bug.cgi?id=603921
This commit is contained in:
parent
01c7fb11ba
commit
c24a1254c9
@ -1029,6 +1029,13 @@ gst_audio_base_sink_wait_event (GstBaseSink * bsink, GstEvent * event)
|
|||||||
GstMapInfo minfo;
|
GstMapInfo minfo;
|
||||||
|
|
||||||
spec = &sink->ringbuffer->spec;
|
spec = &sink->ringbuffer->spec;
|
||||||
|
if (G_UNLIKELY (spec->info.rate == 0)) {
|
||||||
|
GST_ELEMENT_ERROR (sink, STREAM, FORMAT, (NULL),
|
||||||
|
("Sink not negotiated before GAP event."));
|
||||||
|
ret = GST_FLOW_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
gst_event_parse_gap (event, ×tamp, &duration);
|
gst_event_parse_gap (event, ×tamp, &duration);
|
||||||
|
|
||||||
/* If the GAP event has a duration, handle it like a
|
/* If the GAP event has a duration, handle it like a
|
||||||
|
@ -1732,6 +1732,86 @@ gst_audio_decoder_do_byte (GstAudioDecoder * dec)
|
|||||||
dec->priv->ctx.info.rate <= dec->priv->samples_out;
|
dec->priv->ctx.info.rate <= dec->priv->samples_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Must be called holding the GST_AUDIO_DECODER_STREAM_LOCK */
|
||||||
|
static gboolean
|
||||||
|
gst_audio_decoder_negotiate_default_caps (GstAudioDecoder * dec)
|
||||||
|
{
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
caps = gst_pad_get_current_caps (dec->srcpad);
|
||||||
|
if (caps && !gst_audio_info_from_caps (&dec->priv->ctx.info, caps))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
caps = gst_pad_get_allowed_caps (dec->srcpad);
|
||||||
|
if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
caps = gst_caps_fixate (caps);
|
||||||
|
if (!caps || !gst_audio_info_from_caps (&dec->priv->ctx.info, caps))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (dec,
|
||||||
|
"Chose default caps %" GST_PTR_FORMAT " for initial gap", caps);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
|
if (!gst_audio_decoder_negotiate_unlocked (dec)) {
|
||||||
|
GST_INFO_OBJECT (dec, "Failed to negotiate default caps for initial gap");
|
||||||
|
gst_pad_mark_reconfigure (dec->srcpad);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_audio_decoder_handle_gap (GstAudioDecoder * dec, GstEvent * event)
|
||||||
|
{
|
||||||
|
gboolean ret;
|
||||||
|
GstClockTime timestamp, duration;
|
||||||
|
|
||||||
|
/* Ensure we have caps first */
|
||||||
|
GST_AUDIO_DECODER_STREAM_LOCK (dec);
|
||||||
|
if (!GST_AUDIO_INFO_IS_VALID (&dec->priv->ctx.info)) {
|
||||||
|
if (!gst_audio_decoder_negotiate_default_caps (dec)) {
|
||||||
|
GST_ELEMENT_ERROR (dec, STREAM, FORMAT, (NULL),
|
||||||
|
("Decoder output not negotiated before GAP event."));
|
||||||
|
GST_AUDIO_DECODER_STREAM_UNLOCK (dec);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GST_AUDIO_DECODER_STREAM_UNLOCK (dec);
|
||||||
|
|
||||||
|
gst_event_parse_gap (event, ×tamp, &duration);
|
||||||
|
|
||||||
|
/* time progressed without data, see if we can fill the gap with
|
||||||
|
* some concealment data */
|
||||||
|
GST_DEBUG_OBJECT (dec,
|
||||||
|
"gap event: plc %d, do_plc %d, position %" GST_TIME_FORMAT
|
||||||
|
" duration %" GST_TIME_FORMAT,
|
||||||
|
dec->priv->plc, dec->priv->ctx.do_plc,
|
||||||
|
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
|
||||||
|
|
||||||
|
if (dec->priv->plc && dec->priv->ctx.do_plc && dec->input_segment.rate > 0.0) {
|
||||||
|
GstAudioDecoderClass *klass = GST_AUDIO_DECODER_GET_CLASS (dec);
|
||||||
|
GstBuffer *buf;
|
||||||
|
|
||||||
|
/* hand subclass empty frame with duration that needs covering */
|
||||||
|
buf = gst_buffer_new ();
|
||||||
|
GST_BUFFER_TIMESTAMP (buf) = timestamp;
|
||||||
|
GST_BUFFER_DURATION (buf) = duration;
|
||||||
|
/* best effort, not much error handling */
|
||||||
|
gst_audio_decoder_handle_frame (dec, klass, buf);
|
||||||
|
ret = TRUE;
|
||||||
|
gst_event_unref (event);
|
||||||
|
} else {
|
||||||
|
/* sub-class doesn't know how to handle empty buffers,
|
||||||
|
* so just try sending GAP downstream */
|
||||||
|
send_pending_events (dec);
|
||||||
|
ret = gst_audio_decoder_push_event (dec, event);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
|
gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
|
||||||
{
|
{
|
||||||
@ -1819,39 +1899,9 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
|
|||||||
ret = TRUE;
|
ret = TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GST_EVENT_GAP:{
|
case GST_EVENT_GAP:
|
||||||
GstClockTime timestamp, duration;
|
ret = gst_audio_decoder_handle_gap (dec, event);
|
||||||
gst_event_parse_gap (event, ×tamp, &duration);
|
|
||||||
|
|
||||||
/* time progressed without data, see if we can fill the gap with
|
|
||||||
* some concealment data */
|
|
||||||
GST_DEBUG_OBJECT (dec,
|
|
||||||
"gap event: plc %d, do_plc %d, position %" GST_TIME_FORMAT
|
|
||||||
" duration %" GST_TIME_FORMAT,
|
|
||||||
dec->priv->plc, dec->priv->ctx.do_plc,
|
|
||||||
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
|
|
||||||
if (dec->priv->plc && dec->priv->ctx.do_plc &&
|
|
||||||
dec->input_segment.rate > 0.0) {
|
|
||||||
GstAudioDecoderClass *klass;
|
|
||||||
GstBuffer *buf;
|
|
||||||
|
|
||||||
klass = GST_AUDIO_DECODER_GET_CLASS (dec);
|
|
||||||
/* hand subclass empty frame with duration that needs covering */
|
|
||||||
buf = gst_buffer_new ();
|
|
||||||
GST_BUFFER_TIMESTAMP (buf) = timestamp;
|
|
||||||
GST_BUFFER_DURATION (buf) = duration;
|
|
||||||
/* best effort, not much error handling */
|
|
||||||
gst_audio_decoder_handle_frame (dec, klass, buf);
|
|
||||||
ret = TRUE;
|
|
||||||
gst_event_unref (event);
|
|
||||||
} else {
|
|
||||||
/* FIXME: sub-class doesn't know how to handle empty buffers,
|
|
||||||
* so just try sending GAP downstream */
|
|
||||||
send_pending_events (dec);
|
|
||||||
ret = gst_audio_decoder_push_event (dec, event);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case GST_EVENT_FLUSH_STOP:
|
case GST_EVENT_FLUSH_STOP:
|
||||||
GST_AUDIO_DECODER_STREAM_LOCK (dec);
|
GST_AUDIO_DECODER_STREAM_LOCK (dec);
|
||||||
/* prepare for fresh start */
|
/* prepare for fresh start */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user