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 GstCaps *sys_caps = gst_caps_simplify (_set_caps_features (caps,
213 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY));
215 tmp = _set_caps_features (sys_caps, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
216 tmp = gst_caps_merge (tmp, sys_caps);
219 tmp = gst_caps_ref (caps);
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);
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);
233 result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
234 gst_caps_unref (tmp);
239 GST_DEBUG_OBJECT (bt, "returning caps %" GST_PTR_FORMAT, result);
245 gst_gl_download_element_fixate_caps (GstBaseTransform * bt,
246 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
248 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
249 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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)) {
255 for (i = 0; i < gst_caps_get_size (othercaps); i++) {
256 GstCapsFeatures *features = gst_caps_get_features (othercaps, i);
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--);
267 return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (bt, direction,
272 gst_gl_download_element_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
275 gboolean ret = FALSE;
278 ret = gst_video_info_from_caps (&info, caps);
280 *size = GST_VIDEO_INFO_SIZE (&info);
285 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
295 _free_dmabuf_info (struct DmabufInfo *info)
297 gst_memory_unref (info->dmabuf);
302 _dmabuf_info_quark (void)
304 static GQuark quark = 0;
307 quark = g_quark_from_static_string ("GstGLDownloadDmabufInfo");
311 static struct DmabufInfo *
312 _get_cached_dmabuf_info (GstGLMemory * mem)
314 return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
315 _dmabuf_info_quark ());
319 _set_cached_dmabuf_info (GstGLMemory * mem, struct DmabufInfo *info)
321 return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
322 _dmabuf_info_quark (), info, (GDestroyNotify) _free_dmabuf_info);
325 struct DmabufTransfer
327 GstGLDownloadElement *download;
329 struct DmabufInfo *info;
333 _create_cached_dmabuf_info (GstGLContext * context, gpointer data)
335 struct DmabufTransfer *transfer = (struct DmabufTransfer *) data;
338 image = gst_egl_image_from_texture (context, transfer->glmem, NULL);
344 if (gst_egl_image_export_dmabuf (image, &fd, &stride, &offset)) {
345 GstGLDownloadElement *download = transfer->download;
346 struct DmabufInfo *info;
350 gst_gl_memory_get_texture_height (transfer->glmem) * stride + offset;
352 info = g_new0 (struct DmabufInfo, 1);
354 gst_dmabuf_allocator_alloc (download->dmabuf_allocator, fd, size);
355 info->stride = stride;
356 info->offset = offset;
358 transfer->info = info;
361 gst_egl_image_unref (image);
366 _try_export_dmabuf (GstGLDownloadElement * download, GstBuffer * inbuf)
369 GstBuffer *buffer = NULL;
371 gsize offset[GST_VIDEO_MAX_PLANES];
372 gint stride[GST_VIDEO_MAX_PLANES];
374 GstVideoInfo out_info;
376 GstVideoAlignment *alig = NULL;
378 glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, 0));
380 GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
381 if (gst_gl_context_get_gl_platform (context) != GST_GL_PLATFORM_EGL)
383 alig = &glmem->valign;
386 buffer = gst_buffer_new ();
389 for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
390 struct DmabufInfo *info;
392 glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, i));
393 info = _get_cached_dmabuf_info (glmem);
395 GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
396 struct DmabufTransfer transfer;
398 transfer.download = download;
399 transfer.glmem = glmem;
400 transfer.info = NULL;
401 gst_gl_context_thread_add (context, _create_cached_dmabuf_info,
403 info = transfer.info;
406 _set_cached_dmabuf_info (glmem, 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));
415 gst_buffer_unref (buffer);
417 goto export_complete;
421 src_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM (download)->srcpad);
422 gst_video_info_from_caps (&out_info, src_caps);
424 if (download->add_videometa) {
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);
432 gst_video_meta_set_alignment (meta, *alig);
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]) {
444 gst_buffer_unref (buffer);
453 #endif /* GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF */
456 gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
457 GstBuffer * inbuf, GstBuffer ** outbuf)
459 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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);
469 GstGLContext *context = GST_GL_BASE_FILTER (bt)->context;
470 GstGLSyncMeta *in_sync_meta;
472 in_sync_meta = gst_buffer_get_gl_sync_meta (inbuf);
474 gst_gl_sync_meta_wait (in_sync_meta, context);
476 if (GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata)
477 if (!GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata (bt, inbuf,
479 GST_ELEMENT_WARNING (GST_ELEMENT (bt), STREAM, NOT_IMPLEMENTED,
480 ("could not copy metadata"), (NULL));
486 GstCapsFeatures *features;
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;
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;
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);
509 if (gst_is_gl_memory_pbo (mem))
510 gst_gl_memory_pbo_download_transfer ((GstGLMemoryPBO *) mem);
518 gst_gl_download_element_transform (GstBaseTransform * bt,
519 GstBuffer * inbuf, GstBuffer * outbuf)
525 gst_gl_download_element_decide_allocation (GstBaseTransform * trans,
528 GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (trans);
530 if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
531 download->add_videometa = TRUE;
533 download->add_videometa = FALSE;
536 return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
541 gst_gl_download_element_sink_event (GstBaseTransform * bt, GstEvent * event)
543 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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);
549 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (bt, event);
553 gst_gl_download_element_src_event (GstBaseTransform * bt, GstEvent * event)
555 GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
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);
561 return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (bt, event);
565 gst_gl_download_element_finalize (GObject * object)
567 GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (object);
569 if (download->dmabuf_allocator) {
570 gst_object_unref (GST_OBJECT (download->dmabuf_allocator));
571 download->dmabuf_allocator = NULL;
574 G_OBJECT_CLASS (parent_class)->finalize (object);