Repository navigation
stream: a video session holds HDMI capture while it runs (RustDesk re-park) - #356
Conversation
Capture parks 20 s after its last viewer: VPSS outputs off, the video and JPEG encoders deleted. The direct and WebRTC streamers count their viewers for that; RustDesk's media bridge subscribes to the same video source but counted nothing. With only RustDesk connected, capture parked under it, its next read resumed the native side, and the server still took capture for parked. Nothing parked it again after the session, so VPSS kept converting and the encoder channel stayed allocated until another viewer came and went. Every video session now holds an HDMI capture lease (vm.AcquireHdmiCaptureLease, wired in main) from its start to its end, whoever subscribed. Counted viewers keep working as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dormancygrace
left a comment
There was a problem hiding this comment.
Reviewed 0fac4f4. No actionable defects found. The lease is acquired outside the VideoSource mutex and before either capture loop; runStream closes/drains the native stream before returning, then the deferred lease release runs before session.done closes. Thus a replacement session cannot start before the old lease is released. Native-to-paced fallback stays within the same lease, and multiple subscribers share one lease. Initialization installs the callback before router/RustDesk subscriptions; counted viewers remain independent.
Local validation passed: go test -race -tags teststub,nomsgpack for service/stream, service/vm and service/rustdesk; go vet on those packages and main; real native-binding recovery test plus its positive control. The new session-lifetime test and existing park/resume/lease tests pass. No device test was performed in this review; the RustDesk-only park/re-park hardware observation remains the limitation already recorded in the description.
14 CI checks are currently green on this head; the full firmware build is still running. Merge will wait for that final check.
dormancygrace
left a comment
There was a problem hiding this comment.
Final verification: all 15 checks on 0fac4f4 are now successful, including the full firmware build. No code changes since the reviewed/tested commit. Merging under the user's authorization.
What
Encoding without viewers already stops. Capture parks 20 s after its last viewer (
scheduleHdmiParkTimerLocked): VPSS outputs are disabled and the H.264/H.265 and JPEG encoder channels are deleted. The H.264/H.265 direct and WebRTC streamers count their viewers for this, and MJPEG reports its clients.RustDesk's media bridge (
service/rustdesk/bridge.go,serveMedia) subscribes to the same video source but was not counted. With only RustDesk connected, this happened:kvmv_read_img_locked);hdmiCaptureParkedflag stayedtrue.That flag is cleared only when demand changes, so nothing parked capture again after the RustDesk session. VPSS kept converting every input frame, and the encoder channel stayed allocated, until some other viewer connected and left.
Now every video session holds an HDMI capture lease (
vm.AcquireHdmiCaptureLease) from its start to its end, whoever subscribed.mainpasses the lease function viastream.SetCaptureDemandininitialize, before the router or the RustDesk bridge can subscribe. Fixing it in the shared source rather than in RustDesk alone covers any future subscriber. MJPEG and MCP/PicoClaw already count or lease.Side effects
Tests
TestVideoSessionHoldsCaptureWhileItRuns: one hold per session, kept while any subscriber remains, released when the session ends. Run with-race.go build/go vet/go test -tags teststub,nomsgpackon the touched packages.tests/test-video-recovery-native.py: recovery still touches no video ABI.Not verified on hardware
The stand always had a browser viewer connected, so neither the park under a RustDesk-only session nor the re-park after it was observed. Expected evidence in a no-viewer window:
VENC channel 1 destroyedand frozen VPSSSendOKcounters about 20 s after the last viewer, and a resume on reconnect.🤖 Generated with Claude Code