spa: improve info parsing

Make info parsing a bit easier to read by assigning the key and
value to temporary variables.
Improve the parsing of channelmap using json parser to make it
support more cases.
Add a unit test for channelmap parsing options.
This commit is contained in:
Wim Taymans 2021-03-18 18:40:56 +01:00
parent 972cf8d657
commit 8e590df92f
6 changed files with 138 additions and 82 deletions

View file

@ -785,33 +785,27 @@ impl_init(const struct spa_handle_factory *factory,
snd_config_update_free_global(); snd_config_update_free_global();
for (i = 0; info && i < info->n_items; i++) { for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;
const char *s = info->items[i].value; const char *s = info->items[i].value;
if (!strcmp(info->items[i].key, SPA_KEY_API_ALSA_PATH)) { if (!strcmp(k, SPA_KEY_API_ALSA_PATH)) {
snprintf(this->props.device, 63, "%s", info->items[i].value); snprintf(this->props.device, 63, "%s", s);
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_CHANNELS)) { } else if (!strcmp(k, SPA_KEY_AUDIO_CHANNELS)) {
this->default_channels = atoi(info->items[i].value); this->default_channels = atoi(s);
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_RATE)) { } else if (!strcmp(k, SPA_KEY_AUDIO_RATE)) {
this->default_rate = atoi(info->items[i].value); this->default_rate = atoi(s);
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_FORMAT)) { } else if (!strcmp(k, SPA_KEY_AUDIO_FORMAT)) {
this->default_format = spa_alsa_format_from_name(info->items[i].value, 128); this->default_format = spa_alsa_format_from_name(s, strlen(s));
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_POSITION)) { } else if (!strcmp(k, SPA_KEY_AUDIO_POSITION)) {
size_t len; spa_alsa_parse_position(&this->default_pos, s, strlen(s));
while (*s && this->default_pos.channels < SPA_AUDIO_MAX_CHANNELS) { } else if (!strcmp(k, "api.alsa.period-size")) {
if ((len = strcspn(s, ",")) == 0) this->default_period_size = atoi(s);
break; } else if (!strcmp(k, "api.alsa.headroom")) {
this->default_pos.pos[this->default_pos.channels++] = this->default_headroom = atoi(s);
spa_alsa_channel_from_name(s, len); } else if (!strcmp(k, "api.alsa.disable-mmap")) {
s += len + strspn(s+len, ",");
}
} else if (!strcmp(info->items[i].key, "api.alsa.period-size")) {
this->default_period_size = atoi(info->items[i].value);
} else if (!strcmp(info->items[i].key, "api.alsa.headroom")) {
this->default_headroom = atoi(info->items[i].value);
} else if (!strcmp(info->items[i].key, "api.alsa.disable-mmap")) {
this->disable_mmap = (strcmp(s, "true") == 0 || atoi(s) == 1); this->disable_mmap = (strcmp(s, "true") == 0 || atoi(s) == 1);
} else if (!strcmp(info->items[i].key, "api.alsa.disable-batch")) { } else if (!strcmp(k, "api.alsa.disable-batch")) {
this->disable_batch = (strcmp(s, "true") == 0 || atoi(s) == 1); this->disable_batch = (strcmp(s, "true") == 0 || atoi(s) == 1);
} else if (!strcmp(info->items[i].key, "api.alsa.use-chmap")) { } else if (!strcmp(k, "api.alsa.use-chmap")) {
this->props.use_chmap = (strcmp(s, "true") == 0 || atoi(s) == 1); this->props.use_chmap = (strcmp(s, "true") == 0 || atoi(s) == 1);
} }
} }

View file

