Skip to content

Add periodic intra refreshes to mitigate HEVC errors - #274

Open
razorlikes wants to merge 8 commits into
TheElixZammuto:masterfrom
razorlikes:master
Open

Add periodic intra refreshes to mitigate HEVC errors#274
razorlikes wants to merge 8 commits into
TheElixZammuto:masterfrom
razorlikes:master

Conversation

@razorlikes

@razorlikes razorlikes commented May 10, 2026

Copy link
Copy Markdown

Fix/Workaround for #190.

Requests IDR frames periodically to get rid of the video artifacts that keep accumulating when using HEVC.

Tested on my Series S against Apollo with a 9070 XT.

Screenshot_2026-05-10_06-28-04

Summary by CodeRabbit

  • New Features
    • Added a "Periodic decoder refresh" option in Host Settings with an enable/disable checkbox.
    • Added a slider to configure the decoder refresh interval when enabled.
    • Interval setting is persisted per host and applied automatically to streaming sessions.
    • Decoder now requests periodic keyframes according to the configured interval to improve stream consistency.

Review Change Stack

Copilot AI review requested due to automatic review settings May 10, 2026 13:36
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1950ec09-b2da-4e6c-b5d7-40dc0a49dc66

📥 Commits

Reviewing files that changed from the base of the PR and between efcd345 and e003898.

📒 Files selected for processing (1)
  • Pages/HostSettingsPage.xaml.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • Pages/HostSettingsPage.xaml.cpp

📝 Walkthrough

Walkthrough

Adds a host-configurable IDR interval setting, persisted to state.json, exposed in the Host Settings UI (checkbox + slider), threaded into StreamConfiguration, and used by FFmpegDecoder to request periodic IDR frames based on decoded-frame counting.

Changes

Periodic IDR Frame Request Support

Layer / File(s) Summary
Data Contracts
State/MoonlightHost.h, State/StreamConfiguration.h
Adds IdrInterval bindable property and backing field to MoonlightHost and idrInterval to StreamConfiguration.
Configuration Persistence
State/ApplicationState.cpp
Reads idr_interval from state.json during host load and writes idr_interval when saving hosts.
Decoder Frame Tracking
Streaming/FFmpegDecoder.h, Streaming/FFmpegDecoder.cpp
Adds idrInterval member and m_FramesSinceIDR counter; updates CompleteInitialization signature to accept idrInterval; decoder tracks frames and calls LiRequestIdrFrame() when interval reached.
Stream Initialization Wiring
Pages/AppPage.xaml.cpp, State/MoonlightClient.cpp
Sets StreamConfiguration::idrInterval from host->IdrInterval and passes it into FFmpegDecoder::CompleteInitialization.
Host Settings UI
Pages/HostSettingsPage.xaml, Pages/HostSettingsPage.xaml.cpp, Pages/HostSettingsPage.xaml.h
Adds "Periodic decoder refresh" checkbox and IdrIntervalSlider bound to Host.IdrInterval; OnNavigatedTo initializes controls from saved value; checkbox handlers enable/disable slider and set defaults (min 2/default 10 when enabled; zero/disabled when unchecked).

Sequence Diagram(s)

