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_
|