/* GStreamer * Copyright (C) 2010, 2012 Alexander Saprykin * * gsttocsetter.c: interface for TOC setting on elements * * 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. */ /** * SECTION:gsttocsetter * @short_description: Element interface that allows setting and retrieval * of the TOC * * Element interface that allows setting of the TOC. * * Elements that support some kind of chapters or editions (or tracks like in * the FLAC cue sheet) will implement this interface. * * If you just want to retrieve the TOC in your application then all you * need to do is watch for TOC messages on your pipeline's bus (or you can * perform TOC query). This interface is only for setting TOC data, not for * extracting it. To set TOC from the application, find proper tocsetter element * and set TOC using gst_toc_setter_set_toc(). * * Elements implementing the #GstTocSetter interface can extend existing TOC * by getting extend UID for that (you can use gst_toc_find_entry() to retrieve it) * with any TOC entries received from downstream. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "gst_private.h" #include "gsttocsetter.h" #include #include GST_DEBUG_CATEGORY_STATIC (gst_toc_interface_debug); #define GST_CAT_DEFAULT tag_toc_interface_debug static GQuark gst_toc_key; typedef struct { GstToc *toc; GStaticMutex lock; } GstTocData; GType gst_toc_setter_get_type (void) { static volatile gsize toc_setter_type = 0; if (g_once_init_enter (&toc_setter_type)) { GType _type; static const GTypeInfo toc_setter_info = { sizeof (GstTocSetterIFace), /* class_size */ NULL, /* base_init */ NULL, /* base_finalize */ NULL, NULL, /* class_finalize */ NULL, /* class_data */ 0, 0, NULL }; GST_DEBUG_CATEGORY_INIT (gst_toc_interface_debug, "GstTocInterface", 0, "interfaces for the TOC"); _type = g_type_register_static (G_TYPE_INTERFACE, "GstTocSetter", &toc_setter_info, 0); g_type_interface_add_prerequisite (_type, GST_TYPE_ELEMENT); gst_toc_key = g_quark_from_static_string ("GST_TOC_SETTER"); g_once_init_leave (&toc_setter_type, _type); } return toc_setter_type; } static void gst_toc_data_free (gpointer p) { GstTocData *data = (GstTocData *) p; if (data->toc) gst_toc_free (data->toc); g_static_mutex_free (&data->lock); g_slice_free (GstTocData, data); } static GstTocData * gst_toc_setter_get_data (GstTocSetter * setter) { GstTocData *data; data = g_object_get_qdata (G_OBJECT (setter), gst_toc_key); if (!data) { static GStaticMutex create_mutex = G_STATIC_MUTEX_INIT; /* make sure no other thread is creating a GstTocData at the same time */ g_static_mutex_lock (&create_mutex); data = g_object_get_qdata (G_OBJECT (setter), gst_toc_key); if (!data) { data = g_slice_new (GstTocData); g_static_mutex_init (&data->lock); data->toc = NULL; g_object_set_qdata_full (G_OBJECT (setter), gst_toc_key, data, gst_toc_data_free); } g_static_mutex_unlock (&create_mutex); } return data; } /** * gst_toc_setter_reset_toc: * @setter: a #GstTocSetter. * * Reset the internal TOC. Elements should call this from within the * state-change handler. * * Since: 0.10.37 */ void gst_toc_setter_reset_toc (GstTocSetter * setter) { GstTocData *data; g_return_if_fail (GST_IS_TOC_SETTER (setter)); data = gst_toc_setter_get_data (setter); g_static_mutex_lock (&data->lock); if (data->toc) { gst_toc_free (data->toc); data->toc = NULL; } g_static_mutex_unlock (&data->lock); } /** * gst_toc_setter_get_toc: * @setter: a #GstTocSetter. * * Return current TOC the setter uses. The TOC should not be * modified or freed. * * This function is not thread-safe. Use gst_toc_setter_get_toc_copy() instead. * * Returns: a current snapshot of the TOC used in the setter * or NULL if none is used. * * Since: 0.10.37 */ const GstToc * gst_toc_setter_get_toc (GstTocSetter * setter) { g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL); return gst_toc_setter_get_data (setter)->toc; } /** * gst_toc_setter_get_toc_copy: * @setter: a #GstTocSetter. * * Return current TOC the setter uses. The difference between this * function and gst_toc_setter_get_toc() is that this function returns deep * copy of the TOC, so you can modify it in any way. This function is thread-safe. * Free it when done with gst_toc_free(). * * Returns: a copy of the current snapshot of the TOC used in the setter * or NULL if none is used. * * Since: 0.10.37 */ GstToc * gst_toc_setter_get_toc_copy (GstTocSetter * setter) { GstTocData *data; GstToc *ret = NULL; g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL); data = gst_toc_setter_get_data (setter); g_static_mutex_lock (&data->lock); if (data->toc != NULL) ret = gst_toc_copy (data->toc); g_static_mutex_unlock (&data->lock); return ret; } /** * gst_toc_setter_set_toc: * @setter: a #GstTocSetter. * @toc: a #GstToc to set. * * Set the given TOC on the setter. Previously setted TOC will be * freed before setting a new one. * * Since: 0.10.37 */ void gst_toc_setter_set_toc (GstTocSetter * setter, const GstToc * toc) { GstTocData *data; g_return_if_fail (GST_IS_TOC_SETTER (setter)); data = gst_toc_setter_get_data (setter); g_static_mutex_lock (&data->lock); if (data->toc) gst_toc_free (data->toc); data->toc = gst_toc_copy (toc); g_static_mutex_unlock (&data->lock); } /** * gst_toc_setter_get_toc_entry: * @setter: a #GstTocSetter. * @uid: UID to find entry with. * * Return #GstTocEntry (if any) with given @uid. Returned entry should * not be modified or freed. * * This function is not thread-safe. Use gst_toc_setter_get_toc_entry_copy() instead. * * Returns: a TOC entry with given @uid from the TOC in the setter * or NULL if none entry with such @uid was found. * * Since: 0.10.37 */ const GstTocEntry * gst_toc_setter_get_toc_entry (GstTocSetter * setter, const gchar * uid) { GstTocData *data; const GstTocEntry *ret; g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL); g_return_val_if_fail (uid != NULL, NULL); data = gst_toc_setter_get_data (setter); g_static_mutex_lock (&data->lock); ret = gst_toc_find_entry (data->toc, uid); g_static_mutex_unlock (&data->lock); return ret; } /** * gst_toc_setter_get_toc_entry_copy: * @setter: a #GstTocSetter. * @uid: UID to find entry with. * * Return #GstTocEntry (if any) with given @uid. It perform a deep copying, * so you can modify returned value. Free it when done with gst_toc_entry_free(). * This function is thread-safe. * * Returns: a TOC entry with given @uid from the TOC in the setter * or NULL if none entry with such @uid was found. * * Since: 0.10.37 */ GstTocEntry * gst_toc_setter_get_toc_entry_copy (GstTocSetter * setter, const gchar * uid) { GstTocData *data; GstTocEntry *ret = NULL; const GstTocEntry *search; g_return_val_if_fail (GST_IS_TOC_SETTER (setter), NULL); g_return_val_if_fail (uid != NULL, NULL); data = gst_toc_setter_get_data (setter); g_static_mutex_lock (&data->lock); search = gst_toc_find_entry (data->toc, uid); if (search != NULL) ret = gst_toc_entry_copy (search); g_static_mutex_unlock (&data->lock); return ret; } /** * gst_toc_setter_add_toc_entry: * @setter: a #GstTocSetter. * @parent_uid: UID of the parent entry to append given @entry. Use 0 for the TOC root level. * @entry: #GstTocEntry to append. * * Try to find entry with given @parent_uid and append an @entry to that #GstTocEntry. * * Returns: TRUE if entry with @parent_uid was found, FALSE otherwise. * * Since: 0.10.37 */ gboolean gst_toc_setter_add_toc_entry (GstTocSetter * setter, const gchar * parent_uid, const GstTocEntry * entry) { GstTocData *data; GstTocEntry *parent; GstTocEntry *copy_entry; gboolean ret = FALSE; g_return_val_if_fail (GST_IS_TOC_SETTER (setter), FALSE); g_return_val_if_fail (parent_uid != NULL, FALSE); g_return_val_if_fail (entry != NULL, FALSE); data = gst_toc_setter_get_data (setter); g_static_mutex_lock (&data->lock); copy_entry = gst_toc_entry_copy (entry); if (g_strcmp0 (parent_uid, "0") == 0) data->toc->entries = g_list_append (data->toc->entries, copy_entry); else { parent = gst_toc_find_entry (data->toc, parent_uid); if (parent != NULL) { parent->subentries = g_list_append (parent->subentries, copy_entry); ret = TRUE; } } g_static_mutex_unlock (&data->lock); return ret; }