v4l2: keep separate field for mapped fd

When mapping an input memfd, don't write the mapped pointer into the
buffer but keep a separate field for it.
This commit is contained in:
Wim Taymans 2018-02-12 11:35:49 +01:00
parent 29dbffea52
commit f37f8ce70d
2 changed files with 94 additions and 87 deletions

View file

@ -65,6 +65,7 @@ struct buffer {
struct spa_meta_header *h;
uint32_t flags;
struct v4l2_buffer v4l2_buffer;
void *ptr;
};
struct type {

View file

@ -79,7 +79,7 @@ static int spa_v4l2_open(struct impl *this)
if (xioctl(port->fd, VIDIOC_QUERYCAP, &port->cap) < 0) {
err = errno;
perror("QUERYCAP");
spa_log_error(port->log, "QUERYCAP: %m");
return -err;
}
@ -113,7 +113,7 @@ static int spa_v4l2_buffer_recycle(struct impl *this, uint32_t buffer_id)
if (xioctl(port->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
err = errno;
perror("VIDIOC_QBUF");
spa_log_error(port->log, "VIDIOC_QBUF: %m");
return -err;
}
return 0;
@ -140,13 +140,11 @@ static int spa_v4l2_clear_buffers(struct impl *this)
spa_v4l2_buffer_recycle(this, i);
}
if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_MAPPED)) {
if (d[0].data)
munmap(SPA_MEMBER(d[0].data, -d[0].mapoffset, void),
d[0].maxsize - d[0].mapoffset);
munmap(SPA_MEMBER(b->ptr, -d[0].mapoffset, void),
d[0].maxsize - d[0].mapoffset);
}
if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_ALLOCATED)) {
if (d[0].fd != -1)
close(d[0].fd);
close(d[0].fd);
}
d[0].type = SPA_ID_INVALID;
}
@ -157,7 +155,7 @@ static int spa_v4l2_clear_buffers(struct impl *this)
reqbuf.count = 0;
if (xioctl(port->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
perror("VIDIOC_REQBUFS");
spa_log_warn(port->log, "VIDIOC_REQBUFS: %m");
}
port->n_buffers = 0;
@ -177,7 +175,7 @@ static int spa_v4l2_close(struct impl *this)
spa_log_info(port->log, "v4l2: close");
if (close(port->fd))
perror("close");
spa_log_warn(port->log, "close: %m");
port->fd = -1;
port->opened = false;
@ -581,7 +579,7 @@ spa_v4l2_enum_format(struct impl *this,
if ((res = xioctl(port->fd, VIDIOC_ENUM_FMT, &port->fmtdesc)) < 0) {
res = -errno;
if (errno != EINVAL)
perror("VIDIOC_ENUM_FMT");
spa_log_error(port->log, "VIDIOC_ENUM_FMT: %m");
goto exit;
}
}
@ -625,7 +623,7 @@ spa_v4l2_enum_format(struct impl *this,
goto next_fmtdesc;
res = -errno;
perror("VIDIOC_ENUM_FRAMESIZES");
spa_log_error(port->log, "VIDIOC_ENUM_FRAMESIZES: %m");
goto exit;
}
if (filter) {
@ -715,7 +713,7 @@ spa_v4l2_enum_format(struct impl *this,
goto next_frmsize;
break;
}
perror("VIDIOC_ENUM_FRAMEINTERVALS");
spa_log_error(port->log, "VIDIOC_ENUM_FRAMEINTERVALS: %m");
goto exit;
}
if (filter) {
@ -872,13 +870,13 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
if (xioctl(port->fd, cmd, &fmt) < 0) {
res = -errno;
perror("VIDIOC_S_FMT");
spa_log_error(port->log, "VIDIOC_S_FMT: %m");
return res;
}
/* some cheap USB cam's won't accept any change */
if (xioctl(port->fd, VIDIOC_S_PARM, &streamparm) < 0)
perror("VIDIOC_S_PARM");
spa_log_warn(port->log, "VIDIOC_S_PARM: %m");
spa_log_info(port->log, "v4l2: got %08x %dx%d %d/%d", fmt.fmt.pix.pixelformat,
fmt.fmt.pix.width, fmt.fmt.pix.height,
@ -1027,7 +1025,7 @@ spa_v4l2_enum_controls(struct impl *this,
goto next;
}
res = -errno;
perror("VIDIOC_QUERYCTRL");
spa_log_error(port->log, "VIDIOC_QUERYCTRL: %m");
return res;
}
if (*index & next_fl)
@ -1204,7 +1202,7 @@ static void v4l2_on_fd_events(struct spa_source *source)
static int spa_v4l2_use_buffers(struct impl *this, struct spa_buffer **buffers, uint32_t n_buffers)
{
struct port *state = &this->out_ports[0];
struct port *port = &this->out_ports[0];
struct v4l2_requestbuffers reqbuf;
int i;
struct spa_data *d;
@ -1214,11 +1212,11 @@ static int spa_v4l2_use_buffers(struct impl *this, struct spa_buffer **buffers,
if (d[0].type == this->type.data.MemFd ||
(d[0].type == this->type.data.MemPtr && d[0].data != NULL)) {
state->memtype = V4L2_MEMORY_USERPTR;
port->memtype = V4L2_MEMORY_USERPTR;
} else if (d[0].type == this->type.data.DmaBuf) {
state->memtype = V4L2_MEMORY_DMABUF;
port->memtype = V4L2_MEMORY_DMABUF;
} else {
spa_log_error(state->log, "v4l2: can't use buffers of type %s (%d)",
spa_log_error(port->log, "v4l2: can't use buffers of type %s (%d)",
spa_type_map_get_type (this->map, d[0].type), d[0].type);
return -EINVAL;
}
@ -1226,64 +1224,70 @@ static int spa_v4l2_use_buffers(struct impl *this, struct spa_buffer **buffers,
spa_zero(reqbuf);
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = state->memtype;
reqbuf.memory = port->memtype;
reqbuf.count = n_buffers;
if (xioctl(state->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
spa_log_error(state->log, "v4l2: VIDIOC_REQBUFS %m");
if (xioctl(port->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
spa_log_error(port->log, "v4l2: VIDIOC_REQBUFS %m");
return -errno;
}
spa_log_info(state->log, "v4l2: got %d buffers", reqbuf.count);
spa_log_info(port->log, "v4l2: got %d buffers", reqbuf.count);
if (reqbuf.count < n_buffers) {
spa_log_error(state->log, "v4l2: can't allocate enough buffers");
spa_log_error(port->log, "v4l2: can't allocate enough buffers");
return -ENOMEM;
}
for (i = 0; i < reqbuf.count; i++) {
struct buffer *b;
b = &state->buffers[i];
b = &port->buffers[i];
b->outbuf = buffers[i];
b->flags = BUFFER_FLAG_OUTSTANDING;
b->h = spa_buffer_find_meta(b->outbuf, this->type.meta.Header);
spa_log_info(state->log, "v4l2: import buffer %p", buffers[i]);
spa_log_info(port->log, "v4l2: import buffer %p", buffers[i]);
if (buffers[i]->n_datas < 1) {
spa_log_error(state->log, "v4l2: invalid memory on buffer %p", buffers[i]);
spa_log_error(port->log, "v4l2: invalid memory on buffer %p", buffers[i]);
return -EINVAL;
}
d = buffers[i]->datas;
spa_zero(b->v4l2_buffer);
b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
b->v4l2_buffer.memory = state->memtype;
b->v4l2_buffer.memory = port->memtype;
b->v4l2_buffer.index = i;
if (d[0].type == this->type.data.MemFd && d[0].data == NULL) {
void *data;
data = mmap(NULL,
d[0].maxsize + d[0].mapoffset,
PROT_READ | PROT_WRITE, MAP_SHARED,
d[0].fd,
0);
if (data == MAP_FAILED)
return -errno;
if (port->memtype == V4L2_MEMORY_USERPTR) {
if (d[0].data == NULL) {
void *data;
d[0].data = SPA_MEMBER(data, d[0].mapoffset, void);
data = mmap(NULL,
d[0].maxsize + d[0].mapoffset,
PROT_READ | PROT_WRITE, MAP_SHARED,
d[0].fd,
0);
if (data == MAP_FAILED)
return -errno;
SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
}
b->ptr = SPA_MEMBER(data, d[0].mapoffset, void);
SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
}
else
b->ptr = d[0].data;
if (d[0].type == this->type.data.MemPtr || d[0].type == this->type.data.MemFd) {
b->v4l2_buffer.m.userptr = (unsigned long) d[0].data;
b->v4l2_buffer.m.userptr = (unsigned long) b->ptr;
b->v4l2_buffer.length = d[0].maxsize;
} else if (d[0].type == this->type.data.DmaBuf) {
}
else if (port->memtype == V4L2_MEMORY_DMABUF) {
b->v4l2_buffer.m.fd = d[0].fd;
}
else
return -EIO;
spa_v4l2_buffer_recycle(this, buffers[i]->id);
}
state->n_buffers = reqbuf.count;
port->n_buffers = reqbuf.count;
return 0;
}
@ -1295,53 +1299,53 @@ mmap_init(struct impl *this,
struct spa_buffer **buffers,
uint32_t *n_buffers)
{
struct port *state = &this->out_ports[0];
struct port *port = &this->out_ports[0];
struct v4l2_requestbuffers reqbuf;
int i;
state->memtype = V4L2_MEMORY_MMAP;
port->memtype = V4L2_MEMORY_MMAP;
spa_zero(reqbuf);
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = state->memtype;
reqbuf.memory = port->memtype;
reqbuf.count = *n_buffers;
if (xioctl(state->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
spa_log_error(state->log, "VIDIOC_REQBUFS: %m");
if (xioctl(port->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
spa_log_error(port->log, "VIDIOC_REQBUFS: %m");
return -errno;
}
spa_log_info(state->log, "v4l2: got %d buffers", reqbuf.count);
spa_log_info(port->log, "v4l2: got %d buffers", reqbuf.count);
*n_buffers = reqbuf.count;
if (reqbuf.count < 2) {
spa_log_error(state->log, "v4l2: can't allocate enough buffers");
spa_log_error(port->log, "v4l2: can't allocate enough buffers");
return -ENOMEM;
}
if (state->export_buf)
spa_log_info(state->log, "v4l2: using EXPBUF");
if (port->export_buf)
spa_log_info(port->log, "v4l2: using EXPBUF");
for (i = 0; i < reqbuf.count; i++) {
struct buffer *b;
struct spa_data *d;
if (buffers[i]->n_datas < 1) {
spa_log_error(state->log, "v4l2: invalid buffer data");
spa_log_error(port->log, "v4l2: invalid buffer data");
return -EINVAL;
}
b = &state->buffers[i];
b = &port->buffers[i];
b->outbuf = buffers[i];
b->flags = BUFFER_FLAG_OUTSTANDING | BUFFER_FLAG_ALLOCATED;
b->flags = BUFFER_FLAG_OUTSTANDING;
b->h = spa_buffer_find_meta(b->outbuf, this->type.meta.Header);
spa_zero(b->v4l2_buffer);
b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
b->v4l2_buffer.memory = state->memtype;
b->v4l2_buffer.memory = port->memtype;
b->v4l2_buffer.index = i;
if (xioctl(state->fd, VIDIOC_QUERYBUF, &b->v4l2_buffer) < 0) {
perror("VIDIOC_QUERYBUF");
if (xioctl(port->fd, VIDIOC_QUERYBUF, &b->v4l2_buffer) < 0) {
spa_log_error(port->log, "VIDIOC_QUERYBUF: %m");
return -errno;
}
@ -1350,39 +1354,41 @@ mmap_init(struct impl *this,
d[0].maxsize = b->v4l2_buffer.length;
d[0].chunk->offset = 0;
d[0].chunk->size = 0;
d[0].chunk->stride = state->fmt.fmt.pix.bytesperline;
d[0].chunk->stride = port->fmt.fmt.pix.bytesperline;
if (state->export_buf) {
if (port->export_buf) {
struct v4l2_exportbuffer expbuf;
spa_zero(expbuf);
expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
expbuf.index = i;
expbuf.flags = O_CLOEXEC | O_RDONLY;
if (xioctl(state->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
perror("VIDIOC_EXPBUF");
if (xioctl(port->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
spa_log_error(port->log, "VIDIOC_EXPBUF: %m");
continue;
}
d[0].type = this->type.data.DmaBuf;
d[0].fd = expbuf.fd;
d[0].data = NULL;
SPA_FLAG_SET(b->flags, BUFFER_FLAG_ALLOCATED);
} else {
d[0].type = this->type.data.MemPtr;
d[0].fd = -1;
d[0].data = mmap(NULL,
b->v4l2_buffer.length,
PROT_READ, MAP_SHARED,
state->fd,
port->fd,
b->v4l2_buffer.m.offset);
if (d[0].data == MAP_FAILED) {
perror("mmap");
spa_log_error(port->log, "mmap: %m");
continue;
}
b->ptr = d[0].data;
SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED);
}
spa_v4l2_buffer_recycle(this, i);
}
state->n_buffers = reqbuf.count;
port->n_buffers = reqbuf.count;
return 0;
}
@ -1405,16 +1411,16 @@ spa_v4l2_alloc_buffers(struct impl *this,
uint32_t *n_buffers)
{
int res;
struct port *state = &this->out_ports[0];
struct port *port = &this->out_ports[0];
if (state->n_buffers > 0)
if (port->n_buffers > 0)
return -EIO;
if (state->cap.capabilities & V4L2_CAP_STREAMING) {
if (port->cap.capabilities & V4L2_CAP_STREAMING) {
if ((res = mmap_init(this, params, n_params, buffers, n_buffers)) < 0)
if ((res = userptr_init(this)) < 0)
return res;
} else if (state->cap.capabilities & V4L2_CAP_READWRITE) {
} else if (port->cap.capabilities & V4L2_CAP_READWRITE) {
if ((res = read_init(this)) < 0)
return res;
} else
@ -1425,26 +1431,26 @@ spa_v4l2_alloc_buffers(struct impl *this,
static int spa_v4l2_stream_on(struct impl *this)
{
struct port *state = &this->out_ports[0];
struct port *port = &this->out_ports[0];
enum v4l2_buf_type type;
if (!state->opened)
if (!port->opened)
return -EIO;
if (state->started)
if (port->started)
return 0;
spa_log_debug(this->log, "starting");
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(state->fd, VIDIOC_STREAMON, &type) < 0) {
if (xioctl(port->fd, VIDIOC_STREAMON, &type) < 0) {
spa_log_error(this->log, "VIDIOC_STREAMON: %m");
return -errno;
}
spa_loop_add_source(state->data_loop, &state->source);
spa_loop_add_source(port->data_loop, &port->source);
state->started = true;
port->started = true;
return 0;
}
@ -1456,41 +1462,41 @@ static int do_remove_source(struct spa_loop *loop,
size_t size,
void *user_data)
{
struct port *state = user_data;
spa_loop_remove_source(state->data_loop, &state->source);
struct port *port = user_data;
spa_loop_remove_source(port->data_loop, &port->source);
return 0;
}
static int spa_v4l2_stream_off(struct impl *this)
{
struct port *state = &this->out_ports[0];
struct port *port = &this->out_ports[0];
enum v4l2_buf_type type;
int i;
if (!state->opened)
if (!port->opened)
return -EIO;
if (!state->started)
if (!port->started)
return 0;
spa_log_debug(this->log, "stopping");
spa_loop_invoke(state->data_loop, do_remove_source, 0, NULL, 0, true, state);
spa_loop_invoke(port->data_loop, do_remove_source, 0, NULL, 0, true, port);
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(state->fd, VIDIOC_STREAMOFF, &type) < 0) {
if (xioctl(port->fd, VIDIOC_STREAMOFF, &type) < 0) {
spa_log_error(this->log, "VIDIOC_STREAMOFF: %m");
return -errno;
}
for (i = 0; i < state->n_buffers; i++) {
for (i = 0; i < port->n_buffers; i++) {
struct buffer *b;
b = &state->buffers[i];
b = &port->buffers[i];
if (!SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUTSTANDING))
if (xioctl(state->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0)
if (xioctl(port->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0)
spa_log_warn(this->log, "VIDIOC_QBUF: %s", strerror(errno));
}
state->started = false;
port->started = false;
return 0;
}