Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
108e3ac
Use local webrtc
ipavlidakis Nov 7, 2025
fea0818
Implement StereoPlayoutAvailability reducer
ipavlidakis Nov 7, 2025
ca73238
Implement StoreEffects
ipavlidakis Nov 10, 2025
6c2ffd7
Improve audioOutput preference
ipavlidakis Nov 10, 2025
31b975b
Update to webrtc 137.1.0
ipavlidakis Nov 11, 2025
86bfbba
WIP
ipavlidakis Nov 11, 2025
f32d034
Restart playout whenever the currentRoute changes
ipavlidakis Nov 11, 2025
e0cba0a
SDP Munge workaround
ipavlidakis Nov 11, 2025
ae83da1
Update webrtc version
ipavlidakis Nov 11, 2025
3f83954
Add missing files
ipavlidakis Nov 11, 2025
d8d9b99
Clean up session once the call has ended
ipavlidakis Nov 13, 2025
1f5d5f3
Add reason to the AudioRoute
ipavlidakis Nov 13, 2025
1f70f70
Ensure there is always audio playback
ipavlidakis Nov 13, 2025
6c9ef01
Ensure stereo playout between device changes
ipavlidakis Nov 13, 2025
7a6b06a
Improve the routeChange reporting
ipavlidakis Nov 13, 2025
017f577
Update webrtc version to 137.0.45
ipavlidakis Nov 13, 2025
e21de6a
Log improvement
ipavlidakis Nov 14, 2025
1a40dad
Add missing reducer handling for stereo
ipavlidakis Nov 14, 2025
cc7b5b0
Remove unnecessary method
ipavlidakis Nov 14, 2025
f06bd2e
Small logging update
ipavlidakis Nov 14, 2025
22fde19
Solidify route transition (with stereo playout disabled)
ipavlidakis Nov 14, 2025
c49c938
Clean up
ipavlidakis Nov 17, 2025
6124042
Implement LIvestreamAudioSessionPolicy
ipavlidakis Nov 17, 2025
0771f0a
Solidify route change observation without stereo
ipavlidakis Nov 17, 2025
c4fbce6
StereoPlayout stability
ipavlidakis Nov 17, 2025
1ff421c
AudioSessionObserver
ipavlidakis Nov 17, 2025
f63c2fc
Improve route selection on init
ipavlidakis Nov 17, 2025
cb0e7ab
allowBluetoothHFP always (resolve warning)
ipavlidakis Nov 18, 2025
01ca136
Increase debounce time on StereoPlayout availability
ipavlidakis Nov 18, 2025
27d71af
Update to 137.0.46 and improve stereoPlayout control
ipavlidakis Nov 18, 2025
8f1e74a
Improvements
ipavlidakis Nov 20, 2025
275c58d
update webrtc to 137.0.48
ipavlidakis Nov 20, 2025
49325bb
Improvements
ipavlidakis Nov 20, 2025
ba806a2
Improve logging
ipavlidakis Nov 20, 2025
f068eb9
Update webrtc to 137.0.50
ipavlidakis Nov 20, 2025
4737e4d
Add comments
ipavlidakis Nov 21, 2025
090378f
Further improvements
ipavlidakis Nov 21, 2025
122be21
Fixes and update to webrtc 137.0.51
ipavlidakis Nov 21, 2025
6844c5d
Fix enable/disable audioOutput
ipavlidakis Nov 26, 2025
213f75f
Add comment
ipavlidakis Nov 26, 2025
853bfb5
Clean up
ipavlidakis Nov 26, 2025
c085841
Fix compilation issues on tests and mocks
ipavlidakis Nov 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions DemoApp/Sources/Components/AppEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -554,14 +554,16 @@ extension AppEnvironment {
extension AppEnvironment {

enum AudioSessionPolicyDebugConfiguration: Hashable, Debuggable, Sendable {
case `default`, ownCapabilities
case `default`, ownCapabilities, livestream

var title: String {
switch self {
case .default:
return "Default"
case .ownCapabilities:
return "OwnCapabilities"
case .livestream:
return "Livestream"
}
}

Expand All @@ -571,12 +573,14 @@ extension AppEnvironment {
return DefaultAudioSessionPolicy()
case .ownCapabilities:
return OwnCapabilitiesAudioSessionPolicy()
case .livestream:
return LivestreamAudioSessionPolicy()
}
}
}

static var audioSessionPolicy: AudioSessionPolicyDebugConfiguration = {
.default
.livestream
}()
}

Expand Down Expand Up @@ -616,7 +620,7 @@ extension AppEnvironment {
}

static var proximityPolicies: Set<ProximityPolicyDebugConfiguration> = {
[.speaker, .video]
[]
Copy link
Contributor

Choose a reason for hiding this comment

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

is this affecting stereo?

}()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import Foundation
import StreamVideo

