Skip to content

[Enhancement]Implement static frame pipeline for PiP #724

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

Merged
merged 19 commits into from
Apr 22, 2025

Conversation

ipavlidakis
Copy link
Contributor

@ipavlidakis ipavlidakis commented Mar 21, 2025

🔗 Issue Links

Resolves https://linear.app/stream/issue/IOS-802/picture-in-picture-improvements
Resolves https://linear.app/stream/issue/IOS-701/[blink]picture-in-picture-black-window-when-no-one-has-video
Resolves https://linear.app/stream/issue/IOS-502/[video][reconnection]-pip-should-not-disappear-on-rejoin

🎯 Goal

Improve Picture-in-Picture by:

  • Simplifying desing
  • Enriched UI
  • Improved state management

🧪 Manual Testing Notes

  • Join a call with a few participants
  • Enter PiP
  • Toggle media between participants and observe PiP updating
  • Start/stop screensharing

☑️ 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 added the enhancement New feature or request label Mar 21, 2025
@ipavlidakis ipavlidakis self-assigned this Mar 21, 2025
Copy link

Public Interface

+ public struct PictureInPictureParticipantImageView: View  
+ 
+   public var body: some View
+   
+ 
+   public init(imageURL: URL? = nil,@ViewBuilder contentProvider: () -> some View)



 public final class SerialActorQueue: Sendable  
-   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func cancelAll()
-   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws
+   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws

Copy link

github-actions bot commented Mar 21, 2025

3 Errors
🚫 Please start subject with capital letter.
b7250dd
🚫 Please start subject with capital letter.
0054ff8
🚫 Please start subject with capital letter.
28a76c3
1 Warning
⚠️ Big PR

Generated by 🚫 Danger

@Stream-SDK-Bot
Copy link
Collaborator

Stream-SDK-Bot commented Mar 21, 2025

SDK Size

title develop branch diff status
StreamVideo 7.62 MB 7.62 MB 0 KB 🟢
StreamVideoSwiftUI 2.13 MB 2.25 MB +115 KB 🟢
StreamVideoUIKit 2.27 MB 2.38 MB +114 KB 🟢
StreamWebRTC 9.85 MB 9.85 MB 0 KB 🟢

@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from 02ce9a8 to fae5854 Compare March 26, 2025 17:00
Copy link

Public Interface

+ public struct PictureInPictureParticipantImageView: View  
+ 
+   public var body: some View
+   
+ 
+   public init(imageURL: URL? = nil,@ViewBuilder contentProvider: () -> some View)



 public final class SerialActorQueue: Sendable  
-   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func cancelAll()
-   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws
+   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws

@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch 2 times, most recently from 1a1dc2d to febb279 Compare March 27, 2025 10:22
Copy link

Public Interface

+ public struct PictureInPictureParticipantImageView: View  
+ 
+   public var body: some View
+   
+ 
+   public init(imageURL: URL? = nil,@ViewBuilder contentProvider: () -> some View)



 public final class SerialActorQueue: Sendable  
-   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func cancelAll()
-   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws
+   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws

@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from febb279 to 3a3058f Compare March 27, 2025 16:10
Copy link

Public Interface

+ public struct PictureInPictureParticipantImageView: View  
+ 
+   public var body: some View
+   
+ 
+   public init(imageURL: URL? = nil,@ViewBuilder contentProvider: () -> some View)



 public final class SerialActorQueue: Sendable  
