Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kpipewire for openSUSE:Factory 
checked in at 2023-04-05 21:26:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kpipewire (Old)
 and      /work/SRC/openSUSE:Factory/.kpipewire.new.19717 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kpipewire"

Wed Apr  5 21:26:51 2023 rev:12 rq:1077311 version:5.27.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/kpipewire/kpipewire.changes      2023-03-17 
17:01:33.568830693 +0100
+++ /work/SRC/openSUSE:Factory/.kpipewire.new.19717/kpipewire.changes   
2023-04-05 21:34:44.906257984 +0200
@@ -1,0 +2,20 @@
+Tue Apr  4 15:05:35 UTC 2023 - Fabian Vogt <[email protected]>
+
+- Update to 5.27.4
+  * New bugfix release
+  * For more details please see:
+  * https://kde.org/announcements/plasma/5/5.27.4
+- Changes since 5.27.3:
+  * source: Handle BGRA buffers gracefully
+  * record: Only create the sws_context when necessary
+  * record: Use a good amount of threads as recommended by QThread
+  * record: Make sure we process all the frames before leaving
+  * record: Improve packet fetching
+  * Use a different API call to make importing DmaBufs work on Nvidia 
(kde#448839)
+  * options to disable motion estimation and in-loop filtering
+  * record: Refactor thread distribution
+  * record: Allocate SwsContext only when necessary
+  * recording: Allocate frames when we render
+  * recording: Extend the encoders API
+
+-------------------------------------------------------------------

Old:
----
  kpipewire-5.27.3.tar.xz
  kpipewire-5.27.3.tar.xz.sig

New:
----
  kpipewire-5.27.4.tar.xz
  kpipewire-5.27.4.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kpipewire.spec ++++++
--- /var/tmp/diff_new_pack.PQhYhO/_old  2023-04-05 21:34:45.398260793 +0200
+++ /var/tmp/diff_new_pack.PQhYhO/_new  2023-04-05 21:34:45.410260861 +0200
@@ -21,7 +21,7 @@
 %{!?_plasma5_bugfix: %global _plasma5_bugfix %{version}}
 %bcond_without released
 Name:           kpipewire
-Version:        5.27.3
+Version:        5.27.4
 Release:        0
 Summary:        PipeWire integration for KDE Plasma
 License:        LGPL-2.0-only AND LGPL-3.0-only

++++++ kpipewire-5.27.3.tar.xz -> kpipewire-5.27.4.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/CMakeLists.txt 
new/kpipewire-5.27.4/CMakeLists.txt
--- old/kpipewire-5.27.3/CMakeLists.txt 2023-03-14 13:23:59.000000000 +0100
+++ new/kpipewire-5.27.4/CMakeLists.txt 2023-04-04 12:38:03.000000000 +0200
@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.16)
 project(KPipewire)
-set(PROJECT_VERSION "5.27.3")
+set(PROJECT_VERSION "5.27.4")
 set(PROJECT_VERSION_MAJOR 5)
 
 set(KF5_MIN_VERSION "5.102.0")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/po/de/kpipewire5.po 
new/kpipewire-5.27.4/po/de/kpipewire5.po
--- old/kpipewire-5.27.3/po/de/kpipewire5.po    2023-03-14 13:23:59.000000000 
+0100
+++ new/kpipewire-5.27.4/po/de/kpipewire5.po    2023-04-04 12:38:03.000000000 
+0200
@@ -16,7 +16,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Lokalize 21.12.2\n"
 
 #: pipewirecore.cpp:86
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/po/zh_CN/kpipewire5.po 
new/kpipewire-5.27.4/po/zh_CN/kpipewire5.po
--- old/kpipewire-5.27.3/po/zh_CN/kpipewire5.po 2023-03-14 13:23:59.000000000 
+0100
+++ new/kpipewire-5.27.4/po/zh_CN/kpipewire5.po 2023-04-04 12:38:03.000000000 
+0200
@@ -3,7 +3,7 @@
 "Project-Id-Version: kdeorg\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n";
 "POT-Creation-Date: 2022-05-31 00:52+0000\n"
-"PO-Revision-Date: 2023-03-11 04:55\n"
+"PO-Revision-Date: 2023-03-27 12:02\n"
 "Last-Translator: \n"
 "Language-Team: Chinese Simplified\n"
 "Language: zh_CN\n"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/CMakeLists.txt 
new/kpipewire-5.27.4/src/CMakeLists.txt
--- old/kpipewire-5.27.3/src/CMakeLists.txt     2023-03-14 13:23:59.000000000 
+0100
+++ new/kpipewire-5.27.4/src/CMakeLists.txt     2023-04-04 12:38:03.000000000 
+0200
@@ -42,6 +42,7 @@
     pipewiresourceitem.cpp
     pipewiresourcestream.cpp
     glhelpers.cpp
