Skip to content

feat(amf): add max_au_size option to prevent FEC bypass on Wi-Fi#4926

Open
pollopopo wants to merge 3 commits intoLizardByte:masterfrom
pollopopo:feature/amd-max-au-size
Open

feat(amf): add max_au_size option to prevent FEC bypass on Wi-Fi#4926
pollopopo wants to merge 3 commits intoLizardByte:masterfrom
pollopopo:feature/amd-max-au-size

Conversation

@pollopopo
Copy link
Copy Markdown

@pollopopo pollopopo commented Mar 29, 2026

Description

Adds a new optional AMF encoder parameter amd_max_au_size that exposes the existing FFmpeg/AMF maximum Access Unit size option to Sunshine users. This is not a silver bullet — it is an additional tool in the toolbox for users who have exhausted other options and are still experiencing stream instability, particularly on problematic Wi-Fi connections.

Most users will never need this setting. The existing rate control modes (VBR_LATENCY, CBR) and other options (enforce_hrd, vbaq, preanalysis) should be the first line of troubleshooting. This option is intended as a last resort for difficult network conditions where standard rate control cannot prevent occasional oversized frames from disrupting the stream.

Background

Sunshine uses FEC (Forward Error Correction) to protect video frames against packet loss. FEC has a hard limit: each block supports max 255 data shards, with max 4 blocks per frame. When an encoded frame exceeds this limit (~889 KB at default settings), FEC is disabled for that frame:

// stream.cpp line 1373
if (fec_blocks_needed > MAX_FEC_BLOCKS) {
    BOOST_LOG(warning) << "Skipping FEC for abnormally large encoded frame...";
    fecPercentage = 0;
}

Additionally, large encoded frames produce large packet bursts that can overwhelm Wi-Fi TX queues, causing packet loss even when the link has sufficient average bandwidth.

Standard rate control modes (VBR, CBR) treat bitrate targets as soft constraints and can still produce occasional frame size spikes during high-complexity scenes. For most users on decent networks, these spikes are harmless. But on congested or unreliable Wi-Fi, they can be the difference between a stable and unstable stream.

What this PR does

Exposes the AMF max_au_size encoder option, which sets a hard, hardware-enforced cap on the maximum encoded frame size in bits. This maps to AMF_VIDEO_ENCODER_MAX_AU_SIZE (H.264) and AMF_VIDEO_ENCODER_HEVC_MAX_AU_SIZE (HEVC). AV1 does not support this option and has been excluded.

  • Disabled by default — no change to existing behavior for any user
  • Optional — uses std::optional<int>, completely ignored when not set (FFmpeg default: -1)
  • Zero overhead — the cap is enforced by the AMF hardware encoder, not in software
  • Follows existing patterns — same implementation approach as amd_enforce_hrd, amd_vbaq, etc.

When to use it

This setting is for users who:

  • Are streaming over Wi-Fi and experiencing dropped frames or stuttering
  • Have already tried adjusting bitrate, rate control mode, FEC percentage, and other standard settings
  • Are still seeing instability due to occasional frame size spikes

It is not a replacement for proper rate control configuration and network optimization.

Trade-offs

When the cap is hit, the encoder is forced to reduce quality for that frame, which may cause brief visual quality drops. The value also effectively limits the maximum bitrate regardless of what is configured in the client. Users should set it as high as their network reliably handles — not lower than necessary.

Choosing a value

The value is in bits. It controls the maximum size of any single encoded frame. Start with 800000 (~97 KB per frame, ~46 Mbps at 60fps) and increase until you find the highest value that remains stable on your network.

Value Max frame size Max bitrate at 60fps
800000 ~97 KB ~46 Mbps
1000000 ~122 KB ~58 Mbps
1600000 ~195 KB ~93 Mbps

Screenshot

New "AMF Max AU Size (bits)" field appears in the AMD AMF Encoder > Rate Control Settings section of the Web UI.

Issues Fixed or Closed