enum LogQueue {
static let queue: Queue<LogDetails> = .init(maxCount: 3000)
#if DEBUG
private static let queueCapaity = 10000
#else
private static let queueCapaity = 1000
#endif
static let queue: Queue<LogDetails> = .init(maxCount: queueCapaity)

static func insert(_ element: LogDetails) { queue.insert(element) }

Expand Down
2 changes: 1 addition & 1 deletion DemoApp/Sources/Views/Login/DebugMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ struct DebugMenu: View {
}

makeMenu(
for: [.default, .ownCapabilities],
for: [.default, .ownCapabilities, .livestream],
currentValue: audioSessionPolicy,
label: "AudioSession policy"
) { self.audioSessionPolicy = $0 }
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/apple/swift-protobuf.git", exact: "1.30.0"),
.package(url: "https://github.com/GetStream/stream-video-swift-webrtc.git", exact: "137.0.43")
.package(url: "https://github.com/GetStream/stream-video-swift-webrtc.git", exact: "137.0.51")
],
targets: [
.target(
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ final class AudioEngineLevelNodeAdapter: AudioEngineNodeAdapting {

var subject: CurrentValueSubject<Float, Never>?

// private let publisher: (Float) -> Void
private var inputTap: AVAudioMixerNode?

/// Installs a tap on the supplied audio node to monitor input levels.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,40 @@ import Combine
import StreamWebRTC

/// Abstraction over `RTCAudioDeviceModule` so tests can provide fakes while
/// production code keeps using the WebRTC implementation.
/// production code continues to rely on the WebRTC-backed implementation.
protocol RTCAudioDeviceModuleControlling: AnyObject {
var observer: RTCAudioDeviceModuleDelegate? { get set }
var isPlaying: Bool { get }
var isRecording: Bool { get }
var isPlayoutInitialized: Bool { get }
var isRecordingInitialized: Bool { get }
var isMicrophoneMuted: Bool { get }
var isStereoPlayoutEnabled: Bool { get }
var isVoiceProcessingBypassed: Bool { get set }
var isVoiceProcessingEnabled: Bool { get }
var isVoiceProcessingAGCEnabled: Bool { get }
var prefersStereoPlayout: Bool { get set }

func reset() -> Int
func initAndStartPlayout() -> Int
func startPlayout() -> Int
func stopPlayout() -> Int
func initAndStartRecording() -> Int
func setMicrophoneMuted(_ isMuted: Bool) -> Int
func startRecording() -> Int
func stopRecording() -> Int

/// Publisher that emits whenever the microphone mute state changes.
func microphoneMutedPublisher() -> AnyPublisher<Bool, Never>
func refreshStereoPlayoutState()
}

extension RTCAudioDeviceModule: RTCAudioDeviceModuleControlling {
func microphoneMutedPublisher() -> AnyPublisher<Bool, Never> {
publisher(for: \.isMicrophoneMuted)
.eraseToAnyPublisher()
/// Convenience wrapper that mirrors the old `initPlayout` and
/// `startPlayout` sequence so the caller can request playout in one call.
func initAndStartPlayout() -> Int {
let result = initPlayout()
if result == 0 {
return startPlayout()
} else {
return result
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ extension StreamCallAudioRecorder.Namespace {
/// ensure thread safety when accessing the recorder instance.
final class AVAudioRecorderMiddleware: Middleware<StreamCallAudioRecorder.Namespace>, @unchecked Sendable {

/// Tracks which metering backend is active so we can flip between
/// `AVAudioRecorder` and the audio device module seamlessly.
enum Mode: Equatable {
case invalid
case audioRecorder(AVAudioRecorder)
Expand All @@ -39,6 +41,8 @@ extension StreamCallAudioRecorder.Namespace {

/// Subscription for publishing meter updates at refresh rate.
private var updateMetersCancellable: AnyCancellable?
/// Listens for ADM availability and pivots the metering source on the
/// fly when stereo playout is enabled.
private var audioDeviceModuleCancellable: AnyCancellable?

init(audioRecorder: AVAudioRecorder? = nil) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ extension StreamCallAudioRecorder.Namespace {

// Monitor for category changes that are incompatible with recording
cancellable = audioStore
// Observe the derived configuration so system-driven category
// changes also stop the local recorder.
.publisher(\.audioSessionConfiguration.category)
.filter { $0 != .playAndRecord && $0 != .record }
.sink { [weak self] _ in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import AVFoundation

/// Represents the audio session configuration.
public struct AudioSessionConfiguration: ReflectiveStringConvertible, Equatable, Sendable {
public struct AudioSessionConfiguration: CustomStringConvertible, Equatable, Sendable {
var isActive: Bool
/// The audio session category.
var category: AVAudioSession.Category
Expand All @@ -16,6 +16,17 @@ public struct AudioSessionConfiguration: ReflectiveStringConvertible, Equatable,
/// The audio session port override.
var overrideOutputAudioPort: AVAudioSession.PortOverride?

public var description: String {
var result = "{ "
result += "isActive:\(isActive)"
result += ", category:\(category)"
result += ", mode:\(mode)"
result += ", options:\(options)"
result += ", overrideOutputAudioPort:\(overrideOutputAudioPort)"
result += " }"
return result
}

/// Compares two `AudioSessionConfiguration` instances for equality.
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.isActive == rhs.isActive &&
Expand Down
Loading
Loading