Skip to content

PR #5200 breaks clients that negotiate maxNumReferenceFrames=1 (macOS) #5469

Open

Description

@Optimiza

Is there an existing issue for this?

  • I have searched the existing issues

Is your issue described in the documentation?

  • I have read the documentation

Is your issue present in the latest beta/pre-release?

This issue is present in the latest pre-release

Describe the Bug

After updating to the pre-release, the client does receive twice the frames per second, but the image redraws and then reverts completely to the previous background. I had to go back to the stable version, which only delivers half of the captured frames. The pre-release says this is fixed, but since the screen does not refresh correctly, it is unusable on my system.

Expected Behavior

I expected the pre-release to deliver both things at once: the full 60 fps that #5200 restores, and a picture that stays on screen. Instead the fix trades one problem for a worse one on my client. Going back to the pre-#5200 build gives me a correct picture again, but only half the frames.

Additional Context

Client log, one line per frame (3585/minute at 60 fps):

[h264 @ 0x...] number of reference frames (1+1) exceeds max
(1; probably corrupt input), discarding one

Host telemetry on the pre-#5200 build (VCPRateControlSession):

input_fps=60.00 enc_fps=60.00 tx_fps=30.00 idr_fps=30.00
bit_rate 18190023/10988000 enc_time=2.96 ms

So on the old build the encoder emits half the captured frames, all of them
IDR, and overshoots its bitrate target by 1.66x (18.19 Mbps used against an
11.0 Mbps target). Because every frame is intra-coded, an IDLE desktop costs
the same as full motion: 23.6 Mbps measured at the client, ~10 GB/hour.

It also costs client-side diagnostics: 99.3% of the Moonlight log is
"Network dropped 1 frame" (164691 of 165775 lines), which fills the 10 MB
log cap in about 100 minutes of streaming.

Reproduced twice on the same host, checking which binary was actually running
each time with the "txt" line of lsof -p .

There is no configuration workaround: the binary exposes only vt_coder,
vt_realtime and vt_software, and the reference-frame restriction is internal
(REF_FRAMES_RESTRICT), driven by what the client negotiates.

Host Operating System

macOS

Operating System Version

