1 /*
2  * GStreamer
3  * Copyright (C) 2012-2014 Matthew Waters <ystree00@gmail.com>
4  * Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 
28 #include "gl.h"
29 #include "gstglupload.h"
30 
31 #if GST_GL_HAVE_PLATFORM_EGL
32 #include "egl/gsteglimage.h"
33 #include "egl/gstglmemoryegl.h"
34 #include "egl/gstglcontext_egl.h"
35 #endif
36 
37 #if GST_GL_HAVE_DMABUF
38 #include <gst/allocators/gstdmabuf.h>
39 #endif
40 
41 #if GST_GL_HAVE_VIV_DIRECTVIV
42 #include <gst/allocators/gstphysmemory.h>
43 #include <gst/gl/gstglfuncs.h>
44 #endif
45 
46 /**
47  * SECTION:gstglupload
48  * @title: GstGLUpload
49  * @short_description: an object that uploads to GL textures
50  * @see_also: #GstGLDownload, #GstGLMemory
51  *
52  * #GstGLUpload is an object that uploads data from system memory into GL textures.
53  *
54  * A #GstGLUpload can be created with gst_gl_upload_new()
55  */
56 
57 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
58 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
59 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
60 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
61 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
62 
63 GST_DEBUG_CATEGORY_STATIC (gst_gl_upload_debug);
64 #define GST_CAT_DEFAULT gst_gl_upload_debug
65 
66 static void gst_gl_upload_finalize (GObject * object);
67 
68 static GstGLTextureTarget
_caps_get_texture_target(GstCaps * caps,GstGLTextureTarget default_target)69 _caps_get_texture_target (GstCaps * caps, GstGLTextureTarget default_target)
70 {
71   GstGLTextureTarget ret = 0;
72   GstStructure *s = gst_caps_get_structure (caps, 0);
73 
74   if (gst_structure_has_field_typed (s, "texture-target", G_TYPE_STRING)) {
75     const gchar *target_str = gst_structure_get_string (s, "texture-target");
76     ret = gst_gl_texture_target_from_string (target_str);
77   }
78 
79   if (!ret)
80     ret = default_target;
81 
82   return ret;
83 }
84 
85 /* Define the maximum number of planes we can upload - handle 2 views per buffer */
86 #define GST_GL_UPLOAD_MAX_PLANES (GST_VIDEO_MAX_PLANES * 2)
87 
88 typedef struct _UploadMethod UploadMethod;
89 
90 struct _GstGLUploadPrivate
91 {
92   GstVideoInfo in_info;
93   GstVideoInfo out_info;
94   GstCaps *in_caps;
95   GstCaps *out_caps;
96 
97   GstBuffer *outbuf;
98 
99   /* all method impl pointers */
100   gpointer *upload_impl;
101 
102   /* current method */
103   const UploadMethod *method;
104   gpointer method_impl;
105   int method_i;
106 
107   /* saved method for reconfigure */
108   int saved_method_i;
109 };
110 
111 #define DEBUG_INIT \
112   GST_DEBUG_CATEGORY_INIT (gst_gl_upload_debug, "glupload", 0, "upload");
113 
114 G_DEFINE_TYPE_WITH_CODE (GstGLUpload, gst_gl_upload, GST_TYPE_OBJECT,
115     G_ADD_PRIVATE (GstGLUpload) DEBUG_INIT);
116 
117 static GstCaps *
_set_caps_features_with_passthrough(const GstCaps * caps,const gchar * feature_name,GstCapsFeatures * passthrough)118 _set_caps_features_with_passthrough (const GstCaps * caps,
119     const gchar * feature_name, GstCapsFeatures * passthrough)
120 {
121   guint i, j, m, n;
122   GstCaps *tmp;
123 
124   tmp = gst_caps_new_empty ();
125 
126   n = gst_caps_get_size (caps);
127   for (i = 0; i < n; i++) {
128     GstCapsFeatures *features, *orig_features;
129     GstStructure *s = gst_caps_get_structure (caps, i);
130 
131     orig_features = gst_caps_get_features (caps, i);
132     features = gst_caps_features_new (feature_name, NULL);
133 
134     if (gst_caps_features_is_any (orig_features)) {
135       /* if we have any features, we add both the features with and without @passthrough */
136       gst_caps_append_structure_full (tmp, gst_structure_copy (s),
137           gst_caps_features_copy (features));
138 
139       m = gst_caps_features_get_size (passthrough);
140       for (j = 0; j < m; j++) {
141         const gchar *feature = gst_caps_features_get_nth (passthrough, j);
142 
143         /* if we already have the features */
144         if (gst_caps_features_contains (features, feature))
145           continue;
146 
147         gst_caps_features_add (features, feature);
148       }
149     } else {
150       m = gst_caps_features_get_size (orig_features);
151       for (j = 0; j < m; j++) {
152         const gchar *feature = gst_caps_features_get_nth (orig_features, j);
153 
154         /* if we already have the features */
155         if (gst_caps_features_contains (features, feature))
156           continue;
157 
158         if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0)
159           continue;
160 
161         if (gst_caps_features_contains (passthrough, feature)) {
162           gst_caps_features_add (features, feature);
163         }
164       }
165     }
166 
167     gst_caps_append_structure_full (tmp, gst_structure_copy (s), features);
168   }
169 
170   return tmp;
171 }
172 
173 static GstCaps *
_caps_intersect_texture_target(GstCaps * caps,GstGLTextureTarget target_mask)174 _caps_intersect_texture_target (GstCaps * caps, GstGLTextureTarget target_mask)
175 {
176   GValue targets = G_VALUE_INIT;
177   GstCaps *ret, *target;
178 
179   target = gst_caps_copy (caps);
180   gst_gl_value_set_texture_target_from_mask (&targets, target_mask);
181   gst_caps_set_value (target, "texture-target", &targets);
182 
183   ret = gst_caps_intersect_full (caps, target, GST_CAPS_INTERSECT_FIRST);
184 
185   g_value_unset (&targets);
186   gst_caps_unref (target);
187   return ret;
188 }
189 
190 typedef enum
191 {
192   METHOD_FLAG_CAN_SHARE_CONTEXT = 1,
193 } GstGLUploadMethodFlags;
194 
195 struct _UploadMethod
196 {
197   const gchar *name;
198   GstGLUploadMethodFlags flags;
199 
200   GstStaticCaps *input_template_caps;
201 
202     gpointer (*new) (GstGLUpload * upload);
203   GstCaps *(*transform_caps) (gpointer impl, GstGLContext * context,
204       GstPadDirection direction, GstCaps * caps);
205     gboolean (*accept) (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
206       GstCaps * out_caps);
207   void (*propose_allocation) (gpointer impl, GstQuery * decide_query,
208       GstQuery * query);
209     GstGLUploadReturn (*perform) (gpointer impl, GstBuffer * buffer,
210       GstBuffer ** outbuf);
211   void (*free) (gpointer impl);
212 } _UploadMethod;
213 
214 struct GLMemoryUpload
215 {
216   GstGLUpload *upload;
217   GstGLTextureTarget input_target;
218   GstGLTextureTarget output_target;
219 };
220 
221 static gpointer
_gl_memory_upload_new(GstGLUpload * upload)222 _gl_memory_upload_new (GstGLUpload * upload)
223 {
224   struct GLMemoryUpload *mem = g_new0 (struct GLMemoryUpload, 1);
225 
226   mem->upload = upload;
227   mem->input_target = GST_GL_TEXTURE_TARGET_NONE;
228   mem->output_target = GST_GL_TEXTURE_TARGET_NONE;
229 
230   return mem;
231 }
232 
233 static GstCaps *
_gl_memory_upload_transform_caps(gpointer impl,GstGLContext * context,GstPadDirection direction,GstCaps * caps)234 _gl_memory_upload_transform_caps (gpointer impl, GstGLContext * context,
235     GstPadDirection direction, GstCaps * caps)
236 {
237   struct GLMemoryUpload *upload = impl;
238   GstCapsFeatures *passthrough =
239       gst_caps_features_from_string
240       (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
241   GstCaps *ret;
242 
243   ret =
244       _set_caps_features_with_passthrough (caps,
245       GST_CAPS_FEATURE_MEMORY_GL_MEMORY, passthrough);
246 
247   gst_caps_features_free (passthrough);
248 
249   if (direction == GST_PAD_SINK) {
250     GstCaps *tmp;
251     GstGLTextureTarget target_mask;
252 
253     if (upload->input_target != GST_GL_TEXTURE_TARGET_NONE) {
254       target_mask = 1 << upload->input_target;
255     } else {
256       target_mask = 1 << GST_GL_TEXTURE_TARGET_2D |
257           1 << GST_GL_TEXTURE_TARGET_RECTANGLE |
258           1 << GST_GL_TEXTURE_TARGET_EXTERNAL_OES;
259     }
260 
261     tmp = _caps_intersect_texture_target (ret, target_mask);
262     gst_caps_unref (ret);
263     ret = tmp;
264   } else {
265     gint i, n;
266 
267     n = gst_caps_get_size (ret);
268     for (i = 0; i < n; i++) {
269       GstStructure *s = gst_caps_get_structure (ret, i);
270 
271       gst_structure_remove_fields (s, "texture-target", NULL);
272     }
273   }
274 
275   return ret;
276 }
277 
278 static gboolean
_gl_memory_upload_accept(gpointer impl,GstBuffer * buffer,GstCaps * in_caps,GstCaps * out_caps)279 _gl_memory_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
280     GstCaps * out_caps)
281 {
282   struct GLMemoryUpload *upload = impl;
283   GstCapsFeatures *features;
284   int i;
285 
286   features = gst_caps_get_features (out_caps, 0);
287   if (!gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY))
288     return FALSE;
289 
290   features = gst_caps_get_features (in_caps, 0);
291   if (!gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)
292       && !gst_caps_features_contains (features,
293           GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY))
294     return FALSE;
295 
296   if (buffer) {
297     GstVideoInfo *in_info = &upload->upload->priv->in_info;
298     guint expected_memories = GST_VIDEO_INFO_N_PLANES (in_info);
299 
300     /* Support stereo views for separated multiview mode */
301     if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
302         GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
303       expected_memories *= GST_VIDEO_INFO_VIEWS (in_info);
304 
305     if (gst_buffer_n_memory (buffer) != expected_memories)
306       return FALSE;
307 
308     for (i = 0; i < expected_memories; i++) {
309       GstMemory *mem = gst_buffer_peek_memory (buffer, i);
310 
311       if (!gst_is_gl_memory (mem))
312         return FALSE;
313     }
314   }
315 
316   return TRUE;
317 }
318 
319 static void
_gl_memory_upload_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)320 _gl_memory_upload_propose_allocation (gpointer impl, GstQuery * decide_query,
321     GstQuery * query)
322 {
323   struct GLMemoryUpload *upload = impl;
324   GstBufferPool *pool = NULL;
325   guint n_pools, i;
326   GstCaps *caps;
327   GstCapsFeatures *features;
328 
329   gst_query_parse_allocation (query, &caps, NULL);
330   if (caps == NULL)
331     goto invalid_caps;
332   features = gst_caps_get_features (caps, 0);
333 
334   /* Only offer our custom allocator if that type of memory was negotiated. */
335   if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
336     GstAllocator *allocator;
337     GstAllocationParams params;
338     gst_allocation_params_init (&params);
339 
340     allocator =
341         GST_ALLOCATOR (gst_gl_memory_allocator_get_default (upload->
342             upload->context));
343     gst_query_add_allocation_param (query, allocator, &params);
344     gst_object_unref (allocator);
345 
346 #if GST_GL_HAVE_PLATFORM_EGL
347     if (upload->upload->context
348         && gst_gl_context_get_gl_platform (upload->upload->context) ==
349         GST_GL_PLATFORM_EGL) {
350       allocator =
351           GST_ALLOCATOR (gst_allocator_find (GST_GL_MEMORY_EGL_ALLOCATOR_NAME));
352       gst_query_add_allocation_param (query, allocator, &params);
353       gst_object_unref (allocator);
354     }
355 #endif
356   }
357 
358   n_pools = gst_query_get_n_allocation_pools (query);
359   for (i = 0; i < n_pools; i++) {
360     gst_query_parse_nth_allocation_pool (query, i, &pool, NULL, NULL, NULL);
361     if (!GST_IS_GL_BUFFER_POOL (pool)) {
362       gst_object_unref (pool);
363       pool = NULL;
364     }
365   }
366 
367   if (!pool) {
368     GstStructure *config;
369     GstVideoInfo info;
370     gsize size;
371 
372 
373     if (!gst_video_info_from_caps (&info, caps))
374       goto invalid_caps;
375 
376     pool = gst_gl_buffer_pool_new (upload->upload->context);
377     config = gst_buffer_pool_get_config (pool);
378 
379     /* the normal size of a frame */
380     size = info.size;
381     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
382     gst_buffer_pool_config_add_option (config,
383         GST_BUFFER_POOL_OPTION_GL_SYNC_META);
384     if (upload->upload->priv->out_caps) {
385       GstGLTextureTarget target;
386       const gchar *target_pool_option_str;
387 
388       target =
389           _caps_get_texture_target (upload->upload->priv->out_caps,
390           GST_GL_TEXTURE_TARGET_2D);
391       target_pool_option_str =
392           gst_gl_texture_target_to_buffer_pool_option (target);
393       gst_buffer_pool_config_add_option (config, target_pool_option_str);
394     }
395 
396     if (!gst_buffer_pool_set_config (pool, config)) {
397       gst_object_unref (pool);
398       goto config_failed;
399     }
400 
401     gst_query_add_allocation_pool (query, pool, size, 1, 0);
402   }
403 
404   if (pool)
405     gst_object_unref (pool);
406 
407   return;
408 
409 invalid_caps:
410   {
411     GST_WARNING_OBJECT (upload->upload, "invalid caps specified");
412     return;
413   }
414 config_failed:
415   {
416     GST_WARNING_OBJECT (upload->upload, "failed setting config");
417     return;
418   }
419 }
420 
421 static GstGLUploadReturn
_gl_memory_upload_perform(gpointer impl,GstBuffer * buffer,GstBuffer ** outbuf)422 _gl_memory_upload_perform (gpointer impl, GstBuffer * buffer,
423     GstBuffer ** outbuf)
424 {
425   struct GLMemoryUpload *upload = impl;
426   GstGLMemory *gl_mem;
427   int i, n;
428 
429   n = gst_buffer_n_memory (buffer);
430   for (i = 0; i < n; i++) {
431     GstMemory *mem = gst_buffer_peek_memory (buffer, i);
432 
433     gl_mem = (GstGLMemory *) mem;
434     if (!gst_gl_context_can_share (upload->upload->context,
435             gl_mem->mem.context))
436       return GST_GL_UPLOAD_UNSHARED_GL_CONTEXT;
437 
438     if (upload->output_target == GST_GL_TEXTURE_TARGET_NONE &&
439         upload->upload->priv->out_caps) {
440       upload->output_target =
441           _caps_get_texture_target (upload->upload->priv->out_caps,
442           GST_GL_TEXTURE_TARGET_NONE);
443     }
444 
445     /* always track the last input texture target so ::transform_caps() can
446      * use it to build the output caps */
447     upload->input_target = gl_mem->tex_target;
448     if (upload->output_target != gl_mem->tex_target) {
449       *outbuf = NULL;
450       return GST_GL_UPLOAD_RECONFIGURE;
451     }
452 
453     if (gst_is_gl_memory_pbo (mem))
454       gst_gl_memory_pbo_upload_transfer ((GstGLMemoryPBO *) mem);
455   }
456 
457   *outbuf = gst_buffer_ref (buffer);
458 
459   return GST_GL_UPLOAD_DONE;
460 }
461 
462 static void
_gl_memory_upload_free(gpointer impl)463 _gl_memory_upload_free (gpointer impl)
464 {
465   g_free (impl);
466 }
467 
468 
469 static GstStaticCaps _gl_memory_upload_caps =
470 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
471     (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, GST_GL_MEMORY_VIDEO_FORMATS_STR));
472 
473 static const UploadMethod _gl_memory_upload = {
474   "GLMemory",
475   METHOD_FLAG_CAN_SHARE_CONTEXT,
476   &_gl_memory_upload_caps,
477   &_gl_memory_upload_new,
478   &_gl_memory_upload_transform_caps,
479   &_gl_memory_upload_accept,
480   &_gl_memory_upload_propose_allocation,
481   &_gl_memory_upload_perform,
482   &_gl_memory_upload_free
483 };
484 
485 #if GST_GL_HAVE_DMABUF
486 struct DmabufUpload
487 {
488   GstGLUpload *upload;
489 
490   GstEGLImage *eglimage[GST_VIDEO_MAX_PLANES];
491   GstGLFormat formats[GST_VIDEO_MAX_PLANES];
492   GstBuffer *outbuf;
493   GstGLVideoAllocationParams *params;
494   guint n_mem;
495 
496   gboolean direct;
497   GstVideoInfo out_info;
498   /* only used for pointer comparision */
499   gpointer out_caps;
500 };
501 
502 static GstStaticCaps _dma_buf_upload_caps =
503     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
504     (GST_CAPS_FEATURE_MEMORY_DMABUF,
505         GST_GL_MEMORY_VIDEO_FORMATS_STR) ";"
506     GST_VIDEO_CAPS_MAKE (GST_GL_MEMORY_VIDEO_FORMATS_STR));
507 
508 static gpointer
_dma_buf_upload_new(GstGLUpload * upload)509 _dma_buf_upload_new (GstGLUpload * upload)
510 {
511   struct DmabufUpload *dmabuf = g_new0 (struct DmabufUpload, 1);
512   dmabuf->upload = upload;
513   return dmabuf;
514 }
515 
516 static GstCaps *
_dma_buf_upload_transform_caps(gpointer impl,GstGLContext * context,GstPadDirection direction,GstCaps * caps)517 _dma_buf_upload_transform_caps (gpointer impl, GstGLContext * context,
518     GstPadDirection direction, GstCaps * caps)
519 {
520   struct DmabufUpload *dmabuf = impl;
521   GstCapsFeatures *passthrough;
522   GstCaps *ret;
523 
524   if (context) {
525     /* Don't propose DMABuf caps feature unless it can be supported */
526     if (gst_gl_context_get_gl_platform (context) != GST_GL_PLATFORM_EGL)
527       return NULL;
528 
529     if (!gst_gl_context_check_feature (context, "EGL_KHR_image_base"))
530       return NULL;
531   }
532 
533   passthrough = gst_caps_features_from_string
534       (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
535 
536   if (direction == GST_PAD_SINK) {
537     GstCaps *tmp;
538 
539     ret =
540         _set_caps_features_with_passthrough (caps,
541         GST_CAPS_FEATURE_MEMORY_GL_MEMORY, passthrough);
542 
543     tmp = _caps_intersect_texture_target (ret, 1 << GST_GL_TEXTURE_TARGET_2D);
544     gst_caps_unref (ret);
545     ret = tmp;
546   } else {
547     gint i, n;
548     GstCaps *tmp;
549 
550     ret =
551         _set_caps_features_with_passthrough (caps,
552         GST_CAPS_FEATURE_MEMORY_DMABUF, passthrough);
553     tmp =
554         _set_caps_features_with_passthrough (caps,
555         GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, passthrough);
556     gst_caps_append (ret, tmp);
557 
558 
559     n = gst_caps_get_size (ret);
560     for (i = 0; i < n; i++) {
561       GstStructure *s = gst_caps_get_structure (ret, i);
562 
563       gst_structure_remove_fields (s, "texture-target", NULL);
564     }
565   }
566 
567   gst_caps_features_free (passthrough);
568 
569   GST_DEBUG_OBJECT (dmabuf->upload, "transformed %" GST_PTR_FORMAT " into %"
570       GST_PTR_FORMAT, caps, ret);
571 
572   return ret;
573 }
574 
575 static GQuark
_eglimage_quark(gint plane)576 _eglimage_quark (gint plane)
577 {
578   static GQuark quark[5] = { 0 };
579   static const gchar *quark_str[] = {
580     "GstGLDMABufEGLImage0",
581     "GstGLDMABufEGLImage1",
582     "GstGLDMABufEGLImage2",
583     "GstGLDMABufEGLImage3",
584     "GstGLDMABufEGLImage",
585   };
586 
587   if (!quark[plane])
588     quark[plane] = g_quark_from_static_string (quark_str[plane]);
589 
590   return quark[plane];
591 }
592 
593 static GstEGLImage *
_get_cached_eglimage(GstMemory * mem,gint plane)594 _get_cached_eglimage (GstMemory * mem, gint plane)
595 {
596   return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
597       _eglimage_quark (plane));
598 }
599 
600 static void
_set_cached_eglimage(GstMemory * mem,GstEGLImage * eglimage,gint plane)601 _set_cached_eglimage (GstMemory * mem, GstEGLImage * eglimage, gint plane)
602 {
603   return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
604       _eglimage_quark (plane), eglimage, (GDestroyNotify) gst_egl_image_unref);
605 }
606 
607 static gboolean
_dma_buf_upload_accept(gpointer impl,GstBuffer * buffer,GstCaps * in_caps,GstCaps * out_caps)608 _dma_buf_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
609     GstCaps * out_caps)
610 {
611   struct DmabufUpload *dmabuf = impl;
612   GstVideoInfo *in_info = &dmabuf->upload->priv->in_info;
613   GstVideoInfo *out_info = &dmabuf->out_info;
614   guint n_planes = GST_VIDEO_INFO_N_PLANES (in_info);
615   GstVideoMeta *meta;
616   guint n_mem;
617   GstMemory *mems[GST_VIDEO_MAX_PLANES];
618   gsize offset[GST_VIDEO_MAX_PLANES];
619   gint fd[GST_VIDEO_MAX_PLANES];
620   guint i;
621 
622   n_mem = gst_buffer_n_memory (buffer);
623   meta = gst_buffer_get_video_meta (buffer);
624 
625   /* dmabuf upload is only supported with EGL contexts. */
626   if (gst_gl_context_get_gl_platform (dmabuf->upload->context) !=
627       GST_GL_PLATFORM_EGL)
628     return FALSE;
629 
630   if (!gst_gl_context_check_feature (dmabuf->upload->context,
631           "EGL_KHR_image_base"))
632     return FALSE;
633 
634   /* This will eliminate most non-dmabuf out there */
635   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (buffer, 0)))
636     return FALSE;
637 
638   /* We cannot have multiple dmabuf per plane */
639   if (n_mem > n_planes)
640     return FALSE;
641 
642   /* Update video info based on video meta */
643   if (meta) {
644     in_info->width = meta->width;
645     in_info->height = meta->height;
646 
647     for (i = 0; i < meta->n_planes; i++) {
648       in_info->offset[i] = meta->offset[i];
649       in_info->stride[i] = meta->stride[i];
650     }
651   }
652 
653   if (out_caps != dmabuf->out_caps) {
654     dmabuf->out_caps = out_caps;
655     if (!gst_video_info_from_caps (out_info, out_caps))
656       return FALSE;
657   }
658 
659   if (dmabuf->params)
660     gst_gl_allocation_params_free ((GstGLAllocationParams *) dmabuf->params);
661   if (!(dmabuf->params =
662           gst_gl_video_allocation_params_new_wrapped_gl_handle (dmabuf->
663               upload->context, NULL, out_info, -1, NULL,
664               GST_GL_TEXTURE_TARGET_2D, 0, NULL, NULL, NULL)))
665     return FALSE;
666 
667   /* Find and validate all memories */
668   for (i = 0; i < n_planes; i++) {
669     guint plane_size;
670     guint length;
671     guint mem_idx;
672     gsize mem_skip;
673 
674     plane_size = gst_gl_get_plane_data_size (in_info, NULL, i);
675 
676     if (!gst_buffer_find_memory (buffer, in_info->offset[i], plane_size,
677             &mem_idx, &length, &mem_skip))
678       return FALSE;
679 
680     /* We can't have more then one dmabuf per plane */
681     if (length != 1)
682       return FALSE;
683 
684     mems[i] = gst_buffer_peek_memory (buffer, mem_idx);
685 
686     /* And all memory found must be dmabuf */
687     if (!gst_is_dmabuf_memory (mems[i]))
688       return FALSE;
689 
690     offset[i] = mems[i]->offset + mem_skip;
691     fd[i] = gst_dmabuf_memory_get_fd (mems[i]);
692   }
693 
694   if (dmabuf->direct)
695     dmabuf->n_mem = 1;
696   else
697     dmabuf->n_mem = n_planes;
698 
699   /* Now create an EGLImage for each dmabufs */
700   for (i = 0; i < dmabuf->n_mem; i++) {
701     gint cache_id = dmabuf->direct ? 4 : i;
702 
703     /* check if one is cached */
704     dmabuf->eglimage[i] = _get_cached_eglimage (mems[i], cache_id);
705     if (dmabuf->eglimage[i]) {
706       dmabuf->formats[i] = dmabuf->eglimage[i]->format;
707       continue;
708     }
709 
710     /* otherwise create one and cache it */
711     if (dmabuf->direct)
712       dmabuf->eglimage[i] =
713           gst_egl_image_from_dmabuf_direct (dmabuf->upload->context, fd, offset,
714           in_info);
715     else
716       dmabuf->eglimage[i] = gst_egl_image_from_dmabuf (dmabuf->upload->context,
717           fd[i], in_info, i, offset[i]);
718 
719     if (!dmabuf->eglimage[i])
720       return FALSE;
721 
722     _set_cached_eglimage (mems[i], dmabuf->eglimage[i], cache_id);
723     dmabuf->formats[i] = dmabuf->eglimage[i]->format;
724   }
725 
726   return TRUE;
727 }
728 
729 static void
_dma_buf_upload_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)730 _dma_buf_upload_propose_allocation (gpointer impl, GstQuery * decide_query,
731     GstQuery * query)
732 {
733   /* nothing to do for now. */
734 }
735 
736 static void
_dma_buf_upload_perform_gl_thread(GstGLContext * context,struct DmabufUpload * dmabuf)737 _dma_buf_upload_perform_gl_thread (GstGLContext * context,
738     struct DmabufUpload *dmabuf)
739 {
740   GstGLMemoryAllocator *allocator;
741 
742   allocator =
743       GST_GL_MEMORY_ALLOCATOR (gst_allocator_find
744       (GST_GL_MEMORY_EGL_ALLOCATOR_NAME));
745 
746   /* FIXME: buffer pool */
747   dmabuf->outbuf = gst_buffer_new ();
748   gst_gl_memory_setup_buffer (allocator, dmabuf->outbuf, dmabuf->params,
749       dmabuf->formats, (gpointer *) dmabuf->eglimage, dmabuf->n_mem);
750   gst_object_unref (allocator);
751 }
752 
753 static GstGLUploadReturn
_dma_buf_upload_perform(gpointer impl,GstBuffer * buffer,GstBuffer ** outbuf)754 _dma_buf_upload_perform (gpointer impl, GstBuffer * buffer, GstBuffer ** outbuf)
755 {
756   struct DmabufUpload *dmabuf = impl;
757 
758   gst_gl_context_thread_add (dmabuf->upload->context,
759       (GstGLContextThreadFunc) _dma_buf_upload_perform_gl_thread, dmabuf);
760 
761   if (!dmabuf->outbuf)
762     return GST_GL_UPLOAD_ERROR;
763 
764   gst_buffer_add_parent_buffer_meta (dmabuf->outbuf, buffer);
765 
766   *outbuf = dmabuf->outbuf;
767   dmabuf->outbuf = NULL;
768 
769   return GST_GL_UPLOAD_DONE;
770 }
771 
772 static void
_dma_buf_upload_free(gpointer impl)773 _dma_buf_upload_free (gpointer impl)
774 {
775   struct DmabufUpload *dmabuf = impl;
776 
777   if (dmabuf->params)
778     gst_gl_allocation_params_free ((GstGLAllocationParams *) dmabuf->params);
779 
780   g_free (impl);
781 }
782 
783 static const UploadMethod _dma_buf_upload = {
784   "Dmabuf",
785   0,
786   &_dma_buf_upload_caps,
787   &_dma_buf_upload_new,
788   &_dma_buf_upload_transform_caps,
789   &_dma_buf_upload_accept,
790   &_dma_buf_upload_propose_allocation,
791   &_dma_buf_upload_perform,
792   &_dma_buf_upload_free
793 };
794 
795 /* a variant of the DMABuf uploader that relies on HW color convertion instead
796  * of shaders */
797 
798 static gpointer
_direct_dma_buf_upload_new(GstGLUpload * upload)799 _direct_dma_buf_upload_new (GstGLUpload * upload)
800 {
801   struct DmabufUpload *dmabuf = _dma_buf_upload_new (upload);
802   dmabuf->direct = TRUE;
803   gst_video_info_init (&dmabuf->out_info);
804   return dmabuf;
805 }
806 
807 static GstCaps *
_direct_dma_buf_upload_transform_caps(gpointer impl,GstGLContext * context,GstPadDirection direction,GstCaps * caps)808 _direct_dma_buf_upload_transform_caps (gpointer impl, GstGLContext * context,
809     GstPadDirection direction, GstCaps * caps)
810 {
811   struct DmabufUpload *dmabuf = impl;
812   GstCapsFeatures *passthrough =
813       gst_caps_features_from_string
814       (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
815   GstCaps *ret;
816 
817   if (direction == GST_PAD_SINK) {
818     gint i, n;
819     GstCaps *tmp;
820 
821     ret =
822         _set_caps_features_with_passthrough (caps,
823         GST_CAPS_FEATURE_MEMORY_GL_MEMORY, passthrough);
824 
825     gst_caps_set_simple (ret, "format", G_TYPE_STRING, "RGBA", NULL);
826 
827     n = gst_caps_get_size (ret);
828     for (i = 0; i < n; i++) {
829       GstStructure *s = gst_caps_get_structure (ret, i);
830 
831       gst_structure_remove_fields (s, "chroma-site", NULL);
832       gst_structure_remove_fields (s, "colorimetry", NULL);
833     }
834     tmp = _caps_intersect_texture_target (ret, 1 << GST_GL_TEXTURE_TARGET_2D);
835     gst_caps_unref (ret);
836     ret = tmp;
837   } else {
838     gint i, n;
839     GstCaps *tmp;
840     GValue formats = G_VALUE_INIT;
841     gchar *format_str = g_strdup (GST_GL_MEMORY_VIDEO_FORMATS_STR);
842 
843     ret =
844         _set_caps_features_with_passthrough (caps,
845         GST_CAPS_FEATURE_MEMORY_DMABUF, passthrough);
846     tmp =
847         _set_caps_features_with_passthrough (caps,
848         GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, passthrough);
849     gst_caps_append (ret, tmp);
850 
851     g_value_init (&formats, GST_TYPE_LIST);
852     gst_value_deserialize (&formats, format_str);
853     tmp = gst_caps_copy (ret);
854     gst_caps_set_value (tmp, "format", &formats);
855     gst_caps_append (ret, tmp);
856     g_free (format_str);
857     g_value_unset (&formats);
858 
859     n = gst_caps_get_size (ret);
860     for (i = 0; i < n; i++) {
861       GstStructure *s = gst_caps_get_structure (ret, i);
862 
863       gst_structure_remove_fields (s, "texture-target", NULL);
864     }
865   }
866 
867   gst_caps_features_free (passthrough);
868 
869   GST_DEBUG_OBJECT (dmabuf->upload, "transformed %" GST_PTR_FORMAT " into %"
870       GST_PTR_FORMAT, caps, ret);
871 
872   return ret;
873 }
874 
875 static const UploadMethod _direct_dma_buf_upload = {
876   "DirectDmabuf",
877   0,
878   &_dma_buf_upload_caps,
879   &_direct_dma_buf_upload_new,
880   &_direct_dma_buf_upload_transform_caps,
881   &_dma_buf_upload_accept,
882   &_dma_buf_upload_propose_allocation,
883   &_dma_buf_upload_perform,
884   &_dma_buf_upload_free
885 };
886 
887 #endif /* GST_GL_HAVE_DMABUF */
888 
889 struct GLUploadMeta
890 {
891   GstGLUpload *upload;
892 
893   gboolean result;
894   GstVideoGLTextureUploadMeta *meta;
895   guint texture_ids[GST_GL_UPLOAD_MAX_PLANES];
896   GstBufferPool *pool;
897 };
898 
899 static gpointer
_upload_meta_upload_new(GstGLUpload * upload)900 _upload_meta_upload_new (GstGLUpload * upload)
901 {
902   struct GLUploadMeta *meta = g_new0 (struct GLUploadMeta, 1);
903 
904   meta->upload = upload;
905   meta->pool = NULL;
906 
907   return meta;
908 }
909 
910 static GstCaps *
_upload_meta_upload_transform_caps(gpointer impl,GstGLContext * context,GstPadDirection direction,GstCaps * caps)911 _upload_meta_upload_transform_caps (gpointer impl, GstGLContext * context,
912     GstPadDirection direction, GstCaps * caps)
913 {
914   GstCapsFeatures *passthrough =
915       gst_caps_features_from_string
916       (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
917   GstCaps *ret;
918 
919   if (direction == GST_PAD_SINK) {
920     GstCaps *tmp;
921 
922     ret =
923         _set_caps_features_with_passthrough (caps,
924         GST_CAPS_FEATURE_MEMORY_GL_MEMORY, passthrough);
925 
926     tmp = _caps_intersect_texture_target (ret, 1 << GST_GL_TEXTURE_TARGET_2D);
927     gst_caps_unref (ret);
928     ret = tmp;
929   } else {
930     gint i, n;
931 
932     ret =
933         _set_caps_features_with_passthrough (caps,
934         GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, passthrough);
935     gst_caps_set_simple (ret, "format", G_TYPE_STRING, "RGBA", NULL);
936 
937     n = gst_caps_get_size (ret);
938     for (i = 0; i < n; i++) {
939       GstStructure *s = gst_caps_get_structure (ret, i);
940 
941       gst_structure_remove_fields (s, "texture-target", NULL);
942     }
943   }
944 
945   gst_caps_features_free (passthrough);
946 
947   return ret;
948 }
949 
950 static gboolean
_upload_meta_upload_accept(gpointer impl,GstBuffer * buffer,GstCaps * in_caps,GstCaps * out_caps)951 _upload_meta_upload_accept (gpointer impl, GstBuffer * buffer,
952     GstCaps * in_caps, GstCaps * out_caps)
953 {
954   struct GLUploadMeta *upload = impl;
955   GstCapsFeatures *features;
956   GstVideoGLTextureUploadMeta *meta;
957   gboolean ret = TRUE;
958   GstStructure *config;
959   gsize size;
960 
961   features = gst_caps_get_features (in_caps, 0);
962 
963   if (!gst_caps_features_contains (features,
964           GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META))
965     ret = FALSE;
966 
967   features = gst_caps_get_features (out_caps, 0);
968   if (!gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY))
969     ret = FALSE;
970 
971   if (!ret)
972     return ret;
973 
974   if (upload->pool == NULL)
975     upload->pool = gst_gl_buffer_pool_new (upload->upload->context);
976 
977   if (!gst_buffer_pool_is_active (upload->pool)) {
978     config = gst_buffer_pool_get_config (upload->pool);
979 
980     size = upload->upload->priv->in_info.size;
981     gst_buffer_pool_config_set_params (config, in_caps, size, 0, 0);
982 
983     if (!gst_buffer_pool_set_config (upload->pool, config)) {
984       GST_WARNING_OBJECT (upload->upload, "failed to set bufferpool config");
985       return FALSE;
986     }
987     gst_buffer_pool_set_active (upload->pool, TRUE);
988   }
989 
990   if (buffer) {
991     if ((meta = gst_buffer_get_video_gl_texture_upload_meta (buffer)) == NULL)
992       return FALSE;
993 
994     if (meta->texture_type[0] != GST_VIDEO_GL_TEXTURE_TYPE_RGBA) {
995       GST_FIXME_OBJECT (upload, "only single rgba texture supported");
996       return FALSE;
997     }
998 
999     if (meta->texture_orientation !=
1000         GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL) {
1001       GST_FIXME_OBJECT (upload, "only x-normal, y-normal textures supported");
1002       return FALSE;
1003     }
1004   }
1005 
1006   return TRUE;
1007 }
1008 
1009 static void
_upload_meta_upload_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)1010 _upload_meta_upload_propose_allocation (gpointer impl, GstQuery * decide_query,
1011     GstQuery * query)
1012 {
1013   struct GLUploadMeta *upload = impl;
1014   GstStructure *gl_context;
1015   gchar *platform, *gl_apis;
1016   gpointer handle;
1017 
1018   gl_apis =
1019       gst_gl_api_to_string (gst_gl_context_get_gl_api (upload->upload->
1020           context));
1021   platform =
1022       gst_gl_platform_to_string (gst_gl_context_get_gl_platform (upload->
1023           upload->context));
1024   handle = (gpointer) gst_gl_context_get_gl_context (upload->upload->context);
1025 
1026   gl_context =
1027       gst_structure_new ("GstVideoGLTextureUploadMeta", "gst.gl.GstGLContext",
1028       GST_TYPE_GL_CONTEXT, upload->upload->context, "gst.gl.context.handle",
1029       G_TYPE_POINTER, handle, "gst.gl.context.type", G_TYPE_STRING, platform,
1030       "gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL);
1031   gst_query_add_allocation_meta (query,
1032       GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, gl_context);
1033 
1034   g_free (gl_apis);
1035   g_free (platform);
1036   gst_structure_free (gl_context);
1037 }
1038 
1039 /*
1040  * Uploads using gst_video_gl_texture_upload_meta_upload().
1041  * i.e. consumer of GstVideoGLTextureUploadMeta
1042  */
1043 static void
_do_upload_with_meta(GstGLContext * context,struct GLUploadMeta * upload)1044 _do_upload_with_meta (GstGLContext * context, struct GLUploadMeta *upload)
1045 {
1046   if (!gst_video_gl_texture_upload_meta_upload (upload->meta,
1047           upload->texture_ids)) {
1048     upload->result = FALSE;
1049     return;
1050   }
1051 
1052   upload->result = TRUE;
1053 }
1054 
1055 static GstGLUploadReturn
_upload_meta_upload_perform(gpointer impl,GstBuffer * buffer,GstBuffer ** outbuf)1056 _upload_meta_upload_perform (gpointer impl, GstBuffer * buffer,
1057     GstBuffer ** outbuf)
1058 {
1059   struct GLUploadMeta *upload = impl;
1060   int i;
1061   GstVideoInfo *in_info = &upload->upload->priv->in_info;
1062   guint max_planes = GST_VIDEO_INFO_N_PLANES (in_info);
1063 
1064   /* Support stereo views for separated multiview mode */
1065   if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
1066       GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
1067     max_planes *= GST_VIDEO_INFO_VIEWS (in_info);
1068 
1069   GST_LOG_OBJECT (upload, "Attempting upload with GstVideoGLTextureUploadMeta");
1070 
1071   upload->meta = gst_buffer_get_video_gl_texture_upload_meta (buffer);
1072 
1073   if (gst_buffer_pool_acquire_buffer (upload->pool, outbuf,
1074           NULL) != GST_FLOW_OK) {
1075     GST_WARNING_OBJECT (upload, "failed to acquire buffer from bufferpool");
1076     return GST_GL_UPLOAD_ERROR;
1077   }
1078 
1079   for (i = 0; i < GST_GL_UPLOAD_MAX_PLANES; i++) {
1080     guint tex_id = 0;
1081 
1082     if (i < max_planes) {
1083       GstMemory *mem = gst_buffer_peek_memory (*outbuf, i);
1084       tex_id = ((GstGLMemory *) mem)->tex_id;
1085     }
1086 
1087     upload->texture_ids[i] = tex_id;
1088   }
1089 
1090   GST_LOG ("Uploading with GLTextureUploadMeta with textures "
1091       "%i,%i,%i,%i / %i,%i,%i,%i",
1092       upload->texture_ids[0], upload->texture_ids[1],
1093       upload->texture_ids[2], upload->texture_ids[3],
1094       upload->texture_ids[4], upload->texture_ids[5],
1095       upload->texture_ids[6], upload->texture_ids[7]);
1096 
1097   gst_gl_context_thread_add (upload->upload->context,
1098       (GstGLContextThreadFunc) _do_upload_with_meta, upload);
1099 
1100   if (!upload->result)
1101     return GST_GL_UPLOAD_ERROR;
1102 
1103   return GST_GL_UPLOAD_DONE;
1104 }
1105 
1106 static void
_upload_meta_upload_free(gpointer impl)1107 _upload_meta_upload_free (gpointer impl)
1108 {
1109   struct GLUploadMeta *upload = impl;
1110 
1111   g_return_if_fail (impl != NULL);
1112 
1113   if (upload->pool)
1114     gst_object_unref (upload->pool);
1115 
1116   g_free (upload);
1117 }
1118 
1119 static GstStaticCaps _upload_meta_upload_caps =
1120 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
1121     (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, "RGBA"));
1122 
1123 static const UploadMethod _upload_meta_upload = {
1124   "UploadMeta",
1125   METHOD_FLAG_CAN_SHARE_CONTEXT,
1126   &_upload_meta_upload_caps,
1127   &_upload_meta_upload_new,
1128   &_upload_meta_upload_transform_caps,
1129   &_upload_meta_upload_accept,
1130   &_upload_meta_upload_propose_allocation,
1131   &_upload_meta_upload_perform,
1132   &_upload_meta_upload_free
1133 };
1134 
1135 struct RawUploadFrame
1136 {
1137   gint ref_count;
1138   GstVideoFrame frame;
1139 };
1140 
1141 struct RawUpload
1142 {
1143   GstGLUpload *upload;
1144   struct RawUploadFrame *in_frame;
1145   GstGLVideoAllocationParams *params;
1146 };
1147 
1148 static struct RawUploadFrame *
_raw_upload_frame_new(struct RawUpload * raw,GstBuffer * buffer)1149 _raw_upload_frame_new (struct RawUpload *raw, GstBuffer * buffer)
1150 {
1151   struct RawUploadFrame *frame;
1152   GstVideoInfo *info;
1153   gint i;
1154 
1155   if (!buffer)
1156     return NULL;
1157 
1158   frame = g_slice_new (struct RawUploadFrame);
1159   frame->ref_count = 1;
1160 
1161   if (!gst_video_frame_map (&frame->frame, &raw->upload->priv->in_info,
1162           buffer, GST_MAP_READ)) {
1163     g_slice_free (struct RawUploadFrame, frame);
1164     return NULL;
1165   }
1166 
1167   raw->upload->priv->in_info = frame->frame.info;
1168   info = &raw->upload->priv->in_info;
1169 
1170   /* Recalculate the offsets (and size) */
1171   info->size = 0;
1172   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
1173     info->offset[i] = info->size;
1174     info->size += gst_gl_get_plane_data_size (info, NULL, i);
1175   }
1176 
1177   return frame;
1178 }
1179 
1180 static void
_raw_upload_frame_ref(struct RawUploadFrame * frame)1181 _raw_upload_frame_ref (struct RawUploadFrame *frame)
1182 {
1183   g_atomic_int_inc (&frame->ref_count);
1184 }
1185 
1186 static void
_raw_upload_frame_unref(struct RawUploadFrame * frame)1187 _raw_upload_frame_unref (struct RawUploadFrame *frame)
1188 {
1189   if (g_atomic_int_dec_and_test (&frame->ref_count)) {
1190     gst_video_frame_unmap (&frame->frame);
1191     g_slice_free (struct RawUploadFrame, frame);
1192   }
1193 }
1194 
1195 static gpointer
_raw_data_upload_new(GstGLUpload * upload)1196 _raw_data_upload_new (GstGLUpload * upload)
1197 {
1198   struct RawUpload *raw = g_new0 (struct RawUpload, 1);
1199 
1200   raw->upload = upload;
1201 
1202   return raw;
1203 }
1204 
1205 static GstCaps *
_raw_data_upload_transform_caps(gpointer impl,GstGLContext * context,GstPadDirection direction,GstCaps * caps)1206 _raw_data_upload_transform_caps (gpointer impl, GstGLContext * context,
1207     GstPadDirection direction, GstCaps * caps)
1208 {
1209   GstCapsFeatures *passthrough =
1210       gst_caps_features_from_string
1211       (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
1212   GstCaps *ret;
1213 
1214   if (direction == GST_PAD_SINK) {
1215     GstGLTextureTarget target_mask = 0;
1216     GstCaps *tmp;
1217 
1218     ret =
1219         _set_caps_features_with_passthrough (caps,
1220         GST_CAPS_FEATURE_MEMORY_GL_MEMORY, passthrough);
1221 
1222     target_mask |= 1 << GST_GL_TEXTURE_TARGET_2D;
1223     target_mask |= 1 << GST_GL_TEXTURE_TARGET_RECTANGLE;
1224     tmp = _caps_intersect_texture_target (ret, target_mask);
1225     gst_caps_unref (ret);
1226     ret = tmp;
1227   } else {
1228     gint i, n;
1229 
1230     ret =
1231         _set_caps_features_with_passthrough (caps,
1232         GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, passthrough);
1233 
1234     n = gst_caps_get_size (ret);
1235     for (i = 0; i < n; i++) {
1236       GstStructure *s = gst_caps_get_structure (ret, i);
1237 
1238       gst_structure_remove_fields (s, "texture-target", NULL);
1239     }
1240   }
1241 
1242   gst_caps_features_free (passthrough);
1243 
1244   return ret;
1245 }
1246 
1247 static gboolean
_raw_data_upload_accept(gpointer impl,GstBuffer * buffer,GstCaps * in_caps,GstCaps * out_caps)1248 _raw_data_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
1249     GstCaps * out_caps)
1250 {
1251   struct RawUpload *raw = impl;
1252   GstCapsFeatures *features;
1253 
1254   features = gst_caps_get_features (out_caps, 0);
1255   if (!gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY))
1256     return FALSE;
1257 
1258   if (raw->in_frame)
1259     _raw_upload_frame_unref (raw->in_frame);
1260   raw->in_frame = _raw_upload_frame_new (raw, buffer);
1261 
1262   if (raw->params)
1263     gst_gl_allocation_params_free ((GstGLAllocationParams *) raw->params);
1264   if (!(raw->params =
1265           gst_gl_video_allocation_params_new_wrapped_data (raw->upload->context,
1266               NULL, &raw->upload->priv->in_info, -1, NULL,
1267               GST_GL_TEXTURE_TARGET_2D, 0, NULL, raw->in_frame,
1268               (GDestroyNotify) _raw_upload_frame_unref)))
1269     return FALSE;
1270 
1271   return (raw->in_frame != NULL);
1272 }
1273 
1274 static void
_raw_data_upload_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)1275 _raw_data_upload_propose_allocation (gpointer impl, GstQuery * decide_query,
1276     GstQuery * query)
1277 {
1278   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
1279 }
1280 
1281 static GstGLUploadReturn
_raw_data_upload_perform(gpointer impl,GstBuffer * buffer,GstBuffer ** outbuf)1282 _raw_data_upload_perform (gpointer impl, GstBuffer * buffer,
1283     GstBuffer ** outbuf)
1284 {
1285   GstGLBaseMemoryAllocator *allocator;
1286   struct RawUpload *raw = impl;
1287   int i;
1288   GstVideoInfo *in_info = &raw->upload->priv->in_info;
1289   guint n_mem = GST_VIDEO_INFO_N_PLANES (in_info);
1290 
1291   allocator =
1292       GST_GL_BASE_MEMORY_ALLOCATOR (gst_gl_memory_allocator_get_default
1293       (raw->upload->context));
1294 
1295   /* FIXME Use a buffer pool to cache the generated textures */
1296   *outbuf = gst_buffer_new ();
1297   raw->params->parent.context = raw->upload->context;
1298   if (gst_gl_memory_setup_buffer ((GstGLMemoryAllocator *) allocator, *outbuf,
1299           raw->params, NULL, raw->in_frame->frame.data, n_mem)) {
1300 
1301     for (i = 0; i < n_mem; i++)
1302       _raw_upload_frame_ref (raw->in_frame);
1303     gst_buffer_add_gl_sync_meta (raw->upload->context, *outbuf);
1304   } else {
1305     GST_ERROR_OBJECT (raw->upload, "Failed to allocate wrapped texture");
1306     gst_buffer_unref (*outbuf);
1307     return GST_GL_UPLOAD_ERROR;
1308   }
1309   gst_object_unref (allocator);
1310   _raw_upload_frame_unref (raw->in_frame);
1311   raw->in_frame = NULL;
1312 
1313   return GST_GL_UPLOAD_DONE;
1314 }
1315 
1316 static void
_raw_data_upload_free(gpointer impl)1317 _raw_data_upload_free (gpointer impl)
1318 {
1319   struct RawUpload *raw = impl;
1320 
1321   if (raw->params)
1322     gst_gl_allocation_params_free ((GstGLAllocationParams *) raw->params);
1323 
1324   g_free (raw);
1325 }
1326 
1327 static GstStaticCaps _raw_data_upload_caps =
1328 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_MEMORY_VIDEO_FORMATS_STR));
1329 
1330 static const UploadMethod _raw_data_upload = {
1331   "Raw Data",
1332   0,
1333   &_raw_data_upload_caps,
1334   &_raw_data_upload_new,
1335   &_raw_data_upload_transform_caps,
1336   &_raw_data_upload_accept,
1337   &_raw_data_upload_propose_allocation,
1338   &_raw_data_upload_perform,
1339   &_raw_data_upload_free
1340 };
1341 
1342 #if GST_GL_HAVE_VIV_DIRECTVIV
1343 #ifndef GL_BGRA_EXT
1344 #define GL_BGRA_EXT                                             0x80E1
1345 #endif
1346 #ifndef GL_VIV_YV12
1347 #define GL_VIV_YV12                                             0x8FC0
1348 #endif
1349 #ifndef GL_VIV_NV12
1350 #define GL_VIV_NV12                                             0x8FC1
1351 #endif
1352 #ifndef GL_VIV_YUY2
1353 #define GL_VIV_YUY2                                             0x8FC2
1354 #endif
1355 #ifndef GL_VIV_UYVY
1356 #define GL_VIV_UYVY                                             0x8FC3
1357 #endif
1358 #ifndef GL_VIV_NV21
1359 #define GL_VIV_NV21                                             0x8FC4
1360 #endif
1361 #ifndef GL_VIV_I420
1362 #define GL_VIV_I420                                             0x8FC5
1363 #endif
1364 
1365 struct DirectVIVUpload
1366 {
1367   GstGLUpload *upload;
1368 
1369   GstGLVideoAllocationParams *params;
1370   GstBuffer *inbuf, *outbuf;
1371   void (*TexDirectVIVMap) (GLenum Target, GLsizei Width, GLsizei Height,
1372       GLenum Format, GLvoid ** Logical, const GLuint * Physical);
1373   void (*TexDirectInvalidateVIV) (GLenum Target);
1374   gboolean loaded_functions;
1375 };
1376 
1377 #define GST_GL_DIRECTVIV_FORMAT "{RGBA, I420, YV12, NV12, NV21, YUY2, UYVY, BGRA, RGB16}"
1378 
1379 static GstStaticCaps _directviv_upload_caps =
1380 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_DIRECTVIV_FORMAT));
1381 
1382 static gpointer
_directviv_upload_new(GstGLUpload * upload)1383 _directviv_upload_new (GstGLUpload * upload)
1384 {
1385   struct DirectVIVUpload *directviv = g_new0 (struct DirectVIVUpload, 1);
1386   directviv->upload = upload;
1387   directviv->loaded_functions = FALSE;
1388 
1389   return directviv;
1390 }
1391 
1392 static GstCaps *
_directviv_upload_transform_caps(gpointer impl,GstGLContext * context,GstPadDirection direction,GstCaps * caps)1393 _directviv_upload_transform_caps (gpointer impl, GstGLContext * context,
1394     GstPadDirection direction, GstCaps * caps)
1395 {
1396   GstCapsFeatures *passthrough =
1397       gst_caps_features_from_string
1398       (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
1399   GstCaps *ret;
1400 
1401   if (direction == GST_PAD_SINK) {
1402     GstCaps *tmp;
1403 
1404     ret =
1405         _set_caps_features_with_passthrough (caps,
1406         GST_CAPS_FEATURE_MEMORY_GL_MEMORY, passthrough);
1407 
1408     gst_caps_set_simple (ret, "format", G_TYPE_STRING, "RGBA", NULL);
1409     tmp = _caps_intersect_texture_target (ret, 1 << GST_GL_TEXTURE_TARGET_2D);
1410     gst_caps_unref (ret);
1411     ret = tmp;
1412   } else {
1413     GstCaps *tmp;
1414     tmp = gst_caps_from_string (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
1415         (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, GST_GL_DIRECTVIV_FORMAT));
1416     ret =
1417         _set_caps_features_with_passthrough (tmp,
1418         GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, passthrough);
1419     gst_caps_unref (tmp);
1420   }
1421 
1422   gst_caps_features_free (passthrough);
1423   return ret;
1424 }
1425 
1426 
1427 static void
_directviv_upload_load_functions_gl_thread(GstGLContext * context,struct DirectVIVUpload * directviv)1428 _directviv_upload_load_functions_gl_thread (GstGLContext * context,
1429     struct DirectVIVUpload *directviv)
1430 {
1431   directviv->TexDirectVIVMap =
1432       gst_gl_context_get_proc_address (context, "glTexDirectVIVMap");
1433   directviv->TexDirectInvalidateVIV =
1434       gst_gl_context_get_proc_address (context, "glTexDirectInvalidateVIV");
1435 }
1436 
1437 static gboolean
_directviv_upload_accept(gpointer impl,GstBuffer * buffer,GstCaps * in_caps,GstCaps * out_caps)1438 _directviv_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
1439     GstCaps * out_caps)
1440 {
1441   struct DirectVIVUpload *directviv = impl;
1442   GstCapsFeatures *features;
1443   guint n_mem;
1444   GstMemory *mem;
1445 
1446   if (!directviv->loaded_functions && (!directviv->TexDirectInvalidateVIV ||
1447           !directviv->TexDirectVIVMap)) {
1448     gst_gl_context_thread_add (directviv->upload->context,
1449         (GstGLContextThreadFunc) _directviv_upload_load_functions_gl_thread,
1450         directviv);
1451     directviv->loaded_functions = TRUE;
1452   }
1453   if (!directviv->TexDirectInvalidateVIV || !directviv->TexDirectVIVMap)
1454     return FALSE;
1455 
1456   features = gst_caps_get_features (out_caps, 0);
1457   if (!gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY))
1458     return FALSE;
1459 
1460   if (directviv->params)
1461     gst_gl_allocation_params_free ((GstGLAllocationParams *) directviv->params);
1462   if (!(directviv->params =
1463           gst_gl_video_allocation_params_new (directviv->upload->context, NULL,
1464               &directviv->upload->priv->out_info, -1, NULL,
1465               GST_GL_TEXTURE_TARGET_2D, GST_VIDEO_GL_TEXTURE_TYPE_RGBA)))
1466     return FALSE;
1467 
1468   /* We only support a single memory per buffer at this point */
1469   n_mem = gst_buffer_n_memory (buffer);
1470   if (n_mem == 1) {
1471     mem = gst_buffer_peek_memory (buffer, 0);
1472   } else {
1473     mem = NULL;
1474   }
1475 
1476   return n_mem == 1 && mem && gst_is_phys_memory (mem);
1477 }
1478 
1479 static void
_directviv_upload_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)1480 _directviv_upload_propose_allocation (gpointer impl, GstQuery * decide_query,
1481     GstQuery * query)
1482 {
1483 }
1484 
1485 static GLenum
_directviv_upload_video_format_to_gl_format(GstVideoFormat format)1486 _directviv_upload_video_format_to_gl_format (GstVideoFormat format)
1487 {
1488   switch (format) {
1489     case GST_VIDEO_FORMAT_I420:
1490       return GL_VIV_I420;
1491     case GST_VIDEO_FORMAT_YV12:
1492       return GL_VIV_YV12;
1493     case GST_VIDEO_FORMAT_NV12:
1494       return GL_VIV_NV12;
1495     case GST_VIDEO_FORMAT_NV21:
1496       return GL_VIV_NV21;
1497     case GST_VIDEO_FORMAT_YUY2:
1498       return GL_VIV_YUY2;
1499     case GST_VIDEO_FORMAT_UYVY:
1500       return GL_VIV_UYVY;
1501     case GST_VIDEO_FORMAT_RGB16:
1502       return GL_RGB565;
1503     case GST_VIDEO_FORMAT_RGBA:
1504       return GL_RGBA;
1505     case GST_VIDEO_FORMAT_BGRA:
1506       return GL_BGRA_EXT;
1507     case GST_VIDEO_FORMAT_RGBx:
1508       return GL_RGBA;
1509     case GST_VIDEO_FORMAT_BGRx:
1510       return GL_BGRA_EXT;
1511     default:
1512       return 0;
1513   }
1514 }
1515 
1516 typedef struct
1517 {
1518   GstBuffer *buffer;
1519   GstMemory *memory;
1520   GstMapInfo map;
1521   guintptr phys_addr;
1522 } DirectVIVUnmapData;
1523 
1524 static void
_directviv_memory_unmap(DirectVIVUnmapData * data)1525 _directviv_memory_unmap (DirectVIVUnmapData * data)
1526 {
1527   gst_memory_unmap (data->memory, &data->map);
1528   gst_memory_unref (data->memory);
1529   gst_buffer_unref (data->buffer);
1530   g_free (data);
1531 }
1532 
1533 static void
_directviv_upload_perform_gl_thread(GstGLContext * context,struct DirectVIVUpload * directviv)1534 _directviv_upload_perform_gl_thread (GstGLContext * context,
1535     struct DirectVIVUpload *directviv)
1536 {
1537   static GQuark directviv_unmap_quark = 0;
1538   GstGLMemoryAllocator *allocator;
1539   GstMemory *in_mem;
1540   GstGLMemory *out_gl_mem;
1541   GstVideoInfo *in_info;
1542   DirectVIVUnmapData *unmap_data;
1543   GstVideoMeta *vmeta;
1544   gint width, height, gl_format;
1545   const GstGLFuncs *gl;
1546 
1547   if (!directviv_unmap_quark)
1548     directviv_unmap_quark = g_quark_from_static_string ("GstGLDirectVIVUnmap");
1549 
1550   gl = context->gl_vtable;
1551 
1552   g_assert (gst_buffer_n_memory (directviv->inbuf) == 1);
1553   in_info = &directviv->upload->priv->in_info;
1554   in_mem = gst_buffer_peek_memory (directviv->inbuf, 0);
1555   unmap_data = g_new0 (DirectVIVUnmapData, 1);
1556   if (!gst_memory_map (in_mem, &unmap_data->map, GST_MAP_READ)) {
1557     g_free (unmap_data);
1558     return;
1559   }
1560   unmap_data->phys_addr = gst_phys_memory_get_phys_addr (in_mem);
1561   if (!unmap_data->phys_addr) {
1562     gst_memory_unmap (in_mem, &unmap_data->map);
1563     g_free (unmap_data);
1564     return;
1565   }
1566   unmap_data->memory = gst_memory_ref (in_mem);
1567   unmap_data->buffer = gst_buffer_ref (directviv->inbuf);
1568 
1569   allocator =
1570       GST_GL_MEMORY_ALLOCATOR (gst_allocator_find
1571       (GST_GL_MEMORY_PBO_ALLOCATOR_NAME));
1572 
1573   /* FIXME: buffer pool */
1574   directviv->outbuf = gst_buffer_new ();
1575   gst_gl_memory_setup_buffer (allocator, directviv->outbuf, directviv->params,
1576       NULL, NULL, 0);
1577   gst_object_unref (allocator);
1578 
1579   out_gl_mem = (GstGLMemory *) gst_buffer_peek_memory (directviv->outbuf, 0);
1580 
1581   /* Need to keep the input memory and buffer mapped and valid until
1582    * the GL memory is not used anymore */
1583   gst_mini_object_set_qdata ((GstMiniObject *) out_gl_mem,
1584       directviv_unmap_quark, unmap_data,
1585       (GDestroyNotify) _directviv_memory_unmap);
1586   gst_buffer_add_parent_buffer_meta (directviv->outbuf, directviv->inbuf);
1587 
1588   /* width/height need to compensate for stride/padding */
1589   vmeta = gst_buffer_get_video_meta (directviv->inbuf);
1590   if (vmeta) {
1591     width = vmeta->stride[0];
1592     if (GST_VIDEO_INFO_N_PLANES (in_info) == 1)
1593       height = gst_memory_get_sizes (in_mem, NULL, NULL) / width;
1594     else
1595       height = vmeta->offset[1] / width;
1596   } else {
1597     width = GST_VIDEO_INFO_PLANE_STRIDE (in_info, 0);
1598     if (GST_VIDEO_INFO_N_PLANES (in_info) == 1)
1599       height = gst_memory_get_sizes (in_mem, NULL, NULL) / width;
1600     else
1601       height = GST_VIDEO_INFO_PLANE_OFFSET (in_info, 1) / width;
1602   }
1603   width /= GST_VIDEO_INFO_COMP_PSTRIDE (in_info, 0);
1604 
1605   gl_format =
1606       _directviv_upload_video_format_to_gl_format (GST_VIDEO_INFO_FORMAT
1607       (in_info));
1608 
1609   gl->BindTexture (GL_TEXTURE_2D, out_gl_mem->tex_id);
1610   directviv->TexDirectVIVMap (GL_TEXTURE_2D, width, height,
1611       gl_format, (void **) &unmap_data->map.data, &unmap_data->phys_addr);
1612   directviv->TexDirectInvalidateVIV (GL_TEXTURE_2D);
1613 }
1614 
1615 static GstGLUploadReturn
_directviv_upload_perform(gpointer impl,GstBuffer * buffer,GstBuffer ** outbuf)1616 _directviv_upload_perform (gpointer impl, GstBuffer * buffer,
1617     GstBuffer ** outbuf)
1618 {
1619   struct DirectVIVUpload *directviv = impl;
1620 
1621   directviv->inbuf = buffer;
1622   directviv->outbuf = NULL;
1623   gst_gl_context_thread_add (directviv->upload->context,
1624       (GstGLContextThreadFunc) _directviv_upload_perform_gl_thread, directviv);
1625   directviv->inbuf = NULL;
1626 
1627   if (!directviv->outbuf)
1628     return GST_GL_UPLOAD_ERROR;
1629 
1630   *outbuf = directviv->outbuf;
1631   directviv->outbuf = NULL;
1632 
1633   return GST_GL_UPLOAD_DONE;
1634 }
1635 
1636 static void
_directviv_upload_free(gpointer impl)1637 _directviv_upload_free (gpointer impl)
1638 {
1639   struct DirectVIVUpload *directviv = impl;
1640 
1641   if (directviv->params)
1642     gst_gl_allocation_params_free ((GstGLAllocationParams *) directviv->params);
1643 
1644   g_free (impl);
1645 }
1646 
1647 static const UploadMethod _directviv_upload = {
1648   "DirectVIV",
1649   0,
1650   &_directviv_upload_caps,
1651   &_directviv_upload_new,
1652   &_directviv_upload_transform_caps,
1653   &_directviv_upload_accept,
1654   &_directviv_upload_propose_allocation,
1655   &_directviv_upload_perform,
1656   &_directviv_upload_free
1657 };
1658 
1659 #endif /* GST_GL_HAVE_VIV_DIRECTVIV */
1660 
1661 static const UploadMethod *upload_methods[] = { &_gl_memory_upload,
1662 #if GST_GL_HAVE_DMABUF
1663   &_direct_dma_buf_upload,
1664   &_dma_buf_upload,
1665 #endif
1666 #if GST_GL_HAVE_VIV_DIRECTVIV
1667   &_directviv_upload,
1668 #endif
1669   &_upload_meta_upload, &_raw_data_upload
1670 };
1671 
1672 static GMutex upload_global_lock;
1673 
1674 GstCaps *
gst_gl_upload_get_input_template_caps(void)1675 gst_gl_upload_get_input_template_caps (void)
1676 {
1677   GstCaps *ret = NULL;
1678   gint i;
1679 
1680   g_mutex_lock (&upload_global_lock);
1681 
1682   /* FIXME: cache this and invalidate on changes to upload_methods */
1683   for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1684     GstCaps *template =
1685         gst_static_caps_get (upload_methods[i]->input_template_caps);
1686     ret = ret == NULL ? template : gst_caps_merge (ret, template);
1687   }
1688 
1689   ret = gst_caps_simplify (ret);
1690   ret = gst_gl_overlay_compositor_add_caps (ret);
1691   g_mutex_unlock (&upload_global_lock);
1692 
1693   return ret;
1694 }
1695 
1696 static void
gst_gl_upload_class_init(GstGLUploadClass * klass)1697 gst_gl_upload_class_init (GstGLUploadClass * klass)
1698 {
1699   G_OBJECT_CLASS (klass)->finalize = gst_gl_upload_finalize;
1700 }
1701 
1702 static void
gst_gl_upload_init(GstGLUpload * upload)1703 gst_gl_upload_init (GstGLUpload * upload)
1704 {
1705   upload->priv = gst_gl_upload_get_instance_private (upload);
1706 }
1707 
1708 /**
1709  * gst_gl_upload_new:
1710  * @context: a #GstGLContext
1711  *
1712  * Returns: (transfer full): a new #GstGLUpload object
1713  */
1714 GstGLUpload *
gst_gl_upload_new(GstGLContext * context)1715 gst_gl_upload_new (GstGLContext * context)
1716 {
1717   GstGLUpload *upload = g_object_new (GST_TYPE_GL_UPLOAD, NULL);
1718   gint i, n;
1719 
1720   gst_object_ref_sink (upload);
1721 
1722   if (context)
1723     gst_gl_upload_set_context (upload, context);
1724   else
1725     upload->context = NULL;
1726 
1727   n = G_N_ELEMENTS (upload_methods);
1728   upload->priv->upload_impl = g_malloc (sizeof (gpointer) * n);
1729   for (i = 0; i < n; i++) {
1730     upload->priv->upload_impl[i] = upload_methods[i]->new (upload);
1731   }
1732 
1733   GST_DEBUG_OBJECT (upload, "Created new GLUpload for context %" GST_PTR_FORMAT,
1734       context);
1735 
1736   return upload;
1737 }
1738 
1739 void
gst_gl_upload_set_context(GstGLUpload * upload,GstGLContext * context)1740 gst_gl_upload_set_context (GstGLUpload * upload, GstGLContext * context)
1741 {
1742   g_return_if_fail (upload != NULL);
1743 
1744   gst_object_replace ((GstObject **) & upload->context, (GstObject *) context);
1745 }
1746 
1747 static void
gst_gl_upload_finalize(GObject * object)1748 gst_gl_upload_finalize (GObject * object)
1749 {
1750   GstGLUpload *upload;
1751   gint i, n;
1752 
1753   upload = GST_GL_UPLOAD (object);
1754 
1755   upload->priv->method_i = 0;
1756 
1757   if (upload->context) {
1758     gst_object_unref (upload->context);
1759     upload->context = NULL;
1760   }
1761 
1762   if (upload->priv->in_caps) {
1763     gst_caps_unref (upload->priv->in_caps);
1764     upload->priv->in_caps = NULL;
1765   }
1766 
1767   if (upload->priv->out_caps) {
1768     gst_caps_unref (upload->priv->out_caps);
1769     upload->priv->out_caps = NULL;
1770   }
1771 
1772   n = G_N_ELEMENTS (upload_methods);
1773   for (i = 0; i < n; i++) {
1774     if (upload->priv->upload_impl[i])
1775       upload_methods[i]->free (upload->priv->upload_impl[i]);
1776   }
1777   g_free (upload->priv->upload_impl);
1778 
1779   G_OBJECT_CLASS (gst_gl_upload_parent_class)->finalize (object);
1780 }
1781 
1782 GstCaps *
gst_gl_upload_transform_caps(GstGLUpload * upload,GstGLContext * context,GstPadDirection direction,GstCaps * caps,GstCaps * filter)1783 gst_gl_upload_transform_caps (GstGLUpload * upload, GstGLContext * context,
1784     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1785 {
1786   GstCaps *result, *tmp;
1787   gint i;
1788 
1789   if (upload->priv->method) {
1790     tmp = upload->priv->method->transform_caps (upload->priv->method_impl,
1791         context, direction, caps);
1792     if (filter) {
1793       result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
1794       gst_caps_unref (tmp);
1795     } else {
1796       result = tmp;
1797     }
1798     if (!gst_caps_is_empty (result))
1799       return result;
1800     else
1801       gst_caps_unref (result);
1802   }
1803 
1804   tmp = gst_caps_new_empty ();
1805 
1806   for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1807     GstCaps *tmp2;
1808 
1809     tmp2 =
1810         upload_methods[i]->transform_caps (upload->priv->upload_impl[i],
1811         context, direction, caps);
1812 
1813     if (tmp2)
1814       tmp = gst_caps_merge (tmp, tmp2);
1815   }
1816 
1817   if (filter) {
1818     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
1819     gst_caps_unref (tmp);
1820   } else {
1821     result = tmp;
1822   }
1823 
1824   return result;
1825 }
1826 
1827 /**
1828  * gst_gl_upload_propose_allocation:
1829  * @upload: a #GstGLUpload
1830  * @decide_query: (allow-none): a #GstQuery from a decide allocation
1831  * @query: the proposed allocation query
1832  *
1833  * Adds the required allocation parameters to support uploading.
1834  */
1835 void
gst_gl_upload_propose_allocation(GstGLUpload * upload,GstQuery * decide_query,GstQuery * query)1836 gst_gl_upload_propose_allocation (GstGLUpload * upload, GstQuery * decide_query,
1837     GstQuery * query)
1838 {
1839   gint i;
1840 
1841   for (i = 0; i < G_N_ELEMENTS (upload_methods); i++)
1842     upload_methods[i]->propose_allocation (upload->priv->upload_impl[i],
1843         decide_query, query);
1844 }
1845 
1846 static gboolean
_gst_gl_upload_set_caps_unlocked(GstGLUpload * upload,GstCaps * in_caps,GstCaps * out_caps)1847 _gst_gl_upload_set_caps_unlocked (GstGLUpload * upload, GstCaps * in_caps,
1848     GstCaps * out_caps)
1849 {
1850   g_return_val_if_fail (upload != NULL, FALSE);
1851   g_return_val_if_fail (gst_caps_is_fixed (in_caps), FALSE);
1852 
1853   if (upload->priv->in_caps && upload->priv->out_caps
1854       && gst_caps_is_equal (upload->priv->in_caps, in_caps)
1855       && gst_caps_is_equal (upload->priv->out_caps, out_caps))
1856     return TRUE;
1857 
1858   gst_caps_replace (&upload->priv->in_caps, in_caps);
1859   gst_caps_replace (&upload->priv->out_caps, out_caps);
1860 
1861   gst_video_info_from_caps (&upload->priv->in_info, in_caps);
1862   gst_video_info_from_caps (&upload->priv->out_info, out_caps);
1863 
1864   upload->priv->method = NULL;
1865   upload->priv->method_impl = NULL;
1866   upload->priv->method_i = 0;
1867 
1868   return TRUE;
1869 }
1870 
1871 /**
1872  * gst_gl_upload_set_caps:
1873  * @upload: a #GstGLUpload
1874  * @in_caps: input #GstCaps
1875  * @out_caps: output #GstCaps
1876  *
1877  * Initializes @upload with the information required for upload.
1878  *
1879  * Returns: whether @in_caps and @out_caps could be set on @upload
1880  */
1881 gboolean
gst_gl_upload_set_caps(GstGLUpload * upload,GstCaps * in_caps,GstCaps * out_caps)1882 gst_gl_upload_set_caps (GstGLUpload * upload, GstCaps * in_caps,
1883     GstCaps * out_caps)
1884 {
1885   gboolean ret;
1886 
1887   GST_OBJECT_LOCK (upload);
1888   ret = _gst_gl_upload_set_caps_unlocked (upload, in_caps, out_caps);
1889   GST_OBJECT_UNLOCK (upload);
1890 
1891   return ret;
1892 }
1893 
1894 /**
1895  * gst_gl_upload_get_caps:
1896  * @upload: a #GstGLUpload
1897  * @in_caps: (transfer full) (allow-none) (out): the input #GstCaps
1898  * @out_caps: (transfer full) (allow-none) (out): the output #GstCaps
1899  */
1900 void
gst_gl_upload_get_caps(GstGLUpload * upload,GstCaps ** in_caps,GstCaps ** out_caps)1901 gst_gl_upload_get_caps (GstGLUpload * upload, GstCaps ** in_caps,
1902     GstCaps ** out_caps)
1903 {
1904   GST_OBJECT_LOCK (upload);
1905   if (in_caps)
1906     *in_caps =
1907         upload->priv->in_caps ? gst_caps_ref (upload->priv->in_caps) : NULL;
1908   if (out_caps)
1909     *out_caps =
1910         upload->priv->out_caps ? gst_caps_ref (upload->priv->out_caps) : NULL;
1911   GST_OBJECT_UNLOCK (upload);
1912 }
1913 
1914 static gboolean
_upload_find_method(GstGLUpload * upload,gpointer last_impl)1915 _upload_find_method (GstGLUpload * upload, gpointer last_impl)
1916 {
1917   gint method_i;
1918 
1919   /* start with the last used method after explicitly reconfiguring to
1920    * negotiate caps for this method */
1921   if (upload->priv->method_i == 0) {
1922     upload->priv->method_i = upload->priv->saved_method_i;
1923     upload->priv->saved_method_i = 0;
1924   }
1925 
1926   if (upload->priv->method_i >= G_N_ELEMENTS (upload_methods)) {
1927     if (last_impl)
1928       upload->priv->method_i = 0;
1929     else
1930       return FALSE;
1931   }
1932 
1933   method_i = upload->priv->method_i;
1934 
1935   if (last_impl == upload->priv->upload_impl[method_i])
1936     return FALSE;
1937 
1938   upload->priv->method = upload_methods[method_i];
1939   upload->priv->method_impl = upload->priv->upload_impl[method_i];
1940 
1941   GST_DEBUG_OBJECT (upload, "attempting upload with uploader %s",
1942       upload->priv->method->name);
1943 
1944   upload->priv->method_i++;
1945 
1946   return TRUE;
1947 }
1948 
1949 /**
1950  * gst_gl_upload_perform_with_buffer:
1951  * @upload: a #GstGLUpload
1952  * @buffer: input #GstBuffer
1953  * @outbuf_ptr: (out): resulting #GstBuffer
1954  *
1955  * Uploads @buffer using the transformation specified by
1956  * gst_gl_upload_set_caps() creating a new #GstBuffer in @outbuf_ptr.
1957  *
1958  * Returns: whether the upload was successful
1959  */
1960 GstGLUploadReturn
gst_gl_upload_perform_with_buffer(GstGLUpload * upload,GstBuffer * buffer,GstBuffer ** outbuf_ptr)1961 gst_gl_upload_perform_with_buffer (GstGLUpload * upload, GstBuffer * buffer,
1962     GstBuffer ** outbuf_ptr)
1963 {
1964   GstGLUploadReturn ret = GST_GL_UPLOAD_ERROR;
1965   GstBuffer *outbuf;
1966   gpointer last_impl = upload->priv->method_impl;
1967 
1968   g_return_val_if_fail (GST_IS_GL_UPLOAD (upload), FALSE);
1969   g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
1970   g_return_val_if_fail (outbuf_ptr != NULL, FALSE);
1971 
1972   GST_OBJECT_LOCK (upload);
1973 
1974 #define NEXT_METHOD \
1975 do { \
1976   if (!_upload_find_method (upload, last_impl)) { \
1977     GST_OBJECT_UNLOCK (upload); \
1978     return FALSE; \
1979   } \
1980   goto restart; \
1981 } while (0)
1982 
1983   if (!upload->priv->method_impl)
1984     _upload_find_method (upload, last_impl);
1985 
1986 restart:
1987   if (!upload->priv->method->accept (upload->priv->method_impl, buffer,
1988           upload->priv->in_caps, upload->priv->out_caps))
1989     NEXT_METHOD;
1990 
1991   ret =
1992       upload->priv->method->perform (upload->priv->method_impl, buffer,
1993       &outbuf);
1994   if (ret == GST_GL_UPLOAD_UNSHARED_GL_CONTEXT) {
1995     gint i;
1996 
1997     for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1998       if (upload_methods[i] == &_raw_data_upload) {
1999         upload->priv->method = &_raw_data_upload;
2000         upload->priv->method_impl = upload->priv->upload_impl[i];
2001         upload->priv->method_i = i;
2002 
2003         break;
2004       }
2005     }
2006     goto restart;
2007   } else if (ret == GST_GL_UPLOAD_DONE || ret == GST_GL_UPLOAD_RECONFIGURE) {
2008     if (last_impl != upload->priv->method_impl) {
2009       GstCaps *caps = gst_gl_upload_transform_caps (upload, upload->context,
2010           GST_PAD_SINK, upload->priv->in_caps, NULL);
2011       if (!gst_caps_is_subset (caps, upload->priv->out_caps)) {
2012         gst_buffer_replace (&outbuf, NULL);
2013         ret = GST_GL_UPLOAD_RECONFIGURE;
2014       }
2015       gst_caps_unref (caps);
2016     }
2017     /* we are done */
2018   } else {
2019     upload->priv->method_impl = NULL;
2020     NEXT_METHOD;
2021   }
2022 
2023   if (outbuf && buffer != outbuf)
2024     gst_buffer_copy_into (outbuf, buffer,
2025         GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2026   *outbuf_ptr = outbuf;
2027 
2028   if (ret == GST_GL_UPLOAD_RECONFIGURE)
2029     upload->priv->saved_method_i = upload->priv->method_i - 1;
2030 
2031   GST_OBJECT_UNLOCK (upload);
2032 
2033   return ret;
2034 
2035 #undef NEXT_METHOD
2036 }
2037