2014-05-22 12:05:54

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 01/14] android/hal-sco: Use nanosleep for SCO synchronization

From: Andrei Emeltchenko <[email protected]>

---
android/hal-sco.c | 56 +++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 42 insertions(+), 14 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index ea9857e..fb7b4d4 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -64,6 +64,8 @@ struct sco_stream_out {
int fd;

uint8_t *downmix_buf;
+ size_t samples;
+ struct timespec start;

struct resampler_itfe *resampler;
int16_t *resample_buf;
@@ -277,6 +279,21 @@ static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer,
}
}

+static uint64_t timespec_diff_us(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 += 1000000000ll; /* 1sec */
+ }
+
+ return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
size_t bytes)
{
@@ -284,13 +301,13 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
size_t len, written = 0;
int ret;
uint16_t mtu = /* out->cfg.mtu */ 48;
- uint8_t read_buf[mtu];
- bool do_write = false;
+ uint64_t audio_sent_us, audio_passed_us;

pfd.fd = out->fd;
pfd.events = POLLOUT | POLLIN | POLLHUP | POLLNVAL;

while (bytes > written) {
+ struct timespec now;

/* poll for sending */
if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
@@ -303,27 +320,38 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
return false;
}

- /* FIXME synchronize by time instead of read() */
- if (pfd.revents & POLLIN) {
- ret = read(out->fd, read_buf, mtu);
- if (ret < 0) {
- error("Error reading fd %d (%s)", out->fd,
- strerror(errno));
- return false;
- }

- do_write = true;
+ clock_gettime(CLOCK_REALTIME, &now);
+ /* Mark start of the stream */
+ if (!out->samples)
+ memcpy(&out->start, &now, sizeof(out->start));
+
+ audio_sent_us = out->samples * 1000000ll / AUDIO_STREAM_SCO_RATE;
+ audio_passed_us = timespec_diff_us(&now, &out->start);
+ if ((int) (audio_sent_us - audio_passed_us) > 1500) {
+ struct timespec timeout = {0,
+ (audio_sent_us -
+ audio_passed_us) * 1000};
+ DBG("Sleeping for %d ms",
+ (int) (audio_sent_us - audio_passed_us));
+ nanosleep(&timeout, NULL);
+ } else if ((int)(audio_passed_us - audio_sent_us) > 50000) {
+ DBG("\n\nResync\n\n");
+ out->samples = 0;
+ memcpy(&out->start, &now, sizeof(out->start));
}

- if (!do_write)
- continue;

len = bytes - written > mtu ? mtu : bytes - written;

ret = write(out->fd, buffer + written, len);
if (ret > 0) {
written += ret;
- do_write = false;
+
+ out->samples += ret / 2;
+
+ DBG("written %d samples %zd total %zd bytes",
+ ret, out->samples, written);
continue;
}

--
1.8.3.2



2014-05-28 10:32:22

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFCv0 07/14] android/hal-sco: Use global sco file descriptor

Hi Andrei,

On Wed, May 28, 2014 at 1:24 PM, Andrei Emeltchenko
<[email protected]> wrote:
> Hi Luiz,
>
> On Tue, May 27, 2014 at 04:08:09PM +0300, Luiz Augusto von Dentz wrote:
>> Hi Andrei,
>>
>> On Thu, May 22, 2014 at 3:06 PM, Andrei Emeltchenko
>> <[email protected]> wrote:
>> > From: Andrei Emeltchenko <[email protected]>
>> >
>> > Android may open input/output stream independently so we use global sco
>> > file descriptor and mutexes.
>> > ---
>> > android/hal-sco.c | 85 ++++++++++++++++++++++++++++++++-----------------------
>> > 1 file changed, 50 insertions(+), 35 deletions(-)
>> >
>> > diff --git a/android/hal-sco.c b/android/hal-sco.c
>> > index e940547..8c2549b 100644
>> > --- a/android/hal-sco.c
>> > +++ b/android/hal-sco.c
>> > @@ -46,6 +46,10 @@
>> > static int listen_sk = -1;
>> > static int ipc_sk = -1;
>> >
>> > +static int sco_fd = -1;
>> > +static uint16_t sco_mtu = 0;
>> > +static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER;
>> > +
>>
>> Do we really need a mutex for this? I mean this is almost the same as
>> protecting the device itself for opening concurrent input and output,
>> if that is the case then I think it is better to put the mutex there.
>>
>
> For some reason Android tries to open several input streams from different
> threads. So input/output streams looks might be open/closed from different
> threads.

So the device itself has to be protected otherwise you risk having more mutexes.


--
Luiz Augusto von Dentz

2014-05-28 10:24:36

by Andrei Emeltchenko

[permalink] [raw]
Subject: Re: [RFCv0 07/14] android/hal-sco: Use global sco file descriptor

Hi Luiz,

On Tue, May 27, 2014 at 04:08:09PM +0300, Luiz Augusto von Dentz wrote:
> Hi Andrei,
>
> On Thu, May 22, 2014 at 3:06 PM, Andrei Emeltchenko
> <[email protected]> wrote:
> > From: Andrei Emeltchenko <[email protected]>
> >
> > Android may open input/output stream independently so we use global sco
> > file descriptor and mutexes.
> > ---
> > android/hal-sco.c | 85 ++++++++++++++++++++++++++++++++-----------------------
> > 1 file changed, 50 insertions(+), 35 deletions(-)
> >
> > diff --git a/android/hal-sco.c b/android/hal-sco.c
> > index e940547..8c2549b 100644
> > --- a/android/hal-sco.c
> > +++ b/android/hal-sco.c
> > @@ -46,6 +46,10 @@
> > static int listen_sk = -1;
> > static int ipc_sk = -1;
> >
> > +static int sco_fd = -1;
> > +static uint16_t sco_mtu = 0;
> > +static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER;
> > +
>
> Do we really need a mutex for this? I mean this is almost the same as
> protecting the device itself for opening concurrent input and output,
> if that is the case then I think it is better to put the mutex there.
>

For some reason Android tries to open several input streams from different
threads. So input/output streams looks might be open/closed from different
threads.

Best regards
Andrei Emeltchenko


2014-05-27 13:40:20

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFCv0 12/14] android/hal-sco: Fix memory leak

Hi Andrei,

On Thu, May 22, 2014 at 3:06 PM, Andrei Emeltchenko
<[email protected]> wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Release resampler on exit.
> ---
> android/hal-sco.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/android/hal-sco.c b/android/hal-sco.c
> index 8635866..bf3b6d4 100644
> --- a/android/hal-sco.c
> +++ b/android/hal-sco.c
> @@ -692,6 +692,8 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
>
> return 0;
> failed:
> + if (out->resampler)
> + release_resampler(out->resampler);
> free(out->cache);
> free(out->downmix_buf);
> free(out);
> @@ -726,6 +728,9 @@ static void sco_close_output_stream(struct audio_hw_device *dev,
>
> close_sco_socket();
>
> + if (out->resampler)
> + release_resampler(out->resampler);
> + free(out->resample_buf);
> free(out->cache);
> free(out->downmix_buf);
> free(out);
> --
> 1.8.3.2

Ive pushed just this one, please rebase this set.


--
Luiz Augusto von Dentz

2014-05-27 13:26:45

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFCv0 01/14] android/hal-sco: Use nanosleep for SCO synchronization

