File: v4l2_ioctl_shim.h

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (280 lines) | stat: -rw-r--r-- 10,141 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_GPU_V4L2_TEST_V4L2_IOCTL_SHIM_H_
#define MEDIA_GPU_V4L2_TEST_V4L2_IOCTL_SHIM_H_

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include <linux/videodev2.h>
#include <string.h>

#include <set>

#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "ui/gfx/geometry/size.h"

namespace media {

namespace v4l2_test {

// MmappedBuffer maintains |mmapped_planes_| for each buffer as well as
// |buffer_id_|. |buffer_id_| is an index used for VIDIOC_REQBUFS ioctl call.
class MmappedBuffer : public base::RefCounted<MmappedBuffer> {
 public:
  REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();

  MmappedBuffer(const base::PlatformFile decode_fd,
                const struct v4l2_buffer& v4l2_buffer);

  class MmappedPlane {
   public:
    raw_ptr<void> start_addr;
    const size_t length;
    size_t bytes_used = 0;

    MmappedPlane(void* start, size_t len) : start_addr(start), length(len) {}

    // Appends the current slice data to the mmapped buffer. Resets |bytes_used|
    // to 0 for the first slice. This function is used for HEVC because multiple
    // slices per frame are supported.
    void CopyInSlice(const uint8_t* frame_data,
                     size_t frame_size,
                     bool is_first_slice) {
      if (is_first_slice) {
        bytes_used = 0;
      }

      LOG_ASSERT((bytes_used + frame_size) < length)
          << "Not enough memory allocated to copy into.";

      memcpy(static_cast<uint8_t*>(start_addr) + bytes_used, frame_data,
             frame_size);
      bytes_used += frame_size;
    }

    // Overwrites the mmapped buffer with the current frame data.
    void CopyIn(const uint8_t* frame_data, size_t frame_size) {
      CopyInSlice(frame_data, frame_size, true);
    }
  };

  using MmappedPlanes = std::vector<MmappedPlane>;

  MmappedPlanes& mmapped_planes() { return mmapped_planes_; }

  uint32_t buffer_id() const { return buffer_id_; }
  void set_buffer_id(uint32_t buffer_id) { buffer_id_ = buffer_id; }

  uint32_t frame_number() const { return frame_number_; }
  void set_frame_number(uint32_t frame_number) { frame_number_ = frame_number; }

 private:
  friend class base::RefCounted<MmappedBuffer>;
  ~MmappedBuffer();

  MmappedBuffer(const MmappedBuffer&) = delete;
  MmappedBuffer& operator=(const MmappedBuffer&) = delete;

  MmappedPlanes mmapped_planes_;
  const uint32_t num_planes_;
  uint32_t buffer_id_;
  // Indicates which frame in input bitstream corresponds to this MmappedBuffer
  // in OUTPUT queue.
  uint32_t frame_number_;
};

using MmappedBuffers = std::vector<scoped_refptr<MmappedBuffer>>;

// V4L2Queue class maintains properties of a queue.
class V4L2Queue {
 public:
  V4L2Queue(enum v4l2_buf_type type,
            const gfx::Size& resolution,
            enum v4l2_memory memory);

  V4L2Queue(const V4L2Queue&) = delete;
  V4L2Queue& operator=(const V4L2Queue&) = delete;
  ~V4L2Queue();

  // Retrieves a mmapped buffer for the given |buffer_id|, which is a decoded
  // surface, from MmappedBuffers.
  scoped_refptr<MmappedBuffer> GetBuffer(const size_t buffer_id) const;

  enum v4l2_buf_type type() const { return type_; }
  uint32_t fourcc() const { return fourcc_; }
  void set_fourcc(uint32_t fourcc) { fourcc_ = fourcc; }

  gfx::Size resolution() const { return resolution_; }
  void set_resolution(gfx::Size resolution) { resolution_ = resolution; }

  enum v4l2_memory memory() const { return memory_; }

  void set_buffers(MmappedBuffers& buffers) { buffers_ = buffers; }

  uint32_t num_buffers() const { return num_buffers_; }
  void set_num_buffers(uint32_t num_buffers) { num_buffers_ = num_buffers; }

  uint32_t num_planes() const { return num_planes_; }
  void set_num_planes(uint32_t num_planes) { num_planes_ = num_planes; }

  uint32_t last_queued_buffer_id() const { return last_queued_buffer_id_; }
  void set_last_queued_buffer_id(uint32_t last_queued_buffer_id) {
    last_queued_buffer_id_ = last_queued_buffer_id;
  }

  int media_request_fd() const { return media_request_fd_; }
  void set_media_request_fd(int media_request_fd) {
    media_request_fd_ = media_request_fd;
  }

  std::set<uint32_t> queued_buffer_ids() const { return queued_buffer_ids_; }

  void QueueBufferId(uint32_t last_queued_buffer_id) {
    queued_buffer_ids_.insert(last_queued_buffer_id);
  }

  void DequeueBufferId(uint32_t buffer_id) {
    queued_buffer_ids_.erase(buffer_id);
  }

  void DequeueAllBufferIds() { queued_buffer_ids_.clear(); }

