bluetooth: Implement vendor codec supporting logic 63/186963/7 accepted/tizen/unified/20181113.163406 submit/tizen/20181109.022710
authorWootak Jung <wootak.jung@samsung.com>
Fri, 17 Aug 2018 00:49:06 +0000 (09:49 +0900)
committerWootak Jung <wootak.jung@samsung.com>
Thu, 8 Nov 2018 07:58:47 +0000 (16:58 +0900)
Introduce new vendor codec module and supporting logic.
vendor codec module should be named with
'module-a2dp-vendor-codec' as a prefix.

Change-Id: I7144353695633228f6d695295cafa57fd3ccb0d6

configure.ac
packaging/pulseaudio.spec
src/Makefile.am
src/modules/bluetooth/a2dp-codecs.h
src/modules/bluetooth/bluez5-util.c
src/modules/bluetooth/bluez5-util.h
src/modules/bluetooth/module-a2dp-vendor-codec-aptx.c [new file with mode: 0644]
src/modules/bluetooth/module-a2dp-vendor-codec.h [new file with mode: 0644]
src/modules/bluetooth/module-bluetooth-discover.c
src/modules/bluetooth/module-bluez5-device.c
src/modules/bluetooth/module-bluez5-discover.c

index e484187..865603a 100644 (file)
@@ -1067,6 +1067,11 @@ AS_IF([test "x$HAVE_BLUEZ_4" = "x1" || test "x$HAVE_BLUEZ_5" = "x1"], HAVE_BLUEZ
 AC_SUBST(HAVE_BLUEZ)
 AM_CONDITIONAL([HAVE_BLUEZ], [test "x$HAVE_BLUEZ" = x1])
 
+## HAVE_BLUEZ_VENDOR_CODEC ##
+AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], HAVE_BLUEZ_VENDOR_CODEC=1, HAVE_BLUEZ_VENDOR_CODEC=0)
+AC_SUBST(HAVE_BLUEZ_VENDOR_CODEC)
+AM_CONDITIONAL([HAVE_BLUEZ_VENDOR_CODEC], [test "x$HAVE_BLUEZ_VENDOR_CODEC" = x1])
+
 ## Bluetooth Headset profiles backend ##
 
 AC_ARG_ENABLE([bluez5-ofono-headset],
@@ -1735,6 +1740,7 @@ AS_IF([test "x$HAVE_SYSTEMD_LOGIN" = "x1"], ENABLE_SYSTEMD_LOGIN=yes, ENABLE_SYS
 AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE_SYSTEMD_JOURNAL=no)
 AS_IF([test "x$HAVE_BLUEZ_4" = "x1"], ENABLE_BLUEZ_4=yes, ENABLE_BLUEZ_4=no)
 AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], ENABLE_BLUEZ_5=yes, ENABLE_BLUEZ_5=no)
+AS_IF([test "x$HAVE_BLUEZ_VENDOR_CODEC" = "x1"], HAVE_BLUEZ_VENDOR_CODEC=yes, HAVE_BLUEZ_VENDOR_CODEC=no)
 AS_IF([test "x$HAVE_BLUEZ_5_OFONO_HEADSET" = "x1"], ENABLE_BLUEZ_5_OFONO_HEADSET=yes, ENABLE_BLUEZ_5_OFONO_HEADSET=no)
 AS_IF([test "x$HAVE_BLUEZ_5_NATIVE_HEADSET" = "x1"], ENABLE_BLUEZ_5_NATIVE_HEADSET=yes, ENABLE_BLUEZ_5_NATIVE_HEADSET=no)
 AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no)
index b67c578..d309c7a 100644 (file)
@@ -411,6 +411,7 @@ setcap -r /usr/bin/pulseaudio
 %{_libdir}/pulse-%{version}/modules/module-bluetooth-policy.so
 %{_libdir}/pulse-%{version}/modules/module-bluez5-discover.so
 %{_libdir}/pulse-%{version}/modules/module-bluez5-device.so
+%{_libdir}/pulse-%{version}/modules/module-a2dp-vendor-codec-aptx.so
 %{_libdir}/pulse-%{version}/modules/libbluez5-util.so
 %endif
 
index 725f137..b7d2140 100644 (file)
@@ -1662,6 +1662,11 @@ modlibexec_LTLIBRARIES += \
                module-bluez5-device.la
 endif
 
+if HAVE_BLUEZ_VENDOR_CODEC
+modlibexec_LTLIBRARIES += \
+               module-a2dp-vendor-codec-aptx.la
+endif
+
 # RAOP depends on RTP, and we don't support RTP on Windows, see comment at
 # librtp.la above.
 if !OS_IS_WIN32
@@ -1785,6 +1790,11 @@ SYMDEF_FILES += \
                module-esound-sink-symdef.h
 endif
 
+if HAVE_BLUEZ_VENDOR_CODEC
+SYMDEF_FILES += \
+               module-a2dp-vendor-aptx-symdef.h
+endif
+
 EXTRA_DIST += $(SYMDEF_FILES)
 BUILT_SOURCES += $(SYMDEF_FILES) builddirs
 
