// Set all incoming videos to a low resolution
await call.setIncomingVideoQualitySettings(
.manual(
group: .all,
targetSize: CGSize(width: 640, height: 480)
)
)
/// Set incoming videos of specific users to a low resolution
// let otherParticipant: CallParticipant = ...
await call.setIncomingVideoQualitySettings(
.manual(
group: .custom(sessionIds: [otherParticipant.sessionId]),
targetSize: CGSize(width: 640, height: 480)
)
)
// Disable incoming video for all participants
await call.setIncomingVideoQualitySettings(.disabled(group: .all))
// Disable incoming video for one specific participant
// let otherParticipant: CallParticipant = ...
await call.setIncomingVideoQualitySettings(.disabled(group: .custom(sessionIds: [otherParticipant.sessionId])))
Manual Video Quality Selection
By default, our SDK chooses the incoming video quality that best matches the size of a video element for a given participant. It makes less sense to waste bandwidth receiving Full HD video when it’s going to be displayed in a 320 by 240 pixel rectangle.
However, it’s still possible to override this behavior and manually request higher resolution video for better quality, or lower resolution to save bandwidth. It’s also possible to disable incoming video altogether for an audio-only experience.
Actual incoming video quality depends on a number of factors, such as the quality of the source video, and network conditions. Manual video quality selection allows you to specify your preference, while the actual resolution is automatically selected from the available resolutions to match that preference as closely as possible.
In this article we’ll build a UI control for manual video quality selection.
Getting and Setting Incoming Video Settings
To get the current incoming video quality settings, we will use the setIncomingVideoQualitySettings method on Call
to set it and the Call.state.incomingVideoQualitySettings
to access it. The value is of type incomingVideoQualitySettings
which is an enum with the following case:
.none
As the name suggests, it means that we have no preference on the incoming video quality settings. In that way we allow the SDK to take control and decide the best possible settings.
.manual(group: Group, targetSize: CGSize)
Allows manual control over the enabled video streams and based on the group applies the provided targetSize
for the video, if required.
.disabled(group: Group)
Disables video streams for the specified group of session IDs.
The Group
type is another enum that is defined with the following cases:
Group.all
This group evaluates as true for every given participant.
Group.custom(sessionIds: Set<String>)
While this group evalues as true only for the participants whose sessionIds are contained from the provided set.
With that in mind we can do the following:
Even though IncomingVideoQualitySettings
provides a lot of flexibility, in this cookbook we assume that the preferences apply to all call participants.
Building Incoming Video Quality Selector
Now we’re ready to build a UI control to display and change the incoming video quality.
import Foundation
import StreamVideo
import StreamVideoSwiftUI
import SwiftUI
struct IncomingVideoQualitySelector: View {
enum ManualQuality: Hashable {
case auto
case fourK
case fullHD
case HD
case SD
case dataSaver
case disabled
var policy: IncomingVideoQualitySettings {
switch self {
case .auto:
return .none
case .fourK:
return .manual(group: .all, targetSize: .init(width: 3840, height: 2160))
case .fullHD:
return .manual(group: .all, targetSize: .init(width: 1920, height: 1080))
case .HD:
return .manual(group: .all, targetSize: .init(width: 1280, height: 720))
case .SD:
return .manual(group: .all, targetSize: .init(width: 640, height: 480))
case .dataSaver:
return .manual(group: .all, targetSize: .init(width: 256, height: 144))
case .disabled:
return .disabled(group: .all)
}
}
}
@State private var isActive: Bool = false
var call: Call?
init(call: Call?) {
self.call = call
}
var body: some View {
Menu {
buttonView(for: .auto)
buttonView(for: .fourK)
buttonView(for: .fullHD)
buttonView(for: .HD)
buttonView(for: .SD)
buttonView(for: .dataSaver)
buttonView(for: .disabled)
} label: {
Button {
isActive.toggle()
} label: {
Label {
Text("Manual quality")
} icon: {
Image(systemName: "square.resize")
}
}
}
}
@MainActor
@ViewBuilder
private func buttonView(
for manualQuality: ManualQuality
) -> some View {
let title = {
switch manualQuality {
case .auto:
return "Auto quality"
case .fourK:
return "4K 2160p"
case .fullHD:
return "Full HD 1080p"
case .HD:
return "HD 720p"
case .SD:
return "SD 480p"
case .dataSaver:
return "Data saver 144p"
case .disabled:
return "Disable video"
}
}()
Button {
execute(manualQuality)
} label: {
Label {
Text(title)
} icon: {
if manualQuality.policy == call?.state.incomingVideoQualitySettings {
Image(systemName: "checkmark")
}
}
}
}
private func execute(
_ manualQuality: ManualQuality
) {
Task { @MainActor in
await call?.setIncomingVideoQualitySettings(manualQuality.policy)
}
}
}