summaryrefslogtreecommitdiff
path: root/android/hal-audio.c
diff options
context:
space:
mode:
authorAndrzej Kaczmarek <andrzej.kaczmarek@tieto.com>2014-03-03 09:56:42 +0100
committerSzymon Janc <szymon.janc@tieto.com>2014-03-04 10:26:37 +0100
commit3eb9a5f61d94dfa86bb0f13f9f7e977d7c0ebaa1 (patch)
tree5c93fd89e1665b3f0823e371b68c20a222414ad7 /android/hal-audio.c
parentfe16c25b4b6be57f9443ab825dcdc77cfb8748b2 (diff)
downloadbluez-3eb9a5f61d94dfa86bb0f13f9f7e977d7c0ebaa1.tar.gz
android/hal-audio: Provide better audio synchronization
Instead of waiting some fixed amount of useconds before next media packet is encoded we use absolute time to wait precisely for a moment when next packet is expected to be produced. This greatly improves flow as is can compensate for time spent on encoding and writing data.
Diffstat (limited to 'android/hal-audio.c')
-rw-r--r--android/hal-audio.c91
1 files changed, 49 insertions, 42 deletions
diff --git a/android/hal-audio.c b/android/hal-audio.c
index 5a69b523e..00dde49f2 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -147,6 +147,27 @@ static inline void timespec_diff(struct timespec *a, struct timespec *b,
}
}
+static void timespec_add(struct timespec *base, uint64_t time_us,
+ struct timespec *res)
+{
+ res->tv_sec = base->tv_sec + time_us / 1000000;
+ res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000;
+
+ if (res->tv_nsec >= 1000000000) {
+ res->tv_sec++;
+ res->tv_nsec -= 1000000000;
+ }
+}
+
+#if defined(ANDROID)
+/* Bionic does not have clock_nanosleep() prototype in time.h even though
+ * it provides its implementation.
+ */
+extern int clock_nanosleep(clockid_t clock_id, int flags,
+ const struct timespec *request,
+ struct timespec *remain);
+#endif
+
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);
@@ -480,45 +501,6 @@ static size_t sbc_get_mediapacket_duration(void *codec_data)
return sbc_data->frame_duration * sbc_data->frames_per_packet;
}
-static int write_media_packet(struct a2dp_stream_out *out, size_t mp_data_len,
- uint32_t input_samples)
-{
- struct audio_endpoint *ep = out->ep;
- struct media_packet *mp = ep->mp;
- struct timespec cur;
- struct timespec diff;
- uint32_t expected_samples;
- int ret;
-
- while (true) {
- ret = write(ep->fd, mp, sizeof(*mp) + mp_data_len);
- if (ret >= 0)
- break;
-
- if (errno != EINTR)
- return -errno;
- }
-
- clock_gettime(CLOCK_MONOTONIC, &cur);
- timespec_diff(&cur, &ep->start, &diff);
- expected_samples = (diff.tv_sec * 1000000ll + diff.tv_nsec / 1000ll) *
- out->cfg.rate / 1000000ll;
-
- /* 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 (ep->samples >= expected_samples)
- usleep(input_samples * 1000000 / out->cfg.rate);
-
- return ret;
-}
-
static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
size_t len, struct media_packet *mp,
size_t mp_data_len, size_t *written)
@@ -968,6 +950,25 @@ static bool write_data(struct a2dp_stream_out *out, const void *buffer,
ssize_t read;
uint32_t samples;
int ret;
+ uint64_t time_us;
+ struct timespec anchor;
+
+ time_us = ep->samples * 1000000ll / out->cfg.rate;
+
+ timespec_add(&ep->start, time_us, &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,
@@ -992,9 +993,15 @@ static bool write_data(struct a2dp_stream_out *out, const void *buffer,
samples = read / (2 * popcount(out->cfg.channels));
ep->samples += samples;
- ret = write_media_packet(out, written, samples);
- if (ret < 0)
- return false;
+ while (true) {
+ ret = write(ep->fd, mp, sizeof(*mp) + written);
+
+ if (ret >= 0)
+ break;
+
+ if (errno != EINTR)
+ return false;
+ }
}
return true;