How to get the sample rate from a mediaDevices.getUserMedia stream

819 views Asked by At

Firefox is limited in its audio resampling ability for audio mediastreams. If the input media stream's sample rate is not the same as the AudioCotext's, then it complains :

DOMException: AudioContext.createMediaStreamSource: Connecting AudioNodes from AudioContexts with different sample-rate is currently not supported.

For example if we get an audio stream like so :

navigator.mediaDevices.getUserMedia(constraints).then(stream => {
  let context = new (window.AudioContext || window.webkitAudioContext)({sampleRate : 48000});  
  let audioInput = this.context.createMediaStreamSource(stream);
});

Firefox will complain about mismatching sample rates - if they are different between the audio context and the hardware device's settings in the audio subsystem.

I can't find a way to get the sample rate from the audio track in the stream. I've tried :

let tracks = stream.getAudioTracks();
let settings = tracks[0].getSettings();
let constraints = tracks[0].getConstraints();

But none of these objects have the streams's sampleRate in them.

Is there another way to enquire an audio track's/stream's sample rate ?

2

There are 2 answers

0
chrisguttandin On

I think the only way to get the real sampleRate in Firefox is by using the MediaRecorder to do the smallest possible recording. By default Firefox will record a MediaStream as 'audio/ogg; codecs=opus'. And that means that it will encode all audio with a sample rate of 48kHz even though the original sample rate was different. But there is a field in the Opus header which stores the original sample rate. Luckily this seems to be populated correctly in Firefox.

const getSampleRateWithFallback = (mediaStream) => new Promise((resolve, reject) => {
    try {
        const { sampleRate } = mediaStream.getAudioTracks()[0].getSettings();

        if (typeof sampleRate === 'number') {
            resolve(sampleRate);
        } else {
            const mediaRecorder = new MediaRecorder(mediaStream, { mimeType: 'audio/ogg; codecs=opus' });

            mediaRecorder.ondataavailable = ({ data }) => {
                data.arrayBuffer().then(
                    (arrayBuffer) => {
                        const dataView = new DataView(arrayBuffer);

                        resolve(dataView.getInt32(40, true));
                    },
                    reject
                );
            };
            mediaRecorder.start();
            mediaRecorder.stop();
        }
    } catch (err) {
        reject(err);
    }
});

It should work in Firefox and Chrome. It can be tested like this:

const audioContext = new AudioContext({ sampleRate: 32000 });
const mediaStreamAudioDestinationNode = new MediaStreamAudioDestinationNode(audioContext);
const oscillatorNode = new OscillatorNode(audioContext);

oscillatorNode.connect(mediaStreamAudioDestinationNode);
oscillatorNode.start();

getSampleRateWithFallback(mediaStreamAudioDestinationNode.stream)
    .then((sampleRate) => console.log(sampleRate));
0
Nils Ohlmeier On

What seems to work is to connect the audio stream from getUserMedia to an default AudioContext with no constraints (do not specify the sampling rate). Once the stream is connected you can look at AudioContext.sampleRate which tells you the sampling rate of the stream from getUserMedia().