Hi Andrei,

On Thu, May 22, 2014 at 3:05 PM, Andrei Emeltchenko
<[email protected]> wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> android/hal-sco.c | 56 +++++++++++++++++++++++++++++++++++++++++--------------
> 1 file changed, 42 insertions(+), 14 deletions(-)
>
> diff --git a/android/hal-sco.c b/android/hal-sco.c
> index ea9857e..fb7b4d4 100644
> --- a/android/hal-sco.c
> +++ b/android/hal-sco.c
> @@ -64,6 +64,8 @@ struct sco_stream_out {
> int fd;
>
> uint8_t *downmix_buf;
> + size_t samples;
> + struct timespec start;
>
> struct resampler_itfe *resampler;
> int16_t *resample_buf;
> @@ -277,6 +279,21 @@ static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer,
> }
> }
>
> +static uint64_t timespec_diff_us(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 += 1000000000ll; /* 1sec */
> + }
> +
> + return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
> +}
> +
> static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
> size_t bytes)
> {
> @@ -284,13 +301,13 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
> size_t len, written = 0;
> int ret;
> uint16_t mtu = /* out->cfg.mtu */ 48;
> - uint8_t read_buf[mtu];
> - bool do_write = false;
> + uint64_t audio_sent_us, audio_passed_us;
>
> pfd.fd = out->fd;
> pfd.events = POLLOUT | POLLIN | POLLHUP | POLLNVAL;
>
> while (bytes > written) {
> + struct timespec now;
>
> /* poll for sending */
> if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
> @@ -303,27 +320,38 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
> return false;
> }
>
> - /* FIXME synchronize by time instead of read() */
> - if (pfd.revents & POLLIN) {
> - ret = read(out->fd, read_buf, mtu);
> - if (ret < 0) {
> - error("Error reading fd %d (%s)", out->fd,
> - strerror(errno));
> - return false;
> - }
>
> - do_write = true;
> + clock_gettime(CLOCK_REALTIME, &now);

Im not sure why did you choose to got with CLOCK_REALTIME, we used
CLOCK_MONOTONIC on hal-audio.c for a very important reason because it
is nonsettable.

> + /* Mark start of the stream */
> + if (!out->samples)
> + memcpy(&out->start, &now, sizeof(out->start));
> +
> + audio_sent_us = out->samples * 1000000ll / AUDIO_STREAM_SCO_RATE;
> + audio_passed_us = timespec_diff_us(&now, &out->start);
> + if ((int) (audio_sent_us - audio_passed_us) > 1500) {

What is 1500 for?

> + struct timespec timeout = {0,
> + (audio_sent_us -
> + audio_passed_us) * 1000};
> + DBG("Sleeping for %d ms",
> + (int) (audio_sent_us - audio_passed_us));
> + nanosleep(&timeout, NULL);

Also we should use clock_nanosleep just as hal-audio.c.

> + } else if ((int)(audio_passed_us - audio_sent_us) > 50000) {
> + DBG("\n\nResync\n\n");
> + out->samples = 0;
> + memcpy(&out->start, &now, sizeof(out->start));
> }
>
> - if (!do_write)
> - continue;
>
> len = bytes - written > mtu ? mtu : bytes - written;
>
> ret = write(out->fd, buffer + written, len);
> if (ret > 0) {
> written += ret;
> - do_write = false;
> +
> + out->samples += ret / 2;
> +
> + DBG("written %d samples %zd total %zd bytes",
> + ret, out->samples, written);
> continue;
> }
>
> --
> 1.8.3.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Luiz Augusto von Dentz

2014-05-27 13:08:09

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFCv0 07/14] android/hal-sco: Use global sco file descriptor

Hi Andrei,

On Thu, May 22, 2014 at 3:06 PM, Andrei Emeltchenko
<[email protected]> wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Android may open input/output stream independently so we use global sco
> file descriptor and mutexes.
> ---
> android/hal-sco.c | 85 ++++++++++++++++++++++++++++++++-----------------------
> 1 file changed, 50 insertions(+), 35 deletions(-)
>
> diff --git a/android/hal-sco.c b/android/hal-sco.c
> index e940547..8c2549b 100644
> --- a/android/hal-sco.c
> +++ b/android/hal-sco.c
> @@ -46,6 +46,10 @@
> static int listen_sk = -1;
> static int ipc_sk = -1;
>
> +static int sco_fd = -1;
> +static uint16_t sco_mtu = 0;
> +static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER;
> +

Do we really need a mutex for this? I mean this is almost the same as
protecting the device itself for opening concurrent input and output,
if that is the case then I think it is better to put the mutex there.

> static pthread_t ipc_th = 0;
> static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
>
> @@ -53,7 +57,6 @@ struct sco_audio_config {
> uint32_t rate;
> uint32_t channels;
> uint32_t frame_num;
> - uint16_t mtu;
> audio_format_t format;
> };
>
> @@ -61,7 +64,6 @@ struct sco_stream_out {
> struct audio_stream_out stream;
>
> struct sco_audio_config cfg;
> - int fd;
>
> uint8_t *downmix_buf;
> uint8_t *cache;
> @@ -79,7 +81,6 @@ struct sco_stream_in {
> struct audio_stream_in stream;
>
> struct sco_audio_config cfg;
> - int fd;
> };
>
> struct sco_dev {
> @@ -257,18 +258,25 @@ failed:
> return SCO_STATUS_FAILED;
> }
>
> -static int ipc_connect_sco(int *fd, uint16_t *mtu)
> +static int ipc_connect_sco(void)
> {
> struct sco_rsp_connect rsp;
> size_t rsp_len = sizeof(rsp);
> - int ret;
> + int ret = SCO_STATUS_SUCCESS;
>
> DBG("");
>
> - ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_CONNECT, 0, NULL, &rsp_len,
> - &rsp, fd);
> + pthread_mutex_lock(&sco_mutex);
> +
> + if (sco_fd < 0) {
> + ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_CONNECT, 0, NULL,
> + &rsp_len, &rsp, &sco_fd);
> +
> + /* Sometimes mtu returned is wrong */
> + sco_mtu = /* rsp.mtu */ 48;
> + }
>
> - *mtu = rsp.mtu;
> + pthread_mutex_unlock(&sco_mutex);
>
> return ret;
> }
> @@ -311,28 +319,27 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
> struct pollfd pfd;
> size_t len, written = 0;
> int ret;
> - uint16_t mtu = out->cfg.mtu;
> uint8_t *p;
> uint64_t audio_sent_us, audio_passed_us;
>
> - pfd.fd = out->fd;
> - pfd.events = POLLOUT | POLLIN | POLLHUP | POLLNVAL;
> + pfd.fd = sco_fd;
> + pfd.events = POLLOUT | POLLHUP | POLLNVAL;
>
> while (bytes > written) {
> struct timespec now;
>
> /* poll for sending */
> if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
> - DBG("timeout fd %d", out->fd);
> + DBG("timeout fd %d", sco_fd);
> return false;
> }
>
> if (pfd.revents & (POLLHUP | POLLNVAL)) {
> - error("error fd %d, events 0x%x", out->fd, pfd.revents);
> + error("error fd %d, events 0x%x", sco_fd, pfd.revents);
> return false;
> }
>
> - len = bytes - written > mtu ? mtu : bytes - written;
> + len = bytes - written > sco_mtu ? sco_mtu : bytes - written;
>
> clock_gettime(CLOCK_REALTIME, &now);
> /* Mark start of the stream */
> @@ -357,11 +364,11 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
> if (out->cache_len) {
> DBG("First packet cache_len %zd", out->cache_len);
> memcpy(out->cache + out->cache_len, buffer,
> - mtu - out->cache_len);
> + sco_mtu - out->cache_len);
> p = out->cache;
> }
>
> - if (bytes - written >= mtu)
> + if (bytes - written >= sco_mtu)
> p = (void *) buffer + written;
> else {
> memcpy(out->cache, buffer + written, bytes - written);
> @@ -371,10 +378,10 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
> continue;
> }
>
> - ret = write(out->fd, p, len);
> + ret = write(sco_fd, p, len);
> if (ret > 0) {
> if (out->cache_len) {
> - written = mtu - out->cache_len;
> + written = sco_mtu - out->cache_len;
> out->cache_len = 0;
> } else
> written += ret;
> @@ -394,7 +401,7 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
>
> if (errno != EINTR) {
> ret = errno;
> - error("write failed (%d) fd %d bytes %zd", ret, out->fd,
> + error("write failed (%d) fd %d bytes %zd", ret, sco_fd,
> bytes);
> return false;
> }
> @@ -414,7 +421,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
> void *send_buf = out->downmix_buf;
> size_t total;
>
> - DBG("write to fd %d bytes %zu", out->fd, bytes);
> + DBG("write to fd %d bytes %zu", sco_fd, bytes);
>
> if (!out->downmix_buf) {
> error("sco: downmix buffer not initialized");
> @@ -585,19 +592,17 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
> {
> struct sco_dev *adev = (struct sco_dev *) dev;
> struct sco_stream_out *out;
> - int fd = -1;
> int chan_num, ret;
> size_t resample_size;
> - uint16_t mtu;
>
> DBG("config %p device flags 0x%02x", config, devices);
>
> - if (ipc_connect_sco(&fd, &mtu) != SCO_STATUS_SUCCESS) {
> + if (ipc_connect_sco() != SCO_STATUS_SUCCESS) {
> error("sco: cannot get fd");
> return -EIO;
> }
>
> - DBG("got sco fd %d mtu %u", fd, mtu);
> + DBG("got sco fd %d mtu %u", sco_fd, sco_mtu);
>
> out = calloc(1, sizeof(struct sco_stream_out));
> if (!out)
> @@ -636,16 +641,13 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
>
> out->cfg.frame_num = OUT_STREAM_FRAMES;
>
> - /* we get wrong mtu size for some reason */
> - out->cfg.mtu = /* mtu */ 48;
> -
> out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common));
> if (!out->downmix_buf) {
> free(out);
> return -ENOMEM;
> }
>
> - out->cache = malloc(out->cfg.mtu);
> + out->cache = malloc(sco_mtu);
> if (!out->cache) {
> free(out->downmix_buf);
> free(out);
> @@ -691,7 +693,6 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
>
> *stream_out = &out->stream;
> adev->out = out;
> - out->fd = fd;
>
> return 0;
> failed:
> @@ -704,18 +705,30 @@ failed:
> return ret;
> }
>
> +static void close_sco_socket(void)
> +{
> + DBG("");
> +
> + pthread_mutex_lock(&sco_mutex);
> +
> + if (sco_fd >= 0) {
> + shutdown(sco_fd, SHUT_RDWR);
> + close(sco_fd);
> + sco_fd = -1;
> + }
> +
> + pthread_mutex_unlock(&sco_mutex);
> +}
> +
> static void sco_close_output_stream(struct audio_hw_device *dev,
> struct audio_stream_out *stream_out)
> {
> struct sco_dev *sco_dev = (struct sco_dev *) dev;
> struct sco_stream_out *out = (struct sco_stream_out *) stream_out;
>
> - DBG("dev %p stream %p fd %d", dev, out, sco_dev->out->fd);
> + DBG("dev %p stream %p fd %d", dev, out, sco_fd);
>
> - if (out && out->fd >= 0) {
> - close(out->fd);
> - out->fd = -1;
> - }
> + close_sco_socket();
>
> free(out->cache);
> free(out->downmix_buf);
> @@ -965,7 +978,9 @@ static void sco_close_input_stream(struct audio_hw_device *dev,
> struct sco_dev *sco_dev = (struct sco_dev *) dev;
> struct sco_stream_in *in = (struct sco_stream_in *) stream_in;
>
> - DBG("dev %p stream %p fd %d", dev, in, in->fd);
> + DBG("dev %p stream %p fd %d", dev, in, sco_fd);
> +
> + close_sco_socket();
>
> free(in);
> sco_dev->in = NULL;
> --
> 1.8.3.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Luiz Augusto von Dentz

2014-05-22 12:06:00

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 07/14] android/hal-sco: Use global sco file descriptor

From: Andrei Emeltchenko <[email protected]>

Android may open input/output stream independently so we use global sco
file descriptor and mutexes.
---
android/hal-sco.c | 85 ++++++++++++++++++++++++++++++++-----------------------
1 file changed, 50 insertions(+), 35 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index e940547..8c2549b 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -46,6 +46,10 @@
static int listen_sk = -1;
static int ipc_sk = -1;

+static int sco_fd = -1;
+static uint16_t sco_mtu = 0;
+static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER;
+
static pthread_t ipc_th = 0;
static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;

@@ -53,7 +57,6 @@ struct sco_audio_config {
uint32_t rate;
uint32_t channels;
uint32_t frame_num;
- uint16_t mtu;
audio_format_t format;
};

@@ -61,7 +64,6 @@ struct sco_stream_out {
struct audio_stream_out stream;

struct sco_audio_config cfg;
- int fd;

uint8_t *downmix_buf;
uint8_t *cache;
@@ -79,7 +81,6 @@ struct sco_stream_in {
struct audio_stream_in stream;

struct sco_audio_config cfg;
- int fd;
};

struct sco_dev {
@@ -257,18 +258,25 @@ failed:
return SCO_STATUS_FAILED;
}

-static int ipc_connect_sco(int *fd, uint16_t *mtu)
+static int ipc_connect_sco(void)
{
struct sco_rsp_connect rsp;
size_t rsp_len = sizeof(rsp);
- int ret;
+ int ret = SCO_STATUS_SUCCESS;

DBG("");

- ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_CONNECT, 0, NULL, &rsp_len,
- &rsp, fd);
+ pthread_mutex_lock(&sco_mutex);
+
+ if (sco_fd < 0) {
+ ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_CONNECT, 0, NULL,
+ &rsp_len, &rsp, &sco_fd);
+
+ /* Sometimes mtu returned is wrong */
+ sco_mtu = /* rsp.mtu */ 48;
+ }

- *mtu = rsp.mtu;
+ pthread_mutex_unlock(&sco_mutex);

return ret;
}
@@ -311,28 +319,27 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
struct pollfd pfd;
size_t len, written = 0;
int ret;
- uint16_t mtu = out->cfg.mtu;
uint8_t *p;
uint64_t audio_sent_us, audio_passed_us;

- pfd.fd = out->fd;
- pfd.events = POLLOUT | POLLIN | POLLHUP | POLLNVAL;
+ pfd.fd = sco_fd;
+ pfd.events = POLLOUT | POLLHUP | POLLNVAL;

while (bytes > written) {
struct timespec now;

/* poll for sending */
if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
- DBG("timeout fd %d", out->fd);
+ DBG("timeout fd %d", sco_fd);
return false;
}

if (pfd.revents & (POLLHUP | POLLNVAL)) {
- error("error fd %d, events 0x%x", out->fd, pfd.revents);
+ error("error fd %d, events 0x%x", sco_fd, pfd.revents);
return false;
}

- len = bytes - written > mtu ? mtu : bytes - written;
+ len = bytes - written > sco_mtu ? sco_mtu : bytes - written;

clock_gettime(CLOCK_REALTIME, &now);
/* Mark start of the stream */
@@ -357,11 +364,11 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
if (out->cache_len) {
DBG("First packet cache_len %zd", out->cache_len);
memcpy(out->cache + out->cache_len, buffer,
- mtu - out->cache_len);
+ sco_mtu - out->cache_len);
p = out->cache;
}

- if (bytes - written >= mtu)
+ if (bytes - written >= sco_mtu)
p = (void *) buffer + written;
else {
memcpy(out->cache, buffer + written, bytes - written);
@@ -371,10 +378,10 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
continue;
}

- ret = write(out->fd, p, len);
+ ret = write(sco_fd, p, len);
if (ret > 0) {
if (out->cache_len) {
- written = mtu - out->cache_len;
+ written = sco_mtu - out->cache_len;
out->cache_len = 0;
} else
written += ret;
@@ -394,7 +401,7 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,

if (errno != EINTR) {
ret = errno;
- error("write failed (%d) fd %d bytes %zd", ret, out->fd,
+ error("write failed (%d) fd %d bytes %zd", ret, sco_fd,
bytes);
return false;
}
@@ -414,7 +421,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
void *send_buf = out->downmix_buf;
size_t total;

- DBG("write to fd %d bytes %zu", out->fd, bytes);
+ DBG("write to fd %d bytes %zu", sco_fd, bytes);

if (!out->downmix_buf) {
error("sco: downmix buffer not initialized");
@@ -585,19 +592,17 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
{
struct sco_dev *adev = (struct sco_dev *) dev;
struct sco_stream_out *out;
- int fd = -1;
int chan_num, ret;
size_t resample_size;
- uint16_t mtu;

DBG("config %p device flags 0x%02x", config, devices);

- if (ipc_connect_sco(&fd, &mtu) != SCO_STATUS_SUCCESS) {
+ if (ipc_connect_sco() != SCO_STATUS_SUCCESS) {
error("sco: cannot get fd");
return -EIO;
}

- DBG("got sco fd %d mtu %u", fd, mtu);
+ DBG("got sco fd %d mtu %u", sco_fd, sco_mtu);

out = calloc(1, sizeof(struct sco_stream_out));
if (!out)
@@ -636,16 +641,13 @@ static int sco_open_output_stream(struct audio_hw_device *dev,

out->cfg.frame_num = OUT_STREAM_FRAMES;

- /* we get wrong mtu size for some reason */
- out->cfg.mtu = /* mtu */ 48;
-
out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common));
if (!out->downmix_buf) {
free(out);
return -ENOMEM;
}

- out->cache = malloc(out->cfg.mtu);
+ out->cache = malloc(sco_mtu);
if (!out->cache) {
free(out->downmix_buf);
free(out);
@@ -691,7 +693,6 @@ static int sco_open_output_stream(struct audio_hw_device *dev,

*stream_out = &out->stream;
adev->out = out;
- out->fd = fd;

return 0;
failed:
@@ -704,18 +705,30 @@ failed:
return ret;
}

+static void close_sco_socket(void)
+{
+ DBG("");
+
+ pthread_mutex_lock(&sco_mutex);
+
+ if (sco_fd >= 0) {
+ shutdown(sco_fd, SHUT_RDWR);
+ close(sco_fd);
+ sco_fd = -1;
+ }
+
+ pthread_mutex_unlock(&sco_mutex);
+}
+
static void sco_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream_out)
{
struct sco_dev *sco_dev = (struct sco_dev *) dev;
struct sco_stream_out *out = (struct sco_stream_out *) stream_out;

- DBG("dev %p stream %p fd %d", dev, out, sco_dev->out->fd);
+ DBG("dev %p stream %p fd %d", dev, out, sco_fd);

- if (out && out->fd >= 0) {
- close(out->fd);
- out->fd = -1;
- }
+ close_sco_socket();

free(out->cache);
free(out->downmix_buf);
@@ -965,7 +978,9 @@ static void sco_close_input_stream(struct audio_hw_device *dev,
struct sco_dev *sco_dev = (struct sco_dev *) dev;
struct sco_stream_in *in = (struct sco_stream_in *) stream_in;

- DBG("dev %p stream %p fd %d", dev, in, in->fd);
+ DBG("dev %p stream %p fd %d", dev, in, sco_fd);
+
+ close_sco_socket();

free(in);
sco_dev->in = NULL;
--
1.8.3.2


2014-05-22 12:06:04

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 11/14] android/hal-sco: Make debug more readable

From: Andrei Emeltchenko <[email protected]>

---
android/hal-sco.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index 8c2549b..8635866 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -654,8 +654,6 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
return -ENOMEM;
}

- DBG("size %zd", out_get_buffer_size(&out->stream.common));
-
/* Channel numbers for resampler */
chan_num = 1;

@@ -667,9 +665,6 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
goto failed;
}

- DBG("Created resampler: input rate [%d] output rate [%d] channels [%d]",
- out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num);
-
out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
out->cfg.rate,
out->cfg.frame_num, 1);
@@ -688,8 +683,9 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
goto failed;
}

- DBG("resampler: frame num %u buf size %zd bytes",
- out->resample_frame_num, resample_size);
+ DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+ out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+ out->resample_frame_num, resample_size);

*stream_out = &out->stream;
adev->out = out;
--
1.8.3.2


2014-05-22 12:06:03

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 10/14] android/haltest: Add loop command

From: Andrei Emeltchenko <[email protected]>

loop command makes stream_in->read() and then stream_out->write(). At
the moment buffers shall be equal.
---
android/client/if-sco.c | 43 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 41 insertions(+), 2 deletions(-)

diff --git a/android/client/if-sco.c b/android/client/if-sco.c
index b4c3506..87e2d79 100644
--- a/android/client/if-sco.c
+++ b/android/client/if-sco.c
@@ -148,6 +148,11 @@ static int feed_from_generator(short *buffer, void *data)
return buffer_size;
}

