From 0d53611c59d1c29f1d1cf22f62a50ae1ee21096b Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Mon, 9 May 2016 20:26:51 +0800 Subject: [PATCH] glimagesink: support video rotation using transform matrix Add "rotate-method" to glimagesink and apply transform matrix to vertex coordinate to control rotation. Upstream-Status: Accepted[1.9.1] https://bugzilla.gnome.org/show_bug.cgi?id=765795 Signed-off-by: Haihua Hu --- ext/gl/gstglimagesink.c | 266 ++++++++++++++++++++++++++++++++--- ext/gl/gstglimagesink.h | 18 +++ gst-libs/gst/gl/gstglshaderstrings.c | 11 ++ gst-libs/gst/gl/gstglshaderstrings.h | 1 + gst-libs/gst/gl/gstglutils.c | 60 ++++++++ gst-libs/gst/gl/gstglutils.h | 5 + 6 files changed, 344 insertions(+), 17 deletions(-) diff --git a/ext/gl/gstglimagesink.c b/ext/gl/gstglimagesink.c index 185577f2a07e..fb60468b361e 100644 --- a/ext/gl/gstglimagesink.c +++ b/ext/gl/gstglimagesink.c @@ -120,6 +120,7 @@ G_DEFINE_TYPE (GstGLImageSinkBin, gst_gl_image_sink_bin, GST_TYPE_GL_SINK_BIN); enum { PROP_BIN_0, + PROP_BIN_ROTATE_METHOD, PROP_BIN_FORCE_ASPECT_RATIO, PROP_BIN_PIXEL_ASPECT_RATIO, PROP_BIN_HANDLE_EVENTS, @@ -181,6 +182,39 @@ _on_client_draw (GstGLImageSink * sink, GstGLContext * context, return ret; } +#define DEFAULT_ROTATE_METHOD GST_GL_ROTATE_METHOD_IDENTITY + +#define GST_TYPE_GL_ROTATE_METHOD (gst_gl_rotate_method_get_type()) + +static const GEnumValue rotate_methods[] = { + {GST_GL_ROTATE_METHOD_IDENTITY, "Identity (no rotation)", "none"}, + {GST_GL_ROTATE_METHOD_90R, "Rotate clockwise 90 degrees", "clockwise"}, + {GST_GL_ROTATE_METHOD_180, "Rotate 180 degrees", "rotate-180"}, + {GST_GL_ROTATE_METHOD_90L, "Rotate counter-clockwise 90 degrees", + "counterclockwise"}, + {GST_GL_ROTATE_METHOD_FLIP_HORIZ, "Flip horizontally", "horizontal-flip"}, + {GST_GL_ROTATE_METHOD_FLIP_VERT, "Flip vertically", "vertical-flip"}, + {GST_GL_ROTATE_METHOD_FLIP_UL_LR, + "Flip across upper left/lower right diagonal", "upper-left-diagonal"}, + {GST_GL_ROTATE_METHOD_FLIP_UR_LL, + "Flip across upper right/lower left diagonal", "upper-right-diagonal"}, + {GST_GL_ROTATE_METHOD_AUTO, + "Select rotate method based on image-orientation tag", "automatic"}, + {0, NULL, NULL}, +}; + +static GType +gst_gl_rotate_method_get_type (void) +{ + static GType rotate_method_type = 0; + + if (!rotate_method_type) { + rotate_method_type = g_enum_register_static ("GstGLRotateMethod", + rotate_methods); + } + return rotate_method_type; +} + static void gst_gl_image_sink_bin_init (GstGLImageSinkBin * self) { @@ -203,6 +237,12 @@ gst_gl_image_sink_bin_class_init (GstGLImageSinkBinClass * klass) gobject_class->set_property = gst_gl_image_sink_bin_set_property; /* gl sink */ + g_object_class_install_property (gobject_class, PROP_BIN_ROTATE_METHOD, + g_param_spec_enum ("rotate-method", + "rotate method", + "rotate method", + GST_TYPE_GL_ROTATE_METHOD, DEFAULT_ROTATE_METHOD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_BIN_FORCE_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio", @@ -291,6 +331,7 @@ static void gst_glimage_sink_set_property (GObject * object, guint prop_id, static void gst_glimage_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * param_spec); +static gboolean gst_glimage_sink_event (GstBaseSink *sink, GstEvent * event); static gboolean gst_glimage_sink_query (GstBaseSink * bsink, GstQuery * query); static void gst_glimage_sink_set_context (GstElement * element, GstContext * context); @@ -347,6 +388,7 @@ enum { ARG_0, ARG_DISPLAY, + PROP_ROTATE_METHOD, PROP_FORCE_ASPECT_RATIO, PROP_PIXEL_ASPECT_RATIO, PROP_CONTEXT, @@ -404,6 +446,128 @@ _display_size_to_stream_size (GstGLImageSink * gl_sink, gdouble x, GST_TRACE ("transform %fx%f into %fx%f", x, y, *stream_x, *stream_y); } +/* rotate 90 */ +static const gfloat clockwise_matrix[] = { + 0.0f, -1.0f, 0.0, 0.0f, + 1.0f, 0.0f, 0.0, 0.0f, + 0.0f, 0.0f, 1.0, 0.0f, + 0.0f, 0.0f, 0.0, 1.0f, +}; + +/* rotate 180 */ +static const gfloat clockwise_180_matrix[] = { + -1.0f, 0.0f, 0.0, 0.0f, + 0.0f, -1.0f, 0.0, 0.0f, + 0.0f, 0.0f, 1.0, 0.0f, + 0.0f, 0.0f, 0.0, 1.0f, +}; + +/* rotate 270 */ +static const gfloat counterclockwise_matrix[] = { + 0.0f, 1.0f, 0.0, 0.0f, + -1.0f, 0.0f, 0.0, 0.0f, + 0.0f, 0.0f, 1.0, 0.0f, + 0.0f, 0.0f, 0.0, 1.0f, +}; + +/* horizontal-flip */ +static const gfloat horizontal_flip_matrix[] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* vertical-flip */ +static const gfloat vertical_flip_matrix[] = { + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* upper-left-diagonal */ +static const gfloat upper_left_matrix[] = { + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +/* upper-right-diagonal */ +static const gfloat upper_right_matrix[] = { + 0.0f, -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +static void +gst_glimage_sink_set_rotate_method(GstGLImageSink *gl_sink, + GstGLRotateMethod method, gboolean from_tag) +{ + GstGLRotateMethod tag_method; + GST_GLIMAGE_SINK_LOCK (gl_sink); + if (from_tag) + tag_method = method; + else + gl_sink->rotate_method = method; + + if (gl_sink->rotate_method == GST_GL_ROTATE_METHOD_AUTO) + method = tag_method; + else + method = gl_sink->rotate_method; + + if (method != gl_sink->current_rotate_method) + { + GST_DEBUG_OBJECT (gl_sink, "Changing method from %s to %s", + rotate_methods[gl_sink->current_rotate_method].value_nick, + rotate_methods[method].value_nick); + + switch (method) + { + case GST_GL_ROTATE_METHOD_IDENTITY: + gl_sink->transform_matrix = NULL; + gl_sink->output_mode_changed = TRUE; + break; + case GST_GL_ROTATE_METHOD_90R: + gl_sink->transform_matrix = clockwise_matrix; + gl_sink->output_mode_changed = TRUE; + break; + case GST_GL_ROTATE_METHOD_180: + gl_sink->transform_matrix = clockwise_180_matrix; + gl_sink->output_mode_changed = TRUE; + break; + case GST_GL_ROTATE_METHOD_90L: + gl_sink->transform_matrix = counterclockwise_matrix; + gl_sink->output_mode_changed = TRUE; + break; + case GST_GL_ROTATE_METHOD_FLIP_HORIZ: + gl_sink->transform_matrix = horizontal_flip_matrix; + gl_sink->output_mode_changed = TRUE; + break; + case GST_GL_ROTATE_METHOD_FLIP_VERT: + gl_sink->transform_matrix = vertical_flip_matrix; + gl_sink->output_mode_changed = TRUE; + break; + case GST_GL_ROTATE_METHOD_FLIP_UL_LR: + gl_sink->transform_matrix = upper_left_matrix; + gl_sink->output_mode_changed = TRUE; + break; + case GST_GL_ROTATE_METHOD_FLIP_UR_LL: + gl_sink->transform_matrix = upper_right_matrix; + gl_sink->output_mode_changed = TRUE; + break; + default: + g_assert_not_reached(); + break; + } + + gl_sink->current_rotate_method = method; + } + GST_GLIMAGE_SINK_UNLOCK (gl_sink); +} + static void gst_glimage_sink_navigation_send_event (GstNavigation * navigation, GstStructure * structure) @@ -485,6 +649,13 @@ gst_glimage_sink_class_init (GstGLImageSinkClass * klass) gobject_class->set_property = gst_glimage_sink_set_property; gobject_class->get_property = gst_glimage_sink_get_property; + g_object_class_install_property (gobject_class, PROP_ROTATE_METHOD, + g_param_spec_enum ("rotate-method", + "rotate method", + "rotate method", + GST_TYPE_GL_ROTATE_METHOD, DEFAULT_ROTATE_METHOD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio", "When enabled, scaling will respect original aspect ratio", @@ -577,6 +748,7 @@ gst_glimage_sink_class_init (GstGLImageSinkClass * klass) gstelement_class->change_state = gst_glimage_sink_change_state; gstelement_class->set_context = gst_glimage_sink_set_context; + gstbasesink_class->event = gst_glimage_sink_event; gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_glimage_sink_query); gstbasesink_class->set_caps = gst_glimage_sink_set_caps; gstbasesink_class->get_caps = gst_glimage_sink_get_caps; @@ -610,6 +782,9 @@ gst_glimage_sink_init (GstGLImageSink * glimage_sink) glimage_sink->mview_output_flags = DEFAULT_MULTIVIEW_FLAGS; glimage_sink->mview_downmix_mode = DEFAULT_MULTIVIEW_DOWNMIX; + glimage_sink->current_rotate_method = DEFAULT_ROTATE_METHOD; + glimage_sink->transform_matrix = NULL; + g_mutex_init (&glimage_sink->drawing_lock); } @@ -624,6 +799,9 @@ gst_glimage_sink_set_property (GObject * object, guint prop_id, glimage_sink = GST_GLIMAGE_SINK (object); switch (prop_id) { + case PROP_ROTATE_METHOD: + gst_glimage_sink_set_rotate_method (glimage_sink, g_value_get_enum (value), FALSE); + break; case PROP_FORCE_ASPECT_RATIO: { glimage_sink->keep_aspect_ratio = g_value_get_boolean (value); @@ -691,6 +869,9 @@ gst_glimage_sink_get_property (GObject * object, guint prop_id, glimage_sink = GST_GLIMAGE_SINK (object); switch (prop_id) { + case PROP_ROTATE_METHOD: + g_value_set_enum (value, glimage_sink->current_rotate_method); + break; case PROP_FORCE_ASPECT_RATIO: g_value_set_boolean (value, glimage_sink->keep_aspect_ratio); break; @@ -848,6 +1029,50 @@ context_error: } static gboolean +gst_glimage_sink_event (GstBaseSink *sink, GstEvent * event) +{ + GstGLImageSink *gl_sink = GST_GLIMAGE_SINK (sink); + GstTagList *taglist; + gchar *orientation; + gboolean ret; + + GST_DEBUG_OBJECT (gl_sink, "handling %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG: + gst_event_parse_tag (event, &taglist); + + if (gst_tag_list_get_string (taglist, "image-orientation", &orientation)) { + if (!g_strcmp0 ("rotate-0", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_IDENTITY, TRUE); + else if (!g_strcmp0 ("rotate-90", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_90R, TRUE); + else if (!g_strcmp0 ("rotate-180", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_180, TRUE); + else if (!g_strcmp0 ("rotate-270", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_90L, TRUE); + else if (!g_strcmp0 ("flip-rotate-0", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_FLIP_HORIZ, TRUE); + else if (!g_strcmp0 ("flip-rotate-90", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_FLIP_UR_LL, TRUE); + else if (!g_strcmp0 ("flip-rotate-180", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_FLIP_VERT, TRUE); + else if (!g_strcmp0 ("flip-rotate-270", orientation)) + gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_FLIP_UL_LR, TRUE); + + g_free (orientation); + } + break; + default: + break; + } + + ret = GST_BASE_SINK_CLASS (parent_class)->event (sink, event); + + return ret; +} + +static gboolean gst_glimage_sink_query (GstBaseSink * bsink, GstQuery * query) { GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (bsink); @@ -1817,7 +2042,10 @@ gst_glimage_sink_thread_init_redisplay (GstGLImageSink * gl_sink) GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, gst_gl_shader_string_fragment_external_oes_default); } else { - vert_stage = gst_glsl_stage_new_default_vertex (gl_sink->context); + vert_stage = gst_glsl_stage_new_with_string (gl_sink->context, + GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE, + GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, + gst_gl_shader_string_vertex_mat4_vertex_transform); frag_stage = gst_glsl_stage_new_default_fragment (gl_sink->context); } if (!vert_stage || !frag_stage) { @@ -1943,8 +2171,16 @@ gst_glimage_sink_on_resize (GstGLImageSink * gl_sink, gint width, gint height) src.x = 0; src.y = 0; - src.w = GST_VIDEO_SINK_WIDTH (gl_sink); - src.h = GST_VIDEO_SINK_HEIGHT (gl_sink); + if (gl_sink->current_rotate_method == GST_GL_ROTATE_METHOD_90R + || gl_sink->current_rotate_method == GST_GL_ROTATE_METHOD_90L + || gl_sink->current_rotate_method == GST_GL_ROTATE_METHOD_FLIP_UL_LR + || gl_sink->current_rotate_method == GST_GL_ROTATE_METHOD_FLIP_UR_LL) { + src.h = GST_VIDEO_SINK_WIDTH (gl_sink); + src.w = GST_VIDEO_SINK_HEIGHT (gl_sink); + } else { + src.w = GST_VIDEO_SINK_WIDTH (gl_sink); + src.h = GST_VIDEO_SINK_HEIGHT (gl_sink); + } dst.x = 0; dst.y = 0; @@ -1974,13 +2210,6 @@ gst_glimage_sink_on_resize (GstGLImageSink * gl_sink, gint width, gint height) GST_GLIMAGE_SINK_UNLOCK (gl_sink); } -static const gfloat identity_matrix[] = { - 1.0f, 0.0f, 0.0, 0.0f, - 0.0f, 1.0f, 0.0, 0.0f, - 0.0f, 0.0f, 1.0, 0.0f, - 0.0f, 0.0f, 0.0, 1.0f, -}; - static void gst_glimage_sink_on_draw (GstGLImageSink * gl_sink) { @@ -2109,18 +2338,21 @@ gst_glimage_sink_on_draw (GstGLImageSink * gl_sink) gl->ActiveTexture (GL_TEXTURE0); gl->BindTexture (gl_target, gl_sink->redisplay_texture); gst_gl_shader_set_uniform_1i (gl_sink->redisplay_shader, "tex", 0); - if (gl_sink->texture_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) { + { GstVideoAffineTransformationMeta *af_meta; + gfloat matrix[16]; af_meta = gst_buffer_get_video_affine_transformation_meta (gl_sink->stored_buffer[0]); - if (af_meta) - gst_gl_shader_set_uniform_matrix_4fv (gl_sink->redisplay_shader, - "u_transformation", 1, FALSE, af_meta->matrix); - else - gst_gl_shader_set_uniform_matrix_4fv (gl_sink->redisplay_shader, - "u_transformation", 1, FALSE, identity_matrix); + + gst_gl_get_affine_transformation_meta_as_ndc (af_meta, matrix); + + if (gl_sink->transform_matrix) + gst_gl_multiply_matrix4 (gl_sink->transform_matrix, matrix, matrix); + + gst_gl_shader_set_uniform_matrix_4fv (gl_sink->redisplay_shader, + "u_transformation", 1, FALSE, matrix); } gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); diff --git a/ext/gl/gstglimagesink.h b/ext/gl/gstglimagesink.h index f9a052a12ed8..c0ab3ffb3b47 100644 --- a/ext/gl/gstglimagesink.h +++ b/ext/gl/gstglimagesink.h @@ -44,6 +44,19 @@ GST_DEBUG_CATEGORY_EXTERN (gst_debug_glimage_sink); #define GST_IS_GLIMAGE_SINK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GLIMAGE_SINK)) +typedef enum +{ + GST_GL_ROTATE_METHOD_IDENTITY, + GST_GL_ROTATE_METHOD_90R, + GST_GL_ROTATE_METHOD_180, + GST_GL_ROTATE_METHOD_90L, + GST_GL_ROTATE_METHOD_FLIP_HORIZ, + GST_GL_ROTATE_METHOD_FLIP_VERT, + GST_GL_ROTATE_METHOD_FLIP_UL_LR, + GST_GL_ROTATE_METHOD_FLIP_UR_LL, + GST_GL_ROTATE_METHOD_AUTO +}GstGLRotateMethod; + typedef struct _GstGLImageSink GstGLImageSink; typedef struct _GstGLImageSinkClass GstGLImageSinkClass; @@ -126,6 +139,11 @@ struct _GstGLImageSink GstGLOverlayCompositor *overlay_compositor; + /* current video flip method */ + GstGLRotateMethod current_rotate_method; + GstGLRotateMethod rotate_method; + const gfloat *transform_matrix; + /* fps print support */ guint64 frame_showed; GstClockTime run_time; diff --git a/gst-libs/gst/gl/gstglshaderstrings.c b/gst-libs/gst/gl/gstglshaderstrings.c index 729be6f75100..10186f1b86ec 100644 --- a/gst-libs/gst/gl/gstglshaderstrings.c +++ b/gst-libs/gst/gl/gstglshaderstrings.c @@ -35,6 +35,17 @@ const gchar *gst_gl_shader_string_vertex_default = " v_texcoord = a_texcoord;\n" "}\n"; +const gchar *gst_gl_shader_string_vertex_mat4_vertex_transform = + "uniform mat4 u_transformation;\n" + "attribute vec4 a_position;\n" + "attribute vec2 a_texcoord;\n" + "varying vec2 v_texcoord;\n" + "void main()\n" + "{\n" + " gl_Position = u_transformation * a_position;\n" + " v_texcoord = a_texcoord;\n" + "}\n"; + const gchar *gst_gl_shader_string_vertex_mat4_texture_transform = "uniform mat4 u_transformation;\n" "attribute vec4 a_position;\n" diff --git a/gst-libs/gst/gl/gstglshaderstrings.h b/gst-libs/gst/gl/gstglshaderstrings.h index 49ea8de427c5..f9a13c8b3d92 100644 --- a/gst-libs/gst/gl/gstglshaderstrings.h +++ b/gst-libs/gst/gl/gstglshaderstrings.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS extern const gchar *gst_gl_shader_string_vertex_default; extern const gchar *gst_gl_shader_string_fragment_default; +extern const gchar *gst_gl_shader_string_vertex_mat4_vertex_transform; extern const gchar *gst_gl_shader_string_vertex_mat4_texture_transform; extern const gchar *gst_gl_shader_string_fragment_external_oes_default; diff --git a/gst-libs/gst/gl/gstglutils.c b/gst-libs/gst/gl/gstglutils.c index e2e04e407ed7..a38772f10be3 100644 --- a/gst-libs/gst/gl/gstglutils.c +++ b/gst-libs/gst/gl/gstglutils.c @@ -1067,3 +1067,63 @@ gst_gl_value_set_texture_target_from_mask (GValue * value, return ret; } } + +static const gfloat identity_matrix[] = { + 1.0f, 0.0f, 0.0, 0.0f, + 0.0f, 1.0f, 0.0, 0.0f, + 0.0f, 0.0f, 1.0, 0.0f, + 0.0f, 0.0f, 0.0, 1.0f, +}; + +static const gfloat from_ndc_matrix[] = { + 0.5f, 0.0f, 0.0, 0.5f, + 0.0f, 0.5f, 0.0, 0.5f, + 0.0f, 0.0f, 0.5, 0.5f, + 0.0f, 0.0f, 0.0, 1.0f, +}; + +static const gfloat to_ndc_matrix[] = { + 2.0f, 0.0f, 0.0, -1.0f, + 0.0f, 2.0f, 0.0, -1.0f, + 0.0f, 0.0f, 2.0, -1.0f, + 0.0f, 0.0f, 0.0, 1.0f, +}; + +void +gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result) +{ + int i, j, k; + gfloat tmp[16] = { 0.0f }; + + if (!a || !b || !result) + return; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + tmp[i + (j * 4)] += a[i + (k * 4)] * b[k + (j * 4)]; + } + } + } + + for (i = 0; i < 16; i++) + result[i] = tmp[i]; +} + +void +gst_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta * + meta, gfloat * matrix) +{ + if (!meta) { + int i; + + for (i = 0; i < 16; i++) { + matrix[i] = identity_matrix[i]; + } + } else { + gfloat tmp[16] = { 0.0f }; + + gst_gl_multiply_matrix4 (from_ndc_matrix, meta->matrix, tmp); + gst_gl_multiply_matrix4 (tmp, to_ndc_matrix, matrix); + } +} diff --git a/gst-libs/gst/gl/gstglutils.h b/gst-libs/gst/gl/gstglutils.h index 1c5ab12344fe..fc12801978e9 100644 --- a/gst-libs/gst/gl/gstglutils.h +++ b/gst-libs/gst/gl/gstglutils.h @@ -24,6 +24,7 @@ #include #include +#include G_BEGIN_DECLS @@ -116,6 +117,10 @@ gboolean gst_gl_value_set_texture_target_from_mask (GValue * value, gboolean gst_gl_value_set_texture_target (GValue * value, GstGLTextureTarget target); GstGLTextureTarget gst_gl_value_get_texture_target_mask (const GValue * value); +void gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result); +void gst_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta * + meta, gfloat * matrix); + G_END_DECLS #endif /* __GST_GL_UTILS_H__ */