public void SendCandidate(string connectionId, RTCIceCandidate candidate)
    {
        Debug.Log("Signaling: SendCandidate");

        CandidateMessage candidateMessage = new CandidateMessage();

        Ice ice = new Ice();

        ice.candidate     = candidate.candidate;
        ice.sdpMid        = candidate.sdpMid;
        ice.sdpMLineIndex = candidate.sdpMLineIndex;

        candidateMessage.ice = ice;
        this.WSSend(JsonUtility.ToJson(candidateMessage));
    }
    private void WSProcessMessage(object sender, MessageEventArgs e)
    {
        var content = Encoding.UTF8.GetString(e.RawData);

        Debug.Log($"Signaling: Receiving message: {content}");

        try
        {
            var    message = JsonUtility.FromJson <Message>(content);
            string type    = message.type;

            switch (type)
            {
            case "accept":
            {
                AcceptMessage acceptMessage = JsonUtility.FromJson <AcceptMessage>(content);
                this.m_acceptMessage = acceptMessage;
                this.OnAccept?.Invoke(this);
                break;
            }

            case "offer":
            {
                OfferMessage offerMessage = JsonUtility.FromJson <OfferMessage>(content);
                DescData     descData     = new DescData();
                descData.connectionId = this.m_acceptMessage.connectionId;
                descData.sdp          = offerMessage.sdp;

                this.OnOffer?.Invoke(this, descData);

                break;
            }

            case "answer":
            {
                AnswerMessage answerMessage = JsonUtility.FromJson <AnswerMessage>(content);
                DescData      descData      = new DescData();
                descData.connectionId = this.m_acceptMessage.connectionId;
                descData.sdp          = answerMessage.sdp;

                this.OnAnswer?.Invoke(this, descData);

                break;
            }

            case "candidate":
            {
                CandidateMessage candidateMessage = JsonUtility.FromJson <CandidateMessage>(content);

                CandidateData candidateData = new CandidateData();
                candidateData.connectionId  = this.m_acceptMessage.connectionId;
                candidateData.candidate     = candidateMessage.ice.candidate;
                candidateData.sdpMLineIndex = candidateMessage.ice.sdpMLineIndex;
                candidateData.sdpMid        = candidateMessage.ice.sdpMid;

                this.OnIceCandidate?.Invoke(this, candidateData);

                break;
            }

            case "ping":
            {
                PongMessage pongMessage = new PongMessage();
                this.WSSend(JsonUtility.ToJson(pongMessage));

                break;
            }

            case "bye":
            {
                // TODO:
                break;
            }

            default:
            {
                Debug.LogError("Signaling: Received message from unknown peer");
                break;
            }
            }
        }
        catch (Exception ex)
        {
            Debug.LogError("Signaling: Failed to parse message: " + ex);
        }
    }