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