2014-01-22 10:34:43

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 00/10] android: Add SBC encoding

Hi,

v1 -> v2
- fixed comments
- added dependency to libsbc shared library on Android

v2 -> v3
- added comment explaining why we need to sleep in out_write
- updated to new SBC API
- libsbc shared library is now built from our Android.mk
- implemented proper get_latency callback



Andrzej Kaczmarek (10):
android: Add MTU data to Open Stream Audio IPC
android: Build Audio HAL with SBC
android/hal-audio: Rename sbc_init to avoid collision with libsbc
android/hal-audio: Initialize SBC encoder
android/hal-audio: Calculate SBC stream parameters
android/hal-audio: Add resume to codec callbacks
android/hal-audio: Return proper buffer size to AudioFlinger
android/hal-audio: Read fd from Output Stream response
android/hal-audio: Add proper SBC encoding
android/hal-audio: Return proper latency for stream

android/Android.mk | 37 ++++++-
android/Makefile.am | 2 +
android/a2dp.c | 8 +-
android/audio-msg.h | 1 +
android/hal-audio.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++---
configure.ac | 7 ++
6 files changed, 338 insertions(+), 18 deletions(-)

--
1.8.5.2



2014-01-23 09:43:10

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH v3 00/10] android: Add SBC encoding

Hi Andrzej,

On Wed, Jan 22, 2014 at 12:34 PM, Andrzej Kaczmarek
<[email protected]> wrote:
> Hi,
>
> v1 -> v2
> - fixed comments
> - added dependency to libsbc shared library on Android
>
> v2 -> v3
> - added comment explaining why we need to sleep in out_write
> - updated to new SBC API
> - libsbc shared library is now built from our Android.mk
> - implemented proper get_latency callback
>
>
>
> Andrzej Kaczmarek (10):
> android: Add MTU data to Open Stream Audio IPC
> android: Build Audio HAL with SBC
> android/hal-audio: Rename sbc_init to avoid collision with libsbc
> android/hal-audio: Initialize SBC encoder
> android/hal-audio: Calculate SBC stream parameters
> android/hal-audio: Add resume to codec callbacks
> android/hal-audio: Return proper buffer size to AudioFlinger
> android/hal-audio: Read fd from Output Stream response
> android/hal-audio: Add proper SBC encoding
> android/hal-audio: Return proper latency for stream
>
> android/Android.mk | 37 ++++++-
> android/Makefile.am | 2 +
> android/a2dp.c | 8 +-
> android/audio-msg.h | 1 +
> android/hal-audio.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++---
> configure.ac | 7 ++
> 6 files changed, 338 insertions(+), 18 deletions(-)
>
> --
> 1.8.5.2

Pushed, thanks.


--
Luiz Augusto von Dentz

2014-01-22 10:34:52

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 09/10] android/hal-audio: Add proper SBC encoding

Input and output stream is configured in a way that each input buffer
can be encoded to exactly one output buffer.

Reading from AudioFlinger is synchronized based on amounts of frames
which were expected to be sent since stream was resumed, i.e. as long
as we sent enough data we can wait for period of single media packet
before we need another buffer from input. Without synchronization
we'd receive next input buffer as soon as we process current one.
---
android/hal-audio.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 99 insertions(+), 3 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 81a452a..ddc6348 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -24,6 +24,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
+#include <arpa/inet.h>

#include <hardware/audio.h>
#include <hardware/hardware.h>
@@ -127,8 +128,22 @@ struct sbc_data {

struct timespec start;
unsigned frames_sent;
+
+ uint16_t seq;
};

+static inline void timespec_diff(struct timespec *a, struct timespec *b,
+ struct timespec *res)
+{
+ res->tv_sec = a->tv_sec - b->tv_sec;
+ res->tv_nsec = a->tv_nsec - b->tv_nsec;
+
+ if (res->tv_nsec < 0) {
+ res->tv_sec--;
+ res->tv_nsec += 1000000000; /* 1sec */
+ }
+}
+
static int sbc_get_presets(struct audio_preset *preset, size_t *len);
static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
void **codec_data);
@@ -137,6 +152,8 @@ static int sbc_get_config(void *codec_data,
struct audio_input_config *config);
static size_t sbc_get_buffer_size(void *codec_data);
static void sbc_resume(void *codec_data);
+static ssize_t sbc_write_data(void *codec_data, const void *buffer,
+ size_t bytes, int fd);

