spa-pod: add support for custom Id properties

Allows creating and parsing Pod Objects with custom properties.
This commit is contained in:
Julian Bouzas 2021-09-21 09:13:25 -04:00
parent 7c03da82e7
commit 711d1ee686
4 changed files with 133 additions and 35 deletions

View file

@ -16,6 +16,7 @@
#include <spa/pod/parser.h>
#define WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE 64
#define WP_SPA_POD_ID_PROPERTY_NAME_MAX 16
/*! \defgroup wpspapod WpSpaPod */
/*!
@ -66,6 +67,7 @@ struct _WpSpaPod
WpSpaIdTable table;
guint32 key;
guint32 flags;
gchar id_key_name[WP_SPA_POD_ID_PROPERTY_NAME_MAX];
} data_property; /* Only used for property pods */
struct wp_control_data {
guint32 offset;
@ -1717,8 +1719,14 @@ wp_spa_pod_get_property (WpSpaPod *self, const char **key,
WpSpaIdValue key_val = wp_spa_id_table_find_value (
self->static_pod.data_property.table,
self->static_pod.data_property.key);
g_return_val_if_fail (key_val != NULL, FALSE);
*key = wp_spa_id_value_short_name (key_val);
if (key_val) {
*key = wp_spa_id_value_short_name (key_val);
} else {
g_snprintf (self->static_pod.data_property.id_key_name,
WP_SPA_POD_ID_PROPERTY_NAME_MAX, "id-%08x",
self->static_pod.data_property.key);
*key = self->static_pod.data_property.id_key_name;
}
}
if (value)
*value = wp_spa_pod_new_wrap (self->pod);
@ -2128,10 +2136,16 @@ wp_spa_pod_builder_add_pod (WpSpaPodBuilder *self, WpSpaPod *pod)
void
wp_spa_pod_builder_add_property (WpSpaPodBuilder *self, const char *key)
{
WpSpaIdTable table = wp_spa_type_get_values_table (self->type);
WpSpaIdValue id = wp_spa_id_table_find_value_from_short_name (table, key);
g_return_if_fail (id != NULL);
spa_pod_builder_prop (&self->builder, wp_spa_id_value_number (id), 0);
guint key_id;
if (g_str_has_prefix (key, "id-")) {
g_return_if_fail (sscanf (key, "id-%08x", &key_id) == 1);
} else {
WpSpaIdTable table = wp_spa_type_get_values_table (self->type);
WpSpaIdValue id = wp_spa_id_table_find_value_from_short_name (table, key);
g_return_if_fail (id != NULL);
key_id = wp_spa_id_value_number (id);
}
spa_pod_builder_prop (&self->builder, key_id, 0);
}
/*!
@ -2201,13 +2215,18 @@ wp_spa_pod_builder_add_valist (WpSpaPodBuilder *self, va_list args)
gboolean choice;
if (wp_spa_type_is_object (self->type)) {
guint key_id;
const char *key_name = va_arg(args, const char *);
if (!key_name)
return;
key = wp_spa_id_table_find_value_from_short_name (table, key_name);
g_return_if_fail (key != NULL);
spa_pod_builder_prop (&self->builder, wp_spa_id_value_number (key), 0);
if (g_str_has_prefix (key_name, "id-")) {
g_return_if_fail (sscanf (key_name, "id-%08x", &key_id) == 1);
} else {
key = wp_spa_id_table_find_value_from_short_name (table, key_name);
g_return_if_fail (key != NULL);
key_id = wp_spa_id_value_number (key);
}
spa_pod_builder_prop (&self->builder, key_id, 0);
}
else if (self->type == SPA_TYPE_Sequence) {
guint32 offset = va_arg(args, uint32_t);
@ -2643,16 +2662,23 @@ wp_spa_pod_parser_get_valist (WpSpaPodParser *self, va_list args)
const char *format;
if (wp_spa_type_is_object (self->type)) {
guint key_id;
const struct spa_pod_object *object;
const char *key_name = va_arg(args, const char *);
if (!key_name)
break;
key = wp_spa_id_table_find_value_from_short_name (table, key_name);
g_return_val_if_fail (key != NULL, FALSE);
const struct spa_pod_object *object = (const struct spa_pod_object *)
spa_pod_parser_frame (&self->parser, &self->frame);
prop = spa_pod_object_find_prop (object, prop,
wp_spa_id_value_number (key));
if (g_str_has_prefix (key_name, "id-")) {
g_return_val_if_fail (sscanf (key_name, "id-%08x", &key_id) == 1, FALSE);
} else {
key = wp_spa_id_table_find_value_from_short_name (table, key_name);
g_return_val_if_fail (key != NULL, FALSE);
key_id = wp_spa_id_value_number (key);
}
object = (const struct spa_pod_object *)spa_pod_parser_frame
(&self->parser, &self->frame);
prop = spa_pod_object_find_prop (object, prop, key_id);
pod = prop ? &prop->value : NULL;
}

View file

@ -276,8 +276,13 @@ builder_add_lua_userdata (WpSpaPodBuilder *b, WpSpaIdValue key_id,
{
WpSpaPod *pod = wplua_checkboxed (L, idx, WP_TYPE_SPA_POD);
if (pod) {
WpSpaType prop_type = wp_spa_id_value_get_value_type (key_id, NULL);
if (is_pod_type_compatible (prop_type, pod)) {
if (key_id) {
WpSpaType prop_type = wp_spa_id_value_get_value_type (key_id, NULL);
if (is_pod_type_compatible (prop_type, pod)) {
wp_spa_pod_builder_add_pod (b, pod);
return TRUE;
}
} else {
wp_spa_pod_builder_add_pod (b, pod);
return TRUE;
}
@ -603,28 +608,25 @@ object_add_property (WpSpaPodBuilder *b, WpSpaIdTable table,
{
guint i;
WpSpaIdValue prop_id = NULL;
WpSpaType prop_type = WP_SPA_TYPE_INVALID;
int ltype = lua_type (L, idx);
if (ltype < 0 || ltype >= MAX_LUA_TYPES)
return FALSE;
/* Get the property type name */
prop_id = wp_spa_id_table_find_value_from_short_name (table, key);
if (!prop_id)
return FALSE;
prop_type = wp_spa_id_value_get_value_type (prop_id, NULL);
if (prop_type == WP_SPA_TYPE_INVALID)
return FALSE;
/* Check if we can add primitive property directly from LUA type */
for (i = 0; primitive_lua_types[i].primitive_type; i++) {
const struct primitive_lua_type *t = primitive_lua_types + i;
if (t->primitive_type == prop_type) {
primitive_lua_add_func f = t->primitive_lua_add_funcs[ltype];
if (f) {
wp_spa_pod_builder_add_property (b, key);
return f (b, prop_id, L, idx);
prop_id = wp_spa_id_table_find_value_from_short_name (table, key);
if (prop_id) {
WpSpaType prop_type = wp_spa_id_value_get_value_type (prop_id, NULL);
if (prop_type != WP_SPA_TYPE_INVALID) {
for (i = 0; primitive_lua_types[i].primitive_type; i++) {
const struct primitive_lua_type *t = primitive_lua_types + i;
if (t->primitive_type == prop_type) {
primitive_lua_add_func f = t->primitive_lua_add_funcs[ltype];
if (f) {
wp_spa_pod_builder_add_property (b, key);
return f (b, prop_id, L, idx);
}
}
}
}
}

View file

@ -420,6 +420,7 @@ test_spa_pod_object (void)
"frequency", "i", 440,
"device", "s", "device-name",
"deviceFd", "h", 5,
"id-01000000", "b", TRUE,
NULL);
g_assert_nonnull (pod);
g_assert_true (wp_spa_pod_is_object (pod));
@ -432,6 +433,7 @@ test_spa_pod_object (void)
gint32 frequency;
const char *device;
gint64 device_fd;
gboolean custom = FALSE;
g_assert_true (wp_spa_pod_get_object (pod,
&id_name,
"mute", "b", &mute,
@ -439,6 +441,7 @@ test_spa_pod_object (void)
"frequency", "i", &frequency,
"device", "s", &device,
"deviceFd", "h", &device_fd,
"id-01000000", "b", &custom,
NULL));
g_assert_cmpstr (id_name, ==, "Props");
g_assert_false (mute);
@ -446,6 +449,7 @@ test_spa_pod_object (void)
g_assert_cmpint (frequency, ==, 440);
g_assert_cmpstr (device, ==, "device-name");
g_assert_cmpint (device_fd, ==, 5);
g_assert_true (custom);
}
/* Dynamic */
@ -462,6 +466,8 @@ test_spa_pod_object (void)
wp_spa_pod_builder_add_string (b, "device-name");
wp_spa_pod_builder_add_property (b, "deviceFd");
wp_spa_pod_builder_add_fd (b, 5);
wp_spa_pod_builder_add_property (b, "id-01000000");
wp_spa_pod_builder_add_boolean (b, TRUE);
g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b);
g_assert_nonnull (pod);
g_assert_true (wp_spa_pod_is_object (pod));
@ -474,6 +480,7 @@ test_spa_pod_object (void)
gint32 frequency;
const char *device;
gint64 device_fd;
gboolean custom = FALSE;
g_autoptr (WpSpaPodParser) p = wp_spa_pod_parser_new_object (pod, &id_name);
g_assert_nonnull (pod);
g_assert_true (wp_spa_pod_parser_get (p, "mute", "b", &mute, NULL));
@ -481,6 +488,7 @@ test_spa_pod_object (void)
g_assert_true (wp_spa_pod_parser_get (p, "frequency", "i", &frequency, NULL));
g_assert_true (wp_spa_pod_parser_get (p, "device", "s", &device, NULL));
g_assert_true (wp_spa_pod_parser_get (p, "deviceFd", "h", &device_fd, NULL));
g_assert_true (wp_spa_pod_parser_get (p, "id-01000000", "b", &custom, NULL));
wp_spa_pod_parser_end (p);
g_assert_cmpstr (id_name, ==, "Props");
g_assert_false (mute);
@ -488,6 +496,7 @@ test_spa_pod_object (void)
g_assert_cmpint (frequency, ==, 440);
g_assert_cmpstr (device, ==, "device-name");
g_assert_cmpint (device_fd, ==, 5);
g_assert_true (custom);
}
}
@ -786,6 +795,8 @@ test_spa_pod_iterator (void)
wp_spa_pod_builder_add_boolean (b, FALSE);
wp_spa_pod_builder_add_property (b, "device");
wp_spa_pod_builder_add_string (b, "device-name");
wp_spa_pod_builder_add_property (b, "id-01000000");
wp_spa_pod_builder_add_boolean (b, TRUE);
g_autoptr (WpSpaPod) pod = wp_spa_pod_builder_end (b);
g_assert_nonnull (pod);
@ -824,13 +835,29 @@ test_spa_pod_iterator (void)
g_value_unset (&next);
}
{
GValue next = G_VALUE_INIT;
g_assert_true (wp_iterator_next (it, &next));
WpSpaPod *p = g_value_get_boxed (&next);
g_assert_nonnull (p);
g_assert_true (wp_spa_pod_is_property (p));
const char *key = NULL;
g_autoptr (WpSpaPod) value = NULL;
g_assert_true (wp_spa_pod_get_property (p, &key, &value));
g_assert_cmpstr (key, ==, "id-01000000");
gboolean b = FALSE;
g_assert_true (wp_spa_pod_get_boolean (value, &b));
g_assert_true (b);
g_value_unset (&next);
}
{
g_assert_false (wp_iterator_next (it, NULL));
}
guint32 total_props = 0;
g_assert_true (wp_iterator_foreach (it, object_foreach, &total_props));
g_assert_cmpuint (total_props, ==, 2);
g_assert_cmpuint (total_props, ==, 3);
}
/* Struct */

