plugins/screencast: Refactor buffer management

Use GraphicsBuffers both for dmabuf and memfd code paths.
This commit is contained in:
Vlad Zahorodnii 2024-03-18 18:50:02 +02:00
parent 608cc0e7f8
commit c3c3b903b1
15 changed files with 242 additions and 278 deletions

View file

@ -4,7 +4,7 @@ target_sources(screencast PRIVATE
outputscreencastsource.cpp
pipewirecore.cpp
regionscreencastsource.cpp
screencastdmabuftexture.cpp
screencastbuffer.cpp
screencastmanager.cpp
screencastsource.cpp
screencaststream.cpp

View file

@ -51,11 +51,11 @@ QSize OutputScreenCastSource::textureSize() const
return m_output->pixelSize();
}
void OutputScreenCastSource::render(spa_data *spa, spa_video_format format)
void OutputScreenCastSource::render(QImage *target)
{
const auto [outputTexture, colorDescription] = Compositor::self()->scene()->textureForOutput(m_output);
if (outputTexture) {
grabTexture(outputTexture.get(), spa, format);
grabTexture(outputTexture.get(), target);
}
}

View file

@ -29,7 +29,7 @@ public:
quint32 drmFormat() const override;
void render(GLFramebuffer *target) override;
void render(spa_data *spa, spa_video_format format) override;
void render(QImage *target) override;
std::chrono::nanoseconds clock() const override;
void resume() override;

View file

@ -152,10 +152,10 @@ void RegionScreenCastSource::render(GLFramebuffer *target)
GLFramebuffer::popFramebuffer();
}
void RegionScreenCastSource::render(spa_data *spa, spa_video_format format)
void RegionScreenCastSource::render(QImage *target)
{
ensureTexture();
grabTexture(m_renderedTexture.get(), spa, format);
grabTexture(m_renderedTexture.get(), target);
}
uint RegionScreenCastSource::refreshRate() const

View file

