blob: e333dc50755b5d42bfa910aaf8f7679d4fc6052e [file] [log] [blame]
Jim Ostrowski977850f2022-01-22 21:04:22 -08001#include "frc971/vision/v4l2_reader.h"
Brian Silverman9dd793b2020-01-31 23:52:21 -08002
3#include <fcntl.h>
4#include <linux/videodev2.h>
5#include <sys/ioctl.h>
6#include <sys/stat.h>
7#include <sys/types.h>
8
Austin Schuh99f7c6a2024-06-25 22:07:44 -07009#include "absl/flags/flag.h"
10
11ABSL_FLAG(bool, ignore_timestamps, false,
12 "Don't require timestamps on images. Used to allow webcams");
Jim Ostrowski8565b402020-02-29 20:26:53 -080013
Stephan Pleinesf63bde82024-01-13 15:59:33 -080014namespace frc971::vision {
Brian Silverman9dd793b2020-01-31 23:52:21 -080015
Austin Schuh77d0bbd2022-12-26 14:00:51 -080016V4L2ReaderBase::V4L2ReaderBase(aos::EventLoop *event_loop,
milind-ufd08c432023-02-05 15:15:21 -080017 std::string_view device_name,
18 std::string_view image_channel)
19 : fd_(open(device_name.data(), O_RDWR | O_NONBLOCK)),
20 event_loop_(event_loop),
21 image_channel_(image_channel) {
Jim Ostrowskifec0c332022-02-06 23:28:26 -080022 PCHECK(fd_.get() != -1) << " Failed to open device " << device_name;
Brian Silverman9dd793b2020-01-31 23:52:21 -080023
Austin Schuh77d0bbd2022-12-26 14:00:51 -080024 // Figure out if we are multi-planar or not.
25 {
26 struct v4l2_capability capability;
27 memset(&capability, 0, sizeof(capability));
28 PCHECK(Ioctl(VIDIOC_QUERYCAP, &capability) == 0);
29
30 LOG(INFO) << "Opening " << device_name;
31 LOG(INFO) << " driver " << capability.driver;
32 LOG(INFO) << " card " << capability.card;
33 LOG(INFO) << " bus_info " << capability.bus_info;
34 if (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
35 LOG(INFO) << " Multi-planar";
36 multiplanar_ = true;
37 }
38 }
39
Brian Silverman9dd793b2020-01-31 23:52:21 -080040 // First, clean up after anybody else who left the device streaming.
Brian Silverman8f24adb2020-02-02 17:15:58 -080041 StreamOff();
Austin Schuh77d0bbd2022-12-26 14:00:51 -080042}
Brian Silverman9dd793b2020-01-31 23:52:21 -080043
Austin Schuh77d0bbd2022-12-26 14:00:51 -080044void V4L2ReaderBase::StreamOn() {
Brian Silverman9dd793b2020-01-31 23:52:21 -080045 {
46 struct v4l2_requestbuffers request;
47 memset(&request, 0, sizeof(request));
48 request.count = buffers_.size();
Austin Schuh77d0bbd2022-12-26 14:00:51 -080049 request.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
50 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
Brian Silverman9dd793b2020-01-31 23:52:21 -080051 request.memory = V4L2_MEMORY_USERPTR;
52 PCHECK(Ioctl(VIDIOC_REQBUFS, &request) == 0);
53 CHECK_EQ(request.count, buffers_.size())
54 << ": Kernel refused to give us the number of buffers we asked for";
55 }
56
Austin Schuh77d0bbd2022-12-26 14:00:51 -080057 {
58 struct v4l2_format format;
59 memset(&format, 0, sizeof(format));
60 format.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
61 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
62 PCHECK(Ioctl(VIDIOC_G_FMT, &format) == 0);
63
64 if (multiplanar()) {
65 cols_ = format.fmt.pix_mp.width;
66 rows_ = format.fmt.pix_mp.height;
67 LOG(INFO) << "Format is " << cols_ << ", " << rows_;
68 CHECK_EQ(format.fmt.pix_mp.pixelformat, V4L2_PIX_FMT_YUYV)
69 << ": Invalid pixel format";
70
71 CHECK_EQ(format.fmt.pix_mp.num_planes, 1u);
72
73 CHECK_EQ(static_cast<int>(format.fmt.pix_mp.plane_fmt[0].bytesperline),
74 cols_ * 2 /* bytes per pixel */);
75 CHECK_EQ(format.fmt.pix_mp.plane_fmt[0].sizeimage, ImageSize());
76 } else {
77 cols_ = format.fmt.pix.width;
78 rows_ = format.fmt.pix.height;
79 LOG(INFO) << "Format is " << cols_ << ", " << rows_;
80 CHECK_EQ(format.fmt.pix.pixelformat, V4L2_PIX_FMT_YUYV)
81 << ": Invalid pixel format";
82
83 CHECK_EQ(static_cast<int>(format.fmt.pix.bytesperline),
84 cols_ * 2 /* bytes per pixel */);
85 CHECK_EQ(format.fmt.pix.sizeimage, ImageSize());
86 }
87 }
88
Brian Silverman9dd793b2020-01-31 23:52:21 -080089 for (size_t i = 0; i < buffers_.size(); ++i) {
milind-ufd08c432023-02-05 15:15:21 -080090 buffers_[i].sender = event_loop_->MakeSender<CameraImage>(image_channel_);
Ravago Jonesc6b919f2023-01-01 21:34:12 -080091 MarkBufferToBeEnqueued(i);
Brian Silverman9dd793b2020-01-31 23:52:21 -080092 }
Austin Schuh77d0bbd2022-12-26 14:00:51 -080093 int type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
94 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
95 PCHECK(Ioctl(VIDIOC_STREAMON, &type) == 0);
Brian Silverman9dd793b2020-01-31 23:52:21 -080096}
97
Ravago Jonesc6b919f2023-01-01 21:34:12 -080098void V4L2ReaderBase::MarkBufferToBeEnqueued(int buffer_index) {
99 ReinitializeBuffer(buffer_index);
100 EnqueueBuffer(buffer_index);
101}
102
Ravago Jones65469be2023-01-13 21:28:23 -0800103void V4L2ReaderBase::MaybeEnqueue() {
Jim Ostrowski977850f2022-01-22 21:04:22 -0800104 // First, enqueue any old buffer we already have. This is the one which
105 // may have been sent.
Brian Silverman967e5df2020-02-09 16:43:34 -0800106 if (saved_buffer_) {
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800107 MarkBufferToBeEnqueued(saved_buffer_.index);
Brian Silverman967e5df2020-02-09 16:43:34 -0800108 saved_buffer_.Clear();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800109 }
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800110 ftrace_.FormatMessage("Enqueued previous buffer %d", saved_buffer_.index);
Ravago Jones65469be2023-01-13 21:28:23 -0800111}
112
113bool V4L2ReaderBase::ReadLatestImage() {
114 MaybeEnqueue();
115
Brian Silverman9dd793b2020-01-31 23:52:21 -0800116 while (true) {
Brian Silverman967e5df2020-02-09 16:43:34 -0800117 const BufferInfo previous_buffer = saved_buffer_;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800118 saved_buffer_ = DequeueBuffer();
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800119 ftrace_.FormatMessage("Dequeued %d", saved_buffer_.index);
Brian Silverman967e5df2020-02-09 16:43:34 -0800120 if (saved_buffer_) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800121 // We got a new buffer. Return the previous one (if relevant) and keep
122 // going.
Brian Silverman967e5df2020-02-09 16:43:34 -0800123 if (previous_buffer) {
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800124 ftrace_.FormatMessage("Previous %d", previous_buffer.index);
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800125 MarkBufferToBeEnqueued(previous_buffer.index);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800126 }
127 continue;
128 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800129 if (!previous_buffer) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800130 // There were no images to read. Return an indication of that.
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800131 ftrace_.FormatMessage("No images to read");
Brian Silverman967e5df2020-02-09 16:43:34 -0800132 return false;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800133 }
134 // We didn't get a new one, but we already got one in a previous
135 // iteration, which means we found an image so return it.
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800136 ftrace_.FormatMessage("Got saved buffer %d", saved_buffer_.index);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800137 saved_buffer_ = previous_buffer;
Brian Silverman967e5df2020-02-09 16:43:34 -0800138 buffers_[saved_buffer_.index].PrepareMessage(rows_, cols_, ImageSize(),
139 saved_buffer_.monotonic_eof);
140 return true;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800141 }
142}
143
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800144void V4L2ReaderBase::SendLatestImage() {
145 buffers_[saved_buffer_.index].Send();
146
147 MarkBufferToBeEnqueued(saved_buffer_.index);
148 saved_buffer_.Clear();
149}
Brian Silverman967e5df2020-02-09 16:43:34 -0800150
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800151void V4L2ReaderBase::SetExposure(size_t duration) {
milind-udaebe9b2022-01-09 18:25:24 -0800152 v4l2_control manual_control;
153 manual_control.id = V4L2_CID_EXPOSURE_AUTO;
154 manual_control.value = V4L2_EXPOSURE_MANUAL;
155 PCHECK(Ioctl(VIDIOC_S_CTRL, &manual_control) == 0);
156
157 v4l2_control exposure_control;
158 exposure_control.id = V4L2_CID_EXPOSURE_ABSOLUTE;
159 exposure_control.value = static_cast<int>(duration); // 100 micro s units
160 PCHECK(Ioctl(VIDIOC_S_CTRL, &exposure_control) == 0);
161}
162
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800163void V4L2ReaderBase::UseAutoExposure() {
milind-udaebe9b2022-01-09 18:25:24 -0800164 v4l2_control control;
165 control.id = V4L2_CID_EXPOSURE_AUTO;
166 control.value = V4L2_EXPOSURE_AUTO;
167 PCHECK(Ioctl(VIDIOC_S_CTRL, &control) == 0);
168}
169
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800170void V4L2ReaderBase::Buffer::InitializeMessage(size_t max_image_size) {
Brian Silverman967e5df2020-02-09 16:43:34 -0800171 message_offset = flatbuffers::Offset<CameraImage>();
172 builder = aos::Sender<CameraImage>::Builder();
173 builder = sender.MakeBuilder();
174 // The kernel has an undocumented requirement that the buffer is aligned
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800175 // to 128 bytes. If you give it a nonaligned pointer, it will return EINVAL
Brian Silverman967e5df2020-02-09 16:43:34 -0800176 // and only print something in dmesg with the relevant dynamic debug
177 // prints turned on.
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800178 builder.fbb()->StartIndeterminateVector(max_image_size, 1, 128,
179 &data_pointer);
180 CHECK_EQ(reinterpret_cast<uintptr_t>(data_pointer) % 128, 0u)
Brian Silverman967e5df2020-02-09 16:43:34 -0800181 << ": Flatbuffers failed to align things as requested";
182}
183
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800184void V4L2ReaderBase::Buffer::PrepareMessage(
Brian Silverman967e5df2020-02-09 16:43:34 -0800185 int rows, int cols, size_t image_size,
186 aos::monotonic_clock::time_point monotonic_eof) {
187 CHECK(data_pointer != nullptr);
188 data_pointer = nullptr;
189
190 const auto data_offset = builder.fbb()->EndIndeterminateVector(image_size, 1);
191 auto image_builder = builder.MakeBuilder<CameraImage>();
192 image_builder.add_data(data_offset);
193 image_builder.add_rows(rows);
194 image_builder.add_cols(cols);
195 image_builder.add_monotonic_timestamp_ns(
196 std::chrono::nanoseconds(monotonic_eof.time_since_epoch()).count());
197 message_offset = image_builder.Finish();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800198}
199
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800200int V4L2ReaderBase::Ioctl(unsigned long number, void *arg) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800201 return ioctl(fd_.get(), number, arg);
202}
203
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800204V4L2ReaderBase::BufferInfo V4L2ReaderBase::DequeueBuffer() {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800205 struct v4l2_buffer buffer;
206 memset(&buffer, 0, sizeof(buffer));
Brian Silverman9dd793b2020-01-31 23:52:21 -0800207 buffer.memory = V4L2_MEMORY_USERPTR;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800208 if (multiplanar()) {
209 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
210 struct v4l2_plane planes[1];
211 std::memset(planes, 0, sizeof(planes));
212 buffer.m.planes = planes;
213 buffer.length = 1;
214 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
215 if (result == -1 && errno == EAGAIN) {
216 return BufferInfo();
217 }
218 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
219 CHECK_LT(buffer.index, buffers_.size());
220
221 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
222 planes[0].m.userptr);
223
224 CHECK_EQ(ImageSize(), planes[0].length);
225 } else {
226 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
227 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
228 if (result == -1 && errno == EAGAIN) {
229 return BufferInfo();
230 }
231 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
232 CHECK_LT(buffer.index, buffers_.size());
233 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
234 buffer.m.userptr);
235 CHECK_EQ(ImageSize(), buffer.length);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800236 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800237 CHECK(buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC);
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700238 if (!absl::GetFlag(FLAGS_ignore_timestamps)) {
Jim Ostrowski8565b402020-02-29 20:26:53 -0800239 // Require that we have good timestamp on images
240 CHECK_EQ(buffer.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK,
241 static_cast<uint32_t>(V4L2_BUF_FLAG_TSTAMP_SRC_EOF));
242 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800243 return {static_cast<int>(buffer.index),
244 aos::time::from_timeval(buffer.timestamp)};
Brian Silverman9dd793b2020-01-31 23:52:21 -0800245}
246
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800247void V4L2ReaderBase::EnqueueBuffer(int buffer_number) {
248 // TODO(austin): Detect multiplanar and do this all automatically.
249
Brian Silverman9dd793b2020-01-31 23:52:21 -0800250 CHECK_GE(buffer_number, 0);
251 CHECK_LT(buffer_number, static_cast<int>(buffers_.size()));
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800252 CHECK(buffers_[buffer_number].data_pointer != nullptr);
253
Brian Silverman9dd793b2020-01-31 23:52:21 -0800254 struct v4l2_buffer buffer;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800255 struct v4l2_plane planes[1];
Brian Silverman9dd793b2020-01-31 23:52:21 -0800256 memset(&buffer, 0, sizeof(buffer));
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800257 memset(&planes, 0, sizeof(planes));
Brian Silverman9dd793b2020-01-31 23:52:21 -0800258 buffer.memory = V4L2_MEMORY_USERPTR;
259 buffer.index = buffer_number;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800260 if (multiplanar()) {
261 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
262 buffer.m.planes = planes;
263 buffer.length = 1;
264 planes[0].m.userptr =
265 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
266 planes[0].length = ImageSize();
267 planes[0].bytesused = planes[0].length;
268 } else {
269 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
270 buffer.m.userptr =
271 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
272 buffer.length = ImageSize();
273 }
274
Brian Silverman9dd793b2020-01-31 23:52:21 -0800275 PCHECK(Ioctl(VIDIOC_QBUF, &buffer) == 0);
276}
277
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800278void V4L2ReaderBase::StreamOff() {
279 int type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
280 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
Brian Silverman8f24adb2020-02-02 17:15:58 -0800281 const int result = Ioctl(VIDIOC_STREAMOFF, &type);
282 if (result == 0) {
283 return;
284 }
Jim Ostrowski977850f2022-01-22 21:04:22 -0800285 // Some devices (like Alex's webcam) return this if streaming isn't
286 // currently on, unlike what the documentations says should happen.
Brian Silverman8f24adb2020-02-02 17:15:58 -0800287 if (errno == EBUSY) {
288 return;
289 }
290 PLOG(FATAL) << "VIDIOC_STREAMOFF failed";
291}
292
milind-ufd08c432023-02-05 15:15:21 -0800293V4L2Reader::V4L2Reader(aos::EventLoop *event_loop, std::string_view device_name,
294 std::string_view image_channel)
295 : V4L2ReaderBase(event_loop, device_name, image_channel) {
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800296 // Don't know why this magic call to SetExposure is required (before the
297 // camera settings are configured) to make things work on boot of the pi, but
298 // it seems to be-- without it, the image exposure is wrong (too dark). Note--
299 // any valid value seems to work-- just choosing 1 for now
300
301 SetExposure(1);
302
303 struct v4l2_format format;
304 memset(&format, 0, sizeof(format));
305 format.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
306 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
milind-u6b094092023-01-09 19:26:12 -0800307
308 constexpr int kWidth = 640;
309 constexpr int kHeight = 480;
310 format.fmt.pix.width = kWidth;
311 format.fmt.pix.height = kHeight;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800312 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
313 // This means we want to capture from a progressive (non-interlaced)
314 // source.
315 format.fmt.pix.field = V4L2_FIELD_NONE;
316 PCHECK(Ioctl(VIDIOC_S_FMT, &format) == 0);
milind-u6b094092023-01-09 19:26:12 -0800317 CHECK_EQ(static_cast<int>(format.fmt.pix.width), kWidth);
318 CHECK_EQ(static_cast<int>(format.fmt.pix.height), kHeight);
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800319 CHECK_EQ(static_cast<int>(format.fmt.pix.bytesperline),
milind-u6b094092023-01-09 19:26:12 -0800320 kWidth * 2 /* bytes per pixel */);
321 CHECK_EQ(format.fmt.pix.sizeimage, ImageSize(kHeight, kWidth));
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800322
323 StreamOn();
324}
325
326RockchipV4L2Reader::RockchipV4L2Reader(aos::EventLoop *event_loop,
Ravago Jonesdc524752022-12-27 01:15:13 -0800327 aos::internal::EPoll *epoll,
milind-ufd08c432023-02-05 15:15:21 -0800328 std::string_view device_name,
329 std::string_view image_sensor_subdev,
330 std::string_view image_channel)
331 : V4L2ReaderBase(event_loop, device_name, image_channel),
Ravago Jones65469be2023-01-13 21:28:23 -0800332 epoll_(epoll),
milind-ufd08c432023-02-05 15:15:21 -0800333 image_sensor_fd_(open(image_sensor_subdev.data(), O_RDWR | O_NONBLOCK)),
Austin Schuh2cd5fe82023-02-04 16:21:19 -0800334 buffer_requeuer_([this](int buffer) { EnqueueBuffer(buffer); },
335 kEnqueueFifoPriority) {
Ravago Jones65469be2023-01-13 21:28:23 -0800336 PCHECK(image_sensor_fd_.get() != -1)
337 << " Failed to open device " << device_name;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800338 StreamOn();
Ravago Jonesdc524752022-12-27 01:15:13 -0800339 epoll_->OnReadable(fd().get(), [this]() { OnImageReady(); });
340}
341
Ravago Jonesfd8aa202023-01-16 14:21:45 -0800342RockchipV4L2Reader::~RockchipV4L2Reader() { epoll_->DeleteFd(fd().get()); }
343
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800344void RockchipV4L2Reader::MarkBufferToBeEnqueued(int buffer) {
345 ReinitializeBuffer(buffer);
346 buffer_requeuer_.Push(buffer);
347}
348
Ravago Jonesdc524752022-12-27 01:15:13 -0800349void RockchipV4L2Reader::OnImageReady() {
350 if (!ReadLatestImage()) {
351 return;
352 }
353
354 SendLatestImage();
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800355}
356
Ravago Jones65469be2023-01-13 21:28:23 -0800357int RockchipV4L2Reader::ImageSensorIoctl(unsigned long number, void *arg) {
358 return ioctl(image_sensor_fd_.get(), number, arg);
359}
360
361void RockchipV4L2Reader::SetExposure(size_t duration) {
362 v4l2_control exposure_control;
363 exposure_control.id = V4L2_CID_EXPOSURE;
364 exposure_control.value = static_cast<int>(duration);
365 PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &exposure_control) == 0);
366}
367
368void RockchipV4L2Reader::SetGain(size_t gain) {
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800369 v4l2_control gain_control;
370 gain_control.id = V4L2_CID_GAIN;
371 gain_control.value = static_cast<int>(gain);
372 PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &gain_control) == 0);
373}
374
375void RockchipV4L2Reader::SetGainExt(size_t gain) {
Ravago Jones65469be2023-01-13 21:28:23 -0800376 struct v4l2_ext_controls controls;
377 memset(&controls, 0, sizeof(controls));
378 struct v4l2_ext_control control[1];
379 memset(&control, 0, sizeof(control));
380
381 controls.ctrl_class = V4L2_CTRL_CLASS_IMAGE_SOURCE;
382 controls.count = 1;
383 controls.controls = control;
384 control[0].id = V4L2_CID_ANALOGUE_GAIN;
385 control[0].value = gain;
386
387 PCHECK(ImageSensorIoctl(VIDIOC_S_EXT_CTRLS, &controls) == 0);
388}
389
Ravago Jonesda1b0082023-01-21 15:33:19 -0800390void RockchipV4L2Reader::SetVerticalBlanking(size_t vblank) {
391 struct v4l2_ext_controls controls;
392 memset(&controls, 0, sizeof(controls));
393 struct v4l2_ext_control control[1];
394 memset(&control, 0, sizeof(control));
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800395
Ravago Jonesda1b0082023-01-21 15:33:19 -0800396 controls.ctrl_class = V4L2_CTRL_CLASS_IMAGE_SOURCE;
397 controls.count = 1;
398 controls.controls = control;
399 control[0].id = V4L2_CID_VBLANK;
400 control[0].value = vblank;
401
402 PCHECK(ImageSensorIoctl(VIDIOC_S_EXT_CTRLS, &controls) == 0);
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800403}
404
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800405} // namespace frc971::vision