Skip to content

Commit e012d85

Browse files
authored
Merge pull request #213 from GetStream/feature/uni-162-implement-handling-sfu-inboundstatenotification
Implement handling InboundStateNotification - this allows SFU to pause tracks if needed
2 parents 5e06cbe + e368656 commit e012d85

File tree

6 files changed

+114
-28
lines changed

6 files changed

+114
-28
lines changed

Packages/StreamVideo/Runtime/Core/LowLevelClient/RtcSession.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,11 @@ var updateLocalParticipantState
15801580
UpdateParticipantTracksState(userId, sessionId, type, isEnabled: false, updateLocalParticipantState,
15811581
out var participant);
15821582

1583+
if (participant != null)
1584+
{
1585+
participant.ClearTrackPausedByServer(type);
1586+
}
1587+
15831588
if (participantSfuDto != null && participant != null)
15841589
{
15851590
participant.UpdateFromSfu(participantSfuDto);
@@ -1982,10 +1987,25 @@ private void OnSfuWebSocketOnChangePublishOptions(ChangePublishOptions obj)
19821987
// StreamTODO: Implement OnSfuWebSocketOnChangePublishOptions
19831988
}
19841989

1985-
private void OnSfuInboundStateNotification(InboundStateNotification obj)
1990+
private void OnSfuInboundStateNotification(InboundStateNotification inboundStateNotification)
19861991
{
1987-
_sfuTracer?.Trace("inboundStateNotification", obj);
1988-
//StreamTODO: implement
1992+
_sfuTracer?.Trace("inboundStateNotification", inboundStateNotification);
1993+
1994+
foreach (var state in inboundStateNotification.InboundVideoStates)
1995+
{
1996+
var trackType = state.TrackType.ToPublicEnum();
1997+
var participant = (StreamVideoCallParticipant)ActiveCall?.Participants
1998+
.FirstOrDefault(p => p.SessionId == state.SessionId);
1999+
2000+
if (participant == null)
2001+
{
2002+
_logs.WarningIfDebug(
2003+
$"[InboundState] Received pause notification for unknown session: {state.SessionId}");
2004+
continue;
2005+
}
2006+
2007+
participant.SetTrackPausedByServer(trackType, state.Paused);
2008+
}
19892009
}
19902010

19912011
private void OnSfuWebSocketOnParticipantMigrationComplete()

Packages/StreamVideo/Runtime/Core/LowLevelClient/WebSockets/SfuWebSocket.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ protected override async Task<JoinResponse> ExecuteConnectAsync(SfuConnectReques
180180
Source = ParticipantSource.WebrtcUnspecified,
181181
};
182182

183+
joinRequest.Capabilities.Add(ClientCapability.SubscriberVideoPause);
184+
183185
var sfuJoinRequest = new SfuRequest
184186
{
185187
JoinRequest = joinRequest,

Packages/StreamVideo/Runtime/Core/Models/Sfu/TrackType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using Unity.WebRTC;

Packages/StreamVideo/Runtime/Core/StatefulModels/StreamVideoCallParticipant.cs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Threading.Tasks;
44
using StreamVideo.Core.InternalDTO.Responses;
@@ -300,6 +300,11 @@ internal void SetTrack(TrackType type, MediaStreamTrack mediaStreamTrack, out IS
300300
throw new ArgumentOutOfRangeException(nameof(type), type, null);
301301
}
302302

303+
if (_pausedTracks.Contains(type))
304+
{
305+
((BaseStreamTrack)streamTrack).SetServerPaused(true);
306+
}
307+
303308
TrackAdded?.Invoke(this, streamTrack);
304309
}
305310

@@ -323,12 +328,38 @@ internal void NotifyTrackEnabled(TrackType type, bool enabled)
323328
// Join call by host and watcher
324329
// Disable track on host -> this causes watcher to disable the track as well
325330
// Leave the call as host and re-join -> the track is re-enabled on host side but watcher still has it disabled
326-
streamTrack.SetEnabled(enabled);
331+
streamTrack.SetPublisherEnabled(enabled);
327332

328333
//StreamTodo: we should trigger some event that track status changed
329334
TrackIsEnabledChanged?.Invoke(this, streamTrack);
330335
}
331336

337+
internal void SetTrackPausedByServer(TrackType trackType, bool paused)
338+
{
339+
bool changed;
340+
if (paused)
341+
{
342+
changed = _pausedTracks.Add(trackType);
343+
}
344+
else
345+
{
346+
changed = _pausedTracks.Remove(trackType);
347+
}
348+
349+
if (changed)
350+
{
351+
GetStreamTrack(trackType)?.SetServerPaused(paused);
352+
}
353+
}
354+
355+
internal void ClearTrackPausedByServer(TrackType trackType)
356+
{
357+
if (_pausedTracks.Remove(trackType))
358+
{
359+
GetStreamTrack(trackType)?.SetServerPaused(false);
360+
}
361+
}
362+
332363
internal void SetIsPinned(bool isPinned) => IsPinned = isPinned;
333364