+    pwhelpers.cpp
     ${kpipewire_SRCS}
 )
 
@@ -120,6 +121,8 @@
     epoxy::epoxy Libdrm::Libdrm Qt::GuiPrivate
 )
 
+target_compile_definitions(KPipeWireRecord INTERFACE -DKPW_WITH_SUGGESTED=1)
+
 ecm_generate_headers(KPipeWireRecord_HEADERS
   HEADER_NAMES
   PipeWireRecord
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/dmabufhandler.cpp 
new/kpipewire-5.27.4/src/dmabufhandler.cpp
--- old/kpipewire-5.27.3/src/dmabufhandler.cpp  2023-03-14 13:23:59.000000000 
+0100
+++ new/kpipewire-5.27.4/src/dmabufhandler.cpp  2023-04-04 12:38:03.000000000 
+0200
@@ -193,6 +193,7 @@
     GLHelpers::initDebugOutput();
     // create GL 2D texture for framebuffer
     GLuint texture;
+    GLuint fbo;
     glGenTextures(1, &texture);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -201,8 +202,20 @@
     glBindTexture(GL_TEXTURE_2D, texture);
 
     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
-    glGetTexImage(GL_TEXTURE_2D, 0, closestGLType(qimage), GL_UNSIGNED_BYTE, 
qimage.bits());
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                           texture, 0);
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        glDeleteFramebuffers(1, &fbo);
+        glDeleteTextures(1, &texture);
+        eglDestroyImageKHR(m_egl.display, image);
+        return false;
+    }
 
+    glReadPixels(0, 0, frame.dmabuf->width, frame.dmabuf->height, 
closestGLType(qimage), GL_UNSIGNED_BYTE, qimage.bits());
+
+    glDeleteFramebuffers(1, &fbo);
     glDeleteTextures(1, &texture);
     eglDestroyImageKHR(m_egl.display, image);
     return true;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/pipewirerecord.cpp 
new/kpipewire-5.27.4/src/pipewirerecord.cpp
--- old/kpipewire-5.27.3/src/pipewirerecord.cpp 2023-03-14 13:23:59.000000000 
+0100
+++ new/kpipewire-5.27.4/src/pipewirerecord.cpp 2023-04-04 12:38:03.000000000 
+0200
@@ -81,12 +81,34 @@
     AVFrame *m_avFrame;
 };
 
+static AVPixelFormat convertQImageFormatToAVPixelFormat(QImage::Format format)
+{
+    // Listing those handed by SpaToQImageFormat
+    switch (format) {
+    case QImage::Format_BGR888:
+        return AV_PIX_FMT_BGR24;
+    case QImage::Format_RGBX8888:
+    case QImage::Format_RGBA8888_Premultiplied:
+        return AV_PIX_FMT_RGBA;
+    case QImage::Format_RGB32:
+        return AV_PIX_FMT_RGB32;
+    default:
+        qDebug() << "Unexpected pixel format" << format;
+        return AV_PIX_FMT_RGB32;
+    }
+}
+
+Q_DECLARE_METATYPE(std::optional<int>);
+Q_DECLARE_METATYPE(std::optional<std::chrono::nanoseconds>);
+
 PipeWireRecord::PipeWireRecord(QObject *parent)
     : QObject(parent)
     , d(new PipeWireRecordPrivate)
 {
     d->m_encoder = "libvpx";
     av_log_set_level(AV_LOG_DEBUG);
+    qRegisterMetaType<std::optional<int>>();
+    qRegisterMetaType<std::optional<std::chrono::nanoseconds>>();
 }
 
 PipeWireRecord::~PipeWireRecord()
@@ -208,8 +230,8 @@
 
     disconnect(m_stream.data(), &PipeWireSourceStream::frameReceived, this, 
&PipeWireRecordProduce::processFrame);
     if (m_writeThread) {
-        m_writeThread->drain();
-        bool done = QThreadPool::globalInstance()->waitForDone(-1);
+        m_writeThread->quit();
+        bool done = m_writeThread->wait();
         Q_ASSERT(done);
     }
 
@@ -228,6 +250,15 @@
     return QStringLiteral("webm");
 }
 
