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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5983>
This commit is contained in:
Thibault Saunier 2023-08-04 16:03:08 -04:00 committed by GStreamer Marge Bot
parent b730e7a1b2
commit b14207cc9e
3 changed files with 152 additions and 60 deletions

View file

@ -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)) {

View file

@ -24,6 +24,7 @@
#include <string.h>
#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);

View file

@ -0,0 +1,39 @@
/* GES and NLE plugins shared header
*
* Copyright (C) 2024 Thibault Saunier <tsaunier@igalia.com>
*
* 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 <gst/gst.h>
#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);