3 * Copyright (C) 2012 Matthew Waters <ystree00@gmail.com>
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.
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.
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.
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>
31 #include "gstgldownloadelement.h"
33 GST_DEBUG_CATEGORY_STATIC (gst_gl_download_element_debug);
34 #define GST_CAT_DEFAULT gst_gl_download_element_debug
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"););
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);
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,
61 static gboolean gst_gl_download_element_src_event (GstBaseTransform * bt,
63 static void gst_gl_download_element_finalize (GObject * object);
65 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
66 #define EXTRA_CAPS_TEMPLATE "video/x-raw(" GST_CAPS_FEATURE_MEMORY_DMABUF "); "
68 #define EXTRA_CAPS_TEMPLATE
71 static GstStaticPadTemplate gst_gl_download_element_src_pad_template =
72 GST_STATIC_PAD_TEMPLATE ("src",
75 GST_STATIC_CAPS (EXTRA_CAPS_TEMPLATE
76 "video/x-raw; video/x-raw(memory:GLMemory)"));
78 static GstStaticPadTemplate gst_gl_download_element_sink_pad_template =
79 GST_STATIC_PAD_TEMPLATE ("sink",
82 GST_STATIC_CAPS ("video/x-raw(memory:GLMemory); video/x-raw"));
85 gst_gl_download_element_class_init (GstGLDownloadElementClass * klass)
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);
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;
104 bt_class->passthrough_on_same_caps = TRUE;
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);
111 gst_element_class_set_metadata (element_class,
112 "OpenGL downloader", "Filter/Video",
113 "Downloads data from OpenGL", "Matthew Waters <matthew@centricular.com>");
115 object_class->finalize = gst_gl_download_element_finalize;
119 gst_gl_download_element_init (GstGLDownloadElement * download)
121 gst_base_transform_set_prefer_passthrough (GST_BASE_TRANSFORM (download),
126 gst_gl_download_element_start (GstBaseTransform * bt)
128 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
129 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
131 dl->dmabuf_allocator = gst_dmabuf_allocator_new ();
132 g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
139 gst_gl_download_element_stop (GstBaseTransform * bt)
141 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
143 if (dl->dmabuf_allocator) {
144 gst_object_unref (GST_OBJECT (dl->dmabuf_allocator));
145 dl->dmabuf_allocator = NULL;
152 gst_gl_download_element_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
155 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
156 GstVideoInfo out_info;
157 GstCapsFeatures *features = NULL;
159 if (!gst_video_info_from_caps (&out_info, out_caps))
162 features = gst_caps_get_features (out_caps, 0);
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;
173 dl->mode = GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS;
180 _set_caps_features (const GstCaps * caps, const gchar * feature_name)
182 GstCaps *tmp = gst_caps_copy (caps);
183 guint n = gst_caps_get_size (tmp);
186 for (i = 0; i < n; i++)
187 gst_caps_set_features (tmp, i,
188 gst_caps_features_from_string (feature_name));
194 _remove_field (GstCaps * caps, const gchar * field)
196 guint n = gst_caps_get_size (caps);
199 for (i = 0; i < n; i++) {
200 GstStructure *s = gst_caps_get_structure (caps, i);
201 gst_structure_remove_field (s, field);
206 gst_gl_download_element_transform_caps (GstBaseTransform * bt,
207 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
209 GstCaps *result, *tmp;
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);
216 tmp = gst_caps_ref (caps);
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);
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);
230 result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
231 gst_caps_unref (tmp);
236 GST_DEBUG_OBJECT (bt, "returning caps %" GST_PTR_FORMAT, result);
242 gst_gl_download_element_fixate_caps (GstBaseTransform * bt,
243 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
245 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
246 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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)) {
252 for (i = 0; i < gst_caps_get_size (othercaps); i++) {
253 GstCapsFeatures *features = gst_caps_get_features (othercaps, i);
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--);
264 return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (bt, direction,
269 gst_gl_download_element_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
272 gboolean ret = FALSE;
275 ret = gst_video_info_from_caps (&info, caps);
277 *size = GST_VIDEO_INFO_SIZE (&info);
282 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
292 _free_dmabuf_info (struct DmabufInfo *info)
294 gst_memory_unref (info->dmabuf);
299 _dmabuf_info_quark (void)
301 static GQuark quark = 0;
304 quark = g_quark_from_static_string ("GstGLDownloadDmabufInfo");
308 static struct DmabufInfo *
309 _get_cached_dmabuf_info (GstGLMemory * mem)
311 return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
312 _dmabuf_info_quark ());
316 _set_cached_dmabuf_info (GstGLMemory * mem, struct DmabufInfo *info)
318 return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
319 _dmabuf_info_quark (), info, (GDestroyNotify) _free_dmabuf_info);
322 struct DmabufTransfer
324 GstGLDownloadElement *download;
326 struct DmabufInfo *info;
330 _create_cached_dmabuf_info (GstGLContext * context, gpointer data)
332 struct DmabufTransfer *transfer = (struct DmabufTransfer *) data;
335 image = gst_egl_image_from_texture (context, transfer->glmem, NULL);
341 if (gst_egl_image_export_dmabuf (image, &fd, &stride, &offset)) {
342 GstGLDownloadElement *download = transfer->download;
343 struct DmabufInfo *info;
347 gst_gl_memory_get_texture_height (transfer->glmem) * stride + offset;
349 info = g_new0 (struct DmabufInfo, 1);
351 gst_dmabuf_allocator_alloc (download->dmabuf_allocator, fd, size);
352 info->stride = stride;
353 info->offset = offset;
355 transfer->info = info;
358 gst_egl_image_unref (image);
363 _try_export_dmabuf (GstGLDownloadElement * download, GstBuffer * inbuf)
366 GstBuffer *buffer = NULL;
368 gsize offset[GST_VIDEO_MAX_PLANES];
369 gint stride[GST_VIDEO_MAX_PLANES];
371 GstVideoInfo out_info;
373 GstVideoAlignment *alig = NULL;
375 glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, 0));
377 GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
378 if (gst_gl_context_get_gl_platform (context) != GST_GL_PLATFORM_EGL)
380 alig = &glmem->valign;
383 buffer = gst_buffer_new ();
386 for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
387 struct DmabufInfo *info;
389 glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, i));
390 info = _get_cached_dmabuf_info (glmem);
392 GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
393 struct DmabufTransfer transfer;
395 transfer.download = download;
396 transfer.glmem = glmem;
397 transfer.info = NULL;
398 gst_gl_context_thread_add (context, _create_cached_dmabuf_info,
400 info = transfer.info;
403 _set_cached_dmabuf_info (glmem, 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));
412 gst_buffer_unref (buffer);
414 goto export_complete;
418 src_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM (download)->srcpad);
419 gst_video_info_from_caps (&out_info, src_caps);
421 if (download->add_videometa) {
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);
429 gst_video_meta_set_alignment (meta, *alig);
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]) {
441 gst_buffer_unref (buffer);
450 #endif /* GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF */
453 gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
454 GstBuffer * inbuf, GstBuffer ** outbuf)
456 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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);
466 GstGLContext *context = GST_GL_BASE_FILTER (bt)->context;
467 GstGLSyncMeta *in_sync_meta;
469 in_sync_meta = gst_buffer_get_gl_sync_meta (inbuf);
471 gst_gl_sync_meta_wait (in_sync_meta, context);
473 if (GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata)
474 if (!GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata (bt, inbuf,
476 GST_ELEMENT_WARNING (GST_ELEMENT (bt), STREAM, NOT_IMPLEMENTED,
477 ("could not copy metadata"), (NULL));
483 GstCapsFeatures *features;
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;
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;
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);
506 if (gst_is_gl_memory_pbo (mem))
507 gst_gl_memory_pbo_download_transfer ((GstGLMemoryPBO *) mem);
515 gst_gl_download_element_transform (GstBaseTransform * bt,
516 GstBuffer * inbuf, GstBuffer * outbuf)
522 gst_gl_download_element_decide_allocation (GstBaseTransform * trans,
525 GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (trans);
527 if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
528 download->add_videometa = TRUE;
530 download->add_videometa = FALSE;
533 return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
538 gst_gl_download_element_sink_event (GstBaseTransform * bt, GstEvent * event)
540 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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);
546 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (bt, event);
550 gst_gl_download_element_src_event (GstBaseTransform * bt, GstEvent * event)
552 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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);
558 return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (bt, event);
562 gst_gl_download_element_finalize (GObject * object)
564 GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (object);
566 if (download->dmabuf_allocator) {
567 gst_object_unref (GST_OBJECT (download->dmabuf_allocator));
568 download->dmabuf_allocator = NULL;
571 G_OBJECT_CLASS (parent_class)->finalize (object);