+static int feed_from_in(short *buffer, void *data)
+{
+ return stream_in->read(stream_in, buffer, buffer_size);
+}
+
static void prepare_sample(void)
{
int x;
@@ -179,8 +184,12 @@ static void *playback_thread(void *data)

/* Use file or fall back to generator */
if (in) {
- filbuff_cb = feed_from_file;
- cb_data = in;
+ if (data == stream_in)
+ filbuff_cb = feed_from_in;
+ else {
+ filbuff_cb = feed_from_file;
+ cb_data = in;
+ }
} else {
prepare_sample();
filbuff_cb = feed_from_generator;
@@ -315,6 +324,35 @@ fail:
fclose(in);
}

+static void loop_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_out);
+ RETURN_IF_NULL(stream_in);
+
+ if (!buffer_size || !buffer_size_in) {
+ haltest_error("Invalid buffer sizes. Streams opened\n");
+ return;
+ }
+
+ if (buffer_size != buffer_size_in) {
+ haltest_error("read/write buffers differ, not supported\n");
+ return;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ haltest_error("Already playing or stream suspended!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (pthread_create(&play_thread, NULL, playback_thread,
+ stream_in) != 0)
+ haltest_error("Cannot create playback thread!\n");
+}
+
static void read_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_audio_sco);
@@ -615,6 +653,7 @@ static struct method methods[] = {
STD_METHOD(close_input_stream),
STD_METHODH(play, "<path to pcm file>"),
STD_METHOD(read),
+ STD_METHOD(loop),
STD_METHOD(stop),
STD_METHOD(suspend),
STD_METHOD(resume),
--
1.8.3.2


2014-05-22 12:06:02

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 09/14] android/haltest: Add read command.

