diff --git a/commitFile.txt b/commitFile.txt new file mode 100644 index 0000000..9d4490a --- /dev/null +++ b/commitFile.txt @@ -0,0 +1,17 @@ +Updating prebuilts and/or headers + +f29a8e1083e2fe546c68761a196b6dfd5aed14f6 - gstegl_src/gst-egl/Makefile +f43858ce2d2f71d6de58ca59ff794fb026689a70 - gstegl_src/gst-egl/LICENSE.libgstnveglglessink +1a0c41a27bfc4e15f352ceaf07a5c88dfc1924bf - gstegl_src/gst-egl/gst-libs/gst/egl/egl.c +b8b167b1729c6e6d1341d9bc61689af8b1dd5064 - gstegl_src/gst-egl/gst-libs/gst/egl/egl.h +34174463c5aa7f9b7f84878cff2f38e41cce28ac - gstegl_src/gst-egl/gst-libs/gst/egl/LICENSE.libgstnvegl-1.0 +9a83abc5044d31ea41387ac916231ec396665c34 - gstegl_src/gst-egl/pre-gen-source_64/config.h +a420f8c656140307d3d3c614228f609b59583c05 - gstegl_src/gst-egl/ext/eglgles/gstegljitter.c +a341ac6c564356940984817c53ad37a8af5ae471 - gstegl_src/gst-egl/ext/eglgles/gstegladaptation.h +702531a3023833fbf940b9d8ab568b7bda1d96a7 - gstegl_src/gst-egl/ext/eglgles/gstegladaptation_egl.c +ba144260ea5c0b76692d05b4b062c1656293fba3 - gstegl_src/gst-egl/ext/eglgles/gstegljitter.h +8149c234efce25ef230ab00a9d6ba141678575c9 - gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.h +948247d93a3b7bd800d3601ce6648cd508792054 - gstegl_src/gst-egl/ext/eglgles/gsteglglessink.h +80aab315fad96b237fea7b282e365b90e5185c7f - gstegl_src/gst-egl/ext/eglgles/gsteglglessink.c +d2f766addf49a14de24d217ca94c2698410b61c7 - gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.c +08e9b3d774d0a93abf96dd711ce8d1c412af6373 - gstegl_src/gst-egl/ext/eglgles/gstegladaptation.c diff --git a/gstegl_src/README.txt b/gstegl_src/README.txt new file mode 100644 index 0000000..d82f532 --- /dev/null +++ b/gstegl_src/README.txt @@ -0,0 +1,43 @@ +This package contains the code for libgstnvegl-1.0.so library + +DEPENDENCIES: +------------ +(1) EGL +(2) OPENGLES2 +(3) Gstreamer >= 1.2.3 +(4) X11, Xext + +The above target machine dependencies can be obtained from any standard +distribution (like https://launchpad.net/ubuntu) or can be self-built. + +Above required packages can also be installed using the following command: + +sudo apt-get install \ + autoconf \ + automake \ + autopoint \ + autotools-dev \ + gtk-doc-tools \ + libegl1-mesa-dev \ + libgles2-mesa-dev \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libgtk2.0-dev \ + libtool \ + libx11-dev \ + libxext-dev \ + pkg-config + +This package was built against the (above) libs present in Ubuntu 16.04. + +INSTALLATION: +------------- +1) Untar the package and enter the dir +2) ./autogen.sh +3) ./configure +4) make +5) sudo make install + +Note: +Pass the appropriate flags and toolchain path to "configure" for tha ARM build. + diff --git a/gstegl_src/gst-egl/BUILD.txt b/gstegl_src/gst-egl/BUILD.txt new file mode 100644 index 0000000..dcae124 --- /dev/null +++ b/gstegl_src/gst-egl/BUILD.txt @@ -0,0 +1,41 @@ +############################################################################### +# +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA Corporation and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA Corporation is strictly prohibited. +# +############################################################################### + +Steps to compile the "gst-nveglglessink" sources: + +1) Install gstreamer related packages using the command: + + sudo apt-get install gstreamer1.0-tools gstreamer1.0-alsa \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ + gstreamer1.0-libav libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev libegl1-mesa-dev + +2) Install wayland related packages if not present: + sudo apt-get install libwayland-dev + +3) Install CUDA Runtime 10.0+ + +4) Install Deepstream 6.0 + +5) Extract the package "gstegl_src.tbz2" as follow: + + tar xvjf gstegl_src.tbz2` + +6) cd "gstegl_src/gst_egl/" + +7) Export the appropriate CUDA_VER using - "export CUDA_VER=" + +8) run "make" to create "libgstnveglglessink.so" + +9) run "sudo make install" to install "libgstnveglglessink.so" in + "/usr/lib/aarch64-linux-gnu/gstreamer-1.0". diff --git a/gstegl_src/gst-egl/LICENSE.libgstnveglglessink b/gstegl_src/gst-egl/LICENSE.libgstnveglglessink new file mode 100644 index 0000000..509b9bb --- /dev/null +++ b/gstegl_src/gst-egl/LICENSE.libgstnveglglessink @@ -0,0 +1,24 @@ +GStreamer EGL/GLES Sink +Copyright (C) 2012 Collabora Ltd. + @author: Reynaldo H. Verdejo Pinochet + @author: Sebastian Dröge + +Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/gstegl_src/gst-egl/Makefile b/gstegl_src/gst-egl/Makefile new file mode 100644 index 0000000..a266201 --- /dev/null +++ b/gstegl_src/gst-egl/Makefile @@ -0,0 +1,74 @@ +############################################################################### +# +# Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA Corporation and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA Corporation is strictly prohibited. +# +############################################################################### + +ifneq ($(MAKECMDGOALS),install) +ifeq ($(CUDA_VER),) + $(error "CUDA_VER is not set. Set it by running - "export CUDA_VER="") +endif +endif + +SO_NAME := libgstnveglglessink.so +DEST_DIR ?= /usr/lib/aarch64-linux-gnu/gstreamer-1.0 + +SRCS := ext/eglgles/gstegladaptation.c \ + ext/eglgles/gstegladaptation_egl.c \ + ext/eglgles/gsteglglessink.c \ + ext/eglgles/gstegljitter.c \ + ext/eglgles/video_platform_wrapper.c \ + gst-libs/gst/egl/egl.c + +INCLUDES += -I./pre-gen-source_64/ \ + -I./gst-libs \ + -I./gst-libs/gst/egl \ + -I/opt/nvidia/deepstream/deepstream-6.0/sources/includes/ \ + -I/usr/local/include/gstreamer-1.0 \ + -I/usr/local/cuda-$(CUDA_VER)/targets/aarch64-linux/include/ \ + -I./ + +PKGS := glib-2.0 \ + gstreamer-1.0 \ + gstreamer-base-1.0 \ + gstreamer-video-1.0 + +OBJS := $(SRCS:.c=.o) + +CFLAGS := -fPIC \ + -DHAVE_CONFIG_H \ + -DG_THREADS_MANDATORY \ + -DG_DISABLE_DEPRECATED \ + -DUSE_EGL_TEGRA \ + -DUSE_EGL_WAYLAND + +CFLAGS += `pkg-config --cflags $(PKGS)` + +LDFLAGS = -Wl,--no-undefined -L/usr/lib/aarch64-linux-gnu/tegra -L/usr/local/cuda-$(CUDA_VER)/targets/aarch64-linux/lib/ + +LIBS = -lnvbufsurface -lGLESv2 -lEGL -lX11 -lm -lcuda -lcudart -lwayland-client -lwayland-egl + +LIBS += `pkg-config --libs $(PKGS)` + +all: $(SO_NAME) + +%.o: %.c + $(CC) -c $< $(CFLAGS) $(INCLUDES) -o $@ + +$(SO_NAME): $(OBJS) + $(CC) -shared -o $(SO_NAME) $(OBJS) $(LIBS) $(LDFLAGS) + +.PHONY: install +install: $(SO_NAME) + cp -vp $(SO_NAME) $(DEST_DIR) + +.PHONY: clean +clean: + rm -rf $(OBJS) $(SO_NAME) + diff --git a/gstegl_src/gst-egl/chooseNvcfgProfile.mk b/gstegl_src/gst-egl/chooseNvcfgProfile.mk new file mode 100644 index 0000000..65de78c --- /dev/null +++ b/gstegl_src/gst-egl/chooseNvcfgProfile.mk @@ -0,0 +1,41 @@ +# Copyright (c) 2020, NVIDIA CORPORATION. All Rights Reserved. +# +# NVIDIA Corporation and its licensors retain all intellectual property and +# proprietary rights in and to this software and related documentation. Any +# use, reproduction, disclosure or distribution of this software and related +# documentation without an express license agreement from NVIDIA Corporation +# is strictly prohibited. +# +# Source for this file: $(TEGRA_TOP)/tmake/artifacts/CommonRulesNvMake.tmk + +ifeq ($(NV_BUILD_CONFIGURATION_LINUX_USERSPACE_IS_EMBEDDED),1) + ifeq ($(NV_BUILD_CONFIGURATION_IS_EXTERNAL),0) + NVCFG_PROFILE=tegragpu_unix_arm_embedded_profile + else ifeq ($(NV_BUILD_CONFIGURATION_IS_GNEXT),1) + NVCFG_PROFILE=tegragpu_unix_arm_embedded_gnext_profile + else + NVCFG_PROFILE=tegragpu_unix_arm_embedded_external_profile + endif +else ifeq ($(NV_BUILD_CONFIGURATION_LINUX_USERSPACE_IS_L4T),1) + ifneq ($(NV_BUILD_CONFIGURATION_IS_EXTERNAL),0) + ifeq ($(NV_BUILD_CONFIGURATION_IS_GNEXT),1) + NVCFG_PROFILE=l4t_global_gnext_profile + else + NVCFG_PROFILE=l4t_global_external_profile + endif + else + NVCFG_PROFILE=tegragpu_unix_arm_global_profile + endif +else ifeq ($(NV_BUILD_CONFIGURATION_OS_IS_QNX),1) + ifeq ($(NV_BUILD_CONFIGURATION_IS_EXTERNAL),0) + NVCFG_PROFILE=tegragpu_unix_arm_embedded_profile + else ifeq ($(NV_BUILD_CONFIGURATION_IS_GNEXT),1) + NVCFG_PROFILE=tegragpu_unix_arm_embedded_gnext_profile + else ifeq ($(NV_BUILD_CONFIGURATION_IS_SAFETY),1) + NVCFG_PROFILE=tegra_with_dgpu_embedded_safety_external_profile + else + NVCFG_PROFILE=tegragpu_unix_arm_embedded_external_profile + endif +else + $(error Unsupported OS) +endif diff --git a/gstegl_src/gst-egl/ext/eglgles/gstegladaptation.c b/gstegl_src/gst-egl/ext/eglgles/gstegladaptation.c new file mode 100644 index 0000000..1cb25e4 --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/gstegladaptation.c @@ -0,0 +1,716 @@ +/* + * GStreamer EGL/GLES Sink Adaptation + * Copyright (C) 2012-2013 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * @author: Sebastian Dröge + * @author: Thiago Santos + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "gstegladaptation.h" + +#include +#include + +#define GST_CAT_DEFAULT egladaption_debug +GST_DEBUG_CATEGORY (egladaption_debug); + +/* GLESv2 GLSL Shaders + * + * OpenGL ES Standard does not mandate YUV support. This is + * why most of these shaders deal with Packed/Planar YUV->RGB + * conversion. + */ + +/* *INDENT-OFF* */ +/* Direct vertex copy */ +static const char *vert_COPY_prog = { + "attribute vec3 position;" + "attribute vec2 texpos;" + "varying vec2 opos;" + "void main(void)" + "{" + " opos = texpos;" + " gl_Position = vec4(position, 1.0);" + "}" +}; + +static const char *vert_COPY_prog_no_tex = { + "attribute vec3 position;" + "void main(void)" + "{" + " gl_Position = vec4(position, 1.0);" + "}" +}; + +/* Paint all black */ +static const char *frag_BLACK_prog = { + "precision mediump float;" + "void main(void)" + "{" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);" + "}" +}; + +/* Direct fragments copy with stride-scaling */ +static const char *frag_COPY_prog = { + "precision mediump float;" + "varying vec2 opos;" + "uniform sampler2D tex;" + "uniform vec2 tex_scale0;" + "uniform vec2 tex_scale1;" + "uniform vec2 tex_scale2;" + "void main(void)" + "{" + " vec4 t = texture2D(tex, opos / tex_scale0);" + " gl_FragColor = vec4(t.rgb, 1.0);" + "}" +}; + +/* Channel reordering for XYZ <-> ZYX conversion */ +static const char *frag_REORDER_prog = { + "precision mediump float;" + "varying vec2 opos;" + "uniform sampler2D tex;" + "uniform vec2 tex_scale0;" + "uniform vec2 tex_scale1;" + "uniform vec2 tex_scale2;" + "void main(void)" + "{" + " vec4 t = texture2D(tex, opos / tex_scale0);" + " gl_FragColor = vec4(t.%c, t.%c, t.%c, 1.0);" + "}" +}; + +/* Packed YUV converters */ + +/** AYUV to RGB conversion */ +static const char *frag_AYUV_prog = { + "precision mediump float;" + "varying vec2 opos;" + "uniform sampler2D tex;" + "uniform vec2 tex_scale0;" + "uniform vec2 tex_scale1;" + "uniform vec2 tex_scale2;" + "const vec3 offset = vec3(-0.0625, -0.5, -0.5);" + "const vec3 rcoeff = vec3(1.164, 0.000, 1.596);" + "const vec3 gcoeff = vec3(1.164,-0.391,-0.813);" + "const vec3 bcoeff = vec3(1.164, 2.018, 0.000);" + "void main(void) {" + " float r,g,b;" + " vec3 yuv;" + " yuv = texture2D(tex,opos / tex_scale0).gba;" + " yuv += offset;" + " r = dot(yuv, rcoeff);" + " g = dot(yuv, gcoeff);" + " b = dot(yuv, bcoeff);" + " gl_FragColor=vec4(r,g,b,1.0);" + "}" +}; + +/* Planar YUV converters */ + +/** YUV to RGB conversion */ +static const char *frag_PLANAR_YUV_prog = { + "precision mediump float;" + "varying vec2 opos;" + "uniform sampler2D Ytex,Utex,Vtex;" + "uniform vec2 tex_scale0;" + "uniform vec2 tex_scale1;" + "uniform vec2 tex_scale2;" + "const vec3 offset = vec3(-0.0625, -0.5, -0.5);" + "const vec3 rcoeff = vec3(1.164, 0.000, 1.596);" + "const vec3 gcoeff = vec3(1.164,-0.391,-0.813);" + "const vec3 bcoeff = vec3(1.164, 2.018, 0.000);" + "void main(void) {" + " float r,g,b;" + " vec3 yuv;" + " yuv.x=texture2D(Ytex,opos / tex_scale0).r;" + " yuv.y=texture2D(Utex,opos / tex_scale1).r;" + " yuv.z=texture2D(Vtex,opos / tex_scale2).r;" + " yuv += offset;" + " r = dot(yuv, rcoeff);" + " g = dot(yuv, gcoeff);" + " b = dot(yuv, bcoeff);" + " gl_FragColor=vec4(r,g,b,1.0);" + "}" +}; + +/** NV12/NV21 to RGB conversion */ +static const char *frag_NV12_NV21_prog = { + "precision mediump float;" + "varying vec2 opos;" + "uniform sampler2D Ytex,UVtex;" + "uniform vec2 tex_scale0;" + "uniform vec2 tex_scale1;" + "uniform vec2 tex_scale2;" + "const vec3 offset = vec3(-0.0625, -0.5, -0.5);" + "const vec3 rcoeff = vec3(1.164, 0.000, 1.596);" + "const vec3 gcoeff = vec3(1.164,-0.391,-0.813);" + "const vec3 bcoeff = vec3(1.164, 2.018, 0.000);" + "void main(void) {" + " float r,g,b;" + " vec3 yuv;" + " yuv.x=texture2D(Ytex,opos / tex_scale0).r;" + " yuv.yz=texture2D(UVtex,opos / tex_scale1).%c%c;" + " yuv += offset;" + " r = dot(yuv, rcoeff);" + " g = dot(yuv, gcoeff);" + " b = dot(yuv, bcoeff);" + " gl_FragColor=vec4(r,g,b,1.0);" + "}" +}; +/* *INDENT-ON* */ + +void +gst_egl_adaption_init (void) +{ + GST_DEBUG_CATEGORY_INIT (egladaption_debug, "egladaption", 0, + "EGL adaption layer"); +} + +static GstCaps * +_gst_video_format_new_template_caps (GstVideoFormat format) +{ + return gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, gst_video_format_to_string (format), + "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); +} + +GstCaps * +gst_egl_adaptation_fill_supported_fbuffer_configs (GstEglAdaptationContext * + ctx) +{ + GstCaps *caps = NULL, *copy1, *copy2; + guint i, n; + + GST_DEBUG_OBJECT (ctx->element, + "Building initial list of wanted eglattribs per format"); + + /* Init supported format/caps list */ + if (_gst_egl_choose_config (ctx, TRUE, NULL)) { + + caps = gst_caps_new_empty (); + + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGBA)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGRA)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_ARGB)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_ABGR)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGBx)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGRx)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_xRGB)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_xBGR)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_AYUV)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y444)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGB)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGR)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_I420)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_YV12)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_NV12)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_NV21)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y42B)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y41B)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGB16)); + + copy1 = gst_caps_copy (caps); + copy2 = gst_caps_copy (caps); + +#ifndef HAVE_IOS + n = gst_caps_get_size (caps); + for (i = 0; i < n; i++) { + GstCapsFeatures *features = + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, NULL); + gst_caps_set_features (caps, i, features); + } +#endif + + n = gst_caps_get_size (copy1); + for (i = 0; i < n; i++) { + GstCapsFeatures *features = + gst_caps_features_new + (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, NULL); + gst_caps_set_features (copy1, i, features); + } + + gst_caps_append (caps, copy1); + gst_caps_append (caps, copy2); + + n = gst_caps_get_size (caps); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGRx)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGBA)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_I420)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_NV12)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGB)); + gst_caps_append (caps, + _gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGR)); + for (i = n; i < n+6; i++) { + GstCapsFeatures *features = + gst_caps_features_new ("memory:NVMM", NULL); + gst_caps_set_features (caps, i, features); + } + + } else { + GST_INFO_OBJECT (ctx->element, + "EGL display doesn't support RGBA8888 config"); + } + + return caps; +} + +void +gst_egl_adaptation_cleanup (GstEglAdaptationContext * ctx) +{ + gint i; + + if (ctx->have_vbo) { + glDeleteBuffers (1, &ctx->position_buffer); + glDeleteBuffers (1, &ctx->index_buffer); + ctx->have_vbo = FALSE; + } + + if (ctx->have_texture) { + glDeleteTextures (ctx->n_textures, ctx->texture); + ctx->have_texture = FALSE; + ctx->n_textures = 0; + } + + for (i = 0; i < 2; i++) { + if (ctx->glslprogram[i]) { + glUseProgram (0); + glDetachShader (ctx->glslprogram[i], ctx->fragshader[i]); + glDetachShader (ctx->glslprogram[i], ctx->vertshader[i]); + glDeleteProgram (ctx->glslprogram[i]); + glDeleteShader (ctx->fragshader[i]); + glDeleteShader (ctx->vertshader[i]); + ctx->glslprogram[i] = 0; + ctx->fragshader[i] = 0; + ctx->vertshader[i] = 0; + } + } + + gst_egl_adaptation_context_make_current (ctx, FALSE); + + gst_egl_adaptation_destroy_surface (ctx); + gst_egl_adaptation_destroy_context (ctx); +} + +gboolean +got_gl_error (const char *wtf) +{ + GLuint error = GL_NO_ERROR; + + if ((error = glGetError ()) != GL_NO_ERROR) { + GST_CAT_ERROR (GST_CAT_DEFAULT, "GL ERROR: %s returned 0x%04x", wtf, error); + return TRUE; + } + return FALSE; +} + +static gboolean +create_shader_program (GstEglAdaptationContext * ctx, GLuint * prog, + GLuint * vert, GLuint * frag, const gchar * vert_text, + const gchar * frag_text) +{ + GLint test; + GLchar *info_log; + + /* Build shader program for video texture rendering */ + *vert = glCreateShader (GL_VERTEX_SHADER); + GST_DEBUG_OBJECT (ctx->element, "Sending %s to handle %d", vert_text, *vert); + glShaderSource (*vert, 1, &vert_text, NULL); + if (got_gl_error ("glShaderSource vertex")) + goto HANDLE_ERROR; + + glCompileShader (*vert); + if (got_gl_error ("glCompileShader vertex")) + goto HANDLE_ERROR; + + glGetShaderiv (*vert, GL_COMPILE_STATUS, &test); + if (test != GL_FALSE) + GST_DEBUG_OBJECT (ctx->element, "Successfully compiled vertex shader"); + else { + GST_ERROR_OBJECT (ctx->element, "Couldn't compile vertex shader"); + glGetShaderiv (*vert, GL_INFO_LOG_LENGTH, &test); + info_log = g_new0 (GLchar, test); + glGetShaderInfoLog (*vert, test, NULL, info_log); + GST_INFO_OBJECT (ctx->element, "Compilation info log:\n%s", info_log); + g_free (info_log); + goto HANDLE_ERROR; + } + + *frag = glCreateShader (GL_FRAGMENT_SHADER); + GST_DEBUG_OBJECT (ctx->element, "Sending %s to handle %d", frag_text, *frag); + glShaderSource (*frag, 1, &frag_text, NULL); + if (got_gl_error ("glShaderSource fragment")) + goto HANDLE_ERROR; + + glCompileShader (*frag); + if (got_gl_error ("glCompileShader fragment")) + goto HANDLE_ERROR; + + glGetShaderiv (*frag, GL_COMPILE_STATUS, &test); + if (test != GL_FALSE) + GST_DEBUG_OBJECT (ctx->element, "Successfully compiled fragment shader"); + else { + GST_ERROR_OBJECT (ctx->element, "Couldn't compile fragment shader"); + glGetShaderiv (*frag, GL_INFO_LOG_LENGTH, &test); + info_log = g_new0 (GLchar, test); + glGetShaderInfoLog (*frag, test, NULL, info_log); + GST_INFO_OBJECT (ctx->element, "Compilation info log:\n%s", info_log); + g_free (info_log); + goto HANDLE_ERROR; + } + + *prog = glCreateProgram (); + if (got_gl_error ("glCreateProgram")) + goto HANDLE_ERROR; + glAttachShader (*prog, *vert); + if (got_gl_error ("glAttachShader vertices")) + goto HANDLE_ERROR; + glAttachShader (*prog, *frag); + if (got_gl_error ("glAttachShader fragments")) + goto HANDLE_ERROR; + glLinkProgram (*prog); + glGetProgramiv (*prog, GL_LINK_STATUS, &test); + if (test != GL_FALSE) { + GST_DEBUG_OBJECT (ctx->element, "GLES: Successfully linked program"); + } else { + GST_ERROR_OBJECT (ctx->element, "Couldn't link program"); + goto HANDLE_ERROR; + } + + return TRUE; + +HANDLE_ERROR: + { + if (*frag && *prog) + glDetachShader (*prog, *frag); + if (*vert && *prog) + glDetachShader (*prog, *vert); + if (*prog) + glDeleteProgram (*prog); + if (*frag) + glDeleteShader (*frag); + if (*vert) + glDeleteShader (*vert); + *prog = 0; + *frag = 0; + *vert = 0; + + return FALSE; + } +} + +gboolean +gst_egl_adaptation_init_surface (GstEglAdaptationContext * ctx, + GstVideoFormat format) +{ + GLboolean ret; + const gchar *texnames[3] = { NULL, }; + gchar *frag_prog = NULL; + gboolean free_frag_prog = FALSE; + gint i; + + GST_DEBUG_OBJECT (ctx->element, "Enter EGL surface setup"); + + if (!gst_egl_adaptation_create_surface (ctx)) { + GST_ERROR_OBJECT (ctx->element, "Can't create surface"); + goto HANDLE_ERROR_LOCKED; + } + + if (!gst_egl_adaptation_context_make_current (ctx, TRUE)) + goto HANDLE_ERROR_LOCKED; + + gst_egl_adaptation_query_buffer_preserved (ctx); + + gst_egl_adaptation_init_exts (ctx); + + /* Save surface dims */ + gst_egl_adaptation_update_surface_dimensions (ctx); + + /* Save display's pixel aspect ratio + * + * DAR is reported as w/h * EGL_DISPLAY_SCALING wich is + * a constant with value 10000. This attribute is only + * supported if the EGL version is >= 1.2 + * XXX: Setup this as a property. + * or some other one time check. Right now it's being called once + * per frame. + */ + gst_egl_adaptation_query_par (ctx); + + /* We have a surface! */ + ctx->have_surface = TRUE; + + /* Init vertex and fragment GLSL shaders. + * Note: Shader compiler support is optional but we currently rely on it. + */ + + glGetBooleanv (GL_SHADER_COMPILER, &ret); + if (ret == GL_FALSE) { + GST_ERROR_OBJECT (ctx->element, "Shader compiler support is unavailable!"); + goto HANDLE_ERROR; + } + + /* Build shader program for video texture rendering */ + + switch (format) { + case GST_VIDEO_FORMAT_AYUV: + frag_prog = (gchar *) frag_AYUV_prog; + free_frag_prog = FALSE; + ctx->n_textures = 1; + texnames[0] = "tex"; + break; + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_Y41B: + frag_prog = (gchar *) frag_PLANAR_YUV_prog; + free_frag_prog = FALSE; + ctx->n_textures = 3; + texnames[0] = "Ytex"; + texnames[1] = "Utex"; + texnames[2] = "Vtex"; + break; + case GST_VIDEO_FORMAT_NV12: + frag_prog = g_strdup_printf (frag_NV12_NV21_prog, 'r', 'a'); + free_frag_prog = TRUE; + ctx->n_textures = 2; + texnames[0] = "Ytex"; + texnames[1] = "UVtex"; + break; + case GST_VIDEO_FORMAT_NV21: + frag_prog = g_strdup_printf (frag_NV12_NV21_prog, 'a', 'r'); + free_frag_prog = TRUE; + ctx->n_textures = 2; + texnames[0] = "Ytex"; + texnames[1] = "UVtex"; + break; + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_BGRA: + frag_prog = g_strdup_printf (frag_REORDER_prog, 'b', 'g', 'r'); + free_frag_prog = TRUE; + ctx->n_textures = 1; + texnames[0] = "tex"; + break; + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_ARGB: + frag_prog = g_strdup_printf (frag_REORDER_prog, 'g', 'b', 'a'); + free_frag_prog = TRUE; + ctx->n_textures = 1; + texnames[0] = "tex"; + break; + case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_ABGR: + frag_prog = g_strdup_printf (frag_REORDER_prog, 'a', 'b', 'g'); + free_frag_prog = TRUE; + ctx->n_textures = 1; + texnames[0] = "tex"; + break; + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_RGB16: + frag_prog = (gchar *) frag_COPY_prog; + free_frag_prog = FALSE; + ctx->n_textures = 1; + texnames[0] = "tex"; + break; + default: + g_assert_not_reached (); + break; + } + + if (!create_shader_program (ctx, + &ctx->glslprogram[0], + &ctx->vertshader[0], + &ctx->fragshader[0], vert_COPY_prog, frag_prog)) { + if (free_frag_prog) + g_free (frag_prog); + frag_prog = NULL; + goto HANDLE_ERROR; + } + if (free_frag_prog) + g_free (frag_prog); + frag_prog = NULL; + + ctx->position_loc[0] = glGetAttribLocation (ctx->glslprogram[0], "position"); + ctx->texpos_loc[0] = glGetAttribLocation (ctx->glslprogram[0], "texpos"); + ctx->tex_scale_loc[0][0] = + glGetUniformLocation (ctx->glslprogram[0], "tex_scale0"); + ctx->tex_scale_loc[0][1] = + glGetUniformLocation (ctx->glslprogram[0], "tex_scale1"); + ctx->tex_scale_loc[0][2] = + glGetUniformLocation (ctx->glslprogram[0], "tex_scale2"); + + for (i = 0; i < ctx->n_textures; i++) { + ctx->tex_loc[0][i] = + glGetUniformLocation (ctx->glslprogram[0], texnames[i]); + } + + if (!ctx->buffer_preserved) { + /* Build shader program for black borders */ + if (!create_shader_program (ctx, + &ctx->glslprogram[1], + &ctx->vertshader[1], + &ctx->fragshader[1], vert_COPY_prog_no_tex, frag_BLACK_prog)) + goto HANDLE_ERROR; + + ctx->position_loc[1] = + glGetAttribLocation (ctx->glslprogram[1], "position"); + } + + /* Generate textures */ + if (!ctx->have_texture) { + GST_INFO_OBJECT (ctx->element, "Performing initial texture setup"); + + glGenTextures (ctx->n_textures, ctx->texture); + if (got_gl_error ("glGenTextures")) + goto HANDLE_ERROR_LOCKED; + + for (i = 0; i < ctx->n_textures; i++) { + glBindTexture (GL_TEXTURE_2D, ctx->texture[i]); + if (got_gl_error ("glBindTexture")) + goto HANDLE_ERROR; + + /* Set 2D resizing params */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + /* If these are not set the texture image unit will return + * (R, G, B, A) = black on glTexImage2D for non-POT width/height + * frames. For a deeper explanation take a look at the OpenGL ES + * documentation for glTexParameter */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (got_gl_error ("glTexParameteri")) + goto HANDLE_ERROR_LOCKED; + } + + ctx->have_texture = TRUE; + } + + glUseProgram (0); + + return TRUE; + + /* Errors */ +HANDLE_ERROR_LOCKED: +HANDLE_ERROR: + GST_ERROR_OBJECT (ctx->element, "Couldn't setup EGL surface"); + return FALSE; +} + +gboolean +gst_egl_adaptation_choose_config (GstEglAdaptationContext * ctx) +{ + gint egl_configs; + + if (!_gst_egl_choose_config (ctx, FALSE, &egl_configs)) { + GST_ERROR_OBJECT (ctx->element, "eglChooseConfig failed"); + goto HANDLE_ERROR; + } + + if (egl_configs < 1) { + GST_ERROR_OBJECT (ctx->element, + "Could not find matching framebuffer config"); + goto HANDLE_ERROR; + } + + if (!gst_egl_adaptation_create_egl_context (ctx)) { + GST_ERROR_OBJECT (ctx->element, "Error getting context, eglCreateContext"); + goto HANDLE_ERROR; + } + + return TRUE; + + /* Errors */ +HANDLE_ERROR: + GST_ERROR_OBJECT (ctx->element, "Couldn't choose an usable config"); + return FALSE; +} + +GstEglAdaptationContext * +gst_egl_adaptation_context_new (GstElement * element) +{ + GstEglAdaptationContext *ctx = g_new0 (GstEglAdaptationContext, 1); + + ctx->element = gst_object_ref (element); + + gst_egl_adaptation_init (ctx); + return ctx; +} + +void +gst_egl_adaptation_context_free (GstEglAdaptationContext * ctx) +{ + gst_egl_adaptation_deinit (ctx); + if (GST_OBJECT_REFCOUNT(ctx->element)) + gst_object_unref (ctx->element); + g_free (ctx); +} + +gboolean +gst_egl_adaptation_reset_window (GstEglAdaptationContext * ctx, + GstVideoFormat format) +{ + if (!gst_egl_adaptation_context_make_current (ctx, FALSE)) + return FALSE; + + gst_egl_adaptation_destroy_surface (ctx); + + ctx->used_window = ctx->window; + + if (!gst_egl_adaptation_init_surface (ctx, format)) + return FALSE; + + if (!gst_egl_adaptation_context_make_current (ctx, TRUE)) + return FALSE; + + return TRUE; +} diff --git a/gstegl_src/gst-egl/ext/eglgles/gstegladaptation.h b/gstegl_src/gst-egl/ext/eglgles/gstegladaptation.h new file mode 100644 index 0000000..a55cc60 --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/gstegladaptation.h @@ -0,0 +1,202 @@ +/* + * GStreamer EGL/GLES Sink Adaptation + * Copyright (C) 2012-2013 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * @author: Sebastian Dröge + * @author: Thiago Santos + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __GST_EGL_ADAPTATION_H__ +#define __GST_EGL_ADAPTATION_H__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#if defined (USE_EGL_RPI) && defined(__GNUC__) +#ifndef __VCCOREVER__ +#define __VCCOREVER__ 0x04000000 +#endif + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC optimize ("gnu89-inline") +#endif + +#define EGL_EGLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES + +#ifdef HAVE_IOS +#include +#else +#include +#include +#include +#include +#include +#endif + +#if defined (USE_EGL_RPI) && defined(__GNUC__) +#pragma GCC reset_options +#pragma GCC diagnostic pop +#endif + +G_BEGIN_DECLS + +typedef struct _GstEglAdaptationContext GstEglAdaptationContext; +typedef struct _GstEglGlesImageFmt GstEglGlesImageFmt; + +#ifdef HAVE_IOS +typedef struct _GstEaglContext GstEaglContext; +#else +typedef struct _GstEglGlesRenderContext GstEglGlesRenderContext; +#endif + +typedef struct _coord5 +{ + float x; + float y; + float z; + float a; /* texpos x */ + float b; /* texpos y */ +} coord5; + +typedef struct +{ + GLuint texture; + EGLDisplay display; + EGLContext eglcontext; +} GstEGLGLESImageData; + +/* + * GstEglAdaptationContext: + * @have_vbo: Set if the GLES VBO setup has been performed + * @have_texture: Set if the GLES texture setup has been performed + * @have_surface: Set if the EGL surface setup has been performed + * + * The #GstEglAdaptationContext data structure. + */ +struct _GstEglAdaptationContext +{ + GstElement *element; + +#ifdef HAVE_IOS + GstEaglContext *eaglctx; + void * window, *used_window; +#else + GstEglGlesRenderContext *eglglesctx; + GstEGLDisplay *display, *set_display; + EGLNativeWindowType window, used_window; +#endif + + GLuint fragshader[2]; /* frame, border */ + GLuint vertshader[2]; /* frame, border */ + GLuint glslprogram[2]; /* frame, border */ + GLuint texture[3]; /* RGB/Y, U/UV, V */ + /* shader vars */ + GLuint position_loc[2]; /* frame, border */ + GLuint texpos_loc[1]; /* frame */ + GLuint tex_scale_loc[1][3]; /* [frame] RGB/Y, U/UV, V */ + GLuint tex_loc[1][3]; /* [frame] RGB/Y, U/UV, V */ + coord5 position_array[16]; /* 4 x Frame x-normal,y-normal, 4x Frame x-normal,y-flip, 4 x Border1, 4 x Border2 */ + unsigned short index_array[4]; + unsigned int position_buffer, index_buffer; + gint n_textures; + + gint surface_width; + gint surface_height; + gint pixel_aspect_ratio_n; + gint pixel_aspect_ratio_d; + + gboolean have_vbo; + gboolean have_texture; + gboolean have_surface; + gboolean buffer_preserved; +}; + +GST_DEBUG_CATEGORY_EXTERN (egladaption_debug); + +void gst_egl_adaption_init (void); + +GstEglAdaptationContext * gst_egl_adaptation_context_new (GstElement * element); +void gst_egl_adaptation_context_free (GstEglAdaptationContext * ctx); +void gst_egl_adaptation_init (GstEglAdaptationContext * ctx); +void gst_egl_adaptation_deinit (GstEglAdaptationContext * ctx); + +gboolean gst_egl_adaptation_create_surface (GstEglAdaptationContext * ctx); +void gst_egl_adaptation_query_buffer_preserved (GstEglAdaptationContext * ctx); +void gst_egl_adaptation_query_par (GstEglAdaptationContext * ctx); +void gst_egl_adaptation_destroy_surface (GstEglAdaptationContext * ctx); +void gst_egl_adaptation_destroy_context (GstEglAdaptationContext * ctx); +void gst_egl_adaptation_release_thread (void); + +gboolean +gst_egl_adaptation_create_egl_context (GstEglAdaptationContext * ctx); + +#ifndef HAVE_IOS +EGLContext gst_egl_adaptation_context_get_egl_context (GstEglAdaptationContext * ctx); +#endif + +/* platform window */ +gboolean gst_egl_adaptation_create_native_window (GstEglAdaptationContext +* ctx, gint width, gint height, gpointer * own_window_data, gchar* winsys); +void gst_egl_adaptation_destroy_native_window (GstEglAdaptationContext * ctx, gpointer * own_window_data, gchar* winsys); + +GstCaps *gst_egl_adaptation_fill_supported_fbuffer_configs (GstEglAdaptationContext * ctx); +gboolean gst_egl_adaptation_init_display (GstEglAdaptationContext * ctx, gchar* winsys); +gboolean gst_egl_adaptation_choose_config (GstEglAdaptationContext * ctx); +gboolean gst_egl_adaptation_init_surface (GstEglAdaptationContext * ctx, GstVideoFormat format); +void gst_egl_adaptation_init_exts (GstEglAdaptationContext * ctx); +gboolean gst_egl_adaptation_update_surface_dimensions (GstEglAdaptationContext * ctx); +gboolean _gst_egl_choose_config (GstEglAdaptationContext * ctx, gboolean try_only, gint * num_configs); + +gboolean got_gl_error (const char *wtf); +gboolean got_egl_error (const char *wtf); + +void gst_egl_adaptation_set_window (GstEglAdaptationContext * ctx, guintptr window); + +gboolean gst_egl_adaptation_context_make_current (GstEglAdaptationContext * ctx, gboolean bind); +void gst_egl_adaptation_cleanup (GstEglAdaptationContext * ctx); + +void gst_egl_adaptation_bind_API (GstEglAdaptationContext * ctx); + +gboolean gst_egl_adaptation_context_swap_buffers (GstEglAdaptationContext * ctx); + +gboolean gst_egl_adaptation_reset_window (GstEglAdaptationContext * ctx, GstVideoFormat format); + +#ifndef HAVE_IOS +/* TODO: The goal is to move this function to gstegl lib (or + * splitted between gstegl lib and gstgl lib) in order to be used in + * webkitVideoSink + * So it has to be independent of GstEglAdaptationContext */ +GstBuffer * +gst_egl_image_allocator_alloc_eglimage (GstAllocator * allocator, + GstEGLDisplay * display, EGLContext eglcontext, GstVideoFormat format, + gint width, gint height); +#endif + +G_END_DECLS + +#endif /* __GST_EGL_ADAPTATION_H__ */ diff --git a/gstegl_src/gst-egl/ext/eglgles/gstegladaptation_egl.c b/gstegl_src/gst-egl/ext/eglgles/gstegladaptation_egl.c new file mode 100644 index 0000000..24f1d59 --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/gstegladaptation_egl.c @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2012-2013 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * @author: Sebastian Dröge + * @author: Thiago Santos + * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "gstegladaptation.h" +#include "gsteglglessink.h" +#include "video_platform_wrapper.h" +#include + +#include +#include +#include +#include +#include + +#define GST_CAT_DEFAULT egladaption_debug + +/* Some EGL implementations are reporting wrong + * values for the display's EGL_PIXEL_ASPECT_RATIO. + * They are required by the khronos specs to report + * this value as w/h * EGL_DISPLAY_SCALING (Which is + * a constant with value 10000) but at least the + * Galaxy SIII (Android) is reporting just 1 when + * w = h. We use these two to bound returned values to + * sanity. + */ +#define EGL_SANE_DAR_MIN ((EGL_DISPLAY_SCALING)/10) +#define EGL_SANE_DAR_MAX ((EGL_DISPLAY_SCALING)*10) + +#define GST_EGLGLESSINK_EGL_MIN_VERSION 1 + +static const EGLint eglglessink_RGBA8888_attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + +/* + * GstEglGlesRenderContext: + * @config: Current EGL config + * @eglcontext: Current EGL context + * @egl_minor: EGL version (minor) + * @egl_major: EGL version (major) + * + * This struct holds the sink's EGL/GLES rendering context. + */ +struct _GstEglGlesRenderContext +{ + EGLConfig config; + EGLContext eglcontext; + EGLSurface surface; + EGLint egl_minor, egl_major; +}; + +gboolean +got_egl_error (const char *wtf) +{ + EGLint error; + + if ((error = eglGetError ()) != EGL_SUCCESS) { + GST_CAT_DEBUG (GST_CAT_DEFAULT, "EGL ERROR: %s returned 0x%04x", wtf, + error); + return TRUE; + } + + return FALSE; +} + +/* Prints available EGL/GLES extensions + * If another rendering path is implemented this is the place + * where you want to check for the availability of its supporting + * EGL/GLES extensions. + */ +void +gst_egl_adaptation_init_exts (GstEglAdaptationContext * ctx) +{ +#ifndef GST_DISABLE_GST_DEBUG + const char *eglexts; + unsigned const char *glexts; + + eglexts = eglQueryString (gst_egl_display_get (ctx->display), EGL_EXTENSIONS); + glexts = glGetString (GL_EXTENSIONS); + + GST_DEBUG_OBJECT (ctx->element, "Available EGL extensions: %s\n", + GST_STR_NULL (eglexts)); + GST_DEBUG_OBJECT (ctx->element, "Available GLES extensions: %s\n", + GST_STR_NULL ((const char *) glexts)); +#endif + return; +} + +gboolean +gst_egl_adaptation_init_display (GstEglAdaptationContext * ctx, gchar* winsys) +{ + GstMessage *msg; + GstEglGlesSink *sink = (GstEglGlesSink *) ctx->element; + EGLDisplay display = EGL_NO_DISPLAY; + GST_DEBUG_OBJECT (ctx->element, "Enter EGL initial configuration"); + + if (!platform_wrapper_init ()) { + GST_ERROR_OBJECT (ctx->element, "Couldn't init EGL platform wrapper"); + goto HANDLE_ERROR; + } + + msg = + gst_message_new_need_context (GST_OBJECT_CAST (ctx->element), + GST_EGL_DISPLAY_CONTEXT_TYPE); + gst_element_post_message (GST_ELEMENT_CAST (ctx->element), msg); + + GST_OBJECT_LOCK (ctx->element); + if (!ctx->set_display) { + GstContext *context; + + GST_OBJECT_UNLOCK (ctx->element); + +#ifdef USE_EGL_WAYLAND + if (g_strcmp0(winsys, "wayland") == 0) { + display = eglGetDisplay (platform_initialize_display_wayland()); + } +#endif + +#ifdef USE_EGL_X11 + if (g_strcmp0(winsys, "x11") == 0) { + display = eglGetDisplay (sink->display); + } +#endif + + if (display == EGL_NO_DISPLAY) { + GST_ERROR_OBJECT (ctx->element, "Could not get EGL display connection"); + goto HANDLE_ERROR; /* No EGL error is set by eglGetDisplay() */ + } + ctx->display = gst_egl_display_new (display, NULL); + + context = gst_context_new_egl_display (ctx->display, FALSE); + msg = gst_message_new_have_context (GST_OBJECT (ctx->element), context); + gst_element_post_message (GST_ELEMENT_CAST (ctx->element), msg); + } else { + ctx->display = ctx->set_display; + GST_OBJECT_UNLOCK (ctx->element); + } + + if (!eglInitialize (gst_egl_display_get (ctx->display), + &ctx->eglglesctx->egl_major, &ctx->eglglesctx->egl_minor)) { + got_egl_error ("eglInitialize"); + GST_ERROR_OBJECT (ctx->element, "Could not init EGL display connection"); + goto HANDLE_EGL_ERROR; + } + + /* Check against required EGL version + * XXX: Need to review the version requirement in terms of the needed API + */ + if (ctx->eglglesctx->egl_major < GST_EGLGLESSINK_EGL_MIN_VERSION) { + GST_ERROR_OBJECT (ctx->element, "EGL v%d needed, but you only have v%d.%d", + GST_EGLGLESSINK_EGL_MIN_VERSION, ctx->eglglesctx->egl_major, + ctx->eglglesctx->egl_minor); + goto HANDLE_ERROR; + } + + GST_INFO_OBJECT (ctx->element, "System reports supported EGL version v%d.%d", + ctx->eglglesctx->egl_major, ctx->eglglesctx->egl_minor); + + eglBindAPI (EGL_OPENGL_ES_API); + + return TRUE; + + /* Errors */ +HANDLE_EGL_ERROR: + GST_ERROR_OBJECT (ctx->element, "EGL call returned error %x", eglGetError ()); +HANDLE_ERROR: + GST_ERROR_OBJECT (ctx->element, "Couldn't setup window/surface from handle"); + return FALSE; +} + +gboolean +gst_egl_adaptation_context_make_current (GstEglAdaptationContext * ctx, + gboolean bind) +{ + g_assert (ctx->display != NULL); + + if (bind && ctx->eglglesctx->surface && ctx->eglglesctx->eglcontext) { + EGLContext *cur_ctx = eglGetCurrentContext (); + + if (cur_ctx == ctx->eglglesctx->eglcontext) { + GST_DEBUG_OBJECT (ctx->element, + "Already attached the context to thread %p", g_thread_self ()); + return TRUE; + } + + GST_DEBUG_OBJECT (ctx->element, "Attaching context to thread %p", + g_thread_self ()); + if (!eglMakeCurrent (gst_egl_display_get (ctx->display), + ctx->eglglesctx->surface, ctx->eglglesctx->surface, + ctx->eglglesctx->eglcontext)) { + got_egl_error ("eglMakeCurrent"); + GST_ERROR_OBJECT (ctx->element, "Couldn't bind context"); + return FALSE; + } + } else { + GST_DEBUG_OBJECT (ctx->element, "Detaching context from thread %p", + g_thread_self ()); + if (!eglMakeCurrent (gst_egl_display_get (ctx->display), + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + got_egl_error ("eglMakeCurrent"); + GST_ERROR_OBJECT (ctx->element, "Couldn't unbind context"); + return FALSE; + } + } + + return TRUE; +} + +/* XXX: Lock eglgles context? */ +gboolean +gst_egl_adaptation_update_surface_dimensions (GstEglAdaptationContext * ctx) +{ + gint width, height; + + /* Save surface dims */ + eglQuerySurface (gst_egl_display_get (ctx->display), + ctx->eglglesctx->surface, EGL_WIDTH, &width); + eglQuerySurface (gst_egl_display_get (ctx->display), + ctx->eglglesctx->surface, EGL_HEIGHT, &height); + + if (width != ctx->surface_width || height != ctx->surface_height) { + ctx->surface_width = width; + ctx->surface_height = height; + GST_INFO_OBJECT (ctx->element, "Got surface of %dx%d pixels", width, + height); + return TRUE; + } + + return FALSE; +} + +void +gst_egl_adaptation_bind_API (GstEglAdaptationContext * ctx) +{ + eglBindAPI (EGL_OPENGL_ES_API); +} + +gboolean +gst_egl_adaptation_context_swap_buffers (GstEglAdaptationContext * ctx) +{ + gboolean ret = eglSwapBuffers (gst_egl_display_get (ctx->display), + ctx->eglglesctx->surface); + if (ret == EGL_FALSE) { + got_egl_error ("eglSwapBuffers"); + } + return ret; +} + +gboolean +_gst_egl_choose_config (GstEglAdaptationContext * ctx, gboolean try_only, + gint * num_configs) +{ + EGLint cfg_number; + gboolean ret; + EGLConfig *config = NULL; + + if (!try_only) + config = &ctx->eglglesctx->config; + + ret = eglChooseConfig (gst_egl_display_get (ctx->display), + eglglessink_RGBA8888_attribs, config, 1, &cfg_number) != EGL_FALSE; + + if (!ret) + got_egl_error ("eglChooseConfig"); + else if (num_configs) + *num_configs = cfg_number; + return ret; +} + +gboolean +gst_egl_adaptation_create_surface (GstEglAdaptationContext * ctx) +{ + ctx->eglglesctx->surface = + eglCreateWindowSurface (gst_egl_display_get (ctx->display), + ctx->eglglesctx->config, ctx->used_window, NULL); + + if (ctx->eglglesctx->surface == EGL_NO_SURFACE) { + got_egl_error ("eglCreateWindowSurface"); + GST_ERROR_OBJECT (ctx->element, "Can't create surface"); + return FALSE; + } + return TRUE; +} + +void +gst_egl_adaptation_query_buffer_preserved (GstEglAdaptationContext * ctx) +{ + EGLint swap_behavior; + + ctx->buffer_preserved = FALSE; + if (eglQuerySurface (gst_egl_display_get (ctx->display), + ctx->eglglesctx->surface, EGL_SWAP_BEHAVIOR, &swap_behavior)) { + GST_DEBUG_OBJECT (ctx->element, "Buffer swap behavior %x", swap_behavior); + ctx->buffer_preserved = swap_behavior == EGL_BUFFER_PRESERVED; + } else { + GST_DEBUG_OBJECT (ctx->element, "Can't query buffer swap behavior"); + } +} + +void +gst_egl_adaptation_query_par (GstEglAdaptationContext * ctx) +{ + EGLint display_par; + + /* fixed value */ + ctx->pixel_aspect_ratio_d = EGL_DISPLAY_SCALING; + + /* Save display's pixel aspect ratio + * + * DAR is reported as w/h * EGL_DISPLAY_SCALING wich is + * a constant with value 10000. This attribute is only + * supported if the EGL version is >= 1.2 + * XXX: Setup this as a property. + * or some other one time check. Right now it's being called once + * per frame. + */ + if (ctx->eglglesctx->egl_major == 1 && ctx->eglglesctx->egl_minor < 2) { + GST_DEBUG_OBJECT (ctx->element, "Can't query PAR. Using default: %dx%d", + EGL_DISPLAY_SCALING, EGL_DISPLAY_SCALING); + ctx->pixel_aspect_ratio_n = EGL_DISPLAY_SCALING; + } else { + eglQuerySurface (gst_egl_display_get (ctx->display), + ctx->eglglesctx->surface, EGL_PIXEL_ASPECT_RATIO, &display_par); + /* Fix for outbound DAR reporting on some implementations not + * honoring the 'should return w/h * EGL_DISPLAY_SCALING' spec + * requirement + */ + if (display_par == EGL_UNKNOWN || display_par < EGL_SANE_DAR_MIN || + display_par > EGL_SANE_DAR_MAX) { + GST_DEBUG_OBJECT (ctx->element, "Nonsensical PAR value returned: %d. " + "Bad EGL implementation? " + "Will use default: %d/%d", ctx->pixel_aspect_ratio_n, + EGL_DISPLAY_SCALING, EGL_DISPLAY_SCALING); + ctx->pixel_aspect_ratio_n = EGL_DISPLAY_SCALING; + } else { + ctx->pixel_aspect_ratio_n = display_par; + } + } +} + +gboolean +gst_egl_adaptation_create_egl_context (GstEglAdaptationContext * ctx) +{ + EGLint con_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + + ctx->eglglesctx->eglcontext = + eglCreateContext (gst_egl_display_get (ctx->display), + ctx->eglglesctx->config, EGL_NO_CONTEXT, con_attribs); + + if (ctx->eglglesctx->eglcontext == EGL_NO_CONTEXT) { + GST_ERROR_OBJECT (ctx->element, "EGL call returned error %x", + eglGetError ()); + return FALSE; + } + + GST_DEBUG_OBJECT (ctx->element, "EGL Context: %p", + ctx->eglglesctx->eglcontext); + + return TRUE; +} + +EGLContext +gst_egl_adaptation_context_get_egl_context (GstEglAdaptationContext * ctx) +{ + g_return_val_if_fail (ctx != NULL, EGL_NO_CONTEXT); + + return ctx->eglglesctx->eglcontext; +} + +static void +gst_egl_gles_image_data_free (GstEGLGLESImageData * data) +{ + if (!eglMakeCurrent (data->display, + EGL_NO_SURFACE, EGL_NO_SURFACE, data->eglcontext)) { + got_egl_error ("eglMakeCurrent"); + g_slice_free (GstEGLGLESImageData, data); + return; + } + glDeleteTextures (1, &data->texture); + g_slice_free (GstEGLGLESImageData, data); +} + + +GstBuffer * +gst_egl_image_allocator_alloc_eglimage (GstAllocator * allocator, + GstEGLDisplay * display, EGLContext eglcontext, GstVideoFormat format, + gint width, gint height) +{ + GstEGLGLESImageData *data = NULL; + GstBuffer *buffer; + GstVideoInfo info; + guint i; + gint stride[3]; + gsize offset[3]; + GstMemory *mem[3] = { NULL, NULL, NULL }; + guint n_mem; + GstMemoryFlags flags = 0; + + memset (stride, 0, sizeof (stride)); + memset (offset, 0, sizeof (offset)); + + if (!gst_egl_image_memory_is_mappable ()) + flags |= GST_MEMORY_FLAG_NOT_MAPPABLE; + /* See https://bugzilla.gnome.org/show_bug.cgi?id=695203 */ + flags |= GST_MEMORY_FLAG_NO_SHARE; + + gst_video_info_set_format (&info, format, width, height); + + switch (format) { + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_BGR:{ + gsize size; + EGLImageKHR image; + + mem[0] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_RGB, GST_VIDEO_INFO_WIDTH (&info), + GST_VIDEO_INFO_HEIGHT (&info), &size); + if (mem[0]) { + stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info); + n_mem = 1; + GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE); + } else { + data = g_slice_new0 (GstEGLGLESImageData); + data->display = gst_egl_display_get (display); + data->eglcontext = eglcontext; + + stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 3); + size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info); + + glGenTextures (1, &data->texture); + if (got_gl_error ("glGenTextures")) + goto mem_error; + + glBindTexture (GL_TEXTURE_2D, data->texture); + if (got_gl_error ("glBindTexture")) + goto mem_error; + + /* Set 2D resizing params */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* If these are not set the texture image unit will return + * * (R, G, B, A) = black on glTexImage2D for non-POT width/height + * * frames. For a deeper explanation take a look at the OpenGL ES + * * documentation for glTexParameter */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (got_gl_error ("glTexParameteri")) + goto mem_error; + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, + GST_VIDEO_INFO_WIDTH (&info), + GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + if (got_gl_error ("glTexImage2D")) + goto mem_error; + + image = + gst_egl_display_image_create (display, + eglcontext, EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer) (uintptr_t) data->texture, NULL); + if (got_egl_error ("eglCreateImageKHR")) + goto mem_error; + + mem[0] = + gst_egl_image_allocator_wrap (allocator, display, + image, GST_VIDEO_GL_TEXTURE_TYPE_RGB, + flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free); + n_mem = 1; + } + break; + } + case GST_VIDEO_FORMAT_RGB16:{ + EGLImageKHR image; + gsize size; + + mem[0] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_RGB, GST_VIDEO_INFO_WIDTH (&info), + GST_VIDEO_INFO_HEIGHT (&info), &size); + if (mem[0]) { + stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info); + n_mem = 1; + GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE); + } else { + data = g_slice_new0 (GstEGLGLESImageData); + data->display = gst_egl_display_get (display); + data->eglcontext = eglcontext; + + stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 2); + size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info); + + glGenTextures (1, &data->texture); + if (got_gl_error ("glGenTextures")) + goto mem_error; + + glBindTexture (GL_TEXTURE_2D, data->texture); + if (got_gl_error ("glBindTexture")) + goto mem_error; + + /* Set 2D resizing params */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* If these are not set the texture image unit will return + * * (R, G, B, A) = black on glTexImage2D for non-POT width/height + * * frames. For a deeper explanation take a look at the OpenGL ES + * * documentation for glTexParameter */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (got_gl_error ("glTexParameteri")) + goto mem_error; + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, + GST_VIDEO_INFO_WIDTH (&info), + GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + NULL); + if (got_gl_error ("glTexImage2D")) + goto mem_error; + + image = + gst_egl_display_image_create (display, + eglcontext, EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer) (uintptr_t) data->texture, NULL); + if (got_egl_error ("eglCreateImageKHR")) + goto mem_error; + + mem[0] = + gst_egl_image_allocator_wrap (allocator, display, + image, GST_VIDEO_GL_TEXTURE_TYPE_RGB, + flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free); + n_mem = 1; + } + break; + } + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_NV21:{ + EGLImageKHR image; + gsize size[2]; + + mem[0] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, + 0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]); + mem[1] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE_ALPHA, + GST_VIDEO_INFO_COMP_WIDTH (&info, 1), + GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]); + + if (mem[0] && mem[1]) { + stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info); + offset[1] = size[0]; + stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info); + n_mem = 2; + GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE); + GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE); + } else { + if (mem[0]) + gst_memory_unref (mem[0]); + if (mem[1]) + gst_memory_unref (mem[1]); + mem[0] = mem[1] = NULL; + + stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0)); + stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1) * 2); + offset[1] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0); + size[0] = offset[1]; + size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1); + + for (i = 0; i < 2; i++) { + data = g_slice_new0 (GstEGLGLESImageData); + data->display = gst_egl_display_get (display); + data->eglcontext = eglcontext; + + glGenTextures (1, &data->texture); + if (got_gl_error ("glGenTextures")) + goto mem_error; + + glBindTexture (GL_TEXTURE_2D, data->texture); + if (got_gl_error ("glBindTexture")) + goto mem_error; + + /* Set 2D resizing params */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* If these are not set the texture image unit will return + * * (R, G, B, A) = black on glTexImage2D for non-POT width/height + * * frames. For a deeper explanation take a look at the OpenGL ES + * * documentation for glTexParameter */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (got_gl_error ("glTexParameteri")) + goto mem_error; + + if (i == 0) + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, + GST_VIDEO_INFO_COMP_WIDTH (&info, i), + GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE, + GL_UNSIGNED_BYTE, NULL); + else + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, + GST_VIDEO_INFO_COMP_WIDTH (&info, i), + GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, NULL); + + if (got_gl_error ("glTexImage2D")) + goto mem_error; + + image = + gst_egl_display_image_create (display, + eglcontext, EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer) (uintptr_t) data->texture, NULL); + if (got_egl_error ("eglCreateImageKHR")) + goto mem_error; + + mem[i] = + gst_egl_image_allocator_wrap (allocator, display, + image, + (i == + 0 ? GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE : + GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE_ALPHA), + flags, size[i], data, + (GDestroyNotify) gst_egl_gles_image_data_free); + } + + n_mem = 2; + } + break; + } + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_Y41B:{ + EGLImageKHR image; + gsize size[3]; + + mem[0] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, + 0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]); + mem[1] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, + 1), GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]); + mem[2] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info, + 2), GST_VIDEO_INFO_COMP_HEIGHT (&info, 2), &size[2]); + + if (mem[0] && mem[1] && mem[2]) { + stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info); + offset[1] = size[0]; + stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info); + offset[2] = size[1]; + stride[2] = size[2] / GST_VIDEO_INFO_HEIGHT (&info); + n_mem = 3; + GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE); + GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE); + GST_MINI_OBJECT_FLAG_SET (mem[2], GST_MEMORY_FLAG_NO_SHARE); + } else { + if (mem[0]) + gst_memory_unref (mem[0]); + if (mem[1]) + gst_memory_unref (mem[1]); + if (mem[2]) + gst_memory_unref (mem[2]); + mem[0] = mem[1] = mem[2] = NULL; + + stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0)); + stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1)); + stride[2] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 2)); + size[0] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0); + size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1); + size[2] = stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 2); + offset[0] = 0; + offset[1] = size[0]; + offset[2] = offset[1] + size[1]; + + for (i = 0; i < 3; i++) { + data = g_slice_new0 (GstEGLGLESImageData); + data->display = gst_egl_display_get (display); + data->eglcontext = eglcontext; + + glGenTextures (1, &data->texture); + if (got_gl_error ("glGenTextures")) + goto mem_error; + + glBindTexture (GL_TEXTURE_2D, data->texture); + if (got_gl_error ("glBindTexture")) + goto mem_error; + + /* Set 2D resizing params */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* If these are not set the texture image unit will return + * * (R, G, B, A) = black on glTexImage2D for non-POT width/height + * * frames. For a deeper explanation take a look at the OpenGL ES + * * documentation for glTexParameter */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (got_gl_error ("glTexParameteri")) + goto mem_error; + + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, + GST_VIDEO_INFO_COMP_WIDTH (&info, i), + GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE, + GL_UNSIGNED_BYTE, NULL); + + if (got_gl_error ("glTexImage2D")) + goto mem_error; + + image = + gst_egl_display_image_create (display, + eglcontext, EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer) (uintptr_t) data->texture, NULL); + if (got_egl_error ("eglCreateImageKHR")) + goto mem_error; + + mem[i] = + gst_egl_image_allocator_wrap (allocator, display, + image, GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE, + flags, size[i], data, + (GDestroyNotify) gst_egl_gles_image_data_free); + } + + n_mem = 3; + } + break; + } + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_AYUV:{ + gsize size; + EGLImageKHR image; + + mem[0] = + gst_egl_image_allocator_alloc (allocator, display, + GST_VIDEO_GL_TEXTURE_TYPE_RGBA, GST_VIDEO_INFO_WIDTH (&info), + GST_VIDEO_INFO_HEIGHT (&info), &size); + if (mem[0]) { + stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info); + n_mem = 1; + GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE); + } else { + data = g_slice_new0 (GstEGLGLESImageData); + data->display = gst_egl_display_get (display); + data->eglcontext = eglcontext; + + stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 4); + size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info); + + glGenTextures (1, &data->texture); + if (got_gl_error ("glGenTextures")) + goto mem_error; + + glBindTexture (GL_TEXTURE_2D, data->texture); + if (got_gl_error ("glBindTexture")) + goto mem_error; + + /* Set 2D resizing params */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* If these are not set the texture image unit will return + * * (R, G, B, A) = black on glTexImage2D for non-POT width/height + * * frames. For a deeper explanation take a look at the OpenGL ES + * * documentation for glTexParameter */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (got_gl_error ("glTexParameteri")) + goto mem_error; + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, + GST_VIDEO_INFO_WIDTH (&info), + GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (got_gl_error ("glTexImage2D")) + goto mem_error; + + image = + gst_egl_display_image_create (display, + eglcontext, EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer) (uintptr_t) data->texture, NULL); + if (got_egl_error ("eglCreateImageKHR")) + goto mem_error; + + mem[0] = + gst_egl_image_allocator_wrap (allocator, display, + image, GST_VIDEO_GL_TEXTURE_TYPE_RGBA, + flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free); + + n_mem = 1; + } + break; + } + default: + g_assert_not_reached (); + break; + } + + buffer = gst_buffer_new (); + gst_buffer_add_video_meta_full (buffer, 0, format, width, height, + GST_VIDEO_INFO_N_PLANES (&info), offset, stride); + + for (i = 0; i < n_mem; i++) + gst_buffer_append_memory (buffer, mem[i]); + + return buffer; + +mem_error: + { + GST_ERROR_OBJECT (GST_CAT_DEFAULT, "Failed to create EGLImage"); + + if (data) + gst_egl_gles_image_data_free (data); + + if (mem[0]) + gst_memory_unref (mem[0]); + if (mem[1]) + gst_memory_unref (mem[1]); + if (mem[2]) + gst_memory_unref (mem[2]); + + return NULL; + } +} + +void +gst_egl_adaptation_destroy_native_window (GstEglAdaptationContext * ctx, + gpointer * own_window_data, gchar* winsys) +{ +#ifdef USE_EGL_WAYLAND + if (g_strcmp0(winsys, "wayland") == 0) { + platform_destroy_native_window_wayland (gst_egl_display_get + (ctx->display), ctx->used_window, own_window_data); + platform_destroy_display_wayland(); + } +#endif + +#ifdef USE_EGL_X11 + if (g_strcmp0(winsys, "x11") == 0) { + platform_destroy_native_window_x11 (gst_egl_display_get + (ctx->display), ctx->used_window, own_window_data); + } +#endif +} + +gboolean +gst_egl_adaptation_create_native_window (GstEglAdaptationContext * ctx, + gint width, gint height, gpointer * own_window_data, gchar* winsys) +{ + GstEglGlesSink *sink = (GstEglGlesSink *) ctx->element; + EGLNativeWindowType window = NULL; + +#ifdef USE_EGL_WAYLAND + if (g_strcmp0(winsys, "wayland") == 0) { + window = + platform_create_native_window_wayland (sink->window_x, + sink->window_y, + width, + height, + own_window_data); + } +#endif + +#ifdef USE_EGL_X11 + if (g_strcmp0(winsys, "x11") == 0) { + window = + platform_create_native_window_x11 (sink->window_x, + sink->window_y, + width, + height, + own_window_data); + } +#endif + + if (window) + gst_egl_adaptation_set_window (ctx, (uintptr_t) window); + GST_DEBUG_OBJECT (ctx->element, "Using window handle %p", (gpointer) window); + return window != 0; +} + +void +gst_egl_adaptation_set_window (GstEglAdaptationContext * ctx, guintptr window) +{ + ctx->window = (EGLNativeWindowType)(uintptr_t) window; +} + +void +gst_egl_adaptation_init (GstEglAdaptationContext * ctx) +{ + ctx->eglglesctx = g_new0 (GstEglGlesRenderContext, 1); +} + +void +gst_egl_adaptation_deinit (GstEglAdaptationContext * ctx) +{ + g_free (ctx->eglglesctx); +} + +void +gst_egl_adaptation_destroy_surface (GstEglAdaptationContext * ctx) +{ + if (ctx->eglglesctx->surface) { + eglDestroySurface (gst_egl_display_get (ctx->display), + ctx->eglglesctx->surface); + ctx->eglglesctx->surface = NULL; + ctx->have_surface = FALSE; + } +} + +void +gst_egl_adaptation_destroy_context (GstEglAdaptationContext * ctx) +{ + if (ctx->eglglesctx->eglcontext) { + eglDestroyContext (gst_egl_display_get (ctx->display), + ctx->eglglesctx->eglcontext); + ctx->eglglesctx->eglcontext = NULL; + } +} + +void +gst_egl_adaptation_release_thread () +{ + eglReleaseThread (); +} diff --git a/gstegl_src/gst-egl/ext/eglgles/gsteglglessink.c b/gstegl_src/gst-egl/ext/eglgles/gsteglglessink.c new file mode 100644 index 0000000..87ee57a --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/gsteglglessink.c @@ -0,0 +1,3400 @@ +/* + * GStreamer EGL/GLES Sink + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * @author: Sebastian Dröge + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * SECTION:element-eglglessink + * + * EglGlesSink renders video frames on a EGL surface it sets up + * from a window it either creates (on X11) or gets a handle to + * through it's xOverlay interface. All the display/surface logic + * in this sink uses EGL to interact with the native window system. + * The rendering logic, in turn, uses OpenGL ES v2. + * + * This sink has been tested to work on X11/Mesa and on Android + * (From Gingerbread on to Jelly Bean) and while it's currently + * using an slow copy-over rendering path it has proven to be fast + * enough on the devices we have tried it on. + * + * + * Supported EGL/OpenGL ES versions + * + * This Sink uses EGLv1 and GLESv2 + * + * + * + * + * Example launch line + * |[ + * gst-launch -v -m videotestsrc ! eglglessink + * ]| + * + * + * + * Example launch line with internal window creation disabled + * + * By setting the can_create_window property to FALSE you can force the + * sink to wait for a window handle through it's xOverlay interface even + * if internal window creation is supported by the platform. Window creation + * is only supported in X11 right now but it should be trivial to add support + * for different platforms. + * + * |[ + * gst-launch -v -m videotestsrc ! eglglessink can_create_window=FALSE + * ]| + * + * + * + * Scaling + * + * The sink will try it's best to consider the incoming frame's and display's + * pixel aspect ratio and fill the corresponding surface without altering the + * decoded frame's geometry when scaling. You can disable this logic by setting + * the force_aspect_ratio property to FALSE, in which case the sink will just + * fill the entire surface it has access to regardles of the PAR/DAR relationship. + * + * + * Querying the display aspect ratio is only supported with EGL versions >= 1.2. + * The sink will just assume the DAR to be 1/1 if it can't get access to this + * information. + * + * + * Here is an example launch line with the PAR/DAR aware scaling disabled: + * + * |[ + * gst-launch -v -m videotestsrc ! eglglessink force_aspect_ratio=FALSE + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define EGL_EGLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES + +#include "nvbufsurface.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gstegladaptation.h" +#include "video_platform_wrapper.h" + +#ifdef USE_EGL_RPI +#include +#endif + +#include "gsteglglessink.h" +#include "gstegljitter.h" + +#ifdef IS_DESKTOP +#define DEFAULT_NVBUF_API_VERSION_NEW TRUE +#else +#define DEFAULT_NVBUF_API_VERSION_NEW FALSE +#endif + + +GST_DEBUG_CATEGORY_STATIC (gst_eglglessink_debug); +#define GST_CAT_DEFAULT gst_eglglessink_debug +#ifdef IS_DESKTOP +#define DEFAULT_GPU_ID 0 +#endif + +GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE); + +/* Input capabilities. */ +static GstStaticPadTemplate gst_eglglessink_sink_template_factory = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ( +#ifndef HAVE_IOS + GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, + "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, " + "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, " + "RGB, BGR, RGB16 }") ";" +#endif + GST_VIDEO_CAPS_MAKE_WITH_FEATURES + (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, + "{ " "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, " + "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, " + "RGB, BGR, RGB16 }") ";" + GST_VIDEO_CAPS_MAKE ("{ " + "RGBA, BGRA, ARGB, ABGR, " "RGBx, BGRx, xRGB, xBGR, " + "AYUV, Y444, I420, YV12, " "NV12, NV21, Y42B, Y41B, " + "RGB, BGR, RGB16 }") + ";" + GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("memory:NVMM", + "{ " "BGRx, RGBA, I420, NV12, BGR, RGB }") + )); + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_CREATE_WINDOW, + PROP_FORCE_ASPECT_RATIO, + PROP_DISPLAY, + PROP_WINDOW_X, + PROP_WINDOW_Y, + PROP_WINDOW_WIDTH, + PROP_WINDOW_HEIGHT, +#ifdef IS_DESKTOP + PROP_GPU_DEVICE_ID, +#endif + PROP_ROWS, + PROP_COLUMNS, + PROP_PROFILE, + PROP_WINSYS, + PROP_NVBUF_API_VERSION +}; + +static void gst_eglglessink_finalize (GObject * object); +static void gst_eglglessink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_eglglessink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_eglglessink_change_state (GstElement * element, + GstStateChange transition); +static void gst_eglglessink_set_context (GstElement * element, + GstContext * context); +static GstFlowReturn gst_eglglessink_prepare (GstBaseSink * bsink, + GstBuffer * buf); +static GstFlowReturn gst_eglglessink_show_frame (GstVideoSink * vsink, + GstBuffer * buf); +static gboolean gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps); +static GstCaps *gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter); +static gboolean gst_eglglessink_propose_allocation (GstBaseSink * bsink, + GstQuery * query); +static gboolean gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query); + +/* VideoOverlay interface cruft */ +static void gst_eglglessink_videooverlay_init (GstVideoOverlayInterface * + iface); + +/* Actual VideoOverlay interface funcs */ +static void gst_eglglessink_expose (GstVideoOverlay * overlay); +static void gst_eglglessink_set_window_handle (GstVideoOverlay * overlay, + guintptr id); +static void gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay, + gint x, gint y, gint width, gint height); + +/* Utility */ +static gboolean gst_eglglessink_create_window (GstEglGlesSink * + eglglessink, gint width, gint height); +static gboolean gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink); +static gboolean +gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps); +static gboolean +gst_eglglessink_cuda_init(GstEglGlesSink * eglglessink); +static void +gst_eglglessink_cuda_cleanup(GstEglGlesSink * eglglessink); +static gboolean +gst_eglglessink_cuda_buffer_copy(GstEglGlesSink * eglglessink, GstBuffer * buf); +static GstFlowReturn gst_eglglessink_upload (GstEglGlesSink * sink, + GstBuffer * buf); +static GstFlowReturn gst_eglglessink_render (GstEglGlesSink * sink); +static GstFlowReturn gst_eglglessink_queue_object (GstEglGlesSink * sink, + GstMiniObject * obj); +static inline gboolean egl_init (GstEglGlesSink * eglglessink); +static const gchar *supportedPlatforms[] = { +#ifdef USE_EGL_X11 + "x11", +#endif +#ifdef USE_EGL_WAYLAND + "wayland" +#endif +}; +gboolean isPlatformSupported (gchar* winsys); + +#ifndef HAVE_IOS +typedef GstBuffer *(*GstEGLImageBufferPoolSendBlockingAllocate) (GstBufferPool * + pool, gpointer data); + +/* EGLImage memory, buffer pool, etc */ +typedef struct +{ + GstVideoBufferPool parent; + + GstAllocator *allocator; + GstAllocationParams params; + GstVideoInfo info; + gboolean add_metavideo; + gboolean want_eglimage; + GstBuffer *last_buffer; + GstEGLImageBufferPoolSendBlockingAllocate send_blocking_allocate_func; + gpointer send_blocking_allocate_data; + GDestroyNotify send_blocking_allocate_destroy; +} GstEGLImageBufferPool; + +typedef GstVideoBufferPoolClass GstEGLImageBufferPoolClass; + +#define GST_EGL_IMAGE_BUFFER_POOL(p) ((GstEGLImageBufferPool*)(p)) + +GType gst_egl_image_buffer_pool_get_type (void); + +G_DEFINE_TYPE (GstEGLImageBufferPool, gst_egl_image_buffer_pool, + GST_TYPE_VIDEO_BUFFER_POOL); + +static GstBufferPool + * gst_egl_image_buffer_pool_new (GstEGLImageBufferPoolSendBlockingAllocate + blocking_allocate_func, gpointer blocking_allocate_data, + GDestroyNotify destroy_func); + +static void +gst_egl_image_buffer_pool_get_video_infos (GstEGLImageBufferPool * pool, + GstVideoFormat * format, gint * width, gint * height) +{ + g_return_if_fail (pool != NULL); + + if (format) + *format = pool->info.finfo->format; + + if (width) + *width = pool->info.width; + + if (height) + *height = pool->info.height; +} + +static void +gst_egl_image_buffer_pool_replace_last_buffer (GstEGLImageBufferPool * pool, + GstBuffer * buffer) +{ + g_return_if_fail (pool != NULL); + + gst_buffer_replace (&pool->last_buffer, buffer); +} + +static GstBuffer * +gst_eglglessink_egl_image_buffer_pool_send_blocking (GstBufferPool * bpool, + gpointer data) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstQuery *query = NULL; + GstStructure *s = NULL; + const GValue *v = NULL; + GstBuffer *buffer = NULL; + GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; + gint width = 0; + gint height = 0; + + GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool); + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (data); + + gst_egl_image_buffer_pool_get_video_infos (pool, &format, &width, &height); + + s = gst_structure_new ("eglglessink-allocate-eglimage", + "format", GST_TYPE_VIDEO_FORMAT, format, + "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); + query = gst_query_new_custom (GST_QUERY_CUSTOM, s); + + ret = + gst_eglglessink_queue_object (eglglessink, GST_MINI_OBJECT_CAST (query)); + + if (ret == GST_FLOW_OK && gst_structure_has_field (s, "buffer")) { + v = gst_structure_get_value (s, "buffer"); + buffer = GST_BUFFER_CAST (g_value_get_pointer (v)); + } + + gst_query_unref (query); + + return buffer; +} + +static void +gst_eglglessink_egl_image_buffer_pool_on_destroy (gpointer data) +{ + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (data); + gst_object_unref (eglglessink); +} + +static const gchar ** +gst_egl_image_buffer_pool_get_options (GstBufferPool * bpool) +{ + static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL + }; + + return options; +} + +static gboolean +gst_egl_image_buffer_pool_set_config (GstBufferPool * bpool, + GstStructure * config) +{ + GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool); + GstCaps *caps; + GstVideoInfo info; + + if (pool->allocator) + gst_object_unref (pool->allocator); + pool->allocator = NULL; + + if (!GST_BUFFER_POOL_CLASS + (gst_egl_image_buffer_pool_parent_class)->set_config (bpool, config)) + return FALSE; + + if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL) + || !caps) + return FALSE; + + if (!gst_video_info_from_caps (&info, caps)) + return FALSE; + + if (!gst_buffer_pool_config_get_allocator (config, &pool->allocator, + &pool->params)) + return FALSE; + + pool->add_metavideo = + gst_buffer_pool_config_has_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + + pool->want_eglimage = (pool->allocator + && g_strcmp0 (pool->allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0); + + pool->info = info; + + return TRUE; +} + +static GstFlowReturn +gst_egl_image_buffer_pool_alloc_buffer (GstBufferPool * bpool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (bpool); + + *buffer = NULL; + + if (!pool->add_metavideo || !pool->want_eglimage) + return + GST_BUFFER_POOL_CLASS + (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool, + buffer, params); + + if (!pool->allocator) + return GST_FLOW_NOT_NEGOTIATED; + + switch (pool->info.finfo->format) { + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_RGB16: + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_NV21: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR: + case GST_VIDEO_FORMAT_AYUV: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_Y41B:{ + + if (pool->send_blocking_allocate_func) + *buffer = pool->send_blocking_allocate_func (bpool, + pool->send_blocking_allocate_data); + + if (!*buffer) { + GST_WARNING ("Fallback memory allocation"); + return + GST_BUFFER_POOL_CLASS + (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool, + buffer, params); + } + + return GST_FLOW_OK; + break; + } + default: + return + GST_BUFFER_POOL_CLASS + (gst_egl_image_buffer_pool_parent_class)->alloc_buffer (bpool, + buffer, params); + break; + } + + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_egl_image_buffer_pool_acquire_buffer (GstBufferPool * bpool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstFlowReturn ret; + GstEGLImageBufferPool *pool; + + ret = + GST_BUFFER_POOL_CLASS + (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool, + buffer, params); + if (ret != GST_FLOW_OK || !*buffer) + return ret; + + pool = GST_EGL_IMAGE_BUFFER_POOL (bpool); + + /* XXX: Don't return the memory we just rendered, glEGLImageTargetTexture2DOES() + * keeps the EGLImage unmappable until the next one is uploaded + */ + if (*buffer && *buffer == pool->last_buffer) { + GstBuffer *oldbuf = *buffer; + + ret = + GST_BUFFER_POOL_CLASS + (gst_egl_image_buffer_pool_parent_class)->acquire_buffer (bpool, + buffer, params); + gst_object_replace ((GstObject **) & oldbuf->pool, (GstObject *) pool); + gst_buffer_unref (oldbuf); + } + + return ret; +} + +static void +gst_egl_image_buffer_pool_finalize (GObject * object) +{ + GstEGLImageBufferPool *pool = GST_EGL_IMAGE_BUFFER_POOL (object); + + if (pool->allocator) + gst_object_unref (pool->allocator); + pool->allocator = NULL; + + gst_egl_image_buffer_pool_replace_last_buffer (pool, NULL); + + if (pool->send_blocking_allocate_destroy) + pool->send_blocking_allocate_destroy (pool->send_blocking_allocate_data); + pool->send_blocking_allocate_destroy = NULL; + pool->send_blocking_allocate_data = NULL; + + G_OBJECT_CLASS (gst_egl_image_buffer_pool_parent_class)->finalize (object); +} + +static void +gst_egl_image_buffer_pool_class_init (GstEGLImageBufferPoolClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; + + gobject_class->finalize = gst_egl_image_buffer_pool_finalize; + gstbufferpool_class->get_options = gst_egl_image_buffer_pool_get_options; + gstbufferpool_class->set_config = gst_egl_image_buffer_pool_set_config; + gstbufferpool_class->alloc_buffer = gst_egl_image_buffer_pool_alloc_buffer; + gstbufferpool_class->acquire_buffer = + gst_egl_image_buffer_pool_acquire_buffer; +} + +static void +gst_egl_image_buffer_pool_init (GstEGLImageBufferPool * pool) +{ +} +#endif + +#define parent_class gst_eglglessink_parent_class +G_DEFINE_TYPE_WITH_CODE (GstEglGlesSink, gst_eglglessink, GST_TYPE_VIDEO_SINK, + G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, + gst_eglglessink_videooverlay_init)); + +gboolean isPlatformSupported (gchar* winsys) +{ + uint i; + for (i = 0; i < (sizeof(supportedPlatforms)/sizeof(gchar*)); i++) { + if (g_strcmp0(winsys, supportedPlatforms[i]) == 0) { + return TRUE; + } + } + + return FALSE; +} + +static inline gboolean +egl_init (GstEglGlesSink * eglglessink) +{ + GstCaps *caps; + + if (!isPlatformSupported (eglglessink->winsys)) { + g_print("Winsys: %s is not supported \n", eglglessink->winsys); + GST_ERROR_OBJECT (eglglessink, "Unsupported Window System \n"); + goto HANDLE_ERROR; + } + g_print("\nUsing winsys: %s \n", eglglessink->winsys); + + if (!gst_egl_adaptation_init_display (eglglessink->egl_context, eglglessink->winsys)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL display"); + goto HANDLE_ERROR; + } + + caps = + gst_egl_adaptation_fill_supported_fbuffer_configs + (eglglessink->egl_context); + if (!caps) { + GST_ERROR_OBJECT (eglglessink, "Display support NONE of our configs"); + goto HANDLE_ERROR; + } else { + GST_OBJECT_LOCK (eglglessink); + gst_caps_replace (&eglglessink->sinkcaps, caps); + GST_OBJECT_UNLOCK (eglglessink); + gst_caps_unref (caps); + } + + eglglessink->egl_started = TRUE; + + eglglessink->glEGLImageTargetTexture2DOES = + (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) + eglGetProcAddress ("glEGLImageTargetTexture2DOES"); + + return TRUE; + +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Failed to perform EGL init"); + return FALSE; +} + +static gpointer +render_thread_func (GstEglGlesSink * eglglessink) +{ + GstMessage *message; + GValue val = { 0 }; + GstDataQueueItem *item = NULL; + GstFlowReturn last_flow = GST_FLOW_OK; + gboolean is_flushing = FALSE; + + cudaError_t CUerr = cudaSuccess; + GST_LOG_OBJECT (eglglessink, "SETTING CUDA DEVICE = %d in eglglessink func=%s\n", eglglessink->gpu_id, __func__); + CUerr = cudaSetDevice(eglglessink->gpu_id); + if (CUerr != cudaSuccess) { + GST_LOG_OBJECT (eglglessink,"\n *** Unable to set device in %s Line %d\n", __func__, __LINE__); + return NULL; + } + + g_value_init (&val, GST_TYPE_G_THREAD); + g_value_set_boxed (&val, g_thread_self ()); + message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink), + GST_STREAM_STATUS_TYPE_ENTER, GST_ELEMENT_CAST (eglglessink)); + gst_message_set_stream_status_object (message, &val); + GST_DEBUG_OBJECT (eglglessink, "posting ENTER stream status"); + gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message); + g_value_unset (&val); + + gst_egl_adaptation_bind_API (eglglessink->egl_context); + + while (gst_data_queue_pop (eglglessink->queue, &item)) { + GstMiniObject *object = item->object; + + GST_DEBUG_OBJECT (eglglessink, "Handling object %" GST_PTR_FORMAT, object); + + if (GST_IS_CAPS (object)) { + GstCaps *caps = GST_CAPS_CAST (object); + + if (caps != eglglessink->configured_caps) { + if (!gst_eglglessink_configure_caps (eglglessink, caps)) { + last_flow = GST_FLOW_NOT_NEGOTIATED; + } + } +#ifndef HAVE_IOS + } else if (GST_IS_QUERY (object)) { + GstQuery *query = GST_QUERY_CAST (object); + GstStructure *s = (GstStructure *) gst_query_get_structure (query); + + if (gst_structure_has_name (s, "eglglessink-allocate-eglimage")) { + GstBuffer *buffer; + GstVideoFormat format; + gint width, height; + GValue v = { 0, }; + + if (!gst_structure_get_enum (s, "format", GST_TYPE_VIDEO_FORMAT, + (gint *) & format) + || !gst_structure_get_int (s, "width", &width) + || !gst_structure_get_int (s, "height", &height)) { + g_assert_not_reached (); + } + + buffer = + gst_egl_image_allocator_alloc_eglimage (GST_EGL_IMAGE_BUFFER_POOL + (eglglessink->pool)->allocator, eglglessink->egl_context->display, + gst_egl_adaptation_context_get_egl_context + (eglglessink->egl_context), format, width, height); + g_value_init (&v, G_TYPE_POINTER); + g_value_set_pointer (&v, buffer); + gst_structure_set_value (s, "buffer", &v); + g_value_unset (&v); + } else if (gst_structure_has_name (s, "eglglessink-flush")) { + eglglessink->last_flow = GST_FLOW_FLUSHING; + is_flushing = TRUE; + } else { + g_assert_not_reached (); + } + last_flow = GST_FLOW_OK; +#endif + } else if (GST_IS_BUFFER (object)) { + GstBuffer *buf = GST_BUFFER_CAST (item->object); + + if (eglglessink->configured_caps) { + last_flow = gst_eglglessink_upload (eglglessink, buf); + } else { + last_flow = GST_FLOW_OK; + GST_DEBUG_OBJECT (eglglessink, + "No caps configured yet, not drawing anything"); + } + } else if (!object) { + if (eglglessink->configured_caps) { + last_flow = gst_eglglessink_render (eglglessink); + + if (eglglessink->last_uploaded_buffer && eglglessink->pool) { + gst_egl_image_buffer_pool_replace_last_buffer (GST_EGL_IMAGE_BUFFER_POOL + (eglglessink->pool), eglglessink->last_uploaded_buffer); + eglglessink->last_uploaded_buffer = NULL; + } + +/* + * gst_eglglessink_render returns error if window has been changed. + * So wait for 1 second to check if window is changing. + */ + if (last_flow != GST_FLOW_OK) { + if (eglglessink->egl_context->used_window == + eglglessink->egl_context->window) { + g_mutex_lock (&eglglessink->render_lock); + g_cond_wait_until (&eglglessink->render_cond, + &eglglessink->render_lock, + g_get_monotonic_time () + G_TIME_SPAN_SECOND); + g_mutex_unlock (&eglglessink->render_lock); + } + + if (eglglessink->egl_context->used_window != + eglglessink->egl_context->window) { + if (gst_egl_adaptation_reset_window (eglglessink->egl_context, + eglglessink->configured_info.finfo->format)) + last_flow = GST_FLOW_OK; + } + } + + } else { + last_flow = GST_FLOW_OK; + GST_DEBUG_OBJECT (eglglessink, + "No caps configured yet, not drawing anything"); + } + } else { + g_assert_not_reached (); + } + + item->destroy (item); + g_mutex_lock (&eglglessink->render_lock); + eglglessink->last_flow = last_flow; + eglglessink->dequeued_object = object; + g_cond_broadcast (&eglglessink->render_cond); + g_mutex_unlock (&eglglessink->render_lock); + + if (last_flow != GST_FLOW_OK) + break; + + if (is_flushing && eglglessink->is_reconfiguring) { + g_mutex_lock (&eglglessink->render_lock); + g_cond_wait (&eglglessink->render_exit_cond, &eglglessink->render_lock); + g_mutex_unlock (&eglglessink->render_lock); + } + is_flushing = FALSE; + + GST_DEBUG_OBJECT (eglglessink, "Successfully handled object"); + } + + if (eglglessink->last_uploaded_buffer && eglglessink->pool) { + gst_egl_image_buffer_pool_replace_last_buffer (GST_EGL_IMAGE_BUFFER_POOL + (eglglessink->pool), eglglessink->last_uploaded_buffer); + eglglessink->last_uploaded_buffer = NULL; + } + + if (last_flow == GST_FLOW_OK) { + g_mutex_lock (&eglglessink->render_lock); + eglglessink->last_flow = GST_FLOW_FLUSHING; + eglglessink->dequeued_object = NULL; + g_cond_broadcast (&eglglessink->render_cond); + g_mutex_unlock (&eglglessink->render_lock); + } + + GST_DEBUG_OBJECT (eglglessink, "Shutting down thread"); + + /* EGL/GLES cleanup */ + g_mutex_lock (&eglglessink->render_lock); + if (!eglglessink->is_closing) { + g_cond_wait (&eglglessink->render_exit_cond, &eglglessink->render_lock); + } + g_mutex_unlock (&eglglessink->render_lock); + + if (eglglessink->using_cuda) { + gst_eglglessink_cuda_cleanup(eglglessink); + } + + gst_egl_adaptation_cleanup (eglglessink->egl_context); + + if (eglglessink->configured_caps) { + gst_caps_unref (eglglessink->configured_caps); + eglglessink->configured_caps = NULL; + } + + gst_egl_adaptation_release_thread (); + + g_value_init (&val, GST_TYPE_G_THREAD); + g_value_set_boxed (&val, g_thread_self ()); + message = gst_message_new_stream_status (GST_OBJECT_CAST (eglglessink), + GST_STREAM_STATUS_TYPE_LEAVE, GST_ELEMENT_CAST (eglglessink)); + gst_message_set_stream_status_object (message, &val); + GST_DEBUG_OBJECT (eglglessink, "posting LEAVE stream status"); + gst_element_post_message (GST_ELEMENT_CAST (eglglessink), message); + g_value_unset (&val); + + return NULL; +} + +static gboolean +gst_eglglessink_start (GstEglGlesSink * eglglessink) +{ + GError *error = NULL; + cudaError_t CUerr = cudaSuccess; + + GST_DEBUG_OBJECT (eglglessink, "Starting"); + + if (eglglessink->thread) { + g_cond_broadcast (&eglglessink->render_exit_cond); + g_thread_join (eglglessink->thread); + eglglessink->thread = NULL; + } + + if (!eglglessink->egl_started) { + GST_ERROR_OBJECT (eglglessink, "EGL uninitialized. Bailing out"); + goto HANDLE_ERROR; + } + + /* Ask for a window to render to */ + if (!eglglessink->have_window) + gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (eglglessink)); + + if (!eglglessink->have_window && !eglglessink->create_window) { + GST_ERROR_OBJECT (eglglessink, "Window handle unavailable and we " + "were instructed not to create an internal one. Bailing out."); + goto HANDLE_ERROR; + } + + eglglessink->last_flow = GST_FLOW_OK; + eglglessink->display_region.w = 0; + eglglessink->display_region.h = 0; + eglglessink->is_reconfiguring = FALSE; + eglglessink->is_closing = FALSE; + + if (!g_strcmp0 (g_getenv("DS_NEW_BUFAPI"), "1")){ + eglglessink->nvbuf_api_version_new = TRUE; + } + + gst_data_queue_set_flushing (eglglessink->queue, FALSE); + + GST_LOG_OBJECT (eglglessink, "SETTING CUDA DEVICE = %d in eglglessink func=%s\n", eglglessink->gpu_id, __func__); + CUerr = cudaSetDevice(eglglessink->gpu_id); + if (CUerr != cudaSuccess) { + GST_LOG_OBJECT (eglglessink,"\n *** Unable to set device in %s Line %d\n", __func__, __LINE__); + goto HANDLE_ERROR; + } + +#if !GLIB_CHECK_VERSION (2, 31, 0) + eglglessink->thread = + g_thread_create ((GThreadFunc) render_thread_func, eglglessink, TRUE, + &error); +#else + eglglessink->thread = g_thread_try_new ("eglglessink-render", + (GThreadFunc) render_thread_func, eglglessink, &error); +#endif + + if (!eglglessink->thread || error != NULL) + goto HANDLE_ERROR; + + GST_DEBUG_OBJECT (eglglessink, "Started"); + + return TRUE; + +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Couldn't start"); + g_clear_error (&error); + return FALSE; +} + +static gboolean +gst_eglglessink_stop (GstEglGlesSink * eglglessink) +{ + GST_DEBUG_OBJECT (eglglessink, "Stopping"); + + gst_data_queue_set_flushing (eglglessink->queue, TRUE); + g_mutex_lock (&eglglessink->render_lock); + g_cond_broadcast (&eglglessink->render_cond); + g_mutex_unlock (&eglglessink->render_lock); + + eglglessink->last_flow = GST_FLOW_FLUSHING; + +#ifndef HAVE_IOS + if (eglglessink->pool) + gst_egl_image_buffer_pool_replace_last_buffer (GST_EGL_IMAGE_BUFFER_POOL + (eglglessink->pool), NULL); +#endif + + if (eglglessink->current_caps) { + gst_caps_unref (eglglessink->current_caps); + eglglessink->current_caps = NULL; + } + + GST_DEBUG_OBJECT (eglglessink, "Stopped"); + + return TRUE; +} + +static void +gst_eglglessink_videooverlay_init (GstVideoOverlayInterface * iface) +{ + iface->set_window_handle = gst_eglglessink_set_window_handle; + iface->expose = gst_eglglessink_expose; + iface->set_render_rectangle = gst_eglglessink_set_render_rectangle; +} + +#ifdef USE_EGL_X11 +static gpointer +gst_eglglessink_event_thread (GstEglGlesSink * eglglessink) +{ + XEvent e; + X11WindowData *data = (eglglessink->own_window_data); + Atom wm_delete; + g_mutex_lock (&eglglessink->window_lock); + while (eglglessink->have_window) { + while (XPending (data->display)) { + XNextEvent (data->display, &e); + switch (e.type) { + case ClientMessage: + wm_delete = XInternAtom (data->display, "WM_DELETE_WINDOW", 1); + if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) { + GST_ELEMENT_ERROR (&eglglessink->videosink, RESOURCE, NOT_FOUND, + ("Output window was closed"), (NULL)); + break; + } + } + } + g_mutex_unlock (&eglglessink->window_lock); + g_usleep (G_USEC_PER_SEC / 20); + g_mutex_lock (&eglglessink->window_lock); + } + g_mutex_unlock (&eglglessink->window_lock); + return NULL; +} +#endif + +static gboolean +gst_eglglessink_create_window (GstEglGlesSink * eglglessink, gint width, + gint height) +{ + gboolean window_created = FALSE; + + if (!eglglessink->create_window) { + GST_ERROR_OBJECT (eglglessink, "This sink can't create a window by itself"); + return FALSE; + } else + GST_INFO_OBJECT (eglglessink, "Attempting internal window creation"); + + window_created = + gst_egl_adaptation_create_native_window (eglglessink->egl_context, width, + height, &eglglessink->own_window_data, eglglessink->winsys); + if (!window_created) { + GST_ERROR_OBJECT (eglglessink, "Could not create window"); + } + +#ifdef USE_EGL_X11 + if (g_strcmp0(eglglessink->winsys, "x11") == 0) { + eglglessink->event_thread = g_thread_try_new ("eglglessink-events", + (GThreadFunc) gst_eglglessink_event_thread, eglglessink, NULL); + } +#endif + + return window_created; +} + +static void +gst_eglglessink_expose (GstVideoOverlay * overlay) +{ + GstEglGlesSink *eglglessink; + GstFlowReturn ret; + + eglglessink = GST_EGLGLESSINK (overlay); + GST_DEBUG_OBJECT (eglglessink, "Expose catched, redisplay"); + + /* Render from last seen buffer */ + ret = gst_eglglessink_queue_object (eglglessink, NULL); + if (ret == GST_FLOW_ERROR) + GST_ERROR_OBJECT (eglglessink, "Redisplay failed"); +} + +static gboolean +gst_eglglessink_setup_vbo (GstEglGlesSink * eglglessink) +{ + gdouble render_width, render_height; + gdouble texture_width, texture_height; + gdouble x1, x2, y1, y2; + gdouble tx1, tx2, ty1, ty2; + + GST_INFO_OBJECT (eglglessink, "VBO setup. have_vbo:%d", + eglglessink->egl_context->have_vbo); + + if (eglglessink->egl_context->have_vbo) { + glDeleteBuffers (1, &eglglessink->egl_context->position_buffer); + glDeleteBuffers (1, &eglglessink->egl_context->index_buffer); + eglglessink->egl_context->have_vbo = FALSE; + } + + render_width = eglglessink->render_region.w; + render_height = eglglessink->render_region.h; + + texture_width = eglglessink->configured_info.width; + texture_height = eglglessink->configured_info.height; + + GST_DEBUG_OBJECT (eglglessink, "Performing VBO setup"); + + x1 = (eglglessink->display_region.x / render_width) * 2.0 - 1; + y1 = (eglglessink->display_region.y / render_height) * 2.0 - 1; + x2 = ((eglglessink->display_region.x + + eglglessink->display_region.w) / render_width) * 2.0 - 1; + y2 = ((eglglessink->display_region.y + + eglglessink->display_region.h) / render_height) * 2.0 - 1; + + tx1 = (eglglessink->crop.x / texture_width); + tx2 = ((eglglessink->crop.x + eglglessink->crop.w) / texture_width); + ty1 = (eglglessink->crop.y / texture_height); + ty2 = ((eglglessink->crop.y + eglglessink->crop.h) / texture_height); + + /* X-normal, Y-normal orientation */ + eglglessink->egl_context->position_array[0].x = x2; + eglglessink->egl_context->position_array[0].y = y2; + eglglessink->egl_context->position_array[0].z = 0; + eglglessink->egl_context->position_array[0].a = tx2; + eglglessink->egl_context->position_array[0].b = ty1; + + eglglessink->egl_context->position_array[1].x = x2; + eglglessink->egl_context->position_array[1].y = y1; + eglglessink->egl_context->position_array[1].z = 0; + eglglessink->egl_context->position_array[1].a = tx2; + eglglessink->egl_context->position_array[1].b = ty2; + + eglglessink->egl_context->position_array[2].x = x1; + eglglessink->egl_context->position_array[2].y = y2; + eglglessink->egl_context->position_array[2].z = 0; + eglglessink->egl_context->position_array[2].a = tx1; + eglglessink->egl_context->position_array[2].b = ty1; + + eglglessink->egl_context->position_array[3].x = x1; + eglglessink->egl_context->position_array[3].y = y1; + eglglessink->egl_context->position_array[3].z = 0; + eglglessink->egl_context->position_array[3].a = tx1; + eglglessink->egl_context->position_array[3].b = ty2; + + /* X-normal, Y-flip orientation */ + eglglessink->egl_context->position_array[4 + 0].x = x2; + eglglessink->egl_context->position_array[4 + 0].y = y2; + eglglessink->egl_context->position_array[4 + 0].z = 0; + eglglessink->egl_context->position_array[4 + 0].a = tx2; + eglglessink->egl_context->position_array[4 + 0].b = ty2; + + eglglessink->egl_context->position_array[4 + 1].x = x2; + eglglessink->egl_context->position_array[4 + 1].y = y1; + eglglessink->egl_context->position_array[4 + 1].z = 0; + eglglessink->egl_context->position_array[4 + 1].a = tx2; + eglglessink->egl_context->position_array[4 + 1].b = ty1; + + eglglessink->egl_context->position_array[4 + 2].x = x1; + eglglessink->egl_context->position_array[4 + 2].y = y2; + eglglessink->egl_context->position_array[4 + 2].z = 0; + eglglessink->egl_context->position_array[4 + 2].a = tx1; + eglglessink->egl_context->position_array[4 + 2].b = ty2; + + eglglessink->egl_context->position_array[4 + 3].x = x1; + eglglessink->egl_context->position_array[4 + 3].y = y1; + eglglessink->egl_context->position_array[4 + 3].z = 0; + eglglessink->egl_context->position_array[4 + 3].a = tx1; + eglglessink->egl_context->position_array[4 + 3].b = ty1; + + + if (eglglessink->display_region.x == 0) { + /* Borders top/bottom */ + + eglglessink->egl_context->position_array[8 + 0].x = 1; + eglglessink->egl_context->position_array[8 + 0].y = 1; + eglglessink->egl_context->position_array[8 + 0].z = 0; + + eglglessink->egl_context->position_array[8 + 1].x = x2; + eglglessink->egl_context->position_array[8 + 1].y = y2; + eglglessink->egl_context->position_array[8 + 1].z = 0; + + eglglessink->egl_context->position_array[8 + 2].x = -1; + eglglessink->egl_context->position_array[8 + 2].y = 1; + eglglessink->egl_context->position_array[8 + 2].z = 0; + + eglglessink->egl_context->position_array[8 + 3].x = x1; + eglglessink->egl_context->position_array[8 + 3].y = y2; + eglglessink->egl_context->position_array[8 + 3].z = 0; + + eglglessink->egl_context->position_array[12 + 0].x = 1; + eglglessink->egl_context->position_array[12 + 0].y = y1; + eglglessink->egl_context->position_array[12 + 0].z = 0; + + eglglessink->egl_context->position_array[12 + 1].x = 1; + eglglessink->egl_context->position_array[12 + 1].y = -1; + eglglessink->egl_context->position_array[12 + 1].z = 0; + + eglglessink->egl_context->position_array[12 + 2].x = x1; + eglglessink->egl_context->position_array[12 + 2].y = y1; + eglglessink->egl_context->position_array[12 + 2].z = 0; + + eglglessink->egl_context->position_array[12 + 3].x = -1; + eglglessink->egl_context->position_array[12 + 3].y = -1; + eglglessink->egl_context->position_array[12 + 3].z = 0; + } else { + /* Borders left/right */ + + eglglessink->egl_context->position_array[8 + 0].x = x1; + eglglessink->egl_context->position_array[8 + 0].y = 1; + eglglessink->egl_context->position_array[8 + 0].z = 0; + + eglglessink->egl_context->position_array[8 + 1].x = x1; + eglglessink->egl_context->position_array[8 + 1].y = -1; + eglglessink->egl_context->position_array[8 + 1].z = 0; + + eglglessink->egl_context->position_array[8 + 2].x = -1; + eglglessink->egl_context->position_array[8 + 2].y = 1; + eglglessink->egl_context->position_array[8 + 2].z = 0; + + eglglessink->egl_context->position_array[8 + 3].x = -1; + eglglessink->egl_context->position_array[8 + 3].y = -1; + eglglessink->egl_context->position_array[8 + 3].z = 0; + + eglglessink->egl_context->position_array[12 + 0].x = 1; + eglglessink->egl_context->position_array[12 + 0].y = 1; + eglglessink->egl_context->position_array[12 + 0].z = 0; + + eglglessink->egl_context->position_array[12 + 1].x = 1; + eglglessink->egl_context->position_array[12 + 1].y = -1; + eglglessink->egl_context->position_array[12 + 1].z = 0; + + eglglessink->egl_context->position_array[12 + 2].x = x2; + eglglessink->egl_context->position_array[12 + 2].y = y2; + eglglessink->egl_context->position_array[12 + 2].z = 0; + + eglglessink->egl_context->position_array[12 + 3].x = x2; + eglglessink->egl_context->position_array[12 + 3].y = -1; + eglglessink->egl_context->position_array[12 + 3].z = 0; + } + + eglglessink->egl_context->index_array[0] = 0; + eglglessink->egl_context->index_array[1] = 1; + eglglessink->egl_context->index_array[2] = 2; + eglglessink->egl_context->index_array[3] = 3; + + glGenBuffers (1, &eglglessink->egl_context->position_buffer); + glGenBuffers (1, &eglglessink->egl_context->index_buffer); + if (got_gl_error ("glGenBuffers")) + goto HANDLE_ERROR_LOCKED; + + glBindBuffer (GL_ARRAY_BUFFER, eglglessink->egl_context->position_buffer); + if (got_gl_error ("glBindBuffer position_buffer")) + goto HANDLE_ERROR_LOCKED; + + glBufferData (GL_ARRAY_BUFFER, + sizeof (eglglessink->egl_context->position_array), + eglglessink->egl_context->position_array, GL_STATIC_DRAW); + if (got_gl_error ("glBufferData position_buffer")) + goto HANDLE_ERROR_LOCKED; + + glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, + eglglessink->egl_context->index_buffer); + if (got_gl_error ("glBindBuffer index_buffer")) + goto HANDLE_ERROR_LOCKED; + + glBufferData (GL_ELEMENT_ARRAY_BUFFER, + sizeof (eglglessink->egl_context->index_array), + eglglessink->egl_context->index_array, GL_STATIC_DRAW); + if (got_gl_error ("glBufferData index_buffer")) + goto HANDLE_ERROR_LOCKED; + + eglglessink->egl_context->have_vbo = TRUE; + + GST_DEBUG_OBJECT (eglglessink, "VBO setup done"); + + return TRUE; + +HANDLE_ERROR_LOCKED: + GST_ERROR_OBJECT (eglglessink, "Unable to perform VBO setup"); + return FALSE; +} + +static void +gst_eglglessink_set_window_handle (GstVideoOverlay * overlay, guintptr id) +{ + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay); + + g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink)); + GST_DEBUG_OBJECT (eglglessink, "We got a window handle: %p", (void *) id); + + /* OK, we have a new window */ + GST_OBJECT_LOCK (eglglessink); + gst_egl_adaptation_set_window (eglglessink->egl_context, id); + eglglessink->have_window = ((uintptr_t) id != 0); + GST_OBJECT_UNLOCK (eglglessink); + + g_mutex_lock (&eglglessink->render_lock); + g_cond_broadcast (&eglglessink->render_cond); + g_mutex_unlock (&eglglessink->render_lock); + + return; +} + +static void +gst_eglglessink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y, + gint width, gint height) +{ + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (overlay); + + g_return_if_fail (GST_IS_EGLGLESSINK (eglglessink)); + + GST_OBJECT_LOCK (eglglessink); + eglglessink->render_region.x = x; + eglglessink->render_region.y = y; + eglglessink->render_region.w = width; + eglglessink->render_region.h = height; + eglglessink->render_region_changed = TRUE; + eglglessink->render_region_user = (width != -1 && height != -1); + GST_OBJECT_UNLOCK (eglglessink); + + return; +} + +static void +queue_item_destroy (GstDataQueueItem * item) +{ + if (item->object && !GST_IS_QUERY (item->object)) + gst_mini_object_unref (item->object); + g_slice_free (GstDataQueueItem, item); +} + +static GstFlowReturn +gst_eglglessink_queue_object (GstEglGlesSink * eglglessink, GstMiniObject * obj) +{ + GstDataQueueItem *item; + GstFlowReturn last_flow; + + g_mutex_lock (&eglglessink->render_lock); + last_flow = eglglessink->last_flow; + g_mutex_unlock (&eglglessink->render_lock); + + if (last_flow != GST_FLOW_OK) + return last_flow; + + item = g_slice_new0 (GstDataQueueItem); + + if (obj == NULL) + item->object = NULL; + else if (GST_IS_QUERY (obj)) + item->object = obj; + else + item->object = gst_mini_object_ref (obj); + item->size = 0; + item->duration = GST_CLOCK_TIME_NONE; + item->visible = TRUE; + item->destroy = (GDestroyNotify) queue_item_destroy; + + GST_DEBUG_OBJECT (eglglessink, "Queueing object %" GST_PTR_FORMAT, obj); + + g_mutex_lock (&eglglessink->render_lock); + if (!gst_data_queue_push (eglglessink->queue, item)) { + item->destroy (item); + g_mutex_unlock (&eglglessink->render_lock); + GST_DEBUG_OBJECT (eglglessink, "Flushing"); + return GST_FLOW_FLUSHING; + } + + GST_DEBUG_OBJECT (eglglessink, "Waiting for object to be handled"); + do { +/* Incase queue is not used before this, we don't want to waste + * unnecessary time here due to context switch, if put to sleep, + * hence a timed wait TODO*/ + g_cond_wait (&eglglessink->render_cond, &eglglessink->render_lock); + } while (eglglessink->dequeued_object != obj + && eglglessink->last_flow != GST_FLOW_FLUSHING); + GST_DEBUG_OBJECT (eglglessink, "Object handled: %s", + gst_flow_get_name (eglglessink->last_flow)); + last_flow = eglglessink->last_flow; + g_mutex_unlock (&eglglessink->render_lock); + + return (obj ? last_flow : GST_FLOW_OK); +} + +static gboolean +gst_eglglessink_crop_changed (GstEglGlesSink * eglglessink, + GstVideoCropMeta * crop) +{ + if (crop) { + return (crop->x != (guint)eglglessink->crop.x || + crop->y != (guint)eglglessink->crop.y || + crop->width != (guint)eglglessink->crop.w || + crop->height != (guint)eglglessink->crop.h); + } + + return (eglglessink->crop.x != 0 || eglglessink->crop.y != 0 || + eglglessink->crop.w != eglglessink->configured_info.width || + eglglessink->crop.h != eglglessink->configured_info.height); +} + +static gboolean +gst_eglglessink_fill_texture (GstEglGlesSink * eglglessink, GstBuffer * buf) +{ + GstVideoFrame vframe; +#ifndef GST_DISABLE_GST_DEBUG + gint w; +#endif + gint h; + + memset (&vframe, 0, sizeof (vframe)); + + if (!gst_video_frame_map (&vframe, &eglglessink->configured_info, buf, + GST_MAP_READ)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't map frame"); + goto HANDLE_ERROR; + } +#ifndef GST_DISABLE_GST_DEBUG + w = GST_VIDEO_FRAME_WIDTH (&vframe); +#endif + h = GST_VIDEO_FRAME_HEIGHT (&vframe); + + GST_DEBUG_OBJECT (eglglessink, + "Got buffer %p: %dx%d size %" G_GSIZE_FORMAT, buf, w, h, + gst_buffer_get_size (buf)); + + switch (eglglessink->configured_info.finfo->format) { + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_RGB:{ + gint stride; + gint stride_width; + gint c_w; + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0); + stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe); + + glActiveTexture (GL_TEXTURE0); + + if (GST_ROUND_UP_8 (c_w * 3) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (c_w * 3) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (c_w * 3) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (c_w * 3 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width * 3) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (stride_width * 3) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (stride_width * 3) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (stride_width * 3 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB, + GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0)); + break; + } + case GST_VIDEO_FORMAT_RGB16:{ + gint stride; + gint stride_width; + gint c_w; + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0); + stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe); + + glActiveTexture (GL_TEXTURE0); + + if (GST_ROUND_UP_8 (c_w * 2) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (c_w * 2) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (c_w * 2 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width * 4) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (stride_width * 2 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, stride_width, h, 0, GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0)); + break; + } + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_RGBx: + case GST_VIDEO_FORMAT_BGRx: + case GST_VIDEO_FORMAT_xRGB: + case GST_VIDEO_FORMAT_xBGR:{ + gint stride; + gint stride_width; + gint c_w; + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0); + stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe); + + glActiveTexture (GL_TEXTURE0); + + if (GST_ROUND_UP_8 (c_w * 4) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (c_w * 4 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width * 4) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (stride_width * 4 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0)); + break; + } + case GST_VIDEO_FORMAT_AYUV:{ + gint stride; + gint stride_width; + gint c_w; + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0); + stride_width = c_w = GST_VIDEO_FRAME_WIDTH (&vframe); + + glActiveTexture (GL_TEXTURE0); + + if (GST_ROUND_UP_8 (c_w * 4) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (c_w * 4 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width * 4) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (stride_width * 4 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, stride_width, h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0)); + break; + } + case GST_VIDEO_FORMAT_Y444: + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_YV12: + case GST_VIDEO_FORMAT_Y42B: + case GST_VIDEO_FORMAT_Y41B:{ + gint stride; + gint stride_width; + gint c_w; + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0); + stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0); + + glActiveTexture (GL_TEXTURE0); + + if (GST_ROUND_UP_8 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (c_w == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (stride_width == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, + stride_width, + GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0), + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, + GST_VIDEO_FRAME_COMP_DATA (&vframe, 0)); + + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1); + stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1); + + glActiveTexture (GL_TEXTURE1); + + if (GST_ROUND_UP_8 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (c_w == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (stride_width == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, + stride_width, + GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1), + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, + GST_VIDEO_FRAME_COMP_DATA (&vframe, 1)); + + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 2); + stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 2); + + glActiveTexture (GL_TEXTURE2); + + if (GST_ROUND_UP_8 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (c_w == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (stride_width == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[2] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[2]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, + stride_width, + GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 2), + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, + GST_VIDEO_FRAME_COMP_DATA (&vframe, 2)); + break; + } + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_NV21:{ + gint stride; + gint stride_width; + gint c_w; + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0); + stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0); + + glActiveTexture (GL_TEXTURE0); + + if (GST_ROUND_UP_8 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (c_w) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (c_w == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + stride_width = stride; + + if (GST_ROUND_UP_8 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (GST_ROUND_UP_2 (stride_width) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else if (stride_width == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[0] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, + stride_width, + GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0), + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, + GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0)); + + + stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1); + stride_width = c_w = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 1); + + glActiveTexture (GL_TEXTURE1); + + if (GST_ROUND_UP_8 (c_w * 2) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (c_w * 2) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (c_w * 2 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else { + stride_width = stride / 2; + + if (GST_ROUND_UP_8 (stride_width * 2) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 8); + } else if (GST_ROUND_UP_4 (stride_width * 2) == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + } else if (stride_width * 2 == stride) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 2); + } else { + GST_ERROR_OBJECT (eglglessink, "Unsupported stride %d", stride); + goto HANDLE_ERROR; + } + } + if (got_gl_error ("glPixelStorei")) + goto HANDLE_ERROR; + + eglglessink->stride[1] = ((gdouble) stride_width) / ((gdouble) c_w); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[1]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, + stride_width, + GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 1), + 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, + GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1)); + break; + } + default: + g_assert_not_reached (); + break; + } + + if (got_gl_error ("glTexImage2D")) + goto HANDLE_ERROR; + + gst_video_frame_unmap (&vframe); + + return TRUE; + +HANDLE_ERROR: + { + if (vframe.buffer) + gst_video_frame_unmap (&vframe); + return FALSE; + } +} + +static gboolean +gst_eglglessink_cuda_buffer_copy (GstEglGlesSink * eglglessink, GstBuffer * buf) +{ + CUarray dpArray; + CUresult result; + guint width, height; + GstMapInfo info = GST_MAP_INFO_INIT; + GstVideoFormat videoFormat; + int is_v4l2_mem = 0; + GstMemory *inMem; + NvBufSurface *in_surface = NULL; + + width = GST_VIDEO_SINK_WIDTH (eglglessink); + height = GST_VIDEO_SINK_HEIGHT (eglglessink); + + result = cuCtxSetCurrent(eglglessink->cuContext); + if (result != CUDA_SUCCESS) { + g_print ("cuCtxSetCurrent failed with error(%d) %s\n", result, __func__); + return FALSE; + } + gst_buffer_map (buf, &info, GST_MAP_READ); + + //Checking for V4l2Memory + inMem = gst_buffer_peek_memory (buf, 0); + if (!g_strcmp0 (inMem->allocator->mem_type, "V4l2Memory")) + is_v4l2_mem = 1; + + gst_buffer_unmap (buf, &info); + + if ((!is_v4l2_mem && info.size != sizeof(NvBufSurface)) || (is_v4l2_mem && !eglglessink->nvbuf_api_version_new)) { + g_print ("nveglglessink cannot handle Legacy NVMM Buffers %s\n", __func__); + return FALSE; + } + + in_surface = (NvBufSurface*) info.data; + + if (in_surface->batchSize != 1) { + g_print ("ERROR: Batch size not 1\n"); + return FALSE; + } + + NvBufSurfaceMemType memType = in_surface->memType; + gboolean is_device_memory = FALSE; + gboolean is_host_memory = FALSE; + if (memType == NVBUF_MEM_DEFAULT) { +#ifdef IS_DESKTOP + memType = NVBUF_MEM_CUDA_DEVICE; +#else + memType = NVBUF_MEM_SURFACE_ARRAY; +#endif + } + + if (memType == NVBUF_MEM_SURFACE_ARRAY || memType == NVBUF_MEM_HANDLE) { + g_print ("eglglessink cannot handle NVRM surface array %s\n", __func__); + return FALSE; + } + + if (memType == NVBUF_MEM_CUDA_DEVICE || memType == NVBUF_MEM_CUDA_UNIFIED) { + is_device_memory = TRUE; + } + else if (memType == NVBUF_MEM_CUDA_PINNED) { + is_host_memory = TRUE; + } + + CUDA_MEMCPY2D m = { 0 }; + + videoFormat = eglglessink->configured_info.finfo->format; + switch (videoFormat) { + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_RGB: { + gint bytesPerPix = 3; + + uint8_t *ptr = (uint8_t *)in_surface->surfaceList[0].dataPtr; + + if (is_device_memory) { + m.srcDevice = (CUdeviceptr) ptr; + m.srcMemoryType = CU_MEMORYTYPE_DEVICE; + } + else if (is_host_memory) { + m.srcHost = (void *)ptr; + m.srcMemoryType = CU_MEMORYTYPE_HOST; + } + m.srcPitch = in_surface->surfaceList[0].planeParams.pitch[0]; + m.dstHost = (void *)eglglessink->swData; + m.dstMemoryType = CU_MEMORYTYPE_HOST; + m.dstPitch = width * bytesPerPix; + m.Height = height; + m.WidthInBytes = width * bytesPerPix; + + result = cuMemcpy2D(&m); + if (result != CUDA_SUCCESS) { + g_print ("cuMemcpy2D failed with error(%d) %s\n", result, __func__); + goto HANDLE_ERROR; + } + + glActiveTexture (GL_TEXTURE0); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, + GL_UNSIGNED_BYTE, (void *)eglglessink->swData); + + eglglessink->stride[0] = 1; + eglglessink->stride[1] = 1; + eglglessink->stride[2] = 1; + } + break; + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRx: { + gint bytesPerPix = 4; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + + result = cuGraphicsMapResources(1, &(eglglessink->cuResource[0]), 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsMapResources failed with error(%d) %s\n", result, __func__); + return FALSE; + } + result = cuGraphicsSubResourceGetMappedArray(&dpArray, eglglessink->cuResource[0], 0, 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsResourceGetMappedPointer failed with error(%d) %s\n", result, __func__); + goto HANDLE_ERROR; + } + + if (is_device_memory) { + m.srcDevice = (CUdeviceptr) in_surface->surfaceList[0].dataPtr; + m.srcMemoryType = CU_MEMORYTYPE_DEVICE; + } + else if (is_host_memory) { + m.srcHost = (void *)in_surface->surfaceList[0].dataPtr; + m.srcMemoryType = CU_MEMORYTYPE_HOST; + } + + m.srcPitch = in_surface->surfaceList[0].planeParams.pitch[0]; + + m.dstPitch = width * bytesPerPix; + m.WidthInBytes = width * bytesPerPix; + + m.dstMemoryType = CU_MEMORYTYPE_ARRAY; + m.dstArray = dpArray; + m.Height = height; + + result = cuMemcpy2D(&m); + if (result != CUDA_SUCCESS) { + g_print ("cuMemcpy2D failed with error(%d) %s\n", result, __func__); + goto HANDLE_ERROR; + } + + result = cuGraphicsUnmapResources(1, &(eglglessink->cuResource[0]), 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsUnmapResources failed with error(%d) %s\n", result, __func__); + goto HANDLE_ERROR; + } + + eglglessink->stride[0] = 1; + eglglessink->stride[1] = 1; + eglglessink->stride[2] = 1; + } // case RGBA + break; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_NV12: { + uint8_t *ptr; + int i, pstride; + int num_planes = (int)in_surface->surfaceList[0].planeParams.num_planes; + + for ( i = 0; i < num_planes; i ++) { + if (i == 0) + glActiveTexture (GL_TEXTURE0); + else if (i == 1) + glActiveTexture (GL_TEXTURE1); + else if (i == 2) + glActiveTexture (GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, eglglessink->egl_context->texture[i]); + + result = cuGraphicsMapResources(1, &(eglglessink->cuResource[i]), 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsMapResources failed with error(%d) %s\n", result, __func__); + return FALSE; + } + result = cuGraphicsSubResourceGetMappedArray(&dpArray, eglglessink->cuResource[i], 0, 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsResourceGetMappedPointer failed with error(%d) %s\n", result, __func__); + goto HANDLE_ERROR; + } + + ptr = (uint8_t *)in_surface->surfaceList[0].dataPtr + in_surface->surfaceList[0].planeParams.offset[i]; + if (is_device_memory) { + m.srcDevice = (CUdeviceptr) ptr; + m.srcMemoryType = CU_MEMORYTYPE_DEVICE; + } + else if (is_host_memory) { + m.srcHost = (void *)ptr; + m.srcMemoryType = CU_MEMORYTYPE_HOST; + } + + width = GST_VIDEO_INFO_COMP_WIDTH(&(eglglessink->configured_info), i); + height = GST_VIDEO_INFO_COMP_HEIGHT(&(eglglessink->configured_info), i); + pstride = GST_VIDEO_INFO_COMP_PSTRIDE(&(eglglessink->configured_info), i); + m.srcPitch = in_surface->surfaceList[0].planeParams.pitch[i]; + + m.dstMemoryType = CU_MEMORYTYPE_ARRAY; + m.dstArray = dpArray; + m.WidthInBytes = width*pstride; + m.Height = height; + + result = cuMemcpy2D(&m); + if (result != CUDA_SUCCESS) { + g_print ("cuMemcpy2D failed with error(%d) %s %d\n", result, __func__, __LINE__); + goto HANDLE_ERROR; + } + + result = cuGraphicsUnmapResources(1, &(eglglessink->cuResource[i]), 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsUnmapResources failed with error(%d) %s\n", result, __func__); + goto HANDLE_ERROR; + } + + eglglessink->stride[i] = pstride; + } + eglglessink->orientation = + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL; + }// case I420 or NV12 + break; + default: + g_print("buffer format not supported\n"); + return FALSE; + break; + } //switch + return TRUE; + +HANDLE_ERROR: + if (eglglessink->cuResource[0]) + cuGraphicsUnmapResources(1, &(eglglessink->cuResource[0]), 0); + if (eglglessink->cuResource[1]) + cuGraphicsUnmapResources(1, &(eglglessink->cuResource[0]), 0); + if (eglglessink->cuResource[2]) + cuGraphicsUnmapResources(1, &(eglglessink->cuResource[0]), 0); + return FALSE; +} + +/* Rendering and display */ +static GstFlowReturn +gst_eglglessink_upload (GstEglGlesSink * eglglessink, GstBuffer * buf) +{ + GstVideoCropMeta *crop = NULL; + + if (!buf) { + GST_DEBUG_OBJECT (eglglessink, "Rendering previous buffer again"); + } else if (buf) { +#ifndef HAVE_IOS + GstMemory *mem; +#endif + GstVideoGLTextureUploadMeta *upload_meta; + + crop = gst_buffer_get_video_crop_meta (buf); + + upload_meta = gst_buffer_get_video_gl_texture_upload_meta (buf); + + if (gst_eglglessink_crop_changed (eglglessink, crop)) { + if (crop) { + eglglessink->crop.x = crop->x; + eglglessink->crop.y = crop->y; + eglglessink->crop.w = crop->width; + eglglessink->crop.h = crop->height; + } else { + eglglessink->crop.x = 0; + eglglessink->crop.y = 0; + eglglessink->crop.w = eglglessink->configured_info.width; + eglglessink->crop.h = eglglessink->configured_info.height; + } + eglglessink->crop_changed = TRUE; + } + + if (upload_meta) { + gint i; + + if (upload_meta->n_textures != (guint)eglglessink->egl_context->n_textures) + goto HANDLE_ERROR; + + if (eglglessink->egl_context->n_textures > 3) { + goto HANDLE_ERROR; + } + + for (i = 0; i < eglglessink->egl_context->n_textures; i++) { + if (i == 0) + glActiveTexture (GL_TEXTURE0); + else if (i == 1) + glActiveTexture (GL_TEXTURE1); + else if (i == 2) + glActiveTexture (GL_TEXTURE2); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]); + } + + if (!gst_video_gl_texture_upload_meta_upload (upload_meta, + eglglessink->egl_context->texture)) + goto HANDLE_ERROR; + + eglglessink->orientation = upload_meta->texture_orientation; + eglglessink->stride[0] = 1; + eglglessink->stride[1] = 1; + eglglessink->stride[2] = 1; +#ifndef HAVE_IOS + } else if (gst_buffer_n_memory (buf) >= 1 && + (mem = gst_buffer_peek_memory (buf, 0)) + && gst_is_egl_image_memory (mem)) { + guint n, i; + + n = gst_buffer_n_memory (buf); + + for (i = 0; i < n; i++) { + mem = gst_buffer_peek_memory (buf, i); + + g_assert (gst_is_egl_image_memory (mem)); + + if (i == 0) + glActiveTexture (GL_TEXTURE0); + else if (i == 1) + glActiveTexture (GL_TEXTURE1); + else if (i == 2) + glActiveTexture (GL_TEXTURE2); + + glBindTexture (GL_TEXTURE_2D, eglglessink->egl_context->texture[i]); + + if (eglglessink->glEGLImageTargetTexture2DOES) { + eglglessink->glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, + gst_egl_image_memory_get_image (mem)); + if (got_gl_error ("glEGLImageTargetTexture2DOES")) + goto HANDLE_ERROR; + } else { + GST_ERROR_OBJECT (eglglessink, + "glEGLImageTargetTexture2DOES not supported"); + return GST_FLOW_ERROR; + } + + eglglessink->orientation = gst_egl_image_memory_get_orientation (mem); + if (eglglessink->orientation != + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL + && eglglessink->orientation != + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) { + GST_ERROR_OBJECT (eglglessink, "Unsupported EGLImage orientation"); + return GST_FLOW_ERROR; + } + } + + eglglessink->last_uploaded_buffer = buf; + + eglglessink->stride[0] = 1; + eglglessink->stride[1] = 1; + eglglessink->stride[2] = 1; +#endif + } else if (eglglessink->using_cuda) { + //Handle Cuda Buffers + if (!gst_eglglessink_cuda_buffer_copy(eglglessink, buf)) { + goto HANDLE_ERROR; + } + } else { + eglglessink->orientation = + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL; + if (!gst_eglglessink_fill_texture (eglglessink, buf)) + goto HANDLE_ERROR; + } + } + + return GST_FLOW_OK; + +HANDLE_ERROR: + { + GST_ERROR_OBJECT (eglglessink, "Failed to upload texture"); + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_eglglessink_render (GstEglGlesSink * eglglessink) +{ + guint dar_n, dar_d; + gint i; + + /* If no one has set a display rectangle on us initialize + * a sane default. According to the docs on the xOverlay + * interface we are supposed to fill the overlay 100%. We + * do this trying to take PAR/DAR into account unless the + * calling party explicitly ask us not to by setting + * force_aspect_ratio to FALSE. + */ + if (gst_egl_adaptation_update_surface_dimensions (eglglessink->egl_context) || + eglglessink->render_region_changed || + !eglglessink->display_region.w || !eglglessink->display_region.h || + eglglessink->crop_changed) { + GST_OBJECT_LOCK (eglglessink); + + if (!eglglessink->render_region_user) { + eglglessink->render_region.x = 0; + eglglessink->render_region.y = 0; + eglglessink->render_region.w = eglglessink->egl_context->surface_width / eglglessink->rows; + eglglessink->render_region.h = eglglessink->egl_context->surface_height / eglglessink->columns; + } + eglglessink->render_region_changed = FALSE; + eglglessink->crop_changed = FALSE; + + if (!eglglessink->force_aspect_ratio) { + eglglessink->display_region.x = 0; + eglglessink->display_region.y = 0; + eglglessink->display_region.w = eglglessink->render_region.w; + eglglessink->display_region.h = eglglessink->render_region.h; + } else { + GstVideoRectangle frame; + + frame.x = 0; + frame.y = 0; + + if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, + eglglessink->crop.w, eglglessink->crop.h, + eglglessink->configured_info.par_n, + eglglessink->configured_info.par_d, + eglglessink->egl_context->pixel_aspect_ratio_n, + eglglessink->egl_context->pixel_aspect_ratio_d)) { + GST_WARNING_OBJECT (eglglessink, "Could not compute resulting DAR"); + frame.w = eglglessink->crop.w; + frame.h = eglglessink->crop.h; + } else { + /* Find suitable matching new size acording to dar & par + * rationale for prefering leaving the height untouched + * comes from interlacing considerations. + * XXX: Move this to gstutils? + */ + if (eglglessink->crop.h % dar_d == 0) { + frame.w = + gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d); + frame.h = eglglessink->crop.h; + } else if (eglglessink->crop.w % dar_n == 0) { + frame.h = + gst_util_uint64_scale_int (eglglessink->crop.w, dar_d, dar_n); + frame.w = eglglessink->crop.w; + } else { + /* Neither width nor height can be precisely scaled. + * Prefer to leave height untouched. See comment above. + */ + frame.w = + gst_util_uint64_scale_int (eglglessink->crop.h, dar_n, dar_d); + frame.h = eglglessink->crop.h; + } + } + + gst_video_sink_center_rect (frame, eglglessink->render_region, + &eglglessink->display_region, TRUE); + } + + glViewport (eglglessink->render_region.x + + (eglglessink->change_port % eglglessink->rows) * eglglessink->render_region.w, + eglglessink->egl_context->surface_height - eglglessink->render_region.h - + (eglglessink->render_region.y + + ((eglglessink->change_port / eglglessink->columns) % eglglessink->columns) * + eglglessink->render_region.h), + eglglessink->render_region.w, + eglglessink->render_region.h); + + /* Clear the surface once if its content is preserved */ + if (eglglessink->egl_context->buffer_preserved || + eglglessink->change_port % (eglglessink->rows * eglglessink->columns) == 0) { + glClearColor (0.0, 0.0, 0.0, 1.0); + glClear (GL_COLOR_BUFFER_BIT); + eglglessink->egl_context->buffer_preserved = FALSE; + } + + if (!gst_eglglessink_setup_vbo (eglglessink)) { + GST_OBJECT_UNLOCK (eglglessink); + GST_ERROR_OBJECT (eglglessink, "VBO setup failed"); + goto HANDLE_ERROR; + } + GST_OBJECT_UNLOCK (eglglessink); + } + + if (!eglglessink->egl_context->buffer_preserved) { + /* Draw black borders */ + GST_DEBUG_OBJECT (eglglessink, "Drawing black border 1"); + glUseProgram (eglglessink->egl_context->glslprogram[1]); + + glEnableVertexAttribArray (eglglessink->egl_context->position_loc[1]); + if (got_gl_error ("glEnableVertexAttribArray")) + goto HANDLE_ERROR; + + glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3, + GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (8 * sizeof (coord5))); + if (got_gl_error ("glVertexAttribPointer")) + goto HANDLE_ERROR; + + glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); + if (got_gl_error ("glDrawElements")) + goto HANDLE_ERROR; + + GST_DEBUG_OBJECT (eglglessink, "Drawing black border 2"); + + glVertexAttribPointer (eglglessink->egl_context->position_loc[1], 3, + GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (12 * sizeof (coord5))); + if (got_gl_error ("glVertexAttribPointer")) + goto HANDLE_ERROR; + + glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); + if (got_gl_error ("glDrawElements")) + goto HANDLE_ERROR; + + glDisableVertexAttribArray (eglglessink->egl_context->position_loc[1]); + } + + /* Draw video frame */ + GST_DEBUG_OBJECT (eglglessink, "Drawing video frame"); + + glUseProgram (eglglessink->egl_context->glslprogram[0]); + + glUniform2f (eglglessink->egl_context->tex_scale_loc[0][0], + eglglessink->stride[0], 1); + glUniform2f (eglglessink->egl_context->tex_scale_loc[0][1], + eglglessink->stride[1], 1); + glUniform2f (eglglessink->egl_context->tex_scale_loc[0][2], + eglglessink->stride[2], 1); + + for (i = 0; i < eglglessink->egl_context->n_textures; i++) { + glUniform1i (eglglessink->egl_context->tex_loc[0][i], i); + if (got_gl_error ("glUniform1i")) + goto HANDLE_ERROR; + } + + glEnableVertexAttribArray (eglglessink->egl_context->position_loc[0]); + if (got_gl_error ("glEnableVertexAttribArray")) + goto HANDLE_ERROR; + + glEnableVertexAttribArray (eglglessink->egl_context->texpos_loc[0]); + if (got_gl_error ("glEnableVertexAttribArray")) + goto HANDLE_ERROR; + + if (eglglessink->orientation == + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL) { + glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3, + GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (0 * sizeof (coord5))); + if (got_gl_error ("glVertexAttribPointer")) + goto HANDLE_ERROR; + + glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2, + GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (3 * sizeof (gfloat))); + if (got_gl_error ("glVertexAttribPointer")) + goto HANDLE_ERROR; + } else if (eglglessink->orientation == + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP) { + glVertexAttribPointer (eglglessink->egl_context->position_loc[0], 3, + GL_FLOAT, GL_FALSE, sizeof (coord5), (gpointer) (4 * sizeof (coord5))); + if (got_gl_error ("glVertexAttribPointer")) + goto HANDLE_ERROR; + + glVertexAttribPointer (eglglessink->egl_context->texpos_loc[0], 2, + GL_FLOAT, GL_FALSE, sizeof (coord5), + (gpointer) (4 * sizeof (coord5) + 3 * sizeof (gfloat))); + if (got_gl_error ("glVertexAttribPointer")) + goto HANDLE_ERROR; + } else { + g_assert_not_reached (); + } + + glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, 0); + if (got_gl_error ("glDrawElements")) + goto HANDLE_ERROR; + + glDisableVertexAttribArray (eglglessink->egl_context->position_loc[0]); + glDisableVertexAttribArray (eglglessink->egl_context->texpos_loc[0]); + + if (!gst_egl_adaptation_context_swap_buffers (eglglessink->egl_context)) { + goto HANDLE_ERROR; + } + + if (eglglessink->profile) + GstEglJitterToolAddPoint(eglglessink->pDeliveryJitter); + + GST_DEBUG_OBJECT (eglglessink, "Succesfully rendered 1 frame"); + return GST_FLOW_OK; + +HANDLE_ERROR: + glDisableVertexAttribArray (eglglessink->egl_context->position_loc[0]); + glDisableVertexAttribArray (eglglessink->egl_context->texpos_loc[0]); + glDisableVertexAttribArray (eglglessink->egl_context->position_loc[1]); + + GST_ERROR_OBJECT (eglglessink, "Rendering disabled for this frame"); + + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_eglglessink_prepare (GstBaseSink * bsink, GstBuffer * buf) +{ + GstEglGlesSink *eglglessink; + + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + + eglglessink = GST_EGLGLESSINK (bsink); + GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf); + + return gst_eglglessink_queue_object (eglglessink, GST_MINI_OBJECT_CAST (buf)); +} + +static GstFlowReturn +gst_eglglessink_show_frame (GstVideoSink * vsink, GstBuffer * buf) +{ + GstEglGlesSink *eglglessink; + + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + + eglglessink = GST_EGLGLESSINK (vsink); + GST_DEBUG_OBJECT (eglglessink, "Got buffer: %p", buf); + + return gst_eglglessink_queue_object (eglglessink, NULL); +} + +static GstCaps * +gst_eglglessink_getcaps (GstBaseSink * bsink, GstCaps * filter) +{ + GstEglGlesSink *eglglessink; + GstCaps *ret = NULL; + + eglglessink = GST_EGLGLESSINK (bsink); + + GST_OBJECT_LOCK (eglglessink); + if (eglglessink->sinkcaps) { + ret = gst_caps_ref (eglglessink->sinkcaps); + } else { + ret = + gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD + (bsink))); + } + GST_OBJECT_UNLOCK (eglglessink); + + if (filter) { + GstCaps *tmp = + gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST); + + gst_caps_unref (ret); + ret = tmp; + } + + return ret; +} + +static gboolean +gst_eglglessink_query (GstBaseSink * bsink, GstQuery * query) +{ + GstEglGlesSink *eglglessink; + + eglglessink = GST_EGLGLESSINK (bsink); + + switch (GST_QUERY_TYPE (query)) { +#ifndef HAVE_IOS + case GST_QUERY_CONTEXT:{ + const gchar *context_type; + + if (gst_query_parse_context_type (query, &context_type) && + strcmp (context_type, GST_EGL_DISPLAY_CONTEXT_TYPE) && + eglglessink->egl_context->display) { + GstContext *context; + + context = + gst_context_new_egl_display (eglglessink->egl_context->display, + FALSE); + gst_query_set_context (query, context); + gst_context_unref (context); + + return TRUE; + } else { + return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink, + query); + } + break; + } +#endif + default: + return GST_BASE_SINK_CLASS (gst_eglglessink_parent_class)->query (bsink, + query); + break; + } +} + +static void +gst_eglglessink_set_context (GstElement * element, GstContext * context) +{ +#ifndef HAVE_IOS + GstEglGlesSink *eglglessink; + GstEGLDisplay *display = NULL; + + eglglessink = GST_EGLGLESSINK (element); + + if (gst_context_get_egl_display (context, &display)) { + GST_OBJECT_LOCK (eglglessink); + if (eglglessink->egl_context->set_display) + gst_egl_display_unref (eglglessink->egl_context->set_display); + eglglessink->egl_context->set_display = display; + GST_OBJECT_UNLOCK (eglglessink); + } +#endif +} + +static gboolean +gst_eglglessink_propose_allocation (GstBaseSink * bsink, GstQuery * query) +{ +#ifndef HAVE_IOS + GstEglGlesSink *eglglessink; + GstBufferPool *pool; + GstStructure *config; + GstCaps *caps; + GstVideoInfo info; + gboolean need_pool; + guint size; + GstAllocator *allocator; + GstAllocationParams params; + + eglglessink = GST_EGLGLESSINK (bsink); + + gst_allocation_params_init (¶ms); + + gst_query_parse_allocation (query, &caps, &need_pool); + if (!caps) { + GST_ERROR_OBJECT (eglglessink, "allocation query without caps"); + return FALSE; + } + + if (!gst_video_info_from_caps (&info, caps)) { + GST_ERROR_OBJECT (eglglessink, "allocation query with invalid caps"); + return FALSE; + } + + GST_OBJECT_LOCK (eglglessink); + pool = eglglessink->pool ? gst_object_ref (eglglessink->pool) : NULL; + GST_OBJECT_UNLOCK (eglglessink); + + if (pool) { + GstCaps *pcaps; + + /* we had a pool, check caps */ + GST_DEBUG_OBJECT (eglglessink, "check existing pool caps"); + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL); + + if (!gst_caps_is_equal (caps, pcaps)) { + GST_DEBUG_OBJECT (eglglessink, "pool has different caps"); + /* different caps, we can't use this pool */ + gst_object_unref (pool); + pool = NULL; + } + gst_structure_free (config); + } + + if (pool == NULL && need_pool) { + GstVideoInfo info; + + if (!gst_video_info_from_caps (&info, caps)) { + GST_ERROR_OBJECT (eglglessink, "allocation query has invalid caps %" + GST_PTR_FORMAT, caps); + return FALSE; + } + + GST_DEBUG_OBJECT (eglglessink, "create new pool"); + pool = + gst_egl_image_buffer_pool_new + (gst_eglglessink_egl_image_buffer_pool_send_blocking, + gst_object_ref (eglglessink), + gst_eglglessink_egl_image_buffer_pool_on_destroy); + + /* the normal size of a frame */ + size = info.size; + + config = gst_buffer_pool_get_config (pool); + /* we need at least 2 buffer because we hold on to the last one */ + gst_buffer_pool_config_set_params (config, caps, size, 2, 0); + gst_buffer_pool_config_set_allocator (config, NULL, ¶ms); + if (!gst_buffer_pool_set_config (pool, config)) { + gst_object_unref (pool); + GST_ERROR_OBJECT (eglglessink, "failed to set pool configuration"); + return FALSE; + } + } + + if (pool) { + /* we need at least 2 buffer because we hold on to the last one */ + gst_query_add_allocation_pool (query, pool, size, 2, 0); + gst_object_unref (pool); + } + + /* First the default allocator */ + if (!gst_egl_image_memory_is_mappable ()) { + allocator = gst_allocator_find (NULL); + gst_query_add_allocation_param (query, allocator, ¶ms); + gst_object_unref (allocator); + } + + allocator = gst_egl_image_allocator_obtain (); + if (!gst_egl_image_memory_is_mappable ()) + params.flags |= GST_MEMORY_FLAG_NOT_MAPPABLE; + gst_query_add_allocation_param (query, allocator, ¶ms); + gst_object_unref (allocator); + + gst_query_add_allocation_meta (query, + GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL); +#endif + + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL); + + return TRUE; +} + +static gboolean +gst_eglglessink_cuda_init (GstEglGlesSink * eglglessink) +{ + CUcontext pctx; + CUresult result; + GLenum error; + int i; + guint width, height, pstride; + GstVideoFormat videoFormat; + + cuInit(0); + result = cuCtxCreate(&pctx, 0, 0); + if (result != CUDA_SUCCESS) { + g_print ("cuCtxCreate failed with error(%d) %s\n", result, __func__); + return FALSE; + } + + eglglessink->cuContext = pctx; + eglglessink->swData = NULL; + + width = GST_VIDEO_SINK_WIDTH (eglglessink); + height = GST_VIDEO_SINK_HEIGHT(eglglessink); + + videoFormat = eglglessink->configured_info.finfo->format; + + switch (videoFormat) { + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_RGB: { + // Allocate memory for sw buffer + eglglessink->swData = (uint8_t *)malloc(width * height * 3 * sizeof(uint8_t)); + } + break; + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGRx: { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, eglglessink->egl_context->texture[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + error = glGetError(); + if (error != GL_NO_ERROR) { + g_print("glerror %x error %d\n", error, __LINE__); + return FALSE; + } + result = cuGraphicsGLRegisterImage(&(eglglessink->cuResource[0]), eglglessink->egl_context->texture[0], GL_TEXTURE_2D, 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsGLRegisterBuffer failed with error(%d) %s texture = %x\n", result, __func__, eglglessink->egl_context->texture[0]); + return FALSE; + } + } + break; + case GST_VIDEO_FORMAT_I420: { + for (i = 0; i < 3; i++) { + if (i == 0) + glActiveTexture (GL_TEXTURE0); + else if (i == 1) + glActiveTexture (GL_TEXTURE1); + else if (i == 2) + glActiveTexture (GL_TEXTURE2); + + width = GST_VIDEO_INFO_COMP_WIDTH(&(eglglessink->configured_info), i); + height = GST_VIDEO_INFO_COMP_HEIGHT(&(eglglessink->configured_info), i); + + glBindTexture(GL_TEXTURE_2D, eglglessink->egl_context->texture[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + error = glGetError(); + if (error != GL_NO_ERROR) { + g_print("glerror %x error %d\n", error, __LINE__); + return FALSE; + } + result = cuGraphicsGLRegisterImage(&(eglglessink->cuResource[i]), eglglessink->egl_context->texture[i], GL_TEXTURE_2D, 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsGLRegisterBuffer failed with error(%d) %s texture = %x\n", result, __func__, eglglessink->egl_context->texture[i]); + return FALSE; + } + } + } + break; + case GST_VIDEO_FORMAT_NV12: { + for (i = 0; i < 2; i++) { + if (i == 0) + glActiveTexture (GL_TEXTURE0); + else if (i == 1) + glActiveTexture (GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, eglglessink->egl_context->texture[i]); + + width = GST_VIDEO_INFO_COMP_WIDTH(&(eglglessink->configured_info), i); + height = GST_VIDEO_INFO_COMP_HEIGHT(&(eglglessink->configured_info), i); + pstride = GST_VIDEO_INFO_COMP_PSTRIDE(&(eglglessink->configured_info), i); + + if (i == 0) + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width*pstride, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + else if ( i == 1) + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width*pstride, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + error = glGetError(); + if (error != GL_NO_ERROR) { + g_print("glerror %x error %d\n", error, __LINE__); + return FALSE; + } + result = cuGraphicsGLRegisterImage(&(eglglessink->cuResource[i]), eglglessink->egl_context->texture[i], GL_TEXTURE_2D, 0); + if (result != CUDA_SUCCESS) { + g_print ("cuGraphicsGLRegisterBuffer failed with error(%d) %s texture = %x\n", result, __func__, eglglessink->egl_context->texture[i]); + return FALSE; + } + } + } + break; + default: + g_print("buffer format not supported\n"); + return FALSE; + } + return TRUE; +} + +static void +gst_eglglessink_cuda_cleanup (GstEglGlesSink * eglglessink) +{ + CUresult result; + guint i; + + for (i = 0; i < 3; i++) { + if (eglglessink->cuResource[i]) + cuGraphicsUnregisterResource (eglglessink->cuResource[i]); + } + + if (eglglessink->cuContext) { + result = cuCtxDestroy(eglglessink->cuContext); + if (result != CUDA_SUCCESS) { + g_print ("cuCtxDestroy failed with error(%d) %s\n", result, __func__); + } + } + + // Free sw buffer memory + if (eglglessink->swData) { + free(eglglessink->swData); + } +} + +static gboolean +gst_eglglessink_configure_caps (GstEglGlesSink * eglglessink, GstCaps * caps) +{ + gboolean ret = TRUE; + GstVideoInfo info; + gint width = 0; + gint height = 0; + + gst_video_info_init (&info); + if (!(ret = gst_video_info_from_caps (&info, caps))) { + GST_ERROR_OBJECT (eglglessink, "Couldn't parse caps"); + goto HANDLE_ERROR; + } + + eglglessink->configured_info = info; + GST_VIDEO_SINK_WIDTH (eglglessink) = info.width; + GST_VIDEO_SINK_HEIGHT (eglglessink) = info.height; + + if (eglglessink->configured_caps) { + GST_DEBUG_OBJECT (eglglessink, "Caps were already set"); + if (gst_caps_can_intersect (caps, eglglessink->configured_caps)) { + GST_DEBUG_OBJECT (eglglessink, "Caps are compatible anyway"); + goto SUCCEED; + } + + GST_DEBUG_OBJECT (eglglessink, "Caps are not compatible, reconfiguring"); + + /* EGL/GLES cleanup */ + if (eglglessink->using_cuda) { + gst_eglglessink_cuda_cleanup(eglglessink); + } + gst_egl_adaptation_cleanup (eglglessink->egl_context); + gst_caps_unref (eglglessink->configured_caps); + eglglessink->configured_caps = NULL; + } + + if (!gst_egl_adaptation_choose_config (eglglessink->egl_context)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't choose EGL config"); + goto HANDLE_ERROR; + } + + gst_caps_replace (&eglglessink->configured_caps, caps); + + /* By now the application should have set a window + * if it meant to do so + */ + GST_OBJECT_LOCK (eglglessink); + if (!eglglessink->have_window) { + + GST_INFO_OBJECT (eglglessink, + "No window. Will attempt internal window creation"); + if (eglglessink->window_width != 0 && eglglessink->window_height != 0) { + width = eglglessink->window_width; + height = eglglessink->window_height; + } else { + width = info.width; + height = info.height; + } + if (!gst_eglglessink_create_window (eglglessink, width, height)) { + GST_ERROR_OBJECT (eglglessink, "Internal window creation failed!"); + GST_OBJECT_UNLOCK (eglglessink); + goto HANDLE_ERROR; + } + eglglessink->using_own_window = TRUE; + eglglessink->have_window = TRUE; + } + GST_DEBUG_OBJECT (eglglessink, "Using window handle %p", + (gpointer) eglglessink->egl_context->window); + eglglessink->egl_context->used_window = eglglessink->egl_context->window; + GST_OBJECT_UNLOCK (eglglessink); + gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (eglglessink), + (uintptr_t) eglglessink->egl_context->used_window); + + if (!eglglessink->egl_context->have_surface) { + if (!gst_egl_adaptation_init_surface (eglglessink->egl_context, + eglglessink->configured_info.finfo->format)) { + GST_ERROR_OBJECT (eglglessink, "Couldn't init EGL surface from window"); + goto HANDLE_ERROR; + } + } + + gst_egl_adaptation_init_exts (eglglessink->egl_context); + + if (eglglessink->using_cuda) { + if (!gst_eglglessink_cuda_init(eglglessink)) { + GST_ERROR_OBJECT (eglglessink, "Cuda Init failed"); + goto HANDLE_ERROR; + } + } + +SUCCEED: + GST_INFO_OBJECT (eglglessink, "Configured caps successfully"); + return TRUE; + +HANDLE_ERROR: + GST_ERROR_OBJECT (eglglessink, "Configuring caps failed"); + return FALSE; +} + +static gboolean +gst_eglglessink_setcaps (GstBaseSink * bsink, GstCaps * caps) +{ + GstEglGlesSink *eglglessink; + GstVideoInfo info; + GstCapsFeatures *features; +#ifndef HAVE_IOS + GstBufferPool *newpool, *oldpool; + GstStructure *config; + GstAllocationParams params = { 0, }; +#endif + eglglessink = GST_EGLGLESSINK (bsink); + + GST_DEBUG_OBJECT (eglglessink, + "Current caps %" GST_PTR_FORMAT ", setting caps %" + GST_PTR_FORMAT, eglglessink->current_caps, caps); + + features = gst_caps_get_features(caps, 0); + if (gst_caps_features_contains(features, "memory:NVMM")) { + eglglessink->using_cuda = TRUE; + } + + if (eglglessink->is_reconfiguring) { + gst_data_queue_set_flushing (eglglessink->queue, FALSE); + eglglessink->last_flow = GST_FLOW_OK; + + g_mutex_lock (&eglglessink->render_lock); + g_cond_signal (&eglglessink->render_exit_cond); + g_mutex_unlock (&eglglessink->render_lock); + + eglglessink->display_region.w = 0; + eglglessink->display_region.h = 0; + } + eglglessink->is_reconfiguring = FALSE; + + if (gst_eglglessink_queue_object (eglglessink, + GST_MINI_OBJECT_CAST (caps)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (eglglessink, "Failed to configure caps"); + return FALSE; + } + + if (!gst_video_info_from_caps (&info, caps)) { + GST_ERROR_OBJECT (eglglessink, "Invalid caps %" GST_PTR_FORMAT, caps); + return FALSE; + } +#ifndef HAVE_IOS + if (!eglglessink->using_cuda) { + newpool = + gst_egl_image_buffer_pool_new + (gst_eglglessink_egl_image_buffer_pool_send_blocking, + gst_object_ref (eglglessink), + gst_eglglessink_egl_image_buffer_pool_on_destroy); + config = gst_buffer_pool_get_config (newpool); + /* we need at least 2 buffer because we hold on to the last one */ + gst_buffer_pool_config_set_params (config, caps, info.size, 2, 0); + gst_buffer_pool_config_set_allocator (config, NULL, ¶ms); + if (!gst_buffer_pool_set_config (newpool, config)) { + gst_object_unref (newpool); + GST_ERROR_OBJECT (eglglessink, "Failed to set buffer pool configuration"); + return FALSE; + } + + GST_OBJECT_LOCK (eglglessink); + oldpool = eglglessink->pool; + eglglessink->pool = newpool; + GST_OBJECT_UNLOCK (eglglessink); + + if (oldpool) + gst_object_unref (oldpool); + } +#endif + + gst_caps_replace (&eglglessink->current_caps, caps); + + return TRUE; +} + +static gboolean +gst_eglglessink_open (GstEglGlesSink * eglglessink) +{ + if (!egl_init (eglglessink)) { + return FALSE; + } + + if (eglglessink->profile) { + eglglessink->pDeliveryJitter = GstEglAllocJitterTool("frame delivery", 100); + GstEglJitterToolSetShow(eglglessink->pDeliveryJitter, 0 /*eglglessink->profile*/); + } + + return TRUE; +} + +static gboolean +gst_eglglessink_close (GstEglGlesSink * eglglessink) +{ + double fJitterAvg = 0, fJitterStd = 0, fJitterHighest = 0; + +#ifndef HAVE_IOS + + g_mutex_lock (&eglglessink->render_lock); + eglglessink->is_closing = TRUE; + g_mutex_unlock (&eglglessink->render_lock); + g_cond_broadcast (&eglglessink->render_exit_cond); + + if (eglglessink->thread) { + g_thread_join (eglglessink->thread); + eglglessink->thread = NULL; + } + + if (eglglessink->using_own_window) { + g_mutex_lock (&eglglessink->window_lock); + gst_egl_adaptation_destroy_native_window (eglglessink->egl_context, + &eglglessink->own_window_data, eglglessink->winsys); + eglglessink->have_window = FALSE; + g_mutex_unlock (&eglglessink->window_lock); + } + eglglessink->egl_context->used_window = 0; + + if (eglglessink->egl_context->display) { + gst_egl_display_unref (eglglessink->egl_context->display); + eglglessink->egl_context->display = NULL; + } + GST_OBJECT_LOCK (eglglessink); + if (eglglessink->pool) + gst_object_unref (eglglessink->pool); + eglglessink->pool = NULL; + GST_OBJECT_UNLOCK (eglglessink); +#endif + + if (eglglessink->profile) { + GstEglJitterToolGetAvgs(eglglessink->pDeliveryJitter, &fJitterStd, &fJitterAvg, &fJitterHighest); + printf("\n"); + printf("--------Jitter Statistics------------"); + printf("--------Average jitter = %f uSec \n", fJitterStd); + printf("--------Highest instantaneous jitter = %f uSec \n", fJitterHighest); + printf("--------Mean time between frame(used in jitter) = %f uSec \n", fJitterAvg); + printf("\n"); + + GstEglFreeJitterTool(eglglessink->pDeliveryJitter); + eglglessink->pDeliveryJitter = NULL; + } + + gst_caps_unref (eglglessink->sinkcaps); + eglglessink->sinkcaps = NULL; + eglglessink->egl_started = FALSE; + +#ifdef USE_EGL_X11 + if (g_strcmp0(eglglessink->winsys, "x11") == 0) { + if (eglglessink->event_thread) { + g_thread_join (eglglessink->event_thread); + } + } +#endif + if (GST_OBJECT_REFCOUNT(eglglessink)) + gst_object_unref (eglglessink); + return TRUE; +} + +static GstStateChangeReturn +gst_eglglessink_change_state (GstElement * element, GstStateChange transition) +{ + GstEglGlesSink *eglglessink; + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + eglglessink = GST_EGLGLESSINK (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_eglglessink_open (eglglessink)) { + ret = GST_STATE_CHANGE_FAILURE; + goto done; + } + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (!gst_eglglessink_start (eglglessink)) { + ret = GST_STATE_CHANGE_FAILURE; + goto done; + } + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + if (!gst_eglglessink_close (eglglessink)) { + ret = GST_STATE_CHANGE_FAILURE; + goto done; + } + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (!gst_eglglessink_stop (eglglessink)) { + ret = GST_STATE_CHANGE_FAILURE; + goto done; + } + break; + default: + break; + } + +done: + return ret; +} + +static void +gst_eglglessink_finalize (GObject * object) +{ + GstEglGlesSink *eglglessink; + + g_return_if_fail (GST_IS_EGLGLESSINK (object)); + + eglglessink = GST_EGLGLESSINK (object); + + if (eglglessink->queue) + g_object_unref (eglglessink->queue); + eglglessink->queue = NULL; + + g_mutex_clear (&eglglessink->window_lock); + g_cond_clear (&eglglessink->render_cond); + g_cond_clear (&eglglessink->render_exit_cond); + g_mutex_clear (&eglglessink->render_lock); + + gst_egl_adaptation_context_free (eglglessink->egl_context); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_eglglessink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstEglGlesSink *eglglessink; + + g_return_if_fail (GST_IS_EGLGLESSINK (object)); + + eglglessink = GST_EGLGLESSINK (object); + + switch (prop_id) { + case PROP_CREATE_WINDOW: + eglglessink->create_window = g_value_get_boolean (value); + break; + case PROP_DISPLAY: + eglglessink->display = g_value_get_pointer (value); + break; + case PROP_FORCE_ASPECT_RATIO: + eglglessink->force_aspect_ratio = g_value_get_boolean (value); + break; + case PROP_WINDOW_X: + eglglessink->window_x = g_value_get_uint (value); + break; + case PROP_WINDOW_Y: + eglglessink->window_y = g_value_get_uint (value); + break; + case PROP_WINDOW_WIDTH: + eglglessink->window_width = g_value_get_uint (value); + break; + case PROP_WINDOW_HEIGHT: + eglglessink->window_height = g_value_get_uint (value); + break; + case PROP_PROFILE: + eglglessink->profile = g_value_get_uint (value); + break; + case PROP_WINSYS: + eglglessink->winsys = g_strdup (g_value_get_string(value)); + break; + case PROP_ROWS: + eglglessink->rows = g_value_get_uint (value); + eglglessink->change_port = -1; + break; + case PROP_COLUMNS: + eglglessink->columns = g_value_get_uint (value); + eglglessink->change_port = -1; + break; +#ifdef IS_DESKTOP + case PROP_GPU_DEVICE_ID: + eglglessink->gpu_id = g_value_get_uint (value); + break; +#endif + case PROP_NVBUF_API_VERSION: + eglglessink->nvbuf_api_version_new = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_eglglessink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstEglGlesSink *eglglessink; + + g_return_if_fail (GST_IS_EGLGLESSINK (object)); + + eglglessink = GST_EGLGLESSINK (object); + + switch (prop_id) { + case PROP_CREATE_WINDOW: + g_value_set_boolean (value, eglglessink->create_window); + break; + case PROP_FORCE_ASPECT_RATIO: + g_value_set_boolean (value, eglglessink->force_aspect_ratio); + break; + case PROP_DISPLAY: + g_value_set_pointer (value, eglglessink->display); + break; + case PROP_WINDOW_X: + g_value_set_uint (value, eglglessink->window_x); + break; + case PROP_WINDOW_Y: + g_value_set_uint (value, eglglessink->window_y); + break; + case PROP_WINDOW_WIDTH: + g_value_set_uint (value, eglglessink->window_width); + break; + case PROP_WINDOW_HEIGHT: + g_value_set_uint (value, eglglessink->window_height); + break; + case PROP_PROFILE: + g_value_set_uint (value, eglglessink->profile); + break; + case PROP_WINSYS: + g_value_set_string (value, eglglessink->winsys); + break; + case PROP_ROWS: + g_value_set_uint (value, eglglessink->rows); + break; + case PROP_COLUMNS: + g_value_set_uint (value, eglglessink->columns); + break; +#ifdef IS_DESKTOP + case PROP_GPU_DEVICE_ID: + g_value_set_uint (value, eglglessink->gpu_id); + break; +#endif + case PROP_NVBUF_API_VERSION: + g_value_set_boolean (value, eglglessink->nvbuf_api_version_new); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_eglglessink_event (GstBaseSink * sink, GstEvent * event) +{ + GstEglGlesSink *eglglessink = GST_EGLGLESSINK (sink); + + if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START && + !(eglglessink->rows == 1 && eglglessink->columns == 1)) { + eglglessink->change_port++; + eglglessink->render_region_changed = TRUE; + } + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_DOWNSTREAM:{ + GstStructure *s; + GstQuery *query; + eglglessink->is_reconfiguring = TRUE; + + s = gst_structure_new_empty ("eglglessink-flush"); + query = gst_query_new_custom (GST_QUERY_CUSTOM, s); + gst_eglglessink_queue_object (eglglessink, GST_MINI_OBJECT_CAST (query)); + + eglglessink->last_flow = GST_FLOW_FLUSHING; + gst_data_queue_set_flushing (eglglessink->queue, TRUE); + + g_mutex_lock (&eglglessink->render_lock); + g_cond_broadcast (&eglglessink->render_cond); + g_mutex_unlock (&eglglessink->render_lock); + + if (gst_base_sink_is_last_sample_enabled (sink)) { + gst_base_sink_set_last_sample_enabled (sink, FALSE); + gst_base_sink_set_last_sample_enabled (sink, TRUE); + } +#ifndef HAVE_IOS + if (eglglessink->pool) + gst_egl_image_buffer_pool_replace_last_buffer (GST_EGL_IMAGE_BUFFER_POOL + (eglglessink->pool), NULL); +#endif + break; + } + default: + break; + } + return GST_BASE_SINK_CLASS (parent_class)->event (sink, event); +} + +/* initialize the eglglessink's class */ +static void +gst_eglglessink_class_init (GstEglGlesSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + GstVideoSinkClass *gstvideosink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + gstvideosink_class = (GstVideoSinkClass *) klass; + + gobject_class->set_property = gst_eglglessink_set_property; + gobject_class->get_property = gst_eglglessink_get_property; + gobject_class->finalize = gst_eglglessink_finalize; + + gstelement_class->change_state = gst_eglglessink_change_state; + gstelement_class->set_context = gst_eglglessink_set_context; + + gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_setcaps); + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_eglglessink_getcaps); + gstbasesink_class->propose_allocation = + GST_DEBUG_FUNCPTR (gst_eglglessink_propose_allocation); + gstbasesink_class->prepare = GST_DEBUG_FUNCPTR (gst_eglglessink_prepare); + gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_eglglessink_query); + gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_eglglessink_event); + + gstvideosink_class->show_frame = + GST_DEBUG_FUNCPTR (gst_eglglessink_show_frame); + + g_object_class_install_property (gobject_class, PROP_WINSYS, + g_param_spec_string ("winsys", "Windowing System", + "Takes in strings \"x11\" or \"wayland\" to specify the windowing system to be used", + "x11", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_CREATE_WINDOW, + g_param_spec_boolean ("create-window", "Create Window", + "If set to true, the sink will attempt to create it's own window to " + "render to if none is provided. This is currently only supported " + "when the sink is used under X11", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, + g_param_spec_boolean ("force-aspect-ratio", + "Respect aspect ratio when scaling", + "If set to true, the sink will attempt to preserve the incoming " + "frame's geometry while scaling, taking both the storage's and " + "display's pixel aspect ratio into account", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DISPLAY, + g_param_spec_pointer ("display", + "Set X Display to be used", + "If set, the sink will use the passed X Display for rendering", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_WINDOW_X, + g_param_spec_uint ("window-x", + "Window x coordinate", + "X coordinate of window", 0, G_MAXINT, 10, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_WINDOW_Y, + g_param_spec_uint ("window-y", + "Window y coordinate", + "Y coordinate of window", 0, G_MAXINT, 10, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH, + g_param_spec_uint ("window-width", + "Window width", + "Width of window", 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT, + g_param_spec_uint ("window-height", + "Window height", + "Height of window", 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PROFILE, + g_param_spec_uint ("profile", + "profile", + "gsteglglessink jitter information", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ROWS, + g_param_spec_uint ("rows", + "Display rows", + "Rows of Display", 1, G_MAXINT, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_COLUMNS, + g_param_spec_uint ("columns", + "Display columns", + "Columns of display", 1, G_MAXINT, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +#ifdef IS_DESKTOP + g_object_class_install_property (gobject_class, PROP_GPU_DEVICE_ID, + g_param_spec_uint ("gpu-id", "Set GPU Device ID", + "Set GPU Device ID", + 0, G_MAXUINT, DEFAULT_GPU_ID, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); +#endif + + gst_element_class_set_static_metadata (gstelement_class, + "EGL/GLES vout Sink", + "Sink/Video", + "An EGL/GLES Video Output Sink Implementing the VideoOverlay interface", + "Reynaldo H. Verdejo Pinochet , " + "Sebastian Dröge "); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_eglglessink_sink_template_factory)); + g_object_class_install_property (gobject_class, PROP_NVBUF_API_VERSION, + g_param_spec_boolean ("bufapi-version", + "Use new buf API", + "Set to use new buf API", + DEFAULT_NVBUF_API_VERSION_NEW, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static gboolean +queue_check_full_func (GstDataQueue * queue, guint visible, guint bytes, + guint64 time, gpointer checkdata) +{ + return visible != 0; +} + +static void +gst_eglglessink_init (GstEglGlesSink * eglglessink) +{ + eglglessink->egl_context = + gst_egl_adaptation_context_new (GST_ELEMENT_CAST (eglglessink)); + + /* Init defaults */ + + /** Flags */ + eglglessink->have_window = FALSE; + eglglessink->egl_context->have_surface = FALSE; + eglglessink->egl_context->have_vbo = FALSE; + eglglessink->egl_context->have_texture = FALSE; + eglglessink->egl_started = FALSE; + eglglessink->using_own_window = FALSE; + eglglessink->using_cuda = FALSE; + eglglessink->nvbuf_api_version_new = DEFAULT_NVBUF_API_VERSION_NEW; + + /** Props */ + g_mutex_init (&eglglessink->window_lock); + eglglessink->create_window = TRUE; + eglglessink->display = EGL_NO_DISPLAY; + eglglessink->force_aspect_ratio = TRUE; + eglglessink->winsys = "x11"; + + g_mutex_init (&eglglessink->render_lock); + g_cond_init (&eglglessink->render_cond); + g_cond_init (&eglglessink->render_exit_cond); + eglglessink->queue = + gst_data_queue_new (queue_check_full_func, NULL, NULL, NULL); + eglglessink->last_flow = GST_FLOW_FLUSHING; + + eglglessink->render_region.x = 0; + eglglessink->render_region.y = 0; + eglglessink->render_region.w = -1; + eglglessink->render_region.h = -1; + eglglessink->render_region_changed = TRUE; + eglglessink->render_region_user = FALSE; + eglglessink->window_x = 10; + eglglessink->window_y = 10; + eglglessink->window_width = 0; + eglglessink->window_height = 0; + eglglessink->profile = 0; + eglglessink->rows = 1; + eglglessink->columns = 1; + eglglessink->cuContext = NULL; + eglglessink->cuResource[0] = NULL; + eglglessink->cuResource[1] = NULL; + eglglessink->cuResource[2] = NULL; + eglglessink->gpu_id = 0; + +} + +#ifndef HAVE_IOS +static GstBufferPool * +gst_egl_image_buffer_pool_new (GstEGLImageBufferPoolSendBlockingAllocate + blocking_allocate_func, gpointer blocking_allocate_data, + GDestroyNotify destroy_func) +{ + GstEGLImageBufferPool *pool; + + pool = g_object_new (gst_egl_image_buffer_pool_get_type (), NULL); + pool->last_buffer = NULL; + pool->send_blocking_allocate_func = blocking_allocate_func; + pool->send_blocking_allocate_data = blocking_allocate_data; + pool->send_blocking_allocate_destroy = destroy_func; + + return (GstBufferPool *) pool; +} +#endif + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +static gboolean +#ifdef IS_DESKTOP +nvdsgst_eglglessink_plugin_init (GstPlugin * plugin) +#else +eglglessink_plugin_init (GstPlugin * plugin) +#endif +{ + /* debug category for fltering log messages */ + GST_DEBUG_CATEGORY_INIT (gst_eglglessink_debug, "nveglglessink", + 0, "Simple EGL/GLES Sink"); + + gst_egl_adaption_init (); + +#ifdef USE_EGL_RPI + GST_DEBUG ("Initialize BCM host"); + bcm_host_init (); +#endif + + return gst_element_register (plugin, "nveglglessink", GST_RANK_SECONDARY, + GST_TYPE_EGLGLESSINK); +} + +/* gstreamer looks for this structure to register eglglessinks */ +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, +#ifdef IS_DESKTOP + nvdsgst_eglglessink, +#else + nveglglessink, +#endif + "EGL/GLES sink", +#ifdef IS_DESKTOP + nvdsgst_eglglessink_plugin_init, + DS_VERSION, +#else + eglglessink_plugin_init, + VERSION, +#endif + GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gstegl_src/gst-egl/ext/eglgles/gsteglglessink.h b/gstegl_src/gst-egl/ext/eglgles/gsteglglessink.h new file mode 100644 index 0000000..058b0e6 --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/gsteglglessink.h @@ -0,0 +1,166 @@ +/* + * GStreamer EGL/GLES Sink + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * @author: Sebastian Dröge + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __GST_EGLGLESSINK_H__ +#define __GST_EGLGLESSINK_H__ + +#include +#include +#include +#include + +#include +#include +#include + +#include "gstegladaptation.h" +#include "gstegljitter.h" + +G_BEGIN_DECLS +#define GST_TYPE_EGLGLESSINK \ + (gst_eglglessink_get_type()) +#define GST_EGLGLESSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_EGLGLESSINK,GstEglGlesSink)) +#define GST_EGLGLESSINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_EGLGLESSINK,GstEglGlesSinkClass)) +#define GST_IS_EGLGLESSINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_EGLGLESSINK)) +#define GST_IS_EGLGLESSINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_EGLGLESSINK)) + +typedef struct _GstEglGlesSink GstEglGlesSink; +typedef struct _GstEglGlesSinkClass GstEglGlesSinkClass; + +/* + * GstEglGlesSink: + * @format: Caps' video format field + * @display_region: Surface region to use as rendering canvas + * @sinkcaps: Full set of suported caps + * @current_caps: Current caps + * @rendering_path: Rendering path (Slow/Fast) + * @eglglesctx: Pointer to the associated EGL/GLESv2 rendering context + * @flow_lock: Simple concurrent access ward to the sink's runtime state + * @have_window: Set if the sink has access to a window to hold it's canvas + * @using_own_window: Set if the sink created its own window + * @egl_started: Set if the whole EGL setup has been performed + * @create_window: Property value holder to allow/forbid internal window creation + * @force_rendering_slow: Property value holder to force slow rendering path + * @force_aspect_ratio: Property value holder to consider PAR/DAR when scaling + * + * The #GstEglGlesSink data structure. + */ +struct _GstEglGlesSink +{ + GstVideoSink videosink; /* Element hook */ + + /* Region of the surface that should be rendered */ + GstVideoRectangle render_region; + gboolean render_region_changed; + gboolean render_region_user; + + /* Region of render_region that should be filled + * with the video frames */ + GstVideoRectangle display_region; + + GstVideoRectangle crop; + gboolean crop_changed; + GstCaps *sinkcaps; + GstCaps *current_caps, *configured_caps; + GstVideoInfo configured_info; + gfloat stride[3]; + GstVideoGLTextureOrientation orientation; +#ifndef HAVE_IOS + GstBufferPool *pool; +#endif + + GstEglAdaptationContext *egl_context; + gint window_x; + gint window_y; + gint window_width; + gint window_height; + guint profile; + gint rows; + gint columns; + gint change_port; + + /* Runtime flags */ + gboolean have_window; + gboolean using_own_window; + gboolean egl_started; + gboolean is_reconfiguring; + gboolean is_closing; + gboolean using_cuda; + + gpointer own_window_data; + GMutex window_lock; + + GThread *thread; + gboolean thread_running; + GstDataQueue *queue; + GCond render_exit_cond; + GCond render_cond; + GMutex render_lock; + GstFlowReturn last_flow; + GstMiniObject *dequeued_object; + GThread *event_thread; + + GstBuffer *last_buffer; + + EGLNativeDisplayType display; + + GstEglJitterTool *pDeliveryJitter; + + /* Properties */ + gboolean create_window; + gboolean force_aspect_ratio; + gchar* winsys; + + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; + + GstBuffer *last_uploaded_buffer; + CUcontext cuContext; + CUgraphicsResource cuResource[3]; + unsigned int gpu_id; + gboolean nvbuf_api_version_new; + + /* + Pointer to a SW Buffer. This is needed in case of RGB/BGR as Cuda + doesn't support 3-channel formats. The cuda buffer (host or device) + is copied using cuMemCpy2D into this sw buffer and then fill the GL + texture from the SW buffer. + */ + uint8_t *swData; +}; + +struct _GstEglGlesSinkClass +{ + GstVideoSinkClass parent_class; +}; + +GType gst_eglglessink_get_type (void); + +G_END_DECLS +#endif /* __GST_EGLGLESSINK_H__ */ diff --git a/gstegl_src/gst-egl/ext/eglgles/gstegljitter.c b/gstegl_src/gst-egl/ext/eglgles/gstegljitter.c new file mode 100644 index 0000000..7b1b083 --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/gstegljitter.c @@ -0,0 +1,158 @@ +/* Copyright (c) 2008 NVIDIA Corporation. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an + * express license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#include +#include +#include +#include +#include +#include + +#include "gstegljitter.h" + +GstEglJitterTool *GstEglAllocJitterTool(const char *pName, guint nTicks) +{ + GstEglJitterTool *pTool; + + assert(pName); + assert(nTicks > 0); + + pTool = malloc(sizeof(GstEglJitterTool)); + if (!pTool) + return NULL; + + memset(pTool, 0, sizeof(GstEglJitterTool)); + pTool->pName = malloc(strlen(pName) + 1); + if (!pTool->pName) + { + free(pTool); + return NULL; + } + memcpy((char *)pTool->pName, pName, strlen(pName) + 1); + + pTool->pTicks = malloc(sizeof(guint64) * nTicks); + if (!pTool->pTicks) + { + free(pTool->pName); + free(pTool); + return NULL; + } + + pTool->nTicksMax = nTicks; + pTool->nTickCount = 0; + pTool->nLastTime = 0; + pTool->bShow = 0; + + return pTool; +} + +void GstEglFreeJitterTool(GstEglJitterTool *pTool) +{ + if (!pTool) + return; + free(pTool->pName); + free(pTool->pTicks); + free(pTool); +} + +void GstEglJitterToolAddPoint(GstEglJitterTool *pTool) +{ + guint64 now; + assert(pTool); + + static guint64 s_oldtime = 0; + static guint64 s_timedelta = 0; + struct timeval tv; + guint64 time; + + (void)gettimeofday( &tv, 0 ); + time = (guint64)( ( (guint64)tv.tv_sec * 1000 * 1000 ) + (guint64)tv.tv_usec ); + if (time < s_oldtime) + s_timedelta += (s_oldtime - time); + s_oldtime = time; + time += s_timedelta; + + now = time; + + if (pTool->nLastTime == 0) + { + pTool->nLastTime = now; + return; + } + + pTool->pTicks[pTool->nTickCount] = now - pTool->nLastTime; + pTool->nLastTime = now; + pTool->nTickCount++; + + if (pTool->nTickCount < pTool->nTicksMax) + return; + + { + double fAvg = 0; + double fStdDev = 0; + guint i; + + for (i = 0; i < pTool->nTicksMax; i++) + fAvg += pTool->pTicks[i]; + fAvg /= pTool->nTicksMax; + + for (i = 0; i < pTool->nTicksMax; i++) + fStdDev += (fAvg - pTool->pTicks[i]) * (fAvg - pTool->pTicks[i]); + fStdDev = sqrt(fStdDev / (pTool->nTicksMax - 1)); + + if (pTool->bShow) + printf("%s: mean: %.2f std. dev: %.2f\n", pTool->pName, + fAvg, fStdDev); + + if (pTool->nPos < MAX_JITTER_HISTORY) + { + pTool->fAvg[pTool->nPos] = fAvg; + pTool->fStdDev[pTool->nPos] = fStdDev; + pTool->nPos++; + } + } + + pTool->nTickCount = 0; +} + +void GstEglJitterToolSetShow(GstEglJitterTool *pTool, gboolean bShow) +{ + pTool->bShow = bShow; +} + +void GstEglJitterToolGetAvgs(GstEglJitterTool *pTool, double *pStdDev, double *pAvg, + double *pHighest) +{ + guint i; + + assert(pTool); + assert(pStdDev); + assert(pAvg); + assert(pHighest); + + *pStdDev = 0; + *pAvg = 0; + *pHighest = 0; + + if (pTool->nPos < 1) + return; + + for (i = 1; i < pTool->nPos && i < MAX_JITTER_HISTORY; i++) + { + *pAvg = *pAvg + pTool->fAvg[i]; + *pStdDev = *pStdDev + pTool->fStdDev[i]; + + if (pTool->fStdDev[i] > *pHighest) + *pHighest = pTool->fStdDev[i]; + } + + *pAvg = *pAvg / (pTool->nPos); + *pStdDev = *pStdDev / (pTool->nPos); +} + diff --git a/gstegl_src/gst-egl/ext/eglgles/gstegljitter.h b/gstegl_src/gst-egl/ext/eglgles/gstegljitter.h new file mode 100644 index 0000000..b206bb9 --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/gstegljitter.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2008 NVIDIA Corporation. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an + * express license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef NVXJITTER_H_ +#define NVXJITTER_H_ + +#include +#include + +typedef struct GstEglJitterTool +{ + gchar *pName; + guint64 *pTicks; + guint nTicksMax; + guint nTickCount; + + guint64 nLastTime; + + gboolean bShow; + +#define MAX_JITTER_HISTORY 3000 + double fAvg[MAX_JITTER_HISTORY]; + double fStdDev[MAX_JITTER_HISTORY]; + guint nPos; +} GstEglJitterTool; + +GstEglJitterTool *GstEglAllocJitterTool(const char *pName, guint nTicks); +void GstEglFreeJitterTool(GstEglJitterTool *pTool); + +void GstEglJitterToolAddPoint(GstEglJitterTool *pTool); +void GstEglJitterToolSetShow(GstEglJitterTool *pTool, gboolean bShow); + +void GstEglJitterToolGetAvgs(GstEglJitterTool *pTool, double *pStdDev, double *pAvg, + double *pHighest); + +#endif + diff --git a/gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.c b/gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.c new file mode 100644 index 0000000..739e8db --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.c @@ -0,0 +1,834 @@ +/* + * GStreamer Android Video Platform Wrapper + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined (USE_EGL_RPI) && defined(__GNUC__) +#ifndef __VCCOREVER__ +#define __VCCOREVER__ 0x04000000 +#endif + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC optimize ("gnu89-inline") +#endif + +#define EGL_EGLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#include +#include +#include +#include + +#if defined (USE_EGL_RPI) && defined(__GNUC__) +#pragma GCC reset_options +#pragma GCC diagnostic pop +#endif + +#include + +#include +#include "video_platform_wrapper.h" + +GST_DEBUG_CATEGORY_STATIC (eglgles_platform_wrapper); +#define GST_CAT_DEFAULT eglgles_platform_wrapper + +/* XXX: Likely to be removed */ +gboolean +platform_wrapper_init (void) +{ + GST_DEBUG_CATEGORY_INIT (eglgles_platform_wrapper, + "eglglessink-platform", 0, + "Platform dependent native-window utility routines for EglGles"); + return TRUE; +} + +#ifdef USE_EGL_X11 +#include +#include + +EGLNativeWindowType +platform_create_native_window_x11 (gint x, gint y, gint width, gint height, gpointer * window_data) +{ + Display *d; + Window w; + int s; + X11WindowData *data; + const gchar *app_name; + XSizeHints hints = {0}; + hints.flags = PPosition ; + hints.x = x; + hints.y = y; + + d = XOpenDisplay (NULL); + if (d == NULL) { + GST_ERROR ("Can't open X11 display"); + return (EGLNativeWindowType) 0; + } + + s = DefaultScreen (d); + w = XCreateSimpleWindow (d, RootWindow (d, s), hints.x, hints.y, width, height, 1, + BlackPixel (d, s), WhitePixel (d, s)); + + /* Prevent X from redrawing the background on ConfigureNotify. + Otherwise flickering is observed when resizing the window. */ + XSetWindowBackgroundPixmap (d, w, None); + + /* set application name as a title */ + app_name = g_get_application_name (); + if (!app_name) + app_name = "eglglessink"; + XStoreName (d, w, app_name); + + XSetNormalHints(d, w, &hints); + XMapWindow (d, w); + + Atom wmDeleteMessage = XInternAtom (d, "WM_DELETE_WINDOW", False); + if (wmDeleteMessage != None) { + XSetWMProtocols (d, w, &wmDeleteMessage, 1); + } + + XFlush (d); + + *window_data = data = g_slice_new0 (X11WindowData); + data->display = d; + + return (EGLNativeWindowType) w; +} + +gboolean +platform_destroy_native_window_x11 (EGLNativeDisplayType display, + EGLNativeWindowType window, gpointer * window_data) +{ + X11WindowData *data = *window_data; + + /* XXX: Should proly catch BadWindow */ + XDestroyWindow (data->display, (Window) window); + XSync (data->display, FALSE); + XCloseDisplay (data->display); + + g_slice_free (X11WindowData, data); + *window_data = NULL; + return TRUE; +} +#endif + +#if USE_EGL_WAYLAND + +WaylandDisplay *wayland_display; +volatile gint instance_count = 0; + +void global_registry_handler (void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version); +void global_registry_remover (void *data, struct wl_registry *registry, uint32_t id); + +void global_registry_handler (void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version) +{ + if (strcmp (interface, "wl_compositor") == 0) { + wayland_display->compositor = wl_registry_bind (registry, + id, + &wl_compositor_interface, + 1); + } + else if (strcmp (interface, "wl_shell") == 0) { + wayland_display->shell = wl_registry_bind (registry, id, + &wl_shell_interface, 1); + } +} + +void global_registry_remover (void *data, struct wl_registry *registry, uint32_t id) +{ +} + +const struct wl_registry_listener registry_listener = { + global_registry_handler, + global_registry_remover +}; + +EGLNativeDisplayType platform_initialize_display_wayland (void) +{ + wayland_display = g_slice_new0 (WaylandDisplay); + wayland_display->display = wl_display_connect (NULL); + if (wayland_display->display == NULL) { + GST_ERROR ("Can't connect to display"); + return NULL; + } + + wayland_display->registry = wl_display_get_registry (wayland_display->display); + + wl_registry_add_listener (wayland_display->registry, ®istry_listener, NULL); + + wl_display_dispatch (wayland_display->display); + wl_display_roundtrip (wayland_display->display); + + if (wayland_display->compositor == NULL || wayland_display->shell == NULL) { + GST_ERROR ("Can't find compositor or shell"); + return NULL; + } + + return (EGLNativeDisplayType) wayland_display->display; +} + +EGLNativeWindowType +platform_create_native_window_wayland (gint x, gint y, gint width, gint height, gpointer * window_data) +{ + WaylandWindowData *data; + *window_data = data = g_slice_new0 (WaylandWindowData); + + data->surface = wl_compositor_create_surface (wayland_display->compositor); + if (data->surface == NULL) { + GST_ERROR ("Can't create surface"); + return (EGLNativeWindowType) 0; + } + + data->shell_surface = wl_shell_get_shell_surface (wayland_display->shell, data->surface); + wl_shell_surface_set_toplevel (data->shell_surface); + + data->egl_window = wl_egl_window_create (data->surface, width, height); + + g_atomic_int_add(&instance_count, 1); + + return (EGLNativeWindowType) data->egl_window; +} + +gboolean platform_destroy_display_wayland (void) +{ + if (g_atomic_int_compare_and_exchange(&instance_count, 0, 0)) { + if (wayland_display->compositor) { + wl_compositor_destroy (wayland_display->compositor); + wayland_display->compositor = NULL; + } + if (wayland_display->shell) { + wayland_display->shell = NULL; + } + if (wayland_display->registry) { + wl_registry_destroy (wayland_display->registry); + wayland_display->registry = NULL; + } + if (wayland_display->display) { + wl_display_flush (wayland_display->display); + wl_display_disconnect (wayland_display->display); + wayland_display->display = NULL; + } + + return TRUE; + } + return FALSE; +} + +gboolean platform_destroy_native_window_wayland (EGLNativeDisplayType display, + EGLNativeWindowType window, gpointer * window_data) +{ + WaylandWindowData *data = *window_data; + + if (data->egl_window) { + data->egl_window = NULL; + } + + if (data->shell_surface) { + data->shell_surface = NULL; + } + + if (data->surface) { + data->surface = NULL; + } + + g_atomic_int_add(&instance_count, -1); + + return TRUE; +} + +#endif + + +#ifdef USE_EGL_MALI_FB +#include + +EGLNativeWindowType +platform_create_native_window (gint x, gint y, gint width, gint height, gpointer * window_data) +{ + fbdev_window *w = g_slice_new0 (fbdev_window); + + w->width = width; + w->height = height; + + return (EGLNativeWindowType) w; +} + +gboolean +platform_destroy_native_window (EGLNativeDisplayType display, + EGLNativeWindowType window, gpointer * window_data) +{ + g_slice_free (fbdev_window, ((fbdev_window *) window)); + + return TRUE; +} + +/* FIXME: Move to gst-libs/gst/egl */ +#if 0 +#include +#include +#include +#include +static gpointer +eglimage_map (GstMemory * gmem, gsize maxsize, GstMapFlags flags) +{ + GstEGLImageMemory *mem; + gint i; + + g_return_val_if_fail (strcmp (gmem->allocator->mem_type, + GST_EGL_IMAGE_MEMORY_NAME) == 0, FALSE); + + mem = GST_EGL_IMAGE_MEMORY (gmem); + + g_mutex_lock (&mem->lock); + for (i = 0; i < mem->n_textures; i++) { + if (mem->memory_refcount[i]) { + /* Only multiple READ maps are allowed */ + if ((mem->memory_flags[i] & GST_MAP_WRITE)) { + g_mutex_unlock (&mem->lock); + return NULL; + } + } + } + + if (!mem->mapped_memory_refcount) { + EGLint attribs[] = { + MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y, + MALI_EGL_IMAGE_ACCESS_MODE, MALI_EGL_IMAGE_ACCESS_READ_ONLY, + EGL_NONE + }; + GstVideoInfo info; + mali_egl_image *mali_egl_image; + guint8 *plane_memory, *p; + gint stride, h; + gint j; + + gst_video_info_set_format (&info, mem->format, mem->width, mem->height); + + mem->mapped_memory = g_malloc (mem->parent.size); + + for (i = 0; i < mem->n_textures; i++) { + mali_egl_image = mali_egl_image_lock_ptr (mem->image[i]); + if (!mali_egl_image) { + g_free (mem->mapped_memory); + GST_ERROR ("Failed to lock Mali EGL image: 0x%04x", + mali_egl_image_get_error ()); + g_mutex_unlock (&mem->lock); + return NULL; + } + plane_memory = mali_egl_image_map_buffer (mali_egl_image, attribs); + if (!plane_memory) { + mali_egl_image_unlock_ptr (mem->image[i]); + g_free (mem->mapped_memory); + GST_ERROR ("Failed to lock Mali map image: 0x%04x", + mali_egl_image_get_error ()); + g_mutex_unlock (&mem->lock); + return NULL; + } + + p = ((guint8 *) mem->mapped_memory) + mem->offset[i]; + stride = mem->stride[i]; + h = GST_VIDEO_INFO_COMP_HEIGHT (&info, i); + for (j = 0; j < h; j++) { + memcpy (p, plane_memory, stride); + p += mem->stride[i]; + plane_memory += mem->stride[i]; + } + + mali_egl_image_unmap_buffer (mem->image[i], attribs); + mali_egl_image_unlock_ptr (mem->image[i]); + } + } else { + /* Only multiple READ maps are allowed */ + if ((mem->mapped_memory_flags & GST_MAP_WRITE)) { + g_mutex_unlock (&mem->lock); + return NULL; + } + } + mem->mapped_memory_refcount++; + + g_mutex_unlock (&mem->lock); + + return mem->mapped_memory; +} + +static void +eglimage_unmap (GstMemory * gmem) +{ + GstEGLImageMemory *mem; + gint i; + + g_return_if_fail (strcmp (gmem->allocator->mem_type, + GST_EGL_IMAGE_MEMORY_NAME) == 0); + + mem = GST_EGL_IMAGE_MEMORY (gmem); + g_return_if_fail (mem->mapped_memory); + + g_mutex_lock (&mem->lock); + + mem->mapped_memory_refcount--; + if (mem->mapped_memory_refcount > 0) { + g_mutex_unlock (&mem->lock); + return; + } + + /* Write back */ + if ((mem->mapped_memory_flags & GST_MAP_WRITE)) { + EGLint attribs[] = { + MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y, + MALI_EGL_IMAGE_ACCESS_MODE, MALI_EGL_IMAGE_ACCESS_WRITE_ONLY, + EGL_NONE + }; + GstVideoInfo info; + mali_egl_image *mali_egl_image; + guint8 *plane_memory, *p; + gint stride, h; + gint j; + + gst_video_info_set_format (&info, mem->format, mem->width, mem->height); + + for (i = 0; i < mem->n_textures; i++) { + mali_egl_image = mali_egl_image_lock_ptr (mem->image[i]); + if (!mali_egl_image) { + g_free (mem->mapped_memory); + GST_ERROR ("Failed to lock Mali EGL image: 0x%04x", + mali_egl_image_get_error ()); + g_mutex_unlock (&mem->lock); + return; + } + plane_memory = mali_egl_image_map_buffer (mali_egl_image, attribs); + if (!plane_memory) { + mali_egl_image_unlock_ptr (mem->image[i]); + g_free (mem->mapped_memory); + GST_ERROR ("Failed to lock Mali map image: 0x%04x", + mali_egl_image_get_error ()); + g_mutex_unlock (&mem->lock); + return; + } + + p = ((guint8 *) mem->mapped_memory) + mem->offset[i]; + stride = mem->stride[i]; + h = GST_VIDEO_INFO_COMP_HEIGHT (&info, i); + for (j = 0; j < h; j++) { + memcpy (plane_memory, p, stride); + p += mem->stride[i]; + plane_memory += mem->stride[i]; + } + + mali_egl_image_unmap_buffer (mem->image[i], attribs); + mali_egl_image_unlock_ptr (mem->image[i]); + } + } + g_free (mem->mapped_memory); + + g_mutex_unlock (&mem->lock); +} + +static gboolean +eglimage_video_map (GstVideoMeta * meta, guint plane, + GstMapInfo * info, gpointer * data, gint * stride, GstMapFlags flags) +{ + GstMemory *gmem; + GstEGLImageMemory *mem; + GstVideoInfo vinfo; + + if (gst_buffer_n_memory (meta->buffer) != 1) + return default_map_video (meta, plane, info, data, stride, flags); + + gmem = gst_buffer_peek_memory (meta->buffer, 0); + if (strcmp (gmem->allocator->mem_type, GST_EGL_IMAGE_MEMORY_NAME) != 0) + return default_map_video (meta, plane, info, data, stride, flags); + + mem = GST_EGL_IMAGE_MEMORY ((gmem->parent ? gmem->parent : gmem)); + + g_mutex_lock (&mem->lock); + if (mem->format == GST_VIDEO_FORMAT_YV12) { + if (plane == 1) + plane = 2; + else if (plane == 2) + plane = 1; + } + + if (mem->mapped_memory_refcount) { + /* Only multiple READ maps are allowed */ + if ((mem->mapped_memory_flags & GST_MAP_WRITE)) { + g_mutex_unlock (&mem->lock); + return FALSE; + } + } + + if (!mem->memory_refcount[plane]) { + EGLint attribs[] = { + MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y, + MALI_EGL_IMAGE_ACCESS_MODE, MALI_EGL_IMAGE_ACCESS_READ_WRITE, + EGL_NONE + }; + + if ((flags & GST_MAP_READ) && (flags & GST_MAP_WRITE)) + attribs[3] = MALI_EGL_IMAGE_ACCESS_READ_WRITE; + else if ((flags & GST_MAP_READ)) + attribs[3] = MALI_EGL_IMAGE_ACCESS_READ_ONLY; + else if ((flags & GST_MAP_WRITE)) + attribs[3] = MALI_EGL_IMAGE_ACCESS_WRITE_ONLY; + + mem->memory_platform_data[plane] = + mali_egl_image_lock_ptr (mem->image[plane]); + if (!mem->memory_platform_data[plane]) { + GST_ERROR ("Failed to lock Mali EGL image: 0x%04x", + mali_egl_image_get_error ()); + goto map_error; + } + + mem->memory[plane] = + mali_egl_image_map_buffer (mem->memory_platform_data[plane], attribs); + if (!mem->memory[plane]) + goto map_error; + + mem->memory_flags[plane] = flags; + } else { + /* Only multiple READ maps are allowed */ + if ((mem->memory_flags[plane] & GST_MAP_WRITE)) { + g_mutex_unlock (&mem->lock); + return FALSE; + } + } + + mem->memory_refcount[plane]++; + gst_video_info_set_format (&vinfo, mem->format, mem->width, mem->height); + + *data = mem->memory[plane]; + *stride = mem->stride[plane]; + + g_mutex_unlock (&mem->lock); + return TRUE; + +map_error: + { + EGLint attribs[] = { + MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y, + EGL_NONE + }; + GST_ERROR ("Failed to map Mali EGL image: 0x%04x", + mali_egl_image_get_error ()); + + if (mem->memory_platform_data[plane]) { + mali_egl_image_unmap_buffer (mem->image[plane], attribs); + mali_egl_image_unlock_ptr (mem->image[plane]); + } + mem->memory[plane] = NULL; + mem->memory_platform_data[plane] = NULL; + + g_mutex_unlock (&mem->lock); + + return FALSE; + } +} + +static gboolean +eglimage_video_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info) +{ + GstMemory *gmem; + GstEGLImageMemory *mem; + EGLint attribs[] = { + MALI_EGL_IMAGE_PLANE, MALI_EGL_IMAGE_PLANE_Y, + EGL_NONE + }; + + if (gst_buffer_n_memory (meta->buffer) != 1) + return default_unmap_video (meta, plane, info); + + gmem = gst_buffer_peek_memory (meta->buffer, 0); + if (strcmp (gmem->allocator->mem_type, GST_EGL_IMAGE_MEMORY_NAME) != 0) + return default_unmap_video (meta, plane, info); + + mem = GST_EGL_IMAGE_MEMORY ((gmem->parent ? gmem->parent : gmem)); + + g_mutex_lock (&mem->lock); + if (mem->format == GST_VIDEO_FORMAT_YV12) { + if (plane == 1) + plane = 2; + else if (plane == 2) + plane = 1; + } + + if (!mem->memory_refcount[plane]) { + g_mutex_unlock (&mem->lock); + g_return_val_if_reached (FALSE); + } + + mem->memory_refcount[plane]--; + if (mem->memory_refcount[plane] > 0) { + g_mutex_unlock (&mem->lock); + return TRUE; + } + + /* Unmaps automatically */ + if (mem->memory_platform_data[plane]) { + mali_egl_image_unmap_buffer (mem->image[plane], attribs); + mali_egl_image_unlock_ptr (mem->image[plane]); + } + mem->memory[plane] = NULL; + mem->memory_platform_data[plane] = NULL; + + g_mutex_unlock (&mem->lock); + + return TRUE; +} + +gboolean +platform_can_map_eglimage (GstMemoryMapFunction * map, + GstMemoryUnmapFunction * unmap, PlatformMapVideo * video_map, + PlatformUnmapVideo * video_unmap) +{ + *map = eglimage_map; + *unmap = eglimage_unmap; + *video_map = eglimage_video_map; + *video_unmap = eglimage_video_unmap; + return TRUE; +} + +gboolean +platform_has_custom_eglimage_alloc (void) +{ + return TRUE; +} + +gboolean +platform_alloc_eglimage (EGLDisplay display, EGLContext context, GLint format, + GLint type, gint width, gint height, GLuint tex_id, EGLImageKHR * image, + gpointer * image_platform_data) +{ + fbdev_pixmap pixmap; + + pixmap.flags = FBDEV_PIXMAP_SUPPORTS_UMP; + pixmap.width = width; + pixmap.height = height; + + switch (format) { + case GL_LUMINANCE: + g_return_val_if_fail (type == GL_UNSIGNED_BYTE, FALSE); + pixmap.red_size = 0; + pixmap.green_size = 0; + pixmap.blue_size = 0; + pixmap.alpha_size = 0; + pixmap.luminance_size = 8; + break; + case GL_LUMINANCE_ALPHA: + g_return_val_if_fail (type == GL_UNSIGNED_BYTE, FALSE); + pixmap.red_size = 0; + pixmap.green_size = 0; + pixmap.blue_size = 0; + pixmap.alpha_size = 8; + pixmap.luminance_size = 8; + break; + case GL_RGB: + if (type == GL_UNSIGNED_BYTE) { + pixmap.red_size = 8; + pixmap.green_size = 8; + pixmap.blue_size = 8; + pixmap.alpha_size = 0; + pixmap.luminance_size = 0; + } else if (type == GL_UNSIGNED_SHORT_5_6_5) { + pixmap.red_size = 5; + pixmap.green_size = 6; + pixmap.blue_size = 5; + pixmap.alpha_size = 0; + pixmap.luminance_size = 0; + } else { + g_return_val_if_reached (FALSE); + } + break; + case GL_RGBA: + g_return_val_if_fail (type == GL_UNSIGNED_BYTE, FALSE); + pixmap.red_size = 8; + pixmap.green_size = 8; + pixmap.blue_size = 8; + pixmap.alpha_size = 8; + pixmap.luminance_size = 0; + break; + default: + g_assert_not_reached (); + return FALSE; + } + + pixmap.buffer_size = + pixmap.red_size + pixmap.green_size + pixmap.blue_size + + pixmap.alpha_size + pixmap.luminance_size; + pixmap.bytes_per_pixel = pixmap.buffer_size / 8; + pixmap.format = 0; + + if (ump_open () != UMP_OK) { + GST_ERROR ("Failed to open UMP"); + return FALSE; + } + + pixmap.data = + ump_ref_drv_allocate (GST_ROUND_UP_4 (pixmap.width) * pixmap.height * + pixmap.bytes_per_pixel, UMP_REF_DRV_CONSTRAINT_PHYSICALLY_LINEAR); + if (pixmap.data == UMP_INVALID_MEMORY_HANDLE) { + GST_ERROR ("Failed to allocate pixmap data via UMP"); + ump_close (); + return FALSE; + } + + *image_platform_data = g_slice_dup (fbdev_pixmap, &pixmap); + *image = + eglCreateImageKHR (display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer) * image_platform_data, NULL); + if (!image) { + GST_ERROR ("Failed to create EGLImage for pixmap"); + ump_reference_release ((ump_handle) pixmap.data); + ump_close (); + g_slice_free (fbdev_pixmap, *image_platform_data); + return FALSE; + } + + return TRUE; +} + +void +platform_free_eglimage (EGLDisplay display, EGLContext context, GLuint tex_id, + EGLImageKHR * image, gpointer * image_platform_data) +{ + fbdev_pixmap *pixmap = *image_platform_data; + + eglDestroyImageKHR (display, *image); + ump_reference_release ((ump_handle) pixmap->data); + ump_close (); + g_slice_free (fbdev_pixmap, *image_platform_data); +} +#endif +#endif + +#ifdef USE_EGL_RPI +#include +#include + +typedef struct +{ + EGL_DISPMANX_WINDOW_T w; + DISPMANX_DISPLAY_HANDLE_T d; +} RPIWindowData; + +EGLNativeWindowType +platform_create_native_window (gint x, gint y, gint width, gint height, gpointer * window_data) +{ + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + RPIWindowData *data; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + GstVideoRectangle src, dst, res; + + uint32_t dp_height; + uint32_t dp_width; + + int ret; + + ret = graphics_get_display_size (0, &dp_width, &dp_height); + if (ret < 0) { + GST_ERROR ("Can't open display"); + return (EGLNativeWindowType) 0; + } + GST_DEBUG ("Got display size: %dx%d\n", dp_width, dp_height); + GST_DEBUG ("Source size: %dx%d\n", width, height); + + /* Center width*height frame inside dp_width*dp_height */ + src.w = width; + src.h = height; + src.x = src.y = 0; + dst.w = dp_width; + dst.h = dp_height; + dst.x = dst.y = 0; + gst_video_sink_center_rect (src, dst, &res, TRUE); + + dst_rect.x = res.x; + dst_rect.y = res.y; + dst_rect.width = res.w; + dst_rect.height = res.h; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = width << 16; + src_rect.height = height << 16; + + dispman_display = vc_dispmanx_display_open (0); + dispman_update = vc_dispmanx_update_start (0); + dispman_element = vc_dispmanx_element_add (dispman_update, + dispman_display, 0, &dst_rect, 0, &src_rect, + DISPMANX_PROTECTION_NONE, 0, 0, 0); + + *window_data = data = g_slice_new0 (RPIWindowData); + data->d = dispman_display; + data->w.element = dispman_element; + data->w.width = width; + data->w.height = height; + vc_dispmanx_update_submit_sync (dispman_update); + + return (EGLNativeWindowType) data; +} + +gboolean +platform_destroy_native_window (EGLNativeDisplayType display, + EGLNativeWindowType window, gpointer * window_data) +{ + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + RPIWindowData *data = *window_data; + + dispman_display = data->d; + dispman_update = vc_dispmanx_update_start (0); + vc_dispmanx_element_remove (dispman_update, data->w.element); + vc_dispmanx_update_submit_sync (dispman_update); + vc_dispmanx_display_close (dispman_display); + + g_slice_free (RPIWindowData, data); + *window_data = NULL; + return TRUE; +} +#endif + +#if !defined(USE_EGL_X11) && !defined(USE_EGL_WAYLAND) && !defined(USE_EGL_MALI_FB) && !defined(USE_EGL_RPI) +/* Dummy functions for creating a native Window */ +EGLNativeWindowType +platform_create_native_window (gint x, gint y, gint width, gint height, gpointer * window_data) +{ + GST_ERROR ("Can't create native window"); + return (EGLNativeWindowType) 0; +} + +gboolean +platform_destroy_native_window (EGLNativeDisplayType display, + EGLNativeWindowType window, gpointer * window_data) +{ + GST_ERROR ("Can't destroy native window"); + return TRUE; +} +#endif diff --git a/gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.h b/gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.h new file mode 100644 index 0000000..ed93da0 --- /dev/null +++ b/gstegl_src/gst-egl/ext/eglgles/video_platform_wrapper.h @@ -0,0 +1,83 @@ +/* + * GStreamer Android Video Platform Wrapper + * Copyright (C) 2012 Collabora Ltd. + * @author: Reynaldo H. Verdejo Pinochet + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * General idea is to have all platform dependent code here for easy + * tweaking and isolation from the main routines + */ + +#ifndef __GST_VIDEO_PLATFORM_WRAPPER__ +#define __GST_VIDEO_PLATFORM_WRAPPER__ + +#include +#include + +#ifdef USE_EGL_X11 +#include +typedef struct +{ + Display *display; +} X11WindowData; + +EGLNativeWindowType platform_create_native_window_x11 (gint x, gint y, gint width, gint height, gpointer * window_data); +gboolean platform_destroy_native_window_x11 (EGLNativeDisplayType display, + EGLNativeWindowType w, gpointer * window_data); +#endif + +#ifdef USE_EGL_WAYLAND +#include +#include "wayland-client-protocol.h" +#include "wayland-egl.h" +typedef struct +{ + struct wl_egl_window *egl_window; + struct wl_shell_surface *shell_surface; + struct wl_surface *surface; +} WaylandWindowData; + +typedef struct +{ + struct wl_display *display; + struct wl_compositor *compositor; + struct wl_shell *shell; + struct wl_registry *registry; +} WaylandDisplay; + +EGLNativeWindowType platform_create_native_window_wayland (gint x, gint y, gint width, gint height, gpointer * window_data); +EGLNativeDisplayType platform_initialize_display_wayland (void); +gboolean platform_destroy_native_window_wayland (EGLNativeDisplayType display, + EGLNativeWindowType w, gpointer * window_data); +gboolean platform_destroy_display_wayland (void); +#endif + +gboolean platform_wrapper_init (void); + +#if !defined(USE_EGL_X11) && !defined(USE_EGL_WAYLAND) +EGLNativeWindowType platform_create_native_window (gint x, gint y, gint width, gint height, gpointer * window_data); +gboolean platform_destroy_native_window (EGLNativeDisplayType display, + EGLNativeWindowType w, gpointer * window_data); +#endif + +#endif \ No newline at end of file diff --git a/gstegl_src/gst-egl/gst-libs/gst/egl/LICENSE.libgstnvegl-1.0 b/gstegl_src/gst-egl/gst-libs/gst/egl/LICENSE.libgstnvegl-1.0 new file mode 100644 index 0000000..379b822 --- /dev/null +++ b/gstegl_src/gst-egl/gst-libs/gst/egl/LICENSE.libgstnvegl-1.0 @@ -0,0 +1,397 @@ +The software listed below is licensed under the terms of the LGPLv2 +(see below). To obtain source code, contact oss-requests@nvidia.com. + +libgstnvegl-1.0.so.0 + +------------------------------------ + +GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1991 Free Software Foundation, Inc. +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Library General Public License, applies to some specially +designated Free Software Foundation software, and to any other libraries whose +authors decide to use it. You can use it for your libraries, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you can +do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +library, or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a +fee, you must give the recipients all the rights that we gave you. You must make +sure that they, too, receive or can get the source code. If you link a program +with the library, you must provide complete object files to the recipients so +that they can relink them with the library, after making changes to the library +and recompiling it. And you must show them these terms so they know their +rights. + +Our method of protecting your rights has two steps: (1) copyright the library, +and (2) offer you this license which gives you legal permission to copy, +distribute and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone +understands that there is no warranty for this free library. If the library is +modified by someone else and passed on, we want its recipients to know that what +they have is not the original version, so that any problems introduced by others +will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish +to avoid the danger that companies distributing free software will individually +obtain patent licenses, thus in effect transforming the program into proprietary +software. To prevent this, we have made it clear that any patent must be +licensed for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License, which was designed for utility programs. This license, +the GNU Library General Public License, applies to certain designated libraries. +This license is quite different from the ordinary one; be sure to read it in +full, and don't assume that anything in it is the same as in the ordinary +license. + +The reason we have a separate public license for some libraries is that they +blur the distinction we usually make between modifying or adding to a program +and simply using it. Linking a program with a library, without changing the +library, is in some sense simply using the library, and is analogous to running +a utility program or application program. However, in a textual and legal sense, +the linked executable is a combined work, a derivative of the original library, +and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License +for libraries did not effectively promote software sharing, because most +developers did not use the libraries. We concluded that weaker conditions might +promote sharing better. + +However, unrestricted linking of non-free programs would deprive the users of +those programs of all benefit from the free status of the libraries themselves. +This Library General Public License is intended to permit developers of non-free +programs to use free libraries, while preserving your freedom as a user of such +programs to change the free libraries that are incorporated in them. (We have +not seen how to achieve this as regards changes in header files, but we have +achieved it as regards changes in the actual functions of the Library.) The hope +is that this will lead to faster development of free libraries. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General +Public License rather than by this special one. +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a +notice placed by the copyright holder or other authorized party saying it may be +distributed under the terms of this Library General Public License (also called +"this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as +to be conveniently linked with application programs (which use some of those +functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been +distributed under these terms. A "work based on the Library" means either the +Library or any derivative work under copyright law: that is to say, a work +containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, +translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making +modifications to it. For a library, complete source code means all the source +code for all modules it contains, plus any associated interface definition +files, plus the scripts used to control compilation and installation of the +library. + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running a program using the +Library is not restricted, and output from such a program is covered only if its +contents constitute a work based on the Library (independent of the use of the +Library in a tool for writing it). Whether that is true depends on what the +Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and distribute a copy of this License along +with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at +your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus +forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) The modified work must itself be a software library. + b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other than +as an argument passed when the facility is invoked, then you must make a good +faith effort to ensure that, in the event an application does not supply such +function or table, the facility still operates, and performs whatever part of +its purpose remains meaningful. + + (For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, +Subsection 2d requires that any application-supplied function or table used by +this function must be optional: if the application does not supply it, the +square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the entire whole, +and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on the +Library. + +In addition, mere aggregation of another work not based on the Library with the +Library (or with a work based on the Library) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. (If +a newer version than version 2 of the ordinary GNU General Public License has +appeared, then you can specify that version instead if you wish.) Do not make +any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so +the ordinary GNU General Public License applies to all subsequent copies and +derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into +a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, +under Section 2) in object code or executable form under the terms of Sections 1 +and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a +designated place, then offering equivalent access to copy the source code from +the same place satisfies the requirement to distribute the source code, even +though third parties are not compelled to copy the source along with the object +code. + +5. A program that contains no derivative of any portion of the Library, but is +designed to work with the Library by being compiled or linked with it, is called +a "work that uses the Library". Such a work, in isolation, is not a derivative +work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions of +the Library), rather than a "work that uses the library". The executable is +therefore covered by this License. Section 6 states terms for distribution of +such executables. + +When a "work that uses the Library" uses material from a header file that is +part of the Library, the object code for the work may be a derivative work of +the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if the +work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less in +length), then the use of the object file is unrestricted, regardless of whether +it is legally a derivative work. (Executables containing this object code plus +portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the +object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also compile or link a "work +that uses the Library" with the Library to produce a work containing portions of +the Library, and distribute that work under terms of your choice, provided that +the terms permit modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is +used in it and that the Library and its use are covered by this License. You +must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable +source code for the Library including whatever changes were used in the work +(which must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user can +modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + b) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for a +charge no more than the cost of performing this distribution. + c) If distribution of the work is made by offering access to copy from a +designated place, offer equivalent access to copy the above specified materials +from the same place. + d) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable from +it. However, as a special exception, the source code distributed need not +include anything that is normally distributed (in either source or binary form) +with the major components (compiler, kernel, and so on) of the operating system +on which the executable runs, unless that component itself accompanies the +executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together in +an executable that you distribute. + +7. You may place library facilities that are a work based on the Library +side-by-side in a single library together with other library facilities not +covered by this License, and distribute such a combined library, provided that +the separate distribution of the work based on the Library and of the other +library facilities is otherwise permitted, and provided that you do these two +things: + + a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to copy, +modify, sublicense, link with, or distribute the Library is void, and will +automatically terminate your rights under this License. However, parties who +have received copies, or rights, from you under this License will not have their +licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Library +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Library (or any +work based on the Library), you indicate your acceptance of this License to do +so, and all its terms and conditions for copying, distributing or modifying the +Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor to +copy, distribute, link with or modify the Library subject to these terms and +conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties to this License. + +11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Library at all. +For example, if a patent license would not permit royalty-free redistribution of +the Library by all those who receive copies directly or indirectly through you, +then the only way you could satisfy both it and this License would be to refrain +entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In such +case, this License incorporates the limitation as if written in the body of this +License. + +13. The Free Software Foundation may publish revised and/or new versions of the +Library General Public License from time to time. Such new versions will be +similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing and +reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE +LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED +IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS +IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY +TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/gstegl_src/gst-egl/gst-libs/gst/egl/egl.c b/gstegl_src/gst-egl/gst-libs/gst/egl/egl.c new file mode 100644 index 0000000..af54ad3 --- /dev/null +++ b/gstegl_src/gst-egl/gst-libs/gst/egl/egl.c @@ -0,0 +1,404 @@ +/* + * GStreamer EGL Library + * Copyright (C) 2012 Collabora Ltd. + * @author: Sebastian Dröge + * * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; + * version 2 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#if defined (USE_EGL_RPI) && defined(__GNUC__) +#ifndef __VCCOREVER__ +#define __VCCOREVER__ 0x04000000 +#endif + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC optimize ("gnu89-inline") +#endif + +#define EGL_EGLEXT_PROTOTYPES + +#include +#include + +#if defined (USE_EGL_RPI) && defined(__GNUC__) +#pragma GCC reset_options +#pragma GCC diagnostic pop +#endif + +struct _GstEGLDisplay +{ + EGLDisplay display; + volatile gint refcount; + GDestroyNotify destroy_notify; + + PFNEGLCREATEIMAGEKHRPROC eglCreateImage; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImage; +}; + +typedef struct +{ + GstMemory parent; + + GstEGLDisplay *display; + EGLImageKHR image; + GstVideoGLTextureType type; + GstVideoGLTextureOrientation orientation; + + gpointer user_data; + GDestroyNotify user_data_destroy; +} GstEGLImageMemory; + +#define GST_EGL_IMAGE_MEMORY(mem) ((GstEGLImageMemory*)(mem)) + +gboolean +gst_egl_image_memory_is_mappable (void) +{ + return FALSE; +} + +gboolean +gst_is_egl_image_memory (GstMemory * mem) +{ + g_return_val_if_fail (mem != NULL, FALSE); + g_return_val_if_fail (mem->allocator != NULL, FALSE); + + return g_strcmp0 (mem->allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0; +} + +EGLImageKHR +gst_egl_image_memory_get_image (GstMemory * mem) +{ + g_return_val_if_fail (gst_is_egl_image_memory (mem), EGL_NO_IMAGE_KHR); + + if (mem->parent) + mem = mem->parent; + + return GST_EGL_IMAGE_MEMORY (mem)->image; +} + +GstEGLDisplay * +gst_egl_image_memory_get_display (GstMemory * mem) +{ + g_return_val_if_fail (gst_is_egl_image_memory (mem), NULL); + + if (mem->parent) + mem = mem->parent; + + return gst_egl_display_ref (GST_EGL_IMAGE_MEMORY (mem)->display); +} + +GstVideoGLTextureType +gst_egl_image_memory_get_type (GstMemory * mem) +{ + g_return_val_if_fail (gst_is_egl_image_memory (mem), -1); + + if (mem->parent) + mem = mem->parent; + + return GST_EGL_IMAGE_MEMORY (mem)->type; +} + +GstVideoGLTextureOrientation +gst_egl_image_memory_get_orientation (GstMemory * mem) +{ + g_return_val_if_fail (gst_is_egl_image_memory (mem), + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL); + + if (mem->parent) + mem = mem->parent; + + return GST_EGL_IMAGE_MEMORY (mem)->orientation; +} + +void +gst_egl_image_memory_set_orientation (GstMemory * mem, + GstVideoGLTextureOrientation orientation) +{ + g_return_if_fail (gst_is_egl_image_memory (mem)); + + if (mem->parent) + mem = mem->parent; + + GST_EGL_IMAGE_MEMORY (mem)->orientation = orientation; +} + +static GstMemory * +gst_egl_image_allocator_alloc_vfunc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_warning + ("Use gst_egl_image_allocator_alloc() to allocate from this allocator"); + + return NULL; +} + +static void +gst_egl_image_allocator_free_vfunc (GstAllocator * allocator, GstMemory * mem) +{ + GstEGLImageMemory *emem = (GstEGLImageMemory *) mem; + EGLDisplay display; + + g_return_if_fail (gst_is_egl_image_memory (mem)); + + /* Shared memory should not destroy all the data */ + if (!mem->parent) { + display = gst_egl_display_get (emem->display); + if (emem->display->eglDestroyImage) + emem->display->eglDestroyImage (display, emem->image); + + if (emem->user_data_destroy) + emem->user_data_destroy (emem->user_data); + + gst_egl_display_unref (emem->display); + } + + g_slice_free (GstEGLImageMemory, emem); +} + +static gpointer +gst_egl_image_mem_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) +{ + return NULL; +} + +static void +gst_egl_image_mem_unmap (GstMemory * mem) +{ +} + +static GstMemory * +gst_egl_image_mem_share (GstMemory * mem, gssize offset, gssize size) +{ + GstMemory *sub; + GstMemory *parent; + + if (offset != 0) + return NULL; + + if (size != -1 && size != (gssize)mem->size) + return NULL; + + /* find the real parent */ + if ((parent = mem->parent) == NULL) + parent = (GstMemory *) mem; + + if (size == -1) + size = mem->size - offset; + + sub = (GstMemory *) g_slice_new (GstEGLImageMemory); + + /* the shared memory is always readonly */ + gst_memory_init (GST_MEMORY_CAST (sub), GST_MINI_OBJECT_FLAGS (parent) | + GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->allocator, parent, + mem->maxsize, mem->align, mem->offset + offset, size); + + return sub; +} + +static GstMemory * +gst_egl_image_mem_copy (GstMemory * mem, gssize offset, gssize size) +{ + return NULL; +} + +static gboolean +gst_egl_image_mem_is_span (GstMemory * mem1, GstMemory * mem2, gsize * offset) +{ + return FALSE; +} + +typedef GstAllocator GstEGLImageAllocator; +typedef GstAllocatorClass GstEGLImageAllocatorClass; + +GType gst_egl_image_allocator_get_type (void); +G_DEFINE_TYPE (GstEGLImageAllocator, gst_egl_image_allocator, + GST_TYPE_ALLOCATOR); + +#define GST_TYPE_EGL_IMAGE_ALLOCATOR (gst_egl_image_mem_allocator_get_type()) +#define GST_IS_EGL_IMAGE_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_EGL_IMAGE_ALLOCATOR)) + +static void +gst_egl_image_allocator_class_init (GstEGLImageAllocatorClass * klass) +{ + GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = gst_egl_image_allocator_alloc_vfunc; + allocator_class->free = gst_egl_image_allocator_free_vfunc; +} + +static void +gst_egl_image_allocator_init (GstEGLImageAllocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_EGL_IMAGE_MEMORY_TYPE; + alloc->mem_map = gst_egl_image_mem_map; + alloc->mem_unmap = gst_egl_image_mem_unmap; + alloc->mem_share = gst_egl_image_mem_share; + alloc->mem_copy = gst_egl_image_mem_copy; + alloc->mem_is_span = gst_egl_image_mem_is_span; + + GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +static gpointer +gst_egl_image_allocator_init_instance (gpointer data) +{ + return g_object_new (gst_egl_image_allocator_get_type (), NULL); +} + +GstAllocator * +gst_egl_image_allocator_obtain (void) +{ + GstAllocator *allocator; + + allocator = gst_egl_image_allocator_init_instance(NULL); + return GST_ALLOCATOR (g_object_ref (allocator)); +} + +GstMemory * +gst_egl_image_allocator_alloc (GstAllocator * allocator, + GstEGLDisplay * display, GstVideoGLTextureType type, gint width, + gint height, gsize * size) +{ + return NULL; +} + +GstMemory * +gst_egl_image_allocator_wrap (GstAllocator * allocator, + GstEGLDisplay * display, EGLImageKHR image, GstVideoGLTextureType type, + GstMemoryFlags flags, gsize size, gpointer user_data, + GDestroyNotify user_data_destroy) +{ + GstEGLImageMemory *mem; + + g_return_val_if_fail (display != NULL, NULL); + g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL); + + if (!allocator) { + allocator = gst_egl_image_allocator_obtain (); + } + + mem = g_slice_new (GstEGLImageMemory); + gst_memory_init (GST_MEMORY_CAST (mem), flags, + allocator, NULL, size, 0, 0, size); + + mem->display = gst_egl_display_ref (display); + mem->image = image; + mem->type = type; + mem->orientation = GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL; + + mem->user_data = user_data; + mem->user_data_destroy = user_data_destroy; + + return GST_MEMORY_CAST (mem); +} + +GstContext * +gst_context_new_egl_display (GstEGLDisplay * display, gboolean persistent) +{ + GstContext *context; + GstStructure *s; + + context = gst_context_new (GST_EGL_DISPLAY_CONTEXT_TYPE, persistent); + s = gst_context_writable_structure (context); + gst_structure_set (s, "display", GST_TYPE_EGL_DISPLAY, display, NULL); + + return context; +} + +gboolean +gst_context_get_egl_display (GstContext * context, GstEGLDisplay ** display) +{ + const GstStructure *s; + + g_return_val_if_fail (GST_IS_CONTEXT (context), FALSE); + g_return_val_if_fail (strcmp (gst_context_get_context_type (context), + GST_EGL_DISPLAY_CONTEXT_TYPE) == 0, FALSE); + + s = gst_context_get_structure (context); + return gst_structure_get (s, "display", GST_TYPE_EGL_DISPLAY, display, NULL); +} + +GstEGLDisplay * +gst_egl_display_new (EGLDisplay display, GDestroyNotify destroy_notify) +{ + GstEGLDisplay *gdisplay; + + gdisplay = g_slice_new (GstEGLDisplay); + gdisplay->display = display; + gdisplay->refcount = 1; + gdisplay->destroy_notify = destroy_notify; + + gdisplay->eglCreateImage = + (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress ("eglCreateImageKHR"); + gdisplay->eglDestroyImage = + (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress ("eglDestroyImageKHR"); + + return gdisplay; +} + +GstEGLDisplay * +gst_egl_display_ref (GstEGLDisplay * display) +{ + g_return_val_if_fail (display != NULL, NULL); + + g_atomic_int_inc (&display->refcount); + + return display; +} + +void +gst_egl_display_unref (GstEGLDisplay * display) +{ + g_return_if_fail (display != NULL); + + if (g_atomic_int_dec_and_test (&display->refcount)) { + if (display->destroy_notify) + display->destroy_notify (display->display); + g_slice_free (GstEGLDisplay, display); + } +} + +EGLDisplay +gst_egl_display_get (GstEGLDisplay * display) +{ + g_return_val_if_fail (display != NULL, EGL_NO_DISPLAY); + + return display->display; +} + +EGLImageKHR +gst_egl_display_image_create (GstEGLDisplay * display, + EGLContext ctx, + EGLenum target, EGLClientBuffer buffer, const EGLint * attrib_list) +{ + if (!display->eglCreateImage) + return EGL_NO_IMAGE_KHR; + + return display->eglCreateImage (gst_egl_display_get (display), ctx, target, + buffer, attrib_list); +} + +G_DEFINE_BOXED_TYPE (GstEGLDisplay, gst_egl_display, + (GBoxedCopyFunc) gst_egl_display_ref, + (GBoxedFreeFunc) gst_egl_display_unref); diff --git a/gstegl_src/gst-egl/gst-libs/gst/egl/egl.h b/gstegl_src/gst-egl/gst-libs/gst/egl/egl.h new file mode 100644 index 0000000..a02cf8b --- /dev/null +++ b/gstegl_src/gst-egl/gst-libs/gst/egl/egl.h @@ -0,0 +1,76 @@ +/* + * GStreamer EGL Library + * Copyright (C) 2012 Collabora Ltd. + * @author: Sebastian Dröge + * * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; + * version 2 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_EGL_H__ +#define __GST_EGL_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_EGL_IMAGE_MEMORY_TYPE "EGLImage" + +#define GST_CAPS_FEATURE_MEMORY_EGL_IMAGE "memory:EGLImage" + +typedef struct _GstEGLDisplay GstEGLDisplay; + +/* EGLImage GstMemory handling */ +gboolean gst_egl_image_memory_is_mappable (void); +gboolean gst_is_egl_image_memory (GstMemory * mem); +EGLImageKHR gst_egl_image_memory_get_image (GstMemory * mem); +GstEGLDisplay *gst_egl_image_memory_get_display (GstMemory * mem); +GstVideoGLTextureType gst_egl_image_memory_get_type (GstMemory * mem); +GstVideoGLTextureOrientation gst_egl_image_memory_get_orientation (GstMemory * mem); +void gst_egl_image_memory_set_orientation (GstMemory * mem, + GstVideoGLTextureOrientation orientation); + +/* Generic EGLImage allocator that doesn't support mapping, copying or anything */ +GstAllocator *gst_egl_image_allocator_obtain (void); +GstMemory *gst_egl_image_allocator_alloc (GstAllocator * allocator, + GstEGLDisplay * display, GstVideoGLTextureType type, gint width, gint height, + gsize * size); +GstMemory *gst_egl_image_allocator_wrap (GstAllocator * allocator, + GstEGLDisplay * display, EGLImageKHR image, GstVideoGLTextureType type, + GstMemoryFlags flags, gsize size, gpointer user_data, + GDestroyNotify user_data_destroy); + +#define GST_EGL_DISPLAY_CONTEXT_TYPE "gst.egl.EGLDisplay" +GstContext * gst_context_new_egl_display (GstEGLDisplay * display, gboolean persistent); +gboolean gst_context_get_egl_display (GstContext * context, + GstEGLDisplay ** display); + +/* EGLDisplay wrapper with refcount, connection is closed after last ref is gone */ +#define GST_TYPE_EGL_DISPLAY (gst_egl_display_get_type()) +GType gst_egl_display_get_type (void); + +GstEGLDisplay *gst_egl_display_new (EGLDisplay display, GDestroyNotify destroy_notify); +GstEGLDisplay *gst_egl_display_ref (GstEGLDisplay * display); +void gst_egl_display_unref (GstEGLDisplay * display); +EGLDisplay gst_egl_display_get (GstEGLDisplay * display); +EGLImageKHR gst_egl_display_image_create (GstEGLDisplay * display, + EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLint * attrib_list); + +G_END_DECLS +#endif /* __GST_EGL_H__ */ diff --git a/gstegl_src/gst-egl/pre-gen-source_64/config.h b/gstegl_src/gst-egl/pre-gen-source_64/config.h new file mode 100644 index 0000000..d06e1a8 --- /dev/null +++ b/gstegl_src/gst-egl/pre-gen-source_64/config.h @@ -0,0 +1,663 @@ +/* config.h. Generated from config.h.in by configure. + * config.h.in. Generated from configure.ac by autoheader. + * + * Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. + * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * . + */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Default audio sink */ +#define DEFAULT_AUDIOSINK "autoaudiosink" + +/* Default audio source */ +#define DEFAULT_AUDIOSRC "alsasrc" + +/* Default video sink */ +#define DEFAULT_VIDEOSINK "autovideosink" + +/* Default video source */ +#define DEFAULT_VIDEOSRC "v4l2src" + +/* Default visualizer */ +#define DEFAULT_VISUALIZER "goom" + +/* Disable Orc */ +#define DISABLE_ORC 1 + +/* Define if an old libdts is used */ +/* #undef DTS_OLD */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#define ENABLE_NLS 1 + +/* The x in 2.x */ +/* #undef FAAD2_MINOR_VERSION */ + +/* Define if AAC is using new api prefix */ +/* #undef FAAD_IS_NEAAC */ + +/* gettext package name */ +#define GETTEXT_PACKAGE "gst-plugins-bad-1.0" + +/* The GIO library directory. */ +#define GIO_LIBDIR "/usr/lib/aarch64-linux-gnu" + +/* The GIO modules directory. */ +#define GIO_MODULE_DIR "/usr/lib/aarch64-linux-gnu/gio/modules" + +/* Define if GSM header in gsm/ subdir */ +/* #undef GSM_HEADER_IN_SUBDIR */ + +/* GStreamer API Version */ +#define GST_API_VERSION "1.0" + +/* Extra platform specific plugin suffix */ +/* #undef GST_EXTRA_MODULE_SUFFIX */ + +/* Defined if gcov is enabled to force a rebuild due to config.h changing */ +/* #undef GST_GCOV_ENABLED */ + +/* Defined when registry scanning through fork is unsafe */ +/* #undef GST_HAVE_UNSAFE_FORK */ + +/* Default errorlevel to use */ +#define GST_LEVEL_DEFAULT GST_LEVEL_NONE + +/* GStreamer license */ +#define GST_LICENSE "LGPL" + +/* mjpegtools API evolution */ +#define GST_MJPEGTOOLS_API 0 + +/* package name in plugins */ +#define GST_PACKAGE_NAME "GStreamer Nvidia Bad Plug-ins source release" + +/* package origin */ +#define GST_PACKAGE_ORIGIN "Unknown package origin" + +/* GStreamer package release date/time for plugins as YYYY-MM-DD */ +#define GST_PACKAGE_RELEASE_DATETIME "2014-02-08" + +/* Define if static plugins should be built */ +/* #undef GST_PLUGIN_BUILD_STATIC */ + +/* Define to enable Windows ACM library (used by acm). */ +/* #undef HAVE_ACM */ + +/* Define to enable Android Media (used by androidmedia). */ +/* #undef HAVE_ANDROID_MEDIA */ + +/* Define to enable AirPort Express Wireless sink (used by apexsink). */ +/* #undef HAVE_APEXSINK */ + +/* Define to enable Apple video (used by applemedia). */ +/* #undef HAVE_APPLE_MEDIA */ + +/* Define to enable ASS/SSA renderer (used by assrender). */ +/* #undef HAVE_ASSRENDER */ + +/* Define to enable AVC Video Services (used by avcsrc). */ +/* #undef HAVE_AVC */ + +/* Define to enable Bluez (used by bluez). */ +/* #undef HAVE_BLUEZ */ + +/* Define to enable bz2 library (used by bz2). */ +/* #undef HAVE_BZ2 */ + +/* Define to enable cdaudio (used by cdaudio). */ +/* #undef HAVE_CDAUDIO */ + +/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the + CoreFoundation framework. */ +/* #undef HAVE_CFLOCALECOPYCURRENT */ + +/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ + +/* Define to enable chromaprint (used by chromaprint). */ +/* #undef HAVE_CHROMAPRINT */ + +/* Define if the target CPU is an Alpha */ +/* #undef HAVE_CPU_ALPHA */ + +/* Define if the target CPU is an ARM */ +#define HAVE_CPU_ARM 1 + +/* Define if the target CPU is a CRIS */ +/* #undef HAVE_CPU_CRIS */ + +/* Define if the target CPU is a CRISv32 */ +/* #undef HAVE_CPU_CRISV32 */ + +/* Define if the target CPU is a HPPA */ +/* #undef HAVE_CPU_HPPA */ + +/* Define if the target CPU is an x86 */ +/* #undef HAVE_CPU_I386 */ + +/* Define if the target CPU is a IA64 */ +/* #undef HAVE_CPU_IA64 */ + +/* Define if the target CPU is a M68K */ +/* #undef HAVE_CPU_M68K */ + +/* Define if the target CPU is a MIPS */ +/* #undef HAVE_CPU_MIPS */ + +/* Define if the target CPU is a PowerPC */ +/* #undef HAVE_CPU_PPC */ + +/* Define if the target CPU is a 64 bit PowerPC */ +/* #undef HAVE_CPU_PPC64 */ + +/* Define if the target CPU is a S390 */ +/* #undef HAVE_CPU_S390 */ + +/* Define if the target CPU is a SPARC */ +/* #undef HAVE_CPU_SPARC */ + +/* Define if the target CPU is a x86_64 */ +/* #undef HAVE_CPU_X86_64 */ + +/* Define to enable Curl plugin (used by curl). */ +/* #undef HAVE_CURL */ + +/* Define to enable daala (used by daala). */ +/* #undef HAVE_DAALA */ + +/* Define to enable DASH plug-in (used by dash). */ +#define HAVE_DASH /**/ + +/* Define to enable libdc1394 (used by dc1394). */ +/* #undef HAVE_DC1394 */ + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +#define HAVE_DCGETTEXT 1 + +/* Define to enable decklink (used by decklink). */ +#define HAVE_DECKLINK /**/ + +/* Define to enable Direct3D plug-in (used by direct3dsink). */ +/* #undef HAVE_DIRECT3D */ + +/* Define to enable DirectDraw plug-in (used by directdrawsink). */ +/* #undef HAVE_DIRECTDRAW */ + +/* Define to enable directfb (used by dfbvideosink ). */ +/* #undef HAVE_DIRECTFB */ + +/* Define to enable DirectShow plug-in (used by winks). */ +/* #undef HAVE_DIRECTSHOW */ + +/* Define to enable DirectSound (used by directsoundsrc). */ +/* #undef HAVE_DIRECTSOUND */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* define for working do while(0) macros */ +#define HAVE_DOWHILE_MACROS 1 + +/* Define to enable dts library (used by dtsdec). */ +/* #undef HAVE_DTS */ + +/* Define to enable DVB Source (used by dvb). */ +#define HAVE_DVB /**/ + +/* Define to enable eglgles sink (used by eglgles). */ +#define HAVE_EGLGLES /**/ + +/* Define to enable building of experimental plug-ins. */ +/* #undef HAVE_EXPERIMENTAL */ + +/* Define to enable building of plug-ins with external deps. */ +#define HAVE_EXTERNAL /**/ + +/* Define to enable AAC encoder plug-in (used by faac). */ +/* #undef HAVE_FAAC */ + +/* Define to enable AAC decoder plug-in (used by faad). */ +/* #undef HAVE_FAAD */ + +/* Define to enable linux framebuffer (used by fbdevsink). */ +#define HAVE_FBDEV /**/ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FCNTL_H */ + +/* FIONREAD ioctl found in sys/filio.h */ +/* #undef HAVE_FIONREAD_IN_SYS_FILIO */ + +/* FIONREAD ioctl found in sys/ioclt.h */ +#define HAVE_FIONREAD_IN_SYS_IOCTL 1 + +/* Define to enable Flite plugin (used by flite). */ +/* #undef HAVE_FLITE */ + +/* Define to enable fluidsynth (used by fluidsynth). */ +/* #undef HAVE_FLUIDSYNTH */ + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define if the GNU gettext() function is already present or preinstalled. */ +#define HAVE_GETTEXT 1 + +/* Define to enable gme decoder (used by gme). */ +/* #undef HAVE_GME */ + +/* Define to 1 if you have the `gmtime_r' function. */ +#define HAVE_GMTIME_R 1 + +/* Define to enable GSettings plugin (used by gsettings). */ +/* #undef HAVE_GSETTINGS */ + +/* Define to enable GSM library (used by gsmenc gsmdec). */ +/* #undef HAVE_GSM */ + +/* Define if gudev is installed */ +/* #undef HAVE_GUDEV */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_HIGHGUI_H */ + +/* Define to enable http live streaming plugin (used by hls). */ +/* #undef HAVE_HLS */ + +/* Define if you have the iconv() function and it works. */ +/* #undef HAVE_ICONV */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if building for Apple iOS */ +/* #undef HAVE_IOS */ + +/* Define to enable Kate (used by kate). */ +/* #undef HAVE_KATE */ + +/* Define to enable ladspa (used by ladspa). */ +/* #undef HAVE_LADSPA */ + +/* Define if gme 0.5.6 or newer is available */ +/* #undef HAVE_LIBGME_ACCURACY */ + +/* Define to enable mms protocol library (used by libmms). */ +/* #undef HAVE_LIBMMS */ + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +/* #undef HAVE_LIBNSL */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define if libusb 1.x is installed */ +/* #undef HAVE_LIBUSB */ + +/* Define to enable Linear Systems SDI plugin (used by linsys). */ +/* #undef HAVE_LINSYS */ + +/* Define if we have liblrdf */ +/* #undef HAVE_LRDF */ + +/* Define to enable lv2 (used by lv2). */ +/* #undef HAVE_LV2 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to enable Multi Format Codec (used by mfc). */ +#define HAVE_MFC /**/ + +/* Define to enable libmimic library (used by mimic). */ +/* #undef HAVE_MIMIC */ + +/* Define to 1 if you have a working `mmap' system call. */ +#define HAVE_MMAP 1 + +/* Define to enable modplug (used by modplug). */ +/* #undef HAVE_MODPLUG */ + +/* Define to enable mpeg2enc (used by mpeg2enc). */ +/* #undef HAVE_MPEG2ENC */ + +/* Define to enable mpg123 audio decoder (used by mpg123). */ +/* #undef HAVE_MPG123 */ + +/* Define to enable mplex (used by mplex). */ +/* #undef HAVE_MPLEX */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MSACM_H */ + +/* Define to enable musepackdec (used by musepack). */ +/* #undef HAVE_MUSEPACK */ + +/* Define to enable MythTV client plugins (used by mythtvsrc). */ +/* #undef HAVE_MYTHTV */ + +/* Define to enable nas plug-in (used by nassink). */ +/* #undef HAVE_NAS */ + +/* Define to enable neon http client plugins (used by neonhttpsrc). */ +/* #undef HAVE_NEON */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_IP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_TCP_H */ + +/* Define to enable ofa plugins (used by ofa). */ +/* #undef HAVE_OFA */ + +/* Define to enable OpenAL plugin (used by openal). */ +/* #undef HAVE_OPENAL */ + +/* Define to enable opencv plugins (used by opencv). */ +/* #undef HAVE_OPENCV */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENCV2_HIGHGUI_HIGHGUI_C_H */ + +/* Define to enable openjpeg library (used by openjpeg). */ +/* #undef HAVE_OPENJPEG */ + +/* Define to enable OpenSL ES (used by opensl). */ +/* #undef HAVE_OPENSLES */ + +/* Define to enable opus (used by opus). */ +/* #undef HAVE_OPUS */ + +/* Use Orc */ +/* #undef HAVE_ORC */ + +/* Apple Mac OS X operating system detected */ +/* #undef HAVE_OSX */ + +/* Define to enable OSX video (used by osxvideosrc). */ +/* #undef HAVE_OSX_VIDEO */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define to enable pvrvideosink (used by pvr). */ +/* #undef HAVE_PVR */ + +/* Define to enable QuickTime wrapper (used by qtwrapper). */ +/* #undef HAVE_QUICKTIME */ + +/* Define if RDTSC is available */ +/* #undef HAVE_RDTSC */ + +/* Define to enable resindvd plugin (used by resindvd). */ +/* #undef HAVE_RESINDVD */ + +/* Define to enable rsvg decoder (used by rsvg). */ +/* #undef HAVE_RSVG */ + +/* Have RSVG 2.36.2 or newer */ +/* #undef HAVE_RSVG_2_36_2 */ + +/* Define to enable rtmp library (used by rtmp). */ +/* #undef HAVE_RTMP */ + +/* Define to enable SBC bluetooth audio codec (used by sbc). */ +/* #undef HAVE_SBC */ + +/* Define to enable Schroedinger video codec (used by schro). */ +/* #undef HAVE_SCHRO */ + +/* Define to enable SDL plug-in (used by sdlvideosink sdlaudiosink). */ +/* #undef HAVE_SDL */ + +/* Define to enable POSIX shared memory source and sink (used by shm). */ +#define HAVE_SHM /**/ + +/* Define to enable Smooth Streaming plug-in (used by smoothstreaming). */ +#define HAVE_SMOOTHSTREAMING /**/ + +/* Define to enable sndfile plug-in (used by sfsrc sfsink). */ +/* #undef HAVE_SNDFILE */ + +/* Define to enable sndio audio (used by sndio). */ +/* #undef HAVE_SNDIO */ + +/* Define to enable soundtouch plug-in (used by soundtouch). */ +/* #undef HAVE_SOUNDTOUCH */ + +/* Defined if the available libSoundTouch is >= 1.4 */ +/* #undef HAVE_SOUNDTOUCH_1_4 */ + +/* Define to enable Spandsp (used by spandsp). */ +/* #undef HAVE_SPANDSP */ + +/* Define to enable spc decoder (used by spc). */ +/* #undef HAVE_SPC */ + +/* Define to enable srtp library (used by srtp). */ +/* #undef HAVE_SRTP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UTSNAME_H 1 + +/* Define to enable Teletext decoder (used by teletextdec). */ +/* #undef HAVE_TELETEXTDEC */ + +/* Define if libtiger is available */ +/* #undef HAVE_TIGER */ + +/* Define to enable timidity midi soft synth plugin (used by timidity). */ +/* #undef HAVE_TIMIDITY */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to enable UVC H264 (used by uvch264). */ +/* #undef HAVE_UVCH264 */ + +/* Define if valgrind should be used */ +/* #undef HAVE_VALGRIND */ + +/* Define to enable Video CD (used by vcdsrc). */ +/* #undef HAVE_VCD */ + +/* Define to enable VDPAU (used by vdpau). */ +/* #undef HAVE_VDPAU */ + +/* Define to enable vo-aacenc library (used by vo-aacenc). */ +/* #undef HAVE_VOAACENC */ + +/* Define to enable vo-amrwbenc library (used by vo-amrwbenc). */ +/* #undef HAVE_VOAMRWBENC */ + +/* Define to enable WASAPI plug-in (used by wasapi). */ +/* #undef HAVE_WASAPI */ + +/* Define to enable wayland sink (used by wayland ). */ +#define HAVE_WAYLAND /**/ + +/* Define to enable WebP (used by webp ). */ +/* #undef HAVE_WEBP */ + +/* Define to enable wildmidi midi soft synth plugin (used by wildmidi). */ +/* #undef HAVE_WILDMIDI */ + +/* Have WildMidi 0.2.2 or earlier library */ +/* #undef HAVE_WILDMIDI_0_2_2 */ + +/* Defined if compiling for Windows */ +/* #undef HAVE_WIN32 */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to enable Windows internet library (used by wininet). */ +/* #undef HAVE_WININET */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WININET_H */ + +/* Define to enable winscreencap plug-in (used by winscreencap). */ +/* #undef HAVE_WINSCREENCAP */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WS2TCPIP_H */ + +/* Define if you have X11 library */ +#define HAVE_X11 1 + +/* Define to enable xvid plugins (used by xvid). */ +/* #undef HAVE_XVID */ + +/* Define to enable ZBar barcode detector (used by zbar). */ +/* #undef HAVE_ZBAR */ + +/* the host CPU */ +#define HOST_CPU "aarch64" + +/* library dir */ +#define LIBDIR "/usr/lib" + +/* gettext locale dir */ +#define LOCALEDIR "/usr/share/locale" + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define if the old MusePack API is used */ +/* #undef MPC_IS_OLD_API */ + +/* opencv install prefix */ +#define OPENCV_PREFIX "" + +/* Name of package */ +#define PACKAGE "gst-plugins-bad" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "http://bugzilla.gnome.org/enter_bug.cgi?product=GStreamer" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "GStreamer Bad Plug-ins" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "GStreamer Bad Plug-ins 1.2.3" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "gst-plugins-bad" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.2.3" + +/* directory where plugins are located */ +#define PLUGINDIR "/usr/lib/gstreamer-1.0" + +/* The size of `char', as computed by sizeof. */ +/* #undef SIZEOF_CHAR */ + +/* The size of `int', as computed by sizeof. */ +/* #undef SIZEOF_INT */ + +/* The size of `long', as computed by sizeof. */ +/* #undef SIZEOF_LONG */ + +/* The size of `short', as computed by sizeof. */ +/* #undef SIZEOF_SHORT */ + +/* The size of `void*', as computed by sizeof. */ +/* #undef SIZEOF_VOIDP */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* the target CPU */ +#define TARGET_CPU "aarch64" + +/* Define location of timidity.cfg */ +/* #undef TIMIDITY_CFG */ + +/* Use Mali FB EGL window system */ +/* #undef USE_EGL_MALI_FB */ + +/* Use RPi EGL window system */ +/* #undef USE_EGL_RPI */ + +/* Use X11 EGL window system */ +#define USE_EGL_X11 1 + +/* Version number of package */ +#define VERSION "1.2.3" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if the X Window System is missing or not being used. */ +/* #undef X_DISPLAY_MISSING */ + +/* We need at least WinXP SP2 for __stat64 */ +/* #undef __MSVCRT_VERSION__ */ diff --git a/push_info.txt b/push_info.txt new file mode 100644 index 0000000..738a8cd --- /dev/null +++ b/push_info.txt @@ -0,0 +1 @@ +jetson_35.4.1