blob: 91777c7446fdb5fad852c2bae2ad6ee979435c75 [file] [log] [blame]
Brian Silverman9dd793b2020-01-31 23:52:21 -08001#include "y2020/vision/v4l2_reader.h"
2
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
15V4L2Reader::V4L2Reader(aos::EventLoop *event_loop,
16 const std::string &device_name)
17 : fd_(open(device_name.c_str(), O_RDWR | O_NONBLOCK)) {
18 PCHECK(fd_.get() != -1);
19
20 // First, clean up after anybody else who left the device streaming.
Brian Silverman8f24adb2020-02-02 17:15:58 -080021 StreamOff();
Brian Silverman9dd793b2020-01-31 23:52:21 -080022
23 {
24 struct v4l2_format format;
25 memset(&format, 0, sizeof(format));
26 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
27 format.fmt.pix.width = cols_;
28 format.fmt.pix.height = rows_;
29 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
30 // This means we want to capture from a progressive (non-interlaced) source.
31 format.fmt.pix.field = V4L2_FIELD_NONE;
32 PCHECK(Ioctl(VIDIOC_S_FMT, &format) == 0);
33 CHECK_EQ(static_cast<int>(format.fmt.pix.width), cols_);
34 CHECK_EQ(static_cast<int>(format.fmt.pix.height), rows_);
35 CHECK_EQ(static_cast<int>(format.fmt.pix.bytesperline),
36 cols_ * 2 /* bytes per pixel */);
37 CHECK_EQ(format.fmt.pix.sizeimage, ImageSize());
38 }
39
40 {
41 struct v4l2_requestbuffers request;
42 memset(&request, 0, sizeof(request));
43 request.count = buffers_.size();
44 request.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
45 request.memory = V4L2_MEMORY_USERPTR;
46 PCHECK(Ioctl(VIDIOC_REQBUFS, &request) == 0);
47 CHECK_EQ(request.count, buffers_.size())
48 << ": Kernel refused to give us the number of buffers we asked for";
49 }
50
51 for (size_t i = 0; i < buffers_.size(); ++i) {
52 buffers_[i].sender = event_loop->MakeSender<CameraImage>("/camera");
53 EnqueueBuffer(i);
54 }
55
56 {
57 int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
58 PCHECK(Ioctl(VIDIOC_STREAMON, &type) == 0);
59 }
60}
61
Brian Silverman967e5df2020-02-09 16:43:34 -080062bool V4L2Reader::ReadLatestImage() {
Brian Silverman9dd793b2020-01-31 23:52:21 -080063 // First, enqueue any old buffer we already have. This is the one which may
64 // have been sent.
Brian Silverman967e5df2020-02-09 16:43:34 -080065 if (saved_buffer_) {
66 EnqueueBuffer(saved_buffer_.index);
67 saved_buffer_.Clear();
Brian Silverman9dd793b2020-01-31 23:52:21 -080068 }
69 while (true) {
Brian Silverman967e5df2020-02-09 16:43:34 -080070 const BufferInfo previous_buffer = saved_buffer_;
Brian Silverman9dd793b2020-01-31 23:52:21 -080071 saved_buffer_ = DequeueBuffer();
Brian Silverman967e5df2020-02-09 16:43:34 -080072 if (saved_buffer_) {
Brian Silverman9dd793b2020-01-31 23:52:21 -080073 // We got a new buffer. Return the previous one (if relevant) and keep
74 // going.
Brian Silverman967e5df2020-02-09 16:43:34 -080075 if (previous_buffer) {
76 EnqueueBuffer(previous_buffer.index);
Brian Silverman9dd793b2020-01-31 23:52:21 -080077 }
78 continue;
79 }
Brian Silverman967e5df2020-02-09 16:43:34 -080080 if (!previous_buffer) {
Brian Silverman9dd793b2020-01-31 23:52:21 -080081 // There were no images to read. Return an indication of that.
Brian Silverman967e5df2020-02-09 16:43:34 -080082 return false;
Brian Silverman9dd793b2020-01-31 23:52:21 -080083 }
84 // We didn't get a new one, but we already got one in a previous
85 // iteration, which means we found an image so return it.
86 saved_buffer_ = previous_buffer;
Brian Silverman967e5df2020-02-09 16:43:34 -080087 buffers_[saved_buffer_.index].PrepareMessage(rows_, cols_, ImageSize(),
88 saved_buffer_.monotonic_eof);
89 return true;
Brian Silverman9dd793b2020-01-31 23:52:21 -080090 }
91}
92
Brian Silverman967e5df2020-02-09 16:43:34 -080093void V4L2Reader::SendLatestImage() { buffers_[saved_buffer_.index].Send(); }
94
95void V4L2Reader::Buffer::InitializeMessage(size_t max_image_size) {
96 message_offset = flatbuffers::Offset<CameraImage>();
97 builder = aos::Sender<CameraImage>::Builder();
98 builder = sender.MakeBuilder();
99 // The kernel has an undocumented requirement that the buffer is aligned
100 // to 64 bytes. If you give it a nonaligned pointer, it will return EINVAL
101 // and only print something in dmesg with the relevant dynamic debug
102 // prints turned on.
103 builder.fbb()->StartIndeterminateVector(max_image_size, 1, 64, &data_pointer);
104 CHECK_EQ(reinterpret_cast<uintptr_t>(data_pointer) % 64, 0u)
105 << ": Flatbuffers failed to align things as requested";
106}
107
108void V4L2Reader::Buffer::PrepareMessage(
109 int rows, int cols, size_t image_size,
110 aos::monotonic_clock::time_point monotonic_eof) {
111 CHECK(data_pointer != nullptr);
112 data_pointer = nullptr;
113
114 const auto data_offset = builder.fbb()->EndIndeterminateVector(image_size, 1);
115 auto image_builder = builder.MakeBuilder<CameraImage>();
116 image_builder.add_data(data_offset);
117 image_builder.add_rows(rows);
118 image_builder.add_cols(cols);
119 image_builder.add_monotonic_timestamp_ns(
120 std::chrono::nanoseconds(monotonic_eof.time_since_epoch()).count());
121 message_offset = image_builder.Finish();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800122}
123
124int V4L2Reader::Ioctl(unsigned long number, void *arg) {
125 return ioctl(fd_.get(), number, arg);
126}
127
Brian Silverman967e5df2020-02-09 16:43:34 -0800128V4L2Reader::BufferInfo V4L2Reader::DequeueBuffer() {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800129 struct v4l2_buffer buffer;
130 memset(&buffer, 0, sizeof(buffer));
131 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
132 buffer.memory = V4L2_MEMORY_USERPTR;
133 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
134 if (result == -1 && errno == EAGAIN) {
Brian Silverman967e5df2020-02-09 16:43:34 -0800135 return BufferInfo();
Brian Silverman9dd793b2020-01-31 23:52:21 -0800136 }
137 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
138 CHECK_LT(buffer.index, buffers_.size());
Brian Silverman9dd793b2020-01-31 23:52:21 -0800139 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
140 buffer.m.userptr);
141 CHECK_EQ(ImageSize(), buffer.length);
Brian Silverman967e5df2020-02-09 16:43:34 -0800142 CHECK(buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC);
Jim Ostrowski8565b402020-02-29 20:26:53 -0800143 if (!FLAGS_ignore_timestamps) {
144 // Require that we have good timestamp on images
145 CHECK_EQ(buffer.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK,
146 static_cast<uint32_t>(V4L2_BUF_FLAG_TSTAMP_SRC_EOF));
147 }
Brian Silverman967e5df2020-02-09 16:43:34 -0800148 return {static_cast<int>(buffer.index),
149 aos::time::from_timeval(buffer.timestamp)};
Brian Silverman9dd793b2020-01-31 23:52:21 -0800150}
151
152void V4L2Reader::EnqueueBuffer(int buffer_number) {
Brian Silverman9dd793b2020-01-31 23:52:21 -0800153 CHECK_GE(buffer_number, 0);
154 CHECK_LT(buffer_number, static_cast<int>(buffers_.size()));
155 buffers_[buffer_number].InitializeMessage(ImageSize());
156 struct v4l2_buffer buffer;
157 memset(&buffer, 0, sizeof(buffer));
158 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
159 buffer.memory = V4L2_MEMORY_USERPTR;
160 buffer.index = buffer_number;
161 buffer.m.userptr =
162 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
163 buffer.length = ImageSize();
164 PCHECK(Ioctl(VIDIOC_QBUF, &buffer) == 0);
165}
166
Brian Silverman8f24adb2020-02-02 17:15:58 -0800167void V4L2Reader::StreamOff() {
168 int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
169 const int result = Ioctl(VIDIOC_STREAMOFF, &type);
170 if (result == 0) {
171 return;
172 }
173 // Some devices (like Alex's webcam) return this if streaming isn't currently
174 // on, unlike what the documentations says should happen.
175 if (errno == EBUSY) {
176 return;
177 }
178 PLOG(FATAL) << "VIDIOC_STREAMOFF failed";
179}
180
Brian Silverman9dd793b2020-01-31 23:52:21 -0800181} // namespace vision
182} // namespace frc971