sequenceDiagram
  participant User as User/UI
  participant Page as HostSettingsPage
  participant Host as MoonlightHost
  participant App as AppPage
  participant Client as MoonlightClient
  participant Decoder as FFmpegDecoder
  User->>Page: Configure periodic refresh (checkbox/slider)
  Page->>Host: Save IdrInterval
  App->>Host: Read host settings on connect
  App->>Client: StartStreaming(streamConfig with idrInterval)
  Client->>Decoder: CompleteInitialization(..., idrInterval)
  loop During decode
    Decoder->>Decoder: count frames
    alt count >= fps * idrInterval
      Decoder->>Client: LiRequestIdrFrame()
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nudge the frames when drift begins to creep,
A checkbox set, a slider watched while streams sleep—
From host to decoder the interval threads through,
Count frames, request refresh, keep keyframes true.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding periodic intra refresh (IDR) requests to mitigate HEVC video corruption errors.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

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 user-configurable “periodic decoder refresh” feature that requests IDR frames at a fixed interval to mitigate HEVC artifact accumulation on Xbox (issue #190).

Changes:

  • Plumbs a new idrInterval setting from host settings UI → stream configuration → FFmpeg decoder initialization.
  • Implements periodic LiRequestIdrFrame() calls based on decoded-frame counting.
  • Persists the new setting in state.json via idr_interval.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Streaming/FFmpegDecoder.h Extends decoder initialization API and stores IDR interval state.
Streaming/FFmpegDecoder.cpp Implements periodic IDR requests during decode based on interval.
State/StreamConfiguration.h Adds idrInterval to stream configuration object.
State/MoonlightHost.h Adds host-level IdrInterval property for settings/UI binding.
State/MoonlightClient.cpp Passes configured interval into decoder initialization before starting connection.
State/ApplicationState.cpp Loads/saves idr_interval in persisted host state JSON.
Pages/HostSettingsPage.xaml.h Adds checkbox event handler declarations for the new setting.
Pages/HostSettingsPage.xaml.cpp Initializes UI state and toggles slider enabled/min/value for interval.
Pages/HostSettingsPage.xaml Adds UI controls (checkbox + slider) to configure the interval.
Pages/AppPage.xaml.cpp Copies host setting into the stream configuration used to start streaming.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Pages/HostSettingsPage.xaml.cpp
Comment thread Streaming/FFmpegDecoder.cpp
Comment thread Streaming/FFmpegDecoder.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Pages/HostSettingsPage.xaml.cpp`:
- Around line 125-132: The page initialization sets
EnableIdrIntervalCheckbox->IsChecked = true which triggers the checkbox Checked
handler and overwrites the saved host->IdrInterval (causing
IdrIntervalSlider->Value to become 10); fix by initializing the slider from
host->IdrInterval and preventing the Checked/Unchecked handler from firing
during navigation—either (a) set IdrIntervalSlider->Value = host->IdrInterval
(and IdrIntervalSlider->IsEnabled = host->IdrInterval > 0) before setting
EnableIdrIntervalCheckbox->IsChecked, or (b) temporarily unsubscribe the
checkbox event handlers (e.g.,
EnableIdrIntervalCheckbox_Checked/EnableIdrIntervalCheckbox_Unchecked) while you
set IsChecked and Value, then re-subscribe them afterwards.

In `@State/MoonlightHost.h`:
- Around line 289-296: The IdrInterval setter currently stores raw input into
the field idrInterval; update it to normalize/clamp inputs so only 0 (disabled)
or values >= 2 (enabled) are persisted: if the incoming value is less than 2,
set idrInterval to 0, otherwise set it to the incoming value; then call
OnPropertyChanged("IdrInterval") only after assigning the (possibly normalized)
value. Reference: property IdrInterval, backing field idrInterval, and
OnPropertyChanged.

In `@Streaming/FFmpegDecoder.cpp`:
- Around line 283-295: The periodic IDR-request logic should only run for HEVC
streams and when fps is a valid positive value; update the block that uses
idrInterval, m_FramesSinceIDR, fps, frame->pict_type and LiRequestIdrFrame() to
first verify the codec is HEVC (e.g., codec id or codec name check) and that fps
> 0 before computing (fps * idrInterval), and only then increment/reset
m_FramesSinceIDR and call LiRequestIdrFrame(); this prevents firing on every
frame when fps is unset or for non-HEVC streams.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf6b8b27-9f8f-4e77-af82-0c61a51ac746

📥 Commits

Reviewing files that changed from the base of the PR and between 6beda9f and d978040.

📒 Files selected for processing (10)
  • Pages/AppPage.xaml.cpp
  • Pages/HostSettingsPage.xaml
  • Pages/HostSettingsPage.xaml.cpp
  • Pages/HostSettingsPage.xaml.h
  • State/ApplicationState.cpp
  • State/MoonlightClient.cpp
  • State/MoonlightHost.h
  • State/StreamConfiguration.h
  • Streaming/FFmpegDecoder.cpp
  • Streaming/FFmpegDecoder.h

Comment thread Pages/HostSettingsPage.xaml.cpp
Comment thread State/MoonlightHost.h
Comment thread Streaming/FFmpegDecoder.cpp
@andygrundman

Copy link
Copy Markdown
Collaborator

The quick menu option Reset Decoder lets the user fix this as needed, this patch wouldn't be any better because there's no way to detect when the issue occurs.

@razorlikes

Copy link
Copy Markdown
Author

Hard disagree, Moonlight on the Xbox is literally unusable for me and probably most other people in #190 without this.

Sure I can reset the decoder from the quick menu but I would have to do that every 2-3 minutes, if not even more often. So unless someone can figure out why this happens, this is the only way I can use Moonlight on my Xbox at all.

I also made it a toggle so it's completely optional.

@andygrundman

Copy link
Copy Markdown
Collaborator

There's something wrong with the AMD encoder that makes this worse. If sending large keyframes on a regular basis is really the only solution, this can live on the server side with an option to run the encoder more like a traditional encoder: regular IDR keyframes, higher latency. There is also some work on an AMF encoder but it's limited to the Vibe* forks at the moment, and also doesn't have working intra-refresh.

BTW I would title this PR, "request periodic IDR frames", this isn't using intra-refresh.

@Eftimin70

Copy link
Copy Markdown

Hi @razorlikes , I contacted @andygrundman about a week ago and he advised me to look at the host implementation. I took the most recent Vibeshine as the basis and with the help of Google I came up with 2 inserts for amf_d3d11.cpp. My prototype now works perfectly for me, 4k HDR 60fps, real AMF GDR intra refresh is at work and no additional stutters have been introduced. The dreaded Xbox artefacts in slow moving scenes are gone.
If somebody is interested in my code I would be more than happy to share it, maybe some better programmer than I am can take over, polish it (e.g. right now it is always active) and make it available for everyone.

@andygrundman

Copy link
Copy Markdown
Collaborator

If the version of the amf encoder that got merged into Vibe* doesn't work for you, and you have a fix, I'd encourage you to submit a PR to that project. Or at least open an issue about it.

@Eftimin70

Copy link
Copy Markdown

I opened an in issue for Vibeshine with all the details, let's see if someone wants to pick it up.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants