e12c3c5bd04ee191003079b1010b1513d5271e8e
[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                                                                                 \
197   *result = TRUE;                                                               \
198                                                                                 \
199   if (safe) {                                                                   \
200     GstPropsType propstype = va_arg (var_args, GstPropsType);                   \
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_warning ("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 static gboolean
1012 gst_props_getv (GstProps *props, gboolean safe, gchar *first_name, va_list var_args)
1013 {
1014   while (first_name) {
1015     const GstPropsEntry *entry = gst_props_get_entry (props, first_name);
1016     gboolean result;
1017
1018     if (!entry) return FALSE;
1019     GST_PROPS_ENTRY_READ (entry, var_args, FALSE, &result);
1020     if (!result) return FALSE;
1021
1022     first_name = va_arg (var_args, gchar *);
1023   }
1024   return TRUE;
1025 }
1026
1027 /**
1028  * gst_props_get:
1029  * @props: the props to query
1030  * @first_name: the first key
1031  * @...: a pointer to a datastructure that can hold the value.
1032  *
1033  * Gets the contents of the props into given key/value pairs.
1034  *
1035  * Returns: TRUE is the props entry could be fetched.
1036  */
1037 gboolean
1038 gst_props_get (GstProps *props, gchar *first_name, ...)
1039 {
1040   va_list var_args;
1041   gboolean ret;
1042
1043   va_start (var_args, first_name);
1044   ret = gst_props_getv (props, FALSE, first_name, var_args);
1045   va_end (var_args);
1046   
1047   return ret;
1048 }
1049
1050 /**
1051  * gst_props_get_safe:
1052  * @props: the props to query
1053  * @first_name: the first key
1054  * @...: a pointer to a datastructure that can hold the value.
1055  *
1056  * Gets the contents of the props into given key/value pairs.
1057  *
1058  * Returns: TRUE is the props entry could be fetched.
1059  */
1060 gboolean
1061 gst_props_get_safe (GstProps *props, gchar *first_name, ...)
1062 {
1063   va_list var_args;
1064   gboolean ret;
1065
1066   va_start (var_args, first_name);
1067   ret = gst_props_getv (props, TRUE, first_name, var_args);
1068   va_end (var_args);
1069   
1070   return ret;
1071 }
1072
1073 /**
1074  * gst_props_entry_get_int:
1075  * @entry: the props entry to query
1076  * @val: a pointer to a gint to hold the value.
1077  *
1078  * Get the contents of the entry into the given gint.
1079  *
1080  * Returns: TRUE is the value could be fetched. FALSE if the 
1081  * entry is not of given type.
1082  */
1083 gboolean
1084 gst_props_entry_get_int (const GstPropsEntry *entry, gint *val)
1085 {
1086   return gst_props_entry_get_safe (entry, GST_PROPS_INT_TYPE, val);
1087 }
1088
1089 /**
1090  * gst_props_entry_get_float:
1091  * @entry: the props entry to query
1092  * @val: a pointer to a gfloat to hold the value.
1093  *
1094  * Get the contents of the entry into the given gfloat.
1095  *
1096  * Returns: TRUE is the value could be fetched. FALSE if the 
1097  * entry is not of given type.
1098  */
1099 gboolean
1100 gst_props_entry_get_float (const GstPropsEntry *entry, gfloat *val)
1101 {
1102   return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_TYPE, val);
1103 }
1104
1105 /**
1106  * gst_props_entry_get_fourcc_int:
1107  * @entry: the props entry to query
1108  * @val: a pointer to a guint32 to hold the value.
1109  *
1110  * Get the contents of the entry into the given guint32.
1111  *
1112  * Returns: TRUE is the value could be fetched. FALSE if the 
1113  * entry is not of given type.
1114  */
1115 gboolean
1116 gst_props_entry_get_fourcc_int (const GstPropsEntry *entry, guint32 *val)
1117 {
1118   return gst_props_entry_get_safe (entry, GST_PROPS_FOURCC_TYPE, val);
1119 }
1120
1121 /**
1122  * gst_props_entry_get_boolean:
1123  * @entry: the props entry to query
1124  * @val: a pointer to a gboolean to hold the value.
1125  *
1126  * Get the contents of the entry into the given gboolean.
1127  *
1128  * Returns: TRUE is the value could be fetched. FALSE if the 
1129  * entry is not of given type.
1130  */
1131 gboolean
1132 gst_props_entry_get_boolean (const GstPropsEntry *entry, gboolean *val)
1133 {
1134   return gst_props_entry_get_safe (entry, GST_PROPS_BOOL_TYPE, val);
1135 }
1136
1137 /**
1138  * gst_props_entry_get_string:
1139  * @entry: the props entry to query
1140  * @val: a pointer to a gchar* to hold the value.
1141  *
1142  * Get the contents of the entry into the given gchar*.
1143  *
1144  * Returns: TRUE is the value could be fetched. FALSE if the 
1145  * entry is not of given type.
1146  */
1147 gboolean
1148 gst_props_entry_get_string (const GstPropsEntry *entry, const gchar **val)
1149 {
1150   return gst_props_entry_get_safe (entry, GST_PROPS_STRING_TYPE, val);
1151 }
1152
1153 /**
1154  * gst_props_entry_get_int_range:
1155  * @entry: the props entry to query
1156  * @min: a pointer to a gint to hold the minimun value.
1157  * @max: a pointer to a gint to hold the maximum value.
1158  *
1159  * Get the contents of the entry into the given gints.
1160  *
1161  * Returns: TRUE is the value could be fetched. FALSE if the 
1162  * entry is not of given type.
1163  */
1164 gboolean
1165 gst_props_entry_get_int_range (const GstPropsEntry *entry, gint *min, gint *max)
1166 {
1167   return gst_props_entry_get_safe (entry, GST_PROPS_INT_RANGE_TYPE, min, max);
1168 }
1169
1170 /**
1171  * gst_props_entry_get_float_range:
1172  * @entry: the props entry to query
1173  * @min: a pointer to a gfloat to hold the minimun value.
1174  * @max: a pointer to a gfloat to hold the maximum value.
1175  *
1176  * Get the contents of the entry into the given gfloats.
1177  *
1178  * Returns: TRUE is the value could be fetched. FALSE if the 
1179  * entry is not of given type.
1180  */
1181 gboolean
1182 gst_props_entry_get_float_range (const GstPropsEntry *entry, gfloat *min, gfloat *max)
1183 {
1184   return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_RANGE_TYPE, min, max);
1185 }
1186
1187 /**
1188  * gst_props_entry_get_list:
1189  * @entry: the props entry to query
1190  * @val: a pointer to a GList to hold the value.
1191  *
1192  * Get the contents of the entry into the given GList.
1193  *
1194  * Returns: TRUE is the value could be fetched. FALSE if the 
1195  * entry is not of given type.
1196  */
1197 gboolean
1198 gst_props_entry_get_list (const GstPropsEntry *entry, const GList **val)
1199 {
1200   return gst_props_entry_get_safe (entry, GST_PROPS_LIST_TYPE, val);
1201 }
1202
1203 /**
1204  * gst_props_merge:
1205  * @props: the property to merge into
1206  * @tomerge: the property to merge 
1207  *
1208  * Merge the properties of tomerge into props.
1209  *
1210  * Returns: the new merged property 
1211  */
1212 GstProps*
1213 gst_props_merge (GstProps *props, GstProps *tomerge)
1214 {
1215   GList *merge_props;
1216
1217   g_return_val_if_fail (props != NULL, NULL);
1218   g_return_val_if_fail (tomerge != NULL, NULL);
1219
1220   merge_props = tomerge->properties;
1221
1222   /* FIXME do proper merging here... */
1223   while (merge_props) {
1224     GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
1225
1226     gst_props_add_entry (props, entry);
1227           
1228     merge_props = g_list_next (merge_props);
1229   }
1230
1231   return props;
1232 }
1233
1234
1235 /* entry2 is always a list, entry1 never is */
1236 static gboolean
1237 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
1238 {
1239   GList *entrylist = entry2->data.list_data.entries;
1240   gboolean found = FALSE;
1241
1242   while (entrylist && !found) {
1243     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1244
1245     found |= gst_props_entry_check_compatibility (entry1, entry);
1246
1247     entrylist = g_list_next (entrylist);
1248   }
1249
1250   return found;
1251 }
1252
1253 static gboolean
1254 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
1255 {
1256   GST_DEBUG (GST_CAT_PROPERTIES,"compare: %s %s", g_quark_to_string (entry1->propid),
1257                              g_quark_to_string (entry2->propid));
1258
1259   if (entry2->propstype == GST_PROPS_LIST_TYPE && entry1->propstype != GST_PROPS_LIST_TYPE) {
1260     return gst_props_entry_check_list_compatibility (entry1, entry2);
1261   }
1262
1263   switch (entry1->propstype) {
1264     case GST_PROPS_LIST_TYPE:
1265     {
1266       GList *entrylist = entry1->data.list_data.entries;
1267       gboolean valid = TRUE;    /* innocent until proven guilty */
1268
1269       while (entrylist && valid) {
1270         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1271
1272         valid &= gst_props_entry_check_compatibility (entry, entry2);
1273         
1274         entrylist = g_list_next (entrylist);
1275       }
1276       
1277       return valid;
1278     }
1279     case GST_PROPS_INT_RANGE_TYPE:
1280       switch (entry2->propstype) {
1281         /* a - b   <--->   a - c */
1282         case GST_PROPS_INT_RANGE_TYPE:
1283           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
1284                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
1285         default:
1286           break;
1287       }
1288       break;
1289     case GST_PROPS_FLOAT_RANGE_TYPE:
1290       switch (entry2->propstype) {
1291         /* a - b   <--->   a - c */
1292         case GST_PROPS_FLOAT_RANGE_TYPE:
1293           return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
1294                   entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
1295         default:
1296           break;
1297       }
1298       break;
1299     case GST_PROPS_FOURCC_TYPE:
1300       switch (entry2->propstype) {
1301         /* b   <--->   a */
1302         case GST_PROPS_FOURCC_TYPE:
1303           GST_DEBUG(GST_CAT_PROPERTIES,"\"%4.4s\" <--> \"%4.4s\" ?",
1304                           (char*) &entry2->data.fourcc_data, (char*) &entry1->data.fourcc_data);
1305           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
1306         default:
1307           break;
1308       }
1309       break;
1310     case GST_PROPS_INT_TYPE:
1311       switch (entry2->propstype) {
1312         /* b   <--->   a - d */
1313         case GST_PROPS_INT_RANGE_TYPE:
1314           GST_DEBUG(GST_CAT_PROPERTIES,"%d <= %d <= %d ?",entry2->data.int_range_data.min,
1315                     entry1->data.int_data,entry2->data.int_range_data.max);
1316           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
1317                   entry2->data.int_range_data.max >= entry1->data.int_data);
1318         /* b   <--->   a */
1319         case GST_PROPS_INT_TYPE:
1320           GST_DEBUG(GST_CAT_PROPERTIES,"%d == %d ?",entry1->data.int_data,entry2->data.int_data);
1321           return (entry2->data.int_data == entry1->data.int_data);
1322         default:
1323           break;
1324       }
1325       break;
1326     case GST_PROPS_FLOAT_TYPE:
1327       switch (entry2->propstype) {
1328         /* b   <--->   a - d */
1329         case GST_PROPS_FLOAT_RANGE_TYPE:
1330           return (entry2->data.float_range_data.min <= entry1->data.float_data &&
1331                   entry2->data.float_range_data.max >= entry1->data.float_data);
1332         /* b   <--->   a */
1333         case GST_PROPS_FLOAT_TYPE:
1334           return (entry2->data.float_data == entry1->data.float_data);
1335         default:
1336           break;
1337       }
1338       break;
1339     case GST_PROPS_BOOL_TYPE:
1340       switch (entry2->propstype) {
1341         /* t   <--->   t */
1342         case GST_PROPS_BOOL_TYPE:
1343           return (entry2->data.bool_data == entry1->data.bool_data);
1344         default:
1345           break;
1346       }
1347     case GST_PROPS_STRING_TYPE:
1348       switch (entry2->propstype) {
1349         /* t   <--->   t */
1350         case GST_PROPS_STRING_TYPE:
1351           GST_DEBUG(GST_CAT_PROPERTIES,"\"%s\" <--> \"%s\" ?",
1352                           entry2->data.string_data.string, entry1->data.string_data.string);
1353           return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
1354         default:
1355           break;
1356       }
1357     default:
1358       break;
1359   }
1360
1361   return FALSE;
1362 }
1363
1364 /**
1365  * gst_props_check_compatibility:
1366  * @fromprops: a property
1367  * @toprops: a property
1368  *
1369  * Checks whether two capabilities are compatible.
1370  *
1371  * Returns: TRUE if compatible, FALSE otherwise
1372  */
1373 gboolean
1374 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
1375 {
1376   GList *sourcelist;
1377   GList *sinklist;
1378   gint missing = 0;
1379   gint more = 0;
1380   gboolean compatible = TRUE;
1381
1382   g_return_val_if_fail (fromprops != NULL, FALSE);
1383   g_return_val_if_fail (toprops != NULL, FALSE);
1384         
1385   sourcelist = fromprops->properties;
1386   sinklist   = toprops->properties;
1387
1388   while (sourcelist && sinklist && compatible) {
1389     GstPropsEntry *entry1;
1390     GstPropsEntry *entry2;
1391
1392     entry1 = (GstPropsEntry *)sourcelist->data;
1393     entry2 = (GstPropsEntry *)sinklist->data;
1394
1395     while (entry1->propid < entry2->propid) {
1396       more++;
1397       sourcelist = g_list_next (sourcelist);
1398       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
1399       else goto end;
1400     }
1401     while (entry1->propid > entry2->propid) {
1402       missing++;
1403       sinklist = g_list_next (sinklist);
1404       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
1405       else goto end;
1406     }
1407
1408     if (!gst_props_entry_check_compatibility (entry1, entry2)) {
1409         compatible = FALSE; 
1410         GST_DEBUG (GST_CAT_PROPERTIES, "%s are not compatible: ",
1411                    g_quark_to_string (entry1->propid));
1412     }
1413
1414     sourcelist = g_list_next (sourcelist);
1415     sinklist = g_list_next (sinklist);
1416   }
1417   if (sinklist && compatible) {
1418     GstPropsEntry *entry2;
1419     entry2 = (GstPropsEntry *)sinklist->data;
1420     missing++;
1421   }
1422 end:
1423
1424   if (missing)
1425     return FALSE;
1426
1427   return compatible;
1428 }
1429
1430 static GstPropsEntry*
1431 gst_props_entry_intersect (GstPropsEntry *entry1, GstPropsEntry *entry2)
1432 {
1433   GstPropsEntry *result = NULL;
1434
1435   /* try to move the ranges and lists first */
1436   switch (entry2->propstype) {
1437     case GST_PROPS_INT_RANGE_TYPE:
1438     case GST_PROPS_FLOAT_RANGE_TYPE:
1439     case GST_PROPS_LIST_TYPE:
1440     {
1441       GstPropsEntry *temp;
1442
1443       temp = entry1;
1444       entry1 = entry2;
1445       entry2 = temp;
1446     }
1447     default:
1448       break;
1449   }
1450
1451   switch (entry1->propstype) {
1452     case GST_PROPS_LIST_TYPE:
1453     {
1454       GList *entrylist = entry1->data.list_data.entries;
1455       GList *intersection = NULL;
1456
1457       while (entrylist) {
1458         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1459         GstPropsEntry *intersectentry;
1460
1461         intersectentry = gst_props_entry_intersect (entry2, entry);
1462
1463         if (intersectentry) {
1464           if (intersectentry->propstype == GST_PROPS_LIST_TYPE) {
1465             intersection = g_list_concat (intersection, 
1466                             g_list_copy (intersectentry->data.list_data.entries));
1467             /* set the list to NULL because the entries are concatenated to the above
1468              * list and we don't want to free them */
1469             intersectentry->data.list_data.entries = NULL;
1470             gst_props_entry_destroy (intersectentry);
1471           }
1472           else {
1473             intersection = g_list_prepend (intersection, intersectentry);
1474           }
1475         }
1476         entrylist = g_list_next (entrylist);
1477       }
1478       if (intersection) {
1479         /* check if the list only contains 1 element, if so, we can just copy it */
1480         if (g_list_next (intersection) == NULL) {
1481           result = (GstPropsEntry *) (intersection->data); 
1482           g_list_free (intersection);
1483         }
1484         /* else we need to create a new entry to hold the list */
1485         else {
1486           result = gst_props_alloc_entry ();
1487           result->propid = entry1->propid;
1488           result->propstype = GST_PROPS_LIST_TYPE;
1489           result->data.list_data.entries = g_list_reverse (intersection);
1490         }
1491       }
1492       return result;
1493     }
1494     case GST_PROPS_INT_RANGE_TYPE:
1495       switch (entry2->propstype) {
1496         /* a - b   <--->   a - c */
1497         case GST_PROPS_INT_RANGE_TYPE:
1498         {
1499           gint lower = MAX (entry1->data.int_range_data.min, entry2->data.int_range_data.min);
1500           gint upper = MIN (entry1->data.int_range_data.max, entry2->data.int_range_data.max);
1501
1502           if (lower <= upper) {
1503             result = gst_props_alloc_entry ();
1504             result->propid = entry1->propid;
1505
1506             if (lower == upper) {
1507               result->propstype = GST_PROPS_INT_TYPE;
1508               result->data.int_data = lower;
1509             }
1510             else {
1511               result->propstype = GST_PROPS_INT_RANGE_TYPE;
1512               result->data.int_range_data.min = lower;
1513               result->data.int_range_data.max = upper;
1514             }
1515           }
1516           break;
1517         }
1518         case GST_PROPS_LIST_TYPE:
1519         {
1520           GList *entries = entry2->data.list_data.entries;
1521           result = gst_props_alloc_entry ();
1522           result->propid = entry1->propid;
1523           result->propstype = GST_PROPS_LIST_TYPE;
1524           result->data.list_data.entries = NULL;
1525           while (entries) {
1526             GstPropsEntry * this = (GstPropsEntry *)entries->data;
1527             if (this->propstype != GST_PROPS_INT_TYPE) {
1528               /* no hope, this list doesn't even contain ints! */
1529               gst_props_entry_destroy (result);
1530               result = NULL;
1531               break;
1532             }
1533             if (this->data.int_data >= entry1->data.int_range_data.min &&
1534                 this->data.int_data <= entry1->data.int_range_data.max) {
1535               result->data.list_data.entries = g_list_append (result->data.list_data.entries,
1536                                                               gst_props_entry_copy (this));
1537             }
1538             entries = g_list_next (entries);
1539           }
1540           break;
1541         }
1542         case GST_PROPS_INT_TYPE:
1543         {
1544           if (entry1->data.int_range_data.min <= entry2->data.int_data && 
1545               entry1->data.int_range_data.max >= entry2->data.int_data) {
1546             result = gst_props_entry_copy (entry2);
1547           }
1548           break;
1549         }
1550         default:
1551           break;
1552       }
1553       break;
1554     case GST_PROPS_FLOAT_RANGE_TYPE:
1555       switch (entry2->propstype) {
1556         /* a - b   <--->   a - c */
1557         case GST_PROPS_FLOAT_RANGE_TYPE:
1558         {
1559           gfloat lower = MAX (entry1->data.float_range_data.min, entry2->data.float_range_data.min);
1560           gfloat upper = MIN (entry1->data.float_range_data.max, entry2->data.float_range_data.max);
1561
1562           if (lower <= upper) {
1563             result = gst_props_alloc_entry ();
1564             result->propid = entry1->propid;
1565
1566             if (lower == upper) {
1567               result->propstype = GST_PROPS_FLOAT_TYPE;
1568               result->data.float_data = lower;
1569             }
1570             else {
1571               result->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
1572               result->data.float_range_data.min = lower;
1573               result->data.float_range_data.max = upper;
1574             }
1575           }
1576           break;
1577         }
1578         case GST_PROPS_FLOAT_TYPE:
1579           if (entry1->data.float_range_data.min <= entry2->data.float_data && 
1580               entry1->data.float_range_data.max >= entry2->data.float_data) {
1581             result = gst_props_entry_copy (entry2);
1582           }
1583         default:
1584           break;
1585       }
1586       break;
1587     case GST_PROPS_FOURCC_TYPE:
1588       switch (entry2->propstype) {
1589         /* b   <--->   a */
1590         case GST_PROPS_FOURCC_TYPE:
1591           if (entry1->data.fourcc_data == entry2->data.fourcc_data)
1592             result = gst_props_entry_copy (entry1);
1593         default:
1594           break;
1595       }
1596       break;
1597     case GST_PROPS_INT_TYPE:
1598       switch (entry2->propstype) {
1599         /* b   <--->   a */
1600         case GST_PROPS_INT_TYPE:
1601           if (entry1->data.int_data == entry2->data.int_data)
1602             result = gst_props_entry_copy (entry1);
1603         default:
1604           break;
1605       }
1606       break;
1607     case GST_PROPS_FLOAT_TYPE:
1608       switch (entry2->propstype) {
1609         /* b   <--->   a */
1610         case GST_PROPS_FLOAT_TYPE:
1611           if (entry1->data.float_data == entry2->data.float_data)
1612             result = gst_props_entry_copy (entry1);
1613         default:
1614           break;
1615       }
1616       break;
1617     case GST_PROPS_BOOL_TYPE:
1618       switch (entry2->propstype) {
1619         /* t   <--->   t */
1620         case GST_PROPS_BOOL_TYPE:
1621           if (entry1->data.bool_data == entry2->data.bool_data)
1622             result = gst_props_entry_copy (entry1);
1623         default:
1624           break;
1625       }
1626     case GST_PROPS_STRING_TYPE:
1627       switch (entry2->propstype) {
1628         /* t   <--->   t */
1629         case GST_PROPS_STRING_TYPE:
1630           if (!strcmp (entry1->data.string_data.string, entry2->data.string_data.string))
1631             result = gst_props_entry_copy (entry1);
1632         default:
1633           break;
1634       }
1635     default:
1636       break;
1637   }
1638
1639   return result;
1640 }
1641
1642 /**
1643  * gst_props_intersect:
1644  * @props1: a property
1645  * @props2: another property
1646  *
1647  * Calculates the intersection bewteen two GstProps.
1648  *
1649  * Returns: a GstProps with the intersection or NULL if the 
1650  * intersection is empty.
1651  */
1652 GstProps*
1653 gst_props_intersect (GstProps *props1, GstProps *props2)
1654 {
1655   GList *props1list;
1656   GList *props2list;
1657   GstProps *intersection;
1658   GList *leftovers;
1659   GstPropsEntry *iprops = NULL;
1660
1661   intersection = gst_props_empty_new ();
1662   intersection->fixed = TRUE;
1663
1664   g_return_val_if_fail (props1 != NULL, NULL);
1665   g_return_val_if_fail (props2 != NULL, NULL);
1666         
1667   props1list = props1->properties;
1668   props2list = props2->properties;
1669
1670   while (props1list && props2list) {
1671     GstPropsEntry *entry1;
1672     GstPropsEntry *entry2;
1673
1674     entry1 = (GstPropsEntry *)props1list->data;
1675     entry2 = (GstPropsEntry *)props2list->data;
1676
1677     while (entry1->propid < entry2->propid) {
1678       GstPropsEntry *toadd;
1679
1680       GST_DEBUG (GST_CAT_PROPERTIES,"source is more specific in \"%s\"", g_quark_to_string (entry1->propid));
1681
1682       toadd = gst_props_entry_copy (entry1);
1683       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1684         intersection->fixed = FALSE;
1685
1686       intersection->properties = g_list_prepend (intersection->properties, toadd);
1687
1688       props1list = g_list_next (props1list);
1689       if (props1list) 
1690         entry1 = (GstPropsEntry *)props1list->data;
1691       else 
1692         goto end;
1693     }
1694     while (entry1->propid > entry2->propid) {
1695       GstPropsEntry *toadd;
1696
1697       toadd = gst_props_entry_copy (entry2);
1698       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1699         intersection->fixed = FALSE;
1700
1701       intersection->properties = g_list_prepend (intersection->properties, toadd);
1702
1703       props2list = g_list_next (props2list);
1704       if (props2list)
1705         entry2 = (GstPropsEntry *)props2list->data;
1706       else 
1707         goto end;
1708     }
1709     /* at this point we are talking about the same property */
1710     iprops = gst_props_entry_intersect (entry1, entry2);
1711
1712     if (iprops) {
1713       if (GST_PROPS_ENTRY_IS_VARIABLE (iprops))
1714         intersection->fixed = FALSE;
1715       intersection->properties = g_list_prepend (intersection->properties, iprops);
1716     }
1717     else {
1718       gst_props_unref (intersection);
1719       return NULL;
1720     }
1721
1722     props1list = g_list_next (props1list);
1723     props2list = g_list_next (props2list);
1724   }
1725
1726 end:
1727   /* at this point one of the lists could contain leftover properties */
1728   if (props1list)
1729     leftovers = props1list;
1730   else if (props2list)
1731     leftovers = props2list;
1732   else 
1733     leftovers = NULL;
1734
1735   while (leftovers) {
1736     GstPropsEntry *entry;
1737
1738     entry = (GstPropsEntry *) leftovers->data;
1739     if (GST_PROPS_ENTRY_IS_VARIABLE (entry))
1740       intersection->fixed = FALSE;
1741     intersection->properties = g_list_prepend (intersection->properties, gst_props_entry_copy (entry));
1742
1743     leftovers = g_list_next (leftovers);
1744   }
1745
1746   intersection->properties = g_list_reverse (intersection->properties);
1747
1748   return intersection;
1749 }
1750
1751 /**
1752  * gst_props_normalize:
1753  * @props: a property
1754  *
1755  * Unrolls all lists in the given GstProps. This is usefull if you
1756  * want to loop over the props.
1757  *
1758  * Returns: A GList with the unrolled props entries.
1759  */
1760 GList*
1761 gst_props_normalize (GstProps *props)
1762 {
1763   GList *entries;
1764   GList *result = NULL;
1765
1766   if (!props) 
1767     return NULL;
1768
1769   entries = props->properties;
1770
1771   while (entries) {
1772     GstPropsEntry *entry = (GstPropsEntry *) entries->data;
1773
1774     if (entry->propstype == GST_PROPS_LIST_TYPE) {
1775       GList *list_entries = entry->data.list_data.entries;
1776
1777       while (list_entries) {
1778         GstPropsEntry *list_entry = (GstPropsEntry *) list_entries->data;
1779         GstPropsEntry *new_entry;
1780         GstProps *newprops;
1781         GList *lentry;
1782
1783         newprops = gst_props_empty_new ();
1784         newprops->properties = gst_props_list_copy (props->properties);
1785         lentry = g_list_find_custom (newprops->properties, GINT_TO_POINTER (list_entry->propid), props_find_func);
1786         if (lentry) {
1787           GList *new_list = NULL;
1788
1789           new_entry = (GstPropsEntry *) lentry->data;
1790           memcpy (new_entry, list_entry, sizeof (GstPropsEntry));
1791
1792           new_list = gst_props_normalize (newprops);
1793           result = g_list_concat (new_list, result);
1794         }
1795         else {
1796           result = g_list_append (result, newprops);
1797         }
1798         
1799         list_entries = g_list_next (list_entries);      
1800       }
1801       /* we break out of the loop because the other lists are
1802        * unrolled in the recursive call */
1803       break;
1804     }
1805     entries = g_list_next (entries);
1806   }
1807   if (!result) {
1808     result = g_list_prepend (result, props);
1809   }
1810   else {
1811     result = g_list_reverse (result);
1812     gst_props_unref (props);
1813   }
1814   return result;
1815 }
1816
1817 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
1818 static xmlNodePtr
1819 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
1820 {
1821   xmlNodePtr subtree;
1822   gchar *str;
1823
1824   switch (entry->propstype) {
1825     case GST_PROPS_INT_TYPE: 
1826       subtree = xmlNewChild (parent, NULL, "int", NULL);
1827       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1828       str = g_strdup_printf ("%d", entry->data.int_data);
1829       xmlNewProp (subtree, "value", str);
1830       g_free(str);
1831       break;
1832     case GST_PROPS_INT_RANGE_TYPE: 
1833       subtree = xmlNewChild (parent, NULL, "range", NULL);
1834       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1835       str = g_strdup_printf ("%d", entry->data.int_range_data.min);
1836       xmlNewProp (subtree, "min", str);
1837       g_free(str);
1838       str = g_strdup_printf ("%d", entry->data.int_range_data.max);
1839       xmlNewProp (subtree, "max", str);
1840       g_free(str);
1841       break;
1842     case GST_PROPS_FLOAT_TYPE: 
1843       subtree = xmlNewChild (parent, NULL, "float", NULL);
1844       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1845       str = g_strdup_printf ("%f", entry->data.float_data);
1846       xmlNewProp (subtree, "value", str);
1847       g_free(str);
1848       break;
1849     case GST_PROPS_FLOAT_RANGE_TYPE: 
1850       subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
1851       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1852       str = g_strdup_printf ("%f", entry->data.float_range_data.min);
1853       xmlNewProp (subtree, "min", str);
1854       g_free(str);
1855       str = g_strdup_printf ("%f", entry->data.float_range_data.max);
1856       xmlNewProp (subtree, "max", str);
1857       g_free(str);
1858       break;
1859     case GST_PROPS_FOURCC_TYPE: 
1860       str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
1861       xmlAddChild (parent, xmlNewComment (str));
1862       g_free(str);
1863       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
1864       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1865       str = g_strdup_printf ("%08x", entry->data.fourcc_data);
1866       xmlNewProp (subtree, "hexvalue", str);
1867       g_free(str);
1868       break;
1869     case GST_PROPS_BOOL_TYPE: 
1870       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
1871       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1872       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
1873       break;
1874     case GST_PROPS_STRING_TYPE: 
1875       subtree = xmlNewChild (parent, NULL, "string", NULL);
1876       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1877       xmlNewProp (subtree, "value", entry->data.string_data.string);
1878       break;
1879     default:
1880       g_warning ("trying to save unknown property type %d", entry->propstype);
1881       break;
1882   }
1883
1884   return parent;
1885 }
1886
1887 /**
1888  * gst_props_save_thyself:
1889  * @props: a property to save
1890  * @parent: the parent XML tree
1891  *
1892  * Saves the property into an XML representation.
1893  *
1894  * Returns: the new XML tree
1895  */
1896 xmlNodePtr
1897 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
1898 {
1899   GList *proplist;
1900   xmlNodePtr subtree;
1901
1902   g_return_val_if_fail (props != NULL, NULL);
1903
1904   proplist = props->properties;
1905
1906   while (proplist) {
1907     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1908
1909     switch (entry->propstype) {
1910       case GST_PROPS_LIST_TYPE: 
1911         subtree = xmlNewChild (parent, NULL, "list", NULL);
1912         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1913         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
1914         break;
1915       default:
1916         gst_props_save_thyself_func (entry, parent);
1917     }
1918
1919     proplist = g_list_next (proplist);
1920   }
1921   
1922   return parent;
1923 }
1924
1925 static GstPropsEntry*
1926 gst_props_load_thyself_func (xmlNodePtr field)
1927 {
1928   GstPropsEntry *entry;
1929   gchar *prop;
1930
1931   entry = gst_props_alloc_entry ();
1932
1933   if (!strcmp(field->name, "int")) {
1934     entry->propstype = GST_PROPS_INT_TYPE;
1935     prop = xmlGetProp(field, "name");
1936     entry->propid = g_quark_from_string (prop);
1937     g_free (prop);
1938     prop = xmlGetProp(field, "value");
1939     sscanf (prop, "%d", &entry->data.int_data);
1940     g_free (prop);
1941   }
1942   else if (!strcmp(field->name, "range")) {
1943     entry->propstype = GST_PROPS_INT_RANGE_TYPE;
1944     prop = xmlGetProp(field, "name");
1945     entry->propid = g_quark_from_string (prop);
1946     g_free (prop);
1947     prop = xmlGetProp (field, "min");
1948     sscanf (prop, "%d", &entry->data.int_range_data.min);
1949     g_free (prop);
1950     prop = xmlGetProp (field, "max");
1951     sscanf (prop, "%d", &entry->data.int_range_data.max);
1952     g_free (prop);
1953   }
1954   else if (!strcmp(field->name, "float")) {
1955     entry->propstype = GST_PROPS_FLOAT_TYPE;
1956     prop = xmlGetProp(field, "name");
1957     entry->propid = g_quark_from_string (prop);
1958     g_free (prop);
1959     prop = xmlGetProp(field, "value");
1960     sscanf (prop, "%f", &entry->data.float_data);
1961     g_free (prop);
1962   }
1963   else if (!strcmp(field->name, "floatrange")) {
1964     entry->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
1965     prop = xmlGetProp(field, "name");
1966     entry->propid = g_quark_from_string (prop);
1967     g_free (prop);
1968     prop = xmlGetProp (field, "min");
1969     sscanf (prop, "%f", &entry->data.float_range_data.min);
1970     g_free (prop);
1971     prop = xmlGetProp (field, "max");
1972     sscanf (prop, "%f", &entry->data.float_range_data.max);
1973     g_free (prop);
1974   }
1975   else if (!strcmp(field->name, "boolean")) {
1976     entry->propstype = GST_PROPS_BOOL_TYPE;
1977     prop = xmlGetProp(field, "name");
1978     entry->propid = g_quark_from_string (prop);
1979     g_free (prop);
1980     prop = xmlGetProp (field, "value");
1981     if (!strcmp (prop, "false")) entry->data.bool_data = 0;
1982     else entry->data.bool_data = 1;
1983     g_free (prop);
1984   }
1985   else if (!strcmp(field->name, "fourcc")) {
1986     entry->propstype = GST_PROPS_FOURCC_TYPE;
1987     prop = xmlGetProp(field, "name");
1988     entry->propid = g_quark_from_string (prop);
1989     g_free (prop);
1990     prop = xmlGetProp (field, "hexvalue");
1991     sscanf (prop, "%08x", &entry->data.fourcc_data);
1992     g_free (prop);
1993   }
1994   else if (!strcmp(field->name, "string")) {
1995     entry->propstype = GST_PROPS_STRING_TYPE;
1996     prop = xmlGetProp(field, "name");
1997     entry->propid = g_quark_from_string (prop);
1998     g_free (prop);
1999     entry->data.string_data.string = xmlGetProp (field, "value");
2000   }
2001   else {
2002     g_mutex_lock (_gst_props_entries_chunk_lock);
2003     g_mem_chunk_free (_gst_props_entries_chunk, entry);
2004     g_mutex_unlock (_gst_props_entries_chunk_lock);
2005     entry = NULL;
2006   }
2007
2008   return entry;
2009 }
2010
2011 /**
2012  * gst_props_load_thyself:
2013  * @parent: the XML tree to load from
2014  *
2015  * Creates a new property out of an XML tree.
2016  *
2017  * Returns: the new property
2018  */
2019 GstProps*
2020 gst_props_load_thyself (xmlNodePtr parent)
2021 {
2022   GstProps *props;
2023   xmlNodePtr field = parent->xmlChildrenNode;
2024   gchar *prop;
2025
2026   props = gst_props_empty_new ();
2027
2028   while (field) {
2029     if (!strcmp (field->name, "list")) {
2030       GstPropsEntry *entry;
2031       xmlNodePtr subfield = field->xmlChildrenNode;
2032
2033       entry = gst_props_alloc_entry ();
2034       prop = xmlGetProp (field, "name");
2035       entry->propid = g_quark_from_string (prop);
2036       g_free (prop);
2037       entry->propstype = GST_PROPS_LIST_TYPE;
2038       entry->data.list_data.entries = NULL;
2039
2040       while (subfield) {
2041         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
2042
2043         if (subentry)
2044           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
2045
2046         subfield = subfield->next;
2047       }
2048       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
2049       gst_props_add_entry (props, entry);
2050     }
2051     else {
2052       GstPropsEntry *entry;
2053
2054       entry = gst_props_load_thyself_func (field);
2055
2056       if (entry) 
2057         gst_props_add_entry (props, entry);
2058     }
2059     field = field->next;
2060   }
2061
2062   return props;
2063 }
2064 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */
2065