* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
-
/**
* SECTION:element-playbin3
* @title: playbin3
* video sinks implement. See the documentation there for more details.
*
* ## Specifying which CD/DVD device to use
- * The device to use for CDs/DVDs needs to be set on the source element
- * playbin3 creates before it is opened. The most generic way of doing this
- * is to connect to playbin3's "source-setup" (or "notify::source") signal,
- * which will be emitted by playbin3 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]
+ *
+ * The device to use for CDs/DVDs needs to be set on the source element playbin3
+ * creates before it is opened. The most generic way of doing this is to connect
+ * to playbin3's "source-setup" signal, which will be emitted by playbin3 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
*
*
*/
-/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
- * with newer GLib versions (>= 2.31.0) */
-#define GLIB_DISABLE_DEPRECATION_WARNINGS
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
typedef struct _GstPlayBin3Class GstPlayBin3Class;
typedef struct _GstSourceGroup GstSourceGroup;
typedef struct _GstSourceCombine GstSourceCombine;
+typedef struct _SourcePad SourcePad;
typedef GstCaps *(*SourceCombineGetMediaCapsFunc) (void);
-/* has the info for a combiner and provides the link to the sink */
+/* GstSourceCombine controls all the information regarding a certain
+ * media type.
+ *
+ * It can control a custom combiner element (by default none)
+ */
struct _GstSourceCombine
{
const gchar *media_type; /* the media type for the combiner */
SourceCombineGetMediaCapsFunc get_media_caps; /* more complex caps for the combiner */
GstPlaySinkType type; /* the sink pad type of the combiner */
+ GstStreamType stream_type; /* The GstStreamType of the combiner */
GstElement *combiner; /* the combiner */
- GPtrArray *channels;
+ GPtrArray *channels; /* Array of GstPad ? */
+
GstPad *srcpad; /* the source pad of the combiner */
GstPad *sinkpad; /* the sinkpad of the sink when the combiner
- * is linked
- */
- gulong block_id;
+ * is linked */
GPtrArray *streams; /* Sorted array of GstStream for the given type */
- gint current_stream; /* Currently selected GstStream */
gboolean has_active_pad; /* stream combiner has the "active-pad" property */
- gboolean has_always_ok; /* stream combiner's sink pads have the "always-ok" property */
+ gboolean is_concat; /* The stream combiner is the 'concat' element */
};
#define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
"audio", "video", "text"
};
+
+#define STREAM_TYPES_FORMAT "s%s%s"
+#define STREAM_TYPES_ARGS(s) (s) & GST_STREAM_TYPE_AUDIO ? "audio " : "", \
+ (s) & GST_STREAM_TYPE_VIDEO ? "video " : "", \
+ (s) & GST_STREAM_TYPE_TEXT ? "text " : ""
+
+
+
+#if 0 /* AUTOPLUG DISABLED */
static void avelements_free (gpointer data);
static GSequence *avelements_create (GstPlayBin3 * playbin,
gboolean isaudioelement);
+#endif
/* The GstAudioVideoElement structure holding the audio/video decoder
* and the audio/video sink factories together with field indicating
guint n_comm_cf; /* number of common caps features */
} GstAVElement;
+/* a structure to hold information about a uridecodebin pad */
+struct _SourcePad
+{
+ GstPad *pad; /* The controlled pad */
+ GstStreamType stream_type; /* stream type of the controlled pad */
+ gulong event_probe_id;
+};
+
/* a structure to hold the objects for decoding a uri and the subtitle uri
*/
struct _GstSourceGroup
gboolean valid; /* the group has valid info to start playback */
gboolean active; /* the group is active */
+ gboolean playing; /* the group is currently playing
+ * (outputted on the sinks) */
+
/* properties */
gchar *uri;
gchar *suburi;
- GValueArray *streaminfo;
- GstElement *source;
+ /* The currently outputted group_id */
+ guint group_id;
- /* urisourcebins for uri and subtitle uri */
- /* FIXME: Just make this an array of uris */
- GstElement *urisourcebin;
- GstElement *suburisourcebin;
+ /* Bit-wise set of stream types we have requested from uridecodebin3 */
+ GstStreamType selected_stream_types;
+
+ /* Bit-wise set of stream types for which pads are present */
+ GstStreamType present_stream_types;
+
+ /* TRUE if a 'about-to-finish' needs to be posted once we have
+ * got source pads for all requested stream types
+ *
+ * FIXME : Move this logic to uridecodebin3 later */
+ gboolean pending_about_to_finish;
+
+ /* uridecodebin to handle uri and suburi */
+ GstElement *uridecodebin;
/* Active sinks for each media type. These are initialized with
* the configured or currently used sink, otherwise
GstElement *video_sink;
GstElement *text_sink;
- gint pending;
- gboolean sub_pending;
+ /* List of source pads */
+ GList *source_pads;
+
+ /* uridecodebin signals */
+ gulong pad_added_id;
+ gulong pad_removed_id;
+ gulong select_stream_id;
+ gulong source_setup_id;
+ gulong about_to_finish_id;
- /* primary uri signals */
- gulong urisrc_pad_added_id;
- gulong urisrc_pad_removed_id;
- gulong notify_source_id;
+#if 0 /* AUTOPLUG DISABLED */
gulong autoplug_factories_id;
gulong autoplug_select_id;
gulong autoplug_continue_id;
gulong autoplug_query_id;
+#endif
- /* subtitle uri signals */
- gulong sub_pad_added_id;
- gulong sub_pad_removed_id;
- gulong sub_autoplug_continue_id;
- gulong sub_autoplug_query_id;
+ gboolean stream_changed_pending;
- gulong block_id;
+ /* Active stream collection */
+ GstStreamCollection *collection;
- GMutex stream_changed_pending_lock;
- gboolean stream_changed_pending;
/* buffering message stored for after switching */
GstMessage *pending_buffering_msg;
#define GST_PLAY_BIN3_DYN_UNLOCK(bin) g_mutex_unlock (&(bin)->dyn_lock)
/* lock for shutdown */
-#define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label) \
-G_STMT_START { \
- if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \
- goto label; \
- GST_PLAY_BIN3_DYN_LOCK (bin); \
- if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \
- GST_PLAY_BIN3_DYN_UNLOCK (bin); \
- goto label; \
- } \
-} G_STMT_END
+#define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label) \
+ G_STMT_START { \
+ if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \
+ goto label; \
+ GST_PLAY_BIN3_DYN_LOCK (bin); \
+ if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \
+ GST_PLAY_BIN3_DYN_UNLOCK (bin); \
+ goto label; \
+ } \
+ } G_STMT_END
/* unlock for shutdown */
-#define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \
- GST_PLAY_BIN3_DYN_UNLOCK (bin); \
+#define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \
+ GST_PLAY_BIN3_DYN_UNLOCK (bin); \
/**
* GstPlayBin3:
GstSourceGroup *curr_group; /* pointer to the currently playing group */
GstSourceGroup *next_group; /* pointer to the next group */
- /* combiners for different streams */
+ /* Array of GstPad controlled by each combiner */
GPtrArray *channels[PLAYBIN_STREAM_LAST]; /* links to combiner pads */
+
+ /* combiners for different streams */
GstSourceCombine combiner[PLAYBIN_STREAM_LAST];
- /* A global decodebin3 that's used to actually do decoding */
- gboolean decodebin_active;
- GstElement *decodebin;
- /* Bit-wise set of stream types we have
- * requested from decodebin vs stream types
- * decodebin has provided */
+ /* Bit-wise set of stream types we have requested from uridecodebin3.
+ * Calculated as the combination of the 'selected_stream_types' of
+ * each sourcegroup */
GstStreamType selected_stream_types;
- GstStreamType active_stream_types;
- /* Decodebin signals */
- gulong db_pad_added_id;
- gulong db_pad_removed_id;
- gulong db_no_more_pads_id;
- gulong db_drained_id;
- gulong db_select_stream_id;
+ /* Bit-wise set of configured output stream types (i.e. active
+ playsink inputs and combiners) */
+ GstStreamType active_stream_types;
/* properties */
guint64 connection_speed; /* connection speed in bits/sec (0 = unknown) */
/* our play sink */
GstPlaySink *playsink;
- /* the last activated source */
- GstElement *source;
+ /* Task for (de)activating groups, protected by the activation lock */
+ GstTask *activation_task;
+ GRecMutex activation_lock;
/* lock protecting dynamic adding/removing */
GMutex dyn_lock;
GSequence *aelements; /* a list of GstAVElements for audio stream */
GSequence *velements; /* a list of GstAVElements for video stream */
- struct
- {
- gboolean valid;
- GstFormat format;
- gint64 duration;
- } duration[5]; /* cached durations */
-
guint64 ring_buffer_max_size; /* 0 means disabled */
-
- GList *contexts;
-
- /* Active stream collection */
- GstStreamCollection *collection;
};
struct _GstPlayBin3Class
/* props */
#define DEFAULT_URI NULL
#define DEFAULT_SUBURI NULL
-#define DEFAULT_SOURCE NULL
#define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \
GST_PLAY_FLAG_SOFT_COLORBALANCE | GST_PLAY_FLAG_BUFFERING
#define DEFAULT_CURRENT_VIDEO -1
#define DEFAULT_CURRENT_AUDIO -1
#define DEFAULT_CURRENT_TEXT -1
-#define DEFAULT_AUTO_SELECT_STREAMS TRUE
#define DEFAULT_SUBTITLE_ENCODING NULL
#define DEFAULT_AUDIO_SINK NULL
#define DEFAULT_VIDEO_SINK NULL
PROP_CURRENT_URI,
PROP_SUBURI,
PROP_CURRENT_SUBURI,
- PROP_SOURCE,
PROP_FLAGS,
PROP_SUBTITLE_ENCODING,
PROP_AUDIO_SINK,
LAST_SIGNAL
};
+#if 0 /* AUTOPLUG DISABLED */
static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
+#endif
static void gst_play_bin3_class_init (GstPlayBin3Class * klass);
static void gst_play_bin3_init (GstPlayBin3 * playbin);
static void gst_play_bin3_handle_message (GstBin * bin, GstMessage * message);
static void gst_play_bin3_deep_element_added (GstBin * playbin,
GstBin * sub_bin, GstElement * child);
-static gboolean gst_play_bin3_query (GstElement * element, GstQuery * query);
-static void gst_play_bin3_set_context (GstElement * element,
- GstContext * context);
static gboolean gst_play_bin3_send_event (GstElement * element,
GstEvent * event);
static GstSample *gst_play_bin3_convert_sample (GstPlayBin3 * playbin,
GstCaps * caps);
-static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin,
- GstState target);
+static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin);
-static void no_more_pads_cb (GstElement * decodebin, GstPlayBin3 * playbin);
+static void gst_play_bin3_check_group_status (GstPlayBin3 * playbin);
+static void emit_about_to_finish (GstPlayBin3 * playbin);
+static void reconfigure_output (GstPlayBin3 * playbin);
static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
- GstPlayBin3 * playbin);
+ GstSourceGroup * group);
static gint select_stream_cb (GstElement * decodebin,
GstStreamCollection * collection, GstStream * stream,
- GstPlayBin3 * playbin);
+ GstSourceGroup * group);
-static void do_stream_selection (GstPlayBin3 * playbin);
+static void do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group);
static GstElementClass *parent_class;
static guint gst_play_bin3_signals[LAST_SIGNAL] = { 0 };
-#define REMOVE_SIGNAL(obj,id) \
-if (id) { \
- g_signal_handler_disconnect (obj, id); \
- id = 0; \
-}
+#define REMOVE_SIGNAL(obj,id) \
+ if (id) { \
+ g_signal_handler_disconnect (obj, id); \
+ id = 0; \
+ }
static void gst_play_bin3_overlay_init (gpointer g_iface,
gpointer g_iface_data);
g_param_spec_string ("uri", "URI", "URI of the media to play",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
+ /**
* GstPlayBin3:current-uri
*
* The currently playing uri.
"The currently playing URI of a subtitle",
NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (gobject_klass, PROP_SOURCE,
- g_param_spec_object ("source", "Source", "Source element",
- GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
/**
* GstPlayBin3:flags
*
/**
* GstPlayBin3:video-stream-combiner
*
- * Get or set the current video stream combiner. By default, an input-selector
- * is created and deleted as-needed.
+ * Get or set the current video stream combiner. By default, no
+ * element is used and the selected stream is used directly.
*/
g_object_class_install_property (gobject_klass, PROP_VIDEO_STREAM_COMBINER,
g_param_spec_object ("video-stream-combiner", "Video stream combiner",
- "Current video stream combiner (NULL = input-selector)",
+ "Current video stream combiner (default: none)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
/**
* GstPlayBin3:audio-stream-combiner
*
- * Get or set the current audio stream combiner. By default, an input-selector
- * is created and deleted as-needed.
+ * Get or set the current audio stream combiner. By default, no
+ * element is used and the selected stream is used directly.
*/
g_object_class_install_property (gobject_klass, PROP_AUDIO_STREAM_COMBINER,
g_param_spec_object ("audio-stream-combiner", "Audio stream combiner",
- "Current audio stream combiner (NULL = input-selector)",
+ "Current audio stream combiner (default: none))",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
/**
* GstPlayBin3:text-stream-combiner
*
- * Get or set the current text stream combiner. By default, an input-selector
- * is created and deleted as-needed.
+ * Get or set the current text stream combiner. By default, no
+ * element is used and the selected stream is used directly.
*/
g_object_class_install_property (gobject_klass, PROP_TEXT_STREAM_COMBINER,
g_param_spec_object ("text-stream-combiner", "Text stream combiner",
- "Current text stream combiner (NULL = input-selector)",
+ "Current text stream combiner (default: none)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_play_bin3_change_state);
- gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin3_query);
- gstelement_klass->set_context = GST_DEBUG_FUNCPTR (gst_play_bin3_set_context);
gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin3_send_event);
gstbin_klass->handle_message =
playbin->combiner[PLAYBIN_STREAM_AUDIO].media_type = "audio";
playbin->combiner[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
+ playbin->combiner[PLAYBIN_STREAM_AUDIO].stream_type = GST_STREAM_TYPE_AUDIO;
playbin->combiner[PLAYBIN_STREAM_AUDIO].channels = playbin->channels[0];
playbin->combiner[PLAYBIN_STREAM_AUDIO].streams =
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
- playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1;
playbin->combiner[PLAYBIN_STREAM_VIDEO].media_type = "video";
playbin->combiner[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
+ playbin->combiner[PLAYBIN_STREAM_VIDEO].stream_type = GST_STREAM_TYPE_VIDEO;
playbin->combiner[PLAYBIN_STREAM_VIDEO].channels = playbin->channels[1];
playbin->combiner[PLAYBIN_STREAM_VIDEO].streams =
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
- playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1;
playbin->combiner[PLAYBIN_STREAM_TEXT].media_type = "text";
playbin->combiner[PLAYBIN_STREAM_TEXT].get_media_caps =
gst_subtitle_overlay_create_factory_caps;
playbin->combiner[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
+ playbin->combiner[PLAYBIN_STREAM_TEXT].stream_type = GST_STREAM_TYPE_TEXT;
playbin->combiner[PLAYBIN_STREAM_TEXT].channels = playbin->channels[2];
playbin->combiner[PLAYBIN_STREAM_TEXT].streams =
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
- playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1;
}
-/* Update the combiner information to be in sync with the current collection */
+/* Update the combiner information to be in sync with the current collection
+ *
+ * FIXME : "current" collection doesn't mean anything until we have a "combined"
+ * collection of all groups */
static void
-update_combiner_info (GstPlayBin3 * playbin)
+update_combiner_info (GstPlayBin3 * playbin, GstStreamCollection * collection)
{
guint i, len;
- if (playbin->collection == NULL)
+ if (collection == NULL)
return;
GST_DEBUG_OBJECT (playbin, "Updating combiner info");
g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE);
playbin->combiner[PLAYBIN_STREAM_AUDIO].streams =
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
- playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1;
playbin->combiner[PLAYBIN_STREAM_VIDEO].streams =
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
- playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1;
playbin->combiner[PLAYBIN_STREAM_TEXT].streams =
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
- playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1;
- len = gst_stream_collection_get_size (playbin->collection);
+ len = gst_stream_collection_get_size (collection);
for (i = 0; i < len; i++) {
- GstStream *stream =
- gst_stream_collection_get_stream (playbin->collection, i);
+ GstStream *stream = gst_stream_collection_get_stream (collection, i);
GstStreamType stype = gst_stream_get_stream_type (stream);
if (stype & GST_STREAM_TYPE_AUDIO) {
playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len);
}
-/* Set the given stream as the selected stream */
-static void
-set_selected_stream (GstPlayBin3 * playbin, GstStream * stream)
-{
- GstSourceCombine *combine = NULL;
- GstStreamType stype = gst_stream_get_stream_type (stream);
-
- if (stype & GST_STREAM_TYPE_AUDIO)
- combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO];
- else if (stype & GST_STREAM_TYPE_VIDEO)
- combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO];
- else if (stype & GST_STREAM_TYPE_TEXT)
- combine = &playbin->combiner[PLAYBIN_STREAM_TEXT];
-
- if (combine) {
- if (combine->combiner == NULL) {
- guint i, len;
-
- GST_DEBUG_OBJECT (playbin, "Called for %s (%p)",
- gst_stream_get_stream_id (stream), combine->combiner);
-
- combine->current_stream = -1;
- len = combine->streams->len;
- for (i = 0; i < len; i++) {
- GstStream *cand = g_ptr_array_index (combine->streams, i);
- if (cand == stream) {
- GST_DEBUG_OBJECT (playbin, "Setting current to %d", i);
- combine->current_stream = i;
- break;
- }
- }
- }
- }
-}
+#ifndef GST_DISABLE_GST_DEBUG
+#define debug_groups(playbin) G_STMT_START { \
+ guint i; \
+ \
+ for (i = 0; i < 2; i++) { \
+ GstSourceGroup *group = &playbin->groups[i]; \
+ \
+ GST_DEBUG ("GstSourceGroup #%d (%s)", i, (group == playbin->curr_group) ? "current" : (group == playbin->next_group) ? "next" : "unused"); \
+ GST_DEBUG (" valid:%d , active:%d , playing:%d", group->valid, group->active, group->playing); \
+ GST_DEBUG (" uri:%s", group->uri); \
+ GST_DEBUG (" suburi:%s", group->suburi); \
+ GST_DEBUG (" group_id:%d", group->group_id); \
+ GST_DEBUG (" pending_about_to_finish:%d", group->pending_about_to_finish); \
+ } \
+ } G_STMT_END
+#else
+#define debug_groups(p) {}
+#endif
static void
init_group (GstPlayBin3 * playbin, GstSourceGroup * group)
g_mutex_init (&group->lock);
group->stream_changed_pending = FALSE;
- g_mutex_init (&group->stream_changed_pending_lock);
+ group->group_id = GST_GROUP_ID_INVALID;
group->playbin = playbin;
}
g_mutex_clear (&group->lock);
group->stream_changed_pending = FALSE;
- g_mutex_clear (&group->stream_changed_pending_lock);
if (group->pending_buffering_msg)
gst_message_unref (group->pending_buffering_msg);
group->pending_buffering_msg = NULL;
+ gst_object_replace ((GstObject **) & group->collection, NULL);
+
gst_object_replace ((GstObject **) & group->audio_sink, NULL);
gst_object_replace ((GstObject **) & group->video_sink, NULL);
gst_object_replace ((GstObject **) & group->text_sink, NULL);
gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value);
}
+#if 0 /* AUTOPLUG DISABLED */
static gint
compare_factories_func (gconstpointer p1, gconstpointer p2)
{
playbin->elements_cookie = cookie;
}
+#endif
static void
gst_play_bin3_init (GstPlayBin3 * playbin)
/* first filter out the interesting element factories */
g_mutex_init (&playbin->elements_lock);
+ g_rec_mutex_init (&playbin->activation_lock);
+
/* add sink */
playbin->playsink =
g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
for (i = 0; i < PLAYBIN_STREAM_LAST; i++)
g_ptr_array_free (playbin->channels[i], TRUE);
- if (playbin->source)
- gst_object_unref (playbin->source);
-
/* Setting states to NULL is safe here because playsink
* will already be gone and none of these sinks will be
* a child of playsink
g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams, TRUE);
g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE);
- if (playbin->decodebin)
- gst_object_unref (playbin->decodebin);
-
if (playbin->elements)
gst_plugin_feature_list_free (playbin->elements);
if (playbin->velements)
g_sequence_free (playbin->velements);
- if (playbin->collection)
- gst_object_unref (playbin->collection);
-
- g_list_free_full (playbin->contexts, (GDestroyNotify) gst_context_unref);
-
+ g_rec_mutex_clear (&playbin->activation_lock);
g_rec_mutex_clear (&playbin->lock);
g_mutex_clear (&playbin->dyn_lock);
g_mutex_clear (&playbin->elements_lock);
return gst_play_sink_convert_sample (playbin->playsink, caps);
}
-/* Returns current stream number, or -1 if none has been selected yet */
-static int
-get_current_stream_number (GstPlayBin3 * playbin, GstSourceCombine * combine,
- GPtrArray * channels)
-{
- /* Internal API cleanup would make this easier... */
- int i;
- GstPad *pad, *current;
- GstObject *combiner = NULL;
- int ret = -1;
-
- if (!combine->has_active_pad) {
- GST_WARNING_OBJECT (playbin,
- "combiner doesn't have the \"active-pad\" property");
- return ret;
- }
-
- for (i = 0; i < channels->len; i++) {
- pad = g_ptr_array_index (channels, i);
- if ((combiner = gst_pad_get_parent (pad))) {
- g_object_get (combiner, "active-pad", ¤t, NULL);
- gst_object_unref (combiner);
-
- if (pad == current) {
- gst_object_unref (current);
- ret = i;
- break;
- }
-
- if (current)
- gst_object_unref (current);
- }
- }
-
- return ret;
-}
-
static gboolean
gst_play_bin3_send_custom_event (GstObject * combiner, const gchar * event_name)
{
GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d",
stream_type_names[stream_type], *current_value, stream);
- if (combine->combiner == NULL) {
+ if (combine->combiner == NULL || combine->is_concat) {
/* FIXME: Check that the current_value is within range */
*current_value = stream;
- do_stream_selection (playbin);
+ do_stream_selection (playbin, playbin->curr_group);
GST_PLAY_BIN3_UNLOCK (playbin);
return TRUE;
}
&playbin->current_text, stream, &playbin->text_pending_flush_finish);
}
-static void
-source_combine_remove_pads (GstPlayBin3 * playbin, GstSourceCombine * combine)
-{
- if (combine->sinkpad) {
- GST_LOG_OBJECT (playbin, "unlinking from sink");
- gst_pad_unlink (combine->srcpad, combine->sinkpad);
-
- /* release back */
- GST_LOG_OBJECT (playbin, "release sink pad");
- gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
- gst_object_unref (combine->sinkpad);
- combine->sinkpad = NULL;
- }
- gst_object_unref (combine->srcpad);
- combine->srcpad = NULL;
-}
-
-static GstPadProbeReturn
-block_serialized_data_cb (GstPad * pad, GstPadProbeInfo * info,
- gpointer user_data)
-{
- if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) {
- GST_DEBUG_OBJECT (pad, "Letting non-serialized event %s pass",
- GST_EVENT_TYPE_NAME (info->data));
- return GST_PAD_PROBE_PASS;
- }
-
- return GST_PAD_PROBE_OK;
-}
static void
gst_play_bin3_set_sink (GstPlayBin3 * playbin, GstPlaySinkType type,
static void
gst_play_bin3_set_encoding (GstPlayBin3 * playbin, const gchar * encoding)
{
- GstElement *elem;
-
GST_PLAY_BIN3_LOCK (playbin);
-
- /* set subtitles on decodebin. */
- if ((elem = playbin->decodebin))
- g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
-
gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding);
GST_PLAY_BIN3_UNLOCK (playbin);
}
gst_play_bin3_set_flags (playbin, g_value_get_flags (value));
if (playbin->curr_group) {
GST_SOURCE_GROUP_LOCK (playbin->curr_group);
- if (playbin->curr_group->urisourcebin) {
- g_object_set (playbin->curr_group->urisourcebin, "download",
+ if (playbin->curr_group->uridecodebin) {
+ g_object_set (playbin->curr_group->uridecodebin, "download",
(g_value_get_flags (value) & GST_PLAY_FLAG_DOWNLOAD) != 0, NULL);
}
GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
playbin->ring_buffer_max_size = g_value_get_uint64 (value);
if (playbin->curr_group) {
GST_SOURCE_GROUP_LOCK (playbin->curr_group);
- if (playbin->curr_group->urisourcebin) {
- g_object_set (playbin->curr_group->urisourcebin,
+ if (playbin->curr_group->uridecodebin) {
+ g_object_set (playbin->curr_group->uridecodebin,
"ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
}
GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
GstElement *combiner;
GST_PLAY_BIN3_LOCK (playbin);
- if ((combiner = playbin->combiner[stream_type].combiner))
+ /* The special concat element should never be returned */
+ if (playbin->combiner[stream_type].is_concat)
+ combiner = NULL;
+ else if ((combiner = playbin->combiner[stream_type].combiner))
gst_object_ref (combiner);
else if ((combiner = *elem))
gst_object_ref (combiner);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
}
- case PROP_SOURCE:
- {
- GST_OBJECT_LOCK (playbin);
- g_value_set_object (value, playbin->source);
- GST_OBJECT_UNLOCK (playbin);
- break;
- }
case PROP_FLAGS:
g_value_set_flags (value, gst_play_bin3_get_flags (playbin));
break;
}
}
-static void
-gst_play_bin3_update_cached_duration_from_query (GstPlayBin3 * playbin,
- gboolean valid, GstQuery * query)
-{
- GstFormat fmt;
- gint64 duration;
- gint i;
-
- GST_DEBUG_OBJECT (playbin, "Updating cached duration from query");
- gst_query_parse_duration (query, &fmt, &duration);
-
- for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
- if (playbin->duration[i].format == 0 || fmt == playbin->duration[i].format) {
- playbin->duration[i].valid = valid;
- playbin->duration[i].format = fmt;
- playbin->duration[i].duration = valid ? duration : -1;
- break;
- }
- }
-}
-
-static void
-gst_play_bin3_update_cached_duration (GstPlayBin3 * playbin)
-{
- const GstFormat formats[] =
- { GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT };
- gboolean ret;
- GstQuery *query;
- gint i;
-
- GST_DEBUG_OBJECT (playbin, "Updating cached durations before group switch");
- for (i = 0; i < G_N_ELEMENTS (formats); i++) {
- query = gst_query_new_duration (formats[i]);
- ret =
- GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (playbin),
- query);
- gst_play_bin3_update_cached_duration_from_query (playbin, ret, query);
- gst_query_unref (query);
- }
-}
-
-static gboolean
-gst_play_bin3_query (GstElement * element, GstQuery * query)
-{
- GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
- gboolean ret;
-
- /* During a group switch we shouldn't allow duration queries
- * because it's not clear if the old or new group's duration
- * is returned and if the sinks are already playing new data
- * or old data. See bug #585969
- *
- * While we're at it, also don't do any other queries during
- * a group switch or any other event that causes topology changes
- * by taking the playbin lock in any case.
- */
- GST_PLAY_BIN3_LOCK (playbin);
-
- if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
- GstSourceGroup *group = playbin->curr_group;
- gboolean pending;
-
- GST_SOURCE_GROUP_LOCK (group);
-
- pending = group->pending || group->stream_changed_pending;
-
- if (pending) {
- GstFormat fmt;
- gint i;
-
- ret = FALSE;
- gst_query_parse_duration (query, &fmt, NULL);
- for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
- if (fmt == playbin->duration[i].format) {
- ret = playbin->duration[i].valid;
- gst_query_set_duration (query, fmt,
- (ret ? playbin->duration[i].duration : -1));
- break;
- }
- }
- /* if nothing cached yet, we might as well request duration,
- * such as during initial startup */
- if (ret) {
- GST_DEBUG_OBJECT (playbin,
- "Taking cached duration because of pending group switch: %d", ret);
- GST_SOURCE_GROUP_UNLOCK (group);
- GST_PLAY_BIN3_UNLOCK (playbin);
- return ret;
- }
- }
- GST_SOURCE_GROUP_UNLOCK (group);
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->query (element, query);
-
- if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION)
- gst_play_bin3_update_cached_duration_from_query (playbin, ret, query);
- GST_PLAY_BIN3_UNLOCK (playbin);
-
- return ret;
-}
-
static gint
get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine,
GList * full_list)
gint i;
GList *tmp;
- for (i = 0; combine->streams->len; i++) {
+ for (i = 0; i < combine->streams->len; i++) {
GstStream *stream = (GstStream *) g_ptr_array_index (combine->streams, i);
const gchar *sid = gst_stream_get_stream_id (stream);
for (tmp = full_list; tmp; tmp = tmp->next) {
static GList *
extend_list_of_streams (GstPlayBin3 * playbin, GstStreamType stype,
- GList * list)
+ GList * list, GstStreamCollection * collection)
{
GList *tmp, *res;
gint i, nb;
res = list;
- nb = gst_stream_collection_get_size (playbin->collection);
+ nb = gst_stream_collection_get_size (collection);
for (i = 0; i < nb; i++) {
- GstStream *stream =
- gst_stream_collection_get_stream (playbin->collection, i);
+ GstStream *stream = gst_stream_collection_get_stream (collection, i);
GstStreamType curtype = gst_stream_get_stream_type (stream);
if (stype == curtype) {
gboolean already_there = FALSE;
}
static GstEvent *
-update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event)
+update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event,
+ GstSourceGroup * group)
{
GList *streams = NULL;
GList *to_use;
return event;
}
+ if (!group->collection) {
+ GST_DEBUG_OBJECT (playbin,
+ "No stream collection for group, no need to modify SELECT_STREAMS event");
+ return event;
+ }
+
gst_event_parse_select_streams (event, &streams);
to_use = g_list_copy_deep (streams, (GCopyFunc) g_strdup, NULL);
/* For each combiner, we want to add all streams of that type to the
* selection */
if (playbin->audio_stream_combiner) {
- to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use);
+ to_use =
+ extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use,
+ group->collection);
combine_id =
get_combiner_stream_id (playbin,
&playbin->combiner[PLAYBIN_STREAM_AUDIO], streams);
gst_play_bin3_set_current_audio_stream (playbin, combine_id);
}
if (playbin->video_stream_combiner) {
- to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use);
+ to_use =
+ extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use,
+ group->collection);
combine_id =
get_combiner_stream_id (playbin,
&playbin->combiner[PLAYBIN_STREAM_VIDEO], streams);
gst_play_bin3_set_current_video_stream (playbin, combine_id);
}
if (playbin->text_stream_combiner) {
- to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use);
+ to_use =
+ extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use,
+ group->collection);
combine_id =
get_combiner_stream_id (playbin,
&playbin->combiner[PLAYBIN_STREAM_TEXT], streams);
return event;
}
+/* Returns TRUE if the given list of streams belongs to the stream collection */
+static gboolean
+gst_streams_belong_to_collection (GList * streams,
+ GstStreamCollection * collection)
+{
+ GList *tmp;
+ guint i, nb;
+
+ if (streams == NULL || collection == NULL)
+ return FALSE;
+ nb = gst_stream_collection_get_size (collection);
+ if (nb == 0)
+ return FALSE;
+
+ for (tmp = streams; tmp; tmp = tmp->next) {
+ const gchar *cand = (const gchar *) tmp->data;
+ gboolean found = FALSE;
+
+ for (i = 0; i < nb; i++) {
+ GstStream *stream = gst_stream_collection_get_stream (collection, i);
+ if (!g_strcmp0 (cand, gst_stream_get_stream_id (stream))) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static GstSourceGroup *
+get_source_group_for_streams (GstPlayBin3 * playbin, GstEvent * event)
+{
+ GList *streams;
+ GstSourceGroup *res = NULL;
+
+ gst_event_parse_select_streams (event, &streams);
+ if (playbin->curr_group->collection &&
+ gst_streams_belong_to_collection (streams,
+ playbin->curr_group->collection))
+ res = playbin->curr_group;
+ else if (playbin->next_group->collection &&
+ gst_streams_belong_to_collection (streams,
+ playbin->next_group->collection))
+ res = playbin->next_group;
+ g_list_free_full (streams, g_free);
+
+ return res;
+}
+
+static GstStreamType
+get_stream_type_for_event (GstStreamCollection * collection, GstEvent * event)
+{
+ GList *stream_list = NULL;
+ GList *tmp;
+ GstStreamType res = 0;
+ guint i, len;
+
+ gst_event_parse_select_streams (event, &stream_list);
+ len = gst_stream_collection_get_size (collection);
+ for (tmp = stream_list; tmp; tmp = tmp->next) {
+ gchar *stid = (gchar *) tmp->data;
+
+ for (i = 0; i < len; i++) {
+ GstStream *stream = gst_stream_collection_get_stream (collection, i);
+ if (!g_strcmp0 (stid, gst_stream_get_stream_id (stream))) {
+ res |= gst_stream_get_stream_type (stream);
+ }
+ }
+ }
+ g_list_free_full (stream_list, g_free);
+
+ return res;
+}
+
static gboolean
gst_play_bin3_send_event (GstElement * element, GstEvent * event)
{
if (GST_EVENT_TYPE (event) == GST_EVENT_SELECT_STREAMS) {
gboolean res;
+ GstSourceGroup *group;
GST_PLAY_BIN3_LOCK (playbin);
GST_LOG_OBJECT (playbin,
/* This is probably already false, but it doesn't hurt to be sure */
playbin->do_stream_selections = FALSE;
+ group = get_source_group_for_streams (playbin, event);
+ if (group == NULL) {
+ GST_WARNING_OBJECT (playbin,
+ "Can't figure out to which uridecodebin the select-streams event should be sent to");
+ GST_PLAY_BIN3_UNLOCK (playbin);
+ return FALSE;
+ }
+
/* If we have custom combiners, we need to extend the selection with
* the list of all streams for that given type since we will be handling
* the selection with that combiner */
- event = update_select_streams_event (playbin, event);
+ event = update_select_streams_event (playbin, event, group);
+
+ if (group->collection) {
+ group->selected_stream_types =
+ get_stream_type_for_event (group->collection, event);
+ playbin->selected_stream_types =
+ playbin->groups[0].selected_stream_types | playbin->
+ groups[1].selected_stream_types;
+ if (playbin->active_stream_types != playbin->selected_stream_types)
+ reconfigure_output (playbin);
+ }
- /* Send this event directly to decodebin, so it works even
- * if decodebin didn't add any pads yet */
- res = gst_element_send_event (playbin->decodebin, event);
+ /* Send this event directly to uridecodebin, so it works even
+ * if uridecodebin didn't add any pads yet */
+ res = gst_element_send_event (group->uridecodebin, event);
GST_PLAY_BIN3_UNLOCK (playbin);
return res;
/* Called with playbin lock held */
static void
-do_stream_selection (GstPlayBin3 * playbin)
+do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group)
{
GstStreamCollection *collection;
guint i, nb_streams;
gint nb_video = 0, nb_audio = 0, nb_text = 0;
GstStreamType chosen_stream_types = 0;
- collection = playbin->collection;
+ if (group == NULL)
+ return;
+
+ collection = group->collection;
if (collection == NULL) {
GST_LOG_OBJECT (playbin, "No stream collection. Not doing stream-select");
return;
GST_INFO_OBJECT (playbin, "Empty collection received! Ignoring");
}
+ GST_DEBUG_OBJECT (playbin, "Doing selection on collection with %d streams",
+ nb_streams);
+
/* Iterate the collection and choose the streams that match
* either the current-* setting, or all streams of a type if there's
* a combiner for that type */
gint pb_stream_type = -1;
gboolean select_this = FALSE;
+ GST_LOG_OBJECT (playbin, "Looking at stream #%d : %s", i, stream_id);
+
if (stream_type & GST_STREAM_TYPE_AUDIO) {
pb_stream_type = PLAYBIN_STREAM_AUDIO;
/* Select the stream if it's the current one or if there's a custom selector */
playbin->audio_stream_combiner != NULL);
nb_audio++;
} else if (stream_type & GST_STREAM_TYPE_VIDEO) {
- pb_stream_type = PLAYBIN_STREAM_AUDIO;
+ pb_stream_type = PLAYBIN_STREAM_VIDEO;
select_this =
(nb_video == playbin->current_video ||
(playbin->current_video == -1 && nb_video == 0) ||
}
if (streams) {
- GstEvent *ev = gst_event_new_select_streams (streams);
- gst_element_send_event (playbin->decodebin, ev);
+ if (group->uridecodebin) {
+ GstEvent *ev = gst_event_new_select_streams (streams);
+ gst_element_send_event (group->uridecodebin, ev);
+ }
g_list_free (streams);
}
- playbin->selected_stream_types = chosen_stream_types;
+
+ group->selected_stream_types = chosen_stream_types;
+ /* Update global selected_stream_types */
+ playbin->selected_stream_types =
+ playbin->groups[0].selected_stream_types | playbin->
+ groups[1].selected_stream_types;
+ if (playbin->active_stream_types != playbin->selected_stream_types)
+ reconfigure_output (playbin);
}
-/* mime types we are not handling on purpose right now, don't post a
- * missing-plugin message for these */
-static const gchar *blacklisted_mimes[] = {
- NULL
-};
+/* Return the GstSourceGroup to which this element belongs
+ * Can be NULL (if it belongs to playsink for example) */
+static GstSourceGroup *
+find_source_group_owner (GstPlayBin3 * playbin, GstObject * element)
+{
+ if (playbin->curr_group->uridecodebin
+ && gst_object_has_as_ancestor (element,
+ GST_OBJECT_CAST (playbin->curr_group->uridecodebin)))
+ return playbin->curr_group;
+ if (playbin->next_group->uridecodebin
+ && gst_object_has_as_ancestor (element,
+ GST_OBJECT_CAST (playbin->next_group->uridecodebin)))
+ return playbin->next_group;
+ return NULL;
+}
static void
gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg)
{
GstPlayBin3 *playbin = GST_PLAY_BIN3 (bin);
- if (gst_is_missing_plugin_message (msg)) {
- gchar *detail;
- guint i;
-
- detail = gst_missing_plugin_message_get_installer_detail (msg);
- for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) {
- if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) {
- GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg);
- gst_message_unref (msg);
- g_free (detail);
- return;
- }
- }
- g_free (detail);
- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
- GstSourceGroup *new_group = playbin->curr_group;
- GstMessage *buffering_msg = NULL;
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
+ GstSourceGroup *group = NULL, *other_group = NULL;
+ gboolean changed = FALSE;
+ guint group_id;
+ GstMessage *buffering_msg;
- GST_SOURCE_GROUP_LOCK (new_group);
- new_group->stream_changed_pending = FALSE;
- if (new_group->pending_buffering_msg) {
- buffering_msg = new_group->pending_buffering_msg;
- new_group->pending_buffering_msg = NULL;
+ if (!gst_message_parse_group_id (msg, &group_id)) {
+ GST_ERROR_OBJECT (bin,
+ "Could not get group_id from STREAM_START message !");
+ goto beach;
}
- GST_SOURCE_GROUP_UNLOCK (new_group);
-
- GST_DEBUG_OBJECT (playbin, "Stream start from new group %p", new_group);
+ GST_DEBUG_OBJECT (bin, "STREAM_START group_id:%u", group_id);
- if (buffering_msg) {
- GST_DEBUG_OBJECT (playbin, "Posting pending buffering message: %"
- GST_PTR_FORMAT, buffering_msg);
- GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
+ /* Figure out to which group this group_id corresponds */
+ GST_PLAY_BIN3_LOCK (playbin);
+ if (playbin->groups[0].group_id == group_id) {
+ group = &playbin->groups[0];
+ other_group = &playbin->groups[1];
+ } else if (playbin->groups[1].group_id == group_id) {
+ group = &playbin->groups[1];
+ other_group = &playbin->groups[0];
+ }
+ if (group == NULL) {
+ GST_ERROR_OBJECT (bin, "group_id %u is not provided by any group !",
+ group_id);
+ GST_PLAY_BIN3_UNLOCK (playbin);
+ goto beach;
}
- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
- GstSourceGroup *group = playbin->curr_group;
- gboolean pending;
+ debug_groups (playbin);
- /* drop buffering messages from child queues while we are switching
- * groups (because the application set a new uri in about-to-finish)
- * if the playsink queue still has buffers to play */
+ /* Do the switch now ! */
+ playbin->curr_group = group;
+ playbin->next_group = other_group;
GST_SOURCE_GROUP_LOCK (group);
- pending = group->stream_changed_pending;
+ if (group->playing == FALSE)
+ changed = TRUE;
+ group->playing = TRUE;
+ buffering_msg = group->pending_buffering_msg;
+ group->pending_buffering_msg = NULL;
+ GST_SOURCE_GROUP_UNLOCK (group);
+
+ GST_SOURCE_GROUP_LOCK (other_group);
+ other_group->playing = FALSE;
+ GST_SOURCE_GROUP_UNLOCK (other_group);
+
+ debug_groups (playbin);
+ GST_PLAY_BIN3_UNLOCK (playbin);
+ if (changed)
+ gst_play_bin3_check_group_status (playbin);
+ else
+ GST_DEBUG_OBJECT (bin, "Groups didn't changed");
+ /* If there was a pending buffering message to send, do it now */
+ if (buffering_msg)
+ GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
+ } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
+ GstSourceGroup *group;
- if (pending) {
+ /* Only post buffering messages for group which is currently playing */
+ group = find_source_group_owner (playbin, msg->src);
+ GST_SOURCE_GROUP_LOCK (group);
+ if (!group->playing) {
GST_DEBUG_OBJECT (playbin, "Storing buffering message from pending group "
"%p %" GST_PTR_FORMAT, group, msg);
gst_message_replace (&group->pending_buffering_msg, msg);
GST_SOURCE_GROUP_UNLOCK (group);
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_COLLECTION) {
GstStreamCollection *collection = NULL;
- GstObject *src = GST_MESSAGE_SRC (msg);
- gboolean pstate = playbin->do_stream_selections;
gst_message_parse_stream_collection (msg, &collection);
if (collection) {
+ gboolean pstate = playbin->do_stream_selections;
+ GstSourceGroup *target_group = NULL;
+
GST_PLAY_BIN3_LOCK (playbin);
GST_DEBUG_OBJECT (playbin,
- "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT, src);
- gst_object_replace ((GstObject **) & playbin->collection,
- (GstObject *) collection);
- update_combiner_info (playbin);
+ "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT,
+ msg->src);
+ target_group = find_source_group_owner (playbin, msg->src);
+ if (target_group)
+ gst_object_replace ((GstObject **) & target_group->collection,
+ (GstObject *) collection);
+ /* FIXME: Only do the following if it's the current group? */
+ if (target_group == playbin->curr_group)
+ update_combiner_info (playbin, target_group->collection);
if (pstate)
playbin->do_stream_selections = FALSE;
- do_stream_selection (playbin);
+ do_stream_selection (playbin, target_group);
if (pstate)
playbin->do_stream_selections = TRUE;
GST_PLAY_BIN3_UNLOCK (playbin);
gst_object_unref (collection);
}
- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAMS_SELECTED) {
- GstStreamCollection *collection = NULL;
- GstObject *src = GST_MESSAGE_SRC (msg);
- gboolean pstate = playbin->do_stream_selections;
-
- gst_message_parse_streams_selected (msg, &collection);
- if (collection) {
- guint i, len;
- GST_PLAY_BIN3_LOCK (playbin);
- GST_DEBUG_OBJECT (playbin,
- "STREAMS_SELECTED: Got a collection from %" GST_PTR_FORMAT, src);
- gst_object_replace ((GstObject **) & playbin->collection,
- (GstObject *) collection);
- update_combiner_info (playbin);
- len = gst_message_streams_selected_get_size (msg);
- for (i = 0; i < len; i++) {
- GstStream *stream;
-
- stream = gst_message_streams_selected_get_stream (msg, i);
- set_selected_stream (playbin, stream);
- gst_object_unref (stream);
- }
- if (pstate)
- playbin->do_stream_selections = FALSE;
- do_stream_selection (playbin);
- if (pstate)
- playbin->do_stream_selections = TRUE;
- GST_PLAY_BIN3_UNLOCK (playbin);
-
- gst_object_unref (collection);
- }
- }
+ }
+beach:
if (msg)
GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
}
GST_BIN_CLASS (parent_class)->deep_element_added (playbin, sub_bin, child);
}
+/* Returns current stream number, or -1 if none has been selected yet */
+static int
+get_current_stream_number (GstPlayBin3 * playbin, GstSourceCombine * combine,
+ GPtrArray * channels)
+{
+ /* Internal API cleanup would make this easier... */
+ int i;
+ GstPad *pad, *current;
+ GstObject *combiner = NULL;
+ int ret = -1;
+
+ if (!combine->has_active_pad) {
+ GST_WARNING_OBJECT (playbin,
+ "combiner doesn't have the \"active-pad\" property");
+ return ret;
+ }
+
+ for (i = 0; i < channels->len; i++) {
+ pad = g_ptr_array_index (channels, i);
+ if ((combiner = gst_pad_get_parent (pad))) {
+ g_object_get (combiner, "active-pad", ¤t, NULL);
+ gst_object_unref (combiner);
+
+ if (pad == current) {
+ gst_object_unref (current);
+ ret = i;
+ break;
+ }
+
+ if (current)
+ gst_object_unref (current);
+ }
+ }
+
+ return ret;
+}
+
static void
combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
GstPlayBin3 * playbin)
{
- const gchar *property;
GstSourceCombine *combine = NULL;
GPtrArray *channels = NULL;
int i;
switch (combine->type) {
case GST_PLAY_SINK_TYPE_VIDEO:
case GST_PLAY_SINK_TYPE_VIDEO_RAW:
- property = "current-video";
playbin->current_video = get_current_stream_number (playbin,
combine, channels);
GST_PLAY_BIN3_UNLOCK (playbin);
gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
"playsink-custom-video-flush-finish");
- goto notify;
}
break;
case GST_PLAY_SINK_TYPE_AUDIO:
case GST_PLAY_SINK_TYPE_AUDIO_RAW:
- property = "current-audio";
playbin->current_audio = get_current_stream_number (playbin,
combine, channels);
GST_PLAY_BIN3_UNLOCK (playbin);
gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
"playsink-custom-audio-flush-finish");
- goto notify;
}
break;
case GST_PLAY_SINK_TYPE_TEXT:
- property = "current-text";
playbin->current_text = get_current_stream_number (playbin,
combine, channels);
GST_PLAY_BIN3_UNLOCK (playbin);
gst_play_bin3_send_custom_event (GST_OBJECT (combiner),
"playsink-custom-subtitle-flush-finish");
- goto notify;
}
break;
default:
- property = NULL;
+ break;
}
GST_PLAY_BIN3_UNLOCK (playbin);
-
-notify:
- if (property)
- g_object_notify (G_OBJECT (playbin), property);
}
static GstCaps *
return out_caps;
}
+static void
+emit_about_to_finish (GstPlayBin3 * playbin)
+{
+ GST_DEBUG_OBJECT (playbin, "Emitting about-to-finish");
+
+ /* after this call, we should have a next group to activate or we EOS */
+ g_signal_emit (G_OBJECT (playbin),
+ gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+
+ debug_groups (playbin);
+
+ /* now activate the next group. If the app did not set a uri, this will
+ * fail and we can do EOS */
+ setup_next_source (playbin);
+}
+
+static SourcePad *
+find_source_pad (GstSourceGroup * group, GstPad * target)
+{
+ GList *tmp;
+
+ for (tmp = group->source_pads; tmp; tmp = tmp->next) {
+ SourcePad *res = (SourcePad *) tmp->data;
+ if (res->pad == target)
+ return res;
+ }
+ return NULL;
+}
+
static GstPadProbeReturn
_decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
{
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
- GstPlayBin3 *playbin = (GstPlayBin3 *) udata;
+ GstSourceGroup *group = (GstSourceGroup *) udata;
+ GstPlayBin3 *playbin = group->playbin;
GstEvent *event = GST_PAD_PROBE_INFO_DATA (info);
switch (GST_EVENT_TYPE (event)) {
}
break;
}
+ case GST_EVENT_STREAM_START:
+ {
+ guint group_id;
+ if (gst_event_parse_group_id (event, &group_id)) {
+ GST_LOG_OBJECT (pad, "STREAM_START group_id:%u", group_id);
+ if (group->group_id == GST_GROUP_ID_INVALID)
+ group->group_id = group_id;
+ else if (group->group_id != group_id) {
+ GST_DEBUG_OBJECT (pad, "group_id changing from %u to %u",
+ group->group_id, group_id);
+ group->group_id = group_id;
+ }
+ }
+ break;
+ }
default:
break;
}
return ret;
}
-/* this function is called when a new pad is added to decodebin. We check the
- * type of the pad and add it to the combiner element
- */
static void
-pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin)
+control_source_pad (GstSourceGroup * group, GstPad * pad,
+ GstStreamType stream_type)
{
- GstPad *sinkpad;
- GstPadLinkReturn res;
- GstSourceCombine *combine = NULL;
- GstStreamType stream_type;
- gint pb_stream_type = -1;
- GstElement *custom_combiner = NULL;
- gulong event_probe_handler;
- gchar *pad_name;
+ SourcePad *sourcepad = g_slice_new0 (SourcePad);
- GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
+ sourcepad->pad = pad;
+ sourcepad->event_probe_id =
+ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ _decodebin_event_probe, group, NULL);
+ sourcepad->stream_type = stream_type;
+ group->source_pads = g_list_append (group->source_pads, sourcepad);
+}
- pad_name = gst_object_get_name (GST_OBJECT (pad));
+static void
+remove_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine)
+{
+ gint n;
- GST_DEBUG_OBJECT (playbin, "decoded pad %s:%s added",
- GST_DEBUG_PAD_NAME (pad));
+ if (combine->combiner == NULL) {
+ GST_DEBUG_OBJECT (playbin, "No combiner element to remove");
+ return;
+ }
- /* major type of the pad, this determines the combiner to use,
- try exact match first */
- if (g_str_has_prefix (pad_name, "video")) {
- stream_type = GST_STREAM_TYPE_VIDEO;
- pb_stream_type = PLAYBIN_STREAM_VIDEO;
+ /* Go over all sink pads and release them ! */
+ for (n = 0; n < combine->channels->len; n++) {
+ GstPad *sinkpad = g_ptr_array_index (combine->channels, n);
+
+ gst_element_release_request_pad (combine->combiner, sinkpad);
+ gst_object_unref (sinkpad);
+ }
+ g_ptr_array_set_size (combine->channels, 0);
+
+ gst_element_set_state (combine->combiner, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
+ combine->combiner = NULL;
+
+}
+
+/* Create the combiner element if needed for the given combine */
+static void
+create_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine)
+{
+ GstElement *custom_combiner = NULL;
+
+ if (combine->combiner) {
+ GST_WARNING_OBJECT (playbin, "Combiner element already exists!");
+ return;
+ }
+
+ if (combine->stream_type == GST_STREAM_TYPE_VIDEO)
custom_combiner = playbin->video_stream_combiner;
- } else if (g_str_has_prefix (pad_name, "audio")) {
- stream_type = GST_STREAM_TYPE_AUDIO;
- pb_stream_type = PLAYBIN_STREAM_AUDIO;
+ else if (combine->stream_type == GST_STREAM_TYPE_AUDIO)
custom_combiner = playbin->audio_stream_combiner;
- } else if (g_str_has_prefix (pad_name, "text")) {
- stream_type = GST_STREAM_TYPE_TEXT;
- pb_stream_type = PLAYBIN_STREAM_TEXT;
+ else if (combine->stream_type == GST_STREAM_TYPE_TEXT)
custom_combiner = playbin->text_stream_combiner;
- }
- g_free (pad_name);
+ combine->combiner = custom_combiner;
- /* no stream type found for the media type, don't bother linking it to a
- * combiner. This will leave the pad unlinked and thus ignored. */
- if (pb_stream_type < 0) {
- GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
- goto unknown_type;
+ if (!combine->combiner) {
+ gchar *concat_name;
+ GST_DEBUG_OBJECT (playbin,
+ "No custom combiner requested, using 'concat' element");
+ concat_name = g_strdup_printf ("%s-concat", combine->media_type);
+ combine->combiner = gst_element_factory_make ("concat", concat_name);
+ g_object_set (combine->combiner, "adjust-base", FALSE, NULL);
+ g_free (concat_name);
+ combine->is_concat = TRUE;
}
- combine = &playbin->combiner[pb_stream_type];
+ combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
- if (custom_combiner && combine->combiner == NULL) {
- combine->combiner = custom_combiner;
- /* find out which properties the stream combiner supports */
+ /* We only want to use 'active-pad' if it's a regular combiner that
+ * will consume all streams, and not concat (which is just used for
+ * gapless) */
+ if (!combine->is_concat) {
combine->has_active_pad =
g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
"active-pad") != NULL;
if (combine->has_active_pad)
g_signal_connect (combine->combiner, "notify::active-pad",
G_CALLBACK (combiner_active_pad_changed), playbin);
-
- GST_DEBUG_OBJECT (playbin, "adding new stream combiner %p",
- combine->combiner);
- gst_element_set_state (combine->combiner, GST_STATE_PAUSED);
- gst_bin_add (GST_BIN_CAST (playbin), combine->combiner);
}
- GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
+ GST_DEBUG_OBJECT (playbin, "adding new stream combiner %" GST_PTR_FORMAT,
+ combine->combiner);
+ gst_bin_add (GST_BIN_CAST (playbin), combine->combiner);
+ gst_element_sync_state_with_parent (combine->combiner);
+}
- if (combine->srcpad == NULL) {
- if (combine->combiner) {
- /* save source pad of the combiner */
- combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
- } else {
- /* no combiner, use the pad as the source pad then */
- combine->srcpad = gst_object_ref (pad);
- }
+static gboolean
+combiner_control_pad (GstPlayBin3 * playbin, GstSourceCombine * combine,
+ GstPad * srcpad)
+{
+ GstPadLinkReturn res;
- /* block the combiner srcpad. It's possible that multiple source elements
- * pushing data into the combiners before we have a chance to collect all
- * streams and connect the sinks, resulting in not-linked errors. After we
- * configure the sinks we will unblock them all. */
- GST_DEBUG_OBJECT (playbin, "blocking %" GST_PTR_FORMAT, combine->srcpad);
- combine->block_id =
- gst_pad_add_probe (combine->srcpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
- block_serialized_data_cb, NULL, NULL);
- }
+ GST_DEBUG_OBJECT (playbin, "srcpad %" GST_PTR_FORMAT, srcpad);
- /* get sinkpad for the new stream */
if (combine->combiner) {
- if ((sinkpad = gst_element_get_request_pad (combine->combiner, "sink_%u"))) {
- GST_DEBUG_OBJECT (playbin, "got pad %s:%s from combiner",
- GST_DEBUG_PAD_NAME (sinkpad));
+ GstPad *sinkpad =
+ gst_element_get_request_pad (combine->combiner, "sink_%u");
- /* find out which properties the sink pad supports */
- combine->has_always_ok =
- g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad),
- "always-ok") != NULL;
+ if (sinkpad == NULL)
+ goto request_pad_failed;
- /* store the combiner for the pad */
- g_object_set_data (G_OBJECT (sinkpad), "playbin.combine", combine);
+ GST_DEBUG_OBJECT (playbin, "Got new combiner pad %" GST_PTR_FORMAT,
+ sinkpad);
- /* store the pad in the array */
- GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad);
- g_ptr_array_add (combine->channels, sinkpad);
+ /* store the pad in the array */
+ GST_DEBUG_OBJECT (playbin, "pad %" GST_PTR_FORMAT " added to array",
+ sinkpad);
+ g_ptr_array_add (combine->channels, sinkpad);
- res = gst_pad_link (pad, sinkpad);
- if (GST_PAD_LINK_FAILED (res))
- goto link_failed;
+ res = gst_pad_link (srcpad, sinkpad);
+ if (GST_PAD_LINK_FAILED (res))
+ goto failed_combiner_link;
- /* store combiner pad so we can release it */
- g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad);
+ GST_DEBUG_OBJECT (playbin,
+ "linked pad %" GST_PTR_FORMAT " to combiner %" GST_PTR_FORMAT, srcpad,
+ combine->combiner);
- GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to combiner %p",
- GST_DEBUG_PAD_NAME (pad), combine->combiner);
- } else {
- goto request_pad_failed;
- }
} else {
- /* no combiner, don't configure anything, we'll link the new pad directly to
- * the sink. */
- sinkpad = NULL;
+ GST_LOG_OBJECT (playbin, "combine->sinkpad:%" GST_PTR_FORMAT,
+ combine->sinkpad);
+ g_assert (combine->sinkpad != NULL);
+ /* Connect directly to playsink */
+ if (gst_pad_is_linked (combine->sinkpad))
+ goto sinkpad_already_linked;
- /* store the combiner for the pad */
- g_object_set_data (G_OBJECT (pad), "playbin.combine", combine);
- }
+ GST_DEBUG_OBJECT (playbin, "Linking new pad straight to playsink");
+ res = gst_pad_link (srcpad, combine->sinkpad);
- event_probe_handler =
- gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
- _decodebin_event_probe, playbin, NULL);
- g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id",
- ULONG_TO_POINTER (event_probe_handler));
-
- playbin->active_stream_types |= stream_type;
-
- /* If we're expecting either audio or video,
- * wait for them to appear before configuring playsink */
- if ((playbin->selected_stream_types & ~playbin->active_stream_types &
- (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO))
- == 0) {
- no_more_pads_cb (decodebin, playbin);
- } else {
- GST_LOG_OBJECT (playbin, "Active stream types 0x%x, want 0x%x. Waiting",
- playbin->active_stream_types, playbin->selected_stream_types);
+ if (res != GST_PAD_LINK_OK)
+ goto failed_sinkpad_link;
}
- return;
-
- /* ERRORS */
-unknown_type:
- GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type");
- return;
+ return TRUE;
-link_failed:
- {
- GST_ERROR_OBJECT (playbin,
- "failed to link pad %s:%s to combiner, reason %s (%d)",
- GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
- return;
- }
+ /* Failure cases */
request_pad_failed:
GST_ELEMENT_ERROR (playbin, CORE, PAD,
("Internal playbin error."),
("Failed to get request pad from combiner %p.", combine->combiner));
- return;
-shutdown:
- {
- GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked");
- /* not going to done as we didn't request the caps */
- return;
+ return FALSE;
+
+
+sinkpad_already_linked:
+ GST_ELEMENT_ERROR (playbin, CORE, PAD,
+ ("Internal playbin error."), ("playsink pad already used !"));
+ return FALSE;
+
+failed_sinkpad_link:
+ GST_ELEMENT_ERROR (playbin, CORE, PAD,
+ ("Internal playbin error."),
+ ("Failed to link pad to sink. Error %d", res));
+ return FALSE;
+
+failed_combiner_link:
+ GST_ELEMENT_ERROR (playbin, CORE, PAD,
+ ("Internal playbin error."),
+ ("Failed to link pad to combiner. Error %d", res));
+ return FALSE;
+}
+
+static void
+combiner_release_pad (GstPlayBin3 * playbin, GstSourceCombine * combine,
+ GstPad * pad)
+{
+ if (combine->combiner) {
+ GstPad *peer = gst_pad_get_peer (pad);
+
+ if (peer) {
+ GST_DEBUG_OBJECT (playbin, "Removing combiner pad %" GST_PTR_FORMAT,
+ peer);
+ g_ptr_array_remove (combine->channels, peer);
+
+ gst_element_release_request_pad (combine->combiner, peer);
+ gst_object_unref (peer);
+ }
+ } else {
+ /* Release direct link if present */
+ if (combine->sinkpad) {
+ GST_DEBUG_OBJECT (playbin, "Unlinking pad from playsink sinkpad");
+ gst_pad_unlink (pad, combine->sinkpad);
+ }
}
}
-/* called when a pad is removed from the decodebin. We unlink the pad from
- * the combiner. This will make the combiner select a new pad. */
+/* Call after pad was unlinked from (potential) combiner */
static void
-pad_removed_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin)
+release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad)
{
- GstPad *peer;
- GstElement *combiner;
- GstSourceCombine *combine;
- gulong event_probe_handler;
- GstStreamType stream_type = GST_STREAM_TYPE_UNKNOWN;
- gchar *pad_name;
+ SourcePad *sourcepad;
+ GList *tmp;
+ GstStreamType alltype = 0;
- GST_DEBUG_OBJECT (playbin,
- "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad));
+ sourcepad = find_source_pad (group, pad);
+ if (!sourcepad) {
+ GST_DEBUG_OBJECT (playbin, "Not a pad controlled by us ?");
+ return;
+ }
- GST_PLAY_BIN3_LOCK (playbin);
+ if (sourcepad->event_probe_id) {
+ gst_pad_remove_probe (pad, sourcepad->event_probe_id);
+ sourcepad->event_probe_id = 0;
+ }
- if ((event_probe_handler =
- POINTER_TO_ULONG (g_object_get_data (G_OBJECT (pad),
- "playbin.event_probe_id")))) {
- gst_pad_remove_probe (pad, event_probe_handler);
- g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", NULL);
+ /* Remove from list of controlled pads and check again for EOS status */
+ group->source_pads = g_list_remove (group->source_pads, sourcepad);
+ g_slice_free (SourcePad, sourcepad);
+
+ /* Update present stream types */
+ for (tmp = group->source_pads; tmp; tmp = tmp->next) {
+ SourcePad *cand = (SourcePad *) tmp->data;
+ alltype |= cand->stream_type;
}
+ group->present_stream_types = alltype;
+}
+
+/* this function is called when a new pad is added to decodebin. We check the
+ * type of the pad and add it to the combiner element
+ */
+static void
+pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstSourceGroup * group)
+{
+ GstSourceCombine *combine = NULL;
+ gint pb_stream_type = -1;
+ gchar *pad_name;
+ GstPlayBin3 *playbin = group->playbin;
+
+ GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
pad_name = gst_object_get_name (GST_OBJECT (pad));
+ GST_DEBUG_OBJECT (playbin, "decoded pad %s:%s added",
+ GST_DEBUG_PAD_NAME (pad));
+
+ /* major type of the pad, this determines the combiner to use,
+ try exact match first */
if (g_str_has_prefix (pad_name, "video")) {
- stream_type = GST_STREAM_TYPE_VIDEO;
+ pb_stream_type = PLAYBIN_STREAM_VIDEO;
} else if (g_str_has_prefix (pad_name, "audio")) {
- stream_type = GST_STREAM_TYPE_AUDIO;
+ pb_stream_type = PLAYBIN_STREAM_AUDIO;
} else if (g_str_has_prefix (pad_name, "text")) {
- stream_type = GST_STREAM_TYPE_TEXT;
+ pb_stream_type = PLAYBIN_STREAM_TEXT;
}
g_free (pad_name);
- if ((combine = g_object_get_data (G_OBJECT (pad), "playbin.combine"))) {
- g_assert (combine->combiner == NULL);
- g_assert (combine->srcpad == pad);
- source_combine_remove_pads (playbin, combine);
- playbin->active_stream_types &= ~stream_type;
- goto exit;
+ /* no stream type found for the media type, don't bother linking it to a
+ * combiner. This will leave the pad unlinked and thus ignored. */
+ if (pb_stream_type < 0) {
+ GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
+ goto unknown_type;
}
- /* get the combiner sinkpad */
- if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin.sinkpad")))
- goto not_linked;
-
- /* unlink the pad now (can fail, the pad is unlinked before it's removed) */
- gst_pad_unlink (pad, peer);
-
- /* get combiner */
- combiner = GST_ELEMENT_CAST (gst_pad_get_parent (peer));
- g_assert (combiner != NULL);
-
- if ((combine = g_object_get_data (G_OBJECT (peer), "playbin.combine"))) {
- /* remove the pad from the array */
- g_ptr_array_remove (combine->channels, peer);
- GST_DEBUG_OBJECT (playbin, "pad %p removed from array", peer);
-
- if (!combine->channels->len && combine->combiner) {
- GST_DEBUG_OBJECT (playbin, "all combiner sinkpads removed");
- GST_DEBUG_OBJECT (playbin, "removing combiner %p", combine->combiner);
- source_combine_remove_pads (playbin, combine);
- gst_element_set_state (combine->combiner, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
- combine->combiner = NULL;
- playbin->active_stream_types &= ~stream_type;
- }
- }
+ combine = &playbin->combiner[pb_stream_type];
- /* release the pad to the combiner, this will make the combiner choose a new
- * pad. */
- gst_element_release_request_pad (combiner, peer);
- gst_object_unref (peer);
+ combiner_control_pad (playbin, combine, pad);
- gst_object_unref (combiner);
-exit:
- GST_PLAY_BIN3_UNLOCK (playbin);
+ control_source_pad (group, pad, combine->stream_type);
- if ((playbin->selected_stream_types & ~playbin->active_stream_types &
- (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO))
- == 0) {
- no_more_pads_cb (decodebin, playbin);
+ /* Update present stream_types and check whether we should post a pending about-to-finish */
+ group->present_stream_types |= combine->stream_type;
+
+ if (group->playing && group->pending_about_to_finish
+ && group->present_stream_types == group->selected_stream_types) {
+ group->pending_about_to_finish = FALSE;
+ emit_about_to_finish (playbin);
}
+ GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
+
return;
/* ERRORS */
-not_linked:
+unknown_type:
+ GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type");
+ return;
+
+shutdown:
{
- GST_DEBUG_OBJECT (playbin, "pad not linked");
- goto exit;
+ GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked");
+ /* not going to done as we didn't request the caps */
+ return;
}
}
+/* called when a pad is removed from the decodebin. We unlink the pad from
+ * the combiner. */
+static void
+pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
+{
+ GstSourceCombine *combine;
+ GstPlayBin3 *playbin = group->playbin;
+
+ GST_DEBUG_OBJECT (playbin,
+ "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad));
+
+ GST_PLAY_BIN3_LOCK (playbin);
+
+ /* Get combiner for pad */
+ if (g_str_has_prefix (GST_PAD_NAME (pad), "video"))
+ combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO];
+ else if (g_str_has_prefix (GST_PAD_NAME (pad), "audio"))
+ combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO];
+ else if (g_str_has_prefix (GST_PAD_NAME (pad), "text"))
+ combine = &playbin->combiner[PLAYBIN_STREAM_TEXT];
+ else
+ return;
+
+ combiner_release_pad (playbin, combine, pad);
+ release_source_pad (playbin, group, pad);
+
+ GST_PLAY_BIN3_UNLOCK (playbin);
+}
+
static gint
select_stream_cb (GstElement * decodebin, GstStreamCollection * collection,
- GstStream * stream, GstPlayBin3 * playbin)
+ GstStream * stream, GstSourceGroup * group)
{
GstStreamType stype = gst_stream_get_stream_type (stream);
GstElement *combiner = NULL;
+ GstPlayBin3 *playbin = group->playbin;
if (stype & GST_STREAM_TYPE_AUDIO)
combiner = playbin->audio_stream_combiner;
return -1;
}
-/* we get called when all pads are available and we must connect the sinks to
- * them.
- * The main purpose of the code is to see if we have video/audio and subtitles
- * and pick the right pipelines to display them.
- *
- * The combiners installed on the group tell us about the presence of
- * audio/video and subtitle streams. This allows us to see if we need
- * visualisation, video or/and audio.
+/* We get called when the selected stream types change and
+ * reconfiguration of output (i.e. playsink and potential combiners)
+ * are required.
*/
static void
-no_more_pads_cb (GstElement * decodebin, GstPlayBin3 * playbin)
+reconfigure_output (GstPlayBin3 * playbin)
{
- GstSourceGroup *group;
GstPadLinkReturn res;
gint i;
- gboolean configure;
- GST_DEBUG_OBJECT (playbin, "no more pads");
+ g_assert (playbin->selected_stream_types != playbin->active_stream_types);
- GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown);
+ GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT,
+ STREAM_TYPES_ARGS (playbin->selected_stream_types));
+ GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT,
+ STREAM_TYPES_ARGS (playbin->active_stream_types));
GST_PLAY_BIN3_LOCK (playbin);
- group = playbin->curr_group;
+ /* Make sure combiners/playsink are in sync with selected stream types */
for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceCombine *combine = &playbin->combiner[i];
-
- /* check if the specific media type was detected and thus has a combiner
- * created for it. If there is the media type, get a sinkpad from the sink
- * and link it. We only do this if we have not yet requested the sinkpad
- * before. */
- if (combine->srcpad && combine->sinkpad == NULL) {
- GST_DEBUG_OBJECT (playbin, "requesting new sink pad %d", combine->type);
- combine->sinkpad =
- gst_play_sink_request_pad (playbin->playsink, combine->type);
- gst_object_ref (combine->sinkpad);
- } else if (combine->srcpad && combine->sinkpad) {
- GST_DEBUG_OBJECT (playbin, "re-using sink pad %d", combine->type);
- } else if (combine->sinkpad && combine->srcpad == NULL) {
- GST_DEBUG_OBJECT (playbin, "releasing sink pad %d", combine->type);
- gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
- gst_object_unref (combine->sinkpad);
- combine->sinkpad = NULL;
- }
- if (combine->sinkpad && combine->srcpad &&
- !gst_pad_is_linked (combine->srcpad)) {
- res = gst_pad_link (combine->srcpad, combine->sinkpad);
- GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d",
- combine->media_type, res);
- if (res != GST_PAD_LINK_OK) {
- GST_ELEMENT_ERROR (playbin, CORE, PAD,
- ("Internal playbin error."),
- ("Failed to link combiner to sink. Error %d", res));
- }
- }
- }
- GST_PLAY_BIN3_UNLOCK (playbin);
-
- GST_SOURCE_GROUP_LOCK (group);
- GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending,
- group->pending - 1);
-
- if (group->pending > 0)
- group->pending--;
-
- if (group->pending == 0) {
- /* we are the last group to complete, we will configure the output and then
- * signal the other waiters. */
- GST_LOG_OBJECT (playbin, "last group complete");
- configure = TRUE;
- } else {
- GST_LOG_OBJECT (playbin, "have more pending groups");
- configure = FALSE;
- }
- GST_SOURCE_GROUP_UNLOCK (group);
-
- if (configure) {
- /* if we have custom sinks, configure them now */
- GST_SOURCE_GROUP_LOCK (group);
-
- if (group->audio_sink) {
- GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT,
- group->audio_sink);
- gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
- group->audio_sink);
- }
-
- if (group->video_sink) {
- GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT,
- group->video_sink);
- gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
- group->video_sink);
- }
-
- if (group->text_sink) {
- GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT,
- group->text_sink);
- gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT,
- group->text_sink);
- }
-
- GST_SOURCE_GROUP_UNLOCK (group);
-
- /* signal the other combiners that they can continue now. */
- GST_PLAY_BIN3_LOCK (playbin);
- /* unblock all combiners */
- for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
- GstSourceCombine *combine = &playbin->combiner[i];
-
+ gboolean is_selected =
+ (combine->stream_type & playbin->selected_stream_types) ==
+ combine->stream_type;
+ gboolean is_active =
+ (combine->stream_type & playbin->active_stream_types) ==
+ combine->stream_type;
+
+ GST_DEBUG_OBJECT (playbin, "Stream type status: '%s' %s %s",
+ combine->media_type, is_selected ? "selected" : "NOT selected",
+ is_active ? "active" : "NOT active");
+ /* FIXME : Remove asserts below once enough testing has been done */
+
+ if (is_selected && is_active) {
+ GST_DEBUG_OBJECT (playbin, "Stream type '%s' already active",
+ combine->media_type);
+ } else if (is_active && !is_selected) {
+ GST_DEBUG_OBJECT (playbin, "Stream type '%s' is no longer requested",
+ combine->media_type);
+
+ /* Unlink combiner from sink */
if (combine->srcpad) {
- GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
- combine->srcpad);
- if (combine->block_id) {
- gst_pad_remove_probe (combine->srcpad, combine->block_id);
- combine->block_id = 0;
- }
+ GST_LOG_OBJECT (playbin, "Unlinking from sink");
+ if (combine->sinkpad)
+ gst_pad_unlink (combine->srcpad, combine->sinkpad);
+ gst_object_unref (combine->srcpad);
+ combine->srcpad = NULL;
}
- }
- GST_PLAY_BIN3_UNLOCK (playbin);
- gst_play_sink_reconfigure (playbin->playsink);
- }
- GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
+ if (combine->sinkpad) {
+ /* Release playsink sink pad */
+ GST_LOG_OBJECT (playbin, "Releasing playsink pad");
+ gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
+ gst_object_unref (combine->sinkpad);
+ combine->sinkpad = NULL;
+ }
- if (configure) {
- do_async_done (playbin);
- }
+ /* Release combiner */
+ GST_FIXME_OBJECT (playbin, "Release combiner");
+ remove_combiner (playbin, combine);
+ } else if (!is_active && is_selected) {
+ GST_DEBUG_OBJECT (playbin, "Stream type '%s' is now requested",
+ combine->media_type);
- return;
+ /* If we are shutting down, do *not* add more combiners */
+ if (g_atomic_int_get (&playbin->shutdown))
+ continue;
-shutdown:
- {
- GST_DEBUG ("ignoring, we are shutting down");
- /* Request a flushing pad from playsink that we then link to the combiner.
- * Then we unblock the combiners so that they stop with a WRONG_STATE
- * instead of a NOT_LINKED error.
- */
- GST_PLAY_BIN3_LOCK (playbin);
- for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
- GstSourceCombine *combine = &playbin->combiner[i];
+ g_assert (combine->sinkpad == NULL);
- if (combine->srcpad) {
- if (combine->sinkpad == NULL) {
- GST_DEBUG_OBJECT (playbin, "requesting new flushing sink pad");
- combine->sinkpad =
- gst_play_sink_request_pad (playbin->playsink,
- GST_PLAY_SINK_TYPE_FLUSHING);
- gst_object_ref (combine->sinkpad);
- res = gst_pad_link (combine->srcpad, combine->sinkpad);
- GST_DEBUG_OBJECT (playbin, "linked flushing, result: %d", res);
- }
- GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
- combine->srcpad);
- if (combine->block_id) {
- gst_pad_remove_probe (combine->srcpad, combine->block_id);
- combine->block_id = 0;
+ /* Request playsink sink pad */
+ combine->sinkpad =
+ gst_play_sink_request_pad (playbin->playsink, combine->type);
+ gst_object_ref (combine->sinkpad);
+ /* Create combiner if needed and link it */
+ create_combiner (playbin, combine);
+ if (combine->combiner) {
+ res = gst_pad_link (combine->srcpad, combine->sinkpad);
+ GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d",
+ combine->media_type, res);
+ if (res != GST_PAD_LINK_OK) {
+ GST_ELEMENT_ERROR (playbin, CORE, PAD,
+ ("Internal playbin error."),
+ ("Failed to link combiner to sink. Error %d", res));
}
+
}
}
- GST_PLAY_BIN3_UNLOCK (playbin);
- return;
}
+
+ playbin->active_stream_types = playbin->selected_stream_types;
+
+ GST_PLAY_BIN3_UNLOCK (playbin);
+
+ gst_play_sink_reconfigure (playbin->playsink);
+
+ do_async_done (playbin);
+
+ GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT,
+ STREAM_TYPES_ARGS (playbin->selected_stream_types));
+ GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT,
+ STREAM_TYPES_ARGS (playbin->active_stream_types));
+
+ return;
}
-#if 0
static void
-drained_cb (GstElement * decodebin, GstSourceGroup * group)
+about_to_finish_cb (GstElement * uridecodebin, GstSourceGroup * group)
{
- GstPlayBin3 *playbin;
-
- playbin = group->playbin;
-
+ GstPlayBin3 *playbin = group->playbin;
GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
- /* after this call, we should have a next group to activate or we EOS */
- g_signal_emit (G_OBJECT (playbin),
- gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+ GST_LOG_OBJECT (playbin, "selected_stream_types:%" STREAM_TYPES_FORMAT,
+ STREAM_TYPES_ARGS (group->selected_stream_types));
+ GST_LOG_OBJECT (playbin, "present_stream_types:%" STREAM_TYPES_FORMAT,
+ STREAM_TYPES_ARGS (group->present_stream_types));
- /* now activate the next group. If the app did not set a uri, this will
- * fail and we can do EOS */
- setup_next_source (playbin, GST_STATE_PAUSED);
+ if (group->selected_stream_types == 0
+ || (group->selected_stream_types != group->present_stream_types)) {
+ GST_LOG_OBJECT (playbin,
+ "Delaying emission of signal until this group is ready");
+ group->pending_about_to_finish = TRUE;
+ } else
+ emit_about_to_finish (playbin);
}
-#endif
+#if 0 /* AUTOPLUG DISABLED */
/* Like gst_element_factory_can_sink_any_caps() but doesn't
* allow ANY caps on the sinkpad template */
static gboolean
return result;
}
-
-static void
-gst_play_bin3_set_context (GstElement * element, GstContext * context)
-{
- GstPlayBin3 *playbin = GST_PLAY_BIN3 (element);
-
- /* Proxy contexts to the sinks, they might not be in playsink yet */
- GST_PLAY_BIN3_LOCK (playbin);
- if (playbin->audio_sink)
- gst_element_set_context (playbin->audio_sink, context);
- if (playbin->video_sink)
- gst_element_set_context (playbin->video_sink, context);
- if (playbin->text_sink)
- gst_element_set_context (playbin->text_sink, context);
-
- GST_SOURCE_GROUP_LOCK (playbin->curr_group);
-
- if (playbin->curr_group->audio_sink)
- gst_element_set_context (playbin->curr_group->audio_sink, context);
- if (playbin->curr_group->video_sink)
- gst_element_set_context (playbin->curr_group->video_sink, context);
- if (playbin->curr_group->text_sink)
- gst_element_set_context (playbin->curr_group->text_sink, context);
-
- GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
- GST_PLAY_BIN3_UNLOCK (playbin);
-
- GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
-}
-
-/* Pass sink messages to the application, e.g. NEED_CONTEXT messages */
-static void
-gst_play_bin3_update_context (GstPlayBin3 * playbin, GstContext * context)
-{
- GList *l;
- const gchar *context_type;
-
- GST_OBJECT_LOCK (playbin);
- context_type = gst_context_get_context_type (context);
- for (l = playbin->contexts; l; l = l->next) {
- GstContext *tmp = l->data;
- const gchar *tmp_type = gst_context_get_context_type (tmp);
-
- /* Always store newest context but never replace
- * a persistent one by a non-persistent one */
- if (strcmp (context_type, tmp_type) == 0 &&
- (gst_context_is_persistent (context) ||
- !gst_context_is_persistent (tmp))) {
- gst_context_replace ((GstContext **) & l->data, context);
- break;
- }
- }
- /* Not found? Add */
- if (l == NULL)
- playbin->contexts =
- g_list_prepend (playbin->contexts, gst_context_ref (context));
- GST_OBJECT_UNLOCK (playbin);
-}
+#endif
static GstBusSyncReply
activate_sink_bus_handler (GstBus * bus, GstMessage * msg,
gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
else
gst_message_unref (msg);
- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
- const gchar *context_type;
- GList *l;
-
- gst_message_parse_context_type (msg, &context_type);
- GST_OBJECT_LOCK (playbin);
- for (l = playbin->contexts; l; l = l->next) {
- GstContext *tmp = l->data;
- const gchar *tmp_type = gst_context_get_context_type (tmp);
-
- if (strcmp (context_type, tmp_type) == 0) {
- gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), l->data);
- break;
- }
- }
- GST_OBJECT_UNLOCK (playbin);
-
- /* Forward if we couldn't answer the message */
- if (l == NULL) {
- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
- } else {
- gst_message_unref (msg);
- }
- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
- GstContext *context;
-
- gst_message_parse_have_context (msg, &context);
- gst_play_bin3_update_context (playbin, context);
- gst_context_unref (context);
-
- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
} else {
gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
}
return ret;
}
+#if 0 /* AUTOPLUG DISABLED */
/* autoplug-continue decides, if a pad has raw caps that can be exposed
* directly or if further decoding is necessary. We use this to expose
* supported subtitles directly */
return FALSE;
}
}
-
-static void
-notify_source_cb (GstElement * urisourcebin, GParamSpec * pspec,
- GstSourceGroup * group)
-{
- GstPlayBin3 *playbin;
- GstElement *source;
-
- playbin = group->playbin;
-
- g_object_get (urisourcebin, "source", &source, NULL);
-
- GST_OBJECT_LOCK (playbin);
- if (playbin->source)
- gst_object_unref (playbin->source);
- playbin->source = source;
- GST_OBJECT_UNLOCK (playbin);
-
- g_object_notify (G_OBJECT (playbin), "source");
-
- g_signal_emit (playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP],
- 0, playbin->source);
-}
+#endif
/* must be called with the group lock */
static gboolean
{
GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group);
- if (group->urisourcebin)
- gst_element_set_locked_state (group->urisourcebin, locked);
- if (group->suburisourcebin)
- gst_element_set_locked_state (group->suburisourcebin, locked);
+ if (group->uridecodebin)
+ gst_element_set_locked_state (group->uridecodebin, locked);
return TRUE;
}
return TRUE;
}
-static void
-urisrc_pad_added (GstElement * urisrc, GstPad * pad, GstSourceGroup * group)
-{
- GstPadLinkReturn res;
- GstPad *sinkpad = NULL;
- GstPlayBin3 *playbin;
-
- GST_SOURCE_GROUP_LOCK (group);
- playbin = group->playbin;
- if (urisrc == group->urisourcebin) {
- /* Primary stream, link to the main pad of decodebin3 */
- sinkpad = gst_element_get_static_pad (playbin->decodebin, "sink");
- if (gst_pad_is_linked (sinkpad)) {
- gst_object_unref (GST_OBJECT (sinkpad));
- sinkpad = NULL;
- }
- }
- GST_SOURCE_GROUP_UNLOCK (group);
-
- if (sinkpad == NULL) {
- /* Auxiliary stream, request a new pad from decodebin */
- if ((sinkpad = gst_element_get_request_pad (playbin->decodebin, "sink_%u"))) {
- g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad);
- }
- }
- if (sinkpad) {
- GST_DEBUG_OBJECT (playbin, "New pad %" GST_PTR_FORMAT
- " from urisourcebin %" GST_PTR_FORMAT " linking to %"
- GST_PTR_FORMAT, pad, urisrc, sinkpad);
-
- res = gst_pad_link (pad, sinkpad);
- gst_object_unref (sinkpad);
-
- if (GST_PAD_LINK_FAILED (res))
- goto link_failed;
- }
- return;
-
-link_failed:
- {
- GST_ERROR_OBJECT (playbin,
- "failed to link pad %s:%s to decodebin, reason %s (%d)",
- GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
- return;
- }
-}
static void
-urisrc_pad_removed_cb (GstElement * urisrc, GstPad * pad,
+source_setup_cb (GstElement * element, GstElement * source,
GstSourceGroup * group)
{
+ g_signal_emit (group->playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP], 0,
+ source);
}
/* must be called with PLAY_BIN_LOCK */
static GstStateChangeReturn
-activate_decodebin (GstPlayBin3 * playbin, GstState target)
-{
- GstStateChangeReturn state_ret;
- GstElement *decodebin = NULL;
-
- if (playbin->decodebin_active)
- return GST_STATE_CHANGE_SUCCESS;
-
- GST_LOG_OBJECT (playbin, "Adding and activating decodebin");
-
- if (!make_or_reuse_element (playbin, "decodebin3", &playbin->decodebin))
- goto no_decodebin;
- decodebin = playbin->decodebin;
-
- /* connect pads and other things */
- playbin->db_pad_added_id = g_signal_connect (decodebin, "pad-added",
- G_CALLBACK (pad_added_cb), playbin);
- playbin->db_pad_removed_id = g_signal_connect (decodebin, "pad-removed",
- G_CALLBACK (pad_removed_cb), playbin);
- playbin->db_no_more_pads_id = g_signal_connect (decodebin, "no-more-pads",
- G_CALLBACK (no_more_pads_cb), playbin);
- playbin->db_select_stream_id = g_signal_connect (decodebin, "select-stream",
- G_CALLBACK (select_stream_cb), playbin);
- /* is called when the decodebin is out of data and we can switch to the
- * next uri */
-#if 0
- /* FIXME: Re-enable if/when decodebin3 supports 'drained' */
- playbin->db_drained_id =
- g_signal_connect (decodebin, "drained", G_CALLBACK (drained_cb), playbin);
-#endif
-
- gst_element_set_locked_state (decodebin, TRUE);
- if ((state_ret =
- gst_element_set_state (decodebin,
- target)) == GST_STATE_CHANGE_FAILURE)
- goto decodebin_failure;
- gst_element_set_locked_state (decodebin, FALSE);
-
- playbin->decodebin_active = TRUE;
-
- return state_ret;
-
-
-no_decodebin:
- {
- GstMessage *msg;
-
- msg =
- gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
- "decodebin3");
- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
-
- GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
- (_("Could not create \"decodebin3\" element.")), (NULL));
-
- goto error_cleanup;
- }
-decodebin_failure:
- {
- GST_DEBUG_OBJECT (playbin, "failed state change of decodebin");
- goto error_cleanup;
- }
-error_cleanup:{
- if (decodebin) {
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_no_more_pads_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id);
- gst_element_set_state (decodebin, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (playbin), decodebin);
- }
- return GST_STATE_CHANGE_FAILURE;
- }
-}
-
-/* must be called with PLAY_BIN_LOCK */
-static void
-deactivate_decodebin (GstPlayBin3 * playbin)
-{
- if (playbin->decodebin) {
- GST_LOG_OBJECT (playbin, "Deactivating and removing decodebin");
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_no_more_pads_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id);
- REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id);
- gst_bin_remove (GST_BIN_CAST (playbin), playbin->decodebin);
- playbin->decodebin_active = FALSE;
- playbin->active_stream_types = 0;
- }
-}
-
-/* must be called with PLAY_BIN_LOCK */
-static GstStateChangeReturn
-activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target)
+activate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
{
- GstElement *urisrcbin = NULL;
- GstElement *suburisrcbin = NULL;
+ GstElement *uridecodebin = NULL;
GstPlayFlags flags;
gboolean audio_sink_activated = FALSE;
gboolean video_sink_activated = FALSE;
}
- if (!make_or_reuse_element (playbin, "urisourcebin", &group->urisourcebin))
- goto no_urisrcbin;
- urisrcbin = group->urisourcebin;
+ if (!make_or_reuse_element (playbin, "uridecodebin3", &group->uridecodebin))
+ goto no_uridecodebin;
+ uridecodebin = group->uridecodebin;
flags = gst_play_sink_get_flags (playbin->playsink);
- g_object_set (urisrcbin,
+ g_object_set (uridecodebin,
/* configure connection speed */
"connection-speed", playbin->connection_speed / 1000,
/* configure uri */
"buffer-size", playbin->buffer_size,
"ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
- /* we have 1 pending no-more-pads */
- group->pending = 1;
-
- group->notify_source_id = g_signal_connect (urisrcbin, "notify::source",
- G_CALLBACK (notify_source_cb), group);
-
- /* will be called when a new media type is found. We return a list of decoders
- * including sinks for decodebin to try */
- group->autoplug_factories_id =
- g_signal_connect (urisrcbin, "autoplug-factories",
- G_CALLBACK (autoplug_factories_cb), group);
- group->autoplug_select_id =
- g_signal_connect (urisrcbin, "autoplug-select",
- G_CALLBACK (autoplug_select_cb), group);
- group->autoplug_continue_id =
- g_signal_connect (urisrcbin, "autoplug-continue",
- G_CALLBACK (autoplug_continue_cb), group);
- group->autoplug_query_id =
- g_signal_connect (urisrcbin, "autoplug-query",
- G_CALLBACK (autoplug_query_cb), group);
-
- group->urisrc_pad_added_id = g_signal_connect (urisrcbin, "pad-added",
- G_CALLBACK (urisrc_pad_added), group);
- group->urisrc_pad_removed_id = g_signal_connect (urisrcbin,
- "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group);
-
- if (group->suburi) {
- /* subtitles */
- if (!make_or_reuse_element (playbin, "urisourcebin",
- &group->suburisourcebin))
- goto no_urisrcbin;
- suburisrcbin = group->suburisourcebin;
-
- g_object_set (suburisrcbin,
- /* configure connection speed */
- "connection-speed", playbin->connection_speed,
- /* configure uri */
- "uri", group->suburi, NULL);
-
- /* connect pads and other things */
- group->sub_pad_added_id = g_signal_connect (suburisrcbin, "pad-added",
- G_CALLBACK (urisrc_pad_added), group);
- group->sub_pad_removed_id = g_signal_connect (suburisrcbin,
- "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group);
-
- group->sub_autoplug_continue_id =
- g_signal_connect (suburisrcbin, "autoplug-continue",
- G_CALLBACK (autoplug_continue_cb), group);
-
- group->sub_autoplug_query_id =
- g_signal_connect (suburisrcbin, "autoplug-query",
- G_CALLBACK (autoplug_query_cb), group);
-
- /* we have 2 pending no-more-pads */
- group->pending = 2;
- group->sub_pending = TRUE;
- } else {
- group->sub_pending = FALSE;
- }
+ group->pad_added_id = g_signal_connect (uridecodebin, "pad-added",
+ G_CALLBACK (pad_added_cb), group);
+ group->pad_removed_id = g_signal_connect (uridecodebin,
+ "pad-removed", G_CALLBACK (pad_removed_cb), group);
+ group->select_stream_id = g_signal_connect (uridecodebin, "select-stream",
+ G_CALLBACK (select_stream_cb), group);
+ group->source_setup_id = g_signal_connect (uridecodebin, "source-setup",
+ G_CALLBACK (source_setup_cb), group);
+ group->about_to_finish_id =
+ g_signal_connect (uridecodebin, "about-to-finish",
+ G_CALLBACK (about_to_finish_cb), group);
+
+ if (group->suburi)
+ g_object_set (group->uridecodebin, "suburi", group->suburi, NULL);
/* release the group lock before setting the state of the source bins, they
* might fire signals in this thread that we need to handle with the
* group_lock taken. */
GST_SOURCE_GROUP_UNLOCK (group);
- if (suburisrcbin) {
- if (gst_element_set_state (suburisrcbin,
- target) == GST_STATE_CHANGE_FAILURE) {
- GST_DEBUG_OBJECT (playbin,
- "failed state change of subtitle urisourcebin");
- GST_SOURCE_GROUP_LOCK (group);
-
- REMOVE_SIGNAL (suburisrcbin, group->sub_pad_added_id);
- REMOVE_SIGNAL (suburisrcbin, group->sub_pad_removed_id);
- REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_continue_id);
- REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_query_id);
- /* Might already be removed because of an error message */
- if (GST_OBJECT_PARENT (suburisrcbin) == GST_OBJECT_CAST (playbin))
- gst_bin_remove (GST_BIN_CAST (playbin), suburisrcbin);
- if (group->sub_pending) {
- group->pending--;
- group->sub_pending = FALSE;
- }
- gst_element_set_state (suburisrcbin, GST_STATE_READY);
- g_free (group->suburi);
- group->suburi = NULL;
- GST_SOURCE_GROUP_UNLOCK (group);
- }
- }
if ((state_ret =
- gst_element_set_state (urisrcbin,
- target)) == GST_STATE_CHANGE_FAILURE)
- goto urisrcbin_failure;
+ gst_element_set_state (uridecodebin,
+ GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)
+ goto uridecodebin_failure;
GST_SOURCE_GROUP_LOCK (group);
/* allow state changes of the playbin affect the group elements now */
return state_ret;
/* ERRORS */
-no_urisrcbin:
+no_uridecodebin:
{
GstMessage *msg;
GST_SOURCE_GROUP_UNLOCK (group);
msg =
gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
- "urisourcebin");
+ "uridecodebin3");
gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
- (_("Could not create \"urisourcebin\" element.")), (NULL));
+ (_("Could not create \"uridecodebin3\" element.")), (NULL));
GST_SOURCE_GROUP_LOCK (group);
goto error_cleanup;
}
-urisrcbin_failure:
+uridecodebin_failure:
{
- GST_DEBUG_OBJECT (playbin, "failed state change of urisrcbin");
+ GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin");
GST_SOURCE_GROUP_LOCK (group);
goto error_cleanup;
}
error_cleanup:
{
+ group->selected_stream_types = 0;
+
/* delete any custom sinks we might have */
if (group->audio_sink) {
/* If this is a automatically created sink set it to NULL */
}
group->text_sink = NULL;
- if (urisrcbin) {
- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id);
- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id);
- REMOVE_SIGNAL (group->urisourcebin, group->notify_source_id);
+ if (uridecodebin) {
+ REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
+ REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
+ REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id);
+ REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
+ REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id);
+#if 0
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
+#endif
- gst_element_set_state (urisrcbin, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (playbin), urisrcbin);
+ gst_element_set_state (uridecodebin, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (playbin), uridecodebin);
}
GST_SOURCE_GROUP_UNLOCK (group);
}
}
-/* unlink a group of urisrcbin from the decodebin.
- * must be called with PLAY_BIN_LOCK */
+/* must be called with PLAY_BIN_LOCK */
static gboolean
deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
{
- gint i;
-
g_return_val_if_fail (group->active, FALSE);
g_return_val_if_fail (group->valid, FALSE);
GST_SOURCE_GROUP_LOCK (group);
group->active = FALSE;
- for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
- GstSourceCombine *combine = &playbin->combiner[i];
-
- GST_DEBUG_OBJECT (playbin, "unlinking combiner %s", combine->media_type);
+ group->playing = FALSE;
+ group->group_id = GST_GROUP_ID_INVALID;
- if (combine->srcpad) {
- source_combine_remove_pads (playbin, combine);
- }
-
- if (combine->combiner) {
- gint n;
-
- /* release and unref requests pad from the combiner */
- for (n = 0; n < combine->channels->len; n++) {
- GstPad *sinkpad = g_ptr_array_index (combine->channels, n);
-
- gst_element_release_request_pad (combine->combiner, sinkpad);
- gst_object_unref (sinkpad);
- }
- g_ptr_array_set_size (combine->channels, 0);
+ group->selected_stream_types = 0;
+ /* Update global selected_stream_types */
+ playbin->selected_stream_types =
+ playbin->groups[0].selected_stream_types | playbin->
+ groups[1].selected_stream_types;
+ if (playbin->active_stream_types != playbin->selected_stream_types)
+ reconfigure_output (playbin);
- gst_element_set_state (combine->combiner, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
- combine->combiner = NULL;
- }
- }
#if 0
/* delete any custom sinks we might have.
* conditionally set them to null if they aren't inside playsink yet */
group->text_sink = NULL;
#endif
- if (group->urisourcebin) {
- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id);
- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id);
- REMOVE_SIGNAL (group->urisourcebin, group->notify_source_id);
+ if (group->uridecodebin) {
+ REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id);
+ REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
+ REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id);
+
+ gst_element_set_state (group->uridecodebin, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
+
+ REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
+ REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
+#if 0
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
- gst_bin_remove (GST_BIN_CAST (playbin), group->urisourcebin);
- }
-
- if (group->suburisourcebin) {
- REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_added_id);
- REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_removed_id);
- REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_continue_id);
- REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_query_id);
-
- /* Might already be removed because of errors */
- if (GST_OBJECT_PARENT (group->suburisourcebin) == GST_OBJECT_CAST (playbin))
- gst_bin_remove (GST_BIN_CAST (playbin), group->suburisourcebin);
+#endif
}
GST_SOURCE_GROUP_UNLOCK (group);
+ GST_DEBUG_OBJECT (playbin, "Done");
+
return TRUE;
}
* configured. It swaps out the current_group and activates the valid
* next_group. */
static GstStateChangeReturn
-setup_next_source (GstPlayBin3 * playbin, GstState target)
+setup_next_source (GstPlayBin3 * playbin)
{
- GstSourceGroup *new_group, *old_group;
+ GstSourceGroup *new_group;
GstStateChangeReturn state_ret;
- GST_DEBUG_OBJECT (playbin, "setup sources");
+ GST_DEBUG_OBJECT (playbin, "setup next source");
+
+ debug_groups (playbin);
/* see if there is a next group */
GST_PLAY_BIN3_LOCK (playbin);
new_group = playbin->next_group;
- if (!new_group || !new_group->valid)
+ if (!new_group || !new_group->valid || new_group->active)
goto no_next_group;
- /* first unlink the current source, if any */
- old_group = playbin->curr_group;
- if (old_group && old_group->valid && old_group->active) {
- new_group->stream_changed_pending = TRUE;
-
- gst_play_bin3_update_cached_duration (playbin);
- /* unlink our pads with the sink */
- deactivate_group (playbin, old_group);
- old_group->valid = FALSE;
- }
-
- /* swap old and new */
- playbin->curr_group = new_group;
- playbin->next_group = old_group;
-
- /* Get decodebin ready now */
- if ((state_ret =
- activate_decodebin (playbin, target)) == GST_STATE_CHANGE_FAILURE)
- goto activate_failed;
-
/* activate the new group */
- if ((state_ret =
- activate_group (playbin, new_group,
- target)) == GST_STATE_CHANGE_FAILURE)
+ state_ret = activate_group (playbin, new_group);
+ if (state_ret == GST_STATE_CHANGE_FAILURE)
goto activate_failed;
GST_PLAY_BIN3_UNLOCK (playbin);
+ debug_groups (playbin);
+
return state_ret;
/* ERRORS */
no_next_group:
{
GST_DEBUG_OBJECT (playbin, "no next group");
- if (target == GST_STATE_READY && new_group && new_group->uri == NULL)
- GST_ELEMENT_ERROR (playbin, RESOURCE, NOT_FOUND, ("No URI set"), (NULL));
GST_PLAY_BIN3_UNLOCK (playbin);
return GST_STATE_CHANGE_FAILURE;
}
return TRUE;
}
+static void
+gst_play_bin3_check_group_status (GstPlayBin3 * playbin)
+{
+ if (playbin->activation_task)
+ gst_task_start (playbin->activation_task);
+}
+
+static void
+gst_play_bin3_activation_thread (GstPlayBin3 * playbin)
+{
+ GST_DEBUG_OBJECT (playbin, "starting");
+
+ debug_groups (playbin);
+
+ /* Check if next_group needs to be deactivated */
+ GST_PLAY_BIN3_LOCK (playbin);
+ if (playbin->next_group->active) {
+ deactivate_group (playbin, playbin->next_group);
+ playbin->next_group->valid = FALSE;
+ }
+
+ /* Is there a pending about-to-finish to be emitted ? */
+ GST_SOURCE_GROUP_LOCK (playbin->curr_group);
+ if (playbin->curr_group->pending_about_to_finish) {
+ GST_LOG_OBJECT (playbin, "Propagating about-to-finish");
+ playbin->curr_group->pending_about_to_finish = FALSE;
+ GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
+ /* This will activate the next source afterwards */
+ emit_about_to_finish (playbin);
+ } else
+ GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
+
+ GST_LOG_OBJECT (playbin, "Pausing task");
+ if (playbin->activation_task)
+ gst_task_pause (playbin->activation_task);
+ GST_PLAY_BIN3_UNLOCK (playbin);
+
+ GST_DEBUG_OBJECT (playbin, "done");
+ return;
+}
+
+static gboolean
+gst_play_bin3_start (GstPlayBin3 * playbin)
+{
+ GST_DEBUG_OBJECT (playbin, "starting");
+
+ GST_PLAY_BIN3_LOCK (playbin);
+
+ if (playbin->activation_task == NULL) {
+ playbin->activation_task =
+ gst_task_new ((GstTaskFunction) gst_play_bin3_activation_thread,
+ playbin, NULL);
+ if (playbin->activation_task == NULL)
+ goto task_error;
+ gst_task_set_lock (playbin->activation_task, &playbin->activation_lock);
+ }
+ GST_LOG_OBJECT (playbin, "clearing shutdown flag");
+ g_atomic_int_set (&playbin->shutdown, 0);
+ do_async_start (playbin);
+
+ GST_PLAY_BIN3_UNLOCK (playbin);
+
+ return TRUE;
+
+task_error:
+ {
+ GST_PLAY_BIN3_UNLOCK (playbin);
+ GST_ERROR_OBJECT (playbin, "Failed to create task");
+ return FALSE;
+ }
+}
+
+static void
+gst_play_bin3_stop (GstPlayBin3 * playbin)
+{
+ GstTask *task;
+
+ GST_DEBUG_OBJECT (playbin, "stopping");
+
+ /* FIXME unlock our waiting groups */
+ GST_LOG_OBJECT (playbin, "setting shutdown flag");
+ g_atomic_int_set (&playbin->shutdown, 1);
+
+ /* wait for all callbacks to end by taking the lock.
+ * No dynamic (critical) new callbacks will
+ * be able to happen as we set the shutdown flag. */
+ GST_PLAY_BIN3_DYN_LOCK (playbin);
+ GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
+ GST_PLAY_BIN3_DYN_UNLOCK (playbin);
+
+ /* Stop the activation task */
+ GST_PLAY_BIN3_LOCK (playbin);
+ if ((task = playbin->activation_task)) {
+ playbin->activation_task = NULL;
+ GST_PLAY_BIN3_UNLOCK (playbin);
+
+ gst_task_stop (task);
+
+ /* Make sure task is not running */
+ g_rec_mutex_lock (&playbin->activation_lock);
+ g_rec_mutex_unlock (&playbin->activation_lock);
+
+ /* Wait for task to finish and unref it */
+ gst_task_join (task);
+ gst_object_unref (task);
+
+ GST_PLAY_BIN3_LOCK (playbin);
+ }
+ GST_PLAY_BIN3_UNLOCK (playbin);
+}
+
static GstStateChangeReturn
gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
{
playbin = GST_PLAY_BIN3 (element);
switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- memset (&playbin->duration, 0, sizeof (playbin->duration));
- break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- GST_LOG_OBJECT (playbin, "clearing shutdown flag");
- memset (&playbin->duration, 0, sizeof (playbin->duration));
- g_atomic_int_set (&playbin->shutdown, 0);
- do_async_start (playbin);
+ if (!gst_play_bin3_start (playbin))
+ return GST_STATE_CHANGE_FAILURE;
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
async_down:
- /* FIXME unlock our waiting groups */
- GST_LOG_OBJECT (playbin, "setting shutdown flag");
- g_atomic_int_set (&playbin->shutdown, 1);
- memset (&playbin->duration, 0, sizeof (playbin->duration));
-
- /* wait for all callbacks to end by taking the lock.
- * No dynamic (critical) new callbacks will
- * be able to happen as we set the shutdown flag. */
- GST_PLAY_BIN3_DYN_LOCK (playbin);
- GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
- GST_PLAY_BIN3_DYN_UNLOCK (playbin);
+ gst_play_bin3_stop (playbin);
if (!do_save)
break;
case GST_STATE_CHANGE_READY_TO_NULL:
/* we go async to PAUSED, so if that fails, we never make it to PAUSED
- * and no state change PAUSED to READY passes here,
- * though it is a nice-to-have ... */
+ * and we will never be called with the GST_STATE_CHANGE_PAUSED_TO_READY.
+ * Make sure we do go through the same steps (see above) for
+ * proper cleanup */
if (!g_atomic_int_get (&playbin->shutdown)) {
do_save = TRUE;
goto async_down;
}
- memset (&playbin->duration, 0, sizeof (playbin->duration));
/* unlock so that all groups go to NULL */
groups_set_locked_state (playbin, FALSE);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
- if ((ret =
- setup_next_source (playbin,
- GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)
+ if ((ret = setup_next_source (playbin)) == GST_STATE_CHANGE_FAILURE)
goto failure;
if (ret == GST_STATE_CHANGE_SUCCESS)
ret = GST_STATE_CHANGE_ASYNC;
case GST_STATE_CHANGE_READY_TO_NULL:
{
guint i;
- GList *l;
/* also do missed state change down to READY */
if (do_save)
save_current_group (playbin);
- /* Deactive the groups, set the urisrcbins to NULL
- * and unref them.
- */
+ /* Deactive the groups, set uridecodebin to NULL and unref it */
for (i = 0; i < 2; i++) {
if (playbin->groups[i].active && playbin->groups[i].valid) {
deactivate_group (playbin, &playbin->groups[i]);
playbin->groups[i].valid = FALSE;
}
- if (playbin->groups[i].urisourcebin) {
- gst_element_set_state (playbin->groups[i].urisourcebin,
+ if (playbin->groups[i].uridecodebin) {
+ gst_element_set_state (playbin->groups[i].uridecodebin,
GST_STATE_NULL);
- gst_object_unref (playbin->groups[i].urisourcebin);
- playbin->groups[i].urisourcebin = NULL;
+ gst_object_unref (playbin->groups[i].uridecodebin);
+ playbin->groups[i].uridecodebin = NULL;
}
- if (playbin->groups[i].suburisourcebin) {
- gst_element_set_state (playbin->groups[i].suburisourcebin,
- GST_STATE_NULL);
- gst_object_unref (playbin->groups[i].suburisourcebin);
- playbin->groups[i].suburisourcebin = NULL;
- }
- }
-
- deactivate_decodebin (playbin);
- if (playbin->decodebin) {
- gst_object_unref (playbin->decodebin);
- playbin->decodebin = NULL;
- playbin->decodebin_active = FALSE;
}
/* Set our sinks back to NULL, they might not be child of playbin */
/* make sure the groups don't perform a state change anymore until we
* enable them again */
groups_set_locked_state (playbin, TRUE);
-
- /* Remove all non-persistent contexts */
- GST_OBJECT_LOCK (playbin);
- for (l = playbin->contexts; l;) {
- GstContext *context = l->data;
-
- if (!gst_context_is_persistent (context)) {
- GList *next;
-
- gst_context_unref (context);
-
- next = l->next;
- playbin->contexts = g_list_delete_link (playbin->contexts, l);
- l = next;
- } else {
- l = l->next;
- }
- }
-
- if (playbin->source) {
- gst_object_unref (playbin->source);
- playbin->source = NULL;
- }
-
- GST_OBJECT_UNLOCK (playbin);
break;
}
default:
--- /dev/null
+/* GStreamer
+ * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* TODO/FIXME:
+ *
+ * * BUFFERING MESSAGES
+ * ** How/Where do we deal with buffering messages from a new/prerolling
+ * source ? Ideally we want to re-use the same sourcebin ?
+ * ** Remember last buffering messages per source handler, if the SourceEntry
+ * group_id is the one being currently outputted on the source ghostpads,
+ * post the (last) buffering messages.
+ * If no group_id is being outputted (still prerolling), then output
+ * the messages directly
+ *
+ * * ASYNC HANDLING
+ * ** URIDECODEBIN3 is not async-aware.
+ *
+ * * GAPLESS HANDLING
+ * ** Correlate group_id and URI to know when/which stream is being outputted/started
+ */
+
+/**
+ * SECTION:element-uridecodebin3
+ * @title: uridecodebin3
+ *
+ * Decodes data from a URI into raw media. It selects a source element that can
+ * handle the given #GstURIDecodeBin3:uri scheme and connects it to a decodebin.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+#include <gst/gst-i18n-plugin.h>
+#include <gst/pbutils/missing-plugins.h>
+
+#include "gstplay-enum.h"
+#include "gstrawcaps.h"
+#include "gstplayback.h"
+#include "gstplaybackutils.h"
+
+#define GST_TYPE_URI_DECODE_BIN3 \
+ (gst_uri_decode_bin3_get_type())
+#define GST_URI_DECODE_BIN3(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3))
+#define GST_URI_DECODE_BIN3_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3Class))
+#define GST_IS_URI_DECODE_BIN3(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN3))
+#define GST_IS_URI_DECODE_BIN3_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN3))
+#define GST_URI_DECODE_BIN3_CAST(obj) ((GstURIDecodeBin3 *) (obj))
+
+typedef struct _GstSourceGroup GstSourceGroup;
+typedef struct _GstURIDecodeBin3 GstURIDecodeBin3;
+typedef struct _GstURIDecodeBin3Class GstURIDecodeBin3Class;
+
+#define GST_URI_DECODE_BIN3_LOCK(dec) (g_mutex_lock(&((GstURIDecodeBin3*)(dec))->lock))
+#define GST_URI_DECODE_BIN3_UNLOCK(dec) (g_mutex_unlock(&((GstURIDecodeBin3*)(dec))->lock))
+
+typedef struct _GstPlayItem GstPlayItem;
+typedef struct _GstSourceItem GstSourceItem;
+typedef struct _GstSourceHandler GstSourceHandler;
+typedef struct _OutputPad OutputPad;
+
+/* A structure describing a play item, which travels through the elements
+ * over time. */
+struct _GstPlayItem
+{
+ GstURIDecodeBin3 *uridecodebin;
+
+ /* Main URI */
+ GstSourceItem *main_item;
+
+ /* Auxiliary URI */
+ /* FIXME : Replace by a list later */
+ GstSourceItem *sub_item;
+
+ /* The group_id used to identify this play item via STREAM_START events
+ * This is the group_id which will be used externally (i.e. rewritten
+ * to outgoing STREAM_START events and in emitted signals).
+ * The urisourcebin-specific group_id is located in GstSourceItem */
+ guint group_id;
+
+ /* Is this play item the one being currently outputted by decodebin3
+ * and on our source ghostpads */
+ gboolean currently_outputted;
+};
+
+struct _GstSourceItem
+{
+ /* The GstPlayItem to which this GstSourceItem belongs to */
+ GstPlayItem *play_item;
+
+ gchar *uri;
+
+ /* The urisourcebin controlling this uri
+ * Can be NULL */
+ GstSourceHandler *handler;
+
+ /* Last buffering information */
+ gint last_perc;
+ GstMessage *last_buffering_message;
+
+ /* The groupid created by urisourcebin for this uri */
+ guint internal_groupid;
+
+ /* FIXME : Add tag lists and other uri-specific items here ? */
+};
+
+/* Structure wrapping everything related to a urisourcebin */
+struct _GstSourceHandler
+{
+ GstURIDecodeBin3 *uridecodebin;
+
+ GstElement *urisourcebin;
+
+ /* Signal handlers */
+ gulong pad_added_id;
+ gulong pad_removed_id;
+ gulong source_setup_id;
+ gulong about_to_finish_id;
+
+ /* TRUE if the controlled urisourcebin was added to uridecodebin */
+ gboolean active;
+
+ /* whether urisourcebin is drained or not.
+ * Reset if/when setting a new URI */
+ gboolean drained;
+
+ /* Whether urisourcebin posted EOS on all pads and
+ * there is no pending entry */
+ gboolean is_eos;
+
+ /* TRUE if the urisourcebin handles main item */
+ gboolean is_main_source;
+
+ /* buffering message stored for after switching */
+ GstMessage *pending_buffering_msg;
+};
+
+/* Controls an output source pad */
+struct _OutputPad
+{
+ GstURIDecodeBin3 *uridecodebin;
+
+ GstPad *target_pad;
+ GstPad *ghost_pad;
+
+ /* Downstream event probe id */
+ gulong probe_id;
+
+ /* TRUE if the pad saw EOS. Resetted to FALSE on STREAM_START */
+ gboolean is_eos;
+
+ /* The last seen (i.e. current) group_id
+ * Can be (guint)-1 if no group_id was seen yet */
+ guint current_group_id;
+};
+
+/**
+ * GstURIDecodeBin3
+ *
+ * uridecodebin3 element struct
+ */
+struct _GstURIDecodeBin3
+{
+ GstBin parent_instance;
+
+ GMutex lock; /* lock for constructing */
+
+ /* Properties */
+ GstElement *source;
+ guint64 connection_speed; /* In bits/sec (0 = unknown) */
+ GstCaps *caps;
+ guint64 buffer_duration; /* When buffering, buffer duration (ns) */
+ guint buffer_size; /* When buffering, buffer size (bytes) */
+ gboolean download;
+ gboolean use_buffering;
+ guint64 ring_buffer_max_size;
+
+ GList *play_items; /* List of GstPlayItem ordered by time of
+ * creation. Head of list is therefore the
+ * current (or pending if initial) one being
+ * outputted */
+ GstPlayItem *current; /* Currently active GstPlayItem. Can be NULL
+ * if no entry is active yet (i.e. no source
+ * pads) */
+
+ /* sources.
+ * FIXME : Replace by a more modular system later on */
+ GstSourceHandler *main_handler;
+ GstSourceHandler *sub_handler;
+
+ /* URI handling
+ * FIXME : Switch to a playlist-based API */
+ gchar *uri;
+ gboolean uri_changed; /* TRUE if uri changed */
+ gchar *suburi;
+ gboolean suburi_changed; /* TRUE if suburi changed */
+
+ /* A global decodebin3 that's used to actually do decoding */
+ GstElement *decodebin;
+
+ /* db3 signals */
+ gulong db_pad_added_id;
+ gulong db_pad_removed_id;
+ gulong db_select_stream_id;
+ gulong db_about_to_finish_id;
+
+ GList *output_pads; /* List of OutputPad */
+
+ GList *source_handlers; /* List of SourceHandler */
+
+ /* Whether we already signalled about-to-finish or not
+ * FIXME: Track this by group-id ! */
+ gboolean posted_about_to_finish;
+};
+
+struct _GstURIDecodeBin3Class
+{
+ GstBinClass parent_class;
+
+ gint (*select_stream) (GstURIDecodeBin3 * dbin,
+ GstStreamCollection * collection, GstStream * stream);
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin3_debug);
+#define GST_CAT_DEFAULT gst_uri_decode_bin3_debug
+
+/* signals */
+enum
+{
+ SIGNAL_SELECT_STREAM,
+ SIGNAL_SOURCE_SETUP,
+ SIGNAL_ABOUT_TO_FINISH,
+ LAST_SIGNAL
+};
+
+#if 0
+static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
+static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
+#endif
+
+/* properties */
+#define DEFAULT_PROP_URI NULL
+#define DEFAULT_PROP_SUBURI NULL
+#define DEFAULT_PROP_SOURCE NULL
+#define DEFAULT_CONNECTION_SPEED 0
+#define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps))
+#define DEFAULT_BUFFER_DURATION -1
+#define DEFAULT_BUFFER_SIZE -1
+#define DEFAULT_DOWNLOAD FALSE
+#define DEFAULT_USE_BUFFERING FALSE
+#define DEFAULT_RING_BUFFER_MAX_SIZE 0
+
+enum
+{
+ PROP_0,
+ PROP_URI,
+ PROP_CURRENT_URI,
+ PROP_SUBURI,
+ PROP_CURRENT_SUBURI,
+ PROP_SOURCE,
+ PROP_CONNECTION_SPEED,
+ PROP_BUFFER_SIZE,
+ PROP_BUFFER_DURATION,
+ PROP_DOWNLOAD,
+ PROP_USE_BUFFERING,
+ PROP_RING_BUFFER_MAX_SIZE,
+ PROP_CAPS
+};
+
+static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 };
+
+static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS);
+
+static GstStaticPadTemplate video_src_template =
+GST_STATIC_PAD_TEMPLATE ("video_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate audio_src_template =
+GST_STATIC_PAD_TEMPLATE ("audio_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate text_src_template =
+GST_STATIC_PAD_TEMPLATE ("text_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+GType gst_uri_decode_bin3_get_type (void);
+#define gst_uri_decode_bin3_parent_class parent_class
+G_DEFINE_TYPE (GstURIDecodeBin3, gst_uri_decode_bin3, GST_TYPE_BIN);
+
+#define REMOVE_SIGNAL(obj,id) \
+if (id) { \
+ g_signal_handler_disconnect (obj, id); \
+ id = 0; \
+}
+
+static void gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_uri_decode_bin3_finalize (GObject * obj);
+
+static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement *
+ element, GstStateChange transition);
+
+static gboolean
+_gst_int_accumulator (GSignalInvocationHint * ihint,
+ GValue * return_accu, const GValue * handler_return, gpointer dummy)
+{
+ gint res = g_value_get_int (handler_return);
+
+ if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
+ g_value_set_int (return_accu, res);
+
+ if (res == -1)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static void
+gst_uri_decode_bin3_class_init (GstURIDecodeBin3Class * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ gobject_class->set_property = gst_uri_decode_bin3_set_property;
+ gobject_class->get_property = gst_uri_decode_bin3_get_property;
+ gobject_class->finalize = gst_uri_decode_bin3_finalize;
+
+ g_object_class_install_property (gobject_class, PROP_URI,
+ g_param_spec_string ("uri", "URI", "URI to decode",
+ DEFAULT_PROP_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CURRENT_URI,
+ g_param_spec_string ("current-uri", "Current URI",
+ "The currently playing URI", NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SUBURI,
+ g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CURRENT_SUBURI,
+ g_param_spec_string ("current-suburi", "Current .sub-URI",
+ "The currently playing URI of a subtitle",
+ NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SOURCE,
+ g_param_spec_object ("source", "Source", "Source object used",
+ GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
+ g_param_spec_uint64 ("connection-speed", "Connection Speed",
+ "Network connection speed in kbps (0 = unknown)",
+ 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE,
+ g_param_spec_int ("buffer-size", "Buffer size (bytes)",
+ "Buffer size when buffering streams (-1 default value)",
+ -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_BUFFER_DURATION,
+ g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
+ "Buffer duration when buffering streams (-1 default value)",
+ -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstURIDecodeBin3::download:
+ *
+ * For certain media type, enable download buffering.
+ */
+ g_object_class_install_property (gobject_class, PROP_DOWNLOAD,
+ g_param_spec_boolean ("download", "Download",
+ "Attempt download buffering when buffering network streams",
+ DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstURIDecodeBin3::use-buffering:
+ *
+ * Emit BUFFERING messages based on low-/high-percent thresholds of the
+ * demuxed or parsed data.
+ * When download buffering is activated and used for the current media
+ * type, this property does nothing. Otherwise perform buffering on the
+ * demuxed or parsed media.
+ */
+ g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
+ g_param_spec_boolean ("use-buffering", "Use Buffering",
+ "Perform buffering on demuxed/parsed media",
+ DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstURIDecodeBin3::ring-buffer-max-size
+ *
+ * The maximum size of the ring buffer in kilobytes. If set to 0, the ring
+ * buffer is disabled. Default is 0.
+ */
+ g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
+ g_param_spec_uint64 ("ring-buffer-max-size",
+ "Max. ring buffer size (bytes)",
+ "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
+ 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_CAPS,
+ g_param_spec_boxed ("caps", "Caps",
+ "The caps on which to stop decoding. (NULL = default)",
+ GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GstURIDecodebin3::select-stream
+ * @decodebin: a #GstURIDecodebin3
+ * @collection: a #GstStreamCollection
+ * @stream: a #GstStream
+ *
+ * This signal is emitted whenever @decodebin needs to decide whether
+ * to expose a @stream of a given @collection.
+ *
+ * Returns: 1 if the stream should be selected, 0 if it shouldn't be selected.
+ * A value of -1 (default) lets @decodebin decide what to do with the stream.
+ * */
+ gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM] =
+ g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBin3Class, select_stream),
+ _gst_int_accumulator, NULL, g_cclosure_marshal_generic,
+ G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM);
+
+ /**
+ * GstURIDecodeBin3::source-setup:
+ * @bin: the uridecodebin.
+ * @source: source element
+ *
+ * This signal is emitted after a source element has been created, so
+ * it can be configured by setting additional properties (e.g. set a
+ * proxy server for an http source, or set the device and read speed for
+ * an audio cd source).
+ */
+ gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP] =
+ g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ /**
+ * GstURIDecodeBin3::about-to-finish:
+ *
+ * This signal is emitted when the data for the selected URI is
+ * entirely buffered and it is safe to specify anothe URI.
+ */
+ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH] =
+ g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+ 0, G_TYPE_NONE);
+
+
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &video_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &audio_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &text_src_template);
+ gst_element_class_add_static_pad_template (gstelement_class, &src_template);
+ gst_element_class_set_static_metadata (gstelement_class,
+ "URI Decoder", "Generic/Bin/Decoder",
+ "Autoplug and decode an URI to raw media",
+ "Edward Hervey <edward@centricular.com>, Jan Schmidt <jan@centricular.com>");
+
+ gstelement_class->change_state = gst_uri_decode_bin3_change_state;
+
+}
+
+static GstPadProbeReturn
+db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output)
+{
+ /* FIXME : IMPLEMENT */
+
+ /* EOS : Mark pad as EOS */
+
+ /* STREAM_START : Store group_id and check if currently active
+ * PlayEntry changed */
+
+ return GST_PAD_PROBE_OK;
+}
+
+static OutputPad *
+add_output_pad (GstURIDecodeBin3 * dec, GstPad * target_pad)
+{
+ OutputPad *output;
+ gchar *pad_name;
+
+ output = g_slice_new0 (OutputPad);
+
+ GST_LOG_OBJECT (dec, "Created output %p", output);
+
+ output->uridecodebin = dec;
+ output->target_pad = target_pad;
+ output->current_group_id = (guint) - 1;
+ pad_name = gst_pad_get_name (target_pad);
+ output->ghost_pad = gst_ghost_pad_new (pad_name, target_pad);
+ g_free (pad_name);
+
+ gst_pad_set_active (output->ghost_pad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (dec), output->ghost_pad);
+
+ output->probe_id =
+ gst_pad_add_probe (output->target_pad,
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) db_src_probe,
+ output, NULL);
+
+ /* FIXME: LOCK TO PROTECT PAD LIST */
+ dec->output_pads = g_list_append (dec->output_pads, output);
+
+ return output;
+}
+
+static void
+db_pad_added_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
+{
+ GST_DEBUG_OBJECT (dec, "Wrapping new pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ if (GST_PAD_IS_SRC (pad))
+ add_output_pad (dec, pad);
+}
+
+static void
+db_pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec)
+{
+ GList *tmp;
+ OutputPad *output = NULL;
+
+ if (!GST_PAD_IS_SRC (pad))
+ return;
+
+ GST_DEBUG_OBJECT (dec, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+ /* FIXME: LOCK for list access */
+
+ for (tmp = dec->output_pads; tmp; tmp = tmp->next) {
+ OutputPad *cand = (OutputPad *) tmp->data;
+
+ if (cand->target_pad == pad) {
+ output = cand;
+ dec->output_pads = g_list_delete_link (dec->output_pads, tmp);
+ break;
+ }
+ }
+
+ if (output) {
+ GST_LOG_OBJECT (element, "Removing output %p", output);
+ /* Remove source ghost pad */
+ gst_ghost_pad_set_target ((GstGhostPad *) output->ghost_pad, NULL);
+ gst_element_remove_pad ((GstElement *) dec, output->ghost_pad);
+
+ /* FIXME : Update global/current PlayEntry group_id (did we switch ?) */
+
+ /* Remove event probe */
+ gst_pad_remove_probe (output->target_pad, output->probe_id);
+
+ g_slice_free (OutputPad, output);
+ }
+}
+
+static gint
+db_select_stream_cb (GstElement * decodebin,
+ GstStreamCollection * collection, GstStream * stream,
+ GstURIDecodeBin3 * uridecodebin)
+{
+ gint response = -1;
+
+ g_signal_emit (uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM], 0, collection, stream,
+ &response);
+ return response;
+}
+
+static void
+db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin)
+{
+ if (!uridecodebin->posted_about_to_finish) {
+ uridecodebin->posted_about_to_finish = TRUE;
+ g_signal_emit (uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+ }
+}
+
+static void
+gst_uri_decode_bin3_init (GstURIDecodeBin3 * dec)
+{
+ g_mutex_init (&dec->lock);
+
+ dec->caps = gst_static_caps_get (&default_raw_caps);
+
+ dec->decodebin = gst_element_factory_make ("decodebin3", NULL);
+ gst_bin_add (GST_BIN_CAST (dec), dec->decodebin);
+ dec->db_pad_added_id =
+ g_signal_connect (dec->decodebin, "pad-added",
+ G_CALLBACK (db_pad_added_cb), dec);
+ dec->db_pad_removed_id =
+ g_signal_connect (dec->decodebin, "pad-removed",
+ G_CALLBACK (db_pad_removed_cb), dec);
+ dec->db_select_stream_id =
+ g_signal_connect (dec->decodebin, "select-stream",
+ G_CALLBACK (db_select_stream_cb), dec);
+ dec->db_about_to_finish_id =
+ g_signal_connect (dec->decodebin, "about-to-finish",
+ G_CALLBACK (db_about_to_finish_cb), dec);
+
+ GST_OBJECT_FLAG_SET (dec, GST_ELEMENT_FLAG_SOURCE);
+ gst_bin_set_suppressed_flags (GST_BIN (dec),
+ GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
+}
+
+static void
+gst_uri_decode_bin3_finalize (GObject * obj)
+{
+ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (obj);
+
+ g_mutex_clear (&dec->lock);
+ g_free (dec->uri);
+ g_free (dec->suburi);
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static GstStateChangeReturn
+activate_source_item (GstSourceItem * item)
+{
+ GstSourceHandler *handler = item->handler;
+
+ if (handler == NULL) {
+ GST_WARNING ("Can't activate item without a handler");
+ return GST_STATE_CHANGE_FAILURE;
+ }
+
+ g_object_set (handler->urisourcebin, "uri", item->uri, NULL);
+ if (!handler->active) {
+ gst_bin_add ((GstBin *) handler->uridecodebin, handler->urisourcebin);
+ /* if (!gst_element_sync_state_with_parent (handler->urisourcebin)) */
+ /* return GST_STATE_CHANGE_FAILURE; */
+ handler->active = TRUE;
+ }
+
+ return GST_STATE_CHANGE_SUCCESS;
+}
+
+static void
+src_pad_added_cb (GstElement * element, GstPad * pad,
+ GstSourceHandler * handler)
+{
+ GstURIDecodeBin3 *uridecodebin;
+ GstPad *sinkpad = NULL;
+ GstPadLinkReturn res;
+
+ uridecodebin = handler->uridecodebin;
+
+ GST_DEBUG_OBJECT (uridecodebin,
+ "New pad %" GST_PTR_FORMAT " from source %" GST_PTR_FORMAT, pad, element);
+
+ /* FIXME: Add probe to unify group_id and detect EOS */
+
+ /* Try to link to main sink pad only if it's from a main handler */
+ if (handler->is_main_source) {
+ sinkpad = gst_element_get_static_pad (uridecodebin->decodebin, "sink");
+ if (gst_pad_is_linked (sinkpad)) {
+ gst_object_unref (sinkpad);
+ sinkpad = NULL;
+ }
+ }
+
+ if (sinkpad == NULL)
+ sinkpad = gst_element_get_request_pad (uridecodebin->decodebin, "sink_%u");
+
+ if (sinkpad) {
+ GST_DEBUG_OBJECT (uridecodebin,
+ "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, pad, sinkpad);
+ res = gst_pad_link (pad, sinkpad);
+ gst_object_unref (sinkpad);
+ if (GST_PAD_LINK_FAILED (res))
+ goto link_failed;
+ }
+ return;
+
+link_failed:
+ {
+ GST_ERROR_OBJECT (uridecodebin,
+ "failed to link pad %s:%s to decodebin, reason %s (%d)",
+ GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
+ return;
+ }
+}
+
+static void
+src_pad_removed_cb (GstElement * element, GstPad * pad,
+ GstSourceHandler * handler)
+{
+ /* FIXME : IMPLEMENT */
+}
+
+static void
+src_source_setup_cb (GstElement * element, GstElement * source,
+ GstSourceHandler * handler)
+{
+ g_signal_emit (handler->uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP], 0, source, NULL);
+}
+
+static void
+src_about_to_finish_cb (GstElement * element, GstSourceHandler * handler)
+{
+ /* FIXME : check if all sources are done */
+ if (!handler->uridecodebin->posted_about_to_finish) {
+ handler->uridecodebin->posted_about_to_finish = TRUE;
+ g_signal_emit (handler->uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+ }
+}
+
+static GstSourceHandler *
+new_source_handler (GstURIDecodeBin3 * uridecodebin, gboolean is_main)
+{
+ GstSourceHandler *handler;
+
+ handler = g_slice_new0 (GstSourceHandler);
+
+ handler->uridecodebin = uridecodebin;
+ handler->is_main_source = is_main;
+ handler->urisourcebin = gst_element_factory_make ("urisourcebin", NULL);
+ /* Set pending properties */
+ g_object_set (handler->urisourcebin,
+ "connection-speed", uridecodebin->connection_speed / 1000,
+ "download", uridecodebin->download,
+ "use-buffering", uridecodebin->use_buffering,
+ "buffer-duration", uridecodebin->buffer_duration,
+ "buffer-size", uridecodebin->buffer_size,
+ "ring-buffer-max-size", uridecodebin->ring_buffer_max_size, NULL);
+
+ handler->pad_added_id =
+ g_signal_connect (handler->urisourcebin, "pad-added",
+ (GCallback) src_pad_added_cb, handler);
+ handler->pad_removed_id =
+ g_signal_connect (handler->urisourcebin, "pad-removed",
+ (GCallback) src_pad_removed_cb, handler);
+ handler->source_setup_id =
+ g_signal_connect (handler->urisourcebin, "source-setup",
+ (GCallback) src_source_setup_cb, handler);
+ handler->about_to_finish_id =
+ g_signal_connect (handler->urisourcebin, "about-to-finish",
+ (GCallback) src_about_to_finish_cb, handler);
+
+ uridecodebin->source_handlers =
+ g_list_append (uridecodebin->source_handlers, handler);
+
+ return handler;
+}
+
+static void
+gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ if (dec->uri)
+ g_free (dec->uri);
+ dec->uri = g_value_dup_string (value);
+ break;
+ case PROP_SUBURI:
+ if (dec->suburi)
+ g_free (dec->suburi);
+ dec->suburi = g_value_dup_string (value);
+ break;
+ case PROP_CONNECTION_SPEED:
+ GST_URI_DECODE_BIN3_LOCK (dec);
+ dec->connection_speed = g_value_get_uint64 (value) * 1000;
+ GST_URI_DECODE_BIN3_UNLOCK (dec);
+ break;
+ case PROP_BUFFER_SIZE:
+ dec->buffer_size = g_value_get_int (value);
+ break;
+ case PROP_BUFFER_DURATION:
+ dec->buffer_duration = g_value_get_int64 (value);
+ break;
+ case PROP_DOWNLOAD:
+ dec->download = g_value_get_boolean (value);
+ break;
+ case PROP_USE_BUFFERING:
+ dec->use_buffering = g_value_get_boolean (value);
+ break;
+ case PROP_RING_BUFFER_MAX_SIZE:
+ dec->ring_buffer_max_size = g_value_get_uint64 (value);
+ break;
+ case PROP_CAPS:
+ GST_OBJECT_LOCK (dec);
+ if (dec->caps)
+ gst_caps_unref (dec->caps);
+ dec->caps = g_value_dup_boxed (value);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_uri_decode_bin3_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ {
+ g_value_set_string (value, dec->uri);
+ break;
+ }
+ case PROP_CURRENT_URI:
+ {
+ g_value_set_string (value, dec->suburi);
+ break;
+ }
+ case PROP_SUBURI:
+ {
+ /* FIXME : Return current uri */
+ g_value_set_string (value, dec->uri);
+ break;
+ }
+ case PROP_CURRENT_SUBURI:
+ {
+ /* FIXME : Return current suburi */
+ g_value_set_string (value, dec->suburi);
+ break;
+ }
+ case PROP_SOURCE:
+ {
+ GST_OBJECT_LOCK (dec);
+ g_value_set_object (value, dec->source);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ }
+ case PROP_CONNECTION_SPEED:
+ GST_URI_DECODE_BIN3_LOCK (dec);
+ g_value_set_uint64 (value, dec->connection_speed / 1000);
+ GST_URI_DECODE_BIN3_UNLOCK (dec);
+ break;
+ case PROP_BUFFER_SIZE:
+ GST_OBJECT_LOCK (dec);
+ g_value_set_int (value, dec->buffer_size);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ case PROP_BUFFER_DURATION:
+ GST_OBJECT_LOCK (dec);
+ g_value_set_int64 (value, dec->buffer_duration);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ case PROP_DOWNLOAD:
+ g_value_set_boolean (value, dec->download);
+ break;
+ case PROP_USE_BUFFERING:
+ g_value_set_boolean (value, dec->use_buffering);
+ break;
+ case PROP_RING_BUFFER_MAX_SIZE:
+ g_value_set_uint64 (value, dec->ring_buffer_max_size);
+ break;
+ case PROP_CAPS:
+ GST_OBJECT_LOCK (dec);
+ g_value_set_boxed (value, dec->caps);
+ GST_OBJECT_UNLOCK (dec);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+free_source_handler (GstURIDecodeBin3 * uridecodebin,
+ GstSourceHandler * handler)
+{
+ GST_LOG_OBJECT (uridecodebin, "source handler %p", handler);
+ if (handler->active) {
+ GST_LOG_OBJECT (uridecodebin, "Removing %" GST_PTR_FORMAT,
+ handler->urisourcebin);
+ gst_element_set_state (handler->urisourcebin, GST_STATE_NULL);
+ gst_bin_remove ((GstBin *) uridecodebin, handler->urisourcebin);
+ }
+ uridecodebin->source_handlers =
+ g_list_remove (uridecodebin->source_handlers, handler);
+ g_slice_free (GstSourceHandler, handler);
+}
+
+static GstSourceItem *
+new_source_item (GstURIDecodeBin3 * dec, GstPlayItem * item, gchar * uri)
+{
+ GstSourceItem *sourceitem = g_slice_new0 (GstSourceItem);
+
+ sourceitem->play_item = item;
+ sourceitem->uri = uri;
+
+ return sourceitem;
+}
+
+static void
+free_source_item (GstURIDecodeBin3 * uridecodebin, GstSourceItem * item)
+{
+ GST_LOG_OBJECT (uridecodebin, "source item %p", item);
+ if (item->handler)
+ free_source_handler (uridecodebin, item->handler);
+ g_slice_free (GstSourceItem, item);
+}
+
+static GstPlayItem *
+new_play_item (GstURIDecodeBin3 * dec, gchar * uri, gchar * suburi)
+{
+ GstPlayItem *item = g_slice_new0 (GstPlayItem);
+
+ item->uridecodebin = dec;
+ item->main_item = new_source_item (dec, item, uri);
+ if (suburi)
+ item->sub_item = new_source_item (dec, item, suburi);
+
+ return item;
+}
+
+static void
+free_play_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
+{
+ GST_LOG_OBJECT (dec, "play item %p", item);
+ if (item->main_item)
+ free_source_item (dec, item->main_item);
+ if (item->sub_item)
+ free_source_item (dec, item->sub_item);
+
+ g_slice_free (GstPlayItem, item);
+}
+
+/* Sync source handlers for the given play item. Might require creating/removing some
+ * and/or configure the handlers accordingly */
+static GstStateChangeReturn
+assign_handlers_to_item (GstURIDecodeBin3 * dec, GstPlayItem * item)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ /* FIXME : Go over existing handlers to see if we can assign some to the
+ * given item */
+
+ /* Create missing handlers */
+ if (item->main_item->handler == NULL) {
+ item->main_item->handler = new_source_handler (dec, TRUE);
+ ret = activate_source_item (item->main_item);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+ }
+
+ if (item->sub_item && item->sub_item->handler) {
+ item->sub_item->handler = new_source_handler (dec, FALSE);
+ ret = activate_source_item (item->sub_item);
+ }
+
+ return ret;
+}
+
+/* Called to activate the next play item */
+static GstStateChangeReturn
+activate_next_play_item (GstURIDecodeBin3 * dec)
+{
+ GstPlayItem *item;
+ GstStateChangeReturn ret;
+
+ /* If there is no current play entry, create one from the uri/suburi
+ * FIXME : Use a playlist API in the future */
+ item = new_play_item (dec, dec->uri, dec->suburi);
+
+ ret = assign_handlers_to_item (dec, item);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ free_play_item (dec, item);
+ return ret;
+ }
+
+ dec->play_items = g_list_append (dec->play_items, item);
+
+ return ret;
+}
+
+static void
+free_play_items (GstURIDecodeBin3 * dec)
+{
+ GList *tmp;
+
+ for (tmp = dec->play_items; tmp; tmp = tmp->next) {
+ GstPlayItem *item = (GstPlayItem *) tmp->data;
+ free_play_item (dec, item);
+ }
+
+ g_list_free (dec->play_items);
+ dec->play_items = NULL;
+}
+
+static GstStateChangeReturn
+gst_uri_decode_bin3_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ GstURIDecodeBin3 *uridecodebin = (GstURIDecodeBin3 *) element;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ ret = activate_next_play_item (uridecodebin);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto failure;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto failure;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ /* FIXME: Cleanup everything */
+ free_play_items (uridecodebin);
+ /* Free play item */
+ uridecodebin->posted_about_to_finish = FALSE;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+
+ /* ERRORS */
+failure:
+ {
+ if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
+ free_play_items (uridecodebin);
+ return ret;
+ }
+}
+
+
+gboolean
+gst_uri_decode_bin3_plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_uri_decode_bin3_debug, "uridecodebin3", 0,
+ "URI decoder element 3");
+
+ return gst_element_register (plugin, "uridecodebin3", GST_RANK_NONE,
+ GST_TYPE_URI_DECODE_BIN3);
+}
/* for dynamic sources */
guint src_np_sig_id; /* new-pad signal id */
- gboolean async_pending; /* async-start has been emitted */
-
guint64 ring_buffer_max_size; /* 0 means disabled */
GList *pending_pads; /* Pads we have blocked pending assignment
{
GstBinClass parent_class;
- /* signal fired when we found a pad that we cannot decode */
- void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps);
-
- /* signal fired to know if we continue trying to decode the given caps */
- gboolean (*autoplug_continue) (GstElement * element, GstPad * pad,
- GstCaps * caps);
- /* signal fired to get a list of factories to try to autoplug */
- GValueArray *(*autoplug_factories) (GstElement * element, GstPad * pad,
- GstCaps * caps);
- /* signal fired to sort the factories */
- GValueArray *(*autoplug_sort) (GstElement * element, GstPad * pad,
- GstCaps * caps, GValueArray * factories);
- /* signal fired to select from the proposed list of factories */
- GstAutoplugSelectResult (*autoplug_select) (GstElement * element,
- GstPad * pad, GstCaps * caps, GstElementFactory * factory);
- /* signal fired when a autoplugged element that is not linked downstream
- * or exposed wants to query something */
- gboolean (*autoplug_query) (GstElement * element, GstPad * pad,
- GstQuery * query);
-
- /* emitted when all data is decoded */
+ /* emitted when all data has been drained out
+ * FIXME : What do we need this for ?? */
void (*drained) (GstElement * element);
+ /* emitted when all data has been fed into buffering slots (i.e the
+ * actual sources are done) */
+ void (*about_to_finish) (GstElement * element);
};
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u",
/* signals */
enum
{
- SIGNAL_UNKNOWN_TYPE,
- SIGNAL_AUTOPLUG_CONTINUE,
- SIGNAL_AUTOPLUG_FACTORIES,
- SIGNAL_AUTOPLUG_SELECT,
- SIGNAL_AUTOPLUG_SORT,
- SIGNAL_AUTOPLUG_QUERY,
SIGNAL_DRAINED,
+ SIGNAL_ABOUT_TO_FINISH,
SIGNAL_SOURCE_SETUP,
LAST_SIGNAL
};
PROP_RING_BUFFER_MAX_SIZE
};
+#define CUSTOM_EOS_QUARK _custom_eos_quark_get ()
+#define CUSTOM_EOS_QUARK_DATA "custom-eos"
+static GQuark
+_custom_eos_quark_get (void)
+{
+ static gsize g_quark;
+
+ if (g_once_init_enter (&g_quark)) {
+ gsize quark =
+ (gsize) g_quark_from_static_string ("urisourcebin-custom-eos");
+ g_once_init_leave (&g_quark, quark);
+ }
+ return g_quark;
+}
+
static void post_missing_plugin_error (GstElement * dec,
const gchar * element_name);
static GstPad *create_output_pad (GstURISourceBin * urisrc, GstPad * pad);
static void remove_buffering_msgs (GstURISourceBin * bin, GstObject * src);
-static gboolean
-_gst_boolean_accumulator (GSignalInvocationHint * ihint,
- GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
- gboolean myboolean;
-
- myboolean = g_value_get_boolean (handler_return);
- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
- g_value_set_boolean (return_accu, myboolean);
-
- /* stop emission if FALSE */
- return myboolean;
-}
-
-static gboolean
-_gst_boolean_or_accumulator (GSignalInvocationHint * ihint,
- GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
- gboolean myboolean;
- gboolean retboolean;
-
- myboolean = g_value_get_boolean (handler_return);
- retboolean = g_value_get_boolean (return_accu);
-
- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
- g_value_set_boolean (return_accu, myboolean || retboolean);
-
- return TRUE;
-}
-
-static gboolean
-_gst_array_accumulator (GSignalInvocationHint * ihint,
- GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
- gpointer array;
-
- array = g_value_get_boxed (handler_return);
- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
- g_value_set_boxed (return_accu, array);
-
- return FALSE;
-}
-
-static gboolean
-_gst_select_accumulator (GSignalInvocationHint * ihint,
- GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
- GstAutoplugSelectResult res;
-
- res = g_value_get_enum (handler_return);
- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
- g_value_set_enum (return_accu, res);
-
- /* Call the next handler in the chain (if any) when the current callback
- * returns TRY. This makes it possible to register separate autoplug-select
- * handlers that implement different TRY/EXPOSE/SKIP strategies.
- */
- if (res == GST_AUTOPLUG_SELECT_TRY)
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-_gst_array_hasvalue_accumulator (GSignalInvocationHint * ihint,
- GValue * return_accu, const GValue * handler_return, gpointer dummy)
-{
- gpointer array;
-
- array = g_value_get_boxed (handler_return);
- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
- g_value_set_boxed (return_accu, array);
-
- if (array != NULL)
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-gst_uri_source_bin_autoplug_continue (GstElement * element, GstPad * pad,
- GstCaps * caps)
-{
- /* by default we always continue */
- return TRUE;
-}
-
-/* Must be called with factories lock! */
-static void
-gst_uri_source_bin_update_factories_list (GstURISourceBin * dec)
-{
- guint32 cookie;
-
- cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
- if (!dec->factories || dec->factories_cookie != cookie) {
- if (dec->factories)
- gst_plugin_feature_list_free (dec->factories);
- dec->factories =
- gst_element_factory_list_get_elements
- (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
- dec->factories =
- g_list_sort (dec->factories, gst_playback_utils_compare_factories_func);
- dec->factories_cookie = cookie;
- }
-}
-
-static GValueArray *
-gst_uri_source_bin_autoplug_factories (GstElement * element, GstPad * pad,
- GstCaps * caps)
-{
- GList *list, *tmp;
- GValueArray *result;
- GstURISourceBin *dec = GST_URI_SOURCE_BIN_CAST (element);
-
- GST_DEBUG_OBJECT (element, "finding factories");
-
- /* return all compatible factories for caps */
- g_mutex_lock (&dec->factories_lock);
- gst_uri_source_bin_update_factories_list (dec);
- list =
- gst_element_factory_list_filter (dec->factories, caps, GST_PAD_SINK,
- gst_caps_is_fixed (caps));
- g_mutex_unlock (&dec->factories_lock);
-
- result = g_value_array_new (g_list_length (list));
- for (tmp = list; tmp; tmp = tmp->next) {
- GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data);
- GValue val = { 0, };
-
- g_value_init (&val, G_TYPE_OBJECT);
- g_value_set_object (&val, factory);
- g_value_array_append (result, &val);
- g_value_unset (&val);
- }
- gst_plugin_feature_list_free (list);
-
- GST_DEBUG_OBJECT (element, "autoplug-factories returns %p", result);
-
- return result;
-}
-
-static GValueArray *
-gst_uri_source_bin_autoplug_sort (GstElement * element, GstPad * pad,
- GstCaps * caps, GValueArray * factories)
-{
- return NULL;
-}
-
-static GstAutoplugSelectResult
-gst_uri_source_bin_autoplug_select (GstElement * element, GstPad * pad,
- GstCaps * caps, GstElementFactory * factory)
-{
- GST_DEBUG_OBJECT (element, "default autoplug-select returns TRY");
-
- /* Try factory. */
- return GST_AUTOPLUG_SELECT_TRY;
-}
-
-static gboolean
-gst_uri_source_bin_autoplug_query (GstElement * element, GstPad * pad,
- GstQuery * query)
-{
- /* No query handled here */
- return FALSE;
-}
-
static void
gst_uri_source_bin_class_init (GstURISourceBinClass * klass)
{
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
- * GstURISourceBin::unknown-type:
- * @bin: The urisourcebin.
- * @pad: the new pad containing caps that cannot be resolved to a 'final'.
- * stream type.
- * @caps: the #GstCaps of the pad that cannot be resolved.
- *
- * This signal is emitted when a pad for which there is no further possible
- * decoding is added to the urisourcebin.
- */
- gst_uri_source_bin_signals[SIGNAL_UNKNOWN_TYPE] =
- g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, unknown_type),
- NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2,
- GST_TYPE_PAD, GST_TYPE_CAPS);
-
- /**
- * GstURISourceBin::autoplug-continue:
- * @bin: The urisourcebin.
- * @pad: The #GstPad.
- * @caps: The #GstCaps found.
- *
- * This signal is emitted whenever urisourcebin finds a new stream. It is
- * emitted before looking for any elements that can handle that stream.
- *
- * > Invocation of signal handlers stops after the first signal handler
- * > returns #FALSE. Signal handlers are invoked in the order they were
- * > connected in.
- *
- * Returns: #TRUE if you wish urisourcebin to look for elements that can
- * handle the given @caps. If #FALSE, those caps will be considered as
- * final and the pad will be exposed as such (see 'pad-added' signal of
- * #GstElement).
- */
- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_CONTINUE] =
- g_signal_new ("autoplug-continue", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass,
- autoplug_continue), _gst_boolean_accumulator, NULL,
- g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 2, GST_TYPE_PAD,
- GST_TYPE_CAPS);
-
- /**
- * GstURISourceBin::autoplug-factories:
- * @bin: The urisourcebin.
- * @pad: The #GstPad.
- * @caps: The #GstCaps found.
- *
- * This function is emitted when an array of possible factories for @caps on
- * @pad is needed. urisourcebin will by default return an array with all
- * compatible factories, sorted by rank.
- *
- * If this function returns NULL, @pad will be exposed as a final caps.
- *
- * If this function returns an empty array, the pad will be considered as
- * having an unhandled type media type.
- *
- * > Only the signal handler that is connected first will ever by invoked.
- * > Don't connect signal handlers with the #G_CONNECT_AFTER flag to this
- * > signal, they will never be invoked!
- *
- * Returns: a #GValueArray* with a list of factories to try. The factories are
- * by default tried in the returned order or based on the index returned by
- * "autoplug-select".
- */
- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_FACTORIES] =
- g_signal_new ("autoplug-factories", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass,
- autoplug_factories), _gst_array_accumulator, NULL,
- g_cclosure_marshal_generic, G_TYPE_VALUE_ARRAY, 2,
- GST_TYPE_PAD, GST_TYPE_CAPS);
-
- /**
- * GstURISourceBin::autoplug-sort:
- * @bin: The urisourcebin.
- * @pad: The #GstPad.
- * @caps: The #GstCaps.
- * @factories: A #GValueArray of possible #GstElementFactory to use.
- *
- * Once decodebin has found the possible #GstElementFactory objects to try
- * for @caps on @pad, this signal is emited. The purpose of the signal is for
- * the application to perform additional sorting or filtering on the element
- * factory array.
- *
- * The callee should copy and modify @factories or return #NULL if the
- * order should not change.
- *
- * > Invocation of signal handlers stops after one signal handler has
- * > returned something else than #NULL. Signal handlers are invoked in
- * > the order they were connected in.
- * > Don't connect signal handlers with the #G_CONNECT_AFTER flag to this
- * > signal, they will never be invoked!
- *
- * Returns: A new sorted array of #GstElementFactory objects.
- *
- * Since: 0.10.33
- */
- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_SORT] =
- g_signal_new ("autoplug-sort", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, autoplug_sort),
- _gst_array_hasvalue_accumulator, NULL,
- g_cclosure_marshal_generic, G_TYPE_VALUE_ARRAY, 3, GST_TYPE_PAD,
- GST_TYPE_CAPS, G_TYPE_VALUE_ARRAY | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- /**
- * GstURISourceBin::autoplug-select:
- * @bin: The urisourcebin.
- * @pad: The #GstPad.
- * @caps: The #GstCaps.
- * @factory: A #GstElementFactory to use.
- *
- * This signal is emitted once urisourcebin has found all the possible
- * #GstElementFactory that can be used to handle the given @caps. For each of
- * those factories, this signal is emitted.
- *
- * The signal handler should return a #GST_TYPE_AUTOPLUG_SELECT_RESULT enum
- * value indicating what decodebin should do next.
- *
- * A value of #GST_AUTOPLUG_SELECT_TRY will try to autoplug an element from
- * @factory.
- *
- * A value of #GST_AUTOPLUG_SELECT_EXPOSE will expose @pad without plugging
- * any element to it.
- *
- * A value of #GST_AUTOPLUG_SELECT_SKIP will skip @factory and move to the
- * next factory.
- *
- * > The signal handler will not be invoked if any of the previously
- * > registered signal handlers (if any) return a value other than
- * > GST_AUTOPLUG_SELECT_TRY. Which also means that if you return
- * > GST_AUTOPLUG_SELECT_TRY from one signal handler, handlers that get
- * > registered next (again, if any) can override that decision.
- *
- * Returns: a #GST_TYPE_AUTOPLUG_SELECT_RESULT that indicates the required
- * operation. The default handler will always return
- * #GST_AUTOPLUG_SELECT_TRY.
- */
- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_SELECT] =
- g_signal_new ("autoplug-select", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass,
- autoplug_select), _gst_select_accumulator, NULL,
- g_cclosure_marshal_generic,
- GST_TYPE_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS,
- GST_TYPE_ELEMENT_FACTORY);
-
- /**
- * GstDecodeBin::autoplug-query:
- * @bin: The decodebin.
- * @child: The child element doing the query
- * @pad: The #GstPad.
- * @query: The #GstQuery.
- *
- * This signal is emitted whenever an autoplugged element that is
- * not linked downstream yet and not exposed does a query. It can
- * be used to tell the element about the downstream supported caps
- * for example.
- *
- * Returns: #TRUE if the query was handled, #FALSE otherwise.
- */
- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_QUERY] =
- g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, autoplug_query),
- _gst_boolean_or_accumulator, NULL, g_cclosure_marshal_generic,
- G_TYPE_BOOLEAN, 3, GST_TYPE_PAD, GST_TYPE_ELEMENT,
- GST_TYPE_QUERY | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- /**
* GstURISourceBin::drained:
*
* This signal is emitted when the data for the current uri is played.
G_STRUCT_OFFSET (GstURISourceBinClass, drained), NULL, NULL,
g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
+ /**
+ * GstURISourceBin::about-to-finish:
+ *
+ * This signal is emitted when the data for the current uri is played.
+ */
+ gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH] =
+ g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstURISourceBinClass, about_to_finish), NULL, NULL,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
+
/**
* GstURISourceBin::source-setup:
* @bin: the urisourcebin.
GST_DEBUG_FUNCPTR (gst_uri_source_bin_change_state);
gstbin_class->handle_message = GST_DEBUG_FUNCPTR (handle_message);
-
- klass->autoplug_continue =
- GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_continue);
- klass->autoplug_factories =
- GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_factories);
- klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_sort);
- klass->autoplug_select =
- GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_select);
- klass->autoplug_query = GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_query);
}
static void
remove_demuxer (urisrc);
g_mutex_clear (&urisrc->lock);
g_mutex_clear (&urisrc->factories_lock);
+ g_mutex_clear (&urisrc->buffering_lock);
+ g_mutex_clear (&urisrc->buffering_post_lock);
g_free (urisrc->uri);
if (urisrc->factories)
gst_plugin_feature_list_free (urisrc->factories);
}
}
-static void
-do_async_start (GstURISourceBin * dbin)
-{
- GstMessage *message;
-
- dbin->async_pending = TRUE;
-
- message = gst_message_new_async_start (GST_OBJECT_CAST (dbin));
- GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (dbin), message);
-}
-
-static void
-do_async_done (GstURISourceBin * dbin)
-{
- GstMessage *message;
-
- if (dbin->async_pending) {
- GST_DEBUG_OBJECT (dbin, "posting ASYNC_DONE");
- message =
- gst_message_new_async_done (GST_OBJECT_CAST (dbin),
- GST_CLOCK_TIME_NONE);
- GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (dbin), message);
-
- dbin->async_pending = FALSE;
- }
-}
#define DEFAULT_QUEUE_SIZE (3 * GST_SECOND)
#define DEFAULT_QUEUE_MIN_THRESHOLD ((DEFAULT_QUEUE_SIZE * 30) / 100)
slot->is_eos = FALSE;
BUFFERING_UNLOCK (urisrc);
res = TRUE;
+ slot->is_eos = FALSE;
urisrc->pending_pads =
g_list_remove (urisrc->pending_pads, out_info->demux_src_pad);
} else {
return res;
}
+/* Called with lock held */
+static gboolean
+all_slots_are_eos (GstURISourceBin * urisrc)
+{
+ GSList *tmp;
+
+ for (tmp = urisrc->out_slots; tmp; tmp = tmp->next) {
+ OutputSlotInfo *slot = (OutputSlotInfo *) tmp->data;
+ if (slot->is_eos == FALSE)
+ return FALSE;
+ }
+ return TRUE;
+}
+
static GstPadProbeReturn
demux_pad_events (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
switch (GST_EVENT_TYPE (ev)) {
case GST_EVENT_EOS:
{
- GstEvent *event;
- GstStructure *s;
- guint32 seqnum = gst_event_get_seqnum (ev);
+ gboolean all_streams_eos;
GST_LOG_OBJECT (urisrc, "EOS on pad %" GST_PTR_FORMAT, pad);
- /* never forward actual EOS to slot */
- ret = GST_PAD_PROBE_DROP;
-
if ((urisrc->pending_pads &&
link_pending_pad_to_output (urisrc, child_info->output_slot))) {
/* Found a new source pad to give this slot data - no need to send EOS */
GST_URI_SOURCE_BIN_UNLOCK (urisrc);
+ ret = GST_PAD_PROBE_DROP;
goto done;
}
BUFFERING_LOCK (urisrc);
/* Mark that we fed an EOS to this slot */
child_info->output_slot->is_eos = TRUE;
+ all_streams_eos = all_slots_are_eos (urisrc);
BUFFERING_UNLOCK (urisrc);
/* EOS means this element is no longer buffering */
remove_buffering_msgs (urisrc,
GST_OBJECT_CAST (child_info->output_slot->queue));
- /* Actually feed a custom EOS event to avoid marking pads as EOSed */
- s = gst_structure_new_empty ("urisourcebin-custom-eos");
- event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
- gst_event_set_seqnum (event, seqnum);
- gst_pad_send_event (child_info->output_slot->sinkpad, event);
+ /* Mark this custom EOS */
+ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (ev), CUSTOM_EOS_QUARK,
+ (gchar *) CUSTOM_EOS_QUARK_DATA, NULL);
+ if (all_streams_eos) {
+ GST_DEBUG_OBJECT (urisrc, "POSTING ABOUT TO FINISH");
+ g_signal_emit (urisrc,
+ gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+ }
}
break;
case GST_EVENT_CAPS:
return ret;
}
+static GstPadProbeReturn
+pre_queue_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
+{
+ GstURISourceBin *urisrc = GST_URI_SOURCE_BIN (user_data);
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+ GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info);
+
+ switch (GST_EVENT_TYPE (ev)) {
+ case GST_EVENT_EOS:
+ {
+ GST_LOG_OBJECT (urisrc, "EOS on pad %" GST_PTR_FORMAT, pad);
+ GST_DEBUG_OBJECT (urisrc, "POSTING ABOUT TO FINISH");
+ g_signal_emit (urisrc,
+ gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
/* Called with lock held */
static OutputSlotInfo *
get_output_slot (GstURISourceBin * urisrc, gboolean do_download,
if (cur_caps == NULL || gst_caps_is_equal (caps, cur_caps)) {
GST_LOG_OBJECT (urisrc, "Found existing slot %p to link to", slot);
gst_caps_unref (cur_caps);
+ slot->is_eos = FALSE;
return slot;
}
gst_caps_unref (cur_caps);
GST_LOG_OBJECT (pad, "%s, urisrc %p", GST_EVENT_TYPE_NAME (event), event);
- if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM &&
- gst_event_has_name (event, "urisourcebin-custom-eos")) {
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS &&
+ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (event),
+ CUSTOM_EOS_QUARK)) {
OutputSlotInfo *slot;
GST_DEBUG_OBJECT (pad, "we received EOS");
+ /* remove custom-eos */
+ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (event), CUSTOM_EOS_QUARK,
+ NULL, NULL);
+
GST_URI_SOURCE_BIN_LOCK (urisrc);
slot = g_object_get_data (G_OBJECT (pad), "urisourcebin.slotinfo");
guint32 seqnum;
if (slot->linked_info) {
- /* Do not clear output slot yet. A new input was
- * connected. We should just drop this EOS */
+ if (slot->is_eos) {
+ /* linked_info is old input which is stil linked without removal */
+ GST_DEBUG_OBJECT (pad, "push actual EOS");
+ seqnum = gst_event_get_seqnum (event);
+ eos = gst_event_new_eos ();
+ gst_event_set_seqnum (eos, seqnum);
+ gst_pad_push_event (slot->srcpad, eos);
+ } else {
+ /* Do not clear output slot yet. A new input was
+ * connected. We should just drop this EOS */
+ }
GST_URI_SOURCE_BIN_UNLOCK (urisrc);
return GST_PAD_PROBE_DROP;
}
gst_object_unref (pad_tmpl);
g_free (padname);
+ GST_DEBUG_OBJECT (urisrc, "Created output pad %s:%s for pad %s:%s",
+ GST_DEBUG_PAD_NAME (newpad), GST_DEBUG_PAD_NAME (pad));
+
return newpad;
}
gst_pad_set_active (pad, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (urisrc), pad);
-
- /* Once we expose a pad, we're no longer async */
- do_async_done (urisrc);
}
static void
GST_LOG_OBJECT (element,
"Pad %" GST_PTR_FORMAT " was removed without EOS. Sending.", pad);
- s = gst_structure_new_empty ("urisourcebin-custom-eos");
- event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+ event = gst_event_new_eos ();
+ s = gst_event_writable_structure (event);
+ gst_structure_set (s, "urisourcebin-custom-eos", G_TYPE_BOOLEAN, TRUE,
+ NULL);
gst_pad_send_event (slot->sinkpad, event);
} else {
GST_LOG_OBJECT (urisrc, "Removed pad has no output slot");
GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
element_name), (NULL));
- do_async_done (GST_URI_SOURCE_BIN (dec));
}
/**
gst_object_unref (pad);
pad = slot->srcpad;
} else {
- pad = create_output_pad (urisrc, pad);
+ GstPad *tmppad = create_output_pad (urisrc, pad);
+ gst_object_unref (pad);
+
+ pad = tmppad;
}
GST_URI_SOURCE_BIN_UNLOCK (urisrc);
expose_output_pad (urisrc, pad);
+ } else {
gst_object_unref (pad);
}
- gst_object_unref (pad);
g_value_reset (&item);
break;
}
/* FIXME: Fire the right error */
GST_ELEMENT_ERROR (urisrc, CORE, MISSING_PLUGIN, (NULL),
("No demuxer element, check your installation"));
- do_async_done (urisrc);
return NULL;
}
}
gst_query_unref (query);
}
- GST_DEBUG_OBJECT (urisrc, "check media-type %s, %d", media_type,
+ GST_DEBUG_OBJECT (urisrc, "check media-type %s, do_download:%d", media_type,
do_download);
GST_URI_SOURCE_BIN_LOCK (urisrc);
if (slot == NULL || gst_pad_link (srcpad, slot->sinkpad) != GST_PAD_LINK_OK)
goto could_not_link;
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ pre_queue_event_probe, urisrc, NULL);
+
expose_output_pad (urisrc, slot->srcpad);
GST_URI_SOURCE_BIN_UNLOCK (urisrc);
}
{
GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION,
(NULL), ("Adaptive demuxer element has no 'sink' pad"));
- do_async_done (urisrc);
return;
}
could_not_link:
GST_URI_SOURCE_BIN_UNLOCK (urisrc);
GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION,
(NULL), ("Can't link typefind to adaptive demuxer element"));
- do_async_done (urisrc);
return;
}
}
post_missing_plugin_error (GST_ELEMENT_CAST (urisrc), "typefind");
GST_ELEMENT_ERROR (urisrc, CORE, MISSING_PLUGIN, (NULL),
("No typefind element, check your installation"));
- do_async_done (urisrc);
return FALSE;
}
could_not_link:
GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION,
(NULL), ("Can't link source to typefind element"));
gst_bin_remove (GST_BIN_CAST (urisrc), typefind);
- do_async_done (urisrc);
return FALSE;
}
}
/* source provides raw data, we added the pads and we can now signal a
* no_more pads because we are done. */
gst_element_no_more_pads (GST_ELEMENT_CAST (urisrc));
- do_async_done (urisrc);
return TRUE;
}
if (!have_out && !is_dynamic) {
return res;
}
-static void
-sync_slot_queue (OutputSlotInfo * slot)
-{
- gst_element_sync_state_with_parent (slot->queue);
-}
-
static GstStateChangeReturn
gst_uri_source_bin_change_state (GstElement * element,
GstStateChange transition)
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
- do_async_start (urisrc);
+ GST_DEBUG ("ready to paused");
+ if (!setup_source (urisrc))
+ goto source_failed;
break;
default:
break;
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
goto setup_failed;
- else if (ret == GST_STATE_CHANGE_NO_PREROLL)
- do_async_done (urisrc);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
- GST_DEBUG ("ready to paused");
- if (!setup_source (urisrc))
- goto source_failed;
-
- ret = GST_STATE_CHANGE_ASYNC;
-
- /* And now sync the states of everything we added */
- g_slist_foreach (urisrc->out_slots, (GFunc) sync_slot_queue, NULL);
- if (urisrc->typefinds) {
- GList *iter;
- for (iter = urisrc->typefinds; iter; iter = iter->next) {
- GstElement *typefind = iter->data;
- ret = gst_element_set_state (typefind, GST_STATE_PAUSED);
- if (ret == GST_STATE_CHANGE_FAILURE)
- goto setup_failed;
- }
- }
- if (urisrc->source)
- ret = gst_element_set_state (urisrc->source, GST_STATE_PAUSED);
- if (ret == GST_STATE_CHANGE_FAILURE)
- goto setup_failed;
-
- if (ret == GST_STATE_CHANGE_SUCCESS)
- ret = GST_STATE_CHANGE_ASYNC;
- else if (ret == GST_STATE_CHANGE_NO_PREROLL)
- do_async_done (urisrc);
-
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_DEBUG ("paused to ready");
remove_demuxer (urisrc);
remove_source (urisrc);
- do_async_done (urisrc);
g_list_free_full (urisrc->buffering_status,
(GDestroyNotify) gst_message_unref);
urisrc->buffering_status = NULL;
/* ERRORS */
source_failed:
{
- do_async_done (urisrc);
return GST_STATE_CHANGE_FAILURE;
}
setup_failed:
{
/* clean up leftover groups */
- do_async_done (urisrc);
return GST_STATE_CHANGE_FAILURE;
}
}