CustomCaptureVideo.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. using System.Collections;
  2. using UnityEngine;
  3. using UnityEngine.UI;
  4. using UnityEngine.Serialization;
  5. using Agora.Rtc;
  6. using Agora.Util;
  7. using Logger = Agora.Util.Logger;
  8. #if UNITY_2018_1_OR_NEWER
  9. using Unity.Collections;
  10. #endif
  11. namespace Agora_RTC_Plugin.API_Example.Examples.Advanced.CustomCaptureVideo
  12. {
  13. public class CustomCaptureVideo : MonoBehaviour
  14. {
  15. [FormerlySerializedAs("appIdInput")]
  16. [SerializeField]
  17. private AppIdInput _appIdInput;
  18. [Header("_____________Basic Configuration_____________")]
  19. [FormerlySerializedAs("APP_ID")]
  20. [SerializeField]
  21. private string _appID = "";
  22. [FormerlySerializedAs("TOKEN")]
  23. [SerializeField]
  24. private string _token = "";
  25. [FormerlySerializedAs("CHANNEL_NAME")]
  26. [SerializeField]
  27. private string _channelName = "";
  28. public Text LogText;
  29. internal Logger Log;
  30. internal IRtcEngine RtcEngine = null;
  31. private Texture2D _texture;
  32. private Rect _rect;
  33. private int i = 0;
  34. private WebCamTexture _webCameraTexture;
  35. public RawImage RawImage;
  36. public Vector2 CameraSize = new Vector2(640, 480);
  37. public int CameraFPS = 15;
  38. private byte[] _shareData;
  39. // Use this for initialization
  40. private void Start()
  41. {
  42. LoadAssetData();
  43. if (CheckAppId())
  44. {
  45. InitCameraDevice();
  46. InitTexture();
  47. InitEngine();
  48. SetExternalVideoSource();
  49. JoinChannel();
  50. }
  51. }
  52. private void Update()
  53. {
  54. PermissionHelper.RequestMicrophontPermission();
  55. StartCoroutine(ShareScreen());
  56. }
  57. //Show data in AgoraBasicProfile
  58. [ContextMenu("ShowAgoraBasicProfileData")]
  59. private void LoadAssetData()
  60. {
  61. if (_appIdInput == null) return;
  62. _appID = _appIdInput.appID;
  63. _token = _appIdInput.token;
  64. _channelName = _appIdInput.channelName;
  65. }
  66. private IEnumerator ShareScreen()
  67. {
  68. yield return new WaitForEndOfFrame();
  69. IRtcEngine rtc = Agora.Rtc.RtcEngine.Instance;
  70. if (rtc != null)
  71. {
  72. _texture.ReadPixels(_rect, 0, 0);
  73. _texture.Apply();
  74. #if UNITY_2018_1_OR_NEWER
  75. NativeArray<byte> nativeByteArray = _texture.GetRawTextureData<byte>();
  76. if (_shareData?.Length != nativeByteArray.Length)
  77. {
  78. _shareData = new byte[nativeByteArray.Length];
  79. }
  80. nativeByteArray.CopyTo(_shareData);
  81. #else
  82. _shareData = _texture.GetRawTextureData();
  83. #endif
  84. ExternalVideoFrame externalVideoFrame = new ExternalVideoFrame();
  85. externalVideoFrame.type = VIDEO_BUFFER_TYPE.VIDEO_BUFFER_RAW_DATA;
  86. externalVideoFrame.format = VIDEO_PIXEL_FORMAT.VIDEO_PIXEL_RGBA;
  87. externalVideoFrame.buffer = _shareData;
  88. externalVideoFrame.stride = (int)_rect.width;
  89. externalVideoFrame.height = (int)_rect.height;
  90. externalVideoFrame.cropLeft = 10;
  91. externalVideoFrame.cropTop = 10;
  92. externalVideoFrame.cropRight = 10;
  93. externalVideoFrame.cropBottom = 10;
  94. externalVideoFrame.rotation = 180;
  95. externalVideoFrame.timestamp = System.DateTime.Now.Ticks / 10000;
  96. var ret = rtc.PushVideoFrame(externalVideoFrame);
  97. Debug.Log("PushVideoFrame ret = " + ret + "time: " + System.DateTime.Now.Millisecond);
  98. }
  99. }
  100. private void InitEngine()
  101. {
  102. RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
  103. UserEventHandler handler = new UserEventHandler(this);
  104. RtcEngineContext context = new RtcEngineContext(_appID, 0,
  105. CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING,
  106. AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT);
  107. RtcEngine.Initialize(context);
  108. RtcEngine.InitEventHandler(handler);
  109. }
  110. private void SetExternalVideoSource()
  111. {
  112. var ret = RtcEngine.SetExternalVideoSource(true, false, EXTERNAL_VIDEO_SOURCE_TYPE.VIDEO_FRAME, new SenderOptions());
  113. this.Log.UpdateLog("SetExternalVideoSource returns:" + ret);
  114. }
  115. private void JoinChannel()
  116. {
  117. RtcEngine.EnableAudio();
  118. RtcEngine.EnableVideo();
  119. RtcEngine.SetClientRole(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
  120. RtcEngine.JoinChannel(_token, _channelName);
  121. }
  122. private bool CheckAppId()
  123. {
  124. Log = new Logger(LogText);
  125. return Log.DebugAssert(_appID.Length > 10, "Please fill in your appId in Canvas!!!!");
  126. }
  127. private void InitTexture()
  128. {
  129. _rect = new UnityEngine.Rect(0, 0, Screen.width, Screen.height);
  130. _texture = new Texture2D((int)_rect.width, (int)_rect.height, TextureFormat.RGBA32, false);
  131. }
  132. private void InitCameraDevice()
  133. {
  134. WebCamDevice[] devices = WebCamTexture.devices;
  135. _webCameraTexture = new WebCamTexture(devices[0].name, (int)CameraSize.x, (int)CameraSize.y, CameraFPS);
  136. RawImage.texture = _webCameraTexture;
  137. _webCameraTexture.Play();
  138. }
  139. private void OnDestroy()
  140. {
  141. Debug.Log("OnDestroy");
  142. if (_webCameraTexture)
  143. {
  144. _webCameraTexture.Stop();
  145. }
  146. if (RtcEngine == null) return;
  147. RtcEngine.InitEventHandler(null);
  148. RtcEngine.LeaveChannel();
  149. RtcEngine.Dispose();
  150. }
  151. internal string GetChannelName()
  152. {
  153. return _channelName;
  154. }
  155. #region -- Video Render UI Logic ---
  156. internal static void MakeVideoView(uint uid, string channelId = "")
  157. {
  158. GameObject go = GameObject.Find(uid.ToString());
  159. if (!ReferenceEquals(go, null))
  160. {
  161. return; // reuse
  162. }
  163. // create a GameObject and assign to this new user
  164. VideoSurface videoSurface = makeImageSurface(uid.ToString());
  165. if (!ReferenceEquals(videoSurface, null))
  166. {
  167. // configure videoSurface
  168. if (uid == 0)
  169. {
  170. videoSurface.SetForUser(uid, channelId);
  171. }
  172. else
  173. {
  174. videoSurface.SetForUser(uid, channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
  175. }
  176. videoSurface.OnTextureSizeModify += (int width, int height) =>
  177. {
  178. float scale = (float)height / (float)width;
  179. videoSurface.transform.localScale = new Vector3(-5, 5 * scale, 1);
  180. Debug.Log("OnTextureSizeModify: " + width + " " + height);
  181. };
  182. videoSurface.SetEnable(true);
  183. }
  184. }
  185. // VIDEO TYPE 1: 3D Object
  186. private static VideoSurface MakePlaneSurface(string goName)
  187. {
  188. GameObject go = GameObject.CreatePrimitive(PrimitiveType.Plane);
  189. if (go == null)
  190. {
  191. return null;
  192. }
  193. go.name = goName;
  194. // set up transform
  195. go.transform.Rotate(-90.0f, 0.0f, 0.0f);
  196. go.transform.position = Vector3.zero;
  197. go.transform.localScale = new Vector3(0.25f, 0.5f, .5f);
  198. // configure videoSurface
  199. VideoSurface videoSurface = go.AddComponent<VideoSurface>();
  200. return videoSurface;
  201. }
  202. // Video TYPE 2: RawImage
  203. private static VideoSurface makeImageSurface(string goName)
  204. {
  205. GameObject go = new GameObject();
  206. if (go == null)
  207. {
  208. return null;
  209. }
  210. go.name = goName;
  211. // to be renderered onto
  212. go.AddComponent<RawImage>();
  213. // make the object draggable
  214. go.AddComponent<UIElementDrag>();
  215. GameObject canvas = GameObject.Find("VideoCanvas");
  216. if (canvas != null)
  217. {
  218. go.transform.parent = canvas.transform;
  219. Debug.Log("add video view");
  220. }
  221. else
  222. {
  223. Debug.Log("Canvas is null video view");
  224. }
  225. // set up transform
  226. go.transform.Rotate(0f, 0.0f, 180.0f);
  227. go.transform.localPosition = Vector3.zero;
  228. go.transform.localScale = new Vector3(3f, 4f, 1f);
  229. // configure videoSurface
  230. VideoSurface videoSurface = go.AddComponent<VideoSurface>();
  231. return videoSurface;
  232. }
  233. internal static void DestroyVideoView(uint uid)
  234. {
  235. GameObject go = GameObject.Find(uid.ToString());
  236. if (!ReferenceEquals(go, null))
  237. {
  238. Destroy(go);
  239. }
  240. }
  241. #endregion
  242. }
  243. #region -- Agora Event ---
  244. internal class UserEventHandler : IRtcEngineEventHandler
  245. {
  246. private readonly CustomCaptureVideo _customCaptureVideo;
  247. internal UserEventHandler(CustomCaptureVideo customCaptureVideo)
  248. {
  249. _customCaptureVideo = customCaptureVideo;
  250. }
  251. public override void OnError(int err, string msg)
  252. {
  253. _customCaptureVideo.Log.UpdateLog(string.Format("OnError err: {0}, msg: {1}", err, msg));
  254. }
  255. public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
  256. {
  257. int build = 0;
  258. _customCaptureVideo.Log.UpdateLog(string.Format("sdk version: ${0}",
  259. _customCaptureVideo.RtcEngine.GetVersion(ref build)));
  260. _customCaptureVideo.Log.UpdateLog(
  261. string.Format("OnJoinChannelSuccess channelName: {0}, uid: {1}, elapsed: {2}",
  262. connection.channelId, connection.localUid, elapsed));
  263. }
  264. public override void OnRejoinChannelSuccess(RtcConnection connection, int elapsed)
  265. {
  266. _customCaptureVideo.Log.UpdateLog("OnRejoinChannelSuccess");
  267. }
  268. public override void OnLeaveChannel(RtcConnection connection, RtcStats stats)
  269. {
  270. _customCaptureVideo.Log.UpdateLog("OnLeaveChannel");
  271. }
  272. public override void OnClientRoleChanged(RtcConnection connection, CLIENT_ROLE_TYPE oldRole,
  273. CLIENT_ROLE_TYPE newRole)
  274. {
  275. _customCaptureVideo.Log.UpdateLog("OnClientRoleChanged");
  276. }
  277. public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
  278. {
  279. _customCaptureVideo.Log.UpdateLog(
  280. string.Format("OnUserJoined uid: ${0} elapsed: ${1}", uid, elapsed));
  281. CustomCaptureVideo.MakeVideoView(uid, _customCaptureVideo.GetChannelName());
  282. }
  283. public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason)
  284. {
  285. _customCaptureVideo.Log.UpdateLog(string.Format("OnUserOffLine uid: ${0}, reason: ${1}", uid,
  286. (int)reason));
  287. CustomCaptureVideo.DestroyVideoView(uid);
  288. }
  289. }
  290. #endregion
  291. }