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());
            }
        }
Exemple #2
0
        public async Task <ICallInfo> PlaceCallAsync(CallConfiguration config)
        {
            Debug.Assert(_peerId == -1);

            if (PeerConnection != null)
            {
                Debug.WriteLine("[Error] We only support connection to one peer at a time.");
                return(null);
            }

            if (CreatePeerConnection())
            {
                string selectedAudioCodecName = (string)_localSettings.Values["SelectedAudioCodecName"];
                string selectedVideoCodecName = (string)_localSettings.Values["SelectedVideoCodecName"];

                _peerId = PeerId;

                var offerOptions = new RTCOfferOptions();
                offerOptions.OfferToReceiveAudio = true;
                offerOptions.OfferToReceiveVideo = true;
                IRTCSessionDescription offer = await PeerConnection.CreateOffer(offerOptions);

                // Alter sdp to force usage of selected codecs
                string modifiedSdp = offer.Sdp;
                SdpUtils.SelectCodec(ref modifiedSdp, selectedAudioCodecName, "audio");
                SdpUtils.SelectCodec(ref modifiedSdp, selectedVideoCodecName, "video");
                RTCSessionDescriptionInit sdpInit = new RTCSessionDescriptionInit();
                sdpInit.Sdp  = modifiedSdp;
                sdpInit.Type = offer.SdpType;
                var modifiedOffer = new RTCSessionDescription(sdpInit);
                await PeerConnection.SetLocalDescription(modifiedOffer);

                Debug.WriteLine($"Sending offer: {modifiedOffer.Sdp}");

                string jsonString = SdpToJsonString(modifiedOffer);

                CallInfo callInfo = new CallInfo();
                callInfo.SetCall(new Call());
                callInfo.SetSdp(modifiedSdp);
                callInfo.SetJsonString(jsonString);

                OnSendMessageToRemotePeer.Invoke(this, jsonString);

                return(callInfo);
            }
            return(null);
        }
        public override async Task OnEnteringState()
        {
            Debug.Assert(Context.PeerConnection == null);

            Context.VoipCoordinator.StartOutgoingCall(_callRequest);

            var config = new RTCConfiguration
            {
                IceServers = WebRtcSettingsUtils.ToRTCIceServer(IceServerSettings.IceServers)
            };

            Context.PeerConnection = new RTCPeerConnection(config);

            Context.LocalStream = await Context.Media.GetUserMedia(new RTCMediaStreamConstraints
            {
                videoEnabled = _callRequest.VideoEnabled,
                audioEnabled = true
            });

            Context.PeerConnection.AddStream(Context.LocalStream);
            var sdpOffer = await Context.PeerConnection.CreateOffer();

            var sdpString = sdpOffer.Sdp;

            SdpUtils.SelectCodecs(ref sdpString, DtoExtensions.FromDto(Context.GetAudioCodec()), DtoExtensions.FromDto(Context.GetVideoCodec()));
            sdpOffer.Sdp = sdpString;
            await Context.PeerConnection.SetLocalDescription(sdpOffer);

            var tracks = Context.LocalStream.GetVideoTracks();

            if (tracks.Count > 0)
            {
#if WIN10
                var source = Context.Media.CreateMediaSource(tracks[0], VoipContext.LocalMediaStreamId);
#else
                var source = Context.Media.CreateMediaStreamSource(tracks[0], 30, VoipContext.LocalMediaStreamId);
#endif
                Context.LocalVideoRenderer.SetupRenderer(Context.ForegroundProcessId, source, Context.LocalVideoControlSize);
            }

            Context.SendToPeer(RelayMessageTags.SdpOffer, sdpOffer.Sdp);
        }
