PeerConnectionTest.cs 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. using System;
  2. using UnityEngine;
  3. using UnityEngine.TestTools;
  4. using NUnit.Framework;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using Object = UnityEngine.Object;
  9. namespace Unity.WebRTC.RuntimeTest
  10. {
  11. class PeerConnectionTest
  12. {
  13. static RTCConfiguration GetDefaultConfiguration()
  14. {
  15. RTCConfiguration config = default;
  16. config.iceServers = new[]
  17. {
  18. new RTCIceServer
  19. {
  20. urls = new[] {"stun:stun.l.google.com:19302"},
  21. username = "",
  22. credential = "",
  23. credentialType = RTCIceCredentialType.Password
  24. }
  25. };
  26. config.iceTransportPolicy = RTCIceTransportPolicy.All;
  27. config.iceCandidatePoolSize = 0;
  28. config.bundlePolicy = RTCBundlePolicy.BundlePolicyBalanced;
  29. return config;
  30. }
  31. [SetUp]
  32. public void SetUp()
  33. {
  34. WebRTC.Initialize(true);
  35. }
  36. [TearDown]
  37. public void TearDown()
  38. {
  39. WebRTC.Dispose();
  40. }
  41. [Test]
  42. public void Construct()
  43. {
  44. var peer = new RTCPeerConnection();
  45. Assert.AreEqual(0, peer.GetReceivers().Count());
  46. Assert.AreEqual(0, peer.GetSenders().Count());
  47. Assert.AreEqual(0, peer.GetTransceivers().Count());
  48. Assert.AreEqual(RTCPeerConnectionState.New, peer.ConnectionState);
  49. Assert.That(() => peer.LocalDescription, Throws.InvalidOperationException);
  50. Assert.That(() => peer.RemoteDescription, Throws.InvalidOperationException);
  51. Assert.That(() => peer.PendingLocalDescription, Throws.InvalidOperationException);
  52. Assert.That(() => peer.PendingRemoteDescription, Throws.InvalidOperationException);
  53. Assert.That(() => peer.CurrentLocalDescription, Throws.InvalidOperationException);
  54. Assert.That(() => peer.CurrentRemoteDescription, Throws.InvalidOperationException);
  55. peer.Close();
  56. Assert.AreEqual(RTCPeerConnectionState.Closed, peer.ConnectionState);
  57. peer.Dispose();
  58. }
  59. [Test]
  60. public void AccessAfterDisposed()
  61. {
  62. var peer = new RTCPeerConnection();
  63. peer.Dispose();
  64. Assert.That(() => { var state = peer.ConnectionState; }, Throws.TypeOf<ObjectDisposedException>());
  65. }
  66. [Test]
  67. public void GetConfiguration()
  68. {
  69. var config = GetDefaultConfiguration();
  70. var peer = new RTCPeerConnection(ref config);
  71. var config2 = peer.GetConfiguration();
  72. Assert.That(config.iceServers, Is.Not.Null);
  73. Assert.That(config2.iceServers, Is.Not.Null);
  74. Assert.That(config.iceServers.Length, Is.EqualTo(config2.iceServers.Length));
  75. Assert.That(config.iceServers[0].username, Is.EqualTo(config2.iceServers[0].username));
  76. Assert.That(config.iceServers[0].credential, Is.EqualTo(config2.iceServers[0].credential));
  77. Assert.That(config.iceServers[0].urls, Is.EqualTo(config2.iceServers[0].urls));
  78. Assert.That(config.iceTransportPolicy, Is.EqualTo(RTCIceTransportPolicy.All));
  79. Assert.That(config.iceTransportPolicy, Is.EqualTo(config2.iceTransportPolicy));
  80. Assert.That(config.enableDtlsSrtp, Is.Null);
  81. Assert.That(config.enableDtlsSrtp, Is.EqualTo(config2.enableDtlsSrtp));
  82. Assert.That(config.iceCandidatePoolSize, Is.EqualTo(config2.iceCandidatePoolSize));
  83. Assert.That(config.bundlePolicy, Is.EqualTo(config2.bundlePolicy));
  84. peer.Close();
  85. peer.Dispose();
  86. }
  87. [Test]
  88. public void ConstructWithConfigThrowException()
  89. {
  90. RTCConfiguration config = default;
  91. // To specify TURN server, also needs `username` and `credential`.
  92. config.iceServers = new[]
  93. {
  94. new RTCIceServer { urls = new[] {"turn:127.0.0.1?transport=udp"} }
  95. };
  96. Assert.That(() => { new RTCPeerConnection(ref config); }, Throws.ArgumentException);
  97. }
  98. [Test]
  99. public void SetConfiguration()
  100. {
  101. var peer = new RTCPeerConnection();
  102. var config = GetDefaultConfiguration();
  103. var result = peer.SetConfiguration(ref config);
  104. Assert.AreEqual(RTCErrorType.None, result);
  105. peer.Close();
  106. peer.Dispose();
  107. }
  108. [Test]
  109. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  110. public void AddTrack()
  111. {
  112. var peer = new RTCPeerConnection();
  113. var width = 256;
  114. var height = 256;
  115. var format = WebRTC.GetSupportedRenderTextureFormat(UnityEngine.SystemInfo.graphicsDeviceType);
  116. var rt = new UnityEngine.RenderTexture(width, height, 0, format);
  117. rt.Create();
  118. var track = new VideoStreamTrack(rt);
  119. var sender = peer.AddTrack(track);
  120. Assert.That(sender, Is.Not.Null);
  121. Assert.That(track, Is.EqualTo(sender.Track));
  122. RTCRtpSendParameters parameters = sender.GetParameters();
  123. Assert.That(parameters, Is.Not.Null);
  124. Assert.That(parameters.encodings, Is.Empty);
  125. track.Dispose();
  126. peer.Dispose();
  127. Object.DestroyImmediate(rt);
  128. }
  129. [Test]
  130. public void AddTrackThrowException()
  131. {
  132. var peer = new RTCPeerConnection();
  133. Assert.Throws<ArgumentNullException>(() => peer.AddTrack(null));
  134. }
  135. [Test]
  136. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  137. public void AddTransceiver()
  138. {
  139. var peer = new RTCPeerConnection();
  140. var width = 256;
  141. var height = 256;
  142. var format = WebRTC.GetSupportedRenderTextureFormat(SystemInfo.graphicsDeviceType);
  143. var rt = new UnityEngine.RenderTexture(width, height, 0, format);
  144. rt.Create();
  145. var track = new VideoStreamTrack(rt);
  146. Assert.That(peer.GetTransceivers(), Is.Empty);
  147. var transceiver = peer.AddTransceiver(track);
  148. Assert.That(transceiver, Is.Not.Null);
  149. Assert.That(transceiver.Mid, Is.Null);
  150. Assert.That(transceiver.CurrentDirection, Is.Null);
  151. RTCRtpSender sender = transceiver.Sender;
  152. Assert.That(sender, Is.Not.Null);
  153. Assert.That(track, Is.EqualTo(sender.Track));
  154. RTCRtpSendParameters parameters = sender.GetParameters();
  155. Assert.That(parameters, Is.Not.Null);
  156. Assert.That(parameters.encodings, Is.Empty);
  157. Assert.That(parameters.transactionId, Is.Not.Empty);
  158. Assert.That(peer.GetTransceivers(), Has.Count.EqualTo(1));
  159. Assert.That(peer.GetTransceivers().First(), Is.Not.Null);
  160. Assert.That(parameters.codecs, Is.Empty);
  161. Assert.That(parameters.rtcp, Is.Not.Null);
  162. // Some platforms return an empty list
  163. Assert.That(parameters.headerExtensions, Is.Not.Null);
  164. foreach (var extension in parameters.headerExtensions)
  165. {
  166. Assert.That(extension, Is.Not.Null);
  167. Assert.That(extension.uri, Is.Not.Empty);
  168. }
  169. track.Dispose();
  170. peer.Dispose();
  171. Object.DestroyImmediate(rt);
  172. }
  173. [Test]
  174. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  175. public void GetTransceiversReturnsNotEmptyAfterDisposingTransceiver()
  176. {
  177. // `RTCPeerConnection.AddTransceiver` method is not intuitive. Moreover, we don't have the API to remove
  178. // the transceiver from RTCPeerConnection directly.
  179. var peer = new RTCPeerConnection();
  180. var transceiver = peer.AddTransceiver(TrackKind.Video);
  181. Assert.That(peer.GetTransceivers(), Has.Count.EqualTo(1));
  182. transceiver.Dispose();
  183. Assert.That(peer.GetTransceivers(), Has.Count.EqualTo(1));
  184. peer.Dispose();
  185. }
  186. [Test]
  187. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  188. public void GetTransceiversReturnsNotEmptyAfterCallingRemoveTrack()
  189. {
  190. // Also, `RTCPeerConnection.AddTrack` and `RTCPeerConnection.RemoveTrack` method is not intuitive.
  191. var peer = new RTCPeerConnection();
  192. var width = 256;
  193. var height = 256;
  194. var format = WebRTC.GetSupportedRenderTextureFormat(UnityEngine.SystemInfo.graphicsDeviceType);
  195. var rt = new UnityEngine.RenderTexture(width, height, 0, format);
  196. rt.Create();
  197. var track = new VideoStreamTrack(rt);
  198. var sender = peer.AddTrack(track);
  199. Assert.That(peer.GetTransceivers(), Has.Count.EqualTo(1));
  200. Assert.That(peer.RemoveTrack(sender), Is.EqualTo(RTCErrorType.None));
  201. Assert.That(peer.GetTransceivers(), Has.Count.EqualTo(1));
  202. peer.Dispose();
  203. }
  204. [Test]
  205. public void AddTransceiverThrowException()
  206. {
  207. var peer = new RTCPeerConnection();
  208. Assert.Throws<ArgumentNullException>(() => peer.AddTransceiver(null));
  209. }
  210. [Test]
  211. public void AddTransceiverTrackKindAudio()
  212. {
  213. var peer = new RTCPeerConnection();
  214. var transceiver = peer.AddTransceiver(TrackKind.Audio);
  215. Assert.That(transceiver, Is.Not.Null);
  216. Assert.That(transceiver.CurrentDirection, Is.Null);
  217. RTCRtpReceiver receiver = transceiver.Receiver;
  218. Assert.That(receiver, Is.Not.Null);
  219. MediaStreamTrack track = receiver.Track;
  220. Assert.That(track, Is.Not.Null);
  221. Assert.That(track.Kind, Is.EqualTo(TrackKind.Audio));
  222. Assert.That(track, Is.TypeOf<AudioStreamTrack>());
  223. Assert.That(receiver.Streams, Has.Count.EqualTo(0));
  224. Assert.That(peer.GetTransceivers(), Has.Count.EqualTo(1));
  225. Assert.That(peer.GetTransceivers(), Has.All.Not.Null);
  226. peer.Dispose();
  227. }
  228. [Test]
  229. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  230. public void AddTransceiverTrackKindVideo()
  231. {
  232. var peer = new RTCPeerConnection();
  233. var transceiver = peer.AddTransceiver(TrackKind.Video);
  234. Assert.That(transceiver, Is.Not.Null);
  235. Assert.That(transceiver.CurrentDirection, Is.Null);
  236. RTCRtpReceiver receiver = transceiver.Receiver;
  237. Assert.That(receiver, Is.Not.Null);
  238. MediaStreamTrack track = receiver.Track;
  239. Assert.That(track, Is.Not.Null);
  240. Assert.That(track.Kind, Is.EqualTo(TrackKind.Video));
  241. Assert.That(track, Is.TypeOf<VideoStreamTrack>());
  242. Assert.That(receiver.Streams, Has.Count.EqualTo(0));
  243. Assert.That(peer.GetTransceivers(), Has.Count.EqualTo(1));
  244. Assert.That(peer.GetTransceivers(), Has.All.Not.Null);
  245. peer.Dispose();
  246. }
  247. [Test]
  248. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  249. public void AddTransceiverWithInit()
  250. {
  251. var peer = new RTCPeerConnection();
  252. var stream = new MediaStream();
  253. var direction = RTCRtpTransceiverDirection.SendOnly;
  254. var width = 256;
  255. var height = 256;
  256. var format = WebRTC.GetSupportedRenderTextureFormat(UnityEngine.SystemInfo.graphicsDeviceType);
  257. var rt = new RenderTexture(width, height, 0, format);
  258. rt.Create();
  259. var track = new VideoStreamTrack(rt);
  260. var init = new RTCRtpTransceiverInit()
  261. {
  262. direction = direction,
  263. sendEncodings = new RTCRtpEncodingParameters[] {
  264. new RTCRtpEncodingParameters { maxFramerate = 30 }
  265. },
  266. streams = new MediaStream[] { stream }
  267. };
  268. var transceiver = peer.AddTransceiver(track, init);
  269. Assert.That(transceiver, Is.Not.Null);
  270. Assert.That(transceiver.CurrentDirection, Is.Null);
  271. Assert.That(transceiver.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendOnly));
  272. Assert.That(transceiver.Sender, Is.Not.Null);
  273. var parameters = transceiver.Sender.GetParameters();
  274. Assert.That(parameters, Is.Not.Null);
  275. Assert.That(parameters.codecs, Is.Not.Null.And.Empty);
  276. peer.Dispose();
  277. }
  278. [Test]
  279. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  280. public void AddTransceiverWithKindAndInit()
  281. {
  282. var peer = new RTCPeerConnection();
  283. var stream = new MediaStream();
  284. var direction = RTCRtpTransceiverDirection.SendOnly;
  285. var init = new RTCRtpTransceiverInit()
  286. {
  287. direction = direction,
  288. sendEncodings = new RTCRtpEncodingParameters[] {
  289. new RTCRtpEncodingParameters { maxFramerate = 30 }
  290. },
  291. streams = new MediaStream[] { stream }
  292. };
  293. var transceiver = peer.AddTransceiver(TrackKind.Video, init);
  294. Assert.That(transceiver, Is.Not.Null);
  295. Assert.That(transceiver.CurrentDirection, Is.Null);
  296. Assert.That(transceiver.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendOnly));
  297. Assert.That(transceiver.Sender, Is.Not.Null);
  298. var parameters = transceiver.Sender.GetParameters();
  299. Assert.That(parameters, Is.Not.Null);
  300. Assert.That(parameters.codecs, Is.Not.Null.And.Empty);
  301. var init2 = new RTCRtpTransceiverInit()
  302. {
  303. direction = null,
  304. sendEncodings = null,
  305. streams = null
  306. };
  307. var transceiver2 = peer.AddTransceiver(TrackKind.Video, init2);
  308. Assert.That(transceiver2, Is.Not.Null);
  309. Assert.That(transceiver2.CurrentDirection, Is.Null);
  310. Assert.That(transceiver2.Direction, Is.EqualTo(RTCRtpTransceiverDirection.SendRecv));
  311. Assert.That(transceiver2.Sender, Is.Not.Null);
  312. peer.Dispose();
  313. }
  314. [Test]
  315. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  316. public void GetAndSetDirectionTransceiver()
  317. {
  318. var peer = new RTCPeerConnection();
  319. var transceiver = peer.AddTransceiver(TrackKind.Video);
  320. Assert.NotNull(transceiver);
  321. transceiver.Direction = RTCRtpTransceiverDirection.SendOnly;
  322. Assert.AreEqual(RTCRtpTransceiverDirection.SendOnly, transceiver.Direction);
  323. transceiver.Direction = RTCRtpTransceiverDirection.RecvOnly;
  324. Assert.AreEqual(RTCRtpTransceiverDirection.RecvOnly, transceiver.Direction);
  325. peer.Close();
  326. peer.Dispose();
  327. }
  328. [Test]
  329. public void GetTransceivers()
  330. {
  331. var peer = new RTCPeerConnection();
  332. var obj = new GameObject("audio");
  333. var source = obj.AddComponent<AudioSource>();
  334. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  335. var track = new AudioStreamTrack(source);
  336. var sender = peer.AddTrack(track);
  337. Assert.That(peer.GetTransceivers().ToList(), Has.Count.EqualTo(1));
  338. Assert.That(peer.GetTransceivers().Select(t => t.Sender).ToList(), Has.Member(sender));
  339. track.Dispose();
  340. peer.Dispose();
  341. Object.DestroyImmediate(source.clip);
  342. Object.DestroyImmediate(obj);
  343. }
  344. [UnityTest]
  345. [Timeout(1000)]
  346. [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer })]
  347. public IEnumerator CurrentDirection()
  348. {
  349. var config = GetDefaultConfiguration();
  350. var peer1 = new RTCPeerConnection(ref config);
  351. var peer2 = new RTCPeerConnection(ref config);
  352. var obj = new GameObject("audio");
  353. var source = obj.AddComponent<AudioSource>();
  354. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  355. var audioTrack = new AudioStreamTrack(source);
  356. var transceiver1 = peer1.AddTransceiver(TrackKind.Audio);
  357. transceiver1.Direction = RTCRtpTransceiverDirection.RecvOnly;
  358. Assert.IsNull(transceiver1.CurrentDirection);
  359. var op1 = peer1.CreateOffer();
  360. yield return op1;
  361. var desc = op1.Desc;
  362. var op2 = peer1.SetLocalDescription(ref desc);
  363. yield return op2;
  364. var op3 = peer2.SetRemoteDescription(ref desc);
  365. yield return op3;
  366. var transceiver2 = peer2.GetTransceivers().First(x => x.Receiver.Track.Kind == TrackKind.Audio);
  367. Assert.True(transceiver2.Sender.ReplaceTrack(audioTrack));
  368. transceiver2.Direction = RTCRtpTransceiverDirection.SendOnly;
  369. var op4 = peer2.CreateAnswer();
  370. yield return op4;
  371. desc = op4.Desc;
  372. var op5 = peer2.SetLocalDescription(ref desc);
  373. yield return op5;
  374. var op6 = peer1.SetRemoteDescription(ref desc);
  375. yield return op6;
  376. Assert.AreEqual(transceiver1.CurrentDirection, RTCRtpTransceiverDirection.RecvOnly);
  377. Assert.AreEqual(transceiver2.CurrentDirection, RTCRtpTransceiverDirection.SendOnly);
  378. //Assert.That(transceiver2.Stop(), Is.EqualTo(RTCErrorType.None));
  379. //Assert.That(transceiver2.Direction, Is.EqualTo(RTCRtpTransceiverDirection.Stopped));
  380. // todo(kazuki):: Transceiver.CurrentDirection of Sender is not changed to "Stopped" even if waiting
  381. // yield return new WaitUntil(() => transceiver2.CurrentDirection == RTCRtpTransceiverDirection.Stopped);
  382. // Assert.That(transceiver2.CurrentDirection, Is.EqualTo(RTCRtpTransceiverDirection.Stopped));
  383. // todo(kazuki):: Transceiver.CurrentDirection of Receiver is not changed to "Stopped" even if waiting
  384. // yield return new WaitUntil(() => transceiver1.Direction == RTCRtpTransceiverDirection.Stopped);
  385. // Assert.That(transceiver1.Direction, Is.EqualTo(RTCRtpTransceiverDirection.Stopped));
  386. audioTrack.Dispose();
  387. peer1.Close();
  388. peer2.Close();
  389. peer1.Dispose();
  390. peer2.Dispose();
  391. Object.DestroyImmediate(source.clip);
  392. Object.DestroyImmediate(obj);
  393. }
  394. [UnityTest]
  395. [Timeout(5000)]
  396. [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
  397. public IEnumerator TransceiverReturnsSender()
  398. {
  399. RTCConfiguration config = default;
  400. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  401. var peer1 = new RTCPeerConnection(ref config);
  402. var peer2 = new RTCPeerConnection(ref config);
  403. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  404. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  405. var obj1 = new GameObject("audio1");
  406. var source1 = obj1.AddComponent<AudioSource>();
  407. source1.clip = AudioClip.Create("test1", 480, 2, 48000, false);
  408. AudioStreamTrack track1 = new AudioStreamTrack(source1);
  409. peer1.AddTrack(track1);
  410. yield return SignalingOffer(peer1, peer2);
  411. Assert.That(peer2.GetTransceivers().Count(), Is.EqualTo(1));
  412. RTCRtpSender sender1 = peer2.GetTransceivers().First().Sender;
  413. Assert.That(sender1, Is.Not.Null);
  414. var obj2 = new GameObject("audio2");
  415. var source2 = obj2.AddComponent<AudioSource>();
  416. source2.clip = AudioClip.Create("test2", 480, 2, 48000, false);
  417. AudioStreamTrack track2 = new AudioStreamTrack(source2);
  418. RTCRtpSender sender2 = peer2.AddTrack(track2);
  419. Assert.That(sender2, Is.Not.Null);
  420. Assert.That(sender1, Is.EqualTo(sender2));
  421. track1.Dispose();
  422. track2.Dispose();
  423. peer1.Dispose();
  424. peer2.Dispose();
  425. Object.DestroyImmediate(source1.clip);
  426. Object.DestroyImmediate(source2.clip);
  427. Object.DestroyImmediate(obj1);
  428. Object.DestroyImmediate(obj2);
  429. }
  430. [UnityTest]
  431. [Timeout(1000)]
  432. public IEnumerator CreateOffer()
  433. {
  434. var config = GetDefaultConfiguration();
  435. var peer = new RTCPeerConnection(ref config);
  436. var op = peer.CreateOffer();
  437. yield return op;
  438. Assert.True(op.IsDone);
  439. Assert.False(op.IsError);
  440. peer.Close();
  441. peer.Dispose();
  442. }
  443. [UnityTest]
  444. [Timeout(1000)]
  445. public IEnumerator CreateAnswerFailed()
  446. {
  447. var config = GetDefaultConfiguration();
  448. var peer = new RTCPeerConnection(ref config);
  449. var op = peer.CreateAnswer();
  450. yield return op;
  451. Assert.True(op.IsDone);
  452. // This is failed
  453. Assert.True(op.IsError);
  454. Assert.IsNotEmpty(op.Error.message);
  455. peer.Close();
  456. peer.Dispose();
  457. }
  458. [UnityTest]
  459. [Timeout(1000)]
  460. public IEnumerator CreateAnswer()
  461. {
  462. var config = GetDefaultConfiguration();
  463. var peer1 = new RTCPeerConnection(ref config);
  464. var peer2 = new RTCPeerConnection(ref config);
  465. peer1.CreateDataChannel("data");
  466. var op1 = peer1.CreateOffer();
  467. yield return op1;
  468. var desc = op1.Desc;
  469. var op2 = peer1.SetLocalDescription(ref desc);
  470. yield return op2;
  471. var op3 = peer2.SetRemoteDescription(ref desc);
  472. yield return op3;
  473. var op4 = peer2.CreateAnswer();
  474. yield return op4;
  475. Assert.True(op4.IsDone);
  476. Assert.False(op4.IsError);
  477. peer1.Close();
  478. peer2.Close();
  479. peer1.Dispose();
  480. peer2.Dispose();
  481. }
  482. [UnityTest]
  483. [Timeout(1000)]
  484. public IEnumerator SetLocalDescription()
  485. {
  486. var peer = new RTCPeerConnection();
  487. var op = peer.CreateOffer();
  488. yield return op;
  489. Assert.True(op.IsDone);
  490. Assert.False(op.IsError);
  491. var desc = op.Desc;
  492. var op2 = peer.SetLocalDescription(ref desc);
  493. yield return op2;
  494. Assert.True(op2.IsDone);
  495. Assert.False(op2.IsError);
  496. var desc2 = peer.LocalDescription;
  497. Assert.AreEqual(desc.sdp, desc2.sdp);
  498. Assert.AreEqual(desc.type, desc2.type);
  499. peer.Close();
  500. peer.Dispose();
  501. }
  502. [Test]
  503. public void SetLocalDescriptionThrowException()
  504. {
  505. var peer = new RTCPeerConnection();
  506. RTCSessionDescription empty = new RTCSessionDescription();
  507. Assert.Throws<ArgumentException>(() => peer.SetLocalDescription(ref empty));
  508. RTCSessionDescription invalid = new RTCSessionDescription { sdp = "this is invalid parameter" };
  509. Assert.Throws<RTCErrorException>(() => peer.SetLocalDescription(ref invalid));
  510. peer.Dispose();
  511. }
  512. [UnityTest]
  513. [Timeout(1000)]
  514. [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer })]
  515. public IEnumerator SetRemoteDescription()
  516. {
  517. var config = GetDefaultConfiguration();
  518. var peer1 = new RTCPeerConnection(ref config);
  519. var peer2 = new RTCPeerConnection(ref config);
  520. var channel1 = peer1.CreateDataChannel("data");
  521. var op1 = peer1.CreateOffer();
  522. yield return op1;
  523. var desc = op1.Desc;
  524. var op2 = peer1.SetLocalDescription(ref desc);
  525. yield return op2;
  526. var op3 = peer2.SetRemoteDescription(ref desc);
  527. yield return op3;
  528. var op4 = peer2.CreateAnswer();
  529. yield return op4;
  530. desc = op4.Desc;
  531. var op5 = peer2.SetLocalDescription(ref desc);
  532. yield return op5;
  533. var op6 = peer1.SetRemoteDescription(ref desc);
  534. yield return op6;
  535. var desc2 = peer1.RemoteDescription;
  536. Assert.AreEqual(desc.sdp, desc2.sdp);
  537. Assert.AreEqual(desc.type, desc2.type);
  538. channel1.Dispose();
  539. peer1.Close();
  540. peer2.Close();
  541. peer1.Dispose();
  542. peer2.Dispose();
  543. }
  544. [UnityTest]
  545. [Timeout(5000)]
  546. public IEnumerator SetDescriptionInParallel()
  547. {
  548. RTCConfiguration config = default;
  549. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  550. var peer1 = new RTCPeerConnection(ref config);
  551. var peer2 = new RTCPeerConnection(ref config);
  552. var neededNegotiationPeer1 = false;
  553. var neededNegotiationPeer2 = false;
  554. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  555. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  556. peer1.OnNegotiationNeeded += () => neededNegotiationPeer1 = true;
  557. peer2.OnNegotiationNeeded += () => neededNegotiationPeer2 = true;
  558. peer1.AddTransceiver(TrackKind.Audio);
  559. yield return new WaitUntil(() => neededNegotiationPeer1);
  560. var op1 = peer1.SetLocalDescription();
  561. yield return op1;
  562. RTCSessionDescription desc = peer1.LocalDescription;
  563. Assert.That(desc.type, Is.EqualTo(RTCSdpType.Offer));
  564. Assert.That(peer1.SignalingState, Is.EqualTo(RTCSignalingState.HaveLocalOffer));
  565. peer2.AddTransceiver(TrackKind.Audio);
  566. yield return new WaitUntil(() => neededNegotiationPeer2);
  567. var op2 = peer2.SetLocalDescription();
  568. var op3 = peer2.SetRemoteDescription(ref desc);
  569. yield return op2;
  570. yield return op3;
  571. Assert.That(peer2.RemoteDescription.type, Is.EqualTo(RTCSdpType.Offer));
  572. Assert.That(peer2.SignalingState, Is.EqualTo(RTCSignalingState.HaveRemoteOffer));
  573. var op4 = peer2.SetLocalDescription();
  574. yield return op4;
  575. desc = peer2.LocalDescription;
  576. Assert.That(desc.type, Is.EqualTo(RTCSdpType.Answer));
  577. Assert.That(peer2.SignalingState, Is.EqualTo(RTCSignalingState.Stable));
  578. var op5 = peer1.SetRemoteDescription(ref desc);
  579. yield return op5;
  580. peer1.Close();
  581. peer2.Close();
  582. }
  583. [UnityTest]
  584. [Timeout(5000)]
  585. public IEnumerator CreateDescriptionInParallel()
  586. {
  587. RTCConfiguration config = default;
  588. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  589. var peer1 = new RTCPeerConnection(ref config);
  590. var peer2 = new RTCPeerConnection(ref config);
  591. var neededNegotiationPeer1 = false;
  592. var neededNegotiationPeer2 = false;
  593. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  594. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  595. peer1.OnNegotiationNeeded += () => neededNegotiationPeer1 = true;
  596. peer2.OnNegotiationNeeded += () => neededNegotiationPeer2 = true;
  597. peer1.AddTransceiver(TrackKind.Audio);
  598. yield return new WaitUntil(() => neededNegotiationPeer1);
  599. var op1 = peer1.SetLocalDescription();
  600. yield return op1;
  601. RTCSessionDescription desc = peer1.LocalDescription;
  602. Assert.That(desc.type, Is.EqualTo(RTCSdpType.Offer));
  603. Assert.That(peer1.SignalingState, Is.EqualTo(RTCSignalingState.HaveLocalOffer));
  604. peer2.AddTransceiver(TrackKind.Audio);
  605. yield return new WaitUntil(() => neededNegotiationPeer2);
  606. var op2 = peer2.SetRemoteDescription(ref desc);
  607. yield return op2;
  608. Assert.That(peer2.RemoteDescription.type, Is.EqualTo(RTCSdpType.Offer));
  609. Assert.That(peer2.SignalingState, Is.EqualTo(RTCSignalingState.HaveRemoteOffer));
  610. var op3 = peer2.CreateOffer();
  611. var op4 = peer2.CreateAnswer();
  612. yield return op3;
  613. yield return op4;
  614. desc = op4.Desc;
  615. var op5 = peer2.SetLocalDescription(ref desc);
  616. yield return op5;
  617. Assert.That(desc.type, Is.EqualTo(RTCSdpType.Answer));
  618. Assert.That(peer2.SignalingState, Is.EqualTo(RTCSignalingState.Stable));
  619. var op6 = peer1.SetRemoteDescription(ref desc);
  620. yield return op6;
  621. peer1.Close();
  622. peer2.Close();
  623. }
  624. [Test]
  625. public void SetRemoteDescriptionThrowException()
  626. {
  627. var peer = new RTCPeerConnection();
  628. RTCSessionDescription empty = new RTCSessionDescription();
  629. Assert.Throws<ArgumentException>(() => peer.SetRemoteDescription(ref empty));
  630. RTCSessionDescription invalid = new RTCSessionDescription { sdp = "this is invalid parameter" };
  631. Assert.Throws<RTCErrorException>(() => peer.SetRemoteDescription(ref invalid));
  632. peer.Dispose();
  633. }
  634. [UnityTest]
  635. [Timeout(1000)]
  636. public IEnumerator SetLocalDescriptionFailed()
  637. {
  638. var peer = new RTCPeerConnection();
  639. var stream = new MediaStream();
  640. var obj = new GameObject("audio");
  641. var source = obj.AddComponent<AudioSource>();
  642. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  643. var track = new AudioStreamTrack(source);
  644. var sender = peer.AddTrack(track, stream);
  645. var op = peer.CreateOffer();
  646. yield return op;
  647. Assert.True(op.IsDone);
  648. Assert.False(op.IsError);
  649. var desc = op.Desc;
  650. // change sdp to cannot parse
  651. desc.sdp = desc.sdp.Replace("a=mid:0", "a=mid:10");
  652. var op2 = peer.SetLocalDescription(ref desc);
  653. yield return op2;
  654. Assert.True(op2.IsDone);
  655. Assert.True(op2.IsError);
  656. Assert.IsNotEmpty(op2.Error.message);
  657. Assert.That(peer.RemoveTrack(sender), Is.EqualTo(RTCErrorType.None));
  658. track.Dispose();
  659. stream.Dispose();
  660. peer.Close();
  661. peer.Dispose();
  662. Object.DestroyImmediate(source.clip);
  663. Object.DestroyImmediate(obj);
  664. }
  665. [UnityTest]
  666. [Timeout(1000)]
  667. public IEnumerator SetRemoteDescriptionFailed()
  668. {
  669. var config = GetDefaultConfiguration();
  670. var peer1 = new RTCPeerConnection(ref config);
  671. var peer2 = new RTCPeerConnection(ref config);
  672. var stream = new MediaStream();
  673. var obj = new GameObject("audio");
  674. var source = obj.AddComponent<AudioSource>();
  675. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  676. var track = new AudioStreamTrack(source);
  677. var sender = peer1.AddTrack(track, stream);
  678. var op1 = peer1.CreateOffer();
  679. yield return op1;
  680. var desc = op1.Desc;
  681. var op2 = peer1.SetLocalDescription(ref desc);
  682. yield return op2;
  683. // change sdp to cannot parse
  684. desc.sdp = desc.sdp.Replace("a=mid:0", "a=mid:10");
  685. var op3 = peer2.SetRemoteDescription(ref desc);
  686. yield return op3;
  687. Assert.True(op3.IsDone);
  688. Assert.True(op3.IsError);
  689. Assert.IsNotEmpty(op3.Error.message);
  690. peer1.RemoveTrack(sender);
  691. track.Dispose();
  692. stream.Dispose();
  693. peer1.Close();
  694. peer2.Close();
  695. peer1.Dispose();
  696. peer2.Dispose();
  697. Object.DestroyImmediate(source.clip);
  698. Object.DestroyImmediate(obj);
  699. }
  700. [UnityTest]
  701. [Timeout(5000)]
  702. [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
  703. public IEnumerator IceConnectionStateChange()
  704. {
  705. RTCConfiguration config = default;
  706. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  707. var peer1 = new RTCPeerConnection(ref config);
  708. var peer2 = new RTCPeerConnection(ref config);
  709. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  710. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  711. var obj = new GameObject("audio");
  712. var source = obj.AddComponent<AudioSource>();
  713. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  714. var track = new AudioStreamTrack(source);
  715. peer1.AddTrack(track);
  716. var op1 = peer1.CreateOffer();
  717. yield return op1;
  718. var desc = op1.Desc;
  719. var op2 = peer1.SetLocalDescription(ref desc);
  720. yield return op2;
  721. var op3 = peer2.SetRemoteDescription(ref desc);
  722. yield return op3;
  723. var op4 = peer2.CreateAnswer();
  724. yield return op4;
  725. desc = op4.Desc;
  726. var op5 = peer2.SetLocalDescription(ref desc);
  727. yield return op5;
  728. var op6 = peer1.SetRemoteDescription(ref desc);
  729. yield return op6;
  730. var op7 = new WaitUntilWithTimeout(
  731. () => peer1.IceConnectionState == RTCIceConnectionState.Connected ||
  732. peer1.IceConnectionState == RTCIceConnectionState.Completed, 5000);
  733. yield return op7;
  734. Assert.True(op7.IsCompleted);
  735. var op8 = new WaitUntilWithTimeout(
  736. () => peer2.IceConnectionState == RTCIceConnectionState.Connected ||
  737. peer2.IceConnectionState == RTCIceConnectionState.Completed, 5000);
  738. yield return op8;
  739. Assert.True(op8.IsCompleted);
  740. track.Dispose();
  741. peer1.Close();
  742. peer2.Close();
  743. Object.DestroyImmediate(source.clip);
  744. Object.DestroyImmediate(obj);
  745. }
  746. [UnityTest]
  747. [Timeout(5000)]
  748. [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
  749. public IEnumerator AddIceCandidate()
  750. {
  751. RTCConfiguration config = default;
  752. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  753. var peer1 = new RTCPeerConnection(ref config);
  754. var peer2 = new RTCPeerConnection(ref config);
  755. var peer1ReceiveCandidateQueue = new Queue<RTCIceCandidate>();
  756. var peer2ReceiveCandidateQueue = new Queue<RTCIceCandidate>();
  757. peer1.OnIceCandidate = candidate => { peer2ReceiveCandidateQueue.Enqueue(candidate); };
  758. peer2.OnIceCandidate = candidate => { peer1ReceiveCandidateQueue.Enqueue(candidate); };
  759. var obj = new GameObject("audio");
  760. var source = obj.AddComponent<AudioSource>();
  761. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  762. var track = new AudioStreamTrack(source);
  763. peer1.AddTrack(track);
  764. var op1 = peer1.CreateOffer();
  765. yield return op1;
  766. var desc = op1.Desc;
  767. var op2 = peer1.SetLocalDescription(ref desc);
  768. yield return op2;
  769. yield return new WaitUntil(() => peer2ReceiveCandidateQueue.Any());
  770. Assert.That(peer2.AddIceCandidate(peer2ReceiveCandidateQueue.Peek()), Is.False);
  771. var op3 = peer2.SetRemoteDescription(ref desc);
  772. yield return op3;
  773. Assert.That(peer2.AddIceCandidate(peer2ReceiveCandidateQueue.Dequeue()), Is.True);
  774. var op4 = peer2.CreateAnswer();
  775. yield return op4;
  776. desc = op4.Desc;
  777. var op5 = peer2.SetLocalDescription(ref desc);
  778. yield return op5;
  779. yield return new WaitUntil(() => peer1ReceiveCandidateQueue.Any());
  780. Assert.That(peer1.AddIceCandidate(peer1ReceiveCandidateQueue.Peek()), Is.False);
  781. var op6 = peer1.SetRemoteDescription(ref desc);
  782. yield return op6;
  783. Assert.That(peer1.AddIceCandidate(peer1ReceiveCandidateQueue.Dequeue()), Is.True);
  784. var op7 = new WaitUntilWithTimeout(
  785. () => peer1.IceConnectionState == RTCIceConnectionState.Connected ||
  786. peer1.IceConnectionState == RTCIceConnectionState.Completed, 5000);
  787. yield return op7;
  788. Assert.That(op7.IsCompleted, Is.True);
  789. var op8 = new WaitUntilWithTimeout(
  790. () => peer2.IceConnectionState == RTCIceConnectionState.Connected ||
  791. peer2.IceConnectionState == RTCIceConnectionState.Completed, 5000);
  792. yield return op8;
  793. Assert.That(op8.IsCompleted, Is.True);
  794. foreach (var candidate in peer1ReceiveCandidateQueue)
  795. {
  796. Assert.That(peer1.AddIceCandidate(candidate), Is.True);
  797. }
  798. peer1ReceiveCandidateQueue.Clear();
  799. foreach (var candidate in peer2ReceiveCandidateQueue)
  800. {
  801. Assert.That(peer2.AddIceCandidate(candidate), Is.True);
  802. }
  803. peer2ReceiveCandidateQueue.Clear();
  804. track.Dispose();
  805. peer1.Close();
  806. peer2.Close();
  807. Object.DestroyImmediate(source.clip);
  808. Object.DestroyImmediate(obj);
  809. }
  810. [UnityTest]
  811. [Timeout(5000)]
  812. [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
  813. public IEnumerator MediaStreamTrackThrowExceptionAfterPeerDisposed()
  814. {
  815. RTCConfiguration config = default;
  816. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  817. var peer1 = new RTCPeerConnection(ref config);
  818. var peer2 = new RTCPeerConnection(ref config);
  819. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  820. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  821. var obj = new GameObject("audio");
  822. var source = obj.AddComponent<AudioSource>();
  823. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  824. AudioStreamTrack track = new AudioStreamTrack(source);
  825. peer1.AddTrack(track);
  826. MediaStreamTrack track1 = null;
  827. peer2.OnTrack = e => { track1 = e.Track; };
  828. yield return SignalingOffer(peer1, peer2);
  829. Assert.That(track1, Is.Not.Null);
  830. peer2.Dispose();
  831. Assert.That(() => track1.Id, Throws.TypeOf<ObjectDisposedException>());
  832. track.Dispose();
  833. track1.Dispose();
  834. Object.DestroyImmediate(source.clip);
  835. Object.DestroyImmediate(obj);
  836. }
  837. [UnityTest]
  838. [Timeout(5000)]
  839. [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
  840. public IEnumerator PeerConnectionStateChange()
  841. {
  842. RTCConfiguration config = default;
  843. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  844. var peer1 = new RTCPeerConnection(ref config);
  845. var peer2 = new RTCPeerConnection(ref config);
  846. RTCPeerConnectionState state1 = default;
  847. RTCPeerConnectionState state2 = default;
  848. RTCIceConnectionState iceState1 = default;
  849. RTCIceConnectionState iceState2 = default;
  850. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  851. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  852. peer1.OnConnectionStateChange = state => { state1 = state; };
  853. peer2.OnConnectionStateChange = state => { state2 = state; };
  854. peer1.OnIceConnectionChange = state => { iceState1 = state; };
  855. peer2.OnIceConnectionChange = state => { iceState2 = state; };
  856. Assert.That(state1, Is.EqualTo(RTCPeerConnectionState.New));
  857. Assert.That(state2, Is.EqualTo(RTCPeerConnectionState.New));
  858. var obj = new GameObject("audio");
  859. var source = obj.AddComponent<AudioSource>();
  860. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  861. AudioStreamTrack track1 = new AudioStreamTrack(source);
  862. peer1.AddTrack(track1);
  863. var op1 = peer1.CreateOffer();
  864. yield return op1;
  865. var desc = op1.Desc;
  866. var op2 = peer1.SetLocalDescription(ref desc);
  867. yield return op2;
  868. var op3 = peer2.SetRemoteDescription(ref desc);
  869. yield return op3;
  870. var op4 = peer2.CreateAnswer();
  871. yield return op4;
  872. desc = op4.Desc;
  873. var op5 = peer2.SetLocalDescription(ref desc);
  874. yield return op5;
  875. var op6 = peer1.SetRemoteDescription(ref desc);
  876. yield return op6;
  877. var op7 = new WaitUntilWithTimeout(() =>
  878. state1 == RTCPeerConnectionState.Connected &&
  879. state2 == RTCPeerConnectionState.Connected, 5000);
  880. yield return op7;
  881. Assert.That(op7.IsCompleted, Is.True);
  882. var op8 = new WaitUntilWithTimeout(() =>
  883. (iceState1 == RTCIceConnectionState.Connected || iceState1 == RTCIceConnectionState.Completed) &&
  884. (iceState2 == RTCIceConnectionState.Connected || iceState2 == RTCIceConnectionState.Completed)
  885. , 5000);
  886. yield return op8;
  887. Assert.That(op8.IsCompleted, Is.True);
  888. peer1.Close();
  889. var op9 = new WaitUntilWithTimeout(() =>
  890. state1 == RTCPeerConnectionState.Closed &&
  891. iceState2 == RTCIceConnectionState.Disconnected, 5000);
  892. yield return op9;
  893. Assert.That(op9.IsCompleted, Is.True);
  894. track1.Dispose();
  895. peer2.Close();
  896. Object.DestroyImmediate(source.clip);
  897. Object.DestroyImmediate(obj);
  898. }
  899. [UnityTest]
  900. [Timeout(5000)]
  901. [ConditionalIgnore(ConditionalIgnore.UnsupportedPlatformOpenGL, "Not support VideoStreamTrack for OpenGL")]
  902. public IEnumerator GetStatsReturnsReport()
  903. {
  904. if (SystemInfo.processorType == "Apple M1")
  905. Assert.Ignore("todo:: This test will hang up on Apple M1");
  906. var stream = new MediaStream();
  907. var go = new GameObject("Test");
  908. var cam = go.AddComponent<Camera>();
  909. stream.AddTrack(cam.CaptureStreamTrack(1280, 720));
  910. var source = go.AddComponent<AudioSource>();
  911. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  912. stream.AddTrack(new AudioStreamTrack(source));
  913. var test = new MonoBehaviourTest<SignalingPeers>();
  914. test.component.AddStream(0, stream);
  915. yield return test;
  916. test.component.CoroutineUpdate();
  917. var op1 = test.component.GetSenderStats(0, 0);
  918. var op2 = test.component.GetReceiverStats(0, 0);
  919. var op3 = test.component.GetSenderStats(1, 0);
  920. var op4 = test.component.GetReceiverStats(1, 0);
  921. var ops = new[] { op1, op2, op3, op4 };
  922. foreach (var op in ops)
  923. {
  924. yield return op;
  925. Assert.That(op.IsDone, Is.True);
  926. Assert.That(op.Value, Is.Not.Null);
  927. Assert.That(op.Value.Stats, Is.Not.Null);
  928. }
  929. op1.Value.Dispose();
  930. op2.Value.Dispose();
  931. op3.Value.Dispose();
  932. op4.Value.Dispose();
  933. test.component.Dispose();
  934. foreach (var track in stream.GetTracks())
  935. {
  936. track.Dispose();
  937. }
  938. stream.Dispose();
  939. Object.DestroyImmediate(go);
  940. Object.DestroyImmediate(test.gameObject);
  941. }
  942. [UnityTest]
  943. [Timeout(5000)]
  944. [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
  945. public IEnumerator RestartIceInvokeOnNegotiationNeeded()
  946. {
  947. RTCConfiguration config = default;
  948. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  949. var peer1 = new RTCPeerConnection(ref config);
  950. var peer2 = new RTCPeerConnection(ref config);
  951. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  952. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  953. var obj = new GameObject("audio");
  954. var source = obj.AddComponent<AudioSource>();
  955. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  956. AudioStreamTrack track = new AudioStreamTrack(source);
  957. peer1.AddTrack(track);
  958. yield return SignalingOffer(peer1, peer2);
  959. bool isInvokeOnNegotiationNeeded1 = false;
  960. bool isInvokeOnNegotiationNeeded2 = false;
  961. peer1.OnNegotiationNeeded = () => isInvokeOnNegotiationNeeded1 = true;
  962. peer2.OnNegotiationNeeded = () => isInvokeOnNegotiationNeeded2 = true;
  963. peer1.RestartIce();
  964. var op9 = new WaitUntilWithTimeout(() => isInvokeOnNegotiationNeeded1, 5000);
  965. yield return op9;
  966. Assert.That(op9.IsCompleted, Is.True);
  967. peer2.RestartIce();
  968. var op10 = new WaitUntilWithTimeout(() => isInvokeOnNegotiationNeeded2, 5000);
  969. yield return op10;
  970. Assert.That(op10.IsCompleted, Is.True);
  971. track.Dispose();
  972. peer1.Close();
  973. peer2.Close();
  974. Object.DestroyImmediate(source.clip);
  975. Object.DestroyImmediate(obj);
  976. }
  977. [UnityTest]
  978. [Timeout(5000)]
  979. [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })]
  980. public IEnumerator RemoteOnRemoveTrack()
  981. {
  982. RTCConfiguration config = default;
  983. config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } };
  984. var peer1 = new RTCPeerConnection(ref config);
  985. var peer2 = new RTCPeerConnection(ref config);
  986. peer1.OnIceCandidate = candidate => { peer2.AddIceCandidate(candidate); };
  987. peer2.OnIceCandidate = candidate => { peer1.AddIceCandidate(candidate); };
  988. var stream = new MediaStream();
  989. MediaStream receiveStream = null;
  990. var obj = new GameObject("audio");
  991. var source = obj.AddComponent<AudioSource>();
  992. source.clip = AudioClip.Create("test", 480, 2, 48000, false);
  993. var track = new AudioStreamTrack(source);
  994. stream.AddTrack(track);
  995. RTCRtpSender sender = peer1.AddTrack(track, stream);
  996. bool isInvokeNegotiationNeeded1 = false;
  997. peer1.OnNegotiationNeeded = () => isInvokeNegotiationNeeded1 = true;
  998. bool isInvokeOnRemoveTrack = false;
  999. peer2.OnTrack = e =>
  1000. {
  1001. Assert.That(e.Streams, Has.Count.EqualTo(1));
  1002. receiveStream = e.Streams.First();
  1003. receiveStream.OnRemoveTrack = ev => isInvokeOnRemoveTrack = true;
  1004. };
  1005. yield return SignalingOffer(peer1, peer2);
  1006. peer1.RemoveTrack(sender);
  1007. var op9 = new WaitUntilWithTimeout(() => isInvokeNegotiationNeeded1, 5000);
  1008. yield return op9;
  1009. Assert.That(op9.IsCompleted, Is.True);
  1010. yield return SignalingOffer(peer1, peer2);
  1011. var op10 = new WaitUntilWithTimeout(() => isInvokeOnRemoveTrack, 5000);
  1012. yield return op10;
  1013. Assert.That(op10.IsCompleted, Is.True);
  1014. stream.Dispose();
  1015. receiveStream.Dispose();
  1016. track.Dispose();
  1017. peer1.Dispose();
  1018. peer2.Dispose();
  1019. Object.DestroyImmediate(source.clip);
  1020. Object.DestroyImmediate(obj);
  1021. }
  1022. private IEnumerator SignalingOffer(RTCPeerConnection @from, RTCPeerConnection to)
  1023. {
  1024. var op1 = @from.CreateOffer();
  1025. yield return op1;
  1026. var desc = op1.Desc;
  1027. var op2 = @from.SetLocalDescription(ref desc);
  1028. yield return op2;
  1029. var op3 = to.SetRemoteDescription(ref desc);
  1030. yield return op3;
  1031. var op4 = to.CreateAnswer();
  1032. yield return op4;
  1033. desc = op4.Desc;
  1034. var op5 = to.SetLocalDescription(ref desc);
  1035. yield return op5;
  1036. var op6 = @from.SetRemoteDescription(ref desc);
  1037. yield return op6;
  1038. var op7 = new WaitUntilWithTimeout(
  1039. () => @from.IceConnectionState == RTCIceConnectionState.Connected ||
  1040. @from.IceConnectionState == RTCIceConnectionState.Completed, 5000);
  1041. yield return op7;
  1042. Assert.That(op7.IsCompleted, Is.True);
  1043. var op8 = new WaitUntilWithTimeout(
  1044. () => to.IceConnectionState == RTCIceConnectionState.Connected ||
  1045. to.IceConnectionState == RTCIceConnectionState.Completed, 5000);
  1046. yield return op8;
  1047. Assert.That(op8.IsCompleted, Is.True);
  1048. }
  1049. }
  1050. }