10

I'm using window.MediaRecorder to record a wav audio file and then upload it to S3. I save chunks of data to an array and then create a Blob with them after recording is finished. This is working fine and I have no issues recording or playing the files. However, I can't for the life of me figure out how to set the duration of the resultant Blob file.

Whenever I download the file locally, or upload it to s3, there is no duration metadata anywhere. I am saving the duration of the audio as the user records, but have not been able to effectively attach this to the audio file. I feel like there should be a trivial solution, but I've just spent hours searching and can't seem to find anything on this. It has to be possible, so what am I missing?

Ryan McClure
  • 1,183
  • 2
  • 17
  • 34
  • You could parse the WAV header. It should contain all the information needed to calculate the duration of the aufio file accurately. The header description may be found here: http://soundfile.sapp.org/doc/WaveFormat/ – CoffeeCodeConverterImpl Jul 27 '20 at 21:26
  • Record the start and stop moment and then calculate the seconds. Then add the seconds with a javascript library to the recorded blob. E.g. https://github.com/yusitnikov/fix-webm-duration - Or do the conversion on the server using ffmpeg that sets the length automatically. – Avatar Dec 24 '21 at 05:43
  • 1
    This might help: https://stackoverflow.com/a/55093702/1066234 – Avatar Dec 24 '21 at 05:46

3 Answers3

7

Probably, you have already found a solution to this, but I am posting it for anyone else interested in this to see.

You could use MediaRecorder.onstart and onstop events.

  1. When MediaRecorder's onstart event is triggered you need to get the current date with something like Date.now().
  2. When onstop event is triggered, get the current date as well.
  3. You need to subtract those two dates to get the recorded audio's duration.

References:

cr1ng3
  • 101
  • 2
  • 7
  • By the way, I am not very sure that .wav files are supported by default in Firefox and Chrome (not sure about others). Firefox could record .ogg files and Chrome .webm files. Hope it helps. – cr1ng3 Nov 11 '17 at 15:19
  • How do you use the duration to set it onto the blob? – Corey Cole Dec 27 '19 at 21:24
  • TBH I do not remember why I needed this at the time I have implemented this, however I assume that I wanted an easy way to keep the duration in my DB to make other steps of transformations more straightforward. So, you do not have to store the duration inside the blob, I have used a hidden input to store the duration and send it to the DB as a metadata for the audio. – cr1ng3 Dec 29 '19 at 22:34
  • @CoreyCole, you can't set the duration in a webm/opus encoded file. The duration is derived by parsing the binary file after the fact. – Jacob Jul 26 '20 at 13:06
3

I've done a fair amount of research on this issue and the conclusion I've reached is that due to the way webm files are encoded, in order to get the duration, you'd have to parse the binary file and sum all the frames into a single duration. This is very complicated and tedious. If you're interested in doing this, read How to parse webm.

The route I went was a much simpler approach. Once the file has been recorded you should fetch it and attach it to a audio element. If you inspect the duration now, you'll still see that it's incorrect. What you need to do is wait for the loadedmetadata event. At that point you can run the following function:

checkForDuration(cb =()=> {}) {
  if(this.element.duration === Infinity) {
    this.element.currentTime = 1e101;
    this.element.ontimeupdate = function() {
      this.ontimeupdate = ()=> {return;};
      cb();
    };
  } else {
    cb();
  }
}

At the point that the callback is called, you can then inspect the audio element's duration property to get the correct duration. You can then store the duration in the DB for retrieval in the future.

Jacob
  • 524
  • 5
  • 18
1

There is a library that wraps around the browser's native MediaRecorder called RecordRTC. It exposes a function called getSeekableBlob().

Make sure to include the EBML.js CDN in your webpage, as getSeekableBlob() depends on it:

<script src="https://www.webrtc-experiment.com/EBML.js"></script>

You can see example usage here.

function stopRecordingCallback() {
    video.muted = false;
    video.volume = 1;
    video.src = video.srcObject = null;
    getSeekableBlob(recorder.getBlob(), function(seekableBlob) {
        video.src = URL.createObjectURL(seekableBlob);
        recorder.stream.stop();
        recorder.destroy();
        recorder = null;
        document.getElementById('btn-start-recording').disabled = false;
        invokeSaveAsDialog(seekableBlob, 'seekable-recordrtc.webm');
    });
}
Corey Cole
  • 2,262
  • 1
  • 26
  • 43