diff --git a/gst-libs/gst/audio/gstaudiodecoder.c b/gst-libs/gst/audio/gstaudiodecoder.c
index 88717be419..9bcd9cf746 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 22947481a6..659d28ed23 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 404ca02b15..95f85c65b5 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;
@@ -2135,6 +2152,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 c737e70446..02f1d09fd9 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 e3ca0d6d81..756b16417e 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) {
diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c
index b38747d6f4..f243c2ad24 100644
--- a/gst/playback/gstplaybin2.c
+++ b/gst/playback/gstplaybin2.c
@@ -175,13 +175,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
@@ -196,19 +198,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).
*
*/