2014-05-16 12:57:02

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFC 1/3] 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-16 12:57:03

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFC 2/3] 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-16 12:57:04

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFC 3/3] 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