StartRtmpStreamWithTranscoding.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using UnityEngine.Serialization;
  4. using Agora.Rtc;
  5. using Agora.Util;
  6. using Logger = Agora.Util.Logger;
  7. namespace Agora_RTC_Plugin.API_Example.Examples.Advanced.StartRtmpStreamWithTranscoding
  8. {
  9. public class StartRtmpStreamWithTranscoding : MonoBehaviour
  10. {
  11. [FormerlySerializedAs("appIdInput")]
  12. [SerializeField]
  13. private AppIdInput _appIdInput;
  14. [Header("_____________Basic Configuration_____________")]
  15. [FormerlySerializedAs("APP_ID")]
  16. [SerializeField]
  17. private string _appID = "";
  18. [FormerlySerializedAs("TOKEN")]
  19. [SerializeField]
  20. private string _token = "";
  21. [FormerlySerializedAs("CHANNEL_NAME")]
  22. [SerializeField]
  23. private string _channelName = "";
  24. public Text LogText;
  25. public InputField rtmpUrl;
  26. internal Logger Log;
  27. internal IRtcEngine RtcEngine = null;
  28. public uint Uid = 0;
  29. // Use this for initialization
  30. private void Start()
  31. {
  32. LoadAssetData();
  33. if (CheckAppId())
  34. {
  35. InitEngine();
  36. SetUpUI();
  37. JoinChannel();
  38. }
  39. }
  40. // Update is called once per frame
  41. private void Update()
  42. {
  43. PermissionHelper.RequestMicrophontPermission();
  44. PermissionHelper.RequestCameraPermission();
  45. }
  46. //Show data in AgoraBasicProfile
  47. [ContextMenu("ShowAgoraBasicProfileData")]
  48. public void LoadAssetData()
  49. {
  50. if (_appIdInput == null) return;
  51. _appID = _appIdInput.appID;
  52. _token = _appIdInput.token;
  53. _channelName = _appIdInput.channelName;
  54. }
  55. private bool CheckAppId()
  56. {
  57. Log = new Logger(LogText);
  58. return Log.DebugAssert(_appID.Length > 10, "Please fill in your appId in API-Example/profile/appIdInput.asset");
  59. }
  60. private void InitEngine()
  61. {
  62. RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
  63. UserEventHandler handler = new UserEventHandler(this);
  64. RtcEngineContext context = new RtcEngineContext(_appID, 0,
  65. CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING,
  66. AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT);
  67. RtcEngine.Initialize(context);
  68. RtcEngine.InitEventHandler(handler);
  69. }
  70. private void JoinChannel()
  71. {
  72. RtcEngine.EnableAudio();
  73. RtcEngine.EnableVideo();
  74. RtcEngine.SetClientRole(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
  75. RtcEngine.JoinChannel(_token, _channelName);
  76. }
  77. private void SetUpUI()
  78. {
  79. var btn = this.transform.Find("StartButton").GetComponent<Button>();
  80. btn.onClick.AddListener(this.OnStartButtonPress);
  81. btn = this.transform.Find("UpdateButton").GetComponent<Button>();
  82. btn.onClick.AddListener(this.OnUpdateButtonPress);
  83. btn = this.transform.Find("StopButton").GetComponent<Button>();
  84. btn.onClick.AddListener(this.OnStopButtonPress);
  85. }
  86. private void OnStartButtonPress()
  87. {
  88. if (this.Uid == 0)
  89. {
  90. this.Log.UpdateLog("your must join channel.");
  91. return;
  92. }
  93. var liveTranscoding = new LiveTranscoding();
  94. liveTranscoding.userCount = 1;
  95. liveTranscoding.transcodingUsers = new TranscodingUser[1];
  96. liveTranscoding.transcodingUsers[0] = new TranscodingUser()
  97. {
  98. uid = this.Uid,
  99. x = 0,
  100. y = 0,
  101. width = 360,
  102. height = 640,
  103. alpha = 1,
  104. zOrder = 1,
  105. audioChannel = 0
  106. };
  107. var url = this.rtmpUrl.text;
  108. if (url == "")
  109. {
  110. this.Log.UpdateLog("your must input your rtmpUrl in Inspector of VideoCanvas");
  111. return;
  112. }
  113. var nRet = RtcEngine.StartRtmpStreamWithTranscoding(url, liveTranscoding);
  114. this.Log.UpdateLog("StartRtmpStreamWithTranscoding:" + nRet);
  115. if (nRet == 0)
  116. {
  117. this.Log.UpdateLog(url);
  118. /*
  119. Verify remote
  120. 1.install ffmpeg(brew install ffmpeg)
  121. 2.ffplay rtmp://play.xxxxx.xxx.xxxx
  122. */
  123. }
  124. }
  125. private void OnUpdateButtonPress()
  126. {
  127. var liveTranscoding = new LiveTranscoding();
  128. liveTranscoding.width = 640;
  129. liveTranscoding.height = 960;
  130. liveTranscoding.userCount = 1;
  131. liveTranscoding.transcodingUsers = new TranscodingUser[1];
  132. liveTranscoding.transcodingUsers[0] = new TranscodingUser()
  133. {
  134. uid = this.Uid,
  135. x = 100,
  136. y = 100,
  137. width = 360,
  138. height = 640,
  139. alpha = 1,
  140. zOrder = 1,
  141. audioChannel = 0
  142. };
  143. var nRet = RtcEngine.UpdateRtmpTranscoding(liveTranscoding);
  144. this.Log.UpdateLog("UpdateRtmpTranscoding:" + nRet);
  145. }
  146. private void OnStopButtonPress()
  147. {
  148. var url = this.rtmpUrl.text;
  149. if (url == "")
  150. {
  151. this.Log.UpdateLog("your must input your rtmpUrl in Inspector of VideoCanvas");
  152. return;
  153. }
  154. var nRet = RtcEngine.StopRtmpStream(url);
  155. this.Log.UpdateLog("StopRtmpStream:" + nRet);
  156. }
  157. private void OnDestroy()
  158. {
  159. Debug.Log("OnDestroy");
  160. if (RtcEngine == null) return;
  161. RtcEngine.InitEventHandler(null);
  162. RtcEngine.LeaveChannel();
  163. RtcEngine.Dispose();
  164. }
  165. internal string GetChannelName()
  166. {
  167. return _channelName;
  168. }
  169. #region -- Video Render UI Logic ---
  170. internal static void MakeVideoView(uint uid, string channelId = "")
  171. {
  172. var go = GameObject.Find(uid.ToString());
  173. if (!ReferenceEquals(go, null))
  174. {
  175. return; // reuse
  176. }
  177. // create a GameObject and assign to this new user
  178. var videoSurface = MakeImageSurface(uid.ToString());
  179. if (ReferenceEquals(videoSurface, null)) return;
  180. // configure videoSurface
  181. if (uid == 0)
  182. {
  183. videoSurface.SetForUser(uid, channelId);
  184. }
  185. else
  186. {
  187. videoSurface.SetForUser(uid, channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
  188. }
  189. videoSurface.OnTextureSizeModify += (int width, int height) =>
  190. {
  191. float scale = (float)height / (float)width;
  192. videoSurface.transform.localScale = new Vector3(-5, 5 * scale, 1);
  193. Debug.Log("OnTextureSizeModify: " + width + " " + height);
  194. };
  195. videoSurface.SetEnable(true);
  196. }
  197. // VIDEO TYPE 1: 3D Object
  198. private static VideoSurface MakePlaneSurface(string goName)
  199. {
  200. var go = GameObject.CreatePrimitive(PrimitiveType.Plane);
  201. if (go == null)
  202. {
  203. return null;
  204. }
  205. go.name = goName;
  206. // set up transform
  207. go.transform.Rotate(-90.0f, 0.0f, 0.0f);
  208. var yPos = Random.Range(3.0f, 5.0f);
  209. var xPos = Random.Range(-2.0f, 2.0f);
  210. go.transform.position = Vector3.zero;
  211. go.transform.localScale = new Vector3(0.25f, 0.5f, 0.5f);
  212. // configure videoSurface
  213. var videoSurface = go.AddComponent<VideoSurface>();
  214. return videoSurface;
  215. }
  216. // Video TYPE 2: RawImage
  217. private static VideoSurface MakeImageSurface(string goName)
  218. {
  219. GameObject go = new GameObject();
  220. if (go == null)
  221. {
  222. return null;
  223. }
  224. go.name = goName;
  225. // to be renderered onto
  226. go.AddComponent<RawImage>();
  227. // make the object draggable
  228. go.AddComponent<UIElementDrag>();
  229. var canvas = GameObject.Find("VideoCanvas");
  230. if (canvas != null)
  231. {
  232. go.transform.parent = canvas.transform;
  233. Debug.Log("add video view");
  234. }
  235. else
  236. {
  237. Debug.Log("Canvas is null video view");
  238. }
  239. // set up transform
  240. go.transform.Rotate(0f, 0.0f, 180.0f);
  241. go.transform.localPosition = Vector3.zero;
  242. go.transform.localScale = new Vector3(2f, 3f, 1f);
  243. // configure videoSurface
  244. var videoSurface = go.AddComponent<VideoSurface>();
  245. return videoSurface;
  246. }
  247. internal static void DestroyVideoView(uint uid)
  248. {
  249. var go = GameObject.Find(uid.ToString());
  250. if (!ReferenceEquals(go, null))
  251. {
  252. Destroy(go);
  253. }
  254. }
  255. #endregion
  256. }
  257. #region -- Agora Event ---
  258. internal class UserEventHandler : IRtcEngineEventHandler
  259. {
  260. private readonly StartRtmpStreamWithTranscoding _sample;
  261. internal UserEventHandler(StartRtmpStreamWithTranscoding sample)
  262. {
  263. _sample = sample;
  264. }
  265. public override void OnError(int err, string msg)
  266. {
  267. _sample.Log.UpdateLog(string.Format("OnError err: {0}, msg: {1}", err, msg));
  268. }
  269. public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
  270. {
  271. Debug.Log("Agora: OnJoinChannelSuccess ");
  272. int build = 0;
  273. _sample.Log.UpdateLog(string.Format("sdk version: ${0}",
  274. _sample.RtcEngine.GetVersion(ref build)));
  275. _sample.Log.UpdateLog(
  276. string.Format("OnJoinChannelSuccess channelName: {0}, uid: {1}, elapsed: {2}",
  277. connection.channelId, connection.localUid, elapsed));
  278. _sample.Uid = connection.localUid;
  279. StartRtmpStreamWithTranscoding.MakeVideoView(0);
  280. }
  281. public override void OnRejoinChannelSuccess(RtcConnection connection, int elapsed)
  282. {
  283. _sample.Log.UpdateLog("OnRejoinChannelSuccess");
  284. }
  285. public override void OnLeaveChannel(RtcConnection connection, RtcStats stats)
  286. {
  287. _sample.Log.UpdateLog("OnLeaveChannel");
  288. StartRtmpStreamWithTranscoding.DestroyVideoView(0);
  289. }
  290. public override void OnClientRoleChanged(RtcConnection connection, CLIENT_ROLE_TYPE oldRole, CLIENT_ROLE_TYPE newRole)
  291. {
  292. _sample.Log.UpdateLog("OnClientRoleChanged");
  293. }
  294. public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
  295. {
  296. _sample.Log.UpdateLog(string.Format("OnUserJoined uid: ${0} elapsed: ${1}", uid, elapsed));
  297. StartRtmpStreamWithTranscoding.MakeVideoView(uid, _sample.GetChannelName());
  298. }
  299. public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason)
  300. {
  301. _sample.Log.UpdateLog(string.Format("OnUserOffLine uid: ${0}, reason: ${1}", uid,
  302. (int)reason));
  303. StartRtmpStreamWithTranscoding.DestroyVideoView(uid);
  304. }
  305. public override void OnRtmpStreamingStateChanged(string url, RTMP_STREAM_PUBLISH_STATE state, RTMP_STREAM_PUBLISH_ERROR_TYPE errCode)
  306. {
  307. _sample.Log.UpdateLog(string.Format("OnRtmpStreamingStateChanged url:{0}, state:{1}, errCode:{2}", url, state, errCode));
  308. }
  309. public override void OnTranscodingUpdated()
  310. {
  311. _sample.Log.UpdateLog(string.Format("OnTranscodingUpdated"));
  312. }
  313. }
  314. #endregion
  315. }