[PATCH] Change Encoder::applyEncodingPreference() to buildEncodingOptions()
authorArjen Hiemstra <ahiemstra@heimr.nl>
Thu, 20 Mar 2025 13:26:36 +0000 (14:26 +0100)
committerAurélien COUDERC <coucouf@debian.org>
Tue, 15 Jul 2025 14:45:40 +0000 (16:45 +0200)
As it turns out, FFmpeg does something to the pointers here that causes
the dictionary to never be properly used. This means we were never
applying the encoding options, as avcodec_open() was passed a nullptr
for options.

Fix this by changing the function to return an AVDictionary* instead of
trying to modify a passed-in pointer. This results in a proper dict
being returned, that can then be passed to avcodec_open().

The main result of this is that we now properly apply the encoding
options for VP9 encoding, which means we can now encode VP9 at realtime
speeds instead of it massively lagging behind.

BUG: 488896

Gbp-Pq: Name upstream_52911b70_Change-Encoder-applyEncodingPreference-to-buildEncodingOptions-.patch

15 files changed:
src/encoder.cpp
src/encoder_p.h
src/gifencoder.cpp
src/gifencoder_p.h
src/h264vaapiencoder.cpp
src/h264vaapiencoder_p.h
src/libopenh264encoder.cpp
src/libopenh264encoder_p.h
src/libvpxencoder.cpp
src/libvpxencoder_p.h
src/libvpxvp9encoder.cpp
src/libvpxvp9encoder_p.h
src/libwebpencoder_p.h
src/libx264encoder.cpp
src/libx264encoder_p.h

index 41e6ac58394414ad1648bef62ff29979478d9401..33617850df8ee1d9af14b262f2d76c3051b57754 100644 (file)
@@ -185,8 +185,10 @@ void Encoder::setEncodingPreference(PipeWireBaseEncodedStream::EncodingPreferenc
     m_encodingPreference = preference;
 }
 
-void Encoder::applyEncodingPreference(AVDictionary *options)
+AVDictionary *Encoder::buildEncodingOptions()
 {
+    AVDictionary *options = NULL;
+
     switch (m_encodingPreference) {
     case PipeWireBaseEncodedStream::EncodingPreference::NoPreference:
         av_dict_set(&options, "preset", "veryfast", 0);
@@ -205,6 +207,8 @@ void Encoder::applyEncodingPreference(AVDictionary *options)
         av_dict_set(&options, "preset", "veryfast", 0);
         break;
     }
+
+    return options;
 }
 
 SoftwareEncoder::SoftwareEncoder(PipeWireProduce *produce)
index 1acd25482949f58c1e651618efa2d51e37ec2282..48d3a9ea95b22d729e285a93fef4a36a34b8c387 100644 (file)
@@ -102,6 +102,7 @@ public:
 
 protected:
     virtual int percentageToAbsoluteQuality(const std::optional<quint8> &quality) = 0;
+    virtual AVDictionary *buildEncodingOptions();
 
     PipeWireProduce *m_produce;
 
@@ -114,7 +115,6 @@ protected:
 
     std::optional<quint8> m_quality;
     PipeWireBaseEncodedStream::EncodingPreference m_encodingPreference;
