WebRTC - How to adjust microphone volume on a video stream?

Viewed 3260

I'm trying to adjust the microphone volume in WebRTC chat app which using 2 videos for streaming. It is possible to modify the gain of the microphone? If yes, how can I do it to the following streams I'm using?

/*********************** video call ***************************/
var localStream;

var localVideo = document.getElementById("localVideo");
var remoteVideo = document.getElementById("remoteVideo");
var callButton = document.getElementById("callButton");

var inputLevelSelector = document.getElementById('mic-volume');
var outputLevelSelector = document.getElementById('speaker-volume');
inputLevelSelector.addEventListener('change', changeMicrophoneLevel);
outputLevelSelector.addEventListener('change', changeSpeakerLevel);

callButton.disabled = true;
callButton.onclick = call;

navigator.getUserMedia = navigator.getUserMedia ||
    navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
navigator.getUserMedia({
        audio: true,
        video: true
    }, gotStream, //note that we are adding both audio and video
    function (error) {
        console.log(error);
    });

var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var SessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
var pc = new RTCPeerConnection({
    "iceServers": []
});

function gotStream(stream) {
    // localVideo.src = window.URL.createObjectURL(stream); // DEPRECATED
    localVideo.srcObject = stream; // UPDATED
    localStream = stream;
    callButton.disabled = false;
    pc.addStream(stream);
}

pc.onicecandidate = function (event) {
    console.log(event);
    if (!event || !event.candidate) {
        return;
    } else {
        socket.emit("video call", {
            type: "iceCandidate",
            "candidate": event.candidate
        });
    }
};

var remoteStream;
pc.onaddstream = function (event) {
    remoteStream = event.stream;
    var remoteVideo = document.getElementById("remoteVideo");
    // remoteVideo.src = window.URL.createObjectURL(event.stream); // DEPRECATED
    remoteVideo.srcObject = event.stream; // UPDATED
    remoteVideo.play();
};

Please take in note that I'm newbie, so take it easy! :)

1 Answers

Here below I've made an implementation of the post that I've shared in the comments. It's the same setup but with your code included.

You first create your nodes from the Web Audio API. Because you want to manipulate the volume of a stream you'll need a MediaStreamAudioSourceNode, a MediaStreamAudioDestinationNode and a GainNode. The MediaStreamAudioSourceNode is entry point for the stream. By injecting it here we can connect it through the gain. The stream will pass through the GainNode where the volume will be controlled and then passed to the MediaStreamAudioDestinationNode where you can use the stream again in your RTC client.

From there use the stream from you get from the MediaStreamAudioDestinationNode.stream property.

Edit:

It turns out that the MediaStreamAudioDestinationNode.stream is a MediaStream object with only a audio track. So the video has been removed from the stream and has to be rejoined.

So when you are able to access the stream, get the video tracks from the stream and store them in a variable. Then after passing the stream through the Web Audio API join the video tracks back with the audio tracks that went through the GainNode.

var inputLevelSelector = document.getElementById('mic-volume');

// Renamed the variable after your comment.
var peerConnection = new RTCPeerConnection({
    "iceServers": []
});

function gotStream(stream) {

  // Get the videoTracks from the stream.
  const videoTracks = stream.getVideoTracks();

  /**
   * Create a new audio context and build a stream source,
   * stream destination and a gain node. Pass the stream into 
   * the mediaStreamSource so we can use it in the Web Audio API.
   */
  const context = new AudioContext();
  const mediaStreamSource = context.createMediaStreamSource(stream);
  const mediaStreamDestination = context.createMediaStreamDestination();
  const gainNode = context.createGain();

  /**
   * Connect the stream to the gainNode so that all audio
   * passes through the gain and can be controlled by it.
   * Then pass the stream from the gain to the mediaStreamDestination
   * which can pass it back to the RTC client.
   */
  mediaStreamSource.connect(gainNode);
  gainNode.connect(mediaStreamDestination);

  /**
   * Change the gain levels on the input selector.
   */
  inputLevelSelector.addEventListener('input', event => {
    gainNode.gain.value = event.target.value;
  });

  /**
   * The mediaStreamDestination.stream outputs a MediaStream object
   * containing a single AudioMediaStreamTrack. Add the video track
   * to the new stream to rejoin the video with the controlled audio.
   */
  const controlledStream = mediaStreamDestination.stream;
  for (const videoTrack of videoTracks) {
    controlledStream.addTrack(videoTrack);
  }

  /**
   * Use the stream that went through the gainNode. This
   * is the same stream but with altered input volume levels.
   */
  localVideo.srcObject = controlledStream;
  localStream = controlledStream;
  peerConnection.addStream(controlledStream);
  callButton.disabled = false;

}
Related