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