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). * */