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