PeerConnection.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Unity.WebRTC;
  5. using UnityEngine;
  6. using UnityEngine.Assertions;
  7. namespace Unity.RenderStreaming
  8. {
  9. internal class PeerConnection : IDisposable
  10. {
  11. public delegate void OnConnectEvent();
  12. public delegate void OnDisconnectEvent();
  13. public delegate void OnDataChannelEvent(RTCDataChannel channel);
  14. public delegate void OnTrackEvent(RTCTrackEvent trackEvent);
  15. public delegate void SendOfferEvent(RTCSessionDescription description);
  16. public delegate void SendAnswerEvent(RTCSessionDescription description);
  17. public delegate void SendCandidateEvent(RTCIceCandidate candidate);
  18. public OnConnectEvent OnConnectHandler;
  19. public OnDisconnectEvent OnDisconnectHandler;
  20. public OnDataChannelEvent OnDataChannelHandler;
  21. public OnTrackEvent OnTrackEventHandler;
  22. public SendOfferEvent SendOfferHandler;
  23. public SendAnswerEvent SendAnswerHandler;
  24. public SendCandidateEvent SendCandidateHandler;
  25. public RTCPeerConnection peer => _peer;
  26. /// <summary>
  27. ///
  28. /// </summary>
  29. public bool waitingAnswer
  30. {
  31. get => _waitingAnswer;
  32. private set
  33. {
  34. _waitingAnswer = value;
  35. _timeSinceStartWaitingAnswer =
  36. _waitingAnswer ? Time.realtimeSinceStartup : 0;
  37. }
  38. }
  39. private readonly RTCPeerConnection _peer;
  40. private readonly bool _polite;
  41. private readonly Func<IEnumerator, Coroutine> _startCoroutine;
  42. private readonly Action<Coroutine> _stopCoroutine;
  43. private readonly HashSet<WeakReference<Coroutine>> _processingCoroutineList = new HashSet<WeakReference<Coroutine>>();
  44. // resend offer
  45. private readonly float _resendInterval;
  46. private bool _waitingAnswer;
  47. private float _timeSinceStartWaitingAnswer;
  48. // processing set description
  49. private bool _processingSetDescription;
  50. // processing got description
  51. private bool _ignoreOffer;
  52. private bool _srdAnswerPending;
  53. private bool _disposed = false;
  54. public PeerConnection(bool polite, RTCConfiguration config, float resendInterval, Func<IEnumerator, Coroutine> startCoroutine, Action<Coroutine> stopCoroutine)
  55. {
  56. _polite = polite;
  57. _resendInterval = resendInterval;
  58. _startCoroutine = startCoroutine;
  59. _stopCoroutine = stopCoroutine;
  60. _peer = new RTCPeerConnection(ref config);
  61. _peer.OnDataChannel = channel => OnDataChannelHandler?.Invoke(channel);
  62. _peer.OnIceCandidate = candidate => SendCandidateHandler?.Invoke(candidate);
  63. _peer.OnTrack = trackEvent => OnTrackEventHandler?.Invoke(trackEvent);
  64. _peer.OnConnectionStateChange = state =>
  65. {
  66. switch (state)
  67. {
  68. case RTCPeerConnectionState.Connected:
  69. OnConnectHandler?.Invoke();
  70. break;
  71. case RTCPeerConnectionState.Disconnected:
  72. OnDisconnectHandler?.Invoke();
  73. break;
  74. }
  75. };
  76. _peer.OnNegotiationNeeded = () => StartCoroutine(OnNegotiationNeeded());
  77. }
  78. private void StartCoroutine(IEnumerator enumerator)
  79. {
  80. var co = _startCoroutine(enumerator);
  81. _processingCoroutineList.RemoveWhere(x => !x.TryGetTarget(out _));
  82. _processingCoroutineList.Add(new WeakReference<Coroutine>(co));
  83. }
  84. ~PeerConnection()
  85. {
  86. Dispose();
  87. }
  88. public override string ToString()
  89. {
  90. var str = _polite ? "polite" : "impolite";
  91. return
  92. $"[{str}-{nameof(PeerConnection)} {nameof(_peer.ConnectionState)}:{_peer.ConnectionState} {nameof(_peer.IceConnectionState)}:{_peer.IceConnectionState} {nameof(_peer.SignalingState)}:{_peer.SignalingState} {nameof(_peer.GatheringState)}:{_peer.GatheringState}]";
  93. }
  94. public void Dispose()
  95. {
  96. if (_disposed)
  97. return;
  98. foreach (var weakCo in _processingCoroutineList)
  99. {
  100. if (weakCo.TryGetTarget(out var co))
  101. {
  102. _stopCoroutine?.Invoke(co);
  103. }
  104. }
  105. _processingCoroutineList.Clear();
  106. if (_peer != null)
  107. {
  108. _peer.OnTrack = null;
  109. _peer.OnDataChannel = null;
  110. _peer.OnIceCandidate = null;
  111. _peer.OnNegotiationNeeded = null;
  112. _peer.OnConnectionStateChange = null;
  113. _peer.OnIceConnectionChange = null;
  114. _peer.OnIceGatheringStateChange = null;
  115. _peer.Dispose();
  116. }
  117. _disposed = true;
  118. GC.SuppressFinalize(this);
  119. }
  120. private IEnumerator OnNegotiationNeeded()
  121. {
  122. var waitProcessSetDescription = new WaitWhile(() => _processingSetDescription);
  123. yield return waitProcessSetDescription;
  124. SendOffer();
  125. }
  126. public bool IsConnected()
  127. {
  128. return _peer.ConnectionState == RTCPeerConnectionState.Connected;
  129. }
  130. public bool IsStable()
  131. {
  132. return _peer.SignalingState == RTCSignalingState.Stable ||
  133. (_peer.SignalingState == RTCSignalingState.HaveLocalOffer && _srdAnswerPending);
  134. }
  135. public void SendOffer()
  136. {
  137. if (_processingSetDescription)
  138. {
  139. Debug.LogWarning($"{this} already processing other set description");
  140. return;
  141. }
  142. if (!IsStable())
  143. {
  144. if (!_waitingAnswer)
  145. {
  146. throw new InvalidOperationException(
  147. $"{this} sendoffer needs in stable state, current state is {_peer.SignalingState}");
  148. }
  149. var timeout = _timeSinceStartWaitingAnswer + _resendInterval;
  150. if (timeout < Time.realtimeSinceStartup)
  151. {
  152. SendOfferHandler?.Invoke(_peer.LocalDescription);
  153. _timeSinceStartWaitingAnswer = Time.realtimeSinceStartup;
  154. }
  155. return;
  156. }
  157. StartCoroutine(SendOfferCoroutine());
  158. }
  159. private IEnumerator SendOfferCoroutine()
  160. {
  161. Assert.AreEqual(_peer.SignalingState, RTCSignalingState.Stable);
  162. Assert.AreEqual(_processingSetDescription, false);
  163. Assert.AreEqual(waitingAnswer, false);
  164. _processingSetDescription = true;
  165. var opLocalDesc = _peer.SetLocalDescription();
  166. yield return opLocalDesc;
  167. if (opLocalDesc.IsError)
  168. {
  169. Debug.LogError($"{this} {opLocalDesc.Error.message}");
  170. _processingSetDescription = false;
  171. yield break;
  172. }
  173. if (_peer.SignalingState != RTCSignalingState.HaveLocalOffer)
  174. {
  175. _processingSetDescription = false;
  176. yield break;
  177. }
  178. Assert.AreEqual(_peer.LocalDescription.type, RTCSdpType.Offer);
  179. Assert.AreEqual(_peer.SignalingState, RTCSignalingState.HaveLocalOffer);
  180. _processingSetDescription = false;
  181. waitingAnswer = true;
  182. SendOfferHandler?.Invoke(_peer.LocalDescription);
  183. }
  184. public void SendAnswer()
  185. {
  186. if (_processingSetDescription)
  187. {
  188. Debug.LogWarning($"{this} already processing other set description");
  189. return;
  190. }
  191. StartCoroutine(SendAnswerCoroutine());
  192. }
  193. private IEnumerator SendAnswerCoroutine()
  194. {
  195. Assert.AreEqual(_peer.SignalingState, RTCSignalingState.HaveRemoteOffer);
  196. Assert.AreEqual(_processingSetDescription, false);
  197. _processingSetDescription = true;
  198. var opLocalDesc = _peer.SetLocalDescription();
  199. yield return opLocalDesc;
  200. if (opLocalDesc.IsError)
  201. {
  202. Debug.LogError($"{this} {opLocalDesc.Error.message}");
  203. _processingSetDescription = false;
  204. yield break;
  205. }
  206. Assert.AreEqual(_peer.LocalDescription.type, RTCSdpType.Answer);
  207. Assert.AreEqual(_peer.SignalingState, RTCSignalingState.Stable);
  208. _processingSetDescription = false;
  209. SendAnswerHandler?.Invoke(_peer.LocalDescription);
  210. }
  211. public IEnumerator OnGotDescription(RTCSessionDescription description, Action onComplete)
  212. {
  213. var waitOtherProcess = new WaitWhile(() => _processingSetDescription);
  214. yield return waitOtherProcess;
  215. _ignoreOffer = description.type == RTCSdpType.Offer && !_polite && (_processingSetDescription || !IsStable());
  216. if (_ignoreOffer)
  217. {
  218. Debug.LogWarning($"{this} glare - ignoreOffer.");
  219. yield break;
  220. }
  221. waitingAnswer = false;
  222. _srdAnswerPending = description.type == RTCSdpType.Answer;
  223. _processingSetDescription = true;
  224. var remoteDescOp = _peer.SetRemoteDescription(ref description);
  225. yield return remoteDescOp;
  226. if (remoteDescOp.IsError)
  227. {
  228. Debug.LogError($"{this} {remoteDescOp.Error.message}");
  229. _srdAnswerPending = false;
  230. _processingSetDescription = false;
  231. yield break;
  232. }
  233. _srdAnswerPending = false;
  234. _processingSetDescription = false;
  235. onComplete?.Invoke();
  236. }
  237. public bool OnGotIceCandidate(RTCIceCandidate candidate)
  238. {
  239. if (!_peer.AddIceCandidate(candidate))
  240. {
  241. if (!_ignoreOffer)
  242. Debug.LogWarning($"{this} this candidate can't accept on state.");
  243. return false;
  244. }
  245. return true;
  246. }
  247. }
  248. }