diff --git a/lib/widgets/settings/settings_voice.dart b/lib/widgets/settings/settings_voice.dart index f42cab2..258ecd8 100644 --- a/lib/widgets/settings/settings_voice.dart +++ b/lib/widgets/settings/settings_voice.dart @@ -35,12 +35,30 @@ class _VoiceSectionState extends ConsumerState<_VoiceSection> { } catch (_) { devices = []; } - // Windows (und teils Android) liefert eine LEERE Liste ohne Fehler, - // solange in diesem Prozess noch nie ein Media-Stream geöffnet wurde. - // Deshalb nicht nur bei Exception, sondern auch bei leerer Liste einmal - // kurz getUserMedia anfordern und erneut enumerieren — und zwar SOLANGE - // der Stream noch offen ist (nach stop() melden manche Systeme wieder 0). - final hasAudioInput = + // Windows: die Geräteliste kommt aus dem Audio-Device-Module der + // libwebrtc.dll, und das wird erst initialisiert, wenn irgendeine + // PeerConnection existiert (webrtc-sdk/libwebrtc Issue #141, Fix #142 + // noch in keinem Binary-Release – Diagnose siehe tool/device_probe.dart). + // Ohne laufenden Call ist die Liste deshalb IMMER leer. Workaround: + // kurz eine Dummy-PeerConnection anlegen (kein Mikrofonzugriff nötig), + // enumerieren, wieder schließen. + var hasAudioInput = + devices.any((d) => d.kind?.toLowerCase() == 'audioinput'); + if (devices.isEmpty || !hasAudioInput) { + RTCPeerConnection? admWakeup; + try { + admWakeup = await createPeerConnection({}); + devices = await navigator.mediaDevices.enumerateDevices(); + } catch (_) { + // Fallback unten übernimmt. + } finally { + await admWakeup?.close(); + } + } + // Zweite Stufe (andere Systeme, z. B. teils Android): einmal kurz + // getUserMedia anfordern und erneut enumerieren, SOLANGE der Stream + // offen ist (nach stop() melden manche Systeme wieder 0). + hasAudioInput = devices.any((d) => d.kind?.toLowerCase() == 'audioinput'); if (devices.isEmpty || !hasAudioInput) { try { diff --git a/tool/device_probe.dart b/tool/device_probe.dart new file mode 100644 index 0000000..6deafc5 --- /dev/null +++ b/tool/device_probe.dart @@ -0,0 +1,68 @@ +// Diagnose-Sonde für die Windows-Audiogeräte-Erkennung (ROADMAP M3). +// +// BEFUND (2026-07-07, PC-Diagnose): enumerateDevices()/getSources liefert auf +// Windows IMMER eine leere Liste, solange keine RTCPeerConnection existiert. +// Ursache: flutter_webrtc holt die Geräteliste über das Audio-Device-Module +// der libwebrtc.dll (`GetAudioDevice()`), und dieses ADM bekommt sein Init() +// erst, wenn die Voice-Engine anläuft – sprich: wenn irgendeine +// PeerConnection existiert. Ohne Init() liefern RecordingDevices()/ +// PlayoutDevices() -1 (per Plugin-Instrumentierung verifiziert), die +// Geräteliste bleibt leer, obwohl die MMDevice-API des Systems 23 Endpunkte +// meldet. Upstream-Fix: webrtc-sdk/libwebrtc #142 (2026-06-24, „fix: audio +// devices init issue"), aber noch in KEINEM libwebrtc-Binary-Release +// (Stand 2026-07-07: neuestes ist m144.7559.09 vom 2026-06-11) und damit in +// keiner flutter_webrtc-Version (geprüft: 1.4.1 und 1.5.2 identisch kaputt). +// Workaround in der App: vor dem Enumerieren eine Dummy-PeerConnection +// anlegen (siehe settings_voice.dart) – kein getUserMedia/Mikrofonzugriff +// nötig. +// +// Diese Sonde belegt das stufenweise. Lauf: +// flutter build windows --release -t tool/device_probe.dart +// build\windows\x64\runner\Release\pyramid.exe +// Ergebnis landet in %TEMP%\pyramid_device_probe.txt +import 'dart:io'; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + final out = StringBuffer(); + void log(String s) { + out.writeln(s); + debugPrint('[probe] $s'); + } + + log('device_probe ${DateTime.now().toIso8601String()}'); + + Future countDevices(String phase) async { + try { + final devices = await rtc.navigator.mediaDevices.enumerateDevices(); + log('$phase: ${devices.length} Geräte'); + for (final d in devices.take(30)) { + log(' kind=${d.kind} label="${d.label}"'); + } + return devices.length; + } catch (e) { + log('$phase: FEHLER $e'); + return -1; + } + } + + await countDevices('0-kalt'); + + // Stufe A: nur PeerConnection anlegen – das allein initialisiert das ADM. + final pc = await rtc.createPeerConnection({}); + await countDevices('A-pc-angelegt'); + + // Nachkontrolle: nach close() fällt die Liste wieder auf 0 zurück. + await pc.close(); + await Future.delayed(const Duration(milliseconds: 300)); + await countDevices('B-nach-close'); + + final f = File( + '${Platform.environment['TEMP'] ?? '.'}\\pyramid_device_probe.txt'); + await f.writeAsString(out.toString()); + debugPrint('[probe] geschrieben: ${f.path}'); + exit(0); +}