v3 changes:
- fixed variable names
- added queue for loaded codecs
Andrzej Kaczmarek (5):
android/hal-audio: Always call qos_update
android/hal-audio: Allow codec to init on startup
android/hal-audio-aptx: Add initial support for aptX codec
android/hal-audio-aptx: Load aptX encoder library
android/hal-audio-aptx: Add encoding
android/Android.mk | 4 +
android/Makefile.am | 1 +
android/hal-audio-aptx.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++
android/hal-audio.c | 84 +++++++++++----
android/hal-audio.h | 4 +
5 files changed, 349 insertions(+), 20 deletions(-)
create mode 100644 android/hal-audio-aptx.c
--
1.9.3
Hi Andrzej,
On Mon, Jun 2, 2014 at 7:37 PM, Andrzej Kaczmarek
<[email protected]> wrote:
> v3 changes:
> - fixed variable names
> - added queue for loaded codecs
>
>
> Andrzej Kaczmarek (5):
> android/hal-audio: Always call qos_update
> android/hal-audio: Allow codec to init on startup
> android/hal-audio-aptx: Add initial support for aptX codec
> android/hal-audio-aptx: Load aptX encoder library
> android/hal-audio-aptx: Add encoding
>
> android/Android.mk | 4 +
> android/Makefile.am | 1 +
> android/hal-audio-aptx.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++
> android/hal-audio.c | 84 +++++++++++----
> android/hal-audio.h | 4 +
> 5 files changed, 349 insertions(+), 20 deletions(-)
> create mode 100644 android/hal-audio-aptx.c
>
> --
> 1.9.3
Applied, thanks.
--
Luiz Augusto von Dentz
This patch adds support for aptX codec. Since this is proprietary codec
it requires to obtain license form vendor (CSR) in order to use it.
Also shared library which provices encoder implementation is required
since this implementation only wraps it into audio HAL.
---
android/Android.mk | 2 +
android/Makefile.am | 1 +
android/hal-audio-aptx.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++
android/hal-audio.c | 1 +
android/hal-audio.h | 1 +
5 files changed, 209 insertions(+)
create mode 100644 android/hal-audio-aptx.c
diff --git a/android/Android.mk b/android/Android.mk
index e479bab..a9a0982 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -258,6 +258,7 @@ LOCAL_SRC_FILES := \
bluez/src/shared/queue.c \
bluez/android/hal-audio.c \
bluez/android/hal-audio-sbc.c \
+ bluez/android/hal-audio-aptx.c \
LOCAL_C_INCLUDES = \
$(LOCAL_PATH)/bluez \
@@ -270,6 +271,7 @@ LOCAL_SHARED_LIBRARIES := \
libsbc \
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+LOCAL_LDFLAGS := -ldl
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_MODULE_TAGS := optional
diff --git a/android/Makefile.am b/android/Makefile.am
index bcaec4c..30d3c9a 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -170,6 +170,7 @@ android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
android/hal-audio.h \
android/hal-audio.c \
android/hal-audio-sbc.c \
+ android/hal-audio-aptx.c \
android/hardware/audio.h \
android/hardware/audio_effect.h \
android/hardware/hardware.h \
diff --git a/android/hal-audio-aptx.c b/android/hal-audio-aptx.c
new file mode 100644
index 0000000..fed648b
--- /dev/null
+++ b/android/hal-audio-aptx.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 Tieto Poland
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <malloc.h>
+#include <dlfcn.h>
+
+#include "audio-msg.h"
+#include "hal-audio.h"
+#include "hal-log.h"
+#include "src/shared/util.h"
+#include "profiles/audio/a2dp-codecs.h"
+
+struct aptx_data {
+ a2dp_aptx_t aptx;
+
+ void *enc;
+};
+
+static const a2dp_aptx_t aptx_presets[] = {
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_44100 |
+ APTX_SAMPLING_FREQ_48000,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_48000,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+ {
+ .info = {
+ .vendor_id = APTX_VENDOR_ID,
+ .codec_id = APTX_CODEC_ID,
+ },
+ .frequency = APTX_SAMPLING_FREQ_44100,
+ .channel_mode = APTX_CHANNEL_MODE_STEREO,
+ },
+};
+
+static bool aptx_load(void)
+{
+ /* TODO: load aptX codec library */
+ return false;
+}
+
+static void aptx_unload(void)
+{
+ /* TODO: unload aptX codec library */
+}
+
+static int aptx_get_presets(struct audio_preset *preset, size_t *len)
+{
+ int i;
+ int count;
+ size_t new_len = 0;
+ uint8_t *ptr = (uint8_t *) preset;
+ size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t);
+
+ DBG("");
+
+ count = sizeof(aptx_presets) / sizeof(aptx_presets[0]);
+
+ for (i = 0; i < count; i++) {
+ preset = (struct audio_preset *) ptr;
+
+ if (new_len + preset_size > *len)
+ break;
+
+ preset->len = sizeof(a2dp_aptx_t);
+ memcpy(preset->data, &aptx_presets[i], preset->len);
+
+ new_len += preset_size;
+ ptr += preset_size;
+ }
+
+ *len = new_len;
+
+ return i;
+}
+
+static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len,
+ void **codec_data)
+{
+ struct aptx_data *aptx_data;
+
+ DBG("");
+
+ if (preset->len != sizeof(a2dp_aptx_t)) {
+ error("APTX: preset size mismatch");
+ return false;
+ }
+
+ aptx_data = new0(struct aptx_data, 1);
+ if (!aptx_data)
+ return false;
+
+ memcpy(&aptx_data->aptx, preset->data, preset->len);
+
+ /* TODO: initialize encoder */
+
+ *codec_data = aptx_data;
+
+ return true;
+}
+
+static bool aptx_cleanup(void *codec_data)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+ free(aptx_data->enc);
+ free(codec_data);
+
+ return true;
+}
+
+static bool aptx_get_config(void *codec_data, struct audio_input_config *config)
+{
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+
+ config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ?
+ 48000 : 44100;
+ config->channels = AUDIO_CHANNEL_OUT_STEREO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+ return true;
+}
+
+static size_t aptx_get_buffer_size(void *codec_data)
+{
+ /* TODO: return actual value */
+ return 0;
+}
+
+static size_t aptx_get_mediapacket_duration(void *codec_data)
+{
+ /* TODO: return actual value */
+ return 0;
+}
+
+static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+ size_t len, struct media_packet *mp,
+ size_t mp_data_len, size_t *written)
+{
+ /* TODO: add encoding */
+
+ return len;
+}
+
+static bool aptx_update_qos(void *codec_data, uint8_t op)
+{
+ /*
+ * aptX has constant bitrate of 352kbps (with constant 4:1 compression
+ * ratio) thus QoS is not possible here.
+ */
+
+ return false;
+}
+
+static const struct audio_codec codec = {
+ .type = A2DP_CODEC_VENDOR,
+ .use_rtp = false,
+
+ .load = aptx_load,
+ .unload = aptx_unload,
+
+ .get_presets = aptx_get_presets,
+
+ .init = aptx_codec_init,
+ .cleanup = aptx_cleanup,
+ .get_config = aptx_get_config,
+ .get_buffer_size = aptx_get_buffer_size,
+ .get_mediapacket_duration = aptx_get_mediapacket_duration,
+ .encode_mediapacket = aptx_encode_mediapacket,
+ .update_qos = aptx_update_qos,
+};
+
+const struct audio_codec *codec_aptx(void)
+{
+ return &codec;
+}
diff --git a/android/hal-audio.c b/android/hal-audio.c
index a7cda8a..e9a9c54 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -98,6 +98,7 @@ extern int clock_nanosleep(clockid_t clock_id, int flags,
#endif
static const audio_codec_get_t audio_codecs[] = {
+ codec_aptx,
codec_sbc,
};
diff --git a/android/hal-audio.h b/android/hal-audio.h
index decbdd8..2b47412 100644
--- a/android/hal-audio.h
+++ b/android/hal-audio.h
@@ -99,3 +99,4 @@ struct audio_codec {
typedef const struct audio_codec * (*audio_codec_get_t) (void);
const struct audio_codec *codec_sbc(void);
+const struct audio_codec *codec_aptx(void);
--
1.9.3
This patch adds optional load/unload methods for codec which can be
used to initialize some static data for codec, e.g. load shared library
which provides encoder. Unlike init/cleanup which are called on stream
open/close these methods are called when audio device is opened/closed
thus most likely only once.
Codecs which are loaded properly (or do not have load callback) are
added to separate queue and used later to register endpoints.
---
android/Android.mk | 2 ++
android/hal-audio.c | 77 ++++++++++++++++++++++++++++++++++++++++++-----------
android/hal-audio.h | 3 +++
3 files changed, 66 insertions(+), 16 deletions(-)
diff --git a/android/Android.mk b/android/Android.mk
index de909d6..e479bab 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -255,10 +255,12 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
+ bluez/src/shared/queue.c \
bluez/android/hal-audio.c \
bluez/android/hal-audio-sbc.c \
LOCAL_C_INCLUDES = \
+ $(LOCAL_PATH)/bluez \
$(call include-path-for, system-core) \
$(call include-path-for, libhardware) \
$(call include-path-for, sbc) \
diff --git a/android/hal-audio.c b/android/hal-audio.c
index f15e767..a7cda8a 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -35,7 +35,8 @@
#include "hal-log.h"
#include "hal-msg.h"
#include "hal-audio.h"
-#include "../src/shared/util.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
@@ -104,6 +105,8 @@ static const audio_codec_get_t audio_codecs[] = {
#define MAX_AUDIO_ENDPOINTS NUM_CODECS
+static struct queue *loaded_codecs;
+
struct audio_endpoint {
uint8_t id;
const struct audio_codec *codec;
@@ -415,28 +418,46 @@ static int ipc_suspend_stream_cmd(uint8_t endpoint_id)
return result;
}
-static int register_endpoints(void)
+struct register_state {
+ struct audio_endpoint *ep;
+ bool error;
+};
+
+static void register_endpoint(void *data, void *user_data)
{
- struct audio_endpoint *ep = &audio_endpoints[0];
- size_t i;
+ struct audio_codec *codec = data;
+ struct register_state *state = user_data;
+ struct audio_endpoint *ep = state->ep;
- for (i = 0; i < NUM_CODECS; i++, ep++) {
- const struct audio_codec *codec = audio_codecs[i]();
+ /* don't even try to register more endpoints if one failed */
+ if (state->error)
+ return;
- if (!codec)
- return AUDIO_STATUS_FAILED;
+ ep->id = ipc_open_cmd(codec);
- ep->id = ipc_open_cmd(codec);
+ if (!ep->id) {
+ state->error = true;
+ error("Failed to register endpoint");
+ return;
+ }
- if (!ep->id)
- return AUDIO_STATUS_FAILED;
+ ep->codec = codec;
+ ep->codec_data = NULL;
+ ep->fd = -1;
- ep->codec = codec;
- ep->codec_data = NULL;
- ep->fd = -1;
- }
+ state->ep++;
+}
- return AUDIO_STATUS_SUCCESS;
+static int register_endpoints(void)
+{
+ struct register_state state;
+
+ state.ep = &audio_endpoints[0];
+ state.error = false;
+
+ queue_foreach(loaded_codecs, register_endpoint, &state);
+
+ return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS;
}
static void unregister_endpoints(void)
@@ -1272,6 +1293,14 @@ static int audio_dump(const audio_hw_device_t *device, int fd)
return -ENOSYS;
}
+static void unload_codec(void *data)
+{
+ struct audio_codec *codec = data;
+
+ if (codec->unload)
+ codec->unload();
+}
+
static int audio_close(hw_device_t *device)
{
struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
@@ -1280,6 +1309,9 @@ static int audio_close(hw_device_t *device)
unregister_endpoints();
+ queue_destroy(loaded_codecs, unload_codec);
+ loaded_codecs = NULL;
+
shutdown(listen_sk, SHUT_RDWR);
shutdown(audio_sk, SHUT_RDWR);
@@ -1419,6 +1451,7 @@ static int audio_open(const hw_module_t *module, const char *name,
hw_device_t **device)
{
struct a2dp_audio_dev *a2dp_dev;
+ size_t i;
int err;
DBG("");
@@ -1457,6 +1490,18 @@ static int audio_open(const hw_module_t *module, const char *name,
a2dp_dev->dev.close_input_stream = audio_close_input_stream;
a2dp_dev->dev.dump = audio_dump;
+ loaded_codecs = queue_new();
+
+ for (i = 0; i < NUM_CODECS; i++) {
+ audio_codec_get_t get_codec = audio_codecs[i];
+ const struct audio_codec *codec = get_codec();
+
+ if (codec->load && !codec->load())
+ continue;
+
+ queue_push_tail(loaded_codecs, (void *) codec);
+ }
+
/*
* Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev.
* This results from the structure of following structs:a2dp_audio_dev,
diff --git a/android/hal-audio.h b/android/hal-audio.h
index be71473..decbdd8 100644
--- a/android/hal-audio.h
+++ b/android/hal-audio.h
@@ -75,6 +75,9 @@ struct audio_codec {
uint8_t type;
bool use_rtp;
+ bool (*load) (void);
+ void (*unload) (void);
+
int (*get_presets) (struct audio_preset *preset, size_t *len);
bool (*init) (struct audio_preset *preset, uint16_t mtu,
--
1.9.3
This patch adds loading of aptX encoder library which should be provided
by user. hal-audio-aptx will try to load 'libbt-aptx.so' so it should be
available in search patch, preferably in /system/lib.
---
android/hal-audio-aptx.c | 49 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 46 insertions(+), 3 deletions(-)
diff --git a/android/hal-audio-aptx.c b/android/hal-audio-aptx.c
index fed648b..807d82d 100644
--- a/android/hal-audio-aptx.c
+++ b/android/hal-audio-aptx.c
@@ -27,6 +27,8 @@
#include "src/shared/util.h"
#include "profiles/audio/a2dp-codecs.h"
+#define APTX_SO_NAME "libbt-aptx.so"
+
struct aptx_data {
a2dp_aptx_t aptx;
@@ -61,15 +63,56 @@ static const a2dp_aptx_t aptx_presets[] = {
},
};
+static void *aptx_handle;
+static int aptx_btencsize;
+static int (*aptx_init)(void *, short);
+static int (*aptx_encode)(void *, void *, void *, void *);
+
static bool aptx_load(void)
{
- /* TODO: load aptX codec library */
- return false;
+ const char * (*aptx_version)(void);
+ const char * (*aptx_build)(void);
+ int (*aptx_sizeofenc)(void);
+
+ aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY);
+ if (!aptx_handle) {
+ error("APTX: failed to open library %s (%s)", APTX_SO_NAME,
+ dlerror());
+ return false;
+ }
+
+ aptx_version = dlsym(aptx_handle, "aptxbtenc_version");
+ aptx_build = dlsym(aptx_handle, "aptxbtenc_build");
+
+ if (aptx_version && aptx_build)
+ info("APTX: using library version %s build %s", aptx_version(),
+ aptx_build());
+ else
+ warn("APTX: cannot retrieve library version");
+
+ aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc");
+ aptx_init = dlsym(aptx_handle, "aptxbtenc_init");
+ aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo");
+ if (!aptx_sizeofenc || !aptx_init || !aptx_encode) {
+ error("APTX: failed initialize library");
+ dlclose(aptx_handle);
+ aptx_handle = NULL;
+ return false;
+ }
+ aptx_btencsize = aptx_sizeofenc();
+
+ info("APTX: codec library initialized (size=%d)", aptx_btencsize);
+
+ return true;
}
static void aptx_unload(void)
{
- /* TODO: unload aptX codec library */
+ if (!aptx_handle)
+ return;
+
+ dlclose(aptx_handle);
+ aptx_handle = NULL;
}
static int aptx_get_presets(struct audio_preset *preset, size_t *len)
--
1.9.3
---
android/hal-audio-aptx.c | 35 ++++++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/android/hal-audio-aptx.c b/android/hal-audio-aptx.c
index 807d82d..09636b8 100644
--- a/android/hal-audio-aptx.c
+++ b/android/hal-audio-aptx.c
@@ -163,7 +163,15 @@ static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len,
memcpy(&aptx_data->aptx, preset->data, preset->len);
- /* TODO: initialize encoder */
+ aptx_data->enc = calloc(1, aptx_btencsize);
+ if (!aptx_data->enc) {
+ error("APTX: failed to create encoder");
+ free(aptx_data);
+ return false;
+ }
+
+ /* 1 = big-endian, this is what devices are using */
+ aptx_init(aptx_data->enc, 1);
*codec_data = aptx_data;
@@ -208,9 +216,30 @@ static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer,
size_t len, struct media_packet *mp,
size_t mp_data_len, size_t *written)
{
- /* TODO: add encoding */
+ struct aptx_data *aptx_data = (struct aptx_data *) codec_data;
+ const int16_t *ptr = (const void *) buffer;
+ size_t bytes_in = 0;
+ size_t bytes_out = 0;
+
+ while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) {
+ int pcm_l[4], pcm_r[4];
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ pcm_l[i] = ptr[0];
+ pcm_r[i] = ptr[1];
+ ptr += 2;
+ }
+
+ aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]);
+
+ bytes_in += 16;
+ bytes_out += 4;
+ }
+
+ *written = bytes_out;
- return len;
+ return bytes_in;
}
static bool aptx_update_qos(void *codec_data, uint8_t op)
--
1.9.3
As it turned out, it's better to always call update_qos and just
provide dummy callback from codecs which do not support it.
---
android/hal-audio.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/android/hal-audio.c b/android/hal-audio.c
index 8b82498..f15e767 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -544,8 +544,7 @@ static bool resume_endpoint(struct audio_endpoint *ep)
ep->samples = 0;
ep->resync = false;
- if (ep->codec->update_qos)
- ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT);
+ ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT);
return true;
}
@@ -704,8 +703,7 @@ static bool write_data(struct a2dp_stream_out *out, const void *buffer,
if (diff > MAX_DELAY) {
warn("lag is %jums, resyncing", diff / 1000);
- if (ep->codec->update_qos)
- ep->codec->update_qos(ep->codec_data,
+ ep->codec->update_qos(ep->codec_data,
QOS_POLICY_DECREASE);
ep->resync = true;
}
--
1.9.3