I am working on a web application where I am using the jsSIP library for handling SIP communication. My goal is to implement a conference call feature where I can mix the media streams of multiple sessions on the client side. Essentially, I want to create an "add-to-call" feature to enable a conference call experience.
I've successfully handled incoming and outgoing calls, but I'm struggling with efficiently managing multiple sessions and mixing their media streams. My questions are:
How can I retrieve all active sessions on the client side using jsSIP?
Once I have the sessions, how can I mix their media streams to create a conference call experience?
const attachRemoteStream = (
session: RTCPeerConnectionDeprecated,
elementId: string
) => {
const mediaElement = getVideoElement(elementId);
session.ontrack = (event) => {
if (event.track.kind === "video") {
console.log("event.streams RemoteStream", event.streams);
if (event.streams.length > 0 && mediaElement) {
console.log("RemoteStream If block is called");
mediaElement.srcObject = event.streams[0];
mediaElement.play();
} else {
console.log("RemoteStream Else block is called");
const stream = new MediaStream([event.track]);
if (mediaElement) {
mediaElement.srcObject = stream;
mediaElement.play();
}
}
}
};
};
const attachLocalStream = (session: RTCSession, elementId: string) => {
const mediaElement = getVideoElement(elementId);
session.connection.ontrack = (event) => {
if (event.track.kind === "audio") {
console.log("event.streams LocalStream", event.streams);
if (event.streams.length > 0 && mediaElement) {
console.log("LocalStream If block is called");
mediaElement.srcObject = event.streams[0];
mediaElement.play();
} else {
console.log("Else block is called");
const stream = new MediaStream([event.track]);
if (mediaElement) {
mediaElement.srcObject = stream;
mediaElement.play();
}
}
}
};
};
const attachIncommingLocalStream = (
session: RTCSession,
elementId: string
) => {
const mediaElement = getVideoElement(elementId);
if (!mediaElement) {
console.error(`Media element with id ${elementId} not found.`);
return;
}
// Assuming there is a local video track, add it to the local stream
const localVideoSender = session.connection.getSenders().find((sender) => {
return sender.track?.kind === "video";
});
if (localVideoSender && localVideoSender.track) {
const localStream = new MediaStream([localVideoSender.track]);
// Attach the local stream to the video element
mediaElement.srcObject = localStream;
mediaElement.play();
} else {
console.error("No local video track found in the session.");
}
};
const outgoingCall = async (session: RTCSession) => {
attachLocalStream(session, "videoRemote");
session.on("progress", () => {
console.log("outgoingCall call is in progress");
setOutgoingCall(true);
});
session.on("connecting", () => {
console.log("outgoingCall call is in connecting ...");
});
session.on("ended", () => {
console.log("outgoingCall call has ended");
navigate("/");
setOutgoingCall(false);
});
session.on("confirmed", () => {
console.log("outgoingCall is confirmed");
setOutgoingCall(false);
});
session.on("failed", () => {
console.log("outgoingCall unable to establish the call");
navigate("/");
setOutgoingCall(false);
});
session.on("accepted", (e: any) => {
attachIncommingLocalStream(session, "videoLocal");
navigate("/answer");
});
};
const inCommingCall = (session: RTCSession) => {
session.on("progress", () => {
console.log("inCommingCall is in progress");
setIncommingCall(true);
});
session.on("accepted", () => {
console.log("inCommingCall call has answered");
navigate("/answer");
setIncommingCall(false);
});
session.on("connecting", () => {
console.log("inCommingCall call is in connecting ...");
});
session.on("confirmed", () => {
console.log(
"inCommingCall handler will be called for incoming calls too"
);
attachIncommingLocalStream(session, "videoLocal");
setIncommingCall(false);
});
session.on("ended", () => {
console.log("inCommingCall call has ended");
setIncommingCall(false);
navigate("/");
});
session.on("failed", () => {
console.log("inCommingCall unable to establish the call");
setIncommingCall(false);
});
session.on("peerconnection", (e) => {
attachRemoteStream(e.peerconnection, "videoRemote");
});
session.on("sdp", (e: SDPEvent) => {
console.log("SDP Events are", e);
});
};
useEffect(() => {
sipClient?.on("newRTCSession", (data: RTCSessionEvent) => {
const session: RTCSession = data.session;
setSession(session);
const direction = session.direction;
console.log("direction", direction);
setSession(session);
if (direction === "incoming") {
inCommingCall(session);
} else if (direction === "outgoing") {
outgoingCall(session);
}
});
}, [sipClient]);