Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6c01d24
feat: improve iOS render time
santhoshvai Feb 19, 2026
e4f0fd0
Merge remote-tracking branch 'origin/main' into feat/ios-render-improve
santhoshvai Feb 19, 2026
845408c
podfile lock
santhoshvai Feb 19, 2026
bb061a3
update podfile to test
santhoshvai May 12, 2026
834970d
Merge remote-tracking branch 'origin/main' into feat/ios-render-improve
santhoshvai May 12, 2026
5f68f2c
145 webrtc alpha update
santhoshvai May 13, 2026
5594bbb
RTCAudioSessionDelegate + AVAudioSession.InterruptionReason support b…
santhoshvai May 26, 2026
87ccfb4
init engine subscription
santhoshvai May 27, 2026
8ff6466
Merge branch 'ios-audio-interuption' into feat/ios-render-improve
santhoshvai May 27, 2026
78d9916
Merge remote-tracking branch 'origin/main' into feat/ios-render-improve
santhoshvai May 27, 2026
b01a9e6
wire engine subscription correctly and fix ownership lifecycle
santhoshvai May 29, 2026
6e2d4ae
feat(client): add hasInterruptedTrack helper (#2266)
oliverlaz May 28, 2026
d2b1f6a
chore(egress-composite): bump layout to 1.0.22 (#2267)
oliverlaz May 28, 2026
c98af42
chore(deps): upgrade React Native 0.85, React 19.2, Vite 8/Vitest 4, …
oliverlaz Jun 1, 2026
effda00
feat: improve iOS render time
santhoshvai Feb 19, 2026
39b7d19
Merge remote-tracking branch 'origin/main' into feat/ios-render-improve
santhoshvai Jun 2, 2026
19d3d88
serialise always
santhoshvai Jun 2, 2026
402524c
chore: lint fix
santhoshvai Jun 2, 2026
c122ab5
fix: broken setActive calls
santhoshvai Jun 3, 2026
56e44e7
clarify in docs
santhoshvai Jun 3, 2026
def94d3
Merge branch 'main' into feat/ios-render-improve
santhoshvai Jun 3, 2026
40274e8
chore: add debug logs for engine subs
santhoshvai Jun 4, 2026
a9cb8dd
fix: broken remote video in iOS PiP window
santhoshvai Jun 4, 2026
2a149f3
chore: log interruoptions in IncallManager
santhoshvai Jun 4, 2026
6a89d34
chore: remove yarnrc change
santhoshvai Jun 5, 2026
4053f64
Merge remote-tracking branch 'origin/main' into feat/ios-render-improve
santhoshvai Jun 5, 2026
f9603a9
fix: maven snapshots
santhoshvai Jun 5, 2026
0541b03
fix: maven snapshots
santhoshvai Jun 5, 2026
c334a2a
chore: dedupe yarn.lock
oliverlaz Jun 8, 2026
d827a6e
chore: add audio interruption tracing
santhoshvai Jun 8, 2026
0b2d595
update to next alpha
santhoshvai Jun 9, 2026
abd2f00
webrtc alpha bump
santhoshvai Jun 9, 2026
3389a86
Merge branch 'main' into feat/ios-render-improve
oliverlaz Jun 9, 2026
6a0a2fb
chore: bump webrtc everywhere
oliverlaz Jun 9, 2026
f531220
feat(expo-video-sample): show copyable call ID on call screens
oliverlaz Jun 9, 2026
4e49289
fix(callingx): keep mic live on cold-start CallKit accept
santhoshvai Jun 9, 2026
2878245
temp - hardcode podspec for testing
santhoshvai Jun 10, 2026
b98f067
update webrtc alpha
santhoshvai Jun 10, 2026
aaa2f3d
update to stable webrtc 145
santhoshvai Jun 11, 2026
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
6 changes: 3 additions & 3 deletions packages/noise-cancellation-react-native/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ rootProject.allprojects {
url = 'https://central.sonatype.com/repository/maven-snapshots/'
content {
// This efficiently tells Gradle to only look for this specific dependency in this repository
includeModule('io.getstream', 'stream-video-android-noise-cancellation')
includeModule('io.getstream', 'io.getstream:stream-video-android-noise-cancellation-libwebrtc')
}
}
}
Expand All @@ -86,7 +86,7 @@ rootProject.allprojects {
url = 'https://oss.sonatype.org/content/repositories/snapshots/'
content {
// This efficiently tells Gradle to only look for this specific dependency here
includeModule('io.getstream', 'stream-video-android-noise-cancellation')
includeModule('io.getstream', 'io.getstream:stream-video-android-noise-cancellation-libwebrtc')
}
}
}
Expand All @@ -107,7 +107,7 @@ println "Using targetSdkVersion: ${getExtOrIntegerDefault("targetSdkVersion")}"