@ -10,7 +10,6 @@
#include "opengl/gltexture.h"
#include "opengl/glutils.h"
#include <QImage>
namespace KWin
{
@ -44,7 +43,7 @@ public:
uint refreshRate() const override;
void render(GLFramebuffer *target) override;
void render(spa_data *spa, spa_video_format format) override;
void render(QImage *target) override;
std::chrono::nanoseconds clock() const override;
void update(Output *output, const QRegion &damage);

View file

@ -0,0 +1,121 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "screencastbuffer.h"
#include "compositor.h"
#include "core/drmdevice.h"
#include "core/shmgraphicsbufferallocator.h"
#include "opengl/glframebuffer.h"
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
namespace KWin
{
ScreenCastBuffer::ScreenCastBuffer(GraphicsBuffer *buffer)
: m_buffer(buffer)
{
}
ScreenCastBuffer::~ScreenCastBuffer()
{
m_buffer->drop();
}
DmaBufScreenCastBuffer::DmaBufScreenCastBuffer(GraphicsBuffer *buffer, std::shared_ptr<GLTexture> &&texture, std::unique_ptr<GLFramebuffer> &&framebuffer)
: ScreenCastBuffer(buffer)
, texture(std::move(texture))
, framebuffer(std::move(framebuffer))
{
}
DmaBufScreenCastBuffer *DmaBufScreenCastBuffer::create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options)
{
AbstractEglBackend *backend = dynamic_cast<AbstractEglBackend *>(Compositor::self()->backend());
if (!backend || !backend->drmDevice()) {
return nullptr;
}
GraphicsBuffer *buffer = backend->drmDevice()->allocator()->allocate(options);
if (!buffer) {
return nullptr;
}
const DmaBufAttributes *attrs = buffer->dmabufAttributes();
if (!attrs) {
buffer->drop();
return nullptr;
}
backend->makeCurrent();
auto texture = backend->importDmaBufAsTexture(*attrs);
if (!texture) {
buffer->drop();
return nullptr;
}
auto framebuffer = std::make_unique<GLFramebuffer>(texture.get());
if (!framebuffer->valid()) {
buffer->drop();
return nullptr;
}
struct spa_data *spaData = pwBuffer->buffer->datas;
Q_ASSERT(pwBuffer->buffer->n_datas >= uint(attrs->planeCount));
for (int i = 0; i < attrs->planeCount; ++i) {
spaData[i].type = SPA_DATA_DmaBuf;
spaData[i].flags = SPA_DATA_FLAG_READWRITE;
spaData[i].mapoffset = 0;
spaData[i].maxsize = i == 0 ? attrs->pitch[i] * attrs->height : 0; // TODO: dmabufs don't have a well defined size, it should be zero but some clients check the size to see if the buffer is valid
spaData[i].fd = attrs->fd[i].get();
spaData[i].data = nullptr;
spaData[i].chunk->offset = attrs->offset[i];
spaData[i].chunk->size = spaData[i].maxsize;
spaData[i].chunk->stride = attrs->pitch[i];
spaData[i].chunk->flags = SPA_CHUNK_FLAG_NONE;
};
return new DmaBufScreenCastBuffer(buffer, std::move(texture), std::move(framebuffer));
}
MemFdScreenCastBuffer::MemFdScreenCastBuffer(GraphicsBuffer *buffer, GraphicsBufferView &&view)
: ScreenCastBuffer(buffer)
, view(std::move(view))
{
}
MemFdScreenCastBuffer *MemFdScreenCastBuffer::create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options)
{
GraphicsBuffer *buffer = ShmGraphicsBufferAllocator().allocate(options);
if (!buffer) {
return nullptr;
}
GraphicsBufferView view(buffer, GraphicsBuffer::Read | GraphicsBuffer::Write);
if (view.isNull()) {
buffer->drop();
return nullptr;
}
const ShmAttributes *attributes = buffer->shmAttributes();
struct spa_data *spaData = pwBuffer->buffer->datas;
spaData->type = SPA_DATA_MemFd;
spaData->flags = SPA_DATA_FLAG_READWRITE;
spaData->mapoffset = 0;
spaData->maxsize = attributes->stride * attributes->size.height();
spaData->fd = attributes->fd.get();
spaData->data = view.image()->bits();
spaData->chunk->offset = 0;
spaData->chunk->size = spaData->maxsize;
spaData->chunk->stride = attributes->stride;
spaData->chunk->flags = SPA_CHUNK_FLAG_NONE;
return new MemFdScreenCastBuffer(buffer, std::move(view));
}
} // namespace KWin

View file

@ -0,0 +1,54 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "core/graphicsbufferview.h"
#include <pipewire/pipewire.h>
namespace KWin
{
class GLFramebuffer;
class GLTexture;
class GraphicsBuffer;
struct GraphicsBufferOptions;
class ScreenCastBuffer
{
public:
explicit ScreenCastBuffer(GraphicsBuffer *buffer);
virtual ~ScreenCastBuffer();
private:
GraphicsBuffer *m_buffer;
};
class DmaBufScreenCastBuffer : public ScreenCastBuffer
{
public:
static DmaBufScreenCastBuffer *create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options);
std::shared_ptr<GLTexture> texture;
std::unique_ptr<GLFramebuffer> framebuffer;
private:
DmaBufScreenCastBuffer(GraphicsBuffer *buffer, std::shared_ptr<GLTexture> &&texture, std::unique_ptr<GLFramebuffer> &&framebuffer);
};
class MemFdScreenCastBuffer : public ScreenCastBuffer
{
public:
static MemFdScreenCastBuffer *create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options);
GraphicsBufferView view;
private:
MemFdScreenCastBuffer(GraphicsBuffer *buffer, GraphicsBufferView &&view);
};
} // namespace KWin

View file

@ -1,42 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "plugins/screencast/screencastdmabuftexture.h"
#include "core/graphicsbuffer.h"
#include "opengl/glutils.h"
namespace KWin
{
ScreenCastDmaBufTexture::ScreenCastDmaBufTexture(std::shared_ptr<GLTexture> texture, std::unique_ptr<GLFramebuffer> &&framebuffer, GraphicsBuffer *buffer)
: m_texture(std::move(texture))
, m_framebuffer(std::move(framebuffer))
, m_buffer(buffer)
{
}
ScreenCastDmaBufTexture::~ScreenCastDmaBufTexture()
{
m_framebuffer.reset();
m_texture.reset();
m_buffer->drop();
}
GraphicsBuffer *ScreenCastDmaBufTexture::buffer() const
{
return m_buffer;
}
KWin::GLTexture *ScreenCastDmaBufTexture::texture() const
{
return m_texture.get();
}
KWin::GLFramebuffer *ScreenCastDmaBufTexture::framebuffer() const
{
return m_framebuffer.get();
}
} // namespace KWin

View file

@ -1,33 +0,0 @@
/*
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <memory>
namespace KWin
{
class GLFramebuffer;
class GLTexture;
class GraphicsBuffer;
class ScreenCastDmaBufTexture
{
public:
explicit ScreenCastDmaBufTexture(std::shared_ptr<GLTexture> texture, std::unique_ptr<GLFramebuffer> &&framebuffer, GraphicsBuffer *buffer);
virtual ~ScreenCastDmaBufTexture();
GraphicsBuffer *buffer() const;
GLTexture *texture() const;
GLFramebuffer *framebuffer() const;
protected:
std::shared_ptr<GLTexture> m_texture;
std::unique_ptr<GLFramebuffer> m_framebuffer;
GraphicsBuffer *m_buffer;
};
}

View file

@ -7,8 +7,8 @@
#pragma once
#include <QObject>
#include <spa/buffer/buffer.h>
#include <spa/param/video/raw.h>
class QImage;
namespace KWin
{
@ -29,7 +29,7 @@ public:
virtual QSize textureSize() const = 0;
virtual void render(GLFramebuffer *target) = 0;
virtual void render(spa_data *spa, spa_video_format format) = 0;
virtual void render(QImage *target) = 0;
virtual std::chrono::nanoseconds clock() const = 0;
virtual void resume() = 0;

View file

@ -22,7 +22,7 @@
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
#include "platformsupport/scenes/opengl/openglbackend.h"
#include "scene/workspacescene.h"
#include "screencastdmabuftexture.h"
#include "screencastbuffer.h"
#include "screencastsource.h"
#include "utils/drm_format_helper.h"
@ -31,13 +31,8 @@
#include <QLoggingCategory>
#include <QPainter>
#include <spa/buffer/meta.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <libdrm/drm_fourcc.h>
#include <spa/buffer/meta.h>
namespace KWin
{
@ -219,96 +214,44 @@ void ScreenCastStream::onStreamParamChanged(uint32_t id, const struct spa_pod *f
m_streaming = true;
}
void ScreenCastStream::onStreamAddBuffer(pw_buffer *buffer)
void ScreenCastStream::onStreamAddBuffer(pw_buffer *pwBuffer)
{
if (m_closed) {
return;
}
std::shared_ptr<ScreenCastDmaBufTexture> dmabuff;
m_waitForNewBuffers = false;
struct spa_data *spa_data = buffer->buffer->datas;
struct spa_data *spa_data = pwBuffer->buffer->datas;
if (spa_data[0].type != SPA_ID_INVALID && spa_data[0].type & (1 << SPA_DATA_DmaBuf)) {
Q_ASSERT(m_dmabufParams);
dmabuff = createDmaBufTexture(*m_dmabufParams);
if (auto dmabuf = DmaBufScreenCastBuffer::create(pwBuffer, GraphicsBufferOptions{
.size = QSize(m_dmabufParams->width, m_dmabufParams->height),
.format = m_dmabufParams->format,
.modifiers = {m_dmabufParams->modifier},
})) {
pwBuffer->user_data = dmabuf;
return;
}
}
if (dmabuff) {
const DmaBufAttributes *dmabufAttribs = dmabuff->buffer()->dmabufAttributes();
Q_ASSERT(buffer->buffer->n_datas >= uint(dmabufAttribs->planeCount));
for (int i = 0; i < dmabufAttribs->planeCount; ++i) {
spa_data[i].type = SPA_DATA_DmaBuf;
spa_data[i].flags = SPA_DATA_FLAG_READWRITE;
spa_data[i].mapoffset = 0;
spa_data[i].maxsize = i == 0 ? dmabufAttribs->pitch[i] * dmabufAttribs->height : 0; // TODO: dmabufs don't have a well defined size, it should be zero but some clients check the size to see if the buffer is valid
spa_data[i].fd = dmabufAttribs->fd[i].get();
spa_data[i].data = nullptr;
spa_data[i].chunk->offset = dmabufAttribs->offset[i];
spa_data[i].chunk->size = spa_data[i].maxsize;
spa_data[i].chunk->stride = dmabufAttribs->pitch[i];
spa_data[i].chunk->flags = SPA_CHUNK_FLAG_NONE;
}
m_dmabufDataForPwBuffer.insert(buffer, dmabuff);
#ifdef F_SEAL_SEAL // Disable memfd on systems that don't have it, like BSD < 12
} else {
if (!(spa_data->type & (1 << SPA_DATA_MemFd))) {
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Client doesn't support memfd buffer data type";
if (spa_data->type & (1 << SPA_DATA_MemFd)) {
if (auto memfd = MemFdScreenCastBuffer::create(pwBuffer, GraphicsBufferOptions{
.size = m_resolution,
.format = m_drmFormat,
.software = true,
})) {
pwBuffer->user_data = memfd;
return;
}
const int bytesPerPixel = m_source->hasAlphaChannel() ? 4 : 3;
const int stride = SPA_ROUND_UP_N(m_resolution.width() * bytesPerPixel, 4);
spa_data->type = SPA_DATA_MemFd;
spa_data->flags = SPA_DATA_FLAG_READWRITE;
spa_data->mapoffset = 0;
spa_data->maxsize = stride * m_resolution.height();
spa_data->fd = memfd_create("kwin-screencast-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (spa_data->fd == -1) {
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Can't create memfd";
return;
}
if (ftruncate(spa_data->fd, spa_data->maxsize) < 0) {
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Can't truncate to" << spa_data->maxsize;
return;
}
unsigned int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
if (fcntl(spa_data->fd, F_ADD_SEALS, seals) == -1) {
qCWarning(KWIN_SCREENCAST) << objectName() << "memfd: Failed to add seals";
}
spa_data->data = mmap(nullptr,
spa_data->maxsize,
PROT_READ | PROT_WRITE,
MAP_SHARED,
spa_data->fd,
spa_data->mapoffset);
if (spa_data->data == MAP_FAILED) {
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Failed to mmap memory";
} else {
qCDebug(KWIN_SCREENCAST) << objectName() << "memfd: created successfully" << spa_data->data << spa_data->maxsize;
}
spa_data->chunk->offset = 0;
spa_data->chunk->size = spa_data->maxsize;
spa_data->chunk->stride = stride;
spa_data->chunk->flags = SPA_CHUNK_FLAG_NONE;
#endif
}
m_waitForNewBuffers = false;
}
void ScreenCastStream::onStreamRemoveBuffer(pw_buffer *buffer)
void ScreenCastStream::onStreamRemoveBuffer(pw_buffer *pwBuffer)
{
m_dmabufDataForPwBuffer.remove(buffer);
struct spa_buffer *spa_buffer = buffer->buffer;
struct spa_data *spa_data = spa_buffer->datas;
if (spa_data && spa_data->type == SPA_DATA_MemFd) {
::munmap(spa_data->data, spa_data->maxsize);
::close(spa_data->fd);
if (ScreenCastBuffer *buffer = static_cast<ScreenCastBuffer *>(pwBuffer->user_data)) {
delete buffer;
pwBuffer->user_data = nullptr;
}
}
@ -534,58 +477,38 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
return;
}
struct pw_buffer *buffer = pw_stream_dequeue_buffer(m_pwStream);
if (!buffer) {
struct pw_buffer *pwBuffer = pw_stream_dequeue_buffer(m_pwStream);
if (!pwBuffer) {
return;
}
struct spa_buffer *spa_buffer = buffer->buffer;
struct spa_buffer *spa_buffer = pwBuffer->buffer;
struct spa_data *spa_data = spa_buffer->datas;
ScreenCastBuffer *buffer = static_cast<ScreenCastBuffer *>(pwBuffer->user_data);
if (!buffer) {
qCWarning(KWIN_SCREENCAST) << objectName() << "Failed to record frame: invalid buffer type";
corruptHeader(spa_buffer);
pw_stream_queue_buffer(m_pwStream, pwBuffer);
return;
}
EglContext *context = static_cast<AbstractEglBackend *>(Compositor::self()->backend())->openglContext();
context->makeCurrent();
spa_data->chunk->flags = SPA_CHUNK_FLAG_NONE;
if (spa_data[0].type == SPA_DATA_MemFd) {
uint8_t *data = static_cast<uint8_t *>(spa_data->data);
if (!data) {
qCWarning(KWIN_SCREENCAST) << objectName() << "Failed to record frame: invalid buffer data";
corruptHeader(spa_buffer);
pw_stream_queue_buffer(m_pwStream, buffer);
return;
}
const bool hasAlpha = m_source->hasAlphaChannel();
const int bpp = data && !hasAlpha ? 3 : 4;
const uint stride = SPA_ROUND_UP_N(size.width() * bpp, 4);
if ((stride * size.height()) > spa_data->maxsize) {
qCDebug(KWIN_SCREENCAST) << objectName() << "Failed to record frame: frame is too big";
corruptHeader(spa_buffer);
pw_stream_queue_buffer(m_pwStream, buffer);
return;
}
m_source->render(spa_data, m_videoFormat.format);
if (auto memfd = dynamic_cast<MemFdScreenCastBuffer *>(buffer)) {
m_source->render(memfd->view.image());
auto cursor = Cursors::self()->currentCursor();
if (m_cursor.mode == ScreencastV1Interface::Embedded && includesCursor(cursor)) {
QImage dest(data, size.width(), size.height(), stride, hasAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
QPainter painter(&dest);
QPainter painter(memfd->view.image());
const auto position = (cursor->pos() - m_cursor.viewport.topLeft() - cursor->hotspot()) * m_cursor.scale;
const PlatformCursorImage cursorImage = kwinApp()->cursorImage();
painter.drawImage(QRect{position.toPoint(), cursorImage.image().size()}, cursorImage.image());
}
} else if (spa_data[0].type == SPA_DATA_DmaBuf) {
auto dmabuf = m_dmabufDataForPwBuffer.constFind(buffer);
if (dmabuf == m_dmabufDataForPwBuffer.constEnd()) {
qCDebug(KWIN_SCREENCAST) << objectName() << "Failed to record frame: no dmabuf data";
corruptHeader(spa_buffer);
pw_stream_queue_buffer(m_pwStream, buffer);
return;
}
m_source->render((*dmabuf)->framebuffer());
} else if (auto dmabuf = dynamic_cast<DmaBufScreenCastBuffer *>(buffer)) {
m_source->render(dmabuf->framebuffer.get());
auto cursor = Cursors::self()->currentCursor();
if (m_cursor.mode == ScreencastV1Interface::Embedded && includesCursor(cursor)) {
@ -599,7 +522,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
}
}
if (m_cursor.texture) {
GLFramebuffer::pushFramebuffer((*dmabuf)->framebuffer());
GLFramebuffer::pushFramebuffer(dmabuf->framebuffer.get());
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
@ -632,11 +555,6 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
} else {
glFlush();
}
} else {
qCWarning(KWIN_SCREENCAST, "%s Failed to record frame: invalid buffer type: %d", objectName().toUtf8().constData(), spa_data[0].type);
corruptHeader(spa_buffer);
pw_stream_queue_buffer(m_pwStream, buffer);
return;
}
if (m_cursor.mode == ScreencastV1Interface::Metadata) {
@ -646,7 +564,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
addDamage(spa_buffer, damagedRegion);
addHeader(spa_buffer);
enqueue(buffer);
enqueue(pwBuffer);
}
void ScreenCastStream::addHeader(spa_buffer *spaBuffer)
@ -918,45 +836,6 @@ std::optional<ScreenCastDmaBufTextureParams> ScreenCastStream::testCreateDmaBuf(
};
}
std::shared_ptr<ScreenCastDmaBufTexture> ScreenCastStream::createDmaBufTexture(const ScreenCastDmaBufTextureParams &params)
{
AbstractEglBackend *backend = dynamic_cast<AbstractEglBackend *>(Compositor::self()->backend());
if (!backend) {
return nullptr;
}
GraphicsBuffer *buffer = backend->drmDevice()->allocator()->allocate(GraphicsBufferOptions{
.size = QSize(params.width, params.height),
.format = params.format,
.modifiers = {params.modifier},
});
if (!buffer) {
return nullptr;
}
const DmaBufAttributes *attrs = buffer->dmabufAttributes();
if (!attrs) {
buffer->drop();
return nullptr;
}
backend->makeCurrent();
std::shared_ptr<GLTexture> texture = backend->importDmaBufAsTexture(*attrs);
if (!texture) {
buffer->drop();
return nullptr;
}
std::unique_ptr<GLFramebuffer> framebuffer = std::make_unique<GLFramebuffer>(texture.get());
if (!framebuffer->valid()) {
buffer->drop();
return nullptr;
}
return std::make_shared<ScreenCastDmaBufTexture>(std::move(texture), std::move(framebuffer), buffer);
}
} // namespace KWin
#include "moc_screencaststream.cpp"

View file

@ -8,8 +8,6 @@
#pragma once
#include "config-kwin.h"
#include "wayland/screencast_v1.h"
#include <QDateTime>
@ -30,7 +28,6 @@ namespace KWin
{
class Cursor;
class ScreenCastDmaBufTexture;
class GLTexture;
class PipeWireCore;
class ScreenCastSource;
@ -100,7 +97,6 @@ private:
const QList<uint64_t> &modifiers, quint32 modifiersFlags);
std::optional<ScreenCastDmaBufTextureParams> testCreateDmaBuf(const QSize &size, quint32 format, const QList<uint64_t> &modifiers);
std::shared_ptr<ScreenCastDmaBufTexture> createDmaBufTexture(const ScreenCastDmaBufTextureParams &params);
std::shared_ptr<PipeWireCore> m_pwCore;
std::unique_ptr<ScreenCastSource> m_source;
@ -134,8 +130,6 @@ private:
QMetaObject::Connection positionChangedConnection = QMetaObject::Connection();
} m_cursor;
QHash<struct pw_buffer *, std::shared_ptr<ScreenCastDmaBufTexture>> m_dmabufDataForPwBuffer;
quint64 m_sequential = 0;
bool m_hasDmaBuf = false;
bool m_waitForNewBuffers = false;

View file

@ -9,8 +9,6 @@
#include "opengl/glplatform.h"
#include "opengl/gltexture.h"
#include "opengl/glutils.h"
#include <spa/buffer/buffer.h>
#include <spa/param/video/raw.h>
namespace KWin
{
@ -28,18 +26,12 @@ static void mirrorVertically(uchar *data, int height, int stride)
}
}
static GLenum closestGLType(spa_video_format format)
static GLenum closestGLType(QImage::Format format)
{
switch (format) {
case SPA_VIDEO_FORMAT_RGB:
return GL_RGB;
case SPA_VIDEO_FORMAT_BGR:
return GL_BGR;
case SPA_VIDEO_FORMAT_RGBx:
case SPA_VIDEO_FORMAT_RGBA:
return GL_RGBA;
case SPA_VIDEO_FORMAT_BGRA:
case SPA_VIDEO_FORMAT_BGRx:
case QImage::Format_ARGB32:
case QImage::Format_ARGB32_Premultiplied:
case QImage::Format_RGB32:
return GL_BGRA;
default:
qDebug() << "unknown format" << format;
@ -47,7 +39,7 @@ static GLenum closestGLType(spa_video_format format)
}
}
static void doGrabTexture(GLTexture *texture, spa_data *spa, spa_video_format format)
static void doGrabTexture(GLTexture *texture, QImage *target)
{
const auto context = OpenGlContext::currentContext();
const QSize size = texture->size();
@ -65,12 +57,12 @@ static void doGrabTexture(GLTexture *texture, spa_data *spa, spa_video_format fo
if (context->isOpenGLES() || context->glPlatform()->driver() == Driver_NVidia) {
GLFramebuffer fbo(texture);
GLFramebuffer::pushFramebuffer(&fbo);
glReadPixels(0, 0, size.width(), size.height(), closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
glReadPixels(0, 0, size.width(), size.height(), closestGLType(target->format()), GL_UNSIGNED_BYTE, target->bits());
GLFramebuffer::popFramebuffer();
} else if (context->openglVersion() >= Version(4, 5)) {
glGetTextureImage(texture->texture(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->chunk->size, spa->data);
glGetTextureImage(texture->texture(), 0, closestGLType(target->format()), GL_UNSIGNED_BYTE, target->sizeInBytes(), target->bits());
} else {
glGetTexImage(texture->target(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
glGetTexImage(texture->target(), 0, closestGLType(target->format()), GL_UNSIGNED_BYTE, target->bits());
}
if (invertNeededAndSupported) {
@ -78,15 +70,15 @@ static void doGrabTexture(GLTexture *texture, spa_data *spa, spa_video_format fo
glPixelStorei(GL_PACK_INVERT_MESA, prev);
}
} else if (invertNeeded) {
mirrorVertically(static_cast<uchar *>(spa->data), size.height(), spa->chunk->stride);
mirrorVertically(static_cast<uchar *>(target->bits()), size.height(), target->bytesPerLine());
}
}
static void grabTexture(GLTexture *texture, spa_data *spa, spa_video_format format)
static void grabTexture(GLTexture *texture, QImage *target)
{
const OutputTransform contentTransform = texture->contentTransform();
if (contentTransform == OutputTransform::Normal || contentTransform == OutputTransform::FlipY) {
doGrabTexture(texture, spa, format);
doGrabTexture(texture, target);
} else {
const QSize size = contentTransform.map(texture->size());
const auto backingTexture = GLTexture::allocate(GL_RGBA8, size);
@ -105,7 +97,7 @@ static void grabTexture(GLTexture *texture, spa_data *spa, spa_video_format form
GLFramebuffer::pushFramebuffer(&fbo);
texture->render(size);
GLFramebuffer::popFramebuffer();
doGrabTexture(backingTexture.get(), spa, format);
doGrabTexture(backingTexture.get(), target);
}
}

View file

@ -56,7 +56,7 @@ QSize WindowScreenCastSource::textureSize() const
return m_window->clientGeometry().size().toSize();
}
void WindowScreenCastSource::render(spa_data *spa, spa_video_format format)
void WindowScreenCastSource::render(QImage *target)
{
const auto offscreenTexture = GLTexture::allocate(hasAlphaChannel() ? GL_RGBA8 : GL_RGB8, textureSize());
if (!offscreenTexture) {
@ -66,7 +66,7 @@ void WindowScreenCastSource::render(spa_data *spa, spa_video_format format)
GLFramebuffer offscreenTarget(offscreenTexture.get());
render(&offscreenTarget);
grabTexture(offscreenTexture.get(), spa, format);
grabTexture(offscreenTexture.get(), target);
}
void WindowScreenCastSource::render(GLFramebuffer *target)

View file

@ -29,7 +29,7 @@ public:
uint refreshRate() const override;
void render(GLFramebuffer *target) override;
void render(spa_data *spa, spa_video_format format) override;
void render(QImage *target) override;
std::chrono::nanoseconds clock() const override;
void resume() override;