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