| // 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 <gtest/gtest.h> |
| #include <stdio.h> |
| #include <sys/select.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* mock_audio_area1; |
| static struct cras_audio_area* mock_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_); |
| |
| mock_audio_area1 = (cras_audio_area*)calloc( |
| 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); |
| mock_audio_area1->num_channels = 2; |
| channel_area_set_channel(&mock_audio_area1->channels[0], CRAS_CH_FL); |
| channel_area_set_channel(&mock_audio_area1->channels[1], CRAS_CH_FR); |
| rstream_->input_audio_area = mock_audio_area1; |
| mock_audio_area2 = (cras_audio_area*)calloc( |
| 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); |
| mock_audio_area2->num_channels = 2; |
| channel_area_set_channel(&mock_audio_area2->channels[0], CRAS_CH_FL); |
| channel_area_set_channel(&mock_audio_area2->channels[1], CRAS_CH_FR); |
| rstream2_->input_audio_area = mock_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(mock_audio_area1); |
| free(mock_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->header = (struct cras_audio_shm_header*)calloc( |
| 1, sizeof(*shm->header) + 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->header = (struct cras_audio_shm_header*)calloc( |
| 1, sizeof(*shm->header) + 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->header = |
| (struct cras_audio_shm_header*)calloc(1, sizeof(*shm->header)); |
| |
| 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->header = |
| (struct cras_audio_shm_header*)calloc(1, sizeof(*shm->header)); |
| |
| 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->header); |
| free(new_stream); |
| shm = cras_rstream_output_shm(second_stream); |
| free(shm->header); |
| 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->header = (struct cras_audio_shm_header*)calloc(1, sizeof(*shm->header)); |
| |
| 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->header); |
| 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->header = (struct cras_audio_shm_header*)calloc(1, sizeof(*shm->header)); |
| |
| 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->header); |
| } |
| |
| 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->header = (struct cras_audio_shm_header*)calloc(1, sizeof(*shm->header)); |
| |
| 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->header); |
| 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; |
| |
| mock_audio_area1 = (cras_audio_area*)calloc( |
| 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); |
| mock_audio_area1->num_channels = 2; |
| channel_area_set_channel(&mock_audio_area1->channels[0], CRAS_CH_FL); |
| channel_area_set_channel(&mock_audio_area1->channels[1], CRAS_CH_FR); |
| rstream_->input_audio_area = mock_audio_area1; |
| mock_audio_area2 = (cras_audio_area*)calloc( |
| 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); |
| mock_audio_area2->num_channels = 2; |
| channel_area_set_channel(&mock_audio_area2->channels[0], CRAS_CH_FL); |
| channel_area_set_channel(&mock_audio_area2->channels[1], CRAS_CH_FR); |
| rstream2_->input_audio_area = mock_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->header); |
| free(rstream_); |
| free(mock_audio_area1); |
| free(mock_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->header = (struct cras_audio_shm_header*)calloc( |
| 1, sizeof(*shm->header) + 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->header = (struct cras_audio_shm_header*)calloc( |
| 1, sizeof(*shm->header) + 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(); |
| } |