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