struct audio_codec {
uint8_t type;
@@ -151,7 +168,7 @@ struct audio_codec {
size_t (*get_buffer_size) (void *codec_data);
void (*resume) (void *codec_data);
ssize_t (*write_data) (void *codec_data, const void *buffer,
- size_t bytes);
+ size_t bytes, int fd);
};

static const struct audio_codec audio_codecs[] = {
@@ -165,6 +182,7 @@ static const struct audio_codec audio_codecs[] = {
.get_config = sbc_get_config,
.get_buffer_size = sbc_get_buffer_size,
.resume = sbc_resume,
+ .write_data = sbc_write_data,
}
};

@@ -380,6 +398,80 @@ static void sbc_resume(void *codec_data)
sbc_data->frames_sent = 0;
}

+static ssize_t sbc_write_data(void *codec_data, const void *buffer,
+ size_t bytes, int fd)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+ size_t consumed = 0;
+ size_t encoded = 0;
+ struct media_packet *mp = (struct media_packet *) sbc_data->out_buf;
+ size_t free_space = sbc_data->out_buf_size - sizeof(*mp);
+ struct timespec cur;
+ struct timespec diff;
+ unsigned expected_frames;
+ int ret;
+
+ mp->hdr.v = 2;
+ mp->hdr.pt = 1;
+ mp->hdr.sequence_number = htons(sbc_data->seq++);
+ mp->hdr.ssrc = htonl(1);
+ mp->payload.frame_count = 0;
+
+ while (bytes - consumed >= sbc_data->in_frame_len) {
+ ssize_t written = 0;
+
+ ret = sbc_encode(&sbc_data->enc, buffer + consumed,
+ sbc_data->in_frame_len,
+ mp->data + encoded, free_space,
+ &written);
+
+ if (ret < 0) {
+ DBG("failed to encode block");
+ break;
+ }
+
+ mp->payload.frame_count++;
+
+ consumed += ret;
+ encoded += written;
+ free_space -= written;
+ }
+
+ ret = write(fd, mp, sizeof(*mp) + encoded);
+ if (ret < 0) {
+ int err = errno;
+ DBG("error writing data: %d (%s)", err, strerror(err));
+ }
+
+ if (consumed != bytes || free_space != 0) {
+ /* we should encode all input data and fill output buffer
+ * if we did not, something went wrong but we can't really
+ * handle this so this is just sanity check
+ */
+ DBG("some data were not encoded");
+ }
+
+ sbc_data->frames_sent += mp->payload.frame_count;
+
+ clock_gettime(CLOCK_MONOTONIC, &cur);
+ timespec_diff(&cur, &sbc_data->start, &diff);
+ expected_frames = (diff.tv_sec * 1000000 + diff.tv_nsec / 1000) /
+ sbc_data->frame_duration;
+
+ /* AudioFlinger does not seem to provide any *working* API to provide
+ * data in some interval and will just send another buffer as soon as
+ * we process current one. To prevent overflowing L2CAP socket, we need
+ * to introduce some artificial delay here base on how many audio frames
+ * were sent so far, i.e. if we're not lagging behind audio stream, we
+ * can sleep for duration of single media packet.
+ */
+ if (sbc_data->frames_sent >= expected_frames)
+ usleep(sbc_data->frame_duration * mp->payload.frame_count);
+
+ /* we always assume that all data was processed and sent */
+ return bytes;
+}
+
static void audio_ipc_cleanup(void)
{
if (audio_sk >= 0) {
@@ -712,9 +804,13 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
return -1;
}

- /* TODO: encode data using codec */
+ if (out->ep->fd < 0) {
+ DBG("no transport");
+ return -1;
+ }

- return bytes;
+ return out->ep->codec->write_data(out->ep->codec_data, buffer,
+ bytes, out->ep->fd);
}

static uint32_t out_get_sample_rate(const struct audio_stream *stream)
--
1.8.5.2


2014-01-22 10:34:46

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 03/10] android/hal-audio: Rename sbc_init to avoid collision with libsbc

---
android/hal-audio.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 2f6f8c2..f53dba0 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -56,7 +56,7 @@ struct sbc_data {
};

