diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 7ee74a61c..d92fb2151 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -22,6 +22,9 @@ * DEALINGS IN THE SOFTWARE. */ +#define RATE_MAX (48000u*8u) +#define CHANNELS_MAX (64u) + enum sample_format { SAMPLE_U8, SAMPLE_ALAW, @@ -69,6 +72,16 @@ static inline uint32_t format_pa2id(enum sample_format format) return audio_formats[format].format; } +static inline enum sample_format format_name2pa(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 SAMPLE_INVALID; +} + static inline enum sample_format format_id2pa(uint32_t id) { size_t i; @@ -79,7 +92,6 @@ static inline enum sample_format format_id2pa(uint32_t id) return SAMPLE_INVALID; } - struct sample_spec { enum sample_format format; uint32_t rate; @@ -93,13 +105,184 @@ static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss) return audio_formats[ss->format].size * ss->channels; } -#define CHANNELS_MAX 64 +static inline bool sample_spec_valid(const struct sample_spec *ss) +{ + return (ss->format < SAMPLE_MAX && + ss->rate > 0 && ss->rate <= RATE_MAX && + ss->channels > 0 && ss->channels <= CHANNELS_MAX); +} + +enum channel_position { + CHANNEL_POSITION_INVALID = -1, + CHANNEL_POSITION_MONO = 0, + CHANNEL_POSITION_FRONT_LEFT, + CHANNEL_POSITION_FRONT_RIGHT, + CHANNEL_POSITION_FRONT_CENTER, + + CHANNEL_POSITION_REAR_CENTER, + CHANNEL_POSITION_REAR_LEFT, + CHANNEL_POSITION_REAR_RIGHT, + + CHANNEL_POSITION_LFE, + CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + + CHANNEL_POSITION_SIDE_LEFT, + CHANNEL_POSITION_SIDE_RIGHT, + CHANNEL_POSITION_AUX0, + CHANNEL_POSITION_AUX1, + CHANNEL_POSITION_AUX2, + CHANNEL_POSITION_AUX3, + CHANNEL_POSITION_AUX4, + CHANNEL_POSITION_AUX5, + CHANNEL_POSITION_AUX6, + CHANNEL_POSITION_AUX7, + CHANNEL_POSITION_AUX8, + CHANNEL_POSITION_AUX9, + CHANNEL_POSITION_AUX10, + CHANNEL_POSITION_AUX11, + CHANNEL_POSITION_AUX12, + CHANNEL_POSITION_AUX13, + CHANNEL_POSITION_AUX14, + CHANNEL_POSITION_AUX15, + CHANNEL_POSITION_AUX16, + CHANNEL_POSITION_AUX17, + CHANNEL_POSITION_AUX18, + CHANNEL_POSITION_AUX19, + CHANNEL_POSITION_AUX20, + CHANNEL_POSITION_AUX21, + CHANNEL_POSITION_AUX22, + CHANNEL_POSITION_AUX23, + CHANNEL_POSITION_AUX24, + CHANNEL_POSITION_AUX25, + CHANNEL_POSITION_AUX26, + CHANNEL_POSITION_AUX27, + CHANNEL_POSITION_AUX28, + CHANNEL_POSITION_AUX29, + CHANNEL_POSITION_AUX30, + CHANNEL_POSITION_AUX31, + + CHANNEL_POSITION_TOP_CENTER, + + CHANNEL_POSITION_TOP_FRONT_LEFT, + CHANNEL_POSITION_TOP_FRONT_RIGHT, + CHANNEL_POSITION_TOP_FRONT_CENTER, + + CHANNEL_POSITION_TOP_REAR_LEFT, + CHANNEL_POSITION_TOP_REAR_RIGHT, + CHANNEL_POSITION_TOP_REAR_CENTER, + + CHANNEL_POSITION_MAX +}; + +struct channel { + uint32_t channel; + const char *name; +}; + +static const struct channel audio_channels[] = { + [CHANNEL_POSITION_MONO] = { SPA_AUDIO_CHANNEL_MONO, "mono", }, + + [CHANNEL_POSITION_FRONT_LEFT] = { SPA_AUDIO_CHANNEL_FL, "front-left", }, + [CHANNEL_POSITION_FRONT_RIGHT] = { SPA_AUDIO_CHANNEL_FR, "front-right", }, + [CHANNEL_POSITION_FRONT_CENTER] = { SPA_AUDIO_CHANNEL_FC, "front-center", }, + + [CHANNEL_POSITION_REAR_CENTER] = { SPA_AUDIO_CHANNEL_RC, "rear-center", }, + [CHANNEL_POSITION_REAR_LEFT] = { SPA_AUDIO_CHANNEL_RL, "rear-left", }, + [CHANNEL_POSITION_REAR_RIGHT] = { SPA_AUDIO_CHANNEL_RR, "rear-right", }, + + [CHANNEL_POSITION_LFE] = { SPA_AUDIO_CHANNEL_LFE, "lfe", }, + [CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = { SPA_AUDIO_CHANNEL_FLC, "front-left-of-center", }, + [CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = { SPA_AUDIO_CHANNEL_FRC, "front-right-of-center", }, + + [CHANNEL_POSITION_SIDE_LEFT] = { SPA_AUDIO_CHANNEL_SL, "side-left", }, + [CHANNEL_POSITION_SIDE_RIGHT] = { SPA_AUDIO_CHANNEL_SR, "side-right", }, + + [CHANNEL_POSITION_AUX0] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 1, "aux0", }, + [CHANNEL_POSITION_AUX1] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 2, "aux1", }, + [CHANNEL_POSITION_AUX2] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 3, "aux2", }, + [CHANNEL_POSITION_AUX3] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 4, "aux3", }, + [CHANNEL_POSITION_AUX4] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 5, "aux4", }, + [CHANNEL_POSITION_AUX5] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 6, "aux5", }, + [CHANNEL_POSITION_AUX6] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 7, "aux6", }, + [CHANNEL_POSITION_AUX7] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 8, "aux7", }, + [CHANNEL_POSITION_AUX8] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 9, "aux8", }, + [CHANNEL_POSITION_AUX9] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 10, "aux9", }, + [CHANNEL_POSITION_AUX10] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 11, "aux10", }, + [CHANNEL_POSITION_AUX11] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 12, "aux11", }, + [CHANNEL_POSITION_AUX12] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 13, "aux12", }, + [CHANNEL_POSITION_AUX13] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 14, "aux13", }, + [CHANNEL_POSITION_AUX14] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 15, "aux14", }, + [CHANNEL_POSITION_AUX15] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 16, "aux15", }, + [CHANNEL_POSITION_AUX16] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 17, "aux16", }, + [CHANNEL_POSITION_AUX17] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 18, "aux17", }, + [CHANNEL_POSITION_AUX18] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 19, "aux18", }, + [CHANNEL_POSITION_AUX19] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 20, "aux19", }, + [CHANNEL_POSITION_AUX20] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 21, "aux20", }, + [CHANNEL_POSITION_AUX21] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 22, "aux21", }, + [CHANNEL_POSITION_AUX22] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 23, "aux22", }, + [CHANNEL_POSITION_AUX23] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 24, "aux23", }, + [CHANNEL_POSITION_AUX24] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 25, "aux24", }, + [CHANNEL_POSITION_AUX25] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 26, "aux25", }, + [CHANNEL_POSITION_AUX26] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 27, "aux26", }, + [CHANNEL_POSITION_AUX27] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 28, "aux27", }, + [CHANNEL_POSITION_AUX28] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 29, "aux28", }, + [CHANNEL_POSITION_AUX29] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 30, "aux29", }, + [CHANNEL_POSITION_AUX30] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 31, "aux30", }, + [CHANNEL_POSITION_AUX31] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 32, "aux31", }, + + [CHANNEL_POSITION_TOP_CENTER] = { SPA_AUDIO_CHANNEL_TC, "top-center", }, + + [CHANNEL_POSITION_TOP_FRONT_LEFT] = { SPA_AUDIO_CHANNEL_TFL, "top-front-left", }, + [CHANNEL_POSITION_TOP_FRONT_RIGHT] = { SPA_AUDIO_CHANNEL_TFR, "top-front-right", }, + [CHANNEL_POSITION_TOP_FRONT_CENTER] = { SPA_AUDIO_CHANNEL_TFC, "top-front-center", }, + + [CHANNEL_POSITION_TOP_REAR_LEFT] = { SPA_AUDIO_CHANNEL_TRL, "top-rear-left", }, + [CHANNEL_POSITION_TOP_REAR_RIGHT] = { SPA_AUDIO_CHANNEL_TRR, "top-rear-right", }, + [CHANNEL_POSITION_TOP_REAR_CENTER] = { SPA_AUDIO_CHANNEL_TRC, "top-rear-center", }, +}; struct channel_map { uint8_t channels; - uint32_t map[CHANNELS_MAX]; + enum channel_position map[CHANNELS_MAX]; }; +static inline uint32_t channel_pa2id(enum channel_position channel) +{ + if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels)) + return SPA_AUDIO_CHANNEL_UNKNOWN; + return audio_channels[channel].channel; +} + +static inline enum channel_position channel_id2pa(uint32_t id, uint32_t *aux) +{ + size_t i; + for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) { + if (id == audio_channels[i].channel) + return i; + } + return CHANNEL_POSITION_AUX0 + (*aux)++; +} + + +static inline enum channel_position channel_name2pa(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 CHANNEL_POSITION_INVALID; +} + + +static void channel_map_to_positions(const struct channel_map *map, uint32_t *pos) +{ + int i; + for (i = 0; i < map->channels; i++) + pos[i] = channel_pa2id(map->map[i]); +} + struct cvolume { uint8_t channels; float values[CHANNELS_MAX]; @@ -127,7 +310,7 @@ struct format_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; + uint32_t i, aux = 0; spa_format_parse(param, &info.media_type, &info.media_subtype); @@ -146,8 +329,64 @@ static int format_parse_param(const struct spa_pod *param, struct sample_spec *s ss->channels = info.info.raw.channels; map->channels = info.info.raw.channels; -// for (i = 0; i < map->channels; i++) -// map->map[i] = info.info.raw.position[i]; + for (i = 0; i < map->channels; i++) + map->map[i] = channel_id2pa(info.info.raw.position[i], &aux); return 0; } + +static const struct spa_pod *format_build_param(struct spa_pod_builder *b, + uint32_t id, struct sample_spec *spec, struct channel_map *map) +{ + struct spa_audio_info_raw info; + + info = SPA_AUDIO_INFO_RAW_INIT( + .format = format_pa2id(spec->format), + .channels = spec->channels, + .rate = spec->rate); + if (map) + channel_map_to_positions(map, info.position); + + return spa_format_audio_raw_build(b, id, &info); +} + +static const struct spa_pod *format_info_build_param(struct spa_pod_builder *b, + uint32_t id, struct format_info *info) +{ + const char *str; + struct sample_spec ss; + struct channel_map map, *pmap = NULL; + size_t size; + + spa_zero(ss); + spa_zero(map); + + if ((str = pw_properties_get(info->props, "format.sample_format")) == NULL) + return NULL; + if (str[0] != '\"') + return NULL; + str++; + size = strcspn(str, "\""); + ss.format = format_name2pa(str, size); + if (ss.format == SAMPLE_INVALID) + return NULL; + + if ((str = pw_properties_get(info->props, "format.rate")) == NULL) + return NULL; + ss.rate = atoi(str); + + if ((str = pw_properties_get(info->props, "format.channels")) == NULL) + return NULL; + ss.channels = atoi(str); + + 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); + str += size; + } + if (map.channels == ss.channels) + pmap = ↦ + } + return format_build_param(b, id, &ss, pmap); +} diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 8a6644935..23e2b90f3 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -623,6 +623,64 @@ static int send_command_request(struct stream *stream) return send_message(client, msg); } +static uint32_t usec_to_bytes_round_up(uint64_t usec, const struct sample_spec *ss) +{ + uint64_t u; + u = (uint64_t) usec * (uint64_t) ss->rate; + u = (u + 1000000UL - 1) / 1000000UL; + u *= sample_spec_frame_size(ss); + return (uint32_t) u; +} + +static void fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr) +{ + uint32_t frame_size, max_prebuf, minreq; + + frame_size = s->frame_size; + + if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) + attr->maxlength = MAXLENGTH; + attr->maxlength -= attr->maxlength % frame_size; + attr->maxlength = SPA_MAX(attr->maxlength, frame_size); + + if (attr->tlength == (uint32_t) -1) + attr->tlength = usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*1000, &s->ss); + if (attr->tlength > attr->maxlength) + attr->tlength = attr->maxlength; + attr->tlength -= attr->tlength % frame_size; + attr->tlength = SPA_MAX(attr->tlength, frame_size); + + if (attr->minreq == (uint32_t) -1) { + uint32_t process = usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*1000, &s->ss); + /* With low-latency, tlength/4 gives a decent default in all of traditional, + * adjust latency and early request modes. */ + uint32_t m = attr->tlength / 4; + m -= m % frame_size; + attr->minreq = SPA_MIN(process, m); + } + minreq = usec_to_bytes_round_up(MIN_USEC, &s->ss); + attr->minreq = SPA_MAX(attr->minreq, minreq); + + if (attr->tlength < attr->minreq+frame_size) + attr->tlength = attr->minreq + frame_size; + + attr->minreq -= attr->minreq % frame_size; + if (attr->minreq <= 0) { + attr->minreq = frame_size; + attr->tlength += frame_size*2; + } + if (attr->tlength <= attr->minreq) + attr->tlength = attr->minreq*2 + frame_size; + + max_prebuf = attr->tlength + frame_size - attr->minreq; + if (attr->prebuf == (uint32_t) -1 || attr->prebuf > max_prebuf) + attr->prebuf = max_prebuf; + attr->prebuf -= attr->prebuf % frame_size; + attr->prebuf = SPA_MAX(attr->prebuf, frame_size); + + pw_log_info(NAME" %p: maxlength:%u tlength:%u minreq:%u prebuf:%u", s, + attr->maxlength, attr->tlength, attr->minreq, attr->prebuf); +} static int reply_create_playback_stream(struct stream *stream) { @@ -630,6 +688,18 @@ static int reply_create_playback_stream(struct stream *stream) struct impl *impl = client->impl; struct message *reply; uint32_t size; + struct spa_dict_item items[1]; + char latency[32]; + + fix_playback_buffer_attr(stream, &stream->attr); + + snprintf(latency, sizeof(latency)-1, "%u/%u", + stream->attr.minreq * 2 / stream->frame_size, + stream->ss.rate); + + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, latency); + pw_stream_update_properties(stream->stream, + &SPA_DICT_INIT(items, 1)); size = writable_size(stream, 0); @@ -678,11 +748,49 @@ static int reply_create_playback_stream(struct stream *stream) return send_message(client, reply); } +static void fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr) +{ + uint32_t frame_size, minfrag; + + frame_size = s->frame_size; + + if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) + attr->maxlength = MAXLENGTH; + attr->maxlength -= attr->maxlength % frame_size; + attr->maxlength = SPA_MAX(attr->maxlength, frame_size); + + minfrag = usec_to_bytes_round_up(MIN_USEC, &s->ss); + + if (attr->fragsize == (uint32_t) -1 || attr->fragsize == 0) + attr->fragsize = usec_to_bytes_round_up(DEFAULT_FRAGSIZE_MSEC*1000, &s->ss); + attr->fragsize -= attr->fragsize % frame_size; + attr->fragsize = SPA_MAX(attr->fragsize, minfrag); + attr->fragsize = SPA_MAX(attr->fragsize, frame_size); + + if (attr->fragsize > attr->maxlength) + attr->fragsize = attr->maxlength; + + pw_log_info(NAME" %p: maxlength:%u fragsize:%u minfrag:%u", s, + attr->maxlength, attr->fragsize, minfrag); +} + static int reply_create_record_stream(struct stream *stream) { struct client *client = stream->client; struct impl *impl = client->impl; struct message *reply; + struct spa_dict_item items[1]; + char latency[32]; + + fix_record_buffer_attr(stream, &stream->attr); + + snprintf(latency, sizeof(latency)-1, "%u/%u", + stream->attr.fragsize / stream->frame_size, + stream->ss.rate); + + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, latency); + pw_stream_update_properties(stream->stream, + &SPA_DICT_INIT(items, 1)); reply = reply_new(client, stream->create_tag); message_put(reply, @@ -801,6 +909,10 @@ static void stream_param_changed(void *data, uint32_t id, const struct spa_pod * pw_log_info(NAME" %p: got rate:%u channels:%u", stream, stream->ss.rate, stream->ss.channels); stream->frame_size = sample_spec_frame_size(&stream->ss); + if (stream->frame_size == 0) { + pw_stream_set_error(stream->stream, res, "format not supported"); + return; + } if (stream->create_tag != SPA_ID_INVALID) { if (stream->volume_set) { @@ -956,68 +1068,6 @@ static const struct pw_stream_events stream_events = .drained = stream_drained, }; -static uint32_t usec_to_bytes_round_up(uint64_t usec, const struct sample_spec *ss) -{ - uint64_t u; - u = (uint64_t) usec * (uint64_t) ss->rate; - u = (u + 1000000UL - 1) / 1000000UL; - u *= sample_spec_frame_size(ss); - return (uint32_t) u; -} - -static int fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr) -{ - uint32_t frame_size, max_prebuf, minreq; - - frame_size = s->frame_size; - if (frame_size == 0) - return -EIO; - - if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) - attr->maxlength = MAXLENGTH; - attr->maxlength -= attr->maxlength % frame_size; - attr->maxlength = SPA_MAX(attr->maxlength, frame_size); - - if (attr->tlength == (uint32_t) -1) - attr->tlength = usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*1000, &s->ss); - if (attr->tlength > attr->maxlength) - attr->tlength = attr->maxlength; - attr->tlength -= attr->tlength % frame_size; - attr->tlength = SPA_MAX(attr->tlength, frame_size); - - if (attr->minreq == (uint32_t) -1) { - uint32_t process = usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*1000, &s->ss); - /* With low-latency, tlength/4 gives a decent default in all of traditional, - * adjust latency and early request modes. */ - uint32_t m = attr->tlength / 4; - m -= m % frame_size; - attr->minreq = SPA_MIN(process, m); - } - minreq = usec_to_bytes_round_up(MIN_USEC, &s->ss); - attr->minreq = SPA_MAX(attr->minreq, minreq); - - if (attr->tlength < attr->minreq+frame_size) - attr->tlength = attr->minreq + frame_size; - - attr->minreq -= attr->minreq % frame_size; - if (attr->minreq <= 0) { - attr->minreq = frame_size; - attr->tlength += frame_size*2; - } - if (attr->tlength <= attr->minreq) - attr->tlength = attr->minreq*2 + frame_size; - - max_prebuf = attr->tlength + frame_size - attr->minreq; - if (attr->prebuf == (uint32_t) -1 || attr->prebuf > max_prebuf) - attr->prebuf = max_prebuf; - attr->prebuf -= attr->prebuf % frame_size; - attr->prebuf = SPA_MAX(attr->prebuf, frame_size); - - pw_log_info(NAME" %p: maxlength:%u tlength:%u minreq:%u prebuf:%u", s, - attr->maxlength, attr->tlength, attr->minreq, attr->prebuf); - return 0; -} - static int do_create_playback_stream(struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct impl *impl = client->impl; @@ -1048,11 +1098,9 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui struct cvolume volume; struct pw_properties *props = NULL; uint8_t n_formats = 0; - struct format_info *formats = NULL; struct stream *stream = NULL; - struct spa_audio_info_raw info; - uint32_t n_params, flags; - const struct spa_pod *params[1]; + uint32_t n_params = 0, flags; + const struct spa_pod *params[32]; uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); const char *str; @@ -1134,6 +1182,12 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui TAG_INVALID)) < 0) goto error; } + + if (sample_spec_valid(&ss)) { + if ((params[n_params] = format_build_param(&b, + SPA_PARAM_EnumFormat, &ss, &map)) != NULL) + n_params++; + } if (client->version >= 21) { if ((res = message_get(m, TAG_U8, &n_formats, @@ -1142,12 +1196,16 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui if (n_formats) { uint8_t i; - formats = calloc(n_formats, sizeof(struct format_info)); for (i = 0; i < n_formats; i++) { + struct format_info format; if ((res = message_get(m, - TAG_FORMAT_INFO, &formats[i], + TAG_FORMAT_INFO, &format, TAG_INVALID)) < 0) goto error; + + if ((params[n_params] = format_info_build_param(&b, + SPA_PARAM_EnumFormat, &format)) != NULL) + n_params++; } } } @@ -1182,17 +1240,8 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui stream->volume_set = volume_set; stream->muted = muted; stream->muted_set = muted_set; - - stream->frame_size = sample_spec_frame_size(&stream->ss); - - if ((res = fix_playback_buffer_attr(stream, &attr)) < 0) - goto error; - stream->attr = attr; - pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", - stream->attr.minreq * 2 / stream->frame_size, ss.rate); - if ((str = pw_properties_get(props, PW_KEY_MEDIA_ROLE)) != NULL) { if (strcmp(str, "video") == 0) str = "Movie"; @@ -1232,13 +1281,6 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui &stream->stream_listener, &stream_events, stream); - info = SPA_AUDIO_INFO_RAW_INIT( - .format = format_pa2id(ss.format), - .channels = ss.channels, - .rate = ss.rate); - n_params = 0; - params[n_params++] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info); - pw_stream_connect(stream->stream, PW_DIRECTION_OUTPUT, SPA_ID_INVALID, @@ -1258,35 +1300,6 @@ error: return res; } -static int fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr) -{ - uint32_t frame_size, minfrag; - - frame_size = s->frame_size; - if (frame_size == 0) - return -EIO; - - if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) - attr->maxlength = MAXLENGTH; - attr->maxlength -= attr->maxlength % frame_size; - attr->maxlength = SPA_MAX(attr->maxlength, frame_size); - - minfrag = usec_to_bytes_round_up(MIN_USEC, &s->ss); - - if (attr->fragsize == (uint32_t) -1 || attr->fragsize == 0) - attr->fragsize = usec_to_bytes_round_up(DEFAULT_FRAGSIZE_MSEC*1000, &s->ss); - attr->fragsize -= attr->fragsize % frame_size; - attr->fragsize = SPA_MAX(attr->fragsize, minfrag); - attr->fragsize = SPA_MAX(attr->fragsize, frame_size); - - if (attr->fragsize > attr->maxlength) - attr->fragsize = attr->maxlength; - - pw_log_info(NAME" %p: maxlength:%u fragsize:%u minfrag:%u", s, - attr->maxlength, attr->fragsize, minfrag); - return 0; -} - static int do_create_record_stream(struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct impl *impl = client->impl; @@ -1319,11 +1332,9 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint struct cvolume volume; struct pw_properties *props = NULL; uint8_t n_formats = 0; - struct format_info *formats = NULL; struct stream *stream = NULL; - struct spa_audio_info_raw info; - uint32_t n_params, flags; - const struct spa_pod *params[1]; + uint32_t n_params = 0, flags; + const struct spa_pod *params[32]; uint8_t buffer[4096]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); @@ -1387,6 +1398,11 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint TAG_INVALID)) < 0) goto error; } + if (sample_spec_valid(&ss)) { + if ((params[n_params] = format_build_param(&b, + SPA_PARAM_EnumFormat, &ss, &map)) != NULL) + n_params++; + } if (client->version >= 22) { if ((res = message_get(m, TAG_U8, &n_formats, @@ -1395,12 +1411,16 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint if (n_formats) { uint8_t i; - formats = calloc(n_formats, sizeof(struct format_info)); for (i = 0; i < n_formats; i++) { + struct format_info format; if ((res = message_get(m, - TAG_FORMAT_INFO, &formats[i], + TAG_FORMAT_INFO, &format, TAG_INVALID)) < 0) goto error; + + if ((params[n_params] = format_info_build_param(&b, + SPA_PARAM_EnumFormat, &format)) != NULL) + n_params++; } } if ((res = message_get(m, @@ -1444,17 +1464,8 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint stream->volume_set = volume_set; stream->muted = muted; stream->muted_set = muted_set; - - stream->frame_size = sample_spec_frame_size(&stream->ss); - - if ((res = fix_record_buffer_attr(stream, &attr)) < 0) - goto error; - stream->attr = attr; - pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", - stream->attr.fragsize / stream->frame_size, ss.rate); - if (peak_detect) pw_properties_set(props, PW_KEY_STREAM_MONITOR, "true"); flags = 0; @@ -1471,14 +1482,6 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint &stream->stream_listener, &stream_events, stream); - info = SPA_AUDIO_INFO_RAW_INIT( - .format = format_pa2id(ss.format), - .channels = ss.channels, - .rate = ss.rate); - - n_params = 0; - params[n_params++] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info); - pw_stream_connect(stream->stream, PW_DIRECTION_INPUT, SPA_ID_INVALID,