macOS 26.5.2 (build 25F84). Affected: sunshine-beta 2026.726.710 (post-#5200). Working: sunshine 2026.516.143833 (pre-#5200)

Architecture

arm64/aarch64

Package

macOS - Homebrew

GPU Type

Apple Silicon

GPU Model

Apple M4

GPU Driver/Mesa Version

n/a (VideoToolbox, macOS 26.5.2 build 25F84)

Capture Method

AVCaptureScreen (macOS)

Apps

Log output

Online logs

No response

Activity

  1. Optimiza commented on Aug 3, 2026

    @Optimiza
    Author

    Some details I left out of the original report.

    The client, which is the side that triggers this:

    • Moonlight on macOS 14.8.1, iMac Late 2015, Intel HD 6000 (Broadwell)
    • H.264 hardware decode via VideoToolbox
    • It negotiates maxNumReferenceFrames: 1
    • It cannot hardware-decode HEVC, so H.264 is not a preference here

    Which build each block in the report came from, since they are different:

    And the contrast that matters: on the post-#5200 build the host side is
    correct. The same telemetry reads idr_fps=0 with 60/60/60 fps and
    enc_time around 5.6 ms. So #5200 does fix the encoder. The problem is only
    that the resulting stream references more frames than this client negotiated,
    and its decoder cannot follow it.

  2. ReenigneArcher commented on Aug 7, 2026

    @ReenigneArcher
    Member
  3. Optimiza commented on Aug 22, 2026

    @Optimiza
    Author

    Looking at the diff for #5200, it drops {"max_ref_frames"s, 1} from the h264_videotoolbox common options unconditionally. Making that conditional on what the client negotiated would cover both cases: when config.numRefFrames > 1, omit max_ref_frames, which is the current #5200 behaviour and gives the full frame rate with no forced IDR; when config.numRefFrames == 1, keep max_ref_frames=1, which is the pre-#5200 behaviour and the only stream this client's decoder can follow. The value is already available at that point, since the generic path uses config.numRefFrames for ctx->refs.

    One data point that may be relevant to the unresolved review comment on that diff, which argues the all-IDR behaviour should persist through ctx->refs = config.numRefFrames even after the AVOption is gone: on my host it does not. My client negotiates maxNumReferenceFrames=1, and the post-#5200 build reports idr_fps=0 with 60/60/60 fps and enc_time around 5.6 ms. So the encoder side is genuinely fixed here, and what remains is only that the stream references more frames than the client asked for.

    Happy to test a build. Intel Broadwell iMac on H.264, easy to reproduce.

  4. martona commented on Sep 23, 2026

    @martona
    Contributor

    Possible root cause found, and I think this issue, the freeze in #5511's test report, and probably #5697 are all the same thing.

    VT's low-latency H.264 sessions use long-term reference pictures by default. Parsing the elementary stream from a current master host (AVFoundation capture, ffmpeg h264_videotoolbox): the IDR is marked as a long-term reference and the P-frames that follow carry reference-marking operations; the SPS advertises 5-15 reference frames. An x264 stream from the same host has none of that and a single short-term reference. The "(1+1) exceeds max (1)" log line is a decoder seeing that second reference.

    #5200 exposed it because with max_ref_frames=1, VT produced all-IDR output, so there were never any P-frames to carry long-term references. Dropping it fixed the frame rate and let the references through. Making max_ref_frames conditional on the negotiated count would bring back the all-IDR stream for your client and wouldn't help clients that negotiate more than one reference but whose hardware decoders mishandle long-term references anyway; my NVIDIA-based Moonlight clients on Windows show the same freeze/garble with hardware decode and are fine with software decode. The stream itself is valid: the dumped file plays in mpv and VLC and decodes cleanly in libavcodec, both software and with the VideoToolbox hwaccel.

    I tested VT session settings in a standalone harness that mirrors Sunshine's session config:

    H.264 session setting Long-term references in output
    Current config yes
    + kVTCompressionPropertyKey_EnableLTR = false none
    ReferenceBufferCount = 2 yes
    Main profile instead of High yes
    Low-latency rate control disabled none, but loses low-latency RC

    EnableLTR=false is a public key (macOS 12+), low-latency rate control stays on, and the stream comes out with plain short-term references. I verified it end to end on a native VideoToolbox encoder I have on a separate branch: same host, same NVIDIA clients, H.264 1080p120 with hardware decode now renders correctly.

    For ffmpeg, there is no option to set it. Since build-deps already carries ffmpeg patches, the smallest fix for master is a patch to videotoolboxenc.c that sets EnableLTR=false on H.264 sessions. I can put that patch together if there's interest.

    @Optimiza, when you have the Intel iMac on Thursday: I expect this to satisfy a 1-reference client too, since the extra reference is exactly what disappears. https://github.com/martona/Sunshine/tree/macos-latency is my branch that combines SCKit capture and the native VT encoder, with this fix applied.

  5. Optimiza commented on Sep 24, 2026

    @Optimiza
    Author

    Results from a Windows NVIDIA client that reproduces the freeze, run as an A/B with controls. They support the long-term reference explanation.

    Setup. Host: Mac mini M4, macOS 26.7, two Sunshine instances on the same machine: the Homebrew release (2026.914, commit 63d35f7, ffmpeg h264_videotoolbox) and your macos-latency branch built at 04483743. Client: Windows desktop with an RTX 4090, Moonlight, codec forced to H.264. Every encoder below is quoted from the host's own log for that session, not inferred from the configuration.

    # Host Encoder (host log) Client decode Stream Result
    1 release h264_videotoolbox hardware 1080p60 freezes within the first minute
    2 macos-latency, encoder = videotoolbox_native h264_vt [videotoolbox_native] hardware 1080p60 clean
    3 macos-latency, encoder = videotoolbox h264_videotoolbox hardware 1080p60 freezes
    4 release h264_videotoolbox software 1080p60 clean
    1b release h264_videotoolbox hardware 1080p120 freezes
    2b macos-latency, native h264_vt [videotoolbox_native] hardware 1080p120 clean

    What each comparison isolates:

    • 1 vs 2: the client reproduces the freeze, and your branch does not freeze it.
    • 2 vs 3: same binary, same SCKit capture, same resolution, same client; only the encoder changes. So the capture path is not what clears it; the native encoder with EnableLTR = false is.
    • 1 vs 4: the same stream that freezes on hardware decoding plays cleanly on software decoding, which puts the failure in the client's hardware decode path.
    • 1b and 2b: the same result at your 1080p120 setting.

    Limits, so the table is read for what it is: freezes showed within the first minute of each failing run, and the clean runs were watched with video and window movement for one and a half to three minutes each, not for long sessions; the verdicts are visual; and one Windows client with one GPU was tested. The release does not log the capture resolution; its runs are tied to the client settings by the requested bitrate, identical in runs 1 and 4 and higher for 1b, and by the frame rate the host derives from the request (below).

    Host log lines for each run

    Run 1, release, hardware decode:

    [2026-09-24 06:17:05.174]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-24 06:17:05.274]: Info: CLIENT CONNECTED
    [2026-09-24 06:17:05.277]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-24 06:17:05.277]: Info: Streaming bitrate is 14988000
    [2026-09-24 06:17:05.282]: Info: Minimum FPS target set to ~30fps (33.3333ms)
    [2026-09-24 06:18:46.029]: Info: CLIENT DISCONNECTED
    

    Run 2, macos-latency, native encoder:

    [2026-09-24 06:20:54.032]: Info: Found H.264 encoder: h264_vt [videotoolbox_native]
    [2026-09-24 06:20:54.113]: Info: CLIENT CONNECTED
    [2026-09-24 06:20:54.169]: Info: Creating encoder [h264_vt]
    [2026-09-24 06:20:54.219]: Info: SCCapture stream configured: 1920x1080 @ 60 fps
    [2026-09-24 06:23:42.483]: Info: CLIENT DISCONNECTED
    

    Run 3, macos-latency, ffmpeg encoder (copied during the run; the bench's log is rewritten at each start and this session's file is gone):

    [2026-09-24 06:18:51.891]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-24 06:19:35.813]: Info: CLIENT CONNECTED
    [2026-09-24 06:19:35.875]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-24 06:19:35.918]: Info: SCCapture stream configured: 1920x1080 @ 60 fps
    [2026-09-24 06:19:36.015]: Error: Encoder did not produce IDR frame when requested!
    

    Run 4, release, software decode (the IDR line then repeats continuously until the client leaves):

    [2026-09-24 06:24:51.868]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-24 06:24:51.970]: Info: CLIENT CONNECTED
    [2026-09-24 06:24:51.972]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-24 06:24:51.972]: Info: Streaming bitrate is 14988000
    [2026-09-24 06:24:51.977]: Info: Minimum FPS target set to ~30fps (33.3333ms)
    [2026-09-24 06:24:52.665]: Error: Encoder did not produce IDR frame when requested!
    [2026-09-24 06:24:52.714]: Error: Encoder did not produce IDR frame when requested!
    [2026-09-24 06:26:18.199]: Info: CLIENT DISCONNECTED
    

    Run 1b, release, hardware decode, 120 fps requested:

    [2026-09-24 06:26:54.131]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-24 06:26:54.234]: Info: CLIENT CONNECTED
    [2026-09-24 06:26:54.237]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-24 06:26:54.237]: Info: Streaming bitrate is 21388000
    [2026-09-24 06:26:54.242]: Info: Minimum FPS target set to ~60fps (16.6667ms)
    [2026-09-24 06:27:21.383]: Info: CLIENT DISCONNECTED
    

    Run 2b, macos-latency, native encoder, 120 fps:

    [2026-09-24 06:27:32.800]: Info: Found H.264 encoder: h264_vt [videotoolbox_native]
    [2026-09-24 06:27:32.906]: Info: CLIENT CONNECTED
    [2026-09-24 06:27:32.962]: Info: Creating encoder [h264_vt]
    [2026-09-24 06:27:33.009]: Info: SCCapture stream configured: 1920x1080 @ 120 fps
    [2026-09-24 06:29:48.863]: Info: CLIENT DISCONNECTED
    

    Versions from the same logs: Sunshine version: 2026.914.233613 commit: 63d35f702ee9e362e43263742981836ec0710384 (release) and Sunshine version: 0.0.0-04483743 (bench).

    One host-side detail that looked like evidence and is not: Encoder did not produce IDR frame when requested! does not track the freeze here. It was absent in the frozen run 1 and logged continuously in the clean run 4, so it follows the client asking for recovery frames, which it does constantly on software decoding.

    Our understanding, for you to correct: VT's low-latency H.264 sessions emit long-term reference pictures that some hardware decoders mishandle; #5200 exposed it by dropping max_ref_frames=1, which had kept VT on all-IDR streams; the fix for master is kVTCompressionPropertyKey_EnableLTR = false, which ffmpeg does not expose, so it needs a patch to videotoolboxenc.c in build-deps; and it is out of scope for #5511.

    Still to come from our side: the Intel iMac (HD 6000, one negotiated reference frame) that first showed the freeze in the #5511 report, the client closest to #5469. The same pairs will be run there, the release on software and hardware decoding and macos-latency on hardware decoding, and reported here.

  6. Optimiza commented on Sep 25, 2026

    @Optimiza
    Author

    Results from the Intel iMac, the client this issue was filed for. Your expectation holds: the 1-reference client is clean on the native encoder with EnableLTR = false, and freezes on everything that still emits long-term references.

    Setup. Host: the same Mac mini M4 and the same two builds as the Alienware results above, the Homebrew release (2026.914, commit 63d35f7) and macos-latency at 04483743, still the branch head. Client: iMac Late 2015, Intel HD 6000, macOS 14.8.1, Moonlight over WAN, codec forced to H.264, 1080p60, default bitrate. Every encoder below is the Creating encoder line after CLIENT CONNECTED in the host's log for that session.

    # Host Encoder (host log) Client decode Result
    0 release h264_videotoolbox hardware freezes on the first frame
    0b release h264_videotoolbox software clean
    1 macos-latency, encoder = videotoolbox h264_videotoolbox Automatic (hardware) freezes on the first frame
    1-sw macos-latency, encoder = videotoolbox h264_videotoolbox software clean
    2 macos-latency, encoder = videotoolbox_native h264_vt [videotoolbox_native] Automatic (hardware) clean, cursor tracking fine
    • 1 vs 2: same binary, same SCKit capture, same client and settings; only the encoder changes, and only the one without long-term references plays.
    • 0 vs 0b, 1 vs 1-sw: the stream that freezes on hardware decoding plays cleanly on software decoding, so on this client too the failure is in the hardware decode path, not in the stream.

    Limits: the freezes were immediate, so the contrast is sharp, but the clean runs were short (about 1.5 minutes for run 2, 35 seconds for 1-sw, almost 4 minutes for 0b); the verdicts are visual; and I did not collect Moonlight's own log this time, so I cannot show the (1+1) exceeds max line going away, only the freeze. Runs 1 and 2 used Moonlight's Automatic decoder, the same in both; run 1 freezing is what says it picked hardware.

    Host log lines for each run

    Run 0, release, hardware decode:

    [2026-09-25 06:59:07.061]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-25 06:59:07.564]: Info: CLIENT CONNECTED
    [2026-09-25 06:59:07.569]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-25 06:59:07.569]: Info: Streaming bitrate is 14988000
    [2026-09-25 06:59:07.575]: Info: Minimum FPS target set to ~30fps (33.3333ms)
    [2026-09-25 06:59:59.746]: Info: CLIENT DISCONNECTED
    

    Run 0b, release, software decode:

    [2026-09-25 07:01:25.756]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-25 07:01:26.210]: Info: CLIENT CONNECTED
    [2026-09-25 07:01:26.214]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-25 07:01:26.214]: Info: Streaming bitrate is 14988000
    [2026-09-25 07:01:26.218]: Info: Minimum FPS target set to ~30fps (33.3333ms)
    [2026-09-25 07:05:15.053]: Info: CLIENT DISCONNECTED
    

    Run 1-sw, macos-latency, ffmpeg encoder, software decode:

    [2026-09-25 07:18:24.406]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-25 07:18:24.865]: Info: CLIENT CONNECTED
    [2026-09-25 07:18:24.923]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-25 07:18:24.981]: Info: SCCapture stream configured: 1920x1080 @ 60 fps
    [2026-09-25 07:18:59.067]: Info: CLIENT DISCONNECTED
    

    Run 1, macos-latency, ffmpeg encoder, hardware decode:

    [2026-09-25 07:21:33.907]: Info: Found H.264 encoder: h264_videotoolbox [videotoolbox]
    [2026-09-25 07:21:34.473]: Info: CLIENT CONNECTED
    [2026-09-25 07:21:34.528]: Info: Creating encoder [h264_videotoolbox]
    [2026-09-25 07:21:34.545]: Info: SCCapture stream configured: 1920x1080 @ 60 fps
    [2026-09-25 07:23:12.154]: Info: CLIENT DISCONNECTED
    

    Run 2, macos-latency, native encoder, hardware decode:

    [2026-09-25 07:24:29.124]: Info: Found H.264 encoder: h264_vt [videotoolbox_native]
    [2026-09-25 07:24:29.636]: Info: CLIENT CONNECTED
    [2026-09-25 07:24:29.694]: Info: Creating encoder [h264_vt]
    [2026-09-25 07:24:29.715]: Info: SCCapture stream configured: 1920x1080 @ 60 fps
    [2026-09-25 07:25:50.143]: Info: CLIENT DISCONNECTED
    

    So both clients that reproduce, an Intel iGPU negotiating one reference frame and an RTX 4090, are fixed by the same change. From my side that supports the videotoolboxenc.c patch for master you offered.

    A note on how this is written: I run these tests automated with an AI assistant, and I use it to write in English and to carry the log lines over without copying them by hand. The runs, the verdicts and the decision to post are mine.

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

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