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