ddd6ae2f685dcd011bc3181ec34ae5870c2d3894
[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     tmp = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
213     tmp = gst_caps_merge (gst_caps_ref (caps), tmp);
214   } else {
215     GstCaps *newcaps;
216     tmp = gst_caps_ref (caps);
217
218 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
219     newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_DMABUF);
220     _remove_field (newcaps, "texture-target");
221     tmp = gst_caps_merge (tmp, newcaps);
222 #endif
223
224     newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
225     _remove_field (newcaps, "texture-target");
226     tmp = gst_caps_merge (tmp, newcaps);
227   }
228
229   if (filter) {
230     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
231     gst_caps_unref (tmp);
232   } else {
233     result = tmp;
234   }
235
236   GST_DEBUG_OBJECT (bt, "returning caps %" GST_PTR_FORMAT, result);
237
238   return result;
239 }
240
241 static GstCaps *
242 gst_gl_download_element_fixate_caps (GstBaseTransform * bt,
243     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
244 {
245 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
246   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
247
248   /* Remove DMABuf features if try_dmabuf_exports is not set */
249   if (direction == GST_PAD_SINK && !g_atomic_int_get (&dl->try_dmabuf_exports)) {
250     gint i;
251
252     for (i = 0; i < gst_caps_get_size (othercaps); i++) {
253       GstCapsFeatures *features = gst_caps_get_features (othercaps, i);
254
255       if (features && gst_caps_features_contains (features,
256               GST_CAPS_FEATURE_MEMORY_DMABUF)) {
257         caps = gst_caps_make_writable (othercaps);
258         gst_caps_remove_structure (othercaps, i--);
259       }
260     }
261   }
262 #endif
263
264   return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (bt, direction,
265       caps, othercaps);
266 }
267
268 static gboolean
269 gst_gl_download_element_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
270     gsize * size)
271 {
272   gboolean ret = FALSE;
273   GstVideoInfo info;
274
275   ret = gst_video_info_from_caps (&info, caps);
276   if (ret)
277     *size = GST_VIDEO_INFO_SIZE (&info);
278
279   return TRUE;
280 }
281
282 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
283
284 struct DmabufInfo
285 {
286   GstMemory *dmabuf;
287   gint stride;
288   gsize offset;
289 };
290
291 static void
292 _free_dmabuf_info (struct DmabufInfo *info)
293 {
294   gst_memory_unref (info->dmabuf);
295   g_free (info);
296 }
297
298 static GQuark
299 _dmabuf_info_quark (void)
300 {
301   static GQuark quark = 0;
302
303   if (!quark)
304     quark = g_quark_from_static_string ("GstGLDownloadDmabufInfo");
305   return quark;
306 }
307
308 static struct DmabufInfo *
309 _get_cached_dmabuf_info (GstGLMemory * mem)
310 {
311   return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
312       _dmabuf_info_quark ());
313 }
314
315 static void
316 _set_cached_dmabuf_info (GstGLMemory * mem, struct DmabufInfo *info)
317 {
318   return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
319       _dmabuf_info_quark (), info, (GDestroyNotify) _free_dmabuf_info);
320 }
321
322 struct DmabufTransfer
323 {
324   GstGLDownloadElement *download;
325   GstGLMemory *glmem;
326   struct DmabufInfo *info;
327 };
328
329 static void
330 _create_cached_dmabuf_info (GstGLContext * context, gpointer data)
331 {
332   struct DmabufTransfer *transfer = (struct DmabufTransfer *) data;
333   GstEGLImage *image;
334
335   image = gst_egl_image_from_texture (context, transfer->glmem, NULL);
336   if (image) {
337     int fd;
338     gint stride;
339     gsize offset;
340
341     if (gst_egl_image_export_dmabuf (image, &fd, &stride, &offset)) {
342       GstGLDownloadElement *download = transfer->download;
343       struct DmabufInfo *info;
344       gsize size;
345
346       size =
347           gst_gl_memory_get_texture_height (transfer->glmem) * stride + offset;
348
349       info = g_new0 (struct DmabufInfo, 1);
350       info->dmabuf =
351           gst_dmabuf_allocator_alloc (download->dmabuf_allocator, fd, size);
352       info->stride = stride;
353       info->offset = offset;
354
355       transfer->info = info;
356     }
357
358     gst_egl_image_unref (image);
359   }
360 }
361
362 static GstBuffer *
363 _try_export_dmabuf (GstGLDownloadElement * download, GstBuffer * inbuf)
364 {
365   GstGLMemory *glmem;
366   GstBuffer *buffer = NULL;
367   int i;
368   gsize offset[GST_VIDEO_MAX_PLANES];
369   gint stride[GST_VIDEO_MAX_PLANES];
370   GstCaps *src_caps;
371   GstVideoInfo out_info;
372   gsize total_offset;
373   GstVideoAlignment *alig = NULL;
374
375   glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, 0));
376   if (glmem) {
377     GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
378     if (gst_gl_context_get_gl_platform (context) != GST_GL_PLATFORM_EGL)
379       return NULL;
380     alig = &glmem->valign;
381   }
382
383   buffer = gst_buffer_new ();
384   total_offset = 0;
385
386   for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
387     struct DmabufInfo *info;
388
389     glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, i));
390     info = _get_cached_dmabuf_info (glmem);
391     if (!info) {
392       GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
393       struct DmabufTransfer transfer;
394
395       transfer.download = download;
396       transfer.glmem = glmem;
397       transfer.info = NULL;
398       gst_gl_context_thread_add (context, _create_cached_dmabuf_info,
399           &transfer);
400       info = transfer.info;
401
402       if (info)
403         _set_cached_dmabuf_info (glmem, info);
404     }
405
406     if (info) {
407       offset[i] = total_offset + info->offset;
408       stride[i] = info->stride;
409       total_offset += gst_memory_get_sizes (info->dmabuf, NULL, NULL);
410       gst_buffer_insert_memory (buffer, -1, gst_memory_ref (info->dmabuf));
411     } else {
412       gst_buffer_unref (buffer);
413       buffer = NULL;
414       goto export_complete;
415     }
416   }
417
418   src_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM (download)->srcpad);
419   gst_video_info_from_caps (&out_info, src_caps);
420
421   if (download->add_videometa) {
422     GstVideoMeta *meta;
423
424     meta = gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE,
425         out_info.finfo->format, out_info.width, out_info.height,
426         out_info.finfo->n_planes, offset, stride);
427
428     if (alig)
429       gst_video_meta_set_alignment (meta, *alig);
430   } else {
431     int i;
432     gboolean match = TRUE;
433     for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
434       if (offset[i] != out_info.offset[i] || stride[i] != out_info.stride[i]) {
435         match = FALSE;
436         break;
437       }
438     }
439
440     if (!match) {
441       gst_buffer_unref (buffer);
442       buffer = NULL;
443     }
444   }
445
446 export_complete:
447
448   return buffer;
449 }
450 #endif /* GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF */
451
452 static GstFlowReturn
453 gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
454     GstBuffer * inbuf, GstBuffer ** outbuf)
455 {
456   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
457   gint i, n;
458
459   *outbuf = inbuf;
460
461 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
462   if (dl->mode == GST_GL_DOWNLOAD_MODE_DMABUF_EXPORTS) {
463     GstBuffer *buffer = _try_export_dmabuf (dl, inbuf);
464
465     if (buffer) {
466       GstGLContext *context = GST_GL_BASE_FILTER (bt)->context;
467       GstGLSyncMeta *in_sync_meta;
468
469       in_sync_meta = gst_buffer_get_gl_sync_meta (inbuf);
470       if (in_sync_meta)
471         gst_gl_sync_meta_wait (in_sync_meta, context);
472
473       if (GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata)
474         if (!GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata (bt, inbuf,
475                 buffer)) {
476           GST_ELEMENT_WARNING (GST_ELEMENT (bt), STREAM, NOT_IMPLEMENTED,
477               ("could not copy metadata"), (NULL));
478         }
479
480       *outbuf = buffer;
481     } else {
482       GstCaps *src_caps;
483       GstCapsFeatures *features;
484
485       src_caps = gst_pad_get_current_caps (bt->srcpad);
486       src_caps = gst_caps_make_writable (src_caps);
487       features = gst_caps_get_features (src_caps, 0);
488       gst_caps_features_remove (features, GST_CAPS_FEATURE_MEMORY_DMABUF);
489       g_atomic_int_set (&dl->try_dmabuf_exports, FALSE);
490       dl->mode = GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS;
491
492       if (!gst_base_transform_update_src_caps (bt, src_caps)) {
493         GST_ERROR_OBJECT (bt, "DMABuf exportation didn't work and system "
494             "memory is not supported.");
495         return GST_FLOW_NOT_NEGOTIATED;
496       }
497     }
498   }
499 #endif
500
501   if (dl->mode == GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS) {
502     n = gst_buffer_n_memory (*outbuf);
503     for (i = 0; i < n; i++) {
504       GstMemory *mem = gst_buffer_peek_memory (*outbuf, i);
505
506       if (gst_is_gl_memory_pbo (mem))
507         gst_gl_memory_pbo_download_transfer ((GstGLMemoryPBO *) mem);
508     }
509   }
510
511   return GST_FLOW_OK;
512 }
513
514 static GstFlowReturn
515 gst_gl_download_element_transform (GstBaseTransform * bt,
516     GstBuffer * inbuf, GstBuffer * outbuf)
517 {
518   return GST_FLOW_OK;
519 }
520
521 static gboolean
522 gst_gl_download_element_decide_allocation (GstBaseTransform * trans,
523     GstQuery * query)
524 {
525   GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (trans);
526
527   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
528     download->add_videometa = TRUE;
529   } else {
530     download->add_videometa = FALSE;
531   }
532
533   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
534       query);
535 }
536
537 static gboolean
538 gst_gl_download_element_sink_event (GstBaseTransform * bt, GstEvent * event)
539 {
540   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
541
542   /* Retry exporting whenever we have new caps from upstream */
543   if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS)
544     g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
545
546   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (bt, event);
547 }
548
549 static gboolean
550 gst_gl_download_element_src_event (GstBaseTransform * bt, GstEvent * event)
551 {
552   GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
553
554   /* Retry exporting whenever downstream have changed */
555   if (GST_EVENT_TYPE (event) == GST_EVENT_RECONFIGURE)
556     g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
557
558   return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (bt, event);
559 }
560
561 static void
562 gst_gl_download_element_finalize (GObject * object)
563 {
564   GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (object);
565
566   if (download->dmabuf_allocator) {
567     gst_object_unref (GST_OBJECT (download->dmabuf_allocator));
568     download->dmabuf_allocator = NULL;
569   }
570
571   G_OBJECT_CLASS (parent_class)->finalize (object);
572 }