From: Andrei Emeltchenko <[email protected]>

Read command makes stream_in->read() call to Audio HAL.
---
android/client/if-sco.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)

diff --git a/android/client/if-sco.c b/android/client/if-sco.c
index c71d0b4..b4c3506 100644
--- a/android/client/if-sco.c
+++ b/android/client/if-sco.c
@@ -230,6 +230,45 @@ static void *playback_thread(void *data)
return NULL;
}

+static void *read_thread(void *data)
+{
+ int (*filbuff_cb) (short*, void*) = feed_from_in;
+ short buffer[buffer_size / sizeof(short)];
+ size_t len = 0;
+ void *cb_data = NULL;
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_PLAYING;
+ pthread_mutex_unlock(&state_mutex);
+
+ do {
+ pthread_mutex_lock(&state_mutex);
+
+ if (current_state == STATE_STOPPING) {
+ haltest_info("Detected stopping\n");
+ pthread_mutex_unlock(&state_mutex);
+ break;
+ } else if (current_state == STATE_SUSPENDED) {
+ pthread_mutex_unlock(&state_mutex);
+ usleep(500);
+ continue;
+ }
+
+ pthread_mutex_unlock(&state_mutex);
+
+ len = filbuff_cb(buffer, cb_data);
+ haltest_info("len %zd\n", len);
+ } while (len);
+
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_STOPPED;
+ pthread_mutex_unlock(&state_mutex);
+
+ haltest_info("Done reading.\n");
+
+ return NULL;
+}
+
static void play_p(int argc, const char **argv)
{
const char *fname = NULL;
@@ -276,6 +315,30 @@ fail:
fclose(in);
}

