Browse Source

添加RTC

胡佳骏 1 year ago
parent
commit
b3f670cd14
100 changed files with 9932 additions and 146 deletions
  1. 0 96
      Assets/BaseLoad/jh.baseeditor.engine/BaseEditor/Editor/LoadXR.cs
  2. 0 8
      Assets/GHZSDK.meta
  3. 5 5
      Assets/Game/PrefabTemplate/DeviceInfo.prefab
  4. 10 4
      Assets/Game/RemoteSingleton.cs
  5. 69 9
      Assets/Game/ScenesManager.cs
  6. 10 0
      Assets/Game/ShowLogin/ShowLogin.cs
  7. 3 3
      Assets/Game/ShowSupport/ShowRtc.prefab
  8. 5 0
      Assets/Game/ShowSupport/SupportControl.cs
  9. 1 1
      Assets/Game/XRToolkit/ArtShow/Scripts/Art/ArtInfoMgr.cs
  10. 1 2
      Assets/Game/XRToolkit/GameData/DataConfMgr.cs
  11. 1 2
      Assets/Game/XRToolkit/GameData/XSql.cs
  12. 0 8
      Assets/Immersal.meta
  13. 1 1
      Assets/LangChaoRTC.meta
  14. 56 0
      Assets/LangChaoRTC/AVideoSlide.cs
  15. 1 1
      Assets/LangChaoRTC/AVideoSlide.cs.meta
  16. 1 1
      Assets/LangChaoRTC/Agora-RTC-Plugin.meta
  17. 1 1
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example.meta
  18. 1 1
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput.meta
  19. 17 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.asset
  20. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.asset.meta
  21. 19 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.cs
  22. 1 1
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.cs.meta
  23. 1 1
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor.meta
  24. 214 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/CommandBuild.cs
  25. 1 1
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/CommandBuild.cs.meta
  26. 40 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/SceneWindow.cs
  27. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/SceneWindow.cs.meta
  28. 153 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Home.cs
  29. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Home.cs.meta
  30. 2417 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/HomeScene.unity
  31. 7 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/HomeScene.unity.meta
  32. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Image.meta
  33. BIN
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Image/agora2.png
  34. 152 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Image/agora2.png.meta
  35. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Prefab.meta
  36. 353 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Prefab/CasePanel.prefab
  37. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Prefab/CasePanel.prefab.meta
  38. 106 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.md
  39. 7 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.md.meta
  40. 98 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.zh.md
  41. 7 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.zh.md.meta
  42. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools.meta
  43. 41 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/Logger.cs
  44. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/Logger.cs.meta
  45. 33 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/PermissionHelper.cs
  46. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/PermissionHelper.cs.meta
  47. 36 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RequestToken.cs
  48. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RequestToken.cs.meta
  49. 253 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RingBuffer.cs
  50. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RingBuffer.cs.meta
  51. 15 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/UIElementDragger.cs
  52. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/UIElementDragger.cs.meta
  53. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK.meta
  54. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code.meta
  55. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event.meta
  56. 49 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/CloudAudioEngineEventHandler.cs
  57. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/CloudAudioEngineEventHandler.cs.meta
  58. 98 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaPlayerSourceObserver.cs
  59. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaPlayerSourceObserver.cs.meta
  60. 32 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaRecorderObserver.cs
  61. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaRecorderObserver.cs.meta
  62. 905 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/RtcEngineEventHandler.cs
  63. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/RtcEngineEventHandler.cs.meta
  64. 264 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioDeviceManager.cs
  65. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioDeviceManager.cs.meta
  66. 66 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioEncodedFrameObserver.cs
  67. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioEncodedFrameObserver.cs.meta
  68. 166 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioFrameObserver.cs
  69. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioFrameObserver.cs.meta
  70. 46 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioSpectrumObserver.cs
  71. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioSpectrumObserver.cs.meta
  72. 651 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayer.cs
  73. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayer.cs.meta
  74. 158 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCacheManager.cs
  75. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCacheManager.cs.meta
  76. 48 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCustomDataProvider.cs
  77. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCustomDataProvider.cs.meta
  78. 27 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerFrameObserver.cs
  79. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerFrameObserver.cs.meta
  80. 127 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerSourceObserver.cs
  81. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerSourceObserver.cs.meta
  82. 58 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorder.cs
  83. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorder.cs.meta
  84. 32 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorderObserver.cs
  85. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorderObserver.cs.meta
  86. 56 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMetadataObserver.cs
  87. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMetadataObserver.cs.meta
  88. 1009 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngine.cs
  89. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngine.cs.meta
  90. 1180 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngineEventHandler.cs
  91. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngineEventHandler.cs.meta
  92. 207 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/ISpatialAudio.cs
  93. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/ISpatialAudio.cs.meta
  94. 92 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoDeviceManager.cs
  95. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoDeviceManager.cs.meta
  96. 34 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoEncodedFrameObserver.cs
  97. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoEncodedFrameObserver.cs.meta
  98. 99 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoFrameObserver.cs
  99. 11 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoFrameObserver.cs.meta
  100. 8 0
      Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Impl.meta

+ 0 - 96
Assets/BaseLoad/jh.baseeditor.engine/BaseEditor/Editor/LoadXR.cs

@@ -1,96 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEditor;
-using UnityEditor.XR.Management;
-using UnityEditor.XR.Management.Metadata;
-using UnityEngine;
-using UnityEngine.SceneManagement;
-using UnityEngine.XR.Management;
-
-public class LoadXR
-{
-
-    private static string Path = "Build";           
-    /// <summary>
-    /// 打包成APK
-    /// </summary>
-    [MenuItem("Build/Build APK")]
-    public static void BuildAPK()
-    {
-        switch (GHZSDKSettings.loadSDKType)
-        {
-            case "Rhinox":
-                LoadXR.LoadOnlyRhinox();
-                break;
-            case "Phone":
-                LoadXR.LoadOnlyARCore();
-                break;
-        }
-        List<string> slist = new List<string>();
-        for (int i = 0; i < SceneManager.sceneCount; i++)
-        {
-            Scene sc = SceneManager.GetSceneAt(i);
-            if (sc.IsValid())
-            {
-                slist.Add(sc.path);
-            }
-
-        }
-        string AndroidPath = "/Android/";
-        BuildPipeline.BuildPlayer(slist.ToArray(), Path + AndroidPath + GHZSDKSettings.loadSDKType + ".apk", BuildTarget.Android, BuildOptions.None);
-    }
-
-
-    public static void LoadOnlyARCore()
-    {
-        XRGeneralSettings androidXRSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTargetGroup.Android);
-
-        if (androidXRSettings == null)
-        {
-            var assignedSettings = ScriptableObject.CreateInstance<XRManagerSettings>() as XRManagerSettings;
-            androidXRSettings.AssignedSettings = assignedSettings;
-            EditorUtility.SetDirty(androidXRSettings); // Make sure this gets picked up for serialization later.
-        }
-
-        //取消当前选择的
-        IReadOnlyList<XRLoader> list = androidXRSettings.Manager.activeLoaders;
-        int hasCount = list.Count;
-        //Debug.Log(hasCount);
-        for (int i = 0; i < hasCount; i++)
-        {
-            string nameTemp = list[0].GetType().FullName;
-            Debug.Log("disable xr plug:" + nameTemp);
-            XRPackageMetadataStore.RemoveLoader(androidXRSettings.Manager, nameTemp, BuildTargetGroup.Android);
-        }
-
-        //启用
-        string loaderTypeName = "UnityEngine.XR.ARCore.ARCoreLoader";
-        XRPackageMetadataStore.AssignLoader(androidXRSettings.Manager, loaderTypeName, BuildTargetGroup.Android);
-    }
-    public static void LoadOnlyRhinox()
-    {
-        XRGeneralSettings androidXRSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTargetGroup.Android);
-
-        if (androidXRSettings == null)
-        {
-            var assignedSettings = ScriptableObject.CreateInstance<XRManagerSettings>() as XRManagerSettings;
-            androidXRSettings.AssignedSettings = assignedSettings;
-            EditorUtility.SetDirty(androidXRSettings); // Make sure this gets picked up for serialization later.
-        }
-
-        //取消当前选择的
-        IReadOnlyList<XRLoader> list = androidXRSettings.Manager.activeLoaders;
-        int hasCount = list.Count;
-        //Debug.Log(hasCount);
-        for (int i = 0; i < hasCount; i++)
-        {
-            string nameTemp = list[0].GetType().FullName;
-            Debug.Log("disable xr plug:" + nameTemp);
-            XRPackageMetadataStore.RemoveLoader(androidXRSettings.Manager, nameTemp, BuildTargetGroup.Android);
-        }
-
-        //启用
-        string loaderTypeName = "Ximmerse.XR.XimmerseXRLoader";
-        XRPackageMetadataStore.AssignLoader(androidXRSettings.Manager, loaderTypeName, BuildTargetGroup.Android);
-    }
-}

+ 0 - 8
Assets/GHZSDK.meta

@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: cf2b21a32eb55be46af8ed10aca521d2
-folderAsset: yes
-DefaultImporter:
-  externalObjects: {}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 

+ 5 - 5
Assets/Game/PrefabTemplate/DeviceInfo.prefab

@@ -43,7 +43,7 @@ RectTransform:
   m_AnchorMin: {x: 0.5, y: 0.5}
   m_AnchorMax: {x: 0.5, y: 0.5}
   m_AnchoredPosition: {x: 117.11078, y: 6.914917}
-  m_SizeDelta: {x: 262.1949, y: 100}
+  m_SizeDelta: {x: 850, y: 184}
   m_Pivot: {x: 0.5, y: 0.5}
 --- !u!222 &4480840044778219963
 CanvasRenderer:
@@ -73,8 +73,8 @@ MonoBehaviour:
   m_OnCullStateChanged:
     m_PersistentCalls:
       m_Calls: []
-  m_Sprite: {fileID: 21300000, guid: 412b70b2eeae57b4b968da7d1125c12d, type: 3}
-  m_Type: 1
+  m_Sprite: {fileID: 21300000, guid: ab50f080319c7b54c92fcb929667de11, type: 3}
+  m_Type: 0
   m_PreserveAspect: 0
   m_FillCenter: 1
   m_FillMethod: 4
@@ -529,8 +529,8 @@ MonoBehaviour:
     m_Alignment: 3
     m_AlignByGeometry: 0
     m_RichText: 1
-    m_HorizontalOverflow: 0
-    m_VerticalOverflow: 0
+    m_HorizontalOverflow: 1
+    m_VerticalOverflow: 1
     m_LineSpacing: 1
   m_Text: "\u6D41\u91CF\u6EAF\u6E90"
 --- !u!1 &4556958041915824899

+ 10 - 4
Assets/Game/RemoteSingleton.cs

@@ -39,12 +39,18 @@ namespace SC.XR.Unity
                     window[window.Count - 1].name = Prefab[i].name;
                 }
             }
-            for (int i = 0; i < window.Count; i++)
+            SceneType stp = (SceneType)System.Enum.Parse(typeof(SceneType), this.gameObject.name.Split('(')[0]);
+            int codeint = stp.GetHashCode() / 10000;
+            if(codeint!=50)
             {
-                window[i].SetActive(false);
+                for (int i = 0; i < window.Count; i++)
+                {
+                    window[i].SetActive(false);
+                }
+                if (window.Count > 0)
+                    window[0].SetActive(true);
+
             }
-            if(window.Count>0)
-            window[0].SetActive(true);
         }
         public void gotoWindow(SceneType sc)
         {

+ 69 - 9
Assets/Game/ScenesManager.cs

@@ -13,11 +13,35 @@ public class ScenesManager
         ShowChoose = 3,
         ShowXunJian = 4,
         ShowRTC = 5,
-        ShowZhiDao = 6,
-        publicInt = 7,
-        DeviceInfo = 8,
-        Navigationing = 9,
+        ShowDevice = 6,
+        Navigationing = 8,
+        officeInt = 30,
+        RoomInt = 40,
+        publicInt = 50,
     }
+
+    public void showOffice(SceneType stype)
+    {
+        for (int i = 0; i < dlglist[mType.officeInt.GetHashCode()].Count; i++)
+        {
+            if (stype == dlglist[mType.officeInt.GetHashCode()][i]._sceneType)
+            {
+                dlglist[mType.officeInt.GetHashCode()][i].gameObject.SetActive(true);
+                MonoBehaviour alist = dlglist[mType.officeInt.GetHashCode()][i].GetComponent<MonoBehaviour>();
+                Type t = Type.GetType(alist.name);
+                System.Reflection.MethodInfo md = t.GetMethod("initShow");
+                if (md != null)
+                {
+                    md.Invoke(alist, null);
+                }
+            }
+            else
+            {
+                dlglist[mType.officeInt.GetHashCode()][i].gameObject.SetActive(false);
+            }
+        }
+    }
+
     public enum SceneType
     {
         GameStartLogo = 10001,
@@ -41,7 +65,27 @@ public class ScenesManager
         DeviceDetails = 61002, // 设备详情
         Navigationing = 80001, // 导航中
 
-        PopPublic = 70001,
+
+
+        ShowOffice = 300001,
+        CreateRoom = 300101,
+        JoinRoom = 300102,
+        UserSystem = 300103,
+        RemoteSystem = 300104,
+        OfficeWindow = 300105,
+
+        ShowRoom = 400001,
+        RoomMain = 400101,
+        RoomOtherUser = 400102,
+        RoomInfo = 400103,
+        RoomFile = 400104,
+
+
+        PopPublic = 500001,
+        PopPeerView = 500101,
+        PopUpInfo = 500101,
+        PopCall = 500102,
+
     }
     private static ScenesManager instance;
 
@@ -88,6 +132,22 @@ public class ScenesManager
         }
     }
 
+    public void initRoom()
+    {
+        Debug.Log("HJJLANGCHAORTC   " + mType.RoomInt.GetHashCode());
+        for (int i = 0; i < dlglist[mType.RoomInt.GetHashCode()].Count; i++)
+        {
+            MonoBehaviour alist = dlglist[mType.RoomInt.GetHashCode()][i].GetComponent<MonoBehaviour>();
+            Debug.Log("HJJLANGCHAORTC   " + alist.name);
+            Type t = Type.GetType(alist.name);
+            System.Reflection.MethodInfo md = t.GetMethod("initShow");
+            if (md != null)
+            {
+                md.Invoke(alist, null);
+            }
+        }
+
+    }
     public void showWindow(SceneType sceneType)
     {
         for (int i = 0; i < windows.Count; i++)
@@ -113,10 +173,10 @@ public class ScenesManager
 
     public void initPopUp()
     {
-        for (int i = 0; i < dlglist[mType.publicInt.GetHashCode()].Count; i++)
-        {
-            dlglist[mType.publicInt.GetHashCode()][i].gameObject.SetActive(false);
-        }
+        //   for (int i = 0; i < dlglist[mType.publicInt.GetHashCode()].Count; i++)
+        //  {
+        // dlglist[mType.publicInt.GetHashCode()][i].gameObject.SetActive(false);
+        //  }
 
     }
 

+ 10 - 0
Assets/Game/ShowLogin/ShowLogin.cs

@@ -5,6 +5,16 @@ using static ScenesManager;
 public class ShowLogin : RemoteSingleton<ShowLogin>
 {
 
+    public enum LoginType
+    {
+        INIT = 1,
+        DLG = 2,
+    }
+    [HideInInspector]
+    public GameObject init { get { return window[0]; } }
+    [HideInInspector]
+    public GameObject dlg { get { return window[1]; } }
+    public LoginType loginType;
     public override void initShow()
     {
         base.initShow();

+ 3 - 3
Assets/Game/ShowSupport/ShowRtc.prefab

@@ -2538,9 +2538,9 @@ MonoBehaviour:
   m_OnClick:
     m_PersistentCalls:
       m_Calls:
-      - m_Target: {fileID: 0}
-        m_TargetAssemblyTypeName: StartXunJian, Assembly-CSharp
-        m_MethodName: Next
+      - m_Target: {fileID: 3651108695874027832}
+        m_TargetAssemblyTypeName: SupportControl, Assembly-CSharp
+        m_MethodName: showRTC
         m_Mode: 1
         m_Arguments:
           m_ObjectArgument: {fileID: 0}

+ 5 - 0
Assets/Game/ShowSupport/SupportControl.cs

@@ -10,6 +10,11 @@ public class SupportControl : MonoBehaviour
         
     }
 
+    public void showRTC()
+    {
+        LangChaoRTC.Instance.showRTC(0);
+        ScenesManager.Instance.showWindow(ScenesManager.SceneType.RoomMain);
+    }
     public void Home()
     {
         ScenesManager.Instance.showWindow(ScenesManager.SceneType.ShowChoose);

+ 1 - 1
Assets/Game/XRToolkit/ArtShow/Scripts/Art/ArtInfoMgr.cs

@@ -21,7 +21,7 @@ namespace ShadowStudio.Model
     /// 通过资源类型获取对应的artinfo
     /// 创建一个美术资源:
     /// </summary>
-    public class ArtInfoMgr : Singleton<ArtInfoMgr>
+    public class ArtInfoMgr : SC.XR.Unity.Singleton<ArtInfoMgr>
     {
         private string tableName;
         private TableInterface table;

+ 1 - 2
Assets/Game/XRToolkit/GameData/DataConfMgr.cs

@@ -1,5 +1,4 @@
-using PublicTools.XMLDataBase;
-using SC.XR.Unity;
+using PublicTools.XMLDataBase;
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;

+ 1 - 2
Assets/Game/XRToolkit/GameData/XSql.cs

@@ -1,5 +1,4 @@
-using SC.XR.Unity;
-using System;
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;

+ 0 - 8
Assets/Immersal.meta

@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 672d854d6e0ed0b46ba90c6f2a6a3be2
-folderAsset: yes
-DefaultImporter:
-  externalObjects: {}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 

+ 1 - 1
Assets/AVProPlayer.meta → Assets/LangChaoRTC.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: de81eb1e1550a54418762546c932464f
+guid: 4eca6280fcb35b5458ce7f009b8ff54f
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}

+ 56 - 0
Assets/LangChaoRTC/AVideoSlide.cs

@@ -0,0 +1,56 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.EventSystems;
+using UnityEngine.UI;
+using XRTool.Util;
+
+public class AVideoSlide : PointerHandler
+{
+    public AVProVideoPlayer aVdieoPlayer;
+    public Slider slider;
+   public static bool isDown = false;
+
+    // Start is called before the first frame update
+    void Start()
+    {
+    }
+
+    // Update is called once per frame
+    void Update()
+    {
+        //if (slider && vidoPlayer && isDown == false)
+        //{
+        //    slider.value = float.Parse(vidoPlayer.frame.ToString()) / float.Parse(vidoPlayer.frameCount.ToString());
+        //}
+    }
+
+    void sliderChanged(float per)
+    {
+        if (slider && aVdieoPlayer)
+        {
+            aVdieoPlayer.SetSeek( per);
+            //float targetFrame = vidoPlayer.frameCount * per;
+            //vidoPlayer.frame = (long)targetFrame;
+        }
+    }
+
+    public override void OnPointerDown(PointerEventData eventData)
+    {
+        base.OnPointerDown(eventData);
+        aVdieoPlayer.Pause();
+        isDown = true;
+    }
+
+    public override void OnPointerUp(PointerEventData eventData)
+    {
+        base.OnPointerUp(eventData);
+        sliderChanged(slider.value);
+        TimerMgr.Instance.CreateTimer(()=> {
+            aVdieoPlayer.Play();
+        },0.2f);
+        isDown = false;
+    }
+
+    
+}

+ 1 - 1
Assets/BaseLoad/jh.baseeditor.engine/BaseEditor/Editor/LoadXR.cs.meta → Assets/LangChaoRTC/AVideoSlide.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 5481a3e5f82793346a7af630e70e6c92
+guid: 2ae75351a6c6b084d82feee8f1843e08
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 1 - 1
Assets/BaseLoad.meta → Assets/LangChaoRTC/Agora-RTC-Plugin.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 3df60f0b9340e4347b2c4775d4043d15
+guid: a2fb2627347a94307aaff8ad64afcd73
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}

+ 1 - 1
Assets/BaseLoad/jh.baseeditor.engine.meta → Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: b9e476d4a6c46b34e8520a7c9b64d5eb
+guid: 432304418feee4a6fbc9a8fea4084f9c
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}

+ 1 - 1
Assets/BaseLoad/jh.baseeditor.engine/BaseEditor.meta → Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: d6401fdcb6a443f42a0cdf6d452af9ae
+guid: 0c9562e6bbfaa40be845a370896502de
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}

+ 17 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.asset

@@ -0,0 +1,17 @@
+%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: 310468f085ef24732beac714c9bb64fd, type: 3}
+  m_Name: AppIdInput
+  m_EditorClassIdentifier: 
+  appID: 59a3e20fd9674f53a3422ff48b16db75
+  token: 
+  channelName: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.asset.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 300c6525f002a4dbaac41a5c4b054e35
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 11400000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 19 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.cs

@@ -0,0 +1,19 @@
+using UnityEngine;
+using UnityEngine.Events;
+using System.Collections;
+using System;
+using UnityEngine.Serialization;
+
+[CreateAssetMenu(menuName = "Agora/AppIdInput", fileName = "AppIdInput", order = 1)]
+[Serializable]
+public class AppIdInput : ScriptableObject
+{
+    [FormerlySerializedAs("APP_ID")] [SerializeField]
+    public string appID = "";
+
+    [FormerlySerializedAs("TOKEN")] [SerializeField]
+    public string token = "";
+
+    [FormerlySerializedAs("CHANNEL_NAME")] [SerializeField]
+    public string channelName = "YOUR_CHANNEL_NAME";
+}

+ 1 - 1
Assets/Scripts/Tool/HttpTool.cs.meta → Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/AppIdInput/AppIdInput.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: a88e2e815bdec034c8f83f05aa52199a
+guid: 310468f085ef24732beac714c9bb64fd
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 1 - 1
Assets/BaseLoad/jh.baseeditor.engine/BaseEditor/Editor.meta → Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: f83b30c71896b634f9df2b04d33a6c2e
+guid: e9abbe316aff44acd967104a06f35b46
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}

+ 214 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/CommandBuild.cs

@@ -0,0 +1,214 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEditor.Build;
+#if UNITY_2018_4_OR_NEWER
+using UnityEditor.Build.Reporting;
+#endif 
+using UnityEngine;
+
+public class CommandBuild : MonoBehaviour
+{
+
+
+    private static string[] GetAllScenes()
+    {
+        string[] scenes = new string[] {
+            "Assets/API-Example/HomeScene.unity",
+            "Assets/API-Example/Examples/Basic/JoinChannelVideo/BasicVideoCallScene.unity",
+            "Assets/API-Example/Examples/Basic/JoinChannelAudio/BasicAudioCallScene.unity",
+
+            "Assets/API-Example/Examples/Advanced/AudioMixing/AudioMixingScene.unity",
+            "Assets/API-Example/Examples/Advanced/AudioSpectrum/AudioSpectrumScene.unity",
+            "Assets/API-Example/Examples/Advanced/ChannelMediaRelay/ChannelMediaRelayScene.unity",
+            "Assets/API-Example/Examples/Advanced/ContentInspect/ContentInspectScene.unity",
+            "Assets/API-Example/Examples/Advanced/CustomCaptureAudio/CustomCaptureAudioScene.unity",
+            "Assets/API-Example/Examples/Advanced/CustomCaptureVideo/CustomCaptureVideoScene.unity",
+            "Assets/API-Example/Examples/Advanced/CustomRenderAudio/CustomRenderAudioScene.unity",
+            "Assets/API-Example/Examples/Advanced/DeviceManager/DeviceManagerScene.unity",
+            "Assets/API-Example/Examples/Advanced/DualCamera/DualCameraScene.unity",
+            "Assets/API-Example/Examples/Advanced/JoinChannelVideoToken/JoinChannelVideoTokenScene.unity",
+            "Assets/API-Example/Examples/Advanced/JoinChannelWithUserAccount/JoinChannelWithUserAccountScene.unity",
+            "Assets/API-Example/Examples/Advanced/MediaPlayer/MediaPlayerScene.unity",
+            "Assets/API-Example/Examples/Advanced/MediaRecorder/MediaRecorderScene.unity",
+            "Assets/API-Example/Examples/Advanced/Metadata/MetadataScene.unity",
+            "Assets/API-Example/Examples/Advanced/ProcessAudioRawData/ProcessAudioRawDataScene.unity",
+            "Assets/API-Example/Examples/Advanced/ProcessVideoRawData/ProcessVideoRawDataScene.unity",
+            "Assets/API-Example/Examples/Advanced/PushEncodedVideoImage/PushEncodedVideoImageScene.unity",
+            "Assets/API-Example/Examples/Advanced/RtmpStreaming/RtmpStreamingScene.unity",
+            "Assets/API-Example/Examples/Advanced/ScreenShare/ScreenShareScene.unity",
+            "Assets/API-Example/Examples/Advanced/ScreenShareWhileVideoCall/ScreenShareWhileVideoCallScene.unity",
+            "Assets/API-Example/Examples/Advanced/SetBeautyEffectOptions/SetBeautyEffectOptionsScene.unity",
+            "Assets/API-Example/Examples/Advanced/SetEncryption/SetEncryptionScene.unity",
+            "Assets/API-Example/Examples/Advanced/SetVideoEncodeConfiguration/SetVideoEncodeConfigurationScene.unity",
+            "Assets/API-Example/Examples/Advanced/SpatialAudioWithMediaPlayer/SpatialAudioWithMediaPlayerScene.unity",
+            "Assets/API-Example/Examples/Advanced/StartDirectCdnStreaming/StartDirectCdnStreamingScene.unity",
+            "Assets/API-Example/Examples/Advanced/StartLocalVideoTranscoder/StartLocalVideoTranscoderScene.unity",
+            "Assets/API-Example/Examples/Advanced/StartRhythmPlayer/StartRhythmPlayerScene.unity",
+            "Assets/API-Example/Examples/Advanced/StartRtmpStreamWithTranscoding/StartRtmpStreamWithTranscodingScene.unity",
+            "Assets/API-Example/Examples/Advanced/StreamMessage/StreamMessageScene.unity",
+            "Assets/API-Example/Examples/Advanced/TakeSnapshot/TakeSnapshotScene.unity",
+            "Assets/API-Example/Examples/Advanced/VirtualBackground/VirtualBackgroundScene.unity",
+            "Assets/API-Example/Examples/Advanced/VoiceChanger/VoiceChangerScene.unity"
+        };
+        return scenes;
+    }
+
+    [MenuItem("Build/Android")]
+    public static void BuildAndrod()
+    {
+
+        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
+        buildPlayerOptions.scenes = GetAllScenes();
+        buildPlayerOptions.locationPathName = "../Build/Android.apk";
+        buildPlayerOptions.target = BuildTarget.Android;
+        buildPlayerOptions.options = BuildOptions.None;
+
+#if UNITY_2018_4_OR_NEWER
+        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        BuildSummary summary = report.summary;
+
+        if (summary.result == BuildResult.Succeeded)
+        {
+            Debug.Log("Build Android succeeded: " + summary.totalSize + " bytes");
+        }
+
+        if (summary.result == BuildResult.Failed)
+        {
+            Debug.Log("Build Android failed");
+        }
+#else
+        string message = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        Debug.Log("Build Android: " + message);
+#endif
+    }
+
+
+    [MenuItem("Build/IPhone")]
+    public static void BuildIPhone()
+    {
+        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
+        buildPlayerOptions.scenes = GetAllScenes();
+        buildPlayerOptions.locationPathName = "../Build/IPhone";
+        buildPlayerOptions.target = BuildTarget.iOS;
+        buildPlayerOptions.options = BuildOptions.None;
+
+#if UNITY_2018_4_OR_NEWER
+        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        BuildSummary summary = report.summary;
+
+        if (summary.result == BuildResult.Succeeded)
+        {
+            Debug.Log("Build IPhone succeeded: " + summary.totalSize + " bytes");
+        }
+
+        if (summary.result == BuildResult.Failed)
+        {
+            Debug.Log("Build IPhone failed");
+        }
+#else
+        string message = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        Debug.Log("Build IPhone: " + message);
+#endif
+    }
+
+    [MenuItem("Build/Mac")]
+    public static void BuildMac()
+    {
+        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
+        buildPlayerOptions.scenes = GetAllScenes();
+        buildPlayerOptions.locationPathName = "../Build/Mac.app";
+        buildPlayerOptions.target = BuildTarget.StandaloneOSX;
+        buildPlayerOptions.options = BuildOptions.None;
+
+#if UNITY_2018_4_OR_NEWER
+        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        BuildSummary summary = report.summary;
+
+        if (summary.result == BuildResult.Succeeded)
+        {
+            Debug.Log("Build Mac succeeded: " + summary.totalSize + " bytes");
+        }
+
+        if (summary.result == BuildResult.Failed)
+        {
+            Debug.Log("Build Mac failed");
+        }
+#else
+        string message = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        Debug.Log("Build Mac: " + message);
+#endif
+    }
+
+
+    [MenuItem("Build/x86")]
+    public static void BuildWin32()
+    {
+
+        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
+        buildPlayerOptions.scenes = GetAllScenes();
+        buildPlayerOptions.locationPathName = "../Build/x86/x86.exe";
+        buildPlayerOptions.target = BuildTarget.StandaloneWindows;
+        buildPlayerOptions.options = BuildOptions.None;
+
+#if UNITY_2018_4_OR_NEWER
+        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        BuildSummary summary = report.summary;
+
+        if (summary.result == BuildResult.Succeeded)
+        {
+            Debug.Log("Build x86 succeeded: " + summary.totalSize + " bytes");
+        }
+
+        if (summary.result == BuildResult.Failed)
+        {
+            Debug.Log("Build x86 failed");
+        }
+#else
+        string message = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        Debug.Log("Build Win32: " + message);
+#endif
+
+    }
+
+    [MenuItem("Build/x86_64")]
+    public static void BuildWin64()
+    {
+        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
+        buildPlayerOptions.scenes = GetAllScenes();
+        buildPlayerOptions.locationPathName = "../Build/x86_64/x86_64.exe";
+        buildPlayerOptions.target = BuildTarget.StandaloneWindows64;
+        buildPlayerOptions.options = BuildOptions.None;
+
+#if UNITY_2018_4_OR_NEWER
+        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        BuildSummary summary = report.summary;
+
+        if (summary.result == BuildResult.Succeeded)
+        {
+            Debug.Log("Build x86_64 succeeded: " + summary.totalSize + " bytes");
+        }
+
+        if (summary.result == BuildResult.Failed)
+        {
+            Debug.Log("Build x86_64 failed");
+        }
+#else
+        string message = BuildPipeline.BuildPlayer(buildPlayerOptions);
+        Debug.Log("Build x86_64: " + message);
+#endif
+
+    }
+
+    [MenuItem("Build/All")]
+    public static void BuildAll()
+    {
+        BuildAndrod();
+        BuildIPhone();
+        BuildMac();
+        BuildWin32();
+        BuildWin64();
+    }
+
+
+}

+ 1 - 1
Assets/Scripts/Tool/MonoSingleton.cs.meta → Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/CommandBuild.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: ef8a56946b349de46a89d8261d14ad3d
+guid: eb3b50c9e751a4456a735673fa3790b4
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 40 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/SceneWindow.cs

