init method
Initializes the media client resources.
No-op if already initialized.
Implementation
Future<void> init({FutureOr<RxChat?> Function(ChatId)? getChat}) async {
Log.debug('init()', '$runtimeType');
if (_background) {
_background = false;
_devicesSubscription = MediaUtils.onDeviceChange.listen((e) async {
Log.debug(
'onDeviceChange(${e.map((e) => e.label()).join(', ')})',
'$runtimeType',
);
await _devicesGuard.protect(() async {
if (devices.isEmpty) {
devices.value = await MediaUtils.enumerateDevices();
}
final List<DeviceDetails> previous = List.from(
devices,
growable: false,
);
devices.value = e;
final List<DeviceDetails> removed = [];
for (DeviceDetails d in previous) {
if (devices.none((p) => p.deviceId() == d.deviceId())) {
removed.add(d);
}
}
final bool audioChanged = !previous
.audio()
.map((e) => e.deviceId())
.sameAs(devices.audio().map((e) => e.deviceId()));
final bool outputChanged = !previous
.output()
.map((e) => e.deviceId())
.sameAs(devices.output().map((e) => e.deviceId()));
final bool videoChanged = !previous
.video()
.map((e) => e.deviceId())
.sameAs(devices.video().map((e) => e.deviceId()));
if (audioChanged) {
_pickAudioDevice();
}
if (outputChanged) {
_pickOutputDevice();
}
if (videoChanged) {
_pickVideoDevice(previous, removed);
}
if (!outputChanged) {
if (PlatformUtils.isIOS && !PlatformUtils.isWeb) {
if (outputDevice.value?.speaker == AudioSpeakerKind.headphones) {
final AVAudioSessionRouteDescription route =
await AVAudioSession().currentRoute;
Log.debug(
'onDeviceChange() -> route is ${route.outputs.map((e) => '{id: ${e.uid}, name: ${e.portName}, type: ${e.portType.name}}').join(', ')}',
'$runtimeType',
);
if (route.outputs.none(
(e) => outputDevice.value?.label() == e.portName,
)) {
Log.debug(
'onDeviceChange() -> no device with `${outputDevice.value?.label()}` is within `route.outputs`',
'$runtimeType',
);
await _pickOutputDevice(
without: [?outputDevice.value?.label()],
);
}
}
}
}
});
});
_displaysSubscription = MediaUtils.onDisplayChange.listen((e) async {
Log.debug(
'onDisplayChange(${e.map((e) => e.title() ?? e.deviceId())})',
'$runtimeType',
);
final List<MediaDisplayDetails> previous = List.from(
displays,
growable: false,
);
displays.value = e;
final List<MediaDisplayDetails> removed = [];
for (MediaDisplayDetails d in previous) {
if (displays.none((p) => p.deviceId() == d.deviceId())) {
removed.add(d);
}
}
_pickScreenDevice(removed);
});
// Puts the members of the provided [chat] to the [members] through
// [_addDialing].
Future<void> addDialingsFrom(RxChat? chat) async {
if (chat == null) {
return;
}
final int membersCount = chat.chat.value.membersCount;
final bool shouldAddDialed =
(outgoing && conversationStartedAt == null) ||
chat.chat.value.isDialog;
// Dialed [User]s should be added, if [membersCount] is less than a page
// of [Chat.members].
if (membersCount <= chat.members.perPage && shouldAddDialed) {
if (chat.members.length < membersCount) {
await chat.members.around();
}
// If [connected], then the dialed [User] will be added in [connect],
// when handling [ChatMembersDialedAll].
if (!connected) {
for (UserId e in chat.members.items.keys.where(
(e) => e != me.id.userId,
)) {
_addDialing(e);
}
}
}
}
// Retrieve the [RxChat] this [OngoingCall] is happening in to add its
// members to the [members] in redialing mode as fast as possible.
final FutureOr<RxChat?>? chatOrFuture = getChat?.call(chatId.value);
if (chatOrFuture is RxChat?) {
addDialingsFrom(chatOrFuture);
} else {
chatOrFuture.then(addDialingsFrom);
}
await _initRoom();
await _setInitialMediaSettings();
await _initLocalMedia();
}
}