summaryrefslogtreecommitdiff
path: root/cras/src/libcras/cras_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/libcras/cras_client.c')
-rw-r--r--cras/src/libcras/cras_client.c444
1 files changed, 438 insertions, 6 deletions
diff --git a/cras/src/libcras/cras_client.c b/cras/src/libcras/cras_client.c
index 3fe631d5..8420db1f 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
*/
@@ -283,6 +371,10 @@ static int client_thread_rm_stream(struct cras_client *client,
static int handle_message_from_server(struct cras_client *client);
static int reregister_notifications(struct cras_client *client);
+static struct libcras_node_info *
+libcras_node_info_create(struct cras_iodev_info *iodev,
+ struct cras_ionode_info *ionode);
+
/*
* Unlock the server_state_rwlock if lock_rc is 0.
*
@@ -1084,6 +1176,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 +1191,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 +1255,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 +1273,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 +2370,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 +2444,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 +2467,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)
@@ -3815,3 +3933,317 @@ int cras_client_disable_hotword_callback(struct cras_client *client,
free(handle);
return 0;
}
+
+int get_nodes(struct cras_client *client, enum CRAS_STREAM_DIRECTION direction,
+ struct libcras_node_info ***nodes, size_t *num)
+{
+ struct cras_iodev_info iodevs[CRAS_MAX_IODEVS];
+ struct cras_ionode_info ionodes[CRAS_MAX_IONODES];
+ size_t num_devs = CRAS_MAX_IODEVS, num_nodes = CRAS_MAX_IONODES;
+ int rc, i, j;
+
+ *num = 0;
+ if (direction == CRAS_STREAM_INPUT) {
+ rc = cras_client_get_input_devices(client, iodevs, ionodes,
+ &num_devs, &num_nodes);
+ } else {
+ rc = cras_client_get_output_devices(client, iodevs, ionodes,
+ &num_devs, &num_nodes);
+ }
+
+ if (rc < 0) {
+ syslog(LOG_ERR, "Failed to get devices: %d", rc);
+ return rc;
+ }
+
+ *nodes = (struct libcras_node_info **)calloc(
+ num_nodes, sizeof(struct libcras_node_info *));
+
+ for (i = 0; i < num_devs; i++) {
+ for (j = 0; j < num_nodes; j++) {
+ if (iodevs[i].idx != ionodes[j].iodev_idx)
+ continue;
+ (*nodes)[*num] = libcras_node_info_create(&iodevs[i],
+ &ionodes[j]);
+ if ((*nodes)[*num] == NULL) {
+ rc = -errno;
+ goto clean;
+ }
+ (*num)++;
+ }
+ }
+ return 0;
+clean:
+ for (i = 0; i < *num; i++)
+ libcras_node_info_destroy((*nodes)[i]);
+ free(*nodes);
+ *nodes = NULL;
+ *num = 0;
+ return rc;
+}
+
+int get_default_output_buffer_size(struct cras_client *client, int *size)
+{
+ int rc = cras_client_get_default_output_buffer_size(client);
+ if (rc < 0)
+ return rc;
+ *size = rc;
+ return 0;
+}
+
+int get_aec_group_id(struct cras_client *client, int *id)
+{
+ int rc = cras_client_get_aec_group_id(client);
+ if (rc < 0)
+ return rc;
+ *id = rc;
+ return 0;
+}
+
+int get_aec_supported(struct cras_client *client, int *supported)
+{
+ *supported = cras_client_get_aec_supported(client);
+ return 0;
+}
+
+int get_system_muted(struct cras_client *client, int *muted)
+{
+ *muted = cras_client_get_system_muted(client);
+ return 0;
+}
+
+int get_loopback_dev_idx(struct cras_client *client, int *idx)
+{
+ int rc = cras_client_get_first_dev_type_idx(
+ client, CRAS_NODE_TYPE_POST_MIX_PRE_DSP, CRAS_STREAM_INPUT);
+ if (rc < 0)
+ return rc;
+ *idx = rc;
+ return 0;
+}
+
+struct libcras_client *libcras_client_create()
+{
+ struct libcras_client *client = (struct libcras_client *)calloc(
+ 1, sizeof(struct libcras_client));
+ if (!client) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ return NULL;
+ }
+ if (cras_client_create(&client->client_)) {
+ libcras_client_destroy(client);
+ return NULL;
+ }
+ client->api_version = CRAS_API_VERSION;
+ client->connect = cras_client_connect;
+ client->connect_timeout = cras_client_connect_timeout;
+ client->connected_wait = cras_client_connected_wait;
+ client->run_thread = cras_client_run_thread;
+ client->stop = cras_client_stop;
+ client->add_pinned_stream = cras_client_add_pinned_stream;
+ client->rm_stream = cras_client_rm_stream;
+ client->set_stream_volume = cras_client_set_stream_volume;
+ client->get_nodes = get_nodes;
+ client->get_default_output_buffer_size = get_default_output_buffer_size;
+ client->get_aec_group_id = get_aec_group_id;
+ client->get_aec_supported = get_aec_supported;
+ client->get_system_muted = get_system_muted;
+ client->set_system_mute = cras_client_set_system_mute;
+ client->get_loopback_dev_idx = get_loopback_dev_idx;
+ return client;
+}
+
+void libcras_client_destroy(struct libcras_client *client)
+{
+ cras_client_destroy(client->client_);
+ free(client);
+}
+
+int stream_params_set(struct cras_stream_params *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, 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;
+ params->cb_threshold = cb_threshold;
+ params->stream_type = stream_type;
+ params->client_type = client_type;
+ params->flags = flags;
+ params->user_data = user_data;
+ params->stream_cb = stream_cb;
+ params->err_cb = err_cb;
+ params->format.frame_rate = rate;
+ params->format.format = format;
+ params->format.num_channels = num_channels;
+ return 0;
+}
+
+int stream_params_set_channel_layout(struct cras_stream_params *params,
+ int length, const int8_t *layout)
+{
+ if (length != CRAS_CH_MAX)
+ return -EINVAL;
+ return cras_audio_format_set_channel_layout(&params->format, layout);
+}
+
+struct libcras_stream_params *libcras_stream_params_create()
+{
+ struct libcras_stream_params *params =
+ (struct libcras_stream_params *)calloc(
+ 1, sizeof(struct libcras_stream_params));
+ if (!params) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ return NULL;
+ }
+ params->params_ = (struct cras_stream_params *)calloc(
+ 1, sizeof(struct cras_stream_params));
+ if (params->params_ == NULL) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ free(params->params_);
+ return NULL;
+ }
+ params->api_version = CRAS_API_VERSION;
+ params->set = stream_params_set;
+ params->set_channel_layout = stream_params_set_channel_layout;
+ params->enable_aec = cras_client_stream_params_enable_aec;
+ return params;
+}
+
+void libcras_stream_params_destroy(struct libcras_stream_params *params)
+{
+ free(params->params_);
+ free(params);
+}
+
+struct cras_node_info {
+ uint64_t id;
+ uint32_t dev_idx;
+ uint32_t node_idx;
+ uint32_t max_supported_channels;
+ bool plugged;
+ bool active;
+ char type[CRAS_NODE_TYPE_BUFFER_SIZE];
+ char node_name[CRAS_NODE_NAME_BUFFER_SIZE];
+ char dev_name[CRAS_IODEV_NAME_BUFFER_SIZE];
+};
+
+int cras_node_info_get_id(struct cras_node_info *node, uint64_t *id)
+{
+ (*id) = node->id;
+ return 0;
+}
+
+int cras_node_info_get_dev_idx(struct cras_node_info *node, uint32_t *dev_idx)
+{
+ (*dev_idx) = node->dev_idx;
+ return 0;
+}
+
+int cras_node_info_get_node_idx(struct cras_node_info *node, uint32_t *node_idx)
+{
+ (*node_idx) = node->node_idx;
+ return 0;
+}
+
+int cras_node_info_get_max_supported_channels(struct cras_node_info *node,
+ uint32_t *max_supported_channels)
+{
+ (*max_supported_channels) = node->max_supported_channels;
+ return 0;
+}
+
+int cras_node_info_is_plugged(struct cras_node_info *node, bool *is_plugged)
+{
+ (*is_plugged) = node->plugged;
+ return 0;
+}
+
+int cras_node_info_is_active(struct cras_node_info *node, bool *is_active)
+{
+ (*is_active) = node->active;
+ return 0;
+}
+
+int cras_node_info_get_type(struct cras_node_info *node, char **type)
+{
+ (*type) = node->type;
+ return 0;
+}
+
+int cras_node_info_get_node_name(struct cras_node_info *node, char **node_name)
+{
+ (*node_name) = node->node_name;
+ return 0;
+}
+
+int cras_node_info_get_dev_name(struct cras_node_info *node, char **dev_name)
+{
+ (*dev_name) = node->dev_name;
+ return 0;
+}
+
+struct libcras_node_info *
+libcras_node_info_create(struct cras_iodev_info *iodev,
+ struct cras_ionode_info *ionode)
+{
+ struct libcras_node_info *node = (struct libcras_node_info *)calloc(
+ 1, sizeof(struct libcras_node_info));
+ if (!node) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ return NULL;
+ }
+ node->node_ = (struct cras_node_info *)calloc(
+ 1, sizeof(struct cras_node_info));
+ if (node->node_ == NULL) {
+ syslog(LOG_ERR, "cras_client: calloc failed");
+ free(node);
+ return NULL;
+ }
+ node->api_version = CRAS_API_VERSION;
+ node->node_->id =
+ cras_make_node_id(ionode->iodev_idx, ionode->ionode_idx);
+ node->node_->dev_idx = ionode->iodev_idx;
+ node->node_->node_idx = ionode->ionode_idx;
+ node->node_->max_supported_channels = iodev->max_supported_channels;
+ node->node_->plugged = ionode->plugged;
+ node->node_->active = ionode->active;
+ strncpy(node->node_->type, ionode->type, CRAS_NODE_TYPE_BUFFER_SIZE);
+ node->node_->type[CRAS_NODE_TYPE_BUFFER_SIZE - 1] = '\0';
+ strncpy(node->node_->node_name, ionode->name,
+ CRAS_NODE_NAME_BUFFER_SIZE);
+ node->node_->node_name[CRAS_NODE_NAME_BUFFER_SIZE - 1] = '\0';
+ strncpy(node->node_->dev_name, iodev->name,
+ CRAS_IODEV_NAME_BUFFER_SIZE);
+ node->node_->dev_name[CRAS_IODEV_NAME_BUFFER_SIZE - 1] = '\0';
+ node->get_id = cras_node_info_get_id;
+ node->get_dev_idx = cras_node_info_get_dev_idx;
+ node->get_node_idx = cras_node_info_get_node_idx;
+ node->get_max_supported_channels =
+ cras_node_info_get_max_supported_channels;
+ node->is_plugged = cras_node_info_is_plugged;
+ node->is_active = cras_node_info_is_active;
+ node->get_type = cras_node_info_get_type;
+ node->get_node_name = cras_node_info_get_node_name;
+ node->get_dev_name = cras_node_info_get_dev_name;
+ return node;
+}
+
+void libcras_node_info_destroy(struct libcras_node_info *node)
+{
+ free(node->node_);
+ free(node);
+}
+
+void libcras_node_info_array_destroy(struct libcras_node_info **nodes,
+ size_t num)
+{
+ int i;
+ for (i = 0; i < num; i++)
+ libcras_node_info_destroy(nodes[i]);
+ free(nodes);
+}