4#if defined(QGC_HAS_GST_GLMEMORY_GPU_PATH)
8#include <QtCore/QLoggingCategory>
10#include <private/qvideotexturehelper_p.h>
14#include <gst/gl/gstglbasememory.h>
15#include <gst/gl/gstglmemory.h>
16#include <gst/gl/gstglsyncmeta.h>
17#include <gst/video/video.h>
20#include <QtGui/qopengl.h>
29constexpr int kMaxPlanes = 4;
31std::atomic<quint64> s_mapFailureCount{0};
33QVideoFrameTexturesUPtr fail()
35 s_mapFailureCount.fetch_add(1, std::memory_order_relaxed);
39class FrameTextures final :
public QVideoFrameTextures
42 FrameTextures(QRhi *rhi, QSize size, QVideoFrameFormat::PixelFormat pixelFormat,
43 std::array<GLuint, kMaxPlanes> names,
int count)
44 : _rhi(rhi), _size(size), _pixelFormat(pixelFormat), _names(names), _count(count)
46 const auto *desc = QVideoTextureHelper::textureDescription(pixelFormat);
50 for (
int i = 0; i < _count; ++i) {
54 qCWarning(GstGlBufLog) <<
"FrameTextures: GL texture id is 0 for plane" << i;
57 const QSize planeSize = desc->rhiPlaneSize(size, i, rhi);
58 _textures[i].reset(rhi->newTexture(desc->rhiTextureFormat(i, rhi), planeSize, 1, {}));
59 if (_textures[i] && !_textures[i]->createFrom({names[i], 0})) {
65 QRhiTexture *texture(uint plane)
const override
67 return (
int(plane) < _count) ? _textures[plane].get() :
nullptr;
73 bool matches(QRhi *rhi, QSize size, QVideoFrameFormat::PixelFormat pixelFormat,
74 const std::array<GLuint, kMaxPlanes> &names,
int count)
const
76 if (_rhi != rhi || _size != size || _pixelFormat != pixelFormat || _count != count) {
79 for (
int i = 0; i < _count; ++i) {
80 if (_names[i] == 0 || _names[i] != names[i] || !_textures[i]) {
90 QVideoFrameFormat::PixelFormat _pixelFormat = QVideoFrameFormat::Format_Invalid;
91 std::array<GLuint, kMaxPlanes> _names{};
93 std::unique_ptr<QRhiTexture> _textures[kMaxPlanes];
96std::atomic<quint64> s_textureReuseHits{0};
97std::atomic<quint64> s_gpuWaitCount{0};
98std::atomic<quint64> s_cpuWaitCount{0};
100std::atomic<bool> s_loggedNullSample{
false};
101std::atomic<bool> s_loggedBadBackend{
false};
103#define GL_WARN_ONCE(flag, ...) \
104 do { if (!(flag).exchange(true, std::memory_order_relaxed)) qCWarning(GstGlBufLog) << __VA_ARGS__; } while (0)
108GstGlVideoBuffer::GstGlVideoBuffer(GstSample *sample,
109 const GstVideoInfo &videoInfo,
110 const QVideoFrameFormat &format)
115GstGlVideoBuffer::~GstGlVideoBuffer() =
default;
117QAbstractVideoBuffer::MapData GstGlVideoBuffer::map(QVideoFrame::MapMode )
122bool GstGlVideoBuffer::validatePlaneHandles()
const
125 if (!_sample)
return false;
126 GstBuffer *buffer = gst_sample_get_buffer(_sample);
127 if (!buffer)
return false;
128 const int memCount = qMin(
int(gst_buffer_n_memory(buffer)), kMaxPlanes);
129 if (memCount <= 0)
return false;
130 for (
int i = 0; i < memCount; ++i) {
131 GstMemory *mem = gst_buffer_peek_memory(buffer, i);
132 if (!mem || !gst_is_gl_memory(mem))
return false;
137QVideoFrameTexturesUPtr GstGlVideoBuffer::mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr &old)
139 Q_ASSERT(rhi.thread()->isCurrentThread());
141 GL_WARN_ONCE(s_loggedNullSample,
"mapTextures: GstSample is null");
146 if (rhi.backend() != QRhi::OpenGLES2) {
147 GL_WARN_ONCE(s_loggedBadBackend,
"QRhi backend is" << rhi.backendName()
148 <<
"(GL backend required); GLMemory path disabled");
153 rhi.makeThreadLocalNativeContextCurrent();
155 GstBuffer *buffer = gst_sample_get_buffer(_sample);
156 if (!buffer)
return fail();
158 GstMemory *mem0 = gst_buffer_peek_memory(buffer, 0);
159 if (!mem0 || !gst_is_gl_memory(mem0)) {
163 GstVideoFrame frame{};
165 if (!gst_video_frame_map(&frame, &_videoInfo, buffer,
166 static_cast<GstMapFlags
>(GST_MAP_READ | GST_MAP_GL))) {
167 qCWarning(GstGlBufLog) <<
"gst_video_frame_map(GST_MAP_READ | GST_MAP_GL) failed";
177 GstGLContext *glCtx = GST_GL_BASE_MEMORY_CAST(mem0)->context;
178 if (GstGLSyncMeta *syncMeta = gst_buffer_get_gl_sync_meta(buffer); syncMeta && glCtx) {
179 gst_gl_sync_meta_wait_cpu(syncMeta, glCtx);
180 s_cpuWaitCount.fetch_add(1, std::memory_order_relaxed);
183 const int planeCount = qBound(1,
int(GST_VIDEO_FRAME_N_PLANES(&frame)), kMaxPlanes);
184 std::array<GLuint, kMaxPlanes> names{};
185 for (
int i = 0; i < planeCount; ++i) {
187 const GLuint *texIdPtr =
static_cast<const GLuint *
>(GST_VIDEO_FRAME_PLANE_DATA(&frame, i));
188 names[i] = texIdPtr ? *texIdPtr : 0;
191 gst_video_frame_unmap(&frame);
197 if (
auto *prev =
dynamic_cast<FrameTextures *
>(old.get());
198 prev && prev->matches(&rhi, _format.frameSize(), _format.pixelFormat(), names, planeCount)) {
199 s_textureReuseHits.fetch_add(1, std::memory_order_relaxed);
200 return std::move(old);
204 auto textures = std::make_unique<FrameTextures>(&rhi, _format.frameSize(),
205 _format.pixelFormat(), names, planeCount);
209 for (
int i = 0; i < planeCount; ++i) {
210 if (!textures->texture(
static_cast<uint
>(i))) {
211 qCWarning(GstGlBufLog) <<
"createFrom failed for plane" << i
212 <<
"format=" << _format.pixelFormat();
219quint64 GstGlVideoBuffer::peekTextureReuseHits()
221 return s_textureReuseHits.load(std::memory_order_relaxed);
224quint64 GstGlVideoBuffer::takeTextureReuseHits()
226 return s_textureReuseHits.exchange(0, std::memory_order_relaxed);
229quint64 GstGlVideoBuffer::takeSyncWaitCounts(quint64 &gpuWaits)
231 gpuWaits = s_gpuWaitCount.exchange(0, std::memory_order_relaxed);
232 return s_cpuWaitCount.exchange(0, std::memory_order_relaxed);
235quint64 GstGlVideoBuffer::takeMapFailureCount()
237 return s_mapFailureCount.exchange(0, std::memory_order_relaxed);
240quint64 GstGlVideoBuffer::peekMapFailureCount()
242 return s_mapFailureCount.load(std::memory_order_relaxed);
#define QGC_LOGGING_CATEGORY(name, categoryStr)