]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-video-widget.c
Fiexd wrong title capitalization for checkbox
[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   GdkRGBA 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_rgba_parse (&black, "Black"))
96     gtk_widget_override_background_color (GTK_WIDGET (obj), 0, &black);
97
98   gtk_widget_set_double_buffered (GTK_WIDGET (obj), FALSE);
99 }
100
101 static void
102 empathy_video_widget_realized (GtkWidget *widget, gpointer user_data)
103 {
104   /* requesting the XID forces the GdkWindow to be native in GTK+ 2.18
105    * onwards, requesting the native window in a thread causes a BadWindowID,
106    * so we need to request it now. We could call gdk_window_ensure_native(),
107    * but that would mean we require GTK+ 2.18, so instead we call this */
108   GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (widget)));
109 }
110
111 static void
112 empathy_video_widget_constructed (GObject *object)
113 {
114   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
115   GstElement *colorspace, *videoscale, *sink;
116   GstPad *pad;
117
118   g_signal_connect (object, "realize",
119       G_CALLBACK (empathy_video_widget_realized), NULL);
120
121   priv->videosink = gst_bin_new (NULL);
122
123   gst_object_ref (priv->videosink);
124   gst_object_sink (priv->videosink);
125
126   priv->sink_pad = gst_element_get_static_pad (priv->videosink, "sink");
127
128   sink = gst_element_factory_make ("gconfvideosink", NULL);
129   g_assert (sink != NULL);
130
131   videoscale = gst_element_factory_make ("videoscale", NULL);
132   g_assert (videoscale != NULL);
133
134   g_object_set (videoscale, "qos", FALSE, NULL);
135
136   colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL);
137   g_assert (colorspace != NULL);
138
139   g_object_set (colorspace, "qos", FALSE, NULL);
140
141   gst_bin_add_many (GST_BIN (priv->videosink), colorspace, videoscale,
142     sink, NULL);
143
144   if (!gst_element_link (colorspace, videoscale))
145     g_error ("Failed to link ffmpegcolorspace and videoscale");
146
147   if (!gst_element_link (videoscale, sink))
148     g_error ("Failed to link videoscale and gconfvideosink");
149
150   pad = gst_element_get_static_pad (colorspace, "sink");
151   g_assert (pad != NULL);
152
153   priv->sink_pad = gst_ghost_pad_new ("sink", pad);
154   if (!gst_element_add_pad  (priv->videosink, priv->sink_pad))
155     g_error ("Couldn't add sink ghostpad to the bin");
156
157   gst_object_unref (pad);
158
159   fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->videosink));
160   gst_bus_enable_sync_message_emission (priv->bus);
161
162   g_signal_connect (priv->bus, "sync-message",
163     G_CALLBACK (empathy_video_widget_sync_message_cb), object);
164
165   gtk_widget_set_size_request (GTK_WIDGET (object), priv->min_width,
166     priv->min_height);
167 }
168
169 static void empathy_video_widget_dispose (GObject *object);
170 static void empathy_video_widget_finalize (GObject *object);
171
172
173 static gboolean empathy_video_widget_draw (GtkWidget *widget,
174   cairo_t *cr);
175
176 static void
177 empathy_video_widget_element_set_sink_properties (EmpathyVideoWidget *self);
178
179 static void
180 empathy_video_widget_set_property (GObject *object,
181   guint property_id, const GValue *value, GParamSpec *pspec)
182 {
183   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
184
185   switch (property_id)
186     {
187       case PROP_GST_BUS:
188         priv->bus = g_value_dup_object (value);
189         break;
190       case PROP_MIN_WIDTH:
191         priv->min_width = g_value_get_int (value);
192         break;
193       case PROP_MIN_HEIGHT:
194         priv->min_height = g_value_get_int (value);
195         break;
196       case PROP_SYNC:
197         priv->sync = g_value_get_boolean (value);
198         empathy_video_widget_element_set_sink_properties (
199           EMPATHY_VIDEO_WIDGET (object));
200         break;
201       case PROP_ASYNC:
202         priv->async = g_value_get_boolean (value);
203         empathy_video_widget_element_set_sink_properties (
204           EMPATHY_VIDEO_WIDGET (object));
205         break;
206       default:
207         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
208     }
209 }
210
211 static void
212 empathy_video_widget_get_property (GObject *object,
213   guint property_id, GValue *value, GParamSpec *pspec)
214 {
215   EmpathyVideoWidgetPriv *priv = GET_PRIV (object);
216
217   switch (property_id)
218     {
219       case PROP_GST_ELEMENT:
220         g_value_set_object (value, priv->videosink);
221         break;
222       case PROP_GST_BUS:
223         g_value_set_object (value, priv->bus);
224         break;
225       case PROP_MIN_WIDTH:
226         g_value_set_int (value, priv->min_width);
227         break;
228       case PROP_MIN_HEIGHT:
229         g_value_set_int (value, priv->min_height);
230         break;
231       case PROP_SYNC:
232         g_value_set_boolean (value, priv->sync);
233         break;
234       case PROP_ASYNC:
235         g_value_set_boolean (value, priv->async);
236         break;
237       default:
238         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
239     }
240 }
241
242
243 static void
244 empathy_video_widget_class_init (
245   EmpathyVideoWidgetClass *empathy_video_widget_class)
246 {
247   GObjectClass *object_class = G_OBJECT_CLASS (empathy_video_widget_class);
248   GtkWidgetClass *widget_class =
249     GTK_WIDGET_CLASS (empathy_video_widget_class);
250   GParamSpec *param_spec;
251
252   g_type_class_add_private (empathy_video_widget_class,
253     sizeof (EmpathyVideoWidgetPriv));
254
255   object_class->dispose = empathy_video_widget_dispose;
256   object_class->finalize = empathy_video_widget_finalize;
257   object_class->constructed = empathy_video_widget_constructed;
258
259   object_class->set_property = empathy_video_widget_set_property;
260   object_class->get_property = empathy_video_widget_get_property;
261
262   widget_class->draw = empathy_video_widget_draw;
263
264   param_spec = g_param_spec_object ("gst-element",
265     "gst-element", "The underlaying gstreamer element",
266     GST_TYPE_ELEMENT,
267     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
268   g_object_class_install_property (object_class, PROP_GST_ELEMENT, param_spec);
269
270   param_spec = g_param_spec_object ("gst-bus",
271     "gst-bus",
272     "The toplevel bus from the pipeline in which this bin will be added",
273     GST_TYPE_BUS,
274     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
275   g_object_class_install_property (object_class, PROP_GST_BUS, param_spec);
276
277   param_spec = g_param_spec_int ("min-width",
278     "min-width",
279     "Minimal width of the widget",
280     0, G_MAXINT, EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH,
281     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
282   g_object_class_install_property (object_class, PROP_MIN_WIDTH, param_spec);
283
284   param_spec = g_param_spec_int ("min-height",
285     "min-height",
286     "Minimal height of the widget",
287     0, G_MAXINT, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT,
288     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
289   g_object_class_install_property (object_class, PROP_MIN_HEIGHT, param_spec);
290
291   param_spec = g_param_spec_boolean ("sync",
292     "sync",
293     "Whether the underlying sink should be sync or not",
294     TRUE,
295     G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
296   g_object_class_install_property (object_class, PROP_SYNC, param_spec);
297
298   param_spec = g_param_spec_boolean ("async",
299     "async",
300     "Whether the underlying sink should be async or not",
301     TRUE,
302     G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
303   g_object_class_install_property (object_class, PROP_ASYNC, param_spec);
304 }
305
306 void
307 empathy_video_widget_dispose (GObject *object)
308 {
309   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object);
310   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
311
312   if (priv->dispose_has_run)
313     return;
314
315   priv->dispose_has_run = TRUE;
316
317   g_signal_handlers_disconnect_by_func (priv->bus,
318     empathy_video_widget_sync_message_cb, object);
319
320   if (priv->bus != NULL)
321     g_object_unref (priv->bus);
322
323   priv->bus = NULL;
324
325   if (priv->videosink != NULL)
326     g_object_unref (priv->videosink);
327
328   priv->videosink = NULL;
329
330
331   /* release any references held by the object here */
332
333   if (G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose)
334     G_OBJECT_CLASS (empathy_video_widget_parent_class)->dispose (object);
335 }
336
337 void
338 empathy_video_widget_finalize (GObject *object)
339 {
340   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (object);
341   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
342
343   /* free any data held directly by the object here */
344   g_mutex_free (priv->lock);
345
346   G_OBJECT_CLASS (empathy_video_widget_parent_class)->finalize (object);
347 }
348
349
350 static void
351 empathy_video_widget_element_set_sink_properties_unlocked (
352   EmpathyVideoWidget *self)
353 {
354   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
355
356   if (priv->overlay == NULL)
357     return;
358
359   if (g_object_class_find_property (G_OBJECT_GET_CLASS (priv->overlay),
360       "force-aspect-ratio"))
361     g_object_set (G_OBJECT (priv->overlay), "force-aspect-ratio", TRUE, NULL);
362
363   if (g_object_class_find_property (
364       G_OBJECT_GET_CLASS (priv->overlay), "sync"))
365     g_object_set (G_OBJECT (priv->overlay), "sync", priv->sync, NULL);
366
367   if (g_object_class_find_property (G_OBJECT_GET_CLASS (priv->overlay),
368       "async"))
369     g_object_set (G_OBJECT (priv->overlay), "async", priv->async, NULL);
370 }
371
372 static void
373 empathy_video_widget_element_set_sink_properties (EmpathyVideoWidget *self)
374 {
375   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
376
377   g_mutex_lock (priv->lock);
378   empathy_video_widget_element_set_sink_properties_unlocked (self);
379   g_mutex_unlock (priv->lock);
380 }
381
382 static void
383 empathy_video_widget_element_added_cb (FsElementAddedNotifier *notifier,
384   GstBin *bin, GstElement *element, EmpathyVideoWidget *self)
385 {
386   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
387
388   /* We assume the overlay is the sink */
389   g_mutex_lock (priv->lock);
390   if (priv->overlay == NULL && GST_IS_X_OVERLAY (element))
391     {
392       priv->overlay = element;
393       g_object_add_weak_pointer (G_OBJECT (element),
394         (gpointer) &priv->overlay);
395       empathy_video_widget_element_set_sink_properties_unlocked (self);
396       gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay));
397     }
398   g_mutex_unlock (priv->lock);
399 }
400
401 static void
402 empathy_video_widget_sync_message_cb (GstBus *bus, GstMessage *message,
403   EmpathyVideoWidget *self)
404 {
405   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
406   const GstStructure *s;
407
408   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
409     return;
410
411   if (GST_MESSAGE_SRC (message) != (GstObject *) priv->overlay)
412     return;
413
414   s = gst_message_get_structure (message);
415
416   if (gst_structure_has_name (s, "prepare-xwindow-id"))
417     {
418       g_assert (gtk_widget_get_realized (GTK_WIDGET (self)));
419       gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay),
420         GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (self))));
421     }
422 }
423
424 static gboolean
425 empathy_video_widget_draw (GtkWidget *widget,
426     cairo_t *cr)
427 {
428   EmpathyVideoWidget *self = EMPATHY_VIDEO_WIDGET (widget);
429   EmpathyVideoWidgetPriv *priv = GET_PRIV (self);
430   GtkAllocation allocation;
431
432   if (priv->overlay == NULL)
433     {
434       gtk_widget_get_allocation (widget, &allocation);
435
436       gtk_render_frame (gtk_widget_get_style_context (widget), cr,
437           0, 0,
438           gtk_widget_get_allocated_width (widget),
439           gtk_widget_get_allocated_height (widget));
440       return TRUE;
441     }
442
443   gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (priv->overlay),
444     GDK_WINDOW_XID (gtk_widget_get_window (widget)));
445
446   gst_x_overlay_expose (GST_X_OVERLAY (priv->overlay));
447
448   return TRUE;
449 }
450
451 GtkWidget *
452 empathy_video_widget_new_with_size (GstBus *bus, gint width, gint height)
453 {
454   g_return_val_if_fail (bus != NULL, NULL);
455
456   return GTK_WIDGET (g_object_new (EMPATHY_TYPE_VIDEO_WIDGET,
457     "gst-bus", bus,
458     "min-width", width,
459     "min-height", height,
460     NULL));
461 }
462
463 GtkWidget *
464 empathy_video_widget_new (GstBus *bus)
465 {
466   g_return_val_if_fail (bus != NULL, NULL);
467
468   return GTK_WIDGET (g_object_new (EMPATHY_TYPE_VIDEO_WIDGET,
469     "gst-bus", bus,
470     NULL));
471 }
472
473 GstPad *
474 empathy_video_widget_get_sink (EmpathyVideoWidget *widget)
475 {
476   EmpathyVideoWidgetPriv *priv = GET_PRIV (widget);
477
478   return priv->sink_pad;
479 }
480
481 GstElement *
482 empathy_video_widget_get_element (EmpathyVideoWidget *widget)
483 {
484   EmpathyVideoWidgetPriv *priv = GET_PRIV (widget);
485
486   return priv->videosink;
487 }