64 std::weak_ptr<QRhi> producerRhi, QPointer<QObject> producer,
65 QPointer<QOpenGLContext> producerContext)
78 if (m_mapMode != QVideoFrame::NotMapped || mode != QVideoFrame::ReadOnly)
80 m_mapMode = QVideoFrame::ReadOnly;
82 m_image = readbackOnProducerThread();
83 if (m_image.isNull()) {
84 m_mapMode = QVideoFrame::NotMapped;
88 data.bytesPerLine[0] = m_image.bytesPerLine();
89 data.dataSize[0] =
static_cast<
int>(m_image.sizeInBytes());
90 data.data[0] = m_image.bits();
97 m_mapMode = QVideoFrame::NotMapped;
107 if (!isCompatibleRhi(rhi))
110 m_tex->nativeTexture().object);
114 bool isCompatibleRhi(QRhi &rhi)
const
116 if (rhi.backend() != QRhi::OpenGLES2)
118 if (!m_producerContext)
120 const auto *handles =
121 static_cast<
const QRhiGles2NativeHandles *>(rhi.nativeHandles());
122 if (!handles || !handles->context)
124 return handles->context->shareGroup() == m_producerContext->shareGroup();
127 QImage readbackOnProducerThread()
const
129 auto producerRhi = m_producerRhi.lock();
130 if (!producerRhi || !m_producer)
133 QRhi *rhi = producerRhi.get();
134 QRhiTexture *tex = m_tex.get();
135 QMetaObject::invokeMethod(
138 QRhiReadbackResult result;
140 result.completed = [&done] { done =
true; };
141 QRhiCommandBuffer *cb =
nullptr;
142 if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
144 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
145 rub->readBackTexture({ tex }, &result);
146 cb->resourceUpdate(rub);
147 rhi->endOffscreenFrame();
148 if (!done || result.data.isEmpty())
150 QImage img(
reinterpret_cast<
const uchar *>(result.data.constData()),
151 result.pixelSize.width(), result.pixelSize.height(),
152 result.pixelSize.width() * 4, QImage::Format_RGBA8888);
155 Qt::BlockingQueuedConnection);
160 std::unique_ptr<QRhiTexture> m_tex;
163 std::weak_ptr<QRhi> m_producerRhi;
164 QPointer<QObject> m_producer;
165 QPointer<QOpenGLContext> m_producerContext;
173 m_vertexBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
175 m_vertexBuffer->create();
177 m_uniformBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic,
178 QRhiBuffer::UniformBuffer, 160));
179 m_uniformBuffer->create();
181 m_sampler.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest,
182 QRhiSampler::None, QRhiSampler::ClampToEdge,
183 QRhiSampler::ClampToEdge));
186 m_srb.reset(m_rhi->newShaderResourceBindings());
188 QRhiShaderResourceBinding::uniformBuffer(
190 QRhiShaderResourceBinding::VertexStage
191 | QRhiShaderResourceBinding::FragmentStage,
192 m_uniformBuffer.get()),
193 QRhiShaderResourceBinding::sampledTexture(
194 1, QRhiShaderResourceBinding::FragmentStage, externalTex, m_sampler.get())
198 m_vertexShader = loadShader(
199 QStringLiteral(
":/qt-project.org/multimedia/shaders/externalsampler.vert.qsb"));
200 m_fragmentShader = loadShader(
201 QStringLiteral(
":/qt-project.org/multimedia/shaders/externalsampler.frag.qsb"));
207 static QShader loadShader(
const QString &name)
210 if (f.open(QIODevice::ReadOnly))
211 return QShader::fromSerialized(f.readAll());
215 QRhi *m_rhi{
nullptr };
216 std::unique_ptr<QRhiBuffer> m_vertexBuffer;
217 std::unique_ptr<QRhiBuffer> m_uniformBuffer;
218 std::unique_ptr<QRhiSampler> m_sampler;
219 std::unique_ptr<QRhiShaderResourceBindings> m_srb;
220 QShader m_vertexShader;
221 QShader m_fragmentShader;
225 QRhiShaderResourceBindings *srb,
226 QRhiRenderPassDescriptor *rpd,
227 QShader vs, QShader fs)
229 std::unique_ptr<QRhiGraphicsPipeline> gp(rhi->newGraphicsPipeline());
230 gp->setTopology(QRhiGraphicsPipeline::TriangleFan);
231 gp->setShaderStages({
232 { QRhiShaderStage::Vertex, vs },
233 { QRhiShaderStage::Fragment, fs }
236 layout.setBindings({ { 4 *
sizeof(
float) } });
237 layout.setAttributes({
238 { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
239 { 0, 1, QRhiVertexInputAttribute::Float2, 2 *
sizeof(
float) }
241 gp->setVertexInputLayout(layout);
242 gp->setShaderResourceBindings(srb);
243 gp->setRenderPassDescriptor(rpd);
251 std::unique_ptr<QRhiTexture> tex(
252 m_rhi->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget));
253 if (!tex->create()) {
254 qCWarning(qLcOhosMediaPlugin) <<
"Failed to create frame texture";
258 std::unique_ptr<QRhiTextureRenderTarget> renderTarget(
259 m_rhi->newTextureRenderTarget({ { tex.get() } }));
260 std::unique_ptr<QRhiRenderPassDescriptor> rpd(
261 renderTarget->newCompatibleRenderPassDescriptor());
262 renderTarget->setRenderPassDescriptor(rpd.get());
263 renderTarget->create();
265 QRhiResourceUpdateBatch *rub = m_rhi->nextResourceUpdateBatch();
266 rub->uploadStaticBuffer(m_vertexBuffer.get(),
g_quad);
268 const QMatrix4x4 identity;
269 char *p = m_uniformBuffer->beginFullDynamicBufferUpdateForCurrentFrame();
270 memcpy(p, identity.constData(), 64);
271 memcpy(p + 64, externalTexMatrix.constData(), 64);
272 const float opacity = 1.0f;
273 memcpy(p + 64 + 64, &opacity, 4);
274 m_uniformBuffer->endFullDynamicBufferUpdateForCurrentFrame();
276 auto pipeline = newGraphicsPipeline(m_rhi, m_srb.get(), rpd.get(), m_vertexShader,
279 const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuffer.get(), 0);
280 QRhiCommandBuffer *cb =
nullptr;
281 if (m_rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
284 cb->beginPass(renderTarget.get(), Qt::transparent, { 1.0f, 0 }, rub);
285 cb->setGraphicsPipeline(pipeline.get());
286 cb->setViewport({ 0, 0,
float(size.width()),
float(size.height()) });
287 cb->setShaderResources(m_srb.get());
288 cb->setVertexInput(0, 1, &vbufBinding);
291 m_rhi->endOffscreenFrame();
370 0.0f, -1.0f, 0.0f, 1.0f,
371 0.0f, 0.0f, 1.0f, 0.0f,
372 0.0f, 0.0f, 0.0f, 1.0f);
399 OHNativeWindow *ensureSurface(QRhi *rhi)
404 const bool reuse = m_surfaceImage && m_surfaceImage->isValid()
405 && (rhi ? m_rhi.get() == rhi : m_isHeadless);
407 return m_surfaceImage->nativeWindow();
413 QRhiGles2InitParams params;
414 const auto *nativeHandles =
415 rhi ?
static_cast<
const QRhiGles2NativeHandles *>(rhi->nativeHandles())
417 params.shareContext = nativeHandles ? nativeHandles->context :
nullptr;
418 params.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
419 m_isHeadless = (rhi ==
nullptr);
420 m_rhi.reset(QRhi::create(QRhi::OpenGLES2, ¶ms));
422 qCWarning(qLcOhosMediaPlugin) <<
"Failed to create offscreen GLES2 RHI";
426 m_externalTexture.reset(
427 m_rhi->newTexture(QRhiTexture::RGBA8, m_size.isEmpty() ? QSize{ 1, 1 } : m_size, 1,
428 QRhiTexture::ExternalOES));
429 if (!m_externalTexture->create()) {
430 qCWarning(qLcOhosMediaPlugin) <<
"External OES texture create failed";
431 m_externalTexture.reset();
436 const auto nativeTex = m_externalTexture->nativeTexture();
437 m_surfaceImage = std::make_unique<QOhosSurfaceImage>(uint32_t(nativeTex.object));
438 if (!m_surfaceImage->isValid()) {
439 m_surfaceImage.reset();
440 m_externalTexture.reset();
445 const quint64 index = m_surfaceImage->index();
446 connect(m_surfaceImage.get(), &QOhosSurfaceImage::frameAvailable,
this,
447 [
this, index]() { onFrameAvailable(index); }, Qt::QueuedConnection);
449 m_textureCopy = std::make_unique<TextureCopy>(m_rhi.get(), m_externalTexture.get());
450 return m_surfaceImage->nativeWindow();
453 std::shared_ptr<QRhi> m_rhi;
454 std::unique_ptr<QRhiTexture> m_externalTexture;
455 std::unique_ptr<QOhosSurfaceImage> m_surfaceImage;
456 std::unique_ptr<TextureCopy> m_textureCopy;
457 QSize m_size{ 1, 1 };
459 bool m_isHeadless{
false };
466 m_textureThread = std::make_shared<QOhosTextureThread>();
467 connect(m_textureThread.get(), &QOhosTextureThread::newFrame,
this,
468 &QOhosVideoOutput::onNewFrame, Qt::QueuedConnection);
469 m_textureThread->launch();
471 if (
auto *p = sink ? sink->platformVideoSink() :
nullptr) {
472 connect(p, &QPlatformVideoSink::rhiChanged,
this, &
QOhosVideoOutput::onRhiChanged);
475 if (m_contentSource == ContentSource::Camera) {
476 if (QScreen *screen = QGuiApplication::primaryScreen()) {
477 connect(screen, &QScreen::orientationChanged,
this,
480 updateDisplayRotation();
525 auto *rhi = m_sink ? m_sink->rhi() :
nullptr;
527 m_surfaceCreatedWithoutRhi =
true;
528 }
else if (m_surfaceCreatedWithoutRhi) {
529 QMetaObject::invokeMethod(m_textureThread.get(), &QOhosTextureThread::tearDown,
530 Qt::BlockingQueuedConnection);
531 m_surfaceCreatedWithoutRhi =
false;
533 return m_textureThread->nativeWindowBlocking(rhi);
542 auto *rhi = m_sink ? m_sink->rhi() :
nullptr;
544 m_surfaceCreatedWithoutRhi =
true;
545 else if (m_surfaceCreatedWithoutRhi) {
546 QMetaObject::invokeMethod(m_textureThread.get(), &QOhosTextureThread::tearDown,
547 Qt::BlockingQueuedConnection);
548 m_surfaceCreatedWithoutRhi =
false;
550 return m_textureThread->surfaceIdBlocking(rhi);
QOhosTextureVideoBuffer(std::unique_ptr< QRhiTexture > tex, const QSize &size, std::weak_ptr< QRhi > producerRhi, QPointer< QObject > producer, QPointer< QOpenGLContext > producerContext)
void unmap() override
Releases the memory mapped by the map() function.
QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr &) override
MapData map(QVideoFrame::MapMode mode) override
Maps the planes of a video buffer to memory.
QRhiTexture * texture(uint plane) const override
QOhosVideoFrameTextures(QRhi *rhi, QSize size, quint64 handle)
std::unique_ptr< QRhiTexture > copyExternalTexture(QSize size, const QMatrix4x4 &externalTexMatrix)
TextureCopy(QRhi *rhi, QRhiTexture *externalTex)