#endif
#include "gst_private.h"
+#include "math-compat.h"
#include "gst-i18n-lib.h"
#include "gsttaglist.h"
#include "gstinfo.h"
#include "gstvalue.h"
#include "gstbuffer.h"
#include "gstquark.h"
+#include "gststructure.h"
#include <gobject/gvaluecollector.h>
#include <string.h>
#define GST_TAG_IS_VALID(tag) (gst_tag_get_info (tag) != NULL)
+/* FIXME 0.11: make taglists refcounted maybe? */
+/* a tag list is basically a structure, but we don't make this fact public */
+struct _GstTagList
+{
+ GstStructure structure;
+};
+
/* FIXME 0.11: use GParamSpecs or something similar for tag registrations,
* possibly even gst_tag_register(). Especially value ranges might be
* useful for some tags. */
GstTagMergeFunc merge_func; /* functions to merge the values */
GstTagFlag flag; /* type of tag */
+ GQuark name_quark; /* quark for the name */
}
GstTagInfo;
static GMutex *__tag_mutex;
+/* tags hash table: maps tag name string => GstTagInfo */
static GHashTable *__tags;
#define TAG_LOCK g_mutex_lock (__tag_mutex)
}
void
-_gst_tag_initialize (void)
+_priv_gst_tag_initialize (void)
{
__tag_mutex = g_mutex_new ();
- __tags = g_hash_table_new (g_direct_hash, g_direct_equal);
+ __tags = g_hash_table_new (g_str_hash, g_str_equal);
gst_tag_register (GST_TAG_TITLE, GST_TAG_FLAG_META,
G_TYPE_STRING,
_("title"), _("commonly used title"), gst_tag_merge_strings_with_comma);
}
static GstTagInfo *
-gst_tag_lookup (GQuark entry)
+gst_tag_lookup (const gchar * tag_name)
{
GstTagInfo *ret;
TAG_LOCK;
- ret = g_hash_table_lookup (__tags, GUINT_TO_POINTER (entry));
+ ret = g_hash_table_lookup (__tags, (gpointer) tag_name);
TAG_UNLOCK;
return ret;
gst_tag_register (const gchar * name, GstTagFlag flag, GType type,
const gchar * nick, const gchar * blurb, GstTagMergeFunc func)
{
- GQuark key;
GstTagInfo *info;
+ gchar *name_dup;
g_return_if_fail (name != NULL);
g_return_if_fail (nick != NULL);
g_return_if_fail (blurb != NULL);
g_return_if_fail (type != 0 && type != GST_TYPE_LIST);
- key = g_quark_from_string (name);
- info = gst_tag_lookup (key);
+ info = gst_tag_lookup (name);
if (info) {
g_return_if_fail (info->type == type);
info->blurb = g_strdup (blurb);
info->merge_func = func;
+ /* we make a copy for the hash table anyway, which will stay around, so
+ * can use that for the quark table too */
+ name_dup = g_strdup (name);
+ info->name_quark = g_quark_from_static_string (name_dup);
+
TAG_LOCK;
- g_hash_table_insert (__tags, GUINT_TO_POINTER (key), info);
+ g_hash_table_insert (__tags, (gpointer) name_dup, info);
TAG_UNLOCK;
}
{
g_return_val_if_fail (tag != NULL, FALSE);
- return gst_tag_lookup (g_quark_from_string (tag)) != NULL;
+ return gst_tag_lookup (tag) != NULL;
}
/**
GstTagInfo *info;
g_return_val_if_fail (tag != NULL, 0);
- info = gst_tag_lookup (g_quark_from_string (tag));
+ info = gst_tag_lookup (tag);
g_return_val_if_fail (info != NULL, 0);
return info->type;
GstTagInfo *info;
g_return_val_if_fail (tag != NULL, NULL);
- info = gst_tag_lookup (g_quark_from_string (tag));
+ info = gst_tag_lookup (tag);
g_return_val_if_fail (info != NULL, NULL);
return info->nick;
GstTagInfo *info;
g_return_val_if_fail (tag != NULL, NULL);
- info = gst_tag_lookup (g_quark_from_string (tag));
+ info = gst_tag_lookup (tag);
g_return_val_if_fail (info != NULL, NULL);
return info->blurb;
GstTagInfo *info;
g_return_val_if_fail (tag != NULL, GST_TAG_FLAG_UNDEFINED);
- info = gst_tag_lookup (g_quark_from_string (tag));
+ info = gst_tag_lookup (tag);
g_return_val_if_fail (info != NULL, GST_TAG_FLAG_UNDEFINED);
return info->flag;
GstTagInfo *info;
g_return_val_if_fail (tag != NULL, FALSE);
- info = gst_tag_lookup (g_quark_from_string (tag));
+ info = gst_tag_lookup (tag);
g_return_val_if_fail (info != NULL, FALSE);
return info->merge_func == NULL;
}
/**
- * gst_tag_list_new:
+ * gst_tag_list_new_empty:
*
* Creates a new empty GstTagList.
*
* Returns: (transfer full): An empty tag list
*/
GstTagList *
-gst_tag_list_new (void)
+gst_tag_list_new_empty (void)
{
- return GST_TAG_LIST (gst_structure_id_empty_new (GST_QUARK (TAGLIST)));
+ return GST_TAG_LIST (gst_structure_new_id_empty (GST_QUARK (TAGLIST)));
}
/**
- * gst_tag_list_new_full:
+ * gst_tag_list_new:
* @tag: tag
* @...: NULL-terminated list of values to set
*
*
* Since: 0.10.24
*/
-/* FIXME 0.11: rename gst_tag_list_new_full to _new and _new to _new_empty */
GstTagList *
-gst_tag_list_new_full (const gchar * tag, ...)
+gst_tag_list_new (const gchar * tag, ...)
{
GstTagList *list;
va_list args;
g_return_val_if_fail (tag != NULL, NULL);
- list = gst_tag_list_new ();
+ list = gst_tag_list_new_empty ();
va_start (args, tag);
gst_tag_list_add_valist (list, GST_TAG_MERGE_APPEND, tag, args);
va_end (args);
}
/**
- * gst_tag_list_new_full_valist:
+ * gst_tag_list_new_valist:
* @var_args: tag / value pairs to set
*
- * Just like gst_tag_list_new_full(), only that it takes a va_list argument.
+ * Just like gst_tag_list_new(), only that it takes a va_list argument.
* Useful mostly for language bindings.
*
* Free-function: gst_tag_list_free
* Since: 0.10.24
*/
GstTagList *
-gst_tag_list_new_full_valist (va_list var_args)
+gst_tag_list_new_valist (va_list var_args)
{
GstTagList *list;
const gchar *tag;
- list = gst_tag_list_new ();
+ list = gst_tag_list_new_empty ();
tag = va_arg (var_args, gchar *);
gst_tag_list_add_valist (list, GST_TAG_MERGE_APPEND, tag, var_args);
}
/**
+ * gst_tag_list_to_string:
+ * @list: a #GstTagList
+ *
+ * Serializes a tag list to a string.
+ *
+ * Returns: a newly-allocated string, or NULL in case of an error. The
+ * string must be freed with g_free() when no longer needed.
+ *
+ * Since: 0.10.36
+ */
+gchar *
+gst_tag_list_to_string (const GstTagList * list)
+{
+ g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
+
+ return gst_structure_to_string (GST_STRUCTURE (list));
+}
+
+/**
+ * gst_tag_list_new_from_string:
+ * @str: a string created with gst_tag_list_to_string()
+ *
+ * Deserializes a tag list.
+ *
+ * Returns: a new #GstTagList, or NULL in case of an error.
+ *
+ * Since: 0.10.36
+ */
+GstTagList *
+gst_tag_list_new_from_string (const gchar * str)
+{
+ g_return_val_if_fail (str != NULL, NULL);
+ g_return_val_if_fail (g_str_has_prefix (str, "taglist"), NULL);
+
+ return GST_TAG_LIST (gst_structure_from_string (str, NULL));
+}
+
+/**
* gst_tag_list_is_empty:
* @list: A #GstTagList.
*
return (gst_structure_n_fields ((GstStructure *) list) == 0);
}
+static gboolean
+gst_tag_list_fields_equal (const GValue * value1, const GValue * value2)
+{
+ gdouble d1, d2;
+
+ if (gst_value_compare (value1, value2) == GST_VALUE_EQUAL)
+ return TRUE;
+
+ /* fields not equal: add some tolerance for doubles, otherwise bail out */
+ if (!G_VALUE_HOLDS_DOUBLE (value1) || !G_VALUE_HOLDS_DOUBLE (value2))
+ return FALSE;
+
+ d1 = g_value_get_double (value1);
+ d2 = g_value_get_double (value2);
+
+ /* This will only work for 'normal' values and values around 0,
+ * which should be good enough for our purposes here
+ * FIXME: maybe add this to gst_value_compare_double() ? */
+ return (fabs (d1 - d2) < 0.0000001);
+}
+
+/**
+ * gst_tag_list_is_equal:
+ * @list1: a #GstTagList.
+ * @list2: a #GstTagList.
+ *
+ * Checks if the two given taglists are equal.
+ *
+ * Returns: TRUE if the taglists are equal, otherwise FALSE
+ *
+ * Since: 0.10.36
+ */
+gboolean
+gst_tag_list_is_equal (const GstTagList * list1, const GstTagList * list2)
+{
+ const GstStructure *s1, *s2;
+ gint num_fields1, num_fields2, i;
+
+ g_return_val_if_fail (GST_IS_TAG_LIST (list1), FALSE);
+ g_return_val_if_fail (GST_IS_TAG_LIST (list2), FALSE);
+
+ /* we don't just use gst_structure_is_equal() here so we can add some
+ * tolerance for doubles, though maybe we should just add that to
+ * gst_value_compare_double() as well? */
+ s1 = (const GstStructure *) list1;
+ s2 = (const GstStructure *) list2;
+
+ num_fields1 = gst_structure_n_fields (s1);
+ num_fields2 = gst_structure_n_fields (s2);
+
+ if (num_fields1 != num_fields2)
+ return FALSE;
+
+ for (i = 0; i < num_fields1; i++) {
+ const GValue *value1, *value2;
+ const gchar *tag_name;
+
+ tag_name = gst_structure_nth_field_name (s1, i);
+ value1 = gst_structure_get_value (s1, tag_name);
+ value2 = gst_structure_get_value (s2, tag_name);
+
+ if (value2 == NULL)
+ return FALSE;
+
+ if (!gst_tag_list_fields_equal (value1, value2))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* gst_is_tag_list:
* @p: Object that might be a taglist
typedef struct
{
- GstStructure *list;
+ GstTagList *list;
GstTagMergeMode mode;
}
GstTagCopyData;
static void
-gst_tag_list_add_value_internal (GstStructure * list, GstTagMergeMode mode,
- GQuark tag, const GValue * value, GstTagInfo * info)
+gst_tag_list_add_value_internal (GstTagList * tag_list, GstTagMergeMode mode,
+ const gchar * tag, const GValue * value, GstTagInfo * info)
{
+ GstStructure *list = GST_STRUCTURE (tag_list);
const GValue *value2;
+ GQuark tag_quark;
if (info == NULL) {
info = gst_tag_lookup (tag);
if (G_UNLIKELY (info == NULL)) {
- g_warning ("unknown tag '%s'", g_quark_to_string (tag));
+ g_warning ("unknown tag '%s'", tag);
return;
}
}
+ tag_quark = info->name_quark;
+
if (info->merge_func
- && (value2 = gst_structure_id_get_value (list, tag)) != NULL) {
+ && (value2 = gst_structure_id_get_value (list, tag_quark)) != NULL) {
GValue dest = { 0, };
switch (mode) {
case GST_TAG_MERGE_REPLACE_ALL:
case GST_TAG_MERGE_REPLACE:
- gst_structure_id_set_value (list, tag, value);
+ gst_structure_id_set_value (list, tag_quark, value);
break;
case GST_TAG_MERGE_PREPEND:
gst_value_list_merge (&dest, value, value2);
- gst_structure_id_set_value (list, tag, &dest);
+ gst_structure_id_set_value (list, tag_quark, &dest);
g_value_unset (&dest);
break;
case GST_TAG_MERGE_APPEND:
gst_value_list_merge (&dest, value2, value);
- gst_structure_id_set_value (list, tag, &dest);
+ gst_structure_id_set_value (list, tag_quark, &dest);
g_value_unset (&dest);
break;
case GST_TAG_MERGE_KEEP:
switch (mode) {
case GST_TAG_MERGE_APPEND:
case GST_TAG_MERGE_KEEP:
- if (gst_structure_id_get_value (list, tag) != NULL)
+ if (gst_structure_id_get_value (list, tag_quark) != NULL)
break;
/* fall through */
case GST_TAG_MERGE_REPLACE_ALL:
case GST_TAG_MERGE_REPLACE:
case GST_TAG_MERGE_PREPEND:
- gst_structure_id_set_value (list, tag, value);
+ gst_structure_id_set_value (list, tag_quark, value);
break;
case GST_TAG_MERGE_KEEP_ALL:
break;
}
static gboolean
-gst_tag_list_copy_foreach (GQuark tag, const GValue * value, gpointer user_data)
+gst_tag_list_copy_foreach (GQuark tag_quark, const GValue * value,
+ gpointer user_data)
{
GstTagCopyData *copy = (GstTagCopyData *) user_data;
+ const gchar *tag;
+ tag = g_quark_to_string (tag_quark);
gst_tag_list_add_value_internal (copy->list, copy->mode, tag, value, NULL);
return TRUE;
g_return_if_fail (GST_IS_TAG_LIST (from));
g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
- data.list = (GstStructure *) into;
+ data.list = into;
data.mode = mode;
if (mode == GST_TAG_MERGE_REPLACE_ALL) {
- gst_structure_remove_all_fields (data.list);
+ gst_structure_remove_all_fields (GST_STRUCTURE (data.list));
}
gst_structure_foreach ((GstStructure *) from, gst_tag_list_copy_foreach,
&data);
}
/* create empty list, we need to do this to correctly handling merge modes */
- list1_cp = (list1) ? gst_tag_list_copy (list1) : gst_tag_list_new ();
- list2_cp = (list2) ? list2 : gst_tag_list_new ();
+ list1_cp = (list1) ? gst_tag_list_copy (list1) : gst_tag_list_new_empty ();
+ list2_cp = (list2) ? list2 : gst_tag_list_new_empty ();
gst_tag_list_insert (list1_cp, list2_cp, mode);
const gchar * tag, va_list var_args)
{
GstTagInfo *info;
- GQuark quark;
gchar *error = NULL;
g_return_if_fail (GST_IS_TAG_LIST (list));
g_return_if_fail (tag != NULL);
if (mode == GST_TAG_MERGE_REPLACE_ALL) {
- gst_structure_remove_all_fields (list);
+ gst_structure_remove_all_fields (GST_STRUCTURE (list));
}
while (tag != NULL) {
GValue value = { 0, };
- quark = g_quark_from_string (tag);
- info = gst_tag_lookup (quark);
+ info = gst_tag_lookup (tag);
if (G_UNLIKELY (info == NULL)) {
g_warning ("unknown tag '%s'", tag);
return;
}
-#if GLIB_CHECK_VERSION(2,23,3)
G_VALUE_COLLECT_INIT (&value, info->type, var_args, 0, &error);
-#else
- g_value_init (&value, info->type);
- G_VALUE_COLLECT (&value, var_args, 0, &error);
-#endif
if (error) {
g_warning ("%s: %s", G_STRLOC, error);
g_free (error);
*/
return;
}
- gst_tag_list_add_value_internal (list, mode, quark, &value, info);
+ gst_tag_list_add_value_internal (list, mode, tag, &value, info);
g_value_unset (&value);
tag = va_arg (var_args, gchar *);
}
gst_tag_list_add_valist_values (GstTagList * list, GstTagMergeMode mode,
const gchar * tag, va_list var_args)
{
- GQuark quark;
-
g_return_if_fail (GST_IS_TAG_LIST (list));
g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
g_return_if_fail (tag != NULL);
if (mode == GST_TAG_MERGE_REPLACE_ALL) {
- gst_structure_remove_all_fields (list);
+ gst_structure_remove_all_fields (GST_STRUCTURE (list));
}
while (tag != NULL) {
- quark = g_quark_from_string (tag);
- g_return_if_fail (gst_tag_lookup (quark) != NULL);
- gst_tag_list_add_value_internal (list, mode, quark, va_arg (var_args,
- GValue *), NULL);
+ GstTagInfo *info;
+
+ info = gst_tag_lookup (tag);
+ if (G_UNLIKELY (info == NULL)) {
+ g_warning ("unknown tag '%s'", tag);
+ return;
+ }
+ gst_tag_list_add_value_internal (list, mode, tag, va_arg (var_args,
+ GValue *), info);
tag = va_arg (var_args, gchar *);
}
}
g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
g_return_if_fail (tag != NULL);
- gst_tag_list_add_value_internal (list, mode, g_quark_from_string (tag),
- value, NULL);
+ gst_tag_list_add_value_internal (list, mode, tag, value, NULL);
}
/**
* Returns: (transfer none): The GValue for the specified entry or NULL if the
* tag wasn't available or the tag doesn't have as many entries
*/
-G_CONST_RETURN GValue *
+const GValue *
gst_tag_list_get_value_index (const GstTagList * list, const gchar * tag,
guint index)
{
return FALSE;
if (G_VALUE_TYPE (src) == GST_TYPE_LIST) {
- GstTagInfo *info = gst_tag_lookup (g_quark_from_string (tag));
+ GstTagInfo *info = gst_tag_lookup (tag);
if (!info)
return FALSE;
*/
TAG_MERGE_FUNCS (uint, guint, TRUE);
/**
- * gst_tag_list_get_long:
- * @list: a #GstTagList to get the tag from
- * @tag: tag to read out
- * @value: (out): location for the result
- *
- * Copies the contents for the given tag into the value, merging multiple values
- * into one if multiple values are associated with the tag.
- *
- * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
- * given list.
- */
-/**
- * gst_tag_list_get_long_index:
- * @list: a #GstTagList to get the tag from
- * @tag: tag to read out
- * @index: number of entry to read out
- * @value: (out): location for the result
- *
- * Gets the value that is at the given index for the given tag in the given
- * list.
- *
- * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
- * given list.
- */
-TAG_MERGE_FUNCS (long, glong, TRUE);
-/**
- * gst_tag_list_get_ulong:
- * @list: a #GstTagList to get the tag from
- * @tag: tag to read out
- * @value: (out): location for the result
- *
- * Copies the contents for the given tag into the value, merging multiple values
- * into one if multiple values are associated with the tag.
- *
- * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
- * given list.
- */
-/**
- * gst_tag_list_get_ulong_index:
- * @list: a #GstTagList to get the tag from
- * @tag: tag to read out
- * @index: number of entry to read out
- * @value: (out): location for the result
- *
- * Gets the value that is at the given index for the given tag in the given
- * list.
- *
- * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
- * given list.
- */
-TAG_MERGE_FUNCS (ulong, gulong, TRUE);
-/**
- * gst_tag_list_get_int64:
- * @list: a #GstTagList to get the tag from
- * @tag: tag to read out
- * @value: (out): location for the result
- *
- * Copies the contents for the given tag into the value, merging multiple values
- * into one if multiple values are associated with the tag.
- *
- * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the
- * given list.
- */
-/**
* gst_tag_list_get_int64_index:
* @list: a #GstTagList to get the tag from
* @tag: tag to read out