Skip to content

Commit

Permalink
Finalise changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ipavlidakis committed Feb 13, 2025
1 parent 5e174a9 commit 280d001
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 47 deletions.
5 changes: 5 additions & 0 deletions Sources/StreamVideo/Call.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,11 @@ public class Call: @unchecked Sendable, WSEventsSubscriber {

// MARK: - AudioSession

/// Updates the current audio session policy for the call.
///
/// - Parameter policy: A conforming `AudioSessionPolicy` that defines
/// the audio session configuration to be applied.
/// - Throws: An error if the update fails.
public func updateAudioSessionPolicy(_ policy: AudioSessionPolicy) async throws {
try await callController.updateAudioSessionPolicy(policy)
}
Expand Down
41 changes: 40 additions & 1 deletion Sources/StreamVideo/Utils/CurrentDevice/CurrentDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,48 @@ import Foundation
import UIKit
#endif

///
/// A class that determines the current device type by inspecting available iOS
/// or macOS APIs. It reports whether the device is a phone, pad, TV, CarPlay,
/// mac, vision, or unspecified.
///
/// This information can be used throughout the app to adjust layout and
/// functionality based on the user's device.
///
/// ```
/// let device = CurrentDevice.currentValue
/// if device.deviceType == .phone {
/// // Configure layouts for phone
/// }
/// ```
final class CurrentDevice: Sendable {
enum DeviceType { case unspecified, phone, pad, tv, carPlay, mac, vision }

/// An enumeration describing the type of device. Each case can guide UI
/// or behavior adjustments. For example, `.phone` might use a phone layout.
enum DeviceType {
/// The type was not determined or is unknown.
case unspecified
/// The current device is an iPhone or iPod touch.
case phone
/// The current device is an iPad.
case pad
/// The current device is an Apple TV.
case tv
/// The current device is CarPlay.
case carPlay
/// The current device is a Mac.
case mac
/// The current device is Vision Pro.
case vision
}

/// The identified `DeviceType` for the current environment.
let deviceType: DeviceType

/// Creates a `CurrentDevice` by inspecting the user interface idiom.
/// - Important: On platforms where UIKit is unavailable, the type defaults
/// to `.mac` (AppKit) or `.unspecified`.

private init() {
#if canImport(UIKit)
deviceType = switch UIDevice.current.userInterfaceIdiom {
Expand All @@ -38,5 +75,7 @@ extension CurrentDevice: InjectionKey {
}

extension InjectedValues {
/// Retrieves the shared `CurrentDevice` instance. This can be used to query
/// the device type at runtime.
var currentDevice: CurrentDevice { Self[CurrentDevice.self] }
}
97 changes: 51 additions & 46 deletions Sources/StreamVideo/WebRTC/v2/WebRTCStateAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,29 +118,8 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
self.videoCaptureSessionProvider = videoCaptureSessionProvider
self.screenShareSessionProvider = screenShareSessionProvider

audioSession.delegate = self
Task {
await $callSettings
.removeDuplicates()
.sinkTask { [weak audioSession] in
do {
try await audioSession?.didUpdateCallSettings($0)
} catch {
log.error(error)
}
}
.store(in: disposableBag)

await $ownCapabilities
.removeDuplicates()
.sinkTask { [weak audioSession] in
do {
try await audioSession?.didUpdateOwnCapabilities($0)
} catch {
log.error(error)
}
}
.store(in: disposableBag)
await configureAudioSession()
}
}

Expand Down Expand Up @@ -512,6 +491,34 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
previousParticipantOperation = newTask
}

/// Assigns media tracks to participants based on their media type.
/// - Parameter participants: The storage containing participant information.
/// - Returns: An updated participants storage with assigned tracks.
func assignTracks(
on participants: ParticipantsStorage
) -> ParticipantsStorage {
/// Reduces the participants to a new storage with updated tracks.
participants.reduce(into: ParticipantsStorage()) { partialResult, entry in
var newParticipant = entry
.value
/// Updates the participant with a video track if available.
.withUpdated(track: track(for: entry.value, of: .video) as? RTCVideoTrack)
/// Updates the participant with a screensharing track if available.
.withUpdated(screensharingTrack: track(for: entry.value, of: .screenshare) as? RTCVideoTrack)

/// For participants other than the local one, we check if the incomingVideoQualitySettings
/// provide additional limits.
if
newParticipant.sessionId != sessionID,
incomingVideoQualitySettings.isVideoDisabled(for: entry.value.sessionId)
{
newParticipant = newParticipant.withUpdated(track: nil)
}

partialResult[entry.key] = newParticipant
}
}

// MARK: - Private Helpers

/// Handles track events when they are added or removed from peer connections.
Expand Down Expand Up @@ -557,32 +564,30 @@ actor WebRTCStateAdapter: ObservableObject, StreamAudioSessionAdapterDelegate {
)
}

/// Assigns media tracks to participants based on their media type.
/// - Parameter participants: The storage containing participant information.
/// - Returns: An updated participants storage with assigned tracks.
func assignTracks(
on participants: ParticipantsStorage
) -> ParticipantsStorage {
/// Reduces the participants to a new storage with updated tracks.
participants.reduce(into: ParticipantsStorage()) { partialResult, entry in
var newParticipant = entry
.value
/// Updates the participant with a video track if available.
.withUpdated(track: track(for: entry.value, of: .video) as? RTCVideoTrack)
/// Updates the participant with a screensharing track if available.
.withUpdated(screensharingTrack: track(for: entry.value, of: .screenshare) as? RTCVideoTrack)
private func configureAudioSession() {
audioSession.delegate = self

/// For participants other than the local one, we check if the incomingVideoQualitySettings
/// provide additional limits.
if
newParticipant.sessionId != sessionID,
incomingVideoQualitySettings.isVideoDisabled(for: entry.value.sessionId)
{
newParticipant = newParticipant.withUpdated(track: nil)
$callSettings
.removeDuplicates()
.sinkTask { [weak audioSession] in
do {
try await audioSession?.didUpdateCallSettings($0)
} catch {
log.error(error)
}
}

partialResult[entry.key] = newParticipant
}
.store(in: disposableBag)

$ownCapabilities
.removeDuplicates()
.sinkTask { [weak audioSession] in
do {
try await audioSession?.didUpdateOwnCapabilities($0)
} catch {
log.error(error)
}
}
.store(in: disposableBag)
}

// MARK: - AudioSessionDelegate
Expand Down

0 comments on commit 280d001

Please sign in to comment.