• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params);
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