From a8e05cc9cc66bb55868d5950321d7e7f33312eca Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Thu, 16 Mar 2017 17:52:04 +0900 Subject: gst-play: Support track change on playbin3 * playbin3 does not support {current,n}-{audio,video,text} properties, and they were replaced by GstStreams API. So, GstStreams API and select-stream event should be used for track change in case of playbin3. see also https://bugzilla.gnome.org/show_bug.cgi?id=769079 * By using commend line option "--use-playbin3", gst-play will use playbin3 regardless of "USE_PLAYBIN" env variable. https://bugzilla.gnome.org/show_bug.cgi?id=775469 --- tools/gst-play.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 266 insertions(+), 17 deletions(-) (limited to 'tools') diff --git a/tools/gst-play.c b/tools/gst-play.c index 88c51f940..fcca5b7de 100644 --- a/tools/gst-play.c +++ b/tools/gst-play.c @@ -72,6 +72,14 @@ typedef struct GstElement *playbin; + /* playbin3 variables */ + gboolean is_playbin3; + GstStreamCollection *collection; + gchar *cur_audio_sid; + gchar *cur_video_sid; + gchar *cur_text_sid; + GMutex selection_lock; + GMainLoop *loop; guint bus_watch; guint timeout; @@ -139,12 +147,18 @@ gst_play_printf (const gchar * format, ...) static GstPlay * play_new (gchar ** uris, const gchar * audio_sink, const gchar * video_sink, gboolean gapless, gdouble initial_volume, gboolean verbose, - const gchar * flags_string) + const gchar * flags_string, gboolean use_playbin3) { GstElement *sink, *playbin; GstPlay *play; - playbin = gst_element_factory_make ("playbin", "playbin"); + + if (use_playbin3) { + playbin = gst_element_factory_make ("playbin3", "playbin"); + } else { + playbin = gst_element_factory_make ("playbin", "playbin"); + } + if (playbin == NULL) return NULL; @@ -156,6 +170,16 @@ play_new (gchar ** uris, const gchar * audio_sink, const gchar * video_sink, play->playbin = playbin; + if (use_playbin3) { + play->is_playbin3 = TRUE; + } else { + const gchar *env = g_getenv ("USE_PLAYBIN3"); + if (env && g_str_has_prefix (env, "1")) + play->is_playbin3 = TRUE; + } + + g_mutex_init (&play->selection_lock); + if (audio_sink != NULL) { if (strchr (audio_sink, ' ') != NULL) sink = gst_parse_bin_from_description (audio_sink, TRUE, NULL); @@ -244,6 +268,15 @@ play_free (GstPlay * play) g_main_loop_unref (play->loop); g_strfreev (play->uris); + + if (play->collection) + gst_object_unref (play->collection); + g_free (play->cur_audio_sid); + g_free (play->cur_video_sid); + g_free (play->cur_text_sid); + + g_mutex_clear (&play->selection_lock); + g_free (play); } @@ -498,6 +531,63 @@ play_bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data) g_free (val_str); break; } + case GST_MESSAGE_STREAM_COLLECTION: + { + GstStreamCollection *collection = NULL; + gst_message_parse_stream_collection (msg, &collection); + + if (collection) { + g_mutex_lock (&play->selection_lock); + gst_object_replace ((GstObject **) & play->collection, + (GstObject *) collection); + g_mutex_unlock (&play->selection_lock); + } + break; + } + case GST_MESSAGE_STREAMS_SELECTED: + { + GstStreamCollection *collection = NULL; + guint i, len; + + gst_message_parse_streams_selected (msg, &collection); + if (collection) { + g_mutex_lock (&play->selection_lock); + gst_object_replace ((GstObject **) & play->collection, + (GstObject *) collection); + + /* Free all last stream-ids */ + g_free (play->cur_audio_sid); + g_free (play->cur_video_sid); + g_free (play->cur_text_sid); + play->cur_audio_sid = NULL; + play->cur_video_sid = NULL; + play->cur_text_sid = NULL; + + len = gst_message_streams_selected_get_size (msg); + for (i = 0; i < len; i++) { + GstStream *stream = gst_message_streams_selected_get_stream (msg, i); + if (stream) { + GstStreamType type = gst_stream_get_stream_type (stream); + const gchar *stream_id = gst_stream_get_stream_id (stream); + + if (type & GST_STREAM_TYPE_AUDIO) { + play->cur_audio_sid = g_strdup (stream_id); + } else if (type & GST_STREAM_TYPE_VIDEO) { + play->cur_video_sid = g_strdup (stream_id); + } else if (type & GST_STREAM_TYPE_TEXT) { + play->cur_text_sid = g_strdup (stream_id); + } else { + g_print ("Unknown stream type with stream-id %s", stream_id); + } + gst_object_unref (stream); + } + } + + gst_object_unref (collection); + g_mutex_unlock (&play->selection_lock); + } + break; + } default: if (gst_is_missing_plugin_message (msg)) { gchar *desc; @@ -943,6 +1033,44 @@ play_switch_trick_mode (GstPlay * play) } } +static GstStream * +play_get_nth_stream_in_collection (GstPlay * play, guint index, + GstPlayTrackType track_type) +{ + guint len, i, n_streams = 0; + GstStreamType target_type; + + switch (track_type) { + case GST_PLAY_TRACK_TYPE_AUDIO: + target_type = GST_STREAM_TYPE_AUDIO; + break; + case GST_PLAY_TRACK_TYPE_VIDEO: + target_type = GST_STREAM_TYPE_VIDEO; + break; + case GST_PLAY_TRACK_TYPE_SUBTITLE: + target_type = GST_STREAM_TYPE_TEXT; + break; + default: + return NULL; + } + + len = gst_stream_collection_get_size (play->collection); + + for (i = 0; i < len; i++) { + GstStream *stream = gst_stream_collection_get_stream (play->collection, i); + GstStreamType type = gst_stream_get_stream_type (stream); + + if (type & target_type) { + if (index == n_streams) + return stream; + + n_streams++; + } + } + + return NULL; +} + static void play_cycle_track_selection (GstPlay * play, GstPlayTrackType track_type) { @@ -950,6 +1078,48 @@ play_cycle_track_selection (GstPlay * play, GstPlayTrackType track_type) gint cur = -1, n = -1; guint flag, cur_flags; + /* playbin3 variables */ + GList *selected_streams = NULL; + gint cur_audio_idx = -1, cur_video_idx = -1, cur_text_idx = -1; + gint nb_audio = 0, nb_video = 0, nb_text = 0; + guint len, i; + + g_mutex_lock (&play->selection_lock); + if (play->is_playbin3) { + if (!play->collection) { + g_print ("No stream-collection\n"); + g_mutex_unlock (&play->selection_lock); + return; + } + + /* Check the total number of streams of each type */ + len = gst_stream_collection_get_size (play->collection); + for (i = 0; i < len; i++) { + GstStream *stream = + gst_stream_collection_get_stream (play->collection, i); + if (stream) { + GstStreamType type = gst_stream_get_stream_type (stream); + const gchar *sid = gst_stream_get_stream_id (stream); + + if (type & GST_STREAM_TYPE_AUDIO) { + if (play->cur_audio_sid && !g_strcmp0 (play->cur_audio_sid, sid)) + cur_audio_idx = nb_audio; + nb_audio++; + } else if (type & GST_STREAM_TYPE_VIDEO) { + if (play->cur_video_sid && !g_strcmp0 (play->cur_video_sid, sid)) + cur_video_idx = nb_video; + nb_video++; + } else if (type & GST_STREAM_TYPE_TEXT) { + if (play->cur_text_sid && !g_strcmp0 (play->cur_text_sid, sid)) + cur_text_idx = nb_text; + nb_text++; + } else { + g_print ("Unknown stream type with stream-id %s", sid); + } + } + } + } + switch (track_type) { case GST_PLAY_TRACK_TYPE_AUDIO: prop_get = "get-audio-tags"; @@ -957,6 +1127,18 @@ play_cycle_track_selection (GstPlay * play, GstPlayTrackType track_type) prop_n = "n-audio"; name = "audio"; flag = 0x2; + if (play->is_playbin3) { + n = nb_audio; + cur = cur_audio_idx; + if (play->cur_video_sid) { + selected_streams = + g_list_append (selected_streams, play->cur_video_sid); + } + if (play->cur_text_sid) { + selected_streams = + g_list_append (selected_streams, play->cur_text_sid); + } + } break; case GST_PLAY_TRACK_TYPE_VIDEO: prop_get = "get-video-tags"; @@ -964,6 +1146,18 @@ play_cycle_track_selection (GstPlay * play, GstPlayTrackType track_type) prop_n = "n-video"; name = "video"; flag = 0x1; + if (play->is_playbin3) { + n = nb_video; + cur = cur_video_idx; + if (play->cur_audio_sid) { + selected_streams = + g_list_append (selected_streams, play->cur_audio_sid); + } + if (play->cur_text_sid) { + selected_streams = + g_list_append (selected_streams, play->cur_text_sid); + } + } break; case GST_PLAY_TRACK_TYPE_SUBTITLE: prop_get = "get-text-tags"; @@ -971,30 +1165,54 @@ play_cycle_track_selection (GstPlay * play, GstPlayTrackType track_type) prop_n = "n-text"; name = "subtitle"; flag = 0x4; + if (play->is_playbin3) { + n = nb_text; + cur = cur_text_idx; + if (play->cur_audio_sid) { + selected_streams = + g_list_append (selected_streams, play->cur_audio_sid); + } + if (play->cur_video_sid) { + selected_streams = + g_list_append (selected_streams, play->cur_video_sid); + } + } break; default: return; } - g_object_get (play->playbin, prop_cur, &cur, prop_n, &n, "flags", &cur_flags, - NULL); + if (play->is_playbin3) { + if (n > 0) { + if (cur < 0) + cur = 0; + else + cur = (cur + 1) % (n + 1); + } + } else { + g_object_get (play->playbin, prop_cur, &cur, prop_n, &n, "flags", + &cur_flags, NULL); + + if (!(cur_flags & flag)) + cur = 0; + else + cur = (cur + 1) % (n + 1); + } if (n < 1) { g_print ("No %s tracks.\n", name); + g_mutex_unlock (&play->selection_lock); } else { gchar *lcode = NULL, *lname = NULL; const gchar *lang = NULL; GstTagList *tags = NULL; - if (!(cur_flags & flag)) - cur = 0; - else - cur = (cur + 1) % (n + 1); - if (cur >= n && track_type != GST_PLAY_TRACK_TYPE_VIDEO) { cur = -1; g_print ("Disabling %s. \n", name); - if (cur_flags & flag) { + if (play->is_playbin3) { + /* Just make it empty for the track type */ + } else if (cur_flags & flag) { cur_flags &= ~flag; g_object_set (play->playbin, "flags", cur_flags, NULL); } @@ -1002,11 +1220,27 @@ play_cycle_track_selection (GstPlay * play, GstPlayTrackType track_type) /* For video we only want to switch between streams, not disable it altogether */ if (cur >= n) cur = 0; - if (!(cur_flags & flag) && track_type != GST_PLAY_TRACK_TYPE_VIDEO) { - cur_flags |= flag; - g_object_set (play->playbin, "flags", cur_flags, NULL); + + if (play->is_playbin3) { + GstStream *stream; + + stream = play_get_nth_stream_in_collection (play, cur, track_type); + if (stream) { + selected_streams = g_list_append (selected_streams, + (gchar *) gst_stream_get_stream_id (stream)); + tags = gst_stream_get_tags (stream); + } else { + g_print ("Collection has no stream for track %d of %d.\n", + cur + 1, n); + } + } else { + if (!(cur_flags & flag) && track_type != GST_PLAY_TRACK_TYPE_VIDEO) { + cur_flags |= flag; + g_object_set (play->playbin, "flags", cur_flags, NULL); + } + g_signal_emit_by_name (play->playbin, prop_get, cur, &tags); } - g_signal_emit_by_name (play->playbin, prop_get, cur, &tags); + if (tags != NULL) { if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &lcode)) lang = gst_tag_get_language_name (lcode); @@ -1020,10 +1254,20 @@ play_cycle_track_selection (GstPlay * play, GstPlayTrackType track_type) else g_print ("Switching to %s track %d of %d.\n", name, cur + 1, n); } - g_object_set (play->playbin, prop_cur, cur, NULL); g_free (lcode); g_free (lname); + g_mutex_unlock (&play->selection_lock); + + if (play->is_playbin3) { + gst_element_send_event (play->playbin, + gst_event_new_select_streams (selected_streams)); + } else { + g_object_set (play->playbin, prop_cur, cur, NULL); + } } + + if (selected_streams) + g_list_free (selected_streams); } static void @@ -1184,6 +1428,7 @@ main (int argc, char **argv) GError *err = NULL; GOptionContext *ctx; gchar *playlist_file = NULL; + gboolean use_playbin3 = FALSE; GOptionEntry options[] = { {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, N_("Output status information and property notifications"), NULL}, @@ -1209,6 +1454,10 @@ main (int argc, char **argv) N_("Playlist file containing input media files"), NULL}, {"quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Do not print any output (apart from errors)"), NULL}, + {"use-playbin3", 0, 0, G_OPTION_ARG_NONE, &use_playbin3, + N_("Use playbin3 pipeline") + N_("(default varies depending on 'USE_PLAYBIN' env variable)"), + NULL}, {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL}, {NULL} }; @@ -1314,8 +1563,8 @@ main (int argc, char **argv) shuffle_uris (uris, num); /* prepare */ - play = - play_new (uris, audio_sink, video_sink, gapless, volume, verbose, flags); + play = play_new (uris, audio_sink, video_sink, gapless, volume, verbose, + flags, use_playbin3); if (play == NULL) { g_printerr -- cgit v1.2.1