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