753e848aa7f2c1a3b79d9d7808c3d86eb912eb99
[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 #ifdef G_HAVE_ISO_VARARGS
34 #define REPORT_UNLESS(condition, errpoint, ...)                                \
35   G_STMT_START {                                                               \
36     if (!(condition)) {                                                        \
37       gchar *tmp = gst_info_strdup_printf(__VA_ARGS__);                            \
38       *error = g_error_new_literal (GES_ERROR, 0, tmp);                        \
39       g_free (tmp);                                                            \
40       goto errpoint;                                                           \
41     }                                                                          \
42   }                                                                            \
43   G_STMT_END
44 #else /* G_HAVE_GNUC_VARARGS */
45 #ifdef G_HAVE_GNUC_VARARGS
46 #define REPORT_UNLESS(condition, errpoint, args...)                            \
47   G_STMT_START {                                                               \
48     if (!(condition)) {                                                        \
49       gchar *tmp = gst_info_strdup_printf(##args);                            \
50       *error = g_error_new_literal (GES_ERROR, 0, tmp);                        \
51       g_free (tmp);                                                            \
52       goto errpoint;                                                           \
53     }                                                                          \
54   }                                                                            \
55   G_STMT_END
56 #endif /* G_HAVE_ISO_VARARGS */
57 #endif /* G_HAVE_GNUC_VARARGS */
58
59 #define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
60   gboolean found = FALSE; \
61 \
62   if (type == GST_TYPE_CLOCK_TIME) {\
63     found = ges_util_structure_get_clocktime (structure,name, (GstClockTime*)var,NULL);\
64   }\
65   else { \
66     found = gst_structure_get (structure, name, type, var, NULL); \
67   }\
68   if (!found) {\
69     gchar *struct_str = gst_structure_to_string (structure); \
70     *error = g_error_new (GES_ERROR, 0, \
71         "Could not get the mandatory field '%s'" \
72         " of type %s - fields in %s", name, g_type_name (type), struct_str); \
73     g_free (struct_str); \
74     goto label;\
75   } \
76 } G_STMT_END
77
78 #define TRY_GET_STRING(name,var,def) G_STMT_START {\
79   *var = gst_structure_get_string (structure, name); \
80   if (*var == NULL) \
81     *var = def; \
82 } G_STMT_END
83
84 #define TRY_GET_TIME(name, var, var_frames, def) G_STMT_START  {       \
85   if (!ges_util_structure_get_clocktime (structure, name, var, var_frames)) { \
86       *var = def;                                          \
87       *var_frames = GES_FRAME_NUMBER_NONE;                            \
88   }                                                        \
89 } G_STMT_END
90
91 #define TRY_GET(name, type, var, def) G_STMT_START {\
92   g_assert (type != GST_TYPE_CLOCK_TIME);                      \
93   if (!gst_structure_get (structure, name, type, var, NULL))\
94     *var = def;                                             \
95 } G_STMT_END
96
97 typedef struct
98 {
99   const gchar **fields;
100   GList *invalid_fields;
101 } FieldsError;
102
103 static gboolean
104 _check_field (GQuark field_id, const GValue * value, FieldsError * fields_error)
105 {
106   guint i;
107   const gchar *field = g_quark_to_string (field_id);
108
109   for (i = 0; fields_error->fields[i]; i++) {
110     if (g_strcmp0 (fields_error->fields[i], field) == 0) {
111
112       return TRUE;
113     }
114   }
115
116   fields_error->invalid_fields =
117       g_list_append (fields_error->invalid_fields, (gpointer) field);
118
119   return TRUE;
120 }
121
122 static gboolean
123 _check_fields (GstStructure * structure, FieldsError fields_error,
124     GError ** error)
125 {
126   gst_structure_foreach (structure,
127       (GstStructureForeachFunc) _check_field, &fields_error);
128
129   if (fields_error.invalid_fields) {
130     GList *tmp;
131     const gchar *struct_name = gst_structure_get_name (structure);
132     GString *msg = g_string_new (NULL);
133
134     g_string_append_printf (msg, "Unknown propert%s in %s%s:",
135         g_list_length (fields_error.invalid_fields) > 1 ? "ies" : "y",
136         strlen (struct_name) > 1 ? "--" : "-",
137         gst_structure_get_name (structure));
138
139     for (tmp = fields_error.invalid_fields; tmp; tmp = tmp->next)
140       g_string_append_printf (msg, " %s", (gchar *) tmp->data);
141
142     if (error)
143       *error = g_error_new_literal (GES_ERROR, 0, msg->str);
144
145     g_string_free (msg, TRUE);
146
147     return FALSE;
148   }
149
150   return TRUE;
151 }
152
153 static GESTimelineElement *
154 find_element_for_property (GESTimeline * timeline, GstStructure * structure,
155     gchar ** property_name, gboolean require_track_element, GError ** error)
156 {
157   GList *tmp;
158   const gchar *element_name;
159   GESTimelineElement *element;
160
161   element_name = gst_structure_get_string (structure, "element-name");
162   if (element_name == NULL) {
163     element = g_object_get_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA);
164     if (element)
165       gst_object_ref (element);
166   } else {
167     element = ges_timeline_get_element (timeline, element_name);
168   }
169
170   if (*property_name == NULL) {
171     gchar *tmpstr = *property_name;
172     const gchar *name = gst_structure_get_name (structure);
173
174     REPORT_UNLESS (g_str_has_prefix (name, "set-"), err,
175         "Could not find any property name in %" GST_PTR_FORMAT, structure);
176
177     *property_name = g_strdup (&tmpstr[4]);
178
179     g_free (tmpstr);
180   }
181
182   if (element) {
183     if (!ges_timeline_element_lookup_child (element,
184             *property_name, NULL, NULL)) {
185       gst_clear_object (&element);
186     }
187   }
188
189   if (!element) {
190     element = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
191     if (element)
192       gst_object_ref (element);
193   }
194
195   REPORT_UNLESS (GES_IS_TIMELINE_ELEMENT (element), err,
196       "Could not find child %s from %" GST_PTR_FORMAT, element_name, structure);
197
198   if (!require_track_element || GES_IS_TRACK_ELEMENT (element))
199     return element;
200
201
202   REPORT_UNLESS (GES_IS_CONTAINER (element), err,
203       "Could not find child %s from %" GST_PTR_FORMAT, element_name, structure);
204
205   for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
206     if (ges_timeline_element_lookup_child (tmp->data, *property_name, NULL,
207             NULL)) {
208       gst_object_replace ((GstObject **) & element, tmp->data);
209
210       break;
211     }
212   }
213
214   REPORT_UNLESS (GES_IS_TRACK_ELEMENT (element), err,
215       "Could not find TrackElement from %" GST_PTR_FORMAT, structure);
216
217   return element;
218
219 err:
220   g_clear_object (&element);
221   return element;
222 }
223
224 gboolean
225 _ges_save_timeline_if_needed (GESTimeline * timeline, GstStructure * structure,
226     GError ** error)
227 {
228   gboolean res = TRUE;
229   const gchar *nested_timeline_id =
230       gst_structure_get_string (structure, "project-uri");
231
232   if (nested_timeline_id) {
233     res = ges_timeline_save_to_uri (timeline, nested_timeline_id, NULL, TRUE,
234         error);
235   }
236
237   return res;
238 }
239
240 gboolean
241 _ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
242     GstStructure * structure, GError ** error)
243 {
244   GESTimelineElement *element = NULL;
245
246   gboolean absolute;
247   gdouble value;
248   GstClockTime timestamp;
249   GstControlBinding *binding = NULL;
250   GstTimedValueControlSource *source = NULL;
251   gchar *property_name = NULL;
252
253   gboolean ret = FALSE;
254   gboolean setting_value;
255
256   const gchar *valid_fields[] =
257       { "element-name", "property-name", "value", "timestamp", "project-uri",
258     NULL
259   };
260
261   FieldsError fields_error = { valid_fields, NULL };
262
263   if (!_check_fields (structure, fields_error, error))
264     return FALSE;
265
266   GET_AND_CHECK ("property-name", G_TYPE_STRING, &property_name, done);
267   GET_AND_CHECK ("timestamp", GST_TYPE_CLOCK_TIME, &timestamp, done);
268
269   GET_AND_CHECK ("property-name", G_TYPE_STRING, &property_name, done);
270   if (!(element =
271           find_element_for_property (timeline, structure, &property_name, TRUE,
272               error)))
273     goto done;
274
275   REPORT_UNLESS (binding =
276       ges_track_element_get_control_binding (GES_TRACK_ELEMENT (element),
277           property_name),
278       done, "No control binding found for %" GST_PTR_FORMAT, structure);
279
280   g_object_get (binding, "control-source", &source, NULL);
281   REPORT_UNLESS (source, done,
282       "No control source found for '%" GST_PTR_FORMAT
283       "' you should first set-control-binding on it", structure);
284   REPORT_UNLESS (GST_IS_TIMED_VALUE_CONTROL_SOURCE (source), done,
285       "You can use add-keyframe"
286       " only on GstTimedValueControlSource not %s",
287       G_OBJECT_TYPE_NAME (source));
288
289   g_object_get (binding, "absolute", &absolute, NULL);
290   if (absolute) {
291     GParamSpec *pspec;
292     const GValue *v;
293     GValue v2 = G_VALUE_INIT;
294
295     if (!ges_timeline_element_lookup_child (element, property_name, NULL,
296             &pspec)) {
297       *error =
298           g_error_new (GES_ERROR, 0, "Could not get property %s for %s",
299           property_name, GES_TIMELINE_ELEMENT_NAME (element));
300       goto done;
301     }
302
303     v = gst_structure_get_value (structure, "value");
304     if (!v) {
305       gchar *struct_str = gst_structure_to_string (structure);
306
307       *error = g_error_new (GES_ERROR, 0,
308           "Could not get the mandatory field 'value'"
309           " of type %s - fields in %s", g_type_name (pspec->value_type),
310           struct_str);
311       g_free (struct_str);
312       goto done;
313     }
314
315     g_value_init (&v2, G_TYPE_DOUBLE);
316     if (!g_value_transform (v, &v2)) {
317       gchar *struct_str = gst_structure_to_string (structure);
318
319       *error = g_error_new (GES_ERROR, 0,
320           "Could not get the mandatory field 'value'"
321           " of type %s - fields in %s", g_type_name (pspec->value_type),
322           struct_str);
323       g_free (struct_str);
324       goto done;
325     }
326     value = g_value_get_double (&v2);
327     g_value_reset (&v2);
328   } else
329     GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
330
331   setting_value =
332       !g_strcmp0 (gst_structure_get_name (structure), "add-keyframe");
333   if (setting_value)
334     ret = gst_timed_value_control_source_set (source, timestamp, value);
335   else
336     ret = gst_timed_value_control_source_unset (source, timestamp);
337
338   if (!ret) {
339     *error =
340         g_error_new (GES_ERROR, 0,
341         "Could not %sset value for timestamp: %" GST_TIME_FORMAT,
342         setting_value ? "" : "un", GST_TIME_ARGS (timestamp));
343   }
344   ret = _ges_save_timeline_if_needed (timeline, structure, error);
345
346 done:
347   gst_clear_object (&source);
348   gst_clear_object (&element);
349   g_free (property_name);
350
351   return ret;
352
353 }
354
355 GESAsset *
356 _ges_get_asset_from_timeline (GESTimeline * timeline, GType type,
357     const gchar * id, GError ** error)
358 {
359   GESAsset *asset;
360   GESProject *project = ges_timeline_get_project (timeline);
361   GError *err = NULL;
362
363   asset = ges_project_create_asset_sync (project, id, type, &err);
364
365   if (err)
366     g_propagate_error (error, err);
367   if (!asset || (error && *error)) {
368
369     if (error && !*error) {
370       *error = g_error_new (GES_ERROR, 0,
371           "There was an error requesting the asset with id %s and type %s (%s)",
372           id, g_type_name (type), "unknown");
373     }
374
375     GST_ERROR
376         ("There was an error requesting the asset with id %s and type %s (%s)",
377         id, g_type_name (type), error ? (*error)->message : "unknown");
378
379     return NULL;
380   }
381
382   return asset;
383 }
384
385 /* Unref after usage */
386 GESLayer *
387 _ges_get_layer_by_priority (GESTimeline * timeline, gint priority)
388 {
389   GList *layers;
390   gint nlayers;
391   GESLayer *layer = NULL;
392
393   priority = MAX (priority, 0);
394   layers = ges_timeline_get_layers (timeline);
395   nlayers = (gint) g_list_length (layers);
396   if (priority >= nlayers) {
397     gint i = nlayers;
398
399     while (i <= priority) {
400       layer = ges_timeline_append_layer (timeline);
401
402       i++;
403     }
404
405     layer = gst_object_ref (layer);
406
407     goto done;
408   }
409
410   layer = ges_timeline_get_layer (timeline, priority);
411
412 done:
413   g_list_free_full (layers, gst_object_unref);
414
415   return layer;
416 }
417
418 static gchar *
419 ensure_uri (gchar * location)
420 {
421   if (gst_uri_is_valid (location))
422     return g_strdup (location);
423   else
424     return gst_filename_to_uri (location, NULL);
425 }
426
427 static gboolean
428 get_flags_from_string (GType type, const gchar * str_flags, guint * flags)
429 {
430   GValue value = G_VALUE_INIT;
431   g_value_init (&value, type);
432
433   if (!gst_value_deserialize (&value, str_flags)) {
434     g_value_unset (&value);
435
436     return FALSE;
437   }
438
439   *flags = g_value_get_flags (&value);
440   g_value_unset (&value);
441
442   return TRUE;
443 }
444
445 gboolean
446 _ges_add_clip_from_struct (GESTimeline * timeline, GstStructure * structure,
447     GError ** error)
448 {
449   GESAsset *asset = NULL;
450   GESLayer *layer = NULL;
451   GESClip *clip;
452   gint layer_priority;
453   const gchar *name;
454   const gchar *text;
455   const gchar *pattern;
456   const gchar *track_types_str;
457   const gchar *nested_timeline_id;
458   gchar *asset_id = NULL;
459   gchar *check_asset_id = NULL;
460   const gchar *type_string;
461   GType type;
462   gboolean res = FALSE;
463   GESTrackType track_types = GES_TRACK_TYPE_UNKNOWN;
464
465   GESFrameNumber start_frame = GES_FRAME_NUMBER_NONE, inpoint_frame =
466       GES_FRAME_NUMBER_NONE, duration_frame = GES_FRAME_NUMBER_NONE;
467   GstClockTime duration = 1 * GST_SECOND, inpoint = 0, start =
468       GST_CLOCK_TIME_NONE;
469
470   const gchar *valid_fields[] =
471       { "asset-id", "pattern", "name", "layer-priority", "layer", "type",
472     "start", "inpoint", "duration", "text", "track-types", "project-uri",
473     NULL
474   };
475
476   FieldsError fields_error = { valid_fields, NULL };
477
478   if (!_check_fields (structure, fields_error, error))
479     return FALSE;
480
481   GET_AND_CHECK ("asset-id", G_TYPE_STRING, &check_asset_id, beach);
482
483   TRY_GET_STRING ("pattern", &pattern, NULL);
484   TRY_GET_STRING ("text", &text, NULL);
485   TRY_GET_STRING ("name", &name, NULL);
486   TRY_GET ("layer-priority", G_TYPE_INT, &layer_priority, -1);
487   if (layer_priority == -1)
488     TRY_GET ("layer", G_TYPE_INT, &layer_priority, -1);
489   TRY_GET_STRING ("type", &type_string, "GESUriClip");
490   TRY_GET_TIME ("start", &start, &start_frame, GST_CLOCK_TIME_NONE);
491   TRY_GET_TIME ("inpoint", &inpoint, &inpoint_frame, 0);
492   TRY_GET_TIME ("duration", &duration, &duration_frame, GST_CLOCK_TIME_NONE);
493   TRY_GET_STRING ("track-types", &track_types_str, NULL);
494   TRY_GET_STRING ("project-uri", &nested_timeline_id, NULL);
495
496   if (track_types_str) {
497     if (!get_flags_from_string (GES_TYPE_TRACK_TYPE, track_types_str,
498             &track_types)) {
499       *error =
500           g_error_new (GES_ERROR, 0, "Invalid track types: %s",
501           track_types_str);
502     }
503
504   }
505
506   if (!(type = g_type_from_name (type_string))) {
507     *error = g_error_new (GES_ERROR, 0, "This type doesn't exist : %s",
508         type_string);
509
510     goto beach;
511   }
512
513   if (type == GES_TYPE_URI_CLIP) {
514     asset_id = ensure_uri (check_asset_id);
515   } else {
516     asset_id = g_strdup (check_asset_id);
517   }
518
519   gst_structure_set (structure, "asset-id", G_TYPE_STRING, asset_id, NULL);
520   asset = _ges_get_asset_from_timeline (timeline, type, asset_id, error);
521   if (!asset) {
522     res = FALSE;
523
524     goto beach;
525   }
526
527   if (layer_priority == -1) {
528     GESContainer *container;
529
530     container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
531     if (!container || !GES_IS_CLIP (container))
532       layer = _ges_get_layer_by_priority (timeline, 0);
533     else
534       layer = ges_clip_get_layer (GES_CLIP (container));
535
536     if (!layer)
537       layer = _ges_get_layer_by_priority (timeline, 0);
538   } else {
539     layer = _ges_get_layer_by_priority (timeline, layer_priority);
540   }
541
542   if (!layer) {
543     *error =
544         g_error_new (GES_ERROR, 0, "No layer with priority %d", layer_priority);
545     goto beach;
546   }
547
548   if (GES_FRAME_NUMBER_IS_VALID (start_frame))
549     start = ges_timeline_get_frame_time (timeline, start_frame);
550
551   if (GES_FRAME_NUMBER_IS_VALID (inpoint_frame)) {
552     inpoint =
553         ges_clip_asset_get_frame_time (GES_CLIP_ASSET (asset), inpoint_frame);
554     if (!GST_CLOCK_TIME_IS_VALID (inpoint)) {
555       *error =
556           g_error_new (GES_ERROR, 0, "Could not get inpoint from frame %"
557           G_GINT64_FORMAT, inpoint_frame);
558       goto beach;
559     }
560   }
561
562   if (GES_FRAME_NUMBER_IS_VALID (duration_frame)) {
563     duration = ges_timeline_get_frame_time (timeline, duration_frame);
564   }
565
566   if (GES_IS_URI_CLIP_ASSET (asset) && !GST_CLOCK_TIME_IS_VALID (duration)) {
567     duration = GST_CLOCK_DIFF (inpoint,
568         ges_uri_clip_asset_get_duration (GES_URI_CLIP_ASSET (asset)));
569   }
570
571   clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
572       track_types);
573
574   if (clip) {
575     res = TRUE;
576
577     if (GES_TIMELINE_ELEMENT_DURATION (clip) == 0) {
578       *error = g_error_new (GES_ERROR, 0,
579           "Clip %s has 0 as duration, please provide a proper duration",
580           asset_id);
581       res = FALSE;
582       goto beach;
583     }
584
585
586     if (GES_IS_TEST_CLIP (clip)) {
587       if (pattern) {
588         GEnumClass *enum_class =
589             G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_TEST_PATTERN_TYPE));
590         GEnumValue *value = g_enum_get_value_by_nick (enum_class, pattern);
591
592         if (!value) {
593           res = FALSE;
594           goto beach;
595         }
596
597         ges_test_clip_set_vpattern (GES_TEST_CLIP (clip), value->value);
598         g_type_class_unref (enum_class);
599       }
600     }
601
602     if (GES_IS_TITLE_CLIP (clip) && text)
603       ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (clip),
604           "text", text, NULL);
605
606     if (name
607         && !ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), name)) {
608       res = FALSE;
609       *error =
610           g_error_new (GES_ERROR, 0, "couldn't set name %s on clip with id %s",
611           name, asset_id);
612     }
613   } else {
614     *error =
615         g_error_new (GES_ERROR, 0,
616         "Couldn't add clip with id %s to layer with priority %d", asset_id,
617         layer_priority);
618     res = FALSE;
619     goto beach;
620   }
621
622   if (res) {
623     g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
624     g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
625   }
626
627   res = _ges_save_timeline_if_needed (timeline, structure, error);
628
629 beach:
630   gst_clear_object (&layer);
631   gst_clear_object (&asset);
632   g_free (asset_id);
633   g_free (check_asset_id);
634   return res;
635 }
636
637 gboolean
638 _ges_add_track_from_struct (GESTimeline * timeline,
639     GstStructure * structure, GError ** error)
640 {
641   const gchar *ttype;
642   GESTrack *track;
643   GstCaps *caps;
644
645   const gchar *valid_fields[] = { "type", "restrictions", NULL };
646
647   FieldsError fields_error = { valid_fields, NULL };
648
649   if (!_check_fields (structure, fields_error, error))
650     return FALSE;
651
652   ttype = gst_structure_get_string (structure, "type");
653   if (!g_strcmp0 (ttype, "video")) {
654     track = GES_TRACK (ges_video_track_new ());
655   } else if (!g_strcmp0 (ttype, "audio")) {
656     track = GES_TRACK (ges_audio_track_new ());
657   } else {
658     g_set_error (error, GES_ERROR, 0, "Unhandled track type: `%s`", ttype);
659
660     return FALSE;
661   }
662
663   if (gst_structure_has_field (structure, "restrictions")) {
664     GstStructure *restriction_struct;
665     gchar *restriction_str;
666
667     if (gst_structure_get (structure, "restrictions", GST_TYPE_STRUCTURE,
668             &restriction_struct, NULL)) {
669       caps = gst_caps_new_full (restriction_struct, NULL);
670     } else if (gst_structure_get (structure, "restrictions", G_TYPE_STRING,
671             &restriction_str, NULL)) {
672       caps = gst_caps_from_string (restriction_str);
673
674       if (!caps) {
675         g_set_error (error, GES_ERROR, 0, "Invalid restrictions caps: %s",
676             restriction_str);
677
678         g_free (restriction_str);
679         return FALSE;
680       }
681       g_free (restriction_str);
682     } else if (!gst_structure_get (structure, "restrictions", GST_TYPE_CAPS,
683             &caps, NULL)) {
684       gchar *tmp = gst_structure_to_string (structure);
685
686       g_set_error (error, GES_ERROR, 0, "Can't use restrictions caps from %s",
687           tmp);
688
689       g_object_unref (track);
690       return FALSE;
691     }
692
693     ges_track_set_restriction_caps (track, caps);
694     gst_caps_unref (caps);
695   }
696
697   return ges_timeline_add_track (timeline, track);
698 }
699
700 gboolean
701 _ges_container_add_child_from_struct (GESTimeline * timeline,
702     GstStructure * structure, GError ** error)
703 {
704   GESAsset *asset = NULL;
705   GESContainer *container;
706   GESTimelineElement *child = NULL;
707   const gchar *container_name, *child_name, *child_type, *id;
708
709   gboolean res = TRUE;
710   const gchar *valid_fields[] = { "container-name", "asset-id", "inpoint",
711     "child-type", "child-name", "project-uri", NULL
712   };
713
714   FieldsError fields_error = { valid_fields, NULL };
715
716   if (!_check_fields (structure, fields_error, error))
717     return FALSE;
718
719   container_name = gst_structure_get_string (structure, "container-name");
720
721   if (container_name == NULL) {
722     container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
723   } else {
724     container =
725         GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
726   }
727
728   if (!GES_IS_CONTAINER (container)) {
729     *error =
730         g_error_new (GES_ERROR, 0, "Could not find container: %s (%p)",
731         container_name, container);
732
733     res = FALSE;
734     goto beach;
735   }
736
737   id = gst_structure_get_string (structure, "asset-id");
738   child_type = gst_structure_get_string (structure, "child-type");
739
740   if (id && child_type) {
741     asset =
742         _ges_get_asset_from_timeline (timeline, g_type_from_name (child_type),
743         id, error);
744
745     if (asset == NULL) {
746       res = FALSE;
747       goto beach;
748     }
749
750     child = GES_TIMELINE_ELEMENT (ges_asset_extract (asset, NULL));
751     if (!GES_IS_TIMELINE_ELEMENT (child)) {
752       *error = g_error_new (GES_ERROR, 0, "Could not extract child element");
753
754       goto beach;
755     }
756   }
757
758   child_name = gst_structure_get_string (structure, "child-name");
759   if (!child && child_name) {
760     child = ges_timeline_get_element (timeline, child_name);
761     if (!GES_IS_TIMELINE_ELEMENT (child)) {
762       *error = g_error_new (GES_ERROR, 0, "Could not find child element");
763
764       goto beach;
765     }
766   }
767
768   if (!child) {
769     *error =
770         g_error_new (GES_ERROR, 0, "Wrong parameters, could not get a child");
771
772     return FALSE;
773   }
774
775   if (child_name)
776     ges_timeline_element_set_name (child, child_name);
777   else
778     child_name = GES_TIMELINE_ELEMENT_NAME (child);
779
780   if (gst_structure_has_field (structure, "inpoint")) {
781     GstClockTime inpoint;
782     GESFrameNumber finpoint;
783
784     if (!GES_IS_TRACK_ELEMENT (child)) {
785       *error = g_error_new (GES_ERROR, 0, "Child %s is not a trackelement"
786           ", can't set inpoint.", child_name);
787
788       gst_object_unref (child);
789
790       goto beach;
791     }
792
793     if (!ges_util_structure_get_clocktime (structure, "inpoint", &inpoint,
794             &finpoint)) {
795       *error = g_error_new (GES_ERROR, 0, "Could not use inpoint.");
796       gst_object_unref (child);
797
798       goto beach;
799     }
800
801     if (!ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child),
802             TRUE)) {
803       *error =
804           g_error_new (GES_ERROR, 0,
805           "Could not set inpoint as %s can't have an internal source",
806           child_name);
807       gst_object_unref (child);
808
809       goto beach;
810     }
811
812     if (GES_FRAME_NUMBER_IS_VALID (finpoint))
813       inpoint = ges_timeline_get_frame_time (timeline, finpoint);
814
815     ges_timeline_element_set_inpoint (child, inpoint);
816
817   }
818
819   res = ges_container_add (container, child);
820   if (res == FALSE) {
821     g_error_new (GES_ERROR, 0, "Could not add child to container");
822   } else {
823     g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, child);
824   }
825   res = _ges_save_timeline_if_needed (timeline, structure, error);
826
827 beach:
828   gst_clear_object (&asset);
829   return res;
830 }
831
832 gboolean
833 _ges_set_child_property_from_struct (GESTimeline * timeline,
834     GstStructure * structure, GError ** error)
835 {
836   const GValue *value;
837   GValue prop_value = G_VALUE_INIT;
838   gboolean prop_value_set = FALSE;
839   gchar *property_name;
840   GESTimelineElement *element;
841   gchar *serialized;
842   gboolean res;
843
844   const gchar *valid_fields[] =
845       { "element-name", "property", "value", "project-uri", NULL };
846
847   FieldsError fields_error = { valid_fields, NULL };
848
849   if (!_check_fields (structure, fields_error, error))
850     return FALSE;
851
852   GET_AND_CHECK ("property", G_TYPE_STRING, &property_name, err);
853
854   if (!(element =
855           find_element_for_property (timeline, structure, &property_name, FALSE,
856               error)))
857     goto err;
858
859   value = gst_structure_get_value (structure, "value");
860
861   if (G_VALUE_TYPE (value) == G_TYPE_STRING) {
862     GParamSpec *pspec;
863     if (ges_timeline_element_lookup_child (element, property_name, NULL,
864             &pspec)) {
865       GType p_type = pspec->value_type;
866       g_param_spec_unref (pspec);
867       if (p_type != G_TYPE_STRING) {
868         const gchar *val_string = g_value_get_string (value);
869         g_value_init (&prop_value, p_type);
870         if (!gst_value_deserialize (&prop_value, val_string)) {
871           *error = g_error_new (GES_ERROR, 0, "Could not set the property %s "
872               "because the value %s could not be deserialized to the %s type",
873               property_name, val_string, g_type_name (p_type));
874           return FALSE;
875         }
876         prop_value_set = TRUE;
877       }
878     }
879     /* else, let the setter fail below */
880   }
881
882   if (!prop_value_set) {
883     g_value_init (&prop_value, G_VALUE_TYPE (value));
884     g_value_copy (value, &prop_value);
885   }
886
887   serialized = gst_value_serialize (&prop_value);
888   GST_INFO_OBJECT (element, "Setting property %s to %s\n", property_name,
889       serialized);
890   g_free (serialized);
891
892   res = ges_timeline_element_set_child_property (element, property_name,
893       &prop_value);
894   g_value_unset (&prop_value);
895   if (!res) {
896     guint n_specs, i;
897     GParamSpec **specs =
898         ges_timeline_element_list_children_properties (element, &n_specs);
899     GString *errstr = g_string_new (NULL);
900
901     g_string_append_printf (errstr,
902         "\n  Could not set property `%s` on `%s`, valid properties:\n",
903         property_name, GES_TIMELINE_ELEMENT_NAME (element));
904
905     for (i = 0; i < n_specs; i++)
906       g_string_append_printf (errstr, "    - %s\n", specs[i]->name);
907     g_free (specs);
908
909     *error = g_error_new_literal (GES_ERROR, 0, errstr->str);
910     g_string_free (errstr, TRUE);
911
912     return FALSE;
913   }
914   g_free (property_name);
915   return _ges_save_timeline_if_needed (timeline, structure, error);
916
917 err:
918   g_free (property_name);
919   return FALSE;
920 }
921
922 #undef GET_AND_CHECK
923 #undef TRY_GET