[v3] bluetooth: Wide Band Speech implementaion for backend ofono

Submitted by Sathish Narasimman on Aug. 20, 2018, 1:49 p.m.

Details

Message ID 1534772947-4790-1-git-send-email-sathish.narasimman@intel.com
State New
Series "bluetooth: Wide Band Speech implementaion for backend ofono"
Headers show

Commit Message

Sathish Narasimman Aug. 20, 2018, 1:49 p.m.
From: Sathish Narasimman <sathish.narasimman@intel.com>

mSBC-encoded streams for HFP. The Wide Band Speech(WBS) encoding and decoding
is implemeted with this patch. This patch was refered from original
patch of Joao Paula Rechi Vita and was verified with the supported
bluetooth controller.

Signed-off-by: Sathish Narasimman <sathish.narasimman@intel.com>
---
---
 src/modules/bluetooth/backend-ofono.c        |  20 +-
 src/modules/bluetooth/module-bluez5-device.c | 372 ++++++++++++++++++++++++++-
 2 files changed, 378 insertions(+), 14 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c
index 1f0efe9..a836779 100644
--- a/src/modules/bluetooth/backend-ofono.c
+++ b/src/modules/bluetooth/backend-ofono.c
@@ -164,7 +164,7 @@  static int card_acquire(struct hf_audio_card *card) {
                                       DBUS_TYPE_BYTE, &codec,
                                       DBUS_TYPE_INVALID) == true)) {
         dbus_message_unref(r);
-        if (codec != HFP_AUDIO_CODEC_CVSD) {
+        if (codec != HFP_AUDIO_CODEC_CVSD && codec != HFP_AUDIO_CODEC_MSBC) {
             pa_log_error("Invalid codec: %u", codec);
             /* shutdown to make sure connection is dropped immediately */
             shutdown(fd, SHUT_RDWR);
@@ -250,10 +250,17 @@  static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti
      * value from the Isoc USB endpoint in use by btusb and should be
      * made available to userspace by the Bluetooth kernel subsystem.
      * Meanwhile the empiric value 48 will be used. */
-    if (imtu)
-        *imtu = 48;
-    if (omtu)
-        *omtu = 48;
+    if (t->codec == HFP_AUDIO_CODEC_MSBC) {
+        if (imtu)
+            *imtu = 60;
+        if (omtu)
+            *omtu = 60;
+    } else {
+        if (imtu)
+            *imtu = 48;
+        if (omtu)
+            *omtu = 48;
+   }
 
     err = socket_accept(card->fd);
     if (err < 0) {
@@ -464,6 +471,7 @@  static void hf_audio_agent_register(pa_bluetooth_backend *hf) {
     pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register"));
 
     codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD;
+    codecs[ncodecs++] = HFP_AUDIO_CODEC_MSBC;
 
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs,
                                           DBUS_TYPE_INVALID));
@@ -611,7 +619,7 @@  static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage
 
     card->connecting = false;
 
-    if (!card || codec != HFP_AUDIO_CODEC_CVSD || card->fd >= 0) {
+    if (!card || (codec != HFP_AUDIO_CODEC_CVSD && codec != HFP_AUDIO_CODEC_MSBC) || card->fd >= 0) {
         pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d)", path, fd, codec);
         pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call"));
         shutdown(fd, SHUT_RDWR);
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index 9dbdca3..a404709 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -57,6 +57,9 @@  PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE("path=<device object path>"
                 "autodetect_mtu=<boolean>");
 
+#define HFP_AUDIO_CODEC_CVSD    0x01
+#define HFP_AUDIO_CODEC_MSBC    0x02
+
 #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_PLAYBACK_SCO  (25 * PA_USEC_PER_MSEC)
 #define FIXED_LATENCY_RECORD_A2DP   (25 * PA_USEC_PER_MSEC)
@@ -106,6 +109,28 @@  typedef struct sbc_info {
     size_t buffer_size;                  /* Size of the buffer */
 } sbc_info_t;
 
+struct msbc_parser {
+    int len;
+    uint8_t buffer[60];
+};
+
+/* This structure to be moved to libsbc in the future */
+typedef struct msbc_info {
+    bool msbc_initialized;               /* Keep track if the encoder is initialized */
+    sbc_t sbcenc;                        /* Encoder data */
+    uint8_t *ebuffer;                    /* Codec transfer buffer */
+    size_t ebuffer_size;                 /* Size of the buffer */
+    size_t ebuffer_start;                /* start of encoding data */
+    size_t ebuffer_end;                  /* end of encoding data */
+
+    struct msbc_parser parser;           /* mSBC parser for concatenating frames */
+    sbc_t sbcdec;                        /* Decoder data */
+
+    size_t msbc_frame_size;
+    size_t decoded_frame_size;
+
+} msbc_info_t;
+
 struct userdata {
     pa_module *module;
     pa_core *core;
@@ -147,6 +172,7 @@  struct userdata {
     pa_memchunk write_memchunk;
     pa_sample_spec sample_spec;
     struct sbc_info sbc_info;
+    struct msbc_info msbc_info;
 };
 
 typedef enum pa_bluetooth_form_factor {
@@ -251,6 +277,215 @@  static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir
 }
 
 /* Run from IO thread */
+static void msbc_parser_reset(struct msbc_parser *p) {
+    p->len = 0;
+}
+
+/* Run from IO thread */
+static int msbc_state_machine(struct msbc_parser *p, uint8_t byte) {
+    pa_assert(p->len < 60);
+
+    switch (p->len) {
+    case 0:
+        if (byte == 0x01)
+            goto copy;
+        return 0;
+    case 1:
+        if (byte == 0x08 || byte == 0x38 || byte == 0xC8 || byte == 0xF8)
+            goto copy;
+        break;
+    case 2:
+        if (byte == 0xAD)
+            goto copy;
+        break;
+    case 3:
+        if (byte == 0x00)
+            goto copy;
+        break;
+    case 4:
+        if (byte == 0x00)
+            goto copy;
+        break;
+    default:
+        goto copy;
+    }
+
+    p->len = 0;
+    return 0;
+copy:
+    p->buffer[p->len] = byte;
+    p->len ++;
+
+    return p->len;
+}
+
+/* Run from IO thread */
+static size_t msbc_parse(sbc_t *sbcdec, struct msbc_parser *p, uint8_t *data, int len, uint8_t *out, int outlen, int *bytes) {
+    size_t totalwritten = 0;
+    size_t written = 0;
+    int i;
+    *bytes = 0;
+
+    for (i = 0; i < len; i++) {
+        if (msbc_state_machine(p, data[i]) == 60) {
+            int decoded = 0;
+
+            /* Decode the recived data from the socket */
+            decoded = sbc_decode(sbcdec,
+                                 p->buffer + 2, p->len - 2 - 1,
+                                 out, outlen,
+                                 &written);
+
+            if (decoded > 0) {
+                totalwritten += written;
+                *bytes += decoded;
+            } else {
+                pa_log_debug("Error while decoding: %d\n", decoded);
+            }
+            msbc_parser_reset(p);
+        }
+    }
+    return totalwritten;
+}
+
+/* Run from IO thread */
+static void hsp_prepare_buffer(struct userdata *u) {
+
+    pa_assert(u);
+
+    /* Initialize sbc codec if not already done */
+    if (!u->msbc_info.msbc_initialized) {
+        sbc_init_msbc(&u->msbc_info.sbcenc, 0);
+        sbc_init_msbc(&u->msbc_info.sbcdec, 0);
+        u->msbc_info.msbc_frame_size = 2 + sbc_get_frame_length(&u->msbc_info.sbcenc) + 1;
+        u->msbc_info.decoded_frame_size = sbc_get_codesize(&u->msbc_info.sbcenc);
+        u->msbc_info.msbc_initialized = 1;
+        msbc_parser_reset(&u->msbc_info.parser);
+    }
+
+    /* Allocate a buffer for encoding, and a tmp buffer for sending */
+    if (u->msbc_info.ebuffer_size < u->msbc_info.msbc_frame_size) {
+        pa_xfree(u->msbc_info.ebuffer);
+        u->msbc_info.ebuffer_size = u->msbc_info.msbc_frame_size * 4; /* 5 * 48 = 10 * 24 = 4 * 60 */
+        u->msbc_info.ebuffer = pa_xmalloc(u->msbc_info.ebuffer_size);
+        u->msbc_info.ebuffer_start = 0;
+        u->msbc_info.ebuffer_end = 0;
+    }
+}
+
+static const char sntable[4] = { 0x08, 0x38, 0xC8, 0xF8 };
+
+/* Run from IO thread */
+static int sco_process_render_msbc(struct userdata *u) {
+
+    int ret = 0;
+    size_t to_write, to_encode;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+    pa_assert(u->sink);
+
+    hsp_prepare_buffer(u);
+
+    /* First, render some data */
+    if (!u->write_memchunk.memblock)
+        pa_sink_render_full(u->sink, u->msbc_info.decoded_frame_size, &u->write_memchunk);
+
+    for (;;) {
+        int l = 0;
+        bool wrote = false;
+        const void *p;
+        void *d;
+        ssize_t written = 0;
+        ssize_t encoded;
+        uint8_t *h2 = u->msbc_info.ebuffer + u->msbc_info.ebuffer_end;
+        static int sn = 0;
+
+        /* Now write that data to the socket. The socket is of type
+         * SEQPACKET, and we generated the data of the MTU size, so this
+         * should just work. */
+
+        p = (const uint8_t *) pa_memblock_acquire_chunk(&u->write_memchunk);
+        to_encode = u->write_memchunk.length;
+
+        d = ((uint8_t *)u->msbc_info.ebuffer) + u->msbc_info.ebuffer_end + 2;
+        to_write = u->msbc_info.ebuffer_size - u->msbc_info.ebuffer_end - 2;
+
+        h2[0] = 0x01;
+        h2[1] = sntable[sn];
+        h2[59] = 0x00;
+        sn = (sn + 1) % 4;
+
+        pa_assert(u->msbc_info.ebuffer_end + u->msbc_info.msbc_frame_size <= u->msbc_info.ebuffer_size);
+
+        encoded = sbc_encode(&u->msbc_info.sbcenc, p, to_encode, d, to_write, &written);
+
+        if (PA_UNLIKELY(encoded <= 0)) {
+            pa_log_error("MSBC encoding error (%li)", (long) encoded);
+            pa_memblock_release(u->write_memchunk.memblock);
+            return -1;
+        }
+
+        written += 2 /* H2 */ + 1 /* 0x00 */;
+        pa_assert((size_t)written == u->msbc_info.msbc_frame_size);
+        u->msbc_info.ebuffer_end += written;
+
+        /* Send MTU bytes of it, if there is more it will send later */
+        while (u->msbc_info.ebuffer_start + u->write_link_mtu <= u->msbc_info.ebuffer_end) {
+            l = pa_write(u->stream_fd,
+                         u->msbc_info.ebuffer + u->msbc_info.ebuffer_start,
+                         u->write_link_mtu,
+                         &u->stream_write_type);
+
+            wrote = true;
+            if (l <= 0) {
+                pa_log_debug("Error while writing: l %d, errno %d", l, errno);
+                break;
+            }
+
+            u->msbc_info.ebuffer_start += l;
+            if (u->msbc_info.ebuffer_start >= u->msbc_info.ebuffer_end)
+                u->msbc_info.ebuffer_start = u->msbc_info.ebuffer_end = 0;
+        }
+
+        pa_memblock_release(u->write_memchunk.memblock);
+
+        if (wrote && l < 0) {
+
+            if (errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (errno == EAGAIN)
+                /* Hmm, apparently the socket was not writable, give up for now */
+                break;
+
+            pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno));
+            ret = -1;
+            break;
+        }
+
+        if ((size_t) l != (size_t)u->write_link_mtu) {
+            pa_log_error("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
+                        (unsigned long long) l,
+                        (unsigned long long) u->write_link_mtu);
+            ret = -1;
+            break;
+        }
+
+        u->write_index += (uint64_t) u->write_memchunk.length;
+
+        pa_memblock_unref(u->write_memchunk.memblock);
+        pa_memchunk_reset(&u->write_memchunk);
+
+        ret = 1;
+        break;
+    }
+
+    return ret;
+}
+
+/* Run from IO thread */
 static int sco_process_render(struct userdata *u) {
     ssize_t l;
     pa_memchunk memchunk;
@@ -318,6 +553,108 @@  static int sco_process_render(struct userdata *u) {
 }
 
 /* Run from IO thread */
+static int sco_process_push_msbc(struct userdata *u) {
+
+    int ret = 0;
+    pa_memchunk memchunk;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT ||
+                u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY);
+    pa_assert(u->source);
+    pa_assert(u->read_smoother);
+
+    /*Prepare the buffer before allocating memory*/
+    hsp_prepare_buffer(u);
+
+    u->read_block_size = u->msbc_info.decoded_frame_size;
+    memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
+    memchunk.index = memchunk.length = 0;
+
+    for (;;) {
+        ssize_t l;
+        void *p;
+        struct msghdr m;
+        struct cmsghdr *cm;
+        uint8_t aux[1024];
+        struct iovec iov;
+        bool found_tstamp = false;
+        pa_usec_t tstamp;
+        int decoded = 0;
+        size_t written;
+        uint8_t *tmpbuf = pa_xmalloc(u->read_link_mtu);
+
+        pa_zero(m);
+        pa_zero(aux);
+        pa_zero(iov);
+
+        m.msg_iov = &iov;
+        m.msg_iovlen = 1;
+        m.msg_control = aux;
+        m.msg_controllen = sizeof(aux);
+        iov.iov_base = tmpbuf;
+        iov.iov_len = u->read_link_mtu;
+
+        /* Receive data from the socket */
+        l = recvmsg(u->stream_fd, &m, 0);
+
+        if (l <= 0) {
+
+            if (l < 0 && errno == EINTR)
+                /* Retry right away if we got interrupted */
+                continue;
+
+            else if (l < 0 && errno == EAGAIN)
+                /* Hmm, apparently the socket was not readable, give up for now. */
+                break;
+
+            pa_log_error("Failed to read data from SCO socket: %s", l < 0 ? pa_cstrerror(errno) : "EOF");
+            ret = -1;
+            break;
+        }
+
+        p = pa_memblock_acquire(memchunk.memblock);
+        written = msbc_parse(&u->msbc_info.sbcdec, &u->msbc_info.parser, tmpbuf, l, p, pa_memblock_get_length(memchunk.memblock), &decoded);
+        pa_memblock_release(memchunk.memblock);
+
+        pa_xfree(tmpbuf);
+
+        memchunk.length = (size_t) written;
+
+        u->read_index += (uint64_t) decoded;
+
+        for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) {
+            if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
+                struct timeval *tv = (struct timeval*) CMSG_DATA(cm);
+                pa_rtclock_from_wallclock(tv);
+                tstamp = pa_timeval_load(tv);
+                found_tstamp = true;
+                break;
+            }
+        }
+
+        if (!found_tstamp) {
+            pa_log_warn("Couldn't find SO_TIMESTAMP data in auxiliary recvmsg() data!");
+            tstamp = pa_rtclock_now();
+        }
+
+        pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec));
+        pa_smoother_resume(u->read_smoother, tstamp, true);
+
+        if (memchunk.length > 0) {
+            pa_source_post(u->source, &memchunk);
+        }
+
+        ret = decoded;
+        break;
+    }
+
+    pa_memblock_unref(memchunk.memblock);
+
+    return ret;
+}
+
+/* Run from IO thread */
 static int sco_process_push(struct userdata *u) {
     ssize_t l;
     pa_memchunk memchunk;
@@ -1294,7 +1631,7 @@  static void transport_config(struct userdata *u) {
     if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
         u->sample_spec.format = PA_SAMPLE_S16LE;
         u->sample_spec.channels = 1;
-        u->sample_spec.rate = 8000;
+        u->sample_spec.rate = (u->transport->codec == HFP_AUDIO_CODEC_CVSD) ? 8000 : 16000;
     } else {
         sbc_info_t *sbc_info = &u->sbc_info;
         a2dp_sbc_t *config;
@@ -1481,8 +1818,16 @@  static int write_block(struct userdata *u) {
         if ((n_written = a2dp_process_render(u)) < 0)
             return -1;
     } else {
-        if ((n_written = sco_process_render(u)) < 0)
-            return -1;
+        if (u->transport->codec == HFP_AUDIO_CODEC_CVSD) {
+            if ((n_written = sco_process_render(u)) < 0)
+                return -1;
+        } else if (u->transport->codec == HFP_AUDIO_CODEC_MSBC) {
+            if ((n_written = sco_process_render_msbc(u)) < 0)
+                return -1;
+        } else {
+            n_written = -1;
+            pa_log("Invalid codec for encoding: %d", u->transport->codec);
+        }
     }
 
     return n_written;
@@ -1550,11 +1895,21 @@  static void thread_func(void *userdata) {
                 /* If we got woken up by POLLIN let's do some reading */
                 if (pollfd->revents & POLLIN) {
                     int n_read;
-
                     if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
                         n_read = a2dp_process_push(u);
-                    else
-                        n_read = sco_process_push(u);
+                    else {
+                         switch(u->transport->codec) {
+                            case HFP_AUDIO_CODEC_CVSD:
+                                n_read = sco_process_push(u);
+                                break;
+                            case HFP_AUDIO_CODEC_MSBC:
+                                n_read = sco_process_push_msbc(u);
+                                break;
+                            default:
+                                pa_log_error("Invalid codec for encoding %d", u->transport->codec);
+                                n_read = -1;
+                        }
+                    }
 
                     if (n_read < 0)
                         goto fail;
@@ -1604,9 +1959,10 @@  static void thread_func(void *userdata) {
                         if (blocks_to_write > 0)
                             writable = false;
                     }
+                }
 
-                /* There is no source, we have to use the system clock for timing */
-                } else {
+                /* There is no source or audio codec was MSBC, we have to use the system clock for timing */
+                if ((u->transport->codec == HFP_AUDIO_CODEC_MSBC) || !have_source){
                     bool have_written = false;
                     pa_usec_t time_passed = 0;
                     pa_usec_t audio_sent = 0;

Comments

James Bottomley Aug. 20, 2018, 2:41 p.m.
On Mon, 2018-08-20 at 19:19 +0530, Sathish Narasimman wrote:
> From: Sathish Narasimman <sathish.narasimman@intel.com>
> 
> mSBC-encoded streams for HFP. The Wide Band Speech(WBS) encoding and
> decoding
> is implemeted with this patch. This patch was refered from original
> patch of Joao Paula Rechi Vita and was verified with the supported
> bluetooth controller.

Which headset did you test this with?  When I try it with an LG 900 I
get a huge amount of chop which shreds the audio quality.  For this
headset the packet size (MTU) still needs to be set at 48 on both the
send and receive side otherwise the audio doesn't work at all.

What seems to be happening is that the LG insists on running the eSCO
links at full speed but the encoded packets only take about half the
bandwidth, so on the headset receiving side, we get about half as many
mSBC encoded packets with the rest being zero padded.

The problem occurs because on the receive side you count mSBC packets,
so the reader/writer thread you try to send 1 mSBC encoded packet for
every 1 mSBC packet you receive.

But in this send loop:

> +        /* Send MTU bytes of it, if there is more it will send later
> */
> +        while (u->msbc_info.ebuffer_start + u->write_link_mtu <= u-
> >msbc_info.ebuffer_end) {
> +            l = pa_write(u->stream_fd,
> +                         u->msbc_info.ebuffer + u-
> >msbc_info.ebuffer_start,
> +                         u->write_link_mtu,
> +                         &u->stream_write_type);
> +
> +            wrote = true;
> +            if (l <= 0) {
> +                pa_log_debug("Error while writing: l %d, errno %d",
> l, errno);
> +                break;
> +            }
> +
> +            u->msbc_info.ebuffer_start += l;
> +            if (u->msbc_info.ebuffer_start >= u-
> >msbc_info.ebuffer_end)
> +                u->msbc_info.ebuffer_start = u-
> >msbc_info.ebuffer_end = 0;
> +        }
> +
> +        pa_memblock_release(u->write_memchunk.memblock);
> +
> +        if (wrote && l < 0) {
> +
> +            if (errno == EINTR)
> +                /* Retry right away if we got interrupted */
> +                continue;
> +
> +            else if (errno == EAGAIN)
> +                /* Hmm, apparently the socket was not writable, give
> up for now */
> +                break;
> +
> +            pa_log_error("Failed to write data to SCO socket: %s",
> pa_cstrerror(errno));
> +            ret = -1;
> +            break;
> +        }
> +
> +        if ((size_t) l != (size_t)u->write_link_mtu) {
> +            pa_log_error("Wrote memory block to socket only
> partially! %llu written, wanted to write %llu.",
> +                        (unsigned long long) l,
> +                        (unsigned long long) u->write_link_mtu);
> +            ret = -1;
> +            break;
> +        }
> +
> +        u->write_index += (uint64_t) u->write_memchunk.length;
> +
> +        pa_memblock_unref(u->write_memchunk.memblock);
> +        pa_memchunk_reset(&u->write_memchunk);
> +
> +        ret = 1;
> +        break;
> +    }

Because the mtu is 48 and the mSBC encode size is 60, the transmission
stops after 48 bytes, we now wait for a new mSBC packet to be received,
so we miss the next eSCO transmission window and the headset fills in
with zeros causing the packet to be chopped and the next one to be
discarded as invalid.

I suspect the only way to get mSBC to work for this type of headset is
to count actual received packets, always to transmit full 60 byte mSBC
packets in adjacent frames and to pad with zeros if we're not ready.

James
Sathish Narasimman Aug. 21, 2018, 5:46 a.m.
Hi James,

On Mon, Aug 20, 2018 at 8:19 PM James Bottomley <
James.Bottomley@hansenpartnership.com> wrote:

> On Mon, 2018-08-20 at 19:19 +0530, Sathish Narasimman wrote:
> > From: Sathish Narasimman <sathish.narasimman@intel.com>
> >
> > mSBC-encoded streams for HFP. The Wide Band Speech(WBS) encoding and
> > decoding
> > is implemeted with this patch. This patch was refered from original
> > patch of Joao Paula Rechi Vita and was verified with the supported
> > bluetooth controller.
>
> Which headset did you test this with?  When I try it with an LG 900 I
> get a huge amount of chop which shreds the audio quality.  For this
> headset the packet size (MTU) still needs to be set at 48 on both the
> send and receive side otherwise the audio doesn't work at all.
>
I used Jabra EasyGo GNM-OTE4 bluetooth headset.
This patch is refered w.r.t to USB transport.
For USB transport - As per core spec 5, vol 4, Part B Table 2.1(this was
the only reference in core spec for USB transport- please correct me if i
am wrong)
for msbc voice channel it should be 63bytes along with Usb header. So the
host
should send 60 bytes of msbc audio.

>
> What seems to be happening is that the LG insists on running the eSCO
> links at full speed but the encoded packets only take about half the
> bandwidth, so on the headset receiving side, we get about half as many
> mSBC encoded packets with the rest being zero padded.
>
> The problem occurs because on the receive side you count mSBC packets,
> so the reader/writer thread you try to send 1 mSBC encoded packet for
> every 1 mSBC packet you receive.
>
> But in this send loop:
>
> > +        /* Send MTU bytes of it, if there is more it will send later
> > */
> > +        while (u->msbc_info.ebuffer_start + u->write_link_mtu <= u-
> > >msbc_info.ebuffer_end) {
> > +            l = pa_write(u->stream_fd,
> > +                         u->msbc_info.ebuffer + u-
> > >msbc_info.ebuffer_start,
> > +                         u->write_link_mtu,
> > +                         &u->stream_write_type);
> > +
> > +            wrote = true;
> > +            if (l <= 0) {
> > +                pa_log_debug("Error while writing: l %d, errno %d",
> > l, errno);
> > +                break;
> > +            }
> > +
> > +            u->msbc_info.ebuffer_start += l;
> > +            if (u->msbc_info.ebuffer_start >= u-
> > >msbc_info.ebuffer_end)
> > +                u->msbc_info.ebuffer_start = u-
> > >msbc_info.ebuffer_end = 0;
> > +        }
> > +
> > +        pa_memblock_release(u->write_memchunk.memblock);
> > +
> > +        if (wrote && l < 0) {
> > +
> > +            if (errno == EINTR)
> > +                /* Retry right away if we got interrupted */
> > +                continue;
> > +
> > +            else if (errno == EAGAIN)
> > +                /* Hmm, apparently the socket was not writable, give
> > up for now */
> > +                break;
> > +
> > +            pa_log_error("Failed to write data to SCO socket: %s",
> > pa_cstrerror(errno));
> > +            ret = -1;
> > +            break;
> > +        }
> > +
> > +        if ((size_t) l != (size_t)u->write_link_mtu) {
> > +            pa_log_error("Wrote memory block to socket only
> > partially! %llu written, wanted to write %llu.",
> > +                        (unsigned long long) l,
> > +                        (unsigned long long) u->write_link_mtu);
> > +            ret = -1;
> > +            break;
> > +        }
> > +
> > +        u->write_index += (uint64_t) u->write_memchunk.length;
> > +
> > +        pa_memblock_unref(u->write_memchunk.memblock);
> > +        pa_memchunk_reset(&u->write_memchunk);
> > +
> > +        ret = 1;
> > +        break;
> > +    }
>
> Because the mtu is 48 and the mSBC encode size is 60, the transmission
> stops after 48 bytes, we now wait for a new mSBC packet to be received,
> so we miss the next eSCO transmission window and the headset fills in
> with zeros causing the packet to be chopped and the next one to be
> discarded as invalid.
>
> I suspect the only way to get mSBC to work for this type of headset is
> to count actual received packets, always to transmit full 60 byte mSBC
> packets in adjacent frames and to pad with zeros if we're not ready.
>
I tried to get the MTU negotiation from the kernel.
Though getsockopt was not implemented to provide the required MTU from the
BT kernel.
I tried to implement that part to get the right MTU from the BT kernel.
But the problem here is, the USB  transport gets the right MTU from ALT
setting.
which happens after the  successfull sco connection(exactly during accept
connection or sco_accept).
So during hf_audio_agent_transport_acquire call we are unable to get the
right MTU at this function in backend-ofono.c file.
This is the reason i had planned to hard code it for now. This patch is
purely verified with USB transport

>
> James
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss


Thansk,
Sathish N