 private:
  const enum v4l2_buf_type type_;
  uint32_t fourcc_;
  MmappedBuffers buffers_;
  uint32_t num_buffers_;
  // For the OUTPUT queue resolution refers to the coded dimensions of the
  // video. For the CAPTURE queue resolution refers to the size of the
  // buffer necessary for the driver to decode into and must
  // contain the resolution of the OUTPUT queue.
  gfx::Size resolution_;
  uint32_t num_planes_;
  const enum v4l2_memory memory_;
  // File descriptor returned by MEDIA_IOC_REQUEST_ALLOC ioctl call
  // to submit requests.
  int media_request_fd_;
  // Tracks which CAPTURE buffer was queued in the previous frame.
  uint32_t last_queued_buffer_id_;
  std::set<uint32_t> queued_buffer_ids_;
};

// V4L2IoctlShim is a shallow wrapper which wraps V4L2 ioctl requests
// with error checking and maintains the lifetime of a file descriptor
// for decode/media device.
// https://www.kernel.org/doc/html/v5.10/userspace-api/media/v4l/user-func.html
class V4L2IoctlShim {
 public:
  // Finds first decoder that can decode |coded_fourcc|
  V4L2IoctlShim(uint32_t coded_fourcc);
  V4L2IoctlShim(const V4L2IoctlShim&) = delete;
  V4L2IoctlShim& operator=(const V4L2IoctlShim&) = delete;
  ~V4L2IoctlShim();

  // Queries whether the given |ctrl_id| is supported on current platform.
  [[nodiscard]] bool QueryCtrl(const uint32_t ctrl_id) const;

  // Enumerates all frame sizes that the device supports
  // via VIDIOC_ENUM_FRAMESIZES.
  [[nodiscard]] bool EnumFrameSizes(uint32_t fourcc) const;

  // Configures the underlying V4L2 queue via VIDIOC_S_FMT. Returns true
  // if the configuration was successful.
  void SetFmt(const std::unique_ptr<V4L2Queue>& queue) const;

  // Retrieves the format, |fmt|, (via VIDIOC_G_FMT)
  void GetFmt(struct v4l2_format* fmt) const;

  // Tries to configure |fmt|. This does not modify the underlying driver state.
  // https://www.kernel.org/doc/html/v5.10/userspace-api/media/v4l/vidioc-g-fmt.html?highlight=vidioc_try_fmt#description
  void TryFmt(struct v4l2_format* fmt) const;

  // Allocates buffers via VIDIOC_REQBUFS for |queue| with a buffer count.
  void ReqBufs(std::unique_ptr<V4L2Queue>& queue, uint32_t count) const;

  // Enqueues an empty (capturing) or filled (output) buffer
  // in the driver's incoming |queue|.
  [[nodiscard]] bool QBuf(const std::unique_ptr<V4L2Queue>& queue,
                          const uint32_t buffer_id) const;

  // Dequeues a filled (capturing) or decoded (output) buffer
  // from the driver’s outgoing |queue|.
  void DQBuf(const std::unique_ptr<V4L2Queue>& queue,
             uint32_t* buffer_id) const;

  // Starts streaming |queue| (via VIDIOC_STREAMON).
  void StreamOn(const enum v4l2_buf_type type) const;

  // Stops streaming |queue| (via VIDIOC_STREAMOFF).
  void StreamOff(const enum v4l2_buf_type type) const;

  // Sets the value of controls which specify decoding parameters for each
  // frame. |immediate| forces the call to be processed immediately when
  // |MediaIocRequestAlloc| is next called as opposed to being put in the queue.
  void SetExtCtrls(const std::unique_ptr<V4L2Queue>& queue,
                   v4l2_ext_controls* ext_ctrls,
                   bool immediate = false) const;

  // Allocates requests (likely one per OUTPUT buffer) via
  // MEDIA_IOC_REQUEST_ALLOC on the media device.
  void MediaIocRequestAlloc(int* req_fd) const;

  // Submits a request for the given OUTPUT |queue| by queueing
  // the request with |queue|'s media_request_fd().
  void MediaRequestIocQueue(const std::unique_ptr<V4L2Queue>& queue) const;

  // Re-initializes the previously allocated request for reuse.
  void MediaRequestIocReinit(const std::unique_ptr<V4L2Queue>& queue) const;

  // Completion of the request implies that the OUTPUT and CAPTURE buffers
  // are available for dequeueing
  void WaitForRequestCompletion(const std::unique_ptr<V4L2Queue>& queue) const;

  // Finds available media device for video decoder. This function also checks
  // to make sure either |bus_info| or |driver| field from |media_device_info|
  // struct (obtained from MEDIA_IOC_DEVICE_INFO call) is matched from the same
  // field in |v4l2_capability| struct.
  [[nodiscard]] bool FindMediaDevice(struct v4l2_capability* cap);

  // Allocates buffers for the given |queue|.
  void QueryAndMmapQueueBuffers(std::unique_ptr<V4L2Queue>& queue) const;

  enum class DeviceType {
    kDecoder,
    kMedia,
  };

 private:
  // Queries |v4l_fd| to see if it can use the specified |fourcc| format
  // for the given buffer |type|.
  [[nodiscard]] bool QueryFormat(enum v4l2_buf_type type,
                                 uint32_t fourcc) const;

  // Uses a specialized function template to execute V4L2 ioctl request
  // for |request_code| and returns the output of the ioctl() in |arg|
  // if this is a pointer, otherwise |arg| is considered a file descriptor
  // for said ioctl().
  template <typename T>
  [[nodiscard]] bool Ioctl(int request_code, T arg) const;

  // Decode device file descriptor used for ioctl requests.
  base::File decode_fd_;
  // Media device file descriptor used for ioctl requests.
  base::File media_fd_;

  // Whether V4L2_CTRL_WHICH_CUR_VAL is implemented correctly
  bool cur_val_is_supported_ = true;
};

}  // namespace v4l2_test
}  // namespace media

#endif  // MEDIA_GPU_V4L2_TEST_V4L2_IOCTL_SHIM_H_