blob: 1404ec557145ed2d8df80127b0f26f3b7e9d634a [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
Brian Silverman9dd793b2020-01-31 23:52:21 -080012namespace frc971 {
13namespace vision {
14
Austin Schuh77d0bbd2022-12-26 14:00:51 -080015V4L2ReaderBase::V4L2ReaderBase(aos::EventLoop *event_loop,
milind-ufd08c432023-02-05 15:15:21 -080016 std::string_view device_name,
17 std::string_view image_channel)
18 : fd_(open(device_name.data(), O_RDWR | O_NONBLOCK)),
19 event_loop_(event_loop),
20 image_channel_(image_channel) {
Jim Ostrowskifec0c332022-02-06 23:28:26 -080021 PCHECK(fd_.get() != -1) << " Failed to open device " << device_name;
Brian Silverman9dd793b2020-01-31 23:52:21 -080022
Austin Schuh77d0bbd2022-12-26 14:00:51 -080023 // Figure out if we are multi-planar or not.
24 {
25 struct v4l2_capability capability;
26 memset(&capability, 0, sizeof(capability));
27 PCHECK(Ioctl(VIDIOC_QUERYCAP, &capability) == 0);
28
29 LOG(INFO) << "Opening " << device_name;
30 LOG(INFO) << " driver " << capability.driver;
31 LOG(INFO) << " card " << capability.card;
32 LOG(INFO) << " bus_info " << capability.bus_info;
33 if (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
34 LOG(INFO) << " Multi-planar";
35 multiplanar_ = true;
36 }
37 }
38
Brian Silverman9dd793b2020-01-31 23:52:21 -080039 // First, clean up after anybody else who left the device streaming.
Brian Silverman8f24adb2020-02-02 17:15:58 -080040 StreamOff();
Austin Schuh77d0bbd2022-12-26 14:00:51 -080041}
Brian Silverman9dd793b2020-01-31 23:52:21 -080042
Austin Schuh77d0bbd2022-12-26 14:00:51 -080043void V4L2ReaderBase::StreamOn() {
Brian Silverman9dd793b2020-01-31 23:52:21 -080044 {
45 struct v4l2_requestbuffers request;
46 memset(&request, 0, sizeof(request));
47 request.count = buffers_.size();
Austin Schuh77d0bbd2022-12-26 14:00:51 -080048 request.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
49 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
Brian Silverman9dd793b2020-01-31 23:52:21 -080050 request.memory = V4L2_MEMORY_USERPTR;
51 PCHECK(Ioctl(VIDIOC_REQBUFS, &request) == 0);
52 CHECK_EQ(request.count, buffers_.size())
53 << ": Kernel refused to give us the number of buffers we asked for";
54 }
55
Austin Schuh77d0bbd2022-12-26 14:00:51 -080056 {
57 struct v4l2_format format;
58 memset(&format, 0, sizeof(format));
59 format.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
60 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
61 PCHECK(Ioctl(VIDIOC_G_FMT, &format) == 0);
62
63 if (multiplanar()) {
64 cols_ = format.fmt.pix_mp.width;
65 rows_ = format.fmt.pix_mp.height;
66 LOG(INFO) << "Format is " << cols_ << ", " << rows_;
67 CHECK_EQ(format.fmt.pix_mp.pixelformat, V4L2_PIX_FMT_YUYV)
68 << ": Invalid pixel format";
69
70 CHECK_EQ(format.fmt.pix_mp.num_planes, 1u);
71
72 CHECK_EQ(static_cast<int>(format.fmt.pix_mp.plane_fmt[0].bytesperline),
73 cols_ * 2 /* bytes per pixel */);
74 CHECK_EQ(format.fmt.pix_mp.plane_fmt[0].sizeimage, ImageSize());
75 } else {
76 cols_ = format.fmt.pix.width;
77 rows_ = format.fmt.pix.height;
78 LOG(INFO) << "Format is " << cols_ << ", " << rows_;
79 CHECK_EQ(format.fmt.pix.pixelformat, V4L2_PIX_FMT_YUYV)
80 << ": Invalid pixel format";
81
82 CHECK_EQ(static_cast<int>(format.fmt.pix.bytesperline),
83 cols_ * 2 /* bytes per pixel */);
84 CHECK_EQ(format.fmt.pix.sizeimage, ImageSize());
85 }
86 }
87
Brian Silverman9dd793b2020-01-31 23:52:21 -080088 for (size_t i = 0; i < buffers_.size(); ++i) {
milind-ufd08c432023-02-05 15:15:21 -080089 buffers_[i].sender = event_loop_->MakeSender<CameraImage>(image_channel_);
Ravago Jonesc6b919f2023-01-01 21:34:12 -080090 MarkBufferToBeEnqueued(i);
Brian Silverman9dd793b2020-01-31 23:52:21 -080091 }
Austin Schuh77d0bbd2022-12-26 14:00:51 -080092 int type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
93 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
94 PCHECK(Ioctl(VIDIOC_STREAMON, &type) == 0);
Brian Silverman9dd793b2020-01-31 23:52:21 -080095}
96
Ravago Jonesc6b919f2023-01-01 21:34:12 -080097void V4L2ReaderBase::MarkBufferToBeEnqueued(int buffer_index) {
98 ReinitializeBuffer(buffer_index);
99 EnqueueBuffer(buffer_index);
100}
101
Ravago Jones65469be2023-01-13 21:28:23 -0800102void V4L2ReaderBase::MaybeEnqueue() {
Jim Ostrowski977850f2022-01-22 21:04:22 -0800103 // First, enqueue any old buffer we already have. This is the one which
104 // may have been sent.
Brian Silverman967e5df2020-02-09 16:43:34 -0800105 if (saved_buffer_) {
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800106 MarkBufferToBeEnqueued(saved_buffer_.index);
Brian Silverman967e5df2020-02-09 16:43:34 -0800107 saved_buffer_.Clear();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800108 }
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800109 ftrace_.FormatMessage("Enqueued previous buffer %d", saved_buffer_.index);
Ravago Jones65469be2023-01-13 21:28:23 -0800110}
111
112bool V4L2ReaderBase::ReadLatestImage() {
113 MaybeEnqueue();
114
Brian Silverman9dd793b2020-01-31 23:52:21 -0800115 while (true) {
Brian Silverman967e5df2020-02-09 16:43:34 -0800116 const BufferInfo previous_buffer = saved_buffer_;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800117 saved_buffer_ = DequeueBuffer();
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800118 ftrace_.FormatMessage("Dequeued %d", saved_buffer_.index);
Brian Silverman967e5df2020-02-09 16:43:34 -0800119 if (saved_buffer_) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800120 // We got a new buffer. Return the previous one (if relevant) and keep
121 // going.
Brian Silverman967e5df2020-02-09 16:43:34 -0800122 if (previous_buffer) {
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800123 ftrace_.FormatMessage("Previous %d", previous_buffer.index);
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800124 MarkBufferToBeEnqueued(previous_buffer.index);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800125 }
126 continue;
127 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800128 if (!previous_buffer) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800129 // There were no images to read. Return an indication of that.
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800130 ftrace_.FormatMessage("No images to read");
Brian Silverman967e5df2020-02-09 16:43:34 -0800131 return false;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800132 }
133 // We didn't get a new one, but we already got one in a previous
134 // iteration, which means we found an image so return it.
Austin Schuhe4acc1e2022-12-26 14:01:34 -0800135 ftrace_.FormatMessage("Got saved buffer %d", saved_buffer_.index);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800136 saved_buffer_ = previous_buffer;
Brian Silverman967e5df2020-02-09 16:43:34 -0800137 buffers_[saved_buffer_.index].PrepareMessage(rows_, cols_, ImageSize(),
138 saved_buffer_.monotonic_eof);
139 return true;
Brian Silverman9dd793b2020-01-31 23:52:21 -0800140 }
141}
142
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800143void V4L2ReaderBase::SendLatestImage() {
144 buffers_[saved_buffer_.index].Send();
145
146 MarkBufferToBeEnqueued(saved_buffer_.index);
147 saved_buffer_.Clear();
148}
Brian Silverman967e5df2020-02-09 16:43:34 -0800149
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800150void V4L2ReaderBase::SetExposure(size_t duration) {
milind-udaebe9b2022-01-09 18:25:24 -0800151 v4l2_control manual_control;
152 manual_control.id = V4L2_CID_EXPOSURE_AUTO;
153 manual_control.value = V4L2_EXPOSURE_MANUAL;
154 PCHECK(Ioctl(VIDIOC_S_CTRL, &manual_control) == 0);
155
156 v4l2_control exposure_control;
157 exposure_control.id = V4L2_CID_EXPOSURE_ABSOLUTE;
158 exposure_control.value = static_cast<int>(duration); // 100 micro s units
159 PCHECK(Ioctl(VIDIOC_S_CTRL, &exposure_control) == 0);
160}
161
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800162void V4L2ReaderBase::UseAutoExposure() {
milind-udaebe9b2022-01-09 18:25:24 -0800163 v4l2_control control;
164 control.id = V4L2_CID_EXPOSURE_AUTO;
165 control.value = V4L2_EXPOSURE_AUTO;
166 PCHECK(Ioctl(VIDIOC_S_CTRL, &control) == 0);
167}
168
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800169void V4L2ReaderBase::Buffer::InitializeMessage(size_t max_image_size) {
Brian Silverman967e5df2020-02-09 16:43:34 -0800170 message_offset = flatbuffers::Offset<CameraImage>();
171 builder = aos::Sender<CameraImage>::Builder();
172 builder = sender.MakeBuilder();
173 // The kernel has an undocumented requirement that the buffer is aligned
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800174 // to 128 bytes. If you give it a nonaligned pointer, it will return EINVAL
Brian Silverman967e5df2020-02-09 16:43:34 -0800175 // and only print something in dmesg with the relevant dynamic debug
176 // prints turned on.
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800177 builder.fbb()->StartIndeterminateVector(max_image_size, 1, 128,
178 &data_pointer);
179 CHECK_EQ(reinterpret_cast<uintptr_t>(data_pointer) % 128, 0u)
Brian Silverman967e5df2020-02-09 16:43:34 -0800180 << ": Flatbuffers failed to align things as requested";
181}
182
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800183void V4L2ReaderBase::Buffer::PrepareMessage(
Brian Silverman967e5df2020-02-09 16:43:34 -0800184 int rows, int cols, size_t image_size,
185 aos::monotonic_clock::time_point monotonic_eof) {
186 CHECK(data_pointer != nullptr);
187 data_pointer = nullptr;
188
189 const auto data_offset = builder.fbb()->EndIndeterminateVector(image_size, 1);
190 auto image_builder = builder.MakeBuilder<CameraImage>();
191 image_builder.add_data(data_offset);
192 image_builder.add_rows(rows);
193 image_builder.add_cols(cols);
194 image_builder.add_monotonic_timestamp_ns(
195 std::chrono::nanoseconds(monotonic_eof.time_since_epoch()).count());
196 message_offset = image_builder.Finish();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800197}
198
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800199int V4L2ReaderBase::Ioctl(unsigned long number, void *arg) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800200 return ioctl(fd_.get(), number, arg);
201}
202
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800203V4L2ReaderBase::BufferInfo V4L2ReaderBase::DequeueBuffer() {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800204 struct v4l2_buffer buffer;
205 memset(&buffer, 0, sizeof(buffer));
Brian Silverman9dd793b2020-01-31 23:52:21 -0800206 buffer.memory = V4L2_MEMORY_USERPTR;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800207 if (multiplanar()) {
208 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
209 struct v4l2_plane planes[1];
210 std::memset(planes, 0, sizeof(planes));
211 buffer.m.planes = planes;
212 buffer.length = 1;
213 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
214 if (result == -1 && errno == EAGAIN) {
215 return BufferInfo();
216 }
217 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
218 CHECK_LT(buffer.index, buffers_.size());
219
220 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
221 planes[0].m.userptr);
222
223 CHECK_EQ(ImageSize(), planes[0].length);
224 } else {
225 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
226 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
227 if (result == -1 && errno == EAGAIN) {
228 return BufferInfo();
229 }
230 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
231 CHECK_LT(buffer.index, buffers_.size());
232 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
233 buffer.m.userptr);
234 CHECK_EQ(ImageSize(), buffer.length);
Brian Silverman9dd793b2020-01-31 23:52:21 -0800235 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800236 CHECK(buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC);
Jim Ostrowski8565b402020-02-29 20:26:53 -0800237 if (!FLAGS_ignore_timestamps) {
238 // Require that we have good timestamp on images
239 CHECK_EQ(buffer.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK,
240 static_cast<uint32_t>(V4L2_BUF_FLAG_TSTAMP_SRC_EOF));
241 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800242 return {static_cast<int>(buffer.index),
243 aos::time::from_timeval(buffer.timestamp)};
Brian Silverman9dd793b2020-01-31 23:52:21 -0800244}
245
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800246void V4L2ReaderBase::EnqueueBuffer(int buffer_number) {
247 // TODO(austin): Detect multiplanar and do this all automatically.
248
Brian Silverman9dd793b2020-01-31 23:52:21 -0800249 CHECK_GE(buffer_number, 0);
250 CHECK_LT(buffer_number, static_cast<int>(buffers_.size()));
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800251 CHECK(buffers_[buffer_number].data_pointer != nullptr);
252
Brian Silverman9dd793b2020-01-31 23:52:21 -0800253 struct v4l2_buffer buffer;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800254 struct v4l2_plane planes[1];
Brian Silverman9dd793b2020-01-31 23:52:21 -0800255 memset(&buffer, 0, sizeof(buffer));
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800256 memset(&planes, 0, sizeof(planes));
Brian Silverman9dd793b2020-01-31 23:52:21 -0800257 buffer.memory = V4L2_MEMORY_USERPTR;
258 buffer.index = buffer_number;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800259 if (multiplanar()) {
260 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
261 buffer.m.planes = planes;
262 buffer.length = 1;
263 planes[0].m.userptr =
264 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
265 planes[0].length = ImageSize();
266 planes[0].bytesused = planes[0].length;
267 } else {
268 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
269 buffer.m.userptr =
270 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
271 buffer.length = ImageSize();
272 }
273
Brian Silverman9dd793b2020-01-31 23:52:21 -0800274 PCHECK(Ioctl(VIDIOC_QBUF, &buffer) == 0);
275}
276
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800277void V4L2ReaderBase::StreamOff() {
278 int type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
279 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
Brian Silverman8f24adb2020-02-02 17:15:58 -0800280 const int result = Ioctl(VIDIOC_STREAMOFF, &type);
281 if (result == 0) {
282 return;
283 }
Jim Ostrowski977850f2022-01-22 21:04:22 -0800284 // Some devices (like Alex's webcam) return this if streaming isn't
285 // currently on, unlike what the documentations says should happen.
Brian Silverman8f24adb2020-02-02 17:15:58 -0800286 if (errno == EBUSY) {
287 return;
288 }
289 PLOG(FATAL) << "VIDIOC_STREAMOFF failed";
290}
291
milind-ufd08c432023-02-05 15:15:21 -0800292V4L2Reader::V4L2Reader(aos::EventLoop *event_loop, std::string_view device_name,
293 std::string_view image_channel)
294 : V4L2ReaderBase(event_loop, device_name, image_channel) {
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800295 // Don't know why this magic call to SetExposure is required (before the
296 // camera settings are configured) to make things work on boot of the pi, but
297 // it seems to be-- without it, the image exposure is wrong (too dark). Note--
298 // any valid value seems to work-- just choosing 1 for now
299
300 SetExposure(1);
301
302 struct v4l2_format format;
303 memset(&format, 0, sizeof(format));
304 format.type = multiplanar() ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
305 : V4L2_BUF_TYPE_VIDEO_CAPTURE;
milind-u6b094092023-01-09 19:26:12 -0800306
307 constexpr int kWidth = 640;
308 constexpr int kHeight = 480;
309 format.fmt.pix.width = kWidth;
310 format.fmt.pix.height = kHeight;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800311 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
312 // This means we want to capture from a progressive (non-interlaced)
313 // source.
314 format.fmt.pix.field = V4L2_FIELD_NONE;
315 PCHECK(Ioctl(VIDIOC_S_FMT, &format) == 0);
milind-u6b094092023-01-09 19:26:12 -0800316 CHECK_EQ(static_cast<int>(format.fmt.pix.width), kWidth);
317 CHECK_EQ(static_cast<int>(format.fmt.pix.height), kHeight);
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800318 CHECK_EQ(static_cast<int>(format.fmt.pix.bytesperline),
milind-u6b094092023-01-09 19:26:12 -0800319 kWidth * 2 /* bytes per pixel */);
320 CHECK_EQ(format.fmt.pix.sizeimage, ImageSize(kHeight, kWidth));
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800321
322 StreamOn();
323}
324
325RockchipV4L2Reader::RockchipV4L2Reader(aos::EventLoop *event_loop,
Ravago Jonesdc524752022-12-27 01:15:13 -0800326 aos::internal::EPoll *epoll,
milind-ufd08c432023-02-05 15:15:21 -0800327 std::string_view device_name,
328 std::string_view image_sensor_subdev,
329 std::string_view image_channel)
330 : V4L2ReaderBase(event_loop, device_name, image_channel),
Ravago Jones65469be2023-01-13 21:28:23 -0800331 epoll_(epoll),
milind-ufd08c432023-02-05 15:15:21 -0800332 image_sensor_fd_(open(image_sensor_subdev.data(), O_RDWR | O_NONBLOCK)),
Austin Schuh2cd5fe82023-02-04 16:21:19 -0800333 buffer_requeuer_([this](int buffer) { EnqueueBuffer(buffer); },
334 kEnqueueFifoPriority) {
Ravago Jones65469be2023-01-13 21:28:23 -0800335 PCHECK(image_sensor_fd_.get() != -1)
336 << " Failed to open device " << device_name;
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800337 StreamOn();
Ravago Jonesdc524752022-12-27 01:15:13 -0800338 epoll_->OnReadable(fd().get(), [this]() { OnImageReady(); });
339}
340
Ravago Jonesfd8aa202023-01-16 14:21:45 -0800341RockchipV4L2Reader::~RockchipV4L2Reader() { epoll_->DeleteFd(fd().get()); }
342
Ravago Jonesc6b919f2023-01-01 21:34:12 -0800343void RockchipV4L2Reader::MarkBufferToBeEnqueued(int buffer) {
344 ReinitializeBuffer(buffer);
345 buffer_requeuer_.Push(buffer);
346}
347
Ravago Jonesdc524752022-12-27 01:15:13 -0800348void RockchipV4L2Reader::OnImageReady() {
349 if (!ReadLatestImage()) {
350 return;
351 }
352
353 SendLatestImage();
Austin Schuh77d0bbd2022-12-26 14:00:51 -0800354}
355
Ravago Jones65469be2023-01-13 21:28:23 -0800356int RockchipV4L2Reader::ImageSensorIoctl(unsigned long number, void *arg) {
357 return ioctl(image_sensor_fd_.get(), number, arg);
358}
359
360void RockchipV4L2Reader::SetExposure(size_t duration) {
361 v4l2_control exposure_control;
362 exposure_control.id = V4L2_CID_EXPOSURE;
363 exposure_control.value = static_cast<int>(duration);
364 PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &exposure_control) == 0);
365}
366
367void RockchipV4L2Reader::SetGain(size_t gain) {
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800368 v4l2_control gain_control;
369 gain_control.id = V4L2_CID_GAIN;
370 gain_control.value = static_cast<int>(gain);
371 PCHECK(ImageSensorIoctl(VIDIOC_S_CTRL, &gain_control) == 0);
372}
373
374void RockchipV4L2Reader::SetGainExt(size_t gain) {
Ravago Jones65469be2023-01-13 21:28:23 -0800375 struct v4l2_ext_controls controls;
376 memset(&controls, 0, sizeof(controls));
377 struct v4l2_ext_control control[1];
378 memset(&control, 0, sizeof(control));
379
380 controls.ctrl_class = V4L2_CTRL_CLASS_IMAGE_SOURCE;
381 controls.count = 1;
382 controls.controls = control;
383 control[0].id = V4L2_CID_ANALOGUE_GAIN;
384 control[0].value = gain;
385
386 PCHECK(ImageSensorIoctl(VIDIOC_S_EXT_CTRLS, &controls) == 0);
387}
388
Ravago Jonesda1b0082023-01-21 15:33:19 -0800389void RockchipV4L2Reader::SetVerticalBlanking(size_t vblank) {
390 struct v4l2_ext_controls controls;
391 memset(&controls, 0, sizeof(controls));
392 struct v4l2_ext_control control[1];
393 memset(&control, 0, sizeof(control));
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800394
Ravago Jonesda1b0082023-01-21 15:33:19 -0800395 controls.ctrl_class = V4L2_CTRL_CLASS_IMAGE_SOURCE;
396 controls.count = 1;
397 controls.controls = control;
398 control[0].id = V4L2_CID_VBLANK;
399 control[0].value = vblank;
400
401 PCHECK(ImageSensorIoctl(VIDIOC_S_EXT_CTRLS, &controls) == 0);
Ravago Jonesa0a2e062023-01-03 21:45:18 -0800402}
403
Brian Silverman9dd793b2020-01-31 23:52:21 -0800404} // namespace vision
405} // namespace frc971