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