summaryrefslogtreecommitdiff
path: root/spa/plugins/alsa/alsa-sink.c
diff options
context:
space:
mode:
Diffstat (limited to 'spa/plugins/alsa/alsa-sink.c')
-rw-r--r--spa/plugins/alsa/alsa-sink.c244
1 files changed, 210 insertions, 34 deletions
diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c
index 4274e444..acc4a12d 100644
--- a/spa/plugins/alsa/alsa-sink.c
+++ b/spa/plugins/alsa/alsa-sink.c
@@ -32,7 +32,7 @@ typedef struct _SpaALSAState SpaALSASink;
static const char default_device[] = "default";
static const uint32_t default_buffer_time = 10000;
-static const uint32_t default_period_time = 5000;
+static const uint32_t default_period_time = 1000;
static const bool default_period_event = 0;
static void
@@ -47,6 +47,7 @@ reset_alsa_sink_props (SpaALSAProps *props)
static void
update_state (SpaALSASink *this, SpaNodeState state)
{
+ spa_log_info (this->log, "update state %d\n", state);
this->node.state = state;
}
@@ -150,6 +151,62 @@ spa_alsa_sink_node_set_props (SpaNode *node,
}
static SpaResult
+do_send_event (SpaPoll *poll,
+ bool async,
+ uint32_t seq,
+ size_t size,
+ void *data,
+ void *user_data)
+{
+ SpaALSASink *this = user_data;
+
+ this->event_cb (&this->node, data, this->user_data);
+
+ return SPA_RESULT_OK;
+}
+
+static SpaResult
+do_command (SpaPoll *poll,
+ bool async,
+ uint32_t seq,
+ size_t size,
+ void *data,
+ void *user_data)
+{
+ SpaALSASink *this = user_data;
+ SpaResult res;
+ SpaNodeCommand *cmd = data;
+ SpaNodeEventAsyncComplete ac;
+
+ switch (cmd->type) {
+ case SPA_NODE_COMMAND_START:
+ case SPA_NODE_COMMAND_PAUSE:
+ res = spa_node_port_send_command (&this->node,
+ SPA_DIRECTION_INPUT,
+ 0,
+ cmd);
+ break;
+ default:
+ res = SPA_RESULT_NOT_IMPLEMENTED;
+ break;
+ }
+
+ if (async) {
+ ac.event.type = SPA_NODE_EVENT_TYPE_ASYNC_COMPLETE;
+ ac.event.size = sizeof (SpaNodeEventAsyncComplete);
+ ac.seq = seq;
+ ac.res = res;
+ spa_poll_invoke (this->main_loop,
+ do_send_event,
+ SPA_ID_INVALID,
+ sizeof (ac),
+ &ac,
+ this);
+ }
+ return res;
+}
+
+static SpaResult
spa_alsa_sink_node_send_command (SpaNode *node,
SpaNodeCommand *command)
{
@@ -165,15 +222,22 @@ spa_alsa_sink_node_send_command (SpaNode *node,
return SPA_RESULT_INVALID_COMMAND;
case SPA_NODE_COMMAND_START:
- spa_alsa_start (this);
-
- update_state (this, SPA_NODE_STATE_STREAMING);
- break;
case SPA_NODE_COMMAND_PAUSE:
- spa_alsa_pause (this);
+ {
+ if (!this->have_format)
+ return SPA_RESULT_NO_FORMAT;
- update_state (this, SPA_NODE_STATE_PAUSED);
- break;
+ if (this->n_buffers == 0)
+ return SPA_RESULT_NO_BUFFERS;
+
+ return spa_poll_invoke (this->data_loop,
+ do_command,
+ ++this->seq,
+ command->size,
+ command,
+ this);
+
+ }
case SPA_NODE_COMMAND_FLUSH:
case SPA_NODE_COMMAND_DRAIN:
case SPA_NODE_COMMAND_MARKER:
@@ -298,6 +362,16 @@ spa_alsa_sink_node_port_enum_formats (SpaNode *node,
}
static SpaResult
+spa_alsa_clear_buffers (SpaALSASink *this)
+{
+ if (this->n_buffers > 0) {
+ SPA_QUEUE_INIT (&this->ready);
+ this->n_buffers = 0;
+ }
+ return SPA_RESULT_OK;
+}
+
+static SpaResult
spa_alsa_sink_node_port_set_format (SpaNode *node,
SpaDirection direction,
uint32_t port_id,
@@ -316,8 +390,11 @@ spa_alsa_sink_node_port_set_format (SpaNode *node,
return SPA_RESULT_INVALID_PORT;
if (format == NULL) {
+ spa_log_info (this->log, "clear format\n");
+ spa_alsa_pause (this, false);
+ spa_alsa_clear_buffers (this);
+ spa_alsa_close (this);
this->have_format = false;
- this->n_buffers = 0;
update_state (this, SPA_NODE_STATE_CONFIGURE);
return SPA_RESULT_OK;
}
@@ -325,22 +402,27 @@ spa_alsa_sink_node_port_set_format (SpaNode *node,
if ((res = spa_format_audio_parse (format, &this->current_format)) < 0)
return res;
- if (spa_alsa_set_format (this, &this->current_format, false) < 0)
+ if (spa_alsa_set_format (this, &this->current_format, flags) < 0)
return SPA_RESULT_ERROR;
- this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS;
- this->info.maxbuffering = -1;
- this->info.latency = -1;
- this->info.n_params = 1;
+ this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS |
+ SPA_PORT_INFO_FLAG_LIVE;
+ this->info.maxbuffering = this->buffer_frames * this->frame_size;
+ this->info.latency = (this->period_frames * SPA_NSEC_PER_SEC) / this->rate;
+ this->info.n_params = 2;
this->info.params = this->params;
this->params[0] = &this->param_buffers.param;
this->param_buffers.param.type = SPA_ALLOC_PARAM_TYPE_BUFFERS;
this->param_buffers.param.size = sizeof (this->param_buffers);
- this->param_buffers.minsize = 1;
+ this->param_buffers.minsize = this->period_frames * this->frame_size;
this->param_buffers.stride = 0;
this->param_buffers.min_buffers = 1;
- this->param_buffers.max_buffers = 8;
+ this->param_buffers.max_buffers = 32;
this->param_buffers.align = 16;
+ this->params[1] = &this->param_meta.param;
+ this->param_meta.param.type = SPA_ALLOC_PARAM_TYPE_META_ENABLE;
+ this->param_meta.param.size = sizeof (this->param_meta);
+ this->param_meta.type = SPA_META_TYPE_HEADER;
this->info.extra = NULL;
this->have_format = true;
@@ -419,7 +501,54 @@ spa_alsa_sink_node_port_use_buffers (SpaNode *node,
SpaBuffer **buffers,
uint32_t n_buffers)
{
- return SPA_RESULT_NOT_IMPLEMENTED;
+ SpaALSASink *this;
+ int i;
+
+ if (node == NULL)
+ return SPA_RESULT_INVALID_ARGUMENTS;
+
+ if (!CHECK_PORT (this, direction, port_id))
+ return SPA_RESULT_INVALID_PORT;
+
+ this = SPA_CONTAINER_OF (node, SpaALSASink, node);
+
+ spa_log_info (this->log, "use buffers %d\n", n_buffers);
+
+ if (!this->have_format)
+ return SPA_RESULT_NO_FORMAT;
+
+ if (n_buffers == 0) {
+ spa_alsa_pause (this, false);
+ spa_alsa_clear_buffers (this);
+ update_state (this, SPA_NODE_STATE_READY);
+ return SPA_RESULT_OK;
+ }
+
+ for (i = 0; i < n_buffers; i++) {
+ SpaALSABuffer *b = &this->buffers[i];
+ b->outbuf = buffers[i];
+ b->outstanding = true;
+
+ b->h = spa_buffer_find_meta (b->outbuf, SPA_META_TYPE_HEADER);
+
+ switch (buffers[i]->datas[0].type) {
+ case SPA_DATA_TYPE_MEMFD:
+ case SPA_DATA_TYPE_DMABUF:
+ case SPA_DATA_TYPE_MEMPTR:
+ if (buffers[i]->datas[0].data == NULL) {
+ spa_log_error (this->log, "alsa-source: need mapped memory\n");
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ this->n_buffers = n_buffers;
+
+ update_state (this, SPA_NODE_STATE_PAUSED);
+
+ return SPA_RESULT_OK;
}
static SpaResult
@@ -475,7 +604,7 @@ spa_alsa_sink_node_port_push_input (SpaNode *node,
{
SpaALSASink *this;
unsigned int i;
- bool have_error = false, have_enough = false;
+ bool have_error = false;
if (node == NULL || n_info == 0 || info == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
@@ -483,32 +612,38 @@ spa_alsa_sink_node_port_push_input (SpaNode *node,
this = SPA_CONTAINER_OF (node, SpaALSASink, node);
for (i = 0; i < n_info; i++) {
+ SpaALSABuffer *b;
+
if (info[i].port_id != 0) {
info[i].status = SPA_RESULT_INVALID_PORT;
have_error = true;
continue;
}
+ if (!this->have_format) {
+ info[i].status = SPA_RESULT_NO_FORMAT;
+ have_error = true;
+ continue;
+ }
- if (info[i].buffer_id != SPA_ID_INVALID) {
- if (!this->have_format) {
- info[i].status = SPA_RESULT_NO_FORMAT;
- have_error = true;
- continue;
- }
+ if (info[i].buffer_id >= this->n_buffers) {
+ info[i].status = SPA_RESULT_INVALID_BUFFER_ID;
+ have_error = true;
+ continue;
+ }
- if (this->ready.length != 0) {
- info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT;
- have_enough = true;
- continue;
- }
- SPA_QUEUE_PUSH_TAIL (&this->ready, SpaALSABuffer, next, &this->buffers[info[i].buffer_id]);
+ b = &this->buffers[info[i].buffer_id];
+ if (!b->outstanding) {
+ info[i].status = SPA_RESULT_INVALID_BUFFER_ID;
+ have_error = true;
+ continue;
}
+ b->outstanding = false;
+ b->next = NULL;
+ SPA_QUEUE_PUSH_TAIL (&this->ready, SpaALSABuffer, next, b);
info[i].status = SPA_RESULT_OK;
}
if (have_error)
return SPA_RESULT_ERROR;
- if (have_enough)
- return SPA_RESULT_HAVE_ENOUGH_INPUT;
return SPA_RESULT_OK;
}
@@ -535,7 +670,37 @@ spa_alsa_sink_node_port_send_command (SpaNode *node,
uint32_t port_id,
SpaNodeCommand *command)
{
- return SPA_RESULT_NOT_IMPLEMENTED;
+ SpaALSASink *this;
+ SpaResult res;
+
+ if (node == NULL)
+ return SPA_RESULT_INVALID_ARGUMENTS;
+
+ this = SPA_CONTAINER_OF (node, SpaALSASink, node);
+
+ if (port_id != 0)
+ return SPA_RESULT_INVALID_PORT;
+
+ switch (command->type) {
+ case SPA_NODE_COMMAND_PAUSE:
+ {
+ if (SPA_RESULT_IS_OK (res = spa_alsa_pause (this, false))) {
+ update_state (this, SPA_NODE_STATE_PAUSED);
+ }
+ break;
+ }
+ case SPA_NODE_COMMAND_START:
+ {
+ if (SPA_RESULT_IS_OK (res = spa_alsa_start (this, false))) {
+ update_state (this, SPA_NODE_STATE_STREAMING);
+ }
+ break;
+ }
+ default:
+ res = SPA_RESULT_NOT_IMPLEMENTED;
+ break;
+ }
+ return res;
}
@@ -615,6 +780,10 @@ alsa_sink_init (const SpaHandleFactory *factory,
this->map = support[i].data;
else if (strcmp (support[i].uri, SPA_LOG_URI) == 0)
this->log = support[i].data;
+ else if (strcmp (support[i].uri, SPA_POLL__DataLoop) == 0)
+ this->data_loop = support[i].data;
+ else if (strcmp (support[i].uri, SPA_POLL__MainLoop) == 0)
+ this->main_loop = support[i].data;
}
if (this->map == NULL) {
spa_log_error (this->log, "an id-map is needed");
@@ -628,7 +797,14 @@ alsa_sink_init (const SpaHandleFactory *factory,
this->stream = SND_PCM_STREAM_PLAYBACK;
reset_alsa_sink_props (&this->props[1]);
- this->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
+ this->status.flags = SPA_PORT_STATUS_FLAG_NONE;
+
+ for (i = 0; info && i < info->n_items; i++) {
+ if (!strcmp (info->items[i].key, "alsa.card")) {
+ snprintf (this->props[1].device, 63, "hw:%s", info->items[i].value);
+ this->props[1].props.unset_mask &= ~1;
+ }
+ }
update_state (this, SPA_NODE_STATE_CONFIGURE);