gl: download: Fix the wrong transformed result from src direction in transform_caps().
[platform/upstream/gstreamer.git] / ext / gl / gstgldownloadelement.c
1 /*
2  * GStreamer
3  * Copyright (C) 2012 Matthew Waters <ystree00@gmail.com>
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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <gst/gl/gl.h>
26 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
27 #include <gst/gl/egl/gsteglimage.h>
28 #include <gst/allocators/gstdmabuf.h>
29 #endif
30
31 #include "gstgldownloadelement.h"
32
33 GST_DEBUG_CATEGORY_STATIC (gst_gl_download_element_debug);
34 #define GST_CAT_DEFAULT gst_gl_download_element_debug
35
36 #define gst_gl_download_element_parent_class parent_class
37 G_DEFINE_TYPE_WITH_CODE (GstGLDownloadElement, gst_gl_download_element,
38     GST_TYPE_GL_BASE_FILTER,
39     GST_DEBUG_CATEGORY_INIT (gst_gl_download_element_debug, "gldownloadelement",
40         0, "download element"););
41
42 static gboolean gst_gl_download_element_start (GstBaseTransform * bt);
43 static gboolean gst_gl_download_element_stop (GstBaseTransform * bt);
44 static gboolean gst_gl_download_element_get_unit_size (GstBaseTransform * trans,
45     GstCaps * caps, gsize * size);
46 static GstCaps *gst_gl_download_element_transform_caps (GstBaseTransform * bt,
47     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
48 static GstCaps *gst_gl_download_element_fixate_caps (GstBaseTransform * trans,
49     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
50 static gboolean gst_gl_download_element_set_caps (GstBaseTransform * bt,
51     GstCaps * in_caps, GstCaps * out_caps);
52 static GstFlowReturn
53 gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
54     GstBuffer * buffer, GstBuffer ** outbuf);
55 static GstFlowReturn gst_gl_download_element_transform (GstBaseTransform * bt,
56     GstBuffer * buffer, GstBuffer * outbuf);
57 static gboolean gst_gl_download_element_decide_allocation (GstBaseTransform *
58     trans, GstQuery * query);
59 static gboolean gst_gl_download_element_sink_event (GstBaseTransform * bt,
60     GstEvent * event);
61 static gboolean gst_gl_download_element_src_event (GstBaseTransform * bt,
62     GstEvent * event);
63 static void gst_gl_download_element_finalize (GObject * object);
64
65 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
66 #define EXTRA_CAPS_TEMPLATE "video/x-raw(" GST_CAPS_FEATURE_MEMORY_DMABUF "); "
67 #else
68 #define EXTRA_CAPS_TEMPLATE
69 #endif
70
71 static GstStaticPadTemplate gst_gl_download_element_src_pad_template =
72     GST_STATIC_PAD_TEMPLATE ("src",
73     GST_PAD_SRC,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS (EXTRA_CAPS_TEMPLATE
76         "video/x-raw; video/x-raw(memory:GLMemory)"));
77
78 static GstStaticPadTemplate gst_gl_download_element_sink_pad_template =
79     GST_STATIC_PAD_TEMPLATE ("sink",
80     GST_PAD_SINK,
81     GST_PAD_ALWAYS,
82     GST_STATIC_CAPS ("video/x-raw(memory:GLMemory); video/x-raw"));
83
84 static void
85 gst_gl_download_element_class_init (GstGLDownloadElementClass * klass)
86 {
87   GstBaseTransformClass *bt_class = GST_BASE_TRANSFORM_CLASS (klass);
88   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
89   GObjectClass *object_class = G_OBJECT_CLASS (klass);
90
91   bt_class->start = gst_gl_download_element_start;
92   bt_class->stop = gst_gl_download_element_stop;
93   bt_class->transform_caps = gst_gl_download_element_transform_caps;
94   bt_class->fixate_caps = gst_gl_download_element_fixate_caps;
95   bt_class->set_caps = gst_gl_download_element_set_caps;
96   bt_class->get_unit_size = gst_gl_download_element_get_unit_size;
97   bt_class->prepare_output_buffer =
98       gst_gl_download_element_prepare_output_buffer;
99   bt_class->transform = gst_gl_download_element_transform;
100   bt_class->decide_allocation = gst_gl_download_element_decide_allocation;
101   bt_class->sink_event = gst_gl_download_element_sink_event;
102   bt_class->src_event = gst_gl_download_element_src_event;
103
104   bt_class->passthrough_on_same_caps = TRUE;
105
106   gst_element_class_add_static_pad_template (element_class,
107       &gst_gl_download_element_src_pad_template);
108   gst_element_class_add_static_pad_template (element_class,
109       &gst_gl_download_element_sink_pad_template);
110
111   gst_element_class_set_metadata (element_class,
112       "OpenGL downloader", "Filter/Video",
113       "Downloads data from OpenGL", "Matthew Waters <matthew@centricular.com>");
114
115   object_class->finalize = gst_gl_download_element_finalize;
116 }
117
118 static void
119 gst_gl_download_element_init (GstGLDownloadElement * download)
120 {
121   gst_base_transform_set_prefer_passthrough (GST_BASE_TRANSFORM (download),
122       TRUE);
123 }
124
125 static gboolean
126 gst_gl_download_element_start (GstBaseTransform * bt)
127 {
128 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
129   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
130
131   dl->dmabuf_allocator = gst_dmabuf_allocator_new ();
132   g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
133 #endif
134
135   return TRUE;
136 }
137
138 static gboolean
139 gst_gl_download_element_stop (GstBaseTransform * bt)
140 {
141   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
142
143   if (dl->dmabuf_allocator) {
144     gst_object_unref (GST_OBJECT (dl->dmabuf_allocator));
145     dl->dmabuf_allocator = NULL;
146   }
147
148   return TRUE;
149 }
150
151 static gboolean
152 gst_gl_download_element_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
153     GstCaps * out_caps)
154 {
155   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
156   GstVideoInfo out_info;
157   GstCapsFeatures *features = NULL;
158
159   if (!gst_video_info_from_caps (&out_info, out_caps))
160     return FALSE;
161
162   features = gst_caps_get_features (out_caps, 0);
163
164   if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
165     dl->mode = GST_GL_DOWNLOAD_MODE_PASSTHROUGH;
166 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
167   } else if (g_atomic_int_get (&dl->try_dmabuf_exports) &&
168       gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
169     dl->mode = GST_GL_DOWNLOAD_MODE_DMABUF_EXPORTS;
170 #endif
171   } else {
172     /* System Memory */
173     dl->mode = GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS;
174   }
175
176   return TRUE;
177 }
178
179 static GstCaps *
180 _set_caps_features (const GstCaps * caps, const gchar * feature_name)
181 {
182   GstCaps *tmp = gst_caps_copy (caps);
183   guint n = gst_caps_get_size (tmp);
184   guint i = 0;
185
186   for (i = 0; i < n; i++)
187     gst_caps_set_features (tmp, i,
188         gst_caps_features_from_string (feature_name));
189
190   return tmp;
191 }
192
193 static void
194 _remove_field (GstCaps * caps, const gchar * field)
195 {
196   guint n = gst_caps_get_size (caps);
197   guint i = 0;
198
199   for (i = 0; i < n; i++) {
200     GstStructure *s = gst_caps_get_structure (caps, i);
201     gst_structure_remove_field (s, field);
202   }
203 }
204
205 static GstCaps *
206 gst_gl_download_element_transform_caps (GstBaseTransform * bt,
207     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
208 {
209   GstCaps *result, *tmp;
210
211   if (direction == GST_PAD_SRC) {
212     GstCaps *sys_caps = gst_caps_simplify (_set_caps_features (caps,
213             GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY));
214
215     tmp = _set_caps_features (sys_caps, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
216     tmp = gst_caps_merge (tmp, sys_caps);
217   } else {
218     GstCaps *newcaps;
219     tmp = gst_caps_ref (caps);
220
221 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
222     newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_DMABUF);
223     _remove_field (newcaps, "texture-target");
224     tmp = gst_caps_merge (tmp, newcaps);
225 #endif
226
227     newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
228     _remove_field (newcaps, "texture-target");
229     tmp = gst_caps_merge (tmp, newcaps);
230   }
231
232   if (filter) {
233     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
234     gst_caps_unref (tmp);
235   } else {
236     result = tmp;
237   }
238
239   GST_DEBUG_OBJECT (bt, "returning caps %" GST_PTR_FORMAT, result);
240
241   return result;
242 }
243
244 static GstCaps *
245 gst_gl_download_element_fixate_caps (GstBaseTransform * bt,
246     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
247 {
248 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
249   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
250
251   /* Remove DMABuf features if try_dmabuf_exports is not set */
252   if (direction == GST_PAD_SINK && !g_atomic_int_get (&dl->try_dmabuf_exports)) {
253     gint i;
254
255     for (i = 0; i < gst_caps_get_size (othercaps); i++) {
256       GstCapsFeatures *features = gst_caps_get_features (othercaps, i);
257
258       if (features && gst_caps_features_contains (features,
259               GST_CAPS_FEATURE_MEMORY_DMABUF)) {
260         caps = gst_caps_make_writable (othercaps);
261         gst_caps_remove_structure (othercaps, i--);
262       }
263     }
264   }
265 #endif
266
267   return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (bt, direction,
268       caps, othercaps);
269 }
270
271 static gboolean
272 gst_gl_download_element_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
273     gsize * size)
274 {
275   gboolean ret = FALSE;
276   GstVideoInfo info;
277
278   ret = gst_video_info_from_caps (&info, caps);
279   if (ret)
280     *size = GST_VIDEO_INFO_SIZE (&info);
281
282   return TRUE;
283 }
284
285 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
286
287 struct DmabufInfo
288 {
289   GstMemory *dmabuf;
290   gint stride;
291   gsize offset;
292 };
293
294 static void
295 _free_dmabuf_info (struct DmabufInfo *info)
296 {
297   gst_memory_unref (info->dmabuf);
298   g_free (info);
299 }
300
301 static GQuark
302 _dmabuf_info_quark (void)
303 {
304   static GQuark quark = 0;
305
306   if (!quark)
307     quark = g_quark_from_static_string ("GstGLDownloadDmabufInfo");
308   return quark;
309 }
310
311 static struct DmabufInfo *
312 _get_cached_dmabuf_info (GstGLMemory * mem)
313 {
314   return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
315       _dmabuf_info_quark ());
316 }
317
318 static void
319 _set_cached_dmabuf_info (GstGLMemory * mem, struct DmabufInfo *info)
320 {
321   return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
322       _dmabuf_info_quark (), info, (GDestroyNotify) _free_dmabuf_info);
323 }
324
325 struct DmabufTransfer
326 {
327   GstGLDownloadElement *download;
328   GstGLMemory *glmem;
329   struct DmabufInfo *info;
330 };
331
332 static void
333 _create_cached_dmabuf_info (GstGLContext * context, gpointer data)
334 {
335   struct DmabufTransfer *transfer = (struct DmabufTransfer *) data;
336   GstEGLImage *image;
337
338   image = gst_egl_image_from_texture (context, transfer->glmem, NULL);
339   if (image) {
340     int fd;
341     gint stride;
342     gsize offset;
343
344     if (gst_egl_image_export_dmabuf (image, &fd, &stride, &offset)) {
345       GstGLDownloadElement *download = transfer->download;
346       struct DmabufInfo *info;
347       gsize size;
348
349       size =
350           gst_gl_memory_get_texture_height (transfer->glmem) * stride + offset;
351
352       info = g_new0 (struct DmabufInfo, 1);
353       info->dmabuf =
354           gst_dmabuf_allocator_alloc (download->dmabuf_allocator, fd, size);
355       info->stride = stride;
356       info->offset = offset;
357
358       transfer->info = info;
359     }
360
361     gst_egl_image_unref (image);
362   }
363 }
364
365 static GstBuffer *
366 _try_export_dmabuf (GstGLDownloadElement * download, GstBuffer * inbuf)
367 {
368   GstGLMemory *glmem;
369   GstBuffer *buffer = NULL;
370   int i;
371   gsize offset[GST_VIDEO_MAX_PLANES];
372   gint stride[GST_VIDEO_MAX_PLANES];
373   GstCaps *src_caps;
374   GstVideoInfo out_info;
375   gsize total_offset;
376   GstVideoAlignment *alig = NULL;
377
378   glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, 0));
379   if (glmem) {
380     GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
381     if (gst_gl_context_get_gl_platform (context) != GST_GL_PLATFORM_EGL)
382       return NULL;
383     alig = &glmem->valign;
384   }
385
386   buffer = gst_buffer_new ();
387   total_offset = 0;
388
389   for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
390     struct DmabufInfo *info;
391
392     glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, i));
393     info = _get_cached_dmabuf_info (glmem);
394     if (!info) {
395       GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
396       struct DmabufTransfer transfer;
397
398       transfer.download = download;
399       transfer.glmem = glmem;
400       transfer.info = NULL;
401       gst_gl_context_thread_add (context, _create_cached_dmabuf_info,
402           &transfer);
403       info = transfer.info;
404
405       if (info)
406         _set_cached_dmabuf_info (glmem, info);
407     }
408
409     if (info) {
410       offset[i] = total_offset + info->offset;
411       stride[i] = info->stride;
412       total_offset += gst_memory_get_sizes (info->dmabuf, NULL, NULL);
413       gst_buffer_insert_memory (buffer, -1, gst_memory_ref (info->dmabuf));
414     } else {
415       gst_buffer_unref (buffer);
416       buffer = NULL;
417       goto export_complete;
418     }
419   }
420
421   src_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM (download)->srcpad);
422   gst_video_info_from_caps (&out_info, src_caps);
423
424   if (download->add_videometa) {
425     GstVideoMeta *meta;
426
427     meta = gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE,
428         out_info.finfo->format, out_info.width, out_info.height,
429         out_info.finfo->n_planes, offset, stride);
430
431     if (alig)
432       gst_video_meta_set_alignment (meta, *alig);
433   } else {
434     int i;
435     gboolean match = TRUE;
436     for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
437       if (offset[i] != out_info.offset[i] || stride[i] != out_info.stride[i]) {
438         match = FALSE;
439         break;
440       }
441     }
442
443     if (!match) {
444       gst_buffer_unref (buffer);
445       buffer = NULL;
446     }
447   }
448
449 export_complete:
450
451   return buffer;
452 }
453 #endif /* GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF */
454
455 static GstFlowReturn
456 gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
457     GstBuffer * inbuf, GstBuffer ** outbuf)
458 {
459   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
460   gint i, n;
461
462   *outbuf = inbuf;
463
464 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
465   if (dl->mode == GST_GL_DOWNLOAD_MODE_DMABUF_EXPORTS) {
466     GstBuffer *buffer = _try_export_dmabuf (dl, inbuf);
467
468     if (buffer) {
469       GstGLContext *context = GST_GL_BASE_FILTER (bt)->context;
470       GstGLSyncMeta *in_sync_meta;
471
472       in_sync_meta = gst_buffer_get_gl_sync_meta (inbuf);
473       if (in_sync_meta)
474         gst_gl_sync_meta_wait (in_sync_meta, context);
475
476       if (GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata)
477         if (!GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata (bt, inbuf,
478                 buffer)) {
479           GST_ELEMENT_WARNING (GST_ELEMENT (bt), STREAM, NOT_IMPLEMENTED,
480               ("could not copy metadata"), (NULL));
481         }
482
483       *outbuf = buffer;
484     } else {
485       GstCaps *src_caps;
486       GstCapsFeatures *features;
487
488       src_caps = gst_pad_get_current_caps (bt->srcpad);
489       src_caps = gst_caps_make_writable (src_caps);
490       features = gst_caps_get_features (src_caps, 0);
491       gst_caps_features_remove (features, GST_CAPS_FEATURE_MEMORY_DMABUF);
492       g_atomic_int_set (&dl->try_dmabuf_exports, FALSE);
493       dl->mode = GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS;
494
495       if (!gst_base_transform_update_src_caps (bt, src_caps)) {
496         GST_ERROR_OBJECT (bt, "DMABuf exportation didn't work and system "
497             "memory is not supported.");
498         return GST_FLOW_NOT_NEGOTIATED;
499       }
500     }
501   }
502 #endif
503
504   if (dl->mode == GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS) {
505     n = gst_buffer_n_memory (*outbuf);
506     for (i = 0; i < n; i++) {
507       GstMemory *mem = gst_buffer_peek_memory (*outbuf, i);
508
509       if (gst_is_gl_memory_pbo (mem))
510         gst_gl_memory_pbo_download_transfer ((GstGLMemoryPBO *) mem);
511     }
512   }
513
514   return GST_FLOW_OK;
515 }
516
517 static GstFlowReturn
518 gst_gl_download_element_transform (GstBaseTransform * bt,
519     GstBuffer * inbuf, GstBuffer * outbuf)
520 {
521   return GST_FLOW_OK;
522 }
523
524 static gboolean
525 gst_gl_download_element_decide_allocation (GstBaseTransform * trans,
526     GstQuery * query)
527 {
528   GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (trans);
529
530   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
531     download->add_videometa = TRUE;
532   } else {
533     download->add_videometa = FALSE;
534   }
535
536   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
537       query);
538 }
539
540 static gboolean
541 gst_gl_download_element_sink_event (GstBaseTransform * bt, GstEvent * event)
542 {
543   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
544
545   /* Retry exporting whenever we have new caps from upstream */
546   if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS)
547     g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
548
549   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (bt, event);
550 }
551
552 static gboolean
553 gst_gl_download_element_src_event (GstBaseTransform * bt, GstEvent * event)
554 {
555   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
556
557   /* Retry exporting whenever downstream have changed */
558   if (GST_EVENT_TYPE (event) == GST_EVENT_RECONFIGURE)
559     g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
560
561   return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (bt, event);
562 }
563
564 static void
565 gst_gl_download_element_finalize (GObject * object)
566 {
567   GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (object);
568
569   if (download->dmabuf_allocator) {
570     gst_object_unref (GST_OBJECT (download->dmabuf_allocator));
571     download->dmabuf_allocator = NULL;
572   }
573
574   G_OBJECT_CLASS (parent_class)->finalize (object);
575 }