fix: Improve microphone device selection to avoid alias deviceIds

- Add isAlias() helper to identify 'default' and 'communications' aliases
- Prefer actual device IDs (long strings) over alias IDs
- For alias deviceIds, use {audio: true} to let system choose
- For real deviceIds, try exact first, then ideal as fallback
- Add debug logging for isSecureContext and alias detection

This fixes the "Could not start audio source" error when Electron tries
to open a microphone using exact: 'communications' constraint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-18 13:50:49 +08:00
parent 9a6ca5730b
commit 6112799c79

View File

@@ -421,6 +421,9 @@
return;
}
// Debug: Check if we're in a secure context
console.log('isSecureContext:', window.isSecureContext);
// Check for available audio devices first
const devices = await navigator.mediaDevices.enumerateDevices();
const audioInputs = devices.filter(d => d.kind === 'audioinput');
@@ -431,41 +434,57 @@
return;
}
// Helper to identify alias deviceIds (not real device IDs)
const isAlias = (id) => id === 'default' || id === 'communications';
// Filter out Stereo Mix (立體聲混音) - it's not a real microphone
const realMicrophones = audioInputs.filter(d =>
!d.label.includes('立體聲混音') &&
!d.label.toLowerCase().includes('stereo mix') &&
d.deviceId !== 'default' // Skip default which might be Stereo Mix
!d.label.toLowerCase().includes('stereo mix')
);
// Prefer communications device or actual microphone
// Prefer actual device IDs (long strings), not aliases like 'default' or 'communications'
// First try to find a real microphone with actual deviceId
let selectedMic = realMicrophones.find(d =>
d.deviceId === 'communications' ||
!isAlias(d.deviceId) && (
d.label.includes('麥克風') ||
d.label.toLowerCase().includes('microphone')
) || realMicrophones[0];
)
) || realMicrophones.find(d => !isAlias(d.deviceId)) || realMicrophones[0];
console.log('Real microphones found:', realMicrophones.length);
console.log('Selected microphone:', selectedMic);
console.log('Selected deviceId is alias:', selectedMic ? isAlias(selectedMic.deviceId) : 'N/A');
if (!selectedMic) {
alert('No real microphone found. Only Stereo Mix detected. Please connect a microphone.');
return;
}
// Try with the selected microphone
// Get microphone stream with proper constraints handling
try {
if (isAlias(selectedMic.deviceId)) {
// For alias deviceIds (default/communications), let the system choose
console.log('Using system default (alias detected)');
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
} else {
// For real deviceIds, try exact first, then ideal as fallback
try {
mediaStream = await navigator.mediaDevices.getUserMedia({
audio: { deviceId: { exact: selectedMic.deviceId } }
});
console.log('Successfully connected to:', selectedMic.label);
} catch (exactErr) {
console.warn('Exact device ID failed, trying preferred:', exactErr);
// Fallback: try with preferred instead of exact
console.warn('Exact device ID failed, trying ideal:', exactErr);
mediaStream = await navigator.mediaDevices.getUserMedia({
audio: { deviceId: selectedMic.deviceId }
audio: { deviceId: { ideal: selectedMic.deviceId } }
});
}
}
console.log('Successfully connected to:', selectedMic.label);
} catch (err) {
console.error('getUserMedia failed:', err.name, err.message);
throw err; // Let outer catch handle the error message
}
isRecording = true;
recordBtn.textContent = 'Stop Recording';