gst-play: add keyboard shortcut to cycle through trick modes

Make "t" activate trick modes and cycle through the various
modes.
This commit is contained in:
Tim-Philipp Müller 2015-02-28 12:26:21 +00:00
parent a257fcf366
commit 0e9dac0cc7
2 changed files with 121 additions and 28 deletions

View file

@ -56,6 +56,9 @@ Increase/decrease playback rate
.B d .B d
Reverse playback direction Reverse playback direction
.TP 8 .TP 8
.B t
Cycle through trick modes
.TP 8
.B > .B >
Skip to next item in playlist Skip to next item in playlist
.TP 8 .TP 8

View file

@ -2,6 +2,7 @@
* *
* Copyright (C) 2013-2014 Tim-Philipp Müller <tim centricular net> * Copyright (C) 2013-2014 Tim-Philipp Müller <tim centricular net>
* Copyright (C) 2013 Collabora Ltd. * Copyright (C) 2013 Collabora Ltd.
* Copyright (C) 2015 Centricular Ltd
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -43,6 +44,16 @@
GST_DEBUG_CATEGORY (play_debug); GST_DEBUG_CATEGORY (play_debug);
#define GST_CAT_DEFAULT play_debug #define GST_CAT_DEFAULT play_debug
typedef enum
{
GST_PLAY_TRICK_MODE_NONE = 0,
GST_PLAY_TRICK_MODE_DEFAULT,
GST_PLAY_TRICK_MODE_DEFAULT_NO_AUDIO,
GST_PLAY_TRICK_MODE_KEY_UNITS,
GST_PLAY_TRICK_MODE_KEY_UNITS_NO_AUDIO,
GST_PLAY_TRICK_MODE_LAST
} GstPlayTrickMode;
typedef struct typedef struct
{ {
gchar **uris; gchar **uris;
@ -66,6 +77,7 @@ typedef struct
/* configuration */ /* configuration */
gboolean gapless; gboolean gapless;
GstPlayTrickMode trick_mode;
gdouble rate; gdouble rate;
} GstPlay; } GstPlay;
@ -169,6 +181,7 @@ play_new (gchar ** uris, const gchar * audio_sink, const gchar * video_sink,
play_set_relative_volume (play, initial_volume - 1.0); play_set_relative_volume (play, initial_volume - 1.0);
play->rate = 1.0; play->rate = 1.0;
play->trick_mode = GST_PLAY_TRICK_MODE_NONE;
return play; return play;
} }
@ -632,9 +645,11 @@ seek_failed:
} }
} }
static void static gboolean
change_rate (GstPlay * play, gdouble rate) play_set_rate_and_trick_mode (GstPlay * play, gdouble rate,
GstPlayTrickMode mode)
{ {
GstSeekFlags seek_flags;
GstQuery *query; GstQuery *query;
GstEvent *seek; GstEvent *seek;
gboolean seekable = FALSE; gboolean seekable = FALSE;
@ -643,43 +658,120 @@ change_rate (GstPlay * play, gdouble rate)
g_return_if_fail (rate != 0); g_return_if_fail (rate != 0);
if (!gst_element_query_position (play->playbin, GST_FORMAT_TIME, &pos)) if (!gst_element_query_position (play->playbin, GST_FORMAT_TIME, &pos))
goto seek_failed; return FALSE;
query = gst_query_new_seeking (GST_FORMAT_TIME); query = gst_query_new_seeking (GST_FORMAT_TIME);
if (!gst_element_query (play->playbin, query)) { if (!gst_element_query (play->playbin, query)) {
gst_query_unref (query); gst_query_unref (query);
goto seek_failed; return FALSE;
} }
gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL); gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
gst_query_unref (query); gst_query_unref (query);
if (!seekable) if (!seekable)
goto seek_failed; return FALSE;
seek_flags = GST_SEEK_FLAG_FLUSH;
switch (mode) {
case GST_PLAY_TRICK_MODE_DEFAULT:
seek_flags |= GST_SEEK_FLAG_TRICKMODE;
break;
case GST_PLAY_TRICK_MODE_DEFAULT_NO_AUDIO:
seek_flags |= GST_SEEK_FLAG_TRICKMODE | GST_SEEK_FLAG_TRICKMODE_NO_AUDIO;
break;
case GST_PLAY_TRICK_MODE_KEY_UNITS:
seek_flags |= GST_SEEK_FLAG_TRICKMODE_KEY_UNITS;
break;
case GST_PLAY_TRICK_MODE_KEY_UNITS_NO_AUDIO:
seek_flags |=
GST_SEEK_FLAG_TRICKMODE_KEY_UNITS | GST_SEEK_FLAG_TRICKMODE_NO_AUDIO;
break;
case GST_PLAY_TRICK_MODE_NONE:
default:
break;
}
if (rate > 0) if (rate > 0)
seek = gst_event_new_seek (rate, GST_FORMAT_TIME, seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, seek_flags | GST_SEEK_FLAG_KEY_UNIT,
/* start */ GST_SEEK_TYPE_SET, pos, /* start */ GST_SEEK_TYPE_SET, pos,
/* stop */ GST_SEEK_TYPE_NONE, 0); /* stop */ GST_SEEK_TYPE_NONE, 0);
else else
seek = gst_event_new_seek (rate, GST_FORMAT_TIME, seek = gst_event_new_seek (rate, GST_FORMAT_TIME,
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, seek_flags | GST_SEEK_FLAG_ACCURATE,
/* start */ GST_SEEK_TYPE_SET, 0, /* start */ GST_SEEK_TYPE_SET, 0,
/* stop */ GST_SEEK_TYPE_SET, pos); /* stop */ GST_SEEK_TYPE_SET, pos);
if (!gst_element_send_event (play->playbin, seek)) if (!gst_element_send_event (play->playbin, seek))
goto seek_failed; return FALSE;
g_print ("Rate: %.2f \n", rate); play->rate = rate;
return; play->trick_mode = mode;
return TRUE;
}
seek_failed: static void
{ play_set_playback_rate (GstPlay * play, gdouble rate)
{
if (play_set_rate_and_trick_mode (play, rate, play->trick_mode)) {
g_print ("Rate: %.2f \n", rate);
} else {
g_print ("\nCould not change playback rate to %.2f.\n", rate); g_print ("\nCould not change playback rate to %.2f.\n", rate);
} }
} }
static void
play_set_relative_playback_rate (GstPlay * play, gdouble rate_step,
gboolean reverse_direction)
{
gdouble new_rate = play->rate + rate_step;
if (reverse_direction)
new_rate *= -1.0;
play_set_playback_rate (play, new_rate);
}
static const gchar *
trick_mode_get_description (GstPlayTrickMode mode)
{
switch (mode) {
case GST_PLAY_TRICK_MODE_NONE:
return "normal playback, trick modes disabled";
case GST_PLAY_TRICK_MODE_DEFAULT:
return "trick mode: default";
case GST_PLAY_TRICK_MODE_DEFAULT_NO_AUDIO:
return "trick mode: default, no audio";
case GST_PLAY_TRICK_MODE_KEY_UNITS:
return "trick mode: key frames only";
case GST_PLAY_TRICK_MODE_KEY_UNITS_NO_AUDIO:
return "trick mode: key frames only, no audio";
default:
break;
}
return "unknown trick mode";
}
static void
play_switch_trick_mode (GstPlay * play)
{
GstPlayTrickMode new_mode = ++play->trick_mode;
const gchar *mode_desc;
if (new_mode == GST_PLAY_TRICK_MODE_LAST)
new_mode = GST_PLAY_TRICK_MODE_NONE;
mode_desc = trick_mode_get_description (new_mode);
if (play_set_rate_and_trick_mode (play, play->rate, new_mode)) {
g_print ("Rate: %.2f (%s) \n", play->rate, mode_desc);
} else {
g_print ("\nCould not change trick mode to %s.\n", mode_desc);
}
}
static void static void
keyboard_cb (const gchar * key_input, gpointer user_data) keyboard_cb (const gchar * key_input, gpointer user_data)
{ {
@ -704,31 +796,29 @@ keyboard_cb (const gchar * key_input, gpointer user_data)
break; break;
case '+': case '+':
if (play->rate > -0.2 && play->rate < 0.0) if (play->rate > -0.2 && play->rate < 0.0)
play->rate *= -1.0; play_set_relative_playback_rate (play, 0.0, TRUE);
else if (ABS (play->rate) < 2.0) else if (ABS (play->rate) < 2.0)
play->rate += 0.1; play_set_relative_playback_rate (play, 0.1, FALSE);
else if (ABS (play->rate) < 4.0) else if (ABS (play->rate) < 4.0)
play->rate += 0.5; play_set_relative_playback_rate (play, 0.5, FALSE);
else else
play->rate += 1.0; play_set_relative_playback_rate (play, 1.0, FALSE);
change_rate (play, play->rate);
break; break;
case '-': case '-':
if (play->rate > 0.0 && play->rate < 0.20) { if (play->rate > 0.0 && play->rate < 0.20)
play->rate *= -1.0; play_set_relative_playback_rate (play, 0.0, TRUE);
} else if (ABS (play->rate) <= 2.0) else if (ABS (play->rate) <= 2.0)
play->rate -= 0.1; play_set_relative_playback_rate (play, -0.1, FALSE);
else if (ABS (play->rate) <= 4.0) else if (ABS (play->rate) <= 4.0)
play->rate -= 0.5; play_set_relative_playback_rate (play, -0.5, FALSE);
else else
play->rate -= 1.0; play_set_relative_playback_rate (play, -1.0, FALSE);
change_rate (play, play->rate);
break; break;
case 'd': case 'd':
play->rate *= -1.0; play_set_relative_playback_rate (play, 0.0, TRUE);
change_rate (play, play->rate); break;
case 't':
play_switch_trick_mode (play);
break; break;
case 27: /* ESC */ case 27: /* ESC */
if (key_input[1] == '\0') { if (key_input[1] == '\0') {