javascriptwebrtcsipvoipjssip

JsSIP "peerconnection" Event Not Firing for Outbound Calls - Why and How to Fix?


I’m building a web-based SIP phone application using JsSIP (version 3.10.0) to handle VoIP calls over WebRTC. My setup works fine for inbound calls—audio streams both ways—but I’m facing an issue with outbound calls where I can’t hear the remote party, and the "peerconnection" event isn’t firing as expected.

What I’m Doing

Setup: I’m using JsSIP to connect to a SIP server via WebSocket (WSS). The client registers successfully and can initiate/receive calls. Code Structure: I handle all new sessions (inbound and outbound) in a handleNewRTCSession function triggered by the "newRTCSession" event from JsSIP’s UA. For WebRTC, I rely on the "peerconnection" event to access the RTCPeerConnection and attach a "track" listener to capture the remote audio stream. Outbound Call Flow: I call userAgent.call(target, callOptions) to start an outbound call. The call connects (remote party answers), and they can hear me, but I don’t hear them.

 const userAgent = new JsSIP.UA({
  uri: "sip:user@example.com",
  password: "****",
  sockets: [new JsSIP.WebSocketInterface("wss://sip-server.example.com:7443")],
});

const callOptions = {
  mediaConstraints: { audio: true, video: false },
  pcConfig: { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] },
};

userAgent.on("newRTCSession", handleNewRTCSession);

function handleNewRTCSession(e) {
  const session = e.session;
  console.log("New session:", session.direction);

  session.on("peerconnection", (data) => {
    console.log("Peerconnection event:", data);
    const peerConnection = data.peerconnection;
    peerConnection.addEventListener("track", (event) => {
      console.log("Remote stream received:", event.streams[0]);
      document.getElementById("remoteAudio").srcObject = event.streams[0];
    });
  });

  session.on("accepted", () => {
    console.log("Call accepted");
  });

  session.on("failed", (e) => {
    console.log("Call failed:", e.cause);
  });

  if (session.direction === "outgoing") {
    console.log("Outbound call started");
  }
}

function makeCall(target) {
  userAgent.call(target, callOptions);
}

// Start UA
userAgent.start();

The Workaround which i'm trying right now is forcing peerconnection when the call is accepted in outbound case:

   if (session.connection && session.direction === óutgoing) {
          console.log("Manually handling peer connection:", session.connection);
          handlePeerConnection({ peerconnection: session.connection });
        }

function handlePeerConnection(data) {
  console.log(data);
  const peerConnection = data.peerconnection;
  peerConnection.addEventListener("track", (event) => {
    console.log(event);
    console.log("StreamViewType", event.streams);
    const remoteStream = event.streams[0];
    console.log("Remote stream received:", remoteStream);
    audioElement.srcObject = remoteStream;
    audioElement
      .play()
      .catch((e) => console.error("Audio playback failed:", e));
  });

  peerConnection.addEventListener("icecandidate", (event) => {
    if (event.candidate) {
      console.log("ICE candidate:", event.candidate);
    } else {
      console.log("ICE gathering complete");
    }
  });
}

Questions

  1. Why isn’t the "peerconnection" event firing for outbound calls in JsSIP 3.10.0, even though the call connects and session.connection exists?
  2. Is this a known bug, a configuration issue, or something specific to my SIP server’s SDP/ICE handling?
  3. Is manually using session.connection a safe long-term solution, or should I adjust my approach (e.g., update JsSIP, change callOptions)?

Additional Details

JsSIP Version: 3.10.0

Browser: Chrome (latest)

SIP Server: A hosted FusionPBX.

SDP: Local offers Opus/G722/PCMU, remote responds with G722, both sendrecv.

I’d appreciate any insights or suggestions to fix this cleanly without relying on the workaround. Thanks!


Solution

  • My approach is to

    // modify your function makeCall(target)
    function makeCall(target) {
        outgoingSession = userAgent.call(target, callOptions);
        setupMedia(outgoingSession);
    }
    

    and then implement

    session.connection.addEventListener('track', ({ track, streams: [stream] }) => {
        // logic identical to your peerConnection.addEventListener("track", (event) => {});
    });
    

    within setupMedia.

    The specific reason, I suspect, is that addEventListener only captures newly added tracks. In your method, during outgoing calls, the audio may not be considered newly added?