blob: ae9f5ef3724fd27ee2352bd689d559ad515007cc [file] [log] [blame]
// 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();
}