mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 13:41:48 +00:00
2021fd636c
Original commit message from CVS: We need to store the new GList. Else, this causes problems if we remove the first entry from the list (unreferenced pointer). Also, we need to destroy the unused entry afterwards (memleak).
2813 lines
77 KiB
C
2813 lines
77 KiB
C
/* GStreamer
|
|
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
|
|
* 2000 Wim Taymans <wim.taymans@chello.be>
|
|
*
|
|
* gstprops.c: Properties subsystem for generic usage
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "gst_private.h"
|
|
|
|
#include "gstinfo.h"
|
|
#include "gstprops.h"
|
|
#include "gstmemchunk.h"
|
|
|
|
#ifndef GST_DISABLE_TRACE
|
|
/* #define GST_WITH_ALLOC_TRACE */
|
|
#include "gsttrace.h"
|
|
static GstAllocTrace *_props_trace;
|
|
static GstAllocTrace *_entries_trace;
|
|
#endif
|
|
|
|
GType _gst_props_type;
|
|
GType _gst_props_entry_type;
|
|
|
|
#define GST_PROPS_ENTRY_IS_VARIABLE(a) (((GstPropsEntry*)(a))->propstype > GST_PROPS_VAR_TYPE)
|
|
|
|
struct _GstPropsEntry {
|
|
GQuark propid;
|
|
GstPropsType propstype;
|
|
|
|
union {
|
|
/* flat values */
|
|
gboolean bool_data;
|
|
guint32 fourcc_data;
|
|
gint int_data;
|
|
gfloat float_data;
|
|
|
|
/* structured values */
|
|
struct {
|
|
GList *entries;
|
|
} list_data;
|
|
struct {
|
|
gchar *string;
|
|
} string_data;
|
|
struct {
|
|
gint min;
|
|
gint max;
|
|
} int_range_data;
|
|
struct {
|
|
gfloat min;
|
|
gfloat max;
|
|
} float_range_data;
|
|
} data;
|
|
};
|
|
|
|
static GstMemChunk *_gst_props_entries_chunk;
|
|
static GstMemChunk *_gst_props_chunk;
|
|
|
|
/* transform functions */
|
|
static void gst_props_transform_to_string (const GValue *src_value, GValue *dest_value);
|
|
static gchar * gst_props_entry_to_string (GstPropsEntry *entry);
|
|
gboolean __gst_props_parse_string (gchar *r, gchar **end, gchar **next);
|
|
|
|
static gboolean gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2);
|
|
static GList* gst_props_list_copy (GList *propslist);
|
|
|
|
static GstPropsEntry* gst_props_alloc_entry (void);
|
|
static inline void gst_props_entry_free (GstPropsEntry *entry);
|
|
|
|
static void gst_props_destroy (GstProps *props);
|
|
|
|
static gchar *
|
|
gst_props_entry_to_string (GstPropsEntry *entry)
|
|
{
|
|
switch (entry->propstype) {
|
|
case GST_PROPS_INT_TYPE:
|
|
return g_strdup_printf ("int = %d", entry->data.int_data);
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
return g_strdup_printf ("float = %g", entry->data.float_data);
|
|
break;
|
|
case GST_PROPS_FOURCC_TYPE: {
|
|
gchar fourcc[5] = { GST_FOURCC_ARGS (entry->data.fourcc_data), '\0' }; /* Do all compilers understand that? */
|
|
if (g_ascii_isalnum(fourcc[1]) && g_ascii_isalnum(fourcc[2]) &&
|
|
g_ascii_isalnum(fourcc[3]) && g_ascii_isalnum(fourcc[4])) {
|
|
return g_strdup_printf ("fourcc = %s", fourcc);
|
|
} else {
|
|
return g_strdup_printf ("fourcc = %d", entry->data.fourcc_data);
|
|
}
|
|
}
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
return g_strdup_printf ("bool = %s", (entry->data.bool_data ? "TRUE" : "FALSE"));
|
|
case GST_PROPS_STRING_TYPE:
|
|
/* FIXME: Need to escape stuff here */
|
|
return g_strdup_printf ("string = '%s'", entry->data.string_data.string);
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
return g_strdup_printf ("int = [%d, %d]", entry->data.int_range_data.min, entry->data.int_range_data.max);
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
return g_strdup_printf ("float = [%g, %g]", entry->data.float_range_data.min, entry->data.float_range_data.max);
|
|
case GST_PROPS_LIST_TYPE: {
|
|
GList *walk;
|
|
GString *s;
|
|
gchar *temp;
|
|
|
|
s = g_string_new ("list = (");
|
|
walk = entry->data.list_data.entries;
|
|
while (walk) {
|
|
temp = gst_props_entry_to_string ((GstPropsEntry *) walk->data);
|
|
g_string_append (s, temp);
|
|
walk = walk->next;
|
|
if (walk) {
|
|
g_string_append (s, ", ");
|
|
} else {
|
|
g_string_append (s, ")");
|
|
}
|
|
g_free (temp);
|
|
}
|
|
temp = s->str;
|
|
g_string_free (s, FALSE);
|
|
return temp;
|
|
}
|
|
default:
|
|
/* transforms always succeed */
|
|
g_warning("corrupt property list, type==%d\n",entry->propstype);
|
|
g_assert_not_reached();
|
|
return NULL;
|
|
}
|
|
}
|
|
/**
|
|
* gst_props_to_string:
|
|
* props: the props to convert to a string
|
|
*
|
|
* Converts a #GstProps into a readable format. This is mainly intended for
|
|
* debugging purposes. You have to free the string using g_free.
|
|
* A string converted with #gst_props_to_string can always be converted back to
|
|
* its props representation using #gst_props_from_string.
|
|
*
|
|
* Returns: A newly allocated string
|
|
*/
|
|
gchar *
|
|
gst_props_to_string (GstProps *props)
|
|
{
|
|
GString *s;
|
|
gchar *temp;
|
|
GList *propslist;
|
|
|
|
s = g_string_new ("");
|
|
propslist = props->properties;
|
|
while (propslist) {
|
|
GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
|
|
const gchar *name = g_quark_to_string (entry->propid);
|
|
|
|
temp = gst_props_entry_to_string (entry);
|
|
propslist = g_list_next (propslist);
|
|
if (temp) {
|
|
if (propslist) {
|
|
g_string_append_printf (s, "%s:%s, ", name, temp);
|
|
} else {
|
|
g_string_append_printf (s, "%s:%s", name, temp);
|
|
}
|
|
g_free (temp);
|
|
}
|
|
}
|
|
temp = s->str;
|
|
g_string_free (s, FALSE);
|
|
|
|
return temp;
|
|
}
|
|
static void
|
|
gst_props_transform_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GstProps *props = g_value_peek_pointer (src_value);
|
|
|
|
if (props)
|
|
dest_value->data[0].v_pointer = gst_props_to_string (props);
|
|
}
|
|
/*
|
|
* r will still point to the string. if end == next, the string will not be
|
|
* null-terminated. In all other cases it will be.
|
|
* end = pointer to char behind end of string, next = pointer to start of
|
|
* unread data.
|
|
* THIS FUNCTION MODIFIES THE STRING AND DETECTS INSIDE A NONTERMINATED STRING
|
|
*/
|
|
gboolean
|
|
__gst_props_parse_string (gchar *r, gchar **end, gchar **next)
|
|
{
|
|
gchar *w;
|
|
gchar c = '\0';
|
|
|
|
w = r;
|
|
if (*r == '\'' || *r == '\"') {
|
|
c = *r;
|
|
r++;
|
|
}
|
|
|
|
for (;;r++) {
|
|
if (*r == '\0') {
|
|
if (c) {
|
|
goto error;
|
|
} else {
|
|
goto found;
|
|
}
|
|
}
|
|
|
|
if (*r == '\\') {
|
|
r++;
|
|
if (*r == '\0')
|
|
goto error;
|
|
*w++ = *r;
|
|
continue;
|
|
}
|
|
|
|
if (*r == c) {
|
|
r++;
|
|
if (*r == '\0')
|
|
goto found;
|
|
break;
|
|
}
|
|
|
|
if (!c) {
|
|
if (g_ascii_isspace (*r))
|
|
break;
|
|
/* this needs to be escaped */
|
|
if (*r == ',' || *r == ')' || *r == ']' || *r == ':' ||
|
|
*r == ';' || *r == '(' || *r == '[')
|
|
break;
|
|
}
|
|
*w++ = *r;
|
|
}
|
|
|
|
found:
|
|
while (g_ascii_isspace (*r)) r++;
|
|
if (w != r)
|
|
*w++ = '\0';
|
|
*end = w;
|
|
*next = r;
|
|
return TRUE;
|
|
|
|
error:
|
|
return FALSE;
|
|
}
|
|
static GstPropsEntry *
|
|
gst_props_entry_from_string_no_name (gchar *s, gchar **after, gboolean has_type)
|
|
{
|
|
GstPropsEntry *entry;
|
|
gchar org = '\0';
|
|
gchar *end, *next, *check = s;
|
|
GstPropsType type = GST_PROPS_INVALID_TYPE;
|
|
/* [TYPE=]VALUE */
|
|
/*
|
|
* valid type identifiers case insensitive:
|
|
* INT: "i", "int"
|
|
* FLOAT: "f", "float"
|
|
* FOURCC: "4", "fourcc"
|
|
* BOOLEAN: "b", "bool", "boolean"
|
|
* STRING: "s", "str", "string"
|
|
* lists/ranges are identified by the value
|
|
*/
|
|
if (g_ascii_tolower (s[0]) == 'i') {
|
|
type = GST_PROPS_INT_TYPE;
|
|
if (g_ascii_tolower (s[1]) == 'n' && g_ascii_tolower (s[2]) == 't') {
|
|
check = s + 3;
|
|
} else {
|
|
check = s + 1;
|
|
}
|
|
} else if (g_ascii_tolower (s[0]) == 'f') {
|
|
type = GST_PROPS_FLOAT_TYPE;
|
|
if (g_ascii_tolower (s[1]) == 'l' && g_ascii_tolower (s[2]) == 'o' &&
|
|
g_ascii_tolower (s[3]) == 'a' && g_ascii_tolower (s[4]) == 't') {
|
|
check = s + 5;
|
|
} else if (g_ascii_tolower (s[1]) == 'o' && g_ascii_tolower (s[2]) == 'u' &&
|
|
g_ascii_tolower (s[3]) == 'r' && g_ascii_tolower (s[4]) == 'c' &&
|
|
g_ascii_tolower (s[5]) == 'c') {
|
|
check = s + 6;
|
|
type = GST_PROPS_FOURCC_TYPE;
|
|
} else {
|
|
check = s + 1;
|
|
}
|
|
} else if (g_ascii_tolower (s[0]) == '4') {
|
|
type = GST_PROPS_FOURCC_TYPE;
|
|
check = s + 1;
|
|
} else if (g_ascii_tolower (s[0]) == 'b') {
|
|
type = GST_PROPS_BOOLEAN_TYPE;
|
|
if (g_ascii_tolower (s[1]) == 'o' && g_ascii_tolower (s[2]) == 'o' &&
|
|
g_ascii_tolower (s[3]) == 'l') {
|
|
if (g_ascii_tolower (s[4]) == 'e' && g_ascii_tolower (s[5]) == 'a' &&
|
|
g_ascii_tolower (s[6]) == 'n') {
|
|
check = s + 7;
|
|
} else {
|
|
check = s + 4;
|
|
}
|
|
} else {
|
|
check = s + 1;
|
|
}
|
|
} else if (g_ascii_tolower (s[0]) == 's') {
|
|
type = GST_PROPS_STRING_TYPE;
|
|
if (g_ascii_tolower (s[1]) == 't' && g_ascii_tolower (s[2]) == 'r') {
|
|
if (g_ascii_tolower (s[3]) == 'i' && g_ascii_tolower (s[4]) == 'n' &&
|
|
g_ascii_tolower (s[5]) == 'g') {
|
|
check = s + 6;
|
|
} else {
|
|
check = s + 3;
|
|
}
|
|
} else {
|
|
check = s + 1;
|
|
}
|
|
} else if (g_ascii_tolower (s[0]) == 'l') {
|
|
type = GST_PROPS_LIST_TYPE;
|
|
if (g_ascii_tolower (s[1]) == 'i' && g_ascii_tolower (s[2]) == 's' &&
|
|
g_ascii_tolower (s[3]) == 't') {
|
|
check = s + 4;
|
|
} else {
|
|
check = s + 1;
|
|
}
|
|
}
|
|
while (g_ascii_isspace(*check)) check++;
|
|
if (*check != '=') {
|
|
if (has_type) goto error;
|
|
type = GST_PROPS_INVALID_TYPE;
|
|
check = s;
|
|
} else {
|
|
check++;
|
|
while (g_ascii_isspace(*check)) check++;
|
|
}
|
|
/* ok. Now type is GST_PROPS_INVALID_TYPE for guessing or the selected type.
|
|
check points to the string containing the contents. s still is the beginning
|
|
of the string */
|
|
if (type == GST_PROPS_INVALID_TYPE || type == GST_PROPS_INT_TYPE || type == GST_PROPS_FOURCC_TYPE) {
|
|
glong l;
|
|
l = strtol (check, &end, 0);
|
|
while (g_ascii_isspace (*end)) end++;
|
|
if (*end == '\0' || *end == ',' || *end == ';' || *end == ')' || *end == ']') {
|
|
*after = end;
|
|
entry = gst_props_alloc_entry ();
|
|
if (type == GST_PROPS_FOURCC_TYPE) {
|
|
entry->propstype = GST_PROPS_FOURCC_TYPE;
|
|
entry->data.fourcc_data = l;
|
|
} else {
|
|
entry->propstype = GST_PROPS_INT_TYPE;
|
|
entry->data.int_data = l;
|
|
}
|
|
return entry;
|
|
}
|
|
}
|
|
if (type == GST_PROPS_INVALID_TYPE || type == GST_PROPS_FLOAT_TYPE) {
|
|
gdouble d;
|
|
d = g_ascii_strtod (check, &end);
|
|
while (g_ascii_isspace (*end)) end++;
|
|
if (*end == '\0' || *end == ',' || *end == ';' || *end == ')' || *end == ']') {
|
|
*after = end;
|
|
entry = gst_props_alloc_entry ();
|
|
entry->propstype = GST_PROPS_FLOAT_TYPE;
|
|
entry->data.float_data = d;
|
|
return entry;
|
|
}
|
|
}
|
|
if ((type == GST_PROPS_INVALID_TYPE || type == GST_PROPS_FLOAT_TYPE ||
|
|
type == GST_PROPS_INT_TYPE) && *check == '[') {
|
|
GstPropsEntry *min, *max;
|
|
check++;
|
|
if (g_ascii_isspace (*check)) check++;
|
|
min = gst_props_entry_from_string_no_name (check, &check, FALSE);
|
|
if (!min) goto error;
|
|
if (*check++ != ',') goto error;
|
|
if (g_ascii_isspace (*check)) check++;
|
|
max = gst_props_entry_from_string_no_name (check, &check, FALSE);
|
|
if (!max || *check++ != ']') {
|
|
gst_props_entry_destroy (min);
|
|
goto error;
|
|
}
|
|
if (g_ascii_isspace (*check)) check++;
|
|
entry = gst_props_alloc_entry ();
|
|
entry->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
|
|
if (min->propstype == GST_PROPS_INT_TYPE && max->propstype == GST_PROPS_INT_TYPE && type != GST_PROPS_FLOAT_TYPE) {
|
|
entry->propstype = GST_PROPS_INT_RANGE_TYPE;
|
|
entry->data.int_range_data.min = min->data.int_data;
|
|
entry->data.int_range_data.max = max->data.int_data;
|
|
} else if (min->propstype == GST_PROPS_INT_TYPE && max->propstype == GST_PROPS_INT_TYPE && type == GST_PROPS_FLOAT_TYPE) {
|
|
entry->data.float_range_data.min = min->data.int_data;
|
|
entry->data.float_range_data.max = max->data.int_data;
|
|
} else if (min->propstype == GST_PROPS_INT_TYPE && max->propstype == GST_PROPS_FLOAT_TYPE && type != GST_PROPS_INT_TYPE) {
|
|
entry->data.float_range_data.min = min->data.int_data;
|
|
entry->data.float_range_data.max = max->data.float_data;
|
|
} else if (min->propstype == GST_PROPS_FLOAT_TYPE && max->propstype == GST_PROPS_INT_TYPE && type != GST_PROPS_INT_TYPE) {
|
|
entry->data.float_range_data.min = min->data.float_data;
|
|
entry->data.float_range_data.max = max->data.int_data;
|
|
} else if (min->propstype == GST_PROPS_FLOAT_TYPE && max->propstype == GST_PROPS_FLOAT_TYPE && type != GST_PROPS_INT_TYPE) {
|
|
entry->data.float_range_data.min = min->data.float_data;
|
|
entry->data.float_range_data.max = max->data.float_data;
|
|
} else {
|
|
gst_props_entry_destroy (min);
|
|
gst_props_entry_destroy (max);
|
|
gst_props_entry_destroy (entry);
|
|
goto error;
|
|
}
|
|
gst_props_entry_destroy (min);
|
|
gst_props_entry_destroy (max);
|
|
*after = check;
|
|
return entry;
|
|
}
|
|
if ((type == GST_PROPS_INVALID_TYPE || type == GST_PROPS_LIST_TYPE) && *check == '(') {
|
|
GstPropsEntry *next;
|
|
check++;
|
|
entry = gst_props_alloc_entry ();
|
|
entry->propstype = GST_PROPS_LIST_TYPE;
|
|
entry->data.list_data.entries = NULL;
|
|
do {
|
|
while (g_ascii_isspace (*check)) check++;
|
|
next = gst_props_entry_from_string_no_name (check, &check, FALSE);
|
|
/* use g_list_append to keep original order */
|
|
entry->data.list_data.entries = g_list_append (entry->data.list_data.entries, next);
|
|
if (*check == ')') break;
|
|
if (*check++ != ',') goto error;
|
|
} while (TRUE);
|
|
check++;
|
|
while (g_ascii_isspace (*check)) check++;
|
|
*after = check;
|
|
return entry;
|
|
}
|
|
if (!__gst_props_parse_string (check, &next, &end))
|
|
goto error;
|
|
if (next == end) {
|
|
org = *end;
|
|
*end = '\0';
|
|
}
|
|
if (type == GST_PROPS_INVALID_TYPE || type == GST_PROPS_BOOLEAN_TYPE) {
|
|
if (!(g_ascii_strcasecmp (check, "true") &&
|
|
g_ascii_strcasecmp (check, "yes"))) {
|
|
entry = gst_props_alloc_entry ();
|
|
entry->propstype = GST_PROPS_BOOLEAN_TYPE;
|
|
entry->data.bool_data = TRUE;
|
|
goto string_out;
|
|
}
|
|
if (!(g_ascii_strcasecmp (check, "false") &&
|
|
g_ascii_strcasecmp (check, "no"))) {
|
|
entry = gst_props_alloc_entry ();
|
|
entry->propstype = GST_PROPS_BOOLEAN_TYPE;
|
|
entry->data.bool_data = FALSE;
|
|
goto string_out;
|
|
}
|
|
}
|
|
if (type == GST_PROPS_FOURCC_TYPE) {
|
|
gint l = strlen (check);
|
|
entry = gst_props_alloc_entry ();
|
|
entry->propstype = GST_PROPS_FOURCC_TYPE;
|
|
entry->data.fourcc_data = GST_MAKE_FOURCC(l > 0 ? check[0] : ' ',
|
|
l > 1 ? check[1] : ' ',
|
|
l > 2 ? check[2] : ' ',
|
|
l > 3 ? check[3] : ' ');
|
|
goto string_out;
|
|
}
|
|
if (type == GST_PROPS_INVALID_TYPE || type == GST_PROPS_STRING_TYPE) {
|
|
entry = gst_props_alloc_entry ();
|
|
entry->propstype = GST_PROPS_STRING_TYPE;
|
|
entry->data.string_data.string = g_strdup (check);
|
|
goto string_out;
|
|
}
|
|
error:
|
|
return NULL;
|
|
|
|
string_out:
|
|
*next = org;
|
|
*after = end;
|
|
return entry;
|
|
}
|
|
static GstPropsEntry *
|
|
gst_props_entry_from_string (gchar *str, gchar **after)
|
|
{
|
|
/* NAME[:TYPE]=VALUE */
|
|
gchar *name;
|
|
gchar *s, *del;
|
|
gboolean has_type = FALSE;
|
|
GstPropsEntry *entry;
|
|
|
|
name = s = str;
|
|
while (g_ascii_isalnum (*s) || *s == '_' || *s == '-') s++;
|
|
del = s;
|
|
while (g_ascii_isspace (*s)) s++;
|
|
if (!(*s == '=' || *s == ':')) return NULL;
|
|
if (*s == ':') has_type = TRUE;
|
|
s++;
|
|
while (g_ascii_isspace (*s)) s++;
|
|
*del = '\0';
|
|
|
|
entry = gst_props_entry_from_string_no_name (s, &s, has_type);
|
|
if (entry) {
|
|
entry->propid = g_quark_from_string (name);
|
|
*after = s;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
GstProps *
|
|
__gst_props_from_string_func (gchar *s, gchar **end, gboolean caps)
|
|
{
|
|
GstProps *props;
|
|
GstPropsEntry *entry;
|
|
|
|
props = gst_props_empty_new ();
|
|
for (;;) {
|
|
entry = gst_props_entry_from_string (s, &s);
|
|
if (!entry) goto error;
|
|
gst_props_add_entry (props, entry);
|
|
while (g_ascii_isspace (*s)) s++;
|
|
if ((*s == '\0') || /* end */
|
|
(caps && (*s == ';'))) /* another caps */
|
|
break;
|
|
if (!(*s == ',')) goto error;
|
|
s++;
|
|
while (g_ascii_isspace (*s)) s++;
|
|
}
|
|
|
|
*end = s;
|
|
return props;
|
|
error:
|
|
gst_props_unref (props);
|
|
return NULL;
|
|
}
|
|
/**
|
|
* gst_props_from_string:
|
|
* str: the str to convert into props
|
|
*
|
|
* Tries to convert a string into a #GstProps. This is mainly intended for
|
|
* debugging purposes. The returned props are floating.
|
|
*
|
|
* Returns: A floating props or NULL if the string couldn't be converted
|
|
*/
|
|
GstProps *
|
|
gst_props_from_string (gchar *str)
|
|
{
|
|
/*
|
|
* format to parse is ENTRY[,ENTRY ...]
|
|
* ENTRY is NAME[:TYPE]=VALUE
|
|
* NAME is alphanumeric
|
|
* TYPE is a list of values
|
|
* VALUE is evil, see gst_props_entry_to_string
|
|
*/
|
|
GstProps *props;
|
|
gchar *temp, *org;
|
|
|
|
g_return_val_if_fail (str != NULL, NULL);
|
|
|
|
org = g_strdup (str);
|
|
props = __gst_props_from_string_func (org, &temp, FALSE);
|
|
g_free (org);
|
|
|
|
return props;
|
|
}
|
|
|
|
void
|
|
_gst_props_initialize (void)
|
|
{
|
|
_gst_props_entries_chunk = gst_mem_chunk_new ("GstPropsEntries",
|
|
sizeof (GstPropsEntry), sizeof (GstPropsEntry) * 1024,
|
|
G_ALLOC_AND_FREE);
|
|
|
|
_gst_props_chunk = gst_mem_chunk_new ("GstProps",
|
|
sizeof (GstProps), sizeof (GstProps) * 256,
|
|
G_ALLOC_AND_FREE);
|
|
|
|
_gst_props_type = g_boxed_type_register_static ("GstProps",
|
|
(GBoxedCopyFunc) gst_props_ref,
|
|
(GBoxedFreeFunc) gst_props_unref);
|
|
|
|
g_value_register_transform_func (_gst_props_type, G_TYPE_STRING,
|
|
gst_props_transform_to_string);
|
|
|
|
_gst_props_entry_type = g_boxed_type_register_static ("GstPropsEntry",
|
|
(GBoxedCopyFunc) gst_props_entry_copy,
|
|
(GBoxedFreeFunc) gst_props_entry_destroy);
|
|
|
|
#ifndef GST_DISABLE_TRACE
|
|
_props_trace = gst_alloc_trace_register (GST_PROPS_TRACE_NAME);
|
|
_entries_trace = gst_alloc_trace_register (GST_PROPS_ENTRY_TRACE_NAME);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
gst_props_debug_entry (GstPropsEntry *entry)
|
|
{
|
|
/* unused when debugging is disabled */
|
|
G_GNUC_UNUSED const gchar *name = g_quark_to_string (entry->propid);
|
|
|
|
switch (entry->propstype) {
|
|
case GST_PROPS_INT_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: %s: int %d", entry, name, entry->data.int_data);
|
|
break;
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: %s: float %g", entry, name, entry->data.float_data);
|
|
break;
|
|
case GST_PROPS_FOURCC_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: %s: fourcc %c%c%c%c", entry, name,
|
|
(entry->data.fourcc_data>>0)&0xff,
|
|
(entry->data.fourcc_data>>8)&0xff,
|
|
(entry->data.fourcc_data>>16)&0xff,
|
|
(entry->data.fourcc_data>>24)&0xff);
|
|
break;
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: %s: bool %d", entry, name, entry->data.bool_data);
|
|
break;
|
|
case GST_PROPS_STRING_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: %s: string \"%s\"", entry, name, entry->data.string_data.string);
|
|
break;
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: %s: int range %d-%d", entry, name, entry->data.int_range_data.min,
|
|
entry->data.int_range_data.max);
|
|
break;
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: %s: float range %g-%g", entry, name, entry->data.float_range_data.min,
|
|
entry->data.float_range_data.max);
|
|
break;
|
|
case GST_PROPS_LIST_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%p: [list]", entry);
|
|
g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_debug_entry, NULL);
|
|
break;
|
|
default:
|
|
g_warning ("unknown property type %d at %p", entry->propstype, entry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gint
|
|
props_compare_func (gconstpointer a,
|
|
gconstpointer b)
|
|
{
|
|
GstPropsEntry *entry1 = (GstPropsEntry *)a;
|
|
GstPropsEntry *entry2 = (GstPropsEntry *)b;
|
|
|
|
return (entry1->propid - entry2->propid);
|
|
}
|
|
|
|
static gint
|
|
props_find_func (gconstpointer a,
|
|
gconstpointer b)
|
|
{
|
|
GstPropsEntry *entry2 = (GstPropsEntry *)a;
|
|
GQuark quark = (GQuark) GPOINTER_TO_INT (b);
|
|
|
|
return (quark - entry2->propid);
|
|
}
|
|
|
|
/* This is implemented as a huge macro because we cannot pass
|
|
* va_list variables by reference on some architectures.
|
|
*/
|
|
static inline GstPropsType
|
|
gst_props_type_sanitize (GstPropsType type)
|
|
{
|
|
switch (type) {
|
|
case GST_PROPS_INT_TYPE:
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
return GST_PROPS_INT_TYPE;
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
return GST_PROPS_FLOAT_TYPE;
|
|
case GST_PROPS_FOURCC_TYPE:
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
case GST_PROPS_STRING_TYPE:
|
|
return type;
|
|
case GST_PROPS_LIST_TYPE:
|
|
case GST_PROPS_GLIST_TYPE:
|
|
return GST_PROPS_LIST_TYPE;
|
|
case GST_PROPS_END_TYPE:
|
|
case GST_PROPS_INVALID_TYPE:
|
|
case GST_PROPS_VAR_TYPE:
|
|
case GST_PROPS_LAST_TYPE:
|
|
break;
|
|
}
|
|
g_assert_not_reached ();
|
|
return GST_PROPS_END_TYPE;
|
|
}
|
|
#define GST_PROPS_ENTRY_FILL(entry, var_args) \
|
|
G_STMT_START { \
|
|
entry->propstype = va_arg (var_args, GstPropsType); \
|
|
if (entry->propstype == GST_PROPS_LIST_TYPE) { \
|
|
GList *_list = NULL; \
|
|
GstPropsEntry *_cur = NULL; /* initialize so gcc doesn't complain */ \
|
|
GstPropsType _cur_type; \
|
|
GstPropsType _type = va_arg (var_args, GstPropsType); \
|
|
_cur_type = _type; \
|
|
_type = gst_props_type_sanitize (_type); \
|
|
while (_cur_type != GST_PROPS_END_TYPE) { \
|
|
_cur = gst_props_alloc_entry (); \
|
|
_cur->propid = entry->propid; \
|
|
_cur->propstype = _cur_type; \
|
|
_cur_type = gst_props_type_sanitize (_cur_type); \
|
|
g_assert (_cur_type == _type); \
|
|
GST_PROPS_ENTRY_FILL_DATA(_cur, var_args); \
|
|
if (_cur_type == GST_PROPS_INT_TYPE) { \
|
|
_list = gst_props_add_to_int_list (_list, _cur); \
|
|
} else { \
|
|
_list = g_list_prepend (_list, _cur); \
|
|
} \
|
|
_cur_type = va_arg (var_args, GstPropsType); \
|
|
} \
|
|
if (_list && g_list_next (_list)) { \
|
|
entry->data.list_data.entries = _list; \
|
|
} else { \
|
|
entry->propstype = _cur->propstype; \
|
|
entry->data = _cur->data; \
|
|
gst_props_entry_free (_cur); \
|
|
} \
|
|
} else { \
|
|
GST_PROPS_ENTRY_FILL_DATA(entry, var_args); \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
#define GST_PROPS_ENTRY_FILL_DATA(entry, var_args) \
|
|
G_STMT_START { \
|
|
switch (entry->propstype) { \
|
|
case GST_PROPS_INT_TYPE: \
|
|
entry->data.int_data = va_arg (var_args, gint); \
|
|
break; \
|
|
case GST_PROPS_INT_RANGE_TYPE: \
|
|
entry->data.int_range_data.min = va_arg (var_args, gint); \
|
|
entry->data.int_range_data.max = va_arg (var_args, gint); \
|
|
break; \
|
|
case GST_PROPS_FLOAT_TYPE: \
|
|
entry->data.float_data = va_arg (var_args, gdouble); \
|
|
break; \
|
|
case GST_PROPS_FLOAT_RANGE_TYPE: \
|
|
entry->data.float_range_data.min = va_arg (var_args, gdouble); \
|
|
entry->data.float_range_data.max = va_arg (var_args, gdouble); \
|
|
break; \
|
|
case GST_PROPS_FOURCC_TYPE: \
|
|
entry->data.fourcc_data = va_arg (var_args, gulong); \
|
|
break; \
|
|
case GST_PROPS_BOOLEAN_TYPE: \
|
|
entry->data.bool_data = va_arg (var_args, gboolean); \
|
|
break; \
|
|
case GST_PROPS_STRING_TYPE: \
|
|
entry->data.string_data.string = g_strdup (va_arg (var_args, gchar*)); \
|
|
break; \
|
|
case GST_PROPS_GLIST_TYPE: \
|
|
entry->propstype = GST_PROPS_LIST_TYPE; \
|
|
entry->data.list_data.entries = g_list_copy (va_arg (var_args, GList*)); \
|
|
break; \
|
|
case GST_PROPS_END_TYPE: \
|
|
break; \
|
|
default: \
|
|
g_warning("attempt to set invalid props type\n"); \
|
|
break; \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
|
|
#define GST_PROPS_ENTRY_READ(entry, var_args, safe, result) \
|
|
G_STMT_START { \
|
|
\
|
|
*result = TRUE; \
|
|
\
|
|
if (safe) { \
|
|
GstPropsType propstype = va_arg (var_args, GstPropsType); \
|
|
if (propstype != entry->propstype) { \
|
|
*result = FALSE; \
|
|
} \
|
|
} \
|
|
if (*result) { \
|
|
switch (entry->propstype) { \
|
|
case GST_PROPS_INT_TYPE: \
|
|
*(va_arg (var_args, gint*)) = entry->data.int_data; \
|
|
break; \
|
|
case GST_PROPS_INT_RANGE_TYPE: \
|
|
*(va_arg (var_args, gint*)) = entry->data.int_range_data.min; \
|
|
*(va_arg (var_args, gint*)) = entry->data.int_range_data.max; \
|
|
break; \
|
|
case GST_PROPS_FLOAT_TYPE: \
|
|
*(va_arg (var_args, gfloat*)) = entry->data.float_data; \
|
|
break; \
|
|
case GST_PROPS_FLOAT_RANGE_TYPE: \
|
|
*(va_arg (var_args, gfloat*)) = entry->data.float_range_data.min; \
|
|
*(va_arg (var_args, gfloat*)) = entry->data.float_range_data.max; \
|
|
break; \
|
|
case GST_PROPS_FOURCC_TYPE: \
|
|
*(va_arg (var_args, guint32*)) = entry->data.fourcc_data; \
|
|
break; \
|
|
case GST_PROPS_BOOLEAN_TYPE: \
|
|
*(va_arg (var_args, gboolean*)) = entry->data.bool_data; \
|
|
break; \
|
|
case GST_PROPS_STRING_TYPE: \
|
|
*(va_arg (var_args, gchar**)) = entry->data.string_data.string; \
|
|
break; \
|
|
case GST_PROPS_LIST_TYPE: \
|
|
*(va_arg (var_args, GList**)) = entry->data.list_data.entries; \
|
|
break; \
|
|
case GST_PROPS_END_TYPE: \
|
|
break; \
|
|
default: \
|
|
*result = FALSE; \
|
|
break; \
|
|
} \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
static GstPropsEntry*
|
|
gst_props_alloc_entry (void)
|
|
{
|
|
GstPropsEntry *entry;
|
|
|
|
entry = gst_mem_chunk_alloc (_gst_props_entries_chunk);
|
|
#ifndef GST_DISABLE_TRACE
|
|
gst_alloc_trace_new (_entries_trace, entry);
|
|
#endif
|
|
|
|
GST_CAT_LOG (GST_CAT_PROPERTIES, "new entry %p", entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
static void
|
|
gst_props_entry_clean (GstPropsEntry *entry)
|
|
{
|
|
switch (entry->propstype) {
|
|
case GST_PROPS_STRING_TYPE:
|
|
g_free (entry->data.string_data.string);
|
|
break;
|
|
case GST_PROPS_LIST_TYPE:
|
|
g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_entry_destroy, NULL);
|
|
g_list_free (entry->data.list_data.entries);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
static inline void
|
|
gst_props_entry_free (GstPropsEntry *entry)
|
|
{
|
|
#ifdef USE_POISONING
|
|
memset (entry, 0xff, sizeof(*entry));
|
|
#endif
|
|
gst_mem_chunk_free (_gst_props_entries_chunk, entry);
|
|
#ifndef GST_DISABLE_TRACE
|
|
gst_alloc_trace_free (_entries_trace, entry);
|
|
#endif
|
|
}
|
|
/**
|
|
* gst_props_entry_destroy:
|
|
* @entry: the entry to destroy
|
|
*
|
|
* Free the given propsentry
|
|
*/
|
|
void
|
|
gst_props_entry_destroy (GstPropsEntry *entry)
|
|
{
|
|
if (!entry) return;
|
|
|
|
GST_CAT_LOG (GST_CAT_PROPERTIES, "destroy entry %p", entry);
|
|
|
|
gst_props_entry_clean (entry);
|
|
|
|
gst_props_entry_free (entry);
|
|
}
|
|
|
|
GType
|
|
gst_props_get_type (void)
|
|
{
|
|
return _gst_props_type;
|
|
}
|
|
|
|
/**
|
|
* gst_props_empty_new:
|
|
*
|
|
* Create a new empty property.
|
|
*
|
|
* Returns: the new property
|
|
*/
|
|
GstProps*
|
|
gst_props_empty_new (void)
|
|
{
|
|
GstProps *props;
|
|
|
|
props = gst_mem_chunk_alloc (_gst_props_chunk);
|
|
#ifndef GST_DISABLE_TRACE
|
|
gst_alloc_trace_new (_props_trace, props);
|
|
#endif
|
|
|
|
GST_CAT_LOG (GST_CAT_PROPERTIES, "new %p", props);
|
|
|
|
props->properties = NULL;
|
|
props->refcount = 1;
|
|
GST_PROPS_FLAG_SET (props, GST_PROPS_FLOATING);
|
|
GST_PROPS_FLAG_SET (props, GST_PROPS_FIXED);
|
|
|
|
return props;
|
|
}
|
|
|
|
/**
|
|
* gst_props_replace:
|
|
* @oldprops: the props to take replace
|
|
* @newprops: the props to take replace
|
|
*
|
|
* Replace the pointer to the props, doing proper
|
|
* refcounting.
|
|
*/
|
|
void
|
|
gst_props_replace (GstProps **oldprops, GstProps *newprops)
|
|
{
|
|
if (*oldprops != newprops) {
|
|
if (newprops) gst_props_ref (newprops);
|
|
if (*oldprops) gst_props_unref (*oldprops);
|
|
|
|
*oldprops = newprops;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_props_replace_sink:
|
|
* @oldprops: the props to take replace
|
|
* @newprops: the props to take replace
|
|
*
|
|
* Replace the pointer to the props and take ownership.
|
|
*/
|
|
void
|
|
gst_props_replace_sink (GstProps **oldprops, GstProps *newprops)
|
|
{
|
|
gst_props_replace (oldprops, newprops);
|
|
gst_props_sink (newprops);
|
|
}
|
|
|
|
/**
|
|
* gst_props_add_entry:
|
|
* @props: the property to add the entry to
|
|
* @entry: the entry to add
|
|
*
|
|
* Addes the given propsentry to the props
|
|
*/
|
|
void
|
|
gst_props_add_entry (GstProps *props, GstPropsEntry *entry)
|
|
{
|
|
g_return_if_fail (props);
|
|
g_return_if_fail (entry);
|
|
|
|
/* only variable properties can change the fixed flag */
|
|
if (GST_PROPS_IS_FIXED (props) && GST_PROPS_ENTRY_IS_VARIABLE (entry)) {
|
|
GST_PROPS_FLAG_UNSET (props, GST_PROPS_FIXED);
|
|
}
|
|
props->properties = g_list_insert_sorted (props->properties, entry, props_compare_func);
|
|
}
|
|
|
|
static void
|
|
gst_props_remove_entry_by_id (GstProps *props, GQuark propid)
|
|
{
|
|
GList *properties;
|
|
gboolean found;
|
|
|
|
/* assume fixed */
|
|
GST_PROPS_FLAG_SET (props, GST_PROPS_FIXED);
|
|
|
|
found = FALSE;
|
|
|
|
properties = props->properties;
|
|
while (properties) {
|
|
GList *current = properties;
|
|
GstPropsEntry *lentry = (GstPropsEntry *) current->data;
|
|
|
|
properties = g_list_next (properties);
|
|
|
|
if (lentry->propid == propid) {
|
|
found = TRUE;
|
|
props->properties = g_list_delete_link (props->properties, current);
|
|
gst_props_entry_destroy (lentry);
|
|
}
|
|
else if (GST_PROPS_ENTRY_IS_VARIABLE (lentry)) {
|
|
GST_PROPS_FLAG_UNSET (props, GST_PROPS_FIXED);
|
|
/* no need to check for further variable entries
|
|
* if we already removed the entry */
|
|
if (found)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* gst_props_remove_entry:
|
|
* @props: the property to remove the entry from
|
|
* @entry: the entry to remove
|
|
*
|
|
* Removes the given propsentry from the props.
|
|
*/
|
|
void
|
|
gst_props_remove_entry (GstProps *props, GstPropsEntry *entry)
|
|
{
|
|
g_return_if_fail (props != NULL);
|
|
g_return_if_fail (entry != NULL);
|
|
|
|
gst_props_remove_entry_by_id (props, entry->propid);
|
|
}
|
|
|
|
/**
|
|
* gst_props_remove_entry_by_name:
|
|
* @props: the property to remove the entry from
|
|
* @name: the name of the entry to remove
|
|
*
|
|
* Removes the propsentry with the given name from the props.
|
|
*/
|
|
void
|
|
gst_props_remove_entry_by_name (GstProps *props, const gchar *name)
|
|
{
|
|
GQuark quark;
|
|
|
|
g_return_if_fail (props != NULL);
|
|
g_return_if_fail (name != NULL);
|
|
|
|
quark = g_quark_from_string (name);
|
|
gst_props_remove_entry_by_id (props, quark);
|
|
}
|
|
|
|
/**
|
|
* gst_props_new:
|
|
* @firstname: the first property name
|
|
* @...: the property values
|
|
*
|
|
* Create a new property from the given key/value pairs
|
|
*
|
|
* Returns: the new property
|
|
*/
|
|
GstProps*
|
|
gst_props_new (const gchar *firstname, ...)
|
|
{
|
|
GstProps *props;
|
|
va_list var_args;
|
|
|
|
va_start (var_args, firstname);
|
|
|
|
props = gst_props_newv (firstname, var_args);
|
|
|
|
va_end (var_args);
|
|
|
|
return props;
|
|
}
|
|
|
|
/**
|
|
* gst_props_debug:
|
|
* @props: the props to debug
|
|
*
|
|
* Dump the contents of the given properties into the DEBUG log.
|
|
*/
|
|
void
|
|
gst_props_debug (GstProps *props)
|
|
{
|
|
if (!props) {
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "props (null)");
|
|
return;
|
|
}
|
|
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "props %p, refcount %d, flags %d",
|
|
props, props->refcount, props->flags);
|
|
|
|
g_list_foreach (props->properties, (GFunc) gst_props_debug_entry, NULL);
|
|
}
|
|
|
|
/**
|
|
* gst_props_merge_int_entries:
|
|
* @newentry: the new entry
|
|
* @oldentry: an old entry
|
|
*
|
|
* Tries to merge oldentry into newentry, if there is a simpler single entry which represents
|
|
*
|
|
* Assumes that the entries are either ints or int ranges.
|
|
*
|
|
* Returns: TRUE if the entries were merged, FALSE otherwise.
|
|
*/
|
|
static gboolean
|
|
gst_props_merge_int_entries(GstPropsEntry *newentry, GstPropsEntry *oldentry)
|
|
{
|
|
gint new_min, new_max, old_min, old_max;
|
|
gboolean can_merge = FALSE;
|
|
|
|
if (newentry->propstype == GST_PROPS_INT_TYPE) {
|
|
new_min = newentry->data.int_data;
|
|
new_max = newentry->data.int_data;
|
|
} else {
|
|
new_min = newentry->data.int_range_data.min;
|
|
new_max = newentry->data.int_range_data.max;
|
|
}
|
|
|
|
if (oldentry->propstype == GST_PROPS_INT_TYPE) {
|
|
old_min = oldentry->data.int_data;
|
|
old_max = oldentry->data.int_data;
|
|
} else {
|
|
old_min = oldentry->data.int_range_data.min;
|
|
old_max = oldentry->data.int_range_data.max;
|
|
}
|
|
|
|
/* Put range which starts lower into (new_min, new_max) */
|
|
if (old_min < new_min) {
|
|
gint tmp;
|
|
tmp = old_min;
|
|
old_min = new_min;
|
|
new_min = tmp;
|
|
tmp = old_max;
|
|
old_max = new_max;
|
|
new_max = tmp;
|
|
}
|
|
|
|
/* new_min is min of either entry - second half of the following conditional */
|
|
/* is to avoid overflow problems. */
|
|
if (new_max >= old_min - 1 && old_min - 1 < old_min) {
|
|
/* ranges overlap, or are adjacent. Pick biggest maximum. */
|
|
can_merge = TRUE;
|
|
if (old_max > new_max) new_max = old_max;
|
|
}
|
|
|
|
if (can_merge) {
|
|
if (new_min == new_max) {
|
|
newentry->propstype = GST_PROPS_INT_TYPE;
|
|
newentry->data.int_data = new_min;
|
|
} else {
|
|
newentry->propstype = GST_PROPS_INT_RANGE_TYPE;
|
|
newentry->data.int_range_data.min = new_min;
|
|
newentry->data.int_range_data.max = new_max;
|
|
}
|
|
}
|
|
return can_merge;
|
|
}
|
|
|
|
/**
|
|
* gst_props_add_to_int_list:
|
|
* @entries: the existing list of entries
|
|
* @entry: the new entry to add to the list
|
|
*
|
|
* Add an integer property to a list of properties, removing duplicates
|
|
* and merging ranges.
|
|
*
|
|
* Assumes that the existing list is in simplest form, contains
|
|
* only ints and int ranges, and that the new entry is an int or
|
|
* an int range.
|
|
*
|
|
* Returns: a pointer to a list with the new entry added.
|
|
*/
|
|
static GList*
|
|
gst_props_add_to_int_list (GList *entries, GstPropsEntry *newentry)
|
|
{
|
|
GList *i;
|
|
|
|
i = entries;
|
|
while (i) {
|
|
GstPropsEntry *oldentry = (GstPropsEntry *)(i->data);
|
|
gboolean merged = gst_props_merge_int_entries(newentry, oldentry);
|
|
|
|
if (merged) {
|
|
/* replace the existing one with the merged one */
|
|
gst_props_entry_destroy (oldentry);
|
|
|
|
entries = g_list_remove_link (entries, i);
|
|
g_list_free_1 (i);
|
|
|
|
/* start again: it's possible that this change made an earlier entry */
|
|
/* mergeable, and the pointer is now invalid anyway. */
|
|
i = entries;
|
|
}
|
|
|
|
i = g_list_next (i);
|
|
}
|
|
|
|
return g_list_prepend (entries, newentry);
|
|
}
|
|
|
|
GType
|
|
gst_props_entry_get_type (void)
|
|
{
|
|
return _gst_props_entry_type;
|
|
}
|
|
|
|
#define GST_PROPS_ENTRY_NEW(entry, name,var_args) \
|
|
G_STMT_START { \
|
|
entry = gst_props_alloc_entry (); \
|
|
entry->propid = g_quark_from_string (name); \
|
|
GST_PROPS_ENTRY_FILL (entry, var_args); \
|
|
} G_STMT_END
|
|
static GstPropsEntry*
|
|
gst_props_entry_newv (const gchar *name, va_list var_args)
|
|
{
|
|
GstPropsEntry *entry;
|
|
|
|
GST_PROPS_ENTRY_NEW (entry, name, var_args);
|
|
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_new:
|
|
* @name: the name of the props entry
|
|
* @...: the value of the entry
|
|
*
|
|
* Create a new property entry with the given key/value.
|
|
*
|
|
* Returns: the new entry.
|
|
*/
|
|
GstPropsEntry*
|
|
gst_props_entry_new (const gchar *name, ...)
|
|
{
|
|
va_list var_args;
|
|
GstPropsEntry *entry;
|
|
|
|
va_start (var_args, name);
|
|
entry = gst_props_entry_newv (name, var_args);
|
|
va_end (var_args);
|
|
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* gst_props_newv:
|
|
* @firstname: the first property name
|
|
* @var_args: the property values
|
|
*
|
|
* Create a new property from the list of entries.
|
|
*
|
|
* Returns: the new property created from the list of entries
|
|
*/
|
|
GstProps*
|
|
gst_props_newv (const gchar *firstname, va_list var_args)
|
|
{
|
|
GstProps *props;
|
|
const gchar *prop_name;
|
|
|
|
if (firstname == NULL)
|
|
return NULL;
|
|
|
|
props = gst_props_empty_new ();
|
|
|
|
prop_name = firstname;
|
|
|
|
/* properties */
|
|
while (prop_name) {
|
|
GstPropsEntry *entry;
|
|
|
|
GST_PROPS_ENTRY_NEW (entry, prop_name, var_args);
|
|
gst_props_add_entry (props, entry);
|
|
|
|
prop_name = va_arg (var_args, gchar*);
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
/**
|
|
* gst_props_set:
|
|
* @props: the props to modify
|
|
* @name: the name of the entry to modify
|
|
* @...: The prop entry.
|
|
*
|
|
* Modifies the value of the given entry in the props struct.
|
|
* For the optional args, use GST_PROPS_FOO, where FOO is INT,
|
|
* STRING, etc. This macro expands to a variable number of arguments,
|
|
* hence the lack of precision in the function prototype. No
|
|
* terminating NULL is necessary as only one property can be changed.
|
|
*
|
|
* Returns: the new modified property structure.
|
|
*/
|
|
GstProps*
|
|
gst_props_set (GstProps *props, const gchar *name, ...)
|
|
{
|
|
GQuark quark;
|
|
GList *properties;
|
|
va_list var_args;
|
|
gboolean found;
|
|
gboolean was_fixed;
|
|
|
|
g_return_val_if_fail (props != NULL, NULL);
|
|
|
|
found = FALSE;
|
|
was_fixed = GST_PROPS_IS_FIXED (props);
|
|
GST_PROPS_FLAG_SET (props, GST_PROPS_FIXED);
|
|
|
|
quark = g_quark_from_string (name);
|
|
/* this looks a little complicated but the idea is to get
|
|
* out of the loop ASAP. Changing the entry to a variable
|
|
* property immediatly marks the props as non-fixed.
|
|
* changing the entry to fixed when the props was fixed
|
|
* does not change the props and we can get out of the loop
|
|
* as well.
|
|
* When changing the entry to a fixed entry, we need to
|
|
* see if all entries are fixed before we can decide the props
|
|
*/
|
|
properties = props->properties;
|
|
while (properties) {
|
|
GstPropsEntry *entry;
|
|
|
|
entry = (GstPropsEntry *) properties->data;
|
|
|
|
if (entry->propid == quark) {
|
|
found = TRUE;
|
|
|
|
va_start (var_args, name);
|
|
gst_props_entry_clean (entry);
|
|
GST_PROPS_ENTRY_FILL (entry, var_args);
|
|
va_end (var_args);
|
|
|
|
/* if the props was fixed and we changed this entry
|
|
* with a fixed entry, we can stop now as the global
|
|
* props flag cannot change */
|
|
if (was_fixed && !GST_PROPS_ENTRY_IS_VARIABLE (entry))
|
|
break;
|
|
/* if we already found a non fixed entry we can exit */
|
|
if (!GST_PROPS_IS_FIXED (props))
|
|
break;
|
|
/* if the entry is variable, we'll get out of the loop
|
|
* in the next statement */
|
|
/* if the entry is fixed we have to check all other
|
|
* entries before we can decide if the props are fixed */
|
|
}
|
|
|
|
if (GST_PROPS_ENTRY_IS_VARIABLE (entry)) {
|
|
/* mark the props as variable */
|
|
GST_PROPS_FLAG_UNSET (props, GST_PROPS_FIXED);
|
|
/* if we already changed the entry, we can stop now */
|
|
if (found)
|
|
break;
|
|
}
|
|
properties = g_list_next (properties);
|
|
}
|
|
|
|
if (!found) {
|
|
g_warning ("gstprops: no property '%s' to change\n", name);
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
|
|
/**
|
|
* gst_props_unref:
|
|
* @props: the props to unref
|
|
*
|
|
* Decrease the refcount of the property structure, destroying
|
|
* the property if the refcount is 0.
|
|
*
|
|
* Returns: handle to unrefed props or NULL when it was
|
|
* destroyed.
|
|
*/
|
|
GstProps*
|
|
gst_props_unref (GstProps *props)
|
|
{
|
|
if (props == NULL)
|
|
return NULL;
|
|
|
|
g_return_val_if_fail (props->refcount > 0, NULL);
|
|
|
|
GST_CAT_LOG (GST_CAT_PROPERTIES, "unref %p (%d->%d)", props, props->refcount, props->refcount-1);
|
|
props->refcount--;
|
|
|
|
if (props->refcount == 0) {
|
|
gst_props_destroy (props);
|
|
return NULL;
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
/**
|
|
* gst_props_ref:
|
|
* @props: the props to ref
|
|
*
|
|
* Increase the refcount of the property structure.
|
|
*
|
|
* Returns: handle to refed props.
|
|
*/
|
|
GstProps*
|
|
gst_props_ref (GstProps *props)
|
|
{
|
|
if (props == NULL)
|
|
return NULL;
|
|
|
|
g_return_val_if_fail (props->refcount > 0, NULL);
|
|
|
|
GST_CAT_LOG (GST_CAT_PROPERTIES, "ref %p (%d->%d)", props, props->refcount, props->refcount+1);
|
|
|
|
props->refcount++;
|
|
|
|
return props;
|
|
}
|
|
|
|
/**
|
|
* gst_props_sink:
|
|
* @props: the props to sink
|
|
*
|
|
* If the props if floating, decrease its refcount. Usually used
|
|
* with gst_props_ref() to take ownership of the props.
|
|
*/
|
|
void
|
|
gst_props_sink (GstProps *props)
|
|
{
|
|
if (props == NULL)
|
|
return;
|
|
|
|
GST_CAT_LOG (GST_CAT_PROPERTIES, "sink %p", props);
|
|
|
|
if (GST_PROPS_IS_FLOATING (props)) {
|
|
GST_PROPS_FLAG_UNSET (props, GST_PROPS_FLOATING);
|
|
gst_props_unref (props);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_props_destroy:
|
|
* @props: the props to destroy
|
|
*
|
|
* Destroy the property, freeing all the memory that
|
|
* was allocated.
|
|
*/
|
|
static void
|
|
gst_props_destroy (GstProps *props)
|
|
{
|
|
if (props == NULL)
|
|
return;
|
|
|
|
g_list_foreach (props->properties, (GFunc) gst_props_entry_destroy, NULL);
|
|
g_list_free (props->properties);
|
|
|
|
#ifdef USE_POISONING
|
|
memset(props, 0xff, sizeof(*props));
|
|
#endif
|
|
gst_mem_chunk_free (_gst_props_chunk, props);
|
|
#ifndef GST_DISABLE_TRACE
|
|
gst_alloc_trace_free (_props_trace, props);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_copy:
|
|
* @entry: the entry to copy
|
|
*
|
|
* Copy the propsentry.
|
|
*
|
|
* Returns: a new #GstPropsEntry that is a copy of the original
|
|
* given entry.
|
|
*/
|
|
GstPropsEntry*
|
|
gst_props_entry_copy (const GstPropsEntry *entry)
|
|
{
|
|
GstPropsEntry *newentry;
|
|
|
|
newentry = gst_props_alloc_entry ();
|
|
memcpy (newentry, entry, sizeof (GstPropsEntry));
|
|
|
|
switch (entry->propstype) {
|
|
case GST_PROPS_LIST_TYPE:
|
|
newentry->data.list_data.entries = gst_props_list_copy (entry->data.list_data.entries);
|
|
break;
|
|
case GST_PROPS_STRING_TYPE:
|
|
newentry->data.string_data.string = g_strdup (entry->data.string_data.string);
|
|
break;
|
|
default:
|
|
/* FIXME more? */
|
|
break;
|
|
}
|
|
|
|
return newentry;
|
|
}
|
|
|
|
static GList*
|
|
gst_props_list_copy (GList *propslist)
|
|
{
|
|
GList *new = NULL;
|
|
|
|
while (propslist) {
|
|
GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
|
|
|
|
new = g_list_prepend (new, gst_props_entry_copy (entry));
|
|
|
|
propslist = g_list_next (propslist);
|
|
}
|
|
new = g_list_reverse (new);
|
|
|
|
return new;
|
|
}
|
|
|
|
/**
|
|
* gst_props_copy:
|
|
* @props: the props to copy
|
|
*
|
|
* Copy the property structure.
|
|
*
|
|
* Returns: the new property that is a copy of the original
|
|
* one.
|
|
*/
|
|
GstProps*
|
|
gst_props_copy (GstProps *props)
|
|
{
|
|
GstProps *new;
|
|
|
|
if (props == NULL)
|
|
return NULL;
|
|
|
|
new = gst_props_empty_new ();
|
|
new->properties = gst_props_list_copy (props->properties);
|
|
GST_PROPS_FLAGS (new) = GST_PROPS_FLAGS (props) | GST_PROPS_FLOATING;
|
|
|
|
return new;
|
|
}
|
|
|
|
/**
|
|
* gst_props_copy_on_write:
|
|
* @props: the props to copy on write
|
|
*
|
|
* Copy the property structure if the refcount is >1.
|
|
*
|
|
* Returns: A new props that can be safely written to.
|
|
*/
|
|
GstProps*
|
|
gst_props_copy_on_write (GstProps *props)
|
|
{
|
|
GstProps *new = props;;
|
|
|
|
g_return_val_if_fail (props != NULL, NULL);
|
|
|
|
if (props->refcount > 1) {
|
|
new = gst_props_copy (props);
|
|
gst_props_unref (props);
|
|
}
|
|
|
|
return new;
|
|
}
|
|
|
|
/**
|
|
* gst_props_get_entry:
|
|
* @props: the props to query
|
|
* @name: the name of the entry to get
|
|
*
|
|
* Get the props entry with the given name
|
|
*
|
|
* Returns: The props entry with the given name or NULL if
|
|
* the entry does not exist.
|
|
*/
|
|
const GstPropsEntry*
|
|
gst_props_get_entry (GstProps *props, const gchar *name)
|
|
{
|
|
GList *lentry;
|
|
GQuark quark;
|
|
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
if (props == NULL) return FALSE;
|
|
|
|
quark = g_quark_from_string (name);
|
|
|
|
lentry = g_list_find_custom (props->properties, GINT_TO_POINTER (quark), props_find_func);
|
|
|
|
if (lentry) {
|
|
GstPropsEntry *thisentry;
|
|
thisentry = (GstPropsEntry *)lentry->data;
|
|
return thisentry;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* gst_props_has_property:
|
|
* @props: the props to check
|
|
* @name: the name of the key to find
|
|
*
|
|
* Checks if a given props has a property with the given name.
|
|
*
|
|
* Returns: TRUE if the property was found, FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
gst_props_has_property (GstProps *props, const gchar *name)
|
|
{
|
|
return (gst_props_get_entry (props, name) != NULL);
|
|
}
|
|
|
|
/**
|
|
* gst_props_has_property_typed:
|
|
* @props: the props to check
|
|
* @name: the name of the key to find
|
|
* @type: the type of the required property
|
|
*
|
|
* Checks if a given props has a property with the given name and the given type.
|
|
*
|
|
* Returns: TRUE if the property was found, FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
gst_props_has_property_typed (GstProps *props, const gchar *name, GstPropsType type)
|
|
{
|
|
const GstPropsEntry *entry;
|
|
|
|
entry = gst_props_get_entry (props, name);
|
|
if (!entry)
|
|
return FALSE;
|
|
|
|
return (entry->propstype == type);
|
|
}
|
|
|
|
/**
|
|
* gst_props_has_fixed_property:
|
|
* @props: the props to check
|
|
* @name: the name of the key to find
|
|
*
|
|
* Checks if a given props has a property with the given name that
|
|
* is also fixed, ie. is not a list or a range.
|
|
*
|
|
* Returns: TRUE if the property was found, FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
gst_props_has_fixed_property (GstProps *props, const gchar *name)
|
|
{
|
|
const GstPropsEntry *entry;
|
|
|
|
entry = gst_props_get_entry (props, name);
|
|
if (!entry)
|
|
return FALSE;
|
|
|
|
return !GST_PROPS_ENTRY_IS_VARIABLE (entry);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_props_type:
|
|
* @entry: the props entry to query
|
|
*
|
|
* Get the type of the given props entry.
|
|
*
|
|
* Returns: The type of the props entry.
|
|
*/
|
|
GstPropsType
|
|
gst_props_entry_get_props_type (const GstPropsEntry *entry)
|
|
{
|
|
g_return_val_if_fail (entry != NULL, GST_PROPS_INVALID_TYPE);
|
|
|
|
return entry->propstype;
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_name:
|
|
* @entry: the props entry to query
|
|
*
|
|
* Get the name of the given props entry.
|
|
*
|
|
* Returns: The name of the props entry.
|
|
*/
|
|
const gchar*
|
|
gst_props_entry_get_name (const GstPropsEntry *entry)
|
|
{
|
|
g_return_val_if_fail (entry != NULL, NULL);
|
|
|
|
return g_quark_to_string (entry->propid);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_is_fixed:
|
|
* @entry: the props entry to query
|
|
*
|
|
* Checks if the props entry is fixe, ie. is not a list
|
|
* or a range.
|
|
*
|
|
* Returns: TRUE is the props entry is fixed.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_is_fixed (const GstPropsEntry *entry)
|
|
{
|
|
g_return_val_if_fail (entry != NULL, FALSE);
|
|
|
|
return !GST_PROPS_ENTRY_IS_VARIABLE (entry);
|
|
}
|
|
|
|
static gboolean
|
|
gst_props_entry_getv (const GstPropsEntry *entry, gboolean safe, va_list var_args)
|
|
{
|
|
gboolean result;
|
|
|
|
GST_PROPS_ENTRY_READ (entry, var_args, safe, &result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get:
|
|
* @entry: the props entry to query
|
|
* @...: a pointer to a type that can hold the value.
|
|
*
|
|
* Gets the contents of the entry.
|
|
*
|
|
* Returns: TRUE is the props entry could be fetched.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get (const GstPropsEntry *entry, ...)
|
|
{
|
|
gboolean result;
|
|
va_list var_args;
|
|
|
|
g_return_val_if_fail (entry != NULL, FALSE);
|
|
|
|
va_start (var_args, entry);
|
|
result = gst_props_entry_getv (entry, FALSE, var_args);
|
|
va_end (var_args);
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
gst_props_entry_get_safe (const GstPropsEntry *entry, ...)
|
|
{
|
|
gboolean result;
|
|
va_list var_args;
|
|
|
|
if(entry == NULL) return FALSE;
|
|
|
|
va_start (var_args, entry);
|
|
result = gst_props_entry_getv (entry, TRUE, var_args);
|
|
va_end (var_args);
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
gst_props_getv (GstProps *props, gboolean safe, gchar *first_name, va_list var_args)
|
|
{
|
|
while (first_name) {
|
|
const GstPropsEntry *entry = gst_props_get_entry (props, first_name);
|
|
gboolean result;
|
|
|
|
if (!entry) return FALSE;
|
|
GST_PROPS_ENTRY_READ (entry, var_args, safe, &result);
|
|
if (!result) return FALSE;
|
|
|
|
first_name = va_arg (var_args, gchar *);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_props_get:
|
|
* @props: the props to query
|
|
* @first_name: the first key
|
|
* @...: a pointer to a datastructure that can hold the value.
|
|
*
|
|
* Gets the contents of the props into given key/value pairs.
|
|
* Make sure you pass a NULL terminated list.
|
|
*
|
|
* Returns: TRUE if all of the props entries could be fetched.
|
|
*/
|
|
gboolean
|
|
gst_props_get (GstProps *props, gchar *first_name, ...)
|
|
{
|
|
va_list var_args;
|
|
gboolean ret;
|
|
|
|
va_start (var_args, first_name);
|
|
ret = gst_props_getv (props, FALSE, first_name, var_args);
|
|
va_end (var_args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_props_get_safe:
|
|
* @props: the props to query
|
|
* @first_name: the first key
|
|
* @...: a pointer to a datastructure that can hold the value.
|
|
*
|
|
* Gets the contents of the props into given key/value pairs.
|
|
*
|
|
* Returns: TRUE if all of the props entries could be fetched.
|
|
*/
|
|
gboolean
|
|
gst_props_get_safe (GstProps *props, gchar *first_name, ...)
|
|
{
|
|
va_list var_args;
|
|
gboolean ret;
|
|
|
|
va_start (var_args, first_name);
|
|
ret = gst_props_getv (props, TRUE, first_name, var_args);
|
|
va_end (var_args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_int:
|
|
* @entry: the props entry to query
|
|
* @val: a pointer to a gint to hold the value.
|
|
*
|
|
* Get the contents of the entry into the given gint.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_int (const GstPropsEntry *entry, gint *val)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_INT_TYPE, val);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_float:
|
|
* @entry: the props entry to query
|
|
* @val: a pointer to a gfloat to hold the value.
|
|
*
|
|
* Get the contents of the entry into the given gfloat.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_float (const GstPropsEntry *entry, gfloat *val)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_TYPE, val);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_fourcc_int:
|
|
* @entry: the props entry to query
|
|
* @val: a pointer to a guint32 to hold the value.
|
|
*
|
|
* Get the contents of the entry into the given guint32.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_fourcc_int (const GstPropsEntry *entry, guint32 *val)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_FOURCC_TYPE, val);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_boolean:
|
|
* @entry: the props entry to query
|
|
* @val: a pointer to a gboolean to hold the value.
|
|
*
|
|
* Get the contents of the entry into the given gboolean.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_boolean (const GstPropsEntry *entry, gboolean *val)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_BOOLEAN_TYPE, val);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_string:
|
|
* @entry: the props entry to query
|
|
* @val: a pointer to a gchar* to hold the value.
|
|
*
|
|
* Get the contents of the entry into the given gchar*.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_string (const GstPropsEntry *entry, const gchar **val)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_STRING_TYPE, val);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_int_range:
|
|
* @entry: the props entry to query
|
|
* @min: a pointer to a gint to hold the minimun value.
|
|
* @max: a pointer to a gint to hold the maximum value.
|
|
*
|
|
* Get the contents of the entry into the given gints.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_int_range (const GstPropsEntry *entry, gint *min, gint *max)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_INT_RANGE_TYPE, min, max);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_float_range:
|
|
* @entry: the props entry to query
|
|
* @min: a pointer to a gfloat to hold the minimun value.
|
|
* @max: a pointer to a gfloat to hold the maximum value.
|
|
*
|
|
* Get the contents of the entry into the given gfloats.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_float_range (const GstPropsEntry *entry, gfloat *min, gfloat *max)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_FLOAT_RANGE_TYPE, min, max);
|
|
}
|
|
|
|
/**
|
|
* gst_props_entry_get_list:
|
|
* @entry: the props entry to query
|
|
* @val: a pointer to a GList to hold the value.
|
|
*
|
|
* Get the contents of the entry into the given GList.
|
|
*
|
|
* Returns: TRUE is the value could be fetched. FALSE if the
|
|
* entry is not of given type or did not exist.
|
|
*/
|
|
gboolean
|
|
gst_props_entry_get_list (const GstPropsEntry *entry, const GList **val)
|
|
{
|
|
return gst_props_entry_get_safe (entry, GST_PROPS_LIST_TYPE, val);
|
|
}
|
|
|
|
/**
|
|
* gst_props_merge:
|
|
* @props: the property to merge into
|
|
* @tomerge: the property to merge
|
|
*
|
|
* Merge the properties of tomerge into props.
|
|
*
|
|
* Returns: the new merged property
|
|
*/
|
|
GstProps*
|
|
gst_props_merge (GstProps *props, GstProps *tomerge)
|
|
{
|
|
GList *merge_props;
|
|
|
|
g_return_val_if_fail (props != NULL, NULL);
|
|
g_return_val_if_fail (tomerge != NULL, NULL);
|
|
|
|
merge_props = tomerge->properties;
|
|
|
|
/* FIXME do proper merging here... */
|
|
while (merge_props) {
|
|
GstPropsEntry *entry = (GstPropsEntry *)merge_props->data;
|
|
|
|
gst_props_add_entry (props, entry);
|
|
|
|
merge_props = g_list_next (merge_props);
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
|
|
/* entry2 is always a list, entry1 never is */
|
|
static gboolean
|
|
gst_props_entry_check_list_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
|
|
{
|
|
GList *entrylist = entry2->data.list_data.entries;
|
|
gboolean found = FALSE;
|
|
|
|
while (entrylist && !found) {
|
|
GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
|
|
|
|
found |= gst_props_entry_check_compatibility (entry1, entry);
|
|
|
|
entrylist = g_list_next (entrylist);
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static gboolean
|
|
gst_props_entry_check_compatibility (GstPropsEntry *entry1, GstPropsEntry *entry2)
|
|
{
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES,"compare: %s %s", g_quark_to_string (entry1->propid), g_quark_to_string (entry2->propid));
|
|
|
|
if (entry2->propstype == GST_PROPS_LIST_TYPE && entry1->propstype != GST_PROPS_LIST_TYPE) {
|
|
return gst_props_entry_check_list_compatibility (entry1, entry2);
|
|
}
|
|
|
|
switch (entry1->propstype) {
|
|
case GST_PROPS_LIST_TYPE:
|
|
{
|
|
GList *entrylist = entry1->data.list_data.entries;
|
|
gboolean valid = TRUE; /* innocent until proven guilty */
|
|
|
|
while (entrylist && valid) {
|
|
GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
|
|
|
|
valid &= gst_props_entry_check_compatibility (entry, entry2);
|
|
|
|
entrylist = g_list_next (entrylist);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* a - b <---> a - c */
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
return (entry2->data.int_range_data.min <= entry1->data.int_range_data.min &&
|
|
entry2->data.int_range_data.max >= entry1->data.int_range_data.max);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* a - b <---> a - c */
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
return (entry2->data.float_range_data.min <= entry1->data.float_range_data.min &&
|
|
entry2->data.float_range_data.max >= entry1->data.float_range_data.max);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_FOURCC_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* b <---> a */
|
|
case GST_PROPS_FOURCC_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES,"\"%c%c%c%c\" <--> \"%c%c%c%c\" ?",
|
|
(entry2->data.fourcc_data>>0)&0xff,
|
|
(entry2->data.fourcc_data>>8)&0xff,
|
|
(entry2->data.fourcc_data>>16)&0xff,
|
|
(entry2->data.fourcc_data>>24)&0xff,
|
|
(entry1->data.fourcc_data>>0)&0xff,
|
|
(entry1->data.fourcc_data>>8)&0xff,
|
|
(entry1->data.fourcc_data>>16)&0xff,
|
|
(entry1->data.fourcc_data>>24)&0xff);
|
|
return (entry2->data.fourcc_data == entry1->data.fourcc_data);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_INT_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* b <---> a - d */
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES,"%d <= %d <= %d ?",entry2->data.int_range_data.min,
|
|
entry1->data.int_data,entry2->data.int_range_data.max);
|
|
return (entry2->data.int_range_data.min <= entry1->data.int_data &&
|
|
entry2->data.int_range_data.max >= entry1->data.int_data);
|
|
/* b <---> a */
|
|
case GST_PROPS_INT_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES,"%d == %d ?",entry1->data.int_data,entry2->data.int_data);
|
|
return (entry2->data.int_data == entry1->data.int_data);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* b <---> a - d */
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
return (entry2->data.float_range_data.min <= entry1->data.float_data &&
|
|
entry2->data.float_range_data.max >= entry1->data.float_data);
|
|
/* b <---> a */
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
return (entry2->data.float_data == entry1->data.float_data);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* t <---> t */
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
return (entry2->data.bool_data == entry1->data.bool_data);
|
|
default:
|
|
break;
|
|
}
|
|
case GST_PROPS_STRING_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* t <---> t */
|
|
case GST_PROPS_STRING_TYPE:
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES,"\"%s\" <--> \"%s\" ?",
|
|
entry2->data.string_data.string, entry1->data.string_data.string);
|
|
return (!strcmp (entry2->data.string_data.string, entry1->data.string_data.string));
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* gst_props_check_compatibility:
|
|
* @fromprops: a property
|
|
* @toprops: a property
|
|
*
|
|
* Checks whether two capabilities are compatible.
|
|
*
|
|
* Returns: TRUE if compatible, FALSE otherwise
|
|
*/
|
|
gboolean
|
|
gst_props_check_compatibility (GstProps *fromprops, GstProps *toprops)
|
|
{
|
|
GList *sourcelist;
|
|
GList *sinklist;
|
|
gint missing = 0;
|
|
gint more = 0;
|
|
gboolean compatible = TRUE;
|
|
|
|
g_return_val_if_fail (fromprops != NULL, FALSE);
|
|
g_return_val_if_fail (toprops != NULL, FALSE);
|
|
|
|
sourcelist = fromprops->properties;
|
|
sinklist = toprops->properties;
|
|
|
|
while (sourcelist && sinklist && compatible) {
|
|
GstPropsEntry *entry1;
|
|
GstPropsEntry *entry2;
|
|
|
|
entry1 = (GstPropsEntry *)sourcelist->data;
|
|
entry2 = (GstPropsEntry *)sinklist->data;
|
|
|
|
while (entry1->propid < entry2->propid) {
|
|
more++;
|
|
sourcelist = g_list_next (sourcelist);
|
|
if (sourcelist) entry1 = (GstPropsEntry *)sourcelist->data;
|
|
else goto end;
|
|
}
|
|
while (entry1->propid > entry2->propid) {
|
|
missing++;
|
|
sinklist = g_list_next (sinklist);
|
|
if (sinklist) entry2 = (GstPropsEntry *)sinklist->data;
|
|
else goto end;
|
|
}
|
|
|
|
if (!gst_props_entry_check_compatibility (entry1, entry2)) {
|
|
compatible = FALSE;
|
|
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s are not compatible: ",
|
|
g_quark_to_string (entry1->propid));
|
|
}
|
|
|
|
sourcelist = g_list_next (sourcelist);
|
|
sinklist = g_list_next (sinklist);
|
|
}
|
|
if (sinklist && compatible) {
|
|
GstPropsEntry *entry2;
|
|
entry2 = (GstPropsEntry *)sinklist->data;
|
|
missing++;
|
|
}
|
|
end:
|
|
|
|
if (missing)
|
|
return FALSE;
|
|
|
|
return compatible;
|
|
}
|
|
|
|
static gint
|
|
_props_entry_flexibility (GstPropsEntry *entry)
|
|
{
|
|
gint rank;
|
|
switch (entry->propstype) {
|
|
default:
|
|
rank = 0;
|
|
break;
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
rank = 1;
|
|
break;
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
rank = 2;
|
|
break;
|
|
case GST_PROPS_LIST_TYPE:
|
|
rank = 3;
|
|
break;
|
|
}
|
|
return rank;
|
|
}
|
|
|
|
static GstPropsEntry*
|
|
gst_props_entry_intersect (GstPropsEntry *entry1, GstPropsEntry *entry2)
|
|
{
|
|
GstPropsEntry *result = NULL;
|
|
|
|
/* Swap more flexible types into entry1 */
|
|
if (_props_entry_flexibility (entry1) < _props_entry_flexibility (entry2)) {
|
|
GstPropsEntry *temp;
|
|
temp = entry1;
|
|
entry1 = entry2;
|
|
entry2 = temp;
|
|
}
|
|
|
|
switch (entry1->propstype) {
|
|
case GST_PROPS_LIST_TYPE:
|
|
{
|
|
GList *entrylist = entry1->data.list_data.entries;
|
|
GList *intersection = NULL;
|
|
|
|
while (entrylist) {
|
|
GstPropsEntry *entry = (GstPropsEntry *) entrylist->data;
|
|
GstPropsEntry *intersectentry;
|
|
|
|
intersectentry = gst_props_entry_intersect (entry2, entry);
|
|
|
|
if (intersectentry) {
|
|
if (intersectentry->propstype == GST_PROPS_LIST_TYPE) {
|
|
intersection = g_list_concat (intersection,
|
|
intersectentry->data.list_data.entries);
|
|
/* set the list to NULL because the entries are concatenated to the above
|
|
* list and we don't want to free them */
|
|
intersectentry->data.list_data.entries = NULL;
|
|
gst_props_entry_destroy (intersectentry);
|
|
}
|
|
else {
|
|
intersection = g_list_prepend (intersection, intersectentry);
|
|
}
|
|
}
|
|
entrylist = g_list_next (entrylist);
|
|
}
|
|
if (intersection) {
|
|
/* check if the list only contains 1 element, if so, we can just copy it */
|
|
if (g_list_next (intersection) == NULL) {
|
|
result = (GstPropsEntry *) (intersection->data);
|
|
g_list_free (intersection);
|
|
}
|
|
/* else we need to create a new entry to hold the list */
|
|
else {
|
|
result = gst_props_alloc_entry ();
|
|
result->propid = entry1->propid;
|
|
result->propstype = GST_PROPS_LIST_TYPE;
|
|
result->data.list_data.entries = g_list_reverse (intersection);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* a - b <---> a - c */
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
{
|
|
gint lower = MAX (entry1->data.int_range_data.min, entry2->data.int_range_data.min);
|
|
gint upper = MIN (entry1->data.int_range_data.max, entry2->data.int_range_data.max);
|
|
|
|
if (lower <= upper) {
|
|
result = gst_props_alloc_entry ();
|
|
result->propid = entry1->propid;
|
|
|
|
if (lower == upper) {
|
|
result->propstype = GST_PROPS_INT_TYPE;
|
|
result->data.int_data = lower;
|
|
}
|
|
else {
|
|
result->propstype = GST_PROPS_INT_RANGE_TYPE;
|
|
result->data.int_range_data.min = lower;
|
|
result->data.int_range_data.max = upper;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case GST_PROPS_LIST_TYPE:
|
|
{
|
|
GList *entries = entry2->data.list_data.entries;
|
|
result = gst_props_alloc_entry ();
|
|
result->propid = entry1->propid;
|
|
result->propstype = GST_PROPS_LIST_TYPE;
|
|
result->data.list_data.entries = NULL;
|
|
while (entries) {
|
|
GstPropsEntry *this = (GstPropsEntry *)entries->data;
|
|
if (this->propstype != GST_PROPS_INT_TYPE) {
|
|
/* no hope, this list doesn't even contain ints! */
|
|
gst_props_entry_destroy (result);
|
|
result = NULL;
|
|
break;
|
|
}
|
|
if (this->data.int_data >= entry1->data.int_range_data.min &&
|
|
this->data.int_data <= entry1->data.int_range_data.max)
|
|
{
|
|
/* prepend and reverse at the end */
|
|
result->data.list_data.entries = g_list_prepend (result->data.list_data.entries,
|
|
gst_props_entry_copy (this));
|
|
}
|
|
entries = g_list_next (entries);
|
|
}
|
|
if (result) {
|
|
result->data.list_data.entries = g_list_reverse (result->data.list_data.entries);
|
|
}
|
|
break;
|
|
}
|
|
case GST_PROPS_INT_TYPE:
|
|
{
|
|
if (entry1->data.int_range_data.min <= entry2->data.int_data &&
|
|
entry1->data.int_range_data.max >= entry2->data.int_data)
|
|
{
|
|
result = gst_props_entry_copy (entry2);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* a - b <---> a - c */
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
{
|
|
gfloat lower = MAX (entry1->data.float_range_data.min, entry2->data.float_range_data.min);
|
|
gfloat upper = MIN (entry1->data.float_range_data.max, entry2->data.float_range_data.max);
|
|
|
|
if (lower <= upper) {
|
|
result = gst_props_alloc_entry ();
|
|
result->propid = entry1->propid;
|
|
|
|
if (lower == upper) {
|
|
result->propstype = GST_PROPS_FLOAT_TYPE;
|
|
result->data.float_data = lower;
|
|
}
|
|
else {
|
|
result->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
|
|
result->data.float_range_data.min = lower;
|
|
result->data.float_range_data.max = upper;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
if (entry1->data.float_range_data.min <= entry2->data.float_data &&
|
|
entry1->data.float_range_data.max >= entry2->data.float_data)
|
|
{
|
|
result = gst_props_entry_copy (entry2);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_FOURCC_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* b <---> a */
|
|
case GST_PROPS_FOURCC_TYPE:
|
|
if (entry1->data.fourcc_data == entry2->data.fourcc_data)
|
|
result = gst_props_entry_copy (entry1);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_INT_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* b <---> a */
|
|
case GST_PROPS_INT_TYPE:
|
|
if (entry1->data.int_data == entry2->data.int_data)
|
|
result = gst_props_entry_copy (entry1);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* b <---> a */
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
if (entry1->data.float_data == entry2->data.float_data)
|
|
result = gst_props_entry_copy (entry1);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* t <---> t */
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
if (entry1->data.bool_data == entry2->data.bool_data)
|
|
result = gst_props_entry_copy (entry1);
|
|
default:
|
|
break;
|
|
}
|
|
case GST_PROPS_STRING_TYPE:
|
|
switch (entry2->propstype) {
|
|
/* t <---> t */
|
|
case GST_PROPS_STRING_TYPE:
|
|
if (!strcmp (entry1->data.string_data.string, entry2->data.string_data.string))
|
|
result = gst_props_entry_copy (entry1);
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* make caps debugging extremely verbose
|
|
|
|
GST_CAT_DEBUG (GST_CAT_CAPS, "intersecting %s: %s x %s => %s",
|
|
g_quark_to_string (entry1->propid),
|
|
gst_props_entry_to_string (entry1),
|
|
gst_props_entry_to_string (entry2),
|
|
result? gst_props_entry_to_string (result) : "fail");
|
|
*/
|
|
|
|
return result;
|
|
}
|
|
|
|
/* when running over the entries in sorted order we can
|
|
* optimize addition with _prepend and a reverse at the end */
|
|
#define gst_props_entry_add_sorted_prepend(props, entry) \
|
|
G_STMT_START { \
|
|
/* avoid double evaluation of input */ \
|
|
GstPropsEntry *toadd = (entry); \
|
|
if (GST_PROPS_ENTRY_IS_VARIABLE (toadd)) \
|
|
GST_PROPS_FLAG_UNSET ((props), GST_PROPS_FIXED); \
|
|
props->properties = g_list_prepend ((props)->properties, toadd); \
|
|
} G_STMT_END
|
|
|
|
/**
|
|
* gst_props_intersect:
|
|
* @props1: a property
|
|
* @props2: another property
|
|
*
|
|
* Calculates the intersection bewteen two GstProps.
|
|
*
|
|
* Returns: a GstProps with the intersection or NULL if the
|
|
* intersection is empty. The new GstProps is floating and must
|
|
* be unreffed afetr use.
|
|
*/
|
|
GstProps*
|
|
gst_props_intersect (GstProps *props1, GstProps *props2)
|
|
{
|
|
GList *props1list;
|
|
GList *props2list;
|
|
GstProps *intersection;
|
|
GList *leftovers;
|
|
GstPropsEntry *iprops = NULL;
|
|
|
|
g_return_val_if_fail (props1 != NULL, NULL);
|
|
g_return_val_if_fail (props2 != NULL, NULL);
|
|
|
|
intersection = gst_props_empty_new ();
|
|
|
|
props1list = props1->properties;
|
|
props2list = props2->properties;
|
|
|
|
while (props1list && props2list) {
|
|
GstPropsEntry *entry1;
|
|
GstPropsEntry *entry2;
|
|
|
|
entry1 = (GstPropsEntry *)props1list->data;
|
|
entry2 = (GstPropsEntry *)props2list->data;
|
|
|
|
while (entry1->propid < entry2->propid) {
|
|
gst_props_entry_add_sorted_prepend (intersection, gst_props_entry_copy (entry1));
|
|
|
|
props1list = g_list_next (props1list);
|
|
if (!props1list)
|
|
goto end;
|
|
|
|
entry1 = (GstPropsEntry *)props1list->data;
|
|
}
|
|
while (entry1->propid > entry2->propid) {
|
|
gst_props_entry_add_sorted_prepend (intersection, gst_props_entry_copy (entry2));
|
|
|
|
props2list = g_list_next (props2list);
|
|
if (!props2list)
|
|
goto end;
|
|
|
|
entry2 = (GstPropsEntry *)props2list->data;
|
|
}
|
|
/* at this point we are talking about the same property */
|
|
iprops = gst_props_entry_intersect (entry1, entry2);
|
|
if (!iprops) {
|
|
/* common properties did not intersect, intersection is empty */
|
|
gst_props_unref (intersection);
|
|
return NULL;
|
|
}
|
|
|
|
gst_props_entry_add_sorted_prepend (intersection, iprops);
|
|
|
|
props1list = g_list_next (props1list);
|
|
props2list = g_list_next (props2list);
|
|
}
|
|
|
|
end:
|
|
/* at this point one of the lists could contain leftover properties, while
|
|
* the other one is NULL */
|
|
leftovers = props1list;
|
|
if (!leftovers)
|
|
leftovers = props2list;
|
|
|
|
while (leftovers) {
|
|
gst_props_entry_add_sorted_prepend (intersection,
|
|
gst_props_entry_copy ((GstPropsEntry *) leftovers->data));
|
|
leftovers = g_list_next (leftovers);
|
|
}
|
|
|
|
intersection->properties = g_list_reverse (intersection->properties);
|
|
|
|
return intersection;
|
|
}
|
|
|
|
/**
|
|
* gst_props_normalize:
|
|
* @props: a property
|
|
*
|
|
* Unrolls all lists in the given GstProps. This is usefull if you
|
|
* want to loop over the props.
|
|
*
|
|
* Returns: A GList with the unrolled props entries. g_list_free
|
|
* after usage.
|
|
*/
|
|
GList*
|
|
gst_props_normalize (GstProps *props)
|
|
{
|
|
GList *entries;
|
|
GList *result = NULL;
|
|
gboolean fixed = TRUE;
|
|
|
|
if (!props)
|
|
return NULL;
|
|
|
|
/* warning: the property here could have a wrong FIXED flag
|
|
* but it'll be fixed at the end of the loop */
|
|
entries = props->properties;
|
|
|
|
while (entries) {
|
|
GstPropsEntry *entry = (GstPropsEntry *) entries->data;
|
|
|
|
/* be carefull with the bitmasks */
|
|
fixed &= (GST_PROPS_ENTRY_IS_VARIABLE (entry) ? FALSE : TRUE);
|
|
|
|
if (entry->propstype == GST_PROPS_LIST_TYPE) {
|
|
GList *list_entries = entry->data.list_data.entries;
|
|
|
|
while (list_entries) {
|
|
GstPropsEntry *list_entry = (GstPropsEntry *) list_entries->data;
|
|
GstPropsEntry *new_entry;
|
|
GstProps *newprops;
|
|
GList *lentry;
|
|
|
|
newprops = gst_props_copy (props);
|
|
lentry = g_list_find_custom (newprops->properties,
|
|
GINT_TO_POINTER (list_entry->propid),
|
|
props_find_func);
|
|
if (lentry) {
|
|
GList *new_list;
|
|
|
|
new_entry = (GstPropsEntry *) lentry->data;
|
|
memcpy (new_entry, list_entry, sizeof (GstPropsEntry));
|
|
/* it's possible that this property now became fixed, since we
|
|
* removed the list, we'll update the flag when everything is
|
|
* unreolled at the end of this function */
|
|
new_list = gst_props_normalize (newprops);
|
|
result = g_list_concat (new_list, result);
|
|
}
|
|
else {
|
|
/* FIXME append or prepend */
|
|
result = g_list_append (result, newprops);
|
|
}
|
|
|
|
list_entries = g_list_next (list_entries);
|
|
}
|
|
/* we break out of the loop because the other lists are
|
|
* unrolled in the recursive call */
|
|
break;
|
|
}
|
|
entries = g_list_next (entries);
|
|
}
|
|
if (!result) {
|
|
/* at this point, the props did not need any unrolling, we have scanned
|
|
* all entries and the fixed flag will hold the correct value */
|
|
if (fixed)
|
|
GST_PROPS_FLAG_SET (props, GST_PROPS_FIXED);
|
|
else
|
|
GST_PROPS_FLAG_UNSET (props, GST_PROPS_FIXED);
|
|
|
|
/* no result, create list with input props */
|
|
result = g_list_prepend (result, props);
|
|
}
|
|
else {
|
|
result = g_list_reverse (result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#ifndef GST_DISABLE_LOADSAVE_REGISTRY
|
|
static xmlNodePtr
|
|
gst_props_save_thyself_func (GstPropsEntry *entry, xmlNodePtr parent)
|
|
{
|
|
xmlNodePtr subtree;
|
|
gchar *str;
|
|
|
|
switch (entry->propstype) {
|
|
case GST_PROPS_INT_TYPE:
|
|
subtree = xmlNewChild (parent, NULL, "int", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
str = g_strdup_printf ("%d", entry->data.int_data);
|
|
xmlNewProp (subtree, "value", str);
|
|
g_free(str);
|
|
break;
|
|
case GST_PROPS_INT_RANGE_TYPE:
|
|
subtree = xmlNewChild (parent, NULL, "range", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
str = g_strdup_printf ("%d", entry->data.int_range_data.min);
|
|
xmlNewProp (subtree, "min", str);
|
|
g_free(str);
|
|
str = g_strdup_printf ("%d", entry->data.int_range_data.max);
|
|
xmlNewProp (subtree, "max", str);
|
|
g_free(str);
|
|
break;
|
|
case GST_PROPS_FLOAT_TYPE:
|
|
subtree = xmlNewChild (parent, NULL, "float", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
str = g_strdup_printf ("%g", entry->data.float_data);
|
|
xmlNewProp (subtree, "value", str);
|
|
g_free(str);
|
|
break;
|
|
case GST_PROPS_FLOAT_RANGE_TYPE:
|
|
subtree = xmlNewChild (parent, NULL, "floatrange", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
str = g_strdup_printf ("%g", entry->data.float_range_data.min);
|
|
xmlNewProp (subtree, "min", str);
|
|
g_free(str);
|
|
str = g_strdup_printf ("%g", entry->data.float_range_data.max);
|
|
xmlNewProp (subtree, "max", str);
|
|
g_free(str);
|
|
break;
|
|
case GST_PROPS_FOURCC_TYPE:
|
|
str = g_strdup_printf ("%c%c%c%c",
|
|
(entry->data.fourcc_data>>0)&0xff,
|
|
(entry->data.fourcc_data>>8)&0xff,
|
|
(entry->data.fourcc_data>>16)&0xff,
|
|
(entry->data.fourcc_data>>24)&0xff);
|
|
xmlAddChild (parent, xmlNewComment (str));
|
|
g_free(str);
|
|
subtree = xmlNewChild (parent, NULL, "fourcc", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
str = g_strdup_printf ("%08x", entry->data.fourcc_data);
|
|
xmlNewProp (subtree, "hexvalue", str);
|
|
g_free(str);
|
|
break;
|
|
case GST_PROPS_BOOLEAN_TYPE:
|
|
subtree = xmlNewChild (parent, NULL, "boolean", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
xmlNewProp (subtree, "value", (entry->data.bool_data ? "true" : "false"));
|
|
break;
|
|
case GST_PROPS_STRING_TYPE:
|
|
subtree = xmlNewChild (parent, NULL, "string", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
xmlNewProp (subtree, "value", entry->data.string_data.string);
|
|
break;
|
|
default:
|
|
g_warning ("trying to save unknown property type %d", entry->propstype);
|
|
break;
|
|
}
|
|
|
|
return parent;
|
|
}
|
|
|
|
/**
|
|
* gst_props_save_thyself:
|
|
* @props: a property to save
|
|
* @parent: the parent XML tree
|
|
*
|
|
* Saves the property into an XML representation.
|
|
*
|
|
* Returns: the new XML tree
|
|
*/
|
|
xmlNodePtr
|
|
gst_props_save_thyself (GstProps *props, xmlNodePtr parent)
|
|
{
|
|
GList *proplist;
|
|
xmlNodePtr subtree;
|
|
|
|
g_return_val_if_fail (props != NULL, NULL);
|
|
|
|
proplist = props->properties;
|
|
|
|
while (proplist) {
|
|
GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
|
|
|
|
switch (entry->propstype) {
|
|
case GST_PROPS_LIST_TYPE:
|
|
subtree = xmlNewChild (parent, NULL, "list", NULL);
|
|
xmlNewProp (subtree, "name", g_quark_to_string (entry->propid));
|
|
g_list_foreach (entry->data.list_data.entries, (GFunc) gst_props_save_thyself_func, subtree);
|
|
break;
|
|
default:
|
|
gst_props_save_thyself_func (entry, parent);
|
|
}
|
|
|
|
proplist = g_list_next (proplist);
|
|
}
|
|
|
|
return parent;
|
|
}
|
|
|
|
static GstPropsEntry*
|
|
gst_props_load_thyself_func (xmlNodePtr field)
|
|
{
|
|
GstPropsEntry *entry;
|
|
gchar *prop;
|
|
|
|
entry = gst_props_alloc_entry ();
|
|
|
|
if (!strcmp(field->name, "int")) {
|
|
entry->propstype = GST_PROPS_INT_TYPE;
|
|
prop = xmlGetProp(field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
prop = xmlGetProp(field, "value");
|
|
sscanf (prop, "%d", &entry->data.int_data);
|
|
g_free (prop);
|
|
}
|
|
else if (!strcmp(field->name, "range")) {
|
|
entry->propstype = GST_PROPS_INT_RANGE_TYPE;
|
|
prop = xmlGetProp(field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
prop = xmlGetProp (field, "min");
|
|
sscanf (prop, "%d", &entry->data.int_range_data.min);
|
|
g_free (prop);
|
|
prop = xmlGetProp (field, "max");
|
|
sscanf (prop, "%d", &entry->data.int_range_data.max);
|
|
g_free (prop);
|
|
}
|
|
else if (!strcmp(field->name, "float")) {
|
|
entry->propstype = GST_PROPS_FLOAT_TYPE;
|
|
prop = xmlGetProp(field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
prop = xmlGetProp(field, "value");
|
|
sscanf (prop, "%f", &entry->data.float_data);
|
|
g_free (prop);
|
|
}
|
|
else if (!strcmp(field->name, "floatrange")) {
|
|
entry->propstype = GST_PROPS_FLOAT_RANGE_TYPE;
|
|
prop = xmlGetProp(field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
prop = xmlGetProp (field, "min");
|
|
sscanf (prop, "%f", &entry->data.float_range_data.min);
|
|
g_free (prop);
|
|
prop = xmlGetProp (field, "max");
|
|
sscanf (prop, "%f", &entry->data.float_range_data.max);
|
|
g_free (prop);
|
|
}
|
|
else if (!strcmp(field->name, "boolean")) {
|
|
entry->propstype = GST_PROPS_BOOLEAN_TYPE;
|
|
prop = xmlGetProp(field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
prop = xmlGetProp (field, "value");
|
|
if (!strcmp (prop, "false")) entry->data.bool_data = 0;
|
|
else entry->data.bool_data = 1;
|
|
g_free (prop);
|
|
}
|
|
else if (!strcmp(field->name, "fourcc")) {
|
|
entry->propstype = GST_PROPS_FOURCC_TYPE;
|
|
prop = xmlGetProp(field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
prop = xmlGetProp (field, "hexvalue");
|
|
sscanf (prop, "%08x", &entry->data.fourcc_data);
|
|
g_free (prop);
|
|
}
|
|
else if (!strcmp(field->name, "string")) {
|
|
entry->propstype = GST_PROPS_STRING_TYPE;
|
|
prop = xmlGetProp(field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
entry->data.string_data.string = xmlGetProp (field, "value");
|
|
}
|
|
else {
|
|
gst_props_entry_destroy (entry);
|
|
entry = NULL;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* gst_props_load_thyself:
|
|
* @parent: the XML tree to load from
|
|
*
|
|
* Creates a new property out of an XML tree.
|
|
*
|
|
* Returns: the new property
|
|
*/
|
|
GstProps*
|
|
gst_props_load_thyself (xmlNodePtr parent)
|
|
{
|
|
GstProps *props;
|
|
xmlNodePtr field = parent->xmlChildrenNode;
|
|
gchar *prop;
|
|
|
|
props = gst_props_empty_new ();
|
|
|
|
while (field) {
|
|
if (!strcmp (field->name, "list")) {
|
|
GstPropsEntry *entry;
|
|
xmlNodePtr subfield = field->xmlChildrenNode;
|
|
|
|
entry = gst_props_alloc_entry ();
|
|
prop = xmlGetProp (field, "name");
|
|
entry->propid = g_quark_from_string (prop);
|
|
g_free (prop);
|
|
entry->propstype = GST_PROPS_LIST_TYPE;
|
|
entry->data.list_data.entries = NULL;
|
|
|
|
while (subfield) {
|
|
GstPropsEntry *subentry = gst_props_load_thyself_func (subfield);
|
|
|
|
if (subentry)
|
|
entry->data.list_data.entries = g_list_prepend (entry->data.list_data.entries, subentry);
|
|
|
|
subfield = subfield->next;
|
|
}
|
|
entry->data.list_data.entries = g_list_reverse (entry->data.list_data.entries);
|
|
gst_props_add_entry (props, entry);
|
|
}
|
|
else {
|
|
GstPropsEntry *entry;
|
|
|
|
entry = gst_props_load_thyself_func (field);
|
|
|
|
if (entry)
|
|
gst_props_add_entry (props, entry);
|
|
}
|
|
field = field->next;
|
|
}
|
|
|
|
return props;
|
|
}
|
|
#endif /* GST_DISABLE_LOADSAVE_REGISTRY */
|