javascriptaudioblobwebmweb-mediarecorder

Try to download blob (audio/webm) but empty file


I tried to download the recorded audio file using javascript, which is

  1. Check if getUserMedia is supported --> okay
  2. Hit play button and trigger Play() function --> okay
  3. Hit stop button and trigger Stop() function --> okay
  4. Stop() would trigger Download()

recordedBlobs seems fine by checking the log (size !=0),

but var blob = new Blob(recordedBlobs, {type: "audio/webm; codecs=opus"}); would return a empty blob.

Image for console.log

Also something weird: I found that console.log(recordedBlobs.length) is 0 ?! I'm kinda new to javascript, is this normal?

I feel really confused :( Any suggestions would be appreciated, thanks in advance.

Below is my code.

<script>
    let mediaRecorder;
    let recordedBlobs;
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      console.log("getUserMedia supported.");
      navigator.mediaDevices
        .getUserMedia(
          {audio: true, video: false}
        )

        // Success callback
        .then(function (stream) {
           mediaRecorder = new MediaRecorder(stream);
        })

        // Error callback
        .catch(function (err) {
          console.log("The following getUserMedia error occurred: " + err);
        });
    } else {
      console.log("getUserMedia not supported on your browser!");
    }
    
    function handleDataAvailable(event) {
      console.log('handleDataAvailable', event);
      if (event.data && event.data.size > 0) {
        recordedBlobs.push(event.data);
      }
    }

    function Play() {
        console.log("play");
        recordedBlobs = [];
        mediaRecorder.ondataavailable = handleDataAvailable;
        mediaRecorder.start();
        console.log(mediaRecorder.state);
    }    

    function Stop() {
        console.log("stop");
        mediaRecorder.stop();
        downloadVideo();
    }    

    function downloadVideo() {
        console.log('recorded blob:', recordedBlobs);
        var blob = new Blob(recordedBlobs, {type: "audio/webm; codecs=opus"}); 
        console.log('new blob:', blob);
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.style.display = 'none';
        a.href = url;
        a.download = 'test.webm';
        document.body.appendChild(a);
        a.click();
        setTimeout(() => {
           document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
        }, 100);
    }
</script>

Solution

  • I guess it has something to do with Javascript's asynchronous properties. So when stop() is called, the data is not ready yet.

    Therefore, I added a onstop listener, and it works!

    function Play() {
        recordedBlobs = [];
        mediaRecorder.ondataavailable = handleDataAvailable;
        mediaRecorder.onstop = downloadVideo;  // download when onstop is triggered
        mediaRecorder.start();
    }    
    

    Then, in function downloadVideo, I implemented two versions: (1) download locally (2) post to server

    function downloadVideo() {
        // 1. download locally
        /*const blob = new Blob(recordedBlobs, { 'type': 'audio/webm; codecs=opus' });
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.style.display = 'none';
        a.href = url;
        a.download = 'test.webm';
        document.body.appendChild(a);
        a.click();
        setTimeout(() => {
           document.body.removeChild(a);
           window.URL.revokeObjectURL(url);
        }, 1000);*/
            
        // 2. post audio blob to server
        const blob = new Blob(recordedBlobs, { 'type': 'audio/webm; codecs=opus' });
        var data = new FormData();
        var youtube_ID = document.getElementById("youtube_ID").innerHTML;
        data.append('file', blob, youtube_ID);
        data.append('youtube_ID', youtube_ID);
        fetch(`/api/get_recording`, {
            method: "POST", 
            body: data,
        });
    

    Reference: