From 5a36b793d6747b960e53703aae69eef3bf52aa2a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 24 Nov 2013 01:08:48 +0000 Subject: [PATCH] tools: play: add --interactive switch and basic keyboard handling Only pause/play with spacebar for now. --- tools/Makefile.am | 4 +- tools/gst-play-kb.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/gst-play-kb.h | 35 +++++++++++++ tools/gst-play.c | 73 +++++++++++++++++++++++++-- 4 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 tools/gst-play-kb.c create mode 100644 tools/gst-play-kb.h diff --git a/tools/Makefile.am b/tools/Makefile.am index 3d0711e..a025cd0 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -8,7 +8,9 @@ bin_PROGRAMS = \ gst_discoverer_@GST_API_VERSION@_SOURCES = gst-discoverer.c -gst_play_@GST_API_VERSION@_SOURCES = gst-play.c +gst_play_@GST_API_VERSION@_SOURCES = gst-play.c gst-play-kb.c gst-play-kb.h + +noinst_HEADERS = gst-play-kb.h CLEANFILES = $(bin_SCRIPTS) $(bin_PROGRAMS) diff --git a/tools/gst-play-kb.c b/tools/gst-play-kb.c new file mode 100644 index 0000000..ad26e1d --- /dev/null +++ b/tools/gst-play-kb.c @@ -0,0 +1,142 @@ +/* GStreamer command line playback testing utility - keyboard handling helpers + * + * Copyright (C) 2013 Tim-Philipp Müller + * Copyright (C) 2013 Centricular Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gst-play-kb.h" + +#include +#include +#include + +#ifdef G_OS_UNIX +#include +#include +#endif + +#include + +/* This is all not thread-safe, but doesn't have to be really */ + +#ifdef G_OS_UNIX + +static struct termios term_settings; +static gboolean term_settings_saved = FALSE; +static GstPlayKbFunc kb_callback; +static gpointer kb_callback_data; +static gulong io_watch_id; + +static gboolean +gst_play_kb_io_cb (GIOChannel * ioc, GIOCondition cond, gpointer user_data) +{ + GIOStatus status; + + if (cond & G_IO_IN) { + gchar buf[16] = { 0, }; + gsize read; + + status = g_io_channel_read_chars (ioc, buf, sizeof (buf) - 1, &read, NULL); + if (status == G_IO_STATUS_ERROR) + return FALSE; + if (status == G_IO_STATUS_NORMAL) { + if (kb_callback) + kb_callback (buf, kb_callback_data); + } + } + + return TRUE; /* call us again */ +} + +gboolean +gst_play_kb_set_key_handler (GstPlayKbFunc kb_func, gpointer user_data) +{ + GIOChannel *ioc; + int flags; + + if (!isatty (STDIN_FILENO)) { + GST_INFO ("stdin is not connected to a terminal"); + return FALSE; + } + + if (io_watch_id > 0) { + g_source_remove (io_watch_id); + io_watch_id = 0; + } + + if (kb_func == NULL && term_settings_saved) { + /* restore terminal settings */ + if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &term_settings) == 0) + term_settings_saved = FALSE; + else + g_warning ("could not restore terminal attributes"); + + setvbuf (stdin, NULL, _IOLBF, 0); + } + + if (kb_func != NULL) { + struct termios new_settings; + + if (!term_settings_saved) { + if (tcgetattr (STDIN_FILENO, &term_settings) != 0) { + g_warning ("could not save terminal attributes"); + return FALSE; + } + term_settings_saved = TRUE; + + /* Echo off, canonical mode off, extended input processing off */ + new_settings = term_settings; + new_settings.c_lflag &= ~(ECHO | ICANON | IEXTEN); + + if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &new_settings) != 0) { + g_warning ("Could not set terminal state"); + return FALSE; + } + setvbuf (stdin, NULL, _IONBF, 0); + } + } + + ioc = g_io_channel_unix_new (STDIN_FILENO); + + /* make non-blocking */ + flags = g_io_channel_get_flags (ioc); + g_io_channel_set_flags (ioc, flags | G_IO_FLAG_NONBLOCK, NULL); + + io_watch_id = g_io_add_watch_full (ioc, G_PRIORITY_DEFAULT, G_IO_IN, + (GIOFunc) gst_play_kb_io_cb, user_data, NULL); + g_io_channel_unref (ioc); + + kb_callback = kb_func; + kb_callback_data = user_data; + + return TRUE; +} + +#else /* !G_OS_UNIX */ + +gboolean +gst_play_kb_set_key_handler (GstPlayKbFunc key_func, gpointer user_data) +{ + GST_FIXME ("Keyboard handling for this OS needs to be implemented"); +} + +#endif /* !G_OS_UNIX */ diff --git a/tools/gst-play-kb.h b/tools/gst-play-kb.h new file mode 100644 index 0000000..72087b5 --- /dev/null +++ b/tools/gst-play-kb.h @@ -0,0 +1,35 @@ +/* GStreamer command line playback testing utility - keyboard handling helpers + * + * Copyright (C) 2013 Tim-Philipp Müller + * Copyright (C) 2013 Centricular Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef __GST_PLAY_KB_INCLUDED__ +#define __GST_PLAY_KB_INCLUDED__ + +#include + +#define GST_PLAY_KB_KEY_UP "\033[A" +#define GST_PLAY_KB_KEY_DOWN "\033[B" +#define GST_PLAY_KB_KEY_RIGHT "\033[C" +#define GST_PLAY_KB_KEY_LEFT "\033[D" + +typedef void (*GstPlayKbFunc) (const gchar * kb_input, gpointer user_data); + +gboolean gst_play_kb_set_key_handler (GstPlayKbFunc kb_func, gpointer user_data); + +#endif /* __GST_PLAY_KB_INCLUDED__ */ diff --git a/tools/gst-play.c b/tools/gst-play.c index 15f7bdc..61b7c9a 100644 --- a/tools/gst-play.c +++ b/tools/gst-play.c @@ -27,6 +27,10 @@ #include #include #include +#include +#include + +#include "gst-play-kb.h" GST_DEBUG_CATEGORY (play_debug); #define GST_CAT_DEFAULT play_debug @@ -49,6 +53,8 @@ typedef struct gboolean buffering; gboolean is_live; + GstState desired_state; /* as per user interaction, PAUSED or PLAYING */ + /* configuration */ gboolean gapless; } GstPlay; @@ -101,6 +107,8 @@ play_new (gchar ** uris, const gchar * audio_sink, const gchar * video_sink, play->buffering = FALSE; play->is_live = FALSE; + play->desired_state = GST_STATE_PLAYING; + play->gapless = gapless; if (gapless) { g_signal_connect (play->playbin, "about-to-finish", @@ -177,7 +185,7 @@ play_bus_msg (GstBus * bus, GstMessage * msg, gpointer user_data) /* a 100% message means buffering is done */ if (play->buffering) { play->buffering = FALSE; - gst_element_set_state (play->playbin, GST_STATE_PLAYING); + gst_element_set_state (play->playbin, play->desired_state); } } else { /* buffering... */ @@ -276,6 +284,7 @@ play_timeout (gpointer user_data) { GstPlay *play = user_data; gint64 pos = -1, dur = -1; + gchar status[64] = { 0, }; if (play->buffering) return TRUE; @@ -283,6 +292,11 @@ play_timeout (gpointer user_data) gst_element_query_position (play->playbin, GST_FORMAT_TIME, &pos); gst_element_query_duration (play->playbin, GST_FORMAT_TIME, &dur); + if (play->desired_state == GST_STATE_PAUSED) + g_snprintf (status, sizeof (status), "Paused"); + else + memset (status, ' ', sizeof (status) - 1); + if (pos >= 0 && dur > 0) { gchar dstr[32], pstr[32]; @@ -291,7 +305,7 @@ play_timeout (gpointer user_data) pstr[9] = '\0'; g_snprintf (dstr, 32, "%" GST_TIME_FORMAT, GST_TIME_ARGS (dur)); dstr[9] = '\0'; - g_print ("%s / %s\r", pstr, dstr); + g_print ("%s / %s %s\r", pstr, dstr, status); } return TRUE; @@ -335,7 +349,7 @@ play_next (GstPlay * play) g_object_set (play->playbin, "uri", next_uri, NULL); - sret = gst_element_set_state (play->playbin, GST_STATE_PLAYING); + sret = gst_element_set_state (play->playbin, play->desired_state); switch (sret) { case GST_STATE_CHANGE_FAILURE: /* ignore, we should get an error message posted on the bus */ @@ -445,12 +459,52 @@ shuffle_uris (gchar ** uris, guint num) } } +static void +restore_terminal (void) +{ + gst_play_kb_set_key_handler (NULL, NULL); +} + +static void +toggle_paused (GstPlay * play) +{ + if (play->desired_state == GST_STATE_PLAYING) + play->desired_state = GST_STATE_PAUSED; + else + play->desired_state = GST_STATE_PLAYING; + + if (!play->buffering) { + gst_element_set_state (play->playbin, play->desired_state); + } else if (play->desired_state == GST_STATE_PLAYING) { + g_print ("\nWill play as soon as buffering finishes)\n"); + } +} + +static void +keyboard_cb (const gchar * key_input, gpointer user_data) +{ + GstPlay *play = (GstPlay *) user_data; + + switch (g_ascii_tolower (key_input[0])) { + case ' ': + toggle_paused (play); + break; + case 27: /* ESC */ + default: + GST_INFO ("keyboard input:"); + while (*key_input) + GST_INFO (" code %3d", *key_input++); + break; + } +} + int main (int argc, char **argv) { GstPlay *play; GPtrArray *playlist; gboolean print_version = FALSE; + gboolean interactive = FALSE; /* FIXME: maybe enable by default? */ gboolean gapless = FALSE; gboolean shuffle = FALSE; gchar **filenames = NULL; @@ -471,6 +525,8 @@ main (int argc, char **argv) N_("Enable gapless playback"), NULL}, {"shuffle", 0, 0, G_OPTION_ARG_NONE, &shuffle, N_("Shuffle playlist"), NULL}, + {"interactive", 0, 0, G_OPTION_ARG_NONE, &interactive, + N_("interactive"), NULL}, {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL}, {NULL} }; @@ -532,9 +588,18 @@ main (int argc, char **argv) if (shuffle) shuffle_uris (uris, num); - /* play */ + /* prepare */ play = play_new (uris, audio_sink, video_sink, gapless); + if (interactive) { + if (gst_play_kb_set_key_handler (keyboard_cb, play)) { + atexit (restore_terminal); + } else { + g_print ("Interactive keyboard handling in terminal not available.\n"); + } + } + + /* play */ do_play (play); /* clean up */ -- 2.7.4