1 /* GStreamer Video Overlay Composition
2  * Copyright (C) 2011 Intel Corporation
3  * Copyright (C) 2011 Collabora Ltd.
4  * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:gstvideooverlaycomposition
24  * @title: GstVideoOverlayRectangle
25  * @short_description: Video Buffer Overlay Compositions (Subtitles, Logos)
26  *
27  * Functions to create and handle overlay compositions on video buffers.
28  *
29  * An overlay composition describes one or more overlay rectangles to be
30  * blended on top of a video buffer.
31  *
32  * This API serves two main purposes:
33  *
34  * * it can be used to attach overlay information (subtitles or logos)
35  *   to non-raw video buffers such as GL/VAAPI/VDPAU surfaces. The actual
36  *   blending of the overlay can then be done by e.g. the video sink that
37  *   processes these non-raw buffers.
38  *
39  * * it can also be used to blend overlay rectangles on top of raw video
40  *   buffers, thus consolidating blending functionality for raw video in
41  *   one place.
42  *
43  * Together, this allows existing overlay elements to easily handle raw
44  * and non-raw video as input in without major changes (once the overlays
45  * have been put into a #GstVideoOverlayComposition object anyway) - for raw
46  * video the overlay can just use the blending function to blend the data
47  * on top of the video, and for surface buffers it can just attach them to
48  * the buffer and let the sink render the overlays.
49  *
50  */
51 
52 /* TODO:
53  *  - provide accessors for seq_num and other fields (as needed)
54  *  - allow overlay to set/get original pango markup string on/from rectangle
55  */
56 
57 #ifdef HAVE_CONFIG_H
58 #include "config.h"
59 #endif
60 
61 #include "video-overlay-composition.h"
62 #include "video-blend.h"
63 #include "gstvideometa.h"
64 #include <string.h>
65 
66 struct _GstVideoOverlayComposition
67 {
68   GstMiniObject parent;
69 
70   guint num_rectangles;
71   GstVideoOverlayRectangle **rectangles;
72 
73   /* lowest rectangle sequence number still used by the upstream
74    * overlay element. This way a renderer maintaining some kind of
75    * rectangles <-> surface cache can know when to free cached
76    * surfaces/rectangles. */
77   guint min_seq_num_used;
78 
79   /* sequence number for the composition (same series as rectangles) */
80   guint seq_num;
81 };
82 
83 struct _GstVideoOverlayRectangle
84 {
85   GstMiniObject parent;
86 
87   /* Position on video frame and dimension of output rectangle in
88    * output frame terms (already adjusted for the PAR of the output
89    * frame). x/y can be negative (overlay will be clipped then) */
90   gint x, y;
91   guint render_width, render_height;
92 
93   /* Info on overlay pixels (format, width, height) */
94   GstVideoInfo info;
95 
96   /* The flags associated to this rectangle */
97   GstVideoOverlayFormatFlags flags;
98 
99   /* Refcounted blob of memory, no caps or timestamps */
100   GstBuffer *pixels;
101 
102   /* FIXME: how to express source like text or pango markup?
103    *        (just add source type enum + source buffer with data)
104    *
105    * FOR 0.10: always send pixel blobs, but attach source data in
106    * addition (reason: if downstream changes, we can't renegotiate
107    * that properly, if we just do a query of supported formats from
108    * the start). Sink will just ignore pixels and use pango markup
109    * from source data if it supports that.
110    *
111    * FOR 0.11: overlay should query formats (pango markup, pixels)
112    * supported by downstream and then only send that. We can
113    * renegotiate via the reconfigure event.
114    */
115 
116   /* sequence number: useful for backends/renderers/sinks that want
117    * to maintain a cache of rectangles <-> surfaces. The value of
118    * the min_seq_num_used in the composition tells the renderer which
119    * rectangles have expired. */
120   guint seq_num;
121 
122   /* global alpha: global alpha value of the rectangle. Each each per-pixel
123    * alpha value of image-data will be multiplied with the global alpha value
124    * during blending.
125    * Can be used for efficient fading in/out of overlay rectangles.
126    * GstElements that render OverlayCompositions and don't support global alpha
127    * should simply ignore it.*/
128   gfloat global_alpha;
129 
130   /* track alpha-values already applied: */
131   gfloat applied_global_alpha;
132   /* store initial per-pixel alpha values: */
133   guint8 *initial_alpha;
134 
135   /* FIXME: we may also need a (private) way to cache converted/scaled
136    * pixel blobs */
137   GMutex lock;
138 
139   GList *scaled_rectangles;
140 };
141 
142 #define GST_RECTANGLE_LOCK(rect)   g_mutex_lock(&rect->lock)
143 #define GST_RECTANGLE_UNLOCK(rect) g_mutex_unlock(&rect->lock)
144 
145 /* --------------------------- utility functions --------------------------- */
146 
147 #ifndef GST_DISABLE_GST_DEBUG
148 
149 #define GST_CAT_DEFAULT ensure_debug_category()
150 
151 static GstDebugCategory *
ensure_debug_category(void)152 ensure_debug_category (void)
153 {
154   static gsize cat_gonce = 0;
155 
156   if (g_once_init_enter (&cat_gonce)) {
157     gsize cat_done;
158 
159     cat_done = (gsize) _gst_debug_category_new ("video-composition", 0,
160         "video overlay composition");
161 
162     g_once_init_leave (&cat_gonce, cat_done);
163   }
164 
165   return (GstDebugCategory *) cat_gonce;
166 }
167 
168 #else
169 
170 #define ensure_debug_category() /* NOOP */
171 
172 #endif /* GST_DISABLE_GST_DEBUG */
173 
174 static guint
gst_video_overlay_get_seqnum(void)175 gst_video_overlay_get_seqnum (void)
176 {
177   static gint seqnum;           /* 0 */
178 
179   return (guint) g_atomic_int_add (&seqnum, 1);
180 }
181 
182 static gboolean
gst_video_overlay_composition_meta_init(GstMeta * meta,gpointer params,GstBuffer * buf)183 gst_video_overlay_composition_meta_init (GstMeta * meta, gpointer params,
184     GstBuffer * buf)
185 {
186   GstVideoOverlayCompositionMeta *ometa;
187 
188   ometa = (GstVideoOverlayCompositionMeta *) meta;
189 
190   ometa->overlay = NULL;
191 
192   return TRUE;
193 }
194 
195 static void
gst_video_overlay_composition_meta_free(GstMeta * meta,GstBuffer * buf)196 gst_video_overlay_composition_meta_free (GstMeta * meta, GstBuffer * buf)
197 {
198   GstVideoOverlayCompositionMeta *ometa;
199 
200   ometa = (GstVideoOverlayCompositionMeta *) meta;
201 
202   if (ometa->overlay)
203     gst_video_overlay_composition_unref (ometa->overlay);
204 }
205 
206 static gboolean
gst_video_overlay_composition_meta_transform(GstBuffer * dest,GstMeta * meta,GstBuffer * buffer,GQuark type,gpointer data)207 gst_video_overlay_composition_meta_transform (GstBuffer * dest, GstMeta * meta,
208     GstBuffer * buffer, GQuark type, gpointer data)
209 {
210   GstVideoOverlayCompositionMeta *dmeta, *smeta;
211 
212   smeta = (GstVideoOverlayCompositionMeta *) meta;
213 
214   if (GST_META_TRANSFORM_IS_COPY (type)) {
215     GstMetaTransformCopy *copy = data;
216 
217     if (!copy->region) {
218       GST_DEBUG ("copy video overlay composition metadata");
219 
220       /* only copy if the complete data is copied as well */
221       dmeta =
222           (GstVideoOverlayCompositionMeta *) gst_buffer_add_meta (dest,
223           GST_VIDEO_OVERLAY_COMPOSITION_META_INFO, NULL);
224       if (!dmeta)
225         return FALSE;
226 
227       dmeta->overlay = gst_video_overlay_composition_ref (smeta->overlay);
228     }
229   } else {
230     /* return FALSE, if transform type is not supported */
231     return FALSE;
232   }
233   return TRUE;
234 }
235 
236 GType
gst_video_overlay_composition_meta_api_get_type(void)237 gst_video_overlay_composition_meta_api_get_type (void)
238 {
239   static volatile GType type = 0;
240   static const gchar *tags[] = { NULL };
241 
242   if (g_once_init_enter (&type)) {
243     GType _type =
244         gst_meta_api_type_register ("GstVideoOverlayCompositionMetaAPI", tags);
245     g_once_init_leave (&type, _type);
246   }
247   return type;
248 }
249 
250 /* video overlay composition metadata */
251 const GstMetaInfo *
gst_video_overlay_composition_meta_get_info(void)252 gst_video_overlay_composition_meta_get_info (void)
253 {
254   static const GstMetaInfo *video_overlay_composition_meta_info = NULL;
255 
256   if (g_once_init_enter ((GstMetaInfo **) &
257           video_overlay_composition_meta_info)) {
258     const GstMetaInfo *meta =
259         gst_meta_register (GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE,
260         "GstVideoOverlayCompositionMeta",
261         sizeof (GstVideoOverlayCompositionMeta),
262         (GstMetaInitFunction) gst_video_overlay_composition_meta_init,
263         (GstMetaFreeFunction) gst_video_overlay_composition_meta_free,
264         (GstMetaTransformFunction)
265         gst_video_overlay_composition_meta_transform);
266     g_once_init_leave ((GstMetaInfo **) & video_overlay_composition_meta_info,
267         (GstMetaInfo *) meta);
268   }
269   return video_overlay_composition_meta_info;
270 }
271 
272 /**
273  * gst_buffer_add_video_overlay_composition_meta:
274  * @buf: a #GstBuffer
275  * @comp: (allow-none): a #GstVideoOverlayComposition
276  *
277  * Sets an overlay composition on a buffer. The buffer will obtain its own
278  * reference to the composition, meaning this function does not take ownership
279  * of @comp.
280  *
281  * Returns: (transfer none): a #GstVideoOverlayCompositionMeta
282  */
283 GstVideoOverlayCompositionMeta *
gst_buffer_add_video_overlay_composition_meta(GstBuffer * buf,GstVideoOverlayComposition * comp)284 gst_buffer_add_video_overlay_composition_meta (GstBuffer * buf,
285     GstVideoOverlayComposition * comp)
286 {
287   GstVideoOverlayCompositionMeta *ometa;
288 
289   g_return_val_if_fail (gst_buffer_is_writable (buf), NULL);
290 
291   ometa = (GstVideoOverlayCompositionMeta *)
292       gst_buffer_add_meta (buf, GST_VIDEO_OVERLAY_COMPOSITION_META_INFO, NULL);
293 
294   ometa->overlay = gst_video_overlay_composition_ref (comp);
295 
296   return ometa;
297 }
298 
299 /* ------------------------------ composition ------------------------------ */
300 
301 #define RECTANGLE_ARRAY_STEP 4  /* premature optimization */
302 
303 GST_DEFINE_MINI_OBJECT_TYPE (GstVideoOverlayComposition,
304     gst_video_overlay_composition);
305 
306 static void
gst_video_overlay_composition_free(GstMiniObject * mini_obj)307 gst_video_overlay_composition_free (GstMiniObject * mini_obj)
308 {
309   GstVideoOverlayComposition *comp = (GstVideoOverlayComposition *) mini_obj;
310   guint num;
311 
312   num = comp->num_rectangles;
313 
314   while (num > 0) {
315     gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (comp->rectangles[num -
316                 1]), GST_MINI_OBJECT_CAST (comp));
317     gst_video_overlay_rectangle_unref (comp->rectangles[num - 1]);
318     --num;
319   }
320 
321   g_free (comp->rectangles);
322   comp->rectangles = NULL;
323   comp->num_rectangles = 0;
324 
325   g_slice_free (GstVideoOverlayComposition, comp);
326 }
327 
328 /**
329  * gst_video_overlay_composition_new:
330  * @rectangle: (transfer none): a #GstVideoOverlayRectangle to add to the
331  *     composition
332  *
333  * Creates a new video overlay composition object to hold one or more
334  * overlay rectangles.
335  *
336  * Returns: (transfer full): a new #GstVideoOverlayComposition. Unref with
337  *     gst_video_overlay_composition_unref() when no longer needed.
338  */
339 GstVideoOverlayComposition *
gst_video_overlay_composition_new(GstVideoOverlayRectangle * rectangle)340 gst_video_overlay_composition_new (GstVideoOverlayRectangle * rectangle)
341 {
342   GstVideoOverlayComposition *comp;
343 
344 
345   /* FIXME: should we allow empty compositions? Could also be expressed as
346    * buffer without a composition on it. Maybe there are cases where doing
347    * an empty new + _add() in a loop is easier? */
348   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
349 
350   comp = g_slice_new0 (GstVideoOverlayComposition);
351 
352   gst_mini_object_init (GST_MINI_OBJECT_CAST (comp), 0,
353       GST_TYPE_VIDEO_OVERLAY_COMPOSITION,
354       (GstMiniObjectCopyFunction) gst_video_overlay_composition_copy,
355       NULL, (GstMiniObjectFreeFunction) gst_video_overlay_composition_free);
356 
357   comp->rectangles = g_new0 (GstVideoOverlayRectangle *, RECTANGLE_ARRAY_STEP);
358   comp->rectangles[0] = gst_video_overlay_rectangle_ref (rectangle);
359   gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (rectangle),
360       GST_MINI_OBJECT_CAST (comp));
361   comp->num_rectangles = 1;
362 
363   comp->seq_num = gst_video_overlay_get_seqnum ();
364 
365   /* since the rectangle was created earlier, its seqnum is smaller than ours */
366   comp->min_seq_num_used = rectangle->seq_num;
367 
368   GST_LOG ("new composition %p: seq_num %u with rectangle %p", comp,
369       comp->seq_num, rectangle);
370 
371   return comp;
372 }
373 
374 /**
375  * gst_video_overlay_composition_add_rectangle:
376  * @comp: a #GstVideoOverlayComposition
377  * @rectangle: (transfer none): a #GstVideoOverlayRectangle to add to the
378  *     composition
379  *
380  * Adds an overlay rectangle to an existing overlay composition object. This
381  * must be done right after creating the overlay composition.
382  */
383 void
gst_video_overlay_composition_add_rectangle(GstVideoOverlayComposition * comp,GstVideoOverlayRectangle * rectangle)384 gst_video_overlay_composition_add_rectangle (GstVideoOverlayComposition * comp,
385     GstVideoOverlayRectangle * rectangle)
386 {
387   g_return_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp));
388   g_return_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle));
389   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (comp)));
390 
391   if (comp->num_rectangles % RECTANGLE_ARRAY_STEP == 0) {
392     comp->rectangles =
393         g_renew (GstVideoOverlayRectangle *, comp->rectangles,
394         comp->num_rectangles + RECTANGLE_ARRAY_STEP);
395   }
396 
397   comp->rectangles[comp->num_rectangles] =
398       gst_video_overlay_rectangle_ref (rectangle);
399   gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (rectangle),
400       GST_MINI_OBJECT_CAST (comp));
401   comp->num_rectangles += 1;
402 
403   comp->min_seq_num_used = MIN (comp->min_seq_num_used, rectangle->seq_num);
404 
405   GST_LOG ("composition %p: added rectangle %p", comp, rectangle);
406 }
407 
408 /**
409  * gst_video_overlay_composition_n_rectangles:
410  * @comp: a #GstVideoOverlayComposition
411  *
412  * Returns the number of #GstVideoOverlayRectangle<!-- -->s contained in @comp.
413  *
414  * Returns: the number of rectangles
415  */
416 guint
gst_video_overlay_composition_n_rectangles(GstVideoOverlayComposition * comp)417 gst_video_overlay_composition_n_rectangles (GstVideoOverlayComposition * comp)
418 {
419   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), 0);
420 
421   return comp->num_rectangles;
422 }
423 
424 /**
425  * gst_video_overlay_composition_get_rectangle:
426  * @comp: a #GstVideoOverlayComposition
427  * @n: number of the rectangle to get
428  *
429  * Returns the @n-th #GstVideoOverlayRectangle contained in @comp.
430  *
431  * Returns: (transfer none): the @n-th rectangle, or NULL if @n is out of
432  *     bounds. Will not return a new reference, the caller will need to
433  *     obtain her own reference using gst_video_overlay_rectangle_ref()
434  *     if needed.
435  */
436 GstVideoOverlayRectangle *
gst_video_overlay_composition_get_rectangle(GstVideoOverlayComposition * comp,guint n)437 gst_video_overlay_composition_get_rectangle (GstVideoOverlayComposition * comp,
438     guint n)
439 {
440   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), NULL);
441 
442   if (n >= comp->num_rectangles)
443     return NULL;
444 
445   return comp->rectangles[n];
446 }
447 
448 static gboolean
gst_video_overlay_rectangle_needs_scaling(GstVideoOverlayRectangle * r)449 gst_video_overlay_rectangle_needs_scaling (GstVideoOverlayRectangle * r)
450 {
451   return (GST_VIDEO_INFO_WIDTH (&r->info) != r->render_width ||
452       GST_VIDEO_INFO_HEIGHT (&r->info) != r->render_height);
453 }
454 
455 /**
456  * gst_video_overlay_composition_blend:
457  * @comp: a #GstVideoOverlayComposition
458  * @video_buf: a #GstVideoFrame containing raw video data in a
459  *             supported format. It should be mapped using GST_MAP_READWRITE
460  *
461  * Blends the overlay rectangles in @comp on top of the raw video data
462  * contained in @video_buf. The data in @video_buf must be writable and
463  * mapped appropriately.
464  *
465  * Since @video_buf data is read and will be modified, it ought be
466  * mapped with flag GST_MAP_READWRITE.
467  */
468 /* FIXME: formats with more than 8 bit per component which get unpacked into
469  * ARGB64 or AYUV64 (such as v210, v216, UYVP, GRAY16_LE and GRAY16_BE)
470  * are not supported yet by the code in video-blend.c.
471  */
472 gboolean
gst_video_overlay_composition_blend(GstVideoOverlayComposition * comp,GstVideoFrame * video_buf)473 gst_video_overlay_composition_blend (GstVideoOverlayComposition * comp,
474     GstVideoFrame * video_buf)
475 {
476   GstVideoInfo scaled_info;
477   GstVideoInfo *vinfo;
478   GstVideoFrame rectangle_frame;
479   GstVideoFormat fmt;
480   GstBuffer *pixels = NULL;
481   gboolean ret = TRUE;
482   guint n, num;
483   int w, h;
484 
485   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), FALSE);
486   g_return_val_if_fail (video_buf != NULL, FALSE);
487 
488   w = GST_VIDEO_FRAME_WIDTH (video_buf);
489   h = GST_VIDEO_FRAME_HEIGHT (video_buf);
490   fmt = GST_VIDEO_FRAME_FORMAT (video_buf);
491 
492   num = comp->num_rectangles;
493   GST_LOG ("Blending composition %p with %u rectangles onto video buffer %p "
494       "(%ux%u, format %u)", comp, num, video_buf, w, h, fmt);
495 
496   for (n = 0; n < num; ++n) {
497     GstVideoOverlayRectangle *rect;
498     gboolean needs_scaling;
499 
500     rect = comp->rectangles[n];
501 
502     GST_LOG (" rectangle %u %p: %ux%u, format %u", n, rect,
503         GST_VIDEO_INFO_WIDTH (&rect->info), GST_VIDEO_INFO_HEIGHT (&rect->info),
504         GST_VIDEO_INFO_FORMAT (&rect->info));
505 
506     needs_scaling = gst_video_overlay_rectangle_needs_scaling (rect);
507     if (needs_scaling) {
508       gst_video_blend_scale_linear_RGBA (&rect->info, rect->pixels,
509           rect->render_height, rect->render_width, &scaled_info, &pixels);
510       vinfo = &scaled_info;
511     } else {
512       pixels = gst_buffer_ref (rect->pixels);
513       vinfo = &rect->info;
514     }
515 
516     gst_video_frame_map (&rectangle_frame, vinfo, pixels, GST_MAP_READ);
517 
518     ret = gst_video_blend (video_buf, &rectangle_frame, rect->x, rect->y,
519         rect->global_alpha);
520     gst_video_frame_unmap (&rectangle_frame);
521     if (!ret) {
522       GST_WARNING ("Could not blend overlay rectangle onto video buffer");
523     }
524 
525     /* FIXME: should cache scaled pixels in the rectangle struct */
526     gst_buffer_unref (pixels);
527   }
528 
529   return ret;
530 }
531 
532 /**
533  * gst_video_overlay_composition_copy:
534  * @comp: (transfer none): a #GstVideoOverlayComposition to copy
535  *
536  * Makes a copy of @comp and all contained rectangles, so that it is possible
537  * to modify the composition and contained rectangles (e.g. add additional
538  * rectangles or change the render co-ordinates or render dimension). The
539  * actual overlay pixel data buffers contained in the rectangles are not
540  * copied.
541  *
542  * Returns: (transfer full): a new #GstVideoOverlayComposition equivalent
543  *     to @comp.
544  */
545 GstVideoOverlayComposition *
gst_video_overlay_composition_copy(GstVideoOverlayComposition * comp)546 gst_video_overlay_composition_copy (GstVideoOverlayComposition * comp)
547 {
548   GstVideoOverlayComposition *copy;
549   GstVideoOverlayRectangle *rect;
550   guint n;
551 
552   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), NULL);
553 
554   if (G_LIKELY (comp->num_rectangles == 0))
555     return gst_video_overlay_composition_new (NULL);
556 
557   rect = gst_video_overlay_rectangle_copy (comp->rectangles[0]);
558   copy = gst_video_overlay_composition_new (rect);
559   gst_video_overlay_rectangle_unref (rect);
560 
561   for (n = 1; n < comp->num_rectangles; ++n) {
562     rect = gst_video_overlay_rectangle_copy (comp->rectangles[n]);
563     gst_video_overlay_composition_add_rectangle (copy, rect);
564     gst_video_overlay_rectangle_unref (rect);
565   }
566 
567   return copy;
568 }
569 
570 /**
571  * gst_video_overlay_composition_make_writable:
572  * @comp: (transfer full): a #GstVideoOverlayComposition to copy
573  *
574  * Takes ownership of @comp and returns a version of @comp that is writable
575  * (i.e. can be modified). Will either return @comp right away, or create a
576  * new writable copy of @comp and unref @comp itself. All the contained
577  * rectangles will also be copied, but the actual overlay pixel data buffers
578  * contained in the rectangles are not copied.
579  *
580  * Returns: (transfer full): a writable #GstVideoOverlayComposition
581  *     equivalent to @comp.
582  */
583 GstVideoOverlayComposition *
gst_video_overlay_composition_make_writable(GstVideoOverlayComposition * comp)584 gst_video_overlay_composition_make_writable (GstVideoOverlayComposition * comp)
585 {
586   GstVideoOverlayComposition *writable_comp;
587 
588   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), NULL);
589 
590   if (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (comp))) {
591     guint n;
592 
593     for (n = 0; n < comp->num_rectangles; ++n) {
594       if (!gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (comp->rectangles
595                   [n])))
596         goto copy;
597     }
598     return comp;
599   }
600 
601 copy:
602 
603   writable_comp = gst_video_overlay_composition_copy (comp);
604   gst_video_overlay_composition_unref (comp);
605 
606   return writable_comp;
607 }
608 
609 /**
610  * gst_video_overlay_composition_get_seqnum:
611  * @comp: a #GstVideoOverlayComposition
612  *
613  * Returns the sequence number of this composition. Sequence numbers are
614  * monotonically increasing and unique for overlay compositions and rectangles
615  * (meaning there will never be a rectangle with the same sequence number as
616  * a composition).
617  *
618  * Returns: the sequence number of @comp
619  */
620 guint
gst_video_overlay_composition_get_seqnum(GstVideoOverlayComposition * comp)621 gst_video_overlay_composition_get_seqnum (GstVideoOverlayComposition * comp)
622 {
623   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_COMPOSITION (comp), 0);
624 
625   return comp->seq_num;
626 }
627 
628 /* ------------------------------ rectangles ------------------------------ -*/
629 
630 GST_DEFINE_MINI_OBJECT_TYPE (GstVideoOverlayRectangle,
631     gst_video_overlay_rectangle);
632 
633 static void
gst_video_overlay_rectangle_free(GstMiniObject * mini_obj)634 gst_video_overlay_rectangle_free (GstMiniObject * mini_obj)
635 {
636   GstVideoOverlayRectangle *rect = (GstVideoOverlayRectangle *) mini_obj;
637 
638   gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (rect->pixels),
639       GST_MINI_OBJECT_CAST (rect));
640   gst_buffer_replace (&rect->pixels, NULL);
641 
642   while (rect->scaled_rectangles != NULL) {
643     GstVideoOverlayRectangle *scaled_rect = rect->scaled_rectangles->data;
644 
645     gst_video_overlay_rectangle_unref (scaled_rect);
646 
647     rect->scaled_rectangles =
648         g_list_delete_link (rect->scaled_rectangles, rect->scaled_rectangles);
649   }
650 
651   g_free (rect->initial_alpha);
652   g_mutex_clear (&rect->lock);
653 
654   g_slice_free (GstVideoOverlayRectangle, rect);
655 }
656 
657 static inline gboolean
gst_video_overlay_rectangle_check_flags(GstVideoOverlayFormatFlags flags)658 gst_video_overlay_rectangle_check_flags (GstVideoOverlayFormatFlags flags)
659 {
660   /* Check flags only contains flags we know about */
661   return (flags & ~(GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA |
662           GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)) == 0;
663 }
664 
665 static gboolean
gst_video_overlay_rectangle_is_same_alpha_type(GstVideoOverlayFormatFlags flags1,GstVideoOverlayFormatFlags flags2)666 gst_video_overlay_rectangle_is_same_alpha_type (GstVideoOverlayFormatFlags
667     flags1, GstVideoOverlayFormatFlags flags2)
668 {
669   return ((flags1 ^ flags2) & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
670       == 0;
671 }
672 
673 
674 /**
675  * gst_video_overlay_rectangle_new_raw:
676  * @pixels: (transfer none): a #GstBuffer pointing to the pixel memory
677  * @render_x: the X co-ordinate on the video where the top-left corner of this
678  *     overlay rectangle should be rendered to
679  * @render_y: the Y co-ordinate on the video where the top-left corner of this
680  *     overlay rectangle should be rendered to
681  * @render_width: the render width of this rectangle on the video
682  * @render_height: the render height of this rectangle on the video
683  * @flags: flags
684  *
685  * Creates a new video overlay rectangle with ARGB or AYUV pixel data.
686  * The layout in case of ARGB of the components in memory is B-G-R-A
687  * on little-endian platforms
688  * (corresponding to #GST_VIDEO_FORMAT_BGRA) and A-R-G-B on big-endian
689  * platforms (corresponding to #GST_VIDEO_FORMAT_ARGB). In other words,
690  * pixels are treated as 32-bit words and the lowest 8 bits then contain
691  * the blue component value and the highest 8 bits contain the alpha
692  * component value. Unless specified in the flags, the RGB values are
693  * non-premultiplied. This is the format that is used by most hardware,
694  * and also many rendering libraries such as Cairo, for example.
695  * The pixel data buffer must have #GstVideoMeta set.
696  *
697  * Returns: (transfer full): a new #GstVideoOverlayRectangle. Unref with
698  *     gst_video_overlay_rectangle_unref() when no longer needed.
699  */
700 GstVideoOverlayRectangle *
gst_video_overlay_rectangle_new_raw(GstBuffer * pixels,gint render_x,gint render_y,guint render_width,guint render_height,GstVideoOverlayFormatFlags flags)701 gst_video_overlay_rectangle_new_raw (GstBuffer * pixels,
702     gint render_x, gint render_y, guint render_width, guint render_height,
703     GstVideoOverlayFormatFlags flags)
704 {
705   GstVideoOverlayRectangle *rect;
706   GstVideoMeta *vmeta;
707   GstVideoFormat format;
708   guint width, height;
709 
710   g_return_val_if_fail (GST_IS_BUFFER (pixels), NULL);
711   g_return_val_if_fail (render_height > 0 && render_width > 0, NULL);
712   g_return_val_if_fail (gst_video_overlay_rectangle_check_flags (flags), NULL);
713 
714   /* buffer must have video meta with some expected settings */
715   vmeta = gst_buffer_get_video_meta (pixels);
716   g_return_val_if_fail (vmeta, NULL);
717   g_return_val_if_fail (vmeta->format ==
718       GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB ||
719       vmeta->format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV, NULL);
720   g_return_val_if_fail (vmeta->flags == GST_VIDEO_FRAME_FLAG_NONE, NULL);
721 
722   format = vmeta->format;
723   width = vmeta->width;
724   height = vmeta->height;
725 
726   /* technically ((height-1)*stride)+width might be okay too */
727   g_return_val_if_fail (gst_buffer_get_size (pixels) >= height * width * 4,
728       NULL);
729   g_return_val_if_fail (height > 0 && width > 0, NULL);
730 
731   rect = g_slice_new0 (GstVideoOverlayRectangle);
732 
733   gst_mini_object_init (GST_MINI_OBJECT_CAST (rect), 0,
734       GST_TYPE_VIDEO_OVERLAY_RECTANGLE,
735       (GstMiniObjectCopyFunction) gst_video_overlay_rectangle_copy,
736       NULL, (GstMiniObjectFreeFunction) gst_video_overlay_rectangle_free);
737 
738   g_mutex_init (&rect->lock);
739 
740   rect->pixels = gst_buffer_ref (pixels);
741   gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (pixels),
742       GST_MINI_OBJECT_CAST (rect));
743   rect->scaled_rectangles = NULL;
744 
745   gst_video_info_init (&rect->info);
746   if (!gst_video_info_set_format (&rect->info, format, width, height)) {
747     gst_mini_object_unref (GST_MINI_OBJECT_CAST (rect));
748     return NULL;
749   }
750   if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
751     rect->info.flags |= GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA;
752 
753   rect->x = render_x;
754   rect->y = render_y;
755   rect->render_width = render_width;
756   rect->render_height = render_height;
757 
758   rect->global_alpha = 1.0;
759   rect->applied_global_alpha = 1.0;
760   rect->initial_alpha = NULL;
761 
762   rect->flags = flags;
763 
764   rect->seq_num = gst_video_overlay_get_seqnum ();
765 
766   GST_LOG ("new rectangle %p: %ux%u => %ux%u @ %u,%u, seq_num %u, format %u, "
767       "flags %x, pixels %p, global_alpha=%f", rect, width, height, render_width,
768       render_height, render_x, render_y, rect->seq_num, format,
769       rect->flags, pixels, rect->global_alpha);
770 
771   return rect;
772 }
773 
774 /**
775  * gst_video_overlay_rectangle_get_render_rectangle:
776  * @rectangle: a #GstVideoOverlayRectangle
777  * @render_x: (out) (allow-none): address where to store the X render offset
778  * @render_y: (out) (allow-none): address where to store the Y render offset
779  * @render_width: (out) (allow-none): address where to store the render width
780  * @render_height: (out) (allow-none): address where to store the render height
781  *
782  * Retrieves the render position and render dimension of the overlay
783  * rectangle on the video.
784  *
785  * Returns: TRUE if valid render dimensions were retrieved.
786  */
787 gboolean
gst_video_overlay_rectangle_get_render_rectangle(GstVideoOverlayRectangle * rectangle,gint * render_x,gint * render_y,guint * render_width,guint * render_height)788 gst_video_overlay_rectangle_get_render_rectangle (GstVideoOverlayRectangle *
789     rectangle, gint * render_x, gint * render_y, guint * render_width,
790     guint * render_height)
791 {
792   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), FALSE);
793 
794   if (render_x)
795     *render_x = rectangle->x;
796   if (render_y)
797     *render_y = rectangle->y;
798   if (render_width)
799     *render_width = rectangle->render_width;
800   if (render_height)
801     *render_height = rectangle->render_height;
802 
803   return TRUE;
804 }
805 
806 /**
807  * gst_video_overlay_rectangle_set_render_rectangle:
808  * @rectangle: a #GstVideoOverlayRectangle
809  * @render_x: render X position of rectangle on video
810  * @render_y: render Y position of rectangle on video
811  * @render_width: render width of rectangle
812  * @render_height: render height of rectangle
813  *
814  * Sets the render position and dimensions of the rectangle on the video.
815  * This function is mainly for elements that modify the size of the video
816  * in some way (e.g. through scaling or cropping) and need to adjust the
817  * details of any overlays to match the operation that changed the size.
818  *
819  * @rectangle must be writable, meaning its refcount must be 1. You can
820  * make the rectangles inside a #GstVideoOverlayComposition writable using
821  * gst_video_overlay_composition_make_writable() or
822  * gst_video_overlay_composition_copy().
823  */
824 void
gst_video_overlay_rectangle_set_render_rectangle(GstVideoOverlayRectangle * rectangle,gint render_x,gint render_y,guint render_width,guint render_height)825 gst_video_overlay_rectangle_set_render_rectangle (GstVideoOverlayRectangle *
826     rectangle, gint render_x, gint render_y, guint render_width,
827     guint render_height)
828 {
829   g_return_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle));
830   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST
831           (rectangle)));
832 
833   rectangle->x = render_x;
834   rectangle->y = render_y;
835   rectangle->render_width = render_width;
836   rectangle->render_height = render_height;
837 }
838 
839 /* FIXME: orc-ify */
840 static void
gst_video_overlay_rectangle_premultiply_0(GstVideoFrame * frame)841 gst_video_overlay_rectangle_premultiply_0 (GstVideoFrame * frame)
842 {
843   int i, j;
844   int width = GST_VIDEO_FRAME_WIDTH (frame);
845   int height = GST_VIDEO_FRAME_HEIGHT (frame);
846   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
847   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
848 
849   for (j = 0; j < height; ++j) {
850     guint8 *line;
851 
852     line = data;
853     line += stride * j;
854     for (i = 0; i < width; ++i) {
855       int a = line[0];
856       line[1] = line[1] * a / 255;
857       line[2] = line[2] * a / 255;
858       line[3] = line[3] * a / 255;
859       line += 4;
860     }
861   }
862 }
863 
864 static void
gst_video_overlay_rectangle_premultiply_3(GstVideoFrame * frame)865 gst_video_overlay_rectangle_premultiply_3 (GstVideoFrame * frame)
866 {
867   int i, j;
868   int width = GST_VIDEO_FRAME_WIDTH (frame);
869   int height = GST_VIDEO_FRAME_HEIGHT (frame);
870   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
871   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
872 
873   for (j = 0; j < height; ++j) {
874     guint8 *line;
875 
876     line = data;
877     line += stride * j;
878     for (i = 0; i < width; ++i) {
879       int a = line[3];
880       line[0] = line[0] * a / 255;
881       line[1] = line[1] * a / 255;
882       line[2] = line[2] * a / 255;
883       line += 4;
884     }
885   }
886 }
887 
888 static void
gst_video_overlay_rectangle_premultiply(GstVideoFrame * frame)889 gst_video_overlay_rectangle_premultiply (GstVideoFrame * frame)
890 {
891   gint alpha_offset;
892 
893   alpha_offset = GST_VIDEO_FRAME_COMP_POFFSET (frame, 3);
894   switch (alpha_offset) {
895     case 0:
896       gst_video_overlay_rectangle_premultiply_0 (frame);
897       break;
898     case 3:
899       gst_video_overlay_rectangle_premultiply_3 (frame);
900       break;
901     default:
902       g_assert_not_reached ();
903       break;
904   }
905 }
906 
907 /* FIXME: orc-ify */
908 static void
gst_video_overlay_rectangle_unpremultiply_0(GstVideoFrame * frame)909 gst_video_overlay_rectangle_unpremultiply_0 (GstVideoFrame * frame)
910 {
911   int i, j;
912   int width = GST_VIDEO_FRAME_WIDTH (frame);
913   int height = GST_VIDEO_FRAME_HEIGHT (frame);
914   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
915   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
916 
917   for (j = 0; j < height; ++j) {
918     guint8 *line;
919 
920     line = data;
921     line += stride * j;
922     for (i = 0; i < width; ++i) {
923       int a = line[0];
924       if (a) {
925         line[1] = MIN ((line[1] * 255 + a / 2) / a, 255);
926         line[2] = MIN ((line[2] * 255 + a / 2) / a, 255);
927         line[3] = MIN ((line[3] * 255 + a / 2) / a, 255);
928       }
929       line += 4;
930     }
931   }
932 }
933 
934 static void
gst_video_overlay_rectangle_unpremultiply_3(GstVideoFrame * frame)935 gst_video_overlay_rectangle_unpremultiply_3 (GstVideoFrame * frame)
936 {
937   int i, j;
938   int width = GST_VIDEO_FRAME_WIDTH (frame);
939   int height = GST_VIDEO_FRAME_HEIGHT (frame);
940   int stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
941   guint8 *data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
942 
943   for (j = 0; j < height; ++j) {
944     guint8 *line;
945 
946     line = data;
947     line += stride * j;
948     for (i = 0; i < width; ++i) {
949       int a = line[3];
950       if (a) {
951         line[0] = MIN ((line[0] * 255 + a / 2) / a, 255);
952         line[1] = MIN ((line[1] * 255 + a / 2) / a, 255);
953         line[2] = MIN ((line[2] * 255 + a / 2) / a, 255);
954       }
955       line += 4;
956     }
957   }
958 }
959 
960 static void
gst_video_overlay_rectangle_unpremultiply(GstVideoFrame * frame)961 gst_video_overlay_rectangle_unpremultiply (GstVideoFrame * frame)
962 {
963   gint alpha_offset;
964 
965   alpha_offset = GST_VIDEO_FRAME_COMP_POFFSET (frame, 3);
966   switch (alpha_offset) {
967     case 0:
968       gst_video_overlay_rectangle_unpremultiply_0 (frame);
969       break;
970     case 3:
971       gst_video_overlay_rectangle_unpremultiply_3 (frame);
972       break;
973     default:
974       g_assert_not_reached ();
975       break;
976   }
977 }
978 
979 
980 static void
gst_video_overlay_rectangle_extract_alpha(GstVideoOverlayRectangle * rect)981 gst_video_overlay_rectangle_extract_alpha (GstVideoOverlayRectangle * rect)
982 {
983   guint8 *src, *dst;
984   GstVideoFrame frame;
985   gint i, j, w, h, stride, alpha_offset;
986 
987   alpha_offset = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
988   g_return_if_fail (alpha_offset == 0 || alpha_offset == 3);
989 
990   gst_video_frame_map (&frame, &rect->info, rect->pixels, GST_MAP_READ);
991   src = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
992   w = GST_VIDEO_INFO_WIDTH (&rect->info);
993   h = GST_VIDEO_INFO_HEIGHT (&rect->info);
994   stride = GST_VIDEO_INFO_PLANE_STRIDE (&rect->info, 0);
995 
996   g_free (rect->initial_alpha);
997   rect->initial_alpha = g_malloc (w * h);
998   dst = rect->initial_alpha;
999 
1000   for (i = 0; i < h; i++) {
1001     for (j = 0; j < w; j++) {
1002       *dst = src[alpha_offset];
1003       dst++;
1004       src += 4;
1005     }
1006     src += stride - 4 * w;
1007   }
1008   gst_video_frame_unmap (&frame);
1009 }
1010 
1011 
1012 static void
gst_video_overlay_rectangle_apply_global_alpha(GstVideoOverlayRectangle * rect,float global_alpha)1013 gst_video_overlay_rectangle_apply_global_alpha (GstVideoOverlayRectangle * rect,
1014     float global_alpha)
1015 {
1016   guint8 *src, *dst;
1017   GstVideoFrame frame;
1018   gint i, j, w, h, stride;
1019   gint argb_a, argb_r, argb_g, argb_b;
1020   gint alpha_offset;
1021 
1022   g_assert (!(rect->applied_global_alpha != 1.0
1023           && rect->initial_alpha == NULL));
1024 
1025   alpha_offset = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
1026   g_return_if_fail (alpha_offset == 0 || alpha_offset == 3);
1027 
1028   if (global_alpha == rect->applied_global_alpha)
1029     return;
1030 
1031   if (rect->initial_alpha == NULL)
1032     gst_video_overlay_rectangle_extract_alpha (rect);
1033 
1034   src = rect->initial_alpha;
1035   if (!gst_buffer_is_writable (rect->pixels)) {
1036     gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (rect->pixels),
1037         GST_MINI_OBJECT_CAST (rect));
1038     rect->pixels = gst_buffer_copy (rect->pixels);
1039     gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (rect->pixels),
1040         GST_MINI_OBJECT_CAST (rect));
1041   }
1042 
1043   gst_video_frame_map (&frame, &rect->info, rect->pixels, GST_MAP_READ);
1044   dst = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
1045   w = GST_VIDEO_INFO_WIDTH (&rect->info);
1046   h = GST_VIDEO_INFO_HEIGHT (&rect->info);
1047   stride = GST_VIDEO_INFO_PLANE_STRIDE (&rect->info, 0);
1048 
1049   argb_a = GST_VIDEO_INFO_COMP_POFFSET (&rect->info, 3);
1050   argb_r = (argb_a + 1) % 4;
1051   argb_g = (argb_a + 2) % 4;
1052   argb_b = (argb_a + 3) % 4;
1053 
1054   for (i = 0; i < h; i++) {
1055     for (j = 0; j < w; j++) {
1056       guint8 na = (guint8) (*src * global_alpha);
1057 
1058       if (! !(rect->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)) {
1059         dst[argb_r] =
1060             (guint8) ((double) (dst[argb_r] * 255) / (double) dst[argb_a]) *
1061             na / 255;
1062         dst[argb_g] =
1063             (guint8) ((double) (dst[argb_g] * 255) / (double) dst[argb_a]) *
1064             na / 255;
1065         dst[argb_b] =
1066             (guint8) ((double) (dst[argb_b] * 255) / (double) dst[argb_a]) *
1067             na / 255;
1068       }
1069       dst[argb_a] = na;
1070       src++;
1071       dst += 4;
1072     }
1073     dst += stride - 4 * w;
1074   }
1075   gst_video_frame_unmap (&frame);
1076 
1077   rect->applied_global_alpha = global_alpha;
1078 }
1079 
1080 static void
gst_video_overlay_rectangle_convert(GstVideoInfo * src,GstBuffer * src_buffer,GstVideoFormat dest_format,GstVideoInfo * dest,GstBuffer ** dest_buffer)1081 gst_video_overlay_rectangle_convert (GstVideoInfo * src, GstBuffer * src_buffer,
1082     GstVideoFormat dest_format, GstVideoInfo * dest, GstBuffer ** dest_buffer)
1083 {
1084   gint width, height, stride;
1085   GstVideoFrame src_frame, dest_frame;
1086   GstVideoFormat format;
1087   gint k, l;
1088   guint8 *sdata, *ddata;
1089 
1090   format = GST_VIDEO_INFO_FORMAT (src);
1091 
1092   width = GST_VIDEO_INFO_WIDTH (src);
1093   height = GST_VIDEO_INFO_HEIGHT (src);
1094 
1095   gst_video_info_init (dest);
1096   if (!gst_video_info_set_format (dest, dest_format, width, height)) {
1097     g_warn_if_reached ();
1098     return;
1099   }
1100 
1101   *dest_buffer = gst_buffer_new_and_alloc (GST_VIDEO_INFO_SIZE (dest));
1102 
1103   gst_video_frame_map (&src_frame, src, src_buffer, GST_MAP_READ);
1104   gst_video_frame_map (&dest_frame, dest, *dest_buffer, GST_MAP_WRITE);
1105 
1106   sdata = GST_VIDEO_FRAME_PLANE_DATA (&src_frame, 0);
1107   ddata = GST_VIDEO_FRAME_PLANE_DATA (&dest_frame, 0);
1108   stride = GST_VIDEO_FRAME_PLANE_STRIDE (&src_frame, 0);
1109 
1110   if (format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV &&
1111       dest_format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB) {
1112     gint ayuv;
1113     gint a, y, u, v, r, g, b;
1114 
1115     for (k = 0; k < height; k++) {
1116       for (l = 0; l < width; l++) {
1117         ayuv = GST_READ_UINT32_BE (sdata);
1118         a = ayuv >> 24;
1119         y = (ayuv >> 16) & 0xff;
1120         u = (ayuv >> 8) & 0xff;
1121         v = (ayuv & 0xff);
1122 
1123         r = (298 * y + 459 * v - 63514) >> 8;
1124         g = (298 * y - 55 * u - 136 * v + 19681) >> 8;
1125         b = (298 * y + 541 * u - 73988) >> 8;
1126 
1127         r = CLAMP (r, 0, 255);
1128         g = CLAMP (g, 0, 255);
1129         b = CLAMP (b, 0, 255);
1130 
1131         /* native endian ARGB */
1132         *(guint32 *) ddata = ((a << 24) | (r << 16) | (g << 8) | b);
1133 
1134         sdata += 4;
1135         ddata += 4;
1136       }
1137       sdata += stride - 4 * width;
1138     }
1139   } else if (format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB &&
1140       dest_format == GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV) {
1141     gint argb;
1142     gint a, y, u, v, r, g, b;
1143 
1144     for (k = 0; k < height; k++) {
1145       for (l = 0; l < width; l++) {
1146         /* native endian ARGB */
1147         argb = *(guint32 *) sdata;
1148         a = argb >> 24;
1149         r = (argb >> 16) & 0xff;
1150         g = (argb >> 8) & 0xff;
1151         b = (argb & 0xff);
1152 
1153         y = (47 * r + 157 * g + 16 * b + 4096) >> 8;
1154         u = (-26 * r - 87 * g + 112 * b + 32768) >> 8;
1155         v = (112 * r - 102 * g - 10 * b + 32768) >> 8;
1156 
1157         y = CLAMP (y, 0, 255);
1158         u = CLAMP (u, 0, 255);
1159         v = CLAMP (v, 0, 255);
1160 
1161         GST_WRITE_UINT32_BE (ddata, ((a << 24) | (y << 16) | (u << 8) | v));
1162 
1163         sdata += 4;
1164         ddata += 4;
1165       }
1166       sdata += stride - 4 * width;
1167     }
1168   } else {
1169     GST_ERROR ("unsupported conversion");
1170     g_assert_not_reached ();
1171   }
1172 
1173   gst_video_frame_unmap (&src_frame);
1174   gst_video_frame_unmap (&dest_frame);
1175 }
1176 
1177 static GstBuffer *
gst_video_overlay_rectangle_get_pixels_raw_internal(GstVideoOverlayRectangle * rectangle,GstVideoOverlayFormatFlags flags,gboolean unscaled,GstVideoFormat wanted_format)1178 gst_video_overlay_rectangle_get_pixels_raw_internal (GstVideoOverlayRectangle *
1179     rectangle, GstVideoOverlayFormatFlags flags, gboolean unscaled,
1180     GstVideoFormat wanted_format)
1181 {
1182   GstVideoOverlayFormatFlags new_flags;
1183   GstVideoOverlayRectangle *scaled_rect = NULL, *conv_rect = NULL;
1184   GstVideoInfo info;
1185   GstVideoFrame frame;
1186   GstBuffer *buf;
1187   GList *l;
1188   guint width, height;
1189   guint wanted_width;
1190   guint wanted_height;
1191   gboolean apply_global_alpha;
1192   gboolean revert_global_alpha;
1193   GstVideoFormat format;
1194 
1195   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1196   g_return_val_if_fail (gst_video_overlay_rectangle_check_flags (flags), NULL);
1197 
1198   width = GST_VIDEO_INFO_WIDTH (&rectangle->info);
1199   height = GST_VIDEO_INFO_HEIGHT (&rectangle->info);
1200   wanted_width = unscaled ? width : rectangle->render_width;
1201   wanted_height = unscaled ? height : rectangle->render_height;
1202   format = GST_VIDEO_INFO_FORMAT (&rectangle->info);
1203 
1204   apply_global_alpha =
1205       (! !(rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)
1206       && !(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA));
1207   revert_global_alpha =
1208       (! !(rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA)
1209       && ! !(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA));
1210 
1211   /* This assumes we don't need to adjust the format */
1212   if (wanted_width == width &&
1213       wanted_height == height &&
1214       wanted_format == format &&
1215       gst_video_overlay_rectangle_is_same_alpha_type (rectangle->flags,
1216           flags)) {
1217     /* don't need to apply/revert global-alpha either: */
1218     if ((!apply_global_alpha
1219             || rectangle->applied_global_alpha == rectangle->global_alpha)
1220         && (!revert_global_alpha || rectangle->applied_global_alpha == 1.0)) {
1221       return rectangle->pixels;
1222     } else {
1223       /* only apply/revert global-alpha */
1224       scaled_rect = rectangle;
1225       goto done;
1226     }
1227   }
1228 
1229   /* see if we've got one cached already */
1230   GST_RECTANGLE_LOCK (rectangle);
1231   for (l = rectangle->scaled_rectangles; l != NULL; l = l->next) {
1232     GstVideoOverlayRectangle *r = l->data;
1233 
1234     if (GST_VIDEO_INFO_WIDTH (&r->info) == wanted_width &&
1235         GST_VIDEO_INFO_HEIGHT (&r->info) == wanted_height &&
1236         GST_VIDEO_INFO_FORMAT (&r->info) == wanted_format &&
1237         gst_video_overlay_rectangle_is_same_alpha_type (r->flags, flags)) {
1238       /* we'll keep these rectangles around until finalize, so it's ok not
1239        * to take our own ref here */
1240       scaled_rect = r;
1241       break;
1242     }
1243   }
1244   GST_RECTANGLE_UNLOCK (rectangle);
1245 
1246   if (scaled_rect != NULL)
1247     goto done;
1248 
1249   /* maybe have one in the right format though */
1250   if (format != wanted_format) {
1251     GST_RECTANGLE_LOCK (rectangle);
1252     for (l = rectangle->scaled_rectangles; l != NULL; l = l->next) {
1253       GstVideoOverlayRectangle *r = l->data;
1254 
1255       if (GST_VIDEO_INFO_FORMAT (&r->info) == wanted_format &&
1256           gst_video_overlay_rectangle_is_same_alpha_type (r->flags, flags)) {
1257         /* we'll keep these rectangles around until finalize, so it's ok not
1258          * to take our own ref here */
1259         conv_rect = r;
1260         break;
1261       }
1262     }
1263     GST_RECTANGLE_UNLOCK (rectangle);
1264   } else {
1265     conv_rect = rectangle;
1266   }
1267 
1268   if (conv_rect == NULL) {
1269     GstVideoInfo conv_info;
1270 
1271     gst_video_overlay_rectangle_convert (&rectangle->info, rectangle->pixels,
1272         wanted_format, &conv_info, &buf);
1273     gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
1274         GST_VIDEO_INFO_FORMAT (&conv_info), width, height);
1275     conv_rect = gst_video_overlay_rectangle_new_raw (buf,
1276         0, 0, width, height, rectangle->flags);
1277     if (rectangle->global_alpha != 1.0)
1278       gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
1279           rectangle->global_alpha);
1280     gst_buffer_unref (buf);
1281     /* keep this converted one around as well in any case */
1282     GST_RECTANGLE_LOCK (rectangle);
1283     rectangle->scaled_rectangles =
1284         g_list_prepend (rectangle->scaled_rectangles, conv_rect);
1285     GST_RECTANGLE_UNLOCK (rectangle);
1286   }
1287 
1288   /* now we continue from conv_rect */
1289   width = GST_VIDEO_INFO_WIDTH (&conv_rect->info);
1290   height = GST_VIDEO_INFO_HEIGHT (&conv_rect->info);
1291   format = GST_VIDEO_INFO_FORMAT (&conv_rect->info);
1292 
1293   /* not cached yet, do the preprocessing and put the result into our cache */
1294   if (wanted_width != width || wanted_height != height) {
1295     GstVideoInfo scaled_info;
1296 
1297     /* we could check the cache for a scaled rect with global_alpha == 1 here */
1298     gst_video_blend_scale_linear_RGBA (&conv_rect->info, conv_rect->pixels,
1299         wanted_height, wanted_width, &scaled_info, &buf);
1300     info = scaled_info;
1301     gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
1302         GST_VIDEO_INFO_FORMAT (&conv_rect->info), wanted_width, wanted_height);
1303   } else if (!gst_video_overlay_rectangle_is_same_alpha_type (conv_rect->flags,
1304           flags)) {
1305     /* if we don't have to scale, we have to modify the alpha values, so we
1306      * need to make a copy of the pixel memory (and we take ownership below) */
1307     buf = gst_buffer_copy (conv_rect->pixels);
1308     info = conv_rect->info;
1309   } else {
1310     /* do not need to scale or modify alpha values, almost done then */
1311     scaled_rect = conv_rect;
1312     goto done;
1313   }
1314 
1315   new_flags = conv_rect->flags;
1316   gst_video_frame_map (&frame, &info, buf, GST_MAP_READWRITE);
1317   if (!gst_video_overlay_rectangle_is_same_alpha_type (conv_rect->flags, flags)) {
1318     if (rectangle->flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA) {
1319       gst_video_overlay_rectangle_unpremultiply (&frame);
1320       new_flags &= ~GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
1321     } else {
1322       gst_video_overlay_rectangle_premultiply (&frame);
1323       new_flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
1324     }
1325   }
1326   gst_video_frame_unmap (&frame);
1327 
1328   scaled_rect = gst_video_overlay_rectangle_new_raw (buf,
1329       0, 0, wanted_width, wanted_height, new_flags);
1330   if (conv_rect->global_alpha != 1.0)
1331     gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
1332         conv_rect->global_alpha);
1333   gst_buffer_unref (buf);
1334 
1335   GST_RECTANGLE_LOCK (rectangle);
1336   rectangle->scaled_rectangles =
1337       g_list_prepend (rectangle->scaled_rectangles, scaled_rect);
1338   GST_RECTANGLE_UNLOCK (rectangle);
1339 
1340 done:
1341 
1342   GST_RECTANGLE_LOCK (rectangle);
1343   if (apply_global_alpha
1344       && scaled_rect->applied_global_alpha != rectangle->global_alpha) {
1345     gst_video_overlay_rectangle_apply_global_alpha (scaled_rect,
1346         rectangle->global_alpha);
1347     gst_video_overlay_rectangle_set_global_alpha (scaled_rect,
1348         rectangle->global_alpha);
1349   } else if (revert_global_alpha && scaled_rect->applied_global_alpha != 1.0) {
1350     gst_video_overlay_rectangle_apply_global_alpha (scaled_rect, 1.0);
1351   }
1352   GST_RECTANGLE_UNLOCK (rectangle);
1353 
1354   return scaled_rect->pixels;
1355 }
1356 
1357 
1358 /**
1359  * gst_video_overlay_rectangle_get_pixels_raw:
1360  * @rectangle: a #GstVideoOverlayRectangle
1361  * @flags: flags
1362  *    If a global_alpha value != 1 is set for the rectangle, the caller
1363  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1364  *    if he wants to apply global-alpha himself. If the flag is not set
1365  *    global_alpha is applied internally before returning the pixel-data.
1366  *
1367  * Returns: (transfer none): a #GstBuffer holding the pixel data with
1368  *    format as originally provided and specified in video meta with
1369  *    width and height of the render dimensions as per
1370  *    gst_video_overlay_rectangle_get_render_rectangle(). This function does
1371  *    not return a reference, the caller should obtain a reference of her own
1372  *    with gst_buffer_ref() if needed.
1373  */
1374 GstBuffer *
gst_video_overlay_rectangle_get_pixels_raw(GstVideoOverlayRectangle * rectangle,GstVideoOverlayFormatFlags flags)1375 gst_video_overlay_rectangle_get_pixels_raw (GstVideoOverlayRectangle *
1376     rectangle, GstVideoOverlayFormatFlags flags)
1377 {
1378   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1379       flags, FALSE, GST_VIDEO_INFO_FORMAT (&rectangle->info));
1380 }
1381 
1382 /**
1383  * gst_video_overlay_rectangle_get_pixels_argb:
1384  * @rectangle: a #GstVideoOverlayRectangle
1385  * @flags: flags
1386  *    If a global_alpha value != 1 is set for the rectangle, the caller
1387  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1388  *    if he wants to apply global-alpha himself. If the flag is not set
1389  *    global_alpha is applied internally before returning the pixel-data.
1390  *
1391  * Returns: (transfer none): a #GstBuffer holding the ARGB pixel data with
1392  *    width and height of the render dimensions as per
1393  *    gst_video_overlay_rectangle_get_render_rectangle(). This function does
1394  *    not return a reference, the caller should obtain a reference of her own
1395  *    with gst_buffer_ref() if needed.
1396  */
1397 GstBuffer *
gst_video_overlay_rectangle_get_pixels_argb(GstVideoOverlayRectangle * rectangle,GstVideoOverlayFormatFlags flags)1398 gst_video_overlay_rectangle_get_pixels_argb (GstVideoOverlayRectangle *
1399     rectangle, GstVideoOverlayFormatFlags flags)
1400 {
1401   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1402       flags, FALSE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB);
1403 }
1404 
1405 /**
1406  * gst_video_overlay_rectangle_get_pixels_ayuv:
1407  * @rectangle: a #GstVideoOverlayRectangle
1408  * @flags: flags
1409  *    If a global_alpha value != 1 is set for the rectangle, the caller
1410  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1411  *    if he wants to apply global-alpha himself. If the flag is not set
1412  *    global_alpha is applied internally before returning the pixel-data.
1413  *
1414  * Returns: (transfer none): a #GstBuffer holding the AYUV pixel data with
1415  *    width and height of the render dimensions as per
1416  *    gst_video_overlay_rectangle_get_render_rectangle(). This function does
1417  *    not return a reference, the caller should obtain a reference of her own
1418  *    with gst_buffer_ref() if needed.
1419  */
1420 GstBuffer *
gst_video_overlay_rectangle_get_pixels_ayuv(GstVideoOverlayRectangle * rectangle,GstVideoOverlayFormatFlags flags)1421 gst_video_overlay_rectangle_get_pixels_ayuv (GstVideoOverlayRectangle *
1422     rectangle, GstVideoOverlayFormatFlags flags)
1423 {
1424   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1425       flags, FALSE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV);
1426 }
1427 
1428 /**
1429  * gst_video_overlay_rectangle_get_pixels_unscaled_raw:
1430  * @rectangle: a #GstVideoOverlayRectangle
1431  * @flags: flags.
1432  *    If a global_alpha value != 1 is set for the rectangle, the caller
1433  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1434  *    if he wants to apply global-alpha himself. If the flag is not set
1435  *    global_alpha is applied internally before returning the pixel-data.
1436  *
1437  * Retrieves the pixel data as it is. This is useful if the caller can
1438  * do the scaling itself when handling the overlaying. The rectangle will
1439  * need to be scaled to the render dimensions, which can be retrieved using
1440  * gst_video_overlay_rectangle_get_render_rectangle().
1441  *
1442  * Returns: (transfer none): a #GstBuffer holding the pixel data with
1443  *    #GstVideoMeta set. This function does not return a reference, the caller
1444  *    should obtain a reference of her own with gst_buffer_ref() if needed.
1445  */
1446 GstBuffer *
gst_video_overlay_rectangle_get_pixels_unscaled_raw(GstVideoOverlayRectangle * rectangle,GstVideoOverlayFormatFlags flags)1447 gst_video_overlay_rectangle_get_pixels_unscaled_raw (GstVideoOverlayRectangle *
1448     rectangle, GstVideoOverlayFormatFlags flags)
1449 {
1450   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1451 
1452   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1453       flags, TRUE, GST_VIDEO_INFO_FORMAT (&rectangle->info));
1454 }
1455 
1456 /**
1457  * gst_video_overlay_rectangle_get_pixels_unscaled_argb:
1458  * @rectangle: a #GstVideoOverlayRectangle
1459  * @flags: flags.
1460  *    If a global_alpha value != 1 is set for the rectangle, the caller
1461  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1462  *    if he wants to apply global-alpha himself. If the flag is not set
1463  *    global_alpha is applied internally before returning the pixel-data.
1464  *
1465  * Retrieves the pixel data as it is. This is useful if the caller can
1466  * do the scaling itself when handling the overlaying. The rectangle will
1467  * need to be scaled to the render dimensions, which can be retrieved using
1468  * gst_video_overlay_rectangle_get_render_rectangle().
1469  *
1470  * Returns: (transfer none): a #GstBuffer holding the ARGB pixel data with
1471  *    #GstVideoMeta set. This function does not return a reference, the caller
1472  *    should obtain a reference of her own with gst_buffer_ref() if needed.
1473  */
1474 GstBuffer *
gst_video_overlay_rectangle_get_pixels_unscaled_argb(GstVideoOverlayRectangle * rectangle,GstVideoOverlayFormatFlags flags)1475 gst_video_overlay_rectangle_get_pixels_unscaled_argb (GstVideoOverlayRectangle *
1476     rectangle, GstVideoOverlayFormatFlags flags)
1477 {
1478   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1479 
1480   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1481       flags, TRUE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB);
1482 }
1483 
1484 /**
1485  * gst_video_overlay_rectangle_get_pixels_unscaled_ayuv:
1486  * @rectangle: a #GstVideoOverlayRectangle
1487  * @flags: flags.
1488  *    If a global_alpha value != 1 is set for the rectangle, the caller
1489  *    should set the #GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA flag
1490  *    if he wants to apply global-alpha himself. If the flag is not set
1491  *    global_alpha is applied internally before returning the pixel-data.
1492  *
1493  * Retrieves the pixel data as it is. This is useful if the caller can
1494  * do the scaling itself when handling the overlaying. The rectangle will
1495  * need to be scaled to the render dimensions, which can be retrieved using
1496  * gst_video_overlay_rectangle_get_render_rectangle().
1497  *
1498  * Returns: (transfer none): a #GstBuffer holding the AYUV pixel data with
1499  *    #GstVideoMeta set. This function does not return a reference, the caller
1500  *    should obtain a reference of her own with gst_buffer_ref() if needed.
1501  */
1502 GstBuffer *
gst_video_overlay_rectangle_get_pixels_unscaled_ayuv(GstVideoOverlayRectangle * rectangle,GstVideoOverlayFormatFlags flags)1503 gst_video_overlay_rectangle_get_pixels_unscaled_ayuv (GstVideoOverlayRectangle *
1504     rectangle, GstVideoOverlayFormatFlags flags)
1505 {
1506   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1507 
1508   return gst_video_overlay_rectangle_get_pixels_raw_internal (rectangle,
1509       flags, TRUE, GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV);
1510 }
1511 
1512 /**
1513  * gst_video_overlay_rectangle_get_flags:
1514  * @rectangle: a #GstVideoOverlayRectangle
1515  *
1516  * Retrieves the flags associated with a #GstVideoOverlayRectangle.
1517  * This is useful if the caller can handle both premultiplied alpha and
1518  * non premultiplied alpha, for example. By knowing whether the rectangle
1519  * uses premultiplied or not, it can request the pixel data in the format
1520  * it is stored in, to avoid unnecessary conversion.
1521  *
1522  * Returns: the #GstVideoOverlayFormatFlags associated with the rectangle.
1523  */
1524 GstVideoOverlayFormatFlags
gst_video_overlay_rectangle_get_flags(GstVideoOverlayRectangle * rectangle)1525 gst_video_overlay_rectangle_get_flags (GstVideoOverlayRectangle * rectangle)
1526 {
1527   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle),
1528       GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
1529 
1530   return rectangle->flags;
1531 }
1532 
1533 /**
1534  * gst_video_overlay_rectangle_get_global_alpha:
1535  * @rectangle: a #GstVideoOverlayRectangle
1536  *
1537  * Retrieves the global-alpha value associated with a #GstVideoOverlayRectangle.
1538  *
1539  * Returns: the global-alpha value associated with the rectangle.
1540  */
1541 gfloat
gst_video_overlay_rectangle_get_global_alpha(GstVideoOverlayRectangle * rectangle)1542 gst_video_overlay_rectangle_get_global_alpha (GstVideoOverlayRectangle *
1543     rectangle)
1544 {
1545   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), -1);
1546 
1547   return rectangle->global_alpha;
1548 }
1549 
1550 /**
1551  * gst_video_overlay_rectangle_set_global_alpha:
1552  * @rectangle: a #GstVideoOverlayRectangle
1553  * @global_alpha: Global alpha value (0 to 1.0)
1554  *
1555  * Sets the global alpha value associated with a #GstVideoOverlayRectangle. Per-
1556  * pixel alpha values are multiplied with this value. Valid
1557  * values: 0 <= global_alpha <= 1; 1 to deactivate.
1558  *
1559  * @rectangle must be writable, meaning its refcount must be 1. You can
1560  * make the rectangles inside a #GstVideoOverlayComposition writable using
1561  * gst_video_overlay_composition_make_writable() or
1562  * gst_video_overlay_composition_copy().
1563  */
1564 void
gst_video_overlay_rectangle_set_global_alpha(GstVideoOverlayRectangle * rectangle,gfloat global_alpha)1565 gst_video_overlay_rectangle_set_global_alpha (GstVideoOverlayRectangle *
1566     rectangle, gfloat global_alpha)
1567 {
1568   g_return_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle));
1569   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST
1570           (rectangle)));
1571   g_return_if_fail (global_alpha >= 0 && global_alpha <= 1);
1572 
1573   if (rectangle->global_alpha != global_alpha) {
1574     rectangle->global_alpha = global_alpha;
1575     if (global_alpha != 1)
1576       rectangle->flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA;
1577     else
1578       rectangle->flags &= ~GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA;
1579     /* update seq_num automatically to signal the consumer, that data has changed
1580      * note, that this might mislead renderers, that can handle global-alpha
1581      * themselves, because what they want to know is whether the actual pixel data
1582      * has changed. */
1583     rectangle->seq_num = gst_video_overlay_get_seqnum ();
1584   }
1585 }
1586 
1587 /**
1588  * gst_video_overlay_rectangle_copy:
1589  * @rectangle: (transfer none): a #GstVideoOverlayRectangle to copy
1590  *
1591  * Makes a copy of @rectangle, so that it is possible to modify it
1592  * (e.g. to change the render co-ordinates or render dimension). The
1593  * actual overlay pixel data buffers contained in the rectangle are not
1594  * copied.
1595  *
1596  * Returns: (transfer full): a new #GstVideoOverlayRectangle equivalent
1597  *     to @rectangle.
1598  */
1599 GstVideoOverlayRectangle *
gst_video_overlay_rectangle_copy(GstVideoOverlayRectangle * rectangle)1600 gst_video_overlay_rectangle_copy (GstVideoOverlayRectangle * rectangle)
1601 {
1602   GstVideoOverlayRectangle *copy;
1603 
1604   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), NULL);
1605 
1606   copy = gst_video_overlay_rectangle_new_raw (rectangle->pixels,
1607       rectangle->x, rectangle->y,
1608       rectangle->render_width, rectangle->render_height, rectangle->flags);
1609   if (rectangle->global_alpha != 1)
1610     gst_video_overlay_rectangle_set_global_alpha (copy,
1611         rectangle->global_alpha);
1612 
1613   return copy;
1614 }
1615 
1616 /**
1617  * gst_video_overlay_rectangle_get_seqnum:
1618  * @rectangle: a #GstVideoOverlayRectangle
1619  *
1620  * Returns the sequence number of this rectangle. Sequence numbers are
1621  * monotonically increasing and unique for overlay compositions and rectangles
1622  * (meaning there will never be a rectangle with the same sequence number as
1623  * a composition).
1624  *
1625  * Using the sequence number of a rectangle as an indicator for changed
1626  * pixel-data of a rectangle is dangereous. Some API calls, like e.g.
1627  * gst_video_overlay_rectangle_set_global_alpha(), automatically update
1628  * the per rectangle sequence number, which is misleading for renderers/
1629  * consumers, that handle global-alpha themselves. For them  the
1630  * pixel-data returned by gst_video_overlay_rectangle_get_pixels_*()
1631  * wont be different for different global-alpha values. In this case a
1632  * renderer could also use the GstBuffer pointers as a hint for changed
1633  * pixel-data.
1634  *
1635  * Returns: the sequence number of @rectangle
1636  */
1637 guint
gst_video_overlay_rectangle_get_seqnum(GstVideoOverlayRectangle * rectangle)1638 gst_video_overlay_rectangle_get_seqnum (GstVideoOverlayRectangle * rectangle)
1639 {
1640   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rectangle), 0);
1641 
1642   return rectangle->seq_num;
1643 }
1644