Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
avfvideobuffer.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "avfvideobuffer_p.h"
5#include <rhi/qrhi.h>
6#include <CoreVideo/CVMetalTexture.h>
7#include <CoreVideo/CVMetalTextureCache.h>
8#include <QtGui/qopenglcontext.h>
9
10#include <private/qvideotexturehelper_p.h>
11#include "qavfhelpers_p.h"
12
13#import <AVFoundation/AVFoundation.h>
14#import <Metal/Metal.h>
15
17
19 : QAbstractVideoBuffer(sink->rhi() ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle, sink->rhi()),
20 sink(sink),
21 m_buffer(buffer)
22{
23// m_type = QVideoFrame::NoHandle;
24// qDebug() << "RHI" << m_rhi;
25 CVPixelBufferRetain(m_buffer);
26 const bool rhiIsOpenGL = sink && sink->rhi() && sink->rhi()->backend() == QRhi::OpenGLES2;
27 m_format = QAVFHelpers::videoFormatForImageBuffer(m_buffer, rhiIsOpenGL);
28}
29
31{
33 for (int i = 0; i < 3; ++i)
34 if (cvMetalTexture[i])
35 CFRelease(cvMetalTexture[i]);
36#if defined(Q_OS_MACOS)
37 if (cvOpenGLTexture)
38 CVOpenGLTextureRelease(cvOpenGLTexture);
39#elif defined(Q_OS_IOS)
40 if (cvOpenGLESTexture)
41 CFRelease(cvOpenGLESTexture);
42#endif
43 CVPixelBufferRelease(m_buffer);
44}
45
47{
49
50 if (m_mode == QVideoFrame::NotMapped) {
51 CVPixelBufferLockBaseAddress(m_buffer, mode == QVideoFrame::ReadOnly
52 ? kCVPixelBufferLock_ReadOnly
53 : 0);
54 m_mode = mode;
55 }
56
57 mapData.nPlanes = CVPixelBufferGetPlaneCount(m_buffer);
59
60 if (!mapData.nPlanes) {
61 // single plane
62 mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRow(m_buffer);
63 mapData.data[0] = static_cast<uchar*>(CVPixelBufferGetBaseAddress(m_buffer));
64 mapData.size[0] = CVPixelBufferGetDataSize(m_buffer);
65 mapData.nPlanes = mapData.data[0] ? 1 : 0;
66 return mapData;
67 }
68
69 // For a bi-planar or tri-planar format we have to set the parameters correctly:
70 for (int i = 0; i < mapData.nPlanes; ++i) {
71 mapData.bytesPerLine[i] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, i);
72 mapData.size[i] = mapData.bytesPerLine[i]*CVPixelBufferGetHeightOfPlane(m_buffer, i);
73 mapData.data[i] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, i));
74 }
75
76 return mapData;
77}
78
80{
81 if (m_mode != QVideoFrame::NotMapped) {
82 CVPixelBufferUnlockBaseAddress(m_buffer, m_mode == QVideoFrame::ReadOnly
83 ? kCVPixelBufferLock_ReadOnly
84 : 0);
86 }
87}
88
90{
91 switch (f) {
92 default:
94 return MTLPixelFormatInvalid;
96 return MTLPixelFormatRGBA8Unorm;
98 return MTLPixelFormatBGRA8Unorm;
99 case QRhiTexture::R8:
100 return MTLPixelFormatR8Unorm;
101 case QRhiTexture::RG8:
102 return MTLPixelFormatRG8Unorm;
103 case QRhiTexture::R16:
104 return MTLPixelFormatR16Unorm;
106 return MTLPixelFormatRG16Unorm;
107
109 return MTLPixelFormatRGBA16Float;
111 return MTLPixelFormatRGBA32Float;
113 return MTLPixelFormatR16Float;
115 return MTLPixelFormatR32Float;
116 }
117}
118
119
121{
122 auto *textureDescription = QVideoTextureHelper::textureDescription(m_format.pixelFormat());
123 int bufferPlanes = CVPixelBufferGetPlaneCount(m_buffer);
124// qDebug() << "texture handle" << plane << m_rhi << (m_rhi->backend() == QRhi::Metal) << bufferPlanes;
125 if (plane > 0 && plane >= bufferPlanes)
126 return 0;
127 if (!m_rhi)
128 return 0;
129 if (m_rhi->backend() == QRhi::Metal) {
130 if (!cvMetalTexture[plane]) {
131 size_t width = CVPixelBufferGetWidth(m_buffer);
132 size_t height = CVPixelBufferGetHeight(m_buffer);
133 width = textureDescription->widthForPlane(width, plane);
134 height = textureDescription->heightForPlane(height, plane);
135
136 // Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
137 QMutexLocker locker(sink->textureCacheMutex());
138 auto ret = CVMetalTextureCacheCreateTextureFromImage(
139 kCFAllocatorDefault,
140 sink->cvMetalTextureCache,
141 m_buffer, nil,
142 rhiTextureFormatToMetalFormat(textureDescription->textureFormat[plane]),
143 width, height,
144 plane,
145 &cvMetalTexture[plane]);
146
147 if (ret != kCVReturnSuccess)
148 qWarning() << "texture creation failed" << ret;
149// auto t = CVMetalTextureGetTexture(cvMetalTexture[plane]);
150// qDebug() << " metal texture is" << quint64(cvMetalTexture[plane]) << width << height;
151// qDebug() << " " << t.iosurfacePlane << t.pixelFormat << t.width << t.height;
152 }
153
154 // Get a Metal texture using the CoreVideo Metal texture reference.
155// qDebug() << " -> " << quint64(CVMetalTextureGetTexture(cvMetalTexture[plane]));
156 return cvMetalTexture[plane] ? quint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0;
157 } else if (m_rhi->backend() == QRhi::OpenGLES2) {
158#if QT_CONFIG(opengl)
159#ifdef Q_OS_MACOS
160 CVOpenGLTextureCacheFlush(sink->cvOpenGLTextureCache, 0);
161 // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
162 const CVReturn cvret = CVOpenGLTextureCacheCreateTextureFromImage(
163 kCFAllocatorDefault,
164 sink->cvOpenGLTextureCache,
165 m_buffer,
166 nil,
167 &cvOpenGLTexture);
168 if (cvret != kCVReturnSuccess)
169 qWarning() << "OpenGL texture creation failed" << cvret;
170
171 Q_ASSERT(CVOpenGLTextureGetTarget(cvOpenGLTexture) == GL_TEXTURE_RECTANGLE);
172 // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
173 return CVOpenGLTextureGetName(cvOpenGLTexture);
174#endif
175#ifdef Q_OS_IOS
176 CVOpenGLESTextureCacheFlush(sink->cvOpenGLESTextureCache, 0);
177 // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
178 const CVReturn cvret = CVOpenGLESTextureCacheCreateTextureFromImage(
179 kCFAllocatorDefault,
180 sink->cvOpenGLESTextureCache,
181 m_buffer,
182 nil,
183 GL_TEXTURE_2D,
184 GL_RGBA,
185 CVPixelBufferGetWidth(m_buffer),
186 CVPixelBufferGetHeight(m_buffer),
187 GL_RGBA,
189 0,
190 &cvOpenGLESTexture);
191 if (cvret != kCVReturnSuccess)
192 qWarning() << "OpenGL ES texture creation failed" << cvret;
193
194 // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
195 return CVOpenGLESTextureGetName(cvOpenGLESTexture);
196#endif
197#endif
198 }
199 return 0;
200}
static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
void unmap()
Releases the memory mapped by the map() function.
MapData map(QVideoFrame::MapMode mode)
Independently maps the planes of a video buffer to memory.
AVFVideoBuffer(AVFVideoSinkInterface *sink, CVImageBufferRef buffer)
virtual quint64 textureHandle(int plane) const
Returns a texture handle to the data buffer.
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
\inmodule QtCore
Definition qmutex.h:317
Format
Specifies the texture format.
Definition qrhi.h:902
@ RGBA32F
Definition qrhi.h:914
@ RGBA16F
Definition qrhi.h:913
@ UnknownFormat
Definition qrhi.h:903
Implementation backend() const
Definition qrhi.cpp:8289
@ Metal
Definition qrhi.h:1774
@ OpenGLES2
Definition qrhi.h:1772
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of frames in a video stream.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:26
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Definition qvideoframe.h:36
QVideoFrameFormat videoFormatForImageBuffer(CVImageBufferRef buffer, bool openGL=false)
const TextureDescription * textureDescription(QVideoFrameFormat::PixelFormat format)
#define qWarning
Definition qlogging.h:162
return ret
GLenum mode
GLint GLsizei GLsizei height
GLfloat GLfloat f
GLenum GLuint buffer
GLint GLsizei width
#define GL_TEXTURE_RECTANGLE
GLsizei GLenum GLboolean sink
static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame, unsigned char *baseAddress)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define GL_UNSIGNED_BYTE
#define GL_RGBA
unsigned char uchar
Definition qtypes.h:27
unsigned long long quint64
Definition qtypes.h:56
QRhiTexture::Format textureFormat[maxPlanes]
int widthForPlane(int width, int plane) const
int heightForPlane(int height, int plane) const