Revision control
Copy as Markdown
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstdmabufvideobuffer_p.h"
#include <QtMultimedia/private/qvideotexturehelper_p.h>
#include <QtCore/qloggingcategory.h>
#include <gst/allocators/gstdmabuf.h>
#include <gst/video/gstvideometa.h>
#include <array>
#include <optional>
QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(qLcGstDmaBufVideoBuffer, "qt.multimedia.gstreamer.dmabufvideobuffer")
using QtMultimediaPrivate::DmaBufImportMode;
using QtMultimediaPrivate::DmaBufPlane;
using QtMultimediaPrivate::DRMFormat;
static std::optional<DRMFormat> fourccFromGstVideoFormat(const GstVideoFormat format, int plane,
bool singleEGLImage)
{
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
constexpr DRMFormat argb_fourcc = DRMFormat::ARGB8888;
constexpr DRMFormat rgba_fourcc = DRMFormat::ABGR8888;
constexpr DRMFormat rgb_fourcc = DRMFormat::BGR888;
constexpr DRMFormat rg_fourcc = DRMFormat::GR88;
#else
constexpr DRMFormat argb_fourcc = DRMFormat::BGRA8888;
constexpr DRMFormat rgba_fourcc = DRMFormat::RGBA8888;
constexpr DRMFormat rgb_fourcc = DRMFormat::RGB888;
constexpr DRMFormat rg_fourcc = DRMFormat::RG88;
#endif
switch (format) {
case GST_VIDEO_FORMAT_RGB16:
case GST_VIDEO_FORMAT_BGR16:
return DRMFormat::RGB565;
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
return rgb_fourcc;
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_BGRA:
return argb_fourcc;
case GST_VIDEO_FORMAT_AYUV:
if (singleEGLImage)
return DRMFormat::AYUV;
[[fallthrough]];
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_xBGR:
return rgba_fourcc;
case GST_VIDEO_FORMAT_GRAY8:
return DRMFormat::R8;
case GST_VIDEO_FORMAT_YUY2:
return DRMFormat::YUYV;
case GST_VIDEO_FORMAT_UYVY:
return DRMFormat::UYVY;
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
if (singleEGLImage)
return DRMFormat::R16;
return rg_fourcc;
case GST_VIDEO_FORMAT_NV12:
if (singleEGLImage)
return DRMFormat::NV12;
[[fallthrough]];
case GST_VIDEO_FORMAT_NV21:
if (singleEGLImage)
return DRMFormat::NV21;
return plane == 0 ? DRMFormat::R8 : rg_fourcc;
case GST_VIDEO_FORMAT_I420:
if (singleEGLImage)
return DRMFormat::YUV420;
[[fallthrough]];
case GST_VIDEO_FORMAT_YV12:
if (singleEGLImage)
return DRMFormat::YVU420;
[[fallthrough]];
case GST_VIDEO_FORMAT_Y41B:
if (singleEGLImage)
return DRMFormat::YUV411;
[[fallthrough]];
case GST_VIDEO_FORMAT_Y42B:
if (singleEGLImage)
return DRMFormat::YUV422;
[[fallthrough]];
case GST_VIDEO_FORMAT_Y444:
if (singleEGLImage)
return DRMFormat::YUV444;
return DRMFormat::R8;
case GST_VIDEO_FORMAT_BGR10A2_LE:
return DRMFormat::BGRA1010102;
case GST_VIDEO_FORMAT_P010_10LE:
case GST_VIDEO_FORMAT_P010_10BE:
if (singleEGLImage)
return DRMFormat::P010;
return plane == 0 ? DRMFormat::R16 : DRMFormat::RG1616;
default:
return std::nullopt;
}
}
QGstDmaBufVideoBuffer::QGstDmaBufVideoBuffer(QSpan<const DmaBufPlane> planes,
DmaBufImportMode importMode, QGstBufferHandle buffer,
QVideoFrameFormat::PixelFormat format, QSize size)
: QDmaBufVideoBuffer(format, size, planes, std::make_shared<QGstBufferHandle>(buffer),
importMode),
m_buffer(std::move(buffer))
{
}
QGstDmaBufVideoBuffer::~QGstDmaBufVideoBuffer() = default;
std::unique_ptr<QGstDmaBufVideoBuffer>
QGstDmaBufVideoBuffer::create(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo,
const QVideoFrameFormat &frameFormat)
{
using namespace QtMultimediaPrivate;
if (!buffer.isValid())
return nullptr;
GstBuffer *gstBuffer = buffer.get();
if (!gst_is_dmabuf_memory(gst_buffer_peek_memory(gstBuffer, 0)))
return nullptr;
const GstVideoInfo &gstVideoInfo = videoInfo.gstVideoInfo;
if (!gstVideoInfo.finfo)
return nullptr;
const GstVideoMeta *videoMeta = gst_buffer_get_video_meta(gstBuffer);
GstVideoFormat format = videoMeta ? videoMeta->format : GST_VIDEO_INFO_FORMAT(&gstVideoInfo);
if (format == GST_VIDEO_FORMAT_UNKNOWN)
format = GST_VIDEO_INFO_FORMAT(&gstVideoInfo);
const int nPlanes = videoMeta ? videoMeta->n_planes : GST_VIDEO_INFO_N_PLANES(&gstVideoInfo);
const int nMemoryBlocks = gst_buffer_n_memory(gstBuffer);
if (nPlanes < 1 || nPlanes > 4 || (nMemoryBlocks != 1 && nMemoryBlocks != nPlanes))
return nullptr;
const QVideoFrameFormat::PixelFormat pixelFormat = frameFormat.pixelFormat();
const auto *desc = QVideoTextureHelper::textureDescription(pixelFormat);
if (!desc || desc->nplanes != nPlanes)
return nullptr;
static const bool singleEGLImageEnv =
qEnvironmentVariableIntValue("QT_GSTREAMER_FORCE_SINGLE_EGLIMAGE") != 0;
const bool singleEGLImage =
QVideoTextureHelper::forceGlTextureExternalOesIsSet() || singleEGLImageEnv;
std::array<DmaBufPlane, 4> planes;
std::array<int, 4> fds{ -1, -1, -1, -1 };
for (int i = 0; i < nMemoryBlocks && i < 4; ++i)
fds[i] = gst_dmabuf_memory_get_fd(gst_buffer_peek_memory(gstBuffer, i));
const DRMModifier modifier = videoInfo.dmaDrmModifier ? DRMModifier{ *videoInfo.dmaDrmModifier }
: DmaBufFormatModifierInvalid;
for (int plane = 0; plane < nPlanes; ++plane) {
const std::optional<DRMFormat> fourcc =
fourccFromGstVideoFormat(format, plane, singleEGLImage);
if (!fourcc) {
qCWarning(qLcGstDmaBufVideoBuffer)
<< "Unsupported format for DMABuf:" << gst_video_format_to_string(format)
<< "plane:" << plane << "singleEGLImage:" << singleEGLImage;
return nullptr;
}
const int fd = (plane < nMemoryBlocks && fds[plane] >= 0) ? fds[plane] : fds[0];
if (fd < 0)
return nullptr;
planes[plane].fd = fd;
// videoMeta/videoInfo offset can be incorrect when each plane has a separate memory block
if (nPlanes == nMemoryBlocks) {
planes[plane].offset = 0;
} else if (videoMeta) {
planes[plane].offset = videoMeta->offset[plane];
} else {
planes[plane].offset = GST_VIDEO_INFO_PLANE_OFFSET(&gstVideoInfo, plane);
}
if (videoMeta)
planes[plane].pitch = videoMeta->stride[plane];
else
planes[plane].pitch = GST_VIDEO_INFO_PLANE_STRIDE(&gstVideoInfo, plane);
planes[plane].drmFormat = *fourcc;
planes[plane].modifier = modifier;
}
const DmaBufImportMode importMode =
singleEGLImage ? DmaBufImportMode::SingleImage : DmaBufImportMode::PerPlane;
return std::unique_ptr<QGstDmaBufVideoBuffer>(
new QGstDmaBufVideoBuffer(QSpan<const DmaBufPlane>(planes.data(), nPlanes), importMode,
std::move(buffer), pixelFormat, frameFormat.frameSize()));
}
QT_END_NAMESPACE