more leak fixes
[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
30 static gboolean         gst_props_entry_check_compatibility     (GstPropsEntry *entry1, GstPropsEntry *entry2);
31         
32 static guint _arg_len[] = {
33   0,  // GST_PROPS_END_ID_NUM = 0,
34   0,  // GST_PROPS_LIST_ID_NUM,
35   1,  // GST_PROPS_INT_ID_NUM,
36   2,  // GST_PROPS_INT_RANGE_ID_NUM,
37   1,  // GST_PROPS_FOURCC_ID_NUM,
38   1,  // GST_PROPS_BOOL_ID_NUM,
39 };
40
41 void 
42 _gst_props_initialize (void) 
43 {
44 }
45
46 static GstPropsEntry *
47 gst_props_create_entry (GstPropsFactory factory, gint *skipped)
48 {
49   GstPropsFactoryEntry tag;
50   GstPropsEntry *entry;
51   guint i=0;
52
53   entry = g_new0 (GstPropsEntry, 1);
54
55   tag = factory[i++];
56   switch (GPOINTER_TO_INT (tag)) {
57     case GST_PROPS_INT_ID:
58       entry->propstype = GST_PROPS_INT_ID_NUM;
59       entry->data.int_data = GPOINTER_TO_INT (factory[i++]);
60       break;
61     case GST_PROPS_INT_RANGE_ID:
62       entry->propstype = GST_PROPS_INT_RANGE_ID_NUM;
63       entry->data.int_range_data.min = GPOINTER_TO_INT (factory[i++]);
64       entry->data.int_range_data.max = GPOINTER_TO_INT (factory[i++]);
65       break;
66     case GST_PROPS_FOURCC_ID:
67       entry->propstype = GST_PROPS_FOURCC_ID_NUM;
68       entry->data.fourcc_data = GPOINTER_TO_INT (factory[i++]);
69       break;
70     case GST_PROPS_LIST_ID:
71       g_print("gstprops: list not allowed in list\n");
72       break;
73     case GST_PROPS_BOOL_ID:
74       entry->propstype = GST_PROPS_BOOL_ID_NUM;
75       entry->data.bool_data = GPOINTER_TO_INT (factory[i++]);
76       break;
77     default:
78       g_print("gstprops: unknown props id found\n");
79       g_free (entry);
80       entry = NULL;
81       break;
82   }
83
84   *skipped = i;
85
86   return entry;
87 }
88
89
90 static gint 
91 props_compare_func (gconstpointer a,
92                    gconstpointer b) 
93 {
94   GstPropsEntry *entry1 = (GstPropsEntry *)a;
95   GstPropsEntry *entry2 = (GstPropsEntry *)b;
96
97   return (entry1->propid - entry2->propid);
98 }
99
100 /**
101  * gst_props_register:
102  * @factory: the factory to register
103  *
104  * Register the factory. 
105  *
106  * Returns: the new property created from the factory
107  */
108 GstProps *
109 gst_props_register (GstPropsFactory factory)
110 {
111   guint dummy;
112
113   return gst_props_register_count (factory, &dummy);
114 }
115
116 /**
117  * gst_props_register_count:
118  * @factory: the factory to register
119  * @counter: count how many fields were consumed
120  *
121  * Register the factory. 
122  *
123  * Returns: the new property created from the factory
124  */
125 GstProps *
126 gst_props_register_count (GstPropsFactory factory, guint *counter)
127 {
128   GstPropsFactoryEntry tag;
129   gint i = 0;
130   GstProps *props = NULL;
131   gint skipped;
132   
133   g_return_val_if_fail (factory != NULL, NULL);
134
135   tag = factory[i++];
136
137   if (!tag) goto end;
138
139   props = g_new0 (GstProps, 1);
140   g_return_val_if_fail (props != NULL, NULL);
141
142   props->properties = NULL;
143   
144   while (tag) {
145     GQuark quark;
146     GstPropsEntry *entry;
147     
148     if (tag < GST_PROPS_LAST_ID) {
149       g_warning ("properties seem to be wrong\n");
150       return NULL;
151     }
152       
153     quark = g_quark_from_string ((gchar *)tag);
154
155     tag = factory[i];
156     switch (GPOINTER_TO_INT (tag)) {
157       case GST_PROPS_LIST_ID: 
158       {
159         GstPropsEntry *list_entry;
160
161         entry = g_new0 (GstPropsEntry, 1);
162         entry->propid = quark;
163         entry->propstype = GST_PROPS_LIST_ID_NUM;
164         entry->data.list_data.entries = NULL;
165
166         i++; // skip list tag
167         tag = factory[i];
168         while (tag) {
169           list_entry = gst_props_create_entry (&factory[i], &skipped);
170           list_entry->propid = quark;
171           i += skipped;
172           tag = factory[i];
173           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, list_entry);
174         }
175         entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
176         i++; //skip NULL (list end)
177         break;
178       }
179       default:
180       {
181         entry = gst_props_create_entry (&factory[i], &skipped);
182         entry->propid = quark;
183         i += skipped;
184         break;
185       }
186     }
187     props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
188      
189     tag = factory[i++];
190   }
191
192 end:
193   *counter = i;
194
195   return props;
196 }
197
198 /**
199  * gst_props_new:
200  * @entry: the property entries for the property
201  * @...: the property entries for the property
202  *
203  * Create a new property from the list of entries.
204  *
205  * Returns: the new property created from the list of entries
206  */
207 GstProps *
208 gst_props_new (GstPropsFactoryEntry entry, ...)
209 {
210   va_list var_args;
211   GstPropsFactoryEntry value;
212   gint i = 0;
213   gint size, skip;
214   GstPropsFactoryEntry *factory;
215   gboolean inlist = FALSE;
216   GstProps *props;
217
218 #define add_value(value) {\
219     GST_DEBUG (0,"%d %p\n", i, value);\
220     factory[i++] = value;  \
221     if (i >= size) {       \
222       size += 16;          \
223       factory = (GstPropsFactoryEntry *) g_realloc (factory, size*sizeof(GstPropsFactoryEntry));\
224     }\
225 }
226
227   size = 16;
228   factory = (GstPropsFactoryEntry *) g_malloc (size*sizeof(GstPropsFactoryEntry));
229
230   va_start (var_args, entry);
231   // property name
232   value = (GstPropsFactoryEntry) entry;
233   
234   // properties
235   while (value) {
236     if (!inlist) {
237       // add name
238       add_value (value);
239
240       // get value
241       value = va_arg (var_args, GstPropsFactoryEntry);
242     }
243     switch (GPOINTER_TO_INT (value)) {
244       case GST_PROPS_END_ID: 
245         g_assert (inlist == TRUE);
246
247         inlist = FALSE;
248         skip = 0;
249         break;
250       case GST_PROPS_LIST_ID: 
251       {
252         g_assert (inlist == FALSE);
253
254         skip = 0;
255         inlist = TRUE;
256         break;
257       }
258       default:
259         skip = _arg_len[GPOINTER_TO_INT (value)];
260         break;
261     }
262     do {
263       add_value (value);
264       value = va_arg (var_args, GstPropsFactoryEntry);
265     }
266     while (skip--);
267   }
268   factory[i++] = NULL;
269
270   props = gst_props_register (factory);
271
272   return props;
273 }
274
275 /**
276  * gst_props_merge:
277  * @props: the property to merge into
278  * @tomerge: the property to merge 
279  *
280  * Merge the properties of tomerge into props.
281  *
282  * Returns: the new merged property 
283  */
284 GstProps*
285 gst_props_merge (GstProps *props, GstProps *tomerge)
286 {
287   GList *merge_props;
288
289   g_return_val_if_fail (props != NULL, NULL);
290   g_return_val_if_fail (tomerge != NULL, NULL);
291
292   merge_props = tomerge->properties;
293
294   // FIXME do proper merging here...
295   while (merge_props) {
296     GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
297
298     props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
299           
300     merge_props = g_list_next (merge_props);
301   }
302
303   return props;
304 }
305
306
307 /* entry2 is always a list, entry1 never is */
308 static gboolean
309 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
310 {
311   GList *entrylist = entry2->data.list_data.entries;
312   gboolean found = FALSE;
313
314   while (entrylist && !found) {
315     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
316
317     found |= gst_props_entry_check_compatibility (entry1, entry);
318
319     entrylist = g_list_next (entrylist);
320   }
321
322   return found;
323 }
324
325 static gboolean
326 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
327 {
328   GST_DEBUG (0,"compare: %s %s\n", g_quark_to_string (entry1->propid),
329                              g_quark_to_string (entry2->propid));
330   switch (entry1->propstype) {
331     case GST_PROPS_LIST_ID_NUM:
332     {
333       GList *entrylist = entry1->data.list_data.entries;
334       gboolean valid = TRUE;    // innocent until proven guilty
335
336       while (entrylist && valid) {
337         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
338
339         valid &= gst_props_entry_check_compatibility (entry, entry2);
340         
341         entrylist = g_list_next (entrylist);
342       }
343       
344       return valid;
345     }
346     case GST_PROPS_INT_RANGE_ID_NUM:
347       switch (entry2->propstype) {
348         // a - b   <--->   a - c
349         case GST_PROPS_INT_RANGE_ID_NUM:
350           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
351                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
352         case GST_PROPS_LIST_ID_NUM:
353           return gst_props_entry_check_list_compatibility (entry1, entry2);
354         default:
355           return FALSE;
356       }
357       break;
358     case GST_PROPS_FOURCC_ID_NUM:
359       switch (entry2->propstype) {
360         // b   <--->   a
361         case GST_PROPS_FOURCC_ID_NUM:
362           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
363         // b   <--->   a,b,c
364         case GST_PROPS_LIST_ID_NUM:
365           return gst_props_entry_check_list_compatibility (entry1, entry2);
366         default:
367           return FALSE;
368       }
369       break;
370     case GST_PROPS_INT_ID_NUM:
371       switch (entry2->propstype) {
372         // b   <--->   a - d
373         case GST_PROPS_INT_RANGE_ID_NUM:
374           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
375                   entry2->data.int_range_data.max >= entry1->data.int_data);
376         // b   <--->   a
377         case GST_PROPS_INT_ID_NUM:
378           return (entry2->data.int_data == entry1->data.int_data);
379         // b   <--->   a,b,c
380         case GST_PROPS_LIST_ID_NUM:
381           return gst_props_entry_check_list_compatibility (entry1, entry2);
382         default:
383           return FALSE;
384       }
385       break;
386     case GST_PROPS_BOOL_ID_NUM:
387       switch (entry2->propstype) {
388         // t   <--->   t
389         case GST_PROPS_BOOL_ID_NUM:
390           return (entry2->data.bool_data == entry1->data.bool_data);
391         case GST_PROPS_LIST_ID_NUM:
392           return gst_props_entry_check_list_compatibility (entry1, entry2);
393         default:
394           return FALSE;
395       }
396     default:
397       break;
398   }
399
400   return FALSE;
401 }
402
403 /**
404  * gst_props_check_compatibility:
405  * @fromprops: a property
406  * @toprops: a property
407  *
408  * Checks whether two capabilities are compatible.
409  *
410  * Returns: TRUE if compatible, FALSE otherwise
411  */
412 gboolean
413 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
414 {
415   GList *sourcelist;
416   GList *sinklist;
417   gint missing = 0;
418   gint more = 0;
419   gboolean compatible = TRUE;
420
421   g_return_val_if_fail (fromprops != NULL, FALSE);
422   g_return_val_if_fail (toprops != NULL, FALSE);
423         
424   sourcelist = fromprops->properties;
425   sinklist   = toprops->properties;
426
427   while (sourcelist && sinklist && compatible) {
428     GstPropsEntry *entry1;
429     GstPropsEntry *entry2;
430
431     entry1 = (GstPropsEntry *)sourcelist->data;
432     entry2 = (GstPropsEntry *)sinklist->data;
433
434     while (entry1->propid < entry2->propid) {
435       GST_DEBUG (0,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
436       more++;
437       sourcelist = g_list_next (sourcelist);
438       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
439       else goto end;
440     }
441     while (entry1->propid > entry2->propid) {
442       GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
443       missing++;
444       sinklist = g_list_next (sinklist);
445       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
446       else goto end;
447     }
448
449     compatible &= gst_props_entry_check_compatibility (entry1, entry2);
450
451     sourcelist = g_list_next (sourcelist);
452     sinklist = g_list_next (sinklist);
453   }
454   if (sinklist) {
455     GstPropsEntry *entry2;
456     entry2 = (GstPropsEntry *)sinklist->data;
457     missing++;
458     GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
459   }
460 end:
461
462   if (missing)
463     return FALSE;
464
465   return compatible;
466 }
467
468 static xmlNodePtr
469 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
470 {
471   xmlNodePtr subtree;
472   gchar *str;
473
474   switch (entry->propstype) {
475     case GST_PROPS_INT_ID_NUM: 
476       subtree = xmlNewChild (parent, NULL, "int", NULL);
477       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
478       str = g_strdup_printf ("%d", entry->data.int_data);
479       xmlNewProp (subtree, "value", str);
480       g_free(str);
481       break;
482     case GST_PROPS_INT_RANGE_ID_NUM: 
483       subtree = xmlNewChild (parent, NULL, "range", NULL);
484       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
485       str = g_strdup_printf ("%d", entry->data.int_range_data.min);
486       xmlNewProp (subtree, "min", str);
487       g_free(str);
488       str = g_strdup_printf ("%d", entry->data.int_range_data.max);
489       xmlNewProp (subtree, "max", str);
490       g_free(str);
491       break;
492     case GST_PROPS_FOURCC_ID_NUM: 
493       str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
494       xmlAddChild (parent, xmlNewComment (str));
495       g_free(str);
496       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
497       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
498       str = g_strdup_printf ("%08x", entry->data.fourcc_data);
499       xmlNewProp (subtree, "hexvalue", str);
500       g_free(str);
501       break;
502     case GST_PROPS_BOOL_ID_NUM: 
503       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
504       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
505       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
506       break;
507     default:
508       break;
509   }
510
511   return parent;
512 }
513
514 /**
515  * gst_props_save_thyself:
516  * @props: a property to save
517  * @parent: the parent XML tree
518  *
519  * Saves the property into an XML representation.
520  *
521  * Returns: the new XML tree
522  */
523 xmlNodePtr
524 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
525 {
526   GList *proplist;
527   xmlNodePtr subtree;
528
529   g_return_val_if_fail (props != NULL, NULL);
530
531   proplist = props->properties;
532
533   while (proplist) {
534     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
535
536     switch (entry->propstype) {
537       case GST_PROPS_LIST_ID_NUM: 
538         subtree = xmlNewChild (parent, NULL, "list", NULL);
539         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
540         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
541       default:
542         gst_props_save_thyself_func (entry, parent);
543     }
544
545     proplist = g_list_next (proplist);
546   }
547   
548   return parent;
549 }
550
551 static GstPropsEntry*
552 gst_props_load_thyself_func (xmlNodePtr field)
553 {
554   GstPropsEntry *entry;
555   gchar *prop;
556
557   entry = g_new0 (GstPropsEntry, 1);
558
559   if (!strcmp(field->name, "int")) {
560     entry->propstype = GST_PROPS_INT_ID_NUM;
561     prop = xmlGetProp(field, "name");
562     entry->propid = g_quark_from_string (prop);
563     g_free (prop);
564     prop = xmlGetProp(field, "value");
565     sscanf (prop, "%d", &entry->data.int_data);
566     g_free (prop);
567   }
568   else if (!strcmp(field->name, "range")) {
569     entry->propstype = GST_PROPS_INT_RANGE_ID_NUM;
570     prop = xmlGetProp(field, "name");
571     entry->propid = g_quark_from_string (prop);
572     g_free (prop);
573     prop = xmlGetProp (field, "min");
574     sscanf (prop, "%d", &entry->data.int_range_data.min);
575     g_free (prop);
576     prop = xmlGetProp (field, "min");
577     sscanf (prop, "%d", &entry->data.int_range_data.max);
578     g_free (prop);
579   }
580   else if (!strcmp(field->name, "boolean")) {
581     entry->propstype = GST_PROPS_BOOL_ID_NUM;
582     prop = xmlGetProp(field, "name");
583     entry->propid = g_quark_from_string (prop);
584     g_free (prop);
585     prop = xmlGetProp (field, "value");
586     if (!strcmp (prop, "false")) entry->data.bool_data = 0;
587     else entry->data.bool_data = 1;
588     g_free (prop);
589   }
590   else if (!strcmp(field->name, "fourcc")) {
591     entry->propstype = GST_PROPS_FOURCC_ID_NUM;
592     prop = xmlGetProp(field, "name");
593     entry->propid = g_quark_from_string (prop);
594     g_free (prop);
595     prop = xmlGetProp (field, "hexvalue");
596     sscanf (prop, "%08x", &entry->data.fourcc_data);
597     g_free (prop);
598   }
599
600   return entry;
601 }
602
603 /**
604  * gst_props_load_thyself:
605  * @parent: the XML tree to load from
606  *
607  * Creates a new property out of an XML tree.
608  *
609  * Returns: the new property
610  */
611 GstProps*
612 gst_props_load_thyself (xmlNodePtr parent)
613 {
614   GstProps *props = g_new0 (GstProps, 1);
615   xmlNodePtr field = parent->childs;
616   gchar *prop;
617
618   while (field) {
619     if (!strcmp (field->name, "list")) {
620       GstPropsEntry *entry;
621       xmlNodePtr subfield = field->childs;
622
623       entry = g_new0 (GstPropsEntry, 1);
624       entry->propstype = GST_PROPS_LIST_ID_NUM;
625       prop = xmlGetProp (field, "name");
626       entry->propid = g_quark_from_string (prop);
627       g_free (prop);
628
629       while (subfield) {
630         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
631
632         entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
633
634         subfield = subfield->next;
635       }
636       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
637       props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
638     }
639     else {
640       GstPropsEntry *entry;
641
642       entry = gst_props_load_thyself_func (field);
643
644       props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
645     }
646     field = field->next;
647   }
648
649   return props;
650 }
651