summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheng-Yi Chiang <cychiang@chromium.org>2017-02-28 07:08:18 +0800
committerchrome-bot <chrome-bot@chromium.org>2017-03-10 10:54:50 -0800
commit797b23b697f1ec051ab68843f23e3dd14bf8c01a (patch)
treefe5887cb212f9f421a90e7b24e035ffae99a03cf
parentbd7dee79e32fcd3452599f8e75d3599152369ddb (diff)
downloadadhd-797b23b697f1ec051ab68843f23e3dd14bf8c01a.tar.gz
CRAS: dev_stream - Avoid bursting captured data to stream
When there are enough captured samples, only send captured data to input stream when time is late enough. Otherwise, client can not handle this much data. Note that stream with BULK_AUDIO_OK flag can still send data when there is enough data. BUG=chrome-os-partner:60786 TEST=make check TEST=Play sine tone use sox command. At the same time, use cras_test_client --loopback_f /tmp/lb.raw to record using loopback device. Check num_overruns in audio thread log. Also, check CAPTURE_POST in audio thread log and see the interval is roughly sleep interval. Change-Id: I1a6e7056cd0bab40e2e597ca08ef524b7ff1207f Reviewed-on: https://chromium-review.googlesource.com/447184 Commit-Ready: Cheng-Yi Chiang <cychiang@chromium.org> Tested-by: Cheng-Yi Chiang <cychiang@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
-rw-r--r--cras/src/server/dev_stream.c43
-rw-r--r--cras/src/tests/dev_stream_unittest.cc179
2 files changed, 218 insertions, 4 deletions
diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c
index 4ae1c4d1..addb3ba7 100644
--- a/cras/src/server/dev_stream.c
+++ b/cras/src/server/dev_stream.c
@@ -23,6 +23,15 @@ static const unsigned int capture_extra_sleep_frames = 20;
*/
static const int coarse_rate_adjust_step = 3;
+/*
+ * Allow capture callback to fire this much earlier than the scheduled
+ * next_cb_ts to avoid an extra wake of audio thread.
+ */
+static const struct timespec capture_callback_fuzz_ts = {
+ .tv_sec = 0,
+ .tv_nsec = 1000000, /* 1 ms. */
+};
+
struct dev_stream *dev_stream_create(struct cras_rstream *stream,
unsigned int dev_id,
const struct cras_audio_format *dev_fmt,
@@ -470,17 +479,32 @@ int dev_stream_playback_update_rstream(struct dev_stream *dev_stream)
return 0;
}
+static int late_enough_for_capture_callback(struct dev_stream *dev_stream)
+{
+ struct timespec now;
+ struct cras_rstream *rstream = dev_stream->stream;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ add_timespecs(&now, &capture_callback_fuzz_ts);
+ return timespec_after(&now, &rstream->next_cb_ts);
+}
+
int dev_stream_capture_update_rstream(struct dev_stream *dev_stream)
{
struct cras_rstream *rstream = dev_stream->stream;
unsigned int frames_ready = cras_rstream_get_cb_threshold(rstream);
- struct timespec now;
+ int rc;
cras_rstream_update_input_write_pointer(rstream);
- /* If it isn't time for this stream then skip it. */
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+ /*
+ * For stream without BULK_AUDIO_OK flag, if it isn't time for
+ * this stream then skip it.
+ */
+ if (!(rstream->flags & BULK_AUDIO_OK) &&
+ !late_enough_for_capture_callback(dev_stream))
+ return 0;
+ /* If there is not enough data for one callback, skip it. */
if (!cras_rstream_input_level_met(rstream))
return 0;
@@ -493,7 +517,18 @@ int dev_stream_capture_update_rstream(struct dev_stream *dev_stream)
frames_ready,
rstream->shm.area->read_buf_idx);
- return cras_rstream_audio_ready(rstream, frames_ready);
+ rc = cras_rstream_audio_ready(rstream, frames_ready);
+
+ if (rc < 0)
+ return rc;
+
+ /* Update next callback time according to perfect schedule. */
+ add_timespecs(&rstream->next_cb_ts,
+ &rstream->sleep_interval_ts);
+ /* Reset schedule if the schedule is missed. */
+ check_next_wake_time(dev_stream);
+
+ return 0;
}
void cras_set_playback_timestamp(size_t frame_rate,
diff --git a/cras/src/tests/dev_stream_unittest.cc b/cras/src/tests/dev_stream_unittest.cc
index f17bf64a..e20ae252 100644
--- a/cras/src/tests/dev_stream_unittest.cc
+++ b/cras/src/tests/dev_stream_unittest.cc
@@ -93,6 +93,9 @@ static struct rstream_get_readable_call rstream_get_readable_call;
static unsigned int rstream_get_readable_num;
static uint8_t *rstream_get_readable_ptr;
+static int cras_rstream_audio_ready_called;
+static int cras_rstream_audio_ready_count;
+
class CreateSuite : public testing::Test{
protected:
virtual void SetUp() {
@@ -118,6 +121,9 @@ class CreateSuite : public testing::Test{
cras_fmt_conversion_needed_val = 0;
cras_fmt_conv_set_linear_resample_rates_called = 0;
+ cras_rstream_audio_ready_called = 0;
+ cras_rstream_audio_ready_count = 0;
+
memset(&copy_area_call, 0xff, sizeof(copy_area_call));
memset(&conv_frames_call, 0xff, sizeof(conv_frames_call));
@@ -530,6 +536,177 @@ TEST_F(CreateSuite, StreamCanFetch) {
dev_stream_destroy(dev_stream);
}
+TEST_F(CreateSuite, StreamCanSend) {
+ struct dev_stream *dev_stream;
+ unsigned int dev_id = 9;
+ int written_frames;
+ int rc;
+ struct timespec expected_next_cb_ts;
+
+ rstream_.direction = CRAS_STREAM_INPUT;
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+ (void *)0x55, &cb_ts);
+
+ // Assume there is a next_cb_ts on rstream.
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ // Case 1: Not enough samples. Time is not late enough.
+ // Stream can not send data to client.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 2: Not enough samples. Time is late enough.
+ // Stream can not send data to client.
+
+ // Assume time is greater than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ // However, written frames is less than cb_threshold.
+ // Stream still can not send samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 3: Enough samples. Time is not late enough.
+ // Stream can not send data to client.
+
+ // Assume time is less than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ // Enough samples are written.
+ written_frames = rstream_.cb_threshold + 10;
+ cras_shm_buffer_written(&rstream_.shm, written_frames);
+ // Stream still can not send samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 4: Enough samples. Time is late enough.
+ // Stream should send one cb_threshold to client.
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(rstream_.cb_threshold, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Check next_cb_ts is increased by one sleep interval.
+ expected_next_cb_ts.tv_sec = 1;
+ expected_next_cb_ts.tv_nsec = 0;
+ add_timespecs(&expected_next_cb_ts,
+ &rstream_.sleep_interval_ts);
+ EXPECT_EQ(expected_next_cb_ts.tv_sec, rstream_.next_cb_ts.tv_sec);
+ EXPECT_EQ(expected_next_cb_ts.tv_nsec, rstream_.next_cb_ts.tv_nsec);
+
+ // Reset stub data of interest.
+ cras_rstream_audio_ready_called = 0;
+ cras_rstream_audio_ready_count = 0;
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ // Case 5: Enough samples. Time is late enough and it is too late
+ // such that a new next_cb_ts is in the past.
+ // Stream should send one cb_threshold to client and reset schedule.
+ clock_gettime_retspec.tv_sec = 2;
+ clock_gettime_retspec.tv_nsec = 0;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(rstream_.cb_threshold, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Check next_cb_ts is rest to be now plus one sleep interval.
+ expected_next_cb_ts.tv_sec = 2;
+ expected_next_cb_ts.tv_nsec = 0;
+ add_timespecs(&expected_next_cb_ts,
+ &rstream_.sleep_interval_ts);
+ EXPECT_EQ(expected_next_cb_ts.tv_sec, rstream_.next_cb_ts.tv_sec);
+ EXPECT_EQ(expected_next_cb_ts.tv_nsec, rstream_.next_cb_ts.tv_nsec);
+
+ dev_stream_destroy(dev_stream);
+}
+
+TEST_F(CreateSuite, StreamCanSendBulkAudio) {
+ struct dev_stream *dev_stream;
+ unsigned int dev_id = 9;
+ int written_frames;
+ int rc;
+ struct timespec expected_next_cb_ts;
+
+ rstream_.direction = CRAS_STREAM_INPUT;
+ rstream_.flags |= BULK_AUDIO_OK;
+ dev_stream = dev_stream_create(&rstream_, dev_id, &fmt_s16le_44_1,
+ (void *)0x55, &cb_ts);
+
+ // Assume there is a next_cb_ts on rstream.
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ // Case 1: Not enough samples. Time is not late enough.
+ // Bulk audio stream can not send data to client.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 2: Not enough samples. Time is late enough.
+ // Bulk audio stream can not send data to client.
+
+ // Assume time is greater than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ // However, written frames is less than cb_threshold.
+ // Stream still can not send samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(0, cras_rstream_audio_ready_called);
+ EXPECT_EQ(0, rc);
+
+ // Case 3: Enough samples. Time is not late enough.
+ // Bulk audio stream CAN send data to client.
+
+ // Assume time is less than next_cb_ts.
+ clock_gettime_retspec.tv_sec = 0;
+ clock_gettime_retspec.tv_nsec = 0;
+ // Enough samples are written.
+ written_frames = rstream_.cb_threshold + 10;
+ cras_shm_buffer_written(&rstream_.shm, written_frames);
+ // Bulk audio stream can send all written samples to client.
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(written_frames, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Case 4: Enough samples. Time is late enough.
+ // Bulk audio stream can send all written samples to client.
+
+ // Reset stub data of interest.
+ cras_rstream_audio_ready_called = 0;
+ cras_rstream_audio_ready_count = 0;
+ rstream_.next_cb_ts.tv_sec = 1;
+ rstream_.next_cb_ts.tv_nsec = 0;
+
+ clock_gettime_retspec.tv_sec = 1;
+ clock_gettime_retspec.tv_nsec = 500;
+ rc = dev_stream_capture_update_rstream(dev_stream);
+ EXPECT_EQ(1, cras_rstream_audio_ready_called);
+ EXPECT_EQ(written_frames, cras_rstream_audio_ready_count);
+ EXPECT_EQ(0, rc);
+
+ // Check next_cb_ts is increased by one sleep interval.
+ expected_next_cb_ts.tv_sec = 1;
+ expected_next_cb_ts.tv_nsec = 0;
+ add_timespecs(&expected_next_cb_ts,
+ &rstream_.sleep_interval_ts);
+ EXPECT_EQ(expected_next_cb_ts.tv_sec, rstream_.next_cb_ts.tv_sec);
+ EXPECT_EQ(expected_next_cb_ts.tv_nsec, rstream_.next_cb_ts.tv_nsec);
+
+ dev_stream_destroy(dev_stream);
+}
+
// Test set_playback_timestamp.
TEST(DevStreamTimimg, SetPlaybackTimeStampSimple) {
struct cras_timespec ts;
@@ -602,6 +779,8 @@ TEST(DevStreamTimimg, SetCaptureTimeStampWrapPartial) {
extern "C" {
int cras_rstream_audio_ready(struct cras_rstream *stream, size_t count) {
+ cras_rstream_audio_ready_count = count;
+ cras_rstream_audio_ready_called++;
return 0;
}