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