Skip to content

fix(linux/pipewire): avoid video stream freeze (on display switch)#5249

Open
Kishi85 wants to merge 1 commit into
LizardByte:masterfrom
Kishi85:workaround-video-stream-freeze-on-display-switch
Open

fix(linux/pipewire): avoid video stream freeze (on display switch)#5249
Kishi85 wants to merge 1 commit into
LizardByte:masterfrom
Kishi85:workaround-video-stream-freeze-on-display-switch

Conversation

@Kishi85
Copy link
Copy Markdown
Contributor

@Kishi85 Kishi85 commented Jun 4, 2026

Description

This is a trial-and-error workaround to avoid video stream freezing when display switching that somehow happens when not using the added thread sleeps due to a racecondition that needs to be further diagnosed and might even be outside Sunshine's control. Adding a 0.1ms sleep at the relevant codepoints (see changes) seems to fix the issue reliably without creating unwanted behaviour/other issues.

Also the log level for pipewire state changes to ease initial analysis of future issues without having debug logging enabled.

This issue was identified after working around the FFmpeg segfault in #4943 but might occur independently from that. Unsure if this can also help with #5241 but should be tested as it might be the same root cause.

Note: This issue is almost impossible to pinpoint as it is only occurring when running as a systemd user service without having debug logging enabled (so BOOST_LOG(debug) does noop). If run with debug logging enabled, the added runtime by BOOST_LOG is sufficient to allievate the issue. Same is true if running with a debugger attached. The only way to figure out the relevant code segment was by changing the loglevel for each log statement in pipewire.cpp and trying to trigger the issue. After doing that the following codesegment was found to fix the issue when using log level info:

if (!push_captured_image_cb(std::move(img_out), true)) {
BOOST_LOG(debug) << "[pipewire] PipeWire: !push_captured_image_cb -> ok";
return platf::capture_e::ok;
}

That is why the workaround now adds a 0.1ms sleep right before the return (and does so also for the timeout case).

Screenshot

Issues Fixed or Closed

Roadmap Issues

Type of Change

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

Checklist

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

AI Usage

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

@Kishi85 Kishi85 force-pushed the workaround-video-stream-freeze-on-display-switch branch 2 times, most recently from b29d83a to d633d52 Compare June 4, 2026 14:28
@Kishi85 Kishi85 force-pushed the workaround-video-stream-freeze-on-display-switch branch 4 times, most recently from bb8e6da to 1003b5d Compare June 5, 2026 14:08
@ReenigneArcher ReenigneArcher force-pushed the workaround-video-stream-freeze-on-display-switch branch from 1003b5d to f8bb8ae Compare June 5, 2026 17:06
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 5, 2026

Bundle Report

Bundle size has no change ✅

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 5, 2026

Codecov Report

❌ Patch coverage is 0% with 13 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (master@fa4fbbd). Learn more about missing BASE report.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/platform/linux/pipewire.cpp 0.00% 13 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #5249   +/-   ##
=========================================
  Coverage          ?   17.82%           
=========================================
  Files             ?      111           
  Lines             ?    24179           
  Branches          ?    10700           
=========================================
  Hits              ?     4310           
  Misses            ?    17909           
  Partials          ?     1960           
Flag Coverage Δ
Archlinux 11.22% <0.00%> (?)
FreeBSD-amd64 13.35% <0.00%> (?)
Homebrew-ubuntu-22.04 13.56% <0.00%> (?)
Linux-AppImage 12.15% <0.00%> (?)
Windows-AMD64 14.85% <ø> (?)
Windows-ARM64 13.19% <ø> (?)
macOS-arm64 18.86% <ø> (?)
macOS-x86_64 18.34% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/platform/linux/pipewire.cpp 0.00% <0.00%> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fa4fbbd...f8bb8ae. Read the comment docs.

@ReenigneArcher
Copy link
Copy Markdown
Member

Does this resolve #4943 then? And, do you want to merge this or wait for tester feedback (it's unclear from the PR description)?

@ReenigneArcher ReenigneArcher added this to the pipewire milestone Jun 5, 2026
@Kishi85
Copy link
Copy Markdown
Contributor Author

Kishi85 commented Jun 5, 2026

It's the fix for the second issue that I've identified in #4943 after commenting out the frame flush (still waiting on feedback in #4943 for that). The video stream freeze issue can occur independently although I'm unsure about the root cause as it's some kind of race condition with an extremely tight timing. It would probably be good to have someone else look at this if possible but I'd also rather not delay this too long in case it also helps with #5241. Maybe @psyke83 could check this if his time permits? By itself this shouldn't break anything as it is only adding 100us to the display switch capture loop return (and only to that specific case).

I can also add the removal of the frame flush to this PR depending on the final verdict in #4943 if you want to wait for that.

