使用MediaRecorder录制5秒的音频片段,然后上传到服务器

MM *_* PP 3 javascript blob mediarecorder web-mediarecorder

我想记录用户的麦克风5秒长的段,并将每个段上传到服务器。我尝试使用MediaRecorder并以5秒的时间间隔调用了start()和stop()方法,但是当我串联这些录音时,它们之间会发出“滴”声。因此,我尝试使用start()方法的timelice参数记录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);
  });
});
Run Code Online (Sandbox Code Playgroud)

但是只有第一部分是可玩的。我该怎么办,或者如何使所有Blob都可播放?我必须记录然后上传每个片段。我无法创建blob数组(因为用户可以记录24小时或更长时间的数据,并且用户需要在记录的同时将数据上传到服务器上-延迟5秒)。

谢谢!

Kai*_*ido 8

您必须了解如何构建媒体文件。
不仅一些原始数据可以直接转换为音频或视频。

这将取决于所选的格式,但是基本情况是您拥有所谓的元数据,就像描述文件结构的字典一样。

这些元数据对于随后将读取文件的软件来说是必需的,该软件将知道如何解析文件中包含的实际数据。

MediaRecorder API在这里处于一个奇怪的位置,因为它必须能够同时写入这些元数据,并且还可以添加不确定的数据(它是实时记录器)。

因此,发生的情况是浏览器将主要元数据放在文件的开头,这样他们就可以将新数据简单地推送到文件,并且仍然是有效的文件(即使某些信息(例如持续时间)丢失)。

现在,您得到的datavailableEvent.data只是生成的整个文件的一部分。
第一个通常将包含元数据和一些其他数据,具体取决于何时通知事件触发,但是下一部分不一定包含任何元数据。

因此,您不能仅将这些部分作为独立文件来获取,因为生成的唯一文件是由所有这些部分组成的文件,这些文件在一个Blob中连接在一起。


因此,对于您的问题,您有不同的可能方法:

  • 您可以间隔一定时间将从记录器获取的最新片段发送到服务器,然后合并这些服务器端。

    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);
    
    Run Code Online (Sandbox Code Playgroud)

    在服务器端,您可以将新发送的数据附加到正在记录的文件中。

  • 另一种方法是生成许多小的独立文件,而要做到这一点,您只需在一个间隔中生成一个新的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);
    
    Run Code Online (Sandbox Code Playgroud)

    这样做,每个文件将是独立的,持续约5秒钟,您将能够一个一个地播放这些文件。
    现在,如果您希望仅将一个文件存储在服务器上,仍然使用此方法,则也可以使用例如ffmpeg之类的工具在服务器端将这些文件很好地合并在一起。

  • @MMPP 不是真的没有。我没有包含在答案中的第三种方法实际上是同时进行:开始一个循环来记录短序列,以及一个更大的循环来产生全长记录。(仅将完整的块发送到您的服务器) (3认同)