100 std::unique_ptr<QRhiBuffer> &uniformBuffer,
101 std::unique_ptr<QRhiSampler> &textureSampler,
102 std::unique_ptr<QRhiShaderResourceBindings> &shaderResourceBindings,
103 std::unique_ptr<QRhiGraphicsPipeline> &graphicsPipeline,
104 std::unique_ptr<QRhiRenderPassDescriptor> &renderPass,
106 const QVideoFrameTexturesUPtr &videoFrameTextures)
108 auto format = frame.surfaceFormat();
109 auto pixelFormat = format.pixelFormat();
111 auto textureDesc = QVideoTextureHelper::textureDescription(pixelFormat);
115 *b++ = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
116 uniformBuffer.get());
117 for (
int i = 0; i < textureDesc->nplanes; ++i)
118 *b++ = QRhiShaderResourceBinding::sampledTexture(i + 1, QRhiShaderResourceBinding::FragmentStage,
119 videoFrameTextures->texture(i), textureSampler.get());
120 shaderResourceBindings->setBindings(bindings, b);
121 shaderResourceBindings->create();
123 graphicsPipeline.reset(rhi->newGraphicsPipeline());
124 graphicsPipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
126 QShader vs = ensureShader(QVideoTextureHelper::vertexShaderFileName(format));
130 QShader fs = ensureShader(QVideoTextureHelper::fragmentShaderFileName(format, rhi));
134 graphicsPipeline->setShaderStages({
135 { QRhiShaderStage::Vertex, vs },
136 { QRhiShaderStage::Fragment, fs }
140 inputLayout.setBindings({
141 { 4 *
sizeof(
float) }
143 inputLayout.setAttributes({
144 { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
145 { 0, 1, QRhiVertexInputAttribute::Float2, 2 *
sizeof(
float) }
148 graphicsPipeline->setVertexInputLayout(inputLayout);
149 graphicsPipeline->setShaderResourceBindings(shaderResourceBindings.get());
150 graphicsPipeline->setRenderPassDescriptor(renderPass.get());
151 graphicsPipeline->create();
202 QMacAutoReleasePool releasePool;
205 std::unique_ptr<QRhiRenderPassDescriptor> renderPass;
206 std::unique_ptr<QRhiBuffer> vertexBuffer;
207 std::unique_ptr<QRhiBuffer> uniformBuffer;
208 std::unique_ptr<QRhiTexture> targetTexture;
209 std::unique_ptr<QRhiTextureRenderTarget> renderTarget;
210 std::unique_ptr<QRhiSampler> textureSampler;
211 std::unique_ptr<QRhiShaderResourceBindings> shaderResourceBindings;
212 std::unique_ptr<QRhiGraphicsPipeline> graphicsPipeline;
214 if (frame.size().isEmpty() || frame.pixelFormat() == QVideoFrameFormat::Format_Invalid)
217 if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg)
218 return convertJPEG(frame, transformation);
221 return convertCPU(frame, transformation);
225 if (QHwVideoBuffer *buffer = QVideoFramePrivate::hwBuffer(frame))
228 if (!rhi || !rhi->thread()->isCurrentThread())
229 rhi = ensureThreadLocalRhi(rhi);
231 if (!rhi || rhi->isRecordingFrame())
232 return convertCPU(frame, transformation);
236 const QSize frameSize = qRotatedFrameSize(frame.size(), frame.surfaceFormat().rotation());
238 vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(g_quad)));
239 vertexBuffer->create();
241 uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer,
sizeof(QVideoTextureHelper::UniformData)));
242 uniformBuffer->create();
244 textureSampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
245 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
246 textureSampler->create();
248 shaderResourceBindings.reset(rhi->newShaderResourceBindings());
250 targetTexture.reset(rhi->newTexture(QRhiTexture::RGBA8, frameSize, 1, QRhiTexture::RenderTarget));
251 if (!targetTexture->create()) {
252 qCDebug(qLcVideoFrameConverter) <<
"Failed to create target texture. Using CPU conversion.";
253 return convertCPU(frame, transformation);
256 renderTarget.reset(rhi->newTextureRenderTarget({ { targetTexture.get() } }));
257 renderPass.reset(renderTarget->newCompatibleRenderPassDescriptor());
258 renderTarget->setRenderPassDescriptor(renderPass.get());
259 renderTarget->create();
261 QRhiCommandBuffer *cb =
nullptr;
262 QRhi::FrameOpResult r = rhi->beginOffscreenFrame(&cb);
263 if (r != QRhi::FrameOpSuccess) {
264 qCDebug(qLcVideoFrameConverter) <<
"Failed to set up offscreen frame. Using CPU conversion.";
265 return convertCPU(frame, transformation);
268 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
271 rub->uploadStaticBuffer(vertexBuffer.get(), g_quad);
273 QVideoFrame frameTmp = frame;
274 auto videoFrameTextures = QVideoTextureHelper::createTextures(frameTmp, *rhi, *rub, {});
275 if (!videoFrameTextures) {
276 qCDebug(qLcVideoFrameConverter) <<
"Failed obtain textures. Using CPU conversion.";
277 return convertCPU(frame, transformation);
280 if (!updateTextures(rhi, uniformBuffer, textureSampler, shaderResourceBindings,
281 graphicsPipeline, renderPass, frameTmp, videoFrameTextures)) {
282 qCDebug(qLcVideoFrameConverter) <<
"Failed to update textures. Using CPU conversion.";
283 return convertCPU(frame, transformation);
286 float xScale = transformation.mirroredHorizontallyAfterRotation ? -1.0 : 1.0;
289 if (rhi->isYUpInFramebuffer())
292 QMatrix4x4 transform;
293 transform.scale(xScale, yScale);
295 QByteArray uniformData(
sizeof(QVideoTextureHelper::UniformData), Qt::Uninitialized);
296 QVideoTextureHelper::updateUniformData(&uniformData, rhi, frame.surfaceFormat(), frame,
298 rub->updateDynamicBuffer(uniformBuffer.get(), 0, uniformData.size(), uniformData.constData());
300 cb->beginPass(renderTarget.get(), Qt::black, { 1.0f, 0 }, rub);
301 cb->setGraphicsPipeline(graphicsPipeline.get());
303 cb->setViewport({ 0, 0,
float(frameSize.width()),
float(frameSize.height()) });
304 cb->setShaderResources(shaderResourceBindings.get());
306 const quint32 vertexOffset = quint32(
sizeof(
float)) * 16 * transformation.rotationIndex();
307 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer.get(), vertexOffset);
308 cb->setVertexInput(0, 1, &vbufBinding);
312 QRhiReadbackResult readResult;
313 bool readCompleted =
false;
315 readResult.completed = [&readCompleted] { readCompleted =
true; };
317 rub = rhi->nextResourceUpdateBatch();
318 rub->readBackTexture(readDesc, &readResult);
322 rhi->endOffscreenFrame();
324 if (!readCompleted) {
325 qCDebug(qLcVideoFrameConverter) <<
"Failed to read back texture. Using CPU conversion.";
326 return convertCPU(frame, transformation);
329 QByteArray *imageData =
new QByteArray(readResult.data);
331 return QImage(
reinterpret_cast<
const uchar *>(imageData->constData()),
332 readResult.pixelSize.width(), readResult.pixelSize.height(),
333 QImage::Format_RGBA8888_Premultiplied, imageCleanupHandler, imageData);