@Kishi85 Kishi85 force-pushed the workaround-video-stream-freeze-on-display-switch branch from f8bb8ae to f4d7ea3 Compare June 6, 2026 07:03
This is a trial-and-error workaround to avoid video stream freezing when
display switching that somehow happens when not using the added thread
sleeps due to a racecondition that needs to be further diagnosed.

Pipewire state change logging is also move to information log level to
improve initial issue analysis without enabling debug logging
@Kishi85 Kishi85 force-pushed the workaround-video-stream-freeze-on-display-switch branch from f4d7ea3 to db9699b Compare June 6, 2026 07:04
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 6, 2026

@Kishi85
Copy link
Copy Markdown
Contributor Author

Kishi85 commented Jun 6, 2026

To speed this up I've removed the logic order change (as that's the thing I was really unsure about) for the capture timeout case (as it is working as it is now). If someone ever finds and fixes the root cause or has a better solution to avoid the video stream freeze the workaround can/should be replaced/removed.

The way it is now I'm good with merging the change.

Still needs a fix for the FFmpeg segfault to fully handle #4943 for that see #5257, otherwise it needs to be fixed in upstream FFmpeg.

@psyke83
Copy link
Copy Markdown
Contributor

psyke83 commented Jun 6, 2026

You mention that the issue can't be reproduced when Sunshine debug is active, but have you tried checking (only) the pipewire debug output for any clues when this happens? Adding PIPEWIRE_DEBUG=4 into Sunshine's runtime environment should do the trick.

@Kishi85
Copy link
Copy Markdown
Contributor Author

Kishi85 commented Jun 7, 2026

Pipewire debug output (although it is a lot) shows the following right before the stream freezes:

...
Jun 07 08:39:37 desktop sunshine[11991]: D pw.stream [stream.c:424:stream_set_state]: 0x7f95a8000fe0: update state from paused -> streaming (0) (null)
Jun 07 08:39:37 desktop sunshine[11991]: D spa.videoadapter [videoadapter.c:1155:impl_node_send_command]: 0x7f95a8009d08: started
Jun 07 08:39:37 desktop sunshine[11991]: D pw.node [impl-node.c:2748:on_state_complete]: 0x7f95a811dcc0: state complete res:0 seq:0
Jun 07 08:39:37 desktop sunshine[11991]: D pw.node [impl-node.c:459:node_update_state]: 0x7f95a811dcc0: start node driving:0 driver:0 prepared:0
Jun 07 08:39:37 desktop sunshine[11991]: D pw.node [impl-node.c:148:activate_target]: 0x7f95a811dcc0: target state:0x7f964c003008 id:112 pending:0/0 1:0:1
Jun 07 08:39:37 desktop sunshine[11991]: D pw.node [impl-node.c:493:node_update_state]: 0x7f95a811dcc0: (sunshine) suspended -> running ((null))
Jun 07 08:39:37 desktop sunshine[11991]: I pw.node [impl-node.c:503:node_update_state]: (sunshine-137) suspended -> running
Jun 07 08:39:37 desktop sunshine[11991]: D mod.client-node [remote-node.c:1009:node_info_changed]: info changed 0x7f95a800b7e8
Jun 07 08:39:37 desktop sunshine[11991]: D mod.protocol-native [connection.c:280:clear_buffer]: 0x7f95a8120b60 clear fds:0 n_fds:3
Jun 07 08:39:37 desktop sunshine[11991]: D pw.core [core.c:41:core_event_ping]: 0x7f95a80b3420: object 2 ping 1073741865
Jun 07 08:39:37 desktop sunshine[11991]: radv: RADV_PERFTEST=video_encode is deprecated and will be removed in future Mesa releases. Please use RADV_EXPERIMENTAL=video_encode instead.
Jun 07 08:39:37 desktop sunshine[11991]: WARNING: radv is not a conformant Vulkan implementation, testing use only.
Jun 07 08:39:37 desktop sunshine[11991]: [2026-06-07 08:39:37.270]: Info: Streaming bitrate is 30988000
Jun 07 08:39:37 desktop sunshine[11991]: [2026-06-07 08:39:37.272]: Info: Vulkan encode using GPU: AMD Radeon RX 9070 XT (RADV GFX1201)
Jun 07 08:39:37 desktop sunshine[11991]: [2026-06-07 08:39:37.272]: Info: Minimum FPS target set to ~30fps (33.3333ms)

If I'm reading this correctly then pipewire should be streaming already (first line) but whatever happens in Sunshine's video capture loop gets stuck without that tiny delay (Audio still plays, further display switching breaks likely due to push_capture_image_cb no longer getting called) and the encoder does not even properly create.

As already noted I've not found a way to properly debug this yet as even the debugger (or debug logging) throws the timing off enough to not trigger the issue.

With this PR applied I'm getting the same Pipewire debug output but this time the encoder properly creates and starts up:

