mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
Merge branch 'codec-switch-without-reroute' into 'master'
Draft: bluetooth: Switch codecs without tearing down sink/source/thread Closes #1113 See merge request pulseaudio/pulseaudio!489
This commit is contained in:
commit
6d65f7ad37
6 changed files with 194 additions and 71 deletions
|
|
@ -52,13 +52,13 @@ typedef struct pa_a2dp_endpoint_conf {
|
|||
* (const char *endpoint -> const pa_a2dp_codec_capabilities *capability)
|
||||
* and returns corresponding endpoint key (or NULL when there is no valid),
|
||||
* for_encoder is true when capabilities hash map is used for encoding */
|
||||
const char *(*choose_remote_endpoint)(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding);
|
||||
const char *(*choose_remote_endpoint)(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *spec, bool for_encoding);
|
||||
/* Fill codec capabilities, returns size of filled buffer */
|
||||
uint8_t (*fill_capabilities)(uint8_t capabilities_buffer[MAX_A2DP_CAPS_SIZE]);
|
||||
/* Validate codec configuration, returns true on success */
|
||||
bool (*is_configuration_valid)(const uint8_t *config_buffer, uint8_t config_size);
|
||||
/* Fill preferred codec configuration, returns size of filled buffer or 0 on failure */
|
||||
uint8_t (*fill_preferred_configuration)(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]);
|
||||
uint8_t (*fill_preferred_configuration)(const pa_sample_spec *spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]);
|
||||
|
||||
/* Bluetooth codec */
|
||||
pa_bt_codec bt_codec;
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ static bool can_accept_capabilities_hd(const uint8_t *capabilities_buffer, uint8
|
|||
return can_accept_capabilities_common(&capabilities->aptx, APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID);
|
||||
}
|
||||
|
||||
static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) {
|
||||
static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *spec, bool for_encoding) {
|
||||
const pa_a2dp_codec_capabilities *a2dp_capabilities;
|
||||
const char *key;
|
||||
void *state;
|
||||
|
|
@ -103,7 +103,7 @@ static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static const char *choose_remote_endpoint_hd(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) {
|
||||
static const char *choose_remote_endpoint_hd(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *spec, bool for_encoding) {
|
||||
const pa_a2dp_codec_capabilities *a2dp_capabilities;
|
||||
const char *key;
|
||||
void *state;
|
||||
|
|
@ -182,7 +182,7 @@ static bool is_configuration_valid_hd(const uint8_t *config_buffer, uint8_t conf
|
|||
return is_configuration_valid_common(&config->aptx, APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID);
|
||||
}
|
||||
|
||||
static int fill_preferred_configuration_common(const pa_sample_spec *default_sample_spec, const a2dp_aptx_t *capabilities, a2dp_aptx_t *config, uint32_t vendor_id, uint16_t codec_id) {
|
||||
static int fill_preferred_configuration_common(const pa_sample_spec *spec, const a2dp_aptx_t *capabilities, a2dp_aptx_t *config, uint32_t vendor_id, uint16_t codec_id) {
|
||||
int i;
|
||||
|
||||
static const struct {
|
||||
|
|
@ -211,7 +211,7 @@ static int fill_preferred_configuration_common(const pa_sample_spec *default_sam
|
|||
|
||||
/* Find the lowest freq that is at least as high as the requested sampling rate */
|
||||
for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) {
|
||||
if (freq_table[i].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[i].cap)) {
|
||||
if (freq_table[i].rate >= spec->rate && (capabilities->frequency & freq_table[i].cap)) {
|
||||
config->frequency = freq_table[i].cap;
|
||||
break;
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ static int fill_preferred_configuration_common(const pa_sample_spec *default_sam
|
|||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
static uint8_t fill_preferred_configuration(const pa_sample_spec *spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
a2dp_aptx_t *config = (a2dp_aptx_t *) config_buffer;
|
||||
const a2dp_aptx_t *capabilities = (const a2dp_aptx_t *) capabilities_buffer;
|
||||
|
||||
|
|
@ -245,13 +245,13 @@ static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample
|
|||
|
||||
pa_zero(*config);
|
||||
|
||||
if (fill_preferred_configuration_common(default_sample_spec, capabilities, config, APTX_VENDOR_ID, APTX_CODEC_ID) < 0)
|
||||
if (fill_preferred_configuration_common(spec, capabilities, config, APTX_VENDOR_ID, APTX_CODEC_ID) < 0)
|
||||
return 0;
|
||||
|
||||
return sizeof(*config);
|
||||
}
|
||||
|
||||
static uint8_t fill_preferred_configuration_hd(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
static uint8_t fill_preferred_configuration_hd(const pa_sample_spec *spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
a2dp_aptx_hd_t *config = (a2dp_aptx_hd_t *) config_buffer;
|
||||
const a2dp_aptx_hd_t *capabilities = (const a2dp_aptx_hd_t *) capabilities_buffer;
|
||||
|
||||
|
|
@ -262,7 +262,7 @@ static uint8_t fill_preferred_configuration_hd(const pa_sample_spec *default_sam
|
|||
|
||||
pa_zero(*config);
|
||||
|
||||
if (fill_preferred_configuration_common(default_sample_spec, &capabilities->aptx, &config->aptx, APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID) < 0)
|
||||
if (fill_preferred_configuration_common(spec, &capabilities->aptx, &config->aptx, APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID) < 0)
|
||||
return 0;
|
||||
|
||||
return sizeof(*config);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ static bool can_accept_capabilities(const uint8_t *capabilities_buffer, uint8_t
|
|||
return can_accept_capabilities_common(capabilities, LDAC_VENDOR_ID, LDAC_CODEC_ID);
|
||||
}
|
||||
|
||||
static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) {
|
||||
static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *spec, bool for_encoding) {
|
||||
const pa_a2dp_codec_capabilities *a2dp_capabilities;
|
||||
const char *key;
|
||||
void *state;
|
||||
|
|
@ -128,7 +128,7 @@ static bool is_configuration_valid(const uint8_t *config_buffer, uint8_t config_
|
|||
return true;
|
||||
}
|
||||
|
||||
static int fill_preferred_configuration_common(const pa_sample_spec *default_sample_spec, const a2dp_ldac_t *capabilities, a2dp_ldac_t *config, uint32_t vendor_id, uint16_t codec_id) {
|
||||
static int fill_preferred_configuration_common(const pa_sample_spec *spec, const a2dp_ldac_t *capabilities, a2dp_ldac_t *config, uint32_t vendor_id, uint16_t codec_id) {
|
||||
int i;
|
||||
|
||||
static const struct {
|
||||
|
|
@ -157,7 +157,7 @@ static int fill_preferred_configuration_common(const pa_sample_spec *default_sam
|
|||
|
||||
/* Find the lowest freq that is at least as high as the requested sampling rate */
|
||||
for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) {
|
||||
if (freq_table[i].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[i].cap)) {
|
||||
if (freq_table[i].rate >= spec->rate && (capabilities->frequency & freq_table[i].cap)) {
|
||||
config->frequency = freq_table[i].cap;
|
||||
break;
|
||||
}
|
||||
|
|
@ -180,7 +180,7 @@ static int fill_preferred_configuration_common(const pa_sample_spec *default_sam
|
|||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
static uint8_t fill_preferred_configuration(const pa_sample_spec *spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
a2dp_ldac_t *config = (a2dp_ldac_t *) config_buffer;
|
||||
const a2dp_ldac_t *capabilities = (const a2dp_ldac_t *) capabilities_buffer;
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample
|
|||
|
||||
pa_zero(*config);
|
||||
|
||||
if (fill_preferred_configuration_common(default_sample_spec, capabilities, config, LDAC_VENDOR_ID, LDAC_CODEC_ID) < 0)
|
||||
if (fill_preferred_configuration_common(spec, capabilities, config, LDAC_VENDOR_ID, LDAC_CODEC_ID) < 0)
|
||||
return 0;
|
||||
|
||||
return sizeof(*config);
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ static bool can_accept_capabilities_faststream(const uint8_t *capabilities_buffe
|
|||
return true;
|
||||
}
|
||||
|
||||
static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *default_sample_spec, bool for_encoding) {
|
||||
static const char *choose_remote_endpoint(const pa_hashmap *capabilities_hashmap, const pa_sample_spec *spec, bool for_encoding) {
|
||||
const pa_a2dp_codec_capabilities *a2dp_capabilities;
|
||||
const char *key;
|
||||
void *state;
|
||||
|
|
@ -495,7 +495,7 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
|
|||
pa_assert_not_reached();
|
||||
}
|
||||
|
||||
static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample_spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
static uint8_t fill_preferred_configuration(const pa_sample_spec *spec, const uint8_t *capabilities_buffer, uint8_t capabilities_size, uint8_t config_buffer[MAX_A2DP_CAPS_SIZE]) {
|
||||
a2dp_sbc_t *config = (a2dp_sbc_t *) config_buffer;
|
||||
const a2dp_sbc_t *capabilities = (const a2dp_sbc_t *) capabilities_buffer;
|
||||
int i;
|
||||
|
|
@ -519,7 +519,7 @@ static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample
|
|||
|
||||
/* Find the lowest freq that is at least as high as the requested sampling rate */
|
||||
for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
|
||||
if (freq_table[i].rate >= default_sample_spec->rate && (capabilities->frequency & freq_table[i].cap)) {
|
||||
if (freq_table[i].rate >= spec->rate && (capabilities->frequency & freq_table[i].cap)) {
|
||||
config->frequency = freq_table[i].cap;
|
||||
break;
|
||||
}
|
||||
|
|
@ -540,7 +540,7 @@ static uint8_t fill_preferred_configuration(const pa_sample_spec *default_sample
|
|||
|
||||
pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
|
||||
|
||||
if (default_sample_spec->channels <= 1) {
|
||||
if (spec->channels <= 1) {
|
||||
if (capabilities->channel_mode & SBC_CHANNEL_MODE_MONO)
|
||||
config->channel_mode = SBC_CHANNEL_MODE_MONO;
|
||||
else if (capabilities->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
|
||||
|
|
|
|||
|
|
@ -447,7 +447,6 @@ bool pa_bluetooth_device_switch_codec(pa_bluetooth_device *device, pa_bluetooth_
|
|||
uint8_t config[MAX_A2DP_CAPS_SIZE];
|
||||
uint8_t config_size;
|
||||
bool is_a2dp_sink;
|
||||
pa_hashmap *all_endpoints;
|
||||
char *pa_endpoint;
|
||||
const char *endpoint;
|
||||
|
||||
|
|
@ -462,13 +461,8 @@ bool pa_bluetooth_device_switch_codec(pa_bluetooth_device *device, pa_bluetooth_
|
|||
|
||||
is_a2dp_sink = profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
|
||||
|
||||
all_endpoints = NULL;
|
||||
all_endpoints = pa_hashmap_get(is_a2dp_sink ? device->a2dp_sink_endpoints : device->a2dp_source_endpoints,
|
||||
&endpoint_conf->id);
|
||||
pa_assert(all_endpoints);
|
||||
|
||||
pa_assert_se(endpoint = endpoint_conf->choose_remote_endpoint(capabilities_hashmap, &device->discovery->core->default_sample_spec, is_a2dp_sink));
|
||||
pa_assert_se(capabilities = pa_hashmap_get(all_endpoints, endpoint));
|
||||
pa_assert_se(capabilities = pa_hashmap_get(capabilities_hashmap, endpoint));
|
||||
|
||||
config_size = endpoint_conf->fill_preferred_configuration(&device->discovery->core->default_sample_spec,
|
||||
capabilities->buffer, capabilities->size, config);
|
||||
|
|
|
|||
|
|
@ -893,6 +893,13 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
|
|||
if (!PA_SOURCE_IS_OPENED(s->thread_info.state))
|
||||
break;
|
||||
|
||||
/* Ignore the transition if the source is suspended for internal reasons
|
||||
* such as external source-output changes resulting in pa_source_reconfigure.
|
||||
* Logic in our handler decides whether to release the transport or not.
|
||||
*/
|
||||
if (new_suspend_cause & PA_SUSPEND_INTERNAL)
|
||||
break;
|
||||
|
||||
/* Stop the device if the sink is suspended as well */
|
||||
if (!u->sink || u->sink->state == PA_SINK_SUSPENDED)
|
||||
transport_release(u);
|
||||
|
|
@ -910,6 +917,9 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
|
|||
if (s->thread_info.state != PA_SOURCE_SUSPENDED)
|
||||
break;
|
||||
|
||||
if (s->suspend_cause & PA_SUSPEND_INTERNAL)
|
||||
break;
|
||||
|
||||
/* Resume the device if the sink was suspended as well */
|
||||
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
|
||||
if (!setup_transport_and_stream(u))
|
||||
|
|
@ -929,6 +939,24 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, bool passthrough) {
|
||||
struct userdata *u;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert_se(u = s->userdata);
|
||||
|
||||
if (!pa_sample_spec_equal(spec, &u->decoder_sample_spec)) {
|
||||
pa_log_warn("Reconfiguration not yet supported");
|
||||
return;
|
||||
}
|
||||
|
||||
pa_source_set_sample_format(u->source, spec->format);
|
||||
pa_source_set_sample_rate(u->source, spec->rate);
|
||||
// Does not allow reconfiguring channels yet.
|
||||
pa_assert(u->source->sample_spec.channels == spec->channels);
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void source_set_volume_cb(pa_source *s) {
|
||||
pa_volume_t volume;
|
||||
|
|
@ -1032,6 +1060,7 @@ static int add_source(struct userdata *u) {
|
|||
if (u->bt_codec)
|
||||
pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name);
|
||||
pa_source_new_data_set_sample_spec(&data, &u->decoder_sample_spec);
|
||||
pa_source_new_data_set_avoid_resampling(&data, true);
|
||||
if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
|
||||
|| u->profile == PA_BLUETOOTH_PROFILE_HFP_HF)
|
||||
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
|
||||
|
|
@ -1075,6 +1104,7 @@ static int add_source(struct userdata *u) {
|
|||
u->source->userdata = u;
|
||||
u->source->parent.process_msg = source_process_msg;
|
||||
u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
|
||||
u->source->reconfigure = source_reconfigure_cb;
|
||||
|
||||
source_setup_volume_callback(u->source);
|
||||
|
||||
|
|
@ -1086,7 +1116,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
struct userdata *u = PA_SINK(o)->userdata;
|
||||
|
||||
pa_assert(u->sink == PA_SINK(o));
|
||||
pa_assert(u->transport);
|
||||
|
||||
switch (code) {
|
||||
|
||||
|
|
@ -1116,6 +1145,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
}
|
||||
|
||||
case PA_SINK_MESSAGE_SETUP_STREAM:
|
||||
pa_assert(u->transport);
|
||||
|
||||
/* Skip stream setup if stream_fd has been invalidated.
|
||||
This can occur if the stream has already been set up and
|
||||
then immediately received POLLHUP. If the stream has
|
||||
|
|
@ -1147,6 +1178,13 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
|
|||
if (!PA_SINK_IS_OPENED(s->thread_info.state))
|
||||
break;
|
||||
|
||||
/* Ignore the transition if the sink is suspended for internal reasons
|
||||
* such as external sink-input changes resulting in pa_sink_reconfigure.
|
||||
* Logic in our handler decides whether to release the transport or not.
|
||||
*/
|
||||
if (new_suspend_cause & PA_SUSPEND_INTERNAL)
|
||||
break;
|
||||
|
||||
/* Stop the device if the source is suspended as well */
|
||||
if (!u->source || u->source->state == PA_SOURCE_SUSPENDED)
|
||||
/* We deliberately ignore whether stopping
|
||||
|
|
@ -1161,6 +1199,9 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
|
|||
if (s->thread_info.state != PA_SINK_SUSPENDED)
|
||||
break;
|
||||
|
||||
if (s->suspend_cause & PA_SUSPEND_INTERNAL)
|
||||
break;
|
||||
|
||||
/* Resume the device if the source was suspended as well */
|
||||
if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state))
|
||||
if (!setup_transport_and_stream(u))
|
||||
|
|
@ -1177,6 +1218,24 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
|
||||
struct userdata *u;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert_se(u = s->userdata);
|
||||
|
||||
if (!pa_sample_spec_equal(spec, &u->encoder_sample_spec)) {
|
||||
pa_log_warn("Reconfiguration not yet supported");
|
||||
return;
|
||||
}
|
||||
|
||||
pa_sink_set_sample_format(u->sink, spec->format);
|
||||
pa_sink_set_sample_rate(u->sink, spec->rate);
|
||||
// Does not allow reconfiguring channels yet.
|
||||
pa_assert(u->sink->sample_spec.channels == spec->channels);
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void sink_set_volume_cb(pa_sink *s) {
|
||||
pa_volume_t volume;
|
||||
|
|
@ -1282,6 +1341,7 @@ static int add_sink(struct userdata *u) {
|
|||
if (u->bt_codec)
|
||||
pa_proplist_sets(data.proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name);
|
||||
pa_sink_new_data_set_sample_spec(&data, &u->encoder_sample_spec);
|
||||
pa_sink_new_data_set_avoid_resampling(&data, true);
|
||||
if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS
|
||||
|| u->profile == PA_BLUETOOTH_PROFILE_HFP_HF)
|
||||
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
|
||||
|
|
@ -1321,6 +1381,7 @@ static int add_sink(struct userdata *u) {
|
|||
u->sink->userdata = u;
|
||||
u->sink->parent.process_msg = sink_process_msg;
|
||||
u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
|
||||
u->sink->reconfigure = sink_reconfigure_cb;
|
||||
|
||||
sink_setup_volume_callback(u->sink);
|
||||
|
||||
|
|
@ -1815,6 +1876,37 @@ static int start_thread(struct userdata *u) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void release_codec(struct userdata *u) {
|
||||
if (u->bt_codec) {
|
||||
if (u->encoder_info) {
|
||||
u->bt_codec->deinit(u->encoder_info);
|
||||
u->encoder_info = NULL;
|
||||
}
|
||||
|
||||
if (u->decoder_info) {
|
||||
u->bt_codec->deinit(u->decoder_info);
|
||||
u->decoder_info = NULL;
|
||||
}
|
||||
|
||||
u->bt_codec = NULL;
|
||||
}
|
||||
|
||||
if (u->encoder_buffer) {
|
||||
pa_xfree(u->encoder_buffer);
|
||||
u->encoder_buffer = NULL;
|
||||
}
|
||||
|
||||
u->encoder_buffer_size = 0;
|
||||
u->encoder_buffer_used = 0;
|
||||
|
||||
if (u->decoder_buffer) {
|
||||
pa_xfree(u->decoder_buffer);
|
||||
u->decoder_buffer = NULL;
|
||||
}
|
||||
|
||||
u->decoder_buffer_size = 0;
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void stop_thread(struct userdata *u) {
|
||||
pa_assert(u);
|
||||
|
|
@ -1879,34 +1971,7 @@ static void stop_thread(struct userdata *u) {
|
|||
u->read_smoother = NULL;
|
||||
}
|
||||
|
||||
if (u->bt_codec) {
|
||||
if (u->encoder_info) {
|
||||
u->bt_codec->deinit(u->encoder_info);
|
||||
u->encoder_info = NULL;
|
||||
}
|
||||
|
||||
if (u->decoder_info) {
|
||||
u->bt_codec->deinit(u->decoder_info);
|
||||
u->decoder_info = NULL;
|
||||
}
|
||||
|
||||
u->bt_codec = NULL;
|
||||
}
|
||||
|
||||
if (u->encoder_buffer) {
|
||||
pa_xfree(u->encoder_buffer);
|
||||
u->encoder_buffer = NULL;
|
||||
}
|
||||
|
||||
u->encoder_buffer_size = 0;
|
||||
u->encoder_buffer_used = 0;
|
||||
|
||||
if (u->decoder_buffer) {
|
||||
pa_xfree(u->decoder_buffer);
|
||||
u->decoder_buffer = NULL;
|
||||
}
|
||||
|
||||
u->decoder_buffer_size = 0;
|
||||
release_codec(u);
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
|
|
@ -2332,11 +2397,13 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
|
|||
|
||||
oldavail = cp->available;
|
||||
/*
|
||||
* If codec switching is in progress, transport state change should not
|
||||
* make profile unavailable.
|
||||
* If codec switching is in progress, do not acquire the transport until our
|
||||
* "codec switch complete" callback is called, which calls transport_config
|
||||
* in addition to just acquiring it (through setup_transport). That means it
|
||||
* also has to apply any eventual format changes back to the pa_sink/source.
|
||||
*/
|
||||
if (!t->device->codec_switching_in_progress)
|
||||
pa_card_profile_set_available(cp, transport_state_to_availability(t->state));
|
||||
if (t->device->codec_switching_in_progress)
|
||||
return;
|
||||
|
||||
/* Update port availability */
|
||||
pa_assert_se(port = pa_hashmap_get(u->card->ports, u->output_port_name));
|
||||
|
|
@ -2439,12 +2506,15 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa
|
|||
pa_assert(t);
|
||||
pa_assert(u);
|
||||
|
||||
if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
|
||||
if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED && !t->device->codec_switching_in_progress)
|
||||
pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
|
||||
|
||||
if (t->device == u->device)
|
||||
handle_transport_state_change(u, t);
|
||||
|
||||
if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
|
||||
u->transport = NULL;
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
|
|
@ -2509,25 +2579,74 @@ static char* make_message_handler_path(const char *name) {
|
|||
return pa_sprintf_malloc("/card/%s/bluez", name);
|
||||
}
|
||||
|
||||
static void switch_codec_cb_handler(bool success, pa_bluetooth_profile_t profile, void *userdata)
|
||||
{
|
||||
static void switch_codec_cb_handler(bool success, pa_bluetooth_profile_t profile, void *userdata) {
|
||||
struct userdata *u = (struct userdata *) userdata;
|
||||
pa_sample_spec *spec;
|
||||
bool is_a2dp_sink;
|
||||
int r;
|
||||
pa_sink_input *i;
|
||||
pa_source_output *o;
|
||||
uint32_t idx;
|
||||
|
||||
if (!success)
|
||||
goto off;
|
||||
|
||||
u->profile = profile;
|
||||
is_a2dp_sink = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
|
||||
|
||||
if (init_profile(u) < 0) {
|
||||
pa_log_info("Failed to initialise profile after codec switching");
|
||||
goto off;
|
||||
}
|
||||
// Reacquire the transport and configure the codec
|
||||
r = setup_transport(u);
|
||||
pa_assert(!r);
|
||||
// if (r == -EINPROGRESS)
|
||||
// return 0;
|
||||
// else if (r < 0)
|
||||
// return -1;
|
||||
|
||||
if (u->sink || u->source)
|
||||
if (start_thread(u) < 0) {
|
||||
pa_log_info("Failed to start thread after codec switching");
|
||||
goto off;
|
||||
spec = is_a2dp_sink ? &u->encoder_sample_spec : &u->decoder_sample_spec;
|
||||
|
||||
// Finish off by reconfiguring
|
||||
|
||||
if (is_a2dp_sink) {
|
||||
PA_IDXSET_FOREACH(i, u->sink->inputs, idx)
|
||||
pa_sink_input_cork(i, true);
|
||||
if (u->sink->monitor_source) {
|
||||
pa_source_suspend(u->sink->monitor_source, true, PA_SUSPEND_INTERNAL);
|
||||
PA_IDXSET_FOREACH(o, u->sink->monitor_source->outputs, idx)
|
||||
pa_source_output_cork(o, true);
|
||||
}
|
||||
pa_sink_reconfigure(u->sink, spec, false);
|
||||
if (u->sink->monitor_source) {
|
||||
PA_IDXSET_FOREACH(o, u->sink->monitor_source->outputs, idx)
|
||||
pa_source_output_cork(o, false);
|
||||
pa_source_suspend(u->sink->monitor_source, false, PA_SUSPEND_INTERNAL);
|
||||
}
|
||||
PA_IDXSET_FOREACH(i, u->sink->inputs, idx)
|
||||
pa_sink_input_cork(i, false);
|
||||
|
||||
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SETUP_STREAM, NULL, 0, NULL);
|
||||
|
||||
pa_sink_suspend(u->sink, false, PA_SUSPEND_UNAVAILABLE);
|
||||
} else {
|
||||
PA_IDXSET_FOREACH(o, u->source->outputs, idx)
|
||||
pa_source_output_cork(o, true);
|
||||
if (u->source->monitor_of) {
|
||||
pa_sink_suspend(u->source->monitor_of, true, PA_SUSPEND_INTERNAL);
|
||||
PA_IDXSET_FOREACH(i, u->source->monitor_of->inputs, idx)
|
||||
pa_sink_input_cork(i, true);
|
||||
}
|
||||
pa_source_reconfigure(u->source, spec, false);
|
||||
if (u->source->monitor_of) {
|
||||
PA_IDXSET_FOREACH(i, u->source->monitor_of->inputs, idx)
|
||||
pa_sink_input_cork(i, false);
|
||||
pa_sink_suspend(u->source->monitor_of, false, PA_SUSPEND_INTERNAL);
|
||||
}
|
||||
PA_IDXSET_FOREACH(o, u->source->outputs, idx)
|
||||
pa_source_output_cork(o, false);
|
||||
|
||||
pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_SETUP_STREAM, NULL, 0, NULL);
|
||||
|
||||
pa_source_suspend(u->source, false, PA_SUSPEND_UNAVAILABLE);
|
||||
}
|
||||
|
||||
pa_log_info("Codec successfully switched to %s with profile: %s",
|
||||
u->bt_codec->name, pa_bluetooth_profile_to_string(u->profile));
|
||||
|
|
@ -2535,6 +2654,7 @@ static void switch_codec_cb_handler(bool success, pa_bluetooth_profile_t profile
|
|||
return;
|
||||
|
||||
off:
|
||||
stop_thread(u);
|
||||
pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
|
||||
}
|
||||
|
||||
|
|
@ -2698,7 +2818,15 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
|
|||
*/
|
||||
profile = u->profile;
|
||||
|
||||
stop_thread(u);
|
||||
// Suspending releases the transport
|
||||
|
||||
if (is_a2dp_sink)
|
||||
pa_sink_suspend(u->sink, true, PA_SUSPEND_UNAVAILABLE);
|
||||
else
|
||||
pa_source_suspend(u->source, true, PA_SUSPEND_UNAVAILABLE);
|
||||
|
||||
// TODO: Can we do this if it's synchronous?
|
||||
release_codec(u);
|
||||
|
||||
if (!pa_bluetooth_device_switch_codec(u->device, profile, capabilities_hashmap, endpoint_conf, switch_codec_cb_handler, userdata)
|
||||
&& !u->device->codec_switching_in_progress)
|
||||
|
|
@ -2726,6 +2854,7 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
|
|||
return -PA_ERR_NOTIMPLEMENTED;
|
||||
|
||||
profile_off:
|
||||
stop_thread(u);
|
||||
pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0);
|
||||
|
||||
return -PA_ERR_IO;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue