public override async Task OnSdpAnswerAsync(RelayMessage message)
        {
            await Context.PeerConnection.SetRemoteDescription(new RTCSessionDescription(RTCSdpType.Answer, message.Payload));

            if (SdpUtils.IsHold(message.Payload))
            {
                await Context.SwitchState(new Held());
            }
            else
            {
                await Context.SwitchState(new Active());
            }
        }
        public override async Task OnSdpOfferAsync(RelayMessage message)
        {
            bool isHold = SdpUtils.IsHold(message.Payload);

            if (isHold)
            {
                Context.VoipHelper.SetCallHeld();
            }
            else
            {
                Context.VoipHelper.SetCallActive(Context.PeerId, Context.IsVideoEnabled);
            }

            // If PeerConnection is not null, then this is an SDP renegotiation.
            if (Context.PeerConnection == null)
            {
                var config = new RTCConfiguration
                {
                    IceServers = WebRtcSettingsUtils.ToRTCIceServer(IceServerSettings.IceServers)
                };
                Context.PeerConnection = new RTCPeerConnection(config);
            }

            if (isHold)
            {
                // Even for just a renegotiation, it's easier to just teardown the media capture and start over.
                if (Context.LocalStream != null)
                {
                    Context.PeerConnection.RemoveStream(Context.LocalStream);
                }
                Context.LocalStream?.Stop();
                Context.LocalStream = null;
                Context.RemoteStream?.Stop();
                Context.RemoteStream = null;
                Context.ResetRenderers();
            }

            MediaVideoTrack oldVideoTrack = Context.RemoteStream?.GetVideoTracks()?.FirstOrDefault();

            await Context.PeerConnection.SetRemoteDescription(new RTCSessionDescription(RTCSdpType.Offer, message.Payload));

            MediaVideoTrack newVideoTrack = Context.RemoteStream?.GetVideoTracks()?.FirstOrDefault();

            bool videoTrackChanged = oldVideoTrack != null && newVideoTrack != null &&
                                     oldVideoTrack.Id.CompareTo(newVideoTrack.Id) != 0;

            if (videoTrackChanged)
            {
                Context.ResetRemoteRenderer();
                var source = RtcManager.Instance.Media.CreateMediaSource(newVideoTrack, CallContext.PeerMediaStreamId);
                Context.RemoteVideoRenderer.SetupRenderer(Context.ForegroundProcessId, source, Context.RemoteVideoControlSize);
            }
            else if (!isHold)
            {
                Context.LocalStream = await RtcManager.Instance.Media.GetUserMedia(new RTCMediaStreamConstraints
                {
                    videoEnabled = Context.IsVideoEnabled,
                    audioEnabled = true
                });

                Context.PeerConnection.AddStream(Context.LocalStream);

                // Setup the rendering of the local capture.
                var tracks = Context.LocalStream.GetVideoTracks();
                if (tracks.Count > 0)
                {
                    var source = RtcManager.Instance.Media.CreateMediaSource(tracks[0], CallContext.LocalMediaStreamId);
                    Context.LocalVideoRenderer.SetupRenderer(Context.ForegroundProcessId, source, Context.LocalVideoControlSize);
                }
            }

            var sdpAnswer = await Context.PeerConnection.CreateAnswer();

            await Context.PeerConnection.SetLocalDescription(sdpAnswer);

            var sdpVideoCodecIds = SdpUtils.GetVideoCodecIds(message.Payload);

            if (sdpVideoCodecIds.Count > 0)
            {
                Context.VideoCodecUsed = Array.Find((await Hub.Instance.MediaSettingsChannel.GetVideoCodecsAsync())?.Codecs,
                                                    it => it.Id == sdpVideoCodecIds.First())?.FromDto();
            }

            Context.SendToPeer(RelayMessageTags.SdpAnswer, sdpAnswer.Sdp);
            if (isHold)
            {
                await Context.SwitchState(new Held());
            }
            else
            {
                await Context.SwitchState(new Active());
            }
        }