/* * Copyright (C) 2003 the Gstreamer project * Julien Moutte * * 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: bacon-video-widget-gst.c,v 1.85 2004/07/22 17:59:01 hadess Exp $ * */ #include /* libgstplay */ #include /* gstgconf */ #include /* GStreamer Interfaces */ #include #include #include #include /* system */ #include #include /* gtk+/gnome */ #include #include #include "bacon-video-widget.h" #include "baconvideowidget-marshal.h" #include "scrsaver.h" #include "video-utils.h" #include "gstvideowidget.h" #include #define _(String) gettext (String) #ifdef gettext_noop # define N_(String) gettext_noop (String) #else # define N_(String) (String) #endif #define DEFAULT_HEIGHT 420 #define DEFAULT_WIDTH 315 #define CONFIG_FILE ".gnome2"G_DIR_SEPARATOR_S"totem_config" #define DEFAULT_TITLE _("Totem Video Window") /* Signals */ enum { ERROR, EOS, TITLE_CHANGE, CHANNELS_CHANGE, TICK, GOT_METADATA, BUFFERING, SPEED_WARNING, LAST_SIGNAL }; /* Enum for none-signal stuff that needs to go through the AsyncQueue */ enum { RATIO = LAST_SIGNAL }; /* Arguments */ enum { PROP_0, PROP_LOGO_MODE, PROP_POSITION, PROP_CURRENT_TIME, PROP_STREAM_LENGTH, PROP_PLAYING, PROP_SEEKABLE, PROP_SHOWCURSOR, PROP_MEDIADEV, PROP_SHOW_VISUALS }; struct BaconVideoWidgetPrivate { double display_ratio; GstPlay *play; GstVideoWidget *vw; GstMixer *mixer; GstMixerTrack *mixer_track; GstXOverlay *xoverlay; GstColorBalance *balance; GdkPixbuf *logo_pixbuf; gboolean media_has_video; gint64 stream_length; gint64 current_time_nanos; gint64 current_time; float current_position; GHashTable *metadata_hash; char *last_error_message; GdkWindow *video_window; gint video_window_x; gint video_window_y; gint video_window_w; gint video_window_h; /* Visual effects */ GList *vis_plugins_list; gboolean show_vfx; gboolean using_vfx; GstElement *vis_element; /* Other stuff */ int xpos, ypos; gboolean logo_mode; gboolean auto_resize; GAsyncQueue *queue; guint video_width; guint video_height; guint init_width; guint init_height; char *mrl; char *media_device; }; enum { ASYNC_VIDEO_SIZE, ASYNC_ERROR, ASYNC_FOUND_TAG, ASYNC_EOS }; typedef struct _BVWSignal BVWSignal; struct _BVWSignal { gint signal_id; union { struct { gint width; gint height; } video_size; struct { GstElement *element; GError *error; char *debug_message; } error; struct { GstElement *source; GstTagList *tag_list; } found_tag; } signal_data; }; static void bacon_video_widget_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec); static void bacon_video_widget_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec); static void bacon_video_widget_finalize (GObject * object); static GtkWidgetClass *parent_class = NULL; static int bvw_table_signals[LAST_SIGNAL] = { 0 }; static void bacon_video_widget_vw_realized (GtkWidget * widget, BaconVideoWidget * bvw) { GdkWindow *video_window = NULL; video_window = gst_video_widget_get_video_window (GST_VIDEO_WIDGET (widget)); if (video_window) bvw->priv->video_window = video_window; if (GST_IS_X_OVERLAY (bvw->priv->xoverlay) && GDK_IS_WINDOW (video_window)) gst_x_overlay_set_xwindow_id (bvw->priv->xoverlay, GDK_WINDOW_XID (video_window)); else g_warning ("Could not find a XOVERLAY element in the bin"); } static void bacon_video_widget_vw_allocate (GtkWidget * widget, GtkAllocation *allocation, BaconVideoWidget * bvw) { gint x, y, w, h; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); gdk_window_get_geometry (bvw->priv->video_window, &x, &y, &w, &h, NULL); bvw->priv->video_window_x = x; bvw->priv->video_window_y = y; bvw->priv->video_window_w = w; bvw->priv->video_window_h = h; } static gboolean bacon_video_widget_vw_exposed (GtkWidget *widget, GdkEventExpose *event, BaconVideoWidget *bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); if (GST_IS_X_OVERLAY (bvw->priv->xoverlay)) { gst_x_overlay_expose (bvw->priv->xoverlay); } return FALSE; } static gboolean bacon_video_widget_button_press (GtkWidget *widget, GdkEventButton *event, BaconVideoWidget *bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); if (GST_IS_NAVIGATION (bvw->priv->xoverlay)) { gint x, y, w, h; x = bvw->priv->video_window_x; y = bvw->priv->video_window_y; w = bvw->priv->video_window_w; h = bvw->priv->video_window_h; if ( (event->x >= x) && (event->x <= x+w) && (event->y >= y) && (event->y <= y +h) ) gst_navigation_send_mouse_event (GST_NAVIGATION (bvw->priv->xoverlay), "mouse-button-press", event->button, event->x - x, event->y - y); } return TRUE; } static gboolean bacon_video_widget_button_release (GtkWidget *widget, GdkEventButton *event, BaconVideoWidget *bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); if (GST_IS_NAVIGATION (bvw->priv->xoverlay)) { gint x, y, w, h; x = bvw->priv->video_window_x; y = bvw->priv->video_window_y; w = bvw->priv->video_window_w; h = bvw->priv->video_window_h; if ( (event->x >= x) && (event->x <= x+w) && (event->y >= y) && (event->y <= y +h) ) gst_navigation_send_mouse_event (GST_NAVIGATION (bvw->priv->xoverlay), "mouse-button-release", event->button, event->x - x, event->y - y); } return TRUE; } static gboolean bacon_video_widget_motion_notify_callback (GtkWidget *widget, GdkEventMotion *event, BaconVideoWidget *bvw) { gboolean return_code; g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); if (GST_IS_NAVIGATION (bvw->priv->xoverlay)) { gint x, y, w, h; x = bvw->priv->video_window_x; y = bvw->priv->video_window_y; w = bvw->priv->video_window_w; h = bvw->priv->video_window_h; if ( (event->x >= x) && (event->x <= x+w) && (event->y >= y) && (event->y <= y +h) ) gst_navigation_send_mouse_event (GST_NAVIGATION (bvw->priv->xoverlay), "mouse-move", 0, event->x, event->y); } g_signal_emit_by_name (G_OBJECT (bvw), "motion-notify-event", event, &return_code); return return_code; } static void bacon_video_widget_size_request (GtkWidget * widget, GtkRequisition * requisition) { BaconVideoWidget *bvw; GtkRequisition child_requisition; g_return_if_fail (widget != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (widget)); bvw = BACON_VIDEO_WIDGET (widget); gtk_widget_size_request (GTK_WIDGET (bvw->priv->vw), &child_requisition); requisition->width = child_requisition.width; requisition->height = child_requisition.height; } static void bacon_video_widget_size_allocate (GtkWidget * widget, GtkAllocation * allocation) { BaconVideoWidget *bvw; GtkAllocation child_allocation; g_return_if_fail (widget != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (widget)); bvw = BACON_VIDEO_WIDGET (widget); widget->allocation = *allocation; child_allocation.x = allocation->x; child_allocation.y = allocation->y; child_allocation.width = allocation->width; child_allocation.height = allocation->height; gtk_widget_size_allocate (GTK_WIDGET (bvw->priv->vw), &child_allocation); if ((bvw->priv->init_width == 0) && (bvw->priv->init_height == 0)) { bvw->priv->init_width = allocation->width; bvw->priv->init_height = allocation->height; gst_video_widget_set_minimum_size (bvw->priv->vw, bvw->priv->init_width, bvw->priv->init_height); } } static void bacon_video_widget_class_init (BaconVideoWidgetClass * klass) { GObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GObjectClass *) klass; widget_class = (GtkWidgetClass *) klass; parent_class = gtk_type_class (gtk_box_get_type ()); /* GtkWidget */ widget_class->size_request = bacon_video_widget_size_request; widget_class->size_allocate = bacon_video_widget_size_allocate; /* GObject */ object_class->set_property = bacon_video_widget_set_property; object_class->get_property = bacon_video_widget_get_property; object_class->finalize = bacon_video_widget_finalize; /* Properties */ g_object_class_install_property (object_class, PROP_LOGO_MODE, g_param_spec_boolean ("logo_mode", NULL, NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_POSITION, g_param_spec_int ("position", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_STREAM_LENGTH, g_param_spec_int64 ("stream_length", NULL, NULL, 0, G_MAXINT64, 0, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_PLAYING, g_param_spec_boolean ("playing", NULL, NULL, FALSE, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_SEEKABLE, g_param_spec_boolean ("seekable", NULL, NULL, FALSE, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_SHOWCURSOR, g_param_spec_boolean ("showcursor", NULL, NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_MEDIADEV, g_param_spec_string ("mediadev", NULL, NULL, FALSE, G_PARAM_WRITABLE)); g_object_class_install_property (object_class, PROP_SHOW_VISUALS, g_param_spec_boolean ("showvisuals", NULL, NULL, FALSE, G_PARAM_WRITABLE)); /* Signals */ bvw_table_signals[ERROR] = g_signal_new ("error", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, error), NULL, NULL, baconvideowidget_marshal_VOID__STRING_BOOLEAN_BOOLEAN, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); bvw_table_signals[EOS] = g_signal_new ("eos", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, eos), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); bvw_table_signals[GOT_METADATA] = g_signal_new ("got-metadata", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, got_metadata), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); bvw_table_signals[TITLE_CHANGE] = g_signal_new ("title-change", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, title_change), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); bvw_table_signals[CHANNELS_CHANGE] = g_signal_new ("channels-change", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, channels_change), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); bvw_table_signals[TICK] = g_signal_new ("tick", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, tick), NULL, NULL, baconvideowidget_marshal_VOID__INT64_INT64_FLOAT_BOOLEAN, G_TYPE_NONE, 4, G_TYPE_INT64, G_TYPE_INT64, G_TYPE_FLOAT, G_TYPE_BOOLEAN); bvw_table_signals[BUFFERING] = g_signal_new ("buffering", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, buffering), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); bvw_table_signals[SPEED_WARNING] = g_signal_new ("speed-warning", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (BaconVideoWidgetClass, speed_warning), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void bacon_video_widget_init (BaconVideoWidget * bvw) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); GTK_WIDGET_SET_FLAGS (GTK_WIDGET (bvw), GTK_CAN_FOCUS); GTK_WIDGET_SET_FLAGS (GTK_WIDGET (bvw), GTK_NO_WINDOW); GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (bvw), GTK_DOUBLE_BUFFERED); bvw->priv = g_new0 (BaconVideoWidgetPrivate, 1); bvw->priv->queue = g_async_queue_new (); } static void shrink_toplevel (BaconVideoWidget * bvw) { GtkWidget *toplevel; GtkRequisition requisition; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); toplevel = gtk_widget_get_toplevel (GTK_WIDGET (bvw)); gtk_widget_size_request (toplevel, &requisition); gtk_window_resize (GTK_WINDOW (toplevel), requisition.width, requisition.height); } static void store_tag (const GstTagList *list, const gchar *tag, gpointer data) { BaconVideoWidget *bvw = NULL; char *str = NULL; if (data && BACON_IS_VIDEO_WIDGET (data)) bvw = BACON_VIDEO_WIDGET (data); if (gst_tag_list_get_tag_size (list, tag)) { if (gst_tag_get_type (tag) == G_TYPE_STRING) { g_assert (gst_tag_list_get_string_index (list, tag, 0, &str)); } else { str = g_strdup_value_contents ( gst_tag_list_get_value_index (list, tag, 0)); } g_hash_table_replace (bvw->priv->metadata_hash, g_strdup (gst_tag_get_nick (tag)), str); } } static gboolean bacon_video_widget_signal_idler (BaconVideoWidget *bvw) { BVWSignal *signal = NULL; gint queue_length; g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); signal = g_async_queue_try_pop (bvw->priv->queue); if (!signal) return FALSE; switch (signal->signal_id) { case ASYNC_VIDEO_SIZE: { gint width, height; width = signal->signal_data.video_size.width; height = signal->signal_data.video_size.height; if ( (width <= 1) || (height <= 1) ) return FALSE; bvw->priv->media_has_video = TRUE; bvw->priv->video_width = width; bvw->priv->video_height = height; if (bvw->priv->vw) { gst_video_widget_set_logo_focus (bvw->priv->vw, FALSE); gst_video_widget_set_source_size (bvw->priv->vw, width, height); } break; } case ASYNC_ERROR: { char *error_message = NULL; gboolean emit = TRUE; if (signal->signal_data.error.error) error_message = signal->signal_data.error.error->message; if (bvw->priv->last_error_message) { /* Let's check the latest error message */ if (g_ascii_strcasecmp (error_message, bvw->priv->last_error_message) == 0) emit = FALSE; } if (emit) { g_signal_emit (G_OBJECT (bvw), bvw_table_signals[ERROR], 0, error_message, TRUE, FALSE); /* Keep a copy of the last emitted message */ if (bvw->priv->last_error_message) g_free (bvw->priv->last_error_message); bvw->priv->last_error_message = g_strdup (error_message); } /* Cleaning the error infos */ if (signal->signal_data.error.error) g_error_free (signal->signal_data.error.error); if (signal->signal_data.error.debug_message) g_free (signal->signal_data.error.debug_message); break; } case ASYNC_FOUND_TAG: { GstTagList *tag_list = signal->signal_data.found_tag.tag_list; if (GST_IS_TAG_LIST (tag_list)) { gst_tag_list_foreach (tag_list, store_tag, bvw); gst_tag_list_free (tag_list); g_signal_emit (G_OBJECT (bvw), bvw_table_signals[GOT_METADATA], 0, NULL); } break; } case ASYNC_EOS: { gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_READY); g_signal_emit (G_OBJECT (bvw), bvw_table_signals[EOS], 0, NULL); break; } default: break; } g_free (signal); queue_length = g_async_queue_length (bvw->priv->queue); return (queue_length > 0); } static void got_found_tag (GstPlay *play, GstElement *source, GstTagList *tag_list, BaconVideoWidget * bvw) { BVWSignal *signal; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); signal = g_new0 (BVWSignal, 1); signal->signal_id = ASYNC_FOUND_TAG; signal->signal_data.found_tag.source = source; signal->signal_data.found_tag.tag_list = gst_tag_list_copy (tag_list); g_async_queue_push (bvw->priv->queue, signal); g_idle_add ((GSourceFunc) bacon_video_widget_signal_idler, bvw); } static void got_video_size (GstPlay * play, gint width, gint height, BaconVideoWidget * bvw) { BVWSignal *signal; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); signal = g_new0 (BVWSignal, 1); signal->signal_id = ASYNC_VIDEO_SIZE; signal->signal_data.video_size.width = width; signal->signal_data.video_size.height = height; g_async_queue_push (bvw->priv->queue, signal); g_idle_add ((GSourceFunc) bacon_video_widget_signal_idler, bvw); } static void got_eos (GstPlay * play, BaconVideoWidget * bvw) { BVWSignal *signal; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); signal = g_new0 (BVWSignal, 1); signal->signal_id = ASYNC_EOS; g_async_queue_push (bvw->priv->queue, signal); g_idle_add ((GSourceFunc) bacon_video_widget_signal_idler, bvw); } static void got_stream_length (GstPlay * play, gint64 length_nanos, BaconVideoWidget * bvw) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); bvw->priv->stream_length = (gint64) length_nanos / GST_MSECOND; g_signal_emit (G_OBJECT (bvw), bvw_table_signals[GOT_METADATA], 0, NULL); } static void got_time_tick (GstPlay * play, gint64 time_nanos, BaconVideoWidget * bvw) { gboolean seekable; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); if (bvw->priv->logo_mode != FALSE) return; bvw->priv->current_time_nanos = time_nanos; bvw->priv->current_time = (gint64) time_nanos / GST_MSECOND; if (bvw->priv->stream_length == 0) bvw->priv->current_position = 0; else { bvw->priv->current_position = (float) bvw->priv->current_time / bvw->priv->stream_length; } seekable = bacon_video_widget_is_seekable (bvw); g_signal_emit (G_OBJECT (bvw), bvw_table_signals[TICK], 0, bvw->priv->current_time, bvw->priv->stream_length, bvw->priv->current_position, seekable); } static void got_error (GstPlay *play, GstElement *orig, GError *error, gchar *debug, BaconVideoWidget * bvw) { BVWSignal *signal; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); signal = g_new0 (BVWSignal, 1); signal->signal_id = ASYNC_ERROR; signal->signal_data.error.element = orig; signal->signal_data.error.error = g_error_copy (error); if (debug) signal->signal_data.error.debug_message = g_strdup (debug); g_async_queue_push (bvw->priv->queue, signal); g_idle_add ((GSourceFunc) bacon_video_widget_signal_idler, bvw); } static void bacon_video_widget_finalize (GObject * object) { BaconVideoWidget *bvw = (BaconVideoWidget *) object; if (bvw->priv->media_device) { g_free (bvw->priv->media_device); bvw->priv->media_device = NULL; } if (bvw->priv->mrl) { g_free (bvw->priv->mrl); bvw->priv->mrl = NULL; } if (bvw->priv->queue) { g_async_queue_unref (bvw->priv->queue); bvw->priv->queue = NULL; } if (bvw->priv->metadata_hash) { g_hash_table_destroy (bvw->priv->metadata_hash); bvw->priv->metadata_hash = NULL; } if (bvw->priv->vis_plugins_list) { g_list_foreach (bvw->priv->vis_plugins_list, (GFunc) g_free, NULL); g_list_free (bvw->priv->vis_plugins_list); bvw->priv->vis_plugins_list = NULL; } if (bvw->priv->play != NULL && GST_IS_PLAY (bvw->priv->play)) { gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_READY); /* FIXME: we are leaking the GstPlay. */ /* g_object_unref (bvw->priv->play); */ bvw->priv->play = NULL; } } static void bacon_video_widget_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { } static void bacon_video_widget_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { } static gboolean dummy_true_function (gpointer key, gpointer value, gpointer data) { return TRUE; } /* ============================================================= */ /* */ /* Public Methods */ /* */ /* ============================================================= */ char * bacon_video_widget_get_backend_name (BaconVideoWidget * bvw) { guint major, minor, micro; gst_version (&major, &minor, µ); return g_strdup_printf ("GStreamer version %d.%d.%d", major, minor, micro); } int bacon_video_widget_get_subtitle (BaconVideoWidget * bvw) { return -1; } void bacon_video_widget_set_subtitle (BaconVideoWidget * bvw, int subtitle) { } GList * bacon_video_widget_get_subtitles (BaconVideoWidget * bvw) { return NULL; } GList * bacon_video_widget_get_languages (BaconVideoWidget * bvw) { return NULL; } int bacon_video_widget_get_language (BaconVideoWidget * bvw) { return -1; } void bacon_video_widget_set_language (BaconVideoWidget * bvw, int language) { } int bacon_video_widget_get_connection_speed (BaconVideoWidget * bvw) { return 0; } void bacon_video_widget_set_connection_speed (BaconVideoWidget * bvw, int speed) { } void bacon_video_widget_set_deinterlacing (BaconVideoWidget * bvw, gboolean deinterlace) { } gboolean bacon_video_widget_get_deinterlacing (BaconVideoWidget * bvw) { return FALSE; } gboolean bacon_video_widget_set_tv_out (BaconVideoWidget * bvw, TvOutType tvout) { return FALSE; } TvOutType bacon_video_widget_get_tv_out (BaconVideoWidget * bvw) { return TV_OUT_NONE; } BaconVideoWidgetAudioOutType bacon_video_widget_get_audio_out_type (BaconVideoWidget *bvw) { return BVW_AUDIO_SOUND_STEREO; } void bacon_video_widget_set_audio_out_type (BaconVideoWidget *bvw, BaconVideoWidgetAudioOutType type) { } /* =========================================== */ /* */ /* Play/Pause, Stop */ /* */ /* =========================================== */ gboolean bacon_video_widget_open (BaconVideoWidget * bvw, const gchar * mrl, GError ** error) { GstElement * datasrc = NULL; g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (mrl != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (bvw->priv->play != NULL, FALSE); g_return_val_if_fail (bvw->priv->mrl == NULL, FALSE); bvw->priv->mrl = g_strdup (mrl); /* Resetting last_error_message to NULL */ if (bvw->priv->last_error_message) { g_free (bvw->priv->last_error_message); bvw->priv->last_error_message = NULL; } /* Cleaning metadata hash */ g_hash_table_foreach_remove (bvw->priv->metadata_hash, dummy_true_function, bvw); gst_video_widget_set_source_size (GST_VIDEO_WIDGET (bvw->priv->vw), 1, 1); bvw->priv->media_has_video = FALSE; bvw->priv->stream_length = 0; if (g_file_test (mrl, G_FILE_TEST_EXISTS)) { datasrc = gst_element_factory_make ("filesrc", "source"); if (GST_IS_ELEMENT (datasrc)) gst_play_set_data_src (bvw->priv->play, datasrc); gst_play_set_location (bvw->priv->play, mrl); } else if (g_str_has_prefix (mrl, "dvd://")) { datasrc = gst_element_factory_make ("dvdnavsrc", "source"); if (GST_IS_ELEMENT (datasrc)) gst_play_set_data_src (bvw->priv->play, datasrc); gst_play_set_location (bvw->priv->play, bvw->priv->media_device); } else if (g_str_has_prefix (mrl, "v4l://")) { datasrc = gst_element_factory_make ("v4lsrc", "source"); if (GST_IS_ELEMENT (datasrc)) gst_play_set_data_src (bvw->priv->play, datasrc); gst_play_set_location (bvw->priv->play, "/dev/video"); } else if (g_str_has_prefix (mrl, "cdda://")) { datasrc = gst_element_factory_make ("cdparanoia", "source"); if (GST_IS_ELEMENT (datasrc)) gst_play_set_data_src (bvw->priv->play, datasrc); gst_play_set_location (bvw->priv->play, bvw->priv->media_device); } else { datasrc = gst_element_factory_make ("gnomevfssrc", "source"); if (GST_IS_ELEMENT (datasrc)) gst_play_set_data_src (bvw->priv->play, datasrc); gst_play_set_location (bvw->priv->play, mrl); } return TRUE; } gboolean bacon_video_widget_play (BaconVideoWidget * bvw, GError ** error) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); /* Resetting last_error_message to NULL */ if (bvw->priv->last_error_message) { g_free (bvw->priv->last_error_message); bvw->priv->last_error_message = NULL; } gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_PLAYING); return TRUE; } gboolean bacon_video_widget_seek (BaconVideoWidget *bvw, float position, GError **gerror) { gint64 seek_time, length_nanos; g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); /* Resetting last_error_message to NULL */ if (bvw->priv->last_error_message) { g_free (bvw->priv->last_error_message); bvw->priv->last_error_message = NULL; } length_nanos = (gint64) (bvw->priv->stream_length * GST_MSECOND); seek_time = (gint64) (length_nanos * position); gst_play_seek_to_time (bvw->priv->play, seek_time); return TRUE; } gboolean bacon_video_widget_seek_time (BaconVideoWidget *bvw, gint64 time, GError **gerror) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); /* Resetting last_error_message to NULL */ if (bvw->priv->last_error_message) { g_free (bvw->priv->last_error_message); bvw->priv->last_error_message = NULL; } gst_play_seek_to_time (bvw->priv->play, time * GST_MSECOND); return TRUE; } void bacon_video_widget_stop (BaconVideoWidget * bvw) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_READY); } void bacon_video_widget_close (BaconVideoWidget * bvw) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_READY); if (bvw->priv->mrl) { gst_play_set_location (bvw->priv->play, "/dev/null"); g_free (bvw->priv->mrl); bvw->priv->mrl = NULL; } } void bacon_video_widget_dvd_event (BaconVideoWidget * bvw, BaconVideoWidgetDVDEvent type) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); } void bacon_video_widget_set_logo (BaconVideoWidget * bvw, gchar * filename) { GError *error = NULL; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw)); bvw->priv->logo_pixbuf = gdk_pixbuf_new_from_file (filename, &error); if (error) { g_warning ("An error occured trying to open logo %s: %s", filename, error->message); g_error_free (error); } else { gst_video_widget_set_logo (bvw->priv->vw, bvw->priv->logo_pixbuf); } } void bacon_video_widget_set_logo_mode (BaconVideoWidget * bvw, gboolean logo_mode) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw)); gst_video_widget_set_logo_focus (bvw->priv->vw, TRUE); } gboolean bacon_video_widget_get_logo_mode (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw), FALSE); return gst_video_widget_get_logo_focus (bvw->priv->vw); } void bacon_video_widget_pause (BaconVideoWidget * bvw) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_PAUSED); } void bacon_video_widget_set_proprietary_plugins_path (BaconVideoWidget * bvw, const char *path) { } gboolean bacon_video_widget_can_set_volume (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); if (GST_IS_MIXER_TRACK (bvw->priv->mixer_track)) return TRUE; else return FALSE; } void bacon_video_widget_set_volume (BaconVideoWidget * bvw, int volume) { gint *volumes; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); if (GST_IS_MIXER_TRACK (bvw->priv->mixer_track)) { gint i = 0; GstMixerTrack *mixer_track = bvw->priv->mixer_track; volumes = g_malloc (sizeof (gint) * mixer_track->num_channels); /* We set the volume for each one of the channel respecting the min/max volume of the mixer track */ for (i=0; inum_channels; i++) volumes[i] = ((mixer_track->max_volume - mixer_track->min_volume) * volume / 100 ) + mixer_track->min_volume; gst_mixer_set_volume (bvw->priv->mixer, bvw->priv->mixer_track, volumes); g_free (volumes); } } int bacon_video_widget_get_volume (BaconVideoWidget * bvw) { gint *volumes, volume = 0; g_return_val_if_fail (bvw != NULL, -1); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), -1); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), -1); if (GST_IS_MIXER_TRACK (bvw->priv->mixer_track)) { GstMixerTrack *mixer_track = bvw->priv->mixer_track; volumes = g_malloc (sizeof (gint) * mixer_track->num_channels); gst_mixer_get_volume (bvw->priv->mixer, bvw->priv->mixer_track, volumes); volume = (volumes [0] - mixer_track->min_volume) * 100 / (mixer_track->max_volume - mixer_track->min_volume); g_free (volumes); } return volume; } gboolean bacon_video_widget_fullscreen_mode_available (BaconVideoWidget *bvw, TvOutType tvout) { switch(tvout) { case TV_OUT_NONE: /* Asume that ordinary fullscreen always works */ return TRUE; case TV_OUT_NVTV_NTSC: case TV_OUT_NVTV_PAL: return FALSE; case TV_OUT_DXR3: /* FIXME: Add DXR3 detection code */ return FALSE; } return FALSE; } void bacon_video_widget_set_fullscreen (BaconVideoWidget * bvw, gboolean fullscreen) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); if (bvw->priv->vw) { gst_video_widget_set_scale_override (GST_VIDEO_WIDGET (bvw->priv->vw), FALSE); } } void bacon_video_widget_set_show_cursor (BaconVideoWidget * bvw, gboolean use_cursor) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw)); gst_video_widget_set_cursor_visible (bvw->priv->vw, use_cursor); } gboolean bacon_video_widget_get_show_cursor (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw), FALSE); return gst_video_widget_get_cursor_visible (bvw->priv->vw); } void bacon_video_widget_set_media_device (BaconVideoWidget * bvw, const char *path) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); if (bvw->priv->media_device) g_free (bvw->priv->media_device); bvw->priv->media_device = g_strdup (path); } gboolean bacon_video_widget_set_show_visuals (BaconVideoWidget * bvw, gboolean show_visuals) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); if (!bvw->priv->media_has_video) { if (!show_visuals) gst_video_widget_set_logo_focus (bvw->priv->vw, TRUE); else gst_video_widget_set_logo_focus (bvw->priv->vw, FALSE); gst_play_connect_visualization (bvw->priv->play, show_visuals); } return TRUE; } GList * bacon_video_widget_get_visuals_list (BaconVideoWidget * bvw) { GList *pool_registries = NULL, *registries = NULL, *vis_plugins_list = NULL; GList *plugins = NULL, *features = NULL; g_return_val_if_fail (bvw != NULL, NULL); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), NULL); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), NULL); /* Cache */ if (bvw->priv->vis_plugins_list) { return bvw->priv->vis_plugins_list; } pool_registries = gst_registry_pool_list (); registries = pool_registries; while (registries) { GstRegistry *registry = GST_REGISTRY (registries->data); plugins = registry->plugins; while (plugins) { GstPlugin *plugin = GST_PLUGIN (plugins->data); features = gst_plugin_get_feature_list (plugin); while (features) { GstPluginFeature *feature = GST_PLUGIN_FEATURE (features->data); if (GST_IS_ELEMENT_FACTORY (feature)) { GstElementFactory *factory = GST_ELEMENT_FACTORY (feature); if (g_strrstr (factory->details.klass, "Visualization")) { vis_plugins_list = g_list_append (vis_plugins_list, (char *) g_strdup (GST_OBJECT_NAME (factory))); } } features = g_list_next (features); } plugins = g_list_next (plugins); } registries = g_list_next (registries); } g_list_free (pool_registries); pool_registries = NULL; bvw->priv->vis_plugins_list = vis_plugins_list; return vis_plugins_list; } gboolean bacon_video_widget_set_visuals (BaconVideoWidget * bvw, const char *name) { gboolean paused = FALSE; g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); if (GST_STATE (GST_ELEMENT (bvw->priv->play)) == GST_STATE_PLAYING) { gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_PAUSED); paused = TRUE; } bvw->priv->vis_element = gst_element_factory_make (name, "vis_plugin_element"); if (GST_IS_ELEMENT (bvw->priv->vis_element)) { gst_play_set_visualization (bvw->priv->play, bvw->priv->vis_element); if (paused) { gst_play_seek_to_time (bvw->priv->play, bvw->priv->current_time_nanos); gst_element_set_state (GST_ELEMENT (bvw->priv->play), GST_STATE_PLAYING); } return FALSE; } else { return FALSE; } } void bacon_video_widget_set_visuals_quality (BaconVideoWidget * bvw, VisualsQuality quality) { int fps, w, h; g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); if (bvw->priv->vis_element == NULL) return; switch (quality) { case VISUAL_SMALL: fps = 15; w = 320; h = 240; break; case VISUAL_NORMAL: fps = 25; w = 320; h = 240; break; case VISUAL_LARGE: fps = 25; w = 640; h = 480; break; case VISUAL_EXTRA_LARGE: fps = 30; w = 800; h = 600; break; default: /* shut up warnings */ fps = w = h = 0; g_assert_not_reached (); } } gboolean bacon_video_widget_get_auto_resize (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); g_return_val_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw), FALSE); return gst_video_widget_get_auto_resize (bvw->priv->vw); } void bacon_video_widget_set_auto_resize (BaconVideoWidget * bvw, gboolean auto_resize) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); g_return_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw)); gst_video_widget_set_auto_resize (bvw->priv->vw, auto_resize); } void bacon_video_widget_set_aspect_ratio (BaconVideoWidget *bvw, BaconVideoWidgetAspectRatio ratio) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); } BaconVideoWidgetAspectRatio bacon_video_widget_get_aspect_ratio (BaconVideoWidget *bvw) { g_return_val_if_fail (bvw != NULL, 0); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), 0); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), 0); return BVW_RATIO_AUTO; } void bacon_video_widget_set_scale_ratio (BaconVideoWidget * bvw, gfloat ratio) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); g_return_if_fail (GST_IS_VIDEO_WIDGET (bvw->priv->vw)); gst_video_widget_set_scale (bvw->priv->vw, ratio); gst_video_widget_set_scale_override (bvw->priv->vw, TRUE); shrink_toplevel (bvw); } int bacon_video_widget_get_video_property (BaconVideoWidget *bvw, BaconVideoWidgetVideoProperty type) { int value = 65535 / 2; g_return_val_if_fail (bvw != NULL, value); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), value); if (GST_IS_COLOR_BALANCE (bvw->priv->balance)) { const GList *channels_list = NULL; GstColorBalanceChannel *found_channel = NULL; channels_list = gst_color_balance_list_channels (bvw->priv->balance); while (channels_list) { /* We search for the right channel corresponding to type */ GstColorBalanceChannel *channel = channels_list->data; if (type == BVW_VIDEO_BRIGHTNESS && channel && g_strrstr (channel->label, "BRIGHTNESS")) { g_object_ref (channel); found_channel = channel; } else if (type == BVW_VIDEO_CONTRAST && channel && g_strrstr (channel->label, "CONTRAST")) { g_object_ref (channel); found_channel = channel; } else if (type == BVW_VIDEO_SATURATION && channel && g_strrstr (channel->label, "SATURATION")) { g_object_ref (channel); found_channel = channel; } else if (type == BVW_VIDEO_HUE && channel && g_strrstr (channel->label, "HUE")) { g_object_ref (channel); found_channel = channel; } channels_list = g_list_next (channels_list); } if (GST_IS_COLOR_BALANCE_CHANNEL (found_channel)) { value = gst_color_balance_get_value (bvw->priv->balance, found_channel); value = ((double) value - found_channel->min_value) * 65535 / ((double) found_channel->max_value - found_channel->min_value); g_object_unref (found_channel); } } return value; } void bacon_video_widget_set_video_property (BaconVideoWidget *bvw, BaconVideoWidgetVideoProperty type, int value) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); if ( !(value < 65535 && value > 0) ) return; if (GST_IS_COLOR_BALANCE (bvw->priv->balance)) { const GList *channels_list = NULL; GstColorBalanceChannel *found_channel = NULL; channels_list = gst_color_balance_list_channels (bvw->priv->balance); while (channels_list) { /* We search for the right channel corresponding to type */ GstColorBalanceChannel *channel = channels_list->data; if (type == BVW_VIDEO_BRIGHTNESS && channel && g_strrstr (channel->label, "BRIGHTNESS")) { g_object_ref (channel); found_channel = channel; } else if (type == BVW_VIDEO_CONTRAST && channel && g_strrstr (channel->label, "CONTRAST")) { g_object_ref (channel); found_channel = channel; } else if (type == BVW_VIDEO_SATURATION && channel && g_strrstr (channel->label, "SATURATION")) { g_object_ref (channel); found_channel = channel; } else if (type == BVW_VIDEO_HUE && channel && g_strrstr (channel->label, "HUE")) { g_object_ref (channel); found_channel = channel; } channels_list = g_list_next (channels_list); } if (GST_IS_COLOR_BALANCE_CHANNEL (found_channel)) { value = value * ((double) found_channel->max_value - found_channel->min_value) / 65535 + found_channel->min_value; gst_color_balance_set_value (bvw->priv->balance, found_channel, value); g_object_unref (found_channel); } } } float bacon_video_widget_get_position (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, -1); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), -1); return bvw->priv->current_position; } gint64 bacon_video_widget_get_current_time (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, -1); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), -1); return bvw->priv->current_time; } gint64 bacon_video_widget_get_stream_length (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, -1); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), -1); return bvw->priv->stream_length; } gboolean bacon_video_widget_is_playing (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); if (GST_STATE (GST_ELEMENT (bvw->priv->play)) == GST_STATE_PLAYING) return TRUE; return FALSE; } gboolean bacon_video_widget_is_seekable (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); if (bvw->priv->stream_length) return TRUE; else return FALSE; } gboolean bacon_video_widget_can_play (BaconVideoWidget * bvw, MediaType type) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); switch (type) { case MEDIA_DVD: return TRUE; case MEDIA_VCD: return FALSE; case MEDIA_CDDA: return TRUE; default: return FALSE; } } G_CONST_RETURN gchar ** bacon_video_widget_get_mrls (BaconVideoWidget * bvw, MediaType type) { const gchar **mrls; g_return_val_if_fail (bvw != NULL, NULL); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), NULL); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), NULL); switch (type) { case MEDIA_DVD: { mrls = g_malloc0 (sizeof (char *) * 2); mrls[0] = "dvd://"; return mrls; } case MEDIA_VCD: return NULL; case MEDIA_CDDA: { mrls = g_malloc0 (sizeof (char *) * 2); mrls[0] = "cdda://"; return mrls; } default: return NULL; } } static void bacon_video_widget_get_metadata_string (BaconVideoWidget * bvw, BaconVideoWidgetMetadataType type, GValue * value) { const char *string = NULL; g_value_init (value, G_TYPE_STRING); if (bvw->priv->play == NULL) { g_value_set_string (value, ""); return; } switch (type) { case BVW_INFO_TITLE: string = g_hash_table_lookup (bvw->priv->metadata_hash, "title"); break; case BVW_INFO_ARTIST: string = g_hash_table_lookup (bvw->priv->metadata_hash, "artist"); break; case BVW_INFO_YEAR: string = g_hash_table_lookup (bvw->priv->metadata_hash, "year"); break; case BVW_INFO_VIDEO_CODEC: string = g_hash_table_lookup (bvw->priv->metadata_hash, "video-codec"); break; case BVW_INFO_AUDIO_CODEC: string = g_hash_table_lookup (bvw->priv->metadata_hash, "audio-codec"); break; case BVW_INFO_CDINDEX: string = NULL; break; default: g_assert_not_reached (); } g_value_set_string (value, string); return; } static void bacon_video_widget_get_metadata_int (BaconVideoWidget * bvw, BaconVideoWidgetMetadataType type, GValue * value) { int integer = 0; g_value_init (value, G_TYPE_INT); if (bvw->priv->play == NULL) { g_value_set_int (value, 0); return; } switch (type) { case BVW_INFO_DURATION: integer = bacon_video_widget_get_stream_length (bvw) / 1000; break; case BVW_INFO_DIMENSION_X: integer = bvw->priv->video_width; break; case BVW_INFO_DIMENSION_Y: integer = bvw->priv->video_height; break; case BVW_INFO_FPS: integer = 0; break; case BVW_INFO_BITRATE: integer = 0; break; default: g_assert_not_reached (); } g_value_set_int (value, integer); return; } static void bacon_video_widget_get_metadata_bool (BaconVideoWidget * bvw, BaconVideoWidgetMetadataType type, GValue * value) { gboolean boolean = FALSE; g_value_init (value, G_TYPE_BOOLEAN); if (bvw->priv->play == NULL) { g_value_set_boolean (value, FALSE); return; } switch (type) { case BVW_INFO_HAS_VIDEO: boolean = bvw->priv->media_has_video; break; case BVW_INFO_HAS_AUDIO: boolean = TRUE; break; default: g_assert_not_reached (); } g_value_set_boolean (value, boolean); return; } void bacon_video_widget_get_metadata (BaconVideoWidget * bvw, BaconVideoWidgetMetadataType type, GValue * value) { g_return_if_fail (bvw != NULL); g_return_if_fail (BACON_IS_VIDEO_WIDGET (bvw)); g_return_if_fail (GST_IS_PLAY (bvw->priv->play)); switch (type) { case BVW_INFO_TITLE: case BVW_INFO_ARTIST: case BVW_INFO_YEAR: case BVW_INFO_VIDEO_CODEC: case BVW_INFO_AUDIO_CODEC: bacon_video_widget_get_metadata_string (bvw, type, value); break; case BVW_INFO_DURATION: case BVW_INFO_DIMENSION_X: case BVW_INFO_DIMENSION_Y: case BVW_INFO_FPS: case BVW_INFO_BITRATE: bacon_video_widget_get_metadata_int (bvw, type, value); break; case BVW_INFO_HAS_VIDEO: case BVW_INFO_HAS_AUDIO: bacon_video_widget_get_metadata_bool (bvw, type, value); break; default: g_assert_not_reached (); } return; } /* Screenshot functions */ gboolean bacon_video_widget_can_get_frames (BaconVideoWidget * bvw, GError ** error) { g_return_val_if_fail (bvw != NULL, FALSE); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), FALSE); return FALSE; } GdkPixbuf * bacon_video_widget_get_current_frame (BaconVideoWidget * bvw) { g_return_val_if_fail (bvw != NULL, NULL); g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), NULL); g_return_val_if_fail (GST_IS_PLAY (bvw->priv->play), NULL); return NULL; } /* =========================================== */ /* */ /* Widget typing & Creation */ /* */ /* =========================================== */ G_DEFINE_TYPE(BaconVideoWidget, bacon_video_widget, GTK_TYPE_BOX) struct poptOption * bacon_video_widget_get_popt_table (void) { /* Initializing GStreamer backend and parse our options from the command line options */ return (struct poptOption *) gst_init_get_popt_table (); } GQuark bacon_video_widget_error_quark (void) { static GQuark q = 0; if (q == 0) { q = g_quark_from_static_string ("bvw-error-quark"); } return q; } GtkWidget * bacon_video_widget_new (int width, int height, gboolean null_out, GError ** err) { BaconVideoWidget *bvw; GstElement *audio_sink = NULL, *video_sink = NULL; bvw = BACON_VIDEO_WIDGET (g_object_new (bacon_video_widget_get_type (), NULL)); bvw->priv->play = gst_play_new (err); if (*err != NULL) { return NULL; } bvw->priv->media_device = g_strdup ("/dev/dvd"); bvw->priv->metadata_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); bvw->priv->init_width = bvw->priv->init_height = 0; //FIXME if (*err != NULL) { g_message ("error: %s", (*err)->message); return NULL; } audio_sink = gst_gconf_get_default_audio_sink (); if (!GST_IS_ELEMENT (audio_sink)) { g_message ("failed to render default audio sink from gconf"); return NULL; } video_sink = gst_gconf_get_default_video_sink (); if (!GST_IS_ELEMENT (video_sink)) { g_message ("failed to render default video sink from gconf"); return NULL; } gst_play_set_video_sink (bvw->priv->play, video_sink); gst_play_set_audio_sink (bvw->priv->play, audio_sink); bvw->priv->vis_plugins_list = NULL; g_signal_connect (G_OBJECT (bvw->priv->play), "have_video_size", (GtkSignalFunc) got_video_size, (gpointer) bvw); g_signal_connect (G_OBJECT (bvw->priv->play), "eos", (GtkSignalFunc) got_eos, (gpointer) bvw); g_signal_connect (G_OBJECT (bvw->priv->play), "stream_length", (GtkSignalFunc) got_stream_length, (gpointer) bvw); g_signal_connect (G_OBJECT (bvw->priv->play), "found_tag", (GtkSignalFunc) got_found_tag, (gpointer) bvw); g_signal_connect (G_OBJECT (bvw->priv->play), "time_tick", (GtkSignalFunc) got_time_tick, (gpointer) bvw); g_signal_connect (G_OBJECT (bvw->priv->play), "error", (GtkSignalFunc) got_error, (gpointer) bvw); bvw->priv->vw = GST_VIDEO_WIDGET (gst_video_widget_new ()); if (!GST_IS_VIDEO_WIDGET (bvw->priv->vw)) { g_message ("failed to create video widget"); return NULL; } /* VideoWidget signals */ g_signal_connect (G_OBJECT (bvw->priv->vw), "motion-notify-event", G_CALLBACK (bacon_video_widget_motion_notify_callback), bvw); g_signal_connect (G_OBJECT(bvw->priv->vw), "button-press-event", G_CALLBACK (bacon_video_widget_button_press), bvw); g_signal_connect (G_OBJECT(bvw->priv->vw), "button-release-event", G_CALLBACK (bacon_video_widget_button_release), bvw); g_signal_connect_after (G_OBJECT (bvw->priv->vw), "size_allocate", G_CALLBACK (bacon_video_widget_vw_allocate), bvw); g_signal_connect_after (G_OBJECT (bvw->priv->vw), "realize", G_CALLBACK (bacon_video_widget_vw_realized), bvw); g_signal_connect_after (G_OBJECT (bvw->priv->vw), "expose-event", G_CALLBACK (bacon_video_widget_vw_exposed), bvw); gtk_box_pack_end (GTK_BOX (bvw), GTK_WIDGET (bvw->priv->vw), TRUE, TRUE, 0); gtk_widget_show (GTK_WIDGET (bvw->priv->vw)); /* We try to get an element supporting XOverlay interface */ if (GST_IS_BIN (bvw->priv->play)) { GstElement *element = NULL; GList *elements = NULL, *l_elements = NULL; element = gst_bin_get_by_interface (GST_BIN (bvw->priv->play), GST_TYPE_X_OVERLAY); if (GST_IS_X_OVERLAY (element)) bvw->priv->xoverlay = GST_X_OVERLAY (element); /* Get them all and prefer hardware one */ elements = gst_bin_get_all_by_interface (GST_BIN (bvw->priv->play), GST_TYPE_COLOR_BALANCE); l_elements = elements; /* We take the first one */ if (elements && GST_IS_COLOR_BALANCE (elements->data)) element = GST_ELEMENT (elements->data); while (elements) { GstElement *local_element = NULL; GstColorBalanceClass *cb_class = NULL; if (elements->data && GST_IS_COLOR_BALANCE (elements->data)) { local_element = GST_ELEMENT (elements->data); cb_class = GST_COLOR_BALANCE_GET_CLASS (elements->data); /* If one of them is hardware type we use that one then */ if (GST_COLOR_BALANCE_TYPE (cb_class) == GST_COLOR_BALANCE_HARDWARE) { element = local_element; } } elements = g_list_next (elements); } if (l_elements) g_list_free (l_elements); elements = l_elements = NULL; if (GST_IS_COLOR_BALANCE (element)) { bvw->priv->balance = GST_COLOR_BALANCE (element); } /* Get them all and prefer software one */ elements = gst_bin_get_all_by_interface (GST_BIN (bvw->priv->play), GST_TYPE_MIXER); l_elements = elements; /* We take the first one */ if (elements && GST_IS_MIXER (elements->data)) element = GST_ELEMENT (elements->data); while (elements) { GstElement *local_element = NULL; GstMixerClass *m_class = NULL; if (elements->data && GST_IS_MIXER (elements->data)) { local_element = GST_ELEMENT (elements->data); m_class = GST_MIXER_GET_CLASS (elements->data); /* If one of them is hardware type we use that one then */ if (GST_MIXER_TYPE (m_class) == GST_MIXER_SOFTWARE) { element = local_element; } } elements = g_list_next (elements); } if (l_elements) g_list_free (l_elements); elements = l_elements = NULL; if (GST_IS_MIXER (element)) { const GList *tracks; bvw->priv->mixer = GST_MIXER (element); tracks = gst_mixer_list_tracks (GST_MIXER (element)); if (tracks) bvw->priv->mixer_track = GST_MIXER_TRACK (tracks->data); } else g_warning ("can't find any mixer element, no volume."); } return GTK_WIDGET (bvw); }