Video360 plugin implementation added 32/152632/10 accepted/tizen/unified/20171031.055717 submit/tizen/20171030.055657
authorAndriy Martynets <a.martynets@partner.samsung.com>
Tue, 26 Sep 2017 15:41:31 +0000 (18:41 +0300)
committerAndriy Martynets <a.martynets@partner.samsung.com>
Mon, 23 Oct 2017 15:06:50 +0000 (18:06 +0300)
Change-Id: I893ed0a2c35e735fe3476b5d0278dde21228a2fe
Signed-off-by: Andriy Martynets <a.martynets@partner.samsung.com>
13 files changed:
AUTHORS
Makefile.am
PLUGINS
configure.ac
packaging/gst-plugins-tizen.spec
video360/Makefile.am [new file with mode: 0644]
video360/src/Makefile.am [new file with mode: 0644]
video360/src/gstvideo360-equirectangular.c [new file with mode: 0644]
video360/src/gstvideo360-formats.c [new file with mode: 0644]
video360/src/gstvideo360-gl.c [new file with mode: 0644]
video360/src/gstvideo360.c [new file with mode: 0644]
video360/src/gstvideo360.h [new file with mode: 0644]
video360/src/video360.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 96cbfc8..db30c45 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,3 +3,4 @@ Seungbae Shin <seungbae.shin@samsung.com>
 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>
index 1e6e3c0..ffad5e7 100755 (executable)
@@ -57,6 +57,10 @@ if GST_TIZEN_USE_ALFEC
 SUBDIRS += alfec
 endif
 
+if GST_TIZEN_USE_VIDEO360
+SUBDIRS += video360
+endif
+
 DIST_SUBDIRS = common
 
 if GST_TIZEN_USE_ENCODEBIN
@@ -96,6 +100,10 @@ if GST_TIZEN_USE_ALFEC
 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 \
diff --git a/PLUGINS b/PLUGINS
index 02d69f2..4b0758e 100644 (file)
--- a/PLUGINS
+++ b/PLUGINS
@@ -14,6 +14,7 @@ fimcconvert     fimcconvert
 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
index 761c244..2f59780 100755 (executable)
@@ -358,6 +358,34 @@ AC_ARG_ENABLE(ext-alfec, AC_HELP_STRING([--enable-ext-alfec], [using alfec]),
  [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
@@ -385,4 +413,6 @@ tizenipc/Makefile
 tizenipc/src/Makefile
 wfdtizenmanager/Makefile
 alfec/Makefile
+video360/Makefile
+video360/src/Makefile
 )
index 7e171cf..386e1aa 100644 (file)
@@ -9,7 +9,7 @@
 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+
@@ -37,6 +37,7 @@ BuildRequires:  pkgconfig(libtbm)
 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)
@@ -82,7 +83,9 @@ export CFLAGS="$CFLAGS_DEFAULT -DTIZEN_FEATURE_PRODUCT_TM1"
        --enable-ext-wfdtizenmanager\
        --enable-ext-alfec\
        --disable-tizenipc\
-       --disable-static
+       --disable-static\
+  --with-native-formats\
+  --with-gles2
 
 make %{?jobs:-j%jobs}
 
@@ -103,7 +106,10 @@ export CFLAGS="$CFLAGS_DEFAULT"
        --enable-ext-wfdtizenmanager\
        --enable-ext-alfec\
        --disable-tizenipc\
-       --disable-static
+       --disable-static\
+  --with-native-formats\
+  --with-gles2
+
 
 make %{?jobs:-j%jobs}
 
diff --git a/video360/Makefile.am b/video360/Makefile.am
new file mode 100644 (file)
index 0000000..af437a6
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/video360/src/Makefile.am b/video360/src/Makefile.am
new file mode 100644 (file)
index 0000000..802ea26
--- /dev/null
@@ -0,0 +1,21 @@
+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
diff --git a/video360/src/gstvideo360-equirectangular.c b/video360/src/gstvideo360-equirectangular.c
new file mode 100644 (file)
index 0000000..3b04fe8
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * 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;
+}
diff --git a/video360/src/gstvideo360-formats.c b/video360/src/gstvideo360-formats.c
new file mode 100644 (file)
index 0000000..79ef74c
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * 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
diff --git a/video360/src/gstvideo360-gl.c b/video360/src/gstvideo360-gl.c
new file mode 100644 (file)
index 0000000..1b0c26d
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * 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, &param);
+  if (param > 0) {
+    glGetIntegerv (GL_SAMPLES, &param);
+    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
diff --git a/video360/src/gstvideo360.c b/video360/src/gstvideo360.c
new file mode 100644 (file)
index 0000000..2d1bd84
--- /dev/null
@@ -0,0 +1,1014 @@
+/*
+ * 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/")
diff --git a/video360/src/gstvideo360.h b/video360/src/gstvideo360.h
new file mode 100644 (file)
index 0000000..143468c
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * 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__ */
diff --git a/video360/src/video360.h b/video360/src/video360.h
new file mode 100644 (file)
index 0000000..37aed57
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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__ */