Beispiel #1
0
    public async Task SetupAsync(Signaler signaler, bool isServer, uint clientNodeId = 0)
    {
        await pc.InitializeAsync(new PeerConnectionConfiguration {
            IceServers = new List <IceServer> {
                new IceServer {
                    Urls = { signaler.IceServerUrl }
                }
            }
        });

        var tcs = new TaskCompletionSource <bool>();

        // Do signaling
        //  https://microsoft.github.io/MixedReality-WebRTC/manual/cs/cs-signaling.html
        //  https://microsoft.github.io/MixedReality-WebRTC/manual/cs/helloworld-cs-signaling-core3.html

        pc.LocalSdpReadytoSend += (SdpMessage sdpMessage) => {
            // ここはawaitではなくWaitにしないとSocketが切れる.スレッドセーフ関係?
            signaler.SendSdpAsync(sdpMessage.Type == SdpMessageType.Offer, sdpMessage.Content, clientNodeId).Wait();
        };
        pc.IceCandidateReadytoSend += (IceCandidate candidate) => {
            signaler.SendIceAsync(candidate.SdpMid, candidate.SdpMlineIndex, candidate.Content, clientNodeId).Wait();
        };

        pc.IceStateChanged += (IceConnectionState state) => {
            Logger.Debug("Connection", $"ICE state changed to {state}");
            // https://microsoft.github.io/MixedReality-WebRTC/versions/release/2.0/api/Microsoft.MixedReality.WebRTC.IceConnectionState.html
            if (state == IceConnectionState.Connected)
            {
                Connected = true;
            }
            if (state == IceConnectionState.Closed || state == IceConnectionState.Disconnected || state == IceConnectionState.Failed)
            {
                Connected = false;
                OnDisconnect();
            }

            if (!isServer && state == IceConnectionState.Failed)
            {
                tcs.SetException(new ConnectionException("Failed to establish a WebRTC connection"));
            }
        };

        signaler.SdpReceived += async(bool isOffer, string sdp, uint cid) => {
            if (isServer && cid != clientNodeId)
            {
                // ignore messages for other clients
                return;
            }

            await pc.SetRemoteDescriptionAsync(new SdpMessage {
                Type    = isOffer ? SdpMessageType.Offer : SdpMessageType.Answer,
                Content = sdp
            });

            if (isOffer)
            {
                pc.CreateAnswer();
            }
        };
        signaler.IceReceived += (string sdpMid, int sdpMLineIndex, string candidate, uint cid) => {
            if (isServer && cid != clientNodeId)
            {
                // ignore messages for other clients
                return;
            }

            pc.AddIceCandidate(new IceCandidate {
                SdpMid        = sdpMid,
                SdpMlineIndex = sdpMLineIndex,
                Content       = candidate
            });
            //Logger.Write((isServer ? "Server: " : "Client: ") + $"{sdpMid} {sdpMLineIndex} {candidate}");
        };

        if (isServer)
        {
            TaskCompletionSource <DataChannel>[] completionSources = channelTypes.Select(
                _ => new TaskCompletionSource <DataChannel>()
                ).ToArray();

            pc.DataChannelAdded += (dc) => {
                foreach (var type in channelTypes)
                {
                    if (dc.Label == channelLabels[(int)type])
                    {
                        completionSources[(int)type].SetResult(dc);
                    }
                }
            };

            Logger.Debug("Connection", "Server is ready for signaling");
            await signaler.NotifyReadyAsync(clientNodeId);

            Logger.Debug("Connection", "Server: Waiting for DC");
            foreach (var type in channelTypes)
            {
                channels[(int)type] = await completionSources[(int)type].Task;
            }
        }
        else
        {
            // Define channels
            // Sync channel (unreliable)
            channels[(int)ChannelType.Sync] = await pc.AddDataChannelAsync(
                channelLabels[(int)ChannelType.Sync], ordered : false, reliable : false);

            // Message channel (reliable but order is not guaranteed)
            channels[(int)ChannelType.Control] = await pc.AddDataChannelAsync(
                channelLabels[(int)ChannelType.Control], ordered : false, reliable : true);

            // Blob channel (reliable and ordered)
            channels[(int)ChannelType.Blob] = await pc.AddDataChannelAsync(
                channelLabels[(int)ChannelType.Blob], ordered : true, reliable : true);

            // Audio channel (unreliable)
            channels[(int)ChannelType.Audio] = await pc.AddDataChannelAsync(
                channelLabels[(int)ChannelType.Audio], ordered : false, reliable : false);

            Logger.Debug("Connection", "Client: Waiting for server ready");
            await signaler.WaitReadyAsync();

            pc.CreateOffer();
        }

        foreach (var(dc, idx) in channels.Select((dc, idx) => (dc, idx)))
        {
            dc.MessageReceived += (data) => {
                threadChannels[idx].Writer.TryWrite(data);  // Always succeeds because the Channel is unbounded
            };
            dc.StateChanged += () => {
                Logger.Debug("Connection", $"DC {(ChannelType)idx} state changed to {dc.State}");
                if (dc.State == DataChannel.ChannelState.Closing)
                {
                    // Disconnect handling
                    Connected = false;
                }
            };
        }

        //await Task.Delay(5000);

        if (!isServer)
        {
            // FIXME: Waiting pc.Connected not work in server (cannot establish a connection to client)
            //        In server, should wait until all DataChannels are added?
            pc.Connected += () => {
                tcs.SetResult(true);
            };
            await tcs.Task;
        }
    }