Implement mplane support
This commit is contained in:
parent
510177b220
commit
a7e7f802bc
160
camera.c
160
camera.c
|
@ -174,6 +174,20 @@ bool mp_camera_mode_is_equivalent(const MPCameraMode *m1, const MPCameraMode *m2
|
||||||
&& m1->height == m2->height;
|
&& m1->height == m2->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void errno_printerr(const char *s)
|
||||||
|
{
|
||||||
|
g_printerr("MPCamera: %s error %d, %s\n", s, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xioctl(int fd, int request, void *arg)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
do {
|
||||||
|
r = ioctl(fd, request, arg);
|
||||||
|
} while (r == -1 && errno == EINTR);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
struct video_buffer {
|
struct video_buffer {
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
|
@ -188,17 +202,37 @@ struct _MPCamera {
|
||||||
|
|
||||||
struct video_buffer buffers[MAX_VIDEO_BUFFERS];
|
struct video_buffer buffers[MAX_VIDEO_BUFFERS];
|
||||||
uint32_t num_buffers;
|
uint32_t num_buffers;
|
||||||
|
|
||||||
|
bool use_mplane;
|
||||||
};
|
};
|
||||||
|
|
||||||
MPCamera *mp_camera_new(int video_fd, int subdev_fd)
|
MPCamera *mp_camera_new(int video_fd, int subdev_fd)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail(video_fd != -1, NULL);
|
g_return_val_if_fail(video_fd != -1, NULL);
|
||||||
|
|
||||||
|
// Query capabilities
|
||||||
|
struct v4l2_capability cap;
|
||||||
|
if (xioctl(video_fd, VIDIOC_QUERYCAP, &cap) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether this is a video capture device
|
||||||
|
bool use_mplane;
|
||||||
|
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
|
||||||
|
use_mplane = true;
|
||||||
|
printf("!!\n");
|
||||||
|
} else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
|
||||||
|
use_mplane = false;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
MPCamera *camera = malloc(sizeof(MPCamera));
|
MPCamera *camera = malloc(sizeof(MPCamera));
|
||||||
camera->video_fd = video_fd;
|
camera->video_fd = video_fd;
|
||||||
camera->subdev_fd = subdev_fd;
|
camera->subdev_fd = subdev_fd;
|
||||||
camera->has_set_mode = false;
|
camera->has_set_mode = false;
|
||||||
camera->num_buffers = 0;
|
camera->num_buffers = 0;
|
||||||
|
camera->use_mplane = use_mplane;
|
||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,37 +261,49 @@ int mp_camera_get_subdev_fd(MPCamera *camera)
|
||||||
return camera->subdev_fd;
|
return camera->subdev_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void errno_printerr(const char *s)
|
static bool camera_mode_impl(MPCamera *camera, int request, MPCameraMode *mode)
|
||||||
{
|
{
|
||||||
g_printerr("MPCamera: %s error %d, %s\n", s, errno, strerror(errno));
|
uint32_t pixfmt = mp_pixel_format_from_v4l_pixel_format(mode->pixel_format);
|
||||||
}
|
struct v4l2_format fmt = {};
|
||||||
|
if (camera->use_mplane) {
|
||||||
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||||
|
fmt.fmt.pix_mp.width = mode->width;
|
||||||
|
fmt.fmt.pix_mp.height = mode->height;
|
||||||
|
fmt.fmt.pix_mp.pixelformat = pixfmt;
|
||||||
|
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||||
|
} else {
|
||||||
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
fmt.fmt.pix.width = mode->width;
|
||||||
|
fmt.fmt.pix.height = mode->height;
|
||||||
|
fmt.fmt.pix.pixelformat = pixfmt;
|
||||||
|
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||||
|
}
|
||||||
|
|
||||||
static int xioctl(int fd, int request, void *arg)
|
if (xioctl(camera->video_fd, request, &fmt) == -1) {
|
||||||
{
|
return false;
|
||||||
int r;
|
}
|
||||||
do {
|
|
||||||
r = ioctl(fd, request, arg);
|
if (camera->use_mplane) {
|
||||||
} while (r == -1 && errno == EINTR);
|
mode->width = fmt.fmt.pix_mp.width;
|
||||||
return r;
|
mode->height = fmt.fmt.pix_mp.height;
|
||||||
|
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(
|
||||||
|
fmt.fmt.pix_mp.pixelformat);
|
||||||
|
} else {
|
||||||
|
mode->width = fmt.fmt.pix.width;
|
||||||
|
mode->height = fmt.fmt.pix.height;
|
||||||
|
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(
|
||||||
|
fmt.fmt.pix.pixelformat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mp_camera_try_mode(MPCamera *camera, MPCameraMode *mode)
|
bool mp_camera_try_mode(MPCamera *camera, MPCameraMode *mode)
|
||||||
{
|
{
|
||||||
struct v4l2_format fmt = {};
|
if (!camera_mode_impl(camera, VIDIOC_TRY_FMT, mode)) {
|
||||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
||||||
fmt.fmt.pix.width = mode->width;
|
|
||||||
fmt.fmt.pix.height = mode->height;
|
|
||||||
fmt.fmt.pix.pixelformat = mp_pixel_format_from_v4l_pixel_format(mode->pixel_format);
|
|
||||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
|
||||||
if (xioctl(camera->video_fd, VIDIOC_TRY_FMT, &fmt) == -1) {
|
|
||||||
errno_printerr("VIDIOC_S_FMT");
|
errno_printerr("VIDIOC_S_FMT");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mode->width = fmt.fmt.pix.width;
|
|
||||||
mode->height = fmt.fmt.pix.height;
|
|
||||||
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(fmt.fmt.pix.pixelformat);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,21 +363,10 @@ bool mp_camera_set_mode(MPCamera *camera, MPCameraMode *mode)
|
||||||
|
|
||||||
// Set the mode for the video device
|
// Set the mode for the video device
|
||||||
{
|
{
|
||||||
struct v4l2_format fmt = {};
|
if (!camera_mode_impl(camera, VIDIOC_S_FMT, mode)) {
|
||||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
||||||
fmt.fmt.pix.width = mode->width;
|
|
||||||
fmt.fmt.pix.height = mode->height;
|
|
||||||
fmt.fmt.pix.pixelformat = mp_pixel_format_to_v4l_pixel_format(mode->pixel_format);
|
|
||||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
|
||||||
if (xioctl(camera->video_fd, VIDIOC_S_FMT, &fmt) == -1) {
|
|
||||||
errno_printerr("VIDIOC_S_FMT");
|
errno_printerr("VIDIOC_S_FMT");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the mode
|
|
||||||
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(fmt.fmt.pix.pixelformat);
|
|
||||||
mode->width = fmt.fmt.pix.width;
|
|
||||||
mode->height = fmt.fmt.pix.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
camera->has_set_mode = true;
|
camera->has_set_mode = true;
|
||||||
|
@ -370,19 +405,36 @@ bool mp_camera_start_capture(MPCamera *camera)
|
||||||
.index = i,
|
.index = i,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct v4l2_plane planes[1];
|
||||||
|
if (camera->use_mplane) {
|
||||||
|
buf.m.planes = planes;
|
||||||
|
buf.length = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (xioctl(camera->video_fd, VIDIOC_QUERYBUF, &buf) == -1) {
|
if (xioctl(camera->video_fd, VIDIOC_QUERYBUF, &buf) == -1) {
|
||||||
errno_printerr("VIDIOC_QUERYBUF");
|
errno_printerr("VIDIOC_QUERYBUF");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
camera->buffers[i].length = buf.length;
|
if (camera->use_mplane) {
|
||||||
camera->buffers[i].data = mmap(
|
camera->buffers[i].length = planes[0].length;
|
||||||
NULL,
|
camera->buffers[i].data = mmap(
|
||||||
buf.length,
|
NULL,
|
||||||
PROT_READ,
|
planes[0].length,
|
||||||
MAP_SHARED,
|
PROT_READ,
|
||||||
camera->video_fd,
|
MAP_SHARED,
|
||||||
buf.m.offset);
|
camera->video_fd,
|
||||||
|
planes[0].m.mem_offset);
|
||||||
|
} else {
|
||||||
|
camera->buffers[i].length = buf.length;
|
||||||
|
camera->buffers[i].data = mmap(
|
||||||
|
NULL,
|
||||||
|
buf.length,
|
||||||
|
PROT_READ,
|
||||||
|
MAP_SHARED,
|
||||||
|
camera->video_fd,
|
||||||
|
buf.m.offset);
|
||||||
|
}
|
||||||
|
|
||||||
if (camera->buffers[i].data == MAP_FAILED) {
|
if (camera->buffers[i].data == MAP_FAILED) {
|
||||||
errno_printerr("mmap");
|
errno_printerr("mmap");
|
||||||
|
@ -404,6 +456,12 @@ bool mp_camera_start_capture(MPCamera *camera)
|
||||||
.index = i,
|
.index = i,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct v4l2_plane planes[1];
|
||||||
|
if (camera->use_mplane) {
|
||||||
|
buf.m.planes = planes;
|
||||||
|
buf.length = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Queue the buffer for capture
|
// Queue the buffer for capture
|
||||||
if (xioctl(camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
|
if (xioctl(camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
|
||||||
errno_printerr("VIDIOC_QBUF");
|
errno_printerr("VIDIOC_QBUF");
|
||||||
|
@ -483,6 +541,13 @@ bool mp_camera_capture_image(MPCamera *camera, void (*callback)(MPImage, void *)
|
||||||
struct v4l2_buffer buf = {};
|
struct v4l2_buffer buf = {};
|
||||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
buf.memory = V4L2_MEMORY_MMAP;
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
|
||||||
|
struct v4l2_plane planes[1];
|
||||||
|
if (camera->use_mplane) {
|
||||||
|
buf.m.planes = planes;
|
||||||
|
buf.length = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (xioctl(camera->video_fd, VIDIOC_DQBUF, &buf) == -1) {
|
if (xioctl(camera->video_fd, VIDIOC_DQBUF, &buf) == -1) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case EAGAIN:
|
case EAGAIN:
|
||||||
|
@ -500,8 +565,15 @@ bool mp_camera_capture_image(MPCamera *camera, void (*callback)(MPImage, void *)
|
||||||
uint32_t width = camera->current_mode.width;
|
uint32_t width = camera->current_mode.width;
|
||||||
uint32_t height = camera->current_mode.height;
|
uint32_t height = camera->current_mode.height;
|
||||||
|
|
||||||
assert(buf.bytesused == mp_pixel_format_width_to_bytes(pixel_format, width) * height);
|
uint32_t bytesused;
|
||||||
assert(buf.bytesused == camera->buffers[buf.index].length);
|
if (camera->use_mplane) {
|
||||||
|
bytesused = planes[0].bytesused;
|
||||||
|
} else {
|
||||||
|
bytesused = buf.bytesused;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(bytesused == mp_pixel_format_width_to_bytes(pixel_format, width) * height);
|
||||||
|
assert(bytesused == camera->buffers[buf.index].length);
|
||||||
|
|
||||||
MPImage image = {
|
MPImage image = {
|
||||||
.pixel_format = pixel_format,
|
.pixel_format = pixel_format,
|
||||||
|
|
Loading…
Reference in New Issue