Roadmap Issues

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Other changes that don't modify src or test files
  • revert: Reverts a previous commit
  • BREAKING CHANGE: Introduces a breaking change (can be combined with any type above)

Checklist

  • Code follows the style guidelines of this project
  • Code has been self-reviewed
  • Code has been commented, particularly in hard-to-understand areas
  • Code docstring/documentation-blocks for new or existing methods/components have been added or updated
  • Unit tests have been added or updated for any new or modified functionality

AI Usage

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

Add a new optional AMF encoder parameter `amd_max_au_size` that caps
the maximum encoded frame (Access Unit) size in bytes.

When the AMF encoder (especially under CQP rate control) produces
oversized frames, they can exceed the FEC shard limit
(DATA_SHARDS_MAX=255 per block, max 4 blocks). When this happens,
FEC error correction is skipped for those frames entirely
(stream.cpp line 1373). On Wi-Fi networks, these large unprotected
frames are highly susceptible to packet loss, causing visible
dropped frames, stuttering, and stream instability.

By setting `amd_max_au_size` (e.g. 800000 bytes), the hardware
encoder itself enforces the frame size cap, ensuring every frame
stays within FEC protection limits. This works with all rate
control modes (CQP, CBR, VBR) and all three codecs (H.264, HEVC,
AV1). Disabled by default (0 or empty) to preserve existing
behavior.

Tested on AMD RDNA4 (RX 9070 XT) streaming over 5GHz Wi-Fi with
CQP mode. Setting max_au_size to 800000 eliminated FEC bypass
warnings and significantly improved stream stability.
Copilot AI review requested due to automatic review settings March 29, 2026 17:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new AMD AMF encoder configuration option intended to cap maximum encoded frame size to avoid oversized frames bypassing FEC on lossy links (e.g., Wi‑Fi).

Changes:

  • Introduces amd_max_au_size as a new optional AMD video encoder setting in config parsing/defaults.
  • Wires the setting into AMF encoder option dictionaries (H.264/HEVC/AV1) and exposes it in the Web UI.
  • Documents and localizes the new setting.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/config.h Adds std::optional<int> amd_max_au_size to AMD video config struct.
src/config.cpp Initializes default and parses amd_max_au_size from config vars.
src/video.cpp Adds max_au_size option mapping for AMF codec configurations.
src_assets/common/assets/web/config.html Adds default amd_max_au_size field for the UI’s default config.
src_assets/common/assets/web/configs/tabs/encoders/AmdAmfEncoder.vue Adds a numeric input for amd_max_au_size in the AMD AMF tab.
src_assets/common/assets/web/public/assets/locale/en.json Adds label/description strings for the new option.
docs/configuration.md Documents amd_max_au_size and provides an example.

bool_f(vars, "amd_preanalysis", (bool &) video.amd.amd_preanalysis);
bool_f(vars, "amd_vbaq", (bool &) video.amd.amd_vbaq);
bool_f(vars, "amd_enforce_hrd", (bool &) video.amd.amd_enforce_hrd);
int_f(vars, "amd_max_au_size", video.amd.amd_max_au_size);
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parses amd_max_au_size as an optional int but does not enforce non-negative values, and it will treat an explicit "0" as a configured value (so it will still be passed through to FFmpeg). If the intent is “0/empty disables”, consider normalizing values <= 0 to nullopt here (or validating a non-negative range) so downstream option application can skip setting the codec option entirely.

Suggested change
int_f(vars, "amd_max_au_size", video.amd.amd_max_au_size);
int_f(vars, "amd_max_au_size", video.amd.amd_max_au_size);
if (video.amd.amd_max_au_size && *video.amd.amd_max_au_size <= 0) {
video.amd.amd_max_au_size.reset();
}

