2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wim.taymans@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"
28 #include "gstpropsprivate.h"
30 static GMemChunk *_gst_props_entries_chunk;
31 static GMutex *_gst_props_entries_chunk_lock;
33 static GMemChunk *_gst_props_chunk;
34 static GMutex *_gst_props_chunk_lock;
36 static gboolean gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2);
37 static GList* gst_props_list_copy (GList *propslist);
41 _gst_props_initialize (void)
43 _gst_props_entries_chunk = g_mem_chunk_new ("GstPropsEntries",
44 sizeof (GstPropsEntry), sizeof (GstPropsEntry) * 256,
46 _gst_props_entries_chunk_lock = g_mutex_new ();
48 _gst_props_chunk = g_mem_chunk_new ("GstProps",
49 sizeof (GstProps), sizeof (GstProps) * 256,
51 _gst_props_chunk_lock = g_mutex_new ();
55 gst_props_debug_entry (GstPropsEntry *entry)
57 gchar *name = g_quark_to_string (entry->propid);
59 switch (entry->propstype) {
60 case GST_PROPS_INT_ID:
61 GST_DEBUG (GST_CAT_PROPERTIES, "%s: int %d\n", name, entry->data.int_data);
63 case GST_PROPS_FLOAT_ID:
64 GST_DEBUG (GST_CAT_PROPERTIES, "%s: float %f\n", name, entry->data.float_data);
66 case GST_PROPS_FOURCC_ID:
67 GST_DEBUG (GST_CAT_PROPERTIES, "%s: fourcc %4.4s\n", name, (gchar*)&entry->data.fourcc_data);
69 case GST_PROPS_BOOL_ID:
70 GST_DEBUG (GST_CAT_PROPERTIES, "%s: bool %d\n", name, entry->data.bool_data);
72 case GST_PROPS_STRING_ID:
73 GST_DEBUG (GST_CAT_PROPERTIES, "%s: string %s\n", name, entry->data.string_data.string);
75 case GST_PROPS_INT_RANGE_ID:
76 GST_DEBUG (GST_CAT_PROPERTIES, "%s: int range %d-%d\n", name, entry->data.int_range_data.min,
77 entry->data.int_range_data.max);
79 case GST_PROPS_FLOAT_RANGE_ID:
80 GST_DEBUG (GST_CAT_PROPERTIES, "%s: float range %f-%f\n", name, entry->data.float_range_data.min,
81 entry->data.float_range_data.max);
83 case GST_PROPS_LIST_ID:
84 GST_DEBUG (GST_CAT_PROPERTIES, "[list]\n");
86 GList *entries = entry->data.list_data.entries;
89 gst_props_debug_entry ((GstPropsEntry *)entries->data);
90 entries = g_list_next (entries);
95 g_warning ("unknown property type %d", entry->propstype);
101 props_compare_func (gconstpointer a,
104 GstPropsEntry *entry1 = (GstPropsEntry *)a;
105 GstPropsEntry *entry2 = (GstPropsEntry *)b;
107 return (entry1->propid - entry2->propid);
111 props_find_func (gconstpointer a,
114 GstPropsEntry *entry2 = (GstPropsEntry *)a;
115 GQuark quark = (GQuark) GPOINTER_TO_INT (b);
117 return (quark - entry2->propid);
120 /* This is implemented as a huge macro because we cannot pass
121 * va_list variables by reference on some architectures.
123 #define GST_PROPS_ENTRY_FILL(entry, var_args) \
125 entry->propstype = va_arg (var_args, GstPropsId); \
127 switch (entry->propstype) { \
128 case GST_PROPS_INT_ID: \
129 entry->data.int_data = va_arg (var_args, gint); \
131 case GST_PROPS_INT_RANGE_ID: \
132 entry->data.int_range_data.min = va_arg (var_args, gint); \
133 entry->data.int_range_data.max = va_arg (var_args, gint); \
135 case GST_PROPS_FLOAT_ID: \
136 entry->data.float_data = va_arg (var_args, gdouble); \
138 case GST_PROPS_FLOAT_RANGE_ID: \
139 entry->data.float_range_data.min = va_arg (var_args, gdouble); \
140 entry->data.float_range_data.max = va_arg (var_args, gdouble); \
142 case GST_PROPS_FOURCC_ID: \
143 entry->data.fourcc_data = va_arg (var_args, gulong); \
145 case GST_PROPS_BOOL_ID: \
146 entry->data.bool_data = va_arg (var_args, gboolean); \
148 case GST_PROPS_STRING_ID: \
149 entry->data.string_data.string = g_strdup (va_arg (var_args, gchar*)); \
156 static GstPropsEntry*
157 gst_props_alloc_entry (void)
159 GstPropsEntry *entry;
161 g_mutex_lock (_gst_props_entries_chunk_lock);
162 entry = g_mem_chunk_alloc (_gst_props_entries_chunk);
163 g_mutex_unlock (_gst_props_entries_chunk_lock);
169 gst_props_entry_destroy (GstPropsEntry *entry)
171 switch (entry->propstype) {
172 case GST_PROPS_STRING_ID:
173 g_free (entry->data.string_data.string);
175 case GST_PROPS_LIST_ID:
177 GList *entries = entry->data.list_data.entries;
180 gst_props_entry_destroy ((GstPropsEntry *)entries->data);
181 entries = g_list_next (entries);
183 g_list_free (entry->data.list_data.entries);
189 g_mutex_lock (_gst_props_entries_chunk_lock);
190 g_mem_chunk_free (_gst_props_entries_chunk, entry);
191 g_mutex_unlock (_gst_props_entries_chunk_lock);
195 gst_props_alloc (void)
199 g_mutex_lock (_gst_props_chunk_lock);
200 props = g_mem_chunk_alloc (_gst_props_chunk);
201 g_mutex_unlock (_gst_props_chunk_lock);
203 props->properties = NULL;
211 gst_props_add_entry (GstProps *props, GstPropsEntry *entry)
213 g_return_if_fail (props);
214 g_return_if_fail (entry);
216 if (props->fixed && GST_PROPS_ENTRY_IS_VARIABLE (entry)) {
217 props->fixed = FALSE;
219 props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
224 * @firstname: the first property name
225 * @...: the property values
227 * Create a new property from the given key/value pairs
229 * Returns: the new property
232 gst_props_new (const gchar *firstname, ...)
237 va_start (var_args, firstname);
239 props = gst_props_newv (firstname, var_args);
248 gst_props_debug (GstProps *props)
250 GList *propslist = props->properties;
253 GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
255 gst_props_debug_entry (entry);
257 propslist = g_list_next (propslist);
262 * gst_props_merge_int_entries:
263 * @newentry: the new entry
264 * @oldentry: an old entry
266 * Tries to merge oldentry into newentry, if there is a simpler single entry which represents
268 * Assumes that the entries are either ints or int ranges.
270 * Returns: TRUE if the entries were merged, FALSE otherwise.
273 gst_props_merge_int_entries(GstPropsEntry * newentry, GstPropsEntry * oldentry)
275 gint new_min, new_max, old_min, old_max;
276 gboolean can_merge = FALSE;
278 if (newentry->propstype == GST_PROPS_INT_ID) {
279 new_min = newentry->data.int_data;
280 new_max = newentry->data.int_data;
282 new_min = newentry->data.int_range_data.min;
283 new_max = newentry->data.int_range_data.max;
286 if (oldentry->propstype == GST_PROPS_INT_ID) {
287 old_min = oldentry->data.int_data;
288 old_max = oldentry->data.int_data;
290 old_min = oldentry->data.int_range_data.min;
291 old_max = oldentry->data.int_range_data.max;
294 /* Put range which starts lower into (new_min, new_max) */
295 if (old_min < new_min) {
305 /* new_min is min of either entry - second half of the following conditional */
306 /* is to avoid overflow problems. */
307 if (new_max >= old_min - 1 && old_min - 1 < old_min) {
308 /* ranges overlap, or are adjacent. Pick biggest maximum. */
310 if (old_max > new_max) new_max = old_max;
314 if (new_min == new_max) {
315 newentry->propstype = GST_PROPS_INT_ID;
316 newentry->data.int_data = new_min;
318 newentry->propstype = GST_PROPS_INT_RANGE_ID;
319 newentry->data.int_range_data.min = new_min;
320 newentry->data.int_range_data.max = new_max;
327 * gst_props_add_to_int_list:
328 * @entries: the existing list of entries
329 * @entry: the new entry to add to the list
331 * Add an integer property to a list of properties, removing duplicates
332 * and merging ranges.
334 * Assumes that the existing list is in simplest form, contains
335 * only ints and int ranges, and that the new entry is an int or
338 * Returns: a pointer to a list with the new entry added.
341 gst_props_add_to_int_list (GList * entries, GstPropsEntry * newentry)
347 GstPropsEntry * oldentry = (GstPropsEntry *)(i->data);
348 gboolean merged = gst_props_merge_int_entries(newentry, oldentry);
351 /* replace the existing one with the merged one */
352 g_mutex_lock (_gst_props_entries_chunk_lock);
353 g_mem_chunk_free (_gst_props_entries_chunk, oldentry);
354 g_mutex_unlock (_gst_props_entries_chunk_lock);
355 entries = g_list_remove_link (entries, i);
358 /* start again: it's possible that this change made an earlier entry */
359 /* mergeable, and the pointer is now invalid anyway. */
366 return g_list_prepend (entries, newentry);
371 * @firstname: the first property name
372 * @var_args: the property values
374 * Create a new property from the list of entries.
376 * Returns: the new property created from the list of entries
379 gst_props_newv (const gchar *firstname, va_list var_args)
382 gboolean inlist = FALSE;
383 const gchar *prop_name;
384 GstPropsEntry *list_entry = NULL;
387 GST_PROPS_LIST_T_UNSET,
388 GST_PROPS_LIST_T_INTS,
389 GST_PROPS_LIST_T_FLOATS,
390 GST_PROPS_LIST_T_MISC,
393 /* type of the list */
394 list_types list_type = GST_PROPS_LIST_T_UNSET;
395 /* type of current item */
396 list_types entry_type = GST_PROPS_LIST_T_UNSET;
398 if (firstname == NULL)
401 props = gst_props_alloc ();
403 prop_name = firstname;
407 GstPropsEntry *entry;
409 entry = gst_props_alloc_entry ();
410 entry->propid = g_quark_from_string (prop_name);
411 GST_PROPS_ENTRY_FILL (entry, var_args);
413 switch (entry->propstype) {
414 case GST_PROPS_INT_ID:
415 case GST_PROPS_INT_RANGE_ID:
416 entry_type = GST_PROPS_LIST_T_INTS;
418 case GST_PROPS_FLOAT_ID:
419 case GST_PROPS_FLOAT_RANGE_ID:
420 entry_type = GST_PROPS_LIST_T_FLOATS;
422 case GST_PROPS_FOURCC_ID:
423 case GST_PROPS_BOOL_ID:
424 case GST_PROPS_STRING_ID:
425 entry_type = GST_PROPS_LIST_T_MISC;
427 case GST_PROPS_LIST_ID:
428 g_return_val_if_fail (inlist == FALSE, NULL);
431 list_type = GST_PROPS_LIST_T_UNSET;
432 list_entry->data.list_data.entries = NULL;
434 case GST_PROPS_END_ID:
435 g_return_val_if_fail (inlist == TRUE, NULL);
437 /* if list was of size 1, replace the list by a the item it contains */
438 if (g_list_length(list_entry->data.list_data.entries) == 1) {
439 GstPropsEntry * subentry = (GstPropsEntry *)(list_entry->data.list_data.entries->data);
440 list_entry->propstype = subentry->propstype;
441 list_entry->data = subentry->data;
442 g_mutex_lock (_gst_props_entries_chunk_lock);
443 g_mem_chunk_free (_gst_props_entries_chunk, subentry);
444 g_mutex_unlock (_gst_props_entries_chunk_lock);
447 list_entry->data.list_data.entries =
448 g_list_reverse (list_entry->data.list_data.entries);
451 g_mutex_lock (_gst_props_entries_chunk_lock);
452 g_mem_chunk_free (_gst_props_entries_chunk, entry);
453 g_mutex_unlock (_gst_props_entries_chunk_lock);
456 prop_name = va_arg (var_args, gchar*);
459 g_warning ("unknown property type found %d for '%s'\n", entry->propstype, prop_name);
460 g_mutex_lock (_gst_props_entries_chunk_lock);
461 g_mem_chunk_free (_gst_props_entries_chunk, entry);
462 g_mutex_unlock (_gst_props_entries_chunk_lock);
466 if (inlist && (list_entry != entry)) {
467 if (list_type == GST_PROPS_LIST_T_UNSET) list_type = entry_type;
468 if (list_type != entry_type) {
469 g_warning ("property list contained incompatible entry types\n");
472 case GST_PROPS_LIST_T_INTS:
473 list_entry->data.list_data.entries =
474 gst_props_add_to_int_list (list_entry->data.list_data.entries, entry);
477 list_entry->data.list_data.entries =
478 g_list_prepend (list_entry->data.list_data.entries, entry);
484 gst_props_add_entry (props, entry);
487 prop_name = va_arg (var_args, gchar*);
495 * @props: the props to modify
496 * @name: the name of the entry to modify
497 * @...: More property entries.
499 * Modifies the value of the given entry in the props struct.
501 * Returns: the new modified property structure.
504 gst_props_set (GstProps *props, const gchar *name, ...)
510 quark = g_quark_from_string (name);
512 lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
515 GstPropsEntry *entry;
517 entry = (GstPropsEntry *)lentry->data;
519 va_start (var_args, name);
521 GST_PROPS_ENTRY_FILL (entry, var_args);
526 g_print("gstprops: no property '%s' to change\n", name);
534 * @props: the props to unref
536 * Decrease the refcount of the property structure, destroying
537 * the property if the refcount is 0.
540 gst_props_unref (GstProps *props)
547 if (props->refcount == 0)
548 gst_props_destroy (props);
553 * @props: the props to ref
555 * Increase the refcount of the property structure.
558 gst_props_ref (GstProps *props)
560 g_return_if_fail (props != NULL);
568 * @props: the props to destroy
570 * Destroy the property, freeing all the memory that
574 gst_props_destroy (GstProps *props)
581 entries = props->properties;
584 gst_props_entry_destroy ((GstPropsEntry *)entries->data);
585 entries = g_list_next (entries);
587 g_list_free (props->properties);
589 g_mutex_lock (_gst_props_chunk_lock);
590 g_mem_chunk_free (_gst_props_chunk, props);
591 g_mutex_unlock (_gst_props_chunk_lock);
597 static GstPropsEntry*
598 gst_props_entry_copy (GstPropsEntry *entry)
600 GstPropsEntry *newentry;
602 newentry = gst_props_alloc_entry ();
603 memcpy (newentry, entry, sizeof (GstPropsEntry));
604 if (entry->propstype == GST_PROPS_LIST_ID) {
605 newentry->data.list_data.entries = gst_props_list_copy (entry->data.list_data.entries);
607 else if (entry->propstype == GST_PROPS_STRING_ID) {
608 newentry->data.string_data.string = g_strdup (entry->data.string_data.string);
615 gst_props_list_copy (GList *propslist)
620 GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
622 new = g_list_prepend (new, gst_props_entry_copy (entry));
624 propslist = g_list_next (propslist);
626 new = g_list_reverse (new);
633 * @props: the props to copy
635 * Copy the property structure.
637 * Returns: the new property that is a copy of the original
641 gst_props_copy (GstProps *props)
649 new = gst_props_alloc ();
650 new->properties = gst_props_list_copy (props->properties);
651 new->fixed = props->fixed;
657 * gst_props_copy_on_write:
658 * @props: the props to copy on write
660 * Copy the property structure if the refcount is >1.
662 * Returns: A new props that can be safely written to.
665 gst_props_copy_on_write (GstProps *props)
667 GstProps *new = props;;
669 g_return_val_if_fail (props != NULL, NULL);
671 if (props->refcount > 1) {
672 new = gst_props_copy (props);
673 gst_props_unref (props);
679 static GstPropsEntry*
680 gst_props_get_entry_func (GstProps *props, const gchar *name)
685 g_return_val_if_fail (props != NULL, NULL);
686 g_return_val_if_fail (name != NULL, NULL);
688 quark = g_quark_from_string (name);
690 lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
693 GstPropsEntry *thisentry;
694 thisentry = (GstPropsEntry *)lentry->data;
701 gst_props_has_property (GstProps *props, const gchar *name)
703 return (gst_props_get_entry_func (props, name) != NULL);
708 * @props: the props to get the int value from
709 * @name: the name of the props entry to get.
711 * Get the named entry as an integer.
713 * Returns: the integer value of the named entry, 0 if not found.
716 gst_props_get_int (GstProps *props, const gchar *name)
718 GstPropsEntry *thisentry;
720 thisentry = gst_props_get_entry_func (props, name);
723 return thisentry->data.int_data;
726 g_warning ("props: property %s not found", name);
732 * gst_props_get_float:
733 * @props: the props to get the float value from
734 * @name: the name of the props entry to get.
736 * Get the named entry as a float.
738 * Returns: the float value of the named entry, 0.0 if not found.
741 gst_props_get_float (GstProps *props, const gchar *name)
743 GstPropsEntry *thisentry;
745 thisentry = gst_props_get_entry_func (props, name);
748 return thisentry->data.float_data;
751 g_warning ("props: property %s not found", name);
757 * gst_props_get_fourcc_int:
758 * @props: the props to get the fourcc value from
759 * @name: the name of the props entry to get.
761 * Get the named entry as a gulong fourcc.
763 * Returns: the fourcc value of the named entry, 0 if not found.
766 gst_props_get_fourcc_int (GstProps *props, const gchar *name)
768 GstPropsEntry *thisentry;
770 thisentry = gst_props_get_entry_func (props, name);
773 return thisentry->data.fourcc_data;
776 g_warning ("props: property %s not found", name);
782 * gst_props_get_boolean:
783 * @props: the props to get the fourcc value from
784 * @name: the name of the props entry to get.
786 * Get the named entry as a boolean value.
788 * Returns: the boolean value of the named entry, 0 if not found.
791 gst_props_get_boolean (GstProps *props, const gchar *name)
793 GstPropsEntry *thisentry;
795 thisentry = gst_props_get_entry_func (props, name);
798 return thisentry->data.bool_data;
801 g_warning ("props: property %s not found", name);
807 * gst_props_get_string:
808 * @props: the props to get the fourcc value from
809 * @name: the name of the props entry to get.
811 * Get the named entry as a string value.
813 * Returns: the string value of the named entry, NULL if not found.
816 gst_props_get_string (GstProps *props, const gchar *name)
818 GstPropsEntry *thisentry;
820 thisentry = gst_props_get_entry_func (props, name);
823 return thisentry->data.string_data.string;
826 g_warning ("props: property %s not found", name);
833 * @props: the property to merge into
834 * @tomerge: the property to merge
836 * Merge the properties of tomerge into props.
838 * Returns: the new merged property
841 gst_props_merge (GstProps *props, GstProps *tomerge)
845 g_return_val_if_fail (props != NULL, NULL);
846 g_return_val_if_fail (tomerge != NULL, NULL);
848 merge_props = tomerge->properties;
850 /* FIXME do proper merging here... */
851 while (merge_props) {
852 GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
854 gst_props_add_entry (props, entry);
856 merge_props = g_list_next (merge_props);
863 /* entry2 is always a list, entry1 never is */
865 gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
867 GList *entrylist = entry2->data.list_data.entries;
868 gboolean found = FALSE;
870 while (entrylist && !found) {
871 GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
873 found |= gst_props_entry_check_compatibility (entry1, entry);
875 entrylist = g_list_next (entrylist);
882 gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
884 GST_DEBUG (GST_CAT_PROPERTIES,"compare: %s %s\n", g_quark_to_string (entry1->propid),
885 g_quark_to_string (entry2->propid));
887 if (entry2->propstype == GST_PROPS_LIST_ID && entry1->propstype != GST_PROPS_LIST_ID) {
888 return gst_props_entry_check_list_compatibility (entry1, entry2);
891 switch (entry1->propstype) {
892 case GST_PROPS_LIST_ID:
894 GList *entrylist = entry1->data.list_data.entries;
895 gboolean valid = TRUE; /* innocent until proven guilty */
897 while (entrylist && valid) {
898 GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
900 valid &= gst_props_entry_check_compatibility (entry, entry2);
902 entrylist = g_list_next (entrylist);
907 case GST_PROPS_INT_RANGE_ID:
908 switch (entry2->propstype) {
909 /* a - b <---> a - c */
910 case GST_PROPS_INT_RANGE_ID:
911 return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
912 entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
915 case GST_PROPS_FLOAT_RANGE_ID:
916 switch (entry2->propstype) {
917 /* a - b <---> a - c */
918 case GST_PROPS_FLOAT_RANGE_ID:
919 return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
920 entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
923 case GST_PROPS_FOURCC_ID:
924 switch (entry2->propstype) {
926 case GST_PROPS_FOURCC_ID:
927 GST_DEBUG(GST_CAT_PROPERTIES,"\"%4.4s\" <--> \"%4.4s\" ?\n",
928 &entry2->data.fourcc_data, &entry1->data.fourcc_data);
929 return (entry2->data.fourcc_data == entry1->data.fourcc_data);
932 case GST_PROPS_INT_ID:
933 switch (entry2->propstype) {
935 case GST_PROPS_INT_RANGE_ID:
936 GST_DEBUG(GST_CAT_PROPERTIES,"%d <= %d <= %d ?\n",entry2->data.int_range_data.min,
937 entry1->data.int_data,entry2->data.int_range_data.max);
938 return (entry2->data.int_range_data.min <= entry1->data.int_data &&
939 entry2->data.int_range_data.max >= entry1->data.int_data);
941 case GST_PROPS_INT_ID:
942 GST_DEBUG(GST_CAT_PROPERTIES,"%d == %d ?\n",entry1->data.int_data,entry2->data.int_data);
943 return (entry2->data.int_data == entry1->data.int_data);
946 case GST_PROPS_FLOAT_ID:
947 switch (entry2->propstype) {
949 case GST_PROPS_FLOAT_RANGE_ID:
950 return (entry2->data.float_range_data.min <= entry1->data.float_data &&
951 entry2->data.float_range_data.max >= entry1->data.float_data);
953 case GST_PROPS_FLOAT_ID:
954 return (entry2->data.float_data == entry1->data.float_data);
957 case GST_PROPS_BOOL_ID:
958 switch (entry2->propstype) {
960 case GST_PROPS_BOOL_ID:
961 return (entry2->data.bool_data == entry1->data.bool_data);
963 case GST_PROPS_STRING_ID:
964 switch (entry2->propstype) {
966 case GST_PROPS_STRING_ID:
967 return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
975 * gst_props_check_compatibility:
976 * @fromprops: a property
977 * @toprops: a property
979 * Checks whether two capabilities are compatible.
981 * Returns: TRUE if compatible, FALSE otherwise
984 gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
990 gboolean compatible = TRUE;
992 g_return_val_if_fail (fromprops != NULL, FALSE);
993 g_return_val_if_fail (toprops != NULL, FALSE);
995 sourcelist = fromprops->properties;
996 sinklist = toprops->properties;
998 while (sourcelist && sinklist && compatible) {
999 GstPropsEntry *entry1;
1000 GstPropsEntry *entry2;
1002 entry1 = (GstPropsEntry *)sourcelist->data;
1003 entry2 = (GstPropsEntry *)sinklist->data;
1005 while (entry1->propid < entry2->propid) {
1007 sourcelist = g_list_next (sourcelist);
1008 if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
1011 while (entry1->propid > entry2->propid) {
1013 sinklist = g_list_next (sinklist);
1014 if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
1018 if (!gst_props_entry_check_compatibility (entry1, entry2)) {
1020 GST_DEBUG (GST_CAT_PROPERTIES, "%s are not compatible: \n",
1021 g_quark_to_string (entry1->propid));
1024 sourcelist = g_list_next (sourcelist);
1025 sinklist = g_list_next (sinklist);
1027 if (sinklist && compatible) {
1028 GstPropsEntry *entry2;
1029 entry2 = (GstPropsEntry *)sinklist->data;
1040 static GstPropsEntry*
1041 gst_props_entry_intersect (GstPropsEntry *entry1, GstPropsEntry *entry2)
1043 GstPropsEntry *result = NULL;
1045 /* try to move the ranges and lists first */
1046 switch (entry2->propstype) {
1047 case GST_PROPS_INT_RANGE_ID:
1048 case GST_PROPS_FLOAT_RANGE_ID:
1049 case GST_PROPS_LIST_ID:
1051 GstPropsEntry *temp;
1059 switch (entry1->propstype) {
1060 case GST_PROPS_LIST_ID:
1062 GList *entrylist = entry1->data.list_data.entries;
1063 GList *intersection = NULL;
1066 GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
1067 GstPropsEntry *intersectentry;
1069 intersectentry = gst_props_entry_intersect (entry2, entry);
1071 if (intersectentry) {
1072 if (intersectentry->propstype == GST_PROPS_LIST_ID) {
1073 intersection = g_list_concat (intersection, intersectentry->data.list_data.entries);
1074 /* set the list to NULL because the entries are concatenated to the above
1075 * list and we don't want to free them */
1076 intersectentry->data.list_data.entries = NULL;
1077 gst_props_entry_destroy (intersectentry);
1080 intersection = g_list_prepend (intersection, intersectentry);
1083 entrylist = g_list_next (entrylist);
1086 /* check if the list only contains 1 element, if so, we can just copy it */
1087 if (g_list_next (intersection) == NULL) {
1088 result = (GstPropsEntry *) (intersection->data);
1089 g_list_free (intersection);
1091 /* else we need to create a new entry to hold the list */
1093 result = gst_props_alloc_entry ();
1094 result->propid = entry1->propid;
1095 result->propstype = GST_PROPS_LIST_ID;
1096 result->data.list_data.entries = g_list_reverse (intersection);
1101 case GST_PROPS_INT_RANGE_ID:
1102 switch (entry2->propstype) {
1103 /* a - b <---> a - c */
1104 case GST_PROPS_INT_RANGE_ID:
1106 gint lower = MAX (entry1->data.int_range_data.min, entry2->data.int_range_data.min);
1107 gint upper = MIN (entry1->data.int_range_data.max, entry2->data.int_range_data.max);
1109 if (lower <= upper) {
1110 result = gst_props_alloc_entry ();
1111 result->propid = entry1->propid;
1113 if (lower == upper) {
1114 result->propstype = GST_PROPS_INT_ID;
1115 result->data.int_data = lower;
1118 result->propstype = GST_PROPS_INT_RANGE_ID;
1119 result->data.int_range_data.min = lower;
1120 result->data.int_range_data.max = upper;
1125 case GST_PROPS_INT_ID:
1126 if (entry1->data.int_range_data.min <= entry2->data.int_data &&
1127 entry1->data.int_range_data.max >= entry2->data.int_data) {
1128 result = gst_props_entry_copy (entry2);
1132 case GST_PROPS_FLOAT_RANGE_ID:
1133 switch (entry2->propstype) {
1134 /* a - b <---> a - c */
1135 case GST_PROPS_FLOAT_RANGE_ID:
1137 gfloat lower = MAX (entry1->data.float_range_data.min, entry2->data.float_range_data.min);
1138 gfloat upper = MIN (entry1->data.float_range_data.max, entry2->data.float_range_data.max);
1140 if (lower <= upper) {
1141 result = gst_props_alloc_entry ();
1142 result->propid = entry1->propid;
1144 if (lower == upper) {
1145 result->propstype = GST_PROPS_FLOAT_ID;
1146 result->data.float_data = lower;
1149 result->propstype = GST_PROPS_FLOAT_RANGE_ID;
1150 result->data.float_range_data.min = lower;
1151 result->data.float_range_data.max = upper;
1156 case GST_PROPS_FLOAT_ID:
1157 if (entry1->data.float_range_data.min <= entry2->data.float_data &&
1158 entry1->data.float_range_data.max >= entry2->data.float_data) {
1159 result = gst_props_entry_copy (entry2);
1163 case GST_PROPS_FOURCC_ID:
1164 switch (entry2->propstype) {
1166 case GST_PROPS_FOURCC_ID:
1167 if (entry1->data.fourcc_data == entry2->data.fourcc_data)
1168 result = gst_props_entry_copy (entry1);
1171 case GST_PROPS_INT_ID:
1172 switch (entry2->propstype) {
1174 case GST_PROPS_INT_ID:
1175 if (entry1->data.int_data == entry2->data.int_data)
1176 result = gst_props_entry_copy (entry1);
1179 case GST_PROPS_FLOAT_ID:
1180 switch (entry2->propstype) {
1182 case GST_PROPS_FLOAT_ID:
1183 if (entry1->data.float_data == entry2->data.float_data)
1184 result = gst_props_entry_copy (entry1);
1187 case GST_PROPS_BOOL_ID:
1188 switch (entry2->propstype) {
1190 case GST_PROPS_BOOL_ID:
1191 if (entry1->data.bool_data == entry2->data.bool_data)
1192 result = gst_props_entry_copy (entry1);
1194 case GST_PROPS_STRING_ID:
1195 switch (entry2->propstype) {
1197 case GST_PROPS_STRING_ID:
1198 if (!strcmp (entry1->data.string_data.string, entry2->data.string_data.string))
1199 result = gst_props_entry_copy (entry1);
1207 * gst_props_intersect:
1208 * @props1: a property
1209 * @props2: another property
1211 * Calculates the intersection bewteen two GstProps.
1213 * Returns: a GstProps with the intersection or NULL if the
1214 * intersection is empty.
1217 gst_props_intersect (GstProps *props1, GstProps *props2)
1221 GstProps *intersection;
1223 GstPropsEntry *iprops = NULL;
1225 intersection = gst_props_alloc ();
1226 intersection->fixed = TRUE;
1228 g_return_val_if_fail (props1 != NULL, NULL);
1229 g_return_val_if_fail (props2 != NULL, NULL);
1231 props1list = props1->properties;
1232 props2list = props2->properties;
1234 while (props1list && props2list) {
1235 GstPropsEntry *entry1;
1236 GstPropsEntry *entry2;
1238 entry1 = (GstPropsEntry *)props1list->data;
1239 entry2 = (GstPropsEntry *)props2list->data;
1241 while (entry1->propid < entry2->propid) {
1242 GstPropsEntry *toadd;
1244 GST_DEBUG (GST_CAT_PROPERTIES,"source is more specific in \"%s\"\n", g_quark_to_string (entry1->propid));
1246 toadd = gst_props_entry_copy (entry1);
1247 if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1248 intersection->fixed = FALSE;
1250 intersection->properties = g_list_prepend (intersection->properties, toadd);
1252 props1list = g_list_next (props1list);
1254 entry1 = (GstPropsEntry *)props1list->data;
1258 while (entry1->propid > entry2->propid) {
1259 GstPropsEntry *toadd;
1261 toadd = gst_props_entry_copy (entry2);
1262 if (GST_PROPS_ENTRY_IS_VARIABLE (toadd))
1263 intersection->fixed = FALSE;
1265 intersection->properties = g_list_prepend (intersection->properties, toadd);
1267 props2list = g_list_next (props2list);
1269 entry2 = (GstPropsEntry *)props2list->data;
1273 /* at this point we are talking about the same property */
1274 iprops = gst_props_entry_intersect (entry1, entry2);
1277 if (GST_PROPS_ENTRY_IS_VARIABLE (iprops))
1278 intersection->fixed = FALSE;
1279 intersection->properties = g_list_prepend (intersection->properties, iprops);
1282 gst_props_unref (intersection);
1286 props1list = g_list_next (props1list);
1287 props2list = g_list_next (props2list);
1291 /* at this point one of the lists could contain leftover properties */
1293 leftovers = props1list;
1294 else if (props2list)
1295 leftovers = props2list;
1300 GstPropsEntry *entry;
1302 entry = (GstPropsEntry *) leftovers->data;
1303 if (GST_PROPS_ENTRY_IS_VARIABLE (entry))
1304 intersection->fixed = FALSE;
1305 intersection->properties = g_list_prepend (intersection->properties, gst_props_entry_copy (entry));
1307 leftovers = g_list_next (leftovers);
1311 intersection->properties = g_list_reverse (intersection->properties);
1313 return intersection;
1317 gst_props_normalize (GstProps *props)
1320 GList *result = NULL;
1325 entries = props->properties;
1328 GstPropsEntry *entry = (GstPropsEntry *) entries->data;
1330 if (entry->propstype == GST_PROPS_LIST_ID) {
1331 GList *list_entries = entry->data.list_data.entries;
1333 while (list_entries) {
1334 GstPropsEntry *list_entry = (GstPropsEntry *) list_entries->data;
1335 GstPropsEntry *new_entry;
1339 newprops = gst_props_alloc ();
1340 newprops->properties = gst_props_list_copy (props->properties);
1341 lentry = g_list_find_custom (newprops->properties, GINT_TO_POINTER (list_entry->propid), props_find_func);
1343 GList *new_list = NULL;
1345 new_entry = (GstPropsEntry *) lentry->data;
1346 memcpy (new_entry, list_entry, sizeof (GstPropsEntry));
1348 new_list = gst_props_normalize (newprops);
1349 result = g_list_concat (new_list, result);
1352 result = g_list_append (result, newprops);
1355 list_entries = g_list_next (list_entries);
1357 /* we break out of the loop because the other lists are
1358 * unrolled in the recursive call */
1361 entries = g_list_next (entries);
1364 result = g_list_prepend (result, props);
1367 result = g_list_reverse (result);
1368 gst_props_unref (props);
1373 #ifndef GST_DISABLE_LOADSAVE_REGISTRY
1375 gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
1380 switch (entry->propstype) {
1381 case GST_PROPS_INT_ID:
1382 subtree = xmlNewChild (parent, NULL, "int", NULL);
1383 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1384 str = g_strdup_printf ("%d", entry->data.int_data);
1385 xmlNewProp (subtree, "value", str);
1388 case GST_PROPS_INT_RANGE_ID:
1389 subtree = xmlNewChild (parent, NULL, "range", NULL);
1390 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1391 str = g_strdup_printf ("%d", entry->data.int_range_data.min);
1392 xmlNewProp (subtree, "min", str);
1394 str = g_strdup_printf ("%d", entry->data.int_range_data.max);
1395 xmlNewProp (subtree, "max", str);
1398 case GST_PROPS_FLOAT_ID:
1399 subtree = xmlNewChild (parent, NULL, "float", NULL);
1400 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1401 str = g_strdup_printf ("%f", entry->data.float_data);
1402 xmlNewProp (subtree, "value", str);
1405 case GST_PROPS_FLOAT_RANGE_ID:
1406 subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
1407 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1408 str = g_strdup_printf ("%f", entry->data.float_range_data.min);
1409 xmlNewProp (subtree, "min", str);
1411 str = g_strdup_printf ("%f", entry->data.float_range_data.max);
1412 xmlNewProp (subtree, "max", str);
1415 case GST_PROPS_FOURCC_ID:
1416 str = g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
1417 xmlAddChild (parent, xmlNewComment (str));
1419 subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
1420 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1421 str = g_strdup_printf ("%08x", entry->data.fourcc_data);
1422 xmlNewProp (subtree, "hexvalue", str);
1425 case GST_PROPS_BOOL_ID:
1426 subtree = xmlNewChild (parent, NULL, "boolean", NULL);
1427 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1428 xmlNewProp (subtree, "value", (entry->data.bool_data ? "true" : "false"));
1430 case GST_PROPS_STRING_ID:
1431 subtree = xmlNewChild (parent, NULL, "string", NULL);
1432 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1433 xmlNewProp (subtree, "value", entry->data.string_data.string);
1436 g_warning ("trying to save unknown property type %d", entry->propstype);
1444 * gst_props_save_thyself:
1445 * @props: a property to save
1446 * @parent: the parent XML tree
1448 * Saves the property into an XML representation.
1450 * Returns: the new XML tree
1453 gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
1458 g_return_val_if_fail (props != NULL, NULL);
1460 proplist = props->properties;
1463 GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1465 switch (entry->propstype) {
1466 case GST_PROPS_LIST_ID:
1467 subtree = xmlNewChild (parent, NULL, "list", NULL);
1468 xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
1469 g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
1472 gst_props_save_thyself_func (entry, parent);
1475 proplist = g_list_next (proplist);
1481 static GstPropsEntry*
1482 gst_props_load_thyself_func (xmlNodePtr field)
1484 GstPropsEntry *entry;
1487 entry = gst_props_alloc_entry ();
1489 if (!strcmp(field->name, "int")) {
1490 entry->propstype = GST_PROPS_INT_ID;
1491 prop = xmlGetProp(field, "name");
1492 entry->propid = g_quark_from_string (prop);
1494 prop = xmlGetProp(field, "value");
1495 sscanf (prop, "%d", &entry->data.int_data);
1498 else if (!strcmp(field->name, "range")) {
1499 entry->propstype = GST_PROPS_INT_RANGE_ID;
1500 prop = xmlGetProp(field, "name");
1501 entry->propid = g_quark_from_string (prop);
1503 prop = xmlGetProp (field, "min");
1504 sscanf (prop, "%d", &entry->data.int_range_data.min);
1506 prop = xmlGetProp (field, "max");
1507 sscanf (prop, "%d", &entry->data.int_range_data.max);
1510 else if (!strcmp(field->name, "float")) {
1511 entry->propstype = GST_PROPS_FLOAT_ID;
1512 prop = xmlGetProp(field, "name");
1513 entry->propid = g_quark_from_string (prop);
1515 prop = xmlGetProp(field, "value");
1516 sscanf (prop, "%f", &entry->data.float_data);
1519 else if (!strcmp(field->name, "floatrange")) {
1520 entry->propstype = GST_PROPS_FLOAT_RANGE_ID;
1521 prop = xmlGetProp(field, "name");
1522 entry->propid = g_quark_from_string (prop);
1524 prop = xmlGetProp (field, "min");
1525 sscanf (prop, "%f", &entry->data.float_range_data.min);
1527 prop = xmlGetProp (field, "max");
1528 sscanf (prop, "%f", &entry->data.float_range_data.max);
1531 else if (!strcmp(field->name, "boolean")) {
1532 entry->propstype = GST_PROPS_BOOL_ID;
1533 prop = xmlGetProp(field, "name");
1534 entry->propid = g_quark_from_string (prop);
1536 prop = xmlGetProp (field, "value");
1537 if (!strcmp (prop, "false")) entry->data.bool_data = 0;
1538 else entry->data.bool_data = 1;
1541 else if (!strcmp(field->name, "fourcc")) {
1542 entry->propstype = GST_PROPS_FOURCC_ID;
1543 prop = xmlGetProp(field, "name");
1544 entry->propid = g_quark_from_string (prop);
1546 prop = xmlGetProp (field, "hexvalue");
1547 sscanf (prop, "%08x", &entry->data.fourcc_data);
1550 else if (!strcmp(field->name, "string")) {
1551 entry->propstype = GST_PROPS_STRING_ID;
1552 prop = xmlGetProp(field, "name");
1553 entry->propid = g_quark_from_string (prop);
1555 entry->data.string_data.string = xmlGetProp (field, "value");
1558 g_mutex_lock (_gst_props_entries_chunk_lock);
1559 g_mem_chunk_free (_gst_props_entries_chunk, entry);
1560 g_mutex_unlock (_gst_props_entries_chunk_lock);
1568 * gst_props_load_thyself:
1569 * @parent: the XML tree to load from
1571 * Creates a new property out of an XML tree.
1573 * Returns: the new property
1576 gst_props_load_thyself (xmlNodePtr parent)
1579 xmlNodePtr field = parent->xmlChildrenNode;
1582 props = gst_props_alloc ();
1585 if (!strcmp (field->name, "list")) {
1586 GstPropsEntry *entry;
1587 xmlNodePtr subfield = field->xmlChildrenNode;
1589 entry = gst_props_alloc_entry ();
1590 prop = xmlGetProp (field, "name");
1591 entry->propid = g_quark_from_string (prop);
1593 entry->propstype = GST_PROPS_LIST_ID;
1594 entry->data.list_data.entries = NULL;
1597 GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
1600 entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
1602 subfield = subfield->next;
1604 entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
1605 gst_props_add_entry (props, entry);
1608 GstPropsEntry *entry;
1610 entry = gst_props_load_thyself_func (field);
1613 gst_props_add_entry (props, entry);
1615 field = field->next;
1620 #endif /* GST_DISABLE_LOADSAVE_REGISTRY */