• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2021> Collabora Ltd.
3  *   Author: Nicolas Dufresne <nicolas.dufresne@collabora.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 /**
22  * SECTION:element-alphacombine
23  * @title: Alpha Combiner
24  *
25  * This element can combine a Luma plane from one stream as being the alpha
26  * plane of another stream. This element can only work with planar formats
27  * that have an equivalent format with an alpha plane. This is notably used to
28  * combine VP8/VP9 alpha streams from WebM container.
29  *
30  * ## Example launch line
31  * |[
32  * gst-launch-1.0 -v videotestsrc ! c. videotestsrc pattern=ball ! c.
33  *     alphacombine name=c ! compositor ! videoconvert ! autovideosink
34  * ]| This pipeline uses luma of a ball test pattern as alpha, combined with
35  * default test pattern and renders the resulting moving ball on a checker
36  * board.
37  *
38  * Since: 1.20
39  */
40 
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44 
45 #include <gst/video/video.h>
46 
47 #include "gstalphacombine.h"
48 
49 
50 #define SUPPORTED_SINK_FORMATS "{ I420, NV12 }"
51 #define SUPPORTED_ALPHA_FORMATS "{ GRAY8, I420, NV12 }"
52 #define SUPPORTED_SRC_FORMATS "{ A420, AV12 }"
53 
54 /* *INDENT-OFF* */
55 struct {
56   GstVideoFormat sink;
57   GstVideoFormat alpha;
58   GstVideoFormat src;
59 } format_map[] = {
60   {
61     .sink = GST_VIDEO_FORMAT_I420,
62     .alpha = GST_VIDEO_FORMAT_I420,
63     .src = GST_VIDEO_FORMAT_A420
64   },{
65     .sink = GST_VIDEO_FORMAT_I420,
66     .alpha = GST_VIDEO_FORMAT_GRAY8,
67     .src = GST_VIDEO_FORMAT_A420
68  },{
69     .sink = GST_VIDEO_FORMAT_I420,
70     .alpha = GST_VIDEO_FORMAT_NV12,
71     .src = GST_VIDEO_FORMAT_A420
72   }, {
73     .sink = GST_VIDEO_FORMAT_NV12,
74     .alpha = GST_VIDEO_FORMAT_NV12,
75     .src = GST_VIDEO_FORMAT_AV12,
76   }, {
77     .sink = GST_VIDEO_FORMAT_NV12,
78     .alpha = GST_VIDEO_FORMAT_GRAY8,
79     .src = GST_VIDEO_FORMAT_AV12
80  },{
81     .sink = GST_VIDEO_FORMAT_NV12,
82     .alpha = GST_VIDEO_FORMAT_I420,
83     .src = GST_VIDEO_FORMAT_AV12
84   },
85 };
86 /* *INDENT-ON* */
87 
88 GST_DEBUG_CATEGORY_STATIC (alphacombine_debug);
89 #define GST_CAT_DEFAULT (alphacombine_debug)
90 
91 struct _GstAlphaCombine
92 {
93   GstElement parent;
94 
95   GstPad *sink_pad;
96   GstPad *alpha_pad;
97   GstPad *src_pad;
98 
99   /* protected by sink_pad stream lock */
100   GstBuffer *last_alpha_buffer;
101   GstFlowReturn last_flow_ret;
102 
103   GMutex buffer_lock;
104   GCond buffer_cond;
105   GstBuffer *alpha_buffer;
106   /* Ref-counted flushing state */
107   guint flushing;
108 
109   GstVideoInfo sink_vinfo;
110   GstVideoInfo alpha_vinfo;
111   GstVideoFormat src_format;
112 
113   guint sink_format_cookie;
114   guint alpha_format_cookie;
115 };
116 
117 #define gst_alpha_combine_parent_class parent_class
118 G_DEFINE_TYPE_WITH_CODE (GstAlphaCombine, gst_alpha_combine,
119     GST_TYPE_ELEMENT,
120     GST_DEBUG_CATEGORY_INIT (alphacombine_debug, "alphacombine", 0,
121         "Alpha Combiner"));
122 
123 GST_ELEMENT_REGISTER_DEFINE (alpha_combine, "alphacombine",
124     GST_RANK_NONE, GST_TYPE_ALPHA_COMBINE);
125 
126 static GstStaticPadTemplate gst_alpha_combine_sink_template =
127 GST_STATIC_PAD_TEMPLATE ("sink",
128     GST_PAD_SINK,
129     GST_PAD_ALWAYS,
130     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (SUPPORTED_SINK_FORMATS))
131     );
132 
133 static GstStaticPadTemplate gst_alpha_combine_alpha_template =
134 GST_STATIC_PAD_TEMPLATE ("alpha",
135     GST_PAD_SINK,
136     GST_PAD_ALWAYS,
137     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (SUPPORTED_ALPHA_FORMATS))
138     );
139 
140 static GstStaticPadTemplate gst_alpha_combine_src_template =
141 GST_STATIC_PAD_TEMPLATE ("src",
142     GST_PAD_SRC,
143     GST_PAD_ALWAYS,
144     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (SUPPORTED_SRC_FORMATS))
145     );
146 
147 static void
gst_alpha_combine_unlock(GstAlphaCombine * self)148 gst_alpha_combine_unlock (GstAlphaCombine * self)
149 {
150   g_mutex_lock (&self->buffer_lock);
151   self->flushing++;
152   g_cond_broadcast (&self->buffer_cond);
153   g_mutex_unlock (&self->buffer_lock);
154 }
155 
156 static void
gst_alpha_combine_unlock_stop(GstAlphaCombine * self)157 gst_alpha_combine_unlock_stop (GstAlphaCombine * self)
158 {
159   g_mutex_lock (&self->buffer_lock);
160   g_assert (self->flushing);
161   self->flushing--;
162 
163   /* Reset the format cookies to ensure they are equal */
164   if (!self->flushing) {
165     self->sink_format_cookie = 0;
166     self->alpha_format_cookie = 0;
167   }
168 
169   g_mutex_unlock (&self->buffer_lock);
170 }
171 
172 static void
gst_alpha_combine_reset(GstAlphaCombine * self)173 gst_alpha_combine_reset (GstAlphaCombine * self)
174 {
175   gst_buffer_replace (&self->alpha_buffer, NULL);
176   gst_buffer_replace (&self->last_alpha_buffer, NULL);
177   self->last_flow_ret = GST_FLOW_OK;
178 }
179 
180 /*
181  * gst_alpha_combine_negotiate:
182  * @self: #GstAlphaCombine pointer
183  *
184  * Verify that the stream and alpha stream format are compatible and fail
185  * otherwise. There is no effort in helping upstream to dynamically negotiate
186  * a valid combination to keep the complexity low, and because this would be a
187  * very atypical usage.
188  */
189 static gboolean
gst_alpha_combine_negotiate(GstAlphaCombine * self)190 gst_alpha_combine_negotiate (GstAlphaCombine * self)
191 {
192   gint i;
193   GstVideoFormat src_format = GST_VIDEO_FORMAT_UNKNOWN;
194   GstVideoFormat sink_format = GST_VIDEO_INFO_FORMAT (&self->sink_vinfo);
195   GstVideoFormat alpha_format = GST_VIDEO_INFO_FORMAT (&self->alpha_vinfo);
196 
197   if (self->src_format != GST_VIDEO_FORMAT_UNKNOWN)
198     return TRUE;
199 
200   for (i = 0; i < G_N_ELEMENTS (format_map); i++) {
201     if (format_map[i].sink == sink_format
202         && format_map[i].alpha == alpha_format) {
203       src_format = format_map[i].src;
204       break;
205     }
206   }
207 
208   if (src_format == GST_VIDEO_FORMAT_UNKNOWN) {
209     GST_ELEMENT_ERROR (self, STREAM, FORMAT, ("Unsupported formats."),
210         ("Cannot combined '%s' and '%s' into any supported transparent format",
211             gst_video_format_to_string (sink_format),
212             gst_video_format_to_string (alpha_format)));
213     return FALSE;
214   }
215 
216   if (GST_VIDEO_INFO_COLORIMETRY (&self->sink_vinfo).range !=
217       GST_VIDEO_INFO_COLORIMETRY (&self->alpha_vinfo).range) {
218     GST_ELEMENT_ERROR (self, STREAM, FORMAT, ("Color range miss-match"),
219         ("We can only combine buffers if they have the same color range."));
220     return FALSE;
221   }
222 
223   self->src_format = src_format;
224   return TRUE;
225 }
226 
227 static GstFlowReturn
gst_alpha_combine_peek_alpha_buffer(GstAlphaCombine * self,GstBuffer ** alpha_buffer)228 gst_alpha_combine_peek_alpha_buffer (GstAlphaCombine * self,
229     GstBuffer ** alpha_buffer)
230 {
231   g_mutex_lock (&self->buffer_lock);
232 
233   while (!self->alpha_buffer && !self->flushing)
234     g_cond_wait (&self->buffer_cond, &self->buffer_lock);
235 
236   if (self->flushing) {
237     g_mutex_unlock (&self->buffer_lock);
238     return GST_FLOW_FLUSHING;
239   }
240 
241   /* Now is a good time to validate the formats, as the alpha_vinfo won't be
242    * updated until we signal this alpha_buffer_as being consumed */
243   if (!gst_alpha_combine_negotiate (self)) {
244     g_mutex_unlock (&self->buffer_lock);
245     return GST_FLOW_NOT_NEGOTIATED;
246   }
247 
248   *alpha_buffer = gst_buffer_ref (self->alpha_buffer);
249   g_mutex_unlock (&self->buffer_lock);
250 
251   if (GST_BUFFER_FLAG_IS_SET (*alpha_buffer, GST_BUFFER_FLAG_GAP)) {
252     if (!self->last_alpha_buffer) {
253       GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE,
254           ("Cannot handle streams without an initial alpha buffer."), (NULL));
255       gst_clear_buffer (alpha_buffer);
256       return GST_FLOW_ERROR;
257     }
258 
259     /* Re-use the last alpha buffer if one is gone missing */
260     gst_buffer_replace (alpha_buffer, self->last_alpha_buffer);
261   }
262 
263   return GST_FLOW_OK;
264 }
265 
266 static void
gst_alpha_combine_pop_alpha_buffer(GstAlphaCombine * self,GstFlowReturn flow_ret)267 gst_alpha_combine_pop_alpha_buffer (GstAlphaCombine * self,
268     GstFlowReturn flow_ret)
269 {
270   g_mutex_lock (&self->buffer_lock);
271   self->last_flow_ret = flow_ret;
272   gst_clear_buffer (&self->alpha_buffer);
273   g_cond_broadcast (&self->buffer_cond);
274   g_mutex_unlock (&self->buffer_lock);
275 }
276 
277 static GstFlowReturn
gst_alpha_combine_push_alpha_buffer(GstAlphaCombine * self,GstBuffer * buffer)278 gst_alpha_combine_push_alpha_buffer (GstAlphaCombine * self, GstBuffer * buffer)
279 {
280   GstFlowReturn ret;
281 
282   g_mutex_lock (&self->buffer_lock);
283 
284   /* We wait for the alpha_buffer to be consumed and store the buffer for the
285    * sink_chain to pick it up */
286   while (self->alpha_buffer && !self->flushing)
287     g_cond_wait (&self->buffer_cond, &self->buffer_lock);
288 
289   if (self->flushing) {
290     gst_buffer_unref (buffer);
291     g_mutex_unlock (&self->buffer_lock);
292     return GST_FLOW_FLUSHING;
293   }
294 
295   self->alpha_buffer = buffer;
296   GST_DEBUG_OBJECT (self, "Stored pending alpha buffer %p", buffer);
297   g_cond_signal (&self->buffer_cond);
298   ret = self->last_flow_ret;
299   g_mutex_unlock (&self->buffer_lock);
300 
301   return ret;
302 }
303 
304 static GstFlowReturn
gst_alpha_combine_sink_chain(GstPad * pad,GstObject * object,GstBuffer * src_buffer)305 gst_alpha_combine_sink_chain (GstPad * pad, GstObject * object,
306     GstBuffer * src_buffer)
307 {
308   GstAlphaCombine *self = GST_ALPHA_COMBINE (object);
309   GstFlowReturn ret;
310   GstVideoMeta *vmeta;
311   GstBuffer *alpha_buffer;
312   GstMemory *alpha_mem = NULL;
313   gsize alpha_skip = 0;
314   gint alpha_stride;
315   GstBuffer *buffer;
316   guint alpha_plane_idx;
317 
318   ret = gst_alpha_combine_peek_alpha_buffer (self, &alpha_buffer);
319   if (ret != GST_FLOW_OK)
320     return ret;
321 
322   GST_DEBUG_OBJECT (self, "Combining buffer %p with alpha buffer %p",
323       src_buffer, alpha_buffer);
324 
325   vmeta = gst_buffer_get_video_meta (alpha_buffer);
326   if (vmeta) {
327     guint idx, length;
328     if (gst_buffer_find_memory (alpha_buffer, vmeta->offset[GST_VIDEO_COMP_Y],
329             1, &idx, &length, &alpha_skip)) {
330       alpha_mem = gst_buffer_get_memory (alpha_buffer, idx);
331     }
332 
333     alpha_stride = vmeta->stride[GST_VIDEO_COMP_Y];
334   } else {
335     alpha_mem = gst_buffer_get_memory (alpha_buffer, 0);
336     alpha_stride = self->alpha_vinfo.stride[GST_VIDEO_COMP_Y];
337   }
338 
339   if (!alpha_mem) {
340     gst_buffer_unref (alpha_buffer);
341     gst_buffer_unref (src_buffer);
342     GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE,
343         ("Invalid alpha video frame."), ("Could not find the plane"));
344     return GST_FLOW_ERROR;
345   }
346 
347   /* FIXME use some GstBuffer cache to reduce run-time allocation */
348   buffer = gst_buffer_copy (src_buffer);
349   vmeta = gst_buffer_get_video_meta (buffer);
350   if (!vmeta)
351     vmeta = gst_buffer_add_video_meta (buffer, 0,
352         GST_VIDEO_INFO_FORMAT (&self->sink_vinfo),
353         GST_VIDEO_INFO_WIDTH (&self->sink_vinfo),
354         GST_VIDEO_INFO_HEIGHT (&self->sink_vinfo));
355 
356   alpha_skip += gst_buffer_get_size (buffer);
357   gst_buffer_append_memory (buffer, alpha_mem);
358 
359   alpha_plane_idx = GST_VIDEO_INFO_N_PLANES (&self->sink_vinfo);
360   vmeta->offset[alpha_plane_idx] = alpha_skip;
361   vmeta->stride[alpha_plane_idx] = alpha_stride;
362 
363   vmeta->format = self->src_format;
364   vmeta->n_planes = alpha_plane_idx + 1;
365 
366   /* Keep the origina GstBuffer alive to make this buffer pool friendly */
367   gst_buffer_add_parent_buffer_meta (buffer, src_buffer);
368   gst_buffer_add_parent_buffer_meta (buffer, alpha_buffer);
369 
370   gst_buffer_replace (&self->last_alpha_buffer, alpha_buffer);
371   gst_buffer_unref (src_buffer);
372   gst_buffer_unref (alpha_buffer);
373 
374   ret = gst_pad_push (self->src_pad, buffer);
375   gst_alpha_combine_pop_alpha_buffer (self, ret);
376 
377   return ret;
378 }
379 
380 static GstFlowReturn
gst_alpha_combine_alpha_chain(GstPad * pad,GstObject * object,GstBuffer * buffer)381 gst_alpha_combine_alpha_chain (GstPad * pad, GstObject * object,
382     GstBuffer * buffer)
383 {
384   GstAlphaCombine *self = GST_ALPHA_COMBINE (object);
385 
386   return gst_alpha_combine_push_alpha_buffer (self, buffer);
387 }
388 
389 static gboolean
gst_alpha_combine_set_sink_format(GstAlphaCombine * self,GstCaps * caps)390 gst_alpha_combine_set_sink_format (GstAlphaCombine * self, GstCaps * caps)
391 {
392   GstVideoFormat sink_format, src_format = GST_VIDEO_FORMAT_UNKNOWN;
393   GstEvent *event;
394   gint i;
395   gboolean ret;
396 
397   if (!gst_video_info_from_caps (&self->sink_vinfo, caps)) {
398     GST_ELEMENT_ERROR (self, STREAM, FORMAT, ("Invalid video format"), (NULL));
399     return FALSE;
400   }
401 
402   sink_format = GST_VIDEO_INFO_FORMAT (&self->sink_vinfo);
403 
404   /* The sink format determin the src format, though we cannot fully validate
405    * the negotiation here, since we don't have the alpha format yet. */
406   for (i = 0; i < G_N_ELEMENTS (format_map); i++) {
407     if (format_map[i].sink == sink_format) {
408       src_format = format_map[i].src;
409       break;
410     }
411   }
412 
413   if (src_format == GST_VIDEO_FORMAT_UNKNOWN) {
414     GST_ELEMENT_ERROR (self, STREAM, FORMAT, ("Unsupported formats."),
415         ("Sink format '%s' not supported.",
416             gst_video_format_to_string (sink_format)));
417     return FALSE;
418   }
419 
420   caps = gst_caps_copy (caps);
421   gst_caps_set_simple (caps, "format", G_TYPE_STRING,
422       gst_video_format_to_string (src_format), NULL);
423   event = gst_event_new_caps (caps);
424   gst_caps_unref (caps);
425 
426   ret = gst_pad_push_event (self->src_pad, event);
427 
428   /* signal the sink format change */
429   g_mutex_lock (&self->buffer_lock);
430   self->sink_format_cookie++;
431   g_cond_signal (&self->buffer_cond);
432   g_mutex_unlock (&self->buffer_lock);
433 
434   return ret;
435 }
436 
437 static gboolean
gst_alpha_combine_set_alpha_format(GstAlphaCombine * self,GstCaps * caps)438 gst_alpha_combine_set_alpha_format (GstAlphaCombine * self, GstCaps * caps)
439 {
440   /* We wait for the alpha_buffer to be consumed, so that we don't pick the
441    * caps too soon */
442   g_mutex_lock (&self->buffer_lock);
443 
444   /* We wait for the alpha_buffer to be consumed and store the buffer for the
445    * sink_chain to pick it up */
446   while (self->alpha_buffer && !self->flushing)
447     g_cond_wait (&self->buffer_cond, &self->buffer_lock);
448 
449   if (self->flushing) {
450     g_mutex_unlock (&self->buffer_lock);
451     return GST_FLOW_FLUSHING;
452   }
453 
454   if (!gst_video_info_from_caps (&self->alpha_vinfo, caps)) {
455     g_mutex_unlock (&self->buffer_lock);
456     GST_ELEMENT_ERROR (self, STREAM, FORMAT, ("Invalid video format"), (NULL));
457     return FALSE;
458   }
459 
460   self->alpha_format_cookie++;
461 
462   /* wait for the matching format change on the sink pad */
463   while (self->alpha_format_cookie != self->sink_format_cookie &&
464       !self->flushing)
465     g_cond_wait (&self->buffer_cond, &self->buffer_lock);
466 
467   g_mutex_unlock (&self->buffer_lock);
468 
469   return TRUE;
470 }
471 
472 static gboolean
gst_alpha_combine_sink_event(GstPad * pad,GstObject * object,GstEvent * event)473 gst_alpha_combine_sink_event (GstPad * pad, GstObject * object,
474     GstEvent * event)
475 {
476   GstAlphaCombine *self = GST_ALPHA_COMBINE (object);
477 
478   switch (event->type) {
479     case GST_EVENT_FLUSH_START:
480       gst_alpha_combine_unlock (self);
481       break;
482     case GST_EVENT_FLUSH_STOP:
483       gst_alpha_combine_unlock_stop (self);
484       break;
485     case GST_EVENT_CAPS:
486     {
487       GstCaps *caps;
488       gboolean ret;
489 
490       gst_event_parse_caps (event, &caps);
491       ret = gst_alpha_combine_set_sink_format (self, caps);
492       gst_event_unref (event);
493 
494       return ret;
495     }
496     default:
497       break;
498   }
499 
500   return gst_pad_event_default (pad, object, event);
501 }
502 
503 static gboolean
gst_alpha_combine_alpha_event(GstPad * pad,GstObject * object,GstEvent * event)504 gst_alpha_combine_alpha_event (GstPad * pad, GstObject * object,
505     GstEvent * event)
506 {
507   GstAlphaCombine *self = GST_ALPHA_COMBINE (object);
508 
509   switch (event->type) {
510     case GST_EVENT_FLUSH_START:
511       gst_alpha_combine_unlock (self);
512       break;
513     case GST_EVENT_FLUSH_STOP:
514       gst_alpha_combine_unlock_stop (self);
515       gst_alpha_combine_reset (self);
516       break;
517     case GST_EVENT_CAPS:
518     {
519       GstCaps *caps;
520       gst_event_parse_caps (event, &caps);
521       gst_alpha_combine_set_alpha_format (self, caps);
522     }
523     default:
524       break;
525   }
526 
527   /* Events are being duplicated, over both branches, so let's just drop this
528    * secondary stream and use the one from the main stream. */
529   gst_event_unref (event);
530   return TRUE;
531 }
532 
533 static gboolean
gst_alpha_combine_sink_query(GstPad * pad,GstObject * object,GstQuery * query)534 gst_alpha_combine_sink_query (GstPad * pad, GstObject * object,
535     GstQuery * query)
536 {
537   switch (query->type) {
538     case GST_QUERY_ALLOCATION:
539     {
540       int i;
541 
542       if (!gst_pad_query_default (pad, object, query))
543         return FALSE;
544 
545       /* Ensure NULL pool because it cannot be shared between the 2 decoders.
546        * Ideally, we should cache the downstream query and use it for both
547        * decoders, but it is hard to know when we should refresh it */
548       for (i = 0; i < gst_query_get_n_allocation_pools (query); i++) {
549         guint size = 0, min = 0, max = 0;
550         gst_query_parse_nth_allocation_pool (query, i, NULL, &size, &min, &max);
551         gst_query_set_nth_allocation_pool (query, i, NULL, size, min, max);
552       }
553 
554       return TRUE;
555       break;
556     }
557     default:
558       break;
559   }
560 
561   return gst_pad_query_default (pad, object, query);
562 }
563 
564 static GstStateChangeReturn
gst_alpha_combine_change_state(GstElement * element,GstStateChange transition)565 gst_alpha_combine_change_state (GstElement * element, GstStateChange transition)
566 {
567   GstAlphaCombine *self = GST_ALPHA_COMBINE (element);
568   GstStateChangeReturn ret;
569 
570   switch (transition) {
571     case GST_STATE_CHANGE_READY_TO_PAUSED:
572       gst_alpha_combine_unlock_stop (self);
573       break;
574     case GST_STATE_CHANGE_PAUSED_TO_READY:
575       gst_alpha_combine_unlock (self);
576       break;
577     default:
578       break;
579   }
580 
581   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
582 
583   switch (transition) {
584     case GST_STATE_CHANGE_PAUSED_TO_READY:
585       gst_alpha_combine_reset (self);
586       self->src_format = GST_VIDEO_FORMAT_UNKNOWN;
587       gst_video_info_init (&self->sink_vinfo);
588       gst_video_info_init (&self->alpha_vinfo);
589       self->sink_format_cookie = 0;
590       self->alpha_format_cookie = 0;
591       break;
592     default:
593       break;
594   }
595 
596   return ret;
597 }
598 
599 static void
gst_alpha_combine_dispose(GObject * object)600 gst_alpha_combine_dispose (GObject * object)
601 {
602   GstAlphaCombine *self = GST_ALPHA_COMBINE (object);
603 
604   g_clear_object (&self->sink_pad);
605   g_clear_object (&self->alpha_pad);
606   g_clear_object (&self->src_pad);
607 
608   G_OBJECT_CLASS (parent_class)->dispose (object);
609 }
610 
611 static void
gst_alpha_combine_finalize(GObject * object)612 gst_alpha_combine_finalize (GObject * object)
613 {
614   GstAlphaCombine *self = GST_ALPHA_COMBINE (object);
615 
616   g_mutex_clear (&self->buffer_lock);
617   g_cond_clear (&self->buffer_cond);
618 
619   G_OBJECT_CLASS (parent_class)->finalize (object);
620 }
621 
622 static void
gst_alpha_combine_class_init(GstAlphaCombineClass * klass)623 gst_alpha_combine_class_init (GstAlphaCombineClass * klass)
624 {
625   GstElementClass *element_class = (GstElementClass *) klass;
626   GObjectClass *object_class = (GObjectClass *) klass;
627 
628   gst_element_class_set_static_metadata (element_class,
629       "Alpha Combiner", "Codec/Demuxer",
630       "Use luma from an opaque stream as alpha plane on another",
631       "Nicolas Dufresne <nicolas.dufresne@collabora.com>");
632 
633   gst_element_class_add_static_pad_template (element_class,
634       &gst_alpha_combine_sink_template);
635   gst_element_class_add_static_pad_template (element_class,
636       &gst_alpha_combine_alpha_template);
637   gst_element_class_add_static_pad_template (element_class,
638       &gst_alpha_combine_src_template);
639 
640   element_class->change_state =
641       GST_DEBUG_FUNCPTR (gst_alpha_combine_change_state);
642 
643   object_class->dispose = GST_DEBUG_FUNCPTR (gst_alpha_combine_dispose);
644   object_class->finalize = GST_DEBUG_FUNCPTR (gst_alpha_combine_finalize);
645 }
646 
647 static void
gst_alpha_combine_init(GstAlphaCombine * self)648 gst_alpha_combine_init (GstAlphaCombine * self)
649 {
650   gst_element_create_all_pads (GST_ELEMENT (self));
651   self->sink_pad = gst_element_get_static_pad (GST_ELEMENT (self), "sink");
652   self->alpha_pad = gst_element_get_static_pad (GST_ELEMENT (self), "alpha");
653   self->src_pad = gst_element_get_static_pad (GST_ELEMENT (self), "src");
654   self->flushing = 1;
655 
656   g_mutex_init (&self->buffer_lock);
657   g_cond_init (&self->buffer_cond);
658 
659   GST_PAD_SET_PROXY_SCHEDULING (self->sink_pad);
660   GST_PAD_SET_PROXY_SCHEDULING (self->src_pad);
661 
662   GST_PAD_SET_PROXY_ALLOCATION (self->sink_pad);
663   GST_PAD_SET_PROXY_ALLOCATION (self->alpha_pad);
664 
665   gst_pad_set_chain_function (self->sink_pad, gst_alpha_combine_sink_chain);
666   gst_pad_set_chain_function (self->alpha_pad, gst_alpha_combine_alpha_chain);
667 
668   gst_pad_set_event_function (self->sink_pad, gst_alpha_combine_sink_event);
669   gst_pad_set_event_function (self->alpha_pad, gst_alpha_combine_alpha_event);
670 
671   gst_pad_set_query_function (self->sink_pad, gst_alpha_combine_sink_query);
672   gst_pad_set_query_function (self->alpha_pad, gst_alpha_combine_sink_query);
673 }
674