| /* GStreamer |
| * Copyright (C) <2003> David A. Schleef <ds@schleef.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., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:gstvalue |
| * @short_description: GValue implementations specific |
| * to GStreamer |
| * |
| * GValue implementations specific to GStreamer. |
| * |
| * Note that operations on the same #GValue from multiple threads may lead to |
| * undefined behaviour. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| #include <math.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include "gst_private.h" |
| #include "glib-compat-private.h" |
| #include <gst/gst.h> |
| #include <gobject/gvaluecollector.h> |
| #include "gstutils.h" |
| |
| /* GstValueUnionFunc: |
| * @dest: a #GValue for the result |
| * @value1: a #GValue operand |
| * @value2: a #GValue operand |
| * |
| * Used by gst_value_union() to perform unification for a specific #GValue |
| * type. Register a new implementation with gst_value_register_union_func(). |
| * |
| * Returns: %TRUE if a union was successful |
| */ |
| typedef gboolean (*GstValueUnionFunc) (GValue * dest, |
| const GValue * value1, const GValue * value2); |
| |
| /* GstValueIntersectFunc: |
| * @dest: (out caller-allocates): a #GValue for the result |
| * @value1: a #GValue operand |
| * @value2: a #GValue operand |
| * |
| * Used by gst_value_intersect() to perform intersection for a specific #GValue |
| * type. If the intersection is non-empty, the result is |
| * placed in @dest and %TRUE is returned. If the intersection is |
| * empty, @dest is unmodified and %FALSE is returned. |
| * Register a new implementation with gst_value_register_intersect_func(). |
| * |
| * Returns: %TRUE if the values can intersect |
| */ |
| typedef gboolean (*GstValueIntersectFunc) (GValue * dest, |
| const GValue * value1, const GValue * value2); |
| |
| /* GstValueSubtractFunc: |
| * @dest: (out caller-allocates): a #GValue for the result |
| * @minuend: a #GValue operand |
| * @subtrahend: a #GValue operand |
| * |
| * Used by gst_value_subtract() to perform subtraction for a specific #GValue |
| * type. Register a new implementation with gst_value_register_subtract_func(). |
| * |
| * Returns: %TRUE if the subtraction is not empty |
| */ |
| typedef gboolean (*GstValueSubtractFunc) (GValue * dest, |
| const GValue * minuend, const GValue * subtrahend); |
| |
| static void gst_value_register_union_func (GType type1, |
| GType type2, GstValueUnionFunc func); |
| static void gst_value_register_intersect_func (GType type1, |
| GType type2, GstValueIntersectFunc func); |
| static void gst_value_register_subtract_func (GType minuend_type, |
| GType subtrahend_type, GstValueSubtractFunc func); |
| |
| typedef struct _GstValueUnionInfo GstValueUnionInfo; |
| struct _GstValueUnionInfo |
| { |
| GType type1; |
| GType type2; |
| GstValueUnionFunc func; |
| }; |
| |
| typedef struct _GstValueIntersectInfo GstValueIntersectInfo; |
| struct _GstValueIntersectInfo |
| { |
| GType type1; |
| GType type2; |
| GstValueIntersectFunc func; |
| }; |
| |
| typedef struct _GstValueSubtractInfo GstValueSubtractInfo; |
| struct _GstValueSubtractInfo |
| { |
| GType minuend; |
| GType subtrahend; |
| GstValueSubtractFunc func; |
| }; |
| |
| struct _GstFlagSetClass |
| { |
| GTypeClass parent; |
| GType flags_type; /* Type of the GFlags this flagset carries (can be 0) */ |
| }; |
| |
| typedef struct _GstFlagSetClass GstFlagSetClass; |
| |
| #define FUNDAMENTAL_TYPE_ID_MAX \ |
| (G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) |
| #define FUNDAMENTAL_TYPE_ID(type) \ |
| ((type) >> G_TYPE_FUNDAMENTAL_SHIFT) |
| |
| #define VALUE_LIST_ARRAY(v) ((GArray *) (v)->data[0].v_pointer) |
| #define VALUE_LIST_SIZE(v) (VALUE_LIST_ARRAY(v)->len) |
| #define VALUE_LIST_GET_VALUE(v, index) ((const GValue *) &g_array_index (VALUE_LIST_ARRAY(v), GValue, (index))) |
| |
| static GArray *gst_value_table; |
| static GHashTable *gst_value_hash; |
| static GstValueTable *gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID_MAX + 1]; |
| static GArray *gst_value_union_funcs; |
| static GArray *gst_value_intersect_funcs; |
| static GArray *gst_value_subtract_funcs; |
| |
| /* Forward declarations */ |
| static gchar *gst_value_serialize_fraction (const GValue * value); |
| |
| static GstValueCompareFunc gst_value_get_compare_func (const GValue * value1); |
| static gint gst_value_compare_with_func (const GValue * value1, |
| const GValue * value2, GstValueCompareFunc compare); |
| |
| static gchar *gst_string_wrap (const gchar * s); |
| static gchar *gst_string_take_and_wrap (gchar * s); |
| static gchar *gst_string_unwrap (const gchar * s); |
| |
| static void gst_value_move (GValue * dest, GValue * src); |
| static void _gst_value_list_append_and_take_value (GValue * value, |
| GValue * append_value); |
| static void _gst_value_array_append_and_take_value (GValue * value, |
| GValue * append_value); |
| |
| static inline GstValueTable * |
| gst_value_hash_lookup_type (GType type) |
| { |
| if (G_LIKELY (G_TYPE_IS_FUNDAMENTAL (type))) |
| return gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID (type)]; |
| else |
| return g_hash_table_lookup (gst_value_hash, (gpointer) type); |
| } |
| |
| static void |
| gst_value_hash_add_type (GType type, const GstValueTable * table) |
| { |
| if (G_TYPE_IS_FUNDAMENTAL (type)) |
| gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID (type)] = (gpointer) table; |
| |
| g_hash_table_insert (gst_value_hash, (gpointer) type, (gpointer) table); |
| } |
| |
| /******** |
| * list * |
| ********/ |
| |
| /* two helper functions to serialize/stringify any type of list |
| * regular lists are done with { }, arrays with < > |
| */ |
| static gchar * |
| gst_value_serialize_any_list (const GValue * value, const gchar * begin, |
| const gchar * end) |
| { |
| guint i; |
| GArray *array = value->data[0].v_pointer; |
| GString *s; |
| GValue *v; |
| gchar *s_val; |
| guint alen = array->len; |
| |
| /* estimate minimum string length to minimise re-allocs in GString */ |
| s = g_string_sized_new (2 + (6 * alen) + 2); |
| g_string_append (s, begin); |
| for (i = 0; i < alen; i++) { |
| v = &g_array_index (array, GValue, i); |
| s_val = gst_value_serialize (v); |
| if (s_val != NULL) { |
| g_string_append (s, s_val); |
| g_free (s_val); |
| if (i < alen - 1) { |
| g_string_append_len (s, ", ", 2); |
| } |
| } else { |
| GST_WARNING ("Could not serialize list/array value of type '%s'", |
| G_VALUE_TYPE_NAME (v)); |
| } |
| } |
| g_string_append (s, end); |
| return g_string_free (s, FALSE); |
| } |
| |
| static void |
| gst_value_transform_any_list_string (const GValue * src_value, |
| GValue * dest_value, const gchar * begin, const gchar * end) |
| { |
| GValue *list_value; |
| GArray *array; |
| GString *s; |
| guint i; |
| gchar *list_s; |
| guint alen; |
| |
| array = src_value->data[0].v_pointer; |
| alen = array->len; |
| |
| /* estimate minimum string length to minimise re-allocs in GString */ |
| s = g_string_sized_new (2 + (10 * alen) + 2); |
| g_string_append (s, begin); |
| for (i = 0; i < alen; i++) { |
| list_value = &g_array_index (array, GValue, i); |
| |
| if (i != 0) { |
| g_string_append_len (s, ", ", 2); |
| } |
| list_s = g_strdup_value_contents (list_value); |
| g_string_append (s, list_s); |
| g_free (list_s); |
| } |
| g_string_append (s, end); |
| |
| dest_value->data[0].v_pointer = g_string_free (s, FALSE); |
| } |
| |
| /* |
| * helper function to see if a type is fixed. Is used internally here and |
| * there. Do not export, since it doesn't work for types where the content |
| * decides the fixedness (e.g. GST_TYPE_ARRAY). |
| */ |
| static gboolean |
| gst_type_is_fixed (GType type) |
| { |
| /* the basic int, string, double types */ |
| if (type <= G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) { |
| return TRUE; |
| } |
| /* our fundamental types that are certainly not fixed */ |
| if (type == GST_TYPE_INT_RANGE || type == GST_TYPE_DOUBLE_RANGE || |
| type == GST_TYPE_INT64_RANGE || |
| type == GST_TYPE_LIST || type == GST_TYPE_FRACTION_RANGE) { |
| return FALSE; |
| } |
| /* other (boxed) types that are fixed */ |
| if (type == GST_TYPE_BUFFER) { |
| return TRUE; |
| } |
| /* heavy checks */ |
| if (G_TYPE_IS_FUNDAMENTAL (type) || G_TYPE_FUNDAMENTAL (type) <= |
| G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) { |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| /* GValue functions usable for both regular lists and arrays */ |
| static void |
| gst_value_init_list_or_array (GValue * value) |
| { |
| value->data[0].v_pointer = g_array_new (FALSE, TRUE, sizeof (GValue)); |
| } |
| |
| static GArray * |
| copy_garray_of_gstvalue (const GArray * src) |
| { |
| GArray *dest; |
| guint i, len; |
| |
| len = src->len; |
| dest = g_array_sized_new (FALSE, TRUE, sizeof (GValue), len); |
| g_array_set_size (dest, len); |
| for (i = 0; i < len; i++) { |
| gst_value_init_and_copy (&g_array_index (dest, GValue, i), |
| &g_array_index (src, GValue, i)); |
| } |
| |
| return dest; |
| } |
| |
| static void |
| gst_value_copy_list_or_array (const GValue * src_value, GValue * dest_value) |
| { |
| dest_value->data[0].v_pointer = |
| copy_garray_of_gstvalue ((GArray *) src_value->data[0].v_pointer); |
| } |
| |
| static void |
| gst_value_free_list_or_array (GValue * value) |
| { |
| guint i, len; |
| GArray *src = (GArray *) value->data[0].v_pointer; |
| len = src->len; |
| |
| if ((value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS) == 0) { |
| for (i = 0; i < len; i++) { |
| g_value_unset (&g_array_index (src, GValue, i)); |
| } |
| g_array_free (src, TRUE); |
| } |
| } |
| |
| static gpointer |
| gst_value_list_or_array_peek_pointer (const GValue * value) |
| { |
| return value->data[0].v_pointer; |
| } |
| |
| static gchar * |
| gst_value_collect_list_or_array (GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| if (collect_flags & G_VALUE_NOCOPY_CONTENTS) { |
| value->data[0].v_pointer = collect_values[0].v_pointer; |
| value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS; |
| } else { |
| value->data[0].v_pointer = |
| copy_garray_of_gstvalue ((GArray *) collect_values[0].v_pointer); |
| } |
| return NULL; |
| } |
| |
| static gchar * |
| gst_value_lcopy_list_or_array (const GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| GArray **dest = collect_values[0].v_pointer; |
| |
| if (!dest) |
| return g_strdup_printf ("value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| if (!value->data[0].v_pointer) |
| return g_strdup_printf ("invalid value given for `%s'", |
| G_VALUE_TYPE_NAME (value)); |
| if (collect_flags & G_VALUE_NOCOPY_CONTENTS) { |
| *dest = (GArray *) value->data[0].v_pointer; |
| } else { |
| *dest = copy_garray_of_gstvalue ((GArray *) value->data[0].v_pointer); |
| } |
| return NULL; |
| } |
| |
| static gboolean |
| gst_value_list_or_array_get_basic_type (const GValue * value, GType * type) |
| { |
| if (G_UNLIKELY (value == NULL)) |
| return FALSE; |
| |
| if (GST_VALUE_HOLDS_LIST (value)) { |
| if (VALUE_LIST_SIZE (value) == 0) |
| return FALSE; |
| return gst_value_list_or_array_get_basic_type (VALUE_LIST_GET_VALUE (value, |
| 0), type); |
| } |
| if (GST_VALUE_HOLDS_ARRAY (value)) { |
| const GArray *array = (const GArray *) value->data[0].v_pointer; |
| if (array->len == 0) |
| return FALSE; |
| return gst_value_list_or_array_get_basic_type (&g_array_index (array, |
| GValue, 0), type); |
| } |
| |
| *type = G_VALUE_TYPE (value); |
| |
| return TRUE; |
| } |
| |
| #define IS_RANGE_COMPAT(type1,type2,t1,t2) \ |
| (((t1) == (type1) && (t2) == (type2)) || ((t2) == (type1) && (t1) == (type2))) |
| |
| static gboolean |
| gst_value_list_or_array_are_compatible (const GValue * value1, |
| const GValue * value2) |
| { |
| GType basic_type1, basic_type2; |
| |
| /* empty or same type is OK */ |
| if (!gst_value_list_or_array_get_basic_type (value1, &basic_type1) || |
| !gst_value_list_or_array_get_basic_type (value2, &basic_type2) || |
| basic_type1 == basic_type2) |
| return TRUE; |
| |
| /* ranges are distinct types for each bound type... */ |
| if (IS_RANGE_COMPAT (G_TYPE_INT, GST_TYPE_INT_RANGE, basic_type1, |
| basic_type2)) |
| return TRUE; |
| if (IS_RANGE_COMPAT (G_TYPE_INT64, GST_TYPE_INT64_RANGE, basic_type1, |
| basic_type2)) |
| return TRUE; |
| if (IS_RANGE_COMPAT (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE, basic_type1, |
| basic_type2)) |
| return TRUE; |
| if (IS_RANGE_COMPAT (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE, basic_type1, |
| basic_type2)) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| static inline void |
| _gst_value_list_append_and_take_value (GValue * value, GValue * append_value) |
| { |
| g_array_append_vals ((GArray *) value->data[0].v_pointer, append_value, 1); |
| memset (append_value, 0, sizeof (GValue)); |
| } |
| |
| /** |
| * gst_value_list_append_and_take_value: |
| * @value: a #GValue of type #GST_TYPE_LIST |
| * @append_value: (transfer full): the value to append |
| * |
| * Appends @append_value to the GstValueList in @value. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_value_list_append_and_take_value (GValue * value, GValue * append_value) |
| { |
| g_return_if_fail (GST_VALUE_HOLDS_LIST (value)); |
| g_return_if_fail (G_IS_VALUE (append_value)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value, |
| append_value)); |
| |
| _gst_value_list_append_and_take_value (value, append_value); |
| } |
| |
| /** |
| * gst_value_list_append_value: |
| * @value: a #GValue of type #GST_TYPE_LIST |
| * @append_value: (transfer none): the value to append |
| * |
| * Appends @append_value to the GstValueList in @value. |
| */ |
| void |
| gst_value_list_append_value (GValue * value, const GValue * append_value) |
| { |
| GValue val = { 0, }; |
| |
| g_return_if_fail (GST_VALUE_HOLDS_LIST (value)); |
| g_return_if_fail (G_IS_VALUE (append_value)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value, |
| append_value)); |
| |
| gst_value_init_and_copy (&val, append_value); |
| g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1); |
| } |
| |
| /** |
| * gst_value_list_prepend_value: |
| * @value: a #GValue of type #GST_TYPE_LIST |
| * @prepend_value: the value to prepend |
| * |
| * Prepends @prepend_value to the GstValueList in @value. |
| */ |
| void |
| gst_value_list_prepend_value (GValue * value, const GValue * prepend_value) |
| { |
| GValue val = { 0, }; |
| |
| g_return_if_fail (GST_VALUE_HOLDS_LIST (value)); |
| g_return_if_fail (G_IS_VALUE (prepend_value)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value, |
| prepend_value)); |
| |
| gst_value_init_and_copy (&val, prepend_value); |
| g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1); |
| } |
| |
| /** |
| * gst_value_list_concat: |
| * @dest: (out caller-allocates): an uninitialized #GValue to take the result |
| * @value1: a #GValue |
| * @value2: a #GValue |
| * |
| * Concatenates copies of @value1 and @value2 into a list. Values that are not |
| * of type #GST_TYPE_LIST are treated as if they were lists of length 1. |
| * @dest will be initialized to the type #GST_TYPE_LIST. |
| */ |
| void |
| gst_value_list_concat (GValue * dest, const GValue * value1, |
| const GValue * value2) |
| { |
| guint i, value1_length, value2_length; |
| GArray *array; |
| |
| g_return_if_fail (dest != NULL); |
| g_return_if_fail (G_VALUE_TYPE (dest) == 0); |
| g_return_if_fail (G_IS_VALUE (value1)); |
| g_return_if_fail (G_IS_VALUE (value2)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value1, value2)); |
| |
| value1_length = |
| (GST_VALUE_HOLDS_LIST (value1) ? VALUE_LIST_SIZE (value1) : 1); |
| value2_length = |
| (GST_VALUE_HOLDS_LIST (value2) ? VALUE_LIST_SIZE (value2) : 1); |
| g_value_init (dest, GST_TYPE_LIST); |
| array = (GArray *) dest->data[0].v_pointer; |
| g_array_set_size (array, value1_length + value2_length); |
| |
| if (GST_VALUE_HOLDS_LIST (value1)) { |
| for (i = 0; i < value1_length; i++) { |
| gst_value_init_and_copy (&g_array_index (array, GValue, i), |
| VALUE_LIST_GET_VALUE (value1, i)); |
| } |
| } else { |
| gst_value_init_and_copy (&g_array_index (array, GValue, 0), value1); |
| } |
| |
| if (GST_VALUE_HOLDS_LIST (value2)) { |
| for (i = 0; i < value2_length; i++) { |
| gst_value_init_and_copy (&g_array_index (array, GValue, |
| i + value1_length), VALUE_LIST_GET_VALUE (value2, i)); |
| } |
| } else { |
| gst_value_init_and_copy (&g_array_index (array, GValue, value1_length), |
| value2); |
| } |
| } |
| |
| /* same as gst_value_list_concat() but takes ownership of GValues */ |
| static void |
| gst_value_list_concat_and_take_values (GValue * dest, GValue * val1, |
| GValue * val2) |
| { |
| guint i, val1_length, val2_length; |
| gboolean val1_is_list; |
| gboolean val2_is_list; |
| GArray *array; |
| |
| g_assert (dest != NULL); |
| g_assert (G_VALUE_TYPE (dest) == 0); |
| g_assert (G_IS_VALUE (val1)); |
| g_assert (G_IS_VALUE (val2)); |
| g_assert (gst_value_list_or_array_are_compatible (val1, val2)); |
| |
| val1_is_list = GST_VALUE_HOLDS_LIST (val1); |
| val1_length = (val1_is_list ? VALUE_LIST_SIZE (val1) : 1); |
| |
| val2_is_list = GST_VALUE_HOLDS_LIST (val2); |
| val2_length = (val2_is_list ? VALUE_LIST_SIZE (val2) : 1); |
| |
| g_value_init (dest, GST_TYPE_LIST); |
| array = (GArray *) dest->data[0].v_pointer; |
| g_array_set_size (array, val1_length + val2_length); |
| |
| if (val1_is_list) { |
| for (i = 0; i < val1_length; i++) { |
| g_array_index (array, GValue, i) = *VALUE_LIST_GET_VALUE (val1, i); |
| } |
| g_array_set_size (VALUE_LIST_ARRAY (val1), 0); |
| g_value_unset (val1); |
| } else { |
| g_array_index (array, GValue, 0) = *val1; |
| G_VALUE_TYPE (val1) = G_TYPE_INVALID; |
| } |
| |
| if (val2_is_list) { |
| for (i = 0; i < val2_length; i++) { |
| const GValue *v2 = VALUE_LIST_GET_VALUE (val2, i); |
| g_array_index (array, GValue, i + val1_length) = *v2; |
| } |
| g_array_set_size (VALUE_LIST_ARRAY (val2), 0); |
| g_value_unset (val2); |
| } else { |
| g_array_index (array, GValue, val1_length) = *val2; |
| G_VALUE_TYPE (val2) = G_TYPE_INVALID; |
| } |
| } |
| |
| /** |
| * gst_value_list_merge: |
| * @dest: (out caller-allocates): an uninitialized #GValue to take the result |
| * @value1: a #GValue |
| * @value2: a #GValue |
| * |
| * Merges copies of @value1 and @value2. Values that are not |
| * of type #GST_TYPE_LIST are treated as if they were lists of length 1. |
| * |
| * The result will be put into @dest and will either be a list that will not |
| * contain any duplicates, or a non-list type (if @value1 and @value2 |
| * were equal). |
| */ |
| void |
| gst_value_list_merge (GValue * dest, const GValue * value1, |
| const GValue * value2) |
| { |
| guint i, j, k, value1_length, value2_length, skipped; |
| const GValue *src; |
| gboolean skip; |
| GArray *array; |
| |
| g_return_if_fail (dest != NULL); |
| g_return_if_fail (G_VALUE_TYPE (dest) == 0); |
| g_return_if_fail (G_IS_VALUE (value1)); |
| g_return_if_fail (G_IS_VALUE (value2)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value1, value2)); |
| |
| value1_length = |
| (GST_VALUE_HOLDS_LIST (value1) ? VALUE_LIST_SIZE (value1) : 1); |
| value2_length = |
| (GST_VALUE_HOLDS_LIST (value2) ? VALUE_LIST_SIZE (value2) : 1); |
| g_value_init (dest, GST_TYPE_LIST); |
| array = (GArray *) dest->data[0].v_pointer; |
| g_array_set_size (array, value1_length + value2_length); |
| |
| if (GST_VALUE_HOLDS_LIST (value1)) { |
| for (i = 0; i < value1_length; i++) { |
| gst_value_init_and_copy (&g_array_index (array, GValue, i), |
| VALUE_LIST_GET_VALUE (value1, i)); |
| } |
| } else { |
| gst_value_init_and_copy (&g_array_index (array, GValue, 0), value1); |
| } |
| |
| j = value1_length; |
| skipped = 0; |
| if (GST_VALUE_HOLDS_LIST (value2)) { |
| for (i = 0; i < value2_length; i++) { |
| skip = FALSE; |
| src = VALUE_LIST_GET_VALUE (value2, i); |
| for (k = 0; k < value1_length; k++) { |
| if (gst_value_compare (&g_array_index (array, GValue, k), |
| src) == GST_VALUE_EQUAL) { |
| skip = TRUE; |
| skipped++; |
| break; |
| } |
| } |
| if (!skip) { |
| gst_value_init_and_copy (&g_array_index (array, GValue, j), src); |
| j++; |
| } |
| } |
| } else { |
| skip = FALSE; |
| for (k = 0; k < value1_length; k++) { |
| if (gst_value_compare (&g_array_index (array, GValue, k), |
| value2) == GST_VALUE_EQUAL) { |
| skip = TRUE; |
| skipped++; |
| break; |
| } |
| } |
| if (!skip) { |
| gst_value_init_and_copy (&g_array_index (array, GValue, j), value2); |
| } |
| } |
| if (skipped) { |
| guint new_size = value1_length + (value2_length - skipped); |
| |
| if (new_size > 1) { |
| /* shrink list */ |
| g_array_set_size (array, new_size); |
| } else { |
| GValue single_dest; |
| |
| /* size is 1, take single value in list and make it new dest */ |
| single_dest = g_array_index (array, GValue, 0); |
| |
| /* clean up old value allocations: must set array size to 0, because |
| * allocated values are not inited meaning g_value_unset() will not |
| * work on them */ |
| g_array_set_size (array, 0); |
| g_value_unset (dest); |
| |
| /* the single value is our new result */ |
| *dest = single_dest; |
| } |
| } |
| } |
| |
| /** |
| * gst_value_list_get_size: |
| * @value: a #GValue of type #GST_TYPE_LIST |
| * |
| * Gets the number of values contained in @value. |
| * |
| * Returns: the number of values |
| */ |
| guint |
| gst_value_list_get_size (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), 0); |
| |
| return ((GArray *) value->data[0].v_pointer)->len; |
| } |
| |
| /** |
| * gst_value_list_get_value: |
| * @value: a #GValue of type #GST_TYPE_LIST |
| * @index: index of value to get from the list |
| * |
| * Gets the value that is a member of the list contained in @value and |
| * has the index @index. |
| * |
| * Returns: (transfer none): the value at the given index |
| */ |
| const GValue * |
| gst_value_list_get_value (const GValue * value, guint index) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), NULL); |
| g_return_val_if_fail (index < VALUE_LIST_SIZE (value), NULL); |
| |
| return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer, |
| GValue, index); |
| } |
| |
| /** |
| * gst_value_array_append_value: |
| * @value: a #GValue of type #GST_TYPE_ARRAY |
| * @append_value: the value to append |
| * |
| * Appends @append_value to the GstValueArray in @value. |
| */ |
| void |
| gst_value_array_append_value (GValue * value, const GValue * append_value) |
| { |
| GValue val = { 0, }; |
| |
| g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value)); |
| g_return_if_fail (G_IS_VALUE (append_value)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value, |
| append_value)); |
| |
| gst_value_init_and_copy (&val, append_value); |
| g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1); |
| } |
| |
| static inline void |
| _gst_value_array_append_and_take_value (GValue * value, GValue * append_value) |
| { |
| g_array_append_vals ((GArray *) value->data[0].v_pointer, append_value, 1); |
| memset (append_value, 0, sizeof (GValue)); |
| } |
| |
| /** |
| * gst_value_array_append_and_take_value: |
| * @value: a #GValue of type #GST_TYPE_ARRAY |
| * @append_value: (transfer full): the value to append |
| * |
| * Appends @append_value to the GstValueArray in @value. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_value_array_append_and_take_value (GValue * value, GValue * append_value) |
| { |
| g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value)); |
| g_return_if_fail (G_IS_VALUE (append_value)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value, |
| append_value)); |
| |
| _gst_value_array_append_and_take_value (value, append_value); |
| } |
| |
| /** |
| * gst_value_array_prepend_value: |
| * @value: a #GValue of type #GST_TYPE_ARRAY |
| * @prepend_value: the value to prepend |
| * |
| * Prepends @prepend_value to the GstValueArray in @value. |
| */ |
| void |
| gst_value_array_prepend_value (GValue * value, const GValue * prepend_value) |
| { |
| GValue val = { 0, }; |
| |
| g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value)); |
| g_return_if_fail (G_IS_VALUE (prepend_value)); |
| g_return_if_fail (gst_value_list_or_array_are_compatible (value, |
| prepend_value)); |
| |
| gst_value_init_and_copy (&val, prepend_value); |
| g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1); |
| } |
| |
| /** |
| * gst_value_array_get_size: |
| * @value: a #GValue of type #GST_TYPE_ARRAY |
| * |
| * Gets the number of values contained in @value. |
| * |
| * Returns: the number of values |
| */ |
| guint |
| gst_value_array_get_size (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), 0); |
| |
| return ((GArray *) value->data[0].v_pointer)->len; |
| } |
| |
| /** |
| * gst_value_array_get_value: |
| * @value: a #GValue of type #GST_TYPE_ARRAY |
| * @index: index of value to get from the array |
| * |
| * Gets the value that is a member of the array contained in @value and |
| * has the index @index. |
| * |
| * Returns: (transfer none): the value at the given index |
| */ |
| const GValue * |
| gst_value_array_get_value (const GValue * value, guint index) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), NULL); |
| g_return_val_if_fail (index < gst_value_array_get_size (value), NULL); |
| |
| return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer, |
| GValue, index); |
| } |
| |
| static void |
| gst_value_transform_list_string (const GValue * src_value, GValue * dest_value) |
| { |
| gst_value_transform_any_list_string (src_value, dest_value, "{ ", " }"); |
| } |
| |
| static void |
| gst_value_transform_array_string (const GValue * src_value, GValue * dest_value) |
| { |
| gst_value_transform_any_list_string (src_value, dest_value, "< ", " >"); |
| } |
| |
| /* Do an unordered compare of the contents of a list */ |
| static gint |
| gst_value_compare_value_list (const GValue * value1, const GValue * value2) |
| { |
| guint i, j; |
| GArray *array1 = value1->data[0].v_pointer; |
| GArray *array2 = value2->data[0].v_pointer; |
| GValue *v1; |
| GValue *v2; |
| gint len, to_remove; |
| guint8 *removed; |
| GstValueCompareFunc compare; |
| |
| /* get length and do initial length check. */ |
| len = array1->len; |
| if (len != array2->len) |
| return GST_VALUE_UNORDERED; |
| |
| /* place to mark removed value indices of array2 */ |
| removed = g_newa (guint8, len); |
| memset (removed, 0, len); |
| to_remove = len; |
| |
| /* loop over array1, all items should be in array2. When we find an |
| * item in array2, remove it from array2 by marking it as removed */ |
| for (i = 0; i < len; i++) { |
| v1 = &g_array_index (array1, GValue, i); |
| if ((compare = gst_value_get_compare_func (v1))) { |
| for (j = 0; j < len; j++) { |
| /* item is removed, we can skip it */ |
| if (removed[j]) |
| continue; |
| v2 = &g_array_index (array2, GValue, j); |
| if (gst_value_compare_with_func (v1, v2, compare) == GST_VALUE_EQUAL) { |
| /* mark item as removed now that we found it in array2 and |
| * decrement the number of remaining items in array2. */ |
| removed[j] = 1; |
| to_remove--; |
| break; |
| } |
| } |
| /* item in array1 and not in array2, UNORDERED */ |
| if (j == len) |
| return GST_VALUE_UNORDERED; |
| } else |
| return GST_VALUE_UNORDERED; |
| } |
| /* if not all items were removed, array2 contained something not in array1 */ |
| if (to_remove != 0) |
| return GST_VALUE_UNORDERED; |
| |
| /* arrays are equal */ |
| return GST_VALUE_EQUAL; |
| } |
| |
| /* Perform an ordered comparison of the contents of an array */ |
| static gint |
| gst_value_compare_value_array (const GValue * value1, const GValue * value2) |
| { |
| guint i; |
| GArray *array1 = value1->data[0].v_pointer; |
| GArray *array2 = value2->data[0].v_pointer; |
| guint len = array1->len; |
| GValue *v1; |
| GValue *v2; |
| |
| if (len != array2->len) |
| return GST_VALUE_UNORDERED; |
| |
| for (i = 0; i < len; i++) { |
| v1 = &g_array_index (array1, GValue, i); |
| v2 = &g_array_index (array2, GValue, i); |
| if (gst_value_compare (v1, v2) != GST_VALUE_EQUAL) |
| return GST_VALUE_UNORDERED; |
| } |
| |
| return GST_VALUE_EQUAL; |
| } |
| |
| static gchar * |
| gst_value_serialize_value_list (const GValue * value) |
| { |
| return gst_value_serialize_any_list (value, "{ ", " }"); |
| } |
| |
| static gboolean |
| gst_value_deserialize_value_list (GValue * dest, const gchar * s) |
| { |
| g_warning ("gst_value_deserialize_list: unimplemented"); |
| return FALSE; |
| } |
| |
| static gchar * |
| gst_value_serialize_value_array (const GValue * value) |
| { |
| return gst_value_serialize_any_list (value, "< ", " >"); |
| } |
| |
| static gboolean |
| gst_value_deserialize_value_array (GValue * dest, const gchar * s) |
| { |
| g_warning ("gst_value_deserialize_array: unimplemented"); |
| return FALSE; |
| } |
| |
| /************* |
| * int range * |
| * |
| * Values in the range are defined as any value greater or equal |
| * to min*step, AND lesser or equal to max*step. |
| * For step == 1, this falls back to the traditional range semantics. |
| * |
| * data[0] = (min << 32) | (max) |
| * data[1] = step |
| * |
| *************/ |
| |
| #define INT_RANGE_MIN(v) ((gint) (((v)->data[0].v_uint64) >> 32)) |
| #define INT_RANGE_MAX(v) ((gint) (((v)->data[0].v_uint64) & 0xffffffff)) |
| #define INT_RANGE_STEP(v) ((v)->data[1].v_int) |
| |
| static void |
| gst_value_init_int_range (GValue * value) |
| { |
| G_STATIC_ASSERT (sizeof (gint) <= 2 * sizeof (guint64)); |
| |
| value->data[0].v_uint64 = 0; |
| value->data[1].v_int = 1; |
| } |
| |
| static void |
| gst_value_copy_int_range (const GValue * src_value, GValue * dest_value) |
| { |
| dest_value->data[0].v_uint64 = src_value->data[0].v_uint64; |
| dest_value->data[1].v_int = src_value->data[1].v_int; |
| } |
| |
| static gchar * |
| gst_value_collect_int_range (GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| if (n_collect_values != 2) |
| return g_strdup_printf ("not enough value locations for `%s' passed", |
| G_VALUE_TYPE_NAME (value)); |
| if (collect_values[0].v_int >= collect_values[1].v_int) |
| return g_strdup_printf ("range start is not smaller than end for `%s'", |
| G_VALUE_TYPE_NAME (value)); |
| |
| gst_value_set_int_range_step (value, collect_values[0].v_int, |
| collect_values[1].v_int, 1); |
| |
| return NULL; |
| } |
| |
| static gchar * |
| gst_value_lcopy_int_range (const GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| guint32 *int_range_start = collect_values[0].v_pointer; |
| guint32 *int_range_end = collect_values[1].v_pointer; |
| |
| if (!int_range_start) |
| return g_strdup_printf ("start value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| if (!int_range_end) |
| return g_strdup_printf ("end value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| |
| *int_range_start = INT_RANGE_MIN (value); |
| *int_range_end = INT_RANGE_MAX (value); |
| |
| return NULL; |
| } |
| |
| /** |
| * gst_value_set_int_range_step: |
| * @value: a GValue initialized to GST_TYPE_INT_RANGE |
| * @start: the start of the range |
| * @end: the end of the range |
| * @step: the step of the range |
| * |
| * Sets @value to the range specified by @start, @end and @step. |
| */ |
| void |
| gst_value_set_int_range_step (GValue * value, gint start, gint end, gint step) |
| { |
| guint64 sstart, sstop; |
| |
| g_return_if_fail (GST_VALUE_HOLDS_INT_RANGE (value)); |
| g_return_if_fail (start < end); |
| g_return_if_fail (step > 0); |
| g_return_if_fail (start % step == 0); |
| g_return_if_fail (end % step == 0); |
| |
| sstart = (guint) (start / step); |
| sstop = (guint) (end / step); |
| value->data[0].v_uint64 = (sstart << 32) | sstop; |
| value->data[1].v_int = step; |
| } |
| |
| /** |
| * gst_value_set_int_range: |
| * @value: a GValue initialized to GST_TYPE_INT_RANGE |
| * @start: the start of the range |
| * @end: the end of the range |
| * |
| * Sets @value to the range specified by @start and @end. |
| */ |
| void |
| gst_value_set_int_range (GValue * value, gint start, gint end) |
| { |
| gst_value_set_int_range_step (value, start, end, 1); |
| } |
| |
| /** |
| * gst_value_get_int_range_min: |
| * @value: a GValue initialized to GST_TYPE_INT_RANGE |
| * |
| * Gets the minimum of the range specified by @value. |
| * |
| * Returns: the minimum of the range |
| */ |
| gint |
| gst_value_get_int_range_min (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0); |
| |
| return INT_RANGE_MIN (value) * INT_RANGE_STEP (value); |
| } |
| |
| /** |
| * gst_value_get_int_range_max: |
| * @value: a GValue initialized to GST_TYPE_INT_RANGE |
| * |
| * Gets the maximum of the range specified by @value. |
| * |
| * Returns: the maximum of the range |
| */ |
| gint |
| gst_value_get_int_range_max (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0); |
| |
| return INT_RANGE_MAX (value) * INT_RANGE_STEP (value); |
| } |
| |
| /** |
| * gst_value_get_int_range_step: |
| * @value: a GValue initialized to GST_TYPE_INT_RANGE |
| * |
| * Gets the step of the range specified by @value. |
| * |
| * Returns: the step of the range |
| */ |
| gint |
| gst_value_get_int_range_step (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0); |
| |
| return INT_RANGE_STEP (value); |
| } |
| |
| static void |
| gst_value_transform_int_range_string (const GValue * src_value, |
| GValue * dest_value) |
| { |
| if (INT_RANGE_STEP (src_value) == 1) |
| dest_value->data[0].v_pointer = g_strdup_printf ("[%d,%d]", |
| INT_RANGE_MIN (src_value), INT_RANGE_MAX (src_value)); |
| else |
| dest_value->data[0].v_pointer = g_strdup_printf ("[%d,%d,%d]", |
| INT_RANGE_MIN (src_value) * INT_RANGE_STEP (src_value), |
| INT_RANGE_MAX (src_value) * INT_RANGE_STEP (src_value), |
| INT_RANGE_STEP (src_value)); |
| } |
| |
| static gint |
| gst_value_compare_int_range (const GValue * value1, const GValue * value2) |
| { |
| /* calculate the number of values in each range */ |
| gint n1 = INT_RANGE_MAX (value1) - INT_RANGE_MIN (value1) + 1; |
| gint n2 = INT_RANGE_MAX (value2) - INT_RANGE_MIN (value2) + 1; |
| |
| /* they must be equal */ |
| if (n1 != n2) |
| return GST_VALUE_UNORDERED; |
| |
| /* if empty, equal */ |
| if (n1 == 0) |
| return GST_VALUE_EQUAL; |
| |
| /* if more than one value, then it is only equal if the step is equal |
| and bounds lie on the same value */ |
| if (n1 > 1) { |
| if (INT_RANGE_STEP (value1) == INT_RANGE_STEP (value2) && |
| INT_RANGE_MIN (value1) == INT_RANGE_MIN (value2) && |
| INT_RANGE_MAX (value1) == INT_RANGE_MAX (value2)) { |
| return GST_VALUE_EQUAL; |
| } |
| return GST_VALUE_UNORDERED; |
| } else { |
| /* if just one, only if the value is equal */ |
| if (INT_RANGE_MIN (value1) == INT_RANGE_MIN (value2)) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| } |
| |
| static gchar * |
| gst_value_serialize_int_range (const GValue * value) |
| { |
| if (INT_RANGE_STEP (value) == 1) |
| return g_strdup_printf ("[ %d, %d ]", INT_RANGE_MIN (value), |
| INT_RANGE_MAX (value)); |
| else |
| return g_strdup_printf ("[ %d, %d, %d ]", |
| INT_RANGE_MIN (value) * INT_RANGE_STEP (value), |
| INT_RANGE_MAX (value) * INT_RANGE_STEP (value), INT_RANGE_STEP (value)); |
| } |
| |
| static gboolean |
| gst_value_deserialize_int_range (GValue * dest, const gchar * s) |
| { |
| g_warning ("unimplemented"); |
| return FALSE; |
| } |
| |
| /*************** |
| * int64 range * |
| * |
| * Values in the range are defined as any value greater or equal |
| * to min*step, AND lesser or equal to max*step. |
| * For step == 1, this falls back to the traditional range semantics. |
| ***************/ |
| |
| #define INT64_RANGE_MIN(v) (((gint64 *)((v)->data[0].v_pointer))[0]) |
| #define INT64_RANGE_MAX(v) (((gint64 *)((v)->data[0].v_pointer))[1]) |
| #define INT64_RANGE_STEP(v) (((gint64 *)((v)->data[0].v_pointer))[2]) |
| |
| static void |
| gst_value_init_int64_range (GValue * value) |
| { |
| gint64 *vals = g_slice_alloc0 (3 * sizeof (gint64)); |
| value->data[0].v_pointer = vals; |
| INT64_RANGE_MIN (value) = 0; |
| INT64_RANGE_MAX (value) = 0; |
| INT64_RANGE_STEP (value) = 1; |
| } |
| |
| static void |
| gst_value_free_int64_range (GValue * value) |
| { |
| g_return_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value)); |
| g_slice_free1 (3 * sizeof (gint64), value->data[0].v_pointer); |
| value->data[0].v_pointer = NULL; |
| } |
| |
| static void |
| gst_value_copy_int64_range (const GValue * src_value, GValue * dest_value) |
| { |
| gint64 *vals = (gint64 *) dest_value->data[0].v_pointer; |
| gint64 *src_vals = (gint64 *) src_value->data[0].v_pointer; |
| |
| if (vals == NULL) { |
| gst_value_init_int64_range (dest_value); |
| } |
| |
| if (src_vals != NULL) { |
| INT64_RANGE_MIN (dest_value) = INT64_RANGE_MIN (src_value); |
| INT64_RANGE_MAX (dest_value) = INT64_RANGE_MAX (src_value); |
| INT64_RANGE_STEP (dest_value) = INT64_RANGE_STEP (src_value); |
| } |
| } |
| |
| static gchar * |
| gst_value_collect_int64_range (GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| gint64 *vals = value->data[0].v_pointer; |
| |
| if (n_collect_values != 2) |
| return g_strdup_printf ("not enough value locations for `%s' passed", |
| G_VALUE_TYPE_NAME (value)); |
| if (collect_values[0].v_int64 >= collect_values[1].v_int64) |
| return g_strdup_printf ("range start is not smaller than end for `%s'", |
| G_VALUE_TYPE_NAME (value)); |
| |
| if (vals == NULL) { |
| gst_value_init_int64_range (value); |
| } |
| |
| gst_value_set_int64_range_step (value, collect_values[0].v_int64, |
| collect_values[1].v_int64, 1); |
| |
| return NULL; |
| } |
| |
| static gchar * |
| gst_value_lcopy_int64_range (const GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| guint64 *int_range_start = collect_values[0].v_pointer; |
| guint64 *int_range_end = collect_values[1].v_pointer; |
| guint64 *int_range_step = collect_values[2].v_pointer; |
| gint64 *vals = (gint64 *) value->data[0].v_pointer; |
| |
| if (!int_range_start) |
| return g_strdup_printf ("start value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| if (!int_range_end) |
| return g_strdup_printf ("end value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| if (!int_range_step) |
| return g_strdup_printf ("step value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| |
| if (G_UNLIKELY (vals == NULL)) { |
| return g_strdup_printf ("Uninitialised `%s' passed", |
| G_VALUE_TYPE_NAME (value)); |
| } |
| |
| *int_range_start = INT64_RANGE_MIN (value); |
| *int_range_end = INT64_RANGE_MAX (value); |
| *int_range_step = INT64_RANGE_STEP (value); |
| |
| return NULL; |
| } |
| |
| /** |
| * gst_value_set_int64_range_step: |
| * @value: a GValue initialized to GST_TYPE_INT64_RANGE |
| * @start: the start of the range |
| * @end: the end of the range |
| * @step: the step of the range |
| * |
| * Sets @value to the range specified by @start, @end and @step. |
| */ |
| void |
| gst_value_set_int64_range_step (GValue * value, gint64 start, gint64 end, |
| gint64 step) |
| { |
| g_return_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value)); |
| g_return_if_fail (start < end); |
| g_return_if_fail (step > 0); |
| g_return_if_fail (start % step == 0); |
| g_return_if_fail (end % step == 0); |
| |
| INT64_RANGE_MIN (value) = start / step; |
| INT64_RANGE_MAX (value) = end / step; |
| INT64_RANGE_STEP (value) = step; |
| } |
| |
| /** |
| * gst_value_set_int64_range: |
| * @value: a GValue initialized to GST_TYPE_INT64_RANGE |
| * @start: the start of the range |
| * @end: the end of the range |
| * |
| * Sets @value to the range specified by @start and @end. |
| */ |
| void |
| gst_value_set_int64_range (GValue * value, gint64 start, gint64 end) |
| { |
| gst_value_set_int64_range_step (value, start, end, 1); |
| } |
| |
| /** |
| * gst_value_get_int64_range_min: |
| * @value: a GValue initialized to GST_TYPE_INT64_RANGE |
| * |
| * Gets the minimum of the range specified by @value. |
| * |
| * Returns: the minimum of the range |
| */ |
| gint64 |
| gst_value_get_int64_range_min (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value), 0); |
| |
| return INT64_RANGE_MIN (value) * INT64_RANGE_STEP (value); |
| } |
| |
| /** |
| * gst_value_get_int64_range_max: |
| * @value: a GValue initialized to GST_TYPE_INT64_RANGE |
| * |
| * Gets the maximum of the range specified by @value. |
| * |
| * Returns: the maximum of the range |
| */ |
| gint64 |
| gst_value_get_int64_range_max (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value), 0); |
| |
| return INT64_RANGE_MAX (value) * INT64_RANGE_STEP (value); |
| } |
| |
| /** |
| * gst_value_get_int64_range_step: |
| * @value: a GValue initialized to GST_TYPE_INT64_RANGE |
| * |
| * Gets the step of the range specified by @value. |
| * |
| * Returns: the step of the range |
| */ |
| gint64 |
| gst_value_get_int64_range_step (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_INT64_RANGE (value), 0); |
| |
| return INT64_RANGE_STEP (value); |
| } |
| |
| static void |
| gst_value_transform_int64_range_string (const GValue * src_value, |
| GValue * dest_value) |
| { |
| if (INT64_RANGE_STEP (src_value) == 1) |
| dest_value->data[0].v_pointer = |
| g_strdup_printf ("(gint64)[%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT "]", |
| INT64_RANGE_MIN (src_value), INT64_RANGE_MAX (src_value)); |
| else |
| dest_value->data[0].v_pointer = |
| g_strdup_printf ("(gint64)[%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT |
| ",%" G_GINT64_FORMAT "]", |
| INT64_RANGE_MIN (src_value) * INT64_RANGE_STEP (src_value), |
| INT64_RANGE_MAX (src_value) * INT64_RANGE_STEP (src_value), |
| INT64_RANGE_STEP (src_value)); |
| } |
| |
| static gint |
| gst_value_compare_int64_range (const GValue * value1, const GValue * value2) |
| { |
| /* calculate the number of values in each range */ |
| gint64 n1 = INT64_RANGE_MAX (value1) - INT64_RANGE_MIN (value1) + 1; |
| gint64 n2 = INT64_RANGE_MAX (value2) - INT64_RANGE_MIN (value2) + 1; |
| |
| /* they must be equal */ |
| if (n1 != n2) |
| return GST_VALUE_UNORDERED; |
| |
| /* if empty, equal */ |
| if (n1 == 0) |
| return GST_VALUE_EQUAL; |
| |
| /* if more than one value, then it is only equal if the step is equal |
| and bounds lie on the same value */ |
| if (n1 > 1) { |
| if (INT64_RANGE_STEP (value1) == INT64_RANGE_STEP (value2) && |
| INT64_RANGE_MIN (value1) == INT64_RANGE_MIN (value2) && |
| INT64_RANGE_MAX (value1) == INT64_RANGE_MAX (value2)) { |
| return GST_VALUE_EQUAL; |
| } |
| return GST_VALUE_UNORDERED; |
| } else { |
| /* if just one, only if the value is equal */ |
| if (INT64_RANGE_MIN (value1) == INT64_RANGE_MIN (value2)) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| } |
| |
| static gchar * |
| gst_value_serialize_int64_range (const GValue * value) |
| { |
| if (INT64_RANGE_STEP (value) == 1) |
| return g_strdup_printf ("[ %" G_GINT64_FORMAT ", %" G_GINT64_FORMAT " ]", |
| INT64_RANGE_MIN (value), INT64_RANGE_MAX (value)); |
| else |
| return g_strdup_printf ("[ %" G_GINT64_FORMAT ", %" G_GINT64_FORMAT ", %" |
| G_GINT64_FORMAT " ]", |
| INT64_RANGE_MIN (value) * INT64_RANGE_STEP (value), |
| INT64_RANGE_MAX (value) * INT64_RANGE_STEP (value), |
| INT64_RANGE_STEP (value)); |
| } |
| |
| static gboolean |
| gst_value_deserialize_int64_range (GValue * dest, const gchar * s) |
| { |
| g_warning ("unimplemented"); |
| return FALSE; |
| } |
| |
| /**************** |
| * double range * |
| ****************/ |
| |
| static void |
| gst_value_init_double_range (GValue * value) |
| { |
| value->data[0].v_double = 0; |
| value->data[1].v_double = 0; |
| } |
| |
| static void |
| gst_value_copy_double_range (const GValue * src_value, GValue * dest_value) |
| { |
| dest_value->data[0].v_double = src_value->data[0].v_double; |
| dest_value->data[1].v_double = src_value->data[1].v_double; |
| } |
| |
| static gchar * |
| gst_value_collect_double_range (GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| if (n_collect_values != 2) |
| return g_strdup_printf ("not enough value locations for `%s' passed", |
| G_VALUE_TYPE_NAME (value)); |
| if (collect_values[0].v_double >= collect_values[1].v_double) |
| return g_strdup_printf ("range start is not smaller than end for `%s'", |
| G_VALUE_TYPE_NAME (value)); |
| |
| value->data[0].v_double = collect_values[0].v_double; |
| value->data[1].v_double = collect_values[1].v_double; |
| |
| return NULL; |
| } |
| |
| static gchar * |
| gst_value_lcopy_double_range (const GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| gdouble *double_range_start = collect_values[0].v_pointer; |
| gdouble *double_range_end = collect_values[1].v_pointer; |
| |
| if (!double_range_start) |
| return g_strdup_printf ("start value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| if (!double_range_end) |
| return g_strdup_printf ("end value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| |
| *double_range_start = value->data[0].v_double; |
| *double_range_end = value->data[1].v_double; |
| |
| return NULL; |
| } |
| |
| /** |
| * gst_value_set_double_range: |
| * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE |
| * @start: the start of the range |
| * @end: the end of the range |
| * |
| * Sets @value to the range specified by @start and @end. |
| */ |
| void |
| gst_value_set_double_range (GValue * value, gdouble start, gdouble end) |
| { |
| g_return_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value)); |
| g_return_if_fail (start < end); |
| |
| value->data[0].v_double = start; |
| value->data[1].v_double = end; |
| } |
| |
| /** |
| * gst_value_get_double_range_min: |
| * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE |
| * |
| * Gets the minimum of the range specified by @value. |
| * |
| * Returns: the minimum of the range |
| */ |
| gdouble |
| gst_value_get_double_range_min (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0); |
| |
| return value->data[0].v_double; |
| } |
| |
| /** |
| * gst_value_get_double_range_max: |
| * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE |
| * |
| * Gets the maximum of the range specified by @value. |
| * |
| * Returns: the maximum of the range |
| */ |
| gdouble |
| gst_value_get_double_range_max (const GValue * value) |
| { |
| g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0); |
| |
| return value->data[1].v_double; |
| } |
| |
| static void |
| gst_value_transform_double_range_string (const GValue * src_value, |
| GValue * dest_value) |
| { |
| gchar s1[G_ASCII_DTOSTR_BUF_SIZE], s2[G_ASCII_DTOSTR_BUF_SIZE]; |
| |
| dest_value->data[0].v_pointer = g_strdup_printf ("[%s,%s]", |
| g_ascii_dtostr (s1, G_ASCII_DTOSTR_BUF_SIZE, |
| src_value->data[0].v_double), |
| g_ascii_dtostr (s2, G_ASCII_DTOSTR_BUF_SIZE, |
| src_value->data[1].v_double)); |
| } |
| |
| static gint |
| gst_value_compare_double_range (const GValue * value1, const GValue * value2) |
| { |
| if (value2->data[0].v_double == value1->data[0].v_double && |
| value2->data[1].v_double == value1->data[1].v_double) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| |
| static gchar * |
| gst_value_serialize_double_range (const GValue * value) |
| { |
| gchar d1[G_ASCII_DTOSTR_BUF_SIZE]; |
| gchar d2[G_ASCII_DTOSTR_BUF_SIZE]; |
| |
| g_ascii_dtostr (d1, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double); |
| g_ascii_dtostr (d2, G_ASCII_DTOSTR_BUF_SIZE, value->data[1].v_double); |
| return g_strdup_printf ("[ %s, %s ]", d1, d2); |
| } |
| |
| static gboolean |
| gst_value_deserialize_double_range (GValue * dest, const gchar * s) |
| { |
| g_warning ("unimplemented"); |
| return FALSE; |
| } |
| |
| /**************** |
| * fraction range * |
| ****************/ |
| |
| static void |
| gst_value_init_fraction_range (GValue * value) |
| { |
| GValue *vals; |
| GType ftype; |
| |
| ftype = GST_TYPE_FRACTION; |
| |
| value->data[0].v_pointer = vals = g_slice_alloc0 (2 * sizeof (GValue)); |
| g_value_init (&vals[0], ftype); |
| g_value_init (&vals[1], ftype); |
| } |
| |
| static void |
| gst_value_free_fraction_range (GValue * value) |
| { |
| GValue *vals = (GValue *) value->data[0].v_pointer; |
| |
| if (vals != NULL) { |
| /* we know the two values contain fractions without internal allocs */ |
| /* g_value_unset (&vals[0]); */ |
| /* g_value_unset (&vals[1]); */ |
| g_slice_free1 (2 * sizeof (GValue), vals); |
| value->data[0].v_pointer = NULL; |
| } |
| } |
| |
| static void |
| gst_value_copy_fraction_range (const GValue * src_value, GValue * dest_value) |
| { |
| GValue *vals = (GValue *) dest_value->data[0].v_pointer; |
| GValue *src_vals = (GValue *) src_value->data[0].v_pointer; |
| |
| if (vals == NULL) { |
| gst_value_init_fraction_range (dest_value); |
| vals = dest_value->data[0].v_pointer; |
| } |
| if (src_vals != NULL) { |
| g_value_copy (&src_vals[0], &vals[0]); |
| g_value_copy (&src_vals[1], &vals[1]); |
| } |
| } |
| |
| static gchar * |
| gst_value_collect_fraction_range (GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| GValue *vals = (GValue *) value->data[0].v_pointer; |
| |
| if (n_collect_values != 4) |
| return g_strdup_printf ("not enough value locations for `%s' passed", |
| G_VALUE_TYPE_NAME (value)); |
| if (collect_values[1].v_int == 0) |
| return g_strdup_printf ("passed '0' as first denominator for `%s'", |
| G_VALUE_TYPE_NAME (value)); |
| if (collect_values[3].v_int == 0) |
| return g_strdup_printf ("passed '0' as second denominator for `%s'", |
| G_VALUE_TYPE_NAME (value)); |
| if (gst_util_fraction_compare (collect_values[0].v_int, |
| collect_values[1].v_int, collect_values[2].v_int, |
| collect_values[3].v_int) >= 0) |
| return g_strdup_printf ("range start is not smaller than end for `%s'", |
| G_VALUE_TYPE_NAME (value)); |
| |
| if (vals == NULL) { |
| gst_value_init_fraction_range (value); |
| vals = value->data[0].v_pointer; |
| } |
| |
| gst_value_set_fraction (&vals[0], collect_values[0].v_int, |
| collect_values[1].v_int); |
| gst_value_set_fraction (&vals[1], collect_values[2].v_int, |
| collect_values[3].v_int); |
| |
| return NULL; |
| } |
| |
| static gchar * |
| gst_value_lcopy_fraction_range (const GValue * value, guint n_collect_values, |
| GTypeCValue * collect_values, guint collect_flags) |
| { |
| gint i; |
| gint *dest_values[4]; |
| GValue *vals = (GValue *) value->data[0].v_pointer; |
| |
| if (G_UNLIKELY (n_collect_values != 4)) |
| return g_strdup_printf ("not enough value locations for `%s' passed", |
| G_VALUE_TYPE_NAME (value)); |
| |
| for (i = 0; i < 4; i++) { |
| if (G_UNLIKELY (collect_values[i].v_pointer == NULL)) { |
| return g_strdup_printf ("value location for `%s' passed as NULL", |
| G_VALUE_TYPE_NAME (value)); |
| } |
| dest_values[i] = collect_values[i].v_pointer; |
| } |
| |
| if (G_UNLIKELY (vals == NULL)) { |
| return g_strdup_printf ("Uninitialised `%s' passed", |
| G_VALUE_TYPE_NAME (value)); |
| } |
| |
| dest_values[0][0] = gst_value_get_fraction_numerator (&vals[0]); |
| dest_values[1][0] = gst_value_get_fraction_denominator (&vals[0]); |
| dest_values[2][0] = gst_value_get_fraction_numerator (&vals[1]); |
| dest_values[3][0] = gst_value_get_fraction_denominator (&vals[1]); |
| return NULL; |
| } |
| |
| /** |
| * gst_value_set_fraction_range: |
| * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE |
| * @start: the start of the range (a GST_TYPE_FRACTION GValue) |
| * @end: the end of the range (a GST_TYPE_FRACTION GValue) |
| * |
| * Sets @value to the range specified by @start and @end. |
| */ |
| void |
| gst_value_set_fraction_range (GValue * value, const GValue * start, |
| const GValue * end) |
| { |
| GValue *vals; |
| |
| g_return_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value)); |
| g_return_if_fail (GST_VALUE_HOLDS_FRACTION (start)); |
| g_return_if_fail (GST_VALUE_HOLDS_FRACTION (end)); |
| g_return_if_fail (gst_util_fraction_compare (start->data[0].v_int, |
| start->data[1].v_int, end->data[0].v_int, end->data[1].v_int) < 0); |
| |
| vals = (GValue *) value->data[0].v_pointer; |
| if (vals == NULL) { |
| gst_value_init_fraction_range (value); |
| vals = value->data[0].v_pointer; |
| } |
| g_value_copy (start, &vals[0]); |
| g_value_copy (end, &vals[1]); |
| } |
| |
| /** |
| * gst_value_set_fraction_range_full: |
| * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE |
| * @numerator_start: the numerator start of the range |
| * @denominator_start: the denominator start of the range |
| * @numerator_end: the numerator end of the range |
| * @denominator_end: the denominator end of the range |
| * |
| * Sets @value to the range specified by @numerator_start/@denominator_start |
| * and @numerator_end/@denominator_end. |
| */ |
| void |
| gst_value_set_fraction_range_full (GValue * value, |
| gint numerator_start, gint denominator_start, |
| gint numerator_end, gint denominator_end) |
| { |
| GValue start = { 0 }; |
| GValue end = { 0 }; |
| |
| g_return_if_fail (value != NULL); |
| g_return_if_fail (denominator_start != 0); |
| g_return_if_fail (denominator_end != 0); |
| g_return_if_fail (gst_util_fraction_compare (numerator_start, |
| denominator_start, numerator_end, denominator_end) < 0); |
| |
| g_value_init (&start, GST_TYPE_FRACTION); |
| g_value_init (&end, GST_TYPE_FRACTION); |
| |
| gst_value_set_fraction (&start, numerator_start, denominator_start); |
| gst_value_set_fraction (&end, numerator_end, denominator_end); |
| gst_value_set_fraction_range (value, &start, &end); |
| |
| /* we know the two values contain fractions without internal allocs */ |
| /* g_value_unset (&start); */ |
| /* g_value_unset (&end); */ |
| } |
| |
| /* FIXME 2.0: Don't leak the internal representation of fraction |
| * ranges but instead return the numerator and denominator |
| * separately. |
| * This would allow to store fraction ranges as |
| * data[0] = (min_n << 32) | (min_d) |
| * data[1] = (max_n << 32) | (max_d) |
| * without requiring an additional allocation for each value. |
| */ |
| |
| /** |
| * gst_value_get_fraction_range_min: |
| * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE |
| * |
| * Gets the minimum of the range specified by @value. |
| * |
| * Returns: the minimum of the range |
| */ |
| const GValue * |
| gst_value_get_fraction_range_min (const GValue * value) |
| { |
| GValue *vals; |
| |
| g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), NULL); |
| |
| vals = (GValue *) value->data[0].v_pointer; |
| if (vals != NULL) { |
| return &vals[0]; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * gst_value_get_fraction_range_max: |
| * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE |
| * |
| * Gets the maximum of the range specified by @value. |
| * |
| * Returns: the maximum of the range |
| */ |
| const GValue * |
| gst_value_get_fraction_range_max (const GValue * value) |
| { |
| GValue *vals; |
| |
| g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), NULL); |
| |
| vals = (GValue *) value->data[0].v_pointer; |
| if (vals != NULL) { |
| return &vals[1]; |
| } |
| |
| return NULL; |
| } |
| |
| static gchar * |
| gst_value_serialize_fraction_range (const GValue * value) |
| { |
| GValue *vals = (GValue *) value->data[0].v_pointer; |
| gchar *retval; |
| |
| if (vals == NULL) { |
| retval = g_strdup ("[ 0/1, 0/1 ]"); |
| } else { |
| gchar *start, *end; |
| |
| start = gst_value_serialize_fraction (&vals[0]); |
| end = gst_value_serialize_fraction (&vals[1]); |
| |
| retval = g_strdup_printf ("[ %s, %s ]", start, end); |
| g_free (start); |
| g_free (end); |
| } |
| |
| return retval; |
| } |
| |
| static void |
| gst_value_transform_fraction_range_string (const GValue * src_value, |
| GValue * dest_value) |
| { |
| dest_value->data[0].v_pointer = |
| gst_value_serialize_fraction_range (src_value); |
| } |
| |
| static gint |
| gst_value_compare_fraction_range (const GValue * value1, const GValue * value2) |
| { |
| GValue *vals1, *vals2; |
| GstValueCompareFunc compare; |
| |
| if (value2->data[0].v_pointer == value1->data[0].v_pointer) |
| return GST_VALUE_EQUAL; /* Only possible if both are NULL */ |
| |
| if (value2->data[0].v_pointer == NULL || value1->data[0].v_pointer == NULL) |
| return GST_VALUE_UNORDERED; |
| |
| vals1 = (GValue *) value1->data[0].v_pointer; |
| vals2 = (GValue *) value2->data[0].v_pointer; |
| if ((compare = gst_value_get_compare_func (&vals1[0]))) { |
| if (gst_value_compare_with_func (&vals1[0], &vals2[0], compare) == |
| GST_VALUE_EQUAL && |
| gst_value_compare_with_func (&vals1[1], &vals2[1], compare) == |
| GST_VALUE_EQUAL) |
| return GST_VALUE_EQUAL; |
| } |
| return GST_VALUE_UNORDERED; |
| } |
| |
| static gboolean |
| gst_value_deserialize_fraction_range (GValue * dest, const gchar * s) |
| { |
| g_warning ("unimplemented"); |
| return FALSE; |
| } |
| |
| /*********** |
| * GstCaps * |
| ***********/ |
| |
| /** |
| * gst_value_set_caps: |
| * @value: a GValue initialized to GST_TYPE_CAPS |
| * @caps: (transfer none): the caps to set the value to |
| * |
| * Sets the contents of @value to @caps. A reference to the |
| * provided @caps will be taken by the @value. |
| */ |
| void |
| gst_value_set_caps (GValue * value, const GstCaps * caps) |
| { |
| g_return_if_fail (G_IS_VALUE (value)); |
| g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS); |
| g_return_if_fail (caps == NULL || GST_IS_CAPS (caps)); |
| |
| g_value_set_boxed (value, caps); |
| } |
| |
| /** |
| * gst_value_get_caps: |
| * @value: a GValue initialized to GST_TYPE_CAPS |
| * |
| * Gets the contents of @value. The reference count of the returned |
| * #GstCaps will not be modified, therefore the caller must take one |
| * before getting rid of the @value. |
| * |
| * Returns: (transfer none): the contents of @value |
| */ |
| const GstCaps * |
| gst_value_get_caps (const GValue * value) |
| { |
| g_return_val_if_fail (G_IS_VALUE (value), NULL); |
| g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS, NULL); |
| |
| return (GstCaps *) g_value_get_boxed (value); |
| } |
| |
| static gint |
| gst_value_compare_caps (const GValue * value1, const GValue * value2) |
| { |
| GstCaps *caps1 = GST_CAPS (gst_value_get_caps (value1)); |
| GstCaps *caps2 = GST_CAPS (gst_value_get_caps (value2)); |
| |
| if (gst_caps_is_equal (caps1, caps2)) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| |
| static gchar * |
| gst_value_serialize_caps (const GValue * value) |
| { |
| GstCaps *caps = g_value_get_boxed (value); |
| return gst_string_take_and_wrap (gst_caps_to_string (caps)); |
| } |
| |
| static gboolean |
| gst_value_deserialize_caps (GValue * dest, const gchar * s) |
| { |
| GstCaps *caps; |
| |
| if (*s != '"') { |
| caps = gst_caps_from_string (s); |
| } else { |
| gchar *str = gst_string_unwrap (s); |
| |
| if (G_UNLIKELY (!str)) |
| return FALSE; |
| |
| caps = gst_caps_from_string (str); |
| g_free (str); |
| } |
| |
| if (caps) { |
| g_value_take_boxed (dest, caps); |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| /************** |
| * GstSegment * |
| **************/ |
| |
| static gchar * |
| gst_value_serialize_segment_internal (const GValue * value, gboolean escape) |
| { |
| GstSegment *seg = g_value_get_boxed (value); |
| gchar *t, *res; |
| GstStructure *s; |
| |
| s = gst_structure_new ("GstSegment", |
| "flags", GST_TYPE_SEGMENT_FLAGS, seg->flags, |
| "rate", G_TYPE_DOUBLE, seg->rate, |
| "applied-rate", G_TYPE_DOUBLE, seg->applied_rate, |
| "format", GST_TYPE_FORMAT, seg->format, |
| "base", G_TYPE_UINT64, seg->base, |
| "offset", G_TYPE_UINT64, seg->offset, |
| "start", G_TYPE_UINT64, seg->start, |
| "stop", G_TYPE_UINT64, seg->stop, |
| "time", G_TYPE_UINT64, seg->time, |
| "position", G_TYPE_UINT64, seg->position, |
| "duration", G_TYPE_UINT64, seg->duration, NULL); |
| t = gst_structure_to_string (s); |
| if (escape) { |
| res = g_strdup_printf ("\"%s\"", t); |
| g_free (t); |
| } else { |
| res = t; |
| } |
| gst_structure_free (s); |
| |
| return res; |
| } |
| |
| static gchar * |
| gst_value_serialize_segment (const GValue * value) |
| { |
| return gst_value_serialize_segment_internal (value, TRUE); |
| } |
| |
| static gboolean |
| gst_value_deserialize_segment (GValue * dest, const gchar * s) |
| { |
| GstStructure *str; |
| GstSegment seg; |
| gboolean res; |
| |
| str = gst_structure_from_string (s, NULL); |
| if (str == NULL) |
| return FALSE; |
| |
| res = gst_structure_get (str, |
| "flags", GST_TYPE_SEGMENT_FLAGS, &seg.flags, |
| "rate", G_TYPE_DOUBLE, &seg.rate, |
| "applied-rate", G_TYPE_DOUBLE, &seg.applied_rate, |
| "format", GST_TYPE_FORMAT, &seg.format, |
| "base", G_TYPE_UINT64, &seg.base, |
| "offset", G_TYPE_UINT64, &seg.offset, |
| "start", G_TYPE_UINT64, &seg.start, |
| "stop", G_TYPE_UINT64, &seg.stop, |
| "time", G_TYPE_UINT64, &seg.time, |
| "position", G_TYPE_UINT64, &seg.position, |
| "duration", G_TYPE_UINT64, &seg.duration, NULL); |
| gst_structure_free (str); |
| |
| if (res) |
| g_value_set_boxed (dest, &seg); |
| |
| return res; |
| } |
| |
| /**************** |
| * GstStructure * |
| ****************/ |
| |
| /** |
| * gst_value_set_structure: |
| * @value: a GValue initialized to GST_TYPE_STRUCTURE |
| * @structure: the structure to set the value to |
| * |
| * Sets the contents of @value to @structure. The actual |
| */ |
| void |
| gst_value_set_structure (GValue * value, const GstStructure * structure) |
| { |
| g_return_if_fail (G_IS_VALUE (value)); |
| g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE); |
| g_return_if_fail (structure == NULL || GST_IS_STRUCTURE (structure)); |
| |
| g_value_set_boxed (value, structure); |
| } |
| |
| /** |
| * gst_value_get_structure: |
| * @value: a GValue initialized to GST_TYPE_STRUCTURE |
| * |
| * Gets the contents of @value. |
| * |
| * Returns: (transfer none): the contents of @value |
| */ |
| const GstStructure * |
| gst_value_get_structure (const GValue * value) |
| { |
| g_return_val_if_fail (G_IS_VALUE (value), NULL); |
| g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE, NULL); |
| |
| return (GstStructure *) g_value_get_boxed (value); |
| } |
| |
| static gchar * |
| gst_value_serialize_structure (const GValue * value) |
| { |
| GstStructure *structure = g_value_get_boxed (value); |
| |
| return gst_string_take_and_wrap (gst_structure_to_string (structure)); |
| } |
| |
| static gboolean |
| gst_value_deserialize_structure (GValue * dest, const gchar * s) |
| { |
| GstStructure *structure; |
| |
| if (*s != '"') { |
| structure = gst_structure_from_string (s, NULL); |
| } else { |
| gchar *str = gst_string_unwrap (s); |
| |
| if (G_UNLIKELY (!str)) |
| return FALSE; |
| |
| structure = gst_structure_from_string (str, NULL); |
| g_free (str); |
| } |
| |
| if (G_LIKELY (structure)) { |
| g_value_take_boxed (dest, structure); |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static gboolean |
| gst_value_compare_structure (const GValue * value1, const GValue * value2) |
| { |
| GstStructure *structure1 = GST_STRUCTURE (g_value_get_boxed (value1)); |
| GstStructure *structure2 = GST_STRUCTURE (g_value_get_boxed (value2)); |
| |
| if (gst_structure_is_equal (structure1, structure2)) |
| return GST_VALUE_EQUAL; |
| |
| return GST_VALUE_UNORDERED; |
| } |
| |
| /******************* |
| * GstCapsFeatures * |
| *******************/ |
| |
| /** |
| * gst_value_set_caps_features: |
| * @value: a GValue initialized to GST_TYPE_CAPS_FEATURES |
| * @features: the features to set the value to |
| * |
| * Sets the contents of @value to @features. |
| */ |
| void |
| gst_value_set_caps_features (GValue * value, const GstCapsFeatures * features) |
| { |
| g_return_if_fail (G_IS_VALUE (value)); |
| g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS_FEATURES); |
| g_return_if_fail (features == NULL || GST_IS_CAPS_FEATURES (features)); |
| |
| g_value_set_boxed (value, features); |
| } |
| |
| /** |
| * gst_value_get_caps_features: |
| * @value: a GValue initialized to GST_TYPE_CAPS_FEATURES |
| * |
| * Gets the contents of @value. |
| * |
| * Returns: (transfer none): the contents of @value |
| */ |
| const GstCapsFeatures * |
| gst_value_get_caps_features (const GValue * value) |
| { |
| g_return_val_if_fail (G_IS_VALUE (value), NULL); |
| g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS_FEATURES, NULL); |
| |
| return (GstCapsFeatures *) g_value_get_boxed (value); |
| } |
| |
| static gchar * |
| gst_value_serialize_caps_features (const GValue * value) |
| { |
| GstCapsFeatures *features = g_value_get_boxed (value); |
| |
| return gst_string_take_and_wrap (gst_caps_features_to_string (features)); |
| } |
| |
| static gboolean |
| gst_value_deserialize_caps_features (GValue * dest, const gchar * s) |
| { |
| GstCapsFeatures *features; |
| |
| if (*s != '"') { |
| features = gst_caps_features_from_string (s); |
| } else { |
| gchar *str = gst_string_unwrap (s); |
| |
| if (G_UNLIKELY (!str)) |
| return FALSE; |
| |
| features = gst_caps_features_from_string (str); |
| g_free (str); |
| } |
| |
| if (G_LIKELY (features)) { |
| g_value_take_boxed (dest, features); |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| /************** |
| * GstTagList * |
| **************/ |
| static gint |
| gst_value_compare_tag_list (const GValue * value1, const GValue * value2) |
| { |
| GstTagList *taglist1 = GST_TAG_LIST (g_value_get_boxed (value1)); |
| GstTagList *taglist2 = GST_TAG_LIST (g_value_get_boxed (value2)); |
| |
| if (gst_tag_list_is_equal (taglist1, taglist2)) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| |
| static gboolean |
| gst_value_deserialize_tag_list (GValue * dest, const gchar * s) |
| { |
| GstTagList *taglist; |
| |
| if (*s != '"') { |
| taglist = gst_tag_list_new_from_string (s); |
| } else { |
| gchar *str = gst_string_unwrap (s); |
| |
| if (G_UNLIKELY (!str)) |
| return FALSE; |
| |
| taglist = gst_tag_list_new_from_string (str); |
| g_free (str); |
| } |
| |
| if (G_LIKELY (taglist != NULL)) { |
| g_value_take_boxed (dest, taglist); |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static gchar * |
| gst_value_serialize_tag_list (const GValue * value) |
| { |
| GstTagList *taglist = g_value_get_boxed (value); |
| |
| return gst_string_take_and_wrap (gst_tag_list_to_string (taglist)); |
| } |
| |
| |
| /************* |
| * GstBuffer * |
| *************/ |
| |
| static gint |
| compare_buffer (GstBuffer * buf1, GstBuffer * buf2) |
| { |
| gsize size1, size2; |
| GstMapInfo info1, info2; |
| gint result, mret; |
| |
| if (buf1 == buf2) |
| return GST_VALUE_EQUAL; |
| |
| size1 = gst_buffer_get_size (buf1); |
| size2 = gst_buffer_get_size (buf2); |
| |
| if (size1 != size2) |
| return GST_VALUE_UNORDERED; |
| |
| if (size1 == 0) |
| return GST_VALUE_EQUAL; |
| |
| if (!gst_buffer_map (buf1, &info1, GST_MAP_READ)) |
| return GST_VALUE_UNORDERED; |
| |
| if (!gst_buffer_map (buf2, &info2, GST_MAP_READ)) { |
| gst_buffer_unmap (buf1, &info1); |
| return GST_VALUE_UNORDERED; |
| } |
| |
| mret = memcmp (info1.data, info2.data, info1.size); |
| if (mret == 0) |
| result = GST_VALUE_EQUAL; |
| else if (mret < 0) |
| result = GST_VALUE_LESS_THAN; |
| else |
| result = GST_VALUE_GREATER_THAN; |
| |
| gst_buffer_unmap (buf1, &info1); |
| gst_buffer_unmap (buf2, &info2); |
| |
| return result; |
| } |
| |
| static gint |
| gst_value_compare_buffer (const GValue * value1, const GValue * value2) |
| { |
| GstBuffer *buf1 = gst_value_get_buffer (value1); |
| GstBuffer *buf2 = gst_value_get_buffer (value2); |
| |
| return compare_buffer (buf1, buf2); |
| } |
| |
| static gchar * |
| gst_value_serialize_buffer (const GValue * value) |
| { |
| GstMapInfo info; |
| guint8 *data; |
| gint i; |
| gchar *string; |
| GstBuffer *buffer; |
| |
| buffer = gst_value_get_buffer (value); |
| if (buffer == NULL) |
| return NULL; |
| |
| if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) |
| return NULL; |
| |
| data = info.data; |
| |
| string = g_malloc (info.size * 2 + 1); |
| for (i = 0; i < info.size; i++) { |
| sprintf (string + i * 2, "%02x", data[i]); |
| } |
| string[info.size * 2] = 0; |
| |
| gst_buffer_unmap (buffer, &info); |
| |
| return string; |
| } |
| |
| static gboolean |
| gst_value_deserialize_buffer (GValue * dest, const gchar * s) |
| { |
| GstBuffer *buffer; |
| gint len; |
| gchar ts[3]; |
| GstMapInfo info; |
| guint8 *data; |
| gint i; |
| |
| len = strlen (s); |
| if (len & 1) |
| goto wrong_length; |
| |
| buffer = gst_buffer_new_allocate (NULL, len / 2, NULL); |
| if (!gst_buffer_map (buffer, &info, GST_MAP_WRITE)) |
| goto map_failed; |
| data = info.data; |
| |
| for (i = 0; i < len / 2; i++) { |
| if (!isxdigit ((int) s[i * 2]) || !isxdigit ((int) s[i * 2 + 1])) |
| goto wrong_char; |
| |
| ts[0] = s[i * 2 + 0]; |
| ts[1] = s[i * 2 + 1]; |
| ts[2] = 0; |
| |
| data[i] = (guint8) strtoul (ts, NULL, 16); |
| } |
| gst_buffer_unmap (buffer, &info); |
| |
| gst_value_take_buffer (dest, buffer); |
| |
| return TRUE; |
| |
| /* ERRORS */ |
| wrong_length: |
| { |
| return FALSE; |
| } |
| map_failed: |
| { |
| return FALSE; |
| } |
| wrong_char: |
| { |
| gst_buffer_unref (buffer); |
| gst_buffer_unmap (buffer, &info); |
| return FALSE; |
| } |
| } |
| |
| /************* |
| * GstSample * |
| *************/ |
| |
| /* This function is mostly used for comparing image/buffer tags in taglists */ |
| static gint |
| gst_value_compare_sample (const GValue * value1, const GValue * value2) |
| { |
| GstBuffer *buf1 = gst_sample_get_buffer (gst_value_get_sample (value1)); |
| GstBuffer *buf2 = gst_sample_get_buffer (gst_value_get_sample (value2)); |
| |
| /* FIXME: should we take into account anything else such as caps? */ |
| return compare_buffer (buf1, buf2); |
| } |
| |
| static gchar * |
| gst_value_serialize_sample (const GValue * value) |
| { |
| const GstStructure *info_structure; |
| GstSegment *segment; |
| GstBuffer *buffer; |
| GstCaps *caps; |
| GstSample *sample; |
| GValue val = { 0, }; |
| gchar *info_str, *caps_str, *tmp; |
| gchar *buf_str, *seg_str, *s; |
| |
| sample = g_value_get_boxed (value); |
| |
| buffer = gst_sample_get_buffer (sample); |
| if (buffer) { |
| g_value_init (&val, GST_TYPE_BUFFER); |
| g_value_set_boxed (&val, buffer); |
| buf_str = gst_value_serialize_buffer (&val); |
| g_value_unset (&val); |
| } else { |
| buf_str = g_strdup ("None"); |
| } |
| |
| caps = gst_sample_get_caps (sample); |
| if (caps) { |
| tmp = gst_caps_to_string (caps); |
| caps_str = g_base64_encode ((guchar *) tmp, strlen (tmp) + 1); |
| g_strdelimit (caps_str, "=", '_'); |
| g_free (tmp); |
| } else { |
| caps_str = g_strdup ("None"); |
| } |
| |
| segment = gst_sample_get_segment (sample); |
| if (segment) { |
| g_value_init (&val, GST_TYPE_SEGMENT); |
| g_value_set_boxed (&val, segment); |
| tmp = gst_value_serialize_segment_internal (&val, FALSE); |
| seg_str = g_base64_encode ((guchar *) tmp, strlen (tmp) + 1); |
| g_strdelimit (seg_str, "=", '_'); |
| g_free (tmp); |
| g_value_unset (&val); |
| } else { |
| seg_str = g_strdup ("None"); |
| } |
| |
| info_structure = gst_sample_get_info (sample); |
| if (info_structure) { |
| tmp = gst_structure_to_string (info_structure); |
| info_str = g_base64_encode ((guchar *) tmp, strlen (tmp) + 1); |
| g_strdelimit (info_str, "=", '_'); |
| g_free (tmp); |
| } else { |
| info_str = g_strdup ("None"); |
| } |
| |
| s = g_strconcat (buf_str, ":", caps_str, ":", seg_str, ":", info_str, NULL); |
| g_free (buf_str); |
| g_free (caps_str); |
| g_free (seg_str); |
| g_free (info_str); |
| |
| return s; |
| } |
| |
| static gboolean |
| gst_value_deserialize_sample (GValue * dest, const gchar * s) |
| { |
| GValue bval = G_VALUE_INIT, sval = G_VALUE_INIT; |
| GstStructure *info; |
| GstSample *sample; |
| GstCaps *caps; |
| gboolean ret = FALSE; |
| gchar **fields; |
| gsize outlen; |
| gint len; |
| |
| GST_TRACE ("deserialize '%s'", s); |
| |
| fields = g_strsplit (s, ":", -1); |
| len = g_strv_length (fields); |
| if (len != 4) |
| goto wrong_length; |
| |
| g_value_init (&bval, GST_TYPE_BUFFER); |
| g_value_init (&sval, GST_TYPE_SEGMENT); |
| |
| if (!gst_value_deserialize_buffer (&bval, fields[0])) |
| goto fail; |
| |
| if (strcmp (fields[1], "None") != 0) { |
| g_strdelimit (fields[1], "_", '='); |
| g_base64_decode_inplace (fields[1], &outlen); |
| GST_TRACE ("caps : %s", fields[1]); |
| caps = gst_caps_from_string (fields[1]); |
| if (caps == NULL) |
| goto fail; |
| } else { |
| caps = NULL; |
| } |
| |
| if (strcmp (fields[2], "None") != 0) { |
| g_strdelimit (fields[2], "_", '='); |
| g_base64_decode_inplace (fields[2], &outlen); |
| GST_TRACE ("segment : %s", fields[2]); |
| if (!gst_value_deserialize_segment (&sval, fields[2])) |
| goto fail; |
| } |
| |
| if (strcmp (fields[3], "None") != 0) { |
| g_strdelimit (fields[3], "_", '='); |
| g_base64_decode_inplace (fields[3], &outlen); |
| GST_TRACE ("info : %s", fields[3]); |
| info = gst_structure_from_string (fields[3], NULL); |
| if (info == NULL) |
| goto fail; |
| } else { |
| info = NULL; |
| } |
| |
| sample = gst_sample_new (gst_value_get_buffer (&bval), caps, |
| g_value_get_boxed (&sval), info); |
| |
| g_value_take_boxed (dest, sample); |
| |
| if (caps) |
| gst_caps_unref (caps); |
| |
| ret = TRUE; |
| |
| fail: |
| |
| g_value_unset (&bval); |
| g_value_unset (&sval); |
| |
| wrong_length: |
| |
| g_strfreev (fields); |
| |
| return ret; |
| } |
| |
| /*********** |
| * boolean * |
| ***********/ |
| |
| static gint |
| gst_value_compare_boolean (const GValue * value1, const GValue * value2) |
| { |
| if ((value1->data[0].v_int != 0) == (value2->data[0].v_int != 0)) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| |
| static gchar * |
| gst_value_serialize_boolean (const GValue * value) |
| { |
| if (value->data[0].v_int) { |
| return g_strdup ("true"); |
| } |
| return g_strdup ("false"); |
| } |
| |
| static gboolean |
| gst_value_deserialize_boolean (GValue * dest, const gchar * s) |
| { |
| gboolean ret = FALSE; |
| |
| if (g_ascii_strcasecmp (s, "true") == 0 || |
| g_ascii_strcasecmp (s, "yes") == 0 || |
| g_ascii_strcasecmp (s, "t") == 0 || strcmp (s, "1") == 0) { |
| g_value_set_boolean (dest, TRUE); |
| ret = TRUE; |
| } else if (g_ascii_strcasecmp (s, "false") == 0 || |
| g_ascii_strcasecmp (s, "no") == 0 || |
| g_ascii_strcasecmp (s, "f") == 0 || strcmp (s, "0") == 0) { |
| g_value_set_boolean (dest, FALSE); |
| ret = TRUE; |
| } |
| |
| return ret; |
| } |
| |
| #define CREATE_SERIALIZATION_START(_type,_macro) \ |
| static gint \ |
| gst_value_compare_ ## _type \ |
| (const GValue * value1, const GValue * value2) \ |
| { \ |
| g ## _type val1 = g_value_get_ ## _type (value1); \ |
| g ## _type val2 = g_value_get_ ## _type (value2); \ |
| if (val1 > val2) \ |
| return GST_VALUE_GREATER_THAN; \ |
| if (val1 < val2) \ |
| return GST_VALUE_LESS_THAN; \ |
| return GST_VALUE_EQUAL; \ |
| } \ |
| \ |
| static gchar * \ |
| gst_value_serialize_ ## _type (const GValue * value) \ |
| { \ |
| GValue val = { 0, }; \ |
| g_value_init (&val, G_TYPE_STRING); \ |
| if (!g_value_transform (value, &val)) \ |
| g_assert_not_reached (); \ |
| /* NO_COPY_MADNESS!!! */ \ |
| return (char *) g_value_get_string (&val); \ |
| } |
| |
| /* deserialize the given s into to as a gint64. |
| * check if the result is actually storeable in the given size number of |
| * bytes. |
| */ |
| static gboolean |
| gst_value_deserialize_int_helper (gint64 * to, const gchar * s, |
| gint64 min, gint64 max, gint size) |
| { |
| gboolean ret = FALSE; |
| gchar *end; |
| guint64 mask = ~0; |
| |
| errno = 0; |
| *to = g_ascii_strtoull (s, &end, 0); |
| /* a range error is a definitive no-no */ |
| if (errno == ERANGE) { |
| return FALSE; |
| } |
| |
| if (*end == 0) { |
| ret = TRUE; |
| } else { |
| if (g_ascii_strcasecmp (s, "little_endian") == 0) { |
| *to = G_LITTLE_ENDIAN; |
| ret = TRUE; |
| } else if (g_ascii_strcasecmp (s, "big_endian") == 0) { |
| *to = G_BIG_ENDIAN; |
| ret = TRUE; |
| } else if (g_ascii_strcasecmp (s, "byte_order") == 0) { |
| *to = G_BYTE_ORDER; |
| ret = TRUE; |
| } else if (g_ascii_strcasecmp (s, "min") == 0) { |
| *to = min; |
| ret = TRUE; |
| } else if (g_ascii_strcasecmp (s, "max") == 0) { |
| *to = max; |
| ret = TRUE; |
| } |
| } |
| if (ret) { |
| /* by definition, a gint64 fits into a gint64; so ignore those */ |
| if (size != sizeof (mask)) { |
| if (*to >= 0) { |
| /* for positive numbers, we create a mask of 1's outside of the range |
| * and 0's inside the range. An and will thus keep only 1 bits |
| * outside of the range */ |
| mask <<= (size * 8); |
| if ((mask & *to) != 0) { |
| ret = FALSE; |
| } |
| } else { |
| /* for negative numbers, we do a 2's complement version */ |
| mask <<= ((size * 8) - 1); |
| if ((mask & *to) != mask) { |
| ret = FALSE; |
| } |
| } |
| } |
| } |
| return ret; |
| } |
| |
| #define CREATE_SERIALIZATION(_type,_macro) \ |
| CREATE_SERIALIZATION_START(_type,_macro) \ |
| \ |
| static gboolean \ |
| gst_value_deserialize_ ## _type (GValue * dest, const gchar *s) \ |
| { \ |
| gint64 x; \ |
| \ |
| if (gst_value_deserialize_int_helper (&x, s, G_MIN ## _macro, \ |
| G_MAX ## _macro, sizeof (g ## _type))) { \ |
| g_value_set_ ## _type (dest, /*(g ## _type)*/ x); \ |
| return TRUE; \ |
| } else { \ |
| return FALSE; \ |
| } \ |
| } |
| |
| #define CREATE_USERIALIZATION(_type,_macro) \ |
| CREATE_SERIALIZATION_START(_type,_macro) \ |
| \ |
| static gboolean \ |
| gst_value_deserialize_ ## _type (GValue * dest, const gchar *s) \ |
| { \ |
| gint64 x; \ |
| gchar *end; \ |
| gboolean ret = FALSE; \ |
| \ |
| errno = 0; \ |
| x = g_ascii_strtoull (s, &end, 0); \ |
| /* a range error is a definitive no-no */ \ |
| if (errno == ERANGE) { \ |
| return FALSE; \ |
| } \ |
| /* the cast ensures the range check later on makes sense */ \ |
| x = (g ## _type) x; \ |
| if (*end == 0) { \ |
| ret = TRUE; \ |
| } else { \ |
| if (g_ascii_strcasecmp (s, "little_endian") == 0) { \ |
| x = G_LITTLE_ENDIAN; \ |
| ret = TRUE; \ |
| } else if (g_ascii_strcasecmp (s, "big_endian") == 0) { \ |
| x = G_BIG_ENDIAN; \ |
| ret = TRUE; \ |
| } else if (g_ascii_strcasecmp (s, "byte_order") == 0) { \ |
| x = G_BYTE_ORDER; \ |
| ret = TRUE; \ |
| } else if (g_ascii_strcasecmp (s, "min") == 0) { \ |
| x = 0; \ |
| ret = TRUE; \ |
| } else if (g_ascii_strcasecmp (s, "max") == 0) { \ |
| x = G_MAX ## _macro; \ |
| ret = TRUE; \ |
| } \ |
| } \ |
| if (ret) { \ |
| if (x > G_MAX ## _macro) { \ |
| ret = FALSE; \ |
| } else { \ |
| g_value_set_ ## _type (dest, x); \ |
| } \ |
| } \ |
| return ret; \ |
| } |
| |
| CREATE_SERIALIZATION (int, INT); |
| CREATE_SERIALIZATION (int64, INT64); |
| CREATE_SERIALIZATION (long, LONG); |
| |
| CREATE_USERIALIZATION (uint, UINT); |
| CREATE_USERIALIZATION (uint64, UINT64); |
| CREATE_USERIALIZATION (ulong, ULONG); |
| |
| /* FIXME 2.0: remove this again, plugins shouldn't have uchar properties */ |
| #ifndef G_MAXUCHAR |
| #define G_MAXUCHAR 255 |
| #endif |
| CREATE_USERIALIZATION (uchar, UCHAR); |
| |
| /********** |
| * double * |
| **********/ |
| static gint |
| gst_value_compare_double (const GValue * value1, const GValue * value2) |
| { |
| if (value1->data[0].v_double > value2->data[0].v_double) |
| return GST_VALUE_GREATER_THAN; |
| if (value1->data[0].v_double < value2->data[0].v_double) |
| return GST_VALUE_LESS_THAN; |
| if (value1->data[0].v_double == value2->data[0].v_double) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| |
| static gchar * |
| gst_value_serialize_double (const GValue * value) |
| { |
| gchar d[G_ASCII_DTOSTR_BUF_SIZE]; |
| |
| g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double); |
| return g_strdup (d); |
| } |
| |
| static gboolean |
| gst_value_deserialize_double (GValue * dest, const gchar * s) |
| { |
| gdouble x; |
| gboolean ret = FALSE; |
| gchar *end; |
| |
| x = g_ascii_strtod (s, &end); |
| if (*end == 0) { |
| ret = TRUE; |
| } else { |
| if (g_ascii_strcasecmp (s, "min") == 0) { |
| x = -G_MAXDOUBLE; |
| ret = TRUE; |
| } else if (g_ascii_strcasecmp (s, "max") == 0) { |
| x = G_MAXDOUBLE; |
| ret = TRUE; |
| } |
| } |
| if (ret) { |
| g_value_set_double (dest, x); |
| } |
| return ret; |
| } |
| |
| /********* |
| * float * |
| *********/ |
| |
| static gint |
| gst_value_compare_float (const GValue * value1, const GValue * value2) |
| { |
| if (value1->data[0].v_float > value2->data[0].v_float) |
| return GST_VALUE_GREATER_THAN; |
| if (value1->data[0].v_float < value2->data[0].v_float) |
| return GST_VALUE_LESS_THAN; |
| if (value1->data[0].v_float == value2->data[0].v_float) |
| return GST_VALUE_EQUAL; |
| return GST_VALUE_UNORDERED; |
| } |
| |
| static gchar * |
| gst_value_serialize_float (const GValue * value) |
| { |
| gchar d[G_ASCII_DTOSTR_BUF_SIZE]; |
| |
| g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_float); |
| return g_strdup (d); |
| } |
| |
| static gboolean |
| gst_value_deserialize_float (GValue * dest, const gchar * s) |
| { |
| gdouble x; |
| gboolean ret = FALSE; |
| gchar *end; |
| |
| x = g_ascii_strtod (s, &end); |
| if (*end == 0) { |
| ret = TRUE; |
| } else { |
| if (g_ascii_strcasecmp (s, "min") == 0) { |
| x = -G_MAXFLOAT; |
| ret = TRUE; |
| } else if (g_ascii_strcasecmp (s, "max") == 0) { |
| x = G_MAXFLOAT; |
| ret = TRUE; |
| } |
| } |
| if (x > G_MAXFLOAT || x < -G_MAXFLOAT) |
| ret = FALSE; |
| if (ret) { |
| g_value_set_float (dest, (float) x); |
| } |
| return ret; |
| } |
| |
| /********** |
| * string * |
| **********/ |
| |
| static gint |
| gst_value_compare_string (const GValue * value1, const GValue * value2) |
| { |
| if (G_UNLIKELY (!value1->data[0].v_pointer || !value2->data[0].v_pointer)) { |
| /* if only one is NULL, no match - otherwise both NULL == EQUAL */ |
| if (value1->data[0].v_pointer != value2->data[0].v_pointer) |
| return GST_VALUE_UNORDERED; |
| } else { |
| gint x = strcmp (value1->data[0].v_pointer, value2->data[0].v_pointer); |
| |
| if (x < 0) |
| return GST_VALUE_LESS_THAN; |
| if (x > 0) |
| return GST_VALUE_GREATER_THAN; |
| } |
| |
| return GST_VALUE_EQUAL; |
| } |
| |
| static gint |
| gst_string_measure_wrapping (const gchar * s) |
| { |
| gint len; |
| gboolean wrap = FALSE; |
| |
| if (G_UNLIKELY (s == NULL)) |
| return -1; |
| |
| /* Special case: the actual string NULL needs wrapping */ |
| if (G_UNLIKELY (strcmp (s, "NULL") == 0)) |
| return 4; |
| |
| len = 0; |
| while (*s) { |
| if (GST_ASCII_IS_STRING (*s)) { |
| len++; |
| } else if (*s < 0x20 || *s >= 0x7f) { |
| wrap = TRUE; |
| len += 4; |
| } else { |
| wrap = TRUE; |
| len += 2; |
| } |
| s++; |
| } |
| |
| /* Wrap the string if we found something that needs |
| * wrapping, or the empty string (len == 0) */ |
| return (wrap || len == 0) ? len : -1; |
| } |
| |
| static gchar * |
| gst_string_wrap_inner (const gchar * s, gint len) |
| { |
| gchar *d, *e; |
| |
| e = d = g_malloc (len + 3); |
| |
| *e++ = '\"'; |
| while (*s) { |
| if (GST_ASCII_IS_STRING (*s)) { |
| *e++ = *s++; |
| } else if (*s < 0x20 || *s >= 0x7f) { |
| *e++ = '\\'; |
| *e++ = '0' + ((*(guchar *) s) >> 6); |
| *e++ = '0' + (((*s) >> 3) & 0x7); |
| *e++ = '0' + ((*s++) & 0x7); |
| } else { |
| *e++ = '\\'; |
| *e++ = *s++; |
| } |
| } |
| *e++ = '\"'; |
| *e = 0; |
| |
| g_assert (e - d <= len + 3); |
| return d; |
| } |
| |
| /* Do string wrapping/escaping */ |
| static gchar * |
| gst_string_wrap (const gchar * s) |
| { |
| gint len = gst_string_measure_wrapping (s); |
| |
| if (G_LIKELY (len < 0)) |
| return g_strdup (s); |
| |
| return gst_string_wrap_inner (s, len); |
| } |
| |
| /* Same as above, but take ownership of the string */ |
| static gchar * |
| gst_string_take_and_wrap (gchar * s) |
| { |
| gchar *out; |
| gint len = gst_string_measure_wrapping (s); |
| |
| if (G_LIKELY (len < 0)) |
| return s; |
| |
| out = gst_string_wrap_inner (s, len); |
| g_free (s); |
| |
| return out; |
| } |
| |
| /* |
| * This function takes a string delimited with double quotes (") |
| * and unescapes any \xxx octal numbers. |
| * |
| * If sequences of \y are found where y is not in the range of |
| * 0->3, y is copied unescaped. |
| * |
| * If \xyy is found where x is an octal number but y is not, an |
| * error is encountered and %NULL is returned. |
| * |
| * the input string must be \0 terminated. |
| */ |
| static gchar * |
| gst_string_unwrap (const gchar * s) |
| { |
| gchar *ret; |
| gchar *read, *write; |
| |
| /* NULL string returns NULL */ |
| if (s == NULL) |
| return NULL; |
| |
| /* strings not starting with " are invalid */ |
| if (*s != '"') |
| return NULL; |
| |
| /* make copy of original string to hold the result. This |
| * string will always be smaller than the original */ |
| ret = g_strdup (s); |
| read = ret; |
| write = ret; |
| |
| /* need to move to the next position as we parsed the " */ |
| read++; |
| |
| while (*read) { |
| if (GST_ASCII_IS_STRING (*read)) { |
| /* normal chars are just copied */ |
| *write++ = *read++; |
| } else if (*read == '"') { |
| /* quote marks end of string */ |
| break; |
| } else if (*read == '\\') { |
| /* got an escape char, move to next position to read a tripplet |
| * of octal numbers */ |
| read++; |
| /* is the next char a possible first octal number? */ |
| if (*read >= '0' && *read <= '3') { |
| /* parse other 2 numbers, if one of them is not in the range of |
| * an octal number, we error. We also catch the case where a zero |
| * byte is found here. */ |
| if (read[1] < '0' || read[1] > '7' || read[2] < '0' || read[2] > '7') |
| goto beach; |
| |
| /* now convert the octal number to a byte again. */ |
| *write++ = ((read[0] - '0') << 6) + |
| ((read[1] - '0') << 3) + (read[2] - '0'); |
| |
| read += 3; |
| } else { |
| /* if we run into a \0 here, we definitely won't get a quote later */ |
| if (*read == 0) |
| goto beach; |
| |
| /* else copy \X sequence */ |
| *write++ = *read++; |
| } |
| } else { |
| /* weird character, error */ |
| goto beach; |
| } |
| } |
| /* if the string is not ending in " and zero terminated, we error */ |
| if (*read != '"' || read[1] != '\0') |
| goto beach; |
| |
| /* null terminate result string and return */ |
| *write = '\0'; |
| return ret; |
| |
| beach: |
| g_free (ret); |
| return NULL; |
| } |
| |
| static gchar * |
| gst_value_serialize_string (const GValue * value) |
| { |
| return gst_string_wrap (value->data[0].v_pointer); |
| } |
| |
| static gboolean |
| gst_value_deserialize_string (GValue * dest, const gchar * s) |
| { |
| if (G_UNLIKELY (strcmp (s, "NULL") == 0)) { |
| g_value_set_string (dest, NULL); |
| return TRUE; |
| } else if (G_LIKELY (*s != '"' || s[strlen (s) - 1] != '"')) { |
| if (!g_utf8_validate (s, -1, NULL)) |
| return FALSE; |
| g_value_set_string (dest, s); |
| return TRUE; |
| } else { |
| /* strings delimited with double quotes should be unwrapped */ |
| gchar *str = gst_string_unwrap (s); |
| if (G_UNLIKELY (!str)) |
| return FALSE; |
| g_value_take_string (dest, str); |
| } |
| |
| return TRUE; |
| } |
| |
| /******** |
| * enum * |
| ********/ |
| |
| static gint |
| gst_value_compare_enum (const GValue * value1, const GValue * value2) |
| { |
| GEnumValue *en1, *en2; |
| GEnumClass *klass1 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value1)); |
| GEnumClass *klass2 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value2)); |
| |
| g_return_val_if_fail (klass1, GST_VALUE_UNORDERED); |
| g_return_val_if_fail (klass2, GST_VALUE_UNORDERED); |
| en1 = g_enum_get_value (klass1, g_value_get_enum (value1)); |
| en2 = g_enum_get_value (klass2, g_value_get_enum (value2)); |
| g_type_class_unref (klass1); |
| g_type_class_unref (klass2); |
| g_return_val_if_fail (en1, GST_VALUE_UNORDERED); |
| g_return_val_if_fail (en2, GST_VALUE_UNORDERED); |
| if (en1->value < en2->value) |
| return GST_VALUE_LESS_THAN; |
| if (en1->value > en2->value) |
| return GST_VALUE_GREATER_THAN; |
| |
| return GST_VALUE_EQUAL; |
| } |
| |
| static gchar * |
| gst_value_serialize_enum (const GValue * value) |
|