/* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */ /* * GImageView * Copyright (C) 2001 Takuro Ashie * * 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 Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id: image_view_xine.c,v 1.8 2004/10/03 19:18:57 makeinu Exp $ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef ENABLE_XINE #include #include "gimv_image_view.h" #include "gimv_plugin.h" #include "gimv_thumb.h" #include "gimv_thumb_cache.h" #include "gimv_xine.h" #include "gtkutils.h" #include "prefs_xine.h" /* callback functions */ static void cb_playback_finished (GimvXine *gtx, GimvImageView *iv); static void cb_destroy (GtkWidget *widget, GimvImageView *iv); /* other private fiunctions */ static gboolean timeout_movie_status_listener (gpointer data); static gint timeout_create_thumbnail (gpointer data); static void install_movie_listener (GimvImageView *iv); static void remove_movie_listener (GimvImageView *iv); static void install_create_thumbnail_timer (GimvImageView *iv); static void remove_create_thumbnail_timer (GimvImageView *iv); static void imageview_xine_real_play (GimvImageView *iv, guint pos); static gboolean imageview_xine_is_playing (GimvImageView *iv); /* vertual functions */ static gboolean imageview_xine_is_supported (GimvImageView *iv, GimvImageInfo *info); static GtkWidget *imageview_xine_create (GimvImageView *iv); static void imageview_xine_create_thumbnail (GimvImageView *iv, const gchar *type); static gboolean imageview_xine_is_playable (GimvImageView *iv, GimvImageInfo *info); static gboolean imageview_xine_is_seekable (GimvImageView *iv); static guint imageview_xine_get_position (GimvImageView *iv); static guint imageview_xine_get_length (GimvImageView *iv); static void imageview_xine_play (GimvImageView *iv); static void imageview_xine_stop (GimvImageView *iv); static void imageview_xine_pause (GimvImageView *iv); static void imageview_xine_forward (GimvImageView *iv); static void imageview_xine_seek (GimvImageView *iv, gfloat pos); static void imageview_xine_eject (GimvImageView *iv); static GimvImageViewPlayableStatus imageview_xine_get_status (GimvImageView *iv); static guint imageview_xine_get_length (GimvImageView *iv); static guint imageview_xine_get_position (GimvImageView *iv); static GimvImageViewPlayableIF playable_if = { is_playable_fn: imageview_xine_is_playable, is_seekable_fn: imageview_xine_is_seekable, play_fn: imageview_xine_play, stop_fn: imageview_xine_stop, pause_fn: imageview_xine_pause, forward_fn: imageview_xine_forward, reverse_fn: NULL, seek_fn: imageview_xine_seek, eject_fn: imageview_xine_eject, get_status_fn: imageview_xine_get_status, get_length_fn: imageview_xine_get_length, get_position_fn: imageview_xine_get_position, }; static GimvImageViewPlugin imageview_xine[] = { { if_version: GIMV_IMAGE_VIEW_IF_VERSION, label: N_("Movie Player (Xine)"), priority_hint: G_PRIORITY_DEFAULT, is_supported_fn: imageview_xine_is_supported, create_fn: imageview_xine_create, create_thumbnail_fn: imageview_xine_create_thumbnail, fullscreen_fn: NULL, scalable: NULL, rotatable: NULL, playable: &playable_if, } }; static const gchar *mpeg_extensions[] = { "mpg", "mpeg", "mpe", "mp2", "m2v", "vob", }; static const gchar *quicktime_extensions[] = { "mov", "qt", "moov", "qtvr", }; static const gchar *asf_extensions[] = { "asf", }; static const gchar *wmv_extensions[] = { "wmv", }; static const gchar *msvideo_extensions[] = { "avi", }; static const gchar *audio_extensions[] = { "au", "snd", }; static const gchar *mp3_extensions[] = { "mp3", }; static const gchar *realmedia_extensions[] = { "ra", "ram", "rm", "sml", }; static const gchar *wav_extensions[] = { "wav", "wave", }; static GimvMimeTypeEntry xine_mime_types[] = { { mime_type: "application/vnd.ms-asf", description: "ASF Media", extensions: asf_extensions, extensions_len: sizeof (asf_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "audio/au", description: "Basic audio", extensions: audio_extensions, extensions_len: sizeof (audio_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "audio/x-mp3", description: "MPEG layer 3 Audio", extensions: mp3_extensions, extensions_len: sizeof (mp3_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "audio/x-pn-realaudio", description: "RealAudio/Video", extensions: realmedia_extensions, extensions_len: sizeof (realmedia_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "audio/x-real-audio", description: "RealAudio/Video", extensions: realmedia_extensions, extensions_len: sizeof (realmedia_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "audio/x-wav", description: "WAV Audio", extensions: wav_extensions, extensions_len: sizeof (wav_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "video/mpeg", description: "MPEG Video", extensions: mpeg_extensions, extensions_len: sizeof (mpeg_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "video/qicktime", description: "Quicktime Video", extensions: quicktime_extensions, extensions_len: sizeof (quicktime_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "video/x-ms-asf", description: "MS ASF Video", extensions: asf_extensions, extensions_len: sizeof (asf_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "video/x-ms-wmv", description: "MS WMV Video", extensions: wmv_extensions, extensions_len: sizeof (wmv_extensions) / sizeof (gchar *), icon: NULL, }, { mime_type: "video/x-msvideo", description: "Microsoft Video", extensions: msvideo_extensions, extensions_len: sizeof (msvideo_extensions) / sizeof (gchar *), icon: NULL, }, }; /* FIXME */ #include static const gchar * gimv_plugin_get_impl (guint idx, gpointer *impl, guint *size) { g_return_val_if_fail(impl, NULL); *impl = NULL; g_return_val_if_fail(size, NULL); *size = 0; switch (idx) { case 0: *impl = &imageview_xine; *size = sizeof(imageview_xine); return GIMV_PLUGIN_IMAGEVIEW_EMBEDER; case 1: gimv_xine_image_loader_get_impl(impl, size); return GIMV_PLUGIN_IMAGE_LOADER; default: return NULL; } return NULL; } /*GIMV_PLUGIN_GET_IMPL(plugin_impl, GIMV_PLUGIN_IMAGEVIEW_EMBEDER)*/ GIMV_PLUGIN_GET_MIME_TYPE(xine_mime_types) GimvPluginInfo gimv_plugin_info = { if_version: GIMV_PLUGIN_IF_VERSION, name: N_("Xine Embeder & Movie Frame Loader"), version: "0.2.0", author: N_("Takuro Ashie"), description: NULL, get_implement: gimv_plugin_get_impl, get_mime_type: gimv_plugin_get_mime_type, get_prefs_ui: gimv_prefs_ui_xine_get_page, }; static GimvPluginInfo *this; static GHashTable *movie_listener_id_table = NULL; static GHashTable *create_thumbnail_id_table = NULL; gchar * g_module_check_init (GModule *module) { g_module_symbol (module, "gimv_plugin_info", (gpointer) &this); return NULL; } GimvPluginInfo * gimv_xine_plugin_get_info (void) { return this; } /***************************************************************************** * * callback functions * *****************************************************************************/ static void cb_playback_finished (GimvXine *gtx, GimvImageView *iv) { gboolean next; g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv)); gtk_object_get (GTK_OBJECT (iv), "continuance_play", &next, NULL); if (next) gimv_image_view_next (iv); remove_create_thumbnail_timer (iv); remove_movie_listener (iv); gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop); } static void cb_destroy (GtkWidget *widget, GimvImageView *iv) { g_return_if_fail (iv); remove_create_thumbnail_timer (iv); remove_movie_listener (iv); } /***************************************************************************** * * other private functions * *****************************************************************************/ static gboolean timeout_movie_status_listener (gpointer data) { GimvImageView *iv = data; gint now, len; gdouble pos; g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), FALSE); g_return_val_if_fail (iv, FALSE); if (!iv->info || (!gimv_image_info_is_movie (iv->info) && !gimv_image_info_is_audio (iv->info))) { if (movie_listener_id_table) g_hash_table_remove (movie_listener_id_table, iv); gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop); return FALSE; } if (!imageview_xine_is_playing (iv)) { if (movie_listener_id_table) g_hash_table_remove (movie_listener_id_table, iv); gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop); return FALSE; } now = imageview_xine_get_position (iv); len = imageview_xine_get_length (iv); if (len > 0) pos = (gdouble) now / (gdouble) len * 100.0; else pos = 0.0; gimv_image_view_playable_set_position (iv, pos); return TRUE; } static gint timeout_create_thumbnail (gpointer data) { GList *node; GimvImageView *iv = data; g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), FALSE); node = g_list_find (gimv_image_view_get_list(), iv); if (!node) goto ERROR; gimv_image_view_create_thumbnail (iv); ERROR: g_hash_table_remove (create_thumbnail_id_table, iv); return FALSE; } static void install_movie_listener (GimvImageView *iv) { guint timer; remove_movie_listener (iv); timer = gtk_timeout_add (300, timeout_movie_status_listener, iv); g_hash_table_insert (movie_listener_id_table, iv, GUINT_TO_POINTER (timer)); } static void remove_movie_listener (GimvImageView *iv) { guint timer; gpointer data; if (!movie_listener_id_table) return; data = g_hash_table_lookup (movie_listener_id_table, iv); timer = GPOINTER_TO_UINT (data); if (timer > 0) gtk_timeout_remove (timer); g_hash_table_remove (movie_listener_id_table, iv); gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop); } static void install_create_thumbnail_timer (GimvImageView *iv) { gpointer data = g_hash_table_lookup (create_thumbnail_id_table, iv); guint timer = GPOINTER_TO_UINT (data); remove_create_thumbnail_timer (iv); timer = gtk_timeout_add (gimv_prefs_xine_get_delay(this) * 1000, (GtkFunction) timeout_create_thumbnail, (gpointer) iv); g_hash_table_insert (create_thumbnail_id_table, iv, GUINT_TO_POINTER (timer)); } static void remove_create_thumbnail_timer (GimvImageView *iv) { gpointer id_p; guint id; id_p = g_hash_table_lookup (create_thumbnail_id_table, iv); id = GPOINTER_TO_UINT (id_p); if (id > 0) gtk_timeout_remove (id); g_hash_table_remove (create_thumbnail_id_table, iv); } static void imageview_xine_real_play (GimvImageView *iv, guint pos) { GimvXine *gtx; gchar *filename, *cache_file; g_return_if_fail (iv); if (!iv->info) return; if (!gimv_image_info_is_movie (iv->info) && !gimv_image_info_is_audio (iv->info)) return; g_return_if_fail (GTK_IS_BIN (iv->draw_area)); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); if (!GIMV_IS_XINE (gtx)) return; gimv_xine_set_mrl (gtx, gimv_image_info_get_path (iv->info)); gimv_xine_play (gtx, 0, pos); filename = gimv_image_info_get_path_with_archive (iv->info); cache_file = gimv_thumb_find_thumbcache (filename, NULL); if (!cache_file && !gimv_image_info_is_url (iv->info)) { install_create_thumbnail_timer (iv); } g_free (filename); g_free (cache_file); install_movie_listener (iv); gimv_image_view_playable_set_status (iv, GimvImageViewPlayablePlay); } static gboolean imageview_xine_is_playing (GimvImageView *iv) { GimvXine *gtx; g_return_val_if_fail (iv, FALSE); g_return_val_if_fail (iv->info, FALSE); g_return_val_if_fail (gimv_image_info_is_movie (iv->info) || gimv_image_info_is_audio (iv->info), FALSE); g_return_val_if_fail (GTK_IS_BIN (iv->draw_area), FALSE); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); g_return_val_if_fail (GIMV_IS_XINE (gtx), FALSE); return gimv_xine_is_playing (GIMV_XINE (gtx)); } /***************************************************************************** * * Virtual functions * *****************************************************************************/ static gboolean imageview_xine_is_supported (GimvImageView *iv, GimvImageInfo *info) { g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), FALSE); if (!info) return FALSE; return gimv_image_info_is_movie (info) || gimv_image_info_is_audio (info); } static GtkWidget * imageview_xine_create (GimvImageView *iv) { GtkWidget *gxine, *widget; if (!movie_listener_id_table) movie_listener_id_table = g_hash_table_new (g_direct_hash, g_direct_equal); if (!create_thumbnail_id_table) create_thumbnail_id_table = g_hash_table_new (g_direct_hash, g_direct_equal); widget = gtk_event_box_new (); gxine = gimv_xine_new (NULL, NULL); gtk_container_add (GTK_CONTAINER (widget), gxine); gtk_widget_show (gxine); gtk_signal_connect (GTK_OBJECT (gxine), "playback_finished", GTK_SIGNAL_FUNC (cb_playback_finished), iv); gtk_signal_connect (GTK_OBJECT (gxine), "destroy", GTK_SIGNAL_FUNC (cb_destroy), iv); return widget; } static void imageview_xine_create_thumbnail (GimvImageView *iv, const gchar *cache_write_type) { GimvImage *imcache; GtkWidget *gtkxine; gint width, height; guchar *rgb; GimvImage *image; gchar *filename; if (!gimv_image_info_is_movie (iv->info)) return; if (!GTK_IS_BIN (iv->draw_area)) return; gtkxine = GTK_BIN (iv->draw_area)->child; if (!GIMV_IS_XINE (gtkxine)) return; if (!gimv_xine_is_playing (GIMV_XINE (gtkxine))) return; rgb = gimv_xine_get_current_frame_rgb (GIMV_XINE (gtkxine), &width, &height); if (!rgb) return; image = gimv_image_create_from_data (rgb, width, height, FALSE); if (!image) { g_free (rgb); return; } filename = gimv_image_info_get_path_with_archive (iv->info); imcache = gimv_thumb_cache_save (filename, cache_write_type, image, iv->info); g_free (filename); gimv_image_unref (image); if (imcache) { gimv_image_unref (imcache); gtk_signal_emit_by_name (GTK_OBJECT (iv), "thumbnail_created", iv->info); } } static gboolean imageview_xine_is_playable (GimvImageView *iv, GimvImageInfo *info) { return TRUE; } static gboolean imageview_xine_is_seekable (GimvImageView *iv) { return TRUE; } static void imageview_xine_play (GimvImageView *iv) { GimvXine *gtx; g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv)); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); if (!GIMV_IS_XINE (gtx)) return; if (!imageview_xine_is_playing (iv)) { imageview_xine_real_play (iv, 0); } else if (gimv_xine_get_speed (gtx) != GIMV_XINE_SPEED_NORMAL) { gimv_xine_set_speed (gtx, GIMV_XINE_SPEED_NORMAL); gimv_image_view_playable_set_status (iv, GimvImageViewPlayablePlay); } else { imageview_xine_pause (iv); } } static void imageview_xine_stop (GimvImageView *iv) { GimvXine *gtx; g_return_if_fail (iv); if (!iv->info) return; if (!gimv_image_info_is_movie (iv->info) && !gimv_image_info_is_audio (iv->info)) return; g_return_if_fail (GTK_IS_BIN (iv->draw_area)); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); if (!GIMV_IS_XINE (gtx)) return; remove_create_thumbnail_timer (iv); remove_movie_listener (iv); gimv_xine_stop (gtx); gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop); } static void imageview_xine_pause (GimvImageView *iv) { GimvXine *gtx; g_return_if_fail (iv); if (!iv->info) return; if (!gimv_image_info_is_movie (iv->info) && !gimv_image_info_is_audio (iv->info)) return; g_return_if_fail (GTK_IS_BIN (iv->draw_area)); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); if (!GIMV_IS_XINE (gtx)) return; gimv_xine_set_speed (gtx, GIMV_XINE_SPEED_PAUSE); gimv_image_view_playable_set_status (iv, GimvImageViewPlayablePause); } static void imageview_xine_forward (GimvImageView *iv) { GimvXine *gtx; g_return_if_fail (iv); if (!iv->info) return; if (!gimv_image_info_is_movie (iv->info) && !gimv_image_info_is_audio (iv->info)) return; g_return_if_fail (GTK_IS_BIN (iv->draw_area)); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); if (!GIMV_IS_XINE (gtx)) return; gimv_xine_set_speed (gtx, 12); gimv_image_view_playable_set_status (iv, GimvImageViewPlayableForward); } static void imageview_xine_seek (GimvImageView *iv, gfloat pos) { GimvXine *gtx; gfloat ppos; g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv)); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); if (!GIMV_IS_XINE (gtx)) return; ppos = (gfloat) pos * (gfloat) gimv_xine_get_stream_length (gtx) / 100.0; imageview_xine_real_play (iv, ppos); } static void imageview_xine_eject (GimvImageView *iv) { GList *plugin_list = NULL, *mrl_list = NULL; GimvXine *gtx; gchar **plugins, *str, **mrls; gint i, num; g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv)); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); if (!GIMV_IS_XINE (gtx)) return; if (gimv_xine_is_playing (gtx)) { gtkutil_message_dialog (_("Error!!"), _("Please stop first."), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (iv)))); return; } plugins = gimv_xine_get_autoplay_input_plugin_ids (gtx); for (i = 0; plugins[i]; i++) { plugin_list = g_list_append (plugin_list, plugins[i]); } if (!plugin_list) { gtkutil_message_dialog (_("Error!!"), _("No available plugin found."), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (iv)))); return; } str = gtkutil_popup_textentry (_("Available plugins"), _("Plugin: "), plugin_list->data, plugin_list, -1, TEXT_ENTRY_NO_EDITABLE, GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (iv)))); if (!str) goto ERROR; mrls = gimv_xine_get_autoplay_mrls (gtx, str, &num); for (i = 0; i < num; i++) { GimvImageInfo *info; info = gimv_image_info_get_url (mrls[i]); info->flags |= GIMV_IMAGE_INFO_MOVIE_FLAG; info->flags |= GIMV_IMAGE_INFO_MRL_FLAG; mrl_list = g_list_append (mrl_list, info); } if (mrl_list) { gimv_image_view_set_list_self (iv, mrl_list, mrl_list); gimv_image_view_change_image (iv, mrl_list->data); } else { gtkutil_message_dialog (_("Error!!"), _("No available MRL found."), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (iv)))); } g_free (str); ERROR: g_list_free (plugin_list); } static GimvImageViewPlayableStatus imageview_xine_get_status (GimvImageView *iv) { GimvXine *gtx; GimvXineSpeed speed; g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), GimvImageViewPlayableStop); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); g_return_val_if_fail (GIMV_IS_XINE (gtx), GimvImageViewPlayableStop); if (!imageview_xine_is_playing (iv)) return GimvImageViewPlayableStop; speed = gimv_xine_get_speed (gtx); if (speed > GIMV_XINE_SPEED_NORMAL) { return GimvImageViewPlayableForward; } else if (speed == GIMV_XINE_SPEED_PAUSE) { return GimvImageViewPlayablePause; } else if (speed == GIMV_XINE_SPEED_NORMAL) { return GimvImageViewPlayablePlay; } return GimvImageViewPlayableDisable; } static guint imageview_xine_get_position (GimvImageView *iv) { GimvXine *gtx; g_return_val_if_fail (iv, 0); g_return_val_if_fail (iv->info, 0); g_return_val_if_fail (gimv_image_info_is_movie (iv->info) || gimv_image_info_is_audio (iv->info), 0); g_return_val_if_fail (GTK_IS_BIN (iv->draw_area), 0); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); g_return_val_if_fail (GIMV_IS_XINE (gtx), 0); return gimv_xine_get_current_time (GIMV_XINE (gtx)); } static guint imageview_xine_get_length (GimvImageView *iv) { GimvXine *gtx; g_return_val_if_fail (iv, 0); g_return_val_if_fail (iv->info, 0); g_return_val_if_fail (gimv_image_info_is_movie (iv->info) || gimv_image_info_is_audio (iv->info), 0); g_return_val_if_fail (GTK_IS_BIN (iv->draw_area), FALSE); gtx = GIMV_XINE (GTK_BIN (iv->draw_area)->child); g_return_val_if_fail (GIMV_IS_XINE (gtx), 0); return gimv_xine_get_stream_length (GIMV_XINE (gtx)); } #endif /* ENABLE_XINE */