MediaStreamSample.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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 System.Text;
  8. namespace Unity.WebRTC.Samples
  9. {
  10. class MediaStreamSample : MonoBehaviour
  11. {
  12. #pragma warning disable 0649
  13. [SerializeField] private Button callButton;
  14. [SerializeField] private Button addTracksButton;
  15. [SerializeField] private Button removeTracksButton;
  16. [SerializeField] private Camera cam;
  17. [SerializeField] private InputField infoText;
  18. [SerializeField] private RawImage RtImage;
  19. #pragma warning restore 0649
  20. private RTCPeerConnection _pc1, _pc2;
  21. private List<RTCRtpSender> pc1Senders;
  22. private MediaStream videoStream;
  23. private MediaStreamTrack track;
  24. private DelegateOnIceConnectionChange pc1OnIceConnectionChange;
  25. private DelegateOnIceConnectionChange pc2OnIceConnectionChange;
  26. private DelegateOnIceCandidate pc1OnIceCandidate;
  27. private DelegateOnIceCandidate pc2OnIceCandidate;
  28. private DelegateOnTrack pc2Ontrack;
  29. private DelegateOnNegotiationNeeded pc1OnNegotiationNeeded;
  30. private StringBuilder trackInfos;
  31. private bool videoUpdateStarted;
  32. private void Awake()
  33. {
  34. WebRTC.Initialize(WebRTCSettings.LimitTextureSize);
  35. callButton.onClick.AddListener(Call);
  36. addTracksButton.onClick.AddListener(AddTracks);
  37. removeTracksButton.onClick.AddListener(RemoveTracks);
  38. }
  39. private void OnDestroy()
  40. {
  41. WebRTC.Dispose();
  42. }
  43. private void Start()
  44. {
  45. trackInfos = new StringBuilder();
  46. pc1Senders = new List<RTCRtpSender>();
  47. callButton.interactable = true;
  48. pc1OnIceConnectionChange = state => { OnIceConnectionChange(_pc1, state); };
  49. pc2OnIceConnectionChange = state => { OnIceConnectionChange(_pc2, state); };
  50. pc1OnIceCandidate = candidate => { OnIceCandidate(_pc1, candidate); };
  51. pc2OnIceCandidate = candidate => { OnIceCandidate(_pc2, candidate); };
  52. pc2Ontrack = e => { OnTrack(_pc2, e); };
  53. pc1OnNegotiationNeeded = () => { StartCoroutine(PcOnNegotiationNeeded(_pc1)); };
  54. var codecName = WebRTCSettings.UseVideoCodec == null
  55. ? "Default"
  56. : $"{WebRTCSettings.UseVideoCodec.mimeType} {WebRTCSettings.UseVideoCodec.sdpFmtpLine}";
  57. infoText.text = $"Currently selected video codec is {codecName}";
  58. }
  59. private static RTCConfiguration GetSelectedSdpSemantics()
  60. {
  61. RTCConfiguration config = default;
  62. config.iceServers = new[] {new RTCIceServer {urls = new[] {"stun:stun.l.google.com:19302"}}};
  63. return config;
  64. }
  65. private void OnIceConnectionChange(RTCPeerConnection pc, RTCIceConnectionState state)
  66. {
  67. switch (state)
  68. {
  69. case RTCIceConnectionState.New:
  70. Debug.Log($"{GetName(pc)} IceConnectionState: New");
  71. break;
  72. case RTCIceConnectionState.Checking:
  73. Debug.Log($"{GetName(pc)} IceConnectionState: Checking");
  74. break;
  75. case RTCIceConnectionState.Closed:
  76. Debug.Log($"{GetName(pc)} IceConnectionState: Closed");
  77. break;
  78. case RTCIceConnectionState.Completed:
  79. Debug.Log($"{GetName(pc)} IceConnectionState: Completed");
  80. break;
  81. case RTCIceConnectionState.Connected:
  82. Debug.Log($"{GetName(pc)} IceConnectionState: Connected");
  83. break;
  84. case RTCIceConnectionState.Disconnected:
  85. Debug.Log($"{GetName(pc)} IceConnectionState: Disconnected");
  86. break;
  87. case RTCIceConnectionState.Failed:
  88. Debug.Log($"{GetName(pc)} IceConnectionState: Failed");
  89. break;
  90. case RTCIceConnectionState.Max:
  91. Debug.Log($"{GetName(pc)} IceConnectionState: Max");
  92. break;
  93. default:
  94. throw new ArgumentOutOfRangeException(nameof(state), state, null);
  95. }
  96. }
  97. IEnumerator PcOnNegotiationNeeded(RTCPeerConnection pc)
  98. {
  99. Debug.Log($"{GetName(pc)} createOffer start");
  100. var op = pc.CreateOffer();
  101. yield return op;
  102. if (!op.IsError)
  103. {
  104. yield return StartCoroutine(OnCreateOfferSuccess(pc, op.Desc));
  105. }
  106. else
  107. {
  108. OnCreateSessionDescriptionError(op.Error);
  109. }
  110. }
  111. private void AddTracks()
  112. {
  113. pc1Senders.Add(_pc1.AddTrack(track));
  114. if (WebRTCSettings.UseVideoCodec != null)
  115. {
  116. var codecs = new[] { WebRTCSettings.UseVideoCodec };
  117. foreach (var transceiver in _pc1.GetTransceivers())
  118. {
  119. if (pc1Senders.Contains(transceiver.Sender))
  120. {
  121. transceiver.SetCodecPreferences(codecs);
  122. }
  123. }
  124. }
  125. if (!videoUpdateStarted)
  126. {
  127. StartCoroutine(WebRTC.Update());
  128. videoUpdateStarted = true;
  129. }
  130. addTracksButton.interactable = false;
  131. removeTracksButton.interactable = true;
  132. }
  133. private void RemoveTracks()
  134. {
  135. foreach (var sender in pc1Senders)
  136. {
  137. _pc1.RemoveTrack(sender);
  138. }
  139. foreach(var transceiver in _pc1.GetTransceivers())
  140. {
  141. transceiver.Stop();
  142. }
  143. pc1Senders.Clear();
  144. addTracksButton.interactable = true;
  145. removeTracksButton.interactable = false;
  146. trackInfos.Clear();
  147. infoText.text = "";
  148. }
  149. private void Call()
  150. {
  151. callButton.interactable = false;
  152. Debug.Log("GetSelectedSdpSemantics");
  153. var configuration = GetSelectedSdpSemantics();
  154. _pc1 = new RTCPeerConnection(ref configuration);
  155. Debug.Log("Created local peer connection object pc1");
  156. _pc1.OnIceCandidate = pc1OnIceCandidate;
  157. _pc1.OnIceConnectionChange = pc1OnIceConnectionChange;
  158. _pc1.OnNegotiationNeeded = pc1OnNegotiationNeeded;
  159. _pc2 = new RTCPeerConnection(ref configuration);
  160. Debug.Log("Created remote peer connection object pc2");
  161. _pc2.OnIceCandidate = pc2OnIceCandidate;
  162. _pc2.OnIceConnectionChange = pc2OnIceConnectionChange;
  163. _pc2.OnTrack = pc2Ontrack;
  164. videoStream = cam.CaptureStream(WebRTCSettings.StreamSize.x, WebRTCSettings.StreamSize.y);
  165. track = videoStream.GetTracks().First();
  166. RtImage.texture = cam.targetTexture;
  167. }
  168. private void OnIceCandidate(RTCPeerConnection pc, RTCIceCandidate candidate)
  169. {
  170. GetOtherPc(pc).AddIceCandidate(candidate);
  171. Debug.Log($"{GetName(pc)} ICE candidate:\n {candidate.Candidate}");
  172. }
  173. private void OnTrack(RTCPeerConnection pc, RTCTrackEvent e)
  174. {
  175. trackInfos.Append($"{GetName(pc)} receives remote track:\r\n");
  176. trackInfos.Append($"Track kind: {e.Track.Kind}\r\n");
  177. trackInfos.Append($"Track id: {e.Track.Id}\r\n");
  178. infoText.text = trackInfos.ToString();
  179. }
  180. private string GetName(RTCPeerConnection pc)
  181. {
  182. return (pc == _pc1) ? "pc1" : "pc2";
  183. }
  184. private RTCPeerConnection GetOtherPc(RTCPeerConnection pc)
  185. {
  186. return (pc == _pc1) ? _pc2 : _pc1;
  187. }
  188. private IEnumerator OnCreateOfferSuccess(RTCPeerConnection pc, RTCSessionDescription desc)
  189. {
  190. Debug.Log($"Offer from {GetName(pc)}\n{desc.sdp}");
  191. Debug.Log($"{GetName(pc)} setLocalDescription start");
  192. var op = pc.SetLocalDescription(ref desc);
  193. yield return op;
  194. if (!op.IsError)
  195. {
  196. OnSetLocalSuccess(pc);
  197. }
  198. else
  199. {
  200. var error = op.Error;
  201. OnSetSessionDescriptionError(ref error);
  202. }
  203. var otherPc = GetOtherPc(pc);
  204. Debug.Log($"{GetName(otherPc)} setRemoteDescription start");
  205. var op2 = otherPc.SetRemoteDescription(ref desc);
  206. yield return op2;
  207. if (!op2.IsError)
  208. {
  209. OnSetRemoteSuccess(otherPc);
  210. }
  211. else
  212. {
  213. var error = op2.Error;
  214. OnSetSessionDescriptionError(ref error);
  215. }
  216. Debug.Log($"{GetName(otherPc)} createAnswer start");
  217. // Since the 'remote' side has no media stream we need
  218. // to pass in the right constraints in order for it to
  219. // accept the incoming offer of audio and video.
  220. var op3 = otherPc.CreateAnswer();
  221. yield return op3;
  222. if (!op3.IsError)
  223. {
  224. yield return OnCreateAnswerSuccess(otherPc, op3.Desc);
  225. }
  226. else
  227. {
  228. OnCreateSessionDescriptionError(op3.Error);
  229. }
  230. }
  231. private void OnSetLocalSuccess(RTCPeerConnection pc)
  232. {
  233. Debug.Log($"{GetName(pc)} SetLocalDescription complete");
  234. }
  235. static void OnSetSessionDescriptionError(ref RTCError error)
  236. {
  237. Debug.LogError($"Error Detail Type: {error.message}");
  238. }
  239. private void OnSetRemoteSuccess(RTCPeerConnection pc)
  240. {
  241. Debug.Log($"{GetName(pc)} SetRemoteDescription complete");
  242. }
  243. IEnumerator OnCreateAnswerSuccess(RTCPeerConnection pc, RTCSessionDescription desc)
  244. {
  245. Debug.Log($"Answer from {GetName(pc)}:\n{desc.sdp}");
  246. Debug.Log($"{GetName(pc)} setLocalDescription start");
  247. var op = pc.SetLocalDescription(ref desc);
  248. yield return op;
  249. if (!op.IsError)
  250. {
  251. OnSetLocalSuccess(pc);
  252. }
  253. else
  254. {
  255. var error = op.Error;
  256. OnSetSessionDescriptionError(ref error);
  257. }
  258. var otherPc = GetOtherPc(pc);
  259. Debug.Log($"{GetName(otherPc)} setRemoteDescription start");
  260. var op2 = otherPc.SetRemoteDescription(ref desc);
  261. yield return op2;
  262. if (!op2.IsError)
  263. {
  264. OnSetRemoteSuccess(otherPc);
  265. }
  266. else
  267. {
  268. var error = op2.Error;
  269. OnSetSessionDescriptionError(ref error);
  270. }
  271. }
  272. private static void OnCreateSessionDescriptionError(RTCError error)
  273. {
  274. Debug.LogError($"Error Detail Type: {error.message}");
  275. }
  276. }
  277. }