Skip to content

Linux export: libopenh264 ignores the requested bitrate entirely — 47x over target on heavy content, 4.5x under on light #572

Description

@EtienneLescot

libopenh264 in the vendored ffmpeg build does not rate-control at all. It is not that the
requested bitrate is approximate — it has no effect worth measuring, in either direction.

Found while benchmarking the Linux export (#544, #552). It is not specific to that work: every
Linux export goes through this encoder, so the quality of every Linux export is whatever the
encoder feels like, and the setting we expose does not reach it.

Reproduction

120 frames of 1280×720, deliberately incompressible content (so the encoder cannot come in
under target by accident), encoded against the vendored libavcodec with nothing but the
bitrate knobs changed:

requested bit_rate rc_max_rate rc_mode produced
2 Mbps default (quality) 93.5 Mbps
8 Mbps default (quality) 97.5 Mbps
2 Mbps bitrate 94.0 Mbps
8 Mbps bitrate 97.7 Mbps
2 Mbps 2 Mbps bitrate 94.0 Mbps
8 Mbps 8 Mbps bitrate 97.7 Mbps

Asked for 2 Mbps with an explicit 2 Mbps ceiling, it emits 94 Mbps — 47× over. Quadrupling
the request moves the output by 4%.

Neither rc_mode nor rc_max_rate changes anything.

What this looks like in a real export

On screen-recording content the symptom inverts, which is why it went unnoticed: a mostly
static screen is very compressible, so the encoder undershoots instead.

requested produced
a 60 s 1080p60 export 8 Mbps 1.75 Mbps
the same, requested 12 12 Mbps 0.72 Mbps
the same content via h264_vaapi 8 Mbps 6.95 Mbps

So the bitrate we compute from the output resolution — (w * h * 8 Mbps) / (1920 * 1080),
pipeline_linux.rs — never reaches the encoder. Export quality does not scale with resolution,
and raising the setting does not raise quality. On heavy content the same defect produces a
file many times larger than asked for.

h264_vaapi in the same build honours the request, which is what makes this look like a
libopenh264-specific defect rather than a wiring mistake on our side.

What is ruled out

  • Not the option plumbing. av_opt_set(priv_data, "rc_mode", "bitrate") returns 0, and the
    value is still 1 when read back after avcodec_open2. The encoder's own option table
    lists rc_mode with quality as default and bitrate/buffer/timestamp alongside.
  • Not bit_rate being clobbered at open. It reads back as 8000000 after avcodec_open2.
  • Not a ceiling. Setting rc_max_rate equal to the target changes nothing.
  • Not content-dependent. The table above is incompressible input, where a working rate
    control would have the most obvious job to do.

Where I would look

  1. Whether the wrapper forwards the target into OpenH264's SEncParamExt
    iTargetBitrate and sSpatialLayers[0].iSpatialBitrate — or leaves them at defaults.
  2. Whether the bundled OpenH264 is built without its rate-control module, in which case the
    setting has nowhere to land regardless of what the wrapper does.

I could not fix this from application code, which is why this is an issue rather than a PR: no
combination of the options ffmpeg exposes for this encoder had any effect.

Why it is worth fixing rather than working around

Beyond the quality problem, it makes the software encoder impossible to compare against
anything. Benchmarking it against h264_vaapi required pinning VAAPI to the bitrate
libopenh264 happens to emit, otherwise the comparison reports a quality difference as a speed
difference. Any future encoder work runs into the same wall.

Measured on AMD Ryzen 5 7520U, Ubuntu 24.04, against
crates/thirdparty/ffmpeg-linux64-lgpl-shared. The probe is ~40 lines of C linked against the
vendored libraries; happy to attach it if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions