6#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
7#include <QtMultimedia/private/qv4l2camera_p.h>
8#include <QtMultimedia/private/qv4l2filedescriptor_p.h>
9#include <QtMultimedia/private/qv4l2formats_p.h>
10#include <QtCore/qbytearray.h>
11#include <QtCore/qloggingcategory.h>
12#include <QtCore/qvarlengtharray.h>
19#if QT_CONFIG(linux_dmabuf)
20# include <QtMultimedia/private/qdmabufallocator_p.h>
21# include <QtMultimedia/private/qmultimedia_dmabuf_support_p.h>
32v4l2_buffer makeV4l2Buffer(v4l2_memory memoryType, quint32 index = 0)
35 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
36 buf.memory = memoryType;
47 if (
auto requestResult = fileDescriptor->requestBuffers(V4L2_MEMORY_USERPTR, 2)) {
48 std::unique_ptr<UserPtrMemoryTransfer> result(
new UserPtrMemoryTransfer{
49 std::move(fileDescriptor),
50 quint32(*requestResult),
55 return result->enqueueBuffers() ?
std::move(result) :
nullptr;
57 qCDebug(qLcV4L2MemoryTransfer) <<
"Cannot request V4L2_MEMORY_USERPTR buffers";
62 ~UserPtrMemoryTransfer()
override {
fileDescriptor().releaseAllBuffers(V4L2_MEMORY_USERPTR); }
64 std::optional<
Frame> dequeueFrame()
override
66 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_USERPTR);
67 if (!ioctl(VIDIOC_DQBUF, &v4l2Buffer))
70 Q_ASSERT(v4l2Buffer.index < m_byteArrays.size());
72 auto &byteArray = m_byteArrays[v4l2Buffer.index];
74 Q_ASSERT(!byteArray.isEmpty());
75 Q_ASSERT(qsizetype(v4l2Buffer.bytesused) <= byteArray.size());
78 byteArray.resize(v4l2Buffer.bytesused);
81 std::make_unique<QMemoryVideoBuffer>(std::move(byteArray), m_bytesPerLine),
84 if (!enqueueBuffer(v4l2Buffer.index))
85 qCWarning(qLcV4L2MemoryTransfer) <<
"Cannot add buffer";
90 quint32 buffersCount()
const override {
return static_cast<quint32>(m_byteArrays.size()); }
94 quint32 imageSize,
int bytesPerLine)
96 m_imageSize(imageSize),
97 m_bytesPerLine(bytesPerLine),
98 m_byteArrays(buffersCount)
102 bool enqueueBuffer(quint32 index)
override
104 Q_ASSERT(index < m_byteArrays.size());
105 Q_ASSERT(m_byteArrays[index].isEmpty());
107 auto buf = makeV4l2Buffer(V4L2_MEMORY_USERPTR, index);
108 static_assert(
sizeof(
decltype(buf.m.userptr)) ==
sizeof(size_t),
"Not compatible sizes");
110 m_byteArrays[index] = QByteArray(
static_cast<
int>(m_imageSize), Qt::Uninitialized);
112 buf.m.userptr = (
decltype(buf.m.userptr))m_byteArrays[index].data();
113 buf.length = m_byteArrays[index].size();
115 if (!ioctl(VIDIOC_QBUF, &buf)) {
116 qWarning() <<
"Couldn't add V4L2 buffer" << errno << qt_error_string(errno) << index;
123 const quint32 m_imageSize;
124 const int m_bytesPerLine;
125 std::vector<QByteArray> m_byteArrays;
133 void *data =
nullptr;
135 bool inQueue =
false;
140 auto requestResult = fileDescriptor->requestBuffers(V4L2_MEMORY_MMAP, 2);
141 if (!requestResult) {
142 qCWarning(qLcV4L2MemoryTransfer) <<
"Cannot request V4L2_MEMORY_MMAP buffers";
146 std::unique_ptr<MMapMemoryTransfer> result(
147 new MMapMemoryTransfer(
std::move(fileDescriptor), bytesPerLine));
149 return result->init(*requestResult) ?
std::move(result) :
nullptr;
152 bool init(quint32 buffersCount)
154 for (quint32 index = 0; index < buffersCount; ++index) {
155 auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
157 if (!ioctl(VIDIOC_QUERYBUF, &buf)) {
158 qWarning() <<
"Can't map buffer" << index;
162 auto mappedData = mmap(
nullptr, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
163 fileDescriptor().get(), buf.m.offset);
166 qWarning() <<
"mmap failed" << index << buf.length << buf.m.offset;
170 m_spans.push_back(MemorySpan{ mappedData, buf.length,
false });
173 m_spans.shrink_to_fit();
178 ~MMapMemoryTransfer()
override
180 for (
const auto &span : m_spans)
181 munmap(span.data, span.size);
185 std::optional<
Frame> dequeueFrame()
override
187 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_MMAP);
188 if (!ioctl(VIDIOC_DQBUF, &v4l2Buffer))
191 const auto index = v4l2Buffer.index;
193 Q_ASSERT(index < m_spans.size());
195 auto &span = m_spans[index];
197 Q_ASSERT(span.inQueue);
198 span.inQueue =
false;
200 Q_ASSERT(v4l2Buffer.bytesused <= span.size);
203 QByteArray byteArray(
reinterpret_cast<
const char *>(span.data), v4l2Buffer.bytesused);
206 v4l2Buffer.timestamp,
207 std::make_unique<QMemoryVideoBuffer>(std::move(byteArray), m_bytesPerLine),
210 if (!enqueueBuffer(index))
211 qCWarning(qLcV4L2MemoryTransfer) <<
"Cannot add buffer";
216 quint32 buffersCount()
const override {
return static_cast<quint32>(m_spans.size()); }
224 bool enqueueBuffer(quint32 index)
override
226 Q_ASSERT(index < m_spans.size());
227 Q_ASSERT(!m_spans[index].inQueue);
229 auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
230 if (!ioctl(VIDIOC_QBUF, &buf))
233 m_spans[index].inQueue =
true;
237 const int m_bytesPerLine;
238 std::vector<MemorySpan> m_spans;
241#if QT_CONFIG(linux_dmabuf)
243constexpr quint32 kDmaBufBuffersCount = 4;
245class DmaBufMemoryTransfer :
public QV4L2MemoryTransfer
248 using QDmaBuf = QtMultimediaPrivate::QDmaBuf;
249 using QDmaBufAllocator = QtMultimediaPrivate::QDmaBufAllocator;
250 using QDmaBufVector = QVarLengthArray<std::shared_ptr<QDmaBuf>, kDmaBufBuffersCount>;
252 static QV4L2MemoryTransferUPtr create(
const QV4L2FileDescriptorPtr &fileDescriptor,
253 quint32 imageSize,
int bytesPerLine)
255 auto allocator = QDmaBufAllocator::create();
257 qCDebug(qLcV4L2MemoryTransfer)
258 <<
"Cannot create DMA-BUF allocator:" << allocator.error();
263 fileDescriptor->requestBuffers(V4L2_MEMORY_DMABUF, kDmaBufBuffersCount);
264 if (!requestResult) {
265 qCDebug(qLcV4L2MemoryTransfer) <<
"Cannot request V4L2_MEMORY_DMABUF buffers";
269 QDmaBufVector buffers;
271 for (quint32 index = 0; index < quint32(*requestResult); ++index) {
272 auto buf = allocator->allocate(imageSize);
274 qCDebug(qLcV4L2MemoryTransfer) <<
"Cannot allocate DMA-BUF:" << buf.error();
275 fileDescriptor->requestBuffers(V4L2_MEMORY_DMABUF, 0);
278 buffers.push_back(std::move(*buf));
281 std::unique_ptr<DmaBufMemoryTransfer> result(
282 new DmaBufMemoryTransfer(fileDescriptor, std::move(buffers), bytesPerLine));
284 if (result->enqueueBuffers())
287 qCDebug(qLcV4L2MemoryTransfer) <<
"Cannot queue V4L2_MEMORY_DMABUF buffers";
288 fileDescriptor->requestBuffers(V4L2_MEMORY_DMABUF, 0);
292 ~DmaBufMemoryTransfer() override { fileDescriptor().releaseAllBuffers(V4L2_MEMORY_DMABUF); }
294 std::optional<Frame> dequeueFrame() override
296 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_DMABUF);
297 if (!ioctl(VIDIOC_DQBUF, &v4l2Buffer))
300 const quint32 index = v4l2Buffer.index;
301 Q_ASSERT(index < m_buffers.size());
302 const QDmaBuf &buf = *m_buffers[index];
305 auto data = QtMultimediaPrivate::copyDmaBufData(buf, 0, v4l2Buffer.bytesused);
307 if (!enqueueBuffer(index))
308 qCWarning(qLcV4L2MemoryTransfer) <<
"Cannot add buffer";
314 v4l2Buffer.timestamp,
315 std::make_unique<QMemoryVideoBuffer>(std::move(*data), m_bytesPerLine),
319 quint32 buffersCount()
const override {
return quint32(m_buffers.size()); }
322 DmaBufMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, QDmaBufVector buffers,
324 : QV4L2MemoryTransfer(std::move(fileDescriptor)),
325 m_bytesPerLine(bytesPerLine),
326 m_buffers(std::move(buffers))
330 bool enqueueBuffer(quint32 index) override
332 Q_ASSERT(index < m_buffers.size());
334 const auto &buf = *m_buffers[index];
336 auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_DMABUF, index);
337 v4l2Buffer.m.fd = buf.fd();
338 v4l2Buffer.length = quint32(buf.size());
340 if (!ioctl(VIDIOC_QBUF, &v4l2Buffer)) {
341 qWarning() <<
"Couldn't add V4L2 buffer" << errno << qt_error_string(errno) << index;
348 const int m_bytesPerLine;
349 QDmaBufVector m_buffers;
359 Q_ASSERT(m_fileDescriptor);
360 Q_ASSERT(!m_fileDescriptor->streamStarted());
365 Q_ASSERT(!m_fileDescriptor->streamStarted());
370 for (quint32 i = 0; i < buffersCount(); ++i)
371 if (!enqueueBuffer(i))
379 return m_fileDescriptor->call(request, arg);
383 const v4l2_pix_format &format)
385 return UserPtrMemoryTransfer::create(
std::move(fileDescriptor), format.sizeimage,
386 int(format.bytesperline));
390 const v4l2_pix_format &format)
392 return MMapMemoryTransfer::create(
std::move(fileDescriptor),
int(format.bytesperline));
395#if QT_CONFIG(linux_dmabuf)
QV4L2MemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor)
const QV4L2FileDescriptor & fileDescriptor() const
q23::expected< int, std::error_code > ioctl(int request, void *arg)
virtual ~QV4L2MemoryTransfer()
QV4L2MemoryTransferUPtr makeUserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, const v4l2_pix_format &format)
Q_STATIC_LOGGING_CATEGORY(qLcV4L2Camera, "qt.multimedia.v4l2.camera")
QV4L2MemoryTransferUPtr makeMMapMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, const v4l2_pix_format &format)
std::shared_ptr< QV4L2FileDescriptor > QV4L2FileDescriptorPtr