Bernard
Bernard

Reputation: 191

MediaRecorder - How to play chunk/blob of video while recording?

I currently have a MediaStream which is being recorded using MediaRecorder. At the end of the recording after recorder.stop(), it produce a Blob and I am able to play that video back. My goal is to play not the entire video at the end, but play a chunk while recording. For the moment, a chunk is not playable while recording is not ended.

How can i do that using javascript? The final objective is to send a chunk by websocket that is playable even if recording is in action.

I am not able to bring new solutions. Can anyone help or at least explain me the things ?

What I've tried was

                navigator.mediaDevices.getUserMedia().then(function(media_stream) {
                    var recorder = new MediaRecorder(media_stream);

                    recorder.ondataavailable = event => {
                        //How to play each chunk without waiting for recorder.stop() ???
                        //event.data represent the data of a chunk (i.e. a blob)
                    };

                    recorder.start(1000);
                });

Upvotes: 12

Views: 3246

Answers (3)

Cerceis
Cerceis

Reputation: 860

I recently worked has a company project that requires me to process chunks of audio files while recording. Likewise, it would not work because the chunk is not playable without ending the recording.

Reason: Because the header that specify the file is on the first chunk only. To test it out, you can actually play the first chunk without any problem.

A hacky solution: We could just find a way to get the header and merge it with every other requested chunk that is not the first chunk.

Steps:

  1. Fire a quick requestData() at mediaRecorder.onStart
  2. Take that first data you received at mediaRecorder.ondataavailable and store it in a variable as the header blob
  3. On any subsequence requestData() (In this case, could be an Interval), take the header blob, copy it and append the new chunk to the header blob.
  4. Repeat step 3 until end/stop.

I could not post the entire source code, but here are the structure of the function just for a simple reference.

I am using VueJs, Typescript. Same concept could be applied to any JS framework

let mediaRecorder: MediaRecorder | null = null;
let audioChunkInterval: any = null;
let headerBlob: any = null

const startRecord = async() => {

    let mediaHeaderRequested: boolean = false;

    // Request microphone access
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    
    if(audioChunkInterval) clearInterval(audioChunkInterval)

    // Create a MediaRecorder
    mediaRecorder = new MediaRecorder(stream);
    mediaRecorder.start();

    mediaRecorder.onstart = async() => {
        if(!mediaRecorder) return;
            mediaRecorder.requestData();
    }

    // Process chunks   
    mediaRecorder.ondataavailable = async(event: any) => {
        if (event.data.size > 0) {
            if(!mediaHeaderRequested){
                // Set header blob
                headerBlob = event.data;
                mediaHeaderRequested = true;
                return;
            }
            
            // merge event.data with header blob here   
        }
    };

    //Process chunk every X seconds
    audioChunkInterval = setInterval(() => {
        if(!mediaRecorder) return;
        mediaRecorder.requestData();
    }, 10000)

    mediaRecorder.onstop = () => {
        console.log('Recording stopped');
    };
}

In my case, I send all the chunks to my NodeJS backend and dd the merging on the backend, because i am running the audio analyzing logic on the backend. I just applied the same concept and instead on the backend, did the merging on the front. After you have bunch of playable chunks, it's up to you on how would you like to play those chunks.

Upvotes: 0

Merlin -they-them-
Merlin -they-them-

Reputation: 2960

This is the simplest example I could come up with.

You need a video element for playing the video as you stream it, and a record button to kick off the recording.

The script does the following

  1. Checks for support
  2. Creates success and error handlers for when permission is granted / denied
  3. Ask permission to record
  4. If permitted, calls onSuccess
  5. Creates the recorder
  6. Creates an onclick handler for the record button
  7. If record is clicked, the onclick handler is called
  8. The mediaRecorder starts recording
  9. The video element is set to use the stream
  10. The video element is set to autoplay so the stream shows immediately

HTML

<video id="player"></video>
<button class="record">Record</button>

JS

<script>
  const video = document.querySelector('#player');
  const record = document.querySelector('.record');

  (function start() {
    // 1. check for support
    if (! navigator.mediaDevices.getUserMedia) {
      return console.log('getUserMedia not supported on your browser!');
    }

    // 2. create onSuccess handler
    // 4. called if permitted
    const onSuccess = function (stream) {
      // 5. create the recorder
      const mediaRecorder = new MediaRecorder(stream);

      // 6. create onclick handler for record button
      // 7. called if record is clicked
      record.onclick = function() {
        // 8. starts recording
        mediaRecorder.start();
        // 9. sets video element to use the stream
        video.srcObject = stream;
        // 10. sets the video element to autoplay, otherwise user would have to click play
        video.autoplay = true;
      };
    };

    // 2. create onError handler
    const onError = function (error) {
      console.error('The following error occured: ' + error);
    };

    // 3. ask permission to record audio and video
    const constraints = {video: true, audio: true};
    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
  })();
</script>

Upvotes: 0

TripleG
TripleG

Reputation: 1

you have to use RequestData for ondataavailable to fire

Upvotes: -5

Related Questions