@ -805,33 +805,27 @@ impl_init(const struct spa_handle_factory *factory,
snd_config_update_free_global(); snd_config_update_free_global();
for (i = 0; info && i < info->n_items; i++) { for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;
const char *s = info->items[i].value; const char *s = info->items[i].value;
if (!strcmp(info->items[i].key, SPA_KEY_API_ALSA_PATH)) { if (!strcmp(k, SPA_KEY_API_ALSA_PATH)) {
snprintf(this->props.device, 63, "%s", info->items[i].value); snprintf(this->props.device, 63, "%s", s);
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_CHANNELS)) { } else if (!strcmp(k, SPA_KEY_AUDIO_CHANNELS)) {
this->default_channels = atoi(info->items[i].value); this->default_channels = atoi(s);
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_RATE)) { } else if (!strcmp(k, SPA_KEY_AUDIO_RATE)) {
this->default_rate = atoi(info->items[i].value); this->default_rate = atoi(s);
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_FORMAT)) { } else if (!strcmp(k, SPA_KEY_AUDIO_FORMAT)) {
this->default_format = spa_alsa_format_from_name(info->items[i].value, 128); this->default_format = spa_alsa_format_from_name(s, strlen(s));
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_POSITION)) { } else if (!strcmp(k, SPA_KEY_AUDIO_POSITION)) {
size_t len; spa_alsa_parse_position(&this->default_pos, s, strlen(s));
while (*s && this->default_pos.channels < SPA_AUDIO_MAX_CHANNELS) { } else if (!strcmp(k, "api.alsa.period-size")) {
if ((len = strcspn(s, ",")) == 0) this->default_period_size = atoi(s);
break; } else if (!strcmp(k, "api.alsa.headroom")) {
this->default_pos.pos[this->default_pos.channels++] = this->default_headroom = atoi(s);
spa_alsa_channel_from_name(s, len); } else if (!strcmp(k, "api.alsa.disable-mmap")) {
s += len + strspn(s+len, ",");
}
} else if (!strcmp(info->items[i].key, "api.alsa.period-size")) {
this->default_period_size = atoi(info->items[i].value);
} else if (!strcmp(info->items[i].key, "api.alsa.headroom")) {
this->default_headroom = atoi(info->items[i].value);
} else if (!strcmp(info->items[i].key, "api.alsa.disable-mmap")) {
this->disable_mmap = (strcmp(s, "true") == 0 || atoi(s) == 1); this->disable_mmap = (strcmp(s, "true") == 0 || atoi(s) == 1);
} else if (!strcmp(info->items[i].key, "api.alsa.disable-batch")) { } else if (!strcmp(k, "api.alsa.disable-batch")) {
this->disable_batch = (strcmp(s, "true") == 0 || atoi(s) == 1); this->disable_batch = (strcmp(s, "true") == 0 || atoi(s) == 1);
} else if (!strcmp(info->items[i].key, "api.alsa.use-chmap")) { } else if (!strcmp(k, "api.alsa.use-chmap")) {
this->props.use_chmap = (strcmp(s, "true") == 0 || atoi(s) == 1); this->props.use_chmap = (strcmp(s, "true") == 0 || atoi(s) == 1);
} }
} }

View file

@ -38,6 +38,7 @@ extern "C" {
#include <spa/support/loop.h> #include <spa/support/loop.h>
#include <spa/support/log.h> #include <spa/support/log.h>
#include <spa/utils/list.h> #include <spa/utils/list.h>
#include <spa/utils/json.h>
#include <spa/node/node.h> #include <spa/node/node.h>
#include <spa/node/utils.h> #include <spa/node/utils.h>
@ -216,6 +217,23 @@ static inline uint32_t spa_alsa_channel_from_name(const char *name, size_t len)
return SPA_AUDIO_CHANNEL_UNKNOWN; return SPA_AUDIO_CHANNEL_UNKNOWN;
} }
static inline void spa_alsa_parse_position(struct channel_map *map, const char *val, size_t len)
{
struct spa_json it[2];
char v[256];
int l;
spa_json_init(&it[0], val, len);
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], val, len);
map->channels = 0;
while ((l = spa_json_get_string(&it[1], v, sizeof(v))) > 0 &&
map->channels < SPA_AUDIO_MAX_CHANNELS) {
map->pos[map->channels++] = spa_alsa_channel_from_name(v, l);
}
}
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
#endif #endif

View file

