Header file fix (these ## are needed for multi-arguments) and a fix for gst_props_get...
[platform/upstream/gstreamer.git] / gst / gstprops.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wim.taymans@chello.be>
4  *
5  * gstprops.c: Properties subsystem for generic usage
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /* #define GST_DEBUG_ENABLED */
24 #include "gst_private.h"
25
26 #include "gstlog.h"
27 #include "gstprops.h"
28
29 GType _gst_props_type;
30
31 #define GST_PROPS_ENTRY_IS_VARIABLE(a)  (((GstPropsEntry*)(a))->propstype > GST_PROPS_VAR_TYPE)
32
33 struct _GstPropsEntry {
34   GQuark        propid;
35   GstPropsType  propstype;              
36
37   union {
38     /* flat values */
39     gboolean bool_data;
40     guint32  fourcc_data;
41     gint     int_data;
42     gfloat   float_data;
43
44     /* structured values */
45     struct {
46       GList *entries;
47     } list_data;
48     struct {
49       gchar *string;
50     } string_data;
51     struct {
52       gint min;
53       gint max;
54     } int_range_data;
55     struct {
56       gfloat min;
57       gfloat max;
58     } float_range_data;
59   } data;
60 };
61
62 static GMemChunk *_gst_props_entries_chunk;
63 static GMutex *_gst_props_entries_chunk_lock;
64
65 static GMemChunk *_gst_props_chunk;
66 static GMutex *_gst_props_chunk_lock;
67
68 static gboolean         gst_props_entry_check_compatibility     (GstPropsEntry *entry1, GstPropsEntry *entry2);
69 static GList*           gst_props_list_copy                     (GList *propslist);
70
71         
72 void 
73 _gst_props_initialize (void) 
74 {
75   _gst_props_entries_chunk = g_mem_chunk_new ("GstPropsEntries", 
76                   sizeof (GstPropsEntry), sizeof (GstPropsEntry) * 256, 
77                   G_ALLOC_AND_FREE);
78   _gst_props_entries_chunk_lock = g_mutex_new ();
79
80   _gst_props_chunk = g_mem_chunk_new ("GstProps", 
81                   sizeof (GstProps), sizeof (GstProps) * 256, 
82                   G_ALLOC_AND_FREE);
83   _gst_props_chunk_lock = g_mutex_new ();
84
85   _gst_props_type = g_boxed_type_register_static ("GstProps",
86                                        (GBoxedCopyFunc) gst_props_ref,
87                                        (GBoxedFreeFunc) gst_props_unref);
88
89 }
90
91 static void
92 gst_props_debug_entry (GstPropsEntry *entry)
93 {
94   const gchar *name = g_quark_to_string (entry->propid);
95
96   switch (entry->propstype) {
97     case GST_PROPS_INT_TYPE:
98       GST_DEBUG (GST_CAT_PROPERTIES, "%s: int %d", name, entry->data.int_data);
99       break;
100     case GST_PROPS_FLOAT_TYPE:
101       GST_DEBUG (GST_CAT_PROPERTIES, "%s: float %f", name, entry->data.float_data);
102       break;
103     case GST_PROPS_FOURCC_TYPE:
104       GST_DEBUG (GST_CAT_PROPERTIES, "%s: fourcc %4.4s", name, (gchar*)&entry->data.fourcc_data);
105       break;
106     case GST_PROPS_BOOL_TYPE:
107       GST_DEBUG (GST_CAT_PROPERTIES, "%s: bool %d", name, entry->data.bool_data);
108       break;
109     case GST_PROPS_STRING_TYPE:
110       GST_DEBUG (GST_CAT_PROPERTIES, "%s: string %s", name, entry->data.string_data.string);
111       break;
112     case GST_PROPS_INT_RANGE_TYPE:
113       GST_DEBUG (GST_CAT_PROPERTIES, "%s: int range %d-%d", name, entry->data.int_range_data.min,
114                       entry->data.int_range_data.max);
115       break;
116     case GST_PROPS_FLOAT_RANGE_TYPE:
117       GST_DEBUG (GST_CAT_PROPERTIES, "%s: float range %f-%f", name, entry->data.float_range_data.min,
118                       entry->data.float_range_data.max);
119       break;
120     case GST_PROPS_LIST_TYPE:
121       GST_DEBUG (GST_CAT_PROPERTIES, "[list]");
122       {
123         GList *entries = entry->data.list_data.entries;
124
125         while (entries) {
126           gst_props_debug_entry ((GstPropsEntry *)entries->data);
127           entries = g_list_next (entries);
128         }
129       }
130       break;
131     default:
132       g_warning ("unknown property type %d", entry->propstype);
133       break;
134   }
135 }
136
137 static gint 
138 props_compare_func (gconstpointer a,
139                     gconstpointer b) 
140 {
141   GstPropsEntry *entry1 = (GstPropsEntry *)a;
142   GstPropsEntry *entry2 = (GstPropsEntry *)b;
143
144   return (entry1->propid - entry2->propid);
145 }
146
147 static gint 
148 props_find_func (gconstpointer a,
149                  gconstpointer b) 
150 {
151   GstPropsEntry *entry2 = (GstPropsEntry *)a;
152   GQuark quark = (GQuark) GPOINTER_TO_INT (b);
153
154   return (quark - entry2->propid);
155 }
156
157 /* This is implemented as a huge macro because we cannot pass
158  * va_list variables by reference on some architectures.
159  */
160 #define GST_PROPS_ENTRY_FILL(entry, var_args)                                   \
161 G_STMT_START {                                                                  \
162   entry->propstype = va_arg (var_args, GstPropsType);                           \
163                                                                                 \
164   switch (entry->propstype) {                                                   \
165     case GST_PROPS_INT_TYPE:                                                    \
166       entry->data.int_data = va_arg (var_args, gint);                           \
167       break;                                                                    \
168     case GST_PROPS_INT_RANGE_TYPE:                                              \
169       entry->data.int_range_data.min = va_arg (var_args, gint);                 \
170       entry->data.int_range_data.max = va_arg (var_args, gint);                 \
171       break;                                                                    \
172     case GST_PROPS_FLOAT_TYPE:                                                  \
173       entry->data.float_data = va_arg (var_args, gdouble);                      \
174       break;                                                                    \
175     case GST_PROPS_FLOAT_RANGE_TYPE:                                            \
176       entry->data.float_range_data.min = va_arg (var_args, gdouble);            \
177       entry->data.float_range_data.max = va_arg (var_args, gdouble);            \
178       break;                                                                    \
179     case GST_PROPS_FOURCC_TYPE:                                                 \
180       entry->data.fourcc_data = va_arg (var_args, gulong);                      \
181       break;                                                                    \
182     case GST_PROPS_BOOL_TYPE:                                                   \
183       entry->data.bool_data = va_arg (var_args, gboolean);                      \
184       break;                                                                    \
185     case GST_PROPS_STRING_TYPE:                                                 \
186       entry->data.string_data.string = g_strdup (va_arg (var_args, gchar*));    \
187       break;                                                                    \
188     default:                                                                    \
189       break;                                                                    \
190   }                                                                             \
191 } G_STMT_END
192
193
194 #define GST_PROPS_ENTRY_READ(entry, var_args, safe, result)                     \
195 G_STMT_START {                                                                  \
196   GstPropsType propstype = va_arg (var_args, GstPropsType);                     \
197                                                                                 \
198   *result = TRUE;                                                               \
199                                                                                 \
200   if (safe) {                                                                   \
201     if (propstype != entry->propstype) {                                        \
202       *result = FALSE;                                                          \
203     }                                                                           \
204   }                                                                             \
205   if (*result) {                                                                \
206     switch (entry->propstype) {                                                 \
207       case GST_PROPS_INT_TYPE:                                                  \
208         *(va_arg (var_args, gint*)) = entry->data.int_data;                     \
209         break;                                                                  \
210       case GST_PROPS_INT_RANGE_TYPE:                                            \
211         *(va_arg (var_args, gint*)) = entry->data.int_range_data.min;           \
212         *(va_arg (var_args, gint*)) = entry->data.int_range_data.max;           \
213         break;                                                                  \
214       case GST_PROPS_FLOAT_TYPE:                                                \
215         *(va_arg (var_args, gfloat*)) = entry->data.float_data;                 \
216         break;                                                                  \
217       case GST_PROPS_FLOAT_RANGE_TYPE:                                          \
218         *(va_arg (var_args, gfloat*)) = entry->data.float_range_data.min;       \
219         *(va_arg (var_args, gfloat*)) = entry->data.float_range_data.max;       \
220         break;                                                                  \
221       case GST_PROPS_FOURCC_TYPE:                                               \
222         *(va_arg (var_args, guint32*)) = entry->data.fourcc_data;               \
223         break;                                                                  \
224       case GST_PROPS_BOOL_TYPE:                                                 \
225         *(va_arg (var_args, gboolean*)) = entry->data.bool_data;                \
226         break;                                                                  \
227       case GST_PROPS_STRING_TYPE:                                               \
228         *(va_arg (var_args, gchar**)) = entry->data.string_data.string;         \
229         break;                                                                  \
230       case GST_PROPS_LIST_TYPE:                                                 \
231         *(va_arg (var_args, GList**)) = entry->data.list_data.entries;          \
232         break;                                                                  \
233       default:                                                                  \
234         *result = FALSE;                                                        \
235         break;                                                                  \
236     }                                                                           \
237   }                                                                             \
238 } G_STMT_END
239
240 static GstPropsEntry*
241 gst_props_alloc_entry (void)
242 {
243   GstPropsEntry *entry;
244
245   g_mutex_lock (_gst_props_entries_chunk_lock);
246   entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
247   g_mutex_unlock (_gst_props_entries_chunk_lock);
248
249   return entry;
250 }
251
252 static void
253 gst_props_entry_destroy (GstPropsEntry *entry)
254 {
255   switch (entry->propstype) {
256     case GST_PROPS_STRING_TYPE:         
257       g_free (entry->data.string_data.string);
258       break;                            
259     case GST_PROPS_LIST_TYPE:           
260     {
261       GList *entries = entry->data.list_data.entries;
262
263       while (entries) {
264         gst_props_entry_destroy ((GstPropsEntry *)entries->data);
265         entries = g_list_next (entries);
266       }
267       g_list_free (entry->data.list_data.entries);
268       break;
269     }
270     default:                    
271       break;            
272   }
273   g_mutex_lock (_gst_props_entries_chunk_lock);
274   g_mem_chunk_free (_gst_props_entries_chunk, entry);
275   g_mutex_unlock (_gst_props_entries_chunk_lock);
276 }
277
278 /**
279  * gst_props_empty_new:
280  *
281  * Create a new empty property.
282  *
283  * Returns: the new property
284  */
285 GstProps*
286 gst_props_empty_new (void)
287 {
288   GstProps *props;
289
290   g_mutex_lock (_gst_props_chunk_lock);
291   props = g_mem_chunk_alloc (_gst_props_chunk);
292   g_mutex_unlock (_gst_props_chunk_lock);
293
294   props->properties = NULL;
295   props->refcount = 1;
296   props->fixed = TRUE;
297
298   return props;
299 }
300
301 /**
302  * gst_props_add_entry:
303  * @props: the property to add the entry to
304  * @entry: the entry to add
305  *
306  * Addes the given propsentry to the props
307  */
308 void
309 gst_props_add_entry (GstProps *props, GstPropsEntry *entry)
310 {
311   g_return_if_fail (props);
312   g_return_if_fail (entry);
313
314   if (props->fixed && GST_PROPS_ENTRY_IS_VARIABLE (entry)) {
315     props->fixed = FALSE;
316   }
317   props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
318 }
319
320 /**
321  * gst_props_new:
322  * @firstname: the first property name
323  * @...: the property values 
324  *
325  * Create a new property from the given key/value pairs
326  *
327  * Returns: the new property
328  */
329 GstProps*
330 gst_props_new (const gchar *firstname, ...)
331 {
332   GstProps *props;
333   va_list var_args;
334   
335   va_start (var_args, firstname);
336
337   props = gst_props_newv (firstname, var_args);
338   
339   va_end (var_args);
340   
341   return props;
342
343
344
345 /**
346  * gst_props_debug:
347  * @props: the props to debug
348  *
349  * Dump the contents of the given properties into the DEBUG log.
350  */
351 void
352 gst_props_debug (GstProps *props)
353 {
354   GList *propslist = props->properties;
355
356   while (propslist) { 
357     GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
358
359     gst_props_debug_entry (entry);
360     
361     propslist = g_list_next (propslist);
362   }
363 }
364
365 /**
366  * gst_props_merge_int_entries:
367  * @newentry: the new entry
368  * @oldentry: an old entry
369  *
370  * Tries to merge oldentry into newentry, if there is a simpler single entry which represents
371  *
372  * Assumes that the entries are either ints or int ranges.
373  *
374  * Returns: TRUE if the entries were merged, FALSE otherwise.
375  */
376 static gboolean
377 gst_props_merge_int_entries(GstPropsEntry * newentry, GstPropsEntry * oldentry)
378 {
379   gint new_min, new_max, old_min, old_max;
380   gboolean can_merge = FALSE;
381
382   if (newentry->propstype == GST_PROPS_INT_TYPE) {
383     new_min = newentry->data.int_data;
384     new_max = newentry->data.int_data;
385   } else {
386     new_min = newentry->data.int_range_data.min;
387     new_max = newentry->data.int_range_data.max;
388   }
389
390   if (oldentry->propstype == GST_PROPS_INT_TYPE) {
391     old_min = oldentry->data.int_data;
392     old_max = oldentry->data.int_data;
393   } else {
394     old_min = oldentry->data.int_range_data.min;
395     old_max = oldentry->data.int_range_data.max;
396   }
397
398   /* Put range which starts lower into (new_min, new_max) */
399   if (old_min < new_min) {
400     gint tmp;
401     tmp = old_min;
402     old_min = new_min;
403     new_min = tmp;
404     tmp = old_max;
405     old_max = new_max;
406     new_max = tmp;
407   }
408
409   /* new_min is min of either entry - second half of the following conditional */
410   /* is to avoid overflow problems. */
411   if (new_max >= old_min - 1 && old_min - 1 < old_min) {
412     /* ranges overlap, or are adjacent.  Pick biggest maximum. */
413     can_merge = TRUE;
414     if (old_max > new_max) new_max = old_max;
415   }
416
417   if (can_merge) {
418     if (new_min == new_max) {
419       newentry->propstype = GST_PROPS_INT_TYPE;
420       newentry->data.int_data = new_min;
421     } else {
422       newentry->propstype = GST_PROPS_INT_RANGE_TYPE;
423       newentry->data.int_range_data.min = new_min;
424       newentry->data.int_range_data.max = new_max;
425     }
426   }
427   return can_merge;
428 }
429
430 /**
431  * gst_props_add_to_int_list:
432  * @entries: the existing list of entries
433  * @entry: the new entry to add to the list
434  *
435  * Add an integer property to a list of properties, removing duplicates
436  * and merging ranges.
437  *
438  * Assumes that the existing list is in simplest form, contains
439  * only ints and int ranges, and that the new entry is an int or 
440  * an int range.
441  *
442  * Returns: a pointer to a list with the new entry added.
443  */
444 static GList *
445 gst_props_add_to_int_list (GList * entries, GstPropsEntry * newentry)
446 {
447   GList * i;
448
449   i = entries;
450   while (i) {
451     GstPropsEntry * oldentry = (GstPropsEntry *)(i->data);
452     gboolean merged = gst_props_merge_int_entries(newentry, oldentry);
453
454     if (merged) {
455       /* replace the existing one with the merged one */
456       g_mutex_lock (_gst_props_entries_chunk_lock);
457       g_mem_chunk_free (_gst_props_entries_chunk, oldentry);
458       g_mutex_unlock (_gst_props_entries_chunk_lock);
459       entries = g_list_remove_link (entries, i);
460       g_list_free_1 (i);
461
462       /* start again: it's possible that this change made an earlier entry */
463       /* mergeable, and the pointer is now invalid anyway. */
464       i = entries;
465     }
466
467     i = g_list_next (i);
468   }
469
470   return g_list_prepend (entries, newentry);
471 }
472
473 static GstPropsEntry*
474 gst_props_entry_newv (const gchar *name, va_list var_args)
475 {
476   GstPropsEntry *entry;
477
478   entry = gst_props_alloc_entry ();
479   entry->propid = g_quark_from_string (name);
480   GST_PROPS_ENTRY_FILL (entry, var_args);
481   
482   return entry;
483 }
484
485 /**
486  * gst_props_entry_new:
487  * @name: the name of the props entry
488  * @...: the value of the entry
489  *
490  * Create a new property entry with the given key/value.
491  *
492  * Returns: the new entry.
493  */
494 GstPropsEntry*
495 gst_props_entry_new (const gchar *name, ...)
496 {
497   va_list var_args;
498   GstPropsEntry *entry;
499   
500   va_start (var_args, name);
501   entry = gst_props_entry_newv (name, var_args);
502   va_end (var_args);
503
504   return entry;
505 }
506
507 /**
508  * gst_props_newv:
509  * @firstname: the first property name
510  * @var_args: the property values
511  *
512  * Create a new property from the list of entries.
513  *
514  * Returns: the new property created from the list of entries
515  */
516 GstProps*
517 gst_props_newv (const gchar *firstname, va_list var_args)
518 {
519   GstProps *props;
520   gboolean inlist = FALSE;
521   const gchar *prop_name;
522   GstPropsEntry *list_entry = NULL;
523
524   typedef enum {
525       GST_PROPS_LIST_T_UNSET,
526       GST_PROPS_LIST_T_INTS,
527       GST_PROPS_LIST_T_FLOATS,
528       GST_PROPS_LIST_T_MISC,
529   } list_types;
530
531   /* type of the list */
532   list_types list_type = GST_PROPS_LIST_T_UNSET;
533   /* type of current item */
534   list_types entry_type = GST_PROPS_LIST_T_UNSET;
535
536   if (firstname == NULL)
537     return NULL;
538
539   props = gst_props_empty_new ();
540
541   prop_name = firstname;
542
543   /* properties */
544   while (prop_name) {
545     GstPropsEntry *entry;
546    
547     entry = gst_props_alloc_entry ();
548     entry->propid = g_quark_from_string (prop_name);
549     GST_PROPS_ENTRY_FILL (entry, var_args);
550
551     switch (entry->propstype) {
552       case GST_PROPS_INT_TYPE:
553       case GST_PROPS_INT_RANGE_TYPE:
554         entry_type = GST_PROPS_LIST_T_INTS;
555         break;
556       case GST_PROPS_FLOAT_TYPE:
557       case GST_PROPS_FLOAT_RANGE_TYPE:
558         entry_type = GST_PROPS_LIST_T_FLOATS;
559         break;
560       case GST_PROPS_FOURCC_TYPE:
561       case GST_PROPS_BOOL_TYPE:
562       case GST_PROPS_STRING_TYPE:
563         entry_type = GST_PROPS_LIST_T_MISC;
564         break;
565       case GST_PROPS_LIST_TYPE:
566         g_return_val_if_fail (inlist == FALSE, NULL);
567         inlist = TRUE;
568         list_entry = entry;
569         list_type = GST_PROPS_LIST_T_UNSET;
570         list_entry->data.list_data.entries = NULL;
571         break;
572       case GST_PROPS_END_TYPE:
573         g_return_val_if_fail (inlist == TRUE, NULL);
574
575         /* if list was of size 1, replace the list by a the item it contains */
576         if (g_list_length(list_entry->data.list_data.entries) == 1) {
577           GstPropsEntry * subentry = (GstPropsEntry *)(list_entry->data.list_data.entries->data);
578           list_entry->propstype = subentry->propstype;
579           list_entry->data = subentry->data;
580           g_mutex_lock (_gst_props_entries_chunk_lock);
581           g_mem_chunk_free (_gst_props_entries_chunk, subentry);
582           g_mutex_unlock (_gst_props_entries_chunk_lock);
583         }
584         else {
585           list_entry->data.list_data.entries =
586                     g_list_reverse (list_entry->data.list_data.entries);
587         }
588
589         g_mutex_lock (_gst_props_entries_chunk_lock);
590         g_mem_chunk_free (_gst_props_entries_chunk, entry);
591         g_mutex_unlock (_gst_props_entries_chunk_lock);
592         inlist = FALSE;
593         list_entry = NULL;
594         prop_name = va_arg (var_args, gchar*);
595         continue;
596       default:
597         g_warning ("unknown property type found %d for '%s'\n", entry->propstype, prop_name);
598         g_mutex_lock (_gst_props_entries_chunk_lock);
599         g_mem_chunk_free (_gst_props_entries_chunk, entry);
600         g_mutex_unlock (_gst_props_entries_chunk_lock);
601         break;
602     }
603
604     if (inlist && (list_entry != entry)) {
605       if (list_type == GST_PROPS_LIST_T_UNSET) list_type = entry_type;
606       if (list_type != entry_type) {
607         g_warning ("property list contained incompatible entry types\n");
608       } else {
609         switch (list_type) {
610           case GST_PROPS_LIST_T_INTS:
611             list_entry->data.list_data.entries =
612                     gst_props_add_to_int_list (list_entry->data.list_data.entries, entry);
613             break;
614           default:
615             list_entry->data.list_data.entries =
616                     g_list_prepend (list_entry->data.list_data.entries, entry);
617             break;
618         }
619       }
620     }
621     else {
622       gst_props_add_entry (props, entry);
623     }
624     if (!inlist)
625       prop_name = va_arg (var_args, gchar*);
626   }
627
628   return props;
629 }
630
631 /**
632  * gst_props_set:
633  * @props: the props to modify
634  * @name: the name of the entry to modify
635  * @...: The prop entry.
636  *
637  * Modifies the value of the given entry in the props struct.
638  * For the optional args, use GST_PROPS_FOO, where FOO is INT,
639  * STRING, etc. This macro expands to a variable number of arguments,
640  * hence the lack of precision in the function prototype. No
641  * terminating NULL is necessary as only one property can be changed.
642  *
643  * Returns: the new modified property structure.
644  */
645 GstProps*
646 gst_props_set (GstProps *props, const gchar *name, ...)
647 {
648   GQuark quark;
649   GList *lentry;
650   va_list var_args;
651
652   g_return_val_if_fail (props != NULL, NULL);
653   
654   quark = g_quark_from_string (name);
655
656   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
657
658   if (lentry) {
659     GstPropsEntry *entry;
660
661     entry = (GstPropsEntry *)lentry->data;
662
663     va_start (var_args, name);
664
665     GST_PROPS_ENTRY_FILL (entry, var_args);
666
667     va_end (var_args);
668   }
669   else {
670     g_print("gstprops: no property '%s' to change\n", name);
671   }
672
673   return props;
674 }
675
676
677 /**
678  * gst_props_unref:
679  * @props: the props to unref
680  *
681  * Decrease the refcount of the property structure, destroying
682  * the property if the refcount is 0.
683  */
684 void
685 gst_props_unref (GstProps *props)
686 {
687   if (props == NULL)
688     return;
689   
690   props->refcount--;
691
692   if (props->refcount == 0)
693     gst_props_destroy (props);
694 }
695
696 /**
697  * gst_props_ref:
698  * @props: the props to ref
699  *
700  * Increase the refcount of the property structure.
701  */
702 void
703 gst_props_ref (GstProps *props)
704 {
705   g_return_if_fail (props != NULL);
706   
707   props->refcount++;
708 }
709
710
711 /**
712  * gst_props_destroy:
713  * @props: the props to destroy
714  *
715  * Destroy the property, freeing all the memory that
716  * was allocated.
717  */
718 void
719 gst_props_destroy (GstProps *props)
720 {
721   GList *entries;
722
723   if (props == NULL)
724     return;
725   
726   entries = props->properties;
727
728   while (entries) {
729     gst_props_entry_destroy ((GstPropsEntry *)entries->data);
730     entries = g_list_next (entries);
731   }
732   g_list_free (props->properties);
733
734   g_mutex_lock (_gst_props_chunk_lock);
735   g_mem_chunk_free (_gst_props_chunk, props);
736   g_mutex_unlock (_gst_props_chunk_lock);
737 }
738
739 /* 
740  * copy entries 
741  */
742 static GstPropsEntry*
743 gst_props_entry_copy (GstPropsEntry *entry)
744 {
745   GstPropsEntry *newentry;
746
747   newentry = gst_props_alloc_entry ();
748   memcpy (newentry, entry, sizeof (GstPropsEntry));
749   if (entry->propstype == GST_PROPS_LIST_TYPE) {
750     newentry->data.list_data.entries = gst_props_list_copy (entry->data.list_data.entries);
751   }
752   else if (entry->propstype == GST_PROPS_STRING_TYPE) {
753     newentry->data.string_data.string = g_strdup (entry->data.string_data.string);
754   }
755
756   return newentry;
757 }
758
759 static GList*
760 gst_props_list_copy (GList *propslist)
761 {
762   GList *new = NULL;
763
764   while (propslist) {
765     GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
766
767     new = g_list_prepend (new, gst_props_entry_copy (entry));
768     
769     propslist = g_list_next (propslist);
770   }
771   new = g_list_reverse (new);
772
773   return new;
774 }
775
776 /**
777  * gst_props_copy:
778  * @props: the props to copy
779  *
780  * Copy the property structure.
781  *
782  * Returns: the new property that is a copy of the original
783  * one.
784  */
785 GstProps*
786 gst_props_copy (GstProps *props)
787 {
788   GstProps *new;
789
790   if (props == NULL)
791     return NULL;
792
793   new = gst_props_empty_new ();
794   new->properties = gst_props_list_copy (props->properties);
795   new->fixed = props->fixed;
796
797   return new;
798 }
799
800 /**
801  * gst_props_copy_on_write:
802  * @props: the props to copy on write
803  *
804  * Copy the property structure if the refcount is >1.
805  *
806  * Returns: A new props that can be safely written to.
807  */
808 GstProps*
809 gst_props_copy_on_write (GstProps *props)
810 {
811   GstProps *new = props;;
812
813   g_return_val_if_fail (props != NULL, NULL);
814
815   if (props->refcount > 1) {
816     new = gst_props_copy (props);
817     gst_props_unref (props);
818   }
819
820   return new;
821 }
822
823 /**
824  * gst_props_get_entry:
825  * @props: the props to query
826  * @name: the name of the entry to get
827  *
828  * Get the props entry with the geven name
829  *
830  * Returns: The props entry with the geven name or NULL when
831  * the entry was not found.
832  */
833 const GstPropsEntry*
834 gst_props_get_entry (GstProps *props, const gchar *name)
835 {
836   GList *lentry;
837   GQuark quark;
838   
839   g_return_val_if_fail (props != NULL, NULL);
840   g_return_val_if_fail (name != NULL, NULL);
841
842   quark = g_quark_from_string (name);
843
844   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
845
846   if (lentry) {
847     GstPropsEntry *thisentry;
848     thisentry = (GstPropsEntry *)lentry->data;
849     return thisentry;
850   }
851   return NULL;
852 }
853
854 /**
855  * gst_props_has_property:
856  * @props: the props to check
857  * @name: the name of the key to find
858  *
859  * Checks if a given props has a property with the given name.
860  *
861  * Returns: TRUE if the property was found, FALSE otherwise.
862  */
863 gboolean
864 gst_props_has_property (GstProps *props, const gchar *name)
865 {
866   return (gst_props_get_entry (props, name) != NULL);
867 }
868
869 /**
870  * gst_props_has_property_typed:
871  * @props: the props to check
872  * @name: the name of the key to find
873  * @type: the type of the required property
874  *
875  * Checks if a given props has a property with the given name and the given type.
876  *
877  * Returns: TRUE if the property was found, FALSE otherwise.
878  */
879 gboolean
880 gst_props_has_property_typed (GstProps *props, const gchar *name, GstPropsType type)
881 {
882   const GstPropsEntry *entry;
883
884   entry = gst_props_get_entry (props, name);
885   if (!entry) 
886     return FALSE;
887
888   return (entry->propstype == type);
889 }
890
891 /**
892  * gst_props_has_fixed_property:
893  * @props: the props to check
894  * @name: the name of the key to find
895  *
896  * Checks if a given props has a property with the given name that 
897  * is also fixed, ie. is not a list or a range.
898  *
899  * Returns: TRUE if the property was found, FALSE otherwise.
900  */
901 gboolean
902 gst_props_has_fixed_property (GstProps *props, const gchar *name)
903 {
904   const GstPropsEntry *entry;
905
906   entry = gst_props_get_entry (props, name);
907   if (!entry) 
908     return FALSE;
909
910   return !GST_PROPS_ENTRY_IS_VARIABLE (entry);
911 }
912
913 /**
914  * gst_props_entry_get_type:
915  * @entry: the props entry to query
916  *
917  * Get the type of the given props entry. 
918  *
919  * Returns: The type of the props entry.
920  */
921 GstPropsType
922 gst_props_entry_get_type (const GstPropsEntry *entry)
923 {
924   g_return_val_if_fail (entry != NULL, GST_PROPS_INVALID_TYPE);
925
926   return entry->propstype;
927 }
928
929 /**
930  * gst_props_entry_get_name:
931  * @entry: the props entry to query
932  *
933  * Get the name of the given props entry. 
934  *
935  * Returns: The name of the props entry.
936  */
937 const gchar*
938 gst_props_entry_get_name (const GstPropsEntry *entry)
939 {
940   g_return_val_if_fail (entry != NULL, NULL);
941
942   return g_quark_to_string (entry->propid);
943 }
944
945 /**
946  * gst_props_entry_is_fixed:
947  * @entry: the props entry to query
948  *
949  * Checks if the props entry is fixe, ie. is not a list
950  * or a range.
951  *
952  * Returns: TRUE is the props entry is fixed.
953  */
954 gboolean
955 gst_props_entry_is_fixed (const GstPropsEntry *entry)
956 {
957   g_return_val_if_fail (entry != NULL, FALSE);
958
959   return !GST_PROPS_ENTRY_IS_VARIABLE (entry);
960 }
961
962 static gboolean
963 gst_props_entry_getv (const GstPropsEntry *entry, gboolean safe, va_list var_args)
964 {
965   gboolean result;
966
967   GST_PROPS_ENTRY_READ (entry, var_args, safe, &result);
968
969   return result;
970 }
971
972 /**
973  * gst_props_entry_get:
974  * @entry: the props entry to query
975  * @...: a pointer to a type that can hold the value.
976  *
977  * Gets the contents of the entry.
978  *
979  * Returns: TRUE is the props entry could be fetched.
980  */
981 gboolean
982 gst_props_entry_get (const GstPropsEntry *entry, ...)
983 {
984   gboolean result;
985   va_list var_args;
986
987   g_return_val_if_fail (entry != NULL, FALSE);
988
989   va_start (var_args, entry);
990   result = gst_props_entry_getv (entry, FALSE, var_args);
991   va_end (var_args);
992
993   return result;
994 }
995
996 static gboolean
997 gst_props_entry_get_safe (const GstPropsEntry *entry, ...)
998 {
999   gboolean result;
1000   va_list var_args;
1001
1002   g_return_val_if_fail (entry != NULL, FALSE);
1003
1004   va_start (var_args, entry);
1005   result = gst_props_entry_getv (entry, TRUE, var_args);
1006   va_end (var_args);
1007
1008   return result;
1009 }
1010
1011 /**
1012  * gst_props_get:
1013  * @props: the props to query
1014  * @first_name: the first key
1015  * @...: a pointer to a datastructure that can hold the value.
1016  *
1017  * Gets the contents of the props into given key/value pairs.
1018  *
1019  * Returns: TRUE is the props entry could be fetched.
1020  */
1021 gboolean
1022 gst_props_get (GstProps *props, gchar *first_name, ...)
1023 {
1024   va_list var_args;
1025
1026   va_start (var_args, first_name);
1027
1028   while (first_name) {
1029     const GstPropsEntry *entry = gst_props_get_entry (props, first_name);
1030     gboolean result;
1031
1032     if (!entry) return FALSE;
1033     GST_PROPS_ENTRY_READ (entry, var_args, FALSE, &result);
1034     if (!result) return FALSE;
1035
1036     first_name = va_arg (var_args, gchar *);
1037   }
1038   va_end (var_args);
1039   
1040   return TRUE;
1041 }
1042
1043 /**
1044  * gst_props_entry_get_int:
1045  * @entry: the props entry to query
1046  * @val: a pointer to a gint to hold the value.
1047  *
1048  * Get the contents of the entry into the given gint.
1049  *
1050  * Returns: TRUE is the value could be fetched. FALSE if the 
1051  * entry is not of given type.
1052  */
1053 gboolean
1054 gst_props_entry_get_int (const GstPropsEntry *entry, gint *val)
1055 {
1056   return gst_props_entry_get_safe (entry, GST_PROPS_INT_TYPE, val);
1057 }
1058
1059 /**
1060  * gst_props_entry_get_float:
1061  * @entry: the props entry to query
1062  * @val: a pointer to a gfloat to hold the value.
1063  *
1064  * Get the contents of the entry into the given gfloat.
1065  *
1066  * Returns: TRUE is the value could be fetched. FALSE if the 
1067  * entry is not of given type.
1068  */
1069 gboolean
1070 gst_props_entry_get_float (const GstPropsEntry *entry, gfloat *val)
1071 {
1072   return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_TYPE, val);
1073 }
1074
1075 /**
1076  * gst_props_entry_get_fourcc_int:
1077  * @entry: the props entry to query
1078  * @val: a pointer to a guint32 to hold the value.
1079  *
1080  * Get the contents of the entry into the given guint32.
1081  *
1082  * Returns: TRUE is the value could be fetched. FALSE if the 
1083  * entry is not of given type.
1084  */
1085 gboolean
1086 gst_props_entry_get_fourcc_int (const GstPropsEntry *entry, guint32 *val)
1087 {
1088   return gst_props_entry_get_safe (entry, GST_PROPS_FOURCC_TYPE, val);
1089 }
1090
1091 /**
1092  * gst_props_entry_get_boolean:
1093  * @entry: the props entry to query
1094  * @val: a pointer to a gboolean to hold the value.
1095  *
1096  * Get the contents of the entry into the given gboolean.
1097  *
1098  * Returns: TRUE is the value could be fetched. FALSE if the 
1099  * entry is not of given type.
1100  */
1101 gboolean
1102 gst_props_entry_get_boolean (const GstPropsEntry *entry, gboolean *val)
1103 {
1104   return gst_props_entry_get_safe (entry, GST_PROPS_BOOL_TYPE, val);
1105 }
1106
1107 /**
1108  * gst_props_entry_get_string:
1109  * @entry: the props entry to query
1110  * @val: a pointer to a gchar* to hold the value.
1111  *
1112  * Get the contents of the entry into the given gchar*.
1113  *
1114  * Returns: TRUE is the value could be fetched. FALSE if the 
1115  * entry is not of given type.
1116  */
1117 gboolean
1118 gst_props_entry_get_string (const GstPropsEntry *entry, const gchar **val)
1119 {
1120   return gst_props_entry_get_safe (entry, GST_PROPS_STRING_TYPE, val);
1121 }
1122
1123 /**
1124  * gst_props_entry_get_int_range:
1125  * @entry: the props entry to query
1126  * @min: a pointer to a gint to hold the minimun value.
1127  * @max: a pointer to a gint to hold the maximum value.
1128  *
1129  * Get the contents of the entry into the given gints.
1130  *
1131  * Returns: TRUE is the value could be fetched. FALSE if the 
1132  * entry is not of given type.
1133  */
1134 gboolean
1135 gst_props_entry_get_int_range (const GstPropsEntry *entry, gint *min, gint *max)
1136 {
1137   return gst_props_entry_get_safe (entry, GST_PROPS_INT_RANGE_TYPE, min, max);
1138 }
1139
1140 /**
1141  * gst_props_entry_get_float_range:
1142  * @entry: the props entry to query
1143  * @min: a pointer to a gfloat to hold the minimun value.
1144  * @max: a pointer to a gfloat to hold the maximum value.
1145  *
1146  * Get the contents of the entry into the given gfloats.
1147  *
1148  * Returns: TRUE is the value could be fetched. FALSE if the 
1149  * entry is not of given type.
1150  */
1151 gboolean
1152 gst_props_entry_get_float_range (const GstPropsEntry *entry, gfloat *min, gfloat *max)
1153 {
1154   return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_RANGE_TYPE, min, max);
1155 }
1156
1157 /**
1158  * gst_props_entry_get_list:
1159  * @entry: the props entry to query
1160  * @val: a pointer to a GList to hold the value.
1161  *
1162  * Get the contents of the entry into the given GList.
1163  *
1164  * Returns: TRUE is the value could be fetched. FALSE if the 
1165  * entry is not of given type.
1166  */
1167 gboolean
1168 gst_props_entry_get_list (const GstPropsEntry *entry, const GList **val)
1169 {
1170   return gst_props_entry_get_safe (entry, GST_PROPS_LIST_TYPE, val);
1171 }
1172
1173 /**
1174  * gst_props_merge:
1175  * @props: the property to merge into
1176  * @tomerge: the property to merge 
1177  *
1178  * Merge the properties of tomerge into props.
1179  *
1180  * Returns: the new merged property 
1181  */
1182 GstProps*
1183 gst_props_merge (GstProps *props, GstProps *tomerge)
1184 {
1185   GList *merge_props;
1186
1187   g_return_val_if_fail (props != NULL, NULL);
1188   g_return_val_if_fail (tomerge != NULL, NULL);
1189
1190   merge_props = tomerge->properties;
1191
1192   /* FIXME do proper merging here... */
1193   while (merge_props) {
1194     GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
1195
1196     gst_props_add_entry (props, entry);
1197           
1198     merge_props = g_list_next (merge_props);
1199   }
1200
1201   return props;
1202 }
1203
1204
1205 /* entry2 is always a list, entry1 never is */
1206 static gboolean
1207 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
1208 {
1209   GList *entrylist = entry2->data.list_data.entries;
1210   gboolean found = FALSE;
1211
1212   while (entrylist && !found) {
1213     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1214
1215     found |= gst_props_entry_check_compatibility (entry1, entry);
1216
1217     entrylist = g_list_next (entrylist);
1218   }
1219
1220   return found;
1221 }
1222
1223 static gboolean
1224 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
1225 {
1226   GST_DEBUG (GST_CAT_PROPERTIES,"compare: %s %s", g_quark_to_string (entry1->propid),
1227                              g_quark_to_string (entry2->propid));
1228
1229   if (entry2->propstype == GST_PROPS_LIST_TYPE && entry1->propstype != GST_PROPS_LIST_TYPE) {
1230     return gst_props_entry_check_list_compatibility (entry1, entry2);
1231   }
1232
1233   switch (entry1->propstype) {
1234     case GST_PROPS_LIST_TYPE:
1235     {
1236       GList *entrylist = entry1->data.list_data.entries;
1237       gboolean valid = TRUE;    /* innocent until proven guilty */
1238
1239       while (entrylist && valid) {
1240         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1241
1242         valid &= gst_props_entry_check_compatibility (entry, entry2);
1243         
1244         entrylist = g_list_next (entrylist);
1245       }
1246       
1247       return valid;
1248     }
1249     case GST_PROPS_INT_RANGE_TYPE:
1250       switch (entry2->propstype) {
1251         /* a - b   <--->   a - c */
1252         case GST_PROPS_INT_RANGE_TYPE:
1253           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
1254                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
1255         default:
1256           break;
1257       }
1258       break;
1259     case GST_PROPS_FLOAT_RANGE_TYPE:
1260       switch (entry2->propstype) {
1261         /* a - b   <--->   a - c */
1262         case GST_PROPS_FLOAT_RANGE_TYPE:
1263           return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
1264                   entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
1265         default:
1266           break;
1267       }
1268       break;
1269     case GST_PROPS_FOURCC_TYPE:
1270       switch (entry2->propstype) {
1271         /* b   <--->   a */
1272         case GST_PROPS_FOURCC_TYPE:
1273           GST_DEBUG(GST_CAT_PROPERTIES,"\"%4.4s\" <--> \"%4.4s\" ?",
1274                           (char*) &entry2->data.fourcc_data, (char*) &entry1->data.fourcc_data);
1275           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
1276         default:
1277           break;
1278       }
1279       break;
1280     case GST_PROPS_INT_TYPE:
1281       switch (entry2->propstype) {
1282         /* b   <--->   a - d */
1283         case GST_PROPS_INT_RANGE_TYPE:
1284           GST_DEBUG(GST_CAT_PROPERTIES,"%d <= %d <= %d ?",entry2->data.int_range_data.min,
1285                     entry1->data.int_data,entry2->data.int_range_data.max);
1286           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
1287                   entry2->data.int_range_data.max >= entry1->data.int_data);
1288         /* b   <--->   a */
1289         case GST_PROPS_INT_TYPE:
1290           GST_DEBUG(GST_CAT_PROPERTIES,"%d == %d ?",entry1->data.int_data,entry2->data.int_data);
1291           return (entry2->data.int_data == entry1->data.int_data);
1292         default:
1293           break;
1294       }
1295       break;
1296     case GST_PROPS_FLOAT_TYPE:
1297       switch (entry2->propstype) {
1298         /* b   <--->   a - d */
1299         case GST_PROPS_FLOAT_RANGE_TYPE:
1300           return (entry2->data.float_range_data.min <= entry1->data.float_data &&
1301                   entry2->data.float_range_data.max >= entry1->data.float_data);
1302         /* b   <--->   a */
1303         case GST_PROPS_FLOAT_TYPE:
1304           return (entry2->data.float_data == entry1->data.float_data);
1305         default:
1306           break;
1307       }
1308       break;
1309     case GST_PROPS_BOOL_TYPE:
1310       switch (entry2->propstype) {
1311         /* t   <--->   t */
1312         case GST_PROPS_BOOL_TYPE:
1313           return (entry2->data.bool_data == entry1->data.bool_data);
1314         default:
1315           break;
1316       }
1317     case GST_PROPS_STRING_TYPE:
1318       switch (entry2->propstype) {
1319         /* t   <--->   t */
1320         case GST_PROPS_STRING_TYPE:
1321           GST_DEBUG(GST_CAT_PROPERTIES,"\"%s\" <--> \"%s\" ?",
1322                           entry2->data.string_data.string, entry1->data.string_data.string);
1323           return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
1324         default:
1325           break;
1326       }
1327     default:
1328       break;
1329   }
1330
1331   return FALSE;
1332 }
1333
1334 /**
1335  * gst_props_check_compatibility:
1336  * @fromprops: a property
1337  * @toprops: a property
1338  *
1339  * Checks whether two capabilities are compatible.
1340  *
1341  * Returns: TRUE if compatible, FALSE otherwise
1342  */
1343 gboolean
1344 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
1345 {
1346   GList *sourcelist;
1347   GList *sinklist;
1348   gint missing = 0;
1349   gint more = 0;
1350   gboolean compatible = TRUE;
1351
1352   g_return_val_if_fail (fromprops != NULL, FALSE);
1353   g_return_val_if_fail (toprops != NULL, FALSE);
1354         
1355   sourcelist = fromprops->properties;
1356   sinklist   = toprops->properties;
1357
1358   while (sourcelist && sinklist && compatible) {
1359     GstPropsEntry *entry1;
1360     GstPropsEntry *entry2;
1361
1362     entry1 = (GstPropsEntry *)sourcelist->data;
1363     entry2 = (GstPropsEntry *)sinklist->data;
1364
1365     while (entry1->propid < entry2->propid) {
1366       more++;
1367       sourcelist = g_list_next (sourcelist);
1368       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
1369       else goto end;
1370     }
1371     while (entry1->propid > entry2->propid) {
1372       missing++;
1373       sinklist = g_list_next (sinklist);
1374       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
1375       else goto end;
1376     }
1377
1378     if (!gst_props_entry_check_compatibility (entry1, entry2)) {
1379         compatible = FALSE; 
1380         GST_DEBUG (GST_CAT_PROPERTIES, "%s are not compatible: ",
1381                    g_quark_to_string (entry1->propid));
1382     }
1383
1384     sourcelist = g_list_next (sourcelist);
1385     sinklist = g_list_next (sinklist);
1386   }
1387   if (sinklist && compatible) {
1388     GstPropsEntry *entry2;
1389     entry2 = (GstPropsEntry *)sinklist->data;
1390     missing++;
1391   }
1392 end:
1393
1394   if (missing)
1395     return FALSE;
1396
1397   return compatible;
1398 }
1399
1400 static GstPropsEntry*
1401 gst_props_entry_intersect (GstPropsEntry *entry1, GstPropsEntry *entry2)
1402 {
1403   GstPropsEntry *result = NULL;
1404
1405   /* try to move the ranges and lists first */
1406   switch (entry2->propstype) {
1407     case GST_PROPS_INT_RANGE_TYPE:
1408     case GST_PROPS_FLOAT_RANGE_TYPE:
1409     case GST_PROPS_LIST_TYPE:
1410     {
1411       GstPropsEntry *temp;
1412
1413       temp = entry1;
1414       entry1 = entry2;
1415       entry2 = temp;
1416     }
1417     default:
1418       break;
1419   }
1420
1421   switch (entry1->propstype) {
1422     case GST_PROPS_LIST_TYPE:
1423     {
1424       GList *entrylist = entry1->data.list_data.entries;
1425       GList *intersection = NULL;
1426
1427       while (entrylist) {
1428         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1429         GstPropsEntry *intersectentry;
1430
1431         intersectentry = gst_props_entry_intersect (entry2, entry);
1432
1433         if (intersectentry) {
1434           if (intersectentry->propstype == GST_PROPS_LIST_TYPE) {
1435             intersection = g_list_concat (intersection, intersectentry->data.list_data.entries);
1436             /* set the list to NULL because the entries are concatenated to the above
1437              * list and we don't want to free them */
1438             intersectentry->data.list_data.entries = NULL;
1439             gst_props_entry_destroy (intersectentry);
1440           }
1441           else {
1442             intersection = g_list_prepend (intersection, intersectentry);
1443           }
1444         }
1445         entrylist = g_list_next (entrylist);
1446       }
1447       if (intersection) {
1448         /* check if the list only contains 1 element, if so, we can just copy it */
1449         if (g_list_next (intersection) == NULL) {
1450           result = (GstPropsEntry *) (intersection->data); 
1451           g_list_free (intersection);
1452         }
1453         /* else we need to create a new entry to hold the list */
1454         else {
1455           result = gst_props_alloc_entry ();
1456           result->propid = entry1->propid;
1457           result->propstype = GST_PROPS_LIST_TYPE;
1458           result->data.list_data.entries = g_list_reverse (intersection);
1459         }
1460       }
1461       return result;
1462     }
1463     case GST_PROPS_INT_RANGE_TYPE:
1464       switch (entry2->propstype) {
1465         /* a - b   <--->   a - c */
1466         case GST_PROPS_INT_RANGE_TYPE:
1467         {
1468           gint lower = MAX (entry1->data.int_range_data.min, entry2->data.int_range_data.min);
1469           gint upper = MIN (entry1->data.int_range_data.max, entry2->data.int_range_data.max);
1470
1471           if (lower <= upper) {
1472             result = gst_props_alloc_entry ();
1473             result->propid = entry1->propid;
1474
1475             if (lower == upper) {
1476               result->propstype = GST_PROPS_INT_TYPE;
1477               result->data.int_data = lower;
1478             }
1479             else {
1480               result->propstype = GST_PROPS_INT_RANGE_TYPE;
1481               result->data.int_range_data.min = lower;
1482               result->data.int_range_data.max = upper;
1483             }
1484           }
1485           break;
1486         }
1487         case GST_PROPS_LIST_TYPE:
1488         {
1489           GList *entries = entry2->data.list_data.entries;
1490           result = gst_props_alloc_entry ();
1491           result->propid = entry1->propid;
1492           result->propstype = GST_PROPS_LIST_TYPE;
1493           result->data.list_data.entries = NULL;
1494           while (entries) {
1495             GstPropsEntry * this = (GstPropsEntry *)entries->data;
1496             if (this->propstype != GST_PROPS_INT_TYPE) {
1497               /* no hope, this list doesn't even contain ints! */
1498               gst_props_entry_destroy (result);
1499               result = NULL;
1500               break;
1501             }
1502             if (this->data.int_data >= entry1->data.int_range_data.min &&
1503                 this->data.int_data <= entry1->data.int_range_data.max) {
1504               result->data.list_data.entries = g_list_append (result->data.list_data.entries,
1505                                                               gst_props_entry_copy (this));
1506             }
1507             entries = g_list_next (entries);
1508           }
1509           break;
1510         }
1511         case GST_PROPS_INT_TYPE:
1512         {
1513           if (entry1->data.int_range_data.min <= entry2->data.int_data && 
1514               entry1->data.int_range_data.max >= entry2->data.int_data) {
1515             result = gst_props_entry_copy (entry2);
1516           }
1517           break;
1518         }
1519         default:
1520           break;
1521       }
1522       break;
1523     case GST_PROPS_FLOAT_RANGE_TYPE:
1524       switch (entry2->propstype) {
1525         /* a - b   <--->   a - c */
1526         case GST_PROPS_FLOAT_RANGE_TYPE:
1527         {
1528           gfloat lower = MAX (entry1->data.float_range_data.min, entry2->data.float_range_data.min);
1529           gfloat upper = MIN (entry1->data.float_range_data.max, entry2->data.float_range_data.max);
1530
1531           if (lower <= upper) {
1532             result = gst_props_alloc_entry ();
1533             result->propid = entry1->propid;
1534
1535             if (lower == upper) {
1536               result->propstype = GST_PROPS_FLOAT_TYPE;
1537               result->data.float_data = lower;
1538             }
1539             else {
1540               result->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
1541               result->data.float_range_data.min = lower;
1542               result->data.float_range_data.max = upper;
1543             }
1544           }
1545           break;
1546         }
1547         case GST_PROPS_FLOAT_TYPE:
1548           if (entry1->data.float_range_data.min <= entry2->data.float_data && 
1549               entry1->data.float_range_data.max >= entry2->data.float_data) {
1550             result = gst_props_entry_copy (entry2);
1551           }
1552         default:
1553           break;
1554       }
1555       break;
1556     case GST_PROPS_FOURCC_TYPE:
1557       switch (entry2->propstype) {
1558         /* b   <--->   a */
1559         case GST_PROPS_FOURCC_TYPE:
1560           if (entry1->data.fourcc_data == entry2->data.fourcc_data)
1561             result = gst_props_entry_copy (entry1);
1562         default:
1563           break;
1564       }
1565       break;
1566     case GST_PROPS_INT_TYPE:
1567       switch (entry2->propstype) {
1568         /* b   <--->   a */
1569         case GST_PROPS_INT_TYPE:
1570           if (entry1->data.int_data == entry2->data.int_data)
1571             result = gst_props_entry_copy (entry1);
1572         default:
1573           break;
1574       }
1575       break;
1576     case GST_PROPS_FLOAT_TYPE:
1577       switch (entry2->propstype) {
1578         /* b   <--->   a */
1579         case GST_PROPS_FLOAT_TYPE:
1580           if (entry1->data.float_data == entry2->data.float_data)
1581             result = gst_props_entry_copy (entry1);
1582         default:
1583           break;
1584       }
1585       break;
1586     case GST_PROPS_BOOL_TYPE:
1587       switch (entry2->propstype) {
1588         /* t   <--->   t */
1589         case GST_PROPS_BOOL_TYPE:
1590           if (entry1->data.bool_data == entry2->data.bool_data)
1591             result = gst_props_entry_copy (entry1);
1592         default:
1593           break;
1594       }
1595     case GST_PROPS_STRING_TYPE:
1596       switch (entry2->propstype) {
1597         /* t   <--->   t */
1598         case GST_PROPS_STRING_TYPE:
1599           if (!strcmp (entry1->data.string_data.string, entry2->data.string_data.string))
1600             result = gst_props_entry_copy (entry1);
1601         default:
1602           break;
1603       }
1604     default:
1605       break;
1606   }
1607
1608   return result;
1609 }
1610
1611 /**
1612  * gst_props_intersect:
1613  * @props1: a property
1614  * @props2: another property
1615  *
1616  * Calculates the intersection bewteen two GstProps.
1617  *
1618  * Returns: a GstProps with the intersection or NULL if the 
1619  * intersection is empty.
1620  */
1621 GstProps*
1622 gst_props_intersect (GstProps *props1, GstProps *props2)
1623 {
1624   GList *props1list;
1625   GList *props2list;
1626   GstProps *intersection;
1627   GList *leftovers;
1628   GstPropsEntry *iprops = NULL;
1629
1630   intersection = gst_props_empty_new ();
1631   intersection->fixed = TRUE;
1632
1633   g_return_val_if_fail (props1 != NULL, NULL);
1634   g_return_val_if_fail (props2 != NULL, NULL);
1635         
1636   props1list = props1->properties;
1637   props2list = props2->properties;
1638
1639   while (props1list && props2list) {
1640     GstPropsEntry *entry1;
1641     GstPropsEntry *entry2;
1642
1643     entry1 = (GstPropsEntry *)props1list->data;
1644     entry2 = (GstPropsEntry *)props2list->data;
1645
1646     while (entry1->propid < entry2->propid) {
1647       GstPropsEntry *toadd;
1648
1649       GST_DEBUG (GST_CAT_PROPERTIES,"source is more specific in \"%s\"", g_quark_to_string (entry1->propid));
1650
1651       toadd = gst_props_entry_copy (entry1);
1652       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1653         intersection->fixed = FALSE;
1654
1655       intersection->properties = g_list_prepend (intersection->properties, toadd);
1656
1657       props1list = g_list_next (props1list);
1658       if (props1list) 
1659         entry1 = (GstPropsEntry *)props1list->data;
1660       else 
1661         goto end;
1662     }
1663     while (entry1->propid > entry2->propid) {
1664       GstPropsEntry *toadd;
1665
1666       toadd = gst_props_entry_copy (entry2);
1667       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1668         intersection->fixed = FALSE;
1669
1670       intersection->properties = g_list_prepend (intersection->properties, toadd);
1671
1672       props2list = g_list_next (props2list);
1673       if (props2list)
1674         entry2 = (GstPropsEntry *)props2list->data;
1675       else 
1676         goto end;
1677     }
1678     /* at this point we are talking about the same property */
1679     iprops = gst_props_entry_intersect (entry1, entry2);
1680
1681     if (iprops) {
1682       if (GST_PROPS_ENTRY_IS_VARIABLE (iprops))
1683         intersection->fixed = FALSE;
1684       intersection->properties = g_list_prepend (intersection->properties, iprops);
1685     }
1686     else {
1687       gst_props_unref (intersection);
1688       return NULL;
1689     }
1690
1691     props1list = g_list_next (props1list);
1692     props2list = g_list_next (props2list);
1693   }
1694
1695 end:
1696   /* at this point one of the lists could contain leftover properties */
1697   if (props1list)
1698     leftovers = props1list;
1699   else if (props2list)
1700     leftovers = props2list;
1701   else 
1702     leftovers = NULL;
1703
1704   while (leftovers) {
1705     GstPropsEntry *entry;
1706
1707     entry = (GstPropsEntry *) leftovers->data;
1708     if (GST_PROPS_ENTRY_IS_VARIABLE (entry))
1709       intersection->fixed = FALSE;
1710     intersection->properties = g_list_prepend (intersection->properties, gst_props_entry_copy (entry));
1711
1712     leftovers = g_list_next (leftovers);
1713   }
1714
1715   intersection->properties = g_list_reverse (intersection->properties);
1716
1717   return intersection;
1718 }
1719
1720 /**
1721  * gst_props_normalize:
1722  * @props: a property
1723  *
1724  * Unrolls all lists in the given GstProps. This is usefull if you
1725  * want to loop over the props.
1726  *
1727  * Returns: A GList with the unrolled props entries.
1728  */
1729 GList*
1730 gst_props_normalize (GstProps *props)
1731 {
1732   GList *entries;
1733   GList *result = NULL;
1734
1735   if (!props) 
1736     return NULL;
1737
1738   entries = props->properties;
1739
1740   while (entries) {
1741     GstPropsEntry *entry = (GstPropsEntry *) entries->data;
1742
1743     if (entry->propstype == GST_PROPS_LIST_TYPE) {
1744       GList *list_entries = entry->data.list_data.entries;
1745
1746       while (list_entries) {
1747         GstPropsEntry *list_entry = (GstPropsEntry *) list_entries->data;
1748         GstPropsEntry *new_entry;
1749         GstProps *newprops;
1750         GList *lentry;
1751
1752         newprops = gst_props_empty_new ();
1753         newprops->properties = gst_props_list_copy (props->properties);
1754         lentry = g_list_find_custom (newprops->properties, GINT_TO_POINTER (list_entry->propid), props_find_func);
1755         if (lentry) {
1756           GList *new_list = NULL;
1757
1758           new_entry = (GstPropsEntry *) lentry->data;
1759           memcpy (new_entry, list_entry, sizeof (GstPropsEntry));
1760
1761           new_list = gst_props_normalize (newprops);
1762           result = g_list_concat (new_list, result);
1763         }
1764         else {
1765           result = g_list_append (result, newprops);
1766         }
1767         
1768         list_entries = g_list_next (list_entries);      
1769       }
1770       /* we break out of the loop because the other lists are
1771        * unrolled in the recursive call */
1772       break;
1773     }
1774     entries = g_list_next (entries);
1775   }
1776   if (!result) {
1777     result = g_list_prepend (result, props);
1778   }
1779   else {
1780     result = g_list_reverse (result);
1781     gst_props_unref (props);
1782   }
1783   return result;
1784 }
1785
1786 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
1787 static xmlNodePtr
1788 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
1789 {
1790   xmlNodePtr subtree;
1791   gchar *str;
1792
1793   switch (entry->propstype) {
1794     case GST_PROPS_INT_TYPE: 
1795       subtree = xmlNewChild (parent, NULL, "int", NULL);
1796       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1797       str = g_strdup_printf ("%d", entry->data.int_data);
1798       xmlNewProp (subtree, "value", str);
1799       g_free(str);
1800       break;
1801     case GST_PROPS_INT_RANGE_TYPE: 
1802       subtree = xmlNewChild (parent, NULL, "range", NULL);
1803       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1804       str = g_strdup_printf ("%d", entry->data.int_range_data.min);
1805       xmlNewProp (subtree, "min", str);
1806       g_free(str);
1807       str = g_strdup_printf ("%d", entry->data.int_range_data.max);
1808       xmlNewProp (subtree, "max", str);
1809       g_free(str);
1810       break;
1811     case GST_PROPS_FLOAT_TYPE: 
1812       subtree = xmlNewChild (parent, NULL, "float", NULL);
1813       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1814       str = g_strdup_printf ("%f", entry->data.float_data);
1815       xmlNewProp (subtree, "value", str);
1816       g_free(str);
1817       break;
1818     case GST_PROPS_FLOAT_RANGE_TYPE: 
1819       subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
1820       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1821       str = g_strdup_printf ("%f", entry->data.float_range_data.min);
1822       xmlNewProp (subtree, "min", str);
1823       g_free(str);
1824       str = g_strdup_printf ("%f", entry->data.float_range_data.max);
1825       xmlNewProp (subtree, "max", str);
1826       g_free(str);
1827       break;
1828     case GST_PROPS_FOURCC_TYPE: 
1829       str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
1830       xmlAddChild (parent, xmlNewComment (str));
1831       g_free(str);
1832       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
1833       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1834       str = g_strdup_printf ("%08x", entry->data.fourcc_data);
1835       xmlNewProp (subtree, "hexvalue", str);
1836       g_free(str);
1837       break;
1838     case GST_PROPS_BOOL_TYPE: 
1839       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
1840       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1841       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
1842       break;
1843     case GST_PROPS_STRING_TYPE: 
1844       subtree = xmlNewChild (parent, NULL, "string", NULL);
1845       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1846       xmlNewProp (subtree, "value", entry->data.string_data.string);
1847       break;
1848     default:
1849       g_warning ("trying to save unknown property type %d", entry->propstype);
1850       break;
1851   }
1852
1853   return parent;
1854 }
1855
1856 /**
1857  * gst_props_save_thyself:
1858  * @props: a property to save
1859  * @parent: the parent XML tree
1860  *
1861  * Saves the property into an XML representation.
1862  *
1863  * Returns: the new XML tree
1864  */
1865 xmlNodePtr
1866 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
1867 {
1868   GList *proplist;
1869   xmlNodePtr subtree;
1870
1871   g_return_val_if_fail (props != NULL, NULL);
1872
1873   proplist = props->properties;
1874
1875   while (proplist) {
1876     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1877
1878     switch (entry->propstype) {
1879       case GST_PROPS_LIST_TYPE: 
1880         subtree = xmlNewChild (parent, NULL, "list", NULL);
1881         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1882         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
1883         break;
1884       default:
1885         gst_props_save_thyself_func (entry, parent);
1886     }
1887
1888     proplist = g_list_next (proplist);
1889   }
1890   
1891   return parent;
1892 }
1893
1894 static GstPropsEntry*
1895 gst_props_load_thyself_func (xmlNodePtr field)
1896 {
1897   GstPropsEntry *entry;
1898   gchar *prop;
1899
1900   entry = gst_props_alloc_entry ();
1901
1902   if (!strcmp(field->name, "int")) {
1903     entry->propstype = GST_PROPS_INT_TYPE;
1904     prop = xmlGetProp(field, "name");
1905     entry->propid = g_quark_from_string (prop);
1906     g_free (prop);
1907     prop = xmlGetProp(field, "value");
1908     sscanf (prop, "%d", &entry->data.int_data);
1909     g_free (prop);
1910   }
1911   else if (!strcmp(field->name, "range")) {
1912     entry->propstype = GST_PROPS_INT_RANGE_TYPE;
1913     prop = xmlGetProp(field, "name");
1914     entry->propid = g_quark_from_string (prop);
1915     g_free (prop);
1916     prop = xmlGetProp (field, "min");
1917     sscanf (prop, "%d", &entry->data.int_range_data.min);
1918     g_free (prop);
1919     prop = xmlGetProp (field, "max");
1920     sscanf (prop, "%d", &entry->data.int_range_data.max);
1921     g_free (prop);
1922   }
1923   else if (!strcmp(field->name, "float")) {
1924     entry->propstype = GST_PROPS_FLOAT_TYPE;
1925     prop = xmlGetProp(field, "name");
1926     entry->propid = g_quark_from_string (prop);
1927     g_free (prop);
1928     prop = xmlGetProp(field, "value");
1929     sscanf (prop, "%f", &entry->data.float_data);
1930     g_free (prop);
1931   }
1932   else if (!strcmp(field->name, "floatrange")) {
1933     entry->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
1934     prop = xmlGetProp(field, "name");
1935     entry->propid = g_quark_from_string (prop);
1936     g_free (prop);
1937     prop = xmlGetProp (field, "min");
1938     sscanf (prop, "%f", &entry->data.float_range_data.min);
1939     g_free (prop);
1940     prop = xmlGetProp (field, "max");
1941     sscanf (prop, "%f", &entry->data.float_range_data.max);
1942     g_free (prop);
1943   }
1944   else if (!strcmp(field->name, "boolean")) {
1945     entry->propstype = GST_PROPS_BOOL_TYPE;
1946     prop = xmlGetProp(field, "name");
1947     entry->propid = g_quark_from_string (prop);
1948     g_free (prop);
1949     prop = xmlGetProp (field, "value");
1950     if (!strcmp (prop, "false")) entry->data.bool_data = 0;
1951     else entry->data.bool_data = 1;
1952     g_free (prop);
1953   }
1954   else if (!strcmp(field->name, "fourcc")) {
1955     entry->propstype = GST_PROPS_FOURCC_TYPE;
1956     prop = xmlGetProp(field, "name");
1957     entry->propid = g_quark_from_string (prop);
1958     g_free (prop);
1959     prop = xmlGetProp (field, "hexvalue");
1960     sscanf (prop, "%08x", &entry->data.fourcc_data);
1961     g_free (prop);
1962   }
1963   else if (!strcmp(field->name, "string")) {
1964     entry->propstype = GST_PROPS_STRING_TYPE;
1965     prop = xmlGetProp(field, "name");
1966     entry->propid = g_quark_from_string (prop);
1967     g_free (prop);
1968     entry->data.string_data.string = xmlGetProp (field, "value");
1969   }
1970   else {
1971     g_mutex_lock (_gst_props_entries_chunk_lock);
1972     g_mem_chunk_free (_gst_props_entries_chunk, entry);
1973     g_mutex_unlock (_gst_props_entries_chunk_lock);
1974     entry = NULL;
1975   }
1976
1977   return entry;
1978 }
1979
1980 /**
1981  * gst_props_load_thyself:
1982  * @parent: the XML tree to load from
1983  *
1984  * Creates a new property out of an XML tree.
1985  *
1986  * Returns: the new property
1987  */
1988 GstProps*
1989 gst_props_load_thyself (xmlNodePtr parent)
1990 {
1991   GstProps *props;
1992   xmlNodePtr field = parent->xmlChildrenNode;
1993   gchar *prop;
1994
1995   props = gst_props_empty_new ();
1996
1997   while (field) {
1998     if (!strcmp (field->name, "list")) {
1999       GstPropsEntry *entry;
2000       xmlNodePtr subfield = field->xmlChildrenNode;
2001
2002       entry = gst_props_alloc_entry ();
2003       prop = xmlGetProp (field, "name");
2004       entry->propid = g_quark_from_string (prop);
2005       g_free (prop);
2006       entry->propstype = GST_PROPS_LIST_TYPE;
2007       entry->data.list_data.entries = NULL;
2008
2009       while (subfield) {
2010         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
2011
2012         if (subentry)
2013           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
2014
2015         subfield = subfield->next;
2016       }
2017       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
2018       gst_props_add_entry (props, entry);
2019     }
2020     else {
2021       GstPropsEntry *entry;
2022
2023       entry = gst_props_load_thyself_func (field);
2024
2025       if (entry) 
2026         gst_props_add_entry (props, entry);
2027     }
2028     field = field->next;
2029   }
2030
2031   return props;
2032 }
2033 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */
2034