|
|
|
|
@ -86,8 +86,8 @@ void ScreenCastStream::onStreamStateChanged(void *data, pw_stream_state old, pw_ |
|
|
|
|
qCWarning(KWIN_SCREENCAST) << "Stream error: " << error_message; |
|
|
|
|
break; |
|
|
|
|
case PW_STREAM_STATE_PAUSED: |
|
|
|
|
if (pw->nodeId() == 0 && pw->pwStream) { |
|
|
|
|
pw->pwNodeId = pw_stream_get_node_id(pw->pwStream); |
|
|
|
|
if (pw->nodeId() == 0 && pw->m_pwStream) { |
|
|
|
|
pw->m_pwNodeId = pw_stream_get_node_id(pw->m_pwStream); |
|
|
|
|
Q_EMIT pw->streamReady(pw->nodeId()); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
@ -115,7 +115,7 @@ void ScreenCastStream::newStreamParams() |
|
|
|
|
uint8_t paramsBuffer[1024]; |
|
|
|
|
spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(paramsBuffer, sizeof(paramsBuffer)); |
|
|
|
|
const int buffertypes = m_dmabufParams ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) : (1 << SPA_DATA_MemFd); |
|
|
|
|
const int bpp = videoFormat.format == SPA_VIDEO_FORMAT_RGB || videoFormat.format == SPA_VIDEO_FORMAT_BGR ? 3 : 4; |
|
|
|
|
const int bpp = m_videoFormat.format == SPA_VIDEO_FORMAT_RGB || m_videoFormat.format == SPA_VIDEO_FORMAT_BGR ? 3 : 4; |
|
|
|
|
const int stride = SPA_ROUND_UP_N(m_resolution.width() * bpp, 4); |
|
|
|
|
|
|
|
|
|
struct spa_pod_frame f; |
|
|
|
|
@ -151,7 +151,7 @@ void ScreenCastStream::newStreamParams() |
|
|
|
|
SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
pw_stream_update_params(pwStream, params.data(), params.count()); |
|
|
|
|
pw_stream_update_params(m_pwStream, params.data(), params.count()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void ScreenCastStream::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format) |
|
|
|
|
@ -161,7 +161,7 @@ void ScreenCastStream::onStreamParamChanged(void *data, uint32_t id, const struc |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ScreenCastStream *pw = static_cast<ScreenCastStream *>(data); |
|
|
|
|
spa_format_video_raw_parse(format, &pw->videoFormat); |
|
|
|
|
spa_format_video_raw_parse(format, &pw->m_videoFormat); |
|
|
|
|
auto modifierProperty = spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier); |
|
|
|
|
QVector<uint64_t> receivedModifiers; |
|
|
|
|
if (modifierProperty) { |
|
|
|
|
@ -203,7 +203,7 @@ void ScreenCastStream::onStreamParamChanged(void *data, uint32_t id, const struc |
|
|
|
|
qCDebug(KWIN_SCREENCAST) << "Stream dmabuf modifiers received, offering our best suited modifier" << pw->m_dmabufParams.has_value(); |
|
|
|
|
char buffer[2048]; |
|
|
|
|
auto params = pw->buildFormats(pw->m_dmabufParams.has_value(), buffer); |
|
|
|
|
pw_stream_update_params(pw->pwStream, params.data(), params.count()); |
|
|
|
|
pw_stream_update_params(pw->m_pwStream, params.data(), params.count()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -306,7 +306,7 @@ void ScreenCastStream::onStreamRenegotiateFormat(void *data, uint64_t) |
|
|
|
|
stream->m_streaming = false; // pause streaming as we wait for the renegotiation
|
|
|
|
|
char buffer[2048]; |
|
|
|
|
auto params = stream->buildFormats(stream->m_dmabufParams.has_value(), buffer); |
|
|
|
|
pw_stream_update_params(stream->pwStream, params.data(), params.count()); |
|
|
|
|
pw_stream_update_params(stream->m_pwStream, params.data(), params.count()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ScreenCastStream::ScreenCastStream(ScreenCastSource *source, QObject *parent) |
|
|
|
|
@ -319,11 +319,11 @@ ScreenCastStream::ScreenCastStream(ScreenCastSource *source, QObject *parent) |
|
|
|
|
Q_EMIT stopStreaming(); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
pwStreamEvents.version = PW_VERSION_STREAM_EVENTS; |
|
|
|
|
pwStreamEvents.add_buffer = &ScreenCastStream::onStreamAddBuffer; |
|
|
|
|
pwStreamEvents.remove_buffer = &ScreenCastStream::onStreamRemoveBuffer; |
|
|
|
|
pwStreamEvents.state_changed = &ScreenCastStream::onStreamStateChanged; |
|
|
|
|
pwStreamEvents.param_changed = &ScreenCastStream::onStreamParamChanged; |
|
|
|
|
m_pwStreamEvents.version = PW_VERSION_STREAM_EVENTS; |
|
|
|
|
m_pwStreamEvents.add_buffer = &ScreenCastStream::onStreamAddBuffer; |
|
|
|
|
m_pwStreamEvents.remove_buffer = &ScreenCastStream::onStreamRemoveBuffer; |
|
|
|
|
m_pwStreamEvents.state_changed = &ScreenCastStream::onStreamStateChanged; |
|
|
|
|
m_pwStreamEvents.param_changed = &ScreenCastStream::onStreamParamChanged; |
|
|
|
|
|
|
|
|
|
connect(&m_pendingFrame, &QTimer::timeout, this, [this] { |
|
|
|
|
recordFrame(m_pendingDamages); |
|
|
|
|
@ -333,20 +333,20 @@ ScreenCastStream::ScreenCastStream(ScreenCastSource *source, QObject *parent) |
|
|
|
|
ScreenCastStream::~ScreenCastStream() |
|
|
|
|
{ |
|
|
|
|
m_stopped = true; |
|
|
|
|
if (pwStream) { |
|
|
|
|
pw_stream_destroy(pwStream); |
|
|
|
|
if (m_pwStream) { |
|
|
|
|
pw_stream_destroy(m_pwStream); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool ScreenCastStream::init() |
|
|
|
|
{ |
|
|
|
|
pwCore = PipeWireCore::self(); |
|
|
|
|
if (!pwCore->m_error.isEmpty()) { |
|
|
|
|
m_error = pwCore->m_error; |
|
|
|
|
m_pwCore = PipeWireCore::self(); |
|
|
|
|
if (!m_pwCore->m_error.isEmpty()) { |
|
|
|
|
m_error = m_pwCore->m_error; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
connect(pwCore.get(), &PipeWireCore::pipewireFailed, this, &ScreenCastStream::coreFailed); |
|
|
|
|
connect(m_pwCore.get(), &PipeWireCore::pipewireFailed, this, &ScreenCastStream::coreFailed); |
|
|
|
|
|
|
|
|
|
if (!createStream()) { |
|
|
|
|
qCWarning(KWIN_SCREENCAST) << "Failed to create PipeWire stream"; |
|
|
|
|
@ -354,15 +354,15 @@ bool ScreenCastStream::init() |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pwRenegotiate = pw_loop_add_event(pwCore.get()->pwMainLoop, onStreamRenegotiateFormat, this); |
|
|
|
|
m_pwRenegotiate = pw_loop_add_event(m_pwCore->pwMainLoop, onStreamRenegotiateFormat, this); |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint ScreenCastStream::framerate() |
|
|
|
|
{ |
|
|
|
|
if (pwStream) { |
|
|
|
|
return videoFormat.max_framerate.num / videoFormat.max_framerate.denom; |
|
|
|
|
if (m_pwStream) { |
|
|
|
|
return m_videoFormat.max_framerate.num / m_videoFormat.max_framerate.denom; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
@ -370,13 +370,13 @@ uint ScreenCastStream::framerate() |
|
|
|
|
|
|
|
|
|
uint ScreenCastStream::nodeId() |
|
|
|
|
{ |
|
|
|
|
return pwNodeId; |
|
|
|
|
return m_pwNodeId; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool ScreenCastStream::createStream() |
|
|
|
|
{ |
|
|
|
|
const QByteArray objname = "kwin-screencast-" + objectName().toUtf8(); |
|
|
|
|
pwStream = pw_stream_new(pwCore->pwCore, objname, nullptr); |
|
|
|
|
m_pwStream = pw_stream_new(m_pwCore->pwCore, objname, nullptr); |
|
|
|
|
|
|
|
|
|
const auto supported = Compositor::self()->backend()->supportedFormats(); |
|
|
|
|
auto itModifiers = supported.constFind(m_source->drmFormat()); |
|
|
|
|
@ -403,13 +403,13 @@ bool ScreenCastStream::createStream() |
|
|
|
|
char buffer[2048]; |
|
|
|
|
QVector<const spa_pod *> params = buildFormats(false, buffer); |
|
|
|
|
|
|
|
|
|
pw_stream_add_listener(pwStream, &streamListener, &pwStreamEvents, this); |
|
|
|
|
pw_stream_add_listener(m_pwStream, &m_streamListener, &m_pwStreamEvents, this); |
|
|
|
|
auto flags = pw_stream_flags(PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_ALLOC_BUFFERS); |
|
|
|
|
|
|
|
|
|
if (pw_stream_connect(pwStream, PW_DIRECTION_OUTPUT, SPA_ID_INVALID, flags, params.data(), params.count()) != 0) { |
|
|
|
|
if (pw_stream_connect(m_pwStream, PW_DIRECTION_OUTPUT, SPA_ID_INVALID, flags, params.data(), params.count()) != 0) { |
|
|
|
|
qCWarning(KWIN_SCREENCAST) << "Could not connect to stream"; |
|
|
|
|
pw_stream_destroy(pwStream); |
|
|
|
|
pwStream = nullptr; |
|
|
|
|
pw_stream_destroy(m_pwStream); |
|
|
|
|
m_pwStream = nullptr; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -447,8 +447,8 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion) |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (videoFormat.max_framerate.num != 0 && !m_lastSent.isNull()) { |
|
|
|
|
auto frameInterval = (1000. * videoFormat.max_framerate.denom / videoFormat.max_framerate.num); |
|
|
|
|
if (m_videoFormat.max_framerate.num != 0 && !m_lastSent.isNull()) { |
|
|
|
|
auto frameInterval = (1000. * m_videoFormat.max_framerate.denom / m_videoFormat.max_framerate.num); |
|
|
|
|
auto lastSentAgo = m_lastSent.msecsTo(QDateTime::currentDateTimeUtc()); |
|
|
|
|
if (lastSentAgo < frameInterval) { |
|
|
|
|
m_pendingDamages |= damagedRegion; |
|
|
|
|
@ -474,12 +474,12 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion) |
|
|
|
|
m_resolution = size; |
|
|
|
|
m_waitForNewBuffers = true; |
|
|
|
|
m_dmabufParams = std::nullopt; |
|
|
|
|
pw_loop_signal_event(pwCore.get()->pwMainLoop, pwRenegotiate); |
|
|
|
|
pw_loop_signal_event(m_pwCore->pwMainLoop, m_pwRenegotiate); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char *error = ""; |
|
|
|
|
auto state = pw_stream_get_state(pwStream, &error); |
|
|
|
|
auto state = pw_stream_get_state(m_pwStream, &error); |
|
|
|
|
if (state != PW_STREAM_STATE_STREAMING) { |
|
|
|
|
if (error) { |
|
|
|
|
qCWarning(KWIN_SCREENCAST) << "Failed to record frame: stream is not active" << error; |
|
|
|
|
@ -487,7 +487,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion) |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct pw_buffer *buffer = pw_stream_dequeue_buffer(pwStream); |
|
|
|
|
struct pw_buffer *buffer = pw_stream_dequeue_buffer(m_pwStream); |
|
|
|
|
|
|
|
|
|
if (!buffer) { |
|
|
|
|
return; |
|
|
|
|
@ -499,7 +499,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion) |
|
|
|
|
uint8_t *data = (uint8_t *)spa_data->data; |
|
|
|
|
if (!data && spa_buffer->datas->type != SPA_DATA_DmaBuf) { |
|
|
|
|
qCWarning(KWIN_SCREENCAST) << "Failed to record frame: invalid buffer data"; |
|
|
|
|
pw_stream_queue_buffer(pwStream, buffer); |
|
|
|
|
pw_stream_queue_buffer(m_pwStream, buffer); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -513,14 +513,14 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion) |
|
|
|
|
|
|
|
|
|
if ((stride * size.height()) > spa_data->maxsize) { |
|
|
|
|
qCDebug(KWIN_SCREENCAST) << "Failed to record frame: frame is too big"; |
|
|
|
|
pw_stream_queue_buffer(pwStream, buffer); |
|
|
|
|
pw_stream_queue_buffer(m_pwStream, buffer); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
spa_data->chunk->stride = stride; |
|
|
|
|
spa_data->chunk->size = stride * size.height(); |
|
|
|
|
|
|
|
|
|
m_source->render(spa_data, videoFormat.format); |
|
|
|
|
m_source->render(spa_data, m_videoFormat.format); |
|
|
|
|
|
|
|
|
|
auto cursor = Cursors::self()->currentCursor(); |
|
|
|
|
if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded && exclusiveContains(m_cursor.viewport, cursor->pos())) { |
|
|
|
|
@ -652,7 +652,7 @@ void ScreenCastStream::recordCursor() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char *error = ""; |
|
|
|
|
auto state = pw_stream_get_state(pwStream, &error); |
|
|
|
|
auto state = pw_stream_get_state(m_pwStream, &error); |
|
|
|
|
if (state != PW_STREAM_STATE_STREAMING) { |
|
|
|
|
if (error) { |
|
|
|
|
qCWarning(KWIN_SCREENCAST) << "Failed to record cursor position: stream is not active" << error; |
|
|
|
|
@ -664,7 +664,7 @@ void ScreenCastStream::recordCursor() |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
m_pendingBuffer = pw_stream_dequeue_buffer(pwStream); |
|
|
|
|
m_pendingBuffer = pw_stream_dequeue_buffer(m_pwStream); |
|
|
|
|
if (!m_pendingBuffer) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
@ -719,7 +719,7 @@ void ScreenCastStream::enqueue() |
|
|
|
|
if (!m_streaming) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
pw_stream_queue_buffer(pwStream, m_pendingBuffer); |
|
|
|
|
pw_stream_queue_buffer(m_pwStream, m_pendingBuffer); |
|
|
|
|
|
|
|
|
|
if (m_pendingBuffer->buffer->datas[0].chunk->flags != SPA_CHUNK_FLAG_CORRUPTED) { |
|
|
|
|
m_lastSent = QDateTime::currentDateTimeUtc(); |
|
|
|
|
|