Landed the new improved capsnegotiation system.
[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 "gstpropsprivate.h"
29
30 static GMemChunk *_gst_props_entries_chunk;
31 static GMutex *_gst_props_entries_chunk_lock;
32
33 static GMemChunk *_gst_props_chunk;
34 static GMutex *_gst_props_chunk_lock;
35
36 static gboolean         gst_props_entry_check_compatibility     (GstPropsEntry *entry1, GstPropsEntry *entry2);
37 static GList*           gst_props_list_copy                     (GList *propslist);
38
39         
40 void 
41 _gst_props_initialize (void) 
42 {
43   _gst_props_entries_chunk = g_mem_chunk_new ("GstPropsEntries", 
44                   sizeof (GstPropsEntry), sizeof (GstPropsEntry) * 256, 
45                   G_ALLOC_AND_FREE);
46   _gst_props_entries_chunk_lock = g_mutex_new ();
47
48   _gst_props_chunk = g_mem_chunk_new ("GstProps", 
49                   sizeof (GstProps), sizeof (GstProps) * 256, 
50                   G_ALLOC_AND_FREE);
51   _gst_props_chunk_lock = g_mutex_new ();
52 }
53
54 static void
55 gst_props_debug_entry (GstPropsEntry *entry)
56 {
57   gchar *name = g_quark_to_string (entry->propid);
58
59   switch (entry->propstype) {
60     case GST_PROPS_INT_ID:
61       GST_DEBUG (GST_CAT_PROPERTIES, "%s: int %d\n", name, entry->data.int_data);
62       break;
63     case GST_PROPS_FLOAT_ID:
64       GST_DEBUG (GST_CAT_PROPERTIES, "%s: float %f\n", name, entry->data.float_data);
65       break;
66     case GST_PROPS_FOURCC_ID:
67       GST_DEBUG (GST_CAT_PROPERTIES, "%s: fourcc %4.4s\n", name, (gchar*)&entry->data.fourcc_data);
68       break;
69     case GST_PROPS_BOOL_ID:
70       GST_DEBUG (GST_CAT_PROPERTIES, "%s: bool %d\n", name, entry->data.bool_data);
71       break;
72     case GST_PROPS_STRING_ID:
73       GST_DEBUG (GST_CAT_PROPERTIES, "%s: string %s\n", name, entry->data.string_data.string);
74       break;
75     case GST_PROPS_INT_RANGE_ID:
76       GST_DEBUG (GST_CAT_PROPERTIES, "%s: int range %d-%d\n", name, entry->data.int_range_data.min,
77                       entry->data.int_range_data.max);
78       break;
79     case GST_PROPS_FLOAT_RANGE_ID:
80       GST_DEBUG (GST_CAT_PROPERTIES, "%s: float range %f-%f\n", name, entry->data.float_range_data.min,
81                       entry->data.float_range_data.max);
82       break;
83     case GST_PROPS_LIST_ID:
84       GST_DEBUG (GST_CAT_PROPERTIES, "[list]\n");
85       {
86         GList *entries = entry->data.list_data.entries;
87
88         while (entries) {
89           gst_props_debug_entry ((GstPropsEntry *)entries->data);
90           entries = g_list_next (entries);
91         }
92       }
93       break;
94     default:
95       g_warning ("unknown property type %d", entry->propstype);
96       break;
97   }
98 }
99
100 static gint 
101 props_compare_func (gconstpointer a,
102                     gconstpointer b) 
103 {
104   GstPropsEntry *entry1 = (GstPropsEntry *)a;
105   GstPropsEntry *entry2 = (GstPropsEntry *)b;
106
107   return (entry1->propid - entry2->propid);
108 }
109
110 static gint 
111 props_find_func (gconstpointer a,
112                  gconstpointer b) 
113 {
114   GstPropsEntry *entry2 = (GstPropsEntry *)a;
115   GQuark quark = (GQuark) GPOINTER_TO_INT (b);
116
117   return (quark - entry2->propid);
118 }
119
120 /* This is implemented as a huge macro because we cannot pass
121  * va_list variables by reference on some architectures.
122  */
123 #define GST_PROPS_ENTRY_FILL(entry, var_args)                                   \
124 G_STMT_START {                                                                  \
125   entry->propstype = va_arg (var_args, GstPropsId);                             \
126                                                                                 \
127   switch (entry->propstype) {                                                   \
128     case GST_PROPS_INT_ID:                                                      \
129       entry->data.int_data = va_arg (var_args, gint);                           \
130       break;                                                                    \
131     case GST_PROPS_INT_RANGE_ID:                                                \
132       entry->data.int_range_data.min = va_arg (var_args, gint);                 \
133       entry->data.int_range_data.max = va_arg (var_args, gint);                 \
134       break;                                                                    \
135     case GST_PROPS_FLOAT_ID:                                                    \
136       entry->data.float_data = va_arg (var_args, gdouble);                      \
137       break;                                                                    \
138     case GST_PROPS_FLOAT_RANGE_ID:                                              \
139       entry->data.float_range_data.min = va_arg (var_args, gdouble);            \
140       entry->data.float_range_data.max = va_arg (var_args, gdouble);            \
141       break;                                                                    \
142     case GST_PROPS_FOURCC_ID:                                                   \
143       entry->data.fourcc_data = va_arg (var_args, gulong);                      \
144       break;                                                                    \
145     case GST_PROPS_BOOL_ID:                                                     \
146       entry->data.bool_data = va_arg (var_args, gboolean);                      \
147       break;                                                                    \
148     case GST_PROPS_STRING_ID:                                                   \
149       entry->data.string_data.string = g_strdup (va_arg (var_args, gchar*));    \
150       break;                                                                    \
151     default:                                                                    \
152       break;                                                                    \
153   }                                                                             \
154 } G_STMT_END
155
156 static GstPropsEntry*
157 gst_props_alloc_entry (void)
158 {
159   GstPropsEntry *entry;
160
161   g_mutex_lock (_gst_props_entries_chunk_lock);
162   entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
163   g_mutex_unlock (_gst_props_entries_chunk_lock);
164
165   return entry;
166 }
167
168 static void
169 gst_props_entry_destroy (GstPropsEntry *entry)
170 {
171   switch (entry->propstype) {
172     case GST_PROPS_STRING_ID:           
173       g_free (entry->data.string_data.string);
174       break;                            
175     case GST_PROPS_LIST_ID:             
176     {
177       GList *entries = entry->data.list_data.entries;
178
179       while (entries) {
180         gst_props_entry_destroy ((GstPropsEntry *)entries->data);
181         entries = g_list_next (entries);
182       }
183       g_list_free (entry->data.list_data.entries);
184       break;
185     }
186     default:                    
187       break;            
188   }
189   g_mutex_lock (_gst_props_entries_chunk_lock);
190   g_mem_chunk_free (_gst_props_entries_chunk, entry);
191   g_mutex_unlock (_gst_props_entries_chunk_lock);
192 }
193
194 static GstProps*
195 gst_props_alloc (void)
196 {
197   GstProps *props;
198
199   g_mutex_lock (_gst_props_chunk_lock);
200   props = g_mem_chunk_alloc (_gst_props_chunk);
201   g_mutex_unlock (_gst_props_chunk_lock);
202
203   props->properties = NULL;
204   props->refcount = 1;
205   props->fixed = TRUE;
206
207   return props;
208 }
209
210 static void
211 gst_props_add_entry (GstProps *props, GstPropsEntry *entry)
212 {
213   g_return_if_fail (props);
214   g_return_if_fail (entry);
215
216   if (props->fixed && GST_PROPS_ENTRY_IS_VARIABLE (entry)) {
217     props->fixed = FALSE;
218   }
219   props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
220 }
221
222 /**
223  * gst_props_new:
224  * @firstname: the first property name
225  * @...: the property values 
226  *
227  * Create a new property from the given key/value pairs
228  *
229  * Returns: the new property
230  */
231 GstProps*
232 gst_props_new (const gchar *firstname, ...)
233 {
234   GstProps *props;
235   va_list var_args;
236   
237   va_start (var_args, firstname);
238
239   props = gst_props_newv (firstname, var_args);
240   
241   va_end (var_args);
242   
243   return props;
244
245
246
247 void
248 gst_props_debug (GstProps *props)
249 {
250   GList *propslist = props->properties;
251
252   while (propslist) { 
253     GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
254
255     gst_props_debug_entry (entry);
256     
257     propslist = g_list_next (propslist);
258   }
259 }
260
261 /**
262  * gst_props_merge_int_entries:
263  * @newentry: the new entry
264  * @oldentry: an old entry
265  *
266  * Tries to merge oldentry into newentry, if there is a simpler single entry which represents
267  *
268  * Assumes that the entries are either ints or int ranges.
269  *
270  * Returns: TRUE if the entries were merged, FALSE otherwise.
271  */
272 static gboolean
273 gst_props_merge_int_entries(GstPropsEntry * newentry, GstPropsEntry * oldentry)
274 {
275   gint new_min, new_max, old_min, old_max;
276   gboolean can_merge = FALSE;
277
278   if (newentry->propstype == GST_PROPS_INT_ID) {
279     new_min = newentry->data.int_data;
280     new_max = newentry->data.int_data;
281   } else {
282     new_min = newentry->data.int_range_data.min;
283     new_max = newentry->data.int_range_data.max;
284   }
285
286   if (oldentry->propstype == GST_PROPS_INT_ID) {
287     old_min = oldentry->data.int_data;
288     old_max = oldentry->data.int_data;
289   } else {
290     old_min = oldentry->data.int_range_data.min;
291     old_max = oldentry->data.int_range_data.max;
292   }
293
294   /* Put range which starts lower into (new_min, new_max) */
295   if (old_min < new_min) {
296     gint tmp;
297     tmp = old_min;
298     old_min = new_min;
299     new_min = tmp;
300     tmp = old_max;
301     old_max = new_max;
302     new_max = tmp;
303   }
304
305   /* new_min is min of either entry - second half of the following conditional */
306   /* is to avoid overflow problems. */
307   if (new_max >= old_min - 1 && old_min - 1 < old_min) {
308     /* ranges overlap, or are adjacent.  Pick biggest maximum. */
309     can_merge = TRUE;
310     if (old_max > new_max) new_max = old_max;
311   }
312
313   if (can_merge) {
314     if (new_min == new_max) {
315       newentry->propstype = GST_PROPS_INT_ID;
316       newentry->data.int_data = new_min;
317     } else {
318       newentry->propstype = GST_PROPS_INT_RANGE_ID;
319       newentry->data.int_range_data.min = new_min;
320       newentry->data.int_range_data.max = new_max;
321     }
322   }
323   return can_merge;
324 }
325
326 /**
327  * gst_props_add_to_int_list:
328  * @entries: the existing list of entries
329  * @entry: the new entry to add to the list
330  *
331  * Add an integer property to a list of properties, removing duplicates
332  * and merging ranges.
333  *
334  * Assumes that the existing list is in simplest form, contains
335  * only ints and int ranges, and that the new entry is an int or 
336  * an int range.
337  *
338  * Returns: a pointer to a list with the new entry added.
339  */
340 static GList *
341 gst_props_add_to_int_list (GList * entries, GstPropsEntry * newentry)
342 {
343   GList * i;
344
345   i = entries;
346   while (i) {
347     GstPropsEntry * oldentry = (GstPropsEntry *)(i->data);
348     gboolean merged = gst_props_merge_int_entries(newentry, oldentry);
349
350     if (merged) {
351       /* replace the existing one with the merged one */
352       g_mutex_lock (_gst_props_entries_chunk_lock);
353       g_mem_chunk_free (_gst_props_entries_chunk, oldentry);
354       g_mutex_unlock (_gst_props_entries_chunk_lock);
355       entries = g_list_remove_link (entries, i);
356       g_list_free_1 (i);
357
358       /* start again: it's possible that this change made an earlier entry */
359       /* mergeable, and the pointer is now invalid anyway. */
360       i = entries;
361     }
362
363     i = g_list_next (i);
364   }
365
366   return g_list_prepend (entries, newentry);
367 }
368
369 /**
370  * gst_props_newv:
371  * @firstname: the first property name
372  * @var_args: the property values
373  *
374  * Create a new property from the list of entries.
375  *
376  * Returns: the new property created from the list of entries
377  */
378 GstProps*
379 gst_props_newv (const gchar *firstname, va_list var_args)
380 {
381   GstProps *props;
382   gboolean inlist = FALSE;
383   const gchar *prop_name;
384   GstPropsEntry *list_entry = NULL;
385
386   typedef enum {
387       GST_PROPS_LIST_T_UNSET,
388       GST_PROPS_LIST_T_INTS,
389       GST_PROPS_LIST_T_FLOATS,
390       GST_PROPS_LIST_T_MISC,
391   } list_types;
392
393   /* type of the list */
394   list_types list_type = GST_PROPS_LIST_T_UNSET;
395   /* type of current item */
396   list_types entry_type = GST_PROPS_LIST_T_UNSET;
397
398   if (firstname == NULL)
399     return NULL;
400
401   props = gst_props_alloc ();
402
403   prop_name = firstname;
404
405   /* properties */
406   while (prop_name) {
407     GstPropsEntry *entry;
408    
409     entry = gst_props_alloc_entry ();
410     entry->propid = g_quark_from_string (prop_name);
411     GST_PROPS_ENTRY_FILL (entry, var_args);
412
413     switch (entry->propstype) {
414       case GST_PROPS_INT_ID:
415       case GST_PROPS_INT_RANGE_ID:
416         entry_type = GST_PROPS_LIST_T_INTS;
417         break;
418       case GST_PROPS_FLOAT_ID:
419       case GST_PROPS_FLOAT_RANGE_ID:
420         entry_type = GST_PROPS_LIST_T_FLOATS;
421         break;
422       case GST_PROPS_FOURCC_ID:
423       case GST_PROPS_BOOL_ID:
424       case GST_PROPS_STRING_ID:
425         entry_type = GST_PROPS_LIST_T_MISC;
426         break;
427       case GST_PROPS_LIST_ID:
428         g_return_val_if_fail (inlist == FALSE, NULL);
429         inlist = TRUE;
430         list_entry = entry;
431         list_type = GST_PROPS_LIST_T_UNSET;
432         list_entry->data.list_data.entries = NULL;
433         break;
434       case GST_PROPS_END_ID:
435         g_return_val_if_fail (inlist == TRUE, NULL);
436
437         /* if list was of size 1, replace the list by a the item it contains */
438         if (g_list_length(list_entry->data.list_data.entries) == 1) {
439           GstPropsEntry * subentry = (GstPropsEntry *)(list_entry->data.list_data.entries->data);
440           list_entry->propstype = subentry->propstype;
441           list_entry->data = subentry->data;
442           g_mutex_lock (_gst_props_entries_chunk_lock);
443           g_mem_chunk_free (_gst_props_entries_chunk, subentry);
444           g_mutex_unlock (_gst_props_entries_chunk_lock);
445         }
446         else {
447           list_entry->data.list_data.entries =
448                     g_list_reverse (list_entry->data.list_data.entries);
449         }
450
451         g_mutex_lock (_gst_props_entries_chunk_lock);
452         g_mem_chunk_free (_gst_props_entries_chunk, entry);
453         g_mutex_unlock (_gst_props_entries_chunk_lock);
454         inlist = FALSE;
455         list_entry = NULL;
456         prop_name = va_arg (var_args, gchar*);
457         continue;
458       default:
459         g_warning ("unknown property type found %d for '%s'\n", entry->propstype, prop_name);
460         g_mutex_lock (_gst_props_entries_chunk_lock);
461         g_mem_chunk_free (_gst_props_entries_chunk, entry);
462         g_mutex_unlock (_gst_props_entries_chunk_lock);
463         break;
464     }
465
466     if (inlist && (list_entry != entry)) {
467       if (list_type == GST_PROPS_LIST_T_UNSET) list_type = entry_type;
468       if (list_type != entry_type) {
469         g_warning ("property list contained incompatible entry types\n");
470       } else {
471         switch (list_type) {
472           case GST_PROPS_LIST_T_INTS:
473             list_entry->data.list_data.entries =
474                     gst_props_add_to_int_list (list_entry->data.list_data.entries, entry);
475             break;
476           default:
477             list_entry->data.list_data.entries =
478                     g_list_prepend (list_entry->data.list_data.entries, entry);
479             break;
480         }
481       }
482     }
483     else {
484       gst_props_add_entry (props, entry);
485     }
486     if (!inlist)
487       prop_name = va_arg (var_args, gchar*);
488   }
489
490   return props;
491 }
492
493 /**
494  * gst_props_set:
495  * @props: the props to modify
496  * @name: the name of the entry to modify
497  * @...: More property entries.
498  *
499  * Modifies the value of the given entry in the props struct.
500  *
501  * Returns: the new modified property structure.
502  */
503 GstProps*
504 gst_props_set (GstProps *props, const gchar *name, ...)
505 {
506   GQuark quark;
507   GList *lentry;
508   va_list var_args;
509   
510   quark = g_quark_from_string (name);
511
512   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
513
514   if (lentry) {
515     GstPropsEntry *entry;
516
517     entry = (GstPropsEntry *)lentry->data;
518
519     va_start (var_args, name);
520
521     GST_PROPS_ENTRY_FILL (entry, var_args);
522
523     va_end (var_args);
524   }
525   else {
526     g_print("gstprops: no property '%s' to change\n", name);
527   }
528
529   return props;
530 }
531
532 /**
533  * gst_props_unref:
534  * @props: the props to unref
535  *
536  * Decrease the refcount of the property structure, destroying
537  * the property if the refcount is 0.
538  */
539 void
540 gst_props_unref (GstProps *props)
541 {
542   if (props == NULL)
543     return;
544   
545   props->refcount--;
546
547   if (props->refcount == 0)
548     gst_props_destroy (props);
549 }
550
551 /**
552  * gst_props_ref:
553  * @props: the props to ref
554  *
555  * Increase the refcount of the property structure.
556  */
557 void
558 gst_props_ref (GstProps *props)
559 {
560   g_return_if_fail (props != NULL);
561   
562   props->refcount++;
563 }
564
565
566 /**
567  * gst_props_destroy:
568  * @props: the props to destroy
569  *
570  * Destroy the property, freeing all the memory that
571  * was allocated.
572  */
573 void
574 gst_props_destroy (GstProps *props)
575 {
576   GList *entries;
577
578   if (props == NULL)
579     return;
580   
581   entries = props->properties;
582
583   while (entries) {
584     gst_props_entry_destroy ((GstPropsEntry *)entries->data);
585     entries = g_list_next (entries);
586   }
587   g_list_free (props->properties);
588
589   g_mutex_lock (_gst_props_chunk_lock);
590   g_mem_chunk_free (_gst_props_chunk, props);
591   g_mutex_unlock (_gst_props_chunk_lock);
592 }
593
594 /* 
595  * copy entries 
596  */
597 static GstPropsEntry*
598 gst_props_entry_copy (GstPropsEntry *entry)
599 {
600   GstPropsEntry *newentry;
601
602   newentry = gst_props_alloc_entry ();
603   memcpy (newentry, entry, sizeof (GstPropsEntry));
604   if (entry->propstype == GST_PROPS_LIST_ID) {
605     newentry->data.list_data.entries = gst_props_list_copy (entry->data.list_data.entries);
606   }
607   else if (entry->propstype == GST_PROPS_STRING_ID) {
608     newentry->data.string_data.string = g_strdup (entry->data.string_data.string);
609   }
610
611   return newentry;
612 }
613
614 static GList*
615 gst_props_list_copy (GList *propslist)
616 {
617   GList *new = NULL;
618
619   while (propslist) {
620     GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
621
622     new = g_list_prepend (new, gst_props_entry_copy (entry));
623     
624     propslist = g_list_next (propslist);
625   }
626   new = g_list_reverse (new);
627
628   return new;
629 }
630
631 /**
632  * gst_props_copy:
633  * @props: the props to copy
634  *
635  * Copy the property structure.
636  *
637  * Returns: the new property that is a copy of the original
638  * one.
639  */
640 GstProps*
641 gst_props_copy (GstProps *props)
642 {
643   GstProps *new;
644   GList *properties;
645
646   if (props == NULL)
647     return NULL;
648
649   new = gst_props_alloc ();
650   new->properties = gst_props_list_copy (props->properties);
651   new->fixed = props->fixed;
652
653   return new;
654 }
655
656 /**
657  * gst_props_copy_on_write:
658  * @props: the props to copy on write
659  *
660  * Copy the property structure if the refcount is >1.
661  *
662  * Returns: A new props that can be safely written to.
663  */
664 GstProps*
665 gst_props_copy_on_write (GstProps *props)
666 {
667   GstProps *new = props;;
668
669   g_return_val_if_fail (props != NULL, NULL);
670
671   if (props->refcount > 1) {
672     new = gst_props_copy (props);
673     gst_props_unref (props);
674   }
675
676   return new;
677 }
678
679 static GstPropsEntry*
680 gst_props_get_entry_func (GstProps *props, const gchar *name)
681 {
682   GList *lentry;
683   GQuark quark;
684   
685   g_return_val_if_fail (props != NULL, NULL);
686   g_return_val_if_fail (name != NULL, NULL);
687
688   quark = g_quark_from_string (name);
689
690   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
691
692   if (lentry) {
693     GstPropsEntry *thisentry;
694     thisentry = (GstPropsEntry *)lentry->data;
695     return thisentry;
696   }
697   return NULL;
698 }
699
700 gboolean
701 gst_props_has_property (GstProps *props, const gchar *name)
702 {
703   return (gst_props_get_entry_func (props, name) != NULL);
704 }
705
706 /**
707  * gst_props_get_int:
708  * @props: the props to get the int value from
709  * @name: the name of the props entry to get.
710  *
711  * Get the named entry as an integer.
712  *
713  * Returns: the integer value of the named entry, 0 if not found.
714  */
715 gint
716 gst_props_get_int (GstProps *props, const gchar *name)
717 {
718   GstPropsEntry *thisentry;
719
720   thisentry = gst_props_get_entry_func (props, name);
721
722   if (thisentry) {
723     return thisentry->data.int_data;
724   }
725   else {
726     g_warning ("props: property %s not found", name);
727   }
728   return 0;
729 }
730
731 /**
732  * gst_props_get_float:
733  * @props: the props to get the float value from
734  * @name: the name of the props entry to get.
735  *
736  * Get the named entry as a float.
737  *
738  * Returns: the float value of the named entry, 0.0 if not found.
739  */
740 gfloat
741 gst_props_get_float (GstProps *props, const gchar *name)
742 {
743   GstPropsEntry *thisentry;
744
745   thisentry = gst_props_get_entry_func (props, name);
746
747   if (thisentry) {
748     return thisentry->data.float_data;
749   }
750   else {
751     g_warning ("props: property %s not found", name);
752   }
753   return 0.0F;
754 }
755
756 /**
757  * gst_props_get_fourcc_int:
758  * @props: the props to get the fourcc value from
759  * @name: the name of the props entry to get.
760  *
761  * Get the named entry as a gulong fourcc.
762  *
763  * Returns: the fourcc value of the named entry, 0 if not found.
764  */
765 gulong
766 gst_props_get_fourcc_int (GstProps *props, const gchar *name)
767 {
768   GstPropsEntry *thisentry;
769
770   thisentry = gst_props_get_entry_func (props, name);
771
772   if (thisentry) {
773     return thisentry->data.fourcc_data;
774   }
775   else {
776     g_warning ("props: property %s not found", name);
777   }
778   return 0;
779 }
780
781 /**
782  * gst_props_get_boolean:
783  * @props: the props to get the fourcc value from
784  * @name: the name of the props entry to get.
785  *
786  * Get the named entry as a boolean value.
787  *
788  * Returns: the boolean value of the named entry, 0 if not found.
789  */
790 gboolean
791 gst_props_get_boolean (GstProps *props, const gchar *name)
792 {
793   GstPropsEntry *thisentry;
794
795   thisentry = gst_props_get_entry_func (props, name);
796
797   if (thisentry) {
798     return thisentry->data.bool_data;
799   }
800   else {
801     g_warning ("props: property %s not found", name);
802   }
803   return 0;
804 }
805
806 /**
807  * gst_props_get_string:
808  * @props: the props to get the fourcc value from
809  * @name: the name of the props entry to get.
810  *
811  * Get the named entry as a string value.
812  *
813  * Returns: the string value of the named entry, NULL if not found.
814  */
815 const gchar*
816 gst_props_get_string (GstProps *props, const gchar *name)
817 {
818   GstPropsEntry *thisentry;
819
820   thisentry = gst_props_get_entry_func (props, name);
821
822   if (thisentry) {
823     return thisentry->data.string_data.string;
824   }
825   else {
826     g_warning ("props: property %s not found", name);
827   }
828   return NULL;
829 }
830
831 /**
832  * gst_props_merge:
833  * @props: the property to merge into
834  * @tomerge: the property to merge 
835  *
836  * Merge the properties of tomerge into props.
837  *
838  * Returns: the new merged property 
839  */
840 GstProps*
841 gst_props_merge (GstProps *props, GstProps *tomerge)
842 {
843   GList *merge_props;
844
845   g_return_val_if_fail (props != NULL, NULL);
846   g_return_val_if_fail (tomerge != NULL, NULL);
847
848   merge_props = tomerge->properties;
849
850   /* FIXME do proper merging here... */
851   while (merge_props) {
852     GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
853
854     gst_props_add_entry (props, entry);
855           
856     merge_props = g_list_next (merge_props);
857   }
858
859   return props;
860 }
861
862
863 /* entry2 is always a list, entry1 never is */
864 static gboolean
865 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
866 {
867   GList *entrylist = entry2->data.list_data.entries;
868   gboolean found = FALSE;
869
870   while (entrylist && !found) {
871     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
872
873     found |= gst_props_entry_check_compatibility (entry1, entry);
874
875     entrylist = g_list_next (entrylist);
876   }
877
878   return found;
879 }
880
881 static gboolean
882 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
883 {
884   GST_DEBUG (GST_CAT_PROPERTIES,"compare: %s %s\n", g_quark_to_string (entry1->propid),
885                              g_quark_to_string (entry2->propid));
886
887   if (entry2->propstype == GST_PROPS_LIST_ID && entry1->propstype != GST_PROPS_LIST_ID) {
888     return gst_props_entry_check_list_compatibility (entry1, entry2);
889   }
890
891   switch (entry1->propstype) {
892     case GST_PROPS_LIST_ID:
893     {
894       GList *entrylist = entry1->data.list_data.entries;
895       gboolean valid = TRUE;    /* innocent until proven guilty */
896
897       while (entrylist && valid) {
898         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
899
900         valid &= gst_props_entry_check_compatibility (entry, entry2);
901         
902         entrylist = g_list_next (entrylist);
903       }
904       
905       return valid;
906     }
907     case GST_PROPS_INT_RANGE_ID:
908       switch (entry2->propstype) {
909         /* a - b   <--->   a - c */
910         case GST_PROPS_INT_RANGE_ID:
911           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
912                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
913       }
914       break;
915     case GST_PROPS_FLOAT_RANGE_ID:
916       switch (entry2->propstype) {
917         /* a - b   <--->   a - c */
918         case GST_PROPS_FLOAT_RANGE_ID:
919           return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
920                   entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
921       }
922       break;
923     case GST_PROPS_FOURCC_ID:
924       switch (entry2->propstype) {
925         /* b   <--->   a */
926         case GST_PROPS_FOURCC_ID:
927           GST_DEBUG(GST_CAT_PROPERTIES,"\"%4.4s\" <--> \"%4.4s\" ?\n",
928                           &entry2->data.fourcc_data, &entry1->data.fourcc_data);
929           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
930       }
931       break;
932     case GST_PROPS_INT_ID:
933       switch (entry2->propstype) {
934         /* b   <--->   a - d */
935         case GST_PROPS_INT_RANGE_ID:
936           GST_DEBUG(GST_CAT_PROPERTIES,"%d <= %d <= %d ?\n",entry2->data.int_range_data.min,
937                     entry1->data.int_data,entry2->data.int_range_data.max);
938           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
939                   entry2->data.int_range_data.max >= entry1->data.int_data);
940         /* b   <--->   a */
941         case GST_PROPS_INT_ID:
942           GST_DEBUG(GST_CAT_PROPERTIES,"%d == %d ?\n",entry1->data.int_data,entry2->data.int_data);
943           return (entry2->data.int_data == entry1->data.int_data);
944       }
945       break;
946     case GST_PROPS_FLOAT_ID:
947       switch (entry2->propstype) {
948         /* b   <--->   a - d */
949         case GST_PROPS_FLOAT_RANGE_ID:
950           return (entry2->data.float_range_data.min <= entry1->data.float_data &&
951                   entry2->data.float_range_data.max >= entry1->data.float_data);
952         /* b   <--->   a */
953         case GST_PROPS_FLOAT_ID:
954           return (entry2->data.float_data == entry1->data.float_data);
955       }
956       break;
957     case GST_PROPS_BOOL_ID:
958       switch (entry2->propstype) {
959         /* t   <--->   t */
960         case GST_PROPS_BOOL_ID:
961           return (entry2->data.bool_data == entry1->data.bool_data);
962       }
963     case GST_PROPS_STRING_ID:
964       switch (entry2->propstype) {
965         /* t   <--->   t */
966         case GST_PROPS_STRING_ID:
967           return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
968       }
969   }
970
971   return FALSE;
972 }
973
974 /**
975  * gst_props_check_compatibility:
976  * @fromprops: a property
977  * @toprops: a property
978  *
979  * Checks whether two capabilities are compatible.
980  *
981  * Returns: TRUE if compatible, FALSE otherwise
982  */
983 gboolean
984 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
985 {
986   GList *sourcelist;
987   GList *sinklist;
988   gint missing = 0;
989   gint more = 0;
990   gboolean compatible = TRUE;
991
992   g_return_val_if_fail (fromprops != NULL, FALSE);
993   g_return_val_if_fail (toprops != NULL, FALSE);
994         
995   sourcelist = fromprops->properties;
996   sinklist   = toprops->properties;
997
998   while (sourcelist && sinklist && compatible) {
999     GstPropsEntry *entry1;
1000     GstPropsEntry *entry2;
1001
1002     entry1 = (GstPropsEntry *)sourcelist->data;
1003     entry2 = (GstPropsEntry *)sinklist->data;
1004
1005     while (entry1->propid < entry2->propid) {
1006       more++;
1007       sourcelist = g_list_next (sourcelist);
1008       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
1009       else goto end;
1010     }
1011     while (entry1->propid > entry2->propid) {
1012       missing++;
1013       sinklist = g_list_next (sinklist);
1014       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
1015       else goto end;
1016     }
1017
1018     if (!gst_props_entry_check_compatibility (entry1, entry2)) {
1019         compatible = FALSE; 
1020         GST_DEBUG (GST_CAT_PROPERTIES, "%s are not compatible: \n",
1021                    g_quark_to_string (entry1->propid));
1022     }
1023
1024     sourcelist = g_list_next (sourcelist);
1025     sinklist = g_list_next (sinklist);
1026   }
1027   if (sinklist && compatible) {
1028     GstPropsEntry *entry2;
1029     entry2 = (GstPropsEntry *)sinklist->data;
1030     missing++;
1031   }
1032 end:
1033
1034   if (missing)
1035     return FALSE;
1036
1037   return compatible;
1038 }
1039
1040 static GstPropsEntry*
1041 gst_props_entry_intersect (GstPropsEntry *entry1, GstPropsEntry *entry2)
1042 {
1043   GstPropsEntry *result = NULL;
1044
1045   /* try to move the ranges and lists first */
1046   switch (entry2->propstype) {
1047     case GST_PROPS_INT_RANGE_ID:
1048     case GST_PROPS_FLOAT_RANGE_ID:
1049     case GST_PROPS_LIST_ID:
1050     {
1051       GstPropsEntry *temp;
1052
1053       temp = entry1;
1054       entry1 = entry2;
1055       entry2 = temp;
1056     }
1057   }
1058
1059   switch (entry1->propstype) {
1060     case GST_PROPS_LIST_ID:
1061     {
1062       GList *entrylist = entry1->data.list_data.entries;
1063       GList *intersection = NULL;
1064
1065       while (entrylist) {
1066         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1067         GstPropsEntry *intersectentry;
1068
1069         intersectentry = gst_props_entry_intersect (entry2, entry);
1070
1071         if (intersectentry) {
1072           if (intersectentry->propstype == GST_PROPS_LIST_ID) {
1073             intersection = g_list_concat (intersection, intersectentry->data.list_data.entries);
1074             /* set the list to NULL because the entries are concatenated to the above
1075              * list and we don't want to free them */
1076             intersectentry->data.list_data.entries = NULL;
1077             gst_props_entry_destroy (intersectentry);
1078           }
1079           else {
1080             intersection = g_list_prepend (intersection, intersectentry);
1081           }
1082         }
1083         entrylist = g_list_next (entrylist);
1084       }
1085       if (intersection) {
1086         /* check if the list only contains 1 element, if so, we can just copy it */
1087         if (g_list_next (intersection) == NULL) {
1088           result = (GstPropsEntry *) (intersection->data); 
1089           g_list_free (intersection);
1090         }
1091         /* else we need to create a new entry to hold the list */
1092         else {
1093           result = gst_props_alloc_entry ();
1094           result->propid = entry1->propid;
1095           result->propstype = GST_PROPS_LIST_ID;
1096           result->data.list_data.entries = g_list_reverse (intersection);
1097         }
1098       }
1099       return result;
1100     }
1101     case GST_PROPS_INT_RANGE_ID:
1102       switch (entry2->propstype) {
1103         /* a - b   <--->   a - c */
1104         case GST_PROPS_INT_RANGE_ID:
1105         {
1106           gint lower = MAX (entry1->data.int_range_data.min, entry2->data.int_range_data.min);
1107           gint upper = MIN (entry1->data.int_range_data.max, entry2->data.int_range_data.max);
1108
1109           if (lower <= upper) {
1110             result = gst_props_alloc_entry ();
1111             result->propid = entry1->propid;
1112
1113             if (lower == upper) {
1114               result->propstype = GST_PROPS_INT_ID;
1115               result->data.int_data = lower;
1116             }
1117             else {
1118               result->propstype = GST_PROPS_INT_RANGE_ID;
1119               result->data.int_range_data.min = lower;
1120               result->data.int_range_data.max = upper;
1121             }
1122           }
1123           break;
1124         }
1125         case GST_PROPS_INT_ID:
1126           if (entry1->data.int_range_data.min <= entry2->data.int_data && 
1127               entry1->data.int_range_data.max >= entry2->data.int_data) {
1128             result = gst_props_entry_copy (entry2);
1129           }
1130       }
1131       break;
1132     case GST_PROPS_FLOAT_RANGE_ID:
1133       switch (entry2->propstype) {
1134         /* a - b   <--->   a - c */
1135         case GST_PROPS_FLOAT_RANGE_ID:
1136         {
1137           gfloat lower = MAX (entry1->data.float_range_data.min, entry2->data.float_range_data.min);
1138           gfloat upper = MIN (entry1->data.float_range_data.max, entry2->data.float_range_data.max);
1139
1140           if (lower <= upper) {
1141             result = gst_props_alloc_entry ();
1142             result->propid = entry1->propid;
1143
1144             if (lower == upper) {
1145               result->propstype = GST_PROPS_FLOAT_ID;
1146               result->data.float_data = lower;
1147             }
1148             else {
1149               result->propstype = GST_PROPS_FLOAT_RANGE_ID;
1150               result->data.float_range_data.min = lower;
1151               result->data.float_range_data.max = upper;
1152             }
1153           }
1154           break;
1155         }
1156         case GST_PROPS_FLOAT_ID:
1157           if (entry1->data.float_range_data.min <= entry2->data.float_data && 
1158               entry1->data.float_range_data.max >= entry2->data.float_data) {
1159             result = gst_props_entry_copy (entry2);
1160           }
1161       }
1162       break;
1163     case GST_PROPS_FOURCC_ID:
1164       switch (entry2->propstype) {
1165         /* b   <--->   a */
1166         case GST_PROPS_FOURCC_ID:
1167           if (entry1->data.fourcc_data == entry2->data.fourcc_data)
1168             result = gst_props_entry_copy (entry1);
1169       }
1170       break;
1171     case GST_PROPS_INT_ID:
1172       switch (entry2->propstype) {
1173         /* b   <--->   a */
1174         case GST_PROPS_INT_ID:
1175           if (entry1->data.int_data == entry2->data.int_data)
1176             result = gst_props_entry_copy (entry1);
1177       }
1178       break;
1179     case GST_PROPS_FLOAT_ID:
1180       switch (entry2->propstype) {
1181         /* b   <--->   a */
1182         case GST_PROPS_FLOAT_ID:
1183           if (entry1->data.float_data == entry2->data.float_data)
1184             result = gst_props_entry_copy (entry1);
1185       }
1186       break;
1187     case GST_PROPS_BOOL_ID:
1188       switch (entry2->propstype) {
1189         /* t   <--->   t */
1190         case GST_PROPS_BOOL_ID:
1191           if (entry1->data.bool_data == entry2->data.bool_data)
1192             result = gst_props_entry_copy (entry1);
1193       }
1194     case GST_PROPS_STRING_ID:
1195       switch (entry2->propstype) {
1196         /* t   <--->   t */
1197         case GST_PROPS_STRING_ID:
1198           if (!strcmp (entry1->data.string_data.string, entry2->data.string_data.string))
1199             result = gst_props_entry_copy (entry1);
1200       }
1201   }
1202
1203   return result;
1204 }
1205
1206 /**
1207  * gst_props_intersect:
1208  * @props1: a property
1209  * @props2: another property
1210  *
1211  * Calculates the intersection bewteen two GstProps.
1212  *
1213  * Returns: a GstProps with the intersection or NULL if the 
1214  * intersection is empty.
1215  */
1216 GstProps*
1217 gst_props_intersect (GstProps *props1, GstProps *props2)
1218 {
1219   GList *props1list;
1220   GList *props2list;
1221   GstProps *intersection;
1222   GList *leftovers;
1223   GstPropsEntry *iprops = NULL;
1224
1225   intersection = gst_props_alloc ();
1226   intersection->fixed = TRUE;
1227
1228   g_return_val_if_fail (props1 != NULL, NULL);
1229   g_return_val_if_fail (props2 != NULL, NULL);
1230         
1231   props1list = props1->properties;
1232   props2list = props2->properties;
1233
1234   while (props1list && props2list) {
1235     GstPropsEntry *entry1;
1236     GstPropsEntry *entry2;
1237
1238     entry1 = (GstPropsEntry *)props1list->data;
1239     entry2 = (GstPropsEntry *)props2list->data;
1240
1241     while (entry1->propid < entry2->propid) {
1242       GstPropsEntry *toadd;
1243
1244       GST_DEBUG (GST_CAT_PROPERTIES,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
1245
1246       toadd = gst_props_entry_copy (entry1);
1247       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1248         intersection->fixed = FALSE;
1249
1250       intersection->properties = g_list_prepend (intersection->properties, toadd);
1251
1252       props1list = g_list_next (props1list);
1253       if (props1list) 
1254         entry1 = (GstPropsEntry *)props1list->data;
1255       else 
1256         goto end;
1257     }
1258     while (entry1->propid > entry2->propid) {
1259       GstPropsEntry *toadd;
1260
1261       toadd = gst_props_entry_copy (entry2);
1262       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1263         intersection->fixed = FALSE;
1264
1265       intersection->properties = g_list_prepend (intersection->properties, toadd);
1266
1267       props2list = g_list_next (props2list);
1268       if (props2list)
1269         entry2 = (GstPropsEntry *)props2list->data;
1270       else 
1271         goto end;
1272     }
1273     /* at this point we are talking about the same property */
1274     iprops = gst_props_entry_intersect (entry1, entry2);
1275
1276     if (iprops) {
1277       if (GST_PROPS_ENTRY_IS_VARIABLE (iprops))
1278         intersection->fixed = FALSE;
1279       intersection->properties = g_list_prepend (intersection->properties, iprops);
1280     }
1281     else {
1282       gst_props_unref (intersection);
1283       return NULL;
1284     }
1285
1286     props1list = g_list_next (props1list);
1287     props2list = g_list_next (props2list);
1288   }
1289
1290 end:
1291   /* at this point one of the lists could contain leftover properties */
1292   if (props1list)
1293     leftovers = props1list;
1294   else if (props2list)
1295     leftovers = props2list;
1296   else 
1297     goto finish;
1298
1299   while (leftovers) {
1300     GstPropsEntry *entry;
1301
1302     entry = (GstPropsEntry *) leftovers->data;
1303     if (GST_PROPS_ENTRY_IS_VARIABLE (entry))
1304       intersection->fixed = FALSE;
1305     intersection->properties = g_list_prepend (intersection->properties, gst_props_entry_copy (entry));
1306
1307     leftovers = g_list_next (leftovers);
1308   }
1309
1310 finish:
1311   intersection->properties = g_list_reverse (intersection->properties);
1312
1313   return intersection;
1314 }
1315
1316 GList*
1317 gst_props_normalize (GstProps *props)
1318 {
1319   GList *entries;
1320   GList *result = NULL;
1321
1322   if (!props) 
1323     return NULL;
1324
1325   entries = props->properties;
1326
1327   while (entries) {
1328     GstPropsEntry *entry = (GstPropsEntry *) entries->data;
1329
1330     if (entry->propstype == GST_PROPS_LIST_ID) {
1331       GList *list_entries = entry->data.list_data.entries;
1332
1333       while (list_entries) {
1334         GstPropsEntry *list_entry = (GstPropsEntry *) list_entries->data;
1335         GstPropsEntry *new_entry;
1336         GstProps *newprops;
1337         GList *lentry;
1338
1339         newprops = gst_props_alloc ();
1340         newprops->properties = gst_props_list_copy (props->properties);
1341         lentry = g_list_find_custom (newprops->properties, GINT_TO_POINTER (list_entry->propid), props_find_func);
1342         if (lentry) {
1343           GList *new_list = NULL;
1344
1345           new_entry = (GstPropsEntry *) lentry->data;
1346           memcpy (new_entry, list_entry, sizeof (GstPropsEntry));
1347
1348           new_list = gst_props_normalize (newprops);
1349           result = g_list_concat (new_list, result);
1350         }
1351         else {
1352           result = g_list_append (result, newprops);
1353         }
1354         
1355         list_entries = g_list_next (list_entries);      
1356       }
1357       /* we break out of the loop because the other lists are
1358        * unrolled in the recursive call */
1359       break;
1360     }
1361     entries = g_list_next (entries);
1362   }
1363   if (!result) {
1364     result = g_list_prepend (result, props);
1365   }
1366   else {
1367     result = g_list_reverse (result);
1368     gst_props_unref (props);
1369   }
1370   return result;
1371 }
1372
1373 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
1374 static xmlNodePtr
1375 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
1376 {
1377   xmlNodePtr subtree;
1378   gchar *str;
1379
1380   switch (entry->propstype) {
1381     case GST_PROPS_INT_ID: 
1382       subtree = xmlNewChild (parent, NULL, "int", NULL);
1383       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1384       str = g_strdup_printf ("%d", entry->data.int_data);
1385       xmlNewProp (subtree, "value", str);
1386       g_free(str);
1387       break;
1388     case GST_PROPS_INT_RANGE_ID: 
1389       subtree = xmlNewChild (parent, NULL, "range", NULL);
1390       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1391       str = g_strdup_printf ("%d", entry->data.int_range_data.min);
1392       xmlNewProp (subtree, "min", str);
1393       g_free(str);
1394       str = g_strdup_printf ("%d", entry->data.int_range_data.max);
1395       xmlNewProp (subtree, "max", str);
1396       g_free(str);
1397       break;
1398     case GST_PROPS_FLOAT_ID: 
1399       subtree = xmlNewChild (parent, NULL, "float", NULL);
1400       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1401       str = g_strdup_printf ("%f", entry->data.float_data);
1402       xmlNewProp (subtree, "value", str);
1403       g_free(str);
1404       break;
1405     case GST_PROPS_FLOAT_RANGE_ID: 
1406       subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
1407       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1408       str = g_strdup_printf ("%f", entry->data.float_range_data.min);
1409       xmlNewProp (subtree, "min", str);
1410       g_free(str);
1411       str = g_strdup_printf ("%f", entry->data.float_range_data.max);
1412       xmlNewProp (subtree, "max", str);
1413       g_free(str);
1414       break;
1415     case GST_PROPS_FOURCC_ID: 
1416       str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
1417       xmlAddChild (parent, xmlNewComment (str));
1418       g_free(str);
1419       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
1420       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1421       str = g_strdup_printf ("%08x", entry->data.fourcc_data);
1422       xmlNewProp (subtree, "hexvalue", str);
1423       g_free(str);
1424       break;
1425     case GST_PROPS_BOOL_ID: 
1426       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
1427       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1428       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
1429       break;
1430     case GST_PROPS_STRING_ID: 
1431       subtree = xmlNewChild (parent, NULL, "string", NULL);
1432       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1433       xmlNewProp (subtree, "value", entry->data.string_data.string);
1434       break;
1435     default:
1436       g_warning ("trying to save unknown property type %d", entry->propstype);
1437       break;
1438   }
1439
1440   return parent;
1441 }
1442
1443 /**
1444  * gst_props_save_thyself:
1445  * @props: a property to save
1446  * @parent: the parent XML tree
1447  *
1448  * Saves the property into an XML representation.
1449  *
1450  * Returns: the new XML tree
1451  */
1452 xmlNodePtr
1453 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
1454 {
1455   GList *proplist;
1456   xmlNodePtr subtree;
1457
1458   g_return_val_if_fail (props != NULL, NULL);
1459
1460   proplist = props->properties;
1461
1462   while (proplist) {
1463     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1464
1465     switch (entry->propstype) {
1466       case GST_PROPS_LIST_ID: 
1467         subtree = xmlNewChild (parent, NULL, "list", NULL);
1468         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1469         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
1470         break;
1471       default:
1472         gst_props_save_thyself_func (entry, parent);
1473     }
1474
1475     proplist = g_list_next (proplist);
1476   }
1477   
1478   return parent;
1479 }
1480
1481 static GstPropsEntry*
1482 gst_props_load_thyself_func (xmlNodePtr field)
1483 {
1484   GstPropsEntry *entry;
1485   gchar *prop;
1486
1487   entry = gst_props_alloc_entry ();
1488
1489   if (!strcmp(field->name, "int")) {
1490     entry->propstype = GST_PROPS_INT_ID;
1491     prop = xmlGetProp(field, "name");
1492     entry->propid = g_quark_from_string (prop);
1493     g_free (prop);
1494     prop = xmlGetProp(field, "value");
1495     sscanf (prop, "%d", &entry->data.int_data);
1496     g_free (prop);
1497   }
1498   else if (!strcmp(field->name, "range")) {
1499     entry->propstype = GST_PROPS_INT_RANGE_ID;
1500     prop = xmlGetProp(field, "name");
1501     entry->propid = g_quark_from_string (prop);
1502     g_free (prop);
1503     prop = xmlGetProp (field, "min");
1504     sscanf (prop, "%d", &entry->data.int_range_data.min);
1505     g_free (prop);
1506     prop = xmlGetProp (field, "max");
1507     sscanf (prop, "%d", &entry->data.int_range_data.max);
1508     g_free (prop);
1509   }
1510   else if (!strcmp(field->name, "float")) {
1511     entry->propstype = GST_PROPS_FLOAT_ID;
1512     prop = xmlGetProp(field, "name");
1513     entry->propid = g_quark_from_string (prop);
1514     g_free (prop);
1515     prop = xmlGetProp(field, "value");
1516     sscanf (prop, "%f", &entry->data.float_data);
1517     g_free (prop);
1518   }
1519   else if (!strcmp(field->name, "floatrange")) {
1520     entry->propstype = GST_PROPS_FLOAT_RANGE_ID;
1521     prop = xmlGetProp(field, "name");
1522     entry->propid = g_quark_from_string (prop);
1523     g_free (prop);
1524     prop = xmlGetProp (field, "min");
1525     sscanf (prop, "%f", &entry->data.float_range_data.min);
1526     g_free (prop);
1527     prop = xmlGetProp (field, "max");
1528     sscanf (prop, "%f", &entry->data.float_range_data.max);
1529     g_free (prop);
1530   }
1531   else if (!strcmp(field->name, "boolean")) {
1532     entry->propstype = GST_PROPS_BOOL_ID;
1533     prop = xmlGetProp(field, "name");
1534     entry->propid = g_quark_from_string (prop);
1535     g_free (prop);
1536     prop = xmlGetProp (field, "value");
1537     if (!strcmp (prop, "false")) entry->data.bool_data = 0;
1538     else entry->data.bool_data = 1;
1539     g_free (prop);
1540   }
1541   else if (!strcmp(field->name, "fourcc")) {
1542     entry->propstype = GST_PROPS_FOURCC_ID;
1543     prop = xmlGetProp(field, "name");
1544     entry->propid = g_quark_from_string (prop);
1545     g_free (prop);
1546     prop = xmlGetProp (field, "hexvalue");
1547     sscanf (prop, "%08x", &entry->data.fourcc_data);
1548     g_free (prop);
1549   }
1550   else if (!strcmp(field->name, "string")) {
1551     entry->propstype = GST_PROPS_STRING_ID;
1552     prop = xmlGetProp(field, "name");
1553     entry->propid = g_quark_from_string (prop);
1554     g_free (prop);
1555     entry->data.string_data.string = xmlGetProp (field, "value");
1556   }
1557   else {
1558     g_mutex_lock (_gst_props_entries_chunk_lock);
1559     g_mem_chunk_free (_gst_props_entries_chunk, entry);
1560     g_mutex_unlock (_gst_props_entries_chunk_lock);
1561     entry = NULL;
1562   }
1563
1564   return entry;
1565 }
1566
1567 /**
1568  * gst_props_load_thyself:
1569  * @parent: the XML tree to load from
1570  *
1571  * Creates a new property out of an XML tree.
1572  *
1573  * Returns: the new property
1574  */
1575 GstProps*
1576 gst_props_load_thyself (xmlNodePtr parent)
1577 {
1578   GstProps *props;
1579   xmlNodePtr field = parent->xmlChildrenNode;
1580   gchar *prop;
1581
1582   props = gst_props_alloc ();
1583
1584   while (field) {
1585     if (!strcmp (field->name, "list")) {
1586       GstPropsEntry *entry;
1587       xmlNodePtr subfield = field->xmlChildrenNode;
1588
1589       entry = gst_props_alloc_entry ();
1590       prop = xmlGetProp (field, "name");
1591       entry->propid = g_quark_from_string (prop);
1592       g_free (prop);
1593       entry->propstype = GST_PROPS_LIST_ID;
1594       entry->data.list_data.entries = NULL;
1595
1596       while (subfield) {
1597         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
1598
1599         if (subentry)
1600           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
1601
1602         subfield = subfield->next;
1603       }
1604       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
1605       gst_props_add_entry (props, entry);
1606     }
1607     else {
1608       GstPropsEntry *entry;
1609
1610       entry = gst_props_load_thyself_func (field);
1611
1612       if (entry) 
1613         gst_props_add_entry (props, entry);
1614     }
1615     field = field->next;
1616   }
1617
1618   return props;
1619 }
1620 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */
1621