+static void read_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_in);
+
+ if (!buffer_size_in) {
+ haltest_error("Invalid buffer sizes. Streams opened\n");
+ return;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ haltest_error("Already playing or stream suspended!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (pthread_create(&play_thread, NULL, read_thread,
+ stream_in) != 0)
+ haltest_error("Cannot create playback thread!\n");
+
+}
+
static void stop_p(int argc, const char **argv)
{
pthread_mutex_lock(&state_mutex);
@@ -551,6 +614,7 @@ static struct method methods[] = {
STD_METHOD(open_input_stream),
STD_METHOD(close_input_stream),
STD_METHODH(play, "<path to pcm file>"),
+ STD_METHOD(read),
STD_METHOD(stop),
STD_METHOD(suspend),
STD_METHOD(resume),
--
1.8.3.2


2014-05-22 12:06:06

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 13/14] android/hal-sco: Implement read

From: Andrei Emeltchenko <[email protected]>

Add read and resampling from 8000 to 44100.
---
android/hal-sco.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 147 insertions(+), 2 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index bf3b6d4..8c40043 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -40,6 +40,7 @@

#define OUT_BUFFER_SIZE 2560
#define OUT_STREAM_FRAMES 2560
+#define IN_STREAM_FRAMES /* 5292 */ 5120

#define SOCKET_POLL_TIMEOUT_MS 500

@@ -81,6 +82,10 @@ struct sco_stream_in {
struct audio_stream_in stream;

struct sco_audio_config cfg;
+
+ struct resampler_itfe *resampler;
+ int16_t *resample_buf;
+ uint32_t resample_frame_num;
};

struct sco_dev {
@@ -907,12 +912,109 @@ static int in_set_gain(struct audio_stream_in *stream, float gain)
return -ENOSYS;
}

+static bool read_data(struct sco_stream_in *in, char *buffer, size_t bytes)
+{
+ struct pollfd pfd;
+ size_t len, read_bytes = 0;
+
+ pfd.fd = sco_fd;
+ pfd.events = POLLIN | POLLHUP | POLLNVAL;
+
+ while (bytes > read_bytes) {
+ int ret;
+
+ /* poll for reading */
+ if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+ DBG("timeout fd %d", sco_fd);
+ return false;
+ }
+
+ if (pfd.revents & (POLLHUP | POLLNVAL)) {
+ error("error fd %d, events 0x%x", sco_fd, pfd.revents);
+ return false;
+ }
+
+ len = bytes - read_bytes > sco_mtu ? sco_mtu :
+ bytes - read_bytes;
+
+ ret = read(sco_fd, buffer + read_bytes, len);
+ if (ret > 0) {
+ read_bytes += ret;
+ DBG("read %d total %zd", ret, read_bytes);
+ continue;
+ }
+
+ if (errno == EAGAIN) {
+ ret = errno;
+ warn("read failed (%d)", ret);
+ continue;
+ }
+
+ if (errno != EINTR) {
+ ret = errno;
+ error("read failed (%d) fd %d bytes %zd", ret, sco_fd,
+ bytes);
+ return false;
+ }
+ }
+
+ DBG("read %zd bytes", read_bytes);
+
+ return true;
+}
+
static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
size_t bytes)
{
- DBG("");
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+ size_t frame_size = audio_stream_frame_size(&stream->common);
+ size_t frame_num = bytes / frame_size;
+ size_t input_frame_num = frame_num;
+ void *read_buf = buffer;
+ size_t total, read_frames;
+ int ret;

- return -ENOSYS;
+ DBG("Read from fd %d bytes %zu", sco_fd, bytes);
+
+ if (!in->resampler && in->cfg.rate != AUDIO_STREAM_SCO_RATE) {
+ error("Cannot find resampler");
+ return -1;
+ }
+
+ if (in->resampler) {
+ input_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+ in->cfg.rate,
+ frame_num, 0);
+ if (input_frame_num > in->resample_frame_num) {
+ DBG("resize input frames from %zd to %d",
+ input_frame_num, in->resample_frame_num);
+ input_frame_num = in->resample_frame_num;
+ }
+
+ read_buf = in->resample_buf;
+ }
+
+ total = input_frame_num * sizeof(int16_t) * 1;
+
+ if(!read_data(in, read_buf, total))
+ return -1;
+
+ read_frames = input_frame_num;
+
+ ret = in->resampler->resample_from_input(in->resampler,
+ in->resample_buf,
+ &read_frames,
+ (int16_t *) buffer,
+ &frame_num);
+ if (ret) {
+ error("Failed to resample frames: %zd input %zd (%s)",
+ frame_num, input_frame_num, strerror(ret));
+ return -1;
+ }
+
+ DBG("resampler: remain %zd output %zd frames", read_frames, frame_num);
+
+ return bytes;
}

