play: Improve stream selection

- Unset stream ids if a collection does not contain them
  - Automatically select a default stream of a type if the stream type is
    enabled but no stream is selected yet when receiving the stream collection
  - Warn if there's a collection update via streams-selected and if there are
    unexpected streams being selected, or actually selected streams not being
    found
  - Improve debug output a bit

Among other things this also makes sure that we don't forget a selected stream
id when disabling a track so that when enabling it again later the same one can
be enabled again.

See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4344

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9081>
This commit is contained in:
Sebastian Dröge 2025-05-26 19:07:22 +03:00 committed by GStreamer Marge Bot
parent 50cad4a7fa
commit 68815a3a15

View File

@ -1755,6 +1755,52 @@ update_stream_collection (GstPlay * self, GstStreamCollection * collection)
g_signal_connect (self->collection, "stream-notify",
G_CALLBACK (stream_notify_cb), self);
// If no stream selected then we don't have to search
gboolean found_audio = self->audio_sid == NULL;
gboolean found_video = self->video_sid == NULL;
gboolean found_subtitle = self->subtitle_sid == NULL;
guint len = gst_stream_collection_get_size (collection);
for (guint i = 0; i < len; i++) {
GstStream *stream = gst_stream_collection_get_stream (collection, i);
GstStreamType stream_type = gst_stream_get_stream_type (stream);
const gchar *stream_id = gst_stream_get_stream_id (stream);
if ((stream_type & GST_STREAM_TYPE_AUDIO)
&& g_strcmp0 (self->audio_sid, stream_id) == 0) {
found_audio = TRUE;
}
if ((stream_type & GST_STREAM_TYPE_VIDEO)
&& g_strcmp0 (self->video_sid, stream_id) == 0) {
found_video = TRUE;
}
if ((stream_type & GST_STREAM_TYPE_TEXT)
&& g_strcmp0 (self->subtitle_sid, stream_id) == 0) {
found_subtitle = TRUE;
}
}
if (!found_audio) {
GST_WARNING_OBJECT (self, "Didn't find selected audio stream id '%s'",
self->audio_sid);
g_free (self->audio_sid);
self->audio_sid = NULL;
}
if (!found_video) {
GST_WARNING_OBJECT (self, "Didn't find selected video stream id '%s'",
self->video_sid);
g_free (self->video_sid);
self->video_sid = NULL;
}
if (!found_subtitle) {
GST_WARNING_OBJECT (self, "Didn't find selected subtitle stream id '%s'",
self->subtitle_sid);
g_free (self->subtitle_sid);
self->subtitle_sid = NULL;
}
return self->media_info != NULL;
}
@ -1764,8 +1810,6 @@ stream_collection_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
{
GstPlay *self = GST_PLAY (user_data);
GstStreamCollection *collection = NULL;
gboolean updated = FALSE;
gboolean do_default_selection;
gst_message_parse_stream_collection (msg, &collection);
@ -1773,20 +1817,21 @@ stream_collection_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
return;
g_mutex_lock (&self->lock);
do_default_selection = self->collection == NULL;
updated = update_stream_collection (self, collection);
gboolean updated = update_stream_collection (self, collection);
gst_object_unref (collection);
if (do_default_selection) {
gboolean select_audio = self->audio_enabled;
gboolean select_video = self->video_enabled;
gboolean select_subtitle = self->subtitle_enabled;
guint i, len;
// Select a default stream if it is enabled and none was selected by the application
gboolean select_audio = self->audio_enabled && !self->audio_sid;
gboolean select_video = self->video_enabled && !self->video_sid;
gboolean select_subtitle = self->subtitle_enabled && !self->subtitle_sid;
GST_DEBUG_OBJECT (self, "Do initial default selection");
len = gst_stream_collection_get_size (collection);
if (select_audio || select_video || select_subtitle) {
GST_DEBUG_OBJECT (self,
"Do default selection: audio %d video %d subtitle %d", select_audio,
select_video, select_subtitle);
for (i = 0; i < len; i++) {
guint len = gst_stream_collection_get_size (collection);
for (guint i = 0; i < len; i++) {
GstStream *stream = gst_stream_collection_get_stream (collection, i);
GstStreamType stream_type = gst_stream_get_stream_type (stream);
const gchar *stream_id = gst_stream_get_stream_id (stream);
@ -1795,20 +1840,24 @@ stream_collection_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
g_free (self->audio_sid);
self->audio_sid = g_strdup (stream_id);
select_audio = FALSE;
updated = TRUE;
} else if ((stream_type & GST_STREAM_TYPE_VIDEO) && select_video) {
g_free (self->video_sid);
self->video_sid = g_strdup (stream_id);
select_video = FALSE;
updated = TRUE;
} else if ((stream_type & GST_STREAM_TYPE_TEXT) && select_subtitle) {
g_free (self->subtitle_sid);
self->subtitle_sid = g_strdup (stream_id);
select_subtitle = FALSE;
updated = TRUE;
}
}
gst_play_select_streams (self);
}
if (updated)
gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
if (self->media_info && updated)
@ -1821,8 +1870,6 @@ streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
{
GstPlay *self = GST_PLAY (user_data);
GstStreamCollection *collection = NULL;
gboolean updated = FALSE;
guint i, len;
gst_message_parse_streams_selected (msg, &collection);
@ -1830,47 +1877,74 @@ streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
return;
g_mutex_lock (&self->lock);
updated = update_stream_collection (self, collection);
gboolean updated = update_stream_collection (self, collection);
gst_object_unref (collection);
g_free (self->video_sid);
g_free (self->audio_sid);
g_free (self->subtitle_sid);
self->video_sid = NULL;
self->audio_sid = NULL;
self->subtitle_sid = NULL;
// This should not really happen: we should first get a stream-collection
// message with the new collection, then selection happens.
if (updated) {
GST_WARNING_OBJECT (self,
"Updated stream collection from streams-selected message");
}
len = gst_message_streams_selected_get_size (msg);
for (i = 0; i < len; i++) {
gboolean found_audio = self->audio_sid == NULL;
gboolean found_video = self->video_sid == NULL;
gboolean found_subtitle = self->subtitle_sid == NULL;
guint len = gst_message_streams_selected_get_size (msg);
for (guint i = 0; i < len; i++) {
GstStream *stream;
GstStreamType stream_type;
const gchar *stream_id;
gchar **current_sid;
stream = gst_message_streams_selected_get_stream (msg, i);
stream_type = gst_stream_get_stream_type (stream);
stream_id = gst_stream_get_stream_id (stream);
if (stream_type & GST_STREAM_TYPE_AUDIO)
current_sid = &self->audio_sid;
else if (stream_type & GST_STREAM_TYPE_VIDEO)
current_sid = &self->video_sid;
else if (stream_type & GST_STREAM_TYPE_TEXT)
current_sid = &self->subtitle_sid;
else {
GST_WARNING_OBJECT (self,
"Unknown stream-id %s with type 0x%x", stream_id, stream_type);
continue;
if ((stream_type & GST_STREAM_TYPE_AUDIO)) {
GST_DEBUG_OBJECT (self, "Selected audio track %s", stream_id);
if (g_strcmp0 (self->audio_sid, stream_id) == 0 && self->audio_enabled) {
found_audio = TRUE;
} else {
GST_WARNING_OBJECT (self, "Unexpected audio stream id '%s' selected",
stream_id);
}
}
if (G_UNLIKELY (*current_sid)) {
GST_FIXME_OBJECT (self,
"Multiple streams are selected for type %s, choose the first one",
gst_stream_type_get_name (stream_type));
continue;
if ((stream_type & GST_STREAM_TYPE_VIDEO)) {
GST_DEBUG_OBJECT (self, "Selected video track %s", stream_id);
if (g_strcmp0 (self->video_sid, stream_id) == 0 && self->video_enabled) {
found_video = TRUE;
} else {
GST_WARNING_OBJECT (self, "Unexpected video stream id '%s' selected",
stream_id);
}
}
*current_sid = g_strdup (stream_id);
if ((stream_type & GST_STREAM_TYPE_TEXT) && self->subtitle_enabled) {
GST_DEBUG_OBJECT (self, "Selected subtitle track %s", stream_id);
if (g_strcmp0 (self->subtitle_sid, stream_id) == 0) {
found_subtitle = TRUE;
} else {
GST_WARNING_OBJECT (self, "Unexpected subtitle stream id '%s' selected",
stream_id);
}
}
}
if (!found_audio && self->audio_enabled) {
GST_WARNING_OBJECT (self, "Didn't find selected audio stream id '%s'",
self->audio_sid);
}
if (!found_video && self->video_enabled) {
GST_WARNING_OBJECT (self, "Didn't find selected video stream id '%s'",
self->video_sid);
}
if (!found_subtitle && self->subtitle_enabled) {
GST_WARNING_OBJECT (self, "Didn't find selected subtitle stream id '%s'",
self->subtitle_sid);
}
g_mutex_unlock (&self->lock);
@ -3450,12 +3524,18 @@ gst_play_select_streams (GstPlay * self)
if (!self->collection)
return FALSE;
if (self->audio_sid && self->audio_enabled)
if (self->audio_sid && self->audio_enabled) {
GST_DEBUG_OBJECT (self, "Selecting audio track %s", self->audio_sid);
stream_list = g_list_append (stream_list, g_strdup (self->audio_sid));
if (self->video_sid && self->video_enabled)
}
if (self->video_sid && self->video_enabled) {
GST_DEBUG_OBJECT (self, "Selecting video track %s", self->video_sid);
stream_list = g_list_append (stream_list, g_strdup (self->video_sid));
if (self->subtitle_sid && self->subtitle_enabled)
}
if (self->subtitle_sid && self->subtitle_enabled) {
GST_DEBUG_OBJECT (self, "Selecting subtitle track %s", self->subtitle_sid);
stream_list = g_list_append (stream_list, g_strdup (self->subtitle_sid));
}
g_mutex_unlock (&self->lock);
if (stream_list) {
@ -3502,9 +3582,10 @@ gst_play_set_audio_track (GstPlay * self, gint stream_index)
g_free (self->audio_sid);
self->audio_sid = g_strdup (info->stream_id);
GST_DEBUG_OBJECT (self, "Selecting audio stream id '%s'", info->stream_id);
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "set stream id '%s'", info->stream_id);
g_object_unref (info);
return ret;
@ -3543,9 +3624,10 @@ gst_play_set_video_track (GstPlay * self, gint stream_index)
g_free (self->video_sid);
self->video_sid = g_strdup (info->stream_id);
GST_DEBUG_OBJECT (self, "Selecting video stream id '%s'", info->stream_id);
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "set stream id '%s'", info->stream_id);
g_object_unref (info);
return ret;
@ -3583,9 +3665,10 @@ gst_play_set_subtitle_track (GstPlay * self, gint stream_index)
g_free (self->subtitle_sid);
self->subtitle_sid = g_strdup (info->stream_id);
GST_DEBUG_OBJECT (self, "Selecting subtitle stream id '%s'", info->stream_id);
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "set stream id '%s'", info->stream_id);
g_object_unref (info);
return ret;
@ -3632,10 +3715,12 @@ gst_play_set_audio_track_id (GstPlay * self, const gchar * stream_id)
g_free (self->audio_sid);
self->audio_sid = g_strdup (stream_id);
GST_DEBUG_OBJECT (self, "Selecting audio stream id '%s'",
GST_STR_NULL (stream_id));
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "set stream id '%s'", GST_STR_NULL (stream_id));
return ret;
}
@ -3680,10 +3765,12 @@ gst_play_set_video_track_id (GstPlay * self, const gchar * stream_id)
g_free (self->video_sid);
self->video_sid = g_strdup (stream_id);
GST_DEBUG_OBJECT (self, "Selecting video stream id '%s'",
GST_STR_NULL (stream_id));
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "set stream id '%s'", GST_STR_NULL (stream_id));
return ret;
}
@ -3728,10 +3815,12 @@ gst_play_set_subtitle_track_id (GstPlay * self, const gchar * stream_id)
g_free (self->subtitle_sid);
self->subtitle_sid = g_strdup (stream_id);
GST_DEBUG_OBJECT (self, "Selecting subtitle stream id '%s'",
GST_STR_NULL (stream_id));
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "set stream id '%s'", GST_STR_NULL (stream_id));
return ret;
}
@ -3826,13 +3915,14 @@ gst_play_set_track_ids (GstPlay * self, const gchar * audio_stream_id,
self->video_sid = g_strdup (video_stream_id);
g_free (self->subtitle_sid);
self->subtitle_sid = g_strdup (subtitle_stream_id);
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "set stream ids audio '%s' video '%s' subtitle '%s'",
GST_STR_NULL (audio_stream_id), GST_STR_NULL (video_stream_id),
GST_STR_NULL (subtitle_stream_id));
ret = gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
return ret;
}
@ -3851,10 +3941,10 @@ gst_play_set_audio_track_enabled (GstPlay * self, gboolean enabled)
g_mutex_lock (&self->lock);
self->audio_enabled = enabled;
GST_DEBUG_OBJECT (self, "Audio track is %s",
enabled ? "enabled" : "disabled");
gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
}
/**
@ -3872,10 +3962,10 @@ gst_play_set_video_track_enabled (GstPlay * self, gboolean enabled)
g_mutex_lock (&self->lock);
self->video_enabled = enabled;
GST_DEBUG_OBJECT (self, "Video track is %s",
enabled ? "enabled" : "disabled");
gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
}
/**
@ -3893,10 +3983,10 @@ gst_play_set_subtitle_track_enabled (GstPlay * self, gboolean enabled)
g_mutex_lock (&self->lock);
self->subtitle_enabled = enabled;
GST_DEBUG_OBJECT (self, "Subtitle track is %s",
enabled ? "enabled" : "disabled");
gst_play_select_streams (self);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
}
/**