@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+public class SceneEditorWindow : EditorWindow
+{
+    List<SceneAsset> m_SceneAssets = new List<SceneAsset>();
+
+    // Add menu item named "Example Window" to the Window menu
+    [MenuItem("Build/List Scenes", false, 1)]
+    public static void ShowWindow()
+    {
+        //Show existing window instance. If one doesn't exist, make one.
+        EditorWindow.GetWindow(typeof(SceneEditorWindow));
+    }
+
+    void OnGUI()
+    {
+        GUILayout.Space(8);
+        if (GUILayout.Button("Search and Add Scene files"))
+        {
+            List<EditorBuildSettingsScene> editorBuildSettingsScenes = new List<EditorBuildSettingsScene>();
+            foreach (var file in System.IO.Directory.EnumerateFiles(".", "*.unity", System.IO.SearchOption.AllDirectories))
+            {
+                string scenePath = file.Remove(0, 2);
+                UnityEngine.Debug.Log(scenePath);
+                editorBuildSettingsScenes.Add(new EditorBuildSettingsScene(scenePath, true));
+            }
+            EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray();
+        }
+
+        GUILayout.Space(8);
+        if (GUILayout.Button("Clear Scene files"))
+        {
+            EditorBuildSettings.scenes = null;
+        }
+    }
+
+
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Editor/SceneWindow.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2506364097bac4e859841f21a6314af2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 153 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Home.cs

@@ -0,0 +1,153 @@
+using System.Collections;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.SceneManagement;
+using Agora.Util;
+
+public class Home : MonoBehaviour
+{
+    public InputField AppIdInupt;
+    public InputField ChannelInput;
+    public InputField TokenInput;
+
+    public AppIdInput AppInputConfig;
+    public GameObject CasePanel;
+    public GameObject CaseScrollerView;
+
+    public GameObject EventSystem;
+
+    private string _playSceneName = "";
+
+
+    private string[] _baseSceneNameList = {
+        "BasicAudioCallScene",
+        "BasicVideoCallScene"
+    };
+
+    private string[] _advancedNameList = {
+        "AudioMixingScene",
+        "AudioSpectrumScene",
+        "ChannelMediaRelayScene",
+        "ContentInspectScene",
+        "CustomCaptureAudioScene",
+        "CustomCaptureVideoScene",
+        "CustomRenderAudioScene",
+        "DeviceManagerScene",
+        "DualCameraScene",
+        "JoinChannelVideoTokenScene",
+        "JoinChannelWithUserAccountScene",
+        "MediaPlayerScene",
+        "MediaPlayerWithCustomDataProviderScene",
+        "MediaRecorderScene",
+        "MetadataScene",
+        "ProcessAudioRawDataScene",
+        "ProcessVideoRawDataScene",
+        "PushEncodedVideoImageScene",
+        "ScreenShareScene",
+        "ScreenShareWhileVideoCallScene",
+        "SetBeautyEffectOptionsScene",
+        "SetEncryptionScene",
+        "SetVideoEncodeConfigurationScene",
+        "StartLocalVideoTranscoderScene",
+        "SpatialAudioWithMediaPlayerScene",
+        "StartDirectCdnStreamingScene",
+        "StartRhythmPlayerScene",
+        "StartRtmpStreamWithTranscodingScene",
+        "StreamMessageScene",
+        "TakeSnapshotScene",
+        "VirtualBackgroundScene",
+        "VoiceChangerScene"
+    };
+
+    private void Awake()
+    {
+        PermissionHelper.RequestMicrophontPermission();
+        PermissionHelper.RequestCameraPermission();
+
+        GameObject content = GameObject.Find("Content");
+        var contentRectTrans = content.GetComponent<RectTransform>();
+
+        for (int i = 0; i < _baseSceneNameList.Length; i++)
+        {
+            var go = Instantiate(CasePanel, content.transform);
+            var name = go.transform.Find("Text").gameObject.GetComponent<Text>();
+            name.text = _baseSceneNameList[i];
+            var button = go.transform.Find("Button").gameObject.GetComponent<Button>();
+            button.onClick.AddListener(OnJoinSceneClicked);
+            button.onClick.AddListener(SetScolllerActive);
+        }
+
+        for (int i = 0; i < _advancedNameList.Length; i++)
+        {
+            var go = Instantiate(CasePanel, content.transform);
+            var name = go.transform.Find("Text").gameObject.GetComponent<Text>();
+            name.text = _advancedNameList[i];
+            var button = go.transform.Find("Button").gameObject.GetComponent<Button>();
+            button.onClick.AddListener(OnJoinSceneClicked);
+            button.onClick.AddListener(SetScolllerActive);
+        }
+
+
+        if (this.AppInputConfig)
+        {
+            this.AppIdInupt.text = this.AppInputConfig.appID;
+            this.ChannelInput.text = this.AppInputConfig.channelName;
+            this.TokenInput.text = this.AppInputConfig.token;
+        }
+
+    }
+
+    // Start is called before the first frame update
+    private void Start()
+    {
+
+    }
+
+    // Update is called once per frame
+    private void Update()
+    {
+
+    }
+
+    private void OnApplicationQuit()
+    {
+        Debug.Log("OnApplicationQuit");
+    }
+
+    public void OnLeaveButtonClicked()
+    {
+        StartCoroutine(UnloadSceneAsync());
+        CaseScrollerView.SetActive(true);
+    }
+
+    public IEnumerator UnloadSceneAsync()
+    {
+        if (this._playSceneName != "")
+        {
+            AsyncOperation async = SceneManager.UnloadSceneAsync(_playSceneName);
+            yield return async;
+            EventSystem.gameObject.SetActive(true);
+        }
+    }
+
+    public void OnJoinSceneClicked()
+    {
+        this.AppInputConfig.appID = this.AppIdInupt.text;
+        this.AppInputConfig.channelName = this.ChannelInput.text;
+        this.AppInputConfig.token = this.TokenInput.text;
+
+        var button = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject;
+        var sceneName = button.transform.parent.Find("Text").gameObject.GetComponent<Text>().text;
+
+        EventSystem.gameObject.SetActive(false);
+
+        SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
+        this._playSceneName = sceneName;
+
+    }
+
+    public void SetScolllerActive()
+    {
+        CaseScrollerView.SetActive(false);
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Home.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c7b548af9d337405f889b92c979c9e36
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 2417 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/HomeScene.unity

@@ -0,0 +1,2417 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_IndirectSpecularColor: {r: 0.4465934, g: 0.49642956, b: 0.5748249, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 0
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 1
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 0
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 0}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1 &20064290
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 20064291}
+  - component: {fileID: 20064293}
+  - component: {fileID: 20064292}
+  m_Layer: 5
+  m_Name: Content
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &20064291
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 20064290}
+  m_LocalRotation: {x: 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: 237592859}
+  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.000024704623, y: 0.000009595477}
+  m_SizeDelta: {x: -3, y: 0}
+  m_Pivot: {x: 0, y: 1}
+--- !u!114 &20064292
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 20064290}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_HorizontalFit: 0
+  m_VerticalFit: 2
+--- !u!114 &20064293
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 20064290}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Padding:
+    m_Left: 0
+    m_Right: 0
+    m_Top: 0
+    m_Bottom: 0
+  m_ChildAlignment: 0
+  m_Spacing: 20
+  m_ChildForceExpandWidth: 0
+  m_ChildForceExpandHeight: 0
+  m_ChildControlWidth: 0
+  m_ChildControlHeight: 0
+  m_ChildScaleWidth: 0
+  m_ChildScaleHeight: 0
+--- !u!1 &82540983
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 82540984}
+  - component: {fileID: 82540985}
+  - component: {fileID: 82540986}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &82540984
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 82540983}
+  m_LocalRotation: {x: 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: 958552243}
+  m_RootOrder: 1
+  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.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &82540985
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 82540983}
+  m_CullTransparentMesh: 0
+--- !u!114 &82540986
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 82540983}
+  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_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: 1
+    m_MinSize: 10
+    m_MaxSize: 40
+    m_Alignment: 0
+    m_AlignByGeometry: 0
+    m_RichText: 0
+    m_HorizontalOverflow: 1
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: 
+--- !u!1 &98763800
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 98763801}
+  - component: {fileID: 98763803}
+  - component: {fileID: 98763802}
+  m_Layer: 5
+  m_Name: SDKText
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!224 &98763801
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 98763800}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 0.9999968, y: 0.9999968, z: 0.9999968}
+  m_Children: []
+  m_Father: {fileID: 1783804914}
+  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: 544.00006, y: 210.7}
+  m_SizeDelta: {x: 338, y: 30}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &98763802
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 98763800}
+  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: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  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: 1
+    m_MinSize: 10
+    m_MaxSize: 40
+    m_Alignment: 3
+    m_AlignByGeometry: 0
+    m_RichText: 1
+    m_HorizontalOverflow: 0
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: 'SDK Version: 4.0.0'
+--- !u!222 &98763803
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 98763800}
+  m_CullTransparentMesh: 0
+--- !u!1 &112708733
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 112708737}
+  - component: {fileID: 112708736}
+  - component: {fileID: 112708735}
+  - component: {fileID: 112708734}
+  - component: {fileID: 112708738}
+  m_Layer: 5
+  m_Name: Canvas
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &112708734
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 112708733}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_IgnoreReversedGraphics: 1
+  m_BlockingObjects: 0
+  m_BlockingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+--- !u!114 &112708735
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 112708733}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_UiScaleMode: 1
+  m_ReferencePixelsPerUnit: 100
+  m_ScaleFactor: 1
+  m_ReferenceResolution: {x: 1280, y: 720}
+  m_ScreenMatchMode: 0
+  m_MatchWidthOrHeight: 0.856
+  m_PhysicalUnit: 3
+  m_FallbackScreenDPI: 96
+  m_DefaultSpriteDPI: 96
+  m_DynamicPixelsPerUnit: 1
+--- !u!223 &112708736
+Canvas:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 112708733}
+  m_Enabled: 1
+  serializedVersion: 3
+  m_RenderMode: 0
+  m_Camera: {fileID: 0}
+  m_PlaneDistance: 100
+  m_PixelPerfect: 0
+  m_ReceivesEvents: 1
+  m_OverrideSorting: 0
+  m_OverridePixelPerfect: 0
+  m_SortingBucketNormalizedSize: 0
+  m_AdditionalShaderChannelsFlag: 0
+  m_SortingLayerID: 0
+  m_SortingOrder: 0
+  m_TargetDisplay: 0
+--- !u!224 &112708737
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 112708733}
+  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_Children:
+  - {fileID: 1413793155}
+  - {fileID: 1783804914}
+  m_Father: {fileID: 0}
+  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: 0, y: 0}
+  m_Pivot: {x: 0, y: 0}
+--- !u!114 &112708738
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 112708733}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: c7b548af9d337405f889b92c979c9e36, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  AppIdInupt: {fileID: 958552242}
+  ChannelInput: {fileID: 1085297498}
+  TokenInput: {fileID: 444718502}
+  AppInputConfig: {fileID: 11400000, guid: 300c6525f002a4dbaac41a5c4b054e35, type: 2}
+  CasePanel: {fileID: 1036881438794844, guid: 2496050ad79454c69b7285bad8bdc7d5, type: 3}
+  CaseScrollerView: {fileID: 1783804913}
+  EventSystem: {fileID: 260626451}
+--- !u!1 &237592858
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 237592859}
+  - component: {fileID: 237592862}
+  - component: {fileID: 237592861}
+  - component: {fileID: 237592860}
+  m_Layer: 5
+  m_Name: Viewport
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &237592859
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 237592858}
+  m_LocalRotation: {x: 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: 20064291}
+  m_Father: {fileID: 1783804914}
+  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: 0, y: 0}
+  m_Pivot: {x: 0, y: 1}
+--- !u!114 &237592860
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 237592858}
+  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_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 &237592861
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 237592858}
+  m_CullTransparentMesh: 0
+--- !u!114 &237592862
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 237592858}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_ShowMaskGraphic: 0
+--- !u!1 &260626451
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 260626454}
+  - component: {fileID: 260626453}
+  - component: {fileID: 260626452}
+  m_Layer: 0
+  m_Name: EventSystem
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &260626452
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 260626451}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_HorizontalAxis: Horizontal
+  m_VerticalAxis: Vertical
+  m_SubmitButton: Submit
+  m_CancelButton: Cancel
+  m_InputActionsPerSecond: 10
+  m_RepeatDelay: 0.5
+  m_ForceModuleActive: 0
+--- !u!114 &260626453
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 260626451}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_FirstSelected: {fileID: 0}
+  m_sendNavigationEvents: 1
+  m_DragThreshold: 5
+--- !u!4 &260626454
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 260626451}
+  m_LocalRotation: {x: 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: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &330805450
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 330805451}
+  - component: {fileID: 330805453}
+  - component: {fileID: 330805452}
+  m_Layer: 0
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &330805451
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 330805450}
+  m_LocalRotation: {x: 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: 1413793155}
+  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 &330805452
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 330805450}
+  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_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: 4
+    m_AlignByGeometry: 0
+    m_RichText: 1
+    m_HorizontalOverflow: 0
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: Return
+--- !u!222 &330805453
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 330805450}
+  m_CullTransparentMesh: 0
+--- !u!1 &369058273
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 369058277}
+  - component: {fileID: 369058276}
+  - component: {fileID: 369058275}
+  - component: {fileID: 369058274}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &369058274
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 369058273}
+  m_Enabled: 0
+--- !u!124 &369058275
+Behaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 369058273}
+  m_Enabled: 1
+--- !u!20 &369058276
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 369058273}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 2
+  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &369058277
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 369058273}
+  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_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &444718501
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 444718503}
+  - component: {fileID: 444718505}
+  - component: {fileID: 444718504}
+  - component: {fileID: 444718502}
+  m_Layer: 5
+  m_Name: TokenInputField
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &444718502
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 444718501}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    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: Highlighted
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 444718504}
+  m_TextComponent: {fileID: 1567269256}
+  m_Placeholder: {fileID: 1103881581}
+  m_ContentType: 0
+  m_InputType: 0
+  m_AsteriskChar: 42
+  m_KeyboardType: 0
+  m_LineType: 0
+  m_HideMobileInput: 0
+  m_CharacterValidation: 0
+  m_CharacterLimit: 0
+  m_OnEndEdit:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnValueChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_CustomCaretColor: 0
+  m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412}
+  m_Text: 
+  m_CaretBlinkRate: 0.85
+  m_CaretWidth: 1
+  m_ReadOnly: 0
+  m_ShouldActivateOnSelect: 1
+--- !u!224 &444718503
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 444718501}
+  m_LocalRotation: {x: -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: 1103881583}
+  - {fileID: 1567269254}
+  m_Father: {fileID: 1783804914}
+  m_RootOrder: 8
+  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: 543, y: -193.90002}
+  m_SizeDelta: {x: 340, y: 55.7}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &444718504
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 444718501}
+  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_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 10911, 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 &444718505
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 444718501}
+  m_CullTransparentMesh: 0
+--- !u!1 &645418226
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 645418227}
+  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 &645418227
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 645418226}
+  m_LocalRotation: {x: 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: 2042549588}
+  m_Father: {fileID: 1409839258}
+  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 &922351949
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 922351950}
+  - component: {fileID: 922351952}
+  - component: {fileID: 922351951}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &922351950
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 922351949}
+  m_LocalRotation: {x: -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: 1783804914}
+  m_RootOrder: 3
+  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: 455, y: 146.00002}
+  m_SizeDelta: {x: 160, y: 30}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &922351951
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 922351949}
+  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: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  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: 1
+    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: 'APP ID:'
+--- !u!222 &922351952
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 922351949}
+  m_CullTransparentMesh: 0
+--- !u!1 &958552241
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 958552243}
+  - component: {fileID: 958552245}
+  - component: {fileID: 958552244}
+  - component: {fileID: 958552242}
+  m_Layer: 5
+  m_Name: AppIdInputField
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &958552242
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 958552241}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    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: Highlighted
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 958552244}
+  m_TextComponent: {fileID: 82540986}
+  m_Placeholder: {fileID: 1414058528}
+  m_ContentType: 0
+  m_InputType: 0
+  m_AsteriskChar: 42
+  m_KeyboardType: 0
+  m_LineType: 0
+  m_HideMobileInput: 0
+  m_CharacterValidation: 0
+  m_CharacterLimit: 0
+  m_OnEndEdit:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnValueChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_CustomCaretColor: 0
+  m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412}
+  m_Text: 
+  m_CaretBlinkRate: 0.85
+  m_CaretWidth: 1
+  m_ReadOnly: 0
+  m_ShouldActivateOnSelect: 1
+--- !u!224 &958552243
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 958552241}
+  m_LocalRotation: {x: -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: 1414058530}
+  - {fileID: 82540984}
+  m_Father: {fileID: 1783804914}
+  m_RootOrder: 4
+  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: 543, y: 93.1}
+  m_SizeDelta: {x: 340, y: 55.7}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &958552244
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 958552241}
+  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_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 10911, 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 &958552245
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 958552241}
+  m_CullTransparentMesh: 0
+--- !u!1 &1085297497
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1085297499}
+  - component: {fileID: 1085297501}
+  - component: {fileID: 1085297500}
+  - component: {fileID: 1085297498}
+  m_Layer: 5
+  m_Name: ChannelInputField
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1085297498
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085297497}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    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: Highlighted
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 1085297500}
+  m_TextComponent: {fileID: 1222550865}
+  m_Placeholder: {fileID: 1453378164}
+  m_ContentType: 0
+  m_InputType: 0
+  m_AsteriskChar: 42
+  m_KeyboardType: 0
+  m_LineType: 0
+  m_HideMobileInput: 0
+  m_CharacterValidation: 0
+  m_CharacterLimit: 0
+  m_OnEndEdit:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnValueChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_CustomCaretColor: 0
+  m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412}
+  m_Text: Unity_Channel
+  m_CaretBlinkRate: 0.85
+  m_CaretWidth: 1
+  m_ReadOnly: 0
+  m_ShouldActivateOnSelect: 1
+--- !u!224 &1085297499
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085297497}
+  m_LocalRotation: {x: -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: 1453378166}
+  - {fileID: 1222550863}
+  m_Father: {fileID: 1783804914}
+  m_RootOrder: 6
+  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: 543, y: -50.899998}
+  m_SizeDelta: {x: 340, y: 55.7}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1085297500
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085297497}
+  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_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 10911, 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 &1085297501
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085297497}
+  m_CullTransparentMesh: 0
+--- !u!1 &1103881580
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1103881583}
+  - component: {fileID: 1103881582}
+  - component: {fileID: 1103881581}
+  m_Layer: 5
+  m_Name: Placeholder
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1103881581
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1103881580}
+  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: 0.5}
+  m_RaycastTarget: 1
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_FontData:
+    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+    m_FontSize: 14
+    m_FontStyle: 2
+    m_BestFit: 1
+    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: Enter text...
+--- !u!222 &1103881582
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1103881580}
+  m_CullTransparentMesh: 0
+--- !u!224 &1103881583
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1103881580}
+  m_LocalRotation: {x: 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: 444718503}
+  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.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &1222550862
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1222550863}
+  - component: {fileID: 1222550864}
+  - component: {fileID: 1222550865}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1222550863
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1222550862}
+  m_LocalRotation: {x: 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: 1085297499}
+  m_RootOrder: 1
+  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.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &1222550864
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1222550862}
+  m_CullTransparentMesh: 0
+--- !u!114 &1222550865
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1222550862}
+  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_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: 1
+    m_MinSize: 10
+    m_MaxSize: 40
+    m_Alignment: 0
+    m_AlignByGeometry: 0
+    m_RichText: 0
+    m_HorizontalOverflow: 1
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: Unity_Channel
+--- !u!1 &1409839257
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1409839258}
+  - component: {fileID: 1409839261}
+  - component: {fileID: 1409839260}
+  - component: {fileID: 1409839259}
+  m_Layer: 5
+  m_Name: Scrollbar Vertical
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1409839258
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1409839257}
+  m_LocalRotation: {x: 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: 645418227}
+  m_Father: {fileID: 1783804914}
+  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 &1409839259
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1409839257}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    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: Highlighted
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 2042549589}
+  m_HandleRect: {fileID: 2042549588}
+  m_Direction: 2
+  m_Value: 1
+  m_Size: 1
+  m_NumberOfSteps: 0
+  m_OnValueChanged:
+    m_PersistentCalls:
+      m_Calls: []
+--- !u!114 &1409839260
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1409839257}
+  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_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 &1409839261
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1409839257}
+  m_CullTransparentMesh: 0
+--- !u!1 &1413793154
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1413793155}
+  - component: {fileID: 1413793158}
+  - component: {fileID: 1413793157}
+  - component: {fileID: 1413793156}
+  m_Layer: 0
+  m_Name: Button
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1413793155
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1413793154}
+  m_LocalRotation: {x: -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: 330805451}
+  m_Father: {fileID: 112708737}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 1, y: 1}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: -67, y: -63}
+  m_SizeDelta: {x: 77.29999, y: 68.79999}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1413793156
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1413793154}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    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: Highlighted
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 1413793157}
+  m_OnClick:
+    m_PersistentCalls:
+      m_Calls:
+      - m_Target: {fileID: 112708738}
+        m_MethodName: OnLeaveButtonClicked
+        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
+--- !u!114 &1413793157
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1413793154}
+  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_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 &1413793158
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1413793154}
+  m_CullTransparentMesh: 0
+--- !u!1 &1414058527
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1414058530}
+  - component: {fileID: 1414058529}
+  - component: {fileID: 1414058528}
+  m_Layer: 5
+  m_Name: Placeholder
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1414058528
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1414058527}
+  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: 0.5}
+  m_RaycastTarget: 1
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_FontData:
+    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+    m_FontSize: 14
+    m_FontStyle: 2
+    m_BestFit: 1
+    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: Enter text...
+--- !u!222 &1414058529
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1414058527}
+  m_CullTransparentMesh: 0
+--- !u!224 &1414058530
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1414058527}
+  m_LocalRotation: {x: 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: 958552243}
+  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.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &1453378163
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1453378166}
+  - component: {fileID: 1453378165}
+  - component: {fileID: 1453378164}
+  m_Layer: 5
+  m_Name: Placeholder
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1453378164
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1453378163}
+  m_Enabled: 0
+  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: 0.5}
+  m_RaycastTarget: 1
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_FontData:
+    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+    m_FontSize: 14
+    m_FontStyle: 2
+    m_BestFit: 1
+    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: Enter text...
+--- !u!222 &1453378165
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1453378163}
+  m_CullTransparentMesh: 0
+--- !u!224 &1453378166
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1453378163}
+  m_LocalRotation: {x: 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: 1085297499}
+  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.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &1567269253
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1567269254}
+  - component: {fileID: 1567269255}
+  - component: {fileID: 1567269256}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1567269254
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1567269253}
+  m_LocalRotation: {x: 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: 444718503}
+  m_RootOrder: 1
+  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.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &1567269255
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1567269253}
+  m_CullTransparentMesh: 0
+--- !u!114 &1567269256
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1567269253}
+  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_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: 1
+    m_MinSize: 10
+    m_MaxSize: 40
+    m_Alignment: 0
+    m_AlignByGeometry: 0
+    m_RichText: 0
+    m_HorizontalOverflow: 1
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: 
+--- !u!1 &1638969926
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1638969928}
+  - component: {fileID: 1638969927}
+  m_Layer: 0
+  m_Name: Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!108 &1638969927
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1638969926}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
+  m_Intensity: 1
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.802082
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 2
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &1638969928
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1638969926}
+  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_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1 &1770001163
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1770001164}
+  - component: {fileID: 1770001166}
+  - component: {fileID: 1770001165}
+  m_Layer: 5
+  m_Name: Text (1)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1770001164
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1770001163}
+  m_LocalRotation: {x: -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: 1783804914}
+  m_RootOrder: 5
+  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: 455, y: 2}
+  m_SizeDelta: {x: 160, y: 30}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1770001165
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1770001163}
+  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: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  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: 1
+    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: Channel
+--- !u!222 &1770001166
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1770001163}
+  m_CullTransparentMesh: 0
+--- !u!1 &1783804913
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1783804914}
+  - component: {fileID: 1783804917}
+  - component: {fileID: 1783804916}
+  - component: {fileID: 1783804915}
+  m_Layer: 5
+  m_Name: Scroll View
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1783804914
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1783804913}
+  m_LocalRotation: {x: 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: 237592859}
+  - {fileID: 1409839258}
+  - {fileID: 98763801}
+  - {fileID: 922351950}
+  - {fileID: 958552243}
+  - {fileID: 1770001164}
+  - {fileID: 1085297499}
+  - {fileID: 2068508248}
+  - {fileID: 444718503}
+  m_Father: {fileID: 112708737}
+  m_RootOrder: 1
+  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: -223, y: 0}
+  m_SizeDelta: {x: 600, y: 700}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1783804915
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1783804913}
+  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: 0.392}
+  m_RaycastTarget: 1
+  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 &1783804916
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1783804913}
+  m_CullTransparentMesh: 0
+--- !u!114 &1783804917
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1783804913}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Content: {fileID: 20064291}
+  m_Horizontal: 0
+  m_Vertical: 1
+  m_MovementType: 1
+  m_Elasticity: 0.1
+  m_Inertia: 1
+  m_DecelerationRate: 0.135
+  m_ScrollSensitivity: 1
+  m_Viewport: {fileID: 237592859}
+  m_HorizontalScrollbar: {fileID: 0}
+  m_VerticalScrollbar: {fileID: 1409839259}
+  m_HorizontalScrollbarVisibility: 2
+  m_VerticalScrollbarVisibility: 2
+  m_HorizontalScrollbarSpacing: -3
+  m_VerticalScrollbarSpacing: 0
+  m_OnValueChanged:
+    m_PersistentCalls:
+      m_Calls: []
+--- !u!1 &2042549587
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2042549588}
+  - component: {fileID: 2042549590}
+  - component: {fileID: 2042549589}
+  m_Layer: 5
+  m_Name: Handle
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &2042549588
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2042549587}
+  m_LocalRotation: {x: 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: 645418227}
+  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: 20, y: 20}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &2042549589
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2042549587}
+  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_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 &2042549590
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2042549587}
+  m_CullTransparentMesh: 0
+--- !u!1 &2068508247
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2068508248}
+  - component: {fileID: 2068508250}
+  - component: {fileID: 2068508249}
+  m_Layer: 5
+  m_Name: Text (2)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &2068508248
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2068508247}
+  m_LocalRotation: {x: -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: 1783804914}
+  m_RootOrder: 7
+  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: 455, y: -141}
+  m_SizeDelta: {x: 160, y: 30}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &2068508249
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2068508247}
+  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: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  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: 1
+    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: 'Token:'
+--- !u!222 &2068508250
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2068508247}
+  m_CullTransparentMesh: 0

+ 7 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/HomeScene.unity.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: afbde366e660d4272b8d45e2d7d96f50
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Image.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ac7042f5981d94cf080e56c000f43214
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

BIN
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Image/agora2.png


+ 152 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Image/agora2.png.meta

@@ -0,0 +1,152 @@
+fileFormatVersion: 2
+guid: 07f7a6254fefb42078aed732c77c3cda
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    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
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 0
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 1
+  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: 1
+  spriteTessellationDetail: -1
+  textureType: 8
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 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: iPhone
+    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: Lumin
+    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
+  - serializedVersion: 3
+    buildTarget: WebGL
+    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: 5e97eb03825dee720800000000000000
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Prefab.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 902c5c991ba754544926a9139cca6543
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 353 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Prefab/CasePanel.prefab

@@ -0,0 +1,353 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1036881438794844
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 224345646677206142}
+  - component: {fileID: 222977441614407690}
+  - component: {fileID: 114185648007682220}
+  m_Layer: 5
+  m_Name: CasePanel
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &224345646677206142
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1036881438794844}
+  m_LocalRotation: {x: -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: 224308374033113526}
+  - {fileID: 224938511741827896}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 1}
+  m_AnchorMax: {x: 0, y: 1}
+  m_AnchoredPosition: {x: 623, y: -93}
+  m_SizeDelta: {x: 580, y: 80}
+  m_Pivot: {x: 0, y: 1}
+--- !u!222 &222977441614407690
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1036881438794844}
+  m_CullTransparentMesh: 0
+--- !u!114 &114185648007682220
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1036881438794844}
+  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: 0.712}
+  m_RaycastTarget: 1
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 21300000, guid: 07f7a6254fefb42078aed732c77c3cda, type: 3}
+  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!1 &1130559936070958
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 224918487906776580}
+  - component: {fileID: 222387459500286586}
+  - component: {fileID: 114531629486495798}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &224918487906776580
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1130559936070958}
+  m_LocalRotation: {x: 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: 224308374033113526}
+  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!222 &222387459500286586
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1130559936070958}
+  m_CullTransparentMesh: 0
+--- !u!114 &114531629486495798
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1130559936070958}
+  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_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: 4
+    m_AlignByGeometry: 0
+    m_RichText: 1
+    m_HorizontalOverflow: 0
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: Jump
+--- !u!1 &1610732099111804
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 224938511741827896}
+  - component: {fileID: 222656902020873038}
+  - component: {fileID: 114064395545068140}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &224938511741827896
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1610732099111804}
+  m_LocalRotation: {x: 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: 224345646677206142}
+  m_RootOrder: 1
+  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: 300, y: 31.1}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &222656902020873038
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1610732099111804}
+  m_CullTransparentMesh: 0
+--- !u!114 &114064395545068140
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1610732099111804}
+  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_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: 1
+    m_MinSize: 2
+    m_MaxSize: 40
+    m_Alignment: 4
+    m_AlignByGeometry: 0
+    m_RichText: 1
+    m_HorizontalOverflow: 0
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: BasicVideoCall
+--- !u!1 &1966949020592634
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 224308374033113526}
+  - component: {fileID: 222780278525291890}
+  - component: {fileID: 114681506339907642}
+  - component: {fileID: 114300398960532148}
+  m_Layer: 5
+  m_Name: Button
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &224308374033113526
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1966949020592634}
+  m_LocalRotation: {x: -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: 224918487906776580}
+  m_Father: {fileID: 224345646677206142}
+  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: 222, y: 0}
+  m_SizeDelta: {x: 74.8, y: 62.2}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &222780278525291890
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1966949020592634}
+  m_CullTransparentMesh: 0
+--- !u!114 &114681506339907642
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1966949020592634}
+  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_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!114 &114300398960532148
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1966949020592634}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    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: Highlighted
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 114681506339907642}
+  m_OnClick:
+    m_PersistentCalls:
+      m_Calls: []

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Prefab/CasePanel.prefab.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2496050ad79454c69b7285bad8bdc7d5
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 100100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 106 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.md

