using System.Collections; using UnityEngine; using UnityEngine.UI; using Unity.WebRTC; using System; using Unity.WebRTC.Samples; class DataChannelSample : MonoBehaviour { #pragma warning disable 0649 [SerializeField] private Button callButton; [SerializeField] private Button hangupButton; [SerializeField] private Button sendButton; [SerializeField] private InputField textSend; [SerializeField] private InputField textReceive; #pragma warning restore 0649 private RTCPeerConnection pc1, pc2; private RTCDataChannel dataChannel, remoteDataChannel; private DelegateOnIceConnectionChange pc1OnIceConnectionChange; private DelegateOnIceConnectionChange pc2OnIceConnectionChange; private DelegateOnIceCandidate pc1OnIceCandidate; private DelegateOnIceCandidate pc2OnIceCandidate; private DelegateOnMessage onDataChannelMessage; private DelegateOnOpen onDataChannelOpen; private DelegateOnClose onDataChannelClose; private DelegateOnDataChannel onDataChannel; private void Awake() { WebRTC.Initialize(WebRTCSettings.LimitTextureSize); callButton.onClick.AddListener(() => { StartCoroutine(Call()); }); hangupButton.onClick.AddListener(() => { Hangup(); }); } private void OnDestroy() { WebRTC.Dispose(); } private void Start() { callButton.interactable = true; hangupButton.interactable = false; pc1OnIceConnectionChange = state => { OnIceConnectionChange(pc1, state); }; pc2OnIceConnectionChange = state => { OnIceConnectionChange(pc2, state); }; pc1OnIceCandidate = candidate => { OnIceCandidate(pc1, candidate); }; pc2OnIceCandidate = candidate => { OnIceCandidate(pc2, candidate); }; onDataChannel = channel => { remoteDataChannel = channel; remoteDataChannel.OnMessage = onDataChannelMessage; }; onDataChannelMessage = bytes => { textReceive.text = System.Text.Encoding.UTF8.GetString(bytes); }; onDataChannelOpen = () => { sendButton.interactable = true; hangupButton.interactable = true; }; onDataChannelClose = () => { sendButton.interactable = false; hangupButton.interactable = false; }; } RTCConfiguration GetSelectedSdpSemantics() { RTCConfiguration config = default; config.iceServers = new RTCIceServer[] { new RTCIceServer { urls = new string[] { "stun:stun.l.google.com:19302" } } }; return config; } void OnIceConnectionChange(RTCPeerConnection pc, RTCIceConnectionState state) { switch (state) { case RTCIceConnectionState.New: Debug.Log($"{GetName(pc)} IceConnectionState: New"); break; case RTCIceConnectionState.Checking: Debug.Log($"{GetName(pc)} IceConnectionState: Checking"); break; case RTCIceConnectionState.Closed: Debug.Log($"{GetName(pc)} IceConnectionState: Closed"); break; case RTCIceConnectionState.Completed: Debug.Log($"{GetName(pc)} IceConnectionState: Completed"); break; case RTCIceConnectionState.Connected: Debug.Log($"{GetName(pc)} IceConnectionState: Connected"); break; case RTCIceConnectionState.Disconnected: Debug.Log($"{GetName(pc)} IceConnectionState: Disconnected"); break; case RTCIceConnectionState.Failed: Debug.Log($"{GetName(pc)} IceConnectionState: Failed"); break; case RTCIceConnectionState.Max: Debug.Log($"{GetName(pc)} IceConnectionState: Max"); break; default: break; } } void Pc1OnIceConnectinChange(RTCIceConnectionState state) { OnIceConnectionChange(pc1, state); } void Pc2OnIceConnectionChange(RTCIceConnectionState state) { OnIceConnectionChange(pc2, state); } void Pc1OnIceCandidate(RTCIceCandidate candidate) { OnIceCandidate(pc1, candidate); } void Pc2OnIceCandidate(RTCIceCandidate candidate) { OnIceCandidate(pc2, candidate); } IEnumerator Call() { callButton.interactable = false; Debug.Log("GetSelectedSdpSemantics"); var configuration = GetSelectedSdpSemantics(); pc1 = new RTCPeerConnection(ref configuration); Debug.Log("Created local peer connection object pc1"); pc1.OnIceCandidate = pc1OnIceCandidate; pc1.OnIceConnectionChange = pc1OnIceConnectionChange; pc2 = new RTCPeerConnection(ref configuration); Debug.Log("Created remote peer connection object pc2"); pc2.OnIceCandidate = pc2OnIceCandidate; pc2.OnIceConnectionChange = pc2OnIceConnectionChange; pc2.OnDataChannel = onDataChannel; RTCDataChannelInit conf = new RTCDataChannelInit(); dataChannel = pc1.CreateDataChannel("data", conf); dataChannel.OnOpen = onDataChannelOpen; Debug.Log("pc1 createOffer start"); var op = pc1.CreateOffer(); yield return op; if (!op.IsError) { yield return StartCoroutine(OnCreateOfferSuccess(op.Desc)); } else { OnCreateSessionDescriptionError(op.Error); } } void Hangup() { pc1.Close(); pc2.Close(); pc1 = null; pc2 = null; textSend.text = string.Empty; textReceive.text = string.Empty; hangupButton.interactable = false; sendButton.interactable = false; callButton.interactable = true; } /// /// /// /// /// void OnIceCandidate(RTCPeerConnection pc, RTCIceCandidate candidate) { GetOtherPc(pc).AddIceCandidate(candidate); Debug.Log($"{GetName(pc)} ICE candidate:\n {candidate.Candidate}"); } public void SendMsg() { dataChannel.Send(textSend.text); } string GetName(RTCPeerConnection pc) { return (pc == pc1) ? "pc1" : "pc2"; } RTCPeerConnection GetOtherPc(RTCPeerConnection pc) { return (pc == pc1) ? pc2 : pc1; } IEnumerator OnCreateOfferSuccess(RTCSessionDescription desc) { Debug.Log($"Offer from pc1\n{desc.sdp}"); Debug.Log("pc1 setLocalDescription start"); var op = pc1.SetLocalDescription(ref desc); yield return op; if (!op.IsError) { OnSetLocalSuccess(pc1); } else { var error = op.Error; OnSetSessionDescriptionError(ref error); } Debug.Log("pc2 setRemoteDescription start"); var op2 = pc2.SetRemoteDescription(ref desc); yield return op2; if (!op2.IsError) { OnSetRemoteSuccess(pc2); } else { var error = op2.Error; OnSetSessionDescriptionError(ref error); } Debug.Log("pc2 createAnswer start"); // Since the 'remote' side has no media stream we need // to pass in the right constraints in order for it to // accept the incoming offer of audio and video. var op3 = pc2.CreateAnswer(); yield return op3; if (!op3.IsError) { yield return OnCreateAnswerSuccess(op3.Desc); } else { OnCreateSessionDescriptionError(op3.Error); } } void OnSetLocalSuccess(RTCPeerConnection pc) { Debug.Log($"{GetName(pc)} SetLocalDescription complete"); } void OnSetSessionDescriptionError(ref RTCError error) { } void OnSetRemoteSuccess(RTCPeerConnection pc) { Debug.Log($"{GetName(pc)} SetRemoteDescription complete"); } IEnumerator OnCreateAnswerSuccess(RTCSessionDescription desc) { Debug.Log($"Answer from pc2:\n{desc.sdp}"); Debug.Log("pc2 setLocalDescription start"); var op = pc2.SetLocalDescription(ref desc); yield return op; if (!op.IsError) { OnSetLocalSuccess(pc2); } else { var error = op.Error; OnSetSessionDescriptionError(ref error); } Debug.Log("pc1 setRemoteDescription start"); var op2 = pc1.SetRemoteDescription(ref desc); yield return op2; if (!op2.IsError) { OnSetRemoteSuccess(pc1); } else { var error = op2.Error; OnSetSessionDescriptionError(ref error); } } IEnumerator LoopGetStats() { while (true) { yield return new WaitForSeconds(1f); if (!sendButton.interactable) continue; var op1 = pc1.GetStats(); var op2 = pc2.GetStats(); yield return op1; yield return op2; Debug.Log("pc1"); foreach (var stat in op1.Value.Stats.Values) { Debug.Log(stat.Type.ToString()); } Debug.Log("pc2"); foreach (var stat in op2.Value.Stats.Values) { Debug.Log(stat.Type.ToString()); } } } void OnAddIceCandidateSuccess(RTCPeerConnection pc) { Debug.Log($"{GetName(pc)} addIceCandidate success"); } void OnAddIceCandidateError(RTCPeerConnection pc, RTCError error) { Debug.Log($"{GetName(pc)} failed to add ICE Candidate: ${error}"); } void OnCreateSessionDescriptionError(RTCError e) { } }