typescript 未正确设置React MediaRecorder音频数据

oxalkeyp  于 2023-01-21  发布在  TypeScript
关注(0)|答案(1)|浏览(176)

我有一个react应用程序,它使用MediaRecorder API录制音频,然后将录制的音频数据转换为base64。代码如下所示:

import React, { useState } from "react";

const App: React.FC = () => {
    const [recording, setRecording] = useState(false);
    const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder>();
    const [audioChunks, setAudioChunks] = useState<Blob[]>([]);

    const handleStartRecording = async () => {
        const stream = await navigator.mediaDevices.getUserMedia({
            audio: true,
        });
        const mediaRecorder = new MediaRecorder(stream);
        setMediaRecorder(mediaRecorder);
        mediaRecorder.start();
        mediaRecorder.addEventListener("dataavailable", handleDataAvailable);
        setRecording(true);
    };

    const handleStopRecording = () => {
        if (mediaRecorder) {
            mediaRecorder.stop();
            setRecording(false);
        }
    };

    const handleDataAvailable = (e: BlobEvent) => {
        if (e.data.size > 0) {
            setAudioChunks((prev) => [...prev, e.data]);
        }
    };

    const handleDownload = async () => {
        if (audioChunks) {
            const audioBlob = new Blob(audioChunks);
            convertToBase64AndSend(audioBlob);

            setMediaRecorder(undefined);
            setAudioChunks([]);
        }
    };

    const convertToBase64AndSend = (audioBlob: Blob) => {
        const reader = new FileReader();
        reader.readAsDataURL(audioBlob);
        reader.onloadend = () => {
            const base64data = reader.result as string;
            console.log(base64data);
        }
    } 

    return (
        <div>
            <button onClick={recording ? handleStopRecording : handleStartRecording}>
                {recording ? "Stop Recording" : "Start Recording"}
            </button>
            {audioChunks.length > 0 && (
                <button onClick={handleDownload}>Send Audio</button>
            )}
        </div>
    );
};

export default App;
    `

现在代码在这种状态下完全按照预期工作,你点击开始录制,然后音频开始录制,你点击停止录制,然后音频停止录制,当你点击下载,它成功地将音频数据转换为base64并记录下来:

我想修改这段代码,让它在我点击Stop Recording时自动启动下载功能,所以我删除了Download按钮,让handleStopRecording()函数调用handleDownload()函数,如下所示:

const handleStopRecording = () => {
        if (mediaRecorder) {
            mediaRecorder.stop();
            setRecording(false);
        }

        handleDownload();
    };

return (
        <div>
            <button onClick={recording ? handleStopRecording : handleStartRecording}>
                {recording ? "Stop Recording" : "Start Recording"}
            </button>
        </div>
    );

然而,现在这导致了一个问题,我记录的base64AudioData是空的。大概是因为audioChunks钩子没有正确设置。似乎audioChunks钩子只有在有一个额外的按钮按下时才能正确设置,但我不太理解react,不知道为什么。我该如何纠正这个问题?

yhived7q

yhived7q1#

您需要等待audioChunks状态被设置,并且由于设置状态是异步的,并且在本例中它只在调用dataavailable事件之后发生,所以当您在mediaRecorder.stop()之后调用handleDownload()时,块还没有准备好。
一个简单的解决方案是将下载代码移到useEffect块中,这样它就可以在设置块之后执行:

useEffect(() => {
  if(audioChunks.length) {
    const audioBlob = new Blob(audioChunks);
    convertToBase64AndSend(audioBlob);

    setMediaRecorder(undefined);
    setAudioChunks([]);
  }
}, [audioChunks]);

但是,由于现在下载时只需要块,之后会重置它们,因此不需要audioChunks状态,可以直接从dataavailable事件处理程序调用下载,并将当前块传递给它:

const handleDataAvailable = (e: BlobEvent) => {
  if (e.data.size > 0) {
    const audioBlob = new Blob([e.data]);
    convertToBase64AndSend(audioBlob);

    setMediaRecorder(undefined);
  }
};

相关问题