StatsSample.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. using Unity.WebRTC;
  6. using System.Linq;
  7. using Unity.WebRTC.Samples;
  8. class StatsSample : MonoBehaviour
  9. {
  10. #pragma warning disable 0649
  11. [SerializeField] private Button callButton;
  12. [SerializeField] private Button hangupButton;
  13. [SerializeField] private InputField text;
  14. [SerializeField] private Dropdown dropdown;
  15. [SerializeField] private Dropdown dropdownFreq;
  16. [SerializeField] private AudioSource source;
  17. [SerializeField] private Camera cam;
  18. #pragma warning restore 0649
  19. private RTCPeerConnection pc1, pc2;
  20. private RTCDataChannel dataChannel, remoteDataChannel;
  21. private DelegateOnIceConnectionChange pc1OnIceConnectionChange = null;
  22. private DelegateOnIceConnectionChange pc2OnIceConnectionChange = null;
  23. private DelegateOnIceCandidate pc1OnIceCandidate = null;
  24. private DelegateOnIceCandidate pc2OnIceCandidate = null;
  25. private DelegateOnMessage onDataChannelMessage = null;
  26. private DelegateOnOpen onDataChannelOpen = null;
  27. private DelegateOnClose onDataChannelClose = null;
  28. private DelegateOnDataChannel onDataChannel = null;
  29. private int currentValue = -1;
  30. private WaitForSeconds wait;
  31. private Dictionary<string, float> dictFrequency = new Dictionary<string, float>()
  32. {
  33. { "60ms", 0.06f},
  34. { "120ms", 0.12f},
  35. { "300ms", 0.3f},
  36. { "1000ms", 1f},
  37. };
  38. private void Awake()
  39. {
  40. WebRTC.Initialize(WebRTCSettings.LimitTextureSize);
  41. callButton.onClick.AddListener(() =>
  42. {
  43. callButton.interactable = false;
  44. hangupButton.interactable = true;
  45. StartCoroutine(Call());
  46. });
  47. hangupButton.onClick.AddListener(() =>
  48. {
  49. callButton.interactable = true;
  50. hangupButton.interactable = false;
  51. Dispose();
  52. });
  53. }
  54. private void OnDestroy()
  55. {
  56. WebRTC.Dispose();
  57. }
  58. private void Start()
  59. {
  60. dropdown.interactable = false;
  61. callButton.interactable = true;
  62. pc1OnIceConnectionChange = state => { OnIceConnectionChange(pc1, state); };
  63. pc2OnIceConnectionChange = state => { OnIceConnectionChange(pc2, state); };
  64. pc1OnIceCandidate = candidate => { OnIceCandidate(pc1, candidate); };
  65. pc2OnIceCandidate = candidate => { OnIceCandidate(pc1, candidate); };
  66. onDataChannel = channel =>
  67. {
  68. remoteDataChannel = channel;
  69. remoteDataChannel.OnMessage = onDataChannelMessage;
  70. };
  71. onDataChannelOpen = ()=> { };
  72. onDataChannelClose = () => { };
  73. dropdownFreq.options = dictFrequency.Select(pair => new Dropdown.OptionData(pair.Key)).ToList();
  74. dropdownFreq.value = 0;
  75. dropdownFreq.onValueChanged.AddListener(OnValueChangedFreq);
  76. OnValueChangedFreq(0);
  77. StartCoroutine(LoopGetStats());
  78. }
  79. void OnValueChangedFreq(int value)
  80. {
  81. wait = new WaitForSeconds(dictFrequency.ElementAt(value).Value);
  82. }
  83. RTCConfiguration GetSelectedSdpSemantics()
  84. {
  85. RTCConfiguration config = default;
  86. config.iceServers = new RTCIceServer[]
  87. {
  88. new RTCIceServer { urls = new string[] { "stun:stun.l.google.com:19302" } }
  89. };
  90. return config;
  91. }
  92. void OnIceConnectionChange(RTCPeerConnection pc, RTCIceConnectionState state)
  93. {
  94. switch (state)
  95. {
  96. case RTCIceConnectionState.New:
  97. Debug.Log($"{GetName(pc)} IceConnectionState: New");
  98. break;
  99. case RTCIceConnectionState.Checking:
  100. Debug.Log($"{GetName(pc)} IceConnectionState: Checking");
  101. break;
  102. case RTCIceConnectionState.Closed:
  103. Debug.Log($"{GetName(pc)} IceConnectionState: Closed");
  104. break;
  105. case RTCIceConnectionState.Completed:
  106. Debug.Log($"{GetName(pc)} IceConnectionState: Completed");
  107. break;
  108. case RTCIceConnectionState.Connected:
  109. Debug.Log($"{GetName(pc)} IceConnectionState: Connected");
  110. break;
  111. case RTCIceConnectionState.Disconnected:
  112. Debug.Log($"{GetName(pc)} IceConnectionState: Disconnected");
  113. break;
  114. case RTCIceConnectionState.Failed:
  115. Debug.Log($"{GetName(pc)} IceConnectionState: Failed");
  116. break;
  117. case RTCIceConnectionState.Max:
  118. Debug.Log($"{GetName(pc)} IceConnectionState: Max");
  119. break;
  120. default:
  121. break;
  122. }
  123. }
  124. IEnumerator Call()
  125. {
  126. callButton.interactable = false;
  127. Debug.Log("GetSelectedSdpSemantics");
  128. var configuration = GetSelectedSdpSemantics();
  129. pc1 = new RTCPeerConnection(ref configuration);
  130. Debug.Log("Created local peer connection object pc1");
  131. pc1.OnIceCandidate = pc1OnIceCandidate;
  132. pc1.OnIceConnectionChange = pc1OnIceConnectionChange;
  133. pc2 = new RTCPeerConnection(ref configuration);
  134. Debug.Log("Created remote peer connection object pc2");
  135. pc2.OnIceCandidate = pc2OnIceCandidate;
  136. pc2.OnIceConnectionChange = pc2OnIceConnectionChange;
  137. pc2.OnDataChannel = onDataChannel;
  138. dataChannel = pc1.CreateDataChannel("data");
  139. dataChannel.OnOpen = onDataChannelOpen;
  140. var audioTrack = new AudioStreamTrack(source);
  141. var videoTrack = cam.CaptureStreamTrack(1280, 720);
  142. yield return 0;
  143. pc1.AddTrack(audioTrack);
  144. pc1.AddTrack(videoTrack);
  145. Debug.Log("pc1 createOffer start");
  146. var op = pc1.CreateOffer();
  147. yield return op;
  148. if (!op.IsError)
  149. {
  150. yield return StartCoroutine(OnCreateOfferSuccess(op.Desc));
  151. }
  152. else
  153. {
  154. OnCreateSessionDescriptionError(op.Error);
  155. }
  156. }
  157. void Dispose()
  158. {
  159. dropdown.options = new List<Dropdown.OptionData>();
  160. dropdown.interactable = false;
  161. text.text = string.Empty;
  162. text.interactable = false;
  163. dataChannel.Dispose();
  164. pc1.Dispose();
  165. pc2.Dispose();
  166. }
  167. /// <summary>
  168. ///
  169. /// </summary>
  170. /// <param name="pc"></param>
  171. /// <param name="streamEvent"></param>
  172. void OnIceCandidate(RTCPeerConnection pc, RTCIceCandidate candidate)
  173. {
  174. GetOtherPc(pc).AddIceCandidate(candidate);
  175. Debug.Log($"{GetName(pc)} ICE candidate:\n {candidate.Candidate}");
  176. }
  177. string GetName(RTCPeerConnection pc)
  178. {
  179. return (pc == pc1) ? "pc1" : "pc2";
  180. }
  181. RTCPeerConnection GetOtherPc(RTCPeerConnection pc)
  182. {
  183. return (pc == pc1) ? pc2 : pc1;
  184. }
  185. IEnumerator OnCreateOfferSuccess(RTCSessionDescription desc)
  186. {
  187. Debug.Log($"Offer from pc1\n{desc.sdp}");
  188. Debug.Log("pc1 setLocalDescription start");
  189. var op = pc1.SetLocalDescription(ref desc);
  190. yield return op;
  191. if (!op.IsError)
  192. {
  193. OnSetLocalSuccess(pc1);
  194. }
  195. else
  196. {
  197. var error = op.Error;
  198. OnSetSessionDescriptionError(ref error);
  199. }
  200. Debug.Log("pc2 setRemoteDescription start");
  201. var op2 = pc2.SetRemoteDescription(ref desc);
  202. yield return op2;
  203. if (!op2.IsError)
  204. {
  205. OnSetRemoteSuccess(pc2);
  206. }
  207. else
  208. {
  209. var error = op2.Error;
  210. OnSetSessionDescriptionError(ref error);
  211. }
  212. Debug.Log("pc2 createAnswer start");
  213. // Since the 'remote' side has no media stream we need
  214. // to pass in the right constraints in order for it to
  215. // accept the incoming offer of audio and video.
  216. var op3 = pc2.CreateAnswer();
  217. yield return op3;
  218. if (!op3.IsError)
  219. {
  220. yield return OnCreateAnswerSuccess(op3.Desc);
  221. }
  222. else
  223. {
  224. OnCreateSessionDescriptionError(op3.Error);
  225. }
  226. }
  227. void OnSetLocalSuccess(RTCPeerConnection pc)
  228. {
  229. Debug.Log($"{GetName(pc)} SetLocalDescription complete");
  230. }
  231. void OnSetSessionDescriptionError(ref RTCError error) { }
  232. void OnSetRemoteSuccess(RTCPeerConnection pc)
  233. {
  234. Debug.Log($"{GetName(pc)} SetRemoteDescription complete");
  235. }
  236. IEnumerator OnCreateAnswerSuccess(RTCSessionDescription desc)
  237. {
  238. Debug.Log($"Answer from pc2:\n{desc.sdp}");
  239. Debug.Log("pc2 setLocalDescription start");
  240. var op = pc2.SetLocalDescription(ref desc);
  241. yield return op;
  242. if (!op.IsError)
  243. {
  244. OnSetLocalSuccess(pc2);
  245. }
  246. else
  247. {
  248. var error = op.Error;
  249. OnSetSessionDescriptionError(ref error);
  250. }
  251. Debug.Log("pc1 setRemoteDescription start");
  252. var op2 = pc1.SetRemoteDescription(ref desc);
  253. yield return op2;
  254. if (!op2.IsError)
  255. {
  256. OnSetRemoteSuccess(pc1);
  257. }
  258. else
  259. {
  260. var error = op2.Error;
  261. OnSetSessionDescriptionError(ref error);
  262. }
  263. }
  264. IEnumerator LoopGetStats()
  265. {
  266. while (true)
  267. {
  268. yield return wait;
  269. if (callButton.interactable)
  270. continue;
  271. var op1 = pc1.GetStats();
  272. var op2 = pc2.GetStats();
  273. yield return op1;
  274. yield return op2;
  275. if(op1.IsError || op2.IsError)
  276. continue;
  277. if(dropdown.options.Count == 0)
  278. {
  279. List<string> options = new List<string>();
  280. foreach (var stat in op1.Value.Stats.Keys)
  281. {
  282. options.Add($"{stat}");
  283. }
  284. dropdown.ClearOptions();
  285. dropdown.AddOptions(options);
  286. dropdown.interactable = true;
  287. }
  288. if (currentValue != dropdown.value)
  289. {
  290. currentValue = dropdown.value;
  291. }
  292. var id = dropdown.options[currentValue].text;
  293. text.text = "Id:" + op1.Value.Stats[id].Id + "\n";
  294. text.text += "Timestamp:" + op1.Value.Stats[id].Timestamp + "\n";
  295. text.interactable = true;
  296. if (op1.Value.TryGetValue(id, out RTCStats stats))
  297. text.text += stats.Dict.Aggregate(string.Empty,(str, next) =>
  298. str + next.Key + ":" + (next.Value == null ? string.Empty : next.Value.ToString()) + "\n");
  299. op1.Value.Dispose();
  300. op2.Value.Dispose();
  301. }
  302. }
  303. void OnAddIceCandidateSuccess(RTCPeerConnection pc)
  304. {
  305. Debug.Log($"{GetName(pc)} addIceCandidate success");
  306. }
  307. void OnAddIceCandidateError(RTCPeerConnection pc, RTCError error)
  308. {
  309. Debug.Log($"{GetName(pc)} failed to add ICE Candidate: ${error}");
  310. }
  311. void OnCreateSessionDescriptionError(RTCError e)
  312. {
  313. }
  314. }