3#if defined(QGC_HAS_GST_AHARDWAREBUFFER_GPU_PATH)
8#include <QtCore/QLoggingCategory>
9#include <QtCore/QMutex>
10#include <QtCore/QMutexLocker>
11#include <QtCore/QSize>
12#include <QtGui/QOpenGLContext>
13#include <QtGui/QOpenGLFunctions>
14#include <private/qvideotexturehelper_p.h>
17#include <gst/android/gstandroid.h>
18#include <gst/video/video.h>
21#include <GLES2/gl2ext.h>
22#include <EGL/eglext.h>
24#include <android/hardware_buffer.h>
34constexpr int kMaxPlanes = 4;
36std::atomic<quint64> s_mapFailureCount{0};
37std::atomic<bool> s_loggedBadBackend{
false};
40QMutex s_extCacheMutex;
41QHash<EGLDisplay, bool> s_extCache;
43bool queryHasNativeBufferExt(EGLDisplay display)
45 QMutexLocker lock(&s_extCacheMutex);
46 auto it = s_extCache.find(display);
47 if (it != s_extCache.end()) {
50 const char *exts = eglQueryString(display, EGL_EXTENSIONS);
51 const bool supported = exts !=
nullptr
52 && strstr(exts,
"EGL_ANDROID_image_native_buffer") !=
nullptr;
53 s_extCache.insert(display, supported);
57QVideoFrameTexturesUPtr fail()
59 s_mapFailureCount.fetch_add(1, std::memory_order_relaxed);
63class FrameTextures final :
public QVideoFrameTextures
67 FrameTextures(QRhi *rhi, QSize size, QVideoFrameFormat::PixelFormat pixelFormat, GLuint name)
71 const auto *desc = QVideoTextureHelper::textureDescription(pixelFormat);
73 qCWarning(GstAHWBufLog) <<
"no QVideoTextureHelper description for format" << pixelFormat;
76 const QSize planeSize = desc->rhiPlaneSize(size, 0, rhi);
78 _texture.reset(rhi->newTexture(
79 desc->rhiTextureFormat(0, rhi, QVideoTextureHelper::TextureDescription::FallbackPolicy::Disable),
80 planeSize, 1, QRhiTexture::ExternalOES));
81 if (_texture && !_texture->createFrom({_name, 0})) {
82 qCWarning(GstAHWBufLog) <<
"QRhiTexture::createFrom failed for AHardwareBuffer plane 0";
87 ~FrameTextures()
override
92 void onFrameEndInvoked()
override
97 QRhiTexture *texture(uint plane)
const override
99 return plane == 0 ? _texture.get() :
nullptr;
103 void releaseGLTextures()
105 if (_released || !_rhi)
return;
107 _rhi->makeThreadLocalNativeContextCurrent();
108 if (
auto *ctx = QOpenGLContext::currentContext()) {
109 ctx->functions()->glDeleteTextures(1, &_name);
113 QRhi *_rhi =
nullptr;
115 bool _released =
false;
116 std::unique_ptr<QRhiTexture> _texture;
121GstAHardwareBufferVideoBuffer::GstAHardwareBufferVideoBuffer(GstSample *sample,
122 const GstVideoInfo &videoInfo,
123 const QVideoFrameFormat &format,
124 EGLDisplay eglDisplay)
126 , _eglDisplay(eglDisplay)
130GstAHardwareBufferVideoBuffer::~GstAHardwareBufferVideoBuffer() =
default;
132QAbstractVideoBuffer::MapData GstAHardwareBufferVideoBuffer::map(QVideoFrame::MapMode )
137bool GstAHardwareBufferVideoBuffer::validatePlaneHandles()
const
139 if (!_sample)
return false;
140 GstBuffer *buffer = gst_sample_get_buffer(_sample);
141 if (!buffer)
return false;
142 const int memCount = qMin(
int(gst_buffer_n_memory(buffer)), kMaxPlanes);
143 if (memCount <= 0)
return false;
144 for (
int i = 0; i < memCount; ++i) {
145 GstMemory *mem = gst_buffer_peek_memory(buffer, i);
146 if (!mem || !gst_is_ahardware_buffer_memory(mem))
return false;
147 if (!gst_ahardware_buffer_memory_get_buffer(GST_AHARDWARE_BUFFER_MEMORY_CAST(mem))) {
154QVideoFrameTexturesUPtr GstAHardwareBufferVideoBuffer::mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr & )
156 Q_ASSERT(rhi.thread()->isCurrentThread());
159 if (!_sample || _eglDisplay == EGL_NO_DISPLAY) {
162 if (rhi.backend() != QRhi::OpenGLES2) {
163 if (!s_loggedBadBackend.exchange(
true, std::memory_order_relaxed)) {
164 qCWarning(GstAHWBufLog) <<
"QRhi backend is not OpenGLES2; AHardwareBuffer path unsupported";
170 rhi.makeThreadLocalNativeContextCurrent();
173 EGLDisplay eglDpy = eglGetCurrentDisplay();
174 if (eglDpy == EGL_NO_DISPLAY) {
175 eglDpy = _eglDisplay;
178 GstBuffer *buffer = gst_sample_get_buffer(_sample);
179 if (!buffer)
return fail();
181 GstMemory *mem0 = gst_buffer_peek_memory(buffer, 0);
182 if (!mem0 || !gst_is_ahardware_buffer_memory(mem0)) {
186 if (!queryHasNativeBufferExt(eglDpy)) {
187 static bool s_warned =
false;
190 qCWarning(GstAHWBufLog) <<
"EGL_ANDROID_image_native_buffer unavailable; AHardwareBuffer path disabled";
195 static const auto eglGetNativeClientBufferANDROID_ =
196 reinterpret_cast<PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC
>(
197 eglGetProcAddress(
"eglGetNativeClientBufferANDROID"));
198 static const auto eglCreateImageKHR_ =
199 reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC
>(
200 eglGetProcAddress(
"eglCreateImageKHR"));
201 static const auto eglDestroyImageKHR_ =
202 reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC
>(
203 eglGetProcAddress(
"eglDestroyImageKHR"));
204 static const auto glEGLImageTargetTexture2DOES_ =
205 reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC
>(
206 eglGetProcAddress(
"glEGLImageTargetTexture2DOES"));
208 if (!eglGetNativeClientBufferANDROID_ || !eglCreateImageKHR_
209 || !eglDestroyImageKHR_ || !glEGLImageTargetTexture2DOES_) {
210 qCWarning(GstAHWBufLog) <<
"Required EGL/GL proc addresses unavailable";
214 const auto *nativeHandles =
static_cast<const QRhiGles2NativeHandles *
>(rhi.nativeHandles());
215 if (!nativeHandles || !nativeHandles->context) {
216 qCWarning(GstAHWBufLog) <<
"QRhi exposes no GL context";
221 GST_AHARDWARE_BUFFER_MEMORY_CAST(mem0));
223 qCWarning(GstAHWBufLog) <<
"gst_ahardware_buffer_memory_get_buffer returned null";
227 EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID_(ahwb);
229 qCWarning(GstAHWBufLog) <<
"eglGetNativeClientBufferANDROID returned null";
233 const EGLint attribs[] = { EGL_NONE };
234 EGLImageKHR image = eglCreateImageKHR_(eglDpy, EGL_NO_CONTEXT,
235 EGL_NATIVE_BUFFER_ANDROID,
236 clientBuffer, attribs);
237 if (image == EGL_NO_IMAGE_KHR) {
238 qCWarning(GstAHWBufLog) <<
"eglCreateImageKHR failed, err=" << Qt::hex << eglGetError();
244 QOpenGLFunctions functions(nativeHandles->context);
245 functions.glGenTextures(1, &name);
246 functions.glBindTexture(GL_TEXTURE_EXTERNAL_OES, name);
247 glEGLImageTargetTexture2DOES_(GL_TEXTURE_EXTERNAL_OES, image);
248 eglDestroyImageKHR_(eglDpy, image);
250 auto textures = std::make_unique<FrameTextures>(&rhi, _format.frameSize(),
251 QVideoFrameFormat::Format_SamplerExternalOES, name);
252 if (!textures->texture(0)) {
253 qCWarning(GstAHWBufLog) <<
"createFrom failed for plane 0 (SamplerExternalOES)";
254 functions.glDeleteTextures(1, &name);
260quint64 GstAHardwareBufferVideoBuffer::takeMapFailureCount()
262 return s_mapFailureCount.exchange(0, std::memory_order_relaxed);
265quint64 GstAHardwareBufferVideoBuffer::peekMapFailureCount()
267 return s_mapFailureCount.load(std::memory_order_relaxed);
#define QGC_LOGGING_CATEGORY(name, categoryStr)