pulse-server: make sample format and channel_map configurable

Add an entry in the config file for default format and channel map.
Use the defaults in the server_info request
Use the defaults for the default channels and map in the modules.
This commit is contained in:
Wim Taymans 2021-04-09 11:12:13 +02:00
parent 877309bfbe
commit b305f57e4d
5 changed files with 82 additions and 44 deletions

View file

@ -40,6 +40,8 @@ context.modules = [
#pulse.default.frag = 96000/48000 # 2 seconds
#pulse.default.tlength = 96000/48000 # 2 seconds
#pulse.min.quantum = 256/48000 # 5ms
#pulse.default.format = "float32ne"
#pulse.default.channel_map = "front-left,front-right"
}
}
]

View file

@ -79,13 +79,26 @@ static const struct format audio_formats[] = {
[SAMPLE_S24_32LE] = { SAMPLE_S24_32LE, SPA_AUDIO_FORMAT_S24_32_LE, "s24-32le", 4 },
[SAMPLE_S24_32BE] = { SAMPLE_S24_32BE, SPA_AUDIO_FORMAT_S24_32_BE, "s24-32be", 4 },
#if __BYTE_ORDER == __BIG_ENDIAN
{ SAMPLE_S16BE, SPA_AUDIO_FORMAT_S16_BE, "s16ne", 2 },
{ SAMPLE_FLOAT32BE, SPA_AUDIO_FORMAT_F32_BE, "float32ne", 4 },
{ SAMPLE_S32BE, SPA_AUDIO_FORMAT_S32_BE, "s32ne", 4 },
{ SAMPLE_S24BE, SPA_AUDIO_FORMAT_S24_BE, "s24ne", 3 },
{ SAMPLE_S24_32BE, SPA_AUDIO_FORMAT_S24_32_BE, "s24-32ne", 4 },
#elif __BYTE_ORDER == __LITTLE_ENDIAN
{ SAMPLE_S16LE, SPA_AUDIO_FORMAT_S16_LE, "s16ne", 2 },
{ SAMPLE_FLOAT32LE, SPA_AUDIO_FORMAT_F32_LE, "float32ne", 4 },
{ SAMPLE_S32LE, SPA_AUDIO_FORMAT_S32_LE, "s32ne", 4 },
{ SAMPLE_S24LE, SPA_AUDIO_FORMAT_S24_LE, "s24ne", 3 },
{ SAMPLE_S24_32LE, SPA_AUDIO_FORMAT_S24_32_LE, "s24-32ne", 4 },
#endif
/* planar formats, we just report them as inteleaved */
{ SAMPLE_U8, SPA_AUDIO_FORMAT_U8P, "u8", 1 },
{ SAMPLE_S16NE, SPA_AUDIO_FORMAT_S16P, "s16", 2 },
{ SAMPLE_S24_32NE, SPA_AUDIO_FORMAT_S24_32P, "s24-32", 4 },
{ SAMPLE_S32NE, SPA_AUDIO_FORMAT_S32P, "s32", 4 },
{ SAMPLE_S24NE, SPA_AUDIO_FORMAT_S24P, "s24", 3 },
{ SAMPLE_FLOAT32NE, SPA_AUDIO_FORMAT_F32P, "float32", 4 },
{ SAMPLE_U8, SPA_AUDIO_FORMAT_U8P, "u8ne", 1 },
{ SAMPLE_S16NE, SPA_AUDIO_FORMAT_S16P, "s16ne", 2 },
{ SAMPLE_S24_32NE, SPA_AUDIO_FORMAT_S24_32P, "s24-32ne", 4 },
{ SAMPLE_S32NE, SPA_AUDIO_FORMAT_S32P, "s32ne", 4 },
{ SAMPLE_S24NE, SPA_AUDIO_FORMAT_S24P, "s24ne", 3 },
{ SAMPLE_FLOAT32NE, SPA_AUDIO_FORMAT_F32P, "float32ne", 4 },
};
static inline uint32_t format_pa2id(enum sample_format format)
@ -108,8 +121,9 @@ static inline const char *format_id2name(uint32_t format)
static inline uint32_t format_paname2id(const char *name, size_t size)
{
size_t i;
for (i = 0; i < SAMPLE_MAX; i++) {
if (strncmp(name, audio_formats[i].name, size) == 0)
for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) {
if (audio_formats[i].name != NULL &&
strncmp(name, audio_formats[i].name, size) == 0)
return audio_formats[i].id;
}
return SPA_AUDIO_FORMAT_UNKNOWN;
@ -146,11 +160,6 @@ struct sample_spec {
.rate = 0, \
.channels = 0, \
}
#define SAMPLE_SPEC_DEFAULT (struct sample_spec) { \
.format = SPA_AUDIO_FORMAT_F32, \
.rate = 44100, \
.channels = 2, \
}
static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss)
{
@ -325,11 +334,6 @@ struct channel_map {
#define CHANNEL_MAP_INIT (struct channel_map) { \
.channels = 0, \
}
#define CHANNEL_MAP_DEFAULT (struct channel_map) { \
.channels = 2, \
.map[0] = SPA_AUDIO_CHANNEL_FL, \
.map[1] = SPA_AUDIO_CHANNEL_FR, \
}
static inline uint32_t channel_pa2id(enum channel_position channel)
{

View file

@ -264,7 +264,7 @@ static struct module *create_module_loopback(struct impl *impl, const char *argu
info.channels = pw_properties_parse_int(str);
pw_properties_set(props, "channels", NULL);
} else {
info.channels = 2;
info.channels = impl->defs.sample_spec.channels;
}
if ((str = pw_properties_get(props, "rate")) != NULL) {
info.rate = pw_properties_parse_int(str);
@ -282,14 +282,15 @@ static struct module *create_module_loopback(struct impl *impl, const char *argu
channel_map_to_positions(&map, info.position);
pw_properties_set(props, "channel_map", NULL);
} else {
if (info.channels > 2)
ERROR_RETURN("Mismatched channel map");
if (info.channels == 1) {
if (info.channels == impl->defs.channel_map.channels) {
channel_map_to_positions(&impl->defs.channel_map, info.position);
} else if (info.channels == 1) {
info.position[0] = SPA_AUDIO_CHANNEL_MONO;
} else {
} else if (info.channels == 2) {
info.position[0] = SPA_AUDIO_CHANNEL_FL;
info.position[1] = SPA_AUDIO_CHANNEL_FR;
} else {
ERROR_RETURN("Mismatched channel map");
}
/* TODO: pull in all of pa_channel_map_init_auto() */
}

View file

@ -122,6 +122,7 @@ static struct module *create_module_null_sink(struct impl *impl, const char *arg
struct module_null_sink_data *d;
struct pw_properties *props = NULL;
const char *str;
struct channel_map map = CHANNEL_MAP_INIT;
int res;
props = pw_properties_new_dict(&SPA_DICT_INIT_ARRAY(module_null_sink_info));
@ -145,27 +146,30 @@ static struct module *create_module_null_sink(struct impl *impl, const char *arg
if ((str = pw_properties_get(props, "channels")) != NULL) {
pw_properties_set(props, SPA_KEY_AUDIO_CHANNELS, str);
pw_properties_set(props, "channels", NULL);
} else {
pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u",
impl->defs.sample_spec.channels);
}
if ((str = pw_properties_get(props, "rate")) != NULL) {
pw_properties_set(props, SPA_KEY_AUDIO_RATE, str);
pw_properties_set(props, "rate", NULL);
}
if ((str = pw_properties_get(props, "channel_map")) != NULL) {
struct channel_map map = CHANNEL_MAP_INIT;
channel_map_parse(str, &map);
pw_properties_set(props, "channel_map", NULL);
} else {
map = impl->defs.channel_map;
}
if (map.channels > 0) {
uint32_t i;
char *s, *p;
channel_map_parse(str, &map);
p = s = alloca(map.channels * 6);
for (i = 0; i < map.channels; i++)
p += snprintf(p, 6, "%s%s", i == 0 ? "" : ",",
channel_id2name(map.map[i]));
pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s);
pw_properties_set(props, "channel_map", NULL);
} else if (pw_properties_get(props, SPA_KEY_AUDIO_POSITION) == NULL) {
pw_properties_set(props, SPA_KEY_AUDIO_POSITION, "FL,FR");
}
if ((str = pw_properties_get(props, PW_KEY_MEDIA_CLASS)) == NULL)
pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink");

View file

@ -92,9 +92,17 @@ struct stats {
#define DEFAULT_DEFAULT_FRAG "96000/48000"
#define DEFAULT_DEFAULT_TLENGTH "96000/48000"
#define DEFAULT_MIN_QUANTUM "256/48000"
#define DEFAULT_FORMAT "float32le"
#define DEFAULT_CHANNEL_MAP "front-left,front-right"
#define MAX_FORMATS 32
#include "format.c"
#include "volume.c"
#include "message.c"
#include "manager.h"
#include "dbus-name.c"
struct defs {
struct spa_fraction min_req;
struct spa_fraction default_req;
@ -102,14 +110,10 @@ struct defs {
struct spa_fraction default_frag;
struct spa_fraction default_tlength;
struct spa_fraction min_quantum;
struct sample_spec sample_spec;
struct channel_map channel_map;
};
#include "format.c"
#include "volume.c"
#include "message.c"
#include "manager.h"
#include "dbus-name.c"
#define NAME "pulse-server"
static bool debug_messages = false;
@ -3669,19 +3673,15 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t
char name[256];
const char *str;
struct message *reply;
struct sample_spec ss;
struct channel_map map;
uint32_t cookie;
pw_log_info(NAME" %p: [%s] GET_SERVER_INFO tag:%u", impl, client->name, tag);
ss = SAMPLE_SPEC_DEFAULT;
map = CHANNEL_MAP_DEFAULT;
if (info != NULL) {
if (info->props &&
(str = spa_dict_lookup(info->props, "default.clock.rate")) != NULL)
ss.rate = atoi(str);
impl->defs.sample_spec.rate = atoi(str);
cookie = info->cookie;
} else {
cookie = 0;
@ -3695,7 +3695,7 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t
TAG_STRING, "14.0.0",
TAG_STRING, pw_get_user_name(),
TAG_STRING, pw_get_host_name(),
TAG_SAMPLE_SPEC, &ss,
TAG_SAMPLE_SPEC, &impl->defs.sample_spec,
TAG_STRING, get_default(client, true), /* default sink name */
TAG_STRING, get_default(client, false), /* default source name */
TAG_U32, cookie, /* cookie */
@ -3703,7 +3703,7 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t
if (client->version >= 15) {
message_put(reply,
TAG_CHANNEL_MAP, &map,
TAG_CHANNEL_MAP, &impl->defs.channel_map,
TAG_INVALID);
}
return send_message(client, reply);
@ -6357,6 +6357,30 @@ static int parse_frac(struct pw_properties *props, const char *key, const char *
pw_log_info(NAME": defaults: %s = %u/%u", key, res->num, res->denom);
return 0;
}
static int parse_channel_map(struct pw_properties *props, const char *key, const char *def,
struct channel_map *res)
{
const char *str;
if (props == NULL ||
(str = pw_properties_get(props, key)) == NULL)
str = def;
channel_map_parse(str, res);
pw_log_info(NAME": defaults: %s = %s", key, str);
return 0;
}
static int parse_format(struct pw_properties *props, const char *key, const char *def,
struct sample_spec *res)
{
const char *str;
if (props == NULL ||
(str = pw_properties_get(props, key)) == NULL)
str = def;
res->format = format_paname2id(str, strlen(str));
if (res->format == SPA_AUDIO_FORMAT_UNKNOWN)
res->format = SPA_AUDIO_FORMAT_F32;
pw_log_info(NAME": defaults: %s = %s", key, str);
return 0;
}
static void load_defaults(struct defs *def, struct pw_properties *props)
{
@ -6366,6 +6390,9 @@ static void load_defaults(struct defs *def, struct pw_properties *props)
parse_frac(props, "pulse.default.frag", DEFAULT_DEFAULT_FRAG, &def->default_frag);
parse_frac(props, "pulse.default.tlength", DEFAULT_DEFAULT_TLENGTH, &def->default_tlength);
parse_frac(props, "pulse.min.quantum", DEFAULT_MIN_QUANTUM, &def->min_quantum);
parse_format(props, "pulse.default.format", DEFAULT_FORMAT, &def->sample_spec);
parse_channel_map(props, "pulse.default.channel_map", DEFAULT_CHANNEL_MAP, &def->channel_map);
def->sample_spec.channels = def->channel_map.channels;
}
struct pw_protocol_pulse *pw_protocol_pulse_new(struct pw_context *context,