From b14207cc9ee338f6a64fd38a79f3ee39447eb486 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 4 Aug 2023 16:03:08 -0400 Subject: [PATCH] nle: Use a message based mechanism to detect parent nleobject duration Recursing up is pretty ugly and will fail when we start using ancillary pipelines for sources in nle, using a message/event based mechanism is a common pattern that is much cleaner. Part-of: --- .../plugins/ges/gesdemux.c | 124 +++++++++--------- .../plugins/nle/nleobject.c | 49 +++++++ .../plugins/shared/nlegesplugin.h | 39 ++++++ 3 files changed, 152 insertions(+), 60 deletions(-) create mode 100644 subprojects/gst-editing-services/plugins/shared/nlegesplugin.h diff --git a/subprojects/gst-editing-services/plugins/ges/gesdemux.c b/subprojects/gst-editing-services/plugins/ges/gesdemux.c index f865d12cad..c2857f5fcf 100644 --- a/subprojects/gst-editing-services/plugins/ges/gesdemux.c +++ b/subprojects/gst-editing-services/plugins/ges/gesdemux.c @@ -32,6 +32,7 @@ * uri scheme. **/ +#include "plugins/shared/nlegesplugin.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -71,6 +72,10 @@ struct _GESDemux G_DEFINE_TYPE (GESDemux, ges_demux, ges_base_bin_get_type ()); #define GES_DEMUX(obj) ((GESDemux*)obj) +static void +ges_demux_adapt_timeline_duration (GESDemux * self, GESTimeline * timeline, + GstElement * parent); + enum { PROP_0, @@ -344,75 +349,61 @@ ges_demux_set_srcpad_probe (GstElement * element, GstPad * pad, return TRUE; } -static void -ges_demux_adapt_timeline_duration (GESDemux * self, GESTimeline * timeline) +void +ges_demux_adapt_timeline_duration (GESDemux * self, GESTimeline * timeline, + GstElement * parent) { - GType nleobject_type = g_type_from_name ("NleObject"); - GstObject *parent, *tmpparent; + GstClockTime duration, inpoint, timeline_duration; - parent = gst_object_get_parent (GST_OBJECT (self)); - while (parent) { - if (g_type_is_a (G_OBJECT_TYPE (parent), nleobject_type)) { - GstClockTime duration, inpoint, timeline_duration; + g_object_get (parent, "duration", &duration, "inpoint", &inpoint, NULL); + g_object_get (timeline, "duration", &timeline_duration, NULL); - g_object_get (parent, "duration", &duration, "inpoint", &inpoint, NULL); - g_object_get (timeline, "duration", &timeline_duration, NULL); + GST_DEBUG_OBJECT (self, "Adapting duration"); + if (inpoint + duration > timeline_duration) { + GESLayer *layer = ges_timeline_get_layer (timeline, 0); - if (inpoint + duration > timeline_duration) { - GESLayer *layer = ges_timeline_get_layer (timeline, 0); + if (layer) { + GESClip *clip = GES_CLIP (ges_test_clip_new ()); + GList *tmp, *tracks = ges_timeline_get_tracks (timeline); - if (layer) { - GESClip *clip = GES_CLIP (ges_test_clip_new ()); - GList *tmp, *tracks = ges_timeline_get_tracks (timeline); - - g_object_set (clip, "start", timeline_duration, "duration", - inpoint + duration, "vpattern", GES_VIDEO_TEST_PATTERN_SMPTE75, - NULL); - ges_layer_add_clip (layer, clip); - for (tmp = tracks; tmp; tmp = tmp->next) { - if (GES_IS_VIDEO_TRACK (tmp->data)) { - GESEffect *text; - GstCaps *caps; - gchar *effect_str_full = NULL; - const gchar *effect_str = - "textoverlay text=\"Nested timeline too short, please FIX!\" halignment=center valignment=center"; - - g_object_get (tmp->data, "restriction-caps", &caps, NULL); - if (caps) { - gchar *caps_str = gst_caps_to_string (caps); - effect_str = effect_str_full = - g_strdup_printf - ("videoconvertscale ! capsfilter caps=\"%s\" ! %s", - caps_str, effect_str); - g_free (caps_str); - gst_caps_unref (caps); - } - text = ges_effect_new (effect_str); - g_free (effect_str_full); - - if (!ges_container_add (GES_CONTAINER (clip), - GES_TIMELINE_ELEMENT (text))) { - GST_ERROR ("Could not add text overlay to ending clip!"); - } - } + g_object_set (clip, "start", timeline_duration, "duration", + inpoint + duration, "vpattern", GES_VIDEO_TEST_PATTERN_SMPTE75, NULL); + ges_layer_add_clip (layer, clip); + for (tmp = tracks; tmp; tmp = tmp->next) { + if (GES_IS_VIDEO_TRACK (tmp->data)) { + GESEffect *text; + GstCaps *caps; + gchar *effect_str_full = NULL; + const gchar *effect_str = + "textoverlay text=\"Nested timeline too short, please FIX!\" halignment=center valignment=center"; + g_object_get (tmp->data, "restriction-caps", &caps, NULL); + if (caps) { + gchar *caps_str = gst_caps_to_string (caps); + effect_str = effect_str_full = + g_strdup_printf + ("videoconvertscale ! capsfilter caps=\"%s\" ! %s", caps_str, + effect_str); + g_free (caps_str); + gst_caps_unref (caps); + } + text = ges_effect_new (effect_str); + g_free (effect_str_full); + + if (!ges_container_add (GES_CONTAINER (clip), + GES_TIMELINE_ELEMENT (text))) { + GST_ERROR ("Could not add text overlay to ending clip!"); } - g_list_free_full (tracks, gst_object_unref); - GST_INFO_OBJECT (timeline, - "Added test clip with duration: %" GST_TIME_FORMAT " - %" - GST_TIME_FORMAT " to match parent nleobject duration", - GST_TIME_ARGS (timeline_duration), - GST_TIME_ARGS (inpoint + duration - timeline_duration)); } + } - gst_object_unref (parent); - - return; + g_list_free_full (tracks, gst_object_unref); + GST_INFO_OBJECT (timeline, + "Added test clip with duration: %" GST_TIME_FORMAT " - %" + GST_TIME_FORMAT " to match parent nleobject duration", + GST_TIME_ARGS (timeline_duration), + GST_TIME_ARGS (inpoint + duration - timeline_duration)); } - - tmpparent = parent; - parent = gst_object_get_parent (GST_OBJECT (parent)); - gst_object_unref (tmpparent); } } @@ -450,7 +441,20 @@ ges_demux_create_timeline (GESDemux * self, gchar * uri, GError ** error) if (data.error) goto done; - ges_demux_adapt_timeline_duration (self, data.timeline); + NleQueryParentNleObject *query_parent = + g_atomic_rc_box_new0 (NleQueryParentNleObject); + GType query_gtype = g_type_from_name ("NleQueryParentNleObject"); + gst_element_post_message (GST_ELEMENT (self), + gst_message_new_element (GST_OBJECT (self), + gst_structure_new (NLE_QUERY_PARENT_NLE_OBJECT, "query", + query_gtype, query_parent, NULL))); + g_mutex_lock (&query_parent->lock); + if (query_parent->nle_object) { + ges_demux_adapt_timeline_duration (self, data.timeline, + query_parent->nle_object); + } + g_mutex_unlock (&query_parent->lock); + g_boxed_free (query_gtype, query_parent); query = gst_query_new_uri (); if (gst_pad_peer_query (self->sinkpad, query)) { diff --git a/subprojects/gst-editing-services/plugins/nle/nleobject.c b/subprojects/gst-editing-services/plugins/nle/nleobject.c index e746dd1a9b..fcedd22fa8 100644 --- a/subprojects/gst-editing-services/plugins/nle/nleobject.c +++ b/subprojects/gst-editing-services/plugins/nle/nleobject.c @@ -24,6 +24,7 @@ #include #include "nle.h" +#include "../shared/nlegesplugin.h" /** * SECTION:nleobject @@ -33,6 +34,22 @@ * properties provided by all the GNonLin elements. */ +static void +nle_query_parent_nle_object_free (NleQueryParentNleObject * query) +{ + gst_clear_object (&query->nle_object); +} + +void +nle_query_parent_nle_object_release (NleQueryParentNleObject * query) +{ + g_atomic_rc_box_release_full (query, + (GDestroyNotify) nle_query_parent_nle_object_free); +} + +G_DEFINE_BOXED_TYPE (NleQueryParentNleObject, + nle_query_parent_nle_object, + g_atomic_rc_box_acquire, nle_query_parent_nle_object_release); GST_DEBUG_CATEGORY_STATIC (nleobject_debug); #define GST_CAT_DEFAULT nleobject_debug @@ -103,26 +120,58 @@ static gboolean nle_object_commit_func (NleObject * object, gboolean recurse); static GstStateChangeReturn nle_object_prepare (NleObject * object); +static void +nle_bin_handle_message (GstBin * bin, GstMessage * message) +{ + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) { + const GstStructure *s = gst_message_get_structure (message); + + if (gst_structure_has_name (s, NLE_QUERY_PARENT_NLE_OBJECT)) { + NleQueryParentNleObject *query; + + gst_structure_get (s, "query", NLE_TYPE_QUERY_PARENT_NLE_OBJECT, &query, + NULL); + g_assert (query); + + g_mutex_lock (&query->lock); + query->nle_object = gst_object_ref (GST_ELEMENT (bin)); + g_mutex_unlock (&query->lock); + nle_query_parent_nle_object_release (query); + + return; + } + } + + return GST_BIN_CLASS (parent_class)->handle_message (bin, message); +} + static void nle_object_class_init (NleObjectClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; + GstBinClass *gstbin_class; NleObjectClass *nleobject_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gstbin_class = (GstBinClass *) klass; nleobject_class = (NleObjectClass *) klass; GST_DEBUG_CATEGORY_INIT (nleobject_debug, "nleobject", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object"); parent_class = g_type_class_ref (GST_TYPE_BIN); + /* Ensure the NleQueryParentObject GType is registered */ + GType t = NLE_TYPE_QUERY_PARENT_NLE_OBJECT; + g_assert (t); + gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_object_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_object_get_property); gobject_class->constructed = GST_DEBUG_FUNCPTR (nle_object_constructed); gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_object_dispose); gstelement_class->change_state = GST_DEBUG_FUNCPTR (nle_object_change_state); + gstbin_class->handle_message = GST_DEBUG_FUNCPTR (nle_bin_handle_message); nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_object_prepare_func); nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_object_cleanup_func); diff --git a/subprojects/gst-editing-services/plugins/shared/nlegesplugin.h b/subprojects/gst-editing-services/plugins/shared/nlegesplugin.h new file mode 100644 index 0000000000..6013597666 --- /dev/null +++ b/subprojects/gst-editing-services/plugins/shared/nlegesplugin.h @@ -0,0 +1,39 @@ +/* GES and NLE plugins shared header + * + * Copyright (C) 2024 Thibault Saunier + * + * nlegesplugin.h + * + * 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. + * + */ +#pragma once + +#include + +#define NLE_QUERY_PARENT_NLE_OBJECT "nle-query-parent-nle-object" +typedef struct +{ + GMutex lock; + GstElement *nle_object; +} NleQueryParentNleObject; + +/* *INDENT-OFF* */ +#define NLE_TYPE_QUERY_PARENT_NLE_OBJECT nle_query_parent_nle_object_get_type () +GType nle_query_parent_nle_object_get_type (void) G_GNUC_CONST; +/* *INDENT-ON* */ + +void nle_query_parent_nle_object_release (NleQueryParentNleObject * query);