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