blob: a390d8adf7a0003f2d900beec5abb4f5ee1a7188 [file] [log] [blame]
/*
* # 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;
}