Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qv4l2memorytransfer.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include <qloggingcategory.h>
8#include <qdebug.h>
9#include <sys/mman.h>
10#include <optional>
11
13
14static Q_LOGGING_CATEGORY(qLcV4L2MemoryTransfer, "qt.multimedia.ffmpeg.v4l2camera.memorytransfer");
15
16namespace {
17
18v4l2_buffer makeV4l2Buffer(quint32 memoryType, quint32 index = 0)
19{
20 v4l2_buffer buf = {};
21 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
22 buf.memory = memoryType;
23 buf.index = index;
24 return buf;
25}
26
27class UserPtrMemoryTransfer : public QV4L2MemoryTransfer
28{
29public:
31 {
32 quint32 buffersCount = 2;
33 if (!fileDescriptor->requestBuffers(V4L2_MEMORY_USERPTR, buffersCount)) {
34 qCWarning(qLcV4L2MemoryTransfer) << "Cannot request V4L2_MEMORY_USERPTR buffers";
35 return {};
36 }
37
38 std::unique_ptr<UserPtrMemoryTransfer> result(
39 new UserPtrMemoryTransfer(std::move(fileDescriptor), buffersCount, imageSize));
40
41 return result->enqueueBuffers() ? std::move(result) : nullptr;
42 }
43
44 std::optional<Buffer> dequeueBuffer() override
45 {
46 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_USERPTR);
47 if (!fileDescriptor().call(VIDIOC_DQBUF, &v4l2Buffer))
48 return {};
49
50 Q_ASSERT(v4l2Buffer.index < m_byteArrays.size());
51 Q_ASSERT(!m_byteArrays[v4l2Buffer.index].isEmpty());
52
53 return Buffer{ v4l2Buffer, std::move(m_byteArrays[v4l2Buffer.index]) };
54 }
55
56 bool enqueueBuffer(quint32 index) override
57 {
58 Q_ASSERT(index < m_byteArrays.size());
59 Q_ASSERT(m_byteArrays[index].isEmpty());
60
61 auto buf = makeV4l2Buffer(V4L2_MEMORY_USERPTR, index);
62 static_assert(sizeof(decltype(buf.m.userptr)) == sizeof(size_t), "Not compatible sizes");
63
64 m_byteArrays[index] = QByteArray(static_cast<int>(m_imageSize), Qt::Uninitialized);
65
66 buf.m.userptr = (decltype(buf.m.userptr))m_byteArrays[index].data();
67 buf.length = m_byteArrays[index].size();
68
69 if (!fileDescriptor().call(VIDIOC_QBUF, &buf)) {
70 qWarning() << "Couldn't add V4L2 buffer" << errno << strerror(errno) << index;
71 return false;
72 }
73
74 return true;
75 }
76
77 quint32 buffersCount() const override { return static_cast<quint32>(m_byteArrays.size()); }
78
79private:
80 UserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, quint32 buffersCount,
82 : QV4L2MemoryTransfer(std::move(fileDescriptor)),
83 m_imageSize(imageSize),
84 m_byteArrays(buffersCount)
85 {
86 }
87
88private:
89 quint32 m_imageSize;
90 std::vector<QByteArray> m_byteArrays;
91};
92
93class MMapMemoryTransfer : public QV4L2MemoryTransfer
94{
95public:
96 struct MemorySpan
97 {
98 void *data = nullptr;
99 size_t size = 0;
100 bool inQueue = false;
101 };
102
104 {
105 quint32 buffersCount = 2;
106 if (!fileDescriptor->requestBuffers(V4L2_MEMORY_MMAP, buffersCount)) {
107 qCWarning(qLcV4L2MemoryTransfer) << "Cannot request V4L2_MEMORY_MMAP buffers";
108 return {};
109 }
110
111 std::unique_ptr<MMapMemoryTransfer> result(
112 new MMapMemoryTransfer(std::move(fileDescriptor)));
113
114 return result->init(buffersCount) ? std::move(result) : nullptr;
115 }
116
117 bool init(quint32 buffersCount)
118 {
119 for (quint32 index = 0; index < buffersCount; ++index) {
120 auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
121
122 if (!fileDescriptor().call(VIDIOC_QUERYBUF, &buf)) {
123 qWarning() << "Can't map buffer" << index;
124 return false;
125 }
126
127 auto mappedData = mmap(nullptr, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
128 fileDescriptor().get(), buf.m.offset);
129
130 if (mappedData == MAP_FAILED) {
131 qWarning() << "mmap failed" << index << buf.length << buf.m.offset;
132 return false;
133 }
134
135 m_spans.push_back(MemorySpan{ mappedData, buf.length, false });
136 }
137
138 m_spans.shrink_to_fit();
139
140 return enqueueBuffers();
141 }
142
143 ~MMapMemoryTransfer() override
144 {
145 for (const auto &span : m_spans)
146 munmap(span.data, span.size);
147 }
148
149 std::optional<Buffer> dequeueBuffer() override
150 {
151 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_MMAP);
152 if (!fileDescriptor().call(VIDIOC_DQBUF, &v4l2Buffer))
153 return {};
154
155 const auto index = v4l2Buffer.index;
156
157 Q_ASSERT(index < m_spans.size());
158
159 auto &span = m_spans[index];
160
161 Q_ASSERT(span.inQueue);
162 span.inQueue = false;
163
164 return Buffer{ v4l2Buffer,
165 QByteArray(reinterpret_cast<const char *>(span.data), span.size) };
166 }
167
168 bool enqueueBuffer(quint32 index) override
169 {
170 Q_ASSERT(index < m_spans.size());
171 Q_ASSERT(!m_spans[index].inQueue);
172
173 auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
174 if (!fileDescriptor().call(VIDIOC_QBUF, &buf))
175 return false;
176
177 m_spans[index].inQueue = true;
178 return true;
179 }
180
181 quint32 buffersCount() const override { return static_cast<quint32>(m_spans.size()); }
182
183private:
185
186private:
187 std::vector<MemorySpan> m_spans;
188};
189} // namespace
190
192 : m_fileDescriptor(std::move(fileDescriptor))
193{
194 Q_ASSERT(m_fileDescriptor);
195 Q_ASSERT(!m_fileDescriptor->streamStarted());
196}
197
199{
200 Q_ASSERT(!m_fileDescriptor->streamStarted()); // to avoid possible corruptions
201}
202
204{
205 for (quint32 i = 0; i < buffersCount(); ++i)
206 if (!enqueueBuffer(i))
207 return false;
208
209 return true;
210}
211
214{
215 return UserPtrMemoryTransfer::create(std::move(fileDescriptor), imageSize);
216}
217
219{
220 return MMapMemoryTransfer::create(std::move(fileDescriptor));
221}
222
QV4L2MemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor)
virtual quint32 buffersCount() const =0
virtual bool enqueueBuffer(quint32 index)=0
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
static QDBusError::ErrorType get(const char *name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei imageSize
GLenum GLenum GLsizei void GLsizei void void * span
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define MAP_FAILED
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
unsigned int quint32
Definition qtypes.h:50
QV4L2MemoryTransferUPtr makeUserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, quint32 imageSize)
QV4L2MemoryTransferUPtr makeMMapMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor)
std::unique_ptr< QV4L2MemoryTransfer > QV4L2MemoryTransferUPtr
std::shared_ptr< QV4L2FileDescriptor > QV4L2FileDescriptorPtr
QObject::connect nullptr
view create()