0

Is there anyway to notify offerer that non-existing track before just added to get the new stream from the answerer from the code below?

For my current issue now here is that the offerer can add new non-existing track and onnegotiationneeded will be fired and will also be able to createOffer and update media successfully, but when answerer do same process onnegotiationneeded fired normally also from the answerer but no media will be exchanged just because offerer do not have any new track on his end!

I use replaceOrAddTrack(remotePartiID, track, TrackKind) in adding and replacing of tracks

Only the replace works with either ends if it has same track kind from initial connection

_cfg = {
   sdpConstraints: {
     mandatory: {
       OfferToReceiveAudio: true,
       OfferToReceiveVideo: true,
       VoiceActivityDetection: true,
       IceRestart: true
     },
     optional: []
   }
   ...
 };



 var channels_wrap = (function() {
   return {
     ...
     init: function() {

       _cfg.defaultChannel.on('message', (message) => {
         if (_cfg.enableLog) {
           console.log('Client received message:', message);
         }
         if (message.type === 'newparticipant') {
           var partID = message.from;
           var partData = message.fromData;

           // Open a new communication channel to the new participant
           _cfg.offerChannels[partID] = this.openSignalingChannel(partID);

           // Wait for answers (to offers) from the new participant
           _cfg.offerChannels[partID].on('message', (msg) => {
             if (msg.dest === _cfg.myID) {

               if (msg.type === 'reoffer') {
                 if (_cfg.opc.hasOwnProperty(msg.from)) {
                   console.log('reoffering')
                   _cfg.opc[msg.from].negotiationNeeded();
                 }

               } else
               if (msg.type === 'answer') {
                 _cfg.opc[msg.from].peer.setRemoteDescription(new RTCSessionDescription(msg.snDescription),
                   handlers_wrap.setRemoteDescriptionSuccess,
                   handlers_wrap.setRemoteDescriptionError);
               } else if (msg.type === 'candidate') {
                 var candidate = new RTCIceCandidate({
                   sdpMLineIndex: msg.label,
                   candidate: msg.candidate
                 });
                 if (_cfg.enableLog) {
                   console.log('got ice candidate from ' + msg.from);
                 }
                 _cfg.opc[msg.from].peer.addIceCandidate(candidate, handlers_wrap.addIceCandidateSuccess, handlers_wrap.addIceCandidateError);
               }
             }
           });

           // Send an offer to the new participant
           dialogs_wrap.createOffer(partID, partData);

         } else if (message.type === 'bye') {
           handlers_wrap.hangup(message.from, message.fromData);
         }
       });
     },
     initPrivateChannel: function() {
       // Open a private channel (namespace = _cfg.myID) to receive offers
       _cfg.privateAnswerChannel = this.openSignalingChannel(_cfg.myID);

       // Wait for offers or ice candidates
       _cfg.privateAnswerChannel.on('message', (message) => {
         if (message.dest === _cfg.myID) {
           if (message.type === 'offer') {
             var to = message.from;

             dialogs_wrap.createAnswer(message.snDescription, _cfg.privateAnswerChannel, to, message.fromData);
           } else if (message.type === 'candidate') {
             var candidate = new RTCIceCandidate({
               sdpMLineIndex: message.label,
               candidate: message.candidate
             });
             _cfg.apc[message.from].peer.addIceCandidate(candidate, handlers_wrap.addIceCandidateSuccess, handlers_wrap.addIceCandidateError);
           }
         }
       });
     }
   };
 })();

 var tracks_wrap = (function() {
   return {
     getParticipants: function(partID = null) {
       var participants = {};
       if (partID) {
         if (_cfg.opc.hasOwnProperty(partID)) {
           participants[partID] = {
             ID: partID,
             type: 'opc'
           };
         } else
         if (_cfg.apc.hasOwnProperty(partID)) {
           participants[partID] = {
             ID: partID,
             type: 'apc'
           };
         }
       } else {
         for (let key in _cfg.opc) {
           participants[key] = {
             ID: key,
             type: 'opc'
           };
         }
         for (let key in _cfg.apc) {
           participants[key] = {
             ID: key,
             type: 'apc'
           };
         }
       }
       return participants;
     },
     replaceOrAddTrack: function(remotePartiID, track, TrackKind) {
       if (!TrackKind) {
         return;
       }
       var participants = this.getParticipants(remotePartiID);
       for (var partiID in participants) {
         var peer = null;
         if (participants[partiID].type === 'apc' && _cfg.apc.hasOwnProperty(partiID)) {
           peer = _cfg.apc[partiID].peer;
         } else if (participants[partiID].type === 'opc' && _cfg.opc.hasOwnProperty(partiID)) {
           peer = _cfg.opc[partiID].peer;
         } else {
           continue;
         }
         var foundTrack = null;
         peer.getSenders().forEach(function(rtpSender) {
           if (rtpSender.track && TrackKind === rtpSender.track.kind) {
             foundTrack = true;
             rtpSender.replaceTrack(track);
           }
         });
         if (!foundTrack) {
           peer.addTrack(track, _cfg.localStream); //This work only if it is offerrer that add track but not working with answerer even if i tell the offerer to send offer again
         }
       }
     }
   };
 })();

 var dialogs_wrap = (function() {
   return {

     /**
      *
      * Send an offer to peer with id partID and metadata as partData
      *
      */
     createOffer: function(partID, partData) {
       if (_cfg.enableLog) {
         console.log('Creating offer for peer ' + partID, partData);
       }
       var opcPeer = new RTCPeerConnection(_cfg.pcConfig, _cfg.peerSetup);
       _cfg.opc[partID] = {};
       _cfg.opc[partID].peer = opcPeer;
       _cfg.opc[partID].peer.onicecandidate = handlers_wrap.handleIceCandidateAnswer(_cfg.offerChannels[partID], partID, partData);
       _cfg.opc[partID].peer.ontrack = handlers_wrap.handleRemoteStreamAdded(partID, partData);
       _cfg.opc[partID].peer.onremovetrack = handlers_wrap.handleRemoteStreamRemoved(partID, partData);
       _cfg.localStream.getTracks().forEach(track => _cfg.opc[partID].peer.addTrack(track, _cfg.localStream));

       try {
         _cfg.sendChannel[partID] = _cfg.opc[partID].peer.createDataChannel("sendDataChannel", {
           reliable: false
         });
         _cfg.sendChannel[partID].onmessage = handlers_wrap.handleMessage;
         if (_cfg.enableLog) {
           console.log('Created send data channel');
         }
       } catch (e) {
         alert('Failed to create data channel.  \n You need supported RtpDataChannel enabled browser');
         console.log('createDataChannel() failed with exception: ', e.message);
       }
       _cfg.sendChannel[partID].onopen = handlers_wrap.handleSendChannelStateChange(partID);
       _cfg.sendChannel[partID].onclose = handlers_wrap.handleSendChannelStateChange(partID);

       var onSuccess = (partID, partData) => {
         var channel = _cfg.offerChannels[partID];
         if (_cfg.enableLog) {
           console.log('Sending offering');
         }
         channel.emit('message', {
           snDescription: _cfg.opc[partID].peer.localDescription,
           from: _cfg.myID,
           fromData: _cfg.myData,
           type: 'offer',
           dest: partID,
           destData: partData
         });

       }
       _cfg.opc[partID].negotiationNeeded = () => {

         _cfg.opc[partID].peer.createOffer(_cfg.sdpConstraints).then(offer => {
             offer.sdp = sdp_wrap.SDPController(offer.sdp);
             return _cfg.opc[partID].peer.setLocalDescription(offer)
           })
           .then(() => onSuccess(partID, partData)).catch(handlers_wrap.handleCreateOfferError);
       }
       _cfg.opc[partID].peer.onnegotiationneeded = () => {
         _cfg.opc[partID].negotiationNeeded();
       }
     },

     createAnswer: function(snDescription, cnl, to, toData) {
       if (_cfg.enableLog) {
         console.log('Creating answer for peer ' + to);
       }
       if (!_cfg.apc.hasOwnProperty(to)) {
         var apcPeer = new RTCPeerConnection(_cfg.pcConfig, _cfg.peerSetup);
         //apcPeer.setConfiguration(_cfg.pcConfig);
         _cfg.apc[to] = {};
         _cfg.apc[to].peer = apcPeer;
         _cfg.apc[to].peer.onicecandidate = handlers_wrap.handleIceCandidateAnswer(cnl, to, toData);
         _cfg.apc[to].peer.ontrack = handlers_wrap.handleRemoteStreamAdded(to, toData);
         _cfg.apc[to].peer.onremovetrack = handlers_wrap.handleRemoteStreamRemoved(to, toData);
         _cfg.localStream.getTracks().forEach(track => _cfg.apc[to].peer.addTrack(track, _cfg.localStream));
         _cfg.apc[to].peer.ondatachannel = handlers_wrap.gotReceiveChannel(to);
       }
       _cfg.apc[to].peer.setRemoteDescription(new RTCSessionDescription(snDescription), handlers_wrap.setRemoteDescriptionSuccess, handlers_wrap.setRemoteDescriptionError);

       var onSuccess = (channel) => {
         if (_cfg.enableLog) {
           console.log('Sending answering');
         }
         channel.emit('message', {
           snDescription: _cfg.apc[to].peer.localDescription,
           from: _cfg.myID,
           fromData: _cfg.myData,
           type: 'answer',
           dest: to,
           destData: toData
         });

       }
       _cfg.apc[to].peer.createAnswer().then(function(answer) {
           answer.sdp = sdp_wrap.SDPController(answer.sdp);
           return _cfg.apc[to].peer.setLocalDescription(answer);
         })
         .then(() => onSuccess(cnl))
         .catch(handlers_wrap.handleCreateAnswerError);
       var negotiationNeeded = false;
       _cfg.apc[to].peer.onnegotiationneeded = (ev) => {
         if (!negotiationNeeded) {
           negotiationNeeded = true;
           return;
         }
         //So i tried to create this to tell the offerer to do offer again, offerer do resend offer but nothing seem to happen
         cnl.emit('message', {
           from: _cfg.myID,
           fromData: _cfg.myData,
           type: 'reoffer',
           dest: to,
           destData: toData
         });
       }
     }
   };
 })();
General Omosco
  • 606
  • 1
  • 10
  • 20
  • I'm not fully comprehending the question. What do you mean by "non-existing track"? I'm having a bit of trouble following the code with all the wrappers. Can you [reduce it](https://stackoverflow.com/help/minimal-reproducible-example)? – jib Jul 31 '19 at 19:01
  • I also see some ancient patterns in here. If this is in a browser, then `_cfg` isn't doing anything. Also, why are you modifying the ICE candidates and the SDP? That can only spell trouble. – jib Jul 31 '19 at 19:04
  • 1
    You're also using the words "offerer" and "answerer" as if they're fixed roles. Is that what you're trying to do? Use out-of-band signaling to have one side always be the offerer? It's a common technique to avoid glare, but it adds complexity obviously. Note though the WebRTC API itself was designed to allow reversing roles as needed—hence `negotiationneeded` firing on both ends. Doing so in a way that's glare-proof was the topic of [my last blog](https://blog.mozilla.org/webrtc/perfect-negotiation-in-webrtc/). – jib Jul 31 '19 at 19:24
  • Thanks Yes that true, i have to change the architecture. – General Omosco Jul 31 '19 at 19:36

0 Answers0