https://bugs.kde.org/show_bug.cgi?id=526795

            Bug ID: 526795
           Summary: libx264/libopenh264 encoders ignore
                    setColorRange(Full): filter graph is built in the
                    constructor, before the range is set
    Classification: Frameworks and Libraries
           Product: KPipeWire
      Version First 6.7.91
       Reported In:
          Platform: Other
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: general
          Assignee: [email protected]
          Reporter: [email protected]
                CC: [email protected]
  Target Milestone: ---

Created attachment 196944
  --> https://bugs.kde.org/attachment.cgi?id=196944&action=edit
patch

**System**
MX Linux 25.3 KDE (based on Debian 13 trixie; the OS selector has no MX entry),
Plasma 6.3.6, kernel 7.2.8-2-liquorix-amd64, NVIDIA GTX 1080 Ti with the
proprietary 580 driver (no VAAPI encoding, so kpipewire uses libx264). The code
path described below is unchanged in current kpipewire master.

**Related:** bug 507015 (grey RDP stream, fixed in 6.6.0 by cb00651a + krdp
a60e7b15). That fix takes effect for the VAAPI encoder but not for the software
encoders, so 507015's symptom remains on machines without VAAPI encoding.

**What happens**
krdp asks for full-range encoding (`VideoStreamSurface.cpp`:
`encodedStream->setColorRange(PipeWireBaseEncodedStream::ColorRange::Full)`,
added for bug 507015). With the software encoders (libx264, libopenh264) the
stream is still encoded in limited range. Clients that display it without range
expansion show grey blacks and washed-out contrast.

**Why (current master)**
- `LibX264Encoder` and `LibOpenH264Encoder` build `m_filterGraphToParse`,
including `scale=out_range=%1`, in their **constructors**.
- `PipeWireProduce` constructs the encoder
(`std::make_unique<LibX264Encoder>(profile, this)`) and only then calls
`setupEncoder()`, which runs `encoder->setColorRange(m_colorRange)` followed by
`initialize()`.
- So when the constructor reads `m_colorRange`, it is still the default
`Limited`, and the filter graph always gets `out_range=limited`. The VAAPI
encoder is unaffected because it builds its graph in `initialize()`.
- In addition, neither software encoder sets `AVCodecContext::color_range`, so
the stream carries no range flag (VUI `video_full_range_flag`).

**Observed**
To be precise about what was tested: the runtime observation below is from
6.3.6, which has no full-range support at all. The claim that current master
still encodes limited range on the software path comes from reading the code
(above), not from running master.

Windows 10 Remote Desktop Connection (mstsc) client, krdp server with libx264
(NVIDIA GPU, no VAAPI encoding). Konsole's background is RGB (35, 38, 39)
locally and measured (46, 48, 51) on the client: 16 + 35 × 219/255 ≈ 46, so
limited-range data was displayed without expansion. With a locally patched
kpipewire that actually encodes full range and tags the stream, the client
shows (35, 37, 40).

**Proposed fix (attached, against master)**
Build the filter graph in `initialize()`, after `setColorRange()` has run, and
tag the stream:

```diff
 bool LibX264Encoder::initialize(const QSize &size)
 {
+    const bool fullRange = m_colorRange ==
PipeWireBaseEncodedStream::ColorRange::Full;
+    m_filterGraphToParse =
u"format=yuv420p,pad=ceil(iw/2)*2:ceil(ih/2)*2,scale=out_range=%1"_s.arg(fullRange
? u"full"_s : u"limited"_s);
     createFilterGraph(size);
 ...
     m_avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
+    m_avCodecContext->color_range = fullRange ? AVCOL_RANGE_JPEG :
AVCOL_RANGE_MPEG;
```
`LibOpenH264Encoder` gets the same change.

The full-range-plus-tag behaviour was tested on 6.3.6, where my backport
differs in form: it reads the range from an environment variable, because 6.3.6
has no `setColorRange()`. The attached master patch is the same idea but hasn't
been compiled or tested.

---
*Disclosure: I wrote this report and the patch with an AI assistant (Claude).
The setup, testing and validation on real hardware are mine.*

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to