summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYu-Hsuan Hsu <yuhsuan@chromium.org>2020-12-31 15:31:38 +0800
committerCommit Bot <commit-bot@chromium.org>2021-02-17 17:26:25 +0000
commit350e9cdbd7ce9a80aec07f6d76976ecbcba53c83 (patch)
treeda33acf13c68b76920f26c97c16fe468dc7e432d
parentbea57fede9093e2ecff2cfe3258f626194a88714 (diff)
downloadadhd-350e9cdbd7ce9a80aec07f6d76976ecbcba53c83.tar.gz
CRAS: Library ABI for stream callback
Make the stream callback support version skew. BUG=b:176570789 TEST=Verified the audio work fine Change-Id: Ie6e8ea0081866fc9ad99753dc1cf80415ed44d6f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/adhd/+/2605941 Reviewed-by: Cheng-Yi Chiang <cychiang@chromium.org> Commit-Queue: Yu-Hsuan Hsu <yuhsuan@chromium.org> Tested-by: Yu-Hsuan Hsu <yuhsuan@chromium.org>
-rw-r--r--cras/src/libcras/cras_client.c135
-rw-r--r--cras/src/libcras/cras_client.h106
-rw-r--r--cras/src/tests/cras_abi_unittest.cc96
3 files changed, 311 insertions, 26 deletions
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c
index f8891477..c351a80a 100644
--- a/cras/src/libcras/cras_client.c
+++ b/cras/src/libcras/cras_client.c
@@ -119,7 +119,8 @@ struct thread_state {
};
/* Parameters used when setting up a capture or playback stream. See comment
- * above cras_client_create_stream_params in the header for descriptions. */
+ * above cras_client_stream_params_create or libcras_stream_params_set in the
+ * header for descriptions. */
struct cras_stream_params {
enum CRAS_STREAM_DIRECTION direction;
size_t buffer_frames;
@@ -133,6 +134,7 @@ struct cras_stream_params {
cras_unified_cb_t unified_cb;
cras_error_cb_t err_cb;
struct cras_audio_format format;
+ libcras_stream_cb_t stream_cb;
};
/* Represents an attached audio stream.
@@ -274,6 +276,92 @@ struct cras_hotword_handle {
void *user_data;
};
+struct cras_stream_cb_data {
+ cras_stream_id_t stream_id;
+ enum CRAS_STREAM_DIRECTION direction;
+ uint8_t *buf;
+ unsigned int frames;
+ struct timespec sample_ts;
+ void *user_arg;
+};
+
+int stream_cb_get_stream_id(struct cras_stream_cb_data *data,
+ cras_stream_id_t *id)
+{
+ *id = data->stream_id;
+ return 0;
+}
+
+int stream_cb_get_buf(struct cras_stream_cb_data *data, uint8_t **buf)
+{
+ *buf = data->buf;
+ return 0;
+}
+
+int stream_cb_get_frames(struct cras_stream_cb_data *data, unsigned int *frames)
+{
+ *frames = data->frames;
+ return 0;
+}
+
+int stream_cb_get_latency(struct cras_stream_cb_data *data,
+ struct timespec *latency)
+{
+ if (data->direction == CRAS_STREAM_INPUT)
+ cras_client_calc_capture_latency(&data->sample_ts, latency);
+ else
+ cras_client_calc_playback_latency(&data->sample_ts, latency);
+ return 0;
+}
+
+int stream_cb_get_user_arg(struct cras_stream_cb_data *data, void **user_arg)
+{
+ *user_arg = data->user_arg;
+ return 0;
+}
+
+struct libcras_stream_cb_data *
+libcras_stream_cb_data_create(cras_stream_id_t stream_id,
+ enum CRAS_STREAM_DIRECTION direction,
+ uint8_t *buf, unsigned int frames,
+ struct timespec sample_ts, void *user_arg)
+{
+ struct libcras_stream_cb_data *data =
+ (struct libcras_stream_cb_data *)calloc(
+ 1, sizeof(struct libcras_stream_cb_data));
+ if (!data) {
+ syslog(LOG_ERR, "cras_client: calloc: %s", strerror(errno));
+ return NULL;
+ }
+ data->data_ = (struct cras_stream_cb_data *)calloc(
+ 1, sizeof(struct cras_stream_cb_data));
+ if (!data->data_) {
+ syslog(LOG_ERR, "cras_client: calloc: %s", strerror(errno));
+ free(data);
+ return NULL;
+ }
+ data->api_version = CRAS_API_VERSION;
+ data->get_stream_id = stream_cb_get_stream_id;
+ data->get_buf = stream_cb_get_buf;
+ data->get_frames = stream_cb_get_frames;
+ data->get_latency = stream_cb_get_latency;
+ data->get_user_arg = stream_cb_get_user_arg;
+ data->data_->stream_id = stream_id;
+ data->data_->direction = direction;
+ data->data_->buf = buf;
+ data->data_->frames = frames;
+ data->data_->sample_ts = sample_ts;
+ data->data_->user_arg = user_arg;
+ return data;
+}
+
+void libcras_stream_cb_data_destroy(struct libcras_stream_cb_data *data)
+{
+ if (data)
+ free(data->data_);
+ free(data);
+}
+
/*
* Local Helpers
*/
@@ -1084,6 +1172,7 @@ static int handle_capture_data_ready(struct client_stream *stream,
uint8_t *captured_frames;
struct timespec ts;
int rc = 0;
+ struct libcras_stream_cb_data *data;
config = stream->config;
/* If this message is for an output stream, log error and drop it. */
@@ -1098,14 +1187,24 @@ static int handle_capture_data_ready(struct client_stream *stream,
cras_timespec_to_timespec(&ts, &stream->shm->header->ts);
- if (config->unified_cb)
+ if (config->stream_cb) {
+ data = libcras_stream_cb_data_create(
+ stream->id, stream->direction, captured_frames,
+ num_frames, ts, config->user_data);
+ if (!data)
+ return -errno;
+ frames = config->stream_cb(data);
+ libcras_stream_cb_data_destroy(data);
+ data = NULL;
+ } else if (config->unified_cb) {
frames = config->unified_cb(stream->client, stream->id,
captured_frames, NULL, num_frames,
&ts, NULL, config->user_data);
- else
+ } else {
frames = config->aud_cb(stream->client, stream->id,
captured_frames, num_frames, &ts,
config->user_data);
+ }
if (frames < 0) {
send_stream_message(stream, CLIENT_STREAM_EOF);
rc = frames;
@@ -1152,6 +1251,7 @@ static int handle_playback_request(struct client_stream *stream,
struct cras_stream_params *config;
struct cras_audio_shm *shm = stream->shm;
struct timespec ts;
+ struct libcras_stream_cb_data *data;
config = stream->config;
@@ -1169,13 +1269,24 @@ static int handle_playback_request(struct client_stream *stream,
cras_timespec_to_timespec(&ts, &shm->header->ts);
/* Get samples from the user */
- if (config->unified_cb)
+ if (config->stream_cb) {
+ data = libcras_stream_cb_data_create(stream->id,
+ stream->direction, buf,
+ num_frames, ts,
+ config->user_data);
+ if (!data)
+ return -errno;
+ frames = config->stream_cb(data);
+ libcras_stream_cb_data_destroy(data);
+ data = NULL;
+ } else if (config->unified_cb) {
frames = config->unified_cb(stream->client, stream->id, NULL,
buf, num_frames, NULL, &ts,
config->user_data);
- else
+ } else {
frames = config->aud_cb(stream->client, stream->id, buf,
num_frames, &ts, config->user_data);
+ }
if (frames < 0) {
send_stream_message(stream, CLIENT_STREAM_EOF);
rc = frames;
@@ -2255,6 +2366,7 @@ struct cras_stream_params *cras_client_stream_params_create(
params->user_data = user_data;
params->aud_cb = aud_cb;
params->unified_cb = 0;
+ params->stream_cb = 0;
params->err_cb = err_cb;
memcpy(&(params->format), format, sizeof(*format));
return params;
@@ -2328,6 +2440,7 @@ struct cras_stream_params *cras_client_unified_params_create(
params->user_data = user_data;
params->aud_cb = 0;
params->unified_cb = unified_cb;
+ params->stream_cb = 0;
params->err_cb = err_cb;
memcpy(&(params->format), format, sizeof(*format));
@@ -2350,7 +2463,8 @@ static inline int cras_client_send_add_stream_command_message(
if (client == NULL || config == NULL || stream_id_out == NULL)
return -EINVAL;
- if (config->aud_cb == NULL && config->unified_cb == NULL)
+ if (config->stream_cb == NULL && config->aud_cb == NULL &&
+ config->unified_cb == NULL)
return -EINVAL;
if (config->err_cb == NULL)
@@ -3851,9 +3965,9 @@ int stream_params_set(struct cras_stream_params *params,
size_t buffer_frames, size_t cb_threshold,
enum CRAS_STREAM_TYPE stream_type,
enum CRAS_CLIENT_TYPE client_type, uint32_t flags,
- void *user_data, cras_unified_cb_t unified_cb,
- cras_playback_cb_t aud_cb, cras_error_cb_t err_cb,
- size_t rate, snd_pcm_format_t format, size_t num_channels)
+ void *user_data, libcras_stream_cb_t stream_cb,
+ cras_error_cb_t err_cb, size_t rate,
+ snd_pcm_format_t format, size_t num_channels)
{
params->direction = direction;
params->buffer_frames = buffer_frames;
@@ -3862,8 +3976,7 @@ int stream_params_set(struct cras_stream_params *params,
params->client_type = client_type;
params->flags = flags;
params->user_data = user_data;
- params->unified_cb = unified_cb;
- params->aud_cb = aud_cb;
+ params->stream_cb = stream_cb;
params->err_cb = err_cb;
params->format.frame_rate = rate;
params->format.format = format;
diff --git a/cras/src/libcras/cras_client.h b/cras/src/libcras/cras_client.h
index 2a674207..b60d2697 100644
--- a/cras/src/libcras/cras_client.h
+++ b/cras/src/libcras/cras_client.h
@@ -1349,6 +1349,21 @@ struct libcras_client {
float volume_scaler);
};
+struct cras_stream_cb_data;
+struct libcras_stream_cb_data {
+ int api_version;
+ struct cras_stream_cb_data *data_;
+ int (*get_stream_id)(struct cras_stream_cb_data *data,
+ cras_stream_id_t *id);
+ int (*get_buf)(struct cras_stream_cb_data *data, uint8_t **buf);
+ int (*get_frames)(struct cras_stream_cb_data *data,
+ unsigned int *frames);
+ int (*get_latency)(struct cras_stream_cb_data *data,
+ struct timespec *latency);
+ int (*get_user_arg)(struct cras_stream_cb_data *data, void **user_arg);
+};
+typedef int (*libcras_stream_cb_t)(struct libcras_stream_cb_data *data);
+
struct libcras_stream_params {
int api_version;
struct cras_stream_params *params_;
@@ -1356,9 +1371,9 @@ struct libcras_stream_params {
enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames,
size_t cb_threshold, enum CRAS_STREAM_TYPE stream_type,
enum CRAS_CLIENT_TYPE client_type, uint32_t flags,
- void *user_data, cras_unified_cb_t unified_cb,
- cras_playback_cb_t aud_cb, cras_error_cb_t err_cb,
- size_t rate, snd_pcm_format_t format, size_t num_channels);
+ void *user_data, libcras_stream_cb_t stream_cb,
+ cras_error_cb_t err_cb, size_t rate, snd_pcm_format_t format,
+ size_t num_channels);
int (*set_channel_layout)(struct cras_stream_params *params, int length,
const int8_t *layout);
void (*enable_aec)(struct cras_stream_params *params);
@@ -1545,8 +1560,8 @@ void libcras_stream_params_destroy(struct libcras_stream_params *params);
* client_type - The client type, like Chrome or CrOSVM.
* flags - Currently only used for CRAS_INPUT_STREAM_FLAG.
* user_data - Pointer that will be passed to the callback.
- * unified_cb - The playback callback. Called when audio is needed.
- * aud_cb - The capture callback. Called when audio is ready.
+ * stream_cb - The audio callback. Called when audio is needed(playback) or
+ * ready(capture).
* err_cb - Called when there is an error with the stream.
* rate - The sample rate of the audio stream.
* format - The format of the audio stream.
@@ -1559,13 +1574,12 @@ inline int libcras_stream_params_set(
enum CRAS_STREAM_DIRECTION direction, size_t buffer_frames,
size_t cb_threshold, enum CRAS_STREAM_TYPE stream_type,
enum CRAS_CLIENT_TYPE client_type, uint32_t flags, void *user_data,
- cras_unified_cb_t unified_cb, cras_playback_cb_t aud_cb,
- cras_error_cb_t err_cb, size_t rate, snd_pcm_format_t format,
- size_t num_channels)
+ libcras_stream_cb_t stream_cb, cras_error_cb_t err_cb, size_t rate,
+ snd_pcm_format_t format, size_t num_channels)
{
return params->set(params->params_, direction, buffer_frames,
cb_threshold, stream_type, client_type, flags,
- user_data, unified_cb, aud_cb, err_cb, rate, format,
+ user_data, stream_cb, err_cb, rate, format,
num_channels);
}
@@ -1600,6 +1614,80 @@ libcras_stream_params_enable_aec(struct libcras_stream_params *params)
return 0;
}
+/*
+ * Gets stream id from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * id - The pointer to save the stream id.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+inline int
+libcras_stream_cb_data_get_stream_id(struct libcras_stream_cb_data *data,
+ cras_stream_id_t *id)
+{
+ return data->get_stream_id(data->data_, id);
+}
+
+/*
+ * Gets stream buf from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * buf - The pointer to save the stream buffer.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+inline int libcras_stream_cb_data_get_buf(struct libcras_stream_cb_data *data,
+ uint8_t **buf)
+{
+ return data->get_buf(data->data_, buf);
+}
+
+/*
+ * Gets how many frames to read or play from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * frames - The pointer to save the number of frames.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+inline int
+libcras_stream_cb_data_get_frames(struct libcras_stream_cb_data *data,
+ unsigned int *frames)
+{
+ return data->get_frames(data->data_, frames);
+}
+
+/*
+ * Gets the latency from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * frames - The timespec pointer to save the latency.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+inline int
+libcras_stream_cb_data_get_latency(struct libcras_stream_cb_data *data,
+ struct timespec *latency)
+{
+ return data->get_latency(data->data_, latency);
+}
+
+/*
+ * Gets the user data from the callback data.
+ * Args:
+ * data - The pointer passed to the callback function.
+ * frames - The pointer to save the user data.
+ * Returns:
+ * 0 on success negative error code on failure (from errno.h).
+ */
+inline int
+libcras_stream_cb_data_get_usr_arg(struct libcras_stream_cb_data *data,
+ void **user_arg)
+{
+ return data->get_user_arg(data->data_, user_arg);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/cras/src/tests/cras_abi_unittest.cc b/cras/src/tests/cras_abi_unittest.cc
index 4b61166e..d566a9b7 100644
--- a/cras/src/tests/cras_abi_unittest.cc
+++ b/cras/src/tests/cras_abi_unittest.cc
@@ -13,18 +13,60 @@ inline int libcras_unsupported_func(struct libcras_client* client) {
CHECK_VERSION(client, INT_MAX);
return 0;
}
+
+cras_stream_id_t cb_stream_id;
+uint8_t* cb_buf;
+unsigned int cb_frames;
+struct timespec cb_latency;
+void* cb_usr_arg;
+int get_stream_cb_called;
+struct timespec now;
+
+int get_stream_cb(struct libcras_stream_cb_data* data) {
+ get_stream_cb_called++;
+ EXPECT_NE((void*)NULL, data);
+ EXPECT_EQ(0, libcras_stream_cb_data_get_stream_id(data, &cb_stream_id));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_buf(data, &cb_buf));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_frames(data, &cb_frames));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_latency(data, &cb_latency));
+ EXPECT_EQ(0, libcras_stream_cb_data_get_usr_arg(data, &cb_usr_arg));
+ return 0;
+}
}
namespace {
+class CrasAbiTestSuite : public testing::Test {
+ protected:
+ struct cras_audio_shm* InitShm(int frames) {
+ struct cras_audio_shm* shm =
+ static_cast<struct cras_audio_shm*>(calloc(1, sizeof(*shm)));
+ shm->header =
+ static_cast<cras_audio_shm_header*>(calloc(1, sizeof(*shm->header)));
+ cras_shm_set_frame_bytes(shm, 4);
+ uint32_t used_size = frames * 4;
+ cras_shm_set_used_size(shm, used_size);
+ shm->samples_info.length = used_size * 2;
+ memcpy(&shm->header->config, &shm->config, sizeof(shm->config));
+ return shm;
+ }
+
+ void DestroyShm(struct cras_audio_shm* shm) {
+ if (shm)
+ free(shm->header);
+ free(shm);
+ }
-TEST(CrasAbiTestSuite, CheckUnsupportedFunction) {
+ virtual void SetUp() { get_stream_cb_called = 0; }
+};
+
+TEST_F(CrasAbiTestSuite, CheckUnsupportedFunction) {
auto* client = libcras_client_create();
EXPECT_NE((void*)NULL, client);
EXPECT_EQ(-ENOSYS, libcras_unsupported_func(client));
libcras_client_destroy(client);
}
-TEST(CrasAbiTestSuite, BasicStream) {
+TEST_F(CrasAbiTestSuite, BasicStream) {
auto* client = libcras_client_create();
EXPECT_NE((void*)NULL, client);
auto* stream = libcras_stream_params_create();
@@ -32,10 +74,10 @@ TEST(CrasAbiTestSuite, BasicStream) {
/* Returns timeout because there is no real CRAS server in unittest. */
EXPECT_EQ(-ETIMEDOUT, libcras_client_connect_timeout(client, 0));
EXPECT_EQ(0, libcras_client_run_thread(client));
- EXPECT_EQ(0, libcras_stream_params_set(
- stream, CRAS_STREAM_INPUT, 480, 480,
- CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_TEST, 0, NULL,
- NULL, NULL, NULL, 48000, SND_PCM_FORMAT_S16, 2));
+ EXPECT_EQ(0, libcras_stream_params_set(stream, CRAS_STREAM_INPUT, 480, 480,
+ CRAS_STREAM_TYPE_DEFAULT,
+ CRAS_CLIENT_TYPE_TEST, 0, NULL, NULL,
+ NULL, 48000, SND_PCM_FORMAT_S16, 2));
cras_stream_id_t id;
/* Fails to add a stream because the stream callback is not set. */
EXPECT_EQ(-EINVAL, libcras_client_add_pinned_stream(client, 0, &id, stream));
@@ -47,9 +89,51 @@ TEST(CrasAbiTestSuite, BasicStream) {
libcras_client_destroy(client);
}
+TEST_F(CrasAbiTestSuite, StreamCallback) {
+ struct client_stream stream;
+ struct cras_stream_params params;
+ stream.id = 0x123;
+ stream.direction = CRAS_STREAM_INPUT;
+ stream.flags = 0;
+ stream.config = &params;
+ params.stream_cb = get_stream_cb;
+ params.cb_threshold = 480;
+ params.user_data = (void*)0x321;
+ stream.shm = InitShm(960);
+ stream.shm->header->write_offset[0] = 960 * 4;
+ stream.shm->header->write_buf_idx = 0;
+ stream.shm->header->read_offset[0] = 0;
+ stream.shm->header->read_buf_idx = 0;
+ now.tv_sec = 100;
+ now.tv_nsec = 0;
+ stream.shm->header->ts.tv_sec = 90;
+ stream.shm->header->ts.tv_nsec = 0;
+
+ handle_capture_data_ready(&stream, 480);
+
+ EXPECT_EQ(1, get_stream_cb_called);
+ EXPECT_EQ(stream.id, cb_stream_id);
+ EXPECT_EQ(cras_shm_get_write_buffer_base(stream.shm), cb_buf);
+ EXPECT_EQ(480, cb_frames);
+ EXPECT_EQ(10, cb_latency.tv_sec);
+ EXPECT_EQ(0, cb_latency.tv_nsec);
+ EXPECT_EQ((void*)0x321, cb_usr_arg);
+
+ DestroyShm(stream.shm);
+}
+
} // namespace
+extern "C" {
+
+int clock_gettime(clockid_t clk_id, struct timespec* tp) {
+ *tp = now;
+ return 0;
+}
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
+ openlog(NULL, LOG_PERROR, LOG_USER);
return RUN_ALL_TESTS();
}