gstreamer/ges/ges-structured-interface.c
Thibault Saunier 6c5daf8c81 command-line-formatter: Add track management to timeline description
Instead of having it all handled by the tool, this way we can
set the restriction before clips are added to the timeline,
leading to better behavior in term of video images placement
in the scene.

Without that we would have the clips positioned before setting the
restriction caps which leads to weird behavior for the end users.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/227>
2021-02-09 18:11:05 -03:00

895 lines
25 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 "ges-internal.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")
#define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
gboolean found = FALSE; \
\
if (type == GST_TYPE_CLOCK_TIME) {\
found = ges_util_structure_get_clocktime (structure,name, (GstClockTime*)var,NULL);\
}\
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'" \
" of type %s - fields in %s", name, g_type_name (type), 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_TIME(name, var, var_frames, def) G_STMT_START { \
if (!ges_util_structure_get_clocktime (structure, name, var, var_frames)) { \
*var = def; \
*var_frames = GES_FRAME_NUMBER_NONE; \
} \
} G_STMT_END
#define TRY_GET(name, type, var, def) G_STMT_START {\
g_assert (type != GST_TYPE_CLOCK_TIME); \
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;
gboolean absolute;
gdouble value;
GstClockTime timestamp;
GstControlBinding *binding = NULL;
GstTimedValueControlSource *source = NULL;
gchar *element_name = NULL, *property_name = NULL;
gboolean ret = FALSE;
gboolean setting_value;
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 ("timestamp", GST_TYPE_CLOCK_TIME, &timestamp, done);
element =
(GESTrackElement *) ges_timeline_get_element (timeline, element_name);
if (GES_IS_CLIP (element)) {
GList *tmp;
for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
if (ges_timeline_element_lookup_child (tmp->data, property_name, NULL,
NULL)) {
gst_object_replace ((GstObject **) & element, tmp->data);
break;
}
}
}
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;
}
g_object_get (binding, "absolute", &absolute, NULL);
if (absolute) {
GParamSpec *pspec;
const GValue *v;
GValue v2 = G_VALUE_INIT;
if (!ges_timeline_element_lookup_child (GES_TIMELINE_ELEMENT (element),
property_name, NULL, &pspec)) {
*error =
g_error_new (GES_ERROR, 0, "Could not get property %s for %s",
property_name, GES_TIMELINE_ELEMENT_NAME (element));
goto done;
}
v = gst_structure_get_value (structure, "value");
if (!v) {
gchar *struct_str = gst_structure_to_string (structure);
*error = g_error_new (GES_ERROR, 0,
"Could not get the mandatory field 'value'"
" of type %s - fields in %s", g_type_name (pspec->value_type),
struct_str);
g_free (struct_str);
goto done;
}
g_value_init (&v2, G_TYPE_DOUBLE);
if (!g_value_transform (v, &v2)) {
gchar *struct_str = gst_structure_to_string (structure);
*error = g_error_new (GES_ERROR, 0,
"Could not get the mandatory field 'value'"
" of type %s - fields in %s", g_type_name (pspec->value_type),
struct_str);
g_free (struct_str);
goto done;
}
value = g_value_get_double (&v2);
g_value_reset (&v2);
} else
GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
setting_value
= !g_strcmp0 (gst_structure_get_name (structure), "add-keyframe");
if (setting_value)
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 %sset value for timestamp: %" GST_TIME_FORMAT,
setting_value ? "" : "un", 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 = NULL;
GESLayer *layer = NULL;
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;
GESFrameNumber start_frame = GES_FRAME_NUMBER_NONE, inpoint_frame =
GES_FRAME_NUMBER_NONE, duration_frame = GES_FRAME_NUMBER_NONE;
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_TIME ("start", &start, &start_frame, GST_CLOCK_TIME_NONE);
TRY_GET_TIME ("inpoint", &inpoint, &inpoint_frame, 0);
TRY_GET_TIME ("duration", &duration, &duration_frame, 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);
}
gst_structure_set (structure, "asset-id", G_TYPE_STRING, asset_id, NULL);
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;
}
if (GES_FRAME_NUMBER_IS_VALID (start_frame))
start = ges_timeline_get_frame_time (timeline, start_frame);
if (GES_FRAME_NUMBER_IS_VALID (inpoint_frame)) {
inpoint =
ges_clip_asset_get_frame_time (GES_CLIP_ASSET (asset), inpoint_frame);
if (!GST_CLOCK_TIME_IS_VALID (inpoint)) {
*error =
g_error_new (GES_ERROR, 0, "Could not get inpoint from frame %"
G_GINT64_FORMAT, inpoint_frame);
goto beach;
}
}
if (GES_FRAME_NUMBER_IS_VALID (duration_frame)) {
duration = ges_timeline_get_frame_time (timeline, duration_frame);
}
if (GES_IS_URI_CLIP_ASSET (asset) && !GST_CLOCK_TIME_IS_VALID (duration)) {
duration = GST_CLOCK_DIFF (inpoint,
ges_uri_clip_asset_get_duration (GES_URI_CLIP_ASSET (asset)));
}
clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
track_types);
if (clip) {
res = TRUE;
if (GES_TIMELINE_ELEMENT_DURATION (clip) == 0) {
*error = g_error_new (GES_ERROR, 0,
"Clip %s has 0 as duration, please provide a proper duration",
asset_id);
res = FALSE;
goto beach;
}
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);
res = FALSE;
goto beach;
}
if (res) {
g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
}
res = _ges_save_timeline_if_needed (timeline, structure, error);
beach:
gst_clear_object (&layer);
gst_clear_object (&asset);
g_free (asset_id);
g_free (check_asset_id);
return res;
}
gboolean
_ges_add_track_from_struct (GESTimeline * timeline,
GstStructure * structure, GError ** error)
{
const gchar *ttype;
GESTrack *track;
GstCaps *caps;
const gchar *valid_fields[] = { "type", "restrictions", NULL };
FieldsError fields_error = { valid_fields, NULL };
if (!_check_fields (structure, fields_error, error))
return FALSE;
ttype = gst_structure_get_string (structure, "type");
if (!g_strcmp0 (ttype, "video")) {
track = GES_TRACK (ges_video_track_new ());
} else if (!g_strcmp0 (ttype, "audio")) {
track = GES_TRACK (ges_audio_track_new ());
} else {
g_set_error (error, GES_ERROR, 0, "Unhandled track type: `%s`", ttype);
return FALSE;
}
if (gst_structure_has_field (structure, "restrictions")) {
GstStructure *restriction_struct;
gchar *restriction_str;
if (gst_structure_get (structure, "restrictions", GST_TYPE_STRUCTURE,
&restriction_struct, NULL)) {
caps = gst_caps_new_full (restriction_struct, NULL);
} else if (gst_structure_get (structure, "restrictions", G_TYPE_STRING,
&restriction_str, NULL)) {
caps = gst_caps_from_string (restriction_str);
if (!caps) {
g_set_error (error, GES_ERROR, 0, "Invalid restrictions caps: %s",
restriction_str);
g_free (restriction_str);
return FALSE;
}
g_free (restriction_str);
} else if (!gst_structure_get (structure, "restrictions", GST_TYPE_CAPS,
&caps, NULL)) {
gchar *tmp = gst_structure_to_string (structure);
g_set_error (error, GES_ERROR, 0, "Can't use restrictions caps from %s",
tmp);
g_object_unref (track);
return FALSE;
}
ges_track_set_restriction_caps (track, caps);
gst_caps_unref (caps);
}
return ges_timeline_add_track (timeline, track);
}
gboolean
_ges_container_add_child_from_struct (GESTimeline * timeline,
GstStructure * structure, GError ** error)
{
GESAsset *asset = NULL;
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", "inpoint",
"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));
}
if (!GES_IS_CONTAINER (container)) {
*error =
g_error_new (GES_ERROR, 0, "Could not find container: %s (%p)",
container_name, container);
res = FALSE;
goto beach;
}
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)) {
*error = 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)) {
*error = g_error_new (GES_ERROR, 0, "Could not find child element");
goto beach;
}
}
if (!child) {
*error =
g_error_new (GES_ERROR, 0, "Wrong parameters, 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);
if (gst_structure_has_field (structure, "inpoint")) {
GstClockTime inpoint;
GESFrameNumber finpoint;
if (!GES_IS_TRACK_ELEMENT (child)) {
*error = g_error_new (GES_ERROR, 0, "Child %s is not a trackelement"
", can't set inpoint.", child_name);
gst_object_unref (child);
goto beach;
}
if (!ges_util_structure_get_clocktime (structure, "inpoint", &inpoint,
&finpoint)) {
*error = g_error_new (GES_ERROR, 0, "Could not use inpoint.");
gst_object_unref (child);
goto beach;
}
if (!ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child),
TRUE)) {
*error =
g_error_new (GES_ERROR, 0,
"Could not set inpoint as %s can't have an internal source",
child_name);
gst_object_unref (child);
goto beach;
}
if (GES_FRAME_NUMBER_IS_VALID (finpoint))
inpoint = ges_timeline_get_frame_time (timeline, finpoint);
ges_timeline_element_set_inpoint (child, inpoint);
}
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:
gst_clear_object (&asset);
return res;
}
gboolean
_ges_set_child_property_from_struct (GESTimeline * timeline,
GstStructure * structure, GError ** error)
{
const GValue *value;
GValue prop_value = G_VALUE_INIT;
gboolean prop_value_set = FALSE;
GESTimelineElement *element;
const gchar *property_name, *element_name;
gchar *serialized;
gboolean res;
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");
if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
GParamSpec *pspec;
if (ges_timeline_element_lookup_child (element, property_name, NULL,
&pspec)) {
GType p_type = pspec->value_type;
g_param_spec_unref (pspec);
if (p_type != G_TYPE_STRING) {
const gchar *val_string = g_value_get_string (value);
g_value_init (&prop_value, p_type);
if (!gst_value_deserialize (&prop_value, val_string)) {
*error = g_error_new (GES_ERROR, 0, "Could not set the property %s "
"because the value %s could not be deserialized to the %s type",
property_name, val_string, g_type_name (p_type));
return FALSE;
}
prop_value_set = TRUE;
}
}
/* else, let the setter fail below */
}
if (!prop_value_set) {
g_value_init (&prop_value, G_VALUE_TYPE (value));
g_value_copy (value, &prop_value);
}
serialized = gst_value_serialize (&prop_value);
GST_INFO_OBJECT (element, "Setting property %s to %s\n", property_name,
serialized);
g_free (serialized);
res = ges_timeline_element_set_child_property (element, property_name,
&prop_value);
g_value_unset (&prop_value);
if (!res) {
guint n_specs, i;
GParamSpec **specs =
ges_timeline_element_list_children_properties (element, &n_specs);
GString *errstr = g_string_new (NULL);
g_string_append_printf (errstr,
"\n Could not set property `%s` on `%s`, valid properties:\n",
property_name, GES_TIMELINE_ELEMENT_NAME (element));
for (i = 0; i < n_specs; i++)
g_string_append_printf (errstr, " - %s\n", specs[i]->name);
g_free (specs);
*error = g_error_new_literal (GES_ERROR, 0, errstr->str);
g_string_free (errstr, TRUE);
return FALSE;
}
return _ges_save_timeline_if_needed (timeline, structure, error);
}
#undef GET_AND_CHECK
#undef TRY_GET