Angular & Firefox: WebRTC: ICE failed, see about:webrtc for more details

61 Views Asked by At

I carefully followed the WebRTC Documentation (https://webrtc.org/getting-started/overview), went great, but when the peers exchange the ICE, it goes wrong. Im using Google's STUN servers, and installed CoTurn (sudo pacman -S coturn) temporally for testing locally.

Configuration:

const configuration = {
          'iceServers': [
            { 'urls': 'stun:stun.l.google.com:19302' },
            { urls: 'turn:192.168.1.71:3478', username: 'username', credential: 'password' }
          ]
        }

Heres my client side code written in TypeScript (Angular). It's using Websockets to communicate with the API:

var peerConnection = new RTCPeerConnection(configuration);

        peerConnection.addEventListener('connectionstatechange', event => {
          if (peerConnection.connectionState === 'connected') {
            console.warn("SUCCESS! PEERS CONNECTED")
          }
        });

        this.checkloginservice.socket$.subscribe(
          async (message) => {
            let action = (message as websocketDef).action
            switch (action) {
              case "offer":
                peerConnection.addEventListener('icecandidate', (event) => {
                  if (event.candidate) {
                    console.log(event.candidate)
                    this.checkloginservice.socket$.next({
                      Action: 'icecandidate',
                      Value: JSON.stringify({
                        for: this.callData.peerdata.userid,
                        icecandidate: JSON.stringify(event.candidate),
                        action: "icecandidate"
                      })
                    })
                  }
                });
                let data = message as incomingCallData
                this.incomingCall = {
                  callerDisplayname: data.displayName,
                  callerId: data.userid,
                  callerPfp: data.pfp,
                  chatid: data.chatid,
                }

                setTimeout(() => {
                  this.document.querySelector(".ans")?.addEventListener("click", async () => {
                    peerConnection.setRemoteDescription(new RTCSessionDescription(JSON.parse(data.offer)))
                    const answer = await peerConnection.createAnswer();
                    await peerConnection.setLocalDescription(answer);
                    this.checkloginservice.socket$.next({
                      Action: "answer",
                      Value: JSON.stringify({
                        for: data.userid,
                        answer: JSON.stringify(answer),
                        action: "answer"
                      })
                    })
                  })
                }, 10);
                break;
              case "answer":
                peerConnection.addEventListener('icecandidate', (event) => {
                  if (event.candidate) {
                    console.log(event.candidate)
                    this.checkloginservice.socket$.next({
                      Action: 'icecandidate',
                      Value: JSON.stringify({
                        for: this.callData.peerdata.userid,
                        icecandidate: JSON.stringify(event.candidate),
                        action: "icecandidate"
                      })
                    })
                  }
                });
                let answerData = message as answerCallData
                const remoteDesc = new RTCSessionDescription(JSON.parse(answerData.answer));
                await peerConnection.setRemoteDescription(remoteDesc);
                break;

              case "icecandidate":
                let icacand = message as iceCandData

                try {
                  await peerConnection.addIceCandidate(JSON.parse(icacand.icecandidate));
                } catch (e) {
                  console.error('Error adding received ice candidate', e);
                }

            }
          }
        )

        this.msgService.callFriend$.subscribe(async (calldata) => {
          if (calldata.friendid != '') {
            if (await this.chkMicPerms()) {
              let stream = await this.getUserMedia(false)
              if (stream == typeof String) {
                this.toastService.add({
                  severity: "error",
                  summary: this.translateService.instant("callFailed"),
                  detail: this.translateService.instant(stream)
                })
              } else {
                localStream = stream as MediaStream
                this.callData.peerdata.userid = calldata.friendid
                this.callData.peerdata.displayName = calldata.friendname;
                this.callData.peerdata.pfp = calldata.friendpfp;
                this.callData.chatid = calldata.chatid;
                const offer = await peerConnection.createOffer({
                  offerToReceiveAudio: true
                });
                await peerConnection.setLocalDescription(offer)
                this.checkloginservice.socket$.next({
                  Action: 'offer',
                  Value: JSON.stringify({
                    for: calldata.friendid,
                    offer: JSON.stringify(offer),
                    username: userData.username,
                    userid: userData.userid,
                    displayName: userData.displayName,
                    pfp: userData.pfp,
                    chatid: calldata.chatid,
                    action: "offer"
                  })
                })
              }

            } else {
              this.toastService.add({
                severity: "error",
                summary: this.translateService.instant("callFailed"),
                detail: this.translateService.instant("provideMic")
              })
            }
          }
        })

Server-side code written in go:

var (
    upgrader = websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    }

    Rm = &roomManager{
        rooms: make(map[string]*Client),
        mu:    sync.Mutex{},
    }
)

func (rm *roomManager) Broadcast(message interface{}, userid string) {
    rm.mu.Lock()
    defer rm.mu.Unlock()

    for _, userConnection := range rm.rooms {
        if userConnection.Connection != nil && userConnection.Userid == userid {
            // if err := userConnection.Connection.WriteJSON(message); err != nil {}
            userConnection.Connection.WriteJSON(message)
        }
    }
}

func HandleWebSocket(c *gin.Context) {
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
        return
    }

    client := &Client{
        Connection: conn,
    }

    for {
        _, p, err := conn.ReadMessage()

        if err != nil {
            Rm.removeUser(client.Userid)
            return
        }

        var message config.Websocket_DefaultMessage
        if err := json.Unmarshal(p, &message); err != nil {
            return
        }

        switch message.Action {
        case "joinRealtime":

            Rm.connectUser(conn, message.Value)
            client.Userid = message.Value
            Rm.Broadcast("Connected to WebSocket /user", message.Value)

        case "offer":
            type offerData struct {
                For         string `json:"for"`
                Offer       string `json:"offer"`
                Username    string `json:"username"`
                Userid      string `json:"userid"`
                DisplayName string `json:"displayName"`
                Pfp         string `json:"pfp"`
                Chatid      string `json:"chatid"`
                Action      string `json:"action"`
            }

            var offer offerData

            if err := json.Unmarshal([]byte(message.Value), &offer); err != nil {
                return
            }
            Rm.Broadcast(offer, offer.For)

        case "answer":
            type answerData struct {
                For    string `json:"for"`
                Answer string `json:"answer"`
                Action string `json:"action"`
            }

            var answer answerData

            if err := json.Unmarshal([]byte(message.Value), &answer); err != nil {
                return
            }

            Rm.Broadcast(answer, answer.For)

        case "icecandidate":
            type iceCandData struct {
                For          string `json:"for"`
                Action       string `json:"action"`
                Icecandidate string `json:"icecandidate"`
            }

            var iceCand iceCandData

            if err := json.Unmarshal([]byte(message.Value), &iceCand); err != nil {
                return
            }
            Rm.Broadcast(iceCand, iceCand.For)
        }
    }
}

Help would be appreciated. Thanks!

0

There are 0 best solutions below