334365
protected override string InternalUniqueId
@@ -356,6 +387,7 @@ protected override Task UploadCustomDataAsync()
356387
#region Sfu State
357388

358389
private readonly List<TrackType> _publishedTracks = new List<TrackType>();
390+
private readonly HashSet<TrackType> _pausedTracks = new HashSet<TrackType>();
359391
private readonly List<string> _roles = new List<string>();
360392
private float _audioLevel;
361393
private bool _isSpeaking;

Packages/StreamVideo/Runtime/Core/StatefulModels/Tracks/BaseStreamTrack.cs

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using Unity.WebRTC;
33

44
namespace StreamVideo.Core.StatefulModels.Tracks
@@ -12,36 +12,39 @@ public abstract class BaseStreamTrack : IStreamTrack
1212
{
1313
public event StreamTrackStateChangeHandler EnabledChanged;
1414

15-
public bool IsEnabled
16-
{
17-
get => _isEnabled;
18-
private set
19-
{
20-
if(value == _isEnabled)
21-
{
22-
return;
23-
}
24-
25-
_isEnabled = value;
26-
EnabledChanged?.Invoke(_isEnabled);
27-
}
28-
}
15+
/// <summary>
16+
/// Effective enabled state: true only when the publisher has the track enabled
17+
/// AND the Stream Server (SFU) has not paused it.
18+
/// </summary>
19+
public bool IsEnabled => _publisherEnabled && !_serverPaused;
20+
21+
/// <summary>
22+
/// Whether the Stream Server (SFU) has paused this inbound track (e.g. due to insufficient bandwidth).
23+
/// </summary>
24+
public bool IsPausedByServer => _serverPaused;
2925

3026
public void Dispose() => OnDisposing();
3127

3228
internal BaseStreamTrack(MediaStreamTrack track)
3329
{
3430
InternalTrack = track ?? throw new ArgumentNullException(nameof(track));
35-
_isEnabled = track.Enabled;
31+
_publisherEnabled = track.Enabled;
3632
}
3733

3834
internal virtual void Update()
3935
{
4036
}
4137

42-
internal void SetEnabled(bool enabled)
38+
internal void SetPublisherEnabled(bool enabled)
4339
{
44-
IsEnabled = enabled;
40+
if (_publisherEnabled == enabled)
41+
{
42+
return;
43+
}
44+
45+
var wasEnabled = IsEnabled;
46+
_publisherEnabled = enabled;
47+
NotifyIfEffectiveStateChanged(wasEnabled);
4548

4649
//StreamTodo: investigate this. In theory we should disable track whenever the remote user disabled it.
4750
//But there's and edge case where:
@@ -53,14 +56,35 @@ internal void SetEnabled(bool enabled)
5356
// InternalTrack.Enabled = enabled;
5457
}
5558

59+
internal void SetServerPaused(bool paused)
60+
{
61+
if (_serverPaused == paused)
62+
{
63+
return;
64+
}
65+
66+
var wasEnabled = IsEnabled;
67+
_serverPaused = paused;
68+
NotifyIfEffectiveStateChanged(wasEnabled);
69+
}
70+
5671
protected MediaStreamTrack InternalTrack { get; set; }
5772

5873
protected virtual void OnDisposing()
5974
{
6075

6176
}
62-
63-
private bool _isEnabled;
77+
78+
private bool _publisherEnabled;
79+
private bool _serverPaused;
80+
81+
private void NotifyIfEffectiveStateChanged(bool wasEnabled)
82+
{
83+
if (IsEnabled != wasEnabled)
84+
{
85+
EnabledChanged?.Invoke(IsEnabled);
86+
}
87+
}
6488
}
6589

6690
public abstract class BaseStreamTrack<TTrack> : BaseStreamTrack

Packages/StreamVideo/Runtime/Core/StatefulModels/Tracks/IStreamTrack.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22

33
namespace StreamVideo.Core.StatefulModels.Tracks
44
{
@@ -10,8 +10,16 @@ public interface IStreamTrack : IDisposable
1010
event StreamTrackStateChangeHandler EnabledChanged;
1111

1212
/// <summary>
13-
/// Is this track active.
13+
/// Is this track active. This is false when either the publisher has disabled
14+
/// the track or the Stream Server (SFU) has paused it (e.g. due to insufficient bandwidth).
1415
/// </summary>
1516
bool IsEnabled { get; }
17+
18+
/// <summary>
19+
/// Whether the Stream Server (SFU) has paused this inbound track due to bandwidth constraints.
20+
/// Use this to distinguish "publisher turned off the camera" from
21+
/// "video paused by the server due to poor network conditions".
22+
/// </summary>
23+
bool IsPausedByServer { get; }
1624
}
1725
}

0 commit comments

Comments
 (0)