feat: double buffered video input
This commit is contained in:
@@ -68,6 +68,7 @@ static void init_context(const Parameters *params) {
|
||||
memset(context->input_resolutions, 0, sizeof(context->input_resolutions));
|
||||
memset(context->input_formats, 0, sizeof(context->input_formats));
|
||||
memset(context->input_fps, 0, sizeof(context->input_fps));
|
||||
memset(context->input_swap, 0, sizeof(context->input_swap));
|
||||
}
|
||||
|
||||
static void free_context() { shared_close_context(context); }
|
||||
|
||||
+32
-8
@@ -88,8 +88,10 @@ static void rebind_textures(const ShaderProgram *program) {
|
||||
|
||||
#ifdef VIDEO_IN
|
||||
static void link_input_to_texture(ShaderProgram *program, VideoCapture *input,
|
||||
unsigned int texture_index) {
|
||||
input->dma_image = EGL_NO_IMAGE_KHR;
|
||||
unsigned int texture_index, bool swap) {
|
||||
EGLImageKHR dma_image;
|
||||
|
||||
dma_image = EGL_NO_IMAGE_KHR;
|
||||
|
||||
// https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
|
||||
const EGLint attrib_list[] = {EGL_WIDTH,
|
||||
@@ -99,18 +101,23 @@ static void link_input_to_texture(ShaderProgram *program, VideoCapture *input,
|
||||
EGL_LINUX_DRM_FOURCC_EXT,
|
||||
input->pixelformat,
|
||||
EGL_DMA_BUF_PLANE0_FD_EXT,
|
||||
input->exp_fd,
|
||||
swap ? input->exp_fd_swap : input->exp_fd,
|
||||
EGL_DMA_BUF_PLANE0_OFFSET_EXT,
|
||||
0,
|
||||
EGL_DMA_BUF_PLANE0_PITCH_EXT,
|
||||
input->bytesperline,
|
||||
EGL_NONE};
|
||||
|
||||
input->dma_image =
|
||||
eglCreateImageKHR(program->egl_display, EGL_NO_CONTEXT,
|
||||
EGL_LINUX_DMA_BUF_EXT, NULL, attrib_list);
|
||||
dma_image = eglCreateImageKHR(program->egl_display, EGL_NO_CONTEXT,
|
||||
EGL_LINUX_DMA_BUF_EXT, NULL, attrib_list);
|
||||
|
||||
if (input->dma_image == EGL_NO_IMAGE_KHR) {
|
||||
if (swap) {
|
||||
input->dma_image_swap = dma_image;
|
||||
} else {
|
||||
input->dma_image = dma_image;
|
||||
}
|
||||
|
||||
if (dma_image == EGL_NO_IMAGE_KHR) {
|
||||
log_error("(%s) eglCreateImageKHR failed %04x", input->name, eglGetError());
|
||||
return;
|
||||
}
|
||||
@@ -137,7 +144,12 @@ static void init_input(ShaderProgram *program, const ConfigFile *config,
|
||||
if (i < inputs->length && !inputs->values[i].error) {
|
||||
snprintf(name, STR_LEN, "IN_%d_OUT", i + 1);
|
||||
tex_i = config_file_get_int(config, name, 0);
|
||||
link_input_to_texture(program, &inputs->values[i], tex_i);
|
||||
link_input_to_texture(program, &inputs->values[i], tex_i, false);
|
||||
if (inputs->values[i].with_swap) {
|
||||
snprintf(name, STR_LEN, "IN_%d_SWAP_OUT", i + 1);
|
||||
tex_i = config_file_get_int(config, name, 0);
|
||||
link_input_to_texture(program, &inputs->values[i], tex_i, true);
|
||||
}
|
||||
} else {
|
||||
log_warn("Cannot link input %d", i + 1);
|
||||
}
|
||||
@@ -321,6 +333,13 @@ static void init_single_program(ShaderProgram *program, unsigned int i,
|
||||
glGetUniformLocation(program->programs[i], name);
|
||||
}
|
||||
|
||||
prefix = config_file_get_str(config, "UNIFORM_IN_SWAP_PREFIX", "iInputSwap");
|
||||
for (unsigned int j = 0; j < program->in_count; j++) {
|
||||
snprintf(name, STR_LEN, "%s%d", prefix, j + 1);
|
||||
program->iinswap_locations[i * program->in_count + j] =
|
||||
glGetUniformLocation(program->programs[i], name);
|
||||
}
|
||||
|
||||
prefix = config_file_get_str(config, "UNIFORM_SEED_PREFIX", "iSeed");
|
||||
for (unsigned int j = 0; j < program->frag_count; j++) {
|
||||
snprintf(name, STR_LEN, "%s%d", prefix, j + 1);
|
||||
@@ -491,6 +510,8 @@ static void use_program(const ShaderProgram *program, int i, bool output,
|
||||
context->input_formats[j]);
|
||||
write_uniform_1i(program->iinfps_locations[i * program->in_count + j],
|
||||
context->input_fps[j]);
|
||||
write_uniform_1i(program->iinswap_locations[i * program->in_count + j],
|
||||
context->input_swap[j] ? 1 : 0);
|
||||
}
|
||||
|
||||
// set seeds uniforms
|
||||
@@ -673,5 +694,8 @@ void shaders_free_input(const ShaderProgram *program,
|
||||
if (!input->error && input->dma_image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(program->egl_display, input->dma_image);
|
||||
}
|
||||
if (!input->error && input->dma_image_swap != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(program->egl_display, input->dma_image_swap);
|
||||
}
|
||||
#endif /* VIDEO_IN */
|
||||
}
|
||||
@@ -104,6 +104,7 @@ typedef struct ShaderProgram {
|
||||
GLuint iinres_locations[ARRAY_SIZE];
|
||||
GLuint iinfmt_locations[ARRAY_SIZE];
|
||||
GLuint iinfps_locations[ARRAY_SIZE];
|
||||
GLuint iinswap_locations[ARRAY_SIZE];
|
||||
GLuint idemo_locations[ARRAY_SIZE];
|
||||
GLuint iautorand_locations[ARRAY_SIZE];
|
||||
GLuint iautorandcycle_locations[ARRAY_SIZE];
|
||||
@@ -137,15 +138,19 @@ typedef struct ShaderProgram {
|
||||
typedef struct VideoCapture {
|
||||
char name[STR_LEN];
|
||||
bool error;
|
||||
bool with_swap;
|
||||
int fd;
|
||||
int exp_fd;
|
||||
int exp_fd_swap;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int pixelformat;
|
||||
unsigned int bytesperline;
|
||||
#ifdef VIDEO_IN
|
||||
struct v4l2_buffer buf;
|
||||
struct v4l2_buffer buf_swap;
|
||||
EGLImageKHR dma_image;
|
||||
EGLImageKHR dma_image_swap;
|
||||
#endif /* VIDEO_IN */
|
||||
} VideoCapture;
|
||||
|
||||
@@ -190,6 +195,7 @@ typedef struct SharedContext {
|
||||
unsigned int fps;
|
||||
unsigned int input_formats[MAX_VIDEO];
|
||||
unsigned int input_fps[MAX_VIDEO];
|
||||
bool input_swap[MAX_VIDEO];
|
||||
bool stop;
|
||||
} SharedContext;
|
||||
|
||||
|
||||
+59
-19
@@ -75,7 +75,6 @@ static void open_device(VideoCapture *video_capture, const char *name) {
|
||||
strlcpy(video_capture->name, name, STR_LEN);
|
||||
video_capture->error = false;
|
||||
video_capture->fd = -1;
|
||||
video_capture->exp_fd = -1;
|
||||
|
||||
video_capture->fd = open(name, O_RDWR | O_NONBLOCK);
|
||||
if (video_capture->fd == -1) {
|
||||
@@ -201,7 +200,7 @@ static bool request_buffers(VideoCapture *video_capture) {
|
||||
|
||||
reqbuf.type = buf_type;
|
||||
reqbuf.memory = V4L2_MEMORY_MMAP;
|
||||
reqbuf.count = 1;
|
||||
reqbuf.count = 2;
|
||||
|
||||
if (ioctl(video_capture->fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
|
||||
ioctl_error(video_capture, "VIDIOC_REQBUFS",
|
||||
@@ -211,18 +210,21 @@ static bool request_buffers(VideoCapture *video_capture) {
|
||||
|
||||
log_info("(%s) V4L2 Buffer Count: %d", video_capture->name, reqbuf.count);
|
||||
|
||||
video_capture->with_swap = reqbuf.count > 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool export_buffer(VideoCapture *video_capture) {
|
||||
static bool export_buffer(VideoCapture *video_capture, int *fd,
|
||||
unsigned int index) {
|
||||
struct v4l2_exportbuffer expbuf;
|
||||
|
||||
video_capture->exp_fd = -1;
|
||||
*fd = -1;
|
||||
|
||||
memset(&expbuf, 0, sizeof(expbuf));
|
||||
|
||||
expbuf.type = buf_type;
|
||||
expbuf.index = 0;
|
||||
expbuf.index = index;
|
||||
expbuf.flags = O_RDONLY;
|
||||
|
||||
if (ioctl(video_capture->fd, VIDIOC_EXPBUF, &expbuf) == -1) {
|
||||
@@ -232,11 +234,24 @@ static bool export_buffer(VideoCapture *video_capture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video_capture->exp_fd = expbuf.fd;
|
||||
*fd = expbuf.fd;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool export_buffers(VideoCapture *video_capture) {
|
||||
bool result;
|
||||
|
||||
result = export_buffer(video_capture, &video_capture->exp_fd, 0);
|
||||
|
||||
if (result && video_capture->with_swap) {
|
||||
result =
|
||||
result && export_buffer(video_capture, &video_capture->exp_fd_swap, 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool open_stream(VideoCapture *video_capture) {
|
||||
if (ioctl(video_capture->fd, VIDIOC_STREAMON, &buf_type) == -1) {
|
||||
ioctl_error(
|
||||
@@ -248,30 +263,47 @@ static bool open_stream(VideoCapture *video_capture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void create_image_buffer(VideoCapture *video_capture) {
|
||||
memset(&video_capture->buf, 0, sizeof(video_capture->buf));
|
||||
static void create_image_buffer(const VideoCapture *video_capture,
|
||||
struct v4l2_buffer *buf, unsigned int index) {
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
|
||||
video_capture->buf.type = buf_type;
|
||||
video_capture->buf.memory = V4L2_MEMORY_MMAP;
|
||||
video_capture->buf.index = 0;
|
||||
buf->type = buf_type;
|
||||
buf->memory = V4L2_MEMORY_MMAP;
|
||||
buf->index = index;
|
||||
|
||||
ioctl(video_capture->fd, VIDIOC_PREPARE_BUF);
|
||||
ioctl(video_capture->fd, VIDIOC_QBUF, buf);
|
||||
}
|
||||
|
||||
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf);
|
||||
static void create_image_buffers(VideoCapture *video_capture) {
|
||||
create_image_buffer(video_capture, &video_capture->buf, 0);
|
||||
|
||||
if (video_capture->with_swap) {
|
||||
create_image_buffer(video_capture, &video_capture->buf_swap, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_stream(const VideoCapture *video_capture) {
|
||||
ioctl(video_capture->fd, VIDIOC_STREAMOFF, &buf_type);
|
||||
}
|
||||
|
||||
static bool read_video(VideoCapture *video_capture) {
|
||||
static unsigned int read_video(const VideoCapture *video_capture) {
|
||||
int result;
|
||||
|
||||
result = 0;
|
||||
|
||||
if (ioctl(video_capture->fd, VIDIOC_DQBUF, &video_capture->buf) != -1) {
|
||||
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf);
|
||||
|
||||
return true;
|
||||
result = 1;
|
||||
} else if (video_capture->with_swap &&
|
||||
ioctl(video_capture->fd, VIDIOC_DQBUF, &video_capture->buf_swap) !=
|
||||
-1) {
|
||||
ioctl(video_capture->fd, VIDIOC_QBUF, &video_capture->buf_swap);
|
||||
|
||||
result = 2;
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void video_init(VideoCapture *video_capture, const char *name,
|
||||
@@ -298,7 +330,7 @@ void video_init(VideoCapture *video_capture, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!export_buffer(video_capture)) {
|
||||
if (!export_buffers(video_capture)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -306,7 +338,7 @@ void video_init(VideoCapture *video_capture, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
create_image_buffer(video_capture);
|
||||
create_image_buffers(video_capture);
|
||||
}
|
||||
|
||||
bool video_background_read(VideoCapture *video_capture, SharedContext *context,
|
||||
@@ -314,6 +346,7 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
|
||||
pid_t pid;
|
||||
Timer timer;
|
||||
double fps;
|
||||
unsigned int video_result;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
@@ -328,7 +361,8 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
|
||||
timer_init(&timer, 30);
|
||||
|
||||
while (!context->stop) {
|
||||
if (read_video(video_capture) && timer_inc(&timer)) {
|
||||
video_result = read_video(video_capture);
|
||||
if (video_result > 0 && timer_inc(&timer)) {
|
||||
fps = timer_reset(&timer);
|
||||
|
||||
context->input_fps[input_index] = (unsigned int)round(fps);
|
||||
@@ -336,6 +370,9 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
|
||||
log_trace("(%s) %.2ffps", video_capture->name, fps);
|
||||
}
|
||||
}
|
||||
if (video_result > 0) {
|
||||
context->input_swap[input_index] = video_result == 2;
|
||||
}
|
||||
}
|
||||
if (context->stop) {
|
||||
log_info("(%s) background acquisition stopped by main thread (pid: %d)",
|
||||
@@ -355,6 +392,9 @@ void video_free(const VideoCapture *video_capture) {
|
||||
if (video_capture->exp_fd != -1) {
|
||||
close(video_capture->exp_fd);
|
||||
}
|
||||
if (video_capture->exp_fd_swap != -1) {
|
||||
close(video_capture->exp_fd);
|
||||
}
|
||||
if (video_capture->fd != -1) {
|
||||
close(video_capture->fd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user