@ -31,6 +31,7 @@
#include <spa/support/cpu.h> #include <spa/support/cpu.h>
#include <spa/utils/list.h> #include <spa/utils/list.h>
#include <spa/utils/names.h> #include <spa/utils/names.h>
#include <spa/utils/json.h>
#include <spa/node/keys.h> #include <spa/node/keys.h>
#include <spa/node/node.h> #include <spa/node/node.h>
#include <spa/node/io.h> #include <spa/node/io.h>
@ -1235,6 +1236,24 @@ static uint32_t channel_from_name(const char *name, size_t len)
return SPA_AUDIO_CHANNEL_UNKNOWN; return SPA_AUDIO_CHANNEL_UNKNOWN;
} }
static inline uint32_t parse_position(uint32_t *pos, const char *val, size_t len)
{
struct spa_json it[2];
char v[256];
int l;
uint32_t i = 0;
spa_json_init(&it[0], val, len);
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], val, len);
while ((l = spa_json_get_string(&it[1], v, sizeof(v))) > 0 &&
i < SPA_AUDIO_MAX_CHANNELS) {
pos[i++] = channel_from_name(v, l);
}
return i;
}
static int static int
impl_init(const struct spa_handle_factory *factory, impl_init(const struct spa_handle_factory *factory,
struct spa_handle *handle, struct spa_handle *handle,
@ -1244,7 +1263,7 @@ impl_init(const struct spa_handle_factory *factory,
{ {
struct impl *this; struct impl *this;
struct port *port; struct port *port;
const char *str; uint32_t i;
spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(factory != NULL, -EINVAL);
spa_return_val_if_fail(handle != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL);
@ -1264,29 +1283,22 @@ impl_init(const struct spa_handle_factory *factory,
props_reset(&this->props); props_reset(&this->props);
if (info != NULL) { for (i = 0; info && i < info->n_items; i++) {
if ((str = spa_dict_lookup(info, "channelmix.normalize")) != NULL && const char *k = info->items[i].key;
(strcmp(str, "true") == 0 || atoi(str) != 0)) const char *s = info->items[i].value;
if (strcmp(k, "channelmix.normalize") == 0 &&
(strcmp(s, "true") == 0 || atoi(s) != 0))
this->mix.options |= CHANNELMIX_OPTION_NORMALIZE; this->mix.options |= CHANNELMIX_OPTION_NORMALIZE;
if ((str = spa_dict_lookup(info, "channelmix.mix-lfe")) != NULL && if (strcmp(k, "channelmix.mix-lfe") == 0 &&
(strcmp(str, "true") == 0 || atoi(str) != 0)) (strcmp(s, "true") == 0 || atoi(s) != 0))
this->mix.options |= CHANNELMIX_OPTION_MIX_LFE; this->mix.options |= CHANNELMIX_OPTION_MIX_LFE;
if ((str = spa_dict_lookup(info, "channelmix.upmix")) != NULL && if (strcmp(k, "channelmix.upmix") == 0 &&
(strcmp(str, "true") == 0 || atoi(str) != 0)) (strcmp(s, "true") == 0 || atoi(s) != 0))
this->mix.options |= CHANNELMIX_OPTION_UPMIX; this->mix.options |= CHANNELMIX_OPTION_UPMIX;
if ((str = spa_dict_lookup(info, "channelmix.lfe-cutoff")) != NULL) if (strcmp(k, "channelmix.lfe-cutoff") == 0)
this->mix.lfe_cutoff = atoi(str); this->mix.lfe_cutoff = atoi(s);
if ((str = spa_dict_lookup(info, SPA_KEY_AUDIO_POSITION)) != NULL) { if (strcmp(k, SPA_KEY_AUDIO_POSITION) == 0)
size_t len; this->props.n_channels = parse_position(this->props.channel_map, s, strlen(s));
const char *p = str;
while (*p && this->props.n_channels < SPA_AUDIO_MAX_CHANNELS) {
if ((len = strcspn(p, ",")) == 0)
break;
this->props.channel_map[this->props.n_channels++] =
channel_from_name(p, len);
p += len + strspn(p+len, ",");
}
}
} }
this->props.n_channel_volumes = this->props.n_channels; this->props.n_channel_volumes = this->props.n_channels;

View file

@ -34,6 +34,7 @@
#include <spa/support/loop.h> #include <spa/support/loop.h>
#include <spa/utils/list.h> #include <spa/utils/list.h>
#include <spa/utils/keys.h> #include <spa/utils/keys.h>
#include <spa/utils/json.h>
#include <spa/node/node.h> #include <spa/node/node.h>
#include <spa/node/utils.h> #include <spa/node/utils.h>
#include <spa/node/io.h> #include <spa/node/io.h>
@ -733,6 +734,23 @@ static uint32_t channel_from_name(const char *name, size_t len)
return SPA_AUDIO_CHANNEL_UNKNOWN; return SPA_AUDIO_CHANNEL_UNKNOWN;
} }
static inline void parse_position(struct impl *this, const char *val, size_t len)
{
struct spa_json it[2];
char v[256];
int l;
spa_json_init(&it[0], val, len);
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], val, len);
this->props.n_pos = 0;
while ((l = spa_json_get_string(&it[1], v, sizeof(v))) > 0 &&
this->props.n_pos < SPA_AUDIO_MAX_CHANNELS) {
this->props.pos[this->props.n_pos++] = channel_from_name(v, l);
}
}
static int static int
impl_init(const struct spa_handle_factory *factory, impl_init(const struct spa_handle_factory *factory,
struct spa_handle *handle, struct spa_handle *handle,
@ -809,19 +827,14 @@ impl_init(const struct spa_handle_factory *factory,
spa_loop_add_source(this->data_loop, &this->timer_source); spa_loop_add_source(this->data_loop, &this->timer_source);
for (i = 0; info && i < info->n_items; i++) { for (i = 0; info && i < info->n_items; i++) {
if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_CHANNELS)) { const char *k = info->items[i].key;
this->props.channels = atoi(info->items[i].value); const char *s = info->items[i].value;
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_RATE)) { if (!strcmp(k, SPA_KEY_AUDIO_CHANNELS)) {
this->props.rate = atoi(info->items[i].value); this->props.channels = atoi(s);
} else if (!strcmp(info->items[i].key, SPA_KEY_AUDIO_POSITION)) { } else if (!strcmp(k, SPA_KEY_AUDIO_RATE)) {
size_t len; this->props.rate = atoi(s);
const char *p = info->items[i].value; } else if (!strcmp(k, SPA_KEY_AUDIO_POSITION)) {
while (*p && this->props.n_pos < SPA_AUDIO_MAX_CHANNELS) { parse_position(this, s, strlen(s));
if ((len = strcspn(p, ",")) == 0)
break;
this->props.pos[this->props.n_pos++] = channel_from_name(p, len);
p += len + strspn(p+len, ",");
}
} }
} }
if (this->props.n_pos > 0) if (this->props.n_pos > 0)

View file

@ -169,10 +169,35 @@ static void test_encode(void)
spa_assert(strcmp(result, "\x04\x05\x1f\x20\x01\x7f\x90") == 0); spa_assert(strcmp(result, "\x04\x05\x1f\x20\x01\x7f\x90") == 0);
} }
static void test_array(char *str, char **vals)
{
struct spa_json it[2];
char val[256];
int i, len;
spa_json_init(&it[0], str, strlen(str));
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], str, strlen(str));
for (i = 0; vals[i]; i++) {
spa_assert((len = spa_json_get_string(&it[1], val, sizeof(val))) > 0);
spa_assert(strcmp(val, vals[i]) == 0);
}
}
static void test_arrays(void)
{
test_array("FL,FR", (char *[]){ "FL", "FR", NULL });
test_array(" FL , FR ", (char *[]){ "FL", "FR", NULL });
test_array("[ FL , FR ]", (char *[]){ "FL", "FR", NULL });
test_array("[FL FR]", (char *[]){ "FL", "FR", NULL });
test_array("FL FR", (char *[]){ "FL", "FR", NULL });
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
test_abi(); test_abi();
test_parse(); test_parse();
test_encode(); test_encode();
test_arrays();
return 0; return 0;
} }