Jun 07 08:49:49 desktop sunshine[19868]: D pw.stream [stream.c:424:stream_set_state]: 0x7f83200fcf00: update state from paused -> streaming (0) (null)
Jun 07 08:49:49 desktop sunshine[19868]: D spa.videoadapter [videoadapter.c:1155:impl_node_send_command]: 0x7f8320127a68: started
Jun 07 08:49:49 desktop sunshine[19868]: D pw.node [impl-node.c:2748:on_state_complete]: 0x7f83201322b0: state complete res:0 seq:0
Jun 07 08:49:49 desktop sunshine[19868]: D pw.node [impl-node.c:459:node_update_state]: 0x7f83201322b0: start node driving:0 driver:0 prepared:0
Jun 07 08:49:49 desktop sunshine[19868]: D pw.node [impl-node.c:148:activate_target]: 0x7f83201322b0: target state:0x7f83c00b3008 id:113 pending:0/0 1:0:1
Jun 07 08:49:49 desktop sunshine[19868]: D pw.node [impl-node.c:493:node_update_state]: 0x7f83201322b0: (sunshine) suspended -> running ((null))
Jun 07 08:49:49 desktop sunshine[19868]: I pw.node [impl-node.c:503:node_update_state]: (sunshine-138) suspended -> running
Jun 07 08:49:49 desktop sunshine[19868]: D mod.client-node [remote-node.c:1009:node_info_changed]: info changed 0x7f83200192e8
Jun 07 08:49:49 desktop sunshine[19868]: D mod.protocol-native [connection.c:280:clear_buffer]: 0x7f8320103bd0 clear fds:0 n_fds:3
Jun 07 08:49:49 desktop sunshine[19868]: D pw.core [core.c:41:core_event_ping]: 0x7f83200a0920: object 2 ping 1073741865
Jun 07 08:49:49 desktop sunshine[19868]: [2026-06-07 08:49:49.145]: Info: Creating encoder [hevc_vulkan]
Jun 07 08:49:49 desktop sunshine[19868]: [2026-06-07 08:49:49.145]: Info: Color coding: SDR (Rec. 709)
Jun 07 08:49:49 desktop sunshine[19868]: [2026-06-07 08:49:49.145]: Info: Color depth: 8-bit
Jun 07 08:49:49 desktop sunshine[19868]: [2026-06-07 08:49:49.145]: Info: Color range: JPEG
Jun 07 08:49:49 desktop sunshine[19868]: radv: RADV_PERFTEST=video_encode is deprecated and will be removed in future Mesa releases. Please use RADV_EXPERIMENTAL=video_encode instead.
Jun 07 08:49:49 desktop sunshine[19868]: WARNING: radv is not a conformant Vulkan implementation, testing use only.
Jun 07 08:49:49 desktop sunshine[19868]: radv: RADV_PERFTEST=video_encode is deprecated and will be removed in future Mesa releases. Please use RADV_EXPERIMENTAL=video_encode instead.
Jun 07 08:49:49 desktop sunshine[19868]: WARNING: radv is not a conformant Vulkan implementation, testing use only.
Jun 07 08:49:49 desktop sunshine[19868]: [2026-06-07 08:49:49.272]: Info: Streaming bitrate is 30988000
Jun 07 08:49:49 desktop sunshine[19868]: [2026-06-07 08:49:49.274]: Info: Vulkan encode using GPU: AMD Radeon RX 9070 XT (RADV GFX1201)
Jun 07 08:49:49 desktop sunshine[19868]: [2026-06-07 08:49:49.274]: Info: Minimum FPS target set to ~30fps (33.3333ms)

The only difference on pipewire debug output I've seen once after was (and that only because I've been button mashing CTRL+ALT+SHIFT+F1 as hard as possible):

Jun 07 08:49:32 desktop sunshine[19868]: I pw.node [impl-node.c:1577:node_on_fd_events]: (sunshine-112) client missed 1 wakeups

The freeze only happens on portal-/kwingrab, not on kmsgrab and I've also tested both vulkan and vaapi encoders to make sure it's not related to them.

I'm not fully satisfied with adding a microsleep to workaround this issue but so far it's the best thing I could come up with and it's the only thing that has proven to reliably work. Therefore I've marked this as a workaround in code until a better solution is found.

@psyke83
Copy link
Copy Markdown
Contributor

psyke83 commented Jun 7, 2026

Can you see if the issue can reproduce with active content on the host? Leave vrrtest running, for example. When troubleshooting mode change hangs there was one specific case where a hang occurred only when changing modes on an idle desktop.

@Kishi85
Copy link
Copy Markdown
Contributor Author

Kishi85 commented Jun 7, 2026

Also happens on non-idle stream. Tested with running a YouTube video. That's where I noted audio continuing despite the video stream freeze.

@Kishi85
Copy link
Copy Markdown
Contributor Author

Kishi85 commented Jun 7, 2026

To clarify: The issue does not occur on every display switch but has a noticeable chance to occur (like 3 in 10 switches). It can also be force by button mashing any display switch button repeatedly (Note: you might trigger #4943 as well without the proper fix in place).

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