Skip to content

Instantly share code, notes, and snippets.

@chichunchen
Last active July 25, 2018 21:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chichunchen/220e2bcac05ba866241c7e301340aa44 to your computer and use it in GitHub Desktop.
Save chichunchen/220e2bcac05ba866241c7e301340aa44 to your computer and use it in GitHub Desktop.
Simple gst-play example.
#include <gst/gst.h>
#include <stdlib.h>
typedef struct
{
gchar **uris;
guint num_uris;
gint cur_idx;
GstElement *playbin;
GMainLoop *loop;
guint bus_watch;
guint timeout;
/* missing plugin messages */
GList *missing;
gboolean buffering;
gboolean is_live;
/* configuration */
gboolean gapless;
} GstPlay;
static gboolean play_bus_msg (GstBus * bus, GstMessage * msg, gpointer data);
static gboolean play_next (GstPlay * play);
static gboolean play_timeout (gpointer user_data);
static void play_about_to_finish (GstElement * playbin, gpointer user_data);
static void play_reset (GstPlay * play);
/* returns TRUE if something was installed and we should restart playback */
static gboolean
play_install_missing_plugins (GstPlay * play)
{
/* FIXME: implement: try to install any missing plugins we haven't
* tried to install before */
return FALSE;
}
static GstPlay *
play_new (gchar ** uris, const gchar * audio_sink, const gchar * video_sink,
gboolean gapless)
{
GstElement *sink;
GstPlay *play;
play = g_new0 (GstPlay, 1);
play->uris = uris;
play->num_uris = g_strv_length (uris);
play->cur_idx = -1;
play->playbin = gst_element_factory_make ("playbin", "playbin");
if (audio_sink != NULL) {
sink = gst_element_factory_make (audio_sink, NULL);
if (sink != NULL)
g_object_set (play->playbin, "audio-sink", sink, NULL);
else
g_warning ("Couldn't create specified audio sink '%s'", audio_sink);
}
if (video_sink != NULL) {
sink = gst_element_factory_make (video_sink, NULL);
if (sink != NULL)
g_object_set (play->playbin, "video-sink", sink, NULL);
else
g_warning ("Couldn't create specified video sink '%s'", video_sink);
}
play->loop = g_main_loop_new (NULL, FALSE);
play->bus_watch = gst_bus_add_watch (GST_ELEMENT_BUS (play->playbin),
play_bus_msg, play);
/* FIXME: make configurable incl. 0 for disable */
play->timeout = g_timeout_add (100, play_timeout, play);
play->missing = NULL;
play->buffering = FALSE;
play->is_live = FALSE;
play->gapless = gapless;
if (gapless) {
g_signal_connect (play->playbin, "about-to-finish",
G_CALLBACK (play_about_to_finish), play);
}
return play;
}
static gboolean
play_bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data)
{
GstPlay *play = user_data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ASYNC_DONE:
g_print ("Prerolled.\r");
if (play->missing != NULL && play_install_missing_plugins (play)) {
g_print ("New plugins installed, trying again...\n");
--play->cur_idx;
play_next (play);
}
break;
case GST_MESSAGE_BUFFERING:{
gint percent;
if (!play->buffering)
g_print ("\n");
gst_message_parse_buffering (msg, &percent);
//g_print ("%s %d%% \r", _("Buffering..."), percent);
/* no state management needed for live pipelines */
if (play->is_live)
break;
if (percent == 100) {
/* a 100% message means buffering is done */
if (play->buffering) {
play->buffering = FALSE;
gst_element_set_state (play->playbin, GST_STATE_PLAYING);
}
} else {
/* buffering... */
if (!play->buffering) {
gst_element_set_state (play->playbin, GST_STATE_PAUSED);
play->buffering = TRUE;
}
}
break;
}
case GST_MESSAGE_LATENCY:
g_print ("Redistribute latency...\n");
gst_bin_recalculate_latency (GST_BIN (play->playbin));
break;
case GST_MESSAGE_REQUEST_STATE:{
GstState state;
gchar *name;
name = gst_object_get_path_string (GST_MESSAGE_SRC (msg));
gst_message_parse_request_state (msg, &state);
g_print ("Setting state to %s as requested by %s...\n",
gst_element_state_get_name (state), name);
gst_element_set_state (play->playbin, state);
g_free (name);
break;
}
case GST_MESSAGE_EOS:
/* print final position at end */
play_timeout (play);
g_print ("\n");
/* and switch to next item in list */
if (!play_next (play)) {
g_print ("Reached end of play list.\n");
g_main_loop_quit (play->loop);
}
break;
case GST_MESSAGE_WARNING:{
GError *err;
gchar *dbg = NULL;
gst_message_parse_warning (msg, &err, &dbg);
g_printerr ("WARNING %s\n", err->message);
if (dbg != NULL)
g_printerr ("WARNING debug information: %s\n", dbg);
g_error_free (err);
g_free (dbg);
break;
}
case GST_MESSAGE_ERROR:{
GError *err;
gchar *dbg;
gst_message_parse_error (msg, &err, &dbg);
g_printerr ("ERROR %s for %s\n", err->message, play->uris[play->cur_idx]);
if (dbg != NULL)
g_printerr ("ERROR debug information: %s\n", dbg);
g_error_free (err);
g_free (dbg);
/* flush any other error messages from the bus and clean up */
gst_element_set_state (play->playbin, GST_STATE_NULL);
if (play->missing != NULL && play_install_missing_plugins (play)) {
g_print ("New plugins installed, trying again...\n");
--play->cur_idx;
play_next (play);
break;
}
/* try next item in list then */
if (!play_next (play)) {
g_print ("Reached end of play list.\n");
g_main_loop_quit (play->loop);
}
break;
}
default:
g_print("gst_is_missing_plugin msg\n");
break;
}
return TRUE;
}
static gboolean
play_timeout (gpointer user_data)
{
GstPlay *play = user_data;
gint64 pos = -1, dur = -1;
if (play->buffering)
return TRUE;
gst_element_query_position (play->playbin, GST_FORMAT_TIME, &pos);
gst_element_query_duration (play->playbin, GST_FORMAT_TIME, &dur);
if (pos >= 0 && dur > 0) {
gchar dstr[32], pstr[32];
/* FIXME: pretty print in nicer format */
g_snprintf (pstr, 32, "%" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
pstr[9] = '\0';
g_snprintf (dstr, 32, "%" GST_TIME_FORMAT, GST_TIME_ARGS (dur));
dstr[9] = '\0';
g_print ("%s / %s\r", pstr, dstr);
}
return TRUE;
}
static gchar *
play_uri_get_display_name (GstPlay * play, const gchar * uri)
{
gchar *loc;
if (gst_uri_has_protocol (uri, "file")) {
loc = g_filename_from_uri (uri, NULL, NULL);
} else if (gst_uri_has_protocol (uri, "pushfile")) {
loc = g_filename_from_uri (uri + 4, NULL, NULL);
} else {
loc = g_strdup (uri);
}
/* Maybe additionally use glib's filename to display name function */
return loc;
}
/* returns FALSE if we have reached the end of the playlist */
static gboolean
play_next (GstPlay * play)
{
GstStateChangeReturn sret;
const gchar *next_uri;
gchar *loc;
if (++play->cur_idx >= play->num_uris)
return FALSE;
gst_element_set_state (play->playbin, GST_STATE_READY);
play_reset (play);
next_uri = play->uris[play->cur_idx];
loc = play_uri_get_display_name (play, next_uri);
g_print ("Now playing %s\n", loc);
g_free (loc);
g_object_set (play->playbin, "uri", next_uri, NULL);
sret = gst_element_set_state (play->playbin, GST_STATE_PLAYING);
switch (sret) {
case GST_STATE_CHANGE_FAILURE:
/* ignore, we should get an error message posted on the bus */
break;
case GST_STATE_CHANGE_NO_PREROLL:
g_print ("Pipeline is live.\n");
play->is_live = TRUE;
break;
case GST_STATE_CHANGE_ASYNC:
g_print ("Prerolling...\r");
break;
default:
break;
}
return TRUE;
}
static void
play_about_to_finish (GstElement * playbin, gpointer user_data)
{
GstPlay *play = user_data;
const gchar *next_uri;
gchar *loc;
guint next_idx;
if (!play->gapless)
return;
next_idx = play->cur_idx + 1;
if (next_idx >= play->num_uris)
return;
next_uri = play->uris[next_idx];
loc = play_uri_get_display_name (play, next_uri);
g_print ("About to finish, preparing next title: %s\n", loc);
g_free (loc);
g_object_set (play->playbin, "uri", next_uri, NULL);
play->cur_idx = next_idx;
}
static void
do_play (GstPlay * play)
{
gint i;
/* dump playlist */
for (i = 0; i < play->num_uris; ++i)
GST_INFO ("%4u : %s", i, play->uris[i]);
if (!play_next (play))
return;
g_main_loop_run (play->loop);
}
/* reset for new file/stream */
static void
play_reset (GstPlay * play)
{
g_list_foreach (play->missing, (GFunc) gst_message_unref, NULL);
play->missing = NULL;
play->buffering = FALSE;
play->is_live = FALSE;
}
static void
add_to_playlist (GPtrArray * playlist, const gchar * filename)
{
GDir *dir;
gchar *uri;
if (gst_uri_is_valid (filename)) {
g_ptr_array_add (playlist, g_strdup (filename));
return;
}
if ((dir = g_dir_open (filename, 0, NULL))) {
const gchar *entry;
/* FIXME: sort entries for each directory? */
while ((entry = g_dir_read_name (dir))) {
gchar *path;
path = g_strconcat (filename, G_DIR_SEPARATOR_S, entry, NULL);
add_to_playlist (playlist, path);
g_free (path);
}
g_dir_close (dir);
return;
}
uri = gst_filename_to_uri (filename, NULL);
if (uri != NULL)
g_ptr_array_add (playlist, uri);
else
g_warning ("Could not make URI out of filename '%s'", filename);
}
static void
play_free (GstPlay * play)
{
play_reset (play);
gst_element_set_state (play->playbin, GST_STATE_NULL);
gst_object_unref (play->playbin);
g_source_remove (play->bus_watch);
g_source_remove (play->timeout);
g_main_loop_unref (play->loop);
//g_strfreev (play->uris);
g_free (play);
}
int main(int argc, char **argv) {
GstPlay *play;
GPtrArray *playlist;
gboolean gapless = TRUE;
gchar **filenames = argv + 1;
gchar *audio_sink = NULL;
gchar *video_sink = NULL;
gchar **uris;
guint num, i;
GError *err = NULL;
gst_init (&argc, &argv);
if (filenames == NULL || *filenames == NULL) {
g_printerr("You must provide at least one filename or URI to play.\n");
return 1;
}
playlist = g_ptr_array_new ();
/* fill playlist */
num = g_strv_length (filenames);
for (i = 0; i < num; ++i) {
GST_LOG ("command line argument: %s", filenames[i]);
add_to_playlist (playlist, filenames[i]);
}
g_ptr_array_add (playlist, NULL);
/* play */
uris = (gchar **) g_ptr_array_free (playlist, FALSE);
play = play_new (filenames, audio_sink, video_sink, gapless);
do_play (play);
/* clean up */
play_free (play);
return 0;
}
@chichunchen
Copy link
Author

chichunchen commented Jul 25, 2018

Compile: gcc simple-gst-play.c -o simple-gst-play pkg-config --cflags --libs gstreamer-1.0`

Run: ./simple-gst-play <uri> <uri> ...

Example of running: ./simple-gst-play file:///home/nvidia/workspace/gstreamer-try/rhino-2k/output_1.mp4 file:///home/nvidia/workspace/gstreamer-try/rhino-2k/output_2.mp4 file:///home/nvidia/workspace/gstreamer-try/rhino-2k/output_3.mp4 file:///home/nvidia/workspace/gstreamer-try/rhino-2k/output_4.mp4 file:///home/nvidia/workspace/gstreamer-try/rhino-2k/output_5.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment