summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Schmidt <jan@centricular.com>2013-12-26 23:21:45 +1100
committerJan Schmidt <jan@centricular.com>2013-12-27 04:04:45 +1100
commitc24a1254c9f1f73e23a1471ce9dd08f513618c8b (patch)
treec68e0e95c92fd70529d191e33ac3bd0750a8e829
parent01c7fb11ba17bd70bd00ab5c2db6639ea9da3458 (diff)
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
-rw-r--r--gst-libs/gst/audio/gstaudiobasesink.c7
-rw-r--r--gst-libs/gst/audio/gstaudiodecoder.c114
2 files changed, 89 insertions, 32 deletions
diff --git a/gst-libs/gst/audio/gstaudiobasesink.c b/gst-libs/gst/audio/gstaudiobasesink.c
index 52dd7ccb0..61d5db4b6 100644
--- a/gst-libs/gst/audio/gstaudiobasesink.c
+++ b/gst-libs/gst/audio/gstaudiobasesink.c
@@ -1029,6 +1029,13 @@ gst_audio_base_sink_wait_event (GstBaseSink * bsink, GstEvent * event)
GstMapInfo minfo;
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, &timestamp, &duration);
/* If the GAP event has a duration, handle it like a
diff --git a/gst-libs/gst/audio/gstaudiodecoder.c b/gst-libs/gst/audio/gstaudiodecoder.c
index c8068716e..7bd338f17 100644
--- a/gst-libs/gst/audio/gstaudiodecoder.c
+++ b/gst-libs/gst/audio/gstaudiodecoder.c
@@ -1732,6 +1732,86 @@ gst_audio_decoder_do_byte (GstAudioDecoder * dec)
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, &timestamp, &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
gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
{
@@ -1819,39 +1899,9 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
ret = TRUE;
break;
}
- case GST_EVENT_GAP:{
- GstClockTime timestamp, duration;
- gst_event_parse_gap (event, &timestamp, &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);
- }
+ case GST_EVENT_GAP:
+ ret = gst_audio_decoder_handle_gap (dec, event);
break;
- }
case GST_EVENT_FLUSH_STOP:
GST_AUDIO_DECODER_STREAM_LOCK (dec);
/* prepare for fresh start */