public void Connect(
            IObservable <DataMessage> outgoingMessages,
            IObservable <SessionDescription> receivedSessionDescriptions,
            IObservable <IceCandidate> receivedIceCandidates)
        {
            if (!_canConnect)
            {
                throw new Exception($"{GetType().Name}.{nameof(Connect)} can only be called once!");
            }

            _canConnect = false;

            LocalDataChannelReady += (pc, label) =>
            {
                DebugLog($"{Name} is ready to send data on channel '{label}'");
                _disposables.Add(outgoingMessages.Where(data => data.Label == label).Subscribe(SendData));
            };

            DataAvailable += (pc, msg) =>
            {
                DebugLog($"{Name} received data: {msg}");
                _receivedDataStream.TryOnNext(msg);
            };

            LocalSdpReadyToSend += (pc, sd) =>
            {
                DebugLog($"{Name} received local session description: {sd}");
                _localSessionDescriptionStream.TryOnNext(sd);
            };

            IceCandidateReadyToSend += (pc, ice) =>
            {
                DebugLog($"{Name} received local ice candidate: {ice}");
                _localIceCandidateStream.TryOnNext(ice);
            };

            RemoteTrackChanged += (pc, transceiverMid, mediaKind, changeKind) =>
            {
                DebugLog($"{Name} transceiver {transceiverMid} {mediaKind} track {changeKind}");
                _remoteTrackChangeStream.TryOnNext(new RemoteTrackChange(transceiverMid, mediaKind, changeKind));
            };

            RemoteVideoFrameReceived += (pc, frame) =>
                                        _receivedVideoStream.TryOnNext(frame);

            LocalVideoFrameProcessed += (pc, trackId, pixels, isEncoded) =>
                                        _localVideoFrameProcessedStream.TryOnNext(new VideoFrameMessage(trackId, pixels, isEncoded));

            SignalingStateChanged += (pc, state) =>
            {
                DebugLog($"{Name} signaling state changed: {state}");
                _signalingStateStream.TryOnNext(state);

                if (state == SignalingState.HaveRemoteOffer)
                {
                    CreateAnswer();
                }
            };

            ConnectionStateChanged += (pc, state) =>
            {
                DebugLog($"{Name} connection state changed: {state}");
                _connectionStateStream.TryOnNext(state);
            };

            _disposables.Add(receivedIceCandidates.Subscribe(ice =>
            {
                DebugLog($"{Name} received remote ICE candidate: {ice}");
                AddIceCandidate(ice);
            }));

            _disposables.Add(receivedSessionDescriptions.Subscribe(sd =>
            {
                DebugLog($"{Name} received remote session description: {sd}");
                SetRemoteDescription(sd);
            }));
        }