@@ -2408,6 +2418,16 @@ libbluez5_util_la_LDFLAGS = -avoid-version
 libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
 libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 
+if HAVE_BLUEZ_VENDOR_CODEC
+module_a2dp_vendor_codec_aptx_la_SOURCES = \
+               modules/bluetooth/module-a2dp-vendor-codec-aptx.c \
+               modules/bluetooth/module-a2dp-vendor-codec.h \
+               modules/bluetooth/a2dp-codecs.h
+module_a2dp_vendor_codec_aptx_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_a2dp_vendor_codec_aptx_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluez5-util.la
+module_a2dp_vendor_codec_aptx_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+endif
+
 module_bluez5_discover_la_SOURCES = modules/bluetooth/module-bluez5-discover.c
 module_bluez5_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_bluez5_discover_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) libbluez5-util.la
index a86c3f0..9a1b125 100644 (file)
@@ -26,7 +26,7 @@
 #define A2DP_CODEC_MPEG24              0x02
 #define A2DP_CODEC_ATRAC               0x03
 #ifdef __TIZEN_BT__
-#define BLUETOOTH_APTX_SUPPORT         1
+/* #define BLUETOOTH_APTX_SUPPORT              1 */
 #define A2DP_CODEC_VENDOR               0xFF
 #endif
 #define A2DP_CODEC_NON_A2DP            0xFF
index 0ec4ed9..37605ff 100644 (file)
@@ -36,9 +36,6 @@
 #include "a2dp-codecs.h"
 
 #include "bluez5-util.h"
-#ifdef BLUETOOTH_APTX_SUPPORT
-#include <dlfcn.h>
-#endif
 
 #define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC)
 
@@ -102,56 +99,6 @@ struct pa_bluetooth_discovery {
     PA_LLIST_HEAD(pa_dbus_pending, pending);
 };
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-static void *aptx_handle = NULL;
-
-int pa_unload_aptx(void)
-{
-    if (aptx_handle == NULL) {
-        pa_log_warn("Unable to unload apt-X library");
-        return -1;
-    }
-
-    dlclose(aptx_handle);
-    aptx_handle = NULL;
-
-    pa_log_debug("unloaded apt-X library successfully");
-    return 0;
-}
-
-int pa_load_aptx(const char *aptx_lib_name)
-{
-    char* lib_path = NULL ;
-
-    if(aptx_lib_name == NULL)
-        return -1;
-
-    lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, aptx_lib_name);
-
-    if (!lib_path)
-        return -1;
-
-    pa_log_info("aptx_lib_path = [%s]", lib_path);
-
-    aptx_handle = dlopen(lib_path, RTLD_LAZY);
-    if (aptx_handle == NULL) {
-        pa_log_warn("Unable to load apt-X library [%s]", dlerror());
-        pa_xfree(lib_path);
-        return -1;
-    }
-
-    pa_log_debug("loaded apt-X library successfully");
-    pa_xfree(lib_path);
-
-    return 0;
-}
-
-void* pa_aptx_get_handle(void)
-{
-    return aptx_handle;
-}
-#endif
-
 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
                                                                   DBusPendingCallNotifyFunction func, void *call_data) {
     pa_dbus_pending *p;
@@ -1094,8 +1041,7 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa
             register_endpoint(y, path, A2DP_SINK_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK);
 
 #ifdef BLUETOOTH_APTX_SUPPORT
-            if (aptx_handle)
-                register_endpoint(y, path, A2DP_APTX_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
+            register_endpoint(y, path, A2DP_APTX_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
 #endif
         } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
 
index 9b8ed9c..e56befb 100644 (file)
@@ -180,10 +180,14 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y);
 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y);
 void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running);
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-int pa_load_aptx(const char *aptx_lib_name);
-int pa_unload_aptx(void);
-void* pa_aptx_get_handle(void);
 #endif
 
+#ifdef __TIZEN_BT__
+struct pa_bluetooth_vendor_codec_ops {
+    const char *name;
+    bool (*init)();
+    bool (*deinit)();
+    int (*transport_config)(void *data);
+    int (*process_render)(void *data);
+};
 #endif
