video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

video: sync capture: resend frame if needed to avoid capture delay - #485

Closed
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame
Closed

video: sync capture: resend frame if needed to avoid capture delay#485
psyke83 wants to merge 2 commits into
LizardByte:nightlyfrom
psyke83:resend_frame

Conversation

@psyke83

@psyke83psyke83 commented Nov 8, 2022

Copy link
Copy Markdown
Contributor

Description

  • platform: replace generic 1000ms capture delay with real frame delay
  • for sync encoding, ensure that a frame is sent within frame to frame interval
    regardless of timeout status.

This resolves the issue in which the last captured frame is delayed which
is especially noticeable in low framerate content (such as desktop streaming)
on certain encoders.

Please note that I have only verified capture delay as fixed on Windows via AMF, so nvenc and Linux capture needs to be validated before merging.

Issues Fixed or Closed

Fixes#122, #387, #412

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@psyke83
psyke83 marked this pull request as draft November 9, 2022 03:13
@ReenigneArcher

Copy link
Copy Markdown
Member

Thank you for this PR (and all of your contributions)! Are you on our discord server https://app.lizardbyte.dev/discord? We can ask for testers there. On Linux I can test X11 with software encoding only at this point but to be honest I never noticed these delays in that scenario before.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I joined the discord (same username as here on github).

From looking at the encoders listed in video.cpp, the only encoder that need to be tested to see if the frame delay issue is fixed is nvenc on Windows; I have no NVIDIA hardware available to test. The other platform drivers should not be impacted by the change in capture delay, but I was also able to verify that Linux VAAPI capture still works OK in addition to AMF/software on Windows.

@istori1

Copy link
Copy Markdown
Contributor

I tested https://www.testufo.com/framerates#count=1&background=stars&pps=960 on a build with these changes. It looks like the motion is affected. It jumped more than before.

OS: Linux (Flatpak on Fedora 36)
Capture Method: KMS (X11)
GPU: Intel
Encoder: h264_vaapi

@psyke83

Copy link
Copy Markdown
ContributorAuthor

@istori1

Thanks, I'll look into it. I'll need to reinstall Linux on my desktop first, so it will take some time. If some encoders are too sensitive to the low capture interval, we can use a conditional check for setting the delay such as resend_frame ? std::chrono::duration_cast<std::chrono::milliseconds>(delay) : 1000ms. This would preserve the same behaviour as before the patch on async encoders.

If you use moonlight-qt, it might help to enable the statistics (Ctrl + Alt + S) and see if the incoming framerate is reduced from normal.

@ReenigneArcher

Copy link
Copy Markdown
Member

@psyke83 I had an idea, maybe not a good one, but... Would it be possible to have a setting for each application that could be named "desktop mode" (or "game mode")? Then depending on the application and the setting it would stream in the "mode" more desirable for either desktop or gaming. I don't know how difficult it would be to implement on a per app basis.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I actually found a proper fix to this. Will update the PR soon after I research if the issue can also fix nvenc encoding.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

I've refreshed the patches. I may have jumped the gun in saying that I found the proper fix, unfortunately.

In the case of AMF, it seems that the encoder buffers some frames even though "ultralowlatency" is supposed to encode a frame immediately after a frame is submitted. Reducing the buffer from 16 to 1 eliminates the delay, but the encoder also can no longer run at 60fps. Setting buffers to 2 allows the encoder to run at full speed and reduces latency, but doesn't eliminate the delayed frame issue completely, so by forcing a callback when a timeout the span of 3 frames passes, it greatly improves the situation to the point that I can barely notice any delay on desktop content, and I would be absolutely comforable to use Sunshine as a VNC replacement.

The refreshed patch will only change behaviour for amdvce and nvenc on Windows, so the latter is what needs to be tested.

* Add encoder flag "FORCE_CALLBACK" for encoders that don't perform well with callback capture
* Set new flag for "amdenc" and "nvenc" on Windows only.
* When flag is enabled, reduce timeout delay to 2x frame interval, and
force a callback if a timeout transpires. This should mean that a callback is
guaranteed approximately every third frame.
Before change:
* amdenc has a noticeable issue where the last received image can stutter or freeze
for a long period if the capture framerate is low (such as on desktop content).
After change:
* forced callback with a 2x frame delay interval (e.g. 32ms for 60fps) ensures
that the last received frame will not be delayed for more than two frames, and
desktop usage feels much smoother.
Note: using too low of a frame delay interval will result in a forced capture rate
of the client framerate (60fps), which can make low framerate content jittery.
Using 2x frame delay alleviate this issue, allowing the encoder rate to throttle
to ~20fps minimum, this avoiding jitter and reducing encoder strain.
The AMF encoder does not seem to output frames in realtime even if
the "ultralowlatency" usage profile is used. Reducing the HW buffers
from 16 -> 2 helps to reduce latency and minimize delayed frames.
Note that it is necessary to set "initial_pool_size" after av_hwframe_ctx_init()
in order to resize the buffer without causing issues elsewhere in the ffmpeg code.
Resizing "initial_pool_size" affects the following:
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L224
* https://github.com/FFmpeg/FFmpeg/blob/a78f136f3fa039fd7ad664fd6e6e976f1448c68b/libavcodec/amfenc.c#L278-L279
@psyke83

Copy link
Copy Markdown
ContributorAuthor

This PR (or any other kind of hack) will be unnecessary if https://github.com/LizardByte/ffmpeg-prebuilt/pull/19 is merged and Sunshine is linked against newly patched prebuilts. Note that the patch needs to be present on the master branch for it to take effect.

@psyke83

Copy link
Copy Markdown
ContributorAuthor

Closing this test PR; correct proper fix is here: LizardByte/build-deps#18

@psyke83psyke83 closed this Nov 24, 2022
@psyke83
psyke83 deleted the resend_frame branch February 15, 2026 18:07
Sign up for freeto 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.

3 participants

@psyke83@ReenigneArcher@istori1