pulse-server: use channelmap

Convert to an from pulse enum to id in the message layer so that we
can always just deal with native spa types.
Use the channelmap in stream-restore
Parse the channelmap from properties.
This commit is contained in:
Wim Taymans 2020-11-23 15:37:18 +01:00
parent 2b95afeddc
commit f95f278067
5 changed files with 108 additions and 49 deletions

View file

@ -135,7 +135,7 @@ static int do_extension_stream_restore_read(struct client *client, uint32_t comm
if (key_to_name(item->key, name, sizeof(name)) < 0)
continue;
pw_log_info("%s -> %s", item->key, name);
pw_log_debug("%s -> %s: %s", item->key, name, item->value);
spa_json_init(&it[0], item->value, strlen(item->value));
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
@ -151,6 +151,7 @@ static int do_extension_stream_restore_read(struct client *client, uint32_t comm
continue;
}
else if (strncmp(value, "\"volumes\"", len) == 0) {
vol = VOLUME_INIT;
if (spa_json_enter_array(&it[1], &it[2]) <= 0)
continue;
@ -159,6 +160,17 @@ static int do_extension_stream_restore_read(struct client *client, uint32_t comm
break;
}
}
else if (strncmp(value, "\"channels\"", len) == 0) {
if (spa_json_enter_array(&it[1], &it[2]) <= 0)
continue;
for (map.channels = 0; map.channels < CHANNELS_MAX; map.channels++) {
char chname[16];
if (spa_json_get_string(&it[2], chname, sizeof(chname)) <= 0)
break;
map.map[map.channels] = channel_name2id(chname);
}
}
else if (strncmp(value, "\"target-node\"", len) == 0) {
if (spa_json_get_string(&it[1], device_name, sizeof(device_name)) <= 0)
continue;
@ -191,7 +203,7 @@ static int do_extension_stream_restore_write(struct client *client, uint32_t com
return -EPROTO;
while (m->offset < m->length) {
const char *name, *device_name;
const char *name, *device_name = NULL;
struct channel_map map;
struct volume vol;
bool mute = false;
@ -201,6 +213,9 @@ static int do_extension_stream_restore_write(struct client *client, uint32_t com
size_t size;
char key[1024];
spa_zero(map);
spa_zero(vol);
message_get(m,
TAG_STRING, &name,
TAG_CHANNEL_MAP, &map,
@ -214,18 +229,26 @@ static int do_extension_stream_restore_write(struct client *client, uint32_t com
f = open_memstream(&ptr, &size);
fprintf(f, "{");
fprintf(f, " \"mute\": %s ", mute ? "true" : "false");
fprintf(f, ", \"volumes\": [");
for (i = 0; i < vol.channels; i++)
fprintf(f, "%s%f", (i == 0 ? " ":", "), vol.values[i]);
fprintf(f, " ] ");
fprintf(f, " \"mute\": %s", mute ? "true" : "false");
if (vol.channels > 0) {
fprintf(f, ", \"volumes\": [");
for (i = 0; i < vol.channels; i++)
fprintf(f, "%s%f", (i == 0 ? " ":", "), vol.values[i]);
fprintf(f, " ]");
}
if (map.channels > 0) {
fprintf(f, ", \"channels\": [");
for (i = 0; i < map.channels; i++)
fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "), channel_id2name(map.map[i]));
fprintf(f, " ]");
}
if (device_name != NULL && device_name[0])
fprintf(f, ", \"target-node\": \"%s\"", device_name);
fprintf(f, "}");
fprintf(f, " }");
fclose(f);
if (key_from_name(name, key, sizeof(key)) >= 0) {
pw_log_info("%s -> %s", name, key);
pw_log_debug("%s -> %s: %s", name, key, ptr);
pw_manager_set_metadata(client->manager,
client->metadata_routes,
PW_ID_CORE, key, "Spa:String:JSON", "%s", ptr);

View file

@ -72,21 +72,24 @@ static inline uint32_t format_pa2id(enum sample_format format)
return audio_formats[format].format;
}
static inline const char *format_pa2name(enum sample_format format)
static inline const char *format_id2name(uint32_t format)
{
if (format < 0 || (size_t)format >= SPA_N_ELEMENTS(audio_formats))
return "invalid";
return audio_formats[format].name;
int i;
for (i = 0; spa_type_audio_format[i].name; i++) {
if (spa_type_audio_format[i].type == format)
return spa_debug_type_short_name(spa_type_audio_format[i].name);
}
return "UNKNOWN";
}
static inline enum sample_format format_name2pa(const char *name, size_t size)
static inline uint32_t format_paname2id(const char *name, size_t size)
{
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) {
if (strncmp(name, audio_formats[i].name, size) == 0)
return i;
return audio_formats[i].format;
}
return SAMPLE_INVALID;
return SPA_AUDIO_FORMAT_UNKNOWN;
}
static inline enum sample_format format_id2pa(uint32_t id)
@ -100,26 +103,42 @@ static inline enum sample_format format_id2pa(uint32_t id)
}
struct sample_spec {
enum sample_format format;
uint32_t format;
uint32_t rate;
uint8_t channels;
};
#define SAMPLE_SPEC_INIT (struct sample_spec) { \
.format = SAMPLE_FLOAT32LE, \
.format = SPA_AUDIO_FORMAT_F32, \
.rate = 44100, \
.channels = 2, \
}
static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss)
{
if (ss->format < 0 || (size_t)ss->format >= SPA_N_ELEMENTS(audio_formats))
switch (ss->format) {
case SPA_AUDIO_FORMAT_U8:
return ss->channels;
case SPA_AUDIO_FORMAT_S16_LE:
case SPA_AUDIO_FORMAT_S16_BE:
return 2 * ss->channels;
case SPA_AUDIO_FORMAT_S24_LE:
case SPA_AUDIO_FORMAT_S24_BE:
return 3 * ss->channels;
case SPA_AUDIO_FORMAT_F32_LE:
case SPA_AUDIO_FORMAT_F32_BE:
case SPA_AUDIO_FORMAT_S32_LE:
case SPA_AUDIO_FORMAT_S32_BE:
case SPA_AUDIO_FORMAT_S24_32_LE:
case SPA_AUDIO_FORMAT_S24_32_BE:
return 4 * ss->channels;
default:
return 0;
return audio_formats[ss->format].size * ss->channels;
}
}
static inline bool sample_spec_valid(const struct sample_spec *ss)
{
return (ss->format < SAMPLE_MAX &&
return (sample_spec_frame_size(ss) > 0 &&
ss->rate > 0 && ss->rate <= RATE_MAX &&
ss->channels > 0 && ss->channels <= CHANNELS_MAX);
}
@ -256,13 +275,13 @@ static const struct channel audio_channels[] = {
struct channel_map {
uint8_t channels;
enum channel_position map[CHANNELS_MAX];
uint32_t map[CHANNELS_MAX];
};
#define CHANNEL_MAP_INIT (struct channel_map) { \
.channels = 2, \
.map[0] = CHANNEL_POSITION_FRONT_LEFT, \
.map[1] = CHANNEL_POSITION_FRONT_RIGHT, \
.map[0] = SPA_AUDIO_CHANNEL_FL, \
.map[1] = SPA_AUDIO_CHANNEL_FR, \
}
static inline uint32_t channel_pa2id(enum channel_position channel)
@ -272,11 +291,24 @@ static inline uint32_t channel_pa2id(enum channel_position channel)
return audio_channels[channel].channel;
}
static inline const char *channel_pa2name(enum channel_position channel)
static inline const char *channel_id2name(uint32_t channel)
{
if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels))
return "invalid";
return audio_channels[channel].name;
int i;
for (i = 0; spa_type_audio_channel[i].name; i++) {
if (spa_type_audio_channel[i].type == channel)
return spa_debug_type_short_name(spa_type_audio_channel[i].name);
}
return "UNK";
}
static inline uint32_t channel_name2id(const char *name)
{
int i;
for (i = 0; spa_type_audio_channel[i].name; i++) {
if (strcmp(name, spa_type_audio_channel[i].name) == 0)
return spa_type_audio_channel[i].type;
}
return SPA_AUDIO_CHANNEL_UNKNOWN;
}
static inline enum channel_position channel_id2pa(uint32_t id, uint32_t *aux)
@ -290,14 +322,14 @@ static inline enum channel_position channel_id2pa(uint32_t id, uint32_t *aux)
}
static inline enum channel_position channel_name2pa(const char *name, size_t size)
static inline uint32_t channel_paname2id(const char *name, size_t size)
{
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) {
if (strncmp(name, audio_channels[i].name, size) == 0)
return i;
return audio_channels[i].channel;
}
return CHANNEL_POSITION_INVALID;
return SPA_AUDIO_CHANNEL_UNKNOWN;
}
@ -305,7 +337,7 @@ static inline void channel_map_to_positions(const struct channel_map *map, uint3
{
int i;
for (i = 0; i < map->channels; i++)
pos[i] = channel_pa2id(map->map[i]);
pos[i] = map->map[i];
}
static inline bool channel_map_valid(const struct channel_map *map)
@ -314,7 +346,7 @@ static inline bool channel_map_valid(const struct channel_map *map)
if (map->channels == 0 || map->channels > CHANNELS_MAX)
return false;
for (i = 0; i < map->channels; i++)
if (map->map[i] < 0 || map->map[i] >= CHANNEL_POSITION_MAX)
if (map->map[i] >= CHANNEL_POSITION_MAX)
return false;
return true;
}
@ -349,7 +381,7 @@ static void format_info_clear(struct format_info *info)
static int format_parse_param(const struct spa_pod *param, struct sample_spec *ss, struct channel_map *map)
{
struct spa_audio_info info = { 0 };
uint32_t i, aux = 0;
uint32_t i;
spa_format_parse(param, &info.media_type, &info.media_subtype);
@ -360,17 +392,14 @@ static int format_parse_param(const struct spa_pod *param, struct sample_spec *s
return -ENOTSUP;
}
if (ss) {
ss->format = format_id2pa(info.info.raw.format);
if (ss->format == SAMPLE_INVALID)
return -ENOTSUP;
ss->format = info.info.raw.format;
ss->rate = info.info.raw.rate;
ss->channels = info.info.raw.channels;
}
if (map) {
map->channels = info.info.raw.channels;
for (i = 0; i < map->channels; i++)
map->map[i] = channel_id2pa(info.info.raw.position[i], &aux);
map->map[i] = info.info.raw.position[i];
}
return 0;
}
@ -381,7 +410,7 @@ static const struct spa_pod *format_build_param(struct spa_pod_builder *b,
struct spa_audio_info_raw info;
info = SPA_AUDIO_INFO_RAW_INIT(
.format = format_pa2id(spec->format),
.format = spec->format,
.channels = spec->channels,
.rate = spec->rate);
if (map)
@ -406,8 +435,8 @@ static const struct spa_pod *format_info_build_param(struct spa_pod_builder *b,
if (str[0] != '\"')
return NULL;
size = strcspn(++str, "\"");
ss.format = format_name2pa(str, size);
if (ss.format == SAMPLE_INVALID)
ss.format = format_paname2id(str, size);
if (ss.format == SPA_AUDIO_FORMAT_UNKNOWN)
return NULL;
if ((str = pw_properties_get(info->props, "format.rate")) == NULL)
@ -421,7 +450,7 @@ static const struct spa_pod *format_info_build_param(struct spa_pod_builder *b,
if ((str = pw_properties_get(info->props, "format.channel_map")) != NULL) {
while ((*str == '\"' || *str == ',') &&
(size = strcspn(++str, "\",")) > 0) {
map.map[map.channels++] = channel_name2pa(str, size);
map.map[map.channels++] = channel_paname2id(str, size);
str += size;
}
if (map.channels == ss.channels)

View file

@ -164,7 +164,7 @@ static int read_sample_spec(struct message *m, struct sample_spec *ss)
uint8_t tmp;
if ((res = read_u8(m, &tmp)) < 0)
return res;
ss->format = tmp;
ss->format = format_pa2id(tmp);
if ((res = read_u8(m, &ss->channels)) < 0)
return res;
return read_u32(m, &ss->rate);
@ -264,7 +264,7 @@ static int read_channel_map(struct message *m, struct channel_map *map)
for (i = 0; i < map->channels; i ++) {
if ((res = read_u8(m, &tmp)) < 0)
return res;
map->map[i] = tmp;
map->map[i] = channel_pa2id(tmp);
}
return 0;
}
@ -498,7 +498,7 @@ static void write_64(struct message *m, uint8_t tag, uint64_t val)
static void write_sample_spec(struct message *m, struct sample_spec *ss)
{
write_8(m, TAG_SAMPLE_SPEC);
write_8(m, ss->format);
write_8(m, format_id2pa(ss->format));
write_8(m, ss->channels);
write_32(m, ss->rate);
}
@ -527,10 +527,11 @@ static void write_timeval(struct message *m, struct timeval *tv)
static void write_channel_map(struct message *m, struct channel_map *map)
{
uint8_t i;
uint32_t aux = 0;
write_8(m, TAG_CHANNEL_MAP);
write_8(m, map->channels);
for (i = 0; i < map->channels; i ++)
write_8(m, map->map[i]);
write_8(m, channel_id2pa(map->map[i], &aux));
}
static void write_volume(struct message *m, float vol)
@ -768,7 +769,7 @@ static int message_dump(enum spa_log_level level, struct message *m)
if ((res = read_sample_spec(m, &ss)) < 0)
return res;
pw_log(level, "%u: ss: format:%s rate:%d channels:%u", o,
format_pa2name(ss.format), ss.rate,
format_id2name(ss.format), ss.rate,
ss.channels);
break;
}
@ -802,7 +803,7 @@ static int message_dump(enum spa_log_level level, struct message *m)
return res;
pw_log(level, "%u: channelmap: channels:%u", o, map.channels);
for (i = 0; i < map.channels; i++)
pw_log(level, " %d: %s", i, channel_pa2name(map.map[i]));
pw_log(level, " %d: %s", i, channel_id2name(map.map[i]));
break;
}
case TAG_CVOLUME:

View file

@ -55,6 +55,7 @@
#include <spa/utils/result.h>
#include <spa/debug/dict.h>
#include <spa/debug/mem.h>
#include <spa/debug/types.h>
#include <spa/param/audio/raw.h>
#include <spa/pod/pod.h>
#include <spa/param/audio/format-utils.h>

View file

@ -35,6 +35,7 @@ struct volume {
struct volume_info {
struct volume volume;
struct channel_map map;
bool mute;
float level;
float base;
@ -87,6 +88,10 @@ static int volume_parse_param(const struct spa_pod *param, struct volume_info *i
info->steps = 0x10000u * step;
break;
}
case SPA_PROP_channelMap:
info->map.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id,
info->map.map, SPA_AUDIO_MAX_CHANNELS);
break;
default:
break;
}