diff --git a/src/modules/bluetooth/module-a2dp-vendor-codec-aptx.c b/src/modules/bluetooth/module-a2dp-vendor-codec-aptx.c
new file mode 100644 (file)
index 0000000..663c48c
--- /dev/null
@@ -0,0 +1,247 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright (C) 2018 Samsung Electronics Co., Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "module-a2dp-vendor-codec.h"
+
+#define APTX_NEW_NAME "NewAptxEnc"
+#define APTX_ENCODE_NAME "aptxbtenc_encodestereo"
+#define APTX_LIB_NAME "libbtaptx-armv6L.so"
+
+typedef struct {
+    void *aptx_handle;
+    void *(*aptx_new)(short endian);
+    int (*aptx_encode)(void *_state, void *_pcmL, void *_pcmR, void *_buffer);
+    bool aptx_initialized; /* Keep track if the encoder is initialized */
+    void *aptx_data;       /* aptx Codec data */
+} aptx_userdata_t;
+static aptx_userdata_t ud;
+
+static bool aptx_init()
+{
+    char *lib_path = NULL;
+
+    lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, APTX_LIB_NAME);
+    if (!lib_path)
+        return false;
+
+    pa_log_info("lib_path: %s", lib_path);
+
+    ud.aptx_handle = dlopen(lib_path, RTLD_LAZY);
+    if (!ud.aptx_handle) {
+        pa_log_warn("Unable to load aptX library (%s)", dlerror());
+        pa_xfree(lib_path);
+        return false;
+    }
+
+    pa_log_debug("Loaded aptX library successfully");
+    pa_xfree(lib_path);
+
+    ud.aptx_new = (void *(*)(short endian))dlsym(ud.aptx_handle, APTX_NEW_NAME);
+    if (ud.aptx_new) {
+        pa_log_debug("Load Symbol (%s)", APTX_NEW_NAME);
+    } else {
+        pa_log_debug("Fail to Load Symbol (%s)", APTX_NEW_NAME);
+        return false;
+    }
+
+    ud.aptx_encode = (int (*)(void *_state, void *_pcmL, void *_pcmR, void *_buffer))dlsym(ud.aptx_handle, APTX_ENCODE_NAME);
+    if (ud.aptx_encode) {
+        pa_log_debug("Load Symbol (%s)", APTX_ENCODE_NAME);
+    } else {
+        pa_log_debug("Fail to Load Symbol (%s)", APTX_ENCODE_NAME);
+        return false;
+    }
+
+    return true;
+}
+
+static bool aptx_deinit()
+{
+    if (!ud.aptx_handle) {
+        pa_log_warn("Unable to unload aptX");
+        return false;
+    }
+
+    dlclose(ud.aptx_handle);
+    ud.aptx_handle = NULL;
+
+    pa_log_debug("Unloaded aptX successfully");
+    return true;
+}
+
+static int aptx_transport_config(void *data)
+{
+    struct userdata *u = data;
+    const pa_bluetooth_transport *t;
+
+    t = u->transport;
+    pa_assert(t);
+
+    u->sample_spec.format = PA_SAMPLE_S16LE;
+
+    if (!ud.aptx_initialized) {
+#if __BYTE_ORDER==__LITTLE_ENDIAN
+        ud.aptx_data = ud.aptx_new(1);
+#elif __BYTE_ORDER==__BIG_ENDIAN
+        ud.aptx_data = ud.aptx_new(0);
+#else
+#error "Unknown byte order"
+#endif
+        ud.aptx_initialized = true;
+    }
+
+    pa_log_debug("aptX Encoder is initialized!!");
+
+    u->write_block_size = (size_t)(u->write_link_mtu / (size_t)16) * 16 * 4;
+    pa_log_info("aptX parameters block_size (%d), link_mtu (%d)", u->write_block_size, u->write_link_mtu);
+    return 0;
+}
+
+static void aptx_prepare_buffer(struct userdata *u)
+{
+    size_t min_buffer_size = PA_MAX(u->read_link_mtu, u->write_link_mtu);
+
+    pa_assert(u);
+
+    if (u->sbc_info.buffer_size >= min_buffer_size)
+        return;
+
+    u->sbc_info.buffer_size = 2 * min_buffer_size;
+    pa_xfree(u->sbc_info.buffer);
+    u->sbc_info.buffer = pa_xmalloc(u->sbc_info.buffer_size);
+}
+
+/* Run from IO thread */
+#define APTX_PCM_LEN 4
+#define APTX_ENCODE_LEN_MAX 16
+#define APTX_WRITE_LEN_MAX 4
+static int aptx_process_render(void *data)
+{
+    struct userdata *u = data;
+    struct sbc_info *a2dp;
+    size_t nbytes;
+    void *d;
+    const void *p;
+    size_t to_write, to_encode;
+    int ret = 0;
+    int pcmL[APTX_PCM_LEN], pcmR[APTX_PCM_LEN];
+    int i = 0;
+    const uint8_t *mybuffer;
+
+    pa_assert(u);
+    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+    pa_assert(u->sink);
+
+    /* First, render some data */
+    if (!u->write_memchunk.memblock)
+        pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
+
+    pa_assert(u->write_memchunk.length == u->write_block_size);
+
+    aptx_prepare_buffer(u);
+
+    a2dp = &u->sbc_info;
+
+    /* Try to create a packet of the full MTU */
+    p = (const uint8_t *)pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index;
+    to_encode = u->write_memchunk.length;
+
+    d = (uint8_t *)a2dp->buffer;
+    to_write = a2dp->buffer_size;
+
+    while (PA_LIKELY(to_encode > 0 && to_write > 0)) {
+        mybuffer = (uint8_t *)p;
+
+        for (i = 0; i < 4; i += 1) {
+           pcmL[i] = mybuffer[2 * i];
+           pcmR[i] = mybuffer[2 * i + 1];
+        }
+        /* (8 audio samples)16 bytes of audo data encoded to 4 bytes */
+        ud.aptx_encode(ud.aptx_data, pcmL, pcmR, (short *)d);
+
+        pa_assert_fp((size_t)APTX_ENCODE_LEN_MAX <= to_encode);
+        pa_assert_fp((size_t)APTX_WRITE_LEN_MAX <= to_write);
+
+        p = (const uint8_t *)p + APTX_ENCODE_LEN_MAX;
+        to_encode -= APTX_ENCODE_LEN_MAX;
+
+        d = (uint8_t *)d + APTX_WRITE_LEN_MAX;
+        to_write -= APTX_WRITE_LEN_MAX;
+    }
+
+    pa_memblock_release(u->write_memchunk.memblock);
+
+    pa_assert(to_encode == 0);
+
+    PA_ONCE_BEGIN {
+        pa_log_debug("Using APTX encoder implementation");
+    } PA_ONCE_END;
+
+    nbytes = (uint8_t *)d - (uint8_t *)a2dp->buffer;
+
+    for (;;) {
+        ssize_t l;
+
+        l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type);
+
+        pa_assert(l != 0);
+
+        if (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 socket: %s", pa_cstrerror(errno));
+            ret = -1;
+            break;
+        }
+
+        pa_assert((size_t)l <= nbytes);
+
+        if ((size_t)l != nbytes) {
+            pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
+                        (unsigned long long)l,
+                        (unsigned long long)nbytes);
+            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;
+}
+
+struct pa_bluetooth_vendor_codec_ops vendor_codec_ops = {
+    .name = "APTX",
+    .init = aptx_init,
+    .deinit = aptx_deinit,
+    .transport_config = aptx_transport_config,
+    .process_render = aptx_process_render,
+};
diff --git a/src/modules/bluetooth/module-a2dp-vendor-codec.h b/src/modules/bluetooth/module-a2dp-vendor-codec.h
new file mode 100644 (file)
index 0000000..efb592d
--- /dev/null
@@ -0,0 +1,108 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright (C) 2018 Samsung Electronics Co., Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef __MODULE_A2DP_VENDOR_CODEC_H__
+#define __MODULE_A2DP_VENDOR_CODEC_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <sbc/sbc.h>
+#include <dlfcn.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/time-smoother.h>
+
+#include "bluez5-util.h"
+
+typedef struct bluetooth_msg {
+    pa_msgobject parent;
+    pa_card *card;
+} bluetooth_msg;
+
+typedef struct sbc_info {
+    sbc_t sbc;                           /* Codec data */
+    bool sbc_initialized;                /* Keep track if the encoder is initialized */
+    size_t codesize, frame_length;       /* SBC Codesize, frame_length. We simply cache those values here */
+    uint16_t seq_num;                    /* Cumulative packet sequence */
+    uint8_t min_bitpool;
+    uint8_t max_bitpool;
+    void *buffer;                        /* Codec transfer buffer */
+    size_t buffer_size;                  /* Size of the buffer */
+} sbc_info_t;
+
+struct userdata {
+    pa_module *module;
+    pa_core *core;
+
+    pa_hook_slot *device_connection_changed_slot;
+    pa_hook_slot *transport_state_changed_slot;
+    pa_hook_slot *sco_state_changed_slot;
+    pa_hook_slot *transport_speaker_gain_changed_slot;
+    pa_hook_slot *transport_microphone_gain_changed_slot;
+
+    pa_bluetooth_discovery *discovery;
+    pa_bluetooth_device *device;
+    pa_bluetooth_transport *transport;
+    bool transport_acquired;
+    bool stream_setup_done;
+
+    bool transport_suspended_by_remote;
+    pa_card *card;
+    pa_sink *sink;
+    pa_source *source;
+    pa_bluetooth_profile_t profile;
+    char *output_port_name;
+    char *input_port_name;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
+    pa_rtpoll_item *rtpoll_item;
+    bluetooth_msg *msg;
+
+    int stream_fd;
+    int stream_write_type;
+    size_t read_link_mtu;
+    size_t write_link_mtu;
+    size_t read_block_size;
+    size_t write_block_size;
+    uint64_t read_index;
+    uint64_t write_index;
+    pa_usec_t started_at;
+    pa_smoother *read_smoother;
+    pa_memchunk write_memchunk;
+    pa_sample_spec sample_spec;
+    struct sbc_info sbc_info;
+
+    pa_modargs *modargs;
+    struct pa_bluetooth_vendor_codec_ops *selected_ops;
+    pa_proplist *vendor_codec_list;
+    bool is_aptx_supported;
+};
+
+#endif /* __MODULE_A2DP_VENDOR_CODEC_H__ */
index e1f7eb1..d69a77f 100644 (file)
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/module.h>
-#ifdef BLUETOOTH_APTX_SUPPORT
-#include <pulsecore/modargs.h>
-#include <dlfcn.h>
-#endif
 
 #include "module-bluetooth-discover-symdef.h"
 
