Cleanup in gsttypes.c: removed the crazy GList of GHashTables, since the autoplugger...
[platform/upstream/gstreamer.git] / gst / gstprops.c
1 /* Gnome-Streamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #define DEBUG_ENABLED
21
22 #include "gstdebug.h"
23 #include "gstprops.h"
24 #include "gstpropsprivate.h"
25
26 static gboolean         gst_props_entry_check_compatibility     (GstPropsEntry *entry1, GstPropsEntry *entry2);
27         
28
29 void 
30 _gst_props_initialize (void) 
31 {
32 }
33
34 static GstPropsEntry *
35 gst_props_create_entry (GstPropsFactory factory, gint *skipped)
36 {
37   GstPropsFactoryEntry tag;
38   GstPropsEntry *entry;
39   guint i=0;
40
41   entry = g_new0 (GstPropsEntry, 1);
42
43   tag = factory[i++];
44   switch (GPOINTER_TO_INT (tag)) {
45     case GST_PROPS_INT_ID:
46       entry->propstype = GST_PROPS_INT_ID_NUM;
47       entry->data.int_data = GPOINTER_TO_INT (factory[i++]);
48       break;
49     case GST_PROPS_INT_RANGE_ID:
50       entry->propstype = GST_PROPS_INT_RANGE_ID_NUM;
51       entry->data.int_range_data.min = GPOINTER_TO_INT (factory[i++]);
52       entry->data.int_range_data.max = GPOINTER_TO_INT (factory[i++]);
53       break;
54     case GST_PROPS_FOURCC_ID:
55       entry->propstype = GST_PROPS_FOURCC_ID_NUM;
56       entry->data.fourcc_data = GPOINTER_TO_INT (factory[i++]);
57       break;
58     case GST_PROPS_LIST_ID:
59       g_print("gstprops: list not allowed in list\n");
60       break;
61     case GST_PROPS_BOOL_ID:
62       entry->propstype = GST_PROPS_BOOL_ID_NUM;
63       entry->data.bool_data = GPOINTER_TO_INT (factory[i++]);
64       break;
65     default:
66       g_print("gstprops: unknown props id found\n");
67       g_free (entry);
68       entry = NULL;
69       break;
70   }
71
72   *skipped = i;
73
74   return entry;
75 }
76
77
78 static gint 
79 props_compare_func (gconstpointer a,
80                    gconstpointer b) 
81 {
82   GstPropsEntry *entry1 = (GstPropsEntry *)a;
83   GstPropsEntry *entry2 = (GstPropsEntry *)b;
84
85   return (entry1->propid - entry2->propid);
86 }
87
88 /**
89  * gst_props_register:
90  * @factory: the factory to register
91  *
92  * Register the factory. 
93  *
94  * Returns: The registered capability
95  */
96 GstProps *
97 gst_props_register (GstPropsFactory factory)
98 {
99   GstPropsFactoryEntry tag;
100   gint i = 0;
101   GstProps *props;
102   gint skipped;
103   
104   g_return_val_if_fail (factory != NULL, NULL);
105
106   tag = factory[i++];
107
108   if (!tag) return NULL;
109
110   props = g_new0 (GstProps, 1);
111   g_return_val_if_fail (props != NULL, NULL);
112
113   props->properties = NULL;
114   
115   while (tag) {
116     GQuark quark;
117     GstPropsEntry *entry;
118     
119     quark = g_quark_from_string ((gchar *)tag);
120
121     tag = factory[i];
122     switch (GPOINTER_TO_INT (tag)) {
123       case GST_PROPS_LIST_ID: 
124       {
125         GstPropsEntry *list_entry;
126
127         entry = g_new0 (GstPropsEntry, 1);
128         entry->propid = quark;
129         entry->propstype = GST_PROPS_LIST_ID_NUM;
130         entry->data.list_data.entries = NULL;
131
132         i++; // skip list tag
133         tag = factory[i];
134         while (tag) {
135           list_entry = gst_props_create_entry (&factory[i], &skipped);
136           list_entry->propid = quark;
137           i += skipped;
138           tag = factory[i];
139           entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, list_entry);
140         }
141         entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
142         i++; //skip NULL (list end)
143         break;
144       }
145       default:
146       {
147         entry = gst_props_create_entry (&factory[i], &skipped);
148         entry->propid = quark;
149         i += skipped;
150         break;
151       }
152     }
153     props->properties = g_slist_insert_sorted (props->properties, entry, props_compare_func);
154      
155     tag = factory[i++];
156   }
157
158   return props;
159 }
160
161 /* entry2 is always a list, entry1 never is */
162 static gboolean
163 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
164 {
165   GList *entrylist = entry2->data.list_data.entries;
166   gboolean found = FALSE;
167
168   while (entrylist && !found) {
169     GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
170
171     found |= gst_props_entry_check_compatibility (entry1, entry);
172
173     entrylist = g_list_next (entrylist);
174   }
175
176   return found;
177 }
178
179 static gboolean
180 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
181 {
182   DEBUG ("compare: %s %s\n", g_quark_to_string (entry1->propid),
183                              g_quark_to_string (entry2->propid));
184   switch (entry1->propstype) {
185     case GST_PROPS_LIST_ID_NUM:
186     {
187       GList *entrylist = entry1->data.list_data.entries;
188       gboolean valid = TRUE;    // innocent until proven guilty
189
190       while (entrylist && valid) {
191         GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
192
193         valid &= gst_props_entry_check_compatibility (entry, entry2);
194         
195         entrylist = g_list_next (entrylist);
196       }
197       
198       return valid;
199     }
200     case GST_PROPS_INT_RANGE_ID_NUM:
201       switch (entry2->propstype) {
202         // a - b   <--->   a - c
203         case GST_PROPS_INT_RANGE_ID_NUM:
204           return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
205                   entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
206         case GST_PROPS_LIST_ID_NUM:
207           return gst_props_entry_check_list_compatibility (entry1, entry2);
208         default:
209           return FALSE;
210       }
211       break;
212     case GST_PROPS_FOURCC_ID_NUM:
213       switch (entry2->propstype) {
214         // b   <--->   a
215         case GST_PROPS_FOURCC_ID_NUM:
216           return (entry2->data.fourcc_data == entry1->data.fourcc_data);
217         // b   <--->   a,b,c
218         case GST_PROPS_LIST_ID_NUM:
219           return gst_props_entry_check_list_compatibility (entry1, entry2);
220         default:
221           return FALSE;
222       }
223       break;
224     case GST_PROPS_INT_ID_NUM:
225       switch (entry2->propstype) {
226         // b   <--->   a - d
227         case GST_PROPS_INT_RANGE_ID_NUM:
228           return (entry2->data.int_range_data.min <= entry1->data.int_data &&
229                   entry2->data.int_range_data.max >= entry1->data.int_data);
230         // b   <--->   a
231         case GST_PROPS_INT_ID_NUM:
232           return (entry2->data.int_data == entry1->data.int_data);
233         // b   <--->   a,b,c
234         case GST_PROPS_LIST_ID_NUM:
235           return gst_props_entry_check_list_compatibility (entry1, entry2);
236         default:
237           return FALSE;
238       }
239       break;
240     case GST_PROPS_BOOL_ID_NUM:
241       switch (entry2->propstype) {
242         // t   <--->   t
243         case GST_PROPS_BOOL_ID_NUM:
244           return (entry2->data.bool_data == entry1->data.bool_data);
245         case GST_PROPS_LIST_ID_NUM:
246           return gst_props_entry_check_list_compatibility (entry1, entry2);
247         default:
248           return FALSE;
249       }
250     default:
251       break;
252   }
253
254   return FALSE;
255 }
256
257 /**
258  * gst_props_check_compatibility:
259  * @fromprops: a capabilty
260  * @toprops: a capabilty
261  *
262  * Checks whether two capabilities are compatible
263  *
264  * Returns: true if compatible, false otherwise
265  */
266 gboolean
267 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
268 {
269   GSList *sourcelist;
270   GSList *sinklist;
271   gint missing = 0;
272   gint more = 0;
273   gboolean compatible = TRUE;
274
275   g_return_val_if_fail (fromprops != NULL, FALSE);
276   g_return_val_if_fail (toprops != NULL, FALSE);
277         
278   sourcelist = fromprops->properties;
279   sinklist   = toprops->properties;
280
281   while (sourcelist && sinklist && compatible) {
282     GstPropsEntry *entry1;
283     GstPropsEntry *entry2;
284
285     entry1 = (GstPropsEntry *)sourcelist->data;
286     entry2 = (GstPropsEntry *)sinklist->data;
287
288     while (entry1->propid < entry2->propid) {
289       DEBUG ("source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
290       more++;
291       sourcelist = g_slist_next (sourcelist);
292       if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
293       else goto end;
294     }
295     while (entry1->propid > entry2->propid) {
296       DEBUG ("source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
297       missing++;
298       sinklist = g_slist_next (sinklist);
299       if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
300       else goto end;
301     }
302
303     compatible &= gst_props_entry_check_compatibility (entry1, entry2);
304
305     sourcelist = g_slist_next (sourcelist);
306     sinklist = g_slist_next (sinklist);
307   }
308   if (sinklist) {
309     GstPropsEntry *entry2;
310     entry2 = (GstPropsEntry *)sinklist->data;
311     missing++;
312     DEBUG ("source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
313   }
314 end:
315
316   if (missing)
317     return FALSE;
318
319   return compatible;
320 }
321
322 static xmlNodePtr
323 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
324 {
325   xmlNodePtr subtree;
326
327   switch (entry->propstype) {
328     case GST_PROPS_INT_ID_NUM: 
329       subtree = xmlNewChild (parent, NULL, "int", NULL);
330       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
331       xmlNewProp (subtree, "value", g_strdup_printf ("%d", entry->data.int_data));
332       break;
333     case GST_PROPS_INT_RANGE_ID_NUM: 
334       subtree = xmlNewChild (parent, NULL, "range", NULL);
335       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
336       xmlNewProp (subtree, "min", g_strdup_printf ("%d", entry->data.int_range_data.min));
337       xmlNewProp (subtree, "max", g_strdup_printf ("%d", entry->data.int_range_data.max));
338       break;
339     case GST_PROPS_FOURCC_ID_NUM: 
340       xmlAddChild (parent, xmlNewComment (g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data)));
341       subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
342       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
343       xmlNewProp (subtree, "hexvalue", g_strdup_printf ("%08x", entry->data.fourcc_data));
344       break;
345     case GST_PROPS_BOOL_ID_NUM: 
346       subtree = xmlNewChild (parent, NULL, "boolean", NULL);
347       xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
348       xmlNewProp (subtree, "value", (entry->data.bool_data ?  "true" : "false"));
349       break;
350     default:
351       break;
352   }
353
354   return parent;
355 }
356
357 xmlNodePtr
358 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
359 {
360   GSList *proplist;
361   xmlNodePtr subtree;
362
363   g_return_val_if_fail (props != NULL, NULL);
364
365   proplist = props->properties;
366
367   while (proplist) {
368     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
369
370     switch (entry->propstype) {
371       case GST_PROPS_LIST_ID_NUM: 
372         subtree = xmlNewChild (parent, NULL, "list", NULL);
373         xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
374         g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
375       default:
376         gst_props_save_thyself_func (entry, parent);
377     }
378
379     proplist = g_slist_next (proplist);
380   }
381   
382   return parent;
383 }
384
385 static GstPropsEntry*
386 gst_props_load_thyself_func (xmlNodePtr field)
387 {
388   GstPropsEntry *entry;
389
390   entry = g_new0 (GstPropsEntry, 1);
391
392   if (!strcmp(field->name, "int")) {
393     entry->propstype = GST_PROPS_INT_ID_NUM;
394     entry->propid = g_quark_from_string (xmlGetProp(field, "name"));
395     sscanf (xmlGetProp(field, "value"), "%d", &entry->data.int_data);
396   }
397   else if (!strcmp(field->name, "range")) {
398     entry->propstype = GST_PROPS_INT_RANGE_ID_NUM;
399     entry->propid = g_quark_from_string (xmlGetProp(field, "name"));
400     sscanf (xmlGetProp(field, "min"), "%d", &entry->data.int_range_data.min);
401     sscanf (xmlGetProp(field, "max"), "%d", &entry->data.int_range_data.max);
402   }
403   else if (!strcmp(field->name, "boolean")) {
404     entry->propstype = GST_PROPS_BOOL_ID_NUM;
405     entry->propid = g_quark_from_string (xmlGetProp(field, "name"));
406     if (!strcmp (xmlGetProp(field, "value"), "false")) entry->data.bool_data = 0;
407     else entry->data.bool_data = 1;
408   }
409   else if (!strcmp(field->name, "fourcc")) {
410     entry->propstype = GST_PROPS_FOURCC_ID_NUM;
411     entry->propid = g_quark_from_string (xmlGetProp(field, "name"));
412     sscanf (xmlGetProp(field, "hexvalue"), "%08x", &entry->data.fourcc_data);
413   }
414
415   return entry;
416 }
417
418 GstProps*
419 gst_props_load_thyself (xmlNodePtr parent)
420 {
421   GstProps *props = g_new0 (GstProps, 1);
422   xmlNodePtr field = parent->childs;
423
424   while (field) {
425     if (!strcmp (field->name, "list")) {
426       GstPropsEntry *entry;
427       xmlNodePtr subfield = field->childs;
428
429       entry = g_new0 (GstPropsEntry, 1);
430       entry->propstype = GST_PROPS_LIST_ID_NUM;
431       entry->propid = g_quark_from_string (xmlGetProp(field, "name"));
432
433       while (subfield) {
434         GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
435
436         entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
437
438         subfield = subfield->next;
439       }
440       entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
441       props->properties = g_slist_insert_sorted (props->properties, entry, props_compare_func);
442     }
443     else {
444       GstPropsEntry *entry;
445
446       entry = gst_props_load_thyself_func (field);
447
448       props->properties = g_slist_insert_sorted (props->properties, entry, props_compare_func);
449     }
450     field = field->next;
451   }
452
453   return props;
454 }
455