From 68288a76f716a260053bd9a710970213badcaad8 Mon Sep 17 00:00:00 2001 From: Millan Castro Date: Thu, 22 Aug 2019 18:50:00 +0200 Subject: [PATCH] markerlist: implement GESMarkerList Co-authored by Mathieu Duponchelle --- ges/Makefile.am | 2 + ges/ges-internal.h | 8 + ges/ges-marker-list.c | 480 +++++++++++++++++++++++++++++++++++++++++++ ges/ges-marker-list.h | 60 ++++++ ges/ges-meta-container.c | 79 ++++++- ges/ges-meta-container.h | 9 + ges/ges-types.h | 4 + ges/ges.c | 13 ++ ges/ges.h | 1 + ges/meson.build | 4 +- tests/check/ges/layer.c | 65 ++++++ tests/check/ges/markerlist.c | 365 ++++++++++++++++++++++++++++++++ tests/check/meson.build | 1 + 13 files changed, 1088 insertions(+), 3 deletions(-) create mode 100644 ges/ges-marker-list.c create mode 100644 ges/ges-marker-list.h create mode 100644 tests/check/ges/markerlist.c diff --git a/ges/Makefile.am b/ges/Makefile.am index a79a73a..ba6d7bb 100644 --- a/ges/Makefile.am +++ b/ges/Makefile.am @@ -78,6 +78,7 @@ libges_@GST_API_VERSION@_la_SOURCES = \ ges-validate.c \ ges-structured-interface.c \ ges-structure-parser.c \ + ges-marker-list.c \ gstframepositioner.c libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/ @@ -142,6 +143,7 @@ libges_@GST_API_VERSION@include_HEADERS = \ ges-effect-asset.h \ ges-utils.h \ ges-group.h \ + ges-marker-list.h \ ges-version.h noinst_HEADERS = \ diff --git a/ges/ges-internal.h b/ges/ges-internal.h index c3ba7dc..96706e4 100644 --- a/ges/ges-internal.h +++ b/ges/ges-internal.h @@ -450,6 +450,14 @@ G_GNUC_INTERNAL GESMultiFileURI * ges_multi_file_uri_new (const gchar * uri); ************************/ #define GES_PARAM_NO_SERIALIZATION (1 << (G_PARAM_USER_SHIFT + 1)) +/******************************* + * GESMarkerList serialization * + *******************************/ + + +G_GNUC_INTERNAL gchar * ges_marker_list_serialize (const GValue * v); +G_GNUC_INTERNAL gboolean ges_marker_list_deserialize (GValue *dest, const gchar *s); + /******************** * Gnonlin helpers * ********************/ diff --git a/ges/ges-marker-list.c b/ges/ges-marker-list.c new file mode 100644 index 0000000..d0dde9c --- /dev/null +++ b/ges/ges-marker-list.c @@ -0,0 +1,480 @@ +/* GStreamer Editing Services + + * Copyright (C) <2019> Mathieu Duponchelle + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION: gesmarkerlist + * @title: GESMarkerList + * @short_description: implements a list of markers with metadata asociated to time positions + * @see_also: #GESMarker + * + * Since: 1.18 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ges-marker-list.h" +#include "ges.h" +#include "ges-internal.h" +#include "ges-meta-container.h" + +/* GESMarker */ + +static void ges_meta_container_interface_init (GESMetaContainerInterface * + iface); + +struct _GESMarker +{ + GObject parent; + GstClockTime position; +}; + +G_DEFINE_TYPE_WITH_CODE (GESMarker, ges_marker, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, + ges_meta_container_interface_init)); + +enum +{ + PROP_0, + PROP_POSITION, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +/* GObject Standard vmethods*/ +static void +ges_marker_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + GESMarker *marker = GES_MARKER (object); + + switch (property_id) { + case PROP_POSITION: + g_value_set_uint64 (value, marker->position); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +ges_marker_init (GESMarker * self) +{ +} + +static void +ges_marker_class_init (GESMarkerClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = ges_marker_get_property; + /** + * GESMarker:position: + * + * Current position (in nanoseconds) of the #GESMarker + */ + properties[PROP_POSITION] = + g_param_spec_uint64 ("position", "Position", + "The position of the marker", 0, G_MAXUINT64, + GST_CLOCK_TIME_NONE, G_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_POSITION, + properties[PROP_POSITION]); + +} + +static void +ges_meta_container_interface_init (GESMetaContainerInterface * iface) +{ +} + +/* GESMarkerList */ + +struct _GESMarkerList +{ + GObject parent; + + GSequence *markers; + GHashTable *markers_iters; +}; + +enum +{ + MARKER_ADDED, + MARKER_REMOVED, + MARKER_MOVED, + LAST_SIGNAL +}; + +static guint ges_marker_list_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GESMarkerList, ges_marker_list, G_TYPE_OBJECT); + +static void +remove_marker (gpointer data) +{ + GESMarker *marker = (GESMarker *) data; + + g_object_unref (marker); +} + +static void +ges_marker_list_init (GESMarkerList * self) +{ + self->markers = g_sequence_new (remove_marker); + self->markers_iters = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +ges_marker_list_finalize (GObject * object) +{ + GESMarkerList *self = GES_MARKER_LIST (object); + + g_sequence_free (self->markers); + g_hash_table_unref (self->markers_iters); + + G_OBJECT_CLASS (ges_marker_list_parent_class)->finalize (object); +} + +static void +ges_marker_list_class_init (GESMarkerListClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = ges_marker_list_finalize; + + /** + * GESMarkerList::marker-added: + * @marker-list: the #GESMarkerList + * @marker: the #GESMarker that was added. + * + * Will be emitted after the marker was added to the marker-list. + * Since: 1.18 + */ + ges_marker_list_signals[MARKER_ADDED] = + g_signal_new ("marker-added", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_NONE, 2, G_TYPE_UINT64, GES_TYPE_MARKER); + +/** + * GESMarkerList::marker-removed: + * @marker-list: the #GESMarkerList + * @marker: the #GESMarker that was removed. + * + * Will be emitted after the marker was removed the marker-list. + * Since: 1.18 + */ + ges_marker_list_signals[MARKER_REMOVED] = + g_signal_new ("marker-removed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_NONE, 1, GES_TYPE_MARKER); + +/** + * GESMarkerList::marker-moved: + * @marker-list: the #GESMarkerList + * @marker: the #GESMarker that was added. + * + * Will be emitted after the marker was moved to. + * Since: 1.18 + */ + ges_marker_list_signals[MARKER_MOVED] = + g_signal_new ("marker-moved", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_NONE, 2, G_TYPE_UINT64, GES_TYPE_MARKER); +} + +static gint +cmp_marker (gconstpointer a, gconstpointer b, G_GNUC_UNUSED gpointer data) +{ + GESMarker *marker_a = (GESMarker *) a; + GESMarker *marker_b = (GESMarker *) b; + + if (marker_a->position < marker_b->position) + return -1; + else if (marker_a->position == marker_b->position) + return 0; + else + return 1; +} + +/** + * ges_marker_list_new: + * + * Creates a new #GESMarkerList. + + * Returns: A new #GESMarkerList + * Since: 1.18 + */ +GESMarkerList * +ges_marker_list_new (void) +{ + GESMarkerList *ret; + + ret = (GESMarkerList *) g_object_new (GES_TYPE_MARKER_LIST, NULL); + + return ret; +} + +/** + * ges_marker_list_add: + * @position: The position of the new marker + * + * Returns: (transfer none): The newly-added marker, the list keeps ownership + * of the marker + * Since: 1.18 + */ +GESMarker * +ges_marker_list_add (GESMarkerList * self, GstClockTime position) +{ + GESMarker *ret; + GSequenceIter *iter; + + g_return_val_if_fail (GES_IS_MARKER_LIST (self), NULL); + + ret = (GESMarker *) g_object_new (GES_TYPE_MARKER, NULL); + + ret->position = position; + + iter = g_sequence_insert_sorted (self->markers, ret, cmp_marker, NULL); + + g_hash_table_insert (self->markers_iters, ret, iter); + + g_signal_emit (self, ges_marker_list_signals[MARKER_ADDED], 0, position, ret); + + return ret; +} + +/** + * ges_marker_list_size: + * + * Returns: The number of markers in @list + * Since: 1.18 + */ +guint +ges_marker_list_size (GESMarkerList * self) +{ + g_return_val_if_fail (GES_IS_MARKER_LIST (self), 0); + + return g_sequence_get_length (self->markers); +} + +/** + * ges_marker_list_remove: + * + * Removes @marker from @list, this decreases the refcount of the + * marker by 1. + * + * Returns: %TRUE if the marker could be removed, %FALSE otherwise + * (if the marker was not present in the list for example) + * Since: 1.18 + */ +gboolean +ges_marker_list_remove (GESMarkerList * self, GESMarker * marker) +{ + GSequenceIter *iter; + gboolean ret = FALSE; + + g_return_val_if_fail (GES_IS_MARKER_LIST (self), FALSE); + + if (!g_hash_table_lookup_extended (self->markers_iters, + marker, NULL, (gpointer *) & iter)) + goto done; + g_assert (iter != NULL); + g_hash_table_remove (self->markers_iters, marker); + + g_signal_emit (self, ges_marker_list_signals[MARKER_REMOVED], 0, marker); + + g_sequence_remove (iter); + + ret = TRUE; + +done: + return ret; +} + + +/** + * ges_marker_list_get_markers: + * + * Returns: (element-type GESMarker) (transfer full): a #GList + * of the #GESMarker within the GESMarkerList. The user will have + * to unref each #GESMarker and free the #GList. + * + * Since: 1.18 + */ +GList * +ges_marker_list_get_markers (GESMarkerList * self) +{ + GESMarker *marker; + GSequenceIter *iter; + GList *ret; + + g_return_val_if_fail (GES_IS_MARKER_LIST (self), NULL); + ret = NULL; + + for (iter = g_sequence_get_begin_iter (self->markers); + !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) { + marker = GES_MARKER (g_sequence_get (iter)); + + ret = g_list_append (ret, g_object_ref (marker)); + } + + return ret; +} + + +/** + * ges_marker_list_move: + * + * Moves a @marker in a @list to a new @position + * + * Returns: %TRUE if the marker could be moved, %FALSE otherwise + * (if the marker was not present in the list for example) + * + * Since: 1.18 + */ +gboolean +ges_marker_list_move (GESMarkerList * self, GESMarker * marker, + GstClockTime position) +{ + GSequenceIter *iter; + gboolean ret = FALSE; + + g_return_val_if_fail (GES_IS_MARKER_LIST (self), FALSE); + + if (!g_hash_table_lookup_extended (self->markers_iters, + marker, NULL, (gpointer *) & iter)) { + GST_WARNING ("GESMarkerList doesn't contain GESMarker"); + goto done; + } + + marker->position = position; + + g_signal_emit (self, ges_marker_list_signals[MARKER_MOVED], 0, position, + marker); + + g_sequence_sort_changed (iter, cmp_marker, NULL); + + ret = TRUE; + +done: + return ret; +} + +gboolean +ges_marker_list_deserialize (GValue * dest, const gchar * s) +{ + gboolean ret = FALSE; + GstCaps *caps = NULL; + GESMarkerList *list = ges_marker_list_new (); + guint i, l; + + caps = gst_caps_from_string (s); + + l = gst_caps_get_size (caps); + + if (l % 2) { + GST_ERROR ("Failed deserializing marker list: expected evenly-sized caps"); + goto done; + } + + for (i = 0; i < l - 1; i += 2) { + const GstStructure *pos_s = gst_caps_get_structure (caps, i); + const GstStructure *meta_s = gst_caps_get_structure (caps, i + 1); + GstClockTime position; + GESMarker *marker; + gchar *metas; + + if (!gst_structure_has_name (pos_s, "marker-times")) { + GST_ERROR_OBJECT (dest, + "Failed deserializing marker list: unexpected structure %" + GST_PTR_FORMAT, pos_s); + goto done; + } + + if (!gst_structure_get_uint64 (pos_s, "position", &position)) { + GST_ERROR_OBJECT (dest, + "Failed deserializing marker list: unexpected structure %" + GST_PTR_FORMAT, pos_s); + goto done; + } + + marker = ges_marker_list_add (list, position); + + metas = gst_structure_to_string (meta_s); + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (marker), + metas); + g_free (metas); + + } + + ret = TRUE; + +done: + if (caps) + gst_caps_unref (caps); + + if (!ret) + g_object_unref (list); + else + g_value_take_object (dest, list); + + return ret; +} + +gchar * +ges_marker_list_serialize (const GValue * v) +{ + GESMarkerList *list = GES_MARKER_LIST (g_value_get_object (v)); + GSequenceIter *iter; + GstCaps *caps = gst_caps_new_empty (); + gchar *caps_str, *escaped, *res; + + iter = g_sequence_get_begin_iter (list->markers); + + while (!g_sequence_iter_is_end (iter)) { + GESMarker *marker = (GESMarker *) g_sequence_get (iter); + GstStructure *s; + gchar *metas; + + metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (marker)); + + s = gst_structure_new ("marker-times", "position", G_TYPE_UINT64, + marker->position, NULL); + gst_caps_append_structure (caps, s); + s = gst_structure_from_string (metas, NULL); + gst_caps_append_structure (caps, s); + + g_free (metas); + + iter = g_sequence_iter_next (iter); + } + + caps_str = gst_caps_to_string (caps); + escaped = g_strescape (caps_str, NULL); + g_free (caps_str); + res = g_strdup_printf ("\"%s\"", escaped); + g_free (escaped); + gst_caps_unref (caps); + + return res; +} diff --git a/ges/ges-marker-list.h b/ges/ges-marker-list.h new file mode 100644 index 0000000..1586a22 --- /dev/null +++ b/ges/ges-marker-list.h @@ -0,0 +1,60 @@ +/* GStreamer Editing Services + + * Copyright (C) <2019> Mathieu Duponchelle + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GES_MARKER_LIST +#define _GES_MARKER_LIST + +#include +#include + +G_BEGIN_DECLS + +#define GES_TYPE_MARKER ges_marker_get_type () + +GES_API +G_DECLARE_FINAL_TYPE (GESMarker, ges_marker, GES, MARKER, GObject) + +#define GES_TYPE_MARKER_LIST ges_marker_list_get_type () + +GES_API +G_DECLARE_FINAL_TYPE (GESMarkerList, ges_marker_list, GES, MARKER_LIST, GObject) + +GES_API +GESMarkerList * ges_marker_list_new (void); + +GES_API +GESMarker * ges_marker_list_add (GESMarkerList * list, GstClockTime position); + +GES_API +gboolean ges_marker_list_remove (GESMarkerList * list, GESMarker *marker); + +GES_API +guint ges_marker_list_size (GESMarkerList * list); + + +GES_API +GList *ges_marker_list_get_markers (GESMarkerList *list); + +GES_API +gboolean ges_marker_list_move (GESMarkerList *list, GESMarker *marker, GstClockTime position); + +G_END_DECLS + +#endif /* _GES_MARKER_LIST */ diff --git a/ges/ges-meta-container.c b/ges/ges-meta-container.c index 4c37e27..722d5cd 100644 --- a/ges/ges-meta-container.c +++ b/ges/ges-meta-container.c @@ -25,6 +25,7 @@ #include #include "ges-meta-container.h" +#include "ges-marker-list.h" /** * SECTION: gesmetacontainer @@ -37,8 +38,7 @@ static GQuark ges_meta_key; G_DEFINE_INTERFACE_WITH_CODE (GESMetaContainer, ges_meta_container, G_TYPE_OBJECT, ges_meta_key = - g_quark_from_static_string ("ges-meta-container-data"); - ); + g_quark_from_static_string ("ges-meta-container-data");); enum { @@ -435,6 +435,49 @@ ges_meta_container_set_meta (GESMetaContainer * container, } /** + * ges_meta_container_set_marker_list: + * @container: Target container + * @meta_item: Name of the meta item to set + * @list: (allow-none) (transfer none): List to set + * + * Associates a marker list with the given meta item + * + * Return: %TRUE if the meta could be added, %FALSE otherwise + * Since: 1.18 + */ +gboolean +ges_meta_container_set_marker_list (GESMetaContainer * container, + const gchar * meta_item, const GESMarkerList * list) +{ + gboolean ret; + GValue v = G_VALUE_INIT; + g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE); + g_return_val_if_fail (meta_item != NULL, FALSE); + + if (list == NULL) { + GstStructure *structure = _meta_container_get_structure (container); + gst_structure_remove_field (structure, meta_item); + + g_signal_emit (container, _signals[NOTIFY_SIGNAL], 0, meta_item, list); + + return TRUE; + } + + g_return_val_if_fail (GES_IS_MARKER_LIST ((gpointer) list), FALSE); + + if (_can_write_value (container, meta_item, GES_TYPE_MARKER_LIST) == FALSE) + return FALSE; + + g_value_init_from_instance (&v, (gpointer) list); + + ret = _set_value (container, meta_item, &v); + + g_value_unset (&v); + + return ret; +} + +/** * ges_meta_container_metas_to_string: * @container: a #GESMetaContainer * @@ -915,6 +958,38 @@ ges_meta_container_get_meta (GESMetaContainer * container, const gchar * key) } /** + * ges_meta_container_get_marker_list: + * @container: Target container + * @key: The key name of the list to retrieve + * + * Gets the value of a given meta item, returns NULL if @key + * can not be found. + * + * Returns: (transfer full): the #GESMarkerList corresponding to the meta with the given @key. + * Since: 1.18 + */ +GESMarkerList * +ges_meta_container_get_marker_list (GESMetaContainer * container, + const gchar * key) +{ + GstStructure *structure; + const GValue *v; + + g_return_val_if_fail (GES_IS_META_CONTAINER (container), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + + structure = _meta_container_get_structure (container); + + v = gst_structure_get_value (structure, key); + + if (v == NULL) { + return NULL; + } + + return GES_MARKER_LIST (g_value_dup_object (v)); +} + +/** * ges_meta_container_get_date: * @container: Target container * @meta_item: Name of the meta item to get diff --git a/ges/ges-meta-container.h b/ges/ges-meta-container.h index b801e07..893b8e8 100644 --- a/ges/ges-meta-container.h +++ b/ges/ges-meta-container.h @@ -177,6 +177,11 @@ ges_meta_container_set_meta (GESMetaContainer * container, const GValue *value); GES_API gboolean +ges_meta_container_set_marker_list (GESMetaContainer * container, + const gchar * meta_item, + const GESMarkerList *list); + +GES_API gboolean ges_meta_container_register_meta_boolean (GESMetaContainer *container, GESMetaFlag flags, const gchar* meta_item, @@ -297,6 +302,10 @@ GES_API const gchar * ges_meta_container_get_string (GESMetaContainer * container, const gchar * meta_item); +GES_API GESMarkerList * +ges_meta_container_get_marker_list (GESMetaContainer * container, + const gchar * key); + GES_API const GValue * ges_meta_container_get_meta (GESMetaContainer * container, const gchar * key); diff --git a/ges/ges-types.h b/ges/ges-types.h index 6d6f468..d93f131 100644 --- a/ges/ges-types.h +++ b/ges/ges-types.h @@ -188,6 +188,10 @@ typedef struct _GESVideoTrack GESVideoTrack; typedef struct _GESAudioTrackClass GESAudioTrackClass; typedef struct _GESAudioTrack GESAudioTrack; +typedef struct _GESMarkerList GESMarkerList; + +typedef struct _GESMarker GESMarker; + G_END_DECLS #endif /* __GES_TYPES_H__ */ diff --git a/ges/ges.c b/ges/ges.c index 2896ac6..4e76ba4 100644 --- a/ges/ges.c +++ b/ges/ges.c @@ -75,6 +75,13 @@ ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data, { GESUriClipAssetClass *uriasset_klass = NULL; GstElementFactory *nlecomposition_factory = NULL; + static GstValueTable gstvtable = { + G_TYPE_NONE, + (GstValueCompareFunc) NULL, + (GstValueSerializeFunc) ges_marker_list_serialize, + (GstValueDeserializeFunc) ges_marker_list_deserialize + }; + static gboolean marker_list_registered = FALSE; if (initialized_thread) { GST_DEBUG ("already initialized ges"); @@ -123,6 +130,12 @@ ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data, initialized_thread = g_thread_self (); g_type_class_unref (uriasset_klass); + if (!marker_list_registered) { + gstvtable.type = GES_TYPE_MARKER_LIST; + gst_value_register (&gstvtable); + marker_list_registered = TRUE; + } + GST_DEBUG ("GStreamer Editing Services initialized"); return TRUE; diff --git a/ges/ges.h b/ges/ges.h index 1a01bf9..6e82fbe 100644 --- a/ges/ges.h +++ b/ges/ges.h @@ -83,6 +83,7 @@ #include #include #include +#include G_BEGIN_DECLS diff --git a/ges/meson.build b/ges/meson.build index 1bb6eb5..bb76018 100644 --- a/ges/meson.build +++ b/ges/meson.build @@ -61,6 +61,7 @@ ges_sources = files([ 'ges-validate.c', 'ges-structured-interface.c', 'ges-structure-parser.c', + 'ges-marker-list.c', 'gstframepositioner.c' ]) @@ -122,7 +123,8 @@ ges_headers = files([ 'ges-container.h', 'ges-effect-asset.h', 'ges-utils.h', - 'ges-group.h' + 'ges-group.h', + 'ges-marker-list.h' ]) if libxml_dep.found() diff --git a/tests/check/ges/layer.c b/tests/check/ges/layer.c index c2ab229..f592c5b 100644 --- a/tests/check/ges/layer.c +++ b/tests/check/ges/layer.c @@ -1462,6 +1462,70 @@ GST_START_TEST (test_layer_meta_value) GST_END_TEST; + +GST_START_TEST (test_layer_meta_marker_list) +{ + GESTimeline *timeline; + GESLayer *layer, *layer2; + GESMarkerList *mlist, *mlist2; + GESMarker *marker; + gchar *metas1, *metas2; + + ges_init (); + + timeline = ges_timeline_new_audio_video (); + layer = ges_layer_new (); + ges_timeline_add_layer (timeline, layer); + layer2 = ges_layer_new (); + ges_timeline_add_layer (timeline, layer2); + + mlist = ges_marker_list_new (); + marker = ges_marker_list_add (mlist, 42); + ges_meta_container_set_string (GES_META_CONTAINER (marker), "bar", "baz"); + marker = ges_marker_list_add (mlist, 84); + ges_meta_container_set_string (GES_META_CONTAINER (marker), "lorem", + "ip\tsu\"m;"); + + ASSERT_OBJECT_REFCOUNT (mlist, "local ref", 1); + + fail_unless (ges_meta_container_set_marker_list (GES_META_CONTAINER (layer), + "foo", mlist)); + + ASSERT_OBJECT_REFCOUNT (mlist, "GstStructure + local ref", 2); + + mlist2 = + ges_meta_container_get_marker_list (GES_META_CONTAINER (layer), "foo"); + + fail_unless (mlist == mlist2); + + ASSERT_OBJECT_REFCOUNT (mlist, "GstStructure + getter + local ref", 3); + + g_object_unref (mlist2); + + ASSERT_OBJECT_REFCOUNT (mlist, "GstStructure + local ref", 2); + + metas1 = ges_meta_container_metas_to_string (GES_META_CONTAINER (layer)); + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (layer2), + metas1); + metas2 = ges_meta_container_metas_to_string (GES_META_CONTAINER (layer2)); + + fail_unless_equals_string (metas1, metas2); + g_free (metas1); + g_free (metas2); + + fail_unless (ges_meta_container_set_marker_list (GES_META_CONTAINER (layer), + "foo", NULL)); + + ASSERT_OBJECT_REFCOUNT (mlist, "local ref", 1); + + g_object_unref (mlist); + g_object_unref (timeline); + + ges_deinit (); +} + +GST_END_TEST; + GST_START_TEST (test_layer_meta_register) { GESTimeline *timeline; @@ -1667,6 +1731,7 @@ ges_suite (void) tcase_add_test (tc_chain, test_layer_meta_date); tcase_add_test (tc_chain, test_layer_meta_date_time); tcase_add_test (tc_chain, test_layer_meta_value); + tcase_add_test (tc_chain, test_layer_meta_marker_list); tcase_add_test (tc_chain, test_layer_meta_register); tcase_add_test (tc_chain, test_layer_meta_foreach); tcase_add_test (tc_chain, test_layer_get_clips_in_interval); diff --git a/tests/check/ges/markerlist.c b/tests/check/ges/markerlist.c new file mode 100644 index 0000000..588490d --- /dev/null +++ b/tests/check/ges/markerlist.c @@ -0,0 +1,365 @@ +/* GStreamer Editing Services + * + * Copyright (C) <2019> Mathieu Duponchelle + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "test-utils.h" +#include +#include + +GST_START_TEST (test_add) +{ + GESMarkerList *markerlist; + GESMarker *marker; + ges_init (); + + markerlist = ges_marker_list_new (); + marker = ges_marker_list_add (markerlist, 42); + + ASSERT_OBJECT_REFCOUNT (marker, "marker list", 1); + + g_object_ref (marker); + + ASSERT_OBJECT_REFCOUNT (marker, "marker list + local ref", 2); + + g_object_unref (markerlist); + + ASSERT_OBJECT_REFCOUNT (marker, "local ref", 1); + + g_object_unref (marker); + + ges_deinit (); +} + +GST_END_TEST; + +GST_START_TEST (test_remove) +{ + GESMarkerList *markerlist; + GESMarker *marker; + ges_init (); + + markerlist = ges_marker_list_new (); + marker = ges_marker_list_add (markerlist, 42); + + g_object_ref (marker); + + fail_unless_equals_int (ges_marker_list_size (markerlist), 1); + + fail_unless (ges_marker_list_remove (markerlist, marker)); + fail_unless_equals_int (ges_marker_list_size (markerlist), 0); + + ASSERT_OBJECT_REFCOUNT (marker, "local ref", 1); + + fail_if (ges_marker_list_remove (markerlist, marker)); + + g_object_unref (marker); + + g_object_unref (markerlist); + + ges_deinit (); +} + +GST_END_TEST; + + +static void +marker_added_cb (GESMarkerList * mlist, GstClockTime position, + GESMarker * marker, gboolean * called) +{ + fail_unless_equals_int (position, 42); + + ASSERT_OBJECT_REFCOUNT (marker, "local ref + signal", 2); + *called = TRUE; +} + +GST_START_TEST (test_signal_marker_added) +{ + GESMarkerList *mlist; + GESMarker *marker; + gboolean called = FALSE; + + ges_init (); + + mlist = ges_marker_list_new (); + g_signal_connect (mlist, "marker-added", G_CALLBACK (marker_added_cb), + &called); + marker = ges_marker_list_add (mlist, 42); + ASSERT_OBJECT_REFCOUNT (marker, "local ref", 1); + fail_unless (called == TRUE); + g_signal_handlers_disconnect_by_func (mlist, marker_added_cb, &called); + + g_object_unref (mlist); + + ges_deinit (); +} + +GST_END_TEST; + + +static void +marker_removed_cb (GESMarkerList * mlist, GESMarker * marker, gboolean * called) +{ + ASSERT_OBJECT_REFCOUNT (marker, "local ref + signal", 2); + *called = TRUE; +} + +GST_START_TEST (test_signal_marker_removed) +{ + GESMarkerList *mlist; + GESMarker *marker; + gboolean called = FALSE; + + ges_init (); + + mlist = ges_marker_list_new (); + marker = ges_marker_list_add (mlist, 42); + + ASSERT_OBJECT_REFCOUNT (marker, "local ref", 1); + + g_signal_connect (mlist, "marker-removed", G_CALLBACK (marker_removed_cb), + &called); + + fail_unless (ges_marker_list_remove (mlist, marker)); + + fail_unless (called == TRUE); + + g_signal_handlers_disconnect_by_func (mlist, marker_removed_cb, &called); + + g_object_unref (mlist); + + ges_deinit (); +} + +GST_END_TEST; + + +static void +marker_moved_cb (GESMarkerList * mlist, GstClockTime position, + GESMarker * marker, gboolean * called) +{ + fail_unless_equals_int (position, 42); + + ASSERT_OBJECT_REFCOUNT (marker, "local ref + signal", 2); + *called = TRUE; +} + +GST_START_TEST (test_signal_marker_moved) +{ + GESMarkerList *mlist; + GESMarker *marker; + gboolean called = FALSE; + + ges_init (); + + mlist = ges_marker_list_new (); + g_signal_connect (mlist, "marker-moved", G_CALLBACK (marker_moved_cb), + &called); + + marker = ges_marker_list_add (mlist, 10); + ASSERT_OBJECT_REFCOUNT (marker, "local ref", 1); + + fail_unless (ges_marker_list_move (mlist, marker, 42)); + + fail_unless (called == TRUE); + + g_signal_handlers_disconnect_by_func (mlist, marker_moved_cb, &called); + + g_object_unref (mlist); + + ges_deinit (); +} + +GST_END_TEST; + + +GST_START_TEST (test_get_markers) +{ + GESMarkerList *markerlist; + GESMarker *marker1, *marker2, *marker3, *marker4; + GList *markers; + + ges_init (); + + markerlist = ges_marker_list_new (); + marker1 = ges_marker_list_add (markerlist, 0); + marker2 = ges_marker_list_add (markerlist, 10); + marker3 = ges_marker_list_add (markerlist, 20); + marker4 = ges_marker_list_add (markerlist, 30); + + markers = ges_marker_list_get_markers (markerlist); + + ASSERT_OBJECT_REFCOUNT (marker1, "local ref + markers", 2); + ASSERT_OBJECT_REFCOUNT (marker2, "local ref + markers", 2); + ASSERT_OBJECT_REFCOUNT (marker3, "local ref + markers", 2); + ASSERT_OBJECT_REFCOUNT (marker4, "local ref + markers", 2); + + fail_unless (g_list_index (markers, marker1) == 0); + fail_unless (g_list_index (markers, marker2) == 1); + fail_unless (g_list_index (markers, marker3) == 2); + fail_unless (g_list_index (markers, marker4) == 3); + + g_list_foreach (markers, (GFunc) gst_object_unref, NULL); + g_list_free (markers); + + g_object_unref (markerlist); + + ges_deinit (); +} + +GST_END_TEST; + + +GST_START_TEST (test_move_marker) +{ + GESMarkerList *markerlist; + GESMarker *marker_a, *marker_b; + GstClockTime position; + GList *range; + + ges_init (); + + markerlist = ges_marker_list_new (); + + marker_a = ges_marker_list_add (markerlist, 10); + marker_b = ges_marker_list_add (markerlist, 30); + + fail_unless (ges_marker_list_move (markerlist, marker_a, 20)); + + g_object_get (marker_a, "position", &position, NULL); + fail_unless_equals_int (position, 20); + + range = ges_marker_list_get_markers (markerlist); + + fail_unless (g_list_index (range, marker_a) == 0); + fail_unless (g_list_index (range, marker_b) == 1); + + g_list_foreach (range, (GFunc) gst_object_unref, NULL); + g_list_free (range); + + fail_unless (ges_marker_list_move (markerlist, marker_a, 35)); + + range = ges_marker_list_get_markers (markerlist); + + fail_unless (g_list_index (range, marker_b) == 0); + fail_unless (g_list_index (range, marker_a) == 1); + + g_list_foreach (range, (GFunc) gst_object_unref, NULL); + g_list_free (range); + + fail_unless (ges_marker_list_move (markerlist, marker_a, 30)); + + g_object_get (marker_a, "position", &position, NULL); + fail_unless_equals_int (position, 30); + + g_object_get (marker_b, "position", &position, NULL); + fail_unless_equals_int (position, 30); + + fail_unless_equals_int (ges_marker_list_size (markerlist), 2); + + ges_marker_list_remove (markerlist, marker_a); + + fail_unless (!ges_marker_list_move (markerlist, marker_a, 20)); + + g_object_unref (markerlist); + + ges_deinit (); +} + +GST_END_TEST; + +GST_START_TEST (test_serialize_deserialize) +{ + GESMarkerList *markerlist1, *markerlist2; + gchar *metas1, *metas2; + GESTimeline *timeline1, *timeline2; + + ges_init (); + + timeline1 = ges_timeline_new_audio_video (); + + markerlist1 = ges_marker_list_new (); + ges_marker_list_add (markerlist1, 0); + ges_marker_list_add (markerlist1, 10); + + ASSERT_OBJECT_REFCOUNT (markerlist1, "local ref", 1); + + fail_unless (ges_meta_container_set_marker_list (GES_META_CONTAINER + (timeline1), "ges-test", markerlist1)); + + ASSERT_OBJECT_REFCOUNT (markerlist1, "GstStructure + local ref", 2); + + markerlist2 = + ges_meta_container_get_marker_list (GES_META_CONTAINER (timeline1), + "ges-test"); + + fail_unless (markerlist1 == markerlist2); + + ASSERT_OBJECT_REFCOUNT (markerlist1, "GstStructure + getter + local ref", 3); + + g_object_unref (markerlist2); + + ASSERT_OBJECT_REFCOUNT (markerlist1, "GstStructure + local ref", 2); + + timeline2 = ges_timeline_new_audio_video (); + + metas1 = ges_meta_container_metas_to_string (GES_META_CONTAINER (timeline1)); + ges_meta_container_add_metas_from_string (GES_META_CONTAINER (timeline2), + metas1); + metas2 = ges_meta_container_metas_to_string (GES_META_CONTAINER (timeline2)); + + fail_unless_equals_string (metas1, metas2); + + g_free (metas1); + g_free (metas2); + + fail_unless (ges_meta_container_set_marker_list (GES_META_CONTAINER + (timeline1), "ges-test", NULL)); + + ASSERT_OBJECT_REFCOUNT (markerlist1, "local ref", 1); + + g_object_unref (markerlist1); + g_object_unref (timeline1); + g_object_unref (timeline2); + + ges_deinit (); +} + +GST_END_TEST; + +static Suite * +ges_suite (void) +{ + Suite *s = suite_create ("ges-marker-list"); + TCase *tc_chain = tcase_create ("markerlist"); + + suite_add_tcase (s, tc_chain); + + tcase_add_test (tc_chain, test_add); + tcase_add_test (tc_chain, test_remove); + tcase_add_test (tc_chain, test_signal_marker_added); + tcase_add_test (tc_chain, test_signal_marker_removed); + tcase_add_test (tc_chain, test_signal_marker_moved); + tcase_add_test (tc_chain, test_get_markers); + tcase_add_test (tc_chain, test_move_marker); + tcase_add_test (tc_chain, test_serialize_deserialize); + + return s; +} + +GST_CHECK_MAIN (ges); diff --git a/tests/check/meson.build b/tests/check/meson.build index 6147992..907fe89 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -17,6 +17,7 @@ ges_tests = [ ['ges/track'], ['ges/tempochange'], ['ges/negative'], + ['ges/markerlist'], ['nle/simple'], ['nle/complex'], ['nle/nleoperation'], -- 2.7.4