static int sbc_get_presets(struct audio_preset *preset, size_t *len);
-static int sbc_init(struct audio_preset *preset, void **codec_data);
+static int sbc_codec_init(struct audio_preset *preset, void **codec_data);
static int sbc_cleanup(void *codec_data);
static int sbc_get_config(void *codec_data,
struct audio_input_config *config);
@@ -80,7 +80,7 @@ static const struct audio_codec audio_codecs[] = {

.get_presets = sbc_get_presets,

- .init = sbc_init,
+ .init = sbc_codec_init,
.cleanup = sbc_cleanup,
.get_config = sbc_get_config,
}
@@ -184,7 +184,7 @@ static int sbc_get_presets(struct audio_preset *preset, size_t *len)
return i;
}

-static int sbc_init(struct audio_preset *preset, void **codec_data)
+static int sbc_codec_init(struct audio_preset *preset, void **codec_data)
{
struct sbc_data *sbc_data;

--
1.8.5.2


2014-01-22 10:34:53

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 10/10] android/hal-audio: Return proper latency for stream

This patch implements get_latency() for output stream properly by
returning some meaningful value, i.e. calculated duration of single
media packet increased by fixed A2DP playback latency. This is the
same as PulseAudio does.
---
android/hal-audio.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index ddc6348..9fe46d6 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -35,6 +35,8 @@
#include "../profiles/audio/a2dp-codecs.h"
#include <sbc/sbc.h>

+#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
+
static const uint8_t a2dp_src_uuid[] = {
0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
@@ -125,6 +127,7 @@ struct sbc_data {
uint8_t *out_buf;

unsigned frame_duration;
+ unsigned frames_per_packet;

struct timespec start;
unsigned frames_sent;
@@ -151,6 +154,7 @@ static int sbc_cleanup(void *codec_data);
static int sbc_get_config(void *codec_data,
struct audio_input_config *config);
static size_t sbc_get_buffer_size(void *codec_data);
+static size_t sbc_get_mediapacket_duration(void *codec_data);
static void sbc_resume(void *codec_data);
static ssize_t sbc_write_data(void *codec_data, const void *buffer,
size_t bytes, int fd);
@@ -166,6 +170,7 @@ struct audio_codec {
int (*get_config) (void *codec_data,
struct audio_input_config *config);
size_t (*get_buffer_size) (void *codec_data);
+ size_t (*get_mediapacket_duration) (void *codec_data);
void (*resume) (void *codec_data);
ssize_t (*write_data) (void *codec_data, const void *buffer,
size_t bytes, int fd);
@@ -181,6 +186,7 @@ static const struct audio_codec audio_codecs[] = {
.cleanup = sbc_cleanup,
.get_config = sbc_get_config,
.get_buffer_size = sbc_get_buffer_size,
+ .get_mediapacket_duration = sbc_get_mediapacket_duration,
.resume = sbc_resume,
.write_data = sbc_write_data,
}
@@ -330,6 +336,7 @@ static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
sbc_data->out_buf = calloc(1, sbc_data->out_buf_size);

sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+ sbc_data->frames_per_packet = num_frames;

*codec_data = sbc_data;

@@ -387,6 +394,15 @@ static size_t sbc_get_buffer_size(void *codec_data)
return sbc_data->in_buf_size;
}

+static size_t sbc_get_mediapacket_duration(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ DBG("");
+
+ return sbc_data->frame_duration * sbc_data->frames_per_packet;
+}
+
static void sbc_resume(void *codec_data)
{
struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
@@ -948,8 +964,15 @@ static char *out_get_parameters(const struct audio_stream *stream,

static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+ struct audio_endpoint *ep = out->ep;
+ size_t pkt_duration;
+
DBG("");
- return 0;
+
+ pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data);
+
+ return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000;
}

static int out_set_volume(struct audio_stream_out *stream, float left,
--
1.8.5.2


2014-01-22 10:34:51

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 08/10] android/hal-audio: Read fd from Output Stream response

---
android/hal-audio.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 6e4adf3..81a452a 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -577,7 +577,7 @@ static int ipc_close_cmd(uint8_t endpoint_id)
return result;
}

-static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu,
+static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu, int *fd,
struct audio_preset **caps)
{
char buf[BLUEZ_AUDIO_MTU];
@@ -595,7 +595,7 @@ static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu,
cmd.id = endpoint_id;

result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
- sizeof(cmd), &cmd, &rsp_len, rsp, NULL);
+ sizeof(cmd), &cmd, &rsp_len, rsp, fd);

