Switch to native glbox implementation

Not loading Python
- lower memory footprint for native apps
- faster process loads

On Excelsior pyopengl also have compatibility issues, this
effectively works around those.

Bug: 168133392
Change-Id: I6d6f8d94a4a6ca06f06c03f7b5462f595ddbb182
diff --git a/debian/changelog b/debian/changelog
index ff2cdaf..1eee24a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+edgetpuvision (6-4) mendel-eagle; urgency=medium
+
+  * Switch to native glbox.
+
+ -- Coral Team <coral-support@google.com>  Thu, 29 Oct 2020 13:04:30 -0700
+
 edgetpuvision (6-3) mendel-eagle; urgency=medium
 
   * Update support for the mipi camera pipeline.
diff --git a/debian/control b/debian/control
index be6b4b6..06da349 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@
 Maintainer: Coral <coral-support@google.com>
 Section: python
 Priority: optional
-Build-Depends: dh-python, python3-setuptools, python3-all, debhelper (>= 9), libgstreamer1.0-dev, pkg-config
+Build-Depends: dh-python, python3-setuptools, python3-all, debhelper (>= 9), libgstreamer-plugins-base1.0-dev, pkg-config
 Standards-Version: 3.9.8
 Homepage: https://coral.ai/
 
@@ -20,6 +20,7 @@
          gstreamer1.0-plugins-good,
          gstreamer1.0-plugins-ugly,
          gstreamer1.0-python3-plugin-loader,
+         gstreamer1.0-plugins-coral,
          mdpd,
          python3-cairo,
          python3-edgetpu,
@@ -34,3 +35,12 @@
 Description: EdgeTPU camera API
  API to run inference on image data coming from the camera.
 
+Package: gstreamer1.0-plugins-coral
+Architecture: any
+Multi-Arch: same
+Section: libs
+Pre-Depends: ${misc:Pre-Depends}
+Depends: ${shlibs:Depends},
+         ${misc:Depends},
+Description: Coral GStreamer plugins.
+ Coral specific GStreamer plugins.
diff --git a/debian/rules b/debian/rules
index fb65e3f..187cf7a 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,13 +1,22 @@
 #!/usr/bin/make -f
+include /usr/share/dpkg/pkg-info.mk
 
 PLUGINSDIR := $(shell pkg-config --variable=pluginsdir gstreamer-1.0)
-DESTDIR := debian/python3-edgetpuvision/$(PLUGINSDIR)/python
+PY_DESTDIR := debian/python3-edgetpuvision/$(PLUGINSDIR)/python
+SO_DESTDIR := debian/gstreamer1.0-plugins-coral/$(PLUGINSDIR)
+SO_SRCS := plugins/gstglbox.c plugins/gstcoral.c
+SO_DEST := $(SO_DESTDIR)/libgstcoral.so
+SO_DEPS := gstreamer-plugins-base-1.0 gstreamer-video-1.0 gstreamer-gl-1.0
 
 export PYBUILD_NAME=edgetpuvision
+
 %:
 	dh $@ --with python3 --buildsystem=pybuild
 
 override_dh_install:
 	dh_install
