]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-video-widget.c
Keep a weak pointer on the overlay in case it goes away
[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   PROP_MIN_WIDTH,
55   PROP_MIN_HEIGHT,
56   PROP_SYNC,
57   PROP_ASYNC,
58 };
59
60 /* private structure */
61 typedef struct _EmpathyVideoWidgetPriv EmpathyVideoWidgetPriv;
62
63 struct _EmpathyVideoWidgetPriv
64 {
65   gboolean dispose_has_run;
66   GstBus *bus;
67   GstElement *videosink;
68   GstPad *sink_pad;
69   GstElement *overlay;
70   FsElementAddedNotifier *notifier;
71   gint min_width;
72   gint min_height;
73   gboolean sync;
74   gboolean async;
75
76   GMutex *lock;
77 };
78
79 #define GET_PRIV(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
80   EMPATHY_TYPE_VIDEO_WIDGET, EmpathyVideoWidgetPriv))
81
82 static void
83 empathy_video_widget_init (EmpathyVideoWidget *obj)
84 {
85   EmpathyVideoWidgetPriv *priv = GET_PRIV (obj);
86   GdkColor black;
87
88   priv->lock = g_mutex_new ();
89
90   priv->notifier = fs_element_added_notifier_new ();
91   g_signal_connect (priv->notifier, "element-added",
92     G_CALLBACK (empathy_video_widget_element_added_cb),
93     obj);
94
95   if (gdk_color_parse ("Black", &black))
96     gtk_widget_modify_bg (GTK_WIDGET (obj), GTK_STATE_NORMAL,
97       &black);
98
99   GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (obj), GTK_DOUBLE_BUFFERED);
100 }
101
102 static void
103 empathy_video_widget_constructed (GObject *object)
104 {
105   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
106
107   priv->videosink = gst_element_factory_make ("gconfvideosink", NULL);
108   gst_object_ref (priv->videosink);
109   gst_object_sink (priv->videosink);
110
111   priv->sink_pad = gst_element_get_static_pad (priv->videosink, "sink");
112
113   fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->videosink));
114   gst_bus_enable_sync_message_emission (priv->bus);
115
116   g_signal_connect (priv->bus, "sync-message",
117     G_CALLBACK (empathy_video_widget_sync_message_cb), object);
118
119   gtk_widget_set_size_request (GTK_WIDGET (object), priv->min_width,
120     priv->min_height);
121 }
122
123 static void empathy_video_widget_dispose (GObject *object);
124 static void empathy_video_widget_finalize (GObject *object);
125
126 static gboolean empathy_video_widget_expose_event (GtkWidget *widget,
127   GdkEventExpose *event);
128 static void
129 empathy_video_widget_element_set_sink_properties (EmpathyVideoWidget *self);
130
131 static void
132 empathy_video_widget_set_property (GObject *object,
133   guint property_id, const GValue *value, GParamSpec *pspec)
134 {
135   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
136
137   switch (property_id)
138     {
139       case PROP_GST_BUS:
140         priv->bus = g_value_dup_object (value);
141         break;
142       case PROP_MIN_WIDTH:
143         priv->min_width = g_value_get_int (value);
144         break;
145       case PROP_MIN_HEIGHT:
146         priv->min_height = g_value_get_int (value);
147         break;
148       case PROP_SYNC:
149         priv->sync = g_value_get_boolean (value);
150         empathy_video_widget_element_set_sink_properties (
151           EMPATHY_VIDEO_WIDGET (object));
152         break;
153       case PROP_ASYNC:
154         priv->async = g_value_get_boolean (value);
155         empathy_video_widget_element_set_sink_properties (
156           EMPATHY_VIDEO_WIDGET (object));
157         break;
158       default:
159         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
160     }
161 }
162
163 static void
164 empathy_video_widget_get_property (GObject *object,
165   guint property_id, GValue *value, GParamSpec *pspec)
166 {
167   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
168
169   switch (property_id)
170     {
171       case PROP_GST_ELEMENT:
172         g_value_set_object (value, priv->videosink);
173         break;
174       case PROP_GST_BUS:
175         g_value_set_object (value, priv->bus);
176         break;
177       case PROP_MIN_WIDTH:
178         g_value_set_int (value, priv->min_width);
179         break;
180       case PROP_MIN_HEIGHT:
181         g_value_set_int (value, priv->min_height);
182         break;
183       case PROP_SYNC:
184         g_value_set_boolean (value, priv->sync);
185         break;
186       case PROP_ASYNC:
187         g_value_set_boolean (value, priv->async);
188         break;
189       default:
190         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
191     }
192 }
193
194
195 static void
196 empathy_video_widget_class_init (
197   EmpathyVideoWidgetClass *empathy_video_widget_class)
198 {
199   GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_widget_class);
200   GtkWidgetClass *widget_class =
201     GTK_WIDGET_CLASS (empathy_video_widget_class);
202   GParamSpec *param_spec;
203
204   g_type_class_add_private (empathy_video_widget_class,
205     sizeof (EmpathyVideoWidgetPriv));
206
207   object_class->dispose = empathy_video_widget_dispose;
208   object_class->finalize = empathy_video_widget_finalize;
209   object_class->constructed = empathy_video_widget_constructed;
210
211   object_class->set_property = empathy_video_widget_set_property;
212   object_class->get_property = empathy_video_widget_get_property;
213
214   widget_class->expose_event = empathy_video_widget_expose_event;
215
216   param_spec = g_param_spec_object ("gst-element",
217     "gst-element", "The underlaying gstreamer element",
218     GST_TYPE_ELEMENT,
219     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
220   g_object_class_install_property (object_class, PROP_GST_ELEMENT, param_spec);
221
222   param_spec = g_param_spec_object ("gst-bus",
223     "gst-bus",
224     "The toplevel bus from the pipeline in which this bin will be added",
225     GST_TYPE_BUS,
226     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
227   g_object_class_install_property (object_class, PROP_GST_BUS, param_spec);
228
229   param_spec = g_param_spec_int ("min-width",
230     "min-width",
231     "Minimal width of the widget",
232     0, G_MAXINT, 320,
233     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
234   g_object_class_install_property (object_class, PROP_MIN_WIDTH, param_spec);
235
236   param_spec = g_param_spec_int ("min-height",
237     "min-height",
238     "Minimal height of the widget",
239     0, G_MAXINT, 240,
240     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
241   g_object_class_install_property (object_class, PROP_MIN_HEIGHT, param_spec);
242
243   param_spec = g_param_spec_boolean ("sync",
244     "sync",
245     "Whether the underlying sink should be sync or not",
246     TRUE,
247     G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
248   g_object_class_install_property (object_class, PROP_SYNC, param_spec);
249
250   param_spec = g_param_spec_boolean ("async",
251     "async",
252     "Whether the underlying sink should be async or not",
253     TRUE,
254     G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
255   g_object_class_install_property (object_class, PROP_ASYNC, param_spec);
256 }
257
258 void
259 empathy_video_widget_dispose (GObject *object)
260 {
261   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object);
262   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
263
264   if (priv->dispose_has_run)
265     return;
266
267   priv->dispose_has_run = TRUE;
268
269   if (priv->bus != NULL)
270     g_object_unref (priv->bus);
271
272   priv->bus = NULL;
273
274   if (priv->videosink != NULL)
275     g_object_unref (priv->videosink);
276
277   priv->videosink = NULL;
278
279
280   /* release any references held by the object here */
281
282   if (G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose)
283     G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose (object);
284 }
285
286 void
287 empathy_video_widget_finalize (GObject *object)
288 {
289   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object);
290   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
291
292   /* free any data held directly by the object here */
293   g_mutex_free (priv->lock);
294
295   G_OBJECT_CLASS (empathy_video_widget_parent_class)->finalize (object);
296 }
297
298
299 static void
300 empathy_video_widget_element_set_sink_properties_unlocked (
301   EmpathyVideoWidget *self)
302 {
303   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
304
305   if (priv->overlay == NULL)
306     return;
307
308   if (g_object_class_find_property (G_OBJECT_GET_CLASS (priv->overlay),
309       "force-aspect-ratio"))
310     g_object_set (G_OBJECT (priv->overlay), "force-aspect-ratio", TRUE, NULL);
311
312   if (g_object_class_find_property (
313       G_OBJECT_GET_CLASS (priv->overlay), "sync"))
314     g_object_set (G_OBJECT (priv->overlay), "sync", priv->sync, NULL);
315
316   if (g_object_class_find_property (G_OBJECT_GET_CLASS (priv->overlay),
317       "async"))
318     g_object_set (G_OBJECT (priv->overlay), "async", priv->async, NULL);
319 }
320
321 static void
322 empathy_video_widget_element_set_sink_properties (EmpathyVideoWidget *self)
323 {
324   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
325
326   g_mutex_lock (priv->lock);
327   empathy_video_widget_element_set_sink_properties_unlocked (self);
328   g_mutex_unlock (priv->lock);
329 }
330
331 static void
332 empathy_video_widget_element_added_cb (FsElementAddedNotifier *notifier,
333   GstBin *bin, GstElement *element, EmpathyVideoWidget *self)
334 {
335   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
336
337   /* We assume the overlay is the sink */
338   g_mutex_lock (priv->lock);
339   if (priv->overlay == NULL && GST_IS_X_OVERLAY (element))
340     {
341       priv->overlay = element;
342       g_object_add_weak_pointer (G_OBJECT (element),
343         (gpointer) &priv->overlay);
344       empathy_video_widget_element_set_sink_properties_unlocked (self);
345       gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay));
346     }
347   g_mutex_unlock (priv->lock);
348 }
349
350 static void
351 empathy_video_widget_sync_message_cb (GstBus *bus, GstMessage *message,
352   EmpathyVideoWidget *self)
353 {
354   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
355   const GstStructure *s;
356
357   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
358     return;
359
360   if (GST_MESSAGE_SRC (message) != (GstObject *) priv->overlay)
361     return;
362
363   s = gst_message_get_structure (message);
364
365   if (gst_structure_has_name (s, "prepare-xwindow-id"))
366     {
367       gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay),
368         GDK_WINDOW_XID (GTK_WIDGET (self)->window));
369     }
370 }
371
372 static gboolean
373 empathy_video_widget_expose_event (GtkWidget *widget, GdkEventExpose *event)
374 {
375   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (widget);
376   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
377
378   if (event != NULL && event->count > 0)
379     return TRUE;
380
381   if (priv->overlay == NULL)
382     return TRUE;
383
384   gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay),
385     GDK_WINDOW_XID (widget->window));
386
387   gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay));
388
389   return TRUE;
390 }
391
392 GtkWidget *
393 empathy_video_widget_new_with_size (GstBus *bus, gint width, gint height)
394 {
395   g_return_val_if_fail (bus != NULL, NULL);
396
397   return GTK_WIDGET (g_object_new (EMPATHY_TYPE_VIDEO_WIDGET,
398     "gst-bus", bus,
399     "min-width", width,
400     "min-height", height,
401     NULL));
402 }
403
404 GtkWidget *
405 empathy_video_widget_new (GstBus *bus)
406 {
407   g_return_val_if_fail (bus != NULL, NULL);
408
409   return GTK_WIDGET (g_object_new (EMPATHY_TYPE_VIDEO_WIDGET,
410     "gst-bus", bus,
411     NULL));
412 }
413
414 GstPad *
415 empathy_video_widget_get_sink (EmpathyVideoWidget *widget)
416 {
417   EmpathyVideoWidgetPriv *priv = GET_PRIV (widget);
418
419   return priv->sink_pad;
420 }
421
422 GstElement *
423 empathy_video_widget_get_element (EmpathyVideoWidget *widget)
424 {
425   EmpathyVideoWidgetPriv *priv = GET_PRIV (widget);
426
427   return priv->videosink;
428 }