94f6c6d418761bf00f1a7b69d54da3896363a703
[platform/upstream/gst-editing-services.git] / ges / ges-structured-interface.c
1 /* GStreamer Editing Services
2  *
3  * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "ges-structured-interface.h"
25 #include "ges-internal.h"
26
27 #include <string.h>
28
29
30 #define LAST_CONTAINER_QDATA g_quark_from_string("ges-structured-last-container")
31 #define LAST_CHILD_QDATA g_quark_from_string("ges-structured-last-child")
32
33 #define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
34   gboolean found = FALSE; \
35 \
36   if (type == GST_TYPE_CLOCK_TIME) {\
37     found = ges_util_structure_get_clocktime (structure,name, (GstClockTime*)var,NULL);\
38   }\
39   else { \
40     found = gst_structure_get (structure, name, type, var, NULL); \
41   }\
42   if (!found) {\
43     gchar *struct_str = gst_structure_to_string (structure); \
44     *error = g_error_new (GES_ERROR, 0, \
45         "Could not get the mandatory field '%s'" \
46         " of type %s - fields in %s", name, g_type_name (type), struct_str); \
47     g_free (struct_str); \
48     goto label;\
49   } \
50 } G_STMT_END
51
52 #define TRY_GET_STRING(name,var,def) G_STMT_START {\
53   *var = gst_structure_get_string (structure, name); \
54   if (*var == NULL) \
55     *var = def; \
56 } G_STMT_END
57
58 #define TRY_GET_TIME(name, var, var_frames, def) G_STMT_START  {       \
59   if (!ges_util_structure_get_clocktime (structure, name, var, var_frames)) { \
60       *var = def;                                          \
61       *var_frames = GES_FRAME_NUMBER_NONE;                            \
62   }                                                        \
63 } G_STMT_END
64
65 #define TRY_GET(name, type, var, def) G_STMT_START {\
66   g_assert (type != GST_TYPE_CLOCK_TIME);                      \
67   if (!gst_structure_get (structure, name, type, var, NULL))\
68     *var = def;                                             \
69 } G_STMT_END
70
71 typedef struct
72 {
73   const gchar **fields;
74   GList *invalid_fields;
75 } FieldsError;
76
77 static gboolean
78 _check_field (GQuark field_id, const GValue * value, FieldsError * fields_error)
79 {
80   guint i;
81   const gchar *field = g_quark_to_string (field_id);
82
83   for (i = 0; fields_error->fields[i]; i++) {
84     if (g_strcmp0 (fields_error->fields[i], field) == 0) {
85
86       return TRUE;
87     }
88   }
89
90   fields_error->invalid_fields =
91       g_list_append (fields_error->invalid_fields, (gpointer) field);
92
93   return TRUE;
94 }
95
96 static gboolean
97 _check_fields (GstStructure * structure, FieldsError fields_error,
98     GError ** error)
99 {
100   gst_structure_foreach (structure,
101       (GstStructureForeachFunc) _check_field, &fields_error);
102
103   if (fields_error.invalid_fields) {
104     GList *tmp;
105     const gchar *struct_name = gst_structure_get_name (structure);
106     GString *msg = g_string_new (NULL);
107
108     g_string_append_printf (msg, "Unknown propert%s in %s%s:",
109         g_list_length (fields_error.invalid_fields) > 1 ? "ies" : "y",
110         strlen (struct_name) > 1 ? "--" : "-",
111         gst_structure_get_name (structure));
112
113     for (tmp = fields_error.invalid_fields; tmp; tmp = tmp->next)
114       g_string_append_printf (msg, " %s", (gchar *) tmp->data);
115
116     if (error)
117       *error = g_error_new_literal (GES_ERROR, 0, msg->str);
118
119     g_string_free (msg, TRUE);
120
121     return FALSE;
122   }
123
124   return TRUE;
125 }
126
127 gboolean
128 _ges_save_timeline_if_needed (GESTimeline * timeline, GstStructure * structure,
129     GError ** error)
130 {
131   gboolean res = TRUE;
132   const gchar *nested_timeline_id =
133       gst_structure_get_string (structure, "project-uri");
134
135   if (nested_timeline_id) {
136     res = ges_timeline_save_to_uri (timeline, nested_timeline_id, NULL, TRUE,
137         error);
138   }
139
140   return res;
141 }
142
143 gboolean
144 _ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
145     GstStructure * structure, GError ** error)
146 {
147   GESTrackElement *element;
148
149   gboolean absolute;
150   gdouble value;
151   GstClockTime timestamp;
152   GstControlBinding *binding = NULL;
153   GstTimedValueControlSource *source = NULL;
154   gchar *element_name = NULL, *property_name = NULL;
155
156   gboolean ret = FALSE;
157   gboolean setting_value;
158
159   const gchar *valid_fields[] =
160       { "element-name", "property-name", "value", "timestamp", "project-uri",
161     NULL
162   };
163
164   FieldsError fields_error = { valid_fields, NULL };
165
166   if (!_check_fields (structure, fields_error, error))
167     return FALSE;
168
169   GET_AND_CHECK ("element-name", G_TYPE_STRING, &element_name, done);
170   GET_AND_CHECK ("property-name", G_TYPE_STRING, &property_name, done);
171   GET_AND_CHECK ("timestamp", GST_TYPE_CLOCK_TIME, &timestamp, done);
172
173   element =
174       (GESTrackElement *) ges_timeline_get_element (timeline, element_name);
175
176   if (GES_IS_CLIP (element)) {
177     GList *tmp;
178     for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
179       if (ges_timeline_element_lookup_child (tmp->data, property_name, NULL,
180               NULL)) {
181         gst_object_replace ((GstObject **) & element, tmp->data);
182
183         break;
184       }
185     }
186   }
187
188   if (!GES_IS_TRACK_ELEMENT (element)) {
189     *error =
190         g_error_new (GES_ERROR, 0, "Could not find TrackElement %s",
191         element_name);
192
193     goto done;
194   }
195
196   binding = ges_track_element_get_control_binding (element, property_name);
197   if (binding == NULL) {
198     *error = g_error_new (GES_ERROR, 0, "No control binding found for %s:%s"
199         " you should first set-control-binding on it",
200         element_name, property_name);
201
202     goto done;
203   }
204
205   g_object_get (binding, "control-source", &source, NULL);
206
207   if (source == NULL) {
208     *error = g_error_new (GES_ERROR, 0, "No control source found for %s:%s"
209         " you should first set-control-binding on it",
210         element_name, property_name);
211
212     goto done;
213   }
214
215   if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
216     *error = g_error_new (GES_ERROR, 0, "You can use add-keyframe"
217         " only on GstTimedValueControlSource not %s",
218         G_OBJECT_TYPE_NAME (source));
219
220     goto done;
221   }
222
223   g_object_get (binding, "absolute", &absolute, NULL);
224   if (absolute) {
225     GParamSpec *pspec;
226     const GValue *v;
227     GValue v2 = G_VALUE_INIT;
228
229     if (!ges_timeline_element_lookup_child (GES_TIMELINE_ELEMENT (element),
230             property_name, NULL, &pspec)) {
231       *error =
232           g_error_new (GES_ERROR, 0, "Could not get property %s for %s",
233           property_name, GES_TIMELINE_ELEMENT_NAME (element));
234       goto done;
235     }
236
237     v = gst_structure_get_value (structure, "value");
238     if (!v) {
239       gchar *struct_str = gst_structure_to_string (structure);
240
241       *error = g_error_new (GES_ERROR, 0,
242           "Could not get the mandatory field 'value'"
243           " of type %s - fields in %s", g_type_name (pspec->value_type),
244           struct_str);
245       g_free (struct_str);
246       goto done;
247     }
248
249     g_value_init (&v2, G_TYPE_DOUBLE);
250     if (!g_value_transform (v, &v2)) {
251       gchar *struct_str = gst_structure_to_string (structure);
252
253       *error = g_error_new (GES_ERROR, 0,
254           "Could not get the mandatory field 'value'"
255           " of type %s - fields in %s", g_type_name (pspec->value_type),
256           struct_str);
257       g_free (struct_str);
258       goto done;
259     }
260     value = g_value_get_double (&v2);
261     g_value_reset (&v2);
262   } else
263     GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
264
265   setting_value
266       = !g_strcmp0 (gst_structure_get_name (structure), "add-keyframe");
267   if (setting_value)
268     ret = gst_timed_value_control_source_set (source, timestamp, value);
269   else
270     ret = gst_timed_value_control_source_unset (source, timestamp);
271
272   if (!ret) {
273     *error =
274         g_error_new (GES_ERROR, 0,
275         "Could not %sset value for timestamp: %" GST_TIME_FORMAT,
276         setting_value ? "" : "un", GST_TIME_ARGS (timestamp));
277   }
278   ret = _ges_save_timeline_if_needed (timeline, structure, error);
279
280 done:
281   if (source)
282     gst_object_unref (source);
283   g_free (element_name);
284   g_free (property_name);
285
286   return ret;
287
288 }
289
290 GESAsset *
291 _ges_get_asset_from_timeline (GESTimeline * timeline, GType type,
292     const gchar * id, GError ** error)
293 {
294   GESAsset *asset;
295   GESProject *project = ges_timeline_get_project (timeline);
296   GError *err = NULL;
297
298   asset = ges_project_create_asset_sync (project, id, type, &err);
299
300   if (err)
301     g_propagate_error (error, err);
302   if (!asset || (error && *error)) {
303
304     if (error && !*error) {
305       *error = g_error_new (GES_ERROR, 0,
306           "There was an error requesting the asset with id %s and type %s (%s)",
307           id, g_type_name (type), "unknown");
308     }
309
310     GST_ERROR
311         ("There was an error requesting the asset with id %s and type %s (%s)",
312         id, g_type_name (type), error ? (*error)->message : "unknown");
313
314     return NULL;
315   }
316
317   return asset;
318 }
319
320 /* Unref after usage */
321 GESLayer *
322 _ges_get_layer_by_priority (GESTimeline * timeline, gint priority)
323 {
324   GList *layers;
325   gint nlayers;
326   GESLayer *layer = NULL;
327
328   priority = MAX (priority, 0);
329   layers = ges_timeline_get_layers (timeline);
330   nlayers = (gint) g_list_length (layers);
331   if (priority >= nlayers) {
332     gint i = nlayers;
333
334     while (i <= priority) {
335       layer = ges_timeline_append_layer (timeline);
336
337       i++;
338     }
339
340     layer = gst_object_ref (layer);
341
342     goto done;
343   }
344
345   layer = ges_timeline_get_layer (timeline, priority);
346
347 done:
348   g_list_free_full (layers, gst_object_unref);
349
350   return layer;
351 }
352
353 static gchar *
354 ensure_uri (gchar * location)
355 {
356   if (gst_uri_is_valid (location))
357     return g_strdup (location);
358   else
359     return gst_filename_to_uri (location, NULL);
360 }
361
362 static gboolean
363 get_flags_from_string (GType type, const gchar * str_flags, guint * flags)
364 {
365   GValue value = G_VALUE_INIT;
366   g_value_init (&value, type);
367
368   if (!gst_value_deserialize (&value, str_flags)) {
369     g_value_unset (&value);
370
371     return FALSE;
372   }
373
374   *flags = g_value_get_flags (&value);
375   g_value_unset (&value);
376
377   return TRUE;
378 }
379
380 gboolean
381 _ges_add_clip_from_struct (GESTimeline * timeline, GstStructure * structure,
382     GError ** error)
383 {
384   GESAsset *asset = NULL;
385   GESLayer *layer = NULL;
386   GESClip *clip;
387   gint layer_priority;
388   const gchar *name;
389   const gchar *text;
390   const gchar *pattern;
391   const gchar *track_types_str;
392   const gchar *nested_timeline_id;
393   gchar *asset_id = NULL;
394   gchar *check_asset_id = NULL;
395   const gchar *type_string;
396   GType type;
397   gboolean res = FALSE;
398   GESTrackType track_types = GES_TRACK_TYPE_UNKNOWN;
399
400   GESFrameNumber start_frame = GES_FRAME_NUMBER_NONE, inpoint_frame =
401       GES_FRAME_NUMBER_NONE, duration_frame = GES_FRAME_NUMBER_NONE;
402   GstClockTime duration = 1 * GST_SECOND, inpoint = 0, start =
403       GST_CLOCK_TIME_NONE;
404
405   const gchar *valid_fields[] =
406       { "asset-id", "pattern", "name", "layer-priority", "layer", "type",
407     "start", "inpoint", "duration", "text", "track-types", "project-uri",
408     NULL
409   };
410
411   FieldsError fields_error = { valid_fields, NULL };
412
413   if (!_check_fields (structure, fields_error, error))
414     return FALSE;
415
416   GET_AND_CHECK ("asset-id", G_TYPE_STRING, &check_asset_id, beach);
417
418   TRY_GET_STRING ("pattern", &pattern, NULL);
419   TRY_GET_STRING ("text", &text, NULL);
420   TRY_GET_STRING ("name", &name, NULL);
421   TRY_GET ("layer-priority", G_TYPE_INT, &layer_priority, -1);
422   if (layer_priority == -1)
423     TRY_GET ("layer", G_TYPE_INT, &layer_priority, -1);
424   TRY_GET_STRING ("type", &type_string, "GESUriClip");
425   TRY_GET_TIME ("start", &start, &start_frame, GST_CLOCK_TIME_NONE);
426   TRY_GET_TIME ("inpoint", &inpoint, &inpoint_frame, 0);
427   TRY_GET_TIME ("duration", &duration, &duration_frame, GST_CLOCK_TIME_NONE);
428   TRY_GET_STRING ("track-types", &track_types_str, NULL);
429   TRY_GET_STRING ("project-uri", &nested_timeline_id, NULL);
430
431   if (track_types_str) {
432     if (!get_flags_from_string (GES_TYPE_TRACK_TYPE, track_types_str,
433             &track_types)) {
434       *error =
435           g_error_new (GES_ERROR, 0, "Invalid track types: %s",
436           track_types_str);
437     }
438
439   }
440
441   if (!(type = g_type_from_name (type_string))) {
442     *error = g_error_new (GES_ERROR, 0, "This type doesn't exist : %s",
443         type_string);
444
445     goto beach;
446   }
447
448   if (type == GES_TYPE_URI_CLIP) {
449     asset_id = ensure_uri (check_asset_id);
450   } else {
451     asset_id = g_strdup (check_asset_id);
452   }
453
454   gst_structure_set (structure, "asset-id", G_TYPE_STRING, asset_id, NULL);
455   asset = _ges_get_asset_from_timeline (timeline, type, asset_id, error);
456   if (!asset) {
457     res = FALSE;
458
459     goto beach;
460   }
461
462   if (layer_priority == -1) {
463     GESContainer *container;
464
465     container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
466     if (!container || !GES_IS_CLIP (container))
467       layer = _ges_get_layer_by_priority (timeline, 0);
468     else
469       layer = ges_clip_get_layer (GES_CLIP (container));
470
471     if (!layer)
472       layer = _ges_get_layer_by_priority (timeline, 0);
473   } else {
474     layer = _ges_get_layer_by_priority (timeline, layer_priority);
475   }
476
477   if (!layer) {
478     *error =
479         g_error_new (GES_ERROR, 0, "No layer with priority %d", layer_priority);
480     goto beach;
481   }
482
483   if (GES_FRAME_NUMBER_IS_VALID (start_frame))
484     start = ges_timeline_get_frame_time (timeline, start_frame);
485
486   if (GES_FRAME_NUMBER_IS_VALID (inpoint_frame)) {
487     inpoint =
488         ges_clip_asset_get_frame_time (GES_CLIP_ASSET (asset), inpoint_frame);
489     if (!GST_CLOCK_TIME_IS_VALID (inpoint)) {
490       *error =
491           g_error_new (GES_ERROR, 0, "Could not get inpoint from frame %"
492           G_GINT64_FORMAT, inpoint_frame);
493       goto beach;
494     }
495   }
496
497   if (GES_FRAME_NUMBER_IS_VALID (duration_frame)) {
498     duration = ges_timeline_get_frame_time (timeline, duration_frame);
499   }
500
501   if (GES_IS_URI_CLIP_ASSET (asset) && !GST_CLOCK_TIME_IS_VALID (duration)) {
502     duration = GST_CLOCK_DIFF (inpoint,
503         ges_uri_clip_asset_get_duration (GES_URI_CLIP_ASSET (asset)));
504   }
505
506   clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
507       track_types);
508
509   if (clip) {
510     res = TRUE;
511
512     if (GES_TIMELINE_ELEMENT_DURATION (clip) == 0) {
513       *error = g_error_new (GES_ERROR, 0,
514           "Clip %s has 0 as duration, please provide a proper duration",
515           asset_id);
516       res = FALSE;
517       goto beach;
518     }
519
520
521     if (GES_IS_TEST_CLIP (clip)) {
522       if (pattern) {
523         GEnumClass *enum_class =
524             G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_TEST_PATTERN_TYPE));
525         GEnumValue *value = g_enum_get_value_by_nick (enum_class, pattern);
526
527         if (!value) {
528           res = FALSE;
529           goto beach;
530         }
531
532         ges_test_clip_set_vpattern (GES_TEST_CLIP (clip), value->value);
533         g_type_class_unref (enum_class);
534       }
535     }
536
537     if (GES_IS_TITLE_CLIP (clip) && text)
538       ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (clip),
539           "text", text, NULL);
540
541     if (name
542         && !ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), name)) {
543       res = FALSE;
544       *error =
545           g_error_new (GES_ERROR, 0, "couldn't set name %s on clip with id %s",
546           name, asset_id);
547     }
548   } else {
549     *error =
550         g_error_new (GES_ERROR, 0,
551         "Couldn't add clip with id %s to layer with priority %d", asset_id,
552         layer_priority);
553     res = FALSE;
554     goto beach;
555   }
556
557   if (res) {
558     g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
559     g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
560   }
561
562   res = _ges_save_timeline_if_needed (timeline, structure, error);
563
564 beach:
565   gst_clear_object (&layer);
566   gst_clear_object (&asset);
567   g_free (asset_id);
568   g_free (check_asset_id);
569   return res;
570 }
571
572 gboolean
573 _ges_container_add_child_from_struct (GESTimeline * timeline,
574     GstStructure * structure, GError ** error)
575 {
576   GESAsset *asset = NULL;
577   GESContainer *container;
578   GESTimelineElement *child = NULL;
579   const gchar *container_name, *child_name, *child_type, *id;
580
581   gboolean res = TRUE;
582   const gchar *valid_fields[] = { "container-name", "asset-id", "inpoint",
583     "child-type", "child-name", "project-uri", NULL
584   };
585
586   FieldsError fields_error = { valid_fields, NULL };
587
588   if (!_check_fields (structure, fields_error, error))
589     return FALSE;
590
591   container_name = gst_structure_get_string (structure, "container-name");
592
593   if (container_name == NULL) {
594     container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
595   } else {
596     container =
597         GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
598   }
599
600   if (!GES_IS_CONTAINER (container)) {
601     *error =
602         g_error_new (GES_ERROR, 0, "Could not find container: %s (%p)",
603         container_name, container);
604
605     res = FALSE;
606     goto beach;
607   }
608
609   id = gst_structure_get_string (structure, "asset-id");
610   child_type = gst_structure_get_string (structure, "child-type");
611
612   if (id && child_type) {
613     asset =
614         _ges_get_asset_from_timeline (timeline, g_type_from_name (child_type),
615         id, error);
616
617     if (asset == NULL) {
618       res = FALSE;
619       goto beach;
620     }
621
622     child = GES_TIMELINE_ELEMENT (ges_asset_extract (asset, NULL));
623     if (!GES_IS_TIMELINE_ELEMENT (child)) {
624       *error = g_error_new (GES_ERROR, 0, "Could not extract child element");
625
626       goto beach;
627     }
628   }
629
630   child_name = gst_structure_get_string (structure, "child-name");
631   if (!child && child_name) {
632     child = ges_timeline_get_element (timeline, child_name);
633     if (!GES_IS_TIMELINE_ELEMENT (child)) {
634       *error = g_error_new (GES_ERROR, 0, "Could not find child element");
635
636       goto beach;
637     }
638   }
639
640   if (!child) {
641     *error =
642         g_error_new (GES_ERROR, 0, "Wrong parameters, could not get a child");
643
644     return FALSE;
645   }
646
647   if (child_name)
648     ges_timeline_element_set_name (child, child_name);
649   else
650     child_name = GES_TIMELINE_ELEMENT_NAME (child);
651
652   if (gst_structure_has_field (structure, "inpoint")) {
653     GstClockTime inpoint;
654     GESFrameNumber finpoint;
655
656     if (!GES_IS_TRACK_ELEMENT (child)) {
657       *error = g_error_new (GES_ERROR, 0, "Child %s is not a trackelement"
658           ", can't set inpoint.", child_name);
659
660       gst_object_unref (child);
661
662       goto beach;
663     }
664
665     if (!ges_util_structure_get_clocktime (structure, "inpoint", &inpoint,
666             &finpoint)) {
667       *error = g_error_new (GES_ERROR, 0, "Could not use inpoint.");
668       gst_object_unref (child);
669
670       goto beach;
671     }
672
673     if (!ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child),
674             TRUE)) {
675       *error =
676           g_error_new (GES_ERROR, 0,
677           "Could not set inpoint as %s can't have an internal source",
678           child_name);
679       gst_object_unref (child);
680
681       goto beach;
682     }
683
684     if (GES_FRAME_NUMBER_IS_VALID (finpoint))
685       inpoint = ges_timeline_get_frame_time (timeline, finpoint);
686
687     ges_timeline_element_set_inpoint (child, inpoint);
688
689   }
690
691   res = ges_container_add (container, child);
692   if (res == FALSE) {
693     g_error_new (GES_ERROR, 0, "Could not add child to container");
694   } else {
695     g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, child);
696   }
697   res = _ges_save_timeline_if_needed (timeline, structure, error);
698
699 beach:
700   gst_clear_object (&asset);
701   return res;
702 }
703
704 gboolean
705 _ges_set_child_property_from_struct (GESTimeline * timeline,
706     GstStructure * structure, GError ** error)
707 {
708   const GValue *value;
709   GESTimelineElement *element;
710   const gchar *property_name, *element_name;
711
712   const gchar *valid_fields[] =
713       { "element-name", "property", "value", "project-uri", NULL };
714
715   FieldsError fields_error = { valid_fields, NULL };
716
717   if (!_check_fields (structure, fields_error, error))
718     return FALSE;
719
720   element_name = gst_structure_get_string (structure, "element-name");
721   if (element_name == NULL)
722     element = g_object_get_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA);
723   else
724     element = ges_timeline_get_element (timeline, element_name);
725
726   property_name = gst_structure_get_string (structure, "property");
727   if (property_name == NULL) {
728     const gchar *name = gst_structure_get_name (structure);
729
730     if (g_str_has_prefix (name, "set-"))
731       property_name = &name[4];
732     else {
733       gchar *struct_str = gst_structure_to_string (structure);
734
735       *error =
736           g_error_new (GES_ERROR, 0, "Could not find any property name in %s",
737           struct_str);
738       g_free (struct_str);
739
740       return FALSE;
741     }
742   }
743
744   if (element) {
745     if (!ges_track_element_lookup_child (GES_TRACK_ELEMENT (element),
746             property_name, NULL, NULL))
747       element = NULL;
748   }
749
750   if (!element) {
751     element = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
752
753     if (element == NULL) {
754       *error =
755           g_error_new (GES_ERROR, 0,
756           "Could not find anywhere to set property: %s", property_name);
757
758       return FALSE;
759     }
760   }
761
762   if (!GES_IS_TIMELINE_ELEMENT (element)) {
763     *error =
764         g_error_new (GES_ERROR, 0, "Could not find child %s", element_name);
765
766     return FALSE;
767   }
768
769   value = gst_structure_get_value (structure, "value");
770
771   g_print ("%s Setting %s property to %s\n", element->name, property_name,
772       gst_value_serialize (value));
773
774   if (!ges_timeline_element_set_child_property (element, property_name,
775           (GValue *) value)) {
776     guint n_specs, i;
777     GParamSpec **specs =
778         ges_timeline_element_list_children_properties (element, &n_specs);
779     GString *errstr = g_string_new (NULL);
780
781     g_string_append_printf (errstr,
782         "\n  Could not set property `%s` on `%s`, valid properties:\n",
783         property_name, GES_TIMELINE_ELEMENT_NAME (element));
784
785     for (i = 0; i < n_specs; i++)
786       g_string_append_printf (errstr, "    - %s\n", specs[i]->name);
787     g_free (specs);
788
789     *error = g_error_new_literal (GES_ERROR, 0, errstr->str);
790     g_string_free (errstr, TRUE);
791
792     return FALSE;
793   }
794   return _ges_save_timeline_if_needed (timeline, structure, error);
795 }
796
797 #undef GET_AND_CHECK
798 #undef TRY_GET