PushEncodedVideoImage.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. using Agora.Rtc;
  5. using Agora.Util;
  6. using UnityEngine;
  7. using UnityEngine.Serialization;
  8. using UnityEngine.UI;
  9. using Logger = Agora.Util.Logger;
  10. namespace Agora_RTC_Plugin.API_Example.Examples.Advanced.PushEncodedVideoImage
  11. {
  12. public class PushEncodedVideoImage : MonoBehaviour
  13. {
  14. [FormerlySerializedAs("appIdInput")]
  15. [SerializeField]
  16. private AppIdInput _appIdInput;
  17. [Header("_____________Basic Configuration_____________")]
  18. [FormerlySerializedAs("APP_ID")]
  19. [SerializeField]
  20. public string _appID = "";
  21. [FormerlySerializedAs("TOKEN")]
  22. [SerializeField]
  23. public string _token = "";
  24. [FormerlySerializedAs("CHANNEL_NAME")]
  25. [SerializeField]
  26. public string _channelName = "";
  27. public GameObject RolePrefab;
  28. private GameObject _roleLocal;
  29. public Text LogText;
  30. internal Logger Log;
  31. internal IRtcEngineEx RtcEngine = null;
  32. public Dictionary<string, Vector3> RolePositionDic = new Dictionary<string, Vector3>();
  33. internal uint Uid1;
  34. internal uint Uid2;
  35. private System.Random _random = new System.Random();
  36. private void Start()
  37. {
  38. LoadAssetData();
  39. if (CheckAppId())
  40. {
  41. Uid1 = (uint)(_random.Next() % 1000);
  42. Uid2 = (uint)(_random.Next() % 1000 + 1000);
  43. InitEngine();
  44. JoinChannel1();
  45. JoinChannel2();
  46. }
  47. }
  48. [ContextMenu("ShowAgoraBasicProfileData")]
  49. private void LoadAssetData()
  50. {
  51. if (_appIdInput == null) return;
  52. _appID = _appIdInput.appID;
  53. _token = _appIdInput.token;
  54. _channelName = _appIdInput.channelName;
  55. }
  56. private bool CheckAppId()
  57. {
  58. Log = new Logger(LogText);
  59. return Log.DebugAssert(_appID.Length > 10, "Please fill in your appId in API-Example/profile/appIdInput.asset");
  60. }
  61. private void InitEngine()
  62. {
  63. RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngineEx();
  64. UserEventHandler handler = new UserEventHandler(this);
  65. RtcEngineContext context = new RtcEngineContext(_appID, 0,
  66. CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING,
  67. AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_GAME_STREAMING);
  68. RtcEngine.Initialize(context);
  69. RtcEngine.InitEventHandler(handler);
  70. RtcEngine.RegisterVideoEncodedFrameObserver(new VideoEncodedImageReceiver(this), OBSERVER_MODE.INTPTR);
  71. }
  72. private void JoinChannel1()
  73. {
  74. RtcEngine.SetClientRole(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
  75. RtcEngine.EnableVideo();
  76. var option = new ChannelMediaOptions();
  77. option.autoSubscribeVideo.SetValue(true);
  78. option.autoSubscribeAudio.SetValue(true);
  79. option.publishCameraTrack.SetValue(true);
  80. option.publishMicrophoneTrack.SetValue(true);
  81. option.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
  82. option.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING);
  83. var connection = new RtcConnection();
  84. connection.channelId = _channelName;
  85. connection.localUid = Uid1;
  86. var nRet = RtcEngine.JoinChannelEx(_token, connection, option);
  87. this.Log.UpdateLog("joinChanne1: nRet" + nRet + " uid1:" + Uid1);
  88. }
  89. private void JoinChannel2()
  90. {
  91. RtcEngine.SetClientRole(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
  92. RtcEngine.EnableVideo();
  93. SenderOptions senderOptions = new SenderOptions();
  94. senderOptions.codecType = VIDEO_CODEC_TYPE.VIDEO_CODEC_GENERIC;
  95. RtcEngine.SetExternalVideoSource(true, true, EXTERNAL_VIDEO_SOURCE_TYPE.ENCODED_VIDEO_FRAME, senderOptions);
  96. var option = new ChannelMediaOptions();
  97. option.autoSubscribeVideo.SetValue(true);
  98. option.autoSubscribeAudio.SetValue(true);
  99. option.publishCustomAudioTrack.SetValue(false);
  100. option.publishCameraTrack.SetValue(false);
  101. option.publishCustomVideoTrack.SetValue(false);
  102. option.publishEncodedVideoTrack.SetValue(true);
  103. option.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
  104. option.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING);
  105. var connection = new RtcConnection();
  106. connection.channelId = _channelName;
  107. connection.localUid = Uid2;
  108. var nRet = RtcEngine.JoinChannelEx(_token, connection, option);
  109. this.Log.UpdateLog("joinChanne1: nRet" + nRet + " uid2:" + Uid2);
  110. }
  111. private void Update()
  112. {
  113. PermissionHelper.RequestMicrophontPermission();
  114. PermissionHelper.RequestCameraPermission();
  115. lock (this.RolePositionDic)
  116. {
  117. foreach (var e in this.RolePositionDic)
  118. {
  119. this.UpdateRolePositon(e.Key, e.Value);
  120. }
  121. }
  122. }
  123. private void OnDestroy()
  124. {
  125. Debug.Log("OnDestroy");
  126. if (RtcEngine == null) return;
  127. RtcEngine.InitEventHandler(null);
  128. RtcEngine.UnRegisterVideoEncodedFrameObserver();
  129. var connection = new RtcConnection();
  130. connection.channelId = _channelName;
  131. connection.localUid = Uid1;
  132. RtcEngine.LeaveChannelEx(connection);
  133. connection.localUid = Uid2;
  134. RtcEngine.LeaveChannelEx(connection);
  135. RtcEngine.Dispose();
  136. }
  137. public void CreateRole(string uid, bool isLocal)
  138. {
  139. if (GameObject.Find("Role" + uid) != null)
  140. {
  141. return;
  142. }
  143. var role = Instantiate(this.RolePrefab, this.transform);
  144. role.name = "Role" + uid;
  145. var text = role.transform.Find("Text").GetComponent<Text>();
  146. text.text = uid;
  147. if (isLocal)
  148. {
  149. text.text += "\n(Local)";
  150. role.AddComponent<UIElementDrag>();
  151. this._roleLocal = role;
  152. }
  153. else if (this._roleLocal != null)
  154. {
  155. var count = this.transform.childCount;
  156. this._roleLocal.transform.SetSiblingIndex(count - 1);
  157. }
  158. role.GetComponent<RectTransform>().anchoredPosition = Vector3.zero;
  159. }
  160. public void DestroyRole(string uid, bool isLocal)
  161. {
  162. var name = "Role" + uid;
  163. var role = this.gameObject.transform.Find(name).gameObject;
  164. if (role)
  165. {
  166. Destroy(role);
  167. }
  168. if (isLocal)
  169. {
  170. this._roleLocal = null;
  171. }
  172. }
  173. private void UpdateRolePositon(string uid, Vector3 pos)
  174. {
  175. var name = "Role" + uid;
  176. var role = this.gameObject.transform.Find(name);
  177. if (role)
  178. {
  179. role.transform.localPosition = pos;
  180. }
  181. }
  182. public void StartPushEncodeVideoImage()
  183. {
  184. this.InvokeRepeating("UpdateForPushEncodeVideoImage", 0, 0.1f);
  185. this.Log.UpdateLog("Start PushEncodeVideoImage in every frame");
  186. }
  187. public void StopPushEncodeVideoImage()
  188. {
  189. this.CancelInvoke("UpdateForPushEncodeVideoImage");
  190. this.Log.UpdateLog("Stop PushEncodeVideoImage");
  191. }
  192. internal string GetChannelName()
  193. {
  194. return _channelName;
  195. }
  196. private void UpdateForPushEncodeVideoImage()
  197. {
  198. //you can send any data not just video image byte
  199. if (_roleLocal)
  200. {
  201. //in this case, we send pos byte
  202. string json = JsonUtility.ToJson(this._roleLocal.transform.localPosition);
  203. byte[] data = System.Text.Encoding.Default.GetBytes(json);
  204. EncodedVideoFrameInfo encodedVideoFrameInfo = new EncodedVideoFrameInfo()
  205. {
  206. framesPerSecond = 60,
  207. codecType = VIDEO_CODEC_TYPE.VIDEO_CODEC_GENERIC,
  208. frameType = VIDEO_FRAME_TYPE_NATIVE.VIDEO_FRAME_TYPE_KEY_FRAME
  209. };
  210. int nRet = this.RtcEngine.PushEncodedVideoImage(data, Convert.ToUInt32(data.Length), encodedVideoFrameInfo);
  211. Debug.Log("PushEncodedVideoImage: " + nRet);
  212. }
  213. }
  214. internal static void MakeVideoView(uint uid, string channelId = "")
  215. {
  216. var go = GameObject.Find(uid.ToString());
  217. if (!ReferenceEquals(go, null))
  218. {
  219. return; // reuse
  220. }
  221. // create a GameObject and assign to this new user
  222. var videoSurface = MakeImageSurface(uid.ToString());
  223. if (ReferenceEquals(videoSurface, null)) return;
  224. // configure videoSurface
  225. if (uid == 0)
  226. {
  227. videoSurface.SetForUser(uid, channelId);
  228. }
  229. else
  230. {
  231. videoSurface.SetForUser(uid, channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
  232. }
  233. videoSurface.OnTextureSizeModify += (int width, int height) =>
  234. {
  235. float scale = (float)height / (float)width;
  236. videoSurface.transform.localScale = new Vector3(-5, 5 * scale, 1);
  237. Debug.Log("OnTextureSizeModify: " + width + " " + height);
  238. };
  239. videoSurface.SetEnable(true);
  240. }
  241. // VIDEO TYPE 1: 3D Object
  242. private static VideoSurface MakePlaneSurface(string goName)
  243. {
  244. var go = GameObject.CreatePrimitive(PrimitiveType.Plane);
  245. if (go == null)
  246. {
  247. return null;
  248. }
  249. go.name = goName;
  250. // set up transform
  251. go.transform.Rotate(-90.0f, 0.0f, 0.0f);
  252. var yPos = UnityEngine.Random.Range(3.0f, 5.0f);
  253. var xPos = UnityEngine.Random.Range(-2.0f, 2.0f);
  254. go.transform.position = Vector3.zero;
  255. go.transform.localScale = new Vector3(0.25f, 0.5f, 0.5f);
  256. // configure videoSurface
  257. var videoSurface = go.AddComponent<VideoSurface>();
  258. return videoSurface;
  259. }
  260. // Video TYPE 2: RawImage
  261. private static VideoSurface MakeImageSurface(string goName)
  262. {
  263. GameObject go = new GameObject();
  264. if (go == null)
  265. {
  266. return null;
  267. }
  268. go.name = goName;
  269. // to be renderered onto
  270. go.AddComponent<RawImage>();
  271. // make the object draggable
  272. go.AddComponent<UIElementDrag>();
  273. var canvas = GameObject.Find("VideoCanvas");
  274. if (canvas != null)
  275. {
  276. go.transform.parent = canvas.transform;
  277. Debug.Log("add video view");
  278. }
  279. else
  280. {
  281. Debug.Log("Canvas is null video view");
  282. }
  283. // set up transform
  284. go.transform.Rotate(0f, 0.0f, 180.0f);
  285. go.transform.localPosition = Vector3.zero;
  286. go.transform.localScale = new Vector3(2f, 3f, 1f);
  287. // configure videoSurface
  288. var videoSurface = go.AddComponent<VideoSurface>();
  289. return videoSurface;
  290. }
  291. internal static void DestroyVideoView(uint uid)
  292. {
  293. var go = GameObject.Find(uid.ToString());
  294. if (!ReferenceEquals(go, null))
  295. {
  296. Destroy(go);
  297. }
  298. }
  299. }
  300. #region -- Agora Event ---
  301. internal class UserEventHandler : IRtcEngineEventHandler
  302. {
  303. private readonly PushEncodedVideoImage _pushEncodedVideoImage;
  304. internal UserEventHandler(PushEncodedVideoImage videoSample)
  305. {
  306. _pushEncodedVideoImage = videoSample;
  307. }
  308. public override void OnError(int err, string msg)
  309. {
  310. _pushEncodedVideoImage.Log.UpdateLog(string.Format("OnError err: {0}, msg: {1}", err, msg));
  311. }
  312. public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
  313. {
  314. int build = 0;
  315. Debug.Log("Agora: OnJoinChannelSuccess ");
  316. _pushEncodedVideoImage.Log.UpdateLog(string.Format("sdk version: ${0}",
  317. _pushEncodedVideoImage.RtcEngine.GetVersion(ref build)));
  318. _pushEncodedVideoImage.Log.UpdateLog(
  319. string.Format("OnJoinChannelSuccess channelName: {0}, uid: {1}, elapsed: {2}",
  320. connection.channelId, connection.localUid, elapsed));
  321. if (connection.localUid >= 1000)
  322. {
  323. _pushEncodedVideoImage.CreateRole(connection.localUid.ToString(), true);
  324. _pushEncodedVideoImage.Log.UpdateLog("you can drag your role to every where");
  325. _pushEncodedVideoImage.StartPushEncodeVideoImage();
  326. }
  327. else
  328. {
  329. PushEncodedVideoImage.MakeVideoView(0);
  330. }
  331. }
  332. public override void OnRejoinChannelSuccess(RtcConnection connection, int elapsed)
  333. {
  334. _pushEncodedVideoImage.Log.UpdateLog("OnRejoinChannelSuccess");
  335. }
  336. public override void OnLeaveChannel(RtcConnection connection, RtcStats stats)
  337. {
  338. _pushEncodedVideoImage.Log.UpdateLog("OnLeaveChannel");
  339. if (connection.localUid >= 1000)
  340. {
  341. _pushEncodedVideoImage.DestroyRole(connection.localUid.ToString(), true);
  342. _pushEncodedVideoImage.StopPushEncodeVideoImage();
  343. }
  344. else
  345. {
  346. PushEncodedVideoImage.DestroyVideoView(0);
  347. }
  348. }
  349. public override void OnClientRoleChanged(RtcConnection connection, CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole)
  350. {
  351. _pushEncodedVideoImage.Log.UpdateLog("OnClientRoleChanged");
  352. }
  353. public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
  354. {
  355. _pushEncodedVideoImage.Log.UpdateLog(string.Format("OnUserJoined uid: ${0} elapsed: ${1}", uid, elapsed));
  356. if (uid == _pushEncodedVideoImage.Uid1 || uid == _pushEncodedVideoImage.Uid2)
  357. return;
  358. if (uid >= 1000)
  359. {
  360. _pushEncodedVideoImage.CreateRole(uid.ToString(), false);
  361. //you must set options.encodedFrameOnly = true when you receive other
  362. VideoSubscriptionOptions options = new VideoSubscriptionOptions();
  363. options.encodedFrameOnly.SetValue(true);
  364. int nRet = _pushEncodedVideoImage.RtcEngine.SetRemoteVideoSubscriptionOptionsEx(uid, options, connection);
  365. _pushEncodedVideoImage.Log.UpdateLog("SetRemoteVideoSubscriptionOptions nRet:" + nRet);
  366. }
  367. else
  368. {
  369. PushEncodedVideoImage.MakeVideoView(uid, _pushEncodedVideoImage.GetChannelName());
  370. }
  371. }
  372. public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason)
  373. {
  374. _pushEncodedVideoImage.Log.UpdateLog(string.Format("OnUserOffLine uid: ${0}, reason: ${1}", uid,
  375. (int)reason));
  376. if (uid == _pushEncodedVideoImage.Uid1 || uid == _pushEncodedVideoImage.Uid2)
  377. return;
  378. if (uid >= 1000)
  379. {
  380. _pushEncodedVideoImage.DestroyRole(uid.ToString(), false);
  381. }
  382. else
  383. {
  384. PushEncodedVideoImage.DestroyVideoView(uid);
  385. }
  386. }
  387. public override void OnChannelMediaRelayEvent(int code)
  388. {
  389. _pushEncodedVideoImage.Log.UpdateLog(string.Format("OnChannelMediaRelayEvent: {0}", code));
  390. }
  391. public override void OnChannelMediaRelayStateChanged(int state, int code)
  392. {
  393. _pushEncodedVideoImage.Log.UpdateLog(string.Format("OnChannelMediaRelayStateChanged state: {0}, code: {1}", state, code));
  394. }
  395. }
  396. internal class VideoEncodedImageReceiver : IVideoEncodedFrameObserver
  397. {
  398. private readonly PushEncodedVideoImage _pushEncodedVideoImage;
  399. internal VideoEncodedImageReceiver(PushEncodedVideoImage videoSample)
  400. {
  401. _pushEncodedVideoImage = videoSample;
  402. }
  403. public override bool OnEncodedVideoFrameReceived(uint uid, IntPtr imageBufferPtr, UInt64 length, EncodedVideoFrameInfo videoEncodedFrameInfo)
  404. {
  405. byte[] imageBuffer = new byte[length];
  406. Marshal.Copy(imageBufferPtr, imageBuffer, 0, (int)length);
  407. string str = System.Text.Encoding.Default.GetString(imageBuffer);
  408. var pos = JsonUtility.FromJson<Vector3>(str);
  409. var uidStr = uid.ToString();
  410. Debug.Log("OnEncodedVideoImageReceived" + uid + " pos" + str);
  411. //this called is not in Unity MainThread.we need push data in this dic. And read it in Update()
  412. lock (_pushEncodedVideoImage.RolePositionDic)
  413. {
  414. if (_pushEncodedVideoImage.RolePositionDic.ContainsKey(uidStr))
  415. {
  416. _pushEncodedVideoImage.RolePositionDic[uidStr] = pos;
  417. }
  418. else
  419. {
  420. _pushEncodedVideoImage.RolePositionDic.Add(uidStr, pos);
  421. }
  422. }
  423. return true;
  424. }
  425. }
  426. #endregion
  427. }