@@ -38,110 +34,19 @@ PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
     "headset=ofono|native|auto (bluez 5 only)"
     "autodetect_mtu=<boolean> (bluez 5 only)"
-#ifdef BLUETOOTH_APTX_SUPPORT
-    "aptx_lib_name=<name of aptx library name>"
-#endif
 );
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-static const char* const valid_modargs[] = {
-    "aptx_lib_name",
-#ifdef __TIZEN_BT__
-    "enable_scmst",
-#endif
-    NULL
-};
-#endif
-
 struct userdata {
     uint32_t bluez5_module_idx;
     uint32_t bluez4_module_idx;
 };
-#ifdef BLUETOOTH_APTX_SUPPORT
-int pa_load_aptx(const char *aptx_lib_name);
-int pa_unload_aptx(void);
-void* pa_aptx_get_handle(void);
-#endif
-
-#ifdef BLUETOOTH_APTX_SUPPORT
-static void *aptx_handle = NULL;
-
-int pa_unload_aptx(void)
-{
-    if (aptx_handle == NULL) {
-        pa_log_warn("Unable to unload apt-X library");
-        return -1;
-    }
-
-    dlclose(aptx_handle);
-    aptx_handle = NULL;
-
-    pa_log_debug("unloaded apt-X library successfully");
-    return 0;
-}
-
-int pa_load_aptx(const char *aptx_lib_name)
-{
-    char* lib_path = NULL ;
-
-    if(aptx_lib_name == NULL)
-        return -1;
-
-    lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, aptx_lib_name);
-
-    if (!lib_path)
-        return -1;
-
-    pa_log_info("aptx_lib_path = [%s]", lib_path);
-
-    aptx_handle = dlopen(lib_path, RTLD_LAZY);
-    if (aptx_handle == NULL) {
-        pa_log_warn("Unable to load apt-X library [%s]", dlerror());
-        pa_xfree(lib_path);
-        return -1;
-    }
-
-    pa_log_debug("loaded apt-X library successfully");
-    pa_xfree(lib_path);
-
-    return 0;
-}
-
-void* pa_aptx_get_handle(void)
-{
-       return aptx_handle;
-}
-#endif
 
 int pa__init(pa_module* m) {
     struct userdata *u;
     pa_module *mm;
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-    pa_modargs *ma = NULL;
-    const char *aptx_lib_name = NULL;
-#endif
-
     pa_assert(m);
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("Failed to parse module arguments");
-        pa__done(m);
-        return -1;
-    }
-
-    if (pa_modargs_get_value(ma, "async", NULL))
-        pa_log_warn("The 'async' argument is deprecated and does nothing.");
-
-
-    aptx_lib_name = pa_modargs_get_value(ma, "aptx_lib_name", NULL);
-    if (aptx_lib_name)
-        pa_load_aptx(aptx_lib_name);
-    else
-        pa_log("Failed to parse aptx_lib_name argument.");
-#endif
-
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->bluez5_module_idx = PA_INVALID_INDEX;
     u->bluez4_module_idx = PA_INVALID_INDEX;
