Skip to content

Commit

Permalink
[Feature]VideoCodec preference (#583)
Browse files Browse the repository at this point in the history
  • Loading branch information
ipavlidakis authored Oct 25, 2024
1 parent 4832345 commit 00e3785
Show file tree
Hide file tree
Showing 52 changed files with 972 additions and 263 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### ✅ Added
- You can now provide the incoming video quality setting for some or all participants [#571](https://github.com/GetStream/stream-video-swift/pull/571)
- You can now set the time a user can remain in the call - after their connection disrupted - while waiting for their network connection to recover [#573](https://github.com/GetStream/stream-video-swift/pull/573)
- You can now provide the preferred Video stream codec to use [#583](https://github.com/GetStream/stream-video-swift/pull/583)

# [1.13.0](https://github.com/GetStream/stream-video-swift/releases/tag/1.13.0)
_October 08, 2024_
Expand Down
38 changes: 38 additions & 0 deletions DemoApp/Sources/Components/AppEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -475,3 +475,41 @@ extension AppEnvironment {

static var disconnectionTimeout: DisconnectionTimeout = .never
}

extension AppEnvironment {

enum PreferredVideoCodec: Hashable, Debuggable {
case h264
case vp8
case vp9
case av1

var title: String {
switch self {
case .h264:
return "h264"
case .vp8:
return "VP8"
case .vp9:
return "VP9"
case .av1:
return "AV1"
}
}

var videoCodec: VideoCodec {
switch self {
case .h264:
return .h264
case .vp8:
return .vp8
case .vp9:
return .vp9
case .av1:
return .av1
}
}
}

static var preferredVideoCodec: PreferredVideoCodec = .h264
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,13 @@ struct DemoCallingViewModifier: ViewModifier {
Task {
do {
try await streamVideo.connect()
await MainActor.run {
let call = streamVideo.call(callType: callType, callId: callId)
await call.updatePublishOptions(
preferredVideoCodec: AppEnvironment.preferredVideoCodec.videoCodec
)
_ = await Task { @MainActor in
viewModel.joinCall(callType: callType, callId: callId)
}
}.result
} catch {
log.error(error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,22 +130,25 @@ struct DetailedCallingView: View {

Button {
resignFirstResponder()
if callAction == .joinCall {
viewModel.joinCall(callType: .default, callId: text)
} else {
if callFlow == .lobby {
viewModel.enterLobby(
callType: .default,
callId: text,
members: members
)
Task {
await setPreferredVideoCodec(for: text)
if callAction == .joinCall {
viewModel.joinCall(callType: .default, callId: text)
} else {
viewModel.startCall(
callType: .default,
callId: text,
members: members,
ring: callFlow == .ringEvents
)
if callFlow == .lobby {
viewModel.enterLobby(
callType: .default,
callId: text,
members: members
)
} else {
viewModel.startCall(
callType: .default,
callId: text,
members: members,
ring: callFlow == .ringEvents
)
}
}
}
} label: {
Expand Down Expand Up @@ -209,4 +212,11 @@ struct DetailedCallingView: View {
.background(Color.clear)
.frame(maxWidth: .infinity, alignment: .leading)
}

private func setPreferredVideoCodec(for callId: String) async {
let call = streamVideo.call(callType: .default, callId: callId)
await call.updatePublishOptions(
preferredVideoCodec: AppEnvironment.preferredVideoCodec.videoCodec
)
}
}
38 changes: 26 additions & 12 deletions DemoApp/Sources/Views/CallView/CallingView/SimpleCallingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,14 @@ struct SimpleCallingView: View {

Button {
resignFirstResponder()
viewModel.enterLobby(
callType: .default,
callId: text,
members: []
)
Task {
await setPreferredVideoCodec(for: text)
viewModel.enterLobby(
callType: .default,
callId: text,
members: []
)
}
} label: {
CallButtonView(
title: "Join Call",
Expand All @@ -111,13 +114,17 @@ struct SimpleCallingView: View {

Button {
resignFirstResponder()
viewModel.startCall(
callType: .default,
callId: .unique,
members: [],
ring: false,
maxDuration: AppEnvironment.callExpiration.duration
)
Task {
let callId = String.unique
await setPreferredVideoCodec(for: callId)
viewModel.startCall(
callType: .default,
callId: callId,
members: [],
ring: false,
maxDuration: AppEnvironment.callExpiration.duration
)
}
} label: {
CallButtonView(
title: "Start New Call",
Expand Down Expand Up @@ -161,6 +168,13 @@ struct SimpleCallingView: View {
}
}
}

private func setPreferredVideoCodec(for callId: String) async {
let call = streamVideo.call(callType: .default, callId: callId)
await call.updatePublishOptions(
preferredVideoCodec: AppEnvironment.preferredVideoCodec.videoCodec
)
}
}

extension URL: Identifiable {
Expand Down
42 changes: 26 additions & 16 deletions DemoApp/Sources/Views/Login/DebugMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ struct DebugMenu: View {

@Injected(\.colors) var colors

private var appState: AppState = .shared
private var appState = AppState.shared

@State private var loggedInView: AppEnvironment.LoggedInView = AppEnvironment.loggedInView {
@State private var loggedInView = AppEnvironment.loggedInView {
didSet { AppEnvironment.loggedInView = loggedInView }
}

@State private var baseURL: AppEnvironment.BaseURL = AppEnvironment.baseURL {
@State private var baseURL = AppEnvironment.baseURL {
didSet {
switch baseURL {
case .pronto:
Expand All @@ -40,11 +40,11 @@ struct DebugMenu: View {
}
}

@State private var supportedDeeplinks: [AppEnvironment.SupportedDeeplink] = AppEnvironment.supportedDeeplinks {
@State private var supportedDeeplinks = AppEnvironment.supportedDeeplinks {
didSet { AppEnvironment.supportedDeeplinks = supportedDeeplinks }
}

@State private var performanceTrackerVisibility: AppEnvironment.PerformanceTrackerVisibility = AppEnvironment
@State private var performanceTrackerVisibility = AppEnvironment
.performanceTrackerVisibility {
didSet {
switch performanceTrackerVisibility {
Expand All @@ -68,35 +68,39 @@ struct DebugMenu: View {
didSet { AppEnvironment.pictureInPictureIntegration = pictureInPictureIntegration }
}

@State private var tokenExpiration: AppEnvironment.TokenExpiration = AppEnvironment.tokenExpiration {
@State private var tokenExpiration = AppEnvironment.tokenExpiration {
didSet { AppEnvironment.tokenExpiration = tokenExpiration }
}

@State private var callExpiration: AppEnvironment.CallExpiration = AppEnvironment.callExpiration {
@State private var callExpiration = AppEnvironment.callExpiration {
didSet { AppEnvironment.callExpiration = callExpiration }
}

@State private var disconnectionTimeout: AppEnvironment.DisconnectionTimeout = AppEnvironment.disconnectionTimeout {
@State private var disconnectionTimeout = AppEnvironment.disconnectionTimeout {
didSet { AppEnvironment.disconnectionTimeout = disconnectionTimeout }
}

@State private var isLogsViewerVisible: Bool = false
@State private var isLogsViewerVisible = false

@State private var presentsCustomEnvironmentSetup: Bool = false
@State private var presentsCustomEnvironmentSetup = false

@State private var customCallExpirationValue: Int = 0
@State private var presentsCustomCallExpiration: Bool = false
@State private var customCallExpirationValue = 0
@State private var presentsCustomCallExpiration = false

@State private var customTokenExpirationValue: Int = 0
@State private var presentsCustomTokenExpiration: Bool = false
@State private var customTokenExpirationValue = 0
@State private var presentsCustomTokenExpiration = false

@State private var customDisconnectionTimeoutValue: TimeInterval = 0
@State private var presentsCustomDisconnectionTimeout: Bool = false
@State private var presentsCustomDisconnectionTimeout = false

@State private var autoLeavePolicy: AppEnvironment.AutoLeavePolicy = AppEnvironment.autoLeavePolicy {
@State private var autoLeavePolicy = AppEnvironment.autoLeavePolicy {
didSet { AppEnvironment.autoLeavePolicy = autoLeavePolicy }
}

@State private var preferredVideoCodec = AppEnvironment.preferredVideoCodec {
didSet { AppEnvironment.preferredVideoCodec = preferredVideoCodec }
}

var body: some View {
Menu {
makeMenu(
Expand Down Expand Up @@ -163,6 +167,12 @@ struct DebugMenu: View {
label: "Disconnection Timeout"
) { self.disconnectionTimeout = $0 }

makeMenu(
for: [.h264, .vp8, .vp9, .av1],
currentValue: preferredVideoCodec,
label: "Preferred Video Codec"
) { self.preferredVideoCodec = $0 }

makeMenu(
for: [.visible, .hidden],
currentValue: performanceTrackerVisibility,
Expand Down
29 changes: 26 additions & 3 deletions DemoApp/Sources/Views/StatsView/DemoStatsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ struct DemoStatsView: View {
viewModel,
title: "PUBLISH RESOLUTION",
value: "none",
titleTransformer: { report in
let codec = report?
.publisherBaseStats
.last?
.codec
if let codec = codec?.split(separator: "/").last {
return "PUBLISH RESOLUTION(\(codec))"
} else {
return "PUBLISH RESOLUTION"
}
},
valueTransformer: { resolutionFormatter(from: $0?.publisherStats) }
)
} _: {
Expand Down Expand Up @@ -258,21 +269,32 @@ private struct DemoStatView<Value: Comparable>: View {

@ObservedObject private var viewModel: CallViewModel
private var cancellable: AnyCancellable?
private var titleCancellable: AnyCancellable?

@Published var title: String
@Published var value: Value
@Published var previousValue: Value

init(
viewModel: CallViewModel,
title: String = "",
titleTransformer: @escaping (CallStatsReport?) -> String = { _ in "" },
value: Value,
valueTransformer: @escaping (CallStatsReport?) -> Value
) {
self.viewModel = viewModel
self.title = title
title = titleTransformer(nil)
self.value = value
previousValue = value

titleCancellable = viewModel
.call?
.state
.$statsReport
.receive(on: DispatchQueue.global(qos: .utility))
.map(titleTransformer)
.receive(on: DispatchQueue.main)
.assign(to: \.title, onWeak: self)

cancellable = viewModel
.call?
.state
Expand Down Expand Up @@ -339,14 +361,15 @@ private struct DemoStatView<Value: Comparable>: View {
_ callViewModel: CallViewModel,
title: String,
value: Value,
titleTransformer: ((CallStatsReport?) -> String)? = nil,
valueTransformer: @escaping (CallStatsReport?) -> Value,
presentationTransformer: @escaping (Value, Value) -> String = { newValue, _ in "\(newValue)" },
valueQualityTransformer: @escaping (Value) -> DemoStatQuality = { _ in .unknown }
) {
_viewModel = .init(
wrappedValue: .init(
viewModel: callViewModel,
title: title,
titleTransformer: { titleTransformer?($0) ?? title },
value: value,
valueTransformer: valueTransformer
)
Expand Down
16 changes: 16 additions & 0 deletions Sources/StreamVideo/Call.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,22 @@ public class Call: @unchecked Sendable, WSEventsSubscriber {
callController.setDisconnectionTimeout(timeout)
}

/// Updates video publishing options with the preferred video codec and max bitrate for video
/// streaming.
///
/// - Parameters:
/// - preferredVideoCodec: The preferred video codec (e.g., H264, VP8, VP9, AV1).
/// - maxBitrate: The maximum allowed bitrate for video streaming (defaults to 1000000).
public func updatePublishOptions(
preferredVideoCodec: VideoCodec,
maxBitrate: Int = .maxBitrate
) async {
await callController.updatePublishOptions(
preferredVideoCodec: preferredVideoCodec,
maxBitrate: maxBitrate
)
}

// MARK: - Internal

internal func update(reconnectionStatus: ReconnectionStatus) {
Expand Down
Loading

0 comments on commit 00e3785

Please sign in to comment.