-    virtual void applyEncodingPreference(AVDictionary *options);
 };
 
 /**
index 4e24dea4ba35dae3cc00c28ad285ffc74cc6436b..e2d9802bf46e0bb6f7ebeb7c02fa0d7879688ebb 100644 (file)
@@ -54,9 +54,6 @@ bool GifEncoder::initialize(const QSize &size)
     m_avCodecContext->time_base = AVRational{1, 1000};
 
     AVDictionary *options = nullptr;
-
-    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;
@@ -79,7 +76,3 @@ int GifEncoder::percentageToAbsoluteQuality([[maybe_unused]] const std::optional
 {
     return -1; // Not possible to set quality
 }
-
-void GifEncoder::applyEncodingPreference([[maybe_unused]] AVDictionary *options)
-{
-}
index 9eceb0f2dcd04f853bb6d49951cc0275c4d1bd7a..5e2c056803cc0af179b3a1b3b6e5e5abc7aa016d 100644 (file)
@@ -18,5 +18,4 @@ public:
 
 protected:
     int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override;
-    void applyEncodingPreference(AVDictionary *options) override;
 };
index cf315f3c361dd5f2b48212901da5cefbadef75ff..26e7fba24cc3a284cfbce683ad712918c4c5798f 100644 (file)
@@ -140,9 +140,7 @@ bool H264VAAPIEncoder::initialize(const QSize &size)
         break;
     }
 
-    AVDictionary *options = nullptr;
-    // av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0);
-    applyEncodingPreference(options);
+    AVDictionary *options = buildEncodingOptions();
 
     // Assign the right hardware context for encoding frames.
     // We rely on FFmpeg for creating the VAAPI hardware context as part of
@@ -168,11 +166,13 @@ int H264VAAPIEncoder::percentageToAbsoluteQuality(const std::optional<quint8> &q
     return std::max(1, int(MinQuality - (m_quality.value() / 100.0) * MinQuality));
 }
 
-void H264VAAPIEncoder::applyEncodingPreference(AVDictionary *options)
+AVDictionary *H264VAAPIEncoder::buildEncodingOptions()
 {
-    HardwareEncoder::applyEncodingPreference(options);
+    AVDictionary *options = HardwareEncoder::buildEncodingOptions();
     // 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);
+
+    return options;
 }
index 9a226508883823b7911d31cb8bb703e93c979d80..3b02b33524bdd91fe8d40c31658b31b0fb1c1460 100644 (file)
@@ -20,7 +20,7 @@ public:
 
 protected:
     int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override;
-    void applyEncodingPreference(AVDictionary *options) override;
+    AVDictionary *buildEncodingOptions() override;
 
 private:
     H264Profile m_profile = H264Profile::Main;
index 6d4c6a1852bad0fb3212e3540bafed73b64f63c0..7f374d2587a13ff6dd0ac1e8ce5a59ea37ed1e38 100644 (file)
@@ -74,9 +74,7 @@ bool LibOpenH264Encoder::initialize(const QSize &size)
         break;
     }
 
-    AVDictionary *options = nullptr;
-    av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0);
-    applyEncodingPreference(options);
+    AVDictionary *options = buildEncodingOptions();
 
     if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) {
         qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result);
@@ -96,11 +94,13 @@ int LibOpenH264Encoder::percentageToAbsoluteQuality(const std::optional<quint8>
     return 51 - (m_quality.value() / 100.0) * 50;
 }
 
-void LibOpenH264Encoder::applyEncodingPreference(AVDictionary *options)
+AVDictionary *LibOpenH264Encoder::buildEncodingOptions()
 {
-    SoftwareEncoder::applyEncodingPreference(options);
+    AVDictionary *options = SoftwareEncoder::buildEncodingOptions();
     // 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);
+
+    return options;
 }
index fdacf147288ebbd75bc0f835cd610dee362ad14f..64fb8848e8204f86a68f9feabbd8578dd7a320fc 100644 (file)
@@ -21,7 +21,7 @@ public:
 
 protected:
     int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override;
-    void applyEncodingPreference(AVDictionary *options) override;
+    AVDictionary *buildEncodingOptions() override;
 
 private:
     H264Profile m_profile = H264Profile::Main;
index d0cc9ea72eab7322ac6dde3c82d62c0d929d3fb3..2d696d6e7c288fd61b876a322a5115afa111323b 100644 (file)
@@ -57,8 +57,7 @@ bool LibVpxEncoder::initialize(const QSize &size)
         m_avCodecContext->global_quality = 35;
     }
 
-    AVDictionary *options = nullptr;
-    applyEncodingPreference(options);
+    AVDictionary *options = buildEncodingOptions();
 
     if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) {
         qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result);
@@ -78,9 +77,10 @@ int LibVpxEncoder::percentageToAbsoluteQuality(const std::optional<quint8> &qual
     return std::max(1, int(MinQuality - (m_quality.value() / 100.0) * MinQuality));
 }
 
-void LibVpxEncoder::applyEncodingPreference(AVDictionary *options)
+AVDictionary *LibVpxEncoder::buildEncodingOptions()
 {
-    av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0);
+    AVDictionary *options = SoftwareEncoder::buildEncodingOptions();
+
     av_dict_set(&options, "preset", "veryfast", 0);
     av_dict_set(&options, "tune-content", "screen", 0);
     av_dict_set(&options, "deadline", "realtime", 0);
@@ -91,4 +91,6 @@ void LibVpxEncoder::applyEncodingPreference(AVDictionary *options)
     // Disable in-loop filtering
     av_dict_set(&options, "-flags", "+loop", 0);
     av_dict_set(&options, "crf", "45", 0);
+
+    return options;
 }
index 22fcae87ffc3ca2221c9f90f03d69292b47b634a..419117aebe36f678db1e13cc9b1b8556d2480619 100644 (file)
@@ -20,5 +20,5 @@ public:
 
 protected:
     int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override;
-    void applyEncodingPreference(AVDictionary *options) override;
+    AVDictionary *buildEncodingOptions() override;
 };
index d3ce94b0cd3e60d3d03210bc4d500717ceecf057..0b63015593b39aa69560cea384a0ba182e85165f 100644 (file)
@@ -50,9 +50,7 @@ bool LibVpxVp9Encoder::initialize(const QSize &size)
     m_avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
     m_avCodecContext->time_base = AVRational{1, 1000};
 
-    AVDictionary *options = nullptr;
-
-    applyEncodingPreference(options);
+    AVDictionary *options = buildEncodingOptions();
 
     const auto area = size.width() * size.height();
     // m_avCodecContext->framerate is not set, so we use m_produce->maxFramerate() instead.
@@ -89,8 +87,10 @@ int LibVpxVp9Encoder::percentageToAbsoluteQuality(const std::optional<quint8> &q
     return std::max(1, int(MinQuality - (m_quality.value() / 100.0) * MinQuality));
 }
 
-void LibVpxVp9Encoder::applyEncodingPreference(AVDictionary *options)
+AVDictionary *LibVpxVp9Encoder::buildEncodingOptions()
 {
+    AVDictionary *options = SoftwareEncoder::buildEncodingOptions();
+
     // We're probably capturing a screen
     av_dict_set(&options, "tune-content", "screen", 0);
 
@@ -119,4 +119,6 @@ void LibVpxVp9Encoder::applyEncodingPreference(AVDictionary *options)
     // This should make things faster, but it only seems to consume 100MB more RAM.
     // av_dict_set(&options, "row-mt", "1", 0);
     av_dict_set(&options, "frame-parallel", "1", 0);
+
+    return options;
 }
index c20082730a41ef16df28f65c2cc74cb2cb87c9ae..863fda2d4a849b3f915f8ec21c7ac6010c1b5671 100644 (file)
@@ -21,5 +21,5 @@ public:
 
 protected:
     int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override;
-    void applyEncodingPreference(AVDictionary *options) override;
+    AVDictionary *buildEncodingOptions() override;
 };
index a6d5782ac801a228f8fc43ab14d359d1be32ddf6..e4bfd125470c3142e240b713296e70ab027c3a0d 100644 (file)
@@ -17,5 +17,4 @@ public:
 
 protected:
     int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override;
-    void applyEncodingPreference(AVDictionary *options) override;
 };
index 910116457ba9201facbeba79a8e0d3b5b316a66b..7d4f96b2f1a7dd6e3799a6c72ec55b50dabdb9e2 100644 (file)
@@ -78,9 +78,7 @@ bool LibX264Encoder::initialize(const QSize &size)
         break;
     }
 
-    AVDictionary *options = nullptr;
-    av_dict_set_int(&options, "threads", qMin(16, QThread::idealThreadCount()), 0);
-    applyEncodingPreference(options);
+    AVDictionary *options = buildEncodingOptions();
 
     if (int result = avcodec_open2(m_avCodecContext, codec, &options); result < 0) {
         qCWarning(PIPEWIRERECORD_LOGGING) << "Could not open codec" << av_err2str(result);
@@ -100,11 +98,14 @@ int LibX264Encoder::percentageToAbsoluteQuality(const std::optional<quint8> &qua
     return std::max(1, int(MinQuality - (m_quality.value() / 100.0) * MinQuality));
 }
 
-void LibX264Encoder::applyEncodingPreference(AVDictionary *options)
+AVDictionary *LibX264Encoder::buildEncodingOptions()
 {
-    SoftwareEncoder::applyEncodingPreference(options);
+    AVDictionary *options = SoftwareEncoder::buildEncodingOptions();
+
     // 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);
+
+    return options;
 }
index c8b877c98326209c2a93088fe9931ba95c465629..0a8007586daa0f909f8560a88de8b7c485a1abf5 100644 (file)
@@ -20,7 +20,7 @@ public:
 
 protected:
     int percentageToAbsoluteQuality(const std::optional<quint8> &quality) override;
-    void applyEncodingPreference(AVDictionary *options) override;
+    AVDictionary *buildEncodingOptions() override;
 
 private:
     H264Profile m_profile = H264Profile::Main;