WIP
This commit is contained in:
commit
d5fe55d2e0
72
buffer.c
Normal file
72
buffer.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include "buffer.h"
|
||||
#include "buffer_list.h"
|
||||
#include "device.h"
|
||||
|
||||
buffer_t *buffer_open(const char *name, buffer_list_t *buf_list, int index) {
|
||||
buffer_t *buf = calloc(1, sizeof(buffer_t));
|
||||
device_t *dev = buf_list->device;
|
||||
|
||||
buf->name = strdup(name);
|
||||
buf->index = index;
|
||||
buf->buf_list = buf_list;
|
||||
buf->dma_fd = -1;
|
||||
|
||||
if (buf_list->do_mmap) {
|
||||
buf->v4l2_buffer.type = buf_list->type;
|
||||
buf->v4l2_buffer.memory = V4L2_MEMORY_MMAP;
|
||||
buf->v4l2_buffer.index = index;
|
||||
|
||||
if (buf_list->do_mplanes) {
|
||||
buf->v4l2_buffer.length = 1;
|
||||
buf->v4l2_buffer.m.planes = &buf->v4l2_plane;
|
||||
}
|
||||
|
||||
E_XIOCTL(buf_list, dev->fd, VIDIOC_QUERYBUF, &buf->v4l2_buffer, "Cannot query buffer %d", index);
|
||||
|
||||
if (buf_list->do_mplanes) {
|
||||
buf->offset = buf->v4l2_plane.m.mem_offset;
|
||||
buf->length = buf->v4l2_plane.length;
|
||||
} else {
|
||||
buf->offset = buf->v4l2_buffer.m.offset;
|
||||
buf->length = buf->v4l2_buffer.length;
|
||||
}
|
||||
|
||||
buf->start = mmap(NULL, buf->length, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, buf->offset);
|
||||
if (buf->start == MAP_FAILED) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf_list->do_dma) {
|
||||
struct v4l2_exportbuffer v4l2_exp = {0};
|
||||
v4l2_exp.type = buf_list->type;
|
||||
v4l2_exp.index = index;
|
||||
v4l2_exp.plane = 0;
|
||||
E_XIOCTL(buf_list, dev->fd, VIDIOC_EXPBUF, &v4l2_exp, "Can't export queue buffer=%u to DMA", index);
|
||||
buf->dma_fd = v4l2_exp.fd;
|
||||
}
|
||||
|
||||
buffer_capture_enqueue(buf);
|
||||
|
||||
return buf;
|
||||
|
||||
error:
|
||||
buffer_close(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void buffer_close(buffer_t *buf)
|
||||
{
|
||||
if (buf == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf->start && buf->start != MAP_FAILED) {
|
||||
munmap(buf->start, buf->length);
|
||||
}
|
||||
if (buf->dma_fd >= 0) {
|
||||
close(buf->dma_fd);
|
||||
}
|
||||
free(buf->name);
|
||||
free(buf);
|
||||
}
|
21
buffer.h
Normal file
21
buffer.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "v4l2.h"
|
||||
|
||||
typedef struct buffer_s {
|
||||
char *name;
|
||||
struct buffer_list_s *buf_list;
|
||||
int index;
|
||||
void *start;
|
||||
size_t offset;
|
||||
size_t length;
|
||||
struct v4l2_buffer v4l2_buffer;
|
||||
struct v4l2_plane v4l2_plane;
|
||||
int dma_fd;
|
||||
bool enqueued;
|
||||
} buffer_t;
|
||||
|
||||
buffer_t *buffer_open(const char *name, struct buffer_list_s *buf_list, int buffer);
|
||||
void buffer_close(buffer_t *buf);
|
||||
bool buffer_output_dequeue(buffer_t *buf);
|
||||
bool buffer_capture_enqueue(buffer_t *buf);
|
158
buffer_list.c
Normal file
158
buffer_list.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include "buffer.h"
|
||||
#include "buffer_list.h"
|
||||
#include "device.h"
|
||||
|
||||
buffer_list_t *buffer_list_open(const char *name, struct device_s *dev, unsigned type)
|
||||
{
|
||||
buffer_list_t *buf_list = calloc(1, sizeof(buffer_list_t));
|
||||
|
||||
buf_list->device = dev;
|
||||
buf_list->name = strdup(name);
|
||||
buf_list->type = type;
|
||||
|
||||
switch(type) {
|
||||
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
|
||||
buf_list->do_mplanes = true;
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
||||
buf_list->do_dma = true;
|
||||
buf_list->do_mmap = true;
|
||||
buf_list->do_capture = true;
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
|
||||
buf_list->do_dma = true;
|
||||
buf_list->do_mmap = true;
|
||||
buf_list->do_mplanes = true;
|
||||
buf_list->do_capture = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
E_LOG_PERROR(buf_list, "Unknown type=%d", type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return buf_list;
|
||||
|
||||
error:
|
||||
buffer_list_close(buf_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void buffer_list_close(buffer_list_t *buf_list)
|
||||
{
|
||||
if (!buf_list) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf_list->bufs) {
|
||||
for (int i = 0; i < buf_list->nbufs; i++) {
|
||||
buffer_close(buf_list->bufs[i]);
|
||||
}
|
||||
free(buf_list->bufs);
|
||||
buf_list->bufs = NULL;
|
||||
buf_list->nbufs = 0;
|
||||
}
|
||||
|
||||
free(buf_list->name);
|
||||
free(buf_list);
|
||||
}
|
||||
|
||||
int buffer_list_set_format(buffer_list_t *buf_list, unsigned width, unsigned height, unsigned format)
|
||||
{
|
||||
struct v4l2_format *fmt = &buf_list->v4l2_format;
|
||||
|
||||
fmt->type = buf_list->type;
|
||||
|
||||
if (buf_list->do_mplanes) {
|
||||
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;
|
||||
fmt->fmt.pix.width = width;
|
||||
fmt->fmt.pix.height = height;
|
||||
fmt->fmt.pix.pixelformat = format;
|
||||
fmt->fmt.pix.field = V4L2_FIELD_ANY;
|
||||
fmt->fmt.pix.bytesperline = fourcc_to_stride(width, format);
|
||||
} else {
|
||||
fmt->fmt.pix_mp.colorspace = V4L2_COLORSPACE_JPEG;
|
||||
fmt->fmt.pix_mp.width = width;
|
||||
fmt->fmt.pix_mp.height = height;
|
||||
fmt->fmt.pix_mp.pixelformat = format;
|
||||
fmt->fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||
fmt->fmt.pix_mp.num_planes = 1;
|
||||
fmt->fmt.pix_mp.plane_fmt[0].bytesperline = fourcc_to_stride(width, format);
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(buf_list, "Configuring format ...");
|
||||
E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_S_FMT, fmt, "Can't set format");
|
||||
|
||||
if (fmt->fmt.pix.width != width || fmt->fmt.pix.height != height) {
|
||||
E_LOG_ERROR(buf_list, "Requested resolution=%ux%u is unavailable. Got %ux%u.",
|
||||
width, height, fmt->fmt.pix.width, fmt->fmt.pix.height);
|
||||
}
|
||||
|
||||
if (fmt->fmt.pix.pixelformat != format) {
|
||||
E_LOG_ERROR(buf_list, "Could not obtain the requested format=%s; driver gave us %s",
|
||||
fourcc_to_string(format).buf,
|
||||
fourcc_to_string(fmt->fmt.pix.pixelformat).buf);
|
||||
}
|
||||
|
||||
E_LOG_INFO(buf_list, "Using: %ux%u/%s",
|
||||
fmt->fmt.pix.width, fmt->fmt.pix.height, fourcc_to_string(fmt->fmt.pix.pixelformat).buf);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int buffer_list_request(buffer_list_t *buf_list, int nbufs)
|
||||
{
|
||||
struct v4l2_requestbuffers v4l2_req = {0};
|
||||
v4l2_req.count = nbufs;
|
||||
v4l2_req.type = buf_list->type;
|
||||
v4l2_req.memory = buf_list->do_mmap ? V4L2_MEMORY_MMAP : V4L2_MEMORY_DMABUF;
|
||||
|
||||
E_LOG_DEBUG(buf_list, "Requesting %u buffers", v4l2_req.count);
|
||||
|
||||
E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_REQBUFS, &v4l2_req, "Can't request buffers");
|
||||
if (v4l2_req.count < 1) {
|
||||
E_LOG_ERROR(buf_list, "Insufficient buffer memory: %u", v4l2_req.count);
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(buf_list, "Got %u buffers", v4l2_req.count);
|
||||
|
||||
buf_list->bufs = calloc(v4l2_req.count, sizeof(buffer_t*));
|
||||
buf_list->nbufs = v4l2_req.count;
|
||||
|
||||
for (unsigned i = 0; i < buf_list->nbufs; i++) {
|
||||
char name[64];
|
||||
sprintf(name, "%s:buf%d", buf_list->name, i);
|
||||
buffer_t *buf = buffer_open(name, buf_list, i);
|
||||
if (!buf) {
|
||||
E_LOG_ERROR(buf_list, "Cannot open buffer: %u", i);
|
||||
goto error;
|
||||
}
|
||||
buf_list->bufs[i] = buf;
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(buf_list, "Opened %u buffers", buf_list->nbufs);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int buffer_list_stream(buffer_list_t *buf_list, bool do_on)
|
||||
{
|
||||
enum v4l2_buf_type type = buf_list->type;
|
||||
|
||||
E_XIOCTL(buf_list, buf_list->device->fd, do_on ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &type, "Cannot set streaming state");
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
30
buffer_list.h
Normal file
30
buffer_list.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "v4l2.h"
|
||||
|
||||
typedef struct buffer_list_s {
|
||||
char *name;
|
||||
struct device_s *device;
|
||||
buffer_t **bufs;
|
||||
int nbufs;
|
||||
int type;
|
||||
|
||||
struct v4l2_format v4l2_format;
|
||||
bool do_mplanes;
|
||||
bool do_mmap;
|
||||
bool do_dma;
|
||||
bool do_capture;
|
||||
} buffer_list_t;
|
||||
|
||||
buffer_list_t *buffer_list_open(const char *name, struct device_s *dev, unsigned type);
|
||||
void buffer_list_close(buffer_list_t *buf_list);
|
||||
|
||||
int buffer_list_set_format(buffer_list_t *buffer_list, unsigned width, unsigned height, unsigned format);
|
||||
int buffer_list_request(buffer_list_t *buf_list, int nbufs);
|
||||
|
||||
bool buffer_list_wait_pool(buffer_list_t *buf_list, int timeout);
|
||||
|
||||
buffer_t *buffer_list_output_enqueue(buffer_list_t *buf_list, buffer_t *dma_buf);
|
||||
buffer_t *buffer_list_capture_dequeue(buffer_list_t *buf_list);
|
||||
|
||||
int buffer_list_stream(buffer_list_t *buf_list, bool do_on);
|
120
buffer_queue.c
Normal file
120
buffer_queue.c
Normal file
@ -0,0 +1,120 @@
|
||||
#include "buffer.h"
|
||||
#include "buffer_list.h"
|
||||
#include "device.h"
|
||||
|
||||
bool buffer_output_dequeue(buffer_t *buf)
|
||||
{
|
||||
// if (buf->enqueued || buf->buf_list->do_capture) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool buffer_capture_enqueue(buffer_t *buf)
|
||||
{
|
||||
if (buf->enqueued || !buf->buf_list->do_mmap || !buf->buf_list->do_capture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(buf, "Queuing buffer...");
|
||||
E_XIOCTL(buf, buf->buf_list->device->fd, VIDIOC_QBUF, &buf->v4l2_buffer, "Can't queue buffer.");
|
||||
|
||||
buf->enqueued = true;
|
||||
|
||||
error:
|
||||
return true;
|
||||
}
|
||||
|
||||
buffer_t *buffer_list_output_enqueue(buffer_list_t *buf_list, buffer_t *dma_buf)
|
||||
{
|
||||
// if (dma_buf->enqueued || dma_buf->dma_fd < 0) {
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
// struct v4l2_buffer v4l2_buf = {0};
|
||||
// struct v4l2_plane v4l2_plane = {0};
|
||||
|
||||
// v4l2_buf.type = buf_list->type;
|
||||
// v4l2_buf.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
// if (buf_list->do_mplanes) {
|
||||
// v4l2_buf.length = 1;
|
||||
// v4l2_buf.m.planes = &v4l2_plane;
|
||||
// }
|
||||
|
||||
// E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_DQBUF, &v4l2_buf, "Can't grab capture buffer");
|
||||
// E_LOG_DEBUG(buf_list, "Grabbed INPUT buffer=%u", v4l2_buf.index);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer_t *buffer_list_capture_dequeue(buffer_list_t *buf_list)
|
||||
{
|
||||
if (!buf_list->do_mmap || !buf_list->do_capture) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct v4l2_buffer v4l2_buf = {0};
|
||||
struct v4l2_plane v4l2_plane = {0};
|
||||
|
||||
v4l2_buf.type = buf_list->type;
|
||||
v4l2_buf.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (buf_list->do_mplanes) {
|
||||
v4l2_buf.length = 1;
|
||||
v4l2_buf.m.planes = &v4l2_plane;
|
||||
}
|
||||
|
||||
E_XIOCTL(buf_list, buf_list->device->fd, VIDIOC_DQBUF, &v4l2_buf, "Can't grab capture buffer");
|
||||
E_LOG_DEBUG(buf_list, "Grabbed INPUT buffer=%u, bytes=%d",
|
||||
v4l2_buf.index, v4l2_buf.length);
|
||||
|
||||
buffer_t *buf = buf_list->bufs[v4l2_buf.index];
|
||||
buf->v4l2_plane = v4l2_plane;
|
||||
buf->v4l2_buffer = v4l2_buf;
|
||||
if (buf_list->do_mplanes) {
|
||||
buf->v4l2_buffer.m.planes = &buf->v4l2_plane;
|
||||
}
|
||||
|
||||
buf->enqueued = false;
|
||||
|
||||
return buf;
|
||||
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool buffer_list_wait_pool(buffer_list_t *buf_list, int timeout) {
|
||||
struct pollfd fds = {buf_list->device->fd, POLLIN, 0};
|
||||
|
||||
if (poll(&fds, 1, timeout) < 0 && errno != EINTR) {
|
||||
E_LOG_ERROR(buf_list, "Can't poll encoder");
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(buf_list, "Polling encoder %d, %d...", errno, fds.revents);
|
||||
if (fds.revents & POLLIN) {
|
||||
return true;
|
||||
}
|
||||
if (fds.revents & POLLPRI) {
|
||||
E_LOG_DEBUG(buf_list, "fd POLLPRI");
|
||||
}
|
||||
if (fds.revents & POLLOUT) {
|
||||
E_LOG_DEBUG(buf_list, "fd POLLOUT");
|
||||
}
|
||||
if (fds.revents & POLLERR) {
|
||||
E_LOG_DEBUG(buf_list, "fd POLLERR");
|
||||
device_consume_event(buf_list->device);
|
||||
}
|
||||
if (fds.revents & POLLHUP) {
|
||||
E_LOG_DEBUG(buf_list, "fd POLLHUP");
|
||||
}
|
||||
if (fds.revents & POLLNVAL) {
|
||||
E_LOG_DEBUG(buf_list, "fd POLLNVAL");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
BIN
camera_stream
Executable file
BIN
camera_stream
Executable file
Binary file not shown.
141
device.c
Normal file
141
device.c
Normal file
@ -0,0 +1,141 @@
|
||||
#include "device.h"
|
||||
#include "buffer.h"
|
||||
#include "buffer_list.h"
|
||||
|
||||
device_t *device_open(const char *name, const char *path) {
|
||||
device_t *dev = calloc(1, sizeof(device_t));
|
||||
dev->name = strdup(name);
|
||||
dev->path = strdup(path);
|
||||
dev->fd = open(path, O_RDWR|O_NONBLOCK);
|
||||
if(dev->fd < 0) {
|
||||
E_LOG_ERROR(dev, "Can't open device");
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(dev, "Querying device capabilities ...");
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_QUERYCAP, &dev->v4l2_cap, "Can't query device capabilities");\
|
||||
|
||||
if (!(dev->v4l2_cap.capabilities & V4L2_CAP_STREAMING)) {
|
||||
E_LOG_ERROR(dev, "Device doesn't support streaming IO");
|
||||
}
|
||||
|
||||
E_LOG_INFO(dev, "Device path=%s fd=%d opened", dev->path, dev->fd);
|
||||
return dev;
|
||||
|
||||
error:
|
||||
device_close(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void device_close(device_t *dev) {
|
||||
if(dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->capture_list) {
|
||||
buffer_list_close(dev->capture_list);
|
||||
dev->capture_list = NULL;
|
||||
}
|
||||
|
||||
if (dev->output_list) {
|
||||
buffer_list_close(dev->output_list);
|
||||
dev->output_list = NULL;
|
||||
}
|
||||
|
||||
if(dev->fd >= 0) {
|
||||
close(dev->fd);
|
||||
}
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
int device_open_buffer_list(device_t *dev, bool do_capture, unsigned width, unsigned height, unsigned format, int nbufs)
|
||||
{
|
||||
unsigned type;
|
||||
char name[64];
|
||||
struct buffer_list_s **buf_list = NULL;
|
||||
|
||||
if (do_capture) {
|
||||
buf_list = &dev->capture_list;
|
||||
|
||||
if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
sprintf(name, "%s:capture", dev->name);
|
||||
} else if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
sprintf(name, "%s:capture:mplane", dev->name);
|
||||
} else {
|
||||
E_LOG_ERROR(dev, "Video capture is not supported by device");
|
||||
}
|
||||
} else {
|
||||
buf_list = &dev->capture_list;
|
||||
|
||||
if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) {
|
||||
type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
||||
sprintf(name, "%s:output", dev->name);
|
||||
} else if (dev->v4l2_cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) {
|
||||
type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
sprintf(name, "%s:output:mplane", dev->name);
|
||||
} else {
|
||||
E_LOG_ERROR(dev, "Video output is not supported by device");
|
||||
}
|
||||
}
|
||||
|
||||
*buf_list = buffer_list_open(name, dev, type);
|
||||
if (!*buf_list) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (buffer_list_set_format(*buf_list, width, height, format) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (buffer_list_request(*buf_list, nbufs) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
buffer_list_close(*buf_list);
|
||||
*buf_list = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int device_stream(device_t *dev, bool do_on)
|
||||
{
|
||||
if (dev->capture_list) {
|
||||
if (buffer_list_stream(dev->capture_list, do_on) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->output_list) {
|
||||
if (buffer_list_stream(dev->output_list, do_on) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_consume_event(device_t *dev)
|
||||
{
|
||||
struct v4l2_event event;
|
||||
|
||||
E_LOG_DEBUG(dev, "Consuming V4L2 event ...");
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_DQEVENT, &event, "Got some V4L2 device event, but where is it?");
|
||||
|
||||
switch (event.type) {
|
||||
case V4L2_EVENT_SOURCE_CHANGE:
|
||||
E_LOG_INFO(dev, "Got V4L2_EVENT_SOURCE_CHANGE: source changed");
|
||||
return -1;
|
||||
case V4L2_EVENT_EOS:
|
||||
E_LOG_INFO(dev, "Got V4L2_EVENT_EOS: end of stream (ignored)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
21
device.h
Normal file
21
device.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "v4l2.h"
|
||||
|
||||
typedef struct device_s {
|
||||
char *name;
|
||||
char *path;
|
||||
int fd;
|
||||
struct v4l2_capability v4l2_cap;
|
||||
|
||||
struct buffer_list_s *capture_list;
|
||||
struct buffer_list_s *output_list;
|
||||
} device_t;
|
||||
|
||||
device_t *device_open(const char *name, const char *path);
|
||||
void device_close(device_t *device);
|
||||
|
||||
int device_open_buffer_list(device_t *dev, bool do_capture, unsigned width, unsigned height, unsigned format, int nbufs);
|
||||
int device_consume_event(device_t *device);
|
||||
|
||||
int device_stream(device_t *dev, bool do_on);
|
82
main.c
Normal file
82
main.c
Normal file
@ -0,0 +1,82 @@
|
||||
#include "buffer.h"
|
||||
#include "buffer_list.h"
|
||||
#include "device.h"
|
||||
#include "v4l2.h"
|
||||
|
||||
int camera_width = 1920;
|
||||
int camera_height = 1080;
|
||||
int camera_format = V4L2_PIX_FMT_SRGGB10P;
|
||||
int camera_nbufs = 4;
|
||||
|
||||
device_t *camera = NULL;
|
||||
device_t *isp_srgb = NULL;
|
||||
device_t *isp_yuuv = NULL;
|
||||
device_t *isp_yuuv_low = NULL;
|
||||
|
||||
int open_camera(const char *path)
|
||||
{
|
||||
camera = device_open("CAMERA", path);
|
||||
if (!camera) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (device_open_buffer_list(camera, true, camera_width, camera_height, camera_format, camera_nbufs) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_isp(const char *srgb_path, const char *yuuv_path, const char *yuuv_low_path)
|
||||
{
|
||||
isp_srgb = device_open("ISP-SRGB", srgb_path);
|
||||
isp_yuuv = device_open("ISP-YUUV", yuuv_path);
|
||||
isp_yuuv_low = device_open("ISP-YUUV-LOW", yuuv_low_path);
|
||||
|
||||
if (!isp_srgb || !isp_yuuv || !isp_yuuv_low) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (device_open_buffer_list(isp_srgb, false, camera_width, camera_height, camera_format, camera_nbufs) < 0 ||
|
||||
device_open_buffer_list(isp_yuuv, true, camera_width, camera_height, V4L2_PIX_FMT_YUYV, camera_nbufs) < 0 ||
|
||||
device_open_buffer_list(isp_yuuv, true, camera_width / 2, camera_height / 2, V4L2_PIX_FMT_YUYV, camera_nbufs) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (open_camera("/dev/video0") < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// if (open_isp("/dev/video13", "/dev/video14", "/dev/video15") < 0) {
|
||||
// goto error;
|
||||
// }
|
||||
|
||||
// return;
|
||||
|
||||
if (device_stream(camera, true) < 0) {
|
||||
goto error;
|
||||
}
|
||||
//return;
|
||||
|
||||
while(true) {
|
||||
if (buffer_list_wait_pool(camera->capture_list, 1000000)) {
|
||||
buffer_t *buf;
|
||||
if (buf = buffer_list_capture_dequeue(camera->capture_list)) {
|
||||
E_LOG_INFO(camera, "Got camera buffer: %p", buf);
|
||||
buffer_capture_enqueue(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
device_close(isp_yuuv_low);
|
||||
device_close(isp_yuuv);
|
||||
device_close(isp_srgb);
|
||||
device_close(camera);
|
||||
return 0;
|
||||
}
|
69
v4l2.c
Normal file
69
v4l2.c
Normal file
@ -0,0 +1,69 @@
|
||||
#include "v4l2.h"
|
||||
|
||||
int xioctl(int fd, int request, void *arg)
|
||||
{
|
||||
int retries = XIOCTL_RETRIES;
|
||||
int retval = -1;
|
||||
|
||||
do {
|
||||
retval = ioctl(fd, request, arg);
|
||||
} while (
|
||||
retval
|
||||
&& retries--
|
||||
&& (
|
||||
errno == EINTR
|
||||
|| errno == EAGAIN
|
||||
|| errno == ETIMEDOUT
|
||||
)
|
||||
);
|
||||
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (retval && retries <= 0) {
|
||||
E_LOG_PERROR(NULL, "ioctl(%d) retried %u times; giving up", request, XIOCTL_RETRIES);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
fourcc_string fourcc_to_string(unsigned format)
|
||||
{
|
||||
fourcc_string fourcc;
|
||||
char *ptr = fourcc.buf;
|
||||
*ptr++ = format & 0x7F;
|
||||
*ptr++ = (format >> 8) & 0x7F;
|
||||
*ptr++ = (format >> 16) & 0x7F;
|
||||
*ptr++ = (format >> 24) & 0x7F;
|
||||
if (format & ((unsigned)1 << 31)) {
|
||||
*ptr++ = '-';
|
||||
*ptr++ = 'B';
|
||||
*ptr++ = 'E';
|
||||
*ptr++ = '\0';
|
||||
} else {
|
||||
*ptr++ = '\0';
|
||||
}
|
||||
*ptr++ = 0;
|
||||
return fourcc;
|
||||
}
|
||||
|
||||
static size_t align_size(size_t size, size_t to)
|
||||
{
|
||||
return ((size + (to - 1)) & ~(to - 1));
|
||||
}
|
||||
|
||||
unsigned fourcc_to_stride(unsigned width, unsigned format)
|
||||
{
|
||||
switch (format) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
return align_size(width * 2, 32);
|
||||
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
return align_size(width * 3, 32);
|
||||
|
||||
case V4L2_PIX_FMT_SRGGB10P:
|
||||
return align_size(width * 5 / 4, 32);
|
||||
|
||||
default:
|
||||
E_LOG_PERROR(NULL, "Unknown format: %s", fourcc_to_string(format));
|
||||
}
|
||||
}
|
46
v4l2.h
Normal file
46
v4l2.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#ifndef CFG_XIOCTL_RETRIES
|
||||
# define CFG_XIOCTL_RETRIES 4
|
||||
#endif
|
||||
#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES))
|
||||
|
||||
// assumes that name is first item
|
||||
#define dev_name(dev) (dev ? *(const char**)dev : "?")
|
||||
#define E_LOG_ERROR(dev, _msg, ...) do { fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__); goto error; } while(0)
|
||||
#define E_LOG_PERROR(dev, _msg, ...) do { fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__); exit(-1); } while(0)
|
||||
#define E_LOG_INFO(dev, _msg, ...) fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__)
|
||||
#define E_LOG_VERBOSE(dev, _msg, ...) fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__)
|
||||
#define E_LOG_DEBUG(dev, _msg, ...) fprintf(stderr, "%s: " _msg "\n", dev_name(dev), ##__VA_ARGS__)
|
||||
|
||||
typedef struct {
|
||||
char buf[10];
|
||||
} fourcc_string;
|
||||
|
||||
fourcc_string fourcc_to_string(unsigned format);
|
||||
unsigned fourcc_to_stride(unsigned width, unsigned format);
|
||||
int xioctl(int fd, int request, void *arg);
|
||||
|
||||
#define E_XIOCTL(dev, _fd, _request, _value, _msg, ...) do { \
|
||||
int ret; \
|
||||
if ((ret = xioctl(_fd, _request, _value)) < 0) { \
|
||||
E_LOG_ERROR(dev, "xioctl(ret=%d): " _msg, ret, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
Loading…
x
Reference in New Issue
Block a user