+QString PipeWireRecord::currentExtension() const
+{
+    static QHash<QByteArray, QString> s_extensions = {
+        {"libx264", QStringLiteral("mp4")},
+        {"libvpx", QStringLiteral("webm")},
+    };
+    return s_extensions.value(d->m_encoder, QStringLiteral("mkv"));
+}
+
 void PipeWireRecordProduce::setupStream()
 {
     qCDebug(PIPEWIRERECORD_LOGGING) << "Setting up stream";
@@ -265,7 +296,7 @@
     m_avCodecContext->width = size.width();
     m_avCodecContext->height = size.height();
     m_avCodecContext->max_b_frames = 1;
-    m_avCodecContext->gop_size = 1;
+    m_avCodecContext->gop_size = 100;
     if (m_codec->pix_fmts && m_codec->pix_fmts[0] > 0) {
         m_avCodecContext->pix_fmt = m_codec->pix_fmts[0];
     } else {
@@ -274,10 +305,17 @@
     m_avCodecContext->time_base = AVRational{1, 1000};
 
     AVDictionary *options = nullptr;
-    av_dict_set_int(&options, "threads", 4, 0);
-    av_dict_set(&options, "preset", "ultrafast", 0);
+    av_dict_set_int(&options, "threads", qMin(16, 
QThread::idealThreadCount()), 0);
+    av_dict_set(&options, "preset", "veryfast", 0);
     av_dict_set(&options, "tune-content", "screen", 0);
-    av_dict_set(&options, "deadline", "good", 0);
+    av_dict_set(&options, "deadline", "realtime", 0);
+    // In theory a lower number should be faster, but the opposite seems to be 
true
+    av_dict_set(&options, "quality", "40", 0);
+    av_dict_set(&options, "cpu-used", "6", 0);
+    // 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(&options, "-flags", "+loop", 0);
 
     int ret = avcodec_open2(m_avCodecContext, m_codec, &options);
     if (ret < 0) {
@@ -285,13 +323,6 @@
         return;
     }
 
-    m_frame.reset(new CustomAVFrame);
-    ret = m_frame->alloc(m_avCodecContext->width, m_avCodecContext->height, 
m_avCodecContext->pix_fmt);
-    if (ret < 0) {
-        qCWarning(PIPEWIRERECORD_LOGGING) << "Could not allocate raw picture 
buffer" << av_err2str(ret);
-        return;
-    }
-
     ret = avio_open(&m_avFormatContext->pb, 
QFile::encodeName(m_output).constData(), AVIO_FLAG_WRITE);
     if (ret < 0) {
         qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open" << m_output << 
av_err2str(ret);
@@ -318,8 +349,8 @@
     }
 
     connect(m_stream.data(), &PipeWireSourceStream::frameReceived, this, 
&PipeWireRecordProduce::processFrame);
-    m_writeThread = new PipeWireRecordWriteThread(&m_bufferNotEmpty, 
m_avFormatContext, m_avCodecContext);
-    QThreadPool::globalInstance()->start(m_writeThread);
+    m_writeThread = new PipeWireRecordWriteThread(this, m_avFormatContext, 
m_avCodecContext);
+    m_writeThread->start();
 }
 
 void PipeWireRecordProduce::processFrame(const PipeWireFrame &frame)
@@ -381,23 +412,6 @@
     render(frame);
 }
 
-static AVPixelFormat convertQImageFormatToAVPixelFormat(QImage::Format format)
-{
-    // Listing those handed by SpaToQImageFormat
-    switch (format) {
-    case QImage::Format_BGR888:
-        return AV_PIX_FMT_BGR24;
-    case QImage::Format_RGBX8888:
-    case QImage::Format_RGBA8888_Premultiplied:
-        return AV_PIX_FMT_RGBA;
-    case QImage::Format_RGB32:
-        return AV_PIX_FMT_RGB32;
-    default:
-        qDebug() << "Unexpected pixel format" << format;
-        return AV_PIX_FMT_RGB32;
-    }
-}
-
 void PipeWireRecordProduce::render(const PipeWireFrame &frame)
 {
     Q_ASSERT(!m_frameWithoutMetadataCursor.isNull());
@@ -409,53 +423,7 @@
         p.drawImage(*m_cursor.position, m_cursor.texture);
     }
 
