StartLocalVideoTranscoder.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  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.StartLocalVideoTranscoder
  11. {
  12. public class StartLocalVideoTranscoder : MonoBehaviour
  13. {
  14. [FormerlySerializedAs("appIdInput")]
  15. [SerializeField]
  16. private AppIdInput _appIdInput;
  17. [Header("_____________Basic Configuration_____________")]
  18. [FormerlySerializedAs("APP_ID")]
  19. [SerializeField]
  20. private string _appID = "";
  21. [FormerlySerializedAs("TOKEN")]
  22. [SerializeField]
  23. private string _token = "";
  24. [FormerlySerializedAs("CHANNEL_NAME")]
  25. [SerializeField]
  26. private string _channelName = "";
  27. public Text LogText;
  28. internal Logger Log;
  29. internal IRtcEngine RtcEngine = null;
  30. internal IMediaPlayer MediaPlayer = null;
  31. internal List<uint> RemoteUserUids = new List<uint>();
  32. public Toggle ToggleRecord;
  33. public Toggle TogglePrimartCamera;
  34. public Toggle ToggleSecondaryCamera;
  35. public Toggle TogglePng;
  36. public Toggle ToggleJpg;
  37. public Toggle ToggleGif;
  38. public Toggle ToggleRemote;
  39. public Toggle ToggleScreenShare;
  40. public Toggle ToggleMediaPlay;
  41. private void Start()
  42. {
  43. LoadAssetData();
  44. if (CheckAppId())
  45. {
  46. SetUpUI();
  47. InitEngine();
  48. InitMediaPlayer();
  49. JoinChannel();
  50. }
  51. }
  52. // Update is called once per frame
  53. private void Update()
  54. {
  55. PermissionHelper.RequestMicrophontPermission();
  56. PermissionHelper.RequestCameraPermission();
  57. }
  58. //Show data in AgoraBasicProfile
  59. [ContextMenu("ShowAgoraBasicProfileData")]
  60. private void LoadAssetData()
  61. {
  62. if (_appIdInput == null) return;
  63. _appID = _appIdInput.appID;
  64. _token = _appIdInput.token;
  65. _channelName = _appIdInput.channelName;
  66. }
  67. private void SetUpUI()
  68. {
  69. var ui = this.transform.Find("UI");
  70. var btn = ui.Find("StartButton").GetComponent<Button>();
  71. btn.onClick.AddListener(OnStartButtonPress);
  72. btn = ui.Find("UpdateButton").GetComponent<Button>();
  73. btn.onClick.AddListener(OnUpdateButtonPress);
  74. btn = ui.Find("StopButton").GetComponent<Button>();
  75. btn.onClick.AddListener(OnStopButtonPress);
  76. }
  77. private bool CheckAppId()
  78. {
  79. Log = new Logger(LogText);
  80. return Log.DebugAssert(_appID.Length > 10, "Please fill in your appId in API-Example/profile/appIdInput.asset");
  81. }
  82. private void InitEngine()
  83. {
  84. RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
  85. UserEventHandler handler = new UserEventHandler(this);
  86. RtcEngineContext context = new RtcEngineContext(_appID, 0,
  87. CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING,
  88. AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT);
  89. var ret = RtcEngine.Initialize(context);
  90. Debug.Log("Agora: Initialize " + ret);
  91. RtcEngine.InitEventHandler(handler);
  92. }
  93. private void InitMediaPlayer()
  94. {
  95. MediaPlayer = RtcEngine.CreateMediaPlayer();
  96. if (MediaPlayer == null)
  97. {
  98. Debug.Log("GetAgoraRtcMediaPlayer failed!");
  99. }
  100. MpkEventHandler handler = new MpkEventHandler(this);
  101. MediaPlayer.InitEventHandler(handler);
  102. Debug.Log("playerId id: " + MediaPlayer.GetId());
  103. }
  104. private void JoinChannel()
  105. {
  106. RtcEngine.EnableAudio();
  107. RtcEngine.EnableVideo();
  108. RtcEngine.SetClientRole(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
  109. MakeVideoView(0, "", VIDEO_SOURCE_TYPE.VIDEO_SOURCE_TRANSCODED);
  110. var options = new ChannelMediaOptions();
  111. options.publishCameraTrack.SetValue(false);
  112. options.publishSecondaryCameraTrack.SetValue(false);
  113. options.publishTrancodedVideoTrack.SetValue(true);
  114. RtcEngine.JoinChannel(_token, _channelName, 0, options);
  115. }
  116. private LocalTranscoderConfiguration GenerateLocalTranscoderConfiguration()
  117. {
  118. List<TranscodingVideoStream> list = new List<TranscodingVideoStream>();
  119. if (this.ToggleRecord.isOn)
  120. {
  121. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.AUDIO_RECORDING_SOURCE, 0, "", 0, 0, 0, 0, 1, 1, false));
  122. }
  123. if (this.TogglePrimartCamera.isOn)
  124. {
  125. var videoDeviceManager = RtcEngine.GetVideoDeviceManager();
  126. var devices = videoDeviceManager.EnumerateVideoDevices();
  127. if (devices.Length >= 1)
  128. {
  129. var configuration = new CameraCapturerConfiguration()
  130. {
  131. format = new VideoFormat(640, 320, 30),
  132. deviceId = devices[0].deviceId
  133. };
  134. var nRet = this.RtcEngine.StartPrimaryCameraCapture(configuration);
  135. this.Log.UpdateLog("StartPrimaryCameraCapture :" + nRet);
  136. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.PRIMARY_CAMERA_SOURCE, 0, "", 0, 0, 640, 320, 1, 1, false));
  137. }
  138. else
  139. {
  140. this.Log.UpdateLog("PRIMARY_CAMERA Not Found!");
  141. }
  142. }
  143. if (this.ToggleSecondaryCamera.isOn)
  144. {
  145. var videoDeviceManager = RtcEngine.GetVideoDeviceManager();
  146. var devices = videoDeviceManager.EnumerateVideoDevices();
  147. if (devices.Length >= 2)
  148. {
  149. var configuration = new CameraCapturerConfiguration()
  150. {
  151. format = new VideoFormat(640, 320, 30),
  152. deviceId = devices[0].deviceId
  153. };
  154. this.RtcEngine.StartSecondaryCameraCapture(configuration);
  155. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.SECONDARY_CAMERA_SOURCE, 0, "", 0, 0, 360, 240, 1, 1, false));
  156. }
  157. else
  158. {
  159. this.Log.UpdateLog("SECONDARY_CAMERA Not Found!");
  160. }
  161. }
  162. if (this.TogglePng.isOn)
  163. {
  164. #if UNITY_ANDROID && !UNITY_EDITOR
  165. // On Android, the StreamingAssetPath is just accessed by /assets instead of Application.streamingAssetPath
  166. var filePath = "/assets/img/png.png";
  167. #else
  168. var filePath = Path.Combine(Application.streamingAssetsPath, "img/png.png");
  169. #endif
  170. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.RTC_IMAGE_PNG_SOURCE, 0, filePath, 320, 180, 640, 360, 1, 1, false));
  171. }
  172. if (this.ToggleJpg.isOn)
  173. {
  174. #if UNITY_ANDROID && !UNITY_EDITOR
  175. // On Android, the StreamingAssetPath is just accessed by /assets instead of Application.streamingAssetPath
  176. var filePath = "/assets/img/jpg.jpg";
  177. #else
  178. var filePath = Path.Combine(Application.streamingAssetsPath, "img/jpg.jpg");
  179. #endif
  180. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.RTC_IMAGE_JPEG_SOURCE, 0, filePath, 360, 240, 360, 240, 1, 1, false));
  181. }
  182. if (this.ToggleGif.isOn)
  183. {
  184. #if UNITY_ANDROID && !UNITY_EDITOR
  185. // On Android, the StreamingAssetPath is just accessed by /assets instead of Application.streamingAssetPath
  186. var filePath = "/assets/img/gif.gif";
  187. #else
  188. var filePath = Path.Combine(Application.streamingAssetsPath, "img/gif.gif");
  189. #endif
  190. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.RTC_IMAGE_GIF_SOURCE, 0, filePath, 0, 0, 476, 280, 1, 1, false));
  191. }
  192. if (this.ToggleRemote.isOn)
  193. {
  194. if (this.RemoteUserUids.Count >= 1)
  195. {
  196. var remoteUserUid = this.RemoteUserUids[0];
  197. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.REMOTE_VIDEO_SOURCE, remoteUserUid, "", 200, 200, 100, 100, 1, 1, false));
  198. }
  199. else
  200. {
  201. this.Log.UpdateLog("remote user not found");
  202. }
  203. }
  204. if (this.ToggleScreenShare.isOn)
  205. {
  206. if (this.StartScreenShare())
  207. {
  208. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.PRIMARY_SCREEN_SOURCE, 0, "", 480, 640, 640, 320, 1, 1, false));
  209. }
  210. }
  211. else
  212. {
  213. this.StopScreenShare();
  214. }
  215. if (this.ToggleMediaPlay.isOn)
  216. {
  217. var ret = this.MediaPlayer.Open("https://big-class-test.oss-cn-hangzhou.aliyuncs.com/61102.1592987815092.mp4", 0);
  218. this.Log.UpdateLog("Media palyer ret:" + ret);
  219. var sourceId = this.MediaPlayer.GetId();
  220. this.Log.UpdateLog("Media palyer ret:" + ret);
  221. list.Add(new TranscodingVideoStream(MEDIA_SOURCE_TYPE.MEDIA_PLAYER_SOURCE, 0, sourceId.ToString(), 0, 0, 1080, 960, 1, 1, false));
  222. }
  223. else
  224. {
  225. this.MediaPlayer.Stop();
  226. }
  227. var conf = new LocalTranscoderConfiguration();
  228. conf.streamCount = Convert.ToUInt32(list.Count);
  229. conf.VideoInputStreams = new TranscodingVideoStream[list.Count];
  230. for (int i = 0; i < list.Count; i++)
  231. {
  232. conf.VideoInputStreams[i] = list[i];
  233. }
  234. conf.videoOutputConfiguration.dimensions.width = 1080;
  235. conf.videoOutputConfiguration.dimensions.height = 960;
  236. return conf;
  237. }
  238. private bool StartScreenShare()
  239. {
  240. #if UNITY_IPHONE || UNITY_ANDROID
  241. this.Log.UpdateLog("Not Support Screen Share in this platform!");
  242. return false;
  243. #else
  244. SIZE t = new SIZE();
  245. t.width = 360;
  246. t.height = 240;
  247. SIZE s = new SIZE();
  248. s.width = 360;
  249. s.height = 240;
  250. var info = RtcEngine.GetScreenCaptureSources(t, s, true);
  251. if (info.Length > 0)
  252. {
  253. ScreenCaptureSourceInfo item = info[0];
  254. if (item.type == ScreenCaptureSourceType.ScreenCaptureSourceType_Window)
  255. {
  256. RtcEngine.StartScreenCaptureByWindowId(item.sourceId, default(Rectangle),
  257. default(ScreenCaptureParameters));
  258. }
  259. else
  260. {
  261. RtcEngine.StartScreenCaptureByDisplayId((uint)item.sourceId, default(Rectangle),
  262. new ScreenCaptureParameters { captureMouseCursor = true, frameRate = 30 });
  263. }
  264. return true;
  265. }
  266. else
  267. {
  268. this.Log.UpdateLog("Not Screen can share");
  269. return false;
  270. }
  271. #endif
  272. }
  273. private void StopScreenShare()
  274. {
  275. #if UNITY_IPHONE || UNITY_ANDROID
  276. this.Log.UpdateLog("Not Support Screen Share in this platform!");
  277. #else
  278. RtcEngine.StopScreenCapture();
  279. #endif
  280. }
  281. private void OnStartButtonPress()
  282. {
  283. var conf = this.GenerateLocalTranscoderConfiguration();
  284. var nRet = RtcEngine.StartLocalVideoTranscoder(conf);
  285. this.Log.UpdateLog("StartLocalVideoTranscoder:" + nRet);
  286. var options = new ChannelMediaOptions();
  287. options.publishCameraTrack.SetValue(false);
  288. options.publishSecondaryCameraTrack.SetValue(false);
  289. options.publishTrancodedVideoTrack.SetValue(true);
  290. RtcEngine.UpdateChannelMediaOptions(options);
  291. }
  292. private void OnUpdateButtonPress()
  293. {
  294. var conf = this.GenerateLocalTranscoderConfiguration();
  295. var nRet = RtcEngine.UpdateLocalTranscoderConfiguration(conf);
  296. this.Log.UpdateLog("UpdateLocalTranscoderConfiguration:" + nRet);
  297. }
  298. private void OnStopButtonPress()
  299. {
  300. var nRet = RtcEngine.StopLocalVideoTranscoder();
  301. this.Log.UpdateLog("StopLocalVideoTranscoder:" + nRet);
  302. MediaPlayer.Stop();
  303. }
  304. private void OnDestroy()
  305. {
  306. Debug.Log("OnDestroy");
  307. if (RtcEngine == null) return;
  308. RtcEngine.InitEventHandler(null);
  309. RtcEngine.LeaveChannel();
  310. RtcEngine.Dispose();
  311. }
  312. internal string GetChannelName()
  313. {
  314. return _channelName;
  315. }
  316. #region -- Video Render UI Logic ---
  317. internal static VideoSurface MakeVideoView(uint uid, string channelId = "", VIDEO_SOURCE_TYPE source = VIDEO_SOURCE_TYPE.VIDEO_SOURCE_CAMERA)
  318. {
  319. var go = GameObject.Find(uid.ToString());
  320. if (!ReferenceEquals(go, null))
  321. {
  322. return null;
  323. }
  324. // create a GameObject and assign to this new user
  325. var videoSurface = MakeImageSurface(uid.ToString());
  326. if (ReferenceEquals(videoSurface, null)) return null;
  327. // configure videoSurface
  328. if (uid == 0)
  329. {
  330. videoSurface.SetForUser(uid, channelId, source);
  331. }
  332. else
  333. {
  334. videoSurface.SetForUser(uid, channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
  335. }
  336. videoSurface.OnTextureSizeModify += (int width, int height) =>
  337. {
  338. float scale = (float)height / (float)width;
  339. videoSurface.transform.localScale = new Vector3(-5, 5 * scale, 1);
  340. Debug.Log("OnTextureSizeModify: " + width + " " + height);
  341. };
  342. videoSurface.SetEnable(true);
  343. return videoSurface;
  344. }
  345. // VIDEO TYPE 1: 3D Object
  346. private static VideoSurface MakePlaneSurface(string goName)
  347. {
  348. var go = GameObject.CreatePrimitive(PrimitiveType.Plane);
  349. if (go == null)
  350. {
  351. return null;
  352. }
  353. go.name = goName;
  354. // set up transform
  355. go.transform.position = Vector3.zero;
  356. go.transform.localScale = new Vector3(0.25f, 0.5f, 0.5f);
  357. // configure videoSurface
  358. var videoSurface = go.AddComponent<VideoSurface>();
  359. return videoSurface;
  360. }
  361. // Video TYPE 2: RawImage
  362. private static VideoSurface MakeImageSurface(string goName)
  363. {
  364. GameObject go = new GameObject();
  365. if (go == null)
  366. {
  367. return null;
  368. }
  369. go.name = goName;
  370. // to be renderered onto
  371. go.AddComponent<RawImage>();
  372. // make the object draggable
  373. go.AddComponent<UIElementDrag>();
  374. var canvas = GameObject.Find("VideoCanvas");
  375. if (canvas != null)
  376. {
  377. go.transform.parent = canvas.transform;
  378. Debug.Log("add video view");
  379. }
  380. else
  381. {
  382. Debug.Log("Canvas is null video view");
  383. }
  384. // set up transform
  385. go.transform.Rotate(0f, 0.0f, 180.0f);
  386. go.transform.localPosition = Vector3.zero;
  387. go.transform.localScale = new Vector3(2f, 3f, 1f);
  388. // configure videoSurface
  389. var videoSurface = go.AddComponent<VideoSurface>();
  390. return videoSurface;
  391. }
  392. internal static void DestroyVideoView(uint uid)
  393. {
  394. var go = GameObject.Find(uid.ToString());
  395. if (!ReferenceEquals(go, null))
  396. {
  397. Destroy(go);
  398. }
  399. }
  400. #endregion
  401. }
  402. #region -- Agora Event ---
  403. internal class UserEventHandler : IRtcEngineEventHandler
  404. {
  405. private readonly StartLocalVideoTranscoder _sample;
  406. internal UserEventHandler(StartLocalVideoTranscoder sample)
  407. {
  408. _sample = sample;
  409. }
  410. public override void OnError(int err, string msg)
  411. {
  412. _sample.Log.UpdateLog(string.Format("OnError err: {0}, msg: {1}", err, msg));
  413. }
  414. public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
  415. {
  416. int build = 0;
  417. Debug.Log("Agora: OnJoinChannelSuccess ");
  418. _sample.Log.UpdateLog(string.Format("sdk version: ${0}",
  419. _sample.RtcEngine.GetVersion(ref build)));
  420. _sample.Log.UpdateLog(
  421. string.Format("OnJoinChannelSuccess channelName: {0}, uid: {1}, elapsed: {2}",
  422. connection.channelId, connection.localUid, elapsed));
  423. StartLocalVideoTranscoder.MakeVideoView(0);
  424. }
  425. public override void OnRejoinChannelSuccess(RtcConnection connection, int elapsed)
  426. {
  427. _sample.Log.UpdateLog("OnRejoinChannelSuccess");
  428. }
  429. public override void OnLeaveChannel(RtcConnection connection, RtcStats stats)
  430. {
  431. _sample.Log.UpdateLog("OnLeaveChannel");
  432. StartLocalVideoTranscoder.DestroyVideoView(0);
  433. }
  434. public override void OnClientRoleChanged(RtcConnection connection, CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole)
  435. {
  436. _sample.Log.UpdateLog("OnClientRoleChanged");
  437. }
  438. public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
  439. {
  440. _sample.Log.UpdateLog(string.Format("OnUserJoined uid: ${0} elapsed: ${1}", uid, elapsed));
  441. StartLocalVideoTranscoder.MakeVideoView(uid, _sample.GetChannelName());
  442. if (_sample.RemoteUserUids.Contains(uid) == false)
  443. _sample.RemoteUserUids.Add(uid);
  444. }
  445. public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason)
  446. {
  447. _sample.Log.UpdateLog(string.Format("OnUserOffLine uid: ${0}, reason: ${1}", uid,
  448. (int)reason));
  449. StartLocalVideoTranscoder.DestroyVideoView(uid);
  450. _sample.RemoteUserUids.Remove(uid);
  451. }
  452. }
  453. internal class MpkEventHandler : IMediaPlayerSourceObserver
  454. {
  455. private readonly StartLocalVideoTranscoder _sample;
  456. internal MpkEventHandler(StartLocalVideoTranscoder sample)
  457. {
  458. _sample = sample;
  459. }
  460. public override void OnPlayerSourceStateChanged(MEDIA_PLAYER_STATE state, MEDIA_PLAYER_ERROR ec)
  461. {
  462. _sample.Log.UpdateLog(string.Format(
  463. "OnPlayerSourceStateChanged state: {0}, ec: {1}, playId: {2}", state, ec, _sample.MediaPlayer.GetId()));
  464. Debug.Log("OnPlayerSourceStateChanged");
  465. if (state == MEDIA_PLAYER_STATE.PLAYER_STATE_OPEN_COMPLETED)
  466. {
  467. _sample.MediaPlayer.Play();
  468. }
  469. }
  470. public override void OnPlayerEvent(MEDIA_PLAYER_EVENT @event, Int64 elapsedTime, string message)
  471. {
  472. _sample.Log.UpdateLog(string.Format("OnPlayerEvent state: {0}", @event));
  473. }
  474. }
  475. #endregion
  476. }