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