--- src/modules/bluetooth/module-bluetooth-device.c | 136 ++++++++++++++++++----- 1 files changed, 109 insertions(+), 27 deletions(-) diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index cb4714d..60399c9 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -102,6 +102,9 @@ static const char* const valid_modargs[] = { NULL }; +#define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource" +#define A2DP_SOURCE_ENDPOINT_MPEG "/MediaEndpoint/A2DPSourceMpeg" + typedef enum { A2DP_MODE_SBC, A2DP_MODE_MPEG, @@ -218,6 +221,8 @@ enum { static int init_bt(struct userdata *u); static int init_profile(struct userdata *u); +static int bt_transport_acquire(struct userdata *u, pa_bool_t start); +static int bt_transport_config(struct userdata *u); static int service_send(struct userdata *u, const bt_audio_msg_header_t *msg) { ssize_t r; @@ -861,6 +866,62 @@ static void setup_sbc(struct a2dp_info *a2dp, enum profile p) { a2dp->frame_length = sbc_get_frame_length(&a2dp->sbc); } +static int bt_transport_reconfigure_cb(int err, void *data) { + struct userdata *u = data; + const pa_bluetooth_device *d; + const pa_bluetooth_transport *t; + + pa_assert(u); + + pa_log_debug("Configuration for mode %s returned %d", (u->a2dp.mode == A2DP_MODE_SBC) ? "SBC":"MPEG", err); + + if (err < 0) + return err; + + if (!(d = pa_bluetooth_discovery_get_by_path(u->discovery, u->path))) { + pa_log_error("Failed to get device object."); + return -1; + } + + /* check if profile has a new transport */ + if (!(t = pa_bluetooth_device_get_transport(d, u->profile))) { + pa_log("No transport found for profile %d", u->profile); + return -2; + } + + /* Acquire new transport */ + u->transport = pa_xstrdup(t->path); + u->a2dp.has_mpeg = t->has_mpeg; + pa_log_debug("Configured for mode %s", (u->a2dp.mode == A2DP_MODE_SBC) ? "SBC":"MPEG"); + + if (bt_transport_config(u) < 0) + return -1; + + return bt_transport_acquire(u, TRUE); +} + +static int bt_transport_reconfigure(struct userdata *u, const char *endpoint) { + const pa_bluetooth_transport *t; + + pa_log_debug("Configure for mode %s", (u->a2dp.mode == A2DP_MODE_SBC) ? "SBC":"MPEG"); + + t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport); + if (!t) { + pa_log("Transport %s no longer available", u->transport); + pa_xfree(u->transport); + u->transport = NULL; + return -1; + } + + pa_bluetooth_transport_reconfigure(t, endpoint, bt_transport_reconfigure_cb, u); + + /* After request configuration, transport will be recreated */ + pa_xfree(u->transport); + u->transport = NULL; + + return 0; +} + /* Run from main thread */ static int set_conf(struct userdata *u) { union { @@ -1210,37 +1271,47 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_sink_input *i = PA_SINK_INPUT(data); a2dp_mode_t mode; + pa_log_debug("PA_SINK_MESSAGE_ADD_INPUT i %p encoding %d:%s passthrough %d, has_mpeg %d", i, i->format->encoding, + (i->format->encoding == PA_ENCODING_PCM) ? "PCM" : (i->format->encoding == PA_ENCODING_MPEG_IEC61937) ? "IEC" : "Other", + pa_sink_is_passthrough(u->sink), + u->a2dp.has_mpeg); + if (u->profile == PROFILE_A2DP) { - switch(i->format->encoding) { - case PA_ENCODING_PCM: - mode = A2DP_MODE_SBC; - break; + if (pa_sink_is_passthrough(u->sink) && u->a2dp.has_mpeg) + mode = A2DP_MODE_MPEG; + else + mode = A2DP_MODE_SBC; - case PA_ENCODING_MPEG_IEC61937: - pa_assert(u->a2dp.has_mpeg); - mode = A2DP_MODE_MPEG; - break; + if (PA_UNLIKELY(mode != u->a2dp.mode)) { + if (u->transport) { + const char *endpoint = A2DP_SOURCE_ENDPOINT; + bt_transport_release(u); - default: - pa_assert_not_reached(); - } + u->a2dp.mode = mode; + pa_log_debug("DBUS Switch to mode %s", u->a2dp.mode == A2DP_MODE_SBC ? "SBC" : "MPEG"); - if (PA_UNLIKELY(mode != u->a2dp.mode)) { - /* FIXME: Just suspend should suffice? This resets the smoother */ - if (stop_stream_fd(u) < 0 || close_stream(u) < 0) { - failed = TRUE; - break; - } + if (u->a2dp.mode == A2DP_MODE_MPEG) + endpoint = A2DP_SOURCE_ENDPOINT_MPEG; - u->a2dp.mode = mode; - if (set_conf(u) < 0) { - failed = TRUE; - break; - } + if (bt_transport_reconfigure(u, endpoint) < 0) + failed = TRUE; + } else { + /* FIXME: Just suspend should suffice? This resets the smoother */ + if (stop_stream_fd(u) < 0 || close_stream(u) < 0) { + failed = TRUE; + break; + } - if (start_stream_fd(u) < 0) { - failed = TRUE; - break; + u->a2dp.mode = mode; + pa_log_debug("UNIX Switch to mode %s", u->a2dp.mode == A2DP_MODE_SBC ? "SBC" : "MPEG"); + + if (set_conf(u) < 0) { + failed = TRUE; + break; + } + + if (start_stream_fd(u) < 0) + failed = TRUE; } } } @@ -3311,6 +3382,16 @@ static int bt_transport_config(struct userdata *u) { return 0; } + if (u->leftover_bytes) { + pa_log_debug("SBC parameters: %d bytes forgotten", u->leftover_bytes); + u->leftover_bytes = 0; + } + if(u->write_memchunk.memblock) { + pa_log_debug("SBC parameters: memblock forgotten"); + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + } + if (u->a2dp.mode == A2DP_MODE_MPEG) return bt_transport_config_a2dp_mpeg(u); @@ -3368,8 +3449,9 @@ static int setup_bt(struct userdata *u) { t = pa_bluetooth_device_get_transport(d, u->profile); if (t) { u->transport = pa_xstrdup(t->path); - u->a2dp.has_mpeg = (t->codec == 1); - u->a2dp.mode = (t->codec == 1) ? A2DP_MODE_MPEG : A2DP_MODE_SBC; + u->a2dp.has_mpeg = t->has_mpeg; + /* Connect for SBC to start with, switch later if required */ + u->a2dp.mode = A2DP_MODE_SBC; return bt_transport_open(u); } -- 1.7.5.4