-   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func cancelAll()
-   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws
+   public func async(file: StaticString = #file,functionName: StaticString = #function,line: UInt = #line,_ block: @Sendable @escaping () async throws -> Void)
+   public func sync(_ block: @Sendable @escaping () async throws -> Void)async throws

@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from 8b9dd39 to 8fb61a0 Compare April 2, 2025 08:20
Copy link

github-actions bot commented Apr 2, 2025

Public Interface

+ public struct PictureInPictureParticipantImageView: View  
+ 
+   public var body: some View
+   
+ 
+   public init(imageURL: URL? = nil,@ViewBuilder contentProvider: () -> some View)



@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from 8fb61a0 to c6881fd Compare April 8, 2025 19:57
Copy link

github-actions bot commented Apr 8, 2025

Public Interface

🚀 No changes affecting the public interface.

1 similar comment
Copy link

github-actions bot commented Apr 9, 2025

Public Interface

🚀 No changes affecting the public interface.

@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from a1a26ec to e6f762c Compare April 9, 2025 12:04
Copy link

github-actions bot commented Apr 9, 2025

Public Interface

🚀 No changes affecting the public interface.

1 similar comment
Copy link

github-actions bot commented Apr 9, 2025

Public Interface

🚀 No changes affecting the public interface.

@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from f12c77a to ae5dcdd Compare April 16, 2025 09:10
Copy link

Public Interface

+ public final class AnyViewFactory: ViewFactory  
+ 
+   public init(_ factory: T)
+   
+ 
+   public func makeCallControlsView(viewModel: CallViewModel)-> some View
+   public func makeOutgoingCallView(viewModel: CallViewModel)-> some View
+   public func makeJoiningCallView(viewModel: CallViewModel)-> some View
+   public func makeIncomingCallView(viewModel: CallViewModel,callInfo: IncomingCall)-> some View
+   public func makeWaitingLocalUserView(viewModel: CallViewModel)-> some View
+   public func makeVideoParticipantsView(viewModel: CallViewModel,availableFrame: CGRect,onChangeTrackVisibility: @escaping @MainActor(CallParticipant, Bool) -> Void)-> some View
+   public func makeVideoParticipantView(participant: CallParticipant,id: String,availableFrame: CGRect,contentMode: UIView.ContentMode,customData: [String: RawJSON],call: Call?)-> some View
+   public func makeVideoCallParticipantModifier(participant: CallParticipant,call: Call?,availableFrame: CGRect,ratio: CGFloat,showAllInfo: Bool)-> any ViewModifier
+   public func makeCallView(viewModel: CallViewModel)-> some View
+   public func makeMinimizedCallView(viewModel: CallViewModel)-> some View
+   public func makeCallTopView(viewModel: CallViewModel)-> some View
+   public func makeParticipantsListView(viewModel: CallViewModel)-> some View
+   public func makeScreenSharingView(viewModel: CallViewModel,screensharingSession: ScreenSharingSession,availableFrame: CGRect)-> some View
+   public func makeLobbyView(viewModel: CallViewModel,lobbyInfo: LobbyInfo,callSettings: Binding<CallSettings>)-> some View
+   public func makeReconnectionView(viewModel: CallViewModel)-> some View
+   public func makeLocalParticipantViewModifier(localParticipant: CallParticipant,callSettings: Binding<CallSettings>,call: Call?)-> any ViewModifier
+   public func makeUserAvatar(_ user: User,with options: UserAvatarViewOptions)-> some View



@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from ae5dcdd to f78066b Compare April 16, 2025 09:23
Copy link

Public Interface

+ public final class AnyViewFactory: ViewFactory  
+ 
+   public init(_ factory: T)
+   
+ 
+   public func makeCallControlsView(viewModel: CallViewModel)-> some View
+   public func makeOutgoingCallView(viewModel: CallViewModel)-> some View
+   public func makeJoiningCallView(viewModel: CallViewModel)-> some View
+   public func makeIncomingCallView(viewModel: CallViewModel,callInfo: IncomingCall)-> some View
+   public func makeWaitingLocalUserView(viewModel: CallViewModel)-> some View
+   public func makeVideoParticipantsView(viewModel: CallViewModel,availableFrame: CGRect,onChangeTrackVisibility: @escaping @MainActor(CallParticipant, Bool) -> Void)-> some View
+   public func makeVideoParticipantView(participant: CallParticipant,id: String,availableFrame: CGRect,contentMode: UIView.ContentMode,customData: [String: RawJSON],call: Call?)-> some View
+   public func makeVideoCallParticipantModifier(participant: CallParticipant,call: Call?,availableFrame: CGRect,ratio: CGFloat,showAllInfo: Bool)-> any ViewModifier
+   public func makeCallView(viewModel: CallViewModel)-> some View
+   public func makeMinimizedCallView(viewModel: CallViewModel)-> some View
+   public func makeCallTopView(viewModel: CallViewModel)-> some View
+   public func makeParticipantsListView(viewModel: CallViewModel)-> some View
+   public func makeScreenSharingView(viewModel: CallViewModel,screensharingSession: ScreenSharingSession,availableFrame: CGRect)-> some View
+   public func makeLobbyView(viewModel: CallViewModel,lobbyInfo: LobbyInfo,callSettings: Binding<CallSettings>)-> some View
+   public func makeReconnectionView(viewModel: CallViewModel)-> some View
+   public func makeLocalParticipantViewModifier(localParticipant: CallParticipant,callSettings: Binding<CallSettings>,call: Call?)-> any ViewModifier
+   public func makeUserAvatar(_ user: User,with options: UserAvatarViewOptions)-> some View

+ public protocol InternetConnectionProtocol

+ public enum Status: Equatable  
+ 
+   case unknown
+   case available(Quality)
+   case unavailable

+ public enum Quality: Equatable  
+ 
+   case great
+   case expensive
+   case constrained

+ extension InternetConnection: InjectionKey  
+ 
+   nonisolated public static var currentValue: InternetConnectionProtocol

+ extension InternetConnection.Status  
+ 
+   public var isAvailable: Bool

+ public final class InternetConnection: @unchecked Sendable



Copy link

Public Interface

+ public final class InternetConnection: @unchecked Sendable

+ public enum Quality: Equatable  
+ 
+   case great
+   case expensive
+   case constrained

+ public final class AnyViewFactory: ViewFactory  
+ 
+   public init(_ factory: T)
+   
+ 
+   public func makeCallControlsView(viewModel: CallViewModel)-> some View
+   public func makeOutgoingCallView(viewModel: CallViewModel)-> some View
+   public func makeJoiningCallView(viewModel: CallViewModel)-> some View
+   public func makeIncomingCallView(viewModel: CallViewModel,callInfo: IncomingCall)-> some View
+   public func makeWaitingLocalUserView(viewModel: CallViewModel)-> some View
+   public func makeVideoParticipantsView(viewModel: CallViewModel,availableFrame: CGRect,onChangeTrackVisibility: @escaping @MainActor(CallParticipant, Bool) -> Void)-> some View
+   public func makeVideoParticipantView(participant: CallParticipant,id: String,availableFrame: CGRect,contentMode: UIView.ContentMode,customData: [String: RawJSON],call: Call?)-> some View
+   public func makeVideoCallParticipantModifier(participant: CallParticipant,call: Call?,availableFrame: CGRect,ratio: CGFloat,showAllInfo: Bool)-> any ViewModifier
+   public func makeCallView(viewModel: CallViewModel)-> some View
+   public func makeMinimizedCallView(viewModel: CallViewModel)-> some View
+   public func makeCallTopView(viewModel: CallViewModel)-> some View
+   public func makeParticipantsListView(viewModel: CallViewModel)-> some View
+   public func makeScreenSharingView(viewModel: CallViewModel,screensharingSession: ScreenSharingSession,availableFrame: CGRect)-> some View
+   public func makeLobbyView(viewModel: CallViewModel,lobbyInfo: LobbyInfo,callSettings: Binding<CallSettings>)-> some View
+   public func makeReconnectionView(viewModel: CallViewModel)-> some View
+   public func makeLocalParticipantViewModifier(localParticipant: CallParticipant,callSettings: Binding<CallSettings>,call: Call?)-> any ViewModifier
+   public func makeUserAvatar(_ user: User,with options: UserAvatarViewOptions)-> some View

+ public protocol InternetConnectionProtocol

+ public enum Status: Equatable  
+ 
+   case unknown
+   case available(Quality)
+   case unavailable

+ extension InternetConnection: InjectionKey  
+ 
+   nonisolated public static var currentValue: InternetConnectionProtocol

+ extension InternetConnection.Status  
+ 
+   public var isAvailable: Bool



@ipavlidakis ipavlidakis marked this pull request as ready for review April 16, 2025 14:57
@ipavlidakis ipavlidakis requested a review from a team as a code owner April 16, 2025 14:57
Copy link

Public Interface

+ public enum Status: Equatable  
+ 
+   case unknown
+   case available(Quality)
+   case unavailable

+ public protocol InternetConnectionProtocol

+ public enum Quality: Equatable  
+ 
+   case great
+   case expensive
+   case constrained

+ public final class AnyViewFactory: ViewFactory  
+ 
+   public init(_ factory: T)
+   
+ 
+   public func makeCallControlsView(viewModel: CallViewModel)-> some View
+   public func makeOutgoingCallView(viewModel: CallViewModel)-> some View
+   public func makeJoiningCallView(viewModel: CallViewModel)-> some View
+   public func makeIncomingCallView(viewModel: CallViewModel,callInfo: IncomingCall)-> some View
+   public func makeWaitingLocalUserView(viewModel: CallViewModel)-> some View
+   public func makeVideoParticipantsView(viewModel: CallViewModel,availableFrame: CGRect,onChangeTrackVisibility: @escaping @MainActor(CallParticipant, Bool) -> Void)-> some View
+   public func makeVideoParticipantView(participant: CallParticipant,id: String,availableFrame: CGRect,contentMode: UIView.ContentMode,customData: [String: RawJSON],call: Call?)-> some View
+   public func makeVideoCallParticipantModifier(participant: CallParticipant,call: Call?,availableFrame: CGRect,ratio: CGFloat,showAllInfo: Bool)-> any ViewModifier
+   public func makeCallView(viewModel: CallViewModel)-> some View
+   public func makeMinimizedCallView(viewModel: CallViewModel)-> some View
+   public func makeCallTopView(viewModel: CallViewModel)-> some View
+   public func makeParticipantsListView(viewModel: CallViewModel)-> some View
+   public func makeScreenSharingView(viewModel: CallViewModel,screensharingSession: ScreenSharingSession,availableFrame: CGRect)-> some View
+   public func makeLobbyView(viewModel: CallViewModel,lobbyInfo: LobbyInfo,callSettings: Binding<CallSettings>)-> some View
+   public func makeReconnectionView(viewModel: CallViewModel)-> some View
+   public func makeLocalParticipantViewModifier(localParticipant: CallParticipant,callSettings: Binding<CallSettings>,call: Call?)-> any ViewModifier
+   public func makeUserAvatar(_ user: User,with options: UserAvatarViewOptions)-> some View

+ extension InternetConnection: InjectionKey  
+ 
+   nonisolated public static var currentValue: InternetConnectionProtocol

+ public final class InternetConnection: @unchecked Sendable

+ extension InternetConnection.Status  
+ 
+   public var isAvailable: Bool



@@ -26,7 +26,7 @@ extension Notification {
///
/// Basically, it's a wrapper over legacy monitor based on `Reachability` (iOS 11 only)
/// and default monitor based on `Network`.`NWPathMonitor` (iOS 12+).
class InternetConnection: @unchecked Sendable {
public final class InternetConnection: @unchecked Sendable {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making Internet connection public so it can be use from SwiftUI SDK. Let me know your thoughts

Copy link

Public Interface

+ public enum InternetConnectionQuality: Equatable, Sendable  
+ 
+   case great
+   case expensive
+   case constrained

+ extension InternetConnectionStatus  
+ 
+   public var isAvailable: Bool

+ public enum InternetConnectionStatus: Equatable, Sendable  
+ 
+   case unknown
+   case available(InternetConnectionQuality)
+   case unavailable

+ public protocol InternetConnectionProtocol

+ extension InternetConnection: InjectionKey  
+ 
+   nonisolated public static var currentValue: InternetConnectionProtocol



@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from e60fdda to 433f2b0 Compare April 22, 2025 09:39
Copy link

Public Interface

+ extension InternetConnectionStatus  
+ 
+   public var isAvailable: Bool

+ public protocol InternetConnectionProtocol

+ public enum InternetConnectionQuality: Equatable, Sendable  
+ 
+   case great
+   case expensive
+   case constrained

+ public enum InternetConnectionStatus: Equatable, Sendable  
+ 
+   case unknown
+   case available(InternetConnectionQuality)
+   case unavailable

+ extension InternetConnection: InjectionKey  
+ 
+   nonisolated public static var currentValue: InternetConnectionProtocol



@ipavlidakis ipavlidakis force-pushed the enhancement/picture-in-picture-static-frame branch from a95c43a to 0054ff8 Compare April 22, 2025 12:06
Copy link

Public Interface

+ public protocol InternetConnectionProtocol

+ public enum InternetConnectionStatus: Equatable, Sendable  
+ 
+   case unknown
+   case available(InternetConnectionQuality)
+   case unavailable

+ extension InternetConnection: InjectionKey  
+ 
+   nonisolated public static var currentValue: InternetConnectionProtocol

+ extension InternetConnectionStatus  
+ 
+   public var isAvailable: Bool

+ public enum InternetConnectionQuality: Equatable, Sendable  
+ 
+   case great
+   case expensive
+   case constrained



Copy link

Copy link

Public Interface

+ public enum InternetConnectionStatus: Equatable, Sendable  
+ 
+   case unknown
+   case available(InternetConnectionQuality)
+   case unavailable

+ extension InternetConnectionStatus  
+ 
+   public var isAvailable: Bool

+ extension InternetConnection: InjectionKey  
+ 
+   nonisolated public static var currentValue: InternetConnectionProtocol

+ public protocol InternetConnectionProtocol

+ public enum InternetConnectionQuality: Equatable, Sendable  
+ 
+   case great
+   case expensive
+   case constrained



@ipavlidakis ipavlidakis merged commit bb694ae into develop Apr 22, 2025
9 of 11 checks passed
@ipavlidakis ipavlidakis deleted the enhancement/picture-in-picture-static-frame branch April 22, 2025 14:32
@Stream-SDK-Bot Stream-SDK-Bot mentioned this pull request Apr 22, 2025
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