I'm trying to get a sort of voice chat working using WebRTC and a WebSocket for exchanging offers.
First I create my RTCPeerConection
const pc = new RTCPeerConnection(configuration);
I then have this function as success callback of navigator.getUserMedia
let localStream: MediaStream;
export function setupRtc(stream: MediaStream) {
localStream = stream;
// add local media stream tracks to the connection
console.log("adding tracks");
console.log("localStream.getTracks()", localStream.getTracks());
localStream.getTracks().forEach((track: MediaStreamTrack) => {
pc.addTrack(track, localStream);
console.log("added track ", track);
});
console.log("done adding tracks");
// handle pcs tracks
let remoteStream = new MediaStream();
pc.ontrack = function(event: RTCTrackEvent) {
console.log("ontrack", event);
//add audio Tag
event.streams[0].getTracks().forEach((track) => {
remoteStream.addTrack(track);
});
}
let remoteAudio = <HTMLMediaElement> document.getElementById('remoteAudio');
remoteAudio.srcObject = remoteStream;
}
The Caller sends his offer like this:
async function sendOffer(connectionId: string) {
console.log("SENDING OFFER...");
//create offer desc
let sessionDesc: RTCSessionDescriptionInit = await pc.createOffer();
await pc.setLocalDescription(sessionDesc);
//send offer
socket.send(JSON.stringify(
{
type: "signaling_offer",
desc: {
sdp: sessionDesc.sdp,
type: sessionDesc.type
},
connectionId: connectionId
}
));
console.log("SEND OFFER");
}
The connectionId is an id that both Caller and Callee have.
After the WebSocket Server forwards the offer to the Callee he answers using this function:
function sendAnswer(connectionId: string, offer: RTCSessionDescriptionInit) {
console.log("SENDING ANSWER...");
pc.setRemoteDescription(new RTCSessionDescription(offer)).then(async () => {
const answerDesc = await pc.createAnswer();
await pc.setLocalDescription(answerDesc);
//send answer
socket.send(JSON.stringify(
{
type: "signaling_answer",
desc: {
sdp: answerDesc.sdp,
type: answerDesc.type
},
connectionId: connectionId
}
));
console.log("SEND ANSWER!");
});
}
After the WebSocket Server forwards that again the Caller handles the answer using this:
function handleAnswered(connectionId: string, answer: RTCSessionDescriptionInit) {
console.log("HANDLING ANSWER...");
pc.setRemoteDescription(new RTCSessionDescription(answer));
console.log("HANDLED ANSWER!");
}
In the tutorials and instructions I followed the audio element then seems to play the audio mine still looks like an audio element without a source: audio element that's not playing
Both Caller and Callee have added tracks and streams and have received some. I have also attached their console outputs:
Caller: caller console
Callee: callee console
So it turns out I completely missed a step. Both Caller and Callee need to exchange their ice candidates. So I added the following code:
pc.onicecandidate = event => {
if(event.candidate != undefined) {
let candidateInit: RTCIceCandidateInit = event.candidate.toJSON();
//send candidate
socket.send(JSON.stringify(
{
type: "signaling_add_candidate",
candidate: candidateInit,
connectionId: connectionId
}
));
console.log("send candidate from caller", event.candidate);
}
};
The server forwards that candidate to the other party. So from caller to callee and form callee to caller. To then add the forwarded candidates on the other side to the PeerConnection I added this code:
function handleNewCandidate(connectionId: string, candidateInit: RTCIceCandidateInit) {
pc.addIceCandidate(new RTCIceCandidate(candidateInit));
}