@@ -160,14 +65,8 @@ int pa__init(pa_module* m) {
 
     if (u->bluez5_module_idx == PA_INVALID_INDEX && u->bluez4_module_idx == PA_INVALID_INDEX) {
         pa_xfree(u);
-#ifdef BLUETOOTH_APTX_SUPPORT
-        pa_modargs_free(ma);
-#endif
         return -1;
     }
-#ifdef BLUETOOTH_APTX_SUPPORT
-    pa_modargs_free(ma);
-#endif
 
     return 0;
 }
@@ -186,9 +85,5 @@ void pa__done(pa_module* m) {
     if (u->bluez4_module_idx != PA_INVALID_INDEX)
         pa_module_unload_by_index(m->core, u->bluez4_module_idx, true);
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-    pa_unload_aptx();
-#endif
-
     pa_xfree(u);
 }
index 85a4755..b0e9790 100644 (file)
@@ -48,6 +48,8 @@
 #ifdef __TIZEN_BT__
 #include <pulsecore/sink.h>
 #include <pulsecore/namereg.h>
+#include <dirent.h>
+#include <dlfcn.h>
 #endif
 
 #include "a2dp-codecs.h"
@@ -115,10 +117,6 @@ typedef struct sbc_info {
     uint16_t seq_num;                    /* Cumulative packet sequence */
     uint8_t min_bitpool;
     uint8_t max_bitpool;
-#ifdef BLUETOOTH_APTX_SUPPORT
-    bool aptx_initialized;               /* Keep track if the encoder is initialized */
-    void *aptx;                          /* aptx Codec data */
-#endif
     void* buffer;                        /* Codec transfer buffer */
     size_t buffer_size;                  /* Size of the buffer */
 } sbc_info_t;
@@ -173,6 +171,9 @@ struct userdata {
 
 #ifdef __TIZEN_BT__
     pa_modargs *modargs;
+    struct pa_bluetooth_vendor_codec_ops *selected_ops;
+    pa_proplist *vendor_codec_list;
+    bool is_aptx_supported;
 #endif
 };
 
