From 8d122f21ad66a340205cda8342c473922cf0b8eb Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Wed, 7 Aug 2019 16:15:40 +0100 Subject: [PATCH] gl: New pushsrc-based glbasesrc base class The gltestsrc element was refactored to inherit from this base class which handles the GL context. The sub-class only needs to implement the gl_start, gl_stop and fill_gl_memory vfuncs, along with properly advertizing the GL APIs it supports through the supported_gl_api GstGLBaseSrc class attribute. --- ext/gl/gltestsrc.c | 9 +- ext/gl/gstgltestsrc.c | 539 +++------------------------------- ext/gl/gstgltestsrc.h | 24 +- gst-libs/gst/gl/Makefile.am | 2 + gst-libs/gst/gl/gl.h | 1 + gst-libs/gst/gl/gstgl_fwd.h | 6 + gst-libs/gst/gl/gstglbasesrc.c | 641 +++++++++++++++++++++++++++++++++++++++++ gst-libs/gst/gl/gstglbasesrc.h | 96 ++++++ gst-libs/gst/gl/meson.build | 2 + 9 files changed, 800 insertions(+), 520 deletions(-) create mode 100644 gst-libs/gst/gl/gstglbasesrc.c create mode 100644 gst-libs/gst/gl/gstglbasesrc.h diff --git a/ext/gl/gltestsrc.c b/ext/gl/gltestsrc.c index 78991bd..04b2dfe 100644 --- a/ext/gl/gltestsrc.c +++ b/ext/gl/gltestsrc.c @@ -515,6 +515,7 @@ _src_smpte_fill_bound_fbo (gpointer impl) { struct SrcSMPTE *src = impl; gint attr_color_position = -1; + GstGLBaseSrc *bsrc = GST_GL_BASE_SRC (src->base.base.src); src->base.n_attributes = 2; if (src->base.shader) @@ -535,7 +536,7 @@ _src_smpte_fill_bound_fbo (gpointer impl) src->base.index_offset = (N_QUADS - 1) * 6 * sizeof (gushort); gst_gl_shader_use (src->snow_shader); gst_gl_shader_set_uniform_1f (src->snow_shader, "time", - (gfloat) src->base.base.src->running_time / GST_SECOND); + (gfloat) bsrc->running_time / GST_SECOND); if (!_src_shader_fill_bound_fbo (impl)) return FALSE; src->attr_snow_position = src->base.attributes[0].location; @@ -863,13 +864,14 @@ static gboolean _src_snow_fill_bound_fbo (gpointer impl) { struct SrcShader *src = impl; + GstGLBaseSrc *bsrc = GST_GL_BASE_SRC (src->base.src); g_return_val_if_fail (src->base.context, FALSE); g_return_val_if_fail (src->shader, FALSE); gst_gl_shader_use (src->shader); gst_gl_shader_set_uniform_1f (src->shader, "time", - (gfloat) src->base.src->running_time / GST_SECOND); + (gfloat) bsrc->running_time / GST_SECOND); return _src_shader_fill_bound_fbo (impl); } @@ -1007,13 +1009,14 @@ static gboolean _src_mandelbrot_fill_bound_fbo (gpointer impl) { struct SrcShader *src = impl; + GstGLBaseSrc *bsrc = GST_GL_BASE_SRC (src->base.src); g_return_val_if_fail (src->base.context, FALSE); g_return_val_if_fail (src->shader, FALSE); gst_gl_shader_use (src->shader); gst_gl_shader_set_uniform_1f (src->shader, "time", - (gfloat) src->base.src->running_time / GST_SECOND); + (gfloat) bsrc->running_time / GST_SECOND); return _src_shader_fill_bound_fbo (impl); } diff --git a/ext/gl/gstgltestsrc.c b/ext/gl/gstgltestsrc.c index a98a8e9..116090a 100644 --- a/ext/gl/gstgltestsrc.c +++ b/ext/gl/gstgltestsrc.c @@ -48,9 +48,6 @@ #include "gstgltestsrc.h" #include "gltestsrc.h" -#define USE_PEER_BUFFERALLOC -#define SUPPORTED_GL_APIS (GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2) - GST_DEBUG_CATEGORY_STATIC (gl_test_src_debug); #define GST_CAT_DEFAULT gl_test_src_debug @@ -58,7 +55,6 @@ enum { PROP_0, PROP_PATTERN, - PROP_TIMESTAMP_OFFSET, PROP_IS_LIVE /* FILL ME */ }; @@ -77,7 +73,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", /* *INDENT-ON* */ #define gst_gl_test_src_parent_class parent_class -G_DEFINE_TYPE (GstGLTestSrc, gst_gl_test_src, GST_TYPE_PUSH_SRC); +G_DEFINE_TYPE (GstGLTestSrc, gst_gl_test_src, GST_TYPE_GL_BASE_SRC); static void gst_gl_test_src_set_pattern (GstGLTestSrc * gltestsrc, int pattern_type); @@ -86,30 +82,13 @@ static void gst_gl_test_src_set_property (GObject * object, guint prop_id, static void gst_gl_test_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static gboolean gst_gl_test_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps); static GstCaps *gst_gl_test_src_fixate (GstBaseSrc * bsrc, GstCaps * caps); - static gboolean gst_gl_test_src_is_seekable (GstBaseSrc * psrc); -static gboolean gst_gl_test_src_do_seek (GstBaseSrc * bsrc, - GstSegment * segment); -static gboolean gst_gl_test_src_query (GstBaseSrc * bsrc, GstQuery * query); -static void gst_gl_test_src_set_context (GstElement * element, - GstContext * context); -static GstStateChangeReturn gst_gl_test_src_change_state (GstElement * element, - GstStateChange transition); - -static void gst_gl_test_src_get_times (GstBaseSrc * basesrc, - GstBuffer * buffer, GstClockTime * start, GstClockTime * end); -static GstFlowReturn gst_gl_test_src_fill (GstPushSrc * psrc, - GstBuffer * buffer); -static gboolean gst_gl_test_src_start (GstBaseSrc * basesrc); -static gboolean gst_gl_test_src_stop (GstBaseSrc * basesrc); -static gboolean gst_gl_test_src_decide_allocation (GstBaseSrc * basesrc, - GstQuery * query); - static gboolean gst_gl_test_src_callback (gpointer stuff); - -static gboolean gst_gl_test_src_init_shader (GstGLTestSrc * gltestsrc); +static gboolean gst_gl_test_src_gl_start (GstGLBaseSrc * src); +static void gst_gl_test_src_gl_stop (GstGLBaseSrc * src); +static gboolean gst_gl_test_src_fill_memory (GstGLBaseSrc * src, + GstGLMemory * memory); #define GST_TYPE_GL_TEST_SRC_PATTERN (gst_gl_test_src_pattern_get_type ()) static GType @@ -146,15 +125,15 @@ gst_gl_test_src_class_init (GstGLTestSrcClass * klass) { GObjectClass *gobject_class; GstBaseSrcClass *gstbasesrc_class; - GstPushSrcClass *gstpushsrc_class; + GstGLBaseSrcClass *gstglbasesrc_class; GstElementClass *element_class; GST_DEBUG_CATEGORY_INIT (gl_test_src_debug, "gltestsrc", 0, "Video Test Source"); - gobject_class = (GObjectClass *) klass; - gstbasesrc_class = (GstBaseSrcClass *) klass; - gstpushsrc_class = (GstPushSrcClass *) klass; + gobject_class = G_OBJECT_CLASS (klass); + gstbasesrc_class = GST_BASE_SRC_CLASS (klass); + gstglbasesrc_class = GST_GL_BASE_SRC_CLASS (klass); element_class = GST_ELEMENT_CLASS (klass); gobject_class->set_property = gst_gl_test_src_set_property; @@ -164,11 +143,6 @@ gst_gl_test_src_class_init (GstGLTestSrcClass * klass) g_param_spec_enum ("pattern", "Pattern", "Type of test pattern to generate", GST_TYPE_GL_TEST_SRC_PATTERN, GST_GL_TEST_SRC_SMPTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, - PROP_TIMESTAMP_OFFSET, g_param_spec_int64 ("timestamp-offset", - "Timestamp offset", - "An offset added to timestamps set on buffers (in ns)", G_MININT64, - G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_IS_LIVE, g_param_spec_boolean ("is-live", "Is Live", "Whether to act as a live source", FALSE, @@ -180,32 +154,20 @@ gst_gl_test_src_class_init (GstGLTestSrcClass * klass) gst_element_class_add_static_pad_template (element_class, &src_factory); - element_class->set_context = gst_gl_test_src_set_context; - element_class->change_state = gst_gl_test_src_change_state; - - gstbasesrc_class->set_caps = gst_gl_test_src_setcaps; gstbasesrc_class->is_seekable = gst_gl_test_src_is_seekable; - gstbasesrc_class->do_seek = gst_gl_test_src_do_seek; - gstbasesrc_class->query = gst_gl_test_src_query; - gstbasesrc_class->get_times = gst_gl_test_src_get_times; - gstbasesrc_class->start = gst_gl_test_src_start; - gstbasesrc_class->stop = gst_gl_test_src_stop; gstbasesrc_class->fixate = gst_gl_test_src_fixate; - gstbasesrc_class->decide_allocation = gst_gl_test_src_decide_allocation; - gstpushsrc_class->fill = gst_gl_test_src_fill; + gstglbasesrc_class->supported_gl_api = + GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2; + gstglbasesrc_class->gl_start = gst_gl_test_src_gl_start; + gstglbasesrc_class->gl_stop = gst_gl_test_src_gl_stop; + gstglbasesrc_class->fill_gl_memory = gst_gl_test_src_fill_memory; } static void gst_gl_test_src_init (GstGLTestSrc * src) { gst_gl_test_src_set_pattern (src, GST_GL_TEST_SRC_SMPTE); - - src->timestamp_offset = 0; - - /* we operate in time */ - gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); - gst_base_src_set_live (GST_BASE_SRC (src), FALSE); } static GstCaps * @@ -244,9 +206,6 @@ gst_gl_test_src_set_property (GObject * object, guint prop_id, case PROP_PATTERN: gst_gl_test_src_set_pattern (src, g_value_get_enum (value)); break; - case PROP_TIMESTAMP_OFFSET: - src->timestamp_offset = g_value_get_int64 (value); - break; case PROP_IS_LIVE: gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value)); break; @@ -265,9 +224,6 @@ gst_gl_test_src_get_property (GObject * object, guint prop_id, case PROP_PATTERN: g_value_set_enum (value, src->set_pattern); break; - case PROP_TIMESTAMP_OFFSET: - g_value_set_int64 (value, src->timestamp_offset); - break; case PROP_IS_LIVE: g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (src))); break; @@ -278,133 +234,6 @@ gst_gl_test_src_get_property (GObject * object, guint prop_id, } static gboolean -gst_gl_test_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps) -{ - GstGLTestSrc *gltestsrc = GST_GL_TEST_SRC (bsrc); - - GST_DEBUG ("setcaps"); - - if (!gst_video_info_from_caps (&gltestsrc->out_info, caps)) - goto wrong_caps; - - gltestsrc->negotiated = TRUE; - - gst_caps_replace (&gltestsrc->out_caps, caps); - - return TRUE; - -/* ERRORS */ -wrong_caps: - { - GST_WARNING ("wrong caps"); - return FALSE; - } -} - -static void -gst_gl_test_src_set_context (GstElement * element, GstContext * context) -{ - GstGLTestSrc *src = GST_GL_TEST_SRC (element); - - gst_gl_handle_set_context (element, context, &src->display, - &src->other_context); - - if (src->display) - gst_gl_display_filter_gl_api (src->display, SUPPORTED_GL_APIS); - - GST_ELEMENT_CLASS (parent_class)->set_context (element, context); -} - -static gboolean -gst_gl_test_src_query (GstBaseSrc * bsrc, GstQuery * query) -{ - gboolean res = FALSE; - GstGLTestSrc *src; - - src = GST_GL_TEST_SRC (bsrc); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CONTEXT: - { - if (gst_gl_handle_context_query ((GstElement *) src, query, - src->display, src->context, src->other_context)) - return TRUE; - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - res = - gst_video_info_convert (&src->out_info, src_fmt, src_val, dest_fmt, - &dest_val); - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - - return res; - } - default: - break; - } - - return GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); -} - -static void -gst_gl_test_src_get_times (GstBaseSrc * basesrc, GstBuffer * buffer, - GstClockTime * start, GstClockTime * end) -{ - /* for live sources, sync on the timestamp of the buffer */ - if (gst_base_src_is_live (basesrc)) { - GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); - - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - /* get duration to calculate end time */ - GstClockTime duration = GST_BUFFER_DURATION (buffer); - - if (GST_CLOCK_TIME_IS_VALID (duration)) - *end = timestamp + duration; - *start = timestamp; - } - } else { - *start = -1; - *end = -1; - } -} - -static gboolean -gst_gl_test_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment) -{ - GstClockTime time; - GstGLTestSrc *src; - - src = GST_GL_TEST_SRC (bsrc); - - segment->time = segment->start; - time = segment->position; - - /* now move to the time indicated */ - if (src->out_info.fps_n) { - src->n_frames = gst_util_uint64_scale (time, - src->out_info.fps_n, src->out_info.fps_d * GST_SECOND); - } else - src->n_frames = 0; - - if (src->out_info.fps_n) { - src->running_time = gst_util_uint64_scale (src->n_frames, - src->out_info.fps_d * GST_SECOND, src->out_info.fps_n); - } else { - /* FIXME : Not sure what to set here */ - src->running_time = 0; - } - - g_return_val_if_fail (src->running_time <= time, FALSE); - - return TRUE; -} - -static gboolean gst_gl_test_src_is_seekable (GstBaseSrc * psrc) { /* we're seekable... */ @@ -412,288 +241,10 @@ gst_gl_test_src_is_seekable (GstBaseSrc * psrc) } static gboolean -gst_gl_test_src_init_shader (GstGLTestSrc * gltestsrc) -{ - if (gst_gl_context_get_gl_api (gltestsrc->context)) { - /* blocking call, wait until the opengl thread has compiled the shader */ -// if (gltestsrc->vertex_src == NULL) -// return FALSE; -// return gst_gl_context_gen_shader (gltestsrc->context, gltestsrc->vertex_src, -// gltestsrc->fragment_src, &gltestsrc->shader); - } - return TRUE; -} - -static void -_fill_gl (GstGLContext * context, GstGLTestSrc * src) -{ - src->gl_result = gst_gl_framebuffer_draw_to_texture (src->fbo, src->out_tex, - gst_gl_test_src_callback, src); -} - -static GstFlowReturn -gst_gl_test_src_fill (GstPushSrc * psrc, GstBuffer * buffer) -{ - GstGLTestSrc *src = GST_GL_TEST_SRC (psrc); - GstClockTime next_time; - GstVideoFrame out_frame; - GstGLSyncMeta *sync_meta; - - if (G_UNLIKELY (!src->negotiated || !src->context)) - goto not_negotiated; - - /* 0 framerate and we are at the second frame, eos */ - if (G_UNLIKELY (GST_VIDEO_INFO_FPS_N (&src->out_info) == 0 - && src->n_frames == 1)) - goto eos; - - if (!gst_video_frame_map (&out_frame, &src->out_info, buffer, - GST_MAP_WRITE | GST_MAP_GL)) { - return GST_FLOW_NOT_NEGOTIATED; - } - - src->out_tex = (GstGLMemory *) out_frame.map[0].memory; - - gst_gl_context_thread_add (src->context, (GstGLContextThreadFunc) _fill_gl, - src); - if (!src->gl_result) { - gst_video_frame_unmap (&out_frame); - goto gl_error; - } - gst_video_frame_unmap (&out_frame); - if (!src->gl_result) - goto gl_error; - - sync_meta = gst_buffer_get_gl_sync_meta (buffer); - if (sync_meta) - gst_gl_sync_meta_set_sync_point (sync_meta, src->context); - - GST_BUFFER_TIMESTAMP (buffer) = src->timestamp_offset + src->running_time; - GST_BUFFER_OFFSET (buffer) = src->n_frames; - src->n_frames++; - GST_BUFFER_OFFSET_END (buffer) = src->n_frames; - if (src->out_info.fps_n) { - next_time = gst_util_uint64_scale_int (src->n_frames * GST_SECOND, - src->out_info.fps_d, src->out_info.fps_n); - GST_BUFFER_DURATION (buffer) = next_time - src->running_time; - } else { - next_time = src->timestamp_offset; - /* NONE means forever */ - GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; - } - - src->running_time = next_time; - - return GST_FLOW_OK; - -gl_error: - { - GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (_("failed to draw pattern")), - (_("A GL error occurred"))); - return GST_FLOW_NOT_NEGOTIATED; - } -not_negotiated: - { - GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL), - (_("format wasn't negotiated before get function"))); - return GST_FLOW_NOT_NEGOTIATED; - } -eos: - { - GST_DEBUG_OBJECT (src, "eos: 0 framerate, frame %d", (gint) src->n_frames); - return GST_FLOW_EOS; - } -} - -static gboolean -gst_gl_test_src_start (GstBaseSrc * basesrc) -{ - GstGLTestSrc *src = GST_GL_TEST_SRC (basesrc); - - src->running_time = 0; - src->n_frames = 0; - src->negotiated = FALSE; - - return TRUE; -} - -static void -gst_gl_test_src_gl_stop (GstGLContext * context, GstGLTestSrc * src) -{ - if (src->fbo) - gst_object_unref (src->fbo); - src->fbo = NULL; - - if (src->shader) - gst_object_unref (src->shader); - src->shader = NULL; - - - if (src->src_impl) - src->src_funcs->free (src->src_impl); - src->src_impl = NULL; -} - -static gboolean -gst_gl_test_src_stop (GstBaseSrc * basesrc) -{ - GstGLTestSrc *src = GST_GL_TEST_SRC (basesrc); - - if (src->context) - gst_gl_context_thread_add (src->context, - (GstGLContextThreadFunc) gst_gl_test_src_gl_stop, src); - - gst_caps_replace (&src->out_caps, NULL); - - if (src->context) - gst_object_unref (src->context); - src->context = NULL; - - return TRUE; -} - -static gboolean -_find_local_gl_context (GstGLTestSrc * src) -{ - if (gst_gl_query_local_gl_context (GST_ELEMENT (src), GST_PAD_SRC, - &src->context)) - return TRUE; - return FALSE; -} - -static void -_src_generate_fbo_gl (GstGLContext * context, GstGLTestSrc * src) -{ - src->fbo = gst_gl_framebuffer_new_with_default_depth (src->context, - GST_VIDEO_INFO_WIDTH (&src->out_info), - GST_VIDEO_INFO_HEIGHT (&src->out_info)); -} - -static gboolean -gst_gl_test_src_decide_allocation (GstBaseSrc * basesrc, GstQuery * query) -{ - GstGLTestSrc *src = GST_GL_TEST_SRC (basesrc); - GstBufferPool *pool = NULL; - GstStructure *config; - GstCaps *caps; - guint min, max, size; - gboolean update_pool; - GError *error = NULL; - - if (!gst_gl_ensure_element_data (src, &src->display, &src->other_context)) - return FALSE; - - gst_gl_display_filter_gl_api (src->display, SUPPORTED_GL_APIS); - - _find_local_gl_context (src); - - if (!src->context) { - GST_OBJECT_LOCK (src->display); - do { - if (src->context) { - gst_object_unref (src->context); - src->context = NULL; - } - /* just get a GL context. we don't care */ - src->context = - gst_gl_display_get_gl_context_for_thread (src->display, NULL); - if (!src->context) { - if (!gst_gl_display_create_context (src->display, src->other_context, - &src->context, &error)) { - GST_OBJECT_UNLOCK (src->display); - goto context_error; - } - } - } while (!gst_gl_display_add_context (src->display, src->context)); - GST_OBJECT_UNLOCK (src->display); - } - - if ((gst_gl_context_get_gl_api (src->context) & SUPPORTED_GL_APIS) == 0) - goto unsupported_gl_api; - - gst_gl_context_thread_add (src->context, - (GstGLContextThreadFunc) _src_generate_fbo_gl, src); - if (!src->fbo) - goto context_error; - - gst_query_parse_allocation (query, &caps, NULL); - - if (gst_query_get_n_allocation_pools (query) > 0) { - gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); - - update_pool = TRUE; - } else { - GstVideoInfo vinfo; - - gst_video_info_init (&vinfo); - gst_video_info_from_caps (&vinfo, caps); - size = vinfo.size; - min = max = 0; - update_pool = FALSE; - } - - if (!pool || !GST_IS_GL_BUFFER_POOL (pool)) { - /* can't use this pool */ - if (pool) - gst_object_unref (pool); - pool = gst_gl_buffer_pool_new (src->context); - } - config = gst_buffer_pool_get_config (pool); - - gst_buffer_pool_config_set_params (config, caps, size, min, max); - gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); - if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL)) - gst_buffer_pool_config_add_option (config, - GST_BUFFER_POOL_OPTION_GL_SYNC_META); - gst_buffer_pool_config_add_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META); - - gst_buffer_pool_set_config (pool, config); - - if (update_pool) - gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); - else - gst_query_add_allocation_pool (query, pool, size, min, max); - - gst_gl_test_src_init_shader (src); - - gst_object_unref (pool); - - return TRUE; - -unsupported_gl_api: - { - GstGLAPI gl_api = gst_gl_context_get_gl_api (src->context); - gchar *gl_api_str = gst_gl_api_to_string (gl_api); - gchar *supported_gl_api_str = gst_gl_api_to_string (SUPPORTED_GL_APIS); - GST_ELEMENT_ERROR (src, RESOURCE, BUSY, - ("GL API's not compatible context: %s supported: %s", gl_api_str, - supported_gl_api_str), (NULL)); - - g_free (supported_gl_api_str); - g_free (gl_api_str); - return FALSE; - } -context_error: - { - if (error) { - GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("%s", error->message), - (NULL)); - g_clear_error (&error); - } else { - GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL)); - } - if (src->context) - gst_object_unref (src->context); - src->context = NULL; - return FALSE; - } -} - -static gboolean gst_gl_test_src_callback (gpointer stuff) { GstGLTestSrc *src = GST_GL_TEST_SRC (stuff); + GstGLBaseSrc *glbasesrc = GST_GL_BASE_SRC (src); const struct SrcFuncs *funcs; funcs = src->src_funcs; @@ -709,8 +260,7 @@ gst_gl_test_src_callback (gpointer stuff) return FALSE; } src->src_impl = funcs->new (src); - if (!(src->gl_result = - funcs->init (src->src_impl, src->context, &src->out_info))) { + if (!funcs->init (src->src_impl, glbasesrc->context, &glbasesrc->out_info)) { GST_ERROR_OBJECT (src, "Failed to initialize pattern"); return FALSE; } @@ -720,35 +270,34 @@ gst_gl_test_src_callback (gpointer stuff) return funcs->fill_bound_fbo (src->src_impl); } -static GstStateChangeReturn -gst_gl_test_src_change_state (GstElement * element, GstStateChange transition) +static gboolean +gst_gl_test_src_fill_memory (GstGLBaseSrc * src, GstGLMemory * memory) { - GstGLTestSrc *src = GST_GL_TEST_SRC (element); - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - - GST_DEBUG_OBJECT (src, "changing state: %s => %s", - gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), - gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_NULL: - if (src->other_context) { - gst_object_unref (src->other_context); - src->other_context = NULL; - } - - if (src->display) { - gst_object_unref (src->display); - src->display = NULL; - } - break; - default: - break; - } + GstGLTestSrc *test_src = GST_GL_TEST_SRC (src); + return gst_gl_framebuffer_draw_to_texture (test_src->fbo, memory, + gst_gl_test_src_callback, test_src); +} + +static gboolean +gst_gl_test_src_gl_start (GstGLBaseSrc * bsrc) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (bsrc); + src->fbo = gst_gl_framebuffer_new_with_default_depth (bsrc->context, + GST_VIDEO_INFO_WIDTH (&bsrc->out_info), + GST_VIDEO_INFO_HEIGHT (&bsrc->out_info)); + return TRUE; +} - return ret; +static void +gst_gl_test_src_gl_stop (GstGLBaseSrc * bsrc) +{ + GstGLTestSrc *src = GST_GL_TEST_SRC (bsrc); + + if (src->fbo) + gst_object_unref (src->fbo); + src->fbo = NULL; + + if (src->src_impl) + src->src_funcs->free (src->src_impl); + src->src_impl = NULL; } diff --git a/ext/gl/gstgltestsrc.h b/ext/gl/gstgltestsrc.h index df4abb7..bc5ada1 100644 --- a/ext/gl/gstgltestsrc.h +++ b/ext/gl/gstgltestsrc.h @@ -24,7 +24,6 @@ #define __GST_GL_TEST_SRC_H__ #include -#include #include @@ -51,7 +50,7 @@ typedef struct _GstGLTestSrcClass GstGLTestSrcClass; * Opaque data structure. */ struct _GstGLTestSrc { - GstPushSrc element; + GstGLBaseSrc element; /*< private >*/ @@ -59,32 +58,13 @@ struct _GstGLTestSrc { GstGLTestSrcPattern set_pattern; GstGLTestSrcPattern active_pattern; - /* video state */ - GstVideoInfo out_info; - GstGLFramebuffer *fbo; - GstGLMemory *out_tex; - - GstGLShader *shader; - - GstBufferPool *pool; - - GstGLDisplay *display; - GstGLContext *context, *other_context; - gint64 timestamp_offset; /* base offset */ - GstClockTime running_time; /* total running time */ - gint64 n_frames; /* total frames sent */ - gboolean negotiated; - - gboolean gl_result; const struct SrcFuncs *src_funcs; gpointer src_impl; - - GstCaps *out_caps; }; struct _GstGLTestSrcClass { - GstPushSrcClass parent_class; + GstGLBaseSrcClass parent_class; }; GType gst_gl_test_src_get_type (void); diff --git a/gst-libs/gst/gl/Makefile.am b/gst-libs/gst/gl/Makefile.am index fa8dbd0..ffe70e8 100644 --- a/gst-libs/gst/gl/Makefile.am +++ b/gst-libs/gst/gl/Makefile.am @@ -18,6 +18,7 @@ libgstgl_@GST_API_VERSION@_la_SOURCES = \ gstglfilter.c \ gstglformat.c \ gstglbasefilter.c \ + gstglbasesrc.c \ gstglshader.c \ gstglshaderstrings.c \ gstglsl.c \ @@ -50,6 +51,7 @@ gstgl_headers = \ gstglfilter.h \ gstglformat.h \ gstglbasefilter.h \ + gstglbasesrc.h \ gstglshader.h \ gstglshaderstrings.h \ gstglsl.h \ diff --git a/gst-libs/gst/gl/gl.h b/gst-libs/gst/gl/gl.h index cde8465..5246abb 100644 --- a/gst-libs/gst/gl/gl.h +++ b/gst-libs/gst/gl/gl.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include diff --git a/gst-libs/gst/gl/gstgl_fwd.h b/gst-libs/gst/gl/gstgl_fwd.h index 0728bd2..9e84236 100644 --- a/gst-libs/gst/gl/gstgl_fwd.h +++ b/gst-libs/gst/gl/gstgl_fwd.h @@ -90,6 +90,10 @@ typedef struct _GstGLBaseFilter GstGLBaseFilter; typedef struct _GstGLBaseFilterClass GstGLBaseFilterClass; typedef struct _GstGLBaseFilterPrivate GstGLBaseFilterPrivate; +typedef struct _GstGLBaseSrc GstGLBaseSrc; +typedef struct _GstGLBaseSrcClass GstGLBaseSrcClass; +typedef struct _GstGLBaseSrcPrivate GstGLBaseSrcPrivate; + typedef struct _GstGLFilter GstGLFilter; typedef struct _GstGLFilterClass GstGLFilterClass; @@ -112,6 +116,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstGLBaseFilter, gst_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstGLBaseMemoryAllocator, gst_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstGLBaseSrc, gst_object_unref) + G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstGLBufferAllocator, gst_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstGLBufferPool, gst_object_unref) diff --git a/gst-libs/gst/gl/gstglbasesrc.c b/gst-libs/gst/gl/gstglbasesrc.c new file mode 100644 index 0000000..643b94c --- /dev/null +++ b/gst-libs/gst/gl/gstglbasesrc.c @@ -0,0 +1,641 @@ +/* + * GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 2002,2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * Copyright (C) 2015 Matthew Waters + * Copyright (C) 2019 Philippe Normand + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +/** + * SECTION:gstglbasesrc + * @short_description: #GstPushSrc subclass for injecting OpenGL resources in a pipeline + * @title: GstGLBaseSrc + * @see_also: #GstPushSrc + * + * #GstGLBaseSrc handles the nitty gritty details of retrieving an OpenGL + * context. It also provided some wrappers around #GstBaseSrc's `start()` and + * `stop()` virtual methods that ensure an OpenGL context is available and + * current in the calling thread. + */ + +#define GST_CAT_DEFAULT gst_gl_base_src_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +struct _GstGLBaseSrcPrivate +{ + GstGLContext *other_context; + + GstGLMemory *out_tex; + gint64 timestamp_offset; /* base offset */ + gint64 n_frames; /* total frames sent */ + gboolean negotiated; + gboolean gl_result; + gboolean gl_started; +}; + +/* Properties */ +enum +{ + PROP_0, + PROP_TIMESTAMP_OFFSET +}; + +#define gst_gl_base_src_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGLBaseSrc, gst_gl_base_src, + GST_TYPE_PUSH_SRC, G_ADD_PRIVATE (GstGLBaseSrc) + GST_DEBUG_CATEGORY_INIT (gst_gl_base_src_debug, + "glbasesrc", 0, "glbasesrc element"); + ); + +static void gst_gl_base_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_gl_base_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_gl_base_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps); +static gboolean gst_gl_base_src_query (GstBaseSrc * bsrc, GstQuery * query); +static void gst_gl_base_src_set_context (GstElement * element, + GstContext * context); +static GstStateChangeReturn gst_gl_base_src_change_state (GstElement * element, + GstStateChange transition); + +static void gst_gl_base_src_get_times (GstBaseSrc * basesrc, + GstBuffer * buffer, GstClockTime * start, GstClockTime * end); +static GstFlowReturn gst_gl_base_src_fill (GstPushSrc * psrc, + GstBuffer * buffer); +static gboolean gst_gl_base_src_start (GstBaseSrc * basesrc); +static gboolean gst_gl_base_src_stop (GstBaseSrc * basesrc); +static gboolean gst_gl_base_src_decide_allocation (GstBaseSrc * basesrc, + GstQuery * query); +static gboolean gst_gl_base_src_do_seek (GstBaseSrc * basesrc, + GstSegment * segment); + +static gboolean gst_gl_base_src_default_gl_start (GstGLBaseSrc * src); +static void gst_gl_base_src_default_gl_stop (GstGLBaseSrc * src); +static gboolean gst_gl_base_src_default_fill_gl_memory (GstGLBaseSrc * src, + GstGLMemory * mem); + +static void +gst_gl_base_src_class_init (GstGLBaseSrcClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); + GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_gl_base_src_set_property; + gobject_class->get_property = gst_gl_base_src_get_property; + + g_object_class_install_property (gobject_class, + PROP_TIMESTAMP_OFFSET, g_param_spec_int64 ("timestamp-offset", + "Timestamp offset", + "An offset added to timestamps set on buffers (in ns)", G_MININT64, + G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + element_class->set_context = GST_DEBUG_FUNCPTR (gst_gl_base_src_set_context); + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_gl_base_src_change_state); + + gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_gl_base_src_setcaps); + gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_gl_base_src_query); + gstbasesrc_class->get_times = GST_DEBUG_FUNCPTR (gst_gl_base_src_get_times); + gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_gl_base_src_start); + gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_gl_base_src_stop); + gstbasesrc_class->decide_allocation = + GST_DEBUG_FUNCPTR (gst_gl_base_src_decide_allocation); + gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_gl_base_src_do_seek); + + gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_gl_base_src_fill); + + klass->supported_gl_api = GST_GL_API_ANY; + klass->gl_start = GST_DEBUG_FUNCPTR (gst_gl_base_src_default_gl_start); + klass->gl_stop = GST_DEBUG_FUNCPTR (gst_gl_base_src_default_gl_stop); + klass->fill_gl_memory = + GST_DEBUG_FUNCPTR (gst_gl_base_src_default_fill_gl_memory); +} + +static void +gst_gl_base_src_init (GstGLBaseSrc * src) +{ + src->priv = gst_gl_base_src_get_instance_private (src); + src->priv->timestamp_offset = 0; + + /* we operate in time */ + gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); + gst_base_src_set_live (GST_BASE_SRC (src), FALSE); +} + +static void +gst_gl_base_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (object); + + switch (prop_id) { + case PROP_TIMESTAMP_OFFSET: + src->priv->timestamp_offset = g_value_get_int64 (value); + break; + default: + break; + } +} + +static void +gst_gl_base_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (object); + + switch (prop_id) { + case PROP_TIMESTAMP_OFFSET: + g_value_set_int64 (value, src->priv->timestamp_offset); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_gl_base_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps) +{ + GstGLBaseSrc *glbasesrc = GST_GL_BASE_SRC (bsrc); + + GST_DEBUG ("setcaps"); + + if (!gst_video_info_from_caps (&glbasesrc->out_info, caps)) + goto wrong_caps; + + glbasesrc->priv->negotiated = TRUE; + + gst_caps_replace (&glbasesrc->out_caps, caps); + + return TRUE; + +/* ERRORS */ +wrong_caps: + { + GST_WARNING ("wrong caps"); + return FALSE; + } +} + +static void +gst_gl_base_src_set_context (GstElement * element, GstContext * context) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (element); + GstGLBaseSrcClass *klass = GST_GL_BASE_SRC_GET_CLASS (src); + + gst_gl_handle_set_context (element, context, &src->display, + &src->priv->other_context); + + if (src->display) + gst_gl_display_filter_gl_api (src->display, klass->supported_gl_api); + + GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +} + +static gboolean +gst_gl_base_src_query (GstBaseSrc * bsrc, GstQuery * query) +{ + gboolean res = FALSE; + GstGLBaseSrc *src; + + src = GST_GL_BASE_SRC (bsrc); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONTEXT: + { + if (gst_gl_handle_context_query ((GstElement *) src, query, + src->display, src->context, src->priv->other_context)) + return TRUE; + break; + } + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = + gst_video_info_convert (&src->out_info, src_fmt, src_val, dest_fmt, + &dest_val); + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + + return res; + } + default: + break; + } + + return GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query); +} + +static void +gst_gl_base_src_get_times (GstBaseSrc * basesrc, GstBuffer * buffer, + GstClockTime * start, GstClockTime * end) +{ + /* for live sources, sync on the timestamp of the buffer */ + if (gst_base_src_is_live (basesrc)) { + GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); + + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + /* get duration to calculate end time */ + GstClockTime duration = GST_BUFFER_DURATION (buffer); + + if (GST_CLOCK_TIME_IS_VALID (duration)) + *end = timestamp + duration; + *start = timestamp; + } + } else { + *start = -1; + *end = -1; + } +} + +static gboolean +gst_gl_base_src_default_gl_start (GstGLBaseSrc * src) +{ + return TRUE; +} + +static void +gst_gl_base_src_gl_start (GstGLContext * context, gpointer data) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (data); + GstGLBaseSrcClass *src_class = GST_GL_BASE_SRC_GET_CLASS (src); + + gst_gl_insert_debug_marker (src->context, + "starting element %s", GST_OBJECT_NAME (src)); + + src->priv->gl_started = src_class->gl_start (src); +} + +static void +gst_gl_base_src_default_gl_stop (GstGLBaseSrc * src) +{ +} + +static void +gst_gl_base_src_gl_stop (GstGLContext * context, gpointer data) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (data); + GstGLBaseSrcClass *src_class = GST_GL_BASE_SRC_GET_CLASS (src); + + gst_gl_insert_debug_marker (src->context, + "stopping element %s", GST_OBJECT_NAME (src)); + + if (src->priv->gl_started) + src_class->gl_stop (src); + + src->priv->gl_started = FALSE; +} + +static gboolean +gst_gl_base_src_default_fill_gl_memory (GstGLBaseSrc * src, GstGLMemory * mem) +{ + return TRUE; +} + +static void +_fill_gl (GstGLContext * context, GstGLBaseSrc * src) +{ + GstGLBaseSrcClass *klass = GST_GL_BASE_SRC_GET_CLASS (src); + + src->priv->gl_result = klass->fill_gl_memory (src, src->priv->out_tex); +} + +static GstFlowReturn +gst_gl_base_src_fill (GstPushSrc * psrc, GstBuffer * buffer) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (psrc); + GstClockTime next_time; + GstVideoFrame out_frame; + GstGLSyncMeta *sync_meta; + + if (G_UNLIKELY (!src->priv->negotiated || !src->context)) + goto not_negotiated; + + /* 0 framerate and we are at the second frame, eos */ + if (G_UNLIKELY (GST_VIDEO_INFO_FPS_N (&src->out_info) == 0 + && src->priv->n_frames == 1)) + goto eos; + + if (!gst_video_frame_map (&out_frame, &src->out_info, buffer, + GST_MAP_WRITE | GST_MAP_GL)) { + return GST_FLOW_NOT_NEGOTIATED; + } + + src->priv->out_tex = (GstGLMemory *) out_frame.map[0].memory; + + gst_gl_context_thread_add (src->context, (GstGLContextThreadFunc) _fill_gl, + src); + if (!src->priv->gl_result) { + gst_video_frame_unmap (&out_frame); + goto gl_error; + } + gst_video_frame_unmap (&out_frame); + if (!src->priv->gl_result) + goto gl_error; + + sync_meta = gst_buffer_get_gl_sync_meta (buffer); + if (sync_meta) + gst_gl_sync_meta_set_sync_point (sync_meta, src->context); + + GST_BUFFER_TIMESTAMP (buffer) = + src->priv->timestamp_offset + src->running_time; + GST_BUFFER_OFFSET (buffer) = src->priv->n_frames; + src->priv->n_frames++; + GST_BUFFER_OFFSET_END (buffer) = src->priv->n_frames; + if (src->out_info.fps_n) { + next_time = gst_util_uint64_scale_int (src->priv->n_frames * GST_SECOND, + src->out_info.fps_d, src->out_info.fps_n); + GST_BUFFER_DURATION (buffer) = next_time - src->running_time; + } else { + next_time = src->priv->timestamp_offset; + /* NONE means forever */ + GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; + } + + src->running_time = next_time; + + return GST_FLOW_OK; + +gl_error: + { + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (_("failed to draw pattern")), + (_("A GL error occurred"))); + return GST_FLOW_NOT_NEGOTIATED; + } +not_negotiated: + { + GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL), + (_("format wasn't negotiated before get function"))); + return GST_FLOW_NOT_NEGOTIATED; + } +eos: + { + GST_DEBUG_OBJECT (src, "eos: 0 framerate, frame %d", + (gint) src->priv->n_frames); + return GST_FLOW_EOS; + } +} + +static gboolean +gst_gl_base_src_start (GstBaseSrc * basesrc) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (basesrc); + + src->running_time = 0; + src->priv->n_frames = 0; + src->priv->negotiated = FALSE; + + return TRUE; +} + +static gboolean +gst_gl_base_src_stop (GstBaseSrc * basesrc) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (basesrc); + + gst_caps_replace (&src->out_caps, NULL); + + if (src->context) + gst_object_unref (src->context); + src->context = NULL; + + return TRUE; +} + +static gboolean +_find_local_gl_context (GstGLBaseSrc * src) +{ + if (gst_gl_query_local_gl_context (GST_ELEMENT (src), GST_PAD_SRC, + &src->context)) + return TRUE; + return FALSE; +} + +static gboolean +gst_gl_base_src_decide_allocation (GstBaseSrc * basesrc, GstQuery * query) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (basesrc); + GstGLBaseSrcClass *klass = GST_GL_BASE_SRC_GET_CLASS (src); + GstBufferPool *pool = NULL; + GstStructure *config; + GstCaps *caps; + guint min, max, size; + gboolean update_pool; + GError *error = NULL; + gboolean new_context = FALSE; + + if (!src->context) + new_context = TRUE; + + if (!gst_gl_ensure_element_data (src, &src->display, + &src->priv->other_context)) + return FALSE; + + gst_gl_display_filter_gl_api (src->display, klass->supported_gl_api); + + _find_local_gl_context (src); + + if (!src->context) { + GST_OBJECT_LOCK (src->display); + do { + if (src->context) { + gst_object_unref (src->context); + src->context = NULL; + } + /* just get a GL context. we don't care */ + src->context = + gst_gl_display_get_gl_context_for_thread (src->display, NULL); + if (!src->context) { + if (!gst_gl_display_create_context (src->display, + src->priv->other_context, &src->context, &error)) { + GST_OBJECT_UNLOCK (src->display); + goto context_error; + } + } + } while (!gst_gl_display_add_context (src->display, src->context)); + GST_OBJECT_UNLOCK (src->display); + } + + if (new_context || !src->priv->gl_started) { + if (src->priv->gl_started) + gst_gl_context_thread_add (src->context, gst_gl_base_src_gl_stop, src); + + { + if ((gst_gl_context_get_gl_api (src-> + context) & klass->supported_gl_api) == 0) + goto unsupported_gl_api; + } + + gst_gl_context_thread_add (src->context, gst_gl_base_src_gl_start, src); + + if (!src->priv->gl_started) + goto error; + } + + gst_query_parse_allocation (query, &caps, NULL); + + if (gst_query_get_n_allocation_pools (query) > 0) { + gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + + update_pool = TRUE; + } else { + GstVideoInfo vinfo; + + gst_video_info_init (&vinfo); + gst_video_info_from_caps (&vinfo, caps); + size = vinfo.size; + min = max = 0; + update_pool = FALSE; + } + + if (!pool || !GST_IS_GL_BUFFER_POOL (pool)) { + /* can't use this pool */ + if (pool) + gst_object_unref (pool); + pool = gst_gl_buffer_pool_new (src->context); + } + config = gst_buffer_pool_get_config (pool); + + gst_buffer_pool_config_set_params (config, caps, size, min, max); + gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); + if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL)) + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_GL_SYNC_META); + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META); + + gst_buffer_pool_set_config (pool, config); + + if (update_pool) + gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool (query, pool, size, min, max); + + gst_object_unref (pool); + + return TRUE; + +unsupported_gl_api: + { + GstGLAPI gl_api = gst_gl_context_get_gl_api (src->context); + gchar *gl_api_str = gst_gl_api_to_string (gl_api); + gchar *supported_gl_api_str = + gst_gl_api_to_string (klass->supported_gl_api); + GST_ELEMENT_ERROR (src, RESOURCE, BUSY, + ("GL API's not compatible context: %s supported: %s", gl_api_str, + supported_gl_api_str), (NULL)); + + g_free (supported_gl_api_str); + g_free (gl_api_str); + return FALSE; + } +context_error: + { + if (error) { + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("%s", error->message), + (NULL)); + g_clear_error (&error); + } else { + GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL)); + } + if (src->context) + gst_object_unref (src->context); + src->context = NULL; + return FALSE; + } +error: + { + GST_ELEMENT_ERROR (src, LIBRARY, INIT, + ("Subclass failed to initialize."), (NULL)); + return FALSE; + } +} + +static GstStateChangeReturn +gst_gl_base_src_change_state (GstElement * element, GstStateChange transition) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + GST_DEBUG_OBJECT (src, "changing state: %s => %s", + gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), + gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + if (src->priv->other_context) { + gst_object_unref (src->priv->other_context); + src->priv->other_context = NULL; + } + + if (src->display) { + gst_object_unref (src->display); + src->display = NULL; + } + break; + default: + break; + } + + return ret; +} + +static gboolean +gst_gl_base_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment) +{ + GstGLBaseSrc *src = GST_GL_BASE_SRC (basesrc); + GstClockTime time; + segment->time = segment->start; + time = segment->position; + + /* now move to the time indicated */ + if (src->out_info.fps_n) { + src->priv->n_frames = gst_util_uint64_scale (time, + src->out_info.fps_n, src->out_info.fps_d * GST_SECOND); + } else + src->priv->n_frames = 0; + + if (src->out_info.fps_n) { + src->running_time = gst_util_uint64_scale (src->priv->n_frames, + src->out_info.fps_d * GST_SECOND, src->out_info.fps_n); + } else { + /* FIXME : Not sure what to set here */ + src->running_time = 0; + } + + g_return_val_if_fail (src->running_time <= time, FALSE); + + return GST_BASE_SRC_CLASS (parent_class)->do_seek (basesrc, segment); +} diff --git a/gst-libs/gst/gl/gstglbasesrc.h b/gst-libs/gst/gl/gstglbasesrc.h new file mode 100644 index 0000000..a33b5bf --- /dev/null +++ b/gst-libs/gst/gl/gstglbasesrc.h @@ -0,0 +1,96 @@ +/* + * GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 2002,2007 David A. Schleef + * Copyright (C) 2008 Julien Isorce + * Copyright (C) 2019 Philippe Normand + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_GL_BASE_SRC_H__ +#define __GST_GL_BASE_SRC_H__ + +#include +#include + +G_BEGIN_DECLS + +GST_GL_API +GType gst_gl_base_src_get_type(void); + +#define GST_TYPE_GL_BASE_SRC (gst_gl_base_src_get_type()) +#define GST_GL_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_BASE_SRC,GstGLBaseSrc)) +#define GST_GL_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GL_BASE_SRC,GstGLBaseSrcClass)) +#define GST_IS_GL_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_BASE_SRC)) +#define GST_IS_GL_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GL_BASE_SRC)) +#define GST_GL_BASE_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_GL_BASE_SRC,GstGLBaseSrcClass)) + +/** + * GstGLBaseSrc: + * @display: the currently configured #GstGLDisplay + * @context: the currently configured #GstGLContext + * @out_caps: the currently configured output #GstCaps + * @out_info: the currently configured output #GstVideoInfo + * @running_time: the total running time + * + * The parent instance type of a base GStreamer GL Video source. + */ +struct _GstGLBaseSrc { + GstPushSrc parent; + + /*< public >*/ + GstGLDisplay *display; + GstGLContext *context; + + /* video state */ + GstVideoInfo out_info; + GstCaps *out_caps; + + /* total running time */ + GstClockTime running_time; + + /*< private >*/ + gpointer _padding[GST_PADDING]; + + GstGLBaseSrcPrivate *priv; +}; + +/** + * GstGLBaseSrcClass: + * @supported_gl_api: the logical-OR of #GstGLAPI's supported by this element + * @gl_start: called in the GL thread to setup the element GL state. + * @gl_stop: called in the GL thread to setup the element GL state. + * @fill_gl_memory: called in the GL thread to fill the current video texture. + * + * The base class for GStreamer GL Video sources. + */ +struct _GstGLBaseSrcClass { + GstPushSrcClass parent_class; + + /*< public >*/ + GstGLAPI supported_gl_api; + gboolean (*gl_start) (GstGLBaseSrc *src); + void (*gl_stop) (GstGLBaseSrc *src); + gboolean (*fill_gl_memory) (GstGLBaseSrc *src, GstGLMemory *mem); + + /*< private >*/ + gpointer _padding[GST_PADDING]; +}; + +G_END_DECLS + +#endif /* __GST_GL_BASE_SRC_H__ */ diff --git a/gst-libs/gst/gl/meson.build b/gst-libs/gst/gl/meson.build index 0e0ba1b..4aeb559 100644 --- a/gst-libs/gst/gl/meson.build +++ b/gst-libs/gst/gl/meson.build @@ -9,6 +9,7 @@ gl_sources = [ 'gstglapi.c', 'gstglbasefilter.c', 'gstglbasememory.c', + 'gstglbasesrc.c', 'gstglcolorconvert.c', 'gstglbuffer.c', 'gstglbufferpool.c', @@ -45,6 +46,7 @@ gir_gl_headers = [ 'gstglapi.h', 'gstglbasefilter.h', 'gstglbasememory.h', + 'gstglbasesrc.h', 'gstglbuffer.h', 'gstglbufferpool.h', 'gstglcolorconvert.h', -- 2.7.4