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