From 05d2edd5cd045a5bb52117df2ff69e04a79b761f Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 1 Nov 2022 10:53:57 +0900 Subject: [PATCH 001/117] fix: Fixed missing files to make the packing process for webapp (#801) --- WebApp/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WebApp/package.json b/WebApp/package.json index ee2733449..b0a9059ec 100644 --- a/WebApp/package.json +++ b/WebApp/package.json @@ -43,7 +43,8 @@ }, "pkg": { "assets": [ - "client/public/**/*" + "client/public/**/*", + "client/src/**/*" ], "targets": [ "node10" From afeac3f58da6da83e3aa8d767e4d2850a9fb11a6 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 1 Nov 2022 10:56:26 +0900 Subject: [PATCH 002/117] test: Add test cases to increase test coverage for public APIs (#798) * wip * move WebBrowserInputChannelReceiver * add test for InputSender and InputReceiver * move namespace * wip * fix * fix test * wip * addtests * revert * fix test error * ignore test --- WebApp/src/class/httphandler.ts | 8 +- .../Runtime/Scripts/InputReceiver.cs | 18 +++ .../Scripts/Signaling/HttpSignaling.cs | 79 ------------ .../WebBrowserInputChannelReceiver.meta | 8 ++ .../RemoteInput.cs | 2 +- .../RemoteInput.cs.meta | 0 ...derStreaming.Sample.WebBrowserInput.asmdef | 25 ++++ ...reaming.Sample.WebBrowserInput.asmdef.meta | 7 ++ .../WebBrowserInputChannelReceiver.cs | 4 +- .../WebBrowserInputChannelReceiver.cs.meta | 0 .../Tests/Runtime/ArrayHelperTest.cs | 91 ++++++++++++++ ...utTest.cs.meta => ArrayHelperTest.cs.meta} | 2 +- .../Tests/Runtime/Attribute.cs | 10 ++ .../Tests/Runtime/Attribute.cs.meta | 11 ++ .../Runtime/InputPositionCorrectorTest.cs | 33 +++++ .../InputPositionCorrectorTest.cs.meta | 11 ++ .../Runtime/InputSystem/InputRemotingTest.cs | 32 ++++- .../Tests/Runtime/PeerConnectionTest.cs | 4 +- .../Tests/Runtime/PrivateSignalingTest.cs | 24 ++-- .../Tests/Runtime/RemoteInputTest.cs | 44 ------- .../Runtime/RenderStreamingInternalTest.cs | 6 +- .../Tests/Runtime/RenderStreamingTest.cs | 12 ++ .../Tests/Runtime/SignalingHandlerTest.cs | 67 ++++++++-- .../Tests/Runtime/StreamingComponentTest.cs | 119 +++++++++++++++--- .../Unity.RenderStreaming.RuntimeTests.asmdef | 2 +- 25 files changed, 445 insertions(+), 174 deletions(-) create mode 100644 com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver.meta rename com.unity.renderstreaming/{Runtime/Scripts => Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver}/RemoteInput.cs (99%) rename com.unity.renderstreaming/{Runtime/Scripts => Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver}/RemoteInput.cs.meta (100%) create mode 100644 com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef create mode 100644 com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef.meta rename com.unity.renderstreaming/{Runtime/Scripts => Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver}/WebBrowserInputChannelReceiver.cs (96%) rename com.unity.renderstreaming/{Runtime/Scripts => Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver}/WebBrowserInputChannelReceiver.cs.meta (100%) create mode 100644 com.unity.renderstreaming/Tests/Runtime/ArrayHelperTest.cs rename com.unity.renderstreaming/Tests/Runtime/{RemoteInputTest.cs.meta => ArrayHelperTest.cs.meta} (83%) create mode 100644 com.unity.renderstreaming/Tests/Runtime/Attribute.cs create mode 100644 com.unity.renderstreaming/Tests/Runtime/Attribute.cs.meta create mode 100644 com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs create mode 100644 com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs.meta delete mode 100644 com.unity.renderstreaming/Tests/Runtime/RemoteInputTest.cs diff --git a/WebApp/src/class/httphandler.ts b/WebApp/src/class/httphandler.ts index f5c63243d..2b1e33cbf 100644 --- a/WebApp/src/class/httphandler.ts +++ b/WebApp/src/class/httphandler.ts @@ -105,8 +105,10 @@ function _deleteConnection(sessionId:string, connectionId:string) { } function _deleteSession(sessionId: string) { - for(const connectionId of Array.from(clients.get(sessionId))) { - _deleteConnection(sessionId, connectionId); + if(clients.has(sessionId)) { + for(const connectionId of Array.from(clients.get(sessionId))) { + _deleteConnection(sessionId, connectionId); + } } offers.delete(sessionId); answers.delete(sessionId); @@ -132,7 +134,7 @@ function _checkDeletedSession(sessionId: string): void { } } -function _getConnection(sessionId: string): string[] { +function _getConnection(sessionId: string): string[] { _checkDeletedSession(sessionId); return Array.from(clients.get(sessionId)); } diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs index f612622df..776e59a7f 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs @@ -101,6 +101,24 @@ public string defaultActionMap set => m_DefaultActionMap = value; } + /// + /// + /// + public ReadOnlyArray actionEvents + { + get => m_ActionEvents; + set + { + if (m_Enabled) + UninitializeActions(); + + m_ActionEvents = value.ToArray(); + + if (m_Enabled) + InitializeActions(); + } + } + /// /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs index bba7b9bcc..472df9f5a 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs @@ -397,84 +397,5 @@ private bool HTTPGetAll() } return true; } - - - private bool HTTPGetOffers() - { - HttpWebRequest request = - (HttpWebRequest)WebRequest.Create($"{m_url}/signaling/offer?fromtime={m_lastTimeGetOfferRequest}"); - request.Method = "GET"; - request.ContentType = "application/json"; - request.Headers.Add("Session-Id", m_sessionId); - request.KeepAlive = false; - - HttpWebResponse response = HTTPGetResponse(request); - OfferResDataList list = HTTPParseJsonResponse(response); - - if (list == null) return false; - - m_lastTimeGetOfferRequest = DateTimeExtension.ParseHttpDate(response.Headers[HttpResponseHeader.Date]) - .ToJsMilliseconds(); - - foreach (var offer in list.offers) - { - m_mainThreadContext.Post(d => OnOffer?.Invoke(this, offer), null); - } - - return true; - } - - private bool HTTPGetAnswers() - { - HttpWebRequest request = - (HttpWebRequest)WebRequest.Create($"{m_url}/signaling/answer?fromtime={m_lastTimeGetAnswerRequest}"); - request.Method = "GET"; - request.ContentType = "application/json"; - request.Headers.Add("Session-Id", m_sessionId); - request.KeepAlive = false; - - HttpWebResponse response = HTTPGetResponse(request); - AnswerResDataList list = HTTPParseJsonResponse(response); - - if (list == null) return false; - - m_lastTimeGetAnswerRequest = DateTimeExtension.ParseHttpDate(response.Headers[HttpResponseHeader.Date]) - .ToJsMilliseconds(); - - foreach (var answer in list.answers) - { - m_mainThreadContext.Post(d => OnAnswer?.Invoke(this, answer), null); - } - - return true; - } - - private bool HTTPGetCandidates() - { - HttpWebRequest request = - (HttpWebRequest)WebRequest.Create($"{m_url}/signaling/candidate?fromtime={m_lastTimeGetCandidateRequest}"); - request.Method = "GET"; - request.ContentType = "application/json"; - request.Headers.Add("Session-Id", m_sessionId); - request.KeepAlive = false; - - HttpWebResponse response = HTTPGetResponse(request); - CandidateContainerResDataList containers = HTTPParseJsonResponse(response); - - if (containers == null) return false; - m_lastTimeGetCandidateRequest = DateTimeExtension.ParseHttpDate(response.Headers[HttpResponseHeader.Date]) - .ToJsMilliseconds(); - - foreach (var candidateContainer in containers.candidates) - { - foreach (var candidate in candidateContainer.candidates) - { - candidate.connectionId = candidateContainer.connectionId; - m_mainThreadContext.Post(d => OnIceCandidate?.Invoke(this, candidate), null); - } - } - - return true; - } } } diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver.meta b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver.meta new file mode 100644 index 000000000..49824b896 --- /dev/null +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 004f7500100b6c64382185f2af5d6b5a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Runtime/Scripts/RemoteInput.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs similarity index 99% rename from com.unity.renderstreaming/Runtime/Scripts/RemoteInput.cs rename to com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs index 3c081e6c2..819e57c1a 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RemoteInput.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Linq; -namespace Unity.RenderStreaming +namespace Unity.RenderStreaming.Samples { using UnityInputSystem = UnityEngine.InputSystem.InputSystem; diff --git a/com.unity.renderstreaming/Runtime/Scripts/RemoteInput.cs.meta b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs.meta similarity index 100% rename from com.unity.renderstreaming/Runtime/Scripts/RemoteInput.cs.meta rename to com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs.meta diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef new file mode 100644 index 000000000..dfb83fc11 --- /dev/null +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef @@ -0,0 +1,25 @@ +{ + "name": "Unity.RenderStreaming.Sample.WebBrowserInput", + "rootNamespace": "", + "references": [ + "GUID:40a5acf76f04c4c8ebb69605e4b0d5c7", + "GUID:f12aafacab75a87499e7e45c873ffab8", + "GUID:75469ad4d38634e559750d17036d5f7c" + ], + "includePlatforms": [ + "Android", + "Editor", + "iOS", + "LinuxStandalone64", + "macOSStandalone", + "WindowsStandalone64" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef.meta b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef.meta new file mode 100644 index 000000000..2718f571a --- /dev/null +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/Unity.RenderStreaming.Sample.WebBrowserInput.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 87691ed9decf63846bb1eb6a338b76b8 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Runtime/Scripts/WebBrowserInputChannelReceiver.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs similarity index 96% rename from com.unity.renderstreaming/Runtime/Scripts/WebBrowserInputChannelReceiver.cs rename to com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs index ef3acc9d4..9e6b72919 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/WebBrowserInputChannelReceiver.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs @@ -3,9 +3,7 @@ using UnityEngine; using UnityEngine.InputSystem; -////TODO: Move namespace to Unity.RenderStreaming.Samples - -namespace Unity.RenderStreaming +namespace Unity.RenderStreaming.Samples { /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/WebBrowserInputChannelReceiver.cs.meta b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs.meta similarity index 100% rename from com.unity.renderstreaming/Runtime/Scripts/WebBrowserInputChannelReceiver.cs.meta rename to com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs.meta diff --git a/com.unity.renderstreaming/Tests/Runtime/ArrayHelperTest.cs b/com.unity.renderstreaming/Tests/Runtime/ArrayHelperTest.cs new file mode 100644 index 000000000..07035f647 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/ArrayHelperTest.cs @@ -0,0 +1,91 @@ +using NUnit.Framework; + +namespace Unity.RenderStreaming.RuntimeTest +{ + + class ArrayHelpersTest + { + [Test] + public void LengthSafe() + { + var array = new int[] { 1, 2, 3 }; + Assert.That(ArrayHelpers.LengthSafe(array), Is.EqualTo(3)); + + array = null; + Assert.That(ArrayHelpers.LengthSafe(array), Is.EqualTo(0)); + } + + [Test] + public void Append() + { + var array = new int[] { 1, 2, 3 }; + ArrayHelpers.Append(ref array, 4); + Assert.That(array, Is.EqualTo(new int[] { 1, 2, 3, 4 })); + + array = null; + ArrayHelpers.Append(ref array, 1); + Assert.That(array, Is.EqualTo(new int[] { 1 })); + + } + + [Test] + public void AppendArray() + { + var array = new int[] { 1, 2, 3 }; + var array2 = new int[] { 4, 5 }; + ArrayHelpers.Append(ref array, array2); + Assert.That(array, Is.EqualTo(new int[] { 1, 2, 3, 4, 5 })); + + array = null; + ArrayHelpers.Append(ref array, array2); + Assert.That(array, Is.EqualTo(new int[] { 4, 5 })); + } + + [Test] + public void IndexOf() + { + var array = new int[] { 1, 2, 3 }; + Assert.That(ArrayHelpers.IndexOf(array, 2), Is.EqualTo(1)); + Assert.That(ArrayHelpers.IndexOf(array, 4), Is.EqualTo(-1)); + + array = null; + Assert.That(ArrayHelpers.IndexOf(array, 2), Is.EqualTo(-1)); + } + + [Test] + public void Erase() + { + var array = new int[] { 1, 2, 3 }; + Assert.That(ArrayHelpers.Erase(ref array, 2), Is.True); + Assert.That(array, Is.EqualTo(new int[] { 1, 3 })); + + Assert.That(ArrayHelpers.Erase(ref array, 2), Is.False); + Assert.That(array, Is.EqualTo(new int[] { 1, 3 })); + + array = null; + Assert.That(ArrayHelpers.Erase(ref array, 2), Is.False); + Assert.That(array, Is.Null); + } + + + [Test] + public void EraseAt() + { + var array = new int[] { 1, 2, 3 }; + ArrayHelpers.EraseAt(ref array, 1); + Assert.That(array, Is.EqualTo(new int[] { 1, 3 })); + + array = new int[] { 1 }; + ArrayHelpers.EraseAt(ref array, 0); + Assert.That(array, Is.Null); + } + + [Test] + public void PutAtIfNotSet() + { + var array = new int[] { 1, 2, 3 }; + ArrayHelpers.PutAtIfNotSet(ref array, 3, () => { return 4; }); + Assert.That(array, Is.EqualTo(new int[] { 1, 2, 3, 4 })); + } + } +} diff --git a/com.unity.renderstreaming/Tests/Runtime/RemoteInputTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/ArrayHelperTest.cs.meta similarity index 83% rename from com.unity.renderstreaming/Tests/Runtime/RemoteInputTest.cs.meta rename to com.unity.renderstreaming/Tests/Runtime/ArrayHelperTest.cs.meta index 23b80bdaf..5fba5724e 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RemoteInputTest.cs.meta +++ b/com.unity.renderstreaming/Tests/Runtime/ArrayHelperTest.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 52873f48c93685048951dc1da5c81da9 +guid: 12b8da0816272304f9e0df078d160877 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.renderstreaming/Tests/Runtime/Attribute.cs b/com.unity.renderstreaming/Tests/Runtime/Attribute.cs new file mode 100644 index 000000000..9f5b3f9e3 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/Attribute.cs @@ -0,0 +1,10 @@ +using System; +using NUnit.Framework; + +namespace Unity.RenderStreaming.RuntimeTest +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + internal class LongRunningAttribute : CategoryAttribute + { + } +} diff --git a/com.unity.renderstreaming/Tests/Runtime/Attribute.cs.meta b/com.unity.renderstreaming/Tests/Runtime/Attribute.cs.meta new file mode 100644 index 000000000..b985c4e5c --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/Attribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7f1ba5e8f1d8f954e83f4e56f8d59467 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs b/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs new file mode 100644 index 000000000..ff7d99067 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs @@ -0,0 +1,33 @@ +using System.Linq; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; + +namespace Unity.RenderStreaming.RuntimeTest +{ + + class InputPositionCorrectorTest + { + void OnEvent(InputEventPtr ptr, InputDevice device) + { + } + + [Test] + public void Invoke() + { + System.Action< InputEventPtr, InputDevice> onEvent = OnEvent; + var corrector = new InputPositionCorrector(onEvent); + Assert.That(corrector.inputRegion, Is.EqualTo(Rect.zero)); + Assert.That(corrector.outputRegion, Is.EqualTo(Rect.zero)); + + var device = UnityEngine.InputSystem.InputSystem.devices.First(_ => _ is Pointer); + var inputEvent = new InputEvent(); + unsafe + { + var ptr = InputEventPtr.From(&inputEvent); + corrector.Invoke(ptr, device); + } + } + } +} diff --git a/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs.meta new file mode 100644 index 000000000..af67964db --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 968452e4b48884a4d8f3c9374ad112da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs index 2bffbbd2f..62ca1ce49 100644 --- a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs @@ -21,7 +21,7 @@ public void Serialize() { participantId = 1, type = InputRemoting.MessageType.NewEvents, - data = new byte[] {1, 2, 3, 4, 5}, + data = new byte[] { 1, 2, 3, 4, 5 }, }; var bytes = MessageSerializer.Serialize(ref message1); @@ -177,9 +177,10 @@ public void Receiver() receiverDisposer.Dispose(); } - /// todo(kazuki): This test is failed for timeout on macOS + /// todo(kazuki): This test is failed for timeout on macOS. + /// todo(kazuki): This test is failed for timeout on iPhonePlayer. [UnityTest, Timeout(3000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.OSXPlayer, RuntimePlatform.IPhonePlayer })] public IEnumerator AddDevice() { var sender = new Sender(); @@ -192,13 +193,15 @@ public IEnumerator AddDevice() InputDevice device = null; InputDeviceChange change = default; - receiver.onDeviceChange += (_device, _change) => { + receiver.onDeviceChange += (_device, _change) => + { device = _device; change = _change; }; string layoutName = null; InputControlLayoutChange layoutChange = default; - receiver.onLayoutChange += (_name, _change) => { + receiver.onLayoutChange += (_name, _change) => + { layoutName = _name; layoutChange = _change; }; @@ -226,5 +229,24 @@ public IEnumerator AddDevice() sender.Dispose(); receiver.Dispose(); } + + [Test] + public void RemoveRemoteDevices() + { + var sender = new Sender(); + var senderInput = new InputRemoting(sender); + senderInput.RemoveRemoteDevices(0); + sender.Dispose(); + } + + [Test] + public void SaveState() + { + var sender = new Sender(); + var inputRemoting = new InputRemoting(sender); + var state = inputRemoting.SaveState(); + inputRemoting.RestoreState(state, sender); + sender.Dispose(); + } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/PeerConnectionTest.cs b/com.unity.renderstreaming/Tests/Runtime/PeerConnectionTest.cs index a1a6ae201..2bebf2b88 100644 --- a/com.unity.renderstreaming/Tests/Runtime/PeerConnectionTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/PeerConnectionTest.cs @@ -140,7 +140,7 @@ public IEnumerator CalledSendOfferWhenCreateChannel() peer.Dispose(); } - [UnityTest, Timeout(5000)] + [UnityTest, Timeout(5000), LongRunning] public IEnumerator CalledSendOfferTwiceIfGetAnswerNotYet() { var peer = new PeerConnection(true, config, ResendOfferInterval, @@ -848,7 +848,7 @@ public IEnumerator OnGotOfferDescriptionAfterSendOfferImmediatelyInImPolite() peer2.Dispose(); } - [UnityTest, Timeout(5000)] + [UnityTest, Timeout(5000), LongRunning] [TestCase(true, ExpectedResult = null)] [TestCase(false, ExpectedResult = null)] public IEnumerator OnGotAnswerDescriptionAfterSendOfferImmediately(bool polite) diff --git a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs index 572f5f857..4d004b391 100644 --- a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs @@ -82,7 +82,11 @@ public void OneTimeSetUp() Assert.IsTrue(System.IO.File.Exists(fileName), $"webapp file not found in {fileName}"); ProcessStartInfo startInfo = new ProcessStartInfo { - WindowStyle = ProcessWindowStyle.Hidden, FileName = fileName, UseShellExecute = false + WindowStyle = ProcessWindowStyle.Hidden, + FileName = fileName, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true }; string arguments = $"-m private -p {TestUtility.PortNumber}"; @@ -99,7 +103,13 @@ public void OneTimeSetUp() { Debug.Log(e.Data); }; + m_ServerProcess.ErrorDataReceived += (sender, e) => + { + Debug.Log(e.Data); + }; bool success = m_ServerProcess.Start(); + m_ServerProcess.BeginErrorReadLine(); + m_ServerProcess.BeginOutputReadLine(); Assert.True(success); Thread.Sleep(1000); } @@ -238,9 +248,6 @@ public IEnumerator OnConnect() Assert.That(receivePolite1, Is.False); Assert.That(receivePolite2, Is.True); - // wait first connection list on http - yield return new WaitForSeconds(1); - signaling1.CloseConnection(receiveConnectionId1); yield return new WaitUntil(() => raiseOnDestroy1 && raiseOnDestroy2); @@ -250,7 +257,6 @@ public IEnumerator OnConnect() signaling2.CloseConnection(receiveConnectionId2); signaling1.Stop(); signaling2.Stop(); - yield return new WaitForSeconds(1); } @@ -285,7 +291,7 @@ public IEnumerator OnOffer() signaling2.OnOffer += (s, e) => { offerRaised2 = true; }; signaling1.SendOffer(connectionId, m_DescOffer); - yield return new WaitForSeconds(3); + // Do not receive offer other signaling if not connected same sendoffer connectionId in private mode Assert.IsFalse(offerRaised2); @@ -315,7 +321,6 @@ public IEnumerator OnOffer() signaling2.CloseConnection(connectionId2); signaling1.Stop(); signaling2.Stop(); - yield return new WaitForSeconds(1); } [UnityTest, Timeout(10000)] @@ -379,7 +384,6 @@ public IEnumerator OnAnswer() signaling2.CloseConnection(connectionId2); signaling1.Stop(); signaling2.Stop(); - yield return new WaitForSeconds(1); } @@ -454,10 +458,9 @@ public IEnumerator OnCandidate() signaling2.CloseConnection(connectionId2); signaling1.Stop(); signaling2.Stop(); - yield return new WaitForSeconds(1); } - [UnityTest, Timeout(10000)] + [UnityTest, Timeout(10000), LongRunning] public IEnumerator NotReceiveOwnOfferAnswer() { bool startRaised1 = false; @@ -538,7 +541,6 @@ public IEnumerator NotReceiveOwnOfferAnswer() signaling2.CloseConnection(connectionId2); signaling1.Stop(); signaling2.Stop(); - yield return new WaitForSeconds(1); } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/RemoteInputTest.cs b/com.unity.renderstreaming/Tests/Runtime/RemoteInputTest.cs deleted file mode 100644 index 51385a157..000000000 --- a/com.unity.renderstreaming/Tests/Runtime/RemoteInputTest.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.RenderStreaming.RuntimeTest -{ - class RemoteInputTest - { - [Test] - public void RemoteInputReceiverCreate() - { - RemoteInput input = RemoteInputReceiver.Create(); - Assert.NotNull(input); - } - - [Test] - public void RemoteInputReceiverAll() - { - Assert.AreEqual(RemoteInputReceiver.All().Count, 0); - RemoteInput input = RemoteInputReceiver.Create(); - Assert.AreEqual(RemoteInputReceiver.All().Count, 1); - input.Dispose(); - Assert.AreEqual(RemoteInputReceiver.All().Count, 0); - } - - [Test] - public void RemoteInputProperties() - { - RemoteInput input = RemoteInputReceiver.Create(); - Assert.NotNull(input.RemoteMouse); - Assert.NotNull(input.RemoteGamepad); - Assert.NotNull(input.RemoteKeyboard); - Assert.NotNull(input.RemoteTouchscreen); - input.Dispose(); - } - - [Test] - public void RemoteInputProcessInput() - { - RemoteInput input = RemoteInputReceiver.Create(); - Assert.Throws(() => input.ProcessInput(new byte[0])); - input.Dispose(); - } - } -} diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs index bf8cc18a5..4749c9e7a 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs @@ -539,7 +539,7 @@ public IEnumerator OnAddChannelPrivateMode() target2.Dispose(); } - [UnityTest, Timeout(10000)] + [UnityTest, Timeout(10000), LongRunning] public IEnumerator SendOfferThrowExceptionPrivateMode() { MockSignaling.Reset(true); @@ -605,7 +605,7 @@ public IEnumerator SendOfferThrowExceptionPrivateMode() target2.Dispose(); } - [UnityTest, Timeout(30000)] + [UnityTest, Timeout(30000), LongRunning] public IEnumerator SwapTransceiverPrivateMode() { MockSignaling.Reset(true); @@ -688,7 +688,7 @@ public IEnumerator SwapTransceiverPrivateMode() [TestCase(TestMode.PublicMode, ExpectedResult = null)] [TestCase(TestMode.PrivateMode, ExpectedResult = null)] - [UnityTest, Timeout(30000)] + [UnityTest, Timeout(30000), LongRunning] public IEnumerator ResendOfferUntilGotAnswer(TestMode mode) { MockSignaling.Reset(mode == TestMode.PrivateMode); diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs index 020f3bdff..47de504cb 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs @@ -42,6 +42,18 @@ public void Run() component.Run(signaling: mock, handlers: handlers); } + [Test, Ignore("Failed this test on macOS and Linux platform because of the signaling process.")] + public void RunDefault() + { + var handler = component.gameObject.AddComponent(); + var handlers = new SignalingHandlerBase[] { handler }; + ISignaling mock = new MockSignaling(); + component.runOnAwake = false; + component.gameObject.SetActive(true); + component.Run(handlers:handlers); + } + + [Test] public void ThrowExceptionIfHandlerIsNullOrEmpty() { diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs index 2b0fa461a..fd103b7ad 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs @@ -222,14 +222,14 @@ public IEnumerator ReceiveStream() container2.Dispose(); } - //todo:: crash in dispose process on standalone linux + // todo:: Crash in dispose process on Linux standalone. + // todo:: Timeout error on iPhonePlayer. [UnityTest, Timeout(10000)] [UnityPlatform(exclude = new[] { RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor, - RuntimePlatform.LinuxPlayer + RuntimePlatform.LinuxPlayer, RuntimePlatform.IPhonePlayer })] - public IEnumerator SetCodec() { string connectionId = "12345"; @@ -316,7 +316,39 @@ public void SetUp() //todo:: crash in dispose process on standalone Linux and Android [UnityTest, Timeout(10000)] [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.Android })] - public IEnumerator AddStreamSource() + public IEnumerator AddAudioStreamSource() + { + string connectionId = "12345"; + var container = TestContainer.Create("test"); + var streamer = container.test.gameObject.AddComponent(); + + Assert.That(streamer.Track, Is.Null); + Assert.That(streamer.Transceivers, Is.Empty); + + container.test.component.AddComponent(streamer); + container.test.component.CreateConnection(connectionId); + yield return new WaitUntil(() => container.test.component.ExistConnection(connectionId)); + + Assert.That(streamer.Track, Is.Not.Null); + Assert.That(streamer.Transceivers, Is.Not.Empty); + + // SetCodec + streamer.SetCodec(null); + + // SetBitrate + var maxBitrate = streamer.maxBitrate; + var minBitrate = streamer.maxBitrate; + streamer.SetBitrate(minBitrate, maxBitrate); + + container.test.component.DeleteConnection(connectionId); + yield return new WaitUntil(() => !container.test.component.ExistConnection(connectionId)); + container.Dispose(); + } + + //todo:: crash in dispose process on standalone Linux and Android + [UnityTest, Timeout(10000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.Android })] + public IEnumerator AddVideoStreamSource() { string connectionId = "12345"; var container = TestContainer.Create("test"); @@ -332,6 +364,27 @@ public IEnumerator AddStreamSource() Assert.That(streamer.Track, Is.Not.Null); Assert.That(streamer.Transceivers, Is.Not.Empty); + // SetCodec + streamer.SetCodec(null); + + // SetFramerate + var frameRate = streamer.frameRate; + streamer.SetFrameRate(frameRate); + + // SetBitrate + var maxBitrate = streamer.maxBitrate; + var minBitrate = streamer.maxBitrate; + streamer.SetBitrate(minBitrate, maxBitrate); + + // SetScaleResolutionDown + var scaleFactor = streamer.scaleResolutionDown; + streamer.SetScaleResolutionDown(scaleFactor); + + // SetTextureSize + var width = streamer.width; + var height = streamer.height; + streamer.SetTextureSize(new Vector2Int((int)width, (int)height)); + container.test.component.DeleteConnection(connectionId); yield return new WaitUntil(() => !container.test.component.ExistConnection(connectionId)); container.Dispose(); @@ -388,9 +441,10 @@ public IEnumerator AddSource() } - //todo:: crash in dispose process on standalone linux + // todo:: Crash in dispose process on Linux standalone + // todo:: Timeout error on iPhonePlayer [UnityTest, Timeout(10000)] - [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.IPhonePlayer })] public IEnumerator ReceiveStream() { string connectionId = "12345"; @@ -406,7 +460,6 @@ public IEnumerator ReceiveStream() container1.test.component.AddComponent(streamer); container1.test.component.CreateConnection(connectionId); yield return new WaitUntil(() => container1.test.component.ExistConnection(connectionId)); - yield return new WaitUntil(() => isStartedStream0); Assert.That(isStartedStream0, Is.True); diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index 908da143f..16a033e77 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -6,6 +6,8 @@ using UnityEngine; using Unity.WebRTC; using UnityEngine.TestTools; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Users; namespace Unity.RenderStreaming.RuntimeTest { @@ -72,7 +74,7 @@ public IEnumerator CreateTrack() // With camera sender.source = VideoStreamSource.Camera; - Assert.Throws(() => sender.CreateTrack()); + Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); var camera = go.AddComponent(); sender.sourceCamera = camera; @@ -100,18 +102,18 @@ public IEnumerator CreateTrack() // With Texture sender.source = VideoStreamSource.Texture; Assert.That(sender.sourceTexture, Is.Null); - Assert.Throws(() => sender.CreateTrack()); + Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); - var width = 256; - var height = 256; + var width = 640; + var height = 480; var format = WebRTC.WebRTC.GetSupportedRenderTextureFormat(SystemInfo.graphicsDeviceType); var texture = new RenderTexture(width, height, 0, format); sender.sourceTexture = texture; Assert.That(sender.sourceTexture, Is.Not.Null); Assert.That(sender.width, Is.EqualTo(width)); Assert.That(sender.height, Is.EqualTo(height)); - Assert.Throws(() => sender.width = 1280); - Assert.Throws(() => sender.height = 720); + Assert.That(() => sender.width = 1280, Throws.Exception.TypeOf()); + Assert.That(() => sender.height = 720, Throws.Exception.TypeOf()); op = sender.CreateTrack(); yield return op; track = op.Track; @@ -126,7 +128,7 @@ public IEnumerator CreateTrack() sender.source = VideoStreamSource.WebCamera; Assert.That(sender.sourceDeviceIndex, Is.EqualTo(0)); sender.sourceDeviceIndex = -1; - Assert.Throws(() => sender.CreateTrack()); + Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); sender.sourceDeviceIndex = 0; op = sender.CreateTrack(); yield return op; @@ -163,7 +165,7 @@ public void SetBitrate() Assert.That(sender.maxBitrate, Is.EqualTo(maxBitrate)); minBitrate = 3000; - Assert.Throws(() => sender.SetBitrate(minBitrate, maxBitrate)); + Assert.That(() => sender.SetBitrate(minBitrate, maxBitrate), Throws.Exception.TypeOf()); UnityEngine.Object.DestroyImmediate(go); } @@ -182,7 +184,7 @@ public void SetFrameRate() Assert.That(sender.frameRate, Is.EqualTo(framerate)); framerate = -1; - Assert.Throws(() => sender.SetFrameRate(framerate)); + Assert.That(() => sender.SetFrameRate(framerate), Throws.Exception.TypeOf()); UnityEngine.Object.DestroyImmediate(go); } @@ -202,10 +204,10 @@ public void SetScaleResolutionDown() Assert.That(sender.scaleResolutionDown, Is.EqualTo(scaleFactor)); scaleFactor = -1; - Assert.Throws(() => sender.SetScaleResolutionDown(scaleFactor)); + Assert.That(() => sender.SetScaleResolutionDown(scaleFactor), Throws.Exception.TypeOf()); scaleFactor = 0.5f; - Assert.Throws(() => sender.SetScaleResolutionDown(scaleFactor)); + Assert.That(() => sender.SetScaleResolutionDown(scaleFactor), Throws.Exception.TypeOf()); UnityEngine.Object.DestroyImmediate(go); } @@ -315,7 +317,7 @@ public IEnumerator CreateTrack() // With AudioListener sender.source = AudioStreamSource.AudioListener; - Assert.Throws(() => sender.CreateTrack()); + Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); var audioListener = go.AddComponent(); var op = sender.CreateTrack(); @@ -329,7 +331,7 @@ public IEnumerator CreateTrack() var go2 = new GameObject(); sender = go2.AddComponent(); sender.source = AudioStreamSource.AudioSource; - Assert.Throws(() => sender.CreateTrack()); + Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); var audioSource = go2.AddComponent(); sender.audioSource = audioSource; op = sender.CreateTrack(); @@ -356,6 +358,26 @@ public IEnumerator CreateTrack() UnityEngine.Object.DestroyImmediate(go2); } + [UnityTest] + public IEnumerator ReplaceTrack() + { + var go = new GameObject(); + var sender = go.AddComponent(); + + Assert.That(() => sender.ReplaceTrack(null), Throws.Exception.TypeOf()); + + // With AudioListener + sender.source = AudioStreamSource.AudioListener; + var audioListener = go.AddComponent(); + sender.audioListener = audioListener; + var op = sender.CreateTrack(); + yield return op; + var track = op.Track; + Assert.That(track, Is.Not.Null); + sender.ReplaceTrack(track); + UnityEngine.Object.DestroyImmediate(go); + } + [Test] public void SelectCodecCapabilities() { @@ -401,7 +423,7 @@ public void SetBitrate() Assert.That(sender.maxBitrate, Is.EqualTo(maxBitrate)); minBitrate = 3000; - Assert.Throws(() => sender.SetBitrate(minBitrate, maxBitrate)); + Assert.That(() => sender.SetBitrate(minBitrate, maxBitrate), Throws.Exception.TypeOf()); UnityEngine.Object.DestroyImmediate(go); } @@ -467,4 +489,73 @@ public void SetCodec() Assert.That(receiver.codec, Is.Null); } } + + class InputSenderTest + { + [Test] + public void SetChannel() + { + var go = new GameObject(); + var sender = go.AddComponent(); + Assert.That(sender.Channel, Is.Null); + Assert.That(() => sender.SetChannel(null), Throws.Exception.TypeOf()); + + sender.enabled = false; + sender.enabled = true; + + sender.SetChannel(null, null); + + var connection = new RTCPeerConnection(); + var channel = connection.CreateDataChannel("test"); + sender.SetChannel(null, channel); + + UnityEngine.Object.DestroyImmediate(go); + channel.Dispose(); + connection.Dispose(); + } + } + + class InputReceiverTest + { + [Test] + public void SetChannel() + { + var go = new GameObject(); + go.SetActive(false); + var receiver = go.AddComponent(); + var asset = ScriptableObject.CreateInstance(); + var mapName = "test"; + asset.AddActionMap(mapName); + receiver.actions = asset; + go.SetActive(true); + + Assert.That(receiver.Channel, Is.Null); + Assert.That(() => receiver.SetChannel(null), Throws.Exception.TypeOf()); + + receiver.enabled = false; + receiver.enabled = true; + + Assert.That(receiver.inputIsActive, Is.True); + Assert.That(receiver.user.id, Is.Not.EqualTo(InputUser.InvalidId)); + Assert.That(receiver.devices, Is.Empty); + Assert.That(receiver.defaultActionMap, Is.Null); + + Assert.That(receiver.currentActionMap, Is.Null); + receiver.currentActionMap = new InputActionMap(); + Assert.That(receiver.actionEvents, Is.Not.Null); + receiver.actionEvents = new PlayerInput.ActionEvent[]{}; + + receiver.SwitchCurrentActionMap(mapName); + + var device = UnityEngine.InputSystem.InputSystem.devices.First(); + receiver.PerformPairingWithDevice(device); + receiver.PerformPairingWithAllLocalDevices(); + receiver.UnpairDevices(device); + + receiver.SetChannel(null, null); + + UnityEngine.Object.DestroyImmediate(asset); + UnityEngine.Object.DestroyImmediate(go); + } + } } diff --git a/com.unity.renderstreaming/Tests/Runtime/Unity.RenderStreaming.RuntimeTests.asmdef b/com.unity.renderstreaming/Tests/Runtime/Unity.RenderStreaming.RuntimeTests.asmdef index ff40ccbda..627820c60 100644 --- a/com.unity.renderstreaming/Tests/Runtime/Unity.RenderStreaming.RuntimeTests.asmdef +++ b/com.unity.renderstreaming/Tests/Runtime/Unity.RenderStreaming.RuntimeTests.asmdef @@ -19,7 +19,7 @@ "WindowsStandalone64" ], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": true, "precompiledReferences": [ "nunit.framework.dll" From 53ff13db460303c4af7371b717a0ba5d2d4e36a0 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 1 Nov 2022 17:33:38 +0900 Subject: [PATCH 003/117] fix: Recalculate input region to correct pointer position from mouse/touchscreen (#800) --- WebApp/client/src/inputdevice.js | 240 ++++++++++-------- WebApp/client/src/sender.js | 32 +-- .../Runtime/Scripts/InputReceiver.cs | 4 +- .../Runtime/Scripts/InputSender.cs | 4 +- .../Runtime/Scripts/InputSystem/Receiver.cs | 2 +- .../Runtime/Scripts/InputSystem/Sender.cs | 2 +- .../Example/Broadcast/Broadcast.unity | 96 ++----- .../Example/Broadcast/BroadcastSample.cs | 58 ++++- .../Broadcast/CameraControl.inputactions | 115 ++++++++- .../Broadcast/SimpleCameraControllerV2.cs | 28 +- .../Example/Broadcast/UIControllerV2.cs | 76 ++++++ .../UIControllerV2.cs.meta} | 0 .../Samples~/Example/Receiver/Receiver.unity | 86 ++++++- .../Example/Receiver/ReceiverSample.cs | 31 ++- .../UIController.cs | 0 .../WebBrowserInput/UIController.cs.meta | 11 + .../WebBrowserInput/WebBrowserInput.unity | 86 +++---- 17 files changed, 585 insertions(+), 286 deletions(-) create mode 100644 com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs rename com.unity.renderstreaming/Samples~/Example/{Scripts/UIController.cs.meta => Broadcast/UIControllerV2.cs.meta} (100%) rename com.unity.renderstreaming/Samples~/Example/{Scripts => WebBrowserInput}/UIController.cs (100%) create mode 100644 com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs.meta diff --git a/WebApp/client/src/inputdevice.js b/WebApp/client/src/inputdevice.js index 3733d6cc5..3a9dfd67e 100644 --- a/WebApp/client/src/inputdevice.js +++ b/WebApp/client/src/inputdevice.js @@ -1,4 +1,4 @@ -import { +import { MemoryHelper, } from "./memoryhelper.js"; @@ -14,16 +14,16 @@ export class FourCC { */ /** - * - * @param {String} a - * @param {String} b - * @param {String} c - * @param {String} d + * + * @param {String} a + * @param {String} b + * @param {String} c + * @param {String} d */ constructor(a, b, c, d) { - this._code = (a.charCodeAt() << 24) - | (b.charCodeAt() << 16) - | (c.charCodeAt() << 8) + this._code = (a.charCodeAt() << 24) + | (b.charCodeAt() << 16) + | (c.charCodeAt() << 8) | d.charCodeAt(); } @@ -39,23 +39,23 @@ export class FourCC { export class InputDevice { /** - * + * * name; * layout; * deviceId; * variants; * description; - * + * * _inputState; */ - + /** - * - * @param {Number} name - * @param {String} layout - * @param {Number} deviceId - * @param {String} variants - * @param {Object} description + * + * @param {Number} name + * @param {String} layout + * @param {Number} deviceId + * @param {String} variants + * @param {Object} description */ constructor(name, layout, deviceId, variants, description) { this.name = name; @@ -68,8 +68,8 @@ export class InputDevice { } /** - * - * @param {IInputState} state + * + * @param {IInputState} state */ updateState(state) { this._inputState = state; @@ -78,7 +78,7 @@ export class InputDevice { queueEvent(event) { throw new Error(`Please implement this method. event:${event}`); } - + /** * @returns {IInputState} */ @@ -89,7 +89,7 @@ export class InputDevice { export class Mouse extends InputDevice { /** - * @param {(MouseEvent|WheelEvent)} event + * @param {(MouseEvent|WheelEvent)} event */ queueEvent(event) { this.updateState(new MouseState(event)); @@ -99,8 +99,8 @@ export class Mouse extends InputDevice { export class Keyboard extends InputDevice { static get keycount() { return 110; } /** - * - * @param {KeyboardEvent} event + * + * @param {KeyboardEvent} event */ queueEvent(event) { this.updateState(new KeyboardState(event, this.currentState)); @@ -109,7 +109,7 @@ export class Keyboard extends InputDevice { export class Touchscreen extends InputDevice { /** - * @param {TouchScreenEvent} event + * @param {TouchScreenEvent} event */ queueEvent(event, time) { this.updateState(new TouchscreenState(event, this.currentState, time)); @@ -118,7 +118,7 @@ export class Touchscreen extends InputDevice { export class Gamepad extends InputDevice { /** - * @param {GamepadButtonEvent | GamepadAxisEvent} event + * @param {GamepadButtonEvent | GamepadAxisEvent} event */ queueEvent(event) { this.updateState(new GamepadState(event)); @@ -129,29 +129,29 @@ export class InputEvent { static get invalidEventId() { return 0; } static get size() { return 20; } - /** + /** * field offset 0 * @member {Number} type; - * + * * field offset 4 * @member {Number} sizeInBytes; - * + * * field offset 6 * @member {Number} deviceId; - * + * * field offset 8 * @member {Number} time; - * + * * field offset 16 * @member {Number} eventId; */ /** - * - * @param {Number} type + * + * @param {Number} type * @param {Number} sizeInBytes - * @param {Number} deviceId - * @param {Number} time + * @param {Number} deviceId + * @param {Number} time */ constructor(type, sizeInBytes, deviceId, time) { this.type = type; @@ -187,7 +187,7 @@ export class IInputState { * @returns {Number} */ get format() { - throw new Error('Please implement this field'); + throw new Error('Please implement this field'); } } @@ -198,29 +198,29 @@ export class MouseState extends IInputState { /** * field offset 0 * @member {Array} position; - * + * * field offset 8 * @member {Array} delta; - * + * * field offset 16 * @member {Array} scroll; - * + * * field offset 24 * @member {ArrayBuffer} buttons; - * + * * field offset 26 * @member {Array} displayIndex; - * + * * field offset 28 * @member {Array} clickCount; */ /** - * @param {MouseEvent | WheelEvent} event + * @param {MouseEvent | WheelEvent} event */ constructor(event) { super(); - + this.position = [event.clientX, event.clientY]; this.delta = [event.movementX, -event.movementY]; this.scroll = [0, 0]; @@ -281,7 +281,7 @@ export class KeyboardState extends IInputState { */ /** - * @param {KeyboardEvent} event + * @param {KeyboardEvent} event */ constructor(event, state) { super(); @@ -323,7 +323,7 @@ export class KeyboardState extends IInputState { export class TouchState { static get format() { return new FourCC('T', 'O', 'U', 'C').toInt32(); } static get size() { return 56; } - static incrementTouchId() { + static incrementTouchId() { if(TouchState._currentTouchId === undefined) { TouchState._currentTouchId = 0; } @@ -332,7 +332,7 @@ export class TouchState { static prevTouches() { if(TouchState._prevTouches === undefined) { // max touch count is 10 - TouchState._prevTouches = new Array(10); + TouchState._prevTouches = new Array(10); } return TouchState._prevTouches; } @@ -366,42 +366,24 @@ export class TouchState { /** - * @param {Touch} touch - * @param {TouchState} state - * @param {String} type + * @param {Touch} touchId + * @param {TouchState} prevState + * @param {Number[]} position + * @param {Number} pressure + * @param {Number[]} radius + * @param {TouchPhase} phaseId + * @param {Number} time */ - constructor(touch, type, time) { - let phaseId = TouchPhase.Stationary; - switch(type) { - case 'touchstart': - phaseId = TouchPhase.Began; break; - case 'touchend': - phaseId = TouchPhase.Ended; break; - case 'touchmove': - phaseId = TouchPhase.Moved; break; - case 'touchcancel': - phaseId = TouchPhase.Canceled; break; - } - - let touchId = 0; - let state = null; - if(phaseId == TouchPhase.Began) { - touchId = TouchState.incrementTouchId(); - } - else { - state = TouchState.prevTouches[touch.identifier]; - touchId = state.touchId; - } - + constructor(touchId, prevState, position, pressure, radius, phaseId, time) { this.touchId = touchId; - this.position = [touch.pageX, -touch.pageY]; + this.position = position != null ? position.slice() : null; if(phaseId == TouchPhase.Moved) { - this.delta = [this.position[0] - state.position[0], this.position[1] - state.position[1]]; + this.delta = [this.position[0] - prevState.position[0], this.position[1] - prevState.position[1]]; } else { this.delta = [0, 0]; } - this.pressure = touch.force; - this.radius = [touch.radiusX, touch.radiusY]; + this.pressure = pressure; + this.radius = radius != null ? radius.slice(): null; this.phaseId = phaseId; this.tapCount = 0; this.displayIndex = 0; @@ -411,12 +393,27 @@ export class TouchState { this.startTime = time; this.startPosition = this.position.slice(); } else { - this.startTime = state.startTime; - this.startPosition = state.startPosition.slice(); + this.startTime = prevState != null ? prevState.startTime : null; + this.startPosition = prevState != null ? prevState.startPosition.slice() : null; } + } + - // cache state - TouchState.prevTouches[touch.identifier] = this; + copy() { + let state = new TouchState(); + state.touchId = this.touchId; + state.position = this.position.slice(); + state.delta = this.delta.slice(); + state.pressure = this.pressure; + state.radius = this.radius.slice(); + state.phaseId = this.phaseId; + state.tapCount = this.tapCount; + state.displayIndex = this.displayIndex; + state.flags = this.flags; + state.padding = this.padding; + state.startTime = this.startTime; + state.startPosition = this.startPosition.slice(); + return state; } /** @@ -451,12 +448,26 @@ export class TouchState { */ get format() { return TouchState.format; - } + } } export class TouchscreenState extends IInputState { - static get maxTouches() { return 10; } + static get maxTouches() { return 10; } static get format() { return new FourCC('T', 'S', 'C', 'R').toInt32(); } + static convertPhaseId(type) { + let phaseId = TouchPhase.Stationary; + switch(type) { + case 'touchstart': + phaseId = TouchPhase.Began; break; + case 'touchend': + phaseId = TouchPhase.Ended; break; + case 'touchmove': + phaseId = TouchPhase.Moved; break; + case 'touchcancel': + phaseId = TouchPhase.Canceled; break; + } + return phaseId; + } /** * @param {TouchEvent} event @@ -467,6 +478,7 @@ export class TouchscreenState extends IInputState { super(); switch(event.type) { + // `click` event is called when releasing mouse button or finger on screen. case 'click' : { this.touchData = new Array(state.touchData.length); for(let i = 0; i < state.touchData.length; i++) { @@ -482,7 +494,21 @@ export class TouchscreenState extends IInputState { let touches = event.changedTouches; this.touchData = new Array(touches.length); for(let i = 0; i < touches.length; i++) { - this.touchData[i] = new TouchState(touches[i], event.type, time); + const touch = touches[i]; + const position = [touch.clientX, touch.clientY]; + const phaseId = TouchscreenState.convertPhaseId(event.type); + const pressure = touch.force; + const radius = [touch.radiusX, touch.radiusY]; + + // `touchId` in InputSystem must be set uniquely. + // The numbers of `touch.identifier` in Web API are reused, so these are not unique. + const touchId = phaseId == TouchPhase.Began ? TouchState.incrementTouchId() : TouchState.prevTouches()[touch.identifier].touchId; + const prevState = phaseId != TouchPhase.Began ? TouchState.prevTouches()[touch.identifier] : null; + const touchData = new TouchState(touchId, prevState, position, pressure, radius, phaseId, time); + + // cache state + TouchState.prevTouches()[touch.identifier] = touchData; + this.touchData[i] = touchData; } break; } @@ -513,27 +539,27 @@ export class TouchscreenState extends IInputState { export class GamepadState extends IInputState { static get size() { return 28; } static get format() { return new FourCC('G', 'P', 'A', 'D').toInt32(); } - + /** * field offset 0 * @member buttons; - * + * * field offset 4 * @member leftStick; - * + * * field offset 12 * @member rightStick; - * + * * field offset 20 * @member leftTrigger; - * + * * field offset 24 * @member rightTrigger; */ /** - * - * @param {GamepadButtonEvent | GamepadAxisEvent} event + * + * @param {GamepadButtonEvent | GamepadAxisEvent} event */ constructor(event) { super(); @@ -545,7 +571,7 @@ export class GamepadState extends IInputState { this.rightStick = [ gamepad.axes[2], -gamepad.axes[3] ]; this.leftTrigger = buttons[6].value; this.rightTrigger = buttons[7].value; - + // see https://w3c.github.io/gamepad/#remapping MemoryHelper.writeSingleBit(this.buttons, GamepadButton.A, buttons[0].pressed); MemoryHelper.writeSingleBit(this.buttons, GamepadButton.B, buttons[1].pressed); @@ -562,7 +588,7 @@ export class GamepadState extends IInputState { MemoryHelper.writeSingleBit(this.buttons, GamepadButton.DpadUp, buttons[12].pressed); MemoryHelper.writeSingleBit(this.buttons, GamepadButton.DpadDown, buttons[13].pressed); MemoryHelper.writeSingleBit(this.buttons, GamepadButton.DpadLeft, buttons[14].pressed); - MemoryHelper.writeSingleBit(this.buttons, GamepadButton.DpadRight, buttons[15].pressed); + MemoryHelper.writeSingleBit(this.buttons, GamepadButton.DpadRight, buttons[15].pressed); } /** @@ -596,16 +622,16 @@ export class TextEvent { /** * field offset 0 * @member {InputEvent} baseEvent; - * + * * field offset 20 * @member {Number} character; */ /** - * - * @param {Number} deviceId + * + * @param {Number} deviceId * @param {Number} character - * @param {Number} time + * @param {Number} time * @returns {TextEvent} */ @@ -613,7 +639,7 @@ export class TextEvent { const eventSize = InputEvent.size + MemoryHelper.sizeOfInt; let event = new TextEvent(); - event.baseEvent = new InputEvent(TextEvent.format, eventSize, deviceId, time); + event.baseEvent = new InputEvent(TextEvent.format, eventSize, deviceId, time); event.character = character; return event; } @@ -638,18 +664,18 @@ export class StateEvent { /** * field offset 0 * @member {InputEvent} baseEvent; - * + * * field offset 20 * @member {Number} stateFormat; - * + * * field offset 24 * @member {ArrayBuffer} stateData; */ /** - * - * @param {InputDevice} device - * @param {Number} time + * + * @param {InputDevice} device + * @param {Number} time * @returns {StateEvent} */ static from(device, time) { @@ -657,16 +683,16 @@ export class StateEvent { } /** - * - * @param {IInputState} state + * + * @param {IInputState} state * @param {Number} deviceId - * @param {Number} time + * @param {Number} time */ static fromState(state, deviceId, time) { const stateData = state.buffer; const stateSize = stateData.byteLength; const eventSize = InputEvent.size + MemoryHelper.sizeOfInt + stateSize; - + let stateEvent = new StateEvent(); stateEvent.baseEvent = new InputEvent(StateEvent.format, eventSize, deviceId, time); stateEvent.stateFormat = state.format; @@ -688,4 +714,4 @@ export class StateEvent { uint8View.set(new Uint8Array(this.stateData), InputEvent.size+MemoryHelper.sizeOfInt); return _buffer; } -} \ No newline at end of file +} diff --git a/WebApp/client/src/sender.js b/WebApp/client/src/sender.js index 6f3e7b1e5..88d909166 100644 --- a/WebApp/client/src/sender.js +++ b/WebApp/client/src/sender.js @@ -1,4 +1,4 @@ -import { +import { Mouse, Keyboard, Gamepad, @@ -36,8 +36,8 @@ export class Sender extends LocalInputManager { m_Product: "", m_Serial: "", m_Version: "", - m_Capabilities: "" - }; + m_Capabilities: "" + }; this.mouse = new Mouse("Mouse", "Mouse", 1, "", descriptionMouse); this._devices.push(this.mouse); @@ -56,7 +56,7 @@ export class Sender extends LocalInputManager { m_Product: "", m_Serial: "", m_Version: "", - m_Capabilities: "" + m_Capabilities: "" }; this.keyboard = new Keyboard("Keyboard", "Keyboard", 2, "", descriptionKeyboard); this._devices.push(this.keyboard); @@ -80,7 +80,7 @@ export class Sender extends LocalInputManager { window.addEventListener("gamepadconnected", this._onGamepadEvent.bind(this), false); window.addEventListener("gamepaddisconnected", this._onGamepadEvent.bind(this), false); - this._gamepadHandler = new GamepadHandler(); + this._gamepadHandler = new GamepadHandler(); this._gamepadHandler.addEventListener("gamepadupdated", this._onGamepadEvent.bind(this), false); } @@ -92,7 +92,7 @@ export class Sender extends LocalInputManager { m_Product: "", m_Serial: "", m_Version: "", - m_Capabilities: "" + m_Capabilities: "" }; this.touchscreen = new Touchscreen("Touchscreen", "Touchscreen", 4, "", descriptionTouch); this._devices.push(this.touchscreen); @@ -145,7 +145,9 @@ export class Sender extends LocalInputManager { _onTouchEvent(event) { this.touchscreen.queueEvent(event, this.timeSinceStartup); for(let touch of this.touchscreen.currentState.touchData) { - this._queueStateEvent(touch, this.touchscreen); + let clone = touch.copy(); + clone.position = this._corrector.map(clone.position); + this._queueStateEvent(clone, this.touchscreen); } } _onGamepadEvent(event) { @@ -160,14 +162,14 @@ export class Sender extends LocalInputManager { } case 'gamepadupdated': { this.gamepad.queueEvent(event); - this._queueStateEvent(this.gamepad.currentState, this.gamepad); + this._queueStateEvent(this.gamepad.currentState, this.gamepad); break; } } } _queueStateEvent(state, device) { - const stateEvent = + const stateEvent = StateEvent.fromState(state, device.deviceId, this.timeSinceStartup); const e = new CustomEvent( 'event', {detail: { event: stateEvent, device: device}}); @@ -188,15 +190,15 @@ export class Sender extends LocalInputManager { export class Observer { /** - * - * @param {RTCDataChannel} channel + * + * @param {RTCDataChannel} channel */ constructor(channel) { - this.channel = channel; + this.channel = channel; } /** - * - * @param {Message} message + * + * @param {Message} message */ onNext(message) { if(this.channel == null || this.channel.readyState != 'open') { @@ -204,4 +206,4 @@ export class Observer { } this.channel.send(message.buffer); } -} \ No newline at end of file +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs index 776e59a7f..ec7e67016 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs @@ -259,9 +259,9 @@ public override void SetChannel(string connectionId, RTCDataChannel channel) /// /// Texture Size. /// Region of the texture in world coordinate system. - public void SetInputRange(Vector2Int size, Rect region) + public void CalculateInputRegion(Vector2Int size, Rect region) { - receiver.SetInputRange(new Rect(Vector2.zero, size), region); + receiver.CalculateInputRegion(new Rect(Vector2.zero, size), region); } /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs index 98a314182..845a29de3 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs @@ -41,9 +41,9 @@ public override void SetChannel(string connectionId, RTCDataChannel channel) /// /// Texture Size. /// Region of the texture in world coordinate system. - public void SetInputRange(Rect region, Vector2Int size) + public void CalculateInputResion(Rect region, Vector2Int size) { - sender.SetInputRange(region, new Rect(Vector2.zero, size)); + sender.CalculateInputRegion(region, new Rect(Vector2.zero, size)); } /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs index 8b20c28ad..9bd8a3174 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs @@ -189,7 +189,7 @@ public override void QueueEvent(InputEventPtr ptr) /// /// Texture Size. /// Region of the texture in world coordinate system. - public void SetInputRange(Rect inputRegion, Rect outputRegion) + public void CalculateInputRegion(Rect inputRegion, Rect outputRegion) { _corrector.inputRegion = inputRegion; _corrector.outputRegion = outputRegion; diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs index 4d2fc0e67..8c6a323c4 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs @@ -74,7 +74,7 @@ public override IEnumerable layouts /// /// /// - public void SetInputRange(Rect inputRegion, Rect outputRegion) + public void CalculateInputRegion(Rect inputRegion, Rect outputRegion) { _corrector.inputRegion = inputRegion; _corrector.outputRegion = outputRegion; diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity b/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity index 8cd40056b..e798546fa 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity @@ -2740,37 +2740,11 @@ MonoBehaviour: type: 3} m_ActionEvents: - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451157} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnMovement - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 548e32fd-77d1-40e5-8197-32ca56b41bc0 m_ActionName: Player Controls/Movement[/Keyboard/w,/Keyboard/s,/Keyboard/a,/Keyboard/d] - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451157} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnLook - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 8ebbde1f-3044-41bc-bdac-430e0eae1a68 m_ActionName: Player Controls/Look[/Mouse/delta] - m_PersistentCalls: @@ -2798,56 +2772,29 @@ MonoBehaviour: m_ActionId: e773b1f9-ce5b-4fa2-9c1f-d194202c43b7 m_ActionName: Menu Controls/TogglePause[/Keyboard/p] - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451157} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: ResetCamera - m_Mode: 1 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 7f36745c-6b3f-404f-9df5-42688580b961 m_ActionName: Player Controls/ResetCamera[/Keyboard/u] - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451157} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnRotate - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 524fee92-4ef1-4fd9-9cb1-97fb72ae1195 m_ActionName: Player Controls/Rotate - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451157} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnPosition - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 50305201-a606-4afe-954c-0666ccaf6c53 m_ActionName: Player Controls/Position + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 60e72b30-ed9b-46db-80b9-f3660fa002ba + m_ActionName: Player Controls/Point[/Mouse/position] + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 245d7ad4-63bc-43e3-8d43-432ba35a43f0 + m_ActionName: Player Controls/Press[/Mouse/leftButton] + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 02ca192d-af9c-45fe-85c1-c9c38dd9cd48 + m_ActionName: Player Controls/Keyboard[/Keyboard/anyKey] m_DefaultActionMap: Player Controls --- !u!20 &725451154 Camera: @@ -2965,7 +2912,7 @@ MonoBehaviour: m_Curve: - serializedVersion: 3 time: 0 - value: 1 + value: 0.5 inSlope: 0 outSlope: 0 tangentMode: 0 @@ -2974,7 +2921,7 @@ MonoBehaviour: outWeight: 0 - serializedVersion: 3 time: 1 - value: 2.5 + value: 2 inSlope: 0 outSlope: 0 tangentMode: 0 @@ -2984,10 +2931,9 @@ MonoBehaviour: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 - rotationLerpTime: 0.01 + rotationLerpTime: 0.6 invertY: 0 playerInput: {fileID: 725451153} - uiController: {fileID: 1215389824} --- !u!1 &736464805 GameObject: m_ObjectHideFlags: 0 @@ -6182,6 +6128,8 @@ MonoBehaviour: m_EditorClassIdentifier: renderStreaming: {fileID: 1623734776} inputReceiver: {fileID: 725451153} + cameraController: {fileID: 725451157} + uiController: {fileID: 1215389824} videoStreamSender: {fileID: 725451156} bandwidthSelector: {fileID: 1429950776} scaleResolutionDownSelector: {fileID: 1975420023} diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs index ed2269b11..6337a7692 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs @@ -3,6 +3,7 @@ using System.Linq; using UnityEngine; using UnityEngine.UI; +using UnityEngine.InputSystem; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.XR; @@ -12,9 +13,19 @@ namespace Unity.RenderStreaming.Samples static class InputReceiverExtension { - public static void SetInputRange(this InputReceiver reveiver, Vector2Int size) + public static void CalculateInputRegion(this InputReceiver reveiver, Vector2Int size) { - reveiver.SetInputRange(size, new Rect(0, 0, Screen.width, Screen.height)); + reveiver.CalculateInputRegion(size, new Rect(0, 0, Screen.width, Screen.height)); + } + } + + static class InputActionExtension + { + public static void AddListener(this InputAction action, Action callback) + { + action.started += callback; + action.performed += callback; + action.canceled += callback; } } @@ -22,6 +33,8 @@ class BroadcastSample : MonoBehaviour { [SerializeField] private RenderStreaming renderStreaming; [SerializeField] private InputReceiver inputReceiver; + [SerializeField] private SimpleCameraControllerV2 cameraController; + [SerializeField] private UIControllerV2 uiController; [SerializeField] private VideoStreamSender videoStreamSender; [SerializeField] private Dropdown bandwidthSelector; [SerializeField] private Dropdown scaleResolutionDownSelector; @@ -71,6 +84,8 @@ class BroadcastSample : MonoBehaviour }; private RenderStreamingSettings settings; + private int lastWidth; + private int lastHeight; private void Awake() { @@ -126,6 +141,7 @@ private void ChangeScaleResolutionDown(int index) { var scale = scaleResolutionDownOptions.Values.ElementAt(index); videoStreamSender.SetScaleResolutionDown(scale); + CalculateInputRegion(); } private void ChangeFramerate(int index) @@ -139,7 +155,10 @@ private void ChangeResolution(int index) var resolution = resolutionOptions.Values.ElementAt(index); if (videoStreamSender.source != VideoStreamSource.Texture) + { videoStreamSender.SetTextureSize(resolution); + CalculateInputRegion(); + } } private void Start() @@ -151,22 +170,49 @@ private void Start() renderStreaming.Run(signaling: settings?.Signaling); inputReceiver.OnStartedChannel += OnStartedChannel; + var map = inputReceiver.currentActionMap; + map["Movement"].AddListener(cameraController.OnMovement); + map["Look"].AddListener(cameraController.OnLook); + map["ResetCamera"].AddListener(cameraController.OnResetCamera); + map["Rotate"].AddListener(cameraController.OnRotate); + map["Position"].AddListener(cameraController.OnPosition); + map["Point"].AddListener(uiController.OnPoint); + map["Press"].AddListener(uiController.OnPress); + map["PressAnyKey"].AddListener(uiController.OnPressAnyKey); } private void OnStartedChannel(string connectionId) { - inputReceiver.SetInputRange(new Vector2Int((int)videoStreamSender.width, (int)videoStreamSender.height)); - inputReceiver.SetEnableInputPositionCorrection(true); + CalculateInputRegion(); } // Parameters can be changed from the Unity Editor inspector when in Play Mode, // So this method monitors the parameters every frame and updates scene UI. -#if UNITY_EDITOR private void Update() { +#if UNITY_EDITOR SyncDisplayVideoSenderParameters(); - } #endif + // Call SetInputChange if window size is changed. + var width = Screen.width; + var height = Screen.height; + if (lastWidth == width && lastHeight == height) + return; + lastWidth = width; + lastHeight = height; + + CalculateInputRegion(); + } + + private void CalculateInputRegion() + { + if (!inputReceiver.IsConnected) + return; + var width = (int)(videoStreamSender.width / videoStreamSender.scaleResolutionDown); + var height = (int)(videoStreamSender.height / videoStreamSender.scaleResolutionDown); + inputReceiver.CalculateInputRegion(new Vector2Int(width, height)); + inputReceiver.SetEnableInputPositionCorrection(true); + } private void SyncDisplayVideoSenderParameters() { diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/CameraControl.inputactions b/com.unity.renderstreaming/Samples~/Example/Broadcast/CameraControl.inputactions index 367e548c7..89d9be2ab 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/CameraControl.inputactions +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/CameraControl.inputactions @@ -11,7 +11,8 @@ "id": "548e32fd-77d1-40e5-8197-32ca56b41bc0", "expectedControlType": "Vector2", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": true }, { "name": "Look", @@ -19,7 +20,8 @@ "id": "8ebbde1f-3044-41bc-bdac-430e0eae1a68", "expectedControlType": "Vector2", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": true }, { "name": "ResetCamera", @@ -27,7 +29,8 @@ "id": "7f36745c-6b3f-404f-9df5-42688580b961", "expectedControlType": "Button", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": false }, { "name": "Rotate", @@ -35,7 +38,8 @@ "id": "524fee92-4ef1-4fd9-9cb1-97fb72ae1195", "expectedControlType": "Quaternion", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": true }, { "name": "Position", @@ -43,7 +47,35 @@ "id": "50305201-a606-4afe-954c-0666ccaf6c53", "expectedControlType": "Vector3", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Point", + "type": "Value", + "id": "60e72b30-ed9b-46db-80b9-f3660fa002ba", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Press", + "type": "Button", + "id": "245d7ad4-63bc-43e3-8d43-432ba35a43f0", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "PressAnyKey", + "type": "Value", + "id": "02ca192d-af9c-45fe-85c1-c9c38dd9cd48", + "expectedControlType": "Key", + "processors": "", + "interactions": "", + "initialStateCheck": true } ], "bindings": [ @@ -244,6 +276,61 @@ "action": "Position", "isComposite": false, "isPartOfComposite": false + }, + { + "name": "", + "id": "ba5cd7db-5c11-4519-b30c-5f2b5bf291ab", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard And Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "d9fcd8cb-28b5-4414-b98d-f48b3e6a0e79", + "path": "/touch*/position", + "interactions": "", + "processors": "", + "groups": "Touchscreen", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c15d3af8-9943-4dd5-af94-9aa5a5167c95", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": "Keyboard And Mouse", + "action": "Press", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "2d7822b8-e6e2-4aff-9e3e-733fcfa5dc6b", + "path": "/touch*/Press", + "interactions": "", + "processors": "", + "groups": "Touchscreen", + "action": "Press", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b38c6b49-abda-4492-8ceb-d54d14b65e2f", + "path": "/anyKey", + "interactions": "", + "processors": "", + "groups": "", + "action": "PressAnyKey", + "isComposite": false, + "isPartOfComposite": false } ] }, @@ -257,7 +344,8 @@ "id": "538ffe95-ba92-4acb-84f7-314f6ac8e0a5", "expectedControlType": "Vector2", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": false }, { "name": "Left Click", @@ -265,7 +353,8 @@ "id": "96c8be88-a7bb-4861-b5e9-956b4208d043", "expectedControlType": "Button", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": false }, { "name": "Point", @@ -273,7 +362,8 @@ "id": "d54e5ff5-4f35-4d2f-a745-95d14aef8c43", "expectedControlType": "Vector2", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": false }, { "name": "Submit", @@ -281,7 +371,8 @@ "id": "5f571f6a-e9e7-4120-ae3c-79f846bdd202", "expectedControlType": "Button", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": false }, { "name": "Cancel", @@ -289,7 +380,8 @@ "id": "146e681c-77dd-4ff0-9ad5-f4351fea14cc", "expectedControlType": "Button", "processors": "", - "interactions": "" + "interactions": "", + "initialStateCheck": false }, { "name": "TogglePause", @@ -297,7 +389,8 @@ "id": "e773b1f9-ce5b-4fa2-9c1f-d194202c43b7", "expectedControlType": "", "processors": "", - "interactions": "Press" + "interactions": "Press", + "initialStateCheck": false } ], "bindings": [ diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/SimpleCameraControllerV2.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/SimpleCameraControllerV2.cs index f9efc2db7..8af6f0242 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/SimpleCameraControllerV2.cs +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/SimpleCameraControllerV2.cs @@ -75,7 +75,6 @@ public void UpdateTransform(Transform t) private bool invertY; [SerializeField] InputReceiver playerInput; - [SerializeField] UIController uiController; private readonly CameraState m_TargetCameraState = new CameraState(); private readonly CameraState m_InterpolatingCameraState = new CameraState(); @@ -103,13 +102,11 @@ void OnDeviceChange(InputDevice device, InputDeviceChange change) case InputDeviceChange.Added: { playerInput.PerformPairingWithDevice(device); - uiController.SetDevice(device, true); return; } case InputDeviceChange.Removed: { playerInput.UnpairDevices(device); - uiController.SetDevice(device, false); return; } } @@ -181,32 +178,31 @@ public void OnDeviceRegained() { } - public void OnMovement(InputAction.CallbackContext value) + public void OnMovement(InputAction.CallbackContext context) { - inputMovement = value.ReadValue(); + inputMovement = context.ReadValue(); } - public void OnLook(InputAction.CallbackContext value) + public void OnLook(InputAction.CallbackContext context) { - inputLook = value.ReadValue(); + inputLook = context.ReadValue(); } - public void OnPosition(InputAction.CallbackContext value) + public void OnResetCamera(InputAction.CallbackContext context) { - inputPosition = value.ReadValue(); + m_InitialCameraState.UpdateTransform(transform); + m_TargetCameraState.SetFromTransform(transform); + m_InterpolatingCameraState.SetFromTransform(transform); } - public void OnRotate(InputAction.CallbackContext value) + public void OnPosition(InputAction.CallbackContext context) { - inputRotation = value.ReadValue(); + inputPosition = context.ReadValue(); } - - public void ResetCamera() + public void OnRotate(InputAction.CallbackContext context) { - m_InitialCameraState.UpdateTransform(transform); - m_TargetCameraState.SetFromTransform(transform); - m_InterpolatingCameraState.SetFromTransform(transform); + inputRotation = context.ReadValue(); } } } diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs new file mode 100644 index 000000000..6c0b880f1 --- /dev/null +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs @@ -0,0 +1,76 @@ +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.InputSystem; + +namespace Unity.RenderStreaming.Samples +{ + [RequireComponent(typeof(RectTransform))] + class UIControllerV2 : MonoBehaviour + { + [SerializeField] Text text; + [SerializeField] CanvasGroup canvasGroup; + [SerializeField] Image pointer; + [SerializeField] GameObject noticeTouchControl; + [SerializeField] private AnimationCurve transitionCurve = + new AnimationCurve( + new Keyframe(0.75f, 1f, 0f, 0f), + new Keyframe(1f, 0f, 0f, 0f)); + + private float timeTransition = 0f; + private RectTransform m_rectTransform = null; + private bool isSubscribing = false; + + + public void SetDevice(InputDevice device, bool add = false) + { + } + + void Start() + { + m_rectTransform = GetComponent(); + canvasGroup.alpha = 0; + text.text = string.Empty; + } + + private void FixedUpdate() + { + if (!Mathf.Approximately(canvasGroup.alpha, 0f)) + { + timeTransition += Time.deltaTime; + canvasGroup.alpha = transitionCurve.Evaluate(timeTransition); + } + } + + public void OnPressAnyKey(InputAction.CallbackContext context) + { + var keyboard = context.control.device as Keyboard; + + if(!isSubscribing) + { + keyboard.onTextInput += OnTextInput; + isSubscribing = true; + } + } + + void OnTextInput(char c) + { + canvasGroup.alpha = 1f; + text.text = c.ToString(); + timeTransition = 0; + } + + public void OnPoint(InputAction.CallbackContext context) + { + var position = context.ReadValue(); + var screenSize = new Vector2Int(Screen.width, Screen.height); + position = position / screenSize * new Vector2(m_rectTransform.rect.width, m_rectTransform.rect.height); + pointer.rectTransform.anchoredPosition = position; + } + + public void OnPress(InputAction.CallbackContext context) + { + var button = context.ReadValueAsButton(); + pointer.color = button ? Color.red : Color.clear; + } + } +} diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/UIController.cs.meta b/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs.meta similarity index 100% rename from com.unity.renderstreaming/Samples~/Example/Scripts/UIController.cs.meta rename to com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs.meta diff --git a/com.unity.renderstreaming/Samples~/Example/Receiver/Receiver.unity b/com.unity.renderstreaming/Samples~/Example/Receiver/Receiver.unity index f8be85ca5..be4ff770c 100644 --- a/com.unity.renderstreaming/Samples~/Example/Receiver/Receiver.unity +++ b/com.unity.renderstreaming/Samples~/Example/Receiver/Receiver.unity @@ -123,6 +123,86 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &21738248 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 21738249} + - component: {fileID: 21738251} + - component: {fileID: 21738250} + m_Layer: 5 + m_Name: Resolution + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &21738249 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 21738248} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 528948626} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0, y: 1} +--- !u!114 &21738250 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 21738248} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 100 x 100 +--- !u!222 &21738251 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 21738248} + m_CullTransparentMesh: 1 --- !u!1001 &50660490 PrefabInstance: m_ObjectHideFlags: 0 @@ -466,6 +546,7 @@ RectTransform: - {fileID: 1537721409} - {fileID: 1510994870} - {fileID: 50660491} + - {fileID: 21738249} m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -1509,8 +1590,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 15, y: -40} - m_SizeDelta: {x: -330, y: 80} + m_AnchoredPosition: {x: 90, y: -40} + m_SizeDelta: {x: -180, y: 80} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1537721410 MonoBehaviour: @@ -1738,6 +1819,7 @@ MonoBehaviour: receiveVideoViewer: {fileID: 1915034401} receiveAudioViewer: {fileID: 1915034407} connection: {fileID: 1915034404} + resolution: {fileID: 21738250} --- !u!114 &1915034406 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs b/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs index bf5060a82..49b3a0c21 100644 --- a/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs @@ -6,7 +6,7 @@ namespace Unity.RenderStreaming.Samples { static class InputSenderExtension { - public static void SetInputRange(this InputSender sender, RawImage image) + public static (Rect, Vector2Int) GetRegionAndSize(this RawImage image) { // correct pointer position Vector3[] corners = new Vector3[4]; @@ -22,7 +22,7 @@ public static void SetInputRange(this InputSender sender, RawImage image) ); var size = new Vector2Int(image.texture.width, image.texture.height); - sender.SetInputRange(region, size); + return (region, size); } } @@ -38,11 +38,13 @@ class ReceiverSample : MonoBehaviour [SerializeField] private VideoStreamReceiver receiveVideoViewer; [SerializeField] private AudioStreamReceiver receiveAudioViewer; [SerializeField] private SingleConnection connection; + [SerializeField] private Text resolution; #pragma warning restore 0649 private string connectionId; private InputSender inputSender; private RenderStreamingSettings settings; + private Vector2 lastSize; void Awake() { @@ -71,22 +73,39 @@ void Start() renderStreaming.Run(signaling: settings?.Signaling); } + private void Update() + { + // Call SetInputChange if window size is changed. + var size = remoteVideoImage.rectTransform.sizeDelta; + if (lastSize == size) + return; + lastSize = size; + CalculateInputRegion(); + } + void OnUpdateReceiveTexture(Texture texture) { remoteVideoImage.texture = texture; - SetInputChange(); + CalculateInputRegion(); } void OnStartedChannel(string connectionId) { - SetInputChange(); + CalculateInputRegion(); + } + + private void OnRectTransformDimensionsChange() + { + CalculateInputRegion(); } - void SetInputChange() + void CalculateInputRegion() { if (inputSender == null || !inputSender.IsConnected || remoteVideoImage.texture == null) return; - inputSender.SetInputRange(remoteVideoImage); + var (region, size) = remoteVideoImage.GetRegionAndSize(); + resolution.text = $"{(int)region.width} x {(int)region.height}"; + inputSender.CalculateInputResion(region, size); inputSender.EnableInputPositionCorrection(true); } diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/UIController.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs similarity index 100% rename from com.unity.renderstreaming/Samples~/Example/Scripts/UIController.cs rename to com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs.meta b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs.meta new file mode 100644 index 000000000..14290e9fa --- /dev/null +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c2b4eb99a8e1f63459b84b9faa1f4461 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInput.unity b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInput.unity index 7ab370015..767b30dea 100644 --- a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInput.unity +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInput.unity @@ -1891,7 +1891,7 @@ MonoBehaviour: m_RotationOrder: 4 rotationLerpTime: 0.01 invertY: 0 - uiController: {fileID: 1263705289} + uiController: {fileID: 1263705294} receiver: {fileID: 1112556113} --- !u!20 &1112556118 Camera: @@ -2360,7 +2360,7 @@ MonoBehaviour: m_GameObject: {fileID: 1215389819} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 0cf609b6c80ea492288c74dd8f26ae97, type: 3} + m_Script: {fileID: 11500000, guid: c2b4eb99a8e1f63459b84b9faa1f4461, type: 3} m_Name: m_EditorClassIdentifier: text: {fileID: 1127658837} @@ -2403,7 +2403,7 @@ GameObject: - component: {fileID: 1263705292} - component: {fileID: 1263705291} - component: {fileID: 1263705290} - - component: {fileID: 1263705289} + - component: {fileID: 1263705294} m_Layer: 5 m_Name: Canvas (Render Streaming Camera 2) m_TagString: Untagged @@ -2411,46 +2411,6 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!114 &1263705289 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1263705288} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 0cf609b6c80ea492288c74dd8f26ae97, type: 3} - m_Name: - m_EditorClassIdentifier: - text: {fileID: 1187697327} - canvasGroup: {fileID: 1781893189} - pointer: {fileID: 710664488} - noticeTouchControl: {fileID: 0} - transitionCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0.75 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - - serializedVersion: 3 - time: 1 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0 - outWeight: 0 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 --- !u!114 &1263705290 MonoBehaviour: m_ObjectHideFlags: 0 @@ -2534,6 +2494,46 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!114 &1263705294 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1263705288} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c2b4eb99a8e1f63459b84b9faa1f4461, type: 3} + m_Name: + m_EditorClassIdentifier: + text: {fileID: 1187697327} + canvasGroup: {fileID: 1781893189} + pointer: {fileID: 710664488} + noticeTouchControl: {fileID: 0} + transitionCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0.75 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 --- !u!1 &1287993066 GameObject: m_ObjectHideFlags: 0 From 9de88a5ddacfe1cc04629614148009f4d99a8ab7 Mon Sep 17 00:00:00 2001 From: Brian Harrison <92394761+BrianHarrisonUnity@users.noreply.github.com> Date: Wed, 30 Nov 2022 17:31:57 -0700 Subject: [PATCH 004/117] fix: Fix offers being resent indeffinitely when PeerConnection is in failed state (#810) * fix: Fix offers being resent indeffinitely when PeerConnection is in the failed state * fix: Remove PeerConnections that are failed even if they aren't currently waiting for an Answer * test: Add test for deleting failed PeerConnections * test: fix test to allow for running in both public & private mode --- .../Scripts/RenderStreamingInternal.cs | 24 ++++++- .../Runtime/RenderStreamingInternalTest.cs | 65 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs index b4fb998e9..1d581f371 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs @@ -313,12 +313,27 @@ public void SendAnswer(string connectionId) IEnumerator ResendOfferCoroutine() { + HashSet failedConnections = new HashSet(); while (_runningResendCoroutine) { - foreach (var peer in _mapConnectionIdAndPeer.Where(x => x.Value.waitingAnswer)) + failedConnections.Clear(); + foreach (var peer in _mapConnectionIdAndPeer) { - peer.Value.SendOffer(); + if (peer.Value.peer.ConnectionState == RTCPeerConnectionState.Failed) + { + failedConnections.Add(peer.Key); + } + else if(peer.Value.waitingAnswer) + { + peer.Value.SendOffer(); + } } + + foreach (var connectionId in failedConnections) + { + DestroyConnection(connectionId); + } + yield return 0; } } @@ -340,6 +355,11 @@ void OnCreateConnection(ISignaling signaling, string connectionId, bool polite) } void OnDestroyConnection(ISignaling signaling, string connectionId) + { + DestroyConnection(connectionId); + } + + void DestroyConnection(string connectionId) { DeletePeerConnection(connectionId); onDeletedConnection?.Invoke(connectionId); diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs index 4749c9e7a..3ae2377fe 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs @@ -751,6 +751,71 @@ public IEnumerator ResendOfferUntilGotAnswer(TestMode mode) target2.Dispose(); } + [TestCase(TestMode.PublicMode, ExpectedResult = null)] + [TestCase(TestMode.PrivateMode, ExpectedResult = null)] + [UnityTest, Timeout(30000), LongRunning] + public IEnumerator DeleteFailedPeers(TestMode mode) + { + MockSignaling.Reset(mode == TestMode.PrivateMode); + + var dependencies1 = CreateDependencies(); + var dependencies2 = CreateDependencies(); + var target1 = new RenderStreamingInternal(ref dependencies1); + var target2 = new RenderStreamingInternal(ref dependencies2); + + bool isStarted1 = false; + bool isStarted2 = false; + target1.onStart += () => { isStarted1 = true; }; + target2.onStart += () => { isStarted2 = true; }; + yield return new WaitUntil(() => isStarted1 && isStarted2); + Assert.That(isStarted1, Is.True); + Assert.That(isStarted2, Is.True); + + bool isCreatedConnection1 = false; + bool isCreatedConnection2 = false; + target1.onCreatedConnection += _ => { isCreatedConnection1 = true; }; + target2.onCreatedConnection += _ => { isCreatedConnection2 = true; }; + + var connectionId = "12345"; + + // target1 has impolite peer (request first) + target1.CreateConnection(connectionId); + yield return new WaitUntil(() => isCreatedConnection1); + Assert.That(isCreatedConnection1, Is.True); + target2.CreateConnection(connectionId); + yield return new WaitUntil(() => isCreatedConnection2); + Assert.That(isCreatedConnection2, Is.True); + + bool isGotOffer2 = false; + bool isGotAnswer1 = false; + target2.onGotOffer += (_, sdp) => { isGotOffer2 = true; }; + target1.onGotAnswer += (_, sdp) => { isGotAnswer1 = true; }; + + RTCRtpTransceiverInit init1 = new RTCRtpTransceiverInit() + { + direction = RTCRtpTransceiverDirection.SendOnly + }; + target1.AddTransceiver(connectionId, TrackKind.Video, init1); + + yield return new WaitUntil(() => isGotOffer2); + Assert.That(isGotOffer2, Is.True, $"{nameof(isGotOffer2)} is not True."); + + target2.SendAnswer(connectionId); + + yield return new WaitUntil(() => isGotAnswer1); + Assert.That(isGotAnswer1, Is.True, $"{nameof(isGotAnswer1)} is not True."); + + // Improperly dispose of target1 to force failed state on target2 + target1.Dispose(); + + bool isDeletedConnection2 = false; + target2.onDeletedConnection += _ => { isDeletedConnection2 = true; }; + yield return new WaitUntil(() => isDeletedConnection2); + Assert.That(isDeletedConnection2, Is.True, $"{nameof(isDeletedConnection2)} is not True."); + + target2.Dispose(); + } + [UnityTest, Timeout(10000)] public IEnumerator ReNegotiationAfterReceivingFirstOffer() { From 0410dff10a19f6780e40a1f3eb6d7f953d41a20d Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 13 Dec 2022 09:16:13 +0900 Subject: [PATCH 005/117] chore: Upgrade package version 3.1.0-exp.5 (#815) --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + README.md | 3 ++- RenderStreaming~/Packages/manifest.json | 12 +++++----- RenderStreaming~/Packages/packages-lock.json | 24 +++++++++---------- .../ProjectSettings/ProjectVersion.txt | 4 ++-- com.unity.renderstreaming/CHANGELOG.md | 11 +++++++++ .../Documentation~/installation.md | 2 +- .../Documentation~/tutorial.md | 2 +- .../Editor/WebAppDownloader.cs | 2 +- com.unity.renderstreaming/package.json | 4 ++-- 10 files changed, 39 insertions(+), 26 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9065890cd..dc43659ab 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -27,6 +27,7 @@ body: What version of the package are you using? You can check the Unity version in Package Manager Window. See [manual](https://docs.unity3d.com/Manual/upm-ui.html). options: + - 3.1.0-exp.5 - 3.1.0-exp.4 - 3.1.0-exp.3 - 3.1.0-exp.2 diff --git a/README.md b/README.md index 0ee17c2fa..34090ea58 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,8 @@ Please see [Furioos Tutorial](com.unity.renderstreaming/Documentation~/deploy-to | `3.1.0-exp.2` | - Audio Renderer support
- Multiplay sample
- M1 Mac support | Dec 2021 | | `3.1.0-exp.3` | - Fix bugs | Feb 2022 | | `3.1.0-exp.4` | - Streaming settings API
- *Unity 2022.1* support
- Remove *Unity 2019.4* from support list | Oct 2022 | -| `3.1.0-exp.5` | - Streaming Settings Window
- Auto Configuration | Dec 2022 | +| `3.1.0-exp.5` | - Fix bugs | Dec 2023 | +| `3.1.0-exp.6` | - Streaming Settings Window
- Auto Configuration | Feb 2023 | ## FAQ diff --git a/RenderStreaming~/Packages/manifest.json b/RenderStreaming~/Packages/manifest.json index fedbd0d82..c00e92f16 100644 --- a/RenderStreaming~/Packages/manifest.json +++ b/RenderStreaming~/Packages/manifest.json @@ -1,14 +1,14 @@ { "dependencies": { - "com.unity.ide.rider": "3.0.15", - "com.unity.ide.visualstudio": "2.0.16", + "com.unity.ide.rider": "3.0.17", + "com.unity.ide.visualstudio": "2.0.17", "com.unity.ide.vscode": "1.2.5", "com.unity.test-framework": "1.1.33", - "com.unity.testtools.codecoverage": "1.2.0-exp.7", + "com.unity.testtools.codecoverage": "1.2.2", "com.unity.ugui": "1.0.0", - "com.unity.xr.arcore": "4.2.3", - "com.unity.xr.arfoundation": "4.2.3", - "com.unity.xr.arkit": "4.2.3", + "com.unity.xr.arcore": "4.2.7", + "com.unity.xr.arfoundation": "4.2.7", + "com.unity.xr.arkit": "4.2.7", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", "com.unity.modules.audio": "1.0.0", diff --git a/RenderStreaming~/Packages/packages-lock.json b/RenderStreaming~/Packages/packages-lock.json index 1c58803ed..d995254ed 100644 --- a/RenderStreaming~/Packages/packages-lock.json +++ b/RenderStreaming~/Packages/packages-lock.json @@ -15,7 +15,7 @@ "url": "https://packages.unity.com" }, "com.unity.ide.rider": { - "version": "3.0.15", + "version": "3.0.17", "depth": 0, "source": "registry", "dependencies": { @@ -24,7 +24,7 @@ "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { - "version": "2.0.16", + "version": "2.0.17", "depth": 0, "source": "registry", "dependencies": { @@ -40,7 +40,7 @@ "url": "https://packages.unity.com" }, "com.unity.inputsystem": { - "version": "1.4.2", + "version": "1.4.4", "depth": 1, "source": "registry", "dependencies": { @@ -54,7 +54,7 @@ "source": "embedded", "dependencies": { "com.unity.webrtc": "2.4.0-exp.11", - "com.unity.inputsystem": "1.4.1" + "com.unity.inputsystem": "1.4.4" } }, "com.unity.settings-manager": { @@ -85,7 +85,7 @@ "url": "https://packages.unity.com" }, "com.unity.testtools.codecoverage": { - "version": "1.2.0-exp.7", + "version": "1.2.2", "depth": 0, "source": "registry", "dependencies": { @@ -114,11 +114,11 @@ "url": "https://packages.unity.com" }, "com.unity.xr.arcore": { - "version": "4.2.3", + "version": "4.2.7", "depth": 0, "source": "registry", "dependencies": { - "com.unity.xr.arsubsystems": "4.2.3", + "com.unity.xr.arsubsystems": "4.2.7", "com.unity.xr.management": "4.0.1", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.unitywebrequest": "1.0.0" @@ -126,29 +126,29 @@ "url": "https://packages.unity.com" }, "com.unity.xr.arfoundation": { - "version": "4.2.3", + "version": "4.2.7", "depth": 0, "source": "registry", "dependencies": { - "com.unity.xr.arsubsystems": "4.2.3", + "com.unity.xr.arsubsystems": "4.2.7", "com.unity.xr.management": "4.0.1", "com.unity.modules.particlesystem": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.xr.arkit": { - "version": "4.2.3", + "version": "4.2.7", "depth": 0, "source": "registry", "dependencies": { "com.unity.editorcoroutines": "1.0.0", - "com.unity.xr.arsubsystems": "4.2.3", + "com.unity.xr.arsubsystems": "4.2.7", "com.unity.xr.management": "4.0.1" }, "url": "https://packages.unity.com" }, "com.unity.xr.arsubsystems": { - "version": "4.2.3", + "version": "4.2.7", "depth": 1, "source": "registry", "dependencies": { diff --git a/RenderStreaming~/ProjectSettings/ProjectVersion.txt b/RenderStreaming~/ProjectSettings/ProjectVersion.txt index 8ea1b855a..eabd63377 100644 --- a/RenderStreaming~/ProjectSettings/ProjectVersion.txt +++ b/RenderStreaming~/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2021.3.11f1 -m_EditorVersionWithRevision: 2021.3.11f1 (0a5ca18544bf) +m_EditorVersion: 2021.3.15f1 +m_EditorVersionWithRevision: 2021.3.15f1 (e8e88683f834) diff --git a/com.unity.renderstreaming/CHANGELOG.md b/com.unity.renderstreaming/CHANGELOG.md index a69847bd6..c61f9af60 100644 --- a/com.unity.renderstreaming/CHANGELOG.md +++ b/com.unity.renderstreaming/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to com.unity.renderstreaming package will be documented in t The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [3.1.0-exp.5] - 2022-12-22 + +### Changed + +- Upgrade the version of Input System package `1.4.4`. +- Upgrade the version of WebRTC package `3.0.0-pre.2`. + +### Fixed + +- Fix incorrect mouse position in full-screen web player sample. + ## [3.1.0-exp.4] - 2022-10-06 ### Added diff --git a/com.unity.renderstreaming/Documentation~/installation.md b/com.unity.renderstreaming/Documentation~/installation.md index a00df3ad0..b8094569d 100644 --- a/com.unity.renderstreaming/Documentation~/installation.md +++ b/com.unity.renderstreaming/Documentation~/installation.md @@ -11,7 +11,7 @@ Check Package Manager window, Click `+` button and select `Add package from git Input the string below to the input field. ``` -com.unity.renderstreaming@3.1.0-exp.4 +com.unity.renderstreaming@3.1.0-exp.5 ``` The list of version string is [here](https://github.com/Unity-Technologies/UnityRenderStreaming/tags). In most cases, the latest version is recommended to use. diff --git a/com.unity.renderstreaming/Documentation~/tutorial.md b/com.unity.renderstreaming/Documentation~/tutorial.md index 0119419e1..1360fbbb1 100644 --- a/com.unity.renderstreaming/Documentation~/tutorial.md +++ b/com.unity.renderstreaming/Documentation~/tutorial.md @@ -15,7 +15,7 @@ Check Package Manager window, Click `+` button and select `Add package from git Input the string below to the input field. ``` -com.unity.renderstreaming@3.1.0-exp.4 +com.unity.renderstreaming@3.1.0-exp.5 ``` The list of version string is [here](https://github.com/Unity-Technologies/com.unity.renderstreaming/tags). In most cases, the latest version is recommended to use. diff --git a/com.unity.renderstreaming/Editor/WebAppDownloader.cs b/com.unity.renderstreaming/Editor/WebAppDownloader.cs index c520d5506..81a03f5ab 100644 --- a/com.unity.renderstreaming/Editor/WebAppDownloader.cs +++ b/com.unity.renderstreaming/Editor/WebAppDownloader.cs @@ -7,7 +7,7 @@ namespace Unity.RenderStreaming.Editor internal static class WebAppDownloader { const string URLRoot = "https://github.com/Unity-Technologies/UnityRenderStreaming"; - const string LatestKnownVersion = "3.1.0-exp.4"; + const string LatestKnownVersion = "3.1.0-exp.5"; // TODO::fix release process of webserver runtime. const string FileNameWebAppForMac = "webserver_mac"; diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index 08dddaee5..8c61125c9 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -1,12 +1,12 @@ { "name": "com.unity.renderstreaming", "displayName": "Unity Render Streaming", - "version": "3.1.0-exp.4", + "version": "3.1.0-exp.5", "unity": "2020.3", "description": "This is a package for using Unity Render Streaming technology. It contains two samples to use the technology.", "dependencies": { "com.unity.webrtc": "2.4.0-exp.11", - "com.unity.inputsystem": "1.4.1" + "com.unity.inputsystem": "1.4.4" }, "samples": [ { From c9955ddb48eebfd6ed77e9bd1acddae6eed88687 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 13 Dec 2022 09:20:18 +0900 Subject: [PATCH 006/117] chore: Update WebRTC package 3.0.0-pre.2 (#814) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove DomainLOad and DomainUnload * update dependencies * fix bug * fix test error * changed pull request branch to main --- .yamato/upm-ci-renderstreaming-packages.yml | 2 +- .../Runtime/Scripts/PeerConnection.cs | 6 +++- .../Runtime/Scripts/RenderStreaming.cs | 35 ------------------- .../Scripts/RenderStreamingInternal.cs | 10 ------ .../Tests/Runtime/StreamingComponentTest.cs | 4 +-- com.unity.renderstreaming/package.json | 2 +- 6 files changed, 9 insertions(+), 50 deletions(-) diff --git a/.yamato/upm-ci-renderstreaming-packages.yml b/.yamato/upm-ci-renderstreaming-packages.yml index 90e411606..2877be4a8 100644 --- a/.yamato/upm-ci-renderstreaming-packages.yml +++ b/.yamato/upm-ci-renderstreaming-packages.yml @@ -322,7 +322,7 @@ trigger_test_{{ package.name }}_{{ editor.version }}: - .yamato/upm-ci-{{ package.name }}-packages.yml#test_{{ package.name }}_{{ editor.version }} {% if editor.version == "2020.3" -%} triggers: - expression: pull_request.target eq "develop" + expression: pull_request.target eq "main" {% endif -%} test_{{ package.name }}_{{ editor.version }}: diff --git a/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs b/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs index fbe622ebb..eb0b4cfe3 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs @@ -207,7 +207,11 @@ private IEnumerator SendOfferCoroutine() _processingSetDescription = false; yield break; } - + if (_peer.SignalingState != RTCSignalingState.HaveLocalOffer) + { + _processingSetDescription = false; + yield break; + } Assert.AreEqual(_peer.LocalDescription.type, RTCSdpType.Offer); Assert.AreEqual(_peer.SignalingState, RTCSignalingState.HaveLocalOffer); _processingSetDescription = false; diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs index 7537a43c8..2366c2bba 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs @@ -44,41 +44,6 @@ public sealed class RenderStreaming : MonoBehaviour private SignalingEventProvider m_provider; private bool m_running; -#if UNITY_EDITOR - [InitializeOnLoadMethod] - static void InitializeOnEditor() - { - /// todo(kazuki):: This is workaround. - /// When kicking the Unity Editor with batchmode flag on command line, The Unity Editor crashes - /// caused by not unloading WebRTC native plugin. By this workaround, Some static methods of this - /// package don't work correctly when batchmode. These static methods depend on WebRTC API, - /// therefore the package initialization must be completed just after launching Editor. - /// In the future, we will remove this workaround after improving the initialization of the - /// WebRTC package. - if(!IsYamato) - { - if (Application.isBatchMode) - return; - } - RenderStreamingInternal.DomainUnload(); - RenderStreamingInternal.DomainLoad(); - EditorApplication.quitting += RenderStreamingInternal.DomainUnload; - } - - /// - /// Executed from the auto testing environment or not. - /// - static bool IsYamato => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("YAMATO_JOB_ID")); -#else - [RuntimeInitializeOnLoadMethod] - static void InitializeOnRuntime() - { - RenderStreamingInternal.DomainUnload(); - RenderStreamingInternal.DomainLoad(); - Application.quitting += RenderStreamingInternal.DomainUnload; - } -#endif - static Type GetType(string typeName) { var type = Type.GetType(typeName); diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs index 1d581f371..e38c65c46 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs @@ -101,16 +101,6 @@ internal class RenderStreamingInternal : IDisposable, private bool _runningResendCoroutine; private float _resendInterval = 3.0f; - internal static void DomainLoad() - { - WebRTC.WebRTC.Initialize(); - } - - internal static void DomainUnload() - { - WebRTC.WebRTC.Dispose(); - } - /// /// /// diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index 16a033e77..8e659a49c 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -20,7 +20,7 @@ public void GetAvailableCodec() Assert.That(codecs, Is.Not.Empty); Assert.That(codecs.Any(codec => codec.name == "VP8")); Assert.That(codecs.Any(codec => codec.name == "VP9")); - Assert.That(codecs.Any(codec => codec.name == "AV1X")); + Assert.That(codecs.Any(codec => codec.name == "AV1")); foreach(var codec in codecs) { @@ -240,7 +240,7 @@ public void GetAvailableCodec() Assert.That(codecs, Is.Not.Empty); Assert.That(codecs.Any(codec => codec.name == "VP8")); Assert.That(codecs.Any(codec => codec.name == "VP9")); - Assert.That(codecs.Any(codec => codec.name == "AV1X")); + Assert.That(codecs.Any(codec => codec.name == "AV1")); foreach (var codec in codecs) { diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index 8c61125c9..60ad1eb1b 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -5,7 +5,7 @@ "unity": "2020.3", "description": "This is a package for using Unity Render Streaming technology. It contains two samples to use the technology.", "dependencies": { - "com.unity.webrtc": "2.4.0-exp.11", + "com.unity.webrtc": "3.0.0-pre.2", "com.unity.inputsystem": "1.4.4" }, "samples": [ From c64b1b07bc49a47d70f4441da90fbf7b23a7cfab Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 15 Dec 2022 10:44:36 +0900 Subject: [PATCH 007/117] doc: Fix documents for 3.1.0-exp.5 (#816) --- .../Documentation~/TableOfContents.md | 2 - .../Documentation~/deploy-to-furioos.md | 19 ------ .../Documentation~/index.md | 14 ++--- .../Documentation~/template.md | 63 ------------------- 4 files changed, 6 insertions(+), 92 deletions(-) delete mode 100644 com.unity.renderstreaming/Documentation~/deploy-to-furioos.md delete mode 100644 com.unity.renderstreaming/Documentation~/template.md diff --git a/com.unity.renderstreaming/Documentation~/TableOfContents.md b/com.unity.renderstreaming/Documentation~/TableOfContents.md index a0964dad0..ce84efa0c 100644 --- a/com.unity.renderstreaming/Documentation~/TableOfContents.md +++ b/com.unity.renderstreaming/Documentation~/TableOfContents.md @@ -16,7 +16,6 @@ * [Audio Streaming Component](audio-streaming.md) * [Data Streaming Component](data-streaming.md) * [Using with Input System](use-inputsystem.md) -* [Deploy to Furioos](deploy-to-furioos.md) * [Samples](samples.md) * [Receiver](sample-receiver.md) * [Broadcast](sample-broadcast.md) @@ -26,5 +25,4 @@ * [Gyroscope](sample-gyroscope.md) * [Web Browser Input](sample-browserinput.md) * [Multiplay](sample-multiplay.md) -* [Project Template](template.md) * [FAQ](faq.md) diff --git a/com.unity.renderstreaming/Documentation~/deploy-to-furioos.md b/com.unity.renderstreaming/Documentation~/deploy-to-furioos.md deleted file mode 100644 index 23f9eac76..000000000 --- a/com.unity.renderstreaming/Documentation~/deploy-to-furioos.md +++ /dev/null @@ -1,19 +0,0 @@ -# Deploy to Furioos - -As you may already know, **Unity Render Streaming** has a Furioos-compatible signaling option. This means that you can build a dedicated version of your application, host it on **Furioos**, and share it with thousands of customers who will enjoy all the features of **Unity Render Streaming**. But you will **not** have to deal with any of the difficulties of setting up a private server, a machine in the cloud or manage the scalability of your solution. - -To do so, the requirement is to select `FurioosSignaling` in the `Signaling type` parameter of the [RenderStreaming](streaming-management.md#render-streaming) component. -This allow your application to connect to Furioos services when running on the managed virtual machines. - -![Selecting FurioosSignaling](images/furioos_signaling.png) - -Then just build a **standalone Windows version** of your application, and zip it! Don't try to build iOS, Android, linux or whatever version of your app, **Furioos only support Windows applications**. Also, be sure to zip the whole exported folder with all files and sub-folders, not just the ".exe" file. - -![Selecting FurioosSignaling](images/furioos_zip_folder.png) - -Finally just upload it on your account at https://portal.furioos.com/ . -If you need futher help to upload your application on **Furioos**, please follow [this tutorial](https://support.furioos.com/article/adding-an-application-on-furioos/). - -You can check the stream type on https://portal.furioos.com/ by clicking to the "more options" button in the toolbar. - -![Selecting FurioosSignaling](images/furioos_stream_type.png) \ No newline at end of file diff --git a/com.unity.renderstreaming/Documentation~/index.md b/com.unity.renderstreaming/Documentation~/index.md index a97138aec..208e22856 100644 --- a/com.unity.renderstreaming/Documentation~/index.md +++ b/com.unity.renderstreaming/Documentation~/index.md @@ -37,10 +37,13 @@ This version of Render Streaming is compatible with the following versions of th - **Windows** - **Linux** -- **macOS** (**Apple Slicon** is not supported yet) +- **macOS** - **iOS** - **Android** (**ARMv7** is not supported) +> [!NOTE] +> This package depends on [the WebRTC package](https://docs.unity3d.com/Packages/com.unity.webrtc@3.0). If you build for mobile platform (iOS/Android), please see [the package documentation](https://docs.unity3d.com/Packages/com.unity.webrtc@3.0/manual/requirements.html#additional-notes) to know the requirements for building. + ### Browser support Unity Render Streaming supports almost all browsers that can use WebRTC. @@ -62,13 +65,8 @@ Unity Render Streaming supports almost all browsers that can use WebRTC. Please check [this page](samples.md). -## Project template - -Please check [this page](template.md). - ## Furioos compatibility -**Unity Render Streaming** is also supported natively by **Furioos** platform https://www.furioos.com/ . -That means that you can easily build a Unity application, upload it on **Furioos** and enjoy all the features of **Unity Render Streaming** without worrying about the deployment and scalability issues of your project. +Unity provides **[Furioos](https://www.furioos.com)** which is a web service to stream any 3D contents on any devices in real-time. -Please see [Furioos Tutorial](deploy-to-furioos.md) section to find out how it works. +This version of Unity Render Streaming doesn't support Furioos integration. diff --git a/com.unity.renderstreaming/Documentation~/template.md b/com.unity.renderstreaming/Documentation~/template.md deleted file mode 100644 index 0691b3d3d..000000000 --- a/com.unity.renderstreaming/Documentation~/template.md +++ /dev/null @@ -1,63 +0,0 @@ -# Project Template - -This document describes the way to start using the template package of Unity Render Streaming. - -### Download the Package - -Download the template package from [Github Release](https://github.com/Unity-Technologies/com.unity.webrtc/releases). Please download the package below. - -- `com.unity.template.renderstreaming-x.x.x-preview.tgz` - -![Download template package](images/download_template_package.png) - -> [!NOTE] -> This process made for temporary use. It is not needed if these packages are registered to PackageManager in the future. - -### Install the package - -Put the package in a dedicated folder in order to use them as a project template. -The project template can be selected as a template when creating a new Unity project. - -![Template unityhub](images/template_in_unityhub.png) - -When using the template package, put the tgz file in the following folders. -Move `com.unity.template.renderstreaming-x.x.x-preview.tgz` to this folder - -``` - /Data/Resources/PackageManager/ProjectTemplates -``` - -After moving the packages, open the Unity Hub and create a new project. Confirm that `Render Streaming` has been added as a template. - -### Launch the Web application - -To launch web application, Select an item **Edit / Render Streaming / Download web app** from the menu bar. - -![Menu download webapp](images/download_webapp.png) - -Be shown the command prompt when execute the file. Confirm that the following log displays in the command prompt. The web server's address should be displayed. Details on web server command options can be found on the [Web server](webserver.md) page. - -![Launch Web Server command](images/launch_webserver_public_mode_on_windows.png) - -The web server's source code is located in the `WebApp` folder. Additionally, [Node.js](https://nodejs.org) must be installed in order to run the source code. Download the installer from the website. - -### Unity Settings - -Open the `Assets/Scenes/samplescene.unity` sample scene. - -![HDRP scene](images/hdrpscene.png) - -Select `RenderStreaming` in the Hierarchy. Check the `Render Streaming` components in the Inspector. - -![Render Streaming inspector](images/renderstreaming_inspector.png) - -Set the address of the web server you just activated under the `URL Signaling` parameter. For details on the various settings in the Inspector. - -### Accessing the Web Page - -Launch one of the [supported browsers](../index.md) from the list. -Access the Web server's address. The following web page will be displayed. - -![Browser HDRP scene](images/browser_hdrpscene.png) - -If this doesn't happen, it's possible that the browser version is too old. Be sure to install the latest version. \ No newline at end of file From e1444d478a170d49e1627b0048ec6786bae19d01 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 16 Dec 2022 10:20:53 +0900 Subject: [PATCH 008/117] ci: Refactor script for CI jobs (#817) * refactor (cherry picked from commit b3229b12d883c708bdea1c639630245ca1f29495) * fix * fix * wip * fix * fix * fix * revert * change coverage package version --- .yamato/coverage.yml | 38 +++++ .yamato/package.metafile | 2 + .yamato/promotion.yml | 18 +-- .yamato/upm-ci-publish-github-release.yml | 1 - .yamato/upm-ci-renderstreaming-packages.yml | 166 ++++++++------------ .yamato/upm-ci-template.yml | 2 +- 6 files changed, 111 insertions(+), 116 deletions(-) create mode 100644 .yamato/coverage.yml diff --git a/.yamato/coverage.yml b/.yamato/coverage.yml new file mode 100644 index 000000000..7ec307cc5 --- /dev/null +++ b/.yamato/coverage.yml @@ -0,0 +1,38 @@ +{% metadata_file .yamato/package.metafile %} + +# todo(kazuki): Use old version because Code Coverage 1.2.2 has some issues. +coverage_pkg_version: 1.1.1 +--- + +{% for platform in platforms %} +{% if platform.name != "macos" and platform.name != "macos-m1" %} +{% for editor in editors %} +codecoverage_{{ packagename }}_{{ platform.name }}_{{ editor.version }}: + name: Code coverage {{ package_displayname }} {{ platform.name }} {{ editor.version }} + agent: + type: {{ platform.type }} + image: {{ platform.image }} + flavor: {{ platform.flavor }} + commands: + - pip config set global.index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple + - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple + - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} +{% if platform.name == "win" %} + - | + set WEBAPP_PATH=%cd%\Webapp\bin~\{{ platform.packed_webapp_name }} + upm-ci package test -u {{ editor.version }} --extra-utr-arg=--coverage-pkg-version={{ coverage_pkg_version }} --package-path {{ packagename }} --enable-code-coverage --code-coverage-options "generateAdditionalMetrics;generateHtmlReport;generateBadgeReport;assemblyFilters:-UnityEngine.*,+Unity.RenderStreaming" +{% else %} + - | + export WEBAPP_PATH=$(pwd)/WebApp/bin~/{{ platform.packed_webapp_name }} + upm-ci package test -u {{ editor.version }} --extra-utr-arg=--coverage-pkg-version={{ coverage_pkg_version }} --package-path {{ packagename }} --enable-code-coverage --code-coverage-options "generateAdditionalMetrics;generateHtmlReport;generateBadgeReport;assemblyFilters:-UnityEngine.*,+Unity.RenderStreaming" +{% endif %} + artifacts: + {{ packagename }}_{{ editor.version }}_{{ platform.name }}_coverage_results: + paths: + - "upm-ci~/test-results/**" + dependencies: + - .yamato/upm-ci-renderstreaming-packages.yml#pack + - .yamato/upm-ci-webapp.yml#pack_{{ platform.packed_webapp_platform }} +{% endfor %} +{% endif %} +{% endfor %} diff --git a/.yamato/package.metafile b/.yamato/package.metafile index 1d57d955c..0a427b6ff 100644 --- a/.yamato/package.metafile +++ b/.yamato/package.metafile @@ -2,6 +2,8 @@ upm: registry_url: https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-npm package_version: stable intra_pypi_url: https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple +packagename: com.unity.renderstreaming +package_displayname: Render Streaming editors: - version: 2020.3 - version: 2021.3 diff --git a/.yamato/promotion.yml b/.yamato/promotion.yml index f7795c711..d0d23da3a 100644 --- a/.yamato/promotion.yml +++ b/.yamato/promotion.yml @@ -5,12 +5,8 @@ test_platforms: type: Unity::VM image: package-ci/win10:v1.21.0-1103459 flavor: b1.large -packages: - - name: renderstreaming - packagename: com.unity.renderstreaming --- -{% for package in packages %} {% for editor in editors %} {% for platform in test_platforms %} promotion_test_{{ platform.name }}_{{ editor.version }}: @@ -23,13 +19,13 @@ promotion_test_{{ platform.name }}_{{ editor.version }}: UPMCI_PROMOTION: 1 commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} - - upm-ci package test --unity-version {{ editor.version }} --package-path {{ package.packagename }} --platform editmode --backend mono + - upm-ci package test --unity-version {{ editor.version }} --package-path {{ packagename }} --platform editmode --backend mono artifacts: logs: paths: - "upm-ci~/test-results/**/*" dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack {% endfor %} {% endfor %} @@ -43,7 +39,7 @@ promote_dry_run: UPMCI_PROMOTION: 1 commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} - - upm-ci package promote --dry-run --package-path {{ package.packagename }} + - upm-ci package promote --dry-run --package-path {{ packagename }} triggers: tags: only: @@ -53,7 +49,7 @@ promote_dry_run: paths: - "upm-ci~/packages/*.tgz" dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack {% for editor in editors %} {% if editor.version != "trunk" -%} # exclude trunk to test {% for platform in test_platforms %} @@ -72,7 +68,7 @@ promote: UPMCI_PROMOTION: 1 commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} - - upm-ci package promote --package-path {{ package.packagename }} + - upm-ci package promote --package-path {{ packagename }} triggers: tags: only: @@ -82,7 +78,7 @@ promote: paths: - "upm-ci~/packages/*.tgz" dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack {% for editor in editors %} {% if editor.version != "trunk" -%} # exclude trunk to test {% for platform in test_platforms %} @@ -90,5 +86,3 @@ promote: {% endfor %} {% endif %} {% endfor %} - -{% endfor %} diff --git a/.yamato/upm-ci-publish-github-release.yml b/.yamato/upm-ci-publish-github-release.yml index 406053110..b2cf62668 100644 --- a/.yamato/upm-ci-publish-github-release.yml +++ b/.yamato/upm-ci-publish-github-release.yml @@ -1,4 +1,3 @@ -# .yamato/upm-ci-publish-github-release.yml {% metadata_file .yamato/package.metafile %} webapp-platforms: diff --git a/.yamato/upm-ci-renderstreaming-packages.yml b/.yamato/upm-ci-renderstreaming-packages.yml index 2877be4a8..cfc6f15d6 100644 --- a/.yamato/upm-ci-renderstreaming-packages.yml +++ b/.yamato/upm-ci-renderstreaming-packages.yml @@ -1,12 +1,9 @@ {% metadata_file .yamato/package.metafile %} -packages: - - name: renderstreaming - packagename: com.unity.renderstreaming --- -{% for package in packages %} -pack_{{ package.name }}: - name: Pack {{ package.packagename }} + +pack: + name: Pack {{ package_displayname }} agent: type: Unity::VM image: package-ci/ubuntu-20:v1.4.0-1081009 @@ -14,15 +11,15 @@ pack_{{ package.name }}: commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} - find ./{{ project.packagename }} -type l -exec bash -c 'sh BuildScripts~/convert_symlinks.sh "$0"' {} \; - - upm-ci package pack --package-path {{ package.packagename }} + - upm-ci package pack --package-path {{ packagename }} artifacts: - {{ package.name }}_package: + {{ packagename }}_package: paths: - "upm-ci~/packages/**/*" {% for editor in editors %} -build_{{ package.name }}_{{ editor.version }}_ios: - name : Build {{ package.packagename }} with {{ editor.version }} for ios device +build_{{ editor.version }}_ios: + name : Build {{ package_displayname }} with {{ editor.version }} for ios device agent: type: Unity::VM::osx image: package-ci/mac:v1.20.0-1079282 @@ -42,10 +39,10 @@ build_{{ package.name }}_{{ editor.version }}_ios: paths: - "build/logs/**" dependencies: - - .yamato/upm-ci-{{ package.name }}-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack -test_{{ package.name }}_{{ editor.version }}_ios: - name: Test {{ package.packagename }} with {{ editor.version }} on ios device +test_{{ packagename }}_{{ editor.version }}_ios: + name: Test {{ package_displayname }} with {{ editor.version }} on ios device agent: type: Unity::mobile::iPhone image: mobile/macos-10.15-testing:v0.0.7-909915 @@ -63,11 +60,11 @@ test_{{ package.name }}_{{ editor.version }}_ios: paths: - "build/test-results/**" dependencies: - - .yamato/upm-ci-{{ package.name }}-packages.yml#build_{{ package.name }}_{{ editor.version }}_ios + - .yamato/upm-ci-renderstreaming-packages.yml#build_{{ editor.version }}_ios {% for target in test_targets_android %} -build_{{ package.name }}_{{ editor.version }}_android_{{ target.name }}: - name : Build {{ package.packagename }} with {{ editor.version }} for android device {{ target.name }} +build_{{ editor.version }}_android_{{ target.name }}: + name : Build {{ package_displayname }} with {{ editor.version }} for android device {{ target.name }} agent: type: Unity::VM::osx image: package-ci/mac:v1.20.0-1079282 @@ -92,17 +89,17 @@ build_{{ package.name }}_{{ editor.version }}_android_{{ target.name }}: paths: - "build/logs/**" dependencies: - - .yamato/upm-ci-{{ package.name }}-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack -test_{{ package.name }}_{{ editor.version }}_android_{{ target.name }}: - name: Test {{ package.packagename }} with {{ editor.version }} on android device {{ target.name }} +test_{{ packagename }}_{{ editor.version }}_android_{{ target.name }}: + name: Test {{ package_displayname }} with {{ editor.version }} on android device {{ target.name }} agent: type: Unity::mobile::shield image: mobile/android-package-ci-win:v0.1.4-1212670 flavor: b1.medium skip_checkout: true dependencies: - - .yamato/upm-ci-{{ package.name }}-packages.yml#build_{{ package.name }}_{{ editor.version }}_android_{{ target.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#build_{{ editor.version }}_android_{{ target.name }} commands: - wget http://artifactory-slo.bf.unity3d.com/artifactory/mobile-generic/android/ADBKeys.zip!/adbkey.pub -O %USERPROFILE%/.android/adbkey.pub - wget http://artifactory-slo.bf.unity3d.com/artifactory/mobile-generic/android/ADBKeys.zip!/adbkey -O %USERPROFILE%/.android/adbkey @@ -126,8 +123,8 @@ test_{{ package.name }}_{{ editor.version }}_android_{{ target.name }}: {% for platform in platforms %} {% if platform.name != "macos" and platform.name != "macos-m1" %} {% for param in platform.test_params %} -test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: - name : Test {{ package.packagename }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} +test_{{ packagename }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: + name : Test {{ package_displayname }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} agent: type: {{ platform.type }} image: {{ platform.image }} @@ -142,18 +139,18 @@ test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam {% if platform.name == "win" %} - | set WEBAPP_PATH=%cd%\Webapp\bin~\{{ platform.packed_webapp_name }} - upm-ci package test -u {{ editor.version }} --package-path {{ package.packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000" + upm-ci package test -u {{ editor.version }} --package-path {{ packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000" {% else %} - | export WEBAPP_PATH=$(pwd)/WebApp/bin~/{{ platform.packed_webapp_name }} - upm-ci package test -u {{ editor.version }} --package-path {{ package.packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000 --testfilter=!HttpSignaling" + upm-ci package test -u {{ editor.version }} --package-path {{ packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000 --testfilter=!HttpSignaling" {% endif %} artifacts: - {{ package.name }}_{{ editor.version }}_{{ platform.name }}_test_results: + {{ packagename }}_{{ editor.version }}_{{ platform.name }}_test_results: paths: - "upm-ci~/test-results/**/*" dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack - .yamato/upm-ci-webapp.yml#pack_{{ platform.packed_webapp_platform }} {% for project in test_projects %} @@ -171,7 +168,7 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} {% if platform.name != "win" %} - - find ./{{ package.packagename }} -type l -exec bash -c 'sh BuildScripts~/convert_symlinks.sh "$0"' {} \; + - find ./{{ packagename }} -type l -exec bash -c 'sh BuildScripts~/convert_symlinks.sh "$0"' {} \; {% endif %} - upm-ci project pack --project-path {{ project.path }} {% if platform.name == "win" %} @@ -184,7 +181,7 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam upm-ci project test -u {{ editor.version }} --project-path {{ project.path }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000 --testfilter=!HttpSignaling" {% endif %} artifacts: - {{ package.name }}_{{ editor.version }}_{{ platform.name }}_test_results: + {{ packagename }}_{{ editor.version }}_{{ platform.name }}_test_results: paths: - "upm-ci~/test-results/**/*" dependencies: @@ -197,8 +194,8 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam {% if param.platform == "standalone" %} # platform.name == "macos" or platform.name == "macos-m1" and param.platform == "standalone" -build_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: - name : Build {{ package.packagename }} with {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} +build_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: + name : Build {{ package_displayname }} with {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} agent: type: Unity::VM::osx image: package-ci/mac:v1.20.0-1079282 @@ -219,10 +216,10 @@ build_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.na paths: - "build/logs/**" dependencies: - - .yamato/upm-ci-{{ package.name }}-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack -test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: - name : Test {{ package.packagename }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} +test_{{ packagename }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: + name : Test {{ package_displayname }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} agent: type: {{ platform.type }} image: {{ platform.image }} @@ -242,17 +239,17 @@ test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam commands: - BuildScripts~/test_package_mac.sh artifacts: - {{ package.name }}_{{ param.backend }}_{{ editor.version }}_{{ platform.name }}_test_results: + {{ packagename }}_{{ param.backend }}_{{ editor.version }}_{{ platform.name }}_test_results: paths: - "upm-ci~/test-results/**" dependencies: - - .yamato/upm-ci-{{ package.name }}-packages.yml#build_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#build_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} - .yamato/upm-ci-webapp.yml#pack_{{ platform.packed_webapp_platform }} {% else %} # platform.name == "macos" or platform.name == "macos-m1" and param.platform != "standalone" -test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: - name : Test {{ package.packagename }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} +test_{{ packagename }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: + name : Test {{ package_displayname }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} agent: type: {{ platform.type }} image: {{ platform.image }} @@ -274,11 +271,11 @@ test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam - find upm-ci~/packages/ -name "*.tgz" | xargs -I file tar xvf file -C upm-ci~ - BuildScripts~/test_package_mac.sh artifacts: - {{ package.name }}_{{ param.backend }}_{{ editor.version }}_{{ platform.name }}_test_results: + {{ packagename }}_{{ param.backend }}_{{ editor.version }}_{{ platform.name }}_test_results: paths: - "upm-ci~/test-results/**" dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack - .yamato/upm-ci-webapp.yml#pack_{{ platform.packed_webapp_platform }} {% endif %} @@ -303,10 +300,10 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam EDITOR_VERSION: {{ editor.version }} EXTRA_UTR_ARG: --timeout=3000 --testfilter=!HttpSignaling commands: - - find ./{{ package.packagename }} -type l -exec bash -c 'sh BuildScripts~/convert_symlinks.sh "$0"' {} \; + - find ./{{ packagename }} -type l -exec bash -c 'sh BuildScripts~/convert_symlinks.sh "$0"' {} \; - BuildScripts~/test_package_mac.sh artifacts: - {{ package.name }}_{{ editor.version }}_{{ platform.name }}_test_results: + {{ packagename }}_{{ editor.version }}_{{ platform.name }}_test_results: paths: - "upm-ci~/test-results/**" dependencies: @@ -316,17 +313,17 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam {% endif -%} {% endfor %} -trigger_test_{{ package.name }}_{{ editor.version }}: - name : Trigger test {{ package.packagename }} {{ editor.version }} all platforms +trigger_test_{{ packagename }}_{{ editor.version }}: + name : Trigger test {{ package_displayname }} {{ editor.version }} all platforms dependencies: - - .yamato/upm-ci-{{ package.name }}-packages.yml#test_{{ package.name }}_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_{{ editor.version }} {% if editor.version == "2020.3" -%} triggers: expression: pull_request.target eq "main" {% endif -%} -test_{{ package.name }}_{{ editor.version }}: - name : Test {{ package.packagename }} {{ editor.version }} all platforms +test_{{ packagename }}_{{ editor.version }}: + name : Test {{ package_displayname }} {{ editor.version }} all platforms dependencies: {% for platform in platforms %} {% for param in platform.test_params %} @@ -335,116 +332,81 @@ test_{{ package.name }}_{{ editor.version }}: # XCode command line tools has not installed on m1 mac device (Standalone test don't need to install them) {% if platform.name == "macos-m1" %} {% if param.platform == "standalone" %} - - .yamato/upm-ci-{{ package.name }}-packages.yml#test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} {% endif %} {% else %} - - .yamato/upm-ci-{{ package.name }}-packages.yml#test_{{ package.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} {% endif %} {% endfor %} {% endfor %} - - .yamato/upm-ci-{{ package.name }}-packages.yml#test_{{ package.name }}_{{ editor.version }}_ios + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_{{ editor.version }}_ios {% for target in test_targets_android %} # todo: Exclude tests for Unity 2022.1 on Android platform because it is instable. {% if editor.version != "2022.1" %} - - .yamato/upm-ci-{{ package.name }}-packages.yml#test_{{ package.name }}_{{ editor.version }}_android_{{ target.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_{{ editor.version }}_android_{{ target.name }} {% endif %} {% endfor %} -test_renderpipeline_{{ package.name }}_{{ editor.version }}: - name : test {{ package.packagename }} {{ editor.version }} all RenderPipeline +test_renderpipeline_{{ packagename }}_{{ editor.version }}: + name : Test {{ package_displayname }} {{ editor.version }} all RenderPipeline dependencies: {% for platform in platforms %} {% for param in platform.test_params %} {% for project in test_projects %} - - .yamato/upm-ci-{{ package.name }}-packages.yml#test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} {% endfor %} {% endfor %} {% endfor %} {% endfor %} -publish_dry_run_{{ package.name }}: - name: Publish Dry Run {{ package.packagename }} +publish_dry_run_{{ packagename }}: + name: Publish Dry Run {{ package_displayname }} agent: type: Unity::VM image: package-ci/win10:v1.21.0-1103459 flavor: b1.large commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} - - upm-ci package publish --dry-run --package-path {{ package.packagename }} + - upm-ci package publish --dry-run --package-path {{ packagename }} triggers: tags: only: - /^(r|R)(c|C)-\d+\.\d+\.\d+(-preview(\.\d+)?)?$/ artifacts: - {{ package.name }}_artifacts.zip: + {{ packagename }}_artifacts.zip: paths: - "upm-ci~/packages/*.tgz" dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack {% for editor in editors %} {% if editor.version != "trunk" -%} # exclude trunk to test - - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ package.name }}_editmode_mono_win_{{ editor.version }} - - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ package.name }}_editmode_mono_macos_{{ editor.version }} - - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ package.name }}_editmode_mono_linux_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_win_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_macos_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_linux_{{ editor.version }} {% endif %} {% endfor %} -publish_{{ package.name }}: - name: Publish {{ package.packagename }} +publish_{{ packagename }}: + name: Publish {{ package_displayname }} agent: type: Unity::VM image: package-ci/win10:v1.21.0-1103459 flavor: b1.large commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} - - upm-ci package publish --package-path {{ package.packagename }} + - upm-ci package publish --package-path {{ packagename }} triggers: tags: only: - /^(r|R)(c|C)-\d+\.\d+\.\d+(-preview(\.\d+)?)?$/ artifacts: - {{ package.name }}_artifacts.zip: + {{ packagename }}_artifacts.zip: paths: - "upm-ci~/packages/*.tgz" dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} + - .yamato/upm-ci-renderstreaming-packages.yml#pack {% for editor in editors %} {% if editor.version != "trunk" %} # exclude trunk to test - - .yamato/upm-ci-renderstreaming-packages.yml#trigger_test_{{ package.name }}_{{ editor.version }} -{% endif %} -{% endfor %} - -{% for platform in platforms %} -{% if platform.name != "macos" and platform.name != "macos-m1" %} -{% for editor in editors %} -codecoverage_{{ package.packagename }}_{{ platform.name }}_{{ editor.version }}: - name: Code coverage {{ package.packagename }} {{ platform.name }} {{ editor.version }} - agent: - type: {{ platform.type }} - image: {{ platform.image }} - flavor: {{ platform.flavor }} - commands: - - pip config set global.index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} -{% if platform.name == "win" %} - - | - set WEBAPP_PATH=%cd%\Webapp\bin~\{{ platform.packed_webapp_name }} - upm-ci package test -u {{ editor.version }} --package-path {{ package.packagename }} --enable-code-coverage --code-coverage-options "generateAdditionalMetrics;generateHtmlReport;generateBadgeReport;assemblyFilters:-UnityEngine.*,+Unity.RenderStreaming" --extra-utr-arg="--timeout=3000" -{% else %} - - | - export WEBAPP_PATH=$(pwd)/WebApp/bin~/{{ platform.packed_webapp_name }} - upm-ci package test -u {{ editor.version }} --package-path {{ package.packagename }} --enable-code-coverage --code-coverage-options "generateAdditionalMetrics;generateHtmlReport;generateBadgeReport;assemblyFilters:-UnityEngine.*,+Unity.RenderStreaming" --extra-utr-arg="--timeout=3000" + - .yamato/upm-ci-renderstreaming-packages.yml#trigger_test_{{ packagename }}_{{ editor.version }} {% endif %} - artifacts: - {{ package.name }}_{{ editor.version }}_{{ platform.name }}_coverage_results: - paths: - - "upm-ci~/test-results/**" - dependencies: - - .yamato/upm-ci-renderstreaming-packages.yml#pack_{{ package.name }} - - .yamato/upm-ci-webapp.yml#pack_{{ platform.packed_webapp_platform }} -{% endfor %} -{% endif %} -{% endfor %} - {% endfor %} diff --git a/.yamato/upm-ci-template.yml b/.yamato/upm-ci-template.yml index 828256740..91e0b0ab3 100644 --- a/.yamato/upm-ci-template.yml +++ b/.yamato/upm-ci-template.yml @@ -1,7 +1,7 @@ -# .yamato/upm-ci-template.yml {% metadata_file .yamato/package.metafile %} --- + {% for project in template_projects %} {% for editor in editors %} From 2dd1d8bcb5f619cb01d2dd9006381322c06125a2 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 16 Dec 2022 17:21:29 +0900 Subject: [PATCH 009/117] ci: Add CI jobs which is package isolation test for detecting issues (#806) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enable testing in true isolation We are experiencing a hang in our repo where we are building API Validation assemblies in which we take your latest promoted package `com.unity.renderstreaming@3.1.0-exp.4` (not happening with `3.1.0-exp.3`). The script imports the package with the Windows Unity Editor for the version you have specified in the `unity` field of your package (i.e. `2020.3`). We think it's because the use of `SynchronizationContext` are not properly cleaned up after and Unity still waits for them. Unfortunately this doesn't reproduce when we run the same commands on the command line but this PR should, in theory, reproduce the same hang on your repo as well. Enabling testing in true isolation is considered a good practice since this way you should be able to spot also missing dependencies since we are loading the package in a empty project and it should compile without errors. We were also wondering the reason you added that huge --timeout arg to utr if it's related to this issue but we were unable to find any job that exceeded reasonable timings. * add job for testing on Windows without GPU * hopefully this will reproduce the issue (cherry picked from commit 2583fbf5f484f734eb316c8758dbd38c96233ffd) * add job * fix * add dependencies * fix * fix * fix * fix bug * fix * fix issues * update dependency Co-authored-by: Mihai Popescu <57262907+mihai-unity@users.noreply.github.com> --- .yamato/compile-package.yml | 49 +++++++++++++++++++ .yamato/package.metafile | 19 ++++++- .yamato/upm-ci-renderstreaming-packages.yml | 32 ++++++------ .yamato/upm-ci-template.yml | 20 ++++---- RenderStreaming~/Packages/packages-lock.json | 11 +++-- .../Unity.RenderStreaming.Runtime.asmdef | 1 + com.unity.renderstreaming/package.json | 6 ++- 7 files changed, 104 insertions(+), 34 deletions(-) create mode 100644 .yamato/compile-package.yml diff --git a/.yamato/compile-package.yml b/.yamato/compile-package.yml new file mode 100644 index 000000000..c33dd3c1e --- /dev/null +++ b/.yamato/compile-package.yml @@ -0,0 +1,49 @@ +compile_test_for_package_version: + name: Compilation Test for Package Version + agent: + type: Unity::VM + flavor: b1.large + image: package-ci/win10:v4 + variables: + VERSION: 3.1.0-exp.4 + commands: + # When unity-config will be part of the image, this will turn into a no-op + - | + where /q unity-config + if ERRORLEVEL 1 ( + %GSUDO% choco install unity-config -y -s https://artifactory.prd.it.unity3d.com/artifactory/api/nuget/unity-choco-local + ) + - unity-downloader-cli -c editor -u 2020.3 --wait + - .Editor\Unity.exe -createProject CompilationTestProject -logFile logs\CreateProject.log -batchmode -quit + - | + unity-config project set registry --project-path CompilationTestProject candidates + unity-config project add dependency --project-path CompilationTestProject com.unity.renderstreaming@%VERSION% + - .Editor\Unity.exe -projectPath CompilationTestProject -logFile logs\CompilePackage.log -batchmode -quit + artifacts: + logs: + paths: + - logs/* + +compile_test_for_local_path: + name: Compilation Test for Local Path + agent: + type: Unity::VM + flavor: b1.large + image: package-ci/win10:v4 + commands: + # When unity-config will be part of the image, this will turn into a no-op + - | + where /q unity-config + if ERRORLEVEL 1 ( + %GSUDO% choco install unity-config -y -s https://artifactory.prd.it.unity3d.com/artifactory/api/nuget/unity-choco-local + ) + - unity-downloader-cli -c editor -u 2020.3 --wait + - .Editor\Unity.exe -createProject CompilationTestProject -logFile logs\CreateProject.log -batchmode -quit + - | + unity-config project set registry --project-path CompilationTestProject candidates + unity-config project add dependency --project-path CompilationTestProject .\com.unity.renderstreaming + - .Editor\Unity.exe -projectPath CompilationTestProject -logFile logs\CompilePackage.log -batchmode -quit + artifacts: + logs: + paths: + - logs/* diff --git a/.yamato/package.metafile b/.yamato/package.metafile index 0a427b6ff..9d84f7c92 100644 --- a/.yamato/package.metafile +++ b/.yamato/package.metafile @@ -10,7 +10,7 @@ editors: - version: 2022.1 - version: trunk platforms: - - name: win + - name: win-gpu type: Unity::VM::GPU image: renderstreaming/win10:v0.3.13-1084240 flavor: b1.large @@ -29,6 +29,21 @@ platforms: platform: standalone - backend: il2cpp platform: standalone + - name: win + type: Unity::VM + image: renderstreaming/win10:v0.3.13-1084239 + flavor: b1.large + packed_webapp_name: webserver.exe + packed_webapp_platform: win + test_params: + - backend: mono + platform: editmode + - backend: mono + platform: playmode + - backend: mono + platform: standalone + - backend: il2cpp + platform: standalone - name: macos type: Unity::metal::macmini image: package-ci/mac:v1.20.0-1079282 @@ -70,7 +85,7 @@ platforms: # - backend: il2cpp # additional_component_arg: StandaloneSupport-IL2CPP # platform: standalone - - name: linux + - name: linux-gpu type: Unity::VM::GPU image: renderstreaming/ubuntu:v0.2.4-1104053 flavor: b1.large diff --git a/.yamato/upm-ci-renderstreaming-packages.yml b/.yamato/upm-ci-renderstreaming-packages.yml index cfc6f15d6..c0ec27610 100644 --- a/.yamato/upm-ci-renderstreaming-packages.yml +++ b/.yamato/upm-ci-renderstreaming-packages.yml @@ -73,9 +73,9 @@ build_{{ editor.version }}_android_{{ target.name }}: - | find upm-ci~/packages/ -name "*.tgz" | xargs -I file tar xvf file -C upm-ci~ cp -rf upm-ci~/package/Runtime/Plugins Runtime/ -{% if target.name == "vulkan" -%} +{% if target.name == "vulkan" %} cp -f TestProjects/Empty/ProjectSettings/ProjectSettings-android-vulkan.asset TestProjects/Empty/ProjectSettings/ProjectSettings.asset -{% endif -%} +{% endif %} - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - unity-downloader-cli -c Editor -c Android -u {{ editor.version }} --fast -w - curl -s https://artifactory.prd.it.unity3d.com/artifactory/unity-tools/utr-standalone/utr --output utr @@ -136,14 +136,14 @@ test_{{ packagename }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name - pip config set global.index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} -{% if platform.name == "win" %} +{% if platform.name == "win" or platform.name == "win-gpu" %} - | set WEBAPP_PATH=%cd%\Webapp\bin~\{{ platform.packed_webapp_name }} - upm-ci package test -u {{ editor.version }} --package-path {{ packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000" + upm-ci package test -u {{ editor.version }} --package-path {{ packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --enable-load-and-test-isolation {% else %} - | export WEBAPP_PATH=$(pwd)/WebApp/bin~/{{ platform.packed_webapp_name }} - upm-ci package test -u {{ editor.version }} --package-path {{ packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000 --testfilter=!HttpSignaling" + upm-ci package test -u {{ editor.version }} --package-path {{ packagename }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--testfilter=!HttpSignaling" --enable-load-and-test-isolation {% endif %} artifacts: {{ packagename }}_{{ editor.version }}_{{ platform.name }}_test_results: @@ -167,18 +167,18 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam - pip config set global.index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - pip install unity-downloader-cli --index-url https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi/simple - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} -{% if platform.name != "win" %} +{% if platform.name != "win" and platform.name != "win-gpu" %} - find ./{{ packagename }} -type l -exec bash -c 'sh BuildScripts~/convert_symlinks.sh "$0"' {} \; {% endif %} - upm-ci project pack --project-path {{ project.path }} -{% if platform.name == "win" %} +{% if platform.name == "win" or platform.name == "win-gpu" %} - | set WEBAPP_PATH=%cd%\Webapp\bin~\{{ platform.packed_webapp_name }} - upm-ci project test -u {{ editor.version }} --project-path {{ project.path }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000" + upm-ci project test -u {{ editor.version }} --project-path {{ project.path }} --platform {{ param.platform }} --backend {{ param.backend }} {% else %} - | export WEBAPP_PATH=$(pwd)/WebApp/bin~/{{ platform.packed_webapp_name }} - upm-ci project test -u {{ editor.version }} --project-path {{ project.path }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--timeout=3000 --testfilter=!HttpSignaling" + upm-ci project test -u {{ editor.version }} --project-path {{ project.path }} --platform {{ param.platform }} --backend {{ param.backend }} --extra-utr-arg="--testfilter=!HttpSignaling" {% endif %} artifacts: {{ packagename }}_{{ editor.version }}_{{ platform.name }}_test_results: @@ -207,7 +207,7 @@ build_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.ver - unity-downloader-cli -c Editor -c {{ param.additional_component_arg }} -u {{ editor.version }} --fast -w - curl -s https://artifactory.prd.it.unity3d.com/artifactory/unity-tools/utr-standalone/utr --output utr - chmod +x ./utr - - ./utr --suite=playmode --platform=StandaloneOSX --editor-location=.Editor --testproject=TestProjects/Empty --player-save-path=build/players --architecture=x64 --artifacts_path=build/logs --scripting-backend={{ param.backend }} --build-only --timeout=3000 --testfilter=!HttpSignaling + - ./utr --suite=playmode --platform=StandaloneOSX --editor-location=.Editor --testproject=TestProjects/Empty --player-save-path=build/players --architecture=x64 --artifacts_path=build/logs --scripting-backend={{ param.backend }} --build-only --testfilter=!HttpSignaling artifacts: players: paths: @@ -266,7 +266,7 @@ test_{{ packagename }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name TEST_ARCHITECTURE: {{ platform.architecture }} SCRIPTING_BACKEND: {{ param.backend }} EDITOR_VERSION: {{ editor.version }} - EXTRA_UTR_ARG: --timeout=3000 --testfilter=!HttpSignaling + EXTRA_UTR_ARG: --testfilter=!HttpSignaling commands: - find upm-ci~/packages/ -name "*.tgz" | xargs -I file tar xvf file -C upm-ci~ - BuildScripts~/test_package_mac.sh @@ -298,7 +298,7 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam TEST_PLATFORM: {{ param.platform }} SCRIPTING_BACKEND: {{ param.backend }} EDITOR_VERSION: {{ editor.version }} - EXTRA_UTR_ARG: --timeout=3000 --testfilter=!HttpSignaling + EXTRA_UTR_ARG: --testfilter=!HttpSignaling commands: - find ./{{ packagename }} -type l -exec bash -c 'sh BuildScripts~/convert_symlinks.sh "$0"' {} \; - BuildScripts~/test_package_mac.sh @@ -310,7 +310,7 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam - .yamato/upm-ci-webapp.yml#pack_{{ platform.packed_webapp_platform }} {% endfor %} {% endfor %} -{% endif -%} +{% endif %} {% endfor %} trigger_test_{{ packagename }}_{{ editor.version }}: @@ -320,7 +320,7 @@ trigger_test_{{ packagename }}_{{ editor.version }}: {% if editor.version == "2020.3" -%} triggers: expression: pull_request.target eq "main" -{% endif -%} +{% endif %} test_{{ packagename }}_{{ editor.version }}: name : Test {{ package_displayname }} {{ editor.version }} all platforms @@ -380,9 +380,9 @@ publish_dry_run_{{ packagename }}: - .yamato/upm-ci-renderstreaming-packages.yml#pack {% for editor in editors %} {% if editor.version != "trunk" -%} # exclude trunk to test - - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_win_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_win-gpu_{{ editor.version }} - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_macos_{{ editor.version }} - - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_linux_{{ editor.version }} + - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_editmode_mono_linux-gpu_{{ editor.version }} {% endif %} {% endfor %} diff --git a/.yamato/upm-ci-template.yml b/.yamato/upm-ci-template.yml index 91e0b0ab3..8fc8239e4 100644 --- a/.yamato/upm-ci-template.yml +++ b/.yamato/upm-ci-template.yml @@ -5,7 +5,7 @@ {% for project in template_projects %} {% for editor in editors %} -{% if editor.version == "2020.3" -%} +{% if editor.version == "2020.3" %} prepack_{{ project.name }}_{{ editor.version }}: name: Pre-Pack {{ project.packagename }} {{ editor.version }} - Primed Artifacts @@ -44,7 +44,7 @@ pack_{{ project.name }}_{{ editor.version }}: - "upm-ci~/**/*" {% for platform in platforms %} -{% if platform.name == "win" -%} +{% if platform.name == "win" or platform.name == "win-gpu" %} {% for param in platform.test_params %} test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: name : Test {{ project.packagename }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} @@ -66,9 +66,9 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam - .yamato/upm-ci-template.yml#pack_{{ project.name }}_{{ editor.version }} - .yamato/upm-ci-webapp.yml#pack_{{ platform.name }} {% endfor %} -{% else -%} +{% else %} {% for param in platform.test_params %} -{% if project.name != "renderstreaming-rtx" -%} +{% if project.name != "renderstreaming-rtx" %} test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }}: name : Test {{ project.packagename }} {{ param.platform }} {{ param.backend }} {{ editor.version }} on {{ platform.name }} agent: @@ -85,29 +85,29 @@ test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.nam dependencies: - .yamato/upm-ci-template.yml#pack_{{ project.name }}_{{ editor.version }} - .yamato/upm-ci-webapp.yml#pack_{{ platform.name }} -{% endif -%} +{% endif %} {% endfor %} -{% endif -%} +{% endif %} {% endfor %} trigger_template_test_{{ project.name }}_{{ editor.version }}: name : Trigger all Template test {{ project.packagename }} {{ editor.version }} dependencies: {% for platform in platforms %} - {% if platform.name == "win" -%} + {% if platform.name == "win" or platform.name == "win-gpu" %} {% for param in platform.test_params %} - .yamato/upm-ci-template.yml#test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} {% endfor %} - {% else -%} + {% else %} {% if project.name != "renderstreaming-rtx" %} {% for param in platform.test_params %} - .yamato/upm-ci-template.yml#test_{{ project.name }}_{{ param.platform }}_{{ param.backend }}_{{ platform.name }}_{{ editor.version }} {% endfor %} {% endif %} - {% endif -%} + {% endif %} {% endfor %} -{% endif -%} +{% endif %} {% endfor %} publish_{{ project.name }}: diff --git a/RenderStreaming~/Packages/packages-lock.json b/RenderStreaming~/Packages/packages-lock.json index d995254ed..8ff3a71cb 100644 --- a/RenderStreaming~/Packages/packages-lock.json +++ b/RenderStreaming~/Packages/packages-lock.json @@ -53,8 +53,10 @@ "depth": 0, "source": "embedded", "dependencies": { - "com.unity.webrtc": "2.4.0-exp.11", - "com.unity.inputsystem": "1.4.4" + "com.unity.webrtc": "3.0.0-pre.3", + "com.unity.inputsystem": "1.4.4", + "com.unity.ugui": "1.0.0", + "com.unity.modules.screencapture": "1.0.0" } }, "com.unity.settings-manager": { @@ -104,12 +106,13 @@ } }, "com.unity.webrtc": { - "version": "2.4.0-exp.11", + "version": "3.0.0-pre.3", "depth": 1, "source": "registry", "dependencies": { "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.editorcoroutines": "1.0.0" + "com.unity.editorcoroutines": "1.0.0", + "com.unity.modules.audio": "1.0.0" }, "url": "https://packages.unity.com" }, diff --git a/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef b/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef index 68ad48530..3f807a144 100644 --- a/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef +++ b/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef @@ -4,6 +4,7 @@ "references": [ "Unity.InputSystem", "Unity.WebRTC", + "UnityEngine.UI", "Unity.XR.ARSubsystems", "Unity.TextMeshPro", "Unity.RenderPipelines.HighDefinition.Runtime", diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index 60ad1eb1b..57ccb6084 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -5,8 +5,10 @@ "unity": "2020.3", "description": "This is a package for using Unity Render Streaming technology. It contains two samples to use the technology.", "dependencies": { - "com.unity.webrtc": "3.0.0-pre.2", - "com.unity.inputsystem": "1.4.4" + "com.unity.webrtc": "3.0.0-pre.3", + "com.unity.inputsystem": "1.4.4", + "com.unity.ugui": "1.0.0", + "com.unity.modules.screencapture": "1.0.0" }, "samples": [ { From af7df8044a8328aff6c2f4f94cd602e9f7119974 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Mon, 19 Dec 2022 11:23:00 +0900 Subject: [PATCH 010/117] chore: Fix package description (#818) --- com.unity.renderstreaming/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index 57ccb6084..b3ce2f8af 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -3,7 +3,7 @@ "displayName": "Unity Render Streaming", "version": "3.1.0-exp.5", "unity": "2020.3", - "description": "This is a package for using Unity Render Streaming technology. It contains two samples to use the technology.", + "description": "This is a package for using Unity Render Streaming technology.", "dependencies": { "com.unity.webrtc": "3.0.0-pre.3", "com.unity.inputsystem": "1.4.4", From c62166cd3e6109fe88b9350ca20f078e5fb77b0a Mon Sep 17 00:00:00 2001 From: Brian Harrison <92394761+BrianHarrisonUnity@users.noreply.github.com> Date: Wed, 21 Dec 2022 18:04:45 -0700 Subject: [PATCH 011/117] fix: Fix sessions lingering after they were timed out (#819) For checkSessionId, there was a bug where it wasn't using the map syntax for setting the value. Due to this, _checkDeletedSession wouldn't check if sessions were timedout. In addition, posting offers after a connection was established would replace the connectionPair. When the connectionPair is replaced, the code that checks for deleted sessions would no longer be checking the timedout session. Resending offers occur if a PeerConnection goes into the disconnected state. --- WebApp/src/class/httphandler.ts | 18 ++++++---- WebApp/test/httphandler.test.ts | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/WebApp/src/class/httphandler.ts b/WebApp/src/class/httphandler.ts index 2b1e33cbf..e61ba812e 100644 --- a/WebApp/src/class/httphandler.ts +++ b/WebApp/src/class/httphandler.ts @@ -67,8 +67,8 @@ function checkSessionId(req: Request, res: Response, next): void { if (!clients.has(id)) { res.sendStatus(404); return; - } - lastRequestedTime[id] = Date.now(); + } + lastRequestedTime.set(id, Date.now()); next(); } @@ -124,11 +124,11 @@ function _checkDeletedSession(sessionId: string): void { if (pair == null) { continue; } - const otherSessionId = sessionId === pair[0] ? pair[1] : pair[0]; - if(!lastRequestedTime.has(otherSessionId)) - continue; - if(lastRequestedTime[otherSessionId] > Date.now() - TimeoutRequestedTime) + const otherSessionId = sessionId === pair[0] ? pair[1] : pair[0]; + if(!lastRequestedTime.has(otherSessionId)) continue; + if(lastRequestedTime.get(otherSessionId) > Date.now() - TimeoutRequestedTime) + continue; _deleteSession(otherSessionId); console.log("deleted"); } @@ -342,7 +342,11 @@ function postOffer(req: Request, res: Response): void { return; } - connectionPair.set(connectionId, [sessionId, null]); + if(!connectionPair.has(connectionId)) + { + connectionPair.set(connectionId, [sessionId, null]); + } + keySessionId = sessionId; const map = offers.get(keySessionId); map.set(connectionId, new Offer(req.body.sdp, Date.now(), polite)); diff --git a/WebApp/test/httphandler.test.ts b/WebApp/test/httphandler.test.ts index db58f9952..b7e50f5db 100644 --- a/WebApp/test/httphandler.test.ts +++ b/WebApp/test/httphandler.test.ts @@ -1,6 +1,8 @@ import { getMockReq, getMockRes } from '@jest-mock/express'; import * as httpHandler from '../src/class/httphandler'; +const RetriesToForceTimeout = 11; // Waits a second each time, timeout is 10 sec for httphandler. + describe('http signaling test in public mode', () => { const sessionId = "abcd1234"; const sessionId2 = "abcd5678"; @@ -18,6 +20,8 @@ describe('http signaling test in public mode', () => { beforeEach(() => { mockClear(); + httpHandler.checkSessionId(req, res, next); + httpHandler.checkSessionId(req2, res, next); }); test('throw check has session', async () => { @@ -184,6 +188,63 @@ describe('http signaling test in public mode', () => { await httpHandler.deleteSession(req2, res); }); + + test('Timed out session2 deleted after session1 resends offer', async () => { + httpHandler.reset("public"); + + await httpHandler.createSession(sessionId, res); + await httpHandler.createSession(sessionId2, res); + + req.url = ""; + req2.url = ""; + await httpHandler.checkSessionId(req, res, next); + await httpHandler.checkSessionId(req2, res, next); + + await httpHandler.getAll(req, res); + expect(res.json).toHaveBeenLastCalledWith({ messages: [] }); + + const connectBody = { connectionId: connectionId }; + req.body = connectBody; + await httpHandler.createConnection(req, res); + + const offerBody = { connectionId: connectionId, sdp: testsdp, datetime: expect.anything(), type: "offer" }; + req.body = offerBody; + await httpHandler.postOffer(req, res); + + const offer = { connectionId: connectionId, sdp: testsdp, datetime: expect.anything(), type: "offer", polite: false }; + await httpHandler.getAll(req, res); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]) }); + await httpHandler.getAll(req2, res); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]) }); + + const answerBody = { connectionId: connectionId, sdp: testsdp }; + req2.body = answerBody; + await httpHandler.postAnswer(req2, res); + + // resend offer after answer to simulate PeerCandidate entering into failed state + req.body = offerBody; + await httpHandler.postOffer(req, res); + + // Wait a second and then checkSession for only session1 to force timeout of session2. + for (let i = 0; i < RetriesToForceTimeout + 1; ++i) + { + await httpHandler.checkSessionId(req, res, next); + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + // Get all for session1 to trigger cleaning up associated session that timed out. + await httpHandler.getAll(req, res); + + // Check that we do have session1 still + await httpHandler.checkSessionId(req, res, next); + expect(res.sendStatus).toHaveBeenLastCalledWith(200); + + // Check that we no longer have session2 + await httpHandler.checkSessionId(req2, res, next); + expect(res.sendStatus).toHaveBeenLastCalledWith(404); + + await httpHandler.deleteSession(req, res); + }, 16000); }); describe('http signaling test in private mode', () => { @@ -202,6 +263,9 @@ describe('http signaling test in private mode', () => { beforeEach(() => { mockClear(); + + httpHandler.checkSessionId(req, res, next); + httpHandler.checkSessionId(req2, res, next); }); test('throw check has session', async () => { From af278a497ebb136b6de09544cde5331107ace449 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 22 Dec 2022 18:05:07 +0900 Subject: [PATCH 012/117] fix: Fix option title of av1 video codec on inspector window (#820) --- .../Editor/PropertyDrawers/CodecDrawer.cs | 38 ++++++++++---- .../Runtime/Scripts/VideoCodecInfo.cs | 50 +++++++++++++++++++ .../Samples~/Example/Scripts/SceneSelectUI.cs | 2 + 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/CodecDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/CodecDrawer.cs index 88b4e73a9..a51e37f9a 100644 --- a/com.unity.renderstreaming/Editor/PropertyDrawers/CodecDrawer.cs +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/CodecDrawer.cs @@ -19,6 +19,7 @@ interface Codec int channelCount { get; } int sampleRate { get; } string optionTitle { get; } + int order { get; } } class AudioCodec : Codec @@ -37,6 +38,8 @@ public string optionTitle } } + public int order { get { return codec_.channelCount; } } + public AudioCodec(AudioCodecInfo codec) { codec_ = codec; @@ -53,13 +56,14 @@ public string optionTitle { get { - if (codec_ is H264CodecInfo h264Codec) - { - return $"{h264Codec.profile} Profile, Level {h264Codec.level.ToString().Insert(1, ".")}"; - } - else if (codec_ is VP9CodecInfo vp9codec) + switch(codec_) { - return $"Profile {(int)vp9codec.profile}"; + case H264CodecInfo h264Codec: + return $"{h264Codec.profile} Profile, Level {h264Codec.level.ToString().Insert(1, ".")}"; + case VP9CodecInfo vp9codec: + return $"Profile {(int)vp9codec.profile}"; + case AV1CodecInfo av1codec: + return $"Profile {(int)av1codec.profile}"; } return null; } @@ -67,11 +71,27 @@ public string optionTitle public int channelCount { get { throw new NotSupportedException(); } } public int sampleRate { get { throw new NotSupportedException(); } } - + public int order + { + get + { + switch (codec_) + { + case H264CodecInfo h264Codec: + return (int)h264Codec.profile; + case VP9CodecInfo vp9codec: + return (int)vp9codec.profile; + case AV1CodecInfo av1codec: + return (int)av1codec.profile; + } + return 0; + } + } public VideoCodec(VideoCodecInfo codec) { codec_ = codec; } + VideoCodecInfo codec_; } @@ -158,7 +178,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten codecNames = codecNames.Concat(codecs.Select(codec => codec.name)).Distinct().ToArray(); var mimeType = propertyMimeType.stringValue; var codecName = mimeType.GetCodecName(); - selectedCodecs = codecs.Where(codec => codec.name == codecName); + selectedCodecs = codecs.Where(codec => codec.name == codecName).OrderBy(codec => codec.order); codecOptions = selectedCodecs.Select(codec => codec.optionTitle).ToArray(); if (!selectedCodecs.Any()) selectCodecIndex = 0; @@ -185,7 +205,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten if(0 < selectCodecIndex) { string codecName = codecNames[selectCodecIndex]; - selectedCodecs = codecs.Where(codec => codec.name == codecName); + selectedCodecs = codecs.Where(codec => codec.name == codecName).OrderBy(codec => codec.order); codecOptions = selectedCodecs.Select(codec => codec.optionTitle).ToArray(); hasCodecOptions = codecOptions.Length > 1; var codec = selectedCodecs.First(); diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs index a964393b4..fafec9476 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs @@ -128,6 +128,8 @@ static internal VideoCodecInfo Create(RTCRtpCodecCapability caps) return new H264CodecInfo(caps); case "video/VP9": return new VP9CodecInfo(caps); + case "video/AV1": + return new AV1CodecInfo(caps); default: return new VideoCodecInfo(caps); } @@ -257,4 +259,52 @@ internal H264CodecInfo(RTCRtpCodecCapability caps) : base(caps) } } + /// + /// + /// + public enum AV1Profile + { + /// + /// + /// + Profile0 = 0, + /// + /// + /// + Profile1 = 1, + /// + /// + /// + Profile2 = 2, + } + + /// + /// + /// + public class AV1CodecInfo : VideoCodecInfo + { + const string KeyProfile = "profile"; + + /// + /// + /// + public AV1Profile profile + { + get + { + + if (parameters.TryGetValue(KeyProfile, out var value)) + { + return (AV1Profile)Enum.ToObject(typeof(AV1Profile), Convert.ToInt32(value)); + } + // If the parameter is not present, it MUST be inferred to be 0 (“Main” profile). + // https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters + return AV1Profile.Profile0; + } + } + + internal AV1CodecInfo(RTCRtpCodecCapability caps) : base(caps) + { + } + } } diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs index 025820244..de461b626 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs @@ -155,6 +155,8 @@ static string CodecTitle(VideoCodecInfo codec) return $"{h264Codec.mimeType} {h264Codec.profile} {h264Codec.level.ToString().Insert(1, ".")} {h264Codec.codecImplementation}"; case VP9CodecInfo V9Codec: return $"{V9Codec.mimeType} {V9Codec.profile} {V9Codec.codecImplementation}"; + case AV1CodecInfo av1Codec: + return $"{av1Codec.mimeType} {av1Codec.profile} {av1Codec.codecImplementation}"; default: return $"{codec.mimeType} {codec.codecImplementation}"; } From 2a0b9de442ea530679526221e27151ad112b4545 Mon Sep 17 00:00:00 2001 From: Cova8bitdots Date: Fri, 23 Dec 2022 14:57:48 +0900 Subject: [PATCH 013/117] fix: Fix AFoundationSample when disable script (#821) --- .../Samples~/Example/ARFoundation/ARFoundationSample.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs index cc40ce5cc..32fbceb18 100644 --- a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs @@ -90,7 +90,7 @@ void OnDisable() positionAction.started -= UpdatePosition; positionAction.canceled -= UpdatePosition; - quaternionAction.Enable(); + quaternionAction.Disable(); quaternionAction.performed -= UpdateQuaternion; quaternionAction.started -= UpdateQuaternion; quaternionAction.canceled -= UpdateQuaternion; From 33cec295fdec18069d755e6a2a35cb09743dbdae Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 6 Jan 2023 10:03:43 +0900 Subject: [PATCH 014/117] fix: Fix exceptions in Broadcast sample scene (#822) * fix exceptions * fix * fix bugs --- .../Example/Broadcast/UIControllerV2.cs | 2 ++ .../Samples~/Example/Stats/ShowStatsUI.cs | 22 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs index 6c0b880f1..bd33bbaa7 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/UIControllerV2.cs @@ -61,6 +61,8 @@ void OnTextInput(char c) public void OnPoint(InputAction.CallbackContext context) { + if(m_rectTransform == null) + return; var position = context.ReadValue(); var screenSize = new Vector2Int(Screen.width, Screen.height); position = position / screenSize * new Vector2(m_rectTransform.rect.width, m_rectTransform.rect.height); diff --git a/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs b/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs index d7219993a..ed54d0521 100644 --- a/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -215,11 +214,13 @@ private void SetUpSenderBase(StreamSenderBase senderBase) { foreach (var sender in hashSet) { - DestroyImmediate(lastSenderStats[sender].display.gameObject); - lastSenderStats.Remove(sender); + if (lastSenderStats.TryGetValue(sender, out var statsDisplay)) + { + DestroyImmediate(statsDisplay.display.gameObject); + lastSenderStats.Remove(sender); + } } } - activeSenderList.Remove(id); }; @@ -247,7 +248,10 @@ private void SetUpReceiverBase(StreamReceiverBase receiverBase) receiverBase.OnStartedStream += id => { - activeReceiverList[receiverBase].Add(receiverBase.Transceiver.Receiver); + if(activeReceiverList.TryGetValue(receiverBase, out var hashSet)) + { + hashSet.Add(receiverBase.Transceiver.Receiver); + } }; receiverBase.OnStoppedStream += id => { @@ -255,11 +259,13 @@ private void SetUpReceiverBase(StreamReceiverBase receiverBase) { foreach (var receiver in hashSet) { - DestroyImmediate(lastReceiverStats[receiver].display.gameObject); - lastReceiverStats.Remove(receiver); + if (lastReceiverStats.TryGetValue(receiver, out var statsDisplay)) + { + DestroyImmediate(statsDisplay.display.gameObject); + lastReceiverStats.Remove(receiver); + } } } - activeReceiverList.Remove(receiverBase); }; From b2d600be62658e733737eff0084652b3688c6079 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Fri, 6 Jan 2023 17:17:56 +0900 Subject: [PATCH 015/117] fix: Migrate renderpipeline and broadcast sample scripts (#826) * migrate renderpipeline sample scripts * chagne light intensity for hdrp Co-authored-by: kazuki --- .../Example/RenderPipeline/HDRP.unity | 290 ++++++++++++++---- .../RenderPipeline/RenderPipelineSample.cs | 128 -------- .../RenderPipelineSample.cs.meta | 11 - .../Samples~/Example/RenderPipeline/URP.unity | 174 ++++++++--- 4 files changed, 362 insertions(+), 241 deletions(-) delete mode 100644 com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs delete mode 100644 com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs.meta diff --git a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/HDRP.unity b/com.unity.renderstreaming/Samples~/Example/RenderPipeline/HDRP.unity index d9e92ec75..b3f8d9028 100644 --- a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/HDRP.unity +++ b/com.unity.renderstreaming/Samples~/Example/RenderPipeline/HDRP.unity @@ -153,6 +153,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 504867987} m_RootOrder: 2 @@ -233,6 +234,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 693537690} - {fileID: 1328462752} @@ -364,6 +366,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1305717175} m_Father: {fileID: 1419321215} @@ -400,6 +403,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1741850766} m_Father: {fileID: 190998723} @@ -439,6 +443,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 153471469} m_Father: {fileID: 1569171523} @@ -564,6 +569,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 486043064} m_RootOrder: 0 @@ -645,6 +651,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} m_Name: m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 m_MoveRepeatDelay: 0.5 m_MoveRepeatRate: 0.1 m_XRTrackingOrigin: {fileID: 0} @@ -698,6 +705,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 6 @@ -730,6 +738,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 557454589} m_RootOrder: 1 @@ -806,6 +815,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1093696262} - {fileID: 497742260} @@ -1036,6 +1046,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 557454589} m_Father: {fileID: 1853200203} @@ -1074,6 +1085,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 627419987} m_RootOrder: 1 @@ -1149,6 +1161,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1730541270} m_RootOrder: 1 @@ -1225,6 +1238,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 195256504} m_Father: {fileID: 970442468} @@ -1311,6 +1325,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 504867987} m_Father: {fileID: 738693534} @@ -1350,6 +1365,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 763320206} m_Father: {fileID: 312442151} @@ -1474,6 +1490,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1993671239} - {fileID: 620743428} @@ -1562,6 +1579,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 4 @@ -1640,6 +1658,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1536397151} - {fileID: 308321608} @@ -1729,7 +1748,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3} m_Name: m_EditorClassIdentifier: - isGlobal: 1 + m_IsGlobal: 1 priority: 0 blendDistance: 0 weight: 1 @@ -1744,6 +1763,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 1, z: 2} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 4 @@ -1774,6 +1794,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 627419987} m_Father: {fileID: 637610768} @@ -1812,6 +1833,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 504867987} m_RootOrder: 1 @@ -1886,6 +1908,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 995371237} - {fileID: 470677506} @@ -1975,6 +1998,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 612653717} m_Father: {fileID: 1958427996} @@ -2064,6 +2088,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1730541270} m_RootOrder: 0 @@ -2143,6 +2168,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 62426214} m_RootOrder: 0 @@ -2223,6 +2249,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 497653900} m_Father: {fileID: 1030165866} @@ -2310,6 +2337,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1109578821} m_Father: {fileID: 497742260} @@ -2348,6 +2376,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 6 @@ -2425,6 +2454,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1044970639} m_Father: {fileID: 972454873} @@ -2462,6 +2492,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1215316415} - {fileID: 62426214} @@ -2534,7 +2565,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3} m_Name: m_EditorClassIdentifier: - isGlobal: 1 + m_IsGlobal: 1 priority: -1 blendDistance: 0 weight: 1 @@ -2549,6 +2580,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 1, z: 2} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 3 @@ -2644,6 +2676,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 930090858} - {fileID: 486043064} @@ -2726,6 +2759,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1772218099} m_RootOrder: 1 @@ -2802,6 +2836,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 799124603} m_Father: {fileID: 1958427996} @@ -2927,6 +2962,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 627419987} m_RootOrder: 0 @@ -3003,6 +3039,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 738693534} - {fileID: 1419321215} @@ -3110,6 +3147,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 799124603} m_RootOrder: 0 @@ -3186,6 +3224,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1776016164} m_Father: {fileID: 312442151} @@ -3275,6 +3314,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 763320206} m_RootOrder: 0 @@ -3375,6 +3415,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -10.119711, y: -5.336665, z: -13.136775} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -3404,10 +3445,13 @@ MonoBehaviour: m_GameObject: {fileID: 1131364781} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: dabd9aa9b88e3304f9beca84c54d2cda, type: 3} + m_Script: {fileID: 11500000, guid: d207eb28d60cb5941a6a8f93a10ec652, type: 3} m_Name: m_EditorClassIdentifier: renderStreaming: {fileID: 1131364782} + inputReceiver: {fileID: 1299133688} + cameraController: {fileID: 1299133687} + uiController: {fileID: 970442469} videoStreamSender: {fileID: 1299133681} bandwidthSelector: {fileID: 62426215} scaleResolutionDownSelector: {fileID: 2105384913} @@ -3441,6 +3485,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1755695026} m_RootOrder: 1 @@ -3531,6 +3576,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -3579,6 +3625,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.8, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 7 @@ -3611,6 +3658,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 0 @@ -3690,6 +3738,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1772218099} m_RootOrder: 0 @@ -3765,6 +3814,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 557454589} m_RootOrder: 2 @@ -3844,6 +3894,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 627419987} m_RootOrder: 2 @@ -3910,6 +3961,7 @@ GameObject: - component: {fileID: 1299133686} - component: {fileID: 1299133688} - component: {fileID: 1299133681} + - component: {fileID: 1299133689} m_Layer: 0 m_Name: Render Streaming Camera m_TagString: MainCamera @@ -4006,6 +4058,7 @@ Transform: m_LocalRotation: {x: -0.022962164, y: 0.97605896, z: -0.17339188, w: -0.12929425} m_LocalPosition: {x: 2.454, y: 1.436, z: 2.861} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 1 @@ -4077,7 +4130,6 @@ MonoBehaviour: rotationLerpTime: 0.01 invertY: 0 playerInput: {fileID: 1299133688} - uiController: {fileID: 970442469} --- !u!114 &1299133688 MonoBehaviour: m_ObjectHideFlags: 0 @@ -4096,54 +4148,15 @@ MonoBehaviour: type: 3} m_ActionEvents: - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 1299133687} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnMovement - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 548e32fd-77d1-40e5-8197-32ca56b41bc0 m_ActionName: Player Controls/Movement[/Keyboard/w,/Keyboard/s,/Keyboard/a,/Keyboard/d] - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 1299133687} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnLook - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 8ebbde1f-3044-41bc-bdac-430e0eae1a68 m_ActionName: Player Controls/Look[/Mouse/delta,/Mouse/press] - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 1299133687} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: ResetCamera - m_Mode: 1 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 7f36745c-6b3f-404f-9df5-42688580b961 m_ActionName: Player Controls/ResetCamera[/Keyboard/u] - m_PersistentCalls: @@ -4178,7 +4191,144 @@ MonoBehaviour: m_Calls: [] m_ActionId: 50305201-a606-4afe-954c-0666ccaf6c53 m_ActionName: Player Controls/Position + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 60e72b30-ed9b-46db-80b9-f3660fa002ba + m_ActionName: Player Controls/Point[/Mouse/position] + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 245d7ad4-63bc-43e3-8d43-432ba35a43f0 + m_ActionName: Player Controls/Press[/Mouse/leftButton] + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 02ca192d-af9c-45fe-85c1-c9c38dd9cd48 + m_ActionName: Player Controls/PressAnyKey[/Keyboard/anyKey] m_DefaultActionMap: Player Controls +--- !u!114 &1299133689 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1299133680} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 23c1ce4fb46143f46bc5cb5224c934f6, type: 3} + m_Name: + m_EditorClassIdentifier: + clearColorMode: 0 + backgroundColorHDR: {r: 0.025, g: 0.07, b: 0.19, a: 0} + clearDepth: 1 + volumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + volumeAnchorOverride: {fileID: 0} + antialiasing: 0 + SMAAQuality: 2 + dithering: 0 + stopNaNs: 0 + taaSharpenStrength: 0.5 + TAAQuality: 1 + taaHistorySharpening: 0.35 + taaAntiFlicker: 0.5 + taaMotionVectorRejection: 0 + taaAntiHistoryRinging: 0 + taaBaseBlendFactor: 0.875 + taaJitterScale: 1 + physicalParameters: + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + flipYMode: 0 + xrRendering: 1 + fullscreenPassthrough: 0 + allowDynamicResolution: 0 + customRenderingSettings: 0 + invertFaceCulling: 0 + probeLayerMask: + serializedVersion: 2 + m_Bits: 4294967295 + hasPersistentHistory: 0 + allowDeepLearningSuperSampling: 1 + deepLearningSuperSamplingUseCustomQualitySettings: 0 + deepLearningSuperSamplingQuality: 0 + deepLearningSuperSamplingUseCustomAttributes: 0 + deepLearningSuperSamplingUseOptimalSettings: 1 + deepLearningSuperSamplingSharpening: 0 + exposureTarget: {fileID: 0} + materialMipBias: 0 + m_RenderingPathCustomFrameSettings: + bitDatas: + data1: 72198260625768269 + data2: 13763000477350330392 + lodBias: 1 + lodBiasMode: 0 + lodBiasQualityLevel: 0 + maximumLODLevel: 0 + maximumLODLevelMode: 0 + maximumLODLevelQualityLevel: 0 + sssQualityMode: 0 + sssQualityLevel: 0 + sssCustomSampleBudget: 20 + msaaMode: 1 + materialQuality: 0 + renderingPathCustomFrameSettingsOverrideMask: + mask: + data1: 0 + data2: 0 + defaultFrameSettings: 0 + m_Version: 8 + m_ObsoleteRenderingPath: 0 + m_ObsoleteFrameSettings: + overrides: 0 + enableShadow: 0 + enableContactShadows: 0 + enableShadowMask: 0 + enableSSR: 0 + enableSSAO: 0 + enableSubsurfaceScattering: 0 + enableTransmission: 0 + enableAtmosphericScattering: 0 + enableVolumetrics: 0 + enableReprojectionForVolumetrics: 0 + enableLightLayers: 0 + enableExposureControl: 1 + diffuseGlobalDimmer: 0 + specularGlobalDimmer: 0 + shaderLitMode: 0 + enableDepthPrepassWithDeferredRendering: 0 + enableTransparentPrepass: 0 + enableMotionVectors: 0 + enableObjectMotionVectors: 0 + enableDecals: 0 + enableRoughRefraction: 0 + enableTransparentPostpass: 0 + enableDistortion: 0 + enablePostprocess: 0 + enableOpaqueObjects: 0 + enableTransparentObjects: 0 + enableRealtimePlanarReflection: 0 + enableMSAA: 0 + enableAsyncCompute: 0 + runLightListAsync: 0 + runSSRAsync: 0 + runSSAOAsync: 0 + runContactShadowsAsync: 0 + runVolumeVoxelizationAsync: 0 + lightLoopSettings: + overrides: 0 + enableDeferredTileAndCluster: 0 + enableComputeLightEvaluation: 0 + enableComputeLightVariants: 0 + enableComputeMaterialVariants: 0 + enableFptlForForwardOpaque: 0 + enableBigTilePrepass: 0 + isFptlEnabled: 0 --- !u!1 &1305717174 GameObject: m_ObjectHideFlags: 0 @@ -4207,6 +4357,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 64735480} m_RootOrder: 0 @@ -4282,6 +4433,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1772218099} m_RootOrder: 2 @@ -4361,6 +4513,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 62426214} m_RootOrder: 1 @@ -4437,6 +4590,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 64735480} m_Father: {fileID: 1030165866} @@ -4562,6 +4716,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 557454589} m_RootOrder: 0 @@ -4638,6 +4793,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1853200203} - {fileID: 190998723} @@ -4746,6 +4902,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 686441401} - {fileID: 484724705} @@ -4879,6 +5036,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 153471469} m_RootOrder: 0 @@ -4955,6 +5113,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2004198699} - {fileID: 1152511459} @@ -5087,6 +5246,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1240044737} - {fileID: 972108364} @@ -5173,6 +5333,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1772218099} m_Father: {fileID: 1093696262} @@ -5211,6 +5372,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2105384912} m_RootOrder: 0 @@ -5450,6 +5612,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 2 @@ -5530,6 +5693,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 433478301} m_Father: {fileID: 1569171523} @@ -5619,6 +5783,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2105384912} m_RootOrder: 1 @@ -5695,6 +5860,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 637610768} - {fileID: 972454873} @@ -5802,6 +5968,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 504867987} m_RootOrder: 0 @@ -5877,6 +6044,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1755695026} m_RootOrder: 0 @@ -5957,7 +6125,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3} m_Name: m_EditorClassIdentifier: - isGlobal: 1 + m_IsGlobal: 1 priority: 0 blendDistance: 0 weight: 1 @@ -5972,6 +6140,7 @@ Transform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 5 @@ -6006,7 +6175,7 @@ Light: m_Type: 1 m_Shape: 0 m_Color: {r: 1, g: 1, b: 1, a: 1} - m_Intensity: 500 + m_Intensity: 80000 m_Range: 10 m_SpotAngle: 30 m_InnerSpotAngle: 21.802082 @@ -6066,6 +6235,7 @@ Transform: m_LocalRotation: {x: 0.7064338, y: 0.47771442, z: 0.030843567, w: 0.5213338} m_LocalPosition: {x: -3.18, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 2 @@ -6082,15 +6252,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 7a68c43fe1f2a47cfa234b5eeaa98012, type: 3} m_Name: m_EditorClassIdentifier: - m_Version: 11 - m_ObsoleteShadowResolutionTier: 1 - m_ObsoleteUseShadowQualitySettings: 0 - m_ObsoleteCustomShadowResolution: 1024 - m_ObsoleteContactShadows: 0 - m_PointlightHDType: 0 - m_SpotLightShape: 0 - m_AreaLightShape: 0 - m_Intensity: 500 + m_Intensity: 80000 m_EnableSpotReflector: 0 m_LuxAtDistance: 1 m_InnerSpotPercent: 0 @@ -6173,6 +6335,7 @@ MonoBehaviour: m_BarnDoorAngle: 90 m_BarnDoorLength: 0.05 m_preserveCachedShadow: 0 + m_OnDemandShadowRenderOnPlacement: 1 m_ShadowCascadeRatios: - 0.005 - 0.015 @@ -6188,10 +6351,17 @@ MonoBehaviour: useOldInspector: 0 useVolumetric: 1 featuresFoldout: 1 - showAdditionalSettings: 0 m_AreaLightEmissiveMeshShadowCastingMode: 0 m_AreaLightEmissiveMeshMotionVectorGenerationMode: 0 m_AreaLightEmissiveMeshLayer: -1 + m_Version: 11 + m_ObsoleteShadowResolutionTier: 1 + m_ObsoleteUseShadowQualitySettings: 0 + m_ObsoleteCustomShadowResolution: 1024 + m_ObsoleteContactShadows: 0 + m_PointlightHDType: 0 + m_SpotLightShape: 0 + m_AreaLightShape: 0 --- !u!1 &2076773437 GameObject: m_ObjectHideFlags: 0 @@ -6236,6 +6406,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -6284,6 +6455,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 8 @@ -6316,6 +6488,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 970442468} m_RootOrder: 2 @@ -6392,6 +6565,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1789612567} - {fileID: 1940762174} diff --git a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs b/com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs deleted file mode 100644 index e791dab85..000000000 --- a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEngine.UI; - -namespace Unity.RenderStreaming.Samples -{ - class RenderPipelineSample : MonoBehaviour - { - [SerializeField] RenderStreaming renderStreaming; - [SerializeField] VideoStreamSender videoStreamSender; - [SerializeField] private Dropdown bandwidthSelector; - [SerializeField] private Dropdown scaleResolutionDownSelector; - [SerializeField] private Dropdown framerateSelector; - [SerializeField] private Dropdown resolutionSelector; - - private Dictionary bandwidthOptions = - new Dictionary() - { - { "10000", 10000 }, - { "2000", 2000 }, - { "1000", 1000 }, - { "500", 500 }, - { "250", 250 }, - { "125", 125 }, - }; - - private Dictionary scaleResolutionDownOptions = - new Dictionary() - { - { "Not scaling", 1.0f }, - { "Down scale by 2.0", 2.0f }, - { "Down scale by 4.0", 4.0f }, - { "Down scale by 8.0", 8.0f }, - { "Down scale by 16.0", 16.0f } - }; - - private Dictionary framerateOptions = - new Dictionary - { - { "90", 90f }, - { "60", 60f }, - { "30", 30f }, - { "20", 20f }, - { "10", 10f }, - { "5", 5f }, - }; - - private Dictionary resolutionOptions = - new Dictionary - { - { "640x480", new Vector2Int(640, 480) }, - { "1280x720", new Vector2Int(1280, 720) }, - { "1600x1200", new Vector2Int(1600, 1200) }, - { "1920x1200", new Vector2Int(1920, 1200) }, - { "2560x1440", new Vector2Int(2560, 1440) }, - }; - - private RenderStreamingSettings settings; - - private void Awake() - { - settings = SampleManager.Instance.Settings; - - if(settings != null) - { - videoStreamSender.width = (uint)settings.StreamSize.x; - videoStreamSender.height = (uint)settings.StreamSize.y; - videoStreamSender.SetCodec(settings.SenderVideoCodec); - } - - bandwidthSelector.options = bandwidthOptions - .Select(pair => new Dropdown.OptionData { text = pair.Key }) - .ToList(); - framerateSelector.SetValueWithoutNotify(2); // todo: detect default select index - bandwidthSelector.onValueChanged.AddListener(ChangeBandwidth); - scaleResolutionDownSelector.options = scaleResolutionDownOptions - .Select(pair => new Dropdown.OptionData { text = pair.Key }) - .ToList(); - scaleResolutionDownSelector.onValueChanged.AddListener(ChangeScaleResolutionDown); - - framerateSelector.options = framerateOptions - .Select(pair => new Dropdown.OptionData { text = pair.Key }) - .ToList(); - framerateSelector.SetValueWithoutNotify(2); // todo: detect default select index - framerateSelector.onValueChanged.AddListener(ChangeFramerate); - - resolutionSelector.options = resolutionOptions - .Select(pair => new Dropdown.OptionData { text = pair.Key }) - .ToList(); - resolutionSelector.SetValueWithoutNotify(1); // todo: detect default select index - resolutionSelector.onValueChanged.AddListener(ChangeResolution); - } - - private void ChangeBandwidth(int index) - { - var bitrate = bandwidthOptions.Values.ElementAt(index); - videoStreamSender.SetBitrate(bitrate, bitrate); - } - - private void ChangeScaleResolutionDown(int index) - { - var scale = scaleResolutionDownOptions.Values.ElementAt(index); - videoStreamSender.SetScaleResolutionDown(scale); - } - - private void ChangeFramerate(int index) - { - var framerate = framerateOptions.Values.ElementAt(index); - videoStreamSender.SetFrameRate(framerate); - } - - private void ChangeResolution(int index) - { - var resolution = resolutionOptions.Values.ElementAt(index); - - videoStreamSender.SetTextureSize(resolution); - } - - private void Start() - { - if (!renderStreaming.runOnAwake) - { - renderStreaming.Run(signaling: settings?.Signaling); - } - } - } -} diff --git a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs.meta b/com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs.meta deleted file mode 100644 index ffa55ce27..000000000 --- a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/RenderPipelineSample.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dabd9aa9b88e3304f9beca84c54d2cda -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/URP.unity b/com.unity.renderstreaming/Samples~/Example/RenderPipeline/URP.unity index a2ba292db..ed467841f 100644 --- a/com.unity.renderstreaming/Samples~/Example/RenderPipeline/URP.unity +++ b/com.unity.renderstreaming/Samples~/Example/RenderPipeline/URP.unity @@ -152,6 +152,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 504867987} m_RootOrder: 2 @@ -232,6 +233,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 693537690} - {fileID: 1328462752} @@ -363,6 +365,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1305717175} m_Father: {fileID: 1419321215} @@ -399,6 +402,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1741850766} m_Father: {fileID: 190998723} @@ -438,6 +442,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 153471469} m_Father: {fileID: 1569171523} @@ -563,6 +568,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 557454589} m_RootOrder: 1 @@ -639,6 +645,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1093696262} - {fileID: 497742260} @@ -744,6 +751,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 557454589} m_Father: {fileID: 1853200203} @@ -782,6 +790,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 627419987} m_RootOrder: 1 @@ -857,6 +866,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1730541270} m_RootOrder: 1 @@ -930,6 +940,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 504867987} m_Father: {fileID: 738693534} @@ -969,6 +980,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 763320206} m_Father: {fileID: 312442151} @@ -1093,6 +1105,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1993671239} - {fileID: 620743428} @@ -1181,6 +1194,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 4 @@ -1259,6 +1273,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1536397151} - {fileID: 308321608} @@ -1345,6 +1360,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 627419987} m_Father: {fileID: 637610768} @@ -1383,6 +1399,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 504867987} m_RootOrder: 1 @@ -1457,6 +1474,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 995371237} - {fileID: 470677506} @@ -1546,6 +1564,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 612653717} m_Father: {fileID: 1958427996} @@ -1635,6 +1654,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1730541270} m_RootOrder: 0 @@ -1714,6 +1734,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 62426214} m_RootOrder: 0 @@ -1855,6 +1876,7 @@ Transform: m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 2 @@ -1871,7 +1893,14 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} m_Name: m_EditorClassIdentifier: + m_Version: 1 m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_LightLayerMask: 1 + m_CustomShadowLayers: 0 + m_ShadowLayerMask: 1 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} --- !u!1 &725451149 GameObject: m_ObjectHideFlags: 0 @@ -1887,6 +1916,7 @@ GameObject: - component: {fileID: 725451155} - component: {fileID: 725451157} - component: {fileID: 725451156} + - component: {fileID: 725451158} m_Layer: 0 m_Name: Render Streaming Camera m_TagString: Untagged @@ -1904,6 +1934,7 @@ Transform: m_LocalRotation: {x: -0.02296006, y: 0.9760592, z: -0.17338917, w: -0.12929617} m_LocalPosition: {x: 2.454, y: 1.436, z: 2.861} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 1 @@ -1958,7 +1989,6 @@ MonoBehaviour: rotationLerpTime: 0.01 invertY: 0 playerInput: {fileID: 725451157} - uiController: {fileID: 1215389824} --- !u!20 &725451154 Camera: m_ObjectHideFlags: 0 @@ -2073,54 +2103,15 @@ MonoBehaviour: type: 3} m_ActionEvents: - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451153} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnMovement - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 548e32fd-77d1-40e5-8197-32ca56b41bc0 m_ActionName: Player Controls/Movement[/Keyboard/w,/Keyboard/s,/Keyboard/a,/Keyboard/d] - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451153} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: OnLook - m_Mode: 0 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 8ebbde1f-3044-41bc-bdac-430e0eae1a68 m_ActionName: Player Controls/Look[/Mouse/delta,/Mouse/press] - m_PersistentCalls: - m_Calls: - - m_Target: {fileID: 725451153} - m_TargetAssemblyTypeName: Unity.RenderStreaming.Samples.SimpleCameraControllerV2, - Unity.RenderStreaming.Sample - m_MethodName: ResetCamera - m_Mode: 1 - m_Arguments: - m_ObjectArgument: {fileID: 0} - m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine - m_IntArgument: 0 - m_FloatArgument: 0 - m_StringArgument: - m_BoolArgument: 0 - m_CallState: 2 + m_Calls: [] m_ActionId: 7f36745c-6b3f-404f-9df5-42688580b961 m_ActionName: Player Controls/ResetCamera[/Keyboard/u] - m_PersistentCalls: @@ -2155,7 +2146,52 @@ MonoBehaviour: m_Calls: [] m_ActionId: 50305201-a606-4afe-954c-0666ccaf6c53 m_ActionName: Player Controls/Position + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 60e72b30-ed9b-46db-80b9-f3660fa002ba + m_ActionName: Player Controls/Point[/Mouse/position] + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 245d7ad4-63bc-43e3-8d43-432ba35a43f0 + m_ActionName: Player Controls/Press[/Mouse/leftButton] + - m_PersistentCalls: + m_Calls: [] + m_ActionId: 02ca192d-af9c-45fe-85c1-c9c38dd9cd48 + m_ActionName: Player Controls/PressAnyKey[/Keyboard/anyKey] m_DefaultActionMap: Player Controls +--- !u!114 &725451158 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 725451149} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 --- !u!1 &738693533 GameObject: m_ObjectHideFlags: 0 @@ -2185,6 +2221,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 497653900} m_Father: {fileID: 1030165866} @@ -2397,6 +2434,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1109578821} m_Father: {fileID: 497742260} @@ -2435,6 +2473,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 6 @@ -2516,6 +2555,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} m_Name: m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 m_MoveRepeatDelay: 0.5 m_MoveRepeatRate: 0.1 m_XRTrackingOrigin: {fileID: 0} @@ -2569,6 +2609,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 5 @@ -2599,6 +2640,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1044970639} m_Father: {fileID: 972454873} @@ -2638,6 +2680,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 925721385} m_Father: {fileID: 1215389823} @@ -2726,6 +2769,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 920982462} m_RootOrder: 0 @@ -2804,6 +2848,7 @@ RectTransform: m_LocalRotation: {x: 0.000003013527, y: -0.0000021457672, z: -0.0000013764948, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.0047200536} m_LocalScale: {x: 0.97427887, y: 0.97427875, z: 0.97427905} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1215316415} - {fileID: 62426214} @@ -2875,6 +2920,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1772218099} m_RootOrder: 1 @@ -2951,6 +2997,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 799124603} m_Father: {fileID: 1958427996} @@ -3076,6 +3123,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 627419987} m_RootOrder: 0 @@ -3152,6 +3200,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 738693534} - {fileID: 1419321215} @@ -3259,6 +3308,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 799124603} m_RootOrder: 0 @@ -3349,6 +3399,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -3397,6 +3448,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.8, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 4 @@ -3430,6 +3482,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1776016164} m_Father: {fileID: 312442151} @@ -3519,6 +3572,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 763320206} m_RootOrder: 0 @@ -3594,6 +3648,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1755695026} m_RootOrder: 1 @@ -3669,6 +3724,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 0 @@ -3811,6 +3867,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 930090858} - {fileID: 920982462} @@ -3893,6 +3950,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1772218099} m_RootOrder: 0 @@ -3968,6 +4026,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 557454589} m_RootOrder: 2 @@ -4047,6 +4106,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1215389823} m_RootOrder: 2 @@ -4122,6 +4182,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 627419987} m_RootOrder: 2 @@ -4201,6 +4262,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 64735480} m_RootOrder: 0 @@ -4276,6 +4338,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1772218099} m_RootOrder: 2 @@ -4355,6 +4418,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 62426214} m_RootOrder: 1 @@ -4431,6 +4495,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 64735480} m_Father: {fileID: 1030165866} @@ -4556,6 +4621,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 557454589} m_RootOrder: 0 @@ -4632,6 +4698,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1853200203} - {fileID: 190998723} @@ -4764,6 +4831,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -4793,10 +4861,13 @@ MonoBehaviour: m_GameObject: {fileID: 1623734773} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: dabd9aa9b88e3304f9beca84c54d2cda, type: 3} + m_Script: {fileID: 11500000, guid: d207eb28d60cb5941a6a8f93a10ec652, type: 3} m_Name: m_EditorClassIdentifier: renderStreaming: {fileID: 1623734774} + inputReceiver: {fileID: 725451157} + cameraController: {fileID: 725451153} + uiController: {fileID: 1215389824} videoStreamSender: {fileID: 725451156} bandwidthSelector: {fileID: 62426215} scaleResolutionDownSelector: {fileID: 2105384913} @@ -4831,6 +4902,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 686441401} - {fileID: 484724705} @@ -4964,6 +5036,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 153471469} m_RootOrder: 0 @@ -5040,6 +5113,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2004198699} - {fileID: 1152511459} @@ -5172,6 +5246,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1240044737} - {fileID: 972108364} @@ -5258,6 +5333,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1772218099} m_Father: {fileID: 1093696262} @@ -5296,6 +5372,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2105384912} m_RootOrder: 0 @@ -5375,6 +5452,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 930090858} m_RootOrder: 2 @@ -5455,6 +5533,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 433478301} m_Father: {fileID: 1569171523} @@ -5544,6 +5623,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2105384912} m_RootOrder: 1 @@ -5620,6 +5700,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 637610768} - {fileID: 972454873} @@ -5727,6 +5808,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 504867987} m_RootOrder: 0 @@ -5962,6 +6044,7 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1755695026} m_RootOrder: 0 @@ -6042,6 +6125,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1789612567} - {fileID: 1940762174} @@ -6191,6 +6275,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -6239,6 +6324,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 3 From b6fb8dfeb34c56f1543ec4114fca4a6bca03c0aa Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 10 Jan 2023 10:15:42 +0900 Subject: [PATCH 016/117] fix: Add timeout for waiting RTCRtpReceiver.GetStats coroutine (#825) --- .../Samples~/Example/Stats/ShowStatsUI.cs | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs b/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs index ed54d0521..66d3b05c7 100644 --- a/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs @@ -127,9 +127,9 @@ private IEnumerator CollectStats() IEnumerator UpdateStats(RTCRtpReceiver receiver) { var op = receiver.GetStats(); - yield return op; + yield return new WaitUntilWithTimeout(() => op.IsDone, 3f); - if (op.IsError) + if (op.IsError || !op.IsDone) { yield break; } @@ -159,10 +159,9 @@ IEnumerator UpdateStats(RTCRtpReceiver receiver) IEnumerator UpdateStats(RTCRtpSender sender) { var op = sender.GetStats(); - yield return op; + yield return new WaitUntilWithTimeout(() => op.IsDone, 3f); - - if (op.IsError) + if (op.IsError || !op.IsDone) { yield break; } @@ -377,4 +376,33 @@ private static string CreateDisplayString(RTCStatsReport report, RTCStatsReport return builder.ToString(); } } + + internal class WaitUntilWithTimeout : CustomYieldInstruction + { + public bool IsCompleted { get; private set; } + + private readonly float timeoutTime; + + private readonly System.Func predicate; + + public override bool keepWaiting + { + get + { + IsCompleted = predicate(); + if (IsCompleted) + { + return false; + } + + return !(Time.realtimeSinceStartup >= timeoutTime); + } + } + + public WaitUntilWithTimeout(System.Func predicate, float timeout) + { + this.timeoutTime = Time.realtimeSinceStartup + timeout; + this.predicate = predicate; + } + } } From d874de464f647700ddc59d52a79e04f9f26a1cc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 10:51:53 +0900 Subject: [PATCH 017/117] chore: bump json5 from 2.2.1 to 2.2.3 in /WebApp (#829) Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- WebApp/package-lock.json | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/WebApp/package-lock.json b/WebApp/package-lock.json index 425ef09b3..7601b9a1b 100644 --- a/WebApp/package-lock.json +++ b/WebApp/package-lock.json @@ -6479,9 +6479,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -10794,7 +10794,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "8.2.0", @@ -13305,7 +13306,8 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true + "dev": true, + "requires": {} }, "jest-resolve": { "version": "29.0.2", @@ -14083,9 +14085,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonfile": { @@ -15980,7 +15982,8 @@ "ws": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==" + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "requires": {} }, "xmlbuilder": { "version": "15.1.1", From 6e046135ec50a0a03081ad2c17d74174b29830a0 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 10 Jan 2023 10:57:33 +0900 Subject: [PATCH 018/117] fix: Avoid assertion from Input System package when using Android device (#827) * workaround: avoid assertion `Could not find active control after binding resolution.` in InputActionState class. (cherry picked from commit 89c50f8f05076928ebb45976dd0e83d55847a624) * fix * revert * wip * fix * fix issue --- .../Runtime/Scripts/InputReceiver.cs | 22 ++++++++--- .../Scripts/InputSystem/InputManager.cs | 21 ++++++++-- .../Scripts/InputSystem/InputRemoting.cs | 38 ++++++++++++------- .../Tests/Runtime/StreamingComponentTest.cs | 23 +++++++++++ 4 files changed, 83 insertions(+), 21 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs index ec7e67016..519724d2c 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs @@ -8,11 +8,10 @@ using Unity.RenderStreaming.InputSystem; using Inputs = UnityEngine.InputSystem.InputSystem; +using InputRemoting = Unity.RenderStreaming.InputSystem.InputRemoting; namespace Unity.RenderStreaming { - using InputRemoting = Unity.RenderStreaming.InputSystem.InputRemoting; - /// /// Represents a separate player in the game complete with a set of actions exclusive /// to the player and a set of paired device. @@ -308,11 +307,24 @@ protected virtual void Dispose() private void AssignUserAndDevices() { - if (actions == null) - throw new InvalidOperationException("actions field is needed to assign."); + // If we already have a user at this point, clear out all its paired devices + // to start the pairing process from scratch. + if (m_InputUser.valid) + m_InputUser.UnpairDevices(); + // All our input goes through actions so there's no point setting + // anything up if we have none. + if (m_Actions == null) + { + // Make sure user is invalid. + m_InputUser = new InputUser(); + return; + } m_InputUser = InputUser.CreateUserWithoutPairedDevices(); - m_InputUser.AssociateActionsWithUser(actions); + + // If we don't have a valid user at this point, we don't have any paired devices. + if (m_InputUser.valid) + m_InputUser.AssociateActionsWithUser(actions); } private void UnassignUserAndDevices() diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputManager.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputManager.cs index 835f4ca15..b466217ec 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputManager.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.LowLevel; @@ -64,7 +65,13 @@ public interface IInputManager /// /// /// - void SetDeviceUsage(InputDevice device, string usage); + void AddDeviceUsage(InputDevice device, string usage); + /// + /// + /// + /// + /// + void RemoveDeviceUsage(InputDevice device, string usage); /// /// /// @@ -123,17 +130,25 @@ public virtual InputDevice GetDeviceById(int deviceId) public virtual InputDevice AddDevice(string layout, string name = null, string variants = null) { + foreach (var device_ in InputSystem.devices.Where(device => device.enabled)) + InputSystem.ResetDevice(device_); return InputSystem.AddDevice(layout, name, variants); } public virtual void RemoveDevice(InputDevice device) { + foreach (var device_ in InputSystem.devices.Where(device => device.enabled)) + InputSystem.ResetDevice(device_); InputSystem.RemoveDevice(device); } - public virtual void SetDeviceUsage(InputDevice device, string usage) + public virtual void AddDeviceUsage(InputDevice device, string usage) + { + InputSystem.AddDeviceUsage(device, usage); + } + public virtual void RemoveDeviceUsage(InputDevice device, string usage) { - InputSystem.SetDeviceUsage(device, usage); + InputSystem.RemoveDeviceUsage(device, usage); } public virtual InputControlLayout LoadLayout(string name) diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs index 74cd441a7..66b2e3feb 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs @@ -420,7 +420,6 @@ internal struct RemoteInputDevice { public int remoteId; // Device ID used by sender. public int localId; // Device ID used by us in local system. - public string layoutName; public InputDeviceDescription description; } @@ -582,7 +581,7 @@ public struct Data public string name; public string layout; public int deviceId; - public string variants; + public string[] usages; public InputDeviceDescription description; } @@ -595,8 +594,8 @@ public static Message Create(InputDevice device) name = device.name, layout = device.layout, deviceId = device.deviceId, - variants = device.variants, - description = device.description + description = device.description, + usages = device.usages.Select(x => x.ToString()).ToArray() }; var json = JsonUtility.ToJson(data); @@ -634,11 +633,7 @@ public static void Process(InputRemoting Receiver, Message msg) try { ////REVIEW: this gives remote devices names the same way that local devices receive them; should we make remote status visible in the name? - device = Receiver.m_LocalManager.AddDevice(data.layout, data.name, data.variants); - - // todo(kazuki)::Avoid to use reflection - // device.m_ParticipantId = msg.participantId; - device.SetParticipantId(msg.participantId); + device = Receiver.m_LocalManager.AddDevice(data.layout, data.name); } catch (Exception exception) { @@ -646,20 +641,27 @@ public static void Process(InputRemoting Receiver, Message msg) $"Could not create remote device '{data.description}' with layout '{data.layout}' locally (exception: {exception})"); return; } + // todo(kazuki)::Avoid to use reflection + // device.m_ParticipantId = msg.participantId; + device.SetParticipantId(msg.participantId); + // todo(kazuki)::Avoid to use reflection // device.m_Description = data.description; // device.m_DeviceFlags |= InputDevice.DeviceFlags.Remote; device.SetDescription(data.description); + var deviceFlagsRemote = 1 << 3; device.SetDeviceFlags(device.GetDeviceFlags() | deviceFlagsRemote); + foreach (var usage in data.usages) + Receiver.m_LocalManager.AddDeviceUsage(device, usage); + // Remember it. var record = new RemoteInputDevice { remoteId = data.deviceId, localId = device.deviceId, - description = data.description, - layoutName = data.layout + description = data.description }; ArrayHelpers.Append(ref Receiver.m_Senders[senderIndex].devices, record); } @@ -771,10 +773,20 @@ public static void Process(InputRemoting Receiver, Message msg) var device = Receiver.TryGetDeviceByRemoteId(data.deviceId, senderIndex); if (device != null) { - ////TODO: clearing usages and setting multiple usages + foreach (var deviceUsage in device.usages) + { + if (!data.usages.Contains(deviceUsage)) + Receiver.m_LocalManager.RemoveDeviceUsage(device, new InternedString(deviceUsage)); + } if (data.usages.Length == 1) - Receiver.m_LocalManager.SetDeviceUsage(device, new InternedString(data.usages[0])); + Receiver.m_LocalManager.AddDeviceUsage(device, new InternedString(data.usages[0])); + foreach (var dataUsage in data.usages) + { + var internedDataUsage = new InternedString(dataUsage); + if (!device.usages.Contains(internedDataUsage)) + Receiver.m_LocalManager.AddDeviceUsage(device, new InternedString(dataUsage)); + } } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index 8e659a49c..79ec21708 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -557,5 +557,28 @@ public void SetChannel() UnityEngine.Object.DestroyImmediate(asset); UnityEngine.Object.DestroyImmediate(go); } + + [Test] + public void InputUserId() + { + var go = new GameObject(); + go.SetActive(false); + var receiver = go.AddComponent(); + + // user.id is InputUser.InvalidId in default. + Assert.That(receiver.actions, Is.Null); + Assert.That(receiver.user.id, Is.EqualTo(InputUser.InvalidId)); + + var asset = ScriptableObject.CreateInstance(); + var mapName = "test"; + asset.AddActionMap(mapName); + receiver.actions = asset; + + // user.id is not InputUser.InvalidId after set actions parameter. + Assert.That(receiver.actions, Is.Not.Null); + Assert.That(receiver.user.id, Is.EqualTo(InputUser.InvalidId)); + + UnityEngine.Object.DestroyImmediate(go); + } } } From 7c29abb4f529c0134f8665ad84dd5e4d5934ac74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 15:17:42 +0900 Subject: [PATCH 019/117] chore: bump json5 from 2.2.1 to 2.2.3 in /WebApp/client (#828) Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- WebApp/client/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WebApp/client/package-lock.json b/WebApp/client/package-lock.json index 686fdc073..18f83f48e 100644 --- a/WebApp/client/package-lock.json +++ b/WebApp/client/package-lock.json @@ -4032,9 +4032,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -8638,9 +8638,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "kleur": { From 22a662dee7991303597d4a78196ff8835a969f08 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 10 Jan 2023 18:31:00 +0900 Subject: [PATCH 020/117] fix NullReferenceException in InputRemoting (#831) --- WebApp/client/src/inputdevice.js | 8 ++++---- WebApp/client/src/sender.js | 8 ++++---- .../Runtime/Scripts/InputSystem/InputRemoting.cs | 5 +++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/WebApp/client/src/inputdevice.js b/WebApp/client/src/inputdevice.js index 3a9dfd67e..a7f32f628 100644 --- a/WebApp/client/src/inputdevice.js +++ b/WebApp/client/src/inputdevice.js @@ -43,7 +43,7 @@ export class InputDevice { * name; * layout; * deviceId; - * variants; + * usages; * description; * * _inputState; @@ -54,14 +54,14 @@ export class InputDevice { * @param {Number} name * @param {String} layout * @param {Number} deviceId - * @param {String} variants + * @param {String[]} usages * @param {Object} description */ - constructor(name, layout, deviceId, variants, description) { + constructor(name, layout, deviceId, usages, description) { this.name = name; this.layout = layout; this.deviceId = deviceId; - this.variants = variants; + this.usages = usages; this.description = description; this._inputState = null; diff --git a/WebApp/client/src/sender.js b/WebApp/client/src/sender.js index 88d909166..3ca3eb841 100644 --- a/WebApp/client/src/sender.js +++ b/WebApp/client/src/sender.js @@ -38,7 +38,7 @@ export class Sender extends LocalInputManager { m_Version: "", m_Capabilities: "" }; - this.mouse = new Mouse("Mouse", "Mouse", 1, "", descriptionMouse); + this.mouse = new Mouse("Mouse", "Mouse", 1, null, descriptionMouse); this._devices.push(this.mouse); this._elem.addEventListener('click', this._onMouseEvent.bind(this), false); @@ -58,7 +58,7 @@ export class Sender extends LocalInputManager { m_Version: "", m_Capabilities: "" }; - this.keyboard = new Keyboard("Keyboard", "Keyboard", 2, "", descriptionKeyboard); + this.keyboard = new Keyboard("Keyboard", "Keyboard", 2, null, descriptionKeyboard); this._devices.push(this.keyboard); document.addEventListener('keyup', this._onKeyEvent.bind(this), false); @@ -75,7 +75,7 @@ export class Sender extends LocalInputManager { m_Version: "", m_Capabilities: "" }; - this.gamepad = new Gamepad("Gamepad", "Gamepad", 3, "", descriptionGamepad); + this.gamepad = new Gamepad("Gamepad", "Gamepad", 3, null, descriptionGamepad); this._devices.push(this.gamepad); window.addEventListener("gamepadconnected", this._onGamepadEvent.bind(this), false); @@ -94,7 +94,7 @@ export class Sender extends LocalInputManager { m_Version: "", m_Capabilities: "" }; - this.touchscreen = new Touchscreen("Touchscreen", "Touchscreen", 4, "", descriptionTouch); + this.touchscreen = new Touchscreen("Touchscreen", "Touchscreen", 4, null, descriptionTouch); this._devices.push(this.touchscreen); this._elem.addEventListener('touchend', this._onTouchEvent.bind(this), false); diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs index 66b2e3feb..da06f6c4f 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/InputRemoting.cs @@ -653,8 +653,9 @@ public static void Process(InputRemoting Receiver, Message msg) var deviceFlagsRemote = 1 << 3; device.SetDeviceFlags(device.GetDeviceFlags() | deviceFlagsRemote); - foreach (var usage in data.usages) - Receiver.m_LocalManager.AddDeviceUsage(device, usage); + if(data.usages != null) + foreach (var usage in data.usages) + Receiver.m_LocalManager.AddDeviceUsage(device, usage); // Remember it. var record = new RemoteInputDevice From d0930e3880c264d0cff4840c2d19223457eca37a Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 10 Jan 2023 18:31:15 +0900 Subject: [PATCH 021/117] fix: Fix TypeError in Bidirectional sample on Firefox (#832) * Fix TypeError in Bidirectional sample on Firefox 108 * fix * fix * fix --- WebApp/client/public/bidirectional/js/main.js | 14 ++++++++------ WebApp/client/public/bidirectional/js/sendvideo.js | 10 +++++----- WebApp/client/src/pointercorrect.js | 10 +++++----- WebApp/client/src/renderstreaming.js | 6 +++--- WebApp/client/src/signaling.js | 4 ++-- WebApp/client/src/touchflags.js | 2 +- WebApp/client/src/touchphase.js | 2 +- 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/WebApp/client/public/bidirectional/js/main.js b/WebApp/client/public/bidirectional/js/main.js index 15f01785d..9c50395d8 100644 --- a/WebApp/client/public/bidirectional/js/main.js +++ b/WebApp/client/public/bidirectional/js/main.js @@ -57,10 +57,6 @@ setupButton.addEventListener('click', setUp); const hangUpButton = document.getElementById('hangUpButton'); hangUpButton.addEventListener('click', hangUp); -window.addEventListener('beforeunload', async () => { - await sendVideo.stop(); -}, true); - setupConfig(); async function setupConfig() { @@ -84,7 +80,6 @@ async function startVideo() { cameraWidthInput.disabled = true; cameraHeightInput.disabled = true; startButton.disabled = true; - setupButton.disabled = false; let width = 0; let height = 0; @@ -98,6 +93,9 @@ async function startVideo() { } await sendVideo.startLocalVideo(videoSelect.value, audioSelect.value, width, height); + + // enable setup button after initializing local video. + setupButton.disabled = false; } async function setUp() { @@ -127,6 +125,10 @@ async function setUp() { } }; + window.addEventListener('beforeunload', async () => { + await renderstreaming.stop(); + }, true); + await renderstreaming.start(); await renderstreaming.createConnection(connectionId); } @@ -280,4 +282,4 @@ function clearStatsMessage() { remoteVideoStatsDiv.innerHTML = ''; messageDiv.style.display = 'none'; messageDiv.innerHTML = ''; -} \ No newline at end of file +} diff --git a/WebApp/client/public/bidirectional/js/sendvideo.js b/WebApp/client/public/bidirectional/js/sendvideo.js index b6cd6c285..8cea080b3 100644 --- a/WebApp/client/public/bidirectional/js/sendvideo.js +++ b/WebApp/client/public/bidirectional/js/sendvideo.js @@ -7,10 +7,10 @@ export class SendVideo { } /** - * @param {MediaTrackConstraints} videoSource - * @param {MediaTrackConstraints} audioSource - * @param {number} videoWidth - * @param {number} videoHeight + * @param {MediaTrackConstraints} videoSource + * @param {MediaTrackConstraints} audioSource + * @param {number} videoWidth + * @param {number} videoHeight */ async startLocalVideo(videoSource, audioSource, videoWidth, videoHeight) { try { @@ -42,7 +42,7 @@ export class SendVideo { } /** - * @param {MediaStreamTrack} track + * @param {MediaStreamTrack} track */ addRemoteTrack(track) { if (this.remoteVideo.srcObject == null) { diff --git a/WebApp/client/src/pointercorrect.js b/WebApp/client/src/pointercorrect.js index b0ba3649e..ee33a3022 100644 --- a/WebApp/client/src/pointercorrect.js +++ b/WebApp/client/src/pointercorrect.js @@ -5,7 +5,7 @@ export const LetterBoxType = { export class PointerCorrector { /** - * @param {Number} videoWidth + * @param {Number} videoWidth * @param {Number} videoHeight * @param {DOMRect} rect */ @@ -63,7 +63,7 @@ export class PointerCorrector { } /** - * @param {Number} videoWidth + * @param {Number} videoWidth * @param {Number} videoHeight * @param {DOMRect} rect */ @@ -99,8 +99,8 @@ export class PointerCorrector { /** * Returns rectangle for displaying video with the origin at the left-top of the element. * Not considered applying CSS like `object-fit`. - * @returns {Object} - */ + * @returns {Object} + */ get contentRect() { const letterBoxType = this.letterBoxType; const letterBoxSize = this.letterBoxSize; @@ -116,4 +116,4 @@ export class PointerCorrector { _reset() { this._contentRect = this.contentRect; } -} \ No newline at end of file +} diff --git a/WebApp/client/src/renderstreaming.js b/WebApp/client/src/renderstreaming.js index ba933eeb6..628169446 100644 --- a/WebApp/client/src/renderstreaming.js +++ b/WebApp/client/src/renderstreaming.js @@ -88,7 +88,7 @@ export class RenderStreaming { /** * if not set argument, a generated uuid is used. - * @param {string | null} connectionId + * @param {string | null} connectionId */ async createConnection(connectionId) { this._connectionId = connectionId ? connectionId : uuid4(); @@ -150,7 +150,7 @@ export class RenderStreaming { } /** - * @param {string} label + * @param {string} label * @returns {RTCDataChannel | null} */ createDataChannel(label) { @@ -158,7 +158,7 @@ export class RenderStreaming { } /** - * @param {MediaStreamTrack} track + * @param {MediaStreamTrack} track * @returns {RTCRtpSender | null} */ addTrack(track) { diff --git a/WebApp/client/src/signaling.js b/WebApp/client/src/signaling.js index 12d000d3a..657d1b5a2 100644 --- a/WebApp/client/src/signaling.js +++ b/WebApp/client/src/signaling.js @@ -76,7 +76,7 @@ export class Signaling extends EventTarget { break; default: break; - } + } } await this.sleep(this.interval); } @@ -344,4 +344,4 @@ export class WebSocketSignaling extends EventTarget { Logger.log(sendJson); this.websocket.send(sendJson); } -} \ No newline at end of file +} diff --git a/WebApp/client/src/touchflags.js b/WebApp/client/src/touchflags.js index 39d469b42..154ea6447 100644 --- a/WebApp/client/src/touchflags.js +++ b/WebApp/client/src/touchflags.js @@ -4,4 +4,4 @@ export const TouchFlags = PrimaryTouch: 1 << 4, Tap: 1 << 5, OrphanedPrimaryTouch: 1 << 6, -}; \ No newline at end of file +}; diff --git a/WebApp/client/src/touchphase.js b/WebApp/client/src/touchphase.js index 01a4b4df5..e461dcda0 100644 --- a/WebApp/client/src/touchphase.js +++ b/WebApp/client/src/touchphase.js @@ -5,4 +5,4 @@ export const TouchPhase = { Ended: 3, Canceled: 4, Stationary: 5 - }; \ No newline at end of file + }; From 579f88d4e7c0a408f1ea5e26e8d15565ab260517 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 12 Jan 2023 09:30:48 +0900 Subject: [PATCH 022/117] fix: Add autocomplete attribute into input elements to avoid issue of "disabled" attribute on Firefox (#834) * wip (cherry picked from commit f73792b9c689bac448c1a85ce4db2b21ccdf5187) * set autocomplete attribute for firefox * add null check * fix --- WebApp/client/public/bidirectional/index.html | 23 ++++++++++--------- WebApp/client/public/bidirectional/js/main.js | 12 ++++++---- WebApp/client/public/multiplay/index.html | 6 ++--- WebApp/client/public/multiplay/js/main.js | 4 +++- WebApp/client/public/receiver/index.html | 8 +++---- WebApp/client/src/peer.js | 2 +- WebApp/client/src/renderstreaming.js | 10 ++++++++ 7 files changed, 40 insertions(+), 25 deletions(-) diff --git a/WebApp/client/public/bidirectional/index.html b/WebApp/client/public/bidirectional/index.html index e275d5b06..7881ab586 100644 --- a/WebApp/client/public/bidirectional/index.html +++ b/WebApp/client/public/bidirectional/index.html @@ -16,23 +16,24 @@

Bidirectional Sample

- - + + + +
- +
- - + +
- - - + + +
@@ -54,7 +55,7 @@

Remote

Codec preferences: -
@@ -80,4 +81,4 @@

Remote

- \ No newline at end of file + diff --git a/WebApp/client/public/bidirectional/js/main.js b/WebApp/client/public/bidirectional/js/main.js index 9c50395d8..b6805ba0b 100644 --- a/WebApp/client/public/bidirectional/js/main.js +++ b/WebApp/client/public/bidirectional/js/main.js @@ -57,6 +57,12 @@ setupButton.addEventListener('click', setUp); const hangUpButton = document.getElementById('hangUpButton'); hangUpButton.addEventListener('click', hangUp); +window.addEventListener('beforeunload', async () => { + if(!renderstreaming) + return; + await renderstreaming.stop(); +}, true); + setupConfig(); async function setupConfig() { @@ -110,7 +116,7 @@ async function setUp() { renderstreaming.onConnect = () => { const tracks = sendVideo.getLocalTracks(); for (const track of tracks) { - renderstreaming.addTrack(track); + renderstreaming.addTransceiver(track, { direction: 'sendonly' }); } setCodecPreferences(); showStatsMessage(); @@ -125,10 +131,6 @@ async function setUp() { } }; - window.addEventListener('beforeunload', async () => { - await renderstreaming.stop(); - }, true); - await renderstreaming.start(); await renderstreaming.createConnection(connectionId); } diff --git a/WebApp/client/public/multiplay/index.html b/WebApp/client/public/multiplay/index.html index 01e7ad520..ff8efdc41 100644 --- a/WebApp/client/public/multiplay/index.html +++ b/WebApp/client/public/multiplay/index.html @@ -20,14 +20,14 @@

Multiplay Sample

Codec preferences: -
Lock Cursor to Player: - +

@@ -51,4 +51,4 @@

Multiplay Sample

- \ No newline at end of file + diff --git a/WebApp/client/public/multiplay/js/main.js b/WebApp/client/public/multiplay/js/main.js index b75938d42..b9f147c07 100644 --- a/WebApp/client/public/multiplay/js/main.js +++ b/WebApp/client/public/multiplay/js/main.js @@ -39,6 +39,8 @@ window.addEventListener('resize', function () { }, true); window.addEventListener('beforeunload', async () => { + if(!renderstreaming) + return; await renderstreaming.stop(); }, true); @@ -198,4 +200,4 @@ function clearStatsMessage() { intervalId = null; messageDiv.style.display = 'none'; messageDiv.innerHTML = ''; -} \ No newline at end of file +} diff --git a/WebApp/client/public/receiver/index.html b/WebApp/client/public/receiver/index.html index e3dc616d1..d7fd0e5d7 100644 --- a/WebApp/client/public/receiver/index.html +++ b/WebApp/client/public/receiver/index.html @@ -20,18 +20,18 @@

Receiver Sample

Codec preferences: -
Lock Cursor to Player: - +

- For more information about sample, see + For more information about sample, see Broadcast sample document page.

@@ -51,4 +51,4 @@

Receiver Sample

- \ No newline at end of file + diff --git a/WebApp/client/src/peer.js b/WebApp/client/src/peer.js index fcb32dd1c..92fdd6f27 100644 --- a/WebApp/client/src/peer.js +++ b/WebApp/client/src/peer.js @@ -180,7 +180,7 @@ export default class Peer extends EventTarget { try { await this.pc.addIceCandidate(candidate); } catch (e) { - if (this.pc && !this.ignoreOffer) + if (this.pc && !this.ignoreOffer) this.warn(`${this.pc} this candidate can't accept current signaling state ${this.pc.signalingState}.`); } } diff --git a/WebApp/client/src/renderstreaming.js b/WebApp/client/src/renderstreaming.js index 628169446..0a9c42017 100644 --- a/WebApp/client/src/renderstreaming.js +++ b/WebApp/client/src/renderstreaming.js @@ -165,6 +165,16 @@ export class RenderStreaming { return this._peer.addTrack(this._connectionId, track); } + /** + * @param {MediaStreamTrack | string} trackOrKind + * @param {RTCRtpTransceiverInit | null} init + * @returns {RTCRtpTransceiver | null} + */ + addTransceiver(trackOrKind, init) { + return this._peer.addTransceiver(this._connectionId, trackOrKind, init); + } + + /** * @returns {RTCRtpTransceiver[] | null} */ From 7ef33dc19fd5b8d8f0edeb447c6585097f60376d Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 12 Jan 2023 09:31:09 +0900 Subject: [PATCH 023/117] doc: Remove unused document (#835) * [skipci] ci: fix condition of auto trigger * fix * [skipci] fix * remove tutorial doc * fix * fix --- .yamato/upm-ci-webapp.yml | 4 +- .../Documentation~/tutorial.md | 96 ------------------- pack_webapp.cmd | 2 + pack_webapp.sh | 8 +- test_webapp.cmd | 2 + test_webapp.sh | 2 + test_webapp_client.cmd | 2 + test_webapp_client.sh | 2 + 8 files changed, 17 insertions(+), 101 deletions(-) delete mode 100644 com.unity.renderstreaming/Documentation~/tutorial.md diff --git a/.yamato/upm-ci-webapp.yml b/.yamato/upm-ci-webapp.yml index 5876fffbe..c02b9cd9b 100644 --- a/.yamato/upm-ci-webapp.yml +++ b/.yamato/upm-ci-webapp.yml @@ -77,11 +77,11 @@ test_client_{{ platform.name }}: trigger_webapp_test_{{ project.name }}: name : Trigger all WebApp test {{ project.packagename }} triggers: - expression: pull_request.target eq "develop" + expression: pull_request.target eq "main" dependencies: {% for platform in platforms %} - .yamato/upm-ci-webapp.yml#test_{{ platform.name }} - .yamato/upm-ci-webapp.yml#test_client_{{ platform.name }} {% endfor %} -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/com.unity.renderstreaming/Documentation~/tutorial.md b/com.unity.renderstreaming/Documentation~/tutorial.md deleted file mode 100644 index 1360fbbb1..000000000 --- a/com.unity.renderstreaming/Documentation~/tutorial.md +++ /dev/null @@ -1,96 +0,0 @@ -# Tutorial - -In this page, we introduce the way to use samples published on Package Manager. - -## Install package - -Select **Window > Package Manager** in the menu bar. - -![Install Package Manager from menu bar](images/install_select_packman_menu_unity2020.png) - -Check Package Manager window, Click `+` button and select `Add package from git URL...`. - -![Select add package from git url](images/install_select_add_package_from_git_url.png) - -Input the string below to the input field. - -``` -com.unity.renderstreaming@3.1.0-exp.5 -``` - -The list of version string is [here](https://github.com/Unity-Technologies/com.unity.renderstreaming/tags). In most cases, the latest version is recommended to use. - - Click `Add` button, and will start install the package. - -If an input system dialog box appears, click `Yes` - -![Input system backend](images/input_system_backend.png) - -## Install web application - -Click on **Edit > Render Streaming > Download web app** menu item to download the application from [this page](https://github.com/Unity-Technologies/UnityRenderStreaming/releases). - -![Download webapp](images/download_webapp.png) - -When the select download folder window appears, click on `Select Folder` to download the file to the default folder - -![Select Download folder](images/select_download_folder.png) - -After the download is finished and a `powershell` or `cmd` window is opened, and run `webserver.exe` with `-w` option. Please refer to [this page](webapp.md) for commandline options. - -``` -.\webserver.exe -w -``` - -You can see logs on the commandline like below. - -![Launch web server](images/launch_webserver_public_mode_on_windows.png) - -If you use it on macOS or Linux, please run the command below to give the execute permission. - -``` -# for Linux -chmod a+x ./webserver - -# for macOS -chmod a+x ./webserver_mac -``` - -## Install samples - -You can import Samples from the bottom of the `com.unity.renderstreaming` package in the PackageManager Window. - -![Sample List](images/renderstreaming_samples.png) - -## Play Unity - -Open `WebBrowserInput` scene. - -![Open WebBrowserInput scene](images/open_webbrowserinput_scene.png) - -Enter play mode in Unity Editor. - -![Play mode](images/play_mode.png) - -Open your web browser and access `http://localhost`. You can see the top page. Click a `VideoPlayer Sample` link. - -![Main page on the browser](images/browser_mainpage.png) - -You can see the Unity scene on the browser, and control a camera in the Unity scene. - -![Video player sample on the browser](images/browser_videoplayer.png) - -## Deploy to Furioos - -**Unity Render Streaming** is also supported natively by [**Furioos**](https://www.furioos.com) platform. -That means that you can easily build a Unity application, upload it on **Furioos** and enjoy all the features of Unity Render Streaming without worrying about the deployment and scalability issues of your project. - -Please see [this page](deploy-to-furioos.md) to learn how to deploy your application on Furioos service. - -## After tutorial - -About general questions, please see [FAQ](faq.md) page. And you are available for discussions about Unity Render Streaming on [Unity Forum](https://forum.unity.com/forums/unity-render-streaming.413). - -About the operation of inspectors, please see [Components settings](components.md) page. -About options of web application, please see [The web application](webapp.md) page. -You can see more details for samples on the [Samples](samples.md) page. diff --git a/pack_webapp.cmd b/pack_webapp.cmd index fbbc00adb..8cdbb10ed 100644 --- a/pack_webapp.cmd +++ b/pack_webapp.cmd @@ -1,3 +1,5 @@ +@echo off + cd WebApp call npm install --legacy-peer-deps call npm run build diff --git a/pack_webapp.sh b/pack_webapp.sh index f07cb4f9a..14c03ea56 100755 --- a/pack_webapp.sh +++ b/pack_webapp.sh @@ -1,17 +1,19 @@ +#!/bin/bash -eu + cd WebApp npm install --legacy-peer-deps npm run build npm run pack +chmod a+x webserver + app_name=webserver if [ "$(uname)" == 'Darwin' ]; then app_name=webserver_mac + mv webserver $app_name fi -chmod a+x webserver -mv webserver $app_name - cd .. mkdir WebApp/bin~ mv WebApp/$app_name WebApp/bin~ diff --git a/test_webapp.cmd b/test_webapp.cmd index 8d0efaa37..7267b3c7b 100644 --- a/test_webapp.cmd +++ b/test_webapp.cmd @@ -1,3 +1,5 @@ +@echo off + cd WebApp call npm install --legacy-peer-deps call npm run lint diff --git a/test_webapp.sh b/test_webapp.sh index 496c4c903..d1153cf15 100755 --- a/test_webapp.sh +++ b/test_webapp.sh @@ -1,3 +1,5 @@ +#!/bin/bash -eu + cd WebApp npm install --legacy-peer-deps npm run lint diff --git a/test_webapp_client.cmd b/test_webapp_client.cmd index 354eda015..c5a483967 100644 --- a/test_webapp_client.cmd +++ b/test_webapp_client.cmd @@ -1,3 +1,5 @@ +@echo off + cd WebApp\client call npm install call npm run lint diff --git a/test_webapp_client.sh b/test_webapp_client.sh index 786900976..b73c81c3d 100755 --- a/test_webapp_client.sh +++ b/test_webapp_client.sh @@ -1,3 +1,5 @@ +#!/bin/bash -eu + cd WebApp/client npm install npm run lint From 06f100928d70e1ebe028b9a2da4833bb4ca15b6e Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Wed, 18 Jan 2023 18:27:21 +0900 Subject: [PATCH 024/117] chore: Update WebRTC package version 3.0.0-pre.4 (#837) * fix docs * fix for tutorial (cherry picked from commit 727cc6956fb1ab1ea10d950cc3a3c5da7817772a) * upgrade WebRTC package version 3.0.0-pre.4 * update unity project * workaround * workaround * workaround * workaround * fix * workaround --- .yamato/compile-package.yml | 2 +- README.md | 2 +- RenderStreaming~/Packages/packages-lock.json | 4 ++-- .../ProjectSettings/ProjectVersion.txt | 4 ++-- .../Empty/Packages/packages-lock.json | 13 ++++++++----- .../Empty/ProjectSettings/ProjectVersion.txt | 4 ++-- com.unity.renderstreaming/CHANGELOG.md | 4 ++-- .../Documentation~/create-scene.md | 2 +- .../assign_videostreamsender_to_streams.png | Bin 43043 -> 44771 bytes .../images/control_camera_06.png | Bin 25615 -> 45947 bytes .../images/inputreceiver_inspector.png | Bin 18971 -> 18971 bytes .../Runtime/Scripts/RenderStreaming.cs | 1 + .../Runtime/RenderStreamingInternalTest.cs | 4 ++-- .../Tests/Runtime/SignalingHandlerTest.cs | 12 ++++++------ com.unity.renderstreaming/package.json | 2 +- 15 files changed, 29 insertions(+), 25 deletions(-) diff --git a/.yamato/compile-package.yml b/.yamato/compile-package.yml index c33dd3c1e..cbd991b21 100644 --- a/.yamato/compile-package.yml +++ b/.yamato/compile-package.yml @@ -5,7 +5,7 @@ compile_test_for_package_version: flavor: b1.large image: package-ci/win10:v4 variables: - VERSION: 3.1.0-exp.4 + VERSION: 3.1.0-exp.5 commands: # When unity-config will be part of the image, this will turn into a no-op - | diff --git a/README.md b/README.md index 34090ea58..2f8f13a2e 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Please see [Furioos Tutorial](com.unity.renderstreaming/Documentation~/deploy-to | `3.1.0-exp.2` | - Audio Renderer support
- Multiplay sample
- M1 Mac support | Dec 2021 | | `3.1.0-exp.3` | - Fix bugs | Feb 2022 | | `3.1.0-exp.4` | - Streaming settings API
- *Unity 2022.1* support
- Remove *Unity 2019.4* from support list | Oct 2022 | -| `3.1.0-exp.5` | - Fix bugs | Dec 2023 | +| `3.1.0-exp.5` | - Fix bugs | Jan 2023 | | `3.1.0-exp.6` | - Streaming Settings Window
- Auto Configuration | Feb 2023 | ## FAQ diff --git a/RenderStreaming~/Packages/packages-lock.json b/RenderStreaming~/Packages/packages-lock.json index 8ff3a71cb..fb87c376f 100644 --- a/RenderStreaming~/Packages/packages-lock.json +++ b/RenderStreaming~/Packages/packages-lock.json @@ -53,7 +53,7 @@ "depth": 0, "source": "embedded", "dependencies": { - "com.unity.webrtc": "3.0.0-pre.3", + "com.unity.webrtc": "3.0.0-pre.4", "com.unity.inputsystem": "1.4.4", "com.unity.ugui": "1.0.0", "com.unity.modules.screencapture": "1.0.0" @@ -106,7 +106,7 @@ } }, "com.unity.webrtc": { - "version": "3.0.0-pre.3", + "version": "3.0.0-pre.4", "depth": 1, "source": "registry", "dependencies": { diff --git a/RenderStreaming~/ProjectSettings/ProjectVersion.txt b/RenderStreaming~/ProjectSettings/ProjectVersion.txt index eabd63377..3643b0227 100644 --- a/RenderStreaming~/ProjectSettings/ProjectVersion.txt +++ b/RenderStreaming~/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2021.3.15f1 -m_EditorVersionWithRevision: 2021.3.15f1 (e8e88683f834) +m_EditorVersion: 2021.3.16f1 +m_EditorVersionWithRevision: 2021.3.16f1 (4016570cf34f) diff --git a/TestProjects/Empty/Packages/packages-lock.json b/TestProjects/Empty/Packages/packages-lock.json index 2e60e3071..17e5d269e 100644 --- a/TestProjects/Empty/Packages/packages-lock.json +++ b/TestProjects/Empty/Packages/packages-lock.json @@ -15,7 +15,7 @@ "url": "https://packages.unity.com" }, "com.unity.inputsystem": { - "version": "1.4.2", + "version": "1.4.4", "depth": 1, "source": "registry", "dependencies": { @@ -28,8 +28,10 @@ "depth": 0, "source": "local", "dependencies": { - "com.unity.webrtc": "2.4.0-exp.11", - "com.unity.inputsystem": "1.4.1" + "com.unity.webrtc": "3.0.0-pre.4", + "com.unity.inputsystem": "1.4.4", + "com.unity.ugui": "1.0.0", + "com.unity.modules.screencapture": "1.0.0" } }, "com.unity.test-framework": { @@ -74,12 +76,13 @@ } }, "com.unity.webrtc": { - "version": "2.4.0-exp.11", + "version": "3.0.0-pre.4", "depth": 1, "source": "registry", "dependencies": { "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.editorcoroutines": "1.0.0" + "com.unity.editorcoroutines": "1.0.0", + "com.unity.modules.audio": "1.0.0" }, "url": "https://packages.unity.com" }, diff --git a/TestProjects/Empty/ProjectSettings/ProjectVersion.txt b/TestProjects/Empty/ProjectSettings/ProjectVersion.txt index 8ea1b855a..3643b0227 100644 --- a/TestProjects/Empty/ProjectSettings/ProjectVersion.txt +++ b/TestProjects/Empty/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2021.3.11f1 -m_EditorVersionWithRevision: 2021.3.11f1 (0a5ca18544bf) +m_EditorVersion: 2021.3.16f1 +m_EditorVersionWithRevision: 2021.3.16f1 (4016570cf34f) diff --git a/com.unity.renderstreaming/CHANGELOG.md b/com.unity.renderstreaming/CHANGELOG.md index c61f9af60..cd637c3a4 100644 --- a/com.unity.renderstreaming/CHANGELOG.md +++ b/com.unity.renderstreaming/CHANGELOG.md @@ -4,12 +4,12 @@ All notable changes to com.unity.renderstreaming package will be documented in t The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [3.1.0-exp.5] - 2022-12-22 +## [3.1.0-exp.5] - 2023-1-16 ### Changed - Upgrade the version of Input System package `1.4.4`. -- Upgrade the version of WebRTC package `3.0.0-pre.2`. +- Upgrade the version of WebRTC package `3.0.0-pre.4`. ### Fixed diff --git a/com.unity.renderstreaming/Documentation~/create-scene.md b/com.unity.renderstreaming/Documentation~/create-scene.md index e5258c5aa..92dafa398 100644 --- a/com.unity.renderstreaming/Documentation~/create-scene.md +++ b/com.unity.renderstreaming/Documentation~/create-scene.md @@ -31,7 +31,7 @@ Add a [**Video Stream Sender**](video-streaming.html#videostreamsenderapiunityre ![Add VideoStreamSender component](images/add_videostreamsender_component.png) -Assign the [**Video Stream Sender**](video-streaming.html#videostreamsenderapiunityrenderstreamingvideostreamsenderhtml-component) component to the **Broadcast** component property. +Assign the [**Video Stream Sender**](video-streaming.html#videostreamsenderapiunityrenderstreamingvideostreamsenderhtml-component) component to the **Broadcast** component property. And set camera ![Assign VideoStreamSender component to streams](images/assign_videostreamsender_to_streams.png) diff --git a/com.unity.renderstreaming/Documentation~/images/assign_videostreamsender_to_streams.png b/com.unity.renderstreaming/Documentation~/images/assign_videostreamsender_to_streams.png index e4a75808436dd431b4c5173ded98da3f7d0434f6..ec80f33f708f7c75a1c1ce7e151519f692b3cb63 100644 GIT binary patch literal 44771 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 D(LXBh diff --git a/com.unity.renderstreaming/Documentation~/images/control_camera_06.png b/com.unity.renderstreaming/Documentation~/images/control_camera_06.png index b8f10b38142615d5a95fe59d64d75502c0e184cd..3e366f42cb677e587eb549c0dfbb841035c7cb10 100644 GIT binary patch literal 45947 zcmYhDcRbba|NoDKB9vLS>e#C?$`;DV-s70rdmMX3WrfHnve)5oIJRRYD`X$rK}I-M z=CKaHm-pxU$ItEN<{0Pox?b0HJ=gvDxI}BJza}STAO(RyIJ2^Eu5D1w-c40{be1F?r$;b-?q61%l5-4jw{s#h8 z-&IzS)Acjk#*locwz=%tBT1dlpph#i4W}hD{6T!|c^ux7m9_2c>q&RmJa*80mAT|^ z=eD+CREe8s6uo=%W(vh-!NM{Yuiq5p87vM`2ALS|9n8l@o0A; zG=dNqK$s)Ku3o@(dK8RM&rF)q?nyxlc+{#oHqF#JHqO*KH5ntH)>`4LuM#jbs?9w_ z78%OZ6Ce3<4J(7{JU?h>ohjR(k!8We{azHyo^y8ik8!4$GxDuVYznV)?`xM`I^Z54 zN8&DH(uRv1)n1}d?wc+gb^Ks{K95{*!_Z>uv)ie-Iy=?eoApNXXAZYrEABRL+ek&) zT>m)DkTe%9YOPusVq7&c)?U@VO{?ObVXTca`l!d7+EhrkHk*gbRgTq?OXOl7rBBEE(C8{8__(cL(KVv}ab2dMTVNeb`%-$lx2g-C(i$IK*c?8q z({+J=Pa721B2d>^N89@XO*Ln3*gk2mj44XpzvU_jHMcBm6=)dnoDlr_emHMCQcdCW ze>Vc{g+C+n2Gbdy5(GXx)Dc?ar~4JSO5KjUnt|CLTwazjFm6&c(HBd+I|@*Kqx51sua^f@P|1cV z4pX<5^;?!bk;^1H(9iKS6uDq{Jg!Ax?}2{Odf-Q1=IVg8SG6hSf!2sSM~(CAS3fMX zes~Y5q#QMaP>!A0EA;23qP)JE1wXMi9g!zpLxp*^VN%~rK50>Asq5tuQ9--7aVfiD zQJzCuJH8m6AHHJI7(+K#zb#Wo6UbcKSz2QmoRd`oMQe{}<5=ONSla%%(GLgBHqy}~ z*B2Ji5NglqqPkNOYaq&X(i~paDcZb7#v_t5mZ##daG9j;_-)aLl)u3xR2dV>(eBy4 zt-#)Z_lBaA)GJM~qNL^MqO|Ul z3JO$)db8uZ0Ah3Vd(gwd!c?K7!+yHFtlBf&ZuVhcqt{vh4g~sDrj{W(-*R{&O#sqO zeY2n?)dcnEPZjWhs;HP|ih~gaKGAT9_Ut%<*_fJ#9a^v}_&EuF5Tbs2fHg#o^fEWB3VEab0#%*EVl>nq9 z;xtjF8BNlv3Ifq8FKB2_M@K5K(HB?1VB}cHKgS8v%VE>J8DfNC|@sb zShz^Vbbv*4mopMf*FcnJI9+grJQ3JSKPh@hJ3G*g_gB>puJN<^GGX8@WgQs5&D)6B zJF_Pyaf{9#@WWp~OM2E^c>BP083n=GL%+$aC9kh4tEAj+=p3!+VD_WV<2G2IPfPm# zioZr$T4}1ThRX}hp17k=Bp)Bp^D*wj7GSTbtJ7i#A-3#Qo2XboDue;M8 zP}*G2Z$j#=k3TzA_1|=;KrUBY9zEW>zD160W!Uc~)|Z~LOy)}jMizU0sjr=%MQ?Wz zO1g#4A6UX1dymkfV#M)vhAuj3|CXSKH?&$Nj#I3K9BJb|ddI%=?&2Ky~pu$GFbB>Om_cx)%Lne8JHpTZcdO*1_fj7%yPpFaicq=XLC4g`dZERILnZtvrjr&BzT5XCEA-#f{pY6~Nt>QB zn)9WT@m|OVFslni{zXj}K)|z#_%eBK|X5UC*7lMg(B7K$H}pPv0+rG}Sfz zO?N7D*gf1B!~FZx(PHPBKPl!g+Q|<=q00@CKPLcV`;GA)Q$rHYFA6lSP}Tcv8=Fvq zl!|`rChMpp&naatDj6kw$}mHdWk%IoO%EA^v#$*`Fqt(tRM>THDsW{EI}Ueg+HJvQ zogdG-wbXvNl77CP6EXMW9U*2+LuR`>fz{k{L1ZJ|VC`(Xe!*wCJMLhDPOueDpeq_| zt5UQJ%fg@ z(w?Jw!~*7facw0r_aRgaG`R6DVX?+ROat48cv>P%Q7Spr~AGc_gW8^zgkd5W>l5Zn$1 zqb{+%VmtWVA(rOs+#HXl1=n$hzzP|k#_D#q+SdjqM3`FRHOYX3#4jGpC{IBj*qH7Z-X3=Q~|kE+&| z!Z)oGtQ7TTfu-N?>}V%p-4gfNvX{v-uCf1<%5U;F zdT{+?Ld~%s8?XN^b;WM`<7FojGR#9xyFzjbM~|&~>}JP8B5KSGY-`LOXyMt+YaR8L zNE$WxWrS}u*A>ErFDbb|T(4Bb?f=Fw`_7HH)MI?2yEMe3c1W_v@?=fg@jpM1wP;W4 z=`LBe;g7wJu;X5#XJgrOoY_H3(r4?B&gb;CO*Y$3mdGj!RL_I&9#@;0*PNA2;^JDZ z1CFq(D4%WYs|1$qqBq~PKf7$2!pf+~RF2C`>Jhp|SAK5Id#@xWa0{UegGUo4Zreb{ zxF&tFNH|+D*fQaU?4pY!=eH?mm!o@_~GYls)7=@~SId!ff(u0jT;bc9ZDH3^F!Z zZ|Dad2yl&!4s~4)t)|smP--$>?-?_{+^284nryoqhXfyuyB=>%@0@K`)I+85x5V*> zW7{#`ch|B*eS9&{G{_(AgGEEB7P}=x^S;XYJI@|w-ziEsFQVaRAV+J9j)v5D2<)_` z<$R}UXdpvuNc!!eI9}*%r)hU?u|1;vQ%I21;jsKphFdGL=j>0&&)b}jr5h$H%-sec zuRqu``ZIie8@_y8E19NYJujAz`(H&U zzJKC0d$WBvqB8qP<;FTsi@8B&gu_@-BLN|C!)w-HG!@IKYm>`t-45_{P^;N9fzjh} zD$QAaPj)WM9=7G;eBsJT11+E0f0LZbH=R6` zZM3uC_J!DtB5A#nR?GO}Ja<>GwL-BxYwpUC^lP&!j{a#Pz zvIa7K)Jd&TLtRnO+sm2lyjt64;@a4i9!0?*{43<}hU}BkwXz#xam#m2U&S+Rq`3RY z9Qn7rs^HfL{;V+gc&GVbaHnB8zS`nA#lUPYYd~Puu4P?9Df*pO{;Nh37QY{e-@B+y z#IdQ7C#H^yR>gkhW%RwC`6<=N)3->=W?$IXJZ6u_LN)v~rmUi;nDbA$KWrS5zbstme<13v;B zw-;rKp9}1rS!mKHKHbdFHYh1OE1J+#H9Ar&g=dku=7(>Z=tIEEjpN#HkH~>vXa|$& zP8#y(<+DcZj~kdzmxtVm4*7TDEuthN>}|6JxZc{84(kLd+X2<_$2y!YSaiv;wVF4a zpbM=a!i<;Ip`JTESew-=-fg>@mpWbf;xqmlB7L|#UaZfIGA@Ekp8Y8ohc&{|XRss* zbRrw*6v39ug{#Z@bIfjt9bp^+FvWKH2-s$y2X;vn_ucHvhF9%oCK7#f3c+@B|L+r`--ShJ8*8`b;vnXMW4-M7I*Qb%YV z(VW%lA?ZzyH(xF2slAD zeLhod{ncK85w4mN;;J5JnzV>E;H@g${>?e|R<|aCSk2i<-u~FOjufCXdW>lm~Wv($~K3~n%-S11CmFN zv7D#>%VwF8v0ADWcZ-urX!IRp>L> zgU*x(hK{+dbqd-GQP|Q#4z$5TRmsEc*-6CB4W5_~Nr-zp(J`Tla863-QPXnx~G>uXe+se;LALL z2aex9yXCR#P2mAQle5TY0jE7@QlUUwB`ggYgjukqaYw;_{ixzYiy2#DdE3QJ+J9`u z3)qduN9bPOMp; zc&sfv~!k`%v+@i2uB!qVG;cp*Sw>2;N|JsY=SgoMB8X2cx0R+ zt}`>bt~jJ}CM*39u-US9wyKBfY>OP#OKoA6>ph>%icxo%o)4oU?Y&)YXEcMtta9Zp zy}Jxm>jX5U;o=3D3z8O77gLf8x2RB&d8_%LF$HZ({I|FWjtp` zag#6HSDS8OO~Gt@Yk|GS54d*nuED zrbs}(avdtCbib+?!#-sT`>iCtc9y^nx{oSz8oeO@3lpHU36A``4}nblXa2Xzc4%t! zOczr{Zyh73IO)m{*PjiWS2z+!)W>MsW)dEy^dY=T&K%p`iPM?{gVVg2$|_R(O1%J7 zM;-mqugX$BPq71=(o0{blDT||Kj&;AV@NbH-?BW*p-V@&+TZ0S4_c*E_@;t+f({b? zO)C)^$dJ>QO)15Cxju8|p+7Os0)pKZo)5py+;P#iaWEbbTN_@nSlQlzYrK8=IG7wt&R;!Fn9ok$!tt=N_L+B^Po(Tq6Dk-j_AJTx|T- znjhe^xs=Ke*}`^PF?UITCqA|4Z#~nB4R7$1g(>yAQYCa8m}lpJ_`v@LUXS{a)u@^4 z!GR}LA*f>=_y1h#6R#Nk5p^Wy;|6K*^e?$7ZwSs5hcf)dh@q--Sj}yYhCYRdyjg!8 zBlFN#95tGNRIYHY%h}n}>!&BL zJimmXrjp2wCsLMuwY%)-K|l>p^BAtzvC{jcoUDh4-qZqfTLkeQ6%8vS0Ojm&9L%^; zAx<>kWf0XlIN#Y46^J>#n4H!Ams2I&HdyD?rmlw^G=7PeI96huGF%{qdHvhXV}r&w zv?gg}MqoJ6D7kWmr_gM!ggmSx0SLxWYHbk9RiYY!qK45beKq3A%iBvi%rE$iQ3mCF zeGjmu;)2I?yUB(Iv3{|UCwarp*8lDY7M$;;b#qt3!$412_07li>yZH*+@k@USf1vx7|pfftc#dG*q~CRE@!fP^masbOmp7NR!gM3P_rTGUM=y}&eR)Rpj#6!x5$%=J>KPH=wpDh zFE4)@6?6WIDkEI5~1wd9vo-`s@2#8s1z1T^y4Gb}R6Aa4JI zxu1(`2y7|jjFY23I$s3LQsBjD{9DNG{kALjy$MYP*6OSCun%dXJ(nu`kl6nok1I?; zC>yKfX%8=>Dq(A$V+#k0HD4cR7mj?a{(DlK<9a{jWu%b`iFJJ2j zqgea?f4u3vICQhis^}s@lt?=o-iVrEoK0TXl|sJ1_3PgtPT%Zbf+L^R^zeb~0IC%EL1*Ip=QgJnhRALfUPfebd@TA0Z}=i_nGk`E4X5(ck{zgqns8Y{j-I@948HkwlAk>W zRl7M3H&|C(t=6*2^FR12x~OV1LU~lAmJyI}y=uB5aDBul5(pUVlaX#;sZ{psgrIO- zjnHBueq5@qGaLRj=ZX1v?v63ms46#(za;vjNJ_H(FOC;T-UT#>N>65;RX{vA9@8fL|0ck>= zTaG4;AWKtC2j#c_|Qx^ff(Ku+4xY(gx^e_2v;F%rL0BoDVo3B+p8 zq6j`_nLch%V}UQ%ekWc;T;HXCRfW>HX(B>{Z=^i;%|B{yYmC}tRbG<&`}>=Y?Mt~K zI1?yG=wg}|{gb@`0Es=kf3`xf2fuv)7kc|x<{z8y5_fT0zUcpVss$j-l}~(s>s!?lJndy`7XIF%6QjGNWg}Dm=3r^j1yf+hehrC{(4d9}t~w zz|98=>iR?Xo&%txJg^%`Gyk3)Z#zN1i9<^btnw>u>K8%+>w7se8pP)PI!vhDCZO#^ z^iv5ZK&d_bgizLIbgj@9G38L4x#iVw4&bo$>vH|%dn&-GCIB3x;R@7=W+hwK{zbbV z=EMvFeJEW+xDSr+SRwd`S+r}OIL??966(TPMMs%8afjFH3N|)!#$_yVjT0drU9_$X z!8qtb=v7PLYKp&?i5Qy101lw^KNLH2LbG)83*9eP9Ibcc&g{ z8(O>HU;pSMOgGAV1=8p!Bbhv-ARFgxSLZUg%k$kP*3gTQ;M3K#x*BI=2~iJ4 z!L%yW!}WT<;Vn89@%68VGhMOtCJ#U`=d(|`{U4rKcQ?4To^l`oKFu+L{SzzLS~*m; zcwj8%zrQlfUmsJ_6=96n@OtrFt=d*@^ht5`xW37>Z{Lv2)mAw-Qz$Wq%me1tRuIUe zvBqx-IthXFr}ASi{0~gw6E0S5r<6V|0WLPxTebEhqx>dye00=rJY(qw>xT~FXx44o zsJm=PjArC_jVv`|_2{^{C5|hsl)$R_rhfH$zQY{mi z$icc4T-5p>H0f0qPJ$6iPQ7)xDoH_LlV|>&#kA$=M+x{Wdo;SMxY2nVI{|mYaPs6S z%C~B7RvfsgqHsB+-$TbGobit|B-gRiThrAouRQ{oj!TUN4s&x$ywINPDIuB~-=22W z0-XewHSLLFxFfK#Br9Juq?JbrK>G!BZ%YVGZB2Ar`O-NK0cKmf8ZWU^sn5jFu?E;9 zPNfszcES4k{JVMjiRU*!?~7xjRUBtirfI(YY>6}QqJ25;$!N9rDSfvmkFQPE*=1v! z_RETOlpe;as#NF70vFvqVA2TC_XeBTzg?+Bb!8A*gkrFb^0`<|2&S8@g%nS2gtrbo zx7=j)Uy`HAflnu`icpOf!}Hq}0A$R&4A^fmxS&vS7G1my~+POtJ@qJ{=Sf7MZ>ax6f-8 zztaBQ^ZCe)EAq@hX-N+4YlwG&GNu6Wv8hls4Mo8$?&p+LLe$ahf3OuIU=z$E6LjM4 z3v34C)Wh#ygUTD{M4(&p3nUD`#qOl^3!BLsp+6`9wNcBW#PO6hW;jv@b9Xs?z)A5WGHH4;S9Xat@4YTvKR{UV& z6ulGBIBeIP)ZMO774K3#AbP;kmTUNDkoe3q+O#{&s6Y)^BSVn z12Me`a2#_|0BD#nHK=}E$U)K9RM8#B*dznsLQ(vzU3ReTxpsXUZ$qi41NCtDAm+JDn)LDV7_zywV) ztVyWqmL5K^+{_#DAX+yjCTDodPabf%Zdrc&XU$j5pZ=Xu_a<)0^@hn@oergj&4*s$ zm(72&)IJ^lO06QzbsN9mFW7Vqzp)Hkqls!$!Q74ID?ckOJLWf@ zc#qbAWU`92*ojVd;pmx=wZw^V#(NaI{fqOhYK+gU%No$fWDPK72XkI`q;|Ew7a30Y z$&?${?oV_UTa7)ZO0N@MpSH_B+DYM03+5gvCrF4u?-PJNvjRbpmdtgs!hDl%Fnd`V z;6=E;gQ4zJyE#}zt9yT%05Yd+c$7GvJ-pi@%$yc>IAMuGKqUBcjA= z?3fz#=L7wW4=9I6TW0dQJA=oyUDcXMA(R}sbB4y)qz0iJw`$#=0|?vY#qy_TWs2cR zKe45EZT0UQ9jU23KVKli&a+`va$O2D?s2CPJnPs}RlZgGNlDH9mtflkRRG|@;=J2E ze!kz^i8Q>Z^f|&#*||zhR$3BHHh9Qysu;AHwM^Ls2i-m6I(=u2%6W31DV6l@BMJ(y z#O?c4Ky|YYEN!&)u>a9eAZ;+~XbXLp_Z_{5d!Gd%$T3QT;2+7rRt9Nexb9wJ;T2nT zc_oT39jgoyyfO)tAx{q6dqP`UG9=-ss(X1xIWuuf>~&G zi8eJoD6A^@-+a@~1}BTJ%+*N*Ih8pTS@+=!cxM$A1sF^^yp-KW%YJFZawSIh(i7SM3!fx>UhE&Yh^2?ih3>7a4RX;lH-Ti3%fU6`C5t!L+ zIy_$WQtP43pPwH@qu%liMCB6j6M!0xTAibb{oJ9OXpdU!cSe#taKmqimlfu1gXOr^ zbE=_0gN0bFvmI(L7o!$O|1GwdtLMPfU7lOeKPdh2MCspi(MzeW)Q92}KoK6MvZmq0 zf#TQor3Bv+ex8rwpy``558XaH+B9%1}@#t0k1}5H&tu3?t-Yqtq57kHh49uq1Qc8jR01 z`cyY<%jgT(pTE;+#h2@DCz(LQkMPSyi!{e~?f|UjK$*fMB8eF%4u3C%oR>ENanU)L z{kZUPge`Q`NqBJuXh82?0`}J^vD46D+x!nl1QOA!Buc-+WC87dI~>CGbhHamP}dEG zxzq#dMiZx}{G=^>FLH%`$960wR3Dgsv1qrLau*U)KIi!tlI#pK5tRP52NZ-4nKRh? zmZLmpOO`@e<}V*g#7~L3%#KX?-I;eGNa&FH_$fM42yclyA5ffc`E7Q!whdIHve4W` zv6xPIy*xo}=4|2$-8{CjurFG}3Clq9b;{UN5~!x;jX|()o+^^wn^wz`=sn`(GKebS z?TNk@dzDYv4i%W@ioWPZO5zrBw5SbwRE3q$y&t_vJ($K<@m%rCl~=RD*3e&SD3{^! z8=x>rSjp1&7hZEU4r32h#k~RL!*#iWFsinlPk$cwBzJ=H3#Z2M1PJsqMoeU~YW~J! zClt$_q)|=Tul7lZeWa4JwGM7dyS+X4q$fvlTmvoGkK`J;yCsbj7}}x!kUZo;b9Iii zv}UemigcDtHEjGL%Cr0Y!g{3DTGgxv>A_gnnZ*ehrEKCTja%^9A<~rzNRK^$hizyA zy4!O=yk8Hw*n5o|dClq%CIFpAk-TE0zH4lS`iE*} zl555lgT4+%*LlXZET!qX%(NZUJg|)06LOvs0t!fmipQWFP4w!^dwN2*1X@1}NsTv# zbi6`%9Hb&V`X>;|{i3dzGk^{LJl#|1z%Ho>zqfqn5*>cHq%Qh45BK;uw8>l*V<2x7DQbnT(&8sqmWoj0ru>f>d~ZG+sEi6Uy9 zdMZ}B2|G@#$*L~G>XisPwU>qY%=LNN7g=?lutq+{ZcIM$p)T9)Ub8O!jqEHaflIjz z^`t(OPK`S{G=zw$1W2zovR%8L@2l6)kT2Lz(p zVOctc8Ahv2s?O*0A!Ja#z2zvuJ?p(=1NI)OsWyWmN$*?j*4!KDk};v(7u7+ho|7=G zM`SAfqb_M7fO&ER?|t*FXxIW|K|8;n4b`+lfC27La7e6tmY)Om=JtFr4&tVEdV0<* zWFc!c?KLEYK{V|+u0eG2iHdD>rdxy0Q6u@UX9IPV49xrs)kZ`^F8l(SfD%_%(6O&{ zsLc6dml_1(iMe7F9m8p4=<{34otG+1#>|kSgzF*zZ1Zn0T>^rtqA^BPQ5gJ z0EALk@QgXnNZY&rXOe?ay=x}ErJ(NqgqPMO{_TvLIBkaCtv;z?mtmO0!)eW?f_RhK zAmCNyyu{zW#B=21lmz}58(wKy4NkL21^g0kKaC`dOWyN%MNf^FfIpcclt}HidfF&%(J zj=!P$Z>22V8RN)8%%yD*?H=xnYg?DnQO(~=(rv1Df4YY?0qDC!4c9?wl<51b zswc>??50Nrr%*qdFroCwBYt_1{H)t*{g zsu;r>L9j16uG39w6a{sX2vzYGqLk7+5`q(HN^yN1%iDDh7PDjYB{gjHEX2RkyCI87 zQ4Ezq*-x|v2LkD(yorJR+4c*k3BD!^82;>B{q=%Wl7~K-o4DEl(6))Ut&P$s-lz}L zT(@mc|M^@nK2~NhlZqUAK^B9|(cou@;com4Hx`$fZ`8cIIl=pxACV(TuiR5D&GOYi zuibiNVdmq*Ah%LzEJ^SQIg8{JB;<6}6;Q1mOmK`vP66xfAa|v2;<8#k@Fqe&EBJW2 zPKxLsX@Q6s{@!aQOY4`(ktiRSzo9!Ka6q-}hm6wZHC0rXI?}E8-q6PViDWf4NCe*1 z1Lz?`%u1f)!=B^jyfAgMuA6`o(E4aRtwWhJ^R3P;B3di+w#%am7jqSRd-F4Pul-)m zCN(jPutU5}aOLJvfSb=ku&+b~fbpzO>F+9uv)#eRt-C26)-PY-6~1}jbul^rjE*(y z;+QIJHx}mm>eOGQcz~G^ak6U9O=3I+Kjn!CpH|=vGZcaguuYIF$f2%GX&vV3?^Mx3bvsf+29L&g_Ve!)rkQzOqiGd@SN{b>VKq4h z5az1skn?Sqe~Mt9aGKBX(`gEct;OEHqAKkrp-)R5-V`)8YutBrnkC4 z%b_+n=G`A{tEs z88`STXK*r3`Ug<LM;P!VY^fO?i2| z;g+_MH^Q)QAWqOko>^_$n_tMC4wQu7E|GkoQ#)(7A-efq`CZvD{Vny5o7LY6$US*a z{I)BW*8uYUO+vretDBv<8liVyy)u5Tn7N%~VW?GE|ZA~_n9TF2FL%rD~a z(U~=Gg8OTvw^9O#@TJvsIQ z_AMi=OLK5sS6s+nd?t0jjmho#2%(M7*S>qr8QEp7qI`FEQf|E-Gv;}aV{~6T!X8ZK zeI%m;CoZ4-5qmo7BvSeID;=dJtX0La0A7LUq^*)EJ}%thV^DaS63;m{u85Z_jqL`K zU;7BnQg|=Gcre!vr&!Mg94on-vNn*NAWPF9{$KU-$@! z$_%U;0Vw9uLd=evq>UhsOK;#9FzHHD$g>P7=>7PHl(y2q+agOjH;JcxnH%~hC0LT^ zc~QY~Nr82h$_X@PQA#Dg4E`=ViPpm(d^jEYYs?bhTQzDKNqav>JqqZ4_DEpA0l0~3FU3g<|%FT|*=NpZx zMV1;uIi=u->FSNOg8T8@*^d>v-^xRuv!wLzv*C#V~v(3Qt zfH?G9-Gzx;3#~*VCpqc;FYxqTT^DLf=>gf? zqH|PLO8-}42K%RwenM!ZW-O_Vd*q!5O9BmxH_q8h@LYZq88jb@ve3?S#uVb~Z1sSW z+hp@5xwui*4ns+@mi{Ism=&EOcchoM9C~$b-cC(9lZCwkXy>ejoa1Q@9as88;o8~v zC|&!Rf%DxqnQezit=_uJbOjiQcJQhcQcVoX^64_Ru5{Eg#kC^Gs-WWx+UlXf!0h zG9{GgUtQwAs5r)Sh`P+YW$IzGZ(A^3!Srt(h0gC_;2~&C9?-b-8K2Ky&Hd_7Mg;jb z7HFEd%n4|t5yvIqNGfqqsWg;#3=jof;*@R14*RZA{J{4w;+WkWxd>;T1wa}sm3UK6 zl^9y9Gm5@)HlX@L8F~@{j;fVbBGd~3a*Q3oA^e+ozWK7~K~nOH-u}QKBYvH=#y4% zff)(QRTd|^cx>O9?Vl(4nGNYB=F}OpekmyNH5#JNf61y->$Z0n45^)@ttkEMeOJw- zvTOpqvWCQigzFN`NH2mcYH%e$f&SVwLo(JO`4nBxAFZ)o|jR zJ0W!ykfw>G-&pZ`sb=}pN#PeBVaD~YF7+TyEtGL#GvRj-IzYv_Tb5FYt+evh7@|WwyWQA>N4)IVOOB#RcaoE{o~@ z1-%5h_i-0bJ-=fdg5NZseM7^rEQeXqF8T0Px&!Sg2SKA9ReQ%xi{{Z5T_w?GSJ$uoWTDc4YU_z#O4dErTS+n19RKqg_!Dx}ULgR+H8 zI?kbHQqH~XLK;0X91@7BB0OGq$|L33^k?-F>;2<&G0U4SHNYVLY^RErmTl{ z@5!`B*W+W>5s%((>lpskJHMX+(@IlR?C?a>%*XWJW5%aQYf=vOr{n?~l)uC0Xge$% zL4Dn8rl-1*?O>e}viDRJXZH}?;lT_|w(G;Wsm+>JBJ16ZZZ#h4uRh)XQyxtzFrELJ z64xVhIbuRRS#BbBjWDP0$o|I^ZlnNLaT1O5f0Pd8q}TIz@BRNyVi}R7IS)6Kz&C3{ z3~Q~vnKX&lKf&uxu@ZDRgzfF0i0KHl6 zygqypp&Zj)Jfi!fB@kElgjJiGl3Colaz9-}UM5DQh&-A^m-aEs=Y;fRzt1PjepD|Z zPxSfCv4CPLg=s}OHLUV0IKBfe)1x<6iuJfYoV`-U17_JTM2P_>$%@$3nZurD0{Wtv zhI^kJB1+$P67DCuE#)@zNbD_=_#H8dOn(hC)H}w>>*ZKiw_YJl$c)R~LdMDZbchmo zSw4H2rhuibfTQ+Gd8`$Hf8p3*18M|z;GF&VCn7%;rB1pLP#ABTfR8HuZzvdwosms8 zzSz01r}(eo!rG)QB+~>C27sAqhJmvk&hW2j+;15Hj7qIE;-R$Y|0H&IsqgGufZUeL zhvNbAlR?xEkuO%{=!hP=z?_)B-p*Z2FivKZHv&E2iS0aH-dll1 z+Vua8RYX@=IT|wv98&_66u)-}zpcBy1*C93=Ajao3kk$K5R`WbCLz3To04K{w_0-P z9mKhWma>nJuj+>Y-TRMscAgqFN@l%>mEBbEC)|G%!>&1W|1r!Tdo^YnHUu->zR(Sj-atzF0ci%Fm`)A9H1;CJaSt-tV@HBSZJOjdh# zA?!9kw_~ufpyA&3E%`;mTO4Aq9c)cthKNx8n=hANgivn@@L9+v&mS%e31OyHNPBc^ zNKtXC-ZNe;8YJjpP1&C|^{e3FapGXN9C!Y#-9>OLXVeAW?^=?kNj)A*q)5&v%J(bf zt*+ZO|4|V-sfcRJ_C6oTxKU0@`mfWC0O zd+260`j8__K>P8Tu_S0*9ykrz90&p_ME*}p2x5EspFZ&u9i{BiV!hvenUH484Une2 zRN!$z*MEolK~WU{(_@0}KKS281KxDajDI}<&PI>Rqn@znSD0;2ARwC)PU(_gum2B& zI21dDP6UO`T@z*yh)TcA&@!4NiK5qA+S|-K1O#f#2QJQL2i&_f(ju0GXu=pcu)8}` z@3spZ-nAcM&-(mL#3>c1%u;Ftlw3|LbH`j=J2;SD(+!ZVNoaIE9jNpx`Q~>M(b@6z zXWirJ$H@As3wvNtAW;4e?aR?z;Y`X*`n{nq4#T763xAyJbNemU(w$XC31r8MhAOA* zzDbWAP8SU|yv+Uo2NDVBC1&t|o^hIAg;C7)J4O^ZBkQ^y>A&^*4D~?=)3;aASnJi@ zNWM8S<#pPynrbo! zUKDhUQo4CL*~SSN0Qwf54C#(TqoN_<;5wsClHdti(0?=izG#m;UBt=unN)INAZ`cX zO{53;wNq1HDV{iP$o?ddh2~wqWjc7b8qutDt;S`m0rWoO7~l|3^b^Xe7puuSa{xD@ zDRt&)d38Jf|B?3Q;Z*i*|L96l5lX0tG>{>h3{j>gnKDm{MU%`V7MVpV6_pH`Wu6up zmSLGvhLD+MT!@4vQ$pr_e%<%`KEL0wfBW70IJQ6T<9?3kK3LbfuIqcA=VvvC#I*)5O>)(-o*9-+})3khoK|%T9Ha0jLAdPk1MvV`_rB9M zJlprHU~y+xpH2C}k!|o9-yQ<{up6jO9^RDFJ^-M=$E!^U=t4$Yc=t!7w`0VV6;p0T zKXqAZ{5{%$OSm3Ts(`@+v;QnPMtt?NVyC*1c%Sw8l4uFL7{fAGBIR_3*gKkW5I6K< zkDoHgtul-pIQs1H@ZzAC7vfXgtcF=%#XcJLpN665;r5+OjyZz~(;y8fX!eKsuLn>K z3-zu+OP;bmaZrz#MjN zOTz5PYuTS6do+|qG`PgAbHXSlk{dcDszeZ!6(cr~@!*+H$j<9Oa`60pl^PFbKn}~` zD+Mcesu80rmw6#Exzwz1xowGF7@fF#{Q*lZMmn9>!@>DQv!5AopeN{OI9+;c_a1y* z(cj^hI5;pP;g#SOBohqBkg0eHiNLaTUOq;Y{}i#ym>e+-|8xM=THaMZ7DoBrC8X2! zoR1vT_xaH%G3Yoy)?Aoao$a|Gl&viqEUFQmKL`V7C!*h+GL>XfGw3lLqFW_E{WgLV zt!~?cYNP}ym$GID0jDCLlPBy;z8P()=(8}{RZ{ovWZybs7_2&v#Q^|59lN=f;zmtN z`Bi*zrnH^paQMNeZCL=}={BY8_yj_j;SF$oVhyGgb~6xj)rXG@avapEjbGmYDVMnC zQ|blF4*ouge?Nl`7W|;XMJ*w15jRy!I`a?n->2SeEwaCh1e}koqVVsK*QEaNlLC_p zPeoxK_w0%dlIlzvnN^6okFWQR2#{>d9^-a$ZBkkOH!$R9>XkFE?Pxl`(dU{!3olOx>2h7D2v{EevS8HPQL2ab1zKj(xkFC7jSmHU22}S4lk^|;8I8_2 zSxo?z$GXi{?rew}-kYjoNqO*vQKAUuQgV&G`jUS}c6o2Kea#{EM6>uRUz$g>f9QW{ zg}s*D=mz(HqY$oG`!&cj+PFZy>||8S?UuOOvdk8R0R|L&7fa=*991zd)@qy8N^=Mx&9eRwDs%Yeg`uKjMn^%dAm zErKlCX>-YI)2w}hcC2*}ETa9wh5dI_v&4oXr>$Pd#@4)U3HER$VE6Yw8VL{=GZysn z0cT0*E`o2{jr($agKPBx`wQ}dM*tQ*qGc!m(5>76A?sTZ$XI`3jU zm-gcco$a2sZ-*k%j^Q8F)hCX)!W%&WP1CRQa@+4v^W^Jx@*WGkFuW^>8DqHz%A>c^ zS+_;&62bQYF?z*D7Ukx=+dMZ{2MzaI<5WA>R*{ShXYnVkuJC%No_AbwD@oS2|AO01 z-{v%C(n@Rw@8c{Z>WicZWud*yZ5s&FLvHWF2j4GS;p5gP>XUr}LFxSMdOjM9j6ilM zEE)Hh$2Y$M9^0nmmG+Gom!F3?&DAPPgXqew@w(Fa**vti>AnRSajLZ+4$(GAFBv%l zuI83B{5eLV2RZRBbo^Ndg6L!Ax?`!*)x9d6dz3Kz`@%@57ZQ3KL*nE&AXj*KfgGDR3@_-fGW(A77z}uT|&C{^5t+pZcoCr5m{H%Y}*Qr&gR- zyYIg4VT~`DCRv3{4%1?mk3pGEWwjK?k@a`D8jt>N1>y%)k`l}8Tcgch8+ofgBklSD zv59?uU9G);LXvhn$E@m7#R^r(?dG;h_&=v@%a`vjwOnEDaqwDhFzE-sr?s9Sah(Te z(V4miu+Jc6b7M(XnO+o9EecX}nUqeH{%KR523jD2b^!`m_EP_vR%j4LBe*ANR!;eJa3F4mIOgst z@vfuFFN@U+fWD-~lzWxc2TY>!1B}c|lvMaz7&JBI$MkCtu`ZTQ)aOQ&?DSPxC_|A1 zqwJ^jhY0fcL~lvFuK&=v+n;x2Cpth5$b+8sdyAQW5HCjk z6-%tmfEmsOja=G-wEHaYa3$7O3dJtN+20}0zCMJzD{wEN>hkL16s|c&8eHE?C5fXT zT+KO`cHYLkZ-Nf5Es1wfk4xZD+7_1ekgnwTcsb2q$M#=o)wQ5Bss9xk6j+ck$Kmrh zPOVRC(!V23;!BJKp0jl|@hndV@8}jLdQ47#rKGLG0l!{%UHC~G(OolRXPI$f$Q^@A z_8MmIjGY)Tor=SGjjNeLr?+lPlKmdd?4Of4WIcAq#8g|_h{c<#MOp{&Uhglt1wdLe?FRx;zcmsB?lJFpQY^-g|*E3*6TmRL0*jUf5lhqh^ zr9SB& z^RI9m_zv|}S#+OTp)B!%`s7pOd8Z4Sw-~Da{^gnB%iVa5Sqo13?$f04_{6g;5I!S2 znf@VMzy~t4ByrM?70@A&S&shYHos1T=PHH_2o}X)qozo@?vA0L$~m4to>uj8L?3m% zJ*=|4FRl_EbVSR)+;Hl=#*eSBk5HjV{;|`ggo$dY+=*zW6Q-;X0-dtzlILnM>bO&= z$=?ii5NeRMeMia)t@4y(oag3b{M!y%j+uQJOp@Pn)(v4TvZ(XQ?dZ5(E4q$;m*tGQ@2C4wyVV1ZW8<9$upWYgoWLIsD4h) zlvT)g&P?9{0&OYYFdiB%`7t#XQ*zp+vFRTKQ! zS}yjxqQ+c(DKO3ve^Zay#(8$Aaxto&KefYysILB!sj=$2@{+5f^8nD1XOvgWBc(u+ z_dUP;X5KdbP2fkgE%gpP8B?oBKM}D}voNFnlI5Awxtyv4(af6;0s=?#oRTucCWXex zwK5D;@mIn*&81jTemeDQ7Cd#Pc}9eZSW@r)m^>J>TKsmy{E2y}JI>Br&Mu7YK1g58 ze-$Rb#)vjG9E&La2RxbgCfS0cfesH%Tbii)FF%Yk-XFTorIWs|xO5+yHm|6tM0k>62tov_&8@=7fOhm>A zAR5uN1(#MXyHGs)-Uz?reSc#3MJm~uWq8AaThhNx->kl_7!I1}6Ujtc@DU7Zphj5o zrQn=;Pw-!L@7P|cE0s@US$7u%?@~U0Z(K0VXYp2Ctp8$?2UXC~VDx;aWc1I*$dd(z zsh(?=o$+b%Y^j!ujM~Bza9ChiSzxe}GfuY}-j-^CCbszGs_oG&(E5Xr=pLF6zaA3i z_NK}Ua~r#ivY`jZ&lwbwC{FH2VAXceLd?lYFks|@(Jb{Q%pHE|3pJSK(eFA|eGvWx z{P1XQHSg$GZcT;2|9<^9`w#OLIrq@du`AG&p`WWy{|W8GJm2$wpYPEx>ePgpBg3Sk zJVPfVv@kgzhWa81*>QPIfBc)L)e8s-A_a0?bbb9oi`8p8TR?>*PtTj9SNdTHFzZTG z+z%ct;3fl005r!<^**^GcWuao0~3eU{DdTtByMTfM^;LGz?V}XAgWBk@B2(4+@yhs z{s|)eiHsme{DA8u0SDJuj~9t2XXu?SgdWHBbvoasT~_)@b??g-9jR}vTI2otpw}oB zs}qNGCs1be72j`+aIwN%4S>sw!%jji$GK!HfuqK5Axo*Pa~VhzQdC5$aNzi;%hn1Ax#w&>0W;YS6Dc+yPZ;#6OlQgV7J3zxj5YA5*34A1cf zEs&-uCGRlu0dlC#B@lo)$SR#w;1MME7dudZ%r8|)rFO*M$siQup3utBC6p57>_L>C zeJXAStb&DnS+*1sxqbqM$wjht;*Q6pTYEUgzo8au`YEUF1LP?zt3g^B66(KS4+qMu zm`gmGy*2YTFVT|(=w^S6EXBW*HWDk5ry-n5ed z=h8FHEYI@mdbVNAIyo{HZD|1Jb{;C#bL0@n5kG|}y5~nH4 zZHd%kfE;H!Ig|QgsT`M>d#IURoa3&URp@SM^Lkw`mk;TW<94&xFx^I zPrdBg=Qe)^|ImnbJZ$L^zu>wK*h^`UeWeG%sJIP9DY@}Uhjgu`F3I(Eu-)UD0e@L3 zi=2IHh3xv&26Ju`+&_;pRm(yi`f_Ql#8)mesVt)V43zZw|gOIC+_%M4o z%8M_8syK7>OLxv)!Z)x2je0^io|s65sIYsRd~4?_mK*K*!%RFUAQr%V)st}sh?Y}h1u9@*CUxIE!T#x_j48119|CvWEe@bxLr<^S4i z8ivahD5g2LzDxT-vbV16;rS0oqUpOGLsTk%g5JWqPkZ*jVc*O0={}C@yB24J{<7hA zx4$;*R2JU-=ZoI9Bp-R3lV1l1p)lN9jf9qFO3h?#1>6i>V0pFeQ&--*enZVhGjlyH zlSY3NR4}^g8K1ht2u=$RPnnt9{<4}+LX4iS zg*y$cJ(6$po_6#TBp$KTU&%@1G1RNlK4N3?I5z9s>f;$Sggi&4|7dc_L~k~dZ(&)mKoKTZF%(@4K>72Z>Vh?F>PxAtXuy=5OqKSNkj)UXv>l@0FM0;e2i)85_aC zFb&yODoS@&RwaLZ{L&8&#J;L{@_`lSo!WX^B_hTr8v#IU866qjX^wt+l}TQm11Yy| z>Qybc%doyr5m)WId zq*W3~Dyg1nt6I5ovb}kDldpVT^PDi15b1w+Ui+L9%AmxzqeL_@!6Pw7s6tHcV~M)R zn4162veTXtEUg}$hRw92D+BlA9S*9Pj~A_n9-((U(O&KojL~FxW39E1MyDE1{#f&z zIOxjPMQ}^U)(t~DLK!`M+P!3=Z?R~XL#mtv#K0`U3EevuRO5O9VAN8x5t-#5`=mI5(<`&j@+xGx>;!oaJBsA3 z?`l2~i>`cG9{pWg-0lud9_yi~2Dkk`*ZfJ*1EB!A6h9KRoLPtL?2-y!Br2ZAYS9IUE78Sn*Vs0v|^FQpZi+7+- z-oaG=IZ(d7IjAS!IIXpvoZEDzJYJ?^MLs)}ne;g=xj{+>=dV)65&qh!DYn_`ChpXl zlsQiQ=zyWgnZI+(V_1rO+msMzCvHLKvclc#;|p>+iBEij{8kDhCk8VvfpTZjZuNyw z)&oSE=Nt&mZ`pG|hN5>M8HHEkTt-7e4|h~B#{&QV`8INVLkASOin8_2TlIq=&)?(q z!Ly&S5=O2QNd-q#2h&3Ls!1;dOEYULp!Zkog^w1kk)>^n2#?OoU9>OKwzE)Q1Vg~x z&ALOh>lakZerK;IXN1uxH_ob)F;6zw>~PV{bZ>fNNc4$qE-lN7Twa&;Xz-n~cn^|P z%Kpw}oi6$SA%Yh%R5A_re~AWs-Fy6 z9&InrZ7uH5G3MoAePGJa?|tV1a1ce1_t@sfdL-|fmQgwA3kM<|HTZ$rNjA+iY{O;} zuXM@$)>jnrMfYOQMq>tp9}B$_hk5G-CZt1a+Yn`cRr5&wvOM94W5)V&e(cGZN1qF_ z*NvQdOm)q8cRi-QK&@eo#+lR%?X>l!YkE1?Ms}F5s5WBaZW#)n=+JfoZP}uG@SiWb zkJA??cM?hQ$r;nsgv>GR)0Fhsa-W9Wtm~65ohE%9_5!I3DRC+XZdiJ)$a7y}=IHx0 zGZWRjCWDJ^_ouFSg0_R z$x3ZXc{1)zb4_3MhY0Ut^|q@ciRJ}+@YR@fA&>G2hbwhoRDw^;*7_(a#Kx{T2ifrG ztw*+b-nbCQhluvnx2$qWR^;p>-qgOVTXq0a778>$vu>*Qt>a0>v8=`PYfnAjq+57F zEU8U2HQm*stVUS9%rWE0R}OjUKIVI|_ZQV&xWedecMxdqij}D)ty`2-&d2@ythC4c zVpjoNpe}$l`=gJ3crRn)N7!VQAfTl=Ho% z)CD}}E;pleh3@$`X-yMX?LCRYHs{k5eSf%aJk%Ii($$s~HYIWkG|RwfM*DxBk6?!S z|37GytlEFmu)xX2P<)mwwbbAfV*BHX%k;}RhyJIDChss9 z>nb3`F_`B@zOHzqjGqB4$_@7ejG}`+Z|t$@{}^M{n*U*G7e-y>KZELrOm^l*jxLAV zsm^NrR7;hiR`2WcVWiEn)pv%a)Vk1tWkxQA`V0|`Yi3ly3BJdL;W6t9%tpoZDD z289fqUq0kYzEya4rmUJq)q#&C6kydR;xn~&FdC5 z_K7=98Hzb_^@%!}s70A&E1G3wO&M+4TrBC^T#a$tpq!nKKJEFy#9gAAfGZr}Di|Z% zR^Z%vM*Ng@A7y`~c$ZUPKoWIUD_tal2)nK#<|mabU~lp&e3$@%=!s;aSvy$UFqk^g zWM6jKi3hdk2TPa;93%#&nGpRy`jZQ#^~>3HKO1IJikZtJehh3r402okk~1B;bKs#F zliRmWg5hx1>cTb%Y-T~0FR86*-TMT`4b7LOzn2{gYsIJfKiTvZCkIgZ*rmy?(x)rC ze%$%}YI*v|>(WV*4Y_72$K!zA)OAK`7v7AkIK%CS+R5SrGZ$*tC#iP{epe0XW_sHx zUSIsPjLW2KHz|d=t7F`GB}fUBJGT%3Nrar>!sVFTH)=zE21J$|aAw`{Fae`zqR+%bT&!@1DdwWu%4OhITn zhX3%^<9jN$k{2lAAC&Ftr@(SunD491bP?}XLNF<;qWtML2WU)}DJ?XWv&LbhU+1Lt zh%UjvZ1Sn}p8yKovafQCYh%K6ip6{>v2MeDbRWd)NKLQFN>32%r-QFJJDog|n^*G7 zu9>8E|8=_*&d@9HY7bIMUqL2gi!X1`bVaELSj>i8WzWeids;ETy;^ zJ2&WM<=_|Y)UH$!QcB+5O)lb`@uZNqVd`H`hyt#n&Kis{Z%Zv-;-B`W6h5?bmy!{f z;Mw$8R@T$h?4!&4Ry!H{%E9a4hUT#eo{X+c2ZsZv@HU2G3*dL8_uB$wg4S|xB?-3? zuV^hTR4qHnhL9&+c9-)VQRVD(eo2#u?GQ+tk}9y7E||zkTW#Qd#w)BBRgfKGF_;`q z`I&&5OrGvuclsF0vU%9`mD}PGvtr>w^GPMk`T2tw`T#XucuG$klDr)w{BTHwi}EbN ztYk@{u^@|WBfb`!J;wVt!pK7WCBy^|Cx62Z5&W|e_+yx384y`vZjd&bv{YNUfw^KD5Q#I`Acn^1Aj(o4ccAYL# zvt2LyroYu*w@mxvx&-4%0zT_n`%~M7CUgvYUl#nwuUBqR?p|yK02G{y-fd9B5OLl)jS9vPB{zy}z?r_aHX$YCZ^t!gj?pI!1uzC&XDwmG&Y? z?`F9}Y#=+z7StUB3HNp$e&Br(Qj{oQ6*6B91|=N{NjIj-SEr|;`F28`D`DrGQ|*os z^8b6)vbA=JdwfDH*%vDr9MU(R(*8%_P!SLNkXmF}OWDH2j-|GpcUg8n(Z)#;ox);s zU-u=~)-=CrmHp;eDDx+D)ncVa{`7g(lpe9gmC0t=uQsh8l2)z%+;gtpls5C#Wx6;c z*VS(I{Ucw&jF5Q}9O$MCHOfD+)N>vy zX7?jBTYpNIj-=;k-St__%4kqEy!eywOX-~${!6^v;}A;t)xf0$n`u%pK*FY)UN(TQ zafJO;_k}i(bO0SoxSYBxQKqvk0!(^~c$Gu-!(Jlq1iK_kYEQ559w+-s3i zj5q}D42G{73S6U2m*!QRzn!fu!a4O+XMLkCYb!+@_W0APXo@#74e8v_(e)2@tb2pZ z$Xi&vXb%K^P>_@a$cu5^tp0=MeggX!^>d?NpIMdf)Ya8{c_)7EAiIe1TTziKBBB>- zdV?#Ehn+H<_$i|QlbqSuuet-HEYr~>NZ;h; z3#zU_WKKWmZpNVFw_1WHqL&0AC+#+%sv$GpUHLPy7zM&OkF1If*TRv&izLL=BSpUW z^(v5X+wKm6@TVH%)0R1Bk#`!Y7Sv>xtLiOH1T*IN0V){@c@EAw5KPO+A;WMiO&vaZ)-)c=pbdNLkr|1MUD0 zLVnc3!!5hou>Z+Zig8_BE`7e0G3;6*;X^xDlP>r_2k=$vf_N?i&VLv-Rc zkZRXw7QmXynjjQ?N8F>1)6EW)=p$RPWg>J@SGR8+$PpvJwF#VG0V|qBiaS#^sfiPy z@a96W25fscp^P!FP--zhq7?W<;9Wz`J*gwN7k$&wxyoaybm>eqyvA**k`*kh6Xsk4m& z2%Is3zZ;Mj{fKA2apJi#@;QbIu7WzoaB>%~s=HE?D9GxFR)uHxYAgGCft1~Un(ub~ zkB6s%rrn?P0GT0o5KX5cmT=p|#V^0%w(<}`#UOIbJtT9};J z)M?^C)lWXh6Ea9i03FhzzA4+QRh)!RN4-35|5}=?PJzej;#Mq>bHysI?O~JTn0~mb zcu$q>C^Dkn-9)it&T#8{4>BS&d&%0!!1SRcy9ssq%^5tD^O$MqaDBV0u1gHP^*mqf_uqM)qS=Wg(;wP zqG|Y6{*Y}Gr|CG3%kO5a_yxL`pWqxd$iC|8X@J*3>~W!ZIQHHSL(bGmf9xr1I2h3$ zkOC9w?2-BXqCNHID*hj9k4-c^JFS9KC#ER}CKd!jE~>iYwx^)#^j1px@fl&|C~J#v zVsGLnUS;qdq;0p=OeUioicgFzHOM=lgd(=OkA`xDl-BG^wTbUtM zYiCJ^H7k_~;1m;3Xa&T^Z98sQU_C+bZ{NcKS7F%ZYEuYYY*I4Q6j(DZLRglKoYEk2 z%KGZAbchW@esSTd=j7Wn=x?cf{sj>UJpYJlQs!to1m*6Y7%w=NZ0Qc?tW5>gCoY^D z+$Y0ykoO`|I(wP(w0!&h6>VMNb6MppmXVrT+p!?vcsvdJOYDIGQB{gEPu_(;*}7-H zrEpxX61{oUGvFFerkOq^osWWl6R%Ff@}XSA^`8P#K5$mn>SpxS|phxk+fYlMTWQ5i`-*z@Lcnq_khF-o5J8%-%ng3fRjNzA2;=e&X$Q&_swIk&6h(s#hmxVbEbV||^4nI{mcisV2 zV8JvTcKLlgUay{U+Lg98zCZiLkTb=W!|U&tBmHp7-N#)>gsxazD}c_W!}^rct5lt8 z&}!JNxaN*yQ zXSt+Z^YJ+?HkJl;Niwcw1%$-fD*yd9;sPLg$t7EdO?d%`4$nP{bu#=bGz2sN!OHiZD^d~C5gCqte zL33<3JP$5b!<3>9g4H`!@j3DZ&cDCkDsT&QTT2ypD1ge|3ckMpFon`*4lTq9aept9q-x*>yiqX1>TCEb{3}n78n7?jMFfwWxFfCeb@1!heMYd^!Ll zW%CghVqLT9e*z|U0Bq`bL%;I>kP9g$D~Q@&qv|sW>1E{`2C!|zh=G9y4UeszuA48) zXk1hur{(Ms_4TWOIK8b2^&Q$)vJUZ*#A{jx#vm%5hPr=q$7sBq31uH^M@j;Dho$6@ z)+F>hMU~Q!rnbnzdmbIt&NYY3ih79+n_3cGD;SItI%qK7^dC++f~J`4G6Jsu&A z*C|IKv13fS{#4k1KG+HRuS9b}RoUe@f%EHghagTc)=^z@Q_OD`k~(_|M474pvM_5m zVFDF+G|;@Z-}f0U%UKsewG&pOI6>CIHKcbON8K27Z({!#upGondWeB4CpwbB;9Lg8?1%f8uGxRUspBeb@}J2&F(3D#9-G*ab6278KdKQelLre^A}72mDoug&Aojtj z(%C(fllzC-*)Rp&%0NPT-DYGubvG+Uu5pXuCE%y~dm>Eq@fx>EbL;W+!iBIU>YJJM^ z&cD>ATAcFaIn7rz#nR{yU$ksIOjCeuTokAu)|IIyy7DAW&_wKjWIDm&tK zWmrSGHao`qh1^!{{#P#ZakLWDq^EA^{FYA`Sk?R5PJGnDW{Q7yQ1W3SturY;D`QlN z!*Qamn#PRwbNG&>$lhUjyZGKp@9@K4ZO#=WA-e` zDp_Nql;vG{u`0m_Z%|@l5ANct)xPfg*wRF%OjmIque}fJr%Cr^-1VzVA%|NXZW-_7Y}jlR~L&Uo@9D-;-b z*~%f^CB(8%CdYXQy5zz(q@E#vPab34aHBxp$TN6>(*eyN!3m;&U z7az)K*%)ujNp=0OnnvJyC_UDbc=6eW)YQo^4?oE<0CVXFrC@2mwLByKGwrwB(za!g zC#jE;bs4coq9{C^w2w#}gloS0Ec=R6Fed*p+IqCNECXXiZXrfHh~&eSD8ez3 z{Nt69$bz~7*UPNZofh?qxJu@%uE{nVy=|E_A9e+r4l!dVv@+;7UzD4OsDxjRYCp3o zz+6%7jg{c%UrKADTD}cr5tI_|R30-?WLWn|svnIqd)IsI#zLnQZ!z>J4}0|9&l~e^ zxa<*0^UBs}IVex+4*b+aa3#IOuHpvq76HltZFIPYcrU^uwa-Nlnotp7Dh(P$j%c;r)8zI}10e zWkMxeNU8cuhIKAD599(o8TOQgNuA~>(&*FE^V3d$Nlk{#&F3|qH$oaD)oM2*S-4&f z`dl4TYZiE$TwzgXY9ctPwm-r`mw|WJO$f1KAQMkVGXU7$DX(~xt7ITjB^XokBS;xx z+bj*+b{-z0gG7?Mc=MXK)?+*-XqDryqRvJ|?Pn;-E#naJDh=kU+Td+;<0Lbv{yQ}` zJ!r@1<9z&giph5ASeNO74DAA?iv&3rA%_l!5C^rxX-3fN8IdI4Y<25uhz^EeahxqU zIkZ!p_0@`L$PV>S5ZzgscA4#!;HR$lJLy-}4fe8DEst>j2l0A$v%AXIoNw3WVap>@ zr(L5ruCsnhQNA=jV^o_^RSm+tcBh7~qfsd%o2nLqs%6G`38~W@&n~a-DKPp9-$2Iv z@ihOPT-ofrT*3P4zsJMPV(d|V5&l|h{QQUc#1qeIYFk)iTver92Se5#A*>>qjdp|M zmkvRhj_I&@U+WxOFW{VJ6qB_o-woAq>COsJ_Extz>l4sMBabU9vPLjzp8%)<|5rw1 z{(&$(8TN67!gc?gjBq-qV&GMx^yZ#&5dSQh&4boIyyosMt?Pi8IDVc^xfGn*BB}k+ zczj9vmt=E|+W5rZc3vs>+l!;kP9JoS%X5^-qvcV`QV6la3WKVPslXT^uFA)dBcFWk z4x~I6Y=GFJurChbuc)D%Mo9wq)LPK=*?>G+cOS8~LHLuN^7Bi8H*c}E?Q#EXI>{8w zf5g9owvfI%-kc`pr4jY5ngQj0f=!xH0na6sLjp6Bj3u$Z)%EBqlodYs!9kPiiLGb3 zZ}RVKOisT{5bkJ`-%ZcU9Nhi;0P&9cFhoKT@2f8{m+|2mXe+9}KpC~eUGfPY(#Uuw zpq1P`tfl<&ZlGihH5YPT`k3|buhlirhhV=P#&>8;gZl^%gGn6m5BvtzXB^gZ5569K z)?MIJ_ttUp9)e}jWXvh~@Lt~GneWGFB%0)GJ@rF;&S)ikQ$9pJ{*#nPoKyxUrFb^jtp0mfh>t4 z{FU-byc8*ob5|7ohQx!tH5iy(Ae8yk+jx?(`&@O>A}yx-`9B1pxzR4+__OLhxKlp) z14oVnC-+g5?+KQ7s|_lQ-*ytJ@D#E@DW51^U+kNPA-a&vLjAojkxb9g{^Q8z-)C;1 zDkc1a#JJO(&GL0GAP=Zh>MFkV!U5QU`m_Y#^UP{r>K@Dug#7}PR|O=D)yQ>o$%({w z{YN%tcSf2H12EtKgVS%N>RSIIkX{GTebHWGX>1#AuKGT2C}l3!6^C{rmgrre-uxhP zFM^(&A}@Xf7EoqzVtAyH>K#xNG7a|XU19UX&m8lOo0Vt9!c#MRSZJ|M{q_ozN^<~N zlaM$^DCaFn4#riPEvzmK=M+x#Z0Tl<2<%LmW)uvQvhp`~Vh*s^xMBG{&R#gHY!Mq7 zF(2p4lzSHTvFhUnF9k{x zr$8jr+u*(1FegU#v%saXZ6Wjkmyp*l=b8Ow{bc3z0?igyS;aLJ*bp-f2IRuT>z2pl zg*ilhZ-RHJOpMdw!41Ys2JDQMX%kvnrV$|uBPyjJj~A~VXVWvf?W>YbtFSf0V!XBg zonN)?SZXl%0@T){!W0pzxm{po>%#P4+$?Uww|erl_F=Z<#5@%$#=*#FIz=P{#U=YMWm z7Bd#fWUv1I4vBkej_+FHYdzcKVPgJqIlJ@I8!GmDAy!?8u8}1Njbvb_X}VwE(!Cb~ zYczVZ_5*sE%PaI}4bbnqyLaFpkAwCUGMry7W@m8#W>?+^-;VI@4-=BU+YMI`etYxM z+*QuKS_{3%3y_u)W0|c-vjg+SSn<~v7ue-IzFkVI9b8Xv9{#uPgJSvOtE1@4FM1{( zrgx=Mb-a^Pv}dzT0oOxNTy7SfV3;b*uoU2LrjSv6GJS8N{eE=%YTa46dsXHAnM&kq zC*3wDsp9XpdYzfttWbO7h1H}P^Szb znj%D_?XvL6V3n3`Hmzip94YgP4W1An9`IZjo$_-|YMPZcHV8Gi&tgf zUnifP$=31c81_xv4hZDd8}k3b@d3Z?e^fMSWq56FaDtQI-~Hm?XU8Zp%;PGTcB`)e zLwC0j;D*9JN6XIiQ51%^#lksF^}1L^qZm;*WD{4xPT3E1x9rJnm>c6gc_+6F@ap!U zx&Mi_H;cogt5@OaVP`XH+5Pc@70la9{2~-7jGX8Hu0B#?ISkKeDF4?VXm>DJo{J1i zALSJCm7#gJz;RTWz<;kXLTrm#MyaQ(At2|yX^%KK$d_FpCz`FBWPowO@k(1kRB8HBZx&Np57A_R$Ffm?1H z9{(E0mf@3JPnnNQOF51YL1LLt6^JTkP99n7O3Z^!++fllZ9*V_z70w*vgT!BE-(Oy z!D-ZI@NySMZbADW5ZvRxvVdpy{o@}STHV3#>G@ICZs1bM0`hX(B4!Sp3C<{N7GKwm z6t60DUK8MTVH8}3uDAp->te8#-8i9=Ps`#~Js5RJC`8>buN9W+QINslXLIZf$`T;p z3tpook6VQvp~e?~ee{*iPQ3Oe2ED2jl+}#_1`$9k0p2~g!~IWfiHw~F%{v9=K4jg7 zuOzgPZyoN?^dAEy7?|NfQ>ya=KL!c(#2>Yt(b8J%x!axTIs+VLrl-+zc{p>5(7N9; zKupQGmq$cUOs#QR5yT)Eu>63P$PNHbJcL!0frg+Nht~FDaPUFM;~-|XtQ@`av?z?;B-z{pN8!rUitbVz*%)kO|Mh)fUfzXK`^@yTkq z((f_uzZI9DaF!sqFj@8P=K4}0nN8j@d=8ZQC2-wcWe~$)3%t8-7LZ2;0jN%4Mf%~Z zUR;CQ7J~nzf|0w8JgXSF#E!D>P(d9wSGp=P8LAM8EmHF9oHN{}o^`NIJ>ne!jBgD7 zlZz7ntocFqWVDIbK**m*TKCRzb0~T_HfR;S;r=D)wu;zYQ*kK6p$hYK-TX-a!-eh&f=5(H$RSvGCVy^ z5kF^h0P}{^BXrC0_X;`wfny*Dfzk z&~ZEU1Ais1o`f2~s|vA6TY6OcT|@7S9maYg9ULWW`ONH(`zx?F3~cRvHrLc?uHd+W zFSU$aJ+tjiAV~>v`NH}iKLzbxZV}Hnu{n+xyor&^7p-B2cpyMZKbyBG;FDV3Gnw*o zU4`DBbZjuu*POE#qIfZ>hqvzg|4Z5VAL>^)c71heaEKz|FxG?z%i0__*fOyHme;Nn zwIfRq7l1Yb@|VKaL6n1pezww&yrbZMrgC=-%fmt!yF$P?u;4DYK->fT{Ergbt*0AE zrE~v0weOHK5=*pndF>sMl$co6nT0F9tqtxnBMJ#$rqI-xZ88W*s#6wl-ypwqEf689 zTucAvk#Hfzk=kJaJM5YQI1EoUGs zHgZI~-eg_GY*qo+0GETyF~tRAC|rs?15eM zp=ebF9D~I@d1eX^nc`WhiyNOcjY3q%kj(0@^MydL*xBSdG)dNSL6GG%Jfu#X;?}cd zT_)NElu)p>3JQ(%pei4Q{Fw=M5u@>t)Psy1q7Nh}j_;5YlT*wxxGYxB$lheQqeOi>=y@kv`_zY!$Y!u2^AGX@~H4^1jscwA=rrb2)t}} z^)6}=Z7IDA;6KjFOtBm+FQXNu@9n%32Q7ypP9j3W&Ne#?Xb|4%mz%w4Srj@&*W;{L zRg*-HTM|E9(|i6!A3%JRJtYjbLPw|(VUZV|vv^HUsH_7aA9MCSlU9m#AAfUkBVV73 zuoZ0J6F%?sgoh};56d;kz@*z5kh1K%t zQR?rw=4bC96k`e?2U{q_?4<&4y2N26*J-QzEWSPz2S6(bmOGnaqN_W^To$2o(gW%; z+Pe5*F=GnF1hI=+dZA?oHdpklJMpOX%eOJjg?YBO0G=66Bj%0I;73X&rQXZ+13vD6IYE|6=2 z7P9lod&uxfLz)U7dR&hhywK;Cpbnq#*-m?a-m{~2^}jr%v?LNo21bQ-E#0jK){QYw zX2r=FKdu4og>aDu%N4y4wyJa?g)?W!R-Ktf*<5GuTs_9mTw)?SHyb&)R>MLI@`Myz zo$gR_9KTrF-e7^bI60*SouSAyPx>JIH?&JCTA9}#g%Fjz$o%0GtdnvEJ#nfjduPn} zq+)@3B4&(`Sh9Eq3Q>GPP6dRk*aEMpN^kjvEHJoqlxscuh<(+*ESQPd)vb(n%GP~v zmnj@@wX)hh2Qy`#Tk2i)wqdx^f^)YQv%TyLt#kbF7Oo&1nkr5G1`P!TQ8=dgr`Lrm z;0aNjNr`pbTk&dR;EVP7X!pScqowGXRiE_7XA!ghCWPhr%3SuRj8pW%_@pJEZiynh z;}f6yXe{jO!i2~$dXJe%cVa?F#|XF1y@KsKgEiRM(Oi?*tdHp~BsEhp+baerbxyU9 zn7ZokBiOl$GFFs=vtk#M&e8y^xS;j%FH!=QdGt_Ooj5gP95}T#W>QF}`EThl(8gjJ z`1P4%nCp^mE|)V;j8jWN)tD>qE0EsT3VpLFMad`ltA@m34F-Q?Yr<~l-_3G=Q%Z(c zF!u!*bnnaL#CP5(16JGL=6;17wF&gqlAh{VqBp&+687VQ?+x zEQ}nHaW4{6STao?L~Bt!;cVk$$MNbqJTxhg@}oS3H-b*5V>L~8ABV)%kzLPO*e!cE zfw1Wvi8G?27r=kf1t=PW1{%hg)b%v0S6Lmanud>3L*4{ADQWxlRG9D)*?d^|mBxQ0 z?CtwDDoszHH}kvvbSlR`v(a%}V@Ia~CzfV3h?{+RAyruZxU!AZ<$gRe}4>uXg)4k z7fFQ>11_h3UD6`#Ut?!janmeh+oX3@d!>?7kU4A1sX$F|OM=CE{Nd)8<~=JBG(xoR#_ zliu7<8Y;1~SNV3Vt6{HO2d~51dAa-1X!}!t0B{voYwM=Bms{vI1WEh8ATBS{CRxN2 zXcW<`=SF!ilz*@*ehZLUl$a2$*m#KFIR?|2wwwb)Tr)-=Ant@}*xGK|jBPYd%j*$I z3G+Dt0SSRd{JC+RS&se+Uowm;$8cJSXmr2uNV~_VwJD7%r|PsjF?u^(;OvDz;kxb- z+s^x&)aemy3XvrX*TcFZz<9gM!*12(#qfInVT?vXGP_Nz?YXUnOY>xNt22Kec|2Yi)yy&r%egW`4M@w&wJD=6YM@#KDqW$ zFo?IYWv`IrIZt?4j**qeJ|tZ2vzCrY=2Ybs(`PVqT6lzHCMbjhM7F2RW#RXtL{i(2 zOFQA~?~7bq?mpTV)9;z_Ia-AJl<{*r#oG+LNIbG4@p(WtY-c`Lcy7GU=24mFa97Z^ z{L*>)J981aR&|6UJ-3)-^EI|73HTQs{Q^?y(k)qggtbk|RZiSdn>|Eh{PBnl}w z^QQ=fY;+OkE1 zcr}8qI{WghTOhmZJy4Yc{Xi)Q)Lzc78{DYM;N1g<_=i%u0prC}s^U&H;@BpY_7(_A zGT~g|;2TwvUFJY0&R>fs|7>KZP`s;T-dTO{!-| zgfq{#k5~-rrOiC>cOy{9*HKI~Z05jGgP4wGU|hNB92RunWa}fivm4AGo043|60#6t zv}hiRzUYbGFZ=sEC4w!Qw+3XM{7D>#0VgKlG%~s%s3soPbEv>jyn#j_sFX#00WurW zV;A6dA44RQ=^eMF+$Ya3g6RV`1S5HW81IgSY@S0%%3A^NpW_%6iq?6n4VZTbR{LkW zCYt%~Bzy<@KwlX~;;fwQdsbL`a~*Ab^&wno;nN#mkX`YhH5IKPEenL4VoOsKsZ~Vc zKv+(f*MA&D3{X9bp#exk6_-cbs*FQkzz=a|X(^0=!(f0Uw8_yY_?IT;j?9+lZ(Ia- zj#a39)wb>g$~9P@_!P69H+&EZP_d|%uaFTCbqpLBE>%t!%OTlk!jFf0f#PR6rZ@ep zzYgdvBq*w+GL&yCzkl7FUzD^so1NeM+Qs37*PbGQ*yd1@HJUr|!@LSVSbeKwN}VwO zQxBtlGF0EUpCXm5_)mU73T zuT+4+Fs5ir`oN{Pz;Ir52H7kh0QGxJTU%RfOCQwy;xXrTZtHV0@78=OVHp|Nh2%Pf zQgngbdf5J|y#C_UgQ8n(i@?%iDGKV7TvZUs>)f?nvB69UNr3ZMEC!D>lO4wrbn zj11X$m|f!`J~w|;F!ckFUa-5BXE;WC$9Iuw`(xZfhIch6e4ub9UwRAHbkM7OFKe+x zv70||8&e;7h~{Sta?40p1>~yvetSTjxIuQ1LF-Wlk!-*!l_7H?1aH@pvcJSnwpBT= zn>r0iqRJ2wGi~6-Yz(0{SM7q_2$m;qPLn1iCM1(EK1<(*WUGe;Yl3p_1zKB9nQ*Gv z5v(`h4{C4vn)!pS0>a_U;?|x#2=2gZ$f!|hw#ASiA(&A1NoP?`X9mk%Vr6IMxd!Zx zgzIW6C;}iy=Pi&jr;sVM7+s`Ij{q+t@l53YtL(XK!7pGvb%$Ovck0x%BXR!p%emB) zpym-^1Pr2_%bsX6#jidLA;$2XoEjK{dTpA{4%8f7SNit9?9c~*-#3AmY1q!Vb~Tf3 zqj4v{hs2Z#(Q=~K*h!N{f`%WAoggcbu`u&;l^+d^qr4cSpkqvqKFo!pEm0~7Bv%nt z&flJ$6qk`!4%)cl6MW2LT%B;Q>uZV3?{+x!ny}dlo1LgrK)gI9;Q6*08xWQh9AtI@ zj>AT?M_CwK2GX*lPC^rvA#R>DpkcBAlN!*T6O zBE@+%P_X?_p;lFb#p#M=c(<@f;r(=eE9GKh2Rj9yly&FF7FVjFhVQ-Dbf0T{Kgb87 zIJvu1@N!_);)3 z{DcPF484Im>Z^#eV<&C(d!e(J^-~3TzZWXQ!RO!zSK#Lc(t+RY2u6y6r#m6cU~ix5CTHXB3kE0=x0s-JL} z*EUTt!H#~9aU7aoNq`9o;zvGSrmRRC2QeM?hTd3*P>4H{6TL~5*YyIN?uD-y0?9(w z;%xQFuUNPXGNV);26_{sTINby8@aj{!6VJp<`c%GayI<7PZWVFU3S+#s6{%ZM(7N8 z-B%Xx=P%3W>8YmxLmhn4bw)jRGY3Dxl@A(7tC7YO1nz`m{t!nM%-M5HVL9Zdw+AY# zj+BD>NP>5@C$1^$cr9q;V-*(5x9)mpZf&D8D(0>KA^)MGDKzY)ZTt^;Wt1uID1# z9FYN2|4YV1fG9jRxvPSt;RDNK9cHRqH!~8M)n&qOr=DL0`sib_q00C2dgo;g7z}=o zWhNDh=NMu3q+l#eT6d&SoJ#CtTV$p0mM;M1;y7atJ!*h~dka=85zodv8#I4<(Q6z@ z$+9;AQN)=eeeg`og`QqG@Wf4%u{7R220`|j*!R#0Ui>1|=H8~|K`ppSIB769hGLmH z_o^E3zX(?PVA${FpsnA2a>V}YZ%zr?mlWZ0d1+q{(cRytNyd|VueN?G1j)1TAL~XD z`e4)uBSIg)g(VAGs1`8oJd1l4YJcZzd)8Av1-JAHO_Oq&ieYf=iY+y&H!Zgtf*VQy zlwH(ni!9A_PrZ)i{sLXXBN273FM>aN$HS^pA2+%S_!#VY@%hH{mx}YaSxuuz3N1H8 zeRn|ZI1KJAVIvBId&Mmcr~L`%)_qY{FL<_jS(?@q74gaLGB$N7ez>93J{gx4p!N^` zw)nk(Yjr5E8?chs z{|tZg-@xor>CWZRp}!0K%D&4b$f%DBxFJv~Lw-^375<{$C-Sh#8#1^lG$#sPi znB4q-S9$+i8udCd&$8@>KeY%fwXoLT>HXOPK-To&a5Mt~JNe}a!-lgd^}TWdaqDOP zYhKAqAuB*zoJ7@X&Y>w_n_fpV{Fj@TlM~mvopT95fAK(RjAfv-Sc1e=aT%)zty(Xe zT<;(R>&Sa&tD_}U>irXIH;cjES3;QRh!TCA>(l>2ZHf<5k4VfIj49HX(KQO4AHF~C z60#&)zPs9y9b23_`+a&~;2EN&gYhFjaT!$7>i~#zMX^~3wP2EYF&lVjDCLcc_Ic_M zia8!n%Am922i6wkbQDvf|1eRWHb`1v!`Q783?y^lcqF!>3lvmBSY(;}aR#POQy`EH znW5FNu5?(<if(?&ajoFkw^uQ*KY#yPBz?B8#?o5Uz}15~D!Kj0a};;=TMq8W1sEVZSy5sqWq| zc!7;=zR6<|m#>Y+UN$pYLq$AN=n*I%#gON~;%+_S&F~C9zfpe(4?GqRRAi%RX2rri zspe}4#w{$gO?l}8i+dI9fV^F3Gmr~Emt|~Pnus>L`ZQL~dIUYzQ}_dcl836`_Hon8Tv_kT{toQv>_`W9G;nTE37L+Wz#cY$}gnazZ9+P7ODj@6%Tw z5sY7WxRSPD4v(gMSuPJJY!jylYjEGZ+f{X6KL-o3I*%NVCgWLPuoF+1`2EzrqSyv+ zZf^U5FP_#I@|E>_J1p*-J}eHr-_8LAQ`;JpCM^P+9gn<$iP3qkHNip$+(SOrdq#D; zzFB*JvbkFd-=pGCJCITf?P+!&m8Z+5m7P_SYPbrk0H5DVpNoVpz1q|SjVVe_A&$bU zu^xqfrl?L?7-!!YGo)0n``0V+g;%m3>cSCc_KziT9_b57@10M}X$GITb285^Xo+F( z@`uZF&`FfQ4JG%g9Fhh)Ecb~KoN+Wz@VB99H7ez9et1HB>R_2?)wMAF;PONK@gJgO z**BG^R7)qz%F%OmmtHcefsgsHxcs@t-ZHr+ZEJ4iGuD>7$iDm1fkfBwT7}`XeR3xR zeZLonz?JvH-=4xoa}qi6kt$4;lX*VX+S&hwyJ*^3|85 z?3SeY)G4Gq=EA1De?AVs0yC>Y4OEkRJ|t>lWoSn`^%uN0l@O z)ND0uQN(h6=dCbqT0IO~Q68|c=>1Z=Oi082dnlsI5E<%}+kS5sX?Trn zr0*%*YV`Q0*5iZpGzl6MTE(%u$$csl?O&>4l`+>eJivc-WUs7LP%6d`#IuUaZ>Fpn zE>o7=EJR&DYV0(#CR7NG;*;rlxYq$E1y z6)f}I1orb_X#h!tL*{>QYyYK9{rWZPsj!BJBI$|lQO3Hsmvs{{Z zgFD7Z-7y4_BG-{0Nx-fMP4JICg9&VZTw}Asgcvrf6Qe>Dv9PmNkD{_9fgoQ zUq3`8VcbbTY0rf|-^vVAVD$B@7kY%g)gN4rlN_LBMpP6!dUJ9h)j>>;3)F z0r5k_D4fw{4mMwkJLe$|RNRt8rtB#|5MYQ*ELj(?tl;<4jeuxC=pDg%2!`W}&m*vh z4qZnLN4v@VUMc%~h;X`i<+u0#@-QvzLz%qA$Ok2S$?$_Mu`Vlm7YNqZQHC~}S%<&d zgft{~1d!Dhnl|q-@!0Kx5EYRB*lr4SS@yeVWtN7M39)}Z`hKf3r*w9uOyHC7JkA~2 z*U^#>2yTC+z7*)ytimTdhHkD~<$-RD-2n7;g&V721rpk{pXm49k#^}O2VP;MDH8}K zPzCm(kP_v!CELa2iQFD`D$1Qs_x`nhpBEJAAcx%@L%eq`nwy^Ir7|Z z+0g@9BD3o3OYcBnatz%pywA-aOiWhe-?&#N2)u(cP*)PrD5DHq&3%!=Iyk@y()CQZEtvd}H)8cnHz2Q$HF>o%_pDi=x)R(ZkLRNwwdow-k`=Vb5qRQ;!w3|`YY-(FM>F!LL|Qsj zB6!PZkoj-sz;j6Ih-chy)RMqXC~gUeqBtc3SvaRbed|`*CzHs7{5kB+nw$$aeR-?Y zMr6@u1S0sz;)5xvN9CSe!?8rbnnB6W)v*(fPen-O7!iqOR`OV|*(pb#r?Wl>k!K5| zavNt73VSN10a;l;NwZcV*fEN1Vl=ZVvR!ZQl97L*Zx?8LCWJ_JrokBthgX|mI2|^U zzKU2qpc4!(^t$R#*e9)+J&kCN;@pt&;tyvZMgpN_t}?F<%ezOi#HuDZ0W({MbI8;6 z?}!kL(4EP$riB6PkN3h*?QJ})ZghxeuSbF<)~0*kde4sgW|7{pEl%4NB-Mg|etjN? zR93=JOUb6V`-W1d(YnG*`wzH=jZLG8!2xDA4!xUnP7rn<6NtWNhd_#&YJ0=TBAEa%_&r1b!NIpSZQxUqo zj%51n2nuD2ZdFDsuc6YnY6jW(H-Kz`tCFfvM*I9&{|cr~&G1I`-hZerHY6LfA}oxU zXQF~7(0rhPy`TR%Ahn8hTS08YOlB`4Z9%Dfr7mm3!S+B^Dep!=6x1)RSb?QGf_4NT zIUdl1bbBr%eyZhBj9@l$m+ABJ-u!odXE!@*?qK81l*mLuB|^0()c1)N9fHwLp-EKc z{L3FCPPYxKwZ6*0v+#(W?g`r`GC3m)5OmIMP8A$`4)2dDN~2+IlGTCLm<$!}`aLn9 zSS{Ztww*b;fw@-0=v_vS#w+m5)xnw_c;|#BXDIURwfoFlRST4&4vb?2=?ib@(fdEa o{r~?D`mZAq{;T(Bxc4VRi_*c{Tidh1Rg1xBY3QjF)h-172Na*G=Kufz literal 25615 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 diff --git a/com.unity.renderstreaming/Documentation~/images/inputreceiver_inspector.png b/com.unity.renderstreaming/Documentation~/images/inputreceiver_inspector.png index ed64f79299fcc5003f2d2945ac3ddcff4c81ca65..7d8ab06e089495de5954d819946d2078c69ee6c5 100644 GIT binary patch delta 12757 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 delta 12579 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 Date: Wed, 18 Jan 2023 10:26:41 +0900 Subject: [PATCH 025/117] =?UTF-8?q?=EF=BB=BFfix:=20websocket=20is=20in=20d?= =?UTF-8?q?efault=20as=20a=20signaling=20protocol=20(#836)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set websocket default * fix * fix * fix * fix Co-authored-by: Takashi KANNAN --- WebApp/client/test/signaling.test.js | 8 ++--- WebApp/src/class/options.ts | 4 +-- WebApp/src/index.ts | 18 ++++++++--- WebApp/src/server.ts | 2 +- com.unity.renderstreaming/CHANGELOG.md | 6 ++++ .../Documentation~/launch-webapp.md | 6 ++-- .../Documentation~/signaling-type.md | 10 +++--- .../Documentation~/webapp.md | 32 +++++++++---------- .../Tests/Runtime/PrivateSignalingTest.cs | 4 +-- .../Tests/Runtime/SignalingTest.cs | 4 +-- 10 files changed, 54 insertions(+), 40 deletions(-) diff --git a/WebApp/client/test/signaling.test.js b/WebApp/client/test/signaling.test.js index 04a64fdb0..860f6a61f 100644 --- a/WebApp/client/test/signaling.test.js +++ b/WebApp/client/test/signaling.test.js @@ -28,8 +28,8 @@ describe.each([ } else { const path = Path.resolve(`../bin~/${serverExeName()}`); let cmd = `${path} -p ${portNumber}`; - if (mode == "websocket") { - cmd += " -w"; + if (mode == "http") { + cmd += " -t http"; } await setup({ command: cmd, port: portNumber, usedPortAction: 'error' }); @@ -228,8 +228,8 @@ describe.each([ const path = Path.resolve(`../bin~/${serverExeName()}`); let cmd = `${path} -p ${portNumber} -m private`; - if (mode == "websocket") { - cmd += " -w"; + if (mode == "http") { + cmd += " -t http"; } await setup({ command: cmd, port: portNumber, usedPortAction: 'error' }); diff --git a/WebApp/src/class/options.ts b/WebApp/src/class/options.ts index 329f1f29e..664836623 100644 --- a/WebApp/src/class/options.ts +++ b/WebApp/src/class/options.ts @@ -3,7 +3,7 @@ export default interface Options { port?: number; keyfile?: string; certfile?: string; - websocket?: boolean; + type?: string; mode?: string; logging?: string; -} \ No newline at end of file +} diff --git a/WebApp/src/index.ts b/WebApp/src/index.ts index 08f79b3eb..f8ea46f9f 100644 --- a/WebApp/src/index.ts +++ b/WebApp/src/index.ts @@ -20,7 +20,7 @@ export class RenderStreaming { .option('-s, --secure', 'Enable HTTPS (you need server.key and server.cert)', process.env.SECURE || false) .option('-k, --keyfile ', 'https key file (default server.key)', process.env.KEYFILE || 'server.key') .option('-c, --certfile ', 'https cert file (default server.cert)', process.env.CERTFILE || 'server.cert') - .option('-w, --websocket', 'Enable Websocket Signaling', process.env.WEBSOCKET || false) + .option('-t, --type ', 'Type of signaling protocol, Choose websocket or http (default websocket)', process.env.TYPE || 'websocket') .option('-m, --mode ', 'Choose Communication mode public or private (default public)', process.env.MODE || 'public') .option('-l, --logging ', 'Choose http logging type combined, dev, short, tiny or none.(default dev)', process.env.LOGGING || 'dev') .parse(argv); @@ -30,7 +30,7 @@ export class RenderStreaming { secure: option.secure == undefined ? false : option.secure, keyfile: option.keyfile, certfile: option.certfile, - websocket: option.websocket == undefined ? false : option.websocket, + type: option.type == undefined ? 'websocket' : option.type, mode: option.mode, logging: option.logging, }; @@ -69,9 +69,17 @@ export class RenderStreaming { } }); } + if (this.options.type == 'http') { + console.log(`Use http polling for signaling server.`); + } + else if(this.options.type != 'websocket') { + console.log(`signaling type should be set "websocket" or "http". ${this.options.type} is not supported.`); + console.log(`Changing signaling type to websocket.`); + this.options.type = 'websocket'; + } + if (this.options.type == 'websocket') { + console.log(`Use websocket for signaling server ws://${this.getIPAddress()[0]}`); - if (this.options.websocket) { - console.log(`start websocket signaling server ws://${this.getIPAddress()[0]}`); //Start Websocket Signaling server new WSSignaling(this.server, this.options.mode); } @@ -94,4 +102,4 @@ export class RenderStreaming { } } -RenderStreaming.run(process.argv); \ No newline at end of file +RenderStreaming.run(process.argv); diff --git a/WebApp/src/server.ts b/WebApp/src/server.ts index 5bf8eebff..a77cbe43c 100644 --- a/WebApp/src/server.ts +++ b/WebApp/src/server.ts @@ -17,7 +17,7 @@ export const createServer = (config: Options): express.Application => { // const signal = require('./signaling'); app.use(express.urlencoded({ extended: true })); app.use(express.json()); - app.get('/config', (req, res) => res.json({ useWebSocket: config.websocket, startupMode: config.mode, logging: config.logging })); + app.get('/config', (req, res) => res.json({ useWebSocket: config.type == 'websocket', startupMode: config.mode, logging: config.logging })); app.use('/signaling', signaling); app.use(express.static(path.join(__dirname, '../client/public'))); app.use('/module', express.static(path.join(__dirname, '../client/src'))); diff --git a/com.unity.renderstreaming/CHANGELOG.md b/com.unity.renderstreaming/CHANGELOG.md index cd637c3a4..2c2a4c0b5 100644 --- a/com.unity.renderstreaming/CHANGELOG.md +++ b/com.unity.renderstreaming/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to com.unity.renderstreaming package will be documented in t The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- Websocket is in default for signaling protocol instead of HTTP polling. + ## [3.1.0-exp.5] - 2023-1-16 ### Changed diff --git a/com.unity.renderstreaming/Documentation~/launch-webapp.md b/com.unity.renderstreaming/Documentation~/launch-webapp.md index eb892033e..33bdad0d7 100644 --- a/com.unity.renderstreaming/Documentation~/launch-webapp.md +++ b/com.unity.renderstreaming/Documentation~/launch-webapp.md @@ -1,6 +1,6 @@ # Launching The Web Application -After installing the package, you will need to install and run the signaling server. If you want to learn about signaling, you can see [this page](overview.md). +After installing the package, you will need to install and run the signaling server. If you want to learn about signaling, you can see [this page](overview.md). ## Download web application @@ -18,7 +18,7 @@ When the select download folder window appears, click on `Select Folder` to down After the download is finished and a `powershell` or `cmd` window is opened, and run `webserver.exe` with `-w` option. Please refer to [this page](webapp.md) for commandline options. ``` -.\webserver.exe -w +.\webserver.exe ``` You can see logs on the commandline like below. @@ -49,4 +49,4 @@ You downloaded the web application and launched it. This application can be free Then, the web application also provides some useful command options. If you want more detailed information, please refer to [the web application page](webapp.md). -The next step is to work on the Unity project. Go to the [Creating The Scene](create-scene.md) page. \ No newline at end of file +The next step is to work on the Unity project. Go to the [Creating The Scene](create-scene.md) page. diff --git a/com.unity.renderstreaming/Documentation~/signaling-type.md b/com.unity.renderstreaming/Documentation~/signaling-type.md index 812334fe7..3da603807 100644 --- a/com.unity.renderstreaming/Documentation~/signaling-type.md +++ b/com.unity.renderstreaming/Documentation~/signaling-type.md @@ -13,11 +13,11 @@ In the example, the schema given to `URL Signaling` is used to determine which t If it starts with `http`, `Http Signaling` is used. If it starts with `ws`, `WebSocket Signaling` is used. ``` -# launch server for HTTP -webserver.exe - # launch server for WebSocket -webserver.exe -w +webserver.exe + +# launch server for HTTP +webserver.exe -t http ``` ## `Http Signaling` @@ -35,4 +35,4 @@ When the signaling server receives the Offer or Candidate, the server distribute ## `Furioos Signaling` -Please see [this page](deploy-to-furioos.md). \ No newline at end of file +Please see [this page](deploy-to-furioos.md). diff --git a/com.unity.renderstreaming/Documentation~/webapp.md b/com.unity.renderstreaming/Documentation~/webapp.md index 30f9fe617..af1625a77 100644 --- a/com.unity.renderstreaming/Documentation~/webapp.md +++ b/com.unity.renderstreaming/Documentation~/webapp.md @@ -1,9 +1,9 @@ # Web Application -The **Web application** +The **Web application** - handles signaling between Unity and the Web browser -- is the location of the Web page +- is the location of the Web page ## The Web Client @@ -25,16 +25,16 @@ After downloading, run it from the command line. ### Command Options -| Option | Details | Default | -| ------------------------- | -------------------------------------------------- | ------------- | -| `-h` `--help` | Show the help menu | | -| `-p` `—port \` | Set the port number | `80` | -| `-s` `--secure` | Use https | | -| `-k` `—keyfile \` | Designate the private key file to use with https | `server.key` | +| Option | Details | Default | +| ------ | ------- | ------- | +| `-h` `--help` | Show the help menu | | +| `-p` `—port \` | Set the port number | `80` | +| `-s` `--secure` | Use https | | +| `-k` `—keyfile \` | Designate the private key file to use with https | `server.key` | | `-c` `—certfile \` | Designate the certification file to use with https | `server.cert` | -| `-w` `--websocket` | Use Websocket as signaling protocol | | -| `-m` `—-mode \` | Choose Communication mode public or private | `public` | -| `-l` `—logging \` | Choose http logging type (use [morgan](https://www.npmjs.com/package/morgan) library) | `dev` | +| `-t` `--type \` | Type of signaling protocol, Choose websocket or http | `websocket` | +| `-m` `—-mode \` | Choose Communication mode public or private | `public` | +| `-l` `—logging \` | Choose http logging type (use [morgan](https://www.npmjs.com/package/morgan) library) | `dev` | ### Command Examples @@ -50,13 +50,13 @@ This command will run the server as https. Port 443 will be used. A certificate .\webserver -s -p 443 ``` -The command will run in the mode that uses WebSocket as the signaling protocol. +The command will run in the mode that uses **HTTP polling** as the signaling protocol. ```shell -.\webserver -w +.\webserver -t http ``` -The command will run in private mode. +The command will run in private mode. ```shell .\webserver -m private @@ -66,7 +66,7 @@ The command will run in private mode. When running a https server, keep in mind to set the `URL signaling` property of the Renderstreaming component in Unity to https as well. -Use this command to display the help guide. +Use this command to display the help guide. ```shell .\webserver -h @@ -74,4 +74,4 @@ Use this command to display the help guide. ## How to custmize web application -Please see the page [Customize Web Application](customize-webapp.md). \ No newline at end of file +Please see the page [Customize Web Application](customize-webapp.md). diff --git a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs index 4d004b391..1f1e18675 100644 --- a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs @@ -91,9 +91,9 @@ public void OneTimeSetUp() string arguments = $"-m private -p {TestUtility.PortNumber}"; - if (m_SignalingType == typeof(WebSocketSignaling)) + if (m_SignalingType == typeof(HttpSignaling)) { - arguments += " -w"; + arguments += " -t http"; } startInfo.Arguments = arguments; diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs index f2d750ca4..84f6f9a69 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs @@ -88,9 +88,9 @@ public void OneTimeSetUp() string arguments = $"-p {TestUtility.PortNumber}"; - if (m_SignalingType == typeof(WebSocketSignaling)) + if (m_SignalingType == typeof(HttpSignaling)) { - arguments += " -w"; + arguments += " -t http"; } startInfo.Arguments = arguments; From ba9c3ddc862348c5a95442bdd047027a82748760 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 19 Jan 2023 17:57:34 +0900 Subject: [PATCH 026/117] fix: Make available to change properties of VideoStreamsender and AudioStreamSender while streaming (#839) * wip * fix * wip * Add VideoStreamSource dropdown in Broadcast sample * fix scene * fix * wip (cherry picked from commit 83b800497ee5050540cb575c153c07d1a61363f9) * fix * fix * workaround --- .../Runtime/Scripts/AudioStreamSender.cs | 78 +- .../Runtime/Scripts/StreamSenderBase.cs | 15 + .../Runtime/Scripts/VideoStreamSender.cs | 107 +- .../Example/Broadcast/Broadcast.unity | 1594 +++++++++++++++-- .../Example/Broadcast/BroadcastSample.cs | 19 + .../Tests/Runtime/StreamingComponentTest.cs | 34 +- 6 files changed, 1567 insertions(+), 280 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs index c1c352ab1..9bb3f2650 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs @@ -74,9 +74,15 @@ public AudioStreamSource source get { return m_Source; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_Source == value) + return; m_Source = value; + + if (!isPlaying) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } @@ -112,9 +118,15 @@ public int sourceDeviceIndex get { return m_MicrophoneDeviceIndex; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_MicrophoneDeviceIndex == value) + return; m_MicrophoneDeviceIndex = value; + + if (!isPlaying || m_Source != AudioStreamSource.Microphone) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } @@ -126,9 +138,15 @@ public AudioSource audioSource get { return m_AudioSource; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_AudioSource == value) + return; m_AudioSource = value; + + if (!isPlaying || m_Source != AudioStreamSource.AudioSource) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } @@ -140,9 +158,15 @@ public AudioListener audioListener get { return m_AudioListener; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_AudioListener == value) + return; m_AudioListener = value; + + if (!isPlaying || m_Source != AudioStreamSource.AudioListener) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } @@ -244,10 +268,7 @@ AudioStreamSourceImpl CreateAudioStreamSource() switch (m_Source) { case AudioStreamSource.AudioListener: - var source = new AudioStreamSourceAudioListener(this); - // todo:: workaround. - m_onAudioFilterRead = source.OnAudioFilterRead; - return source; + return new AudioStreamSourceAudioListener(this); case AudioStreamSource.AudioSource: return new AudioStreamSourceAudioSource(this); case AudioStreamSource.Microphone: @@ -291,26 +312,23 @@ public AudioStreamSourceImpl(AudioStreamSender parent) class AudioStreamSourceAudioListener : AudioStreamSourceImpl { - AudioStreamTrack m_audioTrack; - + private AudioListener m_audioListener; public AudioStreamSourceAudioListener(AudioStreamSender parent) : base(parent) { - // todo: Should add AudioStreamTrack supports AudioListener - if (!parent.GetComponent()) - throw new InvalidOperationException("Audio Listener have to be set the same gameObject."); + m_audioListener = parent.m_AudioListener; + if (m_audioListener == null) + throw new InvalidOperationException("The audioListener is not assigned."); } public override WaitForCreateTrack CreateTrack() { var instruction = new WaitForCreateTrack(); - m_audioTrack = new AudioStreamTrack(); - instruction.Done(m_audioTrack); + instruction.Done(new AudioStreamTrack(m_audioListener)); return instruction; } public override void Dispose() { - m_audioTrack = null; GC.SuppressFinalize(this); } @@ -318,29 +336,11 @@ public override void Dispose() { Dispose(); } - - public void OnAudioFilterRead(float[] data, int channels, int sampleRate) - { - NativeArray nativeArray = new NativeArray(data, Allocator.Temp); - try - { - m_audioTrack?.SetData(ref nativeArray, channels, sampleRate); - } - // todo(kazuki):: Should catch only ObjectDisposedException but - // AudioStreamTrack also throws NullReferenceException. - catch (Exception) - { - } - finally - { - nativeArray.Dispose(); - } - } } class AudioStreamSourceAudioSource : AudioStreamSourceImpl { - AudioSource m_audioSource; + private AudioSource m_audioSource; public AudioStreamSourceAudioSource(AudioStreamSender parent) : base(parent) { m_audioSource = parent.m_AudioSource; diff --git a/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs b/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs index 2ef3a15da..7bf2d6f3f 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs @@ -31,6 +31,21 @@ public void Done(MediaStreamTrack track) } } + internal Coroutine StartCoroutineWithCallback(T coroutine, Action callback) where T : IEnumerator + { + if (coroutine == null) + throw new ArgumentNullException("coroutine"); + if (callback == null) + throw new ArgumentNullException("callback"); + return StartCoroutine(_Coroutine(coroutine, callback)); + } + + internal IEnumerator _Coroutine(T coroutine, Action callback) where T : IEnumerator + { + yield return StartCoroutine(coroutine); + callback(coroutine); + } + ///

/// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs index 10015c661..15a7269e4 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs @@ -86,24 +86,24 @@ public static RTCError SetBitrate(this RTCRtpSender sender, uint? minBitrate, ui } /// - /// + /// /// public enum VideoStreamSource { /// - /// + /// /// Camera = 0, /// - /// + /// /// Screen = 1, /// - /// + /// /// WebCamera = 2, /// - /// + /// /// Texture = 3 } @@ -160,35 +160,42 @@ public class VideoStreamSender : StreamSenderBase private VideoStreamSourceImpl m_sourceImpl = null; /// - /// + /// /// public VideoStreamSource source { get { return m_Source; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_Source == value) + return; m_Source = value; - if (m_Texture != null) - { - m_TextureSize.x = m_Texture.width; - m_TextureSize.y = m_Texture.height; - } + + if (!isPlaying) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } /// - /// + /// /// public Camera sourceCamera { get { return m_Camera; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_Camera == value) + return; m_Camera = value; + + if (!isPlaying || m_Source != VideoStreamSource.Camera) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } @@ -200,11 +207,15 @@ public Texture sourceTexture get { return m_Texture; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_Texture == value) + return; m_Texture = value; - m_TextureSize.x = m_Texture.width; - m_TextureSize.y = m_Texture.height; + + if (!isPlaying || m_Source != VideoStreamSource.Texture) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } @@ -216,14 +227,20 @@ public int sourceDeviceIndex get { return m_WebCamDeviceIndex; } set { - if (isPlaying) - throw new InvalidOperationException("Can not change this parameter after the streaming is started."); + if (m_WebCamDeviceIndex == value) + return; m_WebCamDeviceIndex = value; + + if (!isPlaying || m_Source != VideoStreamSource.WebCamera) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } } /// - /// + /// /// public WebCamTexture sourceWebCamTexture { @@ -263,7 +280,7 @@ public uint maxBitrate } /// - /// + /// /// public float scaleResolutionDown { @@ -271,7 +288,7 @@ public float scaleResolutionDown } /// - /// + /// /// public uint width { @@ -283,7 +300,7 @@ public uint width } /// - /// + /// /// public uint height { @@ -295,7 +312,7 @@ public uint height } /// - /// + /// /// public VideoCodecInfo codec { @@ -303,7 +320,7 @@ public VideoCodecInfo codec } /// - /// + /// /// public bool autoRequestUserAuthorization { @@ -312,7 +329,7 @@ public bool autoRequestUserAuthorization } /// - /// + /// /// /// public void SetCodec(VideoCodecInfo codec) @@ -328,7 +345,7 @@ public void SetCodec(VideoCodecInfo codec) if (transceiver.Sender.Track.ReadyState == TrackState.Ended) continue; - var codecs = new VideoCodecInfo[] { m_Codec }; + var codecs = new[] { m_Codec }; RTCErrorType error = transceiver.SetCodecPreferences(SelectCodecCapabilities(codecs).ToArray()); if (error != RTCErrorType.None) throw new InvalidOperationException($"Set codec is failed. errorCode={error}"); @@ -336,7 +353,7 @@ public void SetCodec(VideoCodecInfo codec) } /// - /// + /// /// /// public static IEnumerable GetAvailableCodecs() @@ -401,7 +418,7 @@ public void SetScaleResolutionDown(float scaleFactor) } /// - /// + /// /// /// public void SetTextureSize(Vector2Int size) @@ -410,11 +427,11 @@ public void SetTextureSize(Vector2Int size) throw new InvalidOperationException("Video source is set Texture."); m_TextureSize = size; - if (isPlaying) - { - var op = CreateTrack(); - StartCoroutine(op, _ => ReplaceTrack(_.Track)); - } + if (!isPlaying) + return; + + var op = CreateTrack(); + StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track)); } private protected virtual void Awake() @@ -443,22 +460,6 @@ internal override WaitForCreateTrack CreateTrack() return m_sourceImpl.CreateTrack(); } - void StartCoroutine(T coroutine, Action callback) where T : IEnumerator - { - if (coroutine == null) - throw new ArgumentNullException("coroutine"); - if (callback == null) - throw new ArgumentNullException("callback"); - StartCoroutine(_Coroutine(coroutine, callback)); - } - - IEnumerator _Coroutine(T coroutine, Action callback) where T : IEnumerator - { - yield return StartCoroutine(coroutine); - callback(coroutine); - } - - VideoStreamSourceImpl CreateVideoStreamSource() { switch (m_Source) diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity b/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity index e798546fa..0a0ffa821 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/Broadcast.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 705507994} - m_IndirectSpecularColor: {r: 0.18011706, g: 0.22551754, b: 0.30669218, a: 1} + m_IndirectSpecularColor: {r: 0.18028378, g: 0.22571412, b: 0.30692285, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -123,6 +123,81 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &37866129 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 37866130} + - component: {fileID: 37866132} + - component: {fileID: 37866131} + m_Layer: 5 + m_Name: Item Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &37866130 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 37866129} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 165282857} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 10, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &37866131 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 37866129} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &37866132 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 37866129} + m_CullTransparentMesh: 1 --- !u!1 &42939948 GameObject: m_ObjectHideFlags: 0 @@ -150,7 +225,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 141909992} - {fileID: 1770659992} @@ -237,20 +311,19 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 68600726} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 129463857} - {fileID: 1443526842} - {fileID: 122164544} - m_Father: {fileID: 992023663} - m_RootOrder: 7 + m_Father: {fileID: 545394120} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 225, y: -35} m_SizeDelta: {x: 200, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &68600728 @@ -374,7 +447,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1077009976} m_RootOrder: 1 @@ -448,20 +520,19 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 92522334} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 842725225} - {fileID: 224518647} - {fileID: 1990494041} - m_Father: {fileID: 992023663} - m_RootOrder: 5 + m_Father: {fileID: 1008214213} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 225, y: -35} m_SizeDelta: {x: 160, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &92522336 @@ -583,7 +654,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1671870689} m_Father: {fileID: 267424725} @@ -594,6 +664,43 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 50} m_Pivot: {x: 0.5, y: 1} +--- !u!1 &109292047 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 109292048} + m_Layer: 5 + m_Name: Bandwidth + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &109292048 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 109292047} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 523984880} + - {fileID: 1429950775} + m_Father: {fileID: 992023663} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 330, y: 80} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &121386855 GameObject: m_ObjectHideFlags: 0 @@ -622,7 +729,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1671870689} m_RootOrder: 2 @@ -703,7 +809,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 792226112} - {fileID: 273804981} @@ -811,7 +916,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 68600727} m_RootOrder: 0 @@ -891,7 +995,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 42939949} m_RootOrder: 0 @@ -939,6 +1042,172 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 141909991} m_CullTransparentMesh: 1 +--- !u!1 &165282856 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 165282857} + - component: {fileID: 165282858} + m_Layer: 5 + m_Name: Item + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &165282857 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 165282856} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 706985609} + - {fileID: 37866130} + - {fileID: 168984864} + m_Father: {fileID: 392062582} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 50} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &165282858 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 165282856} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 706985610} + toggleTransition: 1 + graphic: {fileID: 37866131} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: [] + m_IsOn: 1 +--- !u!1 &168984863 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 168984864} + - component: {fileID: 168984866} + - component: {fileID: 168984865} + m_Layer: 5 + m_Name: Item Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &168984864 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 168984863} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 165282857} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 5, y: -0.5} + m_SizeDelta: {x: -30, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &168984865 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 168984863} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 18 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Option A +--- !u!222 &168984866 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 168984863} + m_CullTransparentMesh: 1 --- !u!1 &212310406 GameObject: m_ObjectHideFlags: 0 @@ -966,7 +1235,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1100876516} - {fileID: 651274779} @@ -977,7 +1245,7 @@ RectTransform: m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 100, y: 170} + m_SizeDelta: {x: 640, y: 72} m_Pivot: {x: 0, y: 0} --- !u!114 &212310408 MonoBehaviour: @@ -1033,7 +1301,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 92522335} m_RootOrder: 1 @@ -1109,7 +1376,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 781387196} m_RootOrder: 0 @@ -1191,7 +1457,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 96939261} m_Father: {fileID: 342911464} @@ -1282,7 +1547,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1413959481} m_Father: {fileID: 122164544} @@ -1380,6 +1644,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 273804980} m_CullTransparentMesh: 1 +--- !u!1 &286006944 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 286006945} + - component: {fileID: 286006947} + - component: {fileID: 286006946} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &286006945 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 286006944} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 871570056} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -7.5, y: -0.5} + m_SizeDelta: {x: -35, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &286006946 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 286006944} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: undefined +--- !u!222 &286006947 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 286006944} + m_CullTransparentMesh: 1 --- !u!1 &331270467 GameObject: m_ObjectHideFlags: 0 @@ -1408,7 +1751,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1595595783} m_RootOrder: 0 @@ -1481,17 +1823,16 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 341290770} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 992023663} - m_RootOrder: 2 + m_Father: {fileID: 1890263316} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 60, y: 5} m_SizeDelta: {x: 110, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &341290772 @@ -1565,7 +1906,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 267424725} - {fileID: 1952417260} @@ -1645,6 +1985,42 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 342911463} m_CullTransparentMesh: 1 +--- !u!1 &392062581 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 392062582} + m_Layer: 5 + m_Name: Content + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &392062582 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 392062581} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 165282857} + m_Father: {fileID: 457646926} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 50} + m_Pivot: {x: 0.5, y: 1} --- !u!1 &439262020 GameObject: m_ObjectHideFlags: 0 @@ -1674,7 +2050,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1382838829} - {fileID: 818354133} @@ -1782,7 +2157,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1606947282} m_RootOrder: 0 @@ -1830,6 +2204,96 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 454027332} m_CullTransparentMesh: 1 +--- !u!1 &457646925 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 457646926} + - component: {fileID: 457646929} + - component: {fileID: 457646928} + - component: {fileID: 457646927} + m_Layer: 5 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &457646926 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 457646925} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 392062582} + m_Father: {fileID: 740759892} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -18, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &457646927 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 457646925} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 0 +--- !u!114 &457646928 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 457646925} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10917, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &457646929 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 457646925} + m_CullTransparentMesh: 1 --- !u!1 &499094589 GameObject: m_ObjectHideFlags: 0 @@ -1856,7 +2320,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1645667445} m_Father: {fileID: 1039593694} @@ -1892,17 +2355,16 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 523984879} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 992023663} + m_Father: {fileID: 109292048} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 60, y: 5} m_SizeDelta: {x: 100, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &523984881 @@ -1947,6 +2409,43 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 523984879} m_CullTransparentMesh: 1 +--- !u!1 &545394119 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 545394120} + m_Layer: 5 + m_Name: Resolution + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &545394120 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 545394119} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1935761084} + - {fileID: 68600727} + m_Father: {fileID: 992023663} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 330, y: 80} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &610018210 GameObject: m_ObjectHideFlags: 0 @@ -1975,7 +2474,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1606947282} m_RootOrder: 2 @@ -2052,17 +2550,16 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 647684927} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 992023663} - m_RootOrder: 4 + m_Father: {fileID: 1008214213} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 60, y: 5} m_SizeDelta: {x: 100, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &647684929 @@ -2136,7 +2633,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 725135283} m_Father: {fileID: 212310407} @@ -2241,7 +2737,7 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 651274778} m_CullTransparentMesh: 1 ---- !u!1 &675120844 +--- !u!1 &654995499 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2249,39 +2745,192 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 675120845} - - component: {fileID: 675120848} - - component: {fileID: 675120847} - - component: {fileID: 675120846} + - component: {fileID: 654995500} + - component: {fileID: 654995502} + - component: {fileID: 654995501} m_Layer: 5 - m_Name: PlayAudio + m_Name: VideoSource m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &675120845 +--- !u!224 &654995500 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 675120844} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_GameObject: {fileID: 654995499} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 1106356206} - m_Father: {fileID: 212310407} - m_RootOrder: 2 + m_Children: [] + m_Father: {fileID: 2143950603} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 200, y: 50} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 60, y: 5} + m_SizeDelta: {x: 130, y: 50} m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &675120846 +--- !u!114 &654995501 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 654995499} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Video Source:' +--- !u!222 &654995502 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 654995499} + m_CullTransparentMesh: 1 +--- !u!1 &664140253 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 664140254} + - component: {fileID: 664140256} + - component: {fileID: 664140255} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &664140254 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 664140253} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1191036656} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0.2} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &664140255 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 664140253} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &664140256 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 664140253} + m_CullTransparentMesh: 1 +--- !u!1 &675120844 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 675120845} + - component: {fileID: 675120848} + - component: {fileID: 675120847} + - component: {fileID: 675120846} + m_Layer: 5 + m_Name: PlayAudio + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &675120845 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 675120844} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1106356206} + m_Father: {fileID: 212310407} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 200, y: 50} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &675120846 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2498,7 +3147,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 7 @@ -2592,11 +3240,85 @@ Transform: m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &706985608 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 706985609} + - component: {fileID: 706985611} + - component: {fileID: 706985610} + m_Layer: 5 + m_Name: Item Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &706985609 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 706985608} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 165282857} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &706985610 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 706985608} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &706985611 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 706985608} + m_CullTransparentMesh: 1 --- !u!1 &725135282 GameObject: m_ObjectHideFlags: 0 @@ -2625,7 +3347,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 651274779} m_RootOrder: 0 @@ -2709,7 +3430,6 @@ Transform: m_LocalRotation: {x: -0.02296006, y: 0.9760592, z: -0.17338917, w: -0.12929617} m_LocalPosition: {x: 2.454, y: 1.436, z: 2.861} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -2879,7 +3599,7 @@ MonoBehaviour: m_TextureSize: {x: 1280, y: 720} m_Source: 1 m_Camera: {fileID: 725451154} - m_Texture: {fileID: 0} + m_Texture: {fileID: 10309, guid: 0000000000000000f000000000000000, type: 0} m_WebCamDeviceIndex: 0 m_Depth: 16 m_AntiAliasing: 1 @@ -2962,7 +3682,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1077009976} m_RootOrder: 2 @@ -3014,6 +3733,114 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 736464805} m_CullTransparentMesh: 1 +--- !u!1 &740759891 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 740759892} + - component: {fileID: 740759895} + - component: {fileID: 740759894} + - component: {fileID: 740759893} + m_Layer: 5 + m_Name: Template + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &740759892 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 740759891} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 457646926} + - {fileID: 817003794} + m_Father: {fileID: 871570056} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: 0, y: 2} + m_SizeDelta: {x: 0, y: 150} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &740759893 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 740759891} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Content: {fileID: 392062582} + m_Horizontal: 0 + m_Vertical: 1 + m_MovementType: 2 + m_Elasticity: 0.1 + m_Inertia: 1 + m_DecelerationRate: 0.135 + m_ScrollSensitivity: 1 + m_Viewport: {fileID: 457646926} + m_HorizontalScrollbar: {fileID: 0} + m_VerticalScrollbar: {fileID: 817003795} + m_HorizontalScrollbarVisibility: 0 + m_VerticalScrollbarVisibility: 2 + m_HorizontalScrollbarSpacing: 0 + m_VerticalScrollbarSpacing: -3 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &740759894 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 740759891} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &740759895 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 740759891} + m_CullTransparentMesh: 1 --- !u!1001 &757982963 PrefabInstance: m_ObjectHideFlags: 0 @@ -3181,7 +4008,7 @@ LightingSettings: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: Settings.lighting - serializedVersion: 4 + serializedVersion: 3 m_GIWorkflowMode: 0 m_EnableBakedLightmaps: 1 m_EnableRealtimeLightmaps: 1 @@ -3194,7 +4021,7 @@ LightingSettings: m_LightmapMaxSize: 1024 m_BakeResolution: 40 m_Padding: 2 - m_LightmapCompression: 2 + m_TextureCompression: 1 m_AO: 0 m_AOMaxDistance: 1 m_CompAOExponent: 1 @@ -3235,7 +4062,6 @@ LightingSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 - m_PVRTiledBaking: 0 --- !u!1 &781387195 GameObject: m_ObjectHideFlags: 0 @@ -3264,7 +4090,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 244876989} m_Father: {fileID: 1215389823} @@ -3343,7 +4168,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} m_Name: m_EditorClassIdentifier: - m_SendPointerHoverToParent: 1 m_MoveRepeatDelay: 0.5 m_MoveRepeatRate: 0.1 m_XRTrackingOrigin: {fileID: 0} @@ -3397,7 +4221,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 5 @@ -3431,7 +4254,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 971023929} m_Father: {fileID: 122164544} @@ -3493,6 +4315,132 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 792226111} m_CullTransparentMesh: 1 +--- !u!1 &817003793 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 817003794} + - component: {fileID: 817003797} + - component: {fileID: 817003796} + - component: {fileID: 817003795} + m_Layer: 5 + m_Name: Scrollbar + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &817003794 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 817003793} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1191036656} + m_Father: {fileID: 740759892} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 0} + m_Pivot: {x: 1, y: 1} +--- !u!114 &817003795 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 817003793} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 664140255} + m_HandleRect: {fileID: 664140254} + m_Direction: 2 + m_Value: 0 + m_Size: 0.2 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &817003796 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 817003793} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &817003797 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 817003793} + m_CullTransparentMesh: 1 --- !u!1 &818354132 GameObject: m_ObjectHideFlags: 0 @@ -3522,7 +4470,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1436756928} m_Father: {fileID: 439262021} @@ -3648,7 +4595,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 92522335} m_RootOrder: 0 @@ -3698,7 +4644,141 @@ CanvasRenderer: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 842725224} + m_GameObject: {fileID: 842725224} + m_CullTransparentMesh: 1 +--- !u!1 &871570055 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 871570056} + - component: {fileID: 871570059} + - component: {fileID: 871570058} + - component: {fileID: 871570057} + m_Layer: 5 + m_Name: VideoSourceSelect + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &871570056 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 871570055} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 286006945} + - {fileID: 1963303868} + - {fileID: 740759892} + m_Father: {fileID: 2143950603} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 220, y: -35} + m_SizeDelta: {x: 190, y: 50} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &871570057 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 871570055} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0d0b652f32a2cc243917e4028fa0f046, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 871570058} + m_Template: {fileID: 740759892} + m_CaptionText: {fileID: 286006946} + m_CaptionImage: {fileID: 0} + m_ItemText: {fileID: 168984865} + m_ItemImage: {fileID: 0} + m_Value: 0 + m_Options: + m_Options: + - m_Text: undefined + m_Image: {fileID: 0} + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_AlphaFadeSpeed: 0.15 +--- !u!114 &871570058 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 871570055} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &871570059 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 871570055} m_CullTransparentMesh: 1 --- !u!1 &920982461 GameObject: @@ -3729,7 +4809,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1127658836} m_Father: {fileID: 1215389823} @@ -3816,7 +4895,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1606947282} m_Father: {fileID: 792226112} @@ -3854,24 +4932,20 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 523984880} - - {fileID: 1429950775} - - {fileID: 341290771} - - {fileID: 1975420022} - - {fileID: 647684928} - - {fileID: 92522335} - - {fileID: 1935761084} - - {fileID: 68600727} + - {fileID: 2143950603} + - {fileID: 109292048} + - {fileID: 1890263316} + - {fileID: 1008214213} + - {fileID: 545394120} m_Father: {fileID: 1215389823} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 100, y: 100} - m_Pivot: {x: 0, y: 0} + m_SizeDelta: {x: 330.96265, y: 390.0238} + m_Pivot: {x: 1, y: 0} --- !u!114 &992023664 MonoBehaviour: m_ObjectHideFlags: 0 @@ -3881,16 +4955,16 @@ MonoBehaviour: m_GameObject: {fileID: 992023662} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} m_Name: m_EditorClassIdentifier: m_Padding: - m_Left: 10 - m_Right: 10 - m_Top: 10 - m_Bottom: 10 + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 m_ChildAlignment: 0 - m_Spacing: 10 + m_Spacing: 0 m_ChildForceExpandWidth: 1 m_ChildForceExpandHeight: 1 m_ChildControlWidth: 0 @@ -3898,6 +4972,43 @@ MonoBehaviour: m_ChildScaleWidth: 0 m_ChildScaleHeight: 0 m_ReverseArrangement: 0 +--- !u!1 &1008214212 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1008214213} + m_Layer: 5 + m_Name: Framerate + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1008214213 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1008214212} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 647684928} + - {fileID: 92522335} + m_Father: {fileID: 992023663} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 330, y: 80} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1021986362 GameObject: m_ObjectHideFlags: 0 @@ -3926,7 +5037,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 42939949} m_RootOrder: 2 @@ -4132,7 +5242,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 499094590} m_Father: {fileID: 1990494041} @@ -4274,7 +5383,6 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -4323,7 +5431,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 3 @@ -4355,7 +5462,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1220544845} - {fileID: 74593455} @@ -4459,7 +5565,6 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -4508,7 +5613,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.8, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 4 @@ -4542,7 +5646,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2061595962} m_Father: {fileID: 212310407} @@ -4675,7 +5778,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 675120845} m_RootOrder: 0 @@ -4755,7 +5857,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1975420022} m_RootOrder: 1 @@ -4831,7 +5932,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 920982462} m_RootOrder: 0 @@ -4883,6 +5983,42 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1127658835} m_CullTransparentMesh: 0 +--- !u!1 &1191036655 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1191036656} + m_Layer: 5 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1191036656 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1191036655} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 664140254} + m_Father: {fileID: 817003794} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1202645299 GameObject: m_ObjectHideFlags: 0 @@ -4909,7 +6045,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1077009976} m_Father: {fileID: 1382838829} @@ -5011,7 +6146,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 920982462} - {fileID: 1287993067} @@ -5096,7 +6230,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1077009976} m_RootOrder: 0 @@ -5172,7 +6305,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1215389823} m_RootOrder: 1 @@ -5249,7 +6381,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1202645300} m_Father: {fileID: 439262021} @@ -5339,7 +6470,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1429950775} m_RootOrder: 1 @@ -5413,7 +6543,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1557274536} m_Father: {fileID: 273804981} @@ -5453,7 +6582,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1824156626} m_Father: {fileID: 1990494041} @@ -5541,20 +6669,19 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1429950774} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1693954711} - {fileID: 1411395153} - {fileID: 439262021} - m_Father: {fileID: 992023663} + m_Father: {fileID: 109292048} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 220, y: -35} m_SizeDelta: {x: 200, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1429950776 @@ -5676,7 +6803,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1464798962} m_Father: {fileID: 818354133} @@ -5715,7 +6841,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 68600727} m_RootOrder: 1 @@ -5791,7 +6916,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1436756928} m_RootOrder: 0 @@ -5867,7 +6991,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1413959481} m_RootOrder: 0 @@ -5941,7 +7064,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 331270468} m_Father: {fileID: 1952417260} @@ -5979,7 +7101,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 454027333} - {fileID: 1886614246} @@ -6085,7 +7206,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 1 @@ -6131,6 +7251,7 @@ MonoBehaviour: cameraController: {fileID: 725451157} uiController: {fileID: 1215389824} videoStreamSender: {fileID: 725451156} + videoSourceTypeSelector: {fileID: 871570057} bandwidthSelector: {fileID: 1429950776} scaleResolutionDownSelector: {fileID: 1975420023} framerateSelector: {fileID: 92522336} @@ -6163,7 +7284,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 499094590} m_RootOrder: 0 @@ -6239,7 +7359,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1671870689} m_RootOrder: 1 @@ -6314,7 +7433,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1809713326} - {fileID: 1655509523} @@ -6403,7 +7521,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1429950775} m_RootOrder: 0 @@ -6483,7 +7600,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 42939949} m_RootOrder: 1 @@ -6559,7 +7675,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1671870689} m_RootOrder: 0 @@ -6633,7 +7748,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 42939949} m_Father: {fileID: 1418489930} @@ -6672,7 +7786,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1606947282} m_RootOrder: 1 @@ -6720,6 +7833,43 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1886614245} m_CullTransparentMesh: 1 +--- !u!1 &1890263315 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1890263316} + m_Layer: 5 + m_Name: ScaleDown + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1890263316 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1890263315} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 341290771} + - {fileID: 1975420022} + m_Father: {fileID: 992023663} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 330, y: 80} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1935761083 GameObject: m_ObjectHideFlags: 0 @@ -6745,17 +7895,16 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1935761083} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 992023663} - m_RootOrder: 6 + m_Father: {fileID: 545394120} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 60, y: 5} m_SizeDelta: {x: 110, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1935761085 @@ -6829,7 +7978,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1595595783} m_Father: {fileID: 342911464} @@ -6927,6 +8075,81 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1952417259} m_CullTransparentMesh: 1 +--- !u!1 &1963303867 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1963303868} + - component: {fileID: 1963303870} + - component: {fileID: 1963303869} + m_Layer: 5 + m_Name: Arrow + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1963303868 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1963303867} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 871570056} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -15, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1963303869 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1963303867} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10915, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1963303870 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1963303867} + m_CullTransparentMesh: 1 --- !u!1 &1975420021 GameObject: m_ObjectHideFlags: 0 @@ -6953,20 +8176,19 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1975420021} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2119943770} - {fileID: 1120383538} - {fileID: 342911464} - m_Father: {fileID: 992023663} - m_RootOrder: 3 + m_Father: {fileID: 1890263316} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 220, y: -35} m_SizeDelta: {x: 200, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1975420023 @@ -7091,7 +8313,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1418489930} - {fileID: 1039593694} @@ -7199,7 +8420,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1100876516} m_RootOrder: 0 @@ -7279,7 +8499,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1975420022} m_RootOrder: 0 @@ -7331,3 +8550,40 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2119943769} m_CullTransparentMesh: 1 +--- !u!1 &2143950602 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2143950603} + m_Layer: 5 + m_Name: VideoSource + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2143950603 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2143950602} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 654995500} + - {fileID: 871570056} + m_Father: {fileID: 992023663} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 330, y: 80} + m_Pivot: {x: 0.5, y: 0.5} diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs index 6337a7692..5b673f032 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs @@ -36,11 +36,20 @@ class BroadcastSample : MonoBehaviour [SerializeField] private SimpleCameraControllerV2 cameraController; [SerializeField] private UIControllerV2 uiController; [SerializeField] private VideoStreamSender videoStreamSender; + [SerializeField] private Dropdown videoSourceTypeSelector; [SerializeField] private Dropdown bandwidthSelector; [SerializeField] private Dropdown scaleResolutionDownSelector; [SerializeField] private Dropdown framerateSelector; [SerializeField] private Dropdown resolutionSelector; + private Dictionary videoSourceTypeOptions = new Dictionary + { + {"Screen", VideoStreamSource.Screen }, + {"Camera", VideoStreamSource.Camera }, + {"Texture", VideoStreamSource.Texture }, + {"WebCam", VideoStreamSource.WebCamera } + }; + private Dictionary bandwidthOptions = new Dictionary() { @@ -105,6 +114,10 @@ private void Awake() } videoStreamSender.SetCodec(settings.SenderVideoCodec); } + videoSourceTypeSelector.options = videoSourceTypeOptions + .Select(pair => new Dropdown.OptionData { text = pair.Key }) + .ToList(); + videoSourceTypeSelector.onValueChanged.AddListener(ChangeVideoSourceType); bandwidthSelector.options = bandwidthOptions .Select(pair => new Dropdown.OptionData {text = pair.Key}) @@ -131,6 +144,12 @@ private void Awake() resolutionSelector.onValueChanged.AddListener(ChangeResolution); } + private void ChangeVideoSourceType(int index) + { + var source = videoSourceTypeOptions.Values.ElementAt(index); + videoStreamSender.source = source; + } + private void ChangeBandwidth(int index) { var bitrate = bandwidthOptions.Values.ElementAt(index); diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index 79ec21708..0a4fdec74 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -259,8 +259,6 @@ public void GetAvailableCodec() Assert.That(h264codec.level, Is.GreaterThan(0)); Assert.That(h264codec.profile, Is.Not.Zero); break; - default: - break; } } } @@ -313,33 +311,31 @@ public IEnumerator CreateTrack() { var go = new GameObject(); var sender = go.AddComponent(); - MediaStreamTrack track = null; - - // With AudioListener - sender.source = AudioStreamSource.AudioListener; - Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); - - var audioListener = go.AddComponent(); - var op = sender.CreateTrack(); - yield return op; - track = op.Track; - Assert.That(track, Is.Not.Null); - track.Dispose(); - track = null; + MediaStreamTrack track; // With AudioSource var go2 = new GameObject(); sender = go2.AddComponent(); sender.source = AudioStreamSource.AudioSource; Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); - var audioSource = go2.AddComponent(); - sender.audioSource = audioSource; - op = sender.CreateTrack(); + sender.audioSource = go2.AddComponent(); + var op = sender.CreateTrack(); yield return op; track = op.Track; Assert.That(track, Is.Not.Null); track.Dispose(); - track = null; + + // With AudioListener + // workaround(kazuki): Fix NullReferenceException in AudioStreamTrack.ProcessAudio. + + //sender.source = AudioStreamSource.AudioListener; + //Assert.That(() => sender.CreateTrack(), Throws.Exception.TypeOf()); + //sender.audioListener = go.AddComponent(); + //op = sender.CreateTrack(); + //yield return op; + //track = op.Track; + //Assert.That(track, Is.Not.Null); + //track.Dispose(); // With Microphone #if !(UNITY_IPHONE || UNITY_ANDROID) From f06b37a91cc041328597ece2f1f312476e81f42c Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Fri, 20 Jan 2023 14:18:05 +0900 Subject: [PATCH 027/117] feat: Create skeleton about Render Streaming Settings class (#842) * rename renderstreaming handler * create skeleton about renderstreaming settings class * fix review --- ...tor.cs => RenderStreamingHandlerEditor.cs} | 4 +- ...a => RenderStreamingHandlerEditor.cs.meta} | 0 .../Runtime/Scripts/RenderStreaming.cs | 168 +++-------------- .../Runtime/Scripts/RenderStreaming.cs.meta | 12 +- .../Runtime/Scripts/RenderStreamingHandler.cs | 173 ++++++++++++++++++ .../Scripts/RenderStreamingHandler.cs.meta | 11 ++ .../Scripts/RenderStreamingSettings.cs | 10 + .../Scripts/RenderStreamingSettings.cs.meta | 3 + .../Runtime/Scripts/SignalingSettings.cs | 12 ++ .../Runtime/Scripts/SignalingSettings.cs.meta | 3 + .../ARFoundation/ARFoundationSample.cs | 2 +- .../Bidirectional/BidirectionalSample.cs | 2 +- .../Example/Broadcast/BroadcastSample.cs | 2 +- .../Samples~/Example/Gyro/GyroSample.cs | 2 +- .../Example/Multiplay/MultiplaySample.cs | 2 +- .../Example/Receiver/ReceiverSample.cs | 2 +- .../WebBrowserInput/WebBrowserInputSample.cs | 2 +- .../Tests/Runtime/RenderStreamingTest.cs | 4 +- 18 files changed, 248 insertions(+), 166 deletions(-) rename com.unity.renderstreaming/Editor/{RenderStreamingEditor.cs => RenderStreamingHandlerEditor.cs} (95%) rename com.unity.renderstreaming/Editor/{RenderStreamingEditor.cs.meta => RenderStreamingHandlerEditor.cs.meta} (100%) create mode 100644 com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs.meta create mode 100644 com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs.meta create mode 100644 com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs.meta diff --git a/com.unity.renderstreaming/Editor/RenderStreamingEditor.cs b/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs similarity index 95% rename from com.unity.renderstreaming/Editor/RenderStreamingEditor.cs rename to com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs index 9ae9c0d77..70ed833a2 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingEditor.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs @@ -9,8 +9,8 @@ namespace Unity.RenderStreaming.Editor /// /// Render Streaming inspector. /// - [CustomEditor(typeof(RenderStreaming))] - internal class RenderStreamingEditor : UnityEditor.Editor + [CustomEditor(typeof(RenderStreamingHandler))] + internal class RenderStreamingHandlerEditor : UnityEditor.Editor { public override void OnInspectorGUI() { diff --git a/com.unity.renderstreaming/Editor/RenderStreamingEditor.cs.meta b/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs.meta similarity index 100% rename from com.unity.renderstreaming/Editor/RenderStreamingEditor.cs.meta rename to com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs.meta diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs index 27ba72bbf..fbd3b12ab 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs @@ -1,10 +1,5 @@ using System; -using System.Linq; -using System.Collections.Generic; -using System.Threading; using UnityEngine; -using Unity.WebRTC; -using Unity.RenderStreaming.Signaling; #if UNITY_EDITOR using UnityEditor; @@ -12,162 +7,45 @@ namespace Unity.RenderStreaming { - [AddComponentMenu("Render Streaming/Render Streaming")] - public sealed class RenderStreaming : MonoBehaviour +#if UNITY_EDITOR + [InitializeOnLoad] +#endif + public static class RenderStreaming { -#pragma warning disable 0649 - [SerializeField, Tooltip("Signaling server url.")] - private string urlSignaling = "http://localhost"; - - [SerializeField, Tooltip("Type of signaling.")] - private string signalingType = typeof(HttpSignaling).FullName; - - [SerializeField, Tooltip("Array to set your own STUN/TURN servers.")] - private RTCIceServer[] iceServers = new RTCIceServer[] - { - new RTCIceServer() {urls = new string[] {"stun:stun.l.google.com:19302"}} - }; - - [SerializeField, Tooltip("Time interval for polling from signaling server.")] - private float interval = 5.0f; - - [SerializeField, Tooltip("List of handlers of signaling process.")] - private List handlers = new List(); - - /// - /// - /// - [SerializeField, Tooltip("Automatically started when called Awake method.")] - public bool runOnAwake = true; -#pragma warning restore 0649 + private static RenderStreamingSettings s_Settings; - private RenderStreamingInternal m_instance; - private SignalingEventProvider m_provider; - private bool m_running; - - static Type GetType(string typeName) + public static RenderStreamingSettings Settings { - var type = Type.GetType(typeName); - if (type != null) return type; - foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) + get => s_Settings; + set { - type = assembly.GetType(typeName); - if (type != null) return type; - } - return null; - } + if (value == null) + throw new ArgumentNullException(nameof(value)); - static ISignaling CreateSignaling(string type, string url, float interval, SynchronizationContext context) - { - Type _type = GetType(type); - if (_type == null) - { - throw new ArgumentException($"Signaling type is undefined. {type}"); - } - object[] args = { url, interval, context }; - return (ISignaling)Activator.CreateInstance(_type, args); - } + if (s_Settings == value) + return; - /// - /// - /// - /// - /// - public void Run( - ISignaling signaling = null, - SignalingHandlerBase[] handlers = null) - { - _Run(null, signaling, handlers); - } - - /// - /// - /// - /// - /// - /// - /// To use this method, Need to depend WebRTC package - public void Run( - RTCConfiguration conf, - ISignaling signaling = null, - SignalingHandlerBase[] handlers = null - ) - { - _Run(conf, signaling, handlers); - } - - /// - /// - /// - /// - /// - /// - private void _Run( - RTCConfiguration? conf = null, - ISignaling signaling = null, - SignalingHandlerBase[] handlers = null - ) - { - RTCConfiguration _conf = - conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers }); - - if (signaling != null) - { - signalingType = signaling.GetType().FullName; - - //todo:: This property is not needed by FurioosSignaling. - urlSignaling = signaling.Url; - interval = signaling.Interval; + s_Settings = value; } - ISignaling _signaling = signaling ?? CreateSignaling( - signalingType, urlSignaling, interval, SynchronizationContext.Current); - RenderStreamingDependencies dependencies = new RenderStreamingDependencies - { - config = _conf, - signaling = _signaling, - startCoroutine = StartCoroutine, - stopCoroutine = StopCoroutine, - resentOfferInterval = interval, - }; - var _handlers = (handlers ?? this.handlers.AsEnumerable()).Where(_ => _ != null); - if (_handlers.Count() == 0) - throw new InvalidOperationException("Handler list is empty."); - - m_instance = new RenderStreamingInternal(ref dependencies); - m_provider = new SignalingEventProvider(m_instance); - - foreach (var handler in _handlers) - { - handler.SetHandler(m_instance); - m_provider.Subscribe(handler); - } - m_running = true; } - /// - /// - /// - public void Stop() + public static bool AutomaticStreaming { - m_instance?.Dispose(); - m_instance = null; - m_running = false; + get => s_Settings.automaticStreaming; + set => s_Settings.automaticStreaming = value; } - void Awake() + public static T GetSignalingSettings() where T : SignalingSettings { - if (!runOnAwake || m_running) - return; - - RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers }; - ISignaling signaling = CreateSignaling( - signalingType, urlSignaling, interval, SynchronizationContext.Current); - _Run(conf, signaling, handlers.ToArray()); + return s_Settings.signalingSettings as T; } - void OnDestroy() + static RenderStreaming() { - Stop(); + // todo: load from assets + var settings = ScriptableObject.CreateInstance(); + settings.automaticStreaming = true; + s_Settings = settings; } } } diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs.meta index de16b1530..fe3b0501c 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs.meta +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs.meta @@ -1,11 +1,3 @@ fileFormatVersion: 2 -guid: 045786cf504bd7347842d6948241cbd0 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: -200 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: +guid: c3a5616a55e14772b19ab62fe697eeee +timeCreated: 1674112709 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs new file mode 100644 index 000000000..552199350 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs @@ -0,0 +1,173 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Threading; +using UnityEngine; +using Unity.WebRTC; +using Unity.RenderStreaming.Signaling; + +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace Unity.RenderStreaming +{ + [AddComponentMenu("Render Streaming/Render Streaming Handler")] + public sealed class RenderStreamingHandler : MonoBehaviour + { +#pragma warning disable 0649 + [SerializeField, Tooltip("Signaling server url.")] + private string urlSignaling = "http://localhost"; + + [SerializeField, Tooltip("Type of signaling.")] + private string signalingType = typeof(HttpSignaling).FullName; + + [SerializeField, Tooltip("Array to set your own STUN/TURN servers.")] + private RTCIceServer[] iceServers = new RTCIceServer[] + { + new RTCIceServer() {urls = new string[] {"stun:stun.l.google.com:19302"}} + }; + + [SerializeField, Tooltip("Time interval for polling from signaling server.")] + private float interval = 5.0f; + + [SerializeField, Tooltip("List of handlers of signaling process.")] + private List handlers = new List(); + + /// + /// + /// + [SerializeField, Tooltip("Automatically started when called Awake method.")] + public bool runOnAwake = true; +#pragma warning restore 0649 + + private RenderStreamingInternal m_instance; + private SignalingEventProvider m_provider; + private bool m_running; + + static Type GetType(string typeName) + { + var type = Type.GetType(typeName); + if (type != null) return type; + foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) + { + type = assembly.GetType(typeName); + if (type != null) return type; + } + return null; + } + + static ISignaling CreateSignaling(string type, string url, float interval, SynchronizationContext context) + { + Type _type = GetType(type); + if (_type == null) + { + throw new ArgumentException($"Signaling type is undefined. {type}"); + } + object[] args = { url, interval, context }; + return (ISignaling)Activator.CreateInstance(_type, args); + } + + /// + /// + /// + /// + /// + public void Run( + ISignaling signaling = null, + SignalingHandlerBase[] handlers = null) + { + _Run(null, signaling, handlers); + } + + /// + /// + /// + /// + /// + /// + /// To use this method, Need to depend WebRTC package + public void Run( + RTCConfiguration conf, + ISignaling signaling = null, + SignalingHandlerBase[] handlers = null + ) + { + _Run(conf, signaling, handlers); + } + + /// + /// + /// + /// + /// + /// + private void _Run( + RTCConfiguration? conf = null, + ISignaling signaling = null, + SignalingHandlerBase[] handlers = null + ) + { + RTCConfiguration _conf = + conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers }); + + if (signaling != null) + { + signalingType = signaling.GetType().FullName; + + //todo:: This property is not needed by FurioosSignaling. + urlSignaling = signaling.Url; + interval = signaling.Interval; + } + ISignaling _signaling = signaling ?? CreateSignaling( + signalingType, urlSignaling, interval, SynchronizationContext.Current); + RenderStreamingDependencies dependencies = new RenderStreamingDependencies + { + config = _conf, + signaling = _signaling, + startCoroutine = StartCoroutine, + stopCoroutine = StopCoroutine, + resentOfferInterval = interval, + }; + var _handlers = (handlers ?? this.handlers.AsEnumerable()).Where(_ => _ != null); + if (_handlers.Count() == 0) + throw new InvalidOperationException("Handler list is empty."); + + m_instance = new RenderStreamingInternal(ref dependencies); + m_provider = new SignalingEventProvider(m_instance); + + foreach (var handler in _handlers) + { + handler.SetHandler(m_instance); + m_provider.Subscribe(handler); + } + m_running = true; + } + + /// + /// + /// + public void Stop() + { + m_instance?.Dispose(); + m_instance = null; + m_running = false; + } + + void Awake() + { + if (!runOnAwake || m_running) + return; + + RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers }; + ISignaling signaling = CreateSignaling( + signalingType, urlSignaling, interval, SynchronizationContext.Current); + _Run(conf, signaling, handlers.ToArray()); + } + + void OnDestroy() + { + Stop(); + } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs.meta new file mode 100644 index 000000000..de16b1530 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 045786cf504bd7347842d6948241cbd0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: -200 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs new file mode 100644 index 000000000..f6cdaaa01 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace Unity.RenderStreaming +{ + public class RenderStreamingSettings : ScriptableObject + { + [SerializeField] public bool automaticStreaming; + [SerializeField] public SignalingSettings signalingSettings; + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs.meta new file mode 100644 index 000000000..490f353a1 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bc8ba17751ad4107a50fa1415017b6b1 +timeCreated: 1674111941 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs new file mode 100644 index 000000000..416b0f359 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs @@ -0,0 +1,12 @@ +using System; + +namespace Unity.RenderStreaming +{ + public abstract class SignalingSettings + { + public bool runOnAwake; + public string urlSignaling = "http://127.0.0.1:80"; + public Unity.WebRTC.RTCIceServer[] iceServers; + public abstract Type signalingClass { get; } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs.meta new file mode 100644 index 000000000..92109b7b2 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ff254cc8ca9e409bbfe92d5c717e71ab +timeCreated: 1674112040 \ No newline at end of file diff --git a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs index 32fbceb18..275ef592c 100644 --- a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs @@ -11,7 +11,7 @@ namespace Unity.RenderStreaming.Samples internal class ARFoundationSample : MonoBehaviour { #pragma warning disable 0649 - [SerializeField] private RenderStreaming renderStreaming; + [SerializeField] private RenderStreamingHandler renderStreaming; [SerializeField] private Button startButton; [SerializeField] private Button stopButton; [SerializeField] private RawImage remoteVideoImage; diff --git a/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs b/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs index a85147c26..99e4f33e2 100644 --- a/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs @@ -7,7 +7,7 @@ namespace Unity.RenderStreaming.Samples class BidirectionalSample : MonoBehaviour { #pragma warning disable 0649 - [SerializeField] private RenderStreaming renderStreaming; + [SerializeField] private RenderStreamingHandler renderStreaming; [SerializeField] private Dropdown webcamSelectDropdown; [SerializeField] private Dropdown microphoneSelectDropdown; [SerializeField] private Button startButton; diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs index 5b673f032..893e1357c 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs @@ -31,7 +31,7 @@ public static void AddListener(this InputAction action, Action(); + component = obj.AddComponent(); } [TearDown] From f8b2222cd6ea361a9dc7dca4d949b670dc0fdf71 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 20 Jan 2023 16:00:29 +0900 Subject: [PATCH 028/117] refactor: Rename class to RenderStreamingHandlerTest (#843) * rename * add class --- .../Runtime/RenderStreamingHandlerTest.cs | 87 +++++++++++++++++++ .../RenderStreamingHandlerTest.cs.meta | 11 +++ .../Tests/Runtime/RenderStreamingTest.cs | 74 +--------------- .../Tests/Runtime/RenderStreamingTest.cs.meta | 2 +- 4 files changed, 100 insertions(+), 74 deletions(-) create mode 100644 com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs create mode 100644 com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs.meta diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs new file mode 100644 index 000000000..c9e4b0294 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs @@ -0,0 +1,87 @@ +using System.Collections; +using NUnit.Framework; +using Unity.RenderStreaming.RuntimeTest.Signaling; +using Unity.RenderStreaming.Signaling; +using UnityEngine; +using UnityEngine.TestTools; +using Object = UnityEngine.Object; + +namespace Unity.RenderStreaming.RuntimeTest +{ + class RenderStreamingHandlerTest + { + RenderStreamingHandler component; + + [SetUp] + public void SetUp() + { + GameObject obj = new GameObject(); + obj.SetActive(false); + component = obj.AddComponent(); + } + + [TearDown] + public void TearDown() + { + Object.DestroyImmediate(component.gameObject); + } + + [Test] + public void DoNothing() + { + } + + [Test] + public void Run() + { + var handler = component.gameObject.AddComponent(); + var handlers = new SignalingHandlerBase[] { handler }; + ISignaling mock = new MockSignaling(); + component.runOnAwake = false; + component.gameObject.SetActive(true); + component.Run(signaling: mock, handlers: handlers); + } + + [Test, Ignore("Failed this test on macOS and Linux platform because of the signaling process.")] + public void RunDefault() + { + var handler = component.gameObject.AddComponent(); + var handlers = new SignalingHandlerBase[] { handler }; + ISignaling mock = new MockSignaling(); + component.runOnAwake = false; + component.gameObject.SetActive(true); + component.Run(handlers:handlers); + } + + + [Test] + public void ThrowExceptionIfHandlerIsNullOrEmpty() + { + ISignaling mock = new MockSignaling(); + component.runOnAwake = false; + component.gameObject.SetActive(true); + Assert.That(() => component.Run(signaling: mock), + Throws.InvalidOperationException); + + var handlers = new SignalingHandlerBase[] {}; + Assert.That(() => component.Run(signaling: mock, handlers:handlers), + Throws.InvalidOperationException); + } + + + [UnityTest] + public IEnumerator RunAgain() + { + var handler = component.gameObject.AddComponent(); + var handlers = new SignalingHandlerBase[] { handler }; + ISignaling mock = new MockSignaling(); + component.runOnAwake = false; + component.gameObject.SetActive(true); + component.Run(signaling:mock, handlers:handlers); + yield return 0; + component.Stop(); + yield return 0; + component.Run(signaling:mock, handlers:handlers); + } + } +} diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs.meta new file mode 100644 index 000000000..819f26cee --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 160df6e38142e48e0ac66ecd055482eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs index b0c1252b3..ba8380987 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs @@ -10,78 +10,6 @@ namespace Unity.RenderStreaming.RuntimeTest { class RenderStreamingTest { - RenderStreamingHandler component; - - [SetUp] - public void SetUp() - { - GameObject obj = new GameObject(); - obj.SetActive(false); - component = obj.AddComponent(); - } - - [TearDown] - public void TearDown() - { - Object.DestroyImmediate(component.gameObject); - } - - [Test] - public void DoNothing() - { - } - - [Test] - public void Run() - { - var handler = component.gameObject.AddComponent(); - var handlers = new SignalingHandlerBase[] { handler }; - ISignaling mock = new MockSignaling(); - component.runOnAwake = false; - component.gameObject.SetActive(true); - component.Run(signaling: mock, handlers: handlers); - } - - [Test, Ignore("Failed this test on macOS and Linux platform because of the signaling process.")] - public void RunDefault() - { - var handler = component.gameObject.AddComponent(); - var handlers = new SignalingHandlerBase[] { handler }; - ISignaling mock = new MockSignaling(); - component.runOnAwake = false; - component.gameObject.SetActive(true); - component.Run(handlers:handlers); - } - - - [Test] - public void ThrowExceptionIfHandlerIsNullOrEmpty() - { - ISignaling mock = new MockSignaling(); - component.runOnAwake = false; - component.gameObject.SetActive(true); - Assert.That(() => component.Run(signaling: mock), - Throws.InvalidOperationException); - - var handlers = new SignalingHandlerBase[] {}; - Assert.That(() => component.Run(signaling: mock, handlers:handlers), - Throws.InvalidOperationException); - } - - - [UnityTest] - public IEnumerator RunAgain() - { - var handler = component.gameObject.AddComponent(); - var handlers = new SignalingHandlerBase[] { handler }; - ISignaling mock = new MockSignaling(); - component.runOnAwake = false; - component.gameObject.SetActive(true); - component.Run(signaling:mock, handlers:handlers); - yield return 0; - component.Stop(); - yield return 0; - component.Run(signaling:mock, handlers:handlers); - } + // todo } } diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs.meta index 819f26cee..7f377d4f4 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs.meta +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 160df6e38142e48e0ac66ecd055482eb +guid: 2c3eedb01fcca994f84ef9382259eb99 MonoImporter: externalObjects: {} serializedVersion: 2 From 838fe095a10b79998ac75bfb51971e1bd96ca358 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Mon, 23 Jan 2023 14:33:43 +0900 Subject: [PATCH 029/117] fix: Add AudioStreamSender.SetData method to set any audio buffer (#840) --- .../Runtime/Scripts/AudioStreamSender.cs | 99 ++++++++++++------- .../Tests/Runtime/StreamingComponentTest.cs | 28 ++++++ 2 files changed, 91 insertions(+), 36 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs index 9bb3f2650..0c8c32829 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs @@ -9,22 +9,26 @@ namespace Unity.RenderStreaming { /// - /// + /// /// public enum AudioStreamSource { /// - /// + /// /// AudioListener = 0, /// - /// + /// /// AudioSource = 1, /// - /// + /// /// - Microphone = 2 + Microphone = 2, + /// + /// + /// + APIOnly = 3 } /// @@ -61,13 +65,10 @@ public class AudioStreamSender : StreamSenderBase private AudioStreamSourceImpl m_sourceImpl = null; - /// workaround. - private Action m_onAudioFilterRead = null; - private int m_frequency = 48000; /// - /// + /// /// public AudioStreamSource source { @@ -87,7 +88,7 @@ public AudioStreamSource source } /// - /// + /// /// public AudioCodecInfo codec { @@ -95,7 +96,7 @@ public AudioCodecInfo codec } /// - /// + /// /// public uint minBitrate { @@ -103,7 +104,7 @@ public uint minBitrate } /// - /// + /// /// public uint maxBitrate { @@ -131,7 +132,7 @@ public int sourceDeviceIndex } /// - /// + /// /// public AudioSource audioSource { @@ -151,7 +152,7 @@ public AudioSource audioSource } /// - /// + /// /// public AudioListener audioListener { @@ -171,7 +172,7 @@ public AudioListener audioListener } /// - /// + /// /// /// static public IEnumerable GetAvailableCodecs() @@ -182,7 +183,7 @@ static public IEnumerable GetAvailableCodecs() } /// - /// + /// /// /// /// @@ -201,7 +202,7 @@ public void SetBitrate(uint minBitrate, uint maxBitrate) } /// - /// + /// /// /// public void SetCodec(AudioCodecInfo codec) @@ -273,6 +274,8 @@ AudioStreamSourceImpl CreateAudioStreamSource() return new AudioStreamSourceAudioSource(this); case AudioStreamSource.Microphone: return new AudioStreamSourceMicrophone(this); + case AudioStreamSource.APIOnly: + return new AudioStreamSourceAPIOnly(this); } throw new InvalidOperationException(""); } @@ -291,18 +294,18 @@ private protected override void OnDisable() base.OnDisable(); } - /// workaround. - /// todo: Should add AudioStreamTrack supports AudioListener - void OnAudioFilterRead(float[] data, int channels) + public void SetData(ref NativeArray nativeArray, int channels) { - // todo: Should add AudioStreamTrack supports AudioListener - if (m_Source == AudioStreamSource.AudioListener && m_onAudioFilterRead != null) - m_onAudioFilterRead(data, channels, m_sampleRate); + if (m_Source != AudioStreamSource.APIOnly) + throw new InvalidOperationException("To use this method, please set AudioStreamSource.APIOnly to source property"); + if (!isPlaying) + return; + (m_sourceImpl as AudioStreamSourceAPIOnly)?.SetData(ref nativeArray, channels, m_sampleRate); } abstract class AudioStreamSourceImpl : IDisposable { - public AudioStreamSourceImpl(AudioStreamSender parent) + protected AudioStreamSourceImpl(AudioStreamSender parent) { } @@ -313,6 +316,7 @@ public AudioStreamSourceImpl(AudioStreamSender parent) class AudioStreamSourceAudioListener : AudioStreamSourceImpl { private AudioListener m_audioListener; + public AudioStreamSourceAudioListener(AudioStreamSender parent) : base(parent) { m_audioListener = parent.m_AudioListener; @@ -410,12 +414,7 @@ IEnumerator CreateTrackCoroutine(WaitForCreateTrack instruction) // set the latency to “0” samples before the audio starts to play. yield return new WaitUntil(() => Microphone.GetPosition(m_deviceName) > 0); - /// todo: Throw exception if gameObject already has the AudioSource. - /// To fix this, fix the issue of AudioStreamTrack first. - m_audioSource = m_parent.gameObject.GetComponent(); - if(m_audioSource == null) - m_audioSource = m_parent.gameObject.AddComponent(); - + m_audioSource = m_parent.gameObject.AddComponent(); m_audioSource.clip = micClip; m_audioSource.loop = true; m_audioSource.Play(); @@ -435,12 +434,7 @@ public override void Dispose() } m_audioSource.clip = null; - /// todo: AudioCustomFilter should be removed before destroying m_audioSource because - /// AudioSource is the RequiredComponent by AudioCustomFilter. But AudioStreamTrack removes - /// the AudioCustomFilter asyncnouslly. So we got the error log below. - /// "Can't remove AudioSource because AudioCustomFilter (Script) depends on it" - - // Destroy(m_audioSource); + Destroy(m_audioSource); m_audioSource = null; } if (Microphone.IsRecording(m_deviceName)) @@ -453,5 +447,38 @@ public override void Dispose() Dispose(); } } + + class AudioStreamSourceAPIOnly : AudioStreamSourceImpl + { + AudioStreamTrack m_audioTrack; + + public AudioStreamSourceAPIOnly(AudioStreamSender parent) : base(parent) + { + + } + + public override WaitForCreateTrack CreateTrack() + { + var instruction = new WaitForCreateTrack(); + m_audioTrack = new AudioStreamTrack(); + instruction.Done(new AudioStreamTrack()); + return instruction; + } + + public void SetData(ref NativeArray nativeArray, int channels, int sampleRate) + { + m_audioTrack?.SetData(ref nativeArray, channels, sampleRate); + } + + public override void Dispose() + { + GC.SuppressFinalize(this); + } + + ~AudioStreamSourceAPIOnly() + { + Dispose(); + } + } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index 0a4fdec74..4da868a47 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using NUnit.Framework; +using Unity.Collections; using UnityEngine; using Unity.WebRTC; using UnityEngine.TestTools; @@ -311,6 +312,7 @@ public IEnumerator CreateTrack() { var go = new GameObject(); var sender = go.AddComponent(); + MediaStreamTrack track; // With AudioSource @@ -325,6 +327,16 @@ public IEnumerator CreateTrack() Assert.That(track, Is.Not.Null); track.Dispose(); + // APIOnly + var go3 = new GameObject(); + sender = go3.AddComponent(); + sender.source = AudioStreamSource.APIOnly; + op = sender.CreateTrack(); + yield return op; + track = op.Track; + Assert.That(track, Is.Not.Null); + track.Dispose(); + // With AudioListener // workaround(kazuki): Fix NullReferenceException in AudioStreamTrack.ProcessAudio. @@ -352,6 +364,7 @@ public IEnumerator CreateTrack() #endif UnityEngine.Object.DestroyImmediate(go); UnityEngine.Object.DestroyImmediate(go2); + UnityEngine.Object.DestroyImmediate(go3); } [UnityTest] @@ -441,6 +454,21 @@ public void SetCodec() sender.SetCodec(null); Assert.That(sender.codec, Is.Null); } + + [Test] + public void SetData() + { + var go = new GameObject(); + var sender = go.AddComponent(); + + NativeArray nativeArray = new NativeArray(256, Allocator.Temp); + Assert.That(() => sender.SetData(ref nativeArray, 2), Throws.Exception.TypeOf()); + + sender.source = AudioStreamSource.APIOnly; + Assert.That(() => sender.SetData(ref nativeArray, 2), Throws.Nothing); + + nativeArray.Dispose(); + } } class AudioStreamReceiverTest From 90c705f9cd0b3ed5def5f9ba09b9be5af5f999d9 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:02:22 +0900 Subject: [PATCH 030/117] chore: Upgrade package version 3.1.0-exp.6 (#845) --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + .yamato/compile-package.yml | 2 +- README.md | 2 +- RenderStreaming~/Packages/manifest.json | 3 ++- RenderStreaming~/Packages/packages-lock.json | 17 ++++++++--------- .../Documentation~/installation.md | 2 +- .../Editor/WebAppDownloader.cs | 2 +- com.unity.renderstreaming/package.json | 2 +- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index dc43659ab..e4d84b934 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -27,6 +27,7 @@ body: What version of the package are you using? You can check the Unity version in Package Manager Window. See [manual](https://docs.unity3d.com/Manual/upm-ui.html). options: + - 3.1.0-exp.6 - 3.1.0-exp.5 - 3.1.0-exp.4 - 3.1.0-exp.3 diff --git a/.yamato/compile-package.yml b/.yamato/compile-package.yml index cbd991b21..80446154c 100644 --- a/.yamato/compile-package.yml +++ b/.yamato/compile-package.yml @@ -5,7 +5,7 @@ compile_test_for_package_version: flavor: b1.large image: package-ci/win10:v4 variables: - VERSION: 3.1.0-exp.5 + VERSION: 3.1.0-exp.6 commands: # When unity-config will be part of the image, this will turn into a no-op - | diff --git a/README.md b/README.md index 2f8f13a2e..c5b8f667a 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Please see [Furioos Tutorial](com.unity.renderstreaming/Documentation~/deploy-to | `3.1.0-exp.3` | - Fix bugs | Feb 2022 | | `3.1.0-exp.4` | - Streaming settings API
- *Unity 2022.1* support
- Remove *Unity 2019.4* from support list | Oct 2022 | | `3.1.0-exp.5` | - Fix bugs | Jan 2023 | -| `3.1.0-exp.6` | - Streaming Settings Window
- Auto Configuration | Feb 2023 | +| `3.1.0-exp.6` | - Streaming Settings Window
- Auto Configuration
- Command line option | Feb 2023 | ## FAQ diff --git a/RenderStreaming~/Packages/manifest.json b/RenderStreaming~/Packages/manifest.json index c00e92f16..0ae5bdbf5 100644 --- a/RenderStreaming~/Packages/manifest.json +++ b/RenderStreaming~/Packages/manifest.json @@ -1,6 +1,6 @@ { "dependencies": { - "com.unity.ide.rider": "3.0.17", + "com.unity.ide.rider": "3.0.18", "com.unity.ide.visualstudio": "2.0.17", "com.unity.ide.vscode": "1.2.5", "com.unity.test-framework": "1.1.33", @@ -9,6 +9,7 @@ "com.unity.xr.arcore": "4.2.7", "com.unity.xr.arfoundation": "4.2.7", "com.unity.xr.arkit": "4.2.7", + "com.unity.xr.management": "4.3.1", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", "com.unity.modules.audio": "1.0.0", diff --git a/RenderStreaming~/Packages/packages-lock.json b/RenderStreaming~/Packages/packages-lock.json index fb87c376f..25e797b11 100644 --- a/RenderStreaming~/Packages/packages-lock.json +++ b/RenderStreaming~/Packages/packages-lock.json @@ -15,7 +15,7 @@ "url": "https://packages.unity.com" }, "com.unity.ide.rider": { - "version": "3.0.17", + "version": "3.0.18", "depth": 0, "source": "registry", "dependencies": { @@ -162,7 +162,7 @@ }, "com.unity.xr.legacyinputhelpers": { "version": "2.1.10", - "depth": 2, + "depth": 1, "source": "registry", "dependencies": { "com.unity.modules.vr": "1.0.0", @@ -171,15 +171,14 @@ "url": "https://packages.unity.com" }, "com.unity.xr.management": { - "version": "4.2.0", - "depth": 1, + "version": "4.3.1", + "depth": 0, "source": "registry", "dependencies": { "com.unity.modules.subsystems": "1.0.0", "com.unity.modules.vr": "1.0.0", "com.unity.modules.xr": "1.0.0", - "com.unity.xr.legacyinputhelpers": "2.1.7", - "com.unity.subsystemregistration": "1.0.6" + "com.unity.xr.legacyinputhelpers": "2.1.7" }, "url": "https://packages.unity.com" }, @@ -241,7 +240,7 @@ }, "com.unity.modules.subsystems": { "version": "1.0.0", - "depth": 2, + "depth": 1, "source": "builtin", "dependencies": { "com.unity.modules.jsonserialize": "1.0.0" @@ -282,7 +281,7 @@ }, "com.unity.modules.vr": { "version": "1.0.0", - "depth": 2, + "depth": 1, "source": "builtin", "dependencies": { "com.unity.modules.jsonserialize": "1.0.0", @@ -292,7 +291,7 @@ }, "com.unity.modules.xr": { "version": "1.0.0", - "depth": 2, + "depth": 1, "source": "builtin", "dependencies": { "com.unity.modules.physics": "1.0.0", diff --git a/com.unity.renderstreaming/Documentation~/installation.md b/com.unity.renderstreaming/Documentation~/installation.md index b8094569d..127e947cc 100644 --- a/com.unity.renderstreaming/Documentation~/installation.md +++ b/com.unity.renderstreaming/Documentation~/installation.md @@ -11,7 +11,7 @@ Check Package Manager window, Click `+` button and select `Add package from git Input the string below to the input field. ``` -com.unity.renderstreaming@3.1.0-exp.5 +com.unity.renderstreaming@3.1.0-exp.6 ``` The list of version string is [here](https://github.com/Unity-Technologies/UnityRenderStreaming/tags). In most cases, the latest version is recommended to use. diff --git a/com.unity.renderstreaming/Editor/WebAppDownloader.cs b/com.unity.renderstreaming/Editor/WebAppDownloader.cs index 81a03f5ab..508c014eb 100644 --- a/com.unity.renderstreaming/Editor/WebAppDownloader.cs +++ b/com.unity.renderstreaming/Editor/WebAppDownloader.cs @@ -7,7 +7,7 @@ namespace Unity.RenderStreaming.Editor internal static class WebAppDownloader { const string URLRoot = "https://github.com/Unity-Technologies/UnityRenderStreaming"; - const string LatestKnownVersion = "3.1.0-exp.5"; + const string LatestKnownVersion = "3.1.0-exp.6"; // TODO::fix release process of webserver runtime. const string FileNameWebAppForMac = "webserver_mac"; diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index c1e6a5a9f..747ffe0c1 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.renderstreaming", "displayName": "Unity Render Streaming", - "version": "3.1.0-exp.5", + "version": "3.1.0-exp.6", "unity": "2020.3", "description": "This is a package for using Unity Render Streaming technology.", "dependencies": { From 32b881b96ae0d642c688406c0db2d2db4a449acc Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 24 Jan 2023 10:02:43 +0900 Subject: [PATCH 031/117] fix: Remove method calling Unity API to avoid UnityException (#846) --- com.unity.renderstreaming/Runtime/Scripts/StreamReceiverBase.cs | 2 -- com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/StreamReceiverBase.cs b/com.unity.renderstreaming/Runtime/Scripts/StreamReceiverBase.cs index e8f9480d7..b77c08cd5 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/StreamReceiverBase.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/StreamReceiverBase.cs @@ -36,8 +36,6 @@ public bool isPlaying { get { - if (!Application.isPlaying) - return false; if (string.IsNullOrEmpty(Transceiver.Mid)) return false; if (Transceiver.Sender.Track.ReadyState == TrackState.Ended) diff --git a/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs b/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs index 7bf2d6f3f..052d2905f 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/StreamSenderBase.cs @@ -113,8 +113,6 @@ public bool isPlaying { get { - if (!Application.isPlaying) - return false; foreach (var transceiver in Transceivers.Values) { if (string.IsNullOrEmpty(transceiver.Mid)) From 623c7cb61b42fbafe5fd593b7c73401d24287b0f Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Tue, 24 Jan 2023 17:28:11 +0900 Subject: [PATCH 032/117] feat: Implement Automatic Streaming component (#844) * rename renderstreaming handler * create skeleton about renderstreaming settings class * add websocket signaling settings class * signaling handler can add/remove at runtime * implement automatic streaming * run on initialize automatic * avoid exception * set false for not run automatic streaming * rename * add test * fix test * dont provide renderstreaming setting overwrite method * add handler test * fix review * replace propety signaling settings * [skip ci] comment * fix signaling interval propety * revert comment out * fix render streaming handler editor --- .../Editor/RenderStreamingHandlerEditor.cs | 24 +-- .../Runtime/Scripts/AutomaticStreaming.cs | 181 ++++++++++++++++++ .../Scripts/AutomaticStreaming.cs.meta | 3 + .../Runtime/Scripts/RenderStreaming.cs | 83 ++++++-- .../Runtime/Scripts/RenderStreamingHandler.cs | 106 ++++++---- .../Scripts/RenderStreamingSettings.cs | 2 +- .../Scripts/Signaling/FurioosSignaling.cs | 6 +- .../Scripts/Signaling/HttpSignaling.cs | 13 +- .../Runtime/Scripts/Signaling/ISignaling.cs | 2 - .../Scripts/Signaling/WebSocketSignaling.cs | 8 +- .../Runtime/Scripts/SignalingSettings.cs | 20 +- .../Samples~/Example/Scripts/SceneSelectUI.cs | 34 ++-- .../Tests/Editor/RenderStreamingTest.cs | 13 ++ .../Tests/Editor/RenderStreamingTest.cs.meta | 3 + .../Tests/Runtime/PrivateSignalingTest.cs | 18 +- .../Runtime/RenderStreamingHandlerTest.cs | 29 ++- .../Tests/Runtime/RenderStreamingTest.cs | 19 +- .../Tests/Runtime/Signaling/MockSignaling.cs | 5 + .../Signaling/MockSignalingSettings.cs | 9 + .../Signaling/MockSignalingSettings.cs.meta | 3 + .../Tests/Runtime/SignalingTest.cs | 13 +- 21 files changed, 476 insertions(+), 118 deletions(-) create mode 100644 com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs.meta create mode 100644 com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs create mode 100644 com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs.meta create mode 100644 com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs create mode 100644 com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs.meta diff --git a/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs b/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs index 70ed833a2..b10bda86e 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs @@ -14,17 +14,19 @@ internal class RenderStreamingHandlerEditor : UnityEditor.Editor { public override void OnInspectorGUI() { - { - serializedObject.Update(); - ShowSignalingTypes(serializedObject.FindProperty("signalingType")); - EditorGUILayout.PropertyField(serializedObject.FindProperty("urlSignaling"), new GUIContent("Signaling URL")); - ShowIceServerList(serializedObject.FindProperty("iceServers")); - EditorGUILayout.PropertyField(serializedObject.FindProperty("interval")); - EditorGUILayout.PropertyField(serializedObject.FindProperty("handlers")); - EditorGUILayout.PropertyField(serializedObject.FindProperty("runOnAwake")); - - serializedObject.ApplyModifiedProperties(); - } + // ToDo: Create component UI on URS-553 + base.OnInspectorGUI(); + // { + // serializedObject.Update(); + // ShowSignalingTypes(serializedObject.FindProperty("signalingType")); + // EditorGUILayout.PropertyField(serializedObject.FindProperty("urlSignaling"), new GUIContent("Signaling URL")); + // ShowIceServerList(serializedObject.FindProperty("iceServers")); + // EditorGUILayout.PropertyField(serializedObject.FindProperty("interval")); + // EditorGUILayout.PropertyField(serializedObject.FindProperty("handlers")); + // EditorGUILayout.PropertyField(serializedObject.FindProperty("runOnAwake")); + // + // serializedObject.ApplyModifiedProperties(); + // } } static void ShowIceServerList(SerializedProperty list) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs new file mode 100644 index 000000000..627e699bb --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs @@ -0,0 +1,181 @@ +using System; +using System.Linq; +using Unity.Collections; +using Unity.RenderStreaming.InputSystem; +using Unity.WebRTC; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Users; +using UnityEngine.SceneManagement; + +namespace Unity.RenderStreaming +{ + internal class AutomaticStreaming : MonoBehaviour + { + private RenderStreamingHandler renderstreaming; + private Broadcast broadcast; + private VideoStreamSender videoStreamSender; + private AudioStreamSender audioStreamSender; + private AutoInputReceiver inputReceiver; + + private void Awake() + { + gameObject.hideFlags = HideFlags.HideInHierarchy; + + broadcast = gameObject.AddComponent(); + + videoStreamSender = gameObject.AddComponent(); + videoStreamSender.source = VideoStreamSource.Screen; + videoStreamSender.SetTextureSize(new Vector2Int(Screen.width, Screen.height)); + broadcast.AddComponent(videoStreamSender); + + audioStreamSender = gameObject.AddComponent(); + audioStreamSender.source = AudioStreamSource.APIOnly; + broadcast.AddComponent(audioStreamSender); + + inputReceiver = gameObject.AddComponent(); + broadcast.AddComponent(inputReceiver); + + renderstreaming = gameObject.AddComponent(); + renderstreaming.AddSignalingHandler(broadcast); + var signalingSettings = RenderStreaming.GetSignalingSettings(); + renderstreaming.SetSignalingSettings(signalingSettings); + renderstreaming.Run(); + + SceneManager.activeSceneChanged += (scene1, scene2) => + { + var audioListener = FindObjectOfType(); + if (audioListener == null || audioListener.gameObject.GetComponent() != null) + { + return; + } + + var autoFilter = audioListener.gameObject.AddComponent(); + autoFilter.SetSender(audioStreamSender); + }; + } + + private void OnDestroy() + { + renderstreaming.Stop(); + renderstreaming = null; + broadcast = null; + videoStreamSender = null; + audioStreamSender = null; + inputReceiver = null; + } + + class AutoAudioFilter : MonoBehaviour + { + private AudioStreamSender sender; + + public void SetSender(AudioStreamSender sender) + { + this.sender = sender; + } + + private void Awake() + { + this.hideFlags = HideFlags.HideInInspector; + } + + private void OnAudioFilterRead(float[] data, int channels) + { + if (sender == null || sender.source != AudioStreamSource.APIOnly) + { + return; + } + + var nativeArray = new NativeArray(data, Allocator.Temp); + sender.SetData(ref nativeArray, channels); + } + + private void OnDestroy() + { + sender = null; + } + } + + class AutoInputReceiver : InputChannelReceiverBase + { + public override event Action onDeviceChange; + + protected virtual void OnEnable() + { + onDeviceChange += OnDeviceChange; + } + + protected virtual void OnDisable() + { + onDeviceChange -= OnDeviceChange; + } + + private void PerformPairingWithDevice(InputDevice device) + { + inputUser = InputUser.PerformPairingWithDevice(device, inputUser); + } + + private void UnpairDevices(InputDevice device) + { + if (!inputUser.valid) + return; + inputUser.UnpairDevice(device); + } + + public override void SetChannel(string connectionId, RTCDataChannel channel) + { + if (channel == null) + { + Dispose(); + } + else + { + AssignUserAndDevices(); + receiver = new Receiver(channel); + receiver.onDeviceChange += onDeviceChange; + receiverInput = new InputSystem.InputRemoting(receiver); + subscriberDisposer = receiverInput.Subscribe(receiverInput); + receiverInput.StartSending(); + } + + base.SetChannel(connectionId, channel); + } + + protected virtual void OnDestroy() + { + Dispose(); + } + + protected virtual void Dispose() + { + receiverInput?.StopSending(); + subscriberDisposer?.Dispose(); + receiver?.Dispose(); + receiver = null; + } + + [NonSerialized] private InputUser inputUser; + [NonSerialized] private Receiver receiver; + [NonSerialized] private InputSystem.InputRemoting receiverInput; + [NonSerialized] private IDisposable subscriberDisposer; + + private void AssignUserAndDevices() + { + inputUser = InputUser.all.FirstOrDefault(); + } + + protected virtual void OnDeviceChange(InputDevice device, InputDeviceChange change) + { + switch (change) + { + case InputDeviceChange.Added: + PerformPairingWithDevice(device); + return; + case InputDeviceChange.Removed: + UnpairDevices(device); + return; + } + } + } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs.meta new file mode 100644 index 000000000..40da6eca6 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50f50ea0132f471fa447969dcbceb3e9 +timeCreated: 1674179059 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs index fbd3b12ab..05fc0a7f3 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs @@ -1,5 +1,6 @@ -using System; +using Unity.WebRTC; using UnityEngine; +using Object = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; @@ -12,40 +13,84 @@ namespace Unity.RenderStreaming #endif public static class RenderStreaming { - private static RenderStreamingSettings s_Settings; + internal static RenderStreamingSettings s_settings; + internal static GameObject s_automaticStreamingObject; - public static RenderStreamingSettings Settings + public static bool AutomaticStreaming { - get => s_Settings; + get => s_settings.automaticStreaming; set { - if (value == null) - throw new ArgumentNullException(nameof(value)); + s_settings.automaticStreaming = value; + ApplySettings(); + } + } + + public static T GetSignalingSettings() where T : SignalingSettings + { + return s_settings.signalingSettings as T; + } - if (s_Settings == value) - return; + static RenderStreaming() + { + // todo: load from assets + var settings = ScriptableObject.CreateInstance(); + settings.automaticStreaming = false; + var signalingSettings = new WebSocketSignalingSettings + { + urlSignaling = "ws://127.0.0.1:80", + iceServers = new RTCIceServer[] + { + new RTCIceServer() {urls = new string[] {"stun:stun.l.google.com:19302"}} + } + }; + settings.signalingSettings = signalingSettings; + s_settings = settings; + } - s_Settings = value; + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void RunInitialize() + { + if (AutomaticStreaming) + { + CreateAutomaticStreaming(); } } - public static bool AutomaticStreaming + private static void ApplySettings() { - get => s_Settings.automaticStreaming; - set => s_Settings.automaticStreaming = value; + if (!Application.isPlaying) + { + return; + } + + if (s_settings.automaticStreaming && s_automaticStreamingObject == null) + { + CreateAutomaticStreaming(); + } + + if (!s_settings.automaticStreaming) + { + CleanUpAutomaticStreaming(); + } } - public static T GetSignalingSettings() where T : SignalingSettings + private static void CreateAutomaticStreaming() { - return s_Settings.signalingSettings as T; + if (s_automaticStreamingObject != null) + { + Object.DestroyImmediate(s_automaticStreamingObject); + } + + s_automaticStreamingObject = new GameObject("AutomaticStreaming"); + s_automaticStreamingObject.AddComponent(); + Object.DontDestroyOnLoad(s_automaticStreamingObject); } - static RenderStreaming() + private static void CleanUpAutomaticStreaming() { - // todo: load from assets - var settings = ScriptableObject.CreateInstance(); - settings.automaticStreaming = true; - s_Settings = settings; + Object.DestroyImmediate(s_automaticStreamingObject); + s_automaticStreamingObject = null; } } } diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs index 552199350..1f7ef73d5 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs @@ -16,21 +16,16 @@ namespace Unity.RenderStreaming public sealed class RenderStreamingHandler : MonoBehaviour { #pragma warning disable 0649 - [SerializeField, Tooltip("Signaling server url.")] - private string urlSignaling = "http://localhost"; - - [SerializeField, Tooltip("Type of signaling.")] - private string signalingType = typeof(HttpSignaling).FullName; - - [SerializeField, Tooltip("Array to set your own STUN/TURN servers.")] - private RTCIceServer[] iceServers = new RTCIceServer[] + // ToDo: Create component UI on URS-553 + [SerializeReference] private SignalingSettings signalingSettings = new WebSocketSignalingSettings { - new RTCIceServer() {urls = new string[] {"stun:stun.l.google.com:19302"}} + urlSignaling = "ws://127.0.0.1:80", + iceServers = new RTCIceServer[] + { + new RTCIceServer() {urls = new string[] {"stun:stun.l.google.com:19302"}} + } }; - [SerializeField, Tooltip("Time interval for polling from signaling server.")] - private float interval = 5.0f; - [SerializeField, Tooltip("List of handlers of signaling process.")] private List handlers = new List(); @@ -45,27 +40,67 @@ public sealed class RenderStreamingHandler : MonoBehaviour private SignalingEventProvider m_provider; private bool m_running; - static Type GetType(string typeName) + public bool Running => m_running; + + static ISignaling CreateSignaling(SignalingSettings settings, SynchronizationContext context) { - var type = Type.GetType(typeName); - if (type != null) return type; - foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) + if (settings.signalingClass == null) { - type = assembly.GetType(typeName); - if (type != null) return type; + throw new ArgumentException($"Signaling type is undefined. {settings.signalingClass}"); } - return null; + object[] args = { settings, context }; + return (ISignaling)Activator.CreateInstance(settings.signalingClass, args); } - static ISignaling CreateSignaling(string type, string url, float interval, SynchronizationContext context) + /// + /// + /// + /// + /// + public void SetSignalingSettings(SignalingSettings settings) { - Type _type = GetType(type); - if (_type == null) + if (m_running) { - throw new ArgumentException($"Signaling type is undefined. {type}"); + throw new InvalidOperationException("The Signaling process has already started."); } - object[] args = { url, interval, context }; - return (ISignaling)Activator.CreateInstance(_type, args); + + signalingSettings = settings; + } + + /// + /// + /// + /// + public void AddSignalingHandler(SignalingHandlerBase handlerBase) + { + if (handlers.Contains(handlerBase)) + { + return; + } + handlers.Add(handlerBase); + + if (!m_running) + { + return; + } + handlerBase.SetHandler(m_instance); + m_provider.Subscribe(handlerBase); + } + + /// + /// + /// + /// + public void RemoveSignalingHandler(SignalingHandlerBase handlerBase) + { + handlers.Remove(handlerBase); + + if (!m_running) + { + return; + } + handlerBase.SetHandler(null); + m_provider.Unsubscribe(handlerBase); } ///
@@ -109,25 +144,21 @@ private void _Run( ) { RTCConfiguration _conf = - conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers }); + conf.GetValueOrDefault(new RTCConfiguration { iceServers = signalingSettings.iceServers }); if (signaling != null) { - signalingType = signaling.GetType().FullName; - - //todo:: This property is not needed by FurioosSignaling. - urlSignaling = signaling.Url; - interval = signaling.Interval; + signalingSettings.urlSignaling = signaling.Url; } - ISignaling _signaling = signaling ?? CreateSignaling( - signalingType, urlSignaling, interval, SynchronizationContext.Current); + + ISignaling _signaling = signaling ?? CreateSignaling(signalingSettings, SynchronizationContext.Current); RenderStreamingDependencies dependencies = new RenderStreamingDependencies { config = _conf, signaling = _signaling, startCoroutine = StartCoroutine, stopCoroutine = StopCoroutine, - resentOfferInterval = interval, + resentOfferInterval = 5.0f, }; var _handlers = (handlers ?? this.handlers.AsEnumerable()).Where(_ => _ != null); if (_handlers.Count() == 0) @@ -156,12 +187,11 @@ public void Stop() void Awake() { - if (!runOnAwake || m_running) + if (!runOnAwake || m_running || handlers.Count == 0) return; - RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers }; - ISignaling signaling = CreateSignaling( - signalingType, urlSignaling, interval, SynchronizationContext.Current); + RTCConfiguration conf = new RTCConfiguration { iceServers = signalingSettings.iceServers }; + ISignaling signaling = CreateSignaling(signalingSettings, SynchronizationContext.Current); _Run(conf, signaling, handlers.ToArray()); } diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs index f6cdaaa01..58ea3d00b 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs @@ -5,6 +5,6 @@ namespace Unity.RenderStreaming public class RenderStreamingSettings : ScriptableObject { [SerializeField] public bool automaticStreaming; - [SerializeField] public SignalingSettings signalingSettings; + [SerializeReference] public SignalingSettings signalingSettings; } } diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs index ddb682d97..696353b70 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs @@ -36,11 +36,9 @@ public class FurioosSignaling : ISignaling public string Url { get { return string.Empty; } } - public float Interval { get { return m_timeout; } } - - public FurioosSignaling(string url, float timeout, SynchronizationContext mainThreadContext) + public FurioosSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) { - m_timeout = timeout; + m_timeout = 5.0f; m_mainThreadContext = mainThreadContext; m_wsCloseEvent = new AutoResetEvent(false); } diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs index 472df9f5a..986127a27 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs @@ -19,17 +19,13 @@ public class HttpSignaling : ISignaling private string m_sessionId; private long m_lastTimeGetAllRequest; - private long m_lastTimeGetOfferRequest; - private long m_lastTimeGetAnswerRequest; - private long m_lastTimeGetCandidateRequest; public string Url { get { return m_url; } } - public float Interval { get { return m_timeout; } } - public HttpSignaling(string url, float timeout, SynchronizationContext mainThreadContext) + public HttpSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) { - m_url = url; - m_timeout = timeout; + m_url = signalingSettings.urlSignaling; + m_timeout = ((HttpSignalingSettings) signalingSettings).interval; m_mainThreadContext = mainThreadContext; if (m_url.StartsWith("https")) @@ -123,9 +119,6 @@ private void HTTPPolling() { // ignore messages arrived before 30 secs ago m_lastTimeGetAllRequest = DateTime.UtcNow.Millisecond - 30000; - m_lastTimeGetOfferRequest = DateTime.UtcNow.Millisecond - 30000; - m_lastTimeGetAnswerRequest = DateTime.UtcNow.Millisecond - 30000; - m_lastTimeGetCandidateRequest = DateTime.UtcNow.Millisecond - 30000; while (m_running && string.IsNullOrEmpty(m_sessionId)) { diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/ISignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/ISignaling.cs index 3b0abe2e6..5029c45c1 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/ISignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/ISignaling.cs @@ -23,8 +23,6 @@ public interface ISignaling string Url { get; } - float Interval { get; } - void OpenConnection(string connectionId); void CloseConnection(string connectionId); void SendOffer(string connectionId, RTCSessionDescription offer); diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs index 1ec7d522b..2bafa60c3 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs @@ -20,12 +20,10 @@ public class WebSocketSignaling : ISignaling public string Url { get { return m_url; } } - public float Interval { get { return m_timeout; } } - - public WebSocketSignaling(string url, float timeout, SynchronizationContext mainThreadContext) + public WebSocketSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) { - m_url = url; - m_timeout = timeout; + m_url = signalingSettings.urlSignaling; + m_timeout = 5.0f; m_mainThreadContext = mainThreadContext; m_wsCloseEvent = new AutoResetEvent(false); } diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs index 416b0f359..d6654ed6c 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs @@ -1,12 +1,28 @@ using System; +using Unity.RenderStreaming.Signaling; namespace Unity.RenderStreaming { public abstract class SignalingSettings { - public bool runOnAwake; public string urlSignaling = "http://127.0.0.1:80"; - public Unity.WebRTC.RTCIceServer[] iceServers; + public WebRTC.RTCIceServer[] iceServers; public abstract Type signalingClass { get; } } + + public class HttpSignalingSettings : SignalingSettings + { + public override Type signalingClass => typeof(HttpSignaling); + public float interval = 5.0f; + } + + public class WebSocketSignalingSettings : SignalingSettings + { + public override Type signalingClass => typeof(WebSocketSignaling); + } + + public class FurioosSignalingSettings : SignalingSettings + { + public override Type signalingClass => typeof(FurioosSignaling); + } } diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs index de461b626..b078f687b 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs @@ -67,23 +67,33 @@ public ISignaling Signaling switch (signalingType) { case SignalingType.Furioos: + { + var schema = signalingSecured ? "https" : "http"; + var settings = new FurioosSignalingSettings { - var schema = signalingSecured ? "https" : "http"; - return new FurioosSignaling( - $"{schema}://{signalingAddress}", signalingInterval, SynchronizationContext.Current); - } + urlSignaling = $"{schema}://{signalingAddress}" + }; + return new FurioosSignaling(settings, SynchronizationContext.Current); + } case SignalingType.WebSocket: + { + var schema = signalingSecured ? "wss" : "ws"; + var settings = new WebSocketSignalingSettings { - var schema = signalingSecured ? "wss" : "ws"; - return new WebSocketSignaling( - $"{schema}://{signalingAddress}", signalingInterval, SynchronizationContext.Current); - } + urlSignaling = $"{schema}://{signalingAddress}" + }; + return new WebSocketSignaling(settings, SynchronizationContext.Current); + } case SignalingType.Http: + { + var schema = signalingSecured ? "https" : "http"; + var settings = new HttpSignalingSettings { - var schema = signalingSecured ? "https" : "http"; - return new HttpSignaling( - $"{schema}://{signalingAddress}", signalingInterval, SynchronizationContext.Current); - } + urlSignaling = $"{schema}://{signalingAddress}", + interval = signalingInterval + }; + return new FurioosSignaling(settings, SynchronizationContext.Current); + } } throw new InvalidOperationException(); diff --git a/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs new file mode 100644 index 000000000..2f7ee583e --- /dev/null +++ b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs @@ -0,0 +1,13 @@ +using NUnit.Framework; + +namespace Unity.RenderStreaming.EditorTest +{ + public class RenderStreamingTest + { + [Test] + public void RenderStreamingSettings() + { + // todo: check RenderStreaming.GetSignalingSettings method that return correct settings from asset + } + } +} diff --git a/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs.meta b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs.meta new file mode 100644 index 000000000..bb2f1b371 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7107edca028c4640b99957dff2e7b6aa +timeCreated: 1674443070 \ No newline at end of file diff --git a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs index 1f1e18675..000f1a5da 100644 --- a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs @@ -132,12 +132,21 @@ ISignaling CreateSignaling(Type type, SynchronizationContext mainThread) { if (type == typeof(WebSocketSignaling)) { - return new WebSocketSignaling($"ws://localhost:{TestUtility.PortNumber}", 0.1f, mainThread); + var settings = new WebSocketSignalingSettings + { + urlSignaling = $"ws://localhost:{TestUtility.PortNumber}" + }; + return new WebSocketSignaling(settings, mainThread); } if (type == typeof(HttpSignaling)) { - return new HttpSignaling($"http://localhost:{TestUtility.PortNumber}", 0.1f, mainThread); + var settings = new HttpSignalingSettings + { + urlSignaling = $"http://localhost:{TestUtility.PortNumber}", + interval = 0.1f + }; + return new HttpSignaling(settings, mainThread); } if (type == typeof(MockSignaling)) @@ -508,11 +517,12 @@ public IEnumerator NotReceiveOwnOfferAnswer() Assert.That(connectionId1, Is.EqualTo(connectionId)); Assert.That(connectionId2, Is.EqualTo(connectionId)); + const float resendInterval = 0.1f; signaling1.OnOffer += (s, e) => { offerRaised1 = true; }; signaling2.OnOffer += (s, e) => { offerRaised2 = true; }; signaling1.SendOffer(connectionId1, m_DescOffer); // check each signaling invoke onOffer - yield return new WaitForSeconds(signaling1.Interval * 5); + yield return new WaitForSeconds(resendInterval * 5); Assert.That(offerRaised1, Is.False, () => "Receive own offer on private mode"); Assert.That(offerRaised2, Is.True); @@ -520,7 +530,7 @@ public IEnumerator NotReceiveOwnOfferAnswer() signaling2.OnAnswer += (s, e) => { answerRaised2 = true; }; signaling2.SendAnswer(connectionId1, m_DescAnswer); // check each signaling invoke onAnswer - yield return new WaitForSeconds(signaling2.Interval * 5); + yield return new WaitForSeconds(resendInterval * 5); Assert.That(answerRaised1, Is.True); Assert.That(answerRaised2, Is.False, () => "Receive own answer on private mode"); diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs index c9e4b0294..79c91eb8e 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs @@ -60,8 +60,7 @@ public void ThrowExceptionIfHandlerIsNullOrEmpty() ISignaling mock = new MockSignaling(); component.runOnAwake = false; component.gameObject.SetActive(true); - Assert.That(() => component.Run(signaling: mock), - Throws.InvalidOperationException); + Assert.That(() => component.Run(signaling: mock), Throws.InvalidOperationException); var handlers = new SignalingHandlerBase[] {}; Assert.That(() => component.Run(signaling: mock, handlers:handlers), @@ -83,5 +82,31 @@ public IEnumerator RunAgain() yield return 0; component.Run(signaling:mock, handlers:handlers); } + + [Test] + public void AddAndRemoveSignalingSettings() + { + component.runOnAwake = false; + component.gameObject.SetActive(true); + component.SetSignalingSettings(new MockSignalingSettings()); + var handler = component.gameObject.AddComponent(); + component.AddSignalingHandler(handler); + Assert.That(() => component.Run(), Throws.Nothing); + component.RemoveSignalingHandler(handler); + } + + [Test] + public void ThrowExceptionSetSignalingOnRunning() + { + component.runOnAwake = false; + component.gameObject.SetActive(true); + component.SetSignalingSettings(new MockSignalingSettings()); + var handler = component.gameObject.AddComponent(); + component.AddSignalingHandler(handler); + Assert.That(() => component.Run(), Throws.Nothing); + Assert.That(component.Running, Is.True); + + Assert.That(() => component.SetSignalingSettings(new MockSignalingSettings()), Throws.InvalidOperationException); + } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs index ba8380987..b8538a0ef 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs @@ -1,15 +1,22 @@ -using System.Collections; using NUnit.Framework; -using Unity.RenderStreaming.RuntimeTest.Signaling; -using Unity.RenderStreaming.Signaling; using UnityEngine; -using UnityEngine.TestTools; -using Object = UnityEngine.Object; namespace Unity.RenderStreaming.RuntimeTest { class RenderStreamingTest { - // todo + [Test] + public void AutomaticStreaming() + { + RenderStreaming.AutomaticStreaming = true; + + var automaticStreaming = Object.FindObjectOfType(); + Assert.That(automaticStreaming, Is.Not.Null); + + RenderStreaming.AutomaticStreaming = false; + + automaticStreaming = Object.FindObjectOfType(); + Assert.That(automaticStreaming, Is.Null); + } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignaling.cs b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignaling.cs index f9e1dd7e4..19874b11a 100644 --- a/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignaling.cs +++ b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignaling.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Unity.RenderStreaming.Signaling; using Unity.WebRTC; @@ -224,6 +225,10 @@ static MockSignaling() manager = new MockPublicSignalingManager(); } + public MockSignaling(SignalingSettings settings = null, SynchronizationContext context = null) + { + } + public void Start() { manager.Add(this); diff --git a/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs new file mode 100644 index 000000000..f2c512d90 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs @@ -0,0 +1,9 @@ +using System; + +namespace Unity.RenderStreaming.RuntimeTest.Signaling +{ + internal class MockSignalingSettings : SignalingSettings + { + public override Type signalingClass => typeof(MockSignaling); + } +} diff --git a/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs.meta b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs.meta new file mode 100644 index 000000000..92d0418f1 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 957479287b0c4578bf22c6d3f8628642 +timeCreated: 1674444139 \ No newline at end of file diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs index 84f6f9a69..d4e7ab0eb 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs @@ -118,12 +118,21 @@ ISignaling CreateSignaling(Type type, SynchronizationContext mainThread) { if (type == typeof(WebSocketSignaling)) { - return new WebSocketSignaling($"ws://localhost:{TestUtility.PortNumber}", 0.1f, mainThread); + var settings = new WebSocketSignalingSettings + { + urlSignaling = $"ws://localhost:{TestUtility.PortNumber}" + }; + return new WebSocketSignaling(settings, mainThread); } if (type == typeof(HttpSignaling)) { - return new HttpSignaling($"http://localhost:{TestUtility.PortNumber}", 0.1f, mainThread); + var settings = new HttpSignalingSettings + { + urlSignaling = $"http://localhost:{TestUtility.PortNumber}", + interval = 0.1f + }; + return new HttpSignaling(settings, mainThread); } if (type == typeof(MockSignaling)) From 8a70507a731185af110e27f78c579601926cacff Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 26 Jan 2023 14:16:22 +0900 Subject: [PATCH 033/117] feat: Define IceServer class to control signaling settings with APIs (#848) * fix (cherry picked from commit 72834442a85fd3952bf2751cd1a47dd1dabd017d) * fix * fix * fix * fix * workaround * workaround * workaround --- .../Runtime/Scripts/RenderStreaming.cs | 11 +- .../Runtime/Scripts/RenderStreamingHandler.cs | 25 ++- .../Scripts/Signaling/HttpSignaling.cs | 2 +- .../Scripts/Signaling/WebSocketSignaling.cs | 2 +- .../Runtime/Scripts/SignalingSettings.cs | 143 +++++++++++++++++- .../Samples~/Example/Scripts/SceneSelectUI.cs | 20 +-- .../Tests/Runtime/PrivateSignalingTest.cs | 14 +- .../Tests/Runtime/SignalingTest.cs | 14 +- .../Tests/Runtime/StreamingComponentTest.cs | 2 + 9 files changed, 184 insertions(+), 49 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs index 05fc0a7f3..6853e910e 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs @@ -36,14 +36,13 @@ static RenderStreaming() // todo: load from assets var settings = ScriptableObject.CreateInstance(); settings.automaticStreaming = false; - var signalingSettings = new WebSocketSignalingSettings - { - urlSignaling = "ws://127.0.0.1:80", - iceServers = new RTCIceServer[] + var signalingSettings = new WebSocketSignalingSettings( + url: "ws://127.0.0.1:80", + iceServers: new[] { - new RTCIceServer() {urls = new string[] {"stun:stun.l.google.com:19302"}} + new IceServer(urls: new[] {"stun:stun.l.google.com:19302"}) } - }; + ); settings.signalingSettings = signalingSettings; s_settings = settings; } diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs index 1f7ef73d5..cc26fe057 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs @@ -17,14 +17,14 @@ public sealed class RenderStreamingHandler : MonoBehaviour { #pragma warning disable 0649 // ToDo: Create component UI on URS-553 - [SerializeReference] private SignalingSettings signalingSettings = new WebSocketSignalingSettings - { - urlSignaling = "ws://127.0.0.1:80", - iceServers = new RTCIceServer[] + [SerializeReference] + private SignalingSettings signalingSettings = new WebSocketSignalingSettings( + url: "ws://127.0.0.1:80", + iceServers: new IceServer[] { - new RTCIceServer() {urls = new string[] {"stun:stun.l.google.com:19302"}} + new IceServer(urls: new[] {"stun:stun.l.google.com:19302"}) } - }; + ); [SerializeField, Tooltip("List of handlers of signaling process.")] private List handlers = new List(); @@ -143,13 +143,9 @@ private void _Run( SignalingHandlerBase[] handlers = null ) { + RTCIceServer[] iceServers = signalingSettings.iceServers.OfType().ToArray(); RTCConfiguration _conf = - conf.GetValueOrDefault(new RTCConfiguration { iceServers = signalingSettings.iceServers }); - - if (signaling != null) - { - signalingSettings.urlSignaling = signaling.Url; - } + conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers }); ISignaling _signaling = signaling ?? CreateSignaling(signalingSettings, SynchronizationContext.Current); RenderStreamingDependencies dependencies = new RenderStreamingDependencies @@ -187,10 +183,11 @@ public void Stop() void Awake() { - if (!runOnAwake || m_running || handlers.Count == 0) + if (!runOnAwake || m_running || handlers.Count == 0) return; - RTCConfiguration conf = new RTCConfiguration { iceServers = signalingSettings.iceServers }; + RTCIceServer[] iceServers = signalingSettings.iceServers.Cast().ToArray(); + RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers }; ISignaling signaling = CreateSignaling(signalingSettings, SynchronizationContext.Current); _Run(conf, signaling, handlers.ToArray()); } diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs index 986127a27..2992e23db 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs @@ -24,7 +24,7 @@ public class HttpSignaling : ISignaling public HttpSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) { - m_url = signalingSettings.urlSignaling; + m_url = signalingSettings.url; m_timeout = ((HttpSignalingSettings) signalingSettings).interval; m_mainThreadContext = mainThreadContext; diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs index 2bafa60c3..1eca6630c 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs @@ -22,7 +22,7 @@ public class WebSocketSignaling : ISignaling public WebSocketSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) { - m_url = signalingSettings.urlSignaling; + m_url = signalingSettings.url; m_timeout = 5.0f; m_mainThreadContext = mainThreadContext; m_wsCloseEvent = new AutoResetEvent(false); diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs index d6654ed6c..824c1b955 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs @@ -1,28 +1,165 @@ using System; +using System.Linq; using Unity.RenderStreaming.Signaling; +using Unity.WebRTC; +using UnityEngine; +using UnityEngine.InputSystem.Utilities; namespace Unity.RenderStreaming { + /// + /// + /// + public enum IceCredentialType + { + /// + /// + /// + Password = 0, + + /// + /// + /// + OAuth = 1 + } + + /// + /// + /// + [Serializable] + public class IceServer + { + /// + /// + /// + public ReadOnlyArray urls => m_urls; + + /// + /// + /// + public string username => m_username; + + /// + /// + /// + public IceCredentialType credentialType => m_credentialType; + + /// + /// + /// + public string credential => m_credential; + + [SerializeField] + private string[] m_urls; + [SerializeField] + private string m_username; + [SerializeField] + private IceCredentialType m_credentialType; + [SerializeField] + private string m_credential; + + /// + /// + /// + /// + public static implicit operator RTCIceServer(IceServer server) + { + var iceServer = new RTCIceServer + { + urls = server.urls.ToArray(), + username = server.username, + credential = server.credential, + credentialType = (RTCIceCredentialType)server.credentialType + }; + return iceServer; + } + + public IceServer Clone() + { + return new IceServer(this.urls.ToArray(), this.username, this.credentialType, this.credential); + } + + public IceServer(string[] urls = null, string username = null, IceCredentialType credentialType = IceCredentialType.Password, string credential = null) + { + m_urls = urls?.ToArray(); + m_username = username; + m_credential = credential; + m_credentialType = credentialType; + } + + internal IceServer(RTCIceServer server) + { + m_urls = server.urls.ToArray(); + m_username = server.username; + m_credential = server.credential; + m_credentialType = (IceCredentialType)server.credentialType; + } + } + + /// + /// + /// public abstract class SignalingSettings { - public string urlSignaling = "http://127.0.0.1:80"; - public WebRTC.RTCIceServer[] iceServers; + /// + /// + /// + public string url => m_url; + + /// + /// + /// + public ReadOnlyArray iceServers => m_iceServers; + + /// + /// + /// public abstract Type signalingClass { get; } + + [SerializeField] + protected string m_url = "http://127.0.0.1:80"; + [SerializeField] + protected IceServer[] m_iceServers; } + [Serializable] public class HttpSignalingSettings : SignalingSettings { public override Type signalingClass => typeof(HttpSignaling); - public float interval = 5.0f; + public float interval => m_interval; + + [SerializeField] + private float m_interval; + + public HttpSignalingSettings(string url, IceServer[] iceServers = null, float interval = 5.0f) + { + m_url = url; + m_iceServers = iceServers?.Select(server => server.Clone()).ToArray(); + m_interval = interval; + } } + [Serializable] public class WebSocketSignalingSettings : SignalingSettings { public override Type signalingClass => typeof(WebSocketSignaling); + + public WebSocketSignalingSettings(string url, IceServer[] iceServers = null) + { + m_url = url; + m_iceServers = iceServers?.Select(server => server.Clone()).ToArray(); + } } + [Serializable] public class FurioosSignalingSettings : SignalingSettings { public override Type signalingClass => typeof(FurioosSignaling); + + public FurioosSignalingSettings(string url, IceServer[] iceServers = null) + { + m_url = url; + m_iceServers = iceServers?.Select(server => server.Clone()).ToArray(); + } } } diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs index b078f687b..739ddb6e5 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs @@ -70,28 +70,28 @@ public ISignaling Signaling { var schema = signalingSecured ? "https" : "http"; var settings = new FurioosSignalingSettings - { - urlSignaling = $"{schema}://{signalingAddress}" - }; + ( + url: $"{schema}://{signalingAddress}" + ); return new FurioosSignaling(settings, SynchronizationContext.Current); } case SignalingType.WebSocket: { var schema = signalingSecured ? "wss" : "ws"; var settings = new WebSocketSignalingSettings - { - urlSignaling = $"{schema}://{signalingAddress}" - }; + ( + url: $"{schema}://{signalingAddress}" + ); return new WebSocketSignaling(settings, SynchronizationContext.Current); } case SignalingType.Http: { var schema = signalingSecured ? "https" : "http"; var settings = new HttpSignalingSettings - { - urlSignaling = $"{schema}://{signalingAddress}", - interval = signalingInterval - }; + ( + url: $"{schema}://{signalingAddress}", + interval: signalingInterval + ); return new FurioosSignaling(settings, SynchronizationContext.Current); } } diff --git a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs index 000f1a5da..1dfad8a4d 100644 --- a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs @@ -133,19 +133,19 @@ ISignaling CreateSignaling(Type type, SynchronizationContext mainThread) if (type == typeof(WebSocketSignaling)) { var settings = new WebSocketSignalingSettings - { - urlSignaling = $"ws://localhost:{TestUtility.PortNumber}" - }; + ( + url: $"ws://localhost:{TestUtility.PortNumber}" + ); return new WebSocketSignaling(settings, mainThread); } if (type == typeof(HttpSignaling)) { var settings = new HttpSignalingSettings - { - urlSignaling = $"http://localhost:{TestUtility.PortNumber}", - interval = 0.1f - }; + ( + url: $"http://localhost:{TestUtility.PortNumber}", + interval: 0.1f + ); return new HttpSignaling(settings, mainThread); } diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs index d4e7ab0eb..e9d465908 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs @@ -119,19 +119,19 @@ ISignaling CreateSignaling(Type type, SynchronizationContext mainThread) if (type == typeof(WebSocketSignaling)) { var settings = new WebSocketSignalingSettings - { - urlSignaling = $"ws://localhost:{TestUtility.PortNumber}" - }; + ( + url: $"ws://localhost:{TestUtility.PortNumber}" + ); return new WebSocketSignaling(settings, mainThread); } if (type == typeof(HttpSignaling)) { var settings = new HttpSignalingSettings - { - urlSignaling = $"http://localhost:{TestUtility.PortNumber}", - interval = 0.1f - }; + ( + url: $"http://localhost:{TestUtility.PortNumber}", + interval: 0.1f + ); return new HttpSignaling(settings, mainThread); } diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index 4da868a47..cf5642177 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -367,7 +367,9 @@ public IEnumerator CreateTrack() UnityEngine.Object.DestroyImmediate(go3); } + // workaround(kazuki): Fix NullReferenceException in AudioStreamTrack.ProcessAudio. [UnityTest] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer })] public IEnumerator ReplaceTrack() { var go = new GameObject(); From 9ab26c4b9cd08c63852824688d8acb988774e844 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Fri, 27 Jan 2023 15:55:52 +0900 Subject: [PATCH 034/117] feat: Custom signaling settings editor (#847) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * custom signaling settings editor * ISignalingSettingEditor * add test * wip * wip * fix * fix * refactor * fix * wip * fix * fix * fix * fix Co-authored-by: kazuki --- .../Editor/CustomSignalingSettingsEditor.cs | 98 +++++++++ .../CustomSignalingSettingsEditor.cs.meta | 3 + .../SignalingSettingsDrawer.cs | 146 ++++++++++++++ .../SignalingSettingsDrawer.cs.meta | 11 + .../Editor/RenderStreamingHandlerEditor.cs | 60 +----- .../Editor/ReorderableListField.cs | 32 +++ .../Editor/ReorderableListField.cs.meta | 11 + .../Runtime/Scripts/RenderStreamingHandler.cs | 19 +- .../Scripts/Signaling/HttpSignaling.cs | 10 +- .../Scripts/Signaling/WebSocketSignaling.cs | 6 +- .../Runtime/Scripts/SignalingSettings.cs | 190 +++++++++++++++--- .../Tests/Editor/EditorTest.cs | 1 - .../Tests/Editor/EditorUITest.cs | 63 ++++++ .../Tests/Editor/EditorUITest.cs.meta | 11 + .../Runtime/RenderStreamingHandlerTest.cs | 14 +- .../Signaling/MockSignalingSettings.cs | 3 + .../Tests/Runtime/SignalingSettingsTest.cs | 72 +++++++ .../Runtime/SignalingSettingsTest.cs.meta | 11 + 18 files changed, 668 insertions(+), 93 deletions(-) create mode 100644 com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs create mode 100644 com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs.meta create mode 100644 com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs create mode 100644 com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs.meta create mode 100644 com.unity.renderstreaming/Editor/ReorderableListField.cs create mode 100644 com.unity.renderstreaming/Editor/ReorderableListField.cs.meta create mode 100644 com.unity.renderstreaming/Tests/Editor/EditorUITest.cs create mode 100644 com.unity.renderstreaming/Tests/Editor/EditorUITest.cs.meta create mode 100644 com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs create mode 100644 com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs.meta diff --git a/com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs b/com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs new file mode 100644 index 000000000..1d3ff7696 --- /dev/null +++ b/com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace Unity.RenderStreaming.Editor +{ + /// + /// + /// + public class CustomSignalingSettingsEditor : Attribute + { + private static readonly TypeCache.TypeCollection inspectorTypes = + TypeCache.GetTypesWithAttribute(); + + private readonly Type inspectedType; + private readonly string label; + + /// + /// + /// + /// + /// + public CustomSignalingSettingsEditor(Type inspectedType, string label) + { + if (inspectedType == null) + Debug.LogError("Failed to load CustomEditor inspected type"); + this.inspectedType = inspectedType; + this.label = label; + } + + internal static Type FindInspectorTypeByInspectedType(Type inspectedType) + { + foreach (var type in inspectorTypes) + { + foreach (CustomSignalingSettingsEditor custom in + type.GetCustomAttributes(typeof(CustomSignalingSettingsEditor), false)) + { + if (custom.inspectedType == inspectedType) + { + return type; + } + } + } + return null; + } + + internal static Type FindInspectedTypeByLabel(string label) + { + foreach (var type in inspectorTypes) + { + foreach (CustomSignalingSettingsEditor custom in + type.GetCustomAttributes(typeof(CustomSignalingSettingsEditor), false)) + { + if (custom.label == label) + { + return custom.inspectedType; + } + } + } + return null; + } + + internal static string FindLabelByInspectedType(Type inspectedType) + { + foreach (var type in inspectorTypes) + { + foreach (CustomSignalingSettingsEditor custom in + type.GetCustomAttributes(typeof(CustomSignalingSettingsEditor), false)) + { + if (custom.inspectedType == inspectedType) + { + return custom.label; + } + } + } + return null; + } + + internal static string FindLabelByInspectorType(Type inspectorType) + { + var attributes = + inspectorType.GetCustomAttributes(typeof(CustomSignalingSettingsEditor), false); + foreach (var attribute in attributes) + { + if (attribute is CustomSignalingSettingsEditor custom) + return custom.label; + } + return null; + } + + internal static IEnumerable Labels() + { + return inspectorTypes.Select(FindLabelByInspectorType); + } + } +} diff --git a/com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs.meta b/com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs.meta new file mode 100644 index 000000000..a7dca4709 --- /dev/null +++ b/com.unity.renderstreaming/Editor/CustomSignalingSettingsEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7750002ca172433e8ac04d3d8e3bb251 +timeCreated: 1674544311 \ No newline at end of file diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs new file mode 100644 index 000000000..d9827d9de --- /dev/null +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Unity.RenderStreaming.Editor +{ + [CustomPropertyDrawer(typeof(SignalingSettingsAttribute))] + class SignalingSettingsDrawer : PropertyDrawer + { + private VisualElement editorGUI; + private ISignalingSettingEditor editor; + private Dictionary table = new Dictionary(); + + public override VisualElement CreatePropertyGUI(SerializedProperty property) + { + editor = CreateEditor(property); + var root = new VisualElement(); + editorGUI = editor.CreateInspectorGUI(property); + + root.Add(CreatePopUpSignalingType(property, "Signaling Type")); + root.Add(editorGUI); + return root; + } + + ISignalingSettingEditor CreateEditor(SerializedProperty property) + { + var handler = property.serializedObject.targetObject as RenderStreamingHandler; + var settings = handler.GetSignalingSettings(); + var type = CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(settings.GetType()); + return Activator.CreateInstance(type) as ISignalingSettingEditor; + } + + VisualElement CreatePopUpSignalingType(SerializedProperty property, string label) + { + var handler = property.serializedObject.targetObject as RenderStreamingHandler; + var settings = handler.GetSignalingSettings(); + var defaultValue = CustomSignalingSettingsEditor.FindLabelByInspectedType(settings.GetType()); + var choices = CustomSignalingSettingsEditor.Labels().ToList(); + var element = new PopupField(label: label, choices: choices, defaultValue: defaultValue); + element.RegisterValueChangedCallback(e => OnChangedValue(e, property)); + return element; + } + + void OnChangedValue(ChangeEvent e, SerializedProperty property) + { + var handler = property.serializedObject.targetObject as RenderStreamingHandler; + if (handler == null) + return; + + // cache current settings. + var settings = handler.GetSignalingSettings(); + var type = settings.GetType(); + table[type] = settings; + + var label = e.newValue; + var inspectedType = CustomSignalingSettingsEditor.FindInspectedTypeByLabel(label); + if (!table.ContainsKey(inspectedType)) + { + var newSettings = Activator.CreateInstance(inspectedType) as SignalingSettings; + table.Add(inspectedType, newSettings); + } + property.managedReferenceValue = table[inspectedType]; + property.serializedObject.ApplyModifiedProperties(); + + var inspectorType = CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(inspectedType); + var editor = Activator.CreateInstance(inspectorType) as ISignalingSettingEditor; + var newValue = editor.CreateInspectorGUI(property); + ReplaceElement(editorGUI, newValue); + editorGUI = newValue; + + // bind new serializedObject. + editorGUI.Bind(property.serializedObject); + } + + static void ReplaceElement(VisualElement oldValue, VisualElement newValue) + { + var root = oldValue.parent; + var index = root.IndexOf(oldValue); + root.Remove(oldValue); + root.Insert(index, newValue); + } + } + + /// + /// + /// + public interface ISignalingSettingEditor + { + VisualElement CreateInspectorGUI(SerializedProperty property); + } + + [CustomSignalingSettingsEditor(typeof(HttpSignalingSettings), "HTTP Polling")] + internal class HttpSignalingSettingsEditor : ISignalingSettingEditor + { + public VisualElement CreateInspectorGUI(SerializedProperty property) + { + VisualElement root = new VisualElement(); + root.Add(new PropertyField(property.FindPropertyRelative("m_url"), "URL")); + root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); + root.Add(new PropertyField(property.FindPropertyRelative("m_interval"), "Polling Interval")); + return root; + } + + public void SetSignalingSettings(SignalingSettings settings) + { + throw new System.NotImplementedException(); + } + } + + [CustomSignalingSettingsEditor(typeof(WebSocketSignalingSettings), "WebSocket")] + internal class WebSocketSignalingSettingsEditor : ISignalingSettingEditor + { + public VisualElement CreateInspectorGUI(SerializedProperty property) + { + VisualElement root = new VisualElement(); + root.Add(new PropertyField(property.FindPropertyRelative("m_url"), "URL")); + root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); + return root; + } + + public void SetSignalingSettings(SignalingSettings settings) + { + throw new System.NotImplementedException(); + } + } + + [CustomSignalingSettingsEditor(typeof(FurioosSignalingSettings), "Furioos")] + internal class FurioosSignalingSettingsEditor : ISignalingSettingEditor + { + public VisualElement CreateInspectorGUI(SerializedProperty property) + { + VisualElement root = new VisualElement(); + root.Add(new PropertyField(property.FindPropertyRelative("m_url"), "URL")); + root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); + return root; + } + + public void SetSignalingSettings(SignalingSettings settings) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs.meta b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs.meta new file mode 100644 index 000000000..459fdc4bd --- /dev/null +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9553c77a45a7dbe43a8d1c0471b5b819 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs b/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs index b10bda86e..75362efb0 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs @@ -1,8 +1,6 @@ using UnityEditor; -using UnityEngine; -using Unity.RenderStreaming.Signaling; -using System.Collections.Generic; -using System.Linq; +using UnityEditor.UIElements; +using UnityEngine.UIElements; namespace Unity.RenderStreaming.Editor { @@ -12,55 +10,13 @@ namespace Unity.RenderStreaming.Editor [CustomEditor(typeof(RenderStreamingHandler))] internal class RenderStreamingHandlerEditor : UnityEditor.Editor { - public override void OnInspectorGUI() + public override VisualElement CreateInspectorGUI() { - // ToDo: Create component UI on URS-553 - base.OnInspectorGUI(); - // { - // serializedObject.Update(); - // ShowSignalingTypes(serializedObject.FindProperty("signalingType")); - // EditorGUILayout.PropertyField(serializedObject.FindProperty("urlSignaling"), new GUIContent("Signaling URL")); - // ShowIceServerList(serializedObject.FindProperty("iceServers")); - // EditorGUILayout.PropertyField(serializedObject.FindProperty("interval")); - // EditorGUILayout.PropertyField(serializedObject.FindProperty("handlers")); - // EditorGUILayout.PropertyField(serializedObject.FindProperty("runOnAwake")); - // - // serializedObject.ApplyModifiedProperties(); - // } - } - - static void ShowIceServerList(SerializedProperty list) - { - EditorGUILayout.PropertyField(list.FindPropertyRelative("Array.size"), new GUIContent(list.displayName)); - EditorGUI.indentLevel += 1; - for (int i = 0; i < list.arraySize; i++) - { - var element = list.GetArrayElementAtIndex(i); - var label = "Ice server [" + i + "]"; - EditorGUILayout.PropertyField(element, new GUIContent(label), false); - if (element.isExpanded) - { - EditorGUI.indentLevel += 1; - EditorGUILayout.PropertyField(element.FindPropertyRelative("urls"), true); - EditorGUILayout.PropertyField(element.FindPropertyRelative("username")); - EditorGUILayout.PropertyField(element.FindPropertyRelative("credential")); - EditorGUILayout.PropertyField(element.FindPropertyRelative("credentialType")); - EditorGUI.indentLevel -= 1; - } - } - EditorGUI.indentLevel -= 1; - } - - static readonly IReadOnlyList relevantISygnalingTypes = - TypeCache.GetTypesDerivedFrom().Where(t => t.IsVisible && t.IsClass).ToList(); - static readonly string[] options = relevantISygnalingTypes.Select(t => t.Name).ToArray(); - static readonly string[] types = relevantISygnalingTypes.Select(t => t.FullName).ToArray(); - - static void ShowSignalingTypes(SerializedProperty signalingType) - { - int selected = Mathf.Max(0, System.Array.IndexOf(types, signalingType.stringValue)); - selected = EditorGUILayout.Popup("Signaling Type", selected, options); - signalingType.stringValue = types[selected]; + var root = new VisualElement(); + root.Add(new PropertyField(serializedObject.FindProperty("signalingSettings"), "Signaling Settings")); + root.Add(new ReorderableListField(serializedObject.FindProperty("handlers"), "Signaling Handler List")); + root.Add(new PropertyField(serializedObject.FindProperty("runOnAwake"), "Run On Awake")); + return root; } } } diff --git a/com.unity.renderstreaming/Editor/ReorderableListField.cs b/com.unity.renderstreaming/Editor/ReorderableListField.cs new file mode 100644 index 000000000..8185a329e --- /dev/null +++ b/com.unity.renderstreaming/Editor/ReorderableListField.cs @@ -0,0 +1,32 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Unity.RenderStreaming.Editor +{ + // todo(kazuki): workaround. + // ListView.reorderMode is not supported on Unity 2020.3. + + internal class ReorderableListField : IMGUIContainer + { + private readonly ReorderableList reorderable; + + public ReorderableListField(SerializedProperty property, string label) + { + reorderable = new ReorderableList(property.serializedObject, property) + { + drawElementCallback = (rect, index, isActive, isFocused) => EditorGUI.PropertyField(rect, property.GetArrayElementAtIndex(index)), + drawHeaderCallback = rect => EditorGUI.LabelField(rect, label) + }; + onGUIHandler = OnGUIHandler; + } + + void OnGUIHandler() + { + reorderable.DoLayoutList(); + } + } +} diff --git a/com.unity.renderstreaming/Editor/ReorderableListField.cs.meta b/com.unity.renderstreaming/Editor/ReorderableListField.cs.meta new file mode 100644 index 000000000..569d447e0 --- /dev/null +++ b/com.unity.renderstreaming/Editor/ReorderableListField.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e862df8b1759e041ace436210140fbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs index cc26fe057..e357b8623 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs @@ -17,14 +17,15 @@ public sealed class RenderStreamingHandler : MonoBehaviour { #pragma warning disable 0649 // ToDo: Create component UI on URS-553 - [SerializeReference] - private SignalingSettings signalingSettings = new WebSocketSignalingSettings( + [SerializeReference, SignalingSettings] + private SignalingSettings signalingSettings = new WebSocketSignalingSettings + ( url: "ws://127.0.0.1:80", - iceServers: new IceServer[] + iceServers: new[] { new IceServer(urls: new[] {"stun:stun.l.google.com:19302"}) } - ); + ); [SerializeField, Tooltip("List of handlers of signaling process.")] private List handlers = new List(); @@ -60,13 +61,19 @@ static ISignaling CreateSignaling(SignalingSettings settings, SynchronizationCon public void SetSignalingSettings(SignalingSettings settings) { if (m_running) - { throw new InvalidOperationException("The Signaling process has already started."); - } + + if (settings == null) + throw new ArgumentNullException("settings"); signalingSettings = settings; } + public SignalingSettings GetSignalingSettings() + { + return signalingSettings; + } + /// /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs index 2992e23db..09d4b9507 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Threading; using Unity.WebRTC; @@ -24,8 +22,12 @@ public class HttpSignaling : ISignaling public HttpSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) { - m_url = signalingSettings.url; - m_timeout = ((HttpSignalingSettings) signalingSettings).interval; + if (signalingSettings == null) + throw new ArgumentNullException(nameof(signalingSettings)); + if (!(signalingSettings is HttpSignalingSettings settings)) + throw new ArgumentException("signalingSettings is not HttpSignalingSettings"); + m_url = settings.url; + m_timeout = settings.interval; m_mainThreadContext = mainThreadContext; if (m_url.StartsWith("https")) diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs index 1eca6630c..249608433 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignaling.cs @@ -22,7 +22,11 @@ public class WebSocketSignaling : ISignaling public WebSocketSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) { - m_url = signalingSettings.url; + if(signalingSettings == null) + throw new ArgumentNullException(nameof(signalingSettings)); + if(!(signalingSettings is WebSocketSignalingSettings settings)) + throw new ArgumentException("signalingSettings is not WebSocketSignalingSettings"); + m_url = settings.url; m_timeout = 5.0f; m_mainThreadContext = mainThreadContext; m_wsCloseEvent = new AutoResetEvent(false); diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs index 824c1b955..67dee5625 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs @@ -1,51 +1,53 @@ using System; +using System.Collections.Generic; using System.Linq; using Unity.RenderStreaming.Signaling; using Unity.WebRTC; using UnityEngine; -using UnityEngine.InputSystem.Utilities; namespace Unity.RenderStreaming { + internal sealed class SignalingSettingsAttribute : PropertyAttribute { } + /// - /// + /// /// public enum IceCredentialType { /// - /// + /// /// Password = 0, /// - /// + /// /// OAuth = 1 } /// - /// + /// /// [Serializable] public class IceServer { /// - /// + /// /// - public ReadOnlyArray urls => m_urls; + public IReadOnlyCollection urls => m_urls; /// - /// + /// /// public string username => m_username; /// - /// + /// /// public IceCredentialType credentialType => m_credentialType; /// - /// + /// /// public string credential => m_credential; @@ -59,7 +61,7 @@ public class IceServer private string m_credential; /// - /// + /// /// /// public static implicit operator RTCIceServer(IceServer server) @@ -74,11 +76,22 @@ public static implicit operator RTCIceServer(IceServer server) return iceServer; } + /// + /// + /// + /// public IceServer Clone() { return new IceServer(this.urls.ToArray(), this.username, this.credentialType, this.credential); } + /// + /// + /// + /// + /// + /// + /// public IceServer(string[] urls = null, string username = null, IceCredentialType credentialType = IceCredentialType.Password, string credential = null) { m_urls = urls?.ToArray(); @@ -97,69 +110,190 @@ internal IceServer(RTCIceServer server) } /// - /// + /// /// public abstract class SignalingSettings { /// - /// - /// - public string url => m_url; - - /// - /// + /// /// - public ReadOnlyArray iceServers => m_iceServers; + public abstract IReadOnlyCollection iceServers { get; } /// - /// + /// /// public abstract Type signalingClass { get; } - - [SerializeField] - protected string m_url = "http://127.0.0.1:80"; - [SerializeField] - protected IceServer[] m_iceServers; } [Serializable] public class HttpSignalingSettings : SignalingSettings { + /// + /// + /// public override Type signalingClass => typeof(HttpSignaling); + + /// + /// + /// + public override IReadOnlyCollection iceServers => m_iceServers; + + /// + /// + /// + public string url => m_url; + + /// + /// + /// public float interval => m_interval; [SerializeField] private float m_interval; + [SerializeField] + protected string m_url; + [SerializeField] + protected IceServer[] m_iceServers; + /// + /// + /// + /// + /// + /// public HttpSignalingSettings(string url, IceServer[] iceServers = null, float interval = 5.0f) { + if (url == null) + throw new ArgumentNullException("url"); + if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) + throw new ArgumentException("url is not well formed Uri"); + m_url = url; - m_iceServers = iceServers?.Select(server => server.Clone()).ToArray(); + m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); m_interval = interval; } + + /// + /// + /// + public HttpSignalingSettings() + { + m_url = "http://127.0.0.1"; + m_iceServers = new[] + { + new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) + }; + m_interval = 5f; + } } + /// + /// + /// [Serializable] public class WebSocketSignalingSettings : SignalingSettings { + /// + /// + /// public override Type signalingClass => typeof(WebSocketSignaling); + /// + /// + /// + public override IReadOnlyCollection iceServers => m_iceServers; + + /// + /// + /// + public string url => m_url; + + [SerializeField] + protected string m_url; + [SerializeField] + protected IceServer[] m_iceServers; + + /// + /// + /// + /// + /// public WebSocketSignalingSettings(string url, IceServer[] iceServers = null) { + if (url == null) + throw new ArgumentNullException("url"); + if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) + throw new ArgumentException("url is not well formed Uri"); + m_url = url; - m_iceServers = iceServers?.Select(server => server.Clone()).ToArray(); + m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); + } + + /// + /// + /// + public WebSocketSignalingSettings() + { + m_url = "ws://127.0.0.1"; + m_iceServers = new[] + { + new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) + }; } } + /// + /// + /// [Serializable] public class FurioosSignalingSettings : SignalingSettings { + /// + /// + /// public override Type signalingClass => typeof(FurioosSignaling); + /// + /// + /// + public override IReadOnlyCollection iceServers => m_iceServers; + + /// + /// + /// + public string url => m_url; + + [SerializeField] + protected string m_url; + [SerializeField] + protected IceServer[] m_iceServers; + + /// + /// + /// + /// + /// public FurioosSignalingSettings(string url, IceServer[] iceServers = null) { + if (url == null) + throw new ArgumentNullException("url"); + if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) + throw new ArgumentException("url is not well formed Uri"); + m_url = url; - m_iceServers = iceServers?.Select(server => server.Clone()).ToArray(); + m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); + } + + /// + /// + /// + public FurioosSignalingSettings() + { + m_url = "http://127.0.0.1"; + m_iceServers = new[] + { + new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) + }; } } } diff --git a/com.unity.renderstreaming/Tests/Editor/EditorTest.cs b/com.unity.renderstreaming/Tests/Editor/EditorTest.cs index 2822e2e90..838649173 100644 --- a/com.unity.renderstreaming/Tests/Editor/EditorTest.cs +++ b/com.unity.renderstreaming/Tests/Editor/EditorTest.cs @@ -163,6 +163,5 @@ public void SerializeAudioCodecInfo() Assert.That(asset.info.Equals(otherAsset.info), Is.True); AssetDatabase.DeleteAsset(exportPath); } - } } diff --git a/com.unity.renderstreaming/Tests/Editor/EditorUITest.cs b/com.unity.renderstreaming/Tests/Editor/EditorUITest.cs new file mode 100644 index 000000000..a3e9bfe9a --- /dev/null +++ b/com.unity.renderstreaming/Tests/Editor/EditorUITest.cs @@ -0,0 +1,63 @@ +using System; +using NUnit.Framework; +using Unity.RenderStreaming.Editor; + +namespace Unity.RenderStreaming.EditorTest +{ + + + class CustomSignalingSettingsEditorTest + { + [Test] + public void FindInspectorTypeByInspectedType() + { + Type type = typeof(WebSocketSignalingSettings); + Assert.That(CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(type), Is.EqualTo(typeof(WebSocketSignalingSettingsEditor))); + + type = typeof(HttpSignalingSettings); + Assert.That(CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(type), Is.EqualTo(typeof(HttpSignalingSettingsEditor))); + + type = typeof(int); + Assert.That(CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(type), Is.Null); + } + + [Test] + public void FindInspectedTypeByLabel() + { + var labels = CustomSignalingSettingsEditor.Labels(); + foreach (var label in labels) + { + Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel(label), Is.Not.Null); + } + Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel(null), Is.Null); + Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel(string.Empty), Is.Null); + Assert.That(CustomSignalingSettingsEditor.FindInspectedTypeByLabel("dummy"), Is.Null); + } + + [Test] + public void FindLabel() + { + Type inspectorType = typeof(WebSocketSignalingSettingsEditor); + Type inspectedType = typeof(WebSocketSignalingSettings); + Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectorType(inspectorType), Is.EqualTo(CustomSignalingSettingsEditor.FindLabelByInspectedType(inspectedType))); + + inspectorType = typeof(HttpSignalingSettingsEditor); + inspectedType = typeof(HttpSignalingSettings); + Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectorType(inspectorType), Is.EqualTo(CustomSignalingSettingsEditor.FindLabelByInspectedType(inspectedType))); + + inspectorType = typeof(int); + Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectorType(inspectorType), Is.Null); + inspectedType = typeof(int); + Assert.That(CustomSignalingSettingsEditor.FindLabelByInspectedType(inspectedType), Is.Null); + } + + [Test] + public void Labels() + { + var labels = CustomSignalingSettingsEditor.Labels(); + Assert.That(labels, Is.Not.Empty); + Assert.That(labels, Is.Not.Contains(string.Empty)); + Assert.That(labels, Is.Not.Contains(null)); + } + } +} diff --git a/com.unity.renderstreaming/Tests/Editor/EditorUITest.cs.meta b/com.unity.renderstreaming/Tests/Editor/EditorUITest.cs.meta new file mode 100644 index 000000000..51ce4bcf3 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Editor/EditorUITest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d874ab45c92d914fa67a31a981235c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs index 79c91eb8e..637f24bb5 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs @@ -84,7 +84,19 @@ public IEnumerator RunAgain() } [Test] - public void AddAndRemoveSignalingSettings() + public void GetDefaultSignalingSettings() + { + component.runOnAwake = false; + component.gameObject.SetActive(true); + + var settings = component.GetSignalingSettings() as WebSocketSignalingSettings; + Assert.That(settings, Is.Not.Null); + Assert.That(settings.url, Is.Not.Null); + Assert.That(settings.iceServers, Is.Not.Null); + } + + [Test] + public void AddAndRemoveSignalingHandler() { component.runOnAwake = false; component.gameObject.SetActive(true); diff --git a/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs index f2c512d90..a130ac5cd 100644 --- a/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs +++ b/com.unity.renderstreaming/Tests/Runtime/Signaling/MockSignalingSettings.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; namespace Unity.RenderStreaming.RuntimeTest.Signaling { internal class MockSignalingSettings : SignalingSettings { public override Type signalingClass => typeof(MockSignaling); + + public override IReadOnlyCollection iceServers => Array.Empty(); } } diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs new file mode 100644 index 000000000..5ae4dd16f --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Unity.RenderStreaming.Signaling; +using Unity.WebRTC; +using UnityEngine; + +namespace Unity.RenderStreaming.RuntimeTest +{ + class SignalingSettingsTest + { + [Test] + public void WebSocketSignalingSettings() + { + var settings = new WebSocketSignalingSettings(); + Assert.That(settings.signalingClass, Is.EqualTo(typeof(WebSocketSignaling))); + Assert.That(settings.url, Is.Not.Empty); + Assert.That(settings.iceServers, Is.Not.Empty); + + Assert.That(() => new WebSocketSignalingSettings(url: null), Throws.Exception.TypeOf()); + + var url = "ws://localhost"; + settings = new WebSocketSignalingSettings(url:url); + Assert.That(settings.url, Is.EqualTo(url)); + Assert.That(settings.iceServers, Is.Empty); + + var iceUrl = "stun:stun.l.google.com:19302"; + var iceServers = new[] { new IceServer(urls: new[] { iceUrl }) }; + settings = new WebSocketSignalingSettings(url: url, iceServers:iceServers); + Assert.That(settings.iceServers.Count, Is.EqualTo(1)); + Assert.That(settings.iceServers.ElementAt(0).username, Is.Null); + Assert.That(settings.iceServers.ElementAt(0).credential, Is.Null); + Assert.That(settings.iceServers.ElementAt(0).credentialType, Is.EqualTo(IceCredentialType.Password)); + Assert.That(settings.iceServers.ElementAt(0).urls.Count, Is.EqualTo(1)); + Assert.That(settings.iceServers.ElementAt(0).urls.ElementAt(0), Is.EqualTo(iceUrl)); + } + + [Test] + public void HttpSignalingSettings() + { + var settings = new HttpSignalingSettings(); + Assert.That(settings.signalingClass, Is.EqualTo(typeof(HttpSignaling))); + Assert.That(settings.url, Is.Not.Empty); + Assert.That(settings.iceServers, Is.Not.Empty); + + Assert.That(() => new HttpSignalingSettings(url: null), Throws.Exception.TypeOf()); + + var url = "http://localhost"; + settings = new HttpSignalingSettings(url: url); + Assert.That(settings.url, Is.EqualTo(url)); + Assert.That(settings.iceServers, Is.Empty); + } + + [Test] + public void FurioosSignalingSettings() + { + var settings = new FurioosSignalingSettings(); + Assert.That(settings.signalingClass, Is.EqualTo(typeof(FurioosSignaling))); + Assert.That(settings.url, Is.Not.Empty); + Assert.That(settings.iceServers, Is.Not.Empty); + + Assert.That(() => new HttpSignalingSettings(url: null), Throws.Exception.TypeOf()); + + var url = "http://localhost"; + settings = new FurioosSignalingSettings(url: url); + Assert.That(settings.url, Is.EqualTo(url)); + Assert.That(settings.iceServers, Is.Empty); + } + } +} diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs.meta new file mode 100644 index 000000000..6d1c76fff --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a99597f72c2654c4a8e3b9c82774e1dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From d0c74e3e32fd6bd48cbab9b9f61b68856b1004db Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:50:22 +0900 Subject: [PATCH 035/117] feat: Load Render Streaming settings (#849) * render streaming setting sregister preload asset * add default streaming settings asset * load default settings * change running flag * add object field ui * fix * when change settings add config object * fix settings asset * add public settings property * fix review * add setting load test * fix test * fix create automatic streaming object * fix rebase * fix rebase * add default * recreate settings on unity 2020 * fix test build setup * fix test seup * workaround audio replace track --- .../ProjectSettings/EditorBuildSettings.asset | 2 + .../RenderStreamingProjectSettingsProvider.cs | 14 +++ .../RenderStreamingSettingBuildProvider.cs | 50 +++++++++ ...enderStreamingSettingBuildProvider.cs.meta | 3 + .../UXML/RenderStreamingProjectSettings.uxml | 1 + .../Runtime/RenderStreamingSettings.asset | 29 +++++ .../RenderStreamingSettings.asset.meta | 8 ++ .../Runtime/Scripts/RenderStreaming.cs | 105 +++++++++++++++--- .../Scripts/RenderStreamingSettings.cs | 2 +- .../Tests/Editor/RenderStreamingTest.cs | 36 +++++- .../Tests/Runtime/RenderStreamingTest.cs | 87 ++++++++++++++- .../Tests/Runtime/StreamingComponentTest.cs | 4 +- 12 files changed, 318 insertions(+), 23 deletions(-) create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs.meta create mode 100644 com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset create mode 100644 com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset.meta diff --git a/RenderStreaming~/ProjectSettings/EditorBuildSettings.asset b/RenderStreaming~/ProjectSettings/EditorBuildSettings.asset index 4b03cfbe4..c1f0d906b 100644 --- a/RenderStreaming~/ProjectSettings/EditorBuildSettings.asset +++ b/RenderStreaming~/ProjectSettings/EditorBuildSettings.asset @@ -42,5 +42,7 @@ EditorBuildSettings: type: 2} com.unity.input.settings: {fileID: 11400000, guid: 9d3afefa5e1574ee38fb1d298122dbc7, type: 2} + com.unity.renderstreaming.settings: {fileID: 11400000, guid: eaaad242393318e4f85c45e69c8837f0, + type: 2} com.unity.xr.management.loader_settings: {fileID: 11400000, guid: 66105b57eda77e74db55e7eb2b532054, type: 2} diff --git a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs index 4c83d196e..744693e14 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; +using System.Linq; using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine; using UnityEngine.UIElements; namespace Unity.RenderStreaming @@ -39,6 +42,17 @@ public override void OnActivate(string searchContext, VisualElement rootElement) VisualElement newVisualElement = new VisualElement(); template.CloneTree(newVisualElement); rootVisualElement.Add(newVisualElement); + + var renderStreamingSettingsField = rootVisualElement.Q("renderStreamingSettingsField"); + renderStreamingSettingsField.SetValueWithoutNotify(RenderStreaming.Settings); + renderStreamingSettingsField.RegisterCallback>(ev => + { + var settings = ev.newValue as RenderStreamingSettings; + if (settings != null) + { + RenderStreaming.Settings = settings; + } + }); } public RenderStreamingProjectSettingsProvider(string path, SettingsScope scopes, IEnumerable keywords = null) diff --git a/com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs b/com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs new file mode 100644 index 000000000..ad8415c47 --- /dev/null +++ b/com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs @@ -0,0 +1,50 @@ +using System.Linq; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; + +namespace Unity.RenderStreaming.Editor +{ + internal class RenderStreamingSettingBuildProvider : IPreprocessBuildWithReport, IPostprocessBuildWithReport + { + public int callbackOrder => 0; + + public void OnPreprocessBuild(BuildReport report) + { + if (RenderStreaming.Settings == null) + return; + + // If we operate on temporary object instead of input setting asset, + // adding temporary asset would result in preloadedAssets containing null object "{fileID: 0}". + // Hence we ignore adding temporary objects to preloaded assets. + if (!EditorUtility.IsPersistent(RenderStreaming.Settings)) + return; + + // Add InputSettings object assets, if it's not in there already. + var preloadedAssets = PlayerSettings.GetPreloadedAssets(); + if (!preloadedAssets.Contains(RenderStreaming.Settings)) + { + ArrayHelpers.Append(ref preloadedAssets, RenderStreaming.Settings); + PlayerSettings.SetPreloadedAssets(preloadedAssets); + } + } + + public void OnPostprocessBuild(BuildReport report) + { + // Revert back to original state by removing all render streaming settings from preloaded assets. + var preloadedAssets = PlayerSettings.GetPreloadedAssets(); + while (preloadedAssets != null && preloadedAssets.Length > 0) + { + var target = preloadedAssets.FirstOrDefault(x => x is RenderStreamingSettings); + var index = ArrayHelpers.IndexOf(preloadedAssets, target); + if (index != -1) + { + ArrayHelpers.EraseAt(ref preloadedAssets, index); + PlayerSettings.SetPreloadedAssets(preloadedAssets); + } + else + break; + } + } + } +} diff --git a/com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs.meta b/com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs.meta new file mode 100644 index 000000000..c751bbbd4 --- /dev/null +++ b/com.unity.renderstreaming/Editor/RenderStreamingSettingBuildProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 86410c7bcb6a49d39cff48607dc52eff +timeCreated: 1674615170 \ No newline at end of file diff --git a/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml b/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml index edc4650b0..8923b6e77 100644 --- a/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml +++ b/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml @@ -1,6 +1,7 @@ + diff --git a/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset b/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset new file mode 100644 index 000000000..efa992e7c --- /dev/null +++ b/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bc8ba17751ad4107a50fa1415017b6b1, type: 3} + m_Name: RenderStreamingSettings + m_EditorClassIdentifier: + automaticStreaming: 0 + signalingSettings: + id: 0 + references: + version: 1 + 00000000: + type: {class: WebSocketSignalingSettings, ns: Unity.RenderStreaming, asm: Unity.RenderStreaming} + data: + m_url: ws://127.0.0.1:80 + m_iceServers: + - m_urls: + - stun:stun.l.google.com:19302 + m_username: + m_credentialType: 0 + m_credential: diff --git a/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset.meta b/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset.meta new file mode 100644 index 000000000..4d2117e4f --- /dev/null +++ b/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eaaad242393318e4f85c45e69c8837f0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs index 6853e910e..b048438c5 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs @@ -1,4 +1,5 @@ -using Unity.WebRTC; +using System; +using System.Linq; using UnityEngine; using Object = UnityEngine.Object; @@ -13,8 +14,43 @@ namespace Unity.RenderStreaming #endif public static class RenderStreaming { - internal static RenderStreamingSettings s_settings; - internal static GameObject s_automaticStreamingObject; + internal const string EditorBuildSettingsConfigKey = "com.unity.renderstreaming.settings"; + internal const string DefaultRenderStreamingSettingsPath = + "Packages/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset"; + + private static RenderStreamingSettings s_settings; + private static GameObject s_automaticStreamingObject; + + private static bool m_running; + + internal static RenderStreamingSettings Settings + { + get => s_settings; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (s_settings == value) + return; + + // In the editor, we keep track of the settings asset through EditorBuildSettings. +#if UNITY_EDITOR + if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(value))) + { + EditorBuildSettings.AddConfigObject(EditorBuildSettingsConfigKey, value, true); + } +#endif + + if (m_running && s_settings.signalingSettings != value.signalingSettings) + { + Debug.LogWarning("Signaling settings doesn't change on already started signaling instance."); + } + + s_settings = value; + ApplySettings(); + } + } public static bool AutomaticStreaming { @@ -31,20 +67,55 @@ public static T GetSignalingSettings() where T : SignalingSettings return s_settings.signalingSettings as T; } +#if UNITY_EDITOR + public static void SetSignalingSettings(SignalingSettings settings) + { + if (m_running) + { + throw new InvalidOperationException("Signaling settings can't overwrite on playing."); + } + + s_settings.signalingSettings = settings; + ApplySettings(); + } +#endif + static RenderStreaming() { - // todo: load from assets - var settings = ScriptableObject.CreateInstance(); - settings.automaticStreaming = false; - var signalingSettings = new WebSocketSignalingSettings( - url: "ws://127.0.0.1:80", - iceServers: new[] - { - new IceServer(urls: new[] {"stun:stun.l.google.com:19302"}) - } - ); - settings.signalingSettings = signalingSettings; - s_settings = settings; +#if UNITY_EDITOR + InitializeInEditor(); +#else + m_running = true; +#endif + } + +#if UNITY_EDITOR + private static void InitializeInEditor() + { + if (EditorBuildSettings.TryGetConfigObject(EditorBuildSettingsConfigKey, out RenderStreamingSettings settingsAsset)) + { + s_settings = settingsAsset; + } + else + { + s_settings = AssetDatabase.LoadAssetAtPath(DefaultRenderStreamingSettingsPath); + } + + EditorApplication.playModeStateChanged += change => + { + m_running = change == PlayModeStateChange.EnteredPlayMode; + }; + } +#endif + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void LoadSettings() + { + if (s_settings == null) + { + s_settings = Resources.FindObjectsOfTypeAll().FirstOrDefault() ?? + ScriptableObject.CreateInstance(); + } } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] @@ -56,9 +127,9 @@ private static void RunInitialize() } } - private static void ApplySettings() + internal static void ApplySettings() { - if (!Application.isPlaying) + if (!m_running) { return; } diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs index 58ea3d00b..90492d13a 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs @@ -5,6 +5,6 @@ namespace Unity.RenderStreaming public class RenderStreamingSettings : ScriptableObject { [SerializeField] public bool automaticStreaming; - [SerializeReference] public SignalingSettings signalingSettings; + [SerializeReference] public SignalingSettings signalingSettings = new WebSocketSignalingSettings(); } } diff --git a/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs index 2f7ee583e..674d9f3bb 100644 --- a/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs +++ b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs @@ -1,13 +1,45 @@ +using System.Linq; using NUnit.Framework; +using UnityEditor; namespace Unity.RenderStreaming.EditorTest { public class RenderStreamingTest { + private RenderStreamingSettings temp; + + [SetUp] + public void Setup() + { + temp = RenderStreaming.Settings; + RenderStreaming.Settings = + AssetDatabase.LoadAssetAtPath(RenderStreaming.DefaultRenderStreamingSettingsPath); + } + + [TearDown] + public void TearDown() + { + if (temp != null) + { + RenderStreaming.Settings = temp; + } + } + [Test] - public void RenderStreamingSettings() + public void SignalingSettings() { - // todo: check RenderStreaming.GetSignalingSettings method that return correct settings from asset + var url = "wss://127.0.0.1:8081"; + var iceServers = new IceServer[] + { + new IceServer(new string[] {"stun:stun.l.google.com:19302"}) + }; + var signalingSettings = new WebSocketSignalingSettings(url, iceServers); + + Assert.That(() => RenderStreaming.SetSignalingSettings(signalingSettings), Throws.Nothing); + + var settings = RenderStreaming.GetSignalingSettings(); + Assert.That(settings.url, Is.EqualTo(url)); + Assert.That(settings.iceServers.ElementAt(0).urls, Is.EquivalentTo(iceServers[0].urls)); } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs index b8538a0ef..ab72de8d5 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs @@ -1,10 +1,95 @@ +using System.Linq; using NUnit.Framework; +using Unity.RenderStreaming.RuntimeTest.Signaling; +using Unity.RenderStreaming.Signaling; using UnityEngine; +using UnityEngine.TestTools; + +#if UNITY_EDITOR +using UnityEditor; +#endif namespace Unity.RenderStreaming.RuntimeTest { - class RenderStreamingTest + class RenderStreamingTest : IPrebuildSetup, IPostBuildCleanup { + private RenderStreamingSettings temp; + + [SetUp] + public void SetUpTest() + { + temp = RenderStreaming.Settings; + } + + [TearDown] + public void TearDown() + { + if (temp != null) + { + RenderStreaming.Settings = temp; + } + } + + void IPrebuildSetup.Setup() + { +#if UNITY_EDITOR + var defaultSettings = RenderStreaming.Settings; + RenderStreaming.Settings = + AssetDatabase.LoadAssetAtPath(RenderStreaming.DefaultRenderStreamingSettingsPath); + if (defaultSettings != null) + { + EditorBuildSettings.AddConfigObject(RenderStreaming.EditorBuildSettingsConfigKey, defaultSettings, true); + } +#endif + } + + void IPostBuildCleanup.Cleanup() + { +#if UNITY_EDITOR + if (EditorBuildSettings.TryGetConfigObject(RenderStreaming.EditorBuildSettingsConfigKey, out RenderStreamingSettings settingsAsset)) + { + RenderStreaming.Settings = settingsAsset; + } + else + { + RenderStreaming.Settings = + AssetDatabase.LoadAssetAtPath(RenderStreaming.DefaultRenderStreamingSettingsPath); + } +#endif + } + + [Test] + public void CheckDefaultSettings() + { + RenderStreamingSettings defaultSettings = null; +#if UNITY_EDITOR + defaultSettings = + AssetDatabase.LoadAssetAtPath(RenderStreaming.DefaultRenderStreamingSettingsPath); +#else + defaultSettings = Resources.FindObjectsOfTypeAll().FirstOrDefault() ?? + ScriptableObject.CreateInstance(); +#endif + Assert.That(defaultSettings.automaticStreaming, Is.False); + var defaultSignalingSettings = defaultSettings.signalingSettings as WebSocketSignalingSettings; + Assert.That(defaultSignalingSettings, Is.Not.Null); + Assert.That(defaultSignalingSettings.signalingClass, Is.EqualTo(typeof(WebSocketSignaling))); + Assert.That(defaultSignalingSettings.url, Is.EqualTo("ws://127.0.0.1:80")); + Assert.That(defaultSignalingSettings.iceServers.ElementAt(0).urls, Is.EquivalentTo(new string[] {"stun:stun.l.google.com:19302"})); + } + + [Test] + public void Settings() + { + Assert.That(() => RenderStreaming.Settings = null, Throws.ArgumentNullException); + + var settings = ScriptableObject.CreateInstance(); + settings.signalingSettings = new MockSignalingSettings(); + + RenderStreaming.Settings = settings; + Assert.That(RenderStreaming.Settings.automaticStreaming, Is.EqualTo(settings.automaticStreaming)); + Assert.That(RenderStreaming.Settings.signalingSettings, Is.EqualTo(settings.signalingSettings)); + } + [Test] public void AutomaticStreaming() { diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index cf5642177..60d986698 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -367,9 +367,9 @@ public IEnumerator CreateTrack() UnityEngine.Object.DestroyImmediate(go3); } - // workaround(kazuki): Fix NullReferenceException in AudioStreamTrack.ProcessAudio. + // workaround(kazuki): Fix NullReferenceException in AudioStreamTrack.ProcessAudio. (WRS-231) [UnityTest] - [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer })] + [UnityPlatform(exclude = new[] { RuntimePlatform.OSXEditor, RuntimePlatform.OSXPlayer, RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer })] public IEnumerator ReplaceTrack() { var go = new GameObject(); From ee88e26987fa1b84d9bab455e53cc04c9dd91b9c Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:55:58 +0900 Subject: [PATCH 036/117] refactor: Rename RenderStreamingHandler class (#852) --- .../SignalingSettingsDrawer.cs | 6 +- ...lerEditor.cs => SignalingManagerEditor.cs} | 4 +- ...cs.meta => SignalingManagerEditor.cs.meta} | 0 .../Runtime/Scripts/AutomaticStreaming.cs | 4 +- ...treamingHandler.cs => SignalingManager.cs} | 8 +-- ...ndler.cs.meta => SignalingManager.cs.meta} | 0 ...nternal.cs => SignalingManagerInternal.cs} | 6 +- ....meta => SignalingManagerInternal.cs.meta} | 0 .../ARFoundation/ARFoundationSample.cs | 2 +- .../Bidirectional/BidirectionalSample.cs | 2 +- .../Example/Broadcast/BroadcastSample.cs | 2 +- .../Samples~/Example/Gyro/GyroSample.cs | 2 +- .../Example/Multiplay/MultiplaySample.cs | 2 +- .../Example/Receiver/ReceiverSample.cs | 2 +- .../WebBrowserInput/WebBrowserInputSample.cs | 2 +- .../Runtime/InputSystem/InputRemotingTest.cs | 6 +- .../Tests/Runtime/SignalingHandlerTest.cs | 4 +- ...est.cs => SignalingManagerInternalTest.cs} | 56 +++++++++---------- ...a => SignalingManagerInternalTest.cs.meta} | 0 ...HandlerTest.cs => SignalingManagerTest.cs} | 6 +- ...t.cs.meta => SignalingManagerTest.cs.meta} | 0 21 files changed, 57 insertions(+), 57 deletions(-) rename com.unity.renderstreaming/Editor/{RenderStreamingHandlerEditor.cs => SignalingManagerEditor.cs} (85%) rename com.unity.renderstreaming/Editor/{RenderStreamingHandlerEditor.cs.meta => SignalingManagerEditor.cs.meta} (100%) rename com.unity.renderstreaming/Runtime/Scripts/{RenderStreamingHandler.cs => SignalingManager.cs} (96%) rename com.unity.renderstreaming/Runtime/Scripts/{RenderStreamingHandler.cs.meta => SignalingManager.cs.meta} (100%) rename com.unity.renderstreaming/Runtime/Scripts/{RenderStreamingInternal.cs => SignalingManagerInternal.cs} (98%) rename com.unity.renderstreaming/Runtime/Scripts/{RenderStreamingInternal.cs.meta => SignalingManagerInternal.cs.meta} (100%) rename com.unity.renderstreaming/Tests/Runtime/{RenderStreamingInternalTest.cs => SignalingManagerInternalTest.cs} (94%) rename com.unity.renderstreaming/Tests/Runtime/{RenderStreamingInternalTest.cs.meta => SignalingManagerInternalTest.cs.meta} (100%) rename com.unity.renderstreaming/Tests/Runtime/{RenderStreamingHandlerTest.cs => SignalingManagerTest.cs} (96%) rename com.unity.renderstreaming/Tests/Runtime/{RenderStreamingHandlerTest.cs.meta => SignalingManagerTest.cs.meta} (100%) diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs index d9827d9de..6cc2ba052 100644 --- a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs @@ -27,7 +27,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) ISignalingSettingEditor CreateEditor(SerializedProperty property) { - var handler = property.serializedObject.targetObject as RenderStreamingHandler; + var handler = property.serializedObject.targetObject as SignalingManager; var settings = handler.GetSignalingSettings(); var type = CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(settings.GetType()); return Activator.CreateInstance(type) as ISignalingSettingEditor; @@ -35,7 +35,7 @@ ISignalingSettingEditor CreateEditor(SerializedProperty property) VisualElement CreatePopUpSignalingType(SerializedProperty property, string label) { - var handler = property.serializedObject.targetObject as RenderStreamingHandler; + var handler = property.serializedObject.targetObject as SignalingManager; var settings = handler.GetSignalingSettings(); var defaultValue = CustomSignalingSettingsEditor.FindLabelByInspectedType(settings.GetType()); var choices = CustomSignalingSettingsEditor.Labels().ToList(); @@ -46,7 +46,7 @@ VisualElement CreatePopUpSignalingType(SerializedProperty property, string label void OnChangedValue(ChangeEvent e, SerializedProperty property) { - var handler = property.serializedObject.targetObject as RenderStreamingHandler; + var handler = property.serializedObject.targetObject as SignalingManager; if (handler == null) return; diff --git a/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs similarity index 85% rename from com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs rename to com.unity.renderstreaming/Editor/SignalingManagerEditor.cs index 75362efb0..9c88affb7 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs +++ b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs @@ -7,8 +7,8 @@ namespace Unity.RenderStreaming.Editor /// /// Render Streaming inspector. /// - [CustomEditor(typeof(RenderStreamingHandler))] - internal class RenderStreamingHandlerEditor : UnityEditor.Editor + [CustomEditor(typeof(SignalingManager))] + internal class SignalingManagerEditor : UnityEditor.Editor { public override VisualElement CreateInspectorGUI() { diff --git a/com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs.meta b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs.meta similarity index 100% rename from com.unity.renderstreaming/Editor/RenderStreamingHandlerEditor.cs.meta rename to com.unity.renderstreaming/Editor/SignalingManagerEditor.cs.meta diff --git a/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs index 627e699bb..4eb08b72f 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs @@ -12,7 +12,7 @@ namespace Unity.RenderStreaming { internal class AutomaticStreaming : MonoBehaviour { - private RenderStreamingHandler renderstreaming; + private SignalingManager renderstreaming; private Broadcast broadcast; private VideoStreamSender videoStreamSender; private AudioStreamSender audioStreamSender; @@ -36,7 +36,7 @@ private void Awake() inputReceiver = gameObject.AddComponent(); broadcast.AddComponent(inputReceiver); - renderstreaming = gameObject.AddComponent(); + renderstreaming = gameObject.AddComponent(); renderstreaming.AddSignalingHandler(broadcast); var signalingSettings = RenderStreaming.GetSignalingSettings(); renderstreaming.SetSignalingSettings(signalingSettings); diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs similarity index 96% rename from com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs rename to com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs index e357b8623..dae9ec5d0 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs @@ -12,8 +12,8 @@ namespace Unity.RenderStreaming { - [AddComponentMenu("Render Streaming/Render Streaming Handler")] - public sealed class RenderStreamingHandler : MonoBehaviour + [AddComponentMenu("Render Streaming/Signaling Manager")] + public sealed class SignalingManager : MonoBehaviour { #pragma warning disable 0649 // ToDo: Create component UI on URS-553 @@ -37,7 +37,7 @@ public sealed class RenderStreamingHandler : MonoBehaviour public bool runOnAwake = true; #pragma warning restore 0649 - private RenderStreamingInternal m_instance; + private SignalingManagerInternal m_instance; private SignalingEventProvider m_provider; private bool m_running; @@ -167,7 +167,7 @@ private void _Run( if (_handlers.Count() == 0) throw new InvalidOperationException("Handler list is empty."); - m_instance = new RenderStreamingInternal(ref dependencies); + m_instance = new SignalingManagerInternal(ref dependencies); m_provider = new SignalingEventProvider(m_instance); foreach (var handler in _handlers) diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs.meta similarity index 100% rename from com.unity.renderstreaming/Runtime/Scripts/RenderStreamingHandler.cs.meta rename to com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs.meta diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs similarity index 98% rename from com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs rename to com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs index e38c65c46..2a83256a0 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs @@ -43,7 +43,7 @@ internal struct RenderStreamingDependencies /// /// /// - internal class RenderStreamingInternal : IDisposable, + internal class SignalingManagerInternal : IDisposable, IRenderStreamingHandler, IRenderStreamingDelegate { /// @@ -105,7 +105,7 @@ internal class RenderStreamingInternal : IDisposable, /// /// /// - public RenderStreamingInternal(ref RenderStreamingDependencies dependencies) + public SignalingManagerInternal(ref RenderStreamingDependencies dependencies) { if (dependencies.signaling == null) throw new ArgumentException("Signaling instance is null."); @@ -130,7 +130,7 @@ public RenderStreamingInternal(ref RenderStreamingDependencies dependencies) /// /// /// - ~RenderStreamingInternal() + ~SignalingManagerInternal() { Dispose(); } diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs.meta similarity index 100% rename from com.unity.renderstreaming/Runtime/Scripts/RenderStreamingInternal.cs.meta rename to com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs.meta diff --git a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs index 275ef592c..c2f1d5038 100644 --- a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs @@ -11,7 +11,7 @@ namespace Unity.RenderStreaming.Samples internal class ARFoundationSample : MonoBehaviour { #pragma warning disable 0649 - [SerializeField] private RenderStreamingHandler renderStreaming; + [SerializeField] private SignalingManager renderStreaming; [SerializeField] private Button startButton; [SerializeField] private Button stopButton; [SerializeField] private RawImage remoteVideoImage; diff --git a/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs b/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs index 99e4f33e2..efa1025c7 100644 --- a/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs @@ -7,7 +7,7 @@ namespace Unity.RenderStreaming.Samples class BidirectionalSample : MonoBehaviour { #pragma warning disable 0649 - [SerializeField] private RenderStreamingHandler renderStreaming; + [SerializeField] private SignalingManager renderStreaming; [SerializeField] private Dropdown webcamSelectDropdown; [SerializeField] private Dropdown microphoneSelectDropdown; [SerializeField] private Button startButton; diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs index 893e1357c..31b5cfbf7 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs @@ -31,7 +31,7 @@ public static void AddListener(this InputAction action, Action _test; - private RenderStreamingInternal _target1, _target2; + private SignalingManagerInternal _target1, _target2; private RTCDataChannel _channel1, _channel2; private string connectionId = "12345"; const float ResendOfferInterval = 3f; @@ -76,8 +76,8 @@ public IEnumerator UnitySetUp() var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - _target1 = new RenderStreamingInternal(ref dependencies1); - _target2 = new RenderStreamingInternal(ref dependencies2); + _target1 = new SignalingManagerInternal(ref dependencies1); + _target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs index 73ee98188..11a16f2eb 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs @@ -92,7 +92,7 @@ class TestContainer : IDisposable where T : SignalingHandlerBase, IMonoBehavi const float ResendOfferInterval = 3.0f; public MonoBehaviourTest test; - public RenderStreamingInternal instance; + public SignalingManagerInternal instance; public SignalingEventProvider provider; private static RenderStreamingDependencies CreateDependencies(MonoBehaviour behaviour) @@ -114,7 +114,7 @@ public static TestContainer Create(string name) { var test = new MonoBehaviourTest(); var dependencies = CreateDependencies(test.component); - var instance = new RenderStreamingInternal(ref dependencies); + var instance = new SignalingManagerInternal(ref dependencies); var provider = new SignalingEventProvider(instance); var container = new TestContainer { test = test, instance = instance, provider = provider }; test.component.SetHandler(instance); diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs similarity index 94% rename from com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs rename to com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs index 36c1a1fcb..f3a39ed33 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs @@ -15,16 +15,16 @@ enum TestMode PublicMode } - static class RenderStreamingInternalExtension + static class SignalingManagerInternalExtension { - public static RTCRtpTransceiver AddSenderTrack(this RenderStreamingInternal target, string connectionId, MediaStreamTrack track) + public static RTCRtpTransceiver AddSenderTrack(this SignalingManagerInternal target, string connectionId, MediaStreamTrack track) { RTCRtpTransceiverInit init = new RTCRtpTransceiverInit() { direction = RTCRtpTransceiverDirection.SendOnly }; return target.AddTransceiver(connectionId, track, init); } } - class RenderStreamingInternalTest + class SignalingManagerInternalTest { class MyMonoBehaviourTest : MonoBehaviour, IMonoBehaviourTest { @@ -75,7 +75,7 @@ public IEnumerator Construct(TestMode mode) MockSignaling.Reset(mode == TestMode.PrivateMode); var dependencies = CreateDependencies(); - var target = new RenderStreamingInternal(ref dependencies); + var target = new SignalingManagerInternal(ref dependencies); bool isStarted = false; target.onStart += () => { isStarted = true; }; @@ -94,8 +94,8 @@ public IEnumerator ConstructMultiple(TestMode mode) var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -118,7 +118,7 @@ public IEnumerator OpenConnection(TestMode mode) MockSignaling.Reset(mode == TestMode.PrivateMode); var dependencies = CreateDependencies(); - var target = new RenderStreamingInternal(ref dependencies); + var target = new SignalingManagerInternal(ref dependencies); bool isStarted = false; target.onStart += () => { isStarted = true; }; @@ -153,7 +153,7 @@ public IEnumerator OpenConnectionThrowException(TestMode mode) MockSignaling.Reset(mode == TestMode.PrivateMode); var dependencies = CreateDependencies(); - var target = new RenderStreamingInternal(ref dependencies); + var target = new SignalingManagerInternal(ref dependencies); bool isStarted = false; target.onStart += () => { isStarted = true; }; @@ -175,7 +175,7 @@ public IEnumerator AddTrack(TestMode mode) MockSignaling.Reset(mode == TestMode.PrivateMode); var dependencies = CreateDependencies(); - var target = new RenderStreamingInternal(ref dependencies); + var target = new SignalingManagerInternal(ref dependencies); bool isStarted = false; target.onStart += () => { isStarted = true; }; @@ -218,7 +218,7 @@ public IEnumerator AddTrackThrowException(TestMode mode) MockSignaling.Reset(mode == TestMode.PrivateMode); var dependencies = CreateDependencies(); - var target = new RenderStreamingInternal(ref dependencies); + var target = new SignalingManagerInternal(ref dependencies); bool isStarted = false; target.onStart += () => { isStarted = true; }; @@ -255,7 +255,7 @@ public IEnumerator AddTrackMultiple(TestMode mode) MockSignaling.Reset(mode == TestMode.PrivateMode); var dependencies = CreateDependencies(); - var target = new RenderStreamingInternal(ref dependencies); + var target = new SignalingManagerInternal(ref dependencies); bool isStarted = false; target.onStart += () => { isStarted = true; }; @@ -302,7 +302,7 @@ public IEnumerator CreateChannel(TestMode mode) MockSignaling.Reset(mode == TestMode.PrivateMode); var dependencies = CreateDependencies(); - var target = new RenderStreamingInternal(ref dependencies); + var target = new SignalingManagerInternal(ref dependencies); bool isStarted = false; target.onStart += () => { isStarted = true; }; yield return new WaitUntil(() => isStarted); @@ -337,8 +337,8 @@ public IEnumerator OnAddReceiverPrivateMode() var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -410,8 +410,8 @@ public IEnumerator OnAddReceiverPublicMode() var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -481,8 +481,8 @@ public IEnumerator OnAddChannelPrivateMode() var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -546,8 +546,8 @@ public IEnumerator SendOfferThrowExceptionPrivateMode() var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -612,8 +612,8 @@ public IEnumerator SwapTransceiverPrivateMode() var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -695,8 +695,8 @@ public IEnumerator ResendOfferUntilGotAnswer(TestMode mode) var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -760,8 +760,8 @@ public IEnumerator DeleteFailedPeers(TestMode mode) var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; @@ -823,8 +823,8 @@ public IEnumerator ReNegotiationAfterReceivingFirstOffer() var dependencies1 = CreateDependencies(); var dependencies2 = CreateDependencies(); - var target1 = new RenderStreamingInternal(ref dependencies1); - var target2 = new RenderStreamingInternal(ref dependencies2); + var target1 = new SignalingManagerInternal(ref dependencies1); + var target2 = new SignalingManagerInternal(ref dependencies2); bool isStarted1 = false; bool isStarted2 = false; diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs.meta similarity index 100% rename from com.unity.renderstreaming/Tests/Runtime/RenderStreamingInternalTest.cs.meta rename to com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs.meta diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs similarity index 96% rename from com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs rename to com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs index 637f24bb5..cd27bfaa1 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs @@ -8,16 +8,16 @@ namespace Unity.RenderStreaming.RuntimeTest { - class RenderStreamingHandlerTest + class SignalingManagerTest { - RenderStreamingHandler component; + SignalingManager component; [SetUp] public void SetUp() { GameObject obj = new GameObject(); obj.SetActive(false); - component = obj.AddComponent(); + component = obj.AddComponent(); } [TearDown] diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs.meta similarity index 100% rename from com.unity.renderstreaming/Tests/Runtime/RenderStreamingHandlerTest.cs.meta rename to com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs.meta From 7aa362a02831c37c71c7f184f72bc3e9b34d7ada Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 31 Jan 2023 17:33:47 +0900 Subject: [PATCH 037/117] refactor: Devide files for classes what inherits SignalingSettings class (#853) * Refactor SignalingSettings * revert * remove file * revert --- .../SignalingSettingsDrawer.cs | 10 +- .../Signaling/FurioosSignalingSettings.cs | 63 ++++ .../FurioosSignalingSettings.cs.meta | 3 + .../Signaling/HttpSignalingSettings.cs | 70 ++++ .../Signaling/HttpSignalingSettings.cs.meta | 3 + .../Scripts/Signaling/SignalingSettings.cs | 126 ++++++++ .../{ => Signaling}/SignalingSettings.cs.meta | 0 .../Signaling/SignalingSettingsObject.cs | 17 + .../Signaling/SignalingSettingsObject.cs.meta | 3 + .../Signaling/WebSocketSignalingSettings.cs | 63 ++++ .../WebSocketSignalingSettings.cs.meta | 3 + .../Scripts/SignalingManagerInternal.cs | 1 - .../Runtime/Scripts/SignalingSettings.cs | 299 ------------------ 13 files changed, 357 insertions(+), 304 deletions(-) create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs.meta create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs rename com.unity.renderstreaming/Runtime/Scripts/{ => Signaling}/SignalingSettings.cs.meta (100%) create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs.meta create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs.meta delete mode 100644 com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs index 6cc2ba052..8a80e39f1 100644 --- a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using UnityEngine; using UnityEditor; using UnityEditor.UIElements; using UnityEngine.UIElements; @@ -18,10 +19,11 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { editor = CreateEditor(property); var root = new VisualElement(); + var box = new Box(); + root.Add(box); editorGUI = editor.CreateInspectorGUI(property); - - root.Add(CreatePopUpSignalingType(property, "Signaling Type")); - root.Add(editorGUI); + box.Add(CreatePopUpSignalingType(property, "Signaling Type")); + box.Add(editorGUI); return root; } @@ -99,8 +101,8 @@ public VisualElement CreateInspectorGUI(SerializedProperty property) { VisualElement root = new VisualElement(); root.Add(new PropertyField(property.FindPropertyRelative("m_url"), "URL")); - root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); root.Add(new PropertyField(property.FindPropertyRelative("m_interval"), "Polling Interval")); + root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); return root; } diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs new file mode 100644 index 000000000..ca4a17d28 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Unity.RenderStreaming.Signaling; +using UnityEngine; + +namespace Unity.RenderStreaming +{ + /// + /// + /// + [Serializable] + public class FurioosSignalingSettings : SignalingSettings + { + /// + /// + /// + public override Type signalingClass => typeof(FurioosSignaling); + + /// + /// + /// + public override IReadOnlyCollection iceServers => m_iceServers; + + /// + /// + /// + public string url => m_url; + + [SerializeField] + protected string m_url; + [SerializeField] + protected IceServer[] m_iceServers; + + /// + /// + /// + /// + /// + public FurioosSignalingSettings(string url, IceServer[] iceServers = null) + { + if (url == null) + throw new ArgumentNullException("url"); + if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) + throw new ArgumentException("url is not well formed Uri"); + + m_url = url; + m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); + } + + /// + /// + /// + public FurioosSignalingSettings() + { + m_url = "http://127.0.0.1"; + m_iceServers = new[] + { + new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) + }; + } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta new file mode 100644 index 000000000..9dc5f982b --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0144784cda4001d489615dee132aa6e6 +timeCreated: 1674112040 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs new file mode 100644 index 000000000..102c9b699 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Unity.RenderStreaming.Signaling; +using UnityEngine; + +namespace Unity.RenderStreaming +{ + [Serializable] + public class HttpSignalingSettings : SignalingSettings + { + /// + /// + /// + public override Type signalingClass => typeof(HttpSignaling); + + /// + /// + /// + public override IReadOnlyCollection iceServers => m_iceServers; + + /// + /// + /// + public string url => m_url; + + /// + /// + /// + public float interval => m_interval; + + [SerializeField] + private float m_interval; + [SerializeField] + protected string m_url; + [SerializeField] + protected IceServer[] m_iceServers; + + /// + /// + /// + /// + /// + /// + public HttpSignalingSettings(string url, IceServer[] iceServers = null, float interval = 5.0f) + { + if (url == null) + throw new ArgumentNullException("url"); + if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) + throw new ArgumentException("url is not well formed Uri"); + + m_url = url; + m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); + m_interval = interval; + } + + /// + /// + /// + public HttpSignalingSettings() + { + m_url = "http://127.0.0.1"; + m_iceServers = new[] + { + new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) + }; + m_interval = 5f; + } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs.meta new file mode 100644 index 000000000..bf2fa2c3b --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e6fb58902f9c27e4688c3a6fe70d9560 +timeCreated: 1674112040 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs new file mode 100644 index 000000000..9c200f005 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Unity.WebRTC; +using UnityEngine; + +namespace Unity.RenderStreaming +{ + internal sealed class SignalingSettingsAttribute : PropertyAttribute { } + + /// + /// + /// + public enum IceCredentialType + { + /// + /// + /// + Password = 0, + + /// + /// + /// + OAuth = 1 + } + + /// + /// + /// + [Serializable] + public class IceServer + { + /// + /// + /// + public IReadOnlyCollection urls => m_urls; + + /// + /// + /// + public string username => m_username; + + /// + /// + /// + public IceCredentialType credentialType => m_credentialType; + + /// + /// + /// + public string credential => m_credential; + + [SerializeField] + private string[] m_urls; + [SerializeField] + private string m_username; + [SerializeField] + private IceCredentialType m_credentialType; + [SerializeField] + private string m_credential; + + /// + /// + /// + /// + public static implicit operator RTCIceServer(IceServer server) + { + var iceServer = new RTCIceServer + { + urls = server.urls.ToArray(), + username = server.username, + credential = server.credential, + credentialType = (RTCIceCredentialType)server.credentialType + }; + return iceServer; + } + + /// + /// + /// + /// + public IceServer Clone() + { + return new IceServer(this.urls.ToArray(), this.username, this.credentialType, this.credential); + } + + /// + /// + /// + /// + /// + /// + /// + public IceServer(string[] urls = null, string username = null, IceCredentialType credentialType = IceCredentialType.Password, string credential = null) + { + m_urls = urls?.ToArray(); + m_username = username; + m_credential = credential; + m_credentialType = credentialType; + } + + internal IceServer(RTCIceServer server) + { + m_urls = server.urls.ToArray(); + m_username = server.username; + m_credential = server.credential; + m_credentialType = (IceCredentialType)server.credentialType; + } + } + + /// + /// + /// + public abstract class SignalingSettings + { + /// + /// + /// + public abstract IReadOnlyCollection iceServers { get; } + + /// + /// + /// + public abstract Type signalingClass { get; } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs.meta similarity index 100% rename from com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs.meta rename to com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs.meta diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs new file mode 100644 index 000000000..533b3148e --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs @@ -0,0 +1,17 @@ +using UnityEngine; + +namespace Unity.RenderStreaming +{ + internal class SignalingSettingsObject : ScriptableObject + { + [SerializeReference] + public SignalingSettings settings; + + public static ScriptableObject Create() + { + var instance = CreateInstance(); + instance.settings = new WebSocketSignalingSettings(); + return instance; + } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs.meta new file mode 100644 index 000000000..a5a9b3f20 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 22c294fb286b37446a86664f4fb41023 +timeCreated: 1674112040 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs new file mode 100644 index 000000000..25ecf1fd4 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Unity.RenderStreaming.Signaling; +using UnityEngine; + +namespace Unity.RenderStreaming +{ + /// + /// + /// + [Serializable] + public class WebSocketSignalingSettings : SignalingSettings + { + /// + /// + /// + public override Type signalingClass => typeof(WebSocketSignaling); + + /// + /// + /// + public override IReadOnlyCollection iceServers => m_iceServers; + + /// + /// + /// + public string url => m_url; + + [SerializeField] + protected string m_url; + [SerializeField] + protected IceServer[] m_iceServers; + + /// + /// + /// + /// + /// + public WebSocketSignalingSettings(string url, IceServer[] iceServers = null) + { + if (url == null) + throw new ArgumentNullException("url"); + if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) + throw new ArgumentException("url is not well formed Uri"); + + m_url = url; + m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); + } + + /// + /// + /// + public WebSocketSignalingSettings() + { + m_url = "ws://127.0.0.1"; + m_iceServers = new[] + { + new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) + }; + } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs.meta new file mode 100644 index 000000000..864a6cce2 --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bb4c44f4a1edb4d4a9a787ca2046cd1c +timeCreated: 1674112040 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs index 2a83256a0..cd986b6ca 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs @@ -5,7 +5,6 @@ using UnityEngine; using Unity.RenderStreaming.Signaling; using Unity.WebRTC; -using UnityEngine.Assertions; namespace Unity.RenderStreaming { diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs deleted file mode 100644 index 67dee5625..000000000 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingSettings.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Unity.RenderStreaming.Signaling; -using Unity.WebRTC; -using UnityEngine; - -namespace Unity.RenderStreaming -{ - internal sealed class SignalingSettingsAttribute : PropertyAttribute { } - - /// - /// - /// - public enum IceCredentialType - { - /// - /// - /// - Password = 0, - - /// - /// - /// - OAuth = 1 - } - - /// - /// - /// - [Serializable] - public class IceServer - { - /// - /// - /// - public IReadOnlyCollection urls => m_urls; - - /// - /// - /// - public string username => m_username; - - /// - /// - /// - public IceCredentialType credentialType => m_credentialType; - - /// - /// - /// - public string credential => m_credential; - - [SerializeField] - private string[] m_urls; - [SerializeField] - private string m_username; - [SerializeField] - private IceCredentialType m_credentialType; - [SerializeField] - private string m_credential; - - /// - /// - /// - /// - public static implicit operator RTCIceServer(IceServer server) - { - var iceServer = new RTCIceServer - { - urls = server.urls.ToArray(), - username = server.username, - credential = server.credential, - credentialType = (RTCIceCredentialType)server.credentialType - }; - return iceServer; - } - - /// - /// - /// - /// - public IceServer Clone() - { - return new IceServer(this.urls.ToArray(), this.username, this.credentialType, this.credential); - } - - /// - /// - /// - /// - /// - /// - /// - public IceServer(string[] urls = null, string username = null, IceCredentialType credentialType = IceCredentialType.Password, string credential = null) - { - m_urls = urls?.ToArray(); - m_username = username; - m_credential = credential; - m_credentialType = credentialType; - } - - internal IceServer(RTCIceServer server) - { - m_urls = server.urls.ToArray(); - m_username = server.username; - m_credential = server.credential; - m_credentialType = (IceCredentialType)server.credentialType; - } - } - - /// - /// - /// - public abstract class SignalingSettings - { - /// - /// - /// - public abstract IReadOnlyCollection iceServers { get; } - - /// - /// - /// - public abstract Type signalingClass { get; } - } - - [Serializable] - public class HttpSignalingSettings : SignalingSettings - { - /// - /// - /// - public override Type signalingClass => typeof(HttpSignaling); - - /// - /// - /// - public override IReadOnlyCollection iceServers => m_iceServers; - - /// - /// - /// - public string url => m_url; - - /// - /// - /// - public float interval => m_interval; - - [SerializeField] - private float m_interval; - [SerializeField] - protected string m_url; - [SerializeField] - protected IceServer[] m_iceServers; - - /// - /// - /// - /// - /// - /// - public HttpSignalingSettings(string url, IceServer[] iceServers = null, float interval = 5.0f) - { - if (url == null) - throw new ArgumentNullException("url"); - if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) - throw new ArgumentException("url is not well formed Uri"); - - m_url = url; - m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); - m_interval = interval; - } - - /// - /// - /// - public HttpSignalingSettings() - { - m_url = "http://127.0.0.1"; - m_iceServers = new[] - { - new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) - }; - m_interval = 5f; - } - } - - /// - /// - /// - [Serializable] - public class WebSocketSignalingSettings : SignalingSettings - { - /// - /// - /// - public override Type signalingClass => typeof(WebSocketSignaling); - - /// - /// - /// - public override IReadOnlyCollection iceServers => m_iceServers; - - /// - /// - /// - public string url => m_url; - - [SerializeField] - protected string m_url; - [SerializeField] - protected IceServer[] m_iceServers; - - /// - /// - /// - /// - /// - public WebSocketSignalingSettings(string url, IceServer[] iceServers = null) - { - if (url == null) - throw new ArgumentNullException("url"); - if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) - throw new ArgumentException("url is not well formed Uri"); - - m_url = url; - m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); - } - - /// - /// - /// - public WebSocketSignalingSettings() - { - m_url = "ws://127.0.0.1"; - m_iceServers = new[] - { - new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) - }; - } - } - - /// - /// - /// - [Serializable] - public class FurioosSignalingSettings : SignalingSettings - { - /// - /// - /// - public override Type signalingClass => typeof(FurioosSignaling); - - /// - /// - /// - public override IReadOnlyCollection iceServers => m_iceServers; - - /// - /// - /// - public string url => m_url; - - [SerializeField] - protected string m_url; - [SerializeField] - protected IceServer[] m_iceServers; - - /// - /// - /// - /// - /// - public FurioosSignalingSettings(string url, IceServer[] iceServers = null) - { - if (url == null) - throw new ArgumentNullException("url"); - if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) - throw new ArgumentException("url is not well formed Uri"); - - m_url = url; - m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); - } - - /// - /// - /// - public FurioosSignalingSettings() - { - m_url = "http://127.0.0.1"; - m_iceServers = new[] - { - new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) - }; - } - } -} From 973f8acba5094bd01af3c8c2ba86b0f13e42f5f7 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Thu, 2 Feb 2023 14:16:10 +0900 Subject: [PATCH 038/117] feat: Create Render Streaming Project Window (#855) * create renderstreamingsettingseditor * use drawer on project settings * formatted * change to popup for settings select ui * if can't get settings from config, set default settings * check update settings view * fix test * open SaveFilePanel on create settings * rename * fix test * show path on settings select * fix * use utf8 slash * fix review --------- Co-authored-by: kazuki --- .../SignalingSettingsDrawer.cs | 11 +- .../RenderStreamingProjectSettingsProvider.cs | 121 +++++++++++++++++- .../Editor/RenderStreamingSettingsEditor.cs | 18 +++ .../RenderStreamingSettingsEditor.cs.meta | 3 + .../UXML/RenderStreamingProjectSettings.uxml | 4 +- .../Runtime/RenderStreamingSettings.asset | 2 +- .../Runtime/Scripts/RenderStreaming.cs | 8 ++ .../Scripts/RenderStreamingSettings.cs | 4 +- .../Tests/Runtime/RenderStreamingTest.cs | 13 +- 9 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs.meta diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs index 8a80e39f1..28218e4e2 100644 --- a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs @@ -29,16 +29,14 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) ISignalingSettingEditor CreateEditor(SerializedProperty property) { - var handler = property.serializedObject.targetObject as SignalingManager; - var settings = handler.GetSignalingSettings(); + var settings = fieldInfo.GetValue(property.serializedObject.targetObject) as SignalingSettings; var type = CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(settings.GetType()); return Activator.CreateInstance(type) as ISignalingSettingEditor; } VisualElement CreatePopUpSignalingType(SerializedProperty property, string label) { - var handler = property.serializedObject.targetObject as SignalingManager; - var settings = handler.GetSignalingSettings(); + var settings = fieldInfo.GetValue(property.serializedObject.targetObject) as SignalingSettings; var defaultValue = CustomSignalingSettingsEditor.FindLabelByInspectedType(settings.GetType()); var choices = CustomSignalingSettingsEditor.Labels().ToList(); var element = new PopupField(label: label, choices: choices, defaultValue: defaultValue); @@ -48,12 +46,10 @@ VisualElement CreatePopUpSignalingType(SerializedProperty property, string label void OnChangedValue(ChangeEvent e, SerializedProperty property) { - var handler = property.serializedObject.targetObject as SignalingManager; - if (handler == null) + if(!(fieldInfo.GetValue(property.serializedObject.targetObject) is SignalingSettings settings)) return; // cache current settings. - var settings = handler.GetSignalingSettings(); var type = settings.GetType(); table[type] = settings; @@ -64,6 +60,7 @@ void OnChangedValue(ChangeEvent e, SerializedProperty property) var newSettings = Activator.CreateInstance(inspectedType) as SignalingSettings; table.Add(inspectedType, newSettings); } + property.managedReferenceValue = table[inspectedType]; property.serializedObject.ApplyModifiedProperties(); diff --git a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs index 744693e14..1f67e343d 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.IO; using System.Linq; using UnityEditor; using UnityEditor.UIElements; @@ -10,6 +12,13 @@ namespace Unity.RenderStreaming internal class RenderStreamingProjectSettingsProvider : SettingsProvider { internal VisualElement rootVisualElement { get; private set; } + private bool isDefaultSettings => AssetDatabase.GetAssetPath(RenderStreaming.Settings) == + RenderStreaming.DefaultRenderStreamingSettingsPath; + private bool noSettingsInAssets => availableRenderStreamingSettingsAssets.Length == 0 + || availableRenderStreamingSettingsAssets.All(x => x == RenderStreaming.DefaultRenderStreamingSettingsPath); + private string[] availableRenderStreamingSettingsAssets; + private int currentSelectedSettingsAsset; + private RenderStreamingSettings settings; const string kSettingsPath = "Project/Render Streaming"; const string kTemplatePath = "Packages/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml"; @@ -43,16 +52,116 @@ public override void OnActivate(string searchContext, VisualElement rootElement) template.CloneTree(newVisualElement); rootVisualElement.Add(newVisualElement); - var renderStreamingSettingsField = rootVisualElement.Q("renderStreamingSettingsField"); - renderStreamingSettingsField.SetValueWithoutNotify(RenderStreaming.Settings); - renderStreamingSettingsField.RegisterCallback>(ev => + settings = RenderStreaming.Settings; + availableRenderStreamingSettingsAssets = FindRenderStreamingSettingsPathInProject(); + + var selectorContainer = rootVisualElement.Q("renderStreamingSettingsSelector"); + + var defaultIndex = ArrayHelpers.IndexOf(availableRenderStreamingSettingsAssets, AssetDatabase.GetAssetPath(settings)); + var choices = availableRenderStreamingSettingsAssets.Select(x => x.Replace("/", " \u2215 ")).ToList(); + var selectPopup = new PopupField(label: label, choices: choices, defaultIndex: defaultIndex) { - var settings = ev.newValue as RenderStreamingSettings; - if (settings != null) + name = "renderStreamingSettingsSelectPopup" + }; + selectPopup.RegisterValueChangedCallback(evt => + { + currentSelectedSettingsAsset = selectPopup.index; + var newSettings = + AssetDatabase.LoadAssetAtPath(availableRenderStreamingSettingsAssets[currentSelectedSettingsAsset]); + if (newSettings == settings) { - RenderStreaming.Settings = settings; + return; } + + RenderStreaming.Settings = newSettings; }); + selectorContainer.Add(selectPopup); + + var createSettingsButton = new Button {text = "Create New Settings Asset"}; + createSettingsButton.clicked += () => + { + CreateNewSettingsAsset(); + Repaint(); + }; + selectorContainer.Add(createSettingsButton); + + var createAssetHelpBox = new HelpBox("Settings for the Render Streaming are not stored in an asset. Click the button above to create a settings asset you can edit.", HelpBoxMessageType.Info) + { + style = {display = noSettingsInAssets ? DisplayStyle.Flex : DisplayStyle.None} + }; + selectorContainer.Add(createAssetHelpBox); + + ShowRenderStreamingSettingsProperty(); + } + + public override void OnInspectorUpdate() + { + if (RenderStreaming.Settings == settings && settings != null) + { + return; + } + + settings = RenderStreaming.Settings; + var index = ArrayHelpers.IndexOf(availableRenderStreamingSettingsAssets, AssetDatabase.GetAssetPath(settings)); + rootVisualElement.Q>("renderStreamingSettingsSelectPopup").index = index; + ShowRenderStreamingSettingsProperty(); + } + + private static string[] FindRenderStreamingSettingsPathInProject() + { + var guids = AssetDatabase.FindAssets("t:RenderStreamingSettings"); + return guids.Select(AssetDatabase.GUIDToAssetPath).ToArray(); + } + + private static void CreateNewSettingsAsset() + { + // Query for file name. + var projectName = PlayerSettings.productName; + var path = EditorUtility.SaveFilePanel("Create Render Streaming Settings File", "Assets", + projectName, "asset"); + if (string.IsNullOrEmpty(path)) + return; + + // Make sure the path is in the Assets/ folder. + var dataPath = Application.dataPath + "/"; + if (!path.StartsWith(dataPath, StringComparison.CurrentCultureIgnoreCase)) + { + Debug.LogError($"Render Streaming settings must be stored in Assets folder of the project (got: '{path}')"); + return; + } + + // Make sure it ends with .asset. + var extension = Path.GetExtension(path); + if (string.Compare(extension, ".asset", StringComparison.InvariantCultureIgnoreCase) != 0) + path += ".asset"; + + // Create settings file. + var relativePath = "Assets/" + path.Substring(dataPath.Length); + CreateNewSettingsAsset(relativePath); + } + + private static void CreateNewSettingsAsset(string relativePath) + { + var settings = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(settings, relativePath); + EditorGUIUtility.PingObject(settings); + RenderStreaming.Settings = settings; + } + + private void ShowRenderStreamingSettingsProperty() + { + var settingsPropertyContainer = rootVisualElement.Q("settingsPropertyContainer"); + settingsPropertyContainer.Clear(); + + var editor = UnityEditor.Editor.CreateEditor(settings); + var inspectorGUI = editor.CreateInspectorGUI(); + inspectorGUI.SetEnabled(!isDefaultSettings); + inspectorGUI.Bind(editor.serializedObject); + if (!noSettingsInAssets && isDefaultSettings) + { + settingsPropertyContainer.Add(new HelpBox("This is package default settings. Please select other settings asset you can edit.", HelpBoxMessageType.Info)); + } + settingsPropertyContainer.Add(inspectorGUI); } public RenderStreamingProjectSettingsProvider(string path, SettingsScope scopes, IEnumerable keywords = null) diff --git a/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs b/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs new file mode 100644 index 000000000..11878fc45 --- /dev/null +++ b/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs @@ -0,0 +1,18 @@ +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Unity.RenderStreaming.Editor +{ + [CustomEditor(typeof(RenderStreamingSettings))] + internal class RenderStreamingSettingsEditor : UnityEditor.Editor + { + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + root.Add(new PropertyField(serializedObject.FindProperty("automaticStreaming"), "Automatic Streaming")); + root.Add(new PropertyField(serializedObject.FindProperty("signalingSettings"), "Signaling Settings")); + return root; + } + } +} diff --git a/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs.meta b/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs.meta new file mode 100644 index 000000000..23d87b450 --- /dev/null +++ b/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 10aca1086d5f4165b559555ea5d48ef0 +timeCreated: 1675058568 \ No newline at end of file diff --git a/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml b/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml index 8923b6e77..716842537 100644 --- a/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml +++ b/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml @@ -1,7 +1,7 @@ - - + + diff --git a/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset b/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset index efa992e7c..7a9d8c94e 100644 --- a/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset +++ b/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset @@ -12,7 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: bc8ba17751ad4107a50fa1415017b6b1, type: 3} m_Name: RenderStreamingSettings m_EditorClassIdentifier: - automaticStreaming: 0 + automaticStreaming: 1 signalingSettings: id: 0 references: diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs index b048438c5..9b6958922 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs @@ -105,6 +105,14 @@ private static void InitializeInEditor() { m_running = change == PlayModeStateChange.EnteredPlayMode; }; + + EditorApplication.projectChanged += () => + { + if (!EditorBuildSettings.TryGetConfigObject(EditorBuildSettingsConfigKey, out RenderStreamingSettings _)) + { + Settings = AssetDatabase.LoadAssetAtPath(DefaultRenderStreamingSettingsPath); + } + }; } #endif diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs index 90492d13a..40bb817d8 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs @@ -5,6 +5,8 @@ namespace Unity.RenderStreaming public class RenderStreamingSettings : ScriptableObject { [SerializeField] public bool automaticStreaming; - [SerializeReference] public SignalingSettings signalingSettings = new WebSocketSignalingSettings(); + + [SerializeReference, SignalingSettings] + public SignalingSettings signalingSettings = new WebSocketSignalingSettings(); } } diff --git a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs index ab72de8d5..3a0a5ac49 100644 --- a/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/RenderStreamingTest.cs @@ -69,7 +69,7 @@ public void CheckDefaultSettings() defaultSettings = Resources.FindObjectsOfTypeAll().FirstOrDefault() ?? ScriptableObject.CreateInstance(); #endif - Assert.That(defaultSettings.automaticStreaming, Is.False); + Assert.That(defaultSettings.automaticStreaming, Is.True); var defaultSignalingSettings = defaultSettings.signalingSettings as WebSocketSignalingSettings; Assert.That(defaultSignalingSettings, Is.Not.Null); Assert.That(defaultSignalingSettings.signalingClass, Is.EqualTo(typeof(WebSocketSignaling))); @@ -88,20 +88,27 @@ public void Settings() RenderStreaming.Settings = settings; Assert.That(RenderStreaming.Settings.automaticStreaming, Is.EqualTo(settings.automaticStreaming)); Assert.That(RenderStreaming.Settings.signalingSettings, Is.EqualTo(settings.signalingSettings)); + + Object.DestroyImmediate(settings); } [Test] public void AutomaticStreaming() { - RenderStreaming.AutomaticStreaming = true; + var settings = ScriptableObject.CreateInstance(); + settings.automaticStreaming = false; + settings.signalingSettings = new WebSocketSignalingSettings(); + RenderStreaming.Settings = settings; + RenderStreaming.AutomaticStreaming = true; var automaticStreaming = Object.FindObjectOfType(); Assert.That(automaticStreaming, Is.Not.Null); RenderStreaming.AutomaticStreaming = false; - automaticStreaming = Object.FindObjectOfType(); Assert.That(automaticStreaming, Is.Null); + + Object.DestroyImmediate(settings); } } } From 48ab2b6eb253ace0e65deabb4863ea81caac5428 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 3 Feb 2023 18:05:04 +0900 Subject: [PATCH 039/117] feat: Define SignalingSettings asset to make portable signaling configurations (#854) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix SignalingManager inspector * fix bugs * fix * fix * fix * fix * fix --- .../SignalingSettingsDrawer.cs | 73 ++-- .../RenderStreamingProjectSettingsProvider.cs | 20 +- .../Editor/SignalingManagerEditor.cs | 202 ++++++++++- .../Runtime/Scripts/AutomaticStreaming.cs | 2 - .../Scripts/RenderStreamingSettings.cs | 6 + .../Signaling/SignalingSettingsObject.cs | 2 +- .../Runtime/Scripts/SignalingManager.cs | 78 +++- .../Runtime/SignalingSettings.asset | 28 ++ .../Runtime/SignalingSettings.asset.meta | 8 + .../ARFoundation/ARFoundationSample.cs | 6 +- .../Bidirectional/BidirectionalSample.cs | 6 +- .../Example/Broadcast/BroadcastSample.cs | 6 +- .../Samples~/Example/Gyro/GyroSample.cs | 6 +- .../Samples~/Example/Menu/Menu.unity | 343 +++++++++++++++++- .../Example/Multiplay/MultiplaySample.cs | 16 +- .../Example/Receiver/ReceiverSample.cs | 7 +- .../Samples~/Example/Scripts/SceneSelectUI.cs | 38 +- .../WebBrowserInput/WebBrowserInputSample.cs | 12 +- .../Tests/Editor/EditorTest.cs | 9 + .../Tests/Runtime/SignalingManagerTest.cs | 10 +- 20 files changed, 789 insertions(+), 89 deletions(-) create mode 100644 com.unity.renderstreaming/Runtime/SignalingSettings.asset create mode 100644 com.unity.renderstreaming/Runtime/SignalingSettings.asset.meta diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs index 28218e4e2..2ad79255c 100644 --- a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using UnityEngine; using UnityEditor; using UnityEditor.UIElements; using UnityEngine.UIElements; @@ -12,6 +11,7 @@ namespace Unity.RenderStreaming.Editor class SignalingSettingsDrawer : PropertyDrawer { private VisualElement editorGUI; + private PopupField popupFieldSignalingType; private ISignalingSettingEditor editor; private Dictionary table = new Dictionary(); @@ -19,10 +19,14 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) { editor = CreateEditor(property); var root = new VisualElement(); + root.RegisterCallback(OnSignalingSettingsObjectChange, property); + var box = new Box(); root.Add(box); editorGUI = editor.CreateInspectorGUI(property); - box.Add(CreatePopUpSignalingType(property, "Signaling Type")); + popupFieldSignalingType = CreatePopUpSignalingType(property, "Signaling Type"); + popupFieldSignalingType.RegisterValueChangedCallback(e => OnPopupFieldValueChange(e, property)); + box.Add(popupFieldSignalingType); box.Add(editorGUI); return root; } @@ -34,17 +38,34 @@ ISignalingSettingEditor CreateEditor(SerializedProperty property) return Activator.CreateInstance(type) as ISignalingSettingEditor; } - VisualElement CreatePopUpSignalingType(SerializedProperty property, string label) + PopupField CreatePopUpSignalingType(SerializedProperty property, string label) { var settings = fieldInfo.GetValue(property.serializedObject.targetObject) as SignalingSettings; var defaultValue = CustomSignalingSettingsEditor.FindLabelByInspectedType(settings.GetType()); var choices = CustomSignalingSettingsEditor.Labels().ToList(); - var element = new PopupField(label: label, choices: choices, defaultValue: defaultValue); - element.RegisterValueChangedCallback(e => OnChangedValue(e, property)); - return element; + return new PopupField(label: label, choices: choices, defaultValue: defaultValue); + } + + static void ReplaceVisualElement(VisualElement oldValue, VisualElement newValue) + { + var root = oldValue.parent; + var index = root.IndexOf(oldValue); + root.Remove(oldValue); + root.Insert(index, newValue); + } + + void OnSignalingSettingsObjectChange(SerializedPropertyChangeEvent e, SerializedProperty property) + { + var settings = fieldInfo.GetValue(property.serializedObject.targetObject) as SignalingSettings; + var label = CustomSignalingSettingsEditor.FindLabelByInspectedType(settings.GetType()); + + if (popupFieldSignalingType.value == label) + return; + popupFieldSignalingType.value = label; + RecreateEditorGUI(label, property); } - void OnChangedValue(ChangeEvent e, SerializedProperty property) + void OnPopupFieldValueChange(ChangeEvent e, SerializedProperty property) { if(!(fieldInfo.GetValue(property.serializedObject.targetObject) is SignalingSettings settings)) return; @@ -54,6 +75,11 @@ void OnChangedValue(ChangeEvent e, SerializedProperty property) table[type] = settings; var label = e.newValue; + RecreateEditorGUI(label, property); + } + + void RecreateEditorGUI(string label, SerializedProperty property) + { var inspectedType = CustomSignalingSettingsEditor.FindInspectedTypeByLabel(label); if (!table.ContainsKey(inspectedType)) { @@ -65,22 +91,18 @@ void OnChangedValue(ChangeEvent e, SerializedProperty property) property.serializedObject.ApplyModifiedProperties(); var inspectorType = CustomSignalingSettingsEditor.FindInspectorTypeByInspectedType(inspectedType); - var editor = Activator.CreateInstance(inspectorType) as ISignalingSettingEditor; + editor = Activator.CreateInstance(inspectorType) as ISignalingSettingEditor; var newValue = editor.CreateInspectorGUI(property); - ReplaceElement(editorGUI, newValue); + + // Unbind old element to serializedObject. + editorGUI.Unbind(); + + ReplaceVisualElement(editorGUI, newValue); editorGUI = newValue; - // bind new serializedObject. + // bind new element to serializedObject. editorGUI.Bind(property.serializedObject); } - - static void ReplaceElement(VisualElement oldValue, VisualElement newValue) - { - var root = oldValue.parent; - var index = root.IndexOf(oldValue); - root.Remove(oldValue); - root.Insert(index, newValue); - } } /// @@ -102,11 +124,6 @@ public VisualElement CreateInspectorGUI(SerializedProperty property) root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); return root; } - - public void SetSignalingSettings(SignalingSettings settings) - { - throw new System.NotImplementedException(); - } } [CustomSignalingSettingsEditor(typeof(WebSocketSignalingSettings), "WebSocket")] @@ -119,11 +136,6 @@ public VisualElement CreateInspectorGUI(SerializedProperty property) root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); return root; } - - public void SetSignalingSettings(SignalingSettings settings) - { - throw new System.NotImplementedException(); - } } [CustomSignalingSettingsEditor(typeof(FurioosSignalingSettings), "Furioos")] @@ -136,10 +148,5 @@ public VisualElement CreateInspectorGUI(SerializedProperty property) root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); return root; } - - public void SetSignalingSettings(SignalingSettings settings) - { - throw new System.NotImplementedException(); - } } } diff --git a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs index 1f67e343d..d4230f293 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs @@ -58,7 +58,7 @@ public override void OnActivate(string searchContext, VisualElement rootElement) var selectorContainer = rootVisualElement.Q("renderStreamingSettingsSelector"); var defaultIndex = ArrayHelpers.IndexOf(availableRenderStreamingSettingsAssets, AssetDatabase.GetAssetPath(settings)); - var choices = availableRenderStreamingSettingsAssets.Select(x => x.Replace("/", " \u2215 ")).ToList(); + var choices = availableRenderStreamingSettingsAssets.ToList(); var selectPopup = new PopupField(label: label, choices: choices, defaultIndex: defaultIndex) { name = "renderStreamingSettingsSelectPopup" @@ -92,6 +92,11 @@ public override void OnActivate(string searchContext, VisualElement rootElement) selectorContainer.Add(createAssetHelpBox); ShowRenderStreamingSettingsProperty(); + + // Disable UI when running in Playmode + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + if(EditorApplication.isPlaying) + rootVisualElement.SetEnabled(false); } public override void OnInspectorUpdate() @@ -148,6 +153,19 @@ private static void CreateNewSettingsAsset(string relativePath) RenderStreaming.Settings = settings; } + private void OnPlayModeStateChanged(PlayModeStateChange e) + { + switch (e) + { + case PlayModeStateChange.EnteredPlayMode: + rootVisualElement.SetEnabled(false); + break; + case PlayModeStateChange.ExitingPlayMode: + rootVisualElement.SetEnabled(true); + break; + } + } + private void ShowRenderStreamingSettingsProperty() { var settingsPropertyContainer = rootVisualElement.Q("settingsPropertyContainer"); diff --git a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs index 9c88affb7..281b39e4a 100644 --- a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs +++ b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs @@ -1,5 +1,8 @@ +using System; +using System.Linq; using UnityEditor; using UnityEditor.UIElements; +using UnityEngine; using UnityEngine.UIElements; namespace Unity.RenderStreaming.Editor @@ -10,13 +13,208 @@ namespace Unity.RenderStreaming.Editor [CustomEditor(typeof(SignalingManager))] internal class SignalingManagerEditor : UnityEditor.Editor { + const string DefaultSignalingSettingsSavePath = + "Assets/SignalingSettings.asset"; + + const string DefaultSignalingSettingsLoadPath = + "Packages/com.unity.renderstreaming/Runtime/SignalingSettings.asset"; + + VisualElement root; + Button openProjectSettingsButton; + PopupField signalingSettingsPopupField; + PropertyField signalingSettingsField; + public override VisualElement CreateInspectorGUI() { - var root = new VisualElement(); - root.Add(new PropertyField(serializedObject.FindProperty("signalingSettings"), "Signaling Settings")); + root = new VisualElement(); + bool useDefault = serializedObject.FindProperty("m_useDefault").boolValue; + + var useDefaultField = new PropertyField(serializedObject.FindProperty("m_useDefault"), "Use Default Settings in Project Settings"); + useDefaultField.RegisterValueChangeCallback(OnChangeUseDefault); + openProjectSettingsButton = new Button { text = "Open Project Setings" }; + openProjectSettingsButton.clicked += OnClickedOpenProjectSettingsButton; + signalingSettingsPopupField = CreatePopUpSignalingType(serializedObject.FindProperty("signalingSettingsObject"), "Signaling Settings Asset"); + signalingSettingsPopupField.RegisterValueChangedCallback(OnValueChangeSignalingSettingsObject); + signalingSettingsField = new PropertyField(serializedObject.FindProperty("signalingSettings"), "Signaling Settings"); + signalingSettingsField.RegisterValueChangeCallback(OnValueChangeSignalingSettings); + + root.Add(useDefaultField); + root.Add(openProjectSettingsButton); + root.Add(signalingSettingsPopupField); + root.Add(signalingSettingsField); + if (useDefault) + { + signalingSettingsPopupField.style.display = DisplayStyle.None; + signalingSettingsField.style.display = DisplayStyle.None; + } + else + { + openProjectSettingsButton.style.display = DisplayStyle.None; + } root.Add(new ReorderableListField(serializedObject.FindProperty("handlers"), "Signaling Handler List")); root.Add(new PropertyField(serializedObject.FindProperty("runOnAwake"), "Run On Awake")); + + EditorApplication.projectChanged += OnProjectChanged; + + // Disable UI when running in Playmode + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + if (EditorApplication.isPlaying) + root.SetEnabled(false); return root; } + + PopupField CreatePopUpSignalingType(SerializedProperty property, string label) + { + var asset = property.objectReferenceValue as SignalingSettingsObject; + var paths = GetAvailableSignalingSettingsPath(); + + var field = new PopupField(label: label); + field.formatSelectedValueCallback = v => AssetDatabase.GetAssetPath(v); + field.formatListItemCallback = v => AssetDatabase.GetAssetPath(v); + if (paths.Length == 0) + return field; + var availableObjects = paths.Select(path => AssetDatabase.LoadAssetAtPath(path)).ToArray(); + var defaultIndex = ArrayHelpers.IndexOf(availableObjects, asset); + field.choices = availableObjects.ToList(); + field.index = defaultIndex < 0 ? 0 : defaultIndex; + return field; + } + + static string[] GetAvailableSignalingSettingsPath() + { + var guids = AssetDatabase.FindAssets("t:SignalingSettingsObject"); + return guids.Select(AssetDatabase.GUIDToAssetPath).Where(_ => _.StartsWith("Assets")).ToArray(); + } + + static bool IsValidSignalingSettingsObject(SignalingSettingsObject asset) + { + if (asset == null) + return false; + if (AssetDatabase.GetAssetPath(asset).IndexOf("Assets", StringComparison.Ordinal) != 0) + return false; + return true; + } + + void CreateDefaultSignalingSettings() + { + // Create Default SignalingSettings in Assets folder when the useDefault flag is turned off first time. + SignalingSettingsObject asset = AssetDatabase.LoadAssetAtPath(DefaultSignalingSettingsSavePath); + if (asset == null) + { + if (!AssetDatabase.CopyAsset(DefaultSignalingSettingsLoadPath, DefaultSignalingSettingsSavePath)) + { + Debug.LogError("CopyAssets is failed."); + return; + } + asset = AssetDatabase.LoadAssetAtPath(DefaultSignalingSettingsSavePath); + } + var handler = serializedObject.targetObject as SignalingManager; + handler.signalingSettingsObject = asset; + handler.SetSignalingSettings(handler.signalingSettingsObject.settings); + } + + private void OnPlayModeStateChanged(PlayModeStateChange e) + { + switch (e) + { + case PlayModeStateChange.EnteredPlayMode: + root.SetEnabled(false); + break; + case PlayModeStateChange.ExitingPlayMode: + root.SetEnabled(true); + break; + } + } + + private void OnProjectChanged() + { + var paths = GetAvailableSignalingSettingsPath(); + + // Force to use default settings if there are no available settings in project folder. + if (paths.Length == 0) + { + serializedObject.FindProperty("m_useDefault").boolValue = true; + serializedObject.ApplyModifiedProperties(); + return; + } + + var asset = serializedObject.FindProperty("signalingSettingsObject").objectReferenceValue; + var availableObjects = paths.Select(path => AssetDatabase.LoadAssetAtPath(path)).ToArray(); + var defaultIndex = ArrayHelpers.IndexOf(availableObjects, asset); + if (defaultIndex < 0) + { + defaultIndex = 0; + using var e = ChangeEvent.GetPooled(null, availableObjects[defaultIndex]); + e.target = signalingSettingsPopupField; + root.SendEvent(e); + } + signalingSettingsPopupField.choices = availableObjects.ToList(); + signalingSettingsPopupField.index = defaultIndex; + + } + + private void OnClickedOpenProjectSettingsButton() + { + SettingsService.OpenProjectSettings("Project/Render Streaming"); + } + + private void OnChangeUseDefault(SerializedPropertyChangeEvent e) + { + bool useDefault = e.changedProperty.boolValue; + if (useDefault) + { + signalingSettingsPopupField.style.display = DisplayStyle.None; + signalingSettingsField.style.display = DisplayStyle.None; + openProjectSettingsButton.style.display = DisplayStyle.Flex; + } + else + { + signalingSettingsPopupField.style.display = DisplayStyle.Flex; + signalingSettingsField.style.display = DisplayStyle.Flex; + openProjectSettingsButton.style.display = DisplayStyle.None; + + var property = serializedObject.FindProperty("signalingSettingsObject"); + if (!IsValidSignalingSettingsObject(property.objectReferenceValue as SignalingSettingsObject)) + { + CreateDefaultSignalingSettings(); + } + } + } + + private void OnValueChangeSignalingSettingsObject(ChangeEvent e) + { + var asset = e.newValue; + if (asset == null) + { + Debug.LogError("Setting None is not allowed for this parameter. Reverted."); + return; + } + if (AssetDatabase.GetAssetPath(asset).IndexOf("Assets", StringComparison.Ordinal) != 0) + { + Debug.LogError("Setting an asset not placed under Assets folder is not allowed for this parameter. Reverted."); + return; + } + var property = serializedObject.FindProperty("signalingSettingsObject"); + property.objectReferenceValue = asset; + + serializedObject.ApplyModifiedProperties(); + + var handler = serializedObject.targetObject as SignalingManager; + handler.SetSignalingSettings(asset.settings); + + // Send event to repaint SignalingSettingsDrawer. + using SerializedPropertyChangeEvent changeEvent = SerializedPropertyChangeEvent.GetPooled(); + changeEvent.changedProperty = property; + changeEvent.target = signalingSettingsField.Children().First(); + root.SendEvent(changeEvent); + } + + private void OnValueChangeSignalingSettings(SerializedPropertyChangeEvent e) + { + // Update SignalingSettings in ScriptableObject. + var handler = serializedObject.targetObject as SignalingManager; + if (handler.signalingSettingsObject != null) + handler.signalingSettingsObject.settings = handler.GetSignalingSettings(); + } } } diff --git a/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs index 4eb08b72f..06b4a3707 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AutomaticStreaming.cs @@ -38,8 +38,6 @@ private void Awake() renderstreaming = gameObject.AddComponent(); renderstreaming.AddSignalingHandler(broadcast); - var signalingSettings = RenderStreaming.GetSignalingSettings(); - renderstreaming.SetSignalingSettings(signalingSettings); renderstreaming.Run(); SceneManager.activeSceneChanged += (scene1, scene2) => diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs index 40bb817d8..2a7ff0911 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs @@ -2,8 +2,14 @@ namespace Unity.RenderStreaming { + /// + /// + /// public class RenderStreamingSettings : ScriptableObject { + /// + /// + /// [SerializeField] public bool automaticStreaming; [SerializeReference, SignalingSettings] diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs index 533b3148e..f2f6b5391 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettingsObject.cs @@ -7,7 +7,7 @@ internal class SignalingSettingsObject : ScriptableObject [SerializeReference] public SignalingSettings settings; - public static ScriptableObject Create() + public static SignalingSettingsObject Create() { var instance = CreateInstance(); instance.settings = new WebSocketSignalingSettings(); diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs index dae9ec5d0..ceef9903f 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs @@ -16,16 +16,14 @@ namespace Unity.RenderStreaming public sealed class SignalingManager : MonoBehaviour { #pragma warning disable 0649 - // ToDo: Create component UI on URS-553 + [SerializeField] + private bool m_useDefault = true; + + [SerializeField] + internal SignalingSettingsObject signalingSettingsObject; + [SerializeReference, SignalingSettings] - private SignalingSettings signalingSettings = new WebSocketSignalingSettings - ( - url: "ws://127.0.0.1:80", - iceServers: new[] - { - new IceServer(urls: new[] {"stun:stun.l.google.com:19302"}) - } - ); + private SignalingSettings signalingSettings = new WebSocketSignalingSettings(); [SerializeField, Tooltip("List of handlers of signaling process.")] private List handlers = new List(); @@ -53,6 +51,15 @@ static ISignaling CreateSignaling(SignalingSettings settings, SynchronizationCon return (ISignaling)Activator.CreateInstance(settings.signalingClass, args); } + /// + /// Use settings in Project Settings. + /// + public bool useDefaultSettings + { + get { return m_useDefault; } + set { m_useDefault = value; } + } + /// /// /// @@ -69,6 +76,10 @@ public void SetSignalingSettings(SignalingSettings settings) signalingSettings = settings; } + /// + /// + /// + /// public SignalingSettings GetSignalingSettings() { return signalingSettings; @@ -138,6 +149,45 @@ public void Run( _Run(conf, signaling, handlers); } +// void OnValidate() +// { +//#if UNITY_EDITOR +// if (Application.isPlaying) +// return; + +// if (!m_useDefault) +// { +// if (!IsValidSignalingSettingsObject(signalingSettingsObject)) +// { +// // Create Default SignalingSettings in Assets folder when the useDefault flag is turned off first time. +// SignalingSettingsObject obj = AssetDatabase.LoadAssetAtPath(DefaultSignalingSettingsSavePath); +// if (obj == null) +// { +// if (!AssetDatabase.CopyAsset(DefaultSignalingSettingsLoadPath, DefaultSignalingSettingsSavePath)) +// { +// Debug.LogError("CopyAssets is failed."); +// return; +// } +// obj = AssetDatabase.LoadAssetAtPath(DefaultSignalingSettingsSavePath); +// } +// signalingSettingsObject = obj; +// signalingSettings = signalingSettingsObject.settings; +// } +// } +//#endif +// } + +#if UNITY_EDITOR + bool IsValidSignalingSettingsObject(SignalingSettingsObject asset) + { + if (asset == null) + return false; + if (AssetDatabase.GetAssetPath(asset).IndexOf("Assets", StringComparison.Ordinal) != 0) + return false; + return true; + } +#endif + /// /// /// @@ -150,11 +200,12 @@ private void _Run( SignalingHandlerBase[] handlers = null ) { - RTCIceServer[] iceServers = signalingSettings.iceServers.OfType().ToArray(); + var settings = m_useDefault ? RenderStreaming.GetSignalingSettings() : signalingSettings; + RTCIceServer[] iceServers = settings.iceServers.OfType().ToArray(); RTCConfiguration _conf = conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers }); - ISignaling _signaling = signaling ?? CreateSignaling(signalingSettings, SynchronizationContext.Current); + ISignaling _signaling = signaling ?? CreateSignaling(settings, SynchronizationContext.Current); RenderStreamingDependencies dependencies = new RenderStreamingDependencies { config = _conf, @@ -193,9 +244,10 @@ void Awake() if (!runOnAwake || m_running || handlers.Count == 0) return; - RTCIceServer[] iceServers = signalingSettings.iceServers.Cast().ToArray(); + var settings = m_useDefault ? RenderStreaming.GetSignalingSettings() : signalingSettings; + RTCIceServer[] iceServers = settings.iceServers.OfType().ToArray(); RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers }; - ISignaling signaling = CreateSignaling(signalingSettings, SynchronizationContext.Current); + ISignaling signaling = CreateSignaling(settings, SynchronizationContext.Current); _Run(conf, signaling, handlers.ToArray()); } diff --git a/com.unity.renderstreaming/Runtime/SignalingSettings.asset b/com.unity.renderstreaming/Runtime/SignalingSettings.asset new file mode 100644 index 000000000..71d94eb1e --- /dev/null +++ b/com.unity.renderstreaming/Runtime/SignalingSettings.asset @@ -0,0 +1,28 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 22c294fb286b37446a86664f4fb41023, type: 3} + m_Name: SignalingSettings + m_EditorClassIdentifier: + settings: + id: 0 + references: + version: 1 + 00000000: + type: {class: WebSocketSignalingSettings, ns: Unity.RenderStreaming, asm: Unity.RenderStreaming} + data: + m_url: ws://127.0.0.1 + m_iceServers: + - m_urls: + - stun:stun.l.google.com:19302 + m_username: + m_credentialType: 0 + m_credential: diff --git a/com.unity.renderstreaming/Runtime/SignalingSettings.asset.meta b/com.unity.renderstreaming/Runtime/SignalingSettings.asset.meta new file mode 100644 index 000000000..4f513e82c --- /dev/null +++ b/com.unity.renderstreaming/Runtime/SignalingSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 65662d71af45cc24fa4da622b2a770f9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs index c2f1d5038..015fbd2c8 100644 --- a/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/ARFoundation/ARFoundationSample.cs @@ -48,7 +48,11 @@ IEnumerator Start() { if (!renderStreaming.runOnAwake) { - renderStreaming.Run(signaling: settings?.Signaling); + if (settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(); } if ((ARSession.state == ARSessionState.None ) || diff --git a/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs b/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs index efa1025c7..bac22e8c6 100644 --- a/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Bidirectional/BidirectionalSample.cs @@ -76,7 +76,11 @@ void Start() { if (renderStreaming.runOnAwake) return; - renderStreaming.Run(signaling: settings?.Signaling); + if (settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(); } private void SetUp() diff --git a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs index 31b5cfbf7..7485744ad 100644 --- a/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Broadcast/BroadcastSample.cs @@ -186,7 +186,11 @@ private void Start() if (renderStreaming.runOnAwake) return; - renderStreaming.Run(signaling: settings?.Signaling); + if(settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(); inputReceiver.OnStartedChannel += OnStartedChannel; var map = inputReceiver.currentActionMap; diff --git a/com.unity.renderstreaming/Samples~/Example/Gyro/GyroSample.cs b/com.unity.renderstreaming/Samples~/Example/Gyro/GyroSample.cs index 2f0f7b3fa..2e0deb2c9 100644 --- a/com.unity.renderstreaming/Samples~/Example/Gyro/GyroSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Gyro/GyroSample.cs @@ -46,7 +46,11 @@ void Start() { if (renderStreaming.runOnAwake) return; - renderStreaming.Run(signaling: settings?.Signaling); + if (settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(); } void OnEnable() diff --git a/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity b/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity index 555a85cd2..5b25e5a48 100644 --- a/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity +++ b/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity @@ -1299,6 +1299,7 @@ MonoBehaviour: type: 3} m_DeselectOnBackgroundClick: 1 m_PointerBehavior: 0 + m_CursorLockBehavior: 0 --- !u!114 &264030626 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1446,6 +1447,7 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 1632855334} + - {fileID: 1341972279} - {fileID: 1241864396} - {fileID: 1305239481} - {fileID: 789249148} @@ -1456,7 +1458,7 @@ RectTransform: m_AnchorMin: {x: 1, y: 0} m_AnchorMax: {x: 1, y: 0} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 600, y: 400} + m_SizeDelta: {x: 600, y: 471.9563} m_Pivot: {x: 1, y: 0} --- !u!1 &403658633 GameObject: @@ -2970,6 +2972,81 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 661700338} m_CullTransparentMesh: 1 +--- !u!1 &690401032 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 690401033} + - component: {fileID: 690401035} + - component: {fileID: 690401034} + m_Layer: 5 + m_Name: Checkmark + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &690401033 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 690401032} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1922333361} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 40, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &690401034 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 690401032} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &690401035 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 690401032} + m_CullTransparentMesh: 1 --- !u!1 &721044881 GameObject: m_ObjectHideFlags: 0 @@ -3280,11 +3357,11 @@ RectTransform: - {fileID: 1225082140} - {fileID: 2087068098} m_Father: {fileID: 397743427} - m_RootOrder: 3 + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 295, y: -238} + m_AnchoredPosition: {x: 294, y: -303.257} m_SizeDelta: {x: 584, y: 100} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &964629093 @@ -4809,6 +4886,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 8d6df25b96db2814daa5608049a81529, type: 3} m_Name: m_EditorClassIdentifier: + toggleUseDefaultSettings: {fileID: 1341972280} dropdownSignalingType: {fileID: 404516253} inputFieldSignalingAddress: {fileID: 2087068099} toggleSignalingSecured: {fileID: 1305239482} @@ -4867,12 +4945,12 @@ RectTransform: - {fileID: 595137781} - {fileID: 404516252} m_Father: {fileID: 397743427} - m_RootOrder: 1 + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 129.78998} - m_SizeDelta: {x: 541.684, y: 80.9345} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 299, y: -135.46701} + m_SizeDelta: {x: 541.684, y: 80.93451} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1305239480 GameObject: @@ -4905,11 +4983,11 @@ RectTransform: - {fileID: 1850475806} - {fileID: 134408846} m_Father: {fileID: 397743427} - m_RootOrder: 2 + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 153, y: -148} + m_AnchoredPosition: {x: 152, y: -213.25699} m_SizeDelta: {x: 300, y: 80} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1305239482 @@ -5068,6 +5146,92 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1326775951} m_CullTransparentMesh: 1 +--- !u!1 &1341972278 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1341972279} + - component: {fileID: 1341972280} + m_Layer: 5 + m_Name: UseDefaultSettings + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1341972279 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1341972278} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1568416102} + - {fileID: 1922333361} + m_Father: {fileID: 397743427} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 152, y: -55} + m_SizeDelta: {x: 300, y: 80} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1341972280 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1341972278} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1922333362} + toggleTransition: 1 + graphic: {fileID: 690401034} + m_Group: {fileID: 0} + onValueChanged: + m_PersistentCalls: + m_Calls: [] + m_IsOn: 0 --- !u!1 &1350795546 GameObject: m_ObjectHideFlags: 0 @@ -5886,6 +6050,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1543731663} m_CullTransparentMesh: 1 +--- !u!1 &1568416101 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1568416102} + - component: {fileID: 1568416104} + - component: {fileID: 1568416103} + m_Layer: 5 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1568416102 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1568416101} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1341972279} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -68.93698, y: -0.5} + m_SizeDelta: {x: -137.876, y: -3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1568416103 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1568416101} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 28 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 5 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Use Default Settings +--- !u!222 &1568416104 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1568416101} + m_CullTransparentMesh: 1 --- !u!1 &1571725899 GameObject: m_ObjectHideFlags: 0 @@ -7673,6 +7916,82 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1892752226} m_CullTransparentMesh: 1 +--- !u!1 &1922333360 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1922333361} + - component: {fileID: 1922333363} + - component: {fileID: 1922333362} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1922333361 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1922333360} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 690401033} + m_Father: {fileID: 1341972279} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 197, y: 0} + m_SizeDelta: {x: 40, y: 40} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1922333362 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1922333360} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1922333363 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1922333360} + m_CullTransparentMesh: 1 --- !u!1 &1925992335 GameObject: m_ObjectHideFlags: 0 @@ -7786,11 +8105,11 @@ RectTransform: - {fileID: 43381537} - {fileID: 1584422740} m_Father: {fileID: 397743427} - m_RootOrder: 4 + m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 295, y: -338} + m_AnchoredPosition: {x: 294, y: -403.257} m_SizeDelta: {x: 584, y: 100} m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1979558881 diff --git a/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs b/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs index 5ce0e1a79..70fceb559 100644 --- a/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs @@ -87,9 +87,11 @@ void SetUpHost(string username) playerController.CheckPairedDevices(); statsUI.AddSignalingHandler(handler); - renderStreaming.Run(signaling: settings?.Signaling, - handlers: new SignalingHandlerBase[] {handler} - ); + if (settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(handlers: new SignalingHandlerBase[] {handler}); } IEnumerator SetUpGuest(string username, string connectionId) @@ -98,9 +100,11 @@ IEnumerator SetUpGuest(string username, string connectionId) var handler = guestPlayer.GetComponent(); statsUI.AddSignalingHandler(handler); - renderStreaming.Run(signaling: settings?.Signaling, - handlers: new SignalingHandlerBase[] {handler} - ); + if (settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(handlers: new SignalingHandlerBase[] {handler}); videoImage.gameObject.SetActive(true); var receiveVideoViewer = guestPlayer.GetComponent(); diff --git a/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs b/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs index fd4ce920f..b74a145f8 100644 --- a/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs @@ -70,7 +70,12 @@ void Start() { if (renderStreaming.runOnAwake) return; - renderStreaming.Run(signaling: settings?.Signaling); + + if (settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(); } private void Update() diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs index 739ddb6e5..33f2161f8 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs @@ -28,6 +28,7 @@ internal class RenderStreamingSettings public const int DefaultStreamWidth = 1280; public const int DefaultStreamHeight = 720; + private bool useDefaultSettings = false; private SignalingType signalingType = SignalingType.WebSocket; private string signalingAddress = "localhost"; private float signalingInterval = 5; @@ -36,6 +37,12 @@ internal class RenderStreamingSettings private VideoCodecInfo receiverVideoCodec = null; private VideoCodecInfo senderVideoCodec = null; + public bool UseDefaultSettings + { + get { return useDefaultSettings; } + set { useDefaultSettings = value; } + } + public SignalingType SignalingType { get { return signalingType; } @@ -60,7 +67,7 @@ public float SignalingInterval set { signalingInterval = value; } } - public ISignaling Signaling + public SignalingSettings SignalingSettings { get { @@ -69,33 +76,29 @@ public ISignaling Signaling case SignalingType.Furioos: { var schema = signalingSecured ? "https" : "http"; - var settings = new FurioosSignalingSettings + return new FurioosSignalingSettings ( url: $"{schema}://{signalingAddress}" ); - return new FurioosSignaling(settings, SynchronizationContext.Current); } case SignalingType.WebSocket: { var schema = signalingSecured ? "wss" : "ws"; - var settings = new WebSocketSignalingSettings + return new WebSocketSignalingSettings ( url: $"{schema}://{signalingAddress}" ); - return new WebSocketSignaling(settings, SynchronizationContext.Current); } case SignalingType.Http: { var schema = signalingSecured ? "https" : "http"; - var settings = new HttpSignalingSettings + return new HttpSignalingSettings ( url: $"{schema}://{signalingAddress}", interval: signalingInterval ); - return new FurioosSignaling(settings, SynchronizationContext.Current); } } - throw new InvalidOperationException(); } } @@ -121,6 +124,7 @@ public VideoCodecInfo SenderVideoCodec internal class SceneSelectUI : MonoBehaviour { + [SerializeField] private Toggle toggleUseDefaultSettings; [SerializeField] private Dropdown dropdownSignalingType; [SerializeField] private InputField inputFieldSignalingAddress; [SerializeField] private Toggle toggleSignalingSecured; @@ -178,17 +182,21 @@ void Start() SampleManager.Instance.Initialize(); settings = SampleManager.Instance.Settings; + toggleUseDefaultSettings.isOn = settings.UseDefaultSettings; dropdownSignalingType.value = (int)settings.SignalingType; inputFieldSignalingAddress.text = settings.SignalingAddress; toggleSignalingSecured.isOn = settings.SignalingSecured; inputFieldSignalingInterval.text = settings.SignalingInterval.ToString(CultureInfo.InvariantCulture); + toggleUseDefaultSettings.onValueChanged.AddListener(OnChangeUseDefaultSettings); dropdownSignalingType.onValueChanged.AddListener(OnChangeSignalingType); inputFieldSignalingAddress.onValueChanged.AddListener(OnChangeSignalingAddress); toggleSignalingSecured.onValueChanged.AddListener(OnChangeSignalingSecured); inputFieldSignalingInterval.onValueChanged.AddListener(OnChangeSignalingInterval); + SetInteractableSignalingUI(!settings.UseDefaultSettings); + var optionList = streamSizeList.Select(size => new Dropdown.OptionData($" {size.x} x {size.y} ")).ToList(); optionList.Add(new Dropdown.OptionData(" Custom ")); streamSizeSelector.options = optionList; @@ -285,6 +293,20 @@ private void OnChangeSignalingSecured(bool value) settings.SignalingSecured = value; } + private void OnChangeUseDefaultSettings(bool value) + { + settings.UseDefaultSettings = value; + SetInteractableSignalingUI(!value); + } + + private void SetInteractableSignalingUI(bool interactable) + { + dropdownSignalingType.interactable = interactable; + inputFieldSignalingAddress.interactable = interactable; + toggleSignalingSecured.interactable = interactable; + inputFieldSignalingInterval.interactable = interactable; + } + private void OnChangeSignalingInterval(string value) { if (float.TryParse(value, out float _value)) diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputSample.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputSample.cs index f8d030720..08c3a23e6 100644 --- a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputSample.cs @@ -22,10 +22,14 @@ void Start() { dropdownCamera.onValueChanged.AddListener(OnChangeCamera); - if (!renderStreaming.runOnAwake) - { - renderStreaming.Run(signaling: settings?.Signaling); - } + if (renderStreaming.runOnAwake) + return; + + if (settings != null) + renderStreaming.useDefaultSettings = settings.UseDefaultSettings; + if (settings?.SignalingSettings != null) + renderStreaming.SetSignalingSettings(settings.SignalingSettings); + renderStreaming.Run(); } void OnChangeCamera(int value) diff --git a/com.unity.renderstreaming/Tests/Editor/EditorTest.cs b/com.unity.renderstreaming/Tests/Editor/EditorTest.cs index 838649173..be87a61e9 100644 --- a/com.unity.renderstreaming/Tests/Editor/EditorTest.cs +++ b/com.unity.renderstreaming/Tests/Editor/EditorTest.cs @@ -163,5 +163,14 @@ public void SerializeAudioCodecInfo() Assert.That(asset.info.Equals(otherAsset.info), Is.True); AssetDatabase.DeleteAsset(exportPath); } + + [Test] + public void CreateSignalingSettingsObject() + { + var asset = SignalingSettingsObject.Create(); + Assert.That(asset, Is.Not.Null); + Assert.That(asset.settings, Is.Not.Null); + Assert.That(asset.settings, Is.TypeOf()); + } } } diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs index cd27bfaa1..a441a6af5 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs @@ -31,6 +31,14 @@ public void DoNothing() { } + [Test] + public void UseDefaultSettings() + { + Assert.That(component.useDefaultSettings, Is.True); + component.useDefaultSettings = false; + Assert.That(component.useDefaultSettings, Is.False); + } + [Test] public void Run() { @@ -91,8 +99,6 @@ public void GetDefaultSignalingSettings() var settings = component.GetSignalingSettings() as WebSocketSignalingSettings; Assert.That(settings, Is.Not.Null); - Assert.That(settings.url, Is.Not.Null); - Assert.That(settings.iceServers, Is.Not.Null); } [Test] From cf76f13df2eeb3b176cfbefc0d4dbc85cf87c666 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Tue, 7 Feb 2023 17:22:14 +0900 Subject: [PATCH 040/117] feat: Render Streaming Wizard (#857) * create render streaming wizard * wip * fix review * increment counter * fix * rename folder * add icon * fix * fix * update ui * add wizard open behavior * update project settings * check package version * add download behavior * add webapp group * create toggle from script * remove renderstreaming menu item * delete unused * migrate uss * fix * update error message * fix * add buildtarget and graphics api check * add current settings * fix uss style name * fix android sdk validation * fix * add server build check * fix graphics api settings correcter --- .../ProjectSettings/ProjectSettings.asset | 11 +- .../RenderStreamingProjectSettings.asset | 16 + .../Editor/ConfigInfoLine.cs | 101 +++ .../Editor/ConfigInfoLine.cs.meta | 3 + com.unity.renderstreaming/Editor/Icon.meta | 8 + .../Editor/Icon/Error.png | Bin 0 -> 230 bytes .../Editor/Icon/Error.png.meta | 121 ++++ com.unity.renderstreaming/Editor/Icon/OK.png | Bin 0 -> 241 bytes .../Editor/Icon/OK.png.meta | 121 ++++ .../Editor/RenderStreamingMenu.cs | 35 -- .../Editor/RenderStreamingMenu.cs.meta | 11 - .../Editor/RenderStreamingProjectSettings.cs | 77 +++ .../RenderStreamingProjectSettings.cs.meta | 3 + .../Editor/RenderStreamingWizard.cs | 580 ++++++++++++++++++ .../Editor/RenderStreamingWizard.cs.meta | 3 + .../Editor/Styles/RenderStreamingWizard.uss | 118 ++++ .../Styles/RenderStreamingWizard.uss.meta | 3 + .../Editor/UXML/RenderStreamingWizard.uxml | 23 + .../UXML/RenderStreamingWizard.uxml.meta | 3 + 19 files changed, 1184 insertions(+), 53 deletions(-) create mode 100644 RenderStreaming~/ProjectSettings/RenderStreamingProjectSettings.asset create mode 100644 com.unity.renderstreaming/Editor/ConfigInfoLine.cs create mode 100644 com.unity.renderstreaming/Editor/ConfigInfoLine.cs.meta create mode 100644 com.unity.renderstreaming/Editor/Icon.meta create mode 100644 com.unity.renderstreaming/Editor/Icon/Error.png create mode 100644 com.unity.renderstreaming/Editor/Icon/Error.png.meta create mode 100644 com.unity.renderstreaming/Editor/Icon/OK.png create mode 100644 com.unity.renderstreaming/Editor/Icon/OK.png.meta delete mode 100644 com.unity.renderstreaming/Editor/RenderStreamingMenu.cs delete mode 100644 com.unity.renderstreaming/Editor/RenderStreamingMenu.cs.meta create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs.meta create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingWizard.cs create mode 100644 com.unity.renderstreaming/Editor/RenderStreamingWizard.cs.meta create mode 100644 com.unity.renderstreaming/Editor/Styles/RenderStreamingWizard.uss create mode 100644 com.unity.renderstreaming/Editor/Styles/RenderStreamingWizard.uss.meta create mode 100644 com.unity.renderstreaming/Editor/UXML/RenderStreamingWizard.uxml create mode 100644 com.unity.renderstreaming/Editor/UXML/RenderStreamingWizard.uxml.meta diff --git a/RenderStreaming~/ProjectSettings/ProjectSettings.asset b/RenderStreaming~/ProjectSettings/ProjectSettings.asset index dfab294c3..0010d00d0 100644 --- a/RenderStreaming~/ProjectSettings/ProjectSettings.asset +++ b/RenderStreaming~/ProjectSettings/ProjectSettings.asset @@ -149,7 +149,6 @@ PlayerSettings: enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 - enableOpenGLProfilerGPURecorders: 1 useHDRDisplay: 0 D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 @@ -175,7 +174,7 @@ PlayerSettings: stripEngineCode: 0 iPhoneStrippingLevel: 0 iPhoneScriptCallOptimization: 0 - ForceInternetPermission: 0 + ForceInternetPermission: 1 ForceSDCardPermission: 0 CreateWallpaper: 0 APKExpansionFiles: 0 @@ -224,7 +223,6 @@ PlayerSettings: iOSLaunchScreeniPadCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] - macOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 @@ -449,7 +447,6 @@ PlayerSettings: m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] m_BuildTargetNormalMapEncoding: [] - m_BuildTargetDefaultTextureCompressionFormat: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 @@ -468,7 +465,6 @@ PlayerSettings: switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 switchUseGOLDLinker: 0 - switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: switchTitleNames_0: @@ -598,6 +594,7 @@ PlayerSettings: switchNetworkInterfaceManagerInitializeEnabled: 1 switchPlayerConnectionEnabled: 1 switchUseNewStyleFilepaths: 0 + switchUseLegacyFmodPriorities: 1 switchUseMicroSleepForYield: 1 switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 @@ -708,6 +705,7 @@ PlayerSettings: suppressCommonWarnings: 1 allowUnsafeCode: 0 useDeterministicCompilation: 1 + useReferenceAssemblies: 1 enableRoslynAnalyzers: 1 additionalIl2CppArgs: scriptingRuntimeVersion: 1 @@ -788,6 +786,7 @@ PlayerSettings: m_VersionName: apiCompatibilityLevel: 6 activeInputHandler: 1 + windowsGamepadBackendHint: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] @@ -795,6 +794,4 @@ PlayerSettings: organizationId: cloudEnabled: 0 legacyClampBlendShapeWeights: 0 - playerDataPath: - forceSRGBBlit: 1 virtualTexturingSupportEnabled: 0 diff --git a/RenderStreaming~/ProjectSettings/RenderStreamingProjectSettings.asset b/RenderStreaming~/ProjectSettings/RenderStreamingProjectSettings.asset new file mode 100644 index 000000000..cdd22c0ce --- /dev/null +++ b/RenderStreaming~/ProjectSettings/RenderStreamingProjectSettings.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b4f5b5e9819c4eafb84bd87d1c2f266f, type: 3} + m_Name: + m_EditorClassIdentifier: + m_WizardPopupAtStart: 0 + m_WizardPopupAlreadyShownOnce: 1 diff --git a/com.unity.renderstreaming/Editor/ConfigInfoLine.cs b/com.unity.renderstreaming/Editor/ConfigInfoLine.cs new file mode 100644 index 000000000..c012563e7 --- /dev/null +++ b/com.unity.renderstreaming/Editor/ConfigInfoLine.cs @@ -0,0 +1,101 @@ +using System; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Unity.RenderStreaming.Editor +{ + internal class ConfigInfoLine : VisualElement + { + static class Style + { + const string k_IconFolder = "Packages/com.unity.renderstreaming/Editor/Icon/"; + public static readonly Texture ok = EditorGUIUtility.Load(k_IconFolder + "OK.png") as Texture; + public static readonly Texture error = EditorGUIUtility.Load(k_IconFolder + "Error.png") as Texture; + } + + private readonly bool m_visibleStatus; + private readonly bool m_skipErrorIcon; + private Func m_tester; + private bool m_haveFixer; + private bool m_currentStatus; + + public ConfigInfoLine( + string label, + string error, + MessageType messageType, + string resolverButtonLabel, + Func tester, + Action resolver, + bool visibleStatus = true, + bool skipErrorIcon = false + ) + { + m_visibleStatus = visibleStatus; + m_skipErrorIcon = skipErrorIcon; + m_tester = tester; + m_haveFixer = resolver != null; + + var testLabel = new Label(label) {name = "testLabel"}; + var fixer = new Button(resolver) {text = resolverButtonLabel, name = "resolver"}; + var testRow = new VisualElement() {name = "testRow"}; + testRow.Add(testLabel); + if (m_visibleStatus) + { + var statusOk = new Image {image = Style.ok, name = "statusOK"}; + var statusError = new Image {image = Style.error, name = "statusError"}; + testRow.Add(statusOk); + testRow.Add(statusError); + } + + testRow.Add(fixer); + + Add(testRow); + HelpBoxMessageType kind; + switch (messageType) + { + default: + case MessageType.None: + kind = HelpBoxMessageType.None; + break; + case MessageType.Error: + kind = HelpBoxMessageType.Error; + break; + case MessageType.Warning: + kind = HelpBoxMessageType.Warning; + break; + case MessageType.Info: + kind = HelpBoxMessageType.Info; + break; + } + + Add(new HelpBox(error, kind)); + + UpdateDisplay(m_currentStatus, m_haveFixer); + } + + public void CheckUpdate() + { + bool wellConfigured = m_tester(); + if (wellConfigured ^ m_currentStatus) + { + UpdateDisplay(wellConfigured, m_haveFixer); + m_currentStatus = wellConfigured; + } + } + + private void UpdateDisplay(bool statusOK, bool haveFixer) + { + if (m_visibleStatus) + { + this.Q(name: "statusOK").style.display = statusOK ? DisplayStyle.Flex : DisplayStyle.None; + this.Q(name: "statusError").style.display = !statusOK + ? (m_skipErrorIcon ? DisplayStyle.None : DisplayStyle.Flex) + : DisplayStyle.None; + } + + this.Q(name: "resolver").style.display = statusOK || !haveFixer ? DisplayStyle.None : DisplayStyle.Flex; + this.Q(className: HelpBox.ussClassName).style.display = statusOK ? DisplayStyle.None : DisplayStyle.Flex; + } + } +} diff --git a/com.unity.renderstreaming/Editor/ConfigInfoLine.cs.meta b/com.unity.renderstreaming/Editor/ConfigInfoLine.cs.meta new file mode 100644 index 000000000..1b5e55a33 --- /dev/null +++ b/com.unity.renderstreaming/Editor/ConfigInfoLine.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 75e195dbcbb746ff83e761bef32d1871 +timeCreated: 1673939682 \ No newline at end of file diff --git a/com.unity.renderstreaming/Editor/Icon.meta b/com.unity.renderstreaming/Editor/Icon.meta new file mode 100644 index 000000000..b3664e63e --- /dev/null +++ b/com.unity.renderstreaming/Editor/Icon.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f1c3a69adb0de3438eeea3b5e96029a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Editor/Icon/Error.png b/com.unity.renderstreaming/Editor/Icon/Error.png new file mode 100644 index 0000000000000000000000000000000000000000..8bba82fc9a5f22a525e19c2b52eabedba2755063 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k&H|6fVg?31We{epSZZGe6l5>) z^mS!_%qqyo$$Gt9?i5g{)zif>MC1I_{@sGj1_G?%ub8C*RtZQPFiZXTve`$`wBdJv zahcPCiSLhXxHx;~&FXTsDt9;TJ?H;0^p#}%Oby(mvBe>{t=A}r;p)vrT-_HevYpsX zngSU8J!f2;S+p>G+j8CYAv1oQ=SWyoZo8ql_n_By@$3Z=%{P)7qvFzz#QS`CcbIqI YR6Q|H)f3%|fevEuboFyt=akR{00`|;J^%m! literal 0 HcmV?d00001 diff --git a/com.unity.renderstreaming/Editor/Icon/Error.png.meta b/com.unity.renderstreaming/Editor/Icon/Error.png.meta new file mode 100644 index 000000000..e840ae230 --- /dev/null +++ b/com.unity.renderstreaming/Editor/Icon/Error.png.meta @@ -0,0 +1,121 @@ +fileFormatVersion: 2 +guid: 51efedebc42930447b62949d10af6590 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Editor/Icon/OK.png b/com.unity.renderstreaming/Editor/Icon/OK.png new file mode 100644 index 0000000000000000000000000000000000000000..77a552e3a4bcf693d6285005a8c4a18c15d8a211 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k&H|6fVg?31We{epSZZGe6l5>) z^mS!_%qqyoY5H-UUK&tnf~SjPh{pM;mk#na8}PI~ygzdZb3rR_0`rX}u}!`w*fuck zEEDg07Ov9y6%)^|=K jLz7O>G5cnlzf#u!Ut3P}jyO;bbQ^=GtDnm{r-UW|M*dT_ literal 0 HcmV?d00001 diff --git a/com.unity.renderstreaming/Editor/Icon/OK.png.meta b/com.unity.renderstreaming/Editor/Icon/OK.png.meta new file mode 100644 index 000000000..d569e91d6 --- /dev/null +++ b/com.unity.renderstreaming/Editor/Icon/OK.png.meta @@ -0,0 +1,121 @@ +fileFormatVersion: 2 +guid: 076e4a517f3efd6488ac9fc6554acead +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Editor/RenderStreamingMenu.cs b/com.unity.renderstreaming/Editor/RenderStreamingMenu.cs deleted file mode 100644 index 0d75f2bb4..000000000 --- a/com.unity.renderstreaming/Editor/RenderStreamingMenu.cs +++ /dev/null @@ -1,35 +0,0 @@ -using UnityEditor; -using UnityEngine; - -namespace Unity.RenderStreaming.Editor -{ - internal static class RenderStreamingMenu - { - [MenuItem("Edit/Render Streaming/Download web app")] - static void DownloadWebAppFromMenu() { - WebAppDownloader.GetPackageVersion("com.unity.renderstreaming", (version) => { - var dstPath = EditorUtility.OpenFolderPanel("Select download folder", "", ""); - WebAppDownloader.DownloadWebApp(version, dstPath, null); - }); - } - - [MenuItem("Edit/Render Streaming/Show web app documentation")] - static void ShowWepAppDocumentation() - { - WebAppDownloader.GetPackageVersion("com.unity.renderstreaming", (version) => { - var url = WebAppDownloader.GetURLDocumentation(version); - Application.OpenURL(url); - }); - } - - [MenuItem("Edit/Render Streaming/Show web app source code")] - static void ShowWepAppSourceCode() - { - WebAppDownloader.GetPackageVersion("com.unity.renderstreaming", (version) => { - var url = WebAppDownloader.GetURLSourceCode(version); - Application.OpenURL(url); - }); - } - } -} - diff --git a/com.unity.renderstreaming/Editor/RenderStreamingMenu.cs.meta b/com.unity.renderstreaming/Editor/RenderStreamingMenu.cs.meta deleted file mode 100644 index cfa5dfd95..000000000 --- a/com.unity.renderstreaming/Editor/RenderStreamingMenu.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 12f537a158c855d4fa65dd091ad15a7e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs new file mode 100644 index 000000000..4dd0c3983 --- /dev/null +++ b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs @@ -0,0 +1,77 @@ +using System.IO; +using UnityEditorInternal; +using UnityEngine; + +namespace Editor +{ + internal class RenderStreamingProjectSettings : ScriptableObject + { + const string filePath = "ProjectSettings/RenderStreamingProjectSettings.asset"; + + [SerializeField] private bool m_WizardPopupAtStart = true; + [SerializeField] private bool m_WizardPopupAlreadyShownOnce = false; + + public static bool wizardIsStartPopup + { + get => instance.m_WizardPopupAtStart; + set + { + instance.m_WizardPopupAtStart = value; + Save(); + } + } + + public static bool wizardPopupAlreadyShownOnce + { + get => instance.m_WizardPopupAlreadyShownOnce; + set + { + instance.m_WizardPopupAlreadyShownOnce = value; + Save(); + } + } + + static RenderStreamingProjectSettings s_Instance; + static RenderStreamingProjectSettings instance => s_Instance ? s_Instance : CreateOrLoad(); + + RenderStreamingProjectSettings() + { + s_Instance = this; + } + + static RenderStreamingProjectSettings CreateOrLoad() + { + // Object loaded and created from this method. + // So RenderStreamingProjectSettings constructor is called implicitly. + // If RenderStreamingProjectSettings could be loaded, then s_Instance is assigned the loaded instance. + InternalEditorUtility.LoadSerializedFileAndForget(filePath); + + if (s_Instance == null) + { + var created = CreateInstance(); + created.hideFlags = HideFlags.HideAndDontSave; + } + + System.Diagnostics.Debug.Assert(s_Instance != null); + return s_Instance; + } + + static void Save() + { + if (s_Instance == null) + { + Debug.Log("Cannot save ScriptableSingleton: no instance!"); + return; + } + + string folderPath = Path.GetDirectoryName(filePath); + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + InternalEditorUtility.SaveToSerializedFileAndForget(new Object[] {s_Instance}, filePath, + allowTextSerialization: true); + } + } +} diff --git a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs.meta b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs.meta new file mode 100644 index 000000000..6ec2980b1 --- /dev/null +++ b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b4f5b5e9819c4eafb84bd87d1c2f266f +timeCreated: 1673936399 \ No newline at end of file diff --git a/com.unity.renderstreaming/Editor/RenderStreamingWizard.cs b/com.unity.renderstreaming/Editor/RenderStreamingWizard.cs new file mode 100644 index 000000000..c20785450 --- /dev/null +++ b/com.unity.renderstreaming/Editor/RenderStreamingWizard.cs @@ -0,0 +1,580 @@ +using System; +using System.Linq; +using Unity.RenderStreaming; +using Unity.RenderStreaming.Editor; +using UnityEditor; +using UnityEditor.Callbacks; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.Rendering; +using UnityEngine.UIElements; + +namespace Editor +{ + internal class RenderStreamingWizard : EditorWindow + { + private const string packageName = "com.unity.renderstreaming"; + + private static readonly BuildTarget[] supportedBuildTarget = { + BuildTarget.StandaloneWindows64, + BuildTarget.StandaloneOSX, + BuildTarget.StandaloneLinux64, + BuildTarget.iOS, + BuildTarget.Android + }; + +#if UNITY_2021_1_OR_NEWER + const AndroidSdkVersions RequiredAndroidSdkVersion = AndroidSdkVersions.AndroidApiLevel22; +#else + const AndroidSdkVersions RequiredAndroidSdkVersion = AndroidSdkVersions.AndroidApiLevel21; +#endif + + private struct ConfigStyle + { + public readonly string label; + public readonly string error; + public readonly string button; + public readonly MessageType messageType; + + public ConfigStyle(string label, string error, string button = "Fix", + MessageType messageType = MessageType.Error) + { + this.label = label; + this.error = error; + this.button = button; + this.messageType = messageType; + } + } + + static readonly ConfigStyle runInBackground = new ConfigStyle( + label: "Run In Background", + error: "Run In Background must be True for Render Streaming to work in Background."); + + static readonly ConfigStyle inputSystemBackgroundBehavior = new ConfigStyle( + label: "InputSystem Background Behavior", + error: "InputSystem Background Behavior must be Ignore Focus for Input System to work in Background."); + + static readonly ConfigStyle inputSystemPlayModeInputBehavior = new ConfigStyle( + label: "InputSystem PlayMode Input Behavior", + error: "InputSystem PlayMode Input behavior must be AllDeviceInputAlwaysGoesToGameView for InputSystem to work in background PlayMode."); + + static readonly ConfigStyle currentBuildTarget = new ConfigStyle( + label: "Current BuildTarget platform", + error: "Current BuildTarget platform not supported."); + + static readonly ConfigStyle currentGraphicsApi = new ConfigStyle( + label: "Current Graphics API", + error: "Current settings contains not support Graphics API."); + + static readonly ConfigStyle macCameraUsageDescription = new ConfigStyle( + label: "macOS Camera Usage Description", + error: "Require Camera Usage Description for WebCam access on macOS."); + + static readonly ConfigStyle macMicrophoneUsageDescription = new ConfigStyle( + label: "macOS Microphone Usage Description", + error: "Require Microphone Usage Description for Microphone access on macOS."); + + static readonly ConfigStyle iOSCameraUsageDescription = new ConfigStyle( + label: "iOS Camera Usage Description", + error: "Require Camera Usage Description for WebCam access on iOS."); + + static readonly ConfigStyle iOSMicrophoneUsageDescription = new ConfigStyle( + label: "iOS Microphone Usage Description", + error: "Require Microphone Usage Description for Microphone access on iOS."); + + static readonly ConfigStyle androidMinimumAPILevel = new ConfigStyle( + label: "Android Minimum API Level", + error: $"The minimum Android SDK level required is {(int)RequiredAndroidSdkVersion} or higher."); + + static readonly ConfigStyle androidScriptBackend = new ConfigStyle( + label: "Android Script Backend", + error: "Render Streaming only supports IL2CPP as a scripting backend."); + + static readonly ConfigStyle androidTargetArchitecture = new ConfigStyle( + label: "Android Target Architecture", + error: "Render Streaming only supported ARM64 as a Android Target Architecture."); + + static readonly ConfigStyle androidInternetAccess = new ConfigStyle( + label: "Android Internet Access", + error: "InternetAccess must be set Required on Android."); + + enum Scope + { + PlayMode, + BuildSettings + } + + struct Entry + { + public delegate bool Checker(); + + public delegate void Fixer(); + + public readonly Scope scope; + public readonly ConfigStyle configStyle; + public readonly Checker check; + public readonly Fixer fix; + public readonly bool forceDisplayCheck; + public readonly bool skipErrorIcon; + + public Entry( + Scope scope, + ConfigStyle configStyle, + Checker check, + Fixer fix, + bool forceDisplayCheck = false, + bool skipErrorIcon = false + ) + { + this.scope = scope; + this.configStyle = configStyle; + this.check = check; + this.fix = fix; + this.forceDisplayCheck = forceDisplayCheck; + this.skipErrorIcon = skipErrorIcon; + } + } + + private Entry[] entries; + + Entry[] Entries + { + get + { + // due to functor, cannot static link directly in an array and need lazy init + if (entries == null) + entries = new[] + { + new Entry(Scope.PlayMode, runInBackground, IsRunInBackgroundCorrect, FixRunInBackground), + new Entry(Scope.PlayMode, inputSystemBackgroundBehavior, + IsInputSystemBackgroundBehaviorCorrect, + FixInputSystemBackgroundBehavior), + new Entry(Scope.PlayMode, inputSystemPlayModeInputBehavior, + IsInputSystemPlayModeInputBehaviorCorrect, + FixInputSystemPlayModeInputBehavior), + new Entry(Scope.BuildSettings, currentBuildTarget, IsSupportedBuildTarget, FixSupportedBuildTarget), + new Entry(Scope.BuildSettings, currentGraphicsApi, IsSupportedGraphics, FixSupportedGraphics), + new Entry(Scope.BuildSettings, macCameraUsageDescription, IsMacCameraUsageCorrect, FixMacCameraUsage), + new Entry(Scope.BuildSettings, macMicrophoneUsageDescription, IsMacMicrophoneUsageCorrect, + FixMacMicrophoneUsage), + new Entry(Scope.BuildSettings, iOSCameraUsageDescription, IsIOSCameraUsageCorrect, FixIOSCameraUsage), + new Entry(Scope.BuildSettings, iOSMicrophoneUsageDescription, IsIOSMicrophoneUsageCorrect, + FixIOSMicrophoneUsage), + new Entry(Scope.BuildSettings, androidMinimumAPILevel, IsAndroidMinimumAPILevelCorrect, + FixAndroidMinimumAPILevel), + new Entry(Scope.BuildSettings, androidScriptBackend, IsAndroidScriptBackendCorrect, + FixAndroidScriptBackend), + new Entry(Scope.BuildSettings, androidTargetArchitecture, IsAndroidTargetArchitectureCorrect, + FixAndroidTargetArchitecture), + new Entry(Scope.BuildSettings, androidInternetAccess, IsAndroidInternetAccessCorrect, + FixAndroidInternetAccess), + }; + return entries; + } + } + + private static bool IsRunInBackgroundCorrect() => PlayerSettings.runInBackground; + private static void FixRunInBackground() => PlayerSettings.runInBackground = true; + + private static bool IsInputSystemBackgroundBehaviorCorrect() => + InputSystem.settings.backgroundBehavior == InputSettings.BackgroundBehavior.IgnoreFocus; + + private static void FixInputSystemBackgroundBehavior() => + InputSystem.settings.backgroundBehavior = InputSettings.BackgroundBehavior.IgnoreFocus; + + private static bool IsInputSystemPlayModeInputBehaviorCorrect() => + InputSystem.settings.editorInputBehaviorInPlayMode == + InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView; + + private static void FixInputSystemPlayModeInputBehavior() => + InputSystem.settings.editorInputBehaviorInPlayMode = + InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView; + + private static bool IsSupportedBuildTarget() + { + var correctBuildTarget = supportedBuildTarget.Contains(EditorUserBuildSettings.activeBuildTarget); +#if UNITY_2021_1_OR_NEWER + correctBuildTarget = correctBuildTarget && EditorUserBuildSettings.standaloneBuildSubtarget == StandaloneBuildSubtarget.Player; +#endif + return correctBuildTarget; + } + + private static void FixSupportedBuildTarget() + { + BuildTarget target = default; +#if UNITY_EDITOR_WIN + target = BuildTarget.StandaloneWindows64; +#elif UNITY_EDITOR_OSX + target = BuildTarget.StandaloneOSX; +#elif UNITY_EDITOR_LINUX + target = BuildTarget.StandaloneLinux64; +#else + throw new NotSupportedException(); +#endif + EditorUserBuildSettings.SwitchActiveBuildTarget(BuildPipeline.GetBuildTargetGroup(target), target); +#if UNITY_2021_1_OR_NEWER + EditorUserBuildSettings.standaloneBuildSubtarget = StandaloneBuildSubtarget.Player; +#endif + } + + private static bool IsSupportedGraphics() => supportedBuildTarget.All(CheckGraphicsApi); + + private static bool CheckGraphicsApi(BuildTarget target) + { + var targetGraphics = PlayerSettings.GetGraphicsAPIs(target); + switch (target) + { + case BuildTarget.StandaloneOSX: + case BuildTarget.iOS: + return targetGraphics.All(x => x == GraphicsDeviceType.Metal); + case BuildTarget.Android: + return targetGraphics.All(x => x == GraphicsDeviceType.OpenGLES3 || x == GraphicsDeviceType.Vulkan); + case BuildTarget.StandaloneWindows64: + return targetGraphics.All(x => x == GraphicsDeviceType.Direct3D11 || x == GraphicsDeviceType.Direct3D12 || x == GraphicsDeviceType.Vulkan); + case BuildTarget.StandaloneLinux64: + return targetGraphics.All(x => x == GraphicsDeviceType.OpenGLCore || x == GraphicsDeviceType.Vulkan); + default: + return false; + } + } + + private static void FixSupportedGraphics() + { + foreach (var target in supportedBuildTarget.Where(x => !CheckGraphicsApi(x))) + { + switch (target) + { + case BuildTarget.StandaloneOSX: + case BuildTarget.iOS: + PlayerSettings.SetGraphicsAPIs(target, new[] {GraphicsDeviceType.Metal}); + break; + case BuildTarget.Android: + PlayerSettings.SetGraphicsAPIs(target, new[] {GraphicsDeviceType.OpenGLES3, GraphicsDeviceType.Vulkan}); + break; + case BuildTarget.StandaloneWindows64: + PlayerSettings.SetGraphicsAPIs(target, new[] {GraphicsDeviceType.Direct3D11, GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Vulkan}); + break; + case BuildTarget.StandaloneLinux64: + PlayerSettings.SetGraphicsAPIs(target, new[] {GraphicsDeviceType.OpenGLCore, GraphicsDeviceType.Vulkan}); + break; + default: + throw new NotSupportedException($"{nameof(target)} is not supported."); + } + } + } + + private static bool IsMacCameraUsageCorrect() => + !string.IsNullOrEmpty(PlayerSettings.macOS.cameraUsageDescription); + + private static void FixMacCameraUsage() => PlayerSettings.macOS.cameraUsageDescription = "For WebCamTexture"; + + private static bool IsMacMicrophoneUsageCorrect() => + !string.IsNullOrEmpty(PlayerSettings.iOS.microphoneUsageDescription); + + private static void FixMacMicrophoneUsage() => PlayerSettings.iOS.microphoneUsageDescription = "For Microphone"; + + private static bool IsIOSCameraUsageCorrect() => + !string.IsNullOrEmpty(PlayerSettings.iOS.cameraUsageDescription); + + private static void FixIOSCameraUsage() => PlayerSettings.iOS.cameraUsageDescription = "For WebCamTexture"; + + private static bool IsIOSMicrophoneUsageCorrect() => + !string.IsNullOrEmpty(PlayerSettings.iOS.microphoneUsageDescription); + + private static void FixIOSMicrophoneUsage() => PlayerSettings.iOS.microphoneUsageDescription = "For Microphone"; + + private static bool IsAndroidMinimumAPILevelCorrect() => + PlayerSettings.Android.minSdkVersion >= RequiredAndroidSdkVersion; + + private static void FixAndroidMinimumAPILevel() => + PlayerSettings.Android.minSdkVersion = RequiredAndroidSdkVersion; + + private static bool IsAndroidScriptBackendCorrect() => + PlayerSettings.GetScriptingBackend(BuildTargetGroup.Android) == ScriptingImplementation.IL2CPP; + + private static void FixAndroidScriptBackend() => + PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP); + + private static bool IsAndroidTargetArchitectureCorrect() => + PlayerSettings.Android.targetArchitectures == AndroidArchitecture.ARM64; + + private static void FixAndroidTargetArchitecture() => + PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64; + + private static bool IsAndroidInternetAccessCorrect() => PlayerSettings.Android.forceInternetPermission; + private static void FixAndroidInternetAccess() => PlayerSettings.Android.forceInternetPermission = true; + + const string kTemplatePath = "Packages/com.unity.renderstreaming/Editor/UXML/RenderStreamingWizard.uxml"; + const string kStylePath = "Packages/com.unity.renderstreaming/Editor/Styles/RenderStreamingWizard.uss"; + + [MenuItem("Window/Render Streaming/Render Streaming Wizard", priority = 10000)] + static void OpenWindow() + { + var window = GetWindow("Render Streaming Wizard"); + window.minSize = new Vector2(500, 450); + RenderStreamingProjectSettings.wizardPopupAlreadyShownOnce = true; + } + + static RenderStreamingWizard() + { + WizardBehaviour(); + } + + private static int frameToWait; + + private static void WizardBehaviourDelayed() + { + if (frameToWait > 0) + --frameToWait; + else + { + EditorApplication.update -= WizardBehaviourDelayed; + + if (RenderStreamingProjectSettings.wizardIsStartPopup && + !RenderStreamingProjectSettings.wizardPopupAlreadyShownOnce) + { + //Application.isPlaying cannot be called in constructor. Do it here + if (Application.isPlaying) + return; + + OpenWindow(); + } + + EditorApplication.quitting += () => RenderStreamingProjectSettings.wizardPopupAlreadyShownOnce = false; + } + } + + [DidReloadScripts] + static void CheckPersistentPopupAlreadyOpened() + { + EditorApplication.delayCall += () => + { + if (RenderStreamingProjectSettings.wizardPopupAlreadyShownOnce) + EditorApplication.quitting += + () => RenderStreamingProjectSettings.wizardPopupAlreadyShownOnce = false; + }; + } + + [DidReloadScripts] + static void WizardBehaviour() + { + //We need to wait at least one frame or the popup will not show up + frameToWait = 10; + EditorApplication.update += WizardBehaviourDelayed; + } + + private void OnEnable() + { + var styleSheet = EditorGUIUtility.Load(kStylePath) as StyleSheet; + + var uiAsset = AssetDatabase.LoadAssetAtPath(kTemplatePath); + var newVisualElement = new VisualElement(); + uiAsset.CloneTree(newVisualElement); + rootVisualElement.Add(newVisualElement); + rootVisualElement.styleSheets.Add(styleSheet); + + BindCheckVersion(); + BindCurrentSettings(); + BindChecker(); + BindWebApp(); + BindCheckBox(); + } + + private int inspectorCounter = 0; + private Label currentSettingsLabel; + private HelpBox currentSettingsHelpBox; + private Button fixAllButton; + private VisualElement playmodeCheckButtons; + private VisualElement buildSettingsCheckButtons; + + private void OnInspectorUpdate() + { + // limit inspector update per 1 second. + inspectorCounter++; + if (inspectorCounter % 10 != 0) + { + return; + } + + + if (currentSettingsLabel != null) + { + currentSettingsLabel.text = $"Current Render Streaming Settings: {GetSettingsAssetName()}"; + } + + if (currentSettingsHelpBox != null) + { + currentSettingsHelpBox.style.display = IsDefaultSetting() ? DisplayStyle.Flex : DisplayStyle.None; + } + + + fixAllButton?.SetEnabled(entries.Any(x => !x.check())); + + if (playmodeCheckButtons != null && buildSettingsCheckButtons != null) + { + foreach (var visualElement in playmodeCheckButtons.Children() + .Concat(buildSettingsCheckButtons.Children()) + .Select(c => c as ConfigInfoLine) + .Where(c => c != null)) + { + visualElement.CheckUpdate(); + } + } + + inspectorCounter = 0; + } + + private void BindCheckVersion() + { + var checkUpdateContainer = rootVisualElement.Q("checkUpdateContainer"); + + var label = new TextElement {text = "Current Render Streaming version: checking..."}; + checkUpdateContainer.Add(label); + + var button = new Button(() => + UnityEditor.PackageManager.UI.Window.Open(packageName)) {text = "Check update"}; + button.AddToClassList("right-anchored-button"); + checkUpdateContainer.Add(button); + + RequestJobManager.CreateListRequest(true, true, (req) => + { + var packageInfo = req.FindPackage(packageName); + if (null == packageInfo) + { + Debug.LogError($"Not found package \"{packageName}\""); + return; + } + + label.text = $"Current Render Streaming version: {packageInfo.version}"; + }, null); + } + + private void BindCurrentSettings() + { + var checkUpdateContainer = rootVisualElement.Q("currentSettingsContainer"); + + currentSettingsLabel = new Label {text = $"Current Render Streaming Settings: {GetSettingsAssetName()}"}; + currentSettingsLabel.AddToClassList("normal"); + checkUpdateContainer.Add(currentSettingsLabel); + + var button = new Button(() => SettingsService.OpenProjectSettings("Project/Render Streaming")) + { + text = "Open Project Settings" + }; + button.AddToClassList(("open-project-settings")); + checkUpdateContainer.Add(button); + + currentSettingsHelpBox = new HelpBox("Current selected settings is default. If you want to change settings, open the Project Window and create or select another Settings.", HelpBoxMessageType.Info) + { + style = {display = IsDefaultSetting() ? DisplayStyle.Flex : DisplayStyle.None} + }; + checkUpdateContainer.Add(currentSettingsHelpBox); + } + + private static string GetSettingsAssetName() + { + var path = AssetDatabase.GetAssetPath(RenderStreaming.Settings); + var assetName = path == RenderStreaming.DefaultRenderStreamingSettingsPath ? "Default" : path.Split('/').Last(); + return assetName; + } + + private static bool IsDefaultSetting() + { + return AssetDatabase.GetAssetPath(RenderStreaming.Settings) == RenderStreaming.DefaultRenderStreamingSettingsPath; + } + + private void BindChecker() + { + fixAllButton = rootVisualElement.Q [Serializable] - class ChangeLabelEvent : UnityEvent {}; + class ChangeLabelEvent : UnityEvent { }; /// /// @@ -33,7 +33,7 @@ protected override void OnMessage(byte[] bytes) { string str = System.Text.Encoding.UTF8.GetString(bytes); var message = JsonUtility.FromJson(str); - switch(message.type) + switch (message.type) { case ActionType.ChangeLabel: OnChangeLabel?.Invoke(message.argument); diff --git a/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs b/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs index 70fceb559..77b25853f 100644 --- a/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Multiplay/MultiplaySample.cs @@ -91,7 +91,7 @@ void SetUpHost(string username) renderStreaming.useDefaultSettings = settings.UseDefaultSettings; if (settings?.SignalingSettings != null) renderStreaming.SetSignalingSettings(settings.SignalingSettings); - renderStreaming.Run(handlers: new SignalingHandlerBase[] {handler}); + renderStreaming.Run(handlers: new SignalingHandlerBase[] { handler }); } IEnumerator SetUpGuest(string username, string connectionId) @@ -104,7 +104,7 @@ IEnumerator SetUpGuest(string username, string connectionId) renderStreaming.useDefaultSettings = settings.UseDefaultSettings; if (settings?.SignalingSettings != null) renderStreaming.SetSignalingSettings(settings.SignalingSettings); - renderStreaming.Run(handlers: new SignalingHandlerBase[] {handler}); + renderStreaming.Run(handlers: new SignalingHandlerBase[] { handler }); videoImage.gameObject.SetActive(true); var receiveVideoViewer = guestPlayer.GetComponent(); @@ -113,7 +113,7 @@ IEnumerator SetUpGuest(string username, string connectionId) var channel = guestPlayer.GetComponent(); channel.OnStartedChannel += _ => { StartCoroutine(ChangeLabel(channel, username)); }; - if(settings != null) + if (settings != null) receiveVideoViewer.SetCodec(settings.ReceiverVideoCodec); // todo(kazuki): diff --git a/com.unity.renderstreaming/Samples~/Example/Multiplay/PlayerController.cs b/com.unity.renderstreaming/Samples~/Example/Multiplay/PlayerController.cs index e2021f7f9..00d375ae7 100644 --- a/com.unity.renderstreaming/Samples~/Example/Multiplay/PlayerController.cs +++ b/com.unity.renderstreaming/Samples~/Example/Multiplay/PlayerController.cs @@ -1,6 +1,6 @@ +using System.Linq; using UnityEngine; using UnityEngine.InputSystem; -using System.Linq; namespace Unity.RenderStreaming.Samples { @@ -23,7 +23,7 @@ class PlayerController : MonoBehaviour Vector2 inputLook; Vector3 initialPosition; bool inputJump; - float cooldownJumpDelta = CooldownJump; + float cooldownJumpDelta = CooldownJump; protected void Awake() { @@ -36,17 +36,17 @@ void OnDeviceChange(InputDevice device, InputDeviceChange change) switch (change) { case InputDeviceChange.Added: - { - playerInput.PerformPairingWithDevice(device); - CheckPairedDevices(); - return; - } + { + playerInput.PerformPairingWithDevice(device); + CheckPairedDevices(); + return; + } case InputDeviceChange.Removed: - { - playerInput.UnpairDevices(device); - CheckPairedDevices(); - return; - } + { + playerInput.UnpairDevices(device); + CheckPairedDevices(); + return; + } } } diff --git a/com.unity.renderstreaming/Samples~/Example/Receiver/AudioSpectrumView.cs b/com.unity.renderstreaming/Samples~/Example/Receiver/AudioSpectrumView.cs index 6c89026ec..6529c12d0 100644 --- a/com.unity.renderstreaming/Samples~/Example/Receiver/AudioSpectrumView.cs +++ b/com.unity.renderstreaming/Samples~/Example/Receiver/AudioSpectrumView.cs @@ -37,7 +37,7 @@ void Start() array = new Vector3[positionCount]; // This line object is used as a template. - if(line.gameObject.activeInHierarchy) + if (line.gameObject.activeInHierarchy) line.gameObject.SetActive(false); AudioSettings.OnAudioConfigurationChanged += OnAudioConfigurationChanged; @@ -71,7 +71,7 @@ void Update() { if (target.clip == null) { - if(lines.Count > 0) + if (lines.Count > 0) ResetLines(0); clip = null; return; diff --git a/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs b/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs index b74a145f8..dd75197c6 100644 --- a/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs +++ b/com.unity.renderstreaming/Samples~/Example/Receiver/ReceiverSample.cs @@ -50,7 +50,7 @@ void Awake() { startButton.onClick.AddListener(OnStart); stopButton.onClick.AddListener(OnStop); - if(connectionIdInput != null) + if (connectionIdInput != null) connectionIdInput.onValueChanged.AddListener(input => connectionId = input); receiveVideoViewer.OnUpdateReceiveTexture += OnUpdateReceiveTexture; @@ -122,7 +122,7 @@ private void OnStart() connectionIdInput.text = connectionId; } connectionIdInput.interactable = false; - if(settings != null) + if (settings != null) receiveVideoViewer.SetCodec(settings.ReceiverVideoCodec); receiveAudioViewer.targetAudioSource = remoteAudioSource; diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/BackButton.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/BackButton.cs index 71a8d757d..9a73967b8 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/BackButton.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/BackButton.cs @@ -1,6 +1,6 @@ using UnityEngine; -using UnityEngine.SceneManagement; using UnityEngine.InputSystem; +using UnityEngine.SceneManagement; namespace Unity.RenderStreaming.Samples { diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SampleManager.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SampleManager.cs index f6877f6f3..8e19c467c 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SampleManager.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SampleManager.cs @@ -26,7 +26,7 @@ public RenderStreamingSettings Settings public void Initialize() { - if(m_settings == null) + if (m_settings == null) m_settings = new RenderStreamingSettings(); } } diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs index 74da1558f..f1fec0a6c 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs @@ -72,30 +72,30 @@ public SignalingSettings SignalingSettings switch (signalingType) { case SignalingType.Furioos: - { - var schema = signalingSecured ? "https" : "http"; - return new FurioosSignalingSettings - ( - url: $"{schema}://{signalingAddress}" - ); - } + { + var schema = signalingSecured ? "https" : "http"; + return new FurioosSignalingSettings + ( + url: $"{schema}://{signalingAddress}" + ); + } case SignalingType.WebSocket: - { - var schema = signalingSecured ? "wss" : "ws"; - return new WebSocketSignalingSettings - ( - url: $"{schema}://{signalingAddress}" - ); - } + { + var schema = signalingSecured ? "wss" : "ws"; + return new WebSocketSignalingSettings + ( + url: $"{schema}://{signalingAddress}" + ); + } case SignalingType.Http: - { - var schema = signalingSecured ? "https" : "http"; - return new HttpSignalingSettings - ( - url: $"{schema}://{signalingAddress}", - interval: signalingInterval - ); - } + { + var schema = signalingSecured ? "https" : "http"; + return new HttpSignalingSettings + ( + url: $"{schema}://{signalingAddress}", + interval: signalingInterval + ); + } } throw new InvalidOperationException(); } @@ -178,7 +178,7 @@ static string CodecTitle(VideoCodecInfo codec) void Start() { SampleManager.Instance.Initialize(); - settings = SampleManager.Instance.Settings; + settings = SampleManager.Instance.Settings; toggleUseDefaultSettings.isOn = settings.UseDefaultSettings; dropdownSignalingType.value = (int)settings.SignalingType; diff --git a/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs b/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs index 66d3b05c7..d0f2f5562 100644 --- a/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Stats/ShowStatsUI.cs @@ -115,7 +115,7 @@ private IEnumerator CollectStats() var coroutine = StartCoroutine(UpdateStats(receiver)); coroutines.Add(coroutine); } - foreach(var coroutine in coroutines) + foreach (var coroutine in coroutines) { yield return coroutine; } @@ -247,7 +247,7 @@ private void SetUpReceiverBase(StreamReceiverBase receiverBase) receiverBase.OnStartedStream += id => { - if(activeReceiverList.TryGetValue(receiverBase, out var hashSet)) + if (activeReceiverList.TryGetValue(receiverBase, out var hashSet)) { hashSet.Add(receiverBase.Transceiver.Receiver); } diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/SimpleCameraController.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/SimpleCameraController.cs index 6f29524f2..14c9b94ad 100644 --- a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/SimpleCameraController.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/SimpleCameraController.cs @@ -74,7 +74,7 @@ public void UpdateTransform(Transform t) [Header("Movement Settings")] [Tooltip("Movement Sensitivity Factor."), Range(0.001f, 1f)] - [SerializeField] float m_movementSensitivityFactor = 0.1f; + [SerializeField] float m_movementSensitivityFactor = 0.1f; [Tooltip("Exponential boost factor on translation, controllable by mouse wheel.")] [SerializeField] @@ -140,7 +140,7 @@ void OnDeviceChange(InputDevice device, InputDeviceChange change) } } - void SetDevice(InputDevice device, bool add=true) + void SetDevice(InputDevice device, bool add = true) { uiController?.SetDevice(device, add); @@ -159,13 +159,13 @@ void SetDevice(InputDevice device, bool add=true) listKeyboard.Remove(keyboard); return; case Touchscreen screen: - if(add) + if (add) listScreen.Add(screen); else listScreen.Remove(screen); return; case Gamepad pad: - if(add) + if (add) listGamepad.Add(pad); else listGamepad.Remove(pad); @@ -199,9 +199,11 @@ void OnEnable() m_InterpolatingCameraState.SetFromTransform(transform); } -//--------------------------------------------------------------------------------------------------------------------- - Vector3 GetTranslationFromInput(Vector2 input) { - if (!invertY) { + //--------------------------------------------------------------------------------------------------------------------- + Vector3 GetTranslationFromInput(Vector2 input) + { + if (!invertY) + { input.y *= -1; } @@ -211,11 +213,12 @@ Vector3 GetTranslationFromInput(Vector2 input) { return dir; } -//--------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------- void UpdateTargetCameraStateFromInput(Vector2 input) { - if (!invertY) { + if (!invertY) + { input.y *= -1; } float mouseSensitivityFactor = mouseSensitivityCurve.Evaluate(input.magnitude); @@ -224,7 +227,7 @@ void UpdateTargetCameraStateFromInput(Vector2 input) m_TargetCameraState.pitch += input.y * mouseSensitivityFactor; } -//--------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------- Vector3 GetInputTranslationDirection() { @@ -392,14 +395,17 @@ void ResetCamera() } -//--------------------------------------------------------------------------------------------------------------------- - static bool IsMouseDragged(Mouse m, bool useLeftButton) { + //--------------------------------------------------------------------------------------------------------------------- + static bool IsMouseDragged(Mouse m, bool useLeftButton) + { if (null == m) return false; - if (Screen.safeArea.Contains(m.position.ReadValue())) { + if (Screen.safeArea.Contains(m.position.ReadValue())) + { //check left/right click - if ((useLeftButton && m.leftButton.isPressed) || (!useLeftButton && m.rightButton.isPressed)) { + if ((useLeftButton && m.leftButton.isPressed) || (!useLeftButton && m.rightButton.isPressed)) + { return true; } } diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs index 4ae2c6fec..46703bfe2 100644 --- a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/UIController.cs @@ -1,7 +1,7 @@ using System.Linq; using UnityEngine; -using UnityEngine.UI; using UnityEngine.InputSystem; +using UnityEngine.UI; namespace Unity.RenderStreaming.Samples { @@ -12,7 +12,8 @@ class UIController : MonoBehaviour [SerializeField] CanvasGroup canvasGroup; [SerializeField] Image pointer; [SerializeField] GameObject noticeTouchControl; - [SerializeField] private AnimationCurve transitionCurve = + [SerializeField] + private AnimationCurve transitionCurve = new AnimationCurve( new Keyframe(0.75f, 1f, 0f, 0f), new Keyframe(1f, 0f, 0f, 0f)); @@ -94,7 +95,7 @@ void FixedUpdate() } } -//---------------------------------------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------------------------------------- bool HighlightPointerFromMouse(Mouse mouse, Vector2Int screenSize) { if (mouse == null) diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs index 819e57c1a..601b2fc50 100644 --- a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/RemoteInput.cs @@ -1,11 +1,11 @@ using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine.InputSystem; -using UnityEngine.InputSystem.Users; -using UnityEngine.InputSystem.LowLevel; using UnityEngine.InputSystem.EnhancedTouch; +using UnityEngine.InputSystem.LowLevel; +using UnityEngine.InputSystem.Users; using UE = UnityEngine; -using System.Collections.Generic; -using System.Linq; namespace Unity.RenderStreaming.Samples { @@ -26,14 +26,16 @@ enum EventType Gamepad = 5, } - enum GamepadEventType { - ButtonUp = 0, - ButtonDown = 1, - ButtonPressed = 2, - Axis = 3 + enum GamepadEventType + { + ButtonUp = 0, + ButtonDown = 1, + ButtonPressed = 2, + Axis = 3 } - enum GamepadKeyCode { + enum GamepadKeyCode + { Button0 = 0, Button1, Button2, @@ -161,13 +163,13 @@ public void Dispose() GC.SuppressFinalize(this); } -//--------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------- public void ProcessInput(byte[] bytes) { if (bytes == null) throw new ArgumentNullException(); - if(bytes.Length == 0) + if (bytes.Length == 0) throw new ArgumentException("byte length is zero"); switch ((EventType)bytes[0]) @@ -222,7 +224,7 @@ public void ProcessInput(byte[] bytes) ChangeEndStateUnusedTouches(touches); } } - + break; case EventType.ButtonClick: var elementId = BitConverter.ToInt16(bytes, 1); @@ -231,7 +233,7 @@ public void ProcessInput(byte[] bytes) case EventType.Gamepad: { GamepadEventType gamepadEventType = (GamepadEventType)bytes[1]; - + switch (gamepadEventType) { case GamepadEventType.ButtonDown: @@ -240,7 +242,7 @@ public void ProcessInput(byte[] bytes) { var buttonIndex = bytes[2]; var value = BitConverter.ToDouble(bytes, 3); - ProcessGamepadButtonEvent(gamepadEventType, (GamepadKeyCode) buttonIndex, value); + ProcessGamepadButtonEvent(gamepadEventType, (GamepadKeyCode)buttonIndex, value); } break; case GamepadEventType.Axis: @@ -248,7 +250,7 @@ public void ProcessInput(byte[] bytes) var buttonIndex = bytes[2]; var x = BitConverter.ToDouble(bytes, 3); var y = BitConverter.ToDouble(bytes, 11); - ProcessGamepadAxisEvent(x, y, (GamepadKeyCode) buttonIndex); + ProcessGamepadAxisEvent(x, y, (GamepadKeyCode)buttonIndex); } break; } @@ -263,7 +265,7 @@ void ProcessGamepadButtonEvent(GamepadEventType state, GamepadKeyCode buttonInde { GamepadButton buttonToUpdate = GamepadButton.DpadUp; GamepadState gamepadState = m_gamepadState; - switch(buttonIndex) + switch (buttonIndex) { case GamepadKeyCode.DpadUp: buttonToUpdate = GamepadButton.DpadUp; @@ -301,17 +303,17 @@ void ProcessGamepadButtonEvent(GamepadEventType state, GamepadKeyCode buttonInde break; case GamepadKeyCode.Button7: buttonToUpdate = GamepadButton.RightTrigger; - gamepadState.rightTrigger = (float) value; + gamepadState.rightTrigger = (float)value; break; case GamepadKeyCode.Axis0Button: buttonToUpdate = GamepadButton.LeftStick; break; case GamepadKeyCode.Axis1Button: buttonToUpdate = GamepadButton.RightStick; - break; + break; default: UE.Debug.Log("Unmapped button code: " + buttonIndex); - break; + break; } m_gamepadState = gamepadState.WithButton(buttonToUpdate, GamepadEventType.ButtonDown == state || GamepadEventType.ButtonPressed == state); } @@ -319,17 +321,17 @@ void ProcessGamepadButtonEvent(GamepadEventType state, GamepadKeyCode buttonInde void ProcessGamepadAxisEvent(double x, double y, GamepadKeyCode axisKeyCode) { GamepadState gamepadState = m_gamepadState; - if(axisKeyCode == GamepadKeyCode.Axis0) + if (axisKeyCode == GamepadKeyCode.Axis0) gamepadState.leftStick = new UE.Vector2((float)x, (float)y); - if(axisKeyCode == GamepadKeyCode.Axis1) + if (axisKeyCode == GamepadKeyCode.Axis1) gamepadState.rightStick = new UE.Vector2((float)x, (float)y); m_gamepadState = gamepadState; } -#endregion + #endregion void ProcessKeyEvent(KeyboardEventType state, bool repeat, byte keyCode, char character) { - switch(state) + switch (state) { case KeyboardEventType.KeyDown: if (!repeat) @@ -337,7 +339,7 @@ void ProcessKeyEvent(KeyboardEventType state, bool repeat, byte keyCode, char ch m_keyboardState.Set((Key)keyCode, true); UnityInputSystem.QueueStateEvent(RemoteKeyboard, m_keyboardState); } - if(character != 0) + if (character != 0) { UnityInputSystem.QueueTextEvent(RemoteKeyboard, character); } @@ -353,7 +355,8 @@ void ProcessMouseMoveEvent(short x, short y, byte button) { UnityEngine.Vector2Int pos = new UnityEngine.Vector2Int(x, y); UnityEngine.Vector2Int delta = pos - m_prevMousePos; - UnityInputSystem.QueueStateEvent(RemoteMouse, new MouseState { + UnityInputSystem.QueueStateEvent(RemoteMouse, new MouseState + { position = pos, delta = delta, buttons = button diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs index 9e6b72919..f0373aba4 100644 --- a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs @@ -51,10 +51,10 @@ public override void SetChannel(string connectionId, RTCDataChannel channel) { if (remoteInput != null) { - onDeviceChange?.Invoke(remoteInput.RemoteGamepad, InputDeviceChange.Removed); - onDeviceChange?.Invoke(remoteInput.RemoteKeyboard, InputDeviceChange.Removed); - onDeviceChange?.Invoke(remoteInput.RemoteMouse, InputDeviceChange.Removed); - onDeviceChange?.Invoke(remoteInput.RemoteTouchscreen, InputDeviceChange.Removed); + onDeviceChange?.Invoke(remoteInput.RemoteGamepad, InputDeviceChange.Removed); + onDeviceChange?.Invoke(remoteInput.RemoteKeyboard, InputDeviceChange.Removed); + onDeviceChange?.Invoke(remoteInput.RemoteMouse, InputDeviceChange.Removed); + onDeviceChange?.Invoke(remoteInput.RemoteTouchscreen, InputDeviceChange.Removed); remoteInput.Dispose(); remoteInput = null; } @@ -64,10 +64,10 @@ public override void SetChannel(string connectionId, RTCDataChannel channel) remoteInput = RemoteInputReceiver.Create(); remoteInput.ActionButtonClick = OnButtonClick; channel.OnMessage += remoteInput.ProcessInput; - onDeviceChange?.Invoke(remoteInput.RemoteGamepad, InputDeviceChange.Added); - onDeviceChange?.Invoke(remoteInput.RemoteKeyboard, InputDeviceChange.Added); - onDeviceChange?.Invoke(remoteInput.RemoteMouse, InputDeviceChange.Added); - onDeviceChange?.Invoke(remoteInput.RemoteTouchscreen, InputDeviceChange.Added); + onDeviceChange?.Invoke(remoteInput.RemoteGamepad, InputDeviceChange.Added); + onDeviceChange?.Invoke(remoteInput.RemoteKeyboard, InputDeviceChange.Added); + onDeviceChange?.Invoke(remoteInput.RemoteMouse, InputDeviceChange.Added); + onDeviceChange?.Invoke(remoteInput.RemoteTouchscreen, InputDeviceChange.Added); } base.SetChannel(connectionId, channel); } diff --git a/com.unity.renderstreaming/Tests/Editor/EditorTest.cs b/com.unity.renderstreaming/Tests/Editor/EditorTest.cs index 5d3d2eaef..55077b1d7 100644 --- a/com.unity.renderstreaming/Tests/Editor/EditorTest.cs +++ b/com.unity.renderstreaming/Tests/Editor/EditorTest.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using UnityEngine; +using NUnit.Framework.Interfaces; using UnityEditor; using UnityEditor.TestTools; +using UnityEngine; using UnityEngine.TestRunner; -using NUnit.Framework.Interfaces; [assembly: TestPlayerBuildModifier(typeof(Unity.RenderStreaming.EditorTest.BuildModifier))] [assembly: TestRunCallback(typeof(Unity.RenderStreaming.EditorTest.TestListener))] @@ -40,7 +40,7 @@ public void RunStarted(ITest testsToRun) public void RunFinished(ITestResult testResults) { - if(temp != null) + if (temp != null) RenderStreaming.Settings = temp; } diff --git a/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs index 30bb65522..73e091ea5 100644 --- a/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs +++ b/com.unity.renderstreaming/Tests/Editor/RenderStreamingTest.cs @@ -48,7 +48,7 @@ public void SignalingSettings() Assert.That(() => RenderStreamingEditor.SetSignalingSettings(null), Throws.ArgumentNullException); var url = "wss://127.0.0.1:8081"; - var iceServers = new IceServer[] {new IceServer(new string[] {"stun:stun.l.google.com:19302"})}; + var iceServers = new IceServer[] { new IceServer(new string[] { "stun:stun.l.google.com:19302" }) }; var signalingSettings = new WebSocketSignalingSettings(url, iceServers); Assert.That(() => RenderStreamingEditor.SetSignalingSettings(signalingSettings), Throws.Nothing); diff --git a/com.unity.renderstreaming/Tests/Editor/RequestJobTest.cs b/com.unity.renderstreaming/Tests/Editor/RequestJobTest.cs index 33b9ab396..aafd7d76c 100644 --- a/com.unity.renderstreaming/Tests/Editor/RequestJobTest.cs +++ b/com.unity.renderstreaming/Tests/Editor/RequestJobTest.cs @@ -1,9 +1,9 @@ -using NUnit.Framework; //Timeout, Assert using System.Collections; //IEnumerator -using UnityEngine.TestTools; //UnityTest +using NUnit.Framework; //Timeout, Assert using Unity.RenderStreaming.Editor; //RequestJobManager -using UnityEditor.PackageManager.Requests; //ListRequest, AddRequest, etc using UnityEditor.PackageManager; //PackageCollection +using UnityEditor.PackageManager.Requests; //ListRequest, AddRequest, etc +using UnityEngine.TestTools; //UnityTest namespace Unity.RenderStreaming.EditorTest { diff --git a/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs b/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs index ff7d99067..b67bfa618 100644 --- a/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/InputPositionCorrectorTest.cs @@ -16,7 +16,7 @@ void OnEvent(InputEventPtr ptr, InputDevice device) [Test] public void Invoke() { - System.Action< InputEventPtr, InputDevice> onEvent = OnEvent; + System.Action onEvent = OnEvent; var corrector = new InputPositionCorrector(onEvent); Assert.That(corrector.inputRegion, Is.EqualTo(Rect.zero)); Assert.That(corrector.outputRegion, Is.EqualTo(Rect.zero)); diff --git a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputDeviceExtensionTest.cs b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputDeviceExtensionTest.cs index 434209dd8..da998ff94 100644 --- a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputDeviceExtensionTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputDeviceExtensionTest.cs @@ -1,8 +1,7 @@ using NUnit.Framework; +using Unity.RenderStreaming.InputSystem; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Layouts; -using Unity.RenderStreaming.InputSystem; - using Assert = NUnit.Framework.Assert; namespace Unity.RenderStreaming.RuntimeTest diff --git a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs index 9b03cff88..8f27fe7bd 100644 --- a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs @@ -1,10 +1,10 @@ using System.Collections; using NUnit.Framework; -using UnityEngine.InputSystem; using Unity.RenderStreaming.InputSystem; using Unity.RenderStreaming.RuntimeTest.Signaling; using Unity.WebRTC; using UnityEngine; +using UnityEngine.InputSystem; using UnityEngine.TestTools; using Assert = NUnit.Framework.Assert; diff --git a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs index 5cab30f47..9d8042996 100644 --- a/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/PrivateSignalingTest.cs @@ -1,7 +1,7 @@ using System; using System.Collections; -using System.Threading; using System.Diagnostics; +using System.Threading; using NUnit.Framework; using Unity.RenderStreaming.RuntimeTest.Signaling; using Unity.RenderStreaming.Signaling; @@ -15,7 +15,7 @@ namespace Unity.RenderStreaming.RuntimeTest [TestFixture(typeof(WebSocketSignaling))] [TestFixture(typeof(HttpSignaling))] [TestFixture(typeof(MockSignaling))] - [UnityPlatform(exclude = new[] {RuntimePlatform.IPhonePlayer})] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] [ConditionalIgnore(ConditionalIgnore.IL2CPP, "Process.Start does not implement in IL2CPP.")] class PrivateSignalingTest : IPrebuildSetup { @@ -162,7 +162,7 @@ public IEnumerator UnitySetUp() { RTCConfiguration config = default; RTCIceCandidate candidate_ = null; - config.iceServers = new[] {new RTCIceServer {urls = new[] {"stun:stun.l.google.com:19302"}}}; + config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingEventProviderTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingEventProviderTest.cs index 256e33104..6d6bebd91 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingEventProviderTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingEventProviderTest.cs @@ -1,7 +1,7 @@ using System; -using UnityEngine; using NUnit.Framework; using Unity.WebRTC; +using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.TestTools; diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs index f3a39ed33..d2c313f90 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerInternalTest.cs @@ -1,6 +1,6 @@ using System; -using System.Linq; using System.Collections; +using System.Linq; using NUnit.Framework; using Unity.RenderStreaming.RuntimeTest.Signaling; using Unity.WebRTC; diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs index 1ee313c6d..50e3d828a 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingManagerTest.cs @@ -59,7 +59,7 @@ public void RunDefault() ISignaling mock = new MockSignaling(); component.runOnAwake = false; component.gameObject.SetActive(true); - component.Run(handlers:handlers); + component.Run(handlers: handlers); } @@ -71,8 +71,8 @@ public void ThrowExceptionIfHandlerIsNullOrEmpty() component.gameObject.SetActive(true); Assert.That(() => component.Run(signaling: mock), Throws.InvalidOperationException); - var handlers = new SignalingHandlerBase[] {}; - Assert.That(() => component.Run(signaling: mock, handlers:handlers), + var handlers = new SignalingHandlerBase[] { }; + Assert.That(() => component.Run(signaling: mock, handlers: handlers), Throws.InvalidOperationException); } @@ -85,9 +85,9 @@ public void RunAgain() ISignaling mock = new MockSignaling(); component.runOnAwake = false; component.gameObject.SetActive(true); - component.Run(signaling:mock, handlers:handlers); + component.Run(signaling: mock, handlers: handlers); component.Stop(); - component.Run(signaling:mock, handlers:handlers); + component.Run(signaling: mock, handlers: handlers); } [Test] @@ -142,7 +142,7 @@ public void EvaluateCommandlineArguments() settings = new HttpSignalingSettings("http://127.0.0.1"); Assert.That(settings.iceServers, Is.Empty); - arguments = new string[]{ "-signalingType", "websocket" }; + arguments = new string[] { "-signalingType", "websocket" }; Assert.That(SignalingManager.EvaluateCommandlineArguments(ref settings, arguments), Is.True); Assert.That(settings, Is.TypeOf()); Assert.That(settings.iceServers, Is.Not.Empty); diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs index 4f3d5a786..b5babf6b6 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs @@ -11,10 +11,10 @@ class IceServerTest public void Clone() { var iceServer = new IceServer( - urls: new[]{"stun:stun.l.google.com:19302"}, - username:"username", - credentialType:IceCredentialType.Password, - credential:"password"); + urls: new[] { "stun:stun.l.google.com:19302" }, + username: "username", + credentialType: IceCredentialType.Password, + credential: "password"); var copied = iceServer.Clone(); Assert.That(copied.urls, Is.EqualTo(iceServer.urls)); @@ -46,13 +46,13 @@ public void WebSocketSignalingSettings() Assert.That(() => new WebSocketSignalingSettings(url: null), Throws.Exception.TypeOf()); var url = "ws://localhost"; - settings = new WebSocketSignalingSettings(url:url); + settings = new WebSocketSignalingSettings(url: url); Assert.That(settings.url, Is.EqualTo(url)); Assert.That(settings.iceServers, Is.Empty); var iceUrl = "stun:stun.l.google.com:19302"; var iceServers = new[] { new IceServer(urls: new[] { iceUrl }) }; - settings = new WebSocketSignalingSettings(url: url, iceServers:iceServers); + settings = new WebSocketSignalingSettings(url: url, iceServers: iceServers); Assert.That(settings.iceServers.Count, Is.EqualTo(1)); Assert.That(settings.iceServers.ElementAt(0).username, Is.Null); Assert.That(settings.iceServers.ElementAt(0).credential, Is.Null); diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs index eb00f578e..1e495d54e 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingTest.cs @@ -1,7 +1,7 @@ using System; using System.Collections; -using System.Threading; using System.Diagnostics; +using System.Threading; using NUnit.Framework; using Unity.RenderStreaming.RuntimeTest.Signaling; using Unity.RenderStreaming.Signaling; @@ -15,7 +15,7 @@ namespace Unity.RenderStreaming.RuntimeTest [TestFixture(typeof(WebSocketSignaling))] [TestFixture(typeof(HttpSignaling))] [TestFixture(typeof(MockSignaling))] - [UnityPlatform(exclude = new[] {RuntimePlatform.IPhonePlayer})] + [UnityPlatform(exclude = new[] { RuntimePlatform.IPhonePlayer })] [ConditionalIgnore(ConditionalIgnore.IL2CPP, "Process.Start does not implement in IL2CPP.")] class SignalingTest : IPrebuildSetup { @@ -129,7 +129,7 @@ ISignaling CreateSignaling(Type type, SynchronizationContext mainThread) { var settings = new HttpSignalingSettings ( - url: $"http://localhost:{TestUtility.PortNumber}", + url: $"http://localhost:{TestUtility.PortNumber}", interval: 100 ); return new HttpSignaling(settings, mainThread); @@ -147,7 +147,7 @@ public IEnumerator UnitySetUp() { RTCConfiguration config = default; RTCIceCandidate candidate_ = null; - config.iceServers = new[] {new RTCIceServer {urls = new[] {"stun:stun.l.google.com:19302"}}}; + config.iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }; var peer1 = new RTCPeerConnection(ref config); var peer2 = new RTCPeerConnection(ref config); @@ -226,7 +226,8 @@ public IEnumerator OnOffer() signaling2.Start(); yield return new WaitUntil(() => startRaised1 && startRaised2); - signaling1.OnCreateConnection += (s, connectionId, polite) => { + signaling1.OnCreateConnection += (s, connectionId, polite) => + { connectionId1 = connectionId; }; signaling1.OnDestroyConnection += (signaling, id) => @@ -235,7 +236,8 @@ public IEnumerator OnOffer() }; signaling1.OpenConnection(_connectionId); - signaling2.OnCreateConnection += (s, connectionId, polite) => { + signaling2.OnCreateConnection += (s, connectionId, polite) => + { connectionId2 = connectionId; }; signaling1.OnDestroyConnection += (signaling, id) => @@ -276,11 +278,13 @@ public IEnumerator OnAnswer() signaling2.Start(); yield return new WaitUntil(() => startRaised1 && startRaised2); - signaling1.OnCreateConnection += (s, connectionId, polite) => { + signaling1.OnCreateConnection += (s, connectionId, polite) => + { connectionId1 = connectionId; }; signaling1.OpenConnection(Guid.NewGuid().ToString()); - signaling2.OnCreateConnection += (s, connectionId, polite) => { + signaling2.OnCreateConnection += (s, connectionId, polite) => + { connectionId2 = connectionId; }; signaling2.OpenConnection(Guid.NewGuid().ToString()); @@ -314,11 +318,13 @@ public IEnumerator OnCandidate() signaling2.Start(); yield return new WaitUntil(() => startRaised1 && startRaised2); - signaling1.OnCreateConnection += (s, connectionId, polite) => { + signaling1.OnCreateConnection += (s, connectionId, polite) => + { connectionId1 = connectionId; }; signaling1.OpenConnection(Guid.NewGuid().ToString()); - signaling2.OnCreateConnection += (s, connectionId, polite) => { + signaling2.OnCreateConnection += (s, connectionId, polite) => + { connectionId2 = connectionId; }; signaling2.OpenConnection(Guid.NewGuid().ToString()); @@ -359,11 +365,13 @@ public IEnumerator OnStop() signaling2.Start(); yield return new WaitUntil(() => startRaised1 && startRaised2); - signaling1.OnCreateConnection += (s, connectionId, polite) => { + signaling1.OnCreateConnection += (s, connectionId, polite) => + { connectionId1 = connectionId; }; signaling1.OpenConnection(Guid.NewGuid().ToString()); - signaling2.OnCreateConnection += (s, connectionId, polite) => { + signaling2.OnCreateConnection += (s, connectionId, polite) => + { connectionId2 = connectionId; }; signaling2.OpenConnection(Guid.NewGuid().ToString()); diff --git a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs index 43d1fd158..819adc6e7 100644 --- a/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/StreamingComponentTest.cs @@ -1,14 +1,14 @@ using System; -using System.Linq; using System.Collections; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using Unity.Collections; -using UnityEngine; using Unity.WebRTC; -using UnityEngine.TestTools; +using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Users; +using UnityEngine.TestTools; namespace Unity.RenderStreaming.RuntimeTest { @@ -23,7 +23,7 @@ public void GetAvailableCodec() Assert.That(codecs.Any(codec => codec.name == "VP9")); Assert.That(codecs.Any(codec => codec.name == "AV1")); - foreach(var codec in codecs) + foreach (var codec in codecs) { Assert.That(codec.name, Is.Not.Empty); Assert.That(codec.mimeType, Is.Not.Empty); @@ -298,7 +298,7 @@ public void GetAvailableCodec() { IEnumerable codecs = AudioStreamSender.GetAvailableCodecs(); Assert.That(codecs, Is.Not.Empty); - foreach(var codec in codecs) + foreach (var codec in codecs) { Assert.That(codec.name, Is.Not.Empty); Assert.That(codec.mimeType, Is.Not.Empty); @@ -591,7 +591,7 @@ public void SetChannel() Assert.That(receiver.currentActionMap, Is.Null); receiver.currentActionMap = new InputActionMap(); Assert.That(receiver.actionEvents, Is.Not.Null); - receiver.actionEvents = new PlayerInput.ActionEvent[]{}; + receiver.actionEvents = new PlayerInput.ActionEvent[] { }; receiver.SwitchCurrentActionMap(mapName); diff --git a/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs b/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs index cf822040e..de04bc4b4 100644 --- a/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs +++ b/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs @@ -1,160 +1,161 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using UnityEditor; using System; +using System.Collections; +using System.Collections.Generic; using System.IO; using System.Reflection; +using UnityEditor; +using UnityEngine; [CustomEditor(typeof(Readme))] [InitializeOnLoad] -public class ReadmeEditor : Editor { - - static string kShowedReadmeSessionStateName = "ReadmeEditor.showedReadme"; - - static float kSpace = 16f; - - static ReadmeEditor() - { - EditorApplication.delayCall += SelectReadmeAutomatically; - } - - static void SelectReadmeAutomatically() - { - if (!SessionState.GetBool(kShowedReadmeSessionStateName, false )) - { - var readme = SelectReadme(); - SessionState.SetBool(kShowedReadmeSessionStateName, true); - - if (readme && !readme.loadedLayout) - { - LoadLayout(); - readme.loadedLayout = true; - } - } - } - - static void LoadLayout() - { - var assembly = typeof(EditorApplication).Assembly; - var windowLayoutType = assembly.GetType("UnityEditor.WindowLayout", true); - var method = windowLayoutType.GetMethod("LoadWindowLayout", BindingFlags.Public | BindingFlags.Static); - method.Invoke(null, new object[]{Path.Combine(Application.dataPath, "TutorialInfo/Layout.wlt"), false}); - } - - [MenuItem("Tutorial/Show Tutorial Instructions")] - static Readme SelectReadme() - { - var ids = AssetDatabase.FindAssets("Readme t:Readme"); - if (ids.Length == 1) - { - var readmeObject = AssetDatabase.LoadMainAssetAtPath(AssetDatabase.GUIDToAssetPath(ids[0])); - - Selection.objects = new UnityEngine.Object[]{readmeObject}; - - return (Readme)readmeObject; - } - else - { - Debug.Log("Couldn't find a readme"); - return null; - } - } - - protected override void OnHeaderGUI() - { - var readme = (Readme)target; - Init(); - - var iconWidth = Mathf.Min(EditorGUIUtility.currentViewWidth/3f - 20f, 128f); - - GUILayout.BeginHorizontal("In BigTitle"); - { - GUILayout.Label(readme.icon, GUILayout.Width(iconWidth), GUILayout.Height(iconWidth)); - GUILayout.Label(readme.title, TitleStyle); - } - GUILayout.EndHorizontal(); - } - - public override void OnInspectorGUI() - { - var readme = (Readme)target; - Init(); - - foreach (var section in readme.sections) - { - if (!string.IsNullOrEmpty(section.heading)) - { - GUILayout.Label(section.heading, HeadingStyle); - } - if (!string.IsNullOrEmpty(section.text)) - { - GUILayout.Label(section.text, BodyStyle); - } - if (!string.IsNullOrEmpty(section.linkText)) - { - if (LinkLabel(new GUIContent(section.linkText))) - { - Application.OpenURL(section.url); - } - } - GUILayout.Space(kSpace); - } - } - - - bool m_Initialized; - - GUIStyle LinkStyle { get { return m_LinkStyle; } } - [SerializeField] GUIStyle m_LinkStyle; - - GUIStyle TitleStyle { get { return m_TitleStyle; } } - [SerializeField] GUIStyle m_TitleStyle; - - GUIStyle HeadingStyle { get { return m_HeadingStyle; } } - [SerializeField] GUIStyle m_HeadingStyle; - - GUIStyle BodyStyle { get { return m_BodyStyle; } } - [SerializeField] GUIStyle m_BodyStyle; - - void Init() - { - if (m_Initialized) - return; - m_BodyStyle = new GUIStyle(EditorStyles.label); - m_BodyStyle.wordWrap = true; - m_BodyStyle.fontSize = 14; +public class ReadmeEditor : Editor +{ + + static string kShowedReadmeSessionStateName = "ReadmeEditor.showedReadme"; + + static float kSpace = 16f; + + static ReadmeEditor() + { + EditorApplication.delayCall += SelectReadmeAutomatically; + } + + static void SelectReadmeAutomatically() + { + if (!SessionState.GetBool(kShowedReadmeSessionStateName, false)) + { + var readme = SelectReadme(); + SessionState.SetBool(kShowedReadmeSessionStateName, true); + + if (readme && !readme.loadedLayout) + { + LoadLayout(); + readme.loadedLayout = true; + } + } + } + + static void LoadLayout() + { + var assembly = typeof(EditorApplication).Assembly; + var windowLayoutType = assembly.GetType("UnityEditor.WindowLayout", true); + var method = windowLayoutType.GetMethod("LoadWindowLayout", BindingFlags.Public | BindingFlags.Static); + method.Invoke(null, new object[] { Path.Combine(Application.dataPath, "TutorialInfo/Layout.wlt"), false }); + } + + [MenuItem("Tutorial/Show Tutorial Instructions")] + static Readme SelectReadme() + { + var ids = AssetDatabase.FindAssets("Readme t:Readme"); + if (ids.Length == 1) + { + var readmeObject = AssetDatabase.LoadMainAssetAtPath(AssetDatabase.GUIDToAssetPath(ids[0])); + + Selection.objects = new UnityEngine.Object[] { readmeObject }; + + return (Readme)readmeObject; + } + else + { + Debug.Log("Couldn't find a readme"); + return null; + } + } + + protected override void OnHeaderGUI() + { + var readme = (Readme)target; + Init(); + + var iconWidth = Mathf.Min(EditorGUIUtility.currentViewWidth / 3f - 20f, 128f); + + GUILayout.BeginHorizontal("In BigTitle"); + { + GUILayout.Label(readme.icon, GUILayout.Width(iconWidth), GUILayout.Height(iconWidth)); + GUILayout.Label(readme.title, TitleStyle); + } + GUILayout.EndHorizontal(); + } + + public override void OnInspectorGUI() + { + var readme = (Readme)target; + Init(); + + foreach (var section in readme.sections) + { + if (!string.IsNullOrEmpty(section.heading)) + { + GUILayout.Label(section.heading, HeadingStyle); + } + if (!string.IsNullOrEmpty(section.text)) + { + GUILayout.Label(section.text, BodyStyle); + } + if (!string.IsNullOrEmpty(section.linkText)) + { + if (LinkLabel(new GUIContent(section.linkText))) + { + Application.OpenURL(section.url); + } + } + GUILayout.Space(kSpace); + } + } + + + bool m_Initialized; + + GUIStyle LinkStyle { get { return m_LinkStyle; } } + [SerializeField] GUIStyle m_LinkStyle; + + GUIStyle TitleStyle { get { return m_TitleStyle; } } + [SerializeField] GUIStyle m_TitleStyle; + + GUIStyle HeadingStyle { get { return m_HeadingStyle; } } + [SerializeField] GUIStyle m_HeadingStyle; + + GUIStyle BodyStyle { get { return m_BodyStyle; } } + [SerializeField] GUIStyle m_BodyStyle; + + void Init() + { + if (m_Initialized) + return; + m_BodyStyle = new GUIStyle(EditorStyles.label); + m_BodyStyle.wordWrap = true; + m_BodyStyle.fontSize = 14; m_BodyStyle.richText = true; - - m_TitleStyle = new GUIStyle(m_BodyStyle); - m_TitleStyle.fontSize = 26; - - m_HeadingStyle = new GUIStyle(m_BodyStyle); + + m_TitleStyle = new GUIStyle(m_BodyStyle); + m_TitleStyle.fontSize = 26; + + m_HeadingStyle = new GUIStyle(m_BodyStyle); m_HeadingStyle.fontStyle = FontStyle.Bold; - m_HeadingStyle.fontSize = 18 ; - - m_LinkStyle = new GUIStyle(m_BodyStyle); - m_LinkStyle.wordWrap = false; - // Match selection color which works nicely for both light and dark skins - m_LinkStyle.normal.textColor = new Color (0x00/255f, 0x78/255f, 0xDA/255f, 1f); - m_LinkStyle.stretchWidth = false; - - m_Initialized = true; - } - - bool LinkLabel (GUIContent label, params GUILayoutOption[] options) - { - var position = GUILayoutUtility.GetRect(label, LinkStyle, options); - - Handles.BeginGUI (); - Handles.color = LinkStyle.normal.textColor; - Handles.DrawLine (new Vector3(position.xMin, position.yMax), new Vector3(position.xMax, position.yMax)); - Handles.color = Color.white; - Handles.EndGUI (); - - EditorGUIUtility.AddCursorRect (position, MouseCursor.Link); - - return GUI.Button (position, label, LinkStyle); - } + m_HeadingStyle.fontSize = 18; + + m_LinkStyle = new GUIStyle(m_BodyStyle); + m_LinkStyle.wordWrap = false; + // Match selection color which works nicely for both light and dark skins + m_LinkStyle.normal.textColor = new Color(0x00 / 255f, 0x78 / 255f, 0xDA / 255f, 1f); + m_LinkStyle.stretchWidth = false; + + m_Initialized = true; + } + + bool LinkLabel(GUIContent label, params GUILayoutOption[] options) + { + var position = GUILayoutUtility.GetRect(label, LinkStyle, options); + + Handles.BeginGUI(); + Handles.color = LinkStyle.normal.textColor; + Handles.DrawLine(new Vector3(position.xMin, position.yMax), new Vector3(position.xMax, position.yMax)); + Handles.color = Color.white; + Handles.EndGUI(); + + EditorGUIUtility.AddCursorRect(position, MouseCursor.Link); + + return GUI.Button(position, label, LinkStyle); + } } diff --git a/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Readme.cs b/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Readme.cs index 48843dbfd..9b0ae32d4 100644 --- a/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Readme.cs +++ b/com.unity.template.renderstreaming-hd/Assets/TutorialInfo/Scripts/Readme.cs @@ -1,14 +1,16 @@ -using System; +using System; using UnityEngine; -public class Readme : ScriptableObject { - public Texture2D icon; - public string title; - public Section[] sections; - public bool loadedLayout; - - [Serializable] - public class Section { - public string heading, text, linkText, url; - } +public class Readme : ScriptableObject +{ + public Texture2D icon; + public string title; + public Section[] sections; + public bool loadedLayout; + + [Serializable] + public class Section + { + public string heading, text, linkText, url; + } } From ba102703fdfe331a1026ea1a4f7aede99334896a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:14:13 +0900 Subject: [PATCH 073/117] chore: bump word-wrap from 1.2.3 to 1.2.4 in /WebApp/client (#917) Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4. - [Release notes](https://github.com/jonschlinkert/word-wrap/releases) - [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4) --- updated-dependencies: - dependency-name: word-wrap dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- WebApp/client/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WebApp/client/package-lock.json b/WebApp/client/package-lock.json index c39985323..92a66291b 100644 --- a/WebApp/client/package-lock.json +++ b/WebApp/client/package-lock.json @@ -5445,9 +5445,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9658,9 +9658,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "wrap-ansi": { From e5f14302197e89c643153c0a2f04a41ba038cce3 Mon Sep 17 00:00:00 2001 From: codec-abc Date: Wed, 19 Jul 2023 11:34:38 +0200 Subject: [PATCH 074/117] fix: Recompute video player position each time (#915) * Recompute video player position each time * fix testcase --------- Co-authored-by: kazuki --- WebApp/client/src/pointercorrect.js | 43 +++++++++++++---------- WebApp/client/src/sender.js | 4 +-- WebApp/client/test/domvideoelement.js | 11 ++++++ WebApp/client/test/pointercorrect.test.js | 19 ++++++---- 4 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 WebApp/client/test/domvideoelement.js diff --git a/WebApp/client/src/pointercorrect.js b/WebApp/client/src/pointercorrect.js index ee33a3022..04e0f61fe 100644 --- a/WebApp/client/src/pointercorrect.js +++ b/WebApp/client/src/pointercorrect.js @@ -7,10 +7,10 @@ export class PointerCorrector { /** * @param {Number} videoWidth * @param {Number} videoHeight - * @param {DOMRect} rect + * @param {HTMLVideoElement} videoElem */ - constructor(videoWidth, videoHeight, rect) { - this.reset(videoWidth, videoHeight, rect); + constructor(videoWidth, videoHeight, videoElem) { + this.reset(videoWidth, videoHeight, videoElem); } /** @@ -18,14 +18,15 @@ export class PointerCorrector { * @returns {Number[]} */ map(position) { + var rect = this._videoElem.getBoundingClientRect(); const _position = new Array(2); // (1) set origin point to zero - _position[0] = position[0] - this._rect.left; - _position[1] = position[1] - this._rect.top; + _position[0] = position[0] - rect.left; + _position[1] = position[1] - rect.top; // (2) translate Unity coordinate system (reverse y-axis) - _position[1] = this._rect.height - _position[1]; + _position[1] = rect.height - _position[1]; // (3) add offset of letterbox _position[0] -= this._contentRect.x; @@ -55,42 +56,44 @@ export class PointerCorrector { } /** - * @param {DOMRect} rect + * @param {HTMLVideoElement} videoElem */ - setRect(rect) { - this._rect = rect; + setRect(videoElem) { + this._videoElem = videoElem; this._reset(); } /** * @param {Number} videoWidth * @param {Number} videoHeight - * @param {DOMRect} rect + * @param {HTMLVideoElement} videoElem */ - reset(videoWidth, videoHeight, rect) { + reset(videoWidth, videoHeight, videoElem) { this._videoWidth = videoWidth; this._videoHeight = videoHeight; - this._rect = rect; + this._videoElem = videoElem; this._reset(); } get letterBoxType() { const videoRatio = this._videoHeight / this._videoWidth; - const rectRatio = this._rect.height / this._rect.width; + var rect = this._videoElem.getBoundingClientRect(); + const rectRatio = rect.height / rect.width; return videoRatio > rectRatio ? LetterBoxType.Vertical : LetterBoxType.Horizontal; } get letterBoxSize() { + var rect = this._videoElem.getBoundingClientRect(); switch(this.letterBoxType) { case LetterBoxType.Horizontal: { - const ratioWidth = this._rect.width / this._videoWidth; + const ratioWidth = rect.width / this._videoWidth; const height = this._videoHeight * ratioWidth; - return (this._rect.height - height) * 0.5; + return (rect.height - height) * 0.5; } case LetterBoxType.Vertical: { - const ratioHeight = this._rect.height / this._videoHeight; + const ratioHeight = rect.height / this._videoHeight; const width = this._videoWidth * ratioHeight; - return (this._rect.width - width) * 0.5; + return (rect.width - width) * 0.5; } } throw 'invalid status'; @@ -105,10 +108,12 @@ export class PointerCorrector { const letterBoxType = this.letterBoxType; const letterBoxSize = this.letterBoxSize; + var rect = this._videoElem.getBoundingClientRect(); + const x = letterBoxType == LetterBoxType.Vertical ? letterBoxSize : 0; const y = letterBoxType == LetterBoxType.Horizontal ? letterBoxSize : 0; - const width = letterBoxType == LetterBoxType.Vertical ? this._rect.width - letterBoxSize * 2 : this._rect.width; - const height = letterBoxType == LetterBoxType.Horizontal ? this._rect.height - letterBoxSize * 2 : this._rect.height; + const width = letterBoxType == LetterBoxType.Vertical ? rect.width - letterBoxSize * 2 : rect.width; + const height = letterBoxType == LetterBoxType.Horizontal ? rect.height - letterBoxSize * 2 : rect.height; return {x: x, y: y, width: width, height: height}; } diff --git a/WebApp/client/src/sender.js b/WebApp/client/src/sender.js index 3ca3eb841..bfd1c2936 100644 --- a/WebApp/client/src/sender.js +++ b/WebApp/client/src/sender.js @@ -19,7 +19,7 @@ export class Sender extends LocalInputManager { this._corrector = new PointerCorrector( this._elem.videoWidth, this._elem.videoHeight, - this._elem.getBoundingClientRect() + this._elem ); //since line 27 cannot complete resize initialization but can only monitor div dimension changes, line 26 needs to be reserved @@ -115,7 +115,7 @@ export class Sender extends LocalInputManager { this._corrector.reset( this._elem.videoWidth, this._elem.videoHeight, - this._elem.getBoundingClientRect() + this._elem ); } _onMouseEvent(event) { diff --git a/WebApp/client/test/domvideoelement.js b/WebApp/client/test/domvideoelement.js new file mode 100644 index 000000000..858f7d7c7 --- /dev/null +++ b/WebApp/client/test/domvideoelement.js @@ -0,0 +1,11 @@ +// mock class + +export class DOMHTMLVideoElement { + constructor(rect) { + this.rect = rect; + } + + getBoundingClientRect() { + return this.rect; + } +} diff --git a/WebApp/client/test/pointercorrect.test.js b/WebApp/client/test/pointercorrect.test.js index 8334ec510..d85857fa2 100644 --- a/WebApp/client/test/pointercorrect.test.js +++ b/WebApp/client/test/pointercorrect.test.js @@ -1,26 +1,30 @@ -import { +import { LetterBoxType, PointerCorrector } from "../src/pointercorrect.js"; import {DOMRect} from "./domrect.js"; +import {DOMHTMLVideoElement} from "./domvideoelement.js"; describe(`PointerCorrector.map`, () => { test('letterboxType', () => { const rect = new DOMRect(10, 10, 200, 200); - let corrector = new PointerCorrector(50, 100, rect); + const element = new DOMHTMLVideoElement(rect); + let corrector = new PointerCorrector(50, 100, element); expect(corrector.letterBoxType).toBe(LetterBoxType.Vertical); - corrector.reset(100, 50, rect); + corrector.reset(100, 50, element); expect(corrector.letterBoxType).toBe(LetterBoxType.Horizontal); }); test('letterboxSize', () => { const rect = new DOMRect(0, 0, 100, 100); - let corrector = new PointerCorrector(50, 100, rect); + const element = new DOMHTMLVideoElement(rect); + let corrector = new PointerCorrector(50, 100, element); expect(corrector.letterBoxSize).toBe(25); }); test('contentRect', () => { const rect = new DOMRect(0, 0, 100, 100); - let corrector = new PointerCorrector(50, 100, rect); + const element = new DOMHTMLVideoElement(rect); + let corrector = new PointerCorrector(50, 100, element); expect(corrector.contentRect.x).toBe(25); expect(corrector.contentRect.y).toBe(0); expect(corrector.contentRect.width).toBe(50); @@ -28,13 +32,14 @@ describe(`PointerCorrector.map`, () => { }); test('mapping', () => { const rect = new DOMRect(10, 10, 200, 200); + const element = new DOMHTMLVideoElement(rect); const videoWidth = 100; const videoHeight = 100; - let corrector = new PointerCorrector(videoWidth, videoHeight, rect); + let corrector = new PointerCorrector(videoWidth, videoHeight, element); const position = [10, 10]; const newPosition = corrector.map(position); expect(newPosition[0]).toBe(0); expect(newPosition[1]).toBe(100); }); -}); \ No newline at end of file +}); From e592a60a44bcde54dad872a3f798f9aea4ce99d1 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Mon, 24 Jul 2023 09:35:15 +0900 Subject: [PATCH 075/117] fix: Add workaround to fix InputField that not receiving input event (#920) * workaround: InputField * fix * fix test * fix * format * fix --- WebApp/client/src/charnumber.js | 109 ++++++++ WebApp/client/src/inputdevice.js | 13 +- WebApp/client/src/sender.js | 7 +- WebApp/client/test/inputdevice.test.js | 29 +- .../InputSystem/EmulateInputFieldEvent.cs | 251 ++++++++++++++++++ .../EmulateInputFieldEvent.cs.meta | 11 + .../Runtime/Scripts/InputSystem/Receiver.cs | 16 +- .../Unity.RenderStreaming.Runtime.asmdef | 5 + 8 files changed, 415 insertions(+), 26 deletions(-) create mode 100644 WebApp/client/src/charnumber.js create mode 100644 com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs create mode 100644 com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs.meta diff --git a/WebApp/client/src/charnumber.js b/WebApp/client/src/charnumber.js new file mode 100644 index 000000000..2c792379d --- /dev/null +++ b/WebApp/client/src/charnumber.js @@ -0,0 +1,109 @@ +// KeyboardEvent.charcode is already deprecated. +// +export const CharNumber = { + "Backspace": 8, + "Tab": 9, + "Enter": 13, + "Shift": 16, + "Control": 17, + "Alt": 18, + "Pause": 19, + "CapsLock": 20, + "Escape": 27, + " ": 32, + "!": 33, + "\"": 34, + "#": 35, + "$": 36, + "%": 37, + "&": 38, + "'": 39, + "(": 40, + ")": 41, + "*": 42, + "+": 43, + ",": 44, + "-": 45, + ".": 46, + "/": 47, + "0": 48, + "1": 49, + "2": 50, + "3": 51, + "4": 52, + "5": 53, + "6": 54, + "7": 55, + "8": 56, + "9": 57, + ":": 58, + ";": 59, + "<": 60, + "=": 61, + ">": 62, + "?": 63, + "@": 64, + "A": 65, + "B": 66, + "C": 67, + "D": 68, + "E": 69, + "F": 70, + "G": 71, + "H": 72, + "I": 73, + "J": 74, + "K": 75, + "L": 76, + "M": 77, + "N": 78, + "O": 79, + "P": 80, + "Q": 81, + "R": 82, + "S": 83, + "T": 84, + "U": 85, + "V": 86, + "W": 87, + "X": 88, + "Y": 89, + "Z": 90, + "[": 91, + "\\": 92, + "]": 93, + "^": 94, + "_": 95, + "`": 96, + "a": 97, + "b": 98, + "c": 99, + "d": 100, + "e": 101, + "f": 102, + "g": 103, + "h": 104, + "i": 105, + "j": 106, + "k": 107, + "l": 108, + "m": 109, + "n": 110, + "o": 111, + "p": 112, + "q": 113, + "r": 114, + "s": 115, + "t": 116, + "u": 117, + "v": 118, + "w": 119, + "x": 120, + "y": 121, + "z": 122, + "{": 123, + "|": 124, + "}": 125, + "~": 126, + "Delete": 127 + }; diff --git a/WebApp/client/src/inputdevice.js b/WebApp/client/src/inputdevice.js index a7f32f628..50090f447 100644 --- a/WebApp/client/src/inputdevice.js +++ b/WebApp/client/src/inputdevice.js @@ -2,6 +2,7 @@ import { MemoryHelper, } from "./memoryhelper.js"; +import { CharNumber } from "./charnumber.js"; import { Keymap } from "./keymap.js"; import { MouseButton } from "./mousebutton.js"; import { GamepadButton } from "./gamepadbutton.js"; @@ -630,18 +631,18 @@ export class TextEvent { /** * * @param {Number} deviceId - * @param {Number} character + * @param {KeyboardEvent} event * @param {Number} time * @returns {TextEvent} */ - static create(deviceId, character, time) { + static create(deviceId, event, time) { const eventSize = InputEvent.size + MemoryHelper.sizeOfInt; - let event = new TextEvent(); - event.baseEvent = new InputEvent(TextEvent.format, eventSize, deviceId, time); - event.character = character; - return event; + let textEvent = new TextEvent(); + textEvent.baseEvent = new InputEvent(TextEvent.format, eventSize, deviceId, time); + textEvent.character = CharNumber[event.key]; + return textEvent; } /** diff --git a/WebApp/client/src/sender.js b/WebApp/client/src/sender.js index bfd1c2936..f5fb66dff 100644 --- a/WebApp/client/src/sender.js +++ b/WebApp/client/src/sender.js @@ -134,8 +134,7 @@ export class Sender extends LocalInputManager { this._queueStateEvent(this.keyboard.currentState, this.keyboard); } // TextEvent - const key = event.key.charCodeAt(0); - this._queueTextEvent(this.keyboard, key); + this._queueTextEvent(this.keyboard, event); } else if(event.type == 'keyup') { this.keyboard.queueEvent(event); @@ -175,8 +174,8 @@ export class Sender extends LocalInputManager { 'event', {detail: { event: stateEvent, device: device}}); super.onEvent.dispatchEvent(e); } - _queueTextEvent(device, character) { - const textEvent = TextEvent.create(device.deviceId, character, this.timeSinceStartup); + _queueTextEvent(device, event) { + const textEvent = TextEvent.create(device.deviceId, event, this.timeSinceStartup); const e = new CustomEvent( 'event', {detail: { event: textEvent, device: device}}); super.onEvent.dispatchEvent(e); diff --git a/WebApp/client/test/inputdevice.test.js b/WebApp/client/test/inputdevice.test.js index f28aecdf0..5d39ab020 100644 --- a/WebApp/client/test/inputdevice.test.js +++ b/WebApp/client/test/inputdevice.test.js @@ -25,15 +25,15 @@ describe(`MouseState`, () => { let event; beforeEach(() => { event = new MouseEvent('click', { buttons:1, clientX:0, clientY:0}); - }); + }); test('format', () => { const format = new MouseState(event).format; expect(format).toBe(0x4d4f5553); - }); + }); test('buffer', () => { const state = new MouseState(event); expect(state.buffer.byteLength).toBeGreaterThan(0); - }); + }); }); describe(`with WheelEvent`, () => { let event; @@ -43,11 +43,11 @@ describe(`MouseState`, () => { test('format', () => { const format = new MouseState(event).format; expect(format).toBe(0x4d4f5553); - }); + }); test('buffer', () => { const state = new MouseState(event); expect(state.buffer.byteLength).toBeGreaterThan(0); - }); + }); }); }); @@ -59,7 +59,7 @@ describe(`KeyboardState`, () => { test('format', () => { const format = new KeyboardState(event).format; expect(format).toBe(0x4b455953); - }); + }); test('buffer', () => { const state = new KeyboardState(event); expect(state.buffer.byteLength).toBeGreaterThan(0); @@ -69,7 +69,7 @@ describe(`KeyboardState`, () => { describe(`TouchscreenState`, () => { let event; beforeEach(() => { - event = new TouchEvent("touchstart", { + event = new TouchEvent("touchstart", { changedTouches: [{ // InputInit identifier: 0, target: null, @@ -88,7 +88,7 @@ describe(`TouchscreenState`, () => { touchType: "direct" }] }); - }); + }); test('format', () => { const format = new TouchscreenState(event, null, Date.now()).format; expect(format).toBe(0x54534352); @@ -113,7 +113,7 @@ describe(`GamepadState`, () => { test('format', () => { const format = new GamepadState(event).format; expect(format).toBe(0x47504144); - }); + }); test('buffer', () => { const state = new GamepadState(event); expect(state.buffer.byteLength).toBeGreaterThan(0); @@ -134,14 +134,15 @@ describe(`StateEvent`, () => { describe(`TextEvent`, () => { test('buffer', () => { - const character = 0x41; - const textEvent = TextEvent.create(0, character, Date.now()); + const event = new KeyboardEvent('keydown', { code: 'KeyA', key: "a"}); + const textEvent = TextEvent.create(0, event, Date.now()); expect(new Int32Array(textEvent.buffer.slice(0, 4))[0]).toBe(TextEvent.format); const offset = InputEvent.size; - expect(new Uint32Array(textEvent.buffer.slice(offset, offset+4))[0]).toBe(character); + // 'a' is 97 + expect(new Uint32Array(textEvent.buffer.slice(offset, offset+4))[0]).toBe(97); }); }); - + describe(`Mouse`, () => { test('alignedSizeInBytes', () => { let device = new Mouse("Mouse", "Mouse", 1, null, null); @@ -168,4 +169,4 @@ describe(`Gamepad`, () => { let device = new Gamepad("Gamepad", "Gamepad", 1, null, null); expect(device).toBeInstanceOf(Gamepad); }); -}); \ No newline at end of file +}); diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs new file mode 100644 index 000000000..c4c6b424e --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs @@ -0,0 +1,251 @@ +using System.Collections.Generic; +#if URS_USE_TEXTMESHPRO +using TMPro; +#endif +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Controls; +using UnityEngine.InputSystem.LowLevel; + +namespace Unity.RenderStreaming.InputSystem +{ + /// + /// This partial class is for workaround to support Unity UI InputField. + /// + partial class Receiver + { + private static readonly Dictionary s_KeyMap = new Dictionary() + { + { (int)Key.Backspace, KeyCode.Backspace }, + { (int)Key.Tab, KeyCode.Tab }, + { (int)Key.Enter, KeyCode.Return }, + { (int)Key.Space, KeyCode.Space }, + { (int)Key.Comma, KeyCode.Comma }, + { (int)Key.Minus, KeyCode.Minus }, + { (int)Key.Period, KeyCode.Period }, + { (int)Key.Slash, KeyCode.Slash }, + { (int)Key.Digit0, KeyCode.Alpha0 }, + { (int)Key.Digit1, KeyCode.Alpha1 }, + { (int)Key.Digit2, KeyCode.Alpha2 }, + { (int)Key.Digit3, KeyCode.Alpha3 }, + { (int)Key.Digit4, KeyCode.Alpha4 }, + { (int)Key.Digit5, KeyCode.Alpha5 }, + { (int)Key.Digit6, KeyCode.Alpha6 }, + { (int)Key.Digit7, KeyCode.Alpha7 }, + { (int)Key.Digit8, KeyCode.Alpha8 }, + { (int)Key.Digit9, KeyCode.Alpha9 }, + { (int)Key.Semicolon, KeyCode.Semicolon }, + { (int)Key.Equals, KeyCode.Equals }, + { (int)Key.LeftBracket, KeyCode.LeftBracket }, + { (int)Key.Backslash, KeyCode.Backslash }, + { (int)Key.RightBracket, KeyCode.RightBracket }, + { (int)Key.Backquote, KeyCode.BackQuote }, + { (int)Key.Quote, KeyCode.Quote }, + { (int)Key.A, KeyCode.A }, + { (int)Key.B, KeyCode.B }, + { (int)Key.C, KeyCode.C }, + { (int)Key.D, KeyCode.D }, + { (int)Key.E, KeyCode.E }, + { (int)Key.F, KeyCode.F }, + { (int)Key.G, KeyCode.G }, + { (int)Key.H, KeyCode.H }, + { (int)Key.I, KeyCode.I }, + { (int)Key.J, KeyCode.J }, + { (int)Key.K, KeyCode.K }, + { (int)Key.L, KeyCode.L }, + { (int)Key.M, KeyCode.M }, + { (int)Key.N, KeyCode.N }, + { (int)Key.O, KeyCode.O }, + { (int)Key.P, KeyCode.P }, + { (int)Key.Q, KeyCode.Q }, + { (int)Key.R, KeyCode.R }, + { (int)Key.S, KeyCode.S }, + { (int)Key.T, KeyCode.T }, + { (int)Key.U, KeyCode.U }, + { (int)Key.V, KeyCode.V }, + { (int)Key.W, KeyCode.W }, + { (int)Key.X, KeyCode.X }, + { (int)Key.Y, KeyCode.Y }, + { (int)Key.Z, KeyCode.Z }, + { (int)Key.F1, KeyCode.F1 }, + { (int)Key.F2, KeyCode.F2 }, + { (int)Key.F3, KeyCode.F3 }, + { (int)Key.F4, KeyCode.F4 }, + { (int)Key.F5, KeyCode.F5 }, + { (int)Key.F6, KeyCode.F6 }, + { (int)Key.F7, KeyCode.F7 }, + { (int)Key.F8, KeyCode.F8 }, + { (int)Key.F9, KeyCode.F9 }, + { (int)Key.F10, KeyCode.F10 }, + { (int)Key.F11, KeyCode.F11 }, + { (int)Key.F12, KeyCode.F12 }, + { (int)Key.None, KeyCode.None }, + { (int)Key.LeftArrow, KeyCode.LeftArrow }, + { (int)Key.RightArrow, KeyCode.RightArrow }, + { (int)Key.UpArrow, KeyCode.UpArrow }, + { (int)Key.DownArrow, KeyCode.DownArrow }, + { (int)Key.LeftShift, KeyCode.LeftShift }, + { (int)Key.RightShift, KeyCode.RightShift }, + { (int)Key.Delete, KeyCode.Delete }, + { (int)Key.Escape, KeyCode.Escape }, + { (int)Key.LeftAlt, KeyCode.LeftAlt }, + { (int)Key.RightAlt, KeyCode.RightAlt }, + { (int)Key.LeftApple, KeyCode.LeftApple }, + { (int)Key.RightApple, KeyCode.RightApple } + }; + + interface IInputField + { + void ProcessEvent(Event e); + void ForceLabelUpdate(); + void AppendText(char character); + } + + class UGUIInputField : IInputField + { + InputField m_field; + public UGUIInputField(InputField field) + { + m_field = field; + } + + public void ProcessEvent(Event e) + { + m_field.ProcessEvent(e); + } + public void ForceLabelUpdate() + { + m_field.ForceLabelUpdate(); + } + public void AppendText(char character) + { + m_field.text += character; + } + } + +#if URS_USE_TEXTMESHPRO + class TMProInputField : IInputField + { + TMP_InputField m_field; + public TMProInputField(TMP_InputField field) + { + m_field = field; + } + + public void ProcessEvent(Event e) + { + m_field.ProcessEvent(e); + } + public void ForceLabelUpdate() + { + m_field.ForceLabelUpdate(); + } + public void AppendText(char character) + { + m_field.text += character; + } + } +#endif + + IInputField FindInputField(GameObject obj) + { + var field = obj.GetComponent(); + if (field != null) + return new UGUIInputField(field); + +#if URS_USE_TEXTMESHPRO + var tmpField = obj.GetComponent(); + if (tmpField != null) + return new TMProInputField(tmpField); +#endif + return null; + } + + (EventModifiers, KeyCode) GetEventModifiersAndKeyCode(InputEventPtr ptr) + { + EventModifiers modifiers = EventModifiers.None; + KeyCode keyCode = KeyCode.None; + foreach (var control in ptr.GetAllButtonPresses()) + { + if (control is KeyControl keyControl) + { + var key = keyControl.keyCode; + if (key == Key.LeftShift || key == Key.RightShift) + { + modifiers |= EventModifiers.Shift; + } + else if (key == Key.LeftCtrl || key == Key.RightCtrl) + { + modifiers |= EventModifiers.Control; + } + else if (key == Key.LeftAlt || key == Key.RightAlt) + { + modifiers |= EventModifiers.Alt; + } + else if (key == Key.LeftCommand || key == Key.RightCommand) + { + modifiers |= EventModifiers.Command; + } + else if (key == Key.CapsLock) + { + modifiers |= EventModifiers.CapsLock; + } + else if (s_KeyMap.TryGetValue((int)key, out var value)) + { + keyCode = value; + } + } + } + return (modifiers, keyCode); + } + + unsafe Event CreateEvent(InputEventPtr ptr) + { + var (modifiers, keyCode) = GetEventModifiersAndKeyCode(ptr); + + if (ptr.type == TextEvent.Type) + { + var textEventPtr = (TextEvent*)ptr.ToPointer(); + var utf32Char = textEventPtr->character; + if (utf32Char >= 0x10000) + { + // todo: not supported multibyte character. + return null; + } + + return new Event + { + type = EventType.KeyDown, + character = (char)utf32Char, + keyCode = keyCode, + modifiers = modifiers + }; + } + + return new Event + { + type = EventType.KeyDown, + keyCode = keyCode, + modifiers = modifiers + }; + } + + private void EmulateInputFieldEvent(InputEventPtr ptr) + { + var obj = UnityEngine.EventSystems.EventSystem.current?.currentSelectedGameObject; + if (obj == null) + return; + + var field = FindInputField(obj); + if (field == null) + return; + Event e = CreateEvent(ptr); + if (e != null) + { + field.ProcessEvent(e); + field.ForceLabelUpdate(); + } + } + } +} diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs.meta new file mode 100644 index 000000000..2e7b809fa --- /dev/null +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a3d7722788597434b979a31af26668c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs index 646ff5d26..01b8edc6d 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Receiver.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Unity.Collections.LowLevel.Unsafe; using Unity.WebRTC; using UnityEngine; using UnityEngine.InputSystem; @@ -15,7 +14,7 @@ namespace Unity.RenderStreaming.InputSystem /// /// /// - class Receiver : InputManager, IDisposable + partial class Receiver : InputManager, IDisposable { public override event Action onMessage; public new event Action onDeviceChange; @@ -28,6 +27,7 @@ class Receiver : InputManager, IDisposable private readonly List _registeredRemoteLayout = new List(); private InputPositionCorrector _corrector; private Action _onEvent; + /// /// /// @@ -182,6 +182,18 @@ public override void QueueEvent(InputEventPtr ptr) { base.QueueEvent(ptr); } + + // workaround: + // UnityEngine.UI.InputField and TMP_InputField depends on Event.PopEvent. + // Event.PopEvent is old event API, therefore EventSystem.QueueEvent doesn't queue events. + var eventType = ptr.type; + if (device is Keyboard && + (eventType == StateEvent.Type || + eventType == DeltaStateEvent.Type || + eventType == TextEvent.Type)) + { + EmulateInputFieldEvent(ptr); + } } /// diff --git a/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef b/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef index 3f807a144..4989625a5 100644 --- a/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef +++ b/com.unity.renderstreaming/Runtime/Unity.RenderStreaming.Runtime.asmdef @@ -40,6 +40,11 @@ "expression": "", "define": "URS_USE_AR_SUBSYSTEMS" }, + { + "name": "com.unity.textmeshpro", + "expression": "", + "define": "URS_USE_TEXTMESHPRO" + }, { "name": "com.unity.inputsystem", "expression": "1.1", From 65a7747d33e7e39044f2be06115ee4e4bbce594f Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Mon, 24 Jul 2023 09:35:39 +0900 Subject: [PATCH 076/117] refactor: Refactor editor script (#922) * recactor * refactor --- .../Editor/AudioStreamReceiverEditor.cs | 4 +- .../Editor/AudioStreamSenderEditor.cs | 16 +++---- .../Editor/InputSystem/InputReceiverEditor.cs | 32 ++++++++------ .../Editor/RenderStreamingSettingsEditor.cs | 4 +- .../Editor/SignalingManagerEditor.cs | 43 +++++++++++++------ .../Editor/VideoStreamReceiverEditor.cs | 8 ++-- .../Editor/VideoStreamSenderEditor.cs | 24 +++++------ .../Runtime/Scripts/AudioStreamReceiver.cs | 3 ++ .../Runtime/Scripts/AudioStreamSender.cs | 9 ++++ .../Runtime/Scripts/DataChannelBase.cs | 3 ++ .../Runtime/Scripts/InputReceiver.cs | 4 ++ .../Scripts/RenderStreamingSettings.cs | 3 ++ .../Runtime/Scripts/SignalingManager.cs | 7 +++ .../Runtime/Scripts/VideoStreamReceiver.cs | 4 ++ .../Runtime/Scripts/VideoStreamSender.cs | 15 ++++++- 15 files changed, 121 insertions(+), 58 deletions(-) diff --git a/com.unity.renderstreaming/Editor/AudioStreamReceiverEditor.cs b/com.unity.renderstreaming/Editor/AudioStreamReceiverEditor.cs index 34acbacf2..b84bbd6f0 100644 --- a/com.unity.renderstreaming/Editor/AudioStreamReceiverEditor.cs +++ b/com.unity.renderstreaming/Editor/AudioStreamReceiverEditor.cs @@ -19,8 +19,8 @@ class Styles void OnEnable() { - m_codec = serializedObject.FindProperty("m_Codec"); - m_targetAudioSource = serializedObject.FindProperty("m_TargetAudioSource"); + m_codec = serializedObject.FindProperty(AudioStreamReceiver.CodecPropertyName); + m_targetAudioSource = serializedObject.FindProperty(AudioStreamReceiver.TargetAudioSourcePropertyName); } void OnDisable() diff --git a/com.unity.renderstreaming/Editor/AudioStreamSenderEditor.cs b/com.unity.renderstreaming/Editor/AudioStreamSenderEditor.cs index 88941dd95..70848623c 100644 --- a/com.unity.renderstreaming/Editor/AudioStreamSenderEditor.cs +++ b/com.unity.renderstreaming/Editor/AudioStreamSenderEditor.cs @@ -33,14 +33,14 @@ class Styles void OnEnable() { - m_source = serializedObject.FindProperty("m_Source"); - m_audioSource = serializedObject.FindProperty("m_AudioSource"); - m_audioListener = serializedObject.FindProperty("m_AudioListener"); - m_microphoneDeviceIndex = serializedObject.FindProperty("m_MicrophoneDeviceIndex"); - m_autoRequestUserAuthorization = serializedObject.FindProperty("m_AutoRequestUserAuthorization"); - m_codec = serializedObject.FindProperty("m_Codec"); - m_bitrate = serializedObject.FindProperty("m_Bitrate"); - m_loopback = serializedObject.FindProperty("m_Loopback"); + m_source = serializedObject.FindProperty(AudioStreamSender.SourcePropertyName); + m_audioSource = serializedObject.FindProperty(AudioStreamSender.AudioSourcePropertyName); + m_audioListener = serializedObject.FindProperty(AudioStreamSender.AudioListenerPropertyName); + m_microphoneDeviceIndex = serializedObject.FindProperty(AudioStreamSender.MicrophoneDeviceIndexPropertyName); + m_autoRequestUserAuthorization = serializedObject.FindProperty(AudioStreamSender.AutoRequestUserAuthorizationPropertyName); + m_codec = serializedObject.FindProperty(AudioStreamSender.CodecPropertyName); + m_bitrate = serializedObject.FindProperty(AudioStreamSender.BitratePropertyName); + m_loopback = serializedObject.FindProperty(AudioStreamSender.LoopbackPropertyName); if (m_sourceFade == null) { diff --git a/com.unity.renderstreaming/Editor/InputSystem/InputReceiverEditor.cs b/com.unity.renderstreaming/Editor/InputSystem/InputReceiverEditor.cs index 7e2aeeeac..03c6ed129 100644 --- a/com.unity.renderstreaming/Editor/InputSystem/InputReceiverEditor.cs +++ b/com.unity.renderstreaming/Editor/InputSystem/InputReceiverEditor.cs @@ -16,9 +16,11 @@ public void OnEnable() { InputUser.onChange += OnUserChange; - m_ActionsProperty = serializedObject.FindProperty("m_Actions"); - m_ActionEventsProperty = serializedObject.FindProperty("m_ActionEvents"); - m_DefaultActionMapProperty = serializedObject.FindProperty("m_DefaultActionMap"); + m_Local = serializedObject.FindProperty(DataChannelBase.LocalPropertyName); + m_Label = serializedObject.FindProperty(DataChannelBase.LabelPropertyName); + m_Actions = serializedObject.FindProperty(InputReceiver.ActionsPropertyName); + m_ActionEvents = serializedObject.FindProperty(InputReceiver.ActionEventsPropertyName); + m_DefaultActionMap = serializedObject.FindProperty(InputReceiver.DefaultActionMapPropertyName); } private void OnUserChange(InputUser user, InputUserChange change, InputDevice device) { @@ -29,12 +31,12 @@ public override void OnInspectorGUI() { EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(serializedObject.FindProperty("local")); - EditorGUILayout.PropertyField(serializedObject.FindProperty("label")); + EditorGUILayout.PropertyField(m_Local); + EditorGUILayout.PropertyField(m_Label); // Action config section. EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(m_ActionsProperty); + EditorGUILayout.PropertyField(m_Actions); if (EditorGUI.EndChangeCheck() || !m_ActionAssetInitialized) OnActionAssetChange(); ++EditorGUI.indentLevel; @@ -48,15 +50,15 @@ public override void OnInspectorGUI() { if (selected == 0) { - m_DefaultActionMapProperty.stringValue = null; + m_DefaultActionMap.stringValue = null; } else { // Use ID rather than name. - var asset = (InputActionAsset)m_ActionsProperty.objectReferenceValue; + var asset = (InputActionAsset)m_Actions.objectReferenceValue; var actionMap = asset.FindActionMap(m_ActionMapOptions[selected].text); if (actionMap != null) - m_DefaultActionMapProperty.stringValue = actionMap.id.ToString(); + m_DefaultActionMap.stringValue = actionMap.id.ToString(); } m_SelectedDefaultActionMap = selected; } @@ -85,7 +87,7 @@ public override void OnInspectorGUI() if (m_ActionMapIndices[i] != n) continue; - EditorGUILayout.PropertyField(m_ActionEventsProperty.GetArrayElementAtIndex(i), m_ActionNames[i]); + EditorGUILayout.PropertyField(m_ActionEvents.GetArrayElementAtIndex(i), m_ActionNames[i]); } } } @@ -144,7 +146,7 @@ private void OnActionAssetChange() m_ActionAssetInitialized = true; var playerInput = (InputReceiver)target; - var asset = (InputActionAsset)m_ActionsProperty.objectReferenceValue; + var asset = (InputActionAsset)m_Actions.objectReferenceValue; if (asset == null) { m_ActionMapOptions = null; @@ -246,12 +248,14 @@ void AddEntry(InputAction action, PlayerInput.ActionEvent actionEvent) [NonSerialized] private int[] m_ActionMapIndices; [NonSerialized] private int m_NumActionMaps; - [NonSerialized] private SerializedProperty m_ActionEventsProperty; + [NonSerialized] private SerializedProperty m_ActionEvents; [NonSerialized] private int m_SelectedDefaultActionMap; [NonSerialized] private GUIContent[] m_ActionMapOptions; - [NonSerialized] private SerializedProperty m_ActionsProperty; - [NonSerialized] private SerializedProperty m_DefaultActionMapProperty; + [NonSerialized] private SerializedProperty m_Local; + [NonSerialized] private SerializedProperty m_Label; + [NonSerialized] private SerializedProperty m_Actions; + [NonSerialized] private SerializedProperty m_DefaultActionMap; [NonSerialized] private bool m_ActionAssetInitialized; } } diff --git a/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs b/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs index 11878fc45..f1a7e2d72 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingSettingsEditor.cs @@ -10,8 +10,8 @@ internal class RenderStreamingSettingsEditor : UnityEditor.Editor public override VisualElement CreateInspectorGUI() { var root = new VisualElement(); - root.Add(new PropertyField(serializedObject.FindProperty("automaticStreaming"), "Automatic Streaming")); - root.Add(new PropertyField(serializedObject.FindProperty("signalingSettings"), "Signaling Settings")); + root.Add(new PropertyField(serializedObject.FindProperty(RenderStreamingSettings.AutomaticStreamingPropertyName), "Automatic Streaming")); + root.Add(new PropertyField(serializedObject.FindProperty(RenderStreamingSettings.SignalingSettingsPropertyName), "Signaling Settings")); return root; } } diff --git a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs index 34e928cea..540a5894b 100644 --- a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs +++ b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs @@ -19,23 +19,40 @@ internal class SignalingManagerEditor : UnityEditor.Editor const string DefaultSignalingSettingsLoadPath = "Packages/com.unity.renderstreaming/Runtime/SignalingSettings.asset"; + SerializedProperty m_UseDefault; + SerializedProperty m_SignalingSettingsObject; + SerializedProperty m_SignalingSettings; + SerializedProperty m_Handlers; + SerializedProperty m_RunOnAwake; + SerializedProperty m_EvaluateCommandlineArguments; + VisualElement root; Button openProjectSettingsButton; PopupField signalingSettingsPopupField; PropertyField signalingSettingsField; + private void OnEnable() + { + m_UseDefault = serializedObject.FindProperty(SignalingManager.UseDefaultPropertyName); + m_SignalingSettingsObject = serializedObject.FindProperty(SignalingManager.SignalingSettingsObjectPropertyName); + m_SignalingSettings = serializedObject.FindProperty(SignalingManager.SignalingSettingsPropertyName); + m_Handlers = serializedObject.FindProperty(SignalingManager.HandlersPropertyName); + m_RunOnAwake = serializedObject.FindProperty(SignalingManager.RunOnAwakePropertyName); + m_EvaluateCommandlineArguments = serializedObject.FindProperty(SignalingManager.EvaluateCommandlineArgumentsPropertyName); + } + public override VisualElement CreateInspectorGUI() { root = new VisualElement(); - bool useDefault = serializedObject.FindProperty("m_useDefault").boolValue; + bool useDefault = m_UseDefault.boolValue; - var useDefaultField = new PropertyField(serializedObject.FindProperty("m_useDefault"), "Use Default Settings in Project Settings"); + var useDefaultField = new PropertyField(m_UseDefault, "Use Default Settings in Project Settings"); useDefaultField.RegisterValueChangeCallback(OnChangeUseDefault); openProjectSettingsButton = new Button { text = "Open Project Setings" }; openProjectSettingsButton.clicked += OnClickedOpenProjectSettingsButton; - signalingSettingsPopupField = CreatePopUpSignalingType(serializedObject.FindProperty("signalingSettingsObject"), "Signaling Settings Asset"); + signalingSettingsPopupField = CreatePopUpSignalingType(m_SignalingSettingsObject, "Signaling Settings Asset"); signalingSettingsPopupField.RegisterValueChangedCallback(OnValueChangeSignalingSettingsObject); - signalingSettingsField = new PropertyField(serializedObject.FindProperty("signalingSettings"), "Signaling Settings"); + signalingSettingsField = new PropertyField(m_SignalingSettings, "Signaling Settings"); signalingSettingsField.RegisterValueChangeCallback(OnValueChangeSignalingSettings); root.Add(useDefaultField); @@ -51,9 +68,9 @@ public override VisualElement CreateInspectorGUI() { openProjectSettingsButton.style.display = DisplayStyle.None; } - root.Add(new ReorderableListField(serializedObject.FindProperty("handlers"), "Signaling Handler List")); - root.Add(new PropertyField(serializedObject.FindProperty("runOnAwake"), "Run On Awake")); - root.Add(new PropertyField(serializedObject.FindProperty("evaluateCommandlineArguments"), "Evaluate Commandline Arguments")); + root.Add(new ReorderableListField(m_Handlers, "Signaling Handler List")); + root.Add(new PropertyField(m_RunOnAwake, "Run On Awake")); + root.Add(new PropertyField(m_EvaluateCommandlineArguments, "Evaluate Commandline Arguments")); EditorApplication.projectChanged += OnProjectChanged; @@ -134,12 +151,12 @@ private void OnProjectChanged() // Force to use default settings if there are no available settings in project folder. if (paths.Length == 0) { - serializedObject.FindProperty("m_useDefault").boolValue = true; + m_UseDefault.boolValue = true; serializedObject.ApplyModifiedProperties(); return; } - var asset = serializedObject.FindProperty("signalingSettingsObject").objectReferenceValue; + var asset = m_SignalingSettingsObject.objectReferenceValue; var availableObjects = paths.Select(path => AssetDatabase.LoadAssetAtPath(path)).ToArray(); var defaultIndex = ArrayHelpers.IndexOf(availableObjects, asset); if (defaultIndex < 0) @@ -174,8 +191,7 @@ private void OnChangeUseDefault(SerializedPropertyChangeEvent e) signalingSettingsField.style.display = DisplayStyle.Flex; openProjectSettingsButton.style.display = DisplayStyle.None; - var property = serializedObject.FindProperty("signalingSettingsObject"); - if (!IsValidSignalingSettingsObject(property.objectReferenceValue as SignalingSettingsObject)) + if (!IsValidSignalingSettingsObject(m_SignalingSettingsObject.objectReferenceValue as SignalingSettingsObject)) { CreateDefaultSignalingSettings(); } @@ -195,8 +211,7 @@ private void OnValueChangeSignalingSettingsObject(ChangeEvent /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs index da49bc16d..918fef410 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs @@ -40,6 +40,15 @@ public class AudioStreamSender : StreamSenderBase static readonly uint s_defaultMinBitrate = 0; static readonly uint s_defaultMaxBitrate = 200; + internal const string SourcePropertyName = nameof(m_Source); + internal const string AudioSourcePropertyName = nameof(m_AudioSource); + internal const string AudioListenerPropertyName = nameof(m_AudioListener); + internal const string MicrophoneDeviceIndexPropertyName = nameof(m_MicrophoneDeviceIndex); + internal const string AutoRequestUserAuthorizationPropertyName = nameof(m_AutoRequestUserAuthorization); + internal const string CodecPropertyName = nameof(m_Codec); + internal const string BitratePropertyName = nameof(m_Bitrate); + internal const string LoopbackPropertyName = nameof(m_Loopback); + [SerializeField] private AudioStreamSource m_Source; diff --git a/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs b/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs index 6d717777a..eb7953c96 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs @@ -8,6 +8,9 @@ namespace Unity.RenderStreaming /// public abstract class DataChannelBase : MonoBehaviour, IDataChannel { + internal const string LocalPropertyName = nameof(local); + internal const string LabelPropertyName = nameof(label); + /// /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs index fdf3b9821..ea48ee529 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs @@ -19,6 +19,10 @@ namespace Unity.RenderStreaming [AddComponentMenu("Render Streaming/Input Receiver")] public class InputReceiver : InputChannelReceiverBase { + internal const string ActionsPropertyName = nameof(m_Actions); + internal const string ActionEventsPropertyName = nameof(m_ActionEvents); + internal const string DefaultActionMapPropertyName = nameof(m_DefaultActionMap); + /// /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs index 2a7ff0911..f803e105e 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs @@ -7,6 +7,9 @@ namespace Unity.RenderStreaming /// public class RenderStreamingSettings : ScriptableObject { + internal const string AutomaticStreamingPropertyName = nameof(automaticStreaming); + internal const string SignalingSettingsPropertyName = nameof(signalingSettings); + /// /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs index 0e52b2b1f..ede76f05a 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs @@ -16,6 +16,13 @@ namespace Unity.RenderStreaming [AddComponentMenu("Render Streaming/Signaling Manager")] public sealed class SignalingManager : MonoBehaviour { + internal const string UseDefaultPropertyName = nameof(m_useDefault); + internal const string SignalingSettingsObjectPropertyName = nameof(signalingSettingsObject); + internal const string SignalingSettingsPropertyName = nameof(signalingSettings); + internal const string HandlersPropertyName = nameof(handlers); + internal const string RunOnAwakePropertyName = nameof(runOnAwake); + internal const string EvaluateCommandlineArgumentsPropertyName = nameof(evaluateCommandlineArguments); + #pragma warning disable 0649 [SerializeField] private bool m_useDefault = true; diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs index 532c4cb2f..033756cfe 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs @@ -28,6 +28,10 @@ public enum VideoRenderMode [AddComponentMenu("Render Streaming/Video Stream Receiver")] public class VideoStreamReceiver : StreamReceiverBase { + internal const string CodecPropertyName = nameof(m_Codec); + internal const string RenderModePropertyName = nameof(m_RenderMode); + internal const string TargetTexturePropertyName = nameof(m_TargetTexture); + /// /// /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs index 6e3a50c2a..b63117ea8 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs @@ -114,12 +114,23 @@ public enum VideoStreamSource public class VideoStreamSender : StreamSenderBase { static readonly float s_defaultFrameRate = 30; - static readonly uint s_defaultMinBitrate = 0; static readonly uint s_defaultMaxBitrate = 1000; - static readonly int s_defaultDepth = 16; + internal const string SourcePropertyName = nameof(m_Source); + internal const string CameraPropertyName = nameof(m_Camera); + internal const string TexturePropertyName = nameof(m_Texture); + internal const string WebCamDeviceIndexPropertyName = nameof(m_WebCamDeviceIndex); + internal const string CodecPropertyName = nameof(m_Codec); + internal const string TextureSizePropertyName = nameof(m_TextureSize); + internal const string FrameRatePropertyName = nameof(m_FrameRate); + internal const string BitratePropertyName = nameof(m_Bitrate); + internal const string ScaleFactorPropertyName = nameof(m_ScaleFactor); + internal const string DepthPropertyName = nameof(m_Depth); + internal const string AntiAliasingPropertyName = nameof(m_AntiAliasing); + internal const string AutoRequestUserAuthorizationPropertyName = nameof(m_AutoRequestUserAuthorization); + //todo(kazuki): remove this value. [SerializeField, StreamingSize] private Vector2Int m_TextureSize = new Vector2Int(1280, 720); From 008b81dc708c4654fa0f375d932768ba25483db2 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Mon, 24 Jul 2023 17:21:59 +0900 Subject: [PATCH 077/117] fix: Error on http signaling using short interval (#919) * add timestamp on getall response * use timestamp * remove unused code * fix newman test * update package lock * use same datetime and include sametime by fromtime * permit change interval on signaling class * fix test * fix --- WebApp/client/src/signaling.js | 120 +----------------- WebApp/client/test/mocksignaling.js | 7 +- WebApp/client/test/signaling.test.js | 26 ++-- WebApp/package-lock.json | 35 +++-- WebApp/src/class/httphandler.ts | 45 ++++--- WebApp/test/httphandler.test.ts | 33 +++-- .../renderstreaming.postman_collection.json | 1 + .../Scripts/Signaling/HttpSignaling.cs | 4 +- .../Scripts/Signaling/SignalingMessage.cs | 1 + 9 files changed, 96 insertions(+), 176 deletions(-) diff --git a/WebApp/client/src/signaling.js b/WebApp/client/src/signaling.js index 657d1b5a2..6b79d653f 100644 --- a/WebApp/client/src/signaling.js +++ b/WebApp/client/src/signaling.js @@ -2,9 +2,10 @@ import * as Logger from "./logger.js"; export class Signaling extends EventTarget { - constructor() { + constructor(interval = 1000) { super(); this.running = false; + this.interval = interval; this.sleep = msec => new Promise(resolve => setTimeout(resolve, msec)); } @@ -17,10 +18,6 @@ export class Signaling extends EventTarget { } } - get interval() { - return 1000; - } - url(method, parameter='') { let ret = location.origin + '/signaling'; if(method) @@ -53,9 +50,9 @@ export class Signaling extends EventTarget { let lastTimeRequest = Date.now() - 30000; while (this.running) { const res = await this.getAll(lastTimeRequest); - lastTimeRequest = Date.parse(res.headers.get('Date')); - const data = await res.json(); + lastTimeRequest = data.datetime ? data.datetime : Date.now(); + const messages = data.messages; for(const msg of messages) { @@ -82,92 +79,6 @@ export class Signaling extends EventTarget { } } - async loopGetConnection() { - let currentConnections = new Set(); - while (this.running) { - const res = await this.getConnection(); - const data = await res.json(); - const connections = data.connections; - Logger.log('get connections:', connections); - - const newSet = new Set(); - connections.forEach(e => newSet.add(e.connectionId)); - const deleteConnection = new Set([...currentConnections].filter(e => (!newSet.has(e)))); - - deleteConnection.forEach(connection => { - this.dispatchEvent(new CustomEvent('disconnect', { detail: { connectionId: connection } })); - currentConnections.delete(connection); - }); - - newSet.forEach(e => currentConnections.add(e)); - - await this.sleep(this.interval); - } - } - - async loopGetOffer() { - let lastTimeRequest = Date.now() - 30000; - - while (this.running) { - const res = await this.getOffer(lastTimeRequest); - lastTimeRequest = Date.parse(res.headers.get('Date')); - - const data = await res.json(); - const offers = data.offers; - Logger.log('get offers:', offers); - - offers.forEach(offer => { - this.dispatchEvent(new CustomEvent('offer', { detail: offer })); - }); - - await this.sleep(this.interval); - } - } - - async loopGetAnswer() { - // receive answer message from 30secs ago - let lastTimeRequest = Date.now() - 30000; - - while (this.running) { - const res = await this.getAnswer(lastTimeRequest); - lastTimeRequest = Date.parse(res.headers.get('Date')); - - const data = await res.json(); - const answers = data.answers; - Logger.log('get answers:', answers); - - answers.forEach(answer => { - this.dispatchEvent(new CustomEvent('answer', { detail: answer })); - }); - - await this.sleep(this.interval); - } - } - - async loopGetCandidate() { - // receive answer message from 30secs ago - let lastTimeRequest = Date.now() - 30000; - - while (this.running) { - const res = await this.getCandidate(lastTimeRequest); - lastTimeRequest = Date.parse(res.headers.get('Date')); - - const data = await res.json(); - const candidates = data.candidates; - Logger.log('get candidates:', candidates); - - if (candidates.length > 0) { - const connectionId = candidates[0].connectionId; - for (let candidate of candidates[0].candidates) { - const dispatch = { connectionId: connectionId, candidate: candidate.candidate, sdpMLineIndex: candidate.sdpMLineIndex, sdpMid: candidate.sdpMid }; - this.dispatchEvent(new CustomEvent('candidate', { detail: dispatch })); - } - } - - await this.sleep(this.interval); - } - } - async stop() { this.running = false; await fetch(this.url(''), { method: 'DELETE', headers: this.headers() }); @@ -215,22 +126,6 @@ export class Signaling extends EventTarget { await fetch(this.url('candidate'), { method: 'POST', headers: this.headers(), body: JSON.stringify(data) }); } - async getConnection() { - return await fetch(this.url(`connection`), { method: 'GET', headers: this.headers() }); - } - - async getOffer(fromTime = 0) { - return await fetch(this.url(`offer`, `fromtime=${fromTime}`), { method: 'GET', headers: this.headers() }); - } - - async getAnswer(fromTime = 0) { - return await fetch(this.url(`answer`, `fromtime=${fromTime}`), { method: 'GET', headers: this.headers() }); - } - - async getCandidate(fromTime = 0) { - return await fetch(this.url(`candidate`, `fromtime=${fromTime}`), { method: 'GET', headers: this.headers() }); - } - async getAll(fromTime = 0) { return await fetch(this.url(``, `fromtime=${fromTime}`), { method: 'GET', headers: this.headers() }); } @@ -238,8 +133,9 @@ export class Signaling extends EventTarget { export class WebSocketSignaling extends EventTarget { - constructor() { + constructor(interval = 1000) { super(); + this.interval = interval; this.sleep = msec => new Promise(resolve => setTimeout(resolve, msec)); let websocketUrl; @@ -290,10 +186,6 @@ export class WebSocketSignaling extends EventTarget { }; } - get interval() { - return 100; - } - async start() { while (!this.isWsOpen) { await this.sleep(100); diff --git a/WebApp/client/test/mocksignaling.js b/WebApp/client/test/mocksignaling.js index 05fd52206..e82d66c1b 100644 --- a/WebApp/client/test/mocksignaling.js +++ b/WebApp/client/test/mocksignaling.js @@ -9,8 +9,9 @@ export function reset(isPrivate) { export class MockSignaling extends EventTarget { - get interval() { - return 100; + constructor(interval = 1000) { + super(); + this.interval = interval; } async start() { @@ -115,7 +116,7 @@ class MockPrivateSignalingManager { constructor() { // structure Map> connectionIds this.connectionIds = new Map(); - this.delay = () => new Promise(resolve => setTimeout(resolve, 10)); + this.delay = async () => await sleep(10); } async add(signaling) { diff --git a/WebApp/client/test/signaling.test.js b/WebApp/client/test/signaling.test.js index 860f6a61f..5ed9e7303 100644 --- a/WebApp/client/test/signaling.test.js +++ b/WebApp/client/test/signaling.test.js @@ -23,8 +23,8 @@ describe.each([ beforeAll(async () => { if (mode == "mock") { reset(false); - signaling1 = new MockSignaling(); - signaling2 = new MockSignaling(); + signaling1 = new MockSignaling(1); + signaling2 = new MockSignaling(1); } else { const path = Path.resolve(`../bin~/${serverExeName()}`); let cmd = `${path} -p ${portNumber}`; @@ -35,13 +35,13 @@ describe.each([ await setup({ command: cmd, port: portNumber, usedPortAction: 'error' }); if (mode == "http") { - signaling1 = new Signaling(); - signaling2 = new Signaling(); + signaling1 = new Signaling(1); + signaling2 = new Signaling(1); } if (mode == "websocket") { - signaling1 = new WebSocketSignaling(); - signaling2 = new WebSocketSignaling(); + signaling1 = new WebSocketSignaling(1); + signaling2 = new WebSocketSignaling(1); } } @@ -221,8 +221,8 @@ describe.each([ beforeAll(async () => { if (mode == "mock") { reset(true); - signaling1 = new MockSignaling(); - signaling2 = new MockSignaling(); + signaling1 = new MockSignaling(1); + signaling2 = new MockSignaling(1); return; } @@ -235,13 +235,13 @@ describe.each([ await setup({ command: cmd, port: portNumber, usedPortAction: 'error' }); if (mode == "http") { - signaling1 = new Signaling(); - signaling2 = new Signaling(); + signaling1 = new Signaling(1); + signaling2 = new Signaling(1); } if (mode == "websocket") { - signaling1 = new WebSocketSignaling(); - signaling2 = new WebSocketSignaling(); + signaling1 = new WebSocketSignaling(1); + signaling2 = new WebSocketSignaling(1); } await signaling1.start(); @@ -455,6 +455,7 @@ describe.each([ signaling1.addEventListener('offer', (e) => offerRes1 = e.detail); signaling2.addEventListener('offer', (e) => offerRes2 = e.detail); await signaling1.sendOffer(connectionId, testsdp); + await waitFor(() => offerRes2 != null); await sleep(signaling1.interval * 2); expect(offerRes1).toBeUndefined(); expect(offerRes2).not.toBeUndefined(); @@ -464,6 +465,7 @@ describe.each([ signaling1.addEventListener('answer', (e) => answerRes1 = e.detail); signaling2.addEventListener('answer', (e) => answerRes2 = e.detail); await signaling2.sendAnswer(connectionId, testsdp); + await waitFor(() => answerRes1 != null); await sleep(signaling2.interval * 2); expect(answerRes1).not.toBeUndefined(); expect(answerRes1.connectionId).toBe(connectionId); diff --git a/WebApp/package-lock.json b/WebApp/package-lock.json index 7601b9a1b..943e9cb9d 100644 --- a/WebApp/package-lock.json +++ b/WebApp/package-lock.json @@ -11,6 +11,7 @@ "@types/express": "^4.17.13", "@types/node": "^18.7.15", "@types/ws": "^8.5.3", + "cors": "^2.8.5", "debug": "~4.3.4", "express": "~4.18.1", "morgan": "^1.10.0", @@ -2980,6 +2981,18 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -7055,7 +7068,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -10794,8 +10806,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "8.2.0", @@ -11384,6 +11395,15 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -13306,8 +13326,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} + "dev": true }, "jest-resolve": { "version": "29.0.2", @@ -14529,8 +14548,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-hash": { "version": "1.3.1", @@ -15982,8 +16000,7 @@ "ws": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", - "requires": {} + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==" }, "xmlbuilder": { "version": "15.1.1", diff --git a/WebApp/src/class/httphandler.ts b/WebApp/src/class/httphandler.ts index 71c1394c1..4741913d5 100644 --- a/WebApp/src/class/httphandler.ts +++ b/WebApp/src/class/httphandler.ts @@ -72,7 +72,7 @@ function checkSessionId(req: Request, res: Response, next): void { next(); } -function _deleteConnection(sessionId:string, connectionId:string) { +function _deleteConnection(sessionId:string, connectionId:string, datetime:number) { clients.get(sessionId).delete(connectionId); if(isPrivate) { @@ -83,7 +83,7 @@ function _deleteConnection(sessionId:string, connectionId:string) { if (clients.has(otherSessionId)) { clients.get(otherSessionId).delete(connectionId); const array1 = disconnections.get(otherSessionId); - array1.push(new Disconnection(connectionId, Date.now())); + array1.push(new Disconnection(connectionId, datetime)); } } } @@ -91,7 +91,7 @@ function _deleteConnection(sessionId:string, connectionId:string) { disconnections.forEach((array, id) => { if (id == sessionId) return; - array.push(new Disconnection(connectionId, Date.now())); + array.push(new Disconnection(connectionId, datetime)); }); } @@ -101,13 +101,13 @@ function _deleteConnection(sessionId:string, connectionId:string) { candidates.get(sessionId).delete(connectionId); const array2 = disconnections.get(sessionId); - array2.push(new Disconnection(connectionId, Date.now())); + array2.push(new Disconnection(connectionId, datetime)); } function _deleteSession(sessionId: string) { if(clients.has(sessionId)) { for(const connectionId of Array.from(clients.get(sessionId))) { - _deleteConnection(sessionId, connectionId); + _deleteConnection(sessionId, connectionId, Date.now()); } } offers.delete(sessionId); @@ -125,7 +125,7 @@ function _checkForTimedOutSessions(): void { if(lastRequestedTime.get(sessionId) > Date.now() - TimeoutRequestedTime) continue; _deleteSession(sessionId); - console.log("deleted"); + console.log(`deleted sessionId:${sessionId} by timeout.`); } } @@ -142,7 +142,7 @@ function _getDisconnection(sessionId: string, fromTime: number): Disconnection[] } if (fromTime > 0) { - arrayDisconnections = arrayDisconnections.filter((v) => v.datetime > fromTime); + arrayDisconnections = arrayDisconnections.filter((v) => v.datetime >= fromTime); } return arrayDisconnections; } @@ -162,7 +162,7 @@ function _getOffer(sessionId: string, fromTime: number): [string, Offer][] { } if (fromTime > 0) { - arrayOffers = arrayOffers.filter((v) => v[1].datetime > fromTime); + arrayOffers = arrayOffers.filter((v) => v[1].datetime >= fromTime); } return arrayOffers; } @@ -175,7 +175,7 @@ function _getAnswer(sessionId: string, fromTime: number): [string, Answer][] { } if (fromTime > 0) { - arrayAnswers = arrayAnswers.filter((v) => v[1].datetime > fromTime); + arrayAnswers = arrayAnswers.filter((v) => v[1].datetime >= fromTime); } return arrayAnswers; } @@ -193,7 +193,7 @@ function _getCandidate(sessionId: string, fromTime: number): [string, Candidate] continue; } const arrayCandidates = candidates.get(otherSessionId).get(connectionId) - .filter((v) => v.datetime > fromTime); + .filter((v) => v.datetime >= fromTime); if (arrayCandidates.length === 0) { continue; } @@ -243,17 +243,18 @@ function getAll(req: Request, res: Response): void { const answers: [string, Answer][] = _getAnswer(sessionId, fromTime); const candidates: [string, Candidate][] = _getCandidate(sessionId, fromTime); const disconnections: Disconnection[] = _getDisconnection(sessionId, fromTime); + const datetime = lastRequestedTime.get(sessionId); let array: any[] = []; - array = array.concat(connections.map((v) => ({ connectionId: v, type: "connect", datetime: Date.now() }))); + array = array.concat(connections.map((v) => ({ connectionId: v, type: "connect", datetime: datetime }))); array = array.concat(offers.map((v) => ({ connectionId: v[0], sdp: v[1].sdp, polite: v[1].polite, type: "offer", datetime: v[1].datetime }))); array = array.concat(answers.map((v) => ({ connectionId: v[0], sdp: v[1].sdp, type: "answer", datetime: v[1].datetime }))); array = array.concat(candidates.map((v) => ({ connectionId: v[0], candidate: v[1].candidate, sdpMLineIndex: v[1].sdpMLineIndex, sdpMid: v[1].sdpMid, type: "candidate", datetime: v[1].datetime }))); array = array.concat(disconnections.map((v) => ({ connectionId: v.id, type: "disconnect", datetime: v.datetime }))); array.sort((a, b) => a.datetime - b.datetime); - res.json({ messages: array }); + res.json({ messages: array, datetime: datetime }); } function createSession(sessionId: string, res: Response): void; @@ -278,6 +279,8 @@ function deleteSession(req: Request, res: Response): void { function createConnection(req: Request, res: Response): void { const sessionId: string = req.header('session-id'); const { connectionId } = req.body; + const datetime = lastRequestedTime.get(sessionId); + if (connectionId == null) { res.status(400).send({ error: new Error(`connectionId is required`) }); return; @@ -305,14 +308,15 @@ function createConnection(req: Request, res: Response): void { const connectionIds = getOrCreateConnectionIds(sessionId); connectionIds.add(connectionId); - res.json({ connectionId: connectionId, polite: polite, type: "connect", datetime: Date.now() }); + res.json({ connectionId: connectionId, polite: polite, type: "connect", datetime: datetime }); } function deleteConnection(req: Request, res: Response): void { const sessionId: string = req.header('session-id'); const { connectionId } = req.body; + const datetime = lastRequestedTime.get(sessionId); - _deleteConnection(sessionId, connectionId); + _deleteConnection(sessionId, connectionId, datetime); res.json({ connectionId: connectionId }); } @@ -320,6 +324,7 @@ function deleteConnection(req: Request, res: Response): void { function postOffer(req: Request, res: Response): void { const sessionId: string = req.header('session-id'); const { connectionId } = req.body; + const datetime = lastRequestedTime.get(sessionId); let keySessionId = null; let polite = false; @@ -330,7 +335,7 @@ function postOffer(req: Request, res: Response): void { if (keySessionId != null) { polite = true; const map = offers.get(keySessionId); - map.set(connectionId, new Offer(req.body.sdp, Date.now(), polite)); + map.set(connectionId, new Offer(req.body.sdp, datetime, polite)); } } res.sendStatus(200); @@ -344,7 +349,7 @@ function postOffer(req: Request, res: Response): void { keySessionId = sessionId; const map = offers.get(keySessionId); - map.set(connectionId, new Offer(req.body.sdp, Date.now(), polite)); + map.set(connectionId, new Offer(req.body.sdp, datetime, polite)); res.sendStatus(200); } @@ -352,6 +357,7 @@ function postOffer(req: Request, res: Response): void { function postAnswer(req: Request, res: Response): void { const sessionId: string = req.header('session-id'); const { connectionId } = req.body; + const datetime = lastRequestedTime.get(sessionId); const connectionIds = getOrCreateConnectionIds(sessionId); connectionIds.add(connectionId); @@ -374,7 +380,7 @@ function postAnswer(req: Request, res: Response): void { } const map = answers.get(otherSessionId); - map.set(connectionId, new Answer(req.body.sdp, Date.now())); + map.set(connectionId, new Answer(req.body.sdp, datetime)); // update datetime for candidates const mapCandidates = candidates.get(otherSessionId); @@ -382,7 +388,7 @@ function postAnswer(req: Request, res: Response): void { const arrayCandidates = mapCandidates.get(connectionId); if (arrayCandidates) { for (const candidate of arrayCandidates) { - candidate.datetime = Date.now(); + candidate.datetime = datetime; } } } @@ -392,13 +398,14 @@ function postAnswer(req: Request, res: Response): void { function postCandidate(req: Request, res: Response): void { const sessionId: string = req.header('session-id'); const { connectionId } = req.body; + const datetime = lastRequestedTime.get(sessionId); const map = candidates.get(sessionId); if (!map.has(connectionId)) { map.set(connectionId, []); } const arr = map.get(connectionId); - const candidate = new Candidate(req.body.candidate, req.body.sdpMLineIndex, req.body.sdpMid, Date.now()); + const candidate = new Candidate(req.body.candidate, req.body.sdpMLineIndex, req.body.sdpMid, datetime); arr.push(candidate); res.sendStatus(200); } diff --git a/WebApp/test/httphandler.test.ts b/WebApp/test/httphandler.test.ts index 8e87ee0db..615eb31e4 100644 --- a/WebApp/test/httphandler.test.ts +++ b/WebApp/test/httphandler.test.ts @@ -9,7 +9,6 @@ describe('http signaling test in public mode', () => { const sessionId3 = "abcd9101112"; const connectionId = "12345"; const connectionId2 = "67890"; - const connectionId3 = "9101112"; const testsdp = "test sdp"; const { res, next, mockClear } = getMockRes(); @@ -66,7 +65,7 @@ describe('http signaling test in public mode', () => { test('get all from session1', async () => { await httpHandler.getAll(req, res); const connect = { connectionId: connectionId, datetime: expect.anything(), type: "connect" }; - expect(res.json).toHaveBeenCalledWith({ messages: expect.arrayContaining([connect]) }); + expect(res.json).toHaveBeenCalledWith({ messages: expect.arrayContaining([connect]), datetime: expect.anything() }); }); test('post offer from session1', async () => { @@ -130,7 +129,7 @@ describe('http signaling test in public mode', () => { test('disconnection get from session1', async () => { await httpHandler.getAll(req, res); const disconnect = { connectionId: connectionId, datetime: expect.anything(), type: "disconnect" }; - expect(res.json).toHaveBeenCalledWith({ messages: expect.arrayContaining([disconnect]) }); + expect(res.json).toHaveBeenCalledWith({ messages: expect.arrayContaining([disconnect]), datetime: expect.anything() }); }); test('delete connection from session1', async () => { @@ -159,7 +158,7 @@ describe('http signaling test in public mode', () => { await httpHandler.createSession(sessionId2, res); await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: [] }); + expect(res.json).toHaveBeenLastCalledWith({ messages: [], datetime: expect.anything() }); const connectBody = { connectionId: connectionId }; req.body = connectBody; @@ -171,9 +170,9 @@ describe('http signaling test in public mode', () => { const offer = { connectionId: connectionId, sdp: testsdp, datetime: expect.anything(), type: "offer", polite: false }; await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]), datetime: expect.anything() }); await httpHandler.getAll(req2, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]), datetime: expect.anything() }); const deleteBody = { connectionId: connectionId }; req2.body = deleteBody; @@ -187,7 +186,7 @@ describe('http signaling test in public mode', () => { const disconnect = { connectionId: connectionId, type: "disconnect", datetime: expect.anything() }; await httpHandler.getAll(req2, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([disconnect]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([disconnect]), datetime: expect.anything() }); await httpHandler.deleteSession(req2, res); }); @@ -204,7 +203,7 @@ describe('http signaling test in public mode', () => { await httpHandler.checkSessionId(req2, res, next); await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: [] }); + expect(res.json).toHaveBeenLastCalledWith({ messages: [], datetime: expect.anything() }); const connectBody = { connectionId: connectionId }; req.body = connectBody; @@ -216,9 +215,9 @@ describe('http signaling test in public mode', () => { const offer = { connectionId: connectionId, sdp: testsdp, datetime: expect.anything(), type: "offer", polite: false }; await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]), datetime: expect.anything() }); await httpHandler.getAll(req2, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]), datetime: expect.anything() }); const answerBody = { connectionId: connectionId, sdp: testsdp }; req2.body = answerBody; @@ -264,7 +263,7 @@ test('Timed out sessions are deleted when other sessions check', async () => { await httpHandler.checkSessionId(req3, res, next); await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: [] }); + expect(res.json).toHaveBeenLastCalledWith({ messages: [], datetime: expect.anything() }); const connectBody = { connectionId: connectionId }; req.body = connectBody; @@ -276,9 +275,9 @@ test('Timed out sessions are deleted when other sessions check', async () => { const offer = { connectionId: connectionId, sdp: testsdp, datetime: expect.anything(), type: "offer", polite: false }; await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]), datetime: expect.anything() }); await httpHandler.getAll(req2, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]), datetime: expect.anything() }); const answerBody = { connectionId: connectionId, sdp: testsdp }; req2.body = answerBody; @@ -473,7 +472,7 @@ describe('http signaling test in private mode', () => { await httpHandler.createSession(sessionId2, res); await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: [] }); + expect(res.json).toHaveBeenLastCalledWith({ messages: [], datetime: expect.anything() }); const connectBody = { connectionId: connectionId }; req.body = connectBody; @@ -487,9 +486,9 @@ describe('http signaling test in private mode', () => { const offer = { connectionId: connectionId, sdp: testsdp, datetime: expect.anything(), type: "offer", polite: true }; await httpHandler.getAll(req, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.not.arrayContaining([offer]), datetime: expect.anything() }); await httpHandler.getAll(req2, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([offer]), datetime: expect.anything() }); const deleteBody = { connectionId: connectionId }; req2.body = deleteBody; @@ -503,7 +502,7 @@ describe('http signaling test in private mode', () => { const disconnect = { connectionId: connectionId, type: "disconnect", datetime: expect.anything() }; await httpHandler.getAll(req2, res); - expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([disconnect]) }); + expect(res.json).toHaveBeenLastCalledWith({ messages: expect.arrayContaining([disconnect]), datetime: expect.anything() }); await httpHandler.deleteSession(req2, res); }); diff --git a/WebApp/test/renderstreaming.postman_collection.json b/WebApp/test/renderstreaming.postman_collection.json index 158e2c9c5..96287cbad 100644 --- a/WebApp/test/renderstreaming.postman_collection.json +++ b/WebApp/test/renderstreaming.postman_collection.json @@ -860,6 +860,7 @@ "pm.test(\"The response has a valid JSON body\", function () {", " pm.response.to.be.json;", " pm.response.to.have.jsonBody(\"messages\");", + " pm.response.to.have.jsonBody(\"datetime\");", " var jsonData = pm.response.json();", " console.log(jsonData);", " pm.expect(jsonData.messages.length).to.be.above(0);", diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs index e2d96a8bd..0f4ac5c46 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignaling.cs @@ -368,8 +368,8 @@ private bool HTTPGetAll() if (data == null) return false; - m_lastTimeGetAllRequest = DateTimeExtension.ParseHttpDate(response.Headers[HttpResponseHeader.Date]) - .ToJsMilliseconds(); + m_lastTimeGetAllRequest = + long.TryParse(data.datetime, out var result) ? result : DateTime.Now.ToJsMilliseconds(); foreach (var msg in data.messages) { diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingMessage.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingMessage.cs index baf7bab01..975a146ab 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingMessage.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingMessage.cs @@ -100,6 +100,7 @@ class CandidateContainerResData class AllResData { public SignalingMessage[] messages; + public string datetime; } #pragma warning restore 0649 From cef8d001ad12fe42334017229b6ea5ffc833679b Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 25 Jul 2023 09:24:17 +0900 Subject: [PATCH 078/117] fix: Fix thte issue that ICE server configuration are not changed (#923) * Fix script error * Add ICE settings Web UI * fix default settings * fix * format --- WebApp/client/public/css/main.css | 31 ++++++ WebApp/client/public/index.html | 26 ++++- WebApp/client/public/js/config.js | 6 +- WebApp/client/public/js/icesettings.js | 103 ++++++++++++++++++ WebApp/client/public/js/main.js | 11 +- .../Scripts/Signaling/SignalingSettings.cs | 2 +- .../Runtime/Scripts/SignalingManager.cs | 8 +- .../Samples~/Example/Scripts/SceneSelectUI.cs | 2 +- 8 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 WebApp/client/public/js/icesettings.js diff --git a/WebApp/client/public/css/main.css b/WebApp/client/public/css/main.css index 9aa2078df..28f66a2ed 100644 --- a/WebApp/client/public/css/main.css +++ b/WebApp/client/public/css/main.css @@ -18,12 +18,43 @@ body { word-break: break-word; } +button { + margin: 20px 10px 0 0; + width: 130px; +} + +button#gather { + display: block; +} + section { border-bottom: 1px solid #eee; margin: 0 0 1.5em 0; padding: 0 0 1.5em 0; } +section#iceServers label { + display: inline-block; + width: 150px; +} + +section#iceServers input { + margin: 0 0 10px; + width: 260px; +} + +select { + margin: 0 1em 1em 0; + position: relative; + top: -1px; +} + +select#servers { + font-size: 1em; + padding: 5px; + width: 420px; +} + section:last-child { border-bottom: none; margin: 0; diff --git a/WebApp/client/public/index.html b/WebApp/client/public/index.html index 9c53fa29a..b71b437b6 100644 --- a/WebApp/client/public/index.html +++ b/WebApp/client/public/index.html @@ -15,9 +15,33 @@

Unity Render Streaming Samples

+

Server Configuration

+
+

ICE servers

+ +
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+

Receiver Sample

This is a sample for receiving video / audio from Unity.

@@ -50,4 +74,4 @@

VideoPlayer Sample

- \ No newline at end of file + diff --git a/WebApp/client/public/js/config.js b/WebApp/client/public/js/config.js index d532bdc2f..6336048dc 100644 --- a/WebApp/client/public/js/config.js +++ b/WebApp/client/public/js/config.js @@ -1,3 +1,5 @@ +import {getServers} from "./icesettings.js"; + export async function getServerConfig() { const protocolEndPoint = location.origin + '/config'; const createResponse = await fetch(protocolEndPoint); @@ -7,6 +9,6 @@ export async function getServerConfig() { export function getRTCConfiguration() { let config = {}; config.sdpSemantics = 'unified-plan'; - config.iceServers = [{ urls: ['stun:stun.l.google.com:19302'] }]; + config.iceServers = getServers(); return config; -} \ No newline at end of file +} diff --git a/WebApp/client/public/js/icesettings.js b/WebApp/client/public/js/icesettings.js new file mode 100644 index 000000000..22b986f78 --- /dev/null +++ b/WebApp/client/public/js/icesettings.js @@ -0,0 +1,103 @@ +// This code is referenced from webrtc sample. +// https://github.com/webrtc/samples/blob/gh-pages/src/content/peerconnection/trickle-ice/js/main.js + +const servers = document.querySelector('select#servers'); +const urlInput = document.querySelector('input#url'); +const usernameInput = document.querySelector('input#username'); +const passwordInput = document.querySelector('input#password'); + +const allServersKey = 'servers'; + +export function addServer() { + const scheme = urlInput.value.split(':')[0]; + if (!['stun', 'stuns', 'turn', 'turns'].includes(scheme)) { + alert(`URI scheme ${scheme} is not valid`); + return; + } + + // Store the ICE server as a stringified JSON object in option.value. + const option = document.createElement('option'); + const iceServer = { + urls: [urlInput.value], + username: usernameInput.value, + credential: passwordInput.value + }; + option.value = JSON.stringify(iceServer); + option.text = `${urlInput.value} `; + const username = usernameInput.value; + const password = passwordInput.value; + if (username || password) { + option.text += (` [${username}:${password}]`); + } + option.ondblclick = selectServer; + servers.add(option); + urlInput.value = usernameInput.value = passwordInput.value = ''; + writeServersToLocalStorage(); +} + +export function removeServer() { + for (let i = servers.options.length - 1; i >= 0; --i) { + if (servers.options[i].selected) { + servers.remove(i); + } + } + writeServersToLocalStorage(); +} + +export function reset() { + window.localStorage.clear(); + document.querySelectorAll('select#servers option').forEach(option => option.remove()); + const serversSelect = document.querySelector('select#servers'); + setDefaultServer(serversSelect); +} + +function selectServer(event) { + const option = event.target; + const value = JSON.parse(option.value); + urlInput.value = value.urls[0]; + usernameInput.value = value.username || ''; + passwordInput.value = value.credential || ''; +} + +function setDefaultServer(serversSelect) { + const option = document.createElement('option'); + option.value = '{"urls":["stun:stun.l.google.com:19302"]}'; + option.text = 'stun:stun.l.google.com:19302'; + option.ondblclick = selectServer; + serversSelect.add(option); +} + +function writeServersToLocalStorage() { + const serversSelect = document.querySelector('select#servers'); + const allServers = JSON.stringify(Object.values(serversSelect.options).map(o => JSON.parse(o.value))); + window.localStorage.setItem(allServersKey, allServers); +} + +export function readServersFromLocalStorage() { + document.querySelectorAll('select#servers option').forEach(option => option.remove()); + const serversSelect = document.querySelector('select#servers'); + const storedServers = window.localStorage.getItem(allServersKey); + + if (storedServers === null || storedServers === '') { + setDefaultServer(serversSelect); + } else { + JSON.parse(storedServers).forEach((server) => { + const o = document.createElement('option'); + o.value = JSON.stringify(server); + o.text = server.urls[0]; + o.ondblclick = selectServer; + serversSelect.add(o); + }); + } +} + +export function getServers() { + const storedServers = window.localStorage.getItem(allServersKey); + + if (storedServers === null || storedServers === '') { + return [{ urls: ['stun:stun.l.google.com:19302'] }]; + } + else { + return JSON.parse(storedServers); + } +} diff --git a/WebApp/client/public/js/main.js b/WebApp/client/public/js/main.js index 813b8a734..cdf5825b8 100644 --- a/WebApp/client/public/js/main.js +++ b/WebApp/client/public/js/main.js @@ -1,7 +1,15 @@ import * as Config from "./config.js"; +import {addServer, removeServer, reset, readServersFromLocalStorage} from "./icesettings.js"; +const addButton = document.querySelector('button#add'); +const removeButton = document.querySelector('button#remove'); +const resetButton = document.querySelector('button#reset'); const startupDiv = document.getElementById("startup"); -startupDiv.innerHTML = "

Server Configuration

"; + +addButton.onclick = addServer; +removeButton.onclick = removeServer; +resetButton.onclick = reset; +startupDiv.innerHTML = ""; const displayConfig = async () => { const res = await Config.getServerConfig(); @@ -16,3 +24,4 @@ const displayConfig = async () => { }; displayConfig(); +readServersFromLocalStorage(); diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs index 40a5304fb..b2a36f145 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/SignalingSettings.cs @@ -85,7 +85,7 @@ public class IceServer /// ///
/// - public static implicit operator RTCIceServer(IceServer server) + public static explicit operator RTCIceServer(IceServer server) { var iceServer = new RTCIceServer { diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs index ede76f05a..875393fa2 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs @@ -198,7 +198,13 @@ private void _Run( } } #endif - RTCIceServer[] iceServers = settings.iceServers.OfType().ToArray(); + int i = 0; + RTCIceServer[] iceServers = new RTCIceServer[settings.iceServers.Count()]; + foreach (var iceServer in settings.iceServers) + { + iceServers[i] = (RTCIceServer)iceServer; + i++; + } RTCConfiguration _conf = conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers }); diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs index f1fec0a6c..7aace2fa3 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs @@ -26,7 +26,7 @@ internal class RenderStreamingSettings public const int DefaultStreamWidth = 1280; public const int DefaultStreamHeight = 720; - private bool useDefaultSettings = false; + private bool useDefaultSettings = true; private SignalingType signalingType = SignalingType.WebSocket; private string signalingAddress = "localhost"; private int signalingInterval = 5000; From 80892ce7d0c729b782d595a43cbad56de7181834 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 25 Jul 2023 14:03:09 +0900 Subject: [PATCH 079/117] fix: Remove Furioos integration (#926) --- .../Documentation~/index.md | 6 - .../Documentation~/samples.md | 2 +- .../Documentation~/signaling-type.md | 5 - .../SignalingSettingsDrawer.cs | 12 - .../Scripts/Signaling/FurioosSignaling.cs | 291 ------------------ .../Signaling/FurioosSignaling.cs.meta | 3 - .../Signaling/FurioosSignalingSettings.cs | 73 ----- .../FurioosSignalingSettings.cs.meta | 3 - .../Samples~/Example/Menu/Menu.unity | 2 - .../Samples~/Example/Scripts/SceneSelectUI.cs | 9 - .../Tests/Runtime/SignalingSettingsTest.cs | 16 - 11 files changed, 1 insertion(+), 421 deletions(-) delete mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs delete mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs.meta delete mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs delete mode 100644 com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta diff --git a/com.unity.renderstreaming/Documentation~/index.md b/com.unity.renderstreaming/Documentation~/index.md index d0f86e92e..95a6358f7 100644 --- a/com.unity.renderstreaming/Documentation~/index.md +++ b/com.unity.renderstreaming/Documentation~/index.md @@ -64,9 +64,3 @@ Unity Render Streaming supports almost all browsers that can use WebRTC. ## Samples Please check [this page](samples.md). - -## Furioos compatibility - -Unity provides **[Furioos](https://www.furioos.com)** which is a web service to stream any 3D contents on any devices in real-time. - -This version of Unity Render Streaming doesn't support Furioos integration. diff --git a/com.unity.renderstreaming/Documentation~/samples.md b/com.unity.renderstreaming/Documentation~/samples.md index ef05def1f..944225e22 100644 --- a/com.unity.renderstreaming/Documentation~/samples.md +++ b/com.unity.renderstreaming/Documentation~/samples.md @@ -40,7 +40,7 @@ You can change signaling settings below during runtime. | Parameter | Description | | --- | --- | | **Use Default Settings** | Refer to settings of [Project Settings](settings.md). | -| [**Signaling Type**](signaling-type.md) | *Http*, *WebSocket* or *Furioos*. | +| [**Signaling Type**](signaling-type.md) | *Http* or *WebSocket*. | | **Enable/Disable SSL** | Use **https** if set enable. | | **Host Address** | Set IP address or URL of your signaling server. | | **Interval (msec)** | Polling interval for communication of signaling.
This parameter effects Http signaling. | diff --git a/com.unity.renderstreaming/Documentation~/signaling-type.md b/com.unity.renderstreaming/Documentation~/signaling-type.md index 3da603807..90ebf7e9d 100644 --- a/com.unity.renderstreaming/Documentation~/signaling-type.md +++ b/com.unity.renderstreaming/Documentation~/signaling-type.md @@ -4,7 +4,6 @@ - `Http Signaling` - `WebSocket Signaling` -- `Furioos Signaling` In the example, the schema given to `URL Signaling` is used to determine which type to use. @@ -32,7 +31,3 @@ When the signaling server receives the Offer or Candidate, the server distribute > [!WARNING] > WebSocket does not work in iOS Safari on servers that use self-signed certificates. > If you want to verify the behavior of WebSocket signaling in iOS Safari, use a certificate issued by a trusted certification authority. Or try signaling with HTTP. - -## `Furioos Signaling` - -Please see [this page](deploy-to-furioos.md). diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs index 465ae9cf4..08074dfae 100644 --- a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs @@ -137,16 +137,4 @@ public VisualElement CreateInspectorGUI(SerializedProperty property) return root; } } - - [CustomSignalingSettingsEditor(typeof(FurioosSignalingSettings), "Furioos")] - internal class FurioosSignalingSettingsEditor : ISignalingSettingEditor - { - public VisualElement CreateInspectorGUI(SerializedProperty property) - { - VisualElement root = new VisualElement(); - root.Add(new PropertyField(property.FindPropertyRelative("m_url"), "URL")); - root.Add(new PropertyField(property.FindPropertyRelative("m_iceServers"), "ICE Servers")); - return root; - } - } } diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs deleted file mode 100644 index ed8d42654..000000000 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs +++ /dev/null @@ -1,291 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using Unity.WebRTC; -using UnityEngine; -using WebSocketSharp; - -namespace Unity.RenderStreaming.Signaling -{ - [Serializable] - public class FurioosRoutedMessage - { - public string from; - public string to; - public T message; - } - - [Flags] - enum SslProtocolsHack - { - Tls = 192, - Tls11 = 768, - Tls12 = 3072 - } - - public class FurioosSignaling : ISignaling - { - private float m_timeout; - private bool m_running; - private SynchronizationContext m_mainThreadContext; - private Thread m_signalingThread; - private AutoResetEvent m_wsCloseEvent; - private WebSocket m_webSocket; - - public delegate void OnSignedInHandler(ISignaling sender); - - public string Url { get { return string.Empty; } } - - public FurioosSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext) - { - m_timeout = 5.0f; - m_mainThreadContext = mainThreadContext; - m_wsCloseEvent = new AutoResetEvent(false); - } - - public string connectionId - { - get - { - throw new NotImplementedException(); - } - } - - public void Start() - { - m_running = true; - m_signalingThread = new Thread(WSManage); - m_signalingThread.Start(); - } - - public void Stop() - { - if (m_running) - { - m_running = false; - m_webSocket.Close(); - - if (m_signalingThread.ThreadState == ThreadState.WaitSleepJoin) - { - m_signalingThread.Abort(); - } - else - { - m_signalingThread.Join(1000); - } - m_signalingThread = null; - } - } - - //todo(kazuki):: remove warning CS0067 -#pragma warning disable 0067 - public event OnStartHandler OnStart; - public event OnSignedInHandler OnSignedIn; - public event OnConnectHandler OnCreateConnection; - public event OnDisconnectHandler OnDestroyConnection; - public event OnOfferHandler OnOffer; - public event OnAnswerHandler OnAnswer; - public event OnIceCandidateHandler OnIceCandidate; -#pragma warning restore 0067 - public void SendOffer(string connectionId, RTCSessionDescription offer) - { - throw new NotImplementedException(); - } - - public void SendAnswer(string connectionId, RTCSessionDescription answer) - { - DescData data = new DescData(); - data.connectionId = connectionId; - data.sdp = answer.sdp; - data.type = "answer"; - - FurioosRoutedMessage routedMessage = new FurioosRoutedMessage(); - routedMessage.to = connectionId; - routedMessage.message = data; - - WSSend(routedMessage); - } - - public void SendCandidate(string connectionId, RTCIceCandidate candidate) - { - CandidateData data = new CandidateData(); - data.connectionId = connectionId; - data.candidate = candidate.Candidate; - data.sdpMLineIndex = candidate.SdpMLineIndex.GetValueOrDefault(0); - data.sdpMid = candidate.SdpMid; - - FurioosRoutedMessage routedMessage = new FurioosRoutedMessage(); - routedMessage.to = connectionId; - routedMessage.message = data; - - WSSend(routedMessage); - } - - public void OpenConnection(string connectionId) - { - this.WSSend("{\"type\":\"connect\"}"); - } - - public void CloseConnection(string connectionId) - { - throw new NotImplementedException(); - } - - private void WSManage() - { - while (m_running) - { - WSCreate(); - - m_wsCloseEvent.WaitOne(); - - Thread.Sleep((int)(m_timeout * 1000)); - } - - RenderStreaming.Logger.Log("Signaling: WS managing thread ended"); - } - - private void WSCreate() - { - m_webSocket = new WebSocket("ws://127.0.0.1:8081"); - m_webSocket.OnOpen += WSConnected; - m_webSocket.OnMessage += WSProcessMessage; - m_webSocket.OnError += WSError; - m_webSocket.OnClose += WSClosed; - - Monitor.Enter(m_webSocket); - - RenderStreaming.Logger.Log($"Signaling: Connecting to Furioos Server"); - m_webSocket.ConnectAsync(); - } - - private void WSProcessMessage(object sender, MessageEventArgs e) - { - var content = Encoding.UTF8.GetString(e.RawData); - RenderStreaming.Logger.Log($"Signaling: Receiving message: {content}"); - - try - { - var routedMessage = JsonUtility.FromJson>(content); - - SignalingMessage msg; - if (!string.IsNullOrEmpty(routedMessage.from)) - { - msg = routedMessage.message; - } - else - { - msg = JsonUtility.FromJson(content); - } - - if (!string.IsNullOrEmpty(msg.type)) - { - if (msg.type == "signIn") - { - if (msg.status == "SUCCESS") - { - RenderStreaming.Logger.Log("Signaling: Slot signed in."); - this.WSSend("{\"type\":\"furioos\",\"task\":\"enableStreaming\",\"streamType\":\"RenderStreaming\",\"streamProtocols\":[\"WebRTC\"],\"controlsTypes\":[\"RenderStreaming\"]}"); - - OnSignedIn?.Invoke(this); - } - else - { - RenderStreaming.Logger.Log(LogType.Error, "Signaling: Sign-in error : " + msg.message); - } - } - else if (msg.type == "reconnect") - { - if (msg.status == "SUCCESS") - { - RenderStreaming.Logger.Log("Signaling: Slot reconnected."); - } - else - { - RenderStreaming.Logger.Log(LogType.Error, "Signaling: Reconnect error : " + msg.message); - } - } - - if (msg.type == "offer") - { - if (!string.IsNullOrEmpty(routedMessage.from)) - { - DescData offer = new DescData(); - offer.connectionId = routedMessage.from; - offer.sdp = msg.sdp; - offer.polite = false; - - m_mainThreadContext.Post(d => OnOffer?.Invoke(this, offer), null); - } - else - { - RenderStreaming.Logger.Log(LogType.Error, "Signaling: Received message from unknown peer"); - } - } - } - else if (!string.IsNullOrEmpty(msg.candidate)) - { - if (!string.IsNullOrEmpty(routedMessage.from)) - { - CandidateData candidate = new CandidateData(); - candidate.connectionId = routedMessage.from; - candidate.candidate = msg.candidate; - candidate.sdpMLineIndex = msg.sdpMLineIndex; - candidate.sdpMid = msg.sdpMid; - - m_mainThreadContext.Post(d => OnIceCandidate?.Invoke(this, candidate), null); - } - else - { - RenderStreaming.Logger.Log(LogType.Error, "Signaling: Received message from unknown peer"); - } - } - } - catch (Exception ex) - { - RenderStreaming.Logger.Log(LogType.Error, "Signaling: Failed to parse message: " + ex); - } - } - - private void WSConnected(object sender, EventArgs e) - { - RenderStreaming.Logger.Log("Signaling: WS connected."); - this.WSSend("{\"type\" :\"signIn\",\"peerName\" :\"Unity Test App\"}"); - } - - - private void WSError(object sender, ErrorEventArgs e) - { - RenderStreaming.Logger.Log(LogType.Error, $"Signaling: WS connection error: {e.Message}"); - } - - private void WSClosed(object sender, CloseEventArgs e) - { - RenderStreaming.Logger.Log($"Signaling: WS connection closed, code: {e.Code}"); - - m_wsCloseEvent.Set(); - m_webSocket = null; - } - - private void WSSend(object data) - { - if (m_webSocket == null || m_webSocket.ReadyState != WebSocketState.Open) - { - RenderStreaming.Logger.Log(LogType.Error, "Signaling: WS is not connected. Unable to send message"); - return; - } - - if (data is string s) - { - RenderStreaming.Logger.Log("Signaling: Sending WS data: " + s); - m_webSocket.Send(s); - } - else - { - string str = JsonUtility.ToJson(data); - RenderStreaming.Logger.Log("Signaling: Sending WS data: " + str); - m_webSocket.Send(str); - } - } - } -} diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs.meta deleted file mode 100644 index e7afb41ff..000000000 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignaling.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 297659e9af19444f96fa3faaf9e07cd7 -timeCreated: 1586218415 \ No newline at end of file diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs deleted file mode 100644 index e32a77443..000000000 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Unity.RenderStreaming.Signaling; -using UnityEngine; - -namespace Unity.RenderStreaming -{ - /// - /// - /// - [Serializable] - public class FurioosSignalingSettings : SignalingSettings - { - /// - /// - /// - public override Type signalingClass => typeof(FurioosSignaling); - - /// - /// - /// - public override IReadOnlyCollection iceServers => m_iceServers; - - /// - /// - /// - public string url => m_url; - - [SerializeField] - protected string m_url; - [SerializeField] - protected IceServer[] m_iceServers; - - /// - /// - /// - /// - /// - public FurioosSignalingSettings(string url, IceServer[] iceServers = null) - { - if (url == null) - throw new ArgumentNullException("url"); - if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) - throw new ArgumentException("url is not well formed Uri"); - - m_url = url; - m_iceServers = iceServers == null ? Array.Empty() : iceServers.Select(server => server.Clone()).ToArray(); - } - - /// - /// - /// - public FurioosSignalingSettings() - { - m_url = "http://127.0.0.1"; - m_iceServers = new[] - { - new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) - }; - } - - /// - /// - /// - /// - /// - public override bool ParseArguments(string[] arguments) - { - return true; - } - } -} diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta b/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta deleted file mode 100644 index 9dc5f982b..000000000 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/FurioosSignalingSettings.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 0144784cda4001d489615dee132aa6e6 -timeCreated: 1674112040 \ No newline at end of file diff --git a/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity b/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity index 4008e2430..08f5d351e 100644 --- a/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity +++ b/com.unity.renderstreaming/Samples~/Example/Menu/Menu.unity @@ -1590,8 +1590,6 @@ MonoBehaviour: m_Image: {fileID: 0} - m_Text: Http m_Image: {fileID: 0} - - m_Text: Furioos - m_Image: {fileID: 0} m_OnValueChanged: m_PersistentCalls: m_Calls: [] diff --git a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs index 7aace2fa3..0af6bb47d 100644 --- a/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs +++ b/com.unity.renderstreaming/Samples~/Example/Scripts/SceneSelectUI.cs @@ -18,7 +18,6 @@ internal enum SignalingType { WebSocket, Http, - Furioos } internal class RenderStreamingSettings @@ -71,14 +70,6 @@ public SignalingSettings SignalingSettings { switch (signalingType) { - case SignalingType.Furioos: - { - var schema = signalingSecured ? "https" : "http"; - return new FurioosSignalingSettings - ( - url: $"{schema}://{signalingAddress}" - ); - } case SignalingType.WebSocket: { var schema = signalingSecured ? "wss" : "ws"; diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs index b5babf6b6..ca1f59d17 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingSettingsTest.cs @@ -76,21 +76,5 @@ public void HttpSignalingSettings() Assert.That(settings.url, Is.EqualTo(url)); Assert.That(settings.iceServers, Is.Empty); } - - [Test] - public void FurioosSignalingSettings() - { - var settings = new FurioosSignalingSettings(); - Assert.That(settings.signalingClass, Is.EqualTo(typeof(FurioosSignaling))); - Assert.That(settings.url, Is.Not.Empty); - Assert.That(settings.iceServers, Is.Not.Empty); - - Assert.That(() => new HttpSignalingSettings(url: null), Throws.Exception.TypeOf()); - - var url = "http://localhost"; - settings = new FurioosSignalingSettings(url: url); - Assert.That(settings.url, Is.EqualTo(url)); - Assert.That(settings.iceServers, Is.Empty); - } } } From d65cd0b95e94f3505c4b8b613122835eec8e1430 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Tue, 25 Jul 2023 15:28:43 +0900 Subject: [PATCH 080/117] doc: Add doc audio loopback (#927) --- .../Documentation~/audio-streaming.md | 3 ++- .../images/audiostreamsender_inspector.png | Bin 22440 -> 14894 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/Documentation~/audio-streaming.md b/com.unity.renderstreaming/Documentation~/audio-streaming.md index 4f44dd657..e71077d63 100644 --- a/com.unity.renderstreaming/Documentation~/audio-streaming.md +++ b/com.unity.renderstreaming/Documentation~/audio-streaming.md @@ -15,9 +15,10 @@ This component streams the audio rendering results from [`AudioListener`](https: | **Audio Source Type** | Choose the type of source for your audio streaming.
- *Audio Listener*
- *Audio Source*
- *Microphone* | *Audio Listener* | | *Audio Listener* | [`Audio Listener`](https://docs.unity3d.com/ScriptReference/AudioSource.html) instance for sending audio | | | *Audio Source* | [`Audio Source`](https://docs.unity3d.com/ScriptReference/AudioSource.html) instance for sending audio | | -| *Microphone Device Index* | The index of the video input device to be used. See [Microphone.devices](https://docs.unity3d.com/ScriptReference/Microphone-devices.html). | 0 | +| *Microphone Device Index* | The index of the microphone input device to be used. See [Microphone.devices](https://docs.unity3d.com/ScriptReference/Microphone-devices.html). | 0 | | *Auto Request User Authorization* | Whether request permission to use microphone. You don't need to enable it if you call [Application.RequestUserAuthorization](https://docs.unity3d.com/ScriptReference/Application.RequestUserAuthorization.html) yourself. | Enabled | | **Audio Codec** | *Default* option means trying to use all available codecs for negotiating other peers. | Default | +| *Loopback* | The sending audio is also played on sender side. | Disabled | | **Bitrate (kbits/sec)** | The bitrate of the audio streaming. | | | *Min* | The minimum value of the bitrate. | 0 | | *Max* | The maximum value of the bitrate. | 1000 | diff --git a/com.unity.renderstreaming/Documentation~/images/audiostreamsender_inspector.png b/com.unity.renderstreaming/Documentation~/images/audiostreamsender_inspector.png index bcdcfb4320d259436730b7e72dc7012085a0b1cc..efd3b674241748140426376d3e03ab62ec944881 100644 GIT binary patch literal 14894 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 z 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 MR#20#mNgCiKMQd3TL1t6 literal 22440 zcmb5WbyQUC+x{&$z|b?&-60J__s~i!0!p_?OLxQ2AV`O#pn!y=BGMfKH{FeN_weqy zzt8jgt@l~$S?_xPP?*K+nZ2)T?`xmO=Qz$CrKO>ak3)&`=+PtmXDSLW9z8;b1HWH_ zF@gUn!9jb#4>Z>o%5sk?hpBgf7a$wBI`EF_1l&6_4B$1kv&w7NM~_HZQ2)@LynHI)Gm(Vo9Hqoij3PY@g=pe7bYXja$PZS13wdpsI3a!WXKLo= z_HD)m-RRTWXxhYFIv60^F*w{dWctc)IOlN(U+s@tPtT?ww}A&v0iK>7e=}YpxBQn} zOAK?w-G#{+!XR=8bjv9t&$@?(8EdNBz;7)VH4pigaS%B$gF=$_LC|A@L>3h=@J5Wj zFR#At1-=dewgEFpGXMKiJ4FSA4g9fyz0X?>qCJaGDaf}mkajuAj1{;@X5wciY>)6P zles@jzP{Q0HgvKvFvQz>w^|WLIQ6Db3Ey|0gj4ry5(o7O<;m=<>|2c6c@KA=%S>94 z-7(L?=>o3W?k-y&gueZ{`O?5D1d&BMvseA2H3w3;hyMQab!cyy8ZgO zOuE3kKL%MejW)v#3m$V09y|)`)jhO!T?~%xY8_&`6J-{-yvO=n=0sNf{skw|5c6HF zvKK6_cgH(-mUoVDg% zIEQJ2p`T0brYh^D-!1-ZqLB7$yj0y$VGgNBU^5v1*ZOsStLVcj@uYD}XD$mqC1PJX+RnSqiwV zxYvkSlivO3t4(W9zHY^v%?K*TqhA!RTREPY7`UWfO9GlPq}*z)0rz`bUn`sY%MBWa z1AZMe@7kl?oDA>|NnOrcOj)7F&wSRT8!vxd8M{cY2x&E3R@NNHvXz z`_*rsUl2C`_v$*vP`UM7r8vrr!UXBriAtlnH-?jyMs@?t=a@8a%i-TgV#s)ffzv({ zn1uKSn~Q1J5vidJn_OENne&O49*HmNf1|r~LY2Lip3j!6K{#2#wNxY)UsOxb!vk+N z*kG4)O(ohx-J4q`0m7g7j-@Y(qnnP}afg@^B+ZeB56F=>PS0`Q zEovV5R+5Xmm1ao$H&Y1Pr#Fc6keh3Ew;OK_3^Z`2`LS0M zGc?WR$K$qIk^m_XZ9`uNL6-NgMgox*66Fs!Q>}YjwΝOVbe!GoJ-bUGiy^x5K#M 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 From 46a045c485ebb94c8fb588f886e1d86f1fcf5aa1 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Wed, 26 Jul 2023 12:19:06 +0900 Subject: [PATCH 081/117] doc: Update change log for 3.1.0-exp.6 (#928) --- com.unity.renderstreaming/CHANGELOG.md | 17 ++++++++- .../ice-server-configuration-browser.png | Bin 0 -> 83633 bytes .../images/turn-server-settings.png | Bin 0 -> 86391 bytes .../Documentation~/turnserver.md | 33 +++++++----------- 4 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 com.unity.renderstreaming/Documentation~/images/ice-server-configuration-browser.png create mode 100644 com.unity.renderstreaming/Documentation~/images/turn-server-settings.png diff --git a/com.unity.renderstreaming/CHANGELOG.md b/com.unity.renderstreaming/CHANGELOG.md index cbb1b3eb2..e1c939b8a 100644 --- a/com.unity.renderstreaming/CHANGELOG.md +++ b/com.unity.renderstreaming/CHANGELOG.md @@ -4,11 +4,26 @@ All notable changes to com.unity.renderstreaming package will be documented in t The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [3.1.0-exp.7] - 2023-07-31 +## [3.1.0-exp.7] - 2023-08-09 + +### Added + +- Added configurable logger to enable users to customize logging for their environment. ### Changed - Upgrade the version of WebRTC package `3.0.0-pre.6`. +- Add `AudioStreamSender.loopback` property. + +### Fixed + +- Fixed error on HTTP signaling when using short polling interval. +- Fixed `SignalingManager` so that ICE server configurations aren't effected. +- Added a workaround to fix an issue where `InputField` wasn't worked when entering characters from browsers or a `Receiver` scene of package sample. + +### Removed + +- Removed Furioos Integration. ## [3.1.0-exp.6] - 2023-02-24 diff --git a/com.unity.renderstreaming/Documentation~/images/ice-server-configuration-browser.png b/com.unity.renderstreaming/Documentation~/images/ice-server-configuration-browser.png new file mode 100644 index 0000000000000000000000000000000000000000..277fb5c07fdbb6fd12c2be77985580b44ac7922f GIT binary patch literal 83633 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 b!b`a%S`j|wY`eK@xSzVRmQvXx%dr0ekKLa- literal 0 HcmV?d00001 diff --git a/com.unity.renderstreaming/Documentation~/images/turn-server-settings.png b/com.unity.renderstreaming/Documentation~/images/turn-server-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..b3e91bbf0f4c5d148fd8d2e5b199bae37a5807f6 GIT binary patch literal 86391 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 Ib4q9e069iQApigX literal 0 HcmV?d00001 diff --git a/com.unity.renderstreaming/Documentation~/turnserver.md b/com.unity.renderstreaming/Documentation~/turnserver.md index c132bed66..71117f0f7 100644 --- a/com.unity.renderstreaming/Documentation~/turnserver.md +++ b/com.unity.renderstreaming/Documentation~/turnserver.md @@ -9,7 +9,7 @@ This document covers the process of linking Unity Render Streaming to a TURN ser ## Instance settings -[coturn](https://github.com/coturn/coturn) software is an open source implementation for TURN servers. +[coturn](https://github.com/coturn/coturn) software is an open source implementation for TURN servers. The following is an explanation for running coturn on a GCP instance. **ubuntu-minimal-1604-xenial-v20190628** is used in the instance image so that the `apt` command can be used to install coturn. If the distribution is supported by coturn, there shouldn't be any issues. See the [coturn documentation](https://github.com/coturn/coturn) for details on coturn. @@ -27,15 +27,15 @@ The port used by the TURN server needs to be public, so add the following settin ### Installing coturn -Log into the GCP instance with `ssh`. +Log into the GCP instance with `ssh`. Install `coturn`. ```shell sudo apt install coturn ``` -Change the settings for booting with a daemon to use coturn as a TURN server. -Edit the following file. +Change the settings for booting with a daemon to use coturn as a TURN server. +Edit the following file. ```shell sudo vim /etc/default/coturn @@ -72,7 +72,7 @@ realm=yourcompany.com log-file=/var/tmp/turn.log ``` -When finished, restart the coturn service. +When finished, restart the coturn service. ```shell sudo systemctl restart coturn @@ -96,25 +96,16 @@ Use the [webrtc sample](https://webrtc.github.io/samples/src/content/peerconnect ![TURN connection testing](images/turn-connection-testing.png) -Click `Gather candidates` to show a list of potential communication paths. Verify that a log is also printed on the TURN server side. +Click `Gather candidates` to show a list of potential communication paths. Verify that a log is also printed on the TURN server side. ### Browser side changes -Change the `config.iceServers` settings under `video-player.js` on the browser side. - -```javascript -config.iceServers = [{ - urls: ['stun:stun.l.google.com:19302'] - }, { - urls: ['turn:xx.xx.xx.xx:3478?transport=tcp'], - username: 'username', - credential: 'password' - } -]; -``` +Start the web server, access the site, and add the TURN server settings to each of the ICE servers as follows: + +![Set ICE Servers Configuration On Browser](images/ice-server-configuration-browser.png) -### Unity side changes +### Unity side changes -Add the TURN server settings to `Ice Server` in the `Render Streaming` inspector. +Open the Project Settings window and add the URL of the TURN server to the Render Streaming settings as shown below. -![TURN Render Streaming inspector](images/turn-renderstreaming-inspector.png) +![TURN Server Settings](images/turn-server-settings.png) From e24eb78092ad69a131ae9276684fa95763088b23 Mon Sep 17 00:00:00 2001 From: Brian Harrison <92394761+BrianHarrisonUnity@users.noreply.github.com> Date: Tue, 25 Jul 2023 21:20:57 -0600 Subject: [PATCH 082/117] fix: DataChannel closing all connections (#925) * fix: Fix closing all data channels when one client disconnects * fix: Fix an exception from processing input after removing data channel. * test: Add test for DataChannel disconnecting with multiple connections. --- .../Runtime/Scripts/Broadcast.cs | 2 +- .../Runtime/Scripts/DataChannelBase.cs | 40 +++-- .../Runtime/Scripts/SignalingHandlerBase.cs | 9 +- .../WebBrowserInputChannelReceiver.cs | 6 + .../Tests/Runtime/SignalingHandlerTest.cs | 146 ++++++++++++++++++ 5 files changed, 184 insertions(+), 19 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/Broadcast.cs b/com.unity.renderstreaming/Runtime/Scripts/Broadcast.cs index 1fc8a431a..01741ac7a 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Broadcast.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Broadcast.cs @@ -48,7 +48,7 @@ private void Disconnect(string connectionId) { RemoveReceiver(connectionId, receiver); } - foreach (var channel in streams.OfType()) + foreach (var channel in streams.OfType().Where(c => c.ConnectionId == connectionId)) { RemoveChannel(connectionId, channel); } diff --git a/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs b/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs index eb7953c96..ff7339169 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/DataChannelBase.cs @@ -1,10 +1,11 @@ +using System; using Unity.WebRTC; using UnityEngine; namespace Unity.RenderStreaming { /// - /// + /// /// public abstract class DataChannelBase : MonoBehaviour, IDataChannel { @@ -12,48 +13,53 @@ public abstract class DataChannelBase : MonoBehaviour, IDataChannel internal const string LabelPropertyName = nameof(label); /// - /// + /// /// [SerializeField] protected bool local = false; /// - /// + /// /// [SerializeField] protected string label; /// - /// + /// /// public bool IsLocal => local; /// - /// + /// /// public string Label => label; /// - /// + /// /// public bool IsConnected => Channel != null && Channel.ReadyState == RTCDataChannelState.Open; /// - /// + /// + /// + public string ConnectionId { get; protected set; } + + /// + /// /// public RTCDataChannel Channel { get; protected set; } /// - /// + /// /// public OnStartedChannelHandler OnStartedChannel { get; set; } /// - /// + /// /// public OnStoppedChannelHandler OnStoppedChannel { get; set; } /// - /// + /// /// /// /// @@ -62,10 +68,12 @@ public virtual void SetChannel(string connectionId, RTCDataChannel channel) Channel = channel; if (Channel == null) { + ConnectionId = String.Empty; OnStoppedChannel?.Invoke(connectionId); return; } + ConnectionId = connectionId; label = Channel.Label; Channel.OnOpen += () => { OnOpen(connectionId); }; Channel.OnClose += () => { OnClose(connectionId); }; @@ -78,7 +86,7 @@ public virtual void SetChannel(string connectionId, RTCDataChannel channel) } /// - /// + /// /// /// public virtual void Send(byte[] msg) @@ -87,7 +95,7 @@ public virtual void Send(byte[] msg) } /// - /// + /// /// /// public virtual void Send(string msg) @@ -96,7 +104,7 @@ public virtual void Send(string msg) } /// - /// + /// /// /// public virtual void SetChannel(SignalingEventData data) @@ -105,7 +113,7 @@ public virtual void SetChannel(SignalingEventData data) } /// - /// + /// /// /// protected virtual void OnMessage(byte[] bytes) @@ -113,7 +121,7 @@ protected virtual void OnMessage(byte[] bytes) } /// - /// + /// /// /// protected virtual void OnOpen(string connectionId) @@ -121,7 +129,7 @@ protected virtual void OnOpen(string connectionId) OnStartedChannel?.Invoke(connectionId); } /// - /// + /// /// /// protected virtual void OnClose(string connectionId) diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingHandlerBase.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingHandlerBase.cs index a0eacb05b..3a3df7284 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingHandlerBase.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingHandlerBase.cs @@ -198,7 +198,7 @@ public virtual void RemoveSender(string connectionId, IStreamSender sender) } /// - /// + /// /// /// /// @@ -353,7 +353,7 @@ public interface IStreamReceiver } /// - /// + /// /// public interface IDataChannel { @@ -372,6 +372,11 @@ public interface IDataChannel /// string Label { get; } + /// + /// + /// + string ConnectionId { get; } + /// /// /// diff --git a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs index f0373aba4..4d745355c 100644 --- a/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs +++ b/com.unity.renderstreaming/Samples~/Example/WebBrowserInput/WebBrowserInputChannelReceiver/WebBrowserInputChannelReceiver.cs @@ -55,6 +55,12 @@ public override void SetChannel(string connectionId, RTCDataChannel channel) onDeviceChange?.Invoke(remoteInput.RemoteKeyboard, InputDeviceChange.Removed); onDeviceChange?.Invoke(remoteInput.RemoteMouse, InputDeviceChange.Removed); onDeviceChange?.Invoke(remoteInput.RemoteTouchscreen, InputDeviceChange.Removed); + + if (Channel != null) + { + Channel.OnMessage -= remoteInput.ProcessInput; + } + remoteInput.Dispose(); remoteInput = null; } diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs index 11a16f2eb..913bc6bae 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs @@ -303,6 +303,152 @@ public IEnumerator SetCodec() container1.Dispose(); container2.Dispose(); } + + //todo:: crash in dispose process on standalone linux + [UnityTest, Timeout(10000)] + [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer, RuntimePlatform.Android })] + public IEnumerator DataChannelDisconnectWithMultipleConnections() + { + // Ignore signaling log errors due to multiple connections + LogAssert.ignoreFailingMessages = true; + + string connectionId = "12345"; + string connectionId2 = "54321"; + var container1 = TestContainer.Create("test1"); + var container2 = TestContainer.Create("test2"); + var container4 = TestContainer.Create("test4"); + + var channel1 = container1.test.gameObject.AddComponent(); + bool isStartedChannel1 = false; + bool isStoppedChannel1 = false; + channel1.OnStartedChannel += _ => isStartedChannel1 = true; + channel1.OnStoppedChannel += _ => isStoppedChannel1 = true; + container1.test.component.AddComponent(channel1); + + container1.test.component.CreateConnection(connectionId); + yield return new WaitUntil(() => container1.test.component.ExistConnection(connectionId)); + + var channel2 = container2.test.gameObject.AddComponent(); + bool isStartedChannel2 = false; + bool isStoppedChannel2 = false; + channel2.OnStartedChannel += _ => isStartedChannel2 = true; + channel2.OnStoppedChannel += _ => isStoppedChannel2 = true; + + channel2.SetLocal(true); + channel2.SetLabel("test"); + + Assert.That(channel2.IsConnected, Is.False); + Assert.That(channel2.IsLocal, Is.True); + Assert.That(channel2.Label, Is.EqualTo("test")); + + container2.test.component.AddComponent(channel2); + container2.test.component.CreateConnection(connectionId); + yield return new WaitUntil(() => container2.test.component.ExistConnection(connectionId)); + yield return new WaitUntil(() => isStartedChannel1 && isStartedChannel2); + Assert.That(isStartedChannel1, Is.True); + Assert.That(isStartedChannel2, Is.True); + + Assert.That(channel1.IsLocal, Is.False); + Assert.That(channel1.Label, Is.EqualTo("test")); + Assert.That(channel1.IsConnected, Is.True); + Assert.That(channel1.Channel, Is.Not.Null); + Assert.That(channel1.ConnectionId, Is.EqualTo(connectionId)); + + Assert.That(channel2.IsConnected, Is.True); + Assert.That(channel2.Channel, Is.Not.Null); + Assert.That(channel2.ConnectionId, Is.EqualTo(connectionId)); + + var channel3 = container1.test.gameObject.AddComponent(); + bool isStartedChannel3 = false; + bool isStoppedChannel3 = false; + channel3.OnStartedChannel += _ => isStartedChannel3 = true; + channel3.OnStoppedChannel += _ => isStoppedChannel3 = true; + container1.test.component.AddComponent(channel3); + + container1.test.component.CreateConnection(connectionId2); + yield return new WaitUntil(() => container1.test.component.ExistConnection(connectionId2)); + + var channel4 = container4.test.gameObject.AddComponent(); + bool isStartedChannel4 = false; + bool isStoppedChannel4 = false; + channel4.OnStartedChannel += _ => isStartedChannel4 = true; + channel4.OnStoppedChannel += _ => isStoppedChannel4 = true; + + channel4.SetLocal(true); + channel4.SetLabel("test2"); + + Assert.That(channel4.IsConnected, Is.False); + Assert.That(channel4.IsLocal, Is.True); + Assert.That(channel4.Label, Is.EqualTo("test2")); + + container4.test.component.AddComponent(channel4); + container4.test.component.CreateConnection(connectionId2); + yield return new WaitUntil(() => container4.test.component.ExistConnection(connectionId2)); + yield return new WaitUntil(() => isStartedChannel3 && isStartedChannel4); + Assert.That(isStartedChannel3, Is.True); + Assert.That(isStartedChannel4, Is.True); + + Assert.That(channel3.IsLocal, Is.False); + Assert.That(channel3.Label, Is.EqualTo("test2")); + Assert.That(channel3.IsConnected, Is.True); + Assert.That(channel3.Channel, Is.Not.Null); + Assert.That(channel3.ConnectionId, Is.EqualTo(connectionId2)); + + Assert.That(channel4.IsConnected, Is.True); + Assert.That(channel4.Channel, Is.Not.Null); + Assert.That(channel4.ConnectionId, Is.EqualTo(connectionId2)); + + // send message from channel1 to channel2 + string sendMessage = "hello"; + string receivedMessage = null; + channel2.OnReceiveMessage = message => { receivedMessage = message; }; + channel1.Send(sendMessage); + yield return new WaitUntil(() => !string.IsNullOrEmpty(receivedMessage)); + Assert.That(receivedMessage, Is.EqualTo(sendMessage)); + + // send message from channel2 to channel1 + receivedMessage = null; + channel1.OnReceiveMessage = message => { receivedMessage = message; }; + channel2.Send(sendMessage); + yield return new WaitUntil(() => !string.IsNullOrEmpty(receivedMessage)); + Assert.That(receivedMessage, Is.EqualTo(sendMessage)); + + // send message from channel3 to channel4 + receivedMessage = null; + channel4.OnReceiveMessage = message => { receivedMessage = message; }; + channel3.Send(sendMessage); + yield return new WaitUntil(() => !string.IsNullOrEmpty(receivedMessage)); + Assert.That(receivedMessage, Is.EqualTo(sendMessage)); + + // send message from channel4 to channel3 + receivedMessage = null; + channel3.OnReceiveMessage = message => { receivedMessage = message; }; + channel4.Send(sendMessage); + yield return new WaitUntil(() => !string.IsNullOrEmpty(receivedMessage)); + Assert.That(receivedMessage, Is.EqualTo(sendMessage)); + + container1.test.component.DeleteConnection(connectionId); + container2.test.component.DeleteConnection(connectionId); + + yield return new WaitUntil(() => isStoppedChannel1 && isStoppedChannel2); + Assert.That(isStoppedChannel1, Is.True); + Assert.That(isStoppedChannel2, Is.True); + Assert.That(isStoppedChannel3, Is.False); + Assert.That(isStoppedChannel4, Is.False); + + container1.test.component.DeleteConnection(connectionId2); + container4.test.component.DeleteConnection(connectionId2); + + yield return new WaitUntil(() => isStoppedChannel3 && isStoppedChannel4); + Assert.That(isStoppedChannel3, Is.True); + Assert.That(isStoppedChannel4, Is.True); + + container1.Dispose(); + container2.Dispose(); + container4.Dispose(); + + LogAssert.ignoreFailingMessages = false; + } } class SingleConnectionTest From 8feaeaae3897c5d49b4bdf8c359e3a815d624f9c Mon Sep 17 00:00:00 2001 From: Dong Date: Mon, 31 Jul 2023 09:35:27 +0800 Subject: [PATCH 083/117] fix: Fix WebApp devDependencies conflict issue (#931) --- WebApp/package-lock.json | 1904 +++----------------------------------- WebApp/package.json | 4 +- 2 files changed, 127 insertions(+), 1781 deletions(-) diff --git a/WebApp/package-lock.json b/WebApp/package-lock.json index 943e9cb9d..4de2c83a5 100644 --- a/WebApp/package-lock.json +++ b/WebApp/package-lock.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@jest-mock/express": "^2.0.1", - "@types/jest": "^29.0.0", + "@types/jest": "^29.0.2", "@types/morgan": "^1.9.3", "@typescript-eslint/eslint-plugin": "^5.36.2", "@typescript-eslint/parser": "^5.36.2", @@ -34,7 +34,7 @@ "mock-socket": "^9.1.5", "newman": "^5.3.2", "pkg": "^5.8.0", - "ts-jest": "^28.0.8", + "ts-jest": "^29.0.2", "ts-node": "^10.9.1", "typescript": "^4.8.2" } @@ -804,52 +804,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/console/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/core": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.0.2.tgz", @@ -897,18 +851,6 @@ } } }, - "node_modules/@jest/core/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/core/node_modules/@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -935,23 +877,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/core/node_modules/jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -986,23 +911,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/core/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -1047,35 +955,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/environment/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/expect": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.0.2.tgz", @@ -1118,52 +997,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/fake-timers/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/globals": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.0.2.tgz", @@ -1179,35 +1012,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/reporters": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.0.2.tgz", @@ -1252,18 +1056,6 @@ } } }, - "node_modules/@jest/reporters/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/reporters/node_modules/@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -1290,23 +1082,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/reporters/node_modules/jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -1341,23 +1116,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/reporters/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -1428,35 +1186,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/test-result/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/test-sequencer": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.0.2.tgz", @@ -1472,35 +1201,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/test-sequencer/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/test-sequencer/node_modules/jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -1535,23 +1235,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/test-sequencer/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jest/test-sequencer/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -1582,12 +1265,12 @@ } }, "node_modules/@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "version": "29.6.1", + "resolved": "https://registry.npmmirror.com/@jest/types/-/types-29.6.1.tgz", + "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", "dev": true, "dependencies": { - "@jest/schemas": "^28.1.3", + "@jest/schemas": "^29.6.0", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -1595,9 +1278,27 @@ "chalk": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/@jest/schemas": { + "version": "29.6.0", + "resolved": "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.6.0.tgz", + "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/types/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -1867,9 +1568,9 @@ } }, "node_modules/@types/jest": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.0.0.tgz", - "integrity": "sha512-X6Zjz3WO4cT39Gkl0lZ2baFRaEMqJl5NC1OjElkwtNzAlbkr2K/WJXkBkH5VP0zx4Hgsd2TZYdOEfvp2Dxia+Q==", + "version": "29.5.3", + "resolved": "https://registry.npmmirror.com/@types/jest/-/jest-29.5.3.tgz", + "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -3648,52 +3349,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/expect/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/express": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", @@ -4839,52 +4494,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-circus/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4934,52 +4543,6 @@ } } }, - "node_modules/jest-cli/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-config": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.0.2.tgz", @@ -5025,18 +4588,6 @@ } } }, - "node_modules/jest-config/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-config/node_modules/@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -5063,23 +4614,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-config/node_modules/babel-jest": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.0.2.tgz", @@ -5166,23 +4700,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-config/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -5255,52 +4772,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-environment-node": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.0.2.tgz", @@ -5318,52 +4789,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-environment-node/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-get-type": { "version": "29.0.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", @@ -5421,35 +4846,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-message-util/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-mock": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.0.2.tgz", @@ -5463,35 +4859,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-mock/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-pnp-resolver": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", @@ -5551,35 +4918,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-resolve/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-resolve/node_modules/jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -5614,23 +4952,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-resolve/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-resolve/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -5692,18 +5013,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runner/node_modules/@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -5730,23 +5039,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runner/node_modules/jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -5781,23 +5073,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runner/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -5875,18 +5150,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runtime/node_modules/@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -5913,23 +5176,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runtime/node_modules/jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -5964,23 +5210,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-runtime/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -6045,18 +5274,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-snapshot/node_modules/@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -6083,23 +5300,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-snapshot/node_modules/jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -6134,23 +5334,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-snapshot/node_modules/jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -6181,12 +5364,12 @@ } }, "node_modules/jest-util": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.1.tgz", - "integrity": "sha512-FktOu7ca1DZSyhPAxgxB6hfh2+9zMoJ7aEQA759Z6p45NuO8mWcqujH+UdHlCm/V6JTWwDztM2ITCzU1ijJAfw==", + "version": "29.6.2", + "resolved": "https://registry.npmmirror.com/jest-util/-/jest-util-29.6.2.tgz", + "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", "dev": true, "dependencies": { - "@jest/types": "^28.1.1", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -6194,7 +5377,7 @@ "picomatch": "^2.2.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-validate": { @@ -6214,35 +5397,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -6274,52 +5428,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-websocket-mock": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/jest-websocket-mock/-/jest-websocket-mock-2.4.0.tgz", @@ -6390,35 +5498,6 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest/node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest/node_modules/@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/js-sha512": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", @@ -8602,32 +7681,32 @@ "dev": true }, "node_modules/ts-jest": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", + "version": "29.1.1", + "resolved": "https://registry.npmmirror.com/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", "dev": true, "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", + "jest-util": "^29.0.0", + "json5": "^2.2.3", "lodash.memoize": "4.x", "make-error": "1.x", - "semver": "7.x", + "semver": "^7.5.3", "yargs-parser": "^21.0.1" }, "bin": { "ts-jest": "cli.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^28.0.0", - "babel-jest": "^28.0.0", - "jest": "^28.0.0", - "typescript": ">=4.3" + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { "@babel/core": { @@ -8644,6 +7723,21 @@ } } }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -9696,45 +8790,6 @@ "jest-message-util": "^29.0.2", "jest-util": "^29.0.2", "slash": "^3.0.0" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } } }, "@jest/core": { @@ -9773,15 +8828,6 @@ "strip-ansi": "^6.0.0" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, "@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -9805,20 +8851,6 @@ "write-file-atomic": "^4.0.1" } }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -9845,20 +8877,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -9873,51 +8891,26 @@ "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/environment": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.0.2.tgz", - "integrity": "sha512-Yf+EYaLOrVCgts/aTS5nGznU4prZUPa5k9S63Yct8YSOKj2jkdS17hHSUKhk5jxDFMyCy1PXknypDw7vfgc/mA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.0.2", - "@jest/types": "^29.0.2", - "@types/node": "*", - "jest-mock": "^29.0.2" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "has-flag": "^4.0.0" } } } }, + "@jest/environment": { + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.0.2.tgz", + "integrity": "sha512-Yf+EYaLOrVCgts/aTS5nGznU4prZUPa5k9S63Yct8YSOKj2jkdS17hHSUKhk5jxDFMyCy1PXknypDw7vfgc/mA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.0.2", + "@jest/types": "^29.0.2", + "@types/node": "*", + "jest-mock": "^29.0.2" + } + }, "@jest/expect": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.0.2.tgz", @@ -9949,45 +8942,6 @@ "jest-message-util": "^29.0.2", "jest-mock": "^29.0.2", "jest-util": "^29.0.2" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } } }, "@jest/globals": { @@ -10000,31 +8954,6 @@ "@jest/expect": "^29.0.2", "@jest/types": "^29.0.2", "jest-mock": "^29.0.2" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - } } }, "@jest/reporters": { @@ -10060,15 +8989,6 @@ "v8-to-istanbul": "^9.0.1" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, "@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -10092,20 +9012,6 @@ "write-file-atomic": "^4.0.1" } }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -10132,20 +9038,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -10198,31 +9090,6 @@ "@jest/types": "^29.0.2", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - } } }, "@jest/test-sequencer": { @@ -10237,29 +9104,6 @@ "slash": "^3.0.0" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -10286,20 +9130,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -10323,17 +9153,34 @@ } }, "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "version": "29.6.1", + "resolved": "https://registry.npmmirror.com/@jest/types/-/types-29.6.1.tgz", + "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", "dev": true, "requires": { - "@jest/schemas": "^28.1.3", + "@jest/schemas": "^29.6.0", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" + }, + "dependencies": { + "@jest/schemas": { + "version": "29.6.0", + "resolved": "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.6.0.tgz", + "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + } } }, "@jridgewell/gen-mapping": { @@ -10581,9 +9428,9 @@ } }, "@types/jest": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.0.0.tgz", - "integrity": "sha512-X6Zjz3WO4cT39Gkl0lZ2baFRaEMqJl5NC1OjElkwtNzAlbkr2K/WJXkBkH5VP0zx4Hgsd2TZYdOEfvp2Dxia+Q==", + "version": "29.5.3", + "resolved": "https://registry.npmmirror.com/@types/jest/-/jest-29.5.3.tgz", + "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", "dev": true, "requires": { "expect": "^29.0.0", @@ -10806,7 +9653,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "8.2.0", @@ -11872,45 +10720,6 @@ "jest-matcher-utils": "^29.0.2", "jest-message-util": "^29.0.2", "jest-util": "^29.0.2" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } } }, "express": { @@ -12734,31 +11543,6 @@ "@jest/types": "^29.0.2", "import-local": "^3.0.2", "jest-cli": "^29.0.2" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - } } }, "jest-changed-files": { @@ -12809,43 +11593,6 @@ "stack-utils": "^2.0.3" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -12875,45 +11622,6 @@ "jest-validate": "^29.0.2", "prompts": "^2.0.1", "yargs": "^17.3.1" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } } }, "jest-config": { @@ -12946,15 +11654,6 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, "@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -12975,21 +11674,7 @@ "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "write-file-atomic": "^4.0.1" } }, "babel-jest": { @@ -13055,20 +11740,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -13123,45 +11794,6 @@ "jest-get-type": "^29.0.0", "jest-util": "^29.0.2", "pretty-format": "^29.0.2" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } } }, "jest-environment-node": { @@ -13176,45 +11808,6 @@ "@types/node": "*", "jest-mock": "^29.0.2", "jest-util": "^29.0.2" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } } }, "jest-get-type": { @@ -13260,31 +11853,6 @@ "pretty-format": "^29.0.2", "slash": "^3.0.0", "stack-utils": "^2.0.3" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - } } }, "jest-mock": { @@ -13295,38 +11863,14 @@ "requires": { "@jest/types": "^29.0.2", "@types/node": "*" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - } } }, "jest-pnp-resolver": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true + "dev": true, + "requires": {} }, "jest-resolve": { "version": "29.0.2", @@ -13345,29 +11889,6 @@ "slash": "^3.0.0" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -13394,20 +11915,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -13477,15 +11984,6 @@ "source-map-support": "0.5.13" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, "@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -13509,20 +12007,6 @@ "write-file-atomic": "^4.0.1" } }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -13549,20 +12033,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -13624,15 +12094,6 @@ "strip-bom": "^4.0.0" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, "@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -13656,20 +12117,6 @@ "write-file-atomic": "^4.0.1" } }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -13696,20 +12143,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -13764,15 +12197,6 @@ "semver": "^7.3.5" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, "@jest/transform": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.2.tgz", @@ -13796,20 +12220,6 @@ "write-file-atomic": "^4.0.1" } }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "jest-haste-map": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.2.tgz", @@ -13836,20 +12246,6 @@ "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", "dev": true }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, "jest-worker": { "version": "29.0.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.2.tgz", @@ -13873,12 +12269,12 @@ } }, "jest-util": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.1.tgz", - "integrity": "sha512-FktOu7ca1DZSyhPAxgxB6hfh2+9zMoJ7aEQA759Z6p45NuO8mWcqujH+UdHlCm/V6JTWwDztM2ITCzU1ijJAfw==", + "version": "29.6.2", + "resolved": "https://registry.npmmirror.com/jest-util/-/jest-util-29.6.2.tgz", + "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", "dev": true, "requires": { - "@jest/types": "^28.1.1", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -13900,29 +12296,6 @@ "pretty-format": "^29.0.2" }, "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -13945,45 +12318,6 @@ "emittery": "^0.10.2", "jest-util": "^29.0.2", "string-length": "^4.0.1" - }, - "dependencies": { - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/types": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.2.tgz", - "integrity": "sha512-5WNMesBLmlkt1+fVkoCjHa0X3i3q8zc4QLTDkdHgCa2gyPZc7rdlZBWgVLqwS1860ZW5xJuCDwAzqbGaXIr/ew==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.2.tgz", - "integrity": "sha512-ozk8ruEEEACxqpz0hN9UOgtPZS0aN+NffwQduR5dVlhN+eN47vxurtvgZkYZYMpYrsmlAEx1XabkB3BnN0GfKQ==", - "dev": true, - "requires": { - "@jest/types": "^29.0.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } } }, "jest-websocket-mock": { @@ -15706,19 +14040,30 @@ "dev": true }, "ts-jest": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", + "version": "29.1.1", + "resolved": "https://registry.npmmirror.com/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", "dev": true, "requires": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", + "jest-util": "^29.0.0", + "json5": "^2.2.3", "lodash.memoize": "4.x", "make-error": "1.x", - "semver": "7.x", + "semver": "^7.5.3", "yargs-parser": "^21.0.1" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "ts-node": { @@ -16000,7 +14345,8 @@ "ws": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==" + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "requires": {} }, "xmlbuilder": { "version": "15.1.1", diff --git a/WebApp/package.json b/WebApp/package.json index cec85e5bd..3537ecafa 100644 --- a/WebApp/package.json +++ b/WebApp/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@jest-mock/express": "^2.0.1", - "@types/jest": "^29.0.0", + "@types/jest": "^29.0.2", "@types/morgan": "^1.9.3", "@typescript-eslint/eslint-plugin": "^5.36.2", "@typescript-eslint/parser": "^5.36.2", @@ -35,7 +35,7 @@ "mock-socket": "^9.1.5", "newman": "^5.3.2", "pkg": "^5.8.0", - "ts-jest": "^28.0.8", + "ts-jest": "^29.0.2", "ts-node": "^10.9.1", "typescript": "^4.8.2" }, From 4af125703d7d06ce8bb0764ead7d9dfa02d8c541 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 1 Aug 2023 12:34:12 +0900 Subject: [PATCH 084/117] test: Change test Unity version 2022.2 to 2022.3 (#932) * update 2022.2 to 2022.3 * add test * add test * fix * format --- .yamato/package.metafile | 3 ++- .yamato/upm-ci-renderstreaming-packages.yml | 3 --- README.md | 9 +------- .../Documentation~/index.md | 9 ++++---- .../Runtime/Scripts/DateTimeExtension.cs | 7 ------ .../Runtime/Scripts/InputSystem/Sender.cs | 23 ------------------- .../Tests/Runtime/CommandLineParserTest.cs | 1 - .../Tests/Runtime/DataTimeExtensionTest.cs | 15 ++++++++++++ .../Runtime/DataTimeExtensionTest.cs.meta | 11 +++++++++ .../Runtime/InputSystem/InputRemotingTest.cs | 4 +++- .../Tests/Runtime/SignalingHandlerTest.cs | 6 ++++- 11 files changed, 42 insertions(+), 49 deletions(-) create mode 100644 com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs create mode 100644 com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs.meta diff --git a/.yamato/package.metafile b/.yamato/package.metafile index c040a9387..0dc45d010 100644 --- a/.yamato/package.metafile +++ b/.yamato/package.metafile @@ -7,7 +7,8 @@ package_displayname: Render Streaming editors: - version: 2020.3 - version: 2021.3 - - version: 2022.2 + - version: 2022.3 + - version: 2023.1 - version: trunk platforms: - name: win-gpu diff --git a/.yamato/upm-ci-renderstreaming-packages.yml b/.yamato/upm-ci-renderstreaming-packages.yml index 1b161394f..264b24fb9 100644 --- a/.yamato/upm-ci-renderstreaming-packages.yml +++ b/.yamato/upm-ci-renderstreaming-packages.yml @@ -343,10 +343,7 @@ test_{{ packagename }}_{{ editor.version }}: {% endfor %} - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_{{ editor.version }}_ios {% for target in test_targets_android %} -# todo: Exclude tests for Unity 2022.2 on Android platform because it is instable. -{% if editor.version != "2022.2" %} - .yamato/upm-ci-renderstreaming-packages.yml#test_{{ packagename }}_{{ editor.version }}_android_{{ target.name }} -{% endif %} {% endfor %} test_renderpipeline_{{ packagename }}_{{ editor.version }}: diff --git a/README.md b/README.md index 5713cd315..5d104804f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ unity 2020.3 unity 2021.3 -unity 2022.2 +unity 2022.3 **Unity Render Streaming** is a solution that provides Unity's high quality rendering abilities via browser. It's designed to meet the needs of tasks like viewing car configurators or architectural models on mobile devices. This solution's streaming technology takes advantage of [WebRTC](https://webrtc.org/), and developers can even use the [WebRTC package](https://docs.unity3d.com/Packages/com.unity.webrtc@latest) to create their own unique solutions. @@ -13,13 +13,6 @@ This solution's streaming technology takes advantage of [WebRTC](https://webrtc. Please see [Requirements](com.unity.renderstreaming/Documentation~/index.md#requirements) section. -### Furioos compatibility - -**Unity Render Streaming** is also supported natively by **Furioos** platform https://www.furioos.com/ . -That means that you can easily build a Unity application, upload it on **Furioos** and enjoy all the features of **Unity Render Streaming** without worrying about the deployment and scalability issues of your project. - -Please see [Furioos Tutorial](com.unity.renderstreaming/Documentation~/deploy-to-furioos) section to find out how it works. - ### License - `com.unity.renderstreaming` - [LICENSE.md](com.unity.renderstreaming/LICENSE.md) diff --git a/com.unity.renderstreaming/Documentation~/index.md b/com.unity.renderstreaming/Documentation~/index.md index 95a6358f7..f574d935f 100644 --- a/com.unity.renderstreaming/Documentation~/index.md +++ b/com.unity.renderstreaming/Documentation~/index.md @@ -31,15 +31,16 @@ This version of Render Streaming is compatible with the following versions of th - **Unity 2020.3** - **Unity 2021.3** -- **Unity 2022.2** +- **Unity 2022.3** +- **Unity 2023.1** ### Platform -- **Windows** +- **Windows** (x64 only) - **Linux** -- **macOS** +- **macOS** (**Intel** and **Apple Slicon**) - **iOS** -- **Android** (**ARMv7** is not supported) +- **Android** (**ARM64** only. **ARMv7** is not supported) > [!NOTE] > This package depends on [the WebRTC package](https://docs.unity3d.com/Packages/com.unity.webrtc@3.0). If you build for mobile platform (iOS/Android), please see [the package documentation](https://docs.unity3d.com/Packages/com.unity.webrtc@3.0/manual/requirements.html#additional-notes) to know the requirements for building. diff --git a/com.unity.renderstreaming/Runtime/Scripts/DateTimeExtension.cs b/com.unity.renderstreaming/Runtime/Scripts/DateTimeExtension.cs index 8e6f5b891..4be14c79c 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/DateTimeExtension.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/DateTimeExtension.cs @@ -16,12 +16,5 @@ public static long ToJsMilliseconds(this DateTime dt) { return (long)((dt.ToUniversalTime().Ticks - DatetimeMinTimeTicks) / 10000); } - - public static DateTime ParseHttpDate(string text) - { - return DateTime.ParseExact(text, - "ddd, dd MMM yyyy HH:mm:ss Z", - System.Globalization.CultureInfo.InvariantCulture); - } } } diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs index 8c6a323c4..1aabb02ae 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/Sender.cs @@ -41,29 +41,6 @@ public void Dispose() InputSystem.onLayoutChange -= OnLayoutChange; } - /// - /// - /// - public override ReadOnlyArray devices - { - get - { - return InputSystem.devices; - } - } - - /// - /// - /// - public override IEnumerable layouts - { - get - { - // todo(kazuki):: filter layout - return InputSystem.ListLayouts(); - } - } - /// /// /// diff --git a/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs b/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs index 3a460a84c..31235000a 100644 --- a/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs @@ -1,6 +1,5 @@ using System.IO; using NUnit.Framework; -using Unity.RenderStreaming.Signaling; using Unity.WebRTC; using UnityEngine; using UnityEngine.TestTools; diff --git a/com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs b/com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs new file mode 100644 index 000000000..edd1e900f --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs @@ -0,0 +1,15 @@ +using System; +using NUnit.Framework; + +namespace Unity.RenderStreaming.RuntimeTest +{ + + public class DataTimeExtensionTest + { + [Test] + public void ToJsMilliseconds() + { + Assert.That(DateTime.Now.ToJsMilliseconds(), Is.GreaterThan(0)); + } + } +} diff --git a/com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs.meta b/com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs.meta new file mode 100644 index 000000000..a6d68ae13 --- /dev/null +++ b/com.unity.renderstreaming/Tests/Runtime/DataTimeExtensionTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e4d23d9326e94e46ad84b32ceb0d7b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs index 8f27fe7bd..754a83038 100644 --- a/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/InputSystem/InputRemotingTest.cs @@ -10,7 +10,7 @@ namespace Unity.RenderStreaming.RuntimeTest { - using InputRemoting = Unity.RenderStreaming.InputSystem.InputRemoting; + using InputRemoting = InputSystem.InputRemoting; class MessageSerializerTest { @@ -156,6 +156,7 @@ public void Sender() var sender = new Sender(); Assert.That(sender.layouts, Is.Not.Empty); Assert.That(sender.devices, Is.Not.Empty); + Assert.That(sender.GetDeviceById(0), Is.Null); var senderInput = new InputRemoting(sender); var senderDisposer = senderInput.Subscribe(new Observer(_channel1)); senderInput.StartSending(); @@ -170,6 +171,7 @@ public void Receiver() var receiver = new Receiver(_channel1); Assert.That(receiver.remoteDevices, Is.Empty); Assert.That(receiver.remoteLayouts, Is.Empty); + Assert.That(receiver.GetDeviceById(0), Is.Null); var receiverInput = new InputRemoting(receiver); var receiverDisposer = receiverInput.Subscribe(receiverInput); receiverInput.StartSending(); diff --git a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs index 913bc6bae..aa2b16b32 100644 --- a/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/SignalingHandlerTest.cs @@ -142,7 +142,7 @@ public void SetUp() //todo:: crash in dispose process on standalone linux [Test] [UnityPlatform(exclude = new[] { RuntimePlatform.LinuxPlayer })] - public void AddStreamSource() + public void AddAndRemoveStreamSource() { var container = TestContainer.Create("test"); var streamer = container.test.gameObject.AddComponent(); @@ -150,7 +150,11 @@ public void AddStreamSource() Assert.That(streamer.Track, Is.Null); Assert.That(streamer.Transceivers, Is.Empty); + Assert.That(container.test.component.Streams, Is.Empty); container.test.component.AddComponent(streamer); + Assert.That(container.test.component.Streams, Has.Count.EqualTo(1)); + container.test.component.RemoveComponent(streamer); + Assert.That(container.test.component.Streams, Is.Empty); container.Dispose(); } From 0e46feeced5c6588bfadbd8b5d9e71a25975e8f8 Mon Sep 17 00:00:00 2001 From: Takashi Kannan <26959415+kannan-xiao4@users.noreply.github.com> Date: Wed, 2 Aug 2023 15:29:00 +0900 Subject: [PATCH 085/117] fix: Re-save Bidirectional sample scene in 2020.3 (#933) --- .../Example/Bidirectional/Bidirectional.unity | 72 ++----------------- 1 file changed, 5 insertions(+), 67 deletions(-) diff --git a/com.unity.renderstreaming/Samples~/Example/Bidirectional/Bidirectional.unity b/com.unity.renderstreaming/Samples~/Example/Bidirectional/Bidirectional.unity index 9a77b74eb..01d5629e9 100644 --- a/com.unity.renderstreaming/Samples~/Example/Bidirectional/Bidirectional.unity +++ b/com.unity.renderstreaming/Samples~/Example/Bidirectional/Bidirectional.unity @@ -150,7 +150,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1180904220} - {fileID: 1423621364} @@ -216,7 +215,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1013695939} m_RootOrder: 0 @@ -297,7 +295,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1046847830} m_Father: {fileID: 447881815} @@ -423,7 +420,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2128695119} m_RootOrder: 0 @@ -504,7 +500,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2143861285} - {fileID: 678698532} @@ -567,10 +562,7 @@ MonoBehaviour: m_HideMobileInput: 0 m_CharacterValidation: 0 m_CharacterLimit: 0 - m_OnSubmit: - m_PersistentCalls: - m_Calls: [] - m_OnDidEndEdit: + m_OnEndEdit: m_PersistentCalls: m_Calls: [] m_OnValueChanged: @@ -648,7 +640,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2063941925} m_Father: {fileID: 2101892767} @@ -962,7 +953,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 474040021} m_RootOrder: 2 @@ -1043,7 +1033,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1261207007} m_Father: {fileID: 1268027397} @@ -1170,7 +1159,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1676245737} m_Father: {fileID: 932364532} @@ -1291,7 +1279,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1046847830} m_RootOrder: 0 @@ -1368,7 +1355,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1485084886} m_Father: {fileID: 1664650282} @@ -1459,7 +1445,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1031505000} - {fileID: 2101892767} @@ -1567,7 +1552,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1136971054} - {fileID: 966029635} @@ -1843,7 +1827,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 447881815} - {fileID: 310412160} @@ -1884,7 +1867,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2028247180} m_RootOrder: 0 @@ -1960,7 +1942,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 185332755} m_RootOrder: 1 @@ -2040,7 +2021,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1446828911} m_RootOrder: 2 @@ -2171,7 +2151,6 @@ Transform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 1, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -2204,7 +2183,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1134039333} m_RootOrder: 1 @@ -2283,7 +2261,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 185332755} - {fileID: 346362250} @@ -2350,7 +2327,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 474040021} m_RootOrder: 1 @@ -2427,7 +2403,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 172843649} m_Father: {fileID: 2134522315} @@ -2549,7 +2524,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 131784669} m_Father: {fileID: 447881815} @@ -2637,7 +2611,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 375413528} m_Father: {fileID: 174861474} @@ -2677,7 +2650,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1258234880} m_Father: {fileID: 1664650282} @@ -2803,7 +2775,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1446828911} m_RootOrder: 0 @@ -2880,7 +2851,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2028247180} - {fileID: 848285180} @@ -3006,7 +2976,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 474040021} m_RootOrder: 0 @@ -3082,7 +3051,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2125586391} m_RootOrder: 0 @@ -3161,7 +3129,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1363584100} - {fileID: 1221238572} @@ -3228,7 +3195,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1180904220} m_RootOrder: 1 @@ -3395,7 +3361,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1563904669} m_Father: {fileID: 1054277507} @@ -3432,7 +3397,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1708597410} m_Father: {fileID: 314272781} @@ -3472,7 +3436,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1846535030} - {fileID: 314272781} @@ -3582,7 +3545,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} m_Name: m_EditorClassIdentifier: - m_SendPointerHoverToParent: 1 m_MoveRepeatDelay: 0.5 m_MoveRepeatRate: 0.1 m_XRTrackingOrigin: {fileID: 0} @@ -3636,7 +3598,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 3 @@ -3669,7 +3630,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2125586391} m_RootOrder: 1 @@ -3746,7 +3706,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1180904220} m_RootOrder: 0 @@ -3816,6 +3775,7 @@ MonoBehaviour: m_Bitrate: min: 8 max: 208 + m_Loopback: 0 --- !u!1 &1423621363 GameObject: m_ObjectHideFlags: 0 @@ -3843,7 +3803,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2134522315} - {fileID: 932364532} @@ -3908,7 +3867,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1094835990} - {fileID: 1811427819} @@ -3995,7 +3953,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 474040021} m_Father: {fileID: 1846535030} @@ -4032,7 +3989,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1446828911} m_Father: {fileID: 441688740} @@ -4071,7 +4027,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1258234880} m_RootOrder: 0 @@ -4148,7 +4103,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 441688740} - {fileID: 1054277507} @@ -4256,7 +4210,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 346362250} m_RootOrder: 0 @@ -4336,7 +4289,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1261207007} m_RootOrder: 0 @@ -4412,7 +4364,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1859585769} m_RootOrder: 0 @@ -4492,7 +4443,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1859585769} m_RootOrder: 1 @@ -4568,7 +4518,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1446828911} m_RootOrder: 1 @@ -4645,7 +4594,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1464711774} m_Father: {fileID: 1268027397} @@ -4736,7 +4684,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1720753588} - {fileID: 1772713976} @@ -4911,15 +4858,14 @@ MonoBehaviour: m_useDefault: 1 signalingSettingsObject: {fileID: 0} signalingSettings: - rid: 0 + id: 0 handlers: - {fileID: 1915034406} runOnAwake: 0 evaluateCommandlineArguments: 1 references: - version: 2 - RefIds: - - rid: 0 + version: 1 + 00000000: type: {class: WebSocketSignalingSettings, ns: Unity.RenderStreaming, asm: Unity.RenderStreaming} data: m_url: ws://127.0.0.1 @@ -4939,7 +4885,6 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 1 @@ -5057,7 +5002,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 640709221} m_Father: {fileID: 1134039333} @@ -5134,7 +5078,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 220075808} m_RootOrder: 0 @@ -5211,7 +5154,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 220075808} m_Father: {fileID: 447881815} @@ -5338,7 +5280,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1161220085} - {fileID: 1360947117} @@ -5477,7 +5418,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 174875021} m_Father: {fileID: 932364532} @@ -5597,7 +5537,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1859585769} - {fileID: 2125586391} @@ -5665,7 +5604,6 @@ RectTransform: m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 185332755} m_RootOrder: 0 From 8289073020a6b1608411bcbabc27198c2cefb5e7 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:39:48 +0900 Subject: [PATCH 086/117] fix: Ice servers settings doesn't affecte when runOnAwake is enabled (#934) --- .../Runtime/Scripts/SignalingManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs index 875393fa2..33a357b4f 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs @@ -278,7 +278,13 @@ void Awake() return; var settings = m_useDefault ? RenderStreaming.GetSignalingSettings() : signalingSettings; - RTCIceServer[] iceServers = settings.iceServers.OfType().ToArray(); + int i = 0; + RTCIceServer[] iceServers = new RTCIceServer[settings.iceServers.Count()]; + foreach (var iceServer in settings.iceServers) + { + iceServers[i] = (RTCIceServer)iceServer; + i++; + } RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers }; ISignaling signaling = CreateSignaling(settings, SynchronizationContext.Current); _Run(conf, signaling, handlers.ToArray()); From 6538f64b7d16760c2bf00800ab9ac62a634abfc8 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Thu, 3 Aug 2023 19:42:22 +0900 Subject: [PATCH 087/117] fix: Fix NullReferenceException when disabling a flag on SignalingManager inspector (#935) * fix * fix --- .../Editor/SignalingManagerEditor.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs index 540a5894b..4968ea896 100644 --- a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs +++ b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs @@ -33,6 +33,8 @@ internal class SignalingManagerEditor : UnityEditor.Editor private void OnEnable() { + EditorApplication.projectChanged += OnProjectChanged; + m_UseDefault = serializedObject.FindProperty(SignalingManager.UseDefaultPropertyName); m_SignalingSettingsObject = serializedObject.FindProperty(SignalingManager.SignalingSettingsObjectPropertyName); m_SignalingSettings = serializedObject.FindProperty(SignalingManager.SignalingSettingsPropertyName); @@ -41,6 +43,11 @@ private void OnEnable() m_EvaluateCommandlineArguments = serializedObject.FindProperty(SignalingManager.EvaluateCommandlineArgumentsPropertyName); } + private void OnDisable() + { + EditorApplication.projectChanged -= OnProjectChanged; + } + public override VisualElement CreateInspectorGUI() { root = new VisualElement(); @@ -72,8 +79,6 @@ public override VisualElement CreateInspectorGUI() root.Add(new PropertyField(m_RunOnAwake, "Run On Awake")); root.Add(new PropertyField(m_EvaluateCommandlineArguments, "Evaluate Commandline Arguments")); - EditorApplication.projectChanged += OnProjectChanged; - // Disable UI when running in Playmode EditorApplication.playModeStateChanged += OnPlayModeStateChanged; if (EditorApplication.isPlaying) @@ -146,6 +151,8 @@ private void OnPlayModeStateChanged(PlayModeStateChange e) private void OnProjectChanged() { + if (root == null) + return; var paths = GetAvailableSignalingSettingsPath(); // Force to use default settings if there are no available settings in project folder. @@ -168,7 +175,6 @@ private void OnProjectChanged() } signalingSettingsPopupField.choices = availableObjects.ToList(); signalingSettingsPopupField.index = defaultIndex; - } private void OnClickedOpenProjectSettingsButton() From d6e6249765871f7ccb1acc1c55ba9e6acaf8d65f Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:37:16 +0900 Subject: [PATCH 088/117] fix: Add tooltips for Render Streaming Settings in Project Settings Window (#937) --- .../Editor/PropertyDrawers/SignalingSettingsDrawer.cs | 4 +++- .../Editor/RenderStreamingProjectSettingsProvider.cs | 8 ++++++-- .../Editor/SignalingManagerEditor.cs | 1 + .../Runtime/Scripts/RenderStreamingSettings.cs | 3 ++- .../Runtime/Scripts/Signaling/HttpSignalingSettings.cs | 6 +++--- .../Scripts/Signaling/WebSocketSignalingSettings.cs | 5 +++-- .../Runtime/Scripts/SignalingManager.cs | 5 ++++- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs index 08074dfae..49cd0283c 100644 --- a/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs +++ b/com.unity.renderstreaming/Editor/PropertyDrawers/SignalingSettingsDrawer.cs @@ -43,7 +43,9 @@ PopupField CreatePopUpSignalingType(SerializedProperty property, string var settings = fieldInfo.GetValue(property.serializedObject.targetObject) as SignalingSettings; var defaultValue = CustomSignalingSettingsEditor.FindLabelByInspectedType(settings.GetType()); var choices = CustomSignalingSettingsEditor.Labels().ToList(); - return new PopupField(label: label, choices: choices, defaultValue: defaultValue); + var field = new PopupField(label: label, choices: choices, defaultValue: defaultValue); + field.tooltip = "Choose the signaling type. \"WebSocket\" or \"HTTP Polling\"."; + return field; } static void ReplaceVisualElement(VisualElement oldValue, VisualElement newValue) diff --git a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs index 24b29f6cf..49e99ce17 100644 --- a/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs +++ b/com.unity.renderstreaming/Editor/RenderStreamingProjectSettingsProvider.cs @@ -20,6 +20,9 @@ internal class RenderStreamingProjectSettingsProvider : SettingsProvider private int currentSelectedSettingsAsset; private RenderStreamingSettings settings; + const string LabelRenderStreamingSettingsAsset = "Render Streaming Settings Asset"; + const string LabelCreateSettingsButton = "Create New Settings Asset"; + const string kSettingsPath = "Project/Render Streaming"; const string kTemplatePath = "Packages/com.unity.renderstreaming/Editor/UXML/RenderStreamingProjectSettings.uxml"; const string kStylePath = "Packages/com.unity.renderstreaming/Editor/Styles/RenderStreamingProjectSettings.uss"; @@ -59,10 +62,11 @@ public override void OnActivate(string searchContext, VisualElement rootElement) var defaultIndex = ArrayHelpers.IndexOf(availableRenderStreamingSettingsAssets, AssetDatabase.GetAssetPath(settings)); var choices = availableRenderStreamingSettingsAssets.ToList(); - var selectPopup = new PopupField(label: label, choices: choices, defaultIndex: defaultIndex) + var selectPopup = new PopupField(label: LabelRenderStreamingSettingsAsset, choices: choices, defaultIndex: defaultIndex) { name = "renderStreamingSettingsSelectPopup" }; + selectPopup.tooltip = "Choose the Render Streaming Settings."; selectPopup.RegisterValueChangedCallback(evt => { currentSelectedSettingsAsset = selectPopup.index; @@ -77,7 +81,7 @@ public override void OnActivate(string searchContext, VisualElement rootElement) }); selectorContainer.Add(selectPopup); - var createSettingsButton = new Button { text = "Create New Settings Asset" }; + var createSettingsButton = new Button { text = LabelCreateSettingsButton }; createSettingsButton.clicked += () => { CreateNewSettingsAsset(); diff --git a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs index 4968ea896..702177d28 100644 --- a/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs +++ b/com.unity.renderstreaming/Editor/SignalingManagerEditor.cs @@ -92,6 +92,7 @@ PopupField CreatePopUpSignalingType(SerializedProperty var paths = GetAvailableSignalingSettingsPath(); var field = new PopupField(label: label); + field.tooltip = "Choose the signaling settings."; field.formatSelectedValueCallback = v => AssetDatabase.GetAssetPath(v); field.formatListItemCallback = v => AssetDatabase.GetAssetPath(v); if (paths.Length == 0) diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs index f803e105e..dc22c7f51 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreamingSettings.cs @@ -13,7 +13,8 @@ public class RenderStreamingSettings : ScriptableObject /// /// /// - [SerializeField] public bool automaticStreaming; + [SerializeField, Tooltip("Automatically performs the necessary setup for streaming and starts streaming.")] + public bool automaticStreaming; [SerializeReference, SignalingSettings] public SignalingSettings signalingSettings = new WebSocketSignalingSettings(); diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs index 485b995d9..de7e9e893 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/HttpSignalingSettings.cs @@ -29,11 +29,11 @@ public class HttpSignalingSettings : SignalingSettings /// public int interval => m_interval; - [SerializeField] + [SerializeField, Tooltip("Set the polling frequency (in milliseconds) to the signaling server.")] private int m_interval; - [SerializeField] + [SerializeField, Tooltip("Set the signaling server URL. you should specify a URL starting with \"http\" or \"https\".")] protected string m_url; - [SerializeField] + [SerializeField, Tooltip("Set a list of STUN/TURN servers.")] protected IceServer[] m_iceServers; /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs index d56718453..2e9514543 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/Signaling/WebSocketSignalingSettings.cs @@ -27,9 +27,10 @@ public class WebSocketSignalingSettings : SignalingSettings /// public string url => m_url; - [SerializeField] + [SerializeField, Tooltip("Set the signaling server URL. you should specify a URL starting with \"ws\" or \"wss\".")] protected string m_url; - [SerializeField] + + [SerializeField, Tooltip("Set a list of STUN/TURN servers.")] protected IceServer[] m_iceServers; /// diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs index 33a357b4f..b9f8a22a1 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs @@ -24,7 +24,7 @@ public sealed class SignalingManager : MonoBehaviour internal const string EvaluateCommandlineArgumentsPropertyName = nameof(evaluateCommandlineArguments); #pragma warning disable 0649 - [SerializeField] + [SerializeField, Tooltip("Use settings in Project Settings Window.")] private bool m_useDefault = true; [SerializeField] @@ -54,6 +54,9 @@ public sealed class SignalingManager : MonoBehaviour private SignalingEventProvider m_provider; private bool m_running; + /// + /// + /// public bool Running => m_running; static ISignaling CreateSignaling(SignalingSettings settings, SynchronizationContext context) From 9200063d6409df16f92a8a5c75b505146a11c5d3 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:18:59 +0900 Subject: [PATCH 089/117] fix: Fix inputfield when using textmeshpro (#945) * fix inputfield when using textmeshpro * Update EmulateInputFieldEvent.cs --- .../Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs index c4c6b424e..303dcde59 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs @@ -213,6 +213,12 @@ unsafe Event CreateEvent(InputEventPtr ptr) // todo: not supported multibyte character. return null; } + if (utf32Char < 0x100) + { + // ignore control + if (char.IsControl((char)utf32Char)) + return null; + } return new Event { From 985a0c04d20bf82bee3b50e394b5974e5b411760 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:08:24 +0900 Subject: [PATCH 090/117] chore: bump @babel/traverse from 7.19.0 to 7.23.2 in /WebApp/client (#955) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.19.0 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- WebApp/client/package-lock.json | 401 +++++++++++++++++++++----------- 1 file changed, 267 insertions(+), 134 deletions(-) diff --git a/WebApp/client/package-lock.json b/WebApp/client/package-lock.json index 92a66291b..d9d94444e 100644 --- a/WebApp/client/package-lock.json +++ b/WebApp/client/package-lock.json @@ -30,17 +30,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.0.tgz", @@ -90,13 +162,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", - "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.19.0", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -145,34 +218,34 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -231,30 +304,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -284,13 +357,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -369,9 +442,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz", - "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -558,33 +631,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.0.tgz", - "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.0", - "@babel/types": "^7.19.0", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -602,13 +675,13 @@ } }, "node_modules/@babel/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", - "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1127,13 +1200,13 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@nodelib/fs.scandir": { @@ -5592,12 +5665,71 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -5638,13 +5770,14 @@ } }, "@babel/generator": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", - "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.19.0", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -5682,28 +5815,28 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { @@ -5747,24 +5880,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -5785,13 +5918,13 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -5854,9 +5987,9 @@ } }, "@babel/parser": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz", - "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -5986,30 +6119,30 @@ } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.0.tgz", - "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.0", - "@babel/types": "^7.19.0", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6023,13 +6156,13 @@ } }, "@babel/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", - "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -6433,13 +6566,13 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@nodelib/fs.scandir": { From c3a29d1d91dd331c72e0ef1c7acffc027db3f662 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:48:51 +0900 Subject: [PATCH 091/117] fix: Upgrade WebRTC package 3.0.0-pre.7 (#953) * fix * fix * Upgrade package version * fix * add post process to build iOS runtime * fix * postprocess for ios * fix --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + README.md | 3 +- RenderStreaming~/Packages/packages-lock.json | 8 ++-- .../ProjectSettings/ProjectVersion.txt | 4 +- TestProjects/Empty/Assets/Editor.meta | 8 ++++ .../Empty/Assets/Editor/PostProcess.cs | 40 +++++++++++++++++++ .../Empty/Assets/Editor/PostProcess.cs.meta | 11 +++++ TestProjects/Empty/Packages/manifest.json | 4 +- .../Empty/Packages/packages-lock.json | 23 +++-------- .../Empty/ProjectSettings/boot.config | 0 com.unity.renderstreaming/CHANGELOG.md | 6 +++ .../Editor/WebAppDownloader.cs | 2 +- .../Runtime/Scripts/RenderStreaming.cs | 9 ++++- .../Runtime/Scripts/VideoStreamSender.cs | 13 +++++- .../Samples~/Example/Editor/PostProcess.cs | 40 +++++++++++++++++++ .../Example/Editor/PostProcess.cs.meta | 11 +++++ com.unity.renderstreaming/package.json | 4 +- 17 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 TestProjects/Empty/Assets/Editor.meta create mode 100644 TestProjects/Empty/Assets/Editor/PostProcess.cs create mode 100644 TestProjects/Empty/Assets/Editor/PostProcess.cs.meta delete mode 100644 TestProjects/Empty/ProjectSettings/boot.config create mode 100644 com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs create mode 100644 com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs.meta diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d16104a5e..19a67ef04 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -27,6 +27,7 @@ body: What version of the package are you using? You can check the Unity version in Package Manager Window. See [manual](https://docs.unity3d.com/Manual/upm-ui.html). options: + - 3.1.0-exp.8 - 3.1.0-exp.7 - 3.1.0-exp.6 - 3.1.0-exp.5 diff --git a/README.md b/README.md index 5d104804f..21de4644e 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,8 @@ Please see [Requirements](com.unity.renderstreaming/Documentation~/index.md#requ | `3.1.0-exp.4` | - Streaming settings API
- *Unity 2022.1* support
- Remove *Unity 2019.4* from support list | Oct 2022 | | `3.1.0-exp.5` | - Fix bugs | Jan 2023 | | `3.1.0-exp.6` | - Streaming Settings Window
- Auto Configuration
- Command line option | Feb 2023 | -| `3.1.0-exp.7` | - Fix bugs | July 2023 | +| `3.1.0-exp.7` | - Fix bugs | Jul 2023 | +| `3.1.0-exp.8` | - Fix bugs | Nov 2023 | ## FAQ diff --git a/RenderStreaming~/Packages/packages-lock.json b/RenderStreaming~/Packages/packages-lock.json index c2e00d9e1..0a41adf92 100644 --- a/RenderStreaming~/Packages/packages-lock.json +++ b/RenderStreaming~/Packages/packages-lock.json @@ -40,7 +40,7 @@ "url": "https://packages.unity.com" }, "com.unity.inputsystem": { - "version": "1.6.1", + "version": "1.7.0", "depth": 1, "source": "registry", "dependencies": { @@ -60,7 +60,7 @@ "depth": 0, "source": "embedded", "dependencies": { - "com.unity.webrtc": "3.0.0-pre.6", + "com.unity.webrtc": "3.0.0-pre.7", "com.unity.inputsystem": "1.5.1", "com.unity.ugui": "1.0.0", "com.unity.modules.screencapture": "1.0.0" @@ -104,7 +104,7 @@ } }, "com.unity.webrtc": { - "version": "3.0.0-pre.6", + "version": "3.0.0-pre.7", "depth": 1, "source": "registry", "dependencies": { @@ -157,7 +157,7 @@ "url": "https://packages.unity.com" }, "com.unity.xr.core-utils": { - "version": "2.2.1", + "version": "2.2.3", "depth": 1, "source": "registry", "dependencies": { diff --git a/RenderStreaming~/ProjectSettings/ProjectVersion.txt b/RenderStreaming~/ProjectSettings/ProjectVersion.txt index 9cd738f3a..9706d869a 100644 --- a/RenderStreaming~/ProjectSettings/ProjectVersion.txt +++ b/RenderStreaming~/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2021.3.28f1 -m_EditorVersionWithRevision: 2021.3.28f1 (232e59c3f087) +m_EditorVersion: 2021.3.31f1 +m_EditorVersionWithRevision: 2021.3.31f1 (3409e2af086f) diff --git a/TestProjects/Empty/Assets/Editor.meta b/TestProjects/Empty/Assets/Editor.meta new file mode 100644 index 000000000..59e70b1fc --- /dev/null +++ b/TestProjects/Empty/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ff4235c650c6444a9bfcdff2322a547a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestProjects/Empty/Assets/Editor/PostProcess.cs b/TestProjects/Empty/Assets/Editor/PostProcess.cs new file mode 100644 index 000000000..afdcea08d --- /dev/null +++ b/TestProjects/Empty/Assets/Editor/PostProcess.cs @@ -0,0 +1,40 @@ +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +#if UNITY_IOS +using UnityEditor.iOS.Xcode; +#endif + +class PostProcess : IPostprocessBuildWithReport +{ + public int callbackOrder { get { return 0; } } + + public void OnPostprocessBuild(BuildReport report) + { +#if UNITY_IOS + if (report.summary.platform == BuildTarget.iOS) + { + string projectPath = report.summary.outputPath + "/Unity-iPhone.xcodeproj/project.pbxproj"; + + PBXProject pbxProject = new PBXProject(); + pbxProject.ReadFromFile(projectPath); + + //Disabling Bitcode on all targets + + //Main + string target = pbxProject.GetUnityMainTargetGuid(); + pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); + + //Unity Tests + target = pbxProject.TargetGuidByName(PBXProject.GetUnityTestTargetName()); + pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); + + //Unity Framework + target = pbxProject.GetUnityFrameworkTargetGuid(); + pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); + + pbxProject.WriteToFile(projectPath); + } +#endif + } +} diff --git a/TestProjects/Empty/Assets/Editor/PostProcess.cs.meta b/TestProjects/Empty/Assets/Editor/PostProcess.cs.meta new file mode 100644 index 000000000..c0f431468 --- /dev/null +++ b/TestProjects/Empty/Assets/Editor/PostProcess.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40f483dfdb21e42c697cf1fd36e61c0c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestProjects/Empty/Packages/manifest.json b/TestProjects/Empty/Packages/manifest.json index 7040b5701..eea30cd49 100644 --- a/TestProjects/Empty/Packages/manifest.json +++ b/TestProjects/Empty/Packages/manifest.json @@ -1,9 +1,9 @@ { "dependencies": { "com.unity.renderstreaming": "file:../../../com.unity.renderstreaming", - "com.unity.test-framework": "1.1.31", + "com.unity.test-framework": "1.1.33", "com.unity.textmeshpro": "3.0.6", - "com.unity.timeline": "1.6.4", + "com.unity.timeline": "1.7.5", "com.unity.ugui": "1.0.0", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", diff --git a/TestProjects/Empty/Packages/packages-lock.json b/TestProjects/Empty/Packages/packages-lock.json index 17e5d269e..f962d323e 100644 --- a/TestProjects/Empty/Packages/packages-lock.json +++ b/TestProjects/Empty/Packages/packages-lock.json @@ -15,7 +15,7 @@ "url": "https://packages.unity.com" }, "com.unity.inputsystem": { - "version": "1.4.4", + "version": "1.7.0", "depth": 1, "source": "registry", "dependencies": { @@ -28,14 +28,14 @@ "depth": 0, "source": "local", "dependencies": { - "com.unity.webrtc": "3.0.0-pre.4", - "com.unity.inputsystem": "1.4.4", + "com.unity.webrtc": "3.0.0-pre.7", + "com.unity.inputsystem": "1.5.1", "com.unity.ugui": "1.0.0", "com.unity.modules.screencapture": "1.0.0" } }, "com.unity.test-framework": { - "version": "1.1.31", + "version": "1.1.33", "depth": 0, "source": "registry", "dependencies": { @@ -55,7 +55,7 @@ "url": "https://packages.unity.com" }, "com.unity.timeline": { - "version": "1.6.4", + "version": "1.7.5", "depth": 0, "source": "registry", "dependencies": { @@ -76,7 +76,7 @@ } }, "com.unity.webrtc": { - "version": "3.0.0-pre.4", + "version": "3.0.0-pre.7", "depth": 1, "source": "registry", "dependencies": { @@ -218,17 +218,6 @@ "version": "1.0.0", "depth": 0, "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.uielementsnative": "1.0.0" - } - }, - "com.unity.modules.uielementsnative": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", diff --git a/TestProjects/Empty/ProjectSettings/boot.config b/TestProjects/Empty/ProjectSettings/boot.config deleted file mode 100644 index e69de29bb..000000000 diff --git a/com.unity.renderstreaming/CHANGELOG.md b/com.unity.renderstreaming/CHANGELOG.md index e1c939b8a..0c8f713df 100644 --- a/com.unity.renderstreaming/CHANGELOG.md +++ b/com.unity.renderstreaming/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to com.unity.renderstreaming package will be documented in t The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [3.1.0-exp.8] - 2023-11-30 + +### Changed + +- Upgrade the version of WebRTC package `3.0.0-pre.7`. + ## [3.1.0-exp.7] - 2023-08-09 ### Added diff --git a/com.unity.renderstreaming/Editor/WebAppDownloader.cs b/com.unity.renderstreaming/Editor/WebAppDownloader.cs index 71bc0cbf9..db4fcf117 100644 --- a/com.unity.renderstreaming/Editor/WebAppDownloader.cs +++ b/com.unity.renderstreaming/Editor/WebAppDownloader.cs @@ -7,7 +7,7 @@ namespace Unity.RenderStreaming.Editor internal static class WebAppDownloader { const string URLRoot = "https://github.com/Unity-Technologies/UnityRenderStreaming"; - const string LatestKnownVersion = "3.1.0-exp.7"; + const string LatestKnownVersion = "3.1.0-exp.8"; // TODO::fix release process of webserver runtime. const string FileNameWebAppForMac = "webserver_mac"; diff --git a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs index b6c2bd7be..1fb5f4492 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/RenderStreaming.cs @@ -123,9 +123,14 @@ private static void InitializeInEditor() EditorApplication.projectChanged += () => { - if (!EditorBuildSettings.TryGetConfigObject(EditorBuildSettingsConfigKey, out RenderStreamingSettings _)) + if (EditorBuildSettings.TryGetConfigObject(EditorBuildSettingsConfigKey, out RenderStreamingSettings _)) { - Settings = AssetDatabase.LoadAssetAtPath(DefaultRenderStreamingSettingsPath); + return; + } + var value = AssetDatabase.LoadAssetAtPath(DefaultRenderStreamingSettingsPath); + if (value != null) + { + Settings = value; } }; } diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs index b63117ea8..e9c75320c 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs @@ -670,6 +670,11 @@ static Vector2Int GetScreenSize() return new Vector2Int(screenWidth, screenHeight); } + public static void CopyTextureFunction(Texture source, RenderTexture dest) + { + Graphics.Blit(source, dest); + } + public override WaitForCreateTrack CreateTrack() { Vector2Int screenSize = GetScreenSize(); @@ -688,7 +693,13 @@ public override WaitForCreateTrack CreateTrack() m_coroutineScreenCapture = m_behaviour.StartCoroutine(RecordScreenFrame()); var instruction = new WaitForCreateTrack(); - instruction.Done(new VideoStreamTrack(m_screenCopyTexture, isOpenGl)); + + CopyTexture copyTexture = null; + if (!isOpenGl) + { + copyTexture = CopyTextureFunction; + } + instruction.Done(new VideoStreamTrack(m_screenCopyTexture, copyTexture)); return instruction; } diff --git a/com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs b/com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs new file mode 100644 index 000000000..afdcea08d --- /dev/null +++ b/com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs @@ -0,0 +1,40 @@ +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +#if UNITY_IOS +using UnityEditor.iOS.Xcode; +#endif + +class PostProcess : IPostprocessBuildWithReport +{ + public int callbackOrder { get { return 0; } } + + public void OnPostprocessBuild(BuildReport report) + { +#if UNITY_IOS + if (report.summary.platform == BuildTarget.iOS) + { + string projectPath = report.summary.outputPath + "/Unity-iPhone.xcodeproj/project.pbxproj"; + + PBXProject pbxProject = new PBXProject(); + pbxProject.ReadFromFile(projectPath); + + //Disabling Bitcode on all targets + + //Main + string target = pbxProject.GetUnityMainTargetGuid(); + pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); + + //Unity Tests + target = pbxProject.TargetGuidByName(PBXProject.GetUnityTestTargetName()); + pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); + + //Unity Framework + target = pbxProject.GetUnityFrameworkTargetGuid(); + pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO"); + + pbxProject.WriteToFile(projectPath); + } +#endif + } +} diff --git a/com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs.meta b/com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs.meta new file mode 100644 index 000000000..6d1a574aa --- /dev/null +++ b/com.unity.renderstreaming/Samples~/Example/Editor/PostProcess.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a6fe6dcd54394a62ba7efc4d7352432 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index 4c0aecbb1..13a8be1e1 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -1,11 +1,11 @@ { "name": "com.unity.renderstreaming", "displayName": "Unity Render Streaming", - "version": "3.1.0-exp.7", + "version": "3.1.0-exp.8", "unity": "2020.3", "description": "This is a package for using Unity Render Streaming technology.", "dependencies": { - "com.unity.webrtc": "3.0.0-pre.6", + "com.unity.webrtc": "3.0.0-pre.7", "com.unity.inputsystem": "1.5.1", "com.unity.ugui": "1.0.0", "com.unity.modules.screencapture": "1.0.0" From ac89655e3d0917e703f83c48448cb421c331eb8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:49:20 +0900 Subject: [PATCH 092/117] chore: bump @babel/traverse from 7.18.2 to 7.23.2 in /WebApp (#954) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.18.2 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- WebApp/package-lock.json | 597 +++++++++++++++++++++++++++++++-------- 1 file changed, 482 insertions(+), 115 deletions(-) diff --git a/WebApp/package-lock.json b/WebApp/package-lock.json index 4de2c83a5..604ee3341 100644 --- a/WebApp/package-lock.json +++ b/WebApp/package-lock.json @@ -53,17 +53,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.17.10", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz", @@ -168,34 +240,62 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz", - "integrity": "sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" @@ -254,21 +354,44 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -298,13 +421,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.17.12", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", - "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -572,33 +695,59 @@ } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz", - "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==", + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.18.2", - "@babel/helper-environment-visitor": "^7.18.2", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.18.0", - "@babel/types": "^7.18.2", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -606,6 +755,61 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/traverse/node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -1313,9 +1517,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" @@ -1331,19 +1535,19 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@nodelib/fs.scandir": { @@ -8216,12 +8420,71 @@ } }, "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -8306,28 +8569,54 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz", - "integrity": "sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-module-imports": { @@ -8371,18 +8660,37 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + } } }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -8403,13 +8711,13 @@ } }, "@babel/highlight": { - "version": "7.17.12", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", - "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -8604,34 +8912,93 @@ } }, "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "dependencies": { + "@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true + }, + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz", - "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.18.2", - "@babel/helper-environment-visitor": "^7.18.2", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.18.0", - "@babel/types": "^7.18.2", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, "dependencies": { + "@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "requires": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true + }, + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -9194,9 +9561,9 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true }, "@jridgewell/set-array": { @@ -9206,19 +9573,19 @@ "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@nodelib/fs.scandir": { From d1b75694e73d3ea107842abaf83bbc083158b265 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:49:38 +0900 Subject: [PATCH 093/117] chore: bump tough-cookie and newman in /WebApp (#957) Removes [tough-cookie](https://github.com/salesforce/tough-cookie). It's no longer used after updating ancestor dependency [newman](https://github.com/postmanlabs/newman). These dependencies need to be updated together. Removes `tough-cookie` Updates `newman` from 5.3.2 to 6.0.0 - [Changelog](https://github.com/postmanlabs/newman/blob/develop/CHANGELOG.yaml) - [Commits](https://github.com/postmanlabs/newman/compare/v5.3.2...v6.0.0) --- updated-dependencies: - dependency-name: tough-cookie dependency-type: indirect - dependency-name: newman dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- WebApp/package-lock.json | 1105 ++++++++++++++++++++------------------ WebApp/package.json | 2 +- 2 files changed, 584 insertions(+), 523 deletions(-) diff --git a/WebApp/package-lock.json b/WebApp/package-lock.json index 604ee3341..dca2e83bc 100644 --- a/WebApp/package-lock.json +++ b/WebApp/package-lock.json @@ -32,7 +32,7 @@ "jest": "^29.0.2", "jest-websocket-mock": "^2.4.0", "mock-socket": "^9.1.5", - "newman": "^5.3.2", + "newman": "^6.0.0", "pkg": "^5.8.0", "ts-jest": "^29.0.2", "ts-node": "^10.9.1", @@ -838,6 +838,16 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -883,6 +893,12 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", + "dev": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", @@ -1599,6 +1615,30 @@ "node": ">= 6" } }, + "node_modules/@postman/tough-cookie": { + "version": "4.1.3-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", + "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@postman/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@postman/tunnel-agent": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", @@ -1893,21 +1933,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/parser": { "version": "5.36.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.36.2.tgz", @@ -2019,21 +2044,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils": { "version": "5.36.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.36.2.tgz", @@ -2270,9 +2280,9 @@ } }, "node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "node_modules/asynckit": { @@ -2300,9 +2310,9 @@ } }, "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, "node_modules/babel-plugin-istanbul": { @@ -2491,9 +2501,9 @@ } }, "node_modules/brotli": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.2.tgz", - "integrity": "sha512-K0HNa0RRpUpcF8yS4yNSd6vmkrvA+wRd+symIcwhfqGLAi7YgGlKfO4oDYVgiahiLGNviO9uY7Zlb1MCPeTmSA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", "dev": true, "dependencies": { "base64-js": "^1.1.2" @@ -2665,9 +2675,9 @@ } }, "node_modules/chardet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", - "integrity": "sha512-NpwMDdSIprbYx1CLnfbxEIarI0Z+s9MssEgggMNheGM+WD68yOhV7IEA/3r6tr0yTRgQD0HuZJDw32s99i6L+A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.6.0.tgz", + "integrity": "sha512-+QOTw3otC4+FxdjK9RopGpNOglADbr4WPFi0SonkO99JbpkTPbMxmdm4NenhF5Zs+4gPXLI1+y2uazws5TMe8w==", "dev": true }, "node_modules/charset": { @@ -2698,21 +2708,21 @@ "dev": true }, "node_modules/cli-progress": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.10.0.tgz", - "integrity": "sha512-kLORQrhYCAtUPLZxqsAt2YJGOvRdt34+O6jl5cQGb7iF3dM55FQZlTR+rQyIK9JUcO9bBMwZsTlND+3dmFU2Cw==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", "dev": true, "dependencies": { - "string-width": "^4.2.0" + "string-width": "^4.2.3" }, "engines": { "node": ">=4" } }, "node_modules/cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -2721,7 +2731,7 @@ "node": "10.* || >= 12.*" }, "optionalDependencies": { - "colors": "1.4.0" + "@colors/colors": "1.5.0" } }, "node_modules/cliui": { @@ -2800,12 +2810,12 @@ } }, "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true, "engines": { - "node": ">= 10" + "node": ">=16" } }, "node_modules/concat-map": { @@ -3017,6 +3027,16 @@ "node": ">= 0.8" } }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -3490,12 +3510,6 @@ "node": ">= 0.6" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3652,12 +3666,6 @@ "node >=0.6.0" ] }, - "node_modules/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", - "dev": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3744,12 +3752,12 @@ } }, "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "version": "10.0.12", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.12.tgz", + "integrity": "sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==", "dev": true, "engines": { - "node": ">= 0.4.0" + "node": ">= 10.4.0" } }, "node_modules/fill-range": { @@ -3832,9 +3840,9 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, "node_modules/forever-agent": { @@ -4134,13 +4142,13 @@ "dev": true }, "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, @@ -4256,22 +4264,34 @@ } }, "node_modules/httpntlm": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.7.7.tgz", - "integrity": "sha512-Pv2Rvrz8H0qv1Dne5mAdZ9JegG1uc6Vu5lwLflIY6s8RKHdZQbW39L4dYswSgqMDT0pkJILUTKjeyU0VPNRZjA==", + "version": "1.8.13", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", + "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==", "dev": true, + "funding": [ + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/samdecrock" + } + ], "dependencies": { + "des.js": "^1.0.1", "httpreq": ">=0.4.22", + "js-md4": "^0.3.2", "underscore": "~1.12.1" }, "engines": { - "node": ">=0.8.0" + "node": ">=10.4.0" } }, "node_modules/httpreq": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.5.2.tgz", - "integrity": "sha512-2Jm+x9WkExDOeFRrdBCBSpLPT5SokTcRHkunV3pjKmX/cx6av8zQ0WtHUMDrYb6O4hBFzNU6sxJEypvRUVYKnw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", + "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", "dev": true, "engines": { "node": ">= 6.15.1" @@ -4420,15 +4440,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -5702,6 +5713,21 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/jose": { + "version": "4.14.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", + "integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true + }, "node_modules/js-sha512": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", @@ -6049,6 +6075,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6067,6 +6099,21 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -6180,64 +6227,38 @@ "dev": true }, "node_modules/newman": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/newman/-/newman-5.3.2.tgz", - "integrity": "sha512-cWy8pV0iwvMOZLTw3hkAHcwo2ZA0GKkXm8oUMn1Ltii3ZI2nKpnrg9QGdIT0hGHChRkX6prY5e3Aar7uykMGNg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/newman/-/newman-6.0.0.tgz", + "integrity": "sha512-QaANQC5b6ga348MezIVRI9ZmMs+cg3MdYIp0tSEauH2tmWOAR9+cghNsFJNjU9JPui3jJp1ALC7pQq6g3Jqpxw==", "dev": true, "dependencies": { - "async": "3.2.3", - "chardet": "1.4.0", - "cli-progress": "3.10.0", - "cli-table3": "0.6.1", + "@postman/tough-cookie": "4.1.3-postman.1", + "async": "3.2.4", + "chardet": "1.6.0", + "cli-progress": "3.12.0", + "cli-table3": "0.6.3", "colors": "1.4.0", - "commander": "7.2.0", + "commander": "11.0.0", "csv-parse": "4.16.3", - "eventemitter3": "4.0.7", - "filesize": "8.0.7", + "filesize": "10.0.12", + "liquid-json": "0.3.1", "lodash": "4.17.21", - "mkdirp": "1.0.4", - "postman-collection": "4.1.1", - "postman-collection-transformer": "4.1.6", - "postman-request": "2.88.1-postman.31", - "postman-runtime": "7.29.0", + "mkdirp": "3.0.1", + "postman-collection": "4.2.1", + "postman-collection-transformer": "4.1.7", + "postman-request": "2.88.1-postman.33", + "postman-runtime": "7.33.0", "pretty-ms": "7.0.1", - "semver": "7.3.5", + "semver": "7.5.4", "serialised-error": "1.1.3", - "tough-cookie": "3.0.1", - "word-wrap": "1.2.3", + "word-wrap": "1.2.5", "xmlbuilder": "15.1.1" }, "bin": { "newman": "bin/newman.js" }, "engines": { - "node": ">=10" - } - }, - "node_modules/newman/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/newman/node_modules/tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=6" + "node": ">=16" } }, "node_modules/node-abi": { @@ -6696,21 +6717,21 @@ } }, "node_modules/postman-collection": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.1.1.tgz", - "integrity": "sha512-ODpJtlf8r99DMcTU7gFmi/yvQYckFzcuE6zL/fWnyrFT34ugdCBFlX+DN7M+AnP6lmR822fv5s60H4DnL4+fAg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.2.1.tgz", + "integrity": "sha512-DFLt3/yu8+ldtOTIzmBUctoupKJBOVK4NZO0t68K2lIir9smQg7OdQTBjOXYy+PDh7u0pSDvD66tm93eBHEPHA==", "dev": true, "dependencies": { - "faker": "5.5.3", + "@faker-js/faker": "5.5.3", "file-type": "3.9.0", "http-reasons": "0.1.0", "iconv-lite": "0.6.3", "liquid-json": "0.3.1", "lodash": "4.17.21", "mime-format": "2.0.1", - "mime-types": "2.1.34", + "mime-types": "2.1.35", "postman-url-encoder": "3.0.5", - "semver": "7.3.5", + "semver": "7.5.4", "uuid": "8.3.2" }, "engines": { @@ -6718,15 +6739,15 @@ } }, "node_modules/postman-collection-transformer": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.6.tgz", - "integrity": "sha512-xvdQb6sZoWcG9xZXUPSuxocjcd6WCZlINlGGiuHdSfxhgiwQhj9qhF0JRFbagZ8xB0+pYUairD5MiCENc6DEVA==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.7.tgz", + "integrity": "sha512-SxJkm/LnlFZs2splUBnS4jQFicgBptghpm4voHtNnaum3Ad64E2MHLV4fJhv58dVUmFwdSwdQUN3m2q0iLecnQ==", "dev": true, "dependencies": { "commander": "8.3.0", "inherits": "2.0.4", "lodash": "4.17.21", - "semver": "7.3.5", + "semver": "7.5.4", "strip-json-comments": "3.1.1" }, "bin": { @@ -6757,27 +6778,6 @@ "node": ">=0.10.0" } }, - "node_modules/postman-collection/node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/postman-collection/node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "dependencies": { - "mime-db": "1.51.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/postman-collection/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -6788,16 +6788,17 @@ } }, "node_modules/postman-request": { - "version": "2.88.1-postman.31", - "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.31.tgz", - "integrity": "sha512-OJbYqP7ItxQ84yHyuNpDywCZB0HYbpHJisMQ9lb1cSL3N5H3Td6a2+3l/a74UMd3u82BiGC5yQyYmdOIETP/nQ==", + "version": "2.88.1-postman.33", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.33.tgz", + "integrity": "sha512-uL9sCML4gPH6Z4hreDWbeinKU0p0Ke261nU7OvII95NU22HN6Dk7T/SaVPaj6T4TsQqGKIFw6/woLZnH7ugFNA==", "dev": true, "dependencies": { "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", "@postman/tunnel-agent": "^0.6.3", "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "brotli": "~1.3.2", + "aws4": "^1.12.0", + "brotli": "^1.3.3", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", @@ -6807,14 +6808,13 @@ "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", + "mime-types": "^2.1.35", "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", - "qs": "~6.5.2", + "qs": "~6.5.3", "safe-buffer": "^5.1.2", "stream-length": "^1.0.2", - "tough-cookie": "~2.5.0", - "uuid": "^3.3.2" + "uuid": "^8.3.2" }, "engines": { "node": ">= 6" @@ -6830,75 +6830,75 @@ } }, "node_modules/postman-request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/postman-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.29.0.tgz", - "integrity": "sha512-eXxHREE/fUpohkGPRgBY1YccSGx9cyW3mtGiPyIE4zD5fYzasgBHqW6kbEND3Xrd3yf/uht/YI1H8O7J1+A1+w==", + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.33.0.tgz", + "integrity": "sha512-cYCb+5Y12FwZU/T3gOj2SKiOz38pisVLc0tdppb+ZlG7iqn5aLgxghJwhjG62pZCV6uixKiQX1hNdLSk9a9Xtw==", "dev": true, "dependencies": { - "async": "3.2.3", - "aws4": "1.11.0", - "handlebars": "4.7.7", - "httpntlm": "1.7.7", + "@postman/tough-cookie": "4.1.3-postman.1", + "async": "3.2.4", + "aws4": "1.12.0", + "handlebars": "4.7.8", + "httpntlm": "1.8.13", + "jose": "4.14.4", "js-sha512": "0.8.0", "lodash": "4.17.21", - "mime-types": "2.1.34", + "mime-types": "2.1.35", "node-oauth1": "1.3.0", "performance-now": "2.1.0", - "postman-collection": "4.1.1", - "postman-request": "2.88.1-postman.31", - "postman-sandbox": "4.0.6", + "postman-collection": "4.2.0", + "postman-request": "2.88.1-postman.33", + "postman-sandbox": "4.2.7", "postman-url-encoder": "3.0.5", "serialised-error": "1.1.3", - "tough-cookie": "3.0.1", + "strip-json-comments": "3.1.1", "uuid": "8.3.2" }, "engines": { - "node": ">=10" - } - }, - "node_modules/postman-runtime/node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true, - "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/postman-runtime/node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "node_modules/postman-runtime/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "dependencies": { - "mime-db": "1.51.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/postman-runtime/node_modules/tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "node_modules/postman-runtime/node_modules/postman-collection": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.2.0.tgz", + "integrity": "sha512-tvOLgN1h6Kab6dt43PmBoV5kYO/YUta3x0C2QqfmbzmHZe47VTpZ/+gIkGlbNhjKNPUUub5X6ehxYKoaTYdy1w==", "dev": true, "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "@faker-js/faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mime-format": "2.0.1", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.5", + "semver": "7.5.4", + "uuid": "8.3.2" }, "engines": { - "node": ">=6" + "node": ">=10" } }, "node_modules/postman-runtime/node_modules/uuid": { @@ -6911,19 +6911,63 @@ } }, "node_modules/postman-sandbox": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.0.6.tgz", - "integrity": "sha512-PPRanSNEE4zy3kO7CeSBHmAfJnGdD9ecHY/Mjh26CQuZZarGkNO8c0U/n+xX3+5M1BRNc82UYq6YCtdsSDqcng==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.2.7.tgz", + "integrity": "sha512-/EcCrKnb/o+9iLS4u+H76E0kBomJFjPptVjoDiq1uZ7Es/4aTv0MAX+0aoDxdDO+0h9sl8vy65uKQwyjN7AOaw==", "dev": true, "dependencies": { "lodash": "4.17.21", + "postman-collection": "4.2.0", "teleport-javascript": "1.0.0", - "uvm": "2.0.2" + "uvm": "2.1.1" }, "engines": { "node": ">=10" } }, + "node_modules/postman-sandbox/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postman-sandbox/node_modules/postman-collection": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.2.0.tgz", + "integrity": "sha512-tvOLgN1h6Kab6dt43PmBoV5kYO/YUta3x0C2QqfmbzmHZe47VTpZ/+gIkGlbNhjKNPUUub5X6ehxYKoaTYdy1w==", + "dev": true, + "dependencies": { + "@faker-js/faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mime-format": "2.0.1", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.5", + "semver": "7.5.4", + "uuid": "8.3.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postman-sandbox/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/postman-url-encoder": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz", @@ -7066,9 +7110,9 @@ } }, "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "node_modules/pump": { @@ -7104,6 +7148,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7212,6 +7262,12 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -7327,9 +7383,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -7551,9 +7607,9 @@ "dev": true }, "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "dependencies": { "asn1": "~0.2.3", @@ -7578,7 +7634,7 @@ "node_modules/stack-trace": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=", + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==", "dev": true, "engines": { "node": "*" @@ -7616,7 +7672,7 @@ "node_modules/stream-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", - "integrity": "sha1-gnfzy+5JpNqrz9tOL0qbXp8snwA=", + "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", "dev": true, "dependencies": { "bluebird": "^2.6.2" @@ -7865,19 +7921,6 @@ "node": ">=0.6" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -7927,21 +7970,6 @@ } } }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -8021,7 +8049,7 @@ "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "node_modules/type-check": { @@ -8083,9 +8111,9 @@ } }, "node_modules/uglify-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.0.tgz", - "integrity": "sha512-FEikl6bR30n0T3amyBh3LoiBdqHRy/f4H80+My34HOesOKyHfOsxAPAxOoqC0JUnC1amnO0IwkYC3sko51caSw==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true, "bin": { @@ -8127,6 +8155,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8150,23 +8188,17 @@ } }, "node_modules/uvm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.0.2.tgz", - "integrity": "sha512-Ra+aPiS5GXAbwXmyNExqdS42sTqmmx4XWEDF8uJlsTfOkKf9Rd9xNgav1Yckv4HfVEZg4iOFODWHFYuJ+9Fzfg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.1.1.tgz", + "integrity": "sha512-BZ5w8adTpNNr+zczOBRpaX/hH8UPKAf7fmCnidrcsqt3bn8KT9bDIfuS7hgRU9RXgiN01su2pwysBONY6w8W5w==", "dev": true, "dependencies": { - "flatted": "3.1.1" + "flatted": "3.2.6" }, "engines": { "node": ">=10" } }, - "node_modules/uvm/node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -8198,7 +8230,7 @@ "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ "node >=0.6.0" @@ -8265,9 +8297,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8276,7 +8308,7 @@ "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "node_modules/wrap-ansi": { @@ -9023,6 +9055,13 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true + }, "@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -9061,6 +9100,12 @@ "strip-json-comments": "^3.1.1" } }, + "@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", + "dev": true + }, "@humanwhocodes/config-array": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", @@ -9625,6 +9670,26 @@ "mime-types": "^2.1.12" } }, + "@postman/tough-cookie": { + "version": "4.1.3-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", + "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, "@postman/tunnel-agent": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", @@ -9898,17 +9963,6 @@ "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "@typescript-eslint/parser": { @@ -9964,17 +10018,6 @@ "is-glob": "^4.0.3", "semver": "^7.3.7", "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "@typescript-eslint/utils": { @@ -10147,9 +10190,9 @@ "dev": true }, "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "asynckit": { @@ -10171,9 +10214,9 @@ "dev": true }, "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, "babel-plugin-istanbul": { @@ -10330,9 +10373,9 @@ } }, "brotli": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.2.tgz", - "integrity": "sha512-K0HNa0RRpUpcF8yS4yNSd6vmkrvA+wRd+symIcwhfqGLAi7YgGlKfO4oDYVgiahiLGNviO9uY7Zlb1MCPeTmSA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", "dev": true, "requires": { "base64-js": "^1.1.2" @@ -10440,9 +10483,9 @@ "dev": true }, "chardet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", - "integrity": "sha512-NpwMDdSIprbYx1CLnfbxEIarI0Z+s9MssEgggMNheGM+WD68yOhV7IEA/3r6tr0yTRgQD0HuZJDw32s99i6L+A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.6.0.tgz", + "integrity": "sha512-+QOTw3otC4+FxdjK9RopGpNOglADbr4WPFi0SonkO99JbpkTPbMxmdm4NenhF5Zs+4gPXLI1+y2uazws5TMe8w==", "dev": true }, "charset": { @@ -10470,21 +10513,21 @@ "dev": true }, "cli-progress": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.10.0.tgz", - "integrity": "sha512-kLORQrhYCAtUPLZxqsAt2YJGOvRdt34+O6jl5cQGb7iF3dM55FQZlTR+rQyIK9JUcO9bBMwZsTlND+3dmFU2Cw==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", "dev": true, "requires": { - "string-width": "^4.2.0" + "string-width": "^4.2.3" } }, "cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dev": true, "requires": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "string-width": "^4.2.0" } }, @@ -10548,9 +10591,9 @@ } }, "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true }, "concat-map": { @@ -10709,6 +10752,16 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, + "des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -11041,12 +11094,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -11167,12 +11214,6 @@ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true }, - "faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", - "dev": true - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -11249,9 +11290,9 @@ "dev": true }, "filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "version": "10.0.12", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.0.12.tgz", + "integrity": "sha512-6RS9gDchbn+qWmtV2uSjo5vmKizgfCQeb5jKmqx8HyzA3MoLqqyQxN+QcjkGBJt7FjJ9qFce67Auyya5rRRbpw==", "dev": true }, "fill-range": { @@ -11321,9 +11362,9 @@ } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, "forever-agent": { @@ -11555,13 +11596,13 @@ "dev": true }, "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "requires": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" @@ -11644,19 +11685,21 @@ } }, "httpntlm": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.7.7.tgz", - "integrity": "sha512-Pv2Rvrz8H0qv1Dne5mAdZ9JegG1uc6Vu5lwLflIY6s8RKHdZQbW39L4dYswSgqMDT0pkJILUTKjeyU0VPNRZjA==", + "version": "1.8.13", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", + "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==", "dev": true, "requires": { + "des.js": "^1.0.1", "httpreq": ">=0.4.22", + "js-md4": "^0.3.2", "underscore": "~1.12.1" } }, "httpreq": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.5.2.tgz", - "integrity": "sha512-2Jm+x9WkExDOeFRrdBCBSpLPT5SokTcRHkunV3pjKmX/cx6av8zQ0WtHUMDrYb6O4hBFzNU6sxJEypvRUVYKnw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", + "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", "dev": true }, "https-proxy-agent": { @@ -11752,12 +11795,6 @@ "p-is-promise": "^3.0.0" } }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -12741,6 +12778,18 @@ } } }, + "jose": { + "version": "4.14.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", + "integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==", + "dev": true + }, + "js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true + }, "js-sha512": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", @@ -13010,6 +13059,12 @@ "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", "dev": true }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -13025,6 +13080,12 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true + }, "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -13116,51 +13177,32 @@ "dev": true }, "newman": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/newman/-/newman-5.3.2.tgz", - "integrity": "sha512-cWy8pV0iwvMOZLTw3hkAHcwo2ZA0GKkXm8oUMn1Ltii3ZI2nKpnrg9QGdIT0hGHChRkX6prY5e3Aar7uykMGNg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/newman/-/newman-6.0.0.tgz", + "integrity": "sha512-QaANQC5b6ga348MezIVRI9ZmMs+cg3MdYIp0tSEauH2tmWOAR9+cghNsFJNjU9JPui3jJp1ALC7pQq6g3Jqpxw==", "dev": true, "requires": { - "async": "3.2.3", - "chardet": "1.4.0", - "cli-progress": "3.10.0", - "cli-table3": "0.6.1", + "@postman/tough-cookie": "4.1.3-postman.1", + "async": "3.2.4", + "chardet": "1.6.0", + "cli-progress": "3.12.0", + "cli-table3": "0.6.3", "colors": "1.4.0", - "commander": "7.2.0", + "commander": "11.0.0", "csv-parse": "4.16.3", - "eventemitter3": "4.0.7", - "filesize": "8.0.7", + "filesize": "10.0.12", + "liquid-json": "0.3.1", "lodash": "4.17.21", - "mkdirp": "1.0.4", - "postman-collection": "4.1.1", - "postman-collection-transformer": "4.1.6", - "postman-request": "2.88.1-postman.31", - "postman-runtime": "7.29.0", + "mkdirp": "3.0.1", + "postman-collection": "4.2.1", + "postman-collection-transformer": "4.1.7", + "postman-request": "2.88.1-postman.33", + "postman-runtime": "7.33.0", "pretty-ms": "7.0.1", - "semver": "7.3.5", + "semver": "7.5.4", "serialised-error": "1.1.3", - "tough-cookie": "3.0.1", - "word-wrap": "1.2.3", + "word-wrap": "1.2.5", "xmlbuilder": "15.1.1" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } } }, "node-abi": { @@ -13499,21 +13541,21 @@ } }, "postman-collection": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.1.1.tgz", - "integrity": "sha512-ODpJtlf8r99DMcTU7gFmi/yvQYckFzcuE6zL/fWnyrFT34ugdCBFlX+DN7M+AnP6lmR822fv5s60H4DnL4+fAg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.2.1.tgz", + "integrity": "sha512-DFLt3/yu8+ldtOTIzmBUctoupKJBOVK4NZO0t68K2lIir9smQg7OdQTBjOXYy+PDh7u0pSDvD66tm93eBHEPHA==", "dev": true, "requires": { - "faker": "5.5.3", + "@faker-js/faker": "5.5.3", "file-type": "3.9.0", "http-reasons": "0.1.0", "iconv-lite": "0.6.3", "liquid-json": "0.3.1", "lodash": "4.17.21", "mime-format": "2.0.1", - "mime-types": "2.1.34", + "mime-types": "2.1.35", "postman-url-encoder": "3.0.5", - "semver": "7.3.5", + "semver": "7.5.4", "uuid": "8.3.2" }, "dependencies": { @@ -13526,21 +13568,6 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dev": true, - "requires": { - "mime-db": "1.51.0" - } - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -13550,15 +13577,15 @@ } }, "postman-collection-transformer": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.6.tgz", - "integrity": "sha512-xvdQb6sZoWcG9xZXUPSuxocjcd6WCZlINlGGiuHdSfxhgiwQhj9qhF0JRFbagZ8xB0+pYUairD5MiCENc6DEVA==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/postman-collection-transformer/-/postman-collection-transformer-4.1.7.tgz", + "integrity": "sha512-SxJkm/LnlFZs2splUBnS4jQFicgBptghpm4voHtNnaum3Ad64E2MHLV4fJhv58dVUmFwdSwdQUN3m2q0iLecnQ==", "dev": true, "requires": { "commander": "8.3.0", "inherits": "2.0.4", "lodash": "4.17.21", - "semver": "7.3.5", + "semver": "7.5.4", "strip-json-comments": "3.1.1" }, "dependencies": { @@ -13571,16 +13598,17 @@ } }, "postman-request": { - "version": "2.88.1-postman.31", - "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.31.tgz", - "integrity": "sha512-OJbYqP7ItxQ84yHyuNpDywCZB0HYbpHJisMQ9lb1cSL3N5H3Td6a2+3l/a74UMd3u82BiGC5yQyYmdOIETP/nQ==", + "version": "2.88.1-postman.33", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.33.tgz", + "integrity": "sha512-uL9sCML4gPH6Z4hreDWbeinKU0p0Ke261nU7OvII95NU22HN6Dk7T/SaVPaj6T4TsQqGKIFw6/woLZnH7ugFNA==", "dev": true, "requires": { "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", "@postman/tunnel-agent": "^0.6.3", "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "brotli": "~1.3.2", + "aws4": "^1.12.0", + "brotli": "^1.3.3", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", @@ -13590,14 +13618,13 @@ "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", + "mime-types": "^2.1.35", "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", - "qs": "~6.5.2", + "qs": "~6.5.3", "safe-buffer": "^5.1.2", "stream-length": "^1.0.2", - "tough-cookie": "~2.5.0", - "uuid": "^3.3.2" + "uuid": "^8.3.2" }, "dependencies": { "qs": { @@ -13607,61 +13634,65 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true } } }, "postman-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.29.0.tgz", - "integrity": "sha512-eXxHREE/fUpohkGPRgBY1YccSGx9cyW3mtGiPyIE4zD5fYzasgBHqW6kbEND3Xrd3yf/uht/YI1H8O7J1+A1+w==", + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/postman-runtime/-/postman-runtime-7.33.0.tgz", + "integrity": "sha512-cYCb+5Y12FwZU/T3gOj2SKiOz38pisVLc0tdppb+ZlG7iqn5aLgxghJwhjG62pZCV6uixKiQX1hNdLSk9a9Xtw==", "dev": true, "requires": { - "async": "3.2.3", - "aws4": "1.11.0", - "handlebars": "4.7.7", - "httpntlm": "1.7.7", + "@postman/tough-cookie": "4.1.3-postman.1", + "async": "3.2.4", + "aws4": "1.12.0", + "handlebars": "4.7.8", + "httpntlm": "1.8.13", + "jose": "4.14.4", "js-sha512": "0.8.0", "lodash": "4.17.21", - "mime-types": "2.1.34", + "mime-types": "2.1.35", "node-oauth1": "1.3.0", "performance-now": "2.1.0", - "postman-collection": "4.1.1", - "postman-request": "2.88.1-postman.31", - "postman-sandbox": "4.0.6", + "postman-collection": "4.2.0", + "postman-request": "2.88.1-postman.33", + "postman-sandbox": "4.2.7", "postman-url-encoder": "3.0.5", "serialised-error": "1.1.3", - "tough-cookie": "3.0.1", + "strip-json-comments": "3.1.1", "uuid": "8.3.2" }, "dependencies": { - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "dev": true - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "requires": { - "mime-db": "1.51.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "postman-collection": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.2.0.tgz", + "integrity": "sha512-tvOLgN1h6Kab6dt43PmBoV5kYO/YUta3x0C2QqfmbzmHZe47VTpZ/+gIkGlbNhjKNPUUub5X6ehxYKoaTYdy1w==", "dev": true, "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "@faker-js/faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mime-format": "2.0.1", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.5", + "semver": "7.5.4", + "uuid": "8.3.2" } }, "uuid": { @@ -13673,14 +13704,51 @@ } }, "postman-sandbox": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.0.6.tgz", - "integrity": "sha512-PPRanSNEE4zy3kO7CeSBHmAfJnGdD9ecHY/Mjh26CQuZZarGkNO8c0U/n+xX3+5M1BRNc82UYq6YCtdsSDqcng==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/postman-sandbox/-/postman-sandbox-4.2.7.tgz", + "integrity": "sha512-/EcCrKnb/o+9iLS4u+H76E0kBomJFjPptVjoDiq1uZ7Es/4aTv0MAX+0aoDxdDO+0h9sl8vy65uKQwyjN7AOaw==", "dev": true, "requires": { "lodash": "4.17.21", + "postman-collection": "4.2.0", "teleport-javascript": "1.0.0", - "uvm": "2.0.2" + "uvm": "2.1.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "postman-collection": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.2.0.tgz", + "integrity": "sha512-tvOLgN1h6Kab6dt43PmBoV5kYO/YUta3x0C2QqfmbzmHZe47VTpZ/+gIkGlbNhjKNPUUub5X6ehxYKoaTYdy1w==", + "dev": true, + "requires": { + "@faker-js/faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mime-format": "2.0.1", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.5", + "semver": "7.5.4", + "uuid": "8.3.2" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } } }, "postman-url-encoder": { @@ -13788,9 +13856,9 @@ } }, "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "pump": { @@ -13817,6 +13885,12 @@ "side-channel": "^1.0.4" } }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13892,6 +13966,12 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -13967,9 +14047,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -14149,9 +14229,9 @@ "dev": true }, "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -14168,7 +14248,7 @@ "stack-trace": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=", + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==", "dev": true }, "stack-utils": { @@ -14196,7 +14276,7 @@ "stream-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", - "integrity": "sha1-gnfzy+5JpNqrz9tOL0qbXp8snwA=", + "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", "dev": true, "requires": { "bluebird": "^2.6.2" @@ -14390,16 +14470,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -14420,17 +14490,6 @@ "make-error": "1.x", "semver": "^7.5.3", "yargs-parser": "^21.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "ts-node": { @@ -14481,7 +14540,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "type-check": { @@ -14521,9 +14580,9 @@ "dev": true }, "uglify-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.0.tgz", - "integrity": "sha512-FEikl6bR30n0T3amyBh3LoiBdqHRy/f4H80+My34HOesOKyHfOsxAPAxOoqC0JUnC1amnO0IwkYC3sko51caSw==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "dev": true, "optional": true }, @@ -14553,6 +14612,16 @@ "punycode": "^2.1.0" } }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -14570,20 +14639,12 @@ "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, "uvm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.0.2.tgz", - "integrity": "sha512-Ra+aPiS5GXAbwXmyNExqdS42sTqmmx4XWEDF8uJlsTfOkKf9Rd9xNgav1Yckv4HfVEZg4iOFODWHFYuJ+9Fzfg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/uvm/-/uvm-2.1.1.tgz", + "integrity": "sha512-BZ5w8adTpNNr+zczOBRpaX/hH8UPKAf7fmCnidrcsqt3bn8KT9bDIfuS7hgRU9RXgiN01su2pwysBONY6w8W5w==", "dev": true, "requires": { - "flatted": "3.1.1" - }, - "dependencies": { - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - } + "flatted": "3.2.6" } }, "v8-compile-cache-lib": { @@ -14611,7 +14672,7 @@ "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", @@ -14671,15 +14732,15 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, "wrap-ansi": { diff --git a/WebApp/package.json b/WebApp/package.json index 3537ecafa..c76941efe 100644 --- a/WebApp/package.json +++ b/WebApp/package.json @@ -33,7 +33,7 @@ "jest": "^29.0.2", "jest-websocket-mock": "^2.4.0", "mock-socket": "^9.1.5", - "newman": "^5.3.2", + "newman": "^6.0.0", "pkg": "^5.8.0", "ts-jest": "^29.0.2", "ts-node": "^10.9.1", From 88af8ff46fd4caac80b38a89bb8931118dc26fee Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:21:47 +0900 Subject: [PATCH 094/117] chore: Upgrade bokken image for windows (#977) --- .yamato/promotion.yml | 6 +++--- .yamato/upm-ci-renderstreaming-packages.yml | 4 ++-- .yamato/upm-ci-template.yml | 6 +++--- .yamato/upm-ci-webapp.yml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.yamato/promotion.yml b/.yamato/promotion.yml index d0d23da3a..70aa8a506 100644 --- a/.yamato/promotion.yml +++ b/.yamato/promotion.yml @@ -3,7 +3,7 @@ test_platforms: - name: win type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large --- @@ -33,7 +33,7 @@ promote_dry_run: name: Promote Dry Run to Production agent: type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large variables: UPMCI_PROMOTION: 1 @@ -62,7 +62,7 @@ promote: name: Promote to Production agent: type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large variables: UPMCI_PROMOTION: 1 diff --git a/.yamato/upm-ci-renderstreaming-packages.yml b/.yamato/upm-ci-renderstreaming-packages.yml index 264b24fb9..f7fbe8f7f 100644 --- a/.yamato/upm-ci-renderstreaming-packages.yml +++ b/.yamato/upm-ci-renderstreaming-packages.yml @@ -362,7 +362,7 @@ publish_dry_run_{{ packagename }}: name: Publish Dry Run {{ package_displayname }} agent: type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} @@ -389,7 +389,7 @@ publish_{{ packagename }}: name: Publish {{ package_displayname }} agent: type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} diff --git a/.yamato/upm-ci-template.yml b/.yamato/upm-ci-template.yml index 8fc8239e4..ac69dc318 100644 --- a/.yamato/upm-ci-template.yml +++ b/.yamato/upm-ci-template.yml @@ -11,7 +11,7 @@ prepack_{{ project.name }}_{{ editor.version }}: name: Pre-Pack {{ project.packagename }} {{ editor.version }} - Primed Artifacts agent: type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large commands: - pip install unity-downloader-cli --index-url {{ intra_pypi_url }} --upgrade @@ -114,7 +114,7 @@ publish_{{ project.name }}: name: Publish {{ project.packagename }} agent: type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} @@ -143,7 +143,7 @@ publish_dryrun_{{ project.name }}: name: Publish Dry Run {{ project.packagename }} agent: type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.large commands: - npm install upm-ci-utils@{{ upm.package_version }} -g --registry {{ upm.registry_url }} diff --git a/.yamato/upm-ci-webapp.yml b/.yamato/upm-ci-webapp.yml index 5e3109486..24bf1427d 100644 --- a/.yamato/upm-ci-webapp.yml +++ b/.yamato/upm-ci-webapp.yml @@ -1,7 +1,7 @@ platforms: - name: win type: Unity::VM - image: package-ci/win10:v1.21.0-1103459 + image: package-ci/win10:v4 flavor: b1.xlarge pack_command: pack_webapp.cmd test_command: test_webapp.cmd From 2b353b579efd333e255b220fbc2b5ab1f954fd19 Mon Sep 17 00:00:00 2001 From: Brian Harrison <92394761+BrianHarrisonUnity@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:25:23 -0700 Subject: [PATCH 095/117] fix: fix command line arguments incorrectly replacing ice-candidates with null values leading to a crash (#975) --- .../Runtime/Scripts/CommandLineParser.cs | 10 +++++----- .../Tests/Runtime/CommandLineParserTest.cs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/CommandLineParser.cs b/com.unity.renderstreaming/Runtime/Scripts/CommandLineParser.cs index 0d51ad9ae..a58788f56 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/CommandLineParser.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/CommandLineParser.cs @@ -46,18 +46,18 @@ internal abstract class BaseArgument : IArgument public string ArgumentName { get; } /// - /// + /// /// public bool Defined => m_defined; /// - /// + /// /// public readonly bool Required; /// - /// + /// /// public T Value => m_value; @@ -212,10 +212,10 @@ static bool TryParseStringArrayArgument(string[] arguments, string argumentName, list.Add(arguments[startIndex + 1]); i = startIndex + 2; } - if (list.Count == 0 && required) + if (list.Count == 0) { argumentValue = null; - return false; + return !required; } argumentValue = list.ToArray(); return true; diff --git a/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs b/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs index 31235000a..c1fe391ab 100644 --- a/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs +++ b/com.unity.renderstreaming/Tests/Runtime/CommandLineParserTest.cs @@ -19,6 +19,7 @@ public void NothingArgument() Assert.That(CommandLineParser.TryParse(arguments), Is.True); Assert.That((string)CommandLineParser.SignalingUrl, Is.Null); Assert.That((int?)CommandLineParser.PollingInterval, Is.Null); + Assert.That((string[])CommandLineParser.IceServerUrls, Is.Null); } [Test] From 5f331f414b77dbe2eb9f20421729d5c731d12acd Mon Sep 17 00:00:00 2001 From: Brian Harrison <92394761+BrianHarrisonUnity@users.noreply.github.com> Date: Mon, 29 Jan 2024 00:56:40 -0700 Subject: [PATCH 096/117] fix: Fix disconnecting on first time PeerConnection state changes to disconnected, and instead rely on failed. (#976) --- com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs | 2 +- .../Runtime/Scripts/SignalingManagerInternal.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs b/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs index a5291ca23..4713912d8 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/PeerConnection.cs @@ -86,7 +86,7 @@ public PeerConnection(bool polite, RTCConfiguration config, float resendInterval case RTCPeerConnectionState.Connected: OnConnectHandler?.Invoke(); break; - case RTCPeerConnectionState.Disconnected: + case RTCPeerConnectionState.Failed: OnDisconnectHandler?.Invoke(); break; } diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs index 2901c977d..db0fae9af 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManagerInternal.cs @@ -365,7 +365,11 @@ PeerConnection CreatePeerConnection(string connectionId, bool polite) _mapConnectionIdAndPeer[connectionId] = peer; peer.OnConnectHandler += () => onConnect?.Invoke(connectionId); - peer.OnDisconnectHandler += () => onDisconnect?.Invoke(connectionId); + peer.OnDisconnectHandler += () => + { + _signaling?.CloseConnection(connectionId); + onDisconnect?.Invoke(connectionId); + }; peer.OnDataChannelHandler += channel => onAddChannel?.Invoke(connectionId, channel); ; peer.OnTrackEventHandler += e => onAddTransceiver?.Invoke(connectionId, e.Transceiver); peer.SendOfferHandler += desc => _signaling?.SendOffer(connectionId, desc); From 37d49b526695de63e049818298c3f2dd57fdc4c7 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 26 Jul 2024 10:30:01 +0900 Subject: [PATCH 097/117] fix: Remove polyfill (#999) --- WebApp/client/public/bidirectional/index.html | 1 - WebApp/client/public/multiplay/index.html | 1 - WebApp/client/public/receiver/index.html | 1 - WebApp/client/public/videoplayer/index.html | 1 - 4 files changed, 4 deletions(-) diff --git a/WebApp/client/public/bidirectional/index.html b/WebApp/client/public/bidirectional/index.html index 7881ab586..84dad5e82 100644 --- a/WebApp/client/public/bidirectional/index.html +++ b/WebApp/client/public/bidirectional/index.html @@ -76,7 +76,6 @@

Remote

- diff --git a/WebApp/client/public/multiplay/index.html b/WebApp/client/public/multiplay/index.html index ff8efdc41..7cbf3d506 100644 --- a/WebApp/client/public/multiplay/index.html +++ b/WebApp/client/public/multiplay/index.html @@ -46,7 +46,6 @@

Multiplay Sample

- diff --git a/WebApp/client/public/receiver/index.html b/WebApp/client/public/receiver/index.html index d7fd0e5d7..56b281b56 100644 --- a/WebApp/client/public/receiver/index.html +++ b/WebApp/client/public/receiver/index.html @@ -46,7 +46,6 @@

Receiver Sample

- diff --git a/WebApp/client/public/videoplayer/index.html b/WebApp/client/public/videoplayer/index.html index b602c4654..a1933cb68 100644 --- a/WebApp/client/public/videoplayer/index.html +++ b/WebApp/client/public/videoplayer/index.html @@ -30,7 +30,6 @@

VideoPlayer Sample

- From ca1643f2ec1f178989e8f19878febdebe7420656 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Tue, 3 Dec 2024 14:13:31 +0900 Subject: [PATCH 098/117] doc: improve the API documentation of VideoCodecInfo --- .../Runtime/Scripts/VideoCodecInfo.cs | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs index ef07054dd..5f1d7cf9c 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoCodecInfo.cs @@ -6,7 +6,7 @@ namespace Unity.RenderStreaming { /// - /// + /// Represents information about a video codec, including its MIME type, SDP format parameters. /// [Serializable] public class VideoCodecInfo : IEquatable @@ -21,30 +21,37 @@ public class VideoCodecInfo : IEquatable readonly Dictionary m_parameters = new Dictionary(); /// - /// + /// Gets the name of the video codec. /// public string name { get { return m_MimeType.GetCodecName(); } } /// - /// + /// Gets the MIME type of the video codec. /// public string mimeType { get { return m_MimeType; } } /// - /// + /// Gets the codec implementation name. /// public string codecImplementation { get { return parameters[KeyCodecImplementation]; } } /// - /// + /// Gets the SDP format parameters line. /// public string sdpFmtpLine { get { return m_SdpFmtpLine; } } /// - /// + /// Determines whether the specified is equal to the current . /// - /// - /// + /// + /// + /// + /// + /// + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public bool Equals(VideoCodecInfo other) { if (other == null) @@ -54,30 +61,31 @@ public bool Equals(VideoCodecInfo other) } /// - /// + /// Determines whether the specified object is equal to the current . /// - /// - /// + /// The object to compare with the current . + /// True if the specified object is equal to the current ; otherwise, false. public override bool Equals(object obj) { return obj is VideoCodecInfo ? Equals((VideoCodecInfo)obj) : base.Equals(obj); } /// - /// + /// Returns a hash code for the . + /// The hash code is based on the MIME type and SDP format parameters line properties. /// - /// + /// A hash code for the current . public override int GetHashCode() { return new { mimeType, sdpFmtpLine }.GetHashCode(); } /// - /// + /// Determines whether two specified instances of are equal. /// - /// - /// - /// + /// The first to compare. + /// The second to compare. + /// True if the two instances are equal; otherwise, false. public static bool operator ==(VideoCodecInfo left, VideoCodecInfo right) { if (ReferenceEquals(left, null)) @@ -91,11 +99,11 @@ public override int GetHashCode() } /// - /// + /// Determines whether two specified instances of are not equal. /// - /// - /// - /// + /// The first to compare. + /// The second to compare. + /// True if the two instances are not equal; otherwise, false. public static bool operator !=(VideoCodecInfo left, VideoCodecInfo right) { return !(left == right); @@ -158,37 +166,37 @@ internal VideoCodecInfo(RTCRtpCodecCapability caps) } /// - /// + /// Represents the profiles for the VP9 video codec. /// public enum VP9Profile { /// - /// + /// Profile 0. /// Profile0 = 0, /// - /// + /// Profile 1. /// Profile1 = 1, /// - /// + /// Profile 2. /// Profile2 = 2, /// - /// + /// Profile 3. /// Profile3 = 3, } /// - /// + /// Represents information about the VP9 video codec. /// public class VP9CodecInfo : VideoCodecInfo { const string KeyProfileId = "profile-id"; /// - /// + /// Gets the profile of the VP9 video codec. /// public VP9Profile? profile { @@ -208,7 +216,7 @@ internal VP9CodecInfo(RTCRtpCodecCapability caps) : base(caps) } /// - /// + /// Represents the profiles for the H264 video codec. /// public enum H264Profile { @@ -235,14 +243,14 @@ public enum H264Profile } /// - /// + /// Represents information about the H264 video codec. /// public class H264CodecInfo : VideoCodecInfo { const string KeyProfileLevelId = "profile-level-id"; /// - /// + /// Gets the profile of the H264 video codec. /// public H264Profile profile { @@ -250,7 +258,7 @@ public H264Profile profile } /// - /// + /// Gets the level of the H264 video codec. /// public int level { get { return Convert.ToInt32(parameters[KeyProfileLevelId], 16) & 0xFF; } } @@ -260,33 +268,33 @@ internal H264CodecInfo(RTCRtpCodecCapability caps) : base(caps) } /// - /// + /// Represents the profiles for the AV1 video codec. /// public enum AV1Profile { /// - /// + /// Profile 0. /// Profile0 = 0, /// - /// + /// Profile 1. /// Profile1 = 1, /// - /// + /// Profile 2. /// Profile2 = 2, } /// - /// + /// Represents information about the AV1 video codec. /// public class AV1CodecInfo : VideoCodecInfo { const string KeyProfile = "profile"; /// - /// + /// Gets the profile of the AV1 video codec. /// public AV1Profile profile { From aee157eed6f908cf7a8d01dd95c7f2d99b9a9901 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Tue, 3 Dec 2024 14:40:44 +0900 Subject: [PATCH 099/117] doc: improve the API documentation of AudioCodecInfo --- .../Runtime/Scripts/AudioCodecInfo.cs | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioCodecInfo.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioCodecInfo.cs index 680b249e9..662d3b8ca 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioCodecInfo.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioCodecInfo.cs @@ -5,7 +5,7 @@ namespace Unity.RenderStreaming { /// - /// + /// Represents information about an audio codec, including its MIME type, SDP format parameters, channel count, and sample rate. /// [Serializable] public class AudioCodecInfo : IEquatable @@ -20,27 +20,27 @@ public class AudioCodecInfo : IEquatable private int m_SampleRate; /// - /// + /// Gets the name of the audio codec. /// public string name { get { return m_MimeType.GetCodecName(); } } /// - /// + /// Gets the MIME type of the audio codec. /// public string mimeType { get { return m_MimeType; } } /// - /// + /// Gets the number of audio channels. /// public int channelCount { get { return m_ChannelCount; } } /// - /// + /// Gets the sample rate of the audio. /// public int sampleRate { get { return m_SampleRate; } } /// - /// + /// Gets the SDP format parameters line. /// public string sdpFmtpLine { get { return m_SdpFmtpLine; } } @@ -50,10 +50,17 @@ static internal AudioCodecInfo Create(RTCRtpCodecCapability caps) } /// - /// + /// Determines whether the specified is equal to the current . /// - /// - /// + /// + /// + /// + /// + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. public bool Equals(AudioCodecInfo other) { if (other == null) @@ -65,30 +72,30 @@ public bool Equals(AudioCodecInfo other) } /// - /// + /// Determines whether the specified object is equal to the current . /// - /// - /// + /// The object to compare with the current . + /// true if the specified object is equal to the current ; otherwise, false. public override bool Equals(object obj) { return obj is AudioCodecInfo ? Equals((AudioCodecInfo)obj) : base.Equals(obj); } /// - /// + /// Returns a hash code for the . /// - /// + /// A hash code for the current . public override int GetHashCode() { return new { mimeType, sdpFmtpLine, channelCount, sampleRate }.GetHashCode(); } /// - /// + /// Determines whether two specified instances of are equal. /// - /// - /// - /// + /// The first to compare. + /// The second to compare. + /// true if the two instances are equal; otherwise, false. public static bool operator ==(AudioCodecInfo left, AudioCodecInfo right) { if (ReferenceEquals(left, null)) @@ -102,11 +109,11 @@ public override int GetHashCode() } /// - /// + /// Determines whether two specified instances of are not equal. /// - /// - /// - /// + /// The first to compare. + /// The second to compare. + /// true if the two instances are not equal; otherwise, false. public static bool operator !=(AudioCodecInfo left, AudioCodecInfo right) { return !(left == right); From 16ae1feb54945006f3322f9cbf88cc67153a6cf9 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Tue, 3 Dec 2024 16:07:26 +0900 Subject: [PATCH 100/117] doc: improve the API documentation of VideoStreamSender --- .../Runtime/Scripts/VideoStreamSender.cs | 98 +++++++++++++------ 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs index e9c75320c..4c7843bb0 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs @@ -86,29 +86,29 @@ public static RTCError SetBitrate(this RTCRtpSender sender, uint? minBitrate, ui } /// - /// + /// Specifies the source of the video stream. /// public enum VideoStreamSource { /// - /// + /// Use the camera as the video stream source. /// Camera = 0, /// - /// + /// Use the screen as the video stream source. /// Screen = 1, /// - /// + /// Use the web camera as the video stream source. /// WebCamera = 2, /// - /// + /// Use a texture as the video stream source. /// Texture = 3 } /// - /// + /// Component for sending video streams. /// [AddComponentMenu("Render Streaming/Video Stream Sender")] public class VideoStreamSender : StreamSenderBase @@ -171,7 +171,7 @@ public class VideoStreamSender : StreamSenderBase private VideoStreamSourceImpl m_sourceImpl = null; /// - /// + /// Gets or sets the source of the video stream. /// public VideoStreamSource source { @@ -191,7 +191,7 @@ public VideoStreamSource source } /// - /// + /// Gets or sets the camera used as the video stream source. /// public Camera sourceCamera { @@ -211,7 +211,7 @@ public Camera sourceCamera } /// - /// + /// Gets or sets the texture used as the video stream source. /// public Texture sourceTexture { @@ -251,7 +251,7 @@ public int sourceDeviceIndex } /// - /// + /// Gets the WebCamTexture used as the video stream source. /// public WebCamTexture sourceWebCamTexture { @@ -267,7 +267,7 @@ public WebCamTexture sourceWebCamTexture /// - /// + /// Gets the frame rate of the video stream. /// public float frameRate { @@ -275,7 +275,7 @@ public float frameRate } /// - /// + /// Gets the minimum bitrate of the video stream. /// public uint minBitrate { @@ -283,7 +283,7 @@ public uint minBitrate } /// - /// + /// Gets the maximum bitrate of the video stream. /// public uint maxBitrate { @@ -291,7 +291,7 @@ public uint maxBitrate } /// - /// + /// The factor of multiply with the video resolution to reduce the bandwidth. /// public float scaleResolutionDown { @@ -299,7 +299,7 @@ public float scaleResolutionDown } /// - /// + /// Gets or sets the width of the frame buffer used for streaming. /// public uint width { @@ -311,7 +311,7 @@ public uint width } /// - /// + /// Gets or sets the height of the frame buffer used for streaming. /// public uint height { @@ -323,7 +323,7 @@ public uint height } /// - /// + /// Gets the codec information for the video stream. /// public VideoCodecInfo codec { @@ -331,7 +331,7 @@ public VideoCodecInfo codec } /// - /// + /// Whether request permission to use any video input sources. /// public bool autoRequestUserAuthorization { @@ -340,9 +340,17 @@ public bool autoRequestUserAuthorization } /// - /// + /// Sets the codec for the video stream. /// - /// + /// + /// + /// x.mimeType.Contains("VP9")); + /// videoStreamSender.SetCodec(codec); + /// ]]> + /// + /// + /// The codec information to set. public void SetCodec(VideoCodecInfo codec) { if (isPlaying) @@ -364,9 +372,15 @@ public void SetCodec(VideoCodecInfo codec) } /// - /// + /// Gets the available video codecs. /// - /// + /// + /// var codecs = VideoStreamSender.GetAvailableCodecs(); + /// foreach (var codec in codecs) + /// Debug.Log(codec.name); + /// + /// + /// A list of available codecs. public static IEnumerable GetAvailableCodecs() { string[] excludeCodecMimeType = { "video/red", "video/ulpfec", "video/rtx", "video/flexfec-03" }; @@ -375,9 +389,15 @@ public static IEnumerable GetAvailableCodecs() } /// - /// + /// Sets the frame rate for the video stream. /// - /// + /// + /// + /// videoStreamSender.SetFrameRate(30.0f); + /// + /// + /// The new frame rate. Must be greater than zero. + /// Thrown when the frame rate is less than or equal to zero. public void SetFrameRate(float frameRate) { if (frameRate < 0) @@ -392,9 +412,16 @@ public void SetFrameRate(float frameRate) } /// - /// + /// Sets the bitrate range for the video stream. /// - /// + /// + /// + /// videoStreamSender.SetBitrate(1000, 2500); + /// + /// + /// The minimum bitrate in kbps. Must be greater than zero. + /// The maximum bitrate in kbps. Must be greater than or equal to the minimum bitrate. + /// Thrown when the maximum bitrate is less than the minimum bitrate. public void SetBitrate(uint minBitrate, uint maxBitrate) { if (minBitrate > maxBitrate) @@ -410,9 +437,15 @@ public void SetBitrate(uint minBitrate, uint maxBitrate) } /// - /// + /// Sets The factor of multiply with the video resolution to reduce the bandwidth. /// - /// The parameter must be greater than 1.0f. + /// + /// + /// videoStreamSender.SetScaleResolutionDown(2.0f); + /// + /// + /// The scale factor to reduce the resolution. Must be greater than 1.0f. + /// Thrown when the scale factor is less than or equal to 1.0f. public void SetScaleResolutionDown(float scaleFactor) { if (scaleFactor < 1.0f) @@ -429,9 +462,14 @@ public void SetScaleResolutionDown(float scaleFactor) } /// - /// + /// Sets the size of the frame buffer used for streaming. /// - /// + /// + /// + /// SetTextureSize(new Vector2Int(1920, 1080)); + /// + /// + /// The new size of the texture as a Vector2Int. public void SetTextureSize(Vector2Int size) { m_TextureSize = size; From 844d634cf29331cc49351ba993faf2bcacce45c4 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Tue, 3 Dec 2024 17:04:53 +0900 Subject: [PATCH 101/117] doc: improve the API documentation of VideoStreamReceiver --- .../Runtime/Scripts/VideoStreamReceiver.cs | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs index 033756cfe..bb49efa1a 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamReceiver.cs @@ -8,22 +8,22 @@ namespace Unity.RenderStreaming { /// - /// + /// Enum representing the video render mode. /// public enum VideoRenderMode { /// - /// + /// Render to a RenderTexture. /// RenderTexture, /// - /// + /// API only, no rendering. /// APIOnly, } /// - /// + /// Component for receiving video streams. /// [AddComponentMenu("Render Streaming/Video Stream Receiver")] public class VideoStreamReceiver : StreamReceiverBase @@ -33,13 +33,13 @@ public class VideoStreamReceiver : StreamReceiverBase internal const string TargetTexturePropertyName = nameof(m_TargetTexture); /// - /// + /// Delegate for updating the received texture. /// - /// + /// The received texture. public delegate void OnUpdateReceiveTextureHandler(Texture receiveTexture); /// - /// + /// Event triggered when the received texture is updated. /// public OnUpdateReceiveTextureHandler OnUpdateReceiveTexture; @@ -53,7 +53,7 @@ public class VideoStreamReceiver : StreamReceiverBase private RenderTexture m_TargetTexture; /// - /// + /// Gets the codec information for the video stream. /// public VideoCodecInfo codec { @@ -61,22 +61,22 @@ public VideoCodecInfo codec } /// - /// + /// The width of the received video stream. /// public int width => m_texture.width; /// - /// + /// The height of the received video stream. /// public int height => m_texture.height; /// - /// + /// The texture of the received video stream. /// public Texture texture => m_texture; /// - /// + /// The target RenderTexture. /// public RenderTexture targetTexture { @@ -90,9 +90,15 @@ public RenderTexture targetTexture /// - /// + /// Gets the available video codecs. /// - /// + /// + /// var codecs = VideoStreamSender.GetAvailableCodecs(); + /// foreach (var codec in codecs) + /// Debug.Log(codec.name); + /// + /// + /// A list of available codecs. public static IEnumerable GetAvailableCodecs() { string[] excludeCodecMimeType = { "video/red", "video/ulpfec", "video/rtx", "video/flexfec-03" }; @@ -101,9 +107,18 @@ public static IEnumerable GetAvailableCodecs() } /// - /// + /// Sets the codec for the video stream. /// - /// + /// + /// + /// x.mimeType.Contains("VP9")); + /// videoStreamReceiver.SetCodec(codec); + /// ]]> + /// + /// + /// The codec information to set. + /// Thrown if the transceiver is streaming or the track has ended. public void SetCodec(VideoCodecInfo codec) { m_Codec = codec; From b5542102661182e7516b40a2c5f099c23ed0a00b Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Thu, 5 Dec 2024 16:50:10 +0900 Subject: [PATCH 102/117] doc: improve the API documentation of AudioStreamSender --- .../Runtime/Scripts/AudioStreamSender.cs | 86 ++++++++++++++----- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs index 918fef410..2c311fc0b 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs @@ -9,30 +9,30 @@ namespace Unity.RenderStreaming { /// - /// + /// Specifies the source of the audio stream. /// public enum AudioStreamSource { /// - /// + /// Use the AudioListener component as the audio source. /// AudioListener = 0, /// - /// + /// Use the AudioSource component as the audio source. /// AudioSource = 1, /// - /// + /// Use the microphone as the audio source. /// Microphone = 2, /// - /// + /// Use only the API to provide audio data. /// APIOnly = 3 } /// - /// Attach AudioListerner or AudioSource + /// Component for sending audio streams. /// [AddComponentMenu("Render Streaming/Audio Stream Sender")] public class AudioStreamSender : StreamSenderBase @@ -80,7 +80,7 @@ public class AudioStreamSender : StreamSenderBase private int m_frequency = 48000; /// - /// + /// Gets or sets the source of the audio stream. /// public AudioStreamSource source { @@ -100,7 +100,7 @@ public AudioStreamSource source } /// - /// + /// Gets the codec used for the audio stream. /// public AudioCodecInfo codec { @@ -108,7 +108,7 @@ public AudioCodecInfo codec } /// - /// + /// Gets the minimum bitrate for the audio stream. /// public uint minBitrate { @@ -116,7 +116,7 @@ public uint minBitrate } /// - /// + /// Gets the maximum bitrate for the audio stream. /// public uint maxBitrate { @@ -124,7 +124,7 @@ public uint maxBitrate } /// - /// Play or not sending to remote audio in local. + /// Gets or sets whether to play the audio locally while sending it to the remote peer. /// public bool loopback { @@ -149,7 +149,7 @@ public bool loopback } /// - /// The index of Microphone.devices. + /// Gets or sets the index of the microphone device used as the audio source. /// public int sourceDeviceIndex { @@ -169,7 +169,7 @@ public int sourceDeviceIndex } /// - /// + /// Gets or sets the AudioSource component used as the audio source. /// public AudioSource audioSource { @@ -189,7 +189,7 @@ public AudioSource audioSource } /// - /// + /// Gets or sets the AudioListener component used as the audio source. /// public AudioListener audioListener { @@ -209,9 +209,15 @@ public AudioListener audioListener } /// - /// + /// Gets the available video codecs. /// - /// + /// + /// var codecs = VideoStreamSender.GetAvailableCodecs(); + /// foreach (var codec in codecs) + /// Debug.Log(codec.name); + /// + /// + /// A list of available codecs. static public IEnumerable GetAvailableCodecs() { var excludeCodecMimeType = new[] { "audio/CN", "audio/telephone-event" }; @@ -220,10 +226,16 @@ static public IEnumerable GetAvailableCodecs() } /// - /// + /// Sets the bitrate range for the audio stream. /// - /// - /// + /// + /// + /// audioStreamSender.SetBitrate(128, 256); + /// + /// + /// The minimum bitrate in kbps. Must be greater than zero. + /// The maximum bitrate in kbps. Must be greater than or equal to the minimum bitrate. + /// Thrown when the maximum bitrate is less than the minimum bitrate. public void SetBitrate(uint minBitrate, uint maxBitrate) { if (minBitrate > maxBitrate) @@ -239,9 +251,17 @@ public void SetBitrate(uint minBitrate, uint maxBitrate) } /// - /// + /// Sets the codec for the audio stream. /// - /// + /// + /// + /// x.mimeType.Contains("opus")); + /// audioStreamSender.SetCodec(codec); + /// ]]> + /// + /// + /// The codec information to set. public void SetCodec(AudioCodecInfo codec) { m_Codec = codec; @@ -300,7 +320,6 @@ internal override WaitForCreateTrack CreateTrack() return m_sourceImpl.CreateTrack(); } - AudioStreamSourceImpl CreateAudioStreamSource() { switch (m_Source) @@ -317,7 +336,6 @@ AudioStreamSourceImpl CreateAudioStreamSource() throw new InvalidOperationException(""); } - private protected override void OnEnable() { OnAudioConfigurationChanged(false); @@ -331,6 +349,28 @@ private protected override void OnDisable() base.OnDisable(); } + /// + /// Sets the audio data for the stream. + /// + /// + /// + /// (bufferSize, Allocator.Temp); + /// for (int i = 0; i < bufferSize; i++) + /// { + /// audioData[i] = Mathf.Sin(2 * Mathf.PI * frequency * i / sampleRate); + /// } + /// audioStreamSender.SetData(audioData.AsReadOnly(), 1); + /// audioData.Dispose(); + /// ]]> + /// + /// + /// The native array containing the audio data. + /// The number of audio channels. + /// Thrown when the source property is not set to AudioStreamSource.APIOnly. public void SetData(NativeArray.ReadOnly nativeArray, int channels) { if (m_Source != AudioStreamSource.APIOnly) From 4aaafed0251a47b5ccb8d089cf42686302d95180 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Thu, 5 Dec 2024 17:26:18 +0900 Subject: [PATCH 103/117] doc: improve the API documentation of AudioStreamReceiver --- .../Runtime/Scripts/AudioStreamReceiver.cs | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs index be0f45c3d..f0368181d 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs @@ -7,7 +7,7 @@ namespace Unity.RenderStreaming { /// - /// + /// AudioStreamReceiver is a component that receives audio streams and plays them through a specified AudioSource. /// [AddComponentMenu("Render Streaming/Audio Stream Receiver")] public class AudioStreamReceiver : StreamReceiverBase @@ -16,13 +16,13 @@ public class AudioStreamReceiver : StreamReceiverBase internal const string TargetAudioSourcePropertyName = nameof(m_TargetAudioSource); /// - /// + /// Delegate for handling updates to the received audio source. /// - /// + /// The updated AudioSource. public delegate void OnUpdateReceiveAudioSourceHandler(AudioSource source); /// - /// + /// Event triggered when the received audio source is updated. /// public OnUpdateReceiveAudioSourceHandler OnUpdateReceiveAudioSource; @@ -33,7 +33,7 @@ public class AudioStreamReceiver : StreamReceiverBase private AudioCodecInfo m_Codec; /// - /// + /// Gets the codec information for the audio stream. /// public AudioCodecInfo codec { @@ -41,7 +41,7 @@ public AudioCodecInfo codec } /// - /// + /// Gets or sets the target AudioSource where the received audio will be played. /// public AudioSource targetAudioSource { @@ -50,9 +50,15 @@ public AudioSource targetAudioSource } /// - /// + /// Gets the available audio codecs. /// - /// + /// + /// var codecs = AudioStreamReceiver.GetAvailableCodecs(); + /// foreach (var codec in codecs) + /// Debug.Log(codec.name); + /// + /// + /// A list of available codecs. static public IEnumerable GetAvailableCodecs() { var excludeCodecMimeType = new[] { "audio/CN", "audio/telephone-event" }; @@ -61,9 +67,18 @@ static public IEnumerable GetAvailableCodecs() } /// - /// + /// Sets the codec for the audio stream. /// - /// + /// + /// + /// x.mimeType.Contains("opus")); + /// audioStreamReceiver.SetCodec(codec); + /// ]]> + /// + /// + /// The codec information to set. + /// Thrown if the transceiver is streaming or the track has ended. public void SetCodec(AudioCodecInfo codec) { m_Codec = codec; From 01ce850c1bd1860948255372067706c3f0fe7bcf Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Thu, 5 Dec 2024 18:44:09 +0900 Subject: [PATCH 104/117] doc: improve the API documentation of InputSender --- .../Runtime/Scripts/InputSender.cs | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs index c2cdf6e64..9cb0b6bcb 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs @@ -6,7 +6,7 @@ namespace Unity.RenderStreaming { /// - /// + /// The InputSender component is responsible for sending input data over a data channel in a Unity Render Streaming context. /// [AddComponentMenu("Render Streaming/Input Sender")] public class InputSender : DataChannelBase @@ -16,9 +16,23 @@ public class InputSender : DataChannelBase private IDisposable suscriberDisposer; /// - /// + /// Sets the RTCDataChannel for the sender. /// - /// + /// + /// + /// (); + /// var channel = channels.FirstOrDefault(_ => !_.IsLocal && !_.IsConnected); + /// channel?.SetChannel(data); + /// } + /// ]]> + /// + /// + /// The connection ID. + /// The RTCDataChannel to set. public override void SetChannel(string connectionId, RTCDataChannel channel) { if (channel == null) @@ -37,19 +51,34 @@ public override void SetChannel(string connectionId, RTCDataChannel channel) } /// - /// + /// Calculates the input region based on the given texture size and region in world coordinates. /// - /// Texture Size. - /// Region of the texture in world coordinate system. + /// + /// + /// + /// + /// + /// The region of the texture in world coordinate system. + /// The size of the texture. public void CalculateInputResion(Rect region, Vector2Int size) { sender.CalculateInputRegion(region, new Rect(Vector2.zero, size)); } /// - /// + /// Enables or disables input position correction. /// - /// + /// + /// + /// + /// + /// + /// True to enable input position correction, false to disable. public void EnableInputPositionCorrection(bool enabled) { sender.EnableInputPositionCorrection = enabled; From 09bc00b76b20fa26c314a007f33d57761b8d8336 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:02:43 +0900 Subject: [PATCH 105/117] chore: remove CI tests against Unity 2020 (#1014) --- .yamato/package.metafile | 1 - 1 file changed, 1 deletion(-) diff --git a/.yamato/package.metafile b/.yamato/package.metafile index 0dc45d010..8d12e278a 100644 --- a/.yamato/package.metafile +++ b/.yamato/package.metafile @@ -5,7 +5,6 @@ intra_pypi_url: https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi packagename: com.unity.renderstreaming package_displayname: Render Streaming editors: - - version: 2020.3 - version: 2021.3 - version: 2022.3 - version: 2023.1 From 632583462d216629ba3da5991d8d1aed2b47cc8d Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 12:50:42 +0900 Subject: [PATCH 106/117] doc: improve the API documentation of InputReceiver --- .../Runtime/Scripts/InputReceiver.cs | 133 +++++++++++++----- 1 file changed, 99 insertions(+), 34 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs index ea48ee529..a95d35957 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputReceiver.cs @@ -24,12 +24,12 @@ public class InputReceiver : InputChannelReceiverBase internal const string DefaultActionMapPropertyName = nameof(m_DefaultActionMap); /// - /// + /// Event triggered when a device changes. /// public override event Action onDeviceChange; /// - /// + /// Gets or sets the input action asset associated with the player. /// public InputActionAsset actions { @@ -66,22 +66,22 @@ public InputActionAsset actions } /// - /// + /// Indicates whether the input is currently active. /// public bool inputIsActive => m_InputActive; /// - /// + /// Gets or sets the default action map. /// public InputUser user => m_InputUser; /// - /// + /// Gets or sets the action events associated with the player. /// public ReadOnlyArray devices => m_InputUser.pairedDevices; /// - /// + /// Gets or sets the current action map. /// public InputActionMap currentActionMap { @@ -95,7 +95,7 @@ public InputActionMap currentActionMap } /// - /// + /// Gets or sets the default action map. /// public string defaultActionMap { @@ -104,7 +104,7 @@ public string defaultActionMap } /// - /// + /// Gets or sets the action events associated with the player. /// public ReadOnlyArray actionEvents { @@ -121,9 +121,6 @@ public ReadOnlyArray actionEvents } } - /// - /// - /// protected virtual void OnEnable() { m_Enabled = true; @@ -135,9 +132,6 @@ protected virtual void OnEnable() ActivateInput(); } - /// - /// - /// protected virtual void OnDisable() { m_Enabled = false; @@ -149,8 +143,15 @@ protected virtual void OnDisable() } /// - /// + /// Activates input for the player. /// + /// + /// + /// + /// + /// public void ActivateInput() { m_InputActive = true; @@ -164,8 +165,15 @@ public void ActivateInput() } /// - /// + /// Deactivates input for the player. /// + /// + /// + /// + /// + /// public void DeactivateInput() { m_CurrentActionMap?.Disable(); @@ -174,9 +182,17 @@ public void DeactivateInput() } /// - /// + /// Switches the current action map to the one with the given name or ID. /// - /// + /// The name or ID of the action map to switch to. + /// + /// + /// + /// + /// + public void SwitchCurrentActionMap(string mapNameOrId) { // Must be enabled. @@ -205,17 +221,33 @@ public void SwitchCurrentActionMap(string mapNameOrId) } /// - /// + /// Performs pairing with the specified input device. /// - /// + /// + /// + /// (); + /// playerInput.PerformPairingWithDevice(device); + /// ]]> + /// + /// + /// The input device to pair with. public void PerformPairingWithDevice(InputDevice device) { m_InputUser = InputUser.PerformPairingWithDevice(device, m_InputUser); } /// - /// + /// Performs pairing with all local devices. /// + /// + /// + /// (); + /// playerInput.PerformPairingWithAllLocalDevices(); + /// ]]> + /// + /// public void PerformPairingWithAllLocalDevices() { foreach (var device in Inputs.devices.Where(_ => !_.remote)) @@ -225,9 +257,17 @@ public void PerformPairingWithAllLocalDevices() } /// - /// + /// Unpairs the input user with the given device. /// - /// + /// + /// + /// (); + /// playerInput.UnpairDevices(device); + /// ]]> + /// + /// + /// The device to unpair. public void UnpairDevices(InputDevice device) { if (!m_InputUser.valid) @@ -236,9 +276,23 @@ public void UnpairDevices(InputDevice device) } /// - /// + /// Sets the RTCDataChannel for the sender. /// - /// + /// + /// + /// (); + /// var channel = channels.FirstOrDefault(_ => !_.IsLocal && !_.IsConnected); + /// channel?.SetChannel(data); + /// } + /// ]]> + /// + /// + /// The connection ID. + /// The RTCDataChannel to set. public override void SetChannel(string connectionId, RTCDataChannel channel) { if (channel == null) @@ -257,28 +311,39 @@ public override void SetChannel(string connectionId, RTCDataChannel channel) } /// - /// + /// Calculates the input region based on the given texture size and region in world coordinates. /// - /// Texture Size. - /// Region of the texture in world coordinate system. + /// + /// + /// + /// + /// + /// The region of the texture in world coordinate system. + /// The size of the texture. public void CalculateInputRegion(Vector2Int size, Rect region) { receiver.CalculateInputRegion(new Rect(Vector2.zero, size), region); } /// - /// + /// Enables or disables input position correction. /// - /// + /// + /// + /// + /// + /// + /// True to enable input position correction, false to disable. public void SetEnableInputPositionCorrection(bool enabled) { receiver.EnableInputPositionCorrection = enabled; } - - /// - /// - /// protected virtual void OnDestroy() { Dispose(); From 4f1aaabadfa992e32e10f3819cb821ef4cabfde0 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 17:50:46 +0900 Subject: [PATCH 107/117] Update com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs index 4c7843bb0..f85d88814 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs @@ -291,7 +291,7 @@ public uint maxBitrate } /// - /// The factor of multiply with the video resolution to reduce the bandwidth. + /// The scale factor by which to reduce the video resolution to conserve bandwidth. /// public float scaleResolutionDown { From 0a4a40955360e609393830056efb22794969f6c0 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 17:50:53 +0900 Subject: [PATCH 108/117] Update com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs index f85d88814..c5b418415 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs @@ -437,7 +437,7 @@ public void SetBitrate(uint minBitrate, uint maxBitrate) } /// - /// Sets The factor of multiply with the video resolution to reduce the bandwidth. + /// Sets the scale factor by which to reduce the video resolution to conserve bandwidth. /// /// /// From d7a81ac4a4eaec8b59fe12509ceb25db3d9c3258 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 17:51:07 +0900 Subject: [PATCH 109/117] Update com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs Co-authored-by: chrstphrsxtn <74899224+chrstphrsxtn@users.noreply.github.com> --- com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs index c5b418415..9237cdd76 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/VideoStreamSender.cs @@ -444,7 +444,7 @@ public void SetBitrate(uint minBitrate, uint maxBitrate) /// videoStreamSender.SetScaleResolutionDown(2.0f); /// /// - /// The scale factor to reduce the resolution. Must be greater than 1.0f. + /// The scale factor by which to reduce the resolution. Must be greater than 1.0f. /// Thrown when the scale factor is less than or equal to 1.0f. public void SetScaleResolutionDown(float scaleFactor) { From 2e239ad87157e75a1571485857abaa118b596c08 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 16:13:23 +0900 Subject: [PATCH 110/117] doc: improve the API documentation of SignalingManager --- .../Runtime/Scripts/SignalingManager.cs | 104 +++++++++++++----- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs index b9f8a22a1..fec7a5a4a 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/SignalingManager.cs @@ -13,6 +13,12 @@ namespace Unity.RenderStreaming { + /// + /// Manages the signaling process for Unity RenderStreaming. + /// + /// + /// + /// [AddComponentMenu("Render Streaming/Signaling Manager")] public sealed class SignalingManager : MonoBehaviour { @@ -37,13 +43,13 @@ public sealed class SignalingManager : MonoBehaviour private List handlers = new List(); /// - /// + /// Indicates whether the signaling process should automatically start when the Awake method is called. /// [SerializeField, Tooltip("Automatically started when called Awake method.")] public bool runOnAwake = true; /// - /// + /// Indicates whether to evaluate command line arguments if launching runtime on the command line. /// [SerializeField, Tooltip("Evaluate commandline arguments if launching runtime on the command line.")] public bool evaluateCommandlineArguments = true; @@ -55,7 +61,7 @@ public sealed class SignalingManager : MonoBehaviour private bool m_running; /// - /// + /// Gets a value indicating whether the signaling process is running. /// public bool Running => m_running; @@ -79,10 +85,20 @@ public bool useDefaultSettings } /// - /// + /// Sets the signaling settings. /// - /// - /// + /// + /// + /// var settings = new WebSocketSignalingSettings("ws://example.com", new[] + /// { + /// new IceServer (urls: new[] {"stun:stun.l.google.com:19302"}) + /// }); + /// signalingManager.SetSignalingSettings(settings); + /// + /// + /// The signaling settings. + /// Thrown when the signaling process has already started. + /// Thrown when the settings are null. public void SetSignalingSettings(SignalingSettings settings) { if (m_running) @@ -95,18 +111,33 @@ public void SetSignalingSettings(SignalingSettings settings) } /// - /// + /// Gets the signaling settings. /// - /// + /// + /// + /// var settings = signalingManager.GetSignalingSettings(); + /// if (settings is WebSocketSignalingSettings webSocketSettings) + /// { + /// Debug.Log($"WebSocket URL: {webSocketSettings.url}"); + /// } + /// + /// + /// The signaling settings. public SignalingSettings GetSignalingSettings() { return signalingSettings; } /// - /// + /// Adds a signaling handler. /// - /// + /// + /// + /// var handler = instance.GetComponent(); + /// signalingManager.AddSignalingHandler(handler); + /// + /// + /// The signaling handler to add. public void AddSignalingHandler(SignalingHandlerBase handlerBase) { if (handlers.Contains(handlerBase)) @@ -124,9 +155,15 @@ public void AddSignalingHandler(SignalingHandlerBase handlerBase) } /// - /// + /// Removes a signaling handler. /// - /// + /// + /// + /// var handler = instance.GetComponent(); + /// signalingManager.RemoveSignalingHandler(handler); + /// + /// + /// The signaling handler to remove. public void RemoveSignalingHandler(SignalingHandlerBase handlerBase) { handlers.Remove(handlerBase); @@ -140,10 +177,15 @@ public void RemoveSignalingHandler(SignalingHandlerBase handlerBase) } /// - /// + /// Runs the signaling process. /// - /// - /// + /// The signaling instance to use. If null, a new instance will be created. + /// The signaling handlers to use. If null, the existing handlers will be used. + /// + /// + /// signalingManager.Run(); + /// + /// public void Run( ISignaling signaling = null, SignalingHandlerBase[] handlers = null) @@ -152,12 +194,21 @@ public void Run( } /// - /// + /// Runs the signaling process with the specified RTC configuration. /// - /// - /// - /// - /// To use this method, Need to depend WebRTC package + /// + /// + /// var rtcConfig = new RTCConfiguration + /// { + /// iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } } + /// }; + /// signalingManager.Run(rtcConfig); + /// + /// + /// The RTC configuration. + /// The signaling instance to use. If null, a new instance will be created. + /// The signaling handlers to use. If null, the existing handlers will be used. + /// To use this method, the WebRTC package is required. public void Run( RTCConfiguration conf, ISignaling signaling = null, @@ -178,12 +229,6 @@ bool IsValidSignalingSettingsObject(SignalingSettingsObject asset) } #endif - /// - /// - /// - /// - /// - /// private void _Run( RTCConfiguration? conf = null, ISignaling signaling = null, @@ -266,8 +311,13 @@ internal static bool EvaluateCommandlineArguments(ref SignalingSettings settings } /// - /// + /// Stops the signaling process. /// + /// + /// + /// signalingManager.Stop(); + /// + /// public void Stop() { m_instance?.Dispose(); From 7b4c9ebba3b724c3607b945997f6f251786bc03d Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 18:59:01 +0900 Subject: [PATCH 111/117] doc: add see also --- com.unity.renderstreaming/Runtime/Scripts/InputSender.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs b/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs index 9cb0b6bcb..5d1a49f29 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/InputSender.cs @@ -8,6 +8,7 @@ namespace Unity.RenderStreaming /// /// The InputSender component is responsible for sending input data over a data channel in a Unity Render Streaming context. /// + /// [AddComponentMenu("Render Streaming/Input Sender")] public class InputSender : DataChannelBase { From 866a74e33333137d7e496c2bef9075534eea38e7 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 19:11:56 +0900 Subject: [PATCH 112/117] doc: add see also --- com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs index f0368181d..2e80fd430 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamReceiver.cs @@ -9,6 +9,7 @@ namespace Unity.RenderStreaming /// /// AudioStreamReceiver is a component that receives audio streams and plays them through a specified AudioSource. /// + /// [AddComponentMenu("Render Streaming/Audio Stream Receiver")] public class AudioStreamReceiver : StreamReceiverBase { From 5ba8d712f97c214e8857984d63779f01f1e32361 Mon Sep 17 00:00:00 2001 From: Toru Nayuki Date: Fri, 6 Dec 2024 19:13:58 +0900 Subject: [PATCH 113/117] doc: add see also --- com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs index 2c311fc0b..59882ab84 100644 --- a/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs +++ b/com.unity.renderstreaming/Runtime/Scripts/AudioStreamSender.cs @@ -34,6 +34,8 @@ public enum AudioStreamSource /// /// Component for sending audio streams. /// + /// + /// [AddComponentMenu("Render Streaming/Audio Stream Sender")] public class AudioStreamSender : StreamSenderBase { From 01b9c19b81ca7380ee39c72d7fbc2c398af3ab2c Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Fri, 13 Dec 2024 23:08:53 +0900 Subject: [PATCH 114/117] deps: use com.unity.webrtc@3.0.0-pre.8 (#1026) --- com.unity.renderstreaming/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index 13a8be1e1..7b97dd2d0 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -5,7 +5,7 @@ "unity": "2020.3", "description": "This is a package for using Unity Render Streaming technology.", "dependencies": { - "com.unity.webrtc": "3.0.0-pre.7", + "com.unity.webrtc": "3.0.0-pre.8", "com.unity.inputsystem": "1.5.1", "com.unity.ugui": "1.0.0", "com.unity.modules.screencapture": "1.0.0" From 09e1ec4aebd9659f654b812c96305312f45431f9 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:18:17 +0900 Subject: [PATCH 115/117] revert: "chore: remove CI tests against Unity 2020 (#1014)" (#1027) This reverts commit 09bc00b76b20fa26c314a007f33d57761b8d8336. --- .yamato/package.metafile | 1 + 1 file changed, 1 insertion(+) diff --git a/.yamato/package.metafile b/.yamato/package.metafile index 8d12e278a..0dc45d010 100644 --- a/.yamato/package.metafile +++ b/.yamato/package.metafile @@ -5,6 +5,7 @@ intra_pypi_url: https://artifactory.prd.it.unity3d.com/artifactory/api/pypi/pypi packagename: com.unity.renderstreaming package_displayname: Render Streaming editors: + - version: 2020.3 - version: 2021.3 - version: 2022.3 - version: 2023.1 From 001942b055f7aaa9e0606cb6b9b8fa038a467007 Mon Sep 17 00:00:00 2001 From: sindharta-tanuwijaya <71803280+sindharta-tanuwijaya@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:11:35 +0900 Subject: [PATCH 116/117] release: 3.1.0-exp.9 (#1025) * update package.json * update changelog --- com.unity.renderstreaming/CHANGELOG.md | 14 ++++++++++++++ com.unity.renderstreaming/package.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/com.unity.renderstreaming/CHANGELOG.md b/com.unity.renderstreaming/CHANGELOG.md index 0c8f713df..67678c977 100644 --- a/com.unity.renderstreaming/CHANGELOG.md +++ b/com.unity.renderstreaming/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to com.unity.renderstreaming package will be documented in t The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [3.1.0-exp.9] - 2024-12-13 + +### Changed +- deps: use com.unity.webrtc@3.0.0-pre.8 +- doc: improve the API documentation of VideoCodecInfo +- doc: improve the API documentation of AudioCodecInfo +- doc: improve the API documentation of VideoStreamSender +- doc: improve the API documentation of VideoStreamReceiver +- doc: improve the API documentation of InputSender +- doc: improve the API documentation of InputReceiver +- doc: improve the API documentation of AudioStreamSender +- doc: improve the API documentation of AudioStreamReceiver +- doc: improve the API documentation of SignalingManager + ## [3.1.0-exp.8] - 2023-11-30 ### Changed diff --git a/com.unity.renderstreaming/package.json b/com.unity.renderstreaming/package.json index 7b97dd2d0..6c4a51828 100644 --- a/com.unity.renderstreaming/package.json +++ b/com.unity.renderstreaming/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.renderstreaming", "displayName": "Unity Render Streaming", - "version": "3.1.0-exp.8", + "version": "3.1.0-exp.9", "unity": "2020.3", "description": "This is a package for using Unity Render Streaming technology.", "dependencies": { From 130daa99a0cc3da6eaf60fada7db050be71b3575 Mon Sep 17 00:00:00 2001 From: "sourcegraph-commit-signing-unity[bot]" <197732416+sourcegraph-commit-signing-unity[bot]@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:27:47 +0000 Subject: [PATCH 117/117] Remove EmojiOne assets --- .../Sprites/EmojiOne Attribution.txt | 3 - .../Sprites/EmojiOne Attribution.txt.meta | 7 - .../Assets/TextMesh Pro/Sprites/EmojiOne.json | 156 ------- .../TextMesh Pro/Sprites/EmojiOne.json.meta | 8 - .../Assets/TextMesh Pro/Sprites/EmojiOne.png | Bin 73304 -> 0 bytes .../TextMesh Pro/Sprites/EmojiOne.png.meta | 431 ------------------ 6 files changed, 605 deletions(-) delete mode 100644 com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt delete mode 100644 com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt.meta delete mode 100644 com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json delete mode 100644 com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json.meta delete mode 100644 com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.png delete mode 100644 com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.png.meta diff --git a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt b/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt deleted file mode 100644 index 384180a92..000000000 --- a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt +++ /dev/null @@ -1,3 +0,0 @@ -This sample of beautiful emojis are provided by EmojiOne https://www.emojione.com/ - -Please visit their website to view the complete set of their emojis and review their licensing terms. \ No newline at end of file diff --git a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt.meta b/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt.meta deleted file mode 100644 index 0d30e6532..000000000 --- a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 381dcb09d5029d14897e55f98031fca5 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json b/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json deleted file mode 100644 index 6c4e50bc8..000000000 --- a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json +++ /dev/null @@ -1,156 +0,0 @@ -{"frames": [ - -{ - "filename": "1f60a.png", - "frame": {"x":0,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f60b.png", - "frame": {"x":128,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f60d.png", - "frame": {"x":256,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f60e.png", - "frame": {"x":384,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f600.png", - "frame": {"x":0,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f601.png", - "frame": {"x":128,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f602.png", - "frame": {"x":256,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f603.png", - "frame": {"x":384,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f604.png", - "frame": {"x":0,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f605.png", - "frame": {"x":128,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f606.png", - "frame": {"x":256,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f609.png", - "frame": {"x":384,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f618.png", - "frame": {"x":0,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f923.png", - "frame": {"x":128,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "263a.png", - "frame": {"x":256,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "2639.png", - "frame": {"x":384,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}], -"meta": { - "app": "http://www.codeandweb.com/texturepacker", - "version": "1.0", - "image": "EmojiOne.png", - "format": "RGBA8888", - "size": {"w":512,"h":512}, - "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:196a26a2e149d875b91ffc8fa3581e76:fc928c7e275404b7e0649307410475cb:424723c3774975ddb2053fd5c4b85f6e$" -} -} diff --git a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json.meta b/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json.meta deleted file mode 100644 index 762cf15c9..000000000 --- a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.json.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8f05276190cf498a8153f6cbe761d4e6 -timeCreated: 1480316860 -licenseType: Pro -TextScriptImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.png b/com.unity.template.renderstreaming-hd/Assets/TextMesh Pro/Sprites/EmojiOne.png deleted file mode 100644 index 4adb015b03a79ad884e9621af39524e78a5a8d08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73304 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 a68{gyEohdhSMK}(0000