blob: 06c1cd3c79e7853fcf76dc3574fdb0dcf1e3a9d0 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <gtest/gtest.h>
#include <stdint.h>
#include <stdio.h>
extern "C" {
#include "cras_a2dp_iodev.c"
#include "audio_thread.h"
#include "audio_thread_log.h"
#include "cras_audio_area.h"
#include "cras_bt_transport.h"
#include "cras_iodev.h"
}
#define FAKE_OBJECT_PATH "/fake/obj/path"
#define MAX_A2DP_ENCODE_CALLS 8
#define MAX_A2DP_WRITE_CALLS 4
/* Fake the codec to encode (512/4) frames into 128 bytes. */
#define FAKE_A2DP_CODE_SIZE 512
#define FAKE_A2DP_FRAME_LENGTH 128
static struct cras_bt_transport* fake_transport;
static cras_audio_format format;
static size_t cras_bt_device_append_iodev_called;
static size_t cras_bt_device_rm_iodev_called;
static size_t cras_iodev_add_node_called;
static size_t cras_iodev_rm_node_called;
static size_t cras_iodev_set_active_node_called;
static size_t cras_bt_transport_acquire_called;
static size_t cras_bt_transport_configuration_called;
static size_t cras_bt_transport_release_called;
static size_t init_a2dp_called;
static int init_a2dp_return_val;
static size_t destroy_a2dp_called;
static size_t a2dp_reset_called;
static size_t cras_iodev_free_format_called;
static size_t cras_iodev_free_resources_called;
static int a2dp_write_return_val[MAX_A2DP_WRITE_CALLS];
static unsigned int a2dp_write_index;
static int a2dp_encode_called;
static cras_audio_area* mock_audio_area;
static thread_callback write_callback;
static void* write_callback_data;
static const char* fake_device_name = "fake device name";
static const char* cras_bt_device_name_ret;
static unsigned int cras_bt_transport_write_mtu_ret;
static int cras_iodev_fill_odev_zeros_called;
static unsigned int cras_iodev_fill_odev_zeros_frames;
static int audio_thread_config_events_callback_called;
static enum AUDIO_THREAD_EVENTS_CB_TRIGGER
audio_thread_config_events_callback_trigger;
void ResetStubData() {
cras_bt_device_append_iodev_called = 0;
cras_bt_device_rm_iodev_called = 0;
cras_iodev_add_node_called = 0;
cras_iodev_rm_node_called = 0;
cras_iodev_set_active_node_called = 0;
cras_bt_transport_acquire_called = 0;
cras_bt_transport_configuration_called = 0;
cras_bt_transport_release_called = 0;
init_a2dp_called = 0;
init_a2dp_return_val = 0;
destroy_a2dp_called = 0;
a2dp_reset_called = 0;
cras_iodev_free_format_called = 0;
cras_iodev_free_resources_called = 0;
a2dp_write_index = 0;
a2dp_encode_called = 0;
/* Fake the MTU value. min_buffer_level will be derived from this value. */
cras_bt_transport_write_mtu_ret = 950;
cras_iodev_fill_odev_zeros_called = 0;
fake_transport = reinterpret_cast<struct cras_bt_transport*>(0x123);
if (!mock_audio_area) {
mock_audio_area = (cras_audio_area*)calloc(
1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2);
}
write_callback = NULL;
}
int iodev_set_format(struct cras_iodev* iodev, struct cras_audio_format* fmt) {
fmt->format = SND_PCM_FORMAT_S16_LE;
fmt->num_channels = 2;
fmt->frame_rate = 44100;
iodev->format = fmt;
return 0;
}
namespace {
static struct timespec time_now;
class A2dpIodev : public testing::Test {
protected:
virtual void SetUp() {
ResetStubData();
time_now.tv_sec = 0;
time_now.tv_nsec = 0;
atlog = (audio_thread_event_log*)calloc(1, sizeof(audio_thread_event_log));
}
virtual void TearDown() {
free(mock_audio_area);
mock_audio_area = NULL;
free(atlog);
}
};
TEST_F(A2dpIodev, InitializeA2dpIodev) {
struct cras_iodev* iodev;
cras_bt_device_name_ret = NULL;
iodev = a2dp_iodev_create(fake_transport);
ASSERT_NE(iodev, (void*)NULL);
ASSERT_EQ(iodev->direction, CRAS_STREAM_OUTPUT);
ASSERT_EQ(1, cras_bt_transport_configuration_called);
ASSERT_EQ(1, init_a2dp_called);
ASSERT_EQ(1, cras_bt_device_append_iodev_called);
ASSERT_EQ(1, cras_iodev_add_node_called);
ASSERT_EQ(1, cras_iodev_set_active_node_called);
/* Assert iodev name matches the object path when bt device doesn't
* have its readable name populated. */
ASSERT_STREQ(FAKE_OBJECT_PATH, iodev->info.name);
a2dp_iodev_destroy(iodev);
ASSERT_EQ(1, cras_bt_device_rm_iodev_called);
ASSERT_EQ(1, cras_iodev_rm_node_called);
ASSERT_EQ(1, destroy_a2dp_called);
ASSERT_EQ(1, cras_iodev_free_resources_called);
cras_bt_device_name_ret = fake_device_name;
/* Assert iodev name matches the bt device's name */
iodev = a2dp_iodev_create(fake_transport);
ASSERT_STREQ(fake_device_name, iodev->info.name);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, InitializeFail) {
struct cras_iodev* iodev;
init_a2dp_return_val = -1;
iodev = a2dp_iodev_create(fake_transport);
ASSERT_EQ(iodev, (void*)NULL);
ASSERT_EQ(1, cras_bt_transport_configuration_called);
ASSERT_EQ(1, init_a2dp_called);
ASSERT_EQ(0, cras_bt_device_append_iodev_called);
ASSERT_EQ(0, cras_iodev_add_node_called);
ASSERT_EQ(0, cras_iodev_set_active_node_called);
ASSERT_EQ(0, cras_iodev_rm_node_called);
}
TEST_F(A2dpIodev, OpenIodev) {
struct cras_iodev* iodev;
iodev = a2dp_iodev_create(fake_transport);
iodev_set_format(iodev, &format);
iodev->configure_dev(iodev);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
ASSERT_EQ(1, cras_bt_transport_acquire_called);
iodev->close_dev(iodev);
ASSERT_EQ(1, cras_bt_transport_release_called);
ASSERT_EQ(1, a2dp_reset_called);
ASSERT_EQ(1, cras_iodev_free_format_called);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, GetPutBuffer) {
struct cras_iodev* iodev;
struct cras_audio_area *area1, *area2, *area3;
uint8_t* last_buf_head;
unsigned frames;
struct timespec tstamp;
struct a2dp_io* a2dpio;
iodev = a2dp_iodev_create(fake_transport);
a2dpio = (struct a2dp_io*)iodev;
iodev_set_format(iodev, &format);
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
/* (950 - 13) / 128 * 512 / 4 */
ASSERT_EQ(iodev->min_buffer_level, 896);
frames = 1500;
iodev->get_buffer(iodev, &area1, &frames);
ASSERT_EQ(1500, frames);
ASSERT_EQ(1500, area1->frames);
last_buf_head = area1->channels[0].buf;
iodev->put_buffer(iodev, 1000);
/* 1000 frames takes 8 encode call, FAKE_A2DP_CODE_SIZE / 4 = 128
* and 7 * 128 < 1000 < 8 * 128
*/
EXPECT_EQ(8, a2dp_encode_called);
/* Expect flushed one block, leaving 1000 - 896 = 104 queued and
* next_flush_time has shifted. */
EXPECT_EQ(1, a2dp_write_index);
EXPECT_EQ(104, iodev->frames_queued(iodev, &tstamp));
EXPECT_GT(a2dpio->next_flush_time.tv_nsec, 0);
/* Assert buffer possition shifted 1000 * 4 bytes */
frames = 1000;
iodev->get_buffer(iodev, &area2, &frames);
ASSERT_EQ(1000, frames);
ASSERT_EQ(1000, area2->frames);
ASSERT_EQ(4000, area2->channels[0].buf - last_buf_head);
last_buf_head = area2->channels[0].buf;
iodev->put_buffer(iodev, 700);
EXPECT_EQ(804, iodev->frames_queued(iodev, &tstamp));
/* Assert that even next_flush_time is not met, pcm data still processed.
* Expect to takes 7 more encode calls to process the 804 frames of data.
* and 6 * 128 < 804 < 7 * 128
*/
EXPECT_EQ(15, a2dp_encode_called);
EXPECT_EQ(768, a2dpio->a2dp.samples);
time_now.tv_nsec = 25000000;
frames = 50;
iodev->get_buffer(iodev, &area3, &frames);
ASSERT_EQ(50, frames);
/* Assert buffer possition shifted 700 * 4 bytes */
EXPECT_EQ(2800, area3->channels[0].buf - last_buf_head);
iodev->put_buffer(iodev, 50);
/* 804 + 50 = 854 queued, 768 of them are encoded. */
EXPECT_EQ(854, iodev->frames_queued(iodev, &tstamp));
EXPECT_EQ(768, a2dpio->a2dp.samples);
/* Expect one a2dp encode call was executed for the left un-encoded frames.
* 854 - 768 = 86 < 128 */
EXPECT_EQ(16, a2dp_encode_called);
/* Even time now has passed next_flush_time, no a2dp write gets called
* because the number of encoded samples is not sufficient for a flush. */
EXPECT_EQ(1, a2dp_write_index);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, FramesQueued) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
struct timespec tstamp;
unsigned frames;
struct a2dp_io* a2dpio;
iodev = a2dp_iodev_create(fake_transport);
a2dpio = (struct a2dp_io*)iodev;
iodev_set_format(iodev, &format);
time_now.tv_sec = 0;
time_now.tv_nsec = 0;
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
/* a2dp_block_size(mtu) / format_bytes
* (950 - 13) / 128 * 512 / 4 = 896 */
EXPECT_EQ(896, a2dpio->write_block);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
frames = 256;
iodev->get_buffer(iodev, &area, &frames);
ASSERT_EQ(256, frames);
ASSERT_EQ(256, area->frames);
/* Data less than write_block hence not written. */
iodev->put_buffer(iodev, 200);
EXPECT_EQ(200, iodev->frames_queued(iodev, &tstamp));
EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
/* 200 + 800 - 896 = 104 */
a2dp_write_return_val[0] = 0;
frames = 800;
iodev->get_buffer(iodev, &area, &frames);
iodev->put_buffer(iodev, 800);
EXPECT_EQ(104, iodev->frames_queued(iodev, &tstamp));
/* Some time has passed, same amount of frames are queued. */
time_now.tv_nsec = 15000000;
write_callback(write_callback_data, POLLOUT);
EXPECT_EQ(104, iodev->frames_queued(iodev, &tstamp));
/* Put 900 more frames. next_flush_time not yet passed so expect
* total 900 + 104 = 1004 are queued. */
frames = 900;
iodev->get_buffer(iodev, &area, &frames);
iodev->put_buffer(iodev, 900);
EXPECT_EQ(1004, iodev->frames_queued(iodev, &tstamp));
/* Time passes next_flush_time, 1004 + 300 - 896 = 408 */
time_now.tv_nsec = 25000000;
frames = 300;
iodev->get_buffer(iodev, &area, &frames);
iodev->put_buffer(iodev, 300);
EXPECT_EQ(408, iodev->frames_queued(iodev, &tstamp));
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, SleepTimeWithWriteThrottle) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
unsigned frames;
unsigned int level;
unsigned long target;
struct timespec tstamp;
struct a2dp_io* a2dpio;
iodev = a2dp_iodev_create(fake_transport);
a2dpio = (struct a2dp_io*)iodev;
iodev_set_format(iodev, &format);
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
/* a2dp_block_size(mtu) / format_bytes
* 900 / 128 * 512 / 4 = 896 */
EXPECT_EQ(896, a2dpio->write_block);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
/* Both time now and next_flush_time are at 0. Expect write_block of
* time to sleep */
EXPECT_EQ(a2dpio->write_block,
iodev->frames_to_play_in_sleep(iodev, &level, &tstamp));
/* Fake that 1000 frames are put and one block got flushed.
* Expect next_wake_time be fast forward by one flush_period. */
frames = 1000;
iodev->get_buffer(iodev, &area, &frames);
ASSERT_EQ(1000, frames);
ASSERT_EQ(1000, area->frames);
/* Expect the first block be flushed at time 0. */
time_now.tv_nsec = 0;
a2dp_write_return_val[0] = 0;
EXPECT_EQ(0, iodev->put_buffer(iodev, 1000));
EXPECT_EQ(104, iodev->frames_queued(iodev, &tstamp)); /* 1000 - 896 */
/* Same amount of frames are queued after some time has passed. */
time_now.tv_nsec = 10000000;
EXPECT_EQ(104, iodev->frames_queued(iodev, &tstamp));
/* Expect to sleep the time between now(10ms) and next_flush_time(~20.3ms). */
frames = iodev->frames_to_play_in_sleep(iodev, &level, &tstamp);
target =
a2dpio->write_block - time_now.tv_nsec * format.frame_rate / 1000000000;
EXPECT_GE(frames + 1, target);
EXPECT_GE(target + 1, frames);
/* Time now has passed the next flush time(~20.3ms), expect to return
* write_block of time to sleep. */
time_now.tv_nsec = 25000000;
EXPECT_EQ(a2dpio->write_block,
iodev->frames_to_play_in_sleep(iodev, &level, &tstamp));
a2dp_write_return_val[1] = 0;
frames = 1000;
iodev->get_buffer(iodev, &area, &frames);
EXPECT_EQ(0, iodev->put_buffer(iodev, 1000));
EXPECT_EQ(208, iodev->frames_queued(iodev, &tstamp)); /* 104 + 1000 - 896 */
/* Flush another write_block of data, next_wake_time fast forward by
* another flush_period. Expect to sleep the time between now(25ms)
* and next_flush_time(~40.6ms). */
frames = iodev->frames_to_play_in_sleep(iodev, &level, &tstamp);
target = a2dpio->write_block * 2 -
time_now.tv_nsec * format.frame_rate / 1000000000;
EXPECT_GE(frames + 1, target);
EXPECT_GE(target + 1, frames);
/* Put 1000 more frames, and make a fake failure to this flush. */
time_now.tv_nsec = 45000000;
a2dp_write_return_val[2] = -EAGAIN;
frames = 1000;
iodev->get_buffer(iodev, &area, &frames);
EXPECT_EQ(0, iodev->put_buffer(iodev, 1000));
/* Last a2dp write call failed with -EAGAIN, time now(45ms) is after
* next_flush_time. Expect to return exact |write_block| equivalant
* of time to sleep. */
EXPECT_EQ(1208, iodev->frames_queued(iodev, &tstamp)); /* 208 + 1000 */
EXPECT_EQ(a2dpio->write_block,
iodev->frames_to_play_in_sleep(iodev, &level, &tstamp));
/* Fake the event that socket becomes writable so data continues to flush.
* next_flush_time fast forwards by another flush_period. */
a2dp_write_return_val[3] = 0;
write_callback(write_callback_data, POLLOUT);
EXPECT_EQ(312, iodev->frames_queued(iodev, &tstamp)); /* 1208 - 896 */
/* Expect to sleep the time between now and next_flush_time(~60.9ms). */
frames = iodev->frames_to_play_in_sleep(iodev, &level, &tstamp);
target = a2dpio->write_block * 3 -
time_now.tv_nsec * format.frame_rate / 1000000000;
EXPECT_GE(frames + 1, target);
EXPECT_GE(target + 1, frames);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, EnableThreadCallbackAtBufferFull) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
struct timespec tstamp;
unsigned frames;
struct a2dp_io* a2dpio;
iodev = a2dp_iodev_create(fake_transport);
a2dpio = (struct a2dp_io*)iodev;
iodev_set_format(iodev, &format);
time_now.tv_sec = 0;
time_now.tv_nsec = 0;
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
audio_thread_config_events_callback_called = 0;
a2dp_write_return_val[0] = 0;
frames = iodev->buffer_size;
iodev->get_buffer(iodev, &area, &frames);
EXPECT_LE(frames, iodev->buffer_size);
EXPECT_EQ(0, iodev->put_buffer(iodev, frames));
EXPECT_EQ(1, a2dp_write_index);
EXPECT_EQ(a2dpio->flush_period.tv_nsec, a2dpio->next_flush_time.tv_nsec);
EXPECT_EQ(1, audio_thread_config_events_callback_called);
EXPECT_EQ(TRIGGER_NONE, audio_thread_config_events_callback_trigger);
/* Fastfoward time 1ms, not yet reaches the next flush time. */
time_now.tv_nsec = 1000000;
/* Cram into iodev as much data as possible. Expect its buffer to
* be full because flush time does not yet met. */
frames = iodev->buffer_size;
iodev->get_buffer(iodev, &area, &frames);
EXPECT_LE(frames, iodev->buffer_size);
EXPECT_EQ(0, iodev->put_buffer(iodev, frames));
frames = iodev->frames_queued(iodev, &tstamp);
EXPECT_EQ(frames, iodev->buffer_size);
/* Expect a2dp_write didn't get called in last get/put buffer. And
* audio thread callback has been enabled. */
EXPECT_EQ(1, a2dp_write_index);
EXPECT_EQ(2, audio_thread_config_events_callback_called);
EXPECT_EQ(TRIGGER_WAKEUP, audio_thread_config_events_callback_trigger);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, FlushAtLowBufferLevel) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
struct timespec tstamp;
unsigned frames;
iodev = a2dp_iodev_create(fake_transport);
iodev_set_format(iodev, &format);
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
/* (950 - 13)/ 128 * 512 / 4 */
ASSERT_EQ(iodev->min_buffer_level, 896);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
frames = 1500;
iodev->get_buffer(iodev, &area, &frames);
ASSERT_EQ(1500, frames);
ASSERT_EQ(1500, area->frames);
/*
* Assert put_buffer shouldn't trigger the 2nd call to a2dp_encode()
* because buffer is low: 896 < 1500 < 896 * 2
*/
a2dp_write_return_val[0] = 0;
EXPECT_EQ(0, iodev->put_buffer(iodev, 1500));
EXPECT_EQ(1, a2dp_write_index);
/* 1500 - 896 */
time_now.tv_nsec = 25000000;
EXPECT_EQ(604, iodev->frames_queued(iodev, &tstamp));
EXPECT_EQ(tstamp.tv_sec, time_now.tv_sec);
EXPECT_EQ(tstamp.tv_nsec, time_now.tv_nsec);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, HandleUnderrun) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
struct timespec tstamp;
unsigned frames;
iodev = a2dp_iodev_create(fake_transport);
iodev_set_format(iodev, &format);
time_now.tv_sec = 0;
time_now.tv_nsec = 0;
iodev->configure_dev(iodev);
/* (950 - 13) / 128 * 512 / 4 */
EXPECT_EQ(896, iodev->min_buffer_level);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
frames = 300;
iodev->get_buffer(iodev, &area, &frames);
ASSERT_EQ(300, frames);
ASSERT_EQ(300, area->frames);
a2dp_write_return_val[0] = -EAGAIN;
time_now.tv_nsec = 10000000;
iodev->put_buffer(iodev, 300);
time_now.tv_nsec = 20000000;
EXPECT_EQ(300, iodev->frames_queued(iodev, &tstamp));
/* Frames queued below min_buffer_level, which is derived from transport MTU.
* Assert min_cb_level of zero frames are filled. */
iodev->min_cb_level = 150;
iodev->output_underrun(iodev);
ASSERT_EQ(1, cras_iodev_fill_odev_zeros_called);
EXPECT_EQ(150, cras_iodev_fill_odev_zeros_frames);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, LeavingNoStreamStateWithSmallStreamDoesntUnderrun) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
struct timespec tstamp;
unsigned frames;
struct a2dp_io* a2dpio;
iodev = a2dp_iodev_create(fake_transport);
a2dpio = (struct a2dp_io*)iodev;
iodev_set_format(iodev, &format);
time_now.tv_sec = 0;
time_now.tv_nsec = 0;
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
/* (950 - 13)/ 128 * 512 / 4 */
ASSERT_EQ(896, iodev->min_buffer_level);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
/* Put iodev in no_stream state. Verify it doesn't underrun after each
* call of no_stream ops. */
a2dp_write_return_val[0] = 0;
iodev->no_stream(iodev, 1);
EXPECT_EQ(1, a2dp_write_index);
EXPECT_EQ(a2dpio->flush_period.tv_nsec, a2dpio->next_flush_time.tv_nsec);
frames = iodev->frames_queued(iodev, &tstamp);
EXPECT_LE(iodev->min_buffer_level, frames);
/* Some time has passed and a small stream of 200 frames block is added.
* Verify leaving no_stream state doesn't underrun immediately. */
time_now.tv_nsec = 20000000;
iodev->no_stream(iodev, 1);
frames = 200;
iodev->get_buffer(iodev, &area, &frames);
iodev->put_buffer(iodev, 200);
frames = iodev->frames_queued(iodev, &tstamp);
EXPECT_LE(iodev->min_buffer_level, frames);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, NoStreamStateFillZerosToTargetLevel) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
struct timespec tstamp;
unsigned frames;
struct a2dp_io* a2dpio;
iodev = a2dp_iodev_create(fake_transport);
a2dpio = (struct a2dp_io*)iodev;
iodev_set_format(iodev, &format);
time_now.tv_sec = 0;
time_now.tv_nsec = 0;
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
/* (950 - 13)/ 128 * 512 / 4 */
ASSERT_EQ(896, iodev->min_buffer_level);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
iodev->min_cb_level = 480;
frames = 200;
iodev->get_buffer(iodev, &area, &frames);
iodev->put_buffer(iodev, 200);
a2dp_write_return_val[0] = 0;
iodev->no_stream(iodev, 1);
EXPECT_EQ(1, a2dp_write_index);
EXPECT_EQ(a2dpio->flush_period.tv_nsec, a2dpio->next_flush_time.tv_nsec);
/* Some time has passed but not yet reach next flush. Entering no_stream
* fills buffer to 3 times of min_buffer_level. */
time_now.tv_nsec = 10000000;
iodev->no_stream(iodev, 1);
frames = iodev->frames_queued(iodev, &tstamp);
EXPECT_EQ(3 * iodev->min_buffer_level, frames);
/* Time has passed next flush time, expect one block is flushed. */
a2dp_write_return_val[1] = 0;
time_now.tv_nsec = 25000000;
iodev->no_stream(iodev, 1);
frames = iodev->frames_queued(iodev, &tstamp);
ASSERT_EQ(2 * iodev->min_buffer_level, frames);
EXPECT_EQ(2, a2dp_write_index);
/* Leaving no_stream state fills buffer level back to 2 * min_buffer_level.
*/
a2dp_write_return_val[2] = 0;
time_now.tv_nsec = 30000000;
iodev->no_stream(iodev, 0);
frames = iodev->frames_queued(iodev, &tstamp);
ASSERT_EQ(2 * iodev->min_buffer_level, frames);
EXPECT_EQ(2, a2dp_write_index);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
TEST_F(A2dpIodev, EnterNoStreamStateAtHighBufferLevelDoesntFillMore) {
struct cras_iodev* iodev;
struct cras_audio_area* area;
struct timespec tstamp;
unsigned frames, start_level;
struct a2dp_io* a2dpio;
iodev = a2dp_iodev_create(fake_transport);
a2dpio = (struct a2dp_io*)iodev;
iodev_set_format(iodev, &format);
time_now.tv_sec = 0;
time_now.tv_nsec = 0;
iodev->configure_dev(iodev);
ASSERT_NE(write_callback, (void*)NULL);
/* (950 - 13)/ 128 * 512 / 4 */
ASSERT_EQ(896, iodev->min_buffer_level);
iodev->start(iodev);
iodev->state = CRAS_IODEV_STATE_NORMAL_RUN;
a2dp_write_return_val[0] = 0;
start_level = 6000;
frames = start_level;
iodev->get_buffer(iodev, &area, &frames);
iodev->put_buffer(iodev, frames);
frames = iodev->frames_queued(iodev, &tstamp);
/* Assert one block has fluxhed */
EXPECT_EQ(start_level - iodev->min_buffer_level, frames);
EXPECT_EQ(1, a2dp_write_index);
EXPECT_EQ(a2dpio->flush_period.tv_nsec, a2dpio->next_flush_time.tv_nsec);
a2dp_write_return_val[1] = 0;
time_now.tv_nsec = 25000000;
iodev->no_stream(iodev, 1);
frames = iodev->frames_queued(iodev, &tstamp);
/* Next flush time meets requirement so another block is flushed. */
ASSERT_EQ(start_level - 2 * iodev->min_buffer_level, frames);
a2dp_write_return_val[2] = 0;
time_now.tv_nsec = 50000000;
iodev->no_stream(iodev, 1);
frames = iodev->frames_queued(iodev, &tstamp);
/* Another block flushed at leaving no stream state. No more data
* filled because level is high. */
ASSERT_EQ(start_level - 3 * iodev->min_buffer_level, frames);
iodev->close_dev(iodev);
a2dp_iodev_destroy(iodev);
}
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
extern "C" {
int cras_bt_transport_configuration(const struct cras_bt_transport* transport,
void* configuration,
int len) {
memset(configuration, 0, len);
cras_bt_transport_configuration_called++;
return 0;
}
int cras_bt_transport_acquire(struct cras_bt_transport* transport) {
cras_bt_transport_acquire_called++;
return 0;
}
int cras_bt_transport_release(struct cras_bt_transport* transport,
unsigned int blocking) {
cras_bt_transport_release_called++;
return 0;
}
int cras_bt_transport_fd(const struct cras_bt_transport* transport) {
return 0;
}
const char* cras_bt_transport_object_path(
const struct cras_bt_transport* transport) {
return FAKE_OBJECT_PATH;
}
uint16_t cras_bt_transport_write_mtu(
const struct cras_bt_transport* transport) {
return cras_bt_transport_write_mtu_ret;
}
int cras_bt_transport_set_volume(struct cras_bt_transport* transport,
uint16_t volume) {
return 0;
}
void cras_iodev_free_format(struct cras_iodev* iodev) {
cras_iodev_free_format_called++;
}
void cras_iodev_free_resources(struct cras_iodev* iodev) {
cras_iodev_free_resources_called++;
}
// Cras iodev
void cras_iodev_add_node(struct cras_iodev* iodev, struct cras_ionode* node) {
cras_iodev_add_node_called++;
iodev->nodes = node;
}
void cras_iodev_rm_node(struct cras_iodev* iodev, struct cras_ionode* node) {
cras_iodev_rm_node_called++;
iodev->nodes = NULL;
}
void cras_iodev_set_active_node(struct cras_iodev* iodev,
struct cras_ionode* node) {
cras_iodev_set_active_node_called++;
iodev->active_node = node;
}
// From cras_bt_transport
struct cras_bt_device* cras_bt_transport_device(
const struct cras_bt_transport* transport) {
return reinterpret_cast<struct cras_bt_device*>(0x456);
;
}
enum cras_bt_device_profile cras_bt_transport_profile(
const struct cras_bt_transport* transport) {
return CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE;
}
// From cras_bt_device
const char* cras_bt_device_name(const struct cras_bt_device* device) {
return cras_bt_device_name_ret;
}
const char* cras_bt_device_object_path(const struct cras_bt_device* device) {
return "/org/bluez/hci0/dev_1A_2B_3C_4D_5E_6F";
}
int cras_bt_device_get_stable_id(const struct cras_bt_device* device) {
return 123;
}
void cras_bt_device_append_iodev(struct cras_bt_device* device,
struct cras_iodev* iodev,
enum cras_bt_device_profile profile) {
cras_bt_device_append_iodev_called++;
}
void cras_bt_device_rm_iodev(struct cras_bt_device* device,
struct cras_iodev* iodev) {
cras_bt_device_rm_iodev_called++;
}
int cras_bt_device_get_use_hardware_volume(struct cras_bt_device* device) {
return 0;
}
int cras_bt_device_cancel_suspend(struct cras_bt_device* device) {
return 0;
}
int cras_bt_device_schedule_suspend(
struct cras_bt_device* device,
unsigned int msec,
enum cras_bt_device_suspend_reason suspend_reason) {
return 0;
}
int init_a2dp(struct a2dp_info* a2dp, a2dp_sbc_t* sbc) {
init_a2dp_called++;
memset(a2dp, 0, sizeof(*a2dp));
a2dp->frame_length = FAKE_A2DP_FRAME_LENGTH;
a2dp->codesize = FAKE_A2DP_CODE_SIZE;
return init_a2dp_return_val;
}
void destroy_a2dp(struct a2dp_info* a2dp) {
destroy_a2dp_called++;
}
int a2dp_codesize(struct a2dp_info* a2dp) {
return a2dp->codesize;
}
int a2dp_block_size(struct a2dp_info* a2dp, int encoded_bytes) {
return encoded_bytes / a2dp->frame_length * a2dp->codesize;
}
int a2dp_queued_frames(const struct a2dp_info* a2dp) {
return a2dp->samples;
}
void a2dp_reset(struct a2dp_info* a2dp) {
a2dp_reset_called++;
a2dp->samples = 0;
}
int a2dp_encode(struct a2dp_info* a2dp,
const void* pcm_buf,
int pcm_buf_size,
int format_bytes,
size_t link_mtu) {
int processed = 0;
a2dp_encode_called++;
if (a2dp->a2dp_buf_used + a2dp->frame_length > link_mtu)
return 0;
if (pcm_buf_size < a2dp->codesize)
return 0;
processed += a2dp->codesize;
a2dp->a2dp_buf_used += a2dp->frame_length;
a2dp->samples += processed / format_bytes;
return processed;
}
int a2dp_write(struct a2dp_info* a2dp, int stream_fd, size_t link_mtu) {
int ret, samples;
if (a2dp->frame_length + a2dp->a2dp_buf_used < link_mtu)
return 0;
ret = a2dp_write_return_val[a2dp_write_index++];
if (ret < 0)
return ret;
samples = a2dp->samples;
a2dp->samples = 0;
a2dp->a2dp_buf_used = 0;
return samples;
}
int clock_gettime(clockid_t clk_id, struct timespec* tp) {
*tp = time_now;
return 0;
}
void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
iodev->area = mock_audio_area;
}
void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
int cras_iodev_fill_odev_zeros(struct cras_iodev* odev, unsigned int frames) {
struct cras_audio_area* area;
cras_iodev_fill_odev_zeros_called++;
cras_iodev_fill_odev_zeros_frames = frames;
odev->get_buffer(odev, &area, &frames);
odev->put_buffer(odev, frames);
return 0;
}
void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
const struct cras_audio_format* fmt,
uint8_t* base_buffer) {
mock_audio_area->channels[0].buf = base_buffer;
}
struct audio_thread* cras_iodev_list_get_audio_thread() {
return NULL;
}
// From ewma_power
void ewma_power_disable(struct ewma_power* ewma) {}
// From audio_thread
struct audio_thread_event_log* atlog;
void audio_thread_add_events_callback(int fd,
thread_callback cb,
void* data,
int events) {
write_callback = cb;
write_callback_data = data;
}
int audio_thread_rm_callback_sync(struct audio_thread* thread, int fd) {
return 0;
}
void audio_thread_config_events_callback(
int fd,
enum AUDIO_THREAD_EVENTS_CB_TRIGGER trigger) {
audio_thread_config_events_callback_called++;
audio_thread_config_events_callback_trigger = trigger;
}
}
int cras_audio_thread_event_a2dp_overrun() {
return 0;
}
int cras_audio_thread_event_a2dp_throttle() {
return 0;
}