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