alsa: add audio.allowed-rates param

Add a construct and runtime param to limit the amount of allowed
samplerates used by the node.

Fixes #1932
This commit is contained in:
Wim Taymans 2022-01-03 16:03:23 +01:00
parent c3725d5dde
commit 67dc97fa43
4 changed files with 107 additions and 13 deletions

View file

@ -304,6 +304,8 @@ struct spa_audio_info_raw {
#define SPA_KEY_AUDIO_RATE "audio.rate" /**< an audio sample rate as int */
#define SPA_KEY_AUDIO_POSITION "audio.position" /**< channel positions as comma separated list
* of channels ex. "FL,FR" */
#define SPA_KEY_AUDIO_ALLOWED_RATES "audio.allowed-rates" /**< a list of allowed samplerates
* ex. "[ 44100 48000 ]" */
struct spa_audio_info_dsp {
enum spa_audio_format format; /*< format, one of the DSP formats in enum spa_audio_format_dsp */

View file

@ -102,6 +102,10 @@ static int alsa_set_param(struct state *state, const char *k, const char *s)
} else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) {
spa_alsa_parse_position(&state->default_pos, s, strlen(s));
fmt_change++;
} else if (spa_streq(k, SPA_KEY_AUDIO_ALLOWED_RATES)) {
state->n_allowed_rates = spa_alsa_parse_rates(state->allowed_rates,
MAX_RATES, s, strlen(s));
fmt_change++;
} else if (spa_streq(k, "iec958.codecs")) {
spa_alsa_parse_iec958_codecs(&state->iec958_codecs, s, strlen(s));
fmt_change++;
@ -152,6 +156,22 @@ static int position_to_string(struct channel_map *map, char *val, size_t len)
return 0;
}
static int uint32_array_to_string(uint32_t *vals, uint32_t n_vals, char *val, size_t len)
{
uint32_t i, o = 0;
int r;
o += snprintf(val, len, "[ ");
for (i = 0; i < n_vals; i++) {
r = snprintf(val+o, len-o, "%s%d", i == 0 ? "" : ", ", vals[i]);
if (r < 0 || o + r >= len)
return -ENOSPC;
o += r;
}
if (len > o)
o += snprintf(val+o, len-o, " ]");
return 0;
}
struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
uint32_t idx, struct spa_pod_builder *b)
{
@ -197,6 +217,18 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
break;
}
case 4:
{
char buf[1024];
uint32_array_to_string(state->allowed_rates, state->n_allowed_rates, buf, sizeof(buf));
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String(SPA_KEY_AUDIO_ALLOWED_RATES),
SPA_PROP_INFO_description, SPA_POD_String("Audio Allowed Rates"),
SPA_PROP_INFO_type, SPA_POD_String(buf),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
}
case 5:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.period-size"),
@ -204,7 +236,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Int(state->default_period_size),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 5:
case 6:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.period-num"),
@ -212,7 +244,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Int(state->default_period_num),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 6:
case 7:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.headroom"),
@ -220,7 +252,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Int(state->default_headroom),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 7:
case 8:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.start-delay"),
@ -228,7 +260,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Int(state->default_start_delay),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 8:
case 9:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-mmap"),
@ -236,7 +268,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Bool(state->disable_mmap),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 9:
case 10:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.disable-batch"),
@ -244,7 +276,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Bool(state->disable_batch),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 10:
case 11:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.use-chmap"),
@ -252,7 +284,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Bool(state->props.use_chmap),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 11:
case 12:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("api.alsa.multi-rate"),
@ -260,7 +292,7 @@ struct spa_pod *spa_alsa_enum_propinfo(struct state *state,
SPA_PROP_INFO_type, SPA_POD_Bool(state->multi_rate),
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
case 12:
case 13:
param = spa_pod_builder_add_object(b,
SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo,
SPA_PROP_INFO_name, SPA_POD_String("clock.name"),
@ -297,6 +329,11 @@ int spa_alsa_add_prop_params(struct state *state, struct spa_pod_builder *b)
spa_pod_builder_string(b, SPA_KEY_AUDIO_POSITION);
spa_pod_builder_string(b, buf);
uint32_array_to_string(state->allowed_rates, state->n_allowed_rates,
buf, sizeof(buf));
spa_pod_builder_string(b, SPA_KEY_AUDIO_ALLOWED_RATES);
spa_pod_builder_string(b, buf);
spa_pod_builder_string(b, "api.alsa.period-size");
spa_pod_builder_int(b, state->default_period_size);
@ -674,6 +711,15 @@ static void sanitize_map(snd_pcm_chmap_t* map)
}
}
static bool uint32_array_contains(uint32_t *vals, uint32_t n_vals, uint32_t val)
{
uint32_t i;
for (i = 0; i < n_vals; i++)
if (vals[i] == val)
return true;
return false;
}
static int add_rate(struct state *state, uint32_t scale, bool all, uint32_t index, uint32_t *next,
snd_pcm_hw_params_t *params, struct spa_pod_builder *b)
{
@ -705,11 +751,35 @@ static int add_rate(struct state *state, uint32_t scale, bool all, uint32_t inde
if (rate == 0)
rate = state->position ? state->position->clock.rate.denom : DEFAULT_RATE;
spa_pod_builder_int(b, SPA_CLAMP(rate, min, max) * scale);
if (min != max) {
spa_pod_builder_int(b, min * scale);
spa_pod_builder_int(b, max * scale);
choice->body.type = SPA_CHOICE_Range;
if (state->n_allowed_rates > 0) {
uint32_t i, v, last = 0, count = 0;
v = SPA_CLAMP(rate, min, max);
if (uint32_array_contains(state->allowed_rates, state->n_allowed_rates, v)) {
spa_pod_builder_int(b, v * scale);
count++;
}
for (i = 0; i < state->n_allowed_rates; i++) {
v = SPA_CLAMP(state->allowed_rates[i], min, max);
if (v != last &&
uint32_array_contains(state->allowed_rates, state->n_allowed_rates, v)) {
spa_pod_builder_int(b, v * scale);
if (count == 0)
spa_pod_builder_int(b, v * scale);
count++;
}
last = v;
}
if (count > 1)
choice->body.type = SPA_CHOICE_Enum;
} else {
spa_pod_builder_int(b, SPA_CLAMP(rate, min, max) * scale);
if (min != max) {
spa_pod_builder_int(b, min * scale);
spa_pod_builder_int(b, max * scale);
choice->body.type = SPA_CHOICE_Range;
}
}
spa_pod_builder_pop(b, &f[0]);

View file

@ -55,6 +55,8 @@ extern "C" {
#define MIN_LATENCY 16
#define MAX_LATENCY 8192
#define MAX_RATES 16
#define DEFAULT_PERIOD 1024u
#define DEFAULT_RATE 48000u
#define DEFAULT_CHANNELS 2u
@ -140,6 +142,8 @@ struct state {
uint32_t default_format;
unsigned int default_channels;
unsigned int default_rate;
uint32_t allowed_rates[MAX_RATES];
uint32_t n_allowed_rates;
struct channel_map default_pos;
unsigned int disable_mmap;
unsigned int disable_batch;
@ -285,6 +289,22 @@ static inline void spa_alsa_parse_position(struct channel_map *map, const char *
}
}
static inline uint32_t spa_alsa_parse_rates(uint32_t *rates, uint32_t max, const char *val, size_t len)
{
struct spa_json it[2];
char v[256];
uint32_t count;
spa_json_init(&it[0], val, len);
if (spa_json_enter_array(&it[0], &it[1]) <= 0)
spa_json_init(&it[1], val, len);
count = 0;
while (spa_json_get_string(&it[1], v, sizeof(v)) > 0 && count < max)
rates[count++] = atoi(v);
return count;
}
static inline uint32_t spa_alsa_iec958_codec_from_name(const char *name)
{
int i;

View file

@ -314,6 +314,8 @@ extern "C" {
#define PW_KEY_AUDIO_RATE "audio.rate" /**< an audio samplerate */
#define PW_KEY_AUDIO_CHANNELS "audio.channels" /**< number of audio channels */
#define PW_KEY_AUDIO_FORMAT "audio.format" /**< an audio format. Ex: "S16LE" */
#define PW_KEY_AUDIO_ALLOWED_RATES "audio.allowed-rates" /**< a list of allowed samplerates
* ex. "[ 44100 48000 ]" */
/** video related properties */
#define PW_KEY_VIDEO_RATE "video.framerate" /**< a video framerate */