module-echo-cancel: support different channels per stream

Make the channels configurable for capture, source, playback and
sink streams. The playback and sink stream need to have the same number
of channels but the layout can be different.

Use init2 of the echo-cancel plugin when available to pass this info
around. The echo-canceler will then further adjust the settings to make
things work.
This commit is contained in:
Wim Taymans 2023-04-11 16:55:48 +02:00
parent 45b2983439
commit 2a5c23c811

View file

@ -183,7 +183,9 @@ struct impl {
struct spa_hook core_proxy_listener; struct spa_hook core_proxy_listener;
struct spa_hook core_listener; struct spa_hook core_listener;
struct spa_audio_info_raw info; struct spa_audio_info_raw rec_info;
struct spa_audio_info_raw out_info;
struct spa_audio_info_raw play_info;
struct pw_properties *capture_props; struct pw_properties *capture_props;
struct pw_stream *capture; struct pw_stream *capture;
@ -247,10 +249,13 @@ static inline void aec_run(struct impl *impl, const float *rec[], const float *p
if (impl->wav_file == NULL) { if (impl->wav_file == NULL) {
struct wav_file_info info; struct wav_file_info info;
info.info.info.raw = impl->info; spa_zero(info);
info.info.info.raw.channels *= 3;
info.info.media_type = SPA_MEDIA_TYPE_audio; info.info.media_type = SPA_MEDIA_TYPE_audio;
info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw; info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
info.info.info.raw.format = SPA_AUDIO_FORMAT_F32P;
info.info.info.raw.rate = impl->rec_info.rate;
info.info.info.raw.channels = impl->play_info.channels +
impl->rec_info.channels + impl->out_info.channels;
impl->wav_file = wav_file_open(impl->wav_path, impl->wav_file = wav_file_open(impl->wav_path,
"w", &info); "w", &info);
@ -259,14 +264,17 @@ static inline void aec_run(struct impl *impl, const float *rec[], const float *p
impl->wav_path); impl->wav_path);
} }
if (impl->wav_file) { if (impl->wav_file) {
uint32_t i, c = impl->info.channels; uint32_t i, n, c = impl->play_info.channels +
const float *data[c * 3]; impl->rec_info.channels + impl->out_info.channels;
const float *data[c];
for (i = n = 0; i < impl->play_info.channels; i++)
data[n++] = play[i];
for (i = 0; i < impl->rec_info.channels; i++)
data[n++] = rec[i];
for (i = 0; i < impl->out_info.channels; i++)
data[n++] = out[i];
for (i = 0; i < c; i++) {
data[i ] = play[i];
data[i + c] = rec[i];
data[i + 2*c] = out[i];
}
wav_file_write(impl->wav_file, (void*)data, n_samples); wav_file_write(impl->wav_file, (void*)data, n_samples);
} else { } else {
spa_zero(impl->wav_path); spa_zero(impl->wav_path);
@ -281,18 +289,17 @@ static void process(struct impl *impl)
{ {
struct pw_buffer *cout; struct pw_buffer *cout;
struct pw_buffer *pout = NULL; struct pw_buffer *pout = NULL;
float rec_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)]; float rec_buf[impl->rec_info.channels][impl->aec_blocksize / sizeof(float)];
float play_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)]; float play_buf[impl->play_info.channels][impl->aec_blocksize / sizeof(float)];
float play_delayed_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)]; float play_delayed_buf[impl->play_info.channels][impl->aec_blocksize / sizeof(float)];
float out_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)]; float out_buf[impl->out_info.channels][impl->aec_blocksize / sizeof(float)];
const float *rec[impl->info.channels]; const float *rec[impl->rec_info.channels];
const float *play[impl->info.channels]; const float *play[impl->play_info.channels];
const float *play_delayed[impl->info.channels]; const float *play_delayed[impl->play_info.channels];
float *out[impl->info.channels]; float *out[impl->out_info.channels];
struct spa_data *dd; struct spa_data *dd;
uint32_t i, size; uint32_t i, size;
uint32_t rindex, pindex, oindex, pdindex, avail; uint32_t rindex, pindex, oindex, pdindex, avail;
int32_t stride = 0;
if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) { if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) {
pw_log_debug("out of playback buffers: %m"); pw_log_debug("out of playback buffers: %m");
@ -302,32 +309,37 @@ static void process(struct impl *impl)
size = impl->aec_blocksize; size = impl->aec_blocksize;
/* First read a block from the playback and capture ring buffers */ /* First read a block from the playback and capture ring buffers */
spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex);
spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->rec_info.channels; i++) {
/* captured samples, with echo from sink */ /* captured samples, with echo from sink */
rec[i] = &rec_buf[i][0]; rec[i] = &rec_buf[i][0];
/* echo from sink */
play[i] = &play_buf[i][0];
/* echo from sink delayed */
play_delayed[i] = &play_delayed_buf[i][0];
/* filtered samples, without echo from sink */
out[i] = &out_buf[i][0];
stride = 0;
spa_ringbuffer_read_data(&impl->rec_ring, impl->rec_buffer[i], spa_ringbuffer_read_data(&impl->rec_ring, impl->rec_buffer[i],
impl->rec_ringsize, rindex % impl->rec_ringsize, impl->rec_ringsize, rindex % impl->rec_ringsize,
(void*)rec[i], size); (void*)rec[i], size);
stride = 0; }
spa_ringbuffer_read_update(&impl->rec_ring, rindex + size);
for (i = 0; i < impl->out_info.channels; i++) {
/* filtered samples, without echo from sink */
out[i] = &out_buf[i][0];
}
spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
for (i = 0; i < impl->play_info.channels; i++) {
/* echo from sink */
play[i] = &play_buf[i][0];
/* echo from sink delayed */
play_delayed[i] = &play_delayed_buf[i][0];
spa_ringbuffer_read_data(&impl->play_ring, impl->play_buffer[i], spa_ringbuffer_read_data(&impl->play_ring, impl->play_buffer[i],
impl->play_ringsize, pindex % impl->play_ringsize, impl->play_ringsize, pindex % impl->play_ringsize,
(void *)play[i], size); (void *)play[i], size);
stride = 0;
spa_ringbuffer_read_data(&impl->play_delayed_ring, impl->play_buffer[i], spa_ringbuffer_read_data(&impl->play_delayed_ring, impl->play_buffer[i],
impl->play_ringsize, pdindex % impl->play_ringsize, impl->play_ringsize, pdindex % impl->play_ringsize,
(void *)play_delayed[i], size); (void *)play_delayed[i], size);
@ -339,11 +351,9 @@ static void process(struct impl *impl)
dd->chunk->offset = 0; dd->chunk->offset = 0;
dd->chunk->size = size; dd->chunk->size = size;
dd->chunk->stride = stride; dd->chunk->stride = sizeof(float);
} }
} }
spa_ringbuffer_read_update(&impl->rec_ring, rindex + size);
spa_ringbuffer_read_update(&impl->play_ring, pindex + size); spa_ringbuffer_read_update(&impl->play_ring, pindex + size);
spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size);
@ -357,19 +367,20 @@ static void process(struct impl *impl)
/* don't run the canceller until play_buffer has been filled, /* don't run the canceller until play_buffer has been filled,
* copy silence to output in the meantime */ * copy silence to output in the meantime */
silence_size = SPA_MIN(size, delay_left * sizeof(float)); silence_size = SPA_MIN(size, delay_left * sizeof(float));
for (i = 0; i < impl->info.channels; i++) for (i = 0; i < impl->out_info.channels; i++)
memset(out[i], 0, silence_size); memset(out[i], 0, silence_size);
impl->current_delay += silence_size / sizeof(float); impl->current_delay += silence_size / sizeof(float);
pw_log_debug("current_delay %d", impl->current_delay); pw_log_debug("current_delay %d", impl->current_delay);
if (silence_size != size) { if (silence_size != size) {
const float *pd[impl->info.channels]; const float *pd[impl->play_info.channels];
float *o[impl->info.channels]; float *o[impl->out_info.channels];
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->play_info.channels; i++)
pd[i] = play_delayed[i] + delay_left; pd[i] = play_delayed[i] + delay_left;
for (i = 0; i < impl->out_info.channels; i++)
o[i] = out[i] + delay_left; o[i] = out[i] + delay_left;
}
aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left); aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left);
} }
} else { } else {
@ -393,7 +404,7 @@ static void process(struct impl *impl)
avail += drop; avail += drop;
} }
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->out_info.channels; i++) {
/* captured samples, with echo from sink */ /* captured samples, with echo from sink */
spa_ringbuffer_write_data(&impl->out_ring, impl->out_buffer[i], spa_ringbuffer_write_data(&impl->out_ring, impl->out_buffer[i],
impl->out_ringsize, oindex % impl->out_ringsize, impl->out_ringsize, oindex % impl->out_ringsize,
@ -412,14 +423,14 @@ static void process(struct impl *impl)
break; break;
} }
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->out_info.channels; i++) {
dd = &cout->buffer->datas[i]; dd = &cout->buffer->datas[i];
spa_ringbuffer_read_data(&impl->out_ring, impl->out_buffer[i], spa_ringbuffer_read_data(&impl->out_ring, impl->out_buffer[i],
impl->out_ringsize, oindex % impl->out_ringsize, impl->out_ringsize, oindex % impl->out_ringsize,
(void *)dd->data, size); (void *)dd->data, size);
dd->chunk->offset = 0; dd->chunk->offset = 0;
dd->chunk->size = size; dd->chunk->size = size;
dd->chunk->stride = 0; dd->chunk->stride = sizeof(float);
} }
pw_stream_queue_buffer(impl->source, cout); pw_stream_queue_buffer(impl->source, cout);
@ -482,7 +493,7 @@ static void capture_process(void *data)
pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize); pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize);
} }
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->rec_info.channels; i++) {
/* captured samples, with echo from sink */ /* captured samples, with echo from sink */
d = &buf->buffer->datas[i]; d = &buf->buffer->datas[i];
@ -573,11 +584,12 @@ static void reset_buffers(struct impl *impl)
spa_ringbuffer_init(&impl->play_delayed_ring); spa_ringbuffer_init(&impl->play_delayed_ring);
spa_ringbuffer_init(&impl->out_ring); spa_ringbuffer_init(&impl->out_ring);
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->rec_info.channels; i++)
memset(impl->rec_buffer[i], 0, impl->rec_ringsize); memset(impl->rec_buffer[i], 0, impl->rec_ringsize);
for (i = 0; i < impl->play_info.channels; i++)
memset(impl->play_buffer[i], 0, impl->play_ringsize); memset(impl->play_buffer[i], 0, impl->play_ringsize);
for (i = 0; i < impl->out_info.channels; i++)
memset(impl->out_buffer[i], 0, impl->out_ringsize); memset(impl->out_buffer[i], 0, impl->out_ringsize);
}
spa_ringbuffer_get_write_index(&impl->play_ring, &index); spa_ringbuffer_get_write_index(&impl->play_ring, &index);
spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay))); spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay)));
@ -856,7 +868,7 @@ static void sink_process(void *data)
pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize); pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize);
} }
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->play_info.channels; i++) {
/* echo from sink */ /* echo from sink */
d = &buf->buffer->datas[i]; d = &buf->buffer->datas[i];
@ -1034,14 +1046,15 @@ static int setup_streams(struct impl *impl)
spa_pod_dynamic_builder_clean(&b); spa_pod_dynamic_builder_clean(&b);
impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->rec_info.rate / 1000;
impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->info.rate / 1000) + impl->buffer_delay); impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->play_info.rate / 1000) + impl->buffer_delay);
impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->out_info.rate / 1000;
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->rec_info.channels; i++)
impl->rec_buffer[i] = malloc(impl->rec_ringsize); impl->rec_buffer[i] = malloc(impl->rec_ringsize);
for (i = 0; i < impl->play_info.channels; i++)
impl->play_buffer[i] = malloc(impl->play_ringsize); impl->play_buffer[i] = malloc(impl->play_ringsize);
for (i = 0; i < impl->out_info.channels; i++)
impl->out_buffer[i] = malloc(impl->out_ringsize); impl->out_buffer[i] = malloc(impl->out_ringsize);
}
reset_buffers(impl); reset_buffers(impl);
@ -1101,14 +1114,12 @@ static void impl_destroy(struct impl *impl)
pw_properties_free(impl->playback_props); pw_properties_free(impl->playback_props);
pw_properties_free(impl->sink_props); pw_properties_free(impl->sink_props);
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->rec_info.channels; i++)
if (impl->rec_buffer[i])
free(impl->rec_buffer[i]); free(impl->rec_buffer[i]);
if (impl->play_buffer[i]) for (i = 0; i < impl->play_info.channels; i++)
free(impl->play_buffer[i]); free(impl->play_buffer[i]);
if (impl->out_buffer[i]) for (i = 0; i < impl->out_info.channels; i++)
free(impl->out_buffer[i]); free(impl->out_buffer[i]);
}
free(impl); free(impl);
} }
@ -1189,6 +1200,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
{ {
struct pw_context *context = pw_impl_module_get_context(module); struct pw_context *context = pw_impl_module_get_context(module);
struct pw_properties *props, *aec_props; struct pw_properties *props, *aec_props;
struct spa_audio_info_raw info;
struct impl *impl; struct impl *impl;
uint32_t id = pw_global_get_id(pw_impl_module_get_global(module)); uint32_t id = pw_global_get_id(pw_impl_module_get_global(module));
uint32_t pid = getpid(); uint32_t pid = getpid();
@ -1243,12 +1255,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (pw_properties_get(props, "resample.prefill") == NULL) if (pw_properties_get(props, "resample.prefill") == NULL)
pw_properties_set(props, "resample.prefill", "true"); pw_properties_set(props, "resample.prefill", "true");
parse_audio_info(props, &impl->info); parse_audio_info(props, &info);
impl->capture_info = impl->info; impl->capture_info = info;
impl->source_info = impl->info; impl->source_info = info;
impl->sink_info = impl->info; impl->sink_info = info;
impl->playback_info = impl->info; impl->playback_info = info;
if ((str = pw_properties_get(props, "capture.props")) != NULL) if ((str = pw_properties_get(props, "capture.props")) != NULL)
pw_properties_update_string(impl->capture_props, str, strlen(str)); pw_properties_update_string(impl->capture_props, str, strlen(str));
@ -1296,6 +1308,48 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true"); pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true");
} }
copy_props(impl, props, PW_KEY_NODE_GROUP);
copy_props(impl, props, PW_KEY_NODE_LINK_GROUP);
copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS);
copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
copy_props(impl, props, "resample.prefill");
impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS);
if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) {
int req_num, req_denom;
if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) {
if (req_denom != 0) {
impl->buffer_delay = (info.rate * req_num) / req_denom;
} else {
impl->buffer_delay = DELAY_MS * info.rate / 1000;
pw_log_warn("Sample rate for buffer.play_delay is 0 using default");
}
} else {
impl->buffer_delay = DELAY_MS * info.rate / 1000;
pw_log_warn("Wrong value/format for buffer.play_delay using default");
}
} else {
impl->buffer_delay = DELAY_MS * info.rate / 1000;
}
if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
parse_position(&impl->capture_info, str, strlen(str));
}
if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
parse_position(&impl->source_info, str, strlen(str));
}
if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
parse_position(&impl->sink_info, str, strlen(str));
impl->playback_info = impl->sink_info;
}
if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) {
parse_position(&impl->playback_info, str, strlen(str));
if (impl->playback_info.channels != impl->sink_info.channels)
impl->playback_info = impl->sink_info;
}
if ((str = pw_properties_get(props, "aec.method")) != NULL) if ((str = pw_properties_get(props, "aec.method")) != NULL)
pw_log_warn("aec.method is not supported anymore use library.name"); pw_log_warn("aec.method is not supported anymore use library.name");
@ -1313,12 +1367,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
return -EINVAL; return -EINVAL;
} }
struct spa_dict_item info_items[] = { struct spa_dict_item dict_items[] = {
{ SPA_KEY_LIBRARY_NAME, path }, { SPA_KEY_LIBRARY_NAME, path },
}; };
struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); struct spa_dict dict = SPA_DICT_INIT_ARRAY(dict_items);
handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &info); handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &dict);
if (handle == NULL) { if (handle == NULL) {
pw_log_error("aec plugin %s not available library.name %s", SPA_NAME_AEC, path); pw_log_error("aec plugin %s not available library.name %s", SPA_NAME_AEC, path);
return -ENOENT; return -ENOENT;
@ -1346,7 +1400,39 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
else else
aec_props = pw_properties_new(NULL, NULL); aec_props = pw_properties_new(NULL, NULL);
res = spa_audio_aec_init(impl->aec, &aec_props->dict, &impl->info);
if (spa_interface_callback_check(&impl->aec->iface, struct spa_audio_aec_methods, init2, 3)) {
impl->rec_info = impl->capture_info;
impl->out_info = impl->source_info;
impl->play_info = impl->sink_info;
res = spa_audio_aec_init2(impl->aec, &aec_props->dict,
&impl->rec_info, &impl->out_info, &impl->play_info);
if (impl->sink_info.channels != impl->play_info.channels)
impl->sink_info = impl->play_info;
if (impl->playback_info.channels != impl->play_info.channels)
impl->playback_info = impl->play_info;
if (impl->capture_info.channels != impl->rec_info.channels)
impl->capture_info = impl->rec_info;
if (impl->source_info.channels != impl->out_info.channels)
impl->source_info = impl->out_info;
} else {
if (impl->source_info.channels != impl->sink_info.channels)
impl->source_info = impl->sink_info;
if (impl->capture_info.channels != impl->source_info.channels)
impl->capture_info = impl->source_info;
if (impl->playback_info.channels != impl->sink_info.channels)
impl->playback_info = impl->sink_info;
info = impl->playback_info;
res = spa_audio_aec_init(impl->aec, &aec_props->dict, &info);
impl->rec_info.channels = info.channels;
impl->out_info.channels = info.channels;
impl->play_info.channels = info.channels;
}
pw_properties_free(aec_props); pw_properties_free(aec_props);
@ -1372,17 +1458,19 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (factor == 0 || new_num == 0) { if (factor == 0 || new_num == 0) {
pw_log_info("Setting node latency to %s", impl->aec->latency); pw_log_info("Setting node latency to %s", impl->aec->latency);
pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency); pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency);
impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom; impl->aec_blocksize = sizeof(float) * info.rate * num / denom;
} else { } else {
pw_log_info("Setting node latency to %u/%u", new_num, req_denom); pw_log_info("Setting node latency to %u/%u", new_num, req_denom);
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", new_num, req_denom); pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", new_num, req_denom);
impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom * factor; impl->aec_blocksize = sizeof(float) * info.rate * num / denom * factor;
} }
} else { } else {
/* Implementation doesn't care about the block size */ /* Implementation doesn't care about the block size */
impl->aec_blocksize = 0; impl->aec_blocksize = 0;
} }
copy_props(impl, props, PW_KEY_NODE_LATENCY);
impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core); impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
if (impl->core == NULL) { if (impl->core == NULL) {
str = pw_properties_get(props, PW_KEY_REMOTE_NAME); str = pw_properties_get(props, PW_KEY_REMOTE_NAME);
@ -1399,51 +1487,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
goto error; goto error;
} }
copy_props(impl, props, PW_KEY_NODE_GROUP);
copy_props(impl, props, PW_KEY_NODE_LINK_GROUP);
copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
copy_props(impl, props, PW_KEY_NODE_LATENCY);
copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS);
copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
copy_props(impl, props, "resample.prefill");
if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL)
parse_position(&impl->capture_info, str, strlen(str));
if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL)
parse_position(&impl->source_info, str, strlen(str));
if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL)
parse_position(&impl->sink_info, str, strlen(str));
if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL)
parse_position(&impl->playback_info, str, strlen(str));
if (impl->capture_info.channels != impl->info.channels)
impl->capture_info = impl->info;
if (impl->source_info.channels != impl->info.channels)
impl->source_info = impl->info;
if (impl->sink_info.channels != impl->info.channels)
impl->sink_info = impl->info;
if (impl->playback_info.channels != impl->info.channels)
impl->playback_info = impl->info;
impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS);
if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) {
int req_num, req_denom;
if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) {
if (req_denom != 0) {
impl->buffer_delay = (impl->info.rate*req_num)/req_denom;
} else {
impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
pw_log_warn("Sample rate for buffer.play_delay is 0 using default");
}
} else {
impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
pw_log_warn("Wrong value/format for buffer.play_delay using default");
}
} else {
impl->buffer_delay = DELAY_MS * impl->info.rate / 1000;
}
pw_properties_free(props); pw_properties_free(props);
pw_proxy_add_listener((struct pw_proxy*)impl->core, pw_proxy_add_listener((struct pw_proxy*)impl->core,