@@ -258,42 +259,6 @@ static const char *form_factor_to_string(pa_bluetooth_form_factor_t ff) {
     pa_assert_not_reached();
 }
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-void* (*aptx_new)(short endian);
-int (*aptx_encode)(void* _state, void* _pcmL, void* _pcmR, void* _buffer);
-
-const char *aptx_new_name = "NewAptxEnc";
-const char *aptx_encode_name = "aptxbtenc_encodestereo";
-
-static bool pa_load_aptx_sym(void *handle )
-{
-    if (!handle)
-        return false;
-
-    aptx_new = (void* (*)(short endian))dlsym(handle, aptx_new_name);
-
-    if (aptx_new) {
-        pa_log_debug("Load Symbol(%s)", aptx_new_name);
-    } else {
-        pa_log_debug("Fail to Load Symbol(%s)", aptx_new_name);
-        return false;
-    }
-
-    aptx_encode = (int (*)(void* _state, void* _pcmL, void* _pcmR,
-    void* _buffer))
-    dlsym(handle, "aptxbtenc_encodestereo");
-
-    if (aptx_encode) {
-        pa_log_debug("Load Symbol(%s)", aptx_encode_name);
-    } else {
-        pa_log_debug("Fail to Load Symbol(%s)", aptx_encode_name);
-        return false;
-    }
-
-    return true;
-}
-#endif
-
 /* Run from main thread */
 static void connect_ports(struct userdata *u, void *new_data, pa_direction_t direction) {
     pa_device_port *port;
@@ -609,123 +574,6 @@ static int a2dp_process_render(struct userdata *u) {
     return ret;
 }
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-/* Run from IO thread */
-static int a2dp_aptx_process_render(struct userdata *u) {
-    struct sbc_info *a2dp;
-    size_t nbytes;
-    void *d;
-    const void *p;
-    size_t to_write, to_encode;
-    int ret = 0;
-
-    int pcmL[4],pcmR[4];
-    int i=0;
-    const uint8_t *mybuffer;
-
-    pa_assert(u);
-    pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
-    pa_assert(u->sink);
-
-    /* First, render some data */
-    if (!u->write_memchunk.memblock)
-        pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk);
-
-    pa_assert(u->write_memchunk.length == u->write_block_size);
-
-    a2dp_prepare_buffer(u);
-
-    a2dp = &u->sbc_info;
-
-    /* Try to create a packet of the full MTU */
-    p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index;
-    to_encode = u->write_memchunk.length;
-
-    d = (uint8_t*) a2dp->buffer ;
-    to_write = a2dp->buffer_size;
-
-    while (PA_LIKELY(to_encode > 0 && to_write > 0)) {
-        size_t written;
-        ssize_t encoded;
-
-        mybuffer = (uint8_t *)p;
-
-        for (i = 0; i < 4; i += 1) {
-           pcmL[i] = mybuffer[2*i];
-           pcmR[i] = mybuffer[2*i+1];
-        }
-        /*(8 audio samples)16 bytes of audo data encoded to 4 bytes*/
-        aptx_encode(a2dp->aptx, pcmL, pcmR, (short*)d);
-
-        encoded=16;
-        written=4;
-
-        pa_assert_fp((size_t) encoded <= to_encode);
-        pa_assert_fp((size_t) written <= to_write);
-
-        p = (const uint8_t*) p + encoded;
-        to_encode -= encoded;
-
-        d = (uint8_t*) d + written;
-        to_write -= written;
-
-    }
-
-    pa_memblock_release(u->write_memchunk.memblock);
-
-    pa_assert(to_encode == 0);
-
-    PA_ONCE_BEGIN {
-        pa_log_debug("Using APTX encoder implementation");
-    } PA_ONCE_END;
-
-    nbytes = (uint8_t*) d - (uint8_t*) a2dp->buffer;
-
-    for (;;) {
-        ssize_t l;
-
-        l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type);
-
-        pa_assert(l != 0);
-
-        if (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 socket: %s", pa_cstrerror(errno));
-            ret  = -1;
-            break;
-        }
-
-        pa_assert((size_t) l <= nbytes);
-
-        if ((size_t) l != nbytes) {
-            pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.",
-                        (unsigned long long) l,
-                        (unsigned long long) nbytes);
-            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;
-}
-#endif
-
 #ifdef __TIZEN_BT__
 /* Run from IO thread */
 static int a2dp_process_null_render(struct userdata *u) {
@@ -1448,36 +1296,6 @@ static int add_sink(struct userdata *u) {
     return 0;
 }
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-/* should be implemeted */
-static void bt_transport_config_a2dp_for_aptx(struct userdata *u) {
-    const pa_bluetooth_transport *t;
-    struct sbc_info *a2dp = &u->sbc_info;
-
-    t = u->transport;
-    pa_assert(t);
-
-    u->sample_spec.format = PA_SAMPLE_S16LE;
-
-    if (!a2dp->aptx_initialized) {
-        #if __BYTE_ORDER==__LITTLE_ENDIAN
-            a2dp->aptx = aptx_new(1);
-        #elif __BYTE_ORDER==__BIG_ENDIAN
-            a2dp->aptx = aptx_new(0);
-        #else
-            #error "Unknown byte order"
-        #endif
-            a2dp->aptx_initialized = true;
-    }
-
-    pa_log_debug("aptx Encoder is intialized !!");
-
-    u->write_block_size =(size_t)(u->write_link_mtu/(size_t)16) *16*4 ;
-    pa_log_info("APTX parameters block_size(%d),link_mtu(%d)",u->write_block_size,u->write_link_mtu);
-
-}
-#endif
-
 /* Run from main thread */
 static void transport_config(struct userdata *u) {
     if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
@@ -1487,30 +1305,37 @@ static void transport_config(struct userdata *u) {
     } else {
         sbc_info_t *sbc_info = &u->sbc_info;
         a2dp_sbc_t *config;
-#ifdef BLUETOOTH_APTX_SUPPORT
         const pa_bluetooth_transport *t;
         a2dp_aptx_t *aptx_config;
-#endif
 
         pa_assert(u->transport);
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-        t = u->transport;
-        if (t->codec == A2DP_CODEC_VENDOR) {
-            aptx_config = (a2dp_aptx_t *) t->config;
-            if (aptx_config->vendor_id[0] == APTX_VENDOR_ID0 &&
-                aptx_config->vendor_id[1] == APTX_VENDOR_ID1 &&
-                 aptx_config->vendor_id[2] == APTX_VENDOR_ID2 &&
-                  aptx_config->vendor_id[3] == APTX_VENDOR_ID3 &&
-                   aptx_config->codec_id[0] == APTX_CODEC_ID0  &&
-                    aptx_config->codec_id[1] == APTX_CODEC_ID1  ){
-                pa_log("A2DP_CODEC_NON_A2DP and this is APTX Codec");
-
-                bt_transport_config_a2dp_for_aptx(u);
-                return;
-            } else {
-                pa_log("A2DP_CODEC_NON_A2DP but this is not APTX Codec");
-                return;
+#ifdef __TIZEN_BT__
+        if (u->is_aptx_supported) {
+            t = u->transport;
+            if (t->codec == A2DP_CODEC_VENDOR) {
+                aptx_config = (a2dp_aptx_t *) t->config;
+                if (aptx_config->vendor_id[0] == APTX_VENDOR_ID0 &&
+                        aptx_config->vendor_id[1] == APTX_VENDOR_ID1 &&
+                        aptx_config->vendor_id[2] == APTX_VENDOR_ID2 &&
+                        aptx_config->vendor_id[3] == APTX_VENDOR_ID3 &&
+                        aptx_config->codec_id[0] == APTX_CODEC_ID0 &&
+                        aptx_config->codec_id[1] == APTX_CODEC_ID1) {
+                    struct pa_bluetooth_vendor_codec_ops *ops = NULL;
+                    size_t size;
+                    pa_log("A2DP_CODEC_NON_A2DP and this is APTX Codec");
+                    pa_proplist_get(u->vendor_codec_list, "APTX", &ops, &size);
+                    if (ops) {
+                        ops->transport_config(u);
+                        u->selected_ops = ops;
+                    } else {
+                        pa_log_error("APTX ops is NULL");
+                    }
+                    return;
+                } else {
+                    pa_log("A2DP_CODEC_NON_A2DP but this is not APTX Codec");
+                    return;
+                }
             }
         }
 #endif
@@ -1811,7 +1636,7 @@ static void thread_func(void *userdata) {
                 }
 
                 if (writable && do_write > 0) {
-                    int n_written;
+                    int n_written = 0;
 
                     if (u->write_index <= 0)
                         u->started_at = pa_rtclock_now();
@@ -1822,12 +1647,11 @@ static void thread_func(void *userdata) {
                             if ((n_written = a2dp_process_render(u)) < 0)
                                 goto fail;
                         } else {
-#ifdef BLUETOOTH_APTX_SUPPORT
-                            if ((n_written = a2dp_aptx_process_render(u)) < 0)
+                            if (u->selected_ops)
+                                if ((n_written = u->selected_ops->process_render(u)) < 0)
+                                    goto fail;
+                            else
                                 goto fail;
-#else
-                            goto fail;
-#endif
                         }
                     } else if ((u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) &&
                                            u->transport_suspended_by_remote) {
@@ -2601,14 +2425,67 @@ static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t o
     return 0;
 }
 
+#ifdef __TIZEN_BT__
+#define MODULE_A2DP_VENDOR_CODEC "module-a2dp-vendor-codec"
+static void initialize_vendor_codec(struct userdata *u)
+{
+    DIR *dir = NULL;
+    struct dirent *ent = NULL;
+
+    dir = opendir(PA_DLSEARCHPATH);
+    if (!dir)
+        return;
+
+    u->vendor_codec_list = pa_proplist_new();
+
+    /* Search vendor codec module */
+    while ((ent = readdir(dir)) != NULL) {
+        void *handle = NULL;
+        struct pa_bluetooth_vendor_codec_ops *ops = NULL;
+
+        if (strlen(ent->d_name) <= strlen(MODULE_A2DP_VENDOR_CODEC)
+                || strncmp(ent->d_name, MODULE_A2DP_VENDOR_CODEC, strlen(MODULE_A2DP_VENDOR_CODEC)))
+            continue;
+
+        pa_log("Vendor codec found (%s)", ent->d_name);
+        handle = dlopen(ent->d_name, RTLD_LAZY);
+        if (!handle) {
+            pa_log("dlopen() is failed (%s)", dlerror());
+            continue;
+        }
+
+        ops = dlsym(handle, "vendor_codec_ops");
+        if (!ops) {
+            pa_log("dlsym() is failed (%s)", dlerror());
+            dlclose(handle);
+            continue;
+        }
+
+        /* Initialize vendor codec */
+        if (ops->init) {
+            if (ops->init()) {
+                pa_log("Codec is initialized successfully (%s)", ent->d_name);
+                /* Store the vendor_codec_ops */
+                pa_proplist_set(u->vendor_codec_list, ops->name, ops, sizeof(struct pa_bluetooth_vendor_codec_ops));
+                if (strcasecmp(ops->name, "APTX") == 0)
+                    u->is_aptx_supported = true;
+            } else {
+                pa_log("ops->init() is failed");
+           }
+        } else {
+            pa_log("ops->init is NULL");
+        }
+        dlclose(handle);
+    }
+    closedir(dir);
+}
+#endif
+
 int pa__init(pa_module* m) {
     struct userdata *u;
     const char *path;
     pa_modargs *ma;
     bool autodetect_mtu;
-#ifdef BLUETOOTH_APTX_SUPPORT
-    void *handle;
-#endif
 
     pa_assert(m);
 
@@ -2685,13 +2562,8 @@ int pa__init(pa_module* m) {
     u->msg->card = u->card;
     u->stream_setup_done = false;
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-    handle = pa_aptx_get_handle();
-
-    if (handle) {
-        pa_log_debug("Aptx Library loaded\n");
-        pa_load_aptx_sym(handle);
-    }
+#ifdef __TIZEN_BT__
+    initialize_vendor_codec(u);
 #endif
 
 #ifdef __TIZEN_BT__
@@ -2730,6 +2602,12 @@ fail:
 
 void pa__done(pa_module *m) {
     struct userdata *u;
+#ifdef __TIZEN_BT__
+    const char *key = NULL;
+    void *state = NULL;
+    size_t size;
+    struct pa_bluetooth_vendor_codec_ops *ops = NULL;
+#endif
 
     pa_assert(m);
 
@@ -2752,6 +2630,14 @@ void pa__done(pa_module *m) {
         pa_hook_slot_free(u->sco_state_changed_slot);
 #endif
 
+#ifdef __TIZEN_BT__
+    while ((key = pa_proplist_iterate(u->vendor_codec_list, &state))) {
+        pa_proplist_get(u->vendor_codec_list, key, &ops, &size);
+        ops->deinit();
+    }
+    pa_proplist_free(u->vendor_codec_list);
+#endif
+
     if (u->transport_microphone_gain_changed_slot)
         pa_hook_slot_free(u->transport_microphone_gain_changed_slot);
 
index 8133b4b..d2abbda 100644 (file)
@@ -40,9 +40,6 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_USAGE("sco_sink=<name of sink> "
         "sco_source=<name of source> "
         "headset=ofono|native|auto"
-#ifdef BLUETOOTH_APTX_SUPPORT
-        "aptx_lib_name=<name of aptx library name>"
-#endif
 );
 #else
 PA_MODULE_USAGE(
@@ -57,9 +54,6 @@ static const char* const valid_modargs[] = {
     "autodetect_mtu",
     "sco_sink",
     "sco_source",
-#ifdef BLUETOOTH_APTX_SUPPORT
-    "aptx_lib_name",
-#endif
     NULL
 };
 #else
@@ -136,9 +130,6 @@ const char *default_headset_backend = "ofono";
 
 int pa__init(pa_module *m) {
     struct userdata *u;
-#ifdef BLUETOOTH_APTX_SUPPORT
-    const char *aptx_lib_name = NULL;
-#endif
     pa_modargs *ma;
     const char *headset_str;
     int headset_backend;
@@ -151,13 +142,6 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-    aptx_lib_name = pa_modargs_get_value(ma, "aptx_lib_name", NULL);
-    if (aptx_lib_name)
-        pa_load_aptx(aptx_lib_name);
-    else
-        pa_log("Failed to parse aptx_lib_name argument.");
-#endif
 
     pa_assert_se(headset_str = pa_modargs_get_value(ma, "headset", default_headset_backend));
     if (pa_streq(headset_str, "ofono"))
@@ -217,9 +201,5 @@ void pa__done(pa_module *m) {
     if (u->loaded_device_paths)
         pa_hashmap_free(u->loaded_device_paths);
 
-#ifdef BLUETOOTH_APTX_SUPPORT
-    pa_unload_aptx();
-#endif
-
     pa_xfree(u);
 }