From abcd5edb1f0aacbf0984b22638a18b2505ad8c0b Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 7 Feb 2015 18:11:47 +0100 Subject: add seeking and keyhandling --- gplay.c | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 277 insertions(+), 48 deletions(-) (limited to 'gplay.c') diff --git a/gplay.c b/gplay.c index 4702e00..cc70bec 100644 --- a/gplay.c +++ b/gplay.c @@ -1,20 +1,26 @@ #include #include #include +#include #include #include #include -static GtkWindow *main_win; +static GtkWindow *main_win = NULL; static GtkWidget *video_widget; static GtkScale *slider_widget; static GtkToolItem *play_tool, *pause_tool; -static GstElement *playbin; +static GtkFileChooserDialog *open_file_chooser = NULL; -static gboolean window_size_updated; -static gint video_width, video_height; -static gint64 media_duration; +static GCancellable *icon_cancellable = NULL; + +static GstElement *playbin = NULL; +static GstState play_state; + +static gboolean window_size_updated = FALSE; +static gint video_width = 0, video_height = 0; +static gint64 media_duration = 0; static gulong slider_changed_connection_id = 0; static gulong position_update_source = 0; @@ -34,29 +40,172 @@ static void pause_media() gst_element_set_state(playbin, GST_STATE_PAUSED); } +static void seek_relative_media(gdouble seconds) +{ + gint64 current, target; + GstState target_state = play_state == GST_STATE_PLAYING ? GST_STATE_PLAYING : GST_STATE_PAUSED; + + gst_element_set_state(playbin, GST_STATE_PAUSED); + + if (gst_element_query_position(playbin, GST_FORMAT_TIME, ¤t)) { + target = current + (gint64)(seconds * GST_SECOND); + } else { + g_debug("could not query current media position"); + return; + } + + gst_element_seek_simple(playbin, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, + target); + + gst_element_set_state(playbin, target_state); +} + static void update_position_ui() { - GstFormat fmt = GST_FORMAT_TIME; gint64 current; if (!GST_CLOCK_TIME_IS_VALID(media_duration)) { - if (!gst_element_query_duration(playbin, fmt, &media_duration)) { - g_warning("Could not query current duration.\n"); + if (gst_element_query_duration(playbin, GST_FORMAT_TIME, &media_duration)) { + const gdouble seconds = (gdouble)media_duration / GST_SECOND; + g_debug("got media duration: %f seconds", seconds); + gtk_range_set_range(GTK_RANGE(slider_widget), 0, seconds); } else { - /* Set the range of the slider to the clip duration, in SECONDS */ - gtk_range_set_range(GTK_RANGE(slider_widget), 0, (gdouble)media_duration / GST_SECOND); + g_debug("could not query current media duration"); + return; } } - - if (gst_element_query_position(playbin, fmt, ¤t)) { + if (gst_element_query_position(playbin, GST_FORMAT_TIME, ¤t)) { + const gdouble seconds = (gdouble)current / GST_SECOND; g_signal_handler_block(slider_widget, slider_changed_connection_id); - gtk_range_set_value(GTK_RANGE(slider_widget), (gdouble)current / GST_SECOND); + gtk_range_set_value(GTK_RANGE(slider_widget), seconds); g_signal_handler_unblock(slider_widget, slider_changed_connection_id); + } else { + g_debug("could not query current media position"); + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(slider_widget), TRUE); +} + +static void reset_position_ui() +{ + gtk_widget_set_sensitive(GTK_WIDGET(slider_widget), FALSE); + gtk_range_set_range(GTK_RANGE(slider_widget), 0.0, 1.0); + gtk_range_set_value(GTK_RANGE(slider_widget), 0.0); +} + +static void open_icon_ready(GObject *source, GAsyncResult *res, gpointer user_data) +{ + GFile *file = G_FILE(source); + GError *error = NULL; + GFileInfo *info = g_file_query_info_finish(file, res, &error); + GdkPixbuf *icon_pixbuf = NULL; + + if (info) { + GIcon *icon = g_file_info_get_icon(info); + if (icon) { + GtkIconTheme *theme = gtk_icon_theme_get_default(); + GtkIconInfo *info = gtk_icon_theme_lookup_by_gicon(theme, icon, GTK_ICON_SIZE_DIALOG, 0); + if (info) { + icon_pixbuf = gtk_icon_info_load_icon(info, &error); + if (!icon_pixbuf) { + gchar *uri = g_file_get_uri(file); + g_debug("Could not load icon for file '%s': %s", uri, error->message); + g_free(uri); + g_error_free(error); + } + } + } + + g_object_unref(info); + } else { + gchar *uri = g_file_get_uri(file); + g_debug("Could not get icon for file '%s': %s", uri, error->message); + g_free(uri); + g_error_free(error); + } + + gtk_window_set_icon(main_win, icon_pixbuf); +} + +static void open_media(const char *uri) +{ + g_debug("opening '%s'", uri); + + gst_element_set_state(playbin, GST_STATE_NULL); + + window_size_updated = FALSE; + video_width = 0; + video_height = 0; + media_duration = GST_CLOCK_TIME_NONE; + + if (icon_cancellable) { + g_cancellable_cancel(icon_cancellable); + g_clear_object(&icon_cancellable); } + + g_object_set(playbin, "uri", uri, NULL); + + gchar *filename = g_filename_from_uri(uri, NULL, NULL); + if (filename) { + gchar *basename = g_filename_display_basename(filename); + gtk_window_set_title(main_win, basename); + g_free(basename); + g_free(filename); + } else { + gtk_window_set_title(main_win, g_get_application_name()); + } + + GFile *file = g_file_new_for_uri(uri); + icon_cancellable = g_cancellable_new(); + + g_file_query_info_async(file, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, + G_PRIORITY_LOW, icon_cancellable, + open_icon_ready, NULL); + g_object_unref(file); + + gst_element_set_state(playbin, GST_STATE_PAUSED); +} + +static void open_file_chooser_handle_response(GtkDialog *dialog, gint response_id, gpointer user_data) +{ + if (response_id == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(open_file_chooser)); + + open_media(filename); + + g_free(filename); + + gst_element_set_state(playbin, GST_STATE_PLAYING); + } + + gtk_widget_destroy(GTK_WIDGET(open_file_chooser)); + open_file_chooser = 0; +} + +static void menu_handle_open(GtkMenuItem *item, gpointer user_data) +{ + if (open_file_chooser) { + gtk_window_present_with_time(GTK_WINDOW(open_file_chooser), gtk_get_current_event_time()); + return; + } + + open_file_chooser = GTK_FILE_CHOOSER_DIALOG(gtk_file_chooser_dialog_new( + _("Open media"), main_win, GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL)); + g_signal_connect(open_file_chooser, "response", + G_CALLBACK(open_file_chooser_handle_response), NULL); + + gtk_widget_show_all(GTK_WIDGET(open_file_chooser)); } static void slider_widget_handle_value_changed(GtkRange *range, gpointer user_data) { + if (play_state < GST_STATE_PAUSED) return; + gdouble value = gtk_range_get_value(range); gst_element_seek_simple(playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, @@ -106,11 +255,15 @@ static void bus_handle_state_changed(GstBus *bus, GstMessage *msg, gpointer user gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state); if (GST_MESSAGE_SRC(msg) == GST_OBJECT(playbin)) { g_debug("playbin state change: %s", gst_element_state_get_name(new_state)); - gboolean update_size; + + play_state = new_state; + + gboolean update_size = FALSE, reset_size = FALSE; switch(new_state) { case GST_STATE_PLAYING: gtk_widget_hide(GTK_WIDGET(play_tool)); gtk_widget_show(GTK_WIDGET(pause_tool)); + gtk_widget_set_sensitive(GTK_WIDGET(play_tool), TRUE); update_size = !window_size_updated; if (!position_update_source) { position_update_source = g_timeout_add_seconds(1, second_tick, NULL); @@ -119,6 +272,7 @@ static void bus_handle_state_changed(GstBus *bus, GstMessage *msg, gpointer user case GST_STATE_PAUSED: gtk_widget_hide(GTK_WIDGET(pause_tool)); gtk_widget_show(GTK_WIDGET(play_tool)); + gtk_widget_set_sensitive(GTK_WIDGET(play_tool), TRUE); update_size = !window_size_updated; if (position_update_source) { g_source_remove(position_update_source); @@ -126,14 +280,27 @@ static void bus_handle_state_changed(GstBus *bus, GstMessage *msg, gpointer user } update_position_ui(); break; + case GST_STATE_READY: + gtk_widget_hide(GTK_WIDGET(pause_tool)); + gtk_widget_show(GTK_WIDGET(play_tool)); + gtk_widget_set_sensitive(GTK_WIDGET(play_tool), TRUE); + if (position_update_source) { + g_source_remove(position_update_source); + position_update_source = 0; + } + reset_size = TRUE; + reset_position_ui(); + break; default: gtk_widget_hide(GTK_WIDGET(pause_tool)); gtk_widget_show(GTK_WIDGET(play_tool)); - update_size = FALSE; + gtk_widget_set_sensitive(GTK_WIDGET(play_tool), FALSE); if (position_update_source) { g_source_remove(position_update_source); position_update_source = 0; } + reset_size = TRUE; + reset_position_ui(); break; } @@ -145,21 +312,22 @@ static void bus_handle_state_changed(GstBus *bus, GstMessage *msg, gpointer user GstCaps *caps = gst_pad_get_current_caps(pad); if (caps) { GstStructure *s = gst_caps_get_structure(caps, 0); - gint width, height; gst_structure_get_int(s, "width", &video_width); gst_structure_get_int(s, "height", &video_height); - g_debug("video width height %dx%d", width, height); + g_debug("video size %dx%d", video_width, video_height); GtkAllocation allocation, window_allocation; gtk_widget_get_allocation(video_widget, &allocation); gtk_widget_get_allocation(GTK_WIDGET(main_win), &window_allocation); - g_debug("%d %d %dx%d, %d %d %dx%d", allocation.x, allocation.y, allocation.width, allocation.height, - window_allocation.x, window_allocation.y, window_allocation.width, window_allocation.height); + gint window_width, window_height; + gtk_window_get_default_size(main_win, &window_width, &window_height); - gtk_window_resize(main_win, - MAX(window_allocation.width - allocation.width + video_width, 350), - window_allocation.height - allocation.height + video_height); + window_width = MAX(window_allocation.width - allocation.width + video_width, window_width); + window_height = MAX(window_allocation.height - allocation.height + video_height, window_height); + + g_debug("resizing window to %dx%d", window_width, window_height); + gtk_window_resize(main_win, window_width, window_height); window_size_updated = TRUE; @@ -167,13 +335,88 @@ static void bus_handle_state_changed(GstBus *bus, GstMessage *msg, gpointer user } gst_object_unref(pad); } + } else if (reset_size) { + gint width, height; + gtk_window_get_default_size(main_win, &width, &height); + gtk_window_resize(main_win, width, height); } } } +static void bus_handle_duration_changed(GstBus *bus, GstMessage *msg, gpointer user_data) +{ + g_debug("duration changed"); + media_duration = GST_CLOCK_TIME_NONE; +} + +static gboolean main_win_handle_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) +{ + switch (event->keyval) { + case GDK_KEY_space: + switch (play_state) { + case GST_STATE_READY: + case GST_STATE_PAUSED: + gst_element_set_state(playbin, GST_STATE_PLAYING); + break; + case GST_STATE_PLAYING: + gst_element_set_state(playbin, GST_STATE_PAUSED); + break; + default: + break; + } + + return TRUE; + case GDK_KEY_Left: + if (play_state < GST_STATE_PAUSED) return TRUE; + if (event->state & GDK_CONTROL_MASK) { + seek_relative_media(-30.0); + } else if (event->state & GDK_SHIFT_MASK) { + seek_relative_media(-1.0); + } else { + seek_relative_media(-10.0); + } + return TRUE; + case GDK_KEY_Right: + if (play_state < GST_STATE_PAUSED) return TRUE; + if (event->state & GDK_CONTROL_MASK) { + seek_relative_media(+30.0); + } else if (event->state & GDK_SHIFT_MASK) { + seek_relative_media(+1.0); + } else { + seek_relative_media(+10.0); + } + return TRUE; + case GDK_KEY_Down: + if (play_state < GST_STATE_PAUSED) return TRUE; + if (event->state & GDK_CONTROL_MASK) { + seek_relative_media(-10.0 * 60.0); + } else { + seek_relative_media(-1.0 * 60.0); + } + return TRUE; + case GDK_KEY_Up: + if (play_state < GST_STATE_PAUSED) return TRUE; + if (event->state & GDK_CONTROL_MASK) { + seek_relative_media(+10.0 * 60.0); + } else { + seek_relative_media(+1.0 * 60.0); + } + return TRUE; + + case GDK_KEY_Escape: + gtk_main_quit(); + return TRUE; + + default: + return FALSE; + } +} + static void create_ui() { main_win = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + g_signal_connect(main_win, "key-press-event", + G_CALLBACK(main_win_handle_key_press), NULL); g_signal_connect(main_win, "delete-event", G_CALLBACK(gtk_main_quit), NULL); @@ -190,6 +433,8 @@ static void create_ui() gtk_menu_shell_append(GTK_MENU_SHELL(menubar), GTK_WIDGET(file_item)); GtkWidget *open_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL); + g_signal_connect(open_item, "activate", + G_CALLBACK(menu_handle_open), NULL); gtk_menu_shell_append(file_menu, open_item); gtk_menu_shell_append(file_menu, gtk_separator_menu_item_new()); @@ -206,19 +451,23 @@ static void create_ui() gtk_box_pack_start(vbox, video_widget, TRUE, TRUE, 0); GtkToolbar *toolbar = GTK_TOOLBAR(gtk_toolbar_new()); + gtk_widget_set_can_focus(GTK_WIDGET(toolbar), FALSE); gtk_box_pack_start(vbox, GTK_WIDGET(toolbar), FALSE, FALSE, 0); play_tool = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PLAY); + gtk_widget_set_can_focus(GTK_WIDGET(play_tool), FALSE); g_signal_connect(play_tool, "clicked", G_CALLBACK(play_media), NULL); gtk_toolbar_insert(toolbar, play_tool, -1); pause_tool = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PAUSE); + gtk_widget_set_can_focus(GTK_WIDGET(pause_tool), FALSE); g_signal_connect(pause_tool, "clicked", G_CALLBACK(pause_media), NULL); gtk_toolbar_insert(toolbar, pause_tool, -1); slider_widget = GTK_SCALE(gtk_hscale_new_with_range(0, 100, 1)); + gtk_widget_set_can_focus(GTK_WIDGET(slider_widget), FALSE); gtk_scale_set_draw_value(slider_widget, FALSE); slider_changed_connection_id = g_signal_connect(slider_widget, "value-changed", G_CALLBACK(slider_widget_handle_value_changed), NULL); @@ -246,38 +495,16 @@ static void create_gst() G_CALLBACK(bus_handle_error), NULL); g_signal_connect(bus, "message::state-changed", G_CALLBACK(bus_handle_state_changed), NULL); + g_signal_connect(bus, "message::duration-changed", + G_CALLBACK(bus_handle_duration_changed), NULL); gst_object_unref(bus); } -static void open_media(const char *uri) -{ - g_debug("opening '%s'", uri); - - window_size_updated = FALSE; - video_width = 0; - video_height = 0; - media_duration = GST_CLOCK_TIME_NONE; - - g_object_set(playbin, "uri", uri, NULL); - - gchar *filename = g_filename_from_uri(uri, NULL, NULL); - if (filename) { - gchar *basename = g_filename_display_basename(filename); - gtk_window_set_title(main_win, basename); - g_free(basename); - g_free(filename); - } else { - gtk_window_set_title(main_win, "gplay"); - } - - gst_element_set_state(playbin, GST_STATE_PAUSED); -} - int main(int argc, char **argv) { GOptionContext *ctx = g_option_context_new(_(" - simple gtk media player")); - gchar **filenames; - gboolean start_playing; + gchar **filenames = NULL; + gboolean start_playing = FALSE; const GOptionEntry entries[] = { { "play", 'p', 0, G_OPTION_ARG_NONE, &start_playing, "Start playing immediately", NULL }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL }, @@ -285,6 +512,8 @@ int main(int argc, char **argv) }; GError *error = NULL; + g_set_application_name("Gplay"); + g_option_context_add_main_entries(ctx, entries, NULL); g_option_context_add_group(ctx, gst_init_get_option_group()); g_option_context_add_group(ctx, gtk_get_option_group(TRUE)); -- cgit v1.2.3