mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-12 11:26:39 +00:00
a55296314c
When we have nested timelines, we need to make sure the underlying formatted file is reloaded when commiting the main composition to take into account the new timeline. In other to make the implementation as simple as possible we make sure that whenever the toplevel composition is commited, the decodebin holding the gesdemux is torn down so that a new demuxer is created with the new content of the timeline. To do that a we do a NleCompositionQueryNeedsTearDown query to which gesdemux answers leading to a full nlecomposition stack deactivation/activation cycle.
672 lines
18 KiB
C
672 lines
18 KiB
C
/* GStreamer Editing Services
|
|
*
|
|
* Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
|
|
*
|
|
* 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., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "ges-structured-interface.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#define LAST_CONTAINER_QDATA g_quark_from_string("ges-structured-last-container")
|
|
#define LAST_CHILD_QDATA g_quark_from_string("ges-structured-last-child")
|
|
|
|
static gboolean
|
|
_get_clocktime (GstStructure * structure, const gchar * name, gpointer var)
|
|
{
|
|
gboolean found = FALSE;
|
|
GstClockTime *val = (GstClockTime *) var;
|
|
|
|
const GValue *gvalue = gst_structure_get_value (structure, name);
|
|
|
|
if (gvalue) {
|
|
if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME) {
|
|
*val = (GstClockTime) g_value_get_uint64 (gvalue);
|
|
found = TRUE;
|
|
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT64) {
|
|
*val = (GstClockTime) g_value_get_uint64 (gvalue);
|
|
found = TRUE;
|
|
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) {
|
|
*val = (GstClockTime) g_value_get_uint (gvalue);
|
|
found = TRUE;
|
|
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) {
|
|
*val = (GstClockTime) g_value_get_int (gvalue);
|
|
found = TRUE;
|
|
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64) {
|
|
*val = (GstClockTime) g_value_get_int64 (gvalue);
|
|
found = TRUE;
|
|
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_DOUBLE) {
|
|
gdouble d = g_value_get_double (gvalue);
|
|
|
|
found = TRUE;
|
|
if (d == -1.0)
|
|
*val = GST_CLOCK_TIME_NONE;
|
|
else {
|
|
*val = d * GST_SECOND;
|
|
*val = GST_ROUND_UP_4 (*val);
|
|
}
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
#define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
|
|
gboolean found = FALSE; \
|
|
\
|
|
if (type == GST_TYPE_CLOCK_TIME) {\
|
|
found = _get_clocktime(structure,name,var);\
|
|
}\
|
|
else { \
|
|
found = gst_structure_get (structure, name, type, var, NULL); \
|
|
}\
|
|
if (!found) {\
|
|
gchar *struct_str = gst_structure_to_string (structure); \
|
|
*error = g_error_new (GES_ERROR, 0, \
|
|
"Could not get the mandatory field '%s'" \
|
|
" fields in %s", name, struct_str); \
|
|
g_free (struct_str); \
|
|
goto label;\
|
|
} \
|
|
} G_STMT_END
|
|
|
|
#define TRY_GET_STRING(name,var,def) G_STMT_START {\
|
|
*var = gst_structure_get_string (structure, name); \
|
|
if (*var == NULL) \
|
|
*var = def; \
|
|
} G_STMT_END
|
|
|
|
#define TRY_GET(name,type,var,def) G_STMT_START {\
|
|
if (type == GST_TYPE_CLOCK_TIME) {\
|
|
if (!_get_clocktime(structure,name,var))\
|
|
*var = def; \
|
|
} else if (!gst_structure_get (structure, name, type, var, NULL)) {\
|
|
*var = def; \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
typedef struct
|
|
{
|
|
const gchar **fields;
|
|
GList *invalid_fields;
|
|
} FieldsError;
|
|
|
|
static gboolean
|
|
_check_field (GQuark field_id, const GValue * value, FieldsError * fields_error)
|
|
{
|
|
guint i;
|
|
const gchar *field = g_quark_to_string (field_id);
|
|
|
|
for (i = 0; fields_error->fields[i]; i++) {
|
|
if (g_strcmp0 (fields_error->fields[i], field) == 0) {
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
fields_error->invalid_fields =
|
|
g_list_append (fields_error->invalid_fields, (gpointer) field);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_check_fields (GstStructure * structure, FieldsError fields_error,
|
|
GError ** error)
|
|
{
|
|
gst_structure_foreach (structure,
|
|
(GstStructureForeachFunc) _check_field, &fields_error);
|
|
|
|
if (fields_error.invalid_fields) {
|
|
GList *tmp;
|
|
const gchar *struct_name = gst_structure_get_name (structure);
|
|
GString *msg = g_string_new (NULL);
|
|
|
|
g_string_append_printf (msg, "Unknown propert%s in %s%s:",
|
|
g_list_length (fields_error.invalid_fields) > 1 ? "ies" : "y",
|
|
strlen (struct_name) > 1 ? "--" : "-",
|
|
gst_structure_get_name (structure));
|
|
|
|
for (tmp = fields_error.invalid_fields; tmp; tmp = tmp->next)
|
|
g_string_append_printf (msg, " %s", (gchar *) tmp->data);
|
|
|
|
if (error)
|
|
*error = g_error_new_literal (GES_ERROR, 0, msg->str);
|
|
|
|
g_string_free (msg, TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
_ges_save_timeline_if_needed (GESTimeline * timeline, GstStructure * structure,
|
|
GError ** error)
|
|
{
|
|
gboolean res = TRUE;
|
|
const gchar *nested_timeline_id =
|
|
gst_structure_get_string (structure, "project-uri");
|
|
|
|
if (nested_timeline_id) {
|
|
res = ges_timeline_save_to_uri (timeline, nested_timeline_id, NULL, TRUE,
|
|
error);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
gboolean
|
|
_ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
|
|
GstStructure * structure, GError ** error)
|
|
{
|
|
GESTrackElement *element;
|
|
|
|
gdouble value;
|
|
GstClockTime timestamp;
|
|
GstControlBinding *binding = NULL;
|
|
GstTimedValueControlSource *source = NULL;
|
|
gchar *element_name = NULL, *property_name = NULL;
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
const gchar *valid_fields[] =
|
|
{ "element-name", "property-name", "value", "timestamp", "project-uri",
|
|
NULL
|
|
};
|
|
|
|
FieldsError fields_error = { valid_fields, NULL };
|
|
|
|
if (!_check_fields (structure, fields_error, error))
|
|
return FALSE;
|
|
|
|
GET_AND_CHECK ("element-name", G_TYPE_STRING, &element_name, done);
|
|
GET_AND_CHECK ("property-name", G_TYPE_STRING, &property_name, done);
|
|
GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
|
|
GET_AND_CHECK ("timestamp", GST_TYPE_CLOCK_TIME, ×tamp, done);
|
|
|
|
element =
|
|
GES_TRACK_ELEMENT (ges_timeline_get_element (timeline, element_name));
|
|
|
|
if (!GES_IS_TRACK_ELEMENT (element)) {
|
|
*error =
|
|
g_error_new (GES_ERROR, 0, "Could not find TrackElement %s",
|
|
element_name);
|
|
|
|
goto done;
|
|
}
|
|
|
|
binding = ges_track_element_get_control_binding (element, property_name);
|
|
if (binding == NULL) {
|
|
*error = g_error_new (GES_ERROR, 0, "No control binding found for %s:%s"
|
|
" you should first set-control-binding on it",
|
|
element_name, property_name);
|
|
|
|
goto done;
|
|
}
|
|
|
|
g_object_get (binding, "control-source", &source, NULL);
|
|
|
|
if (source == NULL) {
|
|
*error = g_error_new (GES_ERROR, 0, "No control source found for %s:%s"
|
|
" you should first set-control-binding on it",
|
|
element_name, property_name);
|
|
|
|
goto done;
|
|
}
|
|
|
|
if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
|
|
*error = g_error_new (GES_ERROR, 0, "You can use add-keyframe"
|
|
" only on GstTimedValueControlSource not %s",
|
|
G_OBJECT_TYPE_NAME (source));
|
|
|
|
goto done;
|
|
}
|
|
|
|
if (!g_strcmp0 (gst_structure_get_name (structure), "add-keyframe"))
|
|
ret = gst_timed_value_control_source_set (source, timestamp, value);
|
|
else {
|
|
ret = gst_timed_value_control_source_unset (source, timestamp);
|
|
|
|
if (!ret) {
|
|
*error =
|
|
g_error_new (GES_ERROR, 0,
|
|
"Could not unset value for timestamp: %" GST_TIME_FORMAT,
|
|
GST_TIME_ARGS (timestamp));
|
|
}
|
|
}
|
|
ret = _ges_save_timeline_if_needed (timeline, structure, error);
|
|
|
|
done:
|
|
if (source)
|
|
gst_object_unref (source);
|
|
g_free (element_name);
|
|
g_free (property_name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
GESAsset *
|
|
_ges_get_asset_from_timeline (GESTimeline * timeline, GType type,
|
|
const gchar * id, GError ** error)
|
|
{
|
|
GESAsset *asset;
|
|
GESProject *project = ges_timeline_get_project (timeline);
|
|
GError *err = NULL;
|
|
|
|
asset = ges_project_create_asset_sync (project, id, type, &err);
|
|
|
|
if (err)
|
|
g_propagate_error (error, err);
|
|
if (!asset || (error && *error)) {
|
|
|
|
if (error && !*error) {
|
|
*error = g_error_new (GES_ERROR, 0,
|
|
"There was an error requesting the asset with id %s and type %s (%s)",
|
|
id, g_type_name (type), "unknown");
|
|
}
|
|
|
|
GST_ERROR
|
|
("There was an error requesting the asset with id %s and type %s (%s)",
|
|
id, g_type_name (type), error ? (*error)->message : "unknown");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return asset;
|
|
}
|
|
|
|
/* Unref after usage */
|
|
GESLayer *
|
|
_ges_get_layer_by_priority (GESTimeline * timeline, gint priority)
|
|
{
|
|
GList *layers;
|
|
gint nlayers;
|
|
GESLayer *layer = NULL;
|
|
|
|
priority = MAX (priority, 0);
|
|
layers = ges_timeline_get_layers (timeline);
|
|
nlayers = (gint) g_list_length (layers);
|
|
if (priority >= nlayers) {
|
|
gint i = nlayers;
|
|
|
|
while (i <= priority) {
|
|
layer = ges_timeline_append_layer (timeline);
|
|
|
|
i++;
|
|
}
|
|
|
|
layer = gst_object_ref (layer);
|
|
|
|
goto done;
|
|
}
|
|
|
|
layer = ges_timeline_get_layer (timeline, priority);
|
|
|
|
done:
|
|
g_list_free_full (layers, gst_object_unref);
|
|
|
|
return layer;
|
|
}
|
|
|
|
static gchar *
|
|
ensure_uri (gchar * location)
|
|
{
|
|
if (gst_uri_is_valid (location))
|
|
return g_strdup (location);
|
|
else
|
|
return gst_filename_to_uri (location, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
get_flags_from_string (GType type, const gchar * str_flags, guint * flags)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
g_value_init (&value, type);
|
|
|
|
if (!gst_value_deserialize (&value, str_flags)) {
|
|
g_value_unset (&value);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
*flags = g_value_get_flags (&value);
|
|
g_value_unset (&value);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
_ges_add_clip_from_struct (GESTimeline * timeline, GstStructure * structure,
|
|
GError ** error)
|
|
{
|
|
GESAsset *asset;
|
|
GESLayer *layer;
|
|
GESClip *clip;
|
|
gint layer_priority;
|
|
const gchar *name;
|
|
const gchar *text;
|
|
const gchar *pattern;
|
|
const gchar *track_types_str;
|
|
const gchar *nested_timeline_id;
|
|
gchar *asset_id = NULL;
|
|
gchar *check_asset_id = NULL;
|
|
const gchar *type_string;
|
|
GType type;
|
|
gboolean res = FALSE;
|
|
GESTrackType track_types = GES_TRACK_TYPE_UNKNOWN;
|
|
|
|
GstClockTime duration = 1 * GST_SECOND, inpoint = 0, start =
|
|
GST_CLOCK_TIME_NONE;
|
|
|
|
const gchar *valid_fields[] =
|
|
{ "asset-id", "pattern", "name", "layer-priority", "layer", "type",
|
|
"start", "inpoint", "duration", "text", "track-types", "project-uri",
|
|
NULL
|
|
};
|
|
|
|
FieldsError fields_error = { valid_fields, NULL };
|
|
|
|
if (!_check_fields (structure, fields_error, error))
|
|
return FALSE;
|
|
|
|
GET_AND_CHECK ("asset-id", G_TYPE_STRING, &check_asset_id, beach);
|
|
|
|
TRY_GET_STRING ("pattern", &pattern, NULL);
|
|
TRY_GET_STRING ("text", &text, NULL);
|
|
TRY_GET_STRING ("name", &name, NULL);
|
|
TRY_GET ("layer-priority", G_TYPE_INT, &layer_priority, -1);
|
|
if (layer_priority == -1)
|
|
TRY_GET ("layer", G_TYPE_INT, &layer_priority, -1);
|
|
TRY_GET_STRING ("type", &type_string, "GESUriClip");
|
|
TRY_GET ("start", GST_TYPE_CLOCK_TIME, &start, GST_CLOCK_TIME_NONE);
|
|
TRY_GET ("inpoint", GST_TYPE_CLOCK_TIME, &inpoint, 0);
|
|
TRY_GET ("duration", GST_TYPE_CLOCK_TIME, &duration, GST_CLOCK_TIME_NONE);
|
|
TRY_GET_STRING ("track-types", &track_types_str, NULL);
|
|
TRY_GET_STRING ("project-uri", &nested_timeline_id, NULL);
|
|
|
|
if (track_types_str) {
|
|
if (!get_flags_from_string (GES_TYPE_TRACK_TYPE, track_types_str,
|
|
&track_types)) {
|
|
*error =
|
|
g_error_new (GES_ERROR, 0, "Invalid track types: %s",
|
|
track_types_str);
|
|
}
|
|
|
|
}
|
|
|
|
if (!(type = g_type_from_name (type_string))) {
|
|
*error = g_error_new (GES_ERROR, 0, "This type doesn't exist : %s",
|
|
type_string);
|
|
|
|
goto beach;
|
|
}
|
|
|
|
if (type == GES_TYPE_URI_CLIP) {
|
|
asset_id = ensure_uri (check_asset_id);
|
|
} else {
|
|
asset_id = g_strdup (check_asset_id);
|
|
}
|
|
|
|
asset = _ges_get_asset_from_timeline (timeline, type, asset_id, error);
|
|
if (!asset) {
|
|
res = FALSE;
|
|
|
|
goto beach;
|
|
}
|
|
|
|
if (layer_priority == -1) {
|
|
GESContainer *container;
|
|
|
|
container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
|
|
if (!container || !GES_IS_CLIP (container))
|
|
layer = _ges_get_layer_by_priority (timeline, 0);
|
|
else
|
|
layer = ges_clip_get_layer (GES_CLIP (container));
|
|
|
|
if (!layer)
|
|
layer = _ges_get_layer_by_priority (timeline, 0);
|
|
} else {
|
|
layer = _ges_get_layer_by_priority (timeline, layer_priority);
|
|
}
|
|
|
|
if (!layer) {
|
|
*error =
|
|
g_error_new (GES_ERROR, 0, "No layer with priority %d", layer_priority);
|
|
goto beach;
|
|
}
|
|
|
|
clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
|
|
track_types);
|
|
|
|
if (clip) {
|
|
res = TRUE;
|
|
|
|
if (GES_IS_TEST_CLIP (clip)) {
|
|
if (pattern) {
|
|
GEnumClass *enum_class =
|
|
G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_TEST_PATTERN_TYPE));
|
|
GEnumValue *value = g_enum_get_value_by_nick (enum_class, pattern);
|
|
|
|
if (!value) {
|
|
res = FALSE;
|
|
goto beach;
|
|
}
|
|
|
|
ges_test_clip_set_vpattern (GES_TEST_CLIP (clip), value->value);
|
|
g_type_class_unref (enum_class);
|
|
}
|
|
}
|
|
|
|
if (GES_IS_TITLE_CLIP (clip) && text)
|
|
ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (clip),
|
|
"text", text, NULL);
|
|
|
|
if (name
|
|
&& !ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), name)) {
|
|
res = FALSE;
|
|
*error =
|
|
g_error_new (GES_ERROR, 0, "couldn't set name %s on clip with id %s",
|
|
name, asset_id);
|
|
}
|
|
} else {
|
|
*error = g_error_new (GES_ERROR, 0,
|
|
"Couldn't add clip with id %s to layer with priority %d", asset_id,
|
|
layer_priority);
|
|
}
|
|
|
|
if (res) {
|
|
g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
|
|
g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
|
|
}
|
|
|
|
gst_object_unref (layer);
|
|
res = _ges_save_timeline_if_needed (timeline, structure, error);
|
|
|
|
beach:
|
|
g_free (asset_id);
|
|
g_free (check_asset_id);
|
|
return res;
|
|
}
|
|
|
|
gboolean
|
|
_ges_container_add_child_from_struct (GESTimeline * timeline,
|
|
GstStructure * structure, GError ** error)
|
|
{
|
|
GESAsset *asset;
|
|
GESContainer *container;
|
|
GESTimelineElement *child = NULL;
|
|
const gchar *container_name, *child_name, *child_type, *id;
|
|
|
|
gboolean res = TRUE;
|
|
const gchar *valid_fields[] = { "container-name", "asset-id",
|
|
"child-type", "child-name", "project-uri", NULL
|
|
};
|
|
|
|
FieldsError fields_error = { valid_fields, NULL };
|
|
|
|
if (!_check_fields (structure, fields_error, error))
|
|
return FALSE;
|
|
|
|
container_name = gst_structure_get_string (structure, "container-name");
|
|
|
|
if (container_name == NULL) {
|
|
container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
|
|
} else {
|
|
container =
|
|
GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
|
|
}
|
|
|
|
g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
|
|
|
|
id = gst_structure_get_string (structure, "asset-id");
|
|
child_type = gst_structure_get_string (structure, "child-type");
|
|
|
|
if (id && child_type) {
|
|
asset =
|
|
_ges_get_asset_from_timeline (timeline, g_type_from_name (child_type),
|
|
id, error);
|
|
|
|
if (asset == NULL) {
|
|
res = FALSE;
|
|
goto beach;
|
|
}
|
|
|
|
child = GES_TIMELINE_ELEMENT (ges_asset_extract (asset, NULL));
|
|
if (!GES_IS_TIMELINE_ELEMENT (child)) {
|
|
g_error_new (GES_ERROR, 0, "Could not extract child element");
|
|
|
|
goto beach;
|
|
}
|
|
}
|
|
|
|
child_name = gst_structure_get_string (structure, "child-name");
|
|
if (!child && child_name) {
|
|
child = ges_timeline_get_element (timeline, child_name);
|
|
if (!GES_IS_TIMELINE_ELEMENT (child)) {
|
|
g_error_new (GES_ERROR, 0, "Could not find child element");
|
|
|
|
goto beach;
|
|
}
|
|
}
|
|
|
|
if (!child) {
|
|
g_error_new (GES_ERROR, 0, "Wong parametters, could not get a child");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (child_name)
|
|
ges_timeline_element_set_name (child, child_name);
|
|
else
|
|
child_name = GES_TIMELINE_ELEMENT_NAME (child);
|
|
|
|
res = ges_container_add (container, child);
|
|
if (res == FALSE) {
|
|
g_error_new (GES_ERROR, 0, "Could not add child to container");
|
|
} else {
|
|
g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, child);
|
|
}
|
|
res = _ges_save_timeline_if_needed (timeline, structure, error);
|
|
|
|
beach:
|
|
return res;
|
|
}
|
|
|
|
gboolean
|
|
_ges_set_child_property_from_struct (GESTimeline * timeline,
|
|
GstStructure * structure, GError ** error)
|
|
{
|
|
const GValue *value;
|
|
GESTimelineElement *element;
|
|
const gchar *property_name, *element_name;
|
|
|
|
const gchar *valid_fields[] =
|
|
{ "element-name", "property", "value", "project-uri", NULL };
|
|
|
|
FieldsError fields_error = { valid_fields, NULL };
|
|
|
|
if (!_check_fields (structure, fields_error, error))
|
|
return FALSE;
|
|
|
|
element_name = gst_structure_get_string (structure, "element-name");
|
|
if (element_name == NULL)
|
|
element = g_object_get_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA);
|
|
else
|
|
element = ges_timeline_get_element (timeline, element_name);
|
|
|
|
property_name = gst_structure_get_string (structure, "property");
|
|
if (property_name == NULL) {
|
|
const gchar *name = gst_structure_get_name (structure);
|
|
|
|
if (g_str_has_prefix (name, "set-"))
|
|
property_name = &name[4];
|
|
else {
|
|
gchar *struct_str = gst_structure_to_string (structure);
|
|
|
|
*error =
|
|
g_error_new (GES_ERROR, 0, "Could not find any property name in %s",
|
|
struct_str);
|
|
g_free (struct_str);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (element) {
|
|
if (!ges_track_element_lookup_child (GES_TRACK_ELEMENT (element),
|
|
property_name, NULL, NULL))
|
|
element = NULL;
|
|
}
|
|
|
|
if (!element) {
|
|
element = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
|
|
|
|
if (element == NULL) {
|
|
*error =
|
|
g_error_new (GES_ERROR, 0,
|
|
"Could not find anywhere to set property: %s", property_name);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!GES_IS_TIMELINE_ELEMENT (element)) {
|
|
*error =
|
|
g_error_new (GES_ERROR, 0, "Could not find child %s", element_name);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
value = gst_structure_get_value (structure, "value");
|
|
|
|
GST_DEBUG ("%s Setting %s property to %p",
|
|
element->name, property_name, value);
|
|
|
|
ges_timeline_element_set_child_property (element, property_name,
|
|
(GValue *) value);
|
|
return _ges_save_timeline_if_needed (timeline, structure, error);
|
|
}
|
|
|
|
#undef GET_AND_CHECK
|
|
#undef TRY_GET
|