Removed the assert on an unknown propertytype and print usefull info.
[platform/upstream/gstreamer.git] / gst / gstprops.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@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 "gstprops.h"
27 #include "gstpropsprivate.h"
28
29 static GMemChunk *_gst_props_entries_chunk;
30 static GMutex *_gst_props_entries_chunk_lock;
31
32 static GMemChunk *_gst_props_chunk;
33 static GMutex *_gst_props_chunk_lock;
34
35 static gboolean         gst_props_entry_check_compatibility     (GstPropsEntry *entry1, GstPropsEntry *entry2);
36         
37 void 
38 _gst_props_initialize (void) 
39 {
40   _gst_props_entries_chunk = g_mem_chunk_new ("GstPropsEntries", 
41                   sizeof (GstPropsEntry), sizeof (GstPropsEntry) * 256, 
42                   G_ALLOC_AND_FREE);
43   _gst_props_entries_chunk_lock = g_mutex_new ();
44
45   _gst_props_chunk = g_mem_chunk_new ("GstProps", 
46                   sizeof (GstProps), sizeof (GstProps) * 256, 
47                   G_ALLOC_AND_FREE);
48   _gst_props_chunk_lock = g_mutex_new ();
49 }
50
51 static void
52 gst_props_debug_entry (GstPropsEntry *entry)
53 {
54   switch (entry->propstype) {
55     case GST_PROPS_INT_ID:
56       GST_DEBUG (0, "%d\n", entry->data.int_data);
57       break;
58     case GST_PROPS_FOURCC_ID:
59       GST_DEBUG (0, "%4.4s\n", (gchar*)&entry->data.fourcc_data);
60       break;
61     case GST_PROPS_BOOL_ID:
62       GST_DEBUG (0, "%d\n", entry->data.bool_data);
63       break;
64     case GST_PROPS_STRING_ID:
65       GST_DEBUG (0, "%s\n", entry->data.string_data.string);
66       break;
67     case GST_PROPS_INT_RANGE_ID:
68       GST_DEBUG (0, "%d-%d\n", entry->data.int_range_data.min,
69                       entry->data.int_range_data.max);
70       break;
71     default:
72       break;
73   }
74 }
75
76 static gint 
77 props_compare_func (gconstpointer a,
78                     gconstpointer b) 
79 {
80   GstPropsEntry *entry1 = (GstPropsEntry *)a;
81   GstPropsEntry *entry2 = (GstPropsEntry *)b;
82
83   return (entry1->propid - entry2->propid);
84 }
85
86 static gint 
87 props_find_func (gconstpointer a,
88                  gconstpointer b) 
89 {
90   GstPropsEntry *entry2 = (GstPropsEntry *)a;
91   GQuark entry1 = (GQuark) GPOINTER_TO_INT (b);
92
93   return (entry1 - entry2->propid);
94 }
95
96 static void
97 gst_props_entry_fill (GstPropsEntry *entry, va_list *var_args)
98 {
99   entry->propstype = va_arg (*var_args, GstPropsId);
100
101   switch (entry->propstype) {
102     case GST_PROPS_INT_ID:
103       entry->data.int_data = va_arg (*var_args, gint);
104       break;
105     case GST_PROPS_INT_RANGE_ID:
106       entry->data.int_range_data.min = va_arg (*var_args, gint);
107       entry->data.int_range_data.max = va_arg (*var_args, gint);
108       break;
109     case GST_PROPS_FLOAT_ID:
110       entry->data.float_data = va_arg (*var_args, gdouble);
111       break;
112     case GST_PROPS_FLOAT_RANGE_ID:
113       entry->data.float_range_data.min = va_arg (*var_args, gdouble);
114       entry->data.float_range_data.max = va_arg (*var_args, gdouble);
115       break;
116     case GST_PROPS_FOURCC_ID:
117       entry->data.fourcc_data = va_arg (*var_args, gulong);
118       break;
119     case GST_PROPS_BOOL_ID:
120       entry->data.bool_data = va_arg (*var_args, gboolean);
121       break;
122     case GST_PROPS_STRING_ID:
123       entry->data.string_data.string = g_strdup (va_arg (*var_args, gchar*));
124       break;
125     default:
126       break;
127   }
128 }
129
130 /**
131  * gst_props_new:
132  * @firstname: the first property name
133  * @...: the property values 
134  *
135  * Create a new property from the given key/value pairs
136  *
137  * Returns: the new property
138  */
139 GstProps*
140 gst_props_new (const gchar *firstname, ...)
141 {
142   GstProps *props;
143   va_list var_args;
144   
145   va_start (var_args, firstname);
146
147   props = gst_props_newv (firstname, var_args);
148   
149   va_end (var_args);
150   
151   return props;
152 }
153
154 /**
155  * gst_props_newv:
156  * @firstname: the first property name
157  * @var_args: the property values
158  *
159  * Create a new property from the list of entries.
160  *
161  * Returns: the new property created from the list of entries
162  */
163 GstProps*
164 gst_props_newv (const gchar *firstname, va_list var_args)
165 {
166   GstProps *props;
167   gboolean inlist = FALSE;
168   const gchar *prop_name;
169   GstPropsEntry *list_entry = NULL;
170
171   g_mutex_lock (_gst_props_chunk_lock);
172   props = g_mem_chunk_alloc (_gst_props_chunk);
173   g_mutex_unlock (_gst_props_chunk_lock);
174
175   props->properties = NULL;
176   props->refcount = 1;
177
178   prop_name = firstname;
179
180   // properties
181   while (prop_name) {
182     GstPropsEntry *entry;
183     
184     g_mutex_lock (_gst_props_entries_chunk_lock);
185     entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
186     g_mutex_unlock (_gst_props_entries_chunk_lock);
187
188     entry->propid = g_quark_from_string (prop_name);
189     gst_props_entry_fill (entry, &var_args);
190
191     switch (entry->propstype) {
192       case GST_PROPS_INT_ID:
193       case GST_PROPS_INT_RANGE_ID:
194       case GST_PROPS_FLOAT_ID:
195       case GST_PROPS_FLOAT_RANGE_ID:
196       case GST_PROPS_FOURCC_ID:
197       case GST_PROPS_BOOL_ID:
198       case GST_PROPS_STRING_ID:
199         break;
200       case GST_PROPS_LIST_ID:
201         g_return_val_if_fail (inlist == FALSE, NULL);
202         inlist = TRUE;
203         list_entry = entry;
204         list_entry->data.list_data.entries = NULL;
205         break;
206       case GST_PROPS_END_ID:
207         g_return_val_if_fail (inlist == TRUE, NULL);
208         inlist = FALSE;
209         list_entry = NULL;
210         prop_name = va_arg (var_args, gchar*);
211         continue;
212       default:
213         g_warning ("unknown property type found %d for '%s'\n", entry->propstype, prop_name);
214         g_mutex_lock (_gst_props_entries_chunk_lock);
215         g_mem_chunk_free (_gst_props_entries_chunk, entry);
216         g_mutex_unlock (_gst_props_entries_chunk_lock);
217         break;
218     }
219
220     if (inlist && (list_entry != entry)) {
221       list_entry->data.list_data.entries = g_list_prepend (list_entry->data.list_data.entries, entry);
222     }
223     else {
224       props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
225     }
226     if (!inlist)
227       prop_name = va_arg (var_args, gchar*);
228   }
229
230   return props;
231 }
232
233 /**
234  * gst_props_set:
235  * @props: the props to modify
236  * @name: the name of the entry to modify
237  * @...: More property entries.
238  *
239  * Modifies the value of the given entry in the props struct.
240  *
241  * Returns: the new modified property structure.
242  */
243 GstProps*
244 gst_props_set (GstProps *props, const gchar *name, ...)
245 {
246   GQuark quark;
247   GList *lentry;
248   va_list var_args;
249   
250   quark = g_quark_from_string (name);
251
252   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
253
254   if (lentry) {
255     GstPropsEntry *entry;
256
257     entry = (GstPropsEntry *)lentry->data;
258
259     va_start (var_args, name);
260
261     gst_props_entry_fill (entry, &var_args);
262
263     va_end (var_args);
264   }
265   else {
266     g_print("gstprops: no property '%s' to change\n", name);
267   }
268
269   return props;
270 }
271
272 /**
273  * gst_props_unref:
274  * @props: the props to unref
275  *
276  * Decrease the refcount of the property structure, destroying
277  * the property if the refcount is 0.
278  */
279 void
280 gst_props_unref (GstProps *props)
281 {
282   g_return_if_fail (props != NULL);
283   
284   props->refcount--;
285
286   if (props->refcount == 0)
287     gst_props_destroy (props);
288 }
289
290 /**
291  * gst_props_ref:
292  * @props: the props to ref
293  *
294  * Increase the refcount of the property structure.
295  */
296 void
297 gst_props_ref (GstProps *props)
298 {
299   g_return_if_fail (props != NULL);
300   
301   props->refcount++;
302 }
303
304 /**
305  * gst_props_destroy:
306  * @props: the props to destroy
307  *
308  * Destroy the property, freeing all the memory that
309  * was allocated.
310  */
311 void
312 gst_props_destroy (GstProps *props)
313 {
314   GList *entries;
315
316   g_return_if_fail (props != NULL);
317   
318   entries = props->properties;
319
320   while (entries) {
321     GstPropsEntry *entry = (GstPropsEntry *)entries->data;
322
323     // FIXME also free the lists
324     g_mutex_lock (_gst_props_entries_chunk_lock);
325     g_mem_chunk_free (_gst_props_entries_chunk, entry);
326     g_mutex_unlock (_gst_props_entries_chunk_lock);
327
328     entries = g_list_next (entries);
329   }
330
331   g_list_free (props->properties);
332 }
333
334 /**
335  * gst_props_copy:
336  * @props: the props to copy
337  *
338  * Copy the property structure.
339  *
340  * Returns: the new property that is a copy of the original
341  * one.
342  */
343 GstProps*
344 gst_props_copy (GstProps *props)
345 {
346   GstProps *new;
347   GList *properties;
348
349   g_return_val_if_fail (props != NULL, NULL);
350
351   g_mutex_lock (_gst_props_chunk_lock);
352   new = g_mem_chunk_alloc (_gst_props_chunk);
353   g_mutex_unlock (_gst_props_chunk_lock);
354
355   new->properties = NULL;
356
357   properties = props->properties;
358
359   while (properties) {
360     GstPropsEntry *entry = (GstPropsEntry *)properties->data;
361     GstPropsEntry *newentry;
362
363     g_mutex_lock (_gst_props_entries_chunk_lock);
364     newentry = g_mem_chunk_alloc (_gst_props_entries_chunk);
365     g_mutex_unlock (_gst_props_entries_chunk_lock);
366
367     // FIXME copy lists too
368     memcpy (newentry, entry, sizeof (GstPropsEntry));
369
370     new->properties = g_list_prepend (new->properties, newentry);
371     
372     properties = g_list_next (properties);
373   }
374   new->properties = g_list_reverse (new->properties);
375
376   return new;
377 }
378
379 /**
380  * gst_props_copy_on_write:
381  * @props: the props to copy on write
382  *
383  * Copy the property structure if the refcount is >1.
384  *
385  * Returns: A new props that can be safely written to.
386  */
387 GstProps*
388 gst_props_copy_on_write (GstProps *props)
389 {
390   GstProps *new = props;;
391
392   g_return_val_if_fail (props != NULL, NULL);
393
394   if (props->refcount > 1) {
395     new = gst_props_copy (props);
396     gst_props_unref (props);
397   }
398
399   return props;
400 }
401
402 /**
403  * gst_props_get_int:
404  * @props: the props to get the int value from
405  * @name: the name of the props entry to get.
406  *
407  * Get the named entry as an integer.
408  *
409  * Returns: the integer value of the named entry, 0 if not found.
410  */
411 gint
412 gst_props_get_int (GstProps *props, const gchar *name)
413 {
414   GList *lentry;
415   GQuark quark;
416   
417   g_return_val_if_fail (props != NULL, 0);
418   g_return_val_if_fail (name != NULL, 0);
419
420   quark = g_quark_from_string (name);
421
422   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
423
424   if (lentry) {
425     GstPropsEntry *thisentry;
426
427     thisentry = (GstPropsEntry *)lentry->data;
428
429     return thisentry->data.int_data;
430   }
431   
432   return 0;
433 }
434
435 /**
436  * gst_props_get_float:
437  * @props: the props to get the float value from
438  * @name: the name of the props entry to get.
439  *
440  * Get the named entry as a float.
441  *
442  * Returns: the float value of the named entry, 0.0 if not found.
443  */
444 gfloat
445 gst_props_get_float (GstProps *props, const gchar *name)
446 {
447   GList *lentry;
448   GQuark quark;
449   
450   quark = g_quark_from_string (name);
451
452   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
453
454   if (lentry) {
455     GstPropsEntry *thisentry;
456
457     thisentry = (GstPropsEntry *)lentry->data;
458
459     return thisentry->data.float_data;
460   }
461   
462   return 0.0F;
463 }
464
465 /**
466  * gst_props_get_fourcc_int:
467  * @props: the props to get the fourcc value from
468  * @name: the name of the props entry to get.
469  *
470  * Get the named entry as a gulong fourcc.
471  *
472  * Returns: the fourcc value of the named entry, 0 if not found.
473  */
474 gulong
475 gst_props_get_fourcc_int (GstProps *props, const gchar *name)
476 {
477   GList *lentry;
478   GQuark quark;
479   
480   g_return_val_if_fail (props != NULL, 0);
481   g_return_val_if_fail (name != NULL, 0);
482
483   quark = g_quark_from_string (name);
484
485   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
486
487   if (lentry) {
488     GstPropsEntry *thisentry;
489
490     thisentry = (GstPropsEntry *)lentry->data;
491
492     return thisentry->data.fourcc_data;
493   }
494   
495   return 0;
496 }
497
498 /**
499  * gst_props_get_boolean:
500  * @props: the props to get the fourcc value from
501  * @name: the name of the props entry to get.
502  *
503  * Get the named entry as a boolean value.
504  *
505  * Returns: the boolean value of the named entry, 0 if not found.
506  */
507 gboolean
508 gst_props_get_boolean (GstProps *props, const gchar *name)
509 {
510   GList *lentry;
511   GQuark quark;
512   
513   g_return_val_if_fail (props != NULL, FALSE);
514   g_return_val_if_fail (name != NULL, FALSE);
515
516   quark = g_quark_from_string (name);
517
518   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
519
520   if (lentry) {
521     GstPropsEntry *thisentry;
522
523     thisentry = (GstPropsEntry *)lentry->data;
524
525     return thisentry->data.bool_data;
526   }
527   
528   return 0;
529 }
530
531 /**
532  * gst_props_get_string:
533  * @props: the props to get the fourcc value from
534  * @name: the name of the props entry to get.
535  *
536  * Get the named entry as a string value.
537  *
538  * Returns: the string value of the named entry, NULL if not found.
539  */
540 const gchar*
541 gst_props_get_string (GstProps *props, const gchar *name)
542 {
543   GList *lentry;
544   GQuark quark;
545   
546   g_return_val_if_fail (props != NULL, NULL);
547   g_return_val_if_fail (name != NULL, NULL);
548
549   quark = g_quark_from_string (name);
550
551   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
552
553   if (lentry) {
554     GstPropsEntry *thisentry;
555
556     thisentry = (GstPropsEntry *)lentry->data;
557
558     return thisentry->data.string_data.string;
559   }
560   
561   return NULL;
562 }
563
564 /**
565  * gst_props_merge:
566  * @props: the property to merge into
567  * @tomerge: the property to merge 
568  *
569  * Merge the properties of tomerge into props.
570  *
571  * Returns: the new merged property 
572  */
573 GstProps*
574 gst_props_merge (GstProps *props, GstProps *tomerge)
575 {
576   GList *merge_props;
577
578   g_return_val_if_fail (props != NULL, NULL);
579   g_return_val_if_fail (tomerge != NULL, NULL);
580
581   merge_props = tomerge->properties;
582
583   // FIXME do proper merging here...
584   while (merge_props) {
585     GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
586
587     props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
588           
589     merge_props = g_list_next (merge_props);
590   }
591
592   return props;
593 }
594
595
596 /* entry2 is always a list, entry1 never is */
597 static gboolean
598 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
599 {
600   GList *entrylist = entry2->data.list_data.entries;
601   gboolean found = FALSE;
602
603   while (entrylist && !found) {
604     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
605
606     found |= gst_props_entry_check_compatibility (entry1, entry);
607
608     entrylist = g_list_next (entrylist);
609   }
610
611   return found;
612 }
613
614 static gboolean
615 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
616 {
617   GST_DEBUG (0,"compare: %s %s\n", g_quark_to_string (entry1->propid),
618                              g_quark_to_string (entry2->propid));
619   switch (entry1->propstype) {
620     case GST_PROPS_LIST_ID:
621     {
622       GList *entrylist = entry1->data.list_data.entries;
623       gboolean valid = TRUE;    // innocent until proven guilty
624
625       while (entrylist && valid) {
626         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
627
628         valid &= gst_props_entry_check_compatibility (entry, entry2);
629         
630         entrylist = g_list_next (entrylist);
631       }
632       
633       return valid;
634     }
635     case GST_PROPS_INT_RANGE_ID:
636       switch (entry2->propstype) {
637         // a - b   <--->   a - c
638         case GST_PROPS_INT_RANGE_ID:
639           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
640                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
641         case GST_PROPS_LIST_ID:
642           return gst_props_entry_check_list_compatibility (entry1, entry2);
643         default:
644           return FALSE;
645       }
646       break;
647     case GST_PROPS_FLOAT_RANGE_ID:
648       switch (entry2->propstype) {
649         // a - b   <--->   a - c
650         case GST_PROPS_FLOAT_RANGE_ID:
651           return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
652                   entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
653         case GST_PROPS_LIST_ID:
654           return gst_props_entry_check_list_compatibility (entry1, entry2);
655         default:
656           return FALSE;
657       }
658       break;
659     case GST_PROPS_FOURCC_ID:
660       switch (entry2->propstype) {
661         // b   <--->   a
662         case GST_PROPS_FOURCC_ID:
663           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
664         // b   <--->   a,b,c
665         case GST_PROPS_LIST_ID:
666           return gst_props_entry_check_list_compatibility (entry1, entry2);
667         default:
668           return FALSE;
669       }
670       break;
671     case GST_PROPS_INT_ID:
672       switch (entry2->propstype) {
673         // b   <--->   a - d
674         case GST_PROPS_INT_RANGE_ID:
675           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
676                   entry2->data.int_range_data.max >= entry1->data.int_data);
677         // b   <--->   a
678         case GST_PROPS_INT_ID:
679           return (entry2->data.int_data == entry1->data.int_data);
680         // b   <--->   a,b,c
681         case GST_PROPS_LIST_ID:
682           return gst_props_entry_check_list_compatibility (entry1, entry2);
683         default:
684           return FALSE;
685       }
686       break;
687     case GST_PROPS_FLOAT_ID:
688       switch (entry2->propstype) {
689         // b   <--->   a - d
690         case GST_PROPS_FLOAT_RANGE_ID:
691           return (entry2->data.float_range_data.min <= entry1->data.float_data &&
692                   entry2->data.float_range_data.max >= entry1->data.float_data);
693         // b   <--->   a
694         case GST_PROPS_FLOAT_ID:
695           return (entry2->data.float_data == entry1->data.float_data);
696         // b   <--->   a,b,c
697         case GST_PROPS_LIST_ID:
698           return gst_props_entry_check_list_compatibility (entry1, entry2);
699         default:
700           return FALSE;
701       }
702       break;
703     case GST_PROPS_BOOL_ID:
704       switch (entry2->propstype) {
705         // t   <--->   t
706         case GST_PROPS_BOOL_ID:
707           return (entry2->data.bool_data == entry1->data.bool_data);
708         case GST_PROPS_LIST_ID:
709           return gst_props_entry_check_list_compatibility (entry1, entry2);
710         default:
711           return FALSE;
712       }
713     case GST_PROPS_STRING_ID:
714       switch (entry2->propstype) {
715         // t   <--->   t
716         case GST_PROPS_STRING_ID:
717           return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
718         case GST_PROPS_LIST_ID:
719           return gst_props_entry_check_list_compatibility (entry1, entry2);
720         default:
721           return FALSE;
722       }
723     default:
724       break;
725   }
726
727   return FALSE;
728 }
729
730 /**
731  * gst_props_check_compatibility:
732  * @fromprops: a property
733  * @toprops: a property
734  *
735  * Checks whether two capabilities are compatible.
736  *
737  * Returns: TRUE if compatible, FALSE otherwise
738  */
739 gboolean
740 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
741 {
742   GList *sourcelist;
743   GList *sinklist;
744   gint missing = 0;
745   gint more = 0;
746   gboolean compatible = TRUE;
747
748   g_return_val_if_fail (fromprops != NULL, FALSE);
749   g_return_val_if_fail (toprops != NULL, FALSE);
750         
751   sourcelist = fromprops->properties;
752   sinklist   = toprops->properties;
753
754   while (sourcelist && sinklist && compatible) {
755     GstPropsEntry *entry1;
756     GstPropsEntry *entry2;
757
758     entry1 = (GstPropsEntry *)sourcelist->data;
759     entry2 = (GstPropsEntry *)sinklist->data;
760
761     while (entry1->propid < entry2->propid) {
762       GST_DEBUG (0,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
763       more++;
764       sourcelist = g_list_next (sourcelist);
765       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
766       else goto end;
767     }
768     while (entry1->propid > entry2->propid) {
769       GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
770       missing++;
771       sinklist = g_list_next (sinklist);
772       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
773       else goto end;
774     }
775
776     if (!gst_props_entry_check_compatibility (entry1, entry2)) {
777         compatible = FALSE;
778         GST_DEBUG (0, "%s are not compatible\n:",
779                    g_quark_to_string (entry1->propid));
780         gst_props_debug_entry (entry1);
781         gst_props_debug_entry (entry2);
782     }
783
784     sourcelist = g_list_next (sourcelist);
785     sinklist = g_list_next (sinklist);
786   }
787   if (sinklist && compatible) {
788     GstPropsEntry *entry2;
789     entry2 = (GstPropsEntry *)sinklist->data;
790     missing++;
791     GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
792   }
793 end:
794
795   if (missing)
796     return FALSE;
797
798   return compatible;
799 }
800
801 static xmlNodePtr
802 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
803 {
804   xmlNodePtr subtree;
805   gchar *str;
806
807   switch (entry->propstype) {
808     case GST_PROPS_INT_ID: 
809       subtree = xmlNewChild (parent, NULL, "int", NULL);
810       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
811       str = g_strdup_printf ("%d", entry->data.int_data);
812       xmlNewProp (subtree, "value", str);
813       g_free(str);
814       break;
815     case GST_PROPS_INT_RANGE_ID: 
816       subtree = xmlNewChild (parent, NULL, "range", NULL);
817       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
818       str = g_strdup_printf ("%d", entry->data.int_range_data.min);
819       xmlNewProp (subtree, "min", str);
820       g_free(str);
821       str = g_strdup_printf ("%d", entry->data.int_range_data.max);
822       xmlNewProp (subtree, "max", str);
823       g_free(str);
824       break;
825     case GST_PROPS_FLOAT_ID: 
826       subtree = xmlNewChild (parent, NULL, "float", NULL);
827       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
828       str = g_strdup_printf ("%f", entry->data.float_data);
829       xmlNewProp (subtree, "value", str);
830       g_free(str);
831       break;
832     case GST_PROPS_FLOAT_RANGE_ID: 
833       subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
834       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
835       str = g_strdup_printf ("%f", entry->data.float_range_data.min);
836       xmlNewProp (subtree, "min", str);
837       g_free(str);
838       str = g_strdup_printf ("%f", entry->data.float_range_data.max);
839       xmlNewProp (subtree, "max", str);
840       g_free(str);
841       break;
842     case GST_PROPS_FOURCC_ID: 
843       str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
844       xmlAddChild (parent, xmlNewComment (str));
845       g_free(str);
846       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
847       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
848       str = g_strdup_printf ("%08x", entry->data.fourcc_data);
849       xmlNewProp (subtree, "hexvalue", str);
850       g_free(str);
851       break;
852     case GST_PROPS_BOOL_ID: 
853       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
854       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
855       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
856       break;
857     case GST_PROPS_STRING_ID: 
858       subtree = xmlNewChild (parent, NULL, "string", NULL);
859       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
860       xmlNewProp (subtree, "value", entry->data.string_data.string);
861       break;
862     default:
863       break;
864   }
865
866   return parent;
867 }
868
869 /**
870  * gst_props_save_thyself:
871  * @props: a property to save
872  * @parent: the parent XML tree
873  *
874  * Saves the property into an XML representation.
875  *
876  * Returns: the new XML tree
877  */
878 xmlNodePtr
879 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
880 {
881   GList *proplist;
882   xmlNodePtr subtree;
883
884   g_return_val_if_fail (props != NULL, NULL);
885
886   proplist = props->properties;
887
888   while (proplist) {
889     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
890
891     switch (entry->propstype) {
892       case GST_PROPS_LIST_ID: 
893         subtree = xmlNewChild (parent, NULL, "list", NULL);
894         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
895         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
896       default:
897         gst_props_save_thyself_func (entry, parent);
898     }
899
900     proplist = g_list_next (proplist);
901   }
902   
903   return parent;
904 }
905
906 static GstPropsEntry*
907 gst_props_load_thyself_func (xmlNodePtr field)
908 {
909   GstPropsEntry *entry;
910   gchar *prop;
911
912   g_mutex_lock (_gst_props_entries_chunk_lock);
913   entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
914   g_mutex_unlock (_gst_props_entries_chunk_lock);
915
916   if (!strcmp(field->name, "int")) {
917     entry->propstype = GST_PROPS_INT_ID;
918     prop = xmlGetProp(field, "name");
919     entry->propid = g_quark_from_string (prop);
920     g_free (prop);
921     prop = xmlGetProp(field, "value");
922     sscanf (prop, "%d", &entry->data.int_data);
923     g_free (prop);
924   }
925   else if (!strcmp(field->name, "range")) {
926     entry->propstype = GST_PROPS_INT_RANGE_ID;
927     prop = xmlGetProp(field, "name");
928     entry->propid = g_quark_from_string (prop);
929     g_free (prop);
930     prop = xmlGetProp (field, "min");
931     sscanf (prop, "%d", &entry->data.int_range_data.min);
932     g_free (prop);
933     prop = xmlGetProp (field, "max");
934     sscanf (prop, "%d", &entry->data.int_range_data.max);
935     g_free (prop);
936   }
937   else if (!strcmp(field->name, "float")) {
938     entry->propstype = GST_PROPS_FLOAT_ID;
939     prop = xmlGetProp(field, "name");
940     entry->propid = g_quark_from_string (prop);
941     g_free (prop);
942     prop = xmlGetProp(field, "value");
943     sscanf (prop, "%f", &entry->data.float_data);
944     g_free (prop);
945   }
946   else if (!strcmp(field->name, "floatrange")) {
947     entry->propstype = GST_PROPS_FLOAT_RANGE_ID;
948     prop = xmlGetProp(field, "name");
949     entry->propid = g_quark_from_string (prop);
950     g_free (prop);
951     prop = xmlGetProp (field, "min");
952     sscanf (prop, "%f", &entry->data.float_range_data.min);
953     g_free (prop);
954     prop = xmlGetProp (field, "max");
955     sscanf (prop, "%f", &entry->data.float_range_data.max);
956     g_free (prop);
957   }
958   else if (!strcmp(field->name, "boolean")) {
959     entry->propstype = GST_PROPS_BOOL_ID;
960     prop = xmlGetProp(field, "name");
961     entry->propid = g_quark_from_string (prop);
962     g_free (prop);
963     prop = xmlGetProp (field, "value");
964     if (!strcmp (prop, "false")) entry->data.bool_data = 0;
965     else entry->data.bool_data = 1;
966     g_free (prop);
967   }
968   else if (!strcmp(field->name, "fourcc")) {
969     entry->propstype = GST_PROPS_FOURCC_ID;
970     prop = xmlGetProp(field, "name");
971     entry->propid = g_quark_from_string (prop);
972     g_free (prop);
973     prop = xmlGetProp (field, "hexvalue");
974     sscanf (prop, "%08x", &entry->data.fourcc_data);
975     g_free (prop);
976   }
977   else if (!strcmp(field->name, "string")) {
978     entry->propstype = GST_PROPS_STRING_ID;
979     prop = xmlGetProp(field, "name");
980     entry->propid = g_quark_from_string (prop);
981     g_free (prop);
982     entry->data.string_data.string = xmlGetProp (field, "value");
983   }
984   else {
985     g_mutex_lock (_gst_props_entries_chunk_lock);
986     g_mem_chunk_free (_gst_props_entries_chunk, entry);
987     g_mutex_unlock (_gst_props_entries_chunk_lock);
988     entry = NULL;
989   }
990
991   return entry;
992 }
993
994 /**
995  * gst_props_load_thyself:
996  * @parent: the XML tree to load from
997  *
998  * Creates a new property out of an XML tree.
999  *
1000  * Returns: the new property
1001  */
1002 GstProps*
1003 gst_props_load_thyself (xmlNodePtr parent)
1004 {
1005   GstProps *props;
1006   xmlNodePtr field = parent->xmlChildrenNode;
1007   gchar *prop;
1008
1009   g_mutex_lock (_gst_props_chunk_lock);
1010   props = g_mem_chunk_alloc (_gst_props_chunk);
1011   g_mutex_unlock (_gst_props_chunk_lock);
1012
1013   props->properties = NULL;
1014   props->refcount = 1;
1015
1016   while (field) {
1017     if (!strcmp (field->name, "list")) {
1018       GstPropsEntry *entry;
1019       xmlNodePtr subfield = field->xmlChildrenNode;
1020
1021       g_mutex_lock (_gst_props_entries_chunk_lock);
1022       entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
1023       g_mutex_unlock (_gst_props_entries_chunk_lock);
1024
1025       entry->propstype = GST_PROPS_LIST_ID;
1026       entry->data.list_data.entries = NULL;
1027       prop = xmlGetProp (field, "name");
1028       entry->propid = g_quark_from_string (prop);
1029       g_free (prop);
1030
1031       while (subfield) {
1032         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
1033
1034         if (subentry)
1035           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
1036
1037         subfield = subfield->next;
1038       }
1039       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
1040       props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
1041     }
1042     else {
1043       GstPropsEntry *entry;
1044
1045       entry = gst_props_load_thyself_func (field);
1046
1047       if (entry) 
1048         props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
1049     }
1050     field = field->next;
1051   }
1052
1053   return props;
1054 }