@@ -0,0 +1,106 @@
+# API-Example-Unity
+
+*__其他语言版本:__  [__简体中文__](README.zh.md)*
+
+## Overview
+
+The API-Example-Unity project is an open-source demo that will show you different scenes on how to integrate Agora SDK APIs into your project.
+
+Any scene of this project can run successfully alone.
+
+## Project structure
+
+* **Basic demos:**
+
+| Demo                                                         | Description                                        | APIs                                                         |
+| ------------------------------------------------------------ | -------------------------------------------------- | ------------------------------------------------------------ |
+| [JoinChannelAudio](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Basic/JoinChannelAudio) | basic demo to show audio call                      | CreateAgoraRtcEngine, JoinChannel, LeaveChannel              |
+| [JoinChannelVideo](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Basic/JoinChannelVideo) | video demo with role selection in Editor Inspector | SetChannelProfile,SetClientRole,EnableVideo,EnableVideo, JoinChannel, VideoSurface |
+
+* **Advanced demos:**
+
+| Demo                                                         | Description                                                  | APIs                                                         |
+| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [AudioMixing](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/AudioMixing) | audioMixing and play audio effect in the channel             | StartAudioMixing, PlayEffect                                 |
+| [AudioSpectrum](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/AudioSpectrum) | audio spectrum             | IAudioSpectrumObserver, RegisterMediaPlayerAudioSpectrumObserver                                 |
+| [ChannelMediaRelay](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ChannelMediaRelay) | start cross channel media streaming forwarding. This method can be used to realize cross channel wheat connection and other scenes. | StartChannelMediaRelay, UpdateChannelMediaRelay, PauseAllChannelMediaRelay, ResumeAllChannelMediaRelay, StopChannelMediaRelay |
+| [ContentInspect](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ContentInspect) | content inspect | SetContentInspect |
+| [CustomCaptureAudio](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/CustomCaptureAudio) | Sending raw data from AudioSource into the Agora channel     | PushAudioFrame                                               |
+| [CustomCaptureVideo](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/CustomCaptureVideo) | Sending raw data from VideoSource into the Agora channel     | PushVideoFrame                                               |
+| [CustomRenderAudio](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/CustomRenderAudio) | use AudioSource to play raw data received in the Agora channel | PullAudioFrame                                               |
+| [DeviceManager](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/DeviceManager) | show how to get and set Device on the desktop platforms      | GetAudioDeviceManager, GetVideoDeviceManager                 |
+| [DualCamera](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/DualCamera) | show how to use dual camera to capture data                  | StartPrimaryCameraCapture, StartSecondaryCameraCapture       |
+| [JoinChannelVideoToken](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/JoinChannelVideoToken) | demo on how to run Agora app with a token                    | RenewToken                                                   |
+| [JoinChannelWithUserAccount](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/JoinChannelWithUserAccount) | demo on how to join channel with user account                | JoinChannelWithUserAccount,   GetUserInfoByUserAccount       |
+| [MediaPlayer](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/MediaPlayer) | how to  play media                                           | CreateMediaPlayer,  Play, Stop                               |
+| [MediaRecorder](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/MediaRecorder) | Media recording                  | StartRecording,  StopRecording,                                                |
+| [Metadata](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/Metadata) | send meta data                  | IMetadataObserver                                                |
+| [ProcessAudioRawData](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ProcessAudioRawData) | process raw Audio data in rtc engine and play with audioclip                       | RegisterAudioFrameObserver, OnPlaybackAudioFrame, OnRceordAudioFrame |
+| [ProcessVideoRawData](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ProcessVideoRawData) | process raw Video data in rtc engine and render with texture                       | RegisterVideoFrameObserver, OnCaptureVideoFrame, OnRenderVideoFrame |
+| [PushEncodedVideoImage](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/PushEncodedVideoImage) | push encoded data in rtc engine                              | PushEncodedVideoImage                                        |
+| [ScreenShare](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ScreenShare) | sharing screen view with rtc engine                          | StartScreenCaptureByWindowId, StartScreenCaptureByDisplayId  |
+| [ScreenShareWhileVideoCall](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ScreenShareWhileVideoCall) | sharing screen view while with a video call                  | StartScreenCaptureByWindowId, StartScreenCaptureByDisplayId  |
+| [SetBeautyEffectOptions](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SetBeautyEffectOptions) | Turn on beauty during video call                             | SetBeautyEffectOptions                                       |
+| [SetEncryption](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SetEncryption) | sending video with encryption                                | EnableEncryption                                             |
+| [SetVideoEncoderConfiguration](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SetVideoEncoderConfiguration) | video demo with multiple encoding dimension choice           | SetVideoEncoderConfiguration                                 |
+| [SpatialAudioWithMediaPlayer](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SpatialAudioWithMediaPlayer) | play media with spatial audio                                | GetLocalSpatialAudioEngine, UpdateRemotePositionEx           |
+| [StartLocalVideoTranscoder](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartLocalVideoTranscoder) | Merging of multiple video sources, options are jpg,png,gif,medai,etc | StartLocalVideoTranscoder                                    |
+| [StartDirectCdnStreaming](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartDirectCdnStreaming) | stream video by RTMP Push to a CDN          | StartDirectCdnStreaming, SetDirectCdnStreamingVideoConfiguration, StopDirectCdnStreaming                               |
+| [StartRhythmPlayer](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartRhythmPlayer) | use phythm player in rtc engine                              | StartRhythmPlayer                                            |
+| [StartRtmpStreamWithTranscoding](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartRtmpStreamWithTranscoding) | stream video by RTMP Push to a CDN                           | StartRtmpStreamWithTranscoding, UpdateRtmpTranscoding, StopRtmpStream |
+| [StreamMessage](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StreamMessage) | how to send stream message                                   | CreateDataStream, SendStreamMessage                          |
+| [TakeSnapshot](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/TakeSnapshot) | how to screen shot                                           | TakeSnapshot                                                 |
+| [VirtualBackground](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/VirtualBackground) | enable virtual background                                    | EnableVirtualBackground                                      |
+| [VoiceChanger](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/VoiceChanger) | how to modify your voice                                     | SetVoiceBeautifierPreset, SetAudioEffectPreset,SetVoiceConversionPreset,SetLocalVoicePitch, SetLocalVoiceEqualization,   SetLocalVoiceReverb |
+
+## How to run the sample project
+
+#### Developer Environment Requirements
+
+* Unity 2017 LTS and up
+
+#### Steps to run
+
+First, create a developer account at [Agora.io](https://dashboard.agora.io/signin/), and obtain an App ID.
+
+Then do the following:
+
+1. Clone this repository.
+
+2. Open the project in Unity Editor. Note that you will see compiler errors before you download the SDK package.
+
+3. You may download the SDK package in either of the following two ways:
+
+    a. From Unity Asset Store download and import [the Agora Video SDK](https://assetstore.unity.com/packages/tools/video/agora-video-chat-sdk-for-unity-134502)
+    
+    b. Download the ******Agora Video SDK****** from [Agora.io SDK](https://docs.agora.io/en/Video/downloads?platform=Unity). Unzip the downloaded SDK package and import into Unity project
+
+4.  Fill your App ID into the ******API-Example-Unity/Assets/API-Example/appIdInput/AppIdInput.asset****** 
+
+5.  Choose one of the scene that you want to run
+
+Run the game and you are now good to go!
+
+
+
+## Feedback
+
+If you have any problems or suggestions regarding the sample projects, feel free to file an issue.
+
+## Reference
+
+- You can find full API document at [Document Center](https://docs.agora.io/en/video-call-4.x/api-ref)
+- You can find full release note at [Release Note](https://docs.agora.io/en/video-call-4.x/release_unity_ng?platform=Unity)
+- You can file issues about this demo at [issue](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/issues)
+
+## Related resources
+
+- Check our [FAQ](https://docs.agora.io/en/faq) to see if your issue has been recorded.
+- Dive into [Agora SDK Samples](https://github.com/AgoraIO) to see more tutorials
+- Take a look at [Agora Use Case](https://github.com/AgoraIO-usecase) for more complicated real use case
+- Take a look at [AgoraIO-Extensions](https://github.com/AgoraIO-Extensions) for Crossplatform and Marketing-place projects
+- Repositories managed by developer communities can be found at [Agora Community](https://github.com/AgoraIO-Community)
+- If you encounter problems during integration, feel free to ask questions in [Stack Overflow](https://stackoverflow.com/questions/tagged/agora.io)
+
+## License
+The sample projects are under the MIT license.

+ 7 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: cde154e09b7344431830f9b4d1c51318
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 98 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.zh.md

@@ -0,0 +1,98 @@
+# API-Example-Unity
+
+*Read this in other languages: [English](README.md)*
+
+## 简介
+
+这个开源示例项目演示了不同场景下,Agora SDK 的基本集成逻辑。 项目中每个 Scene 都是一个独立的场景,都可以成功独立运行。
+
+在这个示例项目中包含的所有场景都可以独立运行:
+
+## 项目结构
+
+* **基础案例:**
+
+| Demo                                                         | Description                                        | APIs                                                         |
+| ------------------------------------------------------------ | -------------------------------------------------- | ------------------------------------------------------------ |
+| [JoinChannelAudio](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Basic/JoinChannelAudio) | 基础音频通话                      | CreateAgoraRtcEngine, JoinChannelByKey, LeaveChannel                    |
+| [JoinChannelVideo](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Basic/JoinChannelVideo) | 基础视频通话 | SetChannelProfile,SetClientRole,EnableVideo,EnableVideo, JoinChannelByKey, VideoSurface |
+
+* **进阶案例:**
+
+| Demo                                                         | Description                                                  | APIs                                                         |
+| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [AudioMixing](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/AudioMixing) | 在频道内播放混音和已音效             | StartAudioMixing, PlayEffect                                 |
+| [AudioSpectrum](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/AudioSpectrum) | 媒体音谱功能             | IAudioSpectrumObserver, RegisterMediaPlayerAudioSpectrumObserver                                 |
+| [ChannelMediaRelay](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ChannelMediaRelay) | 启动跨通道媒体流转发。该方法可用于实现跨通道连麦等场景。            | StartChannelMediaRelay, UpdateChannelMediaRelay, PauseAllChannelMediaRelay, ResumeAllChannelMediaRelay, StopChannelMediaRelay                      |
+| [ContentInspect](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ContentInspect) | 内容鉴定 | SetContentInspect |
+| [CustomCaptureAudio](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/CustomCaptureAudio) | 推送外部音频帧     | PushAudioFrame                                               |
+| [CustomCaptureVideo](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/CustomCaptureVideo) | 推送外部视频帧     | PushVideoFrame                                               |
+| [CustomRenderAudio](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/CustomRenderAudio) |  拉取远端音频数据 | PullAudioFrame                                               |
+| [DeviceManager](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/DeviceManager) | 获取当前的音视频设备信息      | GetAudioDeviceManager, GetVideoDeviceManager |
+| [DualCamera](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/DualCamera) | 双摄像头工作  | StartPrimaryCameraCapture, StartSecondaryCameraCapture |
+| [JoinChannelVideoToken](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/JoinChannelVideoToken) | 使用Token加入频道和更新Token                    | RenewToken                                                   |
+| [JoinChannelWithUserAccount](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/JoinChannelWithUserAccount) | 使用用户账号加入频道                  | JoinChannelWithUserAccount,   GetUserInfoByUserAccount                                                 |
+| [MediaPlayer](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/MediaPlayer) | 播放媒体文件                  | CreateMediaPlayer,  Play, Stop                                               |
+| [MediaRecorder](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/MediaRecorder) | 媒体录制                  | StartRecording,  StopRecording,                                                |
+| [Metadata](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/Metadata) | 发送元数据                  | IMetadataObserver                                                |
+| [ProcessAudioRawData](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ProcessAudioRawData) | 获注册音频裸数据观测器,并用audioclip播放获取到的音频数据                       | RegisterAudioFrameObserver, OnPlaybackAudioFrame, OnRceordAudioFrame |
+| [ProcessVideoRawData](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ProcessVideoRawData) | 注册视频裸数据观测器,并渲染在Texture上                       | RegisterVideoFrameObserver, OnCaptureVideoFrame, OnRenderVideoFrame |
+| [PushEncodedVideoImage](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/PushEncodedVideoImage) | 发送结构化数据 | PushEncodedVideoImage |
+| [ScreenShare](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ScreenShare) | 屏幕共享            | StartScreenCaptureByWindowId, StartScreenCaptureByDisplayId                       |
+| [ScreenShareWhileVideoCall](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/ScreenShareWhileVideoCall) | 在视频通话时进行屏幕共享            | StartScreenCaptureByWindowId, StartScreenCaptureByDisplayId                       |
+| [SetBeautyEffectOptions](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SetBeautyEffectOptions) | 开启美颜效果            | SetBeautyEffectOptions                   |
+| [SetEncryption](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SetEncryption) | 开启或关闭内置加密                                | EnableEncryption                                             |
+| [SetVideoEncoderConfiguration](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SetVideoEncoderConfiguration) | 设置视频编码属性。           | SetVideoEncoderConfiguration                                 |
+| [SpatialAudioWithMediaPlayer](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/SpatialAudioWithMediaPlayer) | 使用空间音效播放媒体文件        | GetLocalSpatialAudioEngine, UpdateRemotePositionEx                                 |
+| [StartDirectCdnStreaming](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartDirectCdnStreaming) | cdn推流        | StartDirectCdnStreaming, SetDirectCdnStreamingVideoConfiguration, StopDirectCdnStreaming                               |
+| [StartLocalVideoTranscoder](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartLocalVideoTranscoder) | 多路视频合成一路,可以合成png,jpg,jif等等         | StartLocalVideoTranscoder                        |
+| [StartRhythmPlayer](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartRhythmPlayer) | 开启节拍器        | StartRhythmPlayer                        |
+| [StartRtmpStreamWithTranscoding](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StartRtmpStreamWithTranscoding) | 推流到CDN        | StartRtmpStreamWithTranscoding, UpdateRtmpTranscoding, StopRtmpStream                        |
+| [StreamMessage](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/StreamMessage) | 发送流数据        | CreateDataStream, SendStreamMessage                        |
+| [TakeSnapshot](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/TakeSnapshot) | 屏幕截图      | TakeSnapshot                        |
+| [VirtualBackground](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/VirtualBackground) | 开启虚拟背景   | EnableVirtualBackground                 |
+| [VoiceChanger](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/tree/release/4.0.0/API-Example-Unity/Assets/API-Example/Examples/Advanced/VoiceChanger) | 变声设置   | SetVoiceBeautifierPreset, SetAudioEffectPreset,SetVoiceConversionPreset,SetLocalVoicePitch, SetLocalVoiceEqualization,   SetLocalVoiceReverb                  |
+
+
+
+## 如何运行示例程序
+
+#### 运行环境
+
+* Unity 2017 LTS 或以上
+
+#### 运行步骤
+
+* 首先在 [Agora.io 注册](https://dashboard.agora.io/cn/signup/) 注册账号,并创建自己的测试项目,获取到 AppID。
+
+* 然后在 [Agora.io SDK](https://docs.agora.io/cn/Agora%20Platform/downloads) 下载 **Unity SDK**,解压后把 sdk 包中的 unitypackage文件导入工程
+
+* 最后使用 Unity 打开本项目, 将获取到的AppID填入******API-Example-Unity/Assets/API-Example/appIdInput/AppIdInput.asset*****中
+
+* 一切就绪。你可以自由探索示例项目,体验 SDK 的丰富功能。
+
+
+
+## 反馈
+
+如果您对示例项目有任何问题或建议,请随时提交问题。
+
+## 参考文档
+
+- 您可以在 [文档中心](https://docs.agora.io/cn/All/api-ref?platform=Unity)找到完整的API文档
+- 您可以在 [发版说明](https://docs.agora.io/cn/video-call-4.x/release_unity_ng?platform=Unity)找到完整的发版说明
+- 您可以在 [github issue](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/issues)提交你的issue
+
+## 相关资源
+
+- 你可以先参阅[常见问题](https://docs.agora.io/cn/faq)
+- 如果你想了解更多官方示例,可以参考[官方 SDK 示例](https://github.com/AgoraIO)
+- 如果你想了解声网 SDK 在复杂场景下的应用,可以参考[官方场景案例](https://github.com/AgoraIO-usecase)
+- 如果你想了解声网的一些社区开发者维护的项目,可以查看[社区](https://github.com/AgoraIO-Community)
+- 如果你想了解声网的跨平台框架和云市场相关的项目,可以查看[AgoraIO-Extensions](https://github.com/AgoraIO-Extensions)
+- 若遇到问题需要开发者帮助,你可以到[开发者社区](https://rtcdeveloper.com/)提问
+- 如果需要售后技术支持, 你可以在[Agora Dashboard](https://dashboard.agora.io/)提交工单
+
+## 代码许可
+
+示例项目遵守 MIT 许可证。

+ 7 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/README.zh.md.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 40d437cea762a44329ae0070e618829f
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8a55f6d4538aa4172a1c0701d43b1b11
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 41 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/Logger.cs

@@ -0,0 +1,41 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace Agora.Util
+{
+    public class Logger
+    {
+        Text text;
+
+        public Logger(Text text)
+        {
+            this.text = text;
+        }
+
+        public void UpdateLog(string logMessage)
+        {
+            Debug.Log(logMessage);
+            string srcLogMessage = text.text;
+            if (srcLogMessage.Length > 400)
+            {
+                srcLogMessage = srcLogMessage.Substring(srcLogMessage.Length - 50);
+            }
+
+            srcLogMessage += "\r\n \r\n";
+            srcLogMessage += logMessage;
+            text.text = srcLogMessage;
+        }
+
+        public bool DebugAssert(bool condition, string message)
+        {
+            if (!condition)
+            {
+                UpdateLog(message);
+                return false;
+            }
+
+            Debug.Assert(condition, message);
+            return true;
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/Logger.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0f459233ffbc44f538cee04c3774ecfb
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 33 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/PermissionHelper.cs

@@ -0,0 +1,33 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+#if(UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
+using UnityEngine.Android;
+#endif
+
+namespace Agora.Util
+{
+    public class PermissionHelper
+    {
+        public static void RequestMicrophontPermission()
+        {
+#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
+		if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
+		{                 
+			Permission.RequestUserPermission(Permission.Microphone);
+		}
+#endif
+        }
+
+        public static void RequestCameraPermission()
+        {
+#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID)
+		if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
+		{                 
+			Permission.RequestUserPermission(Permission.Camera);
+		}
+#endif
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/PermissionHelper.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 278e9c5c309ce4f94a580b1a9529d922
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 36 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RequestToken.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections;
+using UnityEngine;
+using UnityEngine.Networking;
+
+[Serializable]
+public class TokenObject {
+  public string rtcToken;
+}
+
+namespace Agora.Util
+{
+  public static class HelperClass
+  {
+    public static IEnumerator FetchToken(
+        string url, string channel, int userId, Action<string> callback = null
+    ) {
+      UnityWebRequest request = UnityWebRequest.Get(string.Format(
+        "{0}/rtc/{1}/publisher/uid/{2}/", url, channel, userId
+      ));
+      yield return request.SendWebRequest();
+
+      if (request.isNetworkError || request.isHttpError) {
+        Debug.Log(request.error);
+        callback(null);
+        yield break;
+      }
+
+      TokenObject tokenInfo = JsonUtility.FromJson<TokenObject>(
+        request.downloadHandler.text
+      );
+
+      callback(tokenInfo.rtcToken);
+    }
+  }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RequestToken.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f3e7fb01e14e4e5a97b6ec6f6925e4f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 253 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RingBuffer.cs

@@ -0,0 +1,253 @@
+#region License
+/* Copyright 2015 Joe Osborne
+ * 
+ * This file is part of RingBuffer.
+ *
+ *  RingBuffer is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  RingBuffer is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with RingBuffer. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace RingBuffer {
+    /// <summary>
+    /// A generic ring buffer with fixed capacity.
+    /// </summary>
+    /// <typeparam name="T">The type of data stored in the buffer</typeparam>
+    public class RingBuffer<T> : IEnumerable<T>, IEnumerable, ICollection<T>, 
+        ICollection {
+
+        protected int head = 0;
+        protected int tail = 0;
+        protected int size = 0;
+
+        protected T[] buffer;
+
+        private bool allowOverflow;
+        public bool AllowOverflow { get { return allowOverflow; } }
+
+        /// <summary>
+        /// The total number of elements the buffer can store (grows).
+        /// </summary>
+        public int Capacity { get { return buffer.Length; } }
+
+        /// <summary>
+        /// The number of elements currently contained in the buffer.
+        /// </summary>
+        public int Size { get { return size; } }
+
+        /// <summary>
+        /// Retrieve the next item from the buffer.
+        /// </summary>
+        /// <returns>The oldest item added to the buffer.</returns>
+        public T Get() {
+            if(size == 0) throw new System.InvalidOperationException("Buffer is empty.");
+            T _item = buffer[head];
+            head = (head + 1) % Capacity;
+            size--;
+            return _item;
+        }
+
+        /// <summary>
+        /// Adds an item to the end of the buffer.
+        /// </summary>
+        /// <param name="item">The item to be added.</param>
+        public void Put(T item) {
+            // If tail & head are equal and the buffer is not empty, assume
+            // that it will overflow and throw an exception.
+            if(tail == head && size != 0) {
+                if(allowOverflow) {
+                    addToBuffer(item, true);
+                }
+                else {
+                    throw new System.InvalidOperationException("The RingBuffer is full");
+                }
+            }
+            // If the buffer will not overflow, just add the item.
+            else {
+                addToBuffer(item, false);
+            }
+        }
+
+        public void Put(T[] item)
+        {
+            foreach (var t in item)
+            {
+                Put(t);
+            }
+        }
+
+        protected void addToBuffer(T toAdd, bool overflow) {
+            if(overflow) {
+                head = (head + 1) % Capacity;
+            }
+            else {
+                size++;
+            }
+            buffer[tail] = toAdd;
+            tail = (tail + 1) % Capacity;
+        }
+
+        #region Constructors
+        // Default capacity is 4, default overflow behavior is false.
+        public RingBuffer() : this(4) { }
+
+        public RingBuffer(int capacity) : this(capacity, false) { }
+
+        public RingBuffer(int capacity, bool overflow) {
+            buffer = new T[capacity];
+            allowOverflow = overflow;
+        }
+        #endregion
+
+        #region IEnumerable Members
+        public IEnumerator<T> GetEnumerator() {
+            int _index = head;
+            for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) {
+                yield return buffer[_index];
+            }
+        }
+
+        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
+            return GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() {
+            return (IEnumerator)GetEnumerator();
+        }
+        #endregion
+
+        #region ICollection<T> Members
+        public int Count { get { return size; } }
+        public bool IsReadOnly { get { return false; } }
+
+        public void Add(T item) {
+            Put(item);
+        }
+        
+        /// <summary>
+        /// Determines whether the RingBuffer contains a specific value.
+        /// </summary>
+        /// <param name="item">The value to check the RingBuffer for.</param>
+        /// <returns>True if the RingBuffer contains <paramref name="item"/>
+        /// , false if it does not.
+        /// </returns>
+        public bool Contains(T item) {
+            EqualityComparer<T> comparer = EqualityComparer<T>.Default;
+            int _index = head;
+            for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) {
+                if(comparer.Equals(item, buffer[_index])) return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Removes all items from the RingBuffer.
+        /// </summary>
+        public void Clear() {
+            for(int i = 0; i < Capacity; i++) {
+                buffer[i] = default(T);
+            }
+            head = 0;
+            tail = 0;
+            size = 0;
+        }
+
+        /// <summary>
+        /// Copies the contents of the RingBuffer to <paramref name="array"/>
+        /// starting at <paramref name="arrayIndex"/>.
+        /// </summary>
+        /// <param name="array">The array to be copied to.</param>
+        /// <param name="arrayIndex">The index of <paramref name="array"/>
+        /// where the buffer should begin copying to.</param>
+        public void CopyTo(T[] array, int arrayIndex) {
+            int _index = head;
+            for(int i = 0; i < size; i++, arrayIndex++, _index = (_index + 1) %
+                Capacity) {
+                array[arrayIndex] = buffer[_index];
+            }
+        }
+
+        /// <summary>
+        /// Removes <paramref name="item"/> from the buffer.
+        /// </summary>
+        /// <param name="item"></param>
+        /// <returns>True if <paramref name="item"/> was found and 
+        /// successfully removed. False if <paramref name="item"/> was not
+        /// found or there was a problem removing it from the RingBuffer.
+        /// </returns>
+        public bool Remove(T item) {
+            int _index = head;
+            int _removeIndex = 0;
+            bool _foundItem = false;
+            EqualityComparer<T> _comparer = EqualityComparer<T>.Default;
+            for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) {
+                if(_comparer.Equals(item, buffer[_index])) {
+                    _removeIndex = _index;
+                    _foundItem = true;
+                    break;
+                }
+            }
+            if(_foundItem) {
+                T[] _newBuffer = new T[size - 1];
+                _index = head;
+                bool _pastItem = false;
+                for(int i = 0; i < size - 1; i++, _index = (_index + 1) % Capacity) {
+                    if(_index == _removeIndex) {
+                        _pastItem = true;
+                    }
+                    if(_pastItem) {
+                        _newBuffer[_index] = buffer[(_index + 1) % Capacity];
+                    }
+                    else {
+                        _newBuffer[_index] = buffer[_index];
+                    }
+                }
+                size--;
+                buffer = _newBuffer;
+                return true;
+            }
+            return false;
+        }
+        #endregion
+
+        #region ICollection Members
+        /// <summary>
+        /// Gets an object that can be used to synchronize access to the
+        /// RingBuffer.
+        /// </summary>
+        public Object SyncRoot { get { return this; } }
+
+        /// <summary>
+        /// Gets a value indicating whether access to the RingBuffer is 
+        /// synchronized (thread safe).
+        /// </summary>
+        public bool IsSynchronized { get { return false; } }
+
+        /// <summary>
+        /// Copies the elements of the RingBuffer to <paramref name="array"/>, 
+        /// starting at a particular Array <paramref name="index"/>.
+        /// </summary>
+        /// <param name="array">The one-dimensional Array that is the 
+        /// destination of the elements copied from RingBuffer. The Array must 
+        /// have zero-based indexing.</param>
+        /// <param name="index">The zero-based index in 
+        /// <paramref name="array"/> at which copying begins.</param>
+        void ICollection.CopyTo(Array array, int index) {
+            CopyTo((T[])array, index);
+        }
+        #endregion
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/RingBuffer.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3d08ddab4d4174fc59891898240053f6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 15 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/UIElementDragger.cs

@@ -0,0 +1,15 @@
+using UnityEngine;
+using UnityEngine.EventSystems;
+
+namespace Agora.Util
+{
+    public class UIElementDrag : EventTrigger
+    {
+
+        public override void OnDrag(PointerEventData eventData)
+        {
+            transform.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
+            base.OnDrag(eventData);
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/API-Example/Tools/UIElementDragger.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: acf42074a81cd4084a309705e659759e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 96b9aab82f7de4ab08eb9b232a6da2f4
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 81d178b4c569f48dea52605b78c6fa7f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: eaa692972fe39494eb4d11bb723b8657
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 49 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/CloudAudioEngineEventHandler.cs

@@ -0,0 +1,49 @@
+namespace Agora.Rtc
+{
+    //public delegate void OnTokenWillExpireHandler();
+  
+    //public delegate void OnConnectionStateChangeHandler(SAE_CONNECTION_STATE_TYPE state, SAE_CONNECTION_CHANGED_REASON_TYPE reason);
+
+    //public delegate void OnTeammateLeftHandler(uint uid);
+
+    //public delegate void OnTeammateJoinedHandler(uint uid);
+
+    //public class CloudSpatialAudioEventHandler : ICloudSpatialAudioEventHandler
+    //{
+    //    public event OnTokenWillExpireHandler EventOnTokenWillExpire;
+    //    public event OnConnectionStateChangeHandler EventOnConnectionStateChange;
+    //    public event OnTeammateLeftHandler EventOnTeammateLeft;
+    //    public event OnTeammateJoinedHandler EventOnTeammateJoined;
+
+    //    private static CloudSpatialAudioEventHandler eventInstance = null;
+
+    //    public static CloudSpatialAudioEventHandler GetInstance()
+    //    {
+    //        return eventInstance ?? (eventInstance = new CloudSpatialAudioEventHandler());
+    //    }
+
+    //    public override void OnTokenWillExpire()
+    //    {
+    //        if (EventOnTokenWillExpire == null) return;
+    //        EventOnTokenWillExpire.Invoke();
+    //    }
+  
+    //    public override void OnConnectionStateChange(SAE_CONNECTION_STATE_TYPE state, SAE_CONNECTION_CHANGED_REASON_TYPE reason)
+    //    {
+    //        if (EventOnConnectionStateChange == null) return;
+    //        EventOnConnectionStateChange.Invoke(state, reason);
+    //    }
+
+    //    public override void OnTeammateLeft(uint uid)
+    //    {
+    //        if (EventOnTeammateLeft == null) return;
+    //        EventOnTeammateLeft.Invoke(uid);
+    //    }
+
+    //    public override void OnTeammateJoined(uint uid)
+    //    {
+    //        if (EventOnTeammateJoined == null) return;
+    //        EventOnTeammateJoined.Invoke(uid);
+    //    }
+    //}
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/CloudAudioEngineEventHandler.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 155b11188c7364fb183e34727f17beed
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 98 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaPlayerSourceObserver.cs

@@ -0,0 +1,98 @@
+using System;
+
+namespace Agora.Rtc
+{
+    public delegate void OnPlayerSourceStateChangedHandler(MEDIA_PLAYER_STATE state, MEDIA_PLAYER_ERROR ec);
+
+    public delegate void OnPositionChangedHandler(Int64 position_ms);
+
+    public delegate void OnPlayerEventHandler(MEDIA_PLAYER_EVENT eventCode, Int64 elapsedTime, string message);
+
+    public delegate void OnMetaDataHandler(byte[] data, int length);
+
+    public delegate void OnPlayBufferUpdatedHandler(Int64 playCachedBuffer);
+
+    public delegate void OnCompletedHandler();
+
+    public delegate void OnAgoraCDNTokenWillExpireHandler();
+
+    public delegate void OnPlayerSrcInfoChangedHandler(SrcInfo from, SrcInfo to);
+
+    public delegate void OnPlayerInfoUpdatedHandler(PlayerUpdatedInfo info);
+
+    public delegate void MediaPlayerOnAudioVolumeIndicationHandler(int volume);
+    
+    public class MediaPlayerSourceObserver : IMediaPlayerSourceObserver
+    {
+        public event OnPlayerSourceStateChangedHandler EventOnPlayerSourceStateChanged;
+        public event OnPositionChangedHandler EventOnPositionChanged;
+        public event OnPlayerEventHandler EventOnPlayerEvent;
+        public event OnMetaDataHandler EventOnMetaData;
+        public event OnPlayBufferUpdatedHandler EventOnPlayBufferUpdated;
+        public event OnCompletedHandler EventOnCompleted;
+        public event OnAgoraCDNTokenWillExpireHandler EventOnAgoraCDNTokenWillExpire;
+        public event OnPlayerSrcInfoChangedHandler EventOnPlayerSrcInfoChanged;
+        public event OnPlayerInfoUpdatedHandler EventOnPlayerInfoUpdated;
+        public event MediaPlayerOnAudioVolumeIndicationHandler EventOnAudioVolumeIndication;
+
+        public override void OnPlayerSourceStateChanged(MEDIA_PLAYER_STATE state, MEDIA_PLAYER_ERROR ec)
+        {
+            if (EventOnPlayerSourceStateChanged == null) return;
+            EventOnPlayerSourceStateChanged.Invoke(state, ec);
+        }
+
+        public override void OnPositionChanged(Int64 position_ms)
+        {
+            if (EventOnPositionChanged == null) return;
+            EventOnPositionChanged.Invoke(position_ms);
+        }
+
+        public override void OnPlayerEvent(MEDIA_PLAYER_EVENT eventCode, Int64 elapsedTime, string message)
+        {
+            if (EventOnPlayerEvent == null) return;
+            EventOnPlayerEvent.Invoke(eventCode, elapsedTime, message);
+        }
+
+        public override void OnMetaData(byte[] data, int length)
+        {
+            if (EventOnMetaData == null) return;
+            EventOnMetaData.Invoke(data, length);
+        }
+
+        public override void OnPlayBufferUpdated(Int64 playCachedBuffer)
+        {
+            if (EventOnPlayBufferUpdated == null) return;
+            EventOnPlayBufferUpdated.Invoke(playCachedBuffer);
+        }
+
+        public override void OnCompleted()
+        {
+            if (EventOnCompleted == null) return;
+            EventOnCompleted.Invoke();
+        }
+
+        public override void OnAgoraCDNTokenWillExpire()
+        {
+            if (EventOnAgoraCDNTokenWillExpire == null) return;
+            EventOnAgoraCDNTokenWillExpire.Invoke();
+        }
+
+        public override void OnPlayerSrcInfoChanged(SrcInfo from, SrcInfo to)
+        {
+            if (EventOnPlayerSrcInfoChanged == null) return;
+            EventOnPlayerSrcInfoChanged.Invoke(from, to);
+        }
+
+        public override void OnPlayerInfoUpdated(PlayerUpdatedInfo info)
+        {
+            if (EventOnPlayerInfoUpdated == null) return;
+            EventOnPlayerInfoUpdated.Invoke(info);
+        }
+
+        public override void OnAudioVolumeIndication(int volume)
+        {
+            if (EventOnAudioVolumeIndication == null) return;
+            EventOnAudioVolumeIndication.Invoke(volume);
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaPlayerSourceObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: eb5beb95e9b264a9d87c84770583f106
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 32 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaRecorderObserver.cs

@@ -0,0 +1,32 @@
+namespace Agora.Rtc
+{
+    public delegate void OnRecorderStateChangedHandler(RecorderState state, RecorderErrorCode error);
+
+    public delegate void OnRecorderInfoUpdatedHandler(RecorderInfo info);
+    
+    public class MediaRecorderObserver : IMediaRecorderObserver
+    {
+        public event OnRecorderStateChangedHandler EventOnRecorderStateChanged;
+
+        public event OnRecorderInfoUpdatedHandler EventOnRecorderInfoUpdated;
+
+        private static MediaRecorderObserver eventInstance = null;
+
+        public static MediaRecorderObserver GetInstance()
+        {
+            return eventInstance ?? (eventInstance = new MediaRecorderObserver());
+        }
+
+        public override void OnRecorderStateChanged(RecorderState state, RecorderErrorCode error)
+        {
+            if (EventOnRecorderStateChanged == null) return;
+            EventOnRecorderStateChanged.Invoke(state, error);
+        }
+
+        public override void OnRecorderInfoUpdated(RecorderInfo info)
+        {
+            if (EventOnRecorderInfoUpdated == null) return;
+            EventOnRecorderInfoUpdated.Invoke(info);
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/MediaRecorderObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04f7918fcaeef4ba68d624b76927baf7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 905 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/RtcEngineEventHandler.cs

@@ -0,0 +1,905 @@
+using System;
+
+namespace Agora.Rtc
+{
+    public delegate void OnJoinChannelSuccessHandler(RtcConnection connection, int elapsed);
+
+    public delegate void OnErrorHandler(int err, string msg);
+
+    public delegate void OnLeaveChannelHandler(RtcConnection connection, RtcStats stats);
+
+    public delegate void OnRejoinChannelSuccessHandler(RtcConnection connection, int elapsed);
+
+    public delegate void OnProxyConnectedHandler(string channel, uint uid, PROXY_TYPE proxyType, string localProxyIp, int elapsed);
+
+    public delegate void OnAudioQualityHandler(RtcConnection connection, uint remoteUid, int quality, UInt16 delay, UInt16 lost);
+
+    public delegate void OnLastmileProbeResultHandler(LastmileProbeResult result);
+
+    public delegate void OnAudioVolumeIndicationHandler(RtcConnection connection, AudioVolumeInfo[] speakers, uint speakerNumber, int totalVolume);
+
+    public delegate void OnRtcStatsHandler(RtcConnection connection, RtcStats stats);
+
+    public delegate void OnAudioDeviceStateChangedHandler(string deviceId, MEDIA_DEVICE_TYPE deviceType, MEDIA_DEVICE_STATE_TYPE deviceState);
+
+    public delegate void OnAudioMixingFinishedHandler();
+
+    public delegate void OnAudioEffectFinishedHandler(int soundId);
+
+    public delegate void OnVideoDeviceStateChangedHandler(string deviceId, MEDIA_DEVICE_TYPE deviceType, MEDIA_DEVICE_STATE_TYPE deviceState);
+
+    public delegate void OnMediaDeviceChangedHandler(MEDIA_DEVICE_TYPE deviceType);
+
+    public delegate void OnNetworkQualityHandler(RtcConnection connection, uint remoteUid, int txQuality, int rxQuality);
+
+    public delegate void OnIntraRequestReceivedHandler(RtcConnection connection);
+
+    public delegate void OnUplinkNetworkInfoUpdatedHandler(UplinkNetworkInfo info);
+
+    public delegate void OnDownlinkNetworkInfoUpdatedHandler(DownlinkNetworkInfo info);
+
+    public delegate void OnLastmileQualityHandler(int quality);
+
+    public delegate void OnFirstLocalVideoFrameHandler(RtcConnection connection, int width, int height, int elapsed);
+
+    public delegate void OnFirstLocalVideoFramePublishedHandler(RtcConnection connection, int elapsed);
+
+    public delegate void OnVideoSourceFrameSizeChangedHandler(RtcConnection connection, VIDEO_SOURCE_TYPE sourceType, int width, int height);
+
+    public delegate void OnFirstRemoteVideoDecodedHandler(RtcConnection connection, uint remoteUid, int width, int height, int elapsed);
+
+    public delegate void OnVideoSizeChangedHandler(RtcConnection connection, VIDEO_SOURCE_TYPE sourceType, uint uid, int width, int height, int rotation);
+
+    public delegate void OnContentInspectResultHandler(CONTENT_INSPECT_RESULT result);
+
+    public delegate void OnSnapshotTakenHandlerEx(RtcConnection connection, uint uid, string filePath, int width, int height, int errCode);
+
+    public delegate void OnLocalVideoStateChangedHandler(VIDEO_SOURCE_TYPE source, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_ERROR errorCode);
+
+    public delegate void OnLocalVideoStateChangedHandlerEx(RtcConnection connection, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_ERROR errorCode);
+
+    public delegate void OnRemoteVideoStateChangedHandler(RtcConnection connection, uint remoteUid, REMOTE_VIDEO_STATE state, REMOTE_VIDEO_STATE_REASON reason, int elapsed);
+
+    public delegate void OnFirstRemoteVideoFrameHandler(RtcConnection connection, uint remoteUid, int width, int height, int elapsed);
+
+    public delegate void OnUserJoinedHandler(RtcConnection connection, uint remoteUid, int elapsed);
+
+    public delegate void OnUserOfflineHandler(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason);
+
+    public delegate void OnUserMuteAudioHandler(RtcConnection connection, uint remoteUid, bool muted);
+
+    public delegate void OnUserMuteVideoHandler(RtcConnection connection, uint remoteUid, bool muted);
+
+    public delegate void OnUserEnableVideoHandler(RtcConnection connection, uint remoteUid, bool enabled);
+
+    public delegate void OnUserEnableLocalVideoHandler(RtcConnection connection, uint remoteUid, bool enabled);
+
+    public delegate void OnUserStateChangedHandler(RtcConnection connection, uint remoteUid, uint state);
+
+    public delegate void OnApiCallExecutedHandler(int err, string api, string result);
+
+    public delegate void OnLocalAudioStatsHandler(RtcConnection connection, LocalAudioStats stats);
+
+    public delegate void OnRemoteAudioStatsHandler(RtcConnection connection, RemoteAudioStats stats);
+
+    public delegate void OnLocalVideoStatsHandler(RtcConnection connection, LocalVideoStats stats);
+
+    public delegate void OnRemoteVideoStatsHandler(RtcConnection connection, RemoteVideoStats stats);
+
+    public delegate void OnCameraReadyHandler();
+
+    public delegate void OnCameraFocusAreaChangedHandler(int x, int y, int width, int height);
+
+    public delegate void OnCameraExposureAreaChangedHandler(int x, int y, int width, int height);
+
+    public delegate void OnFacePositionChangedHandler(int imageWidth, int imageHeight, Rectangle vecRectangle, int[] vecDistance, int numFaces);
+
+    public delegate void OnVideoStoppedHandler();
+
+    public delegate void OnAudioMixingStateChangedHandler(AUDIO_MIXING_STATE_TYPE state, AUDIO_MIXING_REASON_TYPE reason);
+
+    public delegate void OnRhythmPlayerStateChangedHandler(RHYTHM_PLAYER_STATE_TYPE state, RHYTHM_PLAYER_ERROR_TYPE errorCode);
+
+    public delegate void OnConnectionLostHandler(RtcConnection connection);
+
+    public delegate void OnConnectionInterruptedHandler(RtcConnection connection);
+
+    public delegate void OnConnectionBannedHandler(RtcConnection connection);
+
+    public delegate void OnStreamMessageHandler(RtcConnection connection, uint remoteUid, int streamId, byte[] data, uint length, UInt64 sentTs);
+
+    public delegate void OnStreamMessageErrorHandler(RtcConnection connection, uint remoteUid, int streamId, int code, int missed, int cached);
+
+    public delegate void OnRequestTokenHandler(RtcConnection connection);
+
+    public delegate void OnTokenPrivilegeWillExpireHandler(RtcConnection connection, string token);
+
+    public delegate void OnFirstLocalAudioFramePublishedHandler(RtcConnection connection, int elapsed);
+
+    public delegate void OnFirstRemoteAudioFrameHandler(RtcConnection connection, uint userId, int elapsed);
+
+    public delegate void OnFirstRemoteAudioDecodedHandler(RtcConnection connection, uint uid, int elapsed);
+
+    public delegate void OnLocalAudioStateChangedHandler(RtcConnection connection, LOCAL_AUDIO_STREAM_STATE state, LOCAL_AUDIO_STREAM_ERROR error);
+
+    public delegate void OnRemoteAudioStateChangedHandler(RtcConnection connection, uint remoteUid, REMOTE_AUDIO_STATE state, REMOTE_AUDIO_STATE_REASON reason, int elapsed);
+
+    public delegate void OnActiveSpeakerHandler(RtcConnection connection, uint uid);
+
+    public delegate void OnClientRoleChangedHandler(RtcConnection connection, CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole);
+
+    public delegate void OnClientRoleChangeFailedHandler(RtcConnection connection, CLIENT_ROLE_CHANGE_FAILED_REASON reason, CLIENT_ROLE_TYPE currentRole);
+
+    public delegate void OnAudioDeviceVolumeChangedHandler(MEDIA_DEVICE_TYPE deviceType, int volume, bool muted);
+
+    public delegate void OnRtmpStreamingStateChangedHandler(string url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR_TYPE errCode);
+
+    public delegate void OnRtmpStreamingEventHandler(string url, RTMP_STREAMING_EVENT eventCode);
+
+    //public delegate void OnStreamPublishedHandler(string url, int error);
+
+    //public delegate void OnStreamUnpublishedHandler(string url);
+
+    public delegate void OnTranscodingUpdatedHandler();
+
+    public delegate void OnAudioRoutingChangedHandler(int routing);
+
+    public delegate void OnChannelMediaRelayStateChangedHandler(int state, int code);
+
+    public delegate void OnChannelMediaRelayEventHandler(int code);
+
+    public delegate void OnLocalPublishFallbackToAudioOnlyHandler(bool isFallbackOrRecover);
+
+    public delegate void OnRemoteSubscribeFallbackToAudioOnlyHandler(uint uid, bool isFallbackOrRecover);
+
+    public delegate void OnRemoteAudioTransportStatsHandler(RtcConnection connection, uint remoteUid, UInt16 delay, UInt16 lost, UInt16 rxKBitRate);
+
+    public delegate void OnRemoteVideoTransportStatsHandler(RtcConnection connection, uint remoteUid, UInt16 delay, UInt16 lost, UInt16 rxKBitRate);
+
+    public delegate void OnConnectionStateChangedHandler(RtcConnection connection, CONNECTION_STATE_TYPE state, CONNECTION_CHANGED_REASON_TYPE reason);
+
+    public delegate void OnWlAccMessageHandler(RtcConnection connection, WLACC_MESSAGE_REASON reason, WLACC_SUGGEST_ACTION action, string wlAccMsg);
+
+    public delegate void OnWlAccStatsHandler(RtcConnection connection, WlAccStats currentStats, WlAccStats averageStats);
+
+    public delegate void OnNetworkTypeChangedHandler(RtcConnection connection, NETWORK_TYPE type);
+
+    public delegate void OnEncryptionErrorHandler(RtcConnection connection, ENCRYPTION_ERROR_TYPE errorType);
+
+    public delegate void OnUploadLogResultHandler(RtcConnection connection, string requestId, bool success, UPLOAD_ERROR_REASON reason);
+
+    public delegate void OnUserAccountUpdatedHandler(RtcConnection connection, uint remoteUid, string userAccount);
+
+    public delegate void OnPermissionErrorHandler(PERMISSION_TYPE permissionType);
+
+    public delegate void OnLocalUserRegisteredHandler(uint uid, string userAccount);
+
+    public delegate void OnUserInfoUpdatedHandler(uint uid, UserInfo info);
+
+    public delegate void OnAudioSubscribeStateChangedHandler(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState);
+
+    public delegate void OnVideoSubscribeStateChangedHandler(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState);
+
+    public delegate void OnAudioPublishStateChangedHandler(string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState);
+
+    public delegate void OnVideoPublishStateChangedHandler(VIDEO_SOURCE_TYPE source, string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState);
+
+    public delegate void OnExtensionEventHandler(string provider, string extension, string key, string value);
+
+    public delegate void OnExtensionStartedHandler(string provider, string extension);
+
+    public delegate void OnExtensionStoppedHandler(string provider, string extension);
+
+    public delegate void OnExtensionErrorHandler(string provider, string extension, int error, string message);
+
+    public delegate void OnDirectCdnStreamingStateChangedHandler(DIRECT_CDN_STREAMING_STATE state, DIRECT_CDN_STREAMING_ERROR error, string message);
+
+    public delegate void OnDirectCdnStreamingStatsHandler(DirectCdnStreamingStats stats);
+
+    public class RtcEngineEventHandler : IRtcEngineEventHandler
+    {
+        public event OnJoinChannelSuccessHandler EventOnJoinChannelSuccess;
+        public event OnLeaveChannelHandler EventOnLeaveChannel;
+        public event OnErrorHandler EventOnError;
+        public event OnRejoinChannelSuccessHandler EventOnRejoinChannelSuccess;
+        public event OnProxyConnectedHandler EventOnProxyConnected;
+        public event OnAudioQualityHandler EventOnAudioQuality;
+        public event OnLastmileProbeResultHandler EventOnLastmileProbeResult;
+        public event OnAudioVolumeIndicationHandler EventOnAudioVolumeIndication;
+        public event OnRtcStatsHandler EventOnRtcStats;
+        public event OnAudioDeviceStateChangedHandler EventOnAudioDeviceStateChanged;
+        public event OnAudioMixingFinishedHandler EventOnAudioMixingFinished;
+        public event OnAudioEffectFinishedHandler EventOnAudioEffectFinished;
+        public event OnVideoDeviceStateChangedHandler EventOnVideoDeviceStateChanged;
+        public event OnMediaDeviceChangedHandler EventOnMediaDeviceChanged;
+        public event OnNetworkQualityHandler EventOnNetworkQuality;
+        public event OnIntraRequestReceivedHandler EventOnIntraRequestReceived;
+        public event OnUplinkNetworkInfoUpdatedHandler EventOnUplinkNetworkInfoUpdated;
+        public event OnDownlinkNetworkInfoUpdatedHandler EventOnDownlinkNetworkInfoUpdated;
+        public event OnLastmileQualityHandler EventOnLastmileQuality;
+        public event OnFirstLocalVideoFrameHandler EventOnFirstLocalVideoFrame;
+        public event OnFirstLocalVideoFramePublishedHandler EventOnFirstLocalVideoFramePublished;
+        public event OnVideoSourceFrameSizeChangedHandler EventOnVideoSourceFrameSizeChanged;
+        public event OnFirstRemoteVideoDecodedHandler EventOnFirstRemoteVideoDecoded;
+        public event OnVideoSizeChangedHandler EventOnVideoSizeChanged;
+        public event OnContentInspectResultHandler EventOnContentInspectResult;
+        public event OnSnapshotTakenHandlerEx EventOnSnapshotTakenEx;
+        public event OnLocalVideoStateChangedHandler EventOnLocalVideoStateChanged;
+        public event OnLocalVideoStateChangedHandlerEx EventOnLocalVideoStateChangedEx;
+        public event OnRemoteVideoStateChangedHandler EventOnRemoteVideoStateChanged;
+        public event OnFirstRemoteVideoFrameHandler EventOnFirstRemoteVideoFrame;
+        public event OnUserJoinedHandler EventOnUserJoined;
+        public event OnUserOfflineHandler EventOnUserOffline;
+        public event OnUserMuteAudioHandler EventOnUserMuteAudio;
+        public event OnUserMuteVideoHandler EventOnUserMuteVideo;
+        public event OnUserEnableVideoHandler EventOnUserEnableVideo;
+        public event OnUserEnableLocalVideoHandler EventOnUserEnableLocalVideo;
+        public event OnUserStateChangedHandler EventOnUserStateChanged;
+        public event OnApiCallExecutedHandler EventOnApiCallExecuted;
+        public event OnLocalAudioStatsHandler EventOnLocalAudioStats;
+        public event OnRemoteAudioStatsHandler EventOnRemoteAudioStats;
+        public event OnLocalVideoStatsHandler EventOnLocalVideoStats;
+        public event OnRemoteVideoStatsHandler EventOnRemoteVideoStats;
+        public event OnCameraReadyHandler EventOnCameraReady;
+        public event OnCameraFocusAreaChangedHandler EventOnCameraFocusAreaChanged;
+        public event OnCameraExposureAreaChangedHandler EventOnCameraExposureAreaChanged;
+        public event OnFacePositionChangedHandler EventOnFacePositionChanged;
+        public event OnVideoStoppedHandler EventOnVideoStopped;
+        public event OnAudioMixingStateChangedHandler EventOnAudioMixingStateChanged;
+        public event OnRhythmPlayerStateChangedHandler EventOnRhythmPlayerStateChanged;
+        public event OnConnectionLostHandler EventOnConnectionLost;
+        public event OnConnectionInterruptedHandler EventOnConnectionInterrupted;
+        public event OnConnectionBannedHandler EventOnConnectionBanned;
+        public event OnStreamMessageHandler EventOnStreamMessage;
+        public event OnStreamMessageErrorHandler EventOnStreamMessageError;
+        public event OnRequestTokenHandler EventOnRequestToken;
+        public event OnTokenPrivilegeWillExpireHandler EventOnTokenPrivilegeWillExpire;
+        public event OnFirstLocalAudioFramePublishedHandler EventOnFirstLocalAudioFramePublished;
+        public event OnFirstRemoteAudioFrameHandler EventOnFirstRemoteAudioFrame;
+        public event OnFirstRemoteAudioDecodedHandler EventOnFirstRemoteAudioDecoded;
+        public event OnLocalAudioStateChangedHandler EventOnLocalAudioStateChanged;
+        public event OnRemoteAudioStateChangedHandler EventOnRemoteAudioStateChanged;
+        public event OnActiveSpeakerHandler EventOnActiveSpeaker;
+        public event OnClientRoleChangedHandler EventOnClientRoleChanged;
+        public event OnClientRoleChangeFailedHandler EventOnClientRoleChangeFailed;
+        public event OnAudioDeviceVolumeChangedHandler EventOnAudioDeviceVolumeChanged;
+        public event OnRtmpStreamingStateChangedHandler EventOnRtmpStreamingStateChanged;
+        public event OnRtmpStreamingEventHandler EventOnRtmpStreamingEvent;
+        //public event OnStreamPublishedHandler EventOnStreamPublished;
+        //public event OnStreamUnpublishedHandler EventOnStreamUnpublished;
+        public event OnTranscodingUpdatedHandler EventOnTranscodingUpdated;
+        public event OnAudioRoutingChangedHandler EventOnAudioRoutingChanged;
+        public event OnChannelMediaRelayStateChangedHandler EventOnChannelMediaRelayStateChanged;
+        public event OnChannelMediaRelayEventHandler EventOnChannelMediaRelayEvent;
+        public event OnLocalPublishFallbackToAudioOnlyHandler EventOnLocalPublishFallbackToAudioOnly;
+        public event OnRemoteSubscribeFallbackToAudioOnlyHandler EventOnRemoteSubscribeFallbackToAudioOnly;
+        public event OnRemoteAudioTransportStatsHandler EventOnRemoteAudioTransportStats;
+        public event OnRemoteVideoTransportStatsHandler EventOnRemoteVideoTransportStats;
+        public event OnConnectionStateChangedHandler EventOnConnectionStateChanged;
+        public event OnWlAccMessageHandler EventOnWlAccMessage;
+        public event OnWlAccStatsHandler EventOnWlAccStats;
+        public event OnNetworkTypeChangedHandler EventOnNetworkTypeChanged;
+        public event OnEncryptionErrorHandler EventOnEncryptionError;
+        public event OnUploadLogResultHandler EventOnUploadLogResult;
+        public event OnUserAccountUpdatedHandler EventOnUserAccountUpdated;
+        public event OnPermissionErrorHandler EventOnPermissionError;
+        public event OnLocalUserRegisteredHandler EventOnLocalUserRegistered;
+        public event OnUserInfoUpdatedHandler EventOnUserInfoUpdated;
+        public event OnAudioSubscribeStateChangedHandler EventOnAudioSubscribeStateChanged;
+        public event OnVideoSubscribeStateChangedHandler EventOnVideoSubscribeStateChanged;
+        public event OnAudioPublishStateChangedHandler EventOnAudioPublishStateChanged;
+        public event OnVideoPublishStateChangedHandler EventOnVideoPublishStateChanged;
+        public event OnExtensionEventHandler EventOnExtensionEvent;
+        public event OnExtensionStartedHandler EventOnExtensionStarted;
+        public event OnExtensionStoppedHandler EventOnExtensionStopped;
+        public event OnExtensionErrorHandler EventOnExtensionErrored;
+        public event OnDirectCdnStreamingStateChangedHandler EventOnDirectCdnStreamingStateChanged;
+        public event OnDirectCdnStreamingStatsHandler EventOnDirectCdnStreamingStats;
+
+        private static RtcEngineEventHandler eventInstance = null;
+
+        public static RtcEngineEventHandler GetInstance()
+        {
+            if (eventInstance == null)
+            {
+                eventInstance = new RtcEngineEventHandler();
+            }
+
+            return eventInstance;
+        }
+
+        public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
+        {
+            if (EventOnJoinChannelSuccess == null) return;
+            EventOnJoinChannelSuccess.Invoke(connection, elapsed);
+        }
+
+        public override void OnError(int err, string msg)
+        {
+            if (EventOnError == null) return;
+            EventOnError.Invoke(err, msg);
+        }
+
+        public override void OnLeaveChannel(RtcConnection connection, RtcStats stats)
+        {
+            if (EventOnLeaveChannel == null) return;
+            EventOnLeaveChannel.Invoke(connection, stats);
+        }
+
+        public override void OnRejoinChannelSuccess(RtcConnection connection, int elapsed)
+        {
+            if (EventOnRejoinChannelSuccess == null) return;
+            EventOnRejoinChannelSuccess.Invoke(connection, elapsed);
+        }
+
+        public override void OnProxyConnected(string channel, uint uid, PROXY_TYPE proxyType, string localProxyIp, int elapsed)
+        {
+            if (EventOnProxyConnected == null) return;
+            EventOnProxyConnected.Invoke(channel, uid, proxyType, localProxyIp, elapsed);
+        }
+
+        public override void OnAudioQuality(RtcConnection connection, uint remoteUid, int quality, UInt16 delay, UInt16 lost)
+        {
+            if (EventOnAudioQuality == null) return;
+            EventOnAudioQuality.Invoke(connection, remoteUid, quality, delay, lost);
+        }
+
+        public override void OnLastmileProbeResult(LastmileProbeResult result)
+        {
+            if (EventOnLastmileProbeResult == null) return;
+            EventOnLastmileProbeResult.Invoke(result);
+        }
+
+        public override void OnAudioVolumeIndication(RtcConnection connection, AudioVolumeInfo[] speakers, uint speakerNumber, int totalVolume)
+        {
+            if (EventOnAudioVolumeIndication == null) return;
+            EventOnAudioVolumeIndication.Invoke(connection, speakers, speakerNumber, totalVolume);
+        }
+
+        public override void OnRtcStats(RtcConnection connection, RtcStats stats)
+        {
+            if (EventOnRtcStats == null) return;
+            EventOnRtcStats.Invoke(connection, stats);
+        }
+
+        public override void OnAudioDeviceStateChanged(string deviceId, MEDIA_DEVICE_TYPE deviceType, MEDIA_DEVICE_STATE_TYPE deviceState)
+        {
+            if (EventOnAudioDeviceStateChanged == null) return;
+            EventOnAudioDeviceStateChanged.Invoke(deviceId, deviceType, deviceState);
+        }
+
+        [Obsolete]
+        public override void OnAudioMixingFinished()
+        {
+            if (EventOnAudioMixingFinished == null) return;
+            EventOnAudioMixingFinished.Invoke();
+        }
+
+        public override void OnAudioEffectFinished(int soundId)
+        {
+            if (EventOnAudioEffectFinished == null) return;
+            EventOnAudioEffectFinished.Invoke(soundId);
+        }
+
+        public override void OnVideoDeviceStateChanged(string deviceId, MEDIA_DEVICE_TYPE deviceType, MEDIA_DEVICE_STATE_TYPE deviceState)
+        {
+            if (EventOnVideoDeviceStateChanged == null) return;
+            EventOnVideoDeviceStateChanged.Invoke(deviceId, deviceType, deviceState);
+        }
+
+        public override void OnMediaDeviceChanged(MEDIA_DEVICE_TYPE deviceType)
+        {
+            if (EventOnMediaDeviceChanged == null) return;
+            EventOnMediaDeviceChanged.Invoke(deviceType);
+        }
+
+        public override void OnNetworkQuality(RtcConnection connection, uint remoteUid, int txQuality, int rxQuality)
+        {
+            if (EventOnNetworkQuality == null) return;
+            EventOnNetworkQuality.Invoke(connection, remoteUid, txQuality, rxQuality);
+        }
+
+        public override void OnIntraRequestReceived(RtcConnection connection)
+        {
+            if (EventOnIntraRequestReceived == null) return;
+            EventOnIntraRequestReceived.Invoke(connection);
+        }
+
+        public override void OnUplinkNetworkInfoUpdated(UplinkNetworkInfo info)
+        {
+            if (EventOnUplinkNetworkInfoUpdated == null) return;
+            EventOnUplinkNetworkInfoUpdated.Invoke(info);
+        }
+
+        public override void OnDownlinkNetworkInfoUpdated(DownlinkNetworkInfo info)
+        {
+            if (EventOnDownlinkNetworkInfoUpdated == null) return;
+            EventOnDownlinkNetworkInfoUpdated.Invoke(info);
+        }
+
+        public override void OnLastmileQuality(int quality)
+        {
+            if (EventOnLastmileQuality == null) return;
+            EventOnLastmileQuality.Invoke(quality);
+        }
+
+        public override void OnFirstLocalVideoFrame(RtcConnection connection, int width, int height, int elapsed)
+        {
+            if (EventOnFirstLocalVideoFrame == null) return;
+            EventOnFirstLocalVideoFrame.Invoke(connection, width, height, elapsed);
+        }
+
+        public override void OnFirstLocalVideoFramePublished(RtcConnection connection, int elapsed)
+        {
+            if (EventOnFirstLocalVideoFramePublished == null) return;
+            EventOnFirstLocalVideoFramePublished.Invoke(connection, elapsed);
+        }
+
+        public override void OnVideoSourceFrameSizeChanged(RtcConnection connection, VIDEO_SOURCE_TYPE sourceType, int width, int height)
+        {
+            if (EventOnVideoSourceFrameSizeChanged == null) return;
+            EventOnVideoSourceFrameSizeChanged.Invoke(connection, sourceType, width, height);
+        }
+
+        public override void OnFirstRemoteVideoDecoded(RtcConnection connection, uint remoteUid, int width, int height, int elapsed)
+        {
+            if (EventOnFirstRemoteVideoDecoded == null) return;
+            EventOnFirstRemoteVideoDecoded.Invoke(connection, remoteUid, width, height, elapsed);
+        }
+
+        public override void OnVideoSizeChanged(RtcConnection connection, VIDEO_SOURCE_TYPE sourceType, uint uid, int width, int height, int rotation)
+        {
+            if (EventOnVideoSizeChanged == null) return;
+            EventOnVideoSizeChanged.Invoke(connection, sourceType, uid, width, height, rotation);
+        }
+
+        public override void OnContentInspectResult(CONTENT_INSPECT_RESULT result)
+        {
+            if (EventOnContentInspectResult == null) return;
+            EventOnContentInspectResult.Invoke(result);
+        }
+
+        public override void OnSnapshotTaken(RtcConnection connection, uint uid, string filePath, int width, int height, int errCode)
+        {
+            if (EventOnSnapshotTakenEx == null) return;
+            EventOnSnapshotTakenEx.Invoke(connection, uid, filePath, width, height, errCode);
+        }
+
+        public override void OnLocalVideoStateChanged(VIDEO_SOURCE_TYPE source, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_ERROR errorCode)
+        {
+            if (EventOnLocalVideoStateChanged == null) return;
+            EventOnLocalVideoStateChanged.Invoke(source, state, errorCode);
+        }
+
+        public override void OnLocalVideoStateChanged(RtcConnection connection, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_ERROR errorCode)
+        {
+            if (EventOnLocalVideoStateChangedEx == null) return;
+            EventOnLocalVideoStateChangedEx.Invoke(connection, state, errorCode);
+        }
+
+        public override void OnRemoteVideoStateChanged(RtcConnection connection, uint remoteUid, REMOTE_VIDEO_STATE state, REMOTE_VIDEO_STATE_REASON reason, int elapsed)
+        {
+            if (EventOnRemoteVideoStateChanged == null) return;
+            EventOnRemoteVideoStateChanged.Invoke(connection, remoteUid, state, reason, elapsed);
+        }
+
+        public override void OnFirstRemoteVideoFrame(RtcConnection connection, uint remoteUid, int width, int height, int elapsed)
+        {
+            if (EventOnFirstRemoteVideoFrame == null) return;
+            EventOnFirstRemoteVideoFrame.Invoke(connection, remoteUid, width, height, elapsed);
+        }
+
+        public override void OnUserJoined(RtcConnection connection, uint remoteUid, int elapsed)
+        {
+            if (EventOnUserJoined == null) return;
+            EventOnUserJoined.Invoke(connection, remoteUid, elapsed);
+        }
+
+        public override void OnUserOffline(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason)
+        {
+            if (EventOnUserOffline == null) return;
+            EventOnUserOffline.Invoke(connection, remoteUid, reason);
+        }
+
+        [Obsolete]
+        public override void OnUserMuteAudio(RtcConnection connection, uint remoteUid, bool muted)
+        {
+            if (EventOnUserMuteAudio == null) return;
+            EventOnUserMuteAudio.Invoke(connection, remoteUid, muted);
+        }
+
+        [Obsolete]
+        public override void OnUserMuteVideo(RtcConnection connection, uint remoteUid, bool muted)
+        {
+            if (EventOnUserMuteVideo == null) return;
+            EventOnUserMuteVideo.Invoke(connection, remoteUid, muted);
+        }
+
+        [Obsolete]
+        public override void OnUserEnableVideo(RtcConnection connection, uint remoteUid, bool enabled)
+        {
+            if (EventOnUserEnableVideo == null) return;
+            EventOnUserEnableVideo.Invoke(connection, remoteUid, enabled);
+        }
+
+        [Obsolete]
+        public override void OnUserEnableLocalVideo(RtcConnection connection, uint remoteUid, bool enabled)
+        {
+            if (EventOnUserEnableLocalVideo == null) return;
+            EventOnUserEnableLocalVideo.Invoke(connection, remoteUid, enabled);
+        }
+
+        public override void OnUserStateChanged(RtcConnection connection, uint remoteUid, uint state)
+        {
+            if (EventOnUserStateChanged == null) return;
+            EventOnUserStateChanged.Invoke(connection, remoteUid, state);
+        }
+
+        public override void OnApiCallExecuted(int err, string api, string result)
+        {
+            if (EventOnApiCallExecuted == null) return;
+            EventOnApiCallExecuted.Invoke(err, api, result);
+        }
+
+        public override void OnLocalAudioStats(RtcConnection connection, LocalAudioStats stats)
+        {
+            if (EventOnLocalAudioStats == null) return;
+            EventOnLocalAudioStats.Invoke(connection, stats);
+        }
+
+        public override void OnRemoteAudioStats(RtcConnection connection, RemoteAudioStats stats)
+        {
+            if (EventOnRemoteAudioStats == null) return;
+            EventOnRemoteAudioStats.Invoke(connection, stats);
+        }
+
+        public override void OnLocalVideoStats(RtcConnection connection, LocalVideoStats stats)
+        {
+            if (EventOnLocalVideoStats == null) return;
+            EventOnLocalVideoStats.Invoke(connection, stats);
+        }
+
+        public override void OnRemoteVideoStats(RtcConnection connection, RemoteVideoStats stats)
+        {
+            if (EventOnRemoteVideoStats == null) return;
+            EventOnRemoteVideoStats.Invoke(connection, stats);
+        }
+
+        public override void OnCameraReady()
+        {
+            if (EventOnCameraReady == null) return;
+            EventOnCameraReady.Invoke();
+        }
+
+        public override void OnCameraFocusAreaChanged(int x, int y, int width, int height)
+        {
+            if (EventOnCameraFocusAreaChanged == null) return;
+            EventOnCameraFocusAreaChanged.Invoke(x, y, width, height);
+        }
+
+        public override void OnCameraExposureAreaChanged(int x, int y, int width, int height)
+        {
+            if (EventOnCameraExposureAreaChanged == null) return;
+            EventOnCameraExposureAreaChanged.Invoke(x, y, width, height);
+        }
+
+        public override void OnFacePositionChanged(int imageWidth, int imageHeight, Rectangle vecRectangle, int[] vecDistance, int numFaces)
+        {
+            if (EventOnFacePositionChanged == null) return;
+            EventOnFacePositionChanged.Invoke(imageWidth, imageHeight, vecRectangle, vecDistance, numFaces);
+        }
+
+        public override void OnVideoStopped()
+        {
+            if (EventOnVideoStopped == null) return;
+            EventOnVideoStopped.Invoke();
+        }
+
+        public override void OnAudioMixingStateChanged(AUDIO_MIXING_STATE_TYPE state, AUDIO_MIXING_REASON_TYPE reason)
+        {
+            if (EventOnAudioMixingStateChanged == null) return;
+            EventOnAudioMixingStateChanged.Invoke(state, reason);
+        }
+
+        public override void OnRhythmPlayerStateChanged(RHYTHM_PLAYER_STATE_TYPE state, RHYTHM_PLAYER_ERROR_TYPE errorCode)
+        {
+            if (EventOnRhythmPlayerStateChanged == null) return;
+            EventOnRhythmPlayerStateChanged.Invoke(state, errorCode);
+        }
+
+        public override void OnConnectionLost(RtcConnection connection)
+        {
+            if (EventOnConnectionLost == null) return;
+            EventOnConnectionLost.Invoke(connection);
+        }
+
+        public override void OnConnectionInterrupted(RtcConnection connection)
+        {
+            if (EventOnConnectionInterrupted == null) return;
+            EventOnConnectionInterrupted.Invoke(connection);
+        }
+
+        public override void OnConnectionBanned(RtcConnection connection)
+        {
+            if (EventOnConnectionBanned == null) return;
+            EventOnConnectionBanned.Invoke(connection);
+        }
+
+        public override void OnStreamMessage(RtcConnection connection, uint remoteUid, int streamId, byte[] data, uint length, UInt64 sentTs)
+        {
+            if (EventOnStreamMessage == null) return;
+            EventOnStreamMessage.Invoke(connection, remoteUid, streamId, data, length, sentTs);
+        }
+
+        public override void OnStreamMessageError(RtcConnection connection, uint remoteUid, int streamId, int code, int missed, int cached)
+        {
+            if (EventOnStreamMessageError == null) return;
+            EventOnStreamMessageError.Invoke(connection, remoteUid, streamId, code, missed, cached);
+        }
+
+        public override void OnRequestToken(RtcConnection connection)
+        {
+            if (EventOnRequestToken == null) return;
+            EventOnRequestToken.Invoke(connection);
+        }
+
+        public override void OnTokenPrivilegeWillExpire(RtcConnection connection, string token)
+        {
+            if (EventOnTokenPrivilegeWillExpire == null) return;
+            EventOnTokenPrivilegeWillExpire.Invoke(connection, token);
+        }
+
+        public override void OnFirstLocalAudioFramePublished(RtcConnection connection, int elapsed)
+        {
+            if (EventOnFirstLocalAudioFramePublished == null) return;
+            EventOnFirstLocalAudioFramePublished.Invoke(connection, elapsed);
+        }
+
+        public override void OnFirstRemoteAudioFrame(RtcConnection connection, uint userId, int elapsed)
+        {
+            if (EventOnFirstRemoteAudioFrame == null) return;
+            EventOnFirstRemoteAudioFrame.Invoke(connection, userId, elapsed);
+        }
+
+        public override void OnFirstRemoteAudioDecoded(RtcConnection connection, uint uid, int elapsed)
+        {
+            if (EventOnFirstRemoteAudioDecoded == null) return;
+            EventOnFirstRemoteAudioDecoded.Invoke(connection, uid, elapsed);
+        }
+
+        public override void OnLocalAudioStateChanged(RtcConnection connection, LOCAL_AUDIO_STREAM_STATE state, LOCAL_AUDIO_STREAM_ERROR error)
+        {
+            if (EventOnLocalAudioStateChanged == null) return;
+            EventOnLocalAudioStateChanged.Invoke(connection, state, error);
+        }
+
+        public override void OnRemoteAudioStateChanged(RtcConnection connection, uint remoteUid, REMOTE_AUDIO_STATE state, REMOTE_AUDIO_STATE_REASON reason, int elapsed)
+        {
+            if (EventOnRemoteAudioStateChanged == null) return;
+            EventOnRemoteAudioStateChanged.Invoke(connection, remoteUid, state, reason, elapsed);
+        }
+
+        public override void OnActiveSpeaker(RtcConnection connection, uint uid)
+        {
+            if (EventOnActiveSpeaker == null) return;
+            EventOnActiveSpeaker.Invoke(connection, uid);
+        }
+
+        public override void OnClientRoleChanged(RtcConnection connection, CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole)
+        {
+            if (EventOnClientRoleChanged == null) return;
+            EventOnClientRoleChanged.Invoke(connection, oldRole, newRole);
+        }
+
+        public override void OnClientRoleChangeFailed(RtcConnection connection, CLIENT_ROLE_CHANGE_FAILED_REASON reason, CLIENT_ROLE_TYPE currentRole)
+        {
+            if (EventOnClientRoleChangeFailed == null) return;
+            EventOnClientRoleChangeFailed.Invoke(connection, reason, currentRole);
+        }
+
+        public override void OnAudioDeviceVolumeChanged(MEDIA_DEVICE_TYPE deviceType, int volume, bool muted)
+        {
+            if (EventOnAudioDeviceVolumeChanged == null) return;
+            EventOnAudioDeviceVolumeChanged.Invoke(deviceType, volume, muted);
+        }
+
+        public override void OnRtmpStreamingStateChanged(string url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR_TYPE errCode)
+        {
+            if (EventOnRtmpStreamingStateChanged == null) return;
+            EventOnRtmpStreamingStateChanged.Invoke(url, state, errCode);
+        }
+
+        public override void OnRtmpStreamingEvent(string url, RTMP_STREAMING_EVENT eventCode)
+        {
+            if (EventOnRtmpStreamingEvent == null) return;
+            EventOnRtmpStreamingEvent.Invoke(url, eventCode);
+        }
+
+        //public override void OnStreamPublished(string url, int error)
+        //{
+        //    if (EventOnStreamPublished == null) return;
+        //    EventOnStreamPublished.Invoke(url, error);
+        //}
+
+        //[Obsolete]
+        //public override void OnStreamUnpublished(string url)
+        //{
+        //    if (EventOnStreamUnpublished == null) return;
+        //    EventOnStreamUnpublished.Invoke(url);
+        //}
+
+        public override void OnTranscodingUpdated()
+        {
+            if (EventOnTranscodingUpdated == null) return;
+            EventOnTranscodingUpdated.Invoke();
+        }
+
+        public override void OnAudioRoutingChanged(int routing)
+        {
+            if (EventOnAudioRoutingChanged == null) return;
+            EventOnAudioRoutingChanged.Invoke(routing);
+        }
+
+        //public override void OnAudioSessionRestrictionResume()
+        //{
+        //    if (EventOnAudioSessionRestrictionResume == null) return;
+        //    EventOnAudioSessionRestrictionResume.Invoke();
+        //}
+
+        public override void OnChannelMediaRelayStateChanged(int state, int code)
+        {
+            if (EventOnChannelMediaRelayStateChanged == null) return;
+            EventOnChannelMediaRelayStateChanged.Invoke(state, code);
+        }
+
+        public override void OnChannelMediaRelayEvent(int code)
+        {
+            if (EventOnChannelMediaRelayEvent == null) return;
+            EventOnChannelMediaRelayEvent.Invoke(code);
+        }
+
+        public override void OnLocalPublishFallbackToAudioOnly(bool isFallbackOrRecover)
+        {
+            if (EventOnLocalPublishFallbackToAudioOnly == null) return;
+            EventOnLocalPublishFallbackToAudioOnly.Invoke(isFallbackOrRecover);
+        }
+
+        public override void OnRemoteSubscribeFallbackToAudioOnly(uint uid, bool isFallbackOrRecover)
+        {
+            if (EventOnRemoteSubscribeFallbackToAudioOnly == null) return;
+            EventOnRemoteSubscribeFallbackToAudioOnly.Invoke(uid, isFallbackOrRecover);
+        }
+
+        public override void OnRemoteAudioTransportStats(RtcConnection connection, uint remoteUid, UInt16 delay, UInt16 lost, UInt16 rxKBitRate)
+        {
+            if (EventOnRemoteAudioTransportStats == null) return;
+            EventOnRemoteAudioTransportStats.Invoke(connection, remoteUid, delay, lost, rxKBitRate);
+        }
+
+        public override void OnRemoteVideoTransportStats(RtcConnection connection, uint remoteUid, UInt16 delay, UInt16 lost, UInt16 rxKBitRate)
+        {
+            if (EventOnRemoteVideoTransportStats == null) return;
+            EventOnRemoteVideoTransportStats.Invoke(connection, remoteUid, delay, lost, rxKBitRate);
+        }
+
+        public override void OnConnectionStateChanged(RtcConnection connection, CONNECTION_STATE_TYPE state, CONNECTION_CHANGED_REASON_TYPE reason)
+        {
+            if (EventOnConnectionStateChanged == null) return;
+            EventOnConnectionStateChanged.Invoke(connection, state, reason);
+        }
+
+        public override void OnWlAccMessage(RtcConnection connection, WLACC_MESSAGE_REASON reason, WLACC_SUGGEST_ACTION action, string wlAccMsg)
+        {
+            if (EventOnWlAccMessage == null) return;
+            EventOnWlAccMessage.Invoke(connection, reason, action, wlAccMsg);
+        }
+
+        public override void OnWlAccStats(RtcConnection connection, WlAccStats currentStats, WlAccStats averageStats)
+        {
+            if (EventOnWlAccStats == null) return;
+            EventOnWlAccStats.Invoke(connection, currentStats, averageStats);
+        }
+
+        public override void OnNetworkTypeChanged(RtcConnection connection, NETWORK_TYPE type)
+        {
+            if (EventOnNetworkTypeChanged == null) return;
+            EventOnNetworkTypeChanged.Invoke(connection, type);
+        }
+
+        public override void OnEncryptionError(RtcConnection connection, ENCRYPTION_ERROR_TYPE errorType)
+        {
+            if (EventOnEncryptionError == null) return;
+            EventOnEncryptionError.Invoke(connection, errorType);
+        }
+
+        public override void OnUploadLogResult(RtcConnection connection, string requestId, bool success, UPLOAD_ERROR_REASON reason)
+        {
+            if (EventOnUploadLogResult == null) return;
+            EventOnUploadLogResult.Invoke(connection, requestId, success, reason);
+        }
+
+        public override void OnUserAccountUpdated(RtcConnection connection, uint remoteUid, string userAccount)
+        {
+            if (EventOnUserAccountUpdated == null) return;
+            EventOnUserAccountUpdated.Invoke(connection, remoteUid, userAccount);
+        }
+
+        public override void OnPermissionError(PERMISSION_TYPE permissionType)
+        {
+            if (EventOnPermissionError == null) return;
+            EventOnPermissionError.Invoke(permissionType);
+        }
+
+        public override void OnLocalUserRegistered(uint uid, string userAccount)
+        {
+            if (EventOnLocalUserRegistered == null) return;
+            EventOnLocalUserRegistered.Invoke(uid, userAccount);
+        }
+
+        public override void OnUserInfoUpdated(uint uid, UserInfo info)
+        {
+            if (EventOnUserInfoUpdated == null) return;
+            EventOnUserInfoUpdated.Invoke(uid, info);
+        }
+
+        public override void OnAudioSubscribeStateChanged(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState)
+        {
+            if (EventOnAudioSubscribeStateChanged == null) return;
+            EventOnAudioSubscribeStateChanged.Invoke(channel, uid, oldState, newState, elapseSinceLastState);
+        }
+
+        public override void OnVideoSubscribeStateChanged(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState)
+        {
+            if (EventOnVideoSubscribeStateChanged == null) return;
+            EventOnVideoSubscribeStateChanged.Invoke(channel, uid, oldState, newState, elapseSinceLastState);
+        }
+
+        public override void OnAudioPublishStateChanged(string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState)
+        {
+            if (EventOnAudioPublishStateChanged == null) return;
+            EventOnAudioPublishStateChanged.Invoke(channel, oldState, newState, elapseSinceLastState);
+        }
+
+        public override void OnVideoPublishStateChanged(VIDEO_SOURCE_TYPE source, string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState)
+        {
+            if (EventOnVideoPublishStateChanged == null) return;
+            EventOnVideoPublishStateChanged.Invoke(source, channel, oldState, newState, elapseSinceLastState);
+        }
+
+        public override void OnExtensionEvent(string provider, string extension, string key, string value)
+        {
+            if (EventOnExtensionEvent == null) return;
+            EventOnExtensionEvent.Invoke(provider, extension, key, value);
+        }
+
+        public override void OnExtensionStarted(string provider, string extension)
+        {
+            if (EventOnExtensionStarted == null) return;
+            EventOnExtensionStarted.Invoke(provider, extension);
+        }
+
+        public override void OnExtensionStopped(string provider, string extension)
+        {
+            if (EventOnExtensionStopped == null) return;
+            EventOnExtensionStopped.Invoke(provider, extension);
+        }
+
+        public override void OnExtensionError(string provider, string extension, int error, string message)
+        {
+            if (EventOnExtensionErrored == null) return;
+            EventOnExtensionErrored.Invoke(provider, extension, error, message);
+        }
+
+        public override void OnDirectCdnStreamingStateChanged(DIRECT_CDN_STREAMING_STATE state, DIRECT_CDN_STREAMING_ERROR error, string message)
+        {
+            if (EventOnExtensionErrored == null) return;
+            EventOnDirectCdnStreamingStateChanged.Invoke(state, error, message);
+        }
+
+        public override void OnDirectCdnStreamingStats(DirectCdnStreamingStats stats)
+        {
+            if (EventOnExtensionErrored == null) return;
+            EventOnDirectCdnStreamingStats.Invoke(stats);
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Event/RtcEngineEventHandler.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e6b2bf1e4dbc74abeb10a6a188ffb45e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 264 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioDeviceManager.cs

@@ -0,0 +1,264 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// Audio device management methods.
+    /// </summary>
+    ///
+    public abstract class IAudioDeviceManager
+    {
+        #region PlaybackDevices
+        ///
+        /// <summary>
+        /// Enumerates the audio playback devices.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Success: Returns a DeviceInfo array, which includes the device ID and device name of all the audio playback devices.Failure: NULL.
+        /// </returns>
+        ///
+        public abstract DeviceInfo[] EnumeratePlaybackDevices();
+
+        ///
+        /// <summary>
+        /// Sets the audio playback device.
+        /// </summary>
+        ///
+        /// <param name="deviceId"> The ID of the specified audio playback device. You can get the device ID by calling EnumeratePlaybackDevices . Plugging or unplugging the audio device does not change the value of deviceId.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetPlaybackDevice(string deviceId);
+
+        ///
+        /// <summary>
+        /// Retrieves the audio playback device associated with the device ID.
+        /// </summary>
+        ///
+        /// <param name="deviceId"> Output parameter. The device ID of the audio playback device. </param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetPlaybackDevice(ref string deviceId);
+
+        ///
+        /// <summary>
+        /// Retrieves the audio playback device associated with the device ID.
+        /// </summary>
+        ///
+        /// <param name="deviceId"> The device ID of the recording device. </param>
+        ///
+        /// <param name="deviceName"> The device name of the recording device. </param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetPlaybackDeviceInfo(ref string deviceId, ref string deviceName);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetPlaybackDeviceVolume(int volume);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int GetPlaybackDeviceVolume(ref int volume);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetPlaybackDeviceMute(bool mute);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int GetPlaybackDeviceMute(ref bool mute);
+
+        ///
+        /// <summary>
+        /// Starts the audio playback device test.
+        /// This method tests whether the audio playback device works properly. Once a user starts the test, the SDK plays an audio file specified by the user. If the user can hear the audio, the playback device works properly.After calling this method, the SDK triggers the OnAudioVolumeIndication callback every 100 ms, reporting uid = 1 and the volume information of the playback device.Ensure that you call this method before joining a channel.
+        /// </summary>
+        ///
+        /// <param name="testAudioFilePath"> The path of the audio file. The data format is string in UTF-8.Supported file formats: wav, mp3, m4a, and aac.Supported file sample rates: 8000, 16000, 32000, 44100, and 48000 Hz.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int StartPlaybackDeviceTest(string testAudioFilePath);
+
+        ///
+        /// <summary>
+        /// Stops the audio playback device test.
+        /// This method stops the audio playback device test. You must call this method to stop the test after calling the StartPlaybackDeviceTest method.Ensure that you call this method before joining a channel.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int StopPlaybackDeviceTest();
+
+        ///
+        /// <summary>
+        /// Sets the audio playback device used by the SDK to follow the system default audio playback device.
+        /// </summary>
+        ///
+        /// <param name="enable"> Whether to follow the system default audio playback device:true: Follow. The SDK immediately switches the audio playback device when the system default audio playback device changes.false: Do not follow. The SDK switches the audio playback device to the system default audio playback device only when the currently used audio playback device is disconnected.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int FollowSystemPlaybackDevice(bool enable);
+        #endregion
+
+        #region RecordingDevices
+        ///
+        /// <summary>
+        /// Enumerates the audio capture devices.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Success: A DeviceInfo array, which includes the device ID and device name of all the audio capture devices.Failure: NULL.
+        /// </returns>
+        ///
+        public abstract DeviceInfo[] EnumerateRecordingDevices();
+
+        ///
+        /// <summary>
+        /// Sets the audio recording device.
+        /// </summary>
+        ///
+        /// <param name="deviceId"> The ID of the audio recording device. You can get the device ID by calling EnumerateRecordingDevices . Plugging or unplugging the audio device does not change the value of deviceId.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetRecordingDevice(string deviceId);
+
+        ///
+        /// <summary>
+        /// Gets the current audio recording device.
+        /// </summary>
+        ///
+        /// <param name="deviceId"> Output parameter. The device ID of the recording device. </param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetRecordingDevice(ref string deviceId);
+
+        ///
+        /// <summary>
+        /// Retrieves the volume of the audio recording device.
+        /// </summary>
+        ///
+        /// <param name="deviceId"> The device ID of the recording device. </param>
+        ///
+        /// <param name="deviceName"> The device name of the recording device. </param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetRecordingDeviceInfo(ref string deviceId, ref string deviceName);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetRecordingDeviceVolume(int volume);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int GetRecordingDeviceVolume(ref int volume);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetRecordingDeviceMute(bool mute);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int GetRecordingDeviceMute(ref bool mute);
+
+        ///
+        /// <summary>
+        /// Starts the audio capture device test.
+        /// This method tests whether the audio capture device works properly. After calling this method, the SDK triggers the OnAudioVolumeIndication callback at the time interval set in this method, which reports uid = 0 and the volume information of the capturing device.Ensure that you call this method before joining a channel.
+        /// </summary>
+        ///
+        /// <param name="indicationInterval"> The time interval (ms) at which the SDK triggers the OnAudioVolumeIndication callback. Agora recommends a setting greater than 200 ms. This value must not be less than 10 ms; otherwise, you can not receive the OnAudioVolumeIndication callback.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int StartRecordingDeviceTest(int indicationInterval);
+
+        ///
+        /// <summary>
+        /// Stops the audio capture device test.
+        /// This method stops the audio capture device test. You must call this method to stop the test after calling the StartRecordingDeviceTest method.Ensure that you call this method before joining a channel.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int StopRecordingDeviceTest();
+
+        ///
+        /// <summary>
+        /// Sets the audio recording device used by the SDK to follow the system default audio recording device.
+        /// </summary>
+        ///
+        /// <param name="enable"> Whether to follow the system default audio recording device:true: Follow. The SDK immediately switches the audio recording device when the system default audio recording device changes.false: Do not follow. The SDK switches the audio recording device to the system default audio recording device only when the currently used audio recording device is disconnected.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int FollowSystemRecordingDevice(bool enable);
+        #endregion
+
+        #region AudioDevice
+        ///
+        /// <summary>
+        /// Starts an audio device loopback test.
+        /// This method tests whether the local audio capture device and playback device are working properly. Once the test starts, the audio recording device records the local audio, and the audio playback device plays the captured audio. The SDK triggers two independent OnAudioVolumeIndication callbacks at the time interval set in this method, which reports the volume information of the capture device (uid = 0) and the volume information of the playback device (uid = 1) respectively.Ensure that you call this method before joining a channel.This method tests local audio devices and does not report the network conditions.
+        /// </summary>
+        ///
+        /// <param name="indicationInterval"> The time interval (ms) at which the SDK triggers the OnAudioVolumeIndication callback. Agora recommends setting a value greater than 200 ms. This value must not be less than 10 ms; otherwise, you can not receive the OnAudioVolumeIndication callback.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int StartAudioDeviceLoopbackTest(int indicationInterval);
+
+        ///
+        /// <summary>
+        /// Stops the audio device loopback test.
+        /// Ensure that you call this method before joining a channel.Ensure that you call this method to stop the loopback test after calling the StartAudioDeviceLoopbackTest method.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int StopAudioDeviceLoopbackTest();
+        #endregion
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioDeviceManager.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 86099b7c6cb1d41d4abc63f7a896bb8c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 66 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioEncodedFrameObserver.cs

@@ -0,0 +1,66 @@
+using System;
+
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The encoded audio observer.
+    /// </summary>
+    ///
+    public abstract class IAudioEncodedFrameObserver
+    {
+        ///
+        /// <summary>
+        /// Gets the encoded audio data of the local user.
+        /// After calling RegisterAudioEncodedFrameObserver and setting the encoded audio as AUDIO_ENCODED_FRAME_OBSERVER_POSITION_RECORD, you can get the encoded audio data of the local user from this callback.
+        /// </summary>
+        ///
+        /// <param name="frameBufferPtr"> The audio buffer.</param>
+        ///
+        /// <param name="length"> The data length (byte).</param>
+        ///
+        /// <param name="audioEncodedFrameInfo"> Audio information after encoding. See EncodedAudioFrameInfo .</param>
+        ///
+        public virtual void OnRecordAudioEncodedFrame(IntPtr frameBufferPtr, int length, 
+                                                    EncodedAudioFrameInfo audioEncodedFrameInfo)
+        {
+
+        }
+
+        ///
+        /// <summary>
+        /// Gets the encoded audio data of all remote users.
+        /// After calling RegisterAudioEncodedFrameObserver and setting the encoded audio as AUDIO_ENCODED_FRAME_OBSERVER_POSITION_PLAYBACK, you can get encoded audio data of all remote users through this callback.
+        /// </summary>
+        ///
+        /// <param name="frameBufferPtr"> The audio buffer.</param>
+        ///
+        /// <param name="length"> The data length (byte).</param>
+        ///
+        /// <param name="audioEncodedFrameInfo"> Audio information after encoding. See EncodedAudioFrameInfo .</param>
+        ///
+        public virtual void OnPlaybackAudioEncodedFrame(IntPtr frameBufferPtr, int length, 
+                                                    EncodedAudioFrameInfo audioEncodedFrameInfo)
+        {
+
+        }
+
+        ///
+        /// <summary>
+        /// Gets the mixed and encoded audio data of the local and all remote users.
+        /// After calling RegisterAudioEncodedFrameObserver and setting the audio profile as AUDIO_ENCODED_FRAME_OBSERVER_POSITION_MIXED, you can get the mixed and encoded audio data of the local and all remote users through this callback.
+        /// </summary>
+        ///
+        /// <param name="frameBufferPtr"> The audio buffer.</param>
+        ///
+        /// <param name="length"> The data length (byte).</param>
+        ///
+        /// <param name="audioEncodedFrameInfo"> Audio information after encoding. See EncodedAudioFrameInfo .</param>
+        ///
+        public virtual void OnMixedAudioEncodedFrame(IntPtr frameBufferPtr, int length, 
+                                                    EncodedAudioFrameInfo audioEncodedFrameInfo)
+        {
+
+        }
+    };
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioEncodedFrameObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: eae1d844021c24a6f88263bcc30ba65f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 166 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioFrameObserver.cs

@@ -0,0 +1,166 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The audio frame observer.
+    /// </summary>
+    ///
+    public abstract class IAudioFrameObserver
+    {
+        ///
+        /// <summary>
+        /// Gets the captured audio frame.
+        /// If you want to set the format of the captured audio frame, Agora recommends that you call the RegisterAudioFrameObserver method to set the format of the audio frame after calling the GetRecordAudioParams method to register an audio frame observer. The SDK calculates the sampling interval according to the AudioParams set in the GetRecordAudioParams callback return value, and triggers the OnRecordAudioFrame callback according to the sampling interval.
+        /// </summary>
+        ///
+        /// <param name="audioFrame"> The raw audio data. See AudioFrame .</param>
+        ///
+        /// <param name="channelId"> The channel ID.</param>
+        ///
+        /// <returns>
+        /// Reserved for future use.
+        /// </returns>
+        ///
+        public virtual bool OnRecordAudioFrame(string channelId ,AudioFrame audioFrame)
+        {
+            return true;
+        }
+
+        ///
+        /// <summary>
+        /// Gets the audio frame for playback.
+        /// If you want to set the format of the audio frame for playback, Agora recommends that you call the RegisterAudioFrameObserver method to set the format of the audio frame after calling the SetPlaybackAudioFrameParameters method to register an audio frame observer.
+        /// </summary>
+        ///
+        /// <param name="audio_Frame"> The raw audio data. See AudioFrame .</param>
+        ///
+        /// <param name="channelId"> The channel ID.</param>
+        ///
+        /// <returns>
+        /// Reserved for future use.
+        /// </returns>
+        ///
+        public virtual bool OnPlaybackAudioFrame(string channelId, AudioFrame audio_frame)
+        {
+            return true;
+        }
+
+        ///
+        /// <summary>
+        /// Retrieves the mixed captured and playback audio frame.
+        /// This callback only returns the single-channel data.If you want to set the format of the mixed captured and playback audio frame, Agora recommends you call the RegisterAudioFrameObserver method to set the format of the audio frames after calling the SetMixedAudioFrameParameters method to register an audio frame observer.
+        /// </summary>
+        ///
+        /// <param name="audio_Frame"> The raw audio data. See AudioFrame .</param>
+        ///
+        /// <param name="channelId"> The channel ID.</param>
+        ///
+        /// <returns>
+        /// Reserved for future use.
+        /// </returns>
+        ///
+        public virtual bool OnMixedAudioFrame(string channelId, AudioFrame audio_frame)
+        {
+            return true;
+        }
+
+        ///
+        /// @ignore
+        ///
+        public virtual int GetObservedAudioFramePosition()
+        {
+            return (int)AUDIO_FRAME_POSITION.AUDIO_FRAME_POSITION_NONE; 
+        }
+
+        ///
+        /// <summary>
+        /// Sets the audio format for the OnPlaybackAudioFrame callback.
+        /// You need to register the callback when calling the RegisterAudioFrameObserver method. After you successfully register the audio observer, the SDK triggers this callback, and you can set the audio format in the return value of this callback.The SDK calculates the sample interval according to the AudioParams you set in the return value of this callback.Sample interval = samplePerCall/(sampleRate × channel).Ensure that the sample interval ≥ 0.01 (s).The SDK triggers the OnPlaybackAudioFrame callback according to the sampling interval.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Sets the audio format. See AudioParams .
+        /// </returns>
+        ///
+        public virtual AudioParams GetPlaybackAudioParams()
+        {
+            return new AudioParams();
+        }
+
+        ///
+        /// <summary>
+        /// Sets the audio format for the OnRecordAudioFrame callback.
+        /// You need to register the callback when calling the RegisterAudioFrameObserver method. After you successfully register the audio observer, the SDK triggers this callback, and you can set the audio format in the return value of this callback.The SDK calculates the sample interval according to the AudioParams you set in the return value of this callback.Sample interval = samplePerCall/(sampleRate × channel).Ensure that the sample interval ≥ 0.01 (s).The SDK triggers the OnRecordAudioFrame callback according to the sampling interval.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Sets the audio format. See AudioParams .
+        /// </returns>
+        ///
+        public virtual AudioParams GetRecordAudioParams()
+        {
+            return new AudioParams();
+        }
+
+        ///
+        /// <summary>
+        /// Sets the audio format for the OnMixedAudioFrame callback.
+        /// You need to register the callback when calling the RegisterAudioFrameObserver method. After you successfully register the audio observer, the SDK triggers this callback, and you can set the audio format in the return value of this callback.The SDK calculates the sample interval according to the AudioParams you set in the return value of this callback.Sample interval = samplePerCall/(sampleRate × channel).Ensure that the sample interval ≥ 0.01 (s).The SDK triggers the OnMixedAudioFrame callback according to the sampling interval.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Sets the audio format. See AudioParams .
+        /// </returns>
+        ///
+        public virtual AudioParams GetMixedAudioParams()
+        {
+            return new AudioParams();
+        }
+
+        ///
+        /// <summary>
+        /// Retrieves the audio frame of a specified user before mixing.
+        /// </summary>
+        ///
+        /// <param name="channel_id"> The channel ID.</param>
+        ///
+        /// <param name="uid"> The user ID of the specified user.</param>
+        ///
+        /// <param name="audio_Frame"> The raw audio data. See AudioFrame .</param>
+        ///
+        /// <returns>
+        /// Reserved for future use.
+        /// </returns>
+        ///
+        public virtual bool OnPlaybackAudioFrameBeforeMixing(string channel_id,
+                                                        uint uid,
+                                                        AudioFrame audio_frame)
+        {
+            return false;
+        }
+
+        ///
+        /// <summary>
+        /// Retrieves the audio frame of a specified user before mixing.
+        /// </summary>
+        ///
+        /// <param name="channel_id"> The channel name that the audio frame came from.</param>
+        ///
+        /// <param name="uid"> The ID of the user sending the audio frame.</param>
+        ///
+        /// <param name="audio_Frame"> The raw audio data. See AudioFrame .</param>
+        ///
+        /// <returns>
+        /// Reserved for future use.
+        /// </returns>
+        ///
+        public virtual bool OnPlaybackAudioFrameBeforeMixing(string channel_id,
+                                                        string uid,
+                                                        AudioFrame audio_frame)
+        {
+            return false;
+        }
+    }
+
+
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioFrameObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6189ec56be1524046a1c43c415051d1c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 46 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioSpectrumObserver.cs

@@ -0,0 +1,46 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The audio spectrum observer.
+    /// </summary>
+    ///
+    public abstract class IAudioSpectrumObserver
+    {
+        ///
+        /// <summary>
+        /// Gets the statistics of a local audio spectrum.
+        /// After successfully calling RegisterAudioSpectrumObserver to implement the OnLocalAudioSpectrum callback in IAudioSpectrumObserver and calling EnableAudioSpectrumMonitor to enable audio spectrum monitoring, the SDK will trigger the callback as the time interval you set to report the received remote audio data spectrum.
+        /// </summary>
+        ///
+        /// <param name="data"> The audio spectrum data of the local user. See AudioSpectrumData .</param>
+        ///
+        /// <returns>
+        /// Whether you have received the spectrum data:true: Spectrum data is received.false: No spectrum data is received.
+        /// </returns>
+        ///
+        public virtual bool OnLocalAudioSpectrum(AudioSpectrumData data)
+        {
+            return true;
+        }
+
+        ///
+        /// <summary>
+        /// Gets the remote audio spectrum.
+        /// After successfully calling RegisterAudioSpectrumObserver to implement the OnRemoteAudioSpectrum callback in the IAudioSpectrumObserver and calling EnableAudioSpectrumMonitor to enable audio spectrum monitoring, the SDK will trigger the callback as the time interval you set to report the received remote audio data spectrum.
+        /// </summary>
+        ///
+        /// <param name="spectrums"> The audio spectrum information of the remote user, see UserAudioSpectrumInfo . The number of arrays is the number of remote users monitored by the SDK. If the array is null, it means that no audio spectrum of remote users is detected.</param>
+        ///
+        /// <param name="spectrumNumber"> The number of remote users.</param>
+        ///
+        /// <returns>
+        /// Whether you have received the spectrum data:true: Spectrum data is received.false: No spectrum data is received.
+        /// </returns>
+        ///
+        public virtual bool OnRemoteAudioSpectrum(UserAudioSpectrumInfo[] spectrums, uint spectrumNumber)
+        {
+            return true;
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IAudioSpectrumObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: af7f8b3dcc399458882296fc4a24cb39
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 651 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayer.cs

@@ -0,0 +1,651 @@
+using System;
+
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// This class provides media player functions and supports multiple instances.
+    /// </summary>
+    ///
+    public abstract class IMediaPlayer
+    {
+        ///
+        /// <summary>
+        /// Releases all the resources occupied by the media player.
+        /// </summary>
+        ///
+        public abstract void Dispose();
+
+        ///
+        /// <summary>
+        /// Gets the ID of the media player.
+        /// </summary>
+        ///
+        /// <returns>
+        /// >= 0: Success. The ID of the media player.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetId();
+
+        ///
+        /// <summary>
+        /// Adds callback event for media player.
+        /// </summary>
+        ///
+        /// <param name="engineEventHandler"> Callback events to be added. </param>
+        ///
+        public abstract void InitEventHandler(IMediaPlayerSourceObserver engineEventHandler);
+
+        ///
+        /// <summary>
+        /// Registers an audio frame observer object.
+        /// You need to implement the IMediaPlayerAudioFrameObserver class in this method and register callbacks according to your scenarios. After you successfully register the video frame observer, the SDK triggers the registered callbacks each time a video frame is received.
+        /// </summary>
+        ///
+        /// <param name="observer"> The audio frame observer, reporting the reception of each audio frame. See IMediaPlayerAudioFrameObserver .</param>
+        ///
+        public abstract void RegisterAudioFrameObserver(IMediaPlayerAudioFrameObserver observer);
+
+        ///
+        /// <summary>
+        /// Registers an audio frame observer object.
+        /// </summary>
+        ///
+        /// <param name="observer"> The audio frame observer, reporting the reception of each audio frame. See IAudioFrameObserver .</param>
+        ///
+        /// <param name="mode"> The use mode of the audio frame. See RAW_AUDIO_FRAME_OP_MODE_TYPE .</param>
+        ///
+        public abstract void RegisterAudioFrameObserver(IMediaPlayerAudioFrameObserver observer, RAW_AUDIO_FRAME_OP_MODE_TYPE mode);
+
+        ///
+        /// <summary>
+        /// Unregisters an audio frame observer.
+        /// </summary>
+        ///
+        public abstract void UnregisterAudioFrameObserver();
+
+        ///
+        /// @ignore
+        ///
+        public abstract void RegisterMediaPlayerAudioSpectrumObserver(IAudioSpectrumObserver observer, int intervalInMS);
+
+        ///
+        /// @ignore
+        ///
+        public abstract void UnregisterMediaPlayerAudioSpectrumObserver();
+
+        ///
+        /// <summary>
+        /// Opens the media resource.
+        /// This method is called asynchronously. If you need to play a media file, make sure you receive the OnPlayerSourceStateChanged callback reporting PLAYER_STATE_OPEN_COMPLETED before calling the Play method to play the file.
+        /// </summary>
+        ///
+        /// <param name="url"> The path of the media file. Both local path and online path are supported.On the Android platform, if you need to open a file in URI format, use Open .</param>
+        ///
+        /// <param name="startPos"> The starting position (ms) for playback. Default value is 0.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Open(string url, Int64 startPos);
+
+        ///
+        /// <summary>
+        /// Opens the custom media resource file.
+        /// Deprecated:This method is deprecated. This method allows you to open custom media resource files. For example, you can call this method to open encrypted media resources.
+        /// </summary>
+        ///
+        /// <param name="playerId"> The ID of the media player.</param>
+        ///
+        /// <param name="startPos"> The starting position (ms) for playback. The default value is 0.</param>
+        ///
+        /// <param name="provider"> The callback for custom media resource files. See IMediaPlayerCustomDataProvider .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int OpenWithCustomSource(Int64 startPos, IMediaPlayerCustomDataProvider provider);
+
+        ///
+        /// <summary>
+        /// Opens a media file and configures the playback scenarios.
+        /// This method supports opening media files of different sources, including a custom media source, and allows you to configure the playback scenarios.
+        /// </summary>
+        ///
+        /// <param name="source"> Media resources. See MediaSource .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int OpenWithMediaSource(MediaSource source);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetSoundPositionParams(float pan, float gain);
+
+        ///
+        /// <summary>
+        /// Plays the media file.
+        /// After calling Open or Seek, you can call this method to play the media file.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Play();
+
+        ///
+        /// <summary>
+        /// Pauses the playback.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Pause();
+
+        ///
+        /// <summary>
+        /// Stops playing the media track.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Stop();
+
+        ///
+        /// <summary>
+        /// Resumes playing the media file.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Resume();
+
+        ///
+        /// <summary>
+        /// Seeks to a new playback position.
+        /// After successfully calling this method, you will receive the OnPlayerEvent callback, reporting the result of the seek operation to the new playback position.To play the media file from a specific position, do the following:Call this method to seek to the position you want to begin playback.Call the Play method to play the media file.
+        /// </summary>
+        ///
+        /// <param name="newPos"> The new playback position (ms).</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Seek(Int64 newPos);
+
+        ///
+        /// <summary>
+        /// Gets the duration of the media resource.
+        /// </summary>
+        ///
+        /// <param name="duration"> Output parameter. The total duration (ms) of the media file.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetDuration(ref Int64 duration);
+
+        ///
+        /// <summary>
+        /// Gets current local playback progress.
+        /// </summary>
+        ///
+        /// <param name="pos"> The playback position (ms) of the audio effect file.</param>
+        ///
+        /// <returns>
+        /// Returns the current playback progress (ms) if the call succeeds.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int GetPlayPosition(ref Int64 pos);
+
+        ///
+        /// <summary>
+        /// Gets the number of the media streams in the media resource.
+        /// Call this method after calling Open .
+        /// </summary>
+        ///
+        /// <param name="count"> Output parameter. The number of the media streams in the media resource.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int GetStreamCount(ref Int64 count);
+
+        ///
+        /// <summary>
+        /// Gets the detailed information of the media stream.
+        /// Call this method after calling GetStreamCount .
+        /// </summary>
+        ///
+        /// <param name="index"> The index of the media stream.</param>
+        ///
+        /// <param name="info"> An output parameter. The detailed information of the media stream. See PlayerStreamInfo .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetStreamInfo(Int64 index, ref PlayerStreamInfo info);
+
+        ///
+        /// <summary>
+        /// Sets the loop playback.
+        /// If you want to loop, call this method and set the number of the loops.When the loop finishes, the SDK triggers OnPlayerSourceStateChanged and reports the playback state as PLAYER_STATE_PLAYBACK_ALL_LOOPS_COMPLETED.
+        /// </summary>
+        ///
+        /// <param name="loopCount"> The number of times the audio effect loops:</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetLoopCount(int loopCount);
+
+        ///
+        /// <summary>
+        /// Sets the channel mode of the current audio file.
+        /// Call this method after calling Open .
+        /// </summary>
+        ///
+        /// <param name="speed"> The playback speed. Agora recommends that you limit this value to between 50 and 400, defined as follows:50: Half the original speed.100: The original speed.400: 4 times the original speed.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetPlaybackSpeed(int speed);
+
+        ///
+        /// <summary>
+        /// Selects the audio track used during playback.
+        /// After getting the track index of the audio file, you can call this method to specify any track to play. For example, if different tracks of a multi-track file store songs in different languages, you can call this method to set the playback language.You need to call this method after calling GetStreamInfo to get the audio stream index value.
+        /// </summary>
+        ///
+        /// <param name="index"> The index of the audio track.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SelectAudioTrack(int index);
+
+        ///
+        /// <summary>
+        /// Sets the private options for the media player.
+        /// The media player supports setting private options by key and value. Under normal circumstances, you do not need to know the private option settings, and just use the default option settings.Ensure that you call this method before Open .If you need to push streams with SEI into the CDN, callSetPlayerOption [1/2] ("sei_data_with_uuid", 1); otherwise, the loss of SEI might occurs.
+        /// </summary>
+        ///
+        /// <param name="key"> The key of the option.</param>
+        ///
+        /// <param name="value"> The value of the key.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetPlayerOption(string key, int value);
+
+        ///
+        /// <summary>
+        /// Sets the private options for the media player.
+        /// The media player supports setting private options by key and value. Under normal circumstances, you do not need to know the private option settings, and just use the default option settings. Ensure that you call this method before Open .
+        /// If you need to push streams with SEI into the CDN, callSetPlayerOption [1/2] ("sei_data_with_uuid", 1); otherwise, the loss of SEI might occurs.
+        /// </summary>
+        ///
+        /// <param name="key"> The key of the option.</param>
+        ///
+        /// <param name="value"> The value of the key.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetPlayerOption(string key, string value);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int TakeScreenshot(string filename);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SelectInternalSubtitle(int index);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetExternalSubtitle(string url);
+
+        ///
+        /// <summary>
+        /// Gets current playback state.
+        /// </summary>
+        ///
+        /// <returns>
+        /// The current playback state. See MEDIA_PLAYER_STATE .
+        /// </returns>
+        ///
+        public abstract MEDIA_PLAYER_STATE GetState();
+
+        ///
+        /// <summary>
+        /// Sets whether to mute the media file.
+        /// </summary>
+        ///
+        /// <param name="muted"> Whether to mute the media file:true: Mute the media file.false: (Default) Unmute the media file.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Mute(bool muted);
+
+        ///
+        /// <summary>
+        /// Reports whether the media resource is muted.
+        /// </summary>
+        ///
+        /// <param name="muted"> Output parameter. Whether the media file is muted:true: Mute the media file.false: The media file is unmuted.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetMute(ref bool muted);
+
+        ///
+        /// <summary>
+        /// Adjusts the local playback volume.
+        /// </summary>
+        ///
+        /// <param name="volume"> The local playback volume, which ranges from 0 to 100:0: Mute.100: (Default) The original volume.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int AdjustPlayoutVolume(int volume);
+
+        ///
+        /// <summary>
+        /// Gets the local playback volume.
+        /// </summary>
+        ///
+        /// <param name="volume"> Output parameter. The local playback volume, which ranges from 0 to 100:0: Mute.100: (Default) The original volume.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetPlayoutVolume(ref int volume);
+
+        ///
+        /// <summary>
+        /// Adjusts the volume of the media file for publishing.
+        /// After connected to the Agora server, you can call this method to adjust the volume of the media file heard by the remote user.
+        /// </summary>
+        ///
+        /// <param name="volume"> The volume, which ranges from 0 to 400:0: Mute.100: (Default) The original volume.400: Four times the original volume (amplifying the audio signals by four times).</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int AdjustPublishSignalVolume(int volume);
+
+        ///
+        /// <summary>
+        /// Gets the volume of the media file for publishing.
+        /// </summary>
+        ///
+        /// <param name="volume"> Output parameter. The remote playback volume.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetPublishSignalVolume(ref int volume);
+
+        ///
+        /// <summary>
+        /// Sets the view.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetView();
+
+        ///
+        /// <summary>
+        /// Sets the render mode of the media player.
+        /// </summary>
+        ///
+        /// <param name="renderMode"> Sets the render mode of the view. See RENDER_MODE_TYPE .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetRenderMode(RENDER_MODE_TYPE renderMode);
+
+        ///
+        /// <summary>
+        /// Sets the channel mode of the current audio file.
+        /// In a stereo music file, the left and right channels can store different audio data. According to your needs, you can set the channel mode to original mode, left channel mode, right channel mode, or mixed channel mode. For example, in the KTV scenario, the left channel of the music file stores the musical accompaniment, and the right channel stores the singing voice. If you only need to listen to the accompaniment, call this method to set the channel mode of the music file to left channel mode; if you need to listen to the accompaniment and the singing voice at the same time, call this method to set the channel mode to mixed channel mode.Call this method after calling Open .This method only applies to stereo audio files.
+        /// </summary>
+        ///
+        /// <param name="mode"> The channel mode. See AUDIO_DUAL_MONO_MODE .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetAudioDualMonoMode(AUDIO_DUAL_MONO_MODE mode);
+
+        ///
+        /// @ignore
+        ///
+        public abstract string GetPlayerSdkVersion();
+
+        ///
+        /// @ignore
+        ///
+        public abstract string GetPlaySrc();
+
+        ///
+        /// <summary>
+        /// Sets the pitch of the current media resource.
+        /// Call this method after calling Open .
+        /// </summary>
+        ///
+        /// <param name="pitch"> Sets the pitch of the local music file by the chromatic scale. The default value is 0, which means keeping the original pitch. The value ranges from -12 to 12, and the pitch value between consecutive values is a chromatic value. The greater the absolute value of this parameter, the higher or lower the pitch of the local music file.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetAudioPitch(int pitch);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetSpatialAudioParams(SpatialAudioParams spatial_audio_params);
+
+        ///
+        /// <summary>
+        /// Opens a media resource and requests all the CDN routes of the media resources through the self-developed scheduling center.
+        /// This method is called asynchronously. If you need to play a media file, make sure you receive the OnPlayerSourceStateChanged callback reporting PLAYER_STATE_OPEN_COMPLETED before calling the Play method to play the file.After you call this method, Agora opens the media resources and tries to obtain all the CDN routes for playing the media resource. By default, Agora uses the first CDN route for playing, and you can call the SwitchAgoraCDNLineByIndex method to switch routes.If you want to ensure the security of the connection and media files, to determine the sign and the ts fields for authentication. Once the fields are determined, use them as the query parameter of the URL to update the URL of the media resource. For example:The URL of the media file to be opened: rtmp://$domain/$appName/$streamNameThe URL updated by the authentication of the media file to be opened: rtmp://$domain/$appName/$streamName?ts=$ts&sign=$signAuthentication information:sign: An encrypted string calculated according to the MD5 algorithm based on authKey, appName, streamName, and ts. You need to for your authKey.ts: The timestamp when the authentication information expires. You can set the validity period of the authentication information according to your scenarios. For example, 24h or 1h30m20s.
+        /// </summary>
+        ///
+        /// <param name="src"> The URL of the media resource.</param>
+        ///
+        /// <param name="startPos"> The starting position (ms) for playback. The default value is 0. This value can be empty if the media resource to be played is live streams.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int OpenWithAgoraCDNSrc(string src, Int64 startPos);
+
+        ///
+        /// <summary>
+        /// Gets the number of CDN routes for the media resource.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Returns the number of CDN routes for the media resource, if the method call succeeds.≤ 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetAgoraCDNLineCount();
+
+        ///
+        /// <summary>
+        /// Changes the CDN route for playing the media resource.
+        /// After calling OpenWithAgoraCDNSrc to open the media resource, you can call this method if you want to change the CDN routes for playing the media resource.Call this method after calling OpenWithAgoraCDNSrc .You can call this method either before or after Play . If you call this method before Play, the switch does not take effect immediately. The SDK waits for the playback to complete before switching the CDN line of the media resource.
+        /// </summary>
+        ///
+        /// <param name="index"> The index of the CDN routes.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SwitchAgoraCDNLineByIndex(int index);
+
+        ///
+        /// <summary>
+        /// Gets the CDN routes index of the current media resource.
+        /// </summary>
+        ///
+        /// <returns>
+        /// The number of CDN routes for the media resource, if the method call succeeds. The value range is [0, GetAgoraCDNLineCount()).&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetCurrentAgoraCDNIndex();
+
+        ///
+        /// <summary>
+        /// Enables/Disables the automatic switch of the CDN routes for playing the media resource.
+        /// You can call this method if you want the SDK to automatically switch the CDN routes according to your network conditions.Call this method before OpenWithAgoraCDNSrc .
+        /// </summary>
+        ///
+        /// <param name="enable"> Whether to enable the automatic switch of the CDN routes for playing the media resource:true: Enables the automatic switch of the CDN routes.false: (Default) Disables the automatic switch of the CDN routes.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int EnableAutoSwitchAgoraCDN(bool enable);
+
+        ///
+        /// <summary>
+        /// Renew the authentication information for the URL of the media resource to be played.
+        /// When the authentication information expires (exceeds the ts field), you can call the OpenWithAgoraCDNSrc method to reopen the media resource or the SwitchAgoraCDNSrc method to switch the media resource, and then pass in the authenticated URL (with the ts field updated) of the media resource.If your authentication information expires when you call the SwitchAgoraCDNLineByIndex to switch the CDN route for playing the media resource, you need to call this method to pass in the updated authentication information to update the authentication information of the media resource URL. After updating the authentication information, you need to call SwitchAgoraCDNLineByIndex to complete the route switching.To avoid frequent expiration of authentication information, ensure that you set the ts field appropriately or according to the scenario requirements.
+        /// </summary>
+        ///
+        /// <param name="token"> The authentication field. See the sign field of the authentication information.</param>
+        ///
+        /// <param name="ts"> The timestamp when the authentication information expires. See the ts field of the authentication information.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int RenewAgoraCDNSrcToken(string token, Int64 ts);
+
+        ///
+        /// <summary>
+        /// Switches the media resource being played.
+        /// If you want to ensure the security of the connection and media files, to determine the sign and the ts fields for authentication. Once the fields are determined, use them as the query parameter of the URL to update the URL of the media resource. For example:The URL of the media file to be opened: rtmp://$domain/$appName/$streamNameThe URL updated by the authentication of the media file to be opened: rtmp://$domain/$appName/$streamName?ts=$ts&sign=$signAuthentication information:sign: An encrypted string calculated according to the MD5 algorithm based on authKey, appName, streamName, and ts. You need to for your authKey.ts: The timestamp when the authentication information expires. You can set the validity period of the authentication information according to your scenarios. For example, 24h or 1h30m20s.If you want to customize the CDN routes for playing the media resource, call this method to switch media resources. Agora changes the CDN route through the self-developed scheduling center to improve the viewing experience. If you do not need to customize CDN routes for playing the media resource, call the SwitchSrc method to switch media resources.
+        /// Call this method after calling OpenWithAgoraCDNSrc .You can call this method either before or after Play . If you call this method before Play, the SDK waits for you to call Play before completing the route switch.
+        /// </summary>
+        ///
+        /// <param name="src"> The URL of the media resource.</param>
+        ///
+        /// <param name="syncPts"> Whether to synchronize the playback position (ms) before and after the switch:true: Synchronize the playback position before and after the switch.false: (Default) Do not synchronize the playback position before and after the switch.falseMake sure to set this parameter as if you need to play live streams, or the switch fails. If you need to play on-demand streams, you can set the value of this parameter according to your scenarios.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SwitchAgoraCDNSrc(string src, bool syncPts = false);
+
+        ///
+        /// <summary>
+        /// Switches the media resource being played.
+        /// You can call this method to switch the media resource to be played according to the current network status. For example:When the network is poor, the media resource to be played is switched to a media resource address with a lower bitrate.When the network is good, the media resource to be played is switched to a media resource address with a higher bitrate.After calling this method, if you receive the OnPlayerEvent event in the PLAYER_EVENT_SWITCH_COMPLETE callback, the switch is successful; If you receive the OnPlayerEvent event in the PLAYER_EVENT_SWITCH_ERROR callback, the switch fails.Ensure that you call this method after Open .To ensure normal playback, pay attention to the following when calling this method:Do not call this method when playback is paused.Do not call the Seek method during switching.Before switching the media resource, make sure that the playback position does not exceed the total duration of the media resource to be switched.
+        /// </summary>
+        ///
+        /// <param name="src"> The URL of the media resource.</param>
+        ///
+        /// <param name="syncPts"> Whether to synchronize the playback position (ms) before and after the switch:true: Synchronize the playback position before and after the switch.false: (Default) Do not synchronize the playback position before and after the switch.Make sure to set this parameter as false if you need to play live streams, or the switch fails. If you need to play on-demand streams, you can set the value of this parameter according to your scenarios.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SwitchSrc(string src, bool syncPts = true);
+
+        ///
+        /// <summary>
+        /// Preloads a media resource.
+        /// You can call this method to preload a media resource into the playlist. If you need to preload multiple media resources, you can call this method multiple times.If the preload is successful and you want to play the media resource, call PlayPreloadedSrc ; if you want to clear the playlist, call Stop .Agora does not support preloading duplicate media resources to the playlist. However, you can preload the media resources that are being played to the playlist again.
+        /// </summary>
+        ///
+        /// <param name="src"> The URL of the media resource.</param>
+        ///
+        /// <param name="startPos"> The starting position (ms) for playing after the media resource is preloaded to the playlist. When preloading a live stream, set this parameter to 0.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int PreloadSrc(string src, Int64 startPos);
+
+        ///
+        /// <summary>
+        /// Plays preloaded media resources.
+        /// After calling the PreloadSrc method to preload the media resource into the playlist, you can call this method to play the preloaded media resource. After calling this method, if you receive the OnPlayerSourceStateChanged callback which reports the PLAYER_STATE_PLAYING state, the playback is successful.If you want to change the preloaded media resource to be played, you can call this method again and specify the URL of the new media resource that you want to preload. If you want to replay the media resource, you need to call PreloadSrc to preload the media resource to the playlist again before playing. If you want to clear the playlist, call the Stop method.If you call this method when playback is paused, this method does not take effect until playback is resumed.
+        /// </summary>
+        ///
+        /// <param name="src"> The URL of the media resource in the playlist must be consistent with the src set by the PreloadSrc method; otherwise, the media resource cannot be played.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int PlayPreloadedSrc(string src);
+
+        ///
+        /// <summary>
+        /// Unloads media resources that are preloaded.
+        /// This method cannot release the media resource being played.
+        /// </summary>
+        ///
+        /// <param name="src"> The URL of the media resource.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int UnloadSrc(string src);
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayer.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c1f11163ef4244bd5b836b279190ce50
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 158 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCacheManager.cs

@@ -0,0 +1,158 @@
+using System;
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// This class provides methods to manage cached media files.
+    /// </summary>
+    ///
+    public abstract class IMediaPlayerCacheManager
+    {
+        ///
+        /// <summary>
+        /// Deletes all cached media files in the media player.
+        /// The cached media file currently being played will not be deleted.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int RemoveAllCaches();
+      
+        ///
+        /// <summary>
+        /// Deletes a cached media file that is the least recently used.
+        /// You can call this method to delete a cached media file when the storage space for the cached files is about to reach its limit. After you call this method, the SDK deletes the cached media file that is least used.The cached media file currently being played will not be deleted.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.
+        /// &lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int RemoveOldCache();
+     
+        ///
+        /// <summary>
+        /// Deletes a cached media file.
+        /// The cached media file currently being played will not be deleted.
+        /// </summary>
+        ///
+        /// <param name="uri"> The URI (Uniform Resource Identifier) of the media file to be deleted.</param>
+        ///
+        /// <returns>
+        /// 0: Success.
+        /// &lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int RemoveCacheByUri(string uri) ;
+      
+        ///
+        /// <summary>
+        /// Sets the storage path for the media files that you want to cache.
+        /// Make sure IRtcEngine is initialized before you call this method.
+        /// </summary>
+        ///
+        /// <param name="path"> The absolute path of the media files to be cached. Ensure that the directory for the media files exists and is writable.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int SetCacheDir(string path) ;
+      
+        ///
+        /// <summary>
+        /// Sets the maximum number of media files that can be cached.
+        /// </summary>
+        ///
+        /// <param name="count"> The maximum number of media files that can be cached. The default value is 1,000.</param>
+        ///
+        /// <returns>
+        /// 0: Success.
+        /// &lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int SetMaxCacheFileCount(int count);
+     
+        ///
+        /// <summary>
+        /// Sets the maximum size of the aggregate storage space for cached media files.
+        /// </summary>
+        ///
+        /// <param name="cacheSize"> The maximum size (bytes) of the aggregate storage space for cached media files. The default value is 1 GB.</param>
+        ///
+        /// <returns>
+        /// 0: Success.
+        /// &lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int SetMaxCacheFileSize(Int64 cacheSize);
+       
+        ///
+        /// <summary>
+        /// Sets whether to delete cached media files automatically.
+        /// If you enable this function to remove cached media files automatically, when the cached media files exceed either the number or size limit you set, the SDK automatically deletes the least recently used cache file.
+        /// </summary>
+        ///
+        /// <param name="enable"> Whether to enable the SDK to delete cached media files automatically:true: Delete cached media files automatically.false: (Default) Do not delete cached media files automatically.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int EnableAutoRemoveCache(bool enable);
+      
+        ///
+        /// <summary>
+        /// Gets the storage path of the cached media files.
+        /// If you have not called the SetCacheDir method to set the storage path for the media files to be cached before calling this method, you get the default storage path used by the SDK.
+        /// </summary>
+        ///
+        /// <param name="path"> An output parameter; the storage path for the media file to be cached.</param>
+        ///
+        /// <param name="length"> An input parameter; the maximum length of the cache file storage path string. Fill in according to the cache file storage path string you obtained from path.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int GetCacheDir(out string path, int length);
+      
+        ///
+        /// <summary>
+        /// Gets the maximum number of media files that can be cached.
+        /// By default, the maximum number of media files that can be cached is 1,000.
+        /// </summary>
+        ///
+        /// <returns>
+        /// > 0: The call succeeds and returns the maximum number of media files that can be cached.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int GetMaxCacheFileCount();
+       
+        ///
+        /// <summary>
+        /// Gets the maximum size of the aggregate storage space for cached media files.
+        /// By default, the maximum size of the aggregate storage space for cached media files is 1 GB. You can call the SetMaxCacheFileSize method to set the limit according to your scenarios.
+        /// </summary>
+        ///
+        /// <returns>
+        /// > 0: The call succeeds and returns the maximum size (in bytes) of the aggregate storage space for cached media files.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract Int64 GetMaxCacheFileSize();
+       
+        ///
+        /// <summary>
+        /// Gets the number of media files that are cached.
+        /// </summary>
+        ///
+        /// <returns>
+        /// >= 0: The call succeeds and returns the number of media files that are cached.&lt; 0: Failure. See MEDIA_PLAYER_ERROR .
+        /// </returns>
+        ///
+        public abstract int GetCacheFileCount();
+    };
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCacheManager.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 26065e546cc8e4c66b40188ef229bfdc
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 48 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCustomDataProvider.cs

@@ -0,0 +1,48 @@
+using System;
+
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The callback for custom media resource files.
+    /// </summary>
+    ///
+    public abstract class IMediaPlayerCustomDataProvider
+    {
+        ///
+        /// <summary>
+        /// Occurs when the SDK seeks the media resource data.
+        /// </summary>
+        ///
+        /// <param name="offset"> An input parameter. The offset of the target position relative to the starting point, in bytes. The value can be positive or negative.</param>
+        ///
+        /// <param name="whence"> An input parameter. The starting point. You can set it as one of the following values:0: The starting point is the head of the data, and the actual data offset after seeking is offset.1: The starting point is the current position, and the actual data offset after seeking is the current position plus offset.2: The starting point is the end of the data, and the actual data offset after seeking is the whole data length plus offset.65536: Do not perform position seeking, return the file size. Agora recommends that you use this parameter value when playing pure audio files such as MP3 and WAV.</param>
+        ///
+        /// <returns>
+        /// When when ce is 65536, the media file size is returned.When when ce is 0, 1, or 2, the actual data offset after the seeking is returned.-1: Seeking failed.
+        /// </returns>
+        ///
+        public virtual Int64 OnSeek(Int64 offset, int whence)
+        {
+            return 0;
+        }
+
+        ///
+        /// <summary>
+        /// Occurs when the SDK reads the media resource data.
+        /// </summary>
+        ///
+        /// <param name="bufferPtr"> An input parameter. Data buffer (bytes). Write the bufferSize data reported by the SDK into this parameter.</param>
+        ///
+        /// <param name="bufferSize"> The length of the data buffer (bytes).</param>
+        ///
+        /// <returns>
+        /// If the data is read successfully, pass in the length of the data (bytes) you actually read in the return value.If reading the data fails, pass in 0 in the return value.
+        /// </returns>
+        ///
+        public virtual int OnReadData(IntPtr bufferPtr, int bufferSize)
+        {
+            return 0;
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerCustomDataProvider.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fa1ac2aeba7fc484583b4a45e2410490
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 27 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerFrameObserver.cs

@@ -0,0 +1,27 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The audio frame observer for the media player.
+    /// </summary>
+    ///
+    public abstract class IMediaPlayerAudioFrameObserver
+    {
+        ///
+        /// <summary>
+        /// Occurs each time the player receives an audio frame.
+        /// After registering the audio frame observer, the callback occurs every time the player receives an audio frame, reporting the detailed information of the audio frame.
+        /// </summary>
+        ///
+        /// <param name="videoFrame"> Audio frame information. See AudioPcmFrame .</param>
+        ///
+        /// <returns>
+        /// Reserved for future use.
+        /// </returns>
+        ///
+        public virtual bool OnFrame(AudioPcmFrame videoFrame)
+        {
+            return true;
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerFrameObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2065edef7e55c432690cc59e33eab0f6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 127 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerSourceObserver.cs

@@ -0,0 +1,127 @@
+using System;
+
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// Provides callbacks for media players.
+    /// </summary>
+    ///
+    public abstract class IMediaPlayerSourceObserver
+    {
+        ///
+        /// <summary>
+        /// Reports the playback state change.
+        /// When the state of the media player changes, the SDK triggers this callback to report the current playback state.
+        /// </summary>
+        ///
+        /// <param name="state"> The playback state, see MEDIA_PLAYER_STATE .</param>
+        ///
+        /// <param name="ec"> The error code. See MEDIA_PLAYER_ERROR .</param>
+        ///
+        public virtual void OnPlayerSourceStateChanged(MEDIA_PLAYER_STATE state, MEDIA_PLAYER_ERROR ec) { }
+
+        ///
+        /// <summary>
+        /// Reports current playback progress.
+        /// When playing media files, the SDK triggers this callback every one second to report current playback progress.
+        /// </summary>
+        ///
+        /// <param name="position_ms"> The playback position (ms) of media files.</param>
+        ///
+        public virtual void OnPositionChanged(Int64 position_ms) { }
+
+        ///
+        /// <summary>
+        /// Reports the playback event.
+        /// After calling the Seek method, the SDK triggers the callback to report the results of the seek operation.
+        /// </summary>
+        ///
+        /// <param name="eventCode"> The playback event. See MEDIA_PLAYER_EVENT .</param>
+        ///
+        /// <param name="elapsedTime"> The time (ms) when the event occurs.</param>
+        ///
+        /// <param name="message"> Information about the event.</param>
+        ///
+        public virtual void OnPlayerEvent(MEDIA_PLAYER_EVENT eventCode, Int64 elapsedTime, string message) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the media metadata is received.
+        /// The callback occurs when the player receives the media metadata and reports the detailed information of the media metadata.
+        /// </summary>
+        ///
+        /// <param name="data"> The detailed data of the media metadata.</param>
+        ///
+        /// <param name="length"> The data length (bytes).</param>
+        ///
+        public virtual void OnMetaData(byte[] data, int length) { }
+
+        ///
+        /// <summary>
+        /// Reports the playback duration that the buffered data can support.
+        /// When playing online media resources, the SDK triggers this callback every two seconds to report the playback duration that the currently buffered data can support.When the playback duration supported by the buffered data is less than the threshold (0 by default), the SDK returns PLAYER_EVENT_BUFFER_LOW.When the playback duration supported by the buffered data is greater than the threshold (0 by default), the SDK returns PLAYER_EVENT_BUFFER_RECOVER.
+        /// </summary>
+        ///
+        /// <param name="playCachedBuffer"> The playback duration (ms) that the buffered data can support.</param>
+        ///
+        public virtual void OnPlayBufferUpdated(Int64 playCachedBuffer) { }
+
+        ///
+        /// <summary>
+        /// Reports the events of preloaded media resources.
+        /// </summary>
+        ///
+        /// <param name="src"> The URL of the media resource.</param>
+        ///
+        /// <param name="@event"> Events that occur when media resources are preloaded. See PLAYER_PRELOAD_EVENT .</param>
+        ///
+        public virtual void OnPreloadEvent(string src, PLAYER_PRELOAD_EVENT @event) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the media file is played once.
+        /// </summary>
+        ///
+        public virtual void OnCompleted() { }
+
+        ///
+        /// <summary>
+        /// Occurs when the token is about to expire.
+        /// If the ts is about to expire when you call the SwitchAgoraCDNLineByIndex method to switch the CDN route for playing the media resource, the SDK triggers this callback to remind you to renew the authentication information. You need to call the RenewAgoraCDNSrcToken method to pass in the updated authentication information to update the authentication information of the media resource URL. After updating the authentication information, you need to call SwitchAgoraCDNLineByIndex to complete the route switching.
+        /// </summary>
+        ///
+        public virtual void OnAgoraCDNTokenWillExpire() { }
+
+        ///
+        /// <summary>
+        /// Occurs when the video bitrate of the media resource changes.
+        /// </summary>
+        ///
+        /// <param name="from"> Information about the video bitrate of the media resource being played. See SrcInfo .</param>
+        ///
+        /// <param name="to"> Information about the changed video bitrate of media resource being played. See SrcInfo .</param>
+        ///
+        public virtual void OnPlayerSrcInfoChanged(SrcInfo from, SrcInfo to) { }
+
+        ///
+        /// <summary>
+        /// Occurs when information related to the media player changes.
+        /// When the information about the media player changes, the SDK triggers this callback. You can use this callback for troubleshooting.
+        /// </summary>
+        ///
+        /// <param name="info"> Information related to the media player. See PlayerUpdatedInfo .</param>
+        ///
+        public virtual void OnPlayerInfoUpdated(PlayerUpdatedInfo info) { }
+
+        ///
+        /// <summary>
+        /// Reports the volume of the media player.
+        /// The SDK triggers this callback every 200 milliseconds to report the current volume of the media player.
+        /// </summary>
+        ///
+        /// <param name="volume"> The volume of the media player. The value ranges from 0 to 255.</param>
+        ///
+        public virtual void OnAudioVolumeIndication(int volume) { }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaPlayerSourceObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a8cc9442f8ca140399dd801b87e4b4af
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 58 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorder.cs

@@ -0,0 +1,58 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// Used for recording audio and video on the client.
+    /// IMediaRecorder can record the following:
+    /// The audio captured by the local microphone and encoded in AAC format.The video captured by the local camera and encoded by the SDK.
+    /// </summary>
+    ///
+    public abstract class IMediaRecorder
+    {
+        ///
+        /// <summary>
+        /// Registers one IMediaRecorderObserver object.
+        /// Make sure the IRtcEngine is initialized before you call this method.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="callback"> The callbacks for recording local audio and video streams. See IMediaRecorderObserver .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetMediaRecorderObserver(RtcConnection connection, IMediaRecorderObserver callback);
+
+        ///
+        /// <summary>
+        /// Starts recording the local audio and video.
+        /// After successfully getting the IMediaRecorder object by calling GetMediaRecorder , you can call this method to enable the recoridng of the local audio and video.This method can record the audio captured by the local microphone and encoded in AAC format, and the video captured by the local camera and encoded in H.264 format. The SDK can generate a recording file only when it detects audio and video streams; when there are no audio and video streams to be recorded or the audio and video streams are interrupted for more than five seconds, the SDK stops the recording and triggers the OnRecorderStateChanged(RECORDER_STATE_ERROR, RECORDER_ERROR_NO_STREAM) callback.Once the recording is started, if the video resolution is changed, the SDK stops the recording; if the sampling rate and audio channel changes, the SDK continues recording and generates audio files respectively.Call this method after joining a channel.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="config"> The recording configuration. See MediaRecorderConfiguration .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.2: The parameter is invalid. Ensure the following:The specified path of the recording file exists and is writable.The specified format of the recording file is supported.The maximum recording duration is correctly set.4: IRtcEngine does not support the request. The recording is ongoing or the recording stops because an error occurs.7: A method is called before IRtcEngine is initialized.
+        /// </returns>
+        ///
+        public abstract int StartRecording(RtcConnection connection, MediaRecorderConfiguration config);
+
+        ///
+        /// <summary>
+        /// Stops recording the local audio and video.
+        /// After calling StartRecording , if you want to stop the recording, you must call this method; otherwise, the generated recording files may not be playable.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.-7: A method is called before IRtcEngine is initialized.
+        /// </returns>
+        ///
+        public abstract int StopRecording(RtcConnection connection);
+    };
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorder.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1d1f694421f6445b2bd5b28d8761834b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 32 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorderObserver.cs

@@ -0,0 +1,32 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The IMediaRecorderObserver class.
+    /// </summary>
+    ///
+    public abstract class IMediaRecorderObserver
+    {
+        ///
+        /// <summary>
+        /// Occurs when the recording state changes.
+        /// When the local audio or video recording state changes, the SDK triggers this callback to report the current recording state and the reason for the change.
+        /// </summary>
+        ///
+        /// <param name="state"> The current recording state. See RecorderState .</param>
+        ///
+        /// <param name="error"> The reason for the state change. See RecorderErrorCode .</param>
+        ///
+        public virtual void OnRecorderStateChanged(RecorderState state, RecorderErrorCode error) {}
+
+        ///
+        /// <summary>
+        /// Occurs when the recording information is updated.
+        /// After you successfully enable the local audio and video recording, the SDK periodically triggers this callback based on the value of recorderInfoUpdateInterval set in MediaRecorderConfiguration . This callback reports the file name, duration, and size of the current recording file.
+        /// </summary>
+        ///
+        /// <param name="info"> The information about the file that is recorded. See RecorderInfo .</param>
+        ///
+        public virtual void OnRecorderInfoUpdated(RecorderInfo info) {}
+    };
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMediaRecorderObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 104d2edbaf263457c97c2b4d190f5c91
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 56 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMetadataObserver.cs

@@ -0,0 +1,56 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The metadata observer.
+    /// </summary>
+    ///
+    public abstract class IMetadataObserver
+    {
+        ///
+        /// <summary>
+        /// Occurs when the SDK requests the maximum size of the metadata.
+        /// After successfully complete the registration by calling RegisterMediaMetadataObserver , the SDK triggers this callback once every video frame is sent. You need to specify the maximum size of the metadata in the return value of this callback.
+        /// </summary>
+        ///
+        /// <returns>
+        /// The maximum size of the buffer of the metadata that you want to use. The highest value is 1024 bytes. Ensure that you set the return value.
+        /// </returns>
+        ///
+        public virtual int GetMaxMetadataSize()
+        {
+            return 0;
+        }
+
+        ///
+        /// <summary>
+        /// Occurs when the SDK is ready to send metadata.
+        /// This callback is triggered when the SDK is ready to send metadata.
+        /// </summary>
+        ///
+        /// <param name="source_type"> Video data type. See VIDEO_SOURCE_TYPE .</param>
+        ///
+        /// <param name="metadata"> The metadata the user wants to send. See Metadata .</param>
+        ///
+        /// <returns>
+        /// true: Send it.false: Do not send it.
+        /// </returns>
+        ///
+        public virtual bool OnReadyToSendMetadata(ref Metadata metadata, VIDEO_SOURCE_TYPE source_type)
+        {
+            return false;
+        }
+
+        ///
+        /// <summary>
+        /// Occurs when the local user receives the metadata.
+        /// </summary>
+        ///
+        /// <param name="metadata"> The metadata received, see Metadata .</param>
+        ///
+        public virtual void OnMetadataReceived(Metadata metadata)
+        {
+
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IMetadataObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8ea6284bc28d342d68c93d56ceeb7fa2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

File diff suppressed because it is too large
+ 1009 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngine.cs


+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngine.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ba8fa656a254b491092c77818c11cddf
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1180 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngineEventHandler.cs

@@ -0,0 +1,1180 @@
+using System;
+
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// IRtcEngineEventHandlerThe SDK uses the interface to send event notifications to your app. Your app can get those notifications through methods that inherit this interface.
+    /// </summary>
+    ///
+    public abstract class IRtcEngineEventHandler
+    {
+        ///
+        /// <summary>
+        /// Occurs when a user joins a channel.
+        /// This callback notifies the application that a user joins a specified channel.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="elapsed"> The time elapsed (ms) from the local user calling JoinChannel [2/2] until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnJoinChannelSuccess(RtcConnection connection, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when a user rejoins the channel.
+        /// When a user loses connection with the server because of network problems, the SDK automatically tries to reconnect and triggers this callback upon reconnection.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="elapsed"> Time elapsed (ms) from the local user calling the JoinChannel [1/2] or JoinChannel [2/2] method until this callback is triggered.</param>
+        ///
+        public virtual void OnRejoinChannelSuccess(RtcConnection connection, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Reports the proxy connection state.
+        /// You can use this callback to listen for the state of the SDK connecting to a proxy. For example, when a user calls SetCloudProxy and joins a channel successfully, the SDK triggers this callback to report the user ID, the proxy type connected, and the time elapsed fromthe user calling JoinChannel [1/2] until this callback is triggered.
+        /// </summary>
+        ///
+        /// <param name="channel"> The channel name.</param>
+        ///
+        /// <param name="uid"> The user ID.</param>
+        ///
+        /// <param name="proxyType"> The proxy type connected. See CLOUD_PROXY_TYPE .</param>
+        ///
+        /// <param name="localProxyIp"> Reserved for future use.</param>
+        ///
+        /// <param name="elapsed"> The time elapsed (ms) from the user calling JoinChannel [1/2] until this callback is triggered.</param>
+        ///
+        public virtual void OnProxyConnected(string channel, uint uid, PROXY_TYPE proxyType, string localProxyIp, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Reports an error during SDK runtime.
+        /// This callback indicates that an error (concerning network or media) occurs during SDK runtime. In most cases, the SDK cannot fix the issue and resume running. The SDK requires the application to take action or informs the user about the issue.
+        /// </summary>
+        ///
+        /// <param name="err"> Error code. See ERROR_CODE_TYPE .</param>
+        ///
+        /// <param name="msg"> The error message.</param>
+        ///
+        public virtual void OnError(int err, string msg) { }
+
+        ///
+        /// <summary>
+        /// Reports the statistics of the audio stream from each remote user.
+        /// Deprecated:Please use OnRemoteAudioStats instead.The SDK triggers this callback once every two seconds to report the audio quality of each remote user/host sending an audio stream. If a channel has multiple users/hosts sending audio streams, the SDK triggers this callback as many times.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The user ID of the remote user sending the audio stream.</param>
+        ///
+        /// <param name="quality"> Audio quality of the user. 
+        ///  QUALITY_UNKNOWN(0): The quality is unknown.
+        ///  QUALITY_EXCELLENT(1): The quality is excellent.
+        ///  QUALITY_GOOD(2): The network quality seems excellent, but the bitrate can be slightly lower than excellent.
+        ///  QUALITY_POOR(3): Users can feel the communication is slightly impaired.
+        ///  QUALITY_BAD(4): Users cannot communicate smoothly.
+        ///  QUALITY_VBAD(5): The quality is so bad that users can barely communicate.
+        ///  QUALITY_DOWN(6): The network is down, and users cannot communicate at all.
+        ///  See QUALITY_TYPE .</param>
+        ///
+        /// <param name="delay"> The network delay (ms) from the sender to the receiver, including the delay caused by audio sampling pre-processing, network transmission, and network jitter buffering.</param>
+        ///
+        /// <param name="lost"> The packet loss rate (%) of the audio packet sent from the remote user.</param>
+        ///
+        public virtual void OnAudioQuality(RtcConnection connection, uint remoteUid, int quality, UInt16 delay, UInt16 lost) { }
+
+        ///
+        /// <summary>
+        /// Reports the last mile network probe result.
+        /// The SDK triggers this callback within 30 seconds after the app calls StartLastmileProbeTest .
+        /// </summary>
+        ///
+        /// <param name="result"> The uplink and downlink last-mile network probe test result. See LastmileProbeResult .</param>
+        ///
+        public virtual void OnLastmileProbeResult(LastmileProbeResult result) { }
+
+        ///
+        /// <summary>
+        /// Reports the volume information of users.
+        /// By default, this callback is disabled. You can enable it by calling EnableAudioVolumeIndication . Once this callback is enabled and users send streams in the channel, the SDK triggers the OnAudioVolumeIndication callback according to the time interval set in EnableAudioVolumeIndication. The SDK triggers two independent OnAudioVolumeIndication callbacks simultaneously, which separately report the volume information of the local user who sends a stream and the remote users (up to three) whose instantaneous volume is the highest.Once this callback is enabled, if the local user calls the MuteLocalAudioStream method for mute, the SDK continues to report the volume indication of the local user.20 seconds after a remote user whose volume is one of the three highest in the channel stops publishing the audio stream, the callback excludes this user's information; 20 seconds after all remote users stop publishing audio streams, the SDK stops triggering the callback for remote users.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="speakers"> The volume information of the users. See AudioVolumeInfo . An empty speakers array in the callback indicates that no remote user is in the channel or sending a stream at the moment.</param>
+        ///
+        /// <param name="speakerNumber"> The total number of users.In the callback for the local user, if the local user is sending streams, the value of speakerNumber is 1.In the callback for remote users, the value range of speakerNumber is [0,3]. If the number of remote users who send streams is greater than or equal to three, the value of speakerNumber is 3.</param>
+        ///
+        /// <param name="totalVolume"> The volume of the speaker. The value ranges between 0 (lowest volume) and 255 (highest volume).In the callback for the local user, totalVolume is the volume of the local user who sends a stream.In the callback for remote users, totalVolume is the sum of the volume of the remote users (up to three) whose instantaneous volume are the highest. </param>
+        ///
+        public virtual void OnAudioVolumeIndication(RtcConnection connection, AudioVolumeInfo[] speakers, uint speakerNumber, int totalVolume) { }
+
+        ///
+        /// <summary>
+        /// Occurs when a user leaves a channel.
+        /// This callback notifies the app that the user leaves the channel by calling LeaveChannel [1/2]
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="stats"> The statistics of the call. See RtcStats .</param>
+        ///
+        public virtual void OnLeaveChannel(RtcConnection connection, RtcStats stats) { }
+
+        ///
+        /// <summary>
+        /// Reports the statistics of the current call.
+        /// The SDK triggers this callback once every two seconds after the user joins the channel.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="stats"> Statistics of the RTC engine. See RtcStats .</param>
+        ///
+        public virtual void OnRtcStats(RtcConnection connection, RtcStats stats) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the audio device state changes.
+        /// This callback notifies the application that the system's audio device state is changed. For example, a headset is unplugged from the device.This method is for Windows and macOS only.
+        /// </summary>
+        ///
+        /// <param name="deviceId"> The device ID.</param>
+        ///
+        /// <param name="deviceType"> The evice type. See MEDIA_DEVICE_TYPE .</param>
+        ///
+        /// <param name="deviceState"> The device state.On macOS:0: The device is ready for use.8: The device is not connected.On Windows: see MEDIA_DEVICE_STATE_TYPE .</param>
+        ///
+        public virtual void OnAudioDeviceStateChanged(string deviceId, MEDIA_DEVICE_TYPE deviceType, MEDIA_DEVICE_STATE_TYPE deviceState) { }
+
+        [Obsolete("This method is deprecated, use onAudioMixingStateChanged instead")]
+        ///
+        /// <summary>
+        /// Occurs when the playback of the local music file finishes.
+        /// Deprecated:Please use OnAudioMixingStateChanged instead.After you call StartAudioMixing [2/2] to play a local music file, this callback occurs when the playback finishes. If the call StartAudioMixing [2/2] fails, the error code WARN_AUDIO_MIXING_OPEN_ERROR is returned.
+        /// </summary>
+        ///
+        public virtual void OnAudioMixingFinished() { }
+
+        ///
+        /// <summary>
+        /// Occurs when the playback of the local music file finishes.
+        /// This callback occurs when the local audio effect file finishes playing.
+        /// </summary>
+        ///
+        /// <param name="soundId"> The audio effect ID. The ID of each audio effect file is unique.</param>
+        ///
+        public virtual void OnAudioEffectFinished(int soundId) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnVideoDeviceStateChanged(string deviceId, MEDIA_DEVICE_TYPE deviceType, MEDIA_DEVICE_STATE_TYPE deviceState) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnMediaDeviceChanged(MEDIA_DEVICE_TYPE deviceType) { }
+
+        ///
+        /// <summary>
+        /// Reports the last mile network quality of each user in the channel.
+        /// This callback reports the last mile network conditions of each user in the channel. Last mile refers to the connection between the local device and Agora's edge server.The SDK triggers this callback once every two seconds. If a channel includes multiple users, the SDK triggers this callback as many times.txQuality is UNKNOWNrxQuality is UNKNOWN
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The user ID. The network quality of the user with this user ID is reported.</param>
+        ///
+        /// <param name="txQuality"> Uplink network quality rating of the user in terms of the transmission bit rate, packet loss rate, average RTT (Round-Trip Time) and jitter of the uplink network. This parameter is a quality rating helping you understand how well the current uplink network conditions can support the selected video encoder configuration. For example, a 1000 Kbps uplink network may be adequate for video frames with a resolution of 640 × 480 and a frame rate of 15 fps in the LIVE_BROADCASTING profile, but may be inadequate for resolutions higher than 1280 × 720. 
+        ///  QUALITY_UNKNOWN(0): The quality is unknown.
+        ///  QUALITY_EXCELLENT(1): The quality is excellent.
+        ///  QUALITY_GOOD(2): The network quality seems excellent, but the bitrate can be slightly lower than excellent.
+        ///  QUALITY_POOR(3): Users can feel the communication is slightly impaired.
+        ///  QUALITY_BAD(4): Users cannot communicate smoothly.
+        ///  QUALITY_VBAD(5): The quality is so bad that users can barely communicate.
+        ///  QUALITY_DOWN(6): The network is down, and users cannot communicate at all.
+        ///  See QUALITY_TYPE .</param>
+        ///
+        /// <param name="rxQuality"> Downlink network quality rating of the user in terms of packet loss rate, average RTT, and jitter of the downlink network. 
+        ///  QUALITY_UNKNOWN(0): The quality is unknown.
+        ///  QUALITY_EXCELLENT(1): The quality is excellent.
+        ///  QUALITY_GOOD(2): The network quality seems excellent, but the bitrate can be slightly lower than excellent.
+        ///  QUALITY_POOR(3): Users can feel the communication is slightly impaired.
+        ///  QUALITY_BAD(4): Users cannot communicate smoothly.
+        ///  QUALITY_VBAD(5): The quality is so bad that users can barely communicate.
+        ///  QUALITY_DOWN(6): The network is down, and users cannot communicate at all.
+        ///  See QUALITY_TYPE .</param>
+        ///
+        public virtual void OnNetworkQuality(RtcConnection connection, uint remoteUid, int txQuality, int rxQuality) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnIntraRequestReceived(RtcConnection connection) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the uplink network information changes.
+        /// The SDK triggers this callback when the uplink network information changes.This callback only applies to scenarios where you push externally encoded video data in H.264 format to the SDK.
+        /// </summary>
+        ///
+        /// <param name="info"> The uplink network information. See UplinkNetworkInfo .</param>
+        ///
+        public virtual void OnUplinkNetworkInfoUpdated(UplinkNetworkInfo info) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnDownlinkNetworkInfoUpdated(DownlinkNetworkInfo info) { }
+
+        ///
+        /// <summary>
+        /// Reports the last-mile network quality of the local user.
+        /// This callback reports the last-mile network conditions of the local user before the user joins the channel. Last mile refers to the connection between the local device and Agora's edge server.Before the user joins the channel, this callback is triggered by the SDK once StartLastmileProbeTest is called and reports the last-mile network conditions of the local user.
+        /// </summary>
+        ///
+        /// <param name="quality"> The last-mile network quality. 
+        ///  QUALITY_UNKNOWN(0): The quality is unknown.
+        ///  QUALITY_EXCELLENT(1): The quality is excellent.
+        ///  QUALITY_GOOD(2): The network quality seems excellent, but the bitrate can be slightly lower than excellent.
+        ///  QUALITY_POOR(3): Users can feel the communication is slightly impaired.
+        ///  QUALITY_BAD(4): Users cannot communicate smoothly.
+        ///  QUALITY_VBAD(5): The quality is so bad that users can barely communicate.
+        ///  QUALITY_DOWN(6): The network is down, and users cannot communicate at all.
+        ///  See QUALITY_TYPE .</param>
+        ///
+        public virtual void OnLastmileQuality(int quality) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the first local video frame is displayed on the local video view.
+        /// The SDK triggers this callback when the first local video frame is displayed on the local video view.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="width"> The width (px) of the first local video frame.</param>
+        ///
+        /// <param name="height"> The height (px) of the first local video frame.</param>
+        ///
+        /// <param name="elapsed"> Time elapsed (ms) from the local user calling JoinChannel [2/2] until the SDK triggers this callback. If you call StartPreview [1/2] JoinChannel [2/2], then this parameter is the time elapsed from calling the StartPreview [1/2]</param>
+        ///
+        public virtual void OnFirstLocalVideoFrame(RtcConnection connection, int width, int height, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the first video frame is published.
+        /// The SDK triggers this callback under one of the following circumstances:The local client enables the video module and calls JoinChannel [2/2] successfully.The local client calls MuteLocalVideoStream (true) and MuteLocalVideoStream(false) in sequence.The local client calls DisableVideo and EnableVideo in sequence.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="elapsed"> Time elapsed (ms) from the local user calling JoinChannel [2/2] until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnFirstLocalVideoFramePublished(RtcConnection connection, int elapsed) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnVideoSourceFrameSizeChanged(RtcConnection connection, VIDEO_SOURCE_TYPE sourceType, int width, int height) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the first remote video frame is received and decoded.
+        /// The SDK triggers this callback under one of the following circumstances:The remote user joins the channel and sends the video stream.The remote user stops sending the video stream and re-sends it after 15 seconds. Reasons for such an interruption include:The remote user leaves the channel.The remote user drops offline.The remote user calls MuteLocalVideoStream to stop sending the video stream.The remote user calls DisableVideo to disable video.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user sending the video stream.</param>
+        ///
+        /// <param name="width"> The width (px) of the video stream.</param>
+        ///
+        /// <param name="height"> The height (px) of the video stream.</param>
+        ///
+        /// <param name="elapsed"> The time elapsed (ms) from the local user calling JoinChannel [2/2] until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnFirstRemoteVideoDecoded(RtcConnection connection, uint remoteUid, int width, int height, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the video size or rotation of a specified user changes.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="sourceType"> The capture type of the custom video source. See VIDEO_SOURCE_TYPE .</param>
+        ///
+        /// <param name="uid"> The ID of the user whose video size or rotation changes. (The uid for the local user is 0. The video is the local user's video preview).</param>
+        ///
+        /// <param name="width"> The width (pixels) of the video stream.</param>
+        ///
+        /// <param name="height"> The height (pixels) of the video stream.</param>
+        ///
+        /// <param name="rotation"> The rotation information. The value range is [0,360).</param>
+        ///
+        public virtual void OnVideoSizeChanged(RtcConnection connection, VIDEO_SOURCE_TYPE sourceType, uint uid, int width, int height, int rotation) { }
+
+        ///
+        /// <summary>
+        /// Reports the result of video content moderation.
+        /// After calling enableContentInspect to enable the video content moderation, and setting the type parameter in ContentInspectConfig toCONTENT_INSPECT_MODERATION, the SDK triggers the onContentInspectResult callback and reports the result of video content moderation.
+        /// </summary>
+        ///
+        /// <param name="result"> The results of video content moderation. See CONTENT_INSPECT_RESULT .</param>
+        ///
+        public virtual void OnContentInspectResult(CONTENT_INSPECT_RESULT result) { }
+
+        ///
+        /// <summary>
+        /// Reports the result of taking a video snapshot.
+        /// After a successful takeSnapshot method call, the SDK triggers this callback to report whether the snapshot is successfully taken, as well as the details for that snapshot.
+        /// </summary>
+        ///
+        /// <param name="channel"> The channel name.</param>
+        ///
+        /// <param name="uid"> The user ID. A uid of 0 indicates the local user.</param>
+        ///
+        /// <param name="filePath"> The local path of the snapshot.</param>
+        ///
+        /// <param name="width"> The width (px) of the snapshot.</param>
+        ///
+        /// <param name="height"> The height (px) of the snapshot.</param>
+        ///
+        /// <param name="errCode"> The message that confirms success or gives the reason why the snapshot is not successfully taken:0: Success.&lt; 0: Failure:-1: The SDK fails to write data to a file or encode a JPEG image.-2: The SDK does not find the video stream of the specified user within one second after the takeSnapshot method call succeeds.-3: Calling the takeSnapshot method too frequently.</param>
+        ///
+        public virtual void OnSnapshotTaken(RtcConnection connection, uint uid, string filePath, int width, int height, int errCode) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the local video stream state changes.
+        /// When the state of the local video stream changes (including the state of the video capture and encoding), the SDK triggers this callback to report the current state. This callback indicates the state of the local video stream, including camera capturing and video encoding, and allows you to troubleshoot issues when exceptions occur.The SDK triggers the OnLocalVideoStateChanged callback with the state code of LOCAL_VIDEO_STREAM_STATE_FAILED and error code of LOCAL_VIDEO_STREAM_ERROR_CAPTURE_FAILURE in the following situations:The app switches to the background, and the system gets the camera resource.The camera starts normally, but does not output video frames for four consecutive seconds.When the camera outputs the captured video frames, if the video frames are the same for 15 consecutive frames, the SDK triggers the OnLocalVideoStateChanged callback with the state code of LOCAL_VIDEO_STREAM_STATE_CAPTURING and error code of LOCAL_VIDEO_STREAM_ERROR_CAPTURE_FAILURE. Note that the video frame duplication detection is only available for video frames with a resolution greater than 200 × 200, a frame rate greater than or equal to 10 fps, and a bitrate less than 20 Kbps.For some device models, the SDK does not trigger this callback when the state of the local video changes while the local video capturing device is in use, so you have to make your own timeout judgment.
+        /// </summary>
+        ///
+        /// <param name="state"> The state of the local video, see LOCAL_VIDEO_STREAM_STATE .</param>
+        ///
+        /// <param name="errorCode"> The detailed error information, see LOCAL_VIDEO_STREAM_ERROR .</param>
+        ///
+        public virtual void OnLocalVideoStateChanged(VIDEO_SOURCE_TYPE source, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_ERROR errorCode) { }
+        public virtual void OnLocalVideoStateChanged(RtcConnection connection, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_ERROR errorCode) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the remote video stream state changes.
+        /// This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 17.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user whose video state changes.</param>
+        ///
+        /// <param name="state"> The state of the remote video, see REMOTE_VIDEO_STATE .</param>
+        ///
+        /// <param name="reason"> The reason for the remote video state change, see REMOTE_VIDEO_STATE_REASON .</param>
+        ///
+        /// <param name="elapsed"> Time elapsed (ms) from the local user calling the JoinChannel [2/2] method until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnRemoteVideoStateChanged(RtcConnection connection, uint remoteUid, REMOTE_VIDEO_STATE state, REMOTE_VIDEO_STATE_REASON reason, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the renderer receives the first frame of the remote video.
+        /// </summary>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user sending the video stream.</param>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="width"> The width (px) of the video stream.</param>
+        ///
+        /// <param name="height"> The height (px) of the video stream.</param>
+        ///
+        /// <param name="elapsed"> The time elapsed (ms) from the local user calling JoinChannel [2/2] until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnFirstRemoteVideoFrame(RtcConnection connection, uint remoteUid, int width, int height, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when a remote user (COMMUNICATION)/ host (LIVE_BROADCASTING) joins the channel.
+        /// In a communication channel, this callback indicates that a remote user joins the channel. The SDK also triggers this callback to report the existing users in the channel when a user joins the channel.In a live-broadcast channel, this callback indicates that a host joins the channel. The SDK also triggers this callback to report the existing hosts in the channel when a host joins the channel. Agora recommends limiting the number of hosts to 17.The SDK triggers this callback under one of the following circumstances:A remote user/host joins the channel by calling the JoinChannel [2/2] method.A remote user switches the user role to the host after joining the channel.A remote user/host rejoins the channel after a network interruption.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the user or host who joins the channel.</param>
+        ///
+        /// <param name="elapsed"> Time delay (ms) from the local user calling JoinChannel [2/2] until this callback is triggered.</param>
+        ///
+        public virtual void OnUserJoined(RtcConnection connection, uint remoteUid, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when a remote user (in the communication profile)/ host (in the live streaming profile) leaves the channel.
+        /// There are two reasons for users to become offline:Leave the channel: When a user/host leaves the channel, the user/host sends a goodbye message. When this message is received, the SDK determines that the user/host leaves the channel.Drop offline: When no data packet of the user or host is received for a certain period of time (20 seconds for the communication profile, and more for the live broadcast profile), the SDK assumes that the user/host drops offline. A poor network connection may lead to false detections. It's recommended to use the Agora RTM SDK for reliable offline detection.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the user who leaves the channel or goes offline.</param>
+        ///
+        /// <param name="reason"> Reasons why the user goes offline: USER_OFFLINE_REASON_TYPE .</param>
+        ///
+        public virtual void OnUserOffline(RtcConnection connection, uint remoteUid, USER_OFFLINE_REASON_TYPE reason) { }
+
+        [Obsolete("Use onRemoteAudioStateChanged instead of")]
+        ///
+        /// <summary>
+        /// Occurs when a remote user (in the communication profile) or a host (in the live streaming profile) stops/resumes sending the audio stream.
+        /// This method is about to be deprecated. Agora recommends you using OnRemoteAudioStateChanged instead.The SDK triggers this callback when the remote user stops or resumes sending the audio stream by calling the MuteLocalAudioStream method.This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 17.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The user ID.</param>
+        ///
+        /// <param name="muted"> Whether the remote user's audio stream is muted/unmuted:true: User's audio stream is muted.false: User's audio stream is unmuted.</param>
+        ///
+        public virtual void OnUserMuteAudio(RtcConnection connection, uint remoteUid, bool muted) { }
+
+        [Obsolete("Use onRemoteVideoStateChanged instead of")]
+        ///
+        /// <summary>
+        /// Occurs when a remote user stops/resumes publishing the video stream.
+        /// This method is about to be deprecated. Agora recommends you using OnRemoteVideoStateChanged instead.When a remote user calls MuteLocalVideoStream to stop or resume publishing the video stream, the SDK triggers this callback to report the state of the remote user's publishing stream to the local user.This callback can be inaccurate when the number of users (in the communication profile) or hosts (in the live streaming profile) in a channel exceeds 17.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The user ID of the remote user.</param>
+        ///
+        /// <param name="muted"> Whether the remote user stops publishing the video stream:true: The remote user stops publishing the video stream.false: The remote user resumes publishing the video stream.</param>
+        ///
+        public virtual void OnUserMuteVideo(RtcConnection connection, uint remoteUid, bool muted) { }
+
+        [Obsolete("Use onRemoteVideoStateChanged instead of")]
+        ///
+        /// <summary>
+        /// Occurs when a remote user enables/disables the video module.
+        /// This method is about to be deprecated. Agora recommends you using OnRemoteVideoStateChanged instead.Once the video module is disabled, the user can only use a voice call. The user cannot send or receive any video.The SDK triggers this callback when a remote user enables or disables the video module by calling the EnableVideo or DisableVideo method.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The user ID of the remote user.</param>
+        ///
+        /// <param name="enabled"> true: Enable.false: Disable.</param>
+        ///
+        public virtual void OnUserEnableVideo(RtcConnection connection, uint remoteUid, bool enabled) { }
+
+        [Obsolete("Use onRemoteVideoStateChanged instead of")]
+        ///
+        /// <summary>
+        /// Occurs when a specific remote user enables/disables the local video capturing function.
+        /// The SDK triggers this callback when the remote user resumes or stops capturing the video stream by calling the EnableLocalVideo method.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The user ID of the remote user.</param>
+        ///
+        /// <param name="enabled"> Whether the specified remote user enables/disables the local video capturing function:true: Enable. Other users in the channel can see the video of this remote user.false: Disable. Other users in the channel can no longer receive the video stream from this remote user, while this remote user can still receive the video streams from other users.</param>
+        ///
+        public virtual void OnUserEnableLocalVideo(RtcConnection connection, uint remoteUid, bool enabled) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnUserStateChanged(RtcConnection connection, uint remoteUid, uint state) { }
+
+        ///
+        /// <summary>
+        /// Occurs when a method is executed by the SDK.
+        /// </summary>
+        ///
+        /// <param name="err"> The error code returned by the SDK when the method call fails. If the SDK returns 0, then the method call is successful.</param>
+        ///
+        /// <param name="api"> The method executed by the SDK.</param>
+        ///
+        /// <param name="result"> The result of the method call.</param>
+        ///
+        public virtual void OnApiCallExecuted(int err, string api, string result) { }
+
+        ///
+        /// <summary>
+        /// Reports the statistics of the local audio stream.
+        /// The SDK triggers this callback once every two seconds.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="stats"> Local audio statistics. See LocalAudioStats .</param>
+        ///
+        public virtual void OnLocalAudioStats(RtcConnection connection, LocalAudioStats stats) { }
+
+        ///
+        /// <summary>
+        /// Reports the statistics of the audio stream sent by each remote users.
+        /// The SDK triggers this callback once every two seconds. If a channel includes multiple users, the SDK triggers this callback as many times.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="stats"> Statistics of the received remote audio stream. See RemoteAudioStats .</param>
+        ///
+        public virtual void OnRemoteAudioStats(RtcConnection connection, RemoteAudioStats stats) { }
+
+        ///
+        /// <summary>
+        /// Reports the statistics of the local video stream.
+        /// The SDK triggers this callback once every two seconds to report the statistics of the local video stream.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="stats"> The statistics of the local video stream. See LocalVideoStats .</param>
+        ///
+        public virtual void OnLocalVideoStats(RtcConnection connection, LocalVideoStats stats) { }
+
+        ///
+        /// <summary>
+        /// Reports the statistics of the video stream sent by each remote users.
+        /// Reports the statistics of the video stream from the remote users. The SDK triggers this callback once every two seconds for each remote user. If a channel has multiple users/hosts sending video streams, the SDK triggers this callback as many times.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="stats"> Statistics of the remote video stream. </param>
+        ///
+        public virtual void OnRemoteVideoStats(RtcConnection connection, RemoteVideoStats stats) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the camera turns on and is ready to capture the video.
+        /// Deprecated:Please use LOCAL_VIDEO_STREAM_STATE_CAPTURING(1) in OnLocalVideoStateChanged instead.This callback indicates that the camera has been successfully turned on and you can start to capture video.
+        /// </summary>
+        ///
+        public virtual void OnCameraReady() { }
+
+        ///
+        /// <summary>
+        /// Occurs when the camera focus area changes.
+        /// This method is for Android and iOS only.
+        /// </summary>
+        ///
+        /// <param name="x"> The x-coordinate of the changed focus area.</param>
+        ///
+        /// <param name="y"> The y-coordinate of the changed focus area.</param>
+        ///
+        /// <param name="width"> The width of the focus area that changes.</param>
+        ///
+        /// <param name="height"> The height of the focus area that changes.</param>
+        ///
+        public virtual void OnCameraFocusAreaChanged(int x, int y, int width, int height) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the camera exposure area changes.
+        /// </summary>
+        ///
+        /// <param name="x"> The x coordinate of the changed camera exposure area.</param>
+        ///
+        /// <param name="y"> The y coordinate of the changed camera exposure area.</param>
+        ///
+        /// <param name="width"> The width of the changed camera exposure area.</param>
+        ///
+        /// <param name="height"> The height of the changed exposure area.</param>
+        ///
+        public virtual void OnCameraExposureAreaChanged(int x, int y, int width, int height) { }
+
+        ///
+        /// <summary>
+        /// Reports the face detection result of the local user.
+        /// Once you enable face detection by calling EnableFaceDetection (true), you can get the following information on the local user in real-time:The width and height of the local video.The position of the human face in the local view.The distance between the human face and the screen.This value is based on the fitting calculation of the local video size and the position of the human face.This callback is for Android and iOS only.When it is detected that the face in front of the camera disappears, the callback will be triggered immediately. When no human face is detected, the frequency of this callback to be rtriggered wil be decreased to reduce power consumption on the local device.The SDK stops triggering this callback when a human face is in close proximity to the screen.
+        /// </summary>
+        ///
+        /// <param name="imageWidth"> The width (px) of the video image captured by the local camera.</param>
+        ///
+        /// <param name="imageHeight"> The height (px) of the video image captured by the local camera.</param>
+        ///
+        /// <param name="vecRectangle"> The information of the detected human face:x: The x-coordinate (px) of the human face in the local view. Taking the top left corner of the view as the origin, the x-coordinate represents the horizontal position of the human face relative to the origin.y: The y-coordinate (px) of the human face in the local view. Taking the top left corner of the view as the origin, the y-coordinate represents the vertical position of the human face relative to the origin.width: The width (px) of the human face in the captured view.height: The height (px) of the human face in the captured view.</param>
+        ///
+        /// <param name="vecDistance"> The distance between the human face and the device screen (cm).</param>
+        ///
+        /// <param name="numFaces"> The number of faces detected. If the value is 0, it means that no human face is detected.</param>
+        ///
+        public virtual void OnFacePositionChanged(int imageWidth, int imageHeight, Rectangle vecRectangle, int[] vecDistance, int numFaces) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the video stops playing.
+        /// Deprecated:Use LOCAL_VIDEO_STREAM_STATE_STOPPED(0) in the OnLocalVideoStateChanged callback instead.The application can use this callback to change the configuration of the view (for example, displaying other pictures in the view) after the video stops playing.
+        /// </summary>
+        ///
+        public virtual void OnVideoStopped() { }
+
+        ///
+        /// <summary>
+        /// Occurs when the playback state of the music file changes.
+        /// This callback occurs when the playback state of the music file changes, and reports the current state and error code.
+        /// </summary>
+        ///
+        /// <param name="state"> The playback state of the music file. See AUDIO_MIXING_STATE_TYPE .</param>
+        ///
+        /// <param name="reason"> Error code. See AUDIO_MIXING_REASON_TYPE .</param>
+        ///
+        public virtual void OnAudioMixingStateChanged(AUDIO_MIXING_STATE_TYPE state, AUDIO_MIXING_REASON_TYPE reason) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnRhythmPlayerStateChanged(RHYTHM_PLAYER_STATE_TYPE state, RHYTHM_PLAYER_ERROR_TYPE errorCode) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the SDK cannot reconnect to Agora's edge server 10 seconds after its connection to the server is interrupted.
+        /// The SDK triggers this callback when it cannot connect to the server 10 seconds after calling the JoinChannel [2/2] method, regardless of whether it is in the channel. If the SDK fails to rejoin the channel within 20 minutes after disconnecting, the SDK will stop trying to reconnect.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        public virtual void OnConnectionLost(RtcConnection connection) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the connection between the SDK and the server is interrupted.
+        /// Deprecated:Use OnConnectionStateChanged instead.The SDK triggers this callback when it loses connection with the server for more than four seconds after the connection is established. After triggering this callback, the SDK tries to reconnect to the server. You can use this callback to implement pop-up reminders. The difference between this callback and OnConnectionLost is:The SDK triggers the OnConnectionInterrupted callback when it loses connection with the server for more than four seconds after it successfully joins the channel.The SDK triggers the OnConnectionLost callback when it loses connection with the server for more than 10 seconds, whether or not it joins the channel.If the SDK fails to rejoin the channel 20 minutes after being disconnected from Agora's edge server, the SDK stops rejoining the channel.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        public virtual void OnConnectionInterrupted(RtcConnection connection) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the connection is banned by the Agora server.
+        /// Deprecated:Please use OnConnectionStateChanged instead.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        public virtual void OnConnectionBanned(RtcConnection connection) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the local user receives the data stream from the remote user.
+        /// The SDK triggers this callback when the local user receives the stream message that the remote user sends by calling the SendStreamMessage method.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user sending the message.</param>
+        ///
+        /// <param name="streamId"> The stream ID of the received message.</param>
+        ///
+        /// <param name="data"> received data.</param>
+        ///
+        /// <param name="length"> The data length (byte).</param>
+        ///
+        /// <param name="sentTs"> The time when the data stream is sent.</param>
+        ///
+        public virtual void OnStreamMessage(RtcConnection connection, uint remoteUid, int streamId, byte[] data, uint length, UInt64 sentTs) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the local user does not receive the data stream from the remote user.
+        /// The SDK triggers this callback when the local user fails to receive the stream message that the remote user sends by calling the SendStreamMessage method.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user sending the message.</param>
+        ///
+        /// <param name="streamId"> The stream ID of the received message.</param>
+        ///
+        /// <param name="code"> The error code.</param>
+        ///
+        /// <param name="missed"> The number of lost messages.</param>
+        ///
+        /// <param name="cached"> Number of incoming cached messages when the data stream is interrupted.</param>
+        ///
+        public virtual void OnStreamMessageError(RtcConnection connection, uint remoteUid, int streamId, int code, int missed, int cached) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the token expires.
+        /// When the token expires during a call, the SDK triggers this callback to remind the app to renew the token.Once you receive this callback, generate a new token on your app server, and call JoinChannel [2/2] to rejoin the channel.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        public virtual void OnRequestToken(RtcConnection connection) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the token expires in 30 seconds.
+        /// When the token is about to expire in 30 seconds, the SDK triggers this callback to remind the app to renew the token.Upon receiving this callback, generate a new token on your server, and call RenewToken to pass the new token to the SDK.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="token"> The token that expires in 30 seconds.</param>
+        ///
+        public virtual void OnTokenPrivilegeWillExpire(RtcConnection connection, string token) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the first audio frame is published.
+        /// The SDK triggers this callback under one of the following circumstances:The local client enables the audio module and calls JoinChannel [2/2] successfully.The local client calls MuteLocalAudioStream (true) and MuteLocalAudioStream(false) in sequence.The local client calls DisableAudio and EnableAudio in sequence.The local client calls pushAudioFrame to successfully push the audio frame to the SDK.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="elapsed"> Time elapsed (ms) from the local user calling JoinChannel [2/2] until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnFirstLocalAudioFramePublished(RtcConnection connection, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the first audio frame sent by a specified remote user is received.
+        /// Deprecated:Use OnRemoteAudioStateChanged instead.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="userId"> The ID of the remote user sending the audio frames.</param>
+        ///
+        /// <param name="elapsed"> The time elapsed (ms) from the local user calling the JoinChannel [2/2] method until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnFirstRemoteAudioFrame(RtcConnection connection, uint userId, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the SDK decodes the first remote audio frame for playback.
+        /// Deprecated:Use OnRemoteAudioStateChanged instead.The SDK triggers this callback under one of the following circumstances:The remote user joins the channel and sends the audio stream.The remote user stops sending the audio stream and re-sends it after 15 seconds, and the possible reasons include:The remote user leaves the channel.The remote user is offline.The remote user calls MuteLocalAudioStream to stop sending the video stream.The remote user calls DisableAudio to disable video.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="uid"> The user ID of the remote user.</param>
+        ///
+        /// <param name="elapsed"> The time elapsed (ms) from the local user calling the JoinChannel [2/2] method until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnFirstRemoteAudioDecoded(RtcConnection connection, uint uid, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the local audio stream state changes.
+        /// When the state of the local audio stream changes (including the state of the audio capture and encoding), the SDK triggers this callback to report the current state. This callback indicates the state of the local audio stream, and allows you to troubleshoot issues when audio exceptions occur.When the state is LOCAL_AUDIO_STREAM_STATE_FAILED (3), you can view the error information in the error parameter.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="state"> The state of the local audio. See LOCAL_AUDIO_STREAM_STATE .</param>
+        ///
+        /// <param name="error"> Local audio state error codes. See LOCAL_AUDIO_STREAM_ERROR .</param>
+        ///
+        public virtual void OnLocalAudioStateChanged(RtcConnection connection, LOCAL_AUDIO_STREAM_STATE state, LOCAL_AUDIO_STREAM_ERROR error) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the remote audio state changes.
+        /// When the audio state of a remote user (in a voice/video call channel) or host (in a live streaming channel) changes, the SDK triggers this callback to report the current state of the remote audio stream.This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 17.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user whose audio state changes.</param>
+        ///
+        /// <param name="state"> The state of the remote audio. See REMOTE_AUDIO_STATE .</param>
+        ///
+        /// <param name="reason"> The reason of the remote audio state change. See REMOTE_AUDIO_STATE_REASON .</param>
+        ///
+        /// <param name="elapsed"> Time elapsed (ms) from the local user calling the JoinChannel [2/2] method until the SDK triggers this callback.</param>
+        ///
+        public virtual void OnRemoteAudioStateChanged(RtcConnection connection, uint remoteUid, REMOTE_AUDIO_STATE state, REMOTE_AUDIO_STATE_REASON reason, int elapsed) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the most active remote speaker is detected.
+        /// After a successful call of EnableAudioVolumeIndication , the SDK continuously detects which remote user has the loudest volume. During the current period, the remote user, who is detected as the loudest for the most times, is the most active user.When the number of users is no less than two and an active remote speaker exists, the SDK triggers this callback and reports the uid of the most active remote speaker.If the most active remote speaker is always the same user, the SDK triggers the OnActiveSpeaker callback only once.If the most active remote speaker changes to another user, the SDK triggers this callback again and reports the uid of the new active remote speaker.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="uid"> The user ID of the most active remote speaker.</param>
+        ///
+        public virtual void OnActiveSpeaker(RtcConnection connection, uint uid) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the user role switches in the interactive live streaming.
+        /// The SDK triggers this callback when the local user switches the user role by calling SetClientRole [1/2]
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="oldRole"> Role that the user switches from: CLIENT_ROLE_BROADCASTER(1): Host.CLIENT_ROLE_AUDIENCE(2): Audience. CLIENT_ROLE_TYPE .</param>
+        ///
+        /// <param name="newRole"> Role that the user switches to: CLIENT_ROLE_BROADCASTER(1): Host.CLIENT_ROLE_AUDIENCE(2): Audience. CLIENT_ROLE_TYPE .</param>
+        ///
+        public virtual void OnClientRoleChanged(RtcConnection connection, CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the user role switch fails in the interactive live streaming.
+        /// In the live broadcasting channel profile, when the local user calls SetClientRole [1/2] to switch their user role after joining the channel but the switch fails, the SDK triggers this callback to report the reason for the failure and the current user role.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="reason"> The reason for a user role switch failure. See CLIENT_ROLE_CHANGE_FAILED_REASON .</param>
+        ///
+        /// <param name="currentRole"> Current user role. See CLIENT_ROLE_TYPE .</param>
+        ///
+        public virtual void OnClientRoleChangeFailed(RtcConnection connection, CLIENT_ROLE_CHANGE_FAILED_REASON reason, CLIENT_ROLE_TYPE currentRole) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnAudioDeviceVolumeChanged(MEDIA_DEVICE_TYPE deviceType, int volume, bool muted) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the media push state changes.
+        /// When the media push state changes, the SDK triggers this callback and reports the URL address and the current state of the media push. This callback indicates the state of the media push. When exceptions occur, you can troubleshoot issues by referring to the detailed error descriptions in the error code parameter.
+        /// </summary>
+        ///
+        /// <param name="url"> The URL address where the state of the media push changes.</param>
+        ///
+        /// <param name="state"> The current state of the media push. See RTMP_STREAM_PUBLISH_STATE .</param>
+        ///
+        /// <param name="errCode"> The detailed error information for the media push. See RTMP_STREAM_PUBLISH_ERROR_TYPE .</param>
+        ///
+        public virtual void OnRtmpStreamingStateChanged(string url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR_TYPE errCode) { }
+
+        ///
+        /// <summary>
+        /// Reports events during the media push.
+        /// </summary>
+        ///
+        /// <param name="url"> The URL of media push.</param>
+        ///
+        /// <param name="eventCode"> The event code of media push. See RTMP_STREAMING_EVENT .</param>
+        ///
+        public virtual void OnRtmpStreamingEvent(string url, RTMP_STREAMING_EVENT eventCode) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the publisher's transcoding is updated.
+        /// When the LiveTranscoding class in the setLiveTranscoding method updates, the SDK triggers the OnTranscodingUpdated callback to report the update information.If you call the setLiveTranscoding method to set the LiveTranscoding class for the first time, the SDK does not trigger this callback.
+        /// </summary>
+        ///
+        public virtual void OnTranscodingUpdated() { }
+
+        ///
+        /// <summary>
+        /// Occurs when the local audio route changes.
+        /// This method is for Android, iOS and macOS only.
+        /// </summary>
+        ///
+        /// <param name="routing"> The current audio routing. See AudioRoute .</param>
+        ///
+        public virtual void OnAudioRoutingChanged(int routing) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the state of the media stream relay changes.
+        /// The SDK returns the state of the current media relay with any error message.
+        /// </summary>
+        ///
+        /// <param name="state"> The state code. See CHANNEL_MEDIA_RELAY_STATE .</param>
+        ///
+        /// <param name="code"> The error code of the channel media relay. See CHANNEL_MEDIA_RELAY_ERROR .</param>
+        ///
+        public virtual void OnChannelMediaRelayStateChanged(int state, int code) { }
+
+        ///
+        /// <summary>
+        /// Reports events during the media stream relay.
+        /// </summary>
+        ///
+        /// <param name="code"> The event code of channel media relay. See CHANNEL_MEDIA_RELAY_EVENT .</param>
+        ///
+        public virtual void OnChannelMediaRelayEvent(int code) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnLocalPublishFallbackToAudioOnly(bool isFallbackOrRecover) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnRemoteSubscribeFallbackToAudioOnly(uint uid, bool isFallbackOrRecover) { }
+
+        ///
+        /// <summary>
+        /// Reports the transport-layer statistics of each remote audio stream.
+        /// Deprecated:Please use OnRemoteAudioStats instead.This callback reports the transport-layer statistics, such as the packet loss rate and network time delay, once every two seconds after the local user receives an audio packet from a remote user. During a call, when the user receives the video packet sent by the remote user/host, the callback is triggered every 2 seconds.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user sending the audio packets.</param>
+        ///
+        /// <param name="delay"> The network delay (ms) from the sender to the receiver.</param>
+        ///
+        /// <param name="lost"> The packet loss rate (%) of the audio packet sent from the remote user.</param>
+        ///
+        /// <param name="rxKBitRate"> The bitrate of the received audio (Kbps).</param>
+        ///
+        public virtual void OnRemoteAudioTransportStats(RtcConnection connection, uint remoteUid, UInt16 delay, UInt16 lost, UInt16 rxKBitRate) { }
+
+        ///
+        /// <summary>
+        /// Reports the transport-layer statistics of each remote video stream.
+        /// Deprecated:This callback is deprecated; use OnRemoteVideoStats instead.This callback reports the transport-layer statistics, such as the packet loss rate and network time delay, once every two seconds after the local user receives a video packet from a remote user.During a call, when the user receives the video packet sent by the remote user/host, the callback is triggered every 2 seconds.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="remoteUid"> The ID of the remote user sending the video packets.</param>
+        ///
+        /// <param name="delay"> The network delay (ms) from the sender to the receiver.</param>
+        ///
+        /// <param name="lost"> The packet loss rate (%) of the video packet sent from the remote user.</param>
+        ///
+        /// <param name="rxKBitRate"> The bitrate of the received video (Kbps).</param>
+        ///
+        public virtual void OnRemoteVideoTransportStats(RtcConnection connection, uint remoteUid, UInt16 delay, UInt16 lost, UInt16 rxKBitRate) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the network connection state changes.
+        /// When the network connection state changes, the SDK triggers this callback and reports the current connection state and the reason for the change.
+        /// </summary>
+        ///
+        /// <param name="state"> The current connection state. For details, see CONNECTION_STATE_TYPE .</param>
+        ///
+        /// <param name="reason"> The reason for a connection state change. For details, see CONNECTION_CHANGED_REASON_TYPE .</param>
+        ///
+        public virtual void OnConnectionStateChanged(RtcConnection connection, CONNECTION_STATE_TYPE state, CONNECTION_CHANGED_REASON_TYPE reason) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnWlAccMessage(RtcConnection connection, WLACC_MESSAGE_REASON reason, WLACC_SUGGEST_ACTION action, string wlAccMsg) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnWlAccStats(RtcConnection connection, WlAccStats currentStats, WlAccStats averageStats) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the local network type changes.
+        /// This callback occurs when the connection state of the local user changes. You can get the connection state and reason for the state change in this callback. When the network connection is interrupted, this callback indicates whether the interruption is caused by a network type change or poor network conditions.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="type"> Network types: See NETWORK_TYPE .</param>
+        ///
+        public virtual void OnNetworkTypeChanged(RtcConnection connection, NETWORK_TYPE type) { }
+
+        ///
+        /// <summary>
+        /// Reports the built-in encryption errors.
+        /// When encryption is enabled by calling EnableEncryption , the SDK triggers this callback if an error occurs in encryption or decryption on the sender or the receiver side.
+        /// </summary>
+        ///
+        /// <param name="connection"> The connection information. See RtcConnection .</param>
+        ///
+        /// <param name="errorType"> For details about the error type, see ENCRYPTION_ERROR_TYPE .</param>
+        ///
+        public virtual void OnEncryptionError(RtcConnection connection, ENCRYPTION_ERROR_TYPE errorType) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnUploadLogResult(RtcConnection connection, string requestId, bool success, UPLOAD_ERROR_REASON reason) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnUserAccountUpdated(RtcConnection connection, uint remoteUid, string userAccount) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the SDK cannot get the device permission.
+        /// When the SDK fails to get the device permission, the SDK triggers this callback to report which device permission cannot be got.This method is for Android and iOS only.
+        /// </summary>
+        ///
+        /// <param name="permissionType"> The type of the device permission. See PERMISSION_TYPE .</param>
+        ///
+        public virtual void OnPermissionError(PERMISSION_TYPE permissionType) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the local user registers a user account.
+        /// After the local user successfully calls RegisterLocalUserAccount to register the user account or calls JoinChannelWithUserAccount [2/2] to join a channel, the SDK triggers the callback and informs the local user's UID and User Account.
+        /// </summary>
+        ///
+        /// <param name="uid"> The ID of the local user.</param>
+        ///
+        /// <param name="userAccount"> The user account of the local user.</param>
+        ///
+        public virtual void OnLocalUserRegistered(uint uid, string userAccount) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the SDK gets the user ID and user account of the remote user.
+        /// After a remote user joins the channel, the SDK gets the UID and user account of the remote user, caches them in a mapping table object, and triggers this callback on the local client.
+        /// </summary>
+        ///
+        /// <param name="uid"> The user ID of the remote user.</param>
+        ///
+        /// <param name="info"> The UserInfo object that contains the user ID and user account of the remote user. See UserInfo for details.</param>
+        ///
+        public virtual void OnUserInfoUpdated(uint uid, UserInfo info) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the audio subscribing state changes.
+        /// </summary>
+        ///
+        /// <param name="channel"> The channel name.</param>
+        ///
+        /// <param name="uid"> The user ID of the remote user.</param>
+        ///
+        /// <param name="oldState"> The previous subscribing status, see STREAM_SUBSCRIBE_STATE for details.</param>
+        ///
+        /// <param name="newState"> The current subscribing status, see STREAM_SUBSCRIBE_STATE for details.</param>
+        ///
+        /// <param name="elapseSinceLastState"> The time elapsed (ms) from the previous state to the current state.</param>
+        ///
+        public virtual void OnAudioSubscribeStateChanged(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the video subscribing state changes.
+        /// </summary>
+        ///
+        /// <param name="channel"> The channel name.</param>
+        ///
+        /// <param name="uid"> The ID of the remote user.</param>
+        ///
+        /// <param name="oldState"> The previous subscribing status, see STREAM_SUBSCRIBE_STATE for details.</param>
+        ///
+        /// <param name="newState"> The current subscribing status, see STREAM_SUBSCRIBE_STATE for details.</param>
+        ///
+        /// <param name="elapseSinceLastState"> The time elapsed (ms) from the previous state to the current state.</param>
+        ///
+        public virtual void OnVideoSubscribeStateChanged(string channel, uint uid, STREAM_SUBSCRIBE_STATE oldState, STREAM_SUBSCRIBE_STATE newState, int elapseSinceLastState) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the audio publishing state changes.
+        /// </summary>
+        ///
+        /// <param name="channel"> The channel name.</param>
+        ///
+        /// <param name="oldState"> The previous subscribing status. See STREAM_PUBLISH_STATE .</param>
+        ///
+        /// <param name="newState"> The current subscribing status. See STREAM_PUBLISH_STATE.</param>
+        ///
+        /// <param name="elapseSinceLastState"> The time elapsed (ms) from the previous state to the current state.</param>
+        ///
+        public virtual void OnAudioPublishStateChanged(string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the video publishing state changes.
+        /// </summary>
+        ///
+        /// <param name="channel"> The channel name.</param>
+        ///
+        /// <param name="source"> The capture type of the custom video source. See VIDEO_SOURCE_TYPE .</param>
+        ///
+        /// <param name="oldState"> For the previous publishing state, see STREAM_PUBLISH_STATE .</param>
+        ///
+        /// <param name="newState"> For the current publishing state, see STREAM_PUBLISH_STATE.</param>
+        ///
+        /// <param name="elapseSinceLastState"> The time elapsed (ms) from the previous state to the current state.</param>
+        ///
+        public virtual void OnVideoPublishStateChanged(VIDEO_SOURCE_TYPE source, string channel, STREAM_PUBLISH_STATE oldState, STREAM_PUBLISH_STATE newState, int elapseSinceLastState) { }
+
+        ///
+        /// <summary>
+        /// The event callback of the extension.
+        /// To listen for events while the extension is running, you need to register this callback.
+        /// </summary>
+        ///
+        /// <param name="value"> The value of the extension key.</param>
+        ///
+        /// <param name="key"> The key of the extension.</param>
+        ///
+        /// <param name="provider"> The name of the extension provider.</param>
+        ///
+        /// <param name="extension"> The name of the extension.</param>
+        ///
+        public virtual void OnExtensionEvent(string provider, string extension, string key, string value) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the extension is enabled.
+        /// After a successful call of EnableExtension (true), the extension triggers this callback.
+        /// </summary>
+        ///
+        /// <param name="provider"> The name of the extension provider.</param>
+        ///
+        /// <param name="extension"> The name of the extension.</param>
+        ///
+        public virtual void OnExtensionStarted(string provider, string extension) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the extension is disabled.
+        /// After a successful call of EnableExtension (false), this callback is triggered.
+        /// </summary>
+        ///
+        /// <param name="extension"> The name of the extension.</param>
+        ///
+        /// <param name="provider"> The name of the extension provider.</param>
+        ///
+        public virtual void OnExtensionStopped(string provider, string extension) { }
+
+        ///
+        /// <summary>
+        /// Occurs when the extension runs incorrectly.
+        /// When calling EnableExtension (true) fails or the extension runs in error, the extension triggers this callback and reports the error code and reason.
+        /// </summary>
+        ///
+        /// <param name="provider"> The name of the extension provider.</param>
+        ///
+        /// <param name="extension"> The name of the extension.</param>
+        ///
+        /// <param name="error"> Error code. For details, see the extension documentation provided by the extension provider.</param>
+        ///
+        /// <param name="message"> Reason. For details, see the extension documentation provided by the extension provider.</param>
+        ///
+        public virtual void OnExtensionError(string provider, string extension, int error, string message) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnDirectCdnStreamingStateChanged(DIRECT_CDN_STREAMING_STATE state, DIRECT_CDN_STREAMING_ERROR error, string message) { }
+
+        ///
+        /// @ignore
+        ///
+        public virtual void OnDirectCdnStreamingStats(DirectCdnStreamingStats stats) { }
+    };
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IRtcEngineEventHandler.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9c09585bd363c4523b32f9b2b2ef6431
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 207 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/ISpatialAudio.cs

@@ -0,0 +1,207 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// This class calculates user positions through the SDK to implement the spatial audio effect.
+    /// </summary>
+    ///
+    public abstract class ILocalSpatialAudioEngine
+    {
+        ///
+        /// <summary>
+        /// Destroys ILocalSpatialAudioEngine .
+        /// This method releases all resources under ILocalSpatialAudioEngine. When the user does not need to use the spatial audio effect, you can call this method to release resources for other operations.After calling this method, you can no longer use any of the APIs under ILocalSpatialAudioEngine. To use the spatial audio effect again, you need to wait until the Dispose method execution to complete before calling Initialize to create a new ILocalSpatialAudioEngine.Call this method before the Dispose method under IRtcEngine .
+        /// </summary>
+        ///
+        public abstract void Dispose();
+
+        ///
+        /// <summary>
+        /// Initializes ILocalSpatialAudioEngine .
+        /// Before calling other methods of the ILocalSpatialAudioEngine class, you need to call this method to initialize ILocalSpatialAudioEngine.The SDK supports creating only one ILocalSpatialAudioEngine instance for an app.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int Initialize();
+
+        ///
+        /// <summary>
+        /// Sets the maximum number of streams that a user can receive in a specified audio reception range.
+        /// If the number of receivable streams exceeds the set value, the local user receives the maxCount streams that are closest to the local user. If there are users who belong to the same team as the local user in the room, the local user receives the audio of the teammates first. For example, when maxCount is set to 3, if there are five remote users in the room, two of whom belong to the same team as the local user, and three of whom belong to different teams but are within the audio reception range of the local user, the local user can hear the two teammates and the one user from a different team closest to the local user.
+        /// </summary>
+        ///
+        /// <param name="maxCount"> The maximum number of streams that a user can receive within a specified audio reception range.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetMaxAudioRecvCount(int maxCount);
+
+        ///
+        /// <summary>
+        /// Sets the audio reception range of the local user.
+        /// After the setting is successful, the local user can only hear the remote users within the setting range or belonging to the same team. You can call this method at any time to update the audio reception range.
+        /// </summary>
+        ///
+        /// <param name="range"> The maximum audio reception range. The unit is meters. The value must be greater than 0.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetAudioRecvRange(float range);
+
+        ///
+        /// <summary>
+        /// Sets the length (in meters) of the game engine distance per unit.
+        /// In a game engine, the unit of distance is customized, while in the Agora spatial audio algorithm, distance is measured in meters. By default, the SDK converts the game engine distance per unit to one meter. You can call this method to convert the game engine distance per unit to a specified number of meters.
+        /// </summary>
+        ///
+        /// <param name="unit"> The number of meters that the game engine distance per unit is equal to. This parameter must be greater than 0.00. For example, setting unit as 2.00 means the game engine distance per unit equals 2 meters.The larger the value is, the faster the sound heard by the local user attenuates when the remote user moves far away from the local user.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetDistanceUnit(float unit);
+
+        ///
+        /// <summary>
+        /// Updates the spatial position of the local user.
+        /// Under the ILocalSpatialAudioEngine class, this method needs to be used with UpdateRemotePosition . The SDK calculates the relative position between the local and remote users according to this method and the parameter settings in UpdateRemotePosition, and then calculates the user's spatial audio effect parameters.
+        /// </summary>
+        ///
+        /// <param name="position"> The coordinates in the world coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <param name="axisForward"> The unit vector of the x axis in the coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <param name="axisRight"> The unit vector of the y axis in the coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <param name="axisUp"> The unit vector of the z axis in the coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int UpdateSelfPosition(float[] position, float[] axisForward, float[] axisRight, float[] axisUp);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int UpdateSelfPositionEx(float[] position, float[] axisForward, float[] axisRight, float[] axisUp, RtcConnection connection);
+
+        ///
+        /// <summary>
+        /// Updates the spatial position of the media player.
+        /// After a successful update, the local user can hear the change in the spatial position of the media player.
+        /// </summary>
+        ///
+        /// <param name="playerId"> The ID of the media player. </param>
+        ///
+        /// <param name="position"> The coordinates in the world coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <param name="forward"> The unit vector of the x axis in the coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int UpdatePlayerPositionInfo(int playerId, float[] position, float[] forward);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int SetParameters(string @params);
+
+        ///
+        /// <summary>
+        /// Stops or resumes publishing the local audio stream.
+        /// This method does not affect any ongoing audio recording, because it does not disable the audio capture device.Call this method after JoinChannel [2/2] .When using the spatial audio effect, if you need to set whether to publish the local audio stream, Agora recommends calling this method instead of the MuteLocalAudioStream method under IRtcEngine .
+        /// </summary>
+        ///
+        /// <param name="mute"> Whether to stop publishing the local audio stream.true: Stop publishing the local audio stream.false: Publish the local audio stream.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int MuteLocalAudioStream(bool mute);
+
+        ///
+        /// <summary>
+        /// Stops or resumes subscribing to the audio streams of all remote users.
+        /// After successfully calling this method, the local user stops or resumes subscribing to the audio streams of all remote users, including all subsequent users.Call this method after JoinChannel [2/2] .When using the spatial audio effect, if you need to set whether to stop subscribing to the audio streams of all remote users, Agora recommends calling this method instead of the MuteAllRemoteAudioStreams method under IRtcEngine .
+        /// </summary>
+        ///
+        /// <param name="mute"> Whether to stop subscribing to the audio streams of all remote users:true: Stop subscribing to the audio streams of all remote users.false: Subscribe to the audio streams of all remote users.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int MuteAllRemoteAudioStreams(bool mute);
+
+        ///
+        /// <summary>
+        /// Updates the spatial position of the specified remote user.
+        /// After successfully calling this method, the SDK calculates the spatial audio parameters based on the relative position of the local and remote user.Call this method after the JoinChannel [2/2] method.
+        /// </summary>
+        ///
+        /// <param name="uid"> The user ID. This parameter must be the same as the user ID passed in when the user joined the channel.</param>
+        ///
+        /// <param name="position"> The coordinates in the world coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <param name="forward"> The unit vector of the x axis in the coordinate system. This parameter is an array of length 3, and the three values represent the front, right, and top coordinates in turn.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int UpdateRemotePosition(uint uid, float[] position, float[] forward);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int UpdateRemotePositionEx(uint uid, float[] position, float[] forward, RtcConnection connection);
+
+        ///
+        /// <summary>
+        /// Removes the spatial position of the specified remote user.
+        /// After successfully calling this method, the local user no longer hears the specified remote user.After leaving the channel, to avoid wasting resources, you can also call this method to delete the spatial position of the specified remote user.
+        /// </summary>
+        ///
+        /// <param name="uid"> The user ID. This parameter must be the same as the user ID passed in when the user joined the channel.</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int RemoveRemotePosition(uint uid);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int RemoveRemotePositionEx(uint uid, RtcConnection connection);
+
+        ///
+        /// <summary>
+        /// Removes the spatial positions of all remote users.
+        /// After successfully calling this method, the local user no longer hears any remote users.After leaving the channel, to avoid wasting resources, you can also call this method to delete the spatial positions of all remote users.
+        /// </summary>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int ClearRemotePositions();
+
+        ///
+        /// @ignore
+        ///
+        public abstract int ClearRemotePositionsEx(RtcConnection connection);
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/ISpatialAudio.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: df321acca8c164b999303128f6f16445
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 92 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoDeviceManager.cs

@@ -0,0 +1,92 @@
+using System;
+
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// Video device management methods.
+    /// </summary>
+    ///
+    public abstract class IVideoDeviceManager
+    {
+        ///
+        /// <summary>
+        /// Enumerates the video devices.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Success: A DeviceInfo array including all video devices in the system.Failure: NULL.
+        /// </returns>
+        ///
+        public abstract DeviceInfo[] EnumerateVideoDevices();
+
+        ///
+        /// <summary>
+        /// Specifies the video capture device with the device ID.
+        /// Plugging or unplugging a device does not change its device ID.
+        /// </summary>
+        ///
+        /// <param name="deviceIdUTF8"> The device ID. You can get the device ID by calling EnumerateVideoDevices .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int SetDevice(string deviceIdUTF8);
+
+        ///
+        /// <summary>
+        /// Retrieves the current video capture device.
+        /// </summary>
+        ///
+        /// <param name="deviceIdUTF8"> Output parameter. The device ID. </param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetDevice(ref string deviceIdUTF8);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int StartDeviceTest(IntPtr hwnd);
+
+        ///
+        /// @ignore
+        ///
+        public abstract int StopDeviceTest();
+
+        ///
+        /// <summary>
+        /// Gets the detailed video frame information of the video capture device in the specified video format.
+        /// After calling NumberOfCapabilities to get the number of video formats supported by the video capture device, you can call this method to get the specific video frame information supported by the specified index number.
+        /// </summary>
+        ///
+        /// <param name="deviceIdUTF8"> The ID of the video capture device.</param>
+        ///
+        /// <param name="deviceCapabilityNumber"> The index number of the video format. If NumberOfCapabilities the return value of is i, the value range of this parameter is [0,i).</param>
+        ///
+        /// <param name="capability"> Output parameter. Indicates the specific information of the specified video format, including width (px), height (px), and frame rate (fps). See VideoFormat .</param>
+        ///
+        /// <returns>
+        /// 0: Success.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int GetCapability(string deviceIdUTF8, uint deviceCapabilityNumber, out VideoFormat capability);
+
+        ///
+        /// <summary>
+        /// Gets the number of video formats supported by the specified video capture device.
+        /// Video capture devices may support multiple video formats, and each format supports different combinations of video frame width, video frame height, and frame rate.You can call this method to get how many video formats the specified video capture device can support, and then call GetCapability to get the specific video frame information in the specified video format.
+        /// </summary>
+        ///
+        /// <param name="deviceIdUTF8"> The ID of the video capture device.</param>
+        ///
+        /// <returns>
+        /// 0: Success. Returns the number of video formats supported by this device. For example: If the specified camera supports 10 different video formats, the return value is 10.&lt; 0: Failure.
+        /// </returns>
+        ///
+        public abstract int NumberOfCapabilities(string deviceIdUTF8);
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoDeviceManager.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 823cf0124362f4668bd9b75a39ebf6f4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 34 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoEncodedFrameObserver.cs

@@ -0,0 +1,34 @@
+using System;
+
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// Receives encoded video images.
+    /// </summary>
+    ///
+    public abstract class IVideoEncodedFrameObserver
+    {
+        ///
+        /// <summary>
+        /// Occurs each time the SDK receives an encoded video image.
+        /// </summary>
+        ///
+        /// <param name="uid"> The user ID of the remote user.</param>
+        ///
+        /// <param name="imageBufferPtr"> The encoded video image buffer.</param>
+        ///
+        /// <param name="length"> The data length of the video image.</param>
+        ///
+        /// <param name="videoEncodedFrameInfo"> For the information of the encoded video frame, see EncodedVideoFrameInfo .</param>
+        ///
+        /// <returns>
+        /// Reserved for future use.
+        /// </returns>
+        ///
+        public virtual bool OnEncodedVideoFrameReceived(uint uid, IntPtr imageBufferPtr, UInt64 length, EncodedVideoFrameInfo videoEncodedFrameInfo)
+        {
+            return true;
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoEncodedFrameObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 77b81c06498304cdf8f411ac8031b7f3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 99 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoFrameObserver.cs

@@ -0,0 +1,99 @@
+namespace Agora.Rtc
+{
+    ///
+    /// <summary>
+    /// The IVideoFrameObserver class.
+    /// </summary>
+    ///
+    public abstract class IVideoFrameObserver
+    {
+        ///
+        /// <summary>
+        /// Occurs each time the SDK receives a video frame captured by the local camera.
+        /// After you successfully register the video frame observer, the SDK triggers this callback each time it receives a video frame. In this callback, you can get the video data captured by the local camera. You can then pre-process the data according to your scenarios.After pre-processing, you can send the processed video data back to the SDK through this callback.The video data that this callback gets has not been pre-processed, and is not watermarked, cropped, rotated or beautified.
+        /// </summary>
+        ///
+        /// <param name="videoFrame"> The video frame. See VideoFrame .</param>
+        ///
+        /// <param name="config"> The configuration of the video frame. See VideoFrameBufferConfig .</param>
+        ///
+        /// <returns>
+        /// true: Sets the SDK to receive the video frame.false: Sets the SDK to discard the video frame.
+        /// </returns>
+        ///
+        public virtual bool OnCaptureVideoFrame(VideoFrame videoFrame, VideoFrameBufferConfig config)
+        {
+            return true;
+        }
+
+        ///
+        /// <summary>
+        /// Occurs each time the SDK receives a video frame before encoding.
+        /// After you successfully register the video frame observer, the SDK triggers this callback each time it receives a video frame. In this callback, you can get the video data before encoding and then process the data according to your particular scenarios.After processing, you can send the processed video data back to the SDK in this callback.To get the video data captured from the second screen before encoding, you need to set POSITION_PRE_ENCODER(1 << 2) as a frame position through GetObservedFramePosition .The video data that this callback gets has been preprocessed, with its content cropped and rotated, and the image enhanced.
+        /// </summary>
+        ///
+        /// <param name="config"> The configuration of the video frame. See VideoFrameBufferConfig .</param>
+        ///
+        /// <param name="videoFrame"> The video frame. See VideoFrame .</param>
+        ///
+        /// <returns>
+        /// true: Sets the SDK to receive the video frame.false: Sets the SDK to discard the video frame.
+        /// </returns>
+        ///
+        public virtual bool OnPreEncodeVideoFrame(VideoFrame videoFrame, VideoFrameBufferConfig config)
+        {
+            return true;
+        }
+        
+        ///
+        /// <summary>
+        /// Occurs each time the SDK receives a video frame sent by the remote user.
+        /// After you successfully register the video frame observer, the SDK triggers this callback each time it receives a video frame. In this callback, you can get the video data before encoding. You can then process the data according to your particular scenarios.
+        /// </summary>
+        ///
+        /// <param name="videoFrame"> The video frame. See VideoFrame .</param>
+        ///
+        /// <param name="uid"> The ID of the remote user who sends the current video frame.</param>
+        ///
+        /// <param name="channelId"> The channel ID.</param>
+        ///
+        /// <returns>
+        /// true:The SDK ignores this return value.false:The SDK ignores this return value.
+        /// </returns>
+        ///
+        public virtual bool OnRenderVideoFrame(string channelId, uint uid, VideoFrame videoFrame)
+        {
+            return true;
+        }
+        
+        ///
+        /// <summary>
+        /// Sets the format of the raw video data output by the SDK.
+        /// If you want to get raw video data in a color encoding format other than YUV 420, register this callback when calling RegisterVideoFrameObserver . After you successfully register the video frame observer, the SDK triggers this callback each time it receives a video frame. You need to set your preferred video data in the return value of this callback.
+        /// </summary>
+        ///
+        /// <returns>
+        /// Sets the video format. See VIDEO_OBSERVER_FRAME_TYPE .
+        /// </returns>
+        ///
+        public virtual VIDEO_OBSERVER_FRAME_TYPE GetVideoFormatPreference()
+        {
+            return VIDEO_OBSERVER_FRAME_TYPE.FRAME_TYPE_RGBA;
+        }
+        
+        ///
+        /// <summary>
+        /// Sets the frame position for the video observer.
+        /// After successfully registering the video data observer, the SDK uses this callback to determine whether to trigger OnCaptureVideoFrame , OnRenderVideoFrame and OnPreEncodeVideoFrame callback at each specific video frame processing position, so that you can observe the locally collected video data, the video data sent by the remote end, and the video data before encoding. You can set one or more positions you need to observe by modifying the return value according to your scenario:POSITION_POST_CAPTURER(1 << 0) : The position after capturing the video data, which corresponds to the OnCaptureVideoFrame callback.POSITION_PRE_RENDERER(1 << 1): The position before receiving the remote video data, which corresponds to the OnRenderVideoFrame callback.POSITION_PRE_ENCODER(1 << 2): The position before encoding the video data, which corresponds to the OnPreEncodeVideoFrame callback.Use '|' (the OR operator) to observe multiple frame positions.This callback observesPOSITION_POST_CAPTURER (1 << 0) andPOSITION_PRE_RENDERER (1 << 1) by default.To conserve the system consumption, you can reduce the number of frame positions that you want to observe.
+        /// </summary>
+        ///
+        /// <returns>
+        /// A bit mask that controls the frame position of the video observer. See VIDEO_MODULE_POSITION .
+        /// </returns>
+        ///
+        public virtual VIDEO_OBSERVER_POSITION GetObservedFramePosition()
+        {
+            return VIDEO_OBSERVER_POSITION.POSITION_POST_CAPTURER | VIDEO_OBSERVER_POSITION.POSITION_PRE_RENDERER;
+        }
+    }
+}

+ 11 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/IVideoFrameObserver.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 56f697bc0ef3c40c8a976d871d322814
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/LangChaoRTC/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Code/Impl.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d5d83516a0b4546969b17561b281e26a
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

Some files were not shown because too many files changed in this diff