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
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],
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)
%{_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
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
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
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
#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
#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)
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;
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)) {
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
--- /dev/null
+/***
+ 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,
+};
--- /dev/null
+/***
+ 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__ */
#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"
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;
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;
}
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);
}
#ifdef __TIZEN_BT__
#include <pulsecore/sink.h>
#include <pulsecore/namereg.h>
+#include <dirent.h>
+#include <dlfcn.h>
#endif
#include "a2dp-codecs.h"
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;
#ifdef __TIZEN_BT__
pa_modargs *modargs;
+ struct pa_bluetooth_vendor_codec_ops *selected_ops;
+ pa_proplist *vendor_codec_list;
+ bool is_aptx_supported;
#endif
};
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;
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) {
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) {
} 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
}
if (writable && do_write > 0) {
- int n_written;
+ int n_written = 0;
if (u->write_index <= 0)
u->started_at = pa_rtclock_now();
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) {
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);
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__
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);
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);
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(
"autodetect_mtu",
"sco_sink",
"sco_source",
-#ifdef BLUETOOTH_APTX_SUPPORT
- "aptx_lib_name",
-#endif
NULL
};
#else
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;
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"))
if (u->loaded_device_paths)
pa_hashmap_free(u->loaded_device_paths);
-#ifdef BLUETOOTH_APTX_SUPPORT
- pa_unload_aptx();
-#endif
-
pa_xfree(u);
}