[RFC v2 18/18] bluetooth: switch between SBC and MPEG

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 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



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux