common: Rewrite input stream opening.

This commit is contained in:
Juho Hämäläinen 2019-10-03 16:52:57 +03:00
parent 2a7dba4d5c
commit 2bbeb30522
2 changed files with 260 additions and 95 deletions

View file

@ -92,7 +92,7 @@ static const char * const droid_combined_auto_inputs[2] = { "primary", NULL
static void droid_port_free(pa_droid_port *p);
static pa_droid_stream *get_primary_output(pa_droid_hw_module *hw);
static int input_stream_set_route(pa_droid_stream *s, audio_devices_t device);
static int input_stream_set_route(pa_droid_hw_module *hw_module);
static pa_droid_profile *profile_new(pa_droid_profile_set *ps,
const pa_droid_config_hw_module *module,
@ -1221,144 +1221,299 @@ fail:
return NULL;
}
static void set_active_input(pa_droid_hw_module *hw_module, pa_droid_stream *stream) {
pa_assert(hw_module);
if (hw_module->state.active_input != stream) {
pa_log_info("Set active input to %p", (void *) stream);
hw_module->state.active_input = stream;
}
}
static bool config_diff(const struct audio_config *a, const struct audio_config *b,
bool *sample_rate, bool *channel_mask, bool *format) {
bool diff = false;
pa_assert(a);
pa_assert(b);
pa_assert(sample_rate);
pa_assert(channel_mask);
pa_assert(format);
*sample_rate = *channel_mask = *format = false;
if (a->sample_rate != b->sample_rate)
diff = *sample_rate = true;
if (a->channel_mask != b->channel_mask)
diff = *channel_mask = true;
if (a->format != b->format)
diff = *format = true;
return diff;
}
static bool stream_config_convert(pa_direction_t direction,
const struct audio_config *config,
pa_sample_spec *sample_spec,
pa_channel_map *channel_map) {
uint32_t format;
uint32_t channel_mask;
uint32_t pa_channel = 0;
uint32_t channel = 0;
uint32_t i = 0;
pa_assert(direction == PA_DIRECTION_INPUT || direction == PA_DIRECTION_OUTPUT);
pa_assert(config);
pa_assert(sample_spec);
pa_assert(channel_map);
if (!pa_convert_format(config->format, CONV_FROM_HAL, (uint32_t *) &format)) {
pa_log("Config format %#010x not supported.", config->format);
return false;
}
sample_spec->format = format;
channel_mask = config->channel_mask;
while (channel_mask) {
uint32_t current = (1 << i++);
if (channel_mask & current) {
if (!pa_convert_input_channel(current, CONV_FROM_HAL, &pa_channel)) {
pa_log_warn("Could not convert channel mask value %#010x", current);
return false;
}
channel_map->map[channel] = pa_channel;
channel++;
channel_mask &= ~current;
}
}
channel_map->channels = channel;
sample_spec->rate = config->sample_rate;
sample_spec->channels = channel_map->channels;
if (!pa_sample_spec_valid(sample_spec)) {
pa_log_warn("Conversion resulted in invalid sample spec.");
return false;
}
if (!pa_channel_map_valid(channel_map)) {
pa_log_warn("Conversion resulted in invalid channel map.");
return false;
}
return true;
}
static void log_input_open(pa_log_level_t log_level, const char *prefix,
audio_devices_t device,
audio_source_t source,
uint32_t flags, /* audio_input_flags_t */
const pa_sample_spec *sample_spec,
const struct audio_config *config,
int return_code) {
pa_logl(log_level,
"%s input stream with device: %#010x source: %#010x flags: %#010x sample rate: %u (%u) channels: %u (%#010x) format: %u (%#010x) (return code %d)",
prefix,
device,
source,
flags,
sample_spec->rate,
config->sample_rate,
sample_spec->channels,
config->channel_mask,
sample_spec->format,
config->format,
return_code);
}
static int input_stream_open(pa_droid_stream *s, bool resume_from_suspend) {
pa_droid_input_stream *input;
audio_stream_in_t *stream;
audio_source_t audio_source = AUDIO_SOURCE_DEFAULT;
pa_droid_hw_module *hw_module;
pa_droid_input_stream *input = NULL;
pa_channel_map channel_map;
pa_sample_spec sample_spec;
struct audio_config config_in;
size_t buffer_size;
int ret = -1;
struct audio_config config_try;
struct audio_config config_in;
bool diff_sample_rate = false;
bool diff_channel_mask = false;
bool diff_format = false;
pa_assert(s);
pa_assert(s->input);
pa_assert(!s->input->stream);
pa_assert_se((hw_module = s->module));
if (s->input->stream) /* already open */
return 0;
pa_assert(!s->module->state.active_input);
input = s->input;
input->stream = NULL;
channel_map = input->channel_map;
sample_spec = input->sample_spec;
/* Copy our requested specs */
sample_spec = input->req_sample_spec;
channel_map = input->req_channel_map;
if (!stream_config_fill(input->device, &sample_spec, &channel_map, &config_in))
if (!stream_config_fill(hw_module->state.input_device, &sample_spec, &channel_map, &config_try))
goto done;
pa_input_device_default_audio_source(input->device, &audio_source);
pa_droid_hw_module_lock(s->module);
ret = s->module->device->open_input_stream(s->module->device,
s->module->stream_in_id++,
input->device,
&config_in,
&stream
while (true) {
config_in = config_try;
log_input_open(PA_LOG_INFO, "Trying to open",
hw_module->state.input_device,
hw_module->state.audio_source,
0, /* AUDIO_INPUT_FLAG_NONE on v3. v1 and v2 don't have input flags. */
&sample_spec,
&config_in,
0);
ret = hw_module->device->open_input_stream(hw_module->device,
++hw_module->stream_in_id,
hw_module->state.input_device,
&config_in,
&input->stream
#if AUDIO_API_VERSION_MAJ >= 3
, input->flags
, NULL /* Don't define address */
, audio_source
, 0
, NULL /* Don't define address */
, hw_module->state.audio_source
#endif
);
);
if (ret < 0) {
if (config_diff(&config_in, &config_try, &diff_sample_rate, &diff_channel_mask, &diff_format)) {
pa_log_info("Could not open input stream, differences in%s%s%s",
diff_sample_rate ? " sample_rate" : "",
diff_channel_mask ? " channel_mask" : "",
diff_format ? " format" : "");
if (diff_sample_rate)
pa_log_info("Wanted sample_rate %d suggested %d", config_try.sample_rate, config_in.sample_rate);
if (diff_channel_mask)
pa_log_info("Wanted channel_mask %#010x suggested %#010x", config_try.channel_mask, config_in.channel_mask);
if (diff_format)
pa_log_info("Wanted format %#010x suggested %#010x", config_try.format, config_in.format);
if (!stream_config_convert(PA_DIRECTION_INPUT, &config_in, &sample_spec, &channel_map)) {
pa_log_warn("Failed to update PulseAudio structures from received config values.");
break;
}
config_try = config_in;
continue;
} else {
pa_log_warn("Could not open input stream and no suggested changes received, bailing out.");
break;
}
} else if (config_diff(&config_in, &config_try, &diff_sample_rate, &diff_channel_mask, &diff_format)) {
pa_log_info("Opened input stream, but differences in%s%s%s",
diff_sample_rate ? " sample_rate" : "",
diff_channel_mask ? " channel_mask" : "",
diff_format ? " format" : "");
if (!stream_config_convert(PA_DIRECTION_INPUT, &config_in, &sample_spec, &channel_map)) {
pa_log_warn("Failed to update PulseAudio structures from received config values.");
input->stream->common.standby(&input->stream->common);
hw_module->device->close_input_stream(hw_module->device, input->stream);
input->stream = NULL;
ret = -1;
}
}
break;
}
pa_droid_hw_module_unlock(s->module);
if (ret < 0 || !stream) {
pa_logl(resume_from_suspend ? PA_LOG_DEBUG : PA_LOG_ERROR,
"Failed to open input stream: %d with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u)",
ret,
input->device,
0, /* AUDIO_INPUT_FLAG_NONE on v3. v1 and v2 don't have input flags. */
config_in.sample_rate,
sample_spec.channels,
config_in.channel_mask,
sample_spec.format,
config_in.format);
if (ret < 0 || !input->stream) {
log_input_open(resume_from_suspend ? PA_LOG_INFO : PA_LOG_ERROR, "Failed to open",
hw_module->state.input_device,
hw_module->state.audio_source,
0, /* AUDIO_INPUT_FLAG_NONE on v3. v1 and v2 don't have input flags. */
&sample_spec,
&config_in,
ret);
goto done;
}
input->stream = stream;
input->input_sample_spec = sample_spec;
input->input_channel_map = channel_map;
log_input_open(PA_LOG_INFO, "Opened",
hw_module->state.input_device,
hw_module->state.audio_source,
0, /* AUDIO_INPUT_FLAG_NONE on v3. v1 and v2 don't have input flags. */
&sample_spec,
&config_in,
ret);
input->sample_spec = sample_spec;
input->channel_map = channel_map;
buffer_size = input->stream->common.get_buffer_size(&input->stream->common);
s->buffer_size = buffer_size;
/* we need to call standby before reading with some devices. */
input->stream->common.standby(&input->stream->common);
/* Set input stream to standby */
s->input->stream->common.standby(&s->input->stream->common);
pa_log_debug("Opened input stream %p", (void *) s);
input_stream_set_route(s, input->device);
set_active_input(s->module, s);
done:
return ret;
}
static void input_stream_close(pa_droid_stream *s) {
pa_droid_input_stream *input;
pa_assert(s);
pa_assert(s->input);
pa_assert(s->input->stream);
input = s->input;
if (!s->input->stream)
return;
pa_mutex_lock(s->module->input_mutex);
s->module->device->close_input_stream(s->module->device, input->stream);
input->stream = NULL;
set_active_input(s->module, NULL);
s->module->device->close_input_stream(s->module->device, s->input->stream);
s->input->stream = NULL;
pa_log_debug("Closed input stream %p", (void *) s);
pa_mutex_unlock(s->module->input_mutex);
}
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
const pa_sample_spec *spec,
const pa_channel_map *map,
audio_devices_t devices,
pa_droid_mapping *am) {
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map) {
pa_droid_stream *s = NULL;
pa_droid_input_stream *input = NULL;
int ret = -1;
s = droid_stream_new(module);
pa_assert(hw_module);
pa_assert(requested_sample_spec);
pa_assert(requested_channel_map);
if (hw_module->state.active_input) {
pa_log_warn("Opening input stream while there is already active input stream.");
return pa_droid_stream_ref(hw_module->state.active_input);
}
s = droid_stream_new(hw_module);
s->input = input = droid_input_stream_new();
input->sample_spec = *spec;
input->channel_map = *map;
input->flags = 0; /* AUDIO_INPUT_FLAG_NONE */
input->device = devices;
/* We need to open the stream for a while so that we can know
* what sample rate we get. We need the rate for droid source. */
/* Copy our requested specs, so we know them when resuming from suspend
* as well. */
input->req_sample_spec = *requested_sample_spec;
input->req_channel_map = *requested_channel_map;
if ((ret = input_stream_open(s, false)) < 0)
goto fail;
if ((input->sample_spec.rate = input->stream->common.get_sample_rate(&input->stream->common)) != spec->rate)
pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, input->sample_spec.rate);
pa_idxset_put(module->inputs, s, NULL);
pa_log_info("Opened droid input stream %p with device: %u flags: %u sample rate: %u channels: %u format: %u buffer size: %u (%llu usec)",
(void *) s,
devices,
input->flags,
input->sample_spec.rate,
input->sample_spec.channels,
input->sample_spec.format,
s->buffer_size,
pa_bytes_to_usec(s->buffer_size, &input->sample_spec));
/* As audio_source_t may not have any effect when opening the input stream
* set input parameters immediately after opening the stream. */
if (!pa_droid_quirk(module, QUIRK_CLOSE_INPUT))
input_stream_set_route(s, devices);
/* We start the stream in suspended state. */
pa_droid_stream_suspend(s, true);
if (input_stream_open(s, false) < 0) {
pa_droid_stream_unref(s);
s = NULL;
}
return s;
fail:
pa_xfree(input);
pa_xfree(s);
return NULL;
}
pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s) {
@ -1378,12 +1533,15 @@ void pa_droid_stream_unref(pa_droid_stream *s) {
return;
if (s->output) {
pa_log_debug("Destroy output stream %p", (void *) s);
pa_mutex_lock(s->module->output_mutex);
pa_idxset_remove_by_data(s->module->outputs, s, NULL);
s->module->device->close_output_stream(s->module->device, s->output->stream);
pa_mutex_unlock(s->module->output_mutex);
pa_xfree(s->output);
} else {
pa_log_debug("Destroy input stream %p", (void *) s);
set_active_input(s->module, NULL);
pa_mutex_lock(s->module->input_mutex);
pa_idxset_remove_by_data(s->module->inputs, s, NULL);
if (s->input->stream) {

View file

@ -96,6 +96,14 @@ struct pa_droid_hw_module {
pa_atomic_t active_outputs;
pa_droid_quirks *quirks;
/* Mode and input control */
struct _state {
audio_mode_t mode;
audio_devices_t input_device;
audio_source_t audio_source;
pa_droid_stream *active_input;
} state;
};
struct pa_droid_output_stream {
@ -110,8 +118,8 @@ struct pa_droid_input_stream {
struct audio_stream_in *stream;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_sample_spec input_sample_spec;
pa_channel_map input_channel_map;
pa_sample_spec req_sample_spec;
pa_channel_map req_channel_map;
uint32_t flags;
uint32_t device;
};
@ -291,12 +299,11 @@ pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module,
*/
int pa_droid_stream_set_route(pa_droid_stream *s, audio_devices_t device);
/* Input stream operations */
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module,
const pa_sample_spec *spec,
const pa_channel_map *map,
audio_devices_t devices,
pa_droid_mapping *am);
/* Open input stream with currently active routing, sample_spec and channel_map
* are requests and may change when opening the stream. */
pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module,
const pa_sample_spec *requested_sample_spec,
const pa_channel_map *requested_channel_map);
const pa_sample_spec *pa_droid_stream_sample_spec(pa_droid_stream *stream);
const pa_channel_map *pa_droid_stream_channel_map(pa_droid_stream *stream);