blob: 2f16ebc75933f7c8be72bbe1051d3711f8dfc28e [file] [log] [blame]
/* 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
* @title: 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.
*/
/* Suppress warnings for GValueAraray */
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#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);
static gboolean _priv_gst_value_parse_list (gchar * s, gchar ** after,
GValue * value, GType type);
static gboolean _priv_gst_value_parse_array (gchar * s, gchar ** after,
GValue * value, GType type);
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;
typedef struct _GstValueAbbreviation GstValueAbbreviation;
struct _GstValueAbbreviation
{
const gchar *type_name;
GType type;
};
#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_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 < >
*/
gchar *
_priv_gst_value_serialize_any_list (const GValue * value, const gchar * begin,
const gchar * end, gboolean print_type)
{
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) {
if (print_type) {
g_string_append_c (s, '(');
g_string_append (s, _priv_gst_value_gtype_to_abbr (G_VALUE_TYPE (v)));
g_string_append_c (s, ')');
}
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);
}
static gchar *
_gst_value_serialize_g_value_array (const GValue * value, const gchar * begin,
const gchar * end)
{
guint i;
GValueArray *array = value->data[0].v_pointer;
GString *s;
GValue *v;
gchar *s_val;
guint alen = 0;
if (array)
alen = array->n_values;
/* 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_value_array_get_nth (array, 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_g_value_array_string (const GValue * src_value,
GValue * dest_value, const gchar * begin, const gchar * end)
{
GValue *list_value;
GValueArray *array;
GString *s;
guint i;
gchar *list_s;
guint alen;
array = src_value->data[0].v_pointer;
alen = array->n_values;
/* 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_value_array_get_nth (array, 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 ||
type == GST_TYPE_STRUCTURE) {
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, "< ", " >");
}
static void
gst_value_transform_g_value_array_string (const GValue * src_value,
GValue * dest_value)
{
_gst_value_transform_g_value_array_string (src_value, dest_value, "< ", " >");
}
static void
gst_value_transform_g_value_array_any_list (const GValue * src_value,
GValue * dest_value)
{
const GValueArray *varray;
GArray *array;
gint i;
/* GLib will unset the value, memset to 0 the data instead of doing a proper
* reset. That's why we need to allocate the array here */
gst_value_init_list_or_array (dest_value);
varray = g_value_get_boxed (src_value);
array = dest_value->data[0].v_pointer;
for (i = 0; i < varray->n_values; i++) {
GValue val = G_VALUE_INIT;
gst_value_init_and_copy (&val, &varray->values[i]);
g_array_append_vals (array, &val, 1);
}
}
static void
gst_value_transform_any_list_g_value_array (const GValue * src_value,
GValue * dest_value)
{
GValueArray *varray;
const GArray *array;
gint i;
array = src_value->data[0].v_pointer;
varray = g_value_array_new (array->len);
for (i = 0; i < array->len; i++)
g_value_array_append (varray, &g_array_index (array, GValue, i));
g_value_take_boxed (dest_value, varray);
}
/* 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 gint
gst_value_compare_g_value_array (const GValue * value1, const GValue * value2)
{
guint i;
GValueArray *array1 = value1->data[0].v_pointer;
GValueArray *array2 = value2->data[0].v_pointer;
guint len = array1->n_values;
GValue *v1;
GValue *v2;
if (len != array2->n_values)
return GST_VALUE_UNORDERED;
for (i = 0; i < len; i++) {
v1 = g_value_array_get_nth (array1, i);
v2 = g_value_array_get_nth (array2, 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 _priv_gst_value_serialize_any_list (value, "{ ", " }", TRUE);
}
static gboolean
gst_value_deserialize_value_list (GValue * dest, const gchar * s)
{
gchar *s2 = (gchar *) s;
return _priv_gst_value_parse_list (s2, &s2, dest, G_TYPE_INVALID);
}
static gchar *
gst_value_serialize_value_array (const GValue * value)
{
return _priv_gst_value_serialize_any_list (value, "< ", " >", TRUE);
}
static gboolean
gst_value_deserialize_value_array (GValue * dest, const gchar * s)
{
gchar *s2 = (gchar *) s;
return _priv_gst_value_parse_array (s2, &s2, dest, G_TYPE_INVALID);
}
static gchar *
gst_value_serialize_g_value_array (const GValue * value)
{
return _gst_value_serialize_g_value_array (value, "< ", " >");
}
static gboolean
gst_value_deserialize_g_value_array (GValue * dest, const gchar * s)
{
g_warning ("gst_value_deserialize_g_value_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: (nullable): 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: (nullable): 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 priv_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;
}
/********************************************
* Serialization/deserialization of GValues *
********************************************/
static GstValueAbbreviation *
_priv_gst_value_get_abbrs (gint * n_abbrs)
{
static GstValueAbbreviation *abbrs = NULL;
static volatile gsize num = 0;
if (g_once_init_enter (&num)) {
/* dynamically generate the array */
gsize _num;
GstValueAbbreviation dyn_abbrs[] = {
{"int", G_TYPE_INT}
,
{"i", G_TYPE_INT}
,
{"uint", G_TYPE_UINT}
,
{"u", G_TYPE_UINT}
,
{"float", G_TYPE_FLOAT}
,
{"f", G_TYPE_FLOAT}
,
{"double", G_TYPE_DOUBLE}
,
{"d", G_TYPE_DOUBLE}
,
{"buffer", GST_TYPE_BUFFER}
,
{"fraction", GST_TYPE_FRACTION}
,
{"boolean", G_TYPE_BOOLEAN}
,
{"bool", G_TYPE_BOOLEAN}
,
{"b", G_TYPE_BOOLEAN}
,
{"string", G_TYPE_STRING}
,
{"str", G_TYPE_STRING}
,
{"s", G_TYPE_STRING}
,
{"structure", GST_TYPE_STRUCTURE}
,
{"date", G_TYPE_DATE}
,
{"datetime", GST_TYPE_DATE_TIME}
,
{"bitmask", GST_TYPE_BITMASK}
,
{"flagset", GST_TYPE_FLAG_SET}
,
{"sample", GST_TYPE_SAMPLE}
,
{"taglist", GST_TYPE_TAG_LIST}
,
{"type", G_TYPE_GTYPE}
,
{"array", GST_TYPE_ARRAY}
,
{"list", GST_TYPE_LIST}
};
_num = G_N_ELEMENTS (dyn_abbrs);
/* permanently allocate and copy the array now */
abbrs = g_new0 (GstValueAbbreviation, _num);
memcpy (abbrs, dyn_abbrs, sizeof (GstValueAbbreviation) * _num);
g_once_init_leave (&num, _num);
}
*n_abbrs = num;
return abbrs;
}
/* given a type_name that could be a type abbreviation or a registered GType,
* return a matching GType */
static GType
_priv_gst_value_gtype_from_abbr (const char *type_name)
{
int i;
GstValueAbbreviation *abbrs;
gint n_abbrs;
GType ret;
g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
abbrs = _priv_gst_value_get_abbrs (&n_abbrs);
for (i = 0; i < n_abbrs; i++) {
if (strcmp (type_name, abbrs[i].type_name) == 0) {
return abbrs[i].type;
}
}
/* this is the fallback */
ret = g_type_from_name (type_name);
/* If not found, try it as a dynamic type */
if (G_UNLIKELY (ret == 0))
ret = gst_dynamic_type_factory_load (type_name);
return ret;
}
const char *
_priv_gst_value_gtype_to_abbr (GType type)
{
int i;
GstValueAbbreviation *abbrs;
gint n_abbrs;
g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
abbrs = _priv_gst_value_get_abbrs (&n_abbrs);
for (i = 0; i < n_abbrs; i++) {
if (type == abbrs[i].type) {
return abbrs[i].type_name;
}
}
return g_type_name (type);
}
/*
* _priv_gst_value_parse_string:
* @s: string to parse
* @end: out-pointer to char behind end of string
* @next: out-pointer to start of unread data
* @unescape: @TRUE if the substring is escaped.
*
* Find the end of a sub-string. If end == next, the string will not be
* null-terminated. In all other cases it will be.
*
* Note: This function modifies the string in @s (if unescape == @TRUE).
*
* Returns: @TRUE if a sub-string was found and @FALSE if the string is not
* terminated.
*/
gboolean
_priv_gst_value_parse_string (gchar * s, gchar ** end, gchar ** next,
gboolean unescape)
{
gchar *w;
if (*s == 0)
return FALSE;
if (*s != '"') {
int ret = _priv_gst_value_parse_simple_string (s, end);
*next = *end;
return ret;
}
/* Find the closing quotes */
if (unescape) {
w = s;
s++;
while (*s != '"') {
if (G_UNLIKELY (*s == 0))
return FALSE;
if (G_UNLIKELY (*s == '\\')) {
s++;
if (G_UNLIKELY (*s == 0))
return FALSE;
}
*w = *s;
w++;
s++;
}
s++;
} else {
s++;
while (*s != '"') {
if (G_UNLIKELY (*s == 0))
return FALSE;
if (G_UNLIKELY (*s == '\\')) {
s++;
if (G_UNLIKELY (*s == 0))
return FALSE;
}
s++;
}
s++;
w = s;
}
*end = w;
*next = s;
return TRUE;
}
static gboolean
_priv_gst_value_parse_range (gchar * s, gchar ** after, GValue * value,
GType type)
{
GValue value1 = { 0 };
GValue value2 = { 0 };
GValue value3 = { 0 };
GType range_type;
gboolean ret, have_step = FALSE;
if (*s != '[')
return FALSE;
s++;
ret = _priv_gst_value_parse_value (s, &s, &value1, type);
if (!ret)
return FALSE;
while (g_ascii_isspace (*s))
s++;
if (*s != ',')
return FALSE;
s++;
while (g_ascii_isspace (*s))
s++;
ret = _priv_gst_value_parse_value (s, &s, &value2, type);
if (!ret)
return FALSE;
while (g_ascii_isspace (*s))
s++;
/* optional step for int and int64 */
if (G_VALUE_TYPE (&value1) == G_TYPE_INT
|| G_VALUE_TYPE (&value1) == G_TYPE_INT64) {
if (*s == ',') {
s++;
while (g_ascii_isspace (*s))
s++;
ret = _priv_gst_value_parse_value (s, &s, &value3, type);
if (!ret)
return FALSE;
while (g_ascii_isspace (*s))
s++;
have_step = TRUE;
}
}
if (*s != ']')
return FALSE;
s++;
if (G_VALUE_TYPE (&value1) != G_VALUE_TYPE (&value2))
return FALSE;
if (have_step && G_VALUE_TYPE (&value1) != G_VALUE_TYPE (&value3))
return FALSE;
if (G_VALUE_TYPE (&value1) == G_TYPE_DOUBLE) {
range_type = GST_TYPE_DOUBLE_RANGE;
g_value_init (value, range_type);
gst_value_set_double_range (value,
gst_g_value_get_double_unchecked (&value1),
gst_g_value_get_double_unchecked (&value2));
} else if (G_VALUE_TYPE (&value1) == G_TYPE_INT) {
range_type = GST_TYPE_INT_RANGE;
g_value_init (value, range_type);
if (have_step)
gst_value_set_int_range_step (value,
gst_g_value_get_int_unchecked (&value1),
gst_g_value_get_int_unchecked (&value2),
gst_g_value_get_int_unchecked (&value3));
else
gst_value_set_int_range (value, gst_g_value_get_int_unchecked (&value1),
gst_g_value_get_int_unchecked (&value2));
} else if (G_VALUE_TYPE (&value1) == G_TYPE_INT64) {
range_type = GST_TYPE_INT64_RANGE;
g_value_init (value, range_type);
if (have_step)
gst_value_set_int64_range_step (value,
gst_g_value_get_int64_unchecked (&value1),
gst_g_value_get_int64_unchecked (&value2),
gst_g_value_get_int64_unchecked (&value3));
else
gst_value_set_int64_range (value,
gst_g_value_get_int64_unchecked (&value1),
gst_g_value_get_int64_unchecked (&value2));
} else if (G_VALUE_TYPE (&value1) == GST_TYPE_FRACTION) {
range_type = GST_TYPE_FRACTION_RANGE;
g_value_init (value, range_type);
gst_value_set_fraction_range (value, &value1, &value2);
} else {
return FALSE;
}
*after = s;
return TRUE;
}
static gboolean
_priv_gst_value_parse_any_list (gchar * s, gchar ** after, GValue * value,
GType type, char begin, char end)
{
GValue list_value = { 0 };
gboolean ret;
GArray *array;
array = g_value_peek_pointer (value);
if (*s != begin)
return FALSE;
s++;
while (g_ascii_isspace (*s))
s++;
if (*s == end) {
s++;
*after = s;
return TRUE;
}
ret = _priv_gst_value_parse_value (s, &s, &list_value, type);
if (!ret)
return FALSE;
g_array_append_val (array, list_value);
while (g_ascii_isspace (*s))
s++;
while (*s != end) {
if (*s != ',')
return FALSE;
s++;
while (g_ascii_isspace (*s))
s++;
memset (&list_value, 0, sizeof (list_value));
ret = _priv_gst_value_parse_value (s, &s, &list_value, type);
if (!ret)
return FALSE;
g_array_append_val (array, list_value);
while (g_ascii_isspace (*s))
s++;
}
s++;
*after = s;
return TRUE;
}
static gboolean
_priv_gst_value_parse_list (gchar * s, gchar ** after, GValue * value,
GType type)
{
return _priv_gst_value_parse_any_list (s, after, value, type, '{', '}');
}
static gboolean
_priv_gst_value_parse_array (gchar * s, gchar ** after, GValue * value,
GType type)
{
return _priv_gst_value_parse_any_list (s, after, value, type, '<', '>');
}
gboolean
_priv_gst_value_parse_simple_string (gchar * str, gchar ** end)
{
char *s = str;
while (G_LIKELY (GST_ASCII_IS_STRING (*s))) {
s++;
}
*end = s;
return (s != str);
}
gboolean
_priv_gst_value_parse_value (gchar * str,
gchar ** after, GValue * value, GType default_type)
{
gchar *type_name;
gchar *type_end;
gchar *value_s;
gchar *value_end;
gchar *s;
gchar c;
int ret = 0;
GType type = default_type;
s = str;
while (g_ascii_isspace (*s))
s++;
/* check if there's a (type_name) 'cast' */
type_name = NULL;
if (*s == '(') {
s++;
while (g_ascii_isspace (*s))
s++;
type_name = s;
if (G_UNLIKELY (!_priv_gst_value_parse_simple_string (s, &type_end)))
return FALSE;
s = type_end;
while (g_ascii_isspace (*s))
s++;
if (G_UNLIKELY (*s != ')'))
return FALSE;
s++;
while (g_ascii_isspace (*s))
s++;
c = *type_end;
*type_end = 0;
type = _priv_gst_value_gtype_from_abbr (type_name);
GST_DEBUG ("trying type name '%s'", type_name);
*type_end = c;
if (G_UNLIKELY (type == G_TYPE_INVALID)) {
GST_WARNING ("invalid type");
return FALSE;
}
}
while (g_ascii_isspace (*s))
s++;
if (*s == '[') {
ret = _priv_gst_value_parse_range (s, &s, value, type);
} else if (*s == '{') {
g_value_init (value, GST_TYPE_LIST);
ret = _priv_gst_value_parse_list (s, &s, value, type);
} else if (*s == '<') {
g_value_init (value, GST_TYPE_ARRAY);
ret = _priv_gst_value_parse_array (s, &s, value, type);
} else {
value_s = s;
if (G_UNLIKELY (type == G_TYPE_INVALID)) {
GType try_types[] =
{ G_TYPE_INT, G_TYPE_DOUBLE, GST_TYPE_FRACTION, GST_TYPE_FLAG_SET,
G_TYPE_BOOLEAN, G_TYPE_STRING
};
int i;
if (G_UNLIKELY (!_priv_gst_value_parse_string (s, &value_end, &s, TRUE)))
return FALSE;
/* Set NULL terminator for deserialization */
c = *value_end;
*value_end = '\0';
for (i = 0; i < G_N_ELEMENTS (try_types); i++) {
g_value_init (value, try_types[i]);
ret = gst_value_deserialize (value, value_s);
if (ret)
break;
g_value_unset (value);
}
} else {
g_value_init (value, type);
if (G_UNLIKELY (!_priv_gst_value_parse_string (s, &value_end, &s,
(type != G_TYPE_STRING))))
return FALSE;
/* Set NULL terminator for deserialization */
c = *value_end;
*value_end = '\0';
ret = gst_value_deserialize (value, value_s);
if (G_UNLIKELY (!ret))
g_value_unset (value);
}
*value_end = c;
}
*after = s;
return ret;
}
/**************
* 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.
*/
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 priv_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 (structure1 == structure2)
return GST_VALUE_EQUAL;
if (!structure1 || !structure2)
return GST_VALUE_UNORDERED;
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 *