-    const std::uint8_t *buffers[] = {image.constBits(), nullptr};
-    const int strides[] = {static_cast<int>(image.bytesPerLine()), 0, 0, 0};
-    struct SwsContext *sws_context = nullptr;
-    sws_context = sws_getCachedContext(sws_context,
-                                       image.width(),
-                                       image.height(),
-                                       
convertQImageFormatToAVPixelFormat(image.format()),
-                                       m_avCodecContext->width,
-                                       m_avCodecContext->height,
-                                       m_avCodecContext->pix_fmt,
-                                       0,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr);
-    sws_scale(sws_context, buffers, strides, 0, m_avCodecContext->height, 
m_frame->m_avFrame->data, m_frame->m_avFrame->linesize);
-
-    if (frame.presentationTimestamp.has_value()) {
-        const auto current = 
std::chrono::duration_cast<std::chrono::milliseconds>(*frame.presentationTimestamp).count();
-        if ((*m_avFormatContext->streams)->start_time == 0) {
-            (*m_avFormatContext->streams)->start_time = current;
-        }
-
-        Q_ASSERT((*m_avFormatContext->streams)->start_time <= current);
-        m_frame->m_avFrame->pts = current - 
(*m_avFormatContext->streams)->start_time;
-    } else {
-        m_frame->m_avFrame->pts = AV_NOPTS_VALUE;
-    }
-
-    // Let's add a key frame every 100 frames and also the first frame
-    if (frame.sequential && (*frame.sequential == 0 || (*frame.sequential - 
m_lastKeyFrame) > 100)) {
-        m_frame->m_avFrame->key_frame = 1;
-        m_lastKeyFrame = *frame.sequential;
-    }
-
-    if (m_lastPts > 0 && m_frame->m_avFrame->pts <= m_lastPts) {
-        // Make sure we don't have two frames at the same presentation time
-        m_frame->m_avFrame->pts = m_lastPts + 1;
-    }
-    m_lastPts = m_frame->m_avFrame->pts;
-
-    const int ret = avcodec_send_frame(m_avCodecContext, m_frame->m_avFrame);
-    // qDebug() << "issued" << m_frame->m_avFrame->pts;
-    if (ret < 0) {
-        qCWarning(PIPEWIRERECORD_LOGGING) << "Error sending a frame for 
encoding:" << av_err2str(ret);
-        return;
-    }
-    m_bufferNotEmpty.wakeAll();
+    Q_EMIT producedFrame(image, frame.sequential, frame.presentationTimestamp);
 }
 
 static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
@@ -476,7 +444,25 @@
 
 void PipeWireRecord::setEncoder(const QByteArray &encoder)
 {
+    if (d->m_encoder == encoder) {
+        return;
+    }
     d->m_encoder = encoder;
+    Q_EMIT encoderChanged();
+}
+
+QByteArray PipeWireRecord::encoder() const
+{
+    return d->m_encoder;
+}
+
+QList<QByteArray> PipeWireRecord::suggestedEncoders() const
+{
+    QList<QByteArray> ret = {"libvpx", "libx264"};
+    std::remove_if(ret.begin(), ret.end(), [](const QByteArray &encoder) {
+        return !avcodec_find_encoder_by_name(encoder.constData());
+    });
+    return ret;
 }
 
 QString PipeWireRecord::output() const
@@ -499,39 +485,94 @@
     return d->m_fd.value_or(0);
 }
 
-PipeWireRecordWriteThread::PipeWireRecordWriteThread(QWaitCondition *notEmpty, 
AVFormatContext *avFormatContext, AVCodecContext *avCodecContext)
-    : QRunnable()
+PipeWireRecordWrite::PipeWireRecordWrite(PipeWireRecordProduce *produce, 
AVFormatContext *avFormatContext, AVCodecContext *avCodecContext)
+    : QObject()
     , m_packet(av_packet_alloc())
     , m_avFormatContext(avFormatContext)
     , m_avCodecContext(avCodecContext)
-    , m_bufferNotEmpty(notEmpty)
 {
+    connect(produce, &PipeWireRecordProduce::producedFrame, this, 
&PipeWireRecordWrite::addFrame);
 }
 
-PipeWireRecordWriteThread::~PipeWireRecordWriteThread()
+PipeWireRecordWrite::~PipeWireRecordWrite()
 {
+    int ret = av_write_trailer(m_avFormatContext);
+    if (ret < 0) {
+        qCWarning(PIPEWIRERECORD_LOGGING) << "failed to write trailer" << 
av_err2str(ret);
+    }
     av_packet_free(&m_packet);
 }
 
