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