mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-18 04:05:34 +00:00
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:
parent
b730e7a1b2
commit
b14207cc9e
3 changed files with 152 additions and 60 deletions
|
@ -32,6 +32,7 @@
|
||||||
* uri scheme.
|
* uri scheme.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
#include "plugins/shared/nlegesplugin.h"
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -71,6 +72,10 @@ struct _GESDemux
|
||||||
G_DEFINE_TYPE (GESDemux, ges_demux, ges_base_bin_get_type ());
|
G_DEFINE_TYPE (GESDemux, ges_demux, ges_base_bin_get_type ());
|
||||||
#define GES_DEMUX(obj) ((GESDemux*)obj)
|
#define GES_DEMUX(obj) ((GESDemux*)obj)
|
||||||
|
|
||||||
|
static void
|
||||||
|
ges_demux_adapt_timeline_duration (GESDemux * self, GESTimeline * timeline,
|
||||||
|
GstElement * parent);
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
|
@ -344,75 +349,61 @@ ges_demux_set_srcpad_probe (GstElement * element, GstPad * pad,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
ges_demux_adapt_timeline_duration (GESDemux * self, GESTimeline * timeline)
|
ges_demux_adapt_timeline_duration (GESDemux * self, GESTimeline * timeline,
|
||||||
|
GstElement * parent)
|
||||||
{
|
{
|
||||||
GType nleobject_type = g_type_from_name ("NleObject");
|
GstClockTime duration, inpoint, timeline_duration;
|
||||||
GstObject *parent, *tmpparent;
|
|
||||||
|
|
||||||
parent = gst_object_get_parent (GST_OBJECT (self));
|
g_object_get (parent, "duration", &duration, "inpoint", &inpoint, NULL);
|
||||||
while (parent) {
|
g_object_get (timeline, "duration", &timeline_duration, NULL);
|
||||||
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);
|
GST_DEBUG_OBJECT (self, "Adapting duration");
|
||||||
g_object_get (timeline, "duration", &timeline_duration, NULL);
|
if (inpoint + duration > timeline_duration) {
|
||||||
|
GESLayer *layer = ges_timeline_get_layer (timeline, 0);
|
||||||
|
|
||||||
if (inpoint + duration > timeline_duration) {
|
if (layer) {
|
||||||
GESLayer *layer = ges_timeline_get_layer (timeline, 0);
|
GESClip *clip = GES_CLIP (ges_test_clip_new ());
|
||||||
|
GList *tmp, *tracks = ges_timeline_get_tracks (timeline);
|
||||||
|
|
||||||
if (layer) {
|
g_object_set (clip, "start", timeline_duration, "duration",
|
||||||
GESClip *clip = GES_CLIP (ges_test_clip_new ());
|
inpoint + duration, "vpattern", GES_VIDEO_TEST_PATTERN_SMPTE75, NULL);
|
||||||
GList *tmp, *tracks = ges_timeline_get_tracks (timeline);
|
ges_layer_add_clip (layer, clip);
|
||||||
|
for (tmp = tracks; tmp; tmp = tmp->next) {
|
||||||
g_object_set (clip, "start", timeline_duration, "duration",
|
if (GES_IS_VIDEO_TRACK (tmp->data)) {
|
||||||
inpoint + duration, "vpattern", GES_VIDEO_TEST_PATTERN_SMPTE75,
|
GESEffect *text;
|
||||||
NULL);
|
GstCaps *caps;
|
||||||
ges_layer_add_clip (layer, clip);
|
gchar *effect_str_full = NULL;
|
||||||
for (tmp = tracks; tmp; tmp = tmp->next) {
|
const gchar *effect_str =
|
||||||
if (GES_IS_VIDEO_TRACK (tmp->data)) {
|
"textoverlay text=\"Nested timeline too short, please FIX!\" halignment=center valignment=center";
|
||||||
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_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);
|
g_list_free_full (tracks, gst_object_unref);
|
||||||
|
GST_INFO_OBJECT (timeline,
|
||||||
return;
|
"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)
|
if (data.error)
|
||||||
goto done;
|
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 ();
|
query = gst_query_new_uri ();
|
||||||
if (gst_pad_peer_query (self->sinkpad, query)) {
|
if (gst_pad_peer_query (self->sinkpad, query)) {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "nle.h"
|
#include "nle.h"
|
||||||
|
#include "../shared/nlegesplugin.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:nleobject
|
* SECTION:nleobject
|
||||||
|
@ -33,6 +34,22 @@
|
||||||
* properties provided by all the GNonLin elements.
|
* 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);
|
GST_DEBUG_CATEGORY_STATIC (nleobject_debug);
|
||||||
#define GST_CAT_DEFAULT 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 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
|
static void
|
||||||
nle_object_class_init (NleObjectClass * klass)
|
nle_object_class_init (NleObjectClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstElementClass *gstelement_class;
|
GstElementClass *gstelement_class;
|
||||||
|
GstBinClass *gstbin_class;
|
||||||
NleObjectClass *nleobject_class;
|
NleObjectClass *nleobject_class;
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
gobject_class = (GObjectClass *) klass;
|
||||||
gstelement_class = (GstElementClass *) klass;
|
gstelement_class = (GstElementClass *) klass;
|
||||||
|
gstbin_class = (GstBinClass *) klass;
|
||||||
nleobject_class = (NleObjectClass *) klass;
|
nleobject_class = (NleObjectClass *) klass;
|
||||||
GST_DEBUG_CATEGORY_INIT (nleobject_debug, "nleobject",
|
GST_DEBUG_CATEGORY_INIT (nleobject_debug, "nleobject",
|
||||||
GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object");
|
GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object");
|
||||||
parent_class = g_type_class_ref (GST_TYPE_BIN);
|
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->set_property = GST_DEBUG_FUNCPTR (nle_object_set_property);
|
||||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_object_get_property);
|
gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_object_get_property);
|
||||||
gobject_class->constructed = GST_DEBUG_FUNCPTR (nle_object_constructed);
|
gobject_class->constructed = GST_DEBUG_FUNCPTR (nle_object_constructed);
|
||||||
gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_object_dispose);
|
gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_object_dispose);
|
||||||
|
|
||||||
gstelement_class->change_state = GST_DEBUG_FUNCPTR (nle_object_change_state);
|
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->prepare = GST_DEBUG_FUNCPTR (nle_object_prepare_func);
|
||||||
nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_object_cleanup_func);
|
nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_object_cleanup_func);
|
||||||
|
|
|
@ -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);
|
Loading…
Reference in a new issue