Copilot uses AI. Check for mistakes.
"amd_enforce_hrd": "AMF Hypothetical Reference Decoder (HRD) Enforcement",
"amd_enforce_hrd_desc": "Increases the constraints on rate control to meet HRD model requirements. This greatly reduces bitrate overflows, but may cause encoding artifacts or reduced quality on certain cards.",
"amd_max_au_size": "AMF Max AU Size",
"amd_max_au_size_desc": "Maximum encoded frame size in bytes (0 or empty = disabled). Caps the largest frame the encoder can produce, preventing oversized frames from exceeding FEC limits. Recommended value: 800000 for Wi-Fi streaming with CQP mode.",
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description says the value is in bytes and recommends 800000, but FFmpeg’s AMF "max_au_size" option for h264_amf/hevc_amf is in bits (and av1_amf doesn’t support this option at all). Please align the UI text with the actual behavior (units, recommended value, and which codecs it applies to) to avoid users configuring an ineffective or failing setting.

Suggested change
"amd_max_au_size_desc": "Maximum encoded frame size in bytes (0 or empty = disabled). Caps the largest frame the encoder can produce, preventing oversized frames from exceeding FEC limits. Recommended value: 800000 for Wi-Fi streaming with CQP mode.",
"amd_max_au_size_desc": "Maximum encoded frame size in bits for H.264/HEVC AMF encoders (0 or empty = disabled). Caps the largest frame the encoder can produce, preventing oversized frames from exceeding FEC limits. Recommended value: 6400000 bits (equivalent to 800000 bytes) for Wi-Fi streaming with CQP mode.",

