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:
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:
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:
67 entry->propstype = GST_PROPS_FOURCC_ID_NUM;
68 entry->data.fourcc_data = GPOINTER_TO_INT (factory[i++]);
70 case GST_PROPS_LIST_ID:
71 g_print("gstprops: list not allowed in list\n");
73 case GST_PROPS_BOOL_ID:
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:
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:
245 g_assert (inlist == TRUE);
250 case GST_PROPS_LIST_ID:
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 compatible &= gst_props_entry_check_compatibility (entry1, entry2);
451 sourcelist = g_list_next (sourcelist);
452 sinklist = g_list_next (sinklist);
455 GstPropsEntry *entry2;
456 entry2 = (GstPropsEntry *)sinklist->data;
458 GST_DEBUG (0,"source has missing property \"%s\"\n", g_quark_to_string (entry2->propid));
469 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
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);
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);
488 str = g_strdup_printf ("%d", entry->data.int_range_data.max);
489 xmlNewProp (subtree, "max", str);
492 case GST_PROPS_FOURCC_ID_NUM:
493 str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
494 xmlAddChild (parent, xmlNewComment (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);
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"));
515 * gst_props_save_thyself:
516 * @props: a property to save
517 * @parent: the parent XML tree
519 * Saves the property into an XML representation.
521 * Returns: the new XML tree
524 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
529 g_return_val_if_fail (props != NULL, NULL);
531 proplist = props->properties;
534 GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
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);
542 gst_props_save_thyself_func (entry, parent);
545 proplist = g_list_next (proplist);
551 static GstPropsEntry*
552 gst_props_load_thyself_func (xmlNodePtr field)
554 GstPropsEntry *entry;
557 entry = g_new0 (GstPropsEntry, 1);
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);
564 prop = xmlGetProp(field, "value");
565 sscanf (prop, "%d", &entry->data.int_data);
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);
573 prop = xmlGetProp (field, "min");
574 sscanf (prop, "%d", &entry->data.int_range_data.min);
576 prop = xmlGetProp (field, "min");
577 sscanf (prop, "%d", &entry->data.int_range_data.max);
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);
585 prop = xmlGetProp (field, "value");
586 if (!strcmp (prop, "false")) entry->data.bool_data = 0;
587 else entry->data.bool_data = 1;
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);
595 prop = xmlGetProp (field, "hexvalue");
596 sscanf (prop, "%08x", &entry->data.fourcc_data);
604 * gst_props_load_thyself:
605 * @parent: the XML tree to load from
607 * Creates a new property out of an XML tree.
609 * Returns: the new property
612 gst_props_load_thyself (xmlNodePtr parent)
614 GstProps *props = g_new0 (GstProps, 1);
615 xmlNodePtr field = parent->childs;
619 if (!strcmp (field->name, "list")) {
620 GstPropsEntry *entry;
621 xmlNodePtr subfield = field->childs;
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);
630 GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
632 entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
634 subfield = subfield->next;
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);
640 GstPropsEntry *entry;
642 entry = gst_props_load_thyself_func (field);
644 props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);