typedef struct _GstPlayBin3 GstPlayBin3;
typedef struct _GstPlayBin3Class GstPlayBin3Class;
-typedef struct _GstSourceGroup GstSourceGroup;
typedef struct _GstSourceCombine GstSourceCombine;
typedef struct _SourcePad SourcePad;
GPtrArray *streams; /* Sorted array of GstStream for the given type */
gboolean has_active_pad; /* stream combiner has the "active-pad" property */
-
- gboolean is_concat; /* The stream combiner is the 'concat' element */
};
-#define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
-#define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
-#define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
-
enum
{
PLAYBIN_STREAM_AUDIO = 0,
gulong event_probe_id;
};
-/* a structure to hold the objects for decoding a uri and the subtitle uri
- */
-struct _GstSourceGroup
-{
- GstPlayBin3 *playbin;
-
- GMutex lock;
-
- 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;
-
- /* The currently outputted group_id */
- guint group_id;
-
- /* 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
- * left as NULL and playbin tries to automatically
- * select a good sink */
- GstElement *audio_sink;
- GstElement *video_sink;
- GstElement *text_sink;
-
- /* 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;
-
- gboolean stream_changed_pending;
-
- /* Active stream collection */
- GstStreamCollection *collection;
-
-
- /* buffering message stored for after switching */
- GstMessage *pending_buffering_msg;
-};
-
#define GST_PLAY_BIN3_GET_LOCK(bin) (&((GstPlayBin3*)(bin))->lock)
#define GST_PLAY_BIN3_LOCK(bin) (g_rec_mutex_lock (GST_PLAY_BIN3_GET_LOCK(bin)))
#define GST_PLAY_BIN3_UNLOCK(bin) (g_rec_mutex_unlock (GST_PLAY_BIN3_GET_LOCK(bin)))
-/* lock to protect dynamic callbacks, like no-more-pads */
-#define GST_PLAY_BIN3_DYN_LOCK(bin) g_mutex_lock (&(bin)->dyn_lock)
-#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
-
-/* unlock for shutdown */
-#define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \
- GST_PLAY_BIN3_DYN_UNLOCK (bin); \
-
/**
* GstPlayBin3:
*
{
GstPipeline parent;
- GRecMutex lock; /* to protect group switching */
+ GRecMutex lock; /* to protect various properties */
+
+ /* uridecodebin to handle uri and suburi */
+ GstElement *uridecodebin;
- /* the input groups, we use a double buffer to switch between current and next */
- GstSourceGroup groups[2]; /* array with group info */
- GstSourceGroup *curr_group; /* pointer to the currently playing group */
- GstSourceGroup *next_group; /* pointer to the next group */
+ /* List of SourcePad structures */
+ GList *source_pads;
+
+ /* Active stream collection */
+ GstStreamCollection *collection;
/* combiners for different streams */
GstSourceCombine combiner[PLAYBIN_STREAM_LAST];
- /* Bit-wise set of stream types we have requested from uridecodebin3.
- * Calculated as the combination of the 'selected_stream_types' of
- * each sourcegroup */
+ /* Bit-wise set of stream types we have requested from uridecodebin3. */
GstStreamType selected_stream_types;
/* Bit-wise set of configured output stream types (i.e. active
GstStreamType active_stream_types;
/* properties */
- guint64 connection_speed; /* connection speed in bits/sec (0 = unknown) */
gint current_video; /* the currently selected stream */
gint current_audio; /* the currently selected stream */
gint current_text; /* the currently selected stream */
gboolean do_stream_selections; /* Set to TRUE when any of current-{video|audio|text} are set to
say playbin should do backwards-compatibility behaviours */
- guint64 buffer_duration; /* When buffering, the max buffer duration (ns) */
- guint buffer_size; /* When buffering, the max buffer size (bytes) */
+ GstObject *collection_source; /* The element that provided the latest stream collection */
gboolean force_aspect_ratio;
/* Multiview/stereoscopic overrides */
/* our play sink */
GstPlaySink *playsink;
- /* Task for (de)activating groups, protected by the activation lock */
- GstTask *activation_task;
- GRecMutex activation_lock;
-
- /* lock protecting dynamic adding/removing */
- GMutex dyn_lock;
- /* if we are shutting down or not */
- gint shutdown;
gboolean async_pending; /* async-start has been emitted */
gboolean have_selector; /* set to FALSE when we fail to create an
GstElement *video_stream_combiner; /* configured video stream combiner, or NULL */
GstElement *text_stream_combiner; /* configured text stream combiner, or NULL */
- guint64 ring_buffer_max_size; /* 0 means disabled */
-
- gboolean is_live; /* Whether our current group is live */
-
- GMutex buffering_post_lock; /* Protect serialisation of buffering messages. Must not acquire this while holding any SOURCE_GROUP lock */
+ gboolean is_live; /* Whether we are live */
};
struct _GstPlayBin3Class
PROP_AUDIO_FILTER,
PROP_VIDEO_FILTER,
PROP_MULTIVIEW_MODE,
- PROP_MULTIVIEW_FLAGS
+ PROP_MULTIVIEW_FLAGS,
+ PROP_INSTANT_URI
};
/* signals */
static GstSample *gst_play_bin3_convert_sample (GstPlayBin3 * playbin,
GstCaps * caps);
-static GstStateChangeReturn setup_next_source (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,
- GstSourceGroup * group);
+/* uridecodebin3 signal callbacks */
+static void pad_added_cb (GstElement * uridecodebin, GstPad * pad,
+ GstPlayBin3 * playbin);
+static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
+ GstPlayBin3 * playbin);
static gint select_stream_cb (GstElement * decodebin,
GstStreamCollection * collection, GstStream * stream,
- GstSourceGroup * group);
+ GstPlayBin3 * playbin);
+static void source_setup_cb (GstElement * element, GstElement * source,
+ GstPlayBin3 * playbin);
+static void about_to_finish_cb (GstElement * uridecodebin,
+ GstPlayBin3 * playbin);
-static void do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group);
+static void do_stream_selection (GstPlayBin3 * playbin);
static GstElementClass *parent_class;
"Buffer size when buffering network streams",
-1, G_MAXINT, DEFAULT_BUFFER_SIZE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
"Buffer duration when buffering network streams",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
+ * GstPlayBin3:instant-uri:
+ *
+ * Changes to uri are applied immediately, instead of on EOS or when the
+ * element is set back to PLAYING.
+ *
+ * Since: 1.22
+ */
+ g_object_class_install_property (gobject_klass, PROP_INSTANT_URI,
+ g_param_spec_boolean ("instant-uri", "Instantaneous URI change",
+ "When enabled, URI changes are applied immediately", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
* GstPlayBin3::about-to-finish
* @playbin: a #GstPlayBin3
*
playbin->async_pending = TRUE;
+ GST_DEBUG_OBJECT (playbin, "posting ASYNC_START");
+
message = gst_message_new_async_start (GST_OBJECT_CAST (playbin));
GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (playbin),
message);
g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
}
-/* 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 */
+/* Update the combiner information to be in sync with the current collection */
static void
update_combiner_info (GstPlayBin3 * playbin, GstStreamCollection * collection)
{
playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len);
}
-#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) : %s", i, (group == playbin->curr_group) ? "current" : (group == playbin->next_group) ? "next" : "unused", \
- group->uridecodebin ? GST_ELEMENT_NAME (group->uridecodebin) : "NULL" ); \
- 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;
- group->group_id = GST_GROUP_ID_INVALID;
-
- group->playbin = playbin;
-}
-
-static void
-free_group (GstPlayBin3 * playbin, GstSourceGroup * group)
-{
- g_free (group->uri);
- g_free (group->suburi);
-
- g_mutex_clear (&group->lock);
- group->stream_changed_pending = FALSE;
-
- 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);
-}
-
static void
notify_volume_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin3 * playbin)
{
gst_play_bin3_init (GstPlayBin3 * playbin)
{
g_rec_mutex_init (&playbin->lock);
- g_mutex_init (&playbin->dyn_lock);
-
- g_mutex_init (&playbin->buffering_post_lock);
/* assume we can create an input-selector */
playbin->have_selector = TRUE;
init_combiners (playbin);
- /* init groups */
- playbin->curr_group = &playbin->groups[0];
- playbin->next_group = &playbin->groups[1];
- init_group (playbin, &playbin->groups[0]);
- init_group (playbin, &playbin->groups[1]);
-
- g_rec_mutex_init (&playbin->activation_lock);
+ /* Create uridecodebin3 */
+ playbin->uridecodebin =
+ gst_element_factory_make ("uridecodebin3", "uridecodebin3");
+ /* Set default property (based on default flags value) */
+ g_object_set (playbin->uridecodebin, "use-buffering", TRUE, NULL);
+ gst_bin_add (GST_BIN_CAST (playbin),
+ GST_ELEMENT_CAST (playbin->uridecodebin));
+
+ g_signal_connect (playbin->uridecodebin, "pad-added",
+ G_CALLBACK (pad_added_cb), playbin);
+ g_signal_connect (playbin->uridecodebin, "pad-removed",
+ G_CALLBACK (pad_removed_cb), playbin);
+ g_signal_connect (playbin->uridecodebin, "select-stream",
+ G_CALLBACK (select_stream_cb), playbin);
+ g_signal_connect (playbin->uridecodebin, "source-setup",
+ G_CALLBACK (source_setup_cb), playbin);
+ g_signal_connect (playbin->uridecodebin, "about-to-finish",
+ G_CALLBACK (about_to_finish_cb), playbin);
/* add sink */
playbin->playsink =
playbin->current_audio = DEFAULT_CURRENT_AUDIO;
playbin->current_text = DEFAULT_CURRENT_TEXT;
- playbin->buffer_duration = DEFAULT_BUFFER_DURATION;
- playbin->buffer_size = DEFAULT_BUFFER_SIZE;
- playbin->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
-
playbin->force_aspect_ratio = TRUE;
playbin->multiview_mode = GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE;
playbin = GST_PLAY_BIN3 (object);
- free_group (playbin, &playbin->groups[0]);
- free_group (playbin, &playbin->groups[1]);
-
/* 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_TEXT].streams, TRUE);
g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].inputpads, TRUE);
- g_rec_mutex_clear (&playbin->activation_lock);
g_rec_mutex_clear (&playbin->lock);
- g_mutex_clear (&playbin->buffering_post_lock);
- g_mutex_clear (&playbin->dyn_lock);
-
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_play_bin3_set_uri (GstPlayBin3 * playbin, const gchar * uri)
{
- GstSourceGroup *group;
+ if (uri == NULL) {
+ g_warning ("cannot set NULL uri");
+ return;
+ }
- if (uri && !gst_playbin_uri_is_valid (playbin, uri)) {
+ if (!gst_playbin_uri_is_valid (playbin, uri)) {
if (g_str_has_prefix (uri, "file:")) {
GST_WARNING_OBJECT (playbin, "not entirely correct file URI '%s' - make "
"sure to escape spaces and non-ASCII characters properly and specify "
}
}
- GST_PLAY_BIN3_LOCK (playbin);
- group = playbin->next_group;
-
- GST_SOURCE_GROUP_LOCK (group);
- /* store the uri in the next group we will play */
- g_free (group->uri);
- if (uri) {
- group->uri = g_strdup (uri);
- group->valid = TRUE;
- } else {
- group->uri = NULL;
- group->valid = FALSE;
- }
- GST_SOURCE_GROUP_UNLOCK (group);
+ g_object_set (playbin->uridecodebin, "uri", uri, NULL);
GST_DEBUG ("set new uri to %s", GST_STR_NULL (uri));
- GST_PLAY_BIN3_UNLOCK (playbin);
}
static void
gst_play_bin3_set_suburi (GstPlayBin3 * playbin, const gchar * suburi)
{
- GstSourceGroup *group;
-
- GST_PLAY_BIN3_LOCK (playbin);
- group = playbin->next_group;
-
- GST_SOURCE_GROUP_LOCK (group);
- g_free (group->suburi);
- group->suburi = g_strdup (suburi);
- GST_SOURCE_GROUP_UNLOCK (group);
+ g_object_set (playbin->uridecodebin, "suburi", suburi, NULL);
GST_DEBUG ("setting new .sub uri to %s", suburi);
-
- GST_PLAY_BIN3_UNLOCK (playbin);
}
static void
gst_play_sink_set_flags (playbin->playsink, flags);
gst_play_sink_reconfigure (playbin->playsink);
}
+ g_object_set (playbin->uridecodebin, "download",
+ ((flags & GST_PLAY_FLAG_DOWNLOAD) != 0),
+ /* configure buffering of demuxed/parsed data */
+ "use-buffering", ((flags & GST_PLAY_FLAG_BUFFERING) != 0), NULL);
}
-static GstPlayFlags
-gst_play_bin3_get_flags (GstPlayBin3 * playbin)
-{
- GstPlayFlags flags;
-
- flags = gst_play_sink_get_flags (playbin->playsink);
-
- return flags;
-}
-
-/* get the currently playing group or if nothing is playing, the next
- * group. Must be called with the PLAY_BIN_LOCK. */
-static GstSourceGroup *
-get_group (GstPlayBin3 * playbin)
-{
- GstSourceGroup *result;
-
- if (!(result = playbin->curr_group))
- result = playbin->next_group;
-
- return result;
-}
-
-
static GstSample *
gst_play_bin3_convert_sample (GstPlayBin3 * playbin, GstCaps * caps)
{
GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d",
stream_type_names[stream_type], *current_value, stream);
- if (combine->combiner == NULL || combine->is_concat) {
+ if (combine->combiner == NULL) {
/* FIXME: Check that the current_value is within range */
*current_value = stream;
- do_stream_selection (playbin, playbin->curr_group);
+ do_stream_selection (playbin);
GST_PLAY_BIN3_UNLOCK (playbin);
return TRUE;
}
break;
case PROP_FLAGS:
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->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);
- }
break;
case PROP_SUBTITLE_ENCODING:
gst_play_bin3_set_encoding (playbin, g_value_get_string (value));
break;
case PROP_CONNECTION_SPEED:
GST_PLAY_BIN3_LOCK (playbin);
- playbin->connection_speed = g_value_get_uint64 (value) * 1000;
+ g_object_set_property ((GObject *) playbin->uridecodebin,
+ "connection-speed", value);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
case PROP_BUFFER_SIZE:
- playbin->buffer_size = g_value_get_int (value);
+ g_object_set_property ((GObject *) playbin->uridecodebin, "buffer-size",
+ value);
break;
case PROP_BUFFER_DURATION:
- playbin->buffer_duration = g_value_get_int64 (value);
+ g_object_set_property ((GObject *) playbin->uridecodebin,
+ "buffer-duration", value);
break;
case PROP_AV_OFFSET:
gst_play_sink_set_av_offset (playbin->playsink,
g_value_get_int64 (value));
break;
case PROP_RING_BUFFER_MAX_SIZE:
- 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->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);
- }
+ g_object_set_property ((GObject *) playbin->uridecodebin,
+ "ring-buffer-max-size", value);
break;
case PROP_FORCE_ASPECT_RATIO:
g_object_set (playbin->playsink, "force-aspect-ratio",
playbin->multiview_flags = g_value_get_flags (value);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
+ case PROP_INSTANT_URI:
+ g_object_set_property ((GObject *) playbin->uridecodebin,
+ "instant-uri", value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
return sink;
}
-static GstElement *
-gst_play_bin3_get_current_stream_combiner (GstPlayBin3 * playbin,
- GstElement ** elem, const gchar * dbg, int stream_type)
-{
- GstElement *combiner;
-
- GST_PLAY_BIN3_LOCK (playbin);
- /* 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);
-
- return combiner;
-}
-
static void
gst_play_bin3_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
switch (prop_id) {
case PROP_URI:
{
- GstSourceGroup *group;
-
GST_PLAY_BIN3_LOCK (playbin);
- group = playbin->next_group;
- g_value_set_string (value, group->uri);
+ g_object_get_property ((GObject *) playbin->uridecodebin, "uri", value);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
}
case PROP_CURRENT_URI:
{
- GstSourceGroup *group;
-
GST_PLAY_BIN3_LOCK (playbin);
- group = get_group (playbin);
- g_value_set_string (value, group->uri);
+ g_object_get_property ((GObject *) playbin->uridecodebin, "current-uri",
+ value);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
}
case PROP_SUBURI:
{
- GstSourceGroup *group;
-
GST_PLAY_BIN3_LOCK (playbin);
- group = playbin->next_group;
- g_value_set_string (value, group->suburi);
+ g_object_get_property ((GObject *) playbin->uridecodebin, "suburi",
+ value);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
}
case PROP_CURRENT_SUBURI:
{
- GstSourceGroup *group;
-
GST_PLAY_BIN3_LOCK (playbin);
- group = get_group (playbin);
- g_value_set_string (value, group->suburi);
+ g_object_get_property ((GObject *) playbin->uridecodebin,
+ "current-suburi", value);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
}
case PROP_FLAGS:
- g_value_set_flags (value, gst_play_bin3_get_flags (playbin));
+ g_value_set_flags (value, gst_play_sink_get_flags (playbin->playsink));
break;
case PROP_SUBTITLE_ENCODING:
GST_PLAY_BIN3_LOCK (playbin);
"text", GST_PLAY_SINK_TYPE_TEXT));
break;
case PROP_VIDEO_STREAM_COMBINER:
- g_value_take_object (value,
- gst_play_bin3_get_current_stream_combiner (playbin,
- &playbin->video_stream_combiner, "video", PLAYBIN_STREAM_VIDEO));
+ g_value_set_object (value, playbin->video_stream_combiner);
break;
case PROP_AUDIO_STREAM_COMBINER:
- g_value_take_object (value,
- gst_play_bin3_get_current_stream_combiner (playbin,
- &playbin->audio_stream_combiner, "audio", PLAYBIN_STREAM_AUDIO));
+ g_value_set_object (value, playbin->audio_stream_combiner);
break;
case PROP_TEXT_STREAM_COMBINER:
- g_value_take_object (value,
- gst_play_bin3_get_current_stream_combiner (playbin,
- &playbin->text_stream_combiner, "text", PLAYBIN_STREAM_TEXT));
+ g_value_set_object (value, playbin->text_stream_combiner);
break;
case PROP_VOLUME:
g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
break;
case PROP_CONNECTION_SPEED:
GST_PLAY_BIN3_LOCK (playbin);
- g_value_set_uint64 (value, playbin->connection_speed / 1000);
+ g_object_get_property ((GObject *) playbin->uridecodebin,
+ "connection-speed", value);
GST_PLAY_BIN3_UNLOCK (playbin);
break;
case PROP_BUFFER_SIZE:
GST_OBJECT_LOCK (playbin);
- g_value_set_int (value, playbin->buffer_size);
+ g_object_get_property ((GObject *) playbin->uridecodebin, "buffer-size",
+ value);
GST_OBJECT_UNLOCK (playbin);
break;
case PROP_BUFFER_DURATION:
GST_OBJECT_LOCK (playbin);
- g_value_set_int64 (value, playbin->buffer_duration);
+ g_object_get_property ((GObject *) playbin->uridecodebin,
+ "buffer-duration", value);
GST_OBJECT_UNLOCK (playbin);
break;
case PROP_AV_OFFSET:
gst_play_sink_get_text_offset (playbin->playsink));
break;
case PROP_RING_BUFFER_MAX_SIZE:
- g_value_set_uint64 (value, playbin->ring_buffer_max_size);
+ g_object_get_property ((GObject *) playbin->uridecodebin,
+ "ring-buffer-max-size", value);
break;
case PROP_FORCE_ASPECT_RATIO:{
gboolean v;
g_value_set_flags (value, playbin->multiview_flags);
GST_OBJECT_UNLOCK (playbin);
break;
+ case PROP_INSTANT_URI:
+ g_object_get_property ((GObject *) playbin->uridecodebin,
+ "instant-uri", value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
static GstEvent *
-update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event,
- GstSourceGroup * group)
+update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event)
{
GList *streams = NULL;
GList *to_use;
return event;
}
- if (!group->collection) {
+ if (!playbin->collection) {
GST_DEBUG_OBJECT (playbin,
- "No stream collection for group, no need to modify SELECT_STREAMS event");
+ "No stream collection, no need to modify SELECT_STREAMS event");
return event;
}
if (playbin->audio_stream_combiner) {
to_use =
extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use,
- group->collection);
+ playbin->collection);
combine_id =
get_combiner_stream_id (playbin,
&playbin->combiner[PLAYBIN_STREAM_AUDIO], streams);
if (playbin->video_stream_combiner) {
to_use =
extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use,
- group->collection);
+ playbin->collection);
combine_id =
get_combiner_stream_id (playbin,
&playbin->combiner[PLAYBIN_STREAM_VIDEO], streams);
if (playbin->text_stream_combiner) {
to_use =
extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use,
- group->collection);
+ playbin->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 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, group);
+ event = update_select_streams_event (playbin, event);
/* Don't reconfigure playsink just yet, until the streams-selected
* message(s) tell us as streams become active / available */
/* 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);
+ res = gst_element_send_event (playbin->uridecodebin, event);
GST_PLAY_BIN3_UNLOCK (playbin);
return res;
/* Called with playbin lock held */
static void
-do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group)
+do_stream_selection (GstPlayBin3 * playbin)
{
GstStreamCollection *collection;
guint i, nb_streams;
gint nb_video = 0, nb_audio = 0, nb_text = 0;
GstStreamType chosen_stream_types = 0;
- if (group == NULL)
- return;
-
- collection = group->collection;
+ collection = playbin->collection;
if (collection == NULL) {
GST_LOG_OBJECT (playbin, "No stream collection. Not doing stream-select");
return;
}
if (streams) {
- if (group->uridecodebin) {
- GstEvent *ev = gst_event_new_select_streams (streams);
- gst_element_send_event (group->uridecodebin, ev);
- }
+ GstEvent *ev = gst_event_new_select_streams (streams);
+ gst_element_send_event ((GstElement *) playbin->collection_source, ev);
g_list_free (streams);
}
- 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;
+ playbin->selected_stream_types = chosen_stream_types;
if (playbin->active_stream_types != playbin->selected_stream_types)
reconfigure_output (playbin);
}
-/* 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);
gboolean do_reset_time = FALSE;
- if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
- GstSourceGroup *group = NULL, *other_group = NULL;
- gboolean changed = FALSE;
- guint group_id;
- GstMessage *buffering_msg;
-
- 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_DEBUG_OBJECT (bin, "STREAM_START group_id:%u", group_id);
-
- /* 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;
- }
-
- debug_groups (playbin);
-
- /* Do the switch now ! */
- playbin->curr_group = group;
- playbin->next_group = other_group;
-
- /* we may need to serialise a buffering
- * message, and need to take that lock
- * before any source group lock, so
- * do that now */
- g_mutex_lock (&playbin->buffering_post_lock);
-
- GST_SOURCE_GROUP_LOCK (group);
- 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);
-
- g_mutex_unlock (&playbin->buffering_post_lock);
-
- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
- GstSourceGroup *group;
-
- /* Only post buffering messages for group which is currently playing */
- GST_PLAY_BIN3_LOCK (playbin);
- group = find_source_group_owner (playbin, msg->src);
- if (group->active) {
- g_mutex_lock (&playbin->buffering_post_lock);
-
- GST_SOURCE_GROUP_LOCK (group);
- GST_PLAY_BIN3_UNLOCK (playbin);
-
- 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_message_unref (msg);
- msg = NULL;
- } else {
- /* Ensure there's no cached buffering message for this group */
- gst_message_replace (&group->pending_buffering_msg, NULL);
- }
- GST_SOURCE_GROUP_UNLOCK (group);
-
- if (msg != NULL) {
- GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
- msg = NULL;
- }
- g_mutex_unlock (&playbin->buffering_post_lock);
- } else {
- GST_PLAY_BIN3_UNLOCK (playbin);
- }
- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_COLLECTION) {
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_COLLECTION) {
GstStreamCollection *collection = NULL;
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,
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);
+ gst_object_replace ((GstObject **) & playbin->collection,
+ (GstObject *) collection);
+ gst_object_replace ((GstObject **) & playbin->collection_source,
+ (GstObject *) GST_MESSAGE_SRC (msg));
+
+ update_combiner_info (playbin, playbin->collection);
if (pstate)
playbin->do_stream_selections = FALSE;
- do_stream_selection (playbin, target_group);
+ do_stream_selection (playbin);
if (pstate)
playbin->do_stream_selections = TRUE;
GST_PLAY_BIN3_UNLOCK (playbin);
do_reset_time = TRUE;
}
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAMS_SELECTED) {
- GstSourceGroup *target_group;
+ GstStreamType selected_types = 0;
+ guint i, nb;
GST_PLAY_BIN3_LOCK (playbin);
- target_group = find_source_group_owner (playbin, msg->src);
- if (target_group) {
- GstStreamType selected_types = 0;
- guint i, nb;
- nb = gst_message_streams_selected_get_size (msg);
- for (i = 0; i < nb; i++) {
- GstStream *stream = gst_message_streams_selected_get_stream (msg, i);
- selected_types |= gst_stream_get_stream_type (stream);
- gst_object_unref (stream);
- }
- target_group->selected_stream_types = selected_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) {
- GST_DEBUG_OBJECT (playbin,
- "selected stream types changed, reconfiguring output");
- reconfigure_output (playbin);
- }
+ nb = gst_message_streams_selected_get_size (msg);
+ for (i = 0; i < nb; i++) {
+ GstStream *stream = gst_message_streams_selected_get_stream (msg, i);
+ selected_types |= gst_stream_get_stream_type (stream);
+ gst_object_unref (stream);
+ }
+ playbin->selected_stream_types = selected_types;
+ if (playbin->active_stream_types != playbin->selected_stream_types) {
+ GST_DEBUG_OBJECT (playbin,
+ "selected stream types changed, reconfiguring output");
+ reconfigure_output (playbin);
}
GST_PLAY_BIN3_UNLOCK (playbin);
}
-beach:
if (msg)
GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
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)
+find_source_pad (GstPlayBin3 * playbin, GstPad * target)
{
GList *tmp;
- for (tmp = group->source_pads; tmp; tmp = tmp->next) {
+ for (tmp = playbin->source_pads; tmp; tmp = tmp->next) {
SourcePad *res = (SourcePad *) tmp->data;
if (res->pad == target)
return res;
}
static GstPadProbeReturn
-_decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
+_decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info,
+ GstPlayBin3 * playbin)
{
GstPadProbeReturn ret = GST_PAD_PROBE_OK;
- 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;
}
}
static void
-control_source_pad (GstSourceGroup * group, GstPad * pad,
+control_source_pad (GstPlayBin3 * playbin, GstPad * pad,
GstPad * combine_pad, GstStreamType stream_type)
{
SourcePad *sourcepad = g_slice_new0 (SourcePad);
sourcepad->pad = pad;
sourcepad->event_probe_id =
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
- _decodebin_event_probe, group, NULL);
+ (GstPadProbeCallback) _decodebin_event_probe, playbin, NULL);
sourcepad->stream_type = stream_type;
sourcepad->combine_sinkpad = combine_pad;
- group->source_pads = g_list_append (group->source_pads, sourcepad);
+ playbin->source_pads = g_list_append (playbin->source_pads, sourcepad);
}
static void
combine->combiner = custom_combiner;
if (!combine->combiner) {
- gchar *concat_name;
- GST_DEBUG_OBJECT (playbin,
- "No custom combiner requested, using 'concat' element");
- concat_name =
- g_strdup_printf ("%s-concat",
- gst_stream_type_get_name (combine->stream_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;
+ GST_DEBUG_OBJECT (playbin, "No custom combiner requested");
+ return;
}
combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
/* 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;
+ * will consume all streams */
+ 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);
- }
+ 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 %" GST_PTR_FORMAT,
combine->combiner);
return NULL;
}
-
-/* Call after pad was unlinked from (potential) combiner */
static void
-release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group,
+release_source_pad (GstPlayBin3 * playbin,
GstSourceCombine * combine, GstPad * pad)
{
SourcePad *sourcepad;
- GList *tmp;
- GstStreamType alltype = 0;
- sourcepad = find_source_pad (group, pad);
+ sourcepad = find_source_pad (playbin, pad);
if (!sourcepad) {
GST_DEBUG_OBJECT (playbin, "Not a pad controlled by us ?");
return;
}
/* Remove from list of controlled pads and check again for EOS status */
- group->source_pads = g_list_remove (group->source_pads, sourcepad);
+ playbin->source_pads = g_list_remove (playbin->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)
+pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstPlayBin3 * playbin)
{
GstSourceCombine *combine = NULL;
gint pb_stream_type = -1;
gchar *pad_name;
- GstPlayBin3 *playbin = group->playbin;
GstPad *combine_pad;
GstStreamType selected, active, cur;
- 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",
/* 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;
}
GST_DEBUG_PAD_NAME (pad));
playbin->selected_stream_types = selected;
reconfigure_output (playbin);
-
- /* shutdown state can be changed meantime then combiner will not be
- * configured */
- if (g_atomic_int_get (&playbin->shutdown)) {
- GST_PLAY_BIN3_UNLOCK (playbin);
- GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
- return;
- }
}
combine_pad = combiner_control_pad (playbin, combine, pad);
+ control_source_pad (playbin, pad, combine_pad, combine->stream_type);
- control_source_pad (group, pad, combine_pad, combine->stream_type);
-
- /* 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_UNLOCK (playbin);
- GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
-
return;
/* ERRORS */
unknown_type:
GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type");
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;
- }
}
/* 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)
+pad_removed_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin)
{
GstSourceCombine *combine;
- GstPlayBin3 *playbin = group->playbin;
GST_DEBUG_OBJECT (playbin,
"decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad));
else
goto done;
- release_source_pad (playbin, group, combine, pad);
+ release_source_pad (playbin, combine, pad);
done:
GST_PLAY_BIN3_UNLOCK (playbin);
static gint
select_stream_cb (GstElement * decodebin, GstStreamCollection * collection,
- GstStream * stream, GstSourceGroup * group)
+ GstStream * stream, GstPlayBin3 * playbin)
{
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;
}
/* 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",
gst_stream_type_get_name (combine->stream_type));
- /* If we are shutting down, do *not* add more combiners */
- if (g_atomic_int_get (&playbin->shutdown))
- continue;
-
g_assert (combine->sinkpad == NULL);
/* Request playsink sink pad */
}
static void
-about_to_finish_cb (GstElement * uridecodebin, GstSourceGroup * group)
+about_to_finish_cb (GstElement * uridecodebin, GstPlayBin3 * playbin)
{
- GstPlayBin3 *playbin = group->playbin;
- GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
+ GST_DEBUG_OBJECT (playbin, "about to finish");
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));
-
- 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);
-}
-
-static GstBusSyncReply
-activate_sink_bus_handler (GstBus * bus, GstMessage * msg,
- GstPlayBin3 * playbin)
-{
- if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
- /* Only proxy errors from a fixed sink. If that fails we can just error out
- * early as stuff will fail later anyway */
- if (playbin->audio_sink
- && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
- GST_OBJECT_CAST (playbin->audio_sink)))
- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
- else if (playbin->video_sink
- && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
- GST_OBJECT_CAST (playbin->video_sink)))
- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
- else if (playbin->text_sink
- && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
- GST_OBJECT_CAST (playbin->text_sink)))
- 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_element_set_context (GST_ELEMENT_CAST (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);
- }
-
- /* Doesn't really matter, nothing is using this bus */
- return GST_BUS_DROP;
-}
-
-static gboolean
-activate_sink (GstPlayBin3 * playbin, GstElement * sink, gboolean * activated)
-{
- GstState state;
- GstBus *bus = NULL;
- GstStateChangeReturn sret;
- gboolean ret = FALSE;
-
- if (activated)
- *activated = FALSE;
-
- GST_OBJECT_LOCK (sink);
- state = GST_STATE (sink);
- GST_OBJECT_UNLOCK (sink);
- if (state >= GST_STATE_READY) {
- ret = TRUE;
- goto done;
- }
-
- if (!GST_OBJECT_PARENT (sink)) {
- bus = gst_bus_new ();
- gst_bus_set_sync_handler (bus,
- (GstBusSyncHandler) activate_sink_bus_handler, playbin, NULL);
- gst_element_set_bus (sink, bus);
- }
-
- sret = gst_element_set_state (sink, GST_STATE_READY);
- if (sret == GST_STATE_CHANGE_FAILURE)
- goto done;
-
- if (activated)
- *activated = TRUE;
- ret = TRUE;
-
-done:
- if (bus) {
- gst_element_set_bus (sink, NULL);
- gst_object_unref (bus);
- }
-
- return ret;
-}
-
-/* must be called with the group lock */
-static gboolean
-group_set_locked_state_unlocked (GstPlayBin3 * playbin, GstSourceGroup * group,
- gboolean locked)
-{
- GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group);
-
- if (group->uridecodebin)
- gst_element_set_locked_state (group->uridecodebin, locked);
-
- return TRUE;
-}
+ STREAM_TYPES_ARGS (playbin->selected_stream_types));
-static gboolean
-make_or_reuse_element (GstPlayBin3 * playbin, const gchar * name,
- GstElement ** elem)
-{
- if (*elem) {
- GST_DEBUG_OBJECT (playbin, "reusing existing %s", name);
- gst_element_set_state (*elem, GST_STATE_READY);
- /* no need to take extra ref, we already have one
- * and the bin will add one since it is no longer floating,
- * as we added a non-floating ref when removing it from the
- * bin earlier */
- } else {
- GstElement *new_elem;
- GST_DEBUG_OBJECT (playbin, "making new %s", name);
- new_elem = gst_element_factory_make (name, NULL);
- if (!new_elem)
- return FALSE;
- *elem = gst_object_ref (new_elem);
- }
+ GST_DEBUG_OBJECT (playbin, "Emitting about-to-finish");
- if (GST_OBJECT_PARENT (*elem) != GST_OBJECT_CAST (playbin))
- gst_bin_add (GST_BIN_CAST (playbin), *elem);
- return TRUE;
+ /* 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);
}
-
static void
source_setup_cb (GstElement * element, GstElement * source,
- GstSourceGroup * group)
+ GstPlayBin3 * playbin)
{
- g_signal_emit (group->playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP], 0,
+ g_signal_emit (playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP], 0,
source);
}
-/* must be called with PLAY_BIN_LOCK */
-static GstStateChangeReturn
-activate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
-{
- GstElement *uridecodebin = NULL;
- GstPlayFlags flags;
- gboolean audio_sink_activated = FALSE;
- gboolean video_sink_activated = FALSE;
- gboolean text_sink_activated = FALSE;
- GstStateChangeReturn state_ret;
-
- g_return_val_if_fail (group->valid, GST_STATE_CHANGE_FAILURE);
- g_return_val_if_fail (!group->active, GST_STATE_CHANGE_FAILURE);
-
- GST_DEBUG_OBJECT (playbin, "activating group %p", group);
-
- GST_SOURCE_GROUP_LOCK (group);
-
- /* First set up the custom sinks */
- if (playbin->audio_sink)
- group->audio_sink = gst_object_ref (playbin->audio_sink);
- else
- group->audio_sink =
- gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO);
-
- if (group->audio_sink) {
- if (!activate_sink (playbin, group->audio_sink, &audio_sink_activated)) {
- if (group->audio_sink == playbin->audio_sink) {
- goto sink_failure;
- } else {
- gst_object_unref (group->audio_sink);
- group->audio_sink = NULL;
- }
- }
- }
-
- if (playbin->video_sink)
- group->video_sink = gst_object_ref (playbin->video_sink);
- else
- group->video_sink =
- gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO);
-
- if (group->video_sink) {
- if (!activate_sink (playbin, group->video_sink, &video_sink_activated)) {
- if (group->video_sink == playbin->video_sink) {
- goto sink_failure;
- } else {
- gst_object_unref (group->video_sink);
- group->video_sink = NULL;
- }
- }
- }
-
- if (playbin->text_sink)
- group->text_sink = gst_object_ref (playbin->text_sink);
- else
- group->text_sink =
- gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT);
-
- if (group->text_sink) {
- if (!activate_sink (playbin, group->text_sink, &text_sink_activated)) {
- if (group->text_sink == playbin->text_sink) {
- goto sink_failure;
- } else {
- gst_object_unref (group->text_sink);
- group->text_sink = NULL;
- }
- }
- }
-
-
- 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 (uridecodebin,
- /* configure connection speed */
- "connection-speed", playbin->connection_speed / 1000,
- /* configure uri */
- "uri", group->uri,
- /* configure download buffering */
- "download", ((flags & GST_PLAY_FLAG_DOWNLOAD) != 0),
- /* configure buffering of demuxed/parsed data */
- "use-buffering", ((flags & GST_PLAY_FLAG_BUFFERING) != 0),
- /* configure buffering parameters */
- "buffer-duration", playbin->buffer_duration,
- "buffer-size", playbin->buffer_size,
- "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
-
- 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 ((state_ret =
- 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 */
- group_set_locked_state_unlocked (playbin, group, FALSE);
- group->active = TRUE;
- GST_SOURCE_GROUP_UNLOCK (group);
-
- return state_ret;
-
- /* ERRORS */
-no_uridecodebin:
- {
- GstMessage *msg;
-
- GST_SOURCE_GROUP_UNLOCK (group);
- msg =
- gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
- "uridecodebin3");
- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
-
- GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
- (_("Could not create \"uridecodebin3\" element.")), (NULL));
-
- GST_SOURCE_GROUP_LOCK (group);
-
- goto error_cleanup;
- }
-uridecodebin_failure:
- {
- GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin");
- GST_SOURCE_GROUP_LOCK (group);
- goto error_cleanup;
- }
-sink_failure:
- {
- GST_ERROR_OBJECT (playbin, "failed to activate sinks");
- 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 */
- if (audio_sink_activated)
- gst_element_set_state (group->audio_sink, GST_STATE_NULL);
- gst_object_unref (group->audio_sink);
- }
- group->audio_sink = NULL;
-
- if (group->video_sink) {
- /* If this is a automatically created sink set it to NULL */
- if (video_sink_activated)
- gst_element_set_state (group->video_sink, GST_STATE_NULL);
- gst_object_unref (group->video_sink);
- }
- group->video_sink = NULL;
-
- if (group->text_sink) {
- /* If this is a automatically created sink set it to NULL */
- if (text_sink_activated)
- gst_element_set_state (group->text_sink, GST_STATE_NULL);
- gst_object_unref (group->text_sink);
- }
- group->text_sink = NULL;
-
- 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);
-
- gst_element_set_state (uridecodebin, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (playbin), uridecodebin);
- }
-
- GST_SOURCE_GROUP_UNLOCK (group);
-
- return GST_STATE_CHANGE_FAILURE;
- }
-}
-
-/* must be called with PLAY_BIN_LOCK, which is dropped temporarily
- * if changing states */
-static gboolean
-deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
-{
- g_return_val_if_fail (group->active, FALSE);
- g_return_val_if_fail (group->valid, FALSE);
-
- GST_DEBUG_OBJECT (playbin, "unlinking group %p", group);
-
- GST_SOURCE_GROUP_LOCK (group);
- group->active = FALSE;
- group->playing = FALSE;
- group->group_id = GST_GROUP_ID_INVALID;
-
- 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);
-
- 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_PLAY_BIN3_UNLOCK (playbin);
- gst_element_set_state (group->uridecodebin, GST_STATE_NULL);
- gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
- GST_PLAY_BIN3_LOCK (playbin);
-
- REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
- REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
- }
-
- GST_SOURCE_GROUP_UNLOCK (group);
-
- GST_DEBUG_OBJECT (playbin, "Done");
-
- return TRUE;
-}
-
-/* setup the next group to play, this assumes the next_group is valid and
- * configured. It swaps out the current_group and activates the valid
- * next_group. */
-static GstStateChangeReturn
-setup_next_source (GstPlayBin3 * playbin)
-{
- GstSourceGroup *new_group;
- GstStateChangeReturn state_ret;
-
- 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 || new_group->active)
- goto no_next_group;
-
- /* activate the new group */
- 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");
- GST_PLAY_BIN3_UNLOCK (playbin);
- return GST_STATE_CHANGE_FAILURE;
- }
-activate_failed:
- {
- new_group->stream_changed_pending = FALSE;
- GST_DEBUG_OBJECT (playbin, "activate failed");
- new_group->valid = FALSE;
- GST_PLAY_BIN3_UNLOCK (playbin);
- return GST_STATE_CHANGE_FAILURE;
- }
-}
-
-/* The group that is currently playing is copied again to the
- * next_group so that it will start playing the next time.
- */
-static gboolean
-save_current_group (GstPlayBin3 * playbin)
-{
- GstSourceGroup *curr_group;
- gboolean swapped = FALSE;
-
- GST_DEBUG_OBJECT (playbin, "save current group");
-
- /* see if there is a current group */
- GST_PLAY_BIN3_LOCK (playbin);
- curr_group = playbin->curr_group;
- if (curr_group && curr_group->valid && curr_group->active) {
- swapped = TRUE;
- }
- /* swap old and new */
- playbin->curr_group = playbin->next_group;
- playbin->next_group = curr_group;
-
- if (swapped) {
- /* unlink our pads with the sink */
- deactivate_group (playbin, curr_group);
- }
- GST_PLAY_BIN3_UNLOCK (playbin);
-
- return TRUE;
-}
-
-/* clear the locked state from all groups. This function is called before a
- * state change to NULL is performed on them. */
-static gboolean
-groups_set_locked_state (GstPlayBin3 * playbin, gboolean locked)
-{
- GST_DEBUG_OBJECT (playbin, "setting locked state to %d on all groups",
- locked);
-
- GST_PLAY_BIN3_LOCK (playbin);
- GST_SOURCE_GROUP_LOCK (playbin->curr_group);
- group_set_locked_state_unlocked (playbin, playbin->curr_group, locked);
- GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
- GST_SOURCE_GROUP_LOCK (playbin->next_group);
- group_set_locked_state_unlocked (playbin, playbin->next_group, locked);
- GST_SOURCE_GROUP_UNLOCK (playbin->next_group);
- GST_PLAY_BIN3_UNLOCK (playbin);
-
- 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);
+ playbin->active_stream_types = 0;
+ playbin->selected_stream_types = 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
{
GstStateChangeReturn ret;
GstPlayBin3 *playbin;
- gboolean do_save = FALSE;
playbin = GST_PLAY_BIN3 (element);
if (!gst_play_bin3_start (playbin))
return GST_STATE_CHANGE_FAILURE;
break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- async_down:
- 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 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;
- }
-
- /* unlock so that all groups go to NULL */
- groups_set_locked_state (playbin, FALSE);
- break;
default:
break;
}
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
- if ((ret = setup_next_source (playbin)) == GST_STATE_CHANGE_FAILURE)
- goto failure;
if (ret == GST_STATE_CHANGE_SUCCESS)
ret = GST_STATE_CHANGE_ASYNC;
-
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
do_async_done (playbin);
- /* FIXME Release audio device when we implement that */
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
playbin->is_live = FALSE;
- save_current_group (playbin);
+ /* Make sure we reset our state */
+ if (playbin->selected_stream_types) {
+ playbin->selected_stream_types = 0;
+ reconfigure_output (playbin);
+ }
break;
case GST_STATE_CHANGE_READY_TO_NULL:
{
- guint i;
-
- /* also do missed state change down to READY */
- if (do_save)
- save_current_group (playbin);
- /* Deactivate the groups, set uridecodebin to NULL and unref it */
- GST_PLAY_BIN3_LOCK (playbin);
- 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].uridecodebin) {
- gst_element_set_state (playbin->groups[i].uridecodebin,
- GST_STATE_NULL);
- gst_object_unref (playbin->groups[i].uridecodebin);
- playbin->groups[i].uridecodebin = NULL;
- }
-
- }
- GST_PLAY_BIN3_UNLOCK (playbin);
-
/* Set our sinks back to NULL, they might not be child of playbin */
if (playbin->audio_sink)
gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
if (playbin->text_stream_combiner)
gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
- /* make sure the groups don't perform a state change anymore until we
- * enable them again */
- groups_set_locked_state (playbin, TRUE);
+ gst_object_replace ((GstObject **) & playbin->collection, NULL);
+ gst_object_replace ((GstObject **) & playbin->collection_source, NULL);
+
break;
}
default:
failure:
{
do_async_done (playbin);
-
- if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
- GstSourceGroup *curr_group;
-
- GST_PLAY_BIN3_LOCK (playbin);
-
- curr_group = playbin->curr_group;
- if (curr_group) {
- if (curr_group->active && curr_group->valid) {
- /* unlink our pads with the sink */
- deactivate_group (playbin, curr_group);
- }
- curr_group->valid = FALSE;
- }
-
- /* Swap current and next group back */
- playbin->curr_group = playbin->next_group;
- playbin->next_group = curr_group;
-
- GST_PLAY_BIN3_UNLOCK (playbin);
- }
return ret;
}
}
* 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
*/
/**
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 _GstSourcePad GstSourcePad;
typedef struct _OutputPad OutputPad;
/* A structure describing a play item, which travels through the elements
- * over time. */
+ * over time.
+ *
+ * All source items in this play item will be played together. Corresponds to an
+ * end-user "play item" (ex: one item from a playlist, even though it might be
+ * using a main content and subtitle content).
+ */
struct _GstPlayItem
{
GstURIDecodeBin3 *uridecodebin;
* 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;
+ /* The two following variables are required for gapless, since there could be
+ * a play item which is started which is different from the one currently
+ * being outputted */
+
+ /* active: TRUE if the backing urisourcebin were created */
+ gboolean active;
+
+ /* Whether about-to-finish was already posted for this play item */
+ gboolean posted_about_to_finish;
+
+ /* Whether about-to-finish should be posted once this play item becomes the
+ * current input item */
+ gboolean pending_about_to_finish;
};
+/* The actual "source" component of a "play item"
+ *
+ * This is defined by having a URI, is backed by a `GstSourceHandler`.
+ */
struct _GstSourceItem
{
/* The GstPlayItem to which this GstSourceItem belongs to */
/* The urisourcebin controlling this uri
* Can be NULL */
GstSourceHandler *handler;
-
- /* 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;
+ GstPlayItem *play_item;
GstElement *urisourcebin;
/* 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;
+
+ /* Number of expected sourcepads. Default 1, else it's the number of streams
+ * specified by GST_MESSAGE_SELECTED_STREAMS from the source */
+ guint expected_pads;
+
+ /* List of GstSourcePad */
+ GList *sourcepads;
+};
+
+/* Structure wrapping everything related to a urisourcebin pad */
+struct _GstSourcePad
+{
+ GstSourceHandler *handler;
+
+ GstPad *src_pad;
+
+ /* GstStream (if present) */
+ GstStream *stream;
+
+ /* Decodebin3 pad to which src_pad is linked to */
+ GstPad *db3_sink_pad;
+
+ /* TRUE if db3_sink_pad is a request pad */
+ gboolean db3_pad_is_request;
+
+ /* TRUE if EOS went through the source pad. Marked as TRUE if decodebin3
+ * notified `about-to-finish` for pull mode */
+ gboolean saw_eos;
+
+ /* Downstream blocking probe id. Only set/valid if we need to block this
+ * pad */
+ gulong block_probe_id;
+
+ /* Downstream event probe id */
+ gulong event_probe_id;
};
/* Controls an output source pad */
/* Downstream event probe id */
gulong probe_id;
- /* TRUE if the pad saw EOS. Reset 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;
};
+#define PLAY_ITEMS_GET_LOCK(d) (&(GST_URI_DECODE_BIN3_CAST(d)->play_items_lock))
+#define PLAY_ITEMS_LOCK(d) G_STMT_START { \
+ GST_TRACE("Locking play_items from thread %p", g_thread_self()); \
+ g_mutex_lock (PLAY_ITEMS_GET_LOCK (d)); \
+ GST_TRACE("Locked play_items from thread %p", g_thread_self()); \
+ } G_STMT_END
+
+#define PLAY_ITEMS_UNLOCK(d) G_STMT_START { \
+ GST_TRACE("Unlocking play_items from thread %p", g_thread_self()); \
+ g_mutex_unlock (PLAY_ITEMS_GET_LOCK (d)); \
+ } G_STMT_END
+
/**
* GstURIDecodeBin3
*
{
GstBin parent_instance;
- GMutex lock; /* lock for constructing */
-
/* Properties */
GstElement *source;
guint64 connection_speed; /* In bits/sec (0 = unknown) */
gboolean download;
gboolean use_buffering;
guint64 ring_buffer_max_size;
+ gboolean instant_uri; /* Whether URI changes should be applied immediately or not */
- 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 */
+ /* Mutex to protect play_items/input_item/output_item */
+ GMutex play_items_lock;
+
+ /* Notify that the input_item sources have all drained */
+ GCond input_source_drained;
+
+ /* List of GstPlayItem ordered by time of creation (first is oldest, new ones
+ * are appended) */
+ GList *play_items;
+
+ /* Play item currently feeding decodebin3. */
+ GstPlayItem *input_item;
+
+ /* Play item currently outputted by decodebin3 */
+ GstPlayItem *output_item;
/* A global decodebin3 that's used to actually do decoding */
GstElement *decodebin;
gulong db_select_stream_id;
gulong db_about_to_finish_id;
- GList *output_pads; /* List of OutputPad */
-
- GList *source_handlers; /* List of SourceHandler */
+ /* 1 if shutting down */
+ gint shutdown;
- /* Whether we already signalled about-to-finish or not
- * FIXME: Track this by group-id ! */
- gboolean posted_about_to_finish;
+ GList *output_pads; /* List of OutputPad */
};
+static GstStateChangeReturn activate_play_item (GstPlayItem * item);
+
static gint
gst_uridecodebin3_select_stream (GstURIDecodeBin3 * dbin,
GstStreamCollection * collection, GstStream * stream)
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_DOWNLOAD FALSE
#define DEFAULT_USE_BUFFERING FALSE
#define DEFAULT_RING_BUFFER_MAX_SIZE 0
+#define DEFAULT_INSTANT_URI FALSE
enum
{
PROP_DOWNLOAD,
PROP_USE_BUFFERING,
PROP_RING_BUFFER_MAX_SIZE,
- PROP_CAPS
+ PROP_CAPS,
+ PROP_INSTANT_URI
};
static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 };
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 void gst_uri_decode_bin3_dispose (GObject * obj);
static GstSourceHandler *new_source_handler (GstURIDecodeBin3 * uridecodebin,
- gboolean is_main);
+ GstPlayItem * item, gboolean is_main);
+static void free_source_item (GstURIDecodeBin3 * uridecodebin,
+ GstSourceItem * item);
+
+static GstPlayItem *new_play_item (GstURIDecodeBin3 * dec);
+static void free_play_item (GstURIDecodeBin3 * dec, GstPlayItem * item);
+static gboolean play_item_is_eos (GstPlayItem * item);
+static void play_item_set_eos (GstPlayItem * item);
+static gboolean play_item_has_all_pads (GstPlayItem * item);
+
+static void gst_uri_decode_bin3_set_uri (GstURIDecodeBin3 * dec,
+ const gchar * uri);
+static void gst_uri_decode_bin3_set_suburi (GstURIDecodeBin3 * dec,
+ const gchar * uri);
static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_uri_decodebin3_send_event (GstElement * element,
GstEvent * event);
+static void gst_uri_decode_bin3_handle_message (GstBin * bin, GstMessage * msg);
static gboolean
_gst_int_accumulator (GSignalInvocationHint * ihint,
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
+ GstBinClass *gstbin_class;
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
+ gstbin_class = GST_BIN_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;
+ gobject_class->dispose = gst_uri_decode_bin3_dispose;
g_object_class_install_property (gobject_class, PROP_URI,
g_param_spec_string ("uri", "URI", "URI to decode",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
+ * GstURIDecodeBin3:instant-uri:
+ *
+ * Changes to uri are applied immediately (instead of on EOS or when the
+ * element is set back to PLAYING.
+ *
+ * Since: 1.22
+ */
+ g_object_class_install_property (gobject_class, PROP_INSTANT_URI,
+ g_param_spec_boolean ("instant-uri", "Instantaneous URI change",
+ "When enabled, URI changes are applied immediately",
+ DEFAULT_INSTANT_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
* GstURIDecodebin3::select-stream
* @decodebin: a #GstURIDecodebin3
* @collection: a #GstStreamCollection
gstelement_class->send_event =
GST_DEBUG_FUNCPTR (gst_uri_decodebin3_send_event);
+ gstbin_class->handle_message = gst_uri_decode_bin3_handle_message;
+
klass->select_stream = gst_uridecodebin3_select_stream;
}
+static void
+check_output_group_id (GstURIDecodeBin3 * dec)
+{
+ GList *iter;
+ guint common_group_id = GST_GROUP_ID_INVALID;
+
+ PLAY_ITEMS_LOCK (dec);
+
+ for (iter = dec->output_pads; iter; iter = iter->next) {
+ OutputPad *pad = iter->data;
+
+ if (common_group_id == GST_GROUP_ID_INVALID)
+ common_group_id = pad->current_group_id;
+ else if (common_group_id != pad->current_group_id) {
+ GST_DEBUG_OBJECT (dec, "transitioning output play item");
+ PLAY_ITEMS_UNLOCK (dec);
+ return;
+ }
+ }
+
+ if (dec->output_item->group_id == common_group_id) {
+ GST_DEBUG_OBJECT (dec, "Output play item %d fully active", common_group_id);
+ } else if (dec->output_item->group_id == GST_GROUP_ID_INVALID) {
+ /* This can happen for pull-based situations */
+ GST_DEBUG_OBJECT (dec, "Assigning group id %u to current output play item",
+ common_group_id);
+ dec->output_item->group_id = common_group_id;
+ } else if (common_group_id != GST_GROUP_ID_INVALID &&
+ dec->output_item->group_id != common_group_id) {
+ GstPlayItem *previous_item = dec->output_item;
+ GST_DEBUG_OBJECT (dec, "Output play item %d fully active", common_group_id);
+ if (g_list_length (dec->play_items) > 1) {
+ dec->play_items = g_list_remove (dec->play_items, previous_item);
+ dec->output_item = dec->play_items->data;
+ dec->output_item->group_id = common_group_id;
+ free_play_item (dec, previous_item);
+ }
+ }
+
+ PLAY_ITEMS_UNLOCK (dec);
+}
+
static GstPadProbeReturn
db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output)
{
- /* FIXME : IMPLEMENT */
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+ GstURIDecodeBin3 *uridecodebin = output->uridecodebin;
+
+ GST_DEBUG_OBJECT (pad, "event %" GST_PTR_FORMAT, event);
/* EOS : Mark pad as EOS */
- /* STREAM_START : Store group_id and check if currently active
- * PlayEntry changed */
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ {
+ /* If there is a next input, drop the EOS event */
+ if (uridecodebin->input_item != uridecodebin->output_item ||
+ uridecodebin->input_item !=
+ g_list_last (uridecodebin->play_items)->data) {
+ GST_DEBUG_OBJECT (uridecodebin,
+ "Dropping EOS event because in gapless mode");
+ return GST_PAD_PROBE_DROP;
+ }
+ break;
+ }
+ case GST_EVENT_STREAM_START:
+ {
+ /* STREAM_START : Store group_id and check if currently active
+ * PlayEntry changed */
+ if (gst_event_parse_group_id (event, &output->current_group_id)) {
+ GST_DEBUG_OBJECT (pad, "current group id %" G_GUINT32_FORMAT,
+ output->current_group_id);
+ /* Check if we switched over to a new output */
+ check_output_group_id (uridecodebin);
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
return GST_PAD_PROBE_OK;
}
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);
+
+ check_output_group_id (dec);
}
}
return response;
}
+static gboolean
+check_pad_mode (GstElement * src, GstPad * pad, gpointer udata)
+{
+ GstPadMode curmode = GST_PAD_MODE (pad);
+ GstPadMode *retmode = (GstPadMode *) udata;
+
+ /* We don't care if pads aren't activated */
+ if (curmode == GST_PAD_MODE_NONE)
+ return TRUE;
+
+ if (*retmode == GST_PAD_MODE_NONE) {
+ *retmode = curmode;
+ } else if (*retmode != curmode) {
+ GST_ERROR_OBJECT (src, "source has different scheduling mode ?");
+ }
+
+ return TRUE;
+}
+
+static gboolean
+play_item_is_pull_based (GstPlayItem * item)
+{
+ GstElement *src;
+ GstPadMode mode = GST_PAD_MODE_NONE;
+
+ g_assert (item->main_item && item->main_item->handler
+ && item->main_item->handler->urisourcebin);
+
+ src = item->main_item->handler->urisourcebin;
+ gst_element_foreach_src_pad (src, check_pad_mode, &mode);
+
+ return (mode == GST_PAD_MODE_PULL);
+}
+
static void
-db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin)
+emit_and_handle_about_to_finish (GstURIDecodeBin3 * uridecodebin,
+ GstPlayItem * item)
{
- 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);
+ GST_DEBUG_OBJECT (uridecodebin, "output %d , posted_about_to_finish:%d",
+ item->group_id, item->posted_about_to_finish);
+
+ if (item->posted_about_to_finish) {
+ GST_DEBUG_OBJECT (uridecodebin,
+ "already handling about-to-finish for this play item");
+ return;
+ }
+
+ if (item != uridecodebin->input_item) {
+ GST_DEBUG_OBJECT (uridecodebin, "Postponing about-to-finish propagation");
+ item->pending_about_to_finish = TRUE;
+ return;
}
+
+ /* If the input entry is pull-based, mark all the source pads as EOS */
+ if (play_item_is_pull_based (item)) {
+ GST_DEBUG_OBJECT (uridecodebin, "Marking play item as EOS");
+ play_item_set_eos (item);
+ }
+
+ item->posted_about_to_finish = TRUE;
+ GST_DEBUG_OBJECT (uridecodebin, "Posting about-to-finish");
+ g_signal_emit (uridecodebin,
+ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
+
+ /* Note : Activation of the (potential) next entry is handled in
+ * gst_uri_decode_bin3_set_uri */
+}
+
+static void
+db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin)
+{
+ GST_LOG_OBJECT (uridecodebin, "about to finish from %s",
+ GST_OBJECT_NAME (decodebin));
+
+ emit_and_handle_about_to_finish (uridecodebin, uridecodebin->output_item);
}
static void
gst_uri_decode_bin3_init (GstURIDecodeBin3 * dec)
{
- g_mutex_init (&dec->lock);
+ GstPlayItem *item;
- dec->uri = DEFAULT_PROP_URI;
- dec->suburi = DEFAULT_PROP_SUBURI;
dec->connection_speed = DEFAULT_CONNECTION_SPEED;
dec->caps = DEFAULT_CAPS;
dec->buffer_duration = DEFAULT_BUFFER_DURATION;
dec->use_buffering = DEFAULT_USE_BUFFERING;
dec->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
+ g_mutex_init (&dec->play_items_lock);
+ g_cond_init (&dec->input_source_drained);
+
dec->decodebin = gst_element_factory_make ("decodebin3", NULL);
gst_bin_add (GST_BIN_CAST (dec), dec->decodebin);
dec->db_pad_added_id =
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);
+
+ item = new_play_item (dec);
+ dec->play_items = g_list_append (dec->play_items, item);
+ /* The initial play item is automatically the input and output one */
+ dec->input_item = dec->output_item = item;
}
static void
-gst_uri_decode_bin3_finalize (GObject * obj)
+gst_uri_decode_bin3_dispose (GObject * obj)
{
GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (obj);
+ GList *iter;
- g_mutex_clear (&dec->lock);
- g_free (dec->uri);
- g_free (dec->suburi);
+ GST_DEBUG_OBJECT (obj, "Disposing");
- G_OBJECT_CLASS (parent_class)->finalize (obj);
+ /* Free all play items */
+ for (iter = dec->play_items; iter; iter = iter->next) {
+ GstPlayItem *item = iter->data;
+ free_play_item (dec, item);
+ }
+ g_list_free (dec->play_items);
+ dec->play_items = NULL;
+
+ g_mutex_clear (&dec->play_items_lock);
+
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
}
static GstStateChangeReturn
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;
}
+ gst_element_sync_state_with_parent (handler->urisourcebin);
+
return GST_STATE_CHANGE_SUCCESS;
}
static void
-src_pad_added_cb (GstElement * element, GstPad * pad,
- GstSourceHandler * handler)
+link_src_pad_to_db3 (GstURIDecodeBin3 * uridecodebin, GstSourcePad * spad)
{
- GstURIDecodeBin3 *uridecodebin;
+ GstSourceHandler *handler = spad->handler;
GstPad *sinkpad = NULL;
- GstPadLinkReturn res;
- GstPlayItem *current_play_item;
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
-
- uridecodebin = handler->uridecodebin;
- current_play_item = uridecodebin->current;
-
- 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) {
}
}
- if (sinkpad == NULL)
+ if (sinkpad == NULL) {
sinkpad =
gst_element_request_pad_simple (uridecodebin->decodebin, "sink_%u");
+ spad->db3_pad_is_request = TRUE;
+ }
if (sinkpad) {
+ GstPadLinkReturn res;
GST_DEBUG_OBJECT (uridecodebin,
- "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, pad, sinkpad);
- res = gst_pad_link (pad, sinkpad);
+ "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, spad->src_pad,
+ sinkpad);
+ res = gst_pad_link (spad->src_pad, sinkpad);
gst_object_unref (sinkpad);
- if (GST_PAD_LINK_FAILED (res))
- goto link_failed;
+ if (GST_PAD_LINK_FAILED (res)) {
+ GST_ERROR_OBJECT (uridecodebin,
+ "failed to link pad %s:%s to decodebin, reason %s (%d)",
+ GST_DEBUG_PAD_NAME (spad->src_pad), gst_pad_link_get_name (res), res);
+ return;
+ }
+ } else {
+ GST_ERROR_OBJECT (uridecodebin, "Could not get a sinkpad from decodebin3");
+ return;
}
+ spad->db3_sink_pad = sinkpad;
+
/* Activate sub_item after the main source activation was finished */
- if (handler->is_main_source && current_play_item->sub_item
- && !current_play_item->sub_item->handler) {
- current_play_item->sub_item->handler =
- new_source_handler (uridecodebin, FALSE);
- ret = activate_source_item (current_play_item->sub_item);
+ if (handler->is_main_source && handler->play_item->sub_item
+ && !handler->play_item->sub_item->handler) {
+ GstStateChangeReturn ret;
+ handler->play_item->sub_item->handler =
+ new_source_handler (uridecodebin, handler->play_item, FALSE);
+ ret = activate_source_item (handler->play_item->sub_item);
if (ret == GST_STATE_CHANGE_FAILURE)
goto sub_item_activation_failed;
}
return;
-link_failed:
+sub_item_activation_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);
+ "failed to activate subtitle playback item");
return;
}
-sub_item_activation_failed:
+}
+
+static GList *
+get_all_play_item_source_pads (GstPlayItem * item)
+{
+ GList *ret = NULL;
+
+ if (item->main_item && item->main_item->handler) {
+ ret = g_list_copy (item->main_item->handler->sourcepads);
+ }
+
+ if (item->sub_item && item->sub_item->handler) {
+ ret =
+ g_list_concat (ret, g_list_copy (item->sub_item->handler->sourcepads));
+ }
+
+ return ret;
+}
+
+static GstSourcePad *
+find_matching_source_pad (GList * candidates, GstSourcePad * target)
+{
+ GList *iter;
+ GstStream *stream = target->stream;
+
+ GST_DEBUG_OBJECT (target->src_pad, "Find match for stream %" GST_PTR_FORMAT,
+ stream);
+
+ for (iter = candidates; iter; iter = iter->next) {
+ GstSourcePad *cand = iter->data;
+
+ if (!cand->db3_sink_pad)
+ continue;
+
+ /* Target doesn't have a specific GstStream, return the first result */
+ if (!stream)
+ return cand;
+
+ if (gst_stream_get_stream_type (cand->stream) ==
+ gst_stream_get_stream_type (stream))
+ return cand;
+ }
+
+ return NULL;
+}
+
+/* PLAY_ITEMS_LOCK held
+ *
+ * Switch the input play item to the next one
+ */
+static void
+switch_and_activate_input_locked (GstURIDecodeBin3 * uridecodebin,
+ GstPlayItem * new_item)
+{
+ GList *new_pads = get_all_play_item_source_pads (new_item);
+ GList *old_pads = get_all_play_item_source_pads (uridecodebin->input_item);
+ GList *to_activate = NULL;
+ GList *iternew, *iterold;
+
+ /* Deactivate old urisourcebins first ? Problem is they might remove the pads */
+
+ /* Go over new item source pads and figure out a candidate replacement in */
+ /* Figure out source pad matches */
+ for (iternew = new_pads; iternew; iternew = iternew->next) {
+ GstSourcePad *new_spad = iternew->data;
+ GstSourcePad *old_spad = find_matching_source_pad (old_pads, new_spad);
+
+ if (old_spad) {
+ GST_DEBUG_OBJECT (uridecodebin, "Relinking %s:%s from %s:%s to %s:%s",
+ GST_DEBUG_PAD_NAME (old_spad->db3_sink_pad),
+ GST_DEBUG_PAD_NAME (old_spad->src_pad),
+ GST_DEBUG_PAD_NAME (new_spad->src_pad));
+ gst_pad_unlink (old_spad->src_pad, old_spad->db3_sink_pad);
+ new_spad->db3_sink_pad = old_spad->db3_sink_pad;
+ new_spad->db3_pad_is_request = old_spad->db3_pad_is_request;
+ old_spad->db3_sink_pad = NULL;
+
+ gst_pad_link (new_spad->src_pad, new_spad->db3_sink_pad);
+ old_pads = g_list_remove (old_pads, old_spad);
+ } else {
+ GST_DEBUG_OBJECT (new_spad->src_pad, "Needs a new pad");
+ to_activate = g_list_append (to_activate, new_spad);
+ }
+ }
+
+ /* Remove unmatched old source pads */
+ for (iterold = old_pads; iterold; iterold = iterold->next) {
+ GstSourcePad *old_spad = iterold->data;
+ if (old_spad->db3_sink_pad && old_spad->db3_pad_is_request) {
+ GST_DEBUG_OBJECT (uridecodebin, "Releasing no longer used db3 pad");
+ gst_element_release_request_pad (uridecodebin->decodebin,
+ old_spad->db3_sink_pad);
+ old_spad->db3_sink_pad = NULL;
+ }
+ }
+
+ /* Link new source pads */
+ for (iternew = to_activate; iternew; iternew = iternew->next) {
+ GstSourcePad *new_spad = iternew->data;
+ link_src_pad_to_db3 (uridecodebin, new_spad);
+ }
+
+ /* Unblock all new item source pads */
+ for (iternew = new_pads; iternew; iternew = iternew->next) {
+ GstSourcePad *new_spad = iternew->data;
+ if (new_spad->block_probe_id) {
+ gst_pad_remove_probe (new_spad->src_pad, new_spad->block_probe_id);
+ new_spad->block_probe_id = 0;
+ }
+ }
+ g_list_free (new_pads);
+ g_list_free (old_pads);
+
+ /* Deactivate old input item (by removing the source components). The final
+ * removal of this play item will be done once decodebin3 starts output the
+ * content of the new play item. */
+ if (uridecodebin->input_item->main_item) {
+ free_source_item (uridecodebin, uridecodebin->input_item->main_item);
+ uridecodebin->input_item->main_item = NULL;
+ }
+ if (uridecodebin->input_item->sub_item) {
+ free_source_item (uridecodebin, uridecodebin->input_item->sub_item);
+ uridecodebin->input_item->sub_item = NULL;
+ }
+
+ /* and set new one as input item */
+ uridecodebin->input_item = new_item;
+
+ if (new_item->main_item->handler->pending_buffering_msg) {
+ GstMessage *msg = new_item->main_item->handler->pending_buffering_msg;
+ new_item->main_item->handler->pending_buffering_msg = NULL;
+ PLAY_ITEMS_UNLOCK (uridecodebin);
+ GST_BIN_CLASS (parent_class)->handle_message ((GstBin *) uridecodebin, msg);
+ PLAY_ITEMS_LOCK (uridecodebin);
+ }
+}
+
+static GstPadProbeReturn
+uri_src_probe (GstPad * pad, GstPadProbeInfo * info, GstSourcePad * srcpad)
+{
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+ GstSourceHandler *handler = srcpad->handler;
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+
+ GST_DEBUG_OBJECT (pad, "event %" GST_PTR_FORMAT, event);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ {
+ GstPad *peer;
+ /* Propagate the EOS *before* triggering any potential switch */
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ gst_pad_send_event (peer, event);
+ gst_object_unref (peer);
+ }
+
+ PLAY_ITEMS_LOCK (handler->uridecodebin);
+ /* EOS : Mark pad as EOS */
+ srcpad->saw_eos = TRUE;
+ /* Check if the input play item is fully EOS. If yes and there is a
+ * pending play item, switch to it */
+ if (handler->play_item == handler->uridecodebin->input_item &&
+ play_item_is_eos (handler->play_item)) {
+ g_cond_signal (&handler->uridecodebin->input_source_drained);
+ }
+ PLAY_ITEMS_UNLOCK (handler->uridecodebin);
+ ret = GST_PAD_PROBE_HANDLED;
+ break;
+ }
+ case GST_EVENT_STREAM_START:
+ {
+ GstStream *stream = NULL;
+ srcpad->saw_eos = FALSE;
+ gst_event_parse_stream (event, &stream);
+ if (stream) {
+ GST_DEBUG_OBJECT (srcpad->src_pad, "Got GstStream %" GST_PTR_FORMAT,
+ stream);
+ if (srcpad->stream)
+ gst_object_unref (srcpad->stream);
+ srcpad->stream = stream;
+ }
+ break;
+ }
+ case GST_EVENT_SEGMENT:
+ {
+ srcpad->saw_eos = FALSE;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static GstPadProbeReturn
+uri_src_block_probe (GstPad * pad, GstPadProbeInfo * info,
+ GstSourcePad * srcpad)
+{
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+ GstSourceHandler *handler = srcpad->handler;
+ GST_DEBUG_OBJECT (pad, "blocked");
+
+ /* We only block on buffers, buffer list and gap events. Everything else is
+ * dropped (sticky events will be propagated later) */
+ if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info)) &&
+ GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_GAP) {
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+ if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START) {
+ GstStream *stream = NULL;
+ gst_event_parse_stream (event, &stream);
+ if (stream) {
+ GST_DEBUG_OBJECT (srcpad->src_pad, "Got GstStream %" GST_PTR_FORMAT,
+ stream);
+ if (srcpad->stream)
+ gst_object_unref (srcpad->stream);
+ srcpad->stream = stream;
+ }
+ }
+ GST_LOG_OBJECT (pad, "Skiping %" GST_PTR_FORMAT, event);
+ return GST_PAD_PROBE_DROP;
+ }
+
+ PLAY_ITEMS_LOCK (handler->uridecodebin);
+ if (play_item_is_eos (handler->uridecodebin->input_item)) {
+ GST_DEBUG_OBJECT (handler->uridecodebin,
+ "We can switch over to the next input item");
+ switch_and_activate_input_locked (handler->uridecodebin,
+ handler->play_item);
+ ret = GST_PAD_PROBE_REMOVE;
+ } else if (play_item_has_all_pads (handler->play_item)) {
+ /* We have all expected pads for this play item but the current input
+ * play item isn't done yet, wait for it */
+ g_cond_wait (&handler->uridecodebin->input_source_drained,
+ &handler->uridecodebin->play_items_lock);
+ if (g_atomic_int_get (&handler->uridecodebin->shutdown))
+ goto shutdown;
+ if (play_item_is_eos (handler->uridecodebin->input_item)) {
+ GST_DEBUG_OBJECT (handler->uridecodebin,
+ "We can switch over to the next input item");
+ switch_and_activate_input_locked (handler->uridecodebin,
+ handler->play_item);
+ ret = GST_PAD_PROBE_REMOVE;
+ }
+ }
+
+ PLAY_ITEMS_UNLOCK (handler->uridecodebin);
+
+ return ret;
+
+ /* ERRORS */
+shutdown:
{
- GST_ERROR_OBJECT (uridecodebin,
- "failed to activate subtitle playback item");
+ GST_LOG_OBJECT (pad, "Shutting down");
+ PLAY_ITEMS_UNLOCK (handler->uridecodebin);
+ return GST_PAD_PROBE_REMOVE;
+ }
+}
+
+static void
+src_pad_added_cb (GstElement * element, GstPad * pad,
+ GstSourceHandler * handler)
+{
+ GstSourcePad *spad = g_slice_new0 (GstSourcePad);
+ GstURIDecodeBin3 *uridecodebin;
+
+ uridecodebin = handler->uridecodebin;
+
+ PLAY_ITEMS_LOCK (uridecodebin);
+
+ GST_DEBUG_OBJECT (uridecodebin,
+ "New pad %" GST_PTR_FORMAT " from source %" GST_PTR_FORMAT, pad, element);
+
+ /* Register the new pad information with the source handler */
+ spad->handler = handler;
+ spad->src_pad = pad;
+ spad->event_probe_id =
+ gst_pad_add_probe (pad,
+ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) uri_src_probe,
+ spad, NULL);
+
+ handler->sourcepads = g_list_append (handler->sourcepads, spad);
+
+ /* Can the pad be linked straight away to db3 ?
+ * This can happen if:
+ * * It is the initial play item
+ * * It is part of the current input item
+ */
+ if (handler->play_item == uridecodebin->input_item) {
+ GST_DEBUG_OBJECT (uridecodebin,
+ "Pad is part of current input item, linking");
+
+ link_src_pad_to_db3 (uridecodebin, spad);
+ PLAY_ITEMS_UNLOCK (uridecodebin);
return;
}
+
+ /* This pad is not from the current input item. We add a blocking probe to
+ * wait until we block on the new urisourcebin streaming thread and can
+ * switch */
+ GST_DEBUG_OBJECT (uridecodebin, "Blocking input pad");
+ spad->block_probe_id =
+ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ (GstPadProbeCallback) uri_src_block_probe, spad, NULL);
+ PLAY_ITEMS_UNLOCK (uridecodebin);
+}
+
+static GstSourcePad *
+handler_get_source_pad (GstSourceHandler * handler, GstPad * srcpad)
+{
+ GList *iter;
+
+ for (iter = handler->sourcepads; iter; iter = iter->next) {
+ GstSourcePad *spad = iter->data;
+ if (spad->src_pad == srcpad)
+ return spad;
+ }
+
+ return NULL;
}
static void
GstSourceHandler * handler)
{
GstURIDecodeBin3 *uridecodebin = handler->uridecodebin;
- GstPad *peer_pad = gst_pad_get_peer (pad);
+ GstSourcePad *spad = handler_get_source_pad (handler, pad);
- if (peer_pad) {
- GstPadTemplate *templ = gst_pad_get_pad_template (peer_pad);
+ if (!spad)
+ return;
- GST_DEBUG_OBJECT (uridecodebin,
- "Source %" GST_PTR_FORMAT " removed pad %" GST_PTR_FORMAT " peer %"
- GST_PTR_FORMAT, element, pad, peer_pad);
+ GST_DEBUG_OBJECT (uridecodebin,
+ "Source %" GST_PTR_FORMAT " removed pad %" GST_PTR_FORMAT " peer %"
+ GST_PTR_FORMAT, element, pad, spad->db3_sink_pad);
- if (templ) {
- if (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST) {
- GST_DEBUG_OBJECT (uridecodebin,
- "Releasing decodebin pad %" GST_PTR_FORMAT, peer_pad);
- gst_element_release_request_pad (uridecodebin->decodebin, peer_pad);
- }
- gst_object_unref (templ);
- }
+ if (spad->db3_sink_pad && spad->db3_pad_is_request)
+ gst_element_release_request_pad (uridecodebin->decodebin,
+ spad->db3_sink_pad);
- gst_object_unref (peer_pad);
- }
+ handler->sourcepads = g_list_remove (handler->sourcepads, spad);
+ g_slice_free (GstSourcePad, spad);
}
static void
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);
- }
+ GST_LOG_OBJECT (handler->uridecodebin, "about to finish from %s",
+ GST_OBJECT_NAME (element));
+
+ emit_and_handle_about_to_finish (handler->uridecodebin, handler->play_item);
}
static GstSourceHandler *
-new_source_handler (GstURIDecodeBin3 * uridecodebin, gboolean is_main)
+new_source_handler (GstURIDecodeBin3 * uridecodebin, GstPlayItem * item,
+ gboolean is_main)
{
GstSourceHandler *handler;
handler = g_slice_new0 (GstSourceHandler);
handler->uridecodebin = uridecodebin;
+ handler->play_item = item;
handler->is_main_source = is_main;
handler->urisourcebin = gst_element_factory_make ("urisourcebin", NULL);
/* Set pending properties */
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);
+ handler->expected_pads = 1;
return handler;
}
+static gboolean
+source_handler_is_eos (GstSourceHandler * handler)
+{
+ GList *iter;
+
+ for (iter = handler->sourcepads; iter; iter = iter->next) {
+ GstSourcePad *spad = iter->data;
+
+ if (!spad->saw_eos)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+source_handler_set_eos (GstSourceHandler * handler)
+{
+ GList *iter;
+
+ for (iter = handler->sourcepads; iter; iter = iter->next) {
+ GstSourcePad *spad = iter->data;
+
+ spad->saw_eos = TRUE;
+ }
+}
+
static void
gst_uri_decode_bin3_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
switch (prop_id) {
case PROP_URI:
- if (dec->uri)
- g_free (dec->uri);
- dec->uri = g_value_dup_string (value);
+ gst_uri_decode_bin3_set_uri (dec, g_value_get_string (value));
break;
case PROP_SUBURI:
- if (dec->suburi)
- g_free (dec->suburi);
- dec->suburi = g_value_dup_string (value);
+ gst_uri_decode_bin3_set_suburi (dec, g_value_get_string (value));
break;
case PROP_CONNECTION_SPEED:
- GST_URI_DECODE_BIN3_LOCK (dec);
+ GST_OBJECT_LOCK (dec);
dec->connection_speed = g_value_get_uint64 (value) * 1000;
- GST_URI_DECODE_BIN3_UNLOCK (dec);
+ GST_OBJECT_UNLOCK (dec);
break;
case PROP_BUFFER_SIZE:
dec->buffer_size = g_value_get_int (value);
dec->caps = g_value_dup_boxed (value);
GST_OBJECT_UNLOCK (dec);
break;
+ case PROP_INSTANT_URI:
+ GST_OBJECT_LOCK (dec);
+ dec->instant_uri = g_value_get_boolean (value);
+ GST_OBJECT_UNLOCK (dec);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
switch (prop_id) {
case PROP_URI:
{
- g_value_set_string (value, dec->uri);
+ GstPlayItem *item = dec->play_items->data;
+ /* Return from the head */
+ if (item->main_item)
+ g_value_set_string (value, item->main_item->uri);
+ else
+ g_value_set_string (value, NULL);
break;
}
case PROP_CURRENT_URI:
{
- if (dec->current && dec->current->main_item) {
- g_value_set_string (value, dec->current->main_item->uri);
+ if (dec->output_item && dec->output_item->main_item) {
+ g_value_set_string (value, dec->output_item->main_item->uri);
} else {
g_value_set_string (value, NULL);
}
}
case PROP_SUBURI:
{
- g_value_set_string (value, dec->suburi);
+ GstPlayItem *item = dec->play_items->data;
+ /* Return from the head */
+ if (item->sub_item)
+ g_value_set_string (value, item->sub_item->uri);
+ else
+ g_value_set_string (value, NULL);
break;
}
case PROP_CURRENT_SUBURI:
{
- if (dec->current && dec->current->sub_item) {
- g_value_set_string (value, dec->current->sub_item->uri);
+ if (dec->output_item && dec->output_item->sub_item) {
+ g_value_set_string (value, dec->output_item->sub_item->uri);
} else {
g_value_set_string (value, NULL);
}
break;
}
case PROP_CONNECTION_SPEED:
- GST_URI_DECODE_BIN3_LOCK (dec);
+ GST_OBJECT_LOCK (dec);
g_value_set_uint64 (value, dec->connection_speed / 1000);
- GST_URI_DECODE_BIN3_UNLOCK (dec);
+ GST_OBJECT_UNLOCK (dec);
break;
case PROP_BUFFER_SIZE:
GST_OBJECT_LOCK (dec);
g_value_set_boxed (value, dec->caps);
GST_OBJECT_UNLOCK (dec);
break;
+ case PROP_INSTANT_URI:
+ GST_OBJECT_LOCK (dec);
+ g_value_set_boolean (value, dec->instant_uri);
+ GST_OBJECT_UNLOCK (dec);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
{
GST_LOG_OBJECT (uridecodebin, "source handler %p", handler);
if (handler->active) {
+ GList *iter;
+ GST_STATE_LOCK (uridecodebin);
GST_LOG_OBJECT (uridecodebin, "Removing %" GST_PTR_FORMAT,
handler->urisourcebin);
+ for (iter = handler->sourcepads; iter; iter = iter->next) {
+ GstSourcePad *spad = iter->data;
+ if (spad->block_probe_id)
+ gst_pad_remove_probe (spad->src_pad, spad->block_probe_id);
+ }
gst_element_set_state (handler->urisourcebin, GST_STATE_NULL);
gst_bin_remove ((GstBin *) uridecodebin, handler->urisourcebin);
+ GST_STATE_UNLOCK (uridecodebin);
+ g_list_free (handler->sourcepads);
}
- uridecodebin->source_handlers =
- g_list_remove (uridecodebin->source_handlers, handler);
+ if (handler->pending_buffering_msg)
+ gst_message_unref (handler->pending_buffering_msg);
g_slice_free (GstSourceHandler, handler);
}
GST_LOG_OBJECT (uridecodebin, "source item %p", item);
if (item->handler)
free_source_handler (uridecodebin, item->handler);
+ g_free (item->uri);
g_slice_free (GstSourceItem, item);
}
+static void
+source_item_set_uri (GstSourceItem * item, const gchar * uri)
+{
+ if (item->uri)
+ g_free (item->uri);
+ item->uri = g_strdup (uri);
+ if (item->handler) {
+ g_object_set (item->handler->urisourcebin, "uri", uri, NULL);
+ }
+}
+
static GstPlayItem *
-new_play_item (GstURIDecodeBin3 * dec, gchar * uri, gchar * suburi)
+new_play_item (GstURIDecodeBin3 * dec)
{
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);
+ item->group_id = GST_GROUP_ID_INVALID;
return item;
}
g_slice_free (GstPlayItem, item);
}
+static void
+play_item_set_uri (GstPlayItem * item, const gchar * uri)
+{
+ if (!item->main_item) {
+ item->main_item =
+ new_source_item (item->uridecodebin, item, g_strdup (uri));
+ } else {
+ source_item_set_uri (item->main_item, uri);
+ }
+}
+
+static void
+play_item_set_suburi (GstPlayItem * item, const gchar * uri)
+{
+ if (!item->sub_item) {
+ item->sub_item = new_source_item (item->uridecodebin, item, g_strdup (uri));
+ } else {
+ source_item_set_uri (item->sub_item, uri);
+ }
+}
+
+static gboolean
+play_item_is_eos (GstPlayItem * item)
+{
+ if (item->main_item && item->main_item->handler) {
+ if (!source_handler_is_eos (item->main_item->handler))
+ return FALSE;
+ }
+ if (item->sub_item && item->sub_item->handler) {
+ if (!source_handler_is_eos (item->sub_item->handler))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Mark all sourcepads of a play item as EOS. Used in pull-mode */
+static void
+play_item_set_eos (GstPlayItem * item)
+{
+ if (item->main_item && item->main_item->handler)
+ source_handler_set_eos (item->main_item->handler);
+
+ if (item->sub_item && item->sub_item->handler)
+ source_handler_set_eos (item->sub_item->handler);
+}
+
+static gboolean
+play_item_has_all_pads (GstPlayItem * item)
+{
+ GstSourceHandler *handler;
+
+ if (item->main_item && item->main_item->handler) {
+ handler = item->main_item->handler;
+ if (handler->expected_pads != g_list_length (handler->sourcepads))
+ return FALSE;
+ }
+
+ if (item->sub_item && item->sub_item->handler) {
+ handler = item->sub_item->handler;
+ if (handler->expected_pads != g_list_length (handler->sourcepads))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Returns the next inactive play item. If none available, it will create one
+ * and add it to the list of play items */
+static GstPlayItem *
+next_inactive_play_item (GstURIDecodeBin3 * dec)
+{
+ GstPlayItem *res;
+ GList *iter;
+
+ for (iter = dec->play_items; iter; iter = iter->next) {
+ res = iter->data;
+ if (!res->active)
+ return res;
+ }
+
+ GST_DEBUG_OBJECT (dec, "No inactive play items, creating a new one");
+ res = new_play_item (dec);
+ dec->play_items = g_list_append (dec->play_items, res);
+
+ return res;
+}
+
+static GstPadProbeReturn
+uri_src_ignore_block_probe (GstPad * pad, GstPadProbeInfo * info,
+ GstSourcePad * srcpad)
+{
+ GST_DEBUG_OBJECT (pad, "blocked");
+ return GST_PAD_PROBE_OK;
+}
+
+static void
+gst_uri_decode_bin3_set_uri (GstURIDecodeBin3 * dec, const gchar * uri)
+{
+ GstPlayItem *item;
+ gboolean start_item = FALSE;
+
+ GST_DEBUG_OBJECT (dec, "uri: %s", uri);
+
+ item = next_inactive_play_item (dec);
+ play_item_set_uri (item, uri);
+ if (dec->instant_uri && item != dec->input_item) {
+ GList *old_pads = get_all_play_item_source_pads (dec->input_item);
+ GList *iter;
+
+ /* Switch immediately if not the current input item */
+ GST_DEBUG_OBJECT (dec, "Switching immediately");
+
+ /* FLUSH START all input pads */
+ for (iter = old_pads; iter; iter = iter->next) {
+ GstSourcePad *spad = iter->data;
+ if (spad->db3_sink_pad) {
+ /* Mark all input pads as EOS */
+ gst_pad_send_event (spad->db3_sink_pad, gst_event_new_flush_start ());
+ }
+ /* Block all input source pads */
+ spad->block_probe_id =
+ gst_pad_add_probe (spad->src_pad, GST_PAD_PROBE_TYPE_IDLE,
+ (GstPadProbeCallback) uri_src_ignore_block_probe, spad, NULL);
+ spad->saw_eos = TRUE;
+ }
+ for (iter = old_pads; iter; iter = iter->next) {
+ /* FLUSH_STOP all current input pads */
+ GstSourcePad *spad = iter->data;
+ if (spad->db3_sink_pad) {
+ gst_pad_send_event (spad->db3_sink_pad,
+ gst_event_new_flush_stop (TRUE));
+ }
+ }
+ start_item = TRUE;
+ } else if (dec->input_item->posted_about_to_finish) {
+ GList *iter = g_list_find (dec->play_items, dec->input_item);
+ /* If the current item is finishing and the new item is the one just after,
+ * we need to activate it */
+ if (iter && iter->next && iter->next->data == item) {
+ GST_DEBUG_OBJECT (dec, "Starting new entry (gapless mode)");
+ start_item = TRUE;
+ }
+ }
+
+ if (start_item) {
+ /* Start new item */
+ activate_play_item (item);
+ }
+}
+
+static void
+gst_uri_decode_bin3_set_suburi (GstURIDecodeBin3 * dec, const gchar * uri)
+{
+ GstPlayItem *item;
+ GST_DEBUG_OBJECT (dec, "suburi: %s", uri);
+
+ /* FIXME : Handle instant-uri-change. Should we just apply it automatically to
+ * the current input item ? */
+
+ item = next_inactive_play_item (dec);
+ play_item_set_suburi (item, uri);
+}
+
/* Sync source handlers for the given play item. Might require creating/removing some
* and/or configure the handlers accordingly */
static GstStateChangeReturn
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
- /* FIXME : Go over existing handlers to see if we can assign some to the
- * given item */
+ if (item->main_item == NULL)
+ return GST_STATE_CHANGE_FAILURE;
/* Create missing handlers */
if (item->main_item->handler == NULL) {
- item->main_item->handler = new_source_handler (dec, TRUE);
+ item->main_item->handler = new_source_handler (dec, item, TRUE);
ret = activate_source_item (item->main_item);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
/* Called to activate the next play item */
static GstStateChangeReturn
-activate_next_play_item (GstURIDecodeBin3 * dec)
+activate_play_item (GstPlayItem * item)
{
- 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);
+ GST_DEBUG_OBJECT (item->uridecodebin, "Activating play item");
- ret = assign_handlers_to_item (dec, item);
- if (ret == GST_STATE_CHANGE_FAILURE) {
- free_play_item (dec, item);
- return ret;
+ ret = assign_handlers_to_item (item->uridecodebin, item);
+ if (ret != GST_STATE_CHANGE_FAILURE) {
+ item->active = TRUE;
}
- dec->play_items = g_list_append (dec->play_items, item);
- dec->current = dec->play_items->data;
-
return ret;
}
+/* Remove all but the last play item */
static void
-free_play_items (GstURIDecodeBin3 * dec)
+purge_play_items (GstURIDecodeBin3 * dec)
{
- GList *tmp;
+ GST_DEBUG_OBJECT (dec, "Purging play items");
- for (tmp = dec->play_items; tmp; tmp = tmp->next) {
- GstPlayItem *item = (GstPlayItem *) tmp->data;
+ PLAY_ITEMS_LOCK (dec);
+ g_cond_signal (&dec->input_source_drained);
+ while (dec->play_items && dec->play_items->next) {
+ GstPlayItem *item = dec->play_items->data;
+ dec->play_items = g_list_remove (dec->play_items, item);
free_play_item (dec, item);
}
- g_list_free (dec->play_items);
- dec->play_items = NULL;
- dec->current = NULL;
+ dec->output_item = dec->input_item = dec->play_items->data;
+ dec->output_item->posted_about_to_finish = FALSE;
+ PLAY_ITEMS_UNLOCK (dec);
}
static GstStateChangeReturn
g_object_set (uridecodebin->decodebin, "caps", uridecodebin->caps, NULL);
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- ret = activate_next_play_item (uridecodebin);
+ g_atomic_int_set (&uridecodebin->shutdown, 0);
+ ret = activate_play_item (uridecodebin->input_item);
if (ret == GST_STATE_CHANGE_FAILURE)
goto failure;
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ PLAY_ITEMS_LOCK (uridecodebin);
+ g_atomic_int_set (&uridecodebin->shutdown, 1);
+ g_cond_signal (&uridecodebin->input_source_drained);
+ PLAY_ITEMS_UNLOCK (uridecodebin);
+ break;
default:
break;
}
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;
+ /* Remove all play items *but* the last one, which becomes the current entry */
+ purge_play_items (uridecodebin);
+ uridecodebin->input_item->active = FALSE;
break;
default:
break;
failure:
{
if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
- free_play_items (uridecodebin);
+ purge_play_items (uridecodebin);
return ret;
}
}
+static GstSourceHandler *
+find_source_handler_for_element (GstURIDecodeBin3 * uridecodebin,
+ GstObject * element)
+{
+ GList *iter;
+
+ for (iter = uridecodebin->play_items; iter; iter = iter->next) {
+ GstPlayItem *item = iter->data;
+
+ if (item->main_item && item->main_item->handler) {
+ GstSourceHandler *handler = item->main_item->handler;
+
+ if (gst_object_has_as_ancestor (element,
+ (GstObject *) handler->urisourcebin))
+ return handler;
+ }
+ if (item->sub_item && item->sub_item->handler) {
+ GstSourceHandler *handler = item->sub_item->handler;
+
+ if (gst_object_has_as_ancestor (element,
+ (GstObject *) handler->urisourcebin))
+ return handler;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+gst_uri_decode_bin3_handle_message (GstBin * bin, GstMessage * msg)
+{
+ GstURIDecodeBin3 *uridecodebin = (GstURIDecodeBin3 *) bin;
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_STREAMS_SELECTED:
+ {
+ GstSourceHandler *handler;
+ GST_DEBUG_OBJECT (uridecodebin, "Handle streams selected");
+ PLAY_ITEMS_LOCK (uridecodebin);
+ /* Find the matching handler (if any) */
+ if ((handler = find_source_handler_for_element (uridecodebin, msg->src))) {
+ handler->expected_pads = gst_message_streams_selected_get_size (msg);
+ GST_DEBUG_OBJECT (uridecodebin,
+ "Got streams-selected for %s with %d streams selected",
+ GST_ELEMENT_NAME (handler->urisourcebin), handler->expected_pads);
+ }
+ PLAY_ITEMS_UNLOCK (uridecodebin);
+ break;
+ }
+ case GST_MESSAGE_BUFFERING:
+ {
+ GstSourceHandler *handler;
+ GST_DEBUG_OBJECT (uridecodebin, "Handle buffering message");
+ PLAY_ITEMS_LOCK (uridecodebin);
+ /* Find the matching handler (if any) */
+ handler = find_source_handler_for_element (uridecodebin, msg->src);
+ if (!handler || !uridecodebin->input_item->main_item) {
+ gst_message_unref (msg);
+ msg = NULL;
+ } else if (handler != uridecodebin->input_item->main_item->handler) {
+ /* Store the message for a later time */
+ if (handler->pending_buffering_msg)
+ gst_message_unref (handler->pending_buffering_msg);
+ handler->pending_buffering_msg = msg;
+ msg = NULL;
+ } else {
+ /* This is the active main input item, we can forward directly */
+ GST_DEBUG_OBJECT (uridecodebin,
+ "Forwarding message for active input item");
+ }
+ PLAY_ITEMS_UNLOCK (uridecodebin);
+ break;
+
+ }
+ default:
+ break;
+ }
+
+ if (msg)
+ GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
+}
+
static gboolean
gst_uri_decodebin3_send_event (GstElement * element, GstEvent * event)
{