In a bare-bones demo, I find that there is a 1.5 second delay before MediaRecorder starts recording audio, on Firefox only.
Please find below a snippet that works well with MediaRecorder on Chrome, Opera and Edge. Apparently MediaRecorder is blocked when the snippet runs on Stack Overflow, so you can't test it here. You can find the same code on GitHub, with a demo here.
On Safari, there is also a delay, but for a different and more manageable reason: the promise that eventually resolves to a user media stream takes longer than in other browsers, but once the stream is available, recording can begin immediately. It is therefore possible to indicate to the end-user that recording has started.
On Firefox, the user media stream is available immediately, and an icon appears in the menu bar immediately in the menu bar to indicate that the microphone is busy. It seems that the stream starts flowing, but there is no data from the microphone for over a second. Recording only starts after about 1.5 seconds, when a second (yellow) icon appears in the menu bar. The recorded audio begins with 1.5 seconds of silence.
Is this due to a mistake in the way I am using MediaRecorder, or do I need to take some specific action on Firefox?
const startRecording = document.getElementById("startRecording")
const stopRecording = document.getElementById("stopRecording")
const playBack = document.getElementById("playBack")
const feedback = document.getElementById("feedback")
startRecording.addEventListener("click", startRecorder, false)
stopRecording.addEventListener("click", stopRecorder, false)
playBack.addEventListener("click", () => audio.play(), false)
const audio = new Audio()
const streams = []
let startTime
let mediaRecorder
let chunks
async function startRecorder() {
navigator.mediaDevices
.getUserMedia({ audio: true })
.then(onSuccess, onError)
}
function onSuccess(stream) {
startTime = new Date()
// The System "recording" icon appears now there is a stream
streams.push(stream)
const mimeType = "audio/webm"
mediaRecorder = new MediaRecorder(stream) //, { mimeType })
chunks = [];
mediaRecorder.onstop = saveRecording
mediaRecorder.ondataavailable = ({data}) => {
chunks.push(data);
};
mediaRecorder.start()
showStartTime()
};
function onError(error) {
alert(`An error occured with getUserMedia():
${error}`);
};
function stopRecorder() {
if (!mediaRecorder) {
return
}
mediaRecorder.stop()
stopAllTracks()
showEndTime()
}
function stopAllTracks() {
// Switch off the System "recording" icon
streams.forEach( stream => {
stream.getTracks() // get all tracks from the MediaStream
.forEach( track => track.stop() ); // stop each of them
})
streams.length = 0
}
function saveRecording() {
const type = mediaRecorder.mimeType
const blob = new Blob(chunks, { type })
const src = window.URL.createObjectURL(blob)
// Play the recording
audio.src = src
audio.play()
}
function showStartTime() {
const text = `Started: ${startTime.toLocaleTimeString("en-gb") + "." + startTime.getMilliseconds()}`
console.log("text:", text);
feedback.textContent = text
}
function showEndTime(){
const endTime = new Date()
const duration = (endTime - startTime) / 1000
const text = `
Ended: ${endTime.toLocaleTimeString("en-gb") + "." + endTime.getMilliseconds()}
Duration: ${duration} seconds`
feedback.textContent += text
}
<button id="startRecording">Start Recording</button>
<button id="stopRecording">Stop Recording</button>
<button id="playBack">Play Back</button>
<pre id="feedback"></pre>
recorder.start(1000)
, occasionally (but quite frequently), theondatavailable
callback initially gets called with a first 130-byte payload, followed by 3 empty payloads (i.e., 0 bytes), and then starts recording. This does not happen with Chrome and Safari. – Legra