Younghwan Ahn <younghwan_.an@samsung.com>
Jeongmo Yang <jm80.yang@samsung.com>
Sangchul Lee <sc11.lee@samsung.com>
+Andriy Martynets <a.martynets@partner.samsung.com>
SUBDIRS += alfec
endif
+if GST_TIZEN_USE_VIDEO360
+SUBDIRS += video360
+endif
+
DIST_SUBDIRS = common
if GST_TIZEN_USE_ENCODEBIN
DIST_SUBDIRS += alfec
endif
+if GST_TIZEN_USE_VIDEO360
+DIST_SUBDIRS += video360
+endif
+
EXTRA_DIST = \
gstreamer.spec gstreamer.spec.in \
configure.ac autogen.sh depcomp \
pdpushsrc pdpushsrc Progressive download src plugin
tizenipc tizenipcsrc,tizenipcsink
toggle toggle Base transform plugin
+video360 video360 Spherical video processing plugin
waylandsrc waylandsrc
wfdmanager wfdsrc,wfdrtpbuffer
wfdtsdemux wfdtsparse,wfdtsdemux
[GST_TIZEN_USE_ALFEC=yes])
AM_CONDITIONAL(GST_TIZEN_USE_ALFEC, test "x$GST_TIZEN_USE_ALFEC" = "xyes")
+dnl use video360 --------------------------------------------------------------------------
+AC_ARG_ENABLE(video360, AC_HELP_STRING([--enable-video360], [using video360]),
+[
+ case "${enableval}" in
+ yes) GST_TIZEN_USE_VIDEO360=yes ;;
+ no) GST_TIZEN_USE_VIDEO360=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-video360) ;;
+ esac
+ ],
+ [GST_TIZEN_USE_VIDEO360=yes])
+AM_CONDITIONAL(GST_TIZEN_USE_VIDEO360, test "x$GST_TIZEN_USE_VIDEO360" = "xyes")
+
+dnl video360 related options ---------------------------------------------------------------
+AC_ARG_WITH([native-formats], AS_HELP_STRING([--with-native-formats],
+ [Build with support for Tizen native video formats]))
+AS_IF([test "x$with_native_formats" = "xyes"], [CFLAGS="$CFLAGS -DGST_TIZEN_USE_NATIVE_FORMATS"])
+
+AC_ARG_WITH([gles2], AS_HELP_STRING([--with-gles2], [Build with OpenGL ES 2.0]))
+AS_IF([test "x$with_gles2" = "xyes"], [
+ PKG_CHECK_MODULES(GLES, [
+ gles20
+ ], [
+ AC_SUBST(GLES_CFLAGS)
+ AC_SUBST(GLES_LIBS)
+ ])
+ CFLAGS="$CFLAGS -DGST_TIZEN_GL_API_GLES2"
+])
+
AC_OUTPUT(
Makefile
common/Makefile
tizenipc/src/Makefile
wfdtizenmanager/Makefile
alfec/Makefile
+video360/Makefile
+video360/src/Makefile
)
Name: gst-plugins-tizen
Version: 1.0.0
Summary: GStreamer tizen plugins (common)
-Release: 47
+Release: 48
Group: Multimedia/Framework
Url: http://gstreamer.freedesktop.org/
License: LGPL-2.1+
BuildRequires: libdrm-devel
BuildRequires: pkgconfig(vconf)
BuildRequires: pkgconfig(mm-common)
+BuildRequires: pkgconfig(gles20)
%if %{with wayland}
BuildRequires: pkgconfig(wayland-client) >= 1.0.0
BuildRequires: pkgconfig(wayland-tbm-client)
--enable-ext-wfdtizenmanager\
--enable-ext-alfec\
--disable-tizenipc\
- --disable-static
+ --disable-static\
+ --with-native-formats\
+ --with-gles2
make %{?jobs:-j%jobs}
--enable-ext-wfdtizenmanager\
--enable-ext-alfec\
--disable-tizenipc\
- --disable-static
+ --disable-static\
+ --with-native-formats\
+ --with-gles2
+
make %{?jobs:-j%jobs}
--- /dev/null
+SUBDIRS = src
--- /dev/null
+plugin_LTLIBRARIES = libgstvideo360.la
+
+# sources used to compile this plug-in
+libgstvideo360_la_SOURCES = gstvideo360.c\
+ gstvideo360-gl.c\
+ gstvideo360-equirectangular.c\
+ gstvideo360-formats.c
+
+# compiler and linker flags used to compile this plugin, set in configure.ac
+libgstvideo360_la_CFLAGS = $(GST_CFLAGS)\
+ $(TBM_CFLAGS)\
+ $(MMCOMMON_CFLAGS)\
+ $(GLES_CFLAGS)\
+ -Wdouble-promotion\
+ -fsingle-precision-constant
+libgstvideo360_la_LIBADD = $(GST_LIBS) $(TBM_LIBS) $(GLES_LIBS)
+libgstvideo360_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstvideo360_la_LIBTOOLFLAGS = --tag=disable-static
+
+# headers we need but don't want installed
+noinst_HEADERS = gstvideo360.h video360.h
--- /dev/null
+/*
+ * Video360 GStreamer plug-in
+ * Copyright (C) 2017 Samsung R&D Institute Ukraine
+ * All rights reserved.
+ *
+ * @author: Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gstvideo360.h"
+
+const char *equirectangular_vertex_source = "\
+ #version 100 \n\
+ \n\
+ precision highp float; \n\
+ \n\
+ attribute vec2 position; \n\
+ uniform vec2 fov; /* Horizontal / vertical field of view */ \n\
+ uniform float zoom; /* Zoom factor */ \n\
+ varying vec2 pos; \n\
+ \n\
+ void main() \n\
+ { \n\
+ pos = vec2 (position.x * fov.x * zoom, -position.y * fov.y * zoom); \n\
+ gl_Position = vec4 (position, 0.0, 1.0); \n\
+ } \n\
+";
+
+const char *equirectangular_fragment_source = "\
+ #version 100 \n\
+ \n\
+ #extension GL_OES_EGL_image_external : require \n\
+ \n\
+ precision highp float; \n\
+ \n\
+ varying vec2 pos; /* in NDC [-1, 1] */ \n\
+ \n\
+ uniform samplerExternalOES tex; /* Input texture - video frame */ \n\
+ uniform vec3 dov; /* Direction of view */ \n\
+ /* Normalized cropped fields: x - left, y - bottom, z - right, w - top */ \n\
+ uniform vec4 crops; \n\
+ \n\
+ #define M_PI 3.14159265358979323846 /* pi */ \n\
+ #define lambda0 dov.x \n\
+ #define phi1 dov.y \n\
+ \n\
+ vec2 texel_coordinates (vec2 position) \n\
+ { \n\
+ float longitude, latitude; /* in radians */ \n\
+ float ro; /* radius from projection centre to the point */ \n\
+ float c; /* angular distance of the point from the center */ \n\
+ float cos_c, sin_c, ctg_c, cos_phi1, sin_phi1; \n\
+ float x, y; /* in texture coordinates [0, 1] */ \n\
+ /* vec4 gl_FragCoord; (x, y) in screen coordinates with bias of 0.5 */ \n\
+ \n\
+ sin_phi1 = sin (phi1); \n\
+ cos_phi1 = cos (phi1); \n\
+ \n\
+ /* We don't check for (position.x != 0.0 || position.y != 0.0) as GL \n\
+ * seems to kindly skip (0.0, 0.0) coordinate \n\
+ */ \n\
+ /* Calculate c and ro */ \n\
+ ro = length (position); \n\
+ c = atan (ro); \n\
+ sin_c = sin (c); \n\
+ cos_c = cos (c); \n\
+ ctg_c = cos_c / sin_c; \n\
+ \n\
+ /* Calculate longitude and latitude of the point in radians */ \n\
+ longitude = lambda0 + \n\
+ atan (position.x, \n\
+ ro * cos_phi1 * ctg_c - position.y * sin_phi1); \n\
+ latitude = asin (cos_c * sin_phi1 + position.y * sin_c * cos_phi1 / ro); \n\
+ \n\
+#ifndef GL_FRAGMENT_PRECISION_HIGH \n\
+ /* Take small region in the centre as it is. This eliminates 'dot' \n\
+ * artifact caused by lack of precision. \n\
+ * */ \n\
+ if (abs (position.x) <= 1.0 / 128.0 && \n\
+ abs (position.y) <= 1.0 / 128.0) { \n\
+ longitude = lambda0 + position.x; \n\
+ latitude = phi1 + position.y; \n\
+ } \n\
+#endif \n\
+ \n\
+ x = fract (longitude / (M_PI * 2.0) + 1.5); \n\
+ y = 0.5 - latitude / M_PI; \n\
+ \n\
+ if (y > crops.w || y < crops.y || x < crops.x || x > crops.z) { \n\
+ /* Discard the fragment if it is outside of the input frame */ \n\
+ discard; \n\
+ } else { \n\
+ /* Convert coordinates to input frame */ \n\
+ x -= crops.x; \n\
+ y -= crops.y; \n\
+ } \n\
+ \n\
+ return vec2 (x, y); \n\
+ } \n\
+ \n\
+ vec4 get_texel () \n\
+ { \n\
+#ifdef GL_FRAGMENT_PRECISION_HIGH \n\
+ return texture2D (tex, texel_coordinates (pos)); \n\
+#else \n\
+ /* Add some alchemy here to compensate horisontal pixels 'rotation' \n\
+ * caused by lack of precision (in arctangent function in particular). \n\
+ * */ \n\
+ vec4 color; \n\
+ vec2 c, c_r, c_rr; \n\
+ \n\
+ c = texel_coordinates (pos); \n\
+ c_r = texel_coordinates (vec2 (pos.x + 1.0/512.0, pos.y)); \n\
+ c_rr = texel_coordinates (vec2 (pos.x + 2.0/512.0, pos.y)); \n\
+ \n\
+ if (c.x == c_r.x) \n\
+ color = mix (texture2D (tex, c), texture2D (tex, c_rr), 0.33); \n\
+ else if (c.x == c_rr.x) \n\
+ color = mix (texture2D (tex, c), texture2D (tex, c_r), 0.67); \n\
+ else \n\
+ color = mix (texture2D (tex, c), texture2D (tex, c_r), 0.5); \n\
+ \n\
+ return color; \n\
+#endif \n\
+ } \n\
+";
+
+const char *direct_colors_fragment_source = "\
+ \n\
+ vec4 get_texel (); \n\
+ \n\
+ void main() \n\
+ { \n\
+ \n\
+ gl_FragColor = get_texel (); \n\
+ } \n\
+";
+
+const char *reverse_colors_fragment_source = "\
+ \n\
+ vec4 get_texel (); \n\
+ \n\
+ void main() \n\
+ { \n\
+ \n\
+ gl_FragColor = get_texel ().bgra; \n\
+ } \n\
+";
+
+gboolean
+_equirectangular_bind_vertex_buffers (GstVideo360 * this)
+{
+ static const float vertices[] = {
+ -1.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, -1.0f,
+ -1.0f, -1.0f
+ };
+ static const GLubyte elements[] = {
+ 0, 1, 2,
+ 2, 3, 0
+ };
+
+ glGenBuffers (2, this->bo_ids);
+ glBindBuffer (GL_ARRAY_BUFFER, this->bo_ids[0]);
+ glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
+ glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, this->bo_ids[1]);
+ glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (elements), elements,
+ GL_STATIC_DRAW);
+
+ return !GL_ERROR;
+}
+
+gboolean
+_equirectangular_program_attribs (GstVideo360 * this)
+{
+ GLint pos_attrib;
+
+ pos_attrib = glGetAttribLocation (this->prog_id, "position");
+ glVertexAttribPointer (pos_attrib, 2, GL_FLOAT, GL_FALSE, 2 * sizeof (float),
+ 0);
+ glEnableVertexAttribArray (pos_attrib);
+
+ glUniform1i (glGetUniformLocation (this->prog_id, "tex"), 1);
+
+ this->fov_uni = glGetUniformLocation (this->prog_id, "fov");
+ gst_video360_set_fov_gl (this);
+
+ this->dov_uni = glGetUniformLocation (this->prog_id, "dov");
+ gst_video360_set_dov_gl (this);
+
+ this->crops_uni = glGetUniformLocation (this->prog_id, "crops");
+ gst_video360_set_crops_gl (this);
+
+ this->zoom_uni = glGetUniformLocation (this->prog_id, "zoom");
+ gst_video360_set_zoom_gl (this);
+
+ return !GL_ERROR;
+}
--- /dev/null
+/*
+ * Video360 GStreamer plug-in
+ * Copyright (C) 2017 Samsung R&D Institute Ukraine
+ * All rights reserved.
+ *
+ * @author: Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gstvideo360.h"
+
+gboolean
+gst_video360_standard_formats_renderer (GstVideo360 * this, void *in_data,
+ void *out_data)
+{
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ tbm_surface_h out_tbm_surface;
+ EGLImage out_egl_image;
+#endif
+#endif
+#endif
+ int i = 0;
+ gboolean ret = TRUE;
+
+#ifdef GST_TIZEN_USE_TIMING
+ struct timespec start_time, stop_time;
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start_time);
+#endif
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ do {
+ memcpy (this->input_image_ptr[i], in_data,
+ this->input_frame_planes[i].size);
+ in_data += this->input_frame_planes[i].size;
+ } while (++i < this->input_surf_info.num_planes);
+
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ if (!(out_tbm_surface =
+ tbm_surface_internal_create_with_flags (this->output_frame_width,
+ this->output_frame_height, TBM_FORMAT_XBGR8888, TBM_BO_WC))) {
+ GST_ERROR ("tbm_surface_create failed");
+ ret = FALSE;
+ }
+
+ out_egl_image = this->eglCreateImage (this->egl_display,
+ EGL_NO_CONTEXT, EGL_NATIVE_SURFACE_TIZEN,
+ (EGLClientBuffer) out_tbm_surface, NULL);
+
+ if (ret && out_egl_image == EGL_NO_IMAGE_KHR) {
+ EGL_ERROR;
+ GST_ERROR ("eglCreateImageKHR failed");
+ ret = FALSE;
+ } else {
+ glActiveTexture (GL_TEXTURE0);
+ this->glEGLImageTargetTexture2D (GL_TEXTURE_2D,
+ (GLeglImageOES) out_egl_image);
+
+ glActiveTexture (GL_TEXTURE1);
+ }
+#endif
+
+#else
+ /* This code is not implemented and must be done in platform specific way.
+ * As an option a standard GL approach might be considered on systems with
+ * optimized driver:
+ * glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, this->input_frame_width,
+ * this->input_frame_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, in_data);
+ * ret = !GL_ERROR;
+ * */
+#error "Sampler texture upload code not implemented"
+#endif
+#else
+ /* Space for alternate platform code */
+#endif
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &stop_time);
+ this->uploads_nanoseconds += (stop_time.tv_sec - start_time.tv_sec) *
+ 1000000000;
+ this->uploads_nanoseconds += stop_time.tv_nsec - start_time.tv_nsec;
+#endif
+
+ ret = ret && gst_video360_draw_gl (this);
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start_time);
+#endif
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ ((MMVideoBuffer *) out_data)->handle.bo[0] =
+ tbm_bo_ref (tbm_surface_internal_get_bo (out_tbm_surface, 0));
+ this->eglDestroyImage (this->egl_display, out_egl_image);
+ tbm_surface_destroy (out_tbm_surface);
+#else
+ memcpy (out_data, this->output_image_ptr, this->output_frame_buffer_size);
+#endif
+#else
+ glReadPixels (0, 0, this->output_frame_width, this->output_frame_height,
+ GL_RGBA, GL_UNSIGNED_BYTE, out_data);
+ ret = !GL_ERROR && ret;
+#endif
+#else
+ /* Space for alternate platform code */
+#endif
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &stop_time);
+ this->downloads_nanoseconds += (stop_time.tv_sec - start_time.tv_sec) *
+ 1000000000;
+ this->downloads_nanoseconds += stop_time.tv_nsec - start_time.tv_nsec;
+#endif
+
+ return ret;
+}
+
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+#if 0
+/* This piece of code is left here for reference purposes only. It explains structure of information for SN12 video format */
+typedef struct
+{
+ MMVideoBufferType type; /* MM_VIDEO_BUFFER_TYPE_TBM_BO - bo field of MMVideoBufferHandle structure to be used. */
+ MMPixelFormatType format; /* MM_PIXEL_FORMAT_NV12 */
+ int plane_num; /* 0 */
+ int width[MM_VIDEO_BUFFER_PLANE_MAX]; /* buffer width: width[0] - Y plane, width[1] - UV plane */
+ int height[MM_VIDEO_BUFFER_PLANE_MAX]; /* buffer height: height[0] - Y plane, height[1] - UV plane */
+ int stride_width[MM_VIDEO_BUFFER_PLANE_MAX]; /* buffer horizontal stride (in bytes): stride_width[0] - Y plane, stride_width[1] - UV plane */
+ int stride_height[MM_VIDEO_BUFFER_PLANE_MAX]; /* buffer vertical stride (in lines): stride_height[0] - Y plane, stride_height[1] - UV plane */
+ int size[MM_VIDEO_BUFFER_PLANE_MAX]; /* size of planes in bytes (stride_width * stride_height): size[0] - Y plane, size[1] - UV plane */
+ void *data[MM_VIDEO_BUFFER_PLANE_MAX]; /* pointers to planes: data[0] - Y plane, data[1] - UV plane (data[1] == data[0] + size[0]) */
+ int handle_num; /* 0 */
+ int handle_size[MM_VIDEO_BUFFER_PLANE_MAX]; /* 0 0 */
+ MMVideoBufferHandle handle; /* buffer object addresses handle.bo[0] */
+ int is_secured; /* FALSE - user accessible. */
+ int flush_request; /* FALSE */
+} MMVideoBuffer;
+#endif
+
+gboolean
+gst_video360_native_formats_renderer (GstVideo360 * this, void *in_data,
+ void *out_data)
+{
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ tbm_surface_h in_tbm_surface = NULL, out_tbm_surface = NULL;
+ EGLImage in_egl_image, out_egl_image;
+#endif
+#endif
+ gboolean ret = TRUE;
+
+#ifdef GST_TIZEN_USE_TIMING
+ struct timespec start_time, stop_time;
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start_time);
+#endif
+
+ if (in_data &&
+ ((MMVideoBuffer *) in_data)->type == MM_VIDEO_BUFFER_TYPE_TBM_BO &&
+ ((MMVideoBuffer *) in_data)->size[0]) {
+
+ /* Note: omx provides separate BOs for each plane when sprd provides a
+ * single one for both.
+ * */
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ if (!(in_tbm_surface =
+ tbm_surface_internal_create_with_bos (&this->input_surf_info,
+ (tbm_bo *) ((MMVideoBuffer *) in_data)->handle.bo,
+ ((MMVideoBuffer *) in_data)->handle.bo[1] ? this->
+ input_surf_info.num_planes : 1)) ||
+ !(out_tbm_surface =
+ tbm_surface_internal_create_with_flags (this->output_frame_width,
+ this->output_frame_height, TBM_FORMAT_XBGR8888, TBM_BO_WC))) {
+ GST_ERROR ("tbm_surface_create failed");
+ ret = FALSE;
+ }
+
+ in_egl_image = this->eglCreateImage (this->egl_display,
+ EGL_NO_CONTEXT, EGL_NATIVE_SURFACE_TIZEN,
+ (EGLClientBuffer) in_tbm_surface, NULL);
+
+ out_egl_image = this->eglCreateImage (this->egl_display,
+ EGL_NO_CONTEXT, EGL_NATIVE_SURFACE_TIZEN,
+ (EGLClientBuffer) out_tbm_surface, NULL);
+
+ if (ret
+ && (in_egl_image == EGL_NO_IMAGE_KHR ||
+ out_egl_image == EGL_NO_IMAGE_KHR)) {
+ EGL_ERROR;
+ GST_ERROR ("eglCreateImageKHR failed");
+ ret = FALSE;
+ } else {
+ glActiveTexture (GL_TEXTURE0);
+ this->glEGLImageTargetTexture2D (GL_TEXTURE_2D,
+ (GLeglImageOES) out_egl_image);
+
+ glActiveTexture (GL_TEXTURE1);
+ this->glEGLImageTargetTexture2D (GL_TEXTURE_EXTERNAL_OES,
+ (GLeglImageOES) in_egl_image);
+ }
+#else
+ /* This code is removed as no longer works and must be added if there will
+ * be a need.
+ * */
+#endif
+#else
+ /* Space for alternate platform code */
+#endif
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &stop_time);
+ this->uploads_nanoseconds += (stop_time.tv_sec - start_time.tv_sec) *
+ 1000000000;
+ this->uploads_nanoseconds += stop_time.tv_nsec - start_time.tv_nsec;
+#endif
+
+ ret = ret && gst_video360_draw_gl (this);
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start_time);
+#endif
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ ((MMVideoBuffer *) out_data)->handle.bo[0] =
+ tbm_bo_ref (tbm_surface_internal_get_bo (out_tbm_surface, 0));
+ this->eglDestroyImage (this->egl_display, in_egl_image);
+ this->eglDestroyImage (this->egl_display, out_egl_image);
+ tbm_surface_destroy (in_tbm_surface);
+ tbm_surface_destroy (out_tbm_surface);
+#else
+ glReadPixels (0, 0, this->output_frame_width, this->output_frame_height,
+ GL_RGBA, GL_UNSIGNED_BYTE, out_data);
+ ret = !GL_ERROR && ret;
+#endif
+#else
+ /* Space for alternate platform code */
+#endif
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &stop_time);
+ this->downloads_nanoseconds += (stop_time.tv_sec - start_time.tv_sec) *
+ 1000000000;
+ this->downloads_nanoseconds += stop_time.tv_nsec - start_time.tv_nsec;
+#endif
+ }
+
+ return ret;
+}
+#endif
--- /dev/null
+/*
+ * Video360 GStreamer plug-in
+ * Copyright (C) 2017 Samsung R&D Institute Ukraine
+ * All rights reserved.
+ *
+ * @author: Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "gstvideo360.h"
+
+/* Below functions return TRUE on success and FALSE otherwise */
+static gboolean _init_gl_platform (GstVideo360 * this);
+static gboolean _prepare_gl_framebuffer (GstVideo360 * this);
+static gboolean _prepare_gl_program (GstVideo360 * this,
+ char const *const *vertex_sources, int vertex_count,
+ char const *const *fragment_sources, int fragment_count);
+
+/* Below functions return TRUE on errors and FALSE otherwise */
+static gboolean _check_gl_shader_compile_error (GLuint shader,
+ const char *name);
+#define GL_SHADER_ERROR(sh,n) _check_gl_shader_compile_error (sh, n)
+
+static gboolean _check_gl_program_link_error (GLuint program);
+#define GL_PROGRAM_ERROR(prog) _check_gl_program_link_error (prog)
+
+/* Returns TRUE if framebuffer is complete */
+static gboolean _check_gl_framebuffer ();
+
+gboolean
+gst_video360_init_gl (GstVideo360 * this)
+{
+ const GLubyte *string;
+ char const *vertex_sources[1];
+ char const *fragment_sources[2];
+ GLint param;
+
+ if (!_init_gl_platform (this))
+ return FALSE;
+
+ if (!(string = glGetString (GL_VERSION))) {
+ GST_ERROR ("OpenGL initialization failed");
+ return FALSE;
+ }
+ GST_INFO ("%s initialized", string);
+
+ string = glGetString (GL_VENDOR);
+ GST_INFO ("OpenGL ES vendor: %s", string);
+
+ string = glGetString (GL_RENDERER);
+ GST_INFO ("OpenGL ES renderer: %s", string);
+
+ string = glGetString (GL_SHADING_LANGUAGE_VERSION);
+ GST_INFO ("%s", string);
+
+ string = glGetString (GL_EXTENSIONS);
+ GST_INFO ("OpenGL ES extensions supported: %s", string);
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ if (strstr ((const char *) string, "GL_OES_EGL_image_external")) {
+ /* We need GL_OES_EGL_image_external extension which introduces new texture
+ * target TEXTURE_EXTERNAL_OES.
+ * GL_OES_EGL_image introduces the same call but for TEXTURE_2D target only.
+ * This target doesn't accept YUV EGL images.
+ * */
+ this->glEGLImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
+ eglGetProcAddress ("glEGLImageTargetTexture2DOES");
+ }
+ if (!this->glEGLImageTargetTexture2D) {
+ GST_ERROR ("GL_OES_EGL_image_external isn't supported");
+ return FALSE;
+ }
+#endif
+#else
+ /* Space for alternate platform code */
+#endif
+
+#ifdef GST_TIZEN_USE_MSAA
+ if (strstr ((const char *) string, "GL_EXT_multisampled_render_to_texture")) {
+ /* We optionally use GL_EXT_multisampled_render_to_texture extension
+ * for multi-sampled anti-aliasing (MSAA).
+ * */
+ this->glFramebufferTexture2DMultisample =
+ (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)
+ eglGetProcAddress ("glFramebufferTexture2DMultisampleEXT");
+ }
+#endif
+
+ if (!_prepare_gl_framebuffer (this))
+ return FALSE;
+
+ glGetIntegerv (GL_SAMPLE_BUFFERS, ¶m);
+ if (param > 0) {
+ glGetIntegerv (GL_SAMPLES, ¶m);
+ GST_INFO ("MSAA is enabled with %d samples", param);
+ } else {
+ GST_INFO ("No MSAA is used");
+ }
+
+ /* TODO(a.martynets): add support for more projection types:
+ * cubemap and mesh
+ */
+ if (this->projection_type == PROJECTION_TYPE_EQUIRECTANGULAR) {
+ vertex_sources[0] = equirectangular_vertex_source;
+ fragment_sources[0] = equirectangular_fragment_source;
+ fragment_sources[1] = this->input_frame_format == GST_VIDEO_FORMAT_BGRA ?
+ direct_colors_fragment_source : reverse_colors_fragment_source;
+ if (!_equirectangular_bind_vertex_buffers (this) ||
+ !_prepare_gl_program (this, vertex_sources, 1, fragment_sources, 2) ||
+ !_equirectangular_program_attribs (this))
+ return FALSE;
+ } else {
+ GST_ERROR
+ ("Equirectangular projection is the only supported at the moment");
+ return FALSE;
+ }
+
+ glClearColor (GAP_FILL_COLOUR_RED, GAP_FILL_COLOUR_GREEN,
+ GAP_FILL_COLOUR_BLUE, 1.0);
+ glViewport (0, 0, this->output_frame_width, this->output_frame_height);
+
+ return !GL_ERROR;
+}
+
+void
+gst_video360_deinit_gl (GstVideo360 * this)
+{
+ this->fov_uni = this->dov_uni = this->crops_uni = this->zoom_uni = -1;
+ if (this->prog_id) {
+ glUseProgram (0);
+ glDeleteProgram (this->prog_id);
+ this->prog_id = 0;
+ }
+ if (this->bo_ids[0]) {
+ glDeleteBuffers (2, this->bo_ids);
+ this->bo_ids[0] = this->bo_ids[1] = 0;
+ }
+ if (this->fbo_id) {
+ glBindFramebuffer (GL_FRAMEBUFFER, 0);
+ glDeleteFramebuffers (1, &this->fbo_id);
+ this->fbo_id = 0;
+ }
+ if (this->tex_ids[0]) {
+ glDeleteTextures (2, this->tex_ids);
+ this->tex_ids[0] = this->tex_ids[1] = 0;
+ }
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ if (this->input_egl_image != EGL_NO_IMAGE_KHR) {
+ this->eglDestroyImage (this->egl_display, this->input_egl_image);
+ this->input_egl_image = EGL_NO_IMAGE_KHR;
+ }
+ if (this->output_egl_image != EGL_NO_IMAGE_KHR) {
+ this->eglDestroyImage (this->egl_display, this->output_egl_image);
+ this->output_egl_image = EGL_NO_IMAGE_KHR;
+ }
+ if (this->input_tbm_surface) {
+ tbm_surface_destroy (this->input_tbm_surface);
+ this->input_tbm_surface = NULL;
+ }
+ if (this->output_tbm_surface) {
+ tbm_surface_destroy (this->output_tbm_surface);
+ this->output_tbm_surface = NULL;
+ }
+#endif
+ if (this->egl_display != EGL_NO_DISPLAY) {
+ /* Free-up context */
+ eglMakeCurrent (this->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ this->egl_config = 0;
+ if (this->egl_surface != EGL_NO_SURFACE) {
+ eglDestroySurface (this->egl_display, this->egl_surface);
+ this->egl_surface = EGL_NO_SURFACE;
+ }
+ /* Unlink from display and destroy unused resources */
+ eglTerminate (this->egl_display);
+ this->egl_display = EGL_NO_DISPLAY;
+ }
+#ifdef GST_TIZEN_GL_WINDOW_WAYLAND
+ if (this->wl_display) {
+ /* Disconnect from the native display */
+ wl_display_disconnect (this->wl_display);
+ this->wl_display = NULL;
+ }
+#else
+ /* Space for alternate windowing system code */
+#endif /* GL_WINDOW_WAYLAND */
+#else
+ /* Space for alternate platform code */
+#endif /* GL_PLATFORM_EGL */
+}
+
+static gboolean
+_init_gl_platform (GstVideo360 * this)
+{
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+ EGLint num_config;
+ EGLint ver_major;
+ EGLint ver_minor;
+ const char *string;
+ static const EGLint rgb_config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ };
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ if (eglBindAPI (EGL_OPENGL_ES_API) != EGL_TRUE || EGL_ERROR) {
+ GST_ERROR ("eglBindAPI failed");
+ return FALSE;
+ }
+#ifdef GST_TIZEN_GL_WINDOW_WAYLAND
+ this->wl_display = wl_display_connect (NULL);
+ if (this->wl_display == NULL) {
+ GST_ERROR ("wl_display_connect failed");
+ return FALSE;
+ }
+
+ /* get an EGL display connection */
+ this->egl_display = eglGetDisplay (this->wl_display);
+ if (EGL_ERROR || this->egl_display == EGL_NO_DISPLAY) {
+ GST_ERROR ("eglGetDisplay failed");
+ return FALSE;
+ }
+#else
+ /* Space for alternate windowing system code */
+#endif /* GL_WINDOW_WAYLAND */
+
+ /* initialize the EGL display connection */
+ eglInitialize (this->egl_display, &ver_major, &ver_minor);
+ if (EGL_ERROR) {
+ GST_ERROR ("eglInitialize failed");
+ return FALSE;
+ }
+ GST_INFO ("EGL platform initialized");
+
+ string = eglQueryString (this->egl_display, EGL_VERSION);
+ GST_INFO ("EGL version: %s", string);
+
+ string = eglQueryString (this->egl_display, EGL_VENDOR);
+ GST_INFO ("EGL vendor: %s", string);
+
+ string = eglQueryString (this->egl_display, EGL_EXTENSIONS);
+ GST_INFO ("EGL extensions supported: %s", string);
+
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ if (!strstr (string, "EGL_TIZEN_image_native_surface")) {
+ GST_ERROR ("EGL_TIZEN_image_native_surface isn't supported");
+ return FALSE;
+ }
+
+ if (ver_major > 1 || (ver_major == 1 && ver_minor > 4)) {
+ this->eglCreateImage =
+ (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress ("eglCreateImage");
+ this->eglDestroyImage =
+ (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress ("eglDestroyImage");
+ } else {
+ /* EGL_TIZEN_image_native_surface assumes EGL_KHR_image_base extension */
+ this->eglCreateImage =
+ (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress ("eglCreateImageKHR");
+ this->eglDestroyImage =
+ (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress ("eglDestroyImageKHR");
+ }
+ if (!this->eglCreateImage || !this->eglDestroyImage) {
+ GST_ERROR ("EGL_KHR_image isn't supported");
+ return FALSE;
+ }
+#endif
+
+ /* get an appropriate EGL frame buffer configuration */
+ if (eglChooseConfig (this->egl_display,
+ rgb_config_attribs, &this->egl_config, 1, &num_config) != EGL_TRUE ||
+ num_config != 1 || EGL_ERROR) {
+ GST_ERROR ("eglChooseConfig failed");
+ return FALSE;
+ }
+
+ /* Create dummy surface - framebuffer will be used instead */
+ this->egl_surface = eglCreatePbufferSurface (this->egl_display,
+ this->egl_config, NULL);
+ if (EGL_ERROR || this->egl_surface == EGL_NO_SURFACE) {
+ GST_ERROR ("eglCreatePbufferSurface failed");
+ return FALSE;
+ }
+
+ /* create an EGL rendering context */
+ this->egl_context =
+ eglCreateContext (this->egl_display, this->egl_config, EGL_NO_CONTEXT,
+ context_attribs);
+ if (EGL_ERROR || this->egl_context == EGL_NO_CONTEXT) {
+ GST_ERROR ("eglCreateContext failed");
+ return FALSE;
+ }
+
+ /* Make the context current */
+ eglMakeCurrent (this->egl_display, this->egl_surface, this->egl_surface,
+ this->egl_context);
+ if (EGL_ERROR) {
+ GST_ERROR ("eglMakeCurrent failed");
+ return FALSE;
+ }
+
+ EGLint value;
+ eglGetConfigAttrib (this->egl_display, this->egl_config, EGL_BUFFER_SIZE,
+ &value);
+
+ return TRUE;
+#else
+ /* Space for alternate platform code */
+ return FALSE;
+#endif /* GL_PLATFORM_EGL */
+}
+
+static gboolean
+_prepare_gl_framebuffer (GstVideo360 * this)
+{
+ GLint samples;
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ tbm_surface_info_s out_info;
+ int i;
+
+ if (!(this->input_tbm_surface = tbm_surface_create (this->input_frame_width,
+ this->input_frame_height,
+ this->input_frame_format == GST_VIDEO_FORMAT_BGRA ?
+ TBM_FORMAT_XBGR8888 :
+ this->input_frame_format == GST_VIDEO_FORMAT_I420 ?
+ TBM_FORMAT_YUV420 : TBM_FORMAT_NV12)) ||
+ !(this->output_tbm_surface = tbm_surface_create (this->output_frame_width,
+ this->output_frame_height, TBM_FORMAT_XBGR8888))) {
+ GST_ERROR ("tbm_surface_create failed");
+ return FALSE;
+ }
+ if (tbm_surface_get_info (this->input_tbm_surface, &this->input_surf_info) !=
+ TBM_SURFACE_ERROR_NONE ||
+ tbm_surface_get_info (this->output_tbm_surface, &out_info) !=
+ TBM_SURFACE_ERROR_NONE) {
+ GST_ERROR ("tbm_surface_get_info failed");
+ return FALSE;
+ }
+
+ /* TODO(a.martynets): add checks for input/output frames stride to match with
+ * the TBM surfaces strides.
+ */
+ for (i = 0; i < TBM_SURF_PLANE_MAX; i++) {
+ /* Note: ptr pointer in the tbm_surface_info_s has offset already added */
+ this->input_image_ptr[i] = this->input_surf_info.planes[i].ptr;
+ }
+ this->input_egl_image = this->eglCreateImage (this->egl_display,
+ EGL_NO_CONTEXT, EGL_NATIVE_SURFACE_TIZEN,
+ (EGLClientBuffer) this->input_tbm_surface, NULL);
+
+ this->output_image_ptr = out_info.planes[0].ptr;
+ this->output_egl_image = this->eglCreateImage (this->egl_display,
+ EGL_NO_CONTEXT, EGL_NATIVE_SURFACE_TIZEN,
+ (EGLClientBuffer) this->output_tbm_surface, NULL);
+
+ if (this->input_egl_image == EGL_NO_IMAGE_KHR ||
+ this->output_egl_image == EGL_NO_IMAGE_KHR) {
+ EGL_ERROR;
+ return FALSE;
+ }
+#endif
+
+ /* Prepare textures */
+ glGenTextures (2, this->tex_ids);
+ /* Texture to render to */
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, this->tex_ids[0]);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ this->glEGLImageTargetTexture2D (GL_TEXTURE_2D,
+ (GLeglImageOES) this->output_egl_image);
+#else
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, this->output_frame_width,
+ this->output_frame_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+#endif
+ if (GL_ERROR) {
+ GST_ERROR ("Framebuffer texture preparation failed");
+ return FALSE;
+ }
+
+ /* Input sampler texture */
+ glActiveTexture (GL_TEXTURE1);
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ glBindTexture (GL_TEXTURE_EXTERNAL_OES, this->tex_ids[1]);
+ glTexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ this->glEGLImageTargetTexture2D (GL_TEXTURE_EXTERNAL_OES,
+ (GLeglImageOES) this->input_egl_image);
+#else
+ /* Space for alternate code which might start with lines:
+ * glBindTexture (GL_TEXTURE_2D, this->tex_ids[1]);
+ * glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ * glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ * */
+#endif
+ if (GL_ERROR) {
+ GST_ERROR ("Sampler texture preparation failed");
+ return FALSE;
+ }
+#ifndef USE_EGLIMAGE
+ /* Note: for non-EGLImage based implementation it is critical to switch to
+ * proper sampler texture unit, e.g.:
+ * glActiveTexture (GL_TEXTURE1);
+ * */
+#endif
+
+ /* Prepare framebuffers */
+ glGenFramebuffers (1, &this->fbo_id);
+ glBindFramebuffer (GL_FRAMEBUFFER, this->fbo_id);
+ if (this->glFramebufferTexture2DMultisample) {
+ /* Make the framebuffer multisampled with GL_MAX_SAMPLES_EXT samples */
+ glGetIntegerv (GL_MAX_SAMPLES_EXT, &samples);
+ this->glFramebufferTexture2DMultisample (GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->tex_ids[0], 0, samples);
+ } else {
+ /* Make the framebuffer singlesampled */
+ glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ this->tex_ids[0], 0);
+ }
+ if (GL_ERROR) {
+ GST_ERROR ("Framebuffer preparation failed");
+ return FALSE;
+ }
+
+ return _check_gl_framebuffer ();
+}
+
+/* Renderers helper function - performs GL calls. */
+gboolean
+gst_video360_draw_gl (GstVideo360 * this)
+{
+#ifdef GST_TIZEN_USE_TIMING
+ struct timespec start_time, stop_time;
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start_time);
+#endif
+
+ glClear (GL_COLOR_BUFFER_BIT);
+ glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);
+ glFinish ();
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &stop_time);
+ this->rendering_nanoseconds += (stop_time.tv_sec - start_time.tv_sec) *
+ 1000000000;
+ this->rendering_nanoseconds += stop_time.tv_nsec - start_time.tv_nsec;
+#endif
+ return !GL_ERROR;
+}
+
+/* GL uniforms setters */
+void
+gst_video360_set_fov_gl (GstVideo360 * this)
+{
+ if (!this->passthrough && this->fov_uni != -1) {
+ pthread_mutex_lock (&this->gl_mutex);
+ glUniform2f (this->fov_uni, (M_PI * this->horizontal_fov_degree) / 360.0,
+ (M_PI * this->vertical_fov_degree) / 360.0);
+ GL_ERROR;
+ pthread_mutex_unlock (&this->gl_mutex);
+ }
+}
+
+void
+gst_video360_set_dov_gl (GstVideo360 * this)
+{
+ if (!this->passthrough && this->dov_uni != -1) {
+ pthread_mutex_lock (&this->gl_mutex);
+ glUniform3f (this->dov_uni, this->pose_yaw_radians,
+ this->pose_pitch_radians, this->pose_roll_radians);
+ GL_ERROR;
+ pthread_mutex_unlock (&this->gl_mutex);
+ }
+}
+
+void
+gst_video360_set_crops_gl (GstVideo360 * this)
+{
+ if (!this->passthrough && this->crops_uni != -1) {
+ pthread_mutex_lock (&this->gl_mutex);
+ glUniform4f (this->crops_uni,
+ (float) this->projection_bounds_left / (float) this->panorama_width,
+ (float) this->projection_bounds_bottom / (float) this->panorama_height,
+ 1.0 -
+ (float) this->projection_bounds_right / (float) this->panorama_width,
+ 1.0 -
+ (float) this->projection_bounds_top / (float) this->panorama_height);
+ GL_ERROR;
+ pthread_mutex_unlock (&this->gl_mutex);
+ }
+}
+
+void
+gst_video360_set_zoom_gl (GstVideo360 * this)
+{
+ if (!this->passthrough && this->zoom_uni != -1) {
+ pthread_mutex_lock (&this->gl_mutex);
+ glUniform1f (this->zoom_uni, this->zoom);
+ GL_ERROR;
+ pthread_mutex_unlock (&this->gl_mutex);
+ }
+}
+
+/* GL program compilation and linking */
+static gboolean
+_prepare_gl_program (GstVideo360 * this,
+ char const *const *vertex_sources, int vertex_count,
+ char const *const *fragment_sources, int fragment_count)
+{
+ GLuint vert;
+ GLuint frag;
+
+ vert = glCreateShader (GL_VERTEX_SHADER);
+ glShaderSource (vert, vertex_count, vertex_sources, NULL);
+ glCompileShader (vert);
+
+ if (GL_SHADER_ERROR (vert, "Vertex"))
+ return FALSE;
+
+ frag = glCreateShader (GL_FRAGMENT_SHADER);
+ glShaderSource (frag, fragment_count, fragment_sources, NULL);
+ glCompileShader (frag);
+
+ if (GL_SHADER_ERROR (frag, "Fragment"))
+ return FALSE;
+
+ this->prog_id = glCreateProgram ();
+ glAttachShader (this->prog_id, vert);
+ glAttachShader (this->prog_id, frag);
+
+ glLinkProgram (this->prog_id);
+ glUseProgram (this->prog_id);
+ glDeleteShader (frag);
+ glDeleteShader (vert);
+
+ if (GL_PROGRAM_ERROR (this->prog_id) || GL_ERROR)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Returns TRUE if framebuffer is complete */
+static gboolean
+_check_gl_framebuffer ()
+{
+ GLenum fb_status;
+ const char *string;
+
+ switch (fb_status = glCheckFramebufferStatus (GL_FRAMEBUFFER)) {
+ case GL_FRAMEBUFFER_COMPLETE:
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ string = "Framebuffer incomplete attachement";
+ break;
+#ifdef GST_TIZEN_GL_API_GLES2
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ string = "Framebuffer incomplete dimentions";
+ break;
+#endif
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ string = "Framebuffer incomplete missing attachement";
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ string = "Framebuffer unsupported";
+ break;
+ case GL_INVALID_ENUM:
+ string = "Not a framebuffer";
+ break;
+ case GL_INVALID_OPERATION:
+ string = "Invalid operation";
+ break;
+ case 0:
+ string = "Internal error";
+ break;
+ default:
+ string = "Undefined error";
+ break;
+ }
+
+ if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
+ GST_ERROR ("glCheckFramebufferStatus failed: %s (%#x)", string, fb_status);
+ return FALSE;
+ }
+
+ GST_INFO ("OpenGL framebuffer is complete");
+ return TRUE;
+}
+
+/* Returns TRUE if there were errors */
+gboolean
+_check_gl_error (const char *file, int line)
+{
+ GLenum err;
+ const char *error = NULL;
+
+ while ((err = glGetError ()) != GL_NO_ERROR) {
+
+ switch (err) {
+ case GL_INVALID_ENUM:
+ error = "INVALID_ENUM";
+ break;
+ case GL_INVALID_VALUE:
+ error = "INVALID_VALUE";
+ break;
+ case GL_INVALID_OPERATION:
+ error = "INVALID_OPERATION";
+ break;
+#ifdef GST_TIZEN_GL_API_OPENGL
+ case GL_STACK_OVERFLOW:
+ error = "GL_STACK_OVERFLOW";
+ break;
+ case GL_STACK_UNDERFLOW:
+ error = "GL_STACK_UNDERFLOW";
+ break;
+#endif
+ case GL_OUT_OF_MEMORY:
+ error = "OUT_OF_MEMORY";
+ break;
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ error = "INVALID_FRAMEBUFFER_OPERATION";
+ break;
+ default:
+ error = "Unknown error";
+ }
+ GST_ERROR ("%s:%u: GL call failed: %s (%#x)", file, line, error, err);
+ }
+ return error != NULL;
+}
+
+/* Returns TRUE if there were errors */
+static gboolean
+_check_gl_shader_compile_error (GLuint shader, const char *name)
+{
+ GLint status;
+ GLint len;
+ char *buffer;
+
+ glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
+
+ if (status != GL_TRUE) {
+ glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &len);
+ if ((buffer = g_try_malloc (len))) {
+ glGetShaderInfoLog (shader, len, NULL, buffer);
+ GST_ERROR ("%s shader compilation failed: %s", name, buffer);
+ g_free (buffer);
+ }
+ return TRUE;
+ }
+
+ GST_INFO ("%s shader successfully compiled", name);
+ return FALSE;
+}
+
+/* Returns TRUE if there were errors */
+static gboolean
+_check_gl_program_link_error (GLuint program)
+{
+ GLint status;
+ GLint len;
+ char *buffer;
+
+ glGetProgramiv (program, GL_LINK_STATUS, &status);
+
+ if (status != GL_TRUE) {
+ glGetProgramiv (program, GL_INFO_LOG_LENGTH, &len);
+ if ((buffer = g_try_malloc (len))) {
+ glGetProgramInfoLog (program, len, NULL, buffer);
+ GST_ERROR ("Program link failed: %s", buffer);
+ g_free (buffer);
+ }
+ return TRUE;
+ }
+
+ GST_INFO ("Program successfully linked");
+ return FALSE;
+}
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+/* Returns TRUE if there were errors */
+gboolean
+_check_egl_error (const char *file, int line)
+{
+ EGLint err_code;
+ const char *error;
+
+ switch (err_code = eglGetError ()) {
+ case EGL_SUCCESS:
+ break;
+ case EGL_NOT_INITIALIZED:
+ error = "EGL_NOT_INITIALIZED";
+ break;
+ case EGL_BAD_ACCESS:
+ error = "EGL_BAD_ACCESS";
+ break;
+ case EGL_BAD_ALLOC:
+ error = "EGL_BAD_ALLOC";
+ break;
+ case EGL_BAD_ATTRIBUTE:
+ error = "EGL_BAD_ATTRIBUTE";
+ break;
+ case EGL_BAD_CONTEXT:
+ error = "EGL_BAD_CONTEXT";
+ break;
+ case EGL_BAD_CONFIG:
+ error = "EGL_BAD_CONFIG";
+ break;
+ case EGL_BAD_CURRENT_SURFACE:
+ error = "EGL_BAD_CURRENT_SURFACE";
+ break;
+ case EGL_BAD_DISPLAY:
+ error = "EGL_BAD_DISPLAY";
+ break;
+ case EGL_BAD_SURFACE:
+ error = "EGL_BAD_SURFACE";
+ break;
+ case EGL_BAD_MATCH:
+ error = "EGL_BAD_MATCH";
+ break;
+ case EGL_BAD_PARAMETER:
+ error = "EGL_BAD_PARAMETER";
+ break;
+ case EGL_BAD_NATIVE_PIXMAP:
+ error = "EGL_BAD_NATIVE_PIXMAP";
+ break;
+ case EGL_BAD_NATIVE_WINDOW:
+ error = "EGL_BAD_NATIVE_WINDOW";
+ break;
+ case EGL_CONTEXT_LOST:
+ error = "EGL_CONTEXT_LOST";
+ break;
+ default:
+ error = "Unknown error";
+ }
+ if (err_code != EGL_SUCCESS) {
+ GST_ERROR ("%s:%u: EGL call failed: %s (%#x)", file, line, error, err_code);
+ return TRUE;
+ }
+ return FALSE;
+}
+#else
+ /* Space for alternate platform code */
+#endif
--- /dev/null
+/*
+ * Video360 GStreamer plug-in
+ * Copyright (C) 2017 Samsung R&D Institute Ukraine
+ * All rights reserved.
+ *
+ * @author: Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-video360
+ *
+ * Spatial Video GStreamer plugin
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch-1.0 filesrc location="test.mp4" ! decodebin ! video360 !
+ * waylandsink rotate=1 display-geometry-method=2
+ * ]|
+ * </refsect2>
+ */
+
+#include "gstvideo360.h"
+
+/* Debug category definition */
+GST_DEBUG_CATEGORY (video360_debug_category);
+
+/* Filter signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+/* TODO(a.martynets): add vertical and horizontal FOV degrees and aspect ratios
+ * as element's properties to be settable by the application to keep them the
+ * same values on both sides.
+ */
+enum
+{
+ PROP_0,
+ PROP_STEREO,
+ PROP_YAW,
+ PROP_PITCH,
+ PROP_ROLL,
+ PROP_PROJECTION,
+ PROP_TOP,
+ PROP_BOTTOM,
+ PROP_LEFT,
+ PROP_RIGHT,
+ PROP_HFOV,
+ PROP_VFOV,
+ PROP_ZOOM,
+ PROP_PASSTHROUGH,
+};
+
+/* The capabilities of the inputs and outputs.
+ *
+ * video/x-raw RGB/NV12/SN12 progressive is the only supported formats here.
+ */
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw, "
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ "format = (string) { BGRA, NV12, SN12, I420 }, "
+#else
+ "format = (string) { BGRA, NV12, I420 }, "
+#endif
+ "width = (int) [ 1, max ], "
+ "height = (int) [ 1, max ], " "framerate = (fraction) [ 0, max ]"
+ /* Interlaced mode removed from CAPS as this causes SPRD decoder to fail
+ * to link
+ * "interlace-mode = (string) progressive"
+ * */
+ )
+ );
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw, "
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ "format = (string) { SR32 }, "
+#else
+ "format = (string) { BGRA }, "
+#endif
+ "width = (int) [ 1, max ], "
+ "height = (int) [ 1, max ], " "framerate = (fraction) [ 0, max ]"
+ /* Interlaced mode removed from CAPS as this causes SPRD decoder to fail
+ * to link
+ * "interlace-mode = (string) progressive"
+ * */
+ )
+ );
+
+#define gst_video360_parent_class parent_class
+G_DEFINE_TYPE (GstVideo360, gst_video360, GST_TYPE_ELEMENT);
+
+static void gst_video360_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_video360_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static GstStateChangeReturn gst_video360_change_state (GstElement * element,
+ GstStateChange transition);
+
+static gboolean gst_video360_sink_event (GstPad * pad, GstObject * parent,
+ GstEvent * event);
+static gboolean gst_video360_src_event (GstPad * pad, GstObject * parent,
+ GstEvent * event);
+static GstFlowReturn gst_video360_chain (GstPad * pad, GstObject * parent,
+ GstBuffer * buf);
+
+/* Below function returns FALSE on failure and sets
+ * the video360_error_message string */
+static int allocate_local_resources (GstVideo360 * this);
+/* Free up resources */
+static void deallocate_local_resources (GstVideo360 * this);
+
+/* Register GTtypes for custom enums */
+#define GST_TYPE_VIDEO360_STEREOMODE (gst_video360_stereomode_get_type ())
+#define GST_TYPE_VIDEO360_PROJTYPE (gst_video360_projtype_get_type ())
+
+static GType
+gst_video360_stereomode_get_type (void)
+{
+ static GType video360_stereomode_type = 0;
+
+ if (!video360_stereomode_type) {
+ static GEnumValue stereomode_types[] = {
+ {MODE_MONOSCOPIC, "Single monoscopic view", "mono"},
+ {MODE_STEREOSCOPIC_TOP_BOTTOM, "Stereoscopic view, left eye on top half "
+ "and right eye at bottom half of the frame", "stereo-top-bottom"},
+ {MODE_STEREOSCOPIC_LEFT_RIGHT, "Stereoscopic view, left eye on left "
+ "half and right eye on right half of the frame",
+ "stereo-left-right"},
+ {MODE_STEREOSCOPIC_STEREO_MESH,
+ "Stereoscopic view, left and right eyes "
+ "are encoded in the (u,v) coordinates of two meshes",
+ "stereo-mesh"},
+ {0, NULL, NULL},
+ };
+
+ video360_stereomode_type =
+ g_enum_register_static ("GstVideo360StereoMode", stereomode_types);
+ }
+
+ return video360_stereomode_type;
+}
+
+static GType
+gst_video360_projtype_get_type (void)
+{
+ static GType video360_projtype_type = 0;
+
+ if (!video360_projtype_type) {
+ static GEnumValue projtype_types[] = {
+ {PROJECTION_TYPE_CUBEMAP, "Cubemap projection", "cubemap"},
+ {PROJECTION_TYPE_EQUIRECTANGULAR, "Equirectangular projection",
+ "equirectangular"},
+ {PROJECTION_TYPE_MESH, "Mesh projection", "mesh"},
+ {0, NULL, NULL},
+ };
+
+ video360_projtype_type =
+ g_enum_register_static ("GstVideo360ProjectionType", projtype_types);
+ }
+
+ return video360_projtype_type;
+}
+
+/* GObject vmethod implementations */
+
+/* initialize the video360's class */
+static void
+gst_video360_class_init (GstVideo360Class * klass)
+{
+
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = gst_video360_set_property;
+ gobject_class->get_property = gst_video360_get_property;
+
+ gstelement_class->change_state = gst_video360_change_state;
+
+ g_object_class_install_property (gobject_class, PROP_STEREO,
+ g_param_spec_enum ("stereo-mode", "StereoMode", "Stereo mode: Monoscopic,"
+ " Stereoscopic Top-Bottom, Stereoscopic Left-Right, "
+ "Stereoscopic Stereo-Mesh",
+ GST_TYPE_VIDEO360_STEREOMODE, MODE_MONOSCOPIC,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_YAW,
+ g_param_spec_int ("pose-yaw", "PoseYawDegrees", "Pose Yaw Degrees: "
+ "counter-clockwise rotation in degrees around the up vector +/-180",
+ -180, 180, 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PITCH,
+ g_param_spec_int ("pose-pitch", "PosePitchDegrees", "Pose Pitch Degrees: "
+ "counter-clockwise rotation in degrees around the right vector +/-90",
+ -90, 90, 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_ROLL,
+ g_param_spec_int ("pose-roll", "PoseRollDegrees", "Pose Roll Degrees: "
+ "counter-clockwise rotation in degrees around the forward vector "
+ "+/-180", -180, 180, 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PROJECTION,
+ g_param_spec_enum ("projection-type", "ProjectionType", "Projection type:"
+ " Cubemap, Equirectangular, Mesh",
+ GST_TYPE_VIDEO360_PROJTYPE, PROJECTION_TYPE_EQUIRECTANGULAR,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_TOP,
+ g_param_spec_uint ("projection-bounds-top", "ProjectionBoundsTop",
+ "The amount from the top of the frame to crop",
+ 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_BOTTOM,
+ g_param_spec_uint ("projection-bounds-bottom", "ProjectionBoundsBottom",
+ "The amount from the bottom of the frame to crop",
+ 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_LEFT,
+ g_param_spec_uint ("projection-bounds-left", "ProjectionBoundsLeft",
+ "The amount from the left of the frame to crop",
+ 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_RIGHT,
+ g_param_spec_uint ("projection-bounds-right", "ProjectionBoundsRight",
+ "The amount from the right of the frame to crop",
+ 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_HFOV,
+ g_param_spec_uint ("horizontal-fov", "HorizontalFOV",
+ "Horizontal field of view in degrees, range 1-360",
+ 1, 360, HORIZONTAL_FOV_DEGREE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_VFOV,
+ g_param_spec_uint ("vertical-fov", "VerticalFOV",
+ "Vertical field of view in degrees, range 1-180",
+ 1, 180, VERTICAL_FOV_DEGREE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_ZOOM,
+ g_param_spec_float ("zoom", "Zoom",
+ "Zoom in factor, range 1.0-0.0 (default 1.0 - no zoom)",
+ 0.0, 1.0, 1.0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PASSTHROUGH,
+ g_param_spec_boolean ("passthrough", "Passthrough",
+ "Passthrough mode: 0 - perform video 360 transformation (default), "
+ "1 - pass frames intact", FALSE, G_PARAM_READWRITE));
+
+ gst_element_class_set_details_simple (gstelement_class,
+ "Video360", /* Long-name */
+ "Effect/Video", /* Klass */
+ "Spatial Video GStreamer plugin", /* Description */
+ "Andriy Martynets <a.martynets@partner.samsung.com>"); /* Author */
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&src_factory));
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sink_factory));
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set pad calback functions
+ * initialize instance structure
+ */
+static void
+gst_video360_init (GstVideo360 * this)
+{
+ this->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
+ gst_pad_set_event_function (this->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_video360_sink_event));
+ gst_pad_set_chain_function (this->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_video360_chain));
+ /* Make the default CAPS query always return the negotiated caps */
+ gst_pad_use_fixed_caps (this->sinkpad);
+ gst_element_add_pad (GST_ELEMENT (this), this->sinkpad);
+
+ this->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
+ gst_pad_set_event_function (this->srcpad,
+ GST_DEBUG_FUNCPTR (gst_video360_src_event));
+ /* Make the default CAPS query always return the negotiated caps */
+ gst_pad_use_fixed_caps (this->srcpad);
+ gst_element_add_pad (GST_ELEMENT (this), this->srcpad);
+
+ this->sinkpad_caps = NULL;
+
+ this->stereo_mode = MODE_MONOSCOPIC;
+ this->pose_yaw_radians = 0.0;
+ this->pose_pitch_radians = 0.0;
+ this->pose_roll_radians = 0.0;
+ this->projection_type = PROJECTION_TYPE_EQUIRECTANGULAR;
+ this->projection_bounds_top = 0;
+ this->projection_bounds_bottom = 0;
+ this->projection_bounds_left = 0;
+ this->projection_bounds_right = 0;
+ this->requested_horizontal_fov_degree =
+ this->horizontal_fov_degree = HORIZONTAL_FOV_DEGREE;
+ this->requested_vertical_fov_degree =
+ this->vertical_fov_degree = VERTICAL_FOV_DEGREE;
+ this->requested_passthrough = this->passthrough = FALSE;
+ this->zoom = 1.0;
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#ifdef GST_TIZEN_GL_WINDOW_WAYLAND
+ this->wl_display = NULL;
+#endif
+ this->egl_display = EGL_NO_DISPLAY;
+ this->egl_surface = EGL_NO_SURFACE;
+ this->egl_config = 0;
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ this->eglCreateImage = NULL;
+ this->eglDestroyImage = NULL;
+ this->glEGLImageTargetTexture2D = NULL;
+ this->input_egl_image = this->output_egl_image = EGL_NO_IMAGE_KHR;
+ this->input_tbm_surface = this->output_tbm_surface = NULL;
+#endif
+#endif
+ this->glFramebufferTexture2DMultisample = NULL;
+ this->fov_uni = this->dov_uni = this->crops_uni = this->zoom_uni = -1;
+ this->tex_ids[0] = this->tex_ids[1] =
+ this->bo_ids[0] = this->bo_ids[1] = this->fbo_id = this->prog_id = 0;
+
+ pthread_mutex_init (&this->gl_mutex, NULL);
+
+#ifdef GST_TIZEN_USE_TIMING
+ this->frames_processed = this->frames_nanoseconds =
+ this->uploads_nanoseconds = this->downloads_nanoseconds =
+ this->rendering_nanoseconds = 0;
+#endif
+}
+
+static void
+gst_video360_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstVideo360 *this = GST_VIDEO360 (object);
+
+ switch (prop_id) {
+ case PROP_STEREO:
+ /* Monoscopic format is the only supported */
+ this->stereo_mode = g_value_get_enum (value);
+ gst_pad_mark_reconfigure (this->sinkpad);
+ break;
+ case PROP_YAW:
+ this->pose_yaw_radians = M_PI * g_value_get_int (value) / 180.0;
+ gst_video360_set_dov_gl (this);
+ break;
+ case PROP_PITCH:
+ this->pose_pitch_radians = M_PI * g_value_get_int (value) / 180.0;
+ gst_video360_set_dov_gl (this);
+ break;
+ case PROP_ROLL:
+ this->pose_roll_radians = M_PI * g_value_get_int (value) / 180.0;
+ gst_video360_set_dov_gl (this);
+ break;
+ case PROP_PROJECTION:
+ /* Equirectangular projection is the only supported */
+ this->projection_type = g_value_get_enum (value);
+ gst_pad_mark_reconfigure (this->sinkpad);
+ break;
+ case PROP_TOP:
+ /* Set max for PROP_BOTTOM property? */
+ this->projection_bounds_top = g_value_get_uint (value);
+ gst_video360_set_crops_gl (this);
+ break;
+ case PROP_BOTTOM:
+ /* must be less than 0xFFFFFFFF - projection_bounds_top */
+ this->projection_bounds_bottom = g_value_get_uint (value);
+ gst_video360_set_crops_gl (this);
+ break;
+ case PROP_LEFT:
+ /* Set max for PROP_RIGHT property? */
+ this->projection_bounds_left = g_value_get_uint (value);
+ gst_video360_set_crops_gl (this);
+ break;
+ case PROP_RIGHT:
+ /* must be less than 0xFFFFFFFF - projection_bounds_left */
+ this->projection_bounds_right = g_value_get_uint (value);
+ gst_video360_set_crops_gl (this);
+ break;
+ case PROP_HFOV:
+ this->requested_horizontal_fov_degree = g_value_get_uint (value);
+ gst_pad_mark_reconfigure (this->sinkpad);
+ break;
+ case PROP_VFOV:
+ this->requested_vertical_fov_degree = g_value_get_uint (value);
+ gst_pad_mark_reconfigure (this->sinkpad);
+ break;
+ case PROP_ZOOM:
+ this->zoom = g_value_get_float (value);
+ gst_video360_set_zoom_gl (this);
+ break;
+ case PROP_PASSTHROUGH:
+ this->requested_passthrough = g_value_get_boolean (value);
+ gst_pad_mark_reconfigure (this->sinkpad);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_video360_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstVideo360 *this = GST_VIDEO360 (object);
+
+ switch (prop_id) {
+ case PROP_STEREO:
+ g_value_set_enum (value, this->stereo_mode);
+ break;
+ case PROP_YAW:
+ g_value_set_int (value, this->pose_yaw_radians * 180.0 / M_PI);
+ break;
+ case PROP_PITCH:
+ g_value_set_int (value, this->pose_pitch_radians * 180.0 / M_PI);
+ break;
+ case PROP_ROLL:
+ g_value_set_int (value, this->pose_roll_radians * 180.0 / M_PI);
+ break;
+ case PROP_PROJECTION:
+ g_value_set_enum (value, this->projection_type);
+ break;
+ case PROP_TOP:
+ g_value_set_uint (value, this->projection_bounds_top);
+ break;
+ case PROP_BOTTOM:
+ g_value_set_uint (value, this->projection_bounds_bottom);
+ break;
+ case PROP_LEFT:
+ g_value_set_uint (value, this->projection_bounds_left);
+ break;
+ case PROP_RIGHT:
+ g_value_set_uint (value, this->projection_bounds_right);
+ break;
+ case PROP_HFOV:
+ g_value_set_uint (value, this->horizontal_fov_degree);
+ break;
+ case PROP_VFOV:
+ g_value_set_uint (value, this->vertical_fov_degree);
+ break;
+ case PROP_ZOOM:
+ g_value_set_float (value, this->zoom);
+ break;
+ case PROP_PASSTHROUGH:
+ g_value_set_boolean (value, this->passthrough);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn
+gst_video360_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ /* Resources allocation depends on negotiated capabilities and thus
+ * is called from sink pad events handler
+ */
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
+ deallocate_local_resources (GST_VIDEO360 (element));
+ }
+
+ return ret;
+}
+
+/* GstElement vmethod implementations */
+
+/* this function handles sink events */
+static gboolean
+gst_video360_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GstVideo360 *this = GST_VIDEO360 (parent);
+ gboolean ret = TRUE;
+ GstCaps *caps;
+ GValue value = G_VALUE_INIT;
+ GstStructure *structure;
+ const char *input_frame_format_srt;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:
+ {
+ /* we should handle the format here */
+ gst_event_parse_caps (event, &caps);
+ if (!(this->passthrough = this->requested_passthrough)) {
+ structure = gst_caps_get_structure (caps, 0);
+
+ ret = gst_structure_get_int (structure, "width",
+ (gint *) & this->input_frame_width);
+
+ ret = ret && gst_structure_get_int (structure, "height",
+ (gint *) & this->input_frame_height);
+
+ /* NOTE: below pointer remains valid until the next call to a
+ * gst_structure_*() function with the given structure
+ */
+ ret = ret &&
+ (input_frame_format_srt = gst_structure_get_string (structure,
+ "format")) != NULL;
+
+ if (G_UNLIKELY (!ret)) {
+ GST_ERROR ("CAPS negotiation error: Wrong CAPS format");
+ } else {
+ this->input_frame_memory_object = 0;
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ this->output_frame_format = GST_VIDEO_FORMAT_SR32;
+#else
+ this->output_frame_format = GST_VIDEO_FORMAT_BGRA;
+#endif
+ if (!strcmp (input_frame_format_srt, "BGRA")) {
+ this->input_frame_format = GST_VIDEO_FORMAT_BGRA;
+ this->renderer = gst_video360_standard_formats_renderer;
+ } else if (!strcmp (input_frame_format_srt, "NV12")) {
+ this->input_frame_format = GST_VIDEO_FORMAT_NV12;
+ this->renderer = gst_video360_standard_formats_renderer;
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ } else if (!strcmp (input_frame_format_srt, "SN12")) {
+ this->input_frame_format = GST_VIDEO_FORMAT_SN12;
+ this->renderer = gst_video360_native_formats_renderer;
+ this->input_frame_memory_object = 1;
+#endif
+ } else if (!strcmp (input_frame_format_srt, "I420")) {
+ this->input_frame_format = GST_VIDEO_FORMAT_I420;
+ this->renderer = gst_video360_standard_formats_renderer;
+ } else {
+ /* TODO(a.martynets): add support for more formats
+ * (e.g. S420)
+ */
+ this->output_frame_format = this->input_frame_format =
+ GST_VIDEO_FORMAT_UNKNOWN;
+ }
+
+ this->panorama_height = this->input_frame_height +
+ this->projection_bounds_bottom + this->projection_bounds_top;
+ this->panorama_width = this->input_frame_width +
+ this->projection_bounds_left + this->projection_bounds_right;
+
+ this->horizontal_fov_degree = this->requested_horizontal_fov_degree;
+ this->vertical_fov_degree = this->requested_vertical_fov_degree;
+
+ /* Select FOV frame size in pixels based on equator length and given
+ * horizontal/vertical FOV in degrees.
+ */
+ /* Align width rounding up to 16 which is required for the
+ * waylandsink. Both RGB and NV12 formats have requirement for width
+ * to be aligned by 4 which is satisfied automatically. This also
+ * allows us to use the width as the stride.
+ */
+ /* Mali-400 driver (drm?) aligns internal buffer stride by 32 pixels.
+ * To simplify TBM surface based images upload/download we need to
+ * apply the same alignment to output image width.
+ * */
+ this->output_frame_width =
+ GST_ROUND_UP_32 (this->panorama_width *
+ this->horizontal_fov_degree / 360);
+ /* Normally height alignment is by 2. But we might want to rotate
+ * screen picture by 90 degrees and height will became width for
+ * wayland. Thus we align height rounding it up to 16 to be on the
+ * safe side.
+ * */
+ this->output_frame_height =
+ GST_ROUND_UP_16 (this->output_frame_width *
+ this->vertical_fov_degree / this->horizontal_fov_degree);
+ /* Output frame stride is equal to its width as it is already
+ * alighned.
+ * */
+ this->output_frame_buffer_size = this->output_frame_height *
+ this->output_frame_width * RGB_BYTES_PER_PIXEL;
+
+ /* Calculate format specific values */
+ switch (this->input_frame_format) {
+ case GST_VIDEO_FORMAT_BGRA:
+ this->input_frame_planes[0].stride =
+ GST_ROUND_UP_4 (this->input_frame_width *
+ RGB_BYTES_PER_PIXEL);
+ this->input_frame_planes[0].size =
+ this->input_frame_planes[0].stride * this->input_frame_height;
+ break;
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ case GST_VIDEO_FORMAT_SN12:
+#endif
+ case GST_VIDEO_FORMAT_NV12:
+ this->input_frame_planes[0].stride =
+ this->input_frame_planes[1].stride =
+ GST_ROUND_UP_4 (this->input_frame_width);
+
+ this->input_frame_planes[0].size =
+ this->input_frame_planes[0].stride *
+ GST_ROUND_UP_2 (this->input_frame_height);
+ this->input_frame_planes[1].size =
+ this->input_frame_planes[0].size >> 1;
+ break;
+ case GST_VIDEO_FORMAT_I420:
+ this->input_frame_planes[0].stride =
+ GST_ROUND_UP_4 (this->input_frame_width);
+ this->input_frame_planes[1].stride =
+ this->input_frame_planes[2].stride =
+ GST_ROUND_UP_4 (GST_ROUND_UP_2 (this->
+ input_frame_width) >> 1);
+
+ this->input_frame_planes[0].size =
+ this->input_frame_planes[0].stride *
+ GST_ROUND_UP_2 (this->input_frame_height);
+ this->input_frame_planes[1].size =
+ this->input_frame_planes[2].size =
+ this->input_frame_planes[1].stride *
+ GST_ROUND_UP_2 (this->input_frame_height) >> 1;
+ break;
+ /* TODO(a.martynets): add support for more formats
+ * (e.g. S420)
+ * */
+ default:
+ /* Reject unsupported CAPS */
+ return FALSE;
+ }
+
+ /* Here we save incoming CAPS which might be needed for subsequent
+ * negotiations. Normally they are present on the PAD and its peer as
+ * sticky CAPS events and can be obtained by gst_pad_get_current_caps
+ * call. Unfortunately some elements (e.g. OMX decoder) seems to unref
+ * these events.
+ * */
+ if (this->sinkpad_caps)
+ gst_caps_unref (this->sinkpad_caps);
+ this->sinkpad_caps = gst_caps_ref (caps);
+
+ /* Once sink capabilities are negotiated translate them to source pad.
+ * Here we do a kind of transform negotiation as we setup fixed CAPS
+ * on the src PAD.
+ * */
+ /* Use fixed CAPS from sink PAD to preserve all fields set up by the
+ * uplink.
+ * NOTE: some elements (e.g. videoscale and waylandsink) are sensitive
+ * to presence of unknown or absence of some format specific fields.
+ * This may lead to the pipe doesn't negotiate.
+ * */
+ caps = gst_caps_make_writable (caps);
+ structure = gst_caps_get_structure (caps, 0);
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, (int) this->output_frame_width);
+ gst_structure_set_value (structure, "width", &value);
+ g_value_set_int (&value, (int) this->output_frame_height);
+ gst_structure_set_value (structure, "height", &value);
+ g_value_unset (&value);
+ g_value_init (&value, G_TYPE_STRING);
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ g_value_set_static_string (&value, "SR32");
+#else
+ g_value_set_static_string (&value, "BGRA");
+#endif
+ gst_structure_set_value (structure, "format", &value);
+ g_value_unset (&value);
+ }
+
+ /* Sanity call to deallocate resources in case of re-negotiation */
+ deallocate_local_resources (this);
+
+ ret = ret && allocate_local_resources (this);
+ }
+
+ /* Set capabilities for the source pad */
+ if (G_UNLIKELY (ret && !gst_pad_set_caps (this->srcpad, caps))) {
+ GST_ERROR ("CAPS negotiation error: Can't set CAPS for source pad");
+ ret = FALSE;
+ }
+
+ if (ret)
+ gst_pad_check_reconfigure (pad);
+
+ break;
+ }
+ default:
+ ret = gst_pad_event_default (pad, parent, event);
+ break;
+ }
+ return ret;
+}
+
+static gboolean
+gst_video360_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GstVideo360 *this = GST_VIDEO360 (parent);
+ const GstStructure *structure;
+ const gchar *type;
+ gdouble x, y;
+ gdouble yaw, pitch, roll;
+ gboolean dov_set = FALSE;
+ int tmp;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NAVIGATION:
+ structure = gst_event_get_structure (event);
+ type = gst_structure_get_string (structure, "event");
+ if (!strcmp (type, "mouse-button-press")) {
+ gst_structure_get_int (structure, "button",
+ &this->pressed_mouse_button);
+ gst_structure_get_double (structure, "pointer_x", &x);
+ gst_structure_get_double (structure, "pointer_y", &y);
+ this->last_x = x;
+ this->last_y = y;
+ } else if (!strcmp (type, "mouse-move")) {
+ if (gst_structure_get_double (structure, "yaw", &yaw) &&
+ gst_structure_get_double (structure, "pitch", &pitch) &&
+ gst_structure_get_double (structure, "roll", &roll)) {
+ this->pose_yaw_radians = yaw;
+ this->pose_pitch_radians = pitch;
+ this->pose_roll_radians = roll;
+ dov_set = TRUE;
+ } else {
+ gst_structure_get_double (structure, "pointer_x", &x);
+ gst_structure_get_double (structure, "pointer_y", &y);
+
+ if (this->pressed_mouse_button == 1) {
+ /* Here we handle navigation events from a videosink if any.
+ * It is expected navigation events to bring image coordinates
+ * otherwise angles will be calculated incorrectly.
+ * */
+ /* tmp variable is introduces here just to get rid of SVACE's error
+ * reports.
+ * */
+ tmp = this->horizontal_fov_degree *
+ ((int) x - this->last_x) / (int) this->output_frame_width;
+ this->pose_yaw_radians -= (float) tmp *M_PI / 180.0;
+ tmp = 2 * this->vertical_fov_degree *
+ ((int) y - this->last_y) / (int) this->output_frame_height;
+ this->pose_pitch_radians += (float) tmp *M_PI / 180.0;
+ if (this->pose_yaw_radians <= -M_PI)
+ this->pose_yaw_radians += 2 * M_PI;
+ else if (this->pose_yaw_radians > M_PI)
+ this->pose_yaw_radians -= 2 * M_PI;
+ if (this->pose_pitch_radians < -M_PI_2)
+ this->pose_pitch_radians = -M_PI_2;
+ else if (this->pose_pitch_radians > M_PI_2)
+ this->pose_pitch_radians = M_PI_2;
+ dov_set = TRUE;
+ }
+ this->last_x = x;
+ this->last_y = y;
+ }
+ if (dov_set)
+ gst_video360_set_dov_gl (this);
+ } else if (!strcmp (type, "mouse-button-release")) {
+ this->pressed_mouse_button = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return gst_pad_event_default (pad, parent, event);
+}
+
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+/* The callback function which releases MMVideoBuffer with attached TBM BO.
+ * It is used for outgoing frame buffers of SR32 format.
+ * */
+static void
+mm_vbuffer_free (void * mm_vbuffer)
+{
+ if (((MMVideoBuffer *) mm_vbuffer)->type == MM_VIDEO_BUFFER_TYPE_TBM_BO &&
+ ((MMVideoBuffer *) mm_vbuffer)->handle.bo[0]) {
+ tbm_bo_unref (((MMVideoBuffer *) mm_vbuffer)->handle.bo[0]);
+ }
+
+ g_free (mm_vbuffer);
+}
+#endif
+
+/* chain function
+ * this function does the actual processing
+ */
+static GstFlowReturn
+gst_video360_chain (GstPad * pad, GstObject * parent, GstBuffer * in_buf)
+{
+ GstVideo360 *this = GST_VIDEO360 (parent);
+ GstBuffer *out_buf;
+ GstMapInfo in_info, out_info;
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ MMVideoBuffer *mm_vbuffer;
+#else
+ GstMemory *out_mem;
+#endif
+ GstMemory *in_mem;
+ int ret;
+
+ if (gst_pad_check_reconfigure (pad)) {
+ if (!gst_video360_sink_event (pad, parent,
+ gst_event_new_caps (this->sinkpad_caps)))
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+
+ if (this->passthrough)
+ return gst_pad_push (this->srcpad, in_buf);
+
+#ifdef GST_TIZEN_USE_TIMING
+ struct timespec start_time, stop_time;
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start_time);
+#endif
+
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ if ((out_buf = gst_buffer_new_wrapped (
+ g_malloc (GST_TIZEN_NATIVE_FORMATS_FIRST_MEMORY_SIZE),
+ GST_TIZEN_NATIVE_FORMATS_FIRST_MEMORY_SIZE)) &&
+ (mm_vbuffer = g_malloc0 (sizeof (MMVideoBuffer)))) {
+
+ mm_vbuffer->type = MM_VIDEO_BUFFER_TYPE_TBM_BO;
+ mm_vbuffer->format = MM_PIXEL_FORMAT_RGBA;
+ mm_vbuffer->plane_num = 1;
+ mm_vbuffer->width[0] = this->output_frame_width;
+ mm_vbuffer->height[0] = this->output_frame_height;
+ mm_vbuffer->stride_width[0] = this->output_frame_width * RGB_BYTES_PER_PIXEL;
+ mm_vbuffer->stride_height[0] = this->output_frame_height;
+ mm_vbuffer->size[0] = this->output_frame_buffer_size;
+ mm_vbuffer->handle_num = 1;
+ mm_vbuffer->handle_size[0] = this->output_frame_buffer_size;
+
+ gst_buffer_append_memory (out_buf,
+ gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY, mm_vbuffer,
+ sizeof (MMVideoBuffer), 0, sizeof (MMVideoBuffer), mm_vbuffer,
+ mm_vbuffer_free));
+#else
+ if ((out_buf = gst_buffer_new_allocate (NULL,
+ this->output_frame_buffer_size, NULL))) {
+#endif
+
+ GST_BUFFER_PTS (out_buf) = GST_BUFFER_PTS (in_buf);
+ GST_BUFFER_DTS (out_buf) = GST_BUFFER_DTS (in_buf);
+ GST_BUFFER_DURATION (out_buf) = GST_BUFFER_DURATION (in_buf);
+ GST_BUFFER_OFFSET (out_buf) = GST_BUFFER_OFFSET (in_buf);
+ GST_BUFFER_OFFSET_END (out_buf) = GST_BUFFER_OFFSET_END (in_buf);
+
+ ret = FALSE;
+ in_info.data = out_info.data = NULL;
+
+ in_mem = gst_buffer_peek_memory (in_buf, this->input_frame_memory_object);
+ gst_memory_map (in_mem, &in_info, GST_MAP_READ);
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+ out_info.data = (void *) mm_vbuffer;
+#else
+ out_mem = gst_buffer_peek_memory (out_buf, 0);
+ gst_memory_map (out_mem, &out_info, GST_MAP_WRITE);
+#endif
+
+ if (in_info.data && out_info.data) {
+ /* Here we do the rendering */
+ pthread_mutex_lock (&this->gl_mutex);
+ ret = (*this->renderer) (this, in_info.data, out_info.data);
+ pthread_mutex_unlock (&this->gl_mutex);
+ }
+
+ if (in_info.data)
+ gst_memory_unmap (in_mem, &in_info);
+#ifndef GST_TIZEN_USE_NATIVE_FORMATS
+ if (out_info.data)
+ gst_memory_unmap (out_mem, &out_info);
+#endif
+
+ if (ret) {
+ gst_buffer_unref (in_buf);
+
+#ifdef GST_TIZEN_USE_TIMING
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &stop_time);
+ this->frames_processed++;
+ this->frames_nanoseconds += (stop_time.tv_sec - start_time.tv_sec) *
+ 1000000000;
+ this->frames_nanoseconds += stop_time.tv_nsec - start_time.tv_nsec;
+#endif
+
+ return gst_pad_push (this->srcpad, out_buf);
+ }
+ }
+
+ GST_ERROR ("Frame rendering error: "
+ "Memory allocation / write permission request failed");
+
+ return GST_FLOW_ERROR;
+}
+
+static int
+allocate_local_resources (GstVideo360 * this)
+{
+ /* On failure sets the video360_error_message string and returns FALSE */
+ if (!gst_video360_init_gl (this)) {
+ deallocate_local_resources (this);
+ GST_ERROR ("Video360 initialization failed: OpenGL initialization failed");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+deallocate_local_resources (GstVideo360 * this)
+{
+ gst_video360_deinit_gl (this);
+
+#ifdef GST_TIZEN_USE_TIMING
+ if (this->frames_processed) {
+ g_message
+ ("\n===================== Video360 =============================\n"
+ "Frames processed: %ld. Average time: %9lld ns per frame.\n"
+ "\tUploads average time:\t%9lld ns per frame.\n"
+ "\tDownloads average time:\t%9lld ns per frame.\n"
+ "\tRendering average time:\t%9lld ns per frame.\n"
+ "============================================================\n",
+ this->frames_processed,
+ this->frames_nanoseconds / this->frames_processed,
+ this->uploads_nanoseconds / this->frames_processed,
+ this->downloads_nanoseconds / this->frames_processed,
+ this->rendering_nanoseconds / this->frames_processed);
+
+ this->frames_processed = this->frames_nanoseconds =
+ this->uploads_nanoseconds = this->downloads_nanoseconds =
+ this->rendering_nanoseconds = 0;
+ }
+#endif
+
+ return;
+}
+
+
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and other features
+ */
+static gboolean
+video360_init (GstPlugin * video360)
+{
+
+ /* Debug category initialization.
+ * It can be used next to filter log messages with GST_DEBUG environmental
+ * variable set like:
+ * GST_DEBUG=video360:4
+ * */
+ GST_DEBUG_CATEGORY_INIT (video360_debug_category, "video360",
+ 0, "Spatial Video GStreamer plugin");
+
+ return gst_element_register (video360, "video360", GST_RANK_NONE,
+ GST_TYPE_VIDEO360);
+}
+
+/* PACKAGE: this is usually set by autotools depending on some _INIT macro
+ * in configure.ac and then written into and defined in config.h, but we can
+ * just set it ourselves here in case someone doesn't use autotools to
+ * compile this code. GST_PLUGIN_DEFINE needs PACKAGE to be defined.
+ */
+#ifndef PACKAGE
+#define PACKAGE "video360-package"
+#endif
+
+/* gstreamer looks for this structure to register video360s
+ *
+ * exchange the string 'Template video360' with your video360 description
+ */
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ video360,
+ "Spatial Video GStreamer plugin",
+ video360_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
--- /dev/null
+/*
+ * Video360 GStreamer plug-in
+ * Copyright (C) 2017 Samsung R&D Institute Ukraine
+ * All rights reserved.
+ *
+ * @author: Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_VIDEO360_H__
+#define __GST_VIDEO360_H__
+
+#define _GNU_SOURCE
+
+/* Compilation macroses */
+
+/* Define one of below macroses to choose OpenGL API:
+ * #define GST_TIZEN_GL_API_GLES2
+ * #define GST_TIZEN_GL_API_OPENGL (Not supported yet)
+ * */
+#define GST_TIZEN_GL_PLATFORM_EGL
+#define GST_TIZEN_GL_WINDOW_WAYLAND
+#define GST_TIZEN_USE_EGLIMAGE
+/* Define below macros to enable multisample antialiasing
+ * #define GST_TIZEN_USE_MSAA
+ * */
+
+/* Define below macros to have per frame timings calculated and summary output:
+ * #define GST_TIZEN_USE_TIMING
+ * */
+
+/* Define below macros to build with Tizen native formats support:
+ * #define GST_TIZEN_USE_NATIVE_FORMATS
+ * */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/gstutils.h>
+#include <gst/video/video.h>
+
+#ifdef GST_TIZEN_GL_API_GLES2
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#ifdef GST_TIZEN_GL_WINDOW_WAYLAND
+#include <wayland-client.h>
+#include <wayland-egl.h>
+#else /* Not GL_WINDOW_WAYLAND */
+#error "Wayland based EGL is the only supported platform"
+#endif /* GL_WINDOW_WAYLAND */
+
+#else /* Not GL_PLATFORM_EGL */
+#error "GLES API supported on EGL platform only"
+#endif /* GL_PLATFORM_EGL */
+
+#else /* Not GL_API_GLES2 */
+#error "OpenGL ES2 is the only supported API"
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif /* GL_API_GLES2 */
+
+#include <string.h>
+#include <math.h>
+#include <pthread.h>
+
+#include "video360.h"
+
+#ifdef GST_TIZEN_USE_TIMING
+#include <time.h>
+#endif
+
+#if defined (GST_TIZEN_USE_NATIVE_FORMATS) || defined (GST_TIZEN_USE_EGLIMAGE)
+#include <mm_types.h>
+#include <tbm_bufmgr.h>
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+#endif
+
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+#error "This code was tested for Little Endian architectures only!"
+#endif
+
+#if (GST_VERSION_MAJOR != 1)
+#error "This code designed for GStreamer version 1.0"
+#endif
+
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+#define GST_TIZEN_NATIVE_FORMATS_FIRST_MEMORY_SIZE 16
+#endif
+
+/* RGB format specific constants */
+#define RGB_BYTES_PER_PIXEL 4
+
+/* Cropped area fill color (buffer clear color) */
+#define GAP_FILL_COLOUR_RED 0.605f
+#define GAP_FILL_COLOUR_GREEN 0.652f
+#define GAP_FILL_COLOUR_BLUE 0.652f
+
+/* Default aspect ratio for output image */
+#define OUTPUT_RATIO_H 16
+#define OUTPUT_RATIO_V 9
+
+/* Default field of view for output image.
+ * Human eye horizontal stereoscopic FOV is 120 degrees.
+ * */
+#define HORIZONTAL_FOV_DEGREE 120
+#define VERTICAL_FOV_DEGREE (HORIZONTAL_FOV_DEGREE * OUTPUT_RATIO_V / OUTPUT_RATIO_H)
+
+G_BEGIN_DECLS
+
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_VIDEO360 \
+ (gst_video360_get_type())
+#define GST_VIDEO360(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO360,GstVideo360))
+#define GST_VIDEO360_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO360,GstVideo360Class))
+#define GST_IS_VIDEO360(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO360))
+#define GST_IS_VIDEO360_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO360))
+#define GST_VIDEO360_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_VIDEO360,GstVideo360Class))
+
+/* Debug category declaration */
+GST_DEBUG_CATEGORY_EXTERN (video360_debug_category);
+#define GST_CAT_DEFAULT video360_debug_category
+
+typedef struct _GstVideo360 GstVideo360;
+typedef struct _GstVideo360Class GstVideo360Class;
+
+struct _GstVideo360
+{
+ GstElement element;
+
+ GstPad *sinkpad, *srcpad;
+ GstCaps *sinkpad_caps;
+
+ /*****************************************************************************
+ * OpenGL ES related variables
+ * **************************************************************************/
+#ifdef GST_TIZEN_GL_WINDOW_WAYLAND
+ struct wl_display *wl_display;
+#endif
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+ EGLDisplay egl_display;
+ EGLConfig egl_config;
+ EGLSurface egl_surface;
+ EGLContext egl_context;
+
+#ifdef GST_TIZEN_USE_EGLIMAGE
+ /* EGL extension functions */
+ PFNEGLCREATEIMAGEKHRPROC eglCreateImage;
+ PFNEGLDESTROYIMAGEKHRPROC eglDestroyImage;
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2D;
+
+ EGLImage input_egl_image;
+ EGLImage output_egl_image;
+ tbm_surface_h input_tbm_surface;
+ tbm_surface_h output_tbm_surface;
+ void *input_image_ptr [TBM_SURF_PLANE_MAX];
+ void *output_image_ptr;
+ tbm_surface_info_s input_surf_info;
+#endif
+
+#endif
+ /* GL extension functions */
+ PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisample;
+
+ GLuint tex_ids [2];
+ GLuint fbo_id;
+ GLuint bo_ids [2];
+ GLuint prog_id;
+ GLint fov_uni;
+ GLint dov_uni;
+ GLint crops_uni;
+ GLint zoom_uni;
+
+ pthread_mutex_t gl_mutex;
+
+ /*****************************************************************************
+ * Element's properties
+ * **************************************************************************/
+ /* Currently monoscopic equirectangular projection is the only supported */
+ ProjectionType projection_type;
+ StereoMode stereo_mode;
+
+ unsigned int projection_bounds_top;
+ unsigned int projection_bounds_bottom;
+ unsigned int projection_bounds_left;
+ unsigned int projection_bounds_right;
+
+ /* counter-clockwise rotation in radians around the up vector +/- PI
+ * (+/-180.0 degrees)
+ */
+ float pose_yaw_radians;
+ /* counter-clockwise rotation in radians around the right vector +/- PI/2
+ * (+/-90.0 degrees) post yaw transform
+ */
+ float pose_pitch_radians;
+ /* clockwise rotation in radians around the forward vector +/- PI
+ * (+/-180.0 degrees) post yaw and pitch transform
+ */
+ float pose_roll_radians;
+
+ unsigned int horizontal_fov_degree;
+ unsigned int vertical_fov_degree;
+ gboolean passthrough;
+ float zoom;
+
+ /* Below fields contain requested values (set via properties) until they are
+ * applied at CAPS negotiation.
+ * */
+ unsigned int requested_horizontal_fov_degree;
+ unsigned int requested_vertical_fov_degree;
+ gboolean requested_passthrough;
+
+ /*****************************************************************************
+ * Element's internal variables
+ * **************************************************************************/
+ GstVideoFormat input_frame_format;
+ GstVideoFormat output_frame_format;
+
+ /* Full input panorama sizes in pixels (input frame sizes + cropped areas) */
+ unsigned int panorama_width, panorama_height;
+
+ /* Number of the memory block in gst buffer which contains actual data */
+ unsigned int input_frame_memory_object;
+
+ /* Input frame dimentions in pixels, rows stride and buffer size in bytes */
+ unsigned int input_frame_width, input_frame_height;
+ struct {
+ unsigned int stride;
+ unsigned int size;
+ } input_frame_planes [TBM_SURF_PLANE_MAX];
+
+ /* Output frame sizes in pixels. Rows stride in bytes is equal to width as it
+ * is always aligned
+ * */
+ unsigned int output_frame_width, output_frame_height;
+ unsigned int output_frame_buffer_size;
+
+ /* Navigation events related stuff. It is used to handle navigation events
+ * from a videosink if any. It is expected navigation events to bring image
+ * coordinates otherwise angles will be calculated incorrectly.
+ * */
+ int last_x, last_y, pressed_mouse_button;
+
+ /* Pointer to frame renderer for current frame format */
+ gboolean (*renderer) (GstVideo360 * this, void * in_data, void * out_data);
+
+#ifdef GST_TIZEN_USE_TIMING
+ unsigned long frames_processed;
+ unsigned long long frames_nanoseconds;
+ unsigned long long uploads_nanoseconds;
+ unsigned long long downloads_nanoseconds;
+ unsigned long long rendering_nanoseconds;
+#endif
+};
+
+struct _GstVideo360Class
+{
+ GstElementClass parent_class;
+};
+
+GType gst_video360_get_type (void);
+
+G_END_DECLS
+
+/* Shader sources for equirectangular projection */
+extern const char * equirectangular_vertex_source;
+extern const char * equirectangular_fragment_source;
+extern const char * direct_colors_fragment_source;
+extern const char * reverse_colors_fragment_source;
+/* Sets VBOs. Returns TRUE on success and FALSE otherwise. */
+gboolean _equirectangular_bind_vertex_buffers (GstVideo360 * this);
+/* Sets attributes and uniforms. Returns TRUE on success and FALSE otherwise. */
+gboolean _equirectangular_program_attribs (GstVideo360 * this);
+
+/* GL stuff initialization. Returns TRUE on success and FALSE otherwise. */
+gboolean gst_video360_init_gl (GstVideo360 * this);
+/* GL stuff deinitialization. */
+void gst_video360_deinit_gl (GstVideo360 * this);
+/* GL errors checking function. Returns TRUE if there were errors. */
+gboolean _check_gl_error(const char *file, int line);
+#define GL_ERROR _check_gl_error(__FILE__,__LINE__)
+
+#ifdef GST_TIZEN_GL_PLATFORM_EGL
+/* Below function returns TRUE on errors and FALSE otherwise */
+gboolean _check_egl_error (const char *file, int line);
+#define EGL_ERROR _check_egl_error(__FILE__,__LINE__)
+#else
+ /* Space for alternate platform code */
+#endif
+
+
+/* Frame renderers for supported frame formats.
+ * All these functions return TRUE on success and FALSE otherwise.
+ */
+gboolean gst_video360_standard_formats_renderer (GstVideo360 * this,
+ void * in_data, void * out_data);
+#ifdef GST_TIZEN_USE_NATIVE_FORMATS
+gboolean gst_video360_native_formats_renderer (GstVideo360 * this,
+ void * in_data, void * out_data);
+#endif
+
+/* Renderers helper function - performs GL calls. */
+gboolean gst_video360_draw_gl (GstVideo360 * this);
+/* GL uniforms setters */
+void gst_video360_set_fov_gl (GstVideo360 * this);
+void gst_video360_set_dov_gl (GstVideo360 * this);
+void gst_video360_set_crops_gl (GstVideo360 * this);
+void gst_video360_set_zoom_gl (GstVideo360 * this);
+
+#endif /* __GST_VIDEO360_H__ */
--- /dev/null
+/*
+ * Video360 GStreamer plug-in
+ * Copyright (C) 2017 Samsung R&D Institute Ukraine
+ * All rights reserved.
+ *
+ * @author: Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* TODO(a.martynets): export this file to be used by target application */
+
+#ifndef __VIDEO360_H__
+#define __VIDEO360_H__
+
+/* Minimum-maximum ranges for properties set using below values */
+typedef enum
+{
+ MODE_UNKNOWN = -1,
+ MODE_MONOSCOPIC = 0,
+ MODE_STEREOSCOPIC_TOP_BOTTOM = 1,
+ MODE_STEREOSCOPIC_LEFT_RIGHT = 2,
+ MODE_STEREOSCOPIC_STEREO_MESH = 3,
+ MODE_LAST_ITEM
+} StereoMode;
+
+typedef enum
+{
+ PROJECTION_TYPE_UNKNOWN = -1,
+ PROJECTION_TYPE_EQUIRECTANGULAR = 0,
+ PROJECTION_TYPE_CUBEMAP = 1,
+ PROJECTION_TYPE_MESH = 2,
+ PROJECTION_TYPE_LAST_ITEM
+} ProjectionType;
+
+#endif /* __VIDEO360_H__ */