Revision control
+ // 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