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