-void PipeWireRecordWriteThread::run()
+PipeWireRecordWriteThread::PipeWireRecordWriteThread(PipeWireRecordProduce 
*produce, AVFormatContext *avFormatContext, AVCodecContext *avCodecContext)
+    : QThread(produce)
+    , m_produce(produce)
+    , m_avFormatContext(avFormatContext)
+    , m_avCodecContext(avCodecContext)
+{
+}
+
+void PipeWireRecordWrite::addFrame(const QImage &image, std::optional<int> 
sequential, std::optional<std::chrono::nanoseconds> presentationTimestamp)
 {
-    QMutex mutex;
-    int ret = 0;
-    while (true) {
+    if (!sws_context || m_lastReceivedSize != image.size()) {
+        sws_context = sws_getCachedContext(sws_context,
+                                           image.width(),
+                                           image.height(),
+                                           
convertQImageFormatToAVPixelFormat(image.format()),
+                                           m_avCodecContext->width,
+                                           m_avCodecContext->height,
+                                           m_avCodecContext->pix_fmt,
+                                           0,
+                                           nullptr,
+                                           nullptr,
+                                           nullptr);
+    }
+
+    CustomAVFrame avFrame;
+    int ret = avFrame.alloc(m_avCodecContext->width, m_avCodecContext->height, 
m_avCodecContext->pix_fmt);
+    if (ret < 0) {
+        qCWarning(PIPEWIRERECORD_LOGGING) << "Could not allocate raw picture 
buffer" << av_err2str(ret);
+        return;
+    }
+    const std::uint8_t *buffers[] = {image.constBits(), nullptr};
+    const int strides[] = {static_cast<int>(image.bytesPerLine()), 0, 0, 0};
+    sws_scale(sws_context, buffers, strides, 0, m_avCodecContext->height, 
avFrame.m_avFrame->data, avFrame.m_avFrame->linesize);
+
+    if (presentationTimestamp.has_value()) {
+        const auto current = 
std::chrono::duration_cast<std::chrono::milliseconds>(*presentationTimestamp).count();
+        if ((*m_avFormatContext->streams)->start_time == 0) {
+            (*m_avFormatContext->streams)->start_time = current;
+        }
+
+        Q_ASSERT((*m_avFormatContext->streams)->start_time <= current);
+        avFrame.m_avFrame->pts = current - 
(*m_avFormatContext->streams)->start_time;
+    } else {
+        avFrame.m_avFrame->pts = AV_NOPTS_VALUE;
+    }
+
+    // Let's add a key frame every 100 frames and also the first frame
+    if (sequential && (*sequential == 0 || (*sequential - m_lastKeyFrame) > 
100)) {
+        avFrame.m_avFrame->key_frame = 1;
+        m_lastKeyFrame = *sequential;
+    }
+
+    if (m_lastPts > 0 && avFrame.m_avFrame->pts <= m_lastPts) {
+        // Make sure we don't have two frames at the same presentation time
+        avFrame.m_avFrame->pts = m_lastPts + 1;
+    }
+    m_lastPts = avFrame.m_avFrame->pts;
+
+    ret = avcodec_send_frame(m_avCodecContext, avFrame.m_avFrame);
+    if (ret < 0) {
+        qCWarning(PIPEWIRERECORD_LOGGING) << "Error sending a frame for 
encoding:" << av_err2str(ret);
+        return;
+    }
+    for (;;) {
         ret = avcodec_receive_packet(m_avCodecContext, m_packet);
-        if (ret == AVERROR_EOF) {
-            break;
-        } else if (ret == AVERROR(EAGAIN)) {
-            if (m_active) {
-                m_bufferNotEmpty->wait(&mutex);
-            } else {
-                int sent = avcodec_send_frame(m_avCodecContext, nullptr);
-                qCDebug(PIPEWIRERECORD_LOGGING) << "draining" << sent;
+        if (ret < 0) {
+            if (ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) {
+                qCWarning(PIPEWIRERECORD_LOGGING) << "Error encoding a frame: 
" << av_err2str(ret) << ret;
             }
-            continue;
-        } else if (ret < 0) {
-            qCWarning(PIPEWIRERECORD_LOGGING) << "Error encoding a frame: " << 
av_err2str(ret);
-            continue;
+            break;
         }
 
         m_packet->stream_index = (*m_avFormatContext->streams)->index;
@@ -540,17 +581,30 @@
         ret = av_interleaved_write_frame(m_avFormatContext, m_packet);
         if (ret < 0) {
             qCWarning(PIPEWIRERECORD_LOGGING) << "Error while writing output 
packet:" << av_err2str(ret);
-            continue;
         }
-    }
-    ret = av_write_trailer(m_avFormatContext);
-    if (ret < 0) {
-        qCWarning(PIPEWIRERECORD_LOGGING) << "failed to write trailer" << 
av_err2str(ret);
+        av_packet_unref(m_packet);
     }
 }
 
-void PipeWireRecordWriteThread::drain()
+void PipeWireRecordWriteThread::run()
 {
-    m_active = false;
-    m_bufferNotEmpty->wakeAll();
+    PipeWireRecordWrite writer(m_produce, m_avFormatContext, m_avCodecContext);
+    QThread::exec();
+    AVPacket *pkt = av_packet_alloc();
+    avcodec_send_frame(m_avCodecContext, nullptr);
+
+    for (;;) {
+        if (avcodec_receive_packet(m_avCodecContext, pkt) < 0)
+            break;
+
+        pkt->stream_index = (*m_avFormatContext->streams)->index;
+        av_packet_rescale_ts(pkt, m_avCodecContext->time_base, 
(*m_avFormatContext->streams)->time_base);
+        log_packet(m_avFormatContext, pkt);
+        int ret = av_interleaved_write_frame(m_avFormatContext, pkt);
+        if (ret < 0) {
+            qCWarning(PIPEWIRERECORD_LOGGING) << "Error while writing output 
packet:" << av_err2str(ret);
+        }
+        av_packet_unref(pkt);
+    }
+    av_packet_free(&pkt);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/pipewirerecord.h 
new/kpipewire-5.27.4/src/pipewirerecord.h
--- old/kpipewire-5.27.3/src/pipewirerecord.h   2023-03-14 13:23:59.000000000 
+0100
+++ new/kpipewire-5.27.4/src/pipewirerecord.h   2023-04-04 12:38:03.000000000 
+0200
@@ -26,6 +26,8 @@
     Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
     Q_PROPERTY(QString output READ output WRITE setOutput NOTIFY outputChanged)
     Q_PROPERTY(State state READ state NOTIFY stateChanged)
+    Q_PROPERTY(QString extension READ extension NOTIFY encoderChanged)
+    Q_PROPERTY(QByteArray encoder READ encoder WRITE setEncoder NOTIFY 
encoderChanged)
 public:
     PipeWireRecord(QObject *parent = nullptr);
     ~PipeWireRecord() override;
@@ -57,8 +59,13 @@
      * ffmpeg -encoders | grep "^ V"
      */
     void setEncoder(const QByteArray &encoder);
+    QByteArray encoder() const;
 
-    static QString extension();
+    /// Returns the encoders that are tested to work, sorted by preference
+    QList<QByteArray> suggestedEncoders() const;
+
+    QString currentExtension() const;
+    Q_DECL_DEPRECATED static QString extension();
 
 Q_SIGNALS:
     void activeChanged(bool active);
@@ -67,6 +74,7 @@
     void outputChanged(const QString &output);
     void errorFound(const QString &error);
     void stateChanged();
+    void encoderChanged();
 
 private:
     void refresh();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/pipewirerecord_p.h 
new/kpipewire-5.27.4/src/pipewirerecord_p.h
--- old/kpipewire-5.27.3/src/pipewirerecord_p.h 2023-03-14 13:23:59.000000000 
+0100
+++ new/kpipewire-5.27.4/src/pipewirerecord_p.h 2023-04-04 12:38:03.000000000 
+0200
@@ -31,27 +31,45 @@
 struct AVFormatContext;
 struct AVPacket;
 class CustomAVFrame;
+class PipeWireRecordProduce;
 struct gbm_device;
 
-class PipeWireRecordWriteThread : public QRunnable
+class PipeWireRecordWrite : public QObject
 {
 public:
-    PipeWireRecordWriteThread(QWaitCondition *notEmpty, AVFormatContext 
*avFormatContext, AVCodecContext *avCodecContext);
-    ~PipeWireRecordWriteThread();
+    PipeWireRecordWrite(PipeWireRecordProduce *produce, AVFormatContext 
*avFormatContext, AVCodecContext *avCodecContext);
+    ~PipeWireRecordWrite();
 
-    void run() override;
-    void drain();
+    void addFrame(const QImage &image, std::optional<int> sequential, 
std::optional<std::chrono::nanoseconds> presentationTimestamp);
 
 private:
     QAtomicInt m_active = true;
     AVPacket *m_packet;
     AVFormatContext *const m_avFormatContext;
     AVCodecContext *const m_avCodecContext;
-    QWaitCondition *const m_bufferNotEmpty;
+    struct SwsContext *sws_context = nullptr;
+    int64_t m_lastPts = -1;
+    uint m_lastKeyFrame = 0;
+    QSize m_lastReceivedSize;
+};
+
+class PipeWireRecordWriteThread : public QThread
+{
+public:
+    PipeWireRecordWriteThread(PipeWireRecordProduce *produce, AVFormatContext 
*avFormatContext, AVCodecContext *avCodecContext);
+
+    void run() override;
+    void drain();
+
+private:
+    PipeWireRecordProduce *const m_produce;
+    AVFormatContext *const m_avFormatContext;
+    AVCodecContext *const m_avCodecContext;
 };
 
 class PipeWireRecordProduce : public QObject
 {
+    Q_OBJECT
 public:
     PipeWireRecordProduce(const QByteArray &encoder, uint nodeId, uint fd, 
const QString &output);
     ~PipeWireRecordProduce() override;
@@ -61,6 +79,9 @@
         return m_error;
     }
 
+Q_SIGNALS:
+    void producedFrame(const QImage &image, std::optional<int> sequential, 
std::optional<std::chrono::nanoseconds> presentationTimestamp);
+
 private:
     friend class PipeWireRecordProduceThread;
     void setupStream();
@@ -78,11 +99,8 @@
     QString m_error;
 
     PipeWireRecordWriteThread *m_writeThread = nullptr;
-    QWaitCondition m_bufferNotEmpty;
     const QByteArray m_encoder;
 
-    QScopedPointer<CustomAVFrame> m_frame;
-
     struct {
         QImage texture;
         std::optional<QPoint> position;
@@ -91,8 +109,6 @@
     } m_cursor;
     QImage m_frameWithoutMetadataCursor;
     DmaBufHandler m_dmabufHandler;
-    uint m_lastKeyFrame = 0;
-    int64_t m_lastPts = -1;
     QAtomicInt m_deactivated = false;
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/pipewiresourcestream.cpp 
new/kpipewire-5.27.4/src/pipewiresourcestream.cpp
--- old/kpipewire-5.27.3/src/pipewiresourcestream.cpp   2023-03-14 
13:23:59.000000000 +0100
+++ new/kpipewire-5.27.4/src/pipewiresourcestream.cpp   2023-04-04 
12:38:03.000000000 +0200
@@ -10,6 +10,7 @@
 #include "glhelpers.h"
 #include "logging.h"
 #include "pipewirecore.h"
+#include "pwhelpers.h"
 
 #include <libdrm/drm_fourcc.h>
 #include <spa/utils/result.h>
@@ -18,11 +19,10 @@
 #include <unistd.h>
 
 #include <QGuiApplication>
-#include <QLoggingCategory>
 #include <QOpenGLTexture>
 #include <QSocketNotifier>
-#include <QVersionNumber>
 #include <QThread>
+#include <QVersionNumber>
 #include <qpa/qplatformnativeinterface.h>
 
 #include <KLocalizedString>
@@ -95,26 +95,6 @@
     }
 }
 
-QImage::Format SpaToQImageFormat(quint32 format)
-{
-    switch (format) {
-    case SPA_VIDEO_FORMAT_BGRx:
-    case SPA_VIDEO_FORMAT_BGRA:
-        return QImage::Format_RGBA8888_Premultiplied; // TODO: Add BGR to 
QImage
-    case SPA_VIDEO_FORMAT_BGR:
-        return QImage::Format_BGR888;
-    case SPA_VIDEO_FORMAT_RGBx:
-        return QImage::Format_RGBX8888;
-    case SPA_VIDEO_FORMAT_RGB:
-        return QImage::Format_RGB888;
-    case SPA_VIDEO_FORMAT_RGBA:
-        return QImage::Format_RGBA8888_Premultiplied;
-    default:
-        qCWarning(PIPEWIRE_LOGGING) << "unknown spa format" << format;
-        return QImage::Format_RGB32;
-    }
-}
-
 static QHash<spa_video_format, QVector<uint64_t>> 
queryDmaBufModifiers(EGLDisplay display, const QVector<spa_video_format> 
&formats)
 {
     QHash<spa_video_format, QVector<uint64_t>> ret;
@@ -488,7 +468,8 @@
             QImage cursorTexture;
             if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) {
                 const uint8_t *bitmap_data = SPA_MEMBER(bitmap, 
bitmap->offset, uint8_t);
-                cursorTexture = QImage(bitmap_data, bitmap->size.width, 
bitmap->size.height, bitmap->stride, SpaToQImageFormat(bitmap->format));
+                cursorTexture =
+                    PWHelpers::SpaBufferToQImage(bitmap_data, 
bitmap->size.width, bitmap->size.height, bitmap->stride, 
spa_video_format(bitmap->format));
             }
             frame.cursor = {{cursor->position.x, cursor->position.y}, 
{cursor->hotspot.x, cursor->hotspot.y}, cursorTexture};
         } else {
@@ -509,7 +490,8 @@
             qCWarning(PIPEWIRE_LOGGING) << "Failed to mmap the memory: " << 
strerror(errno);
             return;
         }
-        QImage img(map, d->videoFormat.size.width, d->videoFormat.size.height, 
spaBuffer->datas->chunk->stride, SpaToQImageFormat(d->videoFormat.format));
+        QImage img =
+            PWHelpers::SpaBufferToQImage(map, d->videoFormat.size.width, 
d->videoFormat.size.height, spaBuffer->datas->chunk->stride, 
d->videoFormat.format);
         frame.image = img.copy();
 
         munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset);
