blob: bdf4a8ebecbb399d325fd663bd444ff875471414 [file] [log] [blame]
Brian Silverman9dd793b2020-01-31 23:52:21 -08001#ifndef Y2020_VISION_V4L2_READER_H_
2#define Y2020_VISION_V4L2_READER_H_
3
4#include <array>
5#include <string>
6
7#include "absl/types/span.h"
8#include "glog/logging.h"
9
10#include "aos/events/event_loop.h"
11#include "aos/scoped/scoped_fd.h"
12#include "y2020/vision/vision_generated.h"
13
14namespace frc971 {
15namespace vision {
16
17// Reads images from a V4L2 capture device (aka camera).
18class V4L2Reader {
19 public:
20 // device_name is the name of the device file (like "/dev/video0").
21 V4L2Reader(aos::EventLoop *event_loop, const std::string &device_name);
22
23 V4L2Reader(const V4L2Reader &) = delete;
24 V4L2Reader &operator=(const V4L2Reader &) = delete;
25
26 // Reads the latest image.
27 //
28 // Returns an empty span if no image was available since this object was
29 // created. The data referenced in the return value is valid until this method
30 // is called again.
31 absl::Span<const char> ReadLatestImage();
32
33 // Sends the latest image.
34 //
35 // ReadLatestImage() must have returned a non-empty span the last time it was
36 // called. After calling this, the data which was returned from
37 // ReadLatestImage() will no longer be valid.
38 void SendLatestImage();
39
40 private:
41 static constexpr int kNumberBuffers = 16;
42
43 struct Buffer {
44 void InitializeMessage(size_t max_image_size) {
45 builder = aos::Sender<CameraImage>::Builder();
46 builder = sender.MakeBuilder();
47 // The kernel has an undocumented requirement that the buffer is aligned
48 // to 64 bytes. If you give it a nonaligned pointer, it will return EINVAL
49 // and only print something in dmesg with the relevant dynamic debug
50 // prints turned on.
51 builder.fbb()->StartIndeterminateVector(max_image_size, 1, 64,
52 &data_pointer);
53 CHECK_EQ(reinterpret_cast<uintptr_t>(data_pointer) % 64, 0u)
54 << ": Flatbuffers failed to align things as requested";
55 }
56
57 void Send(int rows, int cols, size_t image_size) {
58 const auto data_offset =
59 builder.fbb()->EndIndeterminateVector(image_size, 1);
60 auto image_builder = builder.MakeBuilder<CameraImage>();
61 image_builder.add_data(data_offset);
62 image_builder.add_rows(rows);
63 image_builder.add_cols(cols);
64 builder.Send(image_builder.Finish());
65 data_pointer = nullptr;
66 }
67
68 absl::Span<const char> DataSpan(size_t image_size) {
69 return absl::Span<const char>(reinterpret_cast<char *>(data_pointer),
70 image_size);
71 }
72
73 aos::Sender<CameraImage> sender;
74 aos::Sender<CameraImage>::Builder builder;
75
76 uint8_t *data_pointer = nullptr;
77 };
78
79 // TODO(Brian): This concept won't exist once we start using variable-size
80 // H.264 frames.
81 size_t ImageSize() const { return rows_ * cols_ * 2 /* bytes per pixel */; }
82
83 // Attempts to dequeue a buffer (nonblocking). Returns the index of the new
84 // buffer, or -1 if there wasn't a frame to dequeue.
85 int DequeueBuffer();
86
87 void EnqueueBuffer(int buffer);
88
89 int Ioctl(unsigned long number, void *arg);
90
Brian Silverman8f24adb2020-02-02 17:15:58 -080091 void StreamOff();
92
Brian Silverman9dd793b2020-01-31 23:52:21 -080093 // The mmaped V4L2 buffers.
94 std::array<Buffer, kNumberBuffers> buffers_;
95
96 // If this is non-negative, it's the buffer number we're currently holding
97 // onto.
98 int saved_buffer_ = -1;
99
100 const int rows_ = 480;
101 const int cols_ = 640;
102
103 aos::ScopedFD fd_;
104};
105
106} // namespace vision
107} // namespace frc971
108
109#endif // Y2020_VISION_V4L2_READER_H_