Organize sources
This commit is contained in:
78
hw/buffer.c
Normal file
78
hw/buffer.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "hw/buffer.h"
|
||||
#include "hw/buffer_list.h"
|
||||
#include "hw/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;
|
||||
buf->mmap_reflinks = 1;
|
||||
buf->used = 0;
|
||||
|
||||
buf->v4l2_buffer.type = buf_list->type;
|
||||
buf->v4l2_buffer.index = index;
|
||||
|
||||
if (buf_list->do_mplanes) {
|
||||
buf->v4l2_buffer.length = 1;
|
||||
buf->v4l2_buffer.m.planes = &buf->v4l2_plane;
|
||||
buf->v4l2_plane.data_offset = 0;
|
||||
}
|
||||
|
||||
if (buf_list->do_mmap) {
|
||||
buf->v4l2_buffer.memory = V4L2_MEMORY_MMAP;
|
||||
} else {
|
||||
buf->v4l2_buffer.memory = V4L2_MEMORY_DMABUF;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (buf_list->do_mmap) {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
26
hw/buffer.h
Normal file
26
hw/buffer.h
Normal file
@ -0,0 +1,26 @@
|
||||
#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 used;
|
||||
size_t length;
|
||||
struct v4l2_buffer v4l2_buffer;
|
||||
struct v4l2_plane v4l2_plane;
|
||||
int dma_fd;
|
||||
|
||||
int mmap_reflinks;
|
||||
struct buffer_s *mmap_source;
|
||||
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_use(buffer_t *buf);
|
||||
bool buffer_consumed(buffer_t *buf);
|
217
hw/buffer_list.c
Normal file
217
hw/buffer_list.c
Normal file
@ -0,0 +1,217 @@
|
||||
#include "hw/buffer.h"
|
||||
#include "hw/buffer_list.h"
|
||||
#include "hw/device.h"
|
||||
|
||||
buffer_list_t *buffer_list_open(const char *name, struct device_s *dev, unsigned type, bool do_mmap)
|
||||
{
|
||||
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:
|
||||
buf_list->do_mmap = do_mmap;
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
|
||||
buf_list->do_mmap = do_mmap;
|
||||
buf_list->do_mplanes = true;
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
||||
buf_list->do_dma = do_mmap;
|
||||
buf_list->do_mmap = do_mmap;
|
||||
buf_list->do_capture = true;
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
|
||||
buf_list->do_dma = do_mmap;
|
||||
buf_list->do_mmap = do_mmap;
|
||||
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;
|
||||
|
||||
bool retried = false;
|
||||
unsigned orig_width = width;
|
||||
unsigned orig_height = height;
|
||||
unsigned stride = 0;
|
||||
|
||||
retry:
|
||||
|
||||
if (strstr(buf_list->name, "JPEG")) {
|
||||
width = (width) / 16 * 16;
|
||||
height = (height) / 16 * 16;
|
||||
stride = orig_width * 2;
|
||||
printf("JPEG: %dx%d vs %dx%d\n", orig_width, orig_height, width, height);
|
||||
}
|
||||
|
||||
if (buf_list->do_mplanes) {
|
||||
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 = stride;// fourcc_to_stride(orig_width, format);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
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_INFO(buf_list, "Requested resolution=%ux%u is unavailable. Got %ux%u.",
|
||||
width, height, fmt->fmt.pix.width, fmt->fmt.pix.height);
|
||||
if (retried) {
|
||||
E_LOG_ERROR(buf_list, "Did retry.");
|
||||
}
|
||||
retried = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
buf_list->fmt_width = fmt->fmt.pix.width;
|
||||
buf_list->fmt_height = fmt->fmt.pix.height;
|
||||
buf_list->fmt_format = fmt->fmt.pix.pixelformat;
|
||||
|
||||
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)
|
||||
{
|
||||
if (!buf_list) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf_list->streaming == do_on) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
buf_list->streaming = do_on;
|
||||
|
||||
for (int i = 0; i < buf_list->nbufs; i++) {
|
||||
buffer_t *buf = buf_list->bufs[i];
|
||||
|
||||
// dequeue buffers (when stream off)
|
||||
// if (buf->enqueued) {
|
||||
// if (buf->mmap_source) {
|
||||
// buf->mmap_source->used = 0;
|
||||
// buffer_consumed(buf->mmap_source);
|
||||
// buf->mmap_source = NULL;
|
||||
// }
|
||||
|
||||
// buf->enqueued = false;
|
||||
// buf->mmap_reflinks = 1;
|
||||
// }
|
||||
|
||||
// re-enqueue buffers on stream start
|
||||
if (buf_list->streaming && buf_list->do_capture && !buf->enqueued && buf->mmap_reflinks == 1) {
|
||||
if (buf_list->do_mmap) {
|
||||
buffer_consumed(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(buf_list, "Streaming %s...", do_on ? "started" : "stopped");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
36
hw/buffer_list.h
Normal file
36
hw/buffer_list.h
Normal file
@ -0,0 +1,36 @@
|
||||
#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;
|
||||
|
||||
unsigned fmt_width, fmt_height, fmt_format;
|
||||
|
||||
bool streaming;
|
||||
int frames;
|
||||
} buffer_list_t;
|
||||
|
||||
buffer_list_t *buffer_list_open(const char *name, struct device_s *dev, unsigned type, bool do_mmap);
|
||||
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);
|
||||
|
||||
int buffer_list_stream(buffer_list_t *buf_list, bool do_on);
|
||||
|
||||
buffer_t *buffer_list_find_slot(buffer_list_t *buf_list);
|
||||
buffer_t *buffer_list_dequeue(buffer_list_t *buf_list);
|
||||
int buffer_list_count_enqueued(buffer_list_t *buf_list);
|
||||
|
||||
int buffer_list_enqueue(buffer_list_t *buf_list, buffer_t *dma_buf);
|
80
hw/buffer_lock.c
Normal file
80
hw/buffer_lock.c
Normal file
@ -0,0 +1,80 @@
|
||||
#include "hw/buffer_lock.h"
|
||||
|
||||
bool buffer_lock_is_used(buffer_lock_t *buf_lock)
|
||||
{
|
||||
int refs = 0;
|
||||
|
||||
pthread_mutex_lock(&buf_lock->lock);
|
||||
refs = buf_lock->refs;
|
||||
pthread_mutex_unlock(&buf_lock->lock);
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
void buffer_lock_use(buffer_lock_t *buf_lock, int ref)
|
||||
{
|
||||
pthread_mutex_lock(&buf_lock->lock);
|
||||
buf_lock->refs += ref;
|
||||
pthread_mutex_unlock(&buf_lock->lock);
|
||||
}
|
||||
|
||||
bool buffer_lock_needs_buffer(buffer_lock_t *buf_lock)
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
bool needs_buffer = false;
|
||||
|
||||
pthread_mutex_lock(&buf_lock->lock);
|
||||
if (now.tv_sec - buf_lock->buf_time.tv_sec > 1) {
|
||||
buffer_consumed(buf_lock->buf);
|
||||
buf_lock->buf = NULL;
|
||||
needs_buffer = true;
|
||||
}
|
||||
if (buf_lock->refs > 0) {
|
||||
needs_buffer = true;
|
||||
}
|
||||
pthread_mutex_unlock(&buf_lock->lock);
|
||||
|
||||
return needs_buffer;
|
||||
}
|
||||
|
||||
void buffer_lock_capture(buffer_lock_t *buf_lock, buffer_t *buf)
|
||||
{
|
||||
pthread_mutex_lock(&buf_lock->lock);
|
||||
buffer_consumed(buf_lock->buf);
|
||||
buffer_use(buf);
|
||||
buf_lock->buf = buf;
|
||||
buf_lock->counter++;
|
||||
gettimeofday(&buf_lock->buf_time, NULL);
|
||||
E_LOG_DEBUG(buf_lock, "Captured HTTP snapshot: %d", buf_lock->counter);
|
||||
pthread_cond_broadcast(&buf_lock->cond_wait);
|
||||
pthread_mutex_unlock(&buf_lock->lock);
|
||||
}
|
||||
|
||||
buffer_t *buffer_lock_get(buffer_lock_t *buf_lock, int timeout_s, int *counter)
|
||||
{
|
||||
buffer_t *buf = NULL;
|
||||
struct timeval now;
|
||||
struct timespec timeout;
|
||||
gettimeofday(&now, NULL);
|
||||
timeout.tv_nsec = now.tv_usec;
|
||||
timeout.tv_sec = now.tv_sec + timeout_s;
|
||||
|
||||
pthread_mutex_lock(&buf_lock->lock);
|
||||
if (*counter == buf_lock->counter || !buf_lock->buf) {
|
||||
int ret = pthread_cond_timedwait(&buf_lock->cond_wait, &buf_lock->lock, &timeout);
|
||||
if (ret == ETIMEDOUT) {
|
||||
goto ret;
|
||||
} else if (ret < 0) {
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf_lock->buf;
|
||||
buffer_use(buf);
|
||||
*counter = buf_lock->counter;
|
||||
|
||||
ret:
|
||||
pthread_mutex_unlock(&buf_lock->lock);
|
||||
return buf;
|
||||
}
|
23
hw/buffer_lock.h
Normal file
23
hw/buffer_lock.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct buffer_lock_s {
|
||||
const char *name;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond_wait;
|
||||
buffer_t *buf;
|
||||
struct timeval buf_time;
|
||||
int counter;
|
||||
int refs;
|
||||
} buffer_lock_t;
|
||||
|
||||
#define DEFINE_BUFFER_LOCK(name) static buffer_lock_t name = { #name, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, NULL, 0 };
|
||||
|
||||
void buffer_lock_capture(buffer_lock_t *buf_lock, buffer_t *buf);
|
||||
buffer_t *buffer_lock_get(buffer_lock_t *buf_lock, int timeout_s, int *counter);
|
||||
bool buffer_lock_needs_buffer(buffer_lock_t *buf_lock);
|
||||
void buffer_lock_use(buffer_lock_t *buf_lock, int ref);
|
||||
bool buffer_lock_is_used(buffer_lock_t *buf_lock);
|
197
hw/buffer_queue.c
Normal file
197
hw/buffer_queue.c
Normal file
@ -0,0 +1,197 @@
|
||||
#include "hw/buffer.h"
|
||||
#include "hw/buffer_list.h"
|
||||
#include "hw/device.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
bool buffer_use(buffer_t *buf)
|
||||
{
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&buffer_lock);
|
||||
if (!buf->enqueued) {
|
||||
pthread_mutex_unlock(&buffer_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
buf->mmap_reflinks += 1;
|
||||
pthread_mutex_unlock(&buffer_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool buffer_consumed(buffer_t *buf)
|
||||
{
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&buffer_lock);
|
||||
if (buf->mmap_reflinks > 0) {
|
||||
buf->mmap_reflinks--;
|
||||
}
|
||||
|
||||
if (!buf->enqueued && buf->mmap_reflinks == 0) {
|
||||
// update used bytes
|
||||
if (buf->buf_list->do_mplanes) {
|
||||
buf->v4l2_plane.bytesused = buf->used;
|
||||
buf->v4l2_plane.length = buf->length;
|
||||
buf->v4l2_plane.data_offset = 0;
|
||||
} else {
|
||||
buf->v4l2_buffer.bytesused = buf->used;
|
||||
}
|
||||
|
||||
#if 0
|
||||
uint64_t now = get_now_monotonic_u64();
|
||||
struct timeval ts = {
|
||||
.tv_sec = now / 1000000,
|
||||
.tv_usec = now % 1000000,
|
||||
};
|
||||
|
||||
buf->v4l2_buffer.timestamp.tv_sec = ts.tv_sec;
|
||||
buf->v4l2_buffer.timestamp.tv_usec = ts.tv_usec;
|
||||
#endif
|
||||
|
||||
E_LOG_DEBUG(buf, "Queuing buffer... used=%zu length=%zu (linked=%s)", buf->used, buf->length, buf->mmap_source ? buf->mmap_source->name : NULL);
|
||||
E_XIOCTL(buf, buf->buf_list->device->fd, VIDIOC_QBUF, &buf->v4l2_buffer, "Can't queue buffer.");
|
||||
buf->enqueued = true;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&buffer_lock);
|
||||
return true;
|
||||
|
||||
|
||||
error:
|
||||
{
|
||||
buffer_t *mmap_source = buf->mmap_source;
|
||||
buf->mmap_source = NULL;
|
||||
|
||||
pthread_mutex_unlock(&buffer_lock);
|
||||
|
||||
if (mmap_source) {
|
||||
buffer_consumed(mmap_source);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer_t *buffer_list_find_slot(buffer_list_t *buf_list)
|
||||
{
|
||||
buffer_t *buf = NULL;
|
||||
|
||||
for (int i = 0; i < buf_list->nbufs; i++) {
|
||||
if (!buf_list->bufs[i]->enqueued) {
|
||||
buf = buf_list->bufs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int buffer_list_count_enqueued(buffer_list_t *buf_list)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < buf_list->nbufs; i++) {
|
||||
if (buf_list->bufs[i]->enqueued) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int buffer_list_enqueue(buffer_list_t *buf_list, buffer_t *dma_buf)
|
||||
{
|
||||
if (!buf_list->do_mmap && !dma_buf->buf_list->do_mmap) {
|
||||
E_LOG_PERROR(buf_list, "Cannot enqueue non-mmap to non-mmap: %s.", dma_buf->name);
|
||||
}
|
||||
|
||||
buffer_t *buf = buffer_list_find_slot(buf_list);
|
||||
if (!buf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buf_list->do_mmap) {
|
||||
if (dma_buf->used > buf->length) {
|
||||
E_LOG_PERROR(buf_list, "The dma_buf (%s) is too long: %zu vs space=%zu",
|
||||
dma_buf->name, dma_buf->used, buf->length);
|
||||
}
|
||||
|
||||
struct timespec before, after;
|
||||
clock_gettime(CLOCK_MONOTONIC, &before);
|
||||
memcpy(buf->start, dma_buf->start, dma_buf->used);
|
||||
clock_gettime(CLOCK_MONOTONIC, &after);
|
||||
uint64_t time_diff = after.tv_sec * 1000000LL + after.tv_nsec / 1000 - before.tv_sec * 1000000LL - before.tv_nsec / 1000;
|
||||
|
||||
E_LOG_DEBUG(buf, "mmap copy: dest=%p, src=%p (%s), size=%zu, space=%zu, time=%dllus",
|
||||
buf->start, dma_buf->start, dma_buf->name, dma_buf->used, buf->length, time_diff);
|
||||
} else {
|
||||
if (buf_list->do_mplanes) {
|
||||
buf->v4l2_plane.m.fd = dma_buf->dma_fd;
|
||||
} else {
|
||||
buf->v4l2_buffer.m.fd = dma_buf->dma_fd;
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(buf, "dmabuf copy: dest=%p, src=%p (%s, dma_fd=%d), size=%zu",
|
||||
buf->start, dma_buf->start, dma_buf->name, dma_buf->dma_fd, dma_buf->used);
|
||||
|
||||
buf->mmap_source = dma_buf;
|
||||
buf->length = dma_buf->length;
|
||||
dma_buf->mmap_reflinks++;
|
||||
}
|
||||
|
||||
buf->used = dma_buf->used;
|
||||
buffer_consumed(buf);
|
||||
return 1;
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_t *buffer_list_dequeue(buffer_list_t *buf_list)
|
||||
{
|
||||
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");
|
||||
|
||||
buffer_t *buf = buf_list->bufs[v4l2_buf.index];
|
||||
if (buf_list->do_mplanes) {
|
||||
buf->used = v4l2_plane.bytesused;
|
||||
} else {
|
||||
buf->used = v4l2_buf.bytesused;
|
||||
}
|
||||
buf->v4l2_buffer.flags = v4l2_buf.flags;
|
||||
|
||||
buf->enqueued = false;
|
||||
buf->mmap_reflinks = 1;
|
||||
|
||||
E_LOG_DEBUG(buf_list, "Grabbed mmap buffer=%u, bytes=%d, used=%d, frame=%d, linked=%s",
|
||||
buf->index, buf->length, buf->used, buf_list->frames, buf->mmap_source ? buf->mmap_source->name : NULL);
|
||||
|
||||
if (buf->mmap_source) {
|
||||
buf->mmap_source->used = 0;
|
||||
buffer_consumed(buf->mmap_source);
|
||||
buf->mmap_source = NULL;
|
||||
}
|
||||
|
||||
buf_list->frames++;
|
||||
return buf;
|
||||
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
190
hw/device.c
Normal file
190
hw/device.c
Normal file
@ -0,0 +1,190 @@
|
||||
#include "hw/device.h"
|
||||
#include "hw/buffer.h"
|
||||
#include "hw/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);
|
||||
dev->allow_dma = true;
|
||||
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;
|
||||
bool do_mmap = false;
|
||||
|
||||
if (do_capture) {
|
||||
buf_list = &dev->capture_list;
|
||||
do_mmap = true;
|
||||
|
||||
if (dev->capture_list) {
|
||||
E_LOG_ERROR(dev, "The capture_list is already created.");
|
||||
}
|
||||
|
||||
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 | V4L2_CAP_VIDEO_M2M_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: %08x", dev->v4l2_cap.capabilities);
|
||||
}
|
||||
} else {
|
||||
buf_list = &dev->output_list;
|
||||
do_mmap = !dev->allow_dma;
|
||||
|
||||
if (dev->output_list) {
|
||||
E_LOG_ERROR(dev, "The output_list is already created.");
|
||||
}
|
||||
|
||||
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 | V4L2_CAP_VIDEO_M2M_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: %08x", dev->v4l2_cap.capabilities);
|
||||
}
|
||||
}
|
||||
|
||||
*buf_list = buffer_list_open(name, dev, type, do_mmap);
|
||||
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;
|
||||
}
|
||||
|
||||
int device_force_key(device_t *dev)
|
||||
{
|
||||
struct v4l2_control ctl = {0};
|
||||
ctl.id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
|
||||
ctl.value = 1;
|
||||
E_LOG_DEBUG(dev, "Forcing keyframe ...");
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
|
||||
return 0;
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int device_set_fps(device_t *dev, int desired_fps)
|
||||
{
|
||||
struct v4l2_streamparm setfps = {0};
|
||||
setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
setfps.parm.output.timeperframe.numerator = 1;
|
||||
setfps.parm.output.timeperframe.denominator = desired_fps;
|
||||
E_LOG_DEBUG(dev, "Configuring FPS ...");
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_S_PARM, &setfps, "Can't set FPS");
|
||||
return 0;
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int device_set_option(device_t *dev, const char *name, uint32_t id, int32_t value)
|
||||
{
|
||||
struct v4l2_control ctl = {0};
|
||||
ctl.id = id;
|
||||
ctl.value = value;
|
||||
E_LOG_DEBUG(dev, "Configuring option %s (%08x) = %d", name, id, value);
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", name);
|
||||
return 0;
|
||||
error:
|
||||
return -1;
|
||||
}
|
36
hw/device.h
Normal file
36
hw/device.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "v4l2.h"
|
||||
|
||||
typedef struct device_s {
|
||||
char *name;
|
||||
char *path;
|
||||
int fd;
|
||||
struct v4l2_capability v4l2_cap;
|
||||
bool allow_dma;
|
||||
|
||||
struct buffer_list_s *capture_list;
|
||||
struct buffer_list_s *output_list;
|
||||
|
||||
struct device_s *output_device;
|
||||
} 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);
|
||||
int device_force_key(device_t *dev);
|
||||
int device_set_option(device_t *dev, const char *name, uint32_t id, int32_t value);
|
||||
int device_set_fps(device_t *dev, int desired_fps);
|
||||
|
||||
#define DEVICE_SET_OPTION(dev, name, value) \
|
||||
device_set_option(dev, #name, V4L2_CID_##name, value)
|
||||
|
||||
#define DEVICE_SET_OPTION2(dev, type, name, value) \
|
||||
device_set_option(dev, #name, V4L2_CID_##type##_##name, value)
|
||||
|
||||
#define DEVICE_SET_OPTION2_FATAL(dev, type, name, value) \
|
||||
do { if (DEVICE_SET_OPTION2(dev, type, name, value) < 0) return -1; } while(0)
|
228
hw/links.c
Normal file
228
hw/links.c
Normal file
@ -0,0 +1,228 @@
|
||||
#include "hw/device.h"
|
||||
#include "hw/buffer.h"
|
||||
#include "hw/buffer_list.h"
|
||||
#include "hw/links.h"
|
||||
|
||||
#define N_FDS 50
|
||||
|
||||
int _build_fds(link_t *all_links, struct pollfd *fds, link_t **links, buffer_list_t **buf_lists, int max_n)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; all_links[i].capture; i++) {
|
||||
link_t *link = &all_links[i];
|
||||
|
||||
if (!link->capture || !link->capture->capture_list || n >= max_n) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!link->capture->capture_list->do_mmap) {
|
||||
continue;
|
||||
}
|
||||
if (!link->capture->capture_list->streaming) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool can_consume = true;
|
||||
|
||||
for (int j = 0; link->outputs[j]; j++) {
|
||||
device_t *output = link->outputs[j];
|
||||
|
||||
if (!output || !output->output_list || n >= max_n) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!output->output_list->streaming) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int count_enqueued = buffer_list_count_enqueued(output->output_list);
|
||||
|
||||
if (count_enqueued == output->output_list->nbufs) {
|
||||
E_LOG_DEBUG(link->capture->capture_list, "Cannot consume due to %s using %d of %d",
|
||||
output->output_list->name, count_enqueued, output->output_list->nbufs);
|
||||
can_consume = false;
|
||||
}
|
||||
|
||||
// Can something be dequeued?
|
||||
if (count_enqueued == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct pollfd fd = {output->fd, POLLOUT};
|
||||
fds[n] = fd;
|
||||
buf_lists[n] = output->output_list;
|
||||
links[n] = link;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (can_consume) {
|
||||
struct pollfd fd = {link->capture->fd, POLLIN};
|
||||
fds[n] = fd;
|
||||
buf_lists[n] = link->capture->capture_list;
|
||||
links[n] = link;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int links_step(link_t *all_links, int timeout)
|
||||
{
|
||||
struct pollfd fds[N_FDS] = {0};
|
||||
link_t *links[N_FDS];
|
||||
buffer_list_t *buf_lists[N_FDS];
|
||||
buffer_t *buf;
|
||||
int n = _build_fds(all_links, fds, links, buf_lists, N_FDS);
|
||||
int ret = poll(fds, n, timeout);
|
||||
|
||||
if (ret < 0 && errno != EINTR) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
printf("links_step n=%d, ret=%d\n", n, ret);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
buffer_list_t *buf_list = buf_lists[i];
|
||||
link_t *link = links[i];
|
||||
|
||||
E_LOG_DEBUG(buf_list, "pool i=%d revents=%08x streaming=%d enqueued=%d/%d", i, fds[i].revents, buf_list->streaming,
|
||||
buffer_list_count_enqueued(buf_list), buf_list->nbufs);
|
||||
|
||||
if (fds[i].revents & POLLIN) {
|
||||
E_LOG_DEBUG(buf_list, "POLLIN");
|
||||
if (buf = buffer_list_dequeue(buf_list)) {
|
||||
for (int j = 0; link->outputs[j]; j++) {
|
||||
buffer_list_enqueue(link->outputs[j]->output_list, buf);
|
||||
}
|
||||
|
||||
if (link->callbacks.on_buffer) {
|
||||
link->callbacks.on_buffer(buf);
|
||||
}
|
||||
|
||||
buffer_consumed(buf);
|
||||
}
|
||||
}
|
||||
if (fds[i].revents & POLLOUT) {
|
||||
E_LOG_DEBUG(buf_list, "POLLOUT");
|
||||
buffer_list_dequeue(buf_list);
|
||||
}
|
||||
if (fds[i].revents & POLLERR) {
|
||||
E_LOG_DEBUG(buf_list, "POLLERR");
|
||||
device_consume_event(buf_list->device);
|
||||
}
|
||||
if (fds[i].revents & ~(POLLIN|POLLOUT|POLLERR)) {
|
||||
E_LOG_DEBUG(buf_list, "POLL%08x", fds[i].revents);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int links_stream(link_t *all_links, bool do_stream)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; all_links[n].capture; n++);
|
||||
|
||||
for (int i = n; --i >= 0; ) {
|
||||
bool streaming = true;
|
||||
link_t *link = &all_links[i];
|
||||
|
||||
if (link->callbacks.check_streaming) {
|
||||
streaming = link->callbacks.check_streaming();
|
||||
}
|
||||
|
||||
if (buffer_list_stream(link->capture->capture_list, streaming) < 0) {
|
||||
E_LOG_ERROR(link->capture, "Failed to start streaming");
|
||||
}
|
||||
|
||||
for (int j = 0; link->outputs[j]; j++) {
|
||||
if (buffer_list_stream(link->outputs[j]->output_list, streaming) < 0) {
|
||||
E_LOG_ERROR(link->outputs[j], "Failed to start streaming");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
// int links_open_buffer_list_from(device_t *dev, bool do_capture, buffer_list_t *parent_buffer, unsigned format)
|
||||
// {
|
||||
// if (!parent_buffer) {
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
// return device_open_buffer_list(
|
||||
// dev,
|
||||
// do_capture,
|
||||
// parent_buffer->fmt_width,
|
||||
// parent_buffer->fmt_height,
|
||||
// format ? format : parent_buffer->fmt_format,
|
||||
// parent_buffer->nbufs
|
||||
// );
|
||||
// }
|
||||
|
||||
// int links_init(link_t *all_links)
|
||||
// {
|
||||
// // create all outputs (sinks)
|
||||
// for (int i = 0; all_links[i].capture; i++) {
|
||||
// link_t *link = &all_links[i];
|
||||
|
||||
// if (!link->capture) {
|
||||
// E_LOG_ERROR(NULL, "Missing link capture.");
|
||||
// }
|
||||
|
||||
// if (link->capture_format) {
|
||||
// int ret = links_open_buffer_list_from(
|
||||
// link->capture,
|
||||
// true,
|
||||
// link->capture->upstream_device ? link->capture->upstream_device->output_list : link->capture->output_list,
|
||||
// link->capture_format
|
||||
// );
|
||||
|
||||
// if (ret < 0) {
|
||||
// E_LOG_ERROR(link->capture, "Failed to create capture_list.");
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!link->capture->capture_list) {
|
||||
// E_LOG_ERROR(link->capture, "Missing capture device.");
|
||||
// }
|
||||
|
||||
// for (int j = 0; link->outputs[j]; j++) {
|
||||
// device_t *output = link->outputs[j];
|
||||
|
||||
// int ret = links_open_buffer_list_from(output, false, link->capture->capture_list, 0);
|
||||
// if (ret < 0) {
|
||||
// E_LOG_ERROR(output, "Failed to create output_list.");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
|
||||
// error:
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
int links_loop(link_t *all_links, bool *running)
|
||||
{
|
||||
*running = true;
|
||||
|
||||
if (links_stream(all_links, true) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while(*running) {
|
||||
if (links_step(all_links, 1000) < 0) {
|
||||
links_stream(all_links, false);
|
||||
return -1;
|
||||
}
|
||||
//usleep(100*1000);
|
||||
}
|
||||
|
||||
links_stream(all_links, false);
|
||||
return 0;
|
||||
}
|
19
hw/links.h
Normal file
19
hw/links.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "v4l2.h"
|
||||
|
||||
typedef void (*link_on_buffer)(struct buffer_s *buf);
|
||||
typedef bool (*link_check_streaming)();
|
||||
|
||||
typedef struct link_s {
|
||||
struct device_s *capture; // capture_list
|
||||
struct device_s *outputs[10];
|
||||
struct {
|
||||
link_on_buffer on_buffer;
|
||||
link_check_streaming check_streaming;
|
||||
} callbacks;
|
||||
} link_t;
|
||||
|
||||
int links_init(link_t *all_links);
|
||||
int links_step(link_t *all_links, int timeout);
|
||||
int links_loop(link_t *all_links, bool *running);
|
76
hw/v4l2.c
Normal file
76
hw/v4l2.c
Normal file
@ -0,0 +1,76 @@
|
||||
#include "hw/v4l2.h"
|
||||
|
||||
int xioctl(const char *name, 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, "%s: ioctl(%08x) retried %u times; giving up", name, 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_YUV420:
|
||||
return align_size(width * 3 / 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);
|
||||
|
||||
case V4L2_PIX_FMT_JPEG:
|
||||
case V4L2_PIX_FMT_H264:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
E_LOG_PERROR(NULL, "Unknown format: %s", fourcc_to_string(format).buf);
|
||||
}
|
||||
}
|
47
hw/v4l2.h
Normal file
47
hw/v4l2.h
Normal file
@ -0,0 +1,47 @@
|
||||
#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 <pthread.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(const char *name, int fd, int request, void *arg);
|
||||
|
||||
#define E_XIOCTL(dev, _fd, _request, _value, _msg, ...) do { \
|
||||
int ret; \
|
||||
if ((ret = xioctl(dev_name(dev), _fd, _request, _value)) < 0) { \
|
||||
E_LOG_ERROR(dev, "xioctl(ret=%d): " _msg, ret, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
Reference in New Issue
Block a user