1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/video/videooverlay.h>
26 #include <gst/video/navigation.h>
27 #include <gst/controller/gstproxycontrolbinding.h>
28 
29 #include "gstglsinkbin.h"
30 
31 GST_DEBUG_CATEGORY (gst_debug_gl_sink_bin);
32 #define GST_CAT_DEFAULT gst_debug_gl_sink_bin
33 
34 static void gst_gl_sink_bin_finalize (GObject * object);
35 static void gst_gl_sink_bin_set_property (GObject * object, guint prop_id,
36     const GValue * value, GParamSpec * param_spec);
37 static void gst_gl_sink_bin_get_property (GObject * object, guint prop_id,
38     GValue * value, GParamSpec * param_spec);
39 
40 static GstStateChangeReturn gst_gl_sink_bin_change_state (GstElement * element,
41     GstStateChange transition);
42 
43 static void gst_gl_sink_bin_video_overlay_init (gpointer g_iface,
44     gpointer g_iface_data);
45 static void gst_gl_sink_bin_navigation_interface_init (gpointer g_iface,
46     gpointer g_iface_data);
47 static void gst_gl_sink_bin_color_balance_init (gpointer g_iface,
48     gpointer g_iface_data);
49 
50 #define DEFAULT_SYNC                TRUE
51 #define DEFAULT_MAX_LATENESS        -1
52 #define DEFAULT_QOS                 FALSE
53 #define DEFAULT_ASYNC               TRUE
54 #define DEFAULT_TS_OFFSET           0
55 #define DEFAULT_BLOCKSIZE           4096
56 #define DEFAULT_RENDER_DELAY        0
57 #define DEFAULT_ENABLE_LAST_SAMPLE  TRUE
58 #define DEFAULT_THROTTLE_TIME       0
59 #define DEFAULT_MAX_BITRATE         0
60 
61 /* GstGLColorBalance properties */
62 #define DEFAULT_PROP_CONTRAST       1.0
63 #define DEFAULT_PROP_BRIGHTNESS	    0.0
64 #define DEFAULT_PROP_HUE            0.0
65 #define DEFAULT_PROP_SATURATION	    1.0
66 
67 enum
68 {
69   PROP_0,
70   PROP_FORCE_ASPECT_RATIO,
71   PROP_SINK,
72   PROP_SYNC,
73   PROP_MAX_LATENESS,
74   PROP_QOS,
75   PROP_ASYNC,
76   PROP_TS_OFFSET,
77   PROP_ENABLE_LAST_SAMPLE,
78   PROP_LAST_SAMPLE,
79   PROP_BLOCKSIZE,
80   PROP_RENDER_DELAY,
81   PROP_THROTTLE_TIME,
82   PROP_MAX_BITRATE,
83   PROP_CONTRAST,
84   PROP_BRIGHTNESS,
85   PROP_HUE,
86   PROP_SATURATION,
87 };
88 
89 enum
90 {
91   SIGNAL_0,
92   SIGNAL_CREATE_ELEMENT,
93   SIGNAL_LAST,
94 };
95 
96 static guint gst_gl_sink_bin_signals[SIGNAL_LAST] = { 0, };
97 
98 #define gst_gl_sink_bin_parent_class parent_class
99 G_DEFINE_TYPE_WITH_CODE (GstGLSinkBin, gst_gl_sink_bin,
100     GST_TYPE_BIN, G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
101         gst_gl_sink_bin_video_overlay_init);
102     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
103         gst_gl_sink_bin_navigation_interface_init);
104     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
105         gst_gl_sink_bin_color_balance_init)
106     GST_DEBUG_CATEGORY_INIT (gst_debug_gl_sink_bin, "glimagesink", 0,
107         "OpenGL Video Sink Bin"));
108 
109 static void
gst_gl_sink_bin_class_init(GstGLSinkBinClass * klass)110 gst_gl_sink_bin_class_init (GstGLSinkBinClass * klass)
111 {
112   GObjectClass *gobject_class;
113   GstElementClass *element_class;
114   GstCaps *upload_caps;
115 
116   gobject_class = (GObjectClass *) klass;
117   element_class = GST_ELEMENT_CLASS (klass);
118 
119   element_class->change_state = gst_gl_sink_bin_change_state;
120 
121   gobject_class->set_property = gst_gl_sink_bin_set_property;
122   gobject_class->get_property = gst_gl_sink_bin_get_property;
123   gobject_class->finalize = gst_gl_sink_bin_finalize;
124 
125   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
126       g_param_spec_boolean ("force-aspect-ratio",
127           "Force aspect ratio",
128           "When enabled, scaling will respect original aspect ratio", TRUE,
129           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
130   g_object_class_install_property (gobject_class, PROP_SINK,
131       g_param_spec_object ("sink",
132           "GL sink element",
133           "The GL sink chain to use",
134           GST_TYPE_ELEMENT,
135           GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
136           G_PARAM_STATIC_STRINGS));
137 
138   /* base sink */
139   g_object_class_install_property (gobject_class, PROP_SYNC,
140       g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
141           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
142   g_object_class_install_property (gobject_class, PROP_MAX_LATENESS,
143       g_param_spec_int64 ("max-lateness", "Max Lateness",
144           "Maximum number of nanoseconds that a buffer can be late before it "
145           "is dropped (-1 unlimited)", -1, G_MAXINT64, DEFAULT_MAX_LATENESS,
146           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
147   g_object_class_install_property (gobject_class, PROP_QOS,
148       g_param_spec_boolean ("qos", "Qos",
149           "Generate Quality-of-Service events upstream", DEFAULT_QOS,
150           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151   g_object_class_install_property (gobject_class, PROP_ASYNC,
152       g_param_spec_boolean ("async", "Async",
153           "Go asynchronously to PAUSED", DEFAULT_ASYNC,
154           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155   g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
156       g_param_spec_int64 ("ts-offset", "TS Offset",
157           "Timestamp offset in nanoseconds", G_MININT64, G_MAXINT64,
158           DEFAULT_TS_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
159   g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_SAMPLE,
160       g_param_spec_boolean ("enable-last-sample", "Enable Last Buffer",
161           "Enable the last-sample property", DEFAULT_ENABLE_LAST_SAMPLE,
162           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
163   g_object_class_install_property (gobject_class, PROP_LAST_SAMPLE,
164       g_param_spec_boxed ("last-sample", "Last Sample",
165           "The last sample received in the sink", GST_TYPE_SAMPLE,
166           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
167   g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
168       g_param_spec_uint ("blocksize", "Block size",
169           "Size in bytes to pull per buffer (0 = default)", 0, G_MAXUINT,
170           DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
171   g_object_class_install_property (gobject_class, PROP_RENDER_DELAY,
172       g_param_spec_uint64 ("render-delay", "Render Delay",
173           "Additional render delay of the sink in nanoseconds", 0, G_MAXUINT64,
174           DEFAULT_RENDER_DELAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
175   g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME,
176       g_param_spec_uint64 ("throttle-time", "Throttle time",
177           "The time to keep between rendered buffers (0 = disabled)", 0,
178           G_MAXUINT64, DEFAULT_THROTTLE_TIME,
179           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180   g_object_class_install_property (gobject_class, PROP_MAX_BITRATE,
181       g_param_spec_uint64 ("max-bitrate", "Max Bitrate",
182           "The maximum bits per second to render (0 = disabled)", 0,
183           G_MAXUINT64, DEFAULT_MAX_BITRATE,
184           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
185 
186   /* colorbalance */
187   g_object_class_install_property (gobject_class, PROP_CONTRAST,
188       g_param_spec_double ("contrast", "Contrast", "contrast",
189           0.0, 2.0, DEFAULT_PROP_CONTRAST,
190           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
192       g_param_spec_double ("brightness", "Brightness", "brightness", -1.0, 1.0,
193           DEFAULT_PROP_BRIGHTNESS,
194           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195   g_object_class_install_property (gobject_class, PROP_HUE,
196       g_param_spec_double ("hue", "Hue", "hue", -1.0, 1.0, DEFAULT_PROP_HUE,
197           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
198   g_object_class_install_property (gobject_class, PROP_SATURATION,
199       g_param_spec_double ("saturation", "Saturation", "saturation", 0.0, 2.0,
200           DEFAULT_PROP_SATURATION,
201           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
202 
203   /**
204    * GstGLSinkBin::create-element:
205    * @object: the #GstGLSinkBin
206    *
207    * Will be emitted when we need the processing element/s that this bin will use
208    *
209    * Returns: a new #GstElement
210    */
211   gst_gl_sink_bin_signals[SIGNAL_CREATE_ELEMENT] =
212       g_signal_new ("create-element", G_TYPE_FROM_CLASS (klass),
213       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
214       GST_TYPE_ELEMENT, 0);
215 
216   gst_element_class_set_metadata (element_class,
217       "GL Sink Bin", "Sink/Video",
218       "Infrastructure to process GL textures",
219       "Matthew Waters <matthew@centricular.com>");
220 
221   upload_caps = gst_gl_upload_get_input_template_caps ();
222   gst_element_class_add_pad_template (element_class,
223       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, upload_caps));
224   gst_caps_unref (upload_caps);
225 }
226 
227 static void
gst_gl_sink_bin_init(GstGLSinkBin * self)228 gst_gl_sink_bin_init (GstGLSinkBin * self)
229 {
230   gboolean res = TRUE;
231   GstPad *pad;
232 
233   self->upload = gst_element_factory_make ("glupload", NULL);
234   self->convert = gst_element_factory_make ("glcolorconvert", NULL);
235   self->balance = gst_element_factory_make ("glcolorbalance", NULL);
236 
237   res &= gst_bin_add (GST_BIN (self), self->upload);
238   res &= gst_bin_add (GST_BIN (self), self->convert);
239   res &= gst_bin_add (GST_BIN (self), self->balance);
240 
241   res &= gst_element_link_pads (self->upload, "src", self->convert, "sink");
242   res &= gst_element_link_pads (self->convert, "src", self->balance, "sink");
243 
244   pad = gst_element_get_static_pad (self->upload, "sink");
245   if (!pad) {
246     res = FALSE;
247   } else {
248     GST_DEBUG_OBJECT (self, "setting target sink pad %" GST_PTR_FORMAT, pad);
249     self->sinkpad = gst_ghost_pad_new ("sink", pad);
250     gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
251     gst_object_unref (pad);
252   }
253 
254 #define ADD_BINDING(obj,ref,prop) \
255     gst_object_add_control_binding (GST_OBJECT (obj), \
256         gst_proxy_control_binding_new (GST_OBJECT (obj), prop, \
257             GST_OBJECT (ref), prop));
258   ADD_BINDING (self->balance, self, "contrast");
259   ADD_BINDING (self->balance, self, "brightness");
260   ADD_BINDING (self->balance, self, "hue");
261   ADD_BINDING (self->balance, self, "saturation");
262 #undef ADD_BINDING
263 
264   if (!res) {
265     GST_WARNING_OBJECT (self, "Failed to add/connect the necessary machinery");
266   }
267 }
268 
269 static void
gst_gl_sink_bin_finalize(GObject * object)270 gst_gl_sink_bin_finalize (GObject * object)
271 {
272   GstGLSinkBin *self = GST_GL_SINK_BIN (object);
273 
274   if (self->sink)
275     gst_object_unref (self->sink);
276 
277   G_OBJECT_CLASS (parent_class)->finalize (object);
278 }
279 
280 static gboolean
_connect_sink_element(GstGLSinkBin * self)281 _connect_sink_element (GstGLSinkBin * self)
282 {
283   gst_object_set_name (GST_OBJECT (self->sink), "sink");
284 
285   if (gst_bin_add (GST_BIN (self), self->sink) &&
286       gst_element_link_pads (self->balance, "src", self->sink, "sink"))
287     return TRUE;
288 
289   GST_ERROR_OBJECT (self, "Failed to link sink element into the pipeline");
290   return FALSE;
291 }
292 
293 /*
294  * @sink: (transfer full):
295  */
296 static gboolean
gst_gl_sink_bin_set_sink(GstGLSinkBin * self,GstElement * sink)297 gst_gl_sink_bin_set_sink (GstGLSinkBin * self, GstElement * sink)
298 {
299   g_return_val_if_fail (GST_IS_ELEMENT (sink), FALSE);
300 
301   if (self->sink) {
302     gst_element_set_locked_state (self->sink, TRUE);
303     gst_bin_remove (GST_BIN (self), self->sink);
304     gst_element_set_state (self->sink, GST_STATE_NULL);
305     gst_object_unref (self->sink);
306     self->sink = NULL;
307   }
308   self->sink = sink;
309 
310   gst_object_ref_sink (sink);
311 
312   if (sink && !_connect_sink_element (self)) {
313     gst_object_unref (self->sink);
314     self->sink = NULL;
315     return FALSE;
316   }
317 
318   return TRUE;
319 }
320 
321 void
gst_gl_sink_bin_finish_init_with_element(GstGLSinkBin * self,GstElement * element)322 gst_gl_sink_bin_finish_init_with_element (GstGLSinkBin * self,
323     GstElement * element)
324 {
325   gst_gl_sink_bin_set_sink (self, element);
326 }
327 
328 void
gst_gl_sink_bin_finish_init(GstGLSinkBin * self)329 gst_gl_sink_bin_finish_init (GstGLSinkBin * self)
330 {
331   GstGLSinkBinClass *klass = GST_GL_SINK_BIN_GET_CLASS (self);
332   GstElement *element = NULL;
333 
334   if (klass->create_element)
335     element = klass->create_element ();
336 
337   if (element)
338     gst_gl_sink_bin_finish_init_with_element (self, element);
339 }
340 
341 static void
gst_gl_sink_bin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)342 gst_gl_sink_bin_set_property (GObject * object, guint prop_id,
343     const GValue * value, GParamSpec * pspec)
344 {
345   GstGLSinkBin *self = GST_GL_SINK_BIN (object);
346   GParamSpec *sink_pspec;
347 
348   switch (prop_id) {
349     case PROP_SINK:
350       gst_gl_sink_bin_set_sink (self, g_value_get_object (value));
351       break;
352     case PROP_CONTRAST:
353     case PROP_BRIGHTNESS:
354     case PROP_HUE:
355     case PROP_SATURATION:
356       if (self->balance)
357         g_object_set_property (G_OBJECT (self->balance), pspec->name, value);
358       break;
359     default:
360       if (self->sink) {
361         sink_pspec =
362             g_object_class_find_property (G_OBJECT_GET_CLASS (self->sink),
363             pspec->name);
364         if (sink_pspec
365             && G_PARAM_SPEC_TYPE (sink_pspec) == G_PARAM_SPEC_TYPE (pspec)) {
366           g_object_set_property (G_OBJECT (self->sink), pspec->name, value);
367         } else {
368           GST_INFO ("Failed to set unmatched property %s", pspec->name);
369         }
370       }
371       break;
372   }
373 }
374 
375 static void
gst_gl_sink_bin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)376 gst_gl_sink_bin_get_property (GObject * object, guint prop_id,
377     GValue * value, GParamSpec * pspec)
378 {
379   GstGLSinkBin *self = GST_GL_SINK_BIN (object);
380 
381   switch (prop_id) {
382     case PROP_SINK:
383       g_value_set_object (value, self->sink);
384       break;
385     case PROP_CONTRAST:
386     case PROP_BRIGHTNESS:
387     case PROP_HUE:
388     case PROP_SATURATION:
389       if (self->balance)
390         g_object_get_property (G_OBJECT (self->balance), pspec->name, value);
391       break;
392     default:
393       if (self->sink)
394         g_object_get_property (G_OBJECT (self->sink), pspec->name, value);
395       break;
396   }
397 }
398 
399 static GstStateChangeReturn
gst_gl_sink_bin_change_state(GstElement * element,GstStateChange transition)400 gst_gl_sink_bin_change_state (GstElement * element, GstStateChange transition)
401 {
402   GstGLSinkBin *self = GST_GL_SINK_BIN (element);
403   GstGLSinkBinClass *klass = GST_GL_SINK_BIN_GET_CLASS (self);
404   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
405 
406   GST_DEBUG ("changing state: %s => %s",
407       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
408       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
409 
410   switch (transition) {
411     case GST_STATE_CHANGE_NULL_TO_READY:
412       if (!self->sink) {
413         if (klass->create_element)
414           self->sink = klass->create_element ();
415 
416         if (!self->sink) {
417           g_signal_emit (element,
418               gst_gl_sink_bin_signals[SIGNAL_CREATE_ELEMENT], 0, &self->sink);
419           if (self->sink && g_object_is_floating (self->sink))
420             gst_object_ref_sink (self->sink);
421         }
422 
423         if (!self->sink) {
424           GST_ERROR_OBJECT (element, "Failed to retrieve element");
425           return GST_STATE_CHANGE_FAILURE;
426         }
427         if (!_connect_sink_element (self))
428           return GST_STATE_CHANGE_FAILURE;
429       }
430       break;
431     default:
432       break;
433   }
434 
435   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
436   if (ret == GST_STATE_CHANGE_FAILURE)
437     return ret;
438 
439   switch (transition) {
440     default:
441       break;
442   }
443 
444   return ret;
445 }
446 
447 static void
gst_gl_sink_bin_navigation_send_event(GstNavigation * navigation,GstStructure * structure)448 gst_gl_sink_bin_navigation_send_event (GstNavigation * navigation, GstStructure
449     * structure)
450 {
451   GstGLSinkBin *self = GST_GL_SINK_BIN (navigation);
452   GstElement *nav =
453       gst_bin_get_by_interface (GST_BIN (self), GST_TYPE_NAVIGATION);
454 
455   if (nav) {
456     gst_navigation_send_event (GST_NAVIGATION (nav), structure);
457     structure = NULL;
458     gst_object_unref (nav);
459   } else {
460     GstEvent *event = gst_event_new_navigation (structure);
461     structure = NULL;
462     gst_element_send_event (GST_ELEMENT (self), event);
463   }
464 }
465 
466 static void
gst_gl_sink_bin_navigation_interface_init(gpointer g_iface,gpointer g_iface_data)467 gst_gl_sink_bin_navigation_interface_init (gpointer g_iface,
468     gpointer g_iface_data)
469 {
470   GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
471   iface->send_event = gst_gl_sink_bin_navigation_send_event;
472 }
473 
474 static void
gst_gl_sink_bin_overlay_expose(GstVideoOverlay * overlay)475 gst_gl_sink_bin_overlay_expose (GstVideoOverlay * overlay)
476 {
477   GstGLSinkBin *self = GST_GL_SINK_BIN (overlay);
478   GstVideoOverlay *overlay_element = NULL;
479 
480   overlay_element =
481       GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (self),
482           GST_TYPE_VIDEO_OVERLAY));
483 
484   if (overlay_element) {
485     gst_video_overlay_expose (overlay_element);
486     gst_object_unref (overlay_element);
487   }
488 }
489 
490 static void
gst_gl_sink_bin_overlay_handle_events(GstVideoOverlay * overlay,gboolean handle_events)491 gst_gl_sink_bin_overlay_handle_events (GstVideoOverlay * overlay,
492     gboolean handle_events)
493 {
494   GstGLSinkBin *self = GST_GL_SINK_BIN (overlay);
495   GstVideoOverlay *overlay_element = NULL;
496 
497   overlay_element =
498       GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (self),
499           GST_TYPE_VIDEO_OVERLAY));
500 
501   if (overlay_element) {
502     gst_video_overlay_handle_events (overlay_element, handle_events);
503     gst_object_unref (overlay_element);
504   }
505 }
506 
507 static void
gst_gl_sink_bin_overlay_set_render_rectangle(GstVideoOverlay * overlay,gint x,gint y,gint width,gint height)508 gst_gl_sink_bin_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
509     gint y, gint width, gint height)
510 {
511   GstGLSinkBin *self = GST_GL_SINK_BIN (overlay);
512   GstVideoOverlay *overlay_element = NULL;
513 
514   overlay_element =
515       GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (self),
516           GST_TYPE_VIDEO_OVERLAY));
517 
518   if (overlay_element) {
519     gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
520         height);
521     gst_object_unref (overlay_element);
522   }
523 }
524 
525 static void
gst_gl_sink_bin_overlay_set_window_handle(GstVideoOverlay * overlay,guintptr handle)526 gst_gl_sink_bin_overlay_set_window_handle (GstVideoOverlay * overlay,
527     guintptr handle)
528 {
529   GstGLSinkBin *self = GST_GL_SINK_BIN (overlay);
530   GstVideoOverlay *overlay_element = NULL;
531 
532   overlay_element =
533       GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (self),
534           GST_TYPE_VIDEO_OVERLAY));
535 
536   if (overlay_element) {
537     gst_video_overlay_set_window_handle (overlay_element, handle);
538     gst_object_unref (overlay_element);
539   }
540 }
541 
542 static void
gst_gl_sink_bin_video_overlay_init(gpointer g_iface,gpointer g_iface_data)543 gst_gl_sink_bin_video_overlay_init (gpointer g_iface, gpointer g_iface_data)
544 {
545   GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
546   iface->expose = gst_gl_sink_bin_overlay_expose;
547   iface->handle_events = gst_gl_sink_bin_overlay_handle_events;
548   iface->set_render_rectangle = gst_gl_sink_bin_overlay_set_render_rectangle;
549   iface->set_window_handle = gst_gl_sink_bin_overlay_set_window_handle;
550 }
551 
552 static const GList *
gst_gl_sink_bin_color_balance_list_channels(GstColorBalance * balance)553 gst_gl_sink_bin_color_balance_list_channels (GstColorBalance * balance)
554 {
555   GstGLSinkBin *self = GST_GL_SINK_BIN (balance);
556   GstColorBalance *balance_element = NULL;
557   const GList *list = NULL;
558 
559   balance_element =
560       GST_COLOR_BALANCE (gst_bin_get_by_interface (GST_BIN (self),
561           GST_TYPE_COLOR_BALANCE));
562 
563   if (balance_element) {
564     list = gst_color_balance_list_channels (balance_element);
565     gst_object_unref (balance_element);
566   }
567 
568   return list;
569 }
570 
571 static void
gst_gl_sink_bin_color_balance_set_value(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value)572 gst_gl_sink_bin_color_balance_set_value (GstColorBalance * balance,
573     GstColorBalanceChannel * channel, gint value)
574 {
575   GstGLSinkBin *self = GST_GL_SINK_BIN (balance);
576   GstColorBalance *balance_element = NULL;
577 
578   balance_element =
579       GST_COLOR_BALANCE (gst_bin_get_by_interface (GST_BIN (self),
580           GST_TYPE_COLOR_BALANCE));
581 
582   if (balance_element) {
583     gst_color_balance_set_value (balance_element, channel, value);
584     gst_object_unref (balance_element);
585   }
586 }
587 
588 static gint
gst_gl_sink_bin_color_balance_get_value(GstColorBalance * balance,GstColorBalanceChannel * channel)589 gst_gl_sink_bin_color_balance_get_value (GstColorBalance * balance,
590     GstColorBalanceChannel * channel)
591 {
592   GstGLSinkBin *self = GST_GL_SINK_BIN (balance);
593   GstColorBalance *balance_element = NULL;
594   gint val = 0;
595 
596   balance_element =
597       GST_COLOR_BALANCE (gst_bin_get_by_interface (GST_BIN (self),
598           GST_TYPE_COLOR_BALANCE));
599 
600   if (balance_element) {
601     val = gst_color_balance_get_value (balance_element, channel);
602     gst_object_unref (balance_element);
603   }
604 
605   return val;
606 }
607 
608 static GstColorBalanceType
gst_gl_sink_bin_color_balance_get_balance_type(GstColorBalance * balance)609 gst_gl_sink_bin_color_balance_get_balance_type (GstColorBalance * balance)
610 {
611   GstGLSinkBin *self = GST_GL_SINK_BIN (balance);
612   GstColorBalance *balance_element = NULL;
613   GstColorBalanceType type = 0;
614 
615   balance_element =
616       GST_COLOR_BALANCE (gst_bin_get_by_interface (GST_BIN (self),
617           GST_TYPE_COLOR_BALANCE));
618 
619   if (balance_element) {
620     type = gst_color_balance_get_balance_type (balance_element);
621     gst_object_unref (balance_element);
622   }
623 
624   return type;
625 }
626 
627 static void
gst_gl_sink_bin_color_balance_init(gpointer g_iface,gpointer g_iface_data)628 gst_gl_sink_bin_color_balance_init (gpointer g_iface, gpointer g_iface_data)
629 {
630   GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
631 
632   iface->list_channels = gst_gl_sink_bin_color_balance_list_channels;
633   iface->set_value = gst_gl_sink_bin_color_balance_set_value;
634   iface->get_value = gst_gl_sink_bin_color_balance_get_value;
635   iface->get_balance_type = gst_gl_sink_bin_color_balance_get_balance_type;
636 }
637