chrisguttandin/extendable-media-recorder

dataavailable event in extendable-media-recorder vs native MediaRecorder

Closed this issue · 3 comments

Using extendable-media-recorder seems to have effects on the dataavailable event when using a mimeType different from audio/wav.

From the specs:
MediaRecorder.stop()
Stops recording, at which point a dataavailable event containing the final Blob of saved data is fired. No more recording occurs.

When Recording on Chrome using extendable-media-recorder mimeType "audio/wav" a dataavailable event will be emitted one more time after MediaRecorder.stop() is called, and we get the state of the Mediarecorder as "inactive" in
the dataavailable event handler.

dataavailable 96044 recording audio/wav
dataavailable 96000 recording audio/wav
...
After a call to MediaRecorder.stop(), one more dataavailable event is fired, showing the state
of the MediaRecorder as inactive
WebRecorder.js:30 dataavailable 54528 inactive audio/wav

Using "audio/webm;codecs=opus" in Chrome changes this behaviour in a way that no more dataavailable
call is made after calling MediaRecorder.stop().

This is in contrast to the "native" MediaRecorder where after calling stop there is
always one more dataavailable event fired.

I could not find a similar behaviour in Safari when Recording with mimeType "audio/mp4",
but Firefox using "audio/ogg; codecs=opus" shows the same "missing" event.

To get a consistent behaviour I'm using the extensible-media-recorder for "audio/wav" only,
but it would be interesting to know the reason for this :)

Thanks, @bschelling for reporting this. I'm not sure though if the spec is clear on which behavior is correct. I guess both ways of doing it would be correct according to the spec.

The data is recorded on a different thread and the dataavailable event with the partial recording is triggered there. That's why there may already be a dataavailable waiting for getting emitted by the main thread when you call stop(). Chrome would then still emit the queued dataavailable event plus the last one which has yet to be created. This may or may not happen depending on the timing.

I tracked this behavior as bug 9 because it needs to be handled in the WebmPcmMediaRecorder.

Maybe it's worth filing an issue for the spec to clarify if inflight events should be buffered to prevent dispatching more than one Blob after being stopped.

@chrisguttandin thank you for getting back on this so quickly.
The reason i actually noticed this was because i was looking for a way to reliably find out when the last blob was received. I'm not too familiar with the API, but since the spec said after calling stop there is a final event containing the final Blob of saved data fired I tried to implement it as follows.

const recordedChunks = [];
mediaRecorder.addEventListener('dataavailable', (e) => {
  recordedChunks.push(e.data);

  if (mediaRecorder.state == 'inactive') {
  //this must be the last chunk of data, create the final blob
    const blob = new Blob(recordedChunks, {type: 'video/webm'});
    // upload to server
  }
})

If it's not recommended to rely on this behavior - what would be the alternative?

I think right now Chrome will emit all remaining events before resolving any peding promises. That's at least how it was when I tested it right now. It may change with any future browser update.

But for now you could probably do something like this:

const recordedChunks = [];

let promise = null;

mediaRecorder.addEventListener('dataavailable', ({ data }) => {
  recordedChunks.push(data);

  if (mediaRecorder.state === 'inactive' && promise === null) {
    promise = Promise.resolve().then(() => {
      const blob = new Blob(recordedChunks, {type: 'video/webm'});
    });
  }
});

But again, I think it would be helpful to file an issue for the spec (https://github.com/w3c/mediacapture-record) describing the problem.