From e40ea309721eff8e1168844d81e2ca515e7acb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 14 Feb 2012 19:23:27 +0000 Subject: [PATCH 1/5] discoverer: try harder to obtain a duration if we don't get one right away If we don't get a duration right away, set the pipeline to playing and sleep a bit, then try again. This is ugly, but the least worst we can do right now. The alternative would be to make parsers etc. return some bogus duration estimate even after only having pushed a single frame, for example. Fixes discoverer showing 0 durations for some mp3 and aac files (e.g. soweto-adts.aac). --- gst-libs/gst/pbutils/gstdiscoverer.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/gst-libs/gst/pbutils/gstdiscoverer.c b/gst-libs/gst/pbutils/gstdiscoverer.c index 8d2d03bbd5..ba02cdbea5 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer.c +++ b/gst-libs/gst/pbutils/gstdiscoverer.c @@ -1026,6 +1026,31 @@ discoverer_collect (GstDiscoverer * dc) 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, &format, &dur) && + format == GST_FORMAT_TIME && 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) { From c79d2d314c019221f121d0842240c3153da5bdf0 Mon Sep 17 00:00:00 2001 From: Olivier Aubert Date: Tue, 17 Jan 2012 10:54:48 +0100 Subject: [PATCH 2/5] docs: fix playbin2 documentation about DVD URIs and playbin => playbin2 in example pipelines. https://bugzilla.gnome.org/show_bug.cgi?id=668081 --- gst/playback/gstplaybin2.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c index e01ff6c218..ce429f11a2 100644 --- a/gst/playback/gstplaybin2.c +++ b/gst/playback/gstplaybin2.c @@ -178,13 +178,15 @@ * * Specifying which CD/DVD device to use * The device to use for CDs/DVDs needs to be set on the source element - * playbin creates before it is opened. The only way to do this at the moment - * is to connect to playbin's "notify::source" signal, which will be emitted - * by playbin when it has created the source element for a particular URI. - * In the signal callback you can check if the source element has a "device" - * property and set it appropriately. In future ways might be added to specify - * the device as part of the URI, but at the time of writing this is not - * possible yet. + * playbin creates before it is opened. The most generic way of doing this + * is to connect to playbin's "source-setup" (or "notify::source") signal, + * which will be emitted by playbin2 when it has created the source element + * for a particular URI. In the signal callback you can check if the source + * element has a "device" property and set it appropriately. In some cases + * the device can also be set as part of the URI, but it depends on the + * elements involved if this will work or not. For example, for DVD menu + * playback, the following syntax might work (if the resindvd plugin is used): + * dvd://[/path/to/device] * * * Handling redirects @@ -199,19 +201,19 @@ * * Examples * |[ - * gst-launch -v playbin uri=file:///path/to/somefile.avi + * gst-launch -v playbin2 uri=file:///path/to/somefile.avi * ]| This will play back the given AVI video file, given that the video and * audio decoders required to decode the content are installed. Since no * special audio sink or video sink is supplied (not possible via gst-launch), * playbin will try to find a suitable audio and video sink automatically * using the autoaudiosink and autovideosink elements. * |[ - * gst-launch -v playbin uri=cdda://4 + * gst-launch -v playbin2 uri=cdda://4 * ]| This will play back track 4 on an audio CD in your disc drive (assuming * the drive is detected automatically by the plugin). * |[ - * gst-launch -v playbin uri=dvd://1 - * ]| This will play back title 1 of a DVD in your disc drive (assuming + * gst-launch -v playbin2 uri=dvd:// + * ]| This will play back the DVD in your disc drive (assuming * the drive is detected automatically by the plugin). * */ From 95306e8fef384a23fc2cecbc13ce626d84eccd06 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 16 Feb 2012 12:15:47 +0100 Subject: [PATCH 3/5] audiodecoder: assert some more that subclass parsed frame has proper len --- gst-libs/gst/audio/gstaudiodecoder.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst-libs/gst/audio/gstaudiodecoder.c b/gst-libs/gst/audio/gstaudiodecoder.c index b6c8a3be9d..1a812788fa 100644 --- a/gst-libs/gst/audio/gstaudiodecoder.c +++ b/gst-libs/gst/audio/gstaudiodecoder.c @@ -1000,6 +1000,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 { From 5b4dc0252323a5f77470531a814d0d2cceac0056 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 16 Feb 2012 12:18:03 +0100 Subject: [PATCH 4/5] audioencoder: add some properties to tweak baseclass behaviour ... so subclass can also rely upon never being bothered with less data than it desires or with some NULL buffer it can't do any interesting with. --- gst-libs/gst/audio/gstaudioencoder.c | 123 ++++++++++++++++++++++++++- gst-libs/gst/audio/gstaudioencoder.h | 10 +++ 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioencoder.c b/gst-libs/gst/audio/gstaudioencoder.c index d64bd229f7..b915ae7f0b 100644 --- a/gst-libs/gst/audio/gstaudioencoder.c +++ b/gst-libs/gst/audio/gstaudioencoder.c @@ -184,6 +184,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 { @@ -243,6 +245,8 @@ struct _GstAudioEncoderPrivate gboolean perfect_ts; gboolean hard_resync; gboolean granule; + gboolean hard_min; + gboolean drainable; /* pending tags */ GstTagList *tags; @@ -399,6 +403,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); @@ -755,11 +761,15 @@ gst_audio_encoder_push_buffers (GstAudioEncoder * enc, gboolean force) } } - if (need) { + priv->got_data = FALSE; + if (G_LIKELY (need)) { buf = gst_buffer_new (); GST_BUFFER_DATA (buf) = (guint8 *) gst_adapter_peek (priv->adapter, priv->offset + need) + priv->offset; GST_BUFFER_SIZE (buf) = 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", @@ -770,12 +780,19 @@ 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); + finish: /* no data to feed, no leftover provided, then bail out */ if (G_UNLIKELY (!buf && !priv->got_data)) { priv->drained = TRUE; @@ -2132,6 +2149,106 @@ gst_audio_encoder_get_tolerance (GstAudioEncoder * enc) return result; } +/** + * 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 diff --git a/gst-libs/gst/audio/gstaudioencoder.h b/gst-libs/gst/audio/gstaudioencoder.h index 12873017c3..aa40677222 100644 --- a/gst-libs/gst/audio/gstaudioencoder.h +++ b/gst-libs/gst/audio/gstaudioencoder.h @@ -246,6 +246,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); From 439884d628fa80e710698e9bb36f063c503b9e79 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 16 Feb 2012 12:19:20 +0100 Subject: [PATCH 5/5] audiodecoder: add some properties to tweak baseclass behaviour ... so subclass can also rely upon never being bothered with some NULL buffer it can't do any interesting with, or with any data before it received any format configuration (and setup properly). --- gst-libs/gst/audio/gstaudiodecoder.c | 124 +++++++++++++++++++++++++++ gst-libs/gst/audio/gstaudiodecoder.h | 10 +++ 2 files changed, 134 insertions(+) diff --git a/gst-libs/gst/audio/gstaudiodecoder.c b/gst-libs/gst/audio/gstaudiodecoder.c index 1a812788fa..705197cc67 100644 --- a/gst-libs/gst/audio/gstaudiodecoder.c +++ b/gst-libs/gst/audio/gstaudiodecoder.c @@ -181,6 +181,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 { @@ -262,6 +264,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; @@ -395,6 +399,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); @@ -1024,6 +1030,10 @@ gst_audio_decoder_push_buffers (GstAudioDecoder * dec, gboolean force) } else { if (!force) break; + if (!priv->drainable) { + priv->drained = TRUE; + break; + } buffer = NULL; } @@ -1344,6 +1354,9 @@ gst_audio_decoder_chain (GstPad * pad, GstBuffer * buffer) dec = GST_AUDIO_DECODER (GST_PAD_PARENT (pad)); + if (G_UNLIKELY (!GST_PAD_CAPS (pad) && dec->priv->needs_format)) + goto not_negotiated; + GST_LOG_OBJECT (dec, "received buffer of size %d with ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, GST_BUFFER_SIZE (buffer), @@ -1381,6 +1394,15 @@ gst_audio_decoder_chain (GstPad * pad, 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) @@ -2461,3 +2483,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 a6ae61803e..ba4fff3484 100644 --- a/gst-libs/gst/audio/gstaudiodecoder.h +++ b/gst-libs/gst/audio/gstaudiodecoder.h @@ -293,6 +293,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_ */