2

In my multi peer webrtc client (testing on chrome) a stable connection is successfully established but after I receive the answer from the callee, the ontrack event is not firing and thus the stream sourceObj is not attached to my DOM. Why? The callee shows both videos (local and remote) but on the caller side the remote video is not added and it seems that this is due to the ontrack event not firing.

I create all the RTCPeerConnection before sending the Offer/Answer and add the local Tracks at creation time to it after I had bound the ontrack events to it. Then I send the offer/answer and follow the signaling proccess.

class WebRTC_Client {

    private serversCfg = {
        iceServers: [{
            urls: ["stun:stun.l.google.com:19302"]
        }]
    };

    ...

    private gotStream(stream) {
        window.localStream = stream;
        ...
    }

    private stopLocalTracks(){
        if (window.localStream) { 
            window.localStream.getTracks().forEach(function (track) {
                track.stop();
            });
            var videoTracks = window.localStream.getVideoTracks();
            for (var i = 0; i !== videoTracks.length; ++i) {
                videoTracks[i].stop();
            }
        }
    }

    private start() {
        var self = this;
        ...
        this.stopLocalTracks();
        ...
        navigator.mediaDevices.getUserMedia(this.getConstrains())
            .then((stream) => {
                self.gotStream(stream);
                trace('Send signal to peers that I am ready to be called: onReadyForTeamspeak');
                self.SignalingChannel.send(JSON.stringify({type: 'onReadyForTeamspeak'}));
            })
            .catch( self.errorHandler );
    }

    public addPeerId(peerId){
        this.availablePeerIds[peerId] = peerId;
        this.preparePeerConnection(peerId);
    }

    private preparePeerConnection(peerId){
        var self = this;

        if( this.peerConns[peerId] ){
            return;
        }

        this.peerConns[peerId] = new RTCPeerConnection(this.serversCfg);
        this.peerConns[peerId].ontrack = function (evt) { self.gotRemoteStream(evt, peerId); };
        this.peerConns[peerId].onicecandidate = function (evt) { self.iceCallback(evt, peerId); };

        this.addTracks(peerId);
    }

    private addTracks(peerId){
        var self = this;

        var localTracksCount = 0;
        window.localStream.getTracks().forEach(
            function (track) {
                self.peerConns[peerId].addTrack(
                    track,
                    window.localStream
                );
            }
        );
    }

    private call() {
        var self = this;

        for( let peerId in this.availablePeerIds ){
            if( !this.availablePeerIds.hasOwnProperty(peerId) ){
                continue;
            }
            if( this.isCaller(peerId) ) {
                this.preparePeerConnection(peerId);
                this.createOffer(peerId);
            }
        }
    }

    private createOffer(peerId){
        var self = this;

        this.peerConns[peerId].createOffer( this.offerOptions )
            .then( function (offer) { return self.peerConns[peerId].setLocalDescription(offer); } )
            .then( function () {
                self.SignalingChannel.send(JSON.stringify({ "sdp": self.peerConns[peerId].localDescription, "remotePeerId": peerId, "type": "onWebRTCPeerConn" }));
            })
            .catch( this.errorHandler );
    }

    private answerCall(peerId){
        var self = this;

        this.peerConns[peerId].createAnswer()
            .then( function (answer) { return self.peerConns[peerId].setLocalDescription(answer); } )
            .then( function () {
                self.SignalingChannel.send(JSON.stringify({ "sdp": self.peerConns[peerId].localDescription, "remotePeerId": peerId, "type": "onWebRTCPeerConn" }));
            })
            .catch( this.errorHandler );
    }

    ...

    private gotRemoteStream(e, peerId) {
        if (!this.videoBillboards[peerId]) {
            this.createMediaElements(peerId);
        }
        if (this.videoAssets[peerId].srcObject !== e.streams[0]) {
            this.videoAssets[peerId].srcObject = e.streams[0];
        }
    }

    private iceCallback(event, peerId) {
        this.SignalingChannel.send(JSON.stringify({ "candidate": event.candidate, "remotePeerId": peerId, "type": "onWebRTCPeerConn" }));
    }

    private handleCandidate(candidate, peerId) {
        this.peerConns[peerId].addIceCandidate(candidate)
            .then(
                this.onAddIceCandidateSuccess,
                this.onAddIceCandidateError
            );
    }

    ...

    private handleSignals(signal){
        var self = this,
            peerId = signal.connectionId;

        this.addPeerId(peerId);

        if( signal.sdp ) {
            var desc = new RTCSessionDescription(signal.sdp);

            this.peerConns[peerId].setRemoteDescription(desc)
                .then(function () {
                    if (desc.type === 'offer') {
                        self.answerCall(peerId);
                    }
                })
                .catch( this.errorHandler );
        } else if( signal.candidate ){
            this.handleCandidate(new RTCIceCandidate(signal.candidate), peerId);
        } else if( signal.closeConn ){
            this.endCall(peerId,true);
        }
    }
}
Eric Xyz
  • 452
  • 1
  • 5
  • 14
  • Make sure you add a stun/turn server also... – Keyne Viana Feb 28 '18 at 14:25
  • Yepp, i did. I edited the example above and added the code part for the ice servers that I'm using (default google servers). – Eric Xyz Feb 28 '18 at 14:44
  • You're using just stun, you also need a TURN server in case of firewall blocking it. You can create an account here https://global.xirsys.net/ to test if this is the problem – Keyne Viana Feb 28 '18 at 16:08
  • @KeyneViana thanks for the tipp with the TURN servers. I tried it with a turn server added. Still not working. In my client I also added code to be able to switch the source, which sends another offer to the other peer. If I do this on the callee side, so that the caller gets the offer for the new source from the callee, then the remote stream is displayed at the caller side. So the connection is working, it's just not sending the `ontrack` event to the caller when the inital answer is received and the connection initialized. – Eric Xyz Mar 01 '18 at 15:30
  • I could help more with the entire code, including signaling server.So that I can test it – Keyne Viana Mar 01 '18 at 16:44
  • @KeyneViana thanks for the offer but I can't share the whole code as it is integrated in a bigger piece of software and the signaling server is not only managing the WebRTC signals – Eric Xyz Mar 02 '18 at 09:18

1 Answers1

-1

Found a solution! Something was wrong with the options I put in to this.peerConns[peerId].createOffer( this.offerOptions ).

Actually in the code I provided one could not see it but the method that was dynamically creating my class member variable this.offerOptions had a bug. This was obviously telling the callee to NOT send any streams back to the caller.

Eric Xyz
  • 452
  • 1
  • 5
  • 14
  • 2
    Bit late, but any chance you can include here what were the `offerOptions` that actually told the callee to NOT send any streams back to caller? Having same issue more or less. – Thalis K. Sep 11 '19 at 09:03
  • 1
    If you came here and this answer didn't solve your problem this might: https://stackoverflow.com/questions/61354018/webrtc-connection-is-successful-but-answering-peer-never-receives-track-event – SlothFriend Apr 21 '20 at 22:06
  • What does `offerOptions` define actually? – Jignesh M. Khatri May 01 '20 at 07:21
  • @ThalisK. That is offerToReceiveVideo. Set this to false if you don't want any stream back. – ishan shah Sep 19 '20 at 07:38