1 /* GStreamer
2 * Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 /**
21 * SECTION:element-overlaycomposition
22 *
23 * The overlaycomposition element renders an overlay using an application
24 * provided draw function.
25 *
26 * ## Example code
27 *
28 * {{ ../../tests/examples/overlaycomposition/overlaycomposition.c[23:341] }}
29 */
30
31 #if HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <string.h>
36
37 #include "gstoverlaycomposition.h"
38
39 GST_DEBUG_CATEGORY_STATIC (gst_overlay_composition_debug);
40 #define GST_CAT_DEFAULT gst_overlay_composition_debug
41
42 #define OVERLAY_COMPOSITION_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS)
43
44 #define ALL_CAPS OVERLAY_COMPOSITION_CAPS ";" \
45 GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
46
47 enum
48 {
49 SIGNAL_CAPS_CHANGED,
50 SIGNAL_DRAW,
51 LAST_SIGNAL
52 };
53
54 static guint overlay_composition_signals[LAST_SIGNAL];
55
56 static GstStaticCaps overlay_composition_caps =
57 GST_STATIC_CAPS (OVERLAY_COMPOSITION_CAPS);
58
59 static gboolean
can_blend_caps(GstCaps * incaps)60 can_blend_caps (GstCaps * incaps)
61 {
62 gboolean ret;
63 GstCaps *caps;
64
65 caps = gst_static_caps_get (&overlay_composition_caps);
66 ret = gst_caps_is_subset (incaps, caps);
67 gst_caps_unref (caps);
68
69 return ret;
70 }
71
72 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
73 GST_PAD_SRC,
74 GST_PAD_ALWAYS,
75 GST_STATIC_CAPS (ALL_CAPS)
76 );
77
78 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
79 GST_PAD_SINK,
80 GST_PAD_ALWAYS,
81 GST_STATIC_CAPS (ALL_CAPS)
82 );
83
84 #define parent_class gst_overlay_composition_parent_class
85 G_DEFINE_TYPE (GstOverlayComposition, gst_overlay_composition,
86 GST_TYPE_ELEMENT);
87 GST_ELEMENT_REGISTER_DEFINE (overlaycomposition, "overlaycomposition",
88 GST_RANK_NONE, GST_TYPE_OVERLAY_COMPOSITION);
89
90 static GstFlowReturn gst_overlay_composition_sink_chain (GstPad * pad,
91 GstObject * parent, GstBuffer * buffer);
92 static gboolean gst_overlay_composition_sink_event (GstPad * pad,
93 GstObject * parent, GstEvent * event);
94 static gboolean gst_overlay_composition_sink_query (GstPad * pad,
95 GstObject * parent, GstQuery * query);
96 static gboolean gst_overlay_composition_src_query (GstPad * pad,
97 GstObject * parent, GstQuery * query);
98
99 static GstStateChangeReturn gst_overlay_composition_change_state (GstElement *
100 element, GstStateChange transition);
101
102 static void
gst_overlay_composition_class_init(GstOverlayCompositionClass * klass)103 gst_overlay_composition_class_init (GstOverlayCompositionClass * klass)
104 {
105 GstElementClass *gstelement_class = (GstElementClass *) klass;
106
107 GST_DEBUG_CATEGORY_INIT (gst_overlay_composition_debug, "overlaycomposition",
108 0, "Overlay Composition");
109
110 gst_element_class_set_static_metadata (gstelement_class,
111 "Overlay Composition", "Filter/Editor/Video",
112 "Overlay Composition", "Sebastian Dröge <sebastian@centricular.com>");
113
114 gst_element_class_add_pad_template (gstelement_class,
115 gst_static_pad_template_get (&src_template));
116 gst_element_class_add_pad_template (gstelement_class,
117 gst_static_pad_template_get (&sink_template));
118
119 gstelement_class->change_state = gst_overlay_composition_change_state;
120
121 /**
122 * GstOverlayComposition::draw:
123 * @overlay: Overlay element emitting the signal.
124 * @sample: #GstSample containing the current buffer, caps and segment.
125 *
126 * This signal is emitted when the overlay should be drawn.
127 *
128 * Returns: #GstVideoOverlayComposition or %NULL
129 */
130 overlay_composition_signals[SIGNAL_DRAW] =
131 g_signal_new ("draw",
132 G_TYPE_FROM_CLASS (klass), 0, 0, NULL, NULL, NULL,
133 GST_TYPE_VIDEO_OVERLAY_COMPOSITION, 1, GST_TYPE_SAMPLE);
134
135 /**
136 * GstOverlayComposition::caps-changed:
137 * @overlay: Overlay element emitting the signal.
138 * @caps: The #GstCaps of the element.
139 * @window_width: The window render width of downstream, or 0.
140 * @window_height: The window render height of downstream, or 0.
141 *
142 * This signal is emitted when the caps of the element has changed.
143 *
144 * The window width and height define the resolution at which the frame is
145 * going to be rendered in the end by e.g. a video sink (i.e. the window
146 * size).
147 */
148 overlay_composition_signals[SIGNAL_CAPS_CHANGED] =
149 g_signal_new ("caps-changed",
150 G_TYPE_FROM_CLASS (klass), 0, 0, NULL, NULL, NULL, G_TYPE_NONE, 3,
151 GST_TYPE_CAPS, G_TYPE_UINT, G_TYPE_UINT);
152 }
153
154 static void
gst_overlay_composition_init(GstOverlayComposition * self)155 gst_overlay_composition_init (GstOverlayComposition * self)
156 {
157 self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
158 gst_pad_set_chain_function (self->sinkpad,
159 GST_DEBUG_FUNCPTR (gst_overlay_composition_sink_chain));
160 gst_pad_set_event_function (self->sinkpad,
161 GST_DEBUG_FUNCPTR (gst_overlay_composition_sink_event));
162 gst_pad_set_query_function (self->sinkpad,
163 GST_DEBUG_FUNCPTR (gst_overlay_composition_sink_query));
164 GST_PAD_SET_PROXY_ALLOCATION (self->sinkpad);
165 gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
166
167 self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
168 gst_pad_set_query_function (self->srcpad,
169 GST_DEBUG_FUNCPTR (gst_overlay_composition_src_query));
170 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
171 }
172
173 static GstStateChangeReturn
gst_overlay_composition_change_state(GstElement * element,GstStateChange transition)174 gst_overlay_composition_change_state (GstElement * element,
175 GstStateChange transition)
176 {
177 GstOverlayComposition *self = GST_OVERLAY_COMPOSITION (element);
178 GstStateChangeReturn state_ret;
179
180 switch (transition) {
181 default:
182 break;
183 }
184
185 state_ret =
186 GST_ELEMENT_CLASS (gst_overlay_composition_parent_class)->change_state
187 (element, transition);
188 if (state_ret == GST_STATE_CHANGE_FAILURE)
189 return state_ret;
190
191 switch (transition) {
192 case GST_STATE_CHANGE_READY_TO_PAUSED:
193 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
194 break;
195 case GST_STATE_CHANGE_PAUSED_TO_READY:
196 memset (&self->info, 0, sizeof (self->info));
197 self->window_width = self->window_height = 0;
198 self->attach_compo_to_buffer = FALSE;
199 if (self->sample) {
200 gst_sample_unref (self->sample);
201 self->sample = NULL;
202 }
203 gst_caps_replace (&self->caps, NULL);
204 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
205 break;
206 default:
207 break;
208 }
209
210 return state_ret;
211 }
212
213 /* Based on gstbasetextoverlay.c */
214 static gboolean
gst_overlay_composition_negotiate(GstOverlayComposition * self,GstCaps * caps)215 gst_overlay_composition_negotiate (GstOverlayComposition * self, GstCaps * caps)
216 {
217 gboolean upstream_has_meta = FALSE;
218 gboolean caps_has_meta = FALSE;
219 gboolean alloc_has_meta = FALSE;
220 gboolean attach = FALSE;
221 gboolean ret = TRUE;
222 guint width, height;
223 GstCapsFeatures *f;
224 GstCaps *overlay_caps;
225 GstQuery *query;
226 guint alloc_index;
227
228 GST_DEBUG_OBJECT (self, "performing negotiation");
229
230 /* Clear any pending reconfigure to avoid negotiating twice */
231 gst_pad_check_reconfigure (self->srcpad);
232
233 self->window_width = self->window_height = 0;
234
235 if (!caps)
236 caps = gst_pad_get_current_caps (self->sinkpad);
237 else
238 gst_caps_ref (caps);
239
240 if (!caps || gst_caps_is_empty (caps))
241 goto no_format;
242
243 /* Check if upstream caps have meta */
244 if ((f = gst_caps_get_features (caps, 0))) {
245 upstream_has_meta = gst_caps_features_contains (f,
246 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
247 }
248
249 /* Initialize dimensions */
250 width = self->info.width;
251 height = self->info.height;
252
253 if (upstream_has_meta) {
254 overlay_caps = gst_caps_ref (caps);
255 } else {
256 GstCaps *peercaps;
257
258 /* BaseTransform requires caps for the allocation query to work */
259 overlay_caps = gst_caps_copy (caps);
260 f = gst_caps_get_features (overlay_caps, 0);
261 gst_caps_features_add (f,
262 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
263
264 /* Then check if downstream accept overlay composition in caps */
265 /* FIXME: We should probably check if downstream *prefers* the
266 * overlay meta, and only enforce usage of it if we can't handle
267 * the format ourselves and thus would have to drop the overlays.
268 * Otherwise we should prefer what downstream wants here.
269 */
270 peercaps = gst_pad_peer_query_caps (self->srcpad, overlay_caps);
271 caps_has_meta = !gst_caps_is_empty (peercaps);
272 gst_caps_unref (peercaps);
273
274 GST_DEBUG_OBJECT (self, "caps have overlay meta %d", caps_has_meta);
275 }
276
277 if (upstream_has_meta || caps_has_meta) {
278 /* Send caps immediately, it's needed by GstBaseTransform to get a reply
279 * from allocation query */
280 ret = gst_pad_set_caps (self->srcpad, overlay_caps);
281
282 /* First check if the allocation meta has compositon */
283 query = gst_query_new_allocation (overlay_caps, FALSE);
284
285 if (!gst_pad_peer_query (self->srcpad, query)) {
286 /* no problem, we use the query defaults */
287 GST_DEBUG_OBJECT (self, "ALLOCATION query failed");
288
289 /* In case we were flushing, mark reconfigure and fail this method,
290 * will make it retry */
291 if (GST_PAD_IS_FLUSHING (self->srcpad))
292 ret = FALSE;
293 }
294
295 alloc_has_meta = gst_query_find_allocation_meta (query,
296 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, &alloc_index);
297
298 GST_DEBUG_OBJECT (self, "sink alloc has overlay meta %d", alloc_has_meta);
299
300 if (alloc_has_meta) {
301 const GstStructure *params;
302
303 gst_query_parse_nth_allocation_meta (query, alloc_index, ¶ms);
304 if (params) {
305 if (gst_structure_get (params, "width", G_TYPE_UINT, &width,
306 "height", G_TYPE_UINT, &height, NULL)) {
307 GST_DEBUG_OBJECT (self, "received window size: %dx%d", width, height);
308 g_assert (width != 0 && height != 0);
309 }
310 }
311 }
312
313 gst_query_unref (query);
314 }
315
316 /* Update render size if needed */
317 self->window_width = width;
318 self->window_height = height;
319
320 /* For backward compatibility, we will prefer blitting if downstream
321 * allocation does not support the meta. In other case we will prefer
322 * attaching, and will fail the negotiation in the unlikely case we are
323 * force to blit, but format isn't supported. */
324
325 if (upstream_has_meta) {
326 attach = TRUE;
327 } else if (caps_has_meta) {
328 if (alloc_has_meta) {
329 attach = TRUE;
330 } else {
331 /* Don't attach unless we cannot handle the format */
332 attach = !can_blend_caps (caps);
333 }
334 } else {
335 ret = can_blend_caps (caps);
336 }
337
338 /* If we attach, then pick the overlay caps */
339 if (attach) {
340 GST_DEBUG_OBJECT (self, "Using caps %" GST_PTR_FORMAT, overlay_caps);
341 /* Caps where already sent */
342 } else if (ret) {
343 GST_DEBUG_OBJECT (self, "Using caps %" GST_PTR_FORMAT, caps);
344 ret = gst_pad_set_caps (self->srcpad, caps);
345 }
346
347 self->attach_compo_to_buffer = attach;
348
349 if (!ret) {
350 GST_DEBUG_OBJECT (self, "negotiation failed, schedule reconfigure");
351 gst_pad_mark_reconfigure (self->srcpad);
352 }
353
354 g_signal_emit (self, overlay_composition_signals[SIGNAL_CAPS_CHANGED], 0,
355 caps, self->window_width, self->window_height, NULL);
356
357 gst_caps_unref (overlay_caps);
358 gst_caps_unref (caps);
359
360 return ret;
361
362 no_format:
363 {
364 if (caps)
365 gst_caps_unref (caps);
366 gst_pad_mark_reconfigure (self->srcpad);
367 return FALSE;
368 }
369 }
370
371 static gboolean
gst_overlay_composition_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)372 gst_overlay_composition_sink_event (GstPad * pad, GstObject * parent,
373 GstEvent * event)
374 {
375 GstOverlayComposition *self = GST_OVERLAY_COMPOSITION (parent);
376 gboolean ret = FALSE;
377
378 switch (GST_EVENT_TYPE (event)) {
379 case GST_EVENT_SEGMENT:
380 gst_event_copy_segment (event, &self->segment);
381 ret = gst_pad_event_default (pad, parent, event);
382 break;
383 case GST_EVENT_CAPS:{
384 GstCaps *caps;
385
386 gst_event_parse_caps (event, &caps);
387 if (!gst_video_info_from_caps (&self->info, caps)) {
388 gst_event_unref (event);
389 ret = FALSE;
390 break;
391 }
392
393 if (!gst_overlay_composition_negotiate (self, caps)) {
394 gst_event_unref (event);
395 ret = FALSE;
396 break;
397 }
398
399 gst_caps_replace (&self->caps, caps);
400
401 ret = TRUE;
402 gst_event_unref (event);
403
404 break;
405 }
406 case GST_EVENT_FLUSH_STOP:
407 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
408 ret = gst_pad_event_default (pad, parent, event);
409 break;
410 default:
411 ret = gst_pad_event_default (pad, parent, event);
412 break;
413 }
414
415 return ret;
416 }
417
418 /* Based on gstbasetextoverlay.c */
419 /**
420 * add_feature_and_intersect:
421 *
422 * Creates a new #GstCaps containing the (given caps +
423 * given caps feature) + (given caps intersected by the
424 * given filter).
425 *
426 * Returns: the new #GstCaps
427 */
428 static GstCaps *
add_feature_and_intersect(GstCaps * caps,const gchar * feature,GstCaps * filter)429 add_feature_and_intersect (GstCaps * caps,
430 const gchar * feature, GstCaps * filter)
431 {
432 int i, caps_size;
433 GstCaps *new_caps;
434
435 new_caps = gst_caps_copy (caps);
436
437 caps_size = gst_caps_get_size (new_caps);
438 for (i = 0; i < caps_size; i++) {
439 GstCapsFeatures *features = gst_caps_get_features (new_caps, i);
440
441 if (!gst_caps_features_is_any (features)) {
442 gst_caps_features_add (features, feature);
443 }
444 }
445
446 gst_caps_append (new_caps, gst_caps_intersect_full (caps,
447 filter, GST_CAPS_INTERSECT_FIRST));
448
449 return new_caps;
450 }
451
452 /* Based on gstbasetextoverlay.c */
453 /* intersect_by_feature:
454 *
455 * Creates a new #GstCaps based on the following filtering rule.
456 *
457 * For each individual caps contained in given caps, if the
458 * caps uses the given caps feature, keep a version of the caps
459 * with the feature and an another one without. Otherwise, intersect
460 * the caps with the given filter.
461 *
462 * Returns: the new #GstCaps
463 */
464 static GstCaps *
intersect_by_feature(GstCaps * caps,const gchar * feature,GstCaps * filter)465 intersect_by_feature (GstCaps * caps, const gchar * feature, GstCaps * filter)
466 {
467 int i, caps_size;
468 GstCaps *new_caps;
469
470 new_caps = gst_caps_new_empty ();
471
472 caps_size = gst_caps_get_size (caps);
473 for (i = 0; i < caps_size; i++) {
474 GstStructure *caps_structure = gst_caps_get_structure (caps, i);
475 GstCapsFeatures *caps_features =
476 gst_caps_features_copy (gst_caps_get_features (caps, i));
477 GstCaps *filtered_caps;
478 GstCaps *simple_caps =
479 gst_caps_new_full (gst_structure_copy (caps_structure), NULL);
480 gst_caps_set_features (simple_caps, 0, caps_features);
481
482 if (gst_caps_features_contains (caps_features, feature)) {
483 gst_caps_append (new_caps, gst_caps_copy (simple_caps));
484
485 gst_caps_features_remove (caps_features, feature);
486 filtered_caps = gst_caps_ref (simple_caps);
487 } else {
488 filtered_caps = gst_caps_intersect_full (simple_caps, filter,
489 GST_CAPS_INTERSECT_FIRST);
490 }
491
492 gst_caps_unref (simple_caps);
493 gst_caps_append (new_caps, filtered_caps);
494 }
495
496 return new_caps;
497 }
498
499 /* Based on gstbasetextoverlay.c */
500 static GstCaps *
gst_overlay_composition_sink_query_caps(GstOverlayComposition * self,GstCaps * filter)501 gst_overlay_composition_sink_query_caps (GstOverlayComposition * self,
502 GstCaps * filter)
503 {
504 GstCaps *peer_caps = NULL, *caps = NULL, *overlay_filter = NULL;
505
506 if (filter) {
507 /* filter caps + composition feature + filter caps
508 * filtered by the software caps. */
509 GstCaps *sw_caps = gst_static_caps_get (&overlay_composition_caps);
510 overlay_filter = add_feature_and_intersect (filter,
511 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
512 gst_caps_unref (sw_caps);
513
514 GST_DEBUG_OBJECT (self->sinkpad, "overlay filter %" GST_PTR_FORMAT,
515 overlay_filter);
516 }
517
518 peer_caps = gst_pad_peer_query_caps (self->srcpad, overlay_filter);
519
520 if (overlay_filter)
521 gst_caps_unref (overlay_filter);
522
523 if (peer_caps) {
524
525 GST_DEBUG_OBJECT (self->sinkpad, "peer caps %" GST_PTR_FORMAT, peer_caps);
526
527 if (gst_caps_is_any (peer_caps)) {
528 /* if peer returns ANY caps, return filtered src pad template caps */
529 caps = gst_caps_copy (gst_pad_get_pad_template_caps (self->srcpad));
530 } else {
531
532 /* duplicate caps which contains the composition into one version with
533 * the meta and one without. Filter the other caps by the software caps */
534 GstCaps *sw_caps = gst_static_caps_get (&overlay_composition_caps);
535 caps = intersect_by_feature (peer_caps,
536 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
537 gst_caps_unref (sw_caps);
538 }
539
540 gst_caps_unref (peer_caps);
541
542 } else {
543 /* no peer, our padtemplate is enough then */
544 caps = gst_pad_get_pad_template_caps (self->sinkpad);
545 }
546
547 if (filter) {
548 GstCaps *intersection = gst_caps_intersect_full (filter, caps,
549 GST_CAPS_INTERSECT_FIRST);
550 gst_caps_unref (caps);
551 caps = intersection;
552 }
553
554 GST_DEBUG_OBJECT (self->sinkpad, "returning %" GST_PTR_FORMAT, caps);
555
556 return caps;
557 }
558
559 /* Based on gstbasetextoverlay.c */
560 static GstCaps *
gst_overlay_composition_src_query_caps(GstOverlayComposition * self,GstCaps * filter)561 gst_overlay_composition_src_query_caps (GstOverlayComposition * self,
562 GstCaps * filter)
563 {
564 GstCaps *peer_caps = NULL, *caps = NULL, *overlay_filter = NULL;
565
566 if (filter) {
567 /* duplicate filter caps which contains the composition into one version
568 * with the meta and one without. Filter the other caps by the software
569 * caps */
570 GstCaps *sw_caps = gst_static_caps_get (&overlay_composition_caps);
571 overlay_filter =
572 intersect_by_feature (filter,
573 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
574 gst_caps_unref (sw_caps);
575 }
576
577 peer_caps = gst_pad_peer_query_caps (self->sinkpad, overlay_filter);
578
579 if (overlay_filter)
580 gst_caps_unref (overlay_filter);
581
582 if (peer_caps) {
583
584 GST_DEBUG_OBJECT (self->srcpad, "peer caps %" GST_PTR_FORMAT, peer_caps);
585
586 if (gst_caps_is_any (peer_caps)) {
587
588 /* if peer returns ANY caps, return filtered sink pad template caps */
589 caps = gst_caps_copy (gst_pad_get_pad_template_caps (self->sinkpad));
590
591 } else {
592
593 /* return upstream caps + composition feature + upstream caps
594 * filtered by the software caps. */
595 GstCaps *sw_caps = gst_static_caps_get (&overlay_composition_caps);
596 caps = add_feature_and_intersect (peer_caps,
597 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
598 gst_caps_unref (sw_caps);
599 }
600
601 gst_caps_unref (peer_caps);
602
603 } else {
604 /* no peer, our padtemplate is enough then */
605 caps = gst_pad_get_pad_template_caps (self->srcpad);
606 }
607
608 if (filter) {
609 GstCaps *intersection;
610
611 intersection =
612 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
613 gst_caps_unref (caps);
614 caps = intersection;
615 }
616 GST_DEBUG_OBJECT (self->srcpad, "returning %" GST_PTR_FORMAT, caps);
617
618 return caps;
619 }
620
621 static gboolean
gst_overlay_composition_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)622 gst_overlay_composition_sink_query (GstPad * pad, GstObject * parent,
623 GstQuery * query)
624 {
625 GstOverlayComposition *self = GST_OVERLAY_COMPOSITION (parent);
626 gboolean ret = FALSE;
627
628 switch (GST_QUERY_TYPE (query)) {
629 case GST_QUERY_CAPS:{
630 GstCaps *filter, *caps;
631
632 gst_query_parse_caps (query, &filter);
633 caps = gst_overlay_composition_sink_query_caps (self, filter);
634 gst_query_set_caps_result (query, caps);
635 gst_caps_unref (caps);
636 ret = TRUE;
637 break;
638 }
639 default:
640 ret = gst_pad_query_default (pad, parent, query);
641 break;
642 }
643
644 return ret;
645 }
646
647 static gboolean
gst_overlay_composition_src_query(GstPad * pad,GstObject * parent,GstQuery * query)648 gst_overlay_composition_src_query (GstPad * pad, GstObject * parent,
649 GstQuery * query)
650 {
651 GstOverlayComposition *self = GST_OVERLAY_COMPOSITION (parent);
652 gboolean ret = FALSE;
653
654 switch (GST_QUERY_TYPE (query)) {
655 case GST_QUERY_CAPS:{
656 GstCaps *filter, *caps;
657
658 gst_query_parse_caps (query, &filter);
659 caps = gst_overlay_composition_src_query_caps (self, filter);
660 gst_query_set_caps_result (query, caps);
661 gst_caps_unref (caps);
662 ret = TRUE;
663 break;
664 }
665 default:
666 ret = gst_pad_query_default (pad, parent, query);
667 break;
668 }
669
670 return ret;
671 }
672
673 static GstFlowReturn
gst_overlay_composition_sink_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)674 gst_overlay_composition_sink_chain (GstPad * pad, GstObject * parent,
675 GstBuffer * buffer)
676 {
677 GstOverlayComposition *self = GST_OVERLAY_COMPOSITION (parent);
678 GstVideoOverlayComposition *compo = NULL;
679 GstVideoOverlayCompositionMeta *upstream_compo_meta;
680
681 if (gst_pad_check_reconfigure (self->srcpad)) {
682 if (!gst_overlay_composition_negotiate (self, NULL)) {
683 gst_pad_mark_reconfigure (self->srcpad);
684 gst_buffer_unref (buffer);
685 GST_OBJECT_LOCK (self->srcpad);
686 if (GST_PAD_IS_FLUSHING (self->srcpad)) {
687 GST_OBJECT_UNLOCK (self->srcpad);
688 return GST_FLOW_FLUSHING;
689 }
690 GST_OBJECT_UNLOCK (self->srcpad);
691 return GST_FLOW_NOT_NEGOTIATED;
692 }
693 }
694
695 if (!self->sample) {
696 self->sample = gst_sample_new (buffer, self->caps, &self->segment, NULL);
697 } else {
698 self->sample = gst_sample_make_writable (self->sample);
699 gst_sample_set_buffer (self->sample, buffer);
700 gst_sample_set_caps (self->sample, self->caps);
701 gst_sample_set_segment (self->sample, &self->segment);
702 }
703
704 g_signal_emit (self, overlay_composition_signals[SIGNAL_DRAW], 0,
705 self->sample, &compo);
706
707 /* Don't store the buffer in the sample any longer, otherwise it will not
708 * be writable below as we have one reference in the sample and one in
709 * this function.
710 *
711 * If the sample is not writable itself then the application kept an
712 * reference itself.
713 */
714 if (gst_sample_is_writable (self->sample)) {
715 gst_sample_set_buffer (self->sample, NULL);
716 }
717
718 if (!compo) {
719 GST_DEBUG_OBJECT (self->sinkpad,
720 "Application did not provide an overlay composition");
721 return gst_pad_push (self->srcpad, buffer);
722 }
723
724 /* If upstream attached a meta, we can safely add our own things
725 * in it. Upstream must've checked that downstream supports it */
726 upstream_compo_meta = gst_buffer_get_video_overlay_composition_meta (buffer);
727 if (upstream_compo_meta) {
728 GstVideoOverlayComposition *merged_compo =
729 gst_video_overlay_composition_copy (upstream_compo_meta->overlay);
730 guint i, n;
731
732 GST_DEBUG_OBJECT (self->sinkpad,
733 "Appending to upstream overlay composition");
734
735 n = gst_video_overlay_composition_n_rectangles (compo);
736 for (i = 0; i < n; i++) {
737 GstVideoOverlayRectangle *rect =
738 gst_video_overlay_composition_get_rectangle (compo, i);
739 gst_video_overlay_composition_add_rectangle (merged_compo, rect);
740 }
741
742 gst_video_overlay_composition_unref (compo);
743 gst_video_overlay_composition_unref (upstream_compo_meta->overlay);
744 upstream_compo_meta->overlay = merged_compo;
745 } else if (self->attach_compo_to_buffer) {
746 GST_DEBUG_OBJECT (self->sinkpad, "Attaching as meta");
747
748 buffer = gst_buffer_make_writable (buffer);
749 gst_buffer_add_video_overlay_composition_meta (buffer, compo);
750 gst_video_overlay_composition_unref (compo);
751 } else {
752 GstVideoFrame frame;
753
754 buffer = gst_buffer_make_writable (buffer);
755 if (!gst_video_frame_map (&frame, &self->info, buffer, GST_MAP_READWRITE)) {
756 gst_video_overlay_composition_unref (compo);
757 goto map_failed;
758 }
759
760 gst_video_overlay_composition_blend (compo, &frame);
761
762 gst_video_frame_unmap (&frame);
763 gst_video_overlay_composition_unref (compo);
764 }
765
766 return gst_pad_push (self->srcpad, buffer);
767
768 map_failed:
769 {
770 GST_ERROR_OBJECT (self->sinkpad, "Failed to map buffer");
771 gst_buffer_unref (buffer);
772 return GST_FLOW_ERROR;
773 }
774 }
775
776 static gboolean
plugin_init(GstPlugin * plugin)777 plugin_init (GstPlugin * plugin)
778 {
779 return GST_ELEMENT_REGISTER (overlaycomposition, plugin);
780 }
781
782 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
783 GST_VERSION_MINOR,
784 overlaycomposition,
785 "Renders overlays on top of video frames",
786 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
787