]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-video-widget.c
Add audio/video sink and source abstractions
[empathy.git] / libempathy-gtk / empathy-video-widget.c
1 /*
2  * empathy-gst-gtk-widget.c - Source for EmpathyVideoWidget
3  * Copyright (C) 2008 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <gdk/gdkx.h>
26 #include <gst/interfaces/xoverlay.h>
27 #include <gst/farsight/fs-element-added-notifier.h>
28
29 #include "empathy-video-widget.h"
30
31 G_DEFINE_TYPE(EmpathyVideoWidget, empathy_video_widget,
32   GTK_TYPE_DRAWING_AREA)
33
34 static void empathy_video_widget_element_added_cb (
35   FsElementAddedNotifier *notifier, GstBin *bin, GstElement *element,
36   EmpathyVideoWidget *self);
37
38 static void empathy_video_widget_sync_message_cb (
39   GstBus *bus, GstMessage *message, EmpathyVideoWidget *self);
40
41 /* signal enum */
42 #if 0
43 enum
44 {
45     LAST_SIGNAL
46 };
47
48 static guint signals[LAST_SIGNAL] = {0};
49 #endif
50
51 enum {
52   PROP_GST_ELEMENT = 1,
53   PROP_GST_BUS,
54 };
55
56 /* private structure */
57 typedef struct _EmpathyVideoWidgetPriv EmpathyVideoWidgetPriv;
58
59 struct _EmpathyVideoWidgetPriv
60 {
61   gboolean dispose_has_run;
62   GstBus *bus;
63   GstElement *videosink;
64   GstPad *sink_pad;
65   GstElement *overlay;
66   FsElementAddedNotifier *notifier;
67 };
68
69 #define GET_PRIV(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
70   EMPATHY_TYPE_GST_GTK_WIDGET, EmpathyVideoWidgetPriv))
71
72 static void
73 empathy_video_widget_init (EmpathyVideoWidget *obj)
74 {
75   EmpathyVideoWidgetPriv *priv = GET_PRIV (obj);
76
77   priv->notifier = fs_element_added_notifier_new ();
78   g_signal_connect (priv->notifier, "element-added",
79     G_CALLBACK (empathy_video_widget_element_added_cb),
80     obj);
81
82   gtk_widget_set_size_request (GTK_WIDGET (obj), 320, 240);
83
84   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (obj), GTK_DOUBLE_BUFFERED);
85 }
86
87 static void
88 empathy_video_widget_constructed (GObject *object)
89 {
90   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
91
92   priv->videosink = gst_element_factory_make ("gconfvideosink", NULL);
93   priv->sink_pad = gst_element_get_static_pad (priv->videosink, "sink");
94
95   fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->videosink));
96   gst_bus_enable_sync_message_emission (priv->bus);
97
98   g_signal_connect (priv->bus, "sync-message",
99     G_CALLBACK (empathy_video_widget_sync_message_cb), object);
100
101 }
102
103 static void empathy_video_widget_dispose (GObject *object);
104 static void empathy_video_widget_finalize (GObject *object);
105 static gboolean empathy_video_widget_expose_event (GtkWidget *widget,
106   GdkEventExpose *event);
107
108 static void
109 empathy_video_widget_set_property (GObject *object,
110   guint property_id, const GValue *value, GParamSpec *pspec)
111 {
112   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
113
114   switch (property_id)
115     {
116       case PROP_GST_BUS:
117         priv->bus = g_value_dup_object (value);
118         break;
119       default:
120         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
121     }
122 }
123
124 static void
125 empathy_video_widget_get_property (GObject *object,
126   guint property_id, GValue *value, GParamSpec *pspec)
127 {
128   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
129
130   switch (property_id)
131     {
132       case PROP_GST_ELEMENT:
133         g_value_set_object (value, priv->videosink);
134         break;
135       case PROP_GST_BUS:
136         g_value_set_object (value, priv->bus);
137         break;
138       default:
139         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
140     }
141 }
142
143
144 static void
145 empathy_video_widget_class_init (
146   EmpathyVideoWidgetClass *empathy_video_widget_class)
147 {
148   GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_widget_class);
149   GtkWidgetClass *widget_class =
150     GTK_WIDGET_CLASS (empathy_video_widget_class);
151   GParamSpec *param_spec;
152
153   g_type_class_add_private (empathy_video_widget_class,
154     sizeof (EmpathyVideoWidgetPriv));
155
156   object_class->dispose = empathy_video_widget_dispose;
157   object_class->finalize = empathy_video_widget_finalize;
158   object_class->constructed = empathy_video_widget_constructed;
159
160   object_class->set_property = empathy_video_widget_set_property;
161   object_class->get_property = empathy_video_widget_get_property;
162
163   widget_class->expose_event = empathy_video_widget_expose_event;
164
165   param_spec = g_param_spec_object ("gst-element",
166     "gst-element", "The underlaying gstreamer element",
167     GST_TYPE_ELEMENT,
168     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
169   g_object_class_install_property (object_class, PROP_GST_ELEMENT, param_spec);
170
171   param_spec = g_param_spec_object ("gst-bus",
172     "gst-bus",
173     "The toplevel bus from the pipeline in which this bin will be added",
174     GST_TYPE_BUS,
175     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
176   g_object_class_install_property (object_class, PROP_GST_BUS, param_spec);
177 }
178
179 void
180 empathy_video_widget_dispose (GObject *object)
181 {
182   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object);
183   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
184
185   if (priv->dispose_has_run)
186     return;
187
188   priv->dispose_has_run = TRUE;
189
190   if (priv->bus != NULL)
191     g_object_unref (priv->bus);
192
193   priv->bus = NULL;
194
195   /* release any references held by the object here */
196
197   if (G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose)
198     G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose (object);
199 }
200
201 void
202 empathy_video_widget_finalize (GObject *object)
203 {
204   //EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object);
205   //EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
206
207   /* free any data held directly by the object here */
208
209   G_OBJECT_CLASS (empathy_video_widget_parent_class)->finalize (object);
210 }
211
212 static void
213 empathy_video_widget_element_added_cb (FsElementAddedNotifier *notifier,
214   GstBin *bin, GstElement *element, EmpathyVideoWidget *self)
215 {
216   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
217
218   if (priv->overlay == NULL && GST_IS_X_OVERLAY (element))
219     {
220       priv->overlay = element;
221       gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay));
222     }
223
224   if (g_object_class_find_property (
225       G_OBJECT_GET_CLASS (element), "force-aspect-ratio"))
226     {
227       g_object_set (G_OBJECT (element), "force-aspect-ratio", TRUE, NULL);
228     }
229 }
230
231 static void
232 empathy_video_widget_sync_message_cb (GstBus *bus, GstMessage *message,
233   EmpathyVideoWidget *self)
234 {
235   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
236   const GstStructure *s;
237
238   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
239     return;
240
241   if (GST_MESSAGE_SRC (message) != (GstObject *) priv->overlay)
242     return;
243
244   s = gst_message_get_structure (message);
245
246   if (gst_structure_has_name (s, "prepare-xwindow-id"))
247     {
248       gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay),
249         GDK_WINDOW_XID (GTK_WIDGET (self)->window));
250     }
251 }
252
253 static gboolean
254 empathy_video_widget_expose_event (GtkWidget *widget, GdkEventExpose *event)
255 {
256   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (widget);
257   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
258
259   if (event != NULL && event->count > 0)
260     return TRUE;
261
262   if (priv->overlay == NULL)
263     return TRUE;
264
265   gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay),
266     GDK_WINDOW_XID (widget->window));
267
268   gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay));
269
270   return TRUE;
271 }
272
273
274 GtkWidget *
275 empathy_video_widget_new (GstBus *bus)
276 {
277   g_return_val_if_fail (bus != NULL, NULL);
278
279   return GTK_WIDGET (g_object_new (EMPATHY_TYPE_GST_GTK_WIDGET,
280     "gst-bus", bus,
281     NULL));
282 }
283
284 GstPad *
285 empathy_video_widget_get_sink (EmpathyVideoWidget *widget)
286 {
287   EmpathyVideoWidgetPriv *priv = GET_PRIV (widget);
288
289   return priv->sink_pad;
290 }
291
292 GstElement *
293 empathy_video_widget_get_element (EmpathyVideoWidget *widget)
294 {
295   EmpathyVideoWidgetPriv *priv = GET_PRIV (widget);
296
297   return priv->videosink;
298 }