-
Notifications
You must be signed in to change notification settings - Fork 0
/
V4L2Capture.cpp
133 lines (118 loc) · 4.46 KB
/
V4L2Capture.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "V4L2Capture.h"
#include <fcntl.h>
#include <stdexcept>
#include <sys/ioctl.h>
#include <csignal>
V4L2Capture::V4L2Capture(std::string_view device, int width, int height, int fps, int buffer_count) : buffer_count(buffer_count) {
if (buffer_count < 1) {
throw std::invalid_argument("Buffer count must be at least 1");
}
// Open the V4L2 device
fd = open(device.data(), O_RDWR);
if (fd == -1) {
throw std::runtime_error("Failed to open V4L2 device");
}
// Set capture format
struct v4l2_format format{};
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = width;
format.fmt.pix.height = height;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
format.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &format) == -1) {
throw std::runtime_error("Failed to set capture format");
}
// Set capture FPS
setFPS(fps);
// Request buffers
struct v4l2_requestbuffers request_buffers{};
request_buffers.count = buffer_count;
request_buffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
request_buffers.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &request_buffers) == -1) {
throw std::runtime_error("Failed to request buffers");
}
// Create buffers
buffers.reserve(buffer_count);
for (int i = 0; i < buffer_count; i++) {
struct v4l2_buffer buffer{};
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = i;
if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) == -1) {
throw std::runtime_error("Failed to query buffer");
}
void* ptr = mmap(nullptr, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer.m.offset);
if (ptr == MAP_FAILED) {
throw std::runtime_error("Failed to map buffer");
}
buffers.emplace_back(ptr, buffer.length, i);
}
// Queue buffers
for (int i = 0; i < buffer_count; i++) {
struct v4l2_buffer buffer{}; // This only serves as a container to pass in an index, the index refers to the memory-mapped buffers in the "buffers" vector
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = i;
if (ioctl(fd, VIDIOC_QBUF, &buffer) == -1) {
throw std::runtime_error("Failed to queue buffer");
}
}
// Start streaming
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
throw std::runtime_error("Failed to start streaming");
}
}
void V4L2Capture::setFPS(int fps) const {
struct v4l2_streamparm stream_params{};
stream_params.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
stream_params.parm.capture.timeperframe.numerator = 1;
stream_params.parm.capture.timeperframe.denominator = fps;
if (ioctl(fd, VIDIOC_S_PARM, &stream_params) == -1) {
throw std::runtime_error("Failed to set capture FPS");
}
}
V4L2Capture::V4L2Capture(V4L2Capture&& other) noexcept : buffer_count(other.buffer_count), buffers(std::move(other.buffers)), fd(other.fd) {
other.fd = -1;
}
V4L2Capture& V4L2Capture::operator=(V4L2Capture&& other) noexcept {
if (this != &other) {
buffer_count = other.buffer_count;
buffers = std::move(other.buffers);
fd = other.fd;
other.fd = -1;
}
return *this;
}
V4L2Capture::~V4L2Capture() {
if (fd != -1) {
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMOFF, &type);
close(fd);
}
}
const V4L2Buffer& V4L2Capture::dequeueBuffer() const {
if (fd == -1) {
throw std::runtime_error("V4L2 device not initialized");
}
struct v4l2_buffer buffer_metadata{};
buffer_metadata.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer_metadata.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_DQBUF, &buffer_metadata) == -1) {
throw std::runtime_error("Failed to dequeue buffer");
}
return buffers[buffer_metadata.index];
}
void V4L2Capture::queueBuffer(const V4L2Buffer& buffer) const {
if (fd == -1) {
throw std::runtime_error("V4L2 device not initialized");
}
struct v4l2_buffer buffer_metadata{};
buffer_metadata.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer_metadata.memory = V4L2_MEMORY_MMAP;
buffer_metadata.index = buffer.get_index();
if (ioctl(fd, VIDIOC_QBUF, &buffer_metadata) == -1) {
throw std::runtime_error("Failed to queue buffer");
}
}