diff options
author | Wim Taymans <wim.taymans@collabora.co.uk> | 2012-02-16 14:23:28 +0100 |
---|---|---|
committer | Wim Taymans <wim.taymans@collabora.co.uk> | 2012-02-16 14:23:28 +0100 |
commit | e44dd9db8f767f6dd2b157b285774d5938c3fc75 (patch) | |
tree | db6b069e3c71b73db249bc0d2aafe2c20ed6c4db /gst-libs/gst | |
parent | c7d0fb556f88f85bfabebe454d264457ed95afb3 (diff) | |
parent | 439884d628fa80e710698e9bb36f063c503b9e79 (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.c | 125 | ||||
-rw-r--r-- | gst-libs/gst/audio/gstaudiodecoder.h | 10 | ||||
-rw-r--r-- | gst-libs/gst/audio/gstaudioencoder.c | 123 | ||||
-rw-r--r-- | gst-libs/gst/audio/gstaudioencoder.h | 10 | ||||
-rw-r--r-- | gst-libs/gst/pbutils/gstdiscoverer.c | 25 |
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) { |