Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kpipewire6 for openSUSE:Factory checked in at 2024-07-17 15:13:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kpipewire6 (Old) and /work/SRC/openSUSE:Factory/.kpipewire6.new.17339 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kpipewire6" Wed Jul 17 15:13:03 2024 rev:9 rq:1187987 version:6.1.3 Changes: -------- --- /work/SRC/openSUSE:Factory/kpipewire6/kpipewire6.changes 2024-07-04 16:20:44.905975821 +0200 +++ /work/SRC/openSUSE:Factory/.kpipewire6.new.17339/kpipewire6.changes 2024-07-17 15:13:13.329840645 +0200 @@ -1,0 +2,19 @@ +Tue Jul 16 13:35:56 UTC 2024 - Fabian Vogt <fab...@ritter-vogt.de> + +- Add patches to support libopenh264 for encoding + (boo#1227461, kde#476187): + * 0001-Simpler-yet-more-useful-handling-of-KPIPEWIRE_FORCE_.patch + * 0002-Add-encoder-using-libopenh264.patch + +------------------------------------------------------------------- +Tue Jul 16 13:23:41 UTC 2024 - Fabian Vogt <fab...@ritter-vogt.de> + +- Update to 6.1.3: + * New bugfix release + * For more details see https://kde.org/announcements/plasma/6/6.1.3 +- Changes since 6.1.2: + * produce: Properly cleanup on deactivate in all cases (kde#488687) + * produce: Destroy PipeWireSourceStream on the right thread (kde#489434) + * update version for new release + +------------------------------------------------------------------- Old: ---- kpipewire-6.1.2.tar.xz kpipewire-6.1.2.tar.xz.sig New: ---- 0001-Simpler-yet-more-useful-handling-of-KPIPEWIRE_FORCE_.patch 0002-Add-encoder-using-libopenh264.patch kpipewire-6.1.3.tar.xz kpipewire-6.1.3.tar.xz.sig BETA DEBUG BEGIN: New: (boo#1227461, kde#476187): * 0001-Simpler-yet-more-useful-handling-of-KPIPEWIRE_FORCE_.patch * 0002-Add-encoder-using-libopenh264.patch New: * 0001-Simpler-yet-more-useful-handling-of-KPIPEWIRE_FORCE_.patch * 0002-Add-encoder-using-libopenh264.patch BETA DEBUG END: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kpipewire6.spec ++++++ --- /var/tmp/diff_new_pack.AWghcL/_old 2024-07-17 15:13:14.005865411 +0200 +++ /var/tmp/diff_new_pack.AWghcL/_new 2024-07-17 15:13:14.009865558 +0200 @@ -27,7 +27,7 @@ %{!?_plasma6_version: %define _plasma6_version %(echo %{_plasma6_bugfix} | awk -F. '{print $1"."$2}')} %bcond_without released Name: kpipewire6 -Version: 6.1.2 +Version: 6.1.3 Release: 0 Summary: PipeWire integration for KDE Plasma License: LGPL-2.0-only AND LGPL-3.0-only @@ -37,6 +37,9 @@ Source1: https://download.kde.org/stable/plasma/%{version}/%{rname}-%{version}.tar.xz.sig Source2: plasma.keyring %endif +# PATCH-FEATURE-UPSTREAM +Patch1: 0001-Simpler-yet-more-useful-handling-of-KPIPEWIRE_FORCE_.patch +Patch2: 0002-Add-encoder-using-libopenh264.patch BuildRequires: kf6-extra-cmake-modules BuildRequires: pkgconfig BuildRequires: qt6-gui-private-devel >= %{qt6_version} ++++++ 0001-Simpler-yet-more-useful-handling-of-KPIPEWIRE_FORCE_.patch ++++++ >From a50c28da704fbf8b9e71ec92054f325a33b9765f Mon Sep 17 00:00:00 2001 From: Fabian Vogt <fab...@ritter-vogt.de> Date: Sat, 6 Jul 2024 16:27:28 +0200 Subject: [PATCH 1/2] Simpler yet more useful handling of KPIPEWIRE_FORCE_ENCODER Previously, it always overrode the encoder type and profile. Now just force a specific encoder by inlining the encoder selection in the switch cases. This means it's no longer possible to force a different encoder type than the application requested, but it's arguably not that useful to e.g. force VP9 if the application expects H.264 packets. (cherry picked from commit 0c3f8b4f9de7d4dcd24d952184dabdbda74b4c35) --- src/pipewireproduce.cpp | 45 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/src/pipewireproduce.cpp b/src/pipewireproduce.cpp index 3452ce9..416bcd3 100644 --- a/src/pipewireproduce.cpp +++ b/src/pipewireproduce.cpp @@ -266,46 +266,19 @@ void PipeWireProduce::stateChanged(pw_stream_state state) std::unique_ptr<Encoder> PipeWireProduce::makeEncoder() { - auto encoderType = m_encoderType; - bool forceSoftware = false; - bool forceHardware = false; - - if (qEnvironmentVariableIsSet("KPIPEWIRE_FORCE_ENCODER")) { - auto forcedEncoder = qEnvironmentVariable("KPIPEWIRE_FORCE_ENCODER"); - if (forcedEncoder == u"libvpx") { - qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing VP8 Software encoding"; - encoderType = PipeWireBaseEncodedStream::VP8; - forceSoftware = true; - } else if (forcedEncoder == u"libvpx-vp9") { - qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing VP9 Software encoding"; - encoderType = PipeWireBaseEncodedStream::VP9; - forceSoftware = true; - } else if (forcedEncoder == u"libx264") { - qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Software encoding, main profile"; - encoderType = PipeWireBaseEncodedStream::H264Main; - forceSoftware = true; - } else if (forcedEncoder == u"h264_vaapi") { - qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Hardware encoding, main profile"; - encoderType = PipeWireBaseEncodedStream::H264Main; - forceHardware = true; - } else if (forcedEncoder == u"libx264_baseline") { - qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Software encoding, baseline profile"; - encoderType = PipeWireBaseEncodedStream::H264Baseline; - forceSoftware = true; - } else if (forcedEncoder == u"h264_vaapi_baseline") { - qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing H264 Hardware encoding, baseline profile"; - encoderType = PipeWireBaseEncodedStream::H264Baseline; - forceHardware = true; - } + auto forcedEncoder = qEnvironmentVariable("KPIPEWIRE_FORCE_ENCODER"); + if (!forcedEncoder.isNull()) { + qCWarning(PIPEWIRERECORD_LOGGING) << "Forcing encoder to" << forcedEncoder; } auto size = m_stream->size(); - switch (encoderType) { + switch (m_encoderType) { case PipeWireBaseEncodedStream::H264Baseline: case PipeWireBaseEncodedStream::H264Main: { auto profile = m_encoderType == PipeWireBaseEncodedStream::H264Baseline ? Encoder::H264Profile::Baseline : Encoder::H264Profile::Main; - if (!forceSoftware) { + + if (forcedEncoder.isNull() || forcedEncoder == u"h264_vaapi") { auto hardwareEncoder = std::make_unique<H264VAAPIEncoder>(profile, this); hardwareEncoder->setQuality(m_quality); hardwareEncoder->setEncodingPreference(m_encodingPreference); @@ -314,7 +287,7 @@ std::unique_ptr<Encoder> PipeWireProduce::makeEncoder() } } - if (!forceHardware) { + if (forcedEncoder.isNull() || forcedEncoder == u"libx264") { auto softwareEncoder = std::make_unique<LibX264Encoder>(profile, this); softwareEncoder->setQuality(m_quality); softwareEncoder->setEncodingPreference(m_encodingPreference); @@ -325,7 +298,7 @@ std::unique_ptr<Encoder> PipeWireProduce::makeEncoder() break; } case PipeWireBaseEncodedStream::VP8: { - if (!forceHardware) { + if (forcedEncoder.isNull() || forcedEncoder == u"libvpx") { auto encoder = std::make_unique<LibVpxEncoder>(this); encoder->setQuality(m_quality); if (encoder->initialize(size)) { @@ -335,7 +308,7 @@ std::unique_ptr<Encoder> PipeWireProduce::makeEncoder() break; } case PipeWireBaseEncodedStream::VP9: { - if (!forceHardware) { + if (forcedEncoder.isNull() || forcedEncoder == u"libvpx-vp9") { auto encoder = std::make_unique<LibVpxVp9Encoder>(this); encoder->setQuality(m_quality); if (encoder->initialize(size)) { -- 2.45.2 ++++++ 0002-Add-encoder-using-libopenh264.patch ++++++ >From 43ab595c28e031f38bc92bea4cf475de64021958 Mon Sep 17 00:00:00 2001 From: Fabian Vogt <fab...@ritter-vogt.de> Date: Sat, 6 Jul 2024 16:40:42 +0200 Subject: [PATCH 2/2] Add encoder using libopenh264 On some distributions, libopenh264 is the only encoder available OOTB. Add support for it and use it as fallback. BUG: 476187 (cherry picked from commit e17793a3b023f26411001093bb2d5934adf715c7) --- src/CMakeLists.txt | 1 + src/libopenh264encoder.cpp | 106 ++++++++++++++++++++++++++++++ src/libopenh264encoder_p.h | 28 ++++++++ src/pipewirebaseencodedstream.cpp | 2 +- src/pipewireproduce.cpp | 11 ++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/libopenh264encoder.cpp create mode 100644 src/libopenh264encoder_p.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e96f52b..3126528 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -135,6 +135,7 @@ add_library(KPipeWireRecord ${kpipewirerecord_SRCS} encoder.cpp h264vaapiencoder.cpp libx264encoder.cpp + libopenh264encoder.cpp libvpxencoder.cpp libvpxvp9encoder.cpp ) diff --git a/src/libopenh264encoder.cpp b/src/libopenh264encoder.cpp new file mode 100644 index 0000000..6d4c6a1 --- /dev/null +++ b/src/libopenh264encoder.cpp @@ -0,0 +1,106 @@ +/* + SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleix...@kde.org> + SPDX-FileCopyrightText: 2023 Marco Martin <m...@kde.org> + SPDX-FileCopyrightText: 2023 Arjen Hiemstra <ahiems...@heimr.nl> + SPDX-FileCopyrightText: 2024 Fabian Vogt <fab...@ritter-vogt.de> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "libopenh264encoder_p.h" + +#include <QSize> +#include <QThread> + +extern "C" { +#include <libavcodec/avcodec.h> +#include <libavfilter/buffersink.h> +#include <libavfilter/buffersrc.h> +#include <libavutil/pixfmt.h> +} + +#include "logging_record.h" + +LibOpenH264Encoder::LibOpenH264Encoder(H264Profile profile, PipeWireProduce *produce) + : SoftwareEncoder(produce) + , m_profile(profile) +{ +} + +bool LibOpenH264Encoder::initialize(const QSize &size) +{ + createFilterGraph(size); + + auto codec = avcodec_find_encoder_by_name("libopenh264"); + if (!codec) { + qCWarning(PIPEWIRERECORD_LOGGING) << "libopenh264 codec not found"; + return false; + } + + m_avCodecContext = avcodec_alloc_context3(codec); + if (!m_avCodecContext) { + qCWarning(PIPEWIRERECORD_LOGGING) << "Could not allocate video codec context"; + return false; + } + + Q_ASSERT(!size.isEmpty()); + m_avCodecContext->width = size.width(); + m_avCodecContext->height = size.height(); + m_avCodecContext->max_b_frames = 0; + m_avCodecContext->gop_size = 100; + m_avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P; + m_avCodecContext->time_base = AVRational{1, 1000}; + + if (m_quality) { + // "q" here stands for "quantization", but that effectively impacts quality. + m_avCodecContext->qmin = m_avCodecContext->qmax = percentageToAbsoluteQuality(m_quality); + } + + switch (m_profile) { + case H264Profile::Baseline: + // libopenh264 only does constrained baseline. + // There's a bug in the ffmpeg -> openh264 interface though: + // ffmpeg expects CONSTRAINED_BASELINE from the application and + // passes that through, but libopenh264 only allows BASELINE. + // Until that bug is fixed there'll always be a warning that the + // profile is not supported (https://github.com/cisco/openh264/issues/3613) + m_avCodecContext->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; + break; + case H264Profile::Main: + m_avCodecContext->profile = FF_PROFILE_H264_MAIN; + break; + case H264Profile::High: + m_avCodecContext->profile = FF_PROFILE_H264_HIGH; + break; + } + + AVDictionary *options = nullptr; + av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0); + applyEncodingPreference(options); + + if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) { + qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result); + return false; + } + + return true; +} + +int LibOpenH264Encoder::percentageToAbsoluteQuality(const std::optional<quint8> &quality) +{ + if (!quality) { + return -1; + } + + // 1-51 (incl.), lower is better + return 51 - (m_quality.value() / 100.0) * 50; +} + +void LibOpenH264Encoder::applyEncodingPreference(AVDictionary *options) +{ + SoftwareEncoder::applyEncodingPreference(options); + // Disable motion estimation, not great while dragging windows but speeds up encoding by an order of magnitude + av_dict_set(&options, "flags", "+mv4", 0); + // Disable in-loop filtering + av_dict_set_int(&options, "loopfilter", 0, 0); +} diff --git a/src/libopenh264encoder_p.h b/src/libopenh264encoder_p.h new file mode 100644 index 0000000..fdacf14 --- /dev/null +++ b/src/libopenh264encoder_p.h @@ -0,0 +1,28 @@ +/* + SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleix...@kde.org> + SPDX-FileCopyrightText: 2023 Marco Martin <m...@kde.org> + SPDX-FileCopyrightText: 2023 Arjen Hiemstra <ahiems...@heimr.nl> + SPDX-FileCopyrightText: 2024 Fabian Vogt <fab...@ritter-vogt.de> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "encoder_p.h" + +/** + * A software encoder that uses ffmpeg + libopenh264 to encode to H.264. + */ +class LibOpenH264Encoder : public SoftwareEncoder +{ +public: + LibOpenH264Encoder(H264Profile profile, PipeWireProduce *produce); + + bool initialize(const QSize &size) override; + +protected: + int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override; + void applyEncodingPreference(AVDictionary *options) override; + +private: + H264Profile m_profile = H264Profile::Main; +}; diff --git a/src/pipewirebaseencodedstream.cpp b/src/pipewirebaseencodedstream.cpp index 553c334..814d8d9 100644 --- a/src/pipewirebaseencodedstream.cpp +++ b/src/pipewirebaseencodedstream.cpp @@ -225,7 +225,7 @@ QList<PipeWireBaseEncodedStream::Encoder> PipeWireBaseEncodedStream::suggestedEn && avcodec_find_encoder_by_name("h264_vaapi")) { return false; } else { - return !avcodec_find_encoder_by_name("libx264"); + return !(avcodec_find_encoder_by_name("libx264") || avcodec_find_encoder_by_name("libopenh264")); } default: return true; diff --git a/src/pipewireproduce.cpp b/src/pipewireproduce.cpp index 416bcd3..52594e6 100644 --- a/src/pipewireproduce.cpp +++ b/src/pipewireproduce.cpp @@ -16,6 +16,7 @@ #include <qstringliteral.h> #include "h264vaapiencoder_p.h" +#include "libopenh264encoder_p.h" #include "libvpxencoder_p.h" #include "libvpxvp9encoder_p.h" #include "libx264encoder_p.h" @@ -295,6 +296,16 @@ std::unique_ptr<Encoder> PipeWireProduce::makeEncoder() return softwareEncoder; } } + + // Try libopenh264 last, it's slower and has less features. + if (forcedEncoder.isNull() || forcedEncoder == u"libopenh264") { + auto softwareEncoder = std::make_unique<LibOpenH264Encoder>(profile, this); + softwareEncoder->setQuality(m_quality); + softwareEncoder->setEncodingPreference(m_encodingPreference); + if (softwareEncoder->initialize(size)) { + return softwareEncoder; + } + } break; } case PipeWireBaseEncodedStream::VP8: { -- 2.45.2 ++++++ kpipewire-6.1.2.tar.xz -> kpipewire-6.1.3.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kpipewire-6.1.2/CMakeLists.txt new/kpipewire-6.1.3/CMakeLists.txt --- old/kpipewire-6.1.2/CMakeLists.txt 2024-07-02 11:12:47.000000000 +0200 +++ new/kpipewire-6.1.3/CMakeLists.txt 2024-07-16 13:08:23.000000000 +0200 @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(KPipewire VERSION "6.1.2") +project(KPipewire VERSION "6.1.3") set(KF6_MIN_VERSION "6.2.0") find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kpipewire-6.1.2/src/pipewireproduce.cpp new/kpipewire-6.1.3/src/pipewireproduce.cpp --- old/kpipewire-6.1.2/src/pipewireproduce.cpp 2024-07-02 11:12:47.000000000 +0200 +++ new/kpipewire-6.1.3/src/pipewireproduce.cpp 2024-07-16 13:08:23.000000000 +0200 @@ -150,13 +150,44 @@ void PipeWireProduce::deactivate() { m_deactivated = true; + + auto streamState = m_stream->state(); + m_stream->setActive(false); - if (!m_encoder) { - cleanup(); - QThread::currentThread()->quit(); + + // If we have not been initialized properly before, ensure we still run any + // cleanup code and exit the thread, otherwise we risk applications not closing + // properly. + if (!m_encoder || streamState != PW_STREAM_STATE_STREAMING) { + QMetaObject::invokeMethod(this, &PipeWireProduce::destroy, Qt::QueuedConnection); } } +void PipeWireProduce::destroy() +{ + // Ensure we cleanup the PipeWireSourceStream while in the same thread we + // created it in. + Q_ASSERT_X(QThread::currentThread() == thread(), "PipeWireProduce", "destroy() called from a different thread than PipeWireProduce's thread"); + + if (m_passthroughThread.joinable()) { + m_passthroughRunning = false; + m_frameReceivedCondition.notify_all(); + m_passthroughThread.join(); + } + + if (m_outputThread.joinable()) { + m_outputRunning = false; + m_frameReceivedCondition.notify_all(); + m_outputThread.join(); + } + + m_stream.reset(); + + qCDebug(PIPEWIRERECORD_LOGGING) << "finished"; + cleanup(); + QThread::currentThread()->quit(); +} + void PipeWireProduce::setQuality(const std::optional<quint8> &quality) { m_quality = quality; @@ -227,21 +258,10 @@ m_encoder->finish(); - if (m_passthroughThread.joinable()) { - m_passthroughRunning = false; - m_frameReceivedCondition.notify_all(); - m_passthroughThread.join(); - } - - if (m_outputThread.joinable()) { - m_outputRunning = false; - m_frameReceivedCondition.notify_all(); - m_outputThread.join(); - } - - qCDebug(PIPEWIRERECORD_LOGGING) << "finished"; - cleanup(); - QThread::currentThread()->quit(); + // We want to clean up the source stream while in the input thread, but we + // need to do so while not handling any PipeWire callback as that risks + // crashing because we're stil executing PipeWire handling code. + QMetaObject::invokeMethod(this, &PipeWireProduce::destroy, Qt::QueuedConnection); } std::unique_ptr<Encoder> PipeWireProduce::makeEncoder() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kpipewire-6.1.2/src/pipewireproduce_p.h new/kpipewire-6.1.3/src/pipewireproduce_p.h --- old/kpipewire-6.1.2/src/pipewireproduce_p.h 2024-07-02 11:12:47.000000000 +0200 +++ new/kpipewire-6.1.3/src/pipewireproduce_p.h 2024-07-16 13:08:23.000000000 +0200 @@ -91,6 +91,8 @@ void deactivate(); + void destroy(); + void setQuality(const std::optional<quint8> &quality); void setEncodingPreference(const PipeWireBaseEncodedStream::EncodingPreference &encodingPreference);