Skip to content

[Enhancement]Incoming video pause #888

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from

Conversation

ipavlidakis
Copy link
Contributor

@ipavlidakis ipavlidakis commented Jul 14, 2025

🔗 Issue Links

Resolves https://linear.app/stream/issue/IOS-994/featureinbound-video-pause

🎯 Goal

Provide a way for the SFU to instruct the client which tracks are remotely paused.

📝 Summary

Docs preview: https://github.com/GetStream/docs-content/pull/452

🧪 Manual Testing Notes

  • By default the feature is disabled
  • Enter a call with another client
  • Activate the Link conditioner to Edge
  • Remain in the call a few seconds
  • You shouldn't observe any changes during the call. Remote track should be lagging but always visible
  • Disable the link conditioner
  • Logout and enable the feature from the debug menu (Client Capabilities > Subscriber video pause)
  • Enter a call with another client
  • Activate the Link conditioner to Edge
  • Remote track should get disabled at some point (it's ok if it gets reenabled)

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change follows zero ⚠️ policy (required)
  • This change should receive manual QA
  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (tutorial, CMS)

@ipavlidakis ipavlidakis self-assigned this Jul 14, 2025
@ipavlidakis ipavlidakis requested a review from a team as a code owner July 14, 2025 15:48
@ipavlidakis ipavlidakis added the enhancement New feature or request label Jul 14, 2025
Copy link

Public Interface

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause



 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func updateClientCapabilities(_ clientCapabilities: Set<ClientCapability>)async

Copy link

1 Warning
⚠️ Big PR

Generated by 🚫 Danger

@Stream-SDK-Bot
Copy link
Collaborator

Stream-SDK-Bot commented Jul 14, 2025

SDK Size

title develop branch diff status
StreamVideo 8.06 MB 8.08 MB +19 KB 🟢
StreamVideoSwiftUI 2.27 MB 2.29 MB +16 KB 🟢
StreamVideoUIKit 2.4 MB 2.41 MB +16 KB 🟢
StreamWebRTC 9.85 MB 9.85 MB 0 KB 🟢

@ipavlidakis ipavlidakis force-pushed the enhancement/incoming-video-pause branch from 58c8bc0 to 1cc43aa Compare July 15, 2025 08:06
Copy link

Public Interface

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self



 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func updateClientCapabilities(_ clientCapabilities: Set<ClientCapability>)async

 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

Copy link

Public Interface

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause



 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func updateClientCapabilities(_ clientCapabilities: Set<ClientCapability>)async

Copy link
Contributor

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

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

We should have this on by default, without it, it breaks the experience on low-end networks. We can offer an opt out if needed.

@ipavlidakis
Copy link
Contributor Author

We should have this on by default, without it, it breaks the experience on low-end networks. We can offer an opt out if needed.

There is opt-out as integrators can simply pass an empty array for the clientCapabilities. Let me align that with React too.

@ipavlidakis ipavlidakis force-pushed the enhancement/incoming-video-pause branch from fee2556 to d65a70a Compare July 15, 2025 10:09
Copy link
Contributor

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

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

Looks good codewise ✅ Let's test it a bit and also check the failing tests.

Copy link

Public Interface

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause



 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func updateClientCapabilities(_ clientCapabilities: Set<ClientCapability>)async

@ipavlidakis ipavlidakis force-pushed the enhancement/incoming-video-pause branch from d65a70a to 6d66e1f Compare July 15, 2025 11:28
Copy link

Public Interface

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self



 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func updateClientCapabilities(_ clientCapabilities: Set<ClientCapability>)async

Copy link

Public Interface

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause



 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func updateClientCapabilities(_ clientCapabilities: Set<ClientCapability>)async

Copy link

Public Interface

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self



 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func enableClientCapabilities(_ capabilities: Set<ClientCapability>)async 
+   public func disableClientCapabilities(_ capabilities: Set<ClientCapability>)async

 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

@ipavlidakis ipavlidakis force-pushed the enhancement/incoming-video-pause branch from 9e6ad49 to 4883ad4 Compare July 17, 2025 11:00
Copy link

Public Interface

+ extension TrackType  
+ 
+   public static let audio: Self
+   public static let video: Self
+   public static let screenshare: Self

+ public struct TrackType: RawRepresentable, Codable, Hashable, ExpressibleByStringLiteral, Sendable  
+ 
+   public let rawValue: String
+   
+ 
+   public init(rawValue: String)
+   public init(stringLiteral value: String)

+ public enum ClientCapability: Hashable, Sendable, CaseIterable  
+ 
+   case subscriberVideoPause



 public struct CallParticipant: Identifiable, Sendable, Hashable  
-   public var userId: String
+   public var pausedTracks: Set<TrackType>
-   public var name: String
+   public var userId: String
-   public var profileImageURL: URL?
+   public var name: String
-   public var isPinned: Bool
+   public var profileImageURL: URL?
-   public var isPinnedRemotely: Bool
+   public var isPinned: Bool
-   public var shouldDisplayTrack: Bool
+   public var isPinnedRemotely: Bool
-   
+   public var shouldDisplayTrack: Bool
- 
+   
-   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?)
+ 
-   
+   public init(id: String,userId: String,roles: [String],name: String,profileImageURL: URL?,trackLookupPrefix: String?,hasVideo: Bool,hasAudio: Bool,isScreenSharing: Bool,showTrack: Bool,track: RTCVideoTrack? = nil,trackSize: CGSize = CGSize(width: 1024, height: 720),screenshareTrack: RTCVideoTrack? = nil,isSpeaking: Bool = false,isDominantSpeaker: Bool,sessionId: String,connectionQuality: ConnectionQuality,joinedAt: Date,audioLevel: Float,audioLevels: [Float],pin: PinInfo?,pausedTracks: Set<TrackType>)
- 
+   
-   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
+ 
-   public func withUpdated(trackSize: CGSize)-> CallParticipant
+   public static func ==(lhs: CallParticipant,rhs: CallParticipant)-> Bool
-   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(trackSize: CGSize)-> CallParticipant
-   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
+   public func withUpdated(track: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(audio: Bool)-> CallParticipant
+   public func withUpdated(screensharingTrack: RTCVideoTrack?)-> CallParticipant
-   public func withUpdated(video: Bool)-> CallParticipant
+   public func withUpdated(audio: Bool)-> CallParticipant
-   public func withUpdated(screensharing: Bool)-> CallParticipant
+   public func withUpdated(video: Bool)-> CallParticipant
-   public func withUpdated(showTrack: Bool)-> CallParticipant
+   public func withUpdated(screensharing: Bool)-> CallParticipant
-   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
+   public func withUpdated(showTrack: Bool)-> CallParticipant
-   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
+   public func withUpdated(trackLookupPrefix: String)-> CallParticipant
-   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
+   public func withUpdated(isSpeaking: Bool,audioLevel: Float)-> CallParticipant
-   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(dominantSpeaker: Bool)-> CallParticipant
-   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withUpdated(connectionQuality: ConnectionQuality)-> CallParticipant
+   public func withUpdated(pin: PinInfo?)-> CallParticipant
+   public func withPausedTrack(_ trackType: TrackType)-> CallParticipant
+   public func withUnpausedTrack(_ trackType: TrackType)-> CallParticipant

 public class Call: @unchecked Sendable, WSEventsSubscriber  
+   public func enableClientCapabilities(_ capabilities: Set<ClientCapability>)async 
+   public func disableClientCapabilities(_ capabilities: Set<ClientCapability>)async

Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants