summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--gplay.c325
-rw-r--r--gplay.desktop12
-rw-r--r--gplay.files1
4 files changed, 297 insertions, 48 deletions
diff --git a/Makefile b/Makefile
index 9a0e338..e3460bf 100644
--- a/Makefile
+++ b/Makefile
@@ -16,3 +16,10 @@ $(gplay_objs): %.o: %.c
clean:
rm -f gplay *.o
+
+install: gplay
+ install gplay $(DESTDIR)/usr/bin/gplay
+ install gplay.desktop $(DESTDIR)/usr/share/applications/gplay.desktop
+
+uninstall:
+ rm -f $(DESTDIR)/usr/bin/gplay $(DESTDIR)/usr/share/applications/gplay.desktop
diff --git a/gplay.c b/gplay.c
index 4702e00..cc70bec 100644
--- a/gplay.c
+++ b/gplay.c
@@ -1,20 +1,26 @@
#include <stdlib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>
-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, &current)) {
+ 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, &current)) {
+ if (gst_element_query_position(playbin, GST_FORMAT_TIME, &current)) {
+ 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));
diff --git a/gplay.desktop b/gplay.desktop
new file mode 100644
index 0000000..e590fe6
--- /dev/null
+++ b/gplay.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Version=1.0
+Name=Gplay
+GenericName=Media player
+Comment=Play media files
+Exec=/usr/bin/gplay --play %U
+TryExec=/usr/bin/gplay
+Icon=applications-multimedia
+Terminal=false
+Type=Application
+Categories=AudioVideo;Player;
+MimeType=video/dv;video/mpeg;video/x-mpeg;video/msvideo;video/quicktime;video/x-anim;video/x-avi;video/x-ms-asf;video/x-ms-wmv;video/x-msvideo;video/x-nsv;video/x-flc;video/x-fli;video/x-flv;video/vnd.rn-realvideo;video/mp4;video/mp4v-es;video/mp2t;application/ogg;application/x-ogg;video/x-ogm+ogg;audio/x-vorbis+ogg;application/x-matroska;audio/x-matroska;video/x-matroska;video/webm;audio/webm;audio/x-mp3;audio/x-mpeg;audio/mpeg;audio/x-wav;audio/x-mpegurl;audio/x-scpls;audio/x-m4a;audio/x-ms-asf;audio/x-ms-asx;audio/x-ms-wax;application/vnd.rn-realmedia;audio/x-real-audio;audio/x-pn-realaudio;application/x-flac;audio/x-flac;application/x-shockwave-flash;misc/ultravox;audio/vnd.rn-realaudio;audio/x-pn-aiff;audio/x-pn-au;audio/x-pn-wav;audio/x-pn-windows-acm;image/vnd.rn-realpix;audio/x-pn-realaudio-plugin;application/x-extension-mp4;audio/mp4;audio/amr;audio/amr-wb;x-content/video-vcd;x-content/video-svcd;x-content/video-dvd;x-content/audio-cdda;x-content/audio-player;application/xspf+xml;
diff --git a/gplay.files b/gplay.files
index 3b85354..cdd46f7 100644
--- a/gplay.files
+++ b/gplay.files
@@ -1,2 +1,3 @@
Makefile
gplay.c
+gplay.desktop