- Replace 'let _ = tx.send()' with proper error handling: log warning,
set webrtc_disconnected flag, and break drain loop on SendError
- Add Arc<AtomicBool> webrtc_paused shared between State/StatePortal
and SwEncState, synced from wrtc.is_connected() in poll_webrtc()
- Skip encoding in encode_filtered_frame() when paused or disconnected
- Drain and discard stale channel frames on disconnect
- Resume encoding automatically on WebRTC reconnection
- Add src/webrtc.rs: HTTP signaling server + str0m Sans-IO WebRTC transport
with H.264 Annex-B → RTP packetization and key-frame request handling
- avhw: introduce FrameOutput enum (Muxer | Channel) so SwEncState can
output to either MP4 muxer or crossbeam channel for WebRTC
- cap_portal: support portal session restore tokens (PersistMode::ExplicitlyRevoked)
to skip re-authorization dialog; add --no-persist flag to force fresh dialog
- args: make --output optional when --port is used for WebRTC mode
- state_portal: integrate WebRTC pipeline (encoder channel → RTP forwarding)
with shorter GOP for WebRTC (fps/2, min 10)
- main: redirect tracing to stderr; validate --output or --port required
- Add dependencies: str0m 0.20, serde_json 1, dirs 6
BUG-2 (HIGH): SHM Buffer event caused permanent hang
In the ZwlrScreencopyFrameV1 dispatcher, receiving a SHM Buffer event
left in_flight_surface stuck at AllocQueued forever, preventing
queue_alloc_frame() from requesting new frames.
Fix: treat Buffer as a metadata offer (v3 protocol), wait for
BufferDone to decide failure, and add AllocQueued state guard to
LinuxDmabuf handler.
BUG-3 (MEDIUM): Portal backend picked wrong GPU on multi-GPU systems
state_portal.rs hardcoded /dev/dri/renderD128 then renderD129, which
selects the wrong GPU when PipeWire uses a different device.
Fix: extract find_drm_render_nodes() as shared utility; defer DRM
device selection to first PipeWire frame; test each candidate with
av_hwframe_transfer_data to find the GPU that can actually import
the DMA-BUF frame.
BUG-4 (LOW): VAAPI device context created twice unnecessarily
try_finalize_output() created an AvHwDevCtx stored in EverythingButFmt,
but negotiate_format() discarded it (_hw_device_ctx) and EncState::new
created a new one.
Fix: thread the existing hw_device_ctx through negotiate_format() and
create_encoder() to EncState::new() which reuses it when provided.
Add a second capture backend for compositors without wlr-screencopy
(KWin, GNOME, etc.) using the xdg-desktop-portal ScreenCast interface
and PipeWire DMA-BUF streaming.
New files:
- src/backend_detect.rs: auto-detect wlr-screencopy vs portal backend
- src/cap_portal.rs: Portal session setup + PipeWire DMA-BUF thread
- src/state_portal.rs: StatePortal encoder pipeline (DMA-BUF → VAAPI)
Changes:
- Cargo.toml: add ashpd 0.13, tokio 1, pipewire 0.9, libspa 0.9,
crossbeam-channel 0.5
- src/args.rs: add --backend CLI flag
- src/avhw.rs: extract create_encoder() from inline State code
- src/main.rs: route to portal or wlr-screencopy based on backend
- src/state.rs: fix params.destroy() on dup failure, cleanup
in_flight_surface on copy fail, use create_encoder()
- tests/integration_test.rs: add --backend flag tests
- registry_queue_init consumes registry events during its internal
roundtrip without forwarding them to Dispatch<WlRegistry>. Added
bind_initial_globals() to manually iterate GlobalList and bind all
initial globals (wl_output, xdg_output_manager, dmabuf, screencopy,
wlr_output_manager) at State::new time.
- Fix av_freep segfault in build_filter_graph: av_buffersrc_parameters_alloc
returns a plain pointer, use av_free instead of av_freep (which expects
pointer-to-pointer).
- Fix filter graph format negotiation: remove software format filter that
broke scale_vaapi hardware pipeline. Chain is now src -> scale -> sink.
- Downgrade repeat_pps error to warning (not available in FFmpeg 6.x).