Skip to content

Commit

Permalink
fix: respecting default audio output setting (#608)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brazol committed Mar 25, 2024
1 parent d25a9a1 commit 9adbe5d
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 8 deletions.
29 changes: 27 additions & 2 deletions packages/stream_video/lib/src/call/call.dart
Expand Up @@ -270,8 +270,7 @@ class Call {

set connectOptions(CallConnectOptions connectOptions) {
final status = _status.value;
if (status == _ConnectionStatus.connecting ||
status == _ConnectionStatus.connected) {
if (status == _ConnectionStatus.connected) {
_logger.w(
() => '[setConnectOptions] rejected (connectOptions must be'
' set before invoking `connect`)',
Expand Down Expand Up @@ -976,6 +975,17 @@ class Call {
return [...?_session?.getTracks(trackIdPrefix)];
}

void _setDefaultConnectOptions(CallSettings settings) {
connectOptions = connectOptions.copyWith(
camera: TrackOption.fromSetting(
enabled: settings.video.cameraDefaultOn,
),
microphone: TrackOption.fromSetting(
enabled: settings.audio.micDefaultOn,
),
);
}

Future<void> _applyConnectOptions() async {
_logger.d(() => '[applyConnectOptions] connectOptions: $_connectOptions');
await _applyCameraOption(_connectOptions.camera);
Expand Down Expand Up @@ -1192,11 +1202,26 @@ class Call {
custom: custom,
);

final mediaDevicesResult =
await RtcMediaDeviceNotifier.instance.enumerateDevices();
final mediaDevices = mediaDevicesResult.fold(
success: (success) => success.data,
failure: (failure) => <RtcMediaDevice>[],
);

return response.fold(
success: (it) {
_setDefaultConnectOptions(it.data.data.metadata.settings);

_stateManager.lifecycleCallCreated(
CallCreated(it.data.data),
ringing: ringing,
audioOutputs: mediaDevices
.where((d) => d.kind == RtcMediaDeviceKind.audioOutput)
.toList(),
audioInputs: mediaDevices
.where((d) => d.kind == RtcMediaDeviceKind.audioInput)
.toList(),
);
_logger.v(() => '[getOrCreate] completed: ${it.data}');
return it;
Expand Down
4 changes: 3 additions & 1 deletion packages/stream_video/lib/src/call/call_connect_options.dart
Expand Up @@ -18,7 +18,6 @@ class CallConnectOptions with EquatableMixin {
TrackOption? camera,
TrackOption? microphone,
TrackOption? screenShare,
Duration? dropTimeout,
}) {
return CallConnectOptions(
camera: camera ?? this.camera,
Expand All @@ -43,6 +42,9 @@ class CallConnectOptions with EquatableMixin {
abstract class TrackOption with EquatableMixin {
const TrackOption();

factory TrackOption.fromSetting({required bool enabled}) =>
enabled ? TrackOption.enabled() : TrackOption.disabled();

factory TrackOption.enabled() {
return TrackEnabled._instance;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/stream_video/lib/src/call/session/call_session.dart
Expand Up @@ -167,6 +167,15 @@ class CallSession extends Disposable {
if (CurrentPlatform.isIos) {
await rtcManager?.setAppleAudioConfiguration();
}

//FIXME: This is a temporary fix for the issue where the audio output device is not set correctly
// we should remove the delay and figure out why it's not setting the device without it
unawaited(
Future.delayed(const Duration(milliseconds: 250), () async {
await _applyCurrentAudioOutputDevice();
}),
);

_logger.v(() => '[start] completed');
return const Result.success(none);
} catch (e, stk) {
Expand Down
@@ -1,4 +1,5 @@
import 'package:state_notifier/state_notifier.dart';
import 'package:collection/collection.dart';

import '../../../../stream_video.dart';
import '../../../action/internal/lifecycle_action.dart';
Expand Down Expand Up @@ -101,7 +102,26 @@ mixin StateLifecycleMixin on StateNotifier<CallState> {
void lifecycleCallCreated(
CallCreated stage, {
bool ringing = false,
List<RtcMediaDevice>? audioOutputs,
List<RtcMediaDevice>? audioInputs,
}) {
final defaultAudioOutput = audioOutputs?.firstWhereOrNull((device) {
if (stage.data.metadata.settings.audio.defaultDevice ==
AudioSettingsRequestDefaultDeviceEnum.speaker) {
return device.id.equalsIgnoreCase(
AudioSettingsRequestDefaultDeviceEnum.speaker.value,
);
}

return !device.id.equalsIgnoreCase(
AudioSettingsRequestDefaultDeviceEnum.speaker.value,
);
});

final defaultAudioInput = audioInputs
?.firstWhereOrNull((d) => d.label == defaultAudioOutput?.label) ??
audioInputs?.firstOrNull;

_logger.d(() => '[lifecycleCallCreated] ringing: $ringing, state: $state');
state = state.copyWith(
status: stage.data.toCallStatus(state: state, ringing: ringing),
Expand All @@ -118,6 +138,8 @@ mixin StateLifecycleMixin on StateNotifier<CallState> {
isBackstage: stage.data.metadata.details.backstage,
isBroadcasting: stage.data.metadata.details.broadcasting,
isRecording: stage.data.metadata.details.recording,
audioOutputDevice: defaultAudioOutput,
audioInputDevice: defaultAudioInput,
);
}

Expand Down Expand Up @@ -361,3 +383,7 @@ extension on CallRingingData {
}
}
}

extension on String {
bool equalsIgnoreCase(String other) => toUpperCase() == other.toUpperCase();
}
@@ -1,6 +1,7 @@
import 'package:collection/collection.dart';

import '../../../../open_api/video/coordinator/api.dart' as open;
import '../../../stream_video.dart';
import '../../errors/video_error.dart';
import '../../logger/stream_log.dart';
import '../../models/call_cid.dart';
Expand Down Expand Up @@ -176,6 +177,9 @@ extension CallSettingsExt on open.CallSettingsResponse {
accessRequestEnabled: audio.accessRequestEnabled,
opusDtxEnabled: audio.opusDtxEnabled,
redundantCodingEnabled: audio.redundantCodingEnabled,
defaultDevice: audio.defaultDevice.toDomain(),
micDefaultOn: audio.micDefaultOn,
speakerDefaultOn: audio.speakerDefaultOn,
),
video: StreamVideoSettings(
accessRequestEnabled: video.accessRequestEnabled,
Expand Down Expand Up @@ -208,6 +212,16 @@ extension CallSettingsExt on open.CallSettingsResponse {
}
}

extension on open.AudioSettingsDefaultDeviceEnum {
AudioSettingsRequestDefaultDeviceEnum toDomain() {
if (this == open.AudioSettingsDefaultDeviceEnum.speaker) {
return AudioSettingsRequestDefaultDeviceEnum.speaker;
} else {
return AudioSettingsRequestDefaultDeviceEnum.earpiece;
}
}
}

extension on open.TranscriptionSettingsModeEnum {
TranscriptionSettingsMode toDomain() {
if (this == open.TranscriptionSettingsModeEnum.autoOn) {
Expand Down
13 changes: 13 additions & 0 deletions packages/stream_video/lib/src/models/call_settings.dart
Expand Up @@ -77,17 +77,24 @@ class StreamAudioSettings extends MediaSettings {
this.opusDtxEnabled = false,
this.redundantCodingEnabled = false,
this.defaultDevice = AudioSettingsRequestDefaultDeviceEnum.speaker,
this.micDefaultOn = true,
this.speakerDefaultOn = true,
});

final bool opusDtxEnabled;
final bool redundantCodingEnabled;
final AudioSettingsRequestDefaultDeviceEnum defaultDevice;
final bool micDefaultOn;
final bool speakerDefaultOn;

@override
List<Object?> get props => [
accessRequestEnabled,
opusDtxEnabled,
redundantCodingEnabled,
defaultDevice,
micDefaultOn,
speakerDefaultOn,
];

AudioSettingsRequest toOpenDto() {
Expand All @@ -96,6 +103,8 @@ class StreamAudioSettings extends MediaSettings {
accessRequestEnabled: accessRequestEnabled,
opusDtxEnabled: opusDtxEnabled,
redundantCodingEnabled: redundantCodingEnabled,
micDefaultOn: micDefaultOn,
speakerDefaultOn: speakerDefaultOn,
);
}
}
Expand All @@ -104,20 +113,24 @@ class StreamVideoSettings extends MediaSettings {
const StreamVideoSettings({
super.accessRequestEnabled = false,
this.enabled = false,
this.cameraDefaultOn = true,
});

final bool enabled;
final bool cameraDefaultOn;

@override
List<Object?> get props => [
accessRequestEnabled,
enabled,
cameraDefaultOn,
];

VideoSettingsRequest toOpenDto() {
return VideoSettingsRequest(
enabled: enabled,
accessRequestEnabled: accessRequestEnabled,
cameraDefaultOn: cameraDefaultOn,
);
}
}
Expand Down
Expand Up @@ -26,17 +26,19 @@ class RtcMediaDevice with EquatableMixin {
required this.id,
required this.label,
required this.kind,
this.groupId,
});

final String id;
final String label;
final String? groupId;
final RtcMediaDeviceKind kind;

@override
String toString() {
return 'RtcMediaDevice{id: $id, label: $label, kind: $kind}';
return 'RtcMediaDevice{id: $id, label: $label, groupId: $groupId, kind: $kind}';
}

@override
List<Object?> get props => [id, kind, label];
List<Object?> get props => [id, kind, groupId, label];
}
Expand Up @@ -37,6 +37,7 @@ class RtcMediaDeviceNotifier {
return RtcMediaDevice(
id: it.deviceId,
label: it.label,
groupId: it.groupId,
kind: RtcMediaDeviceKind.fromAlias(it.kind),
);
});
Expand Down
Expand Up @@ -47,7 +47,7 @@ class StreamCallContainer extends StatefulWidget {
const StreamCallContainer({
super.key,
required this.call,
this.callConnectOptions = const CallConnectOptions(),
this.callConnectOptions,
this.onBackPressed,
this.onLeaveCallTap,
this.onAcceptCallTap,
Expand All @@ -62,7 +62,7 @@ class StreamCallContainer extends StatefulWidget {
final Call call;

/// Options used while connecting to the call.
final CallConnectOptions callConnectOptions;
final CallConnectOptions? callConnectOptions;

/// The action to perform when the back button is pressed.
final VoidCallback? onBackPressed;
Expand Down Expand Up @@ -161,7 +161,9 @@ class _StreamCallContainerState extends State<StreamCallContainer> {
Future<void> _connect() async {
try {
_logger.d(() => '[connect] no args');
call.connectOptions = widget.callConnectOptions;
if (widget.callConnectOptions != null) {
call.connectOptions = widget.callConnectOptions!;
}
final result = await call.join();
_logger.v(() => '[connect] completed: $result');
} catch (e) {
Expand Down
16 changes: 16 additions & 0 deletions packages/stream_video_flutter/lib/src/call_screen/lobby_video.dart
Expand Up @@ -41,6 +41,22 @@ class _StreamLobbyVideoState extends State<StreamLobbyVideo> {
RtcLocalAudioTrack? _microphoneTrack;
RtcLocalCameraTrack? _cameraTrack;

@override
void initState() {
super.initState();

Future.delayed(Duration.zero, () {
final callSettings = widget.call.state.value.settings;
if (callSettings.audio.micDefaultOn) {
toggleMicrophone();
}

if (callSettings.video.cameraDefaultOn) {
toggleCamera();
}
});
}

Future<void> toggleCamera() async {
if (_cameraTrack != null) {
await _cameraTrack?.stop();
Expand Down

0 comments on commit 9adbe5d

Please sign in to comment.