2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
5 * gstprops.c: Properties subsystem for generic usage
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.
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.
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.
23 //#define GST_DEBUG_ENABLED
24 #include "gst_private.h"
27 #include "gstpropsprivate.h"
30 static gboolean gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2);
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,
42 _gst_props_initialize (void)
46 static GstPropsEntry *
47 gst_props_create_entry (GstPropsFactory factory, gint *skipped)
49 GstPropsFactoryEntry tag;
53 entry = g_new0 (GstPropsEntry, 1);
56 switch (GPOINTER_TO_INT (tag)) {
57 case GST_PROPS_INT_ID_NUM:
58 entry->propstype = GST_PROPS_INT_ID_NUM;
59 entry->data.int_data = GPOINTER_TO_INT (factory[i++]);
61 case GST_PROPS_INT_RANGE_ID_NUM:
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++]);
66 case GST_PROPS_FOURCC_ID_NUM:
67 entry->propstype = GST_PROPS_FOURCC_ID_NUM;
68 entry->data.fourcc_data = GPOINTER_TO_INT (factory[i++]);
70 case GST_PROPS_LIST_ID_NUM:
71 g_print("gstprops: list not allowed in list\n");
73 case GST_PROPS_BOOL_ID_NUM:
74 entry->propstype = GST_PROPS_BOOL_ID_NUM;
75 entry->data.bool_data = GPOINTER_TO_INT (factory[i++]);
78 g_print("gstprops: unknown props id found\n");
91 props_compare_func (gconstpointer a,
94 GstPropsEntry *entry1 = (GstPropsEntry *)a;
95 GstPropsEntry *entry2 = (GstPropsEntry *)b;
97 return (entry1->propid - entry2->propid);
101 * gst_props_register:
102 * @factory: the factory to register
104 * Register the factory.
106 * Returns: the new property created from the factory
109 gst_props_register (GstPropsFactory factory)
113 return gst_props_register_count (factory, &dummy);
117 * gst_props_register_count:
118 * @factory: the factory to register
119 * @counter: count how many fields were consumed
121 * Register the factory.
123 * Returns: the new property created from the factory
126 gst_props_register_count (GstPropsFactory factory, guint *counter)
128 GstPropsFactoryEntry tag;
130 GstProps *props = NULL;
133 g_return_val_if_fail (factory != NULL, NULL);
139 props = g_new0 (GstProps, 1);
140 g_return_val_if_fail (props != NULL, NULL);
142 props->properties = NULL;
146 GstPropsEntry *entry;
148 if (tag < GST_PROPS_LAST_ID) {
149 g_warning ("properties seem to be wrong\n");
153 quark = g_quark_from_string ((gchar *)tag);
156 switch (GPOINTER_TO_INT (tag)) {
157 case GST_PROPS_LIST_ID_NUM:
159 GstPropsEntry *list_entry;
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;
166 i++; // skip list tag
169 list_entry = gst_props_create_entry (&factory[i], &skipped);
170 list_entry->propid = quark;
173 entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, list_entry);
175 entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
176 i++; //skip NULL (list end)
181 entry = gst_props_create_entry (&factory[i], &skipped);
182 entry->propid = quark;
187 props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
200 * @entry: the property entries for the property
201 * @...: the property entries for the property
203 * Create a new property from the list of entries.
205 * Returns: the new property created from the list of entries
208 gst_props_new (GstPropsFactoryEntry entry, ...)
211 GstPropsFactoryEntry value;
214 GstPropsFactoryEntry *factory;
215 gboolean inlist = FALSE;
218 #define add_value(value) {\
219 GST_DEBUG (0,"%d %p\n", i, value);\
220 factory[i++] = value; \
223 factory = (GstPropsFactoryEntry *) g_realloc (factory, size*sizeof(GstPropsFactoryEntry));\
228 factory = (GstPropsFactoryEntry *) g_malloc (size*sizeof(GstPropsFactoryEntry));
230 va_start (var_args, entry);
232 value = (GstPropsFactoryEntry) entry;
241 value = va_arg (var_args, GstPropsFactoryEntry);
243 switch (GPOINTER_TO_INT (value)) {
244 case GST_PROPS_END_ID_NUM:
245 g_assert (inlist == TRUE);
250 case GST_PROPS_LIST_ID_NUM:
252 g_assert (inlist == FALSE);
259 skip = _arg_len[GPOINTER_TO_INT (value)];
264 value = va_arg (var_args, GstPropsFactoryEntry);
270 props = gst_props_register (factory);
277 * @props: the property to merge into
278 * @tomerge: the property to merge
280 * Merge the properties of tomerge into props.
282 * Returns: the new merged property
285 gst_props_merge (GstProps *props, GstProps *tomerge)
289 g_return_val_if_fail (props != NULL, NULL);
290 g_return_val_if_fail (tomerge != NULL, NULL);
292 merge_props = tomerge->properties;
294 // FIXME do proper merging here...
295 while (merge_props) {
296 GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
298 props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
300 merge_props = g_list_next (merge_props);
307 /* entry2 is always a list, entry1 never is */
309 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
311 GList *entrylist = entry2->data.list_data.entries;
312 gboolean found = FALSE;
314 while (entrylist && !found) {
315 GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
317 found |= gst_props_entry_check_compatibility (entry1, entry);
319 entrylist = g_list_next (entrylist);
326 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
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:
333 GList *entrylist = entry1->data.list_data.entries;
334 gboolean valid = TRUE; // innocent until proven guilty
336 while (entrylist && valid) {
337 GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
339 valid &= gst_props_entry_check_compatibility (entry, entry2);
341 entrylist = g_list_next (entrylist);
346 case GST_PROPS_INT_RANGE_ID_NUM:
347 switch (entry2->propstype) {
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);
358 case GST_PROPS_FOURCC_ID_NUM:
359 switch (entry2->propstype) {
361 case GST_PROPS_FOURCC_ID_NUM:
362 return (entry2->data.fourcc_data == entry1->data.fourcc_data);
364 case GST_PROPS_LIST_ID_NUM:
365 return gst_props_entry_check_list_compatibility (entry1, entry2);
370 case GST_PROPS_INT_ID_NUM:
371 switch (entry2->propstype) {
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);
377 case GST_PROPS_INT_ID_NUM:
378 return (entry2->data.int_data == entry1->data.int_data);
380 case GST_PROPS_LIST_ID_NUM:
381 return gst_props_entry_check_list_compatibility (entry1, entry2);
386 case GST_PROPS_BOOL_ID_NUM:
387 switch (entry2->propstype) {
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);
404 * gst_props_check_compatibility:
405 * @fromprops: a property
406 * @toprops: a property
408 * Checks whether two capabilities are compatible.
410 * Returns: TRUE if compatible, FALSE otherwise
413 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
419 gboolean compatible = TRUE;
421 g_return_val_if_fail (fromprops != NULL, FALSE);
422 g_return_val_if_fail (toprops != NULL, FALSE);
424 sourcelist = fromprops->properties;
425 sinklist = toprops->properties;
427 while (sourcelist && sinklist && compatible) {
428 GstPropsEntry *entry1;
429 GstPropsEntry *entry2;
431 entry1 = (GstPropsEntry *)sourcelist->data;
432 entry2 = (GstPropsEntry *)sinklist->data;
434 while (entry1->propid < entry2->propid) {
435 GST_DEBUG (0,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
437 sourcelist = g_list_next (sourcelist);
438 if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
441 while (entry1->propid > entry2->propid) {
442 GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
444 sinklist = g_list_next (sinklist);
445 if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
449 if (!gst_props_entry_check_compatibility (entry1, entry2)) {
451 GST_DEBUG (0, "%s and %s are not compatible\n",
452 g_quark_to_string (entry1->propid),
453 g_quark_to_string (entry2->propid));
456 sourcelist = g_list_next (sourcelist);
457 sinklist = g_list_next (sinklist);
459 if (sinklist && compatible) {
460 GstPropsEntry *entry2;
461 entry2 = (GstPropsEntry *)sinklist->data;
463 GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
474 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
479 switch (entry->propstype) {
480 case GST_PROPS_INT_ID_NUM:
481 subtree = xmlNewChild (parent, NULL, "int", NULL);
482 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
483 str = g_strdup_printf ("%d", entry->data.int_data);
484 xmlNewProp (subtree, "value", str);
487 case GST_PROPS_INT_RANGE_ID_NUM:
488 subtree = xmlNewChild (parent, NULL, "range", NULL);
489 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
490 str = g_strdup_printf ("%d", entry->data.int_range_data.min);
491 xmlNewProp (subtree, "min", str);
493 str = g_strdup_printf ("%d", entry->data.int_range_data.max);
494 xmlNewProp (subtree, "max", str);
497 case GST_PROPS_FOURCC_ID_NUM:
498 str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
499 xmlAddChild (parent, xmlNewComment (str));
501 subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
502 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
503 str = g_strdup_printf ("%08x", entry->data.fourcc_data);
504 xmlNewProp (subtree, "hexvalue", str);
507 case GST_PROPS_BOOL_ID_NUM:
508 subtree = xmlNewChild (parent, NULL, "boolean", NULL);
509 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
510 xmlNewProp (subtree, "value", (entry->data.bool_data ? "true" : "false"));
520 * gst_props_save_thyself:
521 * @props: a property to save
522 * @parent: the parent XML tree
524 * Saves the property into an XML representation.
526 * Returns: the new XML tree
529 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
534 g_return_val_if_fail (props != NULL, NULL);
536 proplist = props->properties;
539 GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
541 switch (entry->propstype) {
542 case GST_PROPS_LIST_ID_NUM:
543 subtree = xmlNewChild (parent, NULL, "list", NULL);
544 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
545 g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
547 gst_props_save_thyself_func (entry, parent);
550 proplist = g_list_next (proplist);
556 static GstPropsEntry*
557 gst_props_load_thyself_func (xmlNodePtr field)
559 GstPropsEntry *entry;
562 entry = g_new0 (GstPropsEntry, 1);
564 if (!strcmp(field->name, "int")) {
565 entry->propstype = GST_PROPS_INT_ID_NUM;
566 prop = xmlGetProp(field, "name");
567 entry->propid = g_quark_from_string (prop);
569 prop = xmlGetProp(field, "value");
570 sscanf (prop, "%d", &entry->data.int_data);
573 else if (!strcmp(field->name, "range")) {
574 entry->propstype = GST_PROPS_INT_RANGE_ID_NUM;
575 prop = xmlGetProp(field, "name");
576 entry->propid = g_quark_from_string (prop);
578 prop = xmlGetProp (field, "min");
579 sscanf (prop, "%d", &entry->data.int_range_data.min);
581 prop = xmlGetProp (field, "max");
582 sscanf (prop, "%d", &entry->data.int_range_data.max);
585 else if (!strcmp(field->name, "boolean")) {
586 entry->propstype = GST_PROPS_BOOL_ID_NUM;
587 prop = xmlGetProp(field, "name");
588 entry->propid = g_quark_from_string (prop);
590 prop = xmlGetProp (field, "value");
591 if (!strcmp (prop, "false")) entry->data.bool_data = 0;
592 else entry->data.bool_data = 1;
595 else if (!strcmp(field->name, "fourcc")) {
596 entry->propstype = GST_PROPS_FOURCC_ID_NUM;
597 prop = xmlGetProp(field, "name");
598 entry->propid = g_quark_from_string (prop);
600 prop = xmlGetProp (field, "hexvalue");
601 sscanf (prop, "%08x", &entry->data.fourcc_data);
609 * gst_props_load_thyself:
610 * @parent: the XML tree to load from
612 * Creates a new property out of an XML tree.
614 * Returns: the new property
617 gst_props_load_thyself (xmlNodePtr parent)
619 GstProps *props = g_new0 (GstProps, 1);
620 xmlNodePtr field = parent->xmlChildrenNode;
624 if (!strcmp (field->name, "list")) {
625 GstPropsEntry *entry;
626 xmlNodePtr subfield = field->xmlChildrenNode;
628 entry = g_new0 (GstPropsEntry, 1);
629 entry->propstype = GST_PROPS_LIST_ID_NUM;
630 prop = xmlGetProp (field, "name");
631 entry->propid = g_quark_from_string (prop);
635 GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
637 entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
639 subfield = subfield->next;
641 entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
642 props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
645 GstPropsEntry *entry;
647 entry = gst_props_load_thyself_func (field);
649 props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);