| /* |
| * # 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; |
| } |