1 /* GStreamer Editing Services
3 * Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
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.
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.
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.
24 #include "ges-structured-interface.h"
25 #include "ges-internal.h"
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")
33 #define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
34 gboolean found = FALSE; \
36 if (type == GST_TYPE_CLOCK_TIME) {\
37 found = ges_util_structure_get_clocktime (structure,name, (GstClockTime*)var,NULL);\
40 found = gst_structure_get (structure, name, type, var, NULL); \
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); \
52 #define TRY_GET_STRING(name,var,def) G_STMT_START {\
53 *var = gst_structure_get_string (structure, name); \
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)) { \
61 *var_frames = GES_FRAME_NUMBER_NONE; \
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))\
74 GList *invalid_fields;
78 _check_field (GQuark field_id, const GValue * value, FieldsError * fields_error)
81 const gchar *field = g_quark_to_string (field_id);
83 for (i = 0; fields_error->fields[i]; i++) {
84 if (g_strcmp0 (fields_error->fields[i], field) == 0) {
90 fields_error->invalid_fields =
91 g_list_append (fields_error->invalid_fields, (gpointer) field);
97 _check_fields (GstStructure * structure, FieldsError fields_error,
100 gst_structure_foreach (structure,
101 (GstStructureForeachFunc) _check_field, &fields_error);
103 if (fields_error.invalid_fields) {
105 const gchar *struct_name = gst_structure_get_name (structure);
106 GString *msg = g_string_new (NULL);
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));
113 for (tmp = fields_error.invalid_fields; tmp; tmp = tmp->next)
114 g_string_append_printf (msg, " %s", (gchar *) tmp->data);
117 *error = g_error_new_literal (GES_ERROR, 0, msg->str);
119 g_string_free (msg, TRUE);
128 _ges_save_timeline_if_needed (GESTimeline * timeline, GstStructure * structure,
132 const gchar *nested_timeline_id =
133 gst_structure_get_string (structure, "project-uri");
135 if (nested_timeline_id) {
136 res = ges_timeline_save_to_uri (timeline, nested_timeline_id, NULL, TRUE,
144 _ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
145 GstStructure * structure, GError ** error)
147 GESTrackElement *element;
151 GstClockTime timestamp;
152 GstControlBinding *binding = NULL;
153 GstTimedValueControlSource *source = NULL;
154 gchar *element_name = NULL, *property_name = NULL;
156 gboolean ret = FALSE;
157 gboolean setting_value;
159 const gchar *valid_fields[] =
160 { "element-name", "property-name", "value", "timestamp", "project-uri",
164 FieldsError fields_error = { valid_fields, NULL };
166 if (!_check_fields (structure, fields_error, error))
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, ×tamp, done);
174 (GESTrackElement *) ges_timeline_get_element (timeline, element_name);
176 if (GES_IS_CLIP (element)) {
178 for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
179 if (ges_timeline_element_lookup_child (tmp->data, property_name, NULL,
181 gst_object_replace ((GstObject **) & element, tmp->data);
188 if (!GES_IS_TRACK_ELEMENT (element)) {
190 g_error_new (GES_ERROR, 0, "Could not find TrackElement %s",
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);
205 g_object_get (binding, "control-source", &source, NULL);
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);
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));
223 g_object_get (binding, "absolute", &absolute, NULL);
227 GValue v2 = G_VALUE_INIT;
229 if (!ges_timeline_element_lookup_child (GES_TIMELINE_ELEMENT (element),
230 property_name, NULL, &pspec)) {
232 g_error_new (GES_ERROR, 0, "Could not get property %s for %s",
233 property_name, GES_TIMELINE_ELEMENT_NAME (element));
237 v = gst_structure_get_value (structure, "value");
239 gchar *struct_str = gst_structure_to_string (structure);
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),
249 g_value_init (&v2, G_TYPE_DOUBLE);
250 if (!g_value_transform (v, &v2)) {
251 gchar *struct_str = gst_structure_to_string (structure);
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),
260 value = g_value_get_double (&v2);
263 GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
266 = !g_strcmp0 (gst_structure_get_name (structure), "add-keyframe");
268 ret = gst_timed_value_control_source_set (source, timestamp, value);
270 ret = gst_timed_value_control_source_unset (source, timestamp);
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));
278 ret = _ges_save_timeline_if_needed (timeline, structure, error);
282 gst_object_unref (source);
283 g_free (element_name);
284 g_free (property_name);
291 _ges_get_asset_from_timeline (GESTimeline * timeline, GType type,
292 const gchar * id, GError ** error)
295 GESProject *project = ges_timeline_get_project (timeline);
298 asset = ges_project_create_asset_sync (project, id, type, &err);
301 g_propagate_error (error, err);
302 if (!asset || (error && *error)) {
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");
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");
320 /* Unref after usage */
322 _ges_get_layer_by_priority (GESTimeline * timeline, gint priority)
326 GESLayer *layer = NULL;
328 priority = MAX (priority, 0);
329 layers = ges_timeline_get_layers (timeline);
330 nlayers = (gint) g_list_length (layers);
331 if (priority >= nlayers) {
334 while (i <= priority) {
335 layer = ges_timeline_append_layer (timeline);
340 layer = gst_object_ref (layer);
345 layer = ges_timeline_get_layer (timeline, priority);
348 g_list_free_full (layers, gst_object_unref);
354 ensure_uri (gchar * location)
356 if (gst_uri_is_valid (location))
357 return g_strdup (location);
359 return gst_filename_to_uri (location, NULL);
363 get_flags_from_string (GType type, const gchar * str_flags, guint * flags)
365 GValue value = G_VALUE_INIT;
366 g_value_init (&value, type);
368 if (!gst_value_deserialize (&value, str_flags)) {
369 g_value_unset (&value);
374 *flags = g_value_get_flags (&value);
375 g_value_unset (&value);
381 _ges_add_clip_from_struct (GESTimeline * timeline, GstStructure * structure,
384 GESAsset *asset = NULL;
385 GESLayer *layer = NULL;
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;
397 gboolean res = FALSE;
398 GESTrackType track_types = GES_TRACK_TYPE_UNKNOWN;
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 =
405 const gchar *valid_fields[] =
406 { "asset-id", "pattern", "name", "layer-priority", "layer", "type",
407 "start", "inpoint", "duration", "text", "track-types", "project-uri",
411 FieldsError fields_error = { valid_fields, NULL };
413 if (!_check_fields (structure, fields_error, error))
416 GET_AND_CHECK ("asset-id", G_TYPE_STRING, &check_asset_id, beach);
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);
431 if (track_types_str) {
432 if (!get_flags_from_string (GES_TYPE_TRACK_TYPE, track_types_str,
435 g_error_new (GES_ERROR, 0, "Invalid track types: %s",
441 if (!(type = g_type_from_name (type_string))) {
442 *error = g_error_new (GES_ERROR, 0, "This type doesn't exist : %s",
448 if (type == GES_TYPE_URI_CLIP) {
449 asset_id = ensure_uri (check_asset_id);
451 asset_id = g_strdup (check_asset_id);
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);
462 if (layer_priority == -1) {
463 GESContainer *container;
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);
469 layer = ges_clip_get_layer (GES_CLIP (container));
472 layer = _ges_get_layer_by_priority (timeline, 0);
474 layer = _ges_get_layer_by_priority (timeline, layer_priority);
479 g_error_new (GES_ERROR, 0, "No layer with priority %d", layer_priority);
483 if (GES_FRAME_NUMBER_IS_VALID (start_frame))
484 start = ges_timeline_get_frame_time (timeline, start_frame);
486 if (GES_FRAME_NUMBER_IS_VALID (inpoint_frame)) {
488 ges_clip_asset_get_frame_time (GES_CLIP_ASSET (asset), inpoint_frame);
489 if (!GST_CLOCK_TIME_IS_VALID (inpoint)) {
491 g_error_new (GES_ERROR, 0, "Could not get inpoint from frame %"
492 G_GINT64_FORMAT, inpoint_frame);
497 if (GES_FRAME_NUMBER_IS_VALID (duration_frame)) {
498 duration = ges_timeline_get_frame_time (timeline, duration_frame);
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)));
506 clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
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",
521 if (GES_IS_TEST_CLIP (clip)) {
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);
532 ges_test_clip_set_vpattern (GES_TEST_CLIP (clip), value->value);
533 g_type_class_unref (enum_class);
537 if (GES_IS_TITLE_CLIP (clip) && text)
538 ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (clip),
542 && !ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), name)) {
545 g_error_new (GES_ERROR, 0, "couldn't set name %s on clip with id %s",
550 g_error_new (GES_ERROR, 0,
551 "Couldn't add clip with id %s to layer with priority %d", asset_id,
558 g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
559 g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
562 res = _ges_save_timeline_if_needed (timeline, structure, error);
565 gst_clear_object (&layer);
566 gst_clear_object (&asset);
568 g_free (check_asset_id);
573 _ges_container_add_child_from_struct (GESTimeline * timeline,
574 GstStructure * structure, GError ** error)
576 GESAsset *asset = NULL;
577 GESContainer *container;
578 GESTimelineElement *child = NULL;
579 const gchar *container_name, *child_name, *child_type, *id;
582 const gchar *valid_fields[] = { "container-name", "asset-id", "inpoint",
583 "child-type", "child-name", "project-uri", NULL
586 FieldsError fields_error = { valid_fields, NULL };
588 if (!_check_fields (structure, fields_error, error))
591 container_name = gst_structure_get_string (structure, "container-name");
593 if (container_name == NULL) {
594 container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
597 GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
600 if (!GES_IS_CONTAINER (container)) {
602 g_error_new (GES_ERROR, 0, "Could not find container: %s (%p)",
603 container_name, container);
609 id = gst_structure_get_string (structure, "asset-id");
610 child_type = gst_structure_get_string (structure, "child-type");
612 if (id && child_type) {
614 _ges_get_asset_from_timeline (timeline, g_type_from_name (child_type),
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");
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");
642 g_error_new (GES_ERROR, 0, "Wrong parameters, could not get a child");
648 ges_timeline_element_set_name (child, child_name);
650 child_name = GES_TIMELINE_ELEMENT_NAME (child);
652 if (gst_structure_has_field (structure, "inpoint")) {
653 GstClockTime inpoint;
654 GESFrameNumber finpoint;
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);
660 gst_object_unref (child);
665 if (!ges_util_structure_get_clocktime (structure, "inpoint", &inpoint,
667 *error = g_error_new (GES_ERROR, 0, "Could not use inpoint.");
668 gst_object_unref (child);
673 if (!ges_track_element_set_has_internal_source (GES_TRACK_ELEMENT (child),
676 g_error_new (GES_ERROR, 0,
677 "Could not set inpoint as %s can't have an internal source",
679 gst_object_unref (child);
684 if (GES_FRAME_NUMBER_IS_VALID (finpoint))
685 inpoint = ges_timeline_get_frame_time (timeline, finpoint);
687 ges_timeline_element_set_inpoint (child, inpoint);
691 res = ges_container_add (container, child);
693 g_error_new (GES_ERROR, 0, "Could not add child to container");
695 g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, child);
697 res = _ges_save_timeline_if_needed (timeline, structure, error);
700 gst_clear_object (&asset);
705 _ges_set_child_property_from_struct (GESTimeline * timeline,
706 GstStructure * structure, GError ** error)
709 GESTimelineElement *element;
710 const gchar *property_name, *element_name;
712 const gchar *valid_fields[] =
713 { "element-name", "property", "value", "project-uri", NULL };
715 FieldsError fields_error = { valid_fields, NULL };
717 if (!_check_fields (structure, fields_error, error))
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);
724 element = ges_timeline_get_element (timeline, element_name);
726 property_name = gst_structure_get_string (structure, "property");
727 if (property_name == NULL) {
728 const gchar *name = gst_structure_get_name (structure);
730 if (g_str_has_prefix (name, "set-"))
731 property_name = &name[4];
733 gchar *struct_str = gst_structure_to_string (structure);
736 g_error_new (GES_ERROR, 0, "Could not find any property name in %s",
745 if (!ges_track_element_lookup_child (GES_TRACK_ELEMENT (element),
746 property_name, NULL, NULL))
751 element = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
753 if (element == NULL) {
755 g_error_new (GES_ERROR, 0,
756 "Could not find anywhere to set property: %s", property_name);
762 if (!GES_IS_TIMELINE_ELEMENT (element)) {
764 g_error_new (GES_ERROR, 0, "Could not find child %s", element_name);
769 value = gst_structure_get_value (structure, "value");
771 g_print ("%s Setting %s property to %s\n", element->name, property_name,
772 gst_value_serialize (value));
774 if (!ges_timeline_element_set_child_property (element, property_name,
778 ges_timeline_element_list_children_properties (element, &n_specs);
779 GString *errstr = g_string_new (NULL);
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));
785 for (i = 0; i < n_specs; i++)
786 g_string_append_printf (errstr, " - %s\n", specs[i]->name);
789 *error = g_error_new_literal (GES_ERROR, 0, errstr->str);
790 g_string_free (errstr, TRUE);
794 return _ges_save_timeline_if_needed (timeline, structure, error);