diff --git a/android/hal-audio.c b/android/hal-audio.c
index 992fdf7..a261871 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
}
}
+static 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 uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+ struct timespec res;
+
+ timespec_diff(a, b, &res);
+
+ return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
#if defined(ANDROID)
/* Bionic does not have clock_nanosleep() prototype in time.h even though
* it provides its implementation.
}
}
-static int set_blocking(int fd)
-{
- int flags;
-
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0) {
- int err = -errno;
- error("fcntl(F_GETFL): %s (%d)", strerror(-err), -err);
- return err;
- }
-
- if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
- int err = -errno;
- error("fcntl(F_SETFL): %s (%d)", strerror(-err), -err);
- return err;
- }
-
- return 0;
-}
-
static bool open_endpoint(struct audio_endpoint *ep,
struct audio_input_config *cfg)
{
AUDIO_STATUS_SUCCESS)
return false;
- if (set_blocking(fd) < 0)
- goto failed;
-
DBG("mtu=%u", mtu);
payload_len = mtu - sizeof(*ep->mp);
if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS)
return false;
- clock_gettime(CLOCK_MONOTONIC, &ep->start);
ep->samples = 0;
return true;
}
}
+static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable)
+{
+ int ret;
+
+ while (true) {
+ struct pollfd pollfd;
+
+ pollfd.fd = ep->fd;
+ pollfd.events = POLLOUT;
+ pollfd.revents = 0;
+
+ ret = poll(&pollfd, 1, 500);
+
+ if (ret >= 0) {
+ *writable = !!(pollfd.revents & POLLOUT);
+ break;
+ }
+
+ if (errno != EINTR) {
+ ret = errno;
+ error("poll failed (%d)", ret);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes)
+{
+ struct media_packet *mp = (struct media_packet *) ep->mp;
+ int ret;
+
+ while (true) {
+ ret = write(ep->fd, mp, sizeof(*mp) + bytes);
+
+ if (ret >= 0)
+ break;
+
+ /* this should not happen so let's issue warning, but do not
+ * fail, we can try to write next packet
+ */
+ if (errno == EAGAIN) {
+ ret = errno;
+ warn("write failed (%d)", ret);
+ break;
+ }
+
+ if (errno != EINTR) {
+ ret = errno;
+ error("write failed (%d)", ret);
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool write_data(struct a2dp_stream_out *out, const void *buffer,
size_t bytes)
{
ssize_t read;
uint32_t samples;
int ret;
- uint64_t time_us;
- struct timespec anchor;
+ struct timespec current;
+ uint64_t audio_sent, audio_passed;
+ bool do_write = false;
- time_us = ep->samples * 1000000ll / out->cfg.rate;
+ /* prepare media packet in advance so we don't waste time after
+ * wakeup
+ */
+ mp->hdr.sequence_number = htons(ep->seq++);
+ mp->hdr.timestamp = htonl(ep->samples);
+ read = ep->codec->encode_mediapacket(ep->codec_data,
+ buffer + consumed,
+ bytes - consumed, mp,
+ free_space, &written);
- timespec_add(&ep->start, time_us, &anchor);
+ /* not much we can do here, let's just ignore remaining
+ * data and continue
+ */
+ if (read <= 0)
+ return true;
- while (true) {
- ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
- &anchor, NULL);
+ /* calculate where are we and where we should be */
+ clock_gettime(CLOCK_MONOTONIC, ¤t);
+ if (!ep->samples)
+ memcpy(&ep->start, ¤t, sizeof(ep->start));
+ audio_sent = ep->samples * 1000000ll / out->cfg.rate;
+ audio_passed = timespec_diff_us(¤t, &ep->start);
- if (!ret)
- break;
+ /* if we're ahead of stream then wait for next write point */
+ if (audio_sent > audio_passed) {
+ struct timespec anchor;
- if (ret != EINTR) {
- error("clock_nanosleep failed (%d)", ret);
- return false;
+ timespec_add(&ep->start, audio_sent, &anchor);
+
+ while (true) {
+ ret = clock_nanosleep(CLOCK_MONOTONIC,
+ TIMER_ABSTIME, &anchor,
+ NULL);
+
+ if (!ret)
+ break;
+
+ if (ret != EINTR) {
+ error("clock_nanosleep failed (%d)",
+ ret);
+ return false;
+ }
}
}
- read = ep->codec->encode_mediapacket(ep->codec_data,
- buffer + consumed,
- bytes - consumed, mp,
- free_space, &written);
-
- /* This is non-fatal and we can just assume buffer was processed
- * properly and wait for next one.
+ /* wait some time for socket to be ready for write,
+ * but we'll just skip writing data if timeout occurs
*/
- if (read <= 0)
- return true;
-
- consumed += read;
+ if (!wait_for_endpoint(ep, &do_write))
+ return false;
- mp->hdr.sequence_number = htons(ep->seq++);
- mp->hdr.timestamp = htonl(ep->samples);
+ if (do_write)
+ if (!write_to_endpoint(ep, written))
+ return false;
/* AudioFlinger provides 16bit PCM, so sample size is 2 bytes
- * multipled by number of channels. Number of channels is simply
- * number of bits set in channels mask.
+ * multiplied by number of channels. Number of channels is
+ * simply number of bits set in channels mask.
*/
samples = read / (2 * popcount(out->cfg.channels));
ep->samples += samples;
-
- while (true) {
- ret = write(ep->fd, mp, sizeof(*mp) + written);
-
- if (ret >= 0)
- break;
-
- if (errno != EINTR)
- return false;
- }
+ consumed += read;
}
return true;