- Added value transform functions for caps and props boxed types
[platform/upstream/gstreamer.git] / gst / gstprops.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wim.taymans@chello.be>
4  *
5  * gstprops.c: Properties subsystem for generic usage
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /* #define GST_DEBUG_ENABLED */
24 #include "gst_private.h"
25
26 #include "gstlog.h"
27 #include "gstprops.h"
28
29 GType _gst_props_type;
30
31 #define GST_PROPS_ENTRY_IS_VARIABLE(a)  (((GstPropsEntry*)(a))->propstype > GST_PROPS_VAR_TYPE)
32
33 struct _GstPropsEntry {
34   GQuark        propid;
35   GstPropsType  propstype;              
36
37   union {
38     /* flat values */
39     gboolean bool_data;
40     guint32  fourcc_data;
41     gint     int_data;
42     gfloat   float_data;
43
44     /* structured values */
45     struct {
46       GList *entries;
47     } list_data;
48     struct {
49       gchar *string;
50     } string_data;
51     struct {
52       gint min;
53       gint max;
54     } int_range_data;
55     struct {
56       gfloat min;
57       gfloat max;
58     } float_range_data;
59   } data;
60 };
61
62 static GMemChunk *_gst_props_entries_chunk;
63 static GMutex *_gst_props_entries_chunk_lock;
64
65 static GMemChunk *_gst_props_chunk;
66 static GMutex *_gst_props_chunk_lock;
67
68 static gboolean         gst_props_entry_check_compatibility     (GstPropsEntry *entry1, GstPropsEntry *entry2);
69 static GList*           gst_props_list_copy                     (GList *propslist);
70
71 static void
72 transform_func (const GValue *src_value,
73                 GValue *dest_value)
74 {
75   GstProps *props = g_value_peek_pointer (src_value);
76   GString *result = g_string_new ("");
77   GList *propslist = props->properties;
78
79   while (propslist) { 
80     GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
81     const gchar *name = g_quark_to_string (entry->propid);
82
83     switch (entry->propstype) {
84       case GST_PROPS_STRING_TYPE:
85         g_string_append_printf (result, "%s='%s'", name, entry->data.string_data.string);
86       default:
87         break;
88     }
89     
90     propslist = g_list_next (propslist);
91     if (propslist) {
92       g_string_append (result, "; ");
93     }
94   }
95   dest_value->data[0].v_pointer = result->str;
96 }
97
98         
99 void 
100 _gst_props_initialize (void) 
101 {
102   _gst_props_entries_chunk = g_mem_chunk_new ("GstPropsEntries", 
103                   sizeof (GstPropsEntry), sizeof (GstPropsEntry) * 256, 
104                   G_ALLOC_AND_FREE);
105   _gst_props_entries_chunk_lock = g_mutex_new ();
106
107   _gst_props_chunk = g_mem_chunk_new ("GstProps", 
108                   sizeof (GstProps), sizeof (GstProps) * 256, 
109                   G_ALLOC_AND_FREE);
110   _gst_props_chunk_lock = g_mutex_new ();
111
112   _gst_props_type = g_boxed_type_register_static ("GstProps",
113                                        (GBoxedCopyFunc) gst_props_ref,
114                                        (GBoxedFreeFunc) gst_props_unref);
115
116   g_value_register_transform_func (_gst_props_type,
117                                    G_TYPE_STRING,
118                                    transform_func);
119 }
120
121 static void
122 gst_props_debug_entry (GstPropsEntry *entry)
123 {
124   const gchar *name = g_quark_to_string (entry->propid);
125
126   switch (entry->propstype) {
127     case GST_PROPS_INT_TYPE:
128       GST_DEBUG (GST_CAT_PROPERTIES, "%s: int %d", name, entry->data.int_data);
129       break;
130     case GST_PROPS_FLOAT_TYPE:
131       GST_DEBUG (GST_CAT_PROPERTIES, "%s: float %f", name, entry->data.float_data);
132       break;
133     case GST_PROPS_FOURCC_TYPE:
134       GST_DEBUG (GST_CAT_PROPERTIES, "%s: fourcc %4.4s", name, (gchar*)&entry->data.fourcc_data);
135       break;
136     case GST_PROPS_BOOL_TYPE:
137       GST_DEBUG (GST_CAT_PROPERTIES, "%s: bool %d", name, entry->data.bool_data);
138       break;
139     case GST_PROPS_STRING_TYPE:
140       GST_DEBUG (GST_CAT_PROPERTIES, "%s: string \"%s\"", name, entry->data.string_data.string);
141       break;
142     case GST_PROPS_INT_RANGE_TYPE:
143       GST_DEBUG (GST_CAT_PROPERTIES, "%s: int range %d-%d", name, entry->data.int_range_data.min,
144                       entry->data.int_range_data.max);
145       break;
146     case GST_PROPS_FLOAT_RANGE_TYPE:
147       GST_DEBUG (GST_CAT_PROPERTIES, "%s: float range %f-%f", name, entry->data.float_range_data.min,
148                       entry->data.float_range_data.max);
149       break;
150     case GST_PROPS_LIST_TYPE:
151       GST_DEBUG (GST_CAT_PROPERTIES, "[list]");
152       {
153         GList *entries = entry->data.list_data.entries;
154
155         while (entries) {
156           gst_props_debug_entry ((GstPropsEntry *)entries->data);
157           entries = g_list_next (entries);
158         }
159       }
160       break;
161     default:
162       g_warning ("unknown property type %d", entry->propstype);
163       break;
164   }
165 }
166
167 static gint 
168 props_compare_func (gconstpointer a,
169                     gconstpointer b) 
170 {
171   GstPropsEntry *entry1 = (GstPropsEntry *)a;
172   GstPropsEntry *entry2 = (GstPropsEntry *)b;
173
174   return (entry1->propid - entry2->propid);
175 }
176
177 static gint 
178 props_find_func (gconstpointer a,
179                  gconstpointer b) 
180 {
181   GstPropsEntry *entry2 = (GstPropsEntry *)a;
182   GQuark quark = (GQuark) GPOINTER_TO_INT (b);
183
184   return (quark - entry2->propid);
185 }
186
187 /* This is implemented as a huge macro because we cannot pass
188  * va_list variables by reference on some architectures.
189  */
190 #define GST_PROPS_ENTRY_FILL(entry, var_args)                                   \
191 G_STMT_START {                                                                  \
192   entry->propstype = va_arg (var_args, GstPropsType);                           \
193                                                                                 \
194   switch (entry->propstype) {                                                   \
195     case GST_PROPS_INT_TYPE:                                                    \
196       entry->data.int_data = va_arg (var_args, gint);                           \
197       break;                                                                    \
198     case GST_PROPS_INT_RANGE_TYPE:                                              \
199       entry->data.int_range_data.min = va_arg (var_args, gint);                 \
200       entry->data.int_range_data.max = va_arg (var_args, gint);                 \
201       break;                                                                    \
202     case GST_PROPS_FLOAT_TYPE:                                                  \
203       entry->data.float_data = va_arg (var_args, gdouble);                      \
204       break;                                                                    \
205     case GST_PROPS_FLOAT_RANGE_TYPE:                                            \
206       entry->data.float_range_data.min = va_arg (var_args, gdouble);            \
207       entry->data.float_range_data.max = va_arg (var_args, gdouble);            \
208       break;                                                                    \
209     case GST_PROPS_FOURCC_TYPE:                                                 \
210       entry->data.fourcc_data = va_arg (var_args, gulong);                      \
211       break;                                                                    \
212     case GST_PROPS_BOOL_TYPE:                                                   \
213       entry->data.bool_data = va_arg (var_args, gboolean);                      \
214       break;                                                                    \
215     case GST_PROPS_STRING_TYPE:                                                 \
216       entry->data.string_data.string = g_strdup (va_arg (var_args, gchar*));    \
217       break;                                                                    \
218     case GST_PROPS_LIST_TYPE:                                                   \
219       entry->data.list_data.entries = g_list_copy (va_arg (var_args, GList*));  \
220       break;                                                                    \
221     default:                                                                    \
222       break;                                                                    \
223   }                                                                             \
224 } G_STMT_END
225
226
227 #define GST_PROPS_ENTRY_READ(entry, var_args, safe, result)                     \
228 G_STMT_START {                                                                  \
229                                                                                 \
230   *result = TRUE;                                                               \
231                                                                                 \
232   if (safe) {                                                                   \
233     GstPropsType propstype = va_arg (var_args, GstPropsType);                   \
234     if (propstype != entry->propstype) {                                        \
235       *result = FALSE;                                                          \
236     }                                                                           \
237   }                                                                             \
238   if (*result) {                                                                \
239     switch (entry->propstype) {                                                 \
240       case GST_PROPS_INT_TYPE:                                                  \
241         *(va_arg (var_args, gint*)) = entry->data.int_data;                     \
242         break;                                                                  \
243       case GST_PROPS_INT_RANGE_TYPE:                                            \
244         *(va_arg (var_args, gint*)) = entry->data.int_range_data.min;           \
245         *(va_arg (var_args, gint*)) = entry->data.int_range_data.max;           \
246         break;                                                                  \
247       case GST_PROPS_FLOAT_TYPE:                                                \
248         *(va_arg (var_args, gfloat*)) = entry->data.float_data;                 \
249         break;                                                                  \
250       case GST_PROPS_FLOAT_RANGE_TYPE:                                          \
251         *(va_arg (var_args, gfloat*)) = entry->data.float_range_data.min;       \
252         *(va_arg (var_args, gfloat*)) = entry->data.float_range_data.max;       \
253         break;                                                                  \
254       case GST_PROPS_FOURCC_TYPE:                                               \
255         *(va_arg (var_args, guint32*)) = entry->data.fourcc_data;               \
256         break;                                                                  \
257       case GST_PROPS_BOOL_TYPE:                                                 \
258         *(va_arg (var_args, gboolean*)) = entry->data.bool_data;                \
259         break;                                                                  \
260       case GST_PROPS_STRING_TYPE:                                               \
261         *(va_arg (var_args, gchar**)) = entry->data.string_data.string;         \
262         break;                                                                  \
263       case GST_PROPS_LIST_TYPE:                                                 \
264         *(va_arg (var_args, GList**)) = entry->data.list_data.entries;          \
265         break;                                                                  \
266       default:                                                                  \
267         *result = FALSE;                                                        \
268         break;                                                                  \
269     }                                                                           \
270   }                                                                             \
271 } G_STMT_END
272
273 static GstPropsEntry*
274 gst_props_alloc_entry (void)
275 {
276   GstPropsEntry *entry;
277
278   g_mutex_lock (_gst_props_entries_chunk_lock);
279   entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
280   g_mutex_unlock (_gst_props_entries_chunk_lock);
281
282   return entry;
283 }
284
285 static void
286 gst_props_entry_destroy (GstPropsEntry *entry)
287 {
288   switch (entry->propstype) {
289     case GST_PROPS_STRING_TYPE:         
290       g_free (entry->data.string_data.string);
291       break;                            
292     case GST_PROPS_LIST_TYPE:           
293     {
294       GList *entries = entry->data.list_data.entries;
295
296       while (entries) {
297         gst_props_entry_destroy ((GstPropsEntry *)entries->data);
298         entries = g_list_next (entries);
299       }
300       g_list_free (entry->data.list_data.entries);
301       break;
302     }
303     default:                    
304       break;            
305   }
306   g_mutex_lock (_gst_props_entries_chunk_lock);
307   g_mem_chunk_free (_gst_props_entries_chunk, entry);
308   g_mutex_unlock (_gst_props_entries_chunk_lock);
309 }
310
311 /**
312  * gst_props_empty_new:
313  *
314  * Create a new empty property.
315  *
316  * Returns: the new property
317  */
318 GstProps*
319 gst_props_empty_new (void)
320 {
321   GstProps *props;
322
323   g_mutex_lock (_gst_props_chunk_lock);
324   props = g_mem_chunk_alloc (_gst_props_chunk);
325   g_mutex_unlock (_gst_props_chunk_lock);
326
327   props->properties = NULL;
328   props->refcount = 1;
329   props->fixed = TRUE;
330
331   return props;
332 }
333
334 /**
335  * gst_props_add_entry:
336  * @props: the property to add the entry to
337  * @entry: the entry to add
338  *
339  * Addes the given propsentry to the props
340  */
341 void
342 gst_props_add_entry (GstProps *props, GstPropsEntry *entry)
343 {
344   g_return_if_fail (props);
345   g_return_if_fail (entry);
346
347   if (props->fixed && GST_PROPS_ENTRY_IS_VARIABLE (entry)) {
348     props->fixed = FALSE;
349   }
350   props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
351 }
352
353 /**
354  * gst_props_new:
355  * @firstname: the first property name
356  * @...: the property values 
357  *
358  * Create a new property from the given key/value pairs
359  *
360  * Returns: the new property
361  */
362 GstProps*
363 gst_props_new (const gchar *firstname, ...)
364 {
365   GstProps *props;
366   va_list var_args;
367   
368   va_start (var_args, firstname);
369
370   props = gst_props_newv (firstname, var_args);
371   
372   va_end (var_args);
373   
374   return props;
375
376
377
378 /**
379  * gst_props_debug:
380  * @props: the props to debug
381  *
382  * Dump the contents of the given properties into the DEBUG log.
383  */
384 void
385 gst_props_debug (GstProps *props)
386 {
387   GList *propslist = props->properties;
388
389   while (propslist) { 
390     GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
391
392     gst_props_debug_entry (entry);
393     
394     propslist = g_list_next (propslist);
395   }
396 }
397
398 /**
399  * gst_props_merge_int_entries:
400  * @newentry: the new entry
401  * @oldentry: an old entry
402  *
403  * Tries to merge oldentry into newentry, if there is a simpler single entry which represents
404  *
405  * Assumes that the entries are either ints or int ranges.
406  *
407  * Returns: TRUE if the entries were merged, FALSE otherwise.
408  */
409 static gboolean
410 gst_props_merge_int_entries(GstPropsEntry * newentry, GstPropsEntry * oldentry)
411 {
412   gint new_min, new_max, old_min, old_max;
413   gboolean can_merge = FALSE;
414
415   if (newentry->propstype == GST_PROPS_INT_TYPE) {
416     new_min = newentry->data.int_data;
417     new_max = newentry->data.int_data;
418   } else {
419     new_min = newentry->data.int_range_data.min;
420     new_max = newentry->data.int_range_data.max;
421   }
422
423   if (oldentry->propstype == GST_PROPS_INT_TYPE) {
424     old_min = oldentry->data.int_data;
425     old_max = oldentry->data.int_data;
426   } else {
427     old_min = oldentry->data.int_range_data.min;
428     old_max = oldentry->data.int_range_data.max;
429   }
430
431   /* Put range which starts lower into (new_min, new_max) */
432   if (old_min < new_min) {
433     gint tmp;
434     tmp = old_min;
435     old_min = new_min;
436     new_min = tmp;
437     tmp = old_max;
438     old_max = new_max;
439     new_max = tmp;
440   }
441
442   /* new_min is min of either entry - second half of the following conditional */
443   /* is to avoid overflow problems. */
444   if (new_max >= old_min - 1 && old_min - 1 < old_min) {
445     /* ranges overlap, or are adjacent.  Pick biggest maximum. */
446     can_merge = TRUE;
447     if (old_max > new_max) new_max = old_max;
448   }
449
450   if (can_merge) {
451     if (new_min == new_max) {
452       newentry->propstype = GST_PROPS_INT_TYPE;
453       newentry->data.int_data = new_min;
454     } else {
455       newentry->propstype = GST_PROPS_INT_RANGE_TYPE;
456       newentry->data.int_range_data.min = new_min;
457       newentry->data.int_range_data.max = new_max;
458     }
459   }
460   return can_merge;
461 }
462
463 /**
464  * gst_props_add_to_int_list:
465  * @entries: the existing list of entries
466  * @entry: the new entry to add to the list
467  *
468  * Add an integer property to a list of properties, removing duplicates
469  * and merging ranges.
470  *
471  * Assumes that the existing list is in simplest form, contains
472  * only ints and int ranges, and that the new entry is an int or 
473  * an int range.
474  *
475  * Returns: a pointer to a list with the new entry added.
476  */
477 static GList *
478 gst_props_add_to_int_list (GList * entries, GstPropsEntry * newentry)
479 {
480   GList * i;
481
482   i = entries;
483   while (i) {
484     GstPropsEntry * oldentry = (GstPropsEntry *)(i->data);
485     gboolean merged = gst_props_merge_int_entries(newentry, oldentry);
486
487     if (merged) {
488       /* replace the existing one with the merged one */
489       g_mutex_lock (_gst_props_entries_chunk_lock);
490       g_mem_chunk_free (_gst_props_entries_chunk, oldentry);
491       g_mutex_unlock (_gst_props_entries_chunk_lock);
492       entries = g_list_remove_link (entries, i);
493       g_list_free_1 (i);
494
495       /* start again: it's possible that this change made an earlier entry */
496       /* mergeable, and the pointer is now invalid anyway. */
497       i = entries;
498     }
499
500     i = g_list_next (i);
501   }
502
503   return g_list_prepend (entries, newentry);
504 }
505
506 static GstPropsEntry*
507 gst_props_entry_newv (const gchar *name, va_list var_args)
508 {
509   GstPropsEntry *entry;
510
511   entry = gst_props_alloc_entry ();
512   entry->propid = g_quark_from_string (name);
513   GST_PROPS_ENTRY_FILL (entry, var_args);
514   
515   return entry;
516 }
517
518 /**
519  * gst_props_entry_new:
520  * @name: the name of the props entry
521  * @...: the value of the entry
522  *
523  * Create a new property entry with the given key/value.
524  *
525  * Returns: the new entry.
526  */
527 GstPropsEntry*
528 gst_props_entry_new (const gchar *name, ...)
529 {
530   va_list var_args;
531   GstPropsEntry *entry;
532   
533   va_start (var_args, name);
534   entry = gst_props_entry_newv (name, var_args);
535   va_end (var_args);
536
537   return entry;
538 }
539
540 /**
541  * gst_props_newv:
542  * @firstname: the first property name
543  * @var_args: the property values
544  *
545  * Create a new property from the list of entries.
546  *
547  * Returns: the new property created from the list of entries
548  */
549 GstProps*
550 gst_props_newv (const gchar *firstname, va_list var_args)
551 {
552   GstProps *props;
553   gboolean inlist = FALSE;
554   const gchar *prop_name;
555   GstPropsEntry *list_entry = NULL;
556
557   typedef enum {
558       GST_PROPS_LIST_T_UNSET,
559       GST_PROPS_LIST_T_INTS,
560       GST_PROPS_LIST_T_FLOATS,
561       GST_PROPS_LIST_T_MISC,
562   } list_types;
563
564   /* type of the list */
565   list_types list_type = GST_PROPS_LIST_T_UNSET;
566   /* type of current item */
567   list_types entry_type = GST_PROPS_LIST_T_UNSET;
568
569   if (firstname == NULL)
570     return NULL;
571
572   props = gst_props_empty_new ();
573
574   prop_name = firstname;
575
576   /* properties */
577   while (prop_name) {
578     GstPropsEntry *entry;
579    
580     entry = gst_props_alloc_entry ();
581     entry->propid = g_quark_from_string (prop_name);
582     GST_PROPS_ENTRY_FILL (entry, var_args);
583
584     switch (entry->propstype) {
585       case GST_PROPS_INT_TYPE:
586       case GST_PROPS_INT_RANGE_TYPE:
587         entry_type = GST_PROPS_LIST_T_INTS;
588         break;
589       case GST_PROPS_FLOAT_TYPE:
590       case GST_PROPS_FLOAT_RANGE_TYPE:
591         entry_type = GST_PROPS_LIST_T_FLOATS;
592         break;
593       case GST_PROPS_FOURCC_TYPE:
594       case GST_PROPS_BOOL_TYPE:
595       case GST_PROPS_STRING_TYPE:
596         entry_type = GST_PROPS_LIST_T_MISC;
597         break;
598       case GST_PROPS_LIST_TYPE:
599         g_return_val_if_fail (inlist == FALSE, NULL);
600         inlist = TRUE;
601         list_entry = entry;
602         list_type = GST_PROPS_LIST_T_UNSET;
603         list_entry->data.list_data.entries = NULL;
604         break;
605       case GST_PROPS_END_TYPE:
606         g_return_val_if_fail (inlist == TRUE, NULL);
607
608         /* if list was of size 1, replace the list by a the item it contains */
609         if (g_list_length(list_entry->data.list_data.entries) == 1) {
610           GstPropsEntry * subentry = (GstPropsEntry *)(list_entry->data.list_data.entries->data);
611           list_entry->propstype = subentry->propstype;
612           list_entry->data = subentry->data;
613           g_mutex_lock (_gst_props_entries_chunk_lock);
614           g_mem_chunk_free (_gst_props_entries_chunk, subentry);
615           g_mutex_unlock (_gst_props_entries_chunk_lock);
616         }
617         else {
618           list_entry->data.list_data.entries =
619                     g_list_reverse (list_entry->data.list_data.entries);
620         }
621
622         g_mutex_lock (_gst_props_entries_chunk_lock);
623         g_mem_chunk_free (_gst_props_entries_chunk, entry);
624         g_mutex_unlock (_gst_props_entries_chunk_lock);
625         inlist = FALSE;
626         list_entry = NULL;
627         prop_name = va_arg (var_args, gchar*);
628         continue;
629       default:
630         g_warning ("unknown property type found %d for '%s'\n", entry->propstype, prop_name);
631         g_mutex_lock (_gst_props_entries_chunk_lock);
632         g_mem_chunk_free (_gst_props_entries_chunk, entry);
633         g_mutex_unlock (_gst_props_entries_chunk_lock);
634         break;
635     }
636
637     if (inlist && (list_entry != entry)) {
638       if (list_type == GST_PROPS_LIST_T_UNSET) list_type = entry_type;
639       if (list_type != entry_type) {
640         g_warning ("property list contained incompatible entry types\n");
641       } else {
642         switch (list_type) {
643           case GST_PROPS_LIST_T_INTS:
644             list_entry->data.list_data.entries =
645                     gst_props_add_to_int_list (list_entry->data.list_data.entries, entry);
646             break;
647           default:
648             list_entry->data.list_data.entries =
649                     g_list_prepend (list_entry->data.list_data.entries, entry);
650             break;
651         }
652       }
653     }
654     else {
655       gst_props_add_entry (props, entry);
656     }
657     if (!inlist)
658       prop_name = va_arg (var_args, gchar*);
659   }
660
661   return props;
662 }
663
664 /**
665  * gst_props_set:
666  * @props: the props to modify
667  * @name: the name of the entry to modify
668  * @...: The prop entry.
669  *
670  * Modifies the value of the given entry in the props struct.
671  * For the optional args, use GST_PROPS_FOO, where FOO is INT,
672  * STRING, etc. This macro expands to a variable number of arguments,
673  * hence the lack of precision in the function prototype. No
674  * terminating NULL is necessary as only one property can be changed.
675  *
676  * Returns: the new modified property structure.
677  */
678 GstProps*
679 gst_props_set (GstProps *props, const gchar *name, ...)
680 {
681   GQuark quark;
682   GList *lentry;
683   va_list var_args;
684
685   g_return_val_if_fail (props != NULL, NULL);
686   
687   quark = g_quark_from_string (name);
688
689   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
690
691   if (lentry) {
692     GstPropsEntry *entry;
693
694     entry = (GstPropsEntry *)lentry->data;
695
696     va_start (var_args, name);
697
698     GST_PROPS_ENTRY_FILL (entry, var_args);
699
700     va_end (var_args);
701   }
702   else {
703     g_warning ("gstprops: no property '%s' to change\n", name);
704   }
705
706   return props;
707 }
708
709
710 /**
711  * gst_props_unref:
712  * @props: the props to unref
713  *
714  * Decrease the refcount of the property structure, destroying
715  * the property if the refcount is 0.
716  */
717 void
718 gst_props_unref (GstProps *props)
719 {
720   if (props == NULL)
721     return;
722   
723   props->refcount--;
724
725   if (props->refcount == 0)
726     gst_props_destroy (props);
727 }
728
729 /**
730  * gst_props_ref:
731  * @props: the props to ref
732  *
733  * Increase the refcount of the property structure.
734  */
735 void
736 gst_props_ref (GstProps *props)
737 {
738   g_return_if_fail (props != NULL);
739   
740   props->refcount++;
741 }
742
743
744 /**
745  * gst_props_destroy:
746  * @props: the props to destroy
747  *
748  * Destroy the property, freeing all the memory that
749  * was allocated.
750  */
751 void
752 gst_props_destroy (GstProps *props)
753 {
754   GList *entries;
755
756   if (props == NULL)
757     return;
758   
759   entries = props->properties;
760
761   while (entries) {
762     gst_props_entry_destroy ((GstPropsEntry *)entries->data);
763     entries = g_list_next (entries);
764   }
765   g_list_free (props->properties);
766
767   g_mutex_lock (_gst_props_chunk_lock);
768   g_mem_chunk_free (_gst_props_chunk, props);
769   g_mutex_unlock (_gst_props_chunk_lock);
770 }
771
772 /* 
773  * copy entries 
774  */
775 static GstPropsEntry*
776 gst_props_entry_copy (GstPropsEntry *entry)
777 {
778   GstPropsEntry *newentry;
779
780   newentry = gst_props_alloc_entry ();
781   memcpy (newentry, entry, sizeof (GstPropsEntry));
782   if (entry->propstype == GST_PROPS_LIST_TYPE) {
783     newentry->data.list_data.entries = gst_props_list_copy (entry->data.list_data.entries);
784   }
785   else if (entry->propstype == GST_PROPS_STRING_TYPE) {
786     newentry->data.string_data.string = g_strdup (entry->data.string_data.string);
787   }
788
789   return newentry;
790 }
791
792 static GList*
793 gst_props_list_copy (GList *propslist)
794 {
795   GList *new = NULL;
796
797   while (propslist) {
798     GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
799
800     new = g_list_prepend (new, gst_props_entry_copy (entry));
801     
802     propslist = g_list_next (propslist);
803   }
804   new = g_list_reverse (new);
805
806   return new;
807 }
808
809 /**
810  * gst_props_copy:
811  * @props: the props to copy
812  *
813  * Copy the property structure.
814  *
815  * Returns: the new property that is a copy of the original
816  * one.
817  */
818 GstProps*
819 gst_props_copy (GstProps *props)
820 {
821   GstProps *new;
822
823   if (props == NULL)
824     return NULL;
825
826   new = gst_props_empty_new ();
827   new->properties = gst_props_list_copy (props->properties);
828   new->fixed = props->fixed;
829
830   return new;
831 }
832
833 /**
834  * gst_props_copy_on_write:
835  * @props: the props to copy on write
836  *
837  * Copy the property structure if the refcount is >1.
838  *
839  * Returns: A new props that can be safely written to.
840  */
841 GstProps*
842 gst_props_copy_on_write (GstProps *props)
843 {
844   GstProps *new = props;;
845
846   g_return_val_if_fail (props != NULL, NULL);
847
848   if (props->refcount > 1) {
849     new = gst_props_copy (props);
850     gst_props_unref (props);
851   }
852
853   return new;
854 }
855
856 /**
857  * gst_props_get_entry:
858  * @props: the props to query
859  * @name: the name of the entry to get
860  *
861  * Get the props entry with the geven name
862  *
863  * Returns: The props entry with the geven name or NULL when
864  * the entry was not found.
865  */
866 const GstPropsEntry*
867 gst_props_get_entry (GstProps *props, const gchar *name)
868 {
869   GList *lentry;
870   GQuark quark;
871   
872   g_return_val_if_fail (props != NULL, NULL);
873   g_return_val_if_fail (name != NULL, NULL);
874
875   quark = g_quark_from_string (name);
876
877   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
878
879   if (lentry) {
880     GstPropsEntry *thisentry;
881     thisentry = (GstPropsEntry *)lentry->data;
882     return thisentry;
883   }
884   return NULL;
885 }
886
887 /**
888  * gst_props_has_property:
889  * @props: the props to check
890  * @name: the name of the key to find
891  *
892  * Checks if a given props has a property with the given name.
893  *
894  * Returns: TRUE if the property was found, FALSE otherwise.
895  */
896 gboolean
897 gst_props_has_property (GstProps *props, const gchar *name)
898 {
899   return (gst_props_get_entry (props, name) != NULL);
900 }
901
902 /**
903  * gst_props_has_property_typed:
904  * @props: the props to check
905  * @name: the name of the key to find
906  * @type: the type of the required property
907  *
908  * Checks if a given props has a property with the given name and the given type.
909  *
910  * Returns: TRUE if the property was found, FALSE otherwise.
911  */
912 gboolean
913 gst_props_has_property_typed (GstProps *props, const gchar *name, GstPropsType type)
914 {
915   const GstPropsEntry *entry;
916
917   entry = gst_props_get_entry (props, name);
918   if (!entry) 
919     return FALSE;
920
921   return (entry->propstype == type);
922 }
923
924 /**
925  * gst_props_has_fixed_property:
926  * @props: the props to check
927  * @name: the name of the key to find
928  *
929  * Checks if a given props has a property with the given name that 
930  * is also fixed, ie. is not a list or a range.
931  *
932  * Returns: TRUE if the property was found, FALSE otherwise.
933  */
934 gboolean
935 gst_props_has_fixed_property (GstProps *props, const gchar *name)
936 {
937   const GstPropsEntry *entry;
938
939   entry = gst_props_get_entry (props, name);
940   if (!entry) 
941     return FALSE;
942
943   return !GST_PROPS_ENTRY_IS_VARIABLE (entry);
944 }
945
946 /**
947  * gst_props_entry_get_type:
948  * @entry: the props entry to query
949  *
950  * Get the type of the given props entry. 
951  *
952  * Returns: The type of the props entry.
953  */
954 GstPropsType
955 gst_props_entry_get_type (const GstPropsEntry *entry)
956 {
957   g_return_val_if_fail (entry != NULL, GST_PROPS_INVALID_TYPE);
958
959   return entry->propstype;
960 }
961
962 /**
963  * gst_props_entry_get_name:
964  * @entry: the props entry to query
965  *
966  * Get the name of the given props entry. 
967  *
968  * Returns: The name of the props entry.
969  */
970 const gchar*
971 gst_props_entry_get_name (const GstPropsEntry *entry)
972 {
973   g_return_val_if_fail (entry != NULL, NULL);
974
975   return g_quark_to_string (entry->propid);
976 }
977
978 /**
979  * gst_props_entry_is_fixed:
980  * @entry: the props entry to query
981  *
982  * Checks if the props entry is fixe, ie. is not a list
983  * or a range.
984  *
985  * Returns: TRUE is the props entry is fixed.
986  */
987 gboolean
988 gst_props_entry_is_fixed (const GstPropsEntry *entry)
989 {
990   g_return_val_if_fail (entry != NULL, FALSE);
991
992   return !GST_PROPS_ENTRY_IS_VARIABLE (entry);
993 }
994
995 static gboolean
996 gst_props_entry_getv (const GstPropsEntry *entry, gboolean safe, va_list var_args)
997 {
998   gboolean result;
999
1000   GST_PROPS_ENTRY_READ (entry, var_args, safe, &result);
1001
1002   return result;
1003 }
1004
1005 /**
1006  * gst_props_entry_get:
1007  * @entry: the props entry to query
1008  * @...: a pointer to a type that can hold the value.
1009  *
1010  * Gets the contents of the entry.
1011  *
1012  * Returns: TRUE is the props entry could be fetched.
1013  */
1014 gboolean
1015 gst_props_entry_get (const GstPropsEntry *entry, ...)
1016 {
1017   gboolean result;
1018   va_list var_args;
1019
1020   g_return_val_if_fail (entry != NULL, FALSE);
1021
1022   va_start (var_args, entry);
1023   result = gst_props_entry_getv (entry, FALSE, var_args);
1024   va_end (var_args);
1025
1026   return result;
1027 }
1028
1029 static gboolean
1030 gst_props_entry_get_safe (const GstPropsEntry *entry, ...)
1031 {
1032   gboolean result;
1033   va_list var_args;
1034
1035   g_return_val_if_fail (entry != NULL, FALSE);
1036
1037   va_start (var_args, entry);
1038   result = gst_props_entry_getv (entry, TRUE, var_args);
1039   va_end (var_args);
1040
1041   return result;
1042 }
1043
1044 static gboolean
1045 gst_props_getv (GstProps *props, gboolean safe, gchar *first_name, va_list var_args)
1046 {
1047   while (first_name) {
1048     const GstPropsEntry *entry = gst_props_get_entry (props, first_name);
1049     gboolean result;
1050
1051     if (!entry) return FALSE;
1052     GST_PROPS_ENTRY_READ (entry, var_args, FALSE, &result);
1053     if (!result) return FALSE;
1054
1055     first_name = va_arg (var_args, gchar *);
1056   }
1057   return TRUE;
1058 }
1059
1060 /**
1061  * gst_props_get:
1062  * @props: the props to query
1063  * @first_name: the first key
1064  * @...: a pointer to a datastructure that can hold the value.
1065  *
1066  * Gets the contents of the props into given key/value pairs.
1067  *
1068  * Returns: TRUE is the props entry could be fetched.
1069  */
1070 gboolean
1071 gst_props_get (GstProps *props, gchar *first_name, ...)
1072 {
1073   va_list var_args;
1074   gboolean ret;
1075
1076   va_start (var_args, first_name);
1077   ret = gst_props_getv (props, FALSE, first_name, var_args);
1078   va_end (var_args);
1079   
1080   return ret;
1081 }
1082
1083 /**
1084  * gst_props_get_safe:
1085  * @props: the props to query
1086  * @first_name: the first key
1087  * @...: a pointer to a datastructure that can hold the value.
1088  *
1089  * Gets the contents of the props into given key/value pairs.
1090  *
1091  * Returns: TRUE is the props entry could be fetched.
1092  */
1093 gboolean
1094 gst_props_get_safe (GstProps *props, gchar *first_name, ...)
1095 {
1096   va_list var_args;
1097   gboolean ret;
1098
1099   va_start (var_args, first_name);
1100   ret = gst_props_getv (props, TRUE, first_name, var_args);
1101   va_end (var_args);
1102   
1103   return ret;
1104 }
1105
1106 /**
1107  * gst_props_entry_get_int:
1108  * @entry: the props entry to query
1109  * @val: a pointer to a gint to hold the value.
1110  *
1111  * Get the contents of the entry into the given gint.
1112  *
1113  * Returns: TRUE is the value could be fetched. FALSE if the 
1114  * entry is not of given type.
1115  */
1116 gboolean
1117 gst_props_entry_get_int (const GstPropsEntry *entry, gint *val)
1118 {
1119   return gst_props_entry_get_safe (entry, GST_PROPS_INT_TYPE, val);
1120 }
1121
1122 /**
1123  * gst_props_entry_get_float:
1124  * @entry: the props entry to query
1125  * @val: a pointer to a gfloat to hold the value.
1126  *
1127  * Get the contents of the entry into the given gfloat.
1128  *
1129  * Returns: TRUE is the value could be fetched. FALSE if the 
1130  * entry is not of given type.
1131  */
1132 gboolean
1133 gst_props_entry_get_float (const GstPropsEntry *entry, gfloat *val)
1134 {
1135   return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_TYPE, val);
1136 }
1137
1138 /**
1139  * gst_props_entry_get_fourcc_int:
1140  * @entry: the props entry to query
1141  * @val: a pointer to a guint32 to hold the value.
1142  *
1143  * Get the contents of the entry into the given guint32.
1144  *
1145  * Returns: TRUE is the value could be fetched. FALSE if the 
1146  * entry is not of given type.
1147  */
1148 gboolean
1149 gst_props_entry_get_fourcc_int (const GstPropsEntry *entry, guint32 *val)
1150 {
1151   return gst_props_entry_get_safe (entry, GST_PROPS_FOURCC_TYPE, val);
1152 }
1153
1154 /**
1155  * gst_props_entry_get_boolean:
1156  * @entry: the props entry to query
1157  * @val: a pointer to a gboolean to hold the value.
1158  *
1159  * Get the contents of the entry into the given gboolean.
1160  *
1161  * Returns: TRUE is the value could be fetched. FALSE if the 
1162  * entry is not of given type.
1163  */
1164 gboolean
1165 gst_props_entry_get_boolean (const GstPropsEntry *entry, gboolean *val)
1166 {
1167   return gst_props_entry_get_safe (entry, GST_PROPS_BOOL_TYPE, val);
1168 }
1169
1170 /**
1171  * gst_props_entry_get_string:
1172  * @entry: the props entry to query
1173  * @val: a pointer to a gchar* to hold the value.
1174  *
1175  * Get the contents of the entry into the given gchar*.
1176  *
1177  * Returns: TRUE is the value could be fetched. FALSE if the 
1178  * entry is not of given type.
1179  */
1180 gboolean
1181 gst_props_entry_get_string (const GstPropsEntry *entry, const gchar **val)
1182 {
1183   return gst_props_entry_get_safe (entry, GST_PROPS_STRING_TYPE, val);
1184 }
1185
1186 /**
1187  * gst_props_entry_get_int_range:
1188  * @entry: the props entry to query
1189  * @min: a pointer to a gint to hold the minimun value.
1190  * @max: a pointer to a gint to hold the maximum value.
1191  *
1192  * Get the contents of the entry into the given gints.
1193  *
1194  * Returns: TRUE is the value could be fetched. FALSE if the 
1195  * entry is not of given type.
1196  */
1197 gboolean
1198 gst_props_entry_get_int_range (const GstPropsEntry *entry, gint *min, gint *max)
1199 {
1200   return gst_props_entry_get_safe (entry, GST_PROPS_INT_RANGE_TYPE, min, max);
1201 }
1202
1203 /**
1204  * gst_props_entry_get_float_range:
1205  * @entry: the props entry to query
1206  * @min: a pointer to a gfloat to hold the minimun value.
1207  * @max: a pointer to a gfloat to hold the maximum value.
1208  *
1209  * Get the contents of the entry into the given gfloats.
1210  *
1211  * Returns: TRUE is the value could be fetched. FALSE if the 
1212  * entry is not of given type.
1213  */
1214 gboolean
1215 gst_props_entry_get_float_range (const GstPropsEntry *entry, gfloat *min, gfloat *max)
1216 {
1217   return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_RANGE_TYPE, min, max);
1218 }
1219
1220 /**
1221  * gst_props_entry_get_list:
1222  * @entry: the props entry to query
1223  * @val: a pointer to a GList to hold the value.
1224  *
1225  * Get the contents of the entry into the given GList.
1226  *
1227  * Returns: TRUE is the value could be fetched. FALSE if the 
1228  * entry is not of given type.
1229  */
1230 gboolean
1231 gst_props_entry_get_list (const GstPropsEntry *entry, const GList **val)
1232 {
1233   return gst_props_entry_get_safe (entry, GST_PROPS_LIST_TYPE, val);
1234 }
1235
1236 /**
1237  * gst_props_merge:
1238  * @props: the property to merge into
1239  * @tomerge: the property to merge 
1240  *
1241  * Merge the properties of tomerge into props.
1242  *
1243  * Returns: the new merged property 
1244  */
1245 GstProps*
1246 gst_props_merge (GstProps *props, GstProps *tomerge)
1247 {
1248   GList *merge_props;
1249
1250   g_return_val_if_fail (props != NULL, NULL);
1251   g_return_val_if_fail (tomerge != NULL, NULL);
1252
1253   merge_props = tomerge->properties;
1254
1255   /* FIXME do proper merging here... */
1256   while (merge_props) {
1257     GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
1258
1259     gst_props_add_entry (props, entry);
1260           
1261     merge_props = g_list_next (merge_props);
1262   }
1263
1264   return props;
1265 }
1266
1267
1268 /* entry2 is always a list, entry1 never is */
1269 static gboolean
1270 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
1271 {
1272   GList *entrylist = entry2->data.list_data.entries;
1273   gboolean found = FALSE;
1274
1275   while (entrylist && !found) {
1276     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1277
1278     found |= gst_props_entry_check_compatibility (entry1, entry);
1279
1280     entrylist = g_list_next (entrylist);
1281   }
1282
1283   return found;
1284 }
1285
1286 static gboolean
1287 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
1288 {
1289   GST_DEBUG (GST_CAT_PROPERTIES,"compare: %s %s", g_quark_to_string (entry1->propid),
1290                              g_quark_to_string (entry2->propid));
1291
1292   if (entry2->propstype == GST_PROPS_LIST_TYPE && entry1->propstype != GST_PROPS_LIST_TYPE) {
1293     return gst_props_entry_check_list_compatibility (entry1, entry2);
1294   }
1295
1296   switch (entry1->propstype) {
1297     case GST_PROPS_LIST_TYPE:
1298     {
1299       GList *entrylist = entry1->data.list_data.entries;
1300       gboolean valid = TRUE;    /* innocent until proven guilty */
1301
1302       while (entrylist && valid) {
1303         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1304
1305         valid &= gst_props_entry_check_compatibility (entry, entry2);
1306         
1307         entrylist = g_list_next (entrylist);
1308       }
1309       
1310       return valid;
1311     }
1312     case GST_PROPS_INT_RANGE_TYPE:
1313       switch (entry2->propstype) {
1314         /* a - b   <--->   a - c */
1315         case GST_PROPS_INT_RANGE_TYPE:
1316           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
1317                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
1318         default:
1319           break;
1320       }
1321       break;
1322     case GST_PROPS_FLOAT_RANGE_TYPE:
1323       switch (entry2->propstype) {
1324         /* a - b   <--->   a - c */
1325         case GST_PROPS_FLOAT_RANGE_TYPE:
1326           return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
1327                   entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
1328         default:
1329           break;
1330       }
1331       break;
1332     case GST_PROPS_FOURCC_TYPE:
1333       switch (entry2->propstype) {
1334         /* b   <--->   a */
1335         case GST_PROPS_FOURCC_TYPE:
1336           GST_DEBUG(GST_CAT_PROPERTIES,"\"%4.4s\" <--> \"%4.4s\" ?",
1337                           (char*) &entry2->data.fourcc_data, (char*) &entry1->data.fourcc_data);
1338           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
1339         default:
1340           break;
1341       }
1342       break;
1343     case GST_PROPS_INT_TYPE:
1344       switch (entry2->propstype) {
1345         /* b   <--->   a - d */
1346         case GST_PROPS_INT_RANGE_TYPE:
1347           GST_DEBUG(GST_CAT_PROPERTIES,"%d <= %d <= %d ?",entry2->data.int_range_data.min,
1348                     entry1->data.int_data,entry2->data.int_range_data.max);
1349           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
1350                   entry2->data.int_range_data.max >= entry1->data.int_data);
1351         /* b   <--->   a */
1352         case GST_PROPS_INT_TYPE:
1353           GST_DEBUG(GST_CAT_PROPERTIES,"%d == %d ?",entry1->data.int_data,entry2->data.int_data);
1354           return (entry2->data.int_data == entry1->data.int_data);
1355         default:
1356           break;
1357       }
1358       break;
1359     case GST_PROPS_FLOAT_TYPE:
1360       switch (entry2->propstype) {
1361         /* b   <--->   a - d */
1362         case GST_PROPS_FLOAT_RANGE_TYPE:
1363           return (entry2->data.float_range_data.min <= entry1->data.float_data &&
1364                   entry2->data.float_range_data.max >= entry1->data.float_data);
1365         /* b   <--->   a */
1366         case GST_PROPS_FLOAT_TYPE:
1367           return (entry2->data.float_data == entry1->data.float_data);
1368         default:
1369           break;
1370       }
1371       break;
1372     case GST_PROPS_BOOL_TYPE:
1373       switch (entry2->propstype) {
1374         /* t   <--->   t */
1375         case GST_PROPS_BOOL_TYPE:
1376           return (entry2->data.bool_data == entry1->data.bool_data);
1377         default:
1378           break;
1379       }
1380     case GST_PROPS_STRING_TYPE:
1381       switch (entry2->propstype) {
1382         /* t   <--->   t */
1383         case GST_PROPS_STRING_TYPE:
1384           GST_DEBUG(GST_CAT_PROPERTIES,"\"%s\" <--> \"%s\" ?",
1385                           entry2->data.string_data.string, entry1->data.string_data.string);
1386           return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
1387         default:
1388           break;
1389       }
1390     default:
1391       break;
1392   }
1393
1394   return FALSE;
1395 }
1396
1397 /**
1398  * gst_props_check_compatibility:
1399  * @fromprops: a property
1400  * @toprops: a property
1401  *
1402  * Checks whether two capabilities are compatible.
1403  *
1404  * Returns: TRUE if compatible, FALSE otherwise
1405  */
1406 gboolean
1407 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
1408 {
1409   GList *sourcelist;
1410   GList *sinklist;
1411   gint missing = 0;
1412   gint more = 0;
1413   gboolean compatible = TRUE;
1414
1415   g_return_val_if_fail (fromprops != NULL, FALSE);
1416   g_return_val_if_fail (toprops != NULL, FALSE);
1417         
1418   sourcelist = fromprops->properties;
1419   sinklist   = toprops->properties;
1420
1421   while (sourcelist && sinklist && compatible) {
1422     GstPropsEntry *entry1;
1423     GstPropsEntry *entry2;
1424
1425     entry1 = (GstPropsEntry *)sourcelist->data;
1426     entry2 = (GstPropsEntry *)sinklist->data;
1427
1428     while (entry1->propid < entry2->propid) {
1429       more++;
1430       sourcelist = g_list_next (sourcelist);
1431       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
1432       else goto end;
1433     }
1434     while (entry1->propid > entry2->propid) {
1435       missing++;
1436       sinklist = g_list_next (sinklist);
1437       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
1438       else goto end;
1439     }
1440
1441     if (!gst_props_entry_check_compatibility (entry1, entry2)) {
1442         compatible = FALSE; 
1443         GST_DEBUG (GST_CAT_PROPERTIES, "%s are not compatible: ",
1444                    g_quark_to_string (entry1->propid));
1445     }
1446
1447     sourcelist = g_list_next (sourcelist);
1448     sinklist = g_list_next (sinklist);
1449   }
1450   if (sinklist && compatible) {
1451     GstPropsEntry *entry2;
1452     entry2 = (GstPropsEntry *)sinklist->data;
1453     missing++;
1454   }
1455 end:
1456
1457   if (missing)
1458     return FALSE;
1459
1460   return compatible;
1461 }
1462
1463 static GstPropsEntry*
1464 gst_props_entry_intersect (GstPropsEntry *entry1, GstPropsEntry *entry2)
1465 {
1466   GstPropsEntry *result = NULL;
1467
1468   /* try to move the ranges and lists first */
1469   switch (entry2->propstype) {
1470     case GST_PROPS_INT_RANGE_TYPE:
1471     case GST_PROPS_FLOAT_RANGE_TYPE:
1472     case GST_PROPS_LIST_TYPE:
1473     {
1474       GstPropsEntry *temp;
1475
1476       temp = entry1;
1477       entry1 = entry2;
1478       entry2 = temp;
1479     }
1480     default:
1481       break;
1482   }
1483
1484   switch (entry1->propstype) {
1485     case GST_PROPS_LIST_TYPE:
1486     {
1487       GList *entrylist = entry1->data.list_data.entries;
1488       GList *intersection = NULL;
1489
1490       while (entrylist) {
1491         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1492         GstPropsEntry *intersectentry;
1493
1494         intersectentry = gst_props_entry_intersect (entry2, entry);
1495
1496         if (intersectentry) {
1497           if (intersectentry->propstype == GST_PROPS_LIST_TYPE) {
1498             intersection = g_list_concat (intersection, 
1499                             g_list_copy (intersectentry->data.list_data.entries));
1500             /* set the list to NULL because the entries are concatenated to the above
1501              * list and we don't want to free them */
1502             intersectentry->data.list_data.entries = NULL;
1503             gst_props_entry_destroy (intersectentry);
1504           }
1505           else {
1506             intersection = g_list_prepend (intersection, intersectentry);
1507           }
1508         }
1509         entrylist = g_list_next (entrylist);
1510       }
1511       if (intersection) {
1512         /* check if the list only contains 1 element, if so, we can just copy it */
1513         if (g_list_next (intersection) == NULL) {
1514           result = (GstPropsEntry *) (intersection->data); 
1515           g_list_free (intersection);
1516         }
1517         /* else we need to create a new entry to hold the list */
1518         else {
1519           result = gst_props_alloc_entry ();
1520           result->propid = entry1->propid;
1521           result->propstype = GST_PROPS_LIST_TYPE;
1522           result->data.list_data.entries = g_list_reverse (intersection);
1523         }
1524       }
1525       return result;
1526     }
1527     case GST_PROPS_INT_RANGE_TYPE:
1528       switch (entry2->propstype) {
1529         /* a - b   <--->   a - c */
1530         case GST_PROPS_INT_RANGE_TYPE:
1531         {
1532           gint lower = MAX (entry1->data.int_range_data.min, entry2->data.int_range_data.min);
1533           gint upper = MIN (entry1->data.int_range_data.max, entry2->data.int_range_data.max);
1534
1535           if (lower <= upper) {
1536             result = gst_props_alloc_entry ();
1537             result->propid = entry1->propid;
1538
1539             if (lower == upper) {
1540               result->propstype = GST_PROPS_INT_TYPE;
1541               result->data.int_data = lower;
1542             }
1543             else {
1544               result->propstype = GST_PROPS_INT_RANGE_TYPE;
1545               result->data.int_range_data.min = lower;
1546               result->data.int_range_data.max = upper;
1547             }
1548           }
1549           break;
1550         }
1551         case GST_PROPS_LIST_TYPE:
1552         {
1553           GList *entries = entry2->data.list_data.entries;
1554           result = gst_props_alloc_entry ();
1555           result->propid = entry1->propid;
1556           result->propstype = GST_PROPS_LIST_TYPE;
1557           result->data.list_data.entries = NULL;
1558           while (entries) {
1559             GstPropsEntry * this = (GstPropsEntry *)entries->data;
1560             if (this->propstype != GST_PROPS_INT_TYPE) {
1561               /* no hope, this list doesn't even contain ints! */
1562               gst_props_entry_destroy (result);
1563               result = NULL;
1564               break;
1565             }
1566             if (this->data.int_data >= entry1->data.int_range_data.min &&
1567                 this->data.int_data <= entry1->data.int_range_data.max) {
1568               result->data.list_data.entries = g_list_append (result->data.list_data.entries,
1569                                                               gst_props_entry_copy (this));
1570             }
1571             entries = g_list_next (entries);
1572           }
1573           break;
1574         }
1575         case GST_PROPS_INT_TYPE:
1576         {
1577           if (entry1->data.int_range_data.min <= entry2->data.int_data && 
1578               entry1->data.int_range_data.max >= entry2->data.int_data) {
1579             result = gst_props_entry_copy (entry2);
1580           }
1581           break;
1582         }
1583         default:
1584           break;
1585       }
1586       break;
1587     case GST_PROPS_FLOAT_RANGE_TYPE:
1588       switch (entry2->propstype) {
1589         /* a - b   <--->   a - c */
1590         case GST_PROPS_FLOAT_RANGE_TYPE:
1591         {
1592           gfloat lower = MAX (entry1->data.float_range_data.min, entry2->data.float_range_data.min);
1593           gfloat upper = MIN (entry1->data.float_range_data.max, entry2->data.float_range_data.max);
1594
1595           if (lower <= upper) {
1596             result = gst_props_alloc_entry ();
1597             result->propid = entry1->propid;
1598
1599             if (lower == upper) {
1600               result->propstype = GST_PROPS_FLOAT_TYPE;
1601               result->data.float_data = lower;
1602             }
1603             else {
1604               result->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
1605               result->data.float_range_data.min = lower;
1606               result->data.float_range_data.max = upper;
1607             }
1608           }
1609           break;
1610         }
1611         case GST_PROPS_FLOAT_TYPE:
1612           if (entry1->data.float_range_data.min <= entry2->data.float_data && 
1613               entry1->data.float_range_data.max >= entry2->data.float_data) {
1614             result = gst_props_entry_copy (entry2);
1615           }
1616         default:
1617           break;
1618       }
1619       break;
1620     case GST_PROPS_FOURCC_TYPE:
1621       switch (entry2->propstype) {
1622         /* b   <--->   a */
1623         case GST_PROPS_FOURCC_TYPE:
1624           if (entry1->data.fourcc_data == entry2->data.fourcc_data)
1625             result = gst_props_entry_copy (entry1);
1626         default:
1627           break;
1628       }
1629       break;
1630     case GST_PROPS_INT_TYPE:
1631       switch (entry2->propstype) {
1632         /* b   <--->   a */
1633         case GST_PROPS_INT_TYPE:
1634           if (entry1->data.int_data == entry2->data.int_data)
1635             result = gst_props_entry_copy (entry1);
1636         default:
1637           break;
1638       }
1639       break;
1640     case GST_PROPS_FLOAT_TYPE:
1641       switch (entry2->propstype) {
1642         /* b   <--->   a */
1643         case GST_PROPS_FLOAT_TYPE:
1644           if (entry1->data.float_data == entry2->data.float_data)
1645             result = gst_props_entry_copy (entry1);
1646         default:
1647           break;
1648       }
1649       break;
1650     case GST_PROPS_BOOL_TYPE:
1651       switch (entry2->propstype) {
1652         /* t   <--->   t */
1653         case GST_PROPS_BOOL_TYPE:
1654           if (entry1->data.bool_data == entry2->data.bool_data)
1655             result = gst_props_entry_copy (entry1);
1656         default:
1657           break;
1658       }
1659     case GST_PROPS_STRING_TYPE:
1660       switch (entry2->propstype) {
1661         /* t   <--->   t */
1662         case GST_PROPS_STRING_TYPE:
1663           if (!strcmp (entry1->data.string_data.string, entry2->data.string_data.string))
1664             result = gst_props_entry_copy (entry1);
1665         default:
1666           break;
1667       }
1668     default:
1669       break;
1670   }
1671
1672   return result;
1673 }
1674
1675 /**
1676  * gst_props_intersect:
1677  * @props1: a property
1678  * @props2: another property
1679  *
1680  * Calculates the intersection bewteen two GstProps.
1681  *
1682  * Returns: a GstProps with the intersection or NULL if the 
1683  * intersection is empty.
1684  */
1685 GstProps*
1686 gst_props_intersect (GstProps *props1, GstProps *props2)
1687 {
1688   GList *props1list;
1689   GList *props2list;
1690   GstProps *intersection;
1691   GList *leftovers;
1692   GstPropsEntry *iprops = NULL;
1693
1694   intersection = gst_props_empty_new ();
1695   intersection->fixed = TRUE;
1696
1697   g_return_val_if_fail (props1 != NULL, NULL);
1698   g_return_val_if_fail (props2 != NULL, NULL);
1699         
1700   props1list = props1->properties;
1701   props2list = props2->properties;
1702
1703   while (props1list && props2list) {
1704     GstPropsEntry *entry1;
1705     GstPropsEntry *entry2;
1706
1707     entry1 = (GstPropsEntry *)props1list->data;
1708     entry2 = (GstPropsEntry *)props2list->data;
1709
1710     while (entry1->propid < entry2->propid) {
1711       GstPropsEntry *toadd;
1712
1713       GST_DEBUG (GST_CAT_PROPERTIES,"source is more specific in \"%s\"", g_quark_to_string (entry1->propid));
1714
1715       toadd = gst_props_entry_copy (entry1);
1716       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1717         intersection->fixed = FALSE;
1718
1719       intersection->properties = g_list_prepend (intersection->properties, toadd);
1720
1721       props1list = g_list_next (props1list);
1722       if (props1list) 
1723         entry1 = (GstPropsEntry *)props1list->data;
1724       else 
1725         goto end;
1726     }
1727     while (entry1->propid > entry2->propid) {
1728       GstPropsEntry *toadd;
1729
1730       toadd = gst_props_entry_copy (entry2);
1731       if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1732         intersection->fixed = FALSE;
1733
1734       intersection->properties = g_list_prepend (intersection->properties, toadd);
1735
1736       props2list = g_list_next (props2list);
1737       if (props2list)
1738         entry2 = (GstPropsEntry *)props2list->data;
1739       else 
1740         goto end;
1741     }
1742     /* at this point we are talking about the same property */
1743     iprops = gst_props_entry_intersect (entry1, entry2);
1744
1745     if (iprops) {
1746       if (GST_PROPS_ENTRY_IS_VARIABLE (iprops))
1747         intersection->fixed = FALSE;
1748       intersection->properties = g_list_prepend (intersection->properties, iprops);
1749     }
1750     else {
1751       gst_props_unref (intersection);
1752       return NULL;
1753     }
1754
1755     props1list = g_list_next (props1list);
1756     props2list = g_list_next (props2list);
1757   }
1758
1759 end:
1760   /* at this point one of the lists could contain leftover properties */
1761   if (props1list)
1762     leftovers = props1list;
1763   else if (props2list)
1764     leftovers = props2list;
1765   else 
1766     leftovers = NULL;
1767
1768   while (leftovers) {
1769     GstPropsEntry *entry;
1770
1771     entry = (GstPropsEntry *) leftovers->data;
1772     if (GST_PROPS_ENTRY_IS_VARIABLE (entry))
1773       intersection->fixed = FALSE;
1774     intersection->properties = g_list_prepend (intersection->properties, gst_props_entry_copy (entry));
1775
1776     leftovers = g_list_next (leftovers);
1777   }
1778
1779   intersection->properties = g_list_reverse (intersection->properties);
1780
1781   return intersection;
1782 }
1783
1784 /**
1785  * gst_props_normalize:
1786  * @props: a property
1787  *
1788  * Unrolls all lists in the given GstProps. This is usefull if you
1789  * want to loop over the props.
1790  *
1791  * Returns: A GList with the unrolled props entries.
1792  */
1793 GList*
1794 gst_props_normalize (GstProps *props)
1795 {
1796   GList *entries;
1797   GList *result = NULL;
1798
1799   if (!props) 
1800     return NULL;
1801
1802   entries = props->properties;
1803
1804   while (entries) {
1805     GstPropsEntry *entry = (GstPropsEntry *) entries->data;
1806
1807     if (entry->propstype == GST_PROPS_LIST_TYPE) {
1808       GList *list_entries = entry->data.list_data.entries;
1809
1810       while (list_entries) {
1811         GstPropsEntry *list_entry = (GstPropsEntry *) list_entries->data;
1812         GstPropsEntry *new_entry;
1813         GstProps *newprops;
1814         GList *lentry;
1815
1816         newprops = gst_props_empty_new ();
1817         newprops->properties = gst_props_list_copy (props->properties);
1818         lentry = g_list_find_custom (newprops->properties, GINT_TO_POINTER (list_entry->propid), props_find_func);
1819         if (lentry) {
1820           GList *new_list = NULL;
1821
1822           new_entry = (GstPropsEntry *) lentry->data;
1823           memcpy (new_entry, list_entry, sizeof (GstPropsEntry));
1824
1825           new_list = gst_props_normalize (newprops);
1826           result = g_list_concat (new_list, result);
1827         }
1828         else {
1829           result = g_list_append (result, newprops);
1830         }
1831         
1832         list_entries = g_list_next (list_entries);      
1833       }
1834       /* we break out of the loop because the other lists are
1835        * unrolled in the recursive call */
1836       break;
1837     }
1838     entries = g_list_next (entries);
1839   }
1840   if (!result) {
1841     result = g_list_prepend (result, props);
1842   }
1843   else {
1844     result = g_list_reverse (result);
1845     gst_props_unref (props);
1846   }
1847   return result;
1848 }
1849
1850 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
1851 static xmlNodePtr
1852 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
1853 {
1854   xmlNodePtr subtree;
1855   gchar *str;
1856
1857   switch (entry->propstype) {
1858     case GST_PROPS_INT_TYPE: 
1859       subtree = xmlNewChild (parent, NULL, "int", NULL);
1860       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1861       str = g_strdup_printf ("%d", entry->data.int_data);
1862       xmlNewProp (subtree, "value", str);
1863       g_free(str);
1864       break;
1865     case GST_PROPS_INT_RANGE_TYPE: 
1866       subtree = xmlNewChild (parent, NULL, "range", NULL);
1867       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1868       str = g_strdup_printf ("%d", entry->data.int_range_data.min);
1869       xmlNewProp (subtree, "min", str);
1870       g_free(str);
1871       str = g_strdup_printf ("%d", entry->data.int_range_data.max);
1872       xmlNewProp (subtree, "max", str);
1873       g_free(str);
1874       break;
1875     case GST_PROPS_FLOAT_TYPE: 
1876       subtree = xmlNewChild (parent, NULL, "float", NULL);
1877       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1878       str = g_strdup_printf ("%f", entry->data.float_data);
1879       xmlNewProp (subtree, "value", str);
1880       g_free(str);
1881       break;
1882     case GST_PROPS_FLOAT_RANGE_TYPE: 
1883       subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
1884       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1885       str = g_strdup_printf ("%f", entry->data.float_range_data.min);
1886       xmlNewProp (subtree, "min", str);
1887       g_free(str);
1888       str = g_strdup_printf ("%f", entry->data.float_range_data.max);
1889       xmlNewProp (subtree, "max", str);
1890       g_free(str);
1891       break;
1892     case GST_PROPS_FOURCC_TYPE: 
1893       str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
1894       xmlAddChild (parent, xmlNewComment (str));
1895       g_free(str);
1896       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
1897       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1898       str = g_strdup_printf ("%08x", entry->data.fourcc_data);
1899       xmlNewProp (subtree, "hexvalue", str);
1900       g_free(str);
1901       break;
1902     case GST_PROPS_BOOL_TYPE: 
1903       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
1904       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1905       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
1906       break;
1907     case GST_PROPS_STRING_TYPE: 
1908       subtree = xmlNewChild (parent, NULL, "string", NULL);
1909       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1910       xmlNewProp (subtree, "value", entry->data.string_data.string);
1911       break;
1912     default:
1913       g_warning ("trying to save unknown property type %d", entry->propstype);
1914       break;
1915   }
1916
1917   return parent;
1918 }
1919
1920 /**
1921  * gst_props_save_thyself:
1922  * @props: a property to save
1923  * @parent: the parent XML tree
1924  *
1925  * Saves the property into an XML representation.
1926  *
1927  * Returns: the new XML tree
1928  */
1929 xmlNodePtr
1930 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
1931 {
1932   GList *proplist;
1933   xmlNodePtr subtree;
1934
1935   g_return_val_if_fail (props != NULL, NULL);
1936
1937   proplist = props->properties;
1938
1939   while (proplist) {
1940     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1941
1942     switch (entry->propstype) {
1943       case GST_PROPS_LIST_TYPE: 
1944         subtree = xmlNewChild (parent, NULL, "list", NULL);
1945         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1946         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
1947         break;
1948       default:
1949         gst_props_save_thyself_func (entry, parent);
1950     }
1951
1952     proplist = g_list_next (proplist);
1953   }
1954   
1955   return parent;
1956 }
1957
1958 static GstPropsEntry*
1959 gst_props_load_thyself_func (xmlNodePtr field)
1960 {
1961   GstPropsEntry *entry;
1962   gchar *prop;
1963
1964   entry = gst_props_alloc_entry ();
1965
1966   if (!strcmp(field->name, "int")) {
1967     entry->propstype = GST_PROPS_INT_TYPE;
1968     prop = xmlGetProp(field, "name");
1969     entry->propid = g_quark_from_string (prop);
1970     g_free (prop);
1971     prop = xmlGetProp(field, "value");
1972     sscanf (prop, "%d", &entry->data.int_data);
1973     g_free (prop);
1974   }
1975   else if (!strcmp(field->name, "range")) {
1976     entry->propstype = GST_PROPS_INT_RANGE_TYPE;
1977     prop = xmlGetProp(field, "name");
1978     entry->propid = g_quark_from_string (prop);
1979     g_free (prop);
1980     prop = xmlGetProp (field, "min");
1981     sscanf (prop, "%d", &entry->data.int_range_data.min);
1982     g_free (prop);
1983     prop = xmlGetProp (field, "max");
1984     sscanf (prop, "%d", &entry->data.int_range_data.max);
1985     g_free (prop);
1986   }
1987   else if (!strcmp(field->name, "float")) {
1988     entry->propstype = GST_PROPS_FLOAT_TYPE;
1989     prop = xmlGetProp(field, "name");
1990     entry->propid = g_quark_from_string (prop);
1991     g_free (prop);
1992     prop = xmlGetProp(field, "value");
1993     sscanf (prop, "%f", &entry->data.float_data);
1994     g_free (prop);
1995   }
1996   else if (!strcmp(field->name, "floatrange")) {
1997     entry->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
1998     prop = xmlGetProp(field, "name");
1999     entry->propid = g_quark_from_string (prop);
2000     g_free (prop);
2001     prop = xmlGetProp (field, "min");
2002     sscanf (prop, "%f", &entry->data.float_range_data.min);
2003     g_free (prop);
2004     prop = xmlGetProp (field, "max");
2005     sscanf (prop, "%f", &entry->data.float_range_data.max);
2006     g_free (prop);
2007   }
2008   else if (!strcmp(field->name, "boolean")) {
2009     entry->propstype = GST_PROPS_BOOL_TYPE;
2010     prop = xmlGetProp(field, "name");
2011     entry->propid = g_quark_from_string (prop);
2012     g_free (prop);
2013     prop = xmlGetProp (field, "value");
2014     if (!strcmp (prop, "false")) entry->data.bool_data = 0;
2015     else entry->data.bool_data = 1;
2016     g_free (prop);
2017   }
2018   else if (!strcmp(field->name, "fourcc")) {
2019     entry->propstype = GST_PROPS_FOURCC_TYPE;
2020     prop = xmlGetProp(field, "name");
2021     entry->propid = g_quark_from_string (prop);
2022     g_free (prop);
2023     prop = xmlGetProp (field, "hexvalue");
2024     sscanf (prop, "%08x", &entry->data.fourcc_data);
2025     g_free (prop);
2026   }
2027   else if (!strcmp(field->name, "string")) {
2028     entry->propstype = GST_PROPS_STRING_TYPE;
2029     prop = xmlGetProp(field, "name");
2030     entry->propid = g_quark_from_string (prop);
2031     g_free (prop);
2032     entry->data.string_data.string = xmlGetProp (field, "value");
2033   }
2034   else {
2035     g_mutex_lock (_gst_props_entries_chunk_lock);
2036     g_mem_chunk_free (_gst_props_entries_chunk, entry);
2037     g_mutex_unlock (_gst_props_entries_chunk_lock);
2038     entry = NULL;
2039   }
2040
2041   return entry;
2042 }
2043
2044 /**
2045  * gst_props_load_thyself:
2046  * @parent: the XML tree to load from
2047  *
2048  * Creates a new property out of an XML tree.
2049  *
2050  * Returns: the new property
2051  */
2052 GstProps*
2053 gst_props_load_thyself (xmlNodePtr parent)
2054 {
2055   GstProps *props;
2056   xmlNodePtr field = parent->xmlChildrenNode;
2057   gchar *prop;
2058
2059   props = gst_props_empty_new ();
2060
2061   while (field) {
2062     if (!strcmp (field->name, "list")) {
2063       GstPropsEntry *entry;
2064       xmlNodePtr subfield = field->xmlChildrenNode;
2065
2066       entry = gst_props_alloc_entry ();
2067       prop = xmlGetProp (field, "name");
2068       entry->propid = g_quark_from_string (prop);
2069       g_free (prop);
2070       entry->propstype = GST_PROPS_LIST_TYPE;
2071       entry->data.list_data.entries = NULL;
2072
2073       while (subfield) {
2074         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
2075
2076         if (subentry)
2077           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
2078
2079         subfield = subfield->next;
2080       }
2081       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
2082       gst_props_add_entry (props, entry);
2083     }
2084     else {
2085       GstPropsEntry *entry;
2086
2087       entry = gst_props_load_thyself_func (field);
2088
2089       if (entry) 
2090         gst_props_add_entry (props, entry);
2091     }
2092     field = field->next;
2093   }
2094
2095   return props;
2096 }
2097 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */
2098