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
This commit is contained in:
Seungha Yang 2017-03-16 17:52:04 +09:00 committed by Edward Hervey
parent b0c184e189
commit a8e05cc9cc

View file

@ -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 (n < 1) {
g_print ("No %s tracks.\n", name);
if (play->is_playbin3) {
if (n > 0) {
if (cur < 0)
cur = 0;
else
cur = (cur + 1) % (n + 1);
}
} else {
gchar *lcode = NULL, *lname = NULL;
const gchar *lang = NULL;
GstTagList *tags = NULL;
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 >= 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