static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
@@ -930,6 +1032,8 @@ static int sco_open_input_stream(struct audio_hw_device *dev,
{
struct sco_dev *sco_dev = (struct sco_dev *) dev;
struct sco_stream_in *in;
+ int chan_num, ret;
+ size_t resample_size;

DBG("");

@@ -967,10 +1071,48 @@ static int sco_open_input_stream(struct audio_hw_device *dev,
in->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
}

+ in->cfg.frame_num = IN_STREAM_FRAMES;
+
+ /* Channel numbers for resampler */
+ chan_num = 1;
+
+ ret = create_resampler(AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+ RESAMPLER_QUALITY_DEFAULT, NULL,
+ &in->resampler);
+ if (ret) {
+ error("Failed to create resampler (%s)", strerror(ret));
+ goto failed;
+ }
+
+ in->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+ in->cfg.rate,
+ in->cfg.frame_num, 0);
+
+ resample_size = sizeof(int16_t) * chan_num * in->resample_frame_num;
+
+ in->resample_buf = malloc(resample_size);
+ if (!in->resample_buf) {
+ error("failed to allocate resample buffer for %d frames",
+ in->resample_frame_num);
+ goto failed;
+ }
+
+ DBG("Resampler: input %d output %d chan %d frames %u size %zd",
+ AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num,
+ in->resample_frame_num, resample_size);
+
*stream_in = &in->stream;
sco_dev->in = in;

return 0;
+failed:
+ if (in->resampler)
+ release_resampler(in->resampler);
+ free(in);
+ stream_in = NULL;
+ sco_dev->in = NULL;
+
+ return ret;
}

static void sco_close_input_stream(struct audio_hw_device *dev,
@@ -983,6 +1125,9 @@ static void sco_close_input_stream(struct audio_hw_device *dev,

close_sco_socket();

+ if (in->resampler)
+ release_resampler(in->resampler);
+ free(in->resample_buf);
free(in);
sco_dev->in = NULL;
}
--
1.8.3.2


2014-05-22 12:06:01

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 08/14] android/haltest: Add open/close input stream commands

From: Andrei Emeltchenko <[email protected]>

---
android/client/if-sco.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)

diff --git a/android/client/if-sco.c b/android/client/if-sco.c
index b7f5a80..c71d0b4 100644
--- a/android/client/if-sco.c
+++ b/android/client/if-sco.c
@@ -23,8 +23,10 @@

audio_hw_device_t *if_audio_sco = NULL;
static struct audio_stream_out *stream_out = NULL;
+static struct audio_stream_in *stream_in = NULL;

static size_t buffer_size = 0;
+static size_t buffer_size_in = 0;
static pthread_t play_thread = 0;
static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -340,6 +342,53 @@ static void close_output_stream_p(int argc, const char **argv)
buffer_size = 0;
}

+static void open_input_stream_p(int argc, const char **argv)
+{
+ int err;
+
+ RETURN_IF_NULL(if_audio_sco);
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state == STATE_PLAYING) {
+ haltest_error("Already playing!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ err = if_audio_sco->open_input_stream(if_audio_sco,
+ 0,
+ AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+ NULL,
+ &stream_in);
+ if (err < 0) {
+ haltest_error("open output stream returned %d\n", err);
+ return;
+ }
+
+ buffer_size_in = stream_in->common.get_buffer_size(&stream_in->common);
+ if (buffer_size_in == 0)
+ haltest_error("Invalid buffer size received!\n");
+ else
+ haltest_info("Using buffer size: %zu\n", buffer_size_in);
+}
+
+static void close_input_stream_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio_sco);
+ RETURN_IF_NULL(stream_in);
+
+ stop_p(argc, argv);
+
+ haltest_info("Waiting for playback thread...\n");
+ pthread_join(play_thread, NULL);
+
+ if_audio_sco->close_input_stream(if_audio_sco, stream_in);
+
+ stream_in = NULL;
+ buffer_size_in = 0;
+}
+
static void cleanup_p(int argc, const char **argv)
{
int err;
@@ -499,6 +548,8 @@ static struct method methods[] = {
STD_METHOD(cleanup),
STD_METHOD(open_output_stream),
STD_METHOD(close_output_stream),
+ STD_METHOD(open_input_stream),
+ STD_METHOD(close_input_stream),
STD_METHODH(play, "<path to pcm file>"),
STD_METHOD(stop),
STD_METHOD(suspend),
--
1.8.3.2


2014-05-22 12:06:07

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 14/14] android/haltest: Implement read to file

From: Andrei Emeltchenko <[email protected]>

Reads data from stream_in and write to specified file.
---
android/Android.mk | 3 +++
android/client/if-sco.c | 53 +++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/android/Android.mk b/android/Android.mk
index cc0f8f5..0da16b6 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -165,6 +165,9 @@ LOCAL_C_INCLUDES += \
$(call include-path-for, system-core) \
$(call include-path-for, libhardware) \

+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez/android \
+
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)

