diff --git a/lib/features/desktop/desktop_client.dart b/lib/features/desktop/desktop_client.dart index 8c6cb1a0..137b1185 100644 --- a/lib/features/desktop/desktop_client.dart +++ b/lib/features/desktop/desktop_client.dart @@ -11,6 +11,7 @@ const int desktopReliableInputChannelId = 0; const int desktopMoveInputChannelId = 1; const int desktopMoveChannelMaxPacketLifeTimeMs = 100; const int desktopMoveBufferedAmountLimit = 16 * 1024; +const Duration desktopOfferRequestTimeout = Duration(seconds: 15); String desktopConnectionStateName(RTCPeerConnectionState state) { final value = state.toString().split('.').last; @@ -307,19 +308,27 @@ class DesktopClient { await _peerConnection!.setLocalDescription(offer); // Send SDP Offer to Bridge - final response = await controller.gatewayAcpClientInternal.request( - method: 'xworkmate.desktop.offer', - params: desktopOfferParams( - sessionId: sessionId, - sdpOffer: offer.sdp, - display: display, - width: width, - height: height, - fps: fps, - bitrate: bitrate, - useGpu: useGpu, - ), - ); + final response = await controller.gatewayAcpClientInternal + .request( + method: 'xworkmate.desktop.offer', + params: desktopOfferParams( + sessionId: sessionId, + sdpOffer: offer.sdp, + display: display, + width: width, + height: height, + fps: fps, + bitrate: bitrate, + useGpu: useGpu, + ), + ) + .timeout( + desktopOfferRequestTimeout, + onTimeout: () => throw TimeoutException( + 'Timed out waiting for desktop SDP answer', + desktopOfferRequestTimeout, + ), + ); final sdpAnswerData = response['result']?['sdpAnswer']; if (sdpAnswerData == null) { diff --git a/lib/features/desktop/desktop_view.dart b/lib/features/desktop/desktop_view.dart index 68d6ea05..6dd587a1 100644 --- a/lib/features/desktop/desktop_view.dart +++ b/lib/features/desktop/desktop_view.dart @@ -64,6 +64,7 @@ class _DesktopViewState extends State { StreamSubscription? _streamSubscription; StreamSubscription? _stateSubscription; Timer? _firstFrameStatsTimer; + int _firstFrameStatsPolls = 0; bool get _hasVideoFrame => desktopHasRenderedVideoFrame( hasStream: _hasStream, @@ -123,10 +124,6 @@ class _DesktopViewState extends State { _localRenderer.onResize = () { if (_localRenderer.videoWidth > 0 && _localRenderer.videoHeight > 0) { _markRemoteDesktopFrameReady(); - return; - } - if (mounted) { - setState(() {}); } }; } @@ -144,13 +141,24 @@ class _DesktopViewState extends State { void _startFirstFrameDiagnostics() { _firstFrameStatsTimer?.cancel(); + _firstFrameStatsPolls = 0; unawaited(_collectFirstFrameStats()); - _firstFrameStatsTimer = Timer.periodic(const Duration(seconds: 2), (_) { + _scheduleFirstFrameStatsPoll(const Duration(milliseconds: 500)); + } + + void _scheduleFirstFrameStatsPoll(Duration delay) { + _firstFrameStatsTimer?.cancel(); + _firstFrameStatsTimer = Timer(delay, () { if (!_hasStream || _hasVideoFrame || !mounted) { _stopFirstFrameDiagnostics(); return; } + _firstFrameStatsPolls += 1; unawaited(_collectFirstFrameStats()); + final nextDelay = _firstFrameStatsPolls < 4 + ? const Duration(milliseconds: 500) + : const Duration(seconds: 2); + _scheduleFirstFrameStatsPoll(nextDelay); }); }