Skip to content

Comments

feat(llc): handle ice restart sfu event, rollback pc#1175

Open
Brazol wants to merge 2 commits intomainfrom
feat/ice-restart-pc-rollback
Open

feat(llc): handle ice restart sfu event, rollback pc#1175
Brazol wants to merge 2 commits intomainfrom
feat/ice-restart-pc-rollback

Conversation

@Brazol
Copy link
Contributor

@Brazol Brazol commented Feb 11, 2026

resolves FLU-376

Summary by CodeRabbit

  • Bug Fixes
    • Fixed handling of SFU ICE restart events to enable proper ICE restart and connection renegotiation
    • Fixed SDP rollback for failed remote answers to prevent inconsistent connection signaling states

@Brazol Brazol requested a review from a team as a code owner February 11, 2026 12:16
@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

The changes implement ICE restart functionality for WebRTC peer connections, including SFU event handling, SDP validation and rollback mechanisms, enhanced error recovery in signaling flows, and improved renegotiation logic with comprehensive error handling.

Changes

Cohort / File(s) Summary
ICE Restart Event Infrastructure
packages/stream_video/lib/src/sfu/data/events/sfu_events.dart, packages/stream_video/lib/src/sfu/data/events/sfu_event_mapper_extensions.dart
Added new SfuIceRestartEvent class with peerType field and mapped iceRestart event payloads to domain events in the SFU event mapper.
Signaling and PeerConnection
packages/stream_video/lib/src/call/session/call_session.dart, packages/stream_video/lib/src/webrtc/peer_connection.dart
Enhanced ICE restart handling with new _onIceRestart handler, modified _onRenegotiationNeeded with Result-based error handling and SDP rollback, added rollbackLocalDescription() method, and improved SDP validation in offer/answer creation.
Utilities
packages/stream_video/lib/src/webrtc/transceiver_cache.dart
Renamed parameter in TransceiverManager.getWith from id to publishOptionId for improved clarity.
Documentation
packages/stream_video/CHANGELOG.md
Added Upcoming section with fixed items for SFU iceRestart event handling and SDP rollback on failed remote answers.

Sequence Diagram

sequenceDiagram
    participant SFU as SFU Server
    participant CallSession as CallSession
    participant PC as StreamPeerConnection
    participant Offer as Remote Offer/Answer

    SFU->>CallSession: SfuIceRestartEvent (peerType)
    activate CallSession
    
    alt Publisher Path
        CallSession->>PC: restartIce()
        activate PC
        PC-->>CallSession: success
        deactivate PC
        
        CallSession->>CallSession: _onRenegotiationNeeded()
        activate CallSession
        CallSession->>PC: createOffer()
        activate PC
        PC-->>CallSession: Result<SessionDescription>
        deactivate PC
        
        alt SDP Valid
            CallSession->>PC: setLocalDescription(offer)
            PC-->>CallSession: Result
            
            CallSession->>SFU: publisherConnection.setPublisher()
            SFU-->>CallSession: RemoteAnswer
            
            CallSession->>PC: setRemoteAnswer(answer)
            PC-->>CallSession: Result
            CallSession-->>CallSession: Return Result.success()
        else SDP Invalid
            CallSession-->>CallSession: Rollback & Return error
        end
        deactivate CallSession
    else Subscriber Path
        CallSession->>PC: restartIce()
        activate PC
        PC-->>CallSession: success
        deactivate PC
    end
    
    deactivate CallSession
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 When iceblocks the stream and connections grow slow,
We restart the dance with a hop-skip-and-go,
With rollback for safety and errors so tight,
The signals flow smoothly—connectivity's right!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author, while the repository template requires multiple sections including Goal, Implementation details, Testing, and Checklist items. Add a comprehensive description covering: (1) Goal explaining why ICE restart and rollback are needed, (2) Implementation details of the changes, (3) Testing approach, and (4) complete the contributor checklist including assignment and Slack notification.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main changes: handling ICE restart SFU events and adding PC rollback functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ice-restart-pc-rollback

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stream_video/lib/src/call/session/call_session.dart (1)

917-974: ⚠️ Potential issue | 🟠 Major

Return values from inside _negotiationLock.synchronized lambda are silently discarded.

The return statements on Lines 928, 940, 956, and 969 exit the closure passed to synchronized(), not _onRenegotiationNeeded itself. Since the result of await _negotiationLock.synchronized(...) is not captured, the method always reaches Line 973 and returns Result.success(null), regardless of failures inside the lock.

Currently no caller inspects the return value, so this has no functional impact yet. However, the Future<Result<void>> signature is misleading — it promises meaningful error reporting that doesn't actually work.

Fix by capturing and returning the synchronized result:

Proposed fix
-    await _negotiationLock.synchronized(() async {
+    final lockResult = await _negotiationLock.synchronized<Result<void>>(() async {
       _logger.d(() => '[negotiate] type: ${pc.type}');

       final offer = await pc.createOffer();
       if (offer is! Success<rtc.RTCSessionDescription>) {
         return Result<void>.error(
           'Failed to create offer: ${offer.getErrorOrNull()}',
         );
       }
       // ... rest of lock body ...
+      return const Result<void>.success(null);
     });
-
-    return const Result.success(null);
+    return lockResult;
🧹 Nitpick comments (2)
packages/stream_video/CHANGELOG.md (1)

1-5: LGTM!

Changelog entries clearly describe both changes. Minor nit: the iceRestart event handling reads more like a feature ("Added handling…") than a fix, but the section header says "Fixed". Consider using "✅ Added" for the first item if it's new functionality rather than a bug fix.

packages/stream_video/lib/src/call/session/call_session.dart (1)

804-838: Publisher ICE restart proceeds to renegotiation even if restartIce() fails.

On Line 817, publisher.restartIce() result is logged but not checked before triggering renegotiation on Line 824. If the ICE restart itself failed, proceeding to renegotiation may be wasteful or could produce confusing errors. Consider returning early on failure.

Suggested improvement
       _logger.i(() => '[onIceRestart] restarting ICE for publisher');
       final result = await publisher.restartIce();
       _logger.d(() => '[onIceRestart] publisher ICE restart result: $result');
+      if (result.isFailure) {
+        _logger.w(() => '[onIceRestart] publisher ICE restart failed, skipping renegotiation');
+        return;
+      }

       // After marking ICE restart, explicitly trigger renegotiation to create

@codecov
Copy link

codecov bot commented Feb 11, 2026

Codecov Report

❌ Patch coverage is 0% with 79 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.46%. Comparing base (d3ad830) to head (8405f9f).

Files with missing lines Patch % Lines
...tream_video/lib/src/call/session/call_session.dart 0.00% 46 Missing ⚠️
...s/stream_video/lib/src/webrtc/peer_connection.dart 0.00% 24 Missing ⚠️
...c/sfu/data/events/sfu_event_mapper_extensions.dart 0.00% 4 Missing ⚠️
...ream_video/lib/src/sfu/data/events/sfu_events.dart 0.00% 3 Missing ⚠️
...stream_video/lib/src/webrtc/transceiver_cache.dart 0.00% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main   #1175      +/-   ##
========================================
- Coverage   6.47%   6.46%   -0.01%     
========================================
  Files        601     601              
  Lines      42031   42094      +63     
========================================
  Hits        2723    2723              
- Misses     39308   39371      +63     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

2 participants