blob: a2a86fee7a89b70856af63af80408c68cd89aec9 [file] [log] [blame]
Austin Schuhc97d48d2022-12-26 14:09:13 -08001#ifndef FRC971_VISION_MEDIA_DEVICE_H_
2#define FRC971_VISION_MEDIA_DEVICE_H_
3
4#include <linux/media.h>
5#include <linux/v4l2-subdev.h>
6#include <linux/videodev2.h>
7
8#include <cstddef>
9#include <cstdint>
10#include <optional>
11#include <string>
12#include <string_view>
13#include <vector>
14
Austin Schuhc97d48d2022-12-26 14:09:13 -080015#include "glog/logging.h"
16
Philipp Schrader790cb542023-07-05 21:06:52 -070017#include "aos/scoped/scoped_fd.h"
18
Austin Schuhc97d48d2022-12-26 14:09:13 -080019namespace frc971 {
20namespace vision {
21
22class MediaDevice;
23class Pad;
24class Entity;
25
26// See
27// https://www.kernel.org/doc/html/v4.9/media/uapi/mediactl/media-controller.html
28// for more info.
29//
30// This set of intertwined classes represents a Media device as exposed by the
31// media controller API from V4L2.
32//
33// A MediaDevice is a peripheral.
34//
35// That piece of hardware has Entities which are functions that that peripheral
36// exposes. This can be things like various steps in the ISP, or
37// encoder/decoder pipelines.
38//
39// Those entities each have Pads. Pads are inputs or outputs from each piece of
40// hardware.
41//
42// Pads are connected by Links. Links can only be enabled/disabled, not
43// connected at will (from what I can tell).
44//
45// The underlying API has enough info in it to make all this possible to deduce
46// without classes, but given how much we want to manipulate things, it is
47// easier to create a set of classes to index things better and add helper
48// functions.
49
50// A link between pads.
51class Link {
52 public:
53 // Returns the source pad where the data comes from.
54 Pad *source() { return source_; }
55 const Pad *source() const { return source_; }
56 // Returns the sink pad where the data goes to.
57 Pad *sink() { return sink_; }
58 const Pad *sink() const { return sink_; }
59
60 // Returns true if this pad is enabled.
61 bool enabled() const { return !!(flags_ & MEDIA_LNK_FL_ENABLED); }
62 // Returns true if this pad can be enabled/disabled.
63 bool immutable() const { return !!(flags_ & MEDIA_LNK_FL_IMMUTABLE); }
64 // Returns true if this pad is dynamic.
65 bool dynamic() const { return !!(flags_ & MEDIA_LNK_FL_DYNAMIC); }
66
67 // Returns the raw flags.
68 uint32_t flags() const { return flags_; }
69
70 private:
71 friend class MediaDevice;
72 uint32_t flags_;
73 uint32_t id_;
74
75 // Pointers to this pad. MediaDevice is responsible for updating the pointers
76 // and maintaining their lifetime.
77 Pad *source_ = nullptr;
78 Pad *sink_ = nullptr;
79};
80
81// A pad of an entity connected by links.
82class Pad {
83 public:
84 // Returns the unique ID that v4l2 assigns to this pad.
85 uint32_t id() const { return id_; }
86 // The pad index that some APIs need to talk about this pad. This starts from
87 // 0 and counts up.
88 uint16_t index() const { return index_; }
89
90 // The entity that this pad is on.
91 const Entity *entity() const { return entity_; }
92
93 // Returns true if this pad is a source.
94 bool source() const { return !!(flags_ & MEDIA_PAD_FL_SOURCE); }
95 // Returns true if this pad is a sink.
96 bool sink() const { return !!(flags_ & MEDIA_PAD_FL_SINK); }
97
98 // Returns the number of links.
99 size_t links_size() const { return links_.size(); }
100 // Returns a specific link.
101 Link *links(size_t index) { return links_[index]; }
102 const Link *links(size_t index) const { return links_[index]; }
103
104 // Prints out a representation of this pad for debugging.
105 void Log() const;
106
107 // Sets the format of this pad.
108 // TODO(austin): Should this set the paired sink pad automatically? Formats
109 // need to match...
110 void SetSubdevFormat(uint32_t width, uint32_t height, uint32_t code);
111 void SetSubdevCrop(uint32_t width, uint32_t height);
112
113 private:
114 friend class MediaDevice;
115
116 uint16_t index_;
117
118 uint32_t id_;
119 uint32_t flags_;
120 // List of links. MediaDevice is responsible for updating them and their
121 // lifetime.
122 std::vector<Link *> links_;
123
124 Entity *entity_ = nullptr;
125};
126
127// A hardware entity.
128class Entity {
129 public:
130 // Returns the ID that v4l2 assignes to this entity.
131 uint32_t id() const { return entity_.id; }
132 // Returns the name of this entity.
133 std::string_view name() const { return entity_.name; }
134
135 // Returns the function that this entity serves.
136 uint32_t function() const { return entity_.function; }
137 // Returns the type of interface.
138 uint32_t interface_type() const { return interface_.intf_type; }
139 // Returns the major and minor device numbers. Most likely, you just want the
140 // device instead.
141 uint32_t major() const { return interface_.devnode.major; }
142 uint32_t minor() const { return interface_.devnode.minor; }
143
144 // Returns the device needed to access this entity.
145 const std::string &device() const {
146 CHECK(has_interface_);
147 return device_;
148 }
149
150 // Returns all the pads, in order.
151 const std::vector<Pad *> pads() const { return pads_; }
152 Pad *pads(size_t index) { return pads_[index]; }
153
154 // Sets the format of this device.
155 void SetFormat(uint32_t width, uint32_t height, uint32_t code);
156
157 // Logs this device in a human readable format.
158 void Log() const;
159
160 private:
161 friend class MediaDevice;
162
163 // Updates device_.
164 void UpdateDevice();
165
166 struct media_v2_entity entity_;
167 // If true, interface_ has been set.
168 bool has_interface_ = false;
169 struct media_v2_interface interface_ = {0, 0, 0, {}, {}};
170
171 std::string device_;
172
173 std::vector<Pad *> pads_;
174};
175
176// Class representing a media device.
177class MediaDevice {
178 public:
179 static std::optional<MediaDevice> Initialize(int index);
180
181 // Not copyable but movable.
182 MediaDevice(const MediaDevice &) = delete;
183 MediaDevice &operator=(const MediaDevice &) = delete;
184 MediaDevice(MediaDevice &&) = default;
185 MediaDevice &operator=(MediaDevice &&) = default;
186
187 // Logs this media device in a human readable format.
188 void Log() const;
189
190 // Finds the entity with the provided name.
191 Entity *FindEntity(std::string_view entity_name);
192
193 // Finds the link connecting a source and destination entity through the
194 // provided pads. LOG(FATAL)'s if it can't find one.
195 Link *FindLink(std::string_view source, int source_pad, std::string_view sink,
196 int sink_pad);
197
198 // Disables all the mutable links in this device.
199 void Reset();
200
201 // TODO(austin): get the fd down deep enough into the link that this works.
202 // Disables the link.
203 void Reset(Link *link);
204 // Enables the link.
205 void Enable(Link *link);
206
207 // Returns the name of the driver implementing the media API as a
208 // NUL-terminated ASCII string. The driver version is stored in the
209 // driver_version field.
210 //
211 // Driver specific applications can use this information to verify the driver
212 // identity. It is also useful to work around known bugs, or to identify
213 // drivers in error reports.
214 std::string_view driver() const { return device_info_.driver; }
215
216 // Returns the device model name as a NUL-terminated UTF-8 string. The device
217 // version is stored in the device_version field and is not be appended to the
218 // model name.
219 std::string_view model() const { return device_info_.model; }
220
221 // Returns the serial number as a NUL-terminated ASCII string.
222 std::string_view serial() const { return device_info_.serial; }
223
224 // Returns the location of the device in the system as a NUL-terminated ASCII
225 // string. This includes the bus type name (PCI, USB, ...) and a bus-specific
226 // identifier.
227 std::string_view bus_info() const { return device_info_.bus_info; }
228
229 // Returns the list of entities in this device.
230 std::vector<Entity> *entities() { return &entities_; }
231
232 // Returns all the links between pads in this device.
233 std::vector<Link> *links() { return &links_; }
234
235 private:
236 MediaDevice(int fd) : fd_(fd) { Update(); }
237
238 void Update();
239
240 friend class Link;
241 friend class Entity;
242 friend class Pad;
243
244 aos::ScopedFD fd_;
245
246 std::vector<Pad> pads_;
247 std::vector<Entity> entities_;
248 std::vector<Link> links_;
249
250 struct media_device_info device_info_;
251};
252
253// Returns the MediaDevice with the requested bus_info() if it can be found, or
254// nullopt otherwise.
255std::optional<MediaDevice> FindMediaDevice(std::string_view device);
256
257} // namespace vision
258} // namespace frc971
259
260#endif // FRC971_VISION_MEDIA_DEVICE_H_