Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions packages/stream_video/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## Upcoming

### ✅ Added
* Added `audioConfigurationPolicy` to `StreamVideoOptions` - a unified audio configuration for both iOS and Android platforms. Includes predefined policies:
* `AudioConfigurationPolicy.call()` - Optimized for voice/video calls (default)
* `AudioConfigurationPolicy.livestream()` - Optimized for livestream playback
* `AudioConfigurationPolicy.custom()` - Full control over platform-specific settings with optional `basePolicy` fallback

### ⚠️ Deprecated
* Deprecated `androidAudioConfiguration` in `StreamVideoOptions`. Use `audioConfigurationPolicy` instead.

## 1.2.3

### ⚡ Performance
Expand Down
9 changes: 4 additions & 5 deletions packages/stream_video/lib/src/call/call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2022,6 +2022,7 @@ class Call {
if (CurrentPlatform.isIos) {
await _session?.rtcManager?.setAppleAudioConfiguration(
speakerOn: _connectOptions.speakerDefaultOn,
policy: _streamVideo.options.audioConfigurationPolicy,
);
}
}
Expand Down Expand Up @@ -2994,11 +2995,9 @@ class Call {

if (enabled && CurrentPlatform.isAndroid) {
try {
if (_streamVideo.options.androidAudioConfiguration != null) {
await rtc.Helper.setAndroidAudioConfiguration(
_streamVideo.options.androidAudioConfiguration!,
);
}
await rtc.Helper.setAndroidAudioConfiguration(
_streamVideo.options.audioConfigurationPolicy.getAndroidConfiguration(),
);
} catch (e) {
_logger.w(
() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ class CallSession extends Disposable {
}

Future<void> _ensureAndroidAudioConfiguration() async {
if (CurrentPlatform.isAndroid &&
_streamVideo.options.androidAudioConfiguration != null) {
if (CurrentPlatform.isAndroid) {
try {
await rtc.Helper.setAndroidAudioConfiguration(
_streamVideo.options.androidAudioConfiguration!,
_streamVideo.options.audioConfigurationPolicy
.getAndroidConfiguration(),
);
_logger.v(
() => '[_ensureAndroidAudioConfiguration] Configuration applied',
Expand Down Expand Up @@ -322,6 +322,7 @@ class CallSession extends Disposable {
rtcManager =
await rtcManagerFactory.makeRtcManager(
sfuClient: sfuClient,
streamVideo: _streamVideo,
clientDetails: clientDetails,
sessionSequence: sessionSeq,
statsOptions: statsOptions,
Expand All @@ -343,6 +344,7 @@ class CallSession extends Disposable {
rtcManager =
await rtcManagerFactory.makeRtcManager(
sfuClient: sfuClient,
streamVideo: _streamVideo,
publisherId: localTrackId,
publishOptions: joinResponseEvent.publishOptions,
clientDetails: clientDetails,
Expand Down
143 changes: 143 additions & 0 deletions packages/stream_video/lib/src/models/audio_configuration_policy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import 'package:stream_webrtc_flutter/stream_webrtc_flutter.dart' as rtc;

/// A policy that defines how audio should be configured on both iOS and Android.
///
/// Use one of the predefined policies:
/// - [AudioConfigurationPolicy.call] - Optimized for voice/video calls
/// - [AudioConfigurationPolicy.livestream] - Optimized for livestream playback
///
/// Or create a custom configuration:
/// - [AudioConfigurationPolicy.custom] - Full control over platform settings
sealed class AudioConfigurationPolicy {
const AudioConfigurationPolicy();

const factory AudioConfigurationPolicy.call() = CallAudioPolicy;

const factory AudioConfigurationPolicy.livestream() = LivestreamAudioPolicy;

/// Custom policy allowing full control over platform-specific settings.
///
/// Use this when you need specific audio configurations that differ from
/// the predefined policies.
///
/// You can provide only the configuration for the platform you want to customize,
/// and use [basePolicy] to define defaults for the other platform.
const factory AudioConfigurationPolicy.custom({
AudioConfigurationPolicy basePolicy,
rtc.AppleAudioConfiguration? appleConfiguration,
rtc.AndroidAudioConfiguration? androidConfiguration,
}) = CustomAudioPolicy;

rtc.AppleAudioConfiguration getAppleConfiguration({
bool defaultToSpeaker = false,
});

rtc.AndroidAudioConfiguration getAndroidConfiguration();
}

/// Audio policy optimized for normal video/audio calls.
class CallAudioPolicy extends AudioConfigurationPolicy {
const CallAudioPolicy();

@override
rtc.AppleAudioConfiguration getAppleConfiguration({
bool defaultToSpeaker = false,
}) {
return rtc.AppleAudioConfiguration(
appleAudioMode: defaultToSpeaker
? rtc.AppleAudioMode.videoChat
: rtc.AppleAudioMode.voiceChat,
appleAudioCategory: rtc.AppleAudioCategory.playAndRecord,
appleAudioCategoryOptions: {
if (defaultToSpeaker) rtc.AppleAudioCategoryOption.defaultToSpeaker,
rtc.AppleAudioCategoryOption.mixWithOthers,
rtc.AppleAudioCategoryOption.allowBluetooth,
rtc.AppleAudioCategoryOption.allowBluetoothA2DP,
rtc.AppleAudioCategoryOption.allowAirPlay,
},
);
}

@override
rtc.AndroidAudioConfiguration getAndroidConfiguration() {
return rtc.AndroidAudioConfiguration(
androidAudioMode: rtc.AndroidAudioMode.inCommunication,
androidAudioStreamType: rtc.AndroidAudioStreamType.voiceCall,
androidAudioAttributesUsageType:
rtc.AndroidAudioAttributesUsageType.voiceCommunication,
androidAudioAttributesContentType:
rtc.AndroidAudioAttributesContentType.speech,
androidAudioFocusMode: rtc.AndroidAudioFocusMode.gain,
forceHandleAudioRouting: true,
);
}
}

/// Audio policy optimized for livestream/broadcast scenarios.
class LivestreamAudioPolicy extends AudioConfigurationPolicy {
const LivestreamAudioPolicy();

@override
rtc.AppleAudioConfiguration getAppleConfiguration({
bool defaultToSpeaker = false,
}) {
return rtc.AppleAudioConfiguration(
appleAudioMode: rtc.AppleAudioMode.default_,
appleAudioCategory: rtc.AppleAudioCategory.playAndRecord,
appleAudioCategoryOptions: const {
rtc.AppleAudioCategoryOption.defaultToSpeaker,
rtc.AppleAudioCategoryOption.mixWithOthers,
rtc.AppleAudioCategoryOption.allowBluetooth,
rtc.AppleAudioCategoryOption.allowBluetoothA2DP,
rtc.AppleAudioCategoryOption.allowAirPlay,
},
);
}

@override
rtc.AndroidAudioConfiguration getAndroidConfiguration() {
return rtc.AndroidAudioConfiguration(
androidAudioMode: rtc.AndroidAudioMode.normal,
androidAudioStreamType: rtc.AndroidAudioStreamType.music,
androidAudioAttributesUsageType:
rtc.AndroidAudioAttributesUsageType.media,
androidAudioAttributesContentType:
rtc.AndroidAudioAttributesContentType.music,
androidAudioFocusMode: rtc.AndroidAudioFocusMode.gain,
forceHandleAudioRouting: false,
);
}
}

/// Custom audio policy with full control over platform settings.
class CustomAudioPolicy extends AudioConfigurationPolicy {
/// At least one of [appleConfiguration] or [androidConfiguration] should be
/// provided. If a configuration is not provided, [basePolicy] will be used
/// for that platform.
const CustomAudioPolicy({
this.basePolicy = const CallAudioPolicy(),
this.appleConfiguration,
this.androidConfiguration,
});

/// The base policy used for platforms where a custom configuration
/// is not provided. Defaults to [CallAudioPolicy].
final AudioConfigurationPolicy basePolicy;

final rtc.AppleAudioConfiguration? appleConfiguration;

final rtc.AndroidAudioConfiguration? androidConfiguration;

@override
rtc.AppleAudioConfiguration getAppleConfiguration({
bool defaultToSpeaker = false,
}) {
return appleConfiguration ??
basePolicy.getAppleConfiguration(defaultToSpeaker: defaultToSpeaker);
}

@override
rtc.AndroidAudioConfiguration getAndroidConfiguration() {
return androidConfiguration ?? basePolicy.getAndroidConfiguration();
}
}
Loading
Loading