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