Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Record 5 seconds segments of audio using MediaRecorder and then upload to the server

I want to record user's microphone 5 seconds long segments and upload each to the server. I tried using MediaRecorder and I called start() and stop() methods at 5 seconds time interval, but when I concatenate these recordings there is a "drop" sound between. So I tried to record 5 seconds segments using timeslice parameter of start() method:

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);
  });
});

But only the first segment is playable. What can I do, or how can I make all blobs playable? I MUST record then upload each segment. I CAN'T make an array of blobs (because the user could record 24hours of data or even more and the data needs to be uploaded on the server while the user is recording - with a 5 seconds delay).

Thank you!

like image 862
MM PP Avatar asked Jul 13 '18 12:07

MM PP


People also ask

What is MediaRecorder recording?

The MediaRecorder API enables you to record audio and video from a web app. It's available now in Firefox and in Chrome for Android and desktop.

What is MediaRecorder API?

The MediaRecorder interface of the MediaStream Recording API provides functionality to easily record media. It is created using the MediaRecorder() constructor.

How do I use Media Recorder API?

To start with the MediaRecorder API, you need a MediaStream. You can either get one from a <video> or <audio> element or by calling getUserMedia to capture the user's camera and microphone. Once you have a stream you can initialise the MediaRecorder with it and you are ready to record.


1 Answers

You have to understand how media files are built.
It is not only some raw data that can be converted to either audio or video directly.

It will depend on the format chosen, but the basic case is that you have what is called metadata which are like a dictionary describing how the file is structured.

These metadata are necessary for the software that will then read the file to know how it should parse the actual data that is contained in the file.

The MediaRecorder API is in a strange position here, since it must be able to at the same time write these metadata, and also add non-determined data (it is a live recorder).

So what happens is that browsers will put the main metadata at the beginning of the file, in a way that they'll be able to simply push new data to the file, and still be a valid file (even though some info like duration will be missing).

Now, what you get in datavailableEvent.data is only a part of a whole file, that is being generated.
The first one will generally contain the metadata, and some other data, depending on when the event has been told to fire, but the next parts won't necessarily contain any metadata.

So you can't just grab these parts as standalone files, because the only file that is generated is the one that is made of all these parts, joined together in a single Blob.


So, to your problem, you have different possible approaches:

  • You could send to your server the latest slices you got from your recorder in an interval, and merge these server-side.

    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);
    

    And on your server-side, you would append the newly sent data to the being recorded file.

  • An other way would be to generate a lot of small standalone files, and to do this, you could simply generate a new MediaRecorder in an interval:

    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);
    

    Doing so, each file will be standalone, with a duration of approximately 5 seconds, and you will be able to play these files one by one.
    Now if you wish to only store a single file on server, still using this method, you can very well merge these files together on server-side too, using e.g a tool like ffmpeg.

like image 149
Kaiido Avatar answered Sep 23 '22 05:09

Kaiido