treeland/examples/test_capture/player.cpp

366 lines
13 KiB
C++

// Copyright (C) 2024 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "player.h"
#include "capture.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <libdrm/drm_fourcc.h>
#include <rhi/qrhi.h>
#include <QSGImageNode>
#include <QSGTexture>
#include <unistd.h>
Player::Player()
{
setFlag(QQuickItem::ItemHasContents);
}
TreelandCaptureContext *Player::captureContext() const
{
return m_captureContext;
}
void Player::setCaptureContext(TreelandCaptureContext *context)
{
if (context == m_captureContext)
return;
if (m_captureContext) {
m_captureContext->disconnect(this);
}
m_captureContext = context;
if (m_captureContext) {
connect(m_captureContext.data(),
&TreelandCaptureContext::destroyed,
this,
std::bind(&Player::setCaptureContext, this, nullptr));
if (m_captureContext->session()) {
connect(m_captureContext->session(),
&TreelandCaptureSession::ready,
this,
&Player::update);
} else {
connect(m_captureContext, &TreelandCaptureContext::sessionChanged, this, [this] {
if (m_captureContext->session())
connect(m_captureContext->session(),
&TreelandCaptureSession::ready,
this,
&Player::update);
});
}
connect(m_captureContext.data(),
&TreelandCaptureContext::sourceReady,
this,
&Player::updateGeometry);
}
Q_EMIT captureContextChanged();
}
QSGNode *Player::updatePaintNode(QSGNode *old, [[maybe_unused]] UpdatePaintNodeData *data)
{
ensureDebugLogger();
if (!m_captureContext || !m_captureContext->session()
|| !m_captureContext->session()->started())
return nullptr;
updateTexture();
auto node = static_cast<QSGImageNode *>(old);
if (Q_UNLIKELY(!node) && m_rhiTexture != nullptr) {
node = window()->createImageNode();
node->setOwnsTexture(false);
node->setTexture(&m_texture);
} else if (m_rhiTexture != nullptr) {
node->markDirty(QSGNode::DirtyMaterial);
} else {
return nullptr;
}
QRectF sourceRect;
if (m_captureContext->sourceType()
== QtWayland::treeland_capture_context_v1::source_type_window) {
sourceRect = QRectF({ 0, 0 }, m_texture.textureSize());
} else {
sourceRect = m_captureContext->captureRegion();
}
qInfo() << "sourceRect" << sourceRect << "textureSize" << m_texture.textureSize();
node->setSourceRect(sourceRect);
node->setRect(boundingRect());
node->setFiltering(QSGTexture::Linear);
node->setMipmapFiltering(QSGTexture::None);
return node;
}
#define CASE_STR(value) \
case value: \
return #value;
static const char *eglGetErrorString(EGLint error)
{
switch (error) {
CASE_STR(EGL_SUCCESS)
CASE_STR(EGL_NOT_INITIALIZED)
CASE_STR(EGL_BAD_ACCESS)
CASE_STR(EGL_BAD_ALLOC)
CASE_STR(EGL_BAD_ATTRIBUTE)
CASE_STR(EGL_BAD_CONTEXT)
CASE_STR(EGL_BAD_CONFIG)
CASE_STR(EGL_BAD_CURRENT_SURFACE)
CASE_STR(EGL_BAD_DISPLAY)
CASE_STR(EGL_BAD_SURFACE)
CASE_STR(EGL_BAD_MATCH)
CASE_STR(EGL_BAD_PARAMETER)
CASE_STR(EGL_BAD_NATIVE_PIXMAP)
CASE_STR(EGL_BAD_NATIVE_WINDOW)
CASE_STR(EGL_CONTEXT_LOST)
default:
return "Unknown";
}
}
[[maybe_unused]] static const char *drmFormatString(int fourcc)
{
switch (fourcc) {
CASE_STR(DRM_FORMAT_R8)
CASE_STR(DRM_FORMAT_RG88)
CASE_STR(DRM_FORMAT_GR88)
CASE_STR(DRM_FORMAT_R16)
CASE_STR(DRM_FORMAT_GR1616)
CASE_STR(DRM_FORMAT_RGB332)
CASE_STR(DRM_FORMAT_BGR233)
CASE_STR(DRM_FORMAT_XRGB4444)
CASE_STR(DRM_FORMAT_XBGR4444)
CASE_STR(DRM_FORMAT_RGBX4444)
CASE_STR(DRM_FORMAT_BGRX4444)
CASE_STR(DRM_FORMAT_ARGB4444)
CASE_STR(DRM_FORMAT_ABGR4444)
CASE_STR(DRM_FORMAT_RGBA4444)
CASE_STR(DRM_FORMAT_BGRA4444)
CASE_STR(DRM_FORMAT_XRGB1555)
CASE_STR(DRM_FORMAT_XBGR1555)
CASE_STR(DRM_FORMAT_RGBX5551)
CASE_STR(DRM_FORMAT_BGRX5551)
CASE_STR(DRM_FORMAT_ARGB1555)
CASE_STR(DRM_FORMAT_ABGR1555)
CASE_STR(DRM_FORMAT_RGBA5551)
CASE_STR(DRM_FORMAT_BGRA5551)
CASE_STR(DRM_FORMAT_RGB565)
CASE_STR(DRM_FORMAT_BGR565)
CASE_STR(DRM_FORMAT_RGB888)
CASE_STR(DRM_FORMAT_BGR888)
CASE_STR(DRM_FORMAT_XRGB8888)
CASE_STR(DRM_FORMAT_XBGR8888)
CASE_STR(DRM_FORMAT_RGBX8888)
CASE_STR(DRM_FORMAT_BGRX8888)
CASE_STR(DRM_FORMAT_ARGB8888)
CASE_STR(DRM_FORMAT_ABGR8888)
CASE_STR(DRM_FORMAT_RGBA8888)
CASE_STR(DRM_FORMAT_BGRA8888)
CASE_STR(DRM_FORMAT_XRGB2101010)
CASE_STR(DRM_FORMAT_XBGR2101010)
CASE_STR(DRM_FORMAT_RGBX1010102)
CASE_STR(DRM_FORMAT_BGRX1010102)
CASE_STR(DRM_FORMAT_ARGB2101010)
CASE_STR(DRM_FORMAT_ABGR2101010)
CASE_STR(DRM_FORMAT_RGBA1010102)
CASE_STR(DRM_FORMAT_BGRA1010102)
CASE_STR(DRM_FORMAT_ABGR16161616)
CASE_STR(DRM_FORMAT_XBGR16161616)
CASE_STR(DRM_FORMAT_XBGR16161616F)
CASE_STR(DRM_FORMAT_ABGR16161616F)
CASE_STR(DRM_FORMAT_YUYV)
CASE_STR(DRM_FORMAT_YVYU)
CASE_STR(DRM_FORMAT_UYVY)
CASE_STR(DRM_FORMAT_VYUY)
CASE_STR(DRM_FORMAT_AYUV)
CASE_STR(DRM_FORMAT_XYUV8888)
CASE_STR(DRM_FORMAT_Y210)
CASE_STR(DRM_FORMAT_Y212)
CASE_STR(DRM_FORMAT_Y216)
CASE_STR(DRM_FORMAT_Y410)
CASE_STR(DRM_FORMAT_Y412)
CASE_STR(DRM_FORMAT_Y416)
CASE_STR(DRM_FORMAT_NV12)
CASE_STR(DRM_FORMAT_NV21)
CASE_STR(DRM_FORMAT_NV16)
CASE_STR(DRM_FORMAT_NV61)
CASE_STR(DRM_FORMAT_P010)
CASE_STR(DRM_FORMAT_P012)
CASE_STR(DRM_FORMAT_P016)
CASE_STR(DRM_FORMAT_P030)
CASE_STR(DRM_FORMAT_YUV410)
CASE_STR(DRM_FORMAT_YVU410)
CASE_STR(DRM_FORMAT_YUV411)
CASE_STR(DRM_FORMAT_YVU411)
CASE_STR(DRM_FORMAT_YUV420)
CASE_STR(DRM_FORMAT_YVU420)
CASE_STR(DRM_FORMAT_YUV422)
CASE_STR(DRM_FORMAT_YVU422)
CASE_STR(DRM_FORMAT_YUV444)
CASE_STR(DRM_FORMAT_YVU444)
default:
return "Unknown";
}
}
#undef CASE_STR
void Player::updateTexture()
{
if (!m_captureContext->session()->frameValid())
return;
auto glContext = QOpenGLContext::currentContext();
Q_ASSERT(glContext);
auto eglContext = glContext->nativeInterface<QNativeInterface::QEGLContext>();
Q_ASSERT(eglContext);
EGLImage eglImage;
EGLAttrib attribs[47];
int atti = 0;
attribs[atti++] = EGL_WIDTH;
attribs[atti++] = m_captureContext->session()->bufferWidth();
attribs[atti++] = EGL_HEIGHT;
attribs[atti++] = m_captureContext->session()->bufferHeight();
attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
attribs[atti++] = m_captureContext->session()->bufferFormat();
auto objects = m_captureContext->session()->objects();
auto nPlanes = objects.size();
if (nPlanes > 0) {
attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
attribs[atti++] = objects[0].fd;
attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
attribs[atti++] = objects[0].offset;
attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
attribs[atti++] = objects[0].stride;
if (m_captureContext->session()->modifierUnion().modifier) {
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modLow;
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modHigh;
}
}
if (nPlanes > 1) {
attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
attribs[atti++] = objects[1].fd;
attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
attribs[atti++] = objects[1].offset;
attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
attribs[atti++] = objects[1].stride;
if (m_captureContext->session()->modifierUnion().modifier) {
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modLow;
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modHigh;
}
}
if (nPlanes > 2) {
attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
attribs[atti++] = objects[2].fd;
attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
attribs[atti++] = objects[2].offset;
attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
attribs[atti++] = objects[2].stride;
if (m_captureContext->session()->modifierUnion().modifier) {
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modLow;
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modHigh;
}
}
if (nPlanes > 3) {
attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT;
attribs[atti++] = objects[3].fd;
attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
attribs[atti++] = objects[3].offset;
attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
attribs[atti++] = objects[3].stride;
if (m_captureContext->session()->modifierUnion().modifier) {
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modLow;
attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
attribs[atti++] = m_captureContext->session()->modifierUnion().modHigh;
}
}
attribs[atti++] = EGL_NONE;
eglImage =
eglCreateImage(eglContext->display(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, 0, attribs);
if (eglImage == EGL_NO_IMAGE) {
qWarning() << eglGetErrorString(eglGetError());
m_captureContext->session()->doneFrame();
return;
}
Q_ASSERT_X(window(), __func__, "Window should be ready for rhi.");
if (m_rhiTexture) {
m_texture.setTexture(nullptr);
delete m_rhiTexture;
}
m_rhiTexture =
window()->rhi()->newTexture(QRhiTexture::RGBA8,
QSize{ int(m_captureContext->session()->bufferWidth()),
int(m_captureContext->session()->bufferHeight()) },
1,
QRhiTexture::TextureArray);
glBindTexture(GL_TEXTURE_2D, m_rhiTexture->nativeTexture().object);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
m_texture.setTexture(m_rhiTexture);
m_texture.setTextureSize(QSize{ int(m_captureContext->session()->bufferWidth()),
int(m_captureContext->session()->bufferHeight()) });
m_texture.setOwnsTexture(false);
glBindTexture(GL_TEXTURE_2D, 0);
eglDestroyImage(eglContext->display(), eglImage);
m_captureContext->session()->doneFrame();
}
void Player::updateGeometry()
{
Q_ASSERT(m_captureContext);
setWidth(m_captureContext->captureRegion().width());
setHeight(m_captureContext->captureRegion().height());
}
static void EGLAPIENTRY debugCallback(EGLenum error,
[[maybe_unused]] const char *command,
[[maybe_unused]] EGLint messageType,
[[maybe_unused]] EGLLabelKHR threadLabel,
[[maybe_unused]] EGLLabelKHR objectLabel,
const char *message)
{
qInfo() << "EGL Debug:" << message << "(Error Code:" << error << ")";
}
void Player::ensureDebugLogger()
{
if (m_loggerInitialized)
return;
if (QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_KHR_debug"))) {
const EGLAttrib debugAttribs[] = {
EGL_DEBUG_MSG_CRITICAL_KHR,
EGL_TRUE,
EGL_DEBUG_MSG_ERROR_KHR,
EGL_TRUE,
EGL_DEBUG_MSG_WARN_KHR,
EGL_TRUE,
EGL_DEBUG_MSG_INFO_KHR,
EGL_TRUE,
EGL_NONE,
};
PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR =
(PFNEGLDEBUGMESSAGECONTROLKHRPROC)eglGetProcAddress("eglDebugMessageControlKHR");
if (eglDebugMessageControlKHR) {
eglDebugMessageControlKHR(debugCallback, debugAttribs);
} else {
qCritical() << "Failed to get eglDebugMessageControlKHR function.";
}
} else {
qCritical() << "GL_KHR_debug is not supported.";
}
m_loggerInitialized = true;
}