MultiAudioReceiveSample.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using UnityEngine;
  6. using UnityEngine.UI;
  7. using Random = UnityEngine.Random;
  8. namespace Unity.WebRTC.Samples
  9. {
  10. class MultiAudioReceiveSample : MonoBehaviour
  11. {
  12. #pragma warning disable 0649
  13. [SerializeField] private Dropdown dropdownAudioclip;
  14. [SerializeField] private Button callButton;
  15. [SerializeField] private Button hangUpButton;
  16. [SerializeField] private Button addAudioObjectButton;
  17. [SerializeField] private Button addTracksButton;
  18. [SerializeField] private Transform sourceObjectParent;
  19. [SerializeField] private Transform receiveObjectParent;
  20. [SerializeField] private List<AudioSource> sourceObjectList;
  21. [SerializeField] private List<AudioSource> receiveObjectList;
  22. [SerializeField] private AudioSource audioObjectPrefab;
  23. [SerializeField] private List<AudioClip> sourceAudioClips;
  24. #pragma warning restore 0649
  25. private RTCPeerConnection _pc1, _pc2;
  26. private List<AudioStreamTrack> audioStreamTrackList;
  27. private List<RTCRtpSender> sendingSenderList;
  28. private DelegateOnIceCandidate pc1OnIceCandidate;
  29. private DelegateOnIceCandidate pc2OnIceCandidate;
  30. private DelegateOnTrack pc2Ontrack;
  31. private DelegateOnNegotiationNeeded pc1OnNegotiationNeeded;
  32. private DelegateOnNegotiationNeeded pc2OnNegotiationNeeded;
  33. private int objectIndex = 0;
  34. private int audioIndex = 0;
  35. private void Awake()
  36. {
  37. WebRTC.Initialize(WebRTCSettings.LimitTextureSize);
  38. StartCoroutine(WebRTC.Update());
  39. callButton.onClick.AddListener(Call);
  40. hangUpButton.onClick.AddListener(HangUp);
  41. addAudioObjectButton.onClick.AddListener(AddVideoObject);
  42. addTracksButton.onClick.AddListener(AddTracks);
  43. }
  44. private void OnDestroy()
  45. {
  46. WebRTC.Dispose();
  47. }
  48. private void Start()
  49. {
  50. audioStreamTrackList = new List<AudioStreamTrack>();
  51. sendingSenderList = new List<RTCRtpSender>();
  52. callButton.interactable = true;
  53. hangUpButton.interactable = false;
  54. dropdownAudioclip.options = sourceAudioClips.Select(clip => new Dropdown.OptionData(clip.name)).ToList();
  55. pc1OnIceCandidate = candidate => { OnIceCandidate(_pc1, candidate); };
  56. pc2OnIceCandidate = candidate => { OnIceCandidate(_pc2, candidate); };
  57. pc2Ontrack = e =>
  58. {
  59. if (e.Track is AudioStreamTrack track)
  60. {
  61. var outputAudioSource = receiveObjectList[audioIndex];
  62. outputAudioSource.SetTrack(track);
  63. outputAudioSource.loop = true;
  64. outputAudioSource.Play();
  65. audioIndex++;
  66. }
  67. };
  68. pc1OnNegotiationNeeded = () => { StartCoroutine(PeerNegotiationNeeded(_pc1)); };
  69. pc2OnNegotiationNeeded = () => { StartCoroutine(PeerNegotiationNeeded(_pc2)); };
  70. }
  71. private static RTCConfiguration GetSelectedSdpSemantics()
  72. {
  73. RTCConfiguration config = default;
  74. config.iceServers = new[] {new RTCIceServer {urls = new[] {"stun:stun.l.google.com:19302"}}};
  75. return config;
  76. }
  77. IEnumerator PeerNegotiationNeeded(RTCPeerConnection pc)
  78. {
  79. Debug.Log($"{GetName(pc)} createOffer start");
  80. var op = pc.CreateOffer();
  81. yield return op;
  82. if (!op.IsError)
  83. {
  84. if (pc.SignalingState != RTCSignalingState.Stable)
  85. {
  86. Debug.LogError($"{GetName(pc)} signaling state is not stable.");
  87. yield break;
  88. }
  89. yield return StartCoroutine(OnCreateOfferSuccess(pc, op.Desc));
  90. }
  91. else
  92. {
  93. OnCreateSessionDescriptionError(op.Error);
  94. }
  95. }
  96. private void AddVideoObject()
  97. {
  98. var newSource = Instantiate(audioObjectPrefab, sourceObjectParent, false);
  99. newSource.name = $"SourceAudioObject{objectIndex}";
  100. newSource.loop = true;
  101. newSource.clip = sourceAudioClips[dropdownAudioclip.value];
  102. newSource.Play();
  103. sourceObjectList.Add(newSource);
  104. var newReceive = Instantiate(audioObjectPrefab, receiveObjectParent, false);
  105. newReceive.name = $"ReceiveAudioObject{objectIndex}";
  106. receiveObjectList.Add(newReceive);
  107. try
  108. {
  109. audioStreamTrackList.Add(new AudioStreamTrack(newSource));
  110. }
  111. catch (Exception e)
  112. {
  113. Debug.LogError(e.Message);
  114. HangUp();
  115. return;
  116. }
  117. objectIndex++;
  118. addTracksButton.interactable = true;
  119. }
  120. private void Call()
  121. {
  122. callButton.interactable = false;
  123. hangUpButton.interactable = true;
  124. addAudioObjectButton.interactable = true;
  125. addTracksButton.interactable = false;
  126. Debug.Log("GetSelectedSdpSemantics");
  127. var configuration = GetSelectedSdpSemantics();
  128. _pc1 = new RTCPeerConnection(ref configuration);
  129. Debug.Log("Created local peer connection object pc1");
  130. _pc1.OnIceCandidate = pc1OnIceCandidate;
  131. _pc1.OnNegotiationNeeded = pc1OnNegotiationNeeded;
  132. _pc2 = new RTCPeerConnection(ref configuration);
  133. Debug.Log("Created remote peer connection object pc2");
  134. _pc2.OnIceCandidate = pc2OnIceCandidate;
  135. _pc2.OnTrack = pc2Ontrack;
  136. _pc2.OnNegotiationNeeded = pc2OnNegotiationNeeded;
  137. }
  138. private void AddTracks()
  139. {
  140. Debug.Log("Add not added tracks");
  141. foreach (var track in audioStreamTrackList.Where(x =>
  142. !sendingSenderList.Exists(y => y.Track.Id == x.Id)))
  143. {
  144. var sender = _pc1.AddTrack(track);
  145. sendingSenderList.Add(sender);
  146. }
  147. }
  148. private void HangUp()
  149. {
  150. foreach (var audioSource in sourceObjectList.Concat(receiveObjectList))
  151. {
  152. DestroyImmediate(audioSource.gameObject);
  153. }
  154. sourceObjectList.Clear();
  155. receiveObjectList.Clear();
  156. foreach (var track in audioStreamTrackList)
  157. {
  158. track.Dispose();
  159. }
  160. audioStreamTrackList.Clear();
  161. sendingSenderList.Clear();
  162. _pc1.Close();
  163. _pc2.Close();
  164. Debug.Log("Close local/remote peer connection");
  165. _pc1.Dispose();
  166. _pc2.Dispose();
  167. _pc1 = null;
  168. _pc2 = null;
  169. audioIndex = 0;
  170. objectIndex = 0;
  171. callButton.interactable = true;
  172. hangUpButton.interactable = false;
  173. addAudioObjectButton.interactable = false;
  174. addTracksButton.interactable = false;
  175. }
  176. private void OnIceCandidate(RTCPeerConnection pc, RTCIceCandidate candidate)
  177. {
  178. GetOtherPc(pc).AddIceCandidate(candidate);
  179. Debug.Log($"{GetName(pc)} ICE candidate:\n {candidate.Candidate}");
  180. }
  181. private string GetName(RTCPeerConnection pc)
  182. {
  183. return (pc == _pc1) ? "pc1" : "pc2";
  184. }
  185. private RTCPeerConnection GetOtherPc(RTCPeerConnection pc)
  186. {
  187. return (pc == _pc1) ? _pc2 : _pc1;
  188. }
  189. private IEnumerator OnCreateOfferSuccess(RTCPeerConnection pc, RTCSessionDescription desc)
  190. {
  191. Debug.Log($"Offer from {GetName(pc)}\n{desc.sdp}");
  192. Debug.Log($"{GetName(pc)} setLocalDescription start");
  193. var op = pc.SetLocalDescription(ref desc);
  194. yield return op;
  195. if (!op.IsError)
  196. {
  197. OnSetLocalSuccess(pc);
  198. }
  199. else
  200. {
  201. var error = op.Error;
  202. OnSetSessionDescriptionError(ref error);
  203. }
  204. var otherPc = GetOtherPc(pc);
  205. Debug.Log($"{GetName(otherPc)} setRemoteDescription start");
  206. var op2 = otherPc.SetRemoteDescription(ref desc);
  207. yield return op2;
  208. if (!op2.IsError)
  209. {
  210. OnSetRemoteSuccess(otherPc);
  211. }
  212. else
  213. {
  214. var error = op2.Error;
  215. OnSetSessionDescriptionError(ref error);
  216. }
  217. Debug.Log($"{GetName(otherPc)} createAnswer start");
  218. var op3 = otherPc.CreateAnswer();
  219. yield return op3;
  220. if (!op3.IsError)
  221. {
  222. yield return OnCreateAnswerSuccess(otherPc, op3.Desc);
  223. }
  224. else
  225. {
  226. OnCreateSessionDescriptionError(op3.Error);
  227. }
  228. }
  229. private void OnSetLocalSuccess(RTCPeerConnection pc)
  230. {
  231. Debug.Log($"{GetName(pc)} SetLocalDescription complete");
  232. }
  233. static void OnSetSessionDescriptionError(ref RTCError error)
  234. {
  235. Debug.LogError($"Error Detail Type: {error.message}");
  236. }
  237. private void OnSetRemoteSuccess(RTCPeerConnection pc)
  238. {
  239. Debug.Log($"{GetName(pc)} SetRemoteDescription complete");
  240. }
  241. IEnumerator OnCreateAnswerSuccess(RTCPeerConnection pc, RTCSessionDescription desc)
  242. {
  243. Debug.Log($"Answer from {GetName(pc)}:\n{desc.sdp}");
  244. Debug.Log($"{GetName(pc)} setLocalDescription start");
  245. var op = pc.SetLocalDescription(ref desc);
  246. yield return op;
  247. if (!op.IsError)
  248. {
  249. OnSetLocalSuccess(pc);
  250. }
  251. else
  252. {
  253. var error = op.Error;
  254. OnSetSessionDescriptionError(ref error);
  255. }
  256. var otherPc = GetOtherPc(pc);
  257. Debug.Log($"{GetName(otherPc)} setRemoteDescription start");
  258. var op2 = otherPc.SetRemoteDescription(ref desc);
  259. yield return op2;
  260. if (!op2.IsError)
  261. {
  262. OnSetRemoteSuccess(otherPc);
  263. }
  264. else
  265. {
  266. var error = op2.Error;
  267. OnSetSessionDescriptionError(ref error);
  268. }
  269. }
  270. private static void OnCreateSessionDescriptionError(RTCError error)
  271. {
  272. Debug.LogError($"Error Detail Type: {error.message}");
  273. }
  274. }
  275. }