WebRTC works in Chrome but not Firefox

2020-07-09 07:06发布

问题:

I read several other questions on a related issue, but none answered my question. I have an odd issue where I am able to use WebRTC to audio chat from chrome to firefox but not firefox to chrome.

Basically, when a user wishes to audio chat, he/she clicks a button #audioChatBtn, which uses getUserMedia() to setup a stream. The thing is, clicking #audioChatBtn from Firefox doesn't fire the onaddstream callback on Chrome, but clicking the button from Chrome fires onaddstream on Firefox. Thus, I can audio chat from Chrome to Firefox but not the other way around. I have been trying to figure this out for several hours, but I'm hoping maybe someone here has an answer.

Relevant source:

var configuration = {
    'iceServers': [
        { url: 'stun:stun.l.google.com:19302' },
        { url: 'stun:stun1.l.google.com:19302' },
        { url: 'stun:stun2.l.google.com:19302' },
        { url: 'stun:stun3.l.google.com:19302' },
        { url: 'stun:stun4.l.google.com:19302' }
    ]
};
var pc = RTCPeerConnection(configuration);
var myStream = null;
var currentAudioIndex = 0; // Number of created channels
var myAudioEnabled = false;

// send any ice candidates to the other peer
pc.onicecandidate = function (evt) {
    if (evt.candidate)
        $(document).trigger("persistState", { mode: 'rtc', 'candidate': evt.candidate });
};

// let the 'negotiationneeded' event trigger offer generation
pc.onnegotiationneeded = function () {
    pc.createOffer(localDescCreated, logError);
}

// once remote stream arrives, play it in the audio element
pc.onaddstream = function (evt) {
    console.log('creating and binding audio');

    var idx = (currentAudioIndex++);
    var audioElement = $('#audio' + idx);

    if (audioElement.length == 0) {
        var audio = $('<audio id="audio' + idx + '" autoplay>');
        $('body').append(audio);
        audioElement = $('#audio' + idx);
    }

    var audioObject = audioElement[0];
    attachMediaStream(audioObject, evt.stream);
};

function localDescCreated(desc) {
    pc.setLocalDescription(desc, function () {
        $(document).trigger("persistState", { mode: 'rtc', 'sdp': pc.localDescription });
    }, logError);
}

function logError(e) {
    bootbox.alert("Audio chat could not be started.");
}

function hasGetUserMedia() {
    return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
              navigator.mozGetUserMedia || navigator.msGetUserMedia);
}

server.onPersist = function(msg) {
    if (msg.mode == "rtc") {
        if (msg.sdp)
            pc.setRemoteDescription(new RTCSessionDescription(msg.sdp), function () {
                // if we received an offer, we need to answer
                if (pc.remoteDescription.type == 'offer')
                    pc.createAnswer(localDescCreated, logError);
            }, logError);
        else
            pc.addIceCandidate(new RTCIceCandidate(msg.candidate));
    }
}



// On click, start audio chat from this user.
$('#audioChatBtn').click(function() {
    if (!hasGetUserMedia()) {
        bootbox.alert('Audio conferencing is not supported by your browser. (Currently only supported by Chrome, Firefox, and Opera web browsers.)');
        return;
    }

    if (myAudioEnabled) {
        myStream.stop();
        displayAlert('Streaming closed', 'Audio chat is off');
        $('#audioChatBtn').removeClass('btn-success').addClass('btn-primary');

    } else {
        getUserMedia({ video: false, audio: true }, function (localMediaStream) {
            myStream = localMediaStream;
            pc.addStream(localMediaStream);
            displayAlert('Streaming...', 'Audio chat is enabled');
            $('#audioChatBtn').removeClass('btn-primary').addClass('btn-success');
        }, logError);
    }

    myAudioEnabled = !myAudioEnabled;
});

What I've tried

  • Tried using 'optional': [{ 'DtlsSrtpKeyAgreement': 'true' }] in the configuration after reading this question
  • Tried creating a new RTCPeerConnection() each request
  • Tried using native browser functions instead of adapter.js.
  • Explored Web Audio API instead of getUserMedia()

回答1:

Firefox does not currently support onnegotiationneeded, because we currently don't support re-negotiation of an existing connection. All addStream/addTrack and a single createDataChannel (if you want to use them) need to be done before createOffer() or createAnswer. You can createDataChannel() after you connect, if you created on before createOffer.

Adding a stream after they're connected won't work.

An (annoying) alternative is to create a new set of PeerConnections to replace the old ones (using a DataChannel in the old pair as a signaling channel for lower latency)

Resolving this is high on our priority list, but will take a few more releases.



回答2:

After a lot of debugging, I came to realize that the bug has nothing to do with my code but has to do with Firefox's implementation of WebRTC. Firefox doesn't trigger the onnegotiationneeded callback, so I have to do it hackily using a timeout (and hope that the stream information has been relayed to the remote client before the function fires). Obviously, this is a firefox bug and I will report it, hoping that they fix the bug in the next build.

        getUserMedia({ video: false, audio: true }, function (localMediaStream) {
            myStream = localMediaStream;
            pc.addStream(localMediaStream);
            displayAlert('Streaming...', 'Audio chat is enabled');
            $('#audioChatBtn').removeClass('btn-primary').addClass('btn-success');

            // Need this for Firefox
            if (webrtcDetectedBrowser == 'firefox')
                setTimeout(pc.onnegotiationneeded, 5000);

        }, logError);