2026-06-17 15:52:43 +05:30
|
|
|
#ifdef PLATFORM_LINUX
|
|
|
|
|
|
|
|
|
|
#include "nolibc.h"
|
|
|
|
|
|
2026-05-25 13:50:49 +05:30
|
|
|
#include "ascii.h"
|
2026-06-10 20:30:10 +05:30
|
|
|
#include "capture.h"
|
|
|
|
|
#include "platform.h"
|
2026-05-25 13:50:49 +05:30
|
|
|
|
2026-05-13 22:18:22 +05:30
|
|
|
#include <linux/videodev2.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
2026-06-10 20:30:10 +05:30
|
|
|
typedef struct webcam_impl webcam_impl_t;
|
|
|
|
|
|
|
|
|
|
struct webcam_impl {
|
|
|
|
|
struct v4l2_buffer buf_info;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static webcam_impl_t _impl_storage;
|
2026-05-13 22:18:22 +05:30
|
|
|
|
2026-06-08 13:36:51 +05:30
|
|
|
int webcam_init(webcam_t *cam, const char *device, int width, int height) {
|
2026-06-10 20:30:10 +05:30
|
|
|
cam->impl = &_impl_storage;
|
|
|
|
|
cam->buffer = MAP_FAILED;
|
|
|
|
|
|
|
|
|
|
// Open device non-blocking (for select)
|
|
|
|
|
cam->fd = open(device ? device : "/dev/video0", O_RDWR | O_NONBLOCK, 0);
|
2026-06-08 13:36:51 +05:30
|
|
|
if (cam->fd < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
struct v4l2_format fmt = {0};
|
|
|
|
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
2026-06-10 20:30:10 +05:30
|
|
|
fmt.fmt.pix.width = (unsigned)width;
|
|
|
|
|
fmt.fmt.pix.height = (unsigned)height;
|
2026-06-08 13:36:51 +05:30
|
|
|
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
|
|
|
|
fmt.fmt.pix.field = V4L2_FIELD_NONE;
|
|
|
|
|
|
|
|
|
|
if (ioctl(cam->fd, VIDIOC_S_FMT, &fmt) < 0) {
|
|
|
|
|
close(cam->fd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 20:30:10 +05:30
|
|
|
cam->width = (int)fmt.fmt.pix.width;
|
|
|
|
|
cam->height = (int)fmt.fmt.pix.height;
|
2026-06-08 13:36:51 +05:30
|
|
|
|
|
|
|
|
struct v4l2_requestbuffers req = {0};
|
|
|
|
|
req.count = 1;
|
|
|
|
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
req.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
|
|
|
|
|
if (ioctl(cam->fd, VIDIOC_REQBUFS, &req) < 0) {
|
|
|
|
|
close(cam->fd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct v4l2_buffer buf = {0};
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
|
|
|
buf.index = 0;
|
|
|
|
|
|
|
|
|
|
if (ioctl(cam->fd, VIDIOC_QUERYBUF, &buf) < 0) {
|
|
|
|
|
close(cam->fd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cam->buffer = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
|
2026-06-10 20:30:10 +05:30
|
|
|
cam->fd, (long)buf.m.offset);
|
2026-06-08 13:36:51 +05:30
|
|
|
if (cam->buffer == MAP_FAILED) {
|
|
|
|
|
close(cam->fd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 20:30:10 +05:30
|
|
|
cam->impl->buf_info = buf;
|
2026-06-08 13:36:51 +05:30
|
|
|
|
|
|
|
|
if (ioctl(cam->fd, VIDIOC_QBUF, &buf) < 0) {
|
|
|
|
|
munmap(cam->buffer, buf.length);
|
|
|
|
|
close(cam->fd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
if (ioctl(cam->fd, VIDIOC_STREAMON, &type) < 0) {
|
|
|
|
|
munmap(cam->buffer, buf.length);
|
|
|
|
|
close(cam->fd);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2026-05-13 22:18:22 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int webcam_wait_frame(webcam_t *cam, int timeout_ms) {
|
2026-06-08 13:36:51 +05:30
|
|
|
nl_fd_set fds;
|
|
|
|
|
struct nl_timeval tv;
|
|
|
|
|
NL_FD_ZERO(&fds);
|
|
|
|
|
NL_FD_SET(cam->fd, &fds);
|
|
|
|
|
tv.tv_sec = timeout_ms / 1000;
|
|
|
|
|
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
|
|
|
|
|
|
|
|
|
int ret = nl_select(cam->fd + 1, &fds, (nl_fd_set *)0, (nl_fd_set *)0, &tv);
|
2026-06-10 20:30:10 +05:30
|
|
|
return (ret <= 0) ? -1 : 0;
|
2026-05-13 22:18:22 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int webcam_capture_frame(webcam_t *cam, uint8_t *gray_buffer) {
|
2026-06-10 20:30:10 +05:30
|
|
|
struct v4l2_buffer buf = cam->impl->buf_info;
|
2026-06-08 13:36:51 +05:30
|
|
|
if (ioctl(cam->fd, VIDIOC_DQBUF, &buf) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2026-06-10 20:30:10 +05:30
|
|
|
yuyv_to_gray_simd((uint8_t *)cam->buffer, gray_buffer, cam->width,
|
|
|
|
|
cam->height);
|
|
|
|
|
|
|
|
|
|
cam->impl->buf_info = buf;
|
2026-06-08 13:36:51 +05:30
|
|
|
return 0;
|
2026-05-13 22:18:22 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int webcam_requeue_buffer(webcam_t *cam) {
|
2026-06-10 20:30:10 +05:30
|
|
|
return (ioctl(cam->fd, VIDIOC_QBUF, &cam->impl->buf_info) < 0) ? -1 : 0;
|
2026-05-13 22:18:22 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void webcam_cleanup(webcam_t *cam) {
|
2026-06-08 13:36:51 +05:30
|
|
|
if (cam->fd >= 0) {
|
|
|
|
|
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
ioctl(cam->fd, VIDIOC_STREAMOFF, &type);
|
|
|
|
|
if (cam->buffer != MAP_FAILED)
|
2026-06-10 20:30:10 +05:30
|
|
|
munmap(cam->buffer, cam->impl->buf_info.length);
|
2026-06-08 13:36:51 +05:30
|
|
|
close(cam->fd);
|
|
|
|
|
}
|
|
|
|
|
cam->fd = -1;
|
|
|
|
|
cam->buffer = MAP_FAILED;
|
2026-06-10 20:30:10 +05:30
|
|
|
cam->impl = (webcam_impl_t *)0;
|
2026-05-13 22:18:22 +05:30
|
|
|
}
|
2026-06-10 20:30:10 +05:30
|
|
|
|
2026-06-17 15:52:43 +05:30
|
|
|
#endif
|