structured: Enhance error message when no clip duration set
[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
26 #include <string.h>
27
28
29 #define LAST_CONTAINER_QDATA g_quark_from_string("ges-structured-last-container")
30 #define LAST_CHILD_QDATA g_quark_from_string("ges-structured-last-child")
31
32 static gboolean
33 _get_clocktime (GstStructure * structure, const gchar * name, gpointer var)
34 {
35   gboolean found = FALSE;
36   GstClockTime *val = (GstClockTime *) var;
37
38   const GValue *gvalue = gst_structure_get_value (structure, name);
39
40   if (gvalue) {
41     if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME) {
42       *val = (GstClockTime) g_value_get_uint64 (gvalue);
43       found = TRUE;
44     } else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT64) {
45       *val = (GstClockTime) g_value_get_uint64 (gvalue);
46       found = TRUE;
47     } else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) {
48       *val = (GstClockTime) g_value_get_uint (gvalue);
49       found = TRUE;
50     } else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) {
51       *val = (GstClockTime) g_value_get_int (gvalue);
52       found = TRUE;
53     } else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64) {
54       *val = (GstClockTime) g_value_get_int64 (gvalue);
55       found = TRUE;
56     } else if (G_VALUE_TYPE (gvalue) == G_TYPE_DOUBLE) {
57       gdouble d = g_value_get_double (gvalue);
58
59       found = TRUE;
60       if (d == -1.0)
61         *val = GST_CLOCK_TIME_NONE;
62       else {
63         *val = d * GST_SECOND;
64         *val = GST_ROUND_UP_4 (*val);
65       }
66     }
67   }
68
69   return found;
70 }
71
72 #define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
73   gboolean found = FALSE; \
74 \
75   if (type == GST_TYPE_CLOCK_TIME) {\
76     found = _get_clocktime(structure,name,var);\
77   }\
78   else { \
79     found = gst_structure_get (structure, name, type, var, NULL); \
80   }\
81   if (!found) {\
82     gchar *struct_str = gst_structure_to_string (structure); \
83     *error = g_error_new (GES_ERROR, 0, \
84         "Could not get the mandatory field '%s'" \
85         " fields in %s", name, struct_str); \
86     g_free (struct_str); \
87     goto label;\
88   } \
89 } G_STMT_END
90
91 #define TRY_GET_STRING(name,var,def) G_STMT_START {\
92   *var = gst_structure_get_string (structure, name); \
93   if (*var == NULL) \
94     *var = def; \
95 } G_STMT_END
96
97 #define TRY_GET(name,type,var,def) G_STMT_START {\
98   if (type == GST_TYPE_CLOCK_TIME) {\
99     if (!_get_clocktime(structure,name,var))\
100       *var = def; \
101   } else if  (!gst_structure_get (structure, name, type, var, NULL)) {\
102     *var = def; \
103   } \
104 } G_STMT_END
105
106 typedef struct
107 {
108   const gchar **fields;
109   GList *invalid_fields;
110 } FieldsError;
111
112 static gboolean
113 _check_field (GQuark field_id, const GValue * value, FieldsError * fields_error)
114 {
115   guint i;
116   const gchar *field = g_quark_to_string (field_id);
117
118   for (i = 0; fields_error->fields[i]; i++) {
119     if (g_strcmp0 (fields_error->fields[i], field) == 0) {
120
121       return TRUE;
122     }
123   }
124
125   fields_error->invalid_fields =
126       g_list_append (fields_error->invalid_fields, (gpointer) field);
127
128   return TRUE;
129 }
130
131 static gboolean
132 _check_fields (GstStructure * structure, FieldsError fields_error,
133     GError ** error)
134 {
135   gst_structure_foreach (structure,
136       (GstStructureForeachFunc) _check_field, &fields_error);
137
138   if (fields_error.invalid_fields) {
139     GList *tmp;
140     const gchar *struct_name = gst_structure_get_name (structure);
141     GString *msg = g_string_new (NULL);
142
143     g_string_append_printf (msg, "Unknown propert%s in %s%s:",
144         g_list_length (fields_error.invalid_fields) > 1 ? "ies" : "y",
145         strlen (struct_name) > 1 ? "--" : "-",
146         gst_structure_get_name (structure));
147
148     for (tmp = fields_error.invalid_fields; tmp; tmp = tmp->next)
149       g_string_append_printf (msg, " %s", (gchar *) tmp->data);
150
151     if (error)
152       *error = g_error_new_literal (GES_ERROR, 0, msg->str);
153     GST_ERROR ("%s", msg->str);
154
155     g_string_free (msg, TRUE);
156
157     return FALSE;
158   }
159
160   return TRUE;
161 }
162
163
164 gboolean
165 _ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
166     GstStructure * structure, GError ** error)
167 {
168   GESTrackElement *element;
169
170   gdouble value;
171   GstClockTime timestamp;
172   GstControlBinding *binding = NULL;
173   GstTimedValueControlSource *source = NULL;
174   gchar *element_name = NULL, *property_name = NULL;
175
176   gboolean ret = FALSE;
177
178   const gchar *valid_fields[] =
179       { "element-name", "property-name", "value", "timestamp",
180     NULL
181   };
182
183   FieldsError fields_error = { valid_fields, NULL };
184
185   if (!_check_fields (structure, fields_error, error))
186     return FALSE;
187
188   GET_AND_CHECK ("element-name", G_TYPE_STRING, &element_name, done);
189   GET_AND_CHECK ("property-name", G_TYPE_STRING, &property_name, done);
190   GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
191   GET_AND_CHECK ("timestamp", GST_TYPE_CLOCK_TIME, &timestamp, done);
192
193   element =
194       GES_TRACK_ELEMENT (ges_timeline_get_element (timeline, element_name));
195
196   if (!GES_IS_TRACK_ELEMENT (element)) {
197     *error =
198         g_error_new (GES_ERROR, 0, "Could not find TrackElement %s",
199         element_name);
200
201     goto done;
202   }
203
204   binding = ges_track_element_get_control_binding (element, property_name);
205   if (binding == NULL) {
206     *error = g_error_new (GES_ERROR, 0, "No control binding found for %s:%s"
207         " you should first set-control-binding on it",
208         element_name, property_name);
209
210     goto done;
211   }
212
213   g_object_get (binding, "control-source", &source, NULL);
214
215   if (source == NULL) {
216     *error = g_error_new (GES_ERROR, 0, "No control source found for %s:%s"
217         " you should first set-control-binding on it",
218         element_name, property_name);
219
220     goto done;
221   }
222
223   if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
224     *error = g_error_new (GES_ERROR, 0, "You can use add-keyframe"
225         " only on GstTimedValueControlSource not %s",
226         G_OBJECT_TYPE_NAME (source));
227
228     goto done;
229   }
230
231   if (!g_strcmp0 (gst_structure_get_name (structure), "add-keyframe"))
232     ret = gst_timed_value_control_source_set (source, timestamp, value);
233   else {
234     ret = gst_timed_value_control_source_unset (source, timestamp);
235
236     if (!ret) {
237       *error =
238           g_error_new (GES_ERROR, 0,
239           "Could not unset value for timestamp: %" GST_TIME_FORMAT,
240           GST_TIME_ARGS (timestamp));
241     }
242   }
243
244 done:
245   if (source)
246     gst_object_unref (source);
247   g_free (element_name);
248   g_free (property_name);
249
250   return ret;
251
252 }
253
254 GESAsset *
255 _ges_get_asset_from_timeline (GESTimeline * timeline, GType type,
256     const gchar * id, GError ** error)
257 {
258   GESAsset *asset;
259   GESProject *project = ges_timeline_get_project (timeline);
260   GError *err = NULL;
261
262   asset = ges_project_create_asset_sync (project, id, type, &err);
263
264   if (err)
265     g_propagate_error (error, err);
266   if (!asset || (error && *error)) {
267
268     if (error && !*error) {
269       *error = g_error_new (GES_ERROR, 0,
270           "There was an error requesting the asset with id %s and type %s (%s)",
271           id, g_type_name (type), "unknown");
272     }
273
274     GST_ERROR
275         ("There was an error requesting the asset with id %s and type %s (%s)",
276         id, g_type_name (type), error ? (*error)->message : "unknown");
277
278     return NULL;
279   }
280
281   return asset;
282 }
283
284 /* Unref after usage */
285 GESLayer *
286 _ges_get_layer_by_priority (GESTimeline * timeline, gint priority)
287 {
288   GList *layers;
289   gint nlayers;
290   GESLayer *layer = NULL;
291
292   priority = MAX (priority, 0);
293   layers = ges_timeline_get_layers (timeline);
294   nlayers = (gint) g_list_length (layers);
295   if (priority >= nlayers) {
296     gint i = nlayers;
297
298     while (i <= priority) {
299       layer = ges_timeline_append_layer (timeline);
300
301       i++;
302     }
303
304     layer = gst_object_ref (layer);
305
306     goto done;
307   }
308
309   layer = ges_timeline_get_layer (timeline, priority);
310
311 done:
312   g_list_free_full (layers, gst_object_unref);
313
314   return layer;
315 }
316
317 static gchar *
318 ensure_uri (gchar * location)
319 {
320   if (gst_uri_is_valid (location))
321     return g_strdup (location);
322   else
323     return gst_filename_to_uri (location, NULL);
324 }
325
326 gboolean
327 _ges_add_clip_from_struct (GESTimeline * timeline, GstStructure * structure,
328     GError ** error)
329 {
330   GESAsset *asset;
331   GESLayer *layer;
332   GESClip *clip;
333   gint layer_priority;
334   const gchar *name;
335   const gchar *text;
336   const gchar *pattern;
337   gchar *asset_id = NULL;
338   gchar *check_asset_id = NULL;
339   const gchar *type_string;
340   GType type;
341   gboolean res = FALSE;
342
343   GstClockTime duration = 1 * GST_SECOND, inpoint = 0, start =
344       GST_CLOCK_TIME_NONE;
345
346   const gchar *valid_fields[] =
347       { "asset-id", "pattern", "name", "layer-priority", "layer", "type",
348     "start", "inpoint", "duration", "text", NULL
349   };
350
351   FieldsError fields_error = { valid_fields, NULL };
352
353   if (!_check_fields (structure, fields_error, error))
354     return FALSE;
355
356   GET_AND_CHECK ("asset-id", G_TYPE_STRING, &check_asset_id, beach);
357
358   TRY_GET_STRING ("pattern", &pattern, NULL);
359   TRY_GET_STRING ("text", &text, NULL);
360   TRY_GET_STRING ("name", &name, NULL);
361   TRY_GET ("layer-priority", G_TYPE_INT, &layer_priority, -1);
362   if (layer_priority == -1)
363     TRY_GET ("layer", G_TYPE_INT, &layer_priority, -1);
364   TRY_GET_STRING ("type", &type_string, "GESUriClip");
365   TRY_GET ("start", GST_TYPE_CLOCK_TIME, &start, GST_CLOCK_TIME_NONE);
366   TRY_GET ("inpoint", GST_TYPE_CLOCK_TIME, &inpoint, 0);
367   TRY_GET ("duration", GST_TYPE_CLOCK_TIME, &duration, GST_CLOCK_TIME_NONE);
368
369   if (!(type = g_type_from_name (type_string))) {
370     *error = g_error_new (GES_ERROR, 0, "This type doesn't exist : %s",
371         type_string);
372
373     goto beach;
374   }
375
376   if (type == GES_TYPE_URI_CLIP) {
377     asset_id = ensure_uri (check_asset_id);
378   } else {
379     asset_id = g_strdup (check_asset_id);
380   }
381
382   asset = _ges_get_asset_from_timeline (timeline, type, asset_id, error);
383   if (!asset) {
384     res = FALSE;
385
386     goto beach;
387   }
388
389   if (layer_priority == -1) {
390     GESContainer *container;
391
392     container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
393     if (!container || !GES_IS_CLIP (container))
394       layer = _ges_get_layer_by_priority (timeline, 0);
395     else
396       layer = ges_clip_get_layer (GES_CLIP (container));
397
398     if (!layer)
399       layer = _ges_get_layer_by_priority (timeline, 0);
400   } else {
401     layer = _ges_get_layer_by_priority (timeline, layer_priority);
402   }
403
404   if (!layer) {
405     *error =
406         g_error_new (GES_ERROR, 0, "No layer with priority %d", layer_priority);
407     goto beach;
408   }
409
410   if (GES_IS_URI_CLIP_ASSET (asset) && !GST_CLOCK_TIME_IS_VALID (duration)) {
411     duration = GST_CLOCK_DIFF (inpoint,
412         ges_uri_clip_asset_get_duration (GES_URI_CLIP_ASSET (asset)));
413   }
414
415   clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
416       GES_TRACK_TYPE_UNKNOWN);
417
418   if (clip) {
419     res = TRUE;
420
421     if (GES_TIMELINE_ELEMENT_DURATION (clip) == 0) {
422       *error = g_error_new (GES_ERROR, 0,
423           "Clip %s has 0 as duration, please provide a proper duration",
424           asset_id);
425       res = FALSE;
426       goto beach;
427     }
428
429
430     if (GES_IS_TEST_CLIP (clip)) {
431       if (pattern) {
432         GEnumClass *enum_class =
433             G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_TEST_PATTERN_TYPE));
434         GEnumValue *value = g_enum_get_value_by_nick (enum_class, pattern);
435
436         if (!value) {
437           res = FALSE;
438           goto beach;
439         }
440
441         ges_test_clip_set_vpattern (GES_TEST_CLIP (clip), value->value);
442         g_type_class_unref (enum_class);
443       }
444     }
445
446     if (GES_IS_TITLE_CLIP (clip) && text)
447       ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (clip),
448           "text", text, NULL);
449
450     if (name
451         && !ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), name)) {
452       res = FALSE;
453       *error =
454           g_error_new (GES_ERROR, 0, "couldn't set name %s on clip with id %s",
455           name, asset_id);
456     }
457   } else {
458     *error = g_error_new (GES_ERROR, 0,
459         "Couldn't add clip with id %s to layer with priority %d", asset_id,
460         layer_priority);
461   }
462
463   if (res) {
464     g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
465     g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
466   }
467
468   gst_object_unref (layer);
469
470 beach:
471   g_free (asset_id);
472   g_free (check_asset_id);
473   return res;
474 }
475
476 gboolean
477 _ges_container_add_child_from_struct (GESTimeline * timeline,
478     GstStructure * structure, GError ** error)
479 {
480   GESAsset *asset;
481   GESContainer *container;
482   GESTimelineElement *child = NULL;
483   const gchar *container_name, *child_name, *child_type, *id;
484
485   gboolean res = TRUE;
486   const gchar *valid_fields[] = { "container-name", "asset-id",
487     "child-type", "child-name", NULL
488   };
489
490   FieldsError fields_error = { valid_fields, NULL };
491
492   if (!_check_fields (structure, fields_error, error))
493     return FALSE;
494
495   container_name = gst_structure_get_string (structure, "container-name");
496
497   if (container_name == NULL) {
498     container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
499   } else {
500     container =
501         GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
502   }
503
504   g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
505
506   id = gst_structure_get_string (structure, "asset-id");
507   child_type = gst_structure_get_string (structure, "child-type");
508
509   if (id && child_type) {
510     asset =
511         _ges_get_asset_from_timeline (timeline, g_type_from_name (child_type),
512         id, error);
513
514     if (asset == NULL) {
515       res = FALSE;
516       goto beach;
517     }
518
519     child = GES_TIMELINE_ELEMENT (ges_asset_extract (asset, NULL));
520     if (!GES_IS_TIMELINE_ELEMENT (child)) {
521       g_error_new (GES_ERROR, 0, "Could not extract child element");
522
523       goto beach;
524     }
525   }
526
527   child_name = gst_structure_get_string (structure, "child-name");
528   if (!child && child_name) {
529     child = ges_timeline_get_element (timeline, child_name);
530     if (!GES_IS_TIMELINE_ELEMENT (child)) {
531       g_error_new (GES_ERROR, 0, "Could not find child element");
532
533       goto beach;
534     }
535   }
536
537   if (!child) {
538     g_error_new (GES_ERROR, 0, "Wong parametters, could not get a child");
539
540     return FALSE;
541   }
542
543   if (child_name)
544     ges_timeline_element_set_name (child, child_name);
545   else
546     child_name = GES_TIMELINE_ELEMENT_NAME (child);
547
548   res = ges_container_add (container, child);
549   if (res == FALSE) {
550     g_error_new (GES_ERROR, 0, "Could not add child to container");
551   } else {
552     g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, child);
553   }
554
555 beach:
556   return res;
557 }
558
559 gboolean
560 _ges_set_child_property_from_struct (GESTimeline * timeline,
561     GstStructure * structure, GError ** error)
562 {
563   const GValue *value;
564   GESTimelineElement *element;
565   const gchar *property_name, *element_name;
566
567   const gchar *valid_fields[] = { "element-name", "property", "value", NULL };
568
569   FieldsError fields_error = { valid_fields, NULL };
570
571   if (!_check_fields (structure, fields_error, error))
572     return FALSE;
573
574   element_name = gst_structure_get_string (structure, "element-name");
575   if (element_name == NULL)
576     element = g_object_get_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA);
577   else
578     element = ges_timeline_get_element (timeline, element_name);
579
580   property_name = gst_structure_get_string (structure, "property");
581   if (property_name == NULL) {
582     const gchar *name = gst_structure_get_name (structure);
583
584     if (g_str_has_prefix (name, "set-"))
585       property_name = &name[4];
586     else {
587       gchar *struct_str = gst_structure_to_string (structure);
588
589       *error =
590           g_error_new (GES_ERROR, 0, "Could not find any property name in %s",
591           struct_str);
592       g_free (struct_str);
593
594       return FALSE;
595     }
596   }
597
598   if (element) {
599     if (!ges_track_element_lookup_child (GES_TRACK_ELEMENT (element),
600             property_name, NULL, NULL))
601       element = NULL;
602   }
603
604   if (!element) {
605     element = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
606
607     if (element == NULL) {
608       *error =
609           g_error_new (GES_ERROR, 0,
610           "Could not find anywhere to set property: %s", property_name);
611
612       return FALSE;
613     }
614   }
615
616   if (!GES_IS_TIMELINE_ELEMENT (element)) {
617     *error =
618         g_error_new (GES_ERROR, 0, "Could not find child %s", element_name);
619
620     return FALSE;
621   }
622
623   value = gst_structure_get_value (structure, "value");
624
625   GST_DEBUG ("%s Setting %s property to %p",
626       element->name, property_name, value);
627
628   ges_timeline_element_set_child_property (element, property_name,
629       (GValue *) value);
630
631   return TRUE;
632 }
633
634 #undef GET_AND_CHECK
635 #undef TRY_GET