playbin3: Implement gapless playback
Similar in vein to the playbin2 architecture except that uridecodebin3 are prerolled much earlier and all streams of the same type are fed through a 'concat' element. This keeps the philosphy of having all elements connected as soon as possible. The 'about-to-finish' signal is emitted whenever one of the uridecodebin is about to finish, allowing the users to set the next uri/suburi. The notion of a group being active has changed. It now means that the uridecodebin3 has been activated, but doesn't mean it is the one currently being outputted by the sinks (i.e. curr_group and next_group). This is done via detecting GST_MESSAGE_STREAM_START emission by playsink and figuring out which group is really playing. When the current group changes, a new thread is started to deactivate the previous one and optionnaly fire 'about-to-finish'.
This commit is contained in:
parent
b9e73d0575
commit
c1e902a084
@ -265,7 +265,7 @@ struct _GstSourceCombine
|
|||||||
|
|
||||||
gboolean has_active_pad; /* stream combiner has the "active-pad" property */
|
gboolean has_active_pad; /* stream combiner has the "active-pad" property */
|
||||||
|
|
||||||
gboolean has_always_ok; /* stream combiner's sink pads have the "always-ok" property */
|
gboolean is_concat; /* The stream combiner is the 'concat' element */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
|
#define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
|
||||||
@ -313,6 +313,7 @@ typedef struct
|
|||||||
struct _SourcePad
|
struct _SourcePad
|
||||||
{
|
{
|
||||||
GstPad *pad; /* The controlled pad */
|
GstPad *pad; /* The controlled pad */
|
||||||
|
GstStreamType stream_type; /* stream type of the controlled pad */
|
||||||
gulong event_probe_id;
|
gulong event_probe_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -327,13 +328,28 @@ struct _GstSourceGroup
|
|||||||
gboolean valid; /* the group has valid info to start playback */
|
gboolean valid; /* the group has valid info to start playback */
|
||||||
gboolean active; /* the group is active */
|
gboolean active; /* the group is active */
|
||||||
|
|
||||||
|
gboolean playing; /* the group is currently playing
|
||||||
|
* (outputted on the sinks) */
|
||||||
|
|
||||||
/* properties */
|
/* properties */
|
||||||
gchar *uri;
|
gchar *uri;
|
||||||
gchar *suburi;
|
gchar *suburi;
|
||||||
|
|
||||||
|
/* The currently outputted group_id */
|
||||||
|
guint group_id;
|
||||||
|
|
||||||
/* Bit-wise set of stream types we have requested from uridecodebin3 */
|
/* Bit-wise set of stream types we have requested from uridecodebin3 */
|
||||||
GstStreamType selected_stream_types;
|
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 */
|
/* uridecodebin to handle uri and suburi */
|
||||||
GstElement *uridecodebin;
|
GstElement *uridecodebin;
|
||||||
|
|
||||||
@ -447,6 +463,9 @@ struct _GstPlayBin3
|
|||||||
/* our play sink */
|
/* our play sink */
|
||||||
GstPlaySink *playsink;
|
GstPlaySink *playsink;
|
||||||
|
|
||||||
|
/* Task for (de)activating groups, protected by the activation lock */
|
||||||
|
GstTask *activation_task;
|
||||||
|
GRecMutex activation_lock;
|
||||||
|
|
||||||
/* lock protecting dynamic adding/removing */
|
/* lock protecting dynamic adding/removing */
|
||||||
GMutex dyn_lock;
|
GMutex dyn_lock;
|
||||||
@ -591,6 +610,8 @@ static GstSample *gst_play_bin3_convert_sample (GstPlayBin3 * playbin,
|
|||||||
|
|
||||||
static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin);
|
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 reconfigure_output (GstPlayBin3 * playbin);
|
||||||
static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
|
static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
|
||||||
GstSourceGroup * group);
|
GstSourceGroup * group);
|
||||||
@ -1147,12 +1168,32 @@ update_combiner_info (GstPlayBin3 * playbin, GstStreamCollection * collection)
|
|||||||
playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len);
|
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)", i, (group == playbin->curr_group) ? "current" : (group == playbin->next_group) ? "next" : "unused"); \
|
||||||
|
GST_DEBUG (" valid:%d , active:%d , playing:%d", group->valid, group->active, group->playing); \
|
||||||
|
GST_DEBUG (" uri:%s", group->uri); \
|
||||||
|
GST_DEBUG (" suburi:%s", group->suburi); \
|
||||||
|
GST_DEBUG (" group_id:%d", group->group_id); \
|
||||||
|
GST_DEBUG (" pending_about_to_finish:%d", group->pending_about_to_finish); \
|
||||||
|
} \
|
||||||
|
} G_STMT_END
|
||||||
|
#else
|
||||||
|
#define debug_groups(p) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
init_group (GstPlayBin3 * playbin, GstSourceGroup * group)
|
init_group (GstPlayBin3 * playbin, GstSourceGroup * group)
|
||||||
{
|
{
|
||||||
g_mutex_init (&group->lock);
|
g_mutex_init (&group->lock);
|
||||||
|
|
||||||
group->stream_changed_pending = FALSE;
|
group->stream_changed_pending = FALSE;
|
||||||
|
group->group_id = GST_GROUP_ID_INVALID;
|
||||||
|
|
||||||
group->playbin = playbin;
|
group->playbin = playbin;
|
||||||
}
|
}
|
||||||
@ -1293,6 +1334,8 @@ gst_play_bin3_init (GstPlayBin3 * playbin)
|
|||||||
/* first filter out the interesting element factories */
|
/* first filter out the interesting element factories */
|
||||||
g_mutex_init (&playbin->elements_lock);
|
g_mutex_init (&playbin->elements_lock);
|
||||||
|
|
||||||
|
g_rec_mutex_init (&playbin->activation_lock);
|
||||||
|
|
||||||
/* add sink */
|
/* add sink */
|
||||||
playbin->playsink =
|
playbin->playsink =
|
||||||
g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
|
g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
|
||||||
@ -1551,7 +1594,7 @@ gst_play_bin3_set_current_stream (GstPlayBin3 * playbin,
|
|||||||
GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d",
|
GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d",
|
||||||
stream_type_names[stream_type], *current_value, stream);
|
stream_type_names[stream_type], *current_value, stream);
|
||||||
|
|
||||||
if (combine->combiner == NULL) {
|
if (combine->combiner == NULL || combine->is_concat) {
|
||||||
/* FIXME: Check that the current_value is within range */
|
/* FIXME: Check that the current_value is within range */
|
||||||
*current_value = stream;
|
*current_value = stream;
|
||||||
do_stream_selection (playbin, playbin->curr_group);
|
do_stream_selection (playbin, playbin->curr_group);
|
||||||
@ -1836,7 +1879,10 @@ gst_play_bin3_get_current_stream_combiner (GstPlayBin3 * playbin,
|
|||||||
GstElement *combiner;
|
GstElement *combiner;
|
||||||
|
|
||||||
GST_PLAY_BIN3_LOCK (playbin);
|
GST_PLAY_BIN3_LOCK (playbin);
|
||||||
if ((combiner = playbin->combiner[stream_type].combiner))
|
/* The special concat element should never be returned */
|
||||||
|
if (playbin->combiner[stream_type].is_concat)
|
||||||
|
combiner = NULL;
|
||||||
|
else if ((combiner = playbin->combiner[stream_type].combiner))
|
||||||
gst_object_ref (combiner);
|
gst_object_ref (combiner);
|
||||||
else if ((combiner = *elem))
|
else if ((combiner = *elem))
|
||||||
gst_object_ref (combiner);
|
gst_object_ref (combiner);
|
||||||
@ -2381,29 +2427,57 @@ gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg)
|
|||||||
{
|
{
|
||||||
GstPlayBin3 *playbin = GST_PLAY_BIN3 (bin);
|
GstPlayBin3 *playbin = GST_PLAY_BIN3 (bin);
|
||||||
|
|
||||||
/* FIXME: REENABLE ONCE WE HAVE CROSSFADE SUPPORT */
|
|
||||||
#if 0
|
|
||||||
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
|
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
|
||||||
GstSourceGroup *new_group = playbin->curr_group;
|
GstSourceGroup *group = NULL, *other_group = NULL;
|
||||||
GstMessage *buffering_msg = NULL;
|
gboolean changed = FALSE;
|
||||||
|
guint group_id;
|
||||||
|
|
||||||
GST_SOURCE_GROUP_LOCK (new_group);
|
if (!gst_message_parse_group_id (msg, &group_id)) {
|
||||||
new_group->stream_changed_pending = FALSE;
|
GST_ERROR_OBJECT (bin,
|
||||||
if (new_group->pending_buffering_msg) {
|
"Could not get group_id from STREAM_START message !");
|
||||||
buffering_msg = new_group->pending_buffering_msg;
|
goto beach;
|
||||||
new_group->pending_buffering_msg = NULL;
|
|
||||||
}
|
}
|
||||||
GST_SOURCE_GROUP_UNLOCK (new_group);
|
GST_DEBUG_OBJECT (bin, "STREAM_START group_id:%u", group_id);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (playbin, "Stream start from new group %p", new_group);
|
/* Figure out to which group this group_id corresponds */
|
||||||
|
GST_PLAY_BIN3_LOCK (playbin);
|
||||||
if (buffering_msg) {
|
if (playbin->groups[0].group_id == group_id) {
|
||||||
GST_DEBUG_OBJECT (playbin, "Posting pending buffering message: %"
|
group = &playbin->groups[0];
|
||||||
GST_PTR_FORMAT, buffering_msg);
|
other_group = &playbin->groups[1];
|
||||||
GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
|
} else if (playbin->groups[1].group_id == group_id) {
|
||||||
|
group = &playbin->groups[1];
|
||||||
|
other_group = &playbin->groups[0];
|
||||||
|
}
|
||||||
|
if (group == NULL) {
|
||||||
|
GST_ERROR_OBJECT (bin, "group_id %u is not provided by any group !",
|
||||||
|
group_id);
|
||||||
|
GST_PLAY_BIN3_UNLOCK (playbin);
|
||||||
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
|
debug_groups (playbin);
|
||||||
|
|
||||||
|
/* Do the switch now ! */
|
||||||
|
playbin->curr_group = group;
|
||||||
|
playbin->next_group = other_group;
|
||||||
|
|
||||||
|
GST_SOURCE_GROUP_LOCK (group);
|
||||||
|
if (group->playing == FALSE)
|
||||||
|
changed = TRUE;
|
||||||
|
group->playing = TRUE;
|
||||||
|
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 0
|
||||||
|
else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
|
||||||
GstSourceGroup *group = playbin->curr_group;
|
GstSourceGroup *group = playbin->curr_group;
|
||||||
gboolean pending;
|
gboolean pending;
|
||||||
|
|
||||||
@ -2455,6 +2529,7 @@ gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beach:
|
||||||
if (msg)
|
if (msg)
|
||||||
GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
|
GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
|
||||||
}
|
}
|
||||||
@ -2620,6 +2695,22 @@ update_video_multiview_caps (GstPlayBin3 * playbin, GstCaps * caps)
|
|||||||
return out_caps;
|
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 *
|
static SourcePad *
|
||||||
find_source_pad (GstSourceGroup * group, GstPad * target)
|
find_source_pad (GstSourceGroup * group, GstPad * target)
|
||||||
{
|
{
|
||||||
@ -2662,6 +2753,21 @@ _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2670,7 +2776,8 @@ _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
control_source_pad (GstSourceGroup * group, GstPad * pad)
|
control_source_pad (GstSourceGroup * group, GstPad * pad,
|
||||||
|
GstStreamType stream_type)
|
||||||
{
|
{
|
||||||
SourcePad *sourcepad = g_slice_new0 (SourcePad);
|
SourcePad *sourcepad = g_slice_new0 (SourcePad);
|
||||||
|
|
||||||
@ -2678,6 +2785,7 @@ control_source_pad (GstSourceGroup * group, GstPad * pad)
|
|||||||
sourcepad->event_probe_id =
|
sourcepad->event_probe_id =
|
||||||
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
||||||
_decodebin_event_probe, group, NULL);
|
_decodebin_event_probe, group, NULL);
|
||||||
|
sourcepad->stream_type = stream_type;
|
||||||
group->source_pads = g_list_append (group->source_pads, sourcepad);
|
group->source_pads = g_list_append (group->source_pads, sourcepad);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2727,12 +2835,22 @@ create_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine)
|
|||||||
combine->combiner = custom_combiner;
|
combine->combiner = custom_combiner;
|
||||||
|
|
||||||
if (!combine->combiner) {
|
if (!combine->combiner) {
|
||||||
GST_DEBUG_OBJECT (playbin, "No custom combiner requested");
|
gchar *concat_name;
|
||||||
return;
|
GST_DEBUG_OBJECT (playbin,
|
||||||
|
"No custom combiner requested, using 'concat' element");
|
||||||
|
concat_name = g_strdup_printf ("%s-concat", combine->media_type);
|
||||||
|
combine->combiner = gst_element_factory_make ("concat", concat_name);
|
||||||
|
g_object_set (combine->combiner, "adjust-base", FALSE, NULL);
|
||||||
|
g_free (concat_name);
|
||||||
|
combine->is_concat = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
|
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 =
|
combine->has_active_pad =
|
||||||
g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
|
g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
|
||||||
"active-pad") != NULL;
|
"active-pad") != NULL;
|
||||||
@ -2740,6 +2858,7 @@ create_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine)
|
|||||||
if (combine->has_active_pad)
|
if (combine->has_active_pad)
|
||||||
g_signal_connect (combine->combiner, "notify::active-pad",
|
g_signal_connect (combine->combiner, "notify::active-pad",
|
||||||
G_CALLBACK (combiner_active_pad_changed), playbin);
|
G_CALLBACK (combiner_active_pad_changed), playbin);
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (playbin, "adding new stream combiner %" GST_PTR_FORMAT,
|
GST_DEBUG_OBJECT (playbin, "adding new stream combiner %" GST_PTR_FORMAT,
|
||||||
combine->combiner);
|
combine->combiner);
|
||||||
@ -2765,11 +2884,6 @@ combiner_control_pad (GstPlayBin3 * playbin, GstSourceCombine * combine,
|
|||||||
GST_DEBUG_OBJECT (playbin, "Got new combiner pad %" GST_PTR_FORMAT,
|
GST_DEBUG_OBJECT (playbin, "Got new combiner pad %" GST_PTR_FORMAT,
|
||||||
sinkpad);
|
sinkpad);
|
||||||
|
|
||||||
/* find out which properties the sink pad supports */
|
|
||||||
combine->has_always_ok =
|
|
||||||
g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad),
|
|
||||||
"always-ok") != NULL;
|
|
||||||
|
|
||||||
/* store the pad in the array */
|
/* store the pad in the array */
|
||||||
GST_DEBUG_OBJECT (playbin, "pad %" GST_PTR_FORMAT " added to array",
|
GST_DEBUG_OBJECT (playbin, "pad %" GST_PTR_FORMAT " added to array",
|
||||||
sinkpad);
|
sinkpad);
|
||||||
@ -2855,6 +2969,8 @@ static void
|
|||||||
release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad)
|
release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad)
|
||||||
{
|
{
|
||||||
SourcePad *sourcepad;
|
SourcePad *sourcepad;
|
||||||
|
GList *tmp;
|
||||||
|
GstStreamType alltype = 0;
|
||||||
|
|
||||||
sourcepad = find_source_pad (group, pad);
|
sourcepad = find_source_pad (group, pad);
|
||||||
if (!sourcepad) {
|
if (!sourcepad) {
|
||||||
@ -2870,6 +2986,13 @@ release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad)
|
|||||||
/* Remove from list of controlled pads and check again for EOS status */
|
/* Remove from list of controlled pads and check again for EOS status */
|
||||||
group->source_pads = g_list_remove (group->source_pads, sourcepad);
|
group->source_pads = g_list_remove (group->source_pads, sourcepad);
|
||||||
g_slice_free (SourcePad, 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
|
/* this function is called when a new pad is added to decodebin. We check the
|
||||||
@ -2913,7 +3036,16 @@ pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstSourceGroup * group)
|
|||||||
|
|
||||||
combiner_control_pad (playbin, combine, pad);
|
combiner_control_pad (playbin, combine, pad);
|
||||||
|
|
||||||
control_source_pad (group, pad);
|
control_source_pad (group, 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_SHUTDOWN_UNLOCK (playbin);
|
GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin);
|
||||||
|
|
||||||
@ -2933,7 +3065,7 @@ shutdown:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* called when a pad is removed from the decodebin. We unlink the pad from
|
/* called when a pad is removed from the decodebin. We unlink the pad from
|
||||||
* the combiner. This will make the combiner select a new pad. */
|
* the combiner. */
|
||||||
static void
|
static void
|
||||||
pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
|
pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
|
||||||
{
|
{
|
||||||
@ -3099,15 +3231,18 @@ about_to_finish_cb (GstElement * uridecodebin, GstSourceGroup * group)
|
|||||||
GstPlayBin3 *playbin = group->playbin;
|
GstPlayBin3 *playbin = group->playbin;
|
||||||
GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
|
GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
|
||||||
|
|
||||||
/* after this call, we should have a next group to activate or we EOS */
|
GST_LOG_OBJECT (playbin, "selected_stream_types:%" STREAM_TYPES_FORMAT,
|
||||||
g_signal_emit (G_OBJECT (playbin),
|
STREAM_TYPES_ARGS (group->selected_stream_types));
|
||||||
gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
|
GST_LOG_OBJECT (playbin, "present_stream_types:%" STREAM_TYPES_FORMAT,
|
||||||
|
STREAM_TYPES_ARGS (group->present_stream_types));
|
||||||
|
|
||||||
#if 0
|
if (group->selected_stream_types == 0
|
||||||
/* now activate the next group. If the app did not set a uri, this will
|
|| (group->selected_stream_types != group->present_stream_types)) {
|
||||||
* fail and we can do EOS */
|
GST_LOG_OBJECT (playbin,
|
||||||
setup_next_source (playbin);
|
"Delaying emission of signal until this group is ready");
|
||||||
#endif
|
group->pending_about_to_finish = TRUE;
|
||||||
|
} else
|
||||||
|
emit_about_to_finish (playbin);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0 /* AUTOPLUG DISABLED */
|
#if 0 /* AUTOPLUG DISABLED */
|
||||||
@ -4469,8 +4604,7 @@ error_cleanup:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unlink a group of uridecodebin from the decodebin.
|
/* must be called with PLAY_BIN_LOCK */
|
||||||
* must be called with PLAY_BIN_LOCK */
|
|
||||||
static gboolean
|
static gboolean
|
||||||
deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
|
deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
|
||||||
{
|
{
|
||||||
@ -4481,6 +4615,8 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
|
|||||||
|
|
||||||
GST_SOURCE_GROUP_LOCK (group);
|
GST_SOURCE_GROUP_LOCK (group);
|
||||||
group->active = FALSE;
|
group->active = FALSE;
|
||||||
|
group->playing = FALSE;
|
||||||
|
group->group_id = GST_GROUP_ID_INVALID;
|
||||||
|
|
||||||
group->selected_stream_types = 0;
|
group->selected_stream_types = 0;
|
||||||
/* Update global selected_stream_types */
|
/* Update global selected_stream_types */
|
||||||
@ -4520,23 +4656,27 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (group->uridecodebin) {
|
if (group->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->select_stream_id);
|
||||||
REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
|
REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
|
||||||
REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id);
|
REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id);
|
||||||
|
|
||||||
|
gst_element_set_state (group->uridecodebin, GST_STATE_NULL);
|
||||||
|
gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
|
||||||
|
|
||||||
|
REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
|
||||||
|
REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
|
||||||
#if 0
|
#if 0
|
||||||
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
|
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id);
|
||||||
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
|
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id);
|
||||||
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
|
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id);
|
||||||
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
|
REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id);
|
||||||
#endif
|
#endif
|
||||||
gst_element_set_state (group->uridecodebin, GST_STATE_NULL);
|
|
||||||
gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_SOURCE_GROUP_UNLOCK (group);
|
GST_SOURCE_GROUP_UNLOCK (group);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (playbin, "Done");
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4546,31 +4686,19 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group)
|
|||||||
static GstStateChangeReturn
|
static GstStateChangeReturn
|
||||||
setup_next_source (GstPlayBin3 * playbin)
|
setup_next_source (GstPlayBin3 * playbin)
|
||||||
{
|
{
|
||||||
GstSourceGroup *new_group, *old_group;
|
GstSourceGroup *new_group;
|
||||||
GstStateChangeReturn state_ret;
|
GstStateChangeReturn state_ret;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (playbin, "setup sources");
|
GST_DEBUG_OBJECT (playbin, "setup next source");
|
||||||
|
|
||||||
|
debug_groups (playbin);
|
||||||
|
|
||||||
/* see if there is a next group */
|
/* see if there is a next group */
|
||||||
GST_PLAY_BIN3_LOCK (playbin);
|
GST_PLAY_BIN3_LOCK (playbin);
|
||||||
new_group = playbin->next_group;
|
new_group = playbin->next_group;
|
||||||
if (!new_group || !new_group->valid)
|
if (!new_group || !new_group->valid || new_group->active)
|
||||||
goto no_next_group;
|
goto no_next_group;
|
||||||
|
|
||||||
/* first unlink the current source, if any */
|
|
||||||
old_group = playbin->curr_group;
|
|
||||||
if (old_group && old_group->valid && old_group->active) {
|
|
||||||
new_group->stream_changed_pending = TRUE;
|
|
||||||
|
|
||||||
/* unlink our pads with the sink */
|
|
||||||
deactivate_group (playbin, old_group);
|
|
||||||
old_group->valid = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* swap old and new */
|
|
||||||
playbin->curr_group = new_group;
|
|
||||||
playbin->next_group = old_group;
|
|
||||||
|
|
||||||
/* activate the new group */
|
/* activate the new group */
|
||||||
state_ret = activate_group (playbin, new_group);
|
state_ret = activate_group (playbin, new_group);
|
||||||
if (state_ret == GST_STATE_CHANGE_FAILURE)
|
if (state_ret == GST_STATE_CHANGE_FAILURE)
|
||||||
@ -4578,6 +4706,8 @@ setup_next_source (GstPlayBin3 * playbin)
|
|||||||
|
|
||||||
GST_PLAY_BIN3_UNLOCK (playbin);
|
GST_PLAY_BIN3_UNLOCK (playbin);
|
||||||
|
|
||||||
|
debug_groups (playbin);
|
||||||
|
|
||||||
return state_ret;
|
return state_ret;
|
||||||
|
|
||||||
/* ERRORS */
|
/* ERRORS */
|
||||||
@ -4642,6 +4772,117 @@ groups_set_locked_state (GstPlayBin3 * playbin, gboolean locked)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_play_bin3_check_group_status (GstPlayBin3 * playbin)
|
||||||
|
{
|
||||||
|
if (playbin->activation_task)
|
||||||
|
gst_task_start (playbin->activation_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_play_bin3_activation_thread (GstPlayBin3 * playbin)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (playbin, "starting");
|
||||||
|
|
||||||
|
debug_groups (playbin);
|
||||||
|
|
||||||
|
/* Check if next_group needs to be deactivated */
|
||||||
|
GST_PLAY_BIN3_LOCK (playbin);
|
||||||
|
if (playbin->next_group->active) {
|
||||||
|
deactivate_group (playbin, playbin->next_group);
|
||||||
|
playbin->next_group->valid = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is there a pending about-to-finish to be emitted ? */
|
||||||
|
GST_SOURCE_GROUP_LOCK (playbin->curr_group);
|
||||||
|
if (playbin->curr_group->pending_about_to_finish) {
|
||||||
|
GST_LOG_OBJECT (playbin, "Propagating about-to-finish");
|
||||||
|
playbin->curr_group->pending_about_to_finish = FALSE;
|
||||||
|
GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
|
||||||
|
/* This will activate the next source afterwards */
|
||||||
|
emit_about_to_finish (playbin);
|
||||||
|
} else
|
||||||
|
GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (playbin, "Pausing task");
|
||||||
|
if (playbin->activation_task)
|
||||||
|
gst_task_pause (playbin->activation_task);
|
||||||
|
GST_PLAY_BIN3_UNLOCK (playbin);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (playbin, "done");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_play_bin3_start (GstPlayBin3 * playbin)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (playbin, "starting");
|
||||||
|
|
||||||
|
GST_PLAY_BIN3_LOCK (playbin);
|
||||||
|
|
||||||
|
if (playbin->activation_task == NULL) {
|
||||||
|
playbin->activation_task =
|
||||||
|
gst_task_new ((GstTaskFunction) gst_play_bin3_activation_thread,
|
||||||
|
playbin, NULL);
|
||||||
|
if (playbin->activation_task == NULL)
|
||||||
|
goto task_error;
|
||||||
|
gst_task_set_lock (playbin->activation_task, &playbin->activation_lock);
|
||||||
|
}
|
||||||
|
GST_LOG_OBJECT (playbin, "clearing shutdown flag");
|
||||||
|
g_atomic_int_set (&playbin->shutdown, 0);
|
||||||
|
do_async_start (playbin);
|
||||||
|
|
||||||
|
GST_PLAY_BIN3_UNLOCK (playbin);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
task_error:
|
||||||
|
{
|
||||||
|
GST_PLAY_BIN3_UNLOCK (playbin);
|
||||||
|
GST_ERROR_OBJECT (playbin, "Failed to create task");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_play_bin3_stop (GstPlayBin3 * playbin)
|
||||||
|
{
|
||||||
|
GstTask *task;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (playbin, "stopping");
|
||||||
|
|
||||||
|
/* FIXME unlock our waiting groups */
|
||||||
|
GST_LOG_OBJECT (playbin, "setting shutdown flag");
|
||||||
|
g_atomic_int_set (&playbin->shutdown, 1);
|
||||||
|
|
||||||
|
/* wait for all callbacks to end by taking the lock.
|
||||||
|
* No dynamic (critical) new callbacks will
|
||||||
|
* be able to happen as we set the shutdown flag. */
|
||||||
|
GST_PLAY_BIN3_DYN_LOCK (playbin);
|
||||||
|
GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
|
||||||
|
GST_PLAY_BIN3_DYN_UNLOCK (playbin);
|
||||||
|
|
||||||
|
/* Stop the activation task */
|
||||||
|
GST_PLAY_BIN3_LOCK (playbin);
|
||||||
|
if ((task = playbin->activation_task)) {
|
||||||
|
playbin->activation_task = NULL;
|
||||||
|
GST_PLAY_BIN3_UNLOCK (playbin);
|
||||||
|
|
||||||
|
gst_task_stop (task);
|
||||||
|
|
||||||
|
/* Make sure task is not running */
|
||||||
|
g_rec_mutex_lock (&playbin->activation_lock);
|
||||||
|
g_rec_mutex_unlock (&playbin->activation_lock);
|
||||||
|
|
||||||
|
/* Wait for task to finish and unref it */
|
||||||
|
gst_task_join (task);
|
||||||
|
gst_object_unref (task);
|
||||||
|
|
||||||
|
GST_PLAY_BIN3_LOCK (playbin);
|
||||||
|
}
|
||||||
|
GST_PLAY_BIN3_UNLOCK (playbin);
|
||||||
|
}
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static GstStateChangeReturn
|
||||||
gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
|
gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
|
||||||
{
|
{
|
||||||
@ -4653,22 +4894,12 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition)
|
|||||||
|
|
||||||
switch (transition) {
|
switch (transition) {
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
GST_LOG_OBJECT (playbin, "clearing shutdown flag");
|
if (!gst_play_bin3_start (playbin))
|
||||||
g_atomic_int_set (&playbin->shutdown, 0);
|
return GST_STATE_CHANGE_FAILURE;
|
||||||
do_async_start (playbin);
|
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
async_down:
|
async_down:
|
||||||
/* FIXME unlock our waiting groups */
|
gst_play_bin3_stop (playbin);
|
||||||
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);
|
|
||||||
if (!do_save)
|
if (!do_save)
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user