2016-11-05 08:18:49 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <gst/gst.h>
|
|
|
|
|
2022-12-13 17:42:11 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <TargetConditionals.h>
|
|
|
|
#endif
|
|
|
|
|
2016-11-05 08:18:49 +00:00
|
|
|
/* Structure to contain all our information, so we can pass it around */
|
2019-02-07 19:32:58 +00:00
|
|
|
typedef struct _CustomData
|
|
|
|
{
|
|
|
|
GstElement *playbin; /* Our one and only element */
|
2016-11-05 08:18:49 +00:00
|
|
|
|
2019-02-07 19:32:58 +00:00
|
|
|
gint n_video; /* Number of embedded video streams */
|
|
|
|
gint n_audio; /* Number of embedded audio streams */
|
|
|
|
gint n_text; /* Number of embedded subtitle streams */
|
2016-11-05 08:18:49 +00:00
|
|
|
|
2019-02-07 19:32:58 +00:00
|
|
|
gint current_video; /* Currently playing video stream */
|
|
|
|
gint current_audio; /* Currently playing audio stream */
|
|
|
|
gint current_text; /* Currently playing subtitle stream */
|
2016-11-05 08:18:49 +00:00
|
|
|
|
2019-02-07 19:32:58 +00:00
|
|
|
GMainLoop *main_loop; /* GLib's Main Loop */
|
2016-11-05 08:18:49 +00:00
|
|
|
} CustomData;
|
|
|
|
|
|
|
|
/* playbin flags */
|
2019-02-07 19:32:58 +00:00
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
GST_PLAY_FLAG_VIDEO = (1 << 0), /* We want video output */
|
|
|
|
GST_PLAY_FLAG_AUDIO = (1 << 1), /* We want audio output */
|
|
|
|
GST_PLAY_FLAG_TEXT = (1 << 2) /* We want subtitle output */
|
2016-11-05 08:18:49 +00:00
|
|
|
} GstPlayFlags;
|
|
|
|
|
|
|
|
/* Forward definition for the message and keyboard processing functions */
|
2019-02-07 19:32:58 +00:00
|
|
|
static gboolean handle_message (GstBus * bus, GstMessage * msg,
|
|
|
|
CustomData * data);
|
|
|
|
static gboolean handle_keyboard (GIOChannel * source, GIOCondition cond,
|
|
|
|
CustomData * data);
|
|
|
|
|
|
|
|
int
|
2022-12-13 17:42:11 +00:00
|
|
|
tutorial_main (int argc, char *argv[])
|
2019-02-07 19:32:58 +00:00
|
|
|
{
|
2016-11-05 08:18:49 +00:00
|
|
|
CustomData data;
|
|
|
|
GstBus *bus;
|
|
|
|
GstStateChangeReturn ret;
|
|
|
|
gint flags;
|
|
|
|
GIOChannel *io_stdin;
|
|
|
|
|
|
|
|
/* Initialize GStreamer */
|
|
|
|
gst_init (&argc, &argv);
|
|
|
|
|
|
|
|
/* Create the elements */
|
|
|
|
data.playbin = gst_element_factory_make ("playbin", "playbin");
|
|
|
|
|
|
|
|
if (!data.playbin) {
|
|
|
|
g_printerr ("Not all elements could be created.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the URI to play */
|
2019-02-07 19:32:58 +00:00
|
|
|
g_object_set (data.playbin, "uri",
|
2023-04-06 17:28:33 +00:00
|
|
|
"https://gstreamer.freedesktop.org/data/media/sintel_cropped_multilingual.webm",
|
2019-02-07 19:32:58 +00:00
|
|
|
NULL);
|
2016-11-05 08:18:49 +00:00
|
|
|
|
|
|
|
/* Set flags to show Audio and Video but ignore Subtitles */
|
|
|
|
g_object_get (data.playbin, "flags", &flags, NULL);
|
|
|
|
flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO;
|
|
|
|
flags &= ~GST_PLAY_FLAG_TEXT;
|
|
|
|
g_object_set (data.playbin, "flags", flags, NULL);
|
|
|
|
|
|
|
|
/* Set connection speed. This will affect some internal decisions of playbin */
|
|
|
|
g_object_set (data.playbin, "connection-speed", 56, NULL);
|
|
|
|
|
|
|
|
/* Add a bus watch, so we get notified when a message arrives */
|
|
|
|
bus = gst_element_get_bus (data.playbin);
|
2019-02-07 19:32:58 +00:00
|
|
|
gst_bus_add_watch (bus, (GstBusFunc) handle_message, &data);
|
2016-11-05 08:18:49 +00:00
|
|
|
|
|
|
|
/* Add a keyboard watch so we get notified of keystrokes */
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
|
|
|
|
#else
|
|
|
|
io_stdin = g_io_channel_unix_new (fileno (stdin));
|
|
|
|
#endif
|
2019-02-07 19:32:58 +00:00
|
|
|
g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, &data);
|
2016-11-05 08:18:49 +00:00
|
|
|
|
|
|
|
/* Start playing */
|
|
|
|
ret = gst_element_set_state (data.playbin, GST_STATE_PLAYING);
|
|
|
|
if (ret == GST_STATE_CHANGE_FAILURE) {
|
|
|
|
g_printerr ("Unable to set the pipeline to the playing state.\n");
|
|
|
|
gst_object_unref (data.playbin);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a GLib Main Loop and set it to run */
|
|
|
|
data.main_loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
g_main_loop_run (data.main_loop);
|
|
|
|
|
|
|
|
/* Free resources */
|
|
|
|
g_main_loop_unref (data.main_loop);
|
|
|
|
g_io_channel_unref (io_stdin);
|
|
|
|
gst_object_unref (bus);
|
|
|
|
gst_element_set_state (data.playbin, GST_STATE_NULL);
|
|
|
|
gst_object_unref (data.playbin);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extract some metadata from the streams and print it on the screen */
|
2019-02-07 19:32:58 +00:00
|
|
|
static void
|
|
|
|
analyze_streams (CustomData * data)
|
|
|
|
{
|
2016-11-05 08:18:49 +00:00
|
|
|
gint i;
|
|
|
|
GstTagList *tags;
|
|
|
|
gchar *str;
|
|
|
|
guint rate;
|
|
|
|
|
|
|
|
/* Read some properties */
|
|
|
|
g_object_get (data->playbin, "n-video", &data->n_video, NULL);
|
|
|
|
g_object_get (data->playbin, "n-audio", &data->n_audio, NULL);
|
|
|
|
g_object_get (data->playbin, "n-text", &data->n_text, NULL);
|
|
|
|
|
|
|
|
g_print ("%d video stream(s), %d audio stream(s), %d text stream(s)\n",
|
2019-02-07 19:32:58 +00:00
|
|
|
data->n_video, data->n_audio, data->n_text);
|
2016-11-05 08:18:49 +00:00
|
|
|
|
|
|
|
g_print ("\n");
|
|
|
|
for (i = 0; i < data->n_video; i++) {
|
|
|
|
tags = NULL;
|
|
|
|
/* Retrieve the stream's video tags */
|
|
|
|
g_signal_emit_by_name (data->playbin, "get-video-tags", i, &tags);
|
|
|
|
if (tags) {
|
|
|
|
g_print ("video stream %d:\n", i);
|
|
|
|
gst_tag_list_get_string (tags, GST_TAG_VIDEO_CODEC, &str);
|
|
|
|
g_print (" codec: %s\n", str ? str : "unknown");
|
|
|
|
g_free (str);
|
|
|
|
gst_tag_list_unref (tags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_print ("\n");
|
|
|
|
for (i = 0; i < data->n_audio; i++) {
|
|
|
|
tags = NULL;
|
|
|
|
/* Retrieve the stream's audio tags */
|
|
|
|
g_signal_emit_by_name (data->playbin, "get-audio-tags", i, &tags);
|
|
|
|
if (tags) {
|
|
|
|
g_print ("audio stream %d:\n", i);
|
|
|
|
if (gst_tag_list_get_string (tags, GST_TAG_AUDIO_CODEC, &str)) {
|
|
|
|
g_print (" codec: %s\n", str);
|
|
|
|
g_free (str);
|
|
|
|
}
|
|
|
|
if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
|
|
|
|
g_print (" language: %s\n", str);
|
|
|
|
g_free (str);
|
|
|
|
}
|
|
|
|
if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &rate)) {
|
|
|
|
g_print (" bitrate: %d\n", rate);
|
|
|
|
}
|
|
|
|
gst_tag_list_unref (tags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_print ("\n");
|
|
|
|
for (i = 0; i < data->n_text; i++) {
|
|
|
|
tags = NULL;
|
|
|
|
/* Retrieve the stream's subtitle tags */
|
|
|
|
g_signal_emit_by_name (data->playbin, "get-text-tags", i, &tags);
|
|
|
|
if (tags) {
|
|
|
|
g_print ("subtitle stream %d:\n", i);
|
|
|
|
if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
|
|
|
|
g_print (" language: %s\n", str);
|
|
|
|
g_free (str);
|
|
|
|
}
|
|
|
|
gst_tag_list_unref (tags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_get (data->playbin, "current-video", &data->current_video, NULL);
|
|
|
|
g_object_get (data->playbin, "current-audio", &data->current_audio, NULL);
|
|
|
|
g_object_get (data->playbin, "current-text", &data->current_text, NULL);
|
|
|
|
|
|
|
|
g_print ("\n");
|
2019-02-07 19:32:58 +00:00
|
|
|
g_print
|
|
|
|
("Currently playing video stream %d, audio stream %d and text stream %d\n",
|
|
|
|
data->current_video, data->current_audio, data->current_text);
|
|
|
|
g_print
|
|
|
|
("Type any number and hit ENTER to select a different audio stream\n");
|
2016-11-05 08:18:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Process messages from GStreamer */
|
2019-02-07 19:32:58 +00:00
|
|
|
static gboolean
|
|
|
|
handle_message (GstBus * bus, GstMessage * msg, CustomData * data)
|
|
|
|
{
|
2016-11-05 08:18:49 +00:00
|
|
|
GError *err;
|
|
|
|
gchar *debug_info;
|
|
|
|
|
|
|
|
switch (GST_MESSAGE_TYPE (msg)) {
|
|
|
|
case GST_MESSAGE_ERROR:
|
|
|
|
gst_message_parse_error (msg, &err, &debug_info);
|
2019-02-07 19:32:58 +00:00
|
|
|
g_printerr ("Error received from element %s: %s\n",
|
|
|
|
GST_OBJECT_NAME (msg->src), err->message);
|
|
|
|
g_printerr ("Debugging information: %s\n",
|
|
|
|
debug_info ? debug_info : "none");
|
2016-11-05 08:18:49 +00:00
|
|
|
g_clear_error (&err);
|
|
|
|
g_free (debug_info);
|
|
|
|
g_main_loop_quit (data->main_loop);
|
|
|
|
break;
|
|
|
|
case GST_MESSAGE_EOS:
|
|
|
|
g_print ("End-Of-Stream reached.\n");
|
|
|
|
g_main_loop_quit (data->main_loop);
|
|
|
|
break;
|
2019-02-07 19:32:58 +00:00
|
|
|
case GST_MESSAGE_STATE_CHANGED:{
|
2016-11-05 08:18:49 +00:00
|
|
|
GstState old_state, new_state, pending_state;
|
2019-02-07 19:32:58 +00:00
|
|
|
gst_message_parse_state_changed (msg, &old_state, &new_state,
|
|
|
|
&pending_state);
|
2016-11-05 08:18:49 +00:00
|
|
|
if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin)) {
|
|
|
|
if (new_state == GST_STATE_PLAYING) {
|
|
|
|
/* Once we are in the playing state, analyze the streams */
|
|
|
|
analyze_streams (data);
|
|
|
|
}
|
|
|
|
}
|
2019-02-07 19:32:58 +00:00
|
|
|
}
|
|
|
|
break;
|
2016-11-05 08:18:49 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We want to keep receiving messages */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process keyboard input */
|
2019-02-07 19:32:58 +00:00
|
|
|
static gboolean
|
|
|
|
handle_keyboard (GIOChannel * source, GIOCondition cond, CustomData * data)
|
|
|
|
{
|
2016-11-05 08:18:49 +00:00
|
|
|
gchar *str = NULL;
|
|
|
|
|
2019-02-07 19:32:58 +00:00
|
|
|
if (g_io_channel_read_line (source, &str, NULL, NULL,
|
|
|
|
NULL) == G_IO_STATUS_NORMAL) {
|
2016-11-05 08:18:49 +00:00
|
|
|
int index = g_ascii_strtoull (str, NULL, 0);
|
|
|
|
if (index < 0 || index >= data->n_audio) {
|
|
|
|
g_printerr ("Index out of bounds\n");
|
|
|
|
} else {
|
|
|
|
/* If the input was a valid audio stream index, set the current audio stream */
|
|
|
|
g_print ("Setting current audio stream to %d\n", index);
|
|
|
|
g_object_set (data->playbin, "current-audio", index, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_free (str);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2022-12-13 17:42:11 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
main (int argc, char *argv[])
|
|
|
|
{
|
|
|
|
#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
|
|
|
|
return gst_macos_main (tutorial_main, argc, argv, NULL);
|
|
|
|
#else
|
|
|
|
return tutorial_main (argc, argv);
|
|
|
|
#endif
|
|
|
|
}
|