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