summaryrefslogtreecommitdiff
path: root/gst-libs/gst
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@collabora.co.uk>2012-02-16 14:23:28 +0100
committerWim Taymans <wim.taymans@collabora.co.uk>2012-02-16 14:23:28 +0100
commite44dd9db8f767f6dd2b157b285774d5938c3fc75 (patch)
treedb6b069e3c71b73db249bc0d2aafe2c20ed6c4db /gst-libs/gst
parentc7d0fb556f88f85bfabebe454d264457ed95afb3 (diff)
parent439884d628fa80e710698e9bb36f063c503b9e79 (diff)
Merge branch 'master' into 0.11
Conflicts: gst-libs/gst/audio/gstaudioencoder.c gst-libs/gst/pbutils/gstdiscoverer.c
Diffstat (limited to 'gst-libs/gst')
-rw-r--r--gst-libs/gst/audio/gstaudiodecoder.c125
-rw-r--r--gst-libs/gst/audio/gstaudiodecoder.h10
-rw-r--r--gst-libs/gst/audio/gstaudioencoder.c123
-rw-r--r--gst-libs/gst/audio/gstaudioencoder.h10
-rw-r--r--gst-libs/gst/pbutils/gstdiscoverer.c25
5 files changed, 290 insertions, 3 deletions
diff --git a/gst-libs/gst/audio/gstaudiodecoder.c b/gst-libs/gst/audio/gstaudiodecoder.c
index 88717be41..9bcd9cf74 100644
--- a/gst-libs/gst/audio/gstaudiodecoder.c
+++ b/gst-libs/gst/audio/gstaudiodecoder.c
@@ -177,6 +177,8 @@ enum
#define DEFAULT_LATENCY 0
#define DEFAULT_TOLERANCE 0
#define DEFAULT_PLC FALSE
+#define DEFAULT_DRAINABLE TRUE
+#define DEFAULT_NEEDS_FORMAT FALSE
typedef struct _GstAudioDecoderContext
{
@@ -258,6 +260,8 @@ struct _GstAudioDecoderPrivate
GstClockTime latency;
GstClockTime tolerance;
gboolean plc;
+ gboolean drainable;
+ gboolean needs_format;
/* pending serialized sink events, will be sent from finish_frame() */
GList *pending_events;
@@ -420,6 +424,8 @@ gst_audio_decoder_init (GstAudioDecoder * dec, GstAudioDecoderClass * klass)
dec->priv->latency = DEFAULT_LATENCY;
dec->priv->tolerance = DEFAULT_TOLERANCE;
dec->priv->plc = DEFAULT_PLC;
+ dec->priv->drainable = DEFAULT_DRAINABLE;
+ dec->priv->needs_format = DEFAULT_NEEDS_FORMAT;
/* init state */
gst_audio_decoder_reset (dec, TRUE);
@@ -1049,6 +1055,7 @@ gst_audio_decoder_push_buffers (GstAudioDecoder * dec, gboolean force)
break;
} else if (ret == GST_FLOW_OK) {
GST_LOG_OBJECT (dec, "frame at offset %d of length %d", offset, len);
+ g_assert (len);
g_assert (offset + len <= av);
priv->sync_flush = 0;
} else {
@@ -1072,6 +1079,10 @@ gst_audio_decoder_push_buffers (GstAudioDecoder * dec, gboolean force)
} else {
if (!force)
break;
+ if (!priv->drainable) {
+ priv->drained = TRUE;
+ break;
+ }
buffer = NULL;
}
@@ -1392,6 +1403,9 @@ gst_audio_decoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
dec = GST_AUDIO_DECODER (parent);
+ if (G_UNLIKELY (!gst_pad_has_current_caps (pad) && dec->priv->needs_format))
+ goto not_negotiated;
+
GST_LOG_OBJECT (dec,
"received buffer of size %" G_GSIZE_FORMAT " with ts %" GST_TIME_FORMAT
", duration %" GST_TIME_FORMAT, gst_buffer_get_size (buffer),
@@ -1429,6 +1443,15 @@ gst_audio_decoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
GST_AUDIO_DECODER_STREAM_UNLOCK (dec);
return ret;
+
+ /* ERRORS */
+not_negotiated:
+ {
+ GST_ELEMENT_ERROR (dec, CORE, NEGOTIATION, (NULL),
+ ("decoder not initialized"));
+ gst_buffer_unref (buffer);
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
}
/* perform upstream byte <-> time conversion (duration, seeking)
@@ -2476,3 +2499,105 @@ gst_audio_decoder_get_tolerance (GstAudioDecoder * dec)
return result;
}
+
+/**
+ * gst_audio_decoder_set_drainable:
+ * @enc: a #GstAudioDecoder
+ * @enabled: new state
+ *
+ * Configures decoder drain handling. If drainable, subclass might
+ * be handed a NULL buffer to have it return any leftover decoded data.
+ * Otherwise, it is not considered so capable and will only ever be passed
+ * real data.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+void
+gst_audio_decoder_set_drainable (GstAudioDecoder * dec, gboolean enabled)
+{
+ g_return_if_fail (GST_IS_AUDIO_DECODER (dec));
+
+ GST_OBJECT_LOCK (dec);
+ dec->priv->drainable = enabled;
+ GST_OBJECT_UNLOCK (dec);
+}
+
+/**
+ * gst_audio_decoder_get_drainable:
+ * @enc: a #GstAudioDecoder
+ *
+ * Queries decoder drain handling.
+ *
+ * Returns: TRUE if drainable handling is enabled.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+gboolean
+gst_audio_decoder_get_drainable (GstAudioDecoder * dec)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_AUDIO_DECODER (dec), 0);
+
+ GST_OBJECT_LOCK (dec);
+ result = dec->priv->drainable;
+ GST_OBJECT_UNLOCK (dec);
+
+ return result;
+}
+
+/**
+ * gst_audio_decoder_set_needs_format:
+ * @enc: a #GstAudioDecoder
+ * @enabled: new state
+ *
+ * Configures decoder format needs. If enabled, subclass needs to be
+ * negotiated with format caps before it can process any data. It will then
+ * never be handed any data before it has been configured.
+ * Otherwise, it might be handed data without having been configured and
+ * is then expected being able to do so either by default
+ * or based on the input data.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+void
+gst_audio_decoder_set_needs_format (GstAudioDecoder * dec, gboolean enabled)
+{
+ g_return_if_fail (GST_IS_AUDIO_DECODER (dec));
+
+ GST_OBJECT_LOCK (dec);
+ dec->priv->needs_format = enabled;
+ GST_OBJECT_UNLOCK (dec);
+}
+
+/**
+ * gst_audio_decoder_get_needs_format:
+ * @enc: a #GstAudioDecoder
+ *
+ * Queries decoder required format handling.
+ *
+ * Returns: TRUE if required format handling is enabled.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+gboolean
+gst_audio_decoder_get_needs_format (GstAudioDecoder * dec)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_AUDIO_DECODER (dec), 0);
+
+ GST_OBJECT_LOCK (dec);
+ result = dec->priv->needs_format;
+ GST_OBJECT_UNLOCK (dec);
+
+ return result;
+}
diff --git a/gst-libs/gst/audio/gstaudiodecoder.h b/gst-libs/gst/audio/gstaudiodecoder.h
index 22947481a..659d28ed2 100644
--- a/gst-libs/gst/audio/gstaudiodecoder.h
+++ b/gst-libs/gst/audio/gstaudiodecoder.h
@@ -296,6 +296,16 @@ void gst_audio_decoder_set_tolerance (GstAudioDecoder * dec,
gint64 gst_audio_decoder_get_tolerance (GstAudioDecoder * dec);
+void gst_audio_decoder_set_drainable (GstAudioDecoder * dec,
+ gboolean enabled);
+
+gboolean gst_audio_decoder_get_drainable (GstAudioDecoder * dec);
+
+void gst_audio_decoder_set_needs_format (GstAudioDecoder * dec,
+ gboolean enabled);
+
+gboolean gst_audio_decoder_get_needs_format (GstAudioDecoder * dec);
+
G_END_DECLS
#endif /* _GST_AUDIO_DECODER_H_ */
diff --git a/gst-libs/gst/audio/gstaudioencoder.c b/gst-libs/gst/audio/gstaudioencoder.c
index 404ca02b1..95f85c65b 100644
--- a/gst-libs/gst/audio/gstaudioencoder.c
+++ b/gst-libs/gst/audio/gstaudioencoder.c
@@ -180,6 +180,8 @@ enum
#define DEFAULT_GRANULE FALSE
#define DEFAULT_HARD_RESYNC FALSE
#define DEFAULT_TOLERANCE 40000000
+#define DEFAULT_HARD_MIN FALSE
+#define DEFAULT_DRAINABLE TRUE
typedef struct _GstAudioEncoderContext
{
@@ -239,6 +241,8 @@ struct _GstAudioEncoderPrivate
gboolean perfect_ts;
gboolean hard_resync;
gboolean granule;
+ gboolean hard_min;
+ gboolean drainable;
/* pending tags */
GstTagList *tags;
@@ -396,6 +400,8 @@ gst_audio_encoder_init (GstAudioEncoder * enc, GstAudioEncoderClass * bclass)
enc->priv->perfect_ts = DEFAULT_PERFECT_TS;
enc->priv->hard_resync = DEFAULT_HARD_RESYNC;
enc->priv->tolerance = DEFAULT_TOLERANCE;
+ enc->priv->hard_min = DEFAULT_HARD_MIN;
+ enc->priv->drainable = DEFAULT_DRAINABLE;
/* init state */
gst_audio_encoder_reset (enc, TRUE);
@@ -769,13 +775,17 @@ gst_audio_encoder_push_buffers (GstAudioEncoder * enc, gboolean force)
}
}
- if (need) {
+ priv->got_data = FALSE;
+ if (G_LIKELY (need)) {
const guint8 *data;
data = gst_adapter_map (priv->adapter, priv->offset + need);
buf =
gst_buffer_new_wrapped_full ((gpointer) data, NULL, priv->offset,
need);
+ } else if (!priv->drainable) {
+ GST_DEBUG_OBJECT (enc, "non-drainable and no more data");
+ goto finish;
}
GST_LOG_OBJECT (enc, "providing subclass with %d bytes at offset %d",
@@ -786,14 +796,21 @@ gst_audio_encoder_push_buffers (GstAudioEncoder * enc, gboolean force)
priv->offset += need;
priv->samples_in += need / ctx->info.bpf;
- priv->got_data = FALSE;
- ret = klass->handle_frame (enc, buf);
+ /* subclass might not want to be bothered with leftover data,
+ * so take care of that here if so, otherwise pass along */
+ if (G_UNLIKELY (priv->force && priv->hard_min && buf)) {
+ GST_DEBUG_OBJECT (enc, "bypassing subclass with leftover");
+ ret = gst_audio_encoder_finish_frame (enc, NULL, -1);
+ } else {
+ ret = klass->handle_frame (enc, buf);
+ }
if (G_LIKELY (buf)) {
gst_buffer_unref (buf);
gst_adapter_unmap (priv->adapter);
}
+ finish:
/* no data to feed, no leftover provided, then bail out */
if (G_UNLIKELY (!buf && !priv->got_data)) {
priv->drained = TRUE;
@@ -2136,6 +2153,106 @@ gst_audio_encoder_get_tolerance (GstAudioEncoder * enc)
}
/**
+ * gst_audio_encoder_set_hard_min:
+ * @enc: a #GstAudioEncoder
+ * @enabled: new state
+ *
+ * Configures encoder hard minimum handling. If enabled, subclass
+ * will never be handed less samples than it configured, which otherwise
+ * might occur near end-of-data handling. Instead, the leftover samples
+ * will simply be discarded.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+void
+gst_audio_encoder_set_hard_min (GstAudioEncoder * enc, gboolean enabled)
+{
+ g_return_if_fail (GST_IS_AUDIO_ENCODER (enc));
+
+ GST_OBJECT_LOCK (enc);
+ enc->priv->hard_min = enabled;
+ GST_OBJECT_UNLOCK (enc);
+}
+
+/**
+ * gst_audio_encoder_get_hard_min:
+ * @enc: a #GstAudioEncoder
+ *
+ * Queries encoder hard minimum handling.
+ *
+ * Returns: TRUE if hard minimum handling is enabled.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+gboolean
+gst_audio_encoder_get_hard_min (GstAudioEncoder * enc)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_AUDIO_ENCODER (enc), 0);
+
+ GST_OBJECT_LOCK (enc);
+ result = enc->priv->hard_min;
+ GST_OBJECT_UNLOCK (enc);
+
+ return result;
+}
+
+/**
+ * gst_audio_encoder_set_drainable:
+ * @enc: a #GstAudioEncoder
+ * @enabled: new state
+ *
+ * Configures encoder drain handling. If drainable, subclass might
+ * be handed a NULL buffer to have it return any leftover encoded data.
+ * Otherwise, it is not considered so capable and will only ever be passed
+ * real data.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+void
+gst_audio_encoder_set_drainable (GstAudioEncoder * enc, gboolean enabled)
+{
+ g_return_if_fail (GST_IS_AUDIO_ENCODER (enc));
+
+ GST_OBJECT_LOCK (enc);
+ enc->priv->drainable = enabled;
+ GST_OBJECT_UNLOCK (enc);
+}
+
+/**
+ * gst_audio_encoder_get_drainable:
+ * @enc: a #GstAudioEncoder
+ *
+ * Queries encoder drain handling.
+ *
+ * Returns: TRUE if drainable handling is enabled.
+ *
+ * MT safe.
+ *
+ * Since: 0.10.36
+ */
+gboolean
+gst_audio_encoder_get_drainable (GstAudioEncoder * enc)
+{
+ gboolean result;
+
+ g_return_val_if_fail (GST_IS_AUDIO_ENCODER (enc), 0);
+
+ GST_OBJECT_LOCK (enc);
+ result = enc->priv->drainable;
+ GST_OBJECT_UNLOCK (enc);
+
+ return result;
+}
+
+/**
* gst_audio_encoder_merge_tags:
* @enc: a #GstAudioEncoder
* @tags: a #GstTagList to merge
diff --git a/gst-libs/gst/audio/gstaudioencoder.h b/gst-libs/gst/audio/gstaudioencoder.h
index c737e7044..02f1d09fd 100644
--- a/gst-libs/gst/audio/gstaudioencoder.h
+++ b/gst-libs/gst/audio/gstaudioencoder.h
@@ -250,6 +250,16 @@ void gst_audio_encoder_set_tolerance (GstAudioEncoder * enc,
gint64 gst_audio_encoder_get_tolerance (GstAudioEncoder * enc);
+void gst_audio_encoder_set_hard_min (GstAudioEncoder * enc,
+ gboolean enabled);
+
+gboolean gst_audio_encoder_get_hard_min (GstAudioEncoder * enc);
+
+void gst_audio_encoder_set_drainable (GstAudioEncoder * enc,
+ gboolean enabled);
+
+gboolean gst_audio_encoder_get_drainable (GstAudioEncoder * enc);
+
void gst_audio_encoder_merge_tags (GstAudioEncoder * enc,
const GstTagList * tags, GstTagMergeMode mode);
diff --git a/gst-libs/gst/pbutils/gstdiscoverer.c b/gst-libs/gst/pbutils/gstdiscoverer.c
index e3ca0d6d8..756b16417 100644
--- a/gst-libs/gst/pbutils/gstdiscoverer.c
+++ b/gst-libs/gst/pbutils/gstdiscoverer.c
@@ -1024,6 +1024,31 @@ discoverer_collect (GstDiscoverer * dc)
if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur)) {
GST_DEBUG ("Got duration %" GST_TIME_FORMAT, GST_TIME_ARGS (dur));
dc->priv->current_info->duration = (guint64) dur;
+ } else {
+ GstStateChangeReturn sret;
+
+ /* Some parsers may not even return a rough estimate right away, e.g.
+ * because they've only processed a single frame so far, so if we
+ * didn't get a duration the first time, spin a bit and try again.
+ * Ugly, but still better than making parsers or other elements return
+ * completely bogus values. We need some API extensions to solve this
+ * better. */
+ GST_INFO ("No duration yet, try a bit harder..");
+ sret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ if (sret != GST_STATE_CHANGE_FAILURE) {
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ g_usleep (G_USEC_PER_SEC / 20);
+ if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur)
+ && dur > 0) {
+ GST_DEBUG ("Got duration %" GST_TIME_FORMAT, GST_TIME_ARGS (dur));
+ dc->priv->current_info->duration = (guint64) dur;
+ break;
+ }
+ }
+ gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ }
}
if (dc->priv->seeking_query) {