@@ -533,11 +515,11 @@
         Q_ASSERT(!attribs.planes.isEmpty());
         frame.dmabuf = attribs;
     } else if (spaBuffer->datas->type == SPA_DATA_MemPtr) {
-        frame.image = QImage(static_cast<uint8_t *>(spaBuffer->datas->data),
-                             d->videoFormat.size.width,
-                             d->videoFormat.size.height,
-                             spaBuffer->datas->chunk->stride,
-                             SpaToQImageFormat(d->videoFormat.format));
+        frame.image = PWHelpers::SpaBufferToQImage(static_cast<uint8_t 
*>(spaBuffer->datas->data),
+                                                   d->videoFormat.size.width,
+                                                   d->videoFormat.size.height,
+                                                   
spaBuffer->datas->chunk->stride,
+                                                   d->videoFormat.format);
     } else {
         if (spaBuffer->datas->type == SPA_ID_INVALID)
             qWarning() << "invalid buffer type";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/pwhelpers.cpp 
new/kpipewire-5.27.4/src/pwhelpers.cpp
--- old/kpipewire-5.27.3/src/pwhelpers.cpp      1970-01-01 01:00:00.000000000 
+0100
+++ new/kpipewire-5.27.4/src/pwhelpers.cpp      2023-04-04 12:38:03.000000000 
+0200
@@ -0,0 +1,46 @@
+/*
+    SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <[email protected]>
+
+    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR 
LicenseRef-KDE-Accepted-LGPL
+*/
+
+#include "pwhelpers.h"
+#include "logging.h"
+
+QImage::Format SpaToQImageFormat(quint32 format)
+{
+    switch (format) {
+    case SPA_VIDEO_FORMAT_BGRx:
+    case SPA_VIDEO_FORMAT_BGRA:
+        return QImage::Format_RGBA8888_Premultiplied; // TODO: Add BGR to 
QImage
+    case SPA_VIDEO_FORMAT_BGR:
+        return QImage::Format_BGR888;
+    case SPA_VIDEO_FORMAT_RGBx:
+        return QImage::Format_RGBX8888;
+    case SPA_VIDEO_FORMAT_RGB:
+        return QImage::Format_RGB888;
+    case SPA_VIDEO_FORMAT_RGBA:
+        return QImage::Format_RGBA8888_Premultiplied;
+    default:
+        qCWarning(PIPEWIRE_LOGGING) << "unknown spa format" << format;
+        return QImage::Format_RGB32;
+    }
+}
+
+QImage PWHelpers::SpaBufferToQImage(const uchar *data, int width, int height, 
qsizetype bytesPerLine, spa_video_format format)
+{
+    switch (format) {
+    case SPA_VIDEO_FORMAT_BGRx:
+    case SPA_VIDEO_FORMAT_BGRA: {
+        // This is needed because QImage does not support BGRA
+        // This is obviously a much slower path, it makes sense to avoid it as 
much as possible
+        return QImage(data, width, height, bytesPerLine, 
SpaToQImageFormat(format)).rgbSwapped();
+    }
+    case SPA_VIDEO_FORMAT_BGR:
+    case SPA_VIDEO_FORMAT_RGBx:
+    case SPA_VIDEO_FORMAT_RGB:
+    case SPA_VIDEO_FORMAT_RGBA:
+    default:
+        return QImage(data, width, height, bytesPerLine, 
SpaToQImageFormat(format));
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpipewire-5.27.3/src/pwhelpers.h 
new/kpipewire-5.27.4/src/pwhelpers.h
--- old/kpipewire-5.27.3/src/pwhelpers.h        1970-01-01 01:00:00.000000000 
+0100
+++ new/kpipewire-5.27.4/src/pwhelpers.h        2023-04-04 12:38:03.000000000 
+0200
@@ -0,0 +1,21 @@
+/*
+    SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <[email protected]>
+
+    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR 
LicenseRef-KDE-Accepted-LGPL
+*/
+
+#pragma once
+
+#include "pipewiresourcestream.h"
+#include <QByteArray>
+#include <epoxy/egl.h>
+#include <kpipewire_export.h>
+
+typedef unsigned int GLenum;
+
+namespace PWHelpers
+{
+
+KPIPEWIRE_EXPORT QImage SpaBufferToQImage(const uchar *data, int width, int 
height, qsizetype bytesPerLine, spa_video_format format);
+
+}

Reply via email to