Copilot uses AI. Check for mistakes.
Comment on lines +2797 to +2815
Maximum encoded frame size (Access Unit) in bytes. When set to a non-zero value, the
encoder will cap the maximum frame size. This prevents oversized frames from exceeding
FEC shard limits (DATA_SHARDS_MAX=255), which otherwise causes FEC to be skipped for
those frames. This is especially important for Wi-Fi streaming where packet loss on
large unprotected frames leads to dropped frames and stuttering.
@note{This option only applies when using amdvce [encoder](#encoder).}
@note{Works with all rate control modes (CQP, CBR, VBR). Most beneficial with CQP
where frame sizes are otherwise unbounded.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}

@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section documents the cap in bytes and implies it applies to all AMF codecs, but in FFmpeg the exposed "max_au_size" AVOption is for h264_amf/hevc_amf (in bits) and av1_amf doesn’t expose an equivalent option. Please update the docs to match the actual supported codecs and units, and clarify the default/disable behavior (e.g., unset vs 0 vs -1).

Suggested change
Maximum encoded frame size (Access Unit) in bytes. When set to a non-zero value, the
encoder will cap the maximum frame size. This prevents oversized frames from exceeding
FEC shard limits (DATA_SHARDS_MAX=255), which otherwise causes FEC to be skipped for
those frames. This is especially important for Wi-Fi streaming where packet loss on
large unprotected frames leads to dropped frames and stuttering.
@note{This option only applies when using amdvce [encoder](#encoder).}
@note{Works with all rate control modes (CQP, CBR, VBR). Most beneficial with CQP
where frame sizes are otherwise unbounded.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
Maximum encoded frame size (Access Unit) in <strong>bits</strong>. When set to a
positive, non-zero value, Sunshine configures the AMF encoder to cap the maximum frame
size. This prevents oversized frames from exceeding FEC shard limits
(DATA_SHARDS_MAX=255), which otherwise causes FEC to be skipped for those frames. This
is especially important for Wi-Fi streaming where packet loss on large unprotected
frames leads to dropped frames and stuttering.
@note{This option is forwarded to FFmpeg's @code{max_au_size} AVOption and only
applies when using the amdvce [encoder](#encoder) with H.264 or HEVC
(@code{h264_amf}/@code{hevc_amf}). It is not available for AV1 (@code{av1_amf}).}
@note{Leave this option unset to use the encoder default (no explicit cap). Non-positive
values are treated as "no limit" and should normally be avoided in favor of leaving the
option unset.}
@note{Works with all rate control modes (CQP, CBR, VBR). Most beneficial with CQP where
frame sizes are otherwise unbounded.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
# unset: do not configure max_au_size, use encoder default (no explicit cap)
# amd_max_au_size is disabled unless you set a positive value
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
# limit access unit size to 800000 bits

Copilot uses AI. Check for mistakes.
src/video.cpp Outdated
{"rc"s, &config::video.amd.amd_rc_av1},
{"usage"s, &config::video.amd.amd_usage_av1},
{"enforce_hrd"s, &config::video.amd.amd_enforce_hrd},
{"max_au_size"s, &config::video.amd.amd_max_au_size},
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FFmpeg’s av1_amf encoder does not expose an AVOption named "max_au_size" (unlike h264_amf/hevc_amf). Adding this option here means the cap won’t work for AV1 and may cause avcodec_open2 to fail with an unknown option when the setting is configured. Consider removing this option from the av1_amf option set (or gating it so it’s only applied for codecs that support it) and updating the UI/docs accordingly.

Suggested change
{"max_au_size"s, &config::video.amd.amd_max_au_size},

Copilot uses AI. Check for mistakes.
{"usage"s, &config::video.amd.amd_usage_hevc},
{"vbaq"s, &config::video.amd.amd_vbaq},
{"enforce_hrd"s, &config::video.amd.amd_enforce_hrd},
{"max_au_size"s, &config::video.amd.amd_max_au_size},
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AMF "max_au_size" AVOption in FFmpeg for h264_amf/hevc_amf is specified in bits (and FFmpeg commonly uses -1 as the disable sentinel). The UI/docs describe this value as bytes and recommend 800000, which will be interpreted as ~100KB if passed through unchanged. Either convert bytes->bits before setting the codec option, or update the config/UI/docs to clearly use bits (and document the disable behavior).

Suggested change
{"max_au_size"s, &config::video.amd.amd_max_au_size},
{"max_au_size"s, []() {
// Config/UI use bytes; FFmpeg AMF option expects bits. Negative disables (maps to -1).
const int value_bytes = config::video.amd.amd_max_au_size;
if (value_bytes < 0) {
return -1;
}
return value_bytes * 8;
}},

Copilot uses AI. Check for mistakes.
{"usage"s, &config::video.amd.amd_usage_h264},
{"vbaq"s, &config::video.amd.amd_vbaq},
{"enforce_hrd"s, &config::video.amd.amd_enforce_hrd},
{"max_au_size"s, &config::video.amd.amd_max_au_size},
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: the FFmpeg "max_au_size" option is in bits. Passing the config value through as-is while documenting bytes will lead to an incorrect cap. Consider converting bytes->bits (and treating 0/empty as unset) before setting this option.

Suggested change
{"max_au_size"s, &config::video.amd.amd_max_au_size},
{"max_au_size"s, []() {
const auto bytes = config::video.amd.amd_max_au_size;
if (bytes <= 0) {
return 0;
}
return bytes * 8;
}},

Copilot uses AI. Check for mistakes.
@ReenigneArcher ReenigneArcher added the ai PR has signs of heavy ai usage (either indicated by user or assumed) label Mar 29, 2026
@ReenigneArcher

This comment was marked as resolved.

Address review feedback:
- max_au_size is in bits per FFmpeg AMF documentation, not bytes
- AV1 encoder (av1_amf) does not support max_au_size, removed
- Updated UI description, docs, and recommended value accordingly
- FFmpeg default is -1 (disabled), aligned placeholder
800000 bits (~97 KB/frame, ~46 Mbps at 60fps) is the correct
recommended value for Wi-Fi streaming, not 6400000.

Added formula for users to calculate based on their bandwidth:
  (target_bitrate_mbps / fps / 1.2) * 1000000
where 1.2 accounts for ~20% FEC overhead.
@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai PR has signs of heavy ai usage (either indicated by user or assumed)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants