From c7f0d7684ac67a5e4e0bc617fe7266e910117265 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Tue, 24 Apr 2018 15:38:23 +0200 Subject: [PATCH] Introduce and use dedicated v4l2 helpers to replace inline ioctls Signed-off-by: Paul Kocialkowski --- src/buffer.c | 50 +++++----- src/config.c | 27 ++---- src/context.c | 66 +++++-------- src/picture.c | 71 +++++--------- src/surface.c | 77 +++------------- src/v4l2.c | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/v4l2.h | 46 ++++++++++ 7 files changed, 385 insertions(+), 201 deletions(-) create mode 100644 src/v4l2.c create mode 100644 src/v4l2.h diff --git a/src/buffer.c b/src/buffer.c index b186a02..a3980f8 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -36,19 +36,22 @@ #include +#include "v4l2.h" + VAStatus SunxiCedrusCreateBuffer(VADriverContextP context, VAContextID context_id, VABufferType type, unsigned int size, unsigned int count, void *data, VABufferID *buffer_id) { struct sunxi_cedrus_driver_data *driver_data = (struct sunxi_cedrus_driver_data *) context->pDriverData; + struct object_buffer *buffer_object = NULL; struct object_context *context_object; - struct object_buffer *buffer_object; - struct v4l2_plane plane[1]; - struct v4l2_buffer buf; void *buffer_data; void *map_data; unsigned int map_size; + unsigned int length; + unsigned int offset; + VAStatus status; VABufferID id; int rc; @@ -59,40 +62,35 @@ VAStatus SunxiCedrusCreateBuffer(VADriverContextP context, case VASliceDataBufferType: case VAImageBufferType: break; + default: - return VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE; + status = VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE; + goto error; } id = object_heap_allocate(&driver_data->buffer_heap); buffer_object = BUFFER(buffer_id); - if (buffer_object == NULL) - return VA_STATUS_ERROR_ALLOCATION_FAILED; + if (buffer_object == NULL) { + status = VA_STATUS_ERROR_ALLOCATION_FAILED; + goto error; + } if(buffer_object->type == VASliceDataBufferType) { context_object = CONTEXT(context_id); if (context_object == NULL) { - object_heap_free(&driver_data->buffer_heap, (struct object_base *) buffer_object); - return VA_STATUS_ERROR_INVALID_CONTEXT; + status = VA_STATUS_ERROR_INVALID_CONTEXT; + goto error; } - memset(plane, 0, sizeof(plane)); - - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = context_object->num_rendered_surfaces % INPUT_BUFFERS_NB; - buf.length = 1; - buf.m.planes = plane; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_QUERYBUF, &buf); + rc = v4l2_request_buffer(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, context_object->num_rendered_surfaces % INPUT_BUFFERS_NB, &length, &offset); if (rc < 0) { - object_heap_free(&driver_data->buffer_heap, (struct object_base *) buffer_object); - return VA_STATUS_ERROR_ALLOCATION_FAILED; + status = VA_STATUS_ERROR_ALLOCATION_FAILED; + goto error; } map_size = driver_data->slice_offset[buf.index] + size * count; map_data = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, - driver_data->mem2mem_fd, buf.m.planes[0].m.mem_offset); + driver_data->mem2mem_fd, offset); buffer_data = map_data + driver_data->slice_offset[buf.index]; driver_data->slice_offset[buf.index] += size * count; @@ -102,9 +100,6 @@ VAStatus SunxiCedrusCreateBuffer(VADriverContextP context, map_data = NULL; } - if (buffer_object->data == NULL || buffer_object->map == MAP_FAILED) - return VA_STATUS_ERROR_ALLOCATION_FAILED; - if (data) memcpy(map_data, data, size * count); @@ -120,6 +115,13 @@ VAStatus SunxiCedrusCreateBuffer(VADriverContextP context, *buffer_id = id; return VA_STATUS_SUCCESS; + +error: + if (buffer_object != NULL) + object_heap_free(&driver_data->buffer_heap, (struct object_base *) buffer_object); + +complete: + return status; } VAStatus SunxiCedrusDestroyBuffer(VADriverContextP context, diff --git a/src/config.c b/src/config.c index aded3d3..e706dcd 100644 --- a/src/config.c +++ b/src/config.c @@ -33,6 +33,8 @@ #include +#include "v4l2.h" + VAStatus SunxiCedrusCreateConfig(VADriverContextP context, VAProfile profile, VAEntrypoint entrypoint, VAConfigAttrib *attributes, int attributes_count, VAConfigID *config_id) @@ -100,28 +102,13 @@ VAStatus SunxiCedrusQueryConfigProfiles(VADriverContextP context, { struct sunxi_cedrus_driver_data *driver_data = (struct sunxi_cedrus_driver_data *) context->pDriverData; - struct v4l2_fmtdesc format_description; unsigned int index = 0; - int rc; + bool found; - memset(&format_description, 0, sizeof(format_description)); - format_description.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - format_description.index = 0; - - for (format_description.index = 0; index < SUNXI_CEDRUS_MAX_CONFIG_ATTRIBUTES ; format_description.index++) { - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_ENUM_FMT, &format_description); - if (rc < 0) - break; - - switch (format_description.pixelformat) { - case V4L2_PIX_FMT_MPEG2_FRAME: - if (index >= (SUNXI_CEDRUS_MAX_CONFIG_ATTRIBUTES - 2)) - break; - - profiles[index++] = VAProfileMPEG2Simple; - profiles[index++] = VAProfileMPEG2Main; - break; - } + found = v4l2_find_format(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_PIX_FMT_MPEG2_FRAME); + if (found && index < (SUNXI_CEDRUS_MAX_CONFIG_ATTRIBUTES - 2)) { + profiles[index++] = VAProfileMPEG2Simple; + profiles[index++] = VAProfileMPEG2Main; } *profiles_count = index; diff --git a/src/context.c b/src/context.c index 15a677f..9996bbc 100644 --- a/src/context.c +++ b/src/context.c @@ -37,6 +37,8 @@ #include +#include "v4l2.h" + VAStatus SunxiCedrusCreateContext(VADriverContextP context, VAConfigID config_id, int picture_width, int picture_height, int flag, VASurfaceID *surfaces_ids, int surfaces_count, @@ -49,9 +51,7 @@ VAStatus SunxiCedrusCreateContext(VADriverContextP context, VASurfaceID *ids = NULL; VAContextID id; VAStatus status; - struct v4l2_create_buffers create_buffers; - struct v4l2_format format; - enum v4l2_buf_type type; + unsigned int pixelformat; unsigned int i; config_object = CONFIG(config_id); @@ -83,58 +83,37 @@ VAStatus SunxiCedrusCreateContext(VADriverContextP context, ids[i] = surfaces_ids[i]; } - memset(&format, 0, sizeof(format)); - format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - format.fmt.pix_mp.width = picture_width; - format.fmt.pix_mp.height = picture_height; - format.fmt.pix_mp.plane_fmt[0].sizeimage = INPUT_BUFFER_MAX_SIZE * INPUT_BUFFERS_NB; - format.fmt.pix_mp.field = V4L2_FIELD_ANY; - format.fmt.pix_mp.num_planes = 1; - switch (config_object->profile) { case VAProfileMPEG2Simple: case VAProfileMPEG2Main: - format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_MPEG2_FRAME; + pixelformat = V4L2_PIX_FMT_MPEG2_FRAME; break; + default: - status = VA_STATUS_ERROR_UNSUPPORTED_PROFILE; - goto error; + return VA_STATUS_ERROR_UNSUPPORTED_PROFILE; } - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_S_FMT, &format); + rc = v4l2_set_format(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, pixelformat, picture_width, picture_height); + if (rc < 0) { + status = VA_STATUS_ERROR_OPERATION_FAILED; + goto error; + } + + rc = v4l2_create_buffers(video_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, INPUT_BUFFERS_NB); if (rc < 0) { status = VA_STATUS_ERROR_ALLOCATION_FAILED; goto error; } - memset(&create_buffers, 0, sizeof(create_buffers)); - create_buffers.count = INPUT_BUFFERS_NB; - create_buffers.memory = V4L2_MEMORY_MMAP; - create_buffers.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_G_FMT, &create_buffers.format); + rc = v4l2_set_stream(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, true); if (rc < 0) { - status = VA_STATUS_ERROR_ALLOCATION_FAILED; + status = VA_STATUS_ERROR_OPERATION_FAILED; goto error; } - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_CREATE_BUFS, &create_buffers); + rc = v4l2_set_stream(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, true); if (rc < 0) { - status = VA_STATUS_ERROR_ALLOCATION_FAILED; - goto error; - } - - type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_STREAMON, &type); - if (rc < 0) { - status = VA_STATUS_ERROR_ALLOCATION_FAILED; - goto error; - } - - type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_STREAMON, &type); - if (rc < 0) { - status = VA_STATUS_ERROR_ALLOCATION_FAILED; + status = VA_STATUS_ERROR_OPERATION_FAILED; goto error; } @@ -163,7 +142,6 @@ VAStatus SunxiCedrusDestroyContext(VADriverContextP context, struct sunxi_cedrus_driver_data *driver_data = (struct sunxi_cedrus_driver_data *) context->pDriverData; struct object_context *context_object; - enum v4l2_buf_type type; context_object = (struct object_context *) object_heap_lookup(&driver_data->context_heap, context_id); if (context_object == NULL) @@ -171,15 +149,13 @@ VAStatus SunxiCedrusDestroyContext(VADriverContextP context, object_heap_free(&driver_data->context_heap, (struct object_base *) context_object); - type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_STREAMOFF, &type); + rc = v4l2_set_stream(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, false); if (rc < 0) - return VA_STATUS_ERROR_UNKNOWN; + return VA_STATUS_ERROR_OPERATION_FAILED; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_STREAMOFF, &type); + rc = v4l2_set_stream(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, false); if (rc < 0) - return VA_STATUS_ERROR_UNKNOWN; + return VA_STATUS_ERROR_OPERATION_FAILED; return VA_STATUS_SUCCESS; } diff --git a/src/picture.c b/src/picture.c index 869ab51..a92e753 100644 --- a/src/picture.c +++ b/src/picture.c @@ -41,6 +41,8 @@ #include +#include "v4l2.h" + VAStatus SunxiCedrusBeginPicture(VADriverContextP context, VAContextID context_id, VASurfaceID surface_id) { @@ -130,12 +132,10 @@ VAStatus SunxiCedrusEndPicture(VADriverContextP context, (struct sunxi_cedrus_driver_data *) context->pDriverData; struct object_context *context_object; struct object_surface *surface_object; - struct v4l2_buffer out_buf, cap_buf; - struct v4l2_plane plane[1]; - struct v4l2_plane planes[2]; - struct v4l2_ext_control ctrl; - struct v4l2_ext_controls ctrls; struct media_request_new media_request; + void *control_data; + unsigned int control_size; + unsigned int control_id; int request_fd; int rc; @@ -157,64 +157,35 @@ VAStatus SunxiCedrusEndPicture(VADriverContextP context, request_fd = media_request.fd; } - memset(plane, 0, sizeof(struct v4l2_plane)); - memset(planes, 0, 2 * sizeof(struct v4l2_plane)); - memset(&ctrl, 0, sizeof(struct v4l2_ext_control)); - memset(&ctrls, 0, sizeof(struct v4l2_ext_controls)); - - memset(&out_buf, 0, sizeof(out_buf)); - out_buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - out_buf.memory = V4L2_MEMORY_MMAP; - out_buf.index = surface_object->input_buf_index; - out_buf.length = 1; - out_buf.m.planes = plane; - out_buf.request_fd = request_fd; - switch (config_object->profile) { case VAProfileMPEG2Simple: case VAProfileMPEG2Main: context_object->mpeg2_frame_hdr.slice_pos = 0; context_object->mpeg2_frame_hdr.slice_len = driver_data->slice_offset[surface_object->input_buf_index] * 8; - out_buf.m.planes[0].bytesused = driver_data->slice_offset[surface_object->input_buf_index]; - ctrl.id = V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR; - ctrl.ptr = &context_object->mpeg2_frame_hdr; - ctrl.size = sizeof(context_object->mpeg2_frame_hdr); + control_id = V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR; + control_data = &context_object->mpeg2_frame_hdr; + control_size = sizeof(context_object->mpeg2_frame_hdr); break; default: - out_buf.m.planes[0].bytesused = 0; - ctrl.id = V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR; - ctrl.ptr = NULL; - ctrl.size = 0; - break; + return VA_STATUS_ERROR_UNSUPPORTED_PROFILE; } + rc = v4l2_set_control(driver_data->mem2mem_fd, request_fd, control_id, control_data, control_size); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + rc = v4l2_queue_buffer(driver_data->mem2mem_fd, request_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, surface_object->output_buf_index, 0); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + rc = v4l2_queue_buffer(driver_data->mem2mem_fd, request_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, surface_object->input_buf_index, driver_data->slice_offset[surface_object->input_buf_index]); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + driver_data->slice_offset[surface_object->input_buf_index] = 0; - memset(&cap_buf, 0, sizeof(cap_buf)); - cap_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - cap_buf.memory = V4L2_MEMORY_MMAP; - cap_buf.index = surface_object->output_buf_index; - cap_buf.length = 2; - cap_buf.m.planes = planes; - - ctrls.controls = &ctrl; - ctrls.count = 1; - ctrls.request_fd = request_fd; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_S_EXT_CTRLS, &ctrls); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_QBUF, &cap_buf); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_QBUF, &out_buf); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; - context_object->render_surface_id = VA_INVALID_ID; return VA_STATUS_SUCCESS; diff --git a/src/surface.c b/src/surface.c index bbd5367..4209331 100644 --- a/src/surface.c +++ b/src/surface.c @@ -38,47 +38,27 @@ #include +#include "v4l2.h" + VAStatus SunxiCedrusCreateSurfaces(VADriverContextP context, int width, int height, int format, int surfaces_count, VASurfaceID *surfaces) { struct sunxi_cedrus_driver_data *driver_data = (struct sunxi_cedrus_driver_data *) context->pDriverData; struct object_surface *surface_object; + unsigned int length[2]; + unsigned int offset[2]; VASurfaceID id; - struct v4l2_buffer buf; - struct v4l2_plane planes[2]; - struct v4l2_create_buffers create_bufs; - struct v4l2_format fmt; int i; - memset(planes, 0, 2 * sizeof(struct v4l2_plane)); - if (format != VA_RT_FORMAT_YUV420) return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT; - memset(&fmt, 0, sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - fmt.fmt.pix_mp.width = width; - fmt.fmt.pix_mp.height = height; - fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_SUNXI; - fmt.fmt.pix_mp.field = V4L2_FIELD_ANY; - fmt.fmt.pix_mp.num_planes = 2; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_S_FMT, &fmt); + rc = v4l2_set_format(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, V4L2_PIX_FMT_SUNXI, width, height); if (rc < 0) return VA_STATUS_ERROR_OPERATION_FAILED; - memset(&create_bufs, 0, sizeof(create_bufs)); - create_bufs.count = surfaces_count; - create_bufs.memory = V4L2_MEMORY_MMAP; - create_bufs.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_G_FMT, &create_bufs.format); - if (rc < 0) - return VA_STATUS_ERROR_OPERATION_FAILED; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_CREATE_BUFS, &create_bufs); + rc = v4l2_create_buffers(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, surfaces_count); if (rc < 0) return VA_STATUS_ERROR_ALLOCATION_FAILED; @@ -90,32 +70,25 @@ VAStatus SunxiCedrusCreateSurfaces(VADriverContextP context, int width, surfaces[i] = surfaceID; - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = create_bufs.index + i; - buf.length = 2; - buf.m.planes = planes; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_QUERYBUF, &buf); + rc = v4l2_request_buffer(driver_data->mem2mem_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, i, length, offset); if (rc < 0) return VA_STATUS_ERROR_ALLOCATION_FAILED; - driver_data->luma_bufs[buf.index] = mmap(NULL, buf.m.planes[0].length, PROT_READ | PROT_WRITE, MAP_SHARED, - driver_data->mem2mem_fd, buf.m.planes[0].m.mem_offset); + driver_data->luma_bufs[index] = mmap(NULL, length[0], PROT_READ | PROT_WRITE, MAP_SHARED, + driver_data->mem2mem_fd, offset[0]); if (driver_data->luma_bufs[buf.index] == MAP_FAILED) return VA_STATUS_ERROR_ALLOCATION_FAILED; - driver_data->chroma_bufs[buf.index] = mmap(NULL, buf.m.planes[1].length, PROT_READ | PROT_WRITE, MAP_SHARED, - driver_data->mem2mem_fd, buf.m.planes[1].m.mem_offset); - if (driver_data->chroma_bufs[buf.index] == MAP_FAILED) + driver_data->chroma_bufs[index] = mmap(NULL, length[1], PROT_READ | PROT_WRITE, MAP_SHARED, + driver_data->mem2mem_fd, offset[1]); + if (driver_data->chroma_bufs[index] == MAP_FAILED) return VA_STATUS_ERROR_ALLOCATION_FAILED; surface_object->status = VASurfaceReady; surface_object->width = width; surface_object->height = height; surface_object->input_buf_index = 0; - surface_object->output_buf_index = create_bufs.index + i; + surface_object->output_buf_index = i; surfaces_ids[i] = id; } @@ -150,16 +123,10 @@ VAStatus SunxiCedrusSyncSurface(VADriverContextP context, struct sunxi_cedrus_driver_data *driver_data = (struct sunxi_cedrus_driver_data *) context->pDriverData; struct object_surface *surface_object; - struct v4l2_buffer buf; - struct v4l2_plane plane[1]; - struct v4l2_plane planes[2]; fd_set read_fds; int request_fd; int rc; - memset(plane, 0, sizeof(struct v4l2_plane)); - memset(planes, 0, 2 * sizeof(struct v4l2_plane)); - surface_object = SURFACE(surface_id); if (surface_object == NULL) return VA_STATUS_ERROR_INVALID_SURFACE; @@ -186,25 +153,11 @@ VAStatus SunxiCedrusSyncSurface(VADriverContextP context, if (rc < 0) return VA_STATUS_ERROR_OPERATION_FAILED; - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = surface_object->input_buf_index; - buf.length = 1; - buf.m.planes = plane; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_DQBUF, &buf); + rc = v4l2_dequeue_buffer(driver_data->mem2mem_fd, request_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, surface_object->input_buf_index); if (rc < 0) return VA_STATUS_ERROR_OPERATION_FAILED; - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = surface_object->output_buf_index; - buf.length = 2; - buf.m.planes = planes; - - rc = ioctl(driver_data->mem2mem_fd, VIDIOC_DQBUF, &buf); + rc = v4l2_dequeue_buffer(driver_data->mem2mem_fd, request_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, surface_object->output_buf_index); if (rc < 0) return VA_STATUS_ERROR_OPERATION_FAILED; diff --git a/src/v4l2.c b/src/v4l2.c new file mode 100644 index 0000000..3cf6d73 --- /dev/null +++ b/src/v4l2.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2018 Paul Kocialkowski + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +#include "sunxi_cedrus.h" +#include "v4l2.h" + +bool v4l2_find_format(int video_fd, unsigned int type, unsigned int pixelformat) +{ + struct v4l2_fmtdesc fmtdesc; + int rc; + + memset(&fmtdesc, 0, sizeof(fmtdesc)); + fmtdesc.type = type; + fmtdesc.index = 0; + + do { + rc = ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc); + if (rc < 0) + break; + + if (fmtdesc.pixelformat == pixelformat) + return true; + + fmtdesc.index++; + } while (rc >= 0); + + return false; +} + +int v4l2_set_format(int video_fd, unsigned int type, unsigned int pixelformat, + unsigned int width, unsigned int height) +{ + struct v4l2_format format; + int rc; + + memset(&format, 0, sizeof(format)); + format.type = type; + format.fmt.pix_mp.width = width; + format.fmt.pix_mp.height = height; + format.fmt.pix_mp.plane_fmt[0].sizeimage = type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? DESTINATION_SIZE_MAX : 0; + format.fmt.pix_mp.pixelformat = pixelformat; + format.fmt.pix_mp.field = V4L2_FIELD_ANY; + format.fmt.pix_mp.num_planes = type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? 2 : 1; + + rc = ioctl(video_fd, VIDIOC_S_FMT, &format); + if (rc < 0) { + sunxi_cedrus_msg("Unable to set format for type %d: %s\n", type, strerror(errno)); + return -1; + } + + return 0; +} + +int v4l2_create_buffers(int video_fd, unsigned int type, + unsigned int buffers_count) +{ + struct v4l2_create_buffers buffers; + int rc; + + memset(&buffers, 0, sizeof(buffers)); + buffers.format.type = type; + buffers.memory = V4L2_MEMORY_MMAP; + buffers.count = buffers_count; + + rc = ioctl(video_fd, VIDIOC_G_FMT, &buffers.format); + if (rc < 0) { + sunxi_cedrus_msg("Unable to get format for type %d: %s\n", type, strerror(errno)); + return -1; + } + + rc = ioctl(video_fd, VIDIOC_CREATE_BUFS, &buffers); + if (rc < 0) { + sunxi_cedrus_msg("Unable to create buffer for type %d: %s\n", type, strerror(errno)); + return -1; + } + + return 0; +} + +int v4l2_request_buffer(int video_fd, unsigned int type, unsigned int index, + unsigned int *length, unsigned int *offset) +{ + struct v4l2_plane planes[2]; + struct v4l2_buffer buffer; + int rc; + + memset(planes, 0, sizeof(planes)); + memset(&buffer, 0, sizeof(buffer)); + + buffer.type = type; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = index; + buffer.length = type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? 2 : 1; + buffer.m.planes = planes; + + rc = ioctl(video_fd, VIDIOC_QUERYBUF, &buffer); + if (rc < 0) { + fprintf(stderr, "Unable to query buffer: %s\n", strerror(errno)); + return -1; + } + + if (length != NULL) { + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + length[0] = buffer.m.planes[0].length; + length[1] = buffer.m.planes[1].length; + } else { + *length = buffer.m.planes[0].length; + } + } + + if (offset != NULL) { + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + offset[0] = buffer.m.planes[0].m.mem_offset; + offset[1] = buffer.m.planes[1].m.mem_offset; + } else { + *offset = buffer.m.planes[0].m.mem_offset; + } + } + + return 0; +} + +int v4l2_queue_buffer(int video_fd, int request_fd, unsigned int type, + unsigned int index, unsigned int size) +{ + struct v4l2_plane planes[2]; + struct v4l2_buffer buffer; + int rc; + + memset(planes, 0, sizeof(planes)); + memset(&buffer, 0, sizeof(buffer)); + + buffer.type = type; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = index; + buffer.length = type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? 2 : 1; + buffer.m.planes = planes; + + buffer.m.planes[0].bytesused = size; + + if (request_fd >= 0) { + buffer.flags = V4L2_BUF_FLAG_REQUEST_FD; + buffer.request_fd = request_fd; + } + + rc = ioctl(video_fd, VIDIOC_QBUF, &buffer); + if (rc < 0) { + fprintf(stderr, "Unable to queue buffer: %s\n", strerror(errno)); + return -1; + } +} + +int v4l2_dequeue_buffer(int video_fd, int request_fd, unsigned int type, + unsigned int index) +{ + struct v4l2_plane planes[2]; + struct v4l2_buffer buffer; + int rc; + + memset(planes, 0, sizeof(planes)); + memset(&buffer, 0, sizeof(buffer)); + + buffer.type = type; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = index; + buffer.length = type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? 2 : 1; + buffer.m.planes = planes; + + if (request_fd >= 0) { + buffer.flags = V4L2_BUF_FLAG_REQUEST_FD; + buffer.request_fd = request_fd; + } + + rc = ioctl(video_fd, VIDIOC_DQBUF, &buffer); + if (rc < 0) { + fprintf(stderr, "Unable to dequeue buffer: %s\n", strerror(errno)); + return -1; + } +} + +int v4l2_set_control(int video_fd, int request_fd, unsigned int id, void *data, + unsigned int size) +{ + struct v4l2_ext_control control; + struct v4l2_ext_controls controls; + int rc; + + memset(&control, 0, sizeof(control)); + memset(&controls, 0, sizeof(controls)); + + control.id = id; + control.ptr = data; + control.size = size; + + controls.controls = &control; + controls.count = 1; + + if (request_fd >= 0) { + controls.which = V4L2_CTRL_WHICH_REQUEST; + controls.request_fd = request_fd; + } + + rc = ioctl(video_fd, VIDIOC_S_EXT_CTRLS, &controls); + if (rc < 0) { + fprintf(stderr, "Unable to set control: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +int v4l2_set_stream(int video_fd, unsigned int type, bool enable) +{ + enum v4l2_buf_type buf_type = type; + int rc; + + rc = ioctl(video_fd, enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &buf_type); + if (rc < 0) { + sunxi_cedrus_msg("Unable to %sable stream: %s\n", enable ? "en" : "dis", strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/v4l2.h b/src/v4l2.h new file mode 100644 index 0000000..7667cf0 --- /dev/null +++ b/src/v4l2.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 Paul Kocialkowski + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _V4L2_H_ +#define _V4L2_H_ + +#define DESTINATION_SIZE_MAX (1024 * 1024) + +bool v4l2_find_format(int video_fd, unsigned int type, + unsigned int pixelformat); +int v4l2_set_format(int video_fd, unsigned int type, unsigned int pixelformat, + unsigned int width, unsigned int height); +int v4l2_create_buffers(int video_fd, unsigned int type, + unsigned int buffers_count); +int v4l2_request_buffer(int video_fd, unsigned int type, unsigned int index, + unsigned int *length, unsigned int *offset); +int v4l2_queue_buffer(int video_fd, int request_fd, unsigned int type, + unsigned int index, unsigned int size); +int v4l2_dequeue_buffer(int video_fd, int request_fd, unsigned int type, + unsigned int index); +int v4l2_set_control(int video_fd, int request_fd, unsigned int id, void *data, + unsigned int size); +int v4l2_set_stream(int video_fd, unsigned int type, bool enable); + +#endif