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