[gthumb] split the gstreamer extension in utils and tools
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb] split the gstreamer extension in utils and tools
- Date: Tue, 16 Feb 2010 14:01:14 +0000 (UTC)
commit f887a13c3c4242713986c4cd2354953ab9fcd281
Author: Paolo Bacchilega <paobac src gnome org>
Date: Tue Feb 16 10:42:58 2010 +0100
split the gstreamer extension in utils and tools
configure.ac | 7 +-
extensions/Makefile.am | 3 +-
extensions/gstreamer_tools/Makefile.am | 39 +
extensions/gstreamer_tools/actions.c | 211 ++++
extensions/gstreamer_tools/actions.h | 32 +
extensions/gstreamer_tools/data/Makefile.am | 18 +
.../data/gthumb-gstreamer.schemas.in | 17 +
extensions/gstreamer_tools/data/ui/Makefile.am | 5 +
extensions/gstreamer_tools/data/ui/mediabar.ui | 230 ++++
.../gstreamer_tools/data/ui/save-screenshot.ui | 171 +++
.../gstreamer_tools.extension.in.in | 12 +
extensions/gstreamer_tools/gth-media-viewer-page.c | 1213 ++++++++++++++++++++
extensions/gstreamer_tools/gth-media-viewer-page.h | 62 +
.../gth-metadata-provider-gstreamer.c | 105 ++
.../gth-metadata-provider-gstreamer.h | 52 +
extensions/gstreamer_tools/main.c | 84 ++
extensions/gstreamer_tools/preferences.h | 28 +
extensions/gstreamer_utils/Makefile.am | 33 +
extensions/gstreamer_utils/gstreamer-utils.c | 792 +++++++++++++
extensions/gstreamer_utils/gstreamer-utils.h | 47 +
.../gstreamer_utils.extension.in.in | 6 +
extensions/gstreamer_utils/gstscreenshot.c | 259 +++++
extensions/gstreamer_utils/gstscreenshot.h | 36 +
extensions/slideshow/Makefile.am | 3 +
extensions/slideshow/gth-slideshow.c | 31 +-
gthumb/gth-main.c | 3 +-
26 files changed, 3479 insertions(+), 20 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 422fbe7..d2a9905 100644
--- a/configure.ac
+++ b/configure.ac
@@ -411,9 +411,10 @@ extensions/file_tools/Makefile
extensions/file_tools/data/Makefile
extensions/file_tools/data/ui/Makefile
extensions/file_viewer/Makefile
-extensions/gstreamer/Makefile
-extensions/gstreamer/data/Makefile
-extensions/gstreamer/data/ui/Makefile
+extensions/gstreamer_tools/Makefile
+extensions/gstreamer_tools/data/Makefile
+extensions/gstreamer_tools/data/ui/Makefile
+extensions/gstreamer_utils/Makefile
extensions/image_print/Makefile
extensions/image_print/data/Makefile
extensions/image_print/data/ui/Makefile
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index bfff982..c2a914c 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -10,7 +10,8 @@ SUBDIRS = \
file_manager \
file_tools \
file_viewer \
- gstreamer \
+ gstreamer_utils \
+ gstreamer_tools \
image_print \
image_rotation \
image_viewer \
diff --git a/extensions/gstreamer_tools/Makefile.am b/extensions/gstreamer_tools/Makefile.am
new file mode 100644
index 0000000..d8c8060
--- /dev/null
+++ b/extensions/gstreamer_tools/Makefile.am
@@ -0,0 +1,39 @@
+if ENABLE_GSTREAMER
+
+SUBDIRS = data
+
+extensiondir = $(pkglibdir)/extensions
+extension_LTLIBRARIES = libgstreamer_tools.la
+
+libgstreamer_tools_la_SOURCES = \
+ actions.c \
+ actions.h \
+ gth-media-viewer-page.c \
+ gth-media-viewer-page.h \
+ gth-metadata-provider-gstreamer.c \
+ gth-metadata-provider-gstreamer.h \
+ main.c \
+ preferences.h
+
+libgstreamer_tools_la_CPPFLAGS = $(GTHUMB_CFLAGS) $(GSTREAMER_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb
+libgstreamer_tools_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
+libgstreamer_tools_la_LIBADD = $(GTHUMB_LIBS) $(GSTREAMER_LIBS) ../gstreamer_utils/libgstreamer_utils.la
+libgstreamer_tools_la_DEPENDENCIES = $(top_builddir)/gthumb/gthumb$(EXEEXT)
+
+extensioninidir = $(extensiondir)
+extensionini_in_files = gstreamer_tools.extension.in.in
+extensionini_DATA = $(extensionini_in_files:.extension.in.in=.extension)
+
+%.extension.in: %.extension.in.in $(extension_LTLIBRARIES)
+ $(AM_V_GEN)( sed -e "s|%LIBRARY%|`. ./$(extension_LTLIBRARIES) && echo $$dlname`|" \
+ $< > $@ )
+
+%.extension: %.extension.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
+
+EXTRA_DIST = $(extensionini_in_files)
+
+DISTCLEANFILES = $(extensionini_DATA)
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/gstreamer_tools/actions.c b/extensions/gstreamer_tools/actions.c
new file mode 100644
index 0000000..3c1aa7b
--- /dev/null
+++ b/extensions/gstreamer_tools/actions.c
@@ -0,0 +1,211 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gst/gst.h>
+#include <gthumb.h>
+#include <extensions/gstreamer_utils/gstreamer-utils.h>
+#include "gth-media-viewer-page.h"
+#include "preferences.h"
+
+#define MAX_ATTEMPTS 1024
+
+
+/* -- media_viewer_activate_action_screenshot -- */
+
+
+typedef struct {
+ GthBrowser *browser;
+ GthMediaViewerPage *page;
+ gboolean playing_before_screenshot;
+ GdkPixbuf *pixbuf;
+ GthFileData *file_data;
+} SaveData;
+
+
+static void
+save_date_free (SaveData *save_data)
+{
+ _g_object_unref (save_data->file_data);
+ _g_object_unref (save_data->pixbuf);
+ g_free (save_data);
+}
+
+
+static void
+screenshot_saved_cb (GthFileData *file_data,
+ GError *error,
+ gpointer user_data)
+{
+ SaveData *save_data = user_data;
+ GthMediaViewerPage *page = save_data->page;
+
+ if (error != NULL)
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (save_data->browser), _("Could not save the file"), &error);
+ else if (save_data->playing_before_screenshot)
+ gst_element_set_state (gth_media_viewer_page_get_playbin (page), GST_STATE_PLAYING);
+
+ save_date_free (save_data);
+}
+
+
+static void
+save_as_response_cb (GtkDialog *file_sel,
+ int response,
+ SaveData *save_data)
+{
+ GFile *file;
+ GFile *folder;
+ char *folder_uri;
+ const char *mime_type;
+
+ if (response != GTK_RESPONSE_OK) {
+ GthMediaViewerPage *page = save_data->page;
+
+ if (save_data->playing_before_screenshot)
+ gst_element_set_state (gth_media_viewer_page_get_playbin (page), GST_STATE_PLAYING);
+ save_date_free (save_data);
+ gtk_widget_destroy (GTK_WIDGET (file_sel));
+ return;
+ }
+
+ if (! gth_file_chooser_dialog_get_file (GTH_FILE_CHOOSER_DIALOG (file_sel), &file, &mime_type))
+ return;
+
+ folder = g_file_get_parent (file);
+ folder_uri = g_file_get_uri (folder);
+ eel_gconf_set_string (PREF_GSTREAMER_SCREESHOT_LOCATION, folder_uri);
+
+ save_data->file_data = gth_file_data_new (file, NULL);
+ gth_file_data_set_mime_type (save_data->file_data, mime_type);
+ _gdk_pixbuf_save_async (save_data->pixbuf,
+ save_data->file_data,
+ mime_type,
+ TRUE,
+ screenshot_saved_cb,
+ save_data);
+
+ gtk_widget_destroy (GTK_WIDGET (file_sel));
+
+ g_free (folder_uri);
+ g_object_unref (folder);
+ g_object_unref (file);
+}
+
+
+static void
+screenshot_ready_cb (GdkPixbuf *pixbuf,
+ gpointer user_data)
+{
+ SaveData *save_data = user_data;
+ GtkWidget *file_sel;
+
+ if (pixbuf == NULL) {
+ _gtk_error_dialog_from_gerror_show (GTK_WINDOW (save_data->browser), _("Could not take a screenshot"), NULL);
+ save_date_free (save_data);
+ return;
+ }
+
+ save_data->pixbuf = pixbuf;
+ file_sel = gth_file_chooser_dialog_new (_("Save Image"), GTK_WINDOW (save_data->browser), "pixbuf-saver");
+
+ {
+ char *last_uri;
+ GFile *last_folder;
+ GthFileData *file_data;
+ char *prefix;
+ char *display_name;
+ int attempt;
+
+ last_uri = eel_gconf_get_string (PREF_GSTREAMER_SCREESHOT_LOCATION, "~");
+ if ((last_uri == NULL) || (strcmp (last_uri, "~") == 0))
+ last_folder = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
+ else
+ last_folder = g_file_new_for_uri (last_uri);
+ gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (file_sel), last_folder, NULL);
+
+ file_data = gth_media_viewer_page_get_file_data (save_data->page);
+ prefix = _g_utf8_remove_extension (g_file_info_get_display_name (file_data->info));
+ if (prefix == NULL)
+ prefix = g_strdup (C_("Filename", "Screenshot"));
+ display_name = NULL;
+ for (attempt = 1; attempt < MAX_ATTEMPTS; attempt++) {
+ GFile *proposed_file;
+
+ g_free (display_name);
+
+ display_name = g_strdup_printf ("%s-%02d.jpeg", prefix, attempt);
+ proposed_file = g_file_get_child_for_display_name (last_folder, display_name, NULL);
+ if ((proposed_file != NULL) && ! g_file_query_exists (proposed_file, NULL)) {
+ g_object_unref (proposed_file);
+ break;
+ }
+ }
+
+ if (display_name != NULL) {
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (file_sel), display_name);
+ g_free (display_name);
+ }
+
+ g_free (prefix);
+ g_object_unref (last_folder);
+ g_free (last_uri);
+ }
+
+ g_signal_connect (GTK_DIALOG (file_sel),
+ "response",
+ G_CALLBACK (save_as_response_cb),
+ save_data);
+
+ gtk_widget_show (file_sel);
+}
+
+
+void
+media_viewer_activate_action_screenshot (GtkAction *action,
+ GthMediaViewerPage *page)
+{
+ GstElement *playbin;
+ SaveData *save_data;
+ int video_fps_n;
+ int video_fps_d;
+
+ playbin = gth_media_viewer_page_get_playbin (page);
+ if (playbin == NULL)
+ return;
+
+ save_data = g_new0 (SaveData, 1);
+ save_data->browser = gth_media_viewer_page_get_browser (page);
+ save_data->page = page;
+ save_data->playing_before_screenshot = gth_media_viewer_page_is_playing (page);
+
+ if (save_data->playing_before_screenshot)
+ gst_element_set_state (playbin, GST_STATE_PAUSED);
+ gth_media_viewer_page_get_video_fps (page, &video_fps_n, &video_fps_d);
+ _gst_playbin_get_current_frame (playbin,
+ video_fps_n,
+ video_fps_d,
+ screenshot_ready_cb,
+ save_data);
+}
diff --git a/extensions/gstreamer_tools/actions.h b/extensions/gstreamer_tools/actions.h
new file mode 100644
index 0000000..52a7759
--- /dev/null
+++ b/extensions/gstreamer_tools/actions.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ACTIONS_H
+#define ACTIONS_H
+
+#include <gtk/gtk.h>
+
+#define DEFINE_ACTION(x) void x (GtkAction *action, gpointer data);
+
+DEFINE_ACTION(media_viewer_activate_action_screenshot)
+
+#endif /* ACTIONS_H */
diff --git a/extensions/gstreamer_tools/data/Makefile.am b/extensions/gstreamer_tools/data/Makefile.am
new file mode 100644
index 0000000..ee3cb38
--- /dev/null
+++ b/extensions/gstreamer_tools/data/Makefile.am
@@ -0,0 +1,18 @@
+SUBDIRS = ui
+
+schemadir = @GCONF_SCHEMA_FILE_DIR@
+schema_in_files = gthumb-gstreamer.schemas.in
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+ INTLTOOL_SCHEMAS_RULE@
+
+if GCONF_SCHEMAS_INSTALL
+install-data-local:
+ GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(top_builddir)/extensions/gstreamer/data/$(schema_DATA)
+endif
+
+EXTRA_DIST = $(schema_in_files)
+
+CLEANFILES = $(schema_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/gstreamer_tools/data/gthumb-gstreamer.schemas.in b/extensions/gstreamer_tools/data/gthumb-gstreamer.schemas.in
new file mode 100644
index 0000000..0fe9cf1
--- /dev/null
+++ b/extensions/gstreamer_tools/data/gthumb-gstreamer.schemas.in
@@ -0,0 +1,17 @@
+<gconfschemafile>
+ <schemalist>
+
+ <schema>
+ <key>/schemas/apps/gthumb/ext/gstreamer/screenshot_location</key>
+ <applyto>/apps/gthumb/ext/gstreamer/screenshot_location</applyto>
+ <owner>gthumb</owner>
+ <type>string</type>
+ <default>~</default>
+ <locale name="C">
+ <short></short>
+ <long></long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</gconfschemafile>
diff --git a/extensions/gstreamer_tools/data/ui/Makefile.am b/extensions/gstreamer_tools/data/ui/Makefile.am
new file mode 100644
index 0000000..18ee18b
--- /dev/null
+++ b/extensions/gstreamer_tools/data/ui/Makefile.am
@@ -0,0 +1,5 @@
+uidir = $(pkgdatadir)/ui
+ui_DATA = mediabar.ui save-screenshot.ui
+EXTRA_DIST = $(ui_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/gstreamer_tools/data/ui/mediabar.ui b/extensions/gstreamer_tools/data/ui/mediabar.ui
new file mode 100644
index 0000000..bb59e91
--- /dev/null
+++ b/extensions/gstreamer_tools/data/ui/mediabar.ui
@@ -0,0 +1,230 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="mediabar">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="spacing">36</property>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="button_play_slower">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Slower</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-rewind</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_play">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Play</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="button_play_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ <property name="icon-size">3</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_play_faster">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Faster</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-forward</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Time:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScale" id="hscale_position">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">adjustment_position</property>
+ <property name="fill_level">100</property>
+ <property name="draw_value">False</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="label_position">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" comments="this is an empty time">--:--</property>
+ <property name="width_chars">6</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="time" comments="this is the separator between current position and duration of a media file">/</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_duration">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">--:--</property>
+ <property name="width_chars">6</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="volume_box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkToggleButton" id="togglebutton_volume">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Toggle volume</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkImage" id="togglebutton_volume_image">
+ <property name="visible">True</property>
+ <property name="icon_name">audio-volume-high</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScale" id="hscale_volume">
+ <property name="width_request">180</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Change volume level</property>
+ <property name="adjustment">adjustment_volume</property>
+ <property name="digits">0</property>
+ <property name="value_pos">right</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_position">
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">1</property>
+ <property name="page_size">1</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_volume">
+ <property name="upper">100</property>
+ <property name="value">100</property>
+ <property name="step_increment">1</property>
+ </object>
+</interface>
diff --git a/extensions/gstreamer_tools/data/ui/save-screenshot.ui b/extensions/gstreamer_tools/data/ui/save-screenshot.ui
new file mode 100644
index 0000000..1a254c3
--- /dev/null
+++ b/extensions/gstreamer_tools/data/ui/save-screenshot.ui
@@ -0,0 +1,171 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="save_screenshot_dialog">
+ <property name="border_width">5</property>
+ <property name="resizable">False</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow" id="preview_scrolledwindow">
+ <property name="width_request">240</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Name:</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Destination:</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="filename_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="destination_filechooserbutton">
+ <property name="visible">True</property>
+ <property name="local_only">False</property>
+ <property name="create_folders">False</property>
+ <property name="action">select-folder</property>
+ <property name="title" translatable="yes">Choose a folder</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ok_button">
+ <property name="label">gtk-save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">cancel_button</action-widget>
+ <action-widget response="0">ok_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/extensions/gstreamer_tools/gstreamer_tools.extension.in.in b/extensions/gstreamer_tools/gstreamer_tools.extension.in.in
new file mode 100644
index 0000000..a4920a4
--- /dev/null
+++ b/extensions/gstreamer_tools/gstreamer_tools.extension.in.in
@@ -0,0 +1,12 @@
+[Extension]
+_Name=Audio/Video support
+_Description=Play audio and video files.
+_Authors=gthumb development team
+Copyright=Copyright © 2009 The Free Software Foundation, Inc.
+Version=1.0
+Icon=video-x-generic
+
+[Loader]
+Type=module
+File=%LIBRARY%
+Requires=gstreamer_utils
\ No newline at end of file
diff --git a/extensions/gstreamer_tools/gth-media-viewer-page.c b/extensions/gstreamer_tools/gth-media-viewer-page.c
new file mode 100644
index 0000000..7ce5ba5
--- /dev/null
+++ b/extensions/gstreamer_tools/gth-media-viewer-page.c
@@ -0,0 +1,1213 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <gst/gst.h>
+#include <gst/interfaces/xoverlay.h>
+#include <gthumb.h>
+#include <extensions/gstreamer_utils/gstreamer-utils.h>
+#include "actions.h"
+#include "gth-media-viewer-page.h"
+
+
+#define GTH_MEDIA_VIEWER_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTH_TYPE_MEDIA_VIEWER_PAGE, GthMediaViewerPagePrivate))
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+#define PROGRESS_DELAY 500
+
+
+struct _GthMediaViewerPagePrivate {
+ GthBrowser *browser;
+ GtkActionGroup *actions;
+ guint merge_id;
+ GthFileData *file_data;
+ GstElement *playbin;
+ GtkBuilder *builder;
+ GtkWidget *area;
+ GtkWidget *area_box;
+ gboolean playing;
+ gboolean paused;
+ gdouble last_volume;
+ gint64 duration;
+ int video_fps_n;
+ int video_fps_d;
+ gboolean has_video;
+ gboolean has_audio;
+ gulong update_progress_id;
+ gdouble rate;
+ GtkWidget *mediabar;
+ GtkWidget *fullscreen_toolbar;
+ gboolean xwin_assigned;
+ GdkPixbuf *icon;
+ PangoLayout *caption_layout;
+ gboolean block_next_jump;
+};
+
+
+static gpointer gth_media_viewer_page_parent_class = NULL;
+
+
+static const char *media_viewer_ui_info =
+"<ui>"
+" <toolbar name='ViewerToolBar'>"
+" <placeholder name='ViewerCommands'>"
+" <toolitem action='View_Fullscreen'/>"
+" <toolitem action='MediaViewer_Screenshot'/>"
+" </placeholder>"
+" </toolbar>"
+" <toolbar name='Fullscreen_ToolBar'>"
+" <placeholder name='ViewerCommands'>"
+" <toolitem action='MediaViewer_Screenshot'/>"
+" </placeholder>"
+" </toolbar>"
+"</ui>";
+
+
+static GtkActionEntry media_viewer_action_entries[] = {
+ { "MediaViewer_Screenshot", "camera-photo",
+ N_("Screenshot"), NULL,
+ N_("Take a screenshot"),
+ G_CALLBACK (media_viewer_activate_action_screenshot) },
+};
+
+
+static void
+_gth_media_viewer_page_update_caption (GthMediaViewerPage *self)
+{
+ if (self->priv->caption_layout == NULL)
+ return;
+
+ if (self->priv->file_data != NULL) {
+ const char *text;
+ GthMetadata *metadata;
+
+ text = NULL;
+ metadata = (GthMetadata *) g_file_info_get_attribute_object (self->priv->file_data->info, "general::title");
+ if (metadata != NULL)
+ text = gth_metadata_get_formatted (metadata);
+ else
+ text = g_file_info_get_display_name (self->priv->file_data->info);
+
+ if (text != NULL)
+ pango_layout_set_text (self->priv->caption_layout, text, -1);
+ }
+ else
+ pango_layout_set_text (self->priv->caption_layout, "", -1);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self->priv->area));
+}
+
+
+static void
+video_area_realize_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ self->priv->caption_layout = gtk_widget_create_pango_layout (widget, "");
+ _gth_media_viewer_page_update_caption (self);
+}
+
+
+static void
+video_area_unrealize_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ g_object_unref (self->priv->caption_layout);
+ self->priv->caption_layout = NULL;
+}
+
+
+static gboolean
+video_area_expose_event_cb (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ if (event->count > 0)
+ return FALSE;
+
+ if (self->priv->xwin_assigned && self->priv->has_video)
+ return FALSE;
+
+ if (self->priv->icon == NULL) {
+ char *type;
+ GIcon *icon;
+ int size;
+
+ type = NULL;
+ if (self->priv->file_data != NULL)
+ type = g_content_type_from_mime_type (gth_file_data_get_mime_type (self->priv->file_data));
+ if (type == NULL)
+ type = g_content_type_from_mime_type ("text/plain");
+ icon = g_content_type_get_icon (type);
+ size = widget->allocation.width;
+ if (size > widget->allocation.height)
+ size = widget->allocation.height;
+ size = size / 3;
+ self->priv->icon = _g_icon_get_pixbuf (icon, size, gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget)));
+
+ g_object_unref (icon);
+ g_free (type);
+ }
+
+ gdk_draw_rectangle (gtk_widget_get_window (widget),
+ self->priv->has_video ? widget->style->black_gc : widget->style->text_gc[GTK_WIDGET_STATE (widget)],
+ TRUE,
+ event->area.x,
+ event->area.y,
+ event->area.width,
+ event->area.height);
+
+ if (self->priv->icon != NULL) {
+ int icon_w, icon_h;
+ int icon_x, icon_y;
+ PangoRectangle logical_rect;
+ int x, y;
+
+ icon_w = gdk_pixbuf_get_width (self->priv->icon);
+ icon_h = gdk_pixbuf_get_height (self->priv->icon);
+ pango_layout_set_width (self->priv->caption_layout, PANGO_SCALE * (icon_w * 3 / 2));
+ pango_layout_get_extents (self->priv->caption_layout, NULL, &logical_rect);
+ icon_x = (widget->allocation.width - icon_w) / 2;
+ x = (widget->allocation.width - PANGO_PIXELS (logical_rect.width)) / 2 + PANGO_PIXELS (logical_rect.x);
+ icon_y = (widget->allocation.height - (icon_h + PANGO_PIXELS (logical_rect.height))) / 2;
+ y = icon_y + icon_h;
+
+ gdk_draw_pixbuf (gtk_widget_get_window (widget),
+ widget->style->base_gc[GTK_WIDGET_STATE (widget)],
+ self->priv->icon,
+ 0, 0,
+ icon_x, icon_y,
+ icon_w, icon_h,
+ GDK_RGB_DITHER_NORMAL,
+ 0, 0);
+ gdk_draw_layout (gtk_widget_get_window (widget),
+ widget->style->base_gc[GTK_WIDGET_STATE (widget)],
+ x, y,
+ self->priv->caption_layout);
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+video_area_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ GthMediaViewerPage *self)
+{
+ if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1) ) {
+ gtk_button_clicked (GTK_BUTTON (GET_WIDGET ("button_play")));
+ return TRUE;
+ }
+
+ return gth_browser_viewer_button_press_cb (self->priv->browser, event);
+}
+
+
+static gboolean
+video_area_scroll_event_cb (GtkWidget *widget,
+ GdkEventScroll *event,
+ GthMediaViewerPage *self)
+{
+ return gth_browser_viewer_scroll_event_cb (self->priv->browser, event);
+}
+
+
+static gboolean
+video_area_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ GthMediaViewerPage *self)
+{
+ return gth_browser_viewer_key_press_cb (self->priv->browser, event);
+}
+
+
+static void
+volume_value_changed_cb (GtkAdjustment *adjustment,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+ if (self->priv->playbin != NULL)
+ g_object_set (self->priv->playbin,
+ "volume",
+ gtk_adjustment_get_value (adjustment) / 10.0,
+ NULL);
+}
+
+
+static void position_value_changed_cb (GtkAdjustment *adjustment,
+ gpointer user_data);
+
+
+static void
+update_current_position_bar (GthMediaViewerPage *self,
+ gboolean update_progressbar)
+{
+ GstFormat format;
+ gint64 current_value = 0;
+
+ format = GST_FORMAT_TIME;
+ if (gst_element_query_position (self->priv->playbin, &format, ¤t_value)) {
+ char *s;
+
+ if (self->priv->duration <= 0) {
+ gst_element_query_duration (self->priv->playbin, &format, &self->priv->duration);
+ s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (self->priv->duration));
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_duration")), s);
+
+ g_free (s);
+ }
+
+ /*
+ g_print ("==> %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " (%0.3g)\n" ,
+ current_value,
+ self->priv->duration,
+ ((double) current_value / self->priv->duration) * 100.0);
+ */
+
+ if (update_progressbar) {
+ g_signal_handlers_block_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("adjustment_position")), (self->priv->duration > 0) ? ((double) current_value / self->priv->duration) * 100.0 : 0.0);
+ g_signal_handlers_unblock_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
+ }
+
+ s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
+
+ g_free (s);
+ }
+}
+
+
+static void
+position_value_changed_cb (GtkAdjustment *adjustment,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+ gint64 current_value;
+ char *s;
+
+ if (self->priv->playbin == NULL)
+ return;
+
+ current_value = (gint64) (gtk_adjustment_get_value (adjustment) / 100.0 * self->priv->duration);
+ if (! gst_element_seek_simple (self->priv->playbin,
+ GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
+ current_value))
+ {
+ g_warning ("seek failed");
+ }
+
+ s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
+ gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
+
+ g_free (s);
+}
+
+
+static char *
+hscale_volume_format_value_cb (GtkScale *scale,
+ double value,
+ gpointer user_data)
+{
+ return g_strdup_printf ("%0.0f%%", value);
+}
+
+
+static gboolean
+hscale_position_change_value_cb (GtkRange *range,
+ GtkScrollType scroll,
+ gdouble value,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ if (self->priv->block_next_jump && (scroll == GTK_SCROLL_JUMP)) {
+ self->priv->block_next_jump = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+hscale_position_button_release_event_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ if (self->priv->playing)
+ self->priv->block_next_jump = TRUE;
+
+ return FALSE;
+}
+
+
+static void
+update_player_rate (GthMediaViewerPage *self)
+{
+ self->priv->rate = CLAMP (self->priv->rate, 0.25, 2.0);
+
+ if (self->priv->playbin == NULL)
+ return;
+
+ if (! self->priv->playing)
+ return;
+
+ if (! gst_element_seek (self->priv->playbin,
+ self->priv->rate,
+ GST_FORMAT_TIME,
+ (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
+ GST_SEEK_TYPE_NONE,
+ 0.0,
+ GST_SEEK_TYPE_NONE,
+ 0.0))
+ {
+ g_warning ("seek failed");
+ }
+}
+
+
+static void
+button_play_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ if (self->priv->playbin == NULL)
+ return;
+ if (! self->priv->playing) {
+ if (! self->priv->paused) {
+ gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
+ gst_element_seek_simple (self->priv->playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, 0);
+ }
+ gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
+ }
+ else
+ gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
+}
+
+
+static void
+togglebutton_volume_toggled_cb (GtkToggleButton *button,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ if (self->priv->playbin == NULL)
+ return;
+
+ if (gtk_toggle_button_get_active (button)) {
+ g_object_get (self->priv->playbin, "volume", &self->priv->last_volume, NULL);
+ g_object_set (self->priv->playbin, "volume", 0.0, NULL);
+ }
+ else
+ g_object_set (self->priv->playbin, "volume", self->priv->last_volume, NULL);
+}
+
+
+static void
+button_play_slower_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ self->priv->rate -= 0.25;
+ update_player_rate (self);
+}
+
+
+static void
+button_play_faster_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ self->priv->rate += 0.25;
+ update_player_rate (self);
+}
+
+
+static void
+update_volume_from_playbin (GthMediaViewerPage *self)
+{
+ double volume;
+
+ if ((self->priv->builder == NULL) || (self->priv->playbin == NULL))
+ return;
+
+ g_object_get (self->priv->playbin, "volume", &volume, NULL);
+ if (volume == 0.0)
+ gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("togglebutton_volume_image")), "audio-volume-muted", GTK_ICON_SIZE_BUTTON);
+ else if (volume < 3.3)
+ gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("togglebutton_volume_image")), "audio-volume-low", GTK_ICON_SIZE_BUTTON);
+ else if (volume < 6.6)
+ gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("togglebutton_volume_image")), "audio-volume-medium", GTK_ICON_SIZE_BUTTON);
+ else
+ gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("togglebutton_volume_image")), "audio-volume-high", GTK_ICON_SIZE_BUTTON);
+
+ g_signal_handlers_block_by_func(GET_WIDGET ("adjustment_volume"), volume_value_changed_cb, self);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("adjustment_volume")), volume * 10.0);
+ g_signal_handlers_unblock_by_func(GET_WIDGET ("adjustment_volume"), volume_value_changed_cb, self);
+}
+
+
+static gboolean
+update_progress_cb (gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ if (self->priv->update_progress_id != 0) {
+ g_source_remove (self->priv->update_progress_id);
+ self->priv->update_progress_id = 0;
+ }
+
+ update_current_position_bar (self, TRUE);
+
+ self->priv->update_progress_id = gdk_threads_add_timeout (PROGRESS_DELAY, update_progress_cb, self);
+
+ return FALSE;
+}
+
+
+static void
+update_play_button (GthMediaViewerPage *self,
+ GstState new_state)
+{
+ if (! self->priv->playing && (new_state == GST_STATE_PLAYING)) {
+ self->priv->playing = TRUE;
+ gtk_image_set_from_stock (GTK_IMAGE (GET_WIDGET ("button_play_image")), GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_widget_set_tooltip_text (GET_WIDGET ("button_play_image"), _("Pause"));
+
+ if (self->priv->update_progress_id == 0)
+ self->priv->update_progress_id = gdk_threads_add_timeout (PROGRESS_DELAY, update_progress_cb, self);
+ }
+ else if (self->priv->playing && (new_state != GST_STATE_PLAYING)) {
+ self->priv->playing = FALSE;
+ gtk_image_set_from_stock (GTK_IMAGE (GET_WIDGET ("button_play_image")), GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_widget_set_tooltip_text (GET_WIDGET ("button_play_image"), _("Play"));
+
+ if (self->priv->update_progress_id != 0) {
+ g_source_remove (self->priv->update_progress_id);
+ self->priv->update_progress_id = 0;
+ }
+ }
+
+ gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
+}
+
+
+static void
+gth_media_viewer_page_real_activate (GthViewerPage *base,
+ GthBrowser *browser)
+{
+ GthMediaViewerPage *self;
+
+ if (! gstreamer_init ())
+ return;
+
+ self = (GthMediaViewerPage*) base;
+
+ self->priv->browser = browser;
+
+ self->priv->actions = gtk_action_group_new ("Video Viewer Actions");
+ gtk_action_group_set_translation_domain (self->priv->actions, NULL);
+ gtk_action_group_add_actions (self->priv->actions,
+ media_viewer_action_entries,
+ G_N_ELEMENTS (media_viewer_action_entries),
+ self);
+ gtk_ui_manager_insert_action_group (gth_browser_get_ui_manager (browser), self->priv->actions, 0);
+
+ self->priv->area_box = gtk_vbox_new (FALSE, 0);
+
+ /* video area */
+
+ self->priv->area = gtk_drawing_area_new ();
+ gtk_widget_set_double_buffered (self->priv->area, FALSE);
+ gtk_widget_add_events (self->priv->area, (gtk_widget_get_events (self->priv->area)
+ | GDK_EXPOSURE_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK
+ | GDK_BUTTON_MOTION_MASK));
+ GTK_WIDGET_SET_FLAGS (self->priv->area, GTK_CAN_FOCUS);
+ gtk_widget_show (self->priv->area);
+ gtk_box_pack_start (GTK_BOX (self->priv->area_box), self->priv->area, TRUE, TRUE, 0);
+
+ g_signal_connect (G_OBJECT (self->priv->area),
+ "realize",
+ G_CALLBACK (video_area_realize_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->area),
+ "unrealize",
+ G_CALLBACK (video_area_unrealize_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->area),
+ "expose_event",
+ G_CALLBACK (video_area_expose_event_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->area),
+ "button_press_event",
+ G_CALLBACK (video_area_button_press_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->area),
+ "scroll_event",
+ G_CALLBACK (video_area_scroll_event_cb),
+ self);
+ g_signal_connect (G_OBJECT (self->priv->area),
+ "key_press_event",
+ G_CALLBACK (video_area_key_press_cb),
+ self);
+
+ /* mediabar */
+
+ self->priv->builder = _gtk_builder_new_from_file ("mediabar.ui", "gstreamer");
+ self->priv->mediabar = GET_WIDGET ("mediabar");
+ gtk_widget_show (self->priv->mediabar);
+ gtk_box_pack_start (GTK_BOX (self->priv->area_box), self->priv->mediabar, FALSE, FALSE, 0);
+
+ g_signal_connect (GET_WIDGET ("adjustment_volume"), "value-changed", G_CALLBACK (volume_value_changed_cb), self);
+ g_signal_connect (GET_WIDGET ("adjustment_position"), "value-changed", G_CALLBACK (position_value_changed_cb), self);
+ g_signal_connect (GET_WIDGET ("hscale_volume"), "format-value", G_CALLBACK (hscale_volume_format_value_cb), self);
+ g_signal_connect (GET_WIDGET ("hscale_position"), "change-value", G_CALLBACK (hscale_position_change_value_cb), self);
+ g_signal_connect (GET_WIDGET ("hscale_position"), "button-release-event", G_CALLBACK (hscale_position_button_release_event_cb), self);
+ g_signal_connect (GET_WIDGET ("button_play"), "clicked", G_CALLBACK (button_play_clicked_cb), self);
+ g_signal_connect (GET_WIDGET ("togglebutton_volume"), "toggled", G_CALLBACK (togglebutton_volume_toggled_cb), self);
+ g_signal_connect (GET_WIDGET ("button_play_slower"), "clicked", G_CALLBACK (button_play_slower_clicked_cb), self);
+ g_signal_connect (GET_WIDGET ("button_play_faster"), "clicked", G_CALLBACK (button_play_faster_clicked_cb), self);
+
+ gtk_widget_show (self->priv->area_box);
+ gth_browser_set_viewer_widget (browser, self->priv->area_box);
+
+ gtk_widget_realize (self->priv->area);
+ gdk_window_ensure_native (gtk_widget_get_window (self->priv->area));
+ gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
+}
+
+
+static void
+gth_media_viewer_page_real_deactivate (GthViewerPage *base)
+{
+ GthMediaViewerPage *self;
+
+ self = (GthMediaViewerPage*) base;
+
+ if (self->priv->builder != NULL) {
+ g_object_unref (self->priv->builder);
+ self->priv->builder = NULL;
+ }
+
+ if (self->priv->update_progress_id != 0) {
+ g_source_remove (self->priv->update_progress_id);
+ self->priv->update_progress_id = 0;
+ }
+
+ if (self->priv->playbin != NULL) {
+ gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (self->priv->playbin));
+ self->priv->playbin = NULL;
+ }
+
+ gth_browser_set_viewer_widget (self->priv->browser, NULL);
+}
+
+
+static GstBusSyncReply
+set_playbin_window (GstBus *bus,
+ GstMessage *message,
+ GthMediaViewerPage *self)
+{
+ GstXOverlay *image_sink;
+
+ /* ignore anything but 'prepare-xwindow-id' element messages */
+
+ if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
+ return GST_BUS_PASS;
+ if (! gst_structure_has_name (message->structure, "prepare-xwindow-id"))
+ return GST_BUS_PASS;
+
+ image_sink = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
+ gst_x_overlay_set_xwindow_id (image_sink, GDK_WINDOW_XID (gtk_widget_get_window (self->priv->area)));
+ g_object_set (image_sink, "force-aspect-ratio", TRUE, NULL);
+ self->priv->xwin_assigned = TRUE;
+
+ gst_message_unref (message);
+
+ return GST_BUS_DROP;
+}
+
+
+static void
+reset_player_state (GthMediaViewerPage *self)
+{
+ if (self->priv->update_progress_id != 0) {
+ g_source_remove (self->priv->update_progress_id);
+ self->priv->update_progress_id = 0;
+ }
+
+ update_play_button (self, GST_STATE_NULL);
+ self->priv->playing = FALSE;
+ self->priv->rate = 1.0;
+}
+
+
+static void
+update_stream_info (GthMediaViewerPage *self)
+{
+ GList *streaminfo;
+ GstPad *videopad;
+
+ streaminfo = NULL;
+ videopad = NULL;
+
+ g_object_get (self->priv->playbin, "stream-info", &streaminfo, NULL);
+ streaminfo = g_list_copy (streaminfo);
+ g_list_foreach (streaminfo, (GFunc) g_object_ref, NULL);
+
+ for (/* void */ ; streaminfo; streaminfo = streaminfo->next) {
+ GObject *info;
+ int type;
+ GParamSpec *pspec;
+ GEnumValue *val;
+
+ info = streaminfo->data;
+ if (info == NULL)
+ continue;
+
+ type = -1;
+
+ g_object_get (info, "type", &type, NULL);
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info), "type");
+ val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
+
+ if (strcmp (val->value_nick, "audio") == 0) {
+ self->priv->has_audio = TRUE;
+ }
+ else if (strcmp (val->value_nick, "video") == 0) {
+ self->priv->has_video = TRUE;
+ if (videopad == NULL)
+ g_object_get (info, "object", &videopad, NULL);
+ }
+ }
+
+ if (videopad != NULL) {
+ GstCaps *caps;
+
+ if ((caps = gst_pad_get_negotiated_caps (videopad)) != NULL) {
+ GstStructure *structure;
+
+ structure = gst_caps_get_structure (caps, 0);
+ gst_structure_get_fraction (structure, "framerate", &self->priv->video_fps_n, &self->priv->video_fps_d);
+
+ gst_caps_unref (caps);
+ }
+ }
+
+ g_list_foreach (streaminfo, (GFunc) g_object_unref, NULL);
+ g_list_free (streaminfo);
+}
+
+
+static void
+bus_message_cb (GstBus *bus,
+ GstMessage *message,
+ gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState old_state;
+ GstState new_state;
+ GstState pending_state;
+
+ old_state = new_state = GST_STATE_NULL;
+ gst_message_parse_state_changed (message, &old_state, &new_state, &pending_state);
+
+ self->priv->paused = (new_state == GST_STATE_PAUSED);
+
+ if (old_state == new_state)
+ break;
+ if (GST_MESSAGE_SRC (message) != GST_OBJECT (self->priv->playbin))
+ break;
+
+ update_current_position_bar (self, TRUE);
+
+ if ((old_state == GST_STATE_READY) && (new_state == GST_STATE_PAUSED)) {
+ update_stream_info (self);
+ gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
+ }
+ if ((old_state == GST_STATE_READY) || (new_state == GST_STATE_PAUSED))
+ update_volume_from_playbin (self);
+ if ((old_state == GST_STATE_PLAYING) || (new_state == GST_STATE_PLAYING))
+ update_play_button (self, new_state);
+ break;
+ }
+
+ case GST_MESSAGE_DURATION: {
+ GstFormat format;
+
+ format = GST_FORMAT_TIME;
+ gst_message_parse_duration (message, &format, &self->priv->duration);
+ update_current_position_bar (self, TRUE);
+ break;
+ }
+
+ case GST_MESSAGE_EOS:
+ reset_player_state (self);
+ break;
+
+ case GST_MESSAGE_BUFFERING: {
+ int percent = 0;
+ gst_message_parse_buffering (message, &percent);
+ g_print ("Buffering (%%%u percent done)", percent);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+static void
+playbin_notify_volume_cb (GObject *playbin,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ update_volume_from_playbin ((GthMediaViewerPage *) user_data);
+}
+
+
+static void
+gth_media_viewer_page_real_show (GthViewerPage *base)
+{
+ GthMediaViewerPage *self;
+ GError *error = NULL;
+ GstBus *bus;
+ char *uri;
+
+ self = (GthMediaViewerPage*) base;
+
+ if (self->priv->merge_id != 0)
+ return;
+
+ self->priv->merge_id = gtk_ui_manager_add_ui_from_string (gth_browser_get_ui_manager (self->priv->browser), media_viewer_ui_info, -1, &error);
+ if (self->priv->merge_id == 0) {
+ g_warning ("ui building failed: %s", error->message);
+ g_error_free (error);
+ }
+
+ gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
+
+ if (self->priv->playbin != NULL)
+ return;
+
+ self->priv->playbin = gst_element_factory_make ("playbin", "playbin");
+ g_signal_connect (self->priv->playbin, "notify::volume", G_CALLBACK (playbin_notify_volume_cb), self);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->playbin));
+ gst_bus_enable_sync_message_emission (bus);
+ gst_bus_set_sync_handler (bus, (GstBusSyncHandler) set_playbin_window, self);
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), self);
+
+ if (self->priv->file_data == NULL)
+ return;
+
+ uri = g_file_get_uri (self->priv->file_data->file);
+ g_object_set (G_OBJECT (self->priv->playbin), "uri", uri, NULL);
+ gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
+
+ g_free (uri);
+}
+
+
+static void
+gth_media_viewer_page_real_hide (GthViewerPage *base)
+{
+ GthMediaViewerPage *self;
+
+ self = (GthMediaViewerPage*) base;
+
+ if (self->priv->merge_id != 0) {
+ gtk_ui_manager_remove_ui (gth_browser_get_ui_manager (self->priv->browser), self->priv->merge_id);
+ self->priv->merge_id = 0;
+ }
+
+ if ((self->priv->playbin != NULL) && self->priv->playing)
+ gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
+}
+
+
+static gboolean
+gth_media_viewer_page_real_can_view (GthViewerPage *base,
+ GthFileData *file_data)
+{
+ GthMediaViewerPage *self;
+
+ self = (GthMediaViewerPage*) base;
+ g_return_val_if_fail (file_data != NULL, FALSE);
+
+ return _g_mime_type_is_video (gth_file_data_get_mime_type (file_data)) || _g_mime_type_is_audio (gth_file_data_get_mime_type (file_data));
+}
+
+
+static gboolean
+set_to_paused (gpointer user_data)
+{
+ GthMediaViewerPage *self = user_data;
+
+ if (self->priv->playbin != NULL)
+ gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
+ return FALSE;
+}
+
+
+static void
+gth_media_viewer_page_real_view (GthViewerPage *base,
+ GthFileData *file_data)
+{
+ GthMediaViewerPage *self;
+ char *uri;
+
+ self = (GthMediaViewerPage*) base;
+ g_return_if_fail (file_data != NULL);
+
+ if (! gstreamer_init ())
+ return;
+
+ gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
+
+ if ((self->priv->file_data != NULL)
+ && g_file_equal (file_data->file, self->priv->file_data->file)
+ && (gth_file_data_get_mtime (file_data) == gth_file_data_get_mtime (self->priv->file_data)))
+ {
+ return;
+ }
+
+ /**/
+
+ _g_object_unref (self->priv->file_data);
+ self->priv->file_data = gth_file_data_dup (file_data);
+
+ self->priv->duration = 0;
+
+ _g_object_unref (self->priv->icon);
+ self->priv->icon = NULL;
+
+ _gth_media_viewer_page_update_caption (self);
+
+ /**/
+
+ gth_viewer_page_file_loaded (GTH_VIEWER_PAGE (self), TRUE);
+ g_signal_handlers_block_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("adjustment_position")), 0.0);
+ g_signal_handlers_unblock_by_func(GET_WIDGET ("adjustment_position"), position_value_changed_cb, self);
+ reset_player_state (self);
+
+ if (self->priv->playbin == NULL)
+ return;
+
+ gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
+ uri = g_file_get_uri (self->priv->file_data->file);
+ g_object_set (G_OBJECT (self->priv->playbin), "uri", uri, NULL);
+
+ gdk_threads_add_idle (set_to_paused, self);
+
+ g_free (uri);
+}
+
+
+static void
+gth_media_viewer_page_real_focus (GthViewerPage *base)
+{
+ GtkWidget *widget;
+
+ widget = GTH_MEDIA_VIEWER_PAGE (base)->priv->area;
+ if (GTK_WIDGET_REALIZED (widget))
+ gtk_widget_grab_focus (widget);
+}
+
+
+static void
+gth_media_viewer_page_real_fullscreen (GthViewerPage *base,
+ gboolean active)
+{
+ GthMediaViewerPage *self = (GthMediaViewerPage*) base;
+ GdkScreen *screen;
+
+ if (! active) {
+ g_object_ref (self->priv->mediabar);
+ gtk_container_remove (GTK_CONTAINER (self->priv->fullscreen_toolbar), self->priv->mediabar);
+ gtk_box_pack_start (GTK_BOX (self->priv->area_box), self->priv->mediabar, FALSE, FALSE, 0);
+ g_object_unref (self->priv->mediabar);
+
+ gtk_widget_destroy (self->priv->fullscreen_toolbar);
+ self->priv->fullscreen_toolbar = NULL;
+
+ return;
+ }
+
+ /* active == TRUE */
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (self->priv->browser));
+
+ if (self->priv->fullscreen_toolbar == NULL) {
+ self->priv->fullscreen_toolbar = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_screen (GTK_WINDOW (self->priv->fullscreen_toolbar), screen);
+ gtk_window_set_default_size (GTK_WINDOW (self->priv->fullscreen_toolbar), gdk_screen_get_width (screen), -1);
+ gtk_container_set_border_width (GTK_CONTAINER (self->priv->fullscreen_toolbar), 0);
+ }
+
+ g_object_ref (self->priv->mediabar);
+ gtk_container_remove (GTK_CONTAINER (self->priv->area_box), self->priv->mediabar);
+ gtk_container_add (GTK_CONTAINER (self->priv->fullscreen_toolbar), self->priv->mediabar);
+ g_object_unref (self->priv->mediabar);
+
+ gtk_widget_realize (self->priv->mediabar);
+ gtk_window_set_gravity (GTK_WINDOW (self->priv->fullscreen_toolbar), GDK_GRAVITY_SOUTH_EAST);
+ gtk_window_move (GTK_WINDOW (self->priv->fullscreen_toolbar), 0, gdk_screen_get_height (screen) - self->priv->mediabar->allocation.height);
+
+ gth_browser_register_fullscreen_control (self->priv->browser, self->priv->fullscreen_toolbar);
+}
+
+
+static void
+gth_media_viewer_page_real_show_pointer (GthViewerPage *base,
+ gboolean show)
+{
+ GthMediaViewerPage *self = (GthMediaViewerPage*) base;
+
+ if (self->priv->fullscreen_toolbar != NULL) {
+ if (show && ! GTK_WIDGET_VISIBLE (self->priv->fullscreen_toolbar)) {
+ gtk_widget_show (self->priv->fullscreen_toolbar);
+ }
+ else if (! show && GTK_WIDGET_VISIBLE (self->priv->fullscreen_toolbar)) {
+ gtk_widget_hide (self->priv->fullscreen_toolbar);
+ }
+ }
+}
+
+
+static void
+set_action_sensitive (GthMediaViewerPage *self,
+ const char *action_name,
+ gboolean sensitive)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (self->priv->actions, action_name);
+ g_object_set (action, "sensitive", sensitive, NULL);
+}
+
+
+static void
+gth_media_viewer_page_real_update_sensitivity (GthViewerPage *base)
+{
+ GthMediaViewerPage *self = (GthMediaViewerPage *) base;
+
+ gtk_widget_set_sensitive (GET_WIDGET ("button_play_slower"), self->priv->playing);
+ gtk_widget_set_sensitive (GET_WIDGET ("button_play_faster"), self->priv->playing);
+ gtk_widget_set_sensitive (GET_WIDGET ("volume_box"), self->priv->has_audio);
+ set_action_sensitive (self, "MediaViewer_Screenshot", self->priv->has_video);
+}
+
+
+static gboolean
+gth_media_viewer_page_real_can_save (GthViewerPage *base)
+{
+ return FALSE;
+}
+
+
+static void
+gth_media_viewer_page_real_save (GthViewerPage *base,
+ GFile *file,
+ FileSavedFunc func,
+ gpointer user_data)
+{
+ /* void */
+}
+
+
+static void
+gth_media_viewer_page_real_save_as (GthViewerPage *base,
+ FileSavedFunc func,
+ gpointer user_data)
+{
+ /* void */
+}
+
+
+static void
+gth_media_viewer_page_real_revert (GthViewerPage *base)
+{
+ /* void */
+}
+
+
+static void
+gth_media_viewer_page_finalize (GObject *obj)
+{
+ GthMediaViewerPage *self;
+
+ self = GTH_MEDIA_VIEWER_PAGE (obj);
+
+ if (self->priv->update_progress_id != 0) {
+ g_source_remove (self->priv->update_progress_id);
+ self->priv->update_progress_id = 0;
+ }
+
+ if (self->priv->playbin != NULL) {
+ gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (self->priv->playbin));
+ self->priv->playbin = NULL;
+ }
+ _g_object_unref (self->priv->icon);
+ _g_object_unref (self->priv->file_data);
+
+ G_OBJECT_CLASS (gth_media_viewer_page_parent_class)->finalize (obj);
+}
+
+
+static void
+gth_media_viewer_page_class_init (GthMediaViewerPageClass *klass)
+{
+ gth_media_viewer_page_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (GthMediaViewerPagePrivate));
+
+ G_OBJECT_CLASS (klass)->finalize = gth_media_viewer_page_finalize;
+}
+
+
+static void
+gth_viewer_page_interface_init (GthViewerPageIface *iface)
+{
+ iface->activate = gth_media_viewer_page_real_activate;
+ iface->deactivate = gth_media_viewer_page_real_deactivate;
+ iface->show = gth_media_viewer_page_real_show;
+ iface->hide = gth_media_viewer_page_real_hide;
+ iface->can_view = gth_media_viewer_page_real_can_view;
+ iface->view = gth_media_viewer_page_real_view;
+ iface->focus = gth_media_viewer_page_real_focus;
+ iface->fullscreen = gth_media_viewer_page_real_fullscreen;
+ iface->show_pointer = gth_media_viewer_page_real_show_pointer;
+ iface->update_sensitivity = gth_media_viewer_page_real_update_sensitivity;
+ iface->can_save = gth_media_viewer_page_real_can_save;
+ iface->save = gth_media_viewer_page_real_save;
+ iface->save_as = gth_media_viewer_page_real_save_as;
+ iface->revert = gth_media_viewer_page_real_revert;
+}
+
+
+static void
+gth_media_viewer_page_instance_init (GthMediaViewerPage *self)
+{
+ self->priv = GTH_MEDIA_VIEWER_PAGE_GET_PRIVATE (self);
+ self->priv->update_progress_id = 0;
+ self->priv->xwin_assigned = FALSE;
+ self->priv->has_video = FALSE;
+ self->priv->has_audio = FALSE;
+ self->priv->video_fps_n = 0;
+ self->priv->video_fps_d = 0;
+ self->priv->icon = NULL;
+}
+
+
+GType
+gth_media_viewer_page_get_type (void) {
+ static GType gth_media_viewer_page_type_id = 0;
+ if (gth_media_viewer_page_type_id == 0) {
+ static const GTypeInfo g_define_type_info = {
+ sizeof (GthMediaViewerPageClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gth_media_viewer_page_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (GthMediaViewerPage),
+ 0,
+ (GInstanceInitFunc) gth_media_viewer_page_instance_init,
+ NULL
+ };
+ static const GInterfaceInfo gth_viewer_page_info = {
+ (GInterfaceInitFunc) gth_viewer_page_interface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+ gth_media_viewer_page_type_id = g_type_register_static (G_TYPE_OBJECT, "GthMediaViewerPage", &g_define_type_info, 0);
+ g_type_add_interface_static (gth_media_viewer_page_type_id, GTH_TYPE_VIEWER_PAGE, >h_viewer_page_info);
+ }
+ return gth_media_viewer_page_type_id;
+}
+
+
+GthBrowser *
+gth_media_viewer_page_get_browser (GthMediaViewerPage *self)
+{
+ return self->priv->browser;
+}
+
+
+GstElement *
+gth_media_viewer_page_get_playbin (GthMediaViewerPage *self)
+{
+ return self->priv->playbin;
+}
+
+
+gboolean
+gth_media_viewer_page_is_playing (GthMediaViewerPage *self)
+{
+ return self->priv->playing;
+}
+
+
+void
+gth_media_viewer_page_get_video_fps (GthMediaViewerPage *self,
+ int *video_fps_n,
+ int *video_fps_d)
+{
+ if (video_fps_n != NULL)
+ *video_fps_n = self->priv->video_fps_n;
+ if (video_fps_d != NULL)
+ *video_fps_d = self->priv->video_fps_d;
+}
+
+
+GthFileData *
+gth_media_viewer_page_get_file_data (GthMediaViewerPage *self)
+{
+ return self->priv->file_data;
+}
diff --git a/extensions/gstreamer_tools/gth-media-viewer-page.h b/extensions/gstreamer_tools/gth-media-viewer-page.h
new file mode 100644
index 0000000..942a45a
--- /dev/null
+++ b/extensions/gstreamer_tools/gth-media-viewer-page.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_MEDIA_VIEWER_PAGE_H
+#define GTH_MEDIA_VIEWER_PAGE_H
+
+#include <gst/gst.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_MEDIA_VIEWER_PAGE (gth_media_viewer_page_get_type ())
+#define GTH_MEDIA_VIEWER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_MEDIA_VIEWER_PAGE, GthMediaViewerPage))
+#define GTH_MEDIA_VIEWER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_MEDIA_VIEWER_PAGE, GthMediaViewerPageClass))
+#define GTH_IS_MEDIA_VIEWER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_MEDIA_VIEWER_PAGE))
+#define GTH_IS_MEDIA_VIEWER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_MEDIA_VIEWER_PAGE))
+#define GTH_MEDIA_VIEWER_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_MEDIA_VIEWER_PAGE, GthMediaViewerPageClass))
+
+typedef struct _GthMediaViewerPage GthMediaViewerPage;
+typedef struct _GthMediaViewerPageClass GthMediaViewerPageClass;
+typedef struct _GthMediaViewerPagePrivate GthMediaViewerPagePrivate;
+
+struct _GthMediaViewerPage {
+ GObject parent_instance;
+ GthMediaViewerPagePrivate * priv;
+};
+
+struct _GthMediaViewerPageClass {
+ GObjectClass parent_class;
+};
+
+GType gth_media_viewer_page_get_type (void);
+GthBrowser * gth_media_viewer_page_get_browser (GthMediaViewerPage *self);
+GstElement * gth_media_viewer_page_get_playbin (GthMediaViewerPage *self);
+gboolean gth_media_viewer_page_is_playing (GthMediaViewerPage *self);
+void gth_media_viewer_page_get_video_fps (GthMediaViewerPage *self,
+ int *video_fps_n,
+ int *video_fps_d);
+GthFileData * gth_media_viewer_page_get_file_data (GthMediaViewerPage *self);
+
+G_END_DECLS
+
+#endif /* GTH_MEDIA_VIEWER_PAGE_H */
diff --git a/extensions/gstreamer_tools/gth-metadata-provider-gstreamer.c b/extensions/gstreamer_tools/gth-metadata-provider-gstreamer.c
new file mode 100644
index 0000000..fd5675f
--- /dev/null
+++ b/extensions/gstreamer_tools/gth-metadata-provider-gstreamer.c
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <gthumb.h>
+#include <extensions/gstreamer_utils/gstreamer-utils.h>
+#include "gth-metadata-provider-gstreamer.h"
+
+
+static GthMetadataProviderClass *parent_class = NULL;
+
+
+static gboolean
+gth_metadata_provider_gstreamer_can_read (GthMetadataProvider *self,
+ const char *mime_type,
+ char **attribute_v)
+{
+ if (! _g_content_type_is_a (mime_type, "audio/*")
+ && ! _g_content_type_is_a (mime_type, "video/*"))
+ {
+ return FALSE;
+ }
+
+ return _g_file_attributes_matches_any_v ("general::title,"
+ "general::format,"
+ "general::dimensions,"
+ "audio-video::*",
+ attribute_v);
+}
+
+
+static void
+gth_metadata_provider_gstreamer_read (GthMetadataProvider *self,
+ GthFileData *file_data,
+ const char *attributes)
+{
+ if (! g_content_type_is_a (gth_file_data_get_mime_type (file_data), "audio/*")
+ && ! g_content_type_is_a (gth_file_data_get_mime_type (file_data), "video/*"))
+ {
+ return;
+ }
+
+ /* this function is executed in a secondary thread, so calling
+ * slow sync functions is not a problem. */
+
+ gstreamer_read_metadata_from_file (file_data->file, file_data->info, NULL);
+}
+
+
+static void
+gth_metadata_provider_gstreamer_class_init (GthMetadataProviderGstreamerClass *klass)
+{
+ parent_class = g_type_class_peek_parent (klass);
+
+ GTH_METADATA_PROVIDER_CLASS (klass)->can_read = gth_metadata_provider_gstreamer_can_read;
+ GTH_METADATA_PROVIDER_CLASS (klass)->read = gth_metadata_provider_gstreamer_read;
+}
+
+
+GType
+gth_metadata_provider_gstreamer_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (GthMetadataProviderGstreamerClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gth_metadata_provider_gstreamer_class_init,
+ NULL,
+ NULL,
+ sizeof (GthMetadataProviderGstreamer),
+ 0,
+ (GInstanceInitFunc) NULL
+ };
+
+ type = g_type_register_static (GTH_TYPE_METADATA_PROVIDER,
+ "GthMetadataProviderGstreamer",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/extensions/gstreamer_tools/gth-metadata-provider-gstreamer.h b/extensions/gstreamer_tools/gth-metadata-provider-gstreamer.h
new file mode 100644
index 0000000..1dd5b50
--- /dev/null
+++ b/extensions/gstreamer_tools/gth-metadata-provider-gstreamer.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_METADATA_PROVIDER_GSTREAMER_H
+#define GTH_METADATA_PROVIDER_GSTREAMER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gthumb.h>
+
+#define GTH_TYPE_METADATA_PROVIDER_GSTREAMER (gth_metadata_provider_gstreamer_get_type ())
+#define GTH_METADATA_PROVIDER_GSTREAMER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_METADATA_PROVIDER_GSTREAMER, GthMetadataProviderGstreamer))
+#define GTH_METADATA_PROVIDER_GSTREAMER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTH_TYPE_METADATA_PROVIDER_GSTREAMER, GthMetadataProviderGstreamerClass))
+#define GTH_IS_METADATA_PROVIDER_GSTREAMER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTH_TYPE_METADATA_PROVIDER_GSTREAMER))
+#define GTH_IS_METADATA_PROVIDER_GSTREAMER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTH_TYPE_METADATA_PROVIDER_GSTREAMER))
+#define GTH_METADATA_PROVIDER_GSTREAMER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTH_TYPE_METADATA_PROVIDER_GSTREAMER, GthMetadataProviderGstreamerClass))
+
+typedef struct _GthMetadataProviderGstreamer GthMetadataProviderGstreamer;
+typedef struct _GthMetadataProviderGstreamerClass GthMetadataProviderGstreamerClass;
+
+struct _GthMetadataProviderGstreamer
+{
+ GthMetadataProvider __parent;
+};
+
+struct _GthMetadataProviderGstreamerClass
+{
+ GthMetadataProviderClass __parent_class;
+};
+
+GType gth_metadata_provider_gstreamer_get_type (void) G_GNUC_CONST;
+
+#endif /* GTH_METADATA_PROVIDER_GSTREAMER_H */
diff --git a/extensions/gstreamer_tools/main.c b/extensions/gstreamer_tools/main.c
new file mode 100644
index 0000000..a6939d7
--- /dev/null
+++ b/extensions/gstreamer_tools/main.c
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <gthumb.h>
+#include "gth-metadata-provider-gstreamer.h"
+#include "gth-media-viewer-page.h"
+
+
+GthMetadataCategory gstreamer_metadata_category[] = {
+ { "audio-video::video", N_("Video"), 40 },
+ { "audio-video::audio", N_("Audio"), 50 },
+ { "audio-video::other", N_("Other"), 60 },
+ { NULL, NULL, 0 }
+};
+
+
+GthMetadataInfo gstreamer_metadata_info[] = {
+ { "audio-video::general::artist", N_("Artist"), "general", 2, GTH_METADATA_ALLOW_EVERYWHERE },
+ { "audio-video::general::album", N_("Album"), "general", 3, GTH_METADATA_ALLOW_EVERYWHERE },
+ { "audio-video::general::bitrate", N_("Bitrate"), "general", 20, GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW },
+ { "audio-video::general::encoder", N_("Encoder"), "general", 21, GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW },
+
+ { "audio-video::video::codec", N_("Codec"), "audio-video::video", 2, GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW },
+ { "audio-video::video::framerate", N_("Framerate"), "audio-video::video", 3, GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW },
+ { "audio-video::video::width", N_("Width"), "audio-video::video", 0, GTH_METADATA_ALLOW_NOWHERE },
+ { "audio-video::video::height", N_("Height"), "audio-video::video", 0, GTH_METADATA_ALLOW_NOWHERE },
+
+ { "audio-video::audio::codec", N_("Codec"), "audio-video::audio", 1, GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW },
+ { "audio-video::audio::channels", N_("Channels"), "audio-video::audio", 2, GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW },
+ { "audio-video::audio::samplerate", N_("Sample rate"), "audio-video::audio", 3, GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW },
+
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+
+G_MODULE_EXPORT void
+gthumb_extension_activate (void)
+{
+ gth_main_register_object (GTH_TYPE_VIEWER_PAGE, NULL, GTH_TYPE_MEDIA_VIEWER_PAGE, NULL);
+ gth_main_register_metadata_category (gstreamer_metadata_category);
+ gth_main_register_metadata_info_v (gstreamer_metadata_info);
+ gth_main_register_metadata_provider (GTH_TYPE_METADATA_PROVIDER_GSTREAMER);
+}
+
+
+G_MODULE_EXPORT void
+gthumb_extension_deactivate (void)
+{
+}
+
+
+G_MODULE_EXPORT gboolean
+gthumb_extension_is_configurable (void)
+{
+ return FALSE;
+}
+
+
+G_MODULE_EXPORT void
+gthumb_extension_configure (GtkWindow *parent)
+{
+}
diff --git a/extensions/gstreamer_tools/preferences.h b/extensions/gstreamer_tools/preferences.h
new file mode 100644
index 0000000..0cad7b1
--- /dev/null
+++ b/extensions/gstreamer_tools/preferences.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PREFERENCES_H
+#define PREFERENCES_H
+
+#define PREF_GSTREAMER_SCREESHOT_LOCATION "/apps/gthumb/ext/gstreamer/screenshot_location"
+
+#endif /* PREFERENCES_H */
diff --git a/extensions/gstreamer_utils/Makefile.am b/extensions/gstreamer_utils/Makefile.am
new file mode 100644
index 0000000..d38e6d5
--- /dev/null
+++ b/extensions/gstreamer_utils/Makefile.am
@@ -0,0 +1,33 @@
+if ENABLE_GSTREAMER
+
+extensiondir = $(pkglibdir)/extensions
+extension_LTLIBRARIES = libgstreamer_utils.la
+
+libgstreamer_utils_la_SOURCES = \
+ gstreamer-utils.c \
+ gstreamer-utils.h \
+ gstscreenshot.c \
+ gstscreenshot.h
+
+libgstreamer_utils_la_CPPFLAGS = $(GTHUMB_CFLAGS) $(GSTREAMER_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb
+libgstreamer_utils_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
+libgstreamer_utils_la_LIBADD = $(GTHUMB_LIBS) $(GSTREAMER_LIBS)
+libgstreamer_utils_la_DEPENDENCIES = $(top_builddir)/gthumb/gthumb$(EXEEXT)
+
+extensioninidir = $(extensiondir)
+extensionini_in_files = gstreamer_utils.extension.in.in
+extensionini_DATA = $(extensionini_in_files:.extension.in.in=.extension)
+
+%.extension.in: %.extension.in.in $(extension_LTLIBRARIES)
+ $(AM_V_GEN)( sed -e "s|%LIBRARY%|`. ./$(extension_LTLIBRARIES) && echo $$dlname`|" \
+ $< > $@ )
+
+%.extension: %.extension.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
+
+EXTRA_DIST = $(extensionini_in_files)
+
+DISTCLEANFILES = $(extensionini_DATA)
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/extensions/gstreamer_utils/gstreamer-utils.c b/extensions/gstreamer_utils/gstreamer-utils.c
new file mode 100644
index 0000000..6db6a21
--- /dev/null
+++ b/extensions/gstreamer_utils/gstreamer-utils.c
@@ -0,0 +1,792 @@
+/* -*- Mode: CPP; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2008-2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+/* This was based on a file from Tracker */
+/*
+ * Tracker - audio/video metadata extraction based on GStreamer
+ * Copyright (C) 2006, Laurent Aguerreche (laurent aguerreche free fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <gst/gst.h>
+#include <gthumb.h>
+#include "gstreamer-utils.h"
+#include "gstscreenshot.h"
+
+
+static gboolean gstreamer_initialized = FALSE;
+
+
+typedef struct {
+ GstElement *playbin;
+ GstTagList *tagcache;
+ gboolean has_audio;
+ gboolean has_video;
+ gint video_height;
+ gint video_width;
+ gint video_fps_n;
+ gint video_fps_d;
+ gint video_bitrate;
+ char *video_codec;
+ gint audio_channels;
+ gint audio_samplerate;
+ gint audio_bitrate;
+ char *audio_codec;
+} MetadataExtractor;
+
+
+static void
+reset_extractor_data (MetadataExtractor *extractor)
+{
+ if (extractor->tagcache != NULL) {
+ gst_tag_list_free (extractor->tagcache);
+ extractor->tagcache = NULL;
+ }
+
+ g_free (extractor->audio_codec);
+ extractor->audio_codec = NULL;
+
+ g_free (extractor->video_codec);
+ extractor->video_codec = NULL;
+
+ extractor->has_audio = FALSE;
+ extractor->has_video = FALSE;
+ extractor->video_fps_n = -1;
+ extractor->video_fps_d = -1;
+ extractor->video_height = -1;
+ extractor->video_width = -1;
+ extractor->video_bitrate = -1;
+ extractor->audio_channels = -1;
+ extractor->audio_samplerate = -1;
+ extractor->audio_bitrate = -1;
+}
+
+
+static void
+metadata_extractor_free (MetadataExtractor *extractor)
+{
+ reset_extractor_data (extractor);
+ gst_element_set_state (extractor->playbin, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (extractor->playbin));
+ g_slice_free (MetadataExtractor, extractor);
+}
+
+
+gboolean
+gstreamer_init (void)
+{
+ if (! gstreamer_initialized) {
+ if (! gst_init_check (NULL, NULL, NULL))
+ return FALSE;
+ gstreamer_initialized = TRUE;
+ }
+
+ return TRUE;
+}
+
+
+static void
+add_metadata (GFileInfo *info,
+ const char *key,
+ char *raw,
+ char *formatted)
+{
+ GthMetadata *metadata;
+
+ if (raw == NULL)
+ return;
+
+ if (strcmp (key, "general::dimensions") == 0) {
+ g_file_info_set_attribute_string (info, key, raw);
+ return;
+ }
+ else if (strcmp (key, "general::duration") == 0) {
+ int secs;
+
+ g_free (formatted);
+ sscanf (raw, "%i", &secs);
+ formatted = _g_format_duration_for_display (secs * 1000);
+ }
+ else if (strcmp (key, "audio-video::general::bitrate") == 0) {
+ int bps;
+
+ g_free (formatted);
+ sscanf (raw, "%i", &bps);
+ formatted = g_strdup_printf ("%d kbps", bps / 1000);
+ }
+
+ metadata = gth_metadata_new ();
+ g_object_set (metadata,
+ "id", key,
+ "formatted", formatted != NULL ? formatted : raw,
+ "raw", raw,
+ NULL);
+ g_file_info_set_attribute_object (info, key, G_OBJECT (metadata));
+
+ g_object_unref (metadata);
+ g_free (raw);
+ g_free (formatted);
+}
+
+
+static void
+add_metadata_from_tag (GFileInfo *info,
+ const GstTagList *list,
+ const char *tag,
+ const char *tag_key)
+{
+ GType tag_type;
+
+ tag_type = gst_tag_get_type (tag);
+
+ if (tag_type == G_TYPE_BOOLEAN) {
+ gboolean ret;
+ if (gst_tag_list_get_boolean (list, tag, &ret)) {
+ if (ret)
+ add_metadata (info, tag_key, g_strdup ("TRUE"), NULL);
+ else
+ add_metadata (info, tag_key, g_strdup ("FALSE"), NULL);
+ }
+ }
+
+ if (tag_type == G_TYPE_STRING) {
+ char *ret = NULL;
+ if (gst_tag_list_get_string (list, tag, &ret))
+ add_metadata (info, tag_key, ret, NULL);
+ }
+
+ if (tag_type == G_TYPE_UCHAR) {
+ guchar ret = 0;
+ if (gst_tag_list_get_uchar (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%u", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_CHAR) {
+ gchar ret = 0;
+ if (gst_tag_list_get_char (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%d", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_UINT) {
+ guint ret = 0;
+ if (gst_tag_list_get_uint (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%u", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_INT) {
+ gint ret = 0;
+ if (gst_tag_list_get_int (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%d", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_ULONG) {
+ gulong ret = 0;
+ if (gst_tag_list_get_ulong (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%lu", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_LONG) {
+ glong ret = 0;
+ if (gst_tag_list_get_long (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%ld", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_INT64) {
+ gint64 ret = 0;
+ if (gst_tag_list_get_int64 (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%" G_GINT64_FORMAT, ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_UINT64) {
+ guint64 ret = 0;
+ if (gst_tag_list_get_uint64 (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%" G_GUINT64_FORMAT, ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_DOUBLE) {
+ gdouble ret = 0;
+ if (gst_tag_list_get_double (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%f", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_FLOAT) {
+ gfloat ret = 0;
+ if (gst_tag_list_get_float (list, tag, &ret))
+ add_metadata (info, tag_key, g_strdup_printf ("%f", ret), NULL);
+ }
+
+ if (tag_type == G_TYPE_DATE) {
+ GDate *ret = NULL;
+ if (gst_tag_list_get_date (list, tag, &ret)) {
+ if (ret != NULL) {
+ char buf[128];
+ char *raw;
+ char *formatted;
+
+ g_date_strftime (buf, 10, "%F %T", ret);
+ raw = g_strdup (buf);
+
+ g_date_strftime (buf, 10, "%x %X", ret);
+ formatted = g_strdup (buf);
+ add_metadata (info, tag_key, raw, formatted);
+ }
+ g_free (ret);
+ }
+ }
+}
+
+
+static void
+tag_iterate (const GstTagList *list,
+ const char *tag,
+ GFileInfo *info)
+{
+ const char *tag_key;
+ char *attribute = NULL;
+
+ tag_key = NULL;
+
+ if (strcmp (tag, "container-format") == 0) {
+ tag_key = "general::format";
+ }
+ else if (strcmp (tag, "bitrate") == 0) {
+ tag_key = "audio-video::general::bitrate";
+ }
+ else if (strcmp (tag, "encoder") == 0) {
+ tag_key = "audio-video::general::encoder";
+ }
+ else if (strcmp (tag, "title") == 0) {
+ tag_key = "general::title";
+ }
+ else if (strcmp (tag, "artist") == 0) {
+ tag_key = "audio-video::general::artist";
+ }
+ else if (strcmp (tag, "album") == 0) {
+ tag_key = "audio-video::general::album";
+ }
+ else if (strcmp (tag, "audio-codec") == 0) {
+ tag_key = "audio-video::audio::codec";
+ }
+ else if (strcmp (tag, "video-codec") == 0) {
+ tag_key = "audio-video::video::codec";
+ }
+
+ if (tag_key == NULL) {
+ GthMetadataInfo *metadata_info;
+
+ attribute = g_strconcat ("audio-video::other::", tag, NULL);
+ metadata_info = gth_main_get_metadata_info (attribute);
+ if (metadata_info == NULL) {
+ GthMetadataInfo info;
+
+ info.id = attribute;
+ info.display_name = gst_tag_get_nick (tag);
+ info.category = "audio-video::other";
+ info.sort_order = 500;
+ info.flags = GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW;
+ metadata_info = gth_main_register_metadata_info (&info);
+ }
+
+ tag_key = attribute;
+ }
+
+ add_metadata_from_tag (info, list, tag, tag_key);
+
+ g_free (attribute);
+}
+
+
+static gint64
+get_media_duration (MetadataExtractor *extractor)
+{
+ GstFormat fmt;
+ gint64 duration;
+
+ g_return_val_if_fail (extractor, -1);
+ g_return_val_if_fail (extractor->playbin, -1);
+
+ fmt = GST_FORMAT_TIME;
+ duration = -1;
+ if (gst_element_query_duration (extractor->playbin, &fmt, &duration) && (duration >= 0))
+ return duration / GST_SECOND;
+ else
+ return -1;
+}
+
+
+static void
+extract_metadata (MetadataExtractor *extractor,
+ GFileInfo *info)
+{
+ gint64 duration;
+
+ if (extractor->audio_channels >= 0)
+ add_metadata (info,
+ "audio-video::audio::channels",
+ g_strdup_printf ("%d", (guint) extractor->audio_channels),
+ g_strdup (extractor->audio_channels == 2 ? _("Stereo") : _("Mono")));
+
+ if (extractor->audio_samplerate >= 0)
+ add_metadata (info,
+ "audio-video::audio::samplerate",
+ g_strdup_printf ("%d", (guint) extractor->audio_samplerate),
+ g_strdup_printf ("%d Hz", (guint) extractor->audio_samplerate));
+
+ if (extractor->audio_bitrate >= 0)
+ add_metadata (info,
+ "audio-video::audio::bitrate",
+ g_strdup_printf ("%d", (guint) extractor->audio_bitrate),
+ g_strdup_printf ("%d bps", (guint) extractor->audio_bitrate));
+
+ if (extractor->video_height >= 0)
+ add_metadata (info,
+ "audio-video::video::height",
+ g_strdup_printf ("%d", (guint) extractor->video_height),
+ NULL);
+
+ if (extractor->video_width >= 0)
+ add_metadata (info,
+ "audio-video::video::width",
+ g_strdup_printf ("%d", (guint) extractor->video_width),
+ NULL);
+
+ if ((extractor->video_height >= 0) && (extractor->video_width >= 0))
+ add_metadata (info,
+ "general::dimensions",
+ g_strdup_printf ("%d x %d", (guint) extractor->video_width, (guint) extractor->video_height),
+ NULL);
+
+ if ((extractor->video_fps_n >= 0) && (extractor->video_fps_d >= 0))
+ add_metadata (info,
+ "audio-video::video::framerate",
+ g_strdup_printf ("%.7g", (gdouble) extractor->video_fps_n / (gdouble) extractor->video_fps_d),
+ g_strdup_printf ("%.7g fps", (gdouble) extractor->video_fps_n / (gdouble) extractor->video_fps_d));
+
+ if (extractor->video_bitrate >= 0)
+ add_metadata (info,
+ "audio-video::video::bitrate",
+ g_strdup_printf ("%d", (guint) extractor->video_bitrate),
+ g_strdup_printf ("%d bps", (guint) extractor->video_bitrate));
+
+ duration = get_media_duration (extractor);
+ if (duration >= 0)
+ add_metadata (info,
+ "general::duration",
+ g_strdup_printf ("%" G_GINT64_FORMAT, duration),
+ g_strdup_printf ("%" G_GINT64_FORMAT " sec", duration));
+
+ if (extractor->tagcache != NULL)
+ gst_tag_list_foreach (extractor->tagcache, (GstTagForeachFunc) tag_iterate, info);
+}
+
+
+static void
+caps_set (GstPad *pad,
+ MetadataExtractor *extractor,
+ const char *type)
+{
+ GstCaps *caps;
+ GstStructure *structure;
+
+ if ((caps = gst_pad_get_negotiated_caps (pad)) == NULL)
+ return;
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (structure == NULL) {
+ gst_caps_unref (caps);
+ return;
+ }
+
+ if (strcmp (type, "audio") == 0) {
+ gst_structure_get_int (structure, "channels", &extractor->audio_channels);
+ gst_structure_get_int (structure, "rate", &extractor->audio_samplerate);
+ gst_structure_get_int (structure, "bitrate", &extractor->audio_bitrate);
+ }
+ else if (strcmp (type, "video") == 0) {
+ gst_structure_get_fraction (structure, "framerate", &extractor->video_fps_n, &extractor->video_fps_d);
+ gst_structure_get_int (structure, "bitrate", &extractor->video_bitrate);
+ gst_structure_get_int (structure, "width", &extractor->video_width);
+ gst_structure_get_int (structure, "height", &extractor->video_height);
+ }
+
+ gst_caps_unref (caps);
+}
+
+
+static void
+update_stream_info (MetadataExtractor *extractor)
+{
+ GList *streaminfo;
+ GstPad *audiopad;
+ GstPad *videopad;
+
+ g_return_if_fail (extractor);
+
+ streaminfo = NULL;
+ audiopad = videopad = NULL;
+
+ g_object_get (extractor->playbin, "stream-info", &streaminfo, NULL);
+ streaminfo = g_list_copy (streaminfo);
+ g_list_foreach (streaminfo, (GFunc) g_object_ref, NULL);
+
+ for (/* void */ ; streaminfo; streaminfo = streaminfo->next) {
+ GObject *info;
+ int type;
+ GParamSpec *pspec;
+ GEnumValue *val;
+
+ info = streaminfo->data;
+ if (info == NULL)
+ continue;
+
+ type = -1;
+
+ g_object_get (info, "type", &type, NULL);
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info), "type");
+ val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
+
+ if (strcmp (val->value_nick, "audio") == 0) {
+ extractor->has_audio = TRUE;
+ if (audiopad == NULL)
+ g_object_get (info, "object", &audiopad, NULL);
+ }
+ else if (strcmp (val->value_nick, "video") == 0) {
+ extractor->has_video = TRUE;
+ if (videopad == NULL)
+ g_object_get (info, "object", &videopad, NULL);
+ }
+ }
+
+ if (audiopad != NULL) {
+ GstCaps *caps;
+
+ if ((caps = gst_pad_get_negotiated_caps (audiopad)) != NULL) {
+ caps_set (audiopad, extractor, "audio");
+ gst_caps_unref (caps);
+ }
+ }
+
+ if (videopad != NULL) {
+ GstCaps *caps;
+
+ if ((caps = gst_pad_get_negotiated_caps (videopad)) != NULL) {
+ caps_set (videopad, extractor, "video");
+ gst_caps_unref (caps);
+ }
+ }
+
+ g_list_foreach (streaminfo, (GFunc) g_object_unref, NULL);
+ g_list_free (streaminfo);
+}
+
+
+static gboolean
+message_loop_to_state_change (MetadataExtractor *extractor,
+ GstState state)
+{
+ GstBus *bus;
+ GstMessageType events;
+
+ g_return_val_if_fail (extractor, FALSE);
+ g_return_val_if_fail (extractor->playbin, FALSE);
+
+ bus = gst_element_get_bus (extractor->playbin);
+
+ events = (GST_MESSAGE_TAG | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
+
+ for (;;) {
+ GstMessage *message;
+
+ message = gst_bus_timed_pop_filtered (bus, GST_SECOND * 5, events);
+ if (message == NULL)
+ goto timed_out;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState old_state;
+ GstState new_state;
+
+ old_state = new_state = GST_STATE_NULL;
+
+ gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
+ if (old_state == new_state)
+ break;
+
+ /* we only care about playbin (pipeline) state changes */
+ if (GST_MESSAGE_SRC (message) != GST_OBJECT (extractor->playbin))
+ break;
+
+ if ((old_state == GST_STATE_READY) && (new_state == GST_STATE_PAUSED))
+ update_stream_info (extractor);
+ else if ((old_state == GST_STATE_PAUSED) && (new_state == GST_STATE_READY))
+ reset_extractor_data (extractor);
+
+ if (new_state == state) {
+ gst_message_unref (message);
+ goto success;
+ }
+
+ break;
+ }
+
+ case GST_MESSAGE_TAG: {
+ GstTagList *tag_list;
+ GstTagList *result;
+
+ tag_list = NULL;
+ gst_message_parse_tag (message, &tag_list);
+ result = gst_tag_list_merge (extractor->tagcache, tag_list, GST_TAG_MERGE_KEEP);
+ if (extractor->tagcache != NULL)
+ gst_tag_list_free (extractor->tagcache);
+ extractor->tagcache = result;
+
+ gst_tag_list_free (tag_list);
+
+ break;
+ }
+
+ case GST_MESSAGE_ERROR: {
+ gchar *debug = NULL;
+ GError *gsterror = NULL;
+
+ gst_message_parse_error (message, &gsterror, &debug);
+
+ /*g_warning ("Error: %s (%s)", gsterror->message, debug);*/
+
+ g_error_free (gsterror);
+ gst_message_unref (message);
+ g_free (debug);
+ goto error;
+ }
+ break;
+
+ case GST_MESSAGE_EOS: {
+ g_warning ("Media file could not be played.");
+ gst_message_unref (message);
+ goto error;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ gst_message_unref (message);
+ }
+
+ g_assert_not_reached ();
+
+ success:
+ /* state change succeeded */
+ GST_DEBUG ("state change to %s succeeded", gst_element_state_get_name (state));
+ return TRUE;
+
+ timed_out:
+ /* it's taking a long time to open */
+ GST_DEBUG ("state change to %s timed out, returning success", gst_element_state_get_name (state));
+ return TRUE;
+
+ error:
+ GST_DEBUG ("error while waiting for state change to %s", gst_element_state_get_name (state));
+ /* already set *error */
+ return FALSE;
+}
+
+
+gboolean
+gstreamer_read_metadata_from_file (GFile *file,
+ GFileInfo *info,
+ GError **error)
+{
+ char *uri;
+ MetadataExtractor *extractor;
+
+ if (! gstreamer_init ())
+ return FALSE;
+
+ uri = g_file_get_uri (file);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ extractor = g_slice_new0 (MetadataExtractor);
+ reset_extractor_data (extractor);
+
+ extractor->playbin = gst_element_factory_make ("playbin", "playbin");
+ g_object_set (G_OBJECT (extractor->playbin),
+ "uri", uri,
+ "audio-sink", gst_element_factory_make ("fakesink", "fakesink-audio"),
+ "video-sink", gst_element_factory_make ("fakesink", "fakesink-video"),
+ NULL);
+
+ gst_element_set_state (extractor->playbin, GST_STATE_PAUSED);
+ message_loop_to_state_change (extractor, GST_STATE_PAUSED);
+ extract_metadata (extractor, info);
+
+ metadata_extractor_free (extractor);
+ g_free (uri);
+
+ return TRUE;
+}
+
+
+/* -- _gst_playbin_get_current_frame -- */
+
+/* this is voodoo code taken from totem, kudos to the authors, license is GPL */
+
+
+typedef struct {
+
+ GdkPixbuf *pixbuf;
+ FrameReadyCallback cb;
+ gpointer user_data;
+} ScreenshotData;
+
+
+static void
+screenshot_data_finalize (ScreenshotData *data)
+{
+ if (data->cb != NULL)
+ data->cb (data->pixbuf, data->user_data);
+ g_free (data);
+}
+
+
+static void
+destroy_pixbuf (guchar *pix, gpointer data)
+{
+ gst_buffer_unref (GST_BUFFER (data));
+}
+
+
+static void
+get_current_frame_step2 (GstBuffer *buf,
+ gpointer user_data)
+{
+ ScreenshotData *data = user_data;
+ GstStructure *s;
+ int outwidth = 0;
+ int outheight = 0;
+
+ if (buf == NULL) {
+ g_warning ("Could not take screenshot: %s", "conversion failed");
+ screenshot_data_finalize (data);
+ return;
+ }
+
+ if (GST_BUFFER_CAPS (buf) == NULL) {
+ g_warning ("Could not take screenshot: %s", "no caps on output buffer");
+ screenshot_data_finalize (data);
+ return;
+ }
+
+ s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
+ gst_structure_get_int (s, "width", &outwidth);
+ gst_structure_get_int (s, "height", &outheight);
+
+ g_return_if_fail (outwidth > 0 && outheight > 0);
+
+ /* create pixbuf from that - use our own destroy function */
+
+ data->pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buf),
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ outwidth,
+ outheight,
+ GST_ROUND_UP_4 (outwidth * 3),
+ destroy_pixbuf,
+ buf);
+ if (data->pixbuf == NULL) {
+ g_warning ("Could not take screenshot: %s", "could not create pixbuf");
+ gst_buffer_unref (buf);
+ }
+
+ screenshot_data_finalize (data);
+}
+
+
+gboolean
+_gst_playbin_get_current_frame (GstElement *playbin,
+ int video_fps_n,
+ int video_fps_d,
+ FrameReadyCallback cb,
+ gpointer user_data)
+{
+ ScreenshotData *data;
+ GstBuffer *buf;
+ GstCaps *to_caps;
+
+ g_object_get (playbin, "frame", &buf, NULL);
+
+ if (buf == NULL) {
+ g_warning ("Could not take screenshot: %s", "no last video frame");
+ return FALSE;
+ }
+
+ if (GST_BUFFER_CAPS (buf) == NULL) {
+ g_warning ("Could not take screenshot: %s", "no caps on buffer");
+ return FALSE;
+ }
+
+ /* convert to our desired format (RGB24) */
+
+ data = g_new0 (ScreenshotData, 1);
+ data->cb = cb;
+ data->user_data = user_data;
+
+ to_caps = gst_caps_new_simple ("video/x-raw-rgb",
+ "bpp", G_TYPE_INT, 24,
+ "depth", G_TYPE_INT, 24,
+ /* Note: we don't ask for a specific width/height here, so that
+ * videoscale can adjust dimensions from a non-1/1 pixel aspect
+ * ratio to a 1/1 pixel-aspect-ratio */
+ "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
+ "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+ "red_mask", G_TYPE_INT, 0xff0000,
+ "green_mask", G_TYPE_INT, 0x00ff00,
+ "blue_mask", G_TYPE_INT, 0x0000ff,
+ NULL);
+
+ if (video_fps_n > 0 && video_fps_d > 0) {
+ gst_caps_set_simple (to_caps, "framerate",
+ GST_TYPE_FRACTION, video_fps_n, video_fps_d,
+ NULL);
+ }
+
+ return bvw_frame_conv_convert (buf, to_caps, get_current_frame_step2, data);
+}
+
diff --git a/extensions/gstreamer_utils/gstreamer-utils.h b/extensions/gstreamer_utils/gstreamer-utils.h
new file mode 100644
index 0000000..033642d
--- /dev/null
+++ b/extensions/gstreamer_utils/gstreamer-utils.h
@@ -0,0 +1,47 @@
+/* -*- Mode: CPP; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GSTREAMER_UTILS_H
+#define GSTREAMER_UTILS_H
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gst/gst.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+typedef void (*FrameReadyCallback) (GdkPixbuf *, gpointer user_data);
+
+gboolean gstreamer_init (void);
+gboolean gstreamer_read_metadata_from_file (GFile *file,
+ GFileInfo *info,
+ GError **error);
+gboolean _gst_playbin_get_current_frame (GstElement *playbin,
+ int video_fps_n,
+ int video_fps_d,
+ FrameReadyCallback cb,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* GSTREAMER_UTILS_H */
diff --git a/extensions/gstreamer_utils/gstreamer_utils.extension.in.in b/extensions/gstreamer_utils/gstreamer_utils.extension.in.in
new file mode 100644
index 0000000..bb60f9b
--- /dev/null
+++ b/extensions/gstreamer_utils/gstreamer_utils.extension.in.in
@@ -0,0 +1,6 @@
+[Extension]
+Hidden=true
+
+[Loader]
+Type=module
+File=%LIBRARY%
diff --git a/extensions/gstreamer_utils/gstscreenshot.c b/extensions/gstreamer_utils/gstscreenshot.c
new file mode 100644
index 0000000..f7f177e
--- /dev/null
+++ b/extensions/gstreamer_utils/gstscreenshot.c
@@ -0,0 +1,259 @@
+/* Small helper element for format conversion
+ * (c) 2004 Ronald Bultje <rbultje ronald bitfreak net>
+ * Portion Copyright © 2009 Nokia Corporation and/or its
+ * subsidiary(-ies).* All rights reserved. *
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <string.h>
+
+#include "gstscreenshot.h"
+
+typedef struct {
+ GstBuffer *result;
+ GstElement *src;
+ GstElement *sink;
+ GstElement *pipeline;
+ BvwFrameConvCb cb;
+ gpointer cb_data;
+} GstScreenshotData;
+
+/* GST_DEBUG_CATEGORY_EXTERN (_totem_gst_debug_cat); */
+/* #define GST_CAT_DEFAULT _totem_gst_debug_cat */
+
+static void feed_fakesrc(GstElement *src, GstBuffer *buf, GstPad *pad,
+ gpointer data)
+{
+ GstBuffer *in_buf = GST_BUFFER(data);
+
+ g_assert(GST_BUFFER_SIZE(buf) >= GST_BUFFER_SIZE(in_buf));
+ g_assert(!GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_READONLY));
+
+ gst_buffer_set_caps(buf, GST_BUFFER_CAPS(in_buf));
+
+ memcpy(GST_BUFFER_DATA(buf), GST_BUFFER_DATA(in_buf),
+ GST_BUFFER_SIZE(in_buf));
+
+ GST_BUFFER_SIZE(buf) = GST_BUFFER_SIZE(in_buf);
+
+ GST_DEBUG("feeding buffer %p, size %u, caps %" GST_PTR_FORMAT,
+ buf, GST_BUFFER_SIZE(buf), GST_BUFFER_CAPS(buf));
+
+ gst_buffer_unref(in_buf);
+}
+
+static void save_result(GstElement *sink, GstBuffer *buf, GstPad *pad,
+ gpointer data)
+{
+ GstScreenshotData *gsd = data;
+
+ gsd->result = gst_buffer_ref(buf);
+
+ GST_DEBUG("received converted buffer %p with caps %" GST_PTR_FORMAT,
+ gsd->result, GST_BUFFER_CAPS(gsd->result));
+}
+
+static gboolean create_element(const gchar *factory_name, GstElement **element,
+ GError **err)
+{
+ *element = gst_element_factory_make(factory_name, NULL);
+ if (*element)
+ return TRUE;
+
+ if (err && *err == NULL) {
+ *err = g_error_new(
+ GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN,
+ "cannot create element '%s' - please check your "
+ "GStreamer installation", factory_name);
+ }
+
+ return FALSE;
+}
+
+static gboolean finalize_process(GstScreenshotData *gsd)
+{
+ g_signal_handlers_disconnect_matched(gsd->sink, (GSignalMatchType)
+ G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ save_result, NULL);
+ g_signal_handlers_disconnect_matched(gsd->src, (GSignalMatchType)
+ G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ feed_fakesrc, NULL);
+ gst_element_set_state(gsd->pipeline, GST_STATE_NULL);
+
+ g_free(gsd);
+
+ return FALSE;
+}
+
+static gboolean async_bus_handler(GstBus *bus, GstMessage *msg,
+ gpointer data)
+{
+ GstScreenshotData *gsd = data;
+ gboolean keep_watch = TRUE;
+
+ switch (GST_MESSAGE_TYPE(msg)) {
+ case GST_MESSAGE_EOS: {
+ if (gsd->result != NULL) {
+ GST_DEBUG("conversion successful: result = %p",
+ gsd->result);
+ } else {
+ GST_WARNING("EOS but no result frame?!");
+ }
+ gsd->cb(gsd->result, gsd->cb_data);
+ keep_watch = finalize_process(gsd);
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ gchar *dbg = NULL;
+ GError *error = NULL;
+
+ gst_message_parse_error(msg, &error, &dbg);
+ if (error != NULL) {
+ g_warning("Could not take screenshot: %s",
+ error->message);
+ GST_DEBUG("%s [debug: %s]", error->message,
+ GST_STR_NULL(dbg));
+ g_error_free(error);
+ } else {
+ g_warning("Could not take screenshot(and "
+ "NULL error!)");
+ }
+ g_free(dbg);
+ gsd->result = NULL;
+ gsd->cb(gsd->result, gsd->cb_data);
+ keep_watch = finalize_process(gsd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return keep_watch;
+}
+
+/* takes ownership of the input buffer */
+gboolean bvw_frame_conv_convert(GstBuffer *buf, GstCaps *to_caps,
+ BvwFrameConvCb cb, gpointer cb_data)
+{
+ static GstElement *src = NULL, *sink = NULL, *pipeline = NULL,
+ *filter1 = NULL, *filter2 = NULL;
+ static GstBus *bus;
+ GError *error = NULL;
+ GstCaps *to_caps_no_par;
+ GstScreenshotData *gsd;
+
+ g_return_val_if_fail(GST_BUFFER_CAPS(buf) != NULL, FALSE);
+ g_return_val_if_fail(cb != NULL, FALSE);
+
+ if (pipeline == NULL) {
+ GstElement *csp, *vscale;
+
+ pipeline = gst_pipeline_new("screenshot-pipeline");
+ if(pipeline == NULL) {
+ g_warning("Could not take screenshot: "
+ "no pipeline (unknown error)");
+ return FALSE;
+ }
+
+ /* videoscale is here to correct for the
+ * pixel-aspect-ratio for us */
+ GST_DEBUG("creating elements");
+ if(!create_element("fakesrc", &src, &error) ||
+ !create_element("ffmpegcolorspace", &csp, &error) ||
+ !create_element("videoscale", &vscale, &error) ||
+ !create_element("capsfilter", &filter1, &error) ||
+ !create_element("capsfilter", &filter2, &error) ||
+ !create_element("fakesink", &sink, &error)) {
+ g_warning("Could not take screenshot: %s",
+ error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ GST_DEBUG("adding elements");
+ gst_bin_add_many(GST_BIN(pipeline), src, csp, filter1, vscale,
+ filter2, sink, NULL);
+
+ g_object_set(sink, "preroll-queue-len", 1,
+ "signal-handoffs", TRUE, NULL);
+
+ /* set to 'fixed' sizetype */
+ g_object_set(src, "sizetype", 2, "num-buffers", 1,
+ "signal-handoffs", TRUE, NULL);
+
+ /* FIXME: linking is still way too expensive, profile
+ * this properly */
+ GST_DEBUG("linking src->csp");
+ if(!gst_element_link_pads(src, "src", csp, "sink"))
+ return FALSE;
+
+ GST_DEBUG("linking csp->filter1");
+ if(!gst_element_link_pads(csp, "src", filter1, "sink"))
+ return FALSE;
+
+ GST_DEBUG("linking filter1->vscale");
+ if(!gst_element_link_pads(filter1, "src", vscale, "sink"))
+ return FALSE;
+
+ GST_DEBUG("linking vscale->capsfilter");
+ if(!gst_element_link_pads(vscale, "src", filter2, "sink"))
+ return FALSE;
+
+ GST_DEBUG("linking capsfilter->sink");
+ if(!gst_element_link_pads(filter2, "src", sink, "sink"))
+ return FALSE;
+
+ bus = gst_element_get_bus(pipeline);
+ }
+
+ /* adding this superfluous capsfilter makes linking cheaper */
+ to_caps_no_par = gst_caps_copy(to_caps);
+ gst_structure_remove_field(gst_caps_get_structure(to_caps_no_par, 0),
+ "pixel-aspect-ratio");
+ g_object_set(filter1, "caps", to_caps_no_par, NULL);
+ gst_caps_unref(to_caps_no_par);
+
+ g_object_set(filter2, "caps", to_caps, NULL);
+ gst_caps_unref(to_caps);
+
+ gsd = g_new0(GstScreenshotData, 1);
+
+ gsd->src = src;
+ gsd->sink = sink;
+ gsd->pipeline = pipeline;
+ gsd->cb = cb;
+ gsd->cb_data = cb_data;
+
+ g_signal_connect(sink, "handoff", G_CALLBACK(save_result), gsd);
+
+ g_signal_connect(src, "handoff", G_CALLBACK(feed_fakesrc), buf);
+
+ gst_bus_add_watch(bus, async_bus_handler, gsd);
+
+ /* set to 'fixed' sizetype */
+ g_object_set(src, "sizemax", GST_BUFFER_SIZE(buf), NULL);
+
+ GST_DEBUG("running conversion pipeline");
+ gst_element_set_state(pipeline, GST_STATE_PLAYING);
+
+ return TRUE;
+}
diff --git a/extensions/gstreamer_utils/gstscreenshot.h b/extensions/gstreamer_utils/gstscreenshot.h
new file mode 100644
index 0000000..d3cf23c
--- /dev/null
+++ b/extensions/gstreamer_utils/gstscreenshot.h
@@ -0,0 +1,36 @@
+/* Small helper element for format conversion
+ * (c) 2004 Ronald Bultje <rbultje ronald bitfreak net>
+ * Portion Copyright © 2009 Nokia Corporation and/or its
+ * subsidiary(-ies).* All rights reserved. *
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __BVW_FRAME_CONV_H__
+#define __BVW_FRAME_CONV_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef void (*BvwFrameConvCb)(GstBuffer *result, gpointer user_data);
+
+gboolean bvw_frame_conv_convert (GstBuffer *buf, GstCaps *to,
+ BvwFrameConvCb cb, gpointer cb_data);
+
+G_END_DECLS
+
+#endif /* __BVW_FRAME_CONV_H__ */
diff --git a/extensions/slideshow/Makefile.am b/extensions/slideshow/Makefile.am
index e511d8d..8e5419d 100644
--- a/extensions/slideshow/Makefile.am
+++ b/extensions/slideshow/Makefile.am
@@ -23,6 +23,9 @@ libslideshow_la_SOURCES = \
libslideshow_la_CFLAGS = $(GTHUMB_CFLAGS) $(CLUTTER_CFLAGS) $(GSTREAMER_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb
libslideshow_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
libslideshow_la_LIBADD = $(GTHUMB_LIBS) $(CLUTTER_LIBS) $(GSTREAMER_LIBS) ../catalogs/libcatalogs.la
+if ENABLE_GSTREAMER
+libslideshow_la_LIBADD += ../gstreamer_utils/libgstreamer_utils.la
+endif
libslideshow_la_DEPENDENCIES = $(top_builddir)/gthumb/gthumb$(EXEEXT)
extensioninidir = $(extensiondir)
diff --git a/extensions/slideshow/gth-slideshow.c b/extensions/slideshow/gth-slideshow.c
index d0da6db..e728e4d 100644
--- a/extensions/slideshow/gth-slideshow.c
+++ b/extensions/slideshow/gth-slideshow.c
@@ -26,6 +26,7 @@
#include <clutter-gtk/clutter-gtk.h>
#if HAVE_GSTREAMER
#include <gst/gst.h>
+#include <extensions/gstreamer_utils/gstreamer-utils.h>
#endif
#include "gth-slideshow.h"
#include "gth-transition.h"
@@ -552,22 +553,22 @@ gth_slideshow_show_cb (GtkWidget *widget,
return;
#if HAVE_GSTREAMER
- if ((self->priv->audio_files != NULL) && (self->priv->audio_files[0] != NULL)) {
- self->priv->current_audio_file = 0;
- if (self->priv->playbin == NULL) {
- GstBus *bus;
-
- gst_init_check (NULL, NULL, NULL); /* FIXME */
-
- self->priv->playbin = gst_element_factory_make ("playbin", "playbin");
- bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->playbin));
- gst_bus_add_signal_watch (bus);
- g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), self);
+ if (gstreamer_init()) {
+ if ((self->priv->audio_files != NULL) && (self->priv->audio_files[0] != NULL)) {
+ self->priv->current_audio_file = 0;
+ if (self->priv->playbin == NULL) {
+ GstBus *bus;
+
+ self->priv->playbin = gst_element_factory_make ("playbin", "playbin");
+ bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->playbin));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), self);
+ }
+ else
+ gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
+ g_object_set (G_OBJECT (self->priv->playbin), "uri", self->priv->audio_files[self->priv->current_audio_file], NULL);
+ gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
}
- else
- gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
- g_object_set (G_OBJECT (self->priv->playbin), "uri", self->priv->audio_files[self->priv->current_audio_file], NULL);
- gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
}
#endif
diff --git a/gthumb/gth-main.c b/gthumb/gth-main.c
index 3b38407..8568e37 100644
--- a/gthumb/gth-main.c
+++ b/gthumb/gth-main.c
@@ -1198,7 +1198,8 @@ gth_main_activate_extensions (void)
"exiv2_tools",
"file_manager",
"file_tools",
- "gstreamer",
+ "gstreamer_tools",
+ "gstreamer_utils",
"image_print",
"image_rotation",
"image_viewer",
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]