Lots of updates to the plugins for caps negotiation.
[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, "%4.4s\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   g_return_val_if_fail (props != NULL, 0);
567   g_return_val_if_fail (name != NULL, 0);
568
569   quark = g_quark_from_string (name);
570
571   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
572
573   if (lentry) {
574     GstPropsEntry *thisentry;
575
576     thisentry = (GstPropsEntry *)lentry->data;
577
578     return thisentry->data.int_data;
579   }
580   
581   return 0;
582 }
583
584 /**
585  * gst_props_get_fourcc_int:
586  * @props: the props to get the fourcc value from
587  * @name: the name of the props entry to get.
588  *
589  * Get the named entry as a gulong fourcc.
590  *
591  * Returns: the fourcc value of the named entry, 0 if not found.
592  */
593 gulong
594 gst_props_get_fourcc_int (GstProps *props, const gchar *name)
595 {
596   GList *lentry;
597   GQuark quark;
598   
599   g_return_val_if_fail (props != NULL, 0);
600   g_return_val_if_fail (name != NULL, 0);
601
602   quark = g_quark_from_string (name);
603
604   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
605
606   if (lentry) {
607     GstPropsEntry *thisentry;
608
609     thisentry = (GstPropsEntry *)lentry->data;
610
611     return thisentry->data.fourcc_data;
612   }
613   
614   return 0;
615 }
616
617 /**
618  * gst_props_get_boolean:
619  * @props: the props to get the fourcc value from
620  * @name: the name of the props entry to get.
621  *
622  * Get the named entry as a boolean value.
623  *
624  * Returns: the boolean value of the named entry, 0 if not found.
625  */
626 gboolean
627 gst_props_get_boolean (GstProps *props, const gchar *name)
628 {
629   GList *lentry;
630   GQuark quark;
631   
632   g_return_val_if_fail (props != NULL, FALSE);
633   g_return_val_if_fail (name != NULL, FALSE);
634
635   quark = g_quark_from_string (name);
636
637   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
638
639   if (lentry) {
640     GstPropsEntry *thisentry;
641
642     thisentry = (GstPropsEntry *)lentry->data;
643
644     return thisentry->data.bool_data;
645   }
646   
647   return 0;
648 }
649
650 /**
651  * gst_props_get_string:
652  * @props: the props to get the fourcc value from
653  * @name: the name of the props entry to get.
654  *
655  * Get the named entry as a string value.
656  *
657  * Returns: the string value of the named entry, NULL if not found.
658  */
659 const gchar*
660 gst_props_get_string (GstProps *props, const gchar *name)
661 {
662   GList *lentry;
663   GQuark quark;
664   
665   g_return_val_if_fail (props != NULL, NULL);
666   g_return_val_if_fail (name != NULL, NULL);
667
668   quark = g_quark_from_string (name);
669
670   lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
671
672   if (lentry) {
673     GstPropsEntry *thisentry;
674
675     thisentry = (GstPropsEntry *)lentry->data;
676
677     return thisentry->data.string_data.string;
678   }
679   
680   return NULL;
681 }
682
683 /**
684  * gst_props_merge:
685  * @props: the property to merge into
686  * @tomerge: the property to merge 
687  *
688  * Merge the properties of tomerge into props.
689  *
690  * Returns: the new merged property 
691  */
692 GstProps*
693 gst_props_merge (GstProps *props, GstProps *tomerge)
694 {
695   GList *merge_props;
696
697   g_return_val_if_fail (props != NULL, NULL);
698   g_return_val_if_fail (tomerge != NULL, NULL);
699
700   merge_props = tomerge->properties;
701
702   // FIXME do proper merging here...
703   while (merge_props) {
704     GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
705
706     props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
707           
708     merge_props = g_list_next (merge_props);
709   }
710
711   return props;
712 }
713
714
715 /* entry2 is always a list, entry1 never is */
716 static gboolean
717 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
718 {
719   GList *entrylist = entry2->data.list_data.entries;
720   gboolean found = FALSE;
721
722   while (entrylist && !found) {
723     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
724
725     found |= gst_props_entry_check_compatibility (entry1, entry);
726
727     entrylist = g_list_next (entrylist);
728   }
729
730   return found;
731 }
732
733 static gboolean
734 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
735 {
736   GST_DEBUG (0,"compare: %s %s\n", g_quark_to_string (entry1->propid),
737                              g_quark_to_string (entry2->propid));
738   switch (entry1->propstype) {
739     case GST_PROPS_LIST_ID_NUM:
740     {
741       GList *entrylist = entry1->data.list_data.entries;
742       gboolean valid = TRUE;    // innocent until proven guilty
743
744       while (entrylist && valid) {
745         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
746
747         valid &= gst_props_entry_check_compatibility (entry, entry2);
748         
749         entrylist = g_list_next (entrylist);
750       }
751       
752       return valid;
753     }
754     case GST_PROPS_INT_RANGE_ID_NUM:
755       switch (entry2->propstype) {
756         // a - b   <--->   a - c
757         case GST_PROPS_INT_RANGE_ID_NUM:
758           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
759                   entry2->data.int_range_data.max >= entry1->data.int_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_FLOAT_RANGE_ID_NUM:
767       switch (entry2->propstype) {
768         // a - b   <--->   a - c
769         case GST_PROPS_FLOAT_RANGE_ID_NUM:
770           return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
771                   entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
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_FOURCC_ID_NUM:
779       switch (entry2->propstype) {
780         // b   <--->   a
781         case GST_PROPS_FOURCC_ID_NUM:
782           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
783         // b   <--->   a,b,c
784         case GST_PROPS_LIST_ID_NUM:
785           return gst_props_entry_check_list_compatibility (entry1, entry2);
786         default:
787           return FALSE;
788       }
789       break;
790     case GST_PROPS_INT_ID_NUM:
791       switch (entry2->propstype) {
792         // b   <--->   a - d
793         case GST_PROPS_INT_RANGE_ID_NUM:
794           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
795                   entry2->data.int_range_data.max >= entry1->data.int_data);
796         // b   <--->   a
797         case GST_PROPS_INT_ID_NUM:
798           return (entry2->data.int_data == entry1->data.int_data);
799         // b   <--->   a,b,c
800         case GST_PROPS_LIST_ID_NUM:
801           return gst_props_entry_check_list_compatibility (entry1, entry2);
802         default:
803           return FALSE;
804       }
805       break;
806     case GST_PROPS_FLOAT_ID_NUM:
807       switch (entry2->propstype) {
808         // b   <--->   a - d
809         case GST_PROPS_FLOAT_RANGE_ID_NUM:
810           return (entry2->data.float_range_data.min <= entry1->data.float_data &&
811                   entry2->data.float_range_data.max >= entry1->data.float_data);
812         // b   <--->   a
813         case GST_PROPS_FLOAT_ID_NUM:
814           return (entry2->data.float_data == entry1->data.float_data);
815         // b   <--->   a,b,c
816         case GST_PROPS_LIST_ID_NUM:
817           return gst_props_entry_check_list_compatibility (entry1, entry2);
818         default:
819           return FALSE;
820       }
821       break;
822     case GST_PROPS_BOOL_ID_NUM:
823       switch (entry2->propstype) {
824         // t   <--->   t
825         case GST_PROPS_BOOL_ID_NUM:
826           return (entry2->data.bool_data == entry1->data.bool_data);
827         case GST_PROPS_LIST_ID_NUM:
828           return gst_props_entry_check_list_compatibility (entry1, entry2);
829         default:
830           return FALSE;
831       }
832     case GST_PROPS_STRING_ID_NUM:
833       switch (entry2->propstype) {
834         // t   <--->   t
835         case GST_PROPS_STRING_ID_NUM:
836           return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
837         case GST_PROPS_LIST_ID_NUM:
838           return gst_props_entry_check_list_compatibility (entry1, entry2);
839         default:
840           return FALSE;
841       }
842     default:
843       break;
844   }
845
846   return FALSE;
847 }
848
849 /**
850  * gst_props_check_compatibility:
851  * @fromprops: a property
852  * @toprops: a property
853  *
854  * Checks whether two capabilities are compatible.
855  *
856  * Returns: TRUE if compatible, FALSE otherwise
857  */
858 gboolean
859 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
860 {
861   GList *sourcelist;
862   GList *sinklist;
863   gint missing = 0;
864   gint more = 0;
865   gboolean compatible = TRUE;
866
867   g_return_val_if_fail (fromprops != NULL, FALSE);
868   g_return_val_if_fail (toprops != NULL, FALSE);
869         
870   sourcelist = fromprops->properties;
871   sinklist   = toprops->properties;
872
873   while (sourcelist && sinklist && compatible) {
874     GstPropsEntry *entry1;
875     GstPropsEntry *entry2;
876
877     entry1 = (GstPropsEntry *)sourcelist->data;
878     entry2 = (GstPropsEntry *)sinklist->data;
879
880     while (entry1->propid < entry2->propid) {
881       GST_DEBUG (0,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
882       more++;
883       sourcelist = g_list_next (sourcelist);
884       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
885       else goto end;
886     }
887     while (entry1->propid > entry2->propid) {
888       GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
889       missing++;
890       sinklist = g_list_next (sinklist);
891       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
892       else goto end;
893     }
894
895     if (!gst_props_entry_check_compatibility (entry1, entry2)) {
896         compatible = FALSE;
897         GST_DEBUG (0, "%s are not compatible\n:",
898                    g_quark_to_string (entry1->propid));
899         gst_props_debug_entry (entry1);
900         gst_props_debug_entry (entry2);
901     }
902
903     sourcelist = g_list_next (sourcelist);
904     sinklist = g_list_next (sinklist);
905   }
906   if (sinklist && compatible) {
907     GstPropsEntry *entry2;
908     entry2 = (GstPropsEntry *)sinklist->data;
909     missing++;
910     GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
911   }
912 end:
913
914   if (missing)
915     return FALSE;
916
917   return compatible;
918 }
919
920 static xmlNodePtr
921 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
922 {
923   xmlNodePtr subtree;
924   gchar *str;
925
926   switch (entry->propstype) {
927     case GST_PROPS_INT_ID_NUM: 
928       subtree = xmlNewChild (parent, NULL, "int", NULL);
929       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
930       str = g_strdup_printf ("%d", entry->data.int_data);
931       xmlNewProp (subtree, "value", str);
932       g_free(str);
933       break;
934     case GST_PROPS_INT_RANGE_ID_NUM: 
935       subtree = xmlNewChild (parent, NULL, "range", NULL);
936       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
937       str = g_strdup_printf ("%d", entry->data.int_range_data.min);
938       xmlNewProp (subtree, "min", str);
939       g_free(str);
940       str = g_strdup_printf ("%d", entry->data.int_range_data.max);
941       xmlNewProp (subtree, "max", str);
942       g_free(str);
943       break;
944     case GST_PROPS_FLOAT_ID_NUM: 
945       subtree = xmlNewChild (parent, NULL, "float", NULL);
946       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
947       str = g_strdup_printf ("%f", entry->data.float_data);
948       xmlNewProp (subtree, "value", str);
949       g_free(str);
950       break;
951     case GST_PROPS_FLOAT_RANGE_ID_NUM: 
952       subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
953       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
954       str = g_strdup_printf ("%f", entry->data.float_range_data.min);
955       xmlNewProp (subtree, "min", str);
956       g_free(str);
957       str = g_strdup_printf ("%f", entry->data.float_range_data.max);
958       xmlNewProp (subtree, "max", str);
959       g_free(str);
960       break;
961     case GST_PROPS_FOURCC_ID_NUM: 
962       str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
963       xmlAddChild (parent, xmlNewComment (str));
964       g_free(str);
965       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
966       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
967       str = g_strdup_printf ("%08x", entry->data.fourcc_data);
968       xmlNewProp (subtree, "hexvalue", str);
969       g_free(str);
970       break;
971     case GST_PROPS_BOOL_ID_NUM: 
972       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
973       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
974       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
975       break;
976     case GST_PROPS_STRING_ID_NUM: 
977       subtree = xmlNewChild (parent, NULL, "string", NULL);
978       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
979       xmlNewProp (subtree, "value", entry->data.string_data.string);
980       break;
981     default:
982       break;
983   }
984
985   return parent;
986 }
987
988 /**
989  * gst_props_save_thyself:
990  * @props: a property to save
991  * @parent: the parent XML tree
992  *
993  * Saves the property into an XML representation.
994  *
995  * Returns: the new XML tree
996  */
997 xmlNodePtr
998 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
999 {
1000   GList *proplist;
1001   xmlNodePtr subtree;
1002
1003   g_return_val_if_fail (props != NULL, NULL);
1004
1005   proplist = props->properties;
1006
1007   while (proplist) {
1008     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1009
1010     switch (entry->propstype) {
1011       case GST_PROPS_LIST_ID_NUM: 
1012         subtree = xmlNewChild (parent, NULL, "list", NULL);
1013         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1014         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
1015       default:
1016         gst_props_save_thyself_func (entry, parent);
1017     }
1018
1019     proplist = g_list_next (proplist);
1020   }
1021   
1022   return parent;
1023 }
1024
1025 static GstPropsEntry*
1026 gst_props_load_thyself_func (xmlNodePtr field)
1027 {
1028   GstPropsEntry *entry;
1029   gchar *prop;
1030
1031   g_mutex_lock (_gst_props_entries_chunk_lock);
1032   entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
1033   g_mutex_unlock (_gst_props_entries_chunk_lock);
1034
1035   if (!strcmp(field->name, "int")) {
1036     entry->propstype = GST_PROPS_INT_ID_NUM;
1037     prop = xmlGetProp(field, "name");
1038     entry->propid = g_quark_from_string (prop);
1039     g_free (prop);
1040     prop = xmlGetProp(field, "value");
1041     sscanf (prop, "%d", &entry->data.int_data);
1042     g_free (prop);
1043   }
1044   else if (!strcmp(field->name, "range")) {
1045     entry->propstype = GST_PROPS_INT_RANGE_ID_NUM;
1046     prop = xmlGetProp(field, "name");
1047     entry->propid = g_quark_from_string (prop);
1048     g_free (prop);
1049     prop = xmlGetProp (field, "min");
1050     sscanf (prop, "%d", &entry->data.int_range_data.min);
1051     g_free (prop);
1052     prop = xmlGetProp (field, "max");
1053     sscanf (prop, "%d", &entry->data.int_range_data.max);
1054     g_free (prop);
1055   }
1056   else if (!strcmp(field->name, "float")) {
1057     entry->propstype = GST_PROPS_FLOAT_ID_NUM;
1058     prop = xmlGetProp(field, "name");
1059     entry->propid = g_quark_from_string (prop);
1060     g_free (prop);
1061     prop = xmlGetProp(field, "value");
1062     sscanf (prop, "%f", &entry->data.float_data);
1063     g_free (prop);
1064   }
1065   else if (!strcmp(field->name, "floatrange")) {
1066     entry->propstype = GST_PROPS_FLOAT_RANGE_ID_NUM;
1067     prop = xmlGetProp(field, "name");
1068     entry->propid = g_quark_from_string (prop);
1069     g_free (prop);
1070     prop = xmlGetProp (field, "min");
1071     sscanf (prop, "%f", &entry->data.float_range_data.min);
1072     g_free (prop);
1073     prop = xmlGetProp (field, "max");
1074     sscanf (prop, "%f", &entry->data.float_range_data.max);
1075     g_free (prop);
1076   }
1077   else if (!strcmp(field->name, "boolean")) {
1078     entry->propstype = GST_PROPS_BOOL_ID_NUM;
1079     prop = xmlGetProp(field, "name");
1080     entry->propid = g_quark_from_string (prop);
1081     g_free (prop);
1082     prop = xmlGetProp (field, "value");
1083     if (!strcmp (prop, "false")) entry->data.bool_data = 0;
1084     else entry->data.bool_data = 1;
1085     g_free (prop);
1086   }
1087   else if (!strcmp(field->name, "fourcc")) {
1088     entry->propstype = GST_PROPS_FOURCC_ID_NUM;
1089     prop = xmlGetProp(field, "name");
1090     entry->propid = g_quark_from_string (prop);
1091     g_free (prop);
1092     prop = xmlGetProp (field, "hexvalue");
1093     sscanf (prop, "%08x", &entry->data.fourcc_data);
1094     g_free (prop);
1095   }
1096   else if (!strcmp(field->name, "string")) {
1097     entry->propstype = GST_PROPS_STRING_ID_NUM;
1098     prop = xmlGetProp(field, "name");
1099     entry->propid = g_quark_from_string (prop);
1100     g_free (prop);
1101     entry->data.string_data.string = xmlGetProp (field, "value");
1102   }
1103   else {
1104     g_mutex_lock (_gst_props_entries_chunk_lock);
1105     g_mem_chunk_free (_gst_props_entries_chunk, entry);
1106     g_mutex_unlock (_gst_props_entries_chunk_lock);
1107     entry = NULL;
1108   }
1109
1110   return entry;
1111 }
1112
1113 /**
1114  * gst_props_load_thyself:
1115  * @parent: the XML tree to load from
1116  *
1117  * Creates a new property out of an XML tree.
1118  *
1119  * Returns: the new property
1120  */
1121 GstProps*
1122 gst_props_load_thyself (xmlNodePtr parent)
1123 {
1124   GstProps *props;
1125   xmlNodePtr field = parent->xmlChildrenNode;
1126   gchar *prop;
1127
1128   g_mutex_lock (_gst_props_chunk_lock);
1129   props = g_mem_chunk_alloc (_gst_props_chunk);
1130   g_mutex_unlock (_gst_props_chunk_lock);
1131
1132   props->properties = NULL;
1133   props->refcount = 1;
1134
1135   while (field) {
1136     if (!strcmp (field->name, "list")) {
1137       GstPropsEntry *entry;
1138       xmlNodePtr subfield = field->xmlChildrenNode;
1139
1140       g_mutex_lock (_gst_props_entries_chunk_lock);
1141       entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
1142       g_mutex_unlock (_gst_props_entries_chunk_lock);
1143
1144       entry->propstype = GST_PROPS_LIST_ID_NUM;
1145       entry->data.list_data.entries = NULL;
1146       prop = xmlGetProp (field, "name");
1147       entry->propid = g_quark_from_string (prop);
1148       g_free (prop);
1149
1150       while (subfield) {
1151         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
1152
1153         if (subentry)
1154           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
1155
1156         subfield = subfield->next;
1157       }
1158       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
1159       props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
1160     }
1161     else {
1162       GstPropsEntry *entry;
1163
1164       entry = gst_props_load_thyself_func (field);
1165
1166       if (entry) 
1167         props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
1168     }
1169     field = field->next;
1170   }
1171
1172   return props;
1173 }
1174
1175 gfloat* _gst_props_floatpointer (gfloat f)
1176 {
1177   return &f;
1178 }