Exemple #4
0
        /// <summary>
        /// Calls to connect to the selected peer.
        /// </summary>
        /// <param name="peer">Peer to connect to.</param>
        public async void ConnectToPeer(Peer peer)
        {
            Debug.Assert(peer != null);
            Debug.Assert(_peerId == -1);

            if (_peerConnection != null)
            {
                Debug.WriteLine("[Error] Conductor: We only support connecting to one peer at a time");
                return;
            }
#if ORTCLIB
            _signalingMode = Helper.SignalingModeForClientName(peer.Name);
#endif
            _connectToPeerCancelationTokenSource = new System.Threading.CancellationTokenSource();
            _connectToPeerTask = CreatePeerConnection(_connectToPeerCancelationTokenSource.Token);
            bool connectResult = await _connectToPeerTask;
            _connectToPeerTask = null;
            _connectToPeerCancelationTokenSource.Dispose();

            if (connectResult)
            {
                _peerId = peer.Id;
                var offer = await _peerConnection.CreateOffer();

#if !ORTCLIB
                // Alter sdp to force usage of selected codecs
                string newSdp = offer.Sdp;
                SdpUtils.SetMediaBitrate(ref newSdp, "video", VideoBitrate);
                SdpUtils.SelectCodecs(ref newSdp, AudioCodec, VideoCodec);
                offer.Sdp = newSdp;
#endif
                await _peerConnection.SetLocalDescription(offer);

                Debug.WriteLine("Conductor: Sending offer.");
                SendSdp(offer);
#if ORTCLIB
                OrtcStatsManager.Instance.StartCallWatch(SessionId, true);
#endif
            }
        }
        public override async Task OnEnteringStateAsync()
        {
            if (_reason == Reason.HoldCall)
            {
                Context.VoipHelper.SetCallHeld();
            }
            else if (Context.VoipHelper.HasCall())
            {
                Context.VoipHelper.SetCallActive(Context.PeerId, Context.IsVideoEnabled);
            }
            else
            {
                Context.VoipHelper.StartOutgoingCall(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);
            }

            switch (_reason)
            {
            case Reason.HoldCall:
            {
                // 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);
                }
                if (Context.RemoteStream != null)
                {
                    Context.PeerConnection.RemoveStream(Context.RemoteStream);
                }
                foreach (var stream in Context.PeerConnection.GetLocalStreams())
                {
                    Debug.WriteLine("Streams remaining.");
                }

                Context.LocalStream?.Stop();
                Context.LocalStream = null;
                Context.RemoteStream?.Stop();
                Context.RemoteStream = null;
                Context.ResetRenderers();
                break;
            }

            case Reason.EstablishCall:
            {
                Context.LocalStream?.Stop();
                Context.LocalStream = null;
                Context.RemoteStream?.Stop();
                Context.RemoteStream = null;
                Context.ResetRenderers();

                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);
                }
                break;
            }

            case Reason.SwitchCamera:
            {
                var videoDevice = await Context.MediaSettingsChannel.GetVideoDeviceAsync();

                // We expect the camera we want to switch to is selected.
                if (videoDevice == null)
                {
                    // This could happen only in an error scenario and would have the
                    // effect of loosing the video.
                    // Since will not be able to continue the video call, just end the call.
                    await Context.SwitchState(new HangingUp());

                    return;
                }
                RtcManager.Instance.Media.SelectVideoDevice(videoDevice.FromDto());

                // Remove old video track.
                var  videoTracks       = Context.LocalStream.GetVideoTracks();
                bool videoTrackEnabled = true;
                if (videoTracks.Count > 0)
                {
                    var oldVideoTrack = videoTracks[0];
                    videoTrackEnabled = oldVideoTrack.Enabled;
                    oldVideoTrack.Stop();
                    Context.LocalStream.RemoveTrack(oldVideoTrack);
                }
                videoTracks.Clear();
                Context.ResetLocalRenderer();

                // Create new stream having a new video track.
                var newStream = await RtcManager.Instance.Media.GetUserMedia(new RTCMediaStreamConstraints
                    {
                        videoEnabled = true,
                        audioEnabled = false
                    });

                // Move the new video track from new stream to old stream.
                var newVideoTrack = newStream.GetVideoTracks().First();
                newVideoTrack.Enabled = videoTrackEnabled;
                Context.LocalStream.AddTrack(newVideoTrack);

                newStream.RemoveTrack(newVideoTrack);
                newStream.Stop();

                var source = RtcManager.Instance.Media.CreateMediaSource(newVideoTrack, CallContext.LocalMediaStreamId);
                Context.LocalVideoRenderer.SetupRenderer(Context.ForegroundProcessId, source, Context.LocalVideoControlSize);
                break;
            }
            }

            var sdpOffer = await Context.PeerConnection.CreateOffer();

            var sdpString = sdpOffer.Sdp;

            Org.WebRtc.CodecInfo videoCodecToUse = null;
            // In case of camera switch, try to use the codec from the call's first SDP negotiation.
            if (_reason == Reason.SwitchCamera && Context.VideoCodecUsed != null)
            {
                videoCodecToUse = Context.VideoCodecUsed;
            }
            else
            {
                videoCodecToUse = (await Hub.Instance.MediaSettingsChannel.GetVideoCodecAsync()).FromDto();
            }

            SdpUtils.SelectCodecs(ref sdpString,
                                  (await Hub.Instance.MediaSettingsChannel.GetAudioCodecAsync()).FromDto(),
                                  videoCodecToUse);

            if (_reason == Reason.EstablishCall)
            {
                Context.VideoCodecUsed = videoCodecToUse;
            }

            sdpOffer.Sdp = sdpString;
            await Context.PeerConnection.SetLocalDescription(sdpOffer);

            Context.SendToPeer(RelayMessageTags.SdpOffer, sdpOffer.Sdp);
        }
        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());
            }
        }