baseaudiodecoder: improve glitch resilience
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Mon, 21 Mar 2011 10:40:31 +0000 (11:40 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Sat, 27 Aug 2011 13:46:59 +0000 (14:46 +0100)
Provide a replacement for GST_ELEMENT_ERROR to avoid aborting at the first
atom out of place, while on the other hand not failing indefinitely.

gst-libs/gst/audio/gstbaseaudiodecoder.c
gst-libs/gst/audio/gstbaseaudiodecoder.h

index 5ebe65581540946823ac3f0029dab21903c2ec14..d36099780ea65a10fe3cae488a847867bbfd8d84 100644 (file)
@@ -215,6 +215,8 @@ struct _GstBaseAudioDecoderPrivate
   guint64 samples_out;
   /* bytes flushed during parsing */
   guint sync_flush;
+  /* error count */
+  gint error_count;
   /* codec id tag */
   GstTagList *taglist;
 
@@ -386,6 +388,7 @@ gst_base_audio_decoder_reset (GstBaseAudioDecoder * dec, gboolean full)
     dec->priv->bytes_in = 0;
     dec->priv->samples_out = 0;
     dec->priv->agg = -1;
+    dec->priv->error_count = 0;
     gst_base_audio_decoder_clear_queues (dec);
 
     g_free (dec->ctx->state.channel_pos);
@@ -786,6 +789,10 @@ gst_base_audio_decoder_finish_frame (GstBaseAudioDecoder * dec, GstBuffer * buf,
     }
     priv->samples += samples;
     priv->samples_out += samples;
+
+    /* we got data, so note things are looking up */
+    if (G_UNLIKELY (dec->priv->error_count))
+      dec->priv->error_count--;
   }
 
   return gst_base_audio_decoder_output (dec, buf);
@@ -985,6 +992,7 @@ gst_base_audio_decoder_flush (GstBaseAudioDecoder * dec, gboolean hard)
     ret = gst_base_audio_decoder_drain (dec);
   } else {
     gst_segment_init (&dec->segment, GST_FORMAT_TIME);
+    dec->priv->error_count = 0;
   }
   /* only bother subclass with flushing if known it is already alive
    * and kicking out stuff */
@@ -1844,3 +1852,23 @@ stop_failed:
     return GST_STATE_CHANGE_FAILURE;
   }
 }
+
+GstFlowReturn
+_gst_base_audio_decoder_error (GstBaseAudioDecoder * dec, gint weight,
+    GQuark domain, gint code, gchar * txt, gchar * dbg, const gchar * file,
+    const gchar * function, gint line)
+{
+  if (txt)
+    GST_WARNING_OBJECT (dec, "error: %s", txt);
+  if (dbg)
+    GST_WARNING_OBJECT (dec, "error: %s", dbg);
+  dec->priv->error_count += weight;
+  dec->priv->discont = TRUE;
+  if (dec->ctx->max_errors < dec->priv->error_count) {
+    gst_element_message_full (GST_ELEMENT (dec), GST_MESSAGE_ERROR,
+        domain, code, txt, dbg, file, function, line);
+    return GST_FLOW_ERROR;
+  } else {
+    return GST_FLOW_OK;
+  }
+}
index 4db0ffd78b6c3f3b0679744443002e66fdf663f7..c257caaf567833234a9721168fcb52edb07b6a59 100644 (file)
@@ -83,6 +83,44 @@ typedef struct _GstBaseAudioDecoderClass GstBaseAudioDecoderClass;
 typedef struct _GstBaseAudioDecoderPrivate GstBaseAudioDecoderPrivate;
 typedef struct _GstBaseAudioDecoderContext GstBaseAudioDecoderContext;
 
+/* do not use this one, use macro below */
+GstFlowReturn _gst_base_audio_decoder_error (GstBaseAudioDecoder *dec, gint weight,
+                                            GQuark domain, gint code,
+                                            gchar *txt, gchar *debug,
+                                            const gchar *file, const gchar *function,
+                                            gint line);
+
+/**
+ * GST_BASE_AUDIO_DECODER_ERROR:
+ * @el:     the base audio decoder element that generates the error
+ * @weight: element defined weight of the error, added to error count
+ * @domain: like CORE, LIBRARY, RESOURCE or STREAM (see #gstreamer-GstGError)
+ * @code:   error code defined for that domain (see #gstreamer-GstGError)
+ * @text:   the message to display (format string and args enclosed in
+ *          parentheses)
+ * @debug:  debugging information for the message (format string and args
+ *          enclosed in parentheses)
+ * @ret:    variable to receive return value
+ *
+ * Utility function that audio decoder elements can use in case they encountered
+ * a data processing error that may be fatal for the current "data unit" but
+ * need not prevent subsequent decoding.  Such errors are counted and if there
+ * are too many, as configured in the context's max_errors, the pipeline will
+ * post an error message and the application will be requested to stop further
+ * media processing.  Otherwise, it is considered a "glitch" and only a warning
+ * is logged. In either case, @ret is set to the proper value to
+ * return to upstream/caller (indicating either GST_FLOW_ERROR or GST_FLOW_OK).
+ */
+#define GST_BASE_AUDIO_DECODER_ERROR(el, w, domain, code, text, debug, ret) \
+G_STMT_START {                                                              \
+  gchar *__txt = _gst_element_error_printf text;                            \
+  gchar *__dbg = _gst_element_error_printf debug;                           \
+  GstBaseAudioDecoder *dec = GST_BASE_AUDIO_DECODER (el);                   \
+  ret = _gst_base_audio_decoder_error (dec, w, GST_ ## domain ## _ERROR,    \
+      GST_ ## domain ## _ERROR_ ## code, __txt, __dbg, __FILE__,            \
+      GST_FUNCTION, __LINE__);                                              \
+} G_STMT_END
+
 /**
  * GstBaseAudioDecoderContext:
  * @state: a #GstAudioState describing input audio format
@@ -111,6 +149,7 @@ struct _GstBaseAudioDecoderContext {
   /* output */
   gboolean do_plc;
   gboolean do_byte_time;
+  gint max_errors;
   /* MT-protected (with LOCK) */
   GstClockTime min_latency;
   GstClockTime max_latency;