if (result == AUDIO_STATUS_SUCCESS) {
size_t buf_len = sizeof(struct audio_preset) +
@@ -992,6 +992,7 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
struct audio_preset *preset;
const struct audio_codec *codec;
uint16_t mtu;
+ int fd;

out = calloc(1, sizeof(struct a2dp_stream_out));
if (!out)
@@ -1019,13 +1020,15 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
/* TODO: for now we always use endpoint 0 */
out->ep = &audio_endpoints[0];

- if (ipc_open_stream_cmd(out->ep->id, &mtu, &preset) !=
+ if (ipc_open_stream_cmd(out->ep->id, &mtu, &fd, &preset) !=
AUDIO_STATUS_SUCCESS)
goto fail;

- if (!preset)
+ if (!preset || fd < 0)
goto fail;

+ out->ep->fd = fd;
+
codec = out->ep->codec;

codec->init(preset, mtu, &out->ep->codec_data);
@@ -1059,6 +1062,11 @@ static void audio_close_output_stream(struct audio_hw_device *dev,

ipc_close_stream_cmd(ep->id);

+ if (ep->fd >= 0) {
+ close(ep->fd);
+ ep->fd = -1;
+ }
+
ep->codec->cleanup(ep->codec_data);
ep->codec_data = NULL;

--
1.8.5.2


2014-01-22 10:34:50

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 07/10] android/hal-audio: Return proper buffer size to AudioFlinger

---
android/hal-audio.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index f147cd9..6e4adf3 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -135,6 +135,7 @@ static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
static int sbc_cleanup(void *codec_data);
static int sbc_get_config(void *codec_data,
struct audio_input_config *config);
+static size_t sbc_get_buffer_size(void *codec_data);
static void sbc_resume(void *codec_data);

struct audio_codec {
@@ -147,6 +148,7 @@ struct audio_codec {
int (*cleanup) (void *codec_data);
int (*get_config) (void *codec_data,
struct audio_input_config *config);
+ size_t (*get_buffer_size) (void *codec_data);
void (*resume) (void *codec_data);
ssize_t (*write_data) (void *codec_data, const void *buffer,
size_t bytes);
@@ -161,6 +163,7 @@ static const struct audio_codec audio_codecs[] = {
.init = sbc_codec_init,
.cleanup = sbc_cleanup,
.get_config = sbc_get_config,
+ .get_buffer_size = sbc_get_buffer_size,
.resume = sbc_resume,
}
};
@@ -357,6 +360,15 @@ static int sbc_get_config(void *codec_data,
return AUDIO_STATUS_SUCCESS;
}

+static size_t sbc_get_buffer_size(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ DBG("");
+
+ return sbc_data->in_buf_size;
+}
+
static void sbc_resume(void *codec_data)
{
struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
@@ -730,8 +742,11 @@ static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)

static size_t out_get_buffer_size(const struct audio_stream *stream)
{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
DBG("");
- return 20 * 512;
+
+ return out->ep->codec->get_buffer_size(out->ep->codec_data);
}

static uint32_t out_get_channels(const struct audio_stream *stream)
--
1.8.5.2


2014-01-22 10:34:48

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 05/10] android/hal-audio: Calculate SBC stream parameters

This patch adds necessary calculations for SBC stream parameters.

Both input and output buffers are expected to have exact amount of
data to fill single media packet (based on transport channel MTU).

Frame duration will be used to synchronize input and output streams.
---
android/hal-audio.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 97 insertions(+), 6 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 8d604db..57385aa 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -46,6 +46,66 @@ static pthread_t ipc_th = 0;
static pthread_mutex_t close_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;

+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
+
+ unsigned pt:7;
+ unsigned m:1;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ unsigned frame_count:4;
+ unsigned rfa0:1;
+ unsigned is_last_fragment:1;
+ unsigned is_first_fragment:1;
+ unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
+
+ unsigned m:1;
+ unsigned pt:7;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ unsigned is_fragmented:1;
+ unsigned is_first_fragment:1;
+ unsigned is_last_fragment:1;
+ unsigned rfa0:1;
+ unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct media_packet {
+ struct rtp_header hdr;
+ struct rtp_payload payload;
+ uint8_t data[0];
+};
+
struct audio_input_config {
uint32_t rate;
uint32_t channels;
@@ -56,10 +116,19 @@ struct sbc_data {
a2dp_sbc_t sbc;

sbc_t enc;
+
+ size_t in_frame_len;
+ size_t in_buf_size;
+
+ size_t out_buf_size;
+ uint8_t *out_buf;
+
+ unsigned frame_duration;
};

static int sbc_get_presets(struct audio_preset *preset, size_t *len);
-static int sbc_codec_init(struct audio_preset *preset, void **codec_data);
+static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
+ void **codec_data);
static int sbc_cleanup(void *codec_data);
static int sbc_get_config(void *codec_data,
struct audio_input_config *config);
@@ -69,7 +138,8 @@ struct audio_codec {

int (*get_presets) (struct audio_preset *preset, size_t *len);

- int (*init) (struct audio_preset *preset, void **codec_data);
+ int (*init) (struct audio_preset *preset, uint16_t mtu,
+ void **codec_data);
int (*cleanup) (void *codec_data);
int (*get_config) (void *codec_data,
struct audio_input_config *config);
@@ -200,9 +270,14 @@ static void sbc_init_encoder(struct sbc_data *sbc_data)
out->bitpool = in->max_bitpool;
}

-static int sbc_codec_init(struct audio_preset *preset, void **codec_data)
+static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
+ void **codec_data)
{
struct sbc_data *sbc_data;
+ size_t hdr_len = sizeof(struct media_packet);
+ size_t in_frame_len;
+ size_t out_frame_len;
+ size_t num_frames;

DBG("");

@@ -217,6 +292,18 @@ static int sbc_codec_init(struct audio_preset *preset, void **codec_data)

sbc_init_encoder(sbc_data);

+ in_frame_len = sbc_get_codesize(&sbc_data->enc);
+ out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+ num_frames = (mtu - hdr_len) / out_frame_len;
+
+ sbc_data->in_frame_len = in_frame_len;
+ sbc_data->in_buf_size = num_frames * in_frame_len;
+
+ sbc_data->out_buf_size = hdr_len + num_frames * out_frame_len;
+ sbc_data->out_buf = calloc(1, sbc_data->out_buf_size);
+
+ sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+
*codec_data = sbc_data;

return AUDIO_STATUS_SUCCESS;
@@ -229,6 +316,7 @@ static int sbc_cleanup(void *codec_data)
DBG("");

sbc_finish(&sbc_data->enc);
+ free(sbc_data->out_buf);
free(codec_data);

return AUDIO_STATUS_SUCCESS;
@@ -460,7 +548,7 @@ static int ipc_close_cmd(uint8_t endpoint_id)
return result;
}

-static int ipc_open_stream_cmd(uint8_t endpoint_id,
+static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu,
struct audio_preset **caps)
{
char buf[BLUEZ_AUDIO_MTU];
@@ -483,6 +571,7 @@ static int ipc_open_stream_cmd(uint8_t endpoint_id,
if (result == AUDIO_STATUS_SUCCESS) {
size_t buf_len = sizeof(struct audio_preset) +
rsp->preset[0].len;
+ *mtu = rsp->mtu;
*caps = malloc(buf_len);
memcpy(*caps, &rsp->preset, buf_len);
} else {
@@ -868,6 +957,7 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
struct a2dp_stream_out *out;
struct audio_preset *preset;
const struct audio_codec *codec;
+ uint16_t mtu;

out = calloc(1, sizeof(struct a2dp_stream_out));
if (!out)
@@ -895,7 +985,8 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
/* TODO: for now we always use endpoint 0 */
out->ep = &audio_endpoints[0];

- if (ipc_open_stream_cmd(out->ep->id, &preset) != AUDIO_STATUS_SUCCESS)
+ if (ipc_open_stream_cmd(out->ep->id, &mtu, &preset) !=
+ AUDIO_STATUS_SUCCESS)
goto fail;

if (!preset)
@@ -903,7 +994,7 @@ static int audio_open_output_stream(struct audio_hw_device *dev,

codec = out->ep->codec;

- codec->init(preset, &out->ep->codec_data);
+ codec->init(preset, mtu, &out->ep->codec_data);
codec->get_config(out->ep->codec_data, &out->cfg);

DBG("rate=%d channels=%d format=%d", out->cfg.rate,
--
1.8.5.2


2014-01-22 10:34:49

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 06/10] android/hal-audio: Add resume to codec callbacks

Once stream is resumed it may be required to reset some state of codec,
i.e. in case of SBC we need to reset monotonic clock and frames count
which are used for synchronization.
---
android/hal-audio.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 57385aa..f147cd9 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -124,6 +124,9 @@ struct sbc_data {
uint8_t *out_buf;

unsigned frame_duration;
+
+ struct timespec start;
+ unsigned frames_sent;
};

static int sbc_get_presets(struct audio_preset *preset, size_t *len);
@@ -132,6 +135,7 @@ static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
static int sbc_cleanup(void *codec_data);
static int sbc_get_config(void *codec_data,
struct audio_input_config *config);
+static void sbc_resume(void *codec_data);

struct audio_codec {
uint8_t type;
@@ -143,6 +147,7 @@ struct audio_codec {
int (*cleanup) (void *codec_data);
int (*get_config) (void *codec_data,
struct audio_input_config *config);
+ void (*resume) (void *codec_data);
ssize_t (*write_data) (void *codec_data, const void *buffer,
size_t bytes);
};
@@ -156,6 +161,7 @@ static const struct audio_codec audio_codecs[] = {
.init = sbc_codec_init,
.cleanup = sbc_cleanup,
.get_config = sbc_get_config,
+ .resume = sbc_resume,
}
};

@@ -351,6 +357,17 @@ static int sbc_get_config(void *codec_data,
return AUDIO_STATUS_SUCCESS;
}

+static void sbc_resume(void *codec_data)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ DBG("");
+
+ clock_gettime(CLOCK_MONOTONIC, &sbc_data->start);
+
+ sbc_data->frames_sent = 0;
+}
+
static void audio_ipc_cleanup(void)
{
if (audio_sk >= 0) {
@@ -673,6 +690,8 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
if (ipc_resume_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
return -1;

+ out->ep->codec->resume(out->ep->codec_data);
+
out->audio_state = AUDIO_A2DP_STATE_STARTED;
}

--
1.8.5.2


2014-01-22 10:34:45

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 02/10] android: Build Audio HAL with SBC

Build for Android requires libsbc sources to be available in
external/bluetooth/sbc. Build for host requires libsbc package to be
installed.
---
android/Android.mk | 37 +++++++++++++++++++++++++++++++++++--
android/Makefile.am | 2 ++
configure.ac | 7 +++++++
3 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/android/Android.mk b/android/Android.mk
index 43e6036..c274295 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -3,8 +3,9 @@ LOCAL_PATH := external/bluetooth
# Retrieve BlueZ version from configure.ac file
BLUEZ_VERSION := $(shell grep ^AC_INIT $(LOCAL_PATH)/bluez/configure.ac | cpp -P -D'AC_INIT(_,v)=v')

-# Specify pathmap for glib
-pathmap_INCL += glib:external/bluetooth/glib
+# Specify pathmap for glib and sbc
+pathmap_INCL += glib:external/bluetooth/glib \
+ sbc:external/bluetooth/sbc

# Specify common compiler flags
BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
@@ -225,9 +226,11 @@ LOCAL_SRC_FILES := bluez/android/hal-audio.c
LOCAL_C_INCLUDES = \
$(call include-path-for, system-core) \
$(call include-path-for, libhardware) \
+ $(call include-path-for, sbc) \

LOCAL_SHARED_LIBRARIES := \
libcutils \
+ libsbc \

LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) \

@@ -348,3 +351,33 @@ LOCAL_MODULE_TAGS := debug
LOCAL_MODULE := l2ping

include $(BUILD_EXECUTABLE)
+
+#
+# libsbc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ sbc/sbc/sbc.c \
+ sbc/sbc/sbc_primitives.c \
+ sbc/sbc/sbc_primitives_mmx.c \
+ sbc/sbc/sbc_primitives_neon.c \
+ sbc/sbc/sbc_primitives_armv6.c \
+ sbc/sbc/sbc_primitives_iwmmxt.c \
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/sbc/ \
+ $(LOCAL_PATH)/sbc/sbc/ \
+
+LOCAL_CFLAGS:= \
+ -Os \
+ -Wno-sign-compare \
+ -Wno-missing-field-initializers \
+ -Wno-unused-parameter \
+ -Wno-type-limits \
+ -Wno-empty-body \
+
+LOCAL_MODULE := libsbc
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android/Makefile.am b/android/Makefile.am
index 910beb5..9045a99 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -149,6 +149,8 @@ android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \

android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android

+android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
+
android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
-no-undefined -pthread

diff --git a/configure.ac b/configure.ac
index b5c87cc..f607d7a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -252,6 +252,13 @@ AC_ARG_ENABLE(android, AC_HELP_STRING([--enable-android],
[enable_android=${enableval}])
AM_CONDITIONAL(ANDROID, test "${enable_android}" = "yes")

+if (test "${enable_android}" = "yes"); then
+ PKG_CHECK_MODULES(SBC, sbc >= 1.2, dummy=yes,
+ AC_MSG_ERROR(SBC library >= 1.2 is required))
+ AC_SUBST(SBC_CFLAGS)
+ AC_SUBST(SBC_LIBS)
+fi
+
AC_DEFINE_UNQUOTED(ANDROID_STORAGEDIR, "${storagedir}/android",
[Directory for the Android daemon storage files])

--
1.8.5.2


2014-01-22 10:34:47

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 04/10] android/hal-audio: Initialize SBC encoder

---
android/hal-audio.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index f53dba0..8d604db 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -32,6 +32,7 @@
#include "hal-log.h"
#include "hal-msg.h"
#include "../profiles/audio/a2dp-codecs.h"
+#include <sbc/sbc.h>

static const uint8_t a2dp_src_uuid[] = {
0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
@@ -53,6 +54,8 @@ struct audio_input_config {

struct sbc_data {
a2dp_sbc_t sbc;
+
+ sbc_t enc;
};

static int sbc_get_presets(struct audio_preset *preset, size_t *len);
@@ -184,6 +187,19 @@ static int sbc_get_presets(struct audio_preset *preset, size_t *len)
return i;
}

+static void sbc_init_encoder(struct sbc_data *sbc_data)
+{
+ a2dp_sbc_t *in = &sbc_data->sbc;
+ sbc_t *out = &sbc_data->enc;
+
+ DBG("");
+
+ sbc_init_a2dp(out, 0L, in, sizeof(*in));
+
+ out->endian = SBC_LE;
+ out->bitpool = in->max_bitpool;
+}
+
static int sbc_codec_init(struct audio_preset *preset, void **codec_data)
{
struct sbc_data *sbc_data;
@@ -199,6 +215,8 @@ static int sbc_codec_init(struct audio_preset *preset, void **codec_data)

memcpy(&sbc_data->sbc, preset->data, preset->len);

+ sbc_init_encoder(sbc_data);
+
*codec_data = sbc_data;

return AUDIO_STATUS_SUCCESS;
@@ -206,8 +224,11 @@ static int sbc_codec_init(struct audio_preset *preset, void **codec_data)

static int sbc_cleanup(void *codec_data)
{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
DBG("");

+ sbc_finish(&sbc_data->enc);
free(codec_data);

return AUDIO_STATUS_SUCCESS;
--
1.8.5.2


2014-01-22 10:34:44

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v3 01/10] android: Add MTU data to Open Stream Audio IPC

MTU value for transport channel is sent in Open Stream response, which
is required to calculate number of frames which can be packed into
single media packet.

This is to avoid including GPLv2 licensed headers in Audio HAL
implementation.
---
android/a2dp.c | 8 ++++++--
android/audio-msg.h | 1 +
2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/android/a2dp.c b/android/a2dp.c
index a996f79..95ecf9b 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -1304,6 +1304,7 @@ static void bt_stream_open(const void *buf, uint16_t len)
struct audio_rsp_open_stream *rsp;
struct a2dp_setup *setup;
int fd;
+ uint16_t omtu;

DBG("");

@@ -1314,14 +1315,17 @@ static void bt_stream_open(const void *buf, uint16_t len)
return;
}

- if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, NULL, NULL)) {
+ if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu,
+ NULL)) {
error("avdtp_stream_get_transport: failed");
audio_ipc_send_rsp(AUDIO_OP_OPEN_STREAM, AUDIO_STATUS_FAILED);
return;
}

- len = sizeof(struct audio_preset) + setup->preset->len;
+ len = sizeof(struct audio_rsp_open_stream) +
+ sizeof(struct audio_preset) + setup->preset->len;
rsp = g_malloc0(len);
+ rsp->mtu = omtu;
rsp->preset->len = setup->preset->len;
memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);

diff --git a/android/audio-msg.h b/android/audio-msg.h
index 8f03274..17cde09 100644
--- a/android/audio-msg.h
+++ b/android/audio-msg.h
@@ -63,6 +63,7 @@ struct audio_cmd_open_stream {
} __attribute__((packed));

struct audio_rsp_open_stream {
+ uint16_t mtu;
struct audio_preset preset[0];
} __attribute__((packed));

--
1.8.5.2