Improve state handling

Improve the state handling in v4l2
Send buffers in one message
Update design doc
This commit is contained in:
Wim Taymans 2016-08-25 17:07:40 +02:00
parent fbd6304663
commit 7e858ff694
8 changed files with 202 additions and 453 deletions

View file

@ -626,21 +626,14 @@ spa_proxy_node_port_get_status (SpaNode *node,
}
static SpaResult
add_buffer (SpaProxy *this, uint32_t port_id, SpaBuffer *buffer)
add_buffer (SpaProxy *this, SpaControlBuilder *builder, uint32_t port_id, SpaBuffer *buffer)
{
SpaControl control;
SpaControlBuilder builder;
uint8_t buf[1024];
int fds[16];
SpaControlCmdAddMem am;
SpaControlCmdAddBuffer ab;
int i;
SpaResult res;
SpaBuffer *b;
SpaMemory *bmem;
spa_control_builder_init_into (&builder, buf, sizeof (buf), fds, sizeof (fds));
if (buffer->mem.mem.id == SPA_ID_INVALID) {
fprintf (stderr, "proxy %p: alloc buffer space %zd\n", this, buffer->mem.size);
bmem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, buffer, buffer->mem.size);
@ -655,10 +648,10 @@ add_buffer (SpaProxy *this, uint32_t port_id, SpaBuffer *buffer)
am.port_id = port_id;
am.mem = bmem->mem;
am.mem_type = 0;
am.fd_index = spa_control_builder_add_fd (&builder, bmem->fd, false);
am.fd_index = spa_control_builder_add_fd (builder, bmem->fd, false);
am.flags = bmem->flags;
am.size = bmem->size;
spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_ADD_MEM, &am);
spa_control_builder_add_cmd (builder, SPA_CONTROL_CMD_ADD_MEM, &am);
for (i = 0; i < b->n_datas; i++) {
SpaData *d = &SPA_BUFFER_DATAS (b)[i];
@ -672,60 +665,42 @@ add_buffer (SpaProxy *this, uint32_t port_id, SpaBuffer *buffer)
am.port_id = port_id;
am.mem = mem->mem;
am.mem_type = 0;
am.fd_index = spa_control_builder_add_fd (&builder, mem->fd, false);
am.fd_index = spa_control_builder_add_fd (builder, mem->fd, false);
am.flags = mem->flags;
am.size = mem->size;
spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_ADD_MEM, &am);
spa_control_builder_add_cmd (builder, SPA_CONTROL_CMD_ADD_MEM, &am);
}
ab.port_id = port_id;
ab.buffer_id = b->id;
ab.mem.mem = bmem->mem;
ab.mem.offset = b->mem.offset;
ab.mem.size = b->mem.size;
spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_ADD_BUFFER, &ab);
spa_control_builder_end (&builder, &control);
if ((res = spa_control_write (&control, this->fds[0].fd)) < 0)
fprintf (stderr, "proxy %p: error writing control\n", this);
spa_control_clear (&control);
spa_control_builder_add_cmd (builder, SPA_CONTROL_CMD_ADD_BUFFER, &ab);
return SPA_RESULT_OK;
}
static SpaResult
remove_buffer (SpaProxy *this, uint32_t port_id, SpaBuffer *buffer)
remove_buffer (SpaProxy *this, SpaControlBuilder *builder, uint32_t port_id, SpaBuffer *buffer)
{
SpaControl control;
SpaControlBuilder builder;
uint8_t buf[1024];
SpaControlCmdRemoveBuffer rb;
SpaControlCmdRemoveMem rm;
unsigned int i;
SpaResult res;
spa_control_builder_init_into (&builder, buf, sizeof (buf), NULL, 0);
rb.port_id = port_id;
rb.buffer_id = buffer->id;
spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_REMOVE_BUFFER, &rb);
spa_control_builder_add_cmd (builder, SPA_CONTROL_CMD_REMOVE_BUFFER, &rb);
rm.port_id = port_id;
rm.mem = buffer->mem.mem;
spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_REMOVE_MEM, &rm);
spa_control_builder_add_cmd (builder, SPA_CONTROL_CMD_REMOVE_MEM, &rm);
for (i = 0; i < buffer->n_datas; i++) {
SpaData *d = &SPA_BUFFER_DATAS (buffer)[i];
rm.port_id = port_id;
rm.mem = d->mem.mem;
spa_control_builder_add_cmd (&builder, SPA_CONTROL_CMD_REMOVE_MEM, &rm);
spa_control_builder_add_cmd (builder, SPA_CONTROL_CMD_REMOVE_MEM, &rm);
}
spa_control_builder_end (&builder, &control);
if ((res = spa_control_write (&control, this->fds[0].fd)) < 0)
fprintf (stderr, "proxy %p: error writing control\n", this);
spa_control_clear (&control);
return SPA_RESULT_OK;
}
@ -740,6 +715,11 @@ spa_proxy_node_port_use_buffers (SpaNode *node,
SpaProxy *this;
SpaProxyPort *port;
unsigned int i;
SpaControl control;
SpaControlBuilder builder;
uint8_t buf[4096];
int fds[32];
SpaResult res;
if (node == NULL || node->handle == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
@ -755,8 +735,10 @@ spa_proxy_node_port_use_buffers (SpaNode *node,
if (!port->format)
return SPA_RESULT_NO_FORMAT;
spa_control_builder_init_into (&builder, buf, sizeof (buf), fds, sizeof (fds));
for (i = 0; i < port->n_buffers; i++)
remove_buffer (this, port_id, port->buffers[i]);
remove_buffer (this, &builder, port_id, port->buffers[i]);
if (buffers == NULL || n_buffers == 0) {
port->buffers = NULL;
@ -766,7 +748,14 @@ spa_proxy_node_port_use_buffers (SpaNode *node,
port->n_buffers = n_buffers;
}
for (i = 0; i < port->n_buffers; i++)
add_buffer (this, port_id, port->buffers[i]);
add_buffer (this, &builder, port_id, port->buffers[i]);
spa_control_builder_end (&builder, &control);
if ((res = spa_control_write (&control, this->fds[0].fd)) < 0)
fprintf (stderr, "proxy %p: error writing control\n", this);
spa_control_clear (&control);
return SPA_RESULT_OK;
}

View file

@ -119,6 +119,7 @@ typedef struct {
struct _SpaV4l2Source {
SpaHandle handle;
SpaNode node;
SpaNodeState node_state;
SpaV4l2SourceProps props[2];
@ -201,11 +202,16 @@ spa_v4l2_source_node_set_props (SpaNode *node,
}
static void
send_state_change (SpaV4l2Source *this, SpaNodeState state)
update_state (SpaV4l2Source *this, SpaNodeState state)
{
SpaEvent event;
SpaEventStateChange sc;
if (this->node_state == state)
return;
this->node_state = state;
event.type = SPA_EVENT_TYPE_STATE_CHANGE;
event.port_id = -1;
event.data = &sc;
@ -219,6 +225,7 @@ spa_v4l2_source_node_send_command (SpaNode *node,
SpaCommand *command)
{
SpaV4l2Source *this;
SpaResult res;
if (node == NULL || node->handle == NULL || command == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
@ -230,15 +237,37 @@ spa_v4l2_source_node_send_command (SpaNode *node,
return SPA_RESULT_INVALID_COMMAND;
case SPA_COMMAND_START:
spa_v4l2_start (this);
{
SpaV4l2State *state = &this->state[0];
send_state_change (this, SPA_NODE_STATE_STREAMING);
if (state->current_format == NULL)
return SPA_RESULT_NO_FORMAT;
if (!state->have_buffers)
return SPA_RESULT_NO_BUFFERS;
if ((res = spa_v4l2_start (this)) < 0)
return res;
update_state (this, SPA_NODE_STATE_STREAMING);
break;
}
case SPA_COMMAND_PAUSE:
spa_v4l2_stop (this);
{
SpaV4l2State *state = &this->state[0];
send_state_change (this, SPA_NODE_STATE_PAUSED);
if (state->current_format == NULL)
return SPA_RESULT_NO_FORMAT;
if (!state->have_buffers)
return SPA_RESULT_NO_BUFFERS;
if ((res = spa_v4l2_pause (this)) < 0)
return res;
update_state (this, SPA_NODE_STATE_PAUSED);
break;
}
case SPA_COMMAND_FLUSH:
case SPA_COMMAND_DRAIN:
@ -263,7 +292,7 @@ spa_v4l2_source_node_set_event_callback (SpaNode *node,
this->event_cb = event;
this->user_data = user_data;
send_state_change (this, SPA_NODE_STATE_CONFIGURE);
update_state (this, SPA_NODE_STATE_CONFIGURE);
return SPA_RESULT_OK;
}
@ -385,6 +414,8 @@ spa_v4l2_source_node_port_set_format (SpaNode *node,
state = &this->state[port_id];
if (format == NULL) {
spa_v4l2_clear_buffers (this);
spa_v4l2_close (this);
state->current_format = NULL;
return SPA_RESULT_OK;
}
@ -410,6 +441,11 @@ spa_v4l2_source_node_port_set_format (SpaNode *node,
f->size.width == state->current_format->size.width &&
f->size.height == state->current_format->size.height)
return SPA_RESULT_OK;
if (!(flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY)) {
spa_v4l2_use_buffers (this, NULL, 0);
state->current_format = NULL;
}
}
if (spa_v4l2_set_format (this, f, flags & SPA_PORT_FORMAT_FLAG_TEST_ONLY) < 0)
@ -419,7 +455,7 @@ spa_v4l2_source_node_port_set_format (SpaNode *node,
memcpy (tf, f, fs);
state->current_format = tf;
send_state_change (this, SPA_NODE_STATE_READY);
update_state (this, SPA_NODE_STATE_READY);
}
return SPA_RESULT_OK;
@ -494,6 +530,8 @@ spa_v4l2_source_node_port_use_buffers (SpaNode *node,
uint32_t n_buffers)
{
SpaV4l2Source *this;
SpaV4l2State *state;
SpaResult res;
if (node == NULL || node->handle == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
@ -503,9 +541,26 @@ spa_v4l2_source_node_port_use_buffers (SpaNode *node,
if (port_id != 0)
return SPA_RESULT_INVALID_PORT;
spa_v4l2_import_buffers (this, buffers, n_buffers);
state = &this->state[port_id];
return SPA_RESULT_OK;
if (state->current_format == NULL)
return SPA_RESULT_NO_FORMAT;
if (state->have_buffers) {
if ((res = spa_v4l2_clear_buffers (this)) < 0)
return res;
}
if (buffers != NULL) {
if ((res = spa_v4l2_use_buffers (this, buffers, n_buffers)) < 0)
return res;
}
if (state->have_buffers)
update_state (this, SPA_NODE_STATE_PAUSED);
else
update_state (this, SPA_NODE_STATE_READY);
return res;
}
static SpaResult
@ -517,6 +572,8 @@ spa_v4l2_source_node_port_alloc_buffers (SpaNode *node,
unsigned int *n_buffers)
{
SpaV4l2Source *this;
SpaV4l2State *state;
SpaResult res;
if (node == NULL || node->handle == NULL || buffers == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
@ -526,9 +583,17 @@ spa_v4l2_source_node_port_alloc_buffers (SpaNode *node,
if (port_id != 0)
return SPA_RESULT_INVALID_PORT;
spa_v4l2_alloc_buffers (this, params, n_params, buffers, n_buffers);
state = &this->state[port_id];
return SPA_RESULT_OK;
if (state->current_format == NULL)
return SPA_RESULT_NO_FORMAT;
res = spa_v4l2_alloc_buffers (this, params, n_params, buffers, n_buffers);
if (state->have_buffers)
update_state (this, SPA_NODE_STATE_PAUSED);
return res;
}
static SpaResult
@ -537,6 +602,8 @@ spa_v4l2_source_node_port_reuse_buffer (SpaNode *node,
uint32_t buffer_id)
{
SpaV4l2Source *this;
SpaV4l2State *state;
SpaResult res;
if (node == NULL || node->handle == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
@ -546,9 +613,17 @@ spa_v4l2_source_node_port_reuse_buffer (SpaNode *node,
if (port_id != 0)
return SPA_RESULT_INVALID_PORT;
spa_v4l2_buffer_recycle (this, buffer_id);
state = &this->state[port_id];
return SPA_RESULT_OK;
if (!state->have_buffers)
return SPA_RESULT_NO_BUFFERS;
if (buffer_id >= state->reqbuf.count)
return SPA_RESULT_INVALID_BUFFER_ID;
res = spa_v4l2_buffer_recycle (this, buffer_id);
return res;
}
static SpaResult
@ -710,6 +785,8 @@ v4l2_source_init (const SpaHandleFactory *factory,
this->state[0].export_buf = true;
this->node_state = SPA_NODE_STATE_INIT;
return SPA_RESULT_OK;
}

View file

@ -66,6 +66,57 @@ spa_v4l2_open (SpaV4l2Source *this)
return 0;
}
static SpaResult
spa_v4l2_buffer_recycle (SpaV4l2Source *this, uint32_t buffer_id)
{
SpaV4l2State *state = &this->state[0];
V4l2Buffer *b = &state->alloc_buffers[buffer_id];
if (!b->outstanding)
return SPA_RESULT_OK;
b->outstanding = false;
if (xioctl (state->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
perror ("VIDIOC_QBUF");
}
return SPA_RESULT_OK;
}
static SpaResult
spa_v4l2_clear_buffers (SpaV4l2Source *this)
{
SpaV4l2State *state = &this->state[0];
int i;
if (!state->have_buffers)
return SPA_RESULT_OK;
for (i = 0; i < state->reqbuf.count; i++) {
V4l2Buffer *b;
SpaMemory *mem;
b = &state->alloc_buffers[i];
if (b->outstanding) {
fprintf (stderr, "queueing outstanding buffer %p\n", b);
spa_v4l2_buffer_recycle (this, i);
}
mem = spa_memory_find (&b->datas[0].mem.mem);
if (state->export_buf) {
close (mem->fd);
} else {
munmap (mem->ptr, mem->size);
}
spa_memory_unref (&mem->mem);
}
if (state->alloc_mem)
spa_memory_unref (&state->alloc_mem->mem);
state->have_buffers = false;
return SPA_RESULT_OK;
}
static int
spa_v4l2_close (SpaV4l2Source *this)
{
@ -74,6 +125,9 @@ spa_v4l2_close (SpaV4l2Source *this)
if (!state->opened)
return 0;
if (state->have_buffers)
return 0;
fprintf (stderr, "close\n");
if (close(state->fd))
perror ("close");
@ -491,66 +545,13 @@ v4l2_on_fd_events (SpaPollNotifyData *data)
return 0;
}
static void
spa_v4l2_buffer_recycle (SpaV4l2Source *this, uint32_t buffer_id)
{
SpaV4l2State *state = &this->state[0];
V4l2Buffer *b = &state->alloc_buffers[buffer_id];
if (!b->outstanding)
return;
b->outstanding = false;
if (xioctl (state->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
perror ("VIDIOC_QBUF");
}
}
static void
clear_buffers (SpaV4l2Source *this)
{
SpaV4l2State *state = &this->state[0];
int i;
if (!state->have_buffers)
return;
for (i = 0; i < state->reqbuf.count; i++) {
V4l2Buffer *b;
SpaMemory *mem;
b = &state->alloc_buffers[i];
if (b->outstanding) {
fprintf (stderr, "queueing outstanding buffer %p\n", b);
spa_v4l2_buffer_recycle (this, i);
}
mem = spa_memory_find (&b->datas[0].mem.mem);
if (state->export_buf) {
close (mem->fd);
} else {
munmap (mem->ptr, mem->size);
}
spa_memory_unref (&mem->mem);
}
if (state->alloc_mem)
spa_memory_unref (&state->alloc_mem->mem);
state->have_buffers = false;
}
static SpaResult
spa_v4l2_import_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffers)
spa_v4l2_use_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffers)
{
SpaV4l2State *state = &this->state[0];
struct v4l2_requestbuffers reqbuf;
int i;
if (buffers == NULL) {
clear_buffers (this);
return SPA_RESULT_OK;
}
state->memtype = V4L2_MEMORY_USERPTR;
CLEAR(reqbuf);
@ -797,11 +798,11 @@ spa_v4l2_start (SpaV4l2Source *this)
enum v4l2_buf_type type;
SpaEvent event;
if (spa_v4l2_open (this) < 0)
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl (state->fd, VIDIOC_STREAMON, &type) < 0) {
perror ("VIDIOC_STREAMON");
return SPA_RESULT_ERROR;
if (!state->have_buffers)
return SPA_RESULT_NO_BUFFERS;
}
event.type = SPA_EVENT_TYPE_ADD_POLL;
event.port_id = 0;
@ -821,39 +822,27 @@ spa_v4l2_start (SpaV4l2Source *this)
state->poll.user_data = this;
this->event_cb (&this->node, &event, this->user_data);
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl (state->fd, VIDIOC_STREAMON, &type) < 0) {
perror ("VIDIOC_STREAMON");
return SPA_RESULT_ERROR;
}
return SPA_RESULT_OK;
}
static SpaResult
spa_v4l2_stop (SpaV4l2Source *this)
spa_v4l2_pause (SpaV4l2Source *this)
{
SpaV4l2State *state = &this->state[0];
enum v4l2_buf_type type;
SpaEvent event;
if (!state->opened)
return SPA_RESULT_OK;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl (state->fd, VIDIOC_STREAMOFF, &type) < 0) {
perror ("VIDIOC_STREAMOFF");
return SPA_RESULT_ERROR;
}
clear_buffers (this);
event.type = SPA_EVENT_TYPE_REMOVE_POLL;
event.port_id = 0;
event.data = &state->poll;
event.size = sizeof (state->poll);
this->event_cb (&this->node, &event, this->user_data);
spa_v4l2_close (this);
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl (state->fd, VIDIOC_STREAMOFF, &type) < 0) {
perror ("VIDIOC_STREAMOFF");
return SPA_RESULT_ERROR;
}
return SPA_RESULT_OK;
}