1 /*
2 * GStreamer
3 * Copyright (C) 2015 Lubosz Sarnecki <lubosz.sarnecki@collabora.co.uk>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:gstgloverlaycompositor
23 * @title: GstGLOverlayCompositor
24 * @short_description: Composite multiple overlays using OpenGL
25 * @see_also: #GstGLMemory, #GstGLContext
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <stdio.h>
33
34 #include "gstgloverlaycompositor.h"
35
36 #include <gst/gl/gl.h>
37 #include <gst/gl/gstglfuncs.h>
38
39 GST_DEBUG_CATEGORY_STATIC (gst_gl_overlay_compositor_debug);
40 #define GST_CAT_DEFAULT gst_gl_overlay_compositor_debug
41
42 /*****************************************************************************
43 * GstGLCompositionOverlay object is internally used by GstGLOverlayCompositor
44 *****************************************************************************/
45
46 #define GST_TYPE_GL_COMPOSITION_OVERLAY (gst_gl_composition_overlay_get_type())
47 #define GST_GL_COMPOSITION_OVERLAY(obj) \
48 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_COMPOSITION_OVERLAY,\
49 GstGLCompositionOverlay))
50
51 typedef struct _GstGLCompositionOverlay GstGLCompositionOverlay;
52 typedef struct _GstGLCompositionOverlayClass GstGLCompositionOverlayClass;
53
54 static GType gst_gl_composition_overlay_get_type (void);
55
56 /* *INDENT-OFF* */
57 const gchar *fragment_shader =
58 "varying vec2 v_texcoord;\n"
59 "uniform sampler2D tex;\n"
60 "void main(void)\n"
61 "{\n"
62 " vec4 t = texture2D(tex, v_texcoord);\n"
63 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
64 " gl_FragColor = t.bgra;\n"
65 #else
66 " gl_FragColor = t.gbar;\n"
67 #endif
68 "}";
69 /* *INDENT-ON* */
70
71 struct _GstGLCompositionOverlay
72 {
73 GstObject parent;
74 GstGLContext *context;
75
76 GLuint vao;
77 GLuint index_buffer;
78 GLuint position_buffer;
79 GLuint texcoord_buffer;
80 GLint position_attrib;
81 GLint texcoord_attrib;
82
83 GLfloat positions[16];
84
85 GLuint texture_id;
86 GstGLMemory *gl_memory;
87 GstVideoOverlayRectangle *rectangle;
88
89 gboolean yinvert;
90 };
91
92 struct _GstGLCompositionOverlayClass
93 {
94 GstObjectClass object_class;
95 };
96
97 G_DEFINE_TYPE (GstGLCompositionOverlay, gst_gl_composition_overlay,
98 GST_TYPE_OBJECT);
99
100 static void
gst_gl_composition_overlay_init_vertex_buffer(GstGLContext * context,gpointer overlay_pointer)101 gst_gl_composition_overlay_init_vertex_buffer (GstGLContext * context,
102 gpointer overlay_pointer)
103 {
104 const GstGLFuncs *gl = context->gl_vtable;
105 GstGLCompositionOverlay *overlay =
106 (GstGLCompositionOverlay *) overlay_pointer;
107
108 /* *INDENT-OFF* */
109 static const GLfloat texcoords[] = {
110 1.0f, 0.0f,
111 0.0f, 0.0f,
112 0.0f, 1.0f,
113 1.0f, 1.0f
114 };
115
116 static const GLushort indices[] = {
117 0, 1, 2, 0, 2, 3
118 };
119 /* *INDENT-ON* */
120
121 if (gl->GenVertexArrays) {
122 gl->GenVertexArrays (1, &overlay->vao);
123 gl->BindVertexArray (overlay->vao);
124 }
125
126 gl->GenBuffers (1, &overlay->position_buffer);
127 gl->BindBuffer (GL_ARRAY_BUFFER, overlay->position_buffer);
128 gl->BufferData (GL_ARRAY_BUFFER, 4 * 4 * sizeof (GLfloat), overlay->positions,
129 GL_STATIC_DRAW);
130
131 /* Load the vertex position */
132 gl->VertexAttribPointer (overlay->position_attrib, 4, GL_FLOAT, GL_FALSE,
133 4 * sizeof (GLfloat), NULL);
134
135 gl->GenBuffers (1, &overlay->texcoord_buffer);
136 gl->BindBuffer (GL_ARRAY_BUFFER, overlay->texcoord_buffer);
137 gl->BufferData (GL_ARRAY_BUFFER, 4 * 2 * sizeof (GLfloat), texcoords,
138 GL_STATIC_DRAW);
139
140 /* Load the texture coordinate */
141 gl->VertexAttribPointer (overlay->texcoord_attrib, 2, GL_FLOAT, GL_FALSE,
142 2 * sizeof (GLfloat), NULL);
143
144 gl->GenBuffers (1, &overlay->index_buffer);
145 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->index_buffer);
146 gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
147 GL_STATIC_DRAW);
148
149 gl->EnableVertexAttribArray (overlay->position_attrib);
150 gl->EnableVertexAttribArray (overlay->texcoord_attrib);
151
152 if (gl->GenVertexArrays) {
153 gl->BindVertexArray (0);
154 }
155
156 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
157 gl->BindBuffer (GL_ARRAY_BUFFER, 0);
158 }
159
160 static void
gst_gl_composition_overlay_free_vertex_buffer(GstGLContext * context,gpointer overlay_pointer)161 gst_gl_composition_overlay_free_vertex_buffer (GstGLContext * context,
162 gpointer overlay_pointer)
163 {
164 const GstGLFuncs *gl = context->gl_vtable;
165 GstGLCompositionOverlay *overlay =
166 (GstGLCompositionOverlay *) overlay_pointer;
167 if (overlay->vao) {
168 gl->DeleteVertexArrays (1, &overlay->vao);
169 overlay->vao = 0;
170 }
171
172 if (overlay->position_buffer) {
173 gl->DeleteBuffers (1, &overlay->position_buffer);
174 overlay->position_buffer = 0;
175 }
176
177 if (overlay->texcoord_buffer) {
178 gl->DeleteBuffers (1, &overlay->texcoord_buffer);
179 overlay->texcoord_buffer = 0;
180 }
181
182 if (overlay->index_buffer) {
183 gl->DeleteBuffers (1, &overlay->index_buffer);
184 overlay->index_buffer = 0;
185 }
186 }
187
188 static void
gst_gl_composition_overlay_bind_vertex_buffer(GstGLCompositionOverlay * overlay)189 gst_gl_composition_overlay_bind_vertex_buffer (GstGLCompositionOverlay *
190 overlay)
191 {
192 const GstGLFuncs *gl = overlay->context->gl_vtable;
193 gl->BindBuffer (GL_ARRAY_BUFFER, overlay->position_buffer);
194 gl->VertexAttribPointer (overlay->position_attrib, 4, GL_FLOAT, GL_FALSE,
195 4 * sizeof (GLfloat), NULL);
196
197 gl->BindBuffer (GL_ARRAY_BUFFER, overlay->texcoord_buffer);
198 gl->VertexAttribPointer (overlay->texcoord_attrib, 2, GL_FLOAT, GL_FALSE,
199 2 * sizeof (GLfloat), NULL);
200
201 gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, overlay->index_buffer);
202
203 gl->EnableVertexAttribArray (overlay->position_attrib);
204 gl->EnableVertexAttribArray (overlay->texcoord_attrib);
205 }
206
207 static void
gst_gl_composition_overlay_finalize(GObject * object)208 gst_gl_composition_overlay_finalize (GObject * object)
209 {
210 GstGLCompositionOverlay *overlay;
211
212 overlay = GST_GL_COMPOSITION_OVERLAY (object);
213
214 if (overlay->gl_memory)
215 gst_memory_unref ((GstMemory *) overlay->gl_memory);
216
217 if (overlay->context) {
218 gst_gl_context_thread_add (overlay->context,
219 gst_gl_composition_overlay_free_vertex_buffer, overlay);
220 gst_object_unref (overlay->context);
221 }
222
223 G_OBJECT_CLASS (gst_gl_composition_overlay_parent_class)->finalize (object);
224 }
225
226
227 static void
gst_gl_composition_overlay_class_init(GstGLCompositionOverlayClass * klass)228 gst_gl_composition_overlay_class_init (GstGLCompositionOverlayClass * klass)
229 {
230 G_OBJECT_CLASS (klass)->finalize = gst_gl_composition_overlay_finalize;
231 }
232
233 static void
gst_gl_composition_overlay_init(GstGLCompositionOverlay * overlay)234 gst_gl_composition_overlay_init (GstGLCompositionOverlay * overlay)
235 {
236 }
237
238 static void
gst_gl_composition_overlay_add_transformation(GstGLCompositionOverlay * overlay,GstBuffer * video_buffer)239 gst_gl_composition_overlay_add_transformation (GstGLCompositionOverlay *
240 overlay, GstBuffer * video_buffer)
241 {
242 gint comp_x, comp_y;
243 guint comp_width, comp_height;
244 GstVideoMeta *meta;
245 guint width, height;
246 gfloat yswap;
247
248 float rel_x, rel_y, rel_w, rel_h;
249
250 meta = gst_buffer_get_video_meta (video_buffer);
251 if (!meta) {
252 GST_WARNING_OBJECT (overlay, "buffer doesn't contain video meta");
253 return;
254 }
255
256 gst_video_overlay_rectangle_get_render_rectangle (overlay->rectangle,
257 &comp_x, &comp_y, &comp_width, &comp_height);
258
259 width = meta->width;
260 height = meta->height;
261
262 /* calculate relative position */
263 rel_x = (float) comp_x / (float) width;
264 rel_y = (float) comp_y / (float) height;
265
266 rel_w = (float) comp_width / (float) width;
267 rel_h = (float) comp_height / (float) height;
268
269 /* transform from [0,1] to [-1,1], invert y axis */
270 rel_x = rel_x * 2.0 - 1.0;
271 rel_y = (1.0 - rel_y) * 2.0 - 1.0;
272
273 rel_w = rel_w * 2.0;
274 rel_h = rel_h * 2.0;
275
276 yswap = overlay->yinvert ? -1. : 1.;
277
278 /* initialize position array */
279 overlay->positions[0] = rel_x + rel_w;
280 overlay->positions[1] = rel_y * yswap;
281 overlay->positions[2] = 0.0;
282 overlay->positions[3] = 1.0;
283 overlay->positions[4] = rel_x;
284 overlay->positions[5] = rel_y * yswap;
285 overlay->positions[6] = 0.0;
286 overlay->positions[7] = 1.0;
287 overlay->positions[8] = rel_x;
288 overlay->positions[9] = (rel_y - rel_h) * yswap;
289 overlay->positions[10] = 0.0;
290 overlay->positions[11] = 1.0;
291 overlay->positions[12] = rel_x + rel_w;
292 overlay->positions[13] = (rel_y - rel_h) * yswap;
293 overlay->positions[14] = 0.0;
294 overlay->positions[15] = 1.0;
295
296 gst_gl_context_thread_add (overlay->context,
297 gst_gl_composition_overlay_free_vertex_buffer, overlay);
298
299 gst_gl_context_thread_add (overlay->context,
300 gst_gl_composition_overlay_init_vertex_buffer, overlay);
301
302 GST_DEBUG
303 ("overlay position: (%d,%d) size: %dx%d video size: %dx%d",
304 comp_x, comp_y, comp_width, comp_height, meta->width, meta->height);
305 }
306
307 /* helper object API functions */
308
309 static GstGLCompositionOverlay *
gst_gl_composition_overlay_new(GstGLContext * context,GstVideoOverlayRectangle * rectangle,GLint position_attrib,GLint texcoord_attrib)310 gst_gl_composition_overlay_new (GstGLContext * context,
311 GstVideoOverlayRectangle * rectangle,
312 GLint position_attrib, GLint texcoord_attrib)
313 {
314 GstGLCompositionOverlay *overlay =
315 g_object_new (GST_TYPE_GL_COMPOSITION_OVERLAY, NULL);
316
317 overlay->gl_memory = NULL;
318 overlay->texture_id = -1;
319 overlay->rectangle = rectangle;
320 overlay->context = gst_object_ref (context);
321 overlay->vao = 0;
322 overlay->position_attrib = position_attrib;
323 overlay->texcoord_attrib = texcoord_attrib;
324
325 GST_DEBUG_OBJECT (overlay, "Created new GstGLCompositionOverlay");
326
327 return overlay;
328 }
329
330 static void
_video_frame_unmap_and_free(gpointer user_data)331 _video_frame_unmap_and_free (gpointer user_data)
332 {
333 GstVideoFrame *frame = user_data;
334
335 gst_video_frame_unmap (frame);
336 g_slice_free (GstVideoFrame, frame);
337 }
338
339 static void
gst_gl_composition_overlay_upload(GstGLCompositionOverlay * overlay,GstBuffer * buf)340 gst_gl_composition_overlay_upload (GstGLCompositionOverlay * overlay,
341 GstBuffer * buf)
342 {
343 GstGLMemory *comp_gl_memory = NULL;
344 GstBuffer *comp_buffer = NULL;
345 GstBuffer *overlay_buffer = NULL;
346 GstVideoInfo vinfo;
347 GstVideoMeta *vmeta;
348 GstVideoFrame *comp_frame;
349 GstVideoFrame gl_frame;
350 GstVideoOverlayFormatFlags flags;
351 GstVideoOverlayFormatFlags alpha_flags;
352
353 flags = gst_video_overlay_rectangle_get_flags (overlay->rectangle);
354
355 if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA) {
356 alpha_flags = GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
357 } else if (!overlay->context->gl_vtable->BlendFuncSeparate) {
358 GST_FIXME_OBJECT (overlay, "No separate blend mode function, "
359 "cannot perform correct blending of unmultipled alpha in OpenGL. "
360 "Software converting");
361 alpha_flags = GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
362 } else {
363 alpha_flags = 0;
364 }
365
366 comp_buffer =
367 gst_video_overlay_rectangle_get_pixels_unscaled_argb (overlay->rectangle,
368 alpha_flags);
369
370 comp_frame = g_slice_new (GstVideoFrame);
371
372 vmeta = gst_buffer_get_video_meta (comp_buffer);
373 gst_video_info_set_format (&vinfo, vmeta->format, vmeta->width,
374 vmeta->height);
375 vinfo.stride[0] = vmeta->stride[0];
376
377 if (gst_video_frame_map (comp_frame, &vinfo, comp_buffer, GST_MAP_READ)) {
378 GstGLVideoAllocationParams *params;
379 GstGLBaseMemoryAllocator *mem_allocator;
380 GstAllocator *allocator;
381
382 allocator =
383 GST_ALLOCATOR (gst_gl_memory_allocator_get_default (overlay->context));
384 mem_allocator = GST_GL_BASE_MEMORY_ALLOCATOR (allocator);
385
386 gst_gl_composition_overlay_add_transformation (overlay, buf);
387
388 params = gst_gl_video_allocation_params_new_wrapped_data (overlay->context,
389 NULL, &comp_frame->info, 0, NULL, GST_GL_TEXTURE_TARGET_2D,
390 GST_GL_RGBA, comp_frame->data[0], comp_frame,
391 _video_frame_unmap_and_free);
392
393 comp_gl_memory =
394 (GstGLMemory *) gst_gl_base_memory_alloc (mem_allocator,
395 (GstGLAllocationParams *) params);
396
397 gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
398 gst_object_unref (allocator);
399
400 overlay_buffer = gst_buffer_new ();
401 gst_buffer_append_memory (overlay_buffer, (GstMemory *) comp_gl_memory);
402
403 if (!gst_video_frame_map (&gl_frame, &comp_frame->info, overlay_buffer,
404 GST_MAP_READ | GST_MAP_GL)) {
405 gst_buffer_unref (overlay_buffer);
406 _video_frame_unmap_and_free (comp_frame);
407 GST_WARNING_OBJECT (overlay, "Cannot upload overlay texture");
408 return;
409 }
410
411 gst_memory_ref ((GstMemory *) comp_gl_memory);
412 overlay->gl_memory = comp_gl_memory;
413 overlay->texture_id = comp_gl_memory->tex_id;
414
415 gst_buffer_unref (overlay_buffer);
416 gst_video_frame_unmap (&gl_frame);
417
418 GST_DEBUG ("uploaded overlay texture %d", overlay->texture_id);
419 } else {
420 g_slice_free (GstVideoFrame, comp_frame);
421 }
422 }
423
424 static void
gst_gl_composition_overlay_draw(GstGLCompositionOverlay * overlay,GstGLShader * shader)425 gst_gl_composition_overlay_draw (GstGLCompositionOverlay * overlay,
426 GstGLShader * shader)
427 {
428 const GstGLFuncs *gl = overlay->context->gl_vtable;
429 if (gl->GenVertexArrays)
430 gl->BindVertexArray (overlay->vao);
431 else
432 gst_gl_composition_overlay_bind_vertex_buffer (overlay);
433
434 if (overlay->texture_id != -1)
435 gl->BindTexture (GL_TEXTURE_2D, overlay->texture_id);
436 gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
437 }
438
439 typedef struct
440 {
441 gboolean yinvert;
442 } GstGLOverlayCompositorPrivate;
443
444 enum
445 {
446 PROP_0,
447 PROP_YINVERT,
448 };
449
450 /********************************************************************
451 * GstGLOverlayCompositor object, the public helper object to render
452 * GstVideoCompositionOverlayMeta
453 ********************************************************************/
454
455 #define DEBUG_INIT \
456 GST_DEBUG_CATEGORY_INIT (gst_gl_overlay_compositor_debug, \
457 "gloverlaycompositor", 0, "overlaycompositor");
458
459 /* this matches what glimagesink does as this was publicized before being used
460 * in other elements that draw in different orientations */
461 #define DEFAULT_YINVERT FALSE
462
463 G_DEFINE_TYPE_WITH_CODE (GstGLOverlayCompositor, gst_gl_overlay_compositor,
464 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstGLOverlayCompositor);
465 DEBUG_INIT);
466
467 static void gst_gl_overlay_compositor_finalize (GObject * object);
468 static void gst_gl_overlay_compositor_set_property (GObject * object,
469 guint prop_id, const GValue * value, GParamSpec * pspec);
470 static void gst_gl_overlay_compositor_get_property (GObject * object,
471 guint prop_id, GValue * value, GParamSpec * pspec);
472
473 static gboolean _is_rectangle_in_overlays (GList * overlays,
474 GstVideoOverlayRectangle * rectangle);
475 static gboolean _is_overlay_in_rectangles (GstVideoOverlayComposition *
476 composition, GstGLCompositionOverlay * overlay);
477
478 static void
gst_gl_overlay_compositor_class_init(GstGLOverlayCompositorClass * klass)479 gst_gl_overlay_compositor_class_init (GstGLOverlayCompositorClass * klass)
480 {
481 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
482
483 gobject_class->finalize = gst_gl_overlay_compositor_finalize;
484 gobject_class->set_property = gst_gl_overlay_compositor_set_property;
485 gobject_class->get_property = gst_gl_overlay_compositor_get_property;
486
487 g_object_class_install_property (gobject_class, PROP_YINVERT,
488 g_param_spec_boolean ("yinvert",
489 "Y-Invert",
490 "Whether to invert the output across a horizintal axis",
491 DEFAULT_YINVERT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
492 }
493
494 static void
gst_gl_overlay_compositor_init(GstGLOverlayCompositor * compositor)495 gst_gl_overlay_compositor_init (GstGLOverlayCompositor * compositor)
496 {
497 GstGLOverlayCompositorPrivate *priv =
498 gst_gl_overlay_compositor_get_instance_private (compositor);
499
500 priv->yinvert = DEFAULT_YINVERT;
501 }
502
503 static void
gst_gl_overlay_compositor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)504 gst_gl_overlay_compositor_set_property (GObject * object, guint prop_id,
505 const GValue * value, GParamSpec * pspec)
506 {
507 GstGLOverlayCompositor *self = GST_GL_OVERLAY_COMPOSITOR (object);
508 GstGLOverlayCompositorPrivate *priv =
509 gst_gl_overlay_compositor_get_instance_private (self);
510
511 switch (prop_id) {
512 case PROP_YINVERT:
513 /* XXX: invalidiate all current rectangles on a change */
514 priv->yinvert = g_value_get_boolean (value);
515 break;
516 default:
517 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
518 break;
519 }
520 }
521
522 static void
gst_gl_overlay_compositor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)523 gst_gl_overlay_compositor_get_property (GObject * object, guint prop_id,
524 GValue * value, GParamSpec * pspec)
525 {
526 GstGLOverlayCompositor *self = GST_GL_OVERLAY_COMPOSITOR (object);
527 GstGLOverlayCompositorPrivate *priv =
528 gst_gl_overlay_compositor_get_instance_private (self);
529
530 switch (prop_id) {
531 case PROP_YINVERT:
532 g_value_set_boolean (value, priv->yinvert);
533 break;
534 default:
535 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
536 break;
537 }
538 }
539
540 static void
gst_gl_overlay_compositor_init_gl(GstGLContext * context,gpointer compositor_pointer)541 gst_gl_overlay_compositor_init_gl (GstGLContext * context,
542 gpointer compositor_pointer)
543 {
544 GstGLOverlayCompositor *compositor =
545 (GstGLOverlayCompositor *) compositor_pointer;
546 GError *error = NULL;
547 const gchar *frag_strs[2];
548
549 frag_strs[0] =
550 gst_gl_shader_string_get_highest_precision (context,
551 GST_GLSL_VERSION_NONE,
552 GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY);
553 frag_strs[1] = fragment_shader;
554
555 if (!(compositor->shader =
556 gst_gl_shader_new_link_with_stages (context, &error,
557 gst_glsl_stage_new_default_vertex (context),
558 gst_glsl_stage_new_with_strings (context, GL_FRAGMENT_SHADER,
559 GST_GLSL_VERSION_NONE,
560 GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, 2,
561 frag_strs), NULL))) {
562 GST_ERROR_OBJECT (compositor, "could not initialize shader: %s",
563 error->message);
564 return;
565 }
566
567 compositor->position_attrib =
568 gst_gl_shader_get_attribute_location (compositor->shader, "a_position");
569 compositor->texcoord_attrib =
570 gst_gl_shader_get_attribute_location (compositor->shader, "a_texcoord");
571 }
572
573 GstGLOverlayCompositor *
gst_gl_overlay_compositor_new(GstGLContext * context)574 gst_gl_overlay_compositor_new (GstGLContext * context)
575 {
576 GstGLOverlayCompositor *compositor =
577 g_object_new (GST_TYPE_GL_OVERLAY_COMPOSITOR, NULL);
578
579 gst_object_ref_sink (compositor);
580
581 compositor->context = gst_object_ref (context);
582
583 gst_gl_context_thread_add (compositor->context,
584 gst_gl_overlay_compositor_init_gl, compositor);
585
586 GST_DEBUG_OBJECT (compositor, "Created new GstGLOverlayCompositor");
587
588 return compositor;
589 }
590
591 static void
gst_gl_overlay_compositor_finalize(GObject * object)592 gst_gl_overlay_compositor_finalize (GObject * object)
593 {
594 GstGLOverlayCompositor *compositor;
595
596 compositor = GST_GL_OVERLAY_COMPOSITOR (object);
597
598 gst_gl_overlay_compositor_free_overlays (compositor);
599
600 if (compositor->context)
601 gst_object_unref (compositor->context);
602
603 if (compositor->shader) {
604 gst_object_unref (compositor->shader);
605 compositor->shader = NULL;
606 }
607
608 G_OBJECT_CLASS (gst_gl_overlay_compositor_parent_class)->finalize (object);
609 }
610
611 static gboolean
_is_rectangle_in_overlays(GList * overlays,GstVideoOverlayRectangle * rectangle)612 _is_rectangle_in_overlays (GList * overlays,
613 GstVideoOverlayRectangle * rectangle)
614 {
615 GList *l;
616
617 for (l = overlays; l != NULL; l = l->next) {
618 GstGLCompositionOverlay *overlay = (GstGLCompositionOverlay *) l->data;
619 if (overlay->rectangle == rectangle)
620 return TRUE;
621 }
622 return FALSE;
623 }
624
625 static gboolean
_is_overlay_in_rectangles(GstVideoOverlayComposition * composition,GstGLCompositionOverlay * overlay)626 _is_overlay_in_rectangles (GstVideoOverlayComposition * composition,
627 GstGLCompositionOverlay * overlay)
628 {
629 guint i;
630
631 for (i = 0; i < gst_video_overlay_composition_n_rectangles (composition); i++) {
632 GstVideoOverlayRectangle *rectangle =
633 gst_video_overlay_composition_get_rectangle (composition, i);
634 if (overlay->rectangle == rectangle)
635 return TRUE;
636 }
637 return FALSE;
638 }
639
640 void
gst_gl_overlay_compositor_free_overlays(GstGLOverlayCompositor * compositor)641 gst_gl_overlay_compositor_free_overlays (GstGLOverlayCompositor * compositor)
642 {
643 GList *l = compositor->overlays;
644 while (l != NULL) {
645 GList *next = l->next;
646 GstGLCompositionOverlay *overlay = (GstGLCompositionOverlay *) l->data;
647 compositor->overlays = g_list_delete_link (compositor->overlays, l);
648 gst_object_unref (overlay);
649 l = next;
650 }
651 g_list_free (compositor->overlays);
652 compositor->overlays = NULL;
653 }
654
655 void
gst_gl_overlay_compositor_upload_overlays(GstGLOverlayCompositor * compositor,GstBuffer * buf)656 gst_gl_overlay_compositor_upload_overlays (GstGLOverlayCompositor * compositor,
657 GstBuffer * buf)
658 {
659 GstVideoOverlayCompositionMeta *composition_meta;
660 GstGLOverlayCompositorPrivate *priv =
661 gst_gl_overlay_compositor_get_instance_private (compositor);
662
663 composition_meta = gst_buffer_get_video_overlay_composition_meta (buf);
664 if (composition_meta) {
665 GstVideoOverlayComposition *composition = NULL;
666 guint num_overlays, i;
667 GList *l = compositor->overlays;
668
669 GST_DEBUG ("GstVideoOverlayCompositionMeta found.");
670
671 composition = composition_meta->overlay;
672 num_overlays = gst_video_overlay_composition_n_rectangles (composition);
673
674 /* add new overlays to list */
675 for (i = 0; i < num_overlays; i++) {
676 GstVideoOverlayRectangle *rectangle =
677 gst_video_overlay_composition_get_rectangle (composition, i);
678
679 if (!_is_rectangle_in_overlays (compositor->overlays, rectangle)) {
680 GstGLCompositionOverlay *overlay =
681 gst_gl_composition_overlay_new (compositor->context, rectangle,
682 compositor->position_attrib, compositor->texcoord_attrib);
683 gst_object_ref_sink (overlay);
684 overlay->yinvert = priv->yinvert;
685
686 gst_gl_composition_overlay_upload (overlay, buf);
687
688 compositor->overlays = g_list_append (compositor->overlays, overlay);
689 }
690 }
691
692 /* remove old overlays from list */
693 while (l != NULL) {
694 GList *next = l->next;
695 GstGLCompositionOverlay *overlay = (GstGLCompositionOverlay *) l->data;
696 if (!_is_overlay_in_rectangles (composition, overlay)) {
697 compositor->overlays = g_list_delete_link (compositor->overlays, l);
698 gst_object_unref (overlay);
699 }
700 l = next;
701 }
702 } else {
703 gst_gl_overlay_compositor_free_overlays (compositor);
704 }
705 }
706
707 void
gst_gl_overlay_compositor_draw_overlays(GstGLOverlayCompositor * compositor)708 gst_gl_overlay_compositor_draw_overlays (GstGLOverlayCompositor * compositor)
709 {
710 const GstGLFuncs *gl = compositor->context->gl_vtable;
711 if (compositor->overlays != NULL) {
712 GList *l;
713
714 gl->Enable (GL_BLEND);
715
716 gst_gl_shader_use (compositor->shader);
717 gl->ActiveTexture (GL_TEXTURE0);
718 gst_gl_shader_set_uniform_1i (compositor->shader, "tex", 0);
719
720 for (l = compositor->overlays; l != NULL; l = l->next) {
721 GstGLCompositionOverlay *overlay = (GstGLCompositionOverlay *) l->data;
722 GstVideoOverlayFormatFlags flags;
723
724 flags = gst_video_overlay_rectangle_get_flags (overlay->rectangle);
725
726 if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA
727 || !gl->BlendFuncSeparate) {
728 gl->BlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
729 } else {
730 gl->BlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
731 GL_ONE_MINUS_SRC_ALPHA);
732 }
733 gst_gl_composition_overlay_draw (overlay, compositor->shader);
734 }
735
736 gl->BindTexture (GL_TEXTURE_2D, 0);
737 gl->Disable (GL_BLEND);
738 }
739 }
740
741 GstCaps *
gst_gl_overlay_compositor_add_caps(GstCaps * caps)742 gst_gl_overlay_compositor_add_caps (GstCaps * caps)
743 {
744 GstCaps *composition_caps;
745 int i;
746
747 composition_caps = gst_caps_copy (caps);
748
749 for (i = 0; i < gst_caps_get_size (composition_caps); i++) {
750 GstCapsFeatures *f = gst_caps_get_features (composition_caps, i);
751 if (!gst_caps_features_is_any (f))
752 gst_caps_features_add (f,
753 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
754 }
755
756 caps = gst_caps_merge (composition_caps, caps);
757
758 return caps;
759 }
760