static GstStateChangeReturn gst_gl_base_mixer_change_state (GstElement *
element, GstStateChange transition);
+static void gst_gl_base_mixer_gl_start (GstGLContext * context, gpointer data);
+static void gst_gl_base_mixer_gl_stop (GstGLContext * context, gpointer data);
+
struct _GstGLBaseMixerPrivate
{
gboolean negotiated;
GstGLContext *other_context;
+
+ gboolean gl_started;
+ gboolean gl_result;
+
+ GRecMutex context_lock;
};
#define gst_gl_base_mixer_parent_class parent_class
static gboolean
_find_local_gl_context (GstGLBaseMixer * mix)
{
- if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SRC,
- &mix->context))
- return TRUE;
- if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SINK,
- &mix->context))
- return TRUE;
+ GstGLContext *context = mix->context;
+
+ if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SRC, &context)) {
+ if (context->display == mix->display) {
+ mix->context = context;
+ return TRUE;
+ }
+ if (context != mix->context)
+ gst_clear_object (&context);
+ }
+ if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SINK, &context)) {
+ if (context->display == mix->display) {
+ mix->context = context;
+ return TRUE;
+ }
+ if (context != mix->context)
+ gst_clear_object (&context);
+ }
return FALSE;
}
static gboolean
-_get_gl_context (GstGLBaseMixer * mix)
+_get_gl_context_unlocked (GstGLBaseMixer * mix)
{
GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
+ gboolean new_context = FALSE;
GError *error = NULL;
+ if (!mix->context)
+ new_context = TRUE;
+
if (!gst_gl_ensure_element_data (mix, &mix->display,
&mix->priv->other_context))
return FALSE;
}
GST_OBJECT_UNLOCK (mix->display);
- {
- GstGLAPI current_gl_api = gst_gl_context_get_gl_api (mix->context);
- if ((current_gl_api & mix_class->supported_gl_api) == 0)
- goto unsupported_gl_api;
+ if (new_context || !mix->priv->gl_started) {
+ if (mix->priv->gl_started)
+ gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_stop, mix);
+
+ {
+ if ((gst_gl_context_get_gl_api (mix->
+ context) & mix_class->supported_gl_api) == 0)
+ goto unsupported_gl_api;
+ }
+
+ gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_start, mix);
+
+ if (!mix->priv->gl_started)
+ goto error;
}
return TRUE;
g_clear_error (&error);
return FALSE;
}
+error:
+ {
+ GST_ELEMENT_ERROR (mix, LIBRARY, INIT,
+ ("Subclass failed to initialize."), (NULL));
+ return FALSE;
+ }
+}
+
+static gboolean
+_get_gl_context (GstGLBaseMixer * mix)
+{
+ gboolean ret;
+ g_rec_mutex_lock (&mix->priv->context_lock);
+ ret = _get_gl_context_unlocked (mix);
+ g_rec_mutex_unlock (&mix->priv->context_lock);
+ return ret;
}
static gboolean
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
{
- if (gst_gl_handle_context_query ((GstElement *) mix, query,
- mix->display, mix->context, mix->priv->other_context))
- return TRUE;
+ gboolean ret;
+ g_rec_mutex_lock (&mix->priv->context_lock);
+ ret = gst_gl_handle_context_query ((GstElement *) mix, query,
+ mix->display, mix->context, mix->priv->other_context);
+ g_rec_mutex_unlock (&mix->priv->context_lock);
+ if (ret)
+ return ret;
break;
}
default:
static gboolean gst_gl_base_mixer_stop (GstAggregator * agg);
static gboolean gst_gl_base_mixer_start (GstAggregator * agg);
+static void gst_gl_base_mixer_finalize (GObject * object);
static void gst_gl_base_mixer_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_gl_base_mixer_get_property (GObject * object, guint prop_id,
static gboolean gst_gl_base_mixer_decide_allocation (GstAggregator * agg,
GstQuery * query);
+static gboolean gst_gl_base_mixer_default_gl_start (GstGLBaseMixer * src);
+static void gst_gl_base_mixer_default_gl_stop (GstGLBaseMixer * src);
+
static void
gst_gl_base_mixer_class_init (GstGLBaseMixerClass * klass)
{
gobject_class = (GObjectClass *) klass;
element_class = GST_ELEMENT_CLASS (klass);
+ gobject_class->finalize = gst_gl_base_mixer_finalize;
gobject_class->get_property = gst_gl_base_mixer_get_property;
gobject_class->set_property = gst_gl_base_mixer_set_property;
agg_class->decide_allocation = gst_gl_base_mixer_decide_allocation;
agg_class->propose_allocation = gst_gl_base_mixer_propose_allocation;
+ klass->gl_start = gst_gl_base_mixer_default_gl_start;
+ klass->gl_stop = gst_gl_base_mixer_default_gl_stop;
+
g_object_class_install_property (gobject_class, PROP_CONTEXT,
g_param_spec_object ("context",
"OpenGL context",
gst_gl_base_mixer_init (GstGLBaseMixer * mix)
{
mix->priv = gst_gl_base_mixer_get_instance_private (mix);
+
+ g_rec_mutex_init (&mix->priv->context_lock);
+}
+
+static void
+gst_gl_base_mixer_finalize (GObject * object)
+{
+ GstGLBaseMixer *mix = GST_GL_BASE_MIXER (object);
+
+ g_rec_mutex_clear (&mix->priv->context_lock);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_gl_base_mixer_default_gl_start (GstGLBaseMixer * src)
+{
+ return TRUE;
+}
+
+static void
+gst_gl_base_mixer_gl_start (GstGLContext * context, gpointer data)
+{
+ GstGLBaseMixer *src = GST_GL_BASE_MIXER (data);
+ GstGLBaseMixerClass *src_class = GST_GL_BASE_MIXER_GET_CLASS (src);
+
+ GST_INFO_OBJECT (src, "starting");
+ 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_mixer_default_gl_stop (GstGLBaseMixer * src)
+{
+}
+
+static void
+gst_gl_base_mixer_gl_stop (GstGLContext * context, gpointer data)
+{
+ GstGLBaseMixer *src = GST_GL_BASE_MIXER (data);
+ GstGLBaseMixerClass *src_class = GST_GL_BASE_MIXER_GET_CLASS (src);
+
+ GST_INFO_OBJECT (src, "stopping");
+ 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 void
{
GstGLBaseMixer *mix = GST_GL_BASE_MIXER (element);
GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
+ GstGLDisplay *old_display, *new_display;
+ g_rec_mutex_lock (&mix->priv->context_lock);
+ old_display = mix->display ? gst_object_ref (mix->display) : NULL;
gst_gl_handle_set_context (element, context, &mix->display,
&mix->priv->other_context);
-
if (mix->display)
gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
+ new_display = mix->display ? gst_object_ref (mix->display) : NULL;
+
+ if (old_display && new_display) {
+ if (old_display != new_display) {
+ gst_clear_object (&mix->context);
+ _get_gl_context_unlocked (mix);
+ gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (mix));
+ }
+ }
+ gst_clear_object (&old_display);
+ gst_clear_object (&new_display);
+ g_rec_mutex_unlock (&mix->priv->context_lock);
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}
gboolean result = TRUE;
if (active) {
+ g_rec_mutex_lock (&mix->priv->context_lock);
if (!gst_gl_ensure_element_data (mix, &mix->display,
- &mix->priv->other_context))
+ &mix->priv->other_context)) {
+ g_rec_mutex_lock (&mix->priv->context_lock);
return FALSE;
+ }
gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
+ g_rec_mutex_unlock (&mix->priv->context_lock);
}
return result;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
{
- if (gst_gl_handle_context_query ((GstElement *) mix, query,
- mix->display, mix->context, mix->priv->other_context))
- return TRUE;
+ gboolean ret;
+ g_rec_mutex_lock (&mix->priv->context_lock);
+ ret = gst_gl_handle_context_query ((GstElement *) mix, query,
+ mix->display, mix->context, mix->priv->other_context);
+ g_rec_mutex_unlock (&mix->priv->context_lock);
+ if (ret)
+ return ret;
break;
}
default:
{
GstGLBaseMixer *mix = GST_GL_BASE_MIXER (agg);
- if (mix->context) {
- gst_object_unref (mix->context);
- mix->context = NULL;
- }
+ g_rec_mutex_lock (&mix->priv->context_lock);
+ if (mix->priv->gl_started)
+ gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_stop, mix);
+ gst_clear_object (&mix->context);
+ g_rec_mutex_unlock (&mix->priv->context_lock);
return TRUE;
}
mix->priv->other_context = NULL;
}
- if (mix->display) {
- gst_object_unref (mix->display);
- mix->display = NULL;
- }
+ g_rec_mutex_lock (&mix->priv->context_lock);
+ gst_clear_object (&mix->display);
+ g_rec_mutex_unlock (&mix->priv->context_lock);
break;
default:
break;
return ret;
}
+
+/**
+ * gst_gl_base_mixer_get_gl_context:
+ * @mix: a #GstGLBaseMixer
+ *
+ * Returns: (transfer full) (nullable): the #GstGLContext found by @mix
+ *
+ * Since: 1.18
+ */
+GstGLContext *
+gst_gl_base_mixer_get_gl_context (GstGLBaseMixer * mix)
+{
+ GstGLContext *ret;
+
+ g_return_val_if_fail (GST_IS_GL_BASE_MIXER (mix), NULL);
+
+ g_rec_mutex_lock (&mix->priv->context_lock);
+ ret = mix->context ? gst_object_ref (mix->context) : NULL;
+ g_rec_mutex_unlock (&mix->priv->context_lock);
+ return ret;
+}
GstVideoAggregatorClass parent_class;
GstGLAPI supported_gl_api;
+ gboolean (*gl_start) (GstGLBaseMixer * mix);
+ void (*gl_stop) (GstGLBaseMixer * mix);
+
gpointer _padding[GST_PADDING];
};
GType gst_gl_base_mixer_get_type(void);
+GstGLContext * gst_gl_base_mixer_get_gl_context (GstGLBaseMixer * mix);
+
G_END_DECLS
#endif /* __GST_GL_BASE_MIXER_H__ */
static gboolean gst_gl_mixer_decide_allocation (GstAggregator * agg,
GstQuery * query);
+static gboolean gst_gl_mixer_gl_start (GstGLBaseMixer * mix);
+static void gst_gl_mixer_gl_stop (GstGLBaseMixer * mix);
+
static void gst_gl_mixer_finalize (GObject * object);
static void
GstVideoAggregatorClass *videoaggregator_class =
(GstVideoAggregatorClass *) klass;
GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
+ GstGLBaseMixerClass *base_class = (GstGLBaseMixerClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "OpenGL mixer");
videoaggregator_class->aggregate_frames = gst_gl_mixer_aggregate_frames;
videoaggregator_class->find_best_format = _find_best_format;
+ base_class->gl_start = gst_gl_mixer_gl_start;
+ base_class->gl_stop = gst_gl_mixer_gl_stop;
/* Register the pad class */
g_type_class_ref (GST_TYPE_GL_MIXER_PAD);
}
static gboolean
-gst_gl_mixer_decide_allocation (GstAggregator * agg, GstQuery * query)
+gst_gl_mixer_gl_start (GstGLBaseMixer * base_mix)
{
- GstGLBaseMixer *base_mix = GST_GL_BASE_MIXER (agg);
GstGLMixer *mix = GST_GL_MIXER (base_mix);
GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
- GstGLContext *context;
- GstBufferPool *pool = NULL;
- GstStructure *config;
- GstCaps *caps;
- guint min, max, size;
- gboolean update_pool;
-
- if (!GST_AGGREGATOR_CLASS (gst_gl_mixer_parent_class)->decide_allocation (agg,
- query))
- return FALSE;
-
- context = base_mix->context;
g_mutex_lock (&mix->priv->gl_resource_lock);
mix->priv->gl_resource_ready = FALSE;
if (mix->fbo)
gst_object_unref (mix->fbo);
- gst_gl_context_thread_add (context,
+ gst_gl_context_thread_add (base_mix->context,
(GstGLContextThreadFunc) _mixer_create_fbo, mix);
if (!mix->fbo) {
g_cond_signal (&mix->priv->gl_resource_cond);
goto context_error;
}
- gst_query_parse_allocation (query, &caps, NULL);
-
if (mixer_class->set_caps)
- mixer_class->set_caps (mix, caps);
+ mixer_class->set_caps (mix, mix->out_caps);
mix->priv->gl_resource_ready = TRUE;
g_cond_signal (&mix->priv->gl_resource_cond);
g_mutex_unlock (&mix->priv->gl_resource_lock);
+ return GST_GL_BASE_MIXER_CLASS (parent_class)->gl_start (base_mix);
+
+context_error:
+ {
+ GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("Context error"), (NULL));
+ return FALSE;
+ }
+}
+
+static void
+gst_gl_mixer_gl_stop (GstGLBaseMixer * base_mix)
+{
+ GstGLMixer *mix = GST_GL_MIXER (base_mix);
+ GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
+
+ if (mixer_class->reset)
+ mixer_class->reset (mix);
+
+ if (mix->fbo) {
+ gst_object_unref (mix->fbo);
+ mix->fbo = NULL;
+ }
+
+ GST_GL_BASE_MIXER_CLASS (parent_class)->gl_stop (base_mix);
+}
+
+static gboolean
+gst_gl_mixer_decide_allocation (GstAggregator * agg, GstQuery * query)
+{
+ GstGLBaseMixer *base_mix = GST_GL_BASE_MIXER (agg);
+ GstGLContext *context;
+ GstBufferPool *pool = NULL;
+ GstStructure *config;
+ GstCaps *caps;
+ guint min, max, size;
+ gboolean update_pool;
+
+ if (!GST_AGGREGATOR_CLASS (gst_gl_mixer_parent_class)->decide_allocation (agg,
+ query))
+ return FALSE;
+
+ context = gst_gl_base_mixer_get_gl_context (base_mix);
+ if (!context) {
+ GST_WARNING_OBJECT (agg, "No OpenGL context");
+ return FALSE;
+ }
+
+ 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);
gst_object_unref (pool);
- return TRUE;
+ gst_clear_object (&context);
-context_error:
- {
- GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("Context error"), (NULL));
- return FALSE;
- }
+ return TRUE;
}
gboolean
GstVideoFrame out_frame;
GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
- GstGLMixerPrivate *priv = mix->priv;
GST_TRACE ("Processing buffers");
out_tex = (GstGLMemory *) out_frame.map[0].memory;
- g_mutex_lock (&priv->gl_resource_lock);
- if (!priv->gl_resource_ready)
- g_cond_wait (&priv->gl_resource_cond, &priv->gl_resource_lock);
+ g_mutex_lock (&mix->priv->gl_resource_lock);
+ if (!mix->priv->gl_resource_ready)
+ g_cond_wait (&mix->priv->gl_resource_cond, &mix->priv->gl_resource_lock);
- if (!priv->gl_resource_ready) {
- g_mutex_unlock (&priv->gl_resource_lock);
+ if (!mix->priv->gl_resource_ready) {
+ g_mutex_unlock (&mix->priv->gl_resource_lock);
GST_ERROR_OBJECT (mix,
"fbo used to render can't be created, do not run process_textures");
res = FALSE;
mix_class->process_textures (mix, out_tex);
- g_mutex_unlock (&priv->gl_resource_lock);
+ g_mutex_unlock (&mix->priv->gl_resource_lock);
out:
gst_video_frame_unmap (&out_frame);
static GstFlowReturn
gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
{
+ GstGLBaseMixer *base_mix = GST_GL_BASE_MIXER (vagg);
gboolean res = FALSE;
GstGLMixer *mix = GST_GL_MIXER (vagg);
GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (vagg);
- GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
+ GstGLContext *context = gst_gl_base_mixer_get_gl_context (base_mix);
GstGLSyncMeta *sync_meta;
+ if (!context) {
+ GST_DEBUG_OBJECT (vagg, "No OpenGL context, try again later");
+ return GST_AGGREGATOR_FLOW_NEED_DATA;
+ }
+
if (mix_class->process_buffers)
res = gst_gl_mixer_process_buffers (mix, outbuf);
else if (mix_class->process_textures)
if (sync_meta)
gst_gl_sync_meta_set_sync_point (sync_meta, context);
+ gst_clear_object (&context);
+
return res ? GST_FLOW_OK : GST_FLOW_ERROR;
}
gst_gl_mixer_stop (GstAggregator * agg)
{
GstGLMixer *mix = GST_GL_MIXER (agg);
- GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
-
- if (mixer_class->reset)
- mixer_class->reset (mix);
-
- if (mix->fbo) {
- gst_object_unref (mix->fbo);
- mix->fbo = NULL;
- }
gst_gl_mixer_reset (mix);
static GstCaps *_fixate_caps (GstAggregator * agg, GstCaps * caps);
static gboolean gst_gl_video_mixer_propose_allocation (GstAggregator *
agg, GstAggregatorPad * agg_pad, GstQuery * decide_query, GstQuery * query);
-static void gst_gl_video_mixer_reset (GstGLMixer * mixer);
+static gboolean gst_gl_video_mixer_gl_start (GstGLBaseMixer * base_mix);
+static void gst_gl_video_mixer_gl_stop (GstGLBaseMixer * base_mix);
static gboolean gst_gl_video_mixer_set_caps (GstGLMixer * mixer,
GstCaps * outcaps);
DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
GST_GL_MIXER_CLASS (klass)->set_caps = gst_gl_video_mixer_set_caps;
- GST_GL_MIXER_CLASS (klass)->reset = gst_gl_video_mixer_reset;
GST_GL_MIXER_CLASS (klass)->process_textures =
gst_gl_video_mixer_process_textures;
+ GST_GL_BASE_MIXER_CLASS (klass)->gl_stop = gst_gl_video_mixer_gl_stop;
+ GST_GL_BASE_MIXER_CLASS (klass)->gl_start = gst_gl_video_mixer_gl_start;
vagg_class->update_caps = _update_caps;
GST_OBJECT_UNLOCK (vagg);
return NULL;
}
-
}
GST_OBJECT_UNLOCK (vagg);
gst_element_foreach_sink_pad (GST_ELEMENT (video_mixer), _reset_pad_gl, NULL);
}
-static void
-gst_gl_video_mixer_reset (GstGLMixer * mixer)
-{
- GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mixer);
- GstGLContext *context = GST_GL_BASE_MIXER (mixer)->context;
-
- GST_DEBUG_OBJECT (mixer, "context:%p", context);
-
- if (video_mixer->shader)
- gst_object_unref (video_mixer->shader);
- video_mixer->shader = NULL;
-
- if (video_mixer->checker)
- gst_object_unref (video_mixer->checker);
- video_mixer->checker = NULL;
-
- if (GST_GL_BASE_MIXER (mixer)->context)
- gst_gl_context_thread_add (context, (GstGLContextThreadFunc) _reset_gl,
- mixer);
-}
-
static gboolean
gst_gl_video_mixer_set_caps (GstGLMixer * mixer, GstCaps * outcaps)
{
GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mixer);
- g_clear_object (&video_mixer->shader);
-
/* need reconfigure output geometry */
video_mixer->output_geo_change = TRUE;
}
static void
-_video_mixer_process_gl (GstGLContext * context, GstGLVideoMixer * video_mixer)
+gst_gl_video_mixer_gl_stop (GstGLBaseMixer * base_mix)
{
- GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
+ GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (base_mix);
+
+ gst_clear_object (&video_mixer->shader);
+ gst_clear_object (&video_mixer->checker);
+
+ _reset_gl (base_mix->context, video_mixer);
+
+ GST_GL_BASE_MIXER_CLASS (parent_class)->gl_stop (base_mix);
+}
+
+static gboolean
+gst_gl_video_mixer_gl_start (GstGLBaseMixer * base_mix)
+{
+ GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (base_mix);
if (!video_mixer->shader) {
gchar *frag_str = g_strdup_printf ("%s%s",
- gst_gl_shader_string_get_highest_precision (GST_GL_BASE_MIXER
- (mixer)->context, GST_GLSL_VERSION_NONE,
+ gst_gl_shader_string_get_highest_precision (base_mix->context,
+ GST_GLSL_VERSION_NONE,
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY),
video_mixer_f_src);
- gst_gl_context_gen_shader (GST_GL_BASE_MIXER (mixer)->context,
+ gst_gl_context_gen_shader (base_mix->context,
gst_gl_shader_string_vertex_mat4_vertex_transform,
frag_str, &video_mixer->shader);
g_free (frag_str);
}
+ return GST_GL_BASE_MIXER_CLASS (parent_class)->gl_start (base_mix);
+}
+
+static void
+_video_mixer_process_gl (GstGLContext * context, GstGLVideoMixer * video_mixer)
+{
+ GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
+
gst_gl_framebuffer_draw_to_texture (mixer->fbo, video_mixer->out_tex,
gst_gl_video_mixer_callback, video_mixer);
}