1 /* GStreamer Editing Services
3 * Copyright (C) <2019> Mathieu Duponchelle <mathieu@centricular.com>
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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION: gesmarkerlist
23 * @title: GESMarkerList
24 * @short_description: implements a list of markers with metadata asociated to time positions
25 * @see_also: #GESMarker
34 #include "ges-marker-list.h"
36 #include "ges-internal.h"
37 #include "ges-meta-container.h"
41 static void ges_meta_container_interface_init (GESMetaContainerInterface *
47 GstClockTime position;
50 G_DEFINE_TYPE_WITH_CODE (GESMarker, ges_marker, G_TYPE_OBJECT,
51 G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
52 ges_meta_container_interface_init));
61 static GParamSpec *properties[PROP_LAST];
63 /* GObject Standard vmethods*/
65 ges_marker_get_property (GObject * object, guint property_id,
66 GValue * value, GParamSpec * pspec)
68 GESMarker *marker = GES_MARKER (object);
70 switch (property_id) {
72 g_value_set_uint64 (value, marker->position);
75 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
81 ges_marker_init (GESMarker * self)
86 ges_marker_class_init (GESMarkerClass * klass)
88 GObjectClass *object_class = G_OBJECT_CLASS (klass);
90 object_class->get_property = ges_marker_get_property;
94 * Current position (in nanoseconds) of the #GESMarker
96 properties[PROP_POSITION] =
97 g_param_spec_uint64 ("position", "Position",
98 "The position of the marker", 0, G_MAXUINT64,
99 GST_CLOCK_TIME_NONE, G_PARAM_READABLE);
100 g_object_class_install_property (object_class, PROP_POSITION,
101 properties[PROP_POSITION]);
106 ges_meta_container_interface_init (GESMetaContainerInterface * iface)
112 struct _GESMarkerList
117 GHashTable *markers_iters;
128 static guint ges_marker_list_signals[LAST_SIGNAL] = { 0 };
130 G_DEFINE_TYPE (GESMarkerList, ges_marker_list, G_TYPE_OBJECT);
133 remove_marker (gpointer data)
135 GESMarker *marker = (GESMarker *) data;
137 g_object_unref (marker);
141 ges_marker_list_init (GESMarkerList * self)
143 self->markers = g_sequence_new (remove_marker);
144 self->markers_iters = g_hash_table_new (g_direct_hash, g_direct_equal);
148 ges_marker_list_finalize (GObject * object)
150 GESMarkerList *self = GES_MARKER_LIST (object);
152 g_sequence_free (self->markers);
153 g_hash_table_unref (self->markers_iters);
155 G_OBJECT_CLASS (ges_marker_list_parent_class)->finalize (object);
159 ges_marker_list_class_init (GESMarkerListClass * klass)
161 GObjectClass *object_class = G_OBJECT_CLASS (klass);
163 object_class->finalize = ges_marker_list_finalize;
166 * GESMarkerList::marker-added:
167 * @marker-list: the #GESMarkerList
168 * @marker: the #GESMarker that was added.
170 * Will be emitted after the marker was added to the marker-list.
173 ges_marker_list_signals[MARKER_ADDED] =
174 g_signal_new ("marker-added", G_TYPE_FROM_CLASS (klass),
175 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
176 G_TYPE_NONE, 2, G_TYPE_UINT64, GES_TYPE_MARKER);
179 * GESMarkerList::marker-removed:
180 * @marker-list: the #GESMarkerList
181 * @marker: the #GESMarker that was removed.
183 * Will be emitted after the marker was removed the marker-list.
186 ges_marker_list_signals[MARKER_REMOVED] =
187 g_signal_new ("marker-removed", G_TYPE_FROM_CLASS (klass),
188 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
189 G_TYPE_NONE, 1, GES_TYPE_MARKER);
192 * GESMarkerList::marker-moved:
193 * @marker-list: the #GESMarkerList
194 * @marker: the #GESMarker that was added.
196 * Will be emitted after the marker was moved to.
199 ges_marker_list_signals[MARKER_MOVED] =
200 g_signal_new ("marker-moved", G_TYPE_FROM_CLASS (klass),
201 G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
202 G_TYPE_NONE, 2, G_TYPE_UINT64, GES_TYPE_MARKER);
206 cmp_marker (gconstpointer a, gconstpointer b, G_GNUC_UNUSED gpointer data)
208 GESMarker *marker_a = (GESMarker *) a;
209 GESMarker *marker_b = (GESMarker *) b;
211 if (marker_a->position < marker_b->position)
213 else if (marker_a->position == marker_b->position)
220 * ges_marker_list_new:
222 * Creates a new #GESMarkerList.
224 * Returns: A new #GESMarkerList
228 ges_marker_list_new (void)
232 ret = (GESMarkerList *) g_object_new (GES_TYPE_MARKER_LIST, NULL);
238 * ges_marker_list_add:
239 * @position: The position of the new marker
241 * Returns: (transfer none): The newly-added marker, the list keeps ownership
246 ges_marker_list_add (GESMarkerList * self, GstClockTime position)
251 g_return_val_if_fail (GES_IS_MARKER_LIST (self), NULL);
253 ret = (GESMarker *) g_object_new (GES_TYPE_MARKER, NULL);
255 ret->position = position;
257 iter = g_sequence_insert_sorted (self->markers, ret, cmp_marker, NULL);
259 g_hash_table_insert (self->markers_iters, ret, iter);
261 g_signal_emit (self, ges_marker_list_signals[MARKER_ADDED], 0, position, ret);
267 * ges_marker_list_size:
269 * Returns: The number of markers in @list
273 ges_marker_list_size (GESMarkerList * self)
275 g_return_val_if_fail (GES_IS_MARKER_LIST (self), 0);
277 return g_sequence_get_length (self->markers);
281 * ges_marker_list_remove:
283 * Removes @marker from @list, this decreases the refcount of the
286 * Returns: %TRUE if the marker could be removed, %FALSE otherwise
287 * (if the marker was not present in the list for example)
291 ges_marker_list_remove (GESMarkerList * self, GESMarker * marker)
294 gboolean ret = FALSE;
296 g_return_val_if_fail (GES_IS_MARKER_LIST (self), FALSE);
298 if (!g_hash_table_lookup_extended (self->markers_iters,
299 marker, NULL, (gpointer *) & iter))
301 g_assert (iter != NULL);
302 g_hash_table_remove (self->markers_iters, marker);
304 g_signal_emit (self, ges_marker_list_signals[MARKER_REMOVED], 0, marker);
306 g_sequence_remove (iter);
316 * ges_marker_list_get_markers:
318 * Returns: (element-type GESMarker) (transfer full): a #GList
319 * of the #GESMarker within the GESMarkerList. The user will have
320 * to unref each #GESMarker and free the #GList.
325 ges_marker_list_get_markers (GESMarkerList * self)
331 g_return_val_if_fail (GES_IS_MARKER_LIST (self), NULL);
334 for (iter = g_sequence_get_begin_iter (self->markers);
335 !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
336 marker = GES_MARKER (g_sequence_get (iter));
338 ret = g_list_append (ret, g_object_ref (marker));
346 * ges_marker_list_move:
348 * Moves a @marker in a @list to a new @position
350 * Returns: %TRUE if the marker could be moved, %FALSE otherwise
351 * (if the marker was not present in the list for example)
356 ges_marker_list_move (GESMarkerList * self, GESMarker * marker,
357 GstClockTime position)
360 gboolean ret = FALSE;
362 g_return_val_if_fail (GES_IS_MARKER_LIST (self), FALSE);
364 if (!g_hash_table_lookup_extended (self->markers_iters,
365 marker, NULL, (gpointer *) & iter)) {
366 GST_WARNING ("GESMarkerList doesn't contain GESMarker");
370 marker->position = position;
372 g_signal_emit (self, ges_marker_list_signals[MARKER_MOVED], 0, position,
375 g_sequence_sort_changed (iter, cmp_marker, NULL);
384 ges_marker_list_deserialize (GValue * dest, const gchar * s)
386 gboolean ret = FALSE;
387 GstCaps *caps = NULL;
388 GESMarkerList *list = ges_marker_list_new ();
391 caps = gst_caps_from_string (s);
393 l = gst_caps_get_size (caps);
396 GST_ERROR ("Failed deserializing marker list: expected evenly-sized caps");
400 for (i = 0; i < l - 1; i += 2) {
401 const GstStructure *pos_s = gst_caps_get_structure (caps, i);
402 const GstStructure *meta_s = gst_caps_get_structure (caps, i + 1);
403 GstClockTime position;
407 if (!gst_structure_has_name (pos_s, "marker-times")) {
408 GST_ERROR_OBJECT (dest,
409 "Failed deserializing marker list: unexpected structure %"
410 GST_PTR_FORMAT, pos_s);
414 if (!gst_structure_get_uint64 (pos_s, "position", &position)) {
415 GST_ERROR_OBJECT (dest,
416 "Failed deserializing marker list: unexpected structure %"
417 GST_PTR_FORMAT, pos_s);
421 marker = ges_marker_list_add (list, position);
423 metas = gst_structure_to_string (meta_s);
424 ges_meta_container_add_metas_from_string (GES_META_CONTAINER (marker),
434 gst_caps_unref (caps);
437 g_object_unref (list);
439 g_value_take_object (dest, list);
445 ges_marker_list_serialize (const GValue * v)
447 GESMarkerList *list = GES_MARKER_LIST (g_value_get_object (v));
449 GstCaps *caps = gst_caps_new_empty ();
450 gchar *caps_str, *escaped, *res;
452 iter = g_sequence_get_begin_iter (list->markers);
454 while (!g_sequence_iter_is_end (iter)) {
455 GESMarker *marker = (GESMarker *) g_sequence_get (iter);
459 metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (marker));
461 s = gst_structure_new ("marker-times", "position", G_TYPE_UINT64,
462 marker->position, NULL);
463 gst_caps_append_structure (caps, s);
464 s = gst_structure_from_string (metas, NULL);
465 gst_caps_append_structure (caps, s);
469 iter = g_sequence_iter_next (iter);
472 caps_str = gst_caps_to_string (caps);
473 escaped = g_strescape (caps_str, NULL);
475 res = g_strdup_printf ("\"%s\"", escaped);
477 gst_caps_unref (caps);