diff --git a/.github/workflows/license.yml b/.github/workflows/license.yml index 849a5fd5..d2eb8c95 100644 --- a/.github/workflows/license.yml +++ b/.github/workflows/license.yml @@ -19,7 +19,7 @@ jobs: - name: Get changed files id: files - uses: tj-actions/changed-files@v35 + uses: tj-actions/changed-files@v42 - name: Check license headers run: | diff --git a/Examples/Conference/App/Shared/Screens/Conference/ConferenceViewModel.swift b/Examples/Conference/App/Shared/Screens/Conference/ConferenceViewModel.swift index a3569364..3d6d05f9 100644 --- a/Examples/Conference/App/Shared/Screens/Conference/ConferenceViewModel.swift +++ b/Examples/Conference/App/Shared/Screens/Conference/ConferenceViewModel.swift @@ -1,5 +1,5 @@ // -// Copyright 2022-2023 Pexip AS +// Copyright 2022-2024 Pexip AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -164,7 +164,7 @@ final class ConferenceViewModel: ObservableObject { #if os(iOS) Task { [weak audioSession] in - await audioSession?.activate(for: .call) + await audioSession?.activate(for: .videoCall()) } #endif } diff --git a/Sources/PexipMedia/AudioConfiguration.swift b/Sources/PexipMedia/AudioConfiguration.swift index 92336c2b..972ab8d4 100644 --- a/Sources/PexipMedia/AudioConfiguration.swift +++ b/Sources/PexipMedia/AudioConfiguration.swift @@ -1,5 +1,5 @@ // -// Copyright 2023 Pexip AS +// Copyright 2023-2024 Pexip AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -44,24 +44,43 @@ public extension AudioConfiguration { options: [] ) - static let call = AudioConfiguration( - category: .playAndRecord, - mode: .voiceChat, - options: [ + static func audioCall( + mixWithOthers: Bool = false + ) -> AudioConfiguration { + call( + withMode: .voiceChat, + mixWithOthers: mixWithOthers + ) + } + + static func videoCall( + mixWithOthers: Bool = false + ) -> AudioConfiguration { + call( + withMode: .videoChat, + mixWithOthers: mixWithOthers + ) + } + + private static func call( + withMode mode: AVAudioSession.Mode, + mixWithOthers: Bool + ) -> AudioConfiguration { + var options: AVAudioSession.CategoryOptions = [ .allowBluetooth, .allowBluetoothA2DP ] - ) - static let screenCapture = AudioConfiguration( - category: .playAndRecord, - mode: .voiceChat, - options: [ - .allowBluetooth, - .allowBluetoothA2DP, - .mixWithOthers - ] - ) + if mixWithOthers { + options.insert(.mixWithOthers) + } + + return AudioConfiguration( + category: .playAndRecord, + mode: mode, + options: options + ) + } } #endif diff --git a/Sources/PexipMedia/MediaConnectionConfig.swift b/Sources/PexipMedia/MediaConnectionConfig.swift index 91c07224..fe25494f 100644 --- a/Sources/PexipMedia/MediaConnectionConfig.swift +++ b/Sources/PexipMedia/MediaConnectionConfig.swift @@ -1,5 +1,5 @@ // -// Copyright 2022-2023 Pexip AS +// Copyright 2022-2024 Pexip AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -45,6 +45,11 @@ public struct MediaConnectionConfig { /// Sets whether presentation will be mixed with main video feed. public let presentationInMain: Bool + #if os(iOS) + /// Sets whether audio session is managed outside of the SDK. + public var externalAudioManagement = false + #endif + /** Creates a new instance of ``MediaConnectionConfig``. diff --git a/Sources/PexipRTC/Internal/PeerConnection/PeerConnection.swift b/Sources/PexipRTC/Internal/PeerConnection/PeerConnection.swift index dc3914c9..33570b16 100644 --- a/Sources/PexipRTC/Internal/PeerConnection/PeerConnection.swift +++ b/Sources/PexipRTC/Internal/PeerConnection/PeerConnection.swift @@ -1,5 +1,5 @@ // -// Copyright 2023 Pexip AS +// Copyright 2023-2024 Pexip AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -222,6 +222,14 @@ actor PeerConnection { logger?.debug("Data channel - new data channel created.") } + func canSendOrReceive(_ content: MediaContent) -> Bool { + if let transceiver = transceivers[content] { + return transceiver.canSend || transceiver.canReceive + } else { + return false + } + } + // MARK: - Events private func subscribeToEvents() { diff --git a/Sources/PexipRTC/Internal/PeerConnection/Transceiver.swift b/Sources/PexipRTC/Internal/PeerConnection/Transceiver.swift index 6735a9d1..3f6c2458 100644 --- a/Sources/PexipRTC/Internal/PeerConnection/Transceiver.swift +++ b/Sources/PexipRTC/Internal/PeerConnection/Transceiver.swift @@ -1,5 +1,5 @@ // -// Copyright 2023 Pexip AS +// Copyright 2023-2024 Pexip AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -41,6 +41,10 @@ final class Transceiver { transceiver.direction == .recvOnly || transceiver.direction == .sendRecv } + var canSend: Bool { + transceiver.direction == .sendOnly || transceiver.direction == .sendRecv + } + func setDirection(_ direction: RTCRtpTransceiverDirection) throws { guard transceiver.direction != direction else { return diff --git a/Sources/PexipRTC/Internal/WebRTCMediaConnection.swift b/Sources/PexipRTC/Internal/WebRTCMediaConnection.swift index 6cfdbfe0..2a07d9e8 100644 --- a/Sources/PexipRTC/Internal/WebRTCMediaConnection.swift +++ b/Sources/PexipRTC/Internal/WebRTCMediaConnection.swift @@ -1,5 +1,5 @@ // -// Copyright 2022-2023 Pexip AS +// Copyright 2022-2024 Pexip AS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -106,7 +106,9 @@ actor WebRTCMediaConnection: MediaConnection, DataSender { } #if os(iOS) - await audioSession?.activate(for: .call) + if let audioConfig = await audioConfig(mixWithOthers: false) { + await audioSession?.activate(for: audioConfig) + } #endif started = true @@ -130,7 +132,9 @@ actor WebRTCMediaConnection: MediaConnection, DataSender { outgoingIceCandidates.removeAll() #if os(iOS) - await audioSession?.deactivate() + if !config.externalAudioManagement { + await audioSession?.deactivate() + } #endif } @@ -171,9 +175,9 @@ actor WebRTCMediaConnection: MediaConnection, DataSender { try await self?.signalingChannel.releaseFloor() } #if os(iOS) - await self?.audioSession?.activate( - for: isCapturing ? .screenCapture : .call - ) + if let audioConfig = await self?.audioConfig(mixWithOthers: isCapturing) { + await self?.audioSession?.activate(for: audioConfig) + } #endif } catch { self?.logger?.error("Error on takeFloow/releaseFloor: \(error)") @@ -440,5 +444,16 @@ actor WebRTCMediaConnection: MediaConnection, DataSender { break } } + + #if os(iOS) + private func audioConfig(mixWithOthers: Bool) async -> AudioConfiguration? { + guard !config.externalAudioManagement else { + return nil + } + return await peerConnection.canSendOrReceive(.mainVideo) + ? .videoCall(mixWithOthers: mixWithOthers) + : .audioCall(mixWithOthers: mixWithOthers) + } + #endif } // swiftlint:enable type_body_length file_length