Question

Hey I would like to know if there is any way to choose codecs when creating the offer/answer in WebRTC. There are currently not many video codecs to choose from, but there are audio codecs like Opus, PCMU, PCMA etc.

Was it helpful?

Solution

In general, yes. Here is example of how to prefer Opus codec during establishing conneciton. You should call 'preferOpus' from a callback function for createAnswer or createOffer.

var preferOpus = function(sdp) {
  var sdpLines = sdp.split('\r\n');

  for (var i = 0; i < sdpLines.length; i++) {
    if (sdpLines[i].search('m=audio') !== -1) {
      var mLineIndex = i;
      break;
    }
  }

  if (mLineIndex === null) return sdp;

  for (i = 0; i < sdpLines.length; i++) {
    if (sdpLines[i].search('opus/48000') !== -1) {
      var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
      if (opusPayload) 
        sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
      break;
    }
  }

  sdpLines = removeCN(sdpLines, mLineIndex);

  sdp = sdpLines.join('\r\n');
  return sdp;
};

var extractSdp = function(sdpLine, pattern) {
  var result = sdpLine.match(pattern);
  return (result && result.length == 2)? result[1]: null;
};

var setDefaultCodec = function(mLine, payload) {
  var elements = mLine.split(' ');
  var newLine = new Array();
  var index = 0;
  for (var i = 0; i < elements.length; i++) {
    if (index === 3) newLine[index++] = payload;
    if (elements[i] !== payload) newLine[index++] = elements[i];
  }
  return newLine.join(' ');
};

var removeCN = function(sdpLines, mLineIndex) {
  var mLineElements = sdpLines[mLineIndex].split(' ');
  for (var i = sdpLines.length-1; i >= 0; i--) {
    var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
    if (payload) {
      var cnPos = mLineElements.indexOf(payload);
      if (cnPos !== -1) mLineElements.splice(cnPos, 1);
      sdpLines.splice(i, 1);
    }
  }
  sdpLines[mLineIndex] = mLineElements.join(' ');
  return sdpLines;
};

OTHER TIPS

Choosing Opus will only get you halfway there. Even with the codec it might default to mono and around 42 kb/s as it's primarily designed for voice.

If you are not using voice input, and want consistent music, you can disable the audio processing features using constraints:

navigator.mediaDevices.getUserMedia({
  audio: {
    autoGainControl: false,
    channelCount: 2,
    echoCancellation: false,
    latency: 0,
    noiseSuppression: false,
    sampleRate: 48000,
    sampleSize: 16,
    volume: 1.0
  }
});

Then set the SDP to be stereo and increase the maxaveragebitrate:

let answer = await peer.conn.createAnswer(offerOptions);
answer.sdp = answer.sdp.replace('useinbandfec=1', 'useinbandfec=1; stereo=1; maxaveragebitrate=510000');
await peer.conn.setLocalDescription(answer);

This should output a string which looks like this:

a=fmtp:111 minptime=10;useinbandfec=1; stereo=1; maxaveragebitrate=510000

This gives a potential maximum bitrate of 520kb/s for stereo, which is 260kps per channel. Actual bitrate depends on the speed of your network and strength of your signal.

You can read more about the other available attributes at: https://www.rfc-editor.org/rfc/rfc7587

As browsers start to support setCodecPreferences, you can check for the "audio/opus" mimetype and set your codec preferences to opus codecs:

let tcvr = pc.getTransceivers()[0];
let codecs = RTCRtpReceiver.getCapabilities('audio').codecs;
let opus_codecs = [];
// iterate over supported codecs and pull out the codecs we want
for(let i = 0; i < codecs.length; i++)
{
   if(codecs[i].mimeType == "audio/opus")
   {
      opus_codecs .push(codecs[i]);
   }
}
// currently not all browsers support setCodecPreferences
if(tcvr.setCodecPreferences != undefined)
{
   tcvr.setCodecPreferences(opus_codecs);
}

Code adapted from this Pericror blog post to fix audio/video codecs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top