LOCAL_SHARED_LIBRARIES := libhardware
diff --git a/android/client/if-sco.c b/android/client/if-sco.c
index 87e2d79..d575d99 100644
--- a/android/client/if-sco.c
+++ b/android/client/if-sco.c
@@ -15,6 +15,8 @@
*
*/

+#include "../src/shared/util.h"
+
#include "if-main.h"
#include "../hal-utils.h"
#include "pthread.h"
@@ -239,12 +241,29 @@ static void *playback_thread(void *data)
return NULL;
}

+static void write_stereo_pcm16(char *buffer, size_t len, FILE *out)
+{
+ const int16_t *input = (const void *) buffer;
+ int16_t sample[2];
+ size_t i;
+
+ for (i = 0; i < len / 2; i++) {
+ int16_t mono = le16_to_cpu(get_unaligned(&input[i]));
+
+ put_unaligned(cpu_to_le16(mono), &sample[0]);
+ put_unaligned(cpu_to_le16(mono), &sample[1]);
+
+ fwrite(sample, sizeof(sample), 1, out);
+ }
+}
+
static void *read_thread(void *data)
{
int (*filbuff_cb) (short*, void*) = feed_from_in;
short buffer[buffer_size / sizeof(short)];
size_t len = 0;
void *cb_data = NULL;
+ FILE *out = data;

pthread_mutex_lock(&state_mutex);
current_state = STATE_PLAYING;
@@ -266,9 +285,18 @@ static void *read_thread(void *data)
pthread_mutex_unlock(&state_mutex);

len = filbuff_cb(buffer, cb_data);
- haltest_info("len %zd\n", len);
+
+ haltest_info("Read %zd bytes\n", len);
+
+ if (out) {
+ write_stereo_pcm16((char *) buffer, len, out);
+ haltest_info("Written %zd bytes\n", len * 2);
+ }
} while (len);

+ if (out)
+ fclose(out);
+
pthread_mutex_lock(&state_mutex);
current_state = STATE_STOPPED;
pthread_mutex_unlock(&state_mutex);
@@ -355,9 +383,26 @@ static void loop_p(int argc, const char **argv)

static void read_p(int argc, const char **argv)
{
+ const char *fname = NULL;
+ FILE *out = NULL;
+
RETURN_IF_NULL(if_audio_sco);
RETURN_IF_NULL(stream_in);

+ if (argc < 3) {
+ haltest_error("Invalid audio file path.\n");
+ haltest_info("Using read and through away\n");
+ } else {
+ fname = argv[2];
+ out = fopen(fname, "w");
+ if (!out) {
+ haltest_error("Cannot open file: %s\n", fname);
+ return;
+ }
+
+ haltest_info("Reading to file: %s\n", fname);
+ }
+
if (!buffer_size_in) {
haltest_error("Invalid buffer sizes. Streams opened\n");
return;
@@ -371,10 +416,10 @@ static void read_p(int argc, const char **argv)
}
pthread_mutex_unlock(&state_mutex);

- if (pthread_create(&play_thread, NULL, read_thread,
- stream_in) != 0)
+ if (pthread_create(&play_thread, NULL, read_thread, out) != 0) {
haltest_error("Cannot create playback thread!\n");
-
+ fclose(out);
+ }
}

static void stop_p(int argc, const char **argv)
--
1.8.3.2


2014-05-22 12:05:59

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 06/14] android/hal-sco: Check file descriptor >= 0

From: Andrei Emeltchenko <[email protected]>

---
android/hal-sco.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index 4ac5d00..e940547 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -710,11 +710,11 @@ static void sco_close_output_stream(struct audio_hw_device *dev,
struct sco_dev *sco_dev = (struct sco_dev *) dev;
struct sco_stream_out *out = (struct sco_stream_out *) stream_out;

- DBG("dev %p stream %p fd %d", dev, stream_out, sco_dev->out->fd);
+ DBG("dev %p stream %p fd %d", dev, out, sco_dev->out->fd);

- if (sco_dev->out && sco_dev->out->fd) {
- close(sco_dev->out->fd);
- sco_dev->out->fd = -1;
+ if (out && out->fd >= 0) {
+ close(out->fd);
+ out->fd = -1;
}

free(out->cache);
--
1.8.3.2


2014-05-22 12:05:55

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 02/14] android/hal-sco: Fixes for unreliable mtu

From: Andrei Emeltchenko <[email protected]>

---
android/hal-sco.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index fb7b4d4..3c6e5bf 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -300,7 +300,7 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
struct pollfd pfd;
size_t len, written = 0;
int ret;
- uint16_t mtu = /* out->cfg.mtu */ 48;
+ uint16_t mtu = out->cfg.mtu;
uint64_t audio_sent_us, audio_passed_us;

pfd.fd = out->fd;
@@ -594,7 +594,9 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO;
out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
out->cfg.frame_num = OUT_STREAM_FRAMES;
- out->cfg.mtu = mtu;
+
+ /* we get wrong mtu size for some reason */
+ out->cfg.mtu = /* mtu */ 48;

out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common));
if (!out->downmix_buf) {
--
1.8.3.2


2014-05-22 12:05:58

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 05/14] android/hal-sco: Implement open input stream

From: Andrei Emeltchenko <[email protected]>

---
android/hal-sco.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 173 insertions(+), 2 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index 402b184..4ac5d00 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -75,9 +75,17 @@ struct sco_stream_out {
uint32_t resample_frame_num;
};

+struct sco_stream_in {
+ struct audio_stream_in stream;
+
+ struct sco_audio_config cfg;
+ int fd;
+};
+
struct sco_dev {
struct audio_hw_device dev;
struct sco_stream_out *out;
+ struct sco_stream_in *in;
};

