♻️ Trying out the new built-in webrtc

This commit is contained in:
2025-10-19 17:30:06 +08:00
parent 001549b190
commit 3f83bbc1d8
27 changed files with 1420 additions and 580 deletions

View File

@@ -10,7 +10,7 @@ import 'package:island/widgets/chat/call_participant_tile.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
class CallControlsBar extends HookConsumerWidget {
const CallControlsBar({super.key});
@@ -194,9 +194,16 @@ class CallControlsBar extends HookConsumerWidget {
String deviceType,
) async {
try {
final devices = await Hardware.instance.enumerateDevices(
type: deviceType,
);
final devices = await navigator.mediaDevices.enumerateDevices();
final filteredDevices =
devices.where((device) {
if (deviceType == 'videoinput') {
return device.kind == 'videoinput';
} else if (deviceType == 'audioinput') {
return device.kind == 'audioinput';
}
return false;
}).toList();
if (!context.mounted) return;
@@ -209,9 +216,9 @@ class CallControlsBar extends HookConsumerWidget {
? 'selectCamera'.tr()
: 'selectMicrophone'.tr(),
child: ListView.builder(
itemCount: devices.length,
itemCount: filteredDevices.length,
itemBuilder: (context, index) {
final device = devices[index];
final device = filteredDevices[index];
return ListTile(
title: Text(
device.label.isNotEmpty
@@ -236,35 +243,12 @@ class CallControlsBar extends HookConsumerWidget {
Future<void> _switchDevice(
BuildContext context,
WidgetRef ref,
MediaDevice device,
MediaDeviceInfo device,
String deviceType,
) async {
try {
final callNotifier = ref.read(callNotifierProvider.notifier);
if (deviceType == 'videoinput') {
// Switch camera device
final localParticipant = callNotifier.room?.localParticipant;
final videoTrack =
localParticipant?.videoTrackPublications.firstOrNull?.track;
if (videoTrack is LocalVideoTrack) {
await videoTrack.switchCamera(device.deviceId);
}
} else if (deviceType == 'audioinput') {
// Switch microphone device
final localParticipant = callNotifier.room?.localParticipant;
final audioTrack =
localParticipant?.audioTrackPublications.firstOrNull?.track;
if (audioTrack is LocalAudioTrack) {
// For audio devices, we need to restart the track with new device
await audioTrack.restartTrack(
AudioCaptureOptions(deviceId: device.deviceId),
);
}
}
// TODO: Implement device switching for WebRTC
// This would require restarting the media stream with the new device
if (context.mounted) {
showSnackBar(
'switchedTo'.tr(
@@ -289,31 +273,9 @@ class CallOverlayBar extends HookConsumerWidget {
if (!callState.isConnected) return const SizedBox.shrink();
final lastSpeaker =
callNotifier.participants
.where(
(element) => element.remoteParticipant.lastSpokeAt != null,
)
.isEmpty
callNotifier.participants.isNotEmpty
? callNotifier.participants.first
: callNotifier.participants
.where(
(element) => element.remoteParticipant.lastSpokeAt != null,
)
.fold(
callNotifier.participants.first,
(value, element) =>
element.remoteParticipant.lastSpokeAt != null &&
(value.remoteParticipant.lastSpokeAt == null ||
element.remoteParticipant.lastSpokeAt!
.compareTo(
value
.remoteParticipant
.lastSpokeAt!,
) >
0)
? element
: value,
);
: null;
final actionButtonStyle = ButtonStyle(
minimumSize: const MaterialStatePropertyAll(Size(24, 24)),
@@ -330,17 +292,16 @@ class CallOverlayBar extends HookConsumerWidget {
children: [
Builder(
builder: (context) {
if (callNotifier.localParticipant == null) {
return CircularProgressIndicator().center();
if (lastSpeaker == null) {
return const CircularProgressIndicator();
}
return SizedBox(
width: 40,
height: 40,
child:
SpeakingRippleAvatar(
live: lastSpeaker,
size: 36,
).center(),
child: SpeakingRippleAvatar(
live: lastSpeaker,
size: 36,
),
);
},
),
@@ -348,7 +309,9 @@ class CallOverlayBar extends HookConsumerWidget {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('@${lastSpeaker.participant.identity}').bold(),
Text(
'@${lastSpeaker?.participant.identity ?? 'Unknown'}',
),
Text(
formatDuration(callState.duration),
style: Theme.of(context).textTheme.bodySmall,