Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
wants to merge 2 commits into from

Conversation

psyke83
Copy link
Collaborator

@psyke83 psyke83 commented Nov 8, 2022

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
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
Collaborator Author

psyke83 commented Nov 9, 2022

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
Contributor

istori1 commented Nov 9, 2022

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
Collaborator Author

psyke83 commented Nov 9, 2022

@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
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
Collaborator Author

psyke83 commented Nov 10, 2022

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
Collaborator Author

psyke83 commented Nov 11, 2022

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
Collaborator Author

psyke83 commented Nov 21, 2022

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
Collaborator Author

psyke83 commented Nov 24, 2022

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

@psyke83 psyke83 closed this Nov 24, 2022
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.

3 participants