Bret
Bret

Reputation: 13

Mixing Audio elements into one stream destination for use with MediaRecorder

MediaRecorder only lets you record media of one type per track. So I'm using JQuery to get a list off all audio elements and connect them to the same audio context destination in order to mix all the audio tracks into one audio stream to later be recorded by MediaRecorder. What I have so far works but only captures the first track and none of the others.

Any idea why only one track comes through?

my code:

function gettracks(stream){
    var i = 0;
    var audioTrack ;
    var audioctx = new AudioContext();
    var SourceNode = [];
    var  dest = audioctx.createMediaStreamDestination();
    $('audio').each( function() {

    //the audio element id
    var afid = $(this).attr('id');

    var audio = $('#'+afid)[0];
    console.log('audio id '+ afid+' Audio= '+audio);
    SourceNode[i] = audioctx.createMediaElementSource(audio);

    //dont forget to connect the wires!
    SourceNode[i].connect(audioctx.destination);
    SourceNode[i].connect(dest);


      audioTrack = dest.stream.getAudioTracks()[0];
    stream.addTrack(audioTrack);
    i++;
    });
    }


//from a mousedown event I call
stream = canvas.captureStream();
video.srcObject = stream;

gettracks(stream);
startRecording()
    function startRecording() {
          recorder = new MediaRecorder(stream, {
        mimeType: 'video/webm'
      });
      recorder.start();
    }

Upvotes: 1

Views: 3171

Answers (2)

MikeHelland
MikeHelland

Reputation: 1159

What if you create a gain node and connect your source nodes to that:

var gain = audioctx.createGain();
gain.connect(dest);

and in the loop

SourceNode[i].connect(gain);

Then your sources flow into a single gain node, which flows to the your destination.

Upvotes: 1

padenot
padenot

Reputation: 1555

I would do it like this:

var ac = new AudioContext();
var mediaStreamDestination = new MediaStreamAudioDestinationNode(ac);
document.querySelectorAll("audio").forEach((e) => {
  var mediaElementSource = new MediaElementAudioSourceNode(ac, { mediaElement: e });
  mediaElementSource.connect(mediaStreamDestination);
});
console.log(mediaStreamDestination.stream.getAudioTracks()[0]); // give this to MediaRecorder

Breaking down what the above does:

  • var ac = new AudioContext();: create an AudioContext, to be able to route audio to something else than the default audio output.
  • var mediaStreamDestination = new MediaStreamAudioDestinationNode(ac); from this AudioContext, get a special type of DestinationNode, that, instead of send the output of the AudioContext to the audio output device, sends it to a MediaStream that holds a single track of audio.
  • document.querySelectorAll("audio").forEach((e) => {, get all the <audio> elements, and iterate over them.
  • var mediaElementSource = new MediaElementAudioSourceNode(ac, { mediaElement: e });, for each of those media element, capture its output and route it to the AudioContext. This gives you an AudioNode.
  • mediaElementSource.connect(mediaStreamDestination);, connect our AudioNode that has the output of the media element, connect it to our destination that goes to a MediaStream.
  • mediaStreamDestination.stream.getAudioTracks()[0] get the first audio MediaStreamTrack from this MediaStream. It has only one anyways.

Now, I suppose you can do something like stream.addTrack(mediaStreamDestination.stream.getAudioTracks()[0]), passing in the audio track above.

Upvotes: 2

Related Questions