我想录制用户麦克风 5 秒长的片段并将每个片段上传到服务器。我尝试使用 MediaRecorder 并以 5 秒的时间间隔调用 start() 和 stop() 方法,但是当我连接这些录音时,之间会发出“滴”声。所以我尝试使用start()方法的timeslice参数来记录5秒的片段:
navigator.mediaDevices.getUserMedia({ audio: { channelCount: 2, volume: 1.0, echoCancellation: false, noiseSuppression: false } }).then(function(stream) {
const Recorder = new MediaRecorder(stream, { audioBitsPerSecond: 128000, mimeType: "audio/ogg; codecs=opus" });
Recorder.start(5000);
Recorder.addEventListener("dataavailable", function(event) {
const audioBlob = new Blob([event.data], { type: 'audio/ogg' });
upload(audioBlob);
});
});
但只能播放第一段。我能做什么,或者如何使所有斑点都可以播放? 我必须录制然后上传每个片段。我无法制作 blob 数组(因为用户可以记录 24 小时甚至更多的数据,并且需要在用户记录时将数据上传到服务器 - 有 5 秒的延迟)。
谢谢!
您必须了解媒体文件是如何构建的。
它不仅仅是一些可以直接转换为音频或视频的原始数据。
这取决于所选的格式,但基本情况是您拥有所谓的“元数据”,它就像描述文件结构的字典。 这些
元数据对于随后读取文件以了解应如何解析文件中包含的实际数据的软件来说是必需的。 MediaRecorder API 在这里处于一个奇怪的位置,因为它必须能够同时写入这些
元数据,并且还添加未确定的数据(它是一个live记录器)。 因此,浏览器会将主
元数据放在文件的开头,这样他们就可以简单地将新数据推送到文件中,并且仍然是一个有效的文件(即使某些信息像持续时间将会丢失)。 现在,您在
datavailableEvent.data
中得到的只是正在生成的整个文件的一部分。
第一个部分通常包含“元数据”和一些其他数据,具体取决于事件被告知何时触发,但接下来的部分不一定包含任何元数据。
您可以将一段时间内从录音机获得的最新切片发送到您的服务器,并在服务器端合并这些切片。
const recorder = new MediaRecorder(stream);
const chunks = [];
recorder.ondataavailable = e => chunks.push(e.data);
recorder.start(); // you don't need the timeslice argument
setInterval(()=>{
// here we both empty the 'chunks' array, and send its content to the server
sendToServer(new Blob(chunks.splice(0,chunks.length)))
}, 5000);
在服务器端,您可以将新发送的数据附加到正在录制的文件中。
另一种方法是生成许多小的独立文件,为此,您可以简单地在一个时间间隔内生成一个新的 MediaRecorder:
function record_and_send(stream) {
const recorder = new MediaRecorder(stream);
const chunks = [];
recorder.ondataavailable = e => chunks.push(e.data);
recorder.onstop = e => sendToServer(new Blob(chunks));
setTimeout(()=> recorder.stop(), 5000); // we'll have a 5s media file
recorder.start();
}
// generate a new file every 5s
setInterval(record_and_send, 5000);
这样做,每个文件将是独立的,持续时间约为 5 秒,您将能够逐个播放这些文件。
现在,如果您只想在服务器上存储单个文件,仍然使用此方法,您也可以在服务器端将这些文件合并在一起,例如使用像ffmpeg这样的工具。
到服务器,当它们在服务器端连接为统一文件时,不会在图像或声音上产生任何故障:
var mediaRecorder;
var recordingRunning = false;
var chunks;
// call this function to start the process
function startRecording(stream) {
recordingRunning = true;
chunks = [];
mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm; codecs=vp9" });
mediaRecorder.ondataavailable = function (e) {
chunks.push(e.data);
};
mediaRecorder.onstop = function () {
actualChunks = chunks.splice(0, chunks.length);
const blob = new Blob(actualChunks, { type: "video/webm; codecs=vp9" });
uploadVideoPart(blob); // Upload to server
};
recordVideoChunk(stream);
};
// call this function to stop the process
function stopRecording(stream) {
recordingRunning = false
mediaRecorder.stop();
};
function recordVideoChunk(stream) {
mediaRecorder.start();
setTimeout(function() {
if(mediaRecorder.state == "recording")
mediaRecorder.stop();
if(recordingRunning)
recordVideoChunk(stream);
}, 10000); // 10 seconds videos
}
后来在服务器上我用这个命令将它们连接起来:
# list.txt
file 'chunk1'
file 'chunk2'
file 'chunk3'
# command
ffmpeg -avoid_negative_ts 1 -f concat -safe 0 -i list.txt -c copy output.mp4