[pulseaudio-discuss] [RFC v2 18/18] bluetooth: switch between SBC and MPEG
Frédéric Dalleau
frederic.dalleau at linux.intel.com
Thu Mar 22 09:36:42 PDT 2012
---
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
More information about the pulseaudio-discuss
mailing list