View file

@ -93,6 +93,27 @@ assert (val.properties.direction == "Input")
assert (val.properties.mode == "dsp")
assert (val.properties.monitor)
assert (pod:get_type_name() == "Spa:Pod:Object:Param:PortConfig")
pod = Pod.Object {
"Spa:Pod:Object:Param:Props", "Props",
device = "hw:Generic",
deviceName = "",
cardName = "",
minLatency = 16,
maxLatency = 8192,
["id-01000000"] = Pod.Boolean (true),
latencyOffsetNsec = 0
}
val = pod:parse()
assert (val.pod_type == "Object")
assert (val.object_id == "Props")
assert (val.properties.device == "hw:Generic")
assert (val.properties.deviceName == "")
assert (val.properties.cardName == "")
assert (val.properties.minLatency == 16)
assert (val.properties.maxLatency == 8192)
assert (val.properties["id-01000000"] == true)
assert (val.properties.latencyOffsetNsec == 0)
assert (pod:get_type_name() == "Spa:Pod:Object:Param:Props")
-- Sequence
pod = Pod.Sequence {
@ -206,3 +227,25 @@ assert (val.properties.format.properties.position.value_type == "Spa:Id")
assert (val.properties.format.properties.position[1] == "FL")
assert (val.properties.format.properties.position[2] == "FR")
assert (pod:get_type_name() == "Spa:Pod:Object:Param:PortConfig")
-- Nested Object Id Properties
pod = Pod.Object {
"Spa:Pod:Object:Param:Props", "Props",
device = "my-device",
["id-01000000"] = Pod.Int (4),
["id-02000000"] = Pod.Object {
"Spa:Pod:Object:Param:Props", "Props",
device = "my-sub-device",
["id-03000000"] = Pod.Boolean (true),
["id-04000000"] = Pod.String ("string")
}
}
val = pod:parse()
assert (val.pod_type == "Object")
assert (val.object_id == "Props")
assert (val.properties.device == "my-device")
assert (val.properties["id-01000000"] == 4)
assert (val.properties["id-02000000"].properties.device == "my-sub-device")
assert (val.properties["id-02000000"].properties["id-03000000"] == true)
assert (val.properties["id-02000000"].properties["id-04000000"] == "string")
assert (pod:get_type_name() == "Spa:Pod:Object:Param:Props")