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_get_unit_size (GstBaseTransform * trans,
43 GstCaps * caps, gsize * size);
44 static GstCaps *gst_gl_download_element_transform_caps (GstBaseTransform * bt,
45 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
46 static gboolean gst_gl_download_element_set_caps (GstBaseTransform * bt,
47 GstCaps * in_caps, GstCaps * out_caps);
48 static GstFlowReturn
49 gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
50 GstBuffer * buffer, GstBuffer ** outbuf);
51 static GstFlowReturn gst_gl_download_element_transform (GstBaseTransform * bt,
52 GstBuffer * buffer, GstBuffer * outbuf);
53 static gboolean gst_gl_download_element_decide_allocation (GstBaseTransform *
54 trans, GstQuery * query);
55 static void gst_gl_download_element_finalize (GObject * object);
56
57 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
58 #define EXTRA_CAPS_TEMPLATE "video/x-raw(" GST_CAPS_FEATURE_MEMORY_DMABUF "); "
59 #else
60 #define EXTRA_CAPS_TEMPLATE
61 #endif
62
63 static GstStaticPadTemplate gst_gl_download_element_src_pad_template =
64 GST_STATIC_PAD_TEMPLATE ("src",
65 GST_PAD_SRC,
66 GST_PAD_ALWAYS,
67 GST_STATIC_CAPS (EXTRA_CAPS_TEMPLATE
68 "video/x-raw; video/x-raw(memory:GLMemory)"));
69
70 static GstStaticPadTemplate gst_gl_download_element_sink_pad_template =
71 GST_STATIC_PAD_TEMPLATE ("sink",
72 GST_PAD_SINK,
73 GST_PAD_ALWAYS,
74 GST_STATIC_CAPS ("video/x-raw(memory:GLMemory); video/x-raw"));
75
76 static void
gst_gl_download_element_class_init(GstGLDownloadElementClass * klass)77 gst_gl_download_element_class_init (GstGLDownloadElementClass * klass)
78 {
79 GstBaseTransformClass *bt_class = GST_BASE_TRANSFORM_CLASS (klass);
80 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
81 GObjectClass *object_class = G_OBJECT_CLASS (klass);
82
83 bt_class->transform_caps = gst_gl_download_element_transform_caps;
84 bt_class->set_caps = gst_gl_download_element_set_caps;
85 bt_class->get_unit_size = gst_gl_download_element_get_unit_size;
86 bt_class->prepare_output_buffer =
87 gst_gl_download_element_prepare_output_buffer;
88 bt_class->transform = gst_gl_download_element_transform;
89 bt_class->decide_allocation = gst_gl_download_element_decide_allocation;
90
91 bt_class->passthrough_on_same_caps = TRUE;
92
93 gst_element_class_add_static_pad_template (element_class,
94 &gst_gl_download_element_src_pad_template);
95 gst_element_class_add_static_pad_template (element_class,
96 &gst_gl_download_element_sink_pad_template);
97
98 gst_element_class_set_metadata (element_class,
99 "OpenGL downloader", "Filter/Video",
100 "Downloads data from OpenGL", "Matthew Waters <matthew@centricular.com>");
101
102 object_class->finalize = gst_gl_download_element_finalize;
103 }
104
105 static void
gst_gl_download_element_init(GstGLDownloadElement * download)106 gst_gl_download_element_init (GstGLDownloadElement * download)
107 {
108 gst_base_transform_set_prefer_passthrough (GST_BASE_TRANSFORM (download),
109 TRUE);
110 }
111
112 static gboolean
gst_gl_download_element_set_caps(GstBaseTransform * bt,GstCaps * in_caps,GstCaps * out_caps)113 gst_gl_download_element_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
114 GstCaps * out_caps)
115 {
116 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
117 GstVideoInfo out_info;
118 GstCapsFeatures *features = NULL;
119
120 if (!gst_video_info_from_caps (&out_info, out_caps))
121 return FALSE;
122
123 features = gst_caps_get_features (out_caps, 0);
124
125 dl->do_pbo_transfers = FALSE;
126 if (dl->dmabuf_allocator) {
127 gst_object_unref (GST_OBJECT (dl->dmabuf_allocator));
128 dl->dmabuf_allocator = NULL;
129 }
130
131 if (!features) {
132 dl->do_pbo_transfers = TRUE;
133 return TRUE;
134 }
135
136 if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
137 /* do nothing with the buffer */
138 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
139 } else if (gst_caps_features_contains (features,
140 GST_CAPS_FEATURE_MEMORY_DMABUF)) {
141 dl->dmabuf_allocator = gst_dmabuf_allocator_new ();
142 #endif
143 } else if (gst_caps_features_contains (features,
144 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY)) {
145 dl->do_pbo_transfers = TRUE;
146 }
147
148 return TRUE;
149 }
150
151 static GstCaps *
_set_caps_features(const GstCaps * caps,const gchar * feature_name)152 _set_caps_features (const GstCaps * caps, const gchar * feature_name)
153 {
154 GstCaps *tmp = gst_caps_copy (caps);
155 guint n = gst_caps_get_size (tmp);
156 guint i = 0;
157
158 for (i = 0; i < n; i++)
159 gst_caps_set_features (tmp, i,
160 gst_caps_features_from_string (feature_name));
161
162 return tmp;
163 }
164
165 static void
_remove_field(GstCaps * caps,const gchar * field)166 _remove_field (GstCaps * caps, const gchar * field)
167 {
168 guint n = gst_caps_get_size (caps);
169 guint i = 0;
170
171 for (i = 0; i < n; i++) {
172 GstStructure *s = gst_caps_get_structure (caps, i);
173 gst_structure_remove_field (s, field);
174 }
175 }
176
177 static GstCaps *
gst_gl_download_element_transform_caps(GstBaseTransform * bt,GstPadDirection direction,GstCaps * caps,GstCaps * filter)178 gst_gl_download_element_transform_caps (GstBaseTransform * bt,
179 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
180 {
181 GstCaps *result, *tmp;
182
183 if (direction == GST_PAD_SRC) {
184 tmp = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
185 tmp = gst_caps_merge (gst_caps_ref (caps), tmp);
186 } else {
187 GstCaps *newcaps;
188 tmp = gst_caps_ref (caps);
189
190 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
191 newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_DMABUF);
192 _remove_field (newcaps, "texture-target");
193 tmp = gst_caps_merge (tmp, newcaps);
194 #endif
195
196 newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
197 _remove_field (newcaps, "texture-target");
198 tmp = gst_caps_merge (tmp, newcaps);
199 }
200
201 if (filter) {
202 result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
203 gst_caps_unref (tmp);
204 } else {
205 result = tmp;
206 }
207
208 GST_DEBUG_OBJECT (bt, "returning caps %" GST_PTR_FORMAT, result);
209
210 return result;
211 }
212
213 static gboolean
gst_gl_download_element_get_unit_size(GstBaseTransform * trans,GstCaps * caps,gsize * size)214 gst_gl_download_element_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
215 gsize * size)
216 {
217 gboolean ret = FALSE;
218 GstVideoInfo info;
219
220 ret = gst_video_info_from_caps (&info, caps);
221 if (ret)
222 *size = GST_VIDEO_INFO_SIZE (&info);
223
224 return TRUE;
225 }
226
227 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
228
229 struct DmabufInfo
230 {
231 GstMemory *dmabuf;
232 gint stride;
233 gsize offset;
234 };
235
236 static void
_free_dmabuf_info(struct DmabufInfo * info)237 _free_dmabuf_info (struct DmabufInfo *info)
238 {
239 gst_memory_unref (info->dmabuf);
240 g_free (info);
241 }
242
243 static GQuark
_dmabuf_info_quark(void)244 _dmabuf_info_quark (void)
245 {
246 static GQuark quark = 0;
247
248 if (!quark)
249 quark = g_quark_from_static_string ("GstGLDownloadDmabufInfo");
250 return quark;
251 }
252
253 static struct DmabufInfo *
_get_cached_dmabuf_info(GstGLMemory * mem)254 _get_cached_dmabuf_info (GstGLMemory * mem)
255 {
256 return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
257 _dmabuf_info_quark ());
258 }
259
260 static void
_set_cached_dmabuf_info(GstGLMemory * mem,struct DmabufInfo * info)261 _set_cached_dmabuf_info (GstGLMemory * mem, struct DmabufInfo *info)
262 {
263 return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
264 _dmabuf_info_quark (), info, (GDestroyNotify) _free_dmabuf_info);
265 }
266
267 struct DmabufTransfer
268 {
269 GstGLDownloadElement *download;
270 GstGLMemory *glmem;
271 struct DmabufInfo *info;
272 };
273
274 static void
_create_cached_dmabuf_info(GstGLContext * context,gpointer data)275 _create_cached_dmabuf_info (GstGLContext * context, gpointer data)
276 {
277 struct DmabufTransfer *transfer = (struct DmabufTransfer *) data;
278 GstEGLImage *image;
279
280 image = gst_egl_image_from_texture (context, transfer->glmem, NULL);
281 if (image) {
282 int fd;
283 gint stride;
284 gsize offset;
285
286 if (gst_egl_image_export_dmabuf (image, &fd, &stride, &offset)) {
287 GstGLDownloadElement *download = transfer->download;
288 struct DmabufInfo *info;
289 gsize maxsize;
290
291 gst_memory_get_sizes (GST_MEMORY_CAST (transfer->glmem), NULL, &maxsize);
292
293 info = g_new0 (struct DmabufInfo, 1);
294 info->dmabuf =
295 gst_dmabuf_allocator_alloc (download->dmabuf_allocator, fd, maxsize);
296 info->stride = stride;
297 info->offset = offset;
298
299 transfer->info = info;
300 }
301
302 gst_egl_image_unref (image);
303 }
304 }
305
306 static GstBuffer *
_try_export_dmabuf(GstGLDownloadElement * download,GstBuffer * inbuf)307 _try_export_dmabuf (GstGLDownloadElement * download, GstBuffer * inbuf)
308 {
309 GstGLMemory *glmem;
310 GstBuffer *buffer = NULL;
311 int i;
312 gsize offset[GST_VIDEO_MAX_PLANES];
313 gint stride[GST_VIDEO_MAX_PLANES];
314 GstCaps *src_caps;
315 GstVideoInfo out_info;
316 gsize total_offset;
317
318 glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, 0));
319 if (glmem) {
320 GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
321 if (gst_gl_context_get_gl_platform (context) != GST_GL_PLATFORM_EGL)
322 return NULL;
323 }
324
325 buffer = gst_buffer_new ();
326 total_offset = 0;
327
328 for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
329 struct DmabufInfo *info;
330
331 glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, i));
332 info = _get_cached_dmabuf_info (glmem);
333 if (!info) {
334 GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
335 struct DmabufTransfer transfer;
336
337 transfer.download = download;
338 transfer.glmem = glmem;
339 transfer.info = NULL;
340 gst_gl_context_thread_add (context, _create_cached_dmabuf_info,
341 &transfer);
342 info = transfer.info;
343
344 if (info)
345 _set_cached_dmabuf_info (glmem, info);
346 }
347
348 if (info) {
349 offset[i] = total_offset + info->offset;
350 stride[i] = info->stride;
351 total_offset += gst_memory_get_sizes (info->dmabuf, NULL, NULL);
352 gst_buffer_insert_memory (buffer, -1, gst_memory_ref (info->dmabuf));
353 } else {
354 gst_buffer_unref (buffer);
355 buffer = NULL;
356 goto export_complete;
357 }
358 }
359
360 src_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM (download)->srcpad);
361 gst_video_info_from_caps (&out_info, src_caps);
362
363 if (download->add_videometa) {
364 gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE,
365 out_info.finfo->format, out_info.width, out_info.height,
366 out_info.finfo->n_planes, offset, stride);
367 } else {
368 int i;
369 gboolean match = TRUE;
370 for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
371 if (offset[i] != out_info.offset[i] || stride[i] != out_info.stride[i]) {
372 match = FALSE;
373 break;
374 }
375 }
376
377 if (!match) {
378 gst_buffer_unref (buffer);
379 buffer = NULL;
380 }
381 }
382
383 export_complete:
384
385 return buffer;
386 }
387 #endif /* GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF */
388
389 static GstFlowReturn
gst_gl_download_element_prepare_output_buffer(GstBaseTransform * bt,GstBuffer * inbuf,GstBuffer ** outbuf)390 gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
391 GstBuffer * inbuf, GstBuffer ** outbuf)
392 {
393 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
394 gint i, n;
395
396 *outbuf = inbuf;
397
398 if (dl->do_pbo_transfers) {
399 n = gst_buffer_n_memory (*outbuf);
400 for (i = 0; i < n; i++) {
401 GstMemory *mem = gst_buffer_peek_memory (*outbuf, i);
402
403 if (gst_is_gl_memory_pbo (mem))
404 gst_gl_memory_pbo_download_transfer ((GstGLMemoryPBO *) mem);
405 }
406 }
407 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
408 else if (dl->dmabuf_allocator) {
409 GstBuffer *buffer = _try_export_dmabuf (dl, inbuf);
410 if (buffer) {
411 if (GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata)
412 if (!GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata (bt, inbuf,
413 buffer)) {
414 GST_ELEMENT_WARNING (GST_ELEMENT (bt), STREAM, NOT_IMPLEMENTED,
415 ("could not copy metadata"), (NULL));
416 }
417
418 *outbuf = buffer;
419 } else {
420 GstCaps *src_caps;
421 GstCapsFeatures *features;
422
423 gst_object_unref (dl->dmabuf_allocator);
424 dl->dmabuf_allocator = NULL;
425
426 src_caps = gst_pad_get_current_caps (bt->srcpad);
427 src_caps = gst_caps_make_writable (src_caps);
428 features = gst_caps_get_features (src_caps, 0);
429 gst_caps_features_remove (features, GST_CAPS_FEATURE_MEMORY_DMABUF);
430
431 if (!gst_base_transform_update_src_caps (bt, src_caps)) {
432 GST_ERROR_OBJECT (bt, "DMABuf exportation didn't work and system "
433 "memory is not supported.");
434 return GST_FLOW_NOT_NEGOTIATED;
435 }
436 }
437 }
438 #endif
439
440 return GST_FLOW_OK;
441 }
442
443 static GstFlowReturn
gst_gl_download_element_transform(GstBaseTransform * bt,GstBuffer * inbuf,GstBuffer * outbuf)444 gst_gl_download_element_transform (GstBaseTransform * bt,
445 GstBuffer * inbuf, GstBuffer * outbuf)
446 {
447 return GST_FLOW_OK;
448 }
449
450 static gboolean
gst_gl_download_element_decide_allocation(GstBaseTransform * trans,GstQuery * query)451 gst_gl_download_element_decide_allocation (GstBaseTransform * trans,
452 GstQuery * query)
453 {
454 GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (trans);
455
456 if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
457 download->add_videometa = TRUE;
458 } else {
459 download->add_videometa = FALSE;
460 }
461
462 return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
463 query);
464 }
465
466 static void
gst_gl_download_element_finalize(GObject * object)467 gst_gl_download_element_finalize (GObject * object)
468 {
469 GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (object);
470
471 if (download->dmabuf_allocator) {
472 gst_object_unref (GST_OBJECT (download->dmabuf_allocator));
473 download->dmabuf_allocator = NULL;
474 }
475
476 G_OBJECT_CLASS (parent_class)->finalize (object);
477 }
478