#include <gst/gst.h>
#include <gst/gst-i18n-plugin.h>
+#include <gst/pbutils/missing-plugins.h>
#include "gstfactorylists.h"
#include "gstplay-marshal.h"
#include "gstplay-enum.h"
+#include "gstrawcaps.h"
#define GST_TYPE_URI_DECODE_BIN \
(gst_uri_decode_bin_get_type())
GMutex *lock; /* lock for constructing */
+ GMutex *factories_lock;
+ guint32 factories_cookie;
GValueArray *factories; /* factories we can use for selecting elements */
gchar *uri;
gboolean use_buffering;
GstElement *source;
+ GstElement *queue;
GstElement *typefind;
guint have_type_id; /* have-type signal id from typefind */
GSList *decodebins;
GST_PAD_SOMETIMES,
GST_STATIC_CAPS_ANY);
+static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
+
GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin_debug);
#define GST_CAT_DEFAULT gst_uri_decode_bin_debug
#define DEFAULT_PROP_URI NULL
#define DEFAULT_PROP_SOURCE NULL
#define DEFAULT_CONNECTION_SPEED 0
-#define DEFAULT_CAPS NULL
+#define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps))
#define DEFAULT_SUBTITLE_ENCODING NULL
#define DEFAULT_BUFFER_DURATION -1
#define DEFAULT_BUFFER_SIZE -1
static guint gst_uri_decode_bin_signals[LAST_SIGNAL] = { 0 };
+GType gst_uri_decode_bin_get_type (void);
GST_BOILERPLATE (GstURIDecodeBin, gst_uri_decode_bin, GstBin, GST_TYPE_BIN);
static void remove_decoders (GstURIDecodeBin * bin, gboolean force);
return TRUE;
}
+/* Must be called with factories lock! */
+static void
+gst_uri_decode_bin_update_factories_list (GstURIDecodeBin * dec)
+{
+ if (!dec->factories ||
+ dec->factories_cookie !=
+ gst_default_registry_get_feature_list_cookie ()) {
+ if (dec->factories)
+ g_value_array_free (dec->factories);
+ dec->factories = gst_factory_list_get_elements (GST_FACTORY_LIST_DECODER);
+ dec->factories_cookie = gst_default_registry_get_feature_list_cookie ();
+ }
+}
+
static GValueArray *
gst_uri_decode_bin_autoplug_factories (GstElement * element, GstPad * pad,
GstCaps * caps)
{
GValueArray *result;
+ GstURIDecodeBin *dec = GST_URI_DECODE_BIN_CAST (element);
GST_DEBUG_OBJECT (element, "finding factories");
/* return all compatible factories for caps */
- result =
- gst_factory_list_filter (GST_URI_DECODE_BIN_CAST (element)->factories,
- caps);
+ g_mutex_lock (dec->factories_lock);
+ gst_uri_decode_bin_update_factories_list (dec);
+ result = gst_factory_list_filter (dec->factories, caps);
+ g_mutex_unlock (dec->factories_lock);
GST_DEBUG_OBJECT (element, "autoplug-factories returns %p", result);
gst_uri_decode_bin_init (GstURIDecodeBin * dec, GstURIDecodeBinClass * klass)
{
/* first filter out the interesting element factories */
- dec->factories = gst_factory_list_get_elements (GST_FACTORY_LIST_DECODER);
+ dec->factories_lock = g_mutex_new ();
+ gst_uri_decode_bin_update_factories_list (dec);
dec->lock = g_mutex_new ();
remove_decoders (dec, TRUE);
g_mutex_free (dec->lock);
+ g_mutex_free (dec->factories_lock);
g_free (dec->uri);
g_free (dec->encoding);
if (dec->factories)
gchar *capsstr;
capsstr = gst_caps_to_string (caps);
- GST_ELEMENT_WARNING (decoder, CORE, MISSING_PLUGIN,
+ GST_ELEMENT_WARNING (decoder, STREAM, CODEC_NOT_FOUND,
(_("No decoder available for type \'%s\'."), capsstr), (NULL));
g_free (capsstr);
}
GST_DEBUG_OBJECT (bin, "No more pads in source element %s.",
GST_ELEMENT_NAME (element));
- g_signal_handler_disconnect (G_OBJECT (element), bin->src_np_sig_id);
+ g_signal_handler_disconnect (element, bin->src_np_sig_id);
bin->src_np_sig_id = 0;
- g_signal_handler_disconnect (G_OBJECT (element), bin->src_nmp_sig_id);
+ g_signal_handler_disconnect (element, bin->src_nmp_sig_id);
bin->src_nmp_sig_id = 0;
no_more_pads_full (element, FALSE, bin);
gst_element_add_pad (GST_ELEMENT_CAST (decoder), newpad);
}
+
+static gboolean
+source_pad_event_probe (GstPad * pad, GstEvent * event,
+ GstURIDecodeBin * decoder)
+{
+ GST_LOG_OBJECT (pad, "%s, decoder %p", GST_EVENT_TYPE_NAME (event), decoder);
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+ GST_DEBUG_OBJECT (pad, "we received EOS");
+
+ g_signal_emit (decoder,
+ gst_uri_decode_bin_signals[SIGNAL_DRAINED], 0, NULL);
+ }
+ /* never drop events */
+ return TRUE;
+}
+
+/* called when we found a raw pad on the source element. We need to set up a
+ * padprobe to detect EOS before exposing the pad. */
+static void
+expose_decoded_pad (GstElement * element, GstPad * pad,
+ GstURIDecodeBin * decoder)
+{
+ gst_pad_add_event_probe (pad, G_CALLBACK (source_pad_event_probe), decoder);
+
+ new_decoded_pad_cb (element, pad, FALSE, decoder);
+}
+
static void
pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin * decoder)
{
};
#endif
-/* media types we consider raw media */
-static const gchar *raw_media[] = {
- "audio/x-raw", "video/x-raw", "text/plain", "text/x-pango-markup",
- "video/x-dvd-subpicture", "subpicture/x-", NULL
-};
-
/* media types we can download */
static const gchar *download_media[] = {
"video/quicktime", "video/x-flv", NULL
#define IS_QUEUE_URI(uri) (array_has_uri_value (queue_uris, uri))
#define IS_BLACKLISTED_URI(uri) (array_has_uri_value (blacklisted_uris, uri))
#define IS_NO_MEDIA_MIME(mime) (array_has_value (no_media_mimes, mime))
-#define IS_RAW_MEDIA(media) (array_has_value (raw_media, media))
#define IS_DOWNLOAD_MEDIA(media) (array_has_value (download_media, media))
/*
"subtitle-encoding")) {
GST_DEBUG_OBJECT (decoder,
"setting subtitle-encoding=%s to source element", decoder->encoding);
- g_object_set (G_OBJECT (source), "subtitle-encoding", decoder->encoding,
- NULL);
+ g_object_set (source, "subtitle-encoding", decoder->encoding, NULL);
}
return source;
/* whoops, could not create the source element, dig a little deeper to
* figure out what might be wrong. */
if (prot) {
- GST_ELEMENT_ERROR (decoder, RESOURCE, FAILED,
+ GstMessage *msg;
+
+ msg =
+ gst_missing_uri_source_message_new (GST_ELEMENT_CAST (decoder), prot);
+ gst_element_post_message (GST_ELEMENT_CAST (decoder), msg);
+
+ GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN,
(_("No URI handler implemented for \"%s\"."), prot), (NULL));
g_free (prot);
} else
* Returns: %FALSE @pad has no caps. Else TRUE and @all_raw set t the result.
*/
static gboolean
-has_all_raw_caps (GstPad * pad, gboolean * all_raw)
+has_all_raw_caps (GstPad * pad, GstCaps * rawcaps, gboolean * all_raw)
{
- GstCaps *caps;
+ GstCaps *caps, *intersection;
gint capssize;
- guint i, num_raw = 0;
gboolean res = FALSE;
- caps = gst_pad_get_caps (pad);
+ caps = gst_pad_get_caps_reffed (pad);
if (caps == NULL)
return FALSE;
if (capssize == 0 || gst_caps_is_empty (caps) || gst_caps_is_any (caps))
goto done;
- /* count the number of raw formats in the caps */
- for (i = 0; i < capssize; ++i) {
- GstStructure *s;
- const gchar *media_type;
+ intersection = gst_caps_intersect (caps, rawcaps);
+ *all_raw = !gst_caps_is_empty (intersection)
+ && (gst_caps_get_size (intersection) == capssize);
+ gst_caps_unref (intersection);
- s = gst_caps_get_structure (caps, i);
- media_type = gst_structure_get_name (s);
-
- GST_DEBUG_OBJECT (pad, "check media-type %s", media_type);
-
- if (IS_RAW_MEDIA (media_type))
- ++num_raw;
- }
-
- *all_raw = (num_raw == capssize);
res = TRUE;
done:
return res;
}
+static void
+post_missing_plugin_error (GstElement * dec, const gchar * element_name)
+{
+ GstMessage *msg;
+
+ msg = gst_missing_element_message_new (dec, element_name);
+ gst_element_post_message (dec, msg);
+
+ GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN,
+ (_("Missing element '%s' - check your GStreamer installation."),
+ element_name), (NULL));
+}
+
/**
* analyse_source:
* @decoder: a #GstURIDecodeBin
GstIterator *pads_iter;
gboolean done = FALSE;
gboolean res = TRUE;
+ GstCaps *rawcaps;
+ GstPad *pad;
*have_out = FALSE;
*is_raw = FALSE;
*is_dynamic = FALSE;
+ g_object_get (decoder, "caps", &rawcaps, NULL);
+ if (!rawcaps)
+ rawcaps = DEFAULT_CAPS;
+
pads_iter = gst_element_iterate_src_pads (decoder->source);
while (!done) {
- GstPad *pad;
-
switch (gst_iterator_next (pads_iter, (gpointer) & pad)) {
case GST_ITERATOR_ERROR:
res = FALSE;
*have_out = TRUE;
/* if FALSE, this pad has no caps and we continue with the next pad. */
- if (!has_all_raw_caps (pad, is_raw)) {
+ if (!has_all_raw_caps (pad, rawcaps, is_raw)) {
gst_object_unref (pad);
break;
}
GstPad *sinkpad;
/* insert a queue element right before the raw pad */
- outelem = gst_element_factory_make ("queue2", "queue");
+ outelem = gst_element_factory_make ("queue2", NULL);
+ if (!outelem)
+ goto no_queue2;
+
gst_bin_add (GST_BIN_CAST (decoder), outelem);
sinkpad = gst_element_get_static_pad (outelem, "sink");
gst_object_unref (sinkpad);
gst_object_unref (pad);
+ /* save queue pointer so we can remove it later */
+ decoder->queue = outelem;
+
/* get the new raw srcpad */
pad = gst_element_get_static_pad (outelem, "src");
} else {
outelem = decoder->source;
}
- new_decoded_pad_cb (outelem, pad, FALSE, decoder);
+ expose_decoded_pad (outelem, pad, decoder);
}
gst_object_unref (pad);
break;
}
}
gst_iterator_free (pads_iter);
+ gst_caps_unref (rawcaps);
if (!*have_out) {
GstElementClass *elemclass;
}
return res;
+no_queue2:
+ {
+ post_missing_plugin_error (GST_ELEMENT_CAST (decoder), "queue2");
+
+ gst_object_unref (pad);
+ gst_iterator_free (pads_iter);
+ gst_caps_unref (rawcaps);
+
+ return FALSE;
+ }
}
/* Remove all decodebin2 from ourself
gst_element_set_state (decoder, GST_STATE_NULL);
gst_bin_remove (GST_BIN_CAST (bin), decoder);
} else {
+ GstCaps *caps;
+
gst_element_set_state (decoder, GST_STATE_READY);
/* add it to our list of pending decodebins */
g_object_ref (decoder);
gst_bin_remove (GST_BIN_CAST (bin), decoder);
+ /* restore some properties we might have changed */
+ g_object_set (decoder, "sink-caps", NULL, NULL);
+ caps = DEFAULT_CAPS;
+ g_object_set (decoder, "caps", caps, NULL);
+ gst_caps_unref (caps);
+
bin->pending_decodebins =
g_slist_prepend (bin->pending_decodebins, decoder);
}
{
GST_DEBUG_OBJECT (dec, "unknown-type signaled");
- g_signal_emit (G_OBJECT (dec),
+ g_signal_emit (dec,
gst_uri_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps);
}
{
gboolean result;
- g_signal_emit (G_OBJECT (dec),
+ g_signal_emit (dec,
gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE], 0, pad, caps,
&result);
{
GValueArray *result;
- g_signal_emit (G_OBJECT (dec),
+ g_signal_emit (dec,
gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_FACTORIES], 0, pad, caps,
&result);
{
GstAutoplugSelectResult result;
- g_signal_emit (G_OBJECT (dec),
+ g_signal_emit (dec,
gst_uri_decode_bin_signals[SIGNAL_AUTOPLUG_SELECT], 0, pad, caps, factory,
&result);
{
GST_DEBUG_OBJECT (dec, "drained signaled");
- g_signal_emit (G_OBJECT (dec),
- gst_uri_decode_bin_signals[SIGNAL_DRAINED], 0, NULL);
+ g_signal_emit (dec, gst_uri_decode_bin_signals[SIGNAL_DRAINED], 0, NULL);
}
/* make a decodebin and connect to all the signals */
if (!decodebin)
goto no_decodebin;
/* connect signals to proxy */
- g_signal_connect (G_OBJECT (decodebin), "unknown-type",
+ g_signal_connect (decodebin, "unknown-type",
G_CALLBACK (proxy_unknown_type_signal), decoder);
- g_signal_connect (G_OBJECT (decodebin), "autoplug-continue",
+ g_signal_connect (decodebin, "autoplug-continue",
G_CALLBACK (proxy_autoplug_continue_signal), decoder);
- g_signal_connect (G_OBJECT (decodebin), "autoplug-factories",
+ g_signal_connect (decodebin, "autoplug-factories",
G_CALLBACK (proxy_autoplug_factories_signal), decoder);
- g_signal_connect (G_OBJECT (decodebin), "autoplug-select",
+ g_signal_connect (decodebin, "autoplug-select",
G_CALLBACK (proxy_autoplug_select_signal), decoder);
- g_signal_connect (G_OBJECT (decodebin), "drained",
+ g_signal_connect (decodebin, "drained",
G_CALLBACK (proxy_drained_signal), decoder);
/* set up callbacks to create the links between decoded data
* and video/audio/subtitle rendering/output. */
- g_signal_connect (G_OBJECT (decodebin),
+ g_signal_connect (decodebin,
"new-decoded-pad", G_CALLBACK (new_decoded_pad_cb), decoder);
- g_signal_connect (G_OBJECT (decodebin),
+ g_signal_connect (decodebin,
"pad-removed", G_CALLBACK (pad_removed_cb), decoder);
- g_signal_connect (G_OBJECT (decodebin), "no-more-pads",
+ g_signal_connect (decodebin, "no-more-pads",
G_CALLBACK (no_more_pads), decoder);
- g_signal_connect (G_OBJECT (decodebin),
+ g_signal_connect (decodebin,
"unknown-type", G_CALLBACK (unknown_type_cb), decoder);
}
if (decoder->caps)
g_object_set (decodebin, "caps", decoder->caps, NULL);
- if (!decoder->is_download) {
+ if (!decoder->is_stream) {
/* propagate the use-buffering property but only when we are not already
- * doing download buffering. */
+ * doing stream buffering with queue2. FIXME, we might want to do stream
+ * buffering with the multiqueue buffering instead of queue2. */
g_object_set (decodebin, "use-buffering", decoder->use_buffering, NULL);
if (decoder->use_buffering) {
}
g_object_set_data (G_OBJECT (decodebin), "pending", "1");
- g_object_set (G_OBJECT (decodebin), "subtitle-encoding", decoder->encoding,
- NULL);
+ g_object_set (decodebin, "subtitle-encoding", decoder->encoding, NULL);
decoder->pending++;
GST_LOG_OBJECT (decoder, "have %d pending dynamic objects", decoder->pending);
/* ERRORS */
no_decodebin:
{
- GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN,
- (_("Could not create \"decodebin2\" element.")), (NULL));
+ post_missing_plugin_error (GST_ELEMENT_CAST (decoder), "decodebin2");
return NULL;
}
}
+/* signaled when we have a stream and we need to configure the download
+ * buffering or regular buffering */
static void
type_found (GstElement * typefind, guint probability,
GstCaps * caps, GstURIDecodeBin * decoder)
if (!queue)
goto no_queue2;
- g_object_set (G_OBJECT (queue), "use-buffering", TRUE, NULL);
+ g_object_set (queue, "use-buffering", TRUE, NULL);
GST_DEBUG_OBJECT (decoder, "check media-type %s, %d", media_type,
decoder->download);
temp_template, tmp_dir, prgname, filename);
/* configure progressive download for selected media types */
- g_object_set (G_OBJECT (queue), "temp-template", temp_template, NULL);
+ g_object_set (queue, "temp-template", temp_template, NULL);
g_free (filename);
g_free (temp_template);
}
/* Disable max-size-buffers */
- g_object_set (G_OBJECT (queue), "max-size-buffers", 0, NULL);
+ g_object_set (queue, "max-size-buffers", 0, NULL);
/* If buffer size or duration are set, set them on the queue2 element */
if (decoder->buffer_size != -1)
- g_object_set (G_OBJECT (queue), "max-size-bytes",
- decoder->buffer_size, NULL);
+ g_object_set (queue, "max-size-bytes", decoder->buffer_size, NULL);
if (decoder->buffer_duration != -1)
- g_object_set (G_OBJECT (queue), "max-size-time",
- decoder->buffer_duration, NULL);
+ g_object_set (queue, "max-size-time", decoder->buffer_duration, NULL);
gst_bin_add (GST_BIN_CAST (decoder), queue);
/* to force caps on the decodebin element and avoid reparsing stuff by
* typefind. It also avoids a deadlock in the way typefind activates pads in
* the state change */
- g_object_set (G_OBJECT (dec_elem), "sink-caps", caps, NULL);
+ g_object_set (dec_elem, "sink-caps", caps, NULL);
if (!gst_element_link_pads (queue, "src", dec_elem, "sink"))
goto could_not_link;
}
no_queue2:
{
- GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN,
- (_("Could not create \"queue2\" element.")), (NULL));
+ post_missing_plugin_error (GST_ELEMENT_CAST (decoder), "queue2");
return;
}
}
gst_bin_add (GST_BIN_CAST (decoder), typefind);
- if (!gst_element_link (decoder->source, typefind))
+ if (!gst_element_link_pads (decoder->source, NULL, typefind, "sink"))
goto could_not_link;
decoder->typefind = typefind;
/* connect a signal to find out when the typefind element found
* a type */
decoder->have_type_id =
- g_signal_connect (G_OBJECT (decoder->typefind), "have-type",
+ g_signal_connect (decoder->typefind, "have-type",
G_CALLBACK (type_found), decoder);
do_async_start (decoder);
/* ERRORS */
no_typefind:
{
- GST_ELEMENT_ERROR (decoder, CORE, MISSING_PLUGIN,
- (_("Could not create \"typefind\" element.")), (NULL));
+ post_missing_plugin_error (GST_ELEMENT_CAST (decoder), "typefind");
return FALSE;
}
could_not_link:
{
GST_ELEMENT_ERROR (decoder, CORE, NEGOTIATION,
(NULL), ("Can't link source to typefind element"));
+ gst_bin_remove (GST_BIN_CAST (decoder), typefind);
return FALSE;
}
}
+/* remove source and all related elements */
static void
remove_source (GstURIDecodeBin * bin)
{
gst_bin_remove (GST_BIN_CAST (bin), source);
if (bin->src_np_sig_id) {
- g_signal_handler_disconnect (G_OBJECT (source), bin->src_np_sig_id);
+ g_signal_handler_disconnect (source, bin->src_np_sig_id);
bin->src_np_sig_id = 0;
}
if (bin->src_nmp_sig_id) {
- g_signal_handler_disconnect (G_OBJECT (source), bin->src_nmp_sig_id);
+ g_signal_handler_disconnect (source, bin->src_nmp_sig_id);
bin->src_nmp_sig_id = 0;
}
bin->source = NULL;
}
+ if (bin->queue) {
+ GST_DEBUG_OBJECT (bin, "removing old queue element");
+ gst_element_set_state (bin->queue, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (bin), bin->queue);
+ bin->queue = NULL;
+ }
+ if (bin->typefind) {
+ GST_DEBUG_OBJECT (bin, "removing old typefind element");
+ gst_element_set_state (bin->typefind, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (bin), bin->typefind);
+ bin->typefind = NULL;
+ }
}
/* is called when a dynamic source element created a new pad. */
{
GstElement *decoder;
gboolean is_raw;
+ GstCaps *rawcaps;
GST_URI_DECODE_BIN_LOCK (bin);
GST_DEBUG_OBJECT (bin, "Found new pad %s.%s in source element %s",
GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element));
+ g_object_get (bin, "caps", &rawcaps, NULL);
+ if (!rawcaps)
+ rawcaps = DEFAULT_CAPS;
+
/* if this is a pad with all raw caps, we can expose it */
- if (has_all_raw_caps (pad, &is_raw) && is_raw) {
+ if (has_all_raw_caps (pad, rawcaps, &is_raw) && is_raw) {
/* it's all raw, create output pads. */
GST_URI_DECODE_BIN_UNLOCK (bin);
- new_decoded_pad_cb (element, pad, FALSE, bin);
+ gst_caps_unref (rawcaps);
+ expose_decoded_pad (element, pad, bin);
return;
}
+ gst_caps_unref (rawcaps);
/* not raw, create decoder */
decoder = make_decoder (bin);
goto no_decodebin;
/* and link to decoder */
- if (!gst_element_link (bin->source, decoder))
+ if (!gst_element_link_pads (bin->source, NULL, decoder, "sink"))
goto could_not_link;
GST_DEBUG_OBJECT (bin, "linked decoder to new pad");
GST_DEBUG_OBJECT (decoder, "Source has dynamic output pads");
/* connect a handler for the new-pad signal */
decoder->src_np_sig_id =
- g_signal_connect (G_OBJECT (decoder->source), "pad-added",
+ g_signal_connect (decoder->source, "pad-added",
G_CALLBACK (source_new_pad), decoder);
decoder->src_nmp_sig_id =
- g_signal_connect (G_OBJECT (decoder->source), "no-more-pads",
+ g_signal_connect (decoder->source, "no-more-pads",
G_CALLBACK (source_no_more_pads), decoder);
g_object_set_data (G_OBJECT (decoder->source), "pending", "1");
decoder->pending++;
if (!dec_elem)
goto no_decoder;
- if (!gst_element_link (decoder->source, dec_elem))
+ if (!gst_element_link_pads (decoder->source, NULL, dec_elem, "sink"))
goto could_not_link;
}
}
decoder = GST_URI_DECODE_BIN (element);
switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ g_mutex_lock (decoder->factories_lock);
+ gst_uri_decode_bin_update_factories_list (decoder);
+ g_mutex_unlock (decoder->factories_lock);
+ break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
if (!setup_source (decoder))
goto source_failed;