blob: 727f8ba21423b6033f3ca0ee052fb428e7fd0635 [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
9namespace frc971 {
10namespace vision {
11
12V4L2Reader::V4L2Reader(aos::EventLoop *event_loop,
13 const std::string &device_name)
14 : fd_(open(device_name.c_str(), O_RDWR | O_NONBLOCK)) {
15 PCHECK(fd_.get() != -1);
16
17 // First, clean up after anybody else who left the device streaming.
18 {
19 int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
20 PCHECK(Ioctl(VIDIOC_STREAMOFF, &type) == 0);
21 }
22
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
62absl::Span<const char> V4L2Reader::ReadLatestImage() {
63 // First, enqueue any old buffer we already have. This is the one which may
64 // have been sent.
65 if (saved_buffer_ != -1) {
66 EnqueueBuffer(saved_buffer_);
67 saved_buffer_ = -1;
68 }
69 while (true) {
70 const int previous_buffer = saved_buffer_;
71 saved_buffer_ = DequeueBuffer();
72 if (saved_buffer_ != -1) {
73 // We got a new buffer. Return the previous one (if relevant) and keep
74 // going.
75 if (previous_buffer != -1) {
76 EnqueueBuffer(previous_buffer);
77 }
78 continue;
79 }
80 if (previous_buffer == -1) {
81 // There were no images to read. Return an indication of that.
82 return absl::Span<const char>();
83 }
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;
87 return buffers_[saved_buffer_].DataSpan(ImageSize());
88 }
89}
90
91void V4L2Reader::SendLatestImage() {
92 buffers_[saved_buffer_].Send(rows_, cols_, ImageSize());
93}
94
95int V4L2Reader::Ioctl(unsigned long number, void *arg) {
96 return ioctl(fd_.get(), number, arg);
97}
98
99int V4L2Reader::DequeueBuffer() {
100 struct v4l2_buffer buffer;
101 memset(&buffer, 0, sizeof(buffer));
102 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
103 buffer.memory = V4L2_MEMORY_USERPTR;
104 const int result = Ioctl(VIDIOC_DQBUF, &buffer);
105 if (result == -1 && errno == EAGAIN) {
106 return -1;
107 }
108 PCHECK(result == 0) << ": VIDIOC_DQBUF failed";
109 CHECK_LT(buffer.index, buffers_.size());
110 LOG(INFO) << "dequeued " << buffer.index;
111 CHECK_EQ(reinterpret_cast<uintptr_t>(buffers_[buffer.index].data_pointer),
112 buffer.m.userptr);
113 CHECK_EQ(ImageSize(), buffer.length);
114 return buffer.index;
115}
116
117void V4L2Reader::EnqueueBuffer(int buffer_number) {
118 LOG(INFO) << "enqueueing " << buffer_number;
119 CHECK_GE(buffer_number, 0);
120 CHECK_LT(buffer_number, static_cast<int>(buffers_.size()));
121 buffers_[buffer_number].InitializeMessage(ImageSize());
122 struct v4l2_buffer buffer;
123 memset(&buffer, 0, sizeof(buffer));
124 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
125 buffer.memory = V4L2_MEMORY_USERPTR;
126 buffer.index = buffer_number;
127 buffer.m.userptr =
128 reinterpret_cast<uintptr_t>(buffers_[buffer_number].data_pointer);
129 buffer.length = ImageSize();
130 PCHECK(Ioctl(VIDIOC_QBUF, &buffer) == 0);
131}
132
133} // namespace vision
134} // namespace frc971