fix(recorder): choose supported MediaRecorder mime (webm/mp4) and use it for blob/upload to improve playback compatibility

This commit is contained in:
Ender 2025-10-24 03:10:52 +02:00
parent d0398408ac
commit dde15813be

View File

@ -4,6 +4,7 @@ import { Box, Button, Stack, Typography } from '@mui/material';
export default function Recorder({ onTranscript }: { onTranscript?: (t: string) => void }) { export default function Recorder({ onTranscript }: { onTranscript?: (t: string) => void }) {
const mediaRecorderRef = useRef<MediaRecorder | null>(null); const mediaRecorderRef = useRef<MediaRecorder | null>(null);
const chunksRef = useRef<Blob[]>([]); const chunksRef = useRef<Blob[]>([]);
const mimeRef = useRef<string>('audio/webm');
const [recording, setRecording] = useState(false); const [recording, setRecording] = useState(false);
const [audioUrl, setAudioUrl] = useState<string | null>(null); const [audioUrl, setAudioUrl] = useState<string | null>(null);
const [audioBlob, setAudioBlob] = useState<Blob | null>(null); const [audioBlob, setAudioBlob] = useState<Blob | null>(null);
@ -29,7 +30,24 @@ export default function Recorder({ onTranscript }: { onTranscript?: (t: string)
const stream = await requestStream(); const stream = await requestStream();
if (!stream) return; if (!stream) return;
const mr = new MediaRecorder(stream); // Pick a supported mimeType (Safari prefers audio/mp4, Chrome supports audio/webm)
const candidates = [
'audio/webm;codecs=opus',
'audio/webm',
'audio/mp4;codecs=opus',
'audio/mp4'
];
let selected: string | undefined;
for (const c of candidates) {
// @ts-ignore
if ((window as any).MediaRecorder && MediaRecorder.isTypeSupported && MediaRecorder.isTypeSupported(c)) {
selected = c;
break;
}
}
mimeRef.current = selected || 'audio/webm';
const mr = selected ? new MediaRecorder(stream, { mimeType: selected }) : new MediaRecorder(stream);
mediaRecorderRef.current = mr; mediaRecorderRef.current = mr;
chunksRef.current = []; chunksRef.current = [];
@ -39,7 +57,7 @@ export default function Recorder({ onTranscript }: { onTranscript?: (t: string)
} }
}; };
mr.onstop = () => { mr.onstop = () => {
const blob = new Blob(chunksRef.current, { type: 'audio/webm' }); const blob = new Blob(chunksRef.current, { type: mimeRef.current });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
setAudioUrl((prev) => { setAudioUrl((prev) => {
if (prev) URL.revokeObjectURL(prev); if (prev) URL.revokeObjectURL(prev);
@ -71,7 +89,8 @@ export default function Recorder({ onTranscript }: { onTranscript?: (t: string)
return; return;
} }
const form = new FormData(); const form = new FormData();
form.append('audio', audioBlob, 'recording.webm'); const ext = mimeRef.current.includes('mp4') ? 'm4a' : 'webm';
form.append('audio', audioBlob, `recording.${ext}`);
const res = await fetch('/api/media/audio', { const res = await fetch('/api/media/audio', {
method: 'POST', method: 'POST',
body: form, body: form,