@@ -4,6 +4,7 @@ import 'package:flutter_webrtc/flutter_webrtc.dart';
44import 'package:logging/logging.dart' ;
55import 'package:webtrit_signaling/webtrit_signaling.dart' ;
66import 'call_error_reporter.dart' ;
7+ import 'pc_exceptions.dart' ;
78import 'sdp_munger.dart' ;
89
910final _logger = Logger ('RenegotiationHandler' );
@@ -44,7 +45,7 @@ typedef RenegotiationExecutor = Future<void> Function(String callId, int? lineId
4445/// calling `setLocalDescription`. The `await createOffer()` yields control
4546/// to the event loop; a concurrent native callback may update the Dart-side
4647/// cached [RTCPeerConnection.signalingState] in that gap. If the check misses
47- /// a concurrent state change (stale cache), the [on String catch ] below is the
48+ /// a concurrent state change (stale cache), the [RtcJsepErrorParser ] below is the
4849/// authoritative fallback.
4950///
5051/// ## Concurrency guard
@@ -90,52 +91,52 @@ class RenegotiationHandler {
9091 _isHandling = true ;
9192 _pendingRetry = false ;
9293 try {
93- final stateBeforeOffer = peerConnection.signalingState;
94- _logger.fine (() => 'onRenegotiationNeeded signalingState: $stateBeforeOffer ' );
95- if (stateBeforeOffer != RTCSignalingState .RTCSignalingStateStable ) {
96- _logger.fine (() => 'onRenegotiationNeeded skipped: not in stable state ($stateBeforeOffer )' );
97- return ;
98- }
94+ try {
95+ final stateBeforeOffer = peerConnection.signalingState;
96+ _logger.fine (() => 'onRenegotiationNeeded signalingState: $stateBeforeOffer ' );
97+ if (stateBeforeOffer != RTCSignalingState .RTCSignalingStateStable ) {
98+ _logger.fine (() => 'onRenegotiationNeeded skipped: not in stable state ($stateBeforeOffer )' );
99+ return ;
100+ }
99101
100- final localDescription = await peerConnection.createOffer ({});
101- sdpMunger? .apply (localDescription);
102- _logger.info (() => 'onRenegotiationNeeded offer SDP (callId=$callId ):\n ${localDescription .sdp }' );
102+ final localDescription = await peerConnection.createOffer ({});
103+ sdpMunger? .apply (localDescription);
104+ _logger.info (() => 'onRenegotiationNeeded offer SDP (callId=$callId ):\n ${localDescription .sdp }' );
103105
104- final stateAfterOffer = peerConnection.signalingState;
105- if (stateAfterOffer != RTCSignalingState .RTCSignalingStateStable ) {
106- _logger.fine (
107- () =>
108- 'onRenegotiationNeeded: state changed to $stateAfterOffer after createOffer, skipping setLocalDescription' ,
109- );
110- return ;
111- }
106+ final stateAfterOffer = peerConnection.signalingState;
107+ if (stateAfterOffer != RTCSignalingState .RTCSignalingStateStable ) {
108+ _logger.fine (
109+ () =>
110+ 'onRenegotiationNeeded: state changed to $stateAfterOffer after createOffer, skipping setLocalDescription' ,
111+ );
112+ return ;
113+ }
112114
113- // According to RFC 8829 5.6 (https://datatracker.ietf.org/doc/html/rfc8829#section-5.6),
114- // localDescription should be set before sending the offer to transition into have-local-offer state.
115- await peerConnection.setLocalDescription (localDescription);
115+ // According to RFC 8829 5.6 (https://datatracker.ietf.org/doc/html/rfc8829#section-5.6),
116+ // localDescription should be set before sending the offer to transition into have-local-offer state.
117+ await peerConnection.setLocalDescription (localDescription);
116118
117- await execute (callId, lineId, localDescription);
119+ await execute (callId, lineId, localDescription);
120+ } on String catch (e) {
121+ throw RtcJsepErrorParser .parse (e);
122+ }
118123 } on WebtritSignalingErrorException catch (e) {
119124 _logger.warning (
120125 () => 'onRenegotiationNeeded: UpdateRequest rejected by server (callId=$callId , lineId=$lineId ): $e ' ,
121126 );
122127 callErrorReporter.handle (e, null , 'RenegotiationHandler.handle error (callId=$callId , lineId=$lineId )' );
123- } on String catch (e) {
128+ } on PCWrongSignalingState catch (e) {
124129 // flutter_webrtc surfaces native errors as plain strings. A "wrong state" failure
125130 // on setLocalDescription means a concurrent setRemoteDescription (e.g. from an
126131 // incoming updating_call) moved the PC out of stable between the TOCTOU guard and
127132 // the setLocalDescription call. This is a transient race — libwebrtc keeps the
128133 // [[NegotiationNeeded]] flag set and will re-fire onRenegotiationNeeded once the
129134 // PC returns to stable. No user notification is needed.
130- if (e.contains ('wrong state' ) || e.contains ('have-remote-offer' ) || e.contains ('have-local-offer' )) {
131- _logger.warning (
132- () =>
133- 'onRenegotiationNeeded: setLocalDescription failed in wrong state ($e ) '
134- '— libwebrtc will re-fire onRenegotiationNeeded when stable' ,
135- );
136- } else {
137- callErrorReporter.handle (e, null , 'RenegotiationHandler.handle error (callId=$callId , lineId=$lineId )' );
138- }
135+ _logger.warning (
136+ () =>
137+ 'onRenegotiationNeeded: setLocalDescription failed in wrong state (${e .message }) '
138+ '— libwebrtc will re-fire onRenegotiationNeeded when stable' ,
139+ );
139140 } catch (e, s) {
140141 callErrorReporter.handle (e, s, 'RenegotiationHandler.handle error (callId=$callId , lineId=$lineId )' );
141142 } finally {
0 commit comments