// Copyright (c) 2012 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. extern "C" { #include "audio_thread.c" } #include <stdio.h> #include <sys/select.h> #include <gtest/gtest.h> extern "C" { struct dev_stream_capture_call { struct dev_stream *dev_stream; const struct cras_audio_area *area; unsigned int dev_index; unsigned int num_called; }; struct cap_sleep_frames_call { struct dev_stream *dev_stream; unsigned int written; unsigned int num_called; }; static int dev_stream_mix_dont_fill_next; static unsigned int dev_stream_mix_count; static unsigned int cras_mix_mute_count; static unsigned int dev_stream_request_playback_samples_called; static unsigned int cras_rstream_destroy_called; static unsigned int cras_metrics_log_histogram_called; static const char *cras_metrics_log_histogram_name; static unsigned int cras_metrics_log_histogram_sample; static unsigned int cras_metrics_log_event_called; static void (*cras_system_add_select_fd_callback)(void *data); static void *cras_system_add_select_fd_callback_data; static int select_return_value; static struct timeval select_timeval; static int select_max_fd; static fd_set select_in_fds; static fd_set select_out_fds; static uint32_t *select_write_ptr; static uint32_t select_write_value; static unsigned int cras_iodev_set_format_called; static unsigned int dev_stream_set_delay_called; static unsigned int cras_system_get_volume_return; static unsigned int dev_stream_mix_called; static struct timespec time_now; static int cras_fmt_conversion_needed_return_val; static struct cras_audio_area *dummy_audio_area1; static struct cras_audio_area *dummy_audio_area2; static struct cras_audio_format cras_iodev_set_format_val; static struct dev_stream_capture_call dev_stream_capture_call; static struct cap_sleep_frames_call cap_sleep_frames_call; } // Number of frames past target that will be added to sleep times to insure that // all frames are ready. static const int CAP_EXTRA_SLEEP_FRAMES = 16; // Test the audio capture path. class ReadStreamSuite : public testing::Test { protected: virtual void SetUp() { memset(&cras_iodev_set_format_val, 0, sizeof(cras_iodev_set_format_val)); cras_iodev_set_format_val.frame_rate = 44100; cras_iodev_set_format_val.num_channels = 2; cras_iodev_set_format_val.format = SND_PCM_FORMAT_S16_LE; memset(&iodev_, 0, sizeof(iodev_)); iodev_.buffer_size = 16384; cb_threshold_ = 480; iodev_.direction = CRAS_STREAM_INPUT; iodev_.frames_queued = frames_queued; iodev_.delay_frames = delay_frames; iodev_.get_buffer = get_buffer; iodev_.put_buffer = put_buffer; iodev_.is_open = is_open; iodev_.open_dev = open_dev; iodev_.close_dev = close_dev; iodev_.dev_running = dev_running; memcpy(&output_dev_, &iodev_, sizeof(output_dev_)); output_dev_.direction = CRAS_STREAM_OUTPUT; SetupRstream(&rstream_, 1); shm_ = cras_rstream_input_shm(rstream_); SetupRstream(&rstream2_, 2); shm2_ = cras_rstream_input_shm(rstream2_); dummy_audio_area1 = (cras_audio_area*)calloc(1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); dummy_audio_area1->num_channels = 2; channel_area_set_channel(&dummy_audio_area1->channels[0], CRAS_CH_FL); channel_area_set_channel(&dummy_audio_area1->channels[1], CRAS_CH_FR); rstream_->input_audio_area = dummy_audio_area1; dummy_audio_area2 = (cras_audio_area*)calloc(1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); dummy_audio_area2->num_channels = 2; channel_area_set_channel(&dummy_audio_area2->channels[0], CRAS_CH_FL); channel_area_set_channel(&dummy_audio_area2->channels[1], CRAS_CH_FR); rstream2_->input_audio_area = dummy_audio_area2; dev_stream_mix_dont_fill_next = 0; dev_stream_mix_count = 0; dev_running_called_ = 0; is_open_ = 0; close_dev_called_ = 0; cras_iodev_set_format_called = 0; dev_stream_set_delay_called = 0; } virtual void TearDown() { free(shm_->area); free(rstream_); free(shm2_->area); free(rstream2_); free(dummy_audio_area1); free(dummy_audio_area2); } void SetupRstream(struct cras_rstream **rstream, int fd) { struct cras_audio_shm *shm; *rstream = (struct cras_rstream *)calloc(1, sizeof(**rstream)); memcpy(&(*rstream)->format, &cras_iodev_set_format_val, sizeof(cras_iodev_set_format_val)); (*rstream)->direction = CRAS_STREAM_INPUT; (*rstream)->cb_threshold = cb_threshold_; (*rstream)->client = (struct cras_rclient *)this; shm = cras_rstream_input_shm(*rstream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area) + cb_threshold_ * 8); cras_shm_set_frame_bytes(shm, 4); cras_shm_set_used_size( shm, cb_threshold_ * cras_shm_frame_bytes(shm)); } unsigned int GetCaptureSleepFrames() { // Account for padding the sleep interval to ensure the wake up happens // after the last desired frame is received. return cb_threshold_ + 16; } // Stub functions for the iodev structure. static int frames_queued(const cras_iodev* iodev) { return frames_queued_; } static int delay_frames(const cras_iodev* iodev) { return delay_frames_; } static int get_buffer(cras_iodev* iodev, struct cras_audio_area** area, unsigned int* num) { size_t sz = sizeof(*area_) + sizeof(struct cras_channel_area) * 2; if (audio_buffer_size_ < *num) *num = audio_buffer_size_; area_ = (cras_audio_area*)calloc(1, sz); area_->frames = *num; area_->num_channels = 2; area_->channels[0].buf = audio_buffer_; channel_area_set_channel(&area_->channels[0], CRAS_CH_FL); area_->channels[0].step_bytes = 4; area_->channels[1].buf = audio_buffer_ + 2; channel_area_set_channel(&area_->channels[1], CRAS_CH_FR); area_->channels[1].step_bytes = 4; *area = area_; return 0; } static int put_buffer(cras_iodev* iodev, unsigned int num) { free(area_); return 0; } static int is_open(const cras_iodev* iodev) { return is_open_; } static int open_dev(cras_iodev* iodev) { return 0; } static int close_dev(cras_iodev* iodev) { close_dev_called_++; return 0; } static int dev_running(const cras_iodev* iodev) { dev_running_called_++; return 1; } struct cras_iodev iodev_; struct cras_iodev output_dev_; static int is_open_; static int frames_queued_; static int delay_frames_; static unsigned int cb_threshold_; static uint8_t audio_buffer_[8192]; static struct cras_audio_area *area_; static unsigned int audio_buffer_size_; static unsigned int dev_running_called_; static unsigned int close_dev_called_; struct cras_rstream *rstream_; struct cras_rstream *rstream2_; struct cras_audio_shm *shm_; struct cras_audio_shm *shm2_; }; int ReadStreamSuite::is_open_ = 0; int ReadStreamSuite::frames_queued_ = 0; int ReadStreamSuite::delay_frames_ = 0; unsigned int ReadStreamSuite::close_dev_called_ = 0; uint8_t ReadStreamSuite::audio_buffer_[8192]; unsigned int ReadStreamSuite::audio_buffer_size_ = 0; unsigned int ReadStreamSuite::dev_running_called_ = 0; unsigned int ReadStreamSuite::cb_threshold_ = 0; struct cras_audio_area *ReadStreamSuite::area_; TEST_F(ReadStreamSuite, PossiblyReadGetAvailError) { struct timespec ts; int rc; struct audio_thread *thread; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); thread_add_stream(thread, rstream_); EXPECT_EQ(1, cras_iodev_set_format_called); frames_queued_ = -4; is_open_ = 1; rc = unified_io(thread, &ts); EXPECT_EQ(-4, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_EQ(0, ts.tv_nsec); EXPECT_EQ(0, dev_stream_set_delay_called); EXPECT_EQ(1, close_dev_called_); audio_thread_destroy(thread); } TEST_F(ReadStreamSuite, PossiblyReadEmpty) { struct timespec ts; int rc; uint64_t nsec_expected; struct audio_thread *thread; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); thread_add_stream(thread, rstream_); EXPECT_EQ(1, cras_iodev_set_format_called); // If no samples are present, it should sleep for cb_threshold frames. frames_queued_ = 0; is_open_ = 1; nsec_expected = (GetCaptureSleepFrames()) * 1000000000ULL / (uint64_t)cras_iodev_set_format_val.frame_rate; rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_EQ(0, shm_->area->write_offset[0]); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(1, dev_running_called_); EXPECT_EQ(1, dev_stream_set_delay_called); audio_thread_destroy(thread); } TEST_F(ReadStreamSuite, PossiblyReadTooLittleData) { struct timespec ts; int rc; uint64_t nsec_expected; static const uint64_t num_frames_short = 40; struct audio_thread *thread; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); thread_add_stream(thread, rstream_); frames_queued_ = cb_threshold_ - num_frames_short; is_open_ = 1; audio_buffer_size_ = frames_queued_; nsec_expected = ((uint64_t)num_frames_short + CAP_EXTRA_SLEEP_FRAMES) * 1000000000ULL / (uint64_t)cras_iodev_set_format_val.frame_rate; rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); /* As much data as can be, should be read. */ EXPECT_EQ(&audio_buffer_[0], dev_stream_capture_call.area->channels[0].buf); EXPECT_EQ(rstream_, dev_stream_capture_call.dev_stream->stream); EXPECT_EQ(cb_threshold_ - num_frames_short, cap_sleep_frames_call.written); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); audio_thread_destroy(thread); } TEST_F(ReadStreamSuite, PossiblyReadHasDataWriteStream) { struct timespec ts; int rc; uint64_t nsec_expected; struct audio_thread *thread; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); thread_add_stream(thread, rstream_); // A full block plus 4 frames. frames_queued_ = cb_threshold_ + 4; audio_buffer_size_ = frames_queued_; for (unsigned int i = 0; i < sizeof(audio_buffer_); i++) audio_buffer_[i] = i; uint64_t sleep_frames = GetCaptureSleepFrames() - 4; nsec_expected = (uint64_t)sleep_frames * 1000000000ULL / (uint64_t)cras_iodev_set_format_val.frame_rate; is_open_ = 1; // Give it some samples to copy. rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(1, dev_stream_set_delay_called); EXPECT_EQ(&audio_buffer_[0], dev_stream_capture_call.area->channels[0].buf); EXPECT_EQ(rstream_, dev_stream_capture_call.dev_stream->stream); EXPECT_EQ(cb_threshold_, cap_sleep_frames_call.written); EXPECT_EQ(rstream_, cap_sleep_frames_call.dev_stream->stream); audio_thread_destroy(thread); } TEST_F(ReadStreamSuite, PossiblyReadHasDataWriteTwoStreams) { struct timespec ts; int rc; uint64_t nsec_expected; struct audio_thread *thread; dev_stream_capture_call.num_called = 0; cap_sleep_frames_call.num_called = 0; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); rc = thread_add_stream(thread, rstream_); EXPECT_EQ(0, rc); rc = thread_add_stream(thread, rstream2_); EXPECT_EQ(0, rc); // A full block plus 4 frames. frames_queued_ = cb_threshold_ + 4; audio_buffer_size_ = frames_queued_; for (unsigned int i = 0; i < sizeof(audio_buffer_); i++) audio_buffer_[i] = i; uint64_t sleep_frames = GetCaptureSleepFrames() - 4; nsec_expected = (uint64_t)sleep_frames * 1000000000ULL / (uint64_t)cras_iodev_set_format_val.frame_rate; is_open_ = 1; // Give it some samples to copy. rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(2, dev_stream_capture_call.num_called); EXPECT_EQ(2, cap_sleep_frames_call.num_called); audio_thread_destroy(thread); } TEST_F(ReadStreamSuite, PossiblyReadHasDataWriteTwoDifferentStreams) { struct timespec ts; int rc; uint64_t nsec_expected; struct audio_thread *thread; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); cb_threshold_ /= 2; rstream_->cb_threshold = cb_threshold_; rc = thread_add_stream(thread, rstream_); EXPECT_EQ(0, rc); rc = thread_add_stream(thread, rstream2_); EXPECT_EQ(0, rc); // A full block plus 4 frames. frames_queued_ = cb_threshold_ + 4; audio_buffer_size_ = frames_queued_; uint64_t sleep_frames = GetCaptureSleepFrames() - 4; nsec_expected = (uint64_t)sleep_frames * 1000000000ULL / (uint64_t)cras_iodev_set_format_val.frame_rate; is_open_ = 1; // Give it some samples to copy. rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); frames_queued_ = cb_threshold_ + 5; sleep_frames = GetCaptureSleepFrames() - 5; nsec_expected = (uint64_t)sleep_frames * 1000000000ULL / (uint64_t)cras_iodev_set_format_val.frame_rate; audio_buffer_size_ = frames_queued_; is_open_ = 1; // Give it some samples to copy. rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); audio_thread_destroy(thread); } TEST_F(ReadStreamSuite, PossiblyReadWriteThreeBuffers) { struct timespec ts; int rc; struct audio_thread *thread; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); thread_add_stream(thread, rstream_); // A full block plus 4 frames. frames_queued_ = cb_threshold_ + 4; audio_buffer_size_ = frames_queued_; is_open_ = 1; // Give it some samples to copy. rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, cras_shm_num_overruns(shm_)); EXPECT_EQ(&audio_buffer_[0], dev_stream_capture_call.area->channels[0].buf); EXPECT_EQ(rstream_, dev_stream_capture_call.dev_stream->stream); EXPECT_EQ(cb_threshold_, cap_sleep_frames_call.written); EXPECT_EQ(rstream_, cap_sleep_frames_call.dev_stream->stream); is_open_ = 1; rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, cras_shm_num_overruns(shm_)); EXPECT_EQ(&audio_buffer_[0], dev_stream_capture_call.area->channels[0].buf); EXPECT_EQ(rstream_, dev_stream_capture_call.dev_stream->stream); EXPECT_EQ(cb_threshold_, cap_sleep_frames_call.written); EXPECT_EQ(rstream_, cap_sleep_frames_call.dev_stream->stream); is_open_ = 1; rc = unified_io(thread, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(&audio_buffer_[0], dev_stream_capture_call.area->channels[0].buf); EXPECT_EQ(rstream_, dev_stream_capture_call.dev_stream->stream); EXPECT_EQ(cb_threshold_, cap_sleep_frames_call.written); EXPECT_EQ(rstream_, cap_sleep_frames_call.dev_stream->stream); audio_thread_destroy(thread); } // Test the audio playback path. class WriteStreamSuite : public testing::Test { protected: virtual void SetUp() { memset(&fmt_, 0, sizeof(fmt_)); fmt_.frame_rate = 44100; fmt_.num_channels = 2; fmt_.format = SND_PCM_FORMAT_S16_LE; memset(&iodev_, 0, sizeof(iodev_)); iodev_.format = &fmt_; iodev_.buffer_size = 16384; iodev_.direction = CRAS_STREAM_OUTPUT; iodev_.frames_queued = frames_queued; iodev_.delay_frames = delay_frames; iodev_.get_buffer = get_buffer; iodev_.put_buffer = put_buffer; iodev_.dev_running = dev_running; iodev_.is_open = is_open; iodev_.open_dev = open_dev; iodev_.close_dev = close_dev; iodev_.buffer_size = 480; buffer_frames_ = iodev_.buffer_size; cb_threshold_ = 96; SetupRstream(&rstream_, 1); shm_ = cras_rstream_output_shm(rstream_); SetupRstream(&rstream2_, 2); shm2_ = cras_rstream_output_shm(rstream2_); thread_ = audio_thread_create(); ASSERT_TRUE(thread_); thread_set_active_dev(thread_, &iodev_); dev_stream_mix_dont_fill_next = 0; dev_stream_mix_count = 0; select_max_fd = -1; select_write_ptr = NULL; cras_metrics_log_event_called = 0; dev_stream_request_playback_samples_called = 0; cras_rstream_destroy_called = 0; dev_stream_mix_called = 0; is_open_ = 0; close_dev_called_ = 0; dev_running_called_ = 0; audio_buffer_size_ = 8196; thread_add_stream(thread_, rstream_); frames_written_ = 0; } virtual void TearDown() { free(shm_->area); free(rstream_); free(shm2_->area); free(rstream2_); audio_thread_destroy(thread_); } void SetupRstream(struct cras_rstream **rstream, int fd) { struct cras_audio_shm *shm; *rstream = (struct cras_rstream *)calloc(1, sizeof(**rstream)); memcpy(&(*rstream)->format, &fmt_, sizeof(fmt_)); (*rstream)->fd = fd; (*rstream)->buffer_frames = buffer_frames_; (*rstream)->cb_threshold = cb_threshold_; (*rstream)->client = (struct cras_rclient *)this; shm = cras_rstream_output_shm(*rstream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area) + cb_threshold_ * 8); cras_shm_set_frame_bytes(shm, 4); cras_shm_set_used_size( shm, buffer_frames_ * cras_shm_frame_bytes(shm)); } uint64_t GetCaptureSleepFrames() { // Account for padding the sleep interval to ensure the wake up happens // after the last desired frame is received. return cb_threshold_ + CAP_EXTRA_SLEEP_FRAMES; } // Stub functions for the iodev structure. static int frames_queued(const cras_iodev* iodev) { return frames_queued_ + frames_written_; } static int delay_frames(const cras_iodev* iodev) { return delay_frames_; } static int get_buffer(cras_iodev* iodev, struct cras_audio_area** area, unsigned int* num) { size_t sz = sizeof(*area_) + sizeof(struct cras_channel_area) * 2; if (audio_buffer_size_ < *num) *num = audio_buffer_size_; area_ = (cras_audio_area*)calloc(1, sz); area_->frames = *num; area_->num_channels = 2; area_->channels[0].buf = audio_buffer_; channel_area_set_channel(&area_->channels[0], CRAS_CH_FL); area_->channels[0].step_bytes = 4; area_->channels[1].buf = audio_buffer_ + 2; channel_area_set_channel(&area_->channels[1], CRAS_CH_FR); area_->channels[1].step_bytes = 4; *area = area_; return 0; } static int put_buffer(cras_iodev* iodev, unsigned int num) { free(area_); frames_written_ += num; return 0; } static int dev_running(const cras_iodev* iodev) { dev_running_called_++; return dev_running_; } static int is_open(const cras_iodev* iodev) { return is_open_; } static int open_dev(cras_iodev* iodev) { is_open_ = 1; open_dev_called_++; return 0; } static int close_dev(cras_iodev* iodev) { close_dev_called_++; is_open_ = 0; return 0; } struct cras_iodev iodev_; static int is_open_; static int frames_queued_; static int frames_written_; static int delay_frames_; static unsigned int cb_threshold_; static unsigned int buffer_frames_; static uint8_t audio_buffer_[8192]; static unsigned int audio_buffer_size_; static int dev_running_; static unsigned int dev_running_called_; static unsigned int close_dev_called_; static unsigned int open_dev_called_; static struct cras_audio_area *area_; struct cras_audio_format fmt_; struct cras_rstream* rstream_; struct cras_rstream* rstream2_; struct cras_audio_shm* shm_; struct cras_audio_shm* shm2_; struct audio_thread *thread_; }; int WriteStreamSuite::is_open_ = 0; int WriteStreamSuite::frames_queued_ = 0; int WriteStreamSuite::frames_written_ = 0; int WriteStreamSuite::delay_frames_ = 0; unsigned int WriteStreamSuite::cb_threshold_ = 0; unsigned int WriteStreamSuite::buffer_frames_ = 0; uint8_t WriteStreamSuite::audio_buffer_[8192]; unsigned int WriteStreamSuite::audio_buffer_size_ = 0; int WriteStreamSuite::dev_running_ = 1; unsigned int WriteStreamSuite::dev_running_called_ = 0; unsigned int WriteStreamSuite::close_dev_called_ = 0; unsigned int WriteStreamSuite::open_dev_called_ = 0; struct cras_audio_area *WriteStreamSuite::area_; TEST_F(WriteStreamSuite, PossiblyFillGetAvailError) { struct timespec ts; int rc; frames_queued_ = -4; is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(-4, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_EQ(0, ts.tv_nsec); EXPECT_EQ(1, close_dev_called_); } TEST_F(WriteStreamSuite, PossiblyFillEarlyWake) { struct timespec ts; int rc; // If woken and still have tons of data to play, go back to sleep. frames_queued_ = cb_threshold_ * 2; audio_buffer_size_ = buffer_frames_ - frames_queued_; iodev_.direction = CRAS_STREAM_OUTPUT; is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); } TEST_F(WriteStreamSuite, PossiblyFillGetFromStreamFull) { struct timespec ts; int rc; uint64_t nsec_expected; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; nsec_expected = (uint64_t)cb_threshold_ * 1000000000ULL / (uint64_t)fmt_.frame_rate; // shm has plenty of data in it. shm_->area->write_offset[0] = cb_threshold_ * 4; FD_ZERO(&select_out_fds); FD_SET(rstream_->fd, &select_out_fds); select_return_value = 1; is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(cb_threshold_, dev_stream_mix_count); EXPECT_EQ(0, dev_stream_request_playback_samples_called); EXPECT_EQ(-1, select_max_fd); } TEST_F(WriteStreamSuite, PossiblyFillGetFromStreamMinSet) { struct timespec ts; int rc; uint64_t nsec_expected; // Have cb_threshold samples left. frames_queued_ = cb_threshold_ * 2; audio_buffer_size_ = buffer_frames_ - frames_queued_; // Setting the min_buffer_level should shorten the sleep time. iodev_.min_buffer_level = cb_threshold_; // shm has is empty. shm_->area->write_offset[0] = 0; FD_ZERO(&select_out_fds); FD_SET(rstream_->fd, &select_out_fds); select_return_value = 1; is_open_ = 1; // Set write offset after call to select. select_write_ptr = &shm_->area->write_offset[0]; select_write_value = cb_threshold_ * 4; // After the callback there will be cb_thresh of data in the buffer and // cb_thresh x 2 data in the hardware (frames_queued_) = 3 cb_thresh total. // It should sleep until there is a total of cb_threshold + min_buffer_level // left, or 3 - 2 = 1 cb_thresh worth of delay. nsec_expected = (uint64_t)cb_threshold_ * 1000000000ULL / (uint64_t)fmt_.frame_rate; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(cb_threshold_, dev_stream_mix_count); EXPECT_EQ(1, dev_stream_request_playback_samples_called); } TEST_F(WriteStreamSuite, PossiblyFillFramesQueued) { struct timespec ts; int rc; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; // shm has plenty of data in it. shm_->area->write_offset[0] = cras_shm_used_size(shm_); is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(1, dev_running_called_); } TEST_F(WriteStreamSuite, PossiblyFillGetFromStreamOneEmpty) { struct timespec ts; int rc; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; // shm has plenty of data in it. shm_->area->write_offset[0] = cras_shm_used_size(shm_); // Test that nothing breaks if there is an empty stream. dev_stream_mix_dont_fill_next = 1; FD_ZERO(&select_out_fds); FD_SET(rstream_->fd, &select_out_fds); select_return_value = 1; is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, dev_stream_request_playback_samples_called); EXPECT_EQ(-1, select_max_fd); EXPECT_EQ(0, shm_->area->read_offset[0]); EXPECT_EQ(0, shm_->area->read_offset[1]); EXPECT_EQ(cras_shm_used_size(shm_), shm_->area->write_offset[0]); EXPECT_EQ(0, shm_->area->write_offset[1]); } TEST_F(WriteStreamSuite, PossiblyFillGetFromStreamNeedFill) { struct timespec ts; uint64_t nsec_expected; int rc; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; // shm is out of data. shm_->area->write_offset[0] = 0; FD_ZERO(&select_out_fds); FD_SET(rstream_->fd, &select_out_fds); select_return_value = 1; // Set write offset after call to select. select_write_ptr = &shm_->area->write_offset[0]; select_write_value = (buffer_frames_ - cb_threshold_) * 4; nsec_expected = (buffer_frames_ - cb_threshold_) * 1000000000ULL / (uint64_t)fmt_.frame_rate; is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(buffer_frames_ - cb_threshold_, dev_stream_mix_count); EXPECT_EQ(1, dev_stream_request_playback_samples_called); EXPECT_NE(-1, select_max_fd); EXPECT_EQ(0, memcmp(&select_out_fds, &select_in_fds, sizeof(select_in_fds))); EXPECT_EQ(0, shm_->area->read_offset[0]); } TEST_F(WriteStreamSuite, PossiblyFillGetFromTwoStreamsFull) { struct timespec ts; int rc; uint64_t nsec_expected; // Have cb_threshold samples left. frames_queued_ = cras_rstream_get_cb_threshold(rstream_); audio_buffer_size_ = buffer_frames_ - frames_queued_; nsec_expected = (uint64_t)cras_rstream_get_cb_threshold(rstream_) * 1000000000ULL / (uint64_t)fmt_.frame_rate; // shm has plenty of data in it. shm_->area->write_offset[0] = cras_rstream_get_cb_threshold(rstream_) * 4; shm2_->area->write_offset[0] = cras_rstream_get_cb_threshold(rstream2_) * 4; thread_add_stream(thread_, rstream2_); is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(2, dev_stream_mix_called); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(cras_rstream_get_cb_threshold(rstream_), dev_stream_mix_count); EXPECT_EQ(0, dev_stream_request_playback_samples_called); EXPECT_EQ(-1, select_max_fd); } TEST_F(WriteStreamSuite, PossiblyFillGetFromTwoOneEmptySmallerCbThreshold) { struct timespec ts; int rc; uint64_t nsec_expected; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; // First stream is empty and with a smaller cb_threshold. This is to test // the case that when buffer level reaches the cb_threshold of one stream // but not yet the other stream of smaller cb_threshold. rstream_->cb_threshold -= 20; nsec_expected = 20 * 1000000000ULL / (uint64_t)fmt_.frame_rate; shm_->area->write_offset[0] = 0; shm2_->area->write_offset[0] = cras_shm_used_size(shm2_); thread_add_stream(thread_, rstream2_); is_open_ = 1; rc = unified_io(thread_, &ts); // In this case, assert (1) we didn't request the empty stream since buffer // level is larger then its cb_threshold, (2) still mix both streams so // dev_stream_mix_count is zero, and (3) the resulting sleep frames // equals the cb_threshold difference. EXPECT_EQ(0, rc); EXPECT_EQ(2, dev_stream_mix_called); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(0, dev_stream_mix_count); EXPECT_EQ(0, dev_stream_request_playback_samples_called); } TEST_F(WriteStreamSuite, PossiblyFillGetFromTwoOneEmptyAfterFetch) { struct timespec ts; int rc; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; // First stream empty while the second stream full. shm_->area->write_offset[0] = 0; shm2_->area->write_offset[0] = cras_shm_used_size(shm2_); thread_add_stream(thread_, rstream2_); FD_ZERO(&select_out_fds); FD_SET(rstream_->fd, &select_out_fds); select_return_value = 1; is_open_ = 1; rc = unified_io(thread_, &ts); // Assert that the empty stream is skipped, only one stream mixed. EXPECT_EQ(0, rc); EXPECT_EQ(1, dev_stream_mix_called); EXPECT_EQ(buffer_frames_ - cb_threshold_, dev_stream_mix_count); EXPECT_EQ(1, dev_stream_request_playback_samples_called); EXPECT_NE(-1, select_max_fd); EXPECT_EQ(0, memcmp(&select_out_fds, &select_in_fds, sizeof(select_in_fds))); } TEST_F(WriteStreamSuite, PossiblyFillGetFromTwoStreamsFullOneMixes) { struct timespec ts; int rc; size_t written_expected; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; written_expected = buffer_frames_ - cb_threshold_; // shm has plenty of data in it. shm_->area->write_offset[0] = cras_shm_used_size(shm_); shm2_->area->write_offset[0] = cras_shm_used_size(shm2_); thread_add_stream(thread_, rstream2_); // Test that nothing breaks if one stream doesn't fill. dev_stream_mix_dont_fill_next = 1; is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, dev_stream_request_playback_samples_called); EXPECT_EQ(0, shm_->area->read_offset[0]); // No write from first stream. EXPECT_EQ(written_expected * 4, shm2_->area->read_offset[0]); } TEST_F(WriteStreamSuite, PossiblyFillGetFromTwoStreamsOneLimited) { struct timespec ts; int rc; uint64_t nsec_expected; static const unsigned int smaller_frames = 10; // Have cb_threshold samples left. frames_queued_ = cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; nsec_expected = (uint64_t)smaller_frames * (1000000000ULL / (uint64_t)fmt_.frame_rate); // One has too little the other is full. shm_->area->write_offset[0] = smaller_frames * 4; shm_->area->write_buf_idx = 1; shm2_->area->write_offset[0] = cras_shm_used_size(shm2_); shm2_->area->write_buf_idx = 1; thread_add_stream(thread_, rstream2_); FD_ZERO(&select_out_fds); FD_SET(rstream_->fd, &select_out_fds); select_return_value = 1; is_open_ = 1; rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(0, ts.tv_sec); EXPECT_GE(ts.tv_nsec, nsec_expected - 1000); EXPECT_LE(ts.tv_nsec, nsec_expected + 1000); EXPECT_EQ(smaller_frames, dev_stream_mix_count); EXPECT_EQ(1, dev_stream_request_playback_samples_called); EXPECT_NE(-1, select_max_fd); } TEST_F(WriteStreamSuite, DrainOutputBufferCompelete) { frames_queued_ = 3 * cb_threshold_; close_dev_called_ = 0; // All the audio in hw buffer are extra silent frames. iodev_.extra_silent_frames = frames_queued_ + 1; drain_output_buffer(thread_, &iodev_); EXPECT_EQ(1, close_dev_called_); } TEST_F(WriteStreamSuite, DrainOutputBufferWaitForPlayback) { // Hardware buffer is full. frames_queued_ = buffer_frames_; iodev_.extra_silent_frames = 0; close_dev_called_ = 0; drain_output_buffer(thread_, &iodev_); EXPECT_EQ(0, close_dev_called_); } TEST_F(WriteStreamSuite, DrainOutputBufferWaitForAudio) { // Hardware buffer is almost empty frames_queued_ = 30; iodev_.extra_silent_frames = 0; close_dev_called_ = 0; drain_output_buffer(thread_, &iodev_); EXPECT_LT(cb_threshold_ - frames_queued_, frames_written_); EXPECT_EQ(0, close_dev_called_); } TEST_F(WriteStreamSuite, DrainOutputStream) { struct timespec ts; int rc; // Have 3 * cb_threshold samples in the hw buffer. // Have 4 * cb_threshold samples in the first stream's shm // Note: used_size = 5 * cb_threshold. frames_queued_ = 3 * cb_threshold_; audio_buffer_size_ = buffer_frames_ - frames_queued_; shm_->area->write_offset[0] = 4 * cb_threshold_ * 4; is_open_ = 1; close_dev_called_ = 0; open_dev_called_ = 0; thread_disconnect_stream(thread_, rstream_); // We should be draining the audio. EXPECT_EQ(0, close_dev_called_); EXPECT_EQ(0, open_dev_called_); rc = unified_io(thread_, &ts); EXPECT_EQ(0, rc); EXPECT_EQ(2 * cb_threshold_, frames_written_); EXPECT_EQ(0, open_dev_called_); EXPECT_EQ(0, close_dev_called_); // Clear the hardware buffer frames_queued_ = 0; frames_written_ = 0; audio_buffer_size_ = buffer_frames_ - frames_queued_; rc = unified_io(thread_, &ts); // Verified that all data in stream1 is written. // The device is not closed before we have played all the content. EXPECT_EQ(0, rc); EXPECT_EQ(2 * cb_threshold_, frames_written_); EXPECT_EQ(0, close_dev_called_); // Clear the hardware buffer again. frames_queued_ = 0; frames_written_ = 0; audio_buffer_size_ = buffer_frames_ - frames_queued_; rc = unified_io(thread_, &ts); EXPECT_EQ(1, cras_rstream_destroy_called); EXPECT_EQ(1, iodev_.is_draining); EXPECT_EQ(0, rc); EXPECT_EQ(480, thread_->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(96, thread_->cb_threshold[CRAS_STREAM_OUTPUT]); // Clear the hardware buffer again. frames_queued_ = 0; frames_written_ = 0; audio_buffer_size_ = buffer_frames_ - frames_queued_; rc = unified_io(thread_, &ts); EXPECT_EQ(1, close_dev_called_); EXPECT_EQ(0, thread_->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(0, thread_->cb_threshold[CRAS_STREAM_OUTPUT]); } // Test adding and removing streams. class AddStreamSuite : public testing::Test { protected: virtual void SetUp() { memset(&cras_iodev_set_format_val, 0, sizeof(cras_iodev_set_format_val)); cras_iodev_set_format_val.frame_rate = 44100; cras_iodev_set_format_val.num_channels = 2; cras_iodev_set_format_val.format = SND_PCM_FORMAT_S16_LE; memset(&iodev_, 0, sizeof(iodev_)); iodev_.buffer_size = 16384; used_size_ = 480; cb_threshold_ = 96; iodev_.direction = CRAS_STREAM_OUTPUT; iodev_.is_open = is_open; iodev_.open_dev = open_dev; iodev_.close_dev = close_dev; iodev_.get_buffer = get_buffer; iodev_.put_buffer = put_buffer; is_open_ = 0; is_open_called_ = 0; open_dev_called_ = 0; close_dev_called_ = 0; open_dev_return_val_ = 0; cras_iodev_set_format_called = 0; cras_rstream_destroy_called = 0; cras_metrics_log_histogram_called = 0; cras_metrics_log_histogram_name = NULL; cras_metrics_log_histogram_sample = 0; audio_buffer_size_ = 8196; } virtual void TearDown() { } unsigned int GetCaptureSleepFrames() { // Account for padding the sleep interval to ensure the wake up happens // after the last desired frame is received. return cb_threshold_ + 16; } // Stub functions for the iodev structure. static int get_buffer(cras_iodev* iodev, struct cras_audio_area** area, unsigned int* num) { size_t sz = sizeof(*area_) + sizeof(struct cras_channel_area) * 2; if (audio_buffer_size_ < *num) *num = audio_buffer_size_; area_ = (cras_audio_area*)calloc(1, sz); area_->frames = *num; area_->num_channels = 2; area_->channels[0].buf = audio_buffer_; channel_area_set_channel(&area_->channels[0], CRAS_CH_FL); area_->channels[0].step_bytes = 4; area_->channels[1].buf = audio_buffer_ + 2; channel_area_set_channel(&area_->channels[1], CRAS_CH_FR); area_->channels[1].step_bytes = 4; *area = area_; return 0; } static int put_buffer(cras_iodev* iodev, unsigned int num) { free(area_); return 0; } static int is_open(const cras_iodev* iodev) { is_open_called_++; return is_open_; } static int open_dev(cras_iodev* iodev) { open_dev_called_++; is_open_ = true; return open_dev_return_val_; } static int close_dev(cras_iodev* iodev) { close_dev_called_++; is_open_ = false; return 0; } void add_rm_two_streams(CRAS_STREAM_DIRECTION direction) { int rc; struct cras_rstream *new_stream, *second_stream; cras_audio_shm *shm; struct cras_audio_format *fmt; struct audio_thread *thread; thread = audio_thread_create(); fmt = (struct cras_audio_format *)malloc(sizeof(*fmt)); memcpy(fmt, &cras_iodev_set_format_val, sizeof(*fmt)); iodev_.direction = direction; new_stream = (struct cras_rstream *)calloc(1, sizeof(*new_stream)); new_stream->fd = 55; new_stream->buffer_frames = 65; new_stream->cb_threshold = 80; new_stream->direction = direction; memcpy(&new_stream->format, fmt, sizeof(*fmt)); shm = cras_rstream_output_shm(new_stream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area)); if (direction == CRAS_STREAM_INPUT) thread_set_active_dev(thread, &iodev_); else thread_set_active_dev(thread, &iodev_); thread_add_stream(thread, new_stream); EXPECT_EQ(1, thread->devs_open[direction]); EXPECT_EQ(1, open_dev_called_); EXPECT_EQ(65, thread->buffer_frames[direction]); if (direction == CRAS_STREAM_OUTPUT) EXPECT_EQ(32, thread->cb_threshold[direction]); else EXPECT_EQ(80, thread->cb_threshold[direction]); is_open_ = 1; second_stream = (struct cras_rstream *)calloc(1, sizeof(*second_stream)); second_stream->fd = 56; second_stream->buffer_frames = 25; second_stream->cb_threshold = 12; second_stream->direction = direction; memcpy(&second_stream->format, fmt, sizeof(*fmt)); shm = cras_rstream_output_shm(second_stream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area)); is_open_called_ = 0; thread_add_stream(thread, second_stream); EXPECT_EQ(1, thread->devs_open[direction]); EXPECT_EQ(1, open_dev_called_); EXPECT_EQ(25, thread->buffer_frames[direction]); EXPECT_EQ(12, thread->cb_threshold[direction]); // Remove the streams. rc = thread_remove_stream(thread, second_stream); EXPECT_EQ(1, rc); EXPECT_EQ(0, close_dev_called_); if (direction == CRAS_STREAM_OUTPUT) EXPECT_EQ(32, thread->cb_threshold[direction]); else EXPECT_EQ(80, thread->cb_threshold[direction]); rc = thread_remove_stream(thread, new_stream); EXPECT_EQ(0, rc); // For output stream, we enter the draining mode; // for input stream, we close the device directly. if (direction == CRAS_STREAM_INPUT) { EXPECT_EQ(0, thread->devs_open[direction]); EXPECT_EQ(0, thread->buffer_frames[direction]); EXPECT_EQ(0, thread->cb_threshold[direction]); } else { EXPECT_EQ(1, iodev_.is_draining); } free(fmt); shm = cras_rstream_output_shm(new_stream); audio_thread_destroy(thread); free(shm->area); free(new_stream); shm = cras_rstream_output_shm(second_stream); free(shm->area); free(second_stream); } struct cras_iodev iodev_; static int is_open_; static int is_open_called_; static int open_dev_called_; static int open_dev_return_val_; static int close_dev_called_; static int used_size_; static int cb_threshold_; struct cras_audio_format fmt_; static struct cras_audio_area *area_; static uint8_t audio_buffer_[8192]; static unsigned int audio_buffer_size_; }; int AddStreamSuite::is_open_ = 0; int AddStreamSuite::is_open_called_ = 0; int AddStreamSuite::open_dev_called_ = 0; int AddStreamSuite::open_dev_return_val_ = 0; int AddStreamSuite::close_dev_called_ = 0; int AddStreamSuite::used_size_ = 0; int AddStreamSuite::cb_threshold_ = 0; struct cras_audio_area *AddStreamSuite::area_; uint8_t AddStreamSuite::audio_buffer_[8192]; unsigned int AddStreamSuite::audio_buffer_size_ = 0; TEST_F(AddStreamSuite, SimpleAddOutputStream) { int rc; cras_rstream* new_stream; cras_audio_shm *shm; struct audio_thread *thread; thread = audio_thread_create(); new_stream = (struct cras_rstream *)calloc(1, sizeof(*new_stream)); new_stream->client = (struct cras_rclient* )this; new_stream->fd = 55; new_stream->buffer_frames = 65; new_stream->cb_threshold = 80; memcpy(&new_stream->format, &cras_iodev_set_format_val, sizeof(cras_iodev_set_format_val)); shm = cras_rstream_output_shm(new_stream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area)); thread_set_active_dev(thread, &iodev_); rc = thread_add_stream(thread, new_stream); ASSERT_EQ(0, rc); EXPECT_EQ(1, thread->devs_open[CRAS_STREAM_OUTPUT]); EXPECT_EQ(1, open_dev_called_); EXPECT_EQ(65, thread->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(32, thread->cb_threshold[CRAS_STREAM_OUTPUT]); is_open_ = 1; // remove the stream. rc = thread_remove_stream(thread, new_stream); EXPECT_EQ(0, rc); EXPECT_EQ(1, iodev_.is_draining); EXPECT_EQ(0, cras_metrics_log_histogram_called); EXPECT_EQ(0, cras_rstream_destroy_called); rc = thread_disconnect_stream(thread, new_stream); EXPECT_EQ(1, cras_rstream_destroy_called); free(shm->area); audio_thread_destroy(thread); free(new_stream); } TEST_F(AddStreamSuite, AddStreamOpenFail) { struct audio_thread *thread; cras_rstream new_stream; cras_audio_shm *shm; thread = audio_thread_create(); ASSERT_TRUE(thread); thread_set_active_dev(thread, &iodev_); printf("1\n"); shm = cras_rstream_output_shm(&new_stream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area)); open_dev_return_val_ = -1; new_stream.direction = CRAS_STREAM_OUTPUT; EXPECT_EQ(AUDIO_THREAD_OUTPUT_DEV_ERROR, thread_add_stream(thread, &new_stream)); printf("2\n"); EXPECT_EQ(1, open_dev_called_); EXPECT_EQ(1, cras_iodev_set_format_called); audio_thread_destroy(thread); printf("3\n"); free(shm->area); } TEST_F(AddStreamSuite, AddRmTwoOutputStreams) { add_rm_two_streams(CRAS_STREAM_OUTPUT); } TEST_F(AddStreamSuite, AddRmTwoInputStreams) { add_rm_two_streams(CRAS_STREAM_INPUT); } TEST_F(AddStreamSuite, RmStreamLogLongestTimeout) { int rc; cras_rstream* new_stream; cras_audio_shm *shm; struct audio_thread *thread; thread = audio_thread_create(); new_stream = (struct cras_rstream *)calloc(1, sizeof(*new_stream)); new_stream->fd = 55; new_stream->buffer_frames = 65; new_stream->cb_threshold = 80; memcpy(&new_stream->format, &cras_iodev_set_format_val, sizeof(cras_iodev_set_format_val)); shm = cras_rstream_output_shm(new_stream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area)); thread_set_active_dev(thread, &iodev_); rc = thread_add_stream(thread, new_stream); ASSERT_EQ(0, rc); EXPECT_EQ(1, thread->devs_open[CRAS_STREAM_OUTPUT]); EXPECT_EQ(1, open_dev_called_); EXPECT_EQ(65, thread->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(32, thread->cb_threshold[CRAS_STREAM_OUTPUT]); is_open_ = 1; cras_shm_set_longest_timeout(shm, 90); // remove the stream. rc = thread_remove_stream(thread, new_stream); EXPECT_EQ(0, rc); EXPECT_EQ(1, iodev_.is_draining); cras_system_add_select_fd_callback(cras_system_add_select_fd_callback_data); EXPECT_EQ(1, cras_metrics_log_histogram_called); EXPECT_STREQ(kStreamTimeoutMilliSeconds, cras_metrics_log_histogram_name); EXPECT_EQ(90, cras_metrics_log_histogram_sample); free(shm->area); free(new_stream); audio_thread_destroy(thread); } class ActiveDevicesSuite : public testing::Test { protected: virtual void SetUp() { memset(&cras_iodev_set_format_val, 0, sizeof(cras_iodev_set_format_val)); cras_iodev_set_format_val.frame_rate = 44100; cras_iodev_set_format_val.num_channels = 2; cras_iodev_set_format_val.format = SND_PCM_FORMAT_S16_LE; memset(&iodev_, 0, sizeof(iodev_)); memset(&iodev2_, 0, sizeof(iodev2_)); iodev_.close_dev = close_dev; iodev_.is_open = is_open; iodev_.open_dev = open_dev; iodev_.delay_frames = delay_frames; iodev_.get_buffer = get_buffer; iodev_.put_buffer = put_buffer; iodev_.frames_queued = frames_queued; iodev_.delay_frames = delay_frames; iodev_.dev_running = dev_running; iodev_.buffer_size = 2048; iodev2_.close_dev = close_dev; iodev2_.is_open = is_open; iodev2_.open_dev = open_dev; iodev2_.delay_frames = delay_frames; iodev2_.get_buffer = get_buffer; iodev2_.put_buffer = put_buffer; iodev2_.frames_queued = frames_queued; iodev2_.delay_frames = delay_frames; iodev2_.dev_running = dev_running; iodev2_.buffer_size = 2048; thread_ = audio_thread_create(); ASSERT_TRUE(thread_); buffer_frames_ = 500; cb_threshold_ = 250; SetupRstream(&rstream_); SetupRstream(&rstream2_); rstream2_->buffer_frames -= 50; rstream2_->cb_threshold -= 50; dummy_audio_area1 = (cras_audio_area*)calloc(1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); dummy_audio_area1->num_channels = 2; channel_area_set_channel(&dummy_audio_area1->channels[0], CRAS_CH_FL); channel_area_set_channel(&dummy_audio_area1->channels[1], CRAS_CH_FR); rstream_->input_audio_area = dummy_audio_area1; dummy_audio_area2 = (cras_audio_area*)calloc(1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); dummy_audio_area2->num_channels = 2; channel_area_set_channel(&dummy_audio_area2->channels[0], CRAS_CH_FL); channel_area_set_channel(&dummy_audio_area2->channels[1], CRAS_CH_FR); rstream2_->input_audio_area = dummy_audio_area2; cras_iodev_set_format_called = 0; close_dev_called_ = 0; is_open_ = 0; cras_fmt_conversion_needed_return_val = 0; open_dev_val_idx_ = 0; delay_frames_val_idx_ = 0; frames_queued_val_idx_ = 0; frames_queued_[0] = 250; frames_queued_[1] = 250; get_buffer_val_idx_ = 0; put_buffer_val_idx_ = 0; for (int i = 0; i < 8; i++) { open_dev_val_[i] = 0; delay_frames_[i] = 0; audio_buffer_size_[i] = 250; get_buffer_rc_[i] = 0; put_buffer_rc_[i] = 0; } } virtual void TearDown() { struct cras_audio_shm *shm; audio_thread_destroy(thread_); shm = cras_rstream_output_shm(rstream_); free(shm->area); free(rstream_); free(dummy_audio_area1); free(dummy_audio_area2); } void SetupRstream(struct cras_rstream **rstream) { struct cras_audio_shm *shm; *rstream = (struct cras_rstream *)calloc(1, sizeof(**rstream)); memcpy(&(*rstream)->format, &cras_iodev_set_format_val, sizeof(cras_iodev_set_format_val)); (*rstream)->direction = CRAS_STREAM_OUTPUT; (*rstream)->buffer_frames = buffer_frames_; (*rstream)->cb_threshold = cb_threshold_; shm = cras_rstream_output_shm(*rstream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area) + cb_threshold_ * 8); cras_shm_set_frame_bytes(shm, 4); cras_shm_set_used_size( shm, buffer_frames_ * cras_shm_frame_bytes(shm)); shm = cras_rstream_input_shm(*rstream); shm->area = (struct cras_audio_shm_area *)calloc(1, sizeof(*shm->area) + buffer_frames_ * 8); cras_shm_set_frame_bytes(shm, 4); cras_shm_set_used_size( shm, cb_threshold_* cras_shm_frame_bytes(shm)); } static int close_dev(struct cras_iodev *iodev) { close_dev_called_++; return 0; } static int is_open(const cras_iodev *iodev) { return is_open_; } static int open_dev(struct cras_iodev *iodev) { open_dev_val_idx_ %= 8; is_open_ = 1; return open_dev_val_[open_dev_val_idx_++]; } static int delay_frames(const cras_iodev* iodev) { delay_frames_val_idx_ %= 8; return delay_frames_[delay_frames_val_idx_++]; } static int dev_running(const cras_iodev* iodev) { return 1; } static int frames_queued(const cras_iodev* iodev) { frames_queued_val_idx_ %= 2; return frames_queued_[frames_queued_val_idx_++]; } static int get_buffer(cras_iodev* iodev, struct cras_audio_area** area, unsigned int* num) { size_t sz = sizeof(*area_) + sizeof(struct cras_channel_area) * 2; get_buffer_val_idx_ %= 8; if (audio_buffer_size_[get_buffer_val_idx_] < *num) *num = audio_buffer_size_[get_buffer_val_idx_]; area_ = (cras_audio_area*)calloc(1, sz); area_->frames = *num; area_->num_channels = 2; area_->channels[0].buf = audio_buffer_[get_buffer_val_idx_]; channel_area_set_channel(&area_->channels[0], CRAS_CH_FL); area_->channels[0].step_bytes = 4; area_->channels[1].buf = audio_buffer_[get_buffer_val_idx_] + 2; channel_area_set_channel(&area_->channels[1], CRAS_CH_FR); area_->channels[1].step_bytes = 4; *area = area_; get_buffer_val_idx_++; return 0; } static int put_buffer(cras_iodev* iodev, unsigned int num) { free(area_); put_buffer_val_idx_ %= 8; return put_buffer_rc_[put_buffer_val_idx_++]; } static int is_open_; static int open_dev_val_[8]; static int open_dev_val_idx_; static int close_dev_called_; static int buffer_frames_; static int cb_threshold_; static int frames_queued_[2]; static int frames_queued_val_idx_; static uint8_t audio_buffer_[8][8192]; static unsigned int audio_buffer_size_[8]; static int get_buffer_rc_[8]; static int put_buffer_rc_[8]; static int get_buffer_val_idx_; static int put_buffer_val_idx_; struct cras_iodev iodev_; struct cras_iodev iodev2_; struct cras_rstream *rstream_; struct cras_rstream *rstream2_; struct audio_thread *thread_; static struct cras_audio_area *area_; static int delay_frames_val_idx_; static int delay_frames_[8]; }; int ActiveDevicesSuite::is_open_ = 0; int ActiveDevicesSuite::buffer_frames_ = 0; int ActiveDevicesSuite::cb_threshold_ = 0; int ActiveDevicesSuite::frames_queued_[2]; int ActiveDevicesSuite::frames_queued_val_idx_; int ActiveDevicesSuite::close_dev_called_ = 0; int ActiveDevicesSuite::open_dev_val_[8]; int ActiveDevicesSuite::open_dev_val_idx_ = 0; int ActiveDevicesSuite::delay_frames_val_idx_ = 0; int ActiveDevicesSuite::delay_frames_[8]; uint8_t ActiveDevicesSuite::audio_buffer_[8][8192]; unsigned int ActiveDevicesSuite::audio_buffer_size_[8]; int ActiveDevicesSuite::get_buffer_val_idx_ = 0; int ActiveDevicesSuite::put_buffer_val_idx_ = 0; int ActiveDevicesSuite::get_buffer_rc_[8]; int ActiveDevicesSuite::put_buffer_rc_[8]; struct cras_audio_area *ActiveDevicesSuite::area_; TEST_F(ActiveDevicesSuite, SetActiveDevRemoveOld) { struct active_dev *adevs; struct cras_iodev iodev3_; iodev_.direction = CRAS_STREAM_INPUT; iodev2_.direction = CRAS_STREAM_INPUT; iodev3_.direction = CRAS_STREAM_INPUT; thread_set_active_dev(thread_, &iodev_); adevs = thread_->active_devs[CRAS_STREAM_INPUT]; EXPECT_NE((void *)NULL, adevs); EXPECT_EQ(adevs->dev, &iodev_); EXPECT_EQ(1, iodev_.is_active); /* Assert the first active dev is still iodev. */ thread_add_active_dev(thread_, &iodev2_); adevs = thread_->active_devs[CRAS_STREAM_INPUT]; EXPECT_EQ(adevs->dev, &iodev_); thread_set_active_dev(thread_, &iodev3_); adevs = thread_->active_devs[CRAS_STREAM_INPUT]; EXPECT_EQ(adevs->dev, &iodev3_); EXPECT_EQ(iodev3_.is_active, 1); EXPECT_EQ(iodev_.is_active, 0); } TEST_F(ActiveDevicesSuite, SetActiveDevAlreadyInList) { struct active_dev *adevs; iodev_.direction = CRAS_STREAM_INPUT; iodev2_.direction = CRAS_STREAM_INPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); adevs = thread_->active_devs[CRAS_STREAM_INPUT]; EXPECT_EQ(adevs->dev, &iodev_); EXPECT_EQ(iodev_.is_active, 1); thread_set_active_dev(thread_, &iodev2_); adevs = thread_->active_devs[CRAS_STREAM_INPUT]; EXPECT_EQ(adevs->dev, &iodev2_); EXPECT_EQ(iodev2_.is_active, 1); EXPECT_EQ(iodev_.is_active, 0); } TEST_F(ActiveDevicesSuite, AddRemoveActiveDevice) { struct active_dev *adevs; iodev_.direction = CRAS_STREAM_INPUT; iodev2_.direction = CRAS_STREAM_INPUT; thread_set_active_dev(thread_, &iodev_); adevs = thread_->active_devs[CRAS_STREAM_INPUT]; EXPECT_NE((void *)NULL, adevs); EXPECT_EQ(adevs->dev, &iodev_); EXPECT_EQ(1, iodev_.is_active); thread_add_active_dev(thread_, &iodev2_); EXPECT_NE((void *)NULL, adevs->next); EXPECT_EQ(adevs->next->dev, &iodev2_); EXPECT_EQ(1, iodev2_.is_active); thread_rm_active_dev(thread_, &iodev_); adevs = thread_->active_devs[CRAS_STREAM_INPUT]; EXPECT_EQ((void *)NULL, adevs->next); EXPECT_EQ(adevs->dev, &iodev2_); EXPECT_EQ(0, iodev_.is_active); iodev_.direction = CRAS_STREAM_POST_MIX_PRE_DSP; thread_add_active_dev(thread_, &iodev_); EXPECT_NE((void *)NULL, thread_->active_devs[CRAS_STREAM_POST_MIX_PRE_DSP]); EXPECT_EQ(1, iodev_.is_active); } TEST_F(ActiveDevicesSuite, ClearActiveDevices) { iodev_.direction = CRAS_STREAM_OUTPUT; iodev2_.direction = CRAS_STREAM_OUTPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); EXPECT_NE((void *)NULL, thread_->active_devs[CRAS_STREAM_OUTPUT]); EXPECT_EQ(1, iodev_.is_active); EXPECT_EQ(1, iodev2_.is_active); thread_clear_active_devs(thread_, CRAS_STREAM_OUTPUT); EXPECT_EQ((void *)NULL, thread_->active_devs[CRAS_STREAM_OUTPUT]); EXPECT_EQ(0, iodev_.is_active); EXPECT_EQ(0, iodev2_.is_active); } TEST_F(ActiveDevicesSuite, OpenActiveDevices) { iodev_.direction = CRAS_STREAM_OUTPUT; iodev2_.direction = CRAS_STREAM_OUTPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); thread_add_stream(thread_, rstream_); EXPECT_EQ(2, cras_iodev_set_format_called); EXPECT_EQ(500, thread_->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(250, thread_->cb_threshold[CRAS_STREAM_OUTPUT]); } TEST_F(ActiveDevicesSuite, OpenFirstActiveDeviceFail) { int rc; iodev_.direction = CRAS_STREAM_OUTPUT; iodev2_.direction = CRAS_STREAM_OUTPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); open_dev_val_[0] = -1; rc = thread_add_stream(thread_, rstream_); EXPECT_EQ(rc, AUDIO_THREAD_OUTPUT_DEV_ERROR); EXPECT_EQ(2, cras_iodev_set_format_called); EXPECT_EQ(0, thread_->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(0, thread_->cb_threshold[CRAS_STREAM_OUTPUT]); } TEST_F(ActiveDevicesSuite, OpenSecondActiveDeviceFail) { int rc; iodev_.direction = CRAS_STREAM_OUTPUT; iodev2_.direction = CRAS_STREAM_OUTPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); open_dev_val_[1] = -1; rc = thread_add_stream(thread_, rstream_); EXPECT_EQ(0, rc); EXPECT_EQ(2, cras_iodev_set_format_called); EXPECT_EQ(500, thread_->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(250, thread_->cb_threshold[CRAS_STREAM_OUTPUT]); EXPECT_EQ(0, close_dev_called_); EXPECT_EQ((void *)NULL, thread_->active_devs[CRAS_STREAM_OUTPUT]->next); } TEST_F(ActiveDevicesSuite, OpenSecondActiveDeviceFormatIncompatible) { int rc; iodev_.direction = CRAS_STREAM_OUTPUT; iodev2_.direction = CRAS_STREAM_OUTPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); cras_fmt_conversion_needed_return_val = 1; rc = thread_add_stream(thread_, rstream_); EXPECT_EQ(0, rc); EXPECT_EQ(2, cras_iodev_set_format_called); EXPECT_EQ(500, thread_->buffer_frames[CRAS_STREAM_OUTPUT]); EXPECT_EQ(250, thread_->cb_threshold[CRAS_STREAM_OUTPUT]); EXPECT_EQ(1, close_dev_called_); EXPECT_EQ((void *)NULL, thread_->active_devs[CRAS_STREAM_OUTPUT]->next); } TEST_F(ActiveDevicesSuite, CloseActiveDevices) { iodev_.direction = CRAS_STREAM_OUTPUT; iodev2_.direction = CRAS_STREAM_OUTPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); thread_add_stream(thread_, rstream_); EXPECT_EQ(1, thread_->devs_open[CRAS_STREAM_OUTPUT]); EXPECT_EQ(2, cras_iodev_set_format_called); thread_add_stream(thread_, rstream2_); EXPECT_EQ(4, cras_iodev_set_format_called); thread_remove_stream(thread_, rstream2_); thread_remove_stream(thread_, rstream_); EXPECT_EQ(1, iodev_.is_draining); EXPECT_EQ(1, iodev2_.is_draining); } TEST_F(ActiveDevicesSuite, InputDelayFrames) { int fr; iodev_.direction = CRAS_STREAM_INPUT; iodev2_.direction = CRAS_STREAM_INPUT; rstream_->direction = CRAS_STREAM_INPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); thread_add_stream(thread_, rstream_); delay_frames_[0] = 3; delay_frames_[1] = 33; fr = input_delay_frames(thread_->active_devs[CRAS_STREAM_INPUT]); EXPECT_EQ(33, fr); delay_frames_val_idx_ = 0; delay_frames_[1] = -1; fr = input_delay_frames(thread_->active_devs[CRAS_STREAM_INPUT]); EXPECT_EQ(-1, fr); } TEST_F(ActiveDevicesSuite, InputFramesQueued) { int fr; iodev_.direction = CRAS_STREAM_INPUT; iodev2_.direction = CRAS_STREAM_INPUT; thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); frames_queued_val_idx_ = 0; frames_queued_[0] = 195; frames_queued_[1] = 190; fr = input_min_frames_queued(thread_->active_devs[CRAS_STREAM_INPUT]); EXPECT_EQ(190, fr); /* Test error path. */ frames_queued_val_idx_ = 0; frames_queued_[0] = -1; frames_queued_[1] = 190; fr = input_min_frames_queued(thread_->active_devs[CRAS_STREAM_INPUT]); EXPECT_EQ(-1, fr); } TEST_F(ActiveDevicesSuite, MixMultipleInputs) { struct timespec ts; iodev_.direction = CRAS_STREAM_INPUT; iodev2_.direction = CRAS_STREAM_INPUT; rstream_->direction = CRAS_STREAM_INPUT; rstream2_->direction = CRAS_STREAM_INPUT; for (unsigned int dev = 0; dev < 8; dev++) { int16_t *buff = (int16_t*)audio_buffer_[dev]; for (unsigned int i = 0; i < 250; i++) buff[i] = i; } thread_set_active_dev(thread_, &iodev_); thread_add_active_dev(thread_, &iodev2_); /* Assert shm from rstream_ is used. */ thread_add_stream(thread_, rstream_); unified_io(thread_, &ts); EXPECT_EQ(rstream_, cap_sleep_frames_call.dev_stream->stream); thread_add_stream(thread_, rstream2_); unified_io(thread_, &ts); EXPECT_EQ(rstream2_, cap_sleep_frames_call.dev_stream->stream); } extern "C" { const char kNoCodecsFoundMetric[] = "Cras.NoCodecsFoundAtBoot"; const char kStreamTimeoutMilliSeconds[] = "Cras.StreamTimeoutMilliSeconds"; int cras_iodev_get_thread_poll_fd(const struct cras_iodev *iodev) { return 0; } int cras_iodev_read_thread_command(struct cras_iodev *iodev, uint8_t *buf, size_t max_len) { return 0; } int cras_iodev_send_command_response(struct cras_iodev *iodev, int rc) { return 0; } void cras_iodev_fill_time_from_frames(size_t frames, size_t frame_rate, struct timespec *ts) { uint64_t to_play_usec; ts->tv_sec = 0; /* adjust sleep time to target our callback threshold */ to_play_usec = (uint64_t)frames * 1000000L / (uint64_t)frame_rate; while (to_play_usec > 1000000) { ts->tv_sec++; to_play_usec -= 1000000; } ts->tv_nsec = to_play_usec * 1000; } void dev_stream_set_delay(const struct dev_stream *dev_stream, unsigned int delay_frames) { dev_stream_set_delay_called++; } void cras_set_capture_timestamp(size_t frame_rate, size_t frames, struct cras_timespec *ts) { } int cras_iodev_set_format(struct cras_iodev *iodev, struct cras_audio_format *fmt) { cras_iodev_set_format_called++; iodev->format = &cras_iodev_set_format_val; return 0; } // From mixer. unsigned int dev_stream_mix(struct dev_stream *dev_stream, size_t num_channels, uint8_t *dst, size_t *count, size_t *index) { int16_t *src; int16_t *target = (int16_t *)dst; size_t fr_written, fr_in_buf; size_t num_samples; size_t frames = 0; struct cras_audio_shm *shm; if (dev_stream->stream->direction == CRAS_STREAM_OUTPUT) { shm = &dev_stream->stream->output_shm; } else { shm = &dev_stream->stream->input_shm; } dev_stream_mix_called++; if (dev_stream_mix_dont_fill_next) { dev_stream_mix_dont_fill_next = 0; return 0; } dev_stream_mix_count = *count; /* We only copy the data from shm to dst, not actually mix them. */ fr_in_buf = cras_shm_get_frames(shm); if (fr_in_buf == 0) return 0; if (fr_in_buf < *count) *count = fr_in_buf; fr_written = 0; while (fr_written < *count) { src = cras_shm_get_readable_frames(shm, fr_written, &frames); if (frames > *count - fr_written) frames = *count - fr_written; num_samples = frames * num_channels; memcpy(target, src, num_samples * 2); fr_written += frames; target += num_samples; } *index = *index + 1; cras_shm_buffer_read(shm, fr_written); return *count; } void cras_scale_buffer(int16_t *buffer, unsigned int count, float scaler) { } size_t cras_mix_mute_buffer(uint8_t *dst, size_t frame_bytes, size_t count) { cras_mix_mute_count = count; return count; } void cras_mix_add_clip(int16_t *dst, const int16_t *src, size_t count) { int32_t sum; unsigned int i; for (i = 0; i < count; i++) { sum = dst[i] + src[i]; if (sum > 0x7fff) sum = 0x7fff; else if (sum < -0x8000) sum = -0x8000; dst[i] = sum; } } // From cras_metrics.c void cras_metrics_log_event(const char *event) { cras_metrics_log_event_called++; } void cras_metrics_log_histogram(const char *name, int sample, int min, int max, int nbuckets) { cras_metrics_log_histogram_called++; cras_metrics_log_histogram_name = name; cras_metrics_log_histogram_sample = sample; } // From util. int cras_set_rt_scheduling(int rt_lim) { return 0; } int cras_set_thread_priority(int priority) { return 0; } // From rstream. int cras_rstream_get_audio_request_reply(const struct cras_rstream *stream) { return 0; } void cras_rstream_log_overrun(const struct cras_rstream *stream) { } int cras_system_add_select_fd(int fd, void (*callback)(void *data), void *callback_data) { cras_system_add_select_fd_callback = callback; cras_system_add_select_fd_callback_data = callback_data; return 0; } void cras_system_rm_select_fd(int fd) { } size_t cras_system_get_volume() { return cras_system_get_volume_return; } int cras_system_get_mute() { return 0; } int cras_system_get_capture_mute() { return 0; } void cras_rstream_destroy(struct cras_rstream *stream) { cras_rstream_destroy_called++; } void loopback_iodev_set_format(struct cras_iodev *loopback_dev, const struct cras_audio_format *fmt) { } int loopback_iodev_add_audio(struct cras_iodev *loopback_dev, const uint8_t *audio, unsigned int count) { return 0; } int loopback_iodev_add_zeros(struct cras_iodev *dev, unsigned int count) { return 0; } int cras_fmt_conversion_needed(const struct cras_audio_format *a, const struct cras_audio_format *b) { return cras_fmt_conversion_needed_return_val; } void cras_audio_area_config_buf_pointers(struct cras_audio_area *area, const struct cras_audio_format *fmt, uint8_t *base_buffer) { unsigned int i; const int sample_size = snd_pcm_format_physical_width(fmt->format) / 8; /* TODO(dgreid) - assuming interleaved audio here for now. */ for (i = 0 ; i < area->num_channels; i++) { area->channels[i].step_bytes = cras_get_format_bytes(fmt); area->channels[i].buf = base_buffer + i * sample_size; } } void cras_audio_area_copy(const struct cras_audio_area *dst, unsigned int dst_offset, unsigned int dst_format_bytes, const struct cras_audio_area *src, unsigned int src_index) { unsigned count, i; int16_t *dchan, *schan; if (src_index == 0) memset(dst->channels[0].buf, 0, src->frames * dst_format_bytes); dchan = (int16_t *)(dst->channels[0].buf + dst_offset * dst->channels[0].step_bytes); schan = (int16_t *)src->channels[0].buf; count = src->frames * src->num_channels; for (i = 0; i < count; i++) { int32_t sum; sum = *dchan + *schan; if (sum > 0x7fff) sum = 0x7fff; else if (sum < -0x8000) sum = -0x8000; *dchan = sum; dchan++; schan++; } } // Override select so it can be stubbed. int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { select_max_fd = nfds; select_timeval.tv_sec = timeout->tv_sec; select_timeval.tv_usec = timeout->tv_usec; select_in_fds = *readfds; *readfds = select_out_fds; if (select_write_ptr) *select_write_ptr = select_write_value; return select_return_value; } int clock_gettime(clockid_t clk_id, struct timespec *tp) { *tp = time_now; return 0; } struct dev_stream *dev_stream_create(struct cras_rstream *stream, const struct cras_audio_format *fmt) { struct dev_stream *out = static_cast<dev_stream*>(calloc(1, sizeof(*out))); out->stream = stream; return out; } void dev_stream_destroy(struct dev_stream *dev_stream) { free(dev_stream); } void dev_stream_capture(struct dev_stream *dev_stream, const struct cras_audio_area *area, unsigned int dev_index) { dev_stream_capture_call.dev_stream = dev_stream; dev_stream_capture_call.area = area; dev_stream_capture_call.dev_index = dev_index; dev_stream_capture_call.num_called++; } int dev_stream_playback_frames(const struct dev_stream *dev_stream) { struct cras_audio_shm *shm; int frames; shm = cras_rstream_output_shm(dev_stream->stream); frames = cras_shm_get_frames(shm); if (frames < 0) return frames; if (!dev_stream->conv) return frames; return cras_fmt_conv_in_frames_to_out(dev_stream->conv, frames); } unsigned int dev_stream_capture_avail(const struct dev_stream *dev_stream) { struct cras_audio_shm *shm; struct cras_rstream *rstream = dev_stream->stream; unsigned int cb_threshold = cras_rstream_get_cb_threshold(rstream); unsigned int frames_avail; shm = cras_rstream_input_shm(rstream); cras_shm_get_writeable_frames(shm, cb_threshold, &frames_avail); return frames_avail; } int dev_stream_capture_sleep_frames(struct dev_stream *dev_stream, unsigned int written) { cap_sleep_frames_call.dev_stream = dev_stream; cap_sleep_frames_call.written = written; cap_sleep_frames_call.num_called++; return 0; } int dev_stream_request_playback_samples(struct dev_stream *dev_stream) { struct cras_rstream *rstream = dev_stream->stream; dev_stream_request_playback_samples_called++; cras_shm_set_callback_pending(cras_rstream_output_shm(rstream), 1); return 0; } size_t cras_fmt_conv_in_frames_to_out(struct cras_fmt_conv *conv, size_t in_frames) { return in_frames; } size_t cras_fmt_conv_out_frames_to_in(struct cras_fmt_conv *conv, size_t out_frames) { return out_frames; } } // extern "C" int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }