[v2] bluetooth: Wideband speech implementaion

Submitted by Sathish Narasimman on Aug. 16, 2018, 9:06 a.m.

Details

Message ID 1534410374-658-1-git-send-email-sathish.narasimman@intel.com
State New
Headers show
Series "bluetooth: Wideband speech implementaion" ( rev: 2 ) in PulseAudio

Not browsing as part of any series.

Commit Message

Sathish Narasimman Aug. 16, 2018, 9:06 a.m.
mSBC-encoded streams for HFP. The wideband speec 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

Hi,

Any comment over this patch?
may I know the blocking?


On Thu, Aug 16, 2018 at 2:37 PM Sathish Narasimman <nsathish41@gmail.com>
wrote:

> mSBC-encoded streams for HFP. The wideband speec 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(-)
>
> 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;
> --
> 2.7.4
>
>
Hi!

On Thu, Aug 16, 2018 at 11:08 AM Sathish Narasimman
<nsathish41@gmail.com> wrote:
>
> mSBC-encoded streams for HFP. The wideband speec 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>
> ---

Small note; please add below tag as the email body __first line__:

    From: Sathish Narasimman <sathish.narasimman@intel.com>

Then an empty line, then the normal commit log, then the S-o-b.

Otherwise this patch will be credited to your personal gmail account
rather than Intel, which given the S-o-b seems not to be what is
intended.

Thanks,

--
Ahmed S. Darwish
http://darwish.chasingpointers.com
hi,

Sorry, I am getting confused here. the format I understood was
In the commit format, may I follow as below format. Please let me know if
it is correct. If so, i will submit the v3
------------------------------------------------------------------

bluetooth: wideband speech implementation

From: Sathish Narasimman <sathish.narasimman@intel.com>

mSBC-encoded streams for HFP. The wideband speec 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>
 -------------------------------------------------------------------------------------------------------------------------

On Mon, Aug 20, 2018 at 6:21 PM Ahmed S. Darwish <darwish.07@gmail.com>
wrote:

> Hi!
>
> On Thu, Aug 16, 2018 at 11:08 AM Sathish Narasimman
> <nsathish41@gmail.com> wrote:
> >
> > mSBC-encoded streams for HFP. The wideband speec 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>
> > ---
>
> Small note; please add below tag as the email body __first line__:
>
>     From: Sathish Narasimman <sathish.narasimman@intel.com>
>
> Then an empty line, then the normal commit log, then the S-o-b.
>
> Otherwise this patch will be credited to your personal gmail account
> rather than Intel, which given the S-o-b seems not to be what is
> intended.
>
> Thanks,
>
> --
> Ahmed S. Darwish
> http://darwish.chasingpointers.com
>
On Mon, Aug 20, 2018 at 3:16 PM Sathish Narasimman <nsathish41@gmail.com> wrote:
>
> hi,
>
> Sorry, I am getting confused here. the format I understood was
> In the commit format, may I follow as below format. Please let me know if it is correct. If so, i will submit the v3

Please don't top-post:

    http://www.idallen.com/topposting.html

> ------------------------------------------------------------------
>
> bluetooth: wideband speech implementation
>

Yes, that would be the subject line.

> From: Sathish Narasimman <sathish.narasimman@intel.com>
>
> mSBC-encoded streams for HFP. The wideband speec 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>

Yes, that would be the body, along with three dashes '---' after
the S-o-b line.

Thanks,
On Mon, Aug 20, 2018 at 7:06 PM Ahmed S. Darwish <darwish.07@gmail.com>
wrote:

> On Mon, Aug 20, 2018 at 3:16 PM Sathish Narasimman <nsathish41@gmail.com>
> wrote:
> >
> > hi,
> >
> > Sorry, I am getting confused here. the format I understood was
> > In the commit format, may I follow as below format. Please let me know
> if it is correct. If so, i will submit the v3
>
> Please don't top-post:
>
>     http://www.idallen.com/topposting.html
>
> > ------------------------------------------------------------------
> >
> > bluetooth: wideband speech implementation
> >
>
> Yes, that would be the subject line.
>
> > From: Sathish Narasimman <sathish.narasimman@intel.com>
> >
> > mSBC-encoded streams for HFP. The wideband speec 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>
>
> Yes, that would be the body, along with three dashes '---' after
> the S-o-b line.
>
> Thanks,
>
> --
> Darwish
> http://darwish.chasingpointers.com


Thanks for the guide. I will submit v3
So, these rules apply only if it is s-o-b?

If so. It would be helpful if the PA link is updated .
https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/

Thanks,
Sathish N
Hi,

On Mon, Aug 20, 2018 at 3:44 PM Sathish Narasimman <nsathish41@gmail.com> wrote:
>
> On Mon, Aug 20, 2018 at 7:06 PM Ahmed S. Darwish <darwish.07@gmail.com> wrote:
>>
>> On Mon, Aug 20, 2018 at 3:16 PM Sathish Narasimman <nsathish41@gmail.com> wrote:
>> >
>> > hi,
>> >
>> > Sorry, I am getting confused here. the format I understood was
>> > In the commit format, may I follow as below format. Please let me know if it is correct. If so, i will submit the v3
>>
>> Please don't top-post:
>>
>>     http://www.idallen.com/topposting.html
>>
>> > ------------------------------------------------------------------
>> >
>> > bluetooth: wideband speech implementation
>> >
>>
>> Yes, that would be the subject line.
>>
>> > From: Sathish Narasimman <sathish.narasimman@intel.com>
>> >
>> > mSBC-encoded streams for HFP. The wideband speec 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>
>>
>> Yes, that would be the body, along with three dashes '---' after
>> the S-o-b line.
>>
>
> Thanks for the guide. I will submit v3
> So, these rules apply only if it is s-o-b?
>
> If so. It would be helpful if the PA link is updated .
> https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/
>

These rules only apply because you're sending an employer-sponsored
work (Intel) from a personal gmail account, which is not the common
case -- no need to modify the wiki here.

git-am assigns the commit author and commit title from the e-mail
_headers_ "From: " and "Subject: " lines. But since we want the
commit author (Intel) to be different from the _headers_ "From: "
line (personal gmail), we override it in the body.

From git-am(1) manpage:

    The commit author name is taken from the "From: " line of the
    message, and commit author date is taken from the "Date: "
    line of the message. The "Subject: " line is used as the title
    of the commit, after stripping common prefix "[PATCH <anything>]".

    The "Subject: " line is supposed to concisely describe what the
    commit is about in one line of text.

    "From: " and "Subject: " lines starting the body override
    the respective commit author name and title values taken from
    the headers.

Notice the last paragraph.

Good luck,