/*
@@ -781,23 +789,186 @@ static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev,
return -ENOSYS;
}

+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+ DBG("rate %u", in->cfg.rate);
+
+ return in->cfg.rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ DBG("rate %u", rate);
+
+ return 0;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+ size_t size = audio_stream_frame_size(&in->stream.common) *
+ in->cfg.frame_num;
+
+ DBG("buf size %zd", size);
+
+ return size;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+ DBG("channels num: %u", popcount(in->cfg.channels));
+
+ return in->cfg.channels;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+ struct sco_stream_in *in = (struct sco_stream_in *) stream;
+
+ DBG("format: %u", in->cfg.format);
+
+ return in->cfg.format;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ DBG("%s", kvpairs);
+
+ return 0;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ DBG("");
+
+ return strdup("");
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+ size_t bytes)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
static int sco_open_input_stream(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
struct audio_config *config,
struct audio_stream_in **stream_in)
{
+ struct sco_dev *sco_dev = (struct sco_dev *) dev;
+ struct sco_stream_in *in;
+
DBG("");

+ in = calloc(1, sizeof(struct sco_stream_in));
+ if (!in)
+ return -ENOMEM;
+
+ in->stream.common.get_sample_rate = in_get_sample_rate;
+ in->stream.common.set_sample_rate = in_set_sample_rate;
+ in->stream.common.get_buffer_size = in_get_buffer_size;
+ in->stream.common.get_channels = in_get_channels;
+ in->stream.common.get_format = in_get_format;
+ in->stream.common.set_format = in_set_format;
+ in->stream.common.standby = in_standby;
+ in->stream.common.dump = in_dump;
+ in->stream.common.set_parameters = in_set_parameters;
+ in->stream.common.get_parameters = in_get_parameters;
+ in->stream.common.add_audio_effect = in_add_audio_effect;
+ in->stream.common.remove_audio_effect = in_remove_audio_effect;
+ in->stream.set_gain = in_set_gain;
+ in->stream.read = in_read;
+ in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+ if (config) {
+ DBG("config: rate %u chan mask %x format %d offload %p",
+ config->sample_rate, config->channel_mask,
+ config->format, &config->offload_info);
+
+ in->cfg.format = config->format;
+ in->cfg.channels = config->channel_mask;
+ in->cfg.rate = config->sample_rate;
+ } else {
+ in->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+ in->cfg.channels = AUDIO_CHANNEL_OUT_MONO;
+ in->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+ }
+
+ *stream_in = &in->stream;
+ sco_dev->in = in;
+
return 0;
}

static void sco_close_input_stream(struct audio_hw_device *dev,
struct audio_stream_in *stream_in)
{
- DBG("");
+ struct sco_dev *sco_dev = (struct sco_dev *) dev;
+ struct sco_stream_in *in = (struct sco_stream_in *) stream_in;
+
+ DBG("dev %p stream %p fd %d", dev, in, in->fd);

- free(stream_in);
+ free(in);
+ sco_dev->in = NULL;
}

static int sco_dump(const audio_hw_device_t *device, int fd)
--
1.8.3.2


2014-05-22 12:06:05

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 12/14] android/hal-sco: Fix memory leak

From: Andrei Emeltchenko <[email protected]>

Release resampler on exit.
---
android/hal-sco.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index 8635866..bf3b6d4 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -692,6 +692,8 @@ static int sco_open_output_stream(struct audio_hw_device *dev,

return 0;
failed:
+ if (out->resampler)
+ release_resampler(out->resampler);
free(out->cache);
free(out->downmix_buf);
free(out);
@@ -726,6 +728,9 @@ static void sco_close_output_stream(struct audio_hw_device *dev,

close_sco_socket();

+ if (out->resampler)
+ release_resampler(out->resampler);
+ free(out->resample_buf);
free(out->cache);
free(out->downmix_buf);
free(out);
--
1.8.3.2


2014-05-22 12:05:57

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 04/14] android/hal-sco: Make use of config parameter

From: Andrei Emeltchenko <[email protected]>

Use config parameter when opening output stream.
---
android/hal-sco.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index 5cd63c2..402b184 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -582,7 +582,7 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
size_t resample_size;
uint16_t mtu;

- DBG("");
+ DBG("config %p device flags 0x%02x", config, devices);

if (ipc_connect_sco(&fd, &mtu) != SCO_STATUS_SUCCESS) {
error("sco: cannot get fd");
@@ -612,10 +612,20 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
out->stream.write = out_write;
out->stream.get_render_position = out_get_render_position;

- /* Configuration for Android */
- out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
- out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO;
- out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+ if (config) {
+ DBG("config: rate %u chan mask %x format %d offload %p",
+ config->sample_rate, config->channel_mask,
+ config->format, &config->offload_info);
+
+ out->cfg.format = config->format;
+ out->cfg.channels = config->channel_mask;
+ out->cfg.rate = config->sample_rate;
+ } else {
+ out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+ out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+ out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+ }
+
out->cfg.frame_num = OUT_STREAM_FRAMES;

/* we get wrong mtu size for some reason */
--
1.8.3.2


2014-05-22 12:05:56

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv0 03/14] android/hal-sco: Add SCO packet cache

From: Andrei Emeltchenko <[email protected]>

SCO cached is used when Android writes with packet sizes which cannot
fit to 48 bytes SCO frames. Remaining frames are cached and written next
time Android perform out->write().
---
android/hal-sco.c | 38 +++++++++++++++++++++++++++++++++++---
1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/android/hal-sco.c b/android/hal-sco.c
index 3c6e5bf..5cd63c2 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -64,6 +64,9 @@ struct sco_stream_out {
int fd;

uint8_t *downmix_buf;
+ uint8_t *cache;
+ size_t cache_len;
+
size_t samples;
struct timespec start;

@@ -301,6 +304,7 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
size_t len, written = 0;
int ret;
uint16_t mtu = out->cfg.mtu;
+ uint8_t *p;
uint64_t audio_sent_us, audio_passed_us;

pfd.fd = out->fd;
@@ -320,6 +324,7 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
return false;
}

+ len = bytes - written > mtu ? mtu : bytes - written;

clock_gettime(CLOCK_REALTIME, &now);
/* Mark start of the stream */
@@ -341,12 +346,30 @@ static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
memcpy(&out->start, &now, sizeof(out->start));
}

+ if (out->cache_len) {
+ DBG("First packet cache_len %zd", out->cache_len);
+ memcpy(out->cache + out->cache_len, buffer,
+ mtu - out->cache_len);
+ p = out->cache;
+ }

- len = bytes - written > mtu ? mtu : bytes - written;
+ if (bytes - written >= mtu)
+ p = (void *) buffer + written;
+ else {
+ memcpy(out->cache, buffer + written, bytes - written);
+ out->cache_len = bytes - written;
+ DBG("Last packet, cache %zd bytes", bytes - written);
+ written += bytes - written;
+ continue;
+ }

- ret = write(out->fd, buffer + written, len);
+ ret = write(out->fd, p, len);
if (ret > 0) {
- written += ret;
+ if (out->cache_len) {
+ written = mtu - out->cache_len;
+ out->cache_len = 0;
+ } else
+ written += ret;

out->samples += ret / 2;

@@ -604,6 +627,13 @@ static int sco_open_output_stream(struct audio_hw_device *dev,
return -ENOMEM;
}

+ out->cache = malloc(out->cfg.mtu);
+ if (!out->cache) {
+ free(out->downmix_buf);
+ free(out);
+ return -ENOMEM;
+ }
+
DBG("size %zd", out_get_buffer_size(&out->stream.common));

/* Channel numbers for resampler */
@@ -647,6 +677,7 @@ static int sco_open_output_stream(struct audio_hw_device *dev,

return 0;
failed:
+ free(out->cache);
free(out->downmix_buf);
free(out);
stream_out = NULL;
@@ -668,6 +699,7 @@ static void sco_close_output_stream(struct audio_hw_device *dev,
sco_dev->out->fd = -1;
}

+ free(out->cache);
free(out->downmix_buf);
free(out);
sco_dev->out = NULL;
--
1.8.3.2