diff options
author | John Muir <muirj@google.com> | 2016-07-31 14:57:44 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-09-10 22:48:08 -0700 |
commit | fa4cd9766aec6eae544b1078ed835687c4e74617 (patch) | |
tree | 44ad64a844430ce48c424e943795accedb61b943 /cras | |
parent | 654d8e6e44b31384dc62259c990bfff347391f74 (diff) | |
download | adhd-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')
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_)); } |