utils: Add gst_util_fraction_compare() to compare fractions
[platform/upstream/gstreamer.git] / gst / gstvalue.c
1 /* GStreamer
2  * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:gstvalue
22  * @short_description: GValue implementations specific
23  * to GStreamer
24  *
25  * GValue implementations specific to GStreamer.
26  *
27  * Note that operations on the same #GValue from multiple threads may lead to
28  * undefined behaviour.
29  *
30  * Last reviewed on 2008-03-11 (0.10.18)
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #include <math.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41
42 #include "gst_private.h"
43 #include "glib-compat-private.h"
44 #include <gst/gst.h>
45 #include <gobject/gvaluecollector.h>
46 #include "gstutils.h"
47
48 typedef struct _GstValueUnionInfo GstValueUnionInfo;
49 struct _GstValueUnionInfo
50 {
51   GType type1;
52   GType type2;
53   GstValueUnionFunc func;
54 };
55
56 typedef struct _GstValueIntersectInfo GstValueIntersectInfo;
57 struct _GstValueIntersectInfo
58 {
59   GType type1;
60   GType type2;
61   GstValueIntersectFunc func;
62 };
63
64 typedef struct _GstValueSubtractInfo GstValueSubtractInfo;
65 struct _GstValueSubtractInfo
66 {
67   GType minuend;
68   GType subtrahend;
69   GstValueSubtractFunc func;
70 };
71
72 #define FUNDAMENTAL_TYPE_ID_MAX \
73     (G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT)
74 #define FUNDAMENTAL_TYPE_ID(type) \
75     ((type) >> G_TYPE_FUNDAMENTAL_SHIFT)
76
77 #define VALUE_LIST_SIZE(v) (((GArray *) (v)->data[0].v_pointer)->len)
78 #define VALUE_LIST_GET_VALUE(v, index) ((const GValue *) &g_array_index ((GArray *) (v)->data[0].v_pointer, GValue, (index)))
79
80 static GArray *gst_value_table;
81 static GHashTable *gst_value_hash;
82 static GstValueTable *gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID_MAX + 1];
83 static GArray *gst_value_union_funcs;
84 static GArray *gst_value_intersect_funcs;
85 static GArray *gst_value_subtract_funcs;
86
87 /* Forward declarations */
88 static gchar *gst_value_serialize_fraction (const GValue * value);
89
90 static GstValueCompareFunc gst_value_get_compare_func (const GValue * value1);
91 static gint gst_value_compare_with_func (const GValue * value1,
92     const GValue * value2, GstValueCompareFunc compare);
93
94 static gchar *gst_string_wrap (const gchar * s);
95 static gchar *gst_string_take_and_wrap (gchar * s);
96 static gchar *gst_string_unwrap (const gchar * s);
97
98 static inline GstValueTable *
99 gst_value_hash_lookup_type (GType type)
100 {
101   if (G_LIKELY (G_TYPE_IS_FUNDAMENTAL (type)))
102     return gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID (type)];
103   else
104     return g_hash_table_lookup (gst_value_hash, (gpointer) type);
105 }
106
107 static void
108 gst_value_hash_add_type (GType type, const GstValueTable * table)
109 {
110   if (G_TYPE_IS_FUNDAMENTAL (type))
111     gst_value_tables_fundamental[FUNDAMENTAL_TYPE_ID (type)] = (gpointer) table;
112
113   g_hash_table_insert (gst_value_hash, (gpointer) type, (gpointer) table);
114 }
115
116 /********
117  * list *
118  ********/
119
120 /* two helper functions to serialize/stringify any type of list
121  * regular lists are done with { }, arrays with < >
122  */
123 static gchar *
124 gst_value_serialize_any_list (const GValue * value, const gchar * begin,
125     const gchar * end)
126 {
127   guint i;
128   GArray *array = value->data[0].v_pointer;
129   GString *s;
130   GValue *v;
131   gchar *s_val;
132   guint alen = array->len;
133
134   /* estimate minimum string length to minimise re-allocs in GString */
135   s = g_string_sized_new (2 + (6 * alen) + 2);
136   g_string_append (s, begin);
137   for (i = 0; i < alen; i++) {
138     v = &g_array_index (array, GValue, i);
139     s_val = gst_value_serialize (v);
140     g_string_append (s, s_val);
141     g_free (s_val);
142     if (i < alen - 1) {
143       g_string_append_len (s, ", ", 2);
144     }
145   }
146   g_string_append (s, end);
147   return g_string_free (s, FALSE);
148 }
149
150 static void
151 gst_value_transform_any_list_string (const GValue * src_value,
152     GValue * dest_value, const gchar * begin, const gchar * end)
153 {
154   GValue *list_value;
155   GArray *array;
156   GString *s;
157   guint i;
158   gchar *list_s;
159   guint alen;
160
161   array = src_value->data[0].v_pointer;
162   alen = array->len;
163
164   /* estimate minimum string length to minimise re-allocs in GString */
165   s = g_string_sized_new (2 + (10 * alen) + 2);
166   g_string_append (s, begin);
167   for (i = 0; i < alen; i++) {
168     list_value = &g_array_index (array, GValue, i);
169
170     if (i != 0) {
171       g_string_append_len (s, ", ", 2);
172     }
173     list_s = g_strdup_value_contents (list_value);
174     g_string_append (s, list_s);
175     g_free (list_s);
176   }
177   g_string_append (s, end);
178
179   dest_value->data[0].v_pointer = g_string_free (s, FALSE);
180 }
181
182 /*
183  * helper function to see if a type is fixed. Is used internally here and
184  * there. Do not export, since it doesn't work for types where the content
185  * decides the fixedness (e.g. GST_TYPE_ARRAY).
186  */
187 static gboolean
188 gst_type_is_fixed (GType type)
189 {
190   /* the basic int, string, double types */
191   if (type <= G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) {
192     return TRUE;
193   }
194   /* our fundamental types that are certainly not fixed */
195   if (type == GST_TYPE_INT_RANGE || type == GST_TYPE_DOUBLE_RANGE ||
196       type == GST_TYPE_LIST || type == GST_TYPE_FRACTION_RANGE) {
197     return FALSE;
198   }
199   /* other (boxed) types that are fixed */
200   if (type == GST_TYPE_BUFFER) {
201     return TRUE;
202   }
203   /* heavy checks */
204   if (G_TYPE_IS_FUNDAMENTAL (type) || G_TYPE_FUNDAMENTAL (type) <=
205       G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) {
206     return TRUE;
207   }
208
209   return FALSE;
210 }
211
212 /* GValue functions usable for both regular lists and arrays */
213 static void
214 gst_value_init_list_or_array (GValue * value)
215 {
216   value->data[0].v_pointer = g_array_new (FALSE, TRUE, sizeof (GValue));
217 }
218
219 static GArray *
220 copy_garray_of_gstvalue (const GArray * src)
221 {
222   GArray *dest;
223   guint i, len;
224
225   len = src->len;
226   dest = g_array_sized_new (FALSE, TRUE, sizeof (GValue), len);
227   g_array_set_size (dest, len);
228   for (i = 0; i < len; i++) {
229     gst_value_init_and_copy (&g_array_index (dest, GValue, i),
230         &g_array_index (src, GValue, i));
231   }
232
233   return dest;
234 }
235
236 static void
237 gst_value_copy_list_or_array (const GValue * src_value, GValue * dest_value)
238 {
239   dest_value->data[0].v_pointer =
240       copy_garray_of_gstvalue ((GArray *) src_value->data[0].v_pointer);
241 }
242
243 static void
244 gst_value_free_list_or_array (GValue * value)
245 {
246   guint i, len;
247   GArray *src = (GArray *) value->data[0].v_pointer;
248   len = src->len;
249
250   if ((value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS) == 0) {
251     for (i = 0; i < len; i++) {
252       g_value_unset (&g_array_index (src, GValue, i));
253     }
254     g_array_free (src, TRUE);
255   }
256 }
257
258 static gpointer
259 gst_value_list_or_array_peek_pointer (const GValue * value)
260 {
261   return value->data[0].v_pointer;
262 }
263
264 static gchar *
265 gst_value_collect_list_or_array (GValue * value, guint n_collect_values,
266     GTypeCValue * collect_values, guint collect_flags)
267 {
268   if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
269     value->data[0].v_pointer = collect_values[0].v_pointer;
270     value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
271   } else {
272     value->data[0].v_pointer =
273         copy_garray_of_gstvalue ((GArray *) collect_values[0].v_pointer);
274   }
275   return NULL;
276 }
277
278 static gchar *
279 gst_value_lcopy_list_or_array (const GValue * value, guint n_collect_values,
280     GTypeCValue * collect_values, guint collect_flags)
281 {
282   GArray **dest = collect_values[0].v_pointer;
283
284   if (!dest)
285     return g_strdup_printf ("value location for `%s' passed as NULL",
286         G_VALUE_TYPE_NAME (value));
287   if (!value->data[0].v_pointer)
288     return g_strdup_printf ("invalid value given for `%s'",
289         G_VALUE_TYPE_NAME (value));
290   if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
291     *dest = (GArray *) value->data[0].v_pointer;
292   } else {
293     *dest = copy_garray_of_gstvalue ((GArray *) value->data[0].v_pointer);
294   }
295   return NULL;
296 }
297
298 /**
299  * gst_value_list_append_value:
300  * @value: a #GValue of type #GST_TYPE_LIST
301  * @append_value: the value to append
302  *
303  * Appends @append_value to the GstValueList in @value.
304  */
305 void
306 gst_value_list_append_value (GValue * value, const GValue * append_value)
307 {
308   GValue val = { 0, };
309
310   g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
311   g_return_if_fail (G_IS_VALUE (append_value));
312
313   gst_value_init_and_copy (&val, append_value);
314   g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1);
315 }
316
317 /**
318  * gst_value_list_prepend_value:
319  * @value: a #GValue of type #GST_TYPE_LIST
320  * @prepend_value: the value to prepend
321  *
322  * Prepends @prepend_value to the GstValueList in @value.
323  */
324 void
325 gst_value_list_prepend_value (GValue * value, const GValue * prepend_value)
326 {
327   GValue val = { 0, };
328
329   g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
330   g_return_if_fail (G_IS_VALUE (prepend_value));
331
332   gst_value_init_and_copy (&val, prepend_value);
333   g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1);
334 }
335
336 /**
337  * gst_value_list_concat:
338  * @dest: an uninitialized #GValue to take the result
339  * @value1: a #GValue
340  * @value2: a #GValue
341  *
342  * Concatenates copies of @value1 and @value2 into a list.  Values that are not
343  * of type #GST_TYPE_LIST are treated as if they were lists of length 1.
344  * @dest will be initialized to the type #GST_TYPE_LIST.
345  */
346 void
347 gst_value_list_concat (GValue * dest, const GValue * value1,
348     const GValue * value2)
349 {
350   guint i, value1_length, value2_length;
351   GArray *array;
352
353   g_return_if_fail (dest != NULL);
354   g_return_if_fail (G_VALUE_TYPE (dest) == 0);
355   g_return_if_fail (G_IS_VALUE (value1));
356   g_return_if_fail (G_IS_VALUE (value2));
357
358   value1_length =
359       (GST_VALUE_HOLDS_LIST (value1) ? VALUE_LIST_SIZE (value1) : 1);
360   value2_length =
361       (GST_VALUE_HOLDS_LIST (value2) ? VALUE_LIST_SIZE (value2) : 1);
362   g_value_init (dest, GST_TYPE_LIST);
363   array = (GArray *) dest->data[0].v_pointer;
364   g_array_set_size (array, value1_length + value2_length);
365
366   if (GST_VALUE_HOLDS_LIST (value1)) {
367     for (i = 0; i < value1_length; i++) {
368       gst_value_init_and_copy (&g_array_index (array, GValue, i),
369           VALUE_LIST_GET_VALUE (value1, i));
370     }
371   } else {
372     gst_value_init_and_copy (&g_array_index (array, GValue, 0), value1);
373   }
374
375   if (GST_VALUE_HOLDS_LIST (value2)) {
376     for (i = 0; i < value2_length; i++) {
377       gst_value_init_and_copy (&g_array_index (array, GValue,
378               i + value1_length), VALUE_LIST_GET_VALUE (value2, i));
379     }
380   } else {
381     gst_value_init_and_copy (&g_array_index (array, GValue, value1_length),
382         value2);
383   }
384 }
385
386 /**
387  * gst_value_list_get_size:
388  * @value: a #GValue of type #GST_TYPE_LIST
389  *
390  * Gets the number of values contained in @value.
391  *
392  * Returns: the number of values
393  */
394 guint
395 gst_value_list_get_size (const GValue * value)
396 {
397   g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), 0);
398
399   return ((GArray *) value->data[0].v_pointer)->len;
400 }
401
402 /**
403  * gst_value_list_get_value:
404  * @value: a #GValue of type #GST_TYPE_LIST
405  * @index: index of value to get from the list
406  *
407  * Gets the value that is a member of the list contained in @value and
408  * has the index @index.
409  *
410  * Returns: the value at the given index
411  */
412 const GValue *
413 gst_value_list_get_value (const GValue * value, guint index)
414 {
415   g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), NULL);
416   g_return_val_if_fail (index < VALUE_LIST_SIZE (value), NULL);
417
418   return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer,
419       GValue, index);
420 }
421
422 /**
423  * gst_value_array_append_value:
424  * @value: a #GValue of type #GST_TYPE_ARRAY
425  * @append_value: the value to append
426  *
427  * Appends @append_value to the GstValueArray in @value.
428  */
429 void
430 gst_value_array_append_value (GValue * value, const GValue * append_value)
431 {
432   GValue val = { 0, };
433
434   g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value));
435   g_return_if_fail (G_IS_VALUE (append_value));
436
437   gst_value_init_and_copy (&val, append_value);
438   g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1);
439 }
440
441 /**
442  * gst_value_array_prepend_value:
443  * @value: a #GValue of type #GST_TYPE_ARRAY
444  * @prepend_value: the value to prepend
445  *
446  * Prepends @prepend_value to the GstValueArray in @value.
447  */
448 void
449 gst_value_array_prepend_value (GValue * value, const GValue * prepend_value)
450 {
451   GValue val = { 0, };
452
453   g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value));
454   g_return_if_fail (G_IS_VALUE (prepend_value));
455
456   gst_value_init_and_copy (&val, prepend_value);
457   g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1);
458 }
459
460 /**
461  * gst_value_array_get_size:
462  * @value: a #GValue of type #GST_TYPE_ARRAY
463  *
464  * Gets the number of values contained in @value.
465  *
466  * Returns: the number of values
467  */
468 guint
469 gst_value_array_get_size (const GValue * value)
470 {
471   g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), 0);
472
473   return ((GArray *) value->data[0].v_pointer)->len;
474 }
475
476 /**
477  * gst_value_array_get_value:
478  * @value: a #GValue of type #GST_TYPE_ARRAY
479  * @index: index of value to get from the array
480  *
481  * Gets the value that is a member of the array contained in @value and
482  * has the index @index.
483  *
484  * Returns: the value at the given index
485  */
486 const GValue *
487 gst_value_array_get_value (const GValue * value, guint index)
488 {
489   g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), NULL);
490   g_return_val_if_fail (index < gst_value_array_get_size (value), NULL);
491
492   return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer,
493       GValue, index);
494 }
495
496 static void
497 gst_value_transform_list_string (const GValue * src_value, GValue * dest_value)
498 {
499   gst_value_transform_any_list_string (src_value, dest_value, "{ ", " }");
500 }
501
502 static void
503 gst_value_transform_array_string (const GValue * src_value, GValue * dest_value)
504 {
505   gst_value_transform_any_list_string (src_value, dest_value, "< ", " >");
506 }
507
508 /* Do an unordered compare of the contents of a list */
509 static gint
510 gst_value_compare_list (const GValue * value1, const GValue * value2)
511 {
512   guint i, j;
513   GArray *array1 = value1->data[0].v_pointer;
514   GArray *array2 = value2->data[0].v_pointer;
515   GValue *v1;
516   GValue *v2;
517   gint len, to_remove;
518   guint8 *removed;
519   GstValueCompareFunc compare;
520
521   /* get length and do initial length check. */
522   len = array1->len;
523   if (len != array2->len)
524     return GST_VALUE_UNORDERED;
525
526   /* place to mark removed value indices of array2 */
527   removed = g_newa (guint8, len);
528   memset (removed, 0, len);
529   to_remove = len;
530
531   /* loop over array1, all items should be in array2. When we find an
532    * item in array2, remove it from array2 by marking it as removed */
533   for (i = 0; i < len; i++) {
534     v1 = &g_array_index (array1, GValue, i);
535     if ((compare = gst_value_get_compare_func (v1))) {
536       for (j = 0; j < len; j++) {
537         /* item is removed, we can skip it */
538         if (removed[j])
539           continue;
540         v2 = &g_array_index (array2, GValue, j);
541         if (gst_value_compare_with_func (v1, v2, compare) == GST_VALUE_EQUAL) {
542           /* mark item as removed now that we found it in array2 and 
543            * decrement the number of remaining items in array2. */
544           removed[j] = 1;
545           to_remove--;
546           break;
547         }
548       }
549       /* item in array1 and not in array2, UNORDERED */
550       if (j == len)
551         return GST_VALUE_UNORDERED;
552     } else
553       return GST_VALUE_UNORDERED;
554   }
555   /* if not all items were removed, array2 contained something not in array1 */
556   if (to_remove != 0)
557     return GST_VALUE_UNORDERED;
558
559   /* arrays are equal */
560   return GST_VALUE_EQUAL;
561 }
562
563 /* Perform an ordered comparison of the contents of an array */
564 static gint
565 gst_value_compare_array (const GValue * value1, const GValue * value2)
566 {
567   guint i;
568   GArray *array1 = value1->data[0].v_pointer;
569   GArray *array2 = value2->data[0].v_pointer;
570   guint len = array1->len;
571   GValue *v1;
572   GValue *v2;
573
574   if (len != array2->len)
575     return GST_VALUE_UNORDERED;
576
577   for (i = 0; i < len; i++) {
578     v1 = &g_array_index (array1, GValue, i);
579     v2 = &g_array_index (array2, GValue, i);
580     if (gst_value_compare (v1, v2) != GST_VALUE_EQUAL)
581       return GST_VALUE_UNORDERED;
582   }
583
584   return GST_VALUE_EQUAL;
585 }
586
587 static gchar *
588 gst_value_serialize_list (const GValue * value)
589 {
590   return gst_value_serialize_any_list (value, "{ ", " }");
591 }
592
593 static gboolean
594 gst_value_deserialize_list (GValue * dest, const gchar * s)
595 {
596   g_warning ("gst_value_deserialize_list: unimplemented");
597   return FALSE;
598 }
599
600 static gchar *
601 gst_value_serialize_array (const GValue * value)
602 {
603   return gst_value_serialize_any_list (value, "< ", " >");
604 }
605
606 static gboolean
607 gst_value_deserialize_array (GValue * dest, const gchar * s)
608 {
609   g_warning ("gst_value_deserialize_array: unimplemented");
610   return FALSE;
611 }
612
613 /**********
614  * fourcc *
615  **********/
616
617 static void
618 gst_value_init_fourcc (GValue * value)
619 {
620   value->data[0].v_int = 0;
621 }
622
623 static void
624 gst_value_copy_fourcc (const GValue * src_value, GValue * dest_value)
625 {
626   dest_value->data[0].v_int = src_value->data[0].v_int;
627 }
628
629 static gchar *
630 gst_value_collect_fourcc (GValue * value, guint n_collect_values,
631     GTypeCValue * collect_values, guint collect_flags)
632 {
633   value->data[0].v_int = collect_values[0].v_int;
634
635   return NULL;
636 }
637
638 static gchar *
639 gst_value_lcopy_fourcc (const GValue * value, guint n_collect_values,
640     GTypeCValue * collect_values, guint collect_flags)
641 {
642   guint32 *fourcc_p = collect_values[0].v_pointer;
643
644   if (!fourcc_p)
645     return g_strdup_printf ("value location for `%s' passed as NULL",
646         G_VALUE_TYPE_NAME (value));
647
648   *fourcc_p = value->data[0].v_int;
649
650   return NULL;
651 }
652
653 /**
654  * gst_value_set_fourcc:
655  * @value: a GValue initialized to #GST_TYPE_FOURCC
656  * @fourcc: the #guint32 fourcc to set
657  *
658  * Sets @value to @fourcc.
659  */
660 void
661 gst_value_set_fourcc (GValue * value, guint32 fourcc)
662 {
663   g_return_if_fail (GST_VALUE_HOLDS_FOURCC (value));
664
665   value->data[0].v_int = fourcc;
666 }
667
668 /**
669  * gst_value_get_fourcc:
670  * @value: a GValue initialized to #GST_TYPE_FOURCC
671  *
672  * Gets the #guint32 fourcc contained in @value.
673  *
674  * Returns: the #guint32 fourcc contained in @value.
675  */
676 guint32
677 gst_value_get_fourcc (const GValue * value)
678 {
679   g_return_val_if_fail (GST_VALUE_HOLDS_FOURCC (value), 0);
680
681   return value->data[0].v_int;
682 }
683
684 static void
685 gst_value_transform_fourcc_string (const GValue * src_value,
686     GValue * dest_value)
687 {
688   guint32 fourcc = src_value->data[0].v_int;
689   gchar fourcc_char[4];
690
691   fourcc_char[0] = (fourcc >> 0) & 0xff;
692   fourcc_char[1] = (fourcc >> 8) & 0xff;
693   fourcc_char[2] = (fourcc >> 16) & 0xff;
694   fourcc_char[3] = (fourcc >> 24) & 0xff;
695
696   if ((g_ascii_isalnum (fourcc_char[0]) || fourcc_char[0] == ' ') &&
697       (g_ascii_isalnum (fourcc_char[1]) || fourcc_char[1] == ' ') &&
698       (g_ascii_isalnum (fourcc_char[2]) || fourcc_char[2] == ' ') &&
699       (g_ascii_isalnum (fourcc_char[3]) || fourcc_char[3] == ' ')) {
700     dest_value->data[0].v_pointer =
701         g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
702   } else {
703     dest_value->data[0].v_pointer = g_strdup_printf ("0x%08x", fourcc);
704   }
705 }
706
707 static gint
708 gst_value_compare_fourcc (const GValue * value1, const GValue * value2)
709 {
710   if (value2->data[0].v_int == value1->data[0].v_int)
711     return GST_VALUE_EQUAL;
712   return GST_VALUE_UNORDERED;
713 }
714
715 static gchar *
716 gst_value_serialize_fourcc (const GValue * value)
717 {
718   guint32 fourcc = value->data[0].v_int;
719   gchar fourcc_char[4];
720
721   fourcc_char[0] = (fourcc >> 0) & 0xff;
722   fourcc_char[1] = (fourcc >> 8) & 0xff;
723   fourcc_char[2] = (fourcc >> 16) & 0xff;
724   fourcc_char[3] = (fourcc >> 24) & 0xff;
725
726   if ((g_ascii_isalnum (fourcc_char[0]) || fourcc_char[0] == ' ') &&
727       (g_ascii_isalnum (fourcc_char[1]) || fourcc_char[1] == ' ') &&
728       (g_ascii_isalnum (fourcc_char[2]) || fourcc_char[2] == ' ') &&
729       (g_ascii_isalnum (fourcc_char[3]) || fourcc_char[3] == ' ')) {
730     return g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
731   } else {
732     return g_strdup_printf ("0x%08x", fourcc);
733   }
734 }
735
736 static gboolean
737 gst_value_deserialize_fourcc (GValue * dest, const gchar * s)
738 {
739   gboolean ret = FALSE;
740   guint32 fourcc = 0;
741   gchar *end;
742   gint l = strlen (s);
743
744   if (l == 4) {
745     fourcc = GST_MAKE_FOURCC (s[0], s[1], s[2], s[3]);
746     ret = TRUE;
747   } else if (l == 3) {
748     fourcc = GST_MAKE_FOURCC (s[0], s[1], s[2], ' ');
749     ret = TRUE;
750   } else if (l == 2) {
751     fourcc = GST_MAKE_FOURCC (s[0], s[1], ' ', ' ');
752     ret = TRUE;
753   } else if (l == 1) {
754     fourcc = GST_MAKE_FOURCC (s[0], ' ', ' ', ' ');
755     ret = TRUE;
756   } else if (g_ascii_isdigit (*s)) {
757     fourcc = strtoul (s, &end, 0);
758     if (*end == 0) {
759       ret = TRUE;
760     }
761   }
762   gst_value_set_fourcc (dest, fourcc);
763
764   return ret;
765 }
766
767 /*************
768  * int range *
769  *************/
770
771 static void
772 gst_value_init_int_range (GValue * value)
773 {
774   value->data[0].v_int = 0;
775   value->data[1].v_int = 0;
776 }
777
778 static void
779 gst_value_copy_int_range (const GValue * src_value, GValue * dest_value)
780 {
781   dest_value->data[0].v_int = src_value->data[0].v_int;
782   dest_value->data[1].v_int = src_value->data[1].v_int;
783 }
784
785 static gchar *
786 gst_value_collect_int_range (GValue * value, guint n_collect_values,
787     GTypeCValue * collect_values, guint collect_flags)
788 {
789   if (n_collect_values != 2)
790     return g_strdup_printf ("not enough value locations for `%s' passed",
791         G_VALUE_TYPE_NAME (value));
792   if (collect_values[0].v_int >= collect_values[1].v_int)
793     return g_strdup_printf ("range start is not smaller than end for `%s'",
794         G_VALUE_TYPE_NAME (value));
795
796   value->data[0].v_int = collect_values[0].v_int;
797   value->data[1].v_int = collect_values[1].v_int;
798
799   return NULL;
800 }
801
802 static gchar *
803 gst_value_lcopy_int_range (const GValue * value, guint n_collect_values,
804     GTypeCValue * collect_values, guint collect_flags)
805 {
806   guint32 *int_range_start = collect_values[0].v_pointer;
807   guint32 *int_range_end = collect_values[1].v_pointer;
808
809   if (!int_range_start)
810     return g_strdup_printf ("start value location for `%s' passed as NULL",
811         G_VALUE_TYPE_NAME (value));
812   if (!int_range_end)
813     return g_strdup_printf ("end value location for `%s' passed as NULL",
814         G_VALUE_TYPE_NAME (value));
815
816   *int_range_start = value->data[0].v_int;
817   *int_range_end = value->data[1].v_int;
818
819   return NULL;
820 }
821
822 /**
823  * gst_value_set_int_range:
824  * @value: a GValue initialized to GST_TYPE_INT_RANGE
825  * @start: the start of the range
826  * @end: the end of the range
827  *
828  * Sets @value to the range specified by @start and @end.
829  */
830 void
831 gst_value_set_int_range (GValue * value, gint start, gint end)
832 {
833   g_return_if_fail (GST_VALUE_HOLDS_INT_RANGE (value));
834   g_return_if_fail (start < end);
835
836   value->data[0].v_int = start;
837   value->data[1].v_int = end;
838 }
839
840 /**
841  * gst_value_get_int_range_min:
842  * @value: a GValue initialized to GST_TYPE_INT_RANGE
843  *
844  * Gets the minimum of the range specified by @value.
845  *
846  * Returns: the minimum of the range
847  */
848 gint
849 gst_value_get_int_range_min (const GValue * value)
850 {
851   g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
852
853   return value->data[0].v_int;
854 }
855
856 /**
857  * gst_value_get_int_range_max:
858  * @value: a GValue initialized to GST_TYPE_INT_RANGE
859  *
860  * Gets the maximum of the range specified by @value.
861  *
862  * Returns: the maxumum of the range
863  */
864 gint
865 gst_value_get_int_range_max (const GValue * value)
866 {
867   g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
868
869   return value->data[1].v_int;
870 }
871
872 static void
873 gst_value_transform_int_range_string (const GValue * src_value,
874     GValue * dest_value)
875 {
876   dest_value->data[0].v_pointer = g_strdup_printf ("[%d,%d]",
877       (int) src_value->data[0].v_int, (int) src_value->data[1].v_int);
878 }
879
880 static gint
881 gst_value_compare_int_range (const GValue * value1, const GValue * value2)
882 {
883   if (value2->data[0].v_int == value1->data[0].v_int &&
884       value2->data[1].v_int == value1->data[1].v_int)
885     return GST_VALUE_EQUAL;
886   return GST_VALUE_UNORDERED;
887 }
888
889 static gchar *
890 gst_value_serialize_int_range (const GValue * value)
891 {
892   return g_strdup_printf ("[ %d, %d ]", value->data[0].v_int,
893       value->data[1].v_int);
894 }
895
896 static gboolean
897 gst_value_deserialize_int_range (GValue * dest, const gchar * s)
898 {
899   g_warning ("unimplemented");
900   return FALSE;
901 }
902
903 /****************
904  * double range *
905  ****************/
906
907 static void
908 gst_value_init_double_range (GValue * value)
909 {
910   value->data[0].v_double = 0;
911   value->data[1].v_double = 0;
912 }
913
914 static void
915 gst_value_copy_double_range (const GValue * src_value, GValue * dest_value)
916 {
917   dest_value->data[0].v_double = src_value->data[0].v_double;
918   dest_value->data[1].v_double = src_value->data[1].v_double;
919 }
920
921 static gchar *
922 gst_value_collect_double_range (GValue * value, guint n_collect_values,
923     GTypeCValue * collect_values, guint collect_flags)
924 {
925   if (n_collect_values != 2)
926     return g_strdup_printf ("not enough value locations for `%s' passed",
927         G_VALUE_TYPE_NAME (value));
928   if (collect_values[0].v_double >= collect_values[1].v_double)
929     return g_strdup_printf ("range start is not smaller than end for `%s'",
930         G_VALUE_TYPE_NAME (value));
931
932   value->data[0].v_double = collect_values[0].v_double;
933   value->data[1].v_double = collect_values[1].v_double;
934
935   return NULL;
936 }
937
938 static gchar *
939 gst_value_lcopy_double_range (const GValue * value, guint n_collect_values,
940     GTypeCValue * collect_values, guint collect_flags)
941 {
942   gdouble *double_range_start = collect_values[0].v_pointer;
943   gdouble *double_range_end = collect_values[1].v_pointer;
944
945   if (!double_range_start)
946     return g_strdup_printf ("start value location for `%s' passed as NULL",
947         G_VALUE_TYPE_NAME (value));
948   if (!double_range_end)
949     return g_strdup_printf ("end value location for `%s' passed as NULL",
950         G_VALUE_TYPE_NAME (value));
951
952   *double_range_start = value->data[0].v_double;
953   *double_range_end = value->data[1].v_double;
954
955   return NULL;
956 }
957
958 /**
959  * gst_value_set_double_range:
960  * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
961  * @start: the start of the range
962  * @end: the end of the range
963  *
964  * Sets @value to the range specified by @start and @end.
965  */
966 void
967 gst_value_set_double_range (GValue * value, gdouble start, gdouble end)
968 {
969   g_return_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value));
970   g_return_if_fail (start < end);
971
972   value->data[0].v_double = start;
973   value->data[1].v_double = end;
974 }
975
976 /**
977  * gst_value_get_double_range_min:
978  * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
979  *
980  * Gets the minimum of the range specified by @value.
981  *
982  * Returns: the minimum of the range
983  */
984 gdouble
985 gst_value_get_double_range_min (const GValue * value)
986 {
987   g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
988
989   return value->data[0].v_double;
990 }
991
992 /**
993  * gst_value_get_double_range_max:
994  * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
995  *
996  * Gets the maximum of the range specified by @value.
997  *
998  * Returns: the maxumum of the range
999  */
1000 gdouble
1001 gst_value_get_double_range_max (const GValue * value)
1002 {
1003   g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
1004
1005   return value->data[1].v_double;
1006 }
1007
1008 static void
1009 gst_value_transform_double_range_string (const GValue * src_value,
1010     GValue * dest_value)
1011 {
1012   gchar s1[G_ASCII_DTOSTR_BUF_SIZE], s2[G_ASCII_DTOSTR_BUF_SIZE];
1013
1014   dest_value->data[0].v_pointer = g_strdup_printf ("[%s,%s]",
1015       g_ascii_dtostr (s1, G_ASCII_DTOSTR_BUF_SIZE,
1016           src_value->data[0].v_double),
1017       g_ascii_dtostr (s2, G_ASCII_DTOSTR_BUF_SIZE,
1018           src_value->data[1].v_double));
1019 }
1020
1021 static gint
1022 gst_value_compare_double_range (const GValue * value1, const GValue * value2)
1023 {
1024   if (value2->data[0].v_double == value1->data[0].v_double &&
1025       value2->data[0].v_double == value1->data[0].v_double)
1026     return GST_VALUE_EQUAL;
1027   return GST_VALUE_UNORDERED;
1028 }
1029
1030 static gchar *
1031 gst_value_serialize_double_range (const GValue * value)
1032 {
1033   gchar d1[G_ASCII_DTOSTR_BUF_SIZE];
1034   gchar d2[G_ASCII_DTOSTR_BUF_SIZE];
1035
1036   g_ascii_dtostr (d1, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double);
1037   g_ascii_dtostr (d2, G_ASCII_DTOSTR_BUF_SIZE, value->data[1].v_double);
1038   return g_strdup_printf ("[ %s, %s ]", d1, d2);
1039 }
1040
1041 static gboolean
1042 gst_value_deserialize_double_range (GValue * dest, const gchar * s)
1043 {
1044   g_warning ("unimplemented");
1045   return FALSE;
1046 }
1047
1048 /****************
1049  * fraction range *
1050  ****************/
1051
1052 static void
1053 gst_value_init_fraction_range (GValue * value)
1054 {
1055   GValue *vals;
1056   GType ftype;
1057
1058   ftype = GST_TYPE_FRACTION;
1059
1060   value->data[0].v_pointer = vals = g_slice_alloc0 (2 * sizeof (GValue));
1061   g_value_init (&vals[0], ftype);
1062   g_value_init (&vals[1], ftype);
1063 }
1064
1065 static void
1066 gst_value_free_fraction_range (GValue * value)
1067 {
1068   GValue *vals = (GValue *) value->data[0].v_pointer;
1069
1070   if (vals != NULL) {
1071     g_value_unset (&vals[0]);
1072     g_value_unset (&vals[1]);
1073     g_slice_free1 (2 * sizeof (GValue), vals);
1074     value->data[0].v_pointer = NULL;
1075   }
1076 }
1077
1078 static void
1079 gst_value_copy_fraction_range (const GValue * src_value, GValue * dest_value)
1080 {
1081   GValue *vals = (GValue *) dest_value->data[0].v_pointer;
1082   GValue *src_vals = (GValue *) src_value->data[0].v_pointer;
1083
1084   if (vals == NULL) {
1085     gst_value_init_fraction_range (dest_value);
1086     vals = dest_value->data[0].v_pointer;
1087   }
1088   if (src_vals != NULL) {
1089     g_value_copy (&src_vals[0], &vals[0]);
1090     g_value_copy (&src_vals[1], &vals[1]);
1091   }
1092 }
1093
1094 static gchar *
1095 gst_value_collect_fraction_range (GValue * value, guint n_collect_values,
1096     GTypeCValue * collect_values, guint collect_flags)
1097 {
1098   GValue *vals = (GValue *) value->data[0].v_pointer;
1099
1100   if (n_collect_values != 4)
1101     return g_strdup_printf ("not enough value locations for `%s' passed",
1102         G_VALUE_TYPE_NAME (value));
1103   if (collect_values[1].v_int == 0)
1104     return g_strdup_printf ("passed '0' as first denominator for `%s'",
1105         G_VALUE_TYPE_NAME (value));
1106   if (collect_values[3].v_int == 0)
1107     return g_strdup_printf ("passed '0' as second denominator for `%s'",
1108         G_VALUE_TYPE_NAME (value));
1109   if (gst_util_fraction_compare (collect_values[0].v_int,
1110           collect_values[1].v_int, collect_values[2].v_int,
1111           collect_values[3].v_int) >= 0)
1112     return g_strdup_printf ("range start is not smaller than end for `%s'",
1113         G_VALUE_TYPE_NAME (value));
1114
1115   if (vals == NULL) {
1116     gst_value_init_fraction_range (value);
1117     vals = value->data[0].v_pointer;
1118   }
1119
1120   gst_value_set_fraction (&vals[0], collect_values[0].v_int,
1121       collect_values[1].v_int);
1122   gst_value_set_fraction (&vals[1], collect_values[2].v_int,
1123       collect_values[3].v_int);
1124
1125   return NULL;
1126 }
1127
1128 static gchar *
1129 gst_value_lcopy_fraction_range (const GValue * value, guint n_collect_values,
1130     GTypeCValue * collect_values, guint collect_flags)
1131 {
1132   gint i;
1133   gint *dest_values[4];
1134   GValue *vals = (GValue *) value->data[0].v_pointer;
1135
1136   if (G_UNLIKELY (n_collect_values != 4))
1137     return g_strdup_printf ("not enough value locations for `%s' passed",
1138         G_VALUE_TYPE_NAME (value));
1139
1140   for (i = 0; i < 4; i++) {
1141     if (G_UNLIKELY (collect_values[i].v_pointer == NULL)) {
1142       return g_strdup_printf ("value location for `%s' passed as NULL",
1143           G_VALUE_TYPE_NAME (value));
1144     }
1145     dest_values[i] = collect_values[i].v_pointer;
1146   }
1147
1148   if (G_UNLIKELY (vals == NULL)) {
1149     return g_strdup_printf ("Uninitialised `%s' passed",
1150         G_VALUE_TYPE_NAME (value));
1151   }
1152
1153   dest_values[0][0] = gst_value_get_fraction_numerator (&vals[0]);
1154   dest_values[1][0] = gst_value_get_fraction_denominator (&vals[0]);
1155   dest_values[2][0] = gst_value_get_fraction_numerator (&vals[1]);
1156   dest_values[3][0] = gst_value_get_fraction_denominator (&vals[1]);
1157   return NULL;
1158 }
1159
1160 /**
1161  * gst_value_set_fraction_range:
1162  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1163  * @start: the start of the range (a GST_TYPE_FRACTION GValue)
1164  * @end: the end of the range (a GST_TYPE_FRACTION GValue)
1165  *
1166  * Sets @value to the range specified by @start and @end.
1167  */
1168 void
1169 gst_value_set_fraction_range (GValue * value, const GValue * start,
1170     const GValue * end)
1171 {
1172   GValue *vals;
1173
1174   g_return_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value));
1175   g_return_if_fail (GST_VALUE_HOLDS_FRACTION (start));
1176   g_return_if_fail (GST_VALUE_HOLDS_FRACTION (end));
1177   g_return_if_fail (gst_util_fraction_compare (start->data[0].v_int,
1178           start->data[1].v_int, end->data[0].v_int, end->data[1].v_int) < 0);
1179
1180   vals = (GValue *) value->data[0].v_pointer;
1181   if (vals == NULL) {
1182     gst_value_init_fraction_range (value);
1183     vals = value->data[0].v_pointer;
1184   }
1185   g_value_copy (start, &vals[0]);
1186   g_value_copy (end, &vals[1]);
1187 }
1188
1189 /**
1190  * gst_value_set_fraction_range_full:
1191  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1192  * @numerator_start: the numerator start of the range
1193  * @denominator_start: the denominator start of the range
1194  * @numerator_end: the numerator end of the range
1195  * @denominator_end: the denominator end of the range
1196  *
1197  * Sets @value to the range specified by @numerator_start/@denominator_start
1198  * and @numerator_end/@denominator_end.
1199  */
1200 void
1201 gst_value_set_fraction_range_full (GValue * value,
1202     gint numerator_start, gint denominator_start,
1203     gint numerator_end, gint denominator_end)
1204 {
1205   GValue start = { 0 };
1206   GValue end = { 0 };
1207
1208   g_return_if_fail (value != NULL);
1209   g_return_if_fail (denominator_start != 0);
1210   g_return_if_fail (denominator_end != 0);
1211   g_return_if_fail (gst_util_fraction_compare (numerator_start,
1212           denominator_start, numerator_end, denominator_end) < 0);
1213
1214   g_value_init (&start, GST_TYPE_FRACTION);
1215   g_value_init (&end, GST_TYPE_FRACTION);
1216
1217   gst_value_set_fraction (&start, numerator_start, denominator_start);
1218   gst_value_set_fraction (&end, numerator_end, denominator_end);
1219   gst_value_set_fraction_range (value, &start, &end);
1220
1221   g_value_unset (&start);
1222   g_value_unset (&end);
1223 }
1224
1225 /**
1226  * gst_value_get_fraction_range_min:
1227  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1228  *
1229  * Gets the minimum of the range specified by @value.
1230  *
1231  * Returns: the minimum of the range
1232  */
1233 const GValue *
1234 gst_value_get_fraction_range_min (const GValue * value)
1235 {
1236   GValue *vals;
1237
1238   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), NULL);
1239
1240   vals = (GValue *) value->data[0].v_pointer;
1241   if (vals != NULL) {
1242     return &vals[0];
1243   }
1244
1245   return NULL;
1246 }
1247
1248 /**
1249  * gst_value_get_fraction_range_max:
1250  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1251  *
1252  * Gets the maximum of the range specified by @value.
1253  *
1254  * Returns: the maximum of the range
1255  */
1256 const GValue *
1257 gst_value_get_fraction_range_max (const GValue * value)
1258 {
1259   GValue *vals;
1260
1261   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), NULL);
1262
1263   vals = (GValue *) value->data[0].v_pointer;
1264   if (vals != NULL) {
1265     return &vals[1];
1266   }
1267
1268   return NULL;
1269 }
1270
1271 static gchar *
1272 gst_value_serialize_fraction_range (const GValue * value)
1273 {
1274   GValue *vals = (GValue *) value->data[0].v_pointer;
1275   gchar *retval;
1276
1277   if (vals == NULL) {
1278     retval = g_strdup ("[ 0/1, 0/1 ]");
1279   } else {
1280     gchar *start, *end;
1281
1282     start = gst_value_serialize_fraction (&vals[0]);
1283     end = gst_value_serialize_fraction (&vals[1]);
1284
1285     retval = g_strdup_printf ("[ %s, %s ]", start, end);
1286     g_free (start);
1287     g_free (end);
1288   }
1289
1290   return retval;
1291 }
1292
1293 static void
1294 gst_value_transform_fraction_range_string (const GValue * src_value,
1295     GValue * dest_value)
1296 {
1297   dest_value->data[0].v_pointer =
1298       gst_value_serialize_fraction_range (src_value);
1299 }
1300
1301 static gint
1302 gst_value_compare_fraction_range (const GValue * value1, const GValue * value2)
1303 {
1304   GValue *vals1, *vals2;
1305   GstValueCompareFunc compare;
1306
1307   if (value2->data[0].v_pointer == value1->data[0].v_pointer)
1308     return GST_VALUE_EQUAL;     /* Only possible if both are NULL */
1309
1310   if (value2->data[0].v_pointer == NULL || value1->data[0].v_pointer == NULL)
1311     return GST_VALUE_UNORDERED;
1312
1313   vals1 = (GValue *) value1->data[0].v_pointer;
1314   vals2 = (GValue *) value2->data[0].v_pointer;
1315   if ((compare = gst_value_get_compare_func (&vals1[0]))) {
1316     if (gst_value_compare_with_func (&vals1[0], &vals2[0], compare) ==
1317         GST_VALUE_EQUAL &&
1318         gst_value_compare_with_func (&vals1[1], &vals2[1], compare) ==
1319         GST_VALUE_EQUAL)
1320       return GST_VALUE_EQUAL;
1321   }
1322   return GST_VALUE_UNORDERED;
1323 }
1324
1325 static gboolean
1326 gst_value_deserialize_fraction_range (GValue * dest, const gchar * s)
1327 {
1328   g_warning ("unimplemented");
1329   return FALSE;
1330 }
1331
1332 /***********
1333  * GstCaps *
1334  ***********/
1335
1336 /**
1337  * gst_value_set_caps:
1338  * @value: a GValue initialized to GST_TYPE_CAPS
1339  * @caps: the caps to set the value to
1340  *
1341  * Sets the contents of @value to @caps. A reference to the
1342  * provided @caps will be taken by the @value.
1343  */
1344 void
1345 gst_value_set_caps (GValue * value, const GstCaps * caps)
1346 {
1347   g_return_if_fail (G_IS_VALUE (value));
1348   g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS);
1349   g_return_if_fail (caps == NULL || GST_IS_CAPS (caps));
1350
1351   g_value_set_boxed (value, caps);
1352 }
1353
1354 /**
1355  * gst_value_get_caps:
1356  * @value: a GValue initialized to GST_TYPE_CAPS
1357  *
1358  * Gets the contents of @value. The reference count of the returned
1359  * #GstCaps will not be modified, therefore the caller must take one
1360  * before getting rid of the @value.
1361  *
1362  * Returns: the contents of @value
1363  */
1364 const GstCaps *
1365 gst_value_get_caps (const GValue * value)
1366 {
1367   g_return_val_if_fail (G_IS_VALUE (value), NULL);
1368   g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS, NULL);
1369
1370   return (GstCaps *) g_value_get_boxed (value);
1371 }
1372
1373 static gchar *
1374 gst_value_serialize_caps (const GValue * value)
1375 {
1376   GstCaps *caps = g_value_get_boxed (value);
1377
1378   return gst_caps_to_string (caps);
1379 }
1380
1381 static gboolean
1382 gst_value_deserialize_caps (GValue * dest, const gchar * s)
1383 {
1384   GstCaps *caps;
1385
1386   caps = gst_caps_from_string (s);
1387
1388   if (caps) {
1389     g_value_take_boxed (dest, caps);
1390     return TRUE;
1391   }
1392   return FALSE;
1393 }
1394
1395 /****************
1396  * GstStructure *
1397  ****************/
1398
1399 /**
1400  * gst_value_set_structure:
1401  * @value: a GValue initialized to GST_TYPE_STRUCTURE
1402  * @structure: the structure to set the value to
1403  *
1404  * Sets the contents of @value to @structure.  The actual
1405  *
1406  * Since: 0.10.15
1407  */
1408 void
1409 gst_value_set_structure (GValue * value, const GstStructure * structure)
1410 {
1411   g_return_if_fail (G_IS_VALUE (value));
1412   g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE);
1413   g_return_if_fail (structure == NULL || GST_IS_STRUCTURE (structure));
1414
1415   g_value_set_boxed (value, structure);
1416 }
1417
1418 /**
1419  * gst_value_get_structure:
1420  * @value: a GValue initialized to GST_TYPE_STRUCTURE
1421  *
1422  * Gets the contents of @value.
1423  *
1424  * Returns: the contents of @value
1425  *
1426  * Since: 0.10.15
1427  */
1428 const GstStructure *
1429 gst_value_get_structure (const GValue * value)
1430 {
1431   g_return_val_if_fail (G_IS_VALUE (value), NULL);
1432   g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_STRUCTURE, NULL);
1433
1434   return (GstStructure *) g_value_get_boxed (value);
1435 }
1436
1437 static gchar *
1438 gst_value_serialize_structure (const GValue * value)
1439 {
1440   GstStructure *structure = g_value_get_boxed (value);
1441
1442   return gst_string_take_and_wrap (gst_structure_to_string (structure));
1443 }
1444
1445 static gboolean
1446 gst_value_deserialize_structure (GValue * dest, const gchar * s)
1447 {
1448   GstStructure *structure;
1449
1450   if (*s != '"') {
1451     structure = gst_structure_from_string (s, NULL);
1452   } else {
1453     gchar *str = gst_string_unwrap (s);
1454
1455     if (G_UNLIKELY (!str))
1456       return FALSE;
1457
1458     structure = gst_structure_from_string (str, NULL);
1459     g_free (str);
1460   }
1461
1462   if (G_LIKELY (structure)) {
1463     g_value_take_boxed (dest, structure);
1464     return TRUE;
1465   }
1466   return FALSE;
1467 }
1468
1469 /*************
1470  * GstBuffer *
1471  *************/
1472
1473 static gint
1474 gst_value_compare_buffer (const GValue * value1, const GValue * value2)
1475 {
1476   GstBuffer *buf1 = GST_BUFFER (gst_value_get_mini_object (value1));
1477   GstBuffer *buf2 = GST_BUFFER (gst_value_get_mini_object (value2));
1478
1479   if (GST_BUFFER_SIZE (buf1) != GST_BUFFER_SIZE (buf2))
1480     return GST_VALUE_UNORDERED;
1481   if (GST_BUFFER_SIZE (buf1) == 0)
1482     return GST_VALUE_EQUAL;
1483   g_assert (GST_BUFFER_DATA (buf1));
1484   g_assert (GST_BUFFER_DATA (buf2));
1485   if (memcmp (GST_BUFFER_DATA (buf1), GST_BUFFER_DATA (buf2),
1486           GST_BUFFER_SIZE (buf1)) == 0)
1487     return GST_VALUE_EQUAL;
1488
1489   return GST_VALUE_UNORDERED;
1490 }
1491
1492 static gchar *
1493 gst_value_serialize_buffer (const GValue * value)
1494 {
1495   guint8 *data;
1496   gint i;
1497   gint size;
1498   gchar *string;
1499   GstBuffer *buffer;
1500
1501   buffer = gst_value_get_buffer (value);
1502   if (buffer == NULL)
1503     return NULL;
1504
1505   data = GST_BUFFER_DATA (buffer);
1506   size = GST_BUFFER_SIZE (buffer);
1507
1508   string = g_malloc (size * 2 + 1);
1509   for (i = 0; i < size; i++) {
1510     sprintf (string + i * 2, "%02x", data[i]);
1511   }
1512   string[size * 2] = 0;
1513
1514   return string;
1515 }
1516
1517 static gboolean
1518 gst_value_deserialize_buffer (GValue * dest, const gchar * s)
1519 {
1520   GstBuffer *buffer;
1521   gint len;
1522   gchar ts[3];
1523   guint8 *data;
1524   gint i;
1525
1526   len = strlen (s);
1527   if (len & 1)
1528     goto wrong_length;
1529
1530   buffer = gst_buffer_new_and_alloc (len / 2);
1531   data = GST_BUFFER_DATA (buffer);
1532   for (i = 0; i < len / 2; i++) {
1533     if (!isxdigit ((int) s[i * 2]) || !isxdigit ((int) s[i * 2 + 1]))
1534       goto wrong_char;
1535
1536     ts[0] = s[i * 2 + 0];
1537     ts[1] = s[i * 2 + 1];
1538     ts[2] = 0;
1539
1540     data[i] = (guint8) strtoul (ts, NULL, 16);
1541   }
1542
1543   gst_value_take_buffer (dest, buffer);
1544
1545   return TRUE;
1546
1547   /* ERRORS */
1548 wrong_length:
1549   {
1550     return FALSE;
1551   }
1552 wrong_char:
1553   {
1554     gst_buffer_unref (buffer);
1555     return FALSE;
1556   }
1557 }
1558
1559
1560 /***********
1561  * boolean *
1562  ***********/
1563
1564 static gint
1565 gst_value_compare_boolean (const GValue * value1, const GValue * value2)
1566 {
1567   if ((value1->data[0].v_int != 0) == (value2->data[0].v_int != 0))
1568     return GST_VALUE_EQUAL;
1569   return GST_VALUE_UNORDERED;
1570 }
1571
1572 static gchar *
1573 gst_value_serialize_boolean (const GValue * value)
1574 {
1575   if (value->data[0].v_int) {
1576     return g_strdup ("true");
1577   }
1578   return g_strdup ("false");
1579 }
1580
1581 static gboolean
1582 gst_value_deserialize_boolean (GValue * dest, const gchar * s)
1583 {
1584   gboolean ret = FALSE;
1585
1586   if (g_ascii_strcasecmp (s, "true") == 0 ||
1587       g_ascii_strcasecmp (s, "yes") == 0 ||
1588       g_ascii_strcasecmp (s, "t") == 0 || strcmp (s, "1") == 0) {
1589     g_value_set_boolean (dest, TRUE);
1590     ret = TRUE;
1591   } else if (g_ascii_strcasecmp (s, "false") == 0 ||
1592       g_ascii_strcasecmp (s, "no") == 0 ||
1593       g_ascii_strcasecmp (s, "f") == 0 || strcmp (s, "0") == 0) {
1594     g_value_set_boolean (dest, FALSE);
1595     ret = TRUE;
1596   }
1597
1598   return ret;
1599 }
1600
1601 #define CREATE_SERIALIZATION_START(_type,_macro)                        \
1602 static gint                                                             \
1603 gst_value_compare_ ## _type                                             \
1604 (const GValue * value1, const GValue * value2)                          \
1605 {                                                                       \
1606   g ## _type val1 = g_value_get_ ## _type (value1);                     \
1607   g ## _type val2 = g_value_get_ ## _type (value2);                     \
1608   if (val1 > val2)                                                      \
1609     return GST_VALUE_GREATER_THAN;                                      \
1610   if (val1 < val2)                                                      \
1611     return GST_VALUE_LESS_THAN;                                         \
1612   return GST_VALUE_EQUAL;                                               \
1613 }                                                                       \
1614                                                                         \
1615 static gchar *                                                          \
1616 gst_value_serialize_ ## _type (const GValue * value)                    \
1617 {                                                                       \
1618   GValue val = { 0, };                                                  \
1619   g_value_init (&val, G_TYPE_STRING);                                   \
1620   if (!g_value_transform (value, &val))                                 \
1621     g_assert_not_reached ();                                            \
1622   /* NO_COPY_MADNESS!!! */                                              \
1623   return (char *) g_value_get_string (&val);                            \
1624 }
1625
1626 /* deserialize the given s into to as a gint64.
1627  * check if the result is actually storeable in the given size number of
1628  * bytes.
1629  */
1630 static gboolean
1631 gst_value_deserialize_int_helper (gint64 * to, const gchar * s,
1632     gint64 min, gint64 max, gint size)
1633 {
1634   gboolean ret = FALSE;
1635   gchar *end;
1636   gint64 mask = -1;
1637
1638   errno = 0;
1639   *to = g_ascii_strtoull (s, &end, 0);
1640   /* a range error is a definitive no-no */
1641   if (errno == ERANGE) {
1642     return FALSE;
1643   }
1644
1645   if (*end == 0) {
1646     ret = TRUE;
1647   } else {
1648     if (g_ascii_strcasecmp (s, "little_endian") == 0) {
1649       *to = G_LITTLE_ENDIAN;
1650       ret = TRUE;
1651     } else if (g_ascii_strcasecmp (s, "big_endian") == 0) {
1652       *to = G_BIG_ENDIAN;
1653       ret = TRUE;
1654     } else if (g_ascii_strcasecmp (s, "byte_order") == 0) {
1655       *to = G_BYTE_ORDER;
1656       ret = TRUE;
1657     } else if (g_ascii_strcasecmp (s, "min") == 0) {
1658       *to = min;
1659       ret = TRUE;
1660     } else if (g_ascii_strcasecmp (s, "max") == 0) {
1661       *to = max;
1662       ret = TRUE;
1663     }
1664   }
1665   if (ret) {
1666     /* by definition, a gint64 fits into a gint64; so ignore those */
1667     if (size != sizeof (mask)) {
1668       if (*to >= 0) {
1669         /* for positive numbers, we create a mask of 1's outside of the range
1670          * and 0's inside the range.  An and will thus keep only 1 bits
1671          * outside of the range */
1672         mask <<= (size * 8);
1673         if ((mask & *to) != 0) {
1674           ret = FALSE;
1675         }
1676       } else {
1677         /* for negative numbers, we do a 2's complement version */
1678         mask <<= ((size * 8) - 1);
1679         if ((mask & *to) != mask) {
1680           ret = FALSE;
1681         }
1682       }
1683     }
1684   }
1685   return ret;
1686 }
1687
1688 #define CREATE_SERIALIZATION(_type,_macro)                              \
1689 CREATE_SERIALIZATION_START(_type,_macro)                                \
1690                                                                         \
1691 static gboolean                                                         \
1692 gst_value_deserialize_ ## _type (GValue * dest, const gchar *s)         \
1693 {                                                                       \
1694   gint64 x;                                                             \
1695                                                                         \
1696   if (gst_value_deserialize_int_helper (&x, s, G_MIN ## _macro,         \
1697       G_MAX ## _macro, sizeof (g ## _type))) {                          \
1698     g_value_set_ ## _type (dest, /*(g ## _type)*/ x);                   \
1699     return TRUE;                                                        \
1700   } else {                                                              \
1701     return FALSE;                                                       \
1702   }                                                                     \
1703 }
1704
1705 #define CREATE_USERIALIZATION(_type,_macro)                             \
1706 CREATE_SERIALIZATION_START(_type,_macro)                                \
1707                                                                         \
1708 static gboolean                                                         \
1709 gst_value_deserialize_ ## _type (GValue * dest, const gchar *s)         \
1710 {                                                                       \
1711   gint64 x;                                                             \
1712   gchar *end;                                                           \
1713   gboolean ret = FALSE;                                                 \
1714                                                                         \
1715   errno = 0;                                                            \
1716   x = g_ascii_strtoull (s, &end, 0);                                    \
1717   /* a range error is a definitive no-no */                             \
1718   if (errno == ERANGE) {                                                \
1719     return FALSE;                                                       \
1720   }                                                                     \
1721   /* the cast ensures the range check later on makes sense */           \
1722   x = (g ## _type) x;                                                   \
1723   if (*end == 0) {                                                      \
1724     ret = TRUE;                                                         \
1725   } else {                                                              \
1726     if (g_ascii_strcasecmp (s, "little_endian") == 0) {                 \
1727       x = G_LITTLE_ENDIAN;                                              \
1728       ret = TRUE;                                                       \
1729     } else if (g_ascii_strcasecmp (s, "big_endian") == 0) {             \
1730       x = G_BIG_ENDIAN;                                                 \
1731       ret = TRUE;                                                       \
1732     } else if (g_ascii_strcasecmp (s, "byte_order") == 0) {             \
1733       x = G_BYTE_ORDER;                                                 \
1734       ret = TRUE;                                                       \
1735     } else if (g_ascii_strcasecmp (s, "min") == 0) {                    \
1736       x = 0;                                                            \
1737       ret = TRUE;                                                       \
1738     } else if (g_ascii_strcasecmp (s, "max") == 0) {                    \
1739       x = G_MAX ## _macro;                                              \
1740       ret = TRUE;                                                       \
1741     }                                                                   \
1742   }                                                                     \
1743   if (ret) {                                                            \
1744     if (x > G_MAX ## _macro) {                                          \
1745       ret = FALSE;                                                      \
1746     } else {                                                            \
1747       g_value_set_ ## _type (dest, x);                                  \
1748     }                                                                   \
1749   }                                                                     \
1750   return ret;                                                           \
1751 }
1752
1753 #define REGISTER_SERIALIZATION(_gtype, _type)                           \
1754 G_STMT_START {                                                          \
1755   static const GstValueTable gst_value = {                              \
1756     _gtype,                                                             \
1757     gst_value_compare_ ## _type,                                        \
1758     gst_value_serialize_ ## _type,                                      \
1759     gst_value_deserialize_ ## _type,                                    \
1760   };                                                                    \
1761                                                                         \
1762   gst_value_register (&gst_value);                                      \
1763 } G_STMT_END
1764
1765 CREATE_SERIALIZATION (int, INT);
1766 CREATE_SERIALIZATION (int64, INT64);
1767 CREATE_SERIALIZATION (long, LONG);
1768
1769 CREATE_USERIALIZATION (uint, UINT);
1770 CREATE_USERIALIZATION (uint64, UINT64);
1771 CREATE_USERIALIZATION (ulong, ULONG);
1772
1773 /**********
1774  * double *
1775  **********/
1776 static gint
1777 gst_value_compare_double (const GValue * value1, const GValue * value2)
1778 {
1779   if (value1->data[0].v_double > value2->data[0].v_double)
1780     return GST_VALUE_GREATER_THAN;
1781   if (value1->data[0].v_double < value2->data[0].v_double)
1782     return GST_VALUE_LESS_THAN;
1783   if (value1->data[0].v_double == value2->data[0].v_double)
1784     return GST_VALUE_EQUAL;
1785   return GST_VALUE_UNORDERED;
1786 }
1787
1788 static gchar *
1789 gst_value_serialize_double (const GValue * value)
1790 {
1791   gchar d[G_ASCII_DTOSTR_BUF_SIZE];
1792
1793   g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double);
1794   return g_strdup (d);
1795 }
1796
1797 static gboolean
1798 gst_value_deserialize_double (GValue * dest, const gchar * s)
1799 {
1800   gdouble x;
1801   gboolean ret = FALSE;
1802   gchar *end;
1803
1804   x = g_ascii_strtod (s, &end);
1805   if (*end == 0) {
1806     ret = TRUE;
1807   } else {
1808     if (g_ascii_strcasecmp (s, "min") == 0) {
1809       x = -G_MAXDOUBLE;
1810       ret = TRUE;
1811     } else if (g_ascii_strcasecmp (s, "max") == 0) {
1812       x = G_MAXDOUBLE;
1813       ret = TRUE;
1814     }
1815   }
1816   if (ret) {
1817     g_value_set_double (dest, x);
1818   }
1819   return ret;
1820 }
1821
1822 /*********
1823  * float *
1824  *********/
1825
1826 static gint
1827 gst_value_compare_float (const GValue * value1, const GValue * value2)
1828 {
1829   if (value1->data[0].v_float > value2->data[0].v_float)
1830     return GST_VALUE_GREATER_THAN;
1831   if (value1->data[0].v_float < value2->data[0].v_float)
1832     return GST_VALUE_LESS_THAN;
1833   if (value1->data[0].v_float == value2->data[0].v_float)
1834     return GST_VALUE_EQUAL;
1835   return GST_VALUE_UNORDERED;
1836 }
1837
1838 static gchar *
1839 gst_value_serialize_float (const GValue * value)
1840 {
1841   gchar d[G_ASCII_DTOSTR_BUF_SIZE];
1842
1843   g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_float);
1844   return g_strdup (d);
1845 }
1846
1847 static gboolean
1848 gst_value_deserialize_float (GValue * dest, const gchar * s)
1849 {
1850   gdouble x;
1851   gboolean ret = FALSE;
1852   gchar *end;
1853
1854   x = g_ascii_strtod (s, &end);
1855   if (*end == 0) {
1856     ret = TRUE;
1857   } else {
1858     if (g_ascii_strcasecmp (s, "min") == 0) {
1859       x = -G_MAXFLOAT;
1860       ret = TRUE;
1861     } else if (g_ascii_strcasecmp (s, "max") == 0) {
1862       x = G_MAXFLOAT;
1863       ret = TRUE;
1864     }
1865   }
1866   if (x > G_MAXFLOAT || x < -G_MAXFLOAT)
1867     ret = FALSE;
1868   if (ret) {
1869     g_value_set_float (dest, (float) x);
1870   }
1871   return ret;
1872 }
1873
1874 /**********
1875  * string *
1876  **********/
1877
1878 static gint
1879 gst_value_compare_string (const GValue * value1, const GValue * value2)
1880 {
1881   if (G_UNLIKELY (!value1->data[0].v_pointer || !value2->data[0].v_pointer)) {
1882     /* if only one is NULL, no match - otherwise both NULL == EQUAL */
1883     if (value1->data[0].v_pointer != value2->data[0].v_pointer)
1884       return GST_VALUE_UNORDERED;
1885   } else {
1886     gint x = strcmp (value1->data[0].v_pointer, value2->data[0].v_pointer);
1887
1888     if (x < 0)
1889       return GST_VALUE_LESS_THAN;
1890     if (x > 0)
1891       return GST_VALUE_GREATER_THAN;
1892   }
1893
1894   return GST_VALUE_EQUAL;
1895 }
1896
1897 static gint
1898 gst_string_measure_wrapping (const gchar * s)
1899 {
1900   gint len;
1901   gboolean wrap = FALSE;
1902
1903   if (G_UNLIKELY (s == NULL))
1904     return -1;
1905
1906   /* Special case: the actual string NULL needs wrapping */
1907   if (G_UNLIKELY (strcmp (s, "NULL") == 0))
1908     return 4;
1909
1910   len = 0;
1911   while (*s) {
1912     if (GST_ASCII_IS_STRING (*s)) {
1913       len++;
1914     } else if (*s < 0x20 || *s >= 0x7f) {
1915       wrap = TRUE;
1916       len += 4;
1917     } else {
1918       wrap = TRUE;
1919       len += 2;
1920     }
1921     s++;
1922   }
1923
1924   /* Wrap the string if we found something that needs
1925    * wrapping, or the empty string (len == 0) */
1926   return (wrap || len == 0) ? len : -1;
1927 }
1928
1929 static gchar *
1930 gst_string_wrap_inner (const gchar * s, gint len)
1931 {
1932   gchar *d, *e;
1933
1934   e = d = g_malloc (len + 3);
1935
1936   *e++ = '\"';
1937   while (*s) {
1938     if (GST_ASCII_IS_STRING (*s)) {
1939       *e++ = *s++;
1940     } else if (*s < 0x20 || *s >= 0x7f) {
1941       *e++ = '\\';
1942       *e++ = '0' + ((*(guchar *) s) >> 6);
1943       *e++ = '0' + (((*s) >> 3) & 0x7);
1944       *e++ = '0' + ((*s++) & 0x7);
1945     } else {
1946       *e++ = '\\';
1947       *e++ = *s++;
1948     }
1949   }
1950   *e++ = '\"';
1951   *e = 0;
1952
1953   g_assert (e - d <= len + 3);
1954   return d;
1955 }
1956
1957 /* Do string wrapping/escaping */
1958 static gchar *
1959 gst_string_wrap (const gchar * s)
1960 {
1961   gint len = gst_string_measure_wrapping (s);
1962
1963   if (G_LIKELY (len < 0))
1964     return g_strdup (s);
1965
1966   return gst_string_wrap_inner (s, len);
1967 }
1968
1969 /* Same as above, but take ownership of the string */
1970 static gchar *
1971 gst_string_take_and_wrap (gchar * s)
1972 {
1973   gchar *out;
1974   gint len = gst_string_measure_wrapping (s);
1975
1976   if (G_LIKELY (len < 0))
1977     return s;
1978
1979   out = gst_string_wrap_inner (s, len);
1980   g_free (s);
1981
1982   return out;
1983 }
1984
1985 /*
1986  * This function takes a string delimited with double quotes (")
1987  * and unescapes any \xxx octal numbers.
1988  *
1989  * If sequences of \y are found where y is not in the range of
1990  * 0->3, y is copied unescaped.
1991  *
1992  * If \xyy is found where x is an octal number but y is not, an
1993  * error is encountered and NULL is returned.
1994  *
1995  * the input string must be \0 terminated.
1996  */
1997 static gchar *
1998 gst_string_unwrap (const gchar * s)
1999 {
2000   gchar *ret;
2001   gchar *read, *write;
2002
2003   /* NULL string returns NULL */
2004   if (s == NULL)
2005     return NULL;
2006
2007   /* strings not starting with " are invalid */
2008   if (*s != '"')
2009     return NULL;
2010
2011   /* make copy of original string to hold the result. This
2012    * string will always be smaller than the original */
2013   ret = g_strdup (s);
2014   read = ret;
2015   write = ret;
2016
2017   /* need to move to the next position as we parsed the " */
2018   read++;
2019
2020   while (*read) {
2021     if (GST_ASCII_IS_STRING (*read)) {
2022       /* normal chars are just copied */
2023       *write++ = *read++;
2024     } else if (*read == '"') {
2025       /* quote marks end of string */
2026       break;
2027     } else if (*read == '\\') {
2028       /* got an escape char, move to next position to read a tripplet
2029        * of octal numbers */
2030       read++;
2031       /* is the next char a possible first octal number? */
2032       if (*read >= '0' && *read <= '3') {
2033         /* parse other 2 numbers, if one of them is not in the range of
2034          * an octal number, we error. We also catch the case where a zero
2035          * byte is found here. */
2036         if (read[1] < '0' || read[1] > '7' || read[2] < '0' || read[2] > '7')
2037           goto beach;
2038
2039         /* now convert the octal number to a byte again. */
2040         *write++ = ((read[0] - '0') << 6) +
2041             ((read[1] - '0') << 3) + (read[2] - '0');
2042
2043         read += 3;
2044       } else {
2045         /* if we run into a \0 here, we definately won't get a quote later */
2046         if (*read == 0)
2047           goto beach;
2048
2049         /* else copy \X sequence */
2050         *write++ = *read++;
2051       }
2052     } else {
2053       /* weird character, error */
2054       goto beach;
2055     }
2056   }
2057   /* if the string is not ending in " and zero terminated, we error */
2058   if (*read != '"' || read[1] != '\0')
2059     goto beach;
2060
2061   /* null terminate result string and return */
2062   *write = '\0';
2063   return ret;
2064
2065 beach:
2066   g_free (ret);
2067   return NULL;
2068 }
2069
2070 static gchar *
2071 gst_value_serialize_string (const GValue * value)
2072 {
2073   return gst_string_wrap (value->data[0].v_pointer);
2074 }
2075
2076 static gboolean
2077 gst_value_deserialize_string (GValue * dest, const gchar * s)
2078 {
2079   if (G_UNLIKELY (strcmp (s, "NULL") == 0)) {
2080     g_value_set_string (dest, NULL);
2081     return TRUE;
2082   } else if (G_LIKELY (*s != '"')) {
2083     if (!g_utf8_validate (s, -1, NULL))
2084       return FALSE;
2085     g_value_set_string (dest, s);
2086     return TRUE;
2087   } else {
2088     gchar *str = gst_string_unwrap (s);
2089     if (G_UNLIKELY (!str))
2090       return FALSE;
2091     g_value_take_string (dest, str);
2092   }
2093
2094   return TRUE;
2095 }
2096
2097 /********
2098  * enum *
2099  ********/
2100
2101 static gint
2102 gst_value_compare_enum (const GValue * value1, const GValue * value2)
2103 {
2104   GEnumValue *en1, *en2;
2105   GEnumClass *klass1 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value1));
2106   GEnumClass *klass2 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value2));
2107
2108   g_return_val_if_fail (klass1, GST_VALUE_UNORDERED);
2109   g_return_val_if_fail (klass2, GST_VALUE_UNORDERED);
2110   en1 = g_enum_get_value (klass1, g_value_get_enum (value1));
2111   en2 = g_enum_get_value (klass2, g_value_get_enum (value2));
2112   g_type_class_unref (klass1);
2113   g_type_class_unref (klass2);
2114   g_return_val_if_fail (en1, GST_VALUE_UNORDERED);
2115   g_return_val_if_fail (en2, GST_VALUE_UNORDERED);
2116   if (en1->value < en2->value)
2117     return GST_VALUE_LESS_THAN;
2118   if (en1->value > en2->value)
2119     return GST_VALUE_GREATER_THAN;
2120
2121   return GST_VALUE_EQUAL;
2122 }
2123
2124 static gchar *
2125 gst_value_serialize_enum (const GValue * value)
2126 {
2127   GEnumValue *en;
2128   GEnumClass *klass = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value));
2129
2130   g_return_val_if_fail (klass, NULL);
2131   en = g_enum_get_value (klass, g_value_get_enum (value));
2132   g_type_class_unref (klass);
2133
2134   /* might be one of the custom formats registered later */
2135   if (G_UNLIKELY (en == NULL && G_VALUE_TYPE (value) == GST_TYPE_FORMAT)) {
2136     const GstFormatDefinition *format_def;
2137
2138     format_def = gst_format_get_details (g_value_get_enum (value));
2139     g_return_val_if_fail (format_def != NULL, NULL);
2140     return g_strdup (format_def->description);
2141   }
2142
2143   g_return_val_if_fail (en, NULL);
2144   return g_strdup (en->value_name);
2145 }
2146
2147 static gint
2148 gst_value_deserialize_enum_iter_cmp (const GstFormatDefinition * format_def,
2149     const gchar * s)
2150 {
2151   if (g_ascii_strcasecmp (s, format_def->nick) == 0)
2152     return 0;
2153
2154   return g_ascii_strcasecmp (s, format_def->description);
2155 }
2156
2157 static gboolean
2158 gst_value_deserialize_enum (GValue * dest, const gchar * s)
2159 {
2160   GEnumValue *en;
2161   gchar *endptr = NULL;
2162   GEnumClass *klass = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (dest));
2163
2164   g_return_val_if_fail (klass, FALSE);
2165   if (!(en = g_enum_get_value_by_name (klass, s))) {
2166     if (!(en = g_enum_get_value_by_nick (klass, s))) {
2167       gint i = strtol (s, &endptr, 0);
2168
2169       if (endptr && *endptr == '\0') {
2170         en = g_enum_get_value (klass, i);
2171       }
2172     }
2173   }
2174   g_type_class_unref (klass);
2175
2176   /* might be one of the custom formats registered later */
2177   if (G_UNLIKELY (en == NULL && G_VALUE_TYPE (dest) == GST_TYPE_FORMAT)) {
2178     const GstFormatDefinition *format_def;
2179     GstIterator *iter;
2180
2181     iter = gst_format_iterate_definitions ();
2182
2183     format_def = gst_iterator_find_custom (iter,
2184         (GCompareFunc) gst_value_deserialize_enum_iter_cmp, (gpointer) s);
2185
2186     g_return_val_if_fail (format_def != NULL, FALSE);
2187     g_value_set_enum (dest, (gint) format_def->value);
2188     gst_iterator_free (iter);
2189     return TRUE;
2190   }
2191
2192   g_return_val_if_fail (en, FALSE);
2193   g_value_set_enum (dest, en->value);
2194   return TRUE;
2195 }
2196
2197 /********
2198  * flags *
2199  ********/
2200
2201 /* we just compare the value here */
2202 static gint
2203 gst_value_compare_flags (const GValue * value1, const GValue * value2)
2204 {
2205   guint fl1, fl2;
2206   GFlagsClass *klass1 =
2207       (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value1));
2208   GFlagsClass *klass2 =
2209       (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value2));
2210
2211   g_return_val_if_fail (klass1, GST_VALUE_UNORDERED);
2212   g_return_val_if_fail (klass2, GST_VALUE_UNORDERED);
2213   fl1 = g_value_get_flags (value1);
2214   fl2 = g_value_get_flags (value2);
2215   g_type_class_unref (klass1);
2216   g_type_class_unref (klass2);
2217   if (fl1 < fl2)
2218     return GST_VALUE_LESS_THAN;
2219   if (fl1 > fl2)
2220     return GST_VALUE_GREATER_THAN;
2221
2222   return GST_VALUE_EQUAL;
2223 }
2224
2225 /* the different flags are serialized separated with a + */
2226 static gchar *
2227 gst_value_serialize_flags (const GValue * value)
2228 {
2229   guint flags;
2230   GFlagsValue *fl;
2231   GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value));
2232   gchar *result, *tmp;
2233   gboolean first = TRUE;
2234
2235   g_return_val_if_fail (klass, NULL);
2236
2237   flags = g_value_get_flags (value);
2238
2239   /* if no flags are set, try to serialize to the _NONE string */
2240   if (!flags) {
2241     fl = g_flags_get_first_value (klass, flags);
2242     return g_strdup (fl->value_name);
2243   }
2244
2245   /* some flags are set, so serialize one by one */
2246   result = g_strdup ("");
2247   while (flags) {
2248     fl = g_flags_get_first_value (klass, flags);
2249     if (fl != NULL) {
2250       tmp = g_strconcat (result, (first ? "" : "+"), fl->value_name, NULL);
2251       g_free (result);
2252       result = tmp;
2253       first = FALSE;
2254
2255       /* clear flag */
2256       flags &= ~fl->value;
2257     }
2258   }
2259   g_type_class_unref (klass);
2260
2261   return result;
2262 }
2263
2264 static gboolean
2265 gst_value_deserialize_flags (GValue * dest, const gchar * s)
2266 {
2267   GFlagsValue *fl;
2268   gchar *endptr = NULL;
2269   GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (dest));
2270   gchar **split;
2271   guint flags;
2272   gint i;
2273
2274   g_return_val_if_fail (klass, FALSE);
2275
2276   /* split into parts delimited with + */
2277   split = g_strsplit (s, "+", 0);
2278
2279   flags = 0;
2280   i = 0;
2281   /* loop over each part */
2282   while (split[i]) {
2283     if (!(fl = g_flags_get_value_by_name (klass, split[i]))) {
2284       if (!(fl = g_flags_get_value_by_nick (klass, split[i]))) {
2285         gint val = strtol (split[i], &endptr, 0);
2286
2287         /* just or numeric value */
2288         if (endptr && *endptr == '\0') {
2289           flags |= val;
2290         }
2291       }
2292     }
2293     if (fl) {
2294       flags |= fl->value;
2295     }
2296     i++;
2297   }
2298   g_strfreev (split);
2299   g_type_class_unref (klass);
2300   g_value_set_flags (dest, flags);
2301
2302   return TRUE;
2303 }
2304
2305 /*********
2306  * union *
2307  *********/
2308
2309 static gboolean
2310 gst_value_union_int_int_range (GValue * dest, const GValue * src1,
2311     const GValue * src2)
2312 {
2313   if (src2->data[0].v_int <= src1->data[0].v_int &&
2314       src2->data[1].v_int >= src1->data[0].v_int) {
2315     gst_value_init_and_copy (dest, src2);
2316     return TRUE;
2317   }
2318   return FALSE;
2319 }
2320
2321 static gboolean
2322 gst_value_union_int_range_int_range (GValue * dest, const GValue * src1,
2323     const GValue * src2)
2324 {
2325   gint min;
2326   gint max;
2327
2328   min = MAX (src1->data[0].v_int, src2->data[0].v_int);
2329   max = MIN (src1->data[1].v_int, src2->data[1].v_int);
2330
2331   if (min <= max) {
2332     g_value_init (dest, GST_TYPE_INT_RANGE);
2333     gst_value_set_int_range (dest,
2334         MIN (src1->data[0].v_int, src2->data[0].v_int),
2335         MAX (src1->data[1].v_int, src2->data[1].v_int));
2336     return TRUE;
2337   }
2338
2339   return FALSE;
2340 }
2341
2342 /****************
2343  * intersection *
2344  ****************/
2345
2346 static gboolean
2347 gst_value_intersect_int_int_range (GValue * dest, const GValue * src1,
2348     const GValue * src2)
2349 {
2350   if (src2->data[0].v_int <= src1->data[0].v_int &&
2351       src2->data[1].v_int >= src1->data[0].v_int) {
2352     gst_value_init_and_copy (dest, src1);
2353     return TRUE;
2354   }
2355
2356   return FALSE;
2357 }
2358
2359 static gboolean
2360 gst_value_intersect_int_range_int_range (GValue * dest, const GValue * src1,
2361     const GValue * src2)
2362 {
2363   gint min;
2364   gint max;
2365
2366   min = MAX (src1->data[0].v_int, src2->data[0].v_int);
2367   max = MIN (src1->data[1].v_int, src2->data[1].v_int);
2368
2369   if (min < max) {
2370     g_value_init (dest, GST_TYPE_INT_RANGE);
2371     gst_value_set_int_range (dest, min, max);
2372     return TRUE;
2373   }
2374   if (min == max) {
2375     g_value_init (dest, G_TYPE_INT);
2376     g_value_set_int (dest, min);
2377     return TRUE;
2378   }
2379
2380   return FALSE;
2381 }
2382
2383 static gboolean
2384 gst_value_intersect_double_double_range (GValue * dest, const GValue * src1,
2385     const GValue * src2)
2386 {
2387   if (src2->data[0].v_double <= src1->data[0].v_double &&
2388       src2->data[1].v_double >= src1->data[0].v_double) {
2389     gst_value_init_and_copy (dest, src1);
2390     return TRUE;
2391   }
2392
2393   return FALSE;
2394 }
2395
2396 static gboolean
2397 gst_value_intersect_double_range_double_range (GValue * dest,
2398     const GValue * src1, const GValue * src2)
2399 {
2400   gdouble min;
2401   gdouble max;
2402
2403   min = MAX (src1->data[0].v_double, src2->data[0].v_double);
2404   max = MIN (src1->data[1].v_double, src2->data[1].v_double);
2405
2406   if (min < max) {
2407     g_value_init (dest, GST_TYPE_DOUBLE_RANGE);
2408     gst_value_set_double_range (dest, min, max);
2409     return TRUE;
2410   }
2411   if (min == max) {
2412     g_value_init (dest, G_TYPE_DOUBLE);
2413     g_value_set_int (dest, (int) min);
2414     return TRUE;
2415   }
2416
2417   return FALSE;
2418 }
2419
2420 static gboolean
2421 gst_value_intersect_list (GValue * dest, const GValue * value1,
2422     const GValue * value2)
2423 {
2424   guint i, size;
2425   GValue intersection = { 0, };
2426   gboolean ret = FALSE;
2427
2428   size = VALUE_LIST_SIZE (value1);
2429   for (i = 0; i < size; i++) {
2430     const GValue *cur = VALUE_LIST_GET_VALUE (value1, i);
2431
2432     if (gst_value_intersect (&intersection, cur, value2)) {
2433       /* append value */
2434       if (!ret) {
2435         gst_value_init_and_copy (dest, &intersection);
2436         ret = TRUE;
2437       } else if (GST_VALUE_HOLDS_LIST (dest)) {
2438         gst_value_list_append_value (dest, &intersection);
2439       } else {
2440         GValue temp = { 0, };
2441
2442         gst_value_init_and_copy (&temp, dest);
2443         g_value_unset (dest);
2444         gst_value_list_concat (dest, &temp, &intersection);
2445         g_value_unset (&temp);
2446       }
2447       g_value_unset (&intersection);
2448     }
2449   }
2450
2451   return ret;
2452 }
2453
2454 static gboolean
2455 gst_value_intersect_array (GValue * dest, const GValue * src1,
2456     const GValue * src2)
2457 {
2458   guint size;
2459   guint n;
2460   GValue val = { 0 };
2461
2462   /* only works on similar-sized arrays */
2463   size = gst_value_array_get_size (src1);
2464   if (size != gst_value_array_get_size (src2))
2465     return FALSE;
2466   g_value_init (dest, GST_TYPE_ARRAY);
2467
2468   for (n = 0; n < size; n++) {
2469     if (!gst_value_intersect (&val, gst_value_array_get_value (src1, n),
2470             gst_value_array_get_value (src2, n))) {
2471       g_value_unset (dest);
2472       return FALSE;
2473     }
2474     gst_value_array_append_value (dest, &val);
2475     g_value_unset (&val);
2476   }
2477
2478   return TRUE;
2479 }
2480
2481 static gboolean
2482 gst_value_intersect_fraction_fraction_range (GValue * dest, const GValue * src1,
2483     const GValue * src2)
2484 {
2485   gint res1, res2;
2486   GValue *vals;
2487   GstValueCompareFunc compare;
2488
2489   vals = src2->data[0].v_pointer;
2490
2491   if (vals == NULL)
2492     return FALSE;
2493
2494   if ((compare = gst_value_get_compare_func (src1))) {
2495     res1 = gst_value_compare_with_func (&vals[0], src1, compare);
2496     res2 = gst_value_compare_with_func (&vals[1], src1, compare);
2497
2498     if ((res1 == GST_VALUE_EQUAL || res1 == GST_VALUE_LESS_THAN) &&
2499         (res2 == GST_VALUE_EQUAL || res2 == GST_VALUE_GREATER_THAN)) {
2500       gst_value_init_and_copy (dest, src1);
2501       return TRUE;
2502     }
2503   }
2504
2505   return FALSE;
2506 }
2507
2508 static gboolean
2509 gst_value_intersect_fraction_range_fraction_range (GValue * dest,
2510     const GValue * src1, const GValue * src2)
2511 {
2512   GValue *min;
2513   GValue *max;
2514   gint res;
2515   GValue *vals1, *vals2;
2516   GstValueCompareFunc compare;
2517
2518   vals1 = src1->data[0].v_pointer;
2519   vals2 = src2->data[0].v_pointer;
2520   g_return_val_if_fail (vals1 != NULL && vals2 != NULL, FALSE);
2521
2522   if ((compare = gst_value_get_compare_func (&vals1[0]))) {
2523     /* min = MAX (src1.start, src2.start) */
2524     res = gst_value_compare_with_func (&vals1[0], &vals2[0], compare);
2525     g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
2526     if (res == GST_VALUE_LESS_THAN)
2527       min = &vals2[0];          /* Take the max of the 2 */
2528     else
2529       min = &vals1[0];
2530
2531     /* max = MIN (src1.end, src2.end) */
2532     res = gst_value_compare_with_func (&vals1[1], &vals2[1], compare);
2533     g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
2534     if (res == GST_VALUE_GREATER_THAN)
2535       max = &vals2[1];          /* Take the min of the 2 */
2536     else
2537       max = &vals1[1];
2538
2539     res = gst_value_compare_with_func (min, max, compare);
2540     g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
2541     if (res == GST_VALUE_LESS_THAN) {
2542       g_value_init (dest, GST_TYPE_FRACTION_RANGE);
2543       vals1 = dest->data[0].v_pointer;
2544       g_value_copy (min, &vals1[0]);
2545       g_value_copy (max, &vals1[1]);
2546       return TRUE;
2547     }
2548     if (res == GST_VALUE_EQUAL) {
2549       gst_value_init_and_copy (dest, min);
2550       return TRUE;
2551     }
2552   }
2553
2554   return FALSE;
2555 }
2556
2557 /***************
2558  * subtraction *
2559  ***************/
2560
2561 static gboolean
2562 gst_value_subtract_int_int_range (GValue * dest, const GValue * minuend,
2563     const GValue * subtrahend)
2564 {
2565   gint min = gst_value_get_int_range_min (subtrahend);
2566   gint max = gst_value_get_int_range_max (subtrahend);
2567   gint val = g_value_get_int (minuend);
2568
2569   /* subtracting a range from an int only works if the int is not in the
2570    * range */
2571   if (val < min || val > max) {
2572     /* and the result is the int */
2573     gst_value_init_and_copy (dest, minuend);
2574     return TRUE;
2575   }
2576   return FALSE;
2577 }
2578
2579 /* creates a new int range based on input values.
2580  */
2581 static gboolean
2582 gst_value_create_new_range (GValue * dest, gint min1, gint max1, gint min2,
2583     gint max2)
2584 {
2585   GValue v1 = { 0, };
2586   GValue v2 = { 0, };
2587   GValue *pv1, *pv2;            /* yeah, hungarian! */
2588
2589   if (min1 <= max1 && min2 <= max2) {
2590     pv1 = &v1;
2591     pv2 = &v2;
2592   } else if (min1 <= max1) {
2593     pv1 = dest;
2594     pv2 = NULL;
2595   } else if (min2 <= max2) {
2596     pv1 = NULL;
2597     pv2 = dest;
2598   } else {
2599     return FALSE;
2600   }
2601
2602   if (min1 < max1) {
2603     g_value_init (pv1, GST_TYPE_INT_RANGE);
2604     gst_value_set_int_range (pv1, min1, max1);
2605   } else if (min1 == max1) {
2606     g_value_init (pv1, G_TYPE_INT);
2607     g_value_set_int (pv1, min1);
2608   }
2609   if (min2 < max2) {
2610     g_value_init (pv2, GST_TYPE_INT_RANGE);
2611     gst_value_set_int_range (pv2, min2, max2);
2612   } else if (min2 == max2) {
2613     g_value_init (pv2, G_TYPE_INT);
2614     g_value_set_int (pv2, min2);
2615   }
2616
2617   if (min1 <= max1 && min2 <= max2) {
2618     gst_value_list_concat (dest, pv1, pv2);
2619     g_value_unset (pv1);
2620     g_value_unset (pv2);
2621   }
2622   return TRUE;
2623 }
2624
2625 static gboolean
2626 gst_value_subtract_int_range_int (GValue * dest, const GValue * minuend,
2627     const GValue * subtrahend)
2628 {
2629   gint min = gst_value_get_int_range_min (minuend);
2630   gint max = gst_value_get_int_range_max (minuend);
2631   gint val = g_value_get_int (subtrahend);
2632
2633   g_return_val_if_fail (min < max, FALSE);
2634
2635   /* value is outside of the range, return range unchanged */
2636   if (val < min || val > max) {
2637     gst_value_init_and_copy (dest, minuend);
2638     return TRUE;
2639   } else {
2640     /* max must be MAXINT too as val <= max */
2641     if (val == G_MAXINT) {
2642       max--;
2643       val--;
2644     }
2645     /* min must be MININT too as val >= max */
2646     if (val == G_MININT) {
2647       min++;
2648       val++;
2649     }
2650     gst_value_create_new_range (dest, min, val - 1, val + 1, max);
2651   }
2652   return TRUE;
2653 }
2654
2655 static gboolean
2656 gst_value_subtract_int_range_int_range (GValue * dest, const GValue * minuend,
2657     const GValue * subtrahend)
2658 {
2659   gint min1 = gst_value_get_int_range_min (minuend);
2660   gint max1 = gst_value_get_int_range_max (minuend);
2661   gint min2 = gst_value_get_int_range_min (subtrahend);
2662   gint max2 = gst_value_get_int_range_max (subtrahend);
2663
2664   if (max2 == G_MAXINT && min2 == G_MININT) {
2665     return FALSE;
2666   } else if (max2 == G_MAXINT) {
2667     return gst_value_create_new_range (dest, min1, MIN (min2 - 1, max1), 1, 0);
2668   } else if (min2 == G_MININT) {
2669     return gst_value_create_new_range (dest, MAX (max2 + 1, min1), max1, 1, 0);
2670   } else {
2671     return gst_value_create_new_range (dest, min1, MIN (min2 - 1, max1),
2672         MAX (max2 + 1, min1), max1);
2673   }
2674 }
2675
2676 static gboolean
2677 gst_value_subtract_double_double_range (GValue * dest, const GValue * minuend,
2678     const GValue * subtrahend)
2679 {
2680   gdouble min = gst_value_get_double_range_min (subtrahend);
2681   gdouble max = gst_value_get_double_range_max (subtrahend);
2682   gdouble val = g_value_get_double (minuend);
2683
2684   if (val < min || val > max) {
2685     gst_value_init_and_copy (dest, minuend);
2686     return TRUE;
2687   }
2688   return FALSE;
2689 }
2690
2691 static gboolean
2692 gst_value_subtract_double_range_double (GValue * dest, const GValue * minuend,
2693     const GValue * subtrahend)
2694 {
2695   /* since we don't have open ranges, we cannot create a hole in
2696    * a double range. We return the original range */
2697   gst_value_init_and_copy (dest, minuend);
2698   return TRUE;
2699 }
2700
2701 static gboolean
2702 gst_value_subtract_double_range_double_range (GValue * dest,
2703     const GValue * minuend, const GValue * subtrahend)
2704 {
2705   /* since we don't have open ranges, we have to approximate */
2706   /* done like with ints */
2707   gdouble min1 = gst_value_get_double_range_min (minuend);
2708   gdouble max2 = gst_value_get_double_range_max (minuend);
2709   gdouble max1 = MIN (gst_value_get_double_range_min (subtrahend), max2);
2710   gdouble min2 = MAX (gst_value_get_double_range_max (subtrahend), min1);
2711   GValue v1 = { 0, };
2712   GValue v2 = { 0, };
2713   GValue *pv1, *pv2;            /* yeah, hungarian! */
2714
2715   if (min1 < max1 && min2 < max2) {
2716     pv1 = &v1;
2717     pv2 = &v2;
2718   } else if (min1 < max1) {
2719     pv1 = dest;
2720     pv2 = NULL;
2721   } else if (min2 < max2) {
2722     pv1 = NULL;
2723     pv2 = dest;
2724   } else {
2725     return FALSE;
2726   }
2727
2728   if (min1 < max1) {
2729     g_value_init (pv1, GST_TYPE_DOUBLE_RANGE);
2730     gst_value_set_double_range (pv1, min1, max1);
2731   }
2732   if (min2 < max2) {
2733     g_value_init (pv2, GST_TYPE_DOUBLE_RANGE);
2734     gst_value_set_double_range (pv2, min2, max2);
2735   }
2736
2737   if (min1 < max1 && min2 < max2) {
2738     gst_value_list_concat (dest, pv1, pv2);
2739     g_value_unset (pv1);
2740     g_value_unset (pv2);
2741   }
2742   return TRUE;
2743 }
2744
2745 static gboolean
2746 gst_value_subtract_from_list (GValue * dest, const GValue * minuend,
2747     const GValue * subtrahend)
2748 {
2749   guint i, size;
2750   GValue subtraction = { 0, };
2751   gboolean ret = FALSE;
2752   GType ltype;
2753
2754   ltype = gst_value_list_get_type ();
2755
2756   size = VALUE_LIST_SIZE (minuend);
2757   for (i = 0; i < size; i++) {
2758     const GValue *cur = VALUE_LIST_GET_VALUE (minuend, i);
2759
2760     if (gst_value_subtract (&subtraction, cur, subtrahend)) {
2761       if (!ret) {
2762         gst_value_init_and_copy (dest, &subtraction);
2763         ret = TRUE;
2764       } else if (G_VALUE_HOLDS (dest, ltype)
2765           && !G_VALUE_HOLDS (&subtraction, ltype)) {
2766         gst_value_list_append_value (dest, &subtraction);
2767       } else {
2768         GValue temp = { 0, };
2769
2770         gst_value_init_and_copy (&temp, dest);
2771         g_value_unset (dest);
2772         gst_value_list_concat (dest, &temp, &subtraction);
2773         g_value_unset (&temp);
2774       }
2775       g_value_unset (&subtraction);
2776     }
2777   }
2778   return ret;
2779 }
2780
2781 static gboolean
2782 gst_value_subtract_list (GValue * dest, const GValue * minuend,
2783     const GValue * subtrahend)
2784 {
2785   guint i, size;
2786   GValue data[2] = { {0,}, {0,} };
2787   GValue *subtraction = &data[0], *result = &data[1];
2788
2789   gst_value_init_and_copy (result, minuend);
2790   size = VALUE_LIST_SIZE (subtrahend);
2791   for (i = 0; i < size; i++) {
2792     const GValue *cur = VALUE_LIST_GET_VALUE (subtrahend, i);
2793
2794     if (gst_value_subtract (subtraction, result, cur)) {
2795       GValue *temp = result;
2796
2797       result = subtraction;
2798       subtraction = temp;
2799       g_value_unset (subtraction);
2800     } else {
2801       g_value_unset (result);
2802       return FALSE;
2803     }
2804   }
2805   gst_value_init_and_copy (dest, result);
2806   g_value_unset (result);
2807   return TRUE;
2808 }
2809
2810 static gboolean
2811 gst_value_subtract_fraction_fraction_range (GValue * dest,
2812     const GValue * minuend, const GValue * subtrahend)
2813 {
2814   const GValue *min = gst_value_get_fraction_range_min (subtrahend);
2815   const GValue *max = gst_value_get_fraction_range_max (subtrahend);
2816   GstValueCompareFunc compare;
2817
2818   if ((compare = gst_value_get_compare_func (minuend))) {
2819     /* subtracting a range from an fraction only works if the fraction
2820      * is not in the range */
2821     if (gst_value_compare_with_func (minuend, min, compare) ==
2822         GST_VALUE_LESS_THAN ||
2823         gst_value_compare_with_func (minuend, max, compare) ==
2824         GST_VALUE_GREATER_THAN) {
2825       /* and the result is the value */
2826       gst_value_init_and_copy (dest, minuend);
2827       return TRUE;
2828     }
2829   }
2830   return FALSE;
2831 }
2832
2833 static gboolean
2834 gst_value_subtract_fraction_range_fraction (GValue * dest,
2835     const GValue * minuend, const GValue * subtrahend)
2836 {
2837   /* since we don't have open ranges, we cannot create a hole in
2838    * a range. We return the original range */
2839   gst_value_init_and_copy (dest, minuend);
2840   return TRUE;
2841 }
2842
2843 static gboolean
2844 gst_value_subtract_fraction_range_fraction_range (GValue * dest,
2845     const GValue * minuend, const GValue * subtrahend)
2846 {
2847   /* since we don't have open ranges, we have to approximate */
2848   /* done like with ints and doubles. Creates a list of 2 fraction ranges */
2849   const GValue *min1 = gst_value_get_fraction_range_min (minuend);
2850   const GValue *max2 = gst_value_get_fraction_range_max (minuend);
2851   const GValue *max1 = gst_value_get_fraction_range_min (subtrahend);
2852   const GValue *min2 = gst_value_get_fraction_range_max (subtrahend);
2853   gint cmp1, cmp2;
2854   GValue v1 = { 0, };
2855   GValue v2 = { 0, };
2856   GValue *pv1, *pv2;            /* yeah, hungarian! */
2857   GstValueCompareFunc compare;
2858
2859   g_return_val_if_fail (min1 != NULL && max1 != NULL, FALSE);
2860   g_return_val_if_fail (min2 != NULL && max2 != NULL, FALSE);
2861
2862   compare = gst_value_get_compare_func (min1);
2863   g_return_val_if_fail (compare, FALSE);
2864
2865   cmp1 = gst_value_compare_with_func (max2, max1, compare);
2866   g_return_val_if_fail (cmp1 != GST_VALUE_UNORDERED, FALSE);
2867   if (cmp1 == GST_VALUE_LESS_THAN)
2868     max1 = max2;
2869   cmp1 = gst_value_compare_with_func (min1, min2, compare);
2870   g_return_val_if_fail (cmp1 != GST_VALUE_UNORDERED, FALSE);
2871   if (cmp1 == GST_VALUE_GREATER_THAN)
2872     min2 = min1;
2873
2874   cmp1 = gst_value_compare_with_func (min1, max1, compare);
2875   cmp2 = gst_value_compare_with_func (min2, max2, compare);
2876
2877   if (cmp1 == GST_VALUE_LESS_THAN && cmp2 == GST_VALUE_LESS_THAN) {
2878     pv1 = &v1;
2879     pv2 = &v2;
2880   } else if (cmp1 == GST_VALUE_LESS_THAN) {
2881     pv1 = dest;
2882     pv2 = NULL;
2883   } else if (cmp2 == GST_VALUE_LESS_THAN) {
2884     pv1 = NULL;
2885     pv2 = dest;
2886   } else {
2887     return FALSE;
2888   }
2889
2890   if (cmp1 == GST_VALUE_LESS_THAN) {
2891     g_value_init (pv1, GST_TYPE_FRACTION_RANGE);
2892     gst_value_set_fraction_range (pv1, min1, max1);
2893   }
2894   if (cmp2 == GST_VALUE_LESS_THAN) {
2895     g_value_init (pv2, GST_TYPE_FRACTION_RANGE);
2896     gst_value_set_fraction_range (pv2, min2, max2);
2897   }
2898
2899   if (cmp1 == GST_VALUE_LESS_THAN && cmp2 == GST_VALUE_LESS_THAN) {
2900     gst_value_list_concat (dest, pv1, pv2);
2901     g_value_unset (pv1);
2902     g_value_unset (pv2);
2903   }
2904   return TRUE;
2905 }
2906
2907
2908 /**************
2909  * comparison *
2910  **************/
2911
2912 /*
2913  * gst_value_get_compare_func:
2914  * @value1: a value to get the compare function for
2915  *
2916  * Determines the compare function to be used with values of the same type as
2917  * @value1. The function can be given to gst_value_compare_with_func().
2918  *
2919  * Returns: A #GstValueCompareFunc value
2920  */
2921 static GstValueCompareFunc
2922 gst_value_get_compare_func (const GValue * value1)
2923 {
2924   GstValueTable *table, *best = NULL;
2925   guint i;
2926   GType type1;
2927
2928   type1 = G_VALUE_TYPE (value1);
2929
2930   /* this is a fast check */
2931   best = gst_value_hash_lookup_type (type1);
2932
2933   /* slower checks */
2934   if (G_UNLIKELY (!best || !best->compare)) {
2935     guint len = gst_value_table->len;
2936
2937     best = NULL;
2938     for (i = 0; i < len; i++) {
2939       table = &g_array_index (gst_value_table, GstValueTable, i);
2940       if (table->compare && g_type_is_a (type1, table->type)) {
2941         if (!best || g_type_is_a (table->type, best->type))
2942           best = table;
2943       }
2944     }
2945   }
2946   if (G_LIKELY (best))
2947     return best->compare;
2948
2949   return NULL;
2950 }
2951
2952 /**
2953  * gst_value_can_compare:
2954  * @value1: a value to compare
2955  * @value2: another value to compare
2956  *
2957  * Determines if @value1 and @value2 can be compared.
2958  *
2959  * Returns: TRUE if the values can be compared
2960  */
2961 gboolean
2962 gst_value_can_compare (const GValue * value1, const GValue * value2)
2963 {
2964   g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
2965   g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
2966
2967   if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
2968     return FALSE;
2969
2970   return gst_value_get_compare_func (value1) != NULL;
2971 }
2972
2973 /**
2974  * gst_value_compare:
2975  * @value1: a value to compare
2976  * @value2: another value to compare
2977  *
2978  * Compares @value1 and @value2.  If @value1 and @value2 cannot be
2979  * compared, the function returns GST_VALUE_UNORDERED.  Otherwise,
2980  * if @value1 is greater than @value2, GST_VALUE_GREATER_THAN is returned.
2981  * If @value1 is less than @value2, GST_VALUE_LESS_THAN is returned.
2982  * If the values are equal, GST_VALUE_EQUAL is returned.
2983  *
2984  * Returns: comparison result
2985  */
2986 gint
2987 gst_value_compare (const GValue * value1, const GValue * value2)
2988 {
2989   GstValueCompareFunc compare;
2990
2991   g_return_val_if_fail (G_IS_VALUE (value1), GST_VALUE_LESS_THAN);
2992   g_return_val_if_fail (G_IS_VALUE (value2), GST_VALUE_GREATER_THAN);
2993
2994   if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
2995     return GST_VALUE_UNORDERED;
2996
2997   compare = gst_value_get_compare_func (value1);
2998   if (compare) {
2999     return compare (value1, value2);
3000   }
3001
3002   g_critical ("unable to compare values of type %s\n",
3003       g_type_name (G_VALUE_TYPE (value1)));
3004   return GST_VALUE_UNORDERED;
3005 }
3006
3007 /*
3008  * gst_value_compare_with_func:
3009  * @value1: a value to compare
3010  * @value2: another value to compare
3011  * @compare: compare function
3012  *
3013  * Compares @value1 and @value2 using the @compare function. Works like
3014  * gst_value_compare() but allows to save time determining the compare function
3015  * a multiple times. 
3016  *
3017  * Returns: comparison result
3018  */
3019 static gint
3020 gst_value_compare_with_func (const GValue * value1, const GValue * value2,
3021     GstValueCompareFunc compare)
3022 {
3023   g_assert (compare);
3024
3025   if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
3026     return GST_VALUE_UNORDERED;
3027
3028   return compare (value1, value2);
3029 }
3030
3031 /* union */
3032
3033 /**
3034  * gst_value_can_union:
3035  * @value1: a value to union
3036  * @value2: another value to union
3037  *
3038  * Determines if @value1 and @value2 can be non-trivially unioned.
3039  * Any two values can be trivially unioned by adding both of them
3040  * to a GstValueList.  However, certain types have the possibility
3041  * to be unioned in a simpler way.  For example, an integer range
3042  * and an integer can be unioned if the integer is a subset of the
3043  * integer range.  If there is the possibility that two values can
3044  * be unioned, this function returns TRUE.
3045  *
3046  * Returns: TRUE if there is a function allowing the two values to
3047  * be unioned.
3048  */
3049 gboolean
3050 gst_value_can_union (const GValue * value1, const GValue * value2)
3051 {
3052   GstValueUnionInfo *union_info;
3053   guint i, len;
3054
3055   g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
3056   g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
3057
3058   len = gst_value_union_funcs->len;
3059
3060   for (i = 0; i < len; i++) {
3061     union_info = &g_array_index (gst_value_union_funcs, GstValueUnionInfo, i);
3062     if (union_info->type1 == G_VALUE_TYPE (value1) &&
3063         union_info->type2 == G_VALUE_TYPE (value2))
3064       return TRUE;
3065     if (union_info->type1 == G_VALUE_TYPE (value2) &&
3066         union_info->type2 == G_VALUE_TYPE (value1))
3067       return TRUE;
3068   }
3069
3070   return FALSE;
3071 }
3072
3073 /**
3074  * gst_value_union:
3075  * @dest: the destination value
3076  * @value1: a value to union
3077  * @value2: another value to union
3078  *
3079  * Creates a GValue corresponding to the union of @value1 and @value2.
3080  *
3081  * Returns: always returns %TRUE
3082  */
3083 /* FIXME: change return type to 'void'? */
3084 gboolean
3085 gst_value_union (GValue * dest, const GValue * value1, const GValue * value2)
3086 {
3087   GstValueUnionInfo *union_info;
3088   guint i, len;
3089
3090   g_return_val_if_fail (dest != NULL, FALSE);
3091   g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
3092   g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
3093
3094   len = gst_value_union_funcs->len;
3095
3096   for (i = 0; i < len; i++) {
3097     union_info = &g_array_index (gst_value_union_funcs, GstValueUnionInfo, i);
3098     if (union_info->type1 == G_VALUE_TYPE (value1) &&
3099         union_info->type2 == G_VALUE_TYPE (value2)) {
3100       if (union_info->func (dest, value1, value2)) {
3101         return TRUE;
3102       }
3103     }
3104     if (union_info->type1 == G_VALUE_TYPE (value2) &&
3105         union_info->type2 == G_VALUE_TYPE (value1)) {
3106       if (union_info->func (dest, value2, value1)) {
3107         return TRUE;
3108       }
3109     }
3110   }
3111
3112   gst_value_list_concat (dest, value1, value2);
3113   return TRUE;
3114 }
3115
3116 /**
3117  * gst_value_register_union_func:
3118  * @type1: a type to union
3119  * @type2: another type to union
3120  * @func: a function that implments creating a union between the two types
3121  *
3122  * Registers a union function that can create a union between #GValue items
3123  * of the type @type1 and @type2.
3124  *
3125  * Union functions should be registered at startup before any pipelines are
3126  * started, as gst_value_register_union_func() is not thread-safe and cannot
3127  * be used at the same time as gst_value_union() or gst_value_can_union().
3128  */
3129 void
3130 gst_value_register_union_func (GType type1, GType type2, GstValueUnionFunc func)
3131 {
3132   GstValueUnionInfo union_info;
3133
3134   union_info.type1 = type1;
3135   union_info.type2 = type2;
3136   union_info.func = func;
3137
3138   g_array_append_val (gst_value_union_funcs, union_info);
3139 }
3140
3141 /* intersection */
3142
3143 /**
3144  * gst_value_can_intersect:
3145  * @value1: a value to intersect
3146  * @value2: another value to intersect
3147  *
3148  * Determines if intersecting two values will produce a valid result.
3149  * Two values will produce a valid intersection if they have the same
3150  * type, or if there is a method (registered by
3151  * gst_value_register_intersect_func()) to calculate the intersection.
3152  *
3153  * Returns: TRUE if the values can intersect
3154  */
3155 gboolean
3156 gst_value_can_intersect (const GValue * value1, const GValue * value2)
3157 {
3158   GstValueIntersectInfo *intersect_info;
3159   guint i, len;
3160   GType ltype, type1, type2;
3161
3162   g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
3163   g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
3164
3165   ltype = gst_value_list_get_type ();
3166
3167   /* special cases */
3168   if (G_VALUE_HOLDS (value1, ltype) || G_VALUE_HOLDS (value2, ltype))
3169     return TRUE;
3170
3171   type1 = G_VALUE_TYPE (value1);
3172   type2 = G_VALUE_TYPE (value2);
3173
3174   /* practically all GstValue types have a compare function (_can_compare=TRUE)
3175    * GstStructure and GstCaps have npot, but are intersectable */
3176   if (type1 == type2)
3177     return TRUE;
3178
3179   /* check registered intersect functions */
3180   len = gst_value_intersect_funcs->len;
3181   for (i = 0; i < len; i++) {
3182     intersect_info = &g_array_index (gst_value_intersect_funcs,
3183         GstValueIntersectInfo, i);
3184     if ((intersect_info->type1 == type1 && intersect_info->type2 == type2) ||
3185         (intersect_info->type1 == type2 && intersect_info->type2 == type1))
3186       return TRUE;
3187   }
3188
3189   return gst_value_can_compare (value1, value2);
3190 }
3191
3192 /**
3193  * gst_value_intersect:
3194  * @dest: a uninitialized #GValue that will hold the calculated
3195  * intersection value
3196  * @value1: a value to intersect
3197  * @value2: another value to intersect
3198  *
3199  * Calculates the intersection of two values.  If the values have
3200  * a non-empty intersection, the value representing the intersection
3201  * is placed in @dest.  If the intersection is non-empty, @dest is
3202  * not modified.
3203  *
3204  * Returns: TRUE if the intersection is non-empty
3205  */
3206 gboolean
3207 gst_value_intersect (GValue * dest, const GValue * value1,
3208     const GValue * value2)
3209 {
3210   GstValueIntersectInfo *intersect_info;
3211   guint i, len;
3212   GType ltype, type1, type2;
3213
3214   g_return_val_if_fail (dest != NULL, FALSE);
3215   g_return_val_if_fail (G_IS_VALUE (value1), FALSE);
3216   g_return_val_if_fail (G_IS_VALUE (value2), FALSE);
3217
3218   ltype = gst_value_list_get_type ();
3219
3220   /* special cases first */
3221   if (G_VALUE_HOLDS (value1, ltype))
3222     return gst_value_intersect_list (dest, value1, value2);
3223   if (G_VALUE_HOLDS (value2, ltype))
3224     return gst_value_intersect_list (dest, value2, value1);
3225
3226   if (gst_value_compare (value1, value2) == GST_VALUE_EQUAL) {
3227     gst_value_init_and_copy (dest, value1);
3228     return TRUE;
3229   }
3230
3231   type1 = G_VALUE_TYPE (value1);
3232   type2 = G_VALUE_TYPE (value2);
3233
3234   len = gst_value_intersect_funcs->len;
3235   for (i = 0; i < len; i++) {
3236     intersect_info = &g_array_index (gst_value_intersect_funcs,
3237         GstValueIntersectInfo, i);
3238     if (intersect_info->type1 == type1 && intersect_info->type2 == type2) {
3239       return intersect_info->func (dest, value1, value2);
3240     }
3241     if (intersect_info->type1 == type2 && intersect_info->type2 == type1) {
3242       return intersect_info->func (dest, value2, value1);
3243     }
3244   }
3245   return FALSE;
3246 }
3247
3248 /**
3249  * gst_value_register_intersect_func:
3250  * @type1: the first type to intersect
3251  * @type2: the second type to intersect
3252  * @func: the intersection function
3253  *
3254  * Registers a function that is called to calculate the intersection
3255  * of the values having the types @type1 and @type2.
3256  *
3257  * Intersect functions should be registered at startup before any pipelines are
3258  * started, as gst_value_register_intersect_func() is not thread-safe and
3259  * cannot be used at the same time as gst_value_intersect() or
3260  * gst_value_can_intersect().
3261  */
3262 void
3263 gst_value_register_intersect_func (GType type1, GType type2,
3264     GstValueIntersectFunc func)
3265 {
3266   GstValueIntersectInfo intersect_info;
3267
3268   intersect_info.type1 = type1;
3269   intersect_info.type2 = type2;
3270   intersect_info.func = func;
3271
3272   g_array_append_val (gst_value_intersect_funcs, intersect_info);
3273 }
3274
3275
3276 /* subtraction */
3277
3278 /**
3279  * gst_value_subtract:
3280  * @dest: the destination value for the result if the subtraction is not empty
3281  * @minuend: the value to subtract from
3282  * @subtrahend: the value to subtract
3283  *
3284  * Subtracts @subtrahend from @minuend and stores the result in @dest.
3285  * Note that this means subtraction as in sets, not as in mathematics.
3286  *
3287  * Returns: %TRUE if the subtraction is not empty
3288  */
3289 gboolean
3290 gst_value_subtract (GValue * dest, const GValue * minuend,
3291     const GValue * subtrahend)
3292 {
3293   GstValueSubtractInfo *info;
3294   guint i, len;
3295   GType ltype, mtype, stype;
3296
3297   g_return_val_if_fail (dest != NULL, FALSE);
3298   g_return_val_if_fail (G_IS_VALUE (minuend), FALSE);
3299   g_return_val_if_fail (G_IS_VALUE (subtrahend), FALSE);
3300
3301   ltype = gst_value_list_get_type ();
3302
3303   /* special cases first */
3304   if (G_VALUE_HOLDS (minuend, ltype))
3305     return gst_value_subtract_from_list (dest, minuend, subtrahend);
3306   if (G_VALUE_HOLDS (subtrahend, ltype))
3307     return gst_value_subtract_list (dest, minuend, subtrahend);
3308
3309   mtype = G_VALUE_TYPE (minuend);
3310   stype = G_VALUE_TYPE (subtrahend);
3311
3312   len = gst_value_subtract_funcs->len;
3313   for (i = 0; i < len; i++) {
3314     info = &g_array_index (gst_value_subtract_funcs, GstValueSubtractInfo, i);
3315     if (info->minuend == mtype && info->subtrahend == stype) {
3316       return info->func (dest, minuend, subtrahend);
3317     }
3318   }
3319
3320   if (gst_value_compare (minuend, subtrahend) != GST_VALUE_EQUAL) {
3321     gst_value_init_and_copy (dest, minuend);
3322     return TRUE;
3323   }
3324
3325   return FALSE;
3326 }
3327
3328 #if 0
3329 gboolean
3330 gst_value_subtract (GValue * dest, const GValue * minuend,
3331     const GValue * subtrahend)
3332 {
3333   gboolean ret = gst_value_subtract2 (dest, minuend, subtrahend);
3334
3335   g_printerr ("\"%s\"  -  \"%s\"  =  \"%s\"\n", gst_value_serialize (minuend),
3336       gst_value_serialize (subtrahend),
3337       ret ? gst_value_serialize (dest) : "---");
3338   return ret;
3339 }
3340 #endif
3341
3342 /**
3343  * gst_value_can_subtract:
3344  * @minuend: the value to subtract from
3345  * @subtrahend: the value to subtract
3346  *
3347  * Checks if it's possible to subtract @subtrahend from @minuend.
3348  *
3349  * Returns: TRUE if a subtraction is possible
3350  */
3351 gboolean
3352 gst_value_can_subtract (const GValue * minuend, const GValue * subtrahend)
3353 {
3354   GstValueSubtractInfo *info;
3355   guint i, len;
3356   GType ltype, mtype, stype;
3357
3358   g_return_val_if_fail (G_IS_VALUE (minuend), FALSE);
3359   g_return_val_if_fail (G_IS_VALUE (subtrahend), FALSE);
3360
3361   ltype = gst_value_list_get_type ();
3362
3363   /* special cases */
3364   if (G_VALUE_HOLDS (minuend, ltype) || G_VALUE_HOLDS (subtrahend, ltype))
3365     return TRUE;
3366
3367   mtype = G_VALUE_TYPE (minuend);
3368   stype = G_VALUE_TYPE (subtrahend);
3369
3370   len = gst_value_subtract_funcs->len;
3371   for (i = 0; i < len; i++) {
3372     info = &g_array_index (gst_value_subtract_funcs, GstValueSubtractInfo, i);
3373     if (info->minuend == mtype && info->subtrahend == stype)
3374       return TRUE;
3375   }
3376
3377   return gst_value_can_compare (minuend, subtrahend);
3378 }
3379
3380 /**
3381  * gst_value_register_subtract_func:
3382  * @minuend_type: type of the minuend
3383  * @subtrahend_type: type of the subtrahend
3384  * @func: function to use
3385  *
3386  * Registers @func as a function capable of subtracting the values of
3387  * @subtrahend_type from values of @minuend_type.
3388  *
3389  * Subtract functions should be registered at startup before any pipelines are
3390  * started, as gst_value_register_subtract_func() is not thread-safe and
3391  * cannot be used at the same time as gst_value_subtract().
3392  */
3393 void
3394 gst_value_register_subtract_func (GType minuend_type, GType subtrahend_type,
3395     GstValueSubtractFunc func)
3396 {
3397   GstValueSubtractInfo info;
3398
3399   /* one type must be unfixed, other subtractions can be done as comparisons */
3400   g_return_if_fail (!gst_type_is_fixed (minuend_type)
3401       || !gst_type_is_fixed (subtrahend_type));
3402
3403   info.minuend = minuend_type;
3404   info.subtrahend = subtrahend_type;
3405   info.func = func;
3406
3407   g_array_append_val (gst_value_subtract_funcs, info);
3408 }
3409
3410 /**
3411  * gst_value_register:
3412  * @table: structure containing functions to register
3413  *
3414  * Registers functions to perform calculations on #GValue items of a given
3415  * type. Each type can only be added once.
3416  */
3417 void
3418 gst_value_register (const GstValueTable * table)
3419 {
3420   GstValueTable *found;
3421
3422   g_return_if_fail (table != NULL);
3423
3424   g_array_append_val (gst_value_table, *table);
3425
3426   found = gst_value_hash_lookup_type (table->type);
3427   if (found)
3428     g_warning ("adding type %s multiple times", g_type_name (table->type));
3429
3430   /* FIXME: we're not really doing the const justice, we assume the table is
3431    * static */
3432   gst_value_hash_add_type (table->type, table);
3433 }
3434
3435 /**
3436  * gst_value_init_and_copy:
3437  * @dest: the target value
3438  * @src: the source value
3439  *
3440  * Initialises the target value to be of the same type as source and then copies
3441  * the contents from source to target.
3442  */
3443 void
3444 gst_value_init_and_copy (GValue * dest, const GValue * src)
3445 {
3446   g_return_if_fail (G_IS_VALUE (src));
3447   g_return_if_fail (dest != NULL);
3448
3449   g_value_init (dest, G_VALUE_TYPE (src));
3450   g_value_copy (src, dest);
3451 }
3452
3453 /**
3454  * gst_value_serialize:
3455  * @value: a #GValue to serialize
3456  *
3457  * tries to transform the given @value into a string representation that allows
3458  * getting back this string later on using gst_value_deserialize().
3459  *
3460  * Returns: the serialization for @value or NULL if none exists
3461  */
3462 gchar *
3463 gst_value_serialize (const GValue * value)
3464 {
3465   guint i, len;
3466   GValue s_val = { 0 };
3467   GstValueTable *table, *best;
3468   gchar *s;
3469   GType type;
3470
3471   g_return_val_if_fail (G_IS_VALUE (value), NULL);
3472
3473   type = G_VALUE_TYPE (value);
3474
3475   best = gst_value_hash_lookup_type (type);
3476
3477   if (G_UNLIKELY (!best || !best->serialize)) {
3478     len = gst_value_table->len;
3479     best = NULL;
3480     for (i = 0; i < len; i++) {
3481       table = &g_array_index (gst_value_table, GstValueTable, i);
3482       if (table->serialize && g_type_is_a (type, table->type)) {
3483         if (!best || g_type_is_a (table->type, best->type))
3484           best = table;
3485       }
3486     }
3487   }
3488   if (G_LIKELY (best))
3489     return best->serialize (value);
3490
3491   g_value_init (&s_val, G_TYPE_STRING);
3492   if (g_value_transform (value, &s_val)) {
3493     s = gst_string_wrap (g_value_get_string (&s_val));
3494   } else {
3495     s = NULL;
3496   }
3497   g_value_unset (&s_val);
3498
3499   return s;
3500 }
3501
3502 /**
3503  * gst_value_deserialize:
3504  * @dest: #GValue to fill with contents of deserialization
3505  * @src: string to deserialize
3506  *
3507  * Tries to deserialize a string into the type specified by the given GValue.
3508  * If the operation succeeds, TRUE is returned, FALSE otherwise.
3509  *
3510  * Returns: TRUE on success
3511  */
3512 gboolean
3513 gst_value_deserialize (GValue * dest, const gchar * src)
3514 {
3515   GstValueTable *table, *best;
3516   guint i, len;
3517   GType type;
3518
3519   g_return_val_if_fail (src != NULL, FALSE);
3520   g_return_val_if_fail (G_IS_VALUE (dest), FALSE);
3521
3522   type = G_VALUE_TYPE (dest);
3523
3524   best = gst_value_hash_lookup_type (type);
3525   if (G_UNLIKELY (!best || !best->deserialize)) {
3526     len = gst_value_table->len;
3527     best = NULL;
3528     for (i = 0; i < len; i++) {
3529       table = &g_array_index (gst_value_table, GstValueTable, i);
3530       if (table->deserialize && g_type_is_a (type, table->type)) {
3531         if (!best || g_type_is_a (table->type, best->type))
3532           best = table;
3533       }
3534     }
3535   }
3536   if (G_LIKELY (best))
3537     return best->deserialize (dest, src);
3538
3539   return FALSE;
3540 }
3541
3542 /**
3543  * gst_value_is_fixed:
3544  * @value: the #GValue to check
3545  *
3546  * Tests if the given GValue, if available in a GstStructure (or any other
3547  * container) contains a "fixed" (which means: one value) or an "unfixed"
3548  * (which means: multiple possible values, such as data lists or data
3549  * ranges) value.
3550  *
3551  * Returns: true if the value is "fixed".
3552  */
3553
3554 gboolean
3555 gst_value_is_fixed (const GValue * value)
3556 {
3557   GType type;
3558
3559   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
3560
3561   type = G_VALUE_TYPE (value);
3562
3563   /* the most common types are just basic plain glib types */
3564   if (type <= G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) {
3565     return TRUE;
3566   }
3567
3568   if (type == GST_TYPE_ARRAY) {
3569     gint size, n;
3570     const GValue *kid;
3571
3572     /* check recursively */
3573     size = gst_value_array_get_size (value);
3574     for (n = 0; n < size; n++) {
3575       kid = gst_value_array_get_value (value, n);
3576       if (!gst_value_is_fixed (kid))
3577         return FALSE;
3578     }
3579     return TRUE;
3580   }
3581   return gst_type_is_fixed (type);
3582 }
3583
3584 /************
3585  * fraction *
3586  ************/
3587
3588 /* helper functions */
3589 static void
3590 gst_value_init_fraction (GValue * value)
3591 {
3592   value->data[0].v_int = 0;
3593   value->data[1].v_int = 1;
3594 }
3595
3596 static void
3597 gst_value_copy_fraction (const GValue * src_value, GValue * dest_value)
3598 {
3599   dest_value->data[0].v_int = src_value->data[0].v_int;
3600   dest_value->data[1].v_int = src_value->data[1].v_int;
3601 }
3602
3603 static gchar *
3604 gst_value_collect_fraction (GValue * value, guint n_collect_values,
3605     GTypeCValue * collect_values, guint collect_flags)
3606 {
3607   if (n_collect_values != 2)
3608     return g_strdup_printf ("not enough value locations for `%s' passed",
3609         G_VALUE_TYPE_NAME (value));
3610   if (collect_values[1].v_int == 0)
3611     return g_strdup_printf ("passed '0' as denominator for `%s'",
3612         G_VALUE_TYPE_NAME (value));
3613   if (collect_values[0].v_int < -G_MAXINT)
3614     return
3615         g_strdup_printf
3616         ("passed value smaller than -G_MAXINT as numerator for `%s'",
3617         G_VALUE_TYPE_NAME (value));
3618   if (collect_values[1].v_int < -G_MAXINT)
3619     return
3620         g_strdup_printf
3621         ("passed value smaller than -G_MAXINT as denominator for `%s'",
3622         G_VALUE_TYPE_NAME (value));
3623
3624   gst_value_set_fraction (value,
3625       collect_values[0].v_int, collect_values[1].v_int);
3626
3627   return NULL;
3628 }
3629
3630 static gchar *
3631 gst_value_lcopy_fraction (const GValue * value, guint n_collect_values,
3632     GTypeCValue * collect_values, guint collect_flags)
3633 {
3634   gint *numerator = collect_values[0].v_pointer;
3635   gint *denominator = collect_values[1].v_pointer;
3636
3637   if (!numerator)
3638     return g_strdup_printf ("numerator for `%s' passed as NULL",
3639         G_VALUE_TYPE_NAME (value));
3640   if (!denominator)
3641     return g_strdup_printf ("denominator for `%s' passed as NULL",
3642         G_VALUE_TYPE_NAME (value));
3643
3644   *numerator = value->data[0].v_int;
3645   *denominator = value->data[1].v_int;
3646
3647   return NULL;
3648 }
3649
3650 /**
3651  * gst_value_set_fraction:
3652  * @value: a GValue initialized to #GST_TYPE_FRACTION
3653  * @numerator: the numerator of the fraction
3654  * @denominator: the denominator of the fraction
3655  *
3656  * Sets @value to the fraction specified by @numerator over @denominator.
3657  * The fraction gets reduced to the smallest numerator and denominator,
3658  * and if necessary the sign is moved to the numerator.
3659  */
3660 void
3661 gst_value_set_fraction (GValue * value, gint numerator, gint denominator)
3662 {
3663   gint gcd = 0;
3664
3665   g_return_if_fail (GST_VALUE_HOLDS_FRACTION (value));
3666   g_return_if_fail (denominator != 0);
3667   g_return_if_fail (denominator >= -G_MAXINT);
3668   g_return_if_fail (numerator >= -G_MAXINT);
3669
3670   /* normalize sign */
3671   if (denominator < 0) {
3672     numerator = -numerator;
3673     denominator = -denominator;
3674   }
3675
3676   /* check for reduction */
3677   gcd = gst_util_greatest_common_divisor (numerator, denominator);
3678   if (gcd) {
3679     numerator /= gcd;
3680     denominator /= gcd;
3681   }
3682
3683   g_assert (denominator > 0);
3684
3685   value->data[0].v_int = numerator;
3686   value->data[1].v_int = denominator;
3687 }
3688
3689 /**
3690  * gst_value_get_fraction_numerator:
3691  * @value: a GValue initialized to #GST_TYPE_FRACTION
3692  *
3693  * Gets the numerator of the fraction specified by @value.
3694  *
3695  * Returns: the numerator of the fraction.
3696  */
3697 gint
3698 gst_value_get_fraction_numerator (const GValue * value)
3699 {
3700   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (value), 0);
3701
3702   return value->data[0].v_int;
3703 }
3704
3705 /**
3706  * gst_value_get_fraction_denominator:
3707  * @value: a GValue initialized to #GST_TYPE_FRACTION
3708  *
3709  * Gets the denominator of the fraction specified by @value.
3710  *
3711  * Returns: the denominator of the fraction.
3712  */
3713 gint
3714 gst_value_get_fraction_denominator (const GValue * value)
3715 {
3716   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (value), 1);
3717
3718   return value->data[1].v_int;
3719 }
3720
3721 /**
3722  * gst_value_fraction_multiply:
3723  * @product: a GValue initialized to #GST_TYPE_FRACTION
3724  * @factor1: a GValue initialized to #GST_TYPE_FRACTION
3725  * @factor2: a GValue initialized to #GST_TYPE_FRACTION
3726  *
3727  * Multiplies the two #GValue items containing a #GST_TYPE_FRACTION and sets
3728  * @product to the product of the two fractions.
3729  *
3730  * Returns: FALSE in case of an error (like integer overflow), TRUE otherwise.
3731  */
3732 gboolean
3733 gst_value_fraction_multiply (GValue * product, const GValue * factor1,
3734     const GValue * factor2)
3735 {
3736   gint n1, n2, d1, d2;
3737   gint res_n, res_d;
3738
3739   g_return_val_if_fail (product != NULL, FALSE);
3740   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (factor1), FALSE);
3741   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (factor2), FALSE);
3742
3743   n1 = factor1->data[0].v_int;
3744   n2 = factor2->data[0].v_int;
3745   d1 = factor1->data[1].v_int;
3746   d2 = factor2->data[1].v_int;
3747
3748   if (!gst_util_fraction_multiply (n1, d1, n2, d2, &res_n, &res_d))
3749     return FALSE;
3750
3751   gst_value_set_fraction (product, res_n, res_d);
3752
3753   return TRUE;
3754 }
3755
3756 /**
3757  * gst_value_fraction_subtract:
3758  * @dest: a GValue initialized to #GST_TYPE_FRACTION
3759  * @minuend: a GValue initialized to #GST_TYPE_FRACTION
3760  * @subtrahend: a GValue initialized to #GST_TYPE_FRACTION
3761  *
3762  * Subtracts the @subtrahend from the @minuend and sets @dest to the result.
3763  *
3764  * Returns: FALSE in case of an error (like integer overflow), TRUE otherwise.
3765  */
3766 gboolean
3767 gst_value_fraction_subtract (GValue * dest,
3768     const GValue * minuend, const GValue * subtrahend)
3769 {
3770   gint n1, n2, d1, d2;
3771   gint res_n, res_d;
3772
3773   g_return_val_if_fail (dest != NULL, FALSE);
3774   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (minuend), FALSE);
3775   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (subtrahend), FALSE);
3776
3777   n1 = minuend->data[0].v_int;
3778   n2 = subtrahend->data[0].v_int;
3779   d1 = minuend->data[1].v_int;
3780   d2 = subtrahend->data[1].v_int;
3781
3782   if (!gst_util_fraction_add (n1, d1, -n2, d2, &res_n, &res_d))
3783     return FALSE;
3784   gst_value_set_fraction (dest, res_n, res_d);
3785
3786   return TRUE;
3787 }
3788
3789 static gchar *
3790 gst_value_serialize_fraction (const GValue * value)
3791 {
3792   gint32 numerator = value->data[0].v_int;
3793   gint32 denominator = value->data[1].v_int;
3794   gboolean positive = TRUE;
3795
3796   /* get the sign and make components absolute */
3797   if (numerator < 0) {
3798     numerator = -numerator;
3799     positive = !positive;
3800   }
3801   if (denominator < 0) {
3802     denominator = -denominator;
3803     positive = !positive;
3804   }
3805
3806   return g_strdup_printf ("%s%d/%d",
3807       positive ? "" : "-", numerator, denominator);
3808 }
3809
3810 static gboolean
3811 gst_value_deserialize_fraction (GValue * dest, const gchar * s)
3812 {
3813   gint num, den;
3814   gint num_chars;
3815
3816   if (G_UNLIKELY (s == NULL))
3817     return FALSE;
3818
3819   if (G_UNLIKELY (dest == NULL || !GST_VALUE_HOLDS_FRACTION (dest)))
3820     return FALSE;
3821
3822   if (sscanf (s, "%d/%d%n", &num, &den, &num_chars) >= 2) {
3823     if (s[num_chars] != 0)
3824       return FALSE;
3825     if (den == 0)
3826       return FALSE;
3827
3828     gst_value_set_fraction (dest, num, den);
3829     return TRUE;
3830   } else if (g_ascii_strcasecmp (s, "1/max") == 0) {
3831     gst_value_set_fraction (dest, 1, G_MAXINT);
3832     return TRUE;
3833   } else if (sscanf (s, "%d%n", &num, &num_chars) >= 1) {
3834     if (s[num_chars] != 0)
3835       return FALSE;
3836     gst_value_set_fraction (dest, num, 1);
3837     return TRUE;
3838   } else if (g_ascii_strcasecmp (s, "min") == 0) {
3839     gst_value_set_fraction (dest, -G_MAXINT, 1);
3840     return TRUE;
3841   } else if (g_ascii_strcasecmp (s, "max") == 0) {
3842     gst_value_set_fraction (dest, G_MAXINT, 1);
3843     return TRUE;
3844   }
3845
3846   return FALSE;
3847 }
3848
3849 static void
3850 gst_value_transform_fraction_string (const GValue * src_value,
3851     GValue * dest_value)
3852 {
3853   dest_value->data[0].v_pointer = gst_value_serialize_fraction (src_value);
3854 }
3855
3856 static void
3857 gst_value_transform_string_fraction (const GValue * src_value,
3858     GValue * dest_value)
3859 {
3860   if (!gst_value_deserialize_fraction (dest_value,
3861           src_value->data[0].v_pointer))
3862     /* If the deserialize fails, ensure we leave the fraction in a
3863      * valid, if incorrect, state */
3864     gst_value_set_fraction (dest_value, 0, 1);
3865 }
3866
3867 static void
3868 gst_value_transform_double_fraction (const GValue * src_value,
3869     GValue * dest_value)
3870 {
3871   gdouble src = g_value_get_double (src_value);
3872   gint n, d;
3873
3874   gst_util_double_to_fraction (src, &n, &d);
3875   gst_value_set_fraction (dest_value, n, d);
3876 }
3877
3878 static void
3879 gst_value_transform_float_fraction (const GValue * src_value,
3880     GValue * dest_value)
3881 {
3882   gfloat src = g_value_get_float (src_value);
3883   gint n, d;
3884
3885   gst_util_double_to_fraction (src, &n, &d);
3886   gst_value_set_fraction (dest_value, n, d);
3887 }
3888
3889 static void
3890 gst_value_transform_fraction_double (const GValue * src_value,
3891     GValue * dest_value)
3892 {
3893   dest_value->data[0].v_double = ((double) src_value->data[0].v_int) /
3894       ((double) src_value->data[1].v_int);
3895 }
3896
3897 static void
3898 gst_value_transform_fraction_float (const GValue * src_value,
3899     GValue * dest_value)
3900 {
3901   dest_value->data[0].v_float = ((float) src_value->data[0].v_int) /
3902       ((float) src_value->data[1].v_int);
3903 }
3904
3905 static gint
3906 gst_value_compare_fraction (const GValue * value1, const GValue * value2)
3907 {
3908   gint n1, n2;
3909   gint d1, d2;
3910   gint ret;
3911
3912   n1 = value1->data[0].v_int;
3913   n2 = value2->data[0].v_int;
3914   d1 = value1->data[1].v_int;
3915   d2 = value2->data[1].v_int;
3916
3917   /* fractions are reduced when set, so we can quickly see if they're equal */
3918   if (n1 == n2 && d1 == d2)
3919     return GST_VALUE_EQUAL;
3920
3921   if (d1 == 0 && d2 == 0)
3922     return GST_VALUE_UNORDERED;
3923   else if (d1 == 0)
3924     return GST_VALUE_GREATER_THAN;
3925   else if (d2 == 0)
3926     return GST_VALUE_LESS_THAN;
3927
3928   ret = gst_util_fraction_compare (n1, d1, n2, d2);
3929   if (ret == -1)
3930     return GST_VALUE_LESS_THAN;
3931   else if (ret == 1)
3932     return GST_VALUE_GREATER_THAN;
3933
3934   /* Equality can't happen here because we check for that
3935    * first already */
3936   g_return_val_if_reached (GST_VALUE_UNORDERED);
3937 }
3938
3939 /*********
3940  * GDate *
3941  *********/
3942
3943 /**
3944  * gst_value_set_date:
3945  * @value: a GValue initialized to GST_TYPE_DATE
3946  * @date: the date to set the value to
3947  *
3948  * Sets the contents of @value to coorespond to @date.  The actual
3949  * #GDate structure is copied before it is used.
3950  */
3951 void
3952 gst_value_set_date (GValue * value, const GDate * date)
3953 {
3954   g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_DATE);
3955   g_return_if_fail (g_date_valid (date));
3956
3957   g_value_set_boxed (value, date);
3958 }
3959
3960 /**
3961  * gst_value_get_date:
3962  * @value: a GValue initialized to GST_TYPE_DATE
3963  *
3964  * Gets the contents of @value.
3965  *
3966  * Returns: the contents of @value
3967  */
3968 const GDate *
3969 gst_value_get_date (const GValue * value)
3970 {
3971   g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_DATE, NULL);
3972
3973   return (const GDate *) g_value_get_boxed (value);
3974 }
3975
3976 static gpointer
3977 gst_date_copy (gpointer boxed)
3978 {
3979   const GDate *date = (const GDate *) boxed;
3980
3981   if (!g_date_valid (date)) {
3982     GST_WARNING ("invalid GDate");
3983     return NULL;
3984   }
3985
3986   return g_date_new_julian (g_date_get_julian (date));
3987 }
3988
3989 static gint
3990 gst_value_compare_date (const GValue * value1, const GValue * value2)
3991 {
3992   const GDate *date1 = (const GDate *) g_value_get_boxed (value1);
3993   const GDate *date2 = (const GDate *) g_value_get_boxed (value2);
3994   guint32 j1, j2;
3995
3996   if (date1 == date2)
3997     return GST_VALUE_EQUAL;
3998
3999   if ((date1 == NULL || !g_date_valid (date1))
4000       && (date2 != NULL && g_date_valid (date2))) {
4001     return GST_VALUE_LESS_THAN;
4002   }
4003
4004   if ((date2 == NULL || !g_date_valid (date2))
4005       && (date1 != NULL && g_date_valid (date1))) {
4006     return GST_VALUE_GREATER_THAN;
4007   }
4008
4009   if (date1 == NULL || date2 == NULL || !g_date_valid (date1)
4010       || !g_date_valid (date2)) {
4011     return GST_VALUE_UNORDERED;
4012   }
4013
4014   j1 = g_date_get_julian (date1);
4015   j2 = g_date_get_julian (date2);
4016
4017   if (j1 == j2)
4018     return GST_VALUE_EQUAL;
4019   else if (j1 < j2)
4020     return GST_VALUE_LESS_THAN;
4021   else
4022     return GST_VALUE_GREATER_THAN;
4023 }
4024
4025 static gchar *
4026 gst_value_serialize_date (const GValue * val)
4027 {
4028   const GDate *date = (const GDate *) g_value_get_boxed (val);
4029
4030   if (date == NULL || !g_date_valid (date))
4031     return g_strdup ("9999-99-99");
4032
4033   return g_strdup_printf ("%04u-%02u-%02u", g_date_get_year (date),
4034       g_date_get_month (date), g_date_get_day (date));
4035 }
4036
4037 static gboolean
4038 gst_value_deserialize_date (GValue * dest, const gchar * s)
4039 {
4040   guint year, month, day;
4041
4042   if (!s || sscanf (s, "%04u-%02u-%02u", &year, &month, &day) != 3)
4043     return FALSE;
4044
4045   if (!g_date_valid_dmy (day, month, year))
4046     return FALSE;
4047
4048   g_value_take_boxed (dest, g_date_new_dmy (day, month, year));
4049   return TRUE;
4050 }
4051
4052 /*************
4053  * GstDateTime *
4054  *************/
4055
4056 static gint
4057 gst_value_compare_date_time (const GValue * value1, const GValue * value2)
4058 {
4059   const GstDateTime *date1 = (const GstDateTime *) g_value_get_boxed (value1);
4060   const GstDateTime *date2 = (const GstDateTime *) g_value_get_boxed (value2);
4061   gint ret;
4062
4063   if (date1 == date2)
4064     return GST_VALUE_EQUAL;
4065
4066   if ((date1 == NULL) && (date2 != NULL)) {
4067     return GST_VALUE_LESS_THAN;
4068   }
4069   if ((date2 == NULL) && (date1 != NULL)) {
4070     return GST_VALUE_LESS_THAN;
4071   }
4072
4073   ret = priv_gst_date_time_compare (date1, date2);
4074
4075   if (ret == 0)
4076     return GST_VALUE_EQUAL;
4077   else if (ret < 0)
4078     return GST_VALUE_LESS_THAN;
4079   else
4080     return GST_VALUE_GREATER_THAN;
4081 }
4082
4083 static gchar *
4084 gst_value_serialize_date_time (const GValue * val)
4085 {
4086   const GstDateTime *date = (const GstDateTime *) g_value_get_boxed (val);
4087   gint offset;
4088
4089   if (date == NULL)
4090     return g_strdup ("null");
4091
4092   offset = gst_date_time_get_time_zone_offset (date);
4093
4094   return g_strdup_printf ("\"%04d-%02d-%02dT%02d:%02d:%02d.%06d"
4095       "%c%02d%02d\"", gst_date_time_get_year (date),
4096       gst_date_time_get_month (date), gst_date_time_get_day (date),
4097       gst_date_time_get_hour (date), gst_date_time_get_minute (date),
4098       gst_date_time_get_second (date), gst_date_time_get_microsecond (date),
4099       offset >= 0 ? '+' : '-', (gint) ABS (offset) / 60,
4100       (gint) ABS (offset) % 60);
4101 }
4102
4103 static gboolean
4104 gst_value_deserialize_date_time (GValue * dest, const gchar * s)
4105 {
4106   gint year, month, day, hour, minute, second, usecond;
4107   gchar signal;
4108   gint offset = 0;
4109   gint ret;
4110
4111   if (!s || strcmp (s, "null") == 0) {
4112     return FALSE;
4113   }
4114
4115   ret = sscanf (s, "%04d-%02d-%02dT%02d:%02d:%02d.%06d%c%04d",
4116       &year, &month, &day, &hour, &minute, &second, &usecond, &signal, &offset);
4117   if (ret >= 9) {
4118     offset = (offset / 100) * 60 + (offset % 100);
4119     if (signal == '-')
4120       offset = -offset;
4121   } else
4122     return FALSE;
4123
4124   g_value_take_boxed (dest, gst_date_time_new (year, month, day, hour,
4125           minute, second, usecond, offset));
4126   return TRUE;
4127 }
4128
4129 static void
4130 gst_value_transform_date_string (const GValue * src_value, GValue * dest_value)
4131 {
4132   dest_value->data[0].v_pointer = gst_value_serialize_date (src_value);
4133 }
4134
4135 static void
4136 gst_value_transform_string_date (const GValue * src_value, GValue * dest_value)
4137 {
4138   gst_value_deserialize_date (dest_value, src_value->data[0].v_pointer);
4139 }
4140
4141 static void
4142 gst_value_transform_object_string (const GValue * src_value,
4143     GValue * dest_value)
4144 {
4145   GstObject *obj;
4146   gchar *str;
4147
4148   obj = g_value_get_object (src_value);
4149   if (obj) {
4150     str =
4151         g_strdup_printf ("(%s) %s", G_OBJECT_TYPE_NAME (obj),
4152         GST_OBJECT_NAME (obj));
4153   } else {
4154     str = g_strdup ("NULL");
4155   }
4156
4157   dest_value->data[0].v_pointer = str;
4158 }
4159
4160 static GTypeInfo _info = {
4161   0,
4162   NULL,
4163   NULL,
4164   NULL,
4165   NULL,
4166   NULL,
4167   0,
4168   0,
4169   NULL,
4170   NULL,
4171 };
4172
4173 static GTypeFundamentalInfo _finfo = {
4174   0
4175 };
4176
4177 #define FUNC_VALUE_GET_TYPE(type, name)                         \
4178 GType gst_ ## type ## _get_type (void)                          \
4179 {                                                               \
4180   static volatile GType gst_ ## type ## _type = 0;                       \
4181                                                                 \
4182   if (g_once_init_enter (&gst_ ## type ## _type)) {             \
4183     GType _type;                                        \
4184     _info.value_table = & _gst_ ## type ## _value_table;        \
4185     _type = g_type_register_fundamental (       \
4186         g_type_fundamental_next (),                             \
4187         name, &_info, &_finfo, 0);                              \
4188     g_once_init_leave(&gst_ ## type ## _type, _type);   \
4189   }                                                             \
4190                                                                 \
4191   return gst_ ## type ## _type;                                 \
4192 }
4193
4194 static const GTypeValueTable _gst_fourcc_value_table = {
4195   gst_value_init_fourcc,
4196   NULL,
4197   gst_value_copy_fourcc,
4198   NULL,
4199   (char *) "i",
4200   gst_value_collect_fourcc,
4201   (char *) "p",
4202   gst_value_lcopy_fourcc
4203 };
4204
4205 FUNC_VALUE_GET_TYPE (fourcc, "GstFourcc");
4206
4207 static const GTypeValueTable _gst_int_range_value_table = {
4208   gst_value_init_int_range,
4209   NULL,
4210   gst_value_copy_int_range,
4211   NULL,
4212   (char *) "ii",
4213   gst_value_collect_int_range,
4214   (char *) "pp",
4215   gst_value_lcopy_int_range
4216 };
4217
4218 FUNC_VALUE_GET_TYPE (int_range, "GstIntRange");
4219
4220 static const GTypeValueTable _gst_double_range_value_table = {
4221   gst_value_init_double_range,
4222   NULL,
4223   gst_value_copy_double_range,
4224   NULL,
4225   (char *) "dd",
4226   gst_value_collect_double_range,
4227   (char *) "pp",
4228   gst_value_lcopy_double_range
4229 };
4230
4231 FUNC_VALUE_GET_TYPE (double_range, "GstDoubleRange");
4232
4233 static const GTypeValueTable _gst_fraction_range_value_table = {
4234   gst_value_init_fraction_range,
4235   gst_value_free_fraction_range,
4236   gst_value_copy_fraction_range,
4237   NULL,
4238   (char *) "iiii",
4239   gst_value_collect_fraction_range,
4240   (char *) "pppp",
4241   gst_value_lcopy_fraction_range
4242 };
4243
4244 FUNC_VALUE_GET_TYPE (fraction_range, "GstFractionRange");
4245
4246 static const GTypeValueTable _gst_value_list_value_table = {
4247   gst_value_init_list_or_array,
4248   gst_value_free_list_or_array,
4249   gst_value_copy_list_or_array,
4250   gst_value_list_or_array_peek_pointer,
4251   (char *) "p",
4252   gst_value_collect_list_or_array,
4253   (char *) "p",
4254   gst_value_lcopy_list_or_array
4255 };
4256
4257 FUNC_VALUE_GET_TYPE (value_list, "GstValueList");
4258
4259 static const GTypeValueTable _gst_value_array_value_table = {
4260   gst_value_init_list_or_array,
4261   gst_value_free_list_or_array,
4262   gst_value_copy_list_or_array,
4263   gst_value_list_or_array_peek_pointer,
4264   (char *) "p",
4265   gst_value_collect_list_or_array,
4266   (char *) "p",
4267   gst_value_lcopy_list_or_array
4268 };
4269
4270 FUNC_VALUE_GET_TYPE (value_array, "GstValueArray");
4271
4272 static const GTypeValueTable _gst_fraction_value_table = {
4273   gst_value_init_fraction,
4274   NULL,
4275   gst_value_copy_fraction,
4276   NULL,
4277   (char *) "ii",
4278   gst_value_collect_fraction,
4279   (char *) "pp",
4280   gst_value_lcopy_fraction
4281 };
4282
4283 FUNC_VALUE_GET_TYPE (fraction, "GstFraction");
4284
4285
4286 GType
4287 gst_date_get_type (void)
4288 {
4289   static GType gst_date_type = 0;
4290
4291   if (G_UNLIKELY (gst_date_type == 0)) {
4292     /* FIXME 0.11: we require GLib 2.8 already
4293      * Not using G_TYPE_DATE here on purpose, even if we could
4294      * if GLIB_CHECK_VERSION(2,8,0) was true: we don't want the
4295      * serialised strings to have different type strings depending
4296      * on what version is used, so FIXME when we require GLib-2.8 */
4297     gst_date_type = g_boxed_type_register_static ("GstDate",
4298         (GBoxedCopyFunc) gst_date_copy, (GBoxedFreeFunc) g_date_free);
4299   }
4300
4301   return gst_date_type;
4302 }
4303
4304 GType
4305 gst_date_time_get_type (void)
4306 {
4307   static GType gst_date_time_type = 0;
4308
4309   if (G_UNLIKELY (gst_date_time_type == 0)) {
4310     gst_date_time_type = g_boxed_type_register_static ("GstDateTime",
4311         (GBoxedCopyFunc) gst_date_time_ref,
4312         (GBoxedFreeFunc) gst_date_time_unref);
4313   }
4314
4315   return gst_date_time_type;
4316 }
4317
4318
4319 void
4320 _gst_value_initialize (void)
4321 {
4322   gst_value_table = g_array_new (FALSE, FALSE, sizeof (GstValueTable));
4323   gst_value_hash = g_hash_table_new (NULL, NULL);
4324   gst_value_union_funcs = g_array_new (FALSE, FALSE,
4325       sizeof (GstValueUnionInfo));
4326   gst_value_intersect_funcs = g_array_new (FALSE, FALSE,
4327       sizeof (GstValueIntersectInfo));
4328   gst_value_subtract_funcs = g_array_new (FALSE, FALSE,
4329       sizeof (GstValueSubtractInfo));
4330
4331   {
4332     static GstValueTable gst_value = {
4333       0,
4334       gst_value_compare_fourcc,
4335       gst_value_serialize_fourcc,
4336       gst_value_deserialize_fourcc,
4337     };
4338
4339     gst_value.type = gst_fourcc_get_type ();
4340     gst_value_register (&gst_value);
4341   }
4342
4343   {
4344     static GstValueTable gst_value = {
4345       0,
4346       gst_value_compare_int_range,
4347       gst_value_serialize_int_range,
4348       gst_value_deserialize_int_range,
4349     };
4350
4351     gst_value.type = gst_int_range_get_type ();
4352     gst_value_register (&gst_value);
4353   }
4354
4355   {
4356     static GstValueTable gst_value = {
4357       0,
4358       gst_value_compare_double_range,
4359       gst_value_serialize_double_range,
4360       gst_value_deserialize_double_range,
4361     };
4362
4363     gst_value.type = gst_double_range_get_type ();
4364     gst_value_register (&gst_value);
4365   }
4366
4367   {
4368     static GstValueTable gst_value = {
4369       0,
4370       gst_value_compare_fraction_range,
4371       gst_value_serialize_fraction_range,
4372       gst_value_deserialize_fraction_range,
4373     };
4374
4375     gst_value.type = gst_fraction_range_get_type ();
4376     gst_value_register (&gst_value);
4377   }
4378
4379   {
4380     static GstValueTable gst_value = {
4381       0,
4382       gst_value_compare_list,
4383       gst_value_serialize_list,
4384       gst_value_deserialize_list,
4385     };
4386
4387     gst_value.type = gst_value_list_get_type ();
4388     gst_value_register (&gst_value);
4389   }
4390
4391   {
4392     static GstValueTable gst_value = {
4393       0,
4394       gst_value_compare_array,
4395       gst_value_serialize_array,
4396       gst_value_deserialize_array,
4397     };
4398
4399     gst_value.type = gst_value_array_get_type ();
4400     gst_value_register (&gst_value);
4401   }
4402
4403   {
4404 #if 0
4405     static const GTypeValueTable value_table = {
4406       gst_value_init_buffer,
4407       NULL,
4408       gst_value_copy_buffer,
4409       NULL,
4410       "i",
4411       NULL,                     /*gst_value_collect_buffer, */
4412       "p",
4413       NULL                      /*gst_value_lcopy_buffer */
4414     };
4415 #endif
4416     static GstValueTable gst_value = {
4417       0,
4418       gst_value_compare_buffer,
4419       gst_value_serialize_buffer,
4420       gst_value_deserialize_buffer,
4421     };
4422
4423     gst_value.type = GST_TYPE_BUFFER;
4424     gst_value_register (&gst_value);
4425   }
4426   {
4427     static GstValueTable gst_value = {
4428       0,
4429       gst_value_compare_fraction,
4430       gst_value_serialize_fraction,
4431       gst_value_deserialize_fraction,
4432     };
4433
4434     gst_value.type = gst_fraction_get_type ();
4435     gst_value_register (&gst_value);
4436   }
4437   {
4438     static GstValueTable gst_value = {
4439       0,
4440       NULL,
4441       gst_value_serialize_caps,
4442       gst_value_deserialize_caps,
4443     };
4444
4445     gst_value.type = GST_TYPE_CAPS;
4446     gst_value_register (&gst_value);
4447   }
4448   {
4449     static GstValueTable gst_value = {
4450       0,
4451       NULL,
4452       gst_value_serialize_structure,
4453       gst_value_deserialize_structure,
4454     };
4455
4456     gst_value.type = GST_TYPE_STRUCTURE;
4457     gst_value_register (&gst_value);
4458   }
4459   {
4460     static GstValueTable gst_value = {
4461       0,
4462       gst_value_compare_date,
4463       gst_value_serialize_date,
4464       gst_value_deserialize_date,
4465     };
4466
4467     gst_value.type = gst_date_get_type ();
4468     gst_value_register (&gst_value);
4469   }
4470   {
4471     static GstValueTable gst_value = {
4472       0,
4473       gst_value_compare_date_time,
4474       gst_value_serialize_date_time,
4475       gst_value_deserialize_date_time,
4476     };
4477
4478     gst_value.type = gst_date_time_get_type ();
4479     gst_value_register (&gst_value);
4480   }
4481
4482   REGISTER_SERIALIZATION (G_TYPE_DOUBLE, double);
4483   REGISTER_SERIALIZATION (G_TYPE_FLOAT, float);
4484
4485   REGISTER_SERIALIZATION (G_TYPE_STRING, string);
4486   REGISTER_SERIALIZATION (G_TYPE_BOOLEAN, boolean);
4487   REGISTER_SERIALIZATION (G_TYPE_ENUM, enum);
4488
4489   REGISTER_SERIALIZATION (G_TYPE_FLAGS, flags);
4490
4491   REGISTER_SERIALIZATION (G_TYPE_INT, int);
4492
4493   REGISTER_SERIALIZATION (G_TYPE_INT64, int64);
4494   REGISTER_SERIALIZATION (G_TYPE_LONG, long);
4495
4496   REGISTER_SERIALIZATION (G_TYPE_UINT, uint);
4497   REGISTER_SERIALIZATION (G_TYPE_UINT64, uint64);
4498   REGISTER_SERIALIZATION (G_TYPE_ULONG, ulong);
4499
4500   g_value_register_transform_func (GST_TYPE_FOURCC, G_TYPE_STRING,
4501       gst_value_transform_fourcc_string);
4502   g_value_register_transform_func (GST_TYPE_INT_RANGE, G_TYPE_STRING,
4503       gst_value_transform_int_range_string);
4504   g_value_register_transform_func (GST_TYPE_DOUBLE_RANGE, G_TYPE_STRING,
4505       gst_value_transform_double_range_string);
4506   g_value_register_transform_func (GST_TYPE_FRACTION_RANGE, G_TYPE_STRING,
4507       gst_value_transform_fraction_range_string);
4508   g_value_register_transform_func (GST_TYPE_LIST, G_TYPE_STRING,
4509       gst_value_transform_list_string);
4510   g_value_register_transform_func (GST_TYPE_ARRAY, G_TYPE_STRING,
4511       gst_value_transform_array_string);
4512   g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_STRING,
4513       gst_value_transform_fraction_string);
4514   g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_FRACTION,
4515       gst_value_transform_string_fraction);
4516   g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_DOUBLE,
4517       gst_value_transform_fraction_double);
4518   g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_FLOAT,
4519       gst_value_transform_fraction_float);
4520   g_value_register_transform_func (G_TYPE_DOUBLE, GST_TYPE_FRACTION,
4521       gst_value_transform_double_fraction);
4522   g_value_register_transform_func (G_TYPE_FLOAT, GST_TYPE_FRACTION,
4523       gst_value_transform_float_fraction);
4524   g_value_register_transform_func (GST_TYPE_DATE, G_TYPE_STRING,
4525       gst_value_transform_date_string);
4526   g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_DATE,
4527       gst_value_transform_string_date);
4528   g_value_register_transform_func (GST_TYPE_OBJECT, G_TYPE_STRING,
4529       gst_value_transform_object_string);
4530
4531   gst_value_register_intersect_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
4532       gst_value_intersect_int_int_range);
4533   gst_value_register_intersect_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
4534       gst_value_intersect_int_range_int_range);
4535   gst_value_register_intersect_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE,
4536       gst_value_intersect_double_double_range);
4537   gst_value_register_intersect_func (GST_TYPE_DOUBLE_RANGE,
4538       GST_TYPE_DOUBLE_RANGE, gst_value_intersect_double_range_double_range);
4539   gst_value_register_intersect_func (GST_TYPE_ARRAY,
4540       GST_TYPE_ARRAY, gst_value_intersect_array);
4541   gst_value_register_intersect_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
4542       gst_value_intersect_fraction_fraction_range);
4543   gst_value_register_intersect_func (GST_TYPE_FRACTION_RANGE,
4544       GST_TYPE_FRACTION_RANGE,
4545       gst_value_intersect_fraction_range_fraction_range);
4546
4547   gst_value_register_subtract_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
4548       gst_value_subtract_int_int_range);
4549   gst_value_register_subtract_func (GST_TYPE_INT_RANGE, G_TYPE_INT,
4550       gst_value_subtract_int_range_int);
4551   gst_value_register_subtract_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
4552       gst_value_subtract_int_range_int_range);
4553   gst_value_register_subtract_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE,
4554       gst_value_subtract_double_double_range);
4555   gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE, G_TYPE_DOUBLE,
4556       gst_value_subtract_double_range_double);
4557   gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE,
4558       GST_TYPE_DOUBLE_RANGE, gst_value_subtract_double_range_double_range);
4559
4560   gst_value_register_subtract_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
4561       gst_value_subtract_fraction_fraction_range);
4562   gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE, GST_TYPE_FRACTION,
4563       gst_value_subtract_fraction_range_fraction);
4564   gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE,
4565       GST_TYPE_FRACTION_RANGE,
4566       gst_value_subtract_fraction_range_fraction_range);
4567
4568   /* see bug #317246, #64994, #65041 */
4569   {
4570     volatile GType date_type = G_TYPE_DATE;
4571
4572     g_type_name (date_type);
4573   }
4574
4575   gst_value_register_union_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
4576       gst_value_union_int_int_range);
4577   gst_value_register_union_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
4578       gst_value_union_int_range_int_range);
4579
4580 #if 0
4581   /* Implement these if needed */
4582   gst_value_register_union_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
4583       gst_value_union_fraction_fraction_range);
4584   gst_value_register_union_func (GST_TYPE_FRACTION_RANGE,
4585       GST_TYPE_FRACTION_RANGE, gst_value_union_fraction_range_fraction_range);
4586 #endif
4587 }