summaryrefslogtreecommitdiff
path: root/cras
diff options
context:
space:
mode:
authorJohn Muir <muirj@google.com>2016-07-31 14:57:44 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-09-10 22:48:08 -0700
commitfa4cd9766aec6eae544b1078ed835687c4e74617 (patch)
tree44ad64a844430ce48c424e943795accedb61b943 /cras
parent654d8e6e44b31384dc62259c990bfff347391f74 (diff)
downloadadhd-fa4cd9766aec6eae544b1078ed835687c4e74617.tar.gz
CRAS: Use the hardware timestamp with available/used frames.
The ALSA device drivers can provide a timestamp associated with the buffer read-pointer. This can be use to more accurately guage the time that the next buffer is required. Enable this functionality only when supported ALSA in the running kernel. BUG=None TEST=Unit tests pass. Verified on Samus (on kernel v3.14). Verified on new device (kernel v3.18 - htimestamp enabled). Verified a bluetooth device. Verified a USB device. Change-Id: I5bca36f7ad9a236e80b2e277ccc62ccc8c39905b Reviewed-on: https://chromium-review.googlesource.com/366961 Commit-Ready: John Muir <muirj@google.com> Tested-by: John Muir <muirj@google.com> Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'cras')
-rw-r--r--cras/src/common/cras_types.h2
-rw-r--r--cras/src/common/cras_util.h6
-rw-r--r--cras/src/server/audio_thread.c65
-rw-r--r--cras/src/server/cras_a2dp_iodev.c7
-rw-r--r--cras/src/server/cras_alsa_helpers.c80
-rw-r--r--cras/src/server/cras_alsa_helpers.h20
-rw-r--r--cras/src/server/cras_alsa_io.c16
-rw-r--r--cras/src/server/cras_bt_io.c5
-rw-r--r--cras/src/server/cras_empty_iodev.c4
-rw-r--r--cras/src/server/cras_hfp_info.c10
-rw-r--r--cras/src/server/cras_hfp_info.h6
-rw-r--r--cras/src/server/cras_hfp_iodev.c12
-rw-r--r--cras/src/server/cras_iodev.c21
-rw-r--r--cras/src/server/cras_iodev.h25
-rw-r--r--cras/src/server/cras_loopback_iodev.c9
-rw-r--r--cras/src/server/test_iodev.c4
-rw-r--r--cras/src/tests/a2dp_iodev_unittest.cc13
-rw-r--r--cras/src/tests/alsa_helpers_unittest.cc122
-rw-r--r--cras/src/tests/alsa_io_unittest.cc7
-rw-r--r--cras/src/tests/audio_thread_unittest.cc17
-rw-r--r--cras/src/tests/bt_io_unittest.cc6
-rw-r--r--cras/src/tests/cras_test_client.c8
-rw-r--r--cras/src/tests/hfp_info_unittest.cc25
-rw-r--r--cras/src/tests/hfp_iodev_unittest.cc3
-rw-r--r--cras/src/tests/iodev_unittest.cc22
-rw-r--r--cras/src/tests/loopback_iodev_unittest.cc11
26 files changed, 410 insertions, 116 deletions
diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h
index 6ee6837b..e6ec3876 100644
--- a/cras/src/common/cras_types.h
+++ b/cras/src/common/cras_types.h
@@ -149,8 +149,10 @@ enum AUDIO_THREAD_LOG_EVENTS {
AUDIO_THREAD_WAKE,
AUDIO_THREAD_SLEEP,
AUDIO_THREAD_READ_AUDIO,
+ AUDIO_THREAD_READ_AUDIO_TSTAMP,
AUDIO_THREAD_READ_AUDIO_DONE,
AUDIO_THREAD_FILL_AUDIO,
+ AUDIO_THREAD_FILL_AUDIO_TSTAMP,
AUDIO_THREAD_FILL_AUDIO_DONE,
AUDIO_THREAD_WRITE_STREAMS_WAIT,
AUDIO_THREAD_WRITE_STREAMS_WAIT_TO,
diff --git a/cras/src/common/cras_util.h b/cras/src/common/cras_util.h
index 457721df..4b7f3264 100644
--- a/cras/src/common/cras_util.h
+++ b/cras/src/common/cras_util.h
@@ -163,6 +163,12 @@ static inline void ms_to_timespec(time_t milliseconds, struct timespec *ts)
ts->tv_nsec = (milliseconds % 1000) * 1000000;
}
+/* Returns non-zero if the given timespec is non-zero. */
+static inline int timespec_is_nonzero(const struct timespec *ts) {
+ return ts && (ts->tv_sec != 0 ||
+ (ts->tv_sec == 0 && ts->tv_nsec != 0));
+}
+
/* Calculates frames since time beg. */
static inline unsigned int cras_frames_since_time(const struct timespec *beg,
unsigned int rate)
diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c
index 729b1fb6..61724a1e 100644
--- a/cras/src/server/audio_thread.c
+++ b/cras/src/server/audio_thread.c
@@ -972,9 +972,9 @@ static int get_next_output_wake(struct audio_thread *thread,
continue;
frames_to_play_in_sleep = cras_iodev_frames_to_play_in_sleep(
- adev->dev, &hw_level);
-
- adev->wake_ts = *now;
+ adev->dev, &hw_level, &adev->wake_ts);
+ if (!timespec_is_nonzero(&adev->wake_ts))
+ adev->wake_ts = *now;
est_rate = adev->dev->ext_format->frame_rate *
cras_iodev_get_est_rate_ratio(adev->dev);
@@ -1101,6 +1101,7 @@ static int write_output_samples(struct audio_thread *thread,
{
struct cras_iodev *odev = adev->dev;
unsigned int hw_level;
+ struct timespec hw_tstamp;
unsigned int frames, fr_to_req;
snd_pcm_sframes_t written;
snd_pcm_uframes_t total_written = 0;
@@ -1119,20 +1120,24 @@ static int write_output_samples(struct audio_thread *thread,
if (cras_iodev_state(odev) != CRAS_IODEV_STATE_NORMAL_RUN)
return 0;
- rc = cras_iodev_frames_queued(odev);
+ rc = cras_iodev_frames_queued(odev, &hw_tstamp);
if (rc < 0)
return rc;
hw_level = rc;
- if (hw_level < odev->min_cb_level / 2)
- adev->coarse_rate_adjust = 1;
- else if (hw_level > odev->max_cb_level * 2)
- adev->coarse_rate_adjust = -1;
- else
- adev->coarse_rate_adjust = 0;
- if (cras_iodev_update_rate(odev, hw_level))
- update_estimated_rate(thread, adev);
+ ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_TSTAMP, adev->dev->info.idx,
+ hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
+ if (timespec_is_nonzero(&hw_tstamp)) {
+ if (hw_level < odev->min_cb_level / 2)
+ adev->coarse_rate_adjust = 1;
+ else if (hw_level > odev->max_cb_level * 2)
+ adev->coarse_rate_adjust = -1;
+ else
+ adev->coarse_rate_adjust = 0;
+ if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp))
+ update_estimated_rate(thread, adev);
+ }
ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO,
adev->dev->info.idx, hw_level, 0);
@@ -1256,24 +1261,29 @@ static int capture_to_streams(struct audio_thread *thread,
{
struct cras_iodev *idev = adev->dev;
snd_pcm_uframes_t remainder, hw_level;
+ struct timespec hw_tstamp;
int rc;
- rc = cras_iodev_frames_queued(idev);
+ rc = cras_iodev_frames_queued(idev, &hw_tstamp);
if (rc < 0)
return rc;
hw_level = rc;
- if (hw_level < idev->min_cb_level / 2)
- adev->coarse_rate_adjust = 1;
- else if (hw_level > idev->max_cb_level * 2)
- adev->coarse_rate_adjust = -1;
- else
- adev->coarse_rate_adjust = 0;
- if (hw_level)
- adev->input_streaming = 1;
+ ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_TSTAMP, idev->info.idx,
+ hw_tstamp.tv_sec, hw_tstamp.tv_nsec);
+ if (timespec_is_nonzero(&hw_tstamp)) {
+ if (hw_level)
+ adev->input_streaming = 1;
- if (cras_iodev_update_rate(idev, hw_level))
- update_estimated_rate(thread, adev);
+ if (hw_level < idev->min_cb_level / 2)
+ adev->coarse_rate_adjust = 1;
+ else if (hw_level > idev->max_cb_level * 2)
+ adev->coarse_rate_adjust = -1;
+ else
+ adev->coarse_rate_adjust = 0;
+ if (cras_iodev_update_rate(idev, hw_level, &hw_tstamp))
+ update_estimated_rate(thread, adev);
+ }
remainder = MIN(hw_level, get_stream_limit_set_delay(adev, hw_level));
@@ -1344,7 +1354,7 @@ static int send_captured_samples(struct audio_thread *thread)
{
struct open_dev *idev_list = thread->open_devs[CRAS_STREAM_INPUT];
struct open_dev *adev;
- struct timespec now;
+ struct timespec level_tstamp;
// TODO(dgreid) - once per rstream, not once per dev_stream.
DL_FOREACH(idev_list, adev) {
@@ -1355,7 +1365,9 @@ static int send_captured_samples(struct audio_thread *thread)
if (!cras_iodev_is_open(adev->dev))
continue;
- curr_level = cras_iodev_frames_queued(adev->dev);
+ curr_level = cras_iodev_frames_queued(adev->dev, &level_tstamp);
+ if (!timespec_is_nonzero(&level_tstamp))
+ clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp);
DL_FOREACH(adev->dev->streams, stream) {
dev_stream_capture_update_rstream(stream);
@@ -1368,11 +1380,10 @@ static int send_captured_samples(struct audio_thread *thread)
else
min_needed = 0;
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
cras_frames_to_time(min_needed + 10,
adev->dev->ext_format->frame_rate,
&adev->wake_ts);
- add_timespecs(&adev->wake_ts, &now);
+ add_timespecs(&adev->wake_ts, &level_tstamp);
}
return 0;
diff --git a/cras/src/server/cras_a2dp_iodev.c b/cras/src/server/cras_a2dp_iodev.c
index ccc20e98..29e33828 100644
--- a/cras/src/server/cras_a2dp_iodev.c
+++ b/cras/src/server/cras_a2dp_iodev.c
@@ -120,7 +120,8 @@ static int bt_queued_frames(const struct cras_iodev *iodev, int fr)
}
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
int estimate_queued_frames = bt_queued_frames(iodev, 0);
@@ -129,6 +130,7 @@ static int frames_queued(const struct cras_iodev *iodev)
buf_queued_bytes(a2dpio->pcm_buf) /
cras_get_format_bytes(iodev->format);
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return MIN(iodev->buffer_size,
MAX(estimate_queued_frames, local_queued_frames));
}
@@ -332,9 +334,10 @@ encode_more:
static int delay_frames(const struct cras_iodev *iodev)
{
const struct a2dp_io *a2dpio = (struct a2dp_io *)iodev;
+ struct timespec tstamp;
/* The number of frames in the pcm buffer plus two mtu packets */
- return frames_queued(iodev) + a2dpio->sock_depth_frames;
+ return frames_queued(iodev, &tstamp) + a2dpio->sock_depth_frames;
}
static int get_buffer(struct cras_iodev *iodev,
diff --git a/cras/src/server/cras_alsa_helpers.c b/cras/src/server/cras_alsa_helpers.c
index 0e7d7e41..7b635390 100644
--- a/cras/src/server/cras_alsa_helpers.c
+++ b/cras/src/server/cras_alsa_helpers.c
@@ -564,11 +564,10 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
ret_rate, format->num_channels, format->format);
return err;
}
-
return 0;
}
-int cras_alsa_set_swparams(snd_pcm_t *handle)
+int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp)
{
int err;
snd_pcm_sw_params_t *swparams;
@@ -605,7 +604,53 @@ int cras_alsa_set_swparams(snd_pcm_t *handle)
return err;
}
+ if (*enable_htimestamp) {
+ /* Use MONOTONIC_RAW time-stamps. */
+ err = snd_pcm_sw_params_set_tstamp_type(
+ handle, swparams,
+ SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_type: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_sw_params_set_tstamp_mode(
+ handle, swparams, SND_PCM_TSTAMP_ENABLE);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_mode: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ }
+
+ /* This hack is required because ALSA-LIB does not provide any way to
+ * detect whether MONOTONIC_RAW timestamps are supported by the kernel.
+ * In ALSA-LIB, the code checks the hardware protocol version. */
err = snd_pcm_sw_params(handle, swparams);
+ if (err == -EINVAL && *enable_htimestamp) {
+ *enable_htimestamp = 0;
+ syslog(LOG_WARNING,
+ "MONOTONIC_RAW timestamps are not supported.");
+
+ err = snd_pcm_sw_params_set_tstamp_type(
+ handle, swparams,
+ SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_type: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+ err = snd_pcm_sw_params_set_tstamp_mode(
+ handle, swparams, SND_PCM_TSTAMP_NONE);
+ if (err < 0) {
+ syslog(LOG_ERR, "set_tstamp_mode: %s\n",
+ snd_strerror(err));
+ return err;
+ }
+
+ err = snd_pcm_sw_params(handle, swparams);
+ }
+
if (err < 0) {
syslog(LOG_ERR, "sw_params: %s\n", snd_strerror(err));
return err;
@@ -614,20 +659,29 @@ int cras_alsa_set_swparams(snd_pcm_t *handle)
}
int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
- snd_pcm_uframes_t *used,
+ snd_pcm_uframes_t *avail,
+ struct timespec *tstamp,
unsigned int *underruns)
{
snd_pcm_sframes_t frames;
int rc = 0;
+ /* Use snd_pcm_avail still to ensure that the hardware pointer is
+ * up to date. Otherwise, we could use the deprecated snd_pcm_hwsync().
+ * IMO this is a deficiency in the ALSA API.
+ */
frames = snd_pcm_avail(handle);
- if (frames == -EPIPE || frames == -ESTRPIPE) {
- cras_alsa_attempt_resume(handle);
- frames = 0;
- } else if (frames < 0) {
- syslog(LOG_ERR, "pcm_avail error %s\n", snd_strerror(frames));
+ if (frames >= 0)
+ rc = snd_pcm_htimestamp(handle, avail, tstamp);
+ else
rc = frames;
- frames = 0;
+ if (rc == -EPIPE || rc == -ESTRPIPE) {
+ cras_alsa_attempt_resume(handle);
+ rc = 0;
+ goto error;
+ } else if (rc < 0) {
+ syslog(LOG_ERR, "pcm_avail error %s\n", snd_strerror(rc));
+ goto error;
} else if (frames > (snd_pcm_sframes_t)buf_size) {
syslog(LOG_ERR,
"pcm_avail returned frames larger than buf_size: "
@@ -635,7 +689,13 @@ int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
frames = buf_size;
*underruns = *underruns + 1;
}
- *used = frames;
+ *avail = frames;
+ return 0;
+
+error:
+ *avail = 0;
+ tstamp->tv_sec = 0;
+ tstamp->tv_nsec = 0;
return rc;
}
diff --git a/cras/src/server/cras_alsa_helpers.h b/cras/src/server/cras_alsa_helpers.h
index 4d90ad58..091049b3 100644
--- a/cras/src/server/cras_alsa_helpers.h
+++ b/cras/src/server/cras_alsa_helpers.h
@@ -138,22 +138,30 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
/* Sets up the swparams to alsa.
* Args:
* handle - The open PCM to configure.
+ * enable_htimestamp - If non-zero, enable and configure hardware timestamps,
+ * updated to reflect whether MONOTONIC RAW htimestamps
+ * are supported by the kernel implementation.
* Returns:
* 0 on success, negative error on failure.
*/
-int cras_alsa_set_swparams(snd_pcm_t *handle);
+int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp);
/* Get the number of used frames in the alsa buffer.
* Args:
- * handle - The open PCM to configure.
- * buf_size - Number of frames in the ALSA buffer.
- * used - Filled with the number of used frames.
- * underruns - Pointer to the underrun counter.
+ * handle[in] - The open PCM to configure.
+ * buf_size[in] - Number of frames in the ALSA buffer.
+ * avail[out] - Filled with the number of frames available in the buffer.
+ * tstamp[out] - Filled with the hardware timestamp for the available frames.
+ * This value is {0, 0} when the device hasn't actually started
+ * reading or writing frames.
+ * underruns[in,out] - Pointer to the underrun counter updated if there was
+ * an underrun.
* Returns:
* 0 on success, negative error on failure.
*/
int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
- snd_pcm_uframes_t *used,
+ snd_pcm_uframes_t *avail,
+ struct timespec *tstamp,
unsigned int *underruns);
/* Get the current alsa delay, make sure it's no bigger than the buffer size.
diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c
index c5529ec7..1ff32f26 100644
--- a/cras/src/server/cras_alsa_io.c
+++ b/cras/src/server/cras_alsa_io.c
@@ -84,6 +84,7 @@ struct alsa_input_node {
* is_first - true if this is the first iodev on the card.
* fully_specified - true if this device and it's nodes were fully specified.
* That is, don't automatically create nodes for it.
+ * enable_htimestamp - True when the device's htimestamp is used.
* handle - Handle to the opened ALSA device.
* num_underruns - Number of times we have run out of data (playback only).
* alsa_stream - Playback or capture type.
@@ -110,6 +111,7 @@ struct alsa_io {
enum CRAS_ALSA_CARD_TYPE card_type;
int is_first;
int fully_specified;
+ int enable_htimestamp;
snd_pcm_t *handle;
unsigned int num_underruns;
snd_pcm_stream_t alsa_stream;
@@ -134,7 +136,8 @@ static int alsa_iodev_set_active_node(struct cras_iodev *iodev,
* iodev callbacks.
*/
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct alsa_io *aio = (struct alsa_io *)iodev;
int rc;
@@ -142,11 +145,12 @@ static int frames_queued(const struct cras_iodev *iodev)
rc = cras_alsa_get_avail_frames(aio->handle,
aio->base.buffer_size,
- &frames,
+ &frames, tstamp,
&aio->num_underruns);
if (rc < 0)
return rc;
-
+ if (!aio->enable_htimestamp)
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
if (iodev->direction == CRAS_STREAM_INPUT)
return (int)frames;
@@ -240,7 +244,7 @@ static int open_dev(struct cras_iodev *iodev)
}
/* Configure software params. */
- rc = cras_alsa_set_swparams(handle);
+ rc = cras_alsa_set_swparams(handle, &aio->enable_htimestamp);
if (rc < 0) {
cras_alsa_pcm_close(handle);
return rc;
@@ -1404,13 +1408,14 @@ static int possibly_enter_free_run(struct cras_iodev *odev)
int rc;
unsigned int hw_level, fr_to_write;
unsigned int target_hw_level = odev->min_cb_level * 2;
+ struct timespec hw_tstamp;
if (aio->is_free_running)
return 0;
/* Check if all valid samples are played.
* If all valid samples are played, fill whole buffer with zeros. */
- rc = cras_iodev_frames_queued(odev);
+ rc = cras_iodev_frames_queued(odev, &hw_tstamp);
if (rc < 0)
return rc;
hw_level = rc;
@@ -1525,6 +1530,7 @@ struct cras_iodev *alsa_iodev_create(size_t card_index,
aio->device_index = device_index;
aio->card_type = card_type;
aio->is_first = is_first;
+ aio->enable_htimestamp = 1;
aio->handle = NULL;
if (dev_name) {
aio->dev_name = strdup(dev_name);
diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c
index 6319f280..db724aba 100644
--- a/cras/src/server/cras_bt_io.c
+++ b/cras/src/server/cras_bt_io.c
@@ -234,12 +234,13 @@ static void set_bt_volume(struct cras_iodev *iodev)
dev->set_volume(dev);
}
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct cras_iodev *dev = active_profile_dev(iodev);
if (!dev)
return -EINVAL;
- return dev->frames_queued(dev);
+ return dev->frames_queued(dev, tstamp);
}
static int delay_frames(const struct cras_iodev *iodev)
diff --git a/cras/src/server/cras_empty_iodev.c b/cras/src/server/cras_empty_iodev.c
index c9e2bd76..4616cd3c 100644
--- a/cras/src/server/cras_empty_iodev.c
+++ b/cras/src/server/cras_empty_iodev.c
@@ -70,8 +70,10 @@ static unsigned int current_level(const struct cras_iodev *iodev)
* iodev callbacks.
*/
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return current_level(iodev);
}
diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c
index 2bf1c1f4..701586c6 100644
--- a/cras/src/server/cras_hfp_info.c
+++ b/cras/src/server/cras_hfp_info.c
@@ -34,6 +34,8 @@
* adapter, could be different than mtu.
* capture_buf - The buffer to hold samples read from SCO socket.
* playback_buf - The buffer to hold samples about to write to SCO socket.
+ * transfer_ts - Timestamp when the playback or capture buffer was last
+ * modified.
* idev - The input iodev using this hfp_info.
* odev - The output iodev using this hfp_info.
* packet_size_changed_cbs - The callbacks to trigger when SCO packet
@@ -46,6 +48,7 @@ struct hfp_info {
unsigned int packet_size;
struct byte_buffer *capture_buf;
struct byte_buffer *playback_buf;
+ struct timespec transfer_ts;
struct cras_iodev *idev;
struct cras_iodev *odev;
@@ -129,11 +132,13 @@ void hfp_buf_release(struct hfp_info *info, struct cras_iodev *dev,
buf_increment_read(info->capture_buf, written_frames);
}
-int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev)
+int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev,
+ struct timespec *tstamp)
{
size_t format_bytes;
format_bytes = cras_get_format_bytes(dev->format);
+ *tstamp = info->transfer_ts;
if (dev->direction == CRAS_STREAM_OUTPUT)
return buf_queued_bytes(info->playback_buf) / format_bytes;
else
@@ -289,6 +294,7 @@ static int hfp_info_callback(void *arg)
}
}
+ clock_gettime(CLOCK_MONOTONIC_RAW, &info->transfer_ts);
return 0;
read_write_error:
@@ -358,6 +364,8 @@ int hfp_info_stop(struct hfp_info *info)
close(info->fd);
info->fd = 0;
info->started = 0;
+ info->transfer_ts.tv_sec = 0;
+ info->transfer_ts.tv_nsec = 0;
return 0;
}
diff --git a/cras/src/server/cras_hfp_info.h b/cras/src/server/cras_hfp_info.h
index a6a48c7e..3798e0b3 100644
--- a/cras/src/server/cras_hfp_info.h
+++ b/cras/src/server/cras_hfp_info.h
@@ -47,9 +47,11 @@ int hfp_info_stop(struct hfp_info *info);
* Args:
* info - The hfp_info holding the buffer to query.
* dev - The iodev to indicate which buffer to query, playback
- * or capture, depend on its direction.
+ * or capture, depending on its direction.
+ * tstamp - Time stamp associated with the amount queued.
*/
-int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev);
+int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev,
+ struct timespec *tstamp);
/* Gets how many bytes of the buffer are used.
* Args:
diff --git a/cras/src/server/cras_hfp_iodev.c b/cras/src/server/cras_hfp_iodev.c
index 1de6ea90..f9e10ae8 100644
--- a/cras/src/server/cras_hfp_iodev.c
+++ b/cras/src/server/cras_hfp_iodev.c
@@ -48,14 +48,15 @@ static int update_supported_formats(struct cras_iodev *iodev)
return 0;
}
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct hfp_io *hfpio = (struct hfp_io *)iodev;
if (!hfp_info_running(hfpio->info))
return -1;
- return hfp_buf_queued(hfpio->info, iodev);
+ return hfp_buf_queued(hfpio->info, iodev, tstamp);
}
/* Modify the hfpio's buffer_size when the SCO packet size has changed. */
@@ -136,7 +137,9 @@ static void set_hfp_volume(struct cras_iodev *iodev)
static int delay_frames(const struct cras_iodev *iodev)
{
- return frames_queued(iodev);
+ struct timespec tstamp;
+
+ return frames_queued(iodev, &tstamp);
}
static int get_buffer(struct cras_iodev *iodev,
@@ -175,10 +178,11 @@ static int put_buffer(struct cras_iodev *iodev, unsigned nwritten)
static int flush_buffer(struct cras_iodev *iodev)
{
struct hfp_io *hfpio = (struct hfp_io *)iodev;
+ struct timespec tstamp;
unsigned nframes;
if (iodev->direction == CRAS_STREAM_INPUT) {
- nframes = hfp_buf_queued(hfpio->info, iodev);
+ nframes = hfp_buf_queued(hfpio->info, iodev, &tstamp);
hfp_buf_release(hfpio->info, iodev, nframes);
}
return 0;
diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c
index d1a75297..072a5eeb 100644
--- a/cras/src/server/cras_iodev.c
+++ b/cras/src/server/cras_iodev.c
@@ -40,9 +40,10 @@ static int default_no_stream_playback(struct cras_iodev *odev)
int rc;
unsigned int hw_level, fr_to_write;
unsigned int target_hw_level = odev->min_cb_level * 2;
+ struct timespec hw_tstamp;
/* The default action for no stream playback is to fill zeros. */
- rc = cras_iodev_frames_queued(odev);
+ rc = cras_iodev_frames_queued(odev, &hw_tstamp);
if (rc < 0)
return rc;
hw_level = rc;
@@ -871,12 +872,10 @@ int cras_iodev_get_output_buffer(struct cras_iodev *iodev,
return rc;
}
-int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level)
+int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
+ struct timespec *level_tstamp)
{
- struct timespec now;
-
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- return rate_estimator_check(iodev->rate_est, level, &now);
+ return rate_estimator_check(iodev->rate_est, level, level_tstamp);
}
int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev)
@@ -912,11 +911,12 @@ int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev)
return delay;
}
-int cras_iodev_frames_queued(struct cras_iodev *iodev)
+int cras_iodev_frames_queued(struct cras_iodev *iodev,
+ struct timespec *hw_tstamp)
{
int rc;
- rc = iodev->frames_queued(iodev);
+ rc = iodev->frames_queued(iodev, hw_tstamp);
if (rc < 0 || iodev->direction == CRAS_STREAM_INPUT)
return rc;
@@ -998,11 +998,12 @@ int cras_iodev_odev_should_wake(const struct cras_iodev *odev)
}
unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
- unsigned int *hw_level)
+ unsigned int *hw_level,
+ struct timespec *hw_tstamp)
{
int rc;
- rc = cras_iodev_frames_queued(odev);
+ rc = cras_iodev_frames_queued(odev, hw_tstamp);
*hw_level = (rc < 0) ? 0 : rc;
if (odev->streams) {
diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h
index d80ef1a3..ca480bc8 100644
--- a/cras/src/server/cras_iodev.h
+++ b/cras/src/server/cras_iodev.h
@@ -93,7 +93,9 @@ struct cras_ionode {
* open_dev - Opens the device.
* close_dev - Closes the device if it is open.
* update_supported_formats - Refresh supported frame rates and channel counts.
- * frames_queued - The number of frames in the audio buffer.
+ * frames_queued - The number of frames in the audio buffer, and fills tstamp
+ * with the associated timestamp. The timestamp is {0, 0} when
+ * the device hasn't started processing data (and on error).
* delay_frames - The delay of the next sample in frames.
* get_buffer - Returns a buffer to read/write to/from.
* put_buffer - Marks a buffer from get_buffer as read/written.
@@ -165,7 +167,8 @@ struct cras_iodev {
int (*open_dev)(struct cras_iodev *iodev);
int (*close_dev)(struct cras_iodev *iodev);
int (*update_supported_formats)(struct cras_iodev *iodev);
- int (*frames_queued)(const struct cras_iodev *iodev);
+ int (*frames_queued)(const struct cras_iodev *iodev,
+ struct timespec *tstamp);
int (*delay_frames)(const struct cras_iodev *iodev);
int (*get_buffer)(struct cras_iodev *iodev,
struct cras_audio_area **area,
@@ -462,7 +465,8 @@ int cras_iodev_get_output_buffer(struct cras_iodev *iodev,
unsigned *frames);
/* Update the estimated sample rate of the device. */
-int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level);
+int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
+ struct timespec *level_tstamp);
/* Resets the rate estimator of the device. */
int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev);
@@ -474,8 +478,13 @@ double cras_iodev_get_est_rate_ratio(const struct cras_iodev *iodev);
/* Get the delay from DSP processing in frames. */
int cras_iodev_get_dsp_delay(const struct cras_iodev *iodev);
-/* Returns the number of frames in the hardware buffer. */
-int cras_iodev_frames_queued(struct cras_iodev *iodev);
+/* Returns the number of frames in the hardware buffer.
+ * Args:
+ * iodev - The device.
+ * tstamp - The associated hardware time stamp.
+ */
+int cras_iodev_frames_queued(struct cras_iodev *iodev,
+ struct timespec *tstamp);
/* Get the delay for input/output in frames. */
static inline int cras_iodev_delay_frames(const struct cras_iodev *iodev)
@@ -507,12 +516,14 @@ int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames);
/* Gets the number of frames to play when audio thread sleeps.
* Args:
* iodev[in] - The device.
- * hw_level[output] - Pointer to number of frames in hardware.
+ * hw_level[out] - Pointer to number of frames in hardware.
+ * hw_tstamp[out] - Pointer to the timestamp for hw_level.
* Returns:
* Number of frames to play in sleep for this output device.
*/
unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
- unsigned int *hw_level);
+ unsigned int *hw_level,
+ struct timespec *hw_tstamp);
/* Checks if audio thread should wake for this output device.
* Args:
diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c
index f26da616..f532a80b 100644
--- a/cras/src/server/cras_loopback_iodev.c
+++ b/cras/src/server/cras_loopback_iodev.c
@@ -117,7 +117,8 @@ static void device_enabled_hook(struct cras_iodev *iodev, int enabled,
* iodev callbacks.
*/
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *hw_tstamp)
{
struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev;
struct byte_buffer *sbuf = loopdev->sample_buffer;
@@ -141,13 +142,15 @@ static int frames_queued(const struct cras_iodev *iodev)
&loopdev->last_filled);
}
}
-
+ *hw_tstamp = loopdev->last_filled;
return buf_queued_bytes(sbuf) / frame_bytes;
}
static int delay_frames(const struct cras_iodev *iodev)
{
- return frames_queued(iodev);
+ struct timespec tstamp;
+
+ return frames_queued(iodev, &tstamp);
}
static int close_record_dev(struct cras_iodev *iodev)
diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c
index 630b3ea6..a2d735a8 100644
--- a/cras/src/server/test_iodev.c
+++ b/cras/src/server/test_iodev.c
@@ -45,7 +45,8 @@ struct test_iodev {
* iodev callbacks.
*/
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
struct test_iodev *testio = (struct test_iodev *)iodev;
int available;
@@ -53,6 +54,7 @@ static int frames_queued(const struct cras_iodev *iodev)
if (testio->fd < 0)
return 0;
ioctl(testio->fd, FIONREAD, &available);
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return available / testio->fmt_bytes;
}
diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc
index 07253ed7..a090500e 100644
--- a/cras/src/tests/a2dp_iodev_unittest.cc
+++ b/cras/src/tests/a2dp_iodev_unittest.cc
@@ -239,6 +239,7 @@ TEST(A2dpIoInit, GetPutBuffer) {
TEST(A2dpIoInif, FramesQueued) {
struct cras_iodev *iodev;
struct cras_audio_area *area;
+ struct timespec tstamp;
unsigned frames;
ResetStubData();
@@ -267,7 +268,9 @@ TEST(A2dpIoInif, FramesQueued) {
time_now.tv_nsec = 1000000;
iodev->put_buffer(iodev, 300);
write_callback(write_callback_data);
- EXPECT_EQ(350, iodev->frames_queued(iodev));
+ EXPECT_EQ(350, iodev->frames_queued(iodev, &tstamp));
+ EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
/* After writing another 200 frames, check for correct buffer level. */
time_now.tv_sec = 0;
@@ -277,9 +280,11 @@ TEST(A2dpIoInif, FramesQueued) {
a2dp_encode_processed_bytes_val[0] = 800;
write_callback(write_callback_data);
/* 1000000 nsec has passed, estimated queued frames adjusted by 44 */
- EXPECT_EQ(256, iodev->frames_queued(iodev));
+ EXPECT_EQ(256, iodev->frames_queued(iodev, &tstamp));
EXPECT_EQ(1200, pcm_buf_size_val[0]);
EXPECT_EQ(400, pcm_buf_size_val[1]);
+ EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
/* Queued frames and new put buffer are all written */
a2dp_encode_processed_bytes_val[0] = 400;
@@ -295,7 +300,9 @@ TEST(A2dpIoInif, FramesQueued) {
a2dp_encode_processed_bytes_val[0] = 600;
iodev->put_buffer(iodev, 200);
EXPECT_EQ(1200, pcm_buf_size_val[0]);
- EXPECT_EQ(200, iodev->frames_queued(iodev));
+ EXPECT_EQ(200, iodev->frames_queued(iodev, &tstamp));
+ EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
}
} // namespace
diff --git a/cras/src/tests/alsa_helpers_unittest.cc b/cras/src/tests/alsa_helpers_unittest.cc
index 6cb68051..3f2eb1ba 100644
--- a/cras/src/tests/alsa_helpers_unittest.cc
+++ b/cras/src/tests/alsa_helpers_unittest.cc
@@ -3,12 +3,28 @@
// found in the LICENSE file.
#include <gtest/gtest.h>
+#include <vector>
extern "C" {
// For static function test.
#include "cras_alsa_helpers.c"
}
+static int snd_pcm_sw_params_set_tstamp_type_called;
+static int snd_pcm_sw_params_set_tstamp_mode_called;
+static snd_pcm_uframes_t snd_pcm_htimestamp_avail_ret_val;
+static timespec snd_pcm_htimestamp_tstamp_ret_val;
+static std::vector<int> snd_pcm_sw_params_ret_vals;
+
+static void ResetStubData() {
+ snd_pcm_sw_params_set_tstamp_type_called = 0;
+ snd_pcm_sw_params_set_tstamp_mode_called = 0;
+ snd_pcm_htimestamp_avail_ret_val = 0;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_sec = 0;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_nsec = 0;
+ snd_pcm_sw_params_ret_vals.clear();
+}
+
namespace {
static snd_pcm_chmap_query_t *create_chmap_cap(snd_pcm_chmap_type type,
@@ -147,8 +163,114 @@ TEST(AlsaHelper, MatchChannelMapCapability51) {
cras_audio_format_destroy(fmt);
}
+TEST(AlsaHelper, Htimestamp) {
+ snd_pcm_t *dummy_handle = reinterpret_cast<snd_pcm_t*>(0x1);
+ snd_pcm_uframes_t used;
+ struct timespec tstamp;
+ unsigned int underruns = 0;
+ int htimestamp_enabled = 1;
+
+ // Enable htimestamp use.
+ ResetStubData();
+ EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 1);
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 1);
+ EXPECT_EQ(1, htimestamp_enabled);
+
+ // Try to enable htimestamp use: not supported.
+ ResetStubData();
+ snd_pcm_sw_params_ret_vals.push_back(-EINVAL);
+ EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 2);
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 2);
+ EXPECT_EQ(0, htimestamp_enabled);
+
+ // Disable htimestamp use.
+ ResetStubData();
+ EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled));
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 0);
+ EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 0);
+
+ ResetStubData();
+ tstamp.tv_sec = 0;
+ tstamp.tv_nsec = 0;
+ snd_pcm_htimestamp_avail_ret_val = 20000;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_sec = 10;
+ snd_pcm_htimestamp_tstamp_ret_val.tv_nsec = 10000;
+
+ cras_alsa_get_avail_frames(dummy_handle, 48000, &used, &tstamp, &underruns);
+ EXPECT_EQ(used, snd_pcm_htimestamp_avail_ret_val);
+ EXPECT_EQ(tstamp.tv_sec, snd_pcm_htimestamp_tstamp_ret_val.tv_sec);
+ EXPECT_EQ(tstamp.tv_nsec, snd_pcm_htimestamp_tstamp_ret_val.tv_nsec);
+}
+
} // namespace
+extern "C" {
+
+int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) {
+ return 0;
+}
+
+int snd_pcm_sw_params_get_boundary(const snd_pcm_sw_params_t *params,
+ snd_pcm_uframes_t *val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_stop_threshold(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_uframes_t val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_start_threshold(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_uframes_t val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_period_event(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params, int val) {
+ return 0;
+}
+
+int snd_pcm_sw_params_set_tstamp_mode(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_tstamp_t val) {
+ snd_pcm_sw_params_set_tstamp_mode_called++;
+ return 0;
+}
+
+int snd_pcm_sw_params_set_tstamp_type(snd_pcm_t *pcm,
+ snd_pcm_sw_params_t *params,
+ snd_pcm_tstamp_type_t val) {
+ snd_pcm_sw_params_set_tstamp_type_called++;
+ return 0;
+}
+
+int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) {
+ int rc;
+
+ if (snd_pcm_sw_params_ret_vals.size() == 0)
+ return 0;
+ rc = snd_pcm_sw_params_ret_vals.back();
+ snd_pcm_sw_params_ret_vals.pop_back();
+ return rc;
+}
+
+snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm) {
+ return snd_pcm_htimestamp_avail_ret_val;
+}
+
+int snd_pcm_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
+ snd_htimestamp_t *tstamp) {
+ *avail = snd_pcm_htimestamp_avail_ret_val;
+ *tstamp = snd_pcm_htimestamp_tstamp_ret_val;
+ return 0;
+}
+
+}
+
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc
index 431918e5..edacbb3d 100644
--- a/cras/src/tests/alsa_io_unittest.cc
+++ b/cras/src/tests/alsa_io_unittest.cc
@@ -2043,15 +2043,17 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format,
{
return 0;
}
-int cras_alsa_set_swparams(snd_pcm_t *handle)
+int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp)
{
return 0;
}
int cras_alsa_get_avail_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
snd_pcm_uframes_t *used,
+ struct timespec *tstamp,
unsigned int *num_underruns)
{
*used = cras_alsa_get_avail_frames_avail;
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return cras_alsa_get_avail_frames_ret;
}
int cras_alsa_get_delay_frames(snd_pcm_t *handle, snd_pcm_uframes_t buf_size,
@@ -2533,8 +2535,9 @@ int cras_iodev_reset_rate_estimator(const struct cras_iodev *iodev)
return 0;
}
-int cras_iodev_frames_queued(struct cras_iodev *iodev)
+int cras_iodev_frames_queued(struct cras_iodev *iodev, struct timespec *tstamp)
{
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return cras_iodev_frames_queued_ret;
}
diff --git a/cras/src/tests/audio_thread_unittest.cc b/cras/src/tests/audio_thread_unittest.cc
index 29aca3b0..3b1c9207 100644
--- a/cras/src/tests/audio_thread_unittest.cc
+++ b/cras/src/tests/audio_thread_unittest.cc
@@ -126,7 +126,8 @@ class StreamDeviceSuite : public testing::Test {
return 0;
}
- static int frames_queued(const cras_iodev* iodev) {
+ static int frames_queued(const cras_iodev* iodev, struct timespec* tstamp) {
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return frames_queued_;
}
@@ -630,7 +631,8 @@ void cras_iodev_stream_written(struct cras_iodev *iodev,
{
}
-int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level)
+int cras_iodev_update_rate(struct cras_iodev *iodev, unsigned int level,
+ struct timespec *level_tstamp)
{
return 0;
}
@@ -828,14 +830,15 @@ void dev_stream_update_frames(const struct dev_stream *dev_stream)
{
}
-int cras_iodev_frames_queued(struct cras_iodev *iodev)
+int cras_iodev_frames_queued(struct cras_iodev *iodev, struct timespec *tstamp)
{
- return iodev->frames_queued(iodev);
+ return iodev->frames_queued(iodev, tstamp);
}
int cras_iodev_buffer_avail(struct cras_iodev *iodev, unsigned hw_level)
{
- return iodev->buffer_size - iodev->frames_queued(iodev);
+ struct timespec tstamp;
+ return iodev->buffer_size - iodev->frames_queued(iodev, &tstamp);
}
int cras_iodev_fill_odev_zeros(struct cras_iodev *odev, unsigned int frames)
@@ -862,8 +865,10 @@ float cras_iodev_get_software_gain_scaler(const struct cras_iodev *iodev)
}
unsigned int cras_iodev_frames_to_play_in_sleep(struct cras_iodev *odev,
- unsigned int *hw_level)
+ unsigned int *hw_level,
+ struct timespec *hw_tstamp)
{
+ clock_gettime(CLOCK_MONOTONIC_RAW, hw_tstamp);
*hw_level = 0;
return 0;
}
diff --git a/cras/src/tests/bt_io_unittest.cc b/cras/src/tests/bt_io_unittest.cc
index 5e66f31a..979404ac 100644
--- a/cras/src/tests/bt_io_unittest.cc
+++ b/cras/src/tests/bt_io_unittest.cc
@@ -98,7 +98,8 @@ class BtIoBasicSuite : public testing::Test {
update_supported_formats_called_++;
return 0;
}
- static int frames_queued(const cras_iodev* iodev) {
+ static int frames_queued(const cras_iodev* iodev,
+ struct timespec *tstamp) {
frames_queued_called_++;
return 0;
}
@@ -152,6 +153,7 @@ unsigned int BtIoBasicSuite::close_dev_called_;
TEST_F(BtIoBasicSuite, CreateBtIo) {
struct cras_audio_area *fake_area;
struct cras_audio_format fake_fmt;
+ struct timespec tstamp;
unsigned fr;
bt_iodev = cras_bt_io_create(fake_device, &iodev_,
CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE);
@@ -164,7 +166,7 @@ TEST_F(BtIoBasicSuite, CreateBtIo) {
bt_iodev->open_dev(bt_iodev);
EXPECT_EQ(1, open_dev_called_);
- bt_iodev->frames_queued(bt_iodev);
+ bt_iodev->frames_queued(bt_iodev, &tstamp);
EXPECT_EQ(1, frames_queued_called_);
bt_iodev->get_buffer(bt_iodev, &fake_area, &fr);
EXPECT_EQ(1, get_buffer_called_);
diff --git a/cras/src/tests/cras_test_client.c b/cras/src/tests/cras_test_client.c
index 7b16dbc6..b897f8d5 100644
--- a/cras/src/tests/cras_test_client.c
+++ b/cras/src/tests/cras_test_client.c
@@ -405,6 +405,10 @@ static void show_alog_tag(const struct audio_thread_event_log *log,
printf("%-30s dev:%x hw_level:%u read:%u\n",
"READ_AUDIO", data1, data2, data3);
break;
+ case AUDIO_THREAD_READ_AUDIO_TSTAMP:
+ printf("%-30s dev:%x tstamp:%09d.%09d\n",
+ "READ_AUDIO_TSTAMP", data1, (int)data2, (int)data3);
+ break;
case AUDIO_THREAD_READ_AUDIO_DONE:
printf("%-30s read_remainder:%u\n", "READ_AUDIO_DONE", data1);
break;
@@ -412,6 +416,10 @@ static void show_alog_tag(const struct audio_thread_event_log *log,
printf("%-30s dev:%x hw_level:%u\n",
"FILL_AUDIO", data1, data2);
break;
+ case AUDIO_THREAD_FILL_AUDIO_TSTAMP:
+ printf("%-30s dev:%x tstamp:%09d.%09d\n",
+ "FILL_AUDIO_TSTAMP", data1, (int)data2, (int)data3);
+ break;
case AUDIO_THREAD_FILL_AUDIO_DONE:
printf("%-30s hw_level:%u total_written:%u min_cb_level:%u\n",
"FILL_AUDIO_DONE", data1, data2, data3);
diff --git a/cras/src/tests/hfp_info_unittest.cc b/cras/src/tests/hfp_info_unittest.cc
index 20be29c4..841338c9 100644
--- a/cras/src/tests/hfp_info_unittest.cc
+++ b/cras/src/tests/hfp_info_unittest.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+/* Copyright 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
@@ -63,6 +63,7 @@ TEST(HfpInfo, AddRmDevInvalid) {
TEST(HfpInfo, AcquirePlaybackBuffer) {
unsigned buffer_frames, buffer_frames2, queued;
uint8_t *samples;
+ struct timespec tstamp;
ResetStubData();
@@ -78,12 +79,12 @@ TEST(HfpInfo, AcquirePlaybackBuffer) {
ASSERT_EQ(500, buffer_frames);
hfp_buf_release(info, &dev, 500);
- ASSERT_EQ(500, hfp_buf_queued(info, &dev));
+ ASSERT_EQ(500, hfp_buf_queued(info, &dev, &tstamp));
/* Assert the amount of frames of available buffer + queued buf is
* greater than or equal to the buffer size, 2 bytes per frame
*/
- queued = hfp_buf_queued(info, &dev);
+ queued = hfp_buf_queued(info, &dev, &tstamp);
buffer_frames = 500;
hfp_buf_acquire(info, &dev, &samples, &buffer_frames);
ASSERT_GE(info->playback_buf->used_size / 2, buffer_frames + queued);
@@ -91,7 +92,7 @@ TEST(HfpInfo, AcquirePlaybackBuffer) {
/* Consume all queued data from read buffer */
buf_increment_read(info->playback_buf, queued * 2);
- queued = hfp_buf_queued(info, &dev);
+ queued = hfp_buf_queued(info, &dev, &tstamp);
ASSERT_EQ(0, queued);
/* Assert consecutive acquire buffer will acquire full used size of buffer */
@@ -111,6 +112,7 @@ TEST(HfpInfo, AcquirePlaybackBuffer) {
TEST(HfpInfo, AcquireCaptureBuffer) {
unsigned buffer_frames, buffer_frames2;
uint8_t *samples;
+ struct timespec tstamp;
ResetStubData();
@@ -130,7 +132,7 @@ TEST(HfpInfo, AcquireCaptureBuffer) {
ASSERT_EQ(50, buffer_frames);
hfp_buf_release(info, &dev, buffer_frames);
- ASSERT_EQ(0, hfp_buf_queued(info, &dev));
+ ASSERT_EQ(0, hfp_buf_queued(info, &dev, &tstamp));
/* Push fake data to capture buffer */
buf_increment_write(info->capture_buf, info->capture_buf->used_size - 100);
@@ -157,6 +159,7 @@ TEST(HfpInfo, HfpReadWriteFD) {
uint8_t sample[480];
uint8_t *buf;
unsigned buffer_count;
+ struct timespec tstamp;
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
@@ -173,7 +176,7 @@ TEST(HfpInfo, HfpReadWriteFD) {
rc = hfp_read(info);
ASSERT_EQ(48, rc);
- rc = hfp_buf_queued(info, &dev);
+ rc = hfp_buf_queued(info, &dev, &tstamp);
ASSERT_EQ(48 / 2, rc);
/* Fill the write buffer*/
@@ -229,6 +232,7 @@ TEST(HfpInfo, StartHfpInfoAndRead) {
int rc;
int sock[2];
uint8_t sample[480];
+ struct timespec tstamp;
ResetStubData();
@@ -249,7 +253,7 @@ TEST(HfpInfo, StartHfpInfoAndRead) {
ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
/* Expect no data read, since no idev present at previous thread callback */
- rc = hfp_buf_queued(info, &dev);
+ rc = hfp_buf_queued(info, &dev, &tstamp);
ASSERT_EQ(0, rc);
/* Trigger thread callback after idev added. */
@@ -257,7 +261,7 @@ TEST(HfpInfo, StartHfpInfoAndRead) {
ts.tv_nsec = 5000000;
thread_cb((struct hfp_info *)cb_data);
- rc = hfp_buf_queued(info, &dev);
+ rc = hfp_buf_queued(info, &dev, &tstamp);
ASSERT_EQ(48 / 2, rc);
/* Assert wait time is unchanged. */
@@ -274,6 +278,7 @@ TEST(HfpInfo, StartHfpInfoAndWrite) {
int rc;
int sock[2];
uint8_t sample[480];
+ struct timespec tstamp;
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sock));
@@ -291,7 +296,7 @@ TEST(HfpInfo, StartHfpInfoAndWrite) {
ASSERT_EQ(0, hfp_info_add_iodev(info, &dev));
/* Assert queued samples unchanged before output device added */
- ASSERT_EQ(0, hfp_buf_queued(info, &dev));
+ ASSERT_EQ(0, hfp_buf_queued(info, &dev, &tstamp));
/* Put some fake data and trigger thread callback again */
buf_increment_write(info->playback_buf, 1008);
@@ -300,7 +305,7 @@ TEST(HfpInfo, StartHfpInfoAndWrite) {
/* Assert some samples written */
rc = recv(sock[0], sample ,48, 0);
ASSERT_EQ(48, rc);
- ASSERT_EQ(480, hfp_buf_queued(info, &dev));
+ ASSERT_EQ(480, hfp_buf_queued(info, &dev, &tstamp));
hfp_info_stop(info);
hfp_info_destroy(info);
diff --git a/cras/src/tests/hfp_iodev_unittest.cc b/cras/src/tests/hfp_iodev_unittest.cc
index 202bb135..4ad84839 100644
--- a/cras/src/tests/hfp_iodev_unittest.cc
+++ b/cras/src/tests/hfp_iodev_unittest.cc
@@ -288,7 +288,8 @@ int hfp_info_stop(struct hfp_info *info)
return 0;
}
-int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev)
+int hfp_buf_queued(struct hfp_info *info, const struct cras_iodev *dev,
+ struct timespec *tstamp)
{
return 0;
}
diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc
index 57dc3ba2..e15275a4 100644
--- a/cras/src/tests/iodev_unittest.cc
+++ b/cras/src/tests/iodev_unittest.cc
@@ -665,13 +665,16 @@ TEST(IoDevPutOutputBuffer, Scale32Bit) {
static unsigned fr_queued = 0;
-static int frames_queued(const struct cras_iodev *iodev)
+static int frames_queued(const struct cras_iodev *iodev,
+ struct timespec *tstamp)
{
+ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp);
return fr_queued;
}
TEST(IoDevQueuedBuffer, ZeroMinBufferLevel) {
struct cras_iodev iodev;
+ struct timespec tstamp;
int rc;
ResetStubData();
@@ -682,7 +685,7 @@ TEST(IoDevQueuedBuffer, ZeroMinBufferLevel) {
iodev.buffer_size = 200;
fr_queued = 50;
- rc = cras_iodev_frames_queued(&iodev);
+ rc = cras_iodev_frames_queued(&iodev, &tstamp);
EXPECT_EQ(50, rc);
rc = cras_iodev_buffer_avail(&iodev, rc);
EXPECT_EQ(150, rc);
@@ -690,6 +693,7 @@ TEST(IoDevQueuedBuffer, ZeroMinBufferLevel) {
TEST(IoDevQueuedBuffer, NonZeroMinBufferLevel) {
struct cras_iodev iodev;
+ struct timespec hw_tstamp;
int rc;
ResetStubData();
@@ -700,14 +704,14 @@ TEST(IoDevQueuedBuffer, NonZeroMinBufferLevel) {
iodev.buffer_size = 200;
fr_queued = 180;
- rc = cras_iodev_frames_queued(&iodev);
+ rc = cras_iodev_frames_queued(&iodev, &hw_tstamp);
EXPECT_EQ(80, rc);
rc = cras_iodev_buffer_avail(&iodev, rc);
EXPECT_EQ(20, rc);
/* When fr_queued < min_buffer_level*/
fr_queued = 80;
- rc = cras_iodev_frames_queued(&iodev);
+ rc = cras_iodev_frames_queued(&iodev, &hw_tstamp);
EXPECT_EQ(0, rc);
rc = cras_iodev_buffer_avail(&iodev, rc);
EXPECT_EQ(100, rc);
@@ -1283,6 +1287,7 @@ TEST(IoDev, FramesToPlayInSleep) {
struct cras_iodev iodev;
unsigned int min_cb_level = 240, hw_level;
unsigned int got_hw_level, got_frames;
+ struct timespec hw_tstamp;
memset(&iodev, 0, sizeof(iodev));
iodev.frames_queued = frames_queued;
@@ -1300,7 +1305,8 @@ TEST(IoDev, FramesToPlayInSleep) {
fr_queued = hw_level;
iodev.streams = reinterpret_cast<struct dev_stream *>(0x1);
- got_frames = cras_iodev_frames_to_play_in_sleep(&iodev, &got_hw_level);
+ got_frames = cras_iodev_frames_to_play_in_sleep(
+ &iodev, &got_hw_level, &hw_tstamp);
EXPECT_EQ(hw_level, got_hw_level);
EXPECT_EQ(hw_level, got_frames);
@@ -1308,7 +1314,8 @@ TEST(IoDev, FramesToPlayInSleep) {
// hw_level is greater than min_cb_level.
iodev.streams = NULL;
- got_frames = cras_iodev_frames_to_play_in_sleep(&iodev, &got_hw_level);
+ got_frames = cras_iodev_frames_to_play_in_sleep(
+ &iodev, &got_hw_level, &hw_tstamp);
EXPECT_EQ(hw_level, got_hw_level);
EXPECT_EQ(hw_level - min_cb_level, got_frames);
@@ -1318,7 +1325,8 @@ TEST(IoDev, FramesToPlayInSleep) {
hw_level = min_cb_level - 50;
fr_queued = hw_level;
- got_frames = cras_iodev_frames_to_play_in_sleep(&iodev, &got_hw_level);
+ got_frames = cras_iodev_frames_to_play_in_sleep(
+ &iodev, &got_hw_level, &hw_tstamp);
EXPECT_EQ(hw_level, got_hw_level);
EXPECT_EQ(0, got_frames);
}
diff --git a/cras/src/tests/loopback_iodev_unittest.cc b/cras/src/tests/loopback_iodev_unittest.cc
index ca3a75a6..b0387bfd 100644
--- a/cras/src/tests/loopback_iodev_unittest.cc
+++ b/cras/src/tests/loopback_iodev_unittest.cc
@@ -68,6 +68,7 @@ class LoopBackTestSuite : public testing::Test{
TEST_F(LoopBackTestSuite, InstallLoopHook) {
struct cras_iodev iodev;
+ struct timespec tstamp;
iodev.direction = CRAS_STREAM_OUTPUT;
iodev.format = &fmt_;
@@ -86,7 +87,7 @@ TEST_F(LoopBackTestSuite, InstallLoopHook) {
ASSERT_NE(reinterpret_cast<loopback_hook_t>(NULL), loop_hook);
// Check zero frames queued.
- EXPECT_EQ(0, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
// Close loopback devices.
EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
@@ -97,6 +98,7 @@ TEST_F(LoopBackTestSuite, InstallLoopHook) {
TEST_F(LoopBackTestSuite, OpenIdleSystem) {
cras_audio_area *area;
unsigned int nread = 1024;
+ struct timespec tstamp;
int rc;
// No active output device.
@@ -109,7 +111,7 @@ TEST_F(LoopBackTestSuite, OpenIdleSystem) {
// Should be 480 samples after 480/frame rate seconds
time_now.tv_nsec += 480 * 1e9 / 48000;
- EXPECT_EQ(480, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(480, loop_in_->frames_queued(loop_in_, &tstamp));
// Verify frames from loopback record.
loop_in_->get_buffer(loop_in_, &area, &nread);
@@ -120,7 +122,7 @@ TEST_F(LoopBackTestSuite, OpenIdleSystem) {
loop_in_->put_buffer(loop_in_, nread);
// Check zero frames queued.
- EXPECT_EQ(0, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
}
@@ -132,6 +134,7 @@ TEST_F(LoopBackTestSuite, SimpleLoopback) {
int rc;
struct cras_iodev iodev;
struct dev_stream stream;
+ struct timespec tstamp;
iodev.streams = &stream;
enabled_dev = &iodev;
@@ -150,7 +153,7 @@ TEST_F(LoopBackTestSuite, SimpleLoopback) {
loop_in_->put_buffer(loop_in_, nread);
// Check zero frames queued.
- EXPECT_EQ(0, loop_in_->frames_queued(loop_in_));
+ EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
}