gstreamer/ges/ges-structured-interface.c
Tim-Philipp Müller a7347ca8f7 WIP: ges: fix API export/import and 'inconsistent linkage' on MSVC
Export GES library API in headers when we're building the
library itself, otherwise import the API from the headers.

This fixes linker warnings on Windows when building with MSVC.

Fix up some missing config.h includes when building the lib which
is needed to get the export api define from config.h

Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/42
2018-12-15 00:14:51 +00:00

622 lines
16 KiB
C

/* GStreamer Editing Services
*
* Copyright (C) <2015> Thibault Saunier <tsaunier@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ges-structured-interface.h"
#include <string.h>
#define LAST_CONTAINER_QDATA g_quark_from_string("ges-structured-last-container")
#define LAST_CHILD_QDATA g_quark_from_string("ges-structured-last-child")
static gboolean
_get_clocktime (GstStructure * structure, const gchar * name, gpointer var)
{
gboolean found = FALSE;
GstClockTime *val = (GstClockTime *) var;
const GValue *gvalue = gst_structure_get_value (structure, name);
if (gvalue) {
if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME) {
*val = (GstClockTime) g_value_get_uint64 (gvalue);
found = TRUE;
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT64) {
*val = (GstClockTime) g_value_get_uint64 (gvalue);
found = TRUE;
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) {
*val = (GstClockTime) g_value_get_uint (gvalue);
found = TRUE;
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) {
*val = (GstClockTime) g_value_get_int (gvalue);
found = TRUE;
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64) {
*val = (GstClockTime) g_value_get_int64 (gvalue);
found = TRUE;
} else if (G_VALUE_TYPE (gvalue) == G_TYPE_DOUBLE) {
gdouble d = g_value_get_double (gvalue);
found = TRUE;
if (d == -1.0)
*val = GST_CLOCK_TIME_NONE;
else {
*val = d * GST_SECOND;
*val = GST_ROUND_UP_4 (*val);
}
}
}
return found;
}
#define GET_AND_CHECK(name,type,var,label) G_STMT_START {\
gboolean found = FALSE; \
\
if (type == GST_TYPE_CLOCK_TIME) {\
found = _get_clocktime(structure,name,var);\
}\
else { \
found = gst_structure_get (structure, name, type, var, NULL); \
}\
if (!found) {\
gchar *struct_str = gst_structure_to_string (structure); \
*error = g_error_new (GES_ERROR, 0, \
"Could not get the mandatory field '%s'" \
" fields in %s", name, struct_str); \
g_free (struct_str); \
goto label;\
} \
} G_STMT_END
#define TRY_GET_STRING(name,var,def) G_STMT_START {\
*var = gst_structure_get_string (structure, name); \
if (*var == NULL) \
*var = def; \
} G_STMT_END
#define TRY_GET(name,type,var,def) G_STMT_START {\
if (type == GST_TYPE_CLOCK_TIME) {\
if (!_get_clocktime(structure,name,var))\
*var = def; \
} else if (!gst_structure_get (structure, name, type, var, NULL)) {\
*var = def; \
} \
} G_STMT_END
typedef struct
{
const gchar **fields;
GList *invalid_fields;
} FieldsError;
static gboolean
_check_field (GQuark field_id, const GValue * value, FieldsError * fields_error)
{
guint i;
const gchar *field = g_quark_to_string (field_id);
for (i = 0; fields_error->fields[i]; i++) {
if (g_strcmp0 (fields_error->fields[i], field) == 0) {
return TRUE;
}
}
fields_error->invalid_fields =
g_list_append (fields_error->invalid_fields, (gpointer) field);
return TRUE;
}
static gboolean
_check_fields (GstStructure * structure, FieldsError fields_error,
GError ** error)
{
gst_structure_foreach (structure,
(GstStructureForeachFunc) _check_field, &fields_error);
if (fields_error.invalid_fields) {
GList *tmp;
const gchar *struct_name = gst_structure_get_name (structure);
GString *msg = g_string_new (NULL);
g_string_append_printf (msg, "Unknown propert%s in %s%s:",
g_list_length (fields_error.invalid_fields) > 1 ? "ies" : "y",
strlen (struct_name) > 1 ? "--" : "-",
gst_structure_get_name (structure));
for (tmp = fields_error.invalid_fields; tmp; tmp = tmp->next)
g_string_append_printf (msg, " %s", (gchar *) tmp->data);
if (error)
*error = g_error_new_literal (GES_ERROR, 0, msg->str);
GST_ERROR ("%s", msg->str);
g_string_free (msg, TRUE);
return FALSE;
}
return TRUE;
}
gboolean
_ges_add_remove_keyframe_from_struct (GESTimeline * timeline,
GstStructure * structure, GError ** error)
{
GESTrackElement *element;
gdouble value;
GstClockTime timestamp;
GstControlBinding *binding = NULL;
GstTimedValueControlSource *source = NULL;
gchar *element_name = NULL, *property_name = NULL;
gboolean ret = FALSE;
const gchar *valid_fields[] =
{ "element-name", "property-name", "value", "timestamp",
NULL
};
FieldsError fields_error = { valid_fields, NULL };
if (!_check_fields (structure, fields_error, error))
return FALSE;
GET_AND_CHECK ("element-name", G_TYPE_STRING, &element_name, done);
GET_AND_CHECK ("property-name", G_TYPE_STRING, &property_name, done);
GET_AND_CHECK ("value", G_TYPE_DOUBLE, &value, done);
GET_AND_CHECK ("timestamp", GST_TYPE_CLOCK_TIME, &timestamp, done);
element =
GES_TRACK_ELEMENT (ges_timeline_get_element (timeline, element_name));
if (!GES_IS_TRACK_ELEMENT (element)) {
*error =
g_error_new (GES_ERROR, 0, "Could not find TrackElement %s",
element_name);
goto done;
}
binding = ges_track_element_get_control_binding (element, property_name);
if (binding == NULL) {
*error = g_error_new (GES_ERROR, 0, "No control binding found for %s:%s"
" you should first set-control-binding on it",
element_name, property_name);
goto done;
}
g_object_get (binding, "control-source", &source, NULL);
if (source == NULL) {
*error = g_error_new (GES_ERROR, 0, "No control source found for %s:%s"
" you should first set-control-binding on it",
element_name, property_name);
goto done;
}
if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
*error = g_error_new (GES_ERROR, 0, "You can use add-keyframe"
" only on GstTimedValueControlSource not %s",
G_OBJECT_TYPE_NAME (source));
goto done;
}
if (!g_strcmp0 (gst_structure_get_name (structure), "add-keyframe"))
ret = gst_timed_value_control_source_set (source, timestamp, value);
else {
ret = gst_timed_value_control_source_unset (source, timestamp);
if (!ret) {
*error =
g_error_new (GES_ERROR, 0,
"Could not unset value for timestamp: %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
}
}
done:
if (source)
gst_object_unref (source);
g_free (element_name);
g_free (property_name);
return ret;
}
GESAsset *
_ges_get_asset_from_timeline (GESTimeline * timeline, GType type,
const gchar * id, GError ** error)
{
GESAsset *asset;
GESProject *project = ges_timeline_get_project (timeline);
GError *err = NULL;
asset = ges_project_create_asset_sync (project, id, type, &err);
if (err)
g_propagate_error (error, err);
if (!asset || (error && *error)) {
if (error && !*error) {
*error = g_error_new (GES_ERROR, 0,
"There was an error requesting the asset with id %s and type %s (%s)",
id, g_type_name (type), "unknown");
}
GST_ERROR
("There was an error requesting the asset with id %s and type %s (%s)",
id, g_type_name (type), error ? (*error)->message : "unknown");
return NULL;
}
return asset;
}
/* Unref after usage */
GESLayer *
_ges_get_layer_by_priority (GESTimeline * timeline, gint priority)
{
GList *layers;
gint nlayers;
GESLayer *layer = NULL;
priority = MAX (priority, 0);
layers = ges_timeline_get_layers (timeline);
nlayers = (gint) g_list_length (layers);
if (priority >= nlayers) {
gint i = nlayers;
while (i <= priority) {
layer = ges_timeline_append_layer (timeline);
i++;
}
layer = gst_object_ref (layer);
goto done;
}
layer = ges_timeline_get_layer (timeline, priority);
done:
g_list_free_full (layers, gst_object_unref);
return layer;
}
static gchar *
ensure_uri (gchar * location)
{
if (gst_uri_is_valid (location))
return g_strdup (location);
else
return gst_filename_to_uri (location, NULL);
}
gboolean
_ges_add_clip_from_struct (GESTimeline * timeline, GstStructure * structure,
GError ** error)
{
GESAsset *asset;
GESLayer *layer;
GESClip *clip;
gint layer_priority;
const gchar *name;
const gchar *text;
const gchar *pattern;
gchar *asset_id = NULL;
gchar *check_asset_id = NULL;
const gchar *type_string;
GType type;
gboolean res = FALSE;
GstClockTime duration = 1 * GST_SECOND, inpoint = 0, start =
GST_CLOCK_TIME_NONE;
const gchar *valid_fields[] =
{ "asset-id", "pattern", "name", "layer-priority", "layer", "type",
"start", "inpoint", "duration", "text", NULL
};
FieldsError fields_error = { valid_fields, NULL };
if (!_check_fields (structure, fields_error, error))
return FALSE;
GET_AND_CHECK ("asset-id", G_TYPE_STRING, &check_asset_id, beach);
TRY_GET_STRING ("pattern", &pattern, NULL);
TRY_GET_STRING ("text", &text, NULL);
TRY_GET_STRING ("name", &name, NULL);
TRY_GET ("layer-priority", G_TYPE_INT, &layer_priority, -1);
if (layer_priority == -1)
TRY_GET ("layer", G_TYPE_INT, &layer_priority, -1);
TRY_GET_STRING ("type", &type_string, "GESUriClip");
TRY_GET ("start", GST_TYPE_CLOCK_TIME, &start, GST_CLOCK_TIME_NONE);
TRY_GET ("inpoint", GST_TYPE_CLOCK_TIME, &inpoint, 0);
TRY_GET ("duration", GST_TYPE_CLOCK_TIME, &duration, GST_CLOCK_TIME_NONE);
if (!(type = g_type_from_name (type_string))) {
*error = g_error_new (GES_ERROR, 0, "This type doesn't exist : %s",
type_string);
goto beach;
}
if (type == GES_TYPE_URI_CLIP) {
asset_id = ensure_uri (check_asset_id);
} else {
asset_id = g_strdup (check_asset_id);
}
asset = _ges_get_asset_from_timeline (timeline, type, asset_id, error);
if (!asset) {
res = FALSE;
goto beach;
}
if (layer_priority == -1) {
GESContainer *container;
container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
if (!container || !GES_IS_CLIP (container))
layer = _ges_get_layer_by_priority (timeline, 0);
else
layer = ges_clip_get_layer (GES_CLIP (container));
if (!layer)
layer = _ges_get_layer_by_priority (timeline, 0);
} else {
layer = _ges_get_layer_by_priority (timeline, layer_priority);
}
if (!layer) {
*error =
g_error_new (GES_ERROR, 0, "No layer with priority %d", layer_priority);
goto beach;
}
clip = ges_layer_add_asset (layer, asset, start, inpoint, duration,
GES_TRACK_TYPE_UNKNOWN);
if (clip) {
res = TRUE;
if (GES_IS_TEST_CLIP (clip)) {
if (pattern) {
GEnumClass *enum_class =
G_ENUM_CLASS (g_type_class_ref (GES_VIDEO_TEST_PATTERN_TYPE));
GEnumValue *value = g_enum_get_value_by_nick (enum_class, pattern);
if (!value) {
res = FALSE;
goto beach;
}
ges_test_clip_set_vpattern (GES_TEST_CLIP (clip), value->value);
g_type_class_unref (enum_class);
}
}
if (GES_IS_TITLE_CLIP (clip) && text)
ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (clip),
"text", text, NULL);
if (name
&& !ges_timeline_element_set_name (GES_TIMELINE_ELEMENT (clip), name)) {
res = FALSE;
*error =
g_error_new (GES_ERROR, 0, "couldn't set name %s on clip with id %s",
name, asset_id);
}
} else {
*error = g_error_new (GES_ERROR, 0,
"Couldn't add clip with id %s to layer with priority %d", asset_id,
layer_priority);
}
if (res) {
g_object_set_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA, clip);
g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, NULL);
}
gst_object_unref (layer);
beach:
g_free (asset_id);
g_free (check_asset_id);
return res;
}
gboolean
_ges_container_add_child_from_struct (GESTimeline * timeline,
GstStructure * structure, GError ** error)
{
GESAsset *asset;
GESContainer *container;
GESTimelineElement *child = NULL;
const gchar *container_name, *child_name, *child_type, *id;
gboolean res = TRUE;
const gchar *valid_fields[] = { "container-name", "asset-id",
"child-type", "child-name", NULL
};
FieldsError fields_error = { valid_fields, NULL };
if (!_check_fields (structure, fields_error, error))
return FALSE;
container_name = gst_structure_get_string (structure, "container-name");
if (container_name == NULL) {
container = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
} else {
container =
GES_CONTAINER (ges_timeline_get_element (timeline, container_name));
}
g_return_val_if_fail (GES_IS_CONTAINER (container), FALSE);
id = gst_structure_get_string (structure, "asset-id");
child_type = gst_structure_get_string (structure, "child-type");
if (id && child_type) {
asset =
_ges_get_asset_from_timeline (timeline, g_type_from_name (child_type),
id, error);
if (asset == NULL) {
res = FALSE;
goto beach;
}
child = GES_TIMELINE_ELEMENT (ges_asset_extract (asset, NULL));
if (!GES_IS_TIMELINE_ELEMENT (child)) {
g_error_new (GES_ERROR, 0, "Could not extract child element");
goto beach;
}
}
child_name = gst_structure_get_string (structure, "child-name");
if (!child && child_name) {
child = ges_timeline_get_element (timeline, child_name);
if (!GES_IS_TIMELINE_ELEMENT (child)) {
g_error_new (GES_ERROR, 0, "Could not find child element");
goto beach;
}
}
if (!child) {
g_error_new (GES_ERROR, 0, "Wong parametters, could not get a child");
return FALSE;
}
if (child_name)
ges_timeline_element_set_name (child, child_name);
else
child_name = GES_TIMELINE_ELEMENT_NAME (child);
res = ges_container_add (container, child);
if (res == FALSE) {
g_error_new (GES_ERROR, 0, "Could not add child to container");
} else {
g_object_set_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA, child);
}
beach:
return res;
}
gboolean
_ges_set_child_property_from_struct (GESTimeline * timeline,
GstStructure * structure, GError ** error)
{
const GValue *value;
GESTimelineElement *element;
const gchar *property_name, *element_name;
const gchar *valid_fields[] = { "element-name", "property", "value", NULL };
FieldsError fields_error = { valid_fields, NULL };
if (!_check_fields (structure, fields_error, error))
return FALSE;
element_name = gst_structure_get_string (structure, "element-name");
if (element_name == NULL)
element = g_object_get_qdata (G_OBJECT (timeline), LAST_CHILD_QDATA);
else
element = ges_timeline_get_element (timeline, element_name);
property_name = gst_structure_get_string (structure, "property");
if (property_name == NULL) {
const gchar *name = gst_structure_get_name (structure);
if (g_str_has_prefix (name, "set-"))
property_name = &name[4];
else {
gchar *struct_str = gst_structure_to_string (structure);
*error =
g_error_new (GES_ERROR, 0, "Could not find any property name in %s",
struct_str);
g_free (struct_str);
return FALSE;
}
}
if (element) {
if (!ges_track_element_lookup_child (GES_TRACK_ELEMENT (element),
property_name, NULL, NULL))
element = NULL;
}
if (!element) {
element = g_object_get_qdata (G_OBJECT (timeline), LAST_CONTAINER_QDATA);
if (element == NULL) {
*error =
g_error_new (GES_ERROR, 0,
"Could not find anywhere to set property: %s", property_name);
return FALSE;
}
}
if (!GES_IS_TIMELINE_ELEMENT (element)) {
*error =
g_error_new (GES_ERROR, 0, "Could not find child %s", element_name);
return FALSE;
}
value = gst_structure_get_value (structure, "value");
GST_DEBUG ("%s Setting %s property to %p",
element->name, property_name, value);
ges_timeline_element_set_child_property (element, property_name,
(GValue *) value);
return TRUE;
}
#undef GET_AND_CHECK
#undef TRY_GET