From f95f2780671b618f76c42199e733783036e251d2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 23 Nov 2020 15:37:18 +0100 Subject: [PATCH] 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. --- .../ext-stream-restore.c | 41 ++++++-- src/modules/module-protocol-pulse/format.c | 97 ++++++++++++------- src/modules/module-protocol-pulse/message.c | 13 +-- .../module-protocol-pulse/pulse-server.c | 1 + src/modules/module-protocol-pulse/volume.c | 5 + 5 files changed, 108 insertions(+), 49 deletions(-) diff --git a/src/modules/module-protocol-pulse/ext-stream-restore.c b/src/modules/module-protocol-pulse/ext-stream-restore.c index 20e0dece6..06920e8cb 100644 --- a/src/modules/module-protocol-pulse/ext-stream-restore.c +++ b/src/modules/module-protocol-pulse/ext-stream-restore.c @@ -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); diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 3ff2794d7..a2898886b 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -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) diff --git a/src/modules/module-protocol-pulse/message.c b/src/modules/module-protocol-pulse/message.c index 52226accc..673707032 100644 --- a/src/modules/module-protocol-pulse/message.c +++ b/src/modules/module-protocol-pulse/message.c @@ -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: diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 0085e0276..31d6bc322 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include diff --git a/src/modules/module-protocol-pulse/volume.c b/src/modules/module-protocol-pulse/volume.c index 857796a66..faaadbe6f 100644 --- a/src/modules/module-protocol-pulse/volume.c +++ b/src/modules/module-protocol-pulse/volume.c @@ -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; }