dependencies {
implementation "com.facebook.react:react-native:+"
implementation 'io.getstream:stream-video-android-noise-cancellation:1.0.4-kotlin-1.9.25'
implementation 'io.getstream:stream-video-android-noise-cancellation-libwebrtc:3.0.0'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(':stream-io_react-native-webrtc')
}
Expand Down
4 changes: 2 additions & 2 deletions packages/noise-cancellation-react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@
},
"homepage": "https://github.com/GetStream/stream-video-js#readme",
"devDependencies": {
"@stream-io/react-native-webrtc": "137.2.0",
"@stream-io/react-native-webrtc": "145.0.0",
"react": "19.2.3",
"react-native": "0.85.3",
"react-native-builder-bob": "^0.41.0",
"rimraf": "^6.1.3",
"typescript": "^5.9.3"
},
"peerDependencies": {
"@stream-io/react-native-webrtc": ">=137.1.2",
"@stream-io/react-native-webrtc": "^145.0.0",
"react-native": "*"
},
"react-native-builder-bob": {
Expand Down
66 changes: 49 additions & 17 deletions packages/react-native-callingx/ios/AudioSessionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,62 @@ enum DefaultAudioDevice {

@objcMembers public class AudioSessionManager: NSObject {

private static let stateQueue = DispatchQueue(label: "io.getstream.callingx.audioSessionManager")
private static var defaultAudioDevice: DefaultAudioDevice = .speaker
private static var configuredInCurrentActivationCycle: Bool = false
public static let shared = AudioSessionManager()

public static func setDefaultAudioDeviceEndpointType(_ endpointType: String) {
/// Guards the `defaultAudioDevice` cache. Strictly an in-memory state queue —
/// session writes never run here (they'd risk a deadlock since
/// `applyCallKitConfiguration` does `stateQueue.sync` to read the cache).
private let stateQueue = DispatchQueue(label: "io.getstream.callingx.audioSessionManager")
private var defaultAudioDevice: DefaultAudioDevice = .speaker

/// Serializes engine-driven session writes against each other (multiple
/// `.willEnableAudioEngine` events in arrival order). Cross-path
/// serialization vs `stateQueue` / WebRTC's own paths is via
/// `RTCAudioSession.lockForConfiguration`, not via this queue.
private let audioSessionQueue = DispatchQueue(label: "io.getstream.callingx.audioSession")

public func setDefaultAudioDeviceEndpointType(_ endpointType: String) {
let next: DefaultAudioDevice = endpointType.lowercased() == "earpiece" ? .earpiece : .speaker
stateQueue.async { defaultAudioDevice = next }
stateQueue.async { self.defaultAudioDevice = next }
}

public static func reapplyForDidActivateIfNeeded() {
if stateQueue.sync(execute: { configuredInCurrentActivationCycle }) { return }
createAudioSessionIfNeeded()
/// Belt-and-braces config writer kept for the initial-activation window
/// (called from `CXStartCallAction.perform` / `CXAnswerCallAction.perform`).
/// Stays synchronous from the caller's perspective — `audioSessionQueue.sync`
/// blocks until configuration completes so `action.fulfill()` runs on a configured
/// session and `provider(_:didActivate:)` may fire imminently. Serializes with
/// `engineWillEnable` (e.g. work queued by `adm.reset()` runs first on the queue).
/// The engine-observer path is the authoritative reapply on subsequent activations.
public func createAudioSessionIfNeeded() {
audioSessionQueue.sync {
self.applyCallKitConfiguration()
}
}

public static func resetActivationCycle() {
stateQueue.async { configuredInCurrentActivationCycle = false }
/// Called from the AudioDeviceModule publisher's `.willEnableAudioEngine` event.
/// Reapplies the callingx audio-session configuration on every engine rebuild
/// (initial activation, interruption recovery, mode change). CallKit owns
/// activation, so we never call `setActive`.
///
/// Hops onto `audioSessionQueue` because the sink is Combine-driven with no
/// synchronous caller waiting; this serializes back-to-back engine events
/// against each other.
public func engineWillEnable() {
audioSessionQueue.async { [weak self] in
CallingxLog.audio.debugPublic("[engineWillEnable]")
self?.applyCallKitConfiguration()
}
}

public static func createAudioSessionIfNeeded() {
/// Called from the AudioDeviceModule publisher's `.didDisableAudioEngine` event.
/// CallKit owns deactivation — no-op on the CallKit path.
public func engineDidDisable() {
// No-op: CallKit's `provider(_:didDeactivate:)` handles `setActive(false)`.
}

// MARK: - Private

private func applyCallKitConfiguration() {
let currentDevice = stateQueue.sync { defaultAudioDevice }

// XCode 16 and older don't expose .allowBluetoothHFP
Expand Down Expand Up @@ -56,13 +93,8 @@ enum DefaultAudioDevice {

do {
try rtcSession.setConfiguration(rtcConfig)
// Set inside do{} so a failure leaves the flag false and the next
// didActivate reapply auto-recovers.
stateQueue.async { configuredInCurrentActivationCycle = true }
} catch {
#if DEBUG
NSLog("%@","[Callingx][createAudioSessionIfNeeded] Error: \(error)")
#endif
CallingxLog.audio.errorPublic("[createAudioSessionIfNeeded] Error: \(error)")
}
}
}
7 changes: 5 additions & 2 deletions packages/react-native-callingx/ios/Callingx.mm
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ - (void)_setupiOSWithOptions:(NSDictionary *)optionsDict {
WebRTCModule *webrtcModule = [self.moduleRegistry moduleForName:"WebRTCModule"];
_moduleImpl.webRTCModule = webrtcModule;

// Must run after webRTCModule injection: getAudioDeviceModule() depends on it.
[_moduleImpl wireEngineSubscription];

self.callKeepCallController = _moduleImpl.callKeepCallController;
self.callKeepProvider = _moduleImpl.callKeepProvider;
}
Expand Down Expand Up @@ -559,11 +562,11 @@ - (void)fulfillEndCallAction:(NSString *)callId didFail:(BOOL)didFail {

#ifdef RCT_NEW_ARCH_ENABLED
- (void)log:(NSString *)message level:(NSString *)level {
NSLog(@"[Callingx][log] %@, %@", message, level);
[CallingxLogBridge js:message level:level];
}
#else
RCT_EXPORT_METHOD(log:(NSString *)message level:(NSString *)level) {
NSLog(@"[Callingx][log] %@, %@", message, level);
[CallingxLogBridge js:message level:level];
}
#endif

Expand Down
8 changes: 3 additions & 5 deletions packages/react-native-callingx/ios/CallingxCall.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import Foundation
public private(set) var isSelfAnswered: Bool = false
/// Whether endCall was initiated from the app (vs system UI)
public private(set) var isSelfEnded: Bool = false
/// Whether setMutedCall was initiated from the app (vs system UI)
public private(set) var isSelfMuted: Bool = false
// Mute action-source attribution is tracked per-action by UUID in `CallingxImpl`
// (`appInitiatedMuteActionIds`), not via a per-call flag, to avoid a race between
// concurrent app-initiated mute toggles.

// MARK: - Lifecycle timestamps

Expand Down Expand Up @@ -54,17 +55,14 @@ import Foundation

public func markSelfAnswered() { isSelfAnswered = true }
public func markSelfEnded() { isSelfEnded = true }
public func markSelfMuted() { isSelfMuted = true }

public func resetSelfAnswered() { isSelfAnswered = false }
public func resetSelfEnded() { isSelfEnded = false }
public func resetSelfMuted() { isSelfMuted = false }

/// Resets all action-source flags. Called when a CXTransaction fails.
public func resetAllSelfFlags() {
isSelfAnswered = false
isSelfEnded = false
isSelfMuted = false
}

// MARK: - Lifecycle transition methods
Expand Down
Loading
Loading