RecordRTC.js 是一个方便的 JavaScript 库,可让您直接从浏览器录制音频和视频。通过使用 WebRTC 技术,它能让捕捉音频、视频甚至屏幕录制等媒体流变得超级简单。
您可以将录音保存为 WAV、WebM 和 MP3 等多种格式,使其成为满足不同需求的多功能工具。其用户友好的 API 简化了启动、停止和保存录音的过程,使其成为希望将录音功能集成到 Web 应用程序中的开发人员的绝佳选择。
前端代码
"use client";
import React, { useState, useRef } from "react";
import RecordRTC from "recordrtc";
import { v4 as uuidv4 } from "uuid";
const Mic = () => {
const [isRecording, setIsRecording] = useState(false);
const recorderRef = useRef<RecordRTC | null>(null);
const wsRef = useRef<WebSocket | null>(null);
const startRecording = () => {
const sessionId = uuidv4();
const messageId = uuidv4();
const userId = uuidv4();
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
const ws = new WebSocket("ws://localhost:8080");
ws.onopen = () => {
ws.send(
JSON.stringify({
type: "start_audio",
session_id: sessionId,
message_id: messageId,
user_id: userId,
})
);
const recorder = new RecordRTC(stream, {
type: "audio",
recorderType: RecordRTC.StereoAudioRecorder,
mimeType: "audio/wav",
timeSlice: 100,
sampleRate: 48000,
numberOfAudioChannels: 2,
ondataavailable: (blob) => {
console.log("ondataavailable", blob);
const reader = new FileReader();
reader.onloadend = () => {
const base64data = reader.result;
ws.send(
JSON.stringify({
type: "stream_audio",
audio_data: base64data,
})
);
};
reader.readAsDataURL(blob);
},
});
recorder.startRecording();
recorderRef.current = recorder;
wsRef.current = ws;
setIsRecording(true);
};
});
};
const stopRecording = () => {
if (recorderRef.current && wsRef.current) {
recorderRef.current.stopRecording(() => {
wsRef.current?.send(
JSON.stringify({
type: "stop_audio",
})
);
wsRef.current?.close();
setIsRecording(false);
});
}
};
return (
<div>
<button onClick={isRecording ? stopRecording : startRecording}>
{isRecording ? "Stop Recording" : "Start Recording"}
</button>
</div>
);
};
export default Mic;
在前端,我使用了 Nextjs 和应用路由器,并创建了一个名为 Mic 的简单组件。创建了RecordRTC
一个新的实例来处理来自媒体流的音频录制。RecordRTC
实例的配置通过各种属性和回调函数详细说明。
type: "audio"
:指定录音类型为音频。
recorderType: RecordRTC.StereoAudioRecorder
:表示使用StereoAudioRecorder
录制立体声音频。本录音机适合录制高品质音频。
mimeType: "audio/wav"
:设置录制音频的 MIME 类型为 WAV,这是一种常见的未压缩音频格式。
timeSlice: 100
:将录音机配置为每 100 毫秒发出音频数据块。这对于实时流式传输非常有用,因为它允许应用程序频繁处理较小的音频数据块。
sampleRate: 48000
:指定录音的采样率。48,000 Hz 的采样率是高质量录音的标准,可捕捉各种音频频率。
numberOfAudioChannels: 2
:将音频通道数设置为 2,表示音频将以立体声录制。
ondataavailable
:每次有新的音频数据块可用时(每 100 毫秒,由 timeSlice
定义)都会调用此函数。回调处理流式传输方面,读取二进制音频数据,将其编码为 Base64,并通过 WebSocket 发送。
后端代码
此后端代码使用 Node.js 设置 WebSocket 服务器来处理音频流。服务器从客户端以块的形式接收音频数据,将这些块临时存储为 WAV 文件,然后使用 SoX(Sound eXchange)将它们连接成单个音频文件。
const fs = require("fs");
const path = require("path");
const WebSocket = require("ws");
const { exec } = require("child_process");
const wss = new WebSocket.Server({ port: 8080 });
wss.on("connection", (ws) => {
ws.audioDataArray = []; // Initialize an array to store audio data
ws.tempFiles = []; // Initialize an array to store temp file paths
ws.on("message", (message) => {
const data = JSON.parse(message);
if (data.type === "start_audio") {
ws.sessionId = data.session_id;
ws.messageId = data.message_id;
ws.userId = data.user_id;
console.log(`Started audio stream for session: ${ws.sessionId}`);
} else if (data.type === "stop_audio") {
console.log(`Stopped audio stream for session: ${ws.sessionId}`);
// Create a temporary directory to store the chunks
const tempDir = path.join(__dirname, "temp");
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir);
}
// Write each chunk to a temporary file
ws.audioDataArray.forEach((buffer, index) => {
const tempFilePath = path.join(tempDir, `chunk-${index}.wav`);
fs.writeFileSync(tempFilePath, buffer);
ws.tempFiles.push(tempFilePath);
});
// Concatenate the chunks using SoX
const combinedFilePath = path.join(
__dirname,
"uploads",
`${ws.userId}-${ws.sessionId}-${ws.messageId}-combined.wav`
);
const soxCommand = `sox ${ws.tempFiles.join(" ")} ${combinedFilePath}`;
exec(soxCommand, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing sox: ${error}`);
return;
}
console.log(`Combined audio saved to ${combinedFilePath}`);
console.log(stdout);
console.error(stderr);
// Clean up temporary files
ws.tempFiles.forEach((filePath) => {
fs.unlinkSync(filePath);
});
});
} else if (data.type === "stream_audio") {
console.log("Received data:", data); // Log the entire data object
if (typeof data.audio_data === "string") {
const audioData = Buffer.from(data.audio_data.split(",")[1], "base64");
console.log(`Received audio data chunk of length: ${audioData.length}`);
ws.audioDataArray.push(audioData); // Store audio data in the array
} else {
console.error("Invalid audio data format");
}
}
});
});
我之所以选择 SoX 来处理音频块,是因为当我尝试连接数据块并处理音频文件时,在每个数据块之后都会遇到咔哒声。我将进一步研究这个问题,并在今后更新文章。
作者:Kusal Kalinga
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。