-	install -d $(DESTDIR)
-	install -g 0 -o 0 plugins/python/*.py $(DESTDIR)
+	install -d $(PY_DESTDIR)
+	install -g 0 -o 0 plugins/python/*.py $(PY_DESTDIR)
+
+	install -d $(SO_DESTDIR)
+	$(DEB_HOST_GNU_TYPE)-gcc `$(DEB_HOST_GNU_TYPE)-pkg-config --libs --cflags $(SO_DEPS)` $(CFLAGS) -shared -o $(SO_DEST) -DCORAL_VERSION=$(DEB_VERSION) $(SO_SRCS)
diff --git a/plugins/gstcoral.c b/plugins/gstcoral.c
new file mode 100644
index 0000000..7b6cac6
--- /dev/null
+++ b/plugins/gstcoral.c
@@ -0,0 +1,48 @@
+/*
+ * # Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstglbox.h"
+
+#define CORAL_LICENSE GST_LICENSE_UNKNOWN /* Apache not supported */
+#define ORIGIN "https://coral.ai"
+#define PACKAGE "GstCoral"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+
+  if (!gst_element_register (plugin, "glbox",
+          GST_RANK_NONE, gst_gl_box_get_type ())) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (
+    GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    coral,
+    "Coral plugin",
+    plugin_init,
+    G_STRINGIFY (CORAL_VERSION),
+    CORAL_LICENSE,
+    PACKAGE,
+    ORIGIN);
diff --git a/plugins/gstglbox.c b/plugins/gstglbox.c
new file mode 100644
index 0000000..a390d8a
--- /dev/null
+++ b/plugins/gstglbox.c
@@ -0,0 +1,241 @@
+/*
+ * # Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gl/gstglfuncs.h>
+#include <gst/video/gstvideosink.h>
+
+#include "gstglbox.h"
+
+#define GST_CAT_DEFAULT gst_gl_box_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+enum
+{
+  PROP_0,
+  PROP_X,
+  PROP_Y,
+  PROP_WIDTH,
+  PROP_HEIGHT,
+};
+
+#define DEBUG_INIT \
+  GST_DEBUG_CATEGORY_INIT (gst_gl_box_debug, "glbox", 0, "glbox element");
+#define gst_gl_box_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstGLBox, gst_gl_box,
+    GST_TYPE_GL_FILTER, DEBUG_INIT);
+
+static void gst_gl_box_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_gl_box_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static gboolean gst_gl_box_gl_start (GstGLBaseFilter * base_filter);
+static void gst_gl_box_gl_stop (GstGLBaseFilter * base_filter);
+static gboolean gst_gl_box_gl_set_caps (GstGLBaseFilter * bt,
+    GstCaps * incaps, GstCaps * outcaps);
+
+static gboolean gst_gl_box_filter_texture (GstGLFilter * filter,
+    GstGLMemory * in_tex, GstGLMemory * out_tex);
+
+static void
+gst_gl_box_class_init (GstGLBoxClass * klass)
+{
+  gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
+
+  ((GObjectClass *) klass)->set_property = gst_gl_box_set_property;
+  ((GObjectClass *) klass)->get_property = gst_gl_box_get_property;
+
+  gst_element_class_set_metadata (GST_ELEMENT_CLASS (klass),
+      "OpenGL box",
+      "Filter/Effect/Video",
+      "Colorspace convert and scale with retained aspect ratio",
+      "Coral <coral-support@google.com>");
+
+  GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
+  GST_GL_BASE_FILTER_CLASS (klass)->gl_start = GST_DEBUG_FUNCPTR (gst_gl_box_gl_start);
+  GST_GL_BASE_FILTER_CLASS (klass)->gl_stop = GST_DEBUG_FUNCPTR (gst_gl_box_gl_stop);
+  GST_GL_BASE_FILTER_CLASS (klass)->gl_set_caps = gst_gl_box_gl_set_caps;
+  GST_GL_BASE_FILTER_CLASS (klass)->supported_gl_api =
+      GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2;
+  GST_GL_FILTER_CLASS (klass)->filter_texture = gst_gl_box_filter_texture;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_X,
+      g_param_spec_int ("x", "Frame X coordinate", "Frame X coordinate",
+          0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_Y,
+      g_param_spec_int ("y", "Frame Y coordinate", "Frame Y coordinate",
+          0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WIDTH,
+      g_param_spec_int ("width", "Frame width", "Frame width",
+          0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HEIGHT,
+      g_param_spec_int ("height", "Frame height", "Frame height",
+          0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_gl_box_init (GstGLBox * box)
+{
+  box->shader = NULL;
+  memset (&box->viewport, 0, sizeof (GstVideoRectangle));
+}
+
+static void
+gst_gl_box_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_gl_box_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstGLBox *box = GST_GL_BOX (object);
+
+  switch (prop_id) {
+    case PROP_X:
+      g_value_set_int (value, box->viewport.x);
+      break;
+    case PROP_Y:
+      g_value_set_int (value, box->viewport.y);
+      break;
+    case PROP_WIDTH:
+      g_value_set_int (value, box->viewport.w);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_int (value, box->viewport.h);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_gl_box_gl_start (GstGLBaseFilter * base_filter)
+{
+  GstGLBox *box = GST_GL_BOX (base_filter);
+  GstGLFilter *filter = GST_GL_FILTER (base_filter);
+
+  box->shader = gst_gl_shader_new_default (base_filter->context, NULL);
+  if (!box->shader) {
+    GST_ERROR_OBJECT (box, "Failed to initialize shader");
+    return FALSE;
+  }
+
+
+  filter->draw_attr_position_loc =
+      gst_gl_shader_get_attribute_location (box->shader, "a_position");
+  filter->draw_attr_texture_loc =
+      gst_gl_shader_get_attribute_location (box->shader, "a_texcoord");
+
+  return GST_GL_BASE_FILTER_CLASS (parent_class)->gl_start (base_filter);
+}
+
+static void
+gst_gl_box_gl_stop (GstGLBaseFilter * base_filter)
+{
+  GstGLBox *box = GST_GL_BOX (base_filter);
+
+  if (box->shader) {
+    gst_object_unref (box->shader);
+    box->shader = NULL;
+  }
+
+  GST_GL_BASE_FILTER_CLASS (parent_class)->gl_stop (base_filter);
+}
+
+static gboolean
+gst_gl_box_gl_set_caps (GstGLBaseFilter * bt, GstCaps * incaps,
+    GstCaps * outcaps)
+{
+  GstGLBox *box = GST_GL_BOX (bt);
+  GstGLFilter *filter = GST_GL_FILTER (bt);
+  GstVideoRectangle src, dst, viewport;
+
+  src.x = 0;
+  src.y = 0;
+  src.w = GST_VIDEO_INFO_WIDTH (&filter->in_info);
+  src.h = GST_VIDEO_INFO_HEIGHT (&filter->in_info);
+
+  dst.x = 0;
+  dst.y = 0;
+  dst.w = GST_VIDEO_INFO_WIDTH (&filter->out_info);
+  dst.h = GST_VIDEO_INFO_HEIGHT (&filter->out_info);
+
+  gst_video_sink_center_rect (src, dst, &viewport, TRUE);
+
+  if (memcmp (&viewport, &box->viewport, sizeof (GstVideoRectangle))) {
+    GST_INFO_OBJECT (filter, "Scaling %dx%d -> %dx%d @ (%d, %d)", src.w, src.h,
+        viewport.w, viewport.h, viewport.x, viewport.y);
+    box->viewport = viewport;
+  }
+
+  return GST_GL_BASE_FILTER_CLASS (parent_class)->gl_set_caps (
+      GST_GL_BASE_FILTER (filter), incaps, outcaps);
+}
+
+static gboolean
+gst_gl_box_draw (GstGLFilter * filter, GstGLMemory * in_tex,
+    gpointer user_data)
+{
+  GstGLBox *box = GST_GL_BOX (user_data);
+  const GstGLFuncs *gl = GST_GL_BASE_FILTER (filter)->context->gl_vtable;
+
+  g_return_val_if_fail (box->shader, FALSE);
+
+  gl->ClearColor (0.0, 0.0, 0.0, 1.0);
+  gl->Clear (GL_COLOR_BUFFER_BIT);
+
+  gl->Viewport (box->viewport.x, box->viewport.y, box->viewport.w,
+      box->viewport.h);
+
+  gst_gl_shader_use (box->shader);
+  gst_gl_shader_set_uniform_1i (box->shader, "tex", 0);
+  gl->ActiveTexture (GL_TEXTURE0);
+  gl->BindTexture (GL_TEXTURE_2D, gst_gl_memory_get_texture_id (in_tex));
+
+  gst_gl_filter_draw_fullscreen_quad (filter);
+
+  return TRUE;
+}
+
+static gboolean
+gst_gl_box_filter_texture (GstGLFilter * filter, GstGLMemory * in_tex,
+    GstGLMemory * out_tex)
+{
+  GstGLBox *box = GST_GL_BOX (filter);
+
+  if (gst_gl_context_get_gl_api (GST_GL_BASE_FILTER (filter)->context)) {
+    gst_gl_filter_render_to_target (filter, in_tex, out_tex, gst_gl_box_draw,
+        filter);
+  }
+
+  return TRUE;
+}
diff --git a/plugins/gstglbox.h b/plugins/gstglbox.h
new file mode 100644
index 0000000..f6a738d
--- /dev/null
+++ b/plugins/gstglbox.h
@@ -0,0 +1,53 @@
+/*
+ * # Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GST_GLBOX_H
+#define _GST_GLBOX_H
+
+#include <gst/gst.h>
+
+#include <gst/gl/gstglfilter.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_GL_BOX            (gst_gl_box_get_type())
+#define GST_GL_BOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_BOX,GstGLBox))
+#define GST_IS_GL_BOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_BOX))
+#define GST_GL_BOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_GL_BOX,GstGLBoxClass))
+#define GST_IS_GL_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_GL_BOX))
+#define GST_GL_BOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_BOX,GstGLBoxClass))
+
+typedef struct _GstGLBox GstGLBox;
+typedef struct _GstGLBoxClass GstGLBoxClass;
+
+
+struct _GstGLBox
+{
+  GstGLFilter filter;
+  GstGLShader *shader;
+  GstVideoRectangle viewport;
+};
+
+struct _GstGLBoxClass
+{
+    GstGLFilterClass filter_class;
+};
+
+GType gst_gl_box_get_type (void);
+
+G_END_DECLS
+
+#endif /* _GST_GLBOX_H */
diff --git a/plugins/python/glbox.py b/plugins/python/glbox.py
deleted file mode 100644
index 5458a02..0000000
--- a/plugins/python/glbox.py
+++ /dev/null
@@ -1,287 +0,0 @@
-# Copyright 2019 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import numpy
-import os
-import time
-
-import gi
-gi.require_version('GLib', '2.0')
-gi.require_version('GObject', '2.0')
-gi.require_version('Gst', '1.0')
-gi.require_version('GstGL', '1.0')
-gi.require_version('GstVideo', '1.0')
-from gi.repository import GLib, GObject, Gst, GstGL, GstVideo
-
-from OpenGL.arrays.arraydatatype import ArrayDatatype
-from OpenGL.GLES3 import (
-    glActiveTexture, glBindBuffer, glBindTexture, glBindVertexArray, glBufferData, glDeleteBuffers,
-    glClear, glClearColor, glDeleteVertexArrays, glDrawElements, glEnableVertexAttribArray,
-    glGenBuffers, glGenVertexArrays, glVertexAttribPointer)
-from OpenGL.GLES3 import (
-    GL_ARRAY_BUFFER, GL_COLOR_BUFFER_BIT,GL_ELEMENT_ARRAY_BUFFER, GL_FALSE, GL_FLOAT,
-    GL_STATIC_DRAW, GL_TEXTURE0, GL_TEXTURE_2D, GL_TRIANGLES, GL_UNSIGNED_SHORT, GL_VERTEX_SHADER)
-
-
-SINK_CAPS = 'video/x-raw(memory:GLMemory),format=RGBA,width=[1,{max_int}],height=[1,{max_int}],texture-target=2D'
-SINK_CAPS = Gst.Caps.from_string(SINK_CAPS.format(max_int=GLib.MAXINT))
-
-SRC_CAPS = 'video/x-raw(memory:GLMemory),format=RGBA,width=[1,{max_int}],height=[1,{max_int}],texture-target=2D'
-SRC_CAPS = Gst.Caps.from_string(SRC_CAPS.format(max_int=GLib.MAXINT))
-
-VERTEX_SHADER = '''
-attribute vec4 a_position;
-attribute vec2 a_texcoord;
-varying vec2 v_texcoord;
-uniform float u_scale_x;
-uniform float u_scale_y;
-void main()
-{
-  v_texcoord = a_texcoord;
-  gl_Position = vec4(a_position.x * u_scale_x, a_position.y * u_scale_y, a_position.zw);
-}
-'''
-
-POSITIONS = numpy.array([
-        -1.0, -1.0,
-         1.0, -1.0,
-         1.0,  1.0,
-        -1.0,  1.0,
-    ], dtype=numpy.float32)
-
-TEXCOORDS = numpy.array([
-         0.0, 0.0,
-         1.0, 0.0,
-         1.0, 1.0,
-         0.0, 1.0,
-    ], dtype=numpy.float32)
-
-INDICES = numpy.array([
-         0, 1, 2, 0, 2, 3
-    ], dtype=numpy.uint16)
-
-class GlBox(GstGL.GLFilter):
-    __gstmetadata__ = ('GlBox',
-                       'Filter/Converter/Video',
-                       'Scale video preserving aspect ratio',
-                       'Coral <coral-support@google.com>')
-    __gsttemplates__ = (Gst.PadTemplate.new('sink',
-                        Gst.PadDirection.SINK,
-                        Gst.PadPresence.ALWAYS,
-                        SINK_CAPS),
-                        Gst.PadTemplate.new('src',
-                        Gst.PadDirection.SRC,
-                        Gst.PadPresence.ALWAYS,
-                        SRC_CAPS))
-    __gproperties__ = {
-        'x': (int,
-                'Frame x coordinate',
-                'Frame x coordinate',
-                0,
-                GLib.MAXINT,
-                0,
-                GObject.ParamFlags.READABLE),
-        'y': (int,
-                'Frame y coordinate',
-                'Frame y coordinate',
-                0,
-                GLib.MAXINT,
-                0,
-                GObject.ParamFlags.READABLE),
-        'width': (int,
-                'Frame width',
-                'Frame width',
-                0,
-                GLib.MAXINT,
-                0,
-                GObject.ParamFlags.READABLE),
-        'height': (int,
-                'Frame height',
-                'Frame height',
-                0,
-                GLib.MAXINT,
-                0,
-                GObject.ParamFlags.READABLE),
-        'scale-x': (float,
-                'Frame scaling factor x',
-                'Frame scaling factor x',
-                0,
-                GLib.MAXFLOAT,
-                0,
-                GObject.ParamFlags.READABLE),
-        'scale-y': (float,
-                'Frame scaling factor y',
-                'Frame scaling factor y',
-                0,
-                GLib.MAXFLOAT,
-                0,
-                GObject.ParamFlags.READABLE),
-    }
-
-    def __init__(self):
-        GstGL.GLFilter.__init__(self)
-        self.x, self.y, self.w, self.h = 0, 0, 0, 0
-        self.scale_x, self.scale_y = 1.0, 1.0
-
-        self.shader = None
-        self.vao_id = 0
-        self.positions_buffer = 0
-        self.texcoords_buffer = 0
-        self.vbo_indices_buffer = 0
-        self.print_fps = int(os.environ.get('PRINT_FPS', '0'))
-        self.fps_start = 0
-        self.frames = 0
-
-    def do_get_property(self, prop):
-        if prop.name == 'x':
-            return self.x
-        elif prop.name == 'y':
-            return self.y
-        elif prop.name == 'width':
-            return self.w
-        elif prop.name == 'height':
-            return self.h
-        elif prop.name == 'scale-x':
-            return self.scale_x
-        elif prop.name == 'scale-y':
-            return self.scale_y
-        else:
-            raise AttributeError('Unknown property %s' % prop.name)
-
-    def do_transform_internal_caps(self, direction, caps, filter_caps):
-        res = SINK_CAPS if direction == Gst.PadDirection.SRC else SRC_CAPS
-
-        if filter_caps:
-            res = res.intersect(filter_caps)
-        return res
-
-
-    def do_gl_start(self):
-        frag_stage = GstGL.GLSLStage.new_default_fragment(self.context)
-        vert_stage = GstGL.GLSLStage.new_with_string(self.context,
-            GL_VERTEX_SHADER,
-            GstGL.GLSLVersion.NONE,
-            GstGL.GLSLProfile.COMPATIBILITY | GstGL.GLSLProfile.ES,
-            VERTEX_SHADER)
-
-        self.shader = GstGL.GLShader.new(self.context)
-        self.shader.compile_attach_stage(vert_stage)
-        self.shader.compile_attach_stage(frag_stage)
-        self.shader.link()
-
-        a_position = self.shader.get_attribute_location('a_position')
-        a_texcoord = self.shader.get_attribute_location('a_texcoord')
-
-        self.vao_id = glGenVertexArrays(1)
-        glBindVertexArray(self.vao_id)
-
-        self.positions_buffer = glGenBuffers(1)
-        glBindBuffer(GL_ARRAY_BUFFER, self.positions_buffer)
-        glBufferData(GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(POSITIONS), POSITIONS, GL_STATIC_DRAW)
-
-        self.texcoords_buffer = glGenBuffers(1)
-        glBindBuffer(GL_ARRAY_BUFFER, self.texcoords_buffer)
-        glBufferData(GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(TEXCOORDS), TEXCOORDS, GL_STATIC_DRAW)
-
-        self.vbo_indices_buffer = glGenBuffers(1)
-        glBindBuffer(GL_ARRAY_BUFFER, self.vbo_indices_buffer)
-        glBufferData(GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(INDICES), INDICES, GL_STATIC_DRAW)
-
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.vbo_indices_buffer);
-        glBindBuffer(GL_ARRAY_BUFFER, self.positions_buffer);
-        glVertexAttribPointer.wrappedOperation(a_position, 2, GL_FLOAT, GL_FALSE, 0, None)
-        glBindBuffer(GL_ARRAY_BUFFER, self.texcoords_buffer);
-        glVertexAttribPointer.wrappedOperation(a_texcoord, 2, GL_FLOAT, GL_FALSE, 0, None)
-        glEnableVertexAttribArray(a_position)
-        glEnableVertexAttribArray(a_texcoord)
-
-        glBindVertexArray(0)
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
-        glBindBuffer(GL_ARRAY_BUFFER, 0)
-
-        return True
-
-    def do_gl_stop(self):
-        self.shader = None
-        glDeleteVertexArrays(1, [self.vao_id])
-        self.vao_id = None
-        glDeleteBuffers(1, [self.positions_buffer])
-        self.positions_buffer = None
-        glDeleteBuffers(1, [self.texcoords_buffer])
-        self.texcoords_buffer = None
-        glDeleteBuffers(1, [self.vbo_indices_buffer])
-        self.vbo_indices_buffer = None
-
-    def do_gst_gl_filter_set_caps(self, in_caps, out_caps):
-        in_info = GstVideo.VideoInfo()
-        in_info.from_caps(in_caps)
-
-        out_info = GstVideo.VideoInfo()
-        out_info.from_caps(out_caps)
-
-        in_ratio = in_info.width / in_info.height
-        out_ratio = out_info.width / out_info.height
-
-        if in_ratio > out_ratio:
-            w = out_info.width
-            h = out_info.width / in_ratio
-            x = 0
-            y = (out_info.height - h) / 2
-        elif in_ratio < out_ratio:
-            w = out_info.height * in_ratio
-            h = out_info.height
-            x = (out_info.width - w) / 2
-            y = 0
-        else:
-            w = out_info.width
-            h = out_info.height
-            x = 0
-            y = 0
-
-        self.x = int(x)
-        self.y = int(y)
-        self.w = int(w)
-        self.h = int(h)
-        self.scale_x = self.w / out_info.width
-        self.scale_y = self.h / out_info.height
-        return True
-
-    def do_filter_texture(self, in_tex, out_tex):
-        self.render_to_target(in_tex, out_tex, self.do_render)
-        self.frames += 1
-        if not self.fps_start:
-            self.fps_start = time.monotonic()
-
-        elapsed = time.monotonic() - self.fps_start
-        if self.print_fps and elapsed > self.print_fps:
-            print('glbox: out {} ({:.2f} fps)'.format(
-                self.frames, self.frames / elapsed))
-            self.fps_start = time.monotonic()
-            self.frames = 0
-        return True
-
-    def do_render(self, filter, in_tex):
-        # Black borders.
-        glClearColor(0.0, 0.0, 0.0, 0.0)
-        glClear(GL_COLOR_BUFFER_BIT)
-
-        glBindVertexArray(self.vao_id)
-        glActiveTexture(GL_TEXTURE0)
-        glBindTexture(GL_TEXTURE_2D, in_tex.tex_id)
-        self.shader.use()
-        self.shader.set_uniform_1f('u_scale_x', self.scale_x)
-        self.shader.set_uniform_1f('u_scale_y', self.scale_y)
-        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, None)
-
-__gstelementfactory__ = ("glbox", Gst.Rank.NONE, GlBox)