Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
5#include <rhi/qrhi.h>
6#include <CoreVideo/CVMetalTexture.h>
7#include <CoreVideo/CVMetalTextureCache.h>
8#include <QtGui/qopenglcontext.h>
9#include <QtCore/qloggingcategory.h>
10
11#include <private/qvideotexturehelper_p.h>
12#include <QtMultimedia/private/qavfhelpers_p.h>
13
14#import <AVFoundation/AVFoundation.h>
15#import <Metal/Metal.h>
16
17QT_USE_NAMESPACE
18
19Q_STATIC_LOGGING_CATEGORY(qLcVideoBuffer, "qt.multimedia.darwin.videobuffer")
20
21AVFVideoBuffer::AVFVideoBuffer(AVFVideoSinkInterface *sink, QCFType<CVImageBufferRef> buffer)
22 : QHwVideoBuffer(sink->rhi() ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle,
23 sink->rhi()),
24 sink(sink),
25 m_buffer(std::move(buffer))
26{
27// m_type = QVideoFrame::NoHandle;
28// qDebug() << "RHI" << m_rhi;
29 const bool rhiIsOpenGL = sink && sink->rhi() && sink->rhi()->backend() == QRhi::OpenGLES2;
30 m_format = QAVFHelpers::videoFormatForImageBuffer(m_buffer, rhiIsOpenGL);
31
32 if (m_rhi && m_rhi->backend() == QRhi::Metal)
33 metalCache = CVMetalTextureCacheRef(CFRetain(sink->cvMetalTextureCache));
34}
35
37{
38 Q_ASSERT(m_mode == QVideoFrame::NotMapped);
39}
40
41AVFVideoBuffer::MapData AVFVideoBuffer::map(QVideoFrame::MapMode mode)
42{
43 MapData mapData;
44
45 if (m_mode == QVideoFrame::NotMapped) {
46 CVPixelBufferLockBaseAddress(m_buffer, mode == QVideoFrame::ReadOnly
47 ? kCVPixelBufferLock_ReadOnly
48 : 0);
49 m_mode = mode;
50 }
51
52 mapData.planeCount = CVPixelBufferGetPlaneCount(m_buffer);
53 Q_ASSERT(mapData.planeCount <= 3);
54
55 if (!mapData.planeCount) {
56 // single plane
57 mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRow(m_buffer);
58 mapData.data[0] = static_cast<uchar*>(CVPixelBufferGetBaseAddress(m_buffer));
59 mapData.dataSize[0] = CVPixelBufferGetDataSize(m_buffer);
60 mapData.planeCount = mapData.data[0] ? 1 : 0;
61 return mapData;
62 }
63
64 // For a bi-planar or tri-planar format we have to set the parameters correctly:
65 for (int i = 0; i < mapData.planeCount; ++i) {
66 mapData.bytesPerLine[i] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, i);
67 mapData.dataSize[i] = mapData.bytesPerLine[i]*CVPixelBufferGetHeightOfPlane(m_buffer, i);
68 mapData.data[i] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, i));
69 }
70
71 return mapData;
72}
73
75{
76 if (m_mode != QVideoFrame::NotMapped) {
77 CVPixelBufferUnlockBaseAddress(m_buffer, m_mode == QVideoFrame::ReadOnly
78 ? kCVPixelBufferLock_ReadOnly
79 : 0);
80 m_mode = QVideoFrame::NotMapped;
81 }
82}
83
84static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
85{
86 switch (f) {
87 default:
88 case QRhiTexture::UnknownFormat:
89 return MTLPixelFormatInvalid;
90 case QRhiTexture::RGBA8:
91 return MTLPixelFormatRGBA8Unorm;
92 case QRhiTexture::BGRA8:
93 return MTLPixelFormatBGRA8Unorm;
94 case QRhiTexture::R8:
95 case QRhiTexture::RED_OR_ALPHA8:
96 return MTLPixelFormatR8Unorm;
97 case QRhiTexture::RG8:
98 return MTLPixelFormatRG8Unorm;
99 case QRhiTexture::R16:
100 return MTLPixelFormatR16Unorm;
101 case QRhiTexture::RG16:
102 return MTLPixelFormatRG16Unorm;
103 case QRhiTexture::RGBA16F:
104 return MTLPixelFormatRGBA16Float;
105 case QRhiTexture::RGBA32F:
106 return MTLPixelFormatRGBA32Float;
107 case QRhiTexture::R16F:
108 return MTLPixelFormatR16Float;
109 case QRhiTexture::R32F:
110 return MTLPixelFormatR32Float;
111 }
112}
113
114
115quint64 AVFVideoBuffer::textureHandle(QRhi &, int plane)
116{
117 auto *textureDescription = QVideoTextureHelper::textureDescription(m_format.pixelFormat());
118 int bufferPlanes = CVPixelBufferGetPlaneCount(m_buffer);
119// qDebug() << "texture handle" << plane << m_rhi << (m_rhi->backend() == QRhi::Metal) << bufferPlanes;
120 if (plane > 0 && plane >= bufferPlanes)
121 return 0;
122 if (!m_rhi)
123 return 0;
124 if (m_rhi->backend() == QRhi::Metal) {
125 if (!cvMetalTexture[plane]) {
126 size_t width = CVPixelBufferGetWidth(m_buffer);
127 size_t height = CVPixelBufferGetHeight(m_buffer);
128 QSize planeSize = textureDescription->rhiPlaneSize(QSize(width, height), plane, m_rhi);
129
130 if (!metalCache) {
131 qWarning("cannot create texture, Metal texture cache was released?");
132 return {};
133 }
134
135 // Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
136 const auto pixelFormat = rhiTextureFormatToMetalFormat(textureDescription->rhiTextureFormat(plane, m_rhi));
137 if (pixelFormat != MTLPixelFormatInvalid) {
138 // Passing invalid pixel format makes Metal API validation
139 // to crash (and also makes no sense at all).
140 auto ret = CVMetalTextureCacheCreateTextureFromImage(
141 kCFAllocatorDefault,
142 metalCache,
143 m_buffer, nil,
144 pixelFormat,
145 planeSize.width(), planeSize.height(),
146 plane,
147 &cvMetalTexture[plane]);
148
149 if (ret != kCVReturnSuccess)
150 qCWarning(qLcVideoBuffer) << "texture creation failed" << ret;
151 } else {
152 qCWarning(qLcVideoBuffer) << "requested invalid pixel format:"
153 << textureDescription->rhiTextureFormat(plane, m_rhi);
154 }
155 }
156
157 return cvMetalTexture[plane] ? quint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0;
158 } else if (m_rhi->backend() == QRhi::OpenGLES2) {
159#if QT_CONFIG(opengl)
160#ifdef Q_OS_MACOS
161 CVOpenGLTextureCacheFlush(sink->cvOpenGLTextureCache, 0);
162 // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
163 const CVReturn cvret = CVOpenGLTextureCacheCreateTextureFromImage(
164 kCFAllocatorDefault,
165 sink->cvOpenGLTextureCache,
166 m_buffer,
167 nil,
168 &cvOpenGLTexture);
169 if (cvret != kCVReturnSuccess)
170 qWarning() << "OpenGL texture creation failed" << cvret;
171
172 Q_ASSERT(CVOpenGLTextureGetTarget(cvOpenGLTexture) == GL_TEXTURE_RECTANGLE);
173 // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
174 return CVOpenGLTextureGetName(cvOpenGLTexture);
175#endif
176#ifdef Q_OS_IOS
177 CVOpenGLESTextureCacheFlush(sink->cvOpenGLESTextureCache, 0);
178 // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache.
179 const CVReturn cvret = CVOpenGLESTextureCacheCreateTextureFromImage(
180 kCFAllocatorDefault,
181 sink->cvOpenGLESTextureCache,
182 m_buffer,
183 nil,
184 GL_TEXTURE_2D,
185 GL_RGBA,
186 CVPixelBufferGetWidth(m_buffer),
187 CVPixelBufferGetHeight(m_buffer),
188 GL_RGBA,
189 GL_UNSIGNED_BYTE,
190 0,
191 &cvOpenGLESTexture);
192 if (cvret != kCVReturnSuccess)
193 qWarning() << "OpenGL ES texture creation failed" << cvret;
194
195 // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image.
196 return CVOpenGLESTextureGetName(cvOpenGLESTexture);
197#endif
198#endif
199 }
200 return 0;
201}
static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
MapData map(QVideoFrame::MapMode mode) override
Maps the planes of a video buffer to memory.
~AVFVideoBuffer() override
void unmap() override
Releases the memory mapped by the map() function.