blob: 2d67c9c36a57932542cee538b6bbb560e7de59de [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
Jim Ostrowski8565b402020-02-29 20:26:53 -08009DEFINE_bool(ignore_timestamps, false,
10 "Don't require timestamps on images. Used to allow webcams");
11
Stephan Pleinesf63bde82024-01-13 15:59:33 -080012namespace frc971::vision {
Brian Silverman9dd793b2020-01-31 23:52:21 -080013
Austin Schuh77d0bbd2022-12-26 14:00:51 -080014V4L2ReaderBase::V4L2ReaderBase(aos::EventLoop *event_loop,
milind-ufd08c432023-02-05 15:15:21 -080015 std::string_view device_name,
16 std::string_view image_channel)
17 : fd_(open(device_name.data(), O_RDWR | O_NONBLOCK)),
18 event_loop_(event_loop),
19 image_channel_(image_channel) {
Jim Ostrowskifec0c332022-02-06 23:28:26 -080020 PCHECK(fd_.get() != -1) << " Failed to open device " << device_name;
Brian Silverman9dd793b2020-01-31 23:52:21 -080021
Austin Schuh77d0bbd2022-12-26 14:00:51 -080022 // Figure out if we are multi-planar or not.
23 {
24 struct v4l2_capability capability;
25 memset(&capability, 0, sizeof(capability));
26 PCHECK(Ioctl(VIDIOC_QUERYCAP, &capability) == 0);
27
28 LOG(INFO) << "Opening " << device_name;
29 LOG(INFO) << " driver " << capability.driver;
30 LOG(INFO) << " card " << capability.card;
31 LOG(INFO) << " bus_info " << capability.bus_info;
32 if (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
33 LOG(INFO) << " Multi-planar";
34 multiplanar_ = true;
35 }
36 }
37
Brian Silverman9dd793b2020-01-31 23:52:21 -080038 // First, clean up after anybody else who left the device streaming.
Brian Silverman8f24adb2020-02-02 17:15:58 -080039 StreamOff();
Austin Schuh77d0bbd2022-12-26 14:00:51 -080040}
Brian Silverman9dd793b2020-01-31 23:52:21 -080041
Austin Schuh77d0bbd2022-12-26 14:00:51 -080042void V4L2ReaderBase::StreamOn() {
Brian Silverman9dd793b2020-01-31 23:52:21 -080043 {
44 struct v4l2_requestbuffers request;
45 memset(&request, 0, sizeof(request));
46 request.count = buffers_.size();
Austin Schuh77d0bbd2022-12-26 14:00:51 -080047 request.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
48 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
Brian Silverman9dd793b2020-01-31 23:52:21 -080049 request.memory = V4L2_MEMORY_USERPTR;
50 PCHECK(Ioctl(VIDIOC_REQBUFS, &request) == 0);
51 CHECK_EQ(request.count, buffers_.size())
52 << ": Kernel refused to give us the number of buffers we asked for";
53 }
54
Austin Schuh77d0bbd2022-12-26 14:00:51 -080055 {
56 struct v4l2_format format;
57 memset(&format, 0, sizeof(format));
58 format.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
59 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
60 PCHECK(Ioctl(VIDIOC_G_FMT, &format) == 0);
61
62 if (multiplanar()) {
63 cols_ = format.fmt.pix_mp.width;
64 rows_ = format.fmt.pix_mp.height;
65 LOG(INFO) << "Format is " << cols_ << ", " << rows_;
66 CHECK_EQ(format.fmt.pix_mp.pixelformat, V4L2_PIX_FMT_YUYV)
67 << ": Invalid pixel format";
68
69 CHECK_EQ(format.fmt.pix_mp.num_planes, 1u);
70
71 CHECK_EQ(static_cast<int>(format.fmt.pix_mp.plane_fmt[0].bytesperline),
72 cols_ * 2 /* bytes per pixel */);
73 CHECK_EQ(format.fmt.pix_mp.plane_fmt[0].sizeimage, ImageSize());
74 } else {
75 cols_ = format.fmt.pix.width;
76 rows_ = format.fmt.pix.height;
77 LOG(INFO) << "Format is " << cols_ << ", " << rows_;
78 CHECK_EQ(format.fmt.pix.pixelformat, V4L2_PIX_FMT_YUYV)
79 << ": Invalid pixel format";
80
81 CHECK_EQ(static_cast<int>(format.fmt.pix.bytesperline),
82 cols_ * 2 /* bytes per pixel */);
83 CHECK_EQ(format.fmt.pix.sizeimage, ImageSize());
84 }
85 }
86
Brian Silverman9dd793b2020-01-31 23:52:21 -080087 for (size_t i = 0; i < buffers_.size(); ++i) {
milind-ufd08c432023-02-05 15:15:21 -080088 buffers_[i].sender = event_loop_->MakeSender<CameraImage>(image_channel_);
Ravago Jonesc6b919f2023-01-01 21:34:12 -080089 MarkBufferToBeEnqueued(i);
Brian Silverman9dd793b2020-01-31 23:52:21 -080090 }
Austin Schuh77d0bbd2022-12-26 14:00:51 -080091 int type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
92 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
93 PCHECK(Ioctl(VIDIOC_STREAMON, &type) == 0);
Brian Silverman9dd793b2020-01-31 23:52:21 -080094}
95
Ravago Jonesc6b919f2023-01-01 21:34:12 -080096void V4L2ReaderBase::MarkBufferToBeEnqueued(int buffer_index) {
97 ReinitializeBuffer(buffer_index);
98 EnqueueBuffer(buffer_index);
99}
100
Ravago Jones65469be2023-01-13 21:28:23 -0800101void V4L2ReaderBase::MaybeEnqueue() {
Jim Ostrowski977850f2022-01-22 21:04:22 -0800102 // First, enqueue any old buffer we already have. This is the one which
103 // may have been sent.
Brian Silverman967e5df2020-02-09 16:43:34 -0800104 if (saved_buffer_) {
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800105 MarkBufferToBeEnqueued(saved_buffer_.index);
Brian Silverman967e5df2020-02-09 16:43:34 -0800106 saved_buffer_.Clear();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800107 }
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800108 ftrace_.FormatMessage("Enqueued previous buffer %d", saved_buffer_.index);
Ravago Jones65469be2023-01-13 21:28:23 -0800109}
110
111bool V4L2ReaderBase::ReadLatestImage() {
112 MaybeEnqueue();
113
Brian Silverman9dd793b2020-01-31 23:52:21 -0800114 while (true) {
Brian Silverman967e5df2020-02-09 16:43:34 -0800115 const BufferInfo previous_buffer = saved_buffer_;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800116 saved_buffer_ = DequeueBuffer();
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800117 ftrace_.FormatMessage("Dequeued %d", saved_buffer_.index);
Brian Silverman967e5df2020-02-09 16:43:34 -0800118 if (saved_buffer_) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800119 // We got a new buffer. Return the previous one (if relevant) and keep
120 // going.
Brian Silverman967e5df2020-02-09 16:43:34 -0800121 if (previous_buffer) {
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800122 ftrace_.FormatMessage("Previous %d", previous_buffer.index);
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800123 MarkBufferToBeEnqueued(previous_buffer.index);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800124 }
125 continue;
126 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800127 if (!previous_buffer) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800128 // There were no images to read. Return an indication of that.
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800129 ftrace_.FormatMessage("No images to read");
Brian Silverman967e5df2020-02-09 16:43:34 -0800130 return false;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800131 }
132 // We didn't get a new one, but we already got one in a previous
133 // iteration, which means we found an image so return it.
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800134 ftrace_.FormatMessage("Got saved buffer %d", saved_buffer_.index);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800135 saved_buffer_ = previous_buffer;
Brian Silverman967e5df2020-02-09 16:43:34 -0800136 buffers_[saved_buffer_.index].PrepareMessage(rows_, cols_, ImageSize(),
137 saved_buffer_.monotonic_eof);
138 return true;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800139 }
140}
141
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800142void V4L2ReaderBase::SendLatestImage() {
143 buffers_[saved_buffer_.index].Send();
144
145 MarkBufferToBeEnqueued(saved_buffer_.index);
146 saved_buffer_.Clear();
147}
Brian Silverman967e5df2020-02-09 16:43:34 -0800148
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800149void V4L2ReaderBase::SetExposure(size_t duration) {
milind-udaebe9b2022-01-09 18:25:24 -0800150 v4l2_control manual_control;
151 manual_control.id = V4L2_CID_EXPOSURE_AUTO;
152 manual_control.value = V4L2_EXPOSURE_MANUAL;
153 PCHECK(Ioctl(VIDIOC_S_CTRL, &manual_control) == 0);
154
155 v4l2_control exposure_control;
156 exposure_control.id = V4L2_CID_EXPOSURE_ABSOLUTE;
157 exposure_control.value = static_cast<int>(duration); // 100 micro s units
158 PCHECK(Ioctl(VIDIOC_S_CTRL, &exposure_control) == 0);
159}
160
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800161void V4L2ReaderBase::UseAutoExposure() {
milind-udaebe9b2022-01-09 18:25:24 -0800162 v4l2_control control;
163 control.id = V4L2_CID_EXPOSURE_AUTO;
164 control.value = V4L2_EXPOSURE_AUTO;
165 PCHECK(Ioctl(VIDIOC_S_CTRL, &control) == 0);
166}
167
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800168void V4L2ReaderBase::Buffer::InitializeMessage(size_t max_image_size) {
Brian Silverman967e5df2020-02-09 16:43:34 -0800169 message_offset = flatbuffers::Offset<CameraImage>();
170 builder = aos::Sender<CameraImage>::Builder();
171 builder = sender.MakeBuilder();
172 // The kernel has an undocumented requirement that the buffer is aligned
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800173 // to 128 bytes. If you give it a nonaligned pointer, it will return EINVAL
Brian Silverman967e5df2020-02-09 16:43:34 -0800174 // and only print something in dmesg with the relevant dynamic debug
175 // prints turned on.
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800176 builder.fbb()->StartIndeterminateVector(max_image_size, 1, 128,
177 &data_pointer);
178 CHECK_EQ(reinterpret_cast<uintptr_t>(data_pointer) % 128, 0u)
Brian Silverman967e5df2020-02-09 16:43:34 -0800179 << ": Flatbuffers failed to align things as requested";
180}
181
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800182void V4L2ReaderBase::Buffer::PrepareMessage(
Brian Silverman967e5df2020-02-09 16:43:34 -0800183 int rows, int cols, size_t image_size,
184 aos::monotonic_clock::time_point monotonic_eof) {
185 CHECK(data_pointer != nullptr);
186 data_pointer = nullptr;
187
188 const auto data_offset = builder.fbb()->EndIndeterminateVector(image_size, 1);
189 auto image_builder = builder.MakeBuilder<CameraImage>();
190 image_builder.add_data(data_offset);
191 image_builder.add_rows(rows);
192 image_builder.add_cols(cols);
193 image_builder.add_monotonic_timestamp_ns(
194 std::chrono::nanoseconds(monotonic_eof.time_since_epoch()).count());
195 message_offset = image_builder.Finish();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800196}
197
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800198int V4L2ReaderBase::Ioctl(unsigned long number, void *arg) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800199 return ioctl(fd_.get(), number, arg);
200}
201
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800202V4L2ReaderBase::BufferInfo V4L2ReaderBase::DequeueBuffer() {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800203 struct v4l2_buffer buffer;
204 memset(&buffer, 0, sizeof(buffer));
Brian Silverman9dd793b2020-01-31 23:52:21 -0800205 buffer.memory = V4L2_MEMORY_USERPTR;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800206 if (multiplanar()) {
207 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
208 struct v4l2_plane planes[1];
209 std::memset(planes, 0, sizeof(planes));
210 buffer.m.planes = planes;
211 buffer.length = 1;
212 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
213 if (result == -1 && errno == EAGAIN) {
214 return BufferInfo();
215 }
216 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
217 CHECK_LT(buffer.index, buffers_.size());
218
219 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
220 planes[0].m.userptr);
221
222 CHECK_EQ(ImageSize(), planes[0].length);
223 } else {
224 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
225 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
226 if (result == -1 && errno == EAGAIN) {
227 return BufferInfo();
228 }
229 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
230 CHECK_LT(buffer.index, buffers_.size());
231 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
232 buffer.m.userptr);
233 CHECK_EQ(ImageSize(), buffer.length);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800234 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800235 CHECK(buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC);
Jim Ostrowski8565b402020-02-29 20:26:53 -0800236 if (!FLAGS_ignore_timestamps) {
237 // Require that we have good timestamp on images
238 CHECK_EQ(buffer.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK,
239 static_cast<uint32_t>(V4L2_BUF_FLAG_TSTAMP_SRC_EOF));
240 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800241 return {static_cast<int>(buffer.index),
242 aos::time::from_timeval(buffer.timestamp)};
Brian Silverman9dd793b2020-01-31 23:52:21 -0800243}
244
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800245void V4L2ReaderBase::EnqueueBuffer(int buffer_number) {
246 // TODO(austin): Detect multiplanar and do this all automatically.
247
Brian Silverman9dd793b2020-01-31 23:52:21 -0800248 CHECK_GE(buffer_number, 0);
249 CHECK_LT(buffer_number, static_cast<int>(buffers_.size()));
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800250 CHECK(buffers_[buffer_number].data_pointer != nullptr);
251
Brian Silverman9dd793b2020-01-31 23:52:21 -0800252 struct v4l2_buffer buffer;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800253 struct v4l2_plane planes[1];
Brian Silverman9dd793b2020-01-31 23:52:21 -0800254 memset(&buffer, 0, sizeof(buffer));
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800255 memset(&planes, 0, sizeof(planes));
Brian Silverman9dd793b2020-01-31 23:52:21 -0800256 buffer.memory = V4L2_MEMORY_USERPTR;
257 buffer.index = buffer_number;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800258 if (multiplanar()) {
259 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
260 buffer.m.planes = planes;
261 buffer.length = 1;
262 planes[0].m.userptr =
263 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
264 planes[0].length = ImageSize();
265 planes[0].bytesused = planes[0].length;
266 } else {
267 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
268 buffer.m.userptr =
269 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
270 buffer.length = ImageSize();
271 }
272
Brian Silverman9dd793b2020-01-31 23:52:21 -0800273 PCHECK(Ioctl(VIDIOC_QBUF, &buffer) == 0);
274}
275
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800276void V4L2ReaderBase::StreamOff() {
277 int type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
278 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
Brian Silverman8f24adb2020-02-02 17:15:58 -0800279 const int result = Ioctl(VIDIOC_STREAMOFF, &type);
280 if (result == 0) {
281 return;
282 }
Jim Ostrowski977850f2022-01-22 21:04:22 -0800283 // Some devices (like Alex's webcam) return this if streaming isn't
284 // currently on, unlike what the documentations says should happen.
Brian Silverman8f24adb2020-02-02 17:15:58 -0800285 if (errno == EBUSY) {
286 return;
287 }
288 PLOG(FATAL) << "VIDIOC_STREAMOFF failed";
289}
290
milind-ufd08c432023-02-05 15:15:21 -0800291V4L2Reader::V4L2Reader(aos::EventLoop *event_loop, std::string_view device_name,
292 std::string_view image_channel)
293 : V4L2ReaderBase(event_loop, device_name, image_channel) {
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800294 // Don't know why this magic call to SetExposure is required (before the
295 // camera settings are configured) to make things work on boot of the pi, but
296 // it seems to be-- without it, the image exposure is wrong (too dark). Note--
297 // any valid value seems to work-- just choosing 1 for now
298
299 SetExposure(1);
300
301 struct v4l2_format format;
302 memset(&format, 0, sizeof(format));
303 format.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
304 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
milind-u6b094092023-01-09 19:26:12 -0800305
306 constexpr int kWidth = 640;
307 constexpr int kHeight = 480;
308 format.fmt.pix.width = kWidth;
309 format.fmt.pix.height = kHeight;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800310 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
311 // This means we want to capture from a progressive (non-interlaced)
312 // source.
313 format.fmt.pix.field = V4L2_FIELD_NONE;
314 PCHECK(Ioctl(VIDIOC_S_FMT, &format) == 0);
milind-u6b094092023-01-09 19:26:12 -0800315 CHECK_EQ(static_cast<int>(format.fmt.pix.width), kWidth);
316 CHECK_EQ(static_cast<int>(format.fmt.pix.height), kHeight);
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800317 CHECK_EQ(static_cast<int>(format.fmt.pix.bytesperline),
milind-u6b094092023-01-09 19:26:12 -0800318 kWidth * 2 /* bytes per pixel */);
319 CHECK_EQ(format.fmt.pix.sizeimage, ImageSize(kHeight, kWidth));
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800320
321 StreamOn();
322}
323
324RockchipV4L2Reader::RockchipV4L2Reader(aos::EventLoop *event_loop,
Ravago Jonesdc524752022-12-27 01:15:13 -0800325 aos::internal::EPoll *epoll,
milind-ufd08c432023-02-05 15:15:21 -0800326 std::string_view device_name,
327 std::string_view image_sensor_subdev,
328 std::string_view image_channel)
329 : V4L2ReaderBase(event_loop, device_name, image_channel),
Ravago Jones65469be2023-01-13 21:28:23 -0800330 epoll_(epoll),
milind-ufd08c432023-02-05 15:15:21 -0800331 image_sensor_fd_(open(image_sensor_subdev.data(), O_RDWR | O_NONBLOCK)),
Austin Schuh2cd5fe82023-02-04 16:21:19 -0800332 buffer_requeuer_([this](int buffer) { EnqueueBuffer(buffer); },
333 kEnqueueFifoPriority) {
Ravago Jones65469be2023-01-13 21:28:23 -0800334 PCHECK(image_sensor_fd_.get() != -1)
335 << " Failed to open device " << device_name;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800336 StreamOn();
Ravago Jonesdc524752022-12-27 01:15:13 -0800337 epoll_->OnReadable(fd().get(), [this]() { OnImageReady(); });
338}
339
Ravago Jonesfd8aa202023-01-16 14:21:45 -0800340RockchipV4L2Reader::~RockchipV4L2Reader() { epoll_->DeleteFd(fd().get()); }
341
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800342void RockchipV4L2Reader::MarkBufferToBeEnqueued(int buffer) {
343 ReinitializeBuffer(buffer);
344 buffer_requeuer_.Push(buffer);
345}
346
Ravago Jonesdc524752022-12-27 01:15:13 -0800347void RockchipV4L2Reader::OnImageReady() {
348 if (!ReadLatestImage()) {
349 return;
350 }
351
352 SendLatestImage();
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800353}
354
Ravago Jones65469be2023-01-13 21:28:23 -0800355int RockchipV4L2Reader::ImageSensorIoctl(unsigned long number, void *arg) {
356 return ioctl(image_sensor_fd_.get(), number, arg);
357}
358
359void RockchipV4L2Reader::SetExposure(size_t duration) {
360 v4l2_control exposure_control;
361 exposure_control.id = V4L2_CID_EXPOSURE;
362 exposure_control.value = static_cast<int>(duration);
363 PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &exposure_control) == 0);
364}
365
366void RockchipV4L2Reader::SetGain(size_t gain) {
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800367 v4l2_control gain_control;
368 gain_control.id = V4L2_CID_GAIN;
369 gain_control.value = static_cast<int>(gain);
370 PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &gain_control) == 0);
371}
372
373void RockchipV4L2Reader::SetGainExt(size_t gain) {
Ravago Jones65469be2023-01-13 21:28:23 -0800374 struct v4l2_ext_controls controls;
375 memset(&controls, 0, sizeof(controls));
376 struct v4l2_ext_control control[1];
377 memset(&control, 0, sizeof(control));
378
379 controls.ctrl_class = V4L2_CTRL_CLASS_IMAGE_SOURCE;
380 controls.count = 1;
381 controls.controls = control;
382 control[0].id = V4L2_CID_ANALOGUE_GAIN;
383 control[0].value = gain;
384
385 PCHECK(ImageSensorIoctl(VIDIOC_S_EXT_CTRLS, &controls) == 0);
386}
387
Ravago Jonesda1b0082023-01-21 15:33:19 -0800388void RockchipV4L2Reader::SetVerticalBlanking(size_t vblank) {
389 struct v4l2_ext_controls controls;
390 memset(&controls, 0, sizeof(controls));
391 struct v4l2_ext_control control[1];
392 memset(&control, 0, sizeof(control));
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800393
Ravago Jonesda1b0082023-01-21 15:33:19 -0800394 controls.ctrl_class = V4L2_CTRL_CLASS_IMAGE_SOURCE;
395 controls.count = 1;
396 controls.controls = control;
397 control[0].id = V4L2_CID_VBLANK;
398 control[0].value = vblank;
399
400 PCHECK(ImageSensorIoctl(VIDIOC_S_EXT_CTRLS, &controls) == 0);
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800401}
402
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800403} // namespace frc971::vision