Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpeghwaccel_videotoolbox.mm
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#if !defined(Q_OS_DARWIN)
7#error "Configuration error"
8#endif
9
10#include <qvideoframeformat.h>
12#include <qloggingcategory.h>
13#include "private/qvideotexturehelper_p.h"
14
15#include <rhi/qrhi.h>
16
17#include <CoreVideo/CVMetalTexture.h>
18#include <CoreVideo/CVMetalTextureCache.h>
19
20#include <qopenglcontext.h>
21#ifdef Q_OS_MACOS
22#import <AppKit/AppKit.h>
23#endif
24#ifdef Q_OS_IOS
25#import <OpenGLES/EAGL.h>
26#endif
27#import <Metal/Metal.h>
28
30
31static Q_LOGGING_CATEGORY(qLcVideotoolbox, "qt.multimedia.ffmpeg.videotoolbox")
32
33namespace QFFmpeg
34{
35
36static CVMetalTextureCacheRef &mtc(void *&cache) { return reinterpret_cast<CVMetalTextureCacheRef &>(cache); }
37
38class VideoToolBoxTextureSet : public TextureSet
39{
40public:
41 ~VideoToolBoxTextureSet();
42 qint64 textureHandle(QRhi *, int plane) override;
43
44 QRhi *rhi = nullptr;
45 CVMetalTextureRef cvMetalTexture[3] = {};
46
47#if defined(Q_OS_MACOS)
48 CVOpenGLTextureRef cvOpenGLTexture = nullptr;
49#elif defined(Q_OS_IOS)
50 CVOpenGLESTextureRef cvOpenGLESTexture = nullptr;
51#endif
52
53 CVImageBufferRef m_buffer = nullptr;
54};
55
56VideoToolBoxTextureConverter::VideoToolBoxTextureConverter(QRhi *rhi)
57 : TextureConverterBackend(rhi)
58{
59 if (!rhi)
60 return;
61
62 if (rhi->backend() == QRhi::Metal) {
63 const auto *metal = static_cast<const QRhiMetalNativeHandles *>(rhi->nativeHandles());
64
65 // Create a Metal Core Video texture cache from the pixel buffer.
66 Q_ASSERT(!cvMetalTextureCache);
67 if (CVMetalTextureCacheCreate(
68 kCFAllocatorDefault,
69 nil,
70 (id<MTLDevice>)metal->dev,
71 nil,
72 &mtc(cvMetalTextureCache)) != kCVReturnSuccess) {
73 qWarning() << "Metal texture cache creation failed";
74 rhi = nullptr;
75 }
76 } else if (rhi->backend() == QRhi::OpenGLES2) {
77#if QT_CONFIG(opengl)
78#ifdef Q_OS_MACOS
79 const auto *gl = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
80
81 auto nsGLContext = gl->context->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext();
82 auto nsGLPixelFormat = nsGLContext.pixelFormat.CGLPixelFormatObj;
83
84 // Create an OpenGL CoreVideo texture cache from the pixel buffer.
85 if (CVOpenGLTextureCacheCreate(
86 kCFAllocatorDefault,
87 nullptr,
88 reinterpret_cast<CGLContextObj>(nsGLContext.CGLContextObj),
89 nsGLPixelFormat,
90 nil,
91 &cvOpenGLTextureCache)) {
92 qWarning() << "OpenGL texture cache creation failed";
93 rhi = nullptr;
94 }
95#endif
96#ifdef Q_OS_IOS
97 // Create an OpenGL CoreVideo texture cache from the pixel buffer.
98 if (CVOpenGLESTextureCacheCreate(
99 kCFAllocatorDefault,
100 nullptr,
101 [EAGLContext currentContext],
102 nullptr,
103 &cvOpenGLESTextureCache)) {
104 qWarning() << "OpenGL texture cache creation failed";
105 rhi = nullptr;
106 }
107#endif
108#else
109 rhi = nullptr;
110#endif // QT_CONFIG(opengl)
111 }
112}
113
114VideoToolBoxTextureConverter::~VideoToolBoxTextureConverter()
115{
116 freeTextureCaches();
117}
118
119void VideoToolBoxTextureConverter::freeTextureCaches()
120{
121 if (cvMetalTextureCache)
122 CFRelease(cvMetalTextureCache);
123 cvMetalTextureCache = nullptr;
124#if defined(Q_OS_MACOS)
125 if (cvOpenGLTextureCache)
126 CFRelease(cvOpenGLTextureCache);
127 cvOpenGLTextureCache = nullptr;
128#elif defined(Q_OS_IOS)
129 if (cvOpenGLESTextureCache)
130 CFRelease(cvOpenGLESTextureCache);
131 cvOpenGLESTextureCache = nullptr;
132#endif
133}
134
136{
137 switch (f) {
138 default:
140 return MTLPixelFormatInvalid;
142 return MTLPixelFormatRGBA8Unorm;
144 return MTLPixelFormatBGRA8Unorm;
145 case QRhiTexture::R8:
146 return MTLPixelFormatR8Unorm;
147 case QRhiTexture::RG8:
148 return MTLPixelFormatRG8Unorm;
149 case QRhiTexture::R16:
150 return MTLPixelFormatR16Unorm;
152 return MTLPixelFormatRG16Unorm;
153
155 return MTLPixelFormatRGBA16Float;
157 return MTLPixelFormatRGBA32Float;
159 return MTLPixelFormatR16Float;
161 return MTLPixelFormatR32Float;
162 }
163}
164
165TextureSet *VideoToolBoxTextureConverter::getTextures(AVFrame *frame)
166{
167 if (!rhi)
168 return nullptr;
169
170 bool needsConversion = false;
171 QVideoFrameFormat::PixelFormat pixelFormat = QFFmpegVideoBuffer::toQtPixelFormat(HWAccel::format(frame), &needsConversion);
172 if (needsConversion) {
173 // qDebug() << "XXXXXXXXXXXX pixel format needs conversion" << pixelFormat << HWAccel::format(frame);
174 return nullptr;
175 }
176
177 CVPixelBufferRef buffer = (CVPixelBufferRef)frame->data[3];
178
179 auto textureSet = std::make_unique<VideoToolBoxTextureSet>();
180 textureSet->m_buffer = buffer;
181 textureSet->rhi = rhi;
182 CVPixelBufferRetain(buffer);
183
184 auto *textureDescription = QVideoTextureHelper::textureDescription(pixelFormat);
185 int bufferPlanes = CVPixelBufferGetPlaneCount(buffer);
186// qDebug() << "XXXXX getTextures" << pixelFormat << bufferPlanes << buffer;
187
188 if (rhi->backend() == QRhi::Metal) {
189 for (int plane = 0; plane < bufferPlanes; ++plane) {
190 size_t width = CVPixelBufferGetWidth(buffer);
191 size_t height = CVPixelBufferGetHeight(buffer);
192 width = textureDescription->widthForPlane(width, plane);
193 height = textureDescription->heightForPlane(height, plane);
194
195 // Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
196 auto ret = CVMetalTextureCacheCreateTextureFromImage(
197 kCFAllocatorDefault,
198 mtc(cvMetalTextureCache),
199 buffer, nil,
200 rhiTextureFormatToMetalFormat(textureDescription->textureFormat[plane]),
201 width, height,
202 plane,
203 &textureSet->cvMetalTexture[plane]);
204
205 if (ret != kCVReturnSuccess)
206 qWarning() << "texture creation failed" << ret;
207// auto t = CVMetalTextureGetTexture(textureSet->cvMetalTexture[plane]);
208// qDebug() << " metal texture for plane" << plane << "is" << quint64(textureSet->cvMetalTexture[plane]) << width << height;
209// qDebug() << " " << t.iosurfacePlane << t.pixelFormat << t.width << t.height;
210 }
211 } else if (rhi->backend() == QRhi::OpenGLES2) {
212#if QT_CONFIG(opengl)
213#ifdef Q_OS_MACOS
214 CVOpenGLTextureCacheFlush(cvOpenGLTextureCache, 0);
215 // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
216 const CVReturn cvret = CVOpenGLTextureCacheCreateTextureFromImage(
217 kCFAllocatorDefault,
218 cvOpenGLTextureCache,
219 buffer,
220 nil,
221 &textureSet->cvOpenGLTexture);
222 if (cvret != kCVReturnSuccess) {
223 qCWarning(qLcVideotoolbox) << "OpenGL texture creation failed" << cvret;
224 return nullptr;
225 }
226
227 Q_ASSERT(CVOpenGLTextureGetTarget(textureSet->cvOpenGLTexture) == GL_TEXTURE_RECTANGLE);
228#endif
229#ifdef Q_OS_IOS
230 CVOpenGLESTextureCacheFlush(cvOpenGLESTextureCache, 0);
231 // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
232 const CVReturn cvret = CVOpenGLESTextureCacheCreateTextureFromImage(
233 kCFAllocatorDefault,
234 cvOpenGLESTextureCache,
235 buffer,
236 nil,
237 GL_TEXTURE_2D,
238 GL_RGBA,
239 CVPixelBufferGetWidth(buffer),
240 CVPixelBufferGetHeight(buffer),
241 GL_RGBA,
243 0,
244 &textureSet->cvOpenGLESTexture);
245 if (cvret != kCVReturnSuccess) {
246 qCWarning(qLcVideotoolbox) << "OpenGL ES texture creation failed" << cvret;
247 return nullptr;
248 }
249#endif
250#endif
251 }
252
253 return textureSet.release();
254}
255
256VideoToolBoxTextureSet::~VideoToolBoxTextureSet()
257{
258 for (int i = 0; i < 4; ++i)
259 if (cvMetalTexture[i])
260 CFRelease(cvMetalTexture[i]);
261#if defined(Q_OS_MACOS)
262 if (cvOpenGLTexture)
263 CVOpenGLTextureRelease(cvOpenGLTexture);
264#elif defined(Q_OS_IOS)
265 if (cvOpenGLESTexture)
266 CFRelease(cvOpenGLESTexture);
267#endif
268 CVPixelBufferRelease(m_buffer);
269}
270
271qint64 VideoToolBoxTextureSet::textureHandle(QRhi *, int plane)
272{
273 if (rhi->backend() == QRhi::Metal)
274 return cvMetalTexture[plane] ? qint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0;
275#if QT_CONFIG(opengl)
276 Q_ASSERT(plane == 0);
277#ifdef Q_OS_MACOS
278 return CVOpenGLTextureGetName(cvOpenGLTexture);
279#endif
280#ifdef Q_OS_IOS
281 return CVOpenGLESTextureGetName(cvOpenGLESTexture);
282#endif
283#endif
284}
285
286}
287
static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
static QVideoFrameFormat::PixelFormat toQtPixelFormat(AVPixelFormat avPixelFormat, bool *needsConversion=nullptr)
Native interface to an NSOpenGLContext on \macos.
\variable QRhiGles2InitParams::format
\inmodule QtRhi
Format
Specifies the texture format.
Definition qrhi.h:914
@ RGBA32F
Definition qrhi.h:926
@ RGBA16F
Definition qrhi.h:925
@ UnknownFormat
Definition qrhi.h:915
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
Implementation backend() const
Definition qrhi.cpp:8651
@ Metal
Definition qrhi.h:1811
@ OpenGLES2
Definition qrhi.h:1809
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10137
PixelFormat
Enumerates video data types.
QCache< int, Employee > cache
[0]
Combined button and popup list for selecting options.
const TextureDescription * textureDescription(QVideoFrameFormat::PixelFormat format)
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
return ret
GLint GLsizei GLsizei height
GLfloat GLfloat f
GLenum GLuint buffer
GLint GLsizei width
#define GL_TEXTURE_RECTANGLE
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define GL_UNSIGNED_BYTE
#define GL_RGBA
long long qint64
Definition qtypes.h:60
QFrame frame
[0]
QRhiTexture::Format textureFormat[maxPlanes]
int widthForPlane(int width, int plane) const
int heightForPlane(int height, int plane) const