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