Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
5
6#include <QtMultimedia/private/qv4l2filedescriptor_p.h>
7#include <QtCore/qdebug.h>
8#include <QtCore/qloggingcategory.h>
9
10#include <sys/mman.h>
11
12#include <optional>
13
14QT_BEGIN_NAMESPACE
15
16namespace QtV4L2 {
17
18Q_STATIC_LOGGING_CATEGORY(qLcV4L2MemoryTransfer, "qt.multimedia.v4l2.memorytransfer");
19
20namespace {
21
22v4l2_buffer makeV4l2Buffer(quint32 memoryType, quint32 index = 0)
23{
24 v4l2_buffer buf = {};
25 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
26 buf.memory = memoryType;
27 buf.index = index;
28 return buf;
29}
30
31class UserPtrMemoryTransfer : public QV4L2MemoryTransfer
32{
33public:
34 static QV4L2MemoryTransferUPtr create(QV4L2FileDescriptorPtr fileDescriptor, quint32 imageSize)
35 {
36 quint32 buffersCount = 2;
37 if (!fileDescriptor->requestBuffers(V4L2_MEMORY_USERPTR, buffersCount)) {
38 qCDebug(qLcV4L2MemoryTransfer) << "Cannot request V4L2_MEMORY_USERPTR buffers";
39 return {};
40 }
41
42 std::unique_ptr<UserPtrMemoryTransfer> result(
43 new UserPtrMemoryTransfer(std::move(fileDescriptor), buffersCount, imageSize));
44
45 return result->enqueueBuffers() ? std::move(result) : nullptr;
46 }
47
48 std::optional<Buffer> dequeueBuffer() override
49 {
50 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_USERPTR);
51 if (!fileDescriptor().call(VIDIOC_DQBUF, &v4l2Buffer))
52 return {};
53
54 Q_ASSERT(v4l2Buffer.index < m_byteArrays.size());
55
56 auto &byteArray = m_byteArrays[v4l2Buffer.index];
57
58 Q_ASSERT(!byteArray.isEmpty());
59 Q_ASSERT(qsizetype(v4l2Buffer.bytesused) <= byteArray.size());
60
61 // truncate jpeg
62 byteArray.resize(v4l2Buffer.bytesused);
63 return Buffer{ v4l2Buffer, std::move(byteArray) };
64 }
65
66 bool enqueueBuffer(quint32 index) override
67 {
68 Q_ASSERT(index < m_byteArrays.size());
69 Q_ASSERT(m_byteArrays[index].isEmpty());
70
71 auto buf = makeV4l2Buffer(V4L2_MEMORY_USERPTR, index);
72 static_assert(sizeof(decltype(buf.m.userptr)) == sizeof(size_t), "Not compatible sizes");
73
74 m_byteArrays[index] = QByteArray(static_cast<int>(m_imageSize), Qt::Uninitialized);
75
76 buf.m.userptr = (decltype(buf.m.userptr))m_byteArrays[index].data();
77 buf.length = m_byteArrays[index].size();
78
79 if (!fileDescriptor().call(VIDIOC_QBUF, &buf)) {
80 qWarning() << "Couldn't add V4L2 buffer" << errno << qt_error_string(errno) << index;
81 return false;
82 }
83
84 return true;
85 }
86
87 quint32 buffersCount() const override { return static_cast<quint32>(m_byteArrays.size()); }
88
89private:
90 UserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, quint32 buffersCount,
91 quint32 imageSize)
92 : QV4L2MemoryTransfer(std::move(fileDescriptor)),
93 m_imageSize(imageSize),
94 m_byteArrays(buffersCount)
95 {
96 }
97
98private:
99 quint32 m_imageSize;
100 std::vector<QByteArray> m_byteArrays;
101};
102
103class MMapMemoryTransfer : public QV4L2MemoryTransfer
104{
105public:
106 struct MemorySpan
107 {
108 void *data = nullptr;
109 size_t size = 0;
110 bool inQueue = false;
111 };
112
113 static QV4L2MemoryTransferUPtr create(QV4L2FileDescriptorPtr fileDescriptor)
114 {
115 quint32 buffersCount = 2;
116 if (!fileDescriptor->requestBuffers(V4L2_MEMORY_MMAP, buffersCount)) {
117 qCWarning(qLcV4L2MemoryTransfer) << "Cannot request V4L2_MEMORY_MMAP buffers";
118 return {};
119 }
120
121 std::unique_ptr<MMapMemoryTransfer> result(
122 new MMapMemoryTransfer(std::move(fileDescriptor)));
123
124 return result->init(buffersCount) ? std::move(result) : nullptr;
125 }
126
127 bool init(quint32 buffersCount)
128 {
129 for (quint32 index = 0; index < buffersCount; ++index) {
130 auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
131
132 if (!fileDescriptor().call(VIDIOC_QUERYBUF, &buf)) {
133 qWarning() << "Can't map buffer" << index;
134 return false;
135 }
136
137 auto mappedData = mmap(nullptr, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
138 fileDescriptor().get(), buf.m.offset);
139
140 if (mappedData == MAP_FAILED) {
141 qWarning() << "mmap failed" << index << buf.length << buf.m.offset;
142 return false;
143 }
144
145 m_spans.push_back(MemorySpan{ mappedData, buf.length, false });
146 }
147
148 m_spans.shrink_to_fit();
149
150 return enqueueBuffers();
151 }
152
153 ~MMapMemoryTransfer() override
154 {
155 for (const auto &span : m_spans)
156 munmap(span.data, span.size);
157 }
158
159 std::optional<Buffer> dequeueBuffer() override
160 {
161 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_MMAP);
162 if (!fileDescriptor().call(VIDIOC_DQBUF, &v4l2Buffer))
163 return {};
164
165 const auto index = v4l2Buffer.index;
166
167 Q_ASSERT(index < m_spans.size());
168
169 auto &span = m_spans[index];
170
171 Q_ASSERT(span.inQueue);
172 span.inQueue = false;
173
174 Q_ASSERT(v4l2Buffer.bytesused <= span.size);
175
176 // truncate jpeg
177 QByteArray byteArray(reinterpret_cast<const char *>(span.data), v4l2Buffer.bytesused);
178
179 return Buffer{ v4l2Buffer, std::move(byteArray) };
180 }
181
182 bool enqueueBuffer(quint32 index) override
183 {
184 Q_ASSERT(index < m_spans.size());
185 Q_ASSERT(!m_spans[index].inQueue);
186
187 auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
188 if (!fileDescriptor().call(VIDIOC_QBUF, &buf))
189 return false;
190
191 m_spans[index].inQueue = true;
192 return true;
193 }
194
195 quint32 buffersCount() const override { return static_cast<quint32>(m_spans.size()); }
196
197private:
198 using QV4L2MemoryTransfer::QV4L2MemoryTransfer;
199
200private:
201 std::vector<MemorySpan> m_spans;
202};
203} // namespace
204
207{
208 Q_ASSERT(m_fileDescriptor);
209 Q_ASSERT(!m_fileDescriptor->streamStarted());
210}
211
213{
214 Q_ASSERT(!m_fileDescriptor->streamStarted()); // to avoid possible corruptions
215}
216
218{
219 for (quint32 i = 0; i < buffersCount(); ++i)
220 if (!enqueueBuffer(i))
221 return false;
222
223 return true;
224}
225
227 quint32 imageSize)
228{
229 return UserPtrMemoryTransfer::create(std::move(fileDescriptor), imageSize);
230}
231
233{
234 return MMapMemoryTransfer::create(std::move(fileDescriptor));
235}
236
237} // namespace QtV4L2
238
239QT_END_NAMESPACE
QV4L2MemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor)
const QV4L2FileDescriptor & fileDescriptor() const
QV4L2MemoryTransferUPtr makeMMapMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor)
Q_STATIC_LOGGING_CATEGORY(qLcV4L2Camera, "qt.multimedia.v4l2.camera")
QV4L2MemoryTransferUPtr makeUserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, quint32 imageSize)
std::shared_ptr< QV4L2FileDescriptor > QV4L2FileDescriptorPtr
#define MAP_FAILED