9#include <QtGui/private/qopenglextensions_p.h>
10#include <qopenglcontext.h>
11#include <qopenglfunctions.h>
13#include <private/qhwvideobuffer_p.h>
14#include <private/qvideoframeconverter_p.h>
15#include <private/qplatformvideosink_p.h>
16#include <private/qvideoframe_p.h>
17#include <qvideosink.h>
18#include <qvideoframeformat.h>
29 m_tex.reset(rhi->newTexture(QRhiTexture::RGBA8, size, 1));
30 m_tex->createFrom({quint64(handle), 0});
35 return plane == 0 ? m_tex.get() :
nullptr;
39 std::unique_ptr<QRhiTexture> m_tex;
56 m_mapMode = QVideoFrame::NotMapped;
66 std::unique_ptr<QRhiTexture> m_tex;
79 return m_atvb.mapTextures(rhi, oldTextures);
91 QAbstractVideoBuffer::MapData mapData;
93 if (m_mapMode == QVideoFrame::NotMapped && mode == QVideoFrame::ReadOnly) {
94 m_mapMode = QVideoFrame::ReadOnly;
95 m_image = qImageFromVideoFrame(QVideoFramePrivate::createFrame(
96 std::make_unique<ImageFromVideoFrameHelper>(*
this),
97 QVideoFrameFormat(m_size, QVideoFrameFormat::Format_RGBA8888)));
98 mapData.planeCount = 1;
99 mapData.bytesPerLine[0] = m_image.bytesPerLine();
100 mapData.dataSize[0] =
static_cast<
int>(m_image.sizeInBytes());
101 mapData.data[0] = m_image.bits();
108 -1.f, -1.f, 0.f, 0.f,
116 static QShader getShader(
const QString &name)
119 if (f.open(QIODevice::ReadOnly))
120 return QShader::fromSerialized(f.readAll());
128 m_vertexBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(g_quad)));
129 m_vertexBuffer->create();
131 m_uniformBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 64 + 4 + 4));
132 m_uniformBuffer->create();
134 m_sampler.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
135 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
138 m_srb.reset(m_rhi->newShaderResourceBindings());
140 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_uniformBuffer.get()),
141 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, externalTex, m_sampler.get())
145 m_vertexShader = getShader(QStringLiteral(
":/qt-project.org/multimedia/shaders/externalsampler.vert.qsb"));
146 Q_ASSERT(m_vertexShader.isValid());
147 m_fragmentShader = getShader(QStringLiteral(
":/qt-project.org/multimedia/shaders/externalsampler.frag.qsb"));
148 Q_ASSERT(m_fragmentShader.isValid());
156 QRhi *m_rhi =
nullptr;
157 std::unique_ptr<QRhiBuffer> m_vertexBuffer;
158 std::unique_ptr<QRhiBuffer> m_uniformBuffer;
159 std::unique_ptr<QRhiSampler> m_sampler;
160 std::unique_ptr<QRhiShaderResourceBindings> m_srb;
161 QShader m_vertexShader;
162 QShader m_fragmentShader;
166static std::unique_ptr<QRhiGraphicsPipeline> newGraphicsPipeline(QRhi *rhi,
167 QRhiShaderResourceBindings *shaderResourceBindings,
168 QRhiRenderPassDescriptor *renderPassDescriptor,
169 QShader vertexShader,
170 QShader fragmentShader)
172 std::unique_ptr<QRhiGraphicsPipeline> gp(rhi->newGraphicsPipeline());
173 gp->setTopology(QRhiGraphicsPipeline::TriangleFan);
174 gp->setShaderStages({
175 { QRhiShaderStage::Vertex, vertexShader },
176 { QRhiShaderStage::Fragment, fragmentShader }
178 QRhiVertexInputLayout inputLayout;
179 inputLayout.setBindings({
180 { 4 *
sizeof(
float) }
182 inputLayout.setAttributes({
183 { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
184 { 0, 1, QRhiVertexInputAttribute::Float2, 2 *
sizeof(
float) }
186 gp->setVertexInputLayout(inputLayout);
187 gp->setShaderResourceBindings(shaderResourceBindings);
188 gp->setRenderPassDescriptor(renderPassDescriptor);
194std::unique_ptr<QRhiTexture> TextureCopy::copyExternalGlTexture(QSize size,
const QMatrix4x4 &externalTexMatrix)
196 std::unique_ptr<QRhiTexture> tex(m_rhi->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget));
197 if (!tex->create()) {
198 qWarning(
"Failed to create frame texture");
202 std::unique_ptr<QRhiTextureRenderTarget> renderTarget(m_rhi->newTextureRenderTarget({ { tex.get() } }));
203 std::unique_ptr<QRhiRenderPassDescriptor> renderPassDescriptor(renderTarget->newCompatibleRenderPassDescriptor());
204 renderTarget->setRenderPassDescriptor(renderPassDescriptor.get());
205 renderTarget->create();
207 QRhiResourceUpdateBatch *rub = m_rhi->nextResourceUpdateBatch();
208 rub->uploadStaticBuffer(m_vertexBuffer.get(), g_quad);
211 char *p = m_uniformBuffer->beginFullDynamicBufferUpdateForCurrentFrame();
212 memcpy(p, identity.constData(), 64);
213 memcpy(p + 64, externalTexMatrix.constData(), 64);
214 float opacity = 1.0f;
215 memcpy(p + 64 + 64, &opacity, 4);
216 m_uniformBuffer->endFullDynamicBufferUpdateForCurrentFrame();
218 auto graphicsPipeline = newGraphicsPipeline(m_rhi, m_srb.get(), renderPassDescriptor.get(),
219 m_vertexShader, m_fragmentShader);
221 const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuffer.get(), 0);
223 QRhiCommandBuffer *cb =
nullptr;
224 if (m_rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
227 cb->beginPass(renderTarget.get(), Qt::transparent, { 1.0f, 0 }, rub);
228 cb->setGraphicsPipeline(graphicsPipeline.get());
229 cb->setViewport({0, 0,
float(size.width()),
float(size.height())});
230 cb->setShaderResources(m_srb.get());
231 cb->setVertexInput(0, 1, &vbufBinding);
234 m_rhi->endOffscreenFrame();
236 QOpenGLContext *ctx = QOpenGLContext::currentContext();
237 QOpenGLFunctions *f = ctx->functions();
238 static_cast<QOpenGLExtensions *>(f)->flushShared();
243static QMatrix4x4 extTransformMatrix(AndroidSurfaceTexture *surfaceTexture)
245 QMatrix4x4 m = surfaceTexture->getTransformMatrix();
249 static const QMatrix4x4 flipV(1.0f, 0.0f, 0.0f, 0.0f,
250 0.0f, -1.0f, 0.0f, 1.0f,
251 0.0f, 0.0f, 1.0f, 0.0f,
252 0.0f, 0.0f, 0.0f, 1.0f);
350 <<
"Attempting to use Qt Multimedia VideoOutput on a Qt build without OpenGL support. "
351 <<
"This is not supported by the native Android media backend.";
362 std::shared_ptr<QRhi> m_rhi;
363 std::unique_ptr<AndroidSurfaceTexture> m_surfaceTexture;
364 std::unique_ptr<QRhiTexture> m_texture;
365 std::unique_ptr<TextureCopy> m_textureCopy;
374 qDebug() <<
"Cannot create QAndroidTextureVideoOutput without a sink.";
375 m_surfaceThread =
nullptr;
379 startNewSurfaceThread();
384 m_surfaceThread = std::make_shared<AndroidTextureThread>(
this);
385 connect(m_surfaceThread.get(), &AndroidTextureThread::newFrame,
386 this, &QAndroidTextureVideoOutput::newFrame);
387 m_surfaceThread->start();
393 QMetaObject::invokeMethod(m_surfaceThread.get(),
394 &AndroidTextureThread::clearSurfaceTexture, Qt::BlockingQueuedConnection);
400 auto *sink = m_sink->platformVideoSink();
402 sink->setSubtitleText(subtitle);
408 return m_sink->rhi() && m_surfaceCreatedWithoutRhi;
417 QMetaObject::invokeMethod(m_surfaceThread.get(), [&]() {
418 auto rhi = m_sink->rhi();
420 m_surfaceCreatedWithoutRhi =
true;
422 else if (m_surfaceCreatedWithoutRhi) {
423 m_surfaceThread->clearSurfaceTexture();
424 m_surfaceCreatedWithoutRhi =
false;
426 surface = m_surfaceThread->createSurfaceTexture(rhi);
428 Qt::BlockingQueuedConnection);
434 if (m_nativeSize == size)
438 QMetaObject::invokeMethod(m_surfaceThread.get(),
439 [&](){ m_surfaceThread->setFrameSize(size); },
440 Qt::BlockingQueuedConnection);
446 QMetaObject::invokeMethod(m_surfaceThread.get(), [&](){ m_surfaceThread->clearFrame(); });
452 m_sink->platformVideoSink()->setVideoFrame({});
453 QMetaObject::invokeMethod(m_surfaceThread.get(), &AndroidTextureThread::clearSurfaceTexture);
459 m_sink->setVideoFrame(frame);
464#include "qandroidvideooutput.moc"
465#include "moc_qandroidvideooutput_p.cpp"
MapData map(QVideoFrame::MapMode mode) override
Maps the planes of a video buffer to memory.
void unmap() override
Releases the memory mapped by the map() function.
AndroidTextureVideoBuffer(std::unique_ptr< QRhiTexture > tex, const QSize &size)
QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr &) override
void unmap() override
Releases the memory mapped by the map() function.
QVideoFrameTexturesUPtr mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr &oldTextures) override
ImageFromVideoFrameHelper(AndroidTextureVideoBuffer &atvb)
MapData map(QVideoFrame::MapMode) override
Maps the planes of a video buffer to memory.
void setSubtitle(const QString &subtitle)
void setVideoSize(const QSize &) override
AndroidSurfaceTexture * surfaceTexture() override
bool shouldTextureBeUpdated() const
~QAndroidTextureVideoOutput() override
QAndroidVideoFrameTextures(QRhi *rhi, QSize size, quint64 handle)
QRhiTexture * texture(uint plane) const override
TextureCopy(QRhi *rhi, QRhiTexture *externalTex)
Combined button and popup list for selecting options.
static const float g_quad[]