AndroidMediaPlayer.cs 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. #define DLL_METHODS
  2. #if UNITY_ANDROID
  3. #if UNITY_5 || UNITY_5_4_OR_NEWER
  4. #if !UNITY_5_0 && !UNITY_5_1
  5. #define AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52
  6. #endif
  7. #if !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 && !UNITY_5_3 && !UNITY_5_4_0 && !UNITY_5_4_1
  8. #define AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542
  9. #endif
  10. #endif
  11. using UnityEngine;
  12. using System;
  13. using System.Runtime.InteropServices;
  14. //-----------------------------------------------------------------------------
  15. // Copyright 2015-2020 RenderHeads Ltd. All rights reserved.
  16. //-----------------------------------------------------------------------------
  17. namespace RenderHeads.Media.AVProVideo
  18. {
  19. /// <summary>
  20. /// Android implementation of BaseMediaPlayer
  21. /// </summary>
  22. // TODO: seal this class
  23. public class AndroidMediaPlayer : BaseMediaPlayer
  24. {
  25. protected static AndroidJavaObject s_ActivityContext = null;
  26. protected static AndroidJavaObject s_Interface = null;
  27. protected static bool s_bInitialised = false;
  28. private static string s_Version = "Plug-in not yet initialised";
  29. #if AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52
  30. private static System.IntPtr _nativeFunction_RenderEvent = System.IntPtr.Zero;
  31. #endif
  32. protected AndroidJavaObject m_Video;
  33. private Texture2D m_Texture;
  34. private int m_TextureHandle;
  35. private bool m_UseFastOesPath;
  36. private float m_DurationMs = 0.0f;
  37. private int m_Width = 0;
  38. private int m_Height = 0;
  39. protected int m_iPlayerIndex = -1;
  40. private Android.VideoApi m_API;
  41. private bool m_HeadRotationEnabled = false;
  42. private bool m_FocusEnabled = false;
  43. private System.IntPtr m_Method_Update;
  44. private System.IntPtr m_Method_SetHeadRotation;
  45. private System.IntPtr m_Method_GetCurrentTimeMs;
  46. private System.IntPtr m_Method_GetSourceVideoFrameRate;
  47. private System.IntPtr m_Method_IsPlaying;
  48. private System.IntPtr m_Method_IsPaused;
  49. private System.IntPtr m_Method_IsFinished;
  50. private System.IntPtr m_Method_IsSeeking;
  51. private System.IntPtr m_Method_IsBuffering;
  52. private System.IntPtr m_Method_IsLooping;
  53. private System.IntPtr m_Method_HasVideo;
  54. private System.IntPtr m_Method_HasAudio;
  55. private System.IntPtr m_Method_SetFocusProps;
  56. private System.IntPtr m_Method_SetFocusEnabled;
  57. private System.IntPtr m_Method_SetFocusRotation;
  58. private jvalue[] m_Value0 = new jvalue[0];
  59. private jvalue[] m_Value1 = new jvalue[1];
  60. private jvalue[] m_Value2 = new jvalue[2];
  61. private jvalue[] m_Value4 = new jvalue[4];
  62. #if AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542
  63. private int _textureQuality = QualitySettings.masterTextureLimit;
  64. #endif
  65. public static bool InitialisePlatform()
  66. {
  67. #if UNITY_5_4_OR_NEWER
  68. if (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Vulkan)
  69. {
  70. Debug.LogError("[AVProVideo] Vulkan graphics API is not supported");
  71. return false;
  72. }
  73. #endif
  74. // Get the activity context
  75. if (s_ActivityContext == null)
  76. {
  77. AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
  78. if (activityClass != null)
  79. {
  80. s_ActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
  81. }
  82. }
  83. if (!s_bInitialised)
  84. {
  85. s_Interface = new AndroidJavaObject("com.RenderHeads.AVProVideo.AVProMobileVideo");
  86. if (s_Interface != null)
  87. {
  88. s_Version = s_Interface.Call<string>("GetPluginVersion");
  89. s_Interface.Call("SetContext", s_ActivityContext);
  90. // Calling this native function cause the .SO library to become loaded
  91. // This is important for Unity < 5.2.0 where GL.IssuePluginEvent works differently
  92. #if AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52
  93. _nativeFunction_RenderEvent = Native.GetRenderEventFunc();
  94. #else
  95. Native.GetRenderEventFunc();
  96. #endif
  97. s_bInitialised = true;
  98. }
  99. }
  100. return s_bInitialised;
  101. }
  102. public static void DeinitPlatform()
  103. {
  104. if (s_bInitialised)
  105. {
  106. if (s_Interface != null)
  107. {
  108. s_Interface.CallStatic("Deinitialise");
  109. s_Interface = null;
  110. }
  111. s_ActivityContext = null;
  112. s_bInitialised = false;
  113. }
  114. }
  115. private static void IssuePluginEvent(Native.AVPPluginEvent type, int param)
  116. {
  117. // Build eventId from the type and param.
  118. int eventId = 0x5d5ac000 | ((int)type << 8);
  119. switch (type)
  120. {
  121. case Native.AVPPluginEvent.PlayerSetup:
  122. case Native.AVPPluginEvent.PlayerUpdate:
  123. case Native.AVPPluginEvent.PlayerDestroy:
  124. case Native.AVPPluginEvent.ExtractFrame:
  125. {
  126. eventId |= param & 0xff;
  127. }
  128. break;
  129. }
  130. #if AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52
  131. GL.IssuePluginEvent(_nativeFunction_RenderEvent, eventId);
  132. #else
  133. GL.IssuePluginEvent(eventId);
  134. #endif
  135. }
  136. private System.IntPtr GetMethod(string methodName, string signature)
  137. {
  138. #if UNITY_5 || UNITY_5_4_OR_NEWER
  139. Debug.Assert(m_Video != null);
  140. #endif
  141. System.IntPtr result = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), methodName, signature, false);
  142. #if UNITY_5 || UNITY_5_4_OR_NEWER
  143. Debug.Assert(result != System.IntPtr.Zero);
  144. #endif
  145. if (result == System.IntPtr.Zero)
  146. {
  147. Debug.LogError("[AVProVideo] Unable to get method " + methodName + " " + signature);
  148. throw new System.Exception("[AVProVideo] Unable to get method " + methodName + " " + signature);
  149. }
  150. return result;
  151. }
  152. public AndroidMediaPlayer(bool useFastOesPath, bool showPosterFrame, Android.VideoApi api, bool enable360Audio, Audio360ChannelMode channelMode, bool preferSoftware)
  153. {
  154. #if UNITY_5 || UNITY_5_4_OR_NEWER
  155. Debug.Assert(s_Interface != null);
  156. Debug.Assert(s_bInitialised);
  157. #endif
  158. m_API = api;
  159. // Create a java-size video class up front
  160. m_Video = s_Interface.Call<AndroidJavaObject>("CreatePlayer", (int)m_API, useFastOesPath, enable360Audio, (int)channelMode, preferSoftware);
  161. if (m_Video != null)
  162. {
  163. m_Method_Update = GetMethod("Update", "()V");
  164. m_Method_SetHeadRotation = GetMethod("SetHeadRotation", "(FFFF)V");
  165. m_Method_SetFocusProps = GetMethod("SetFocusProps", "(FF)V");
  166. m_Method_SetFocusEnabled = GetMethod("SetFocusEnabled", "(Z)V");
  167. m_Method_SetFocusRotation = GetMethod("SetFocusRotation", "(FFFF)V");
  168. m_Method_GetCurrentTimeMs = GetMethod("GetCurrentTimeMs", "()J");
  169. m_Method_GetSourceVideoFrameRate = GetMethod("GetSourceVideoFrameRate", "()F");
  170. m_Method_IsPlaying = GetMethod("IsPlaying", "()Z");
  171. m_Method_IsPaused = GetMethod("IsPaused", "()Z");
  172. m_Method_IsFinished = GetMethod("IsFinished", "()Z");
  173. m_Method_IsSeeking = GetMethod("IsSeeking", "()Z");
  174. m_Method_IsBuffering = GetMethod("IsBuffering", "()Z");
  175. m_Method_IsLooping = GetMethod("IsLooping", "()Z");
  176. m_Method_HasVideo = GetMethod("HasVideo", "()Z");
  177. m_Method_HasAudio = GetMethod("HasAudio", "()Z");
  178. m_iPlayerIndex = m_Video.Call<int>("GetPlayerIndex");
  179. Helper.LogInfo("Creating player " + m_iPlayerIndex);
  180. //Debug.Log( "AVPro: useFastOesPath: " + useFastOesPath );
  181. SetOptions(useFastOesPath, showPosterFrame);
  182. // Initialise renderer, on the render thread
  183. AndroidMediaPlayer.IssuePluginEvent(Native.AVPPluginEvent.PlayerSetup, m_iPlayerIndex);
  184. }
  185. else
  186. {
  187. Debug.LogError("[AVProVideo] Failed to create player instance");
  188. }
  189. }
  190. public void SetOptions(bool useFastOesPath, bool showPosterFrame)
  191. {
  192. m_UseFastOesPath = useFastOesPath;
  193. if (m_Video != null)
  194. {
  195. // Show poster frame is only needed when using the MediaPlayer API
  196. showPosterFrame = (m_API == Android.VideoApi.MediaPlayer) ? showPosterFrame : false;
  197. m_Video.Call("SetPlayerOptions", m_UseFastOesPath, showPosterFrame);
  198. }
  199. }
  200. public override long GetEstimatedTotalBandwidthUsed()
  201. {
  202. long result = -1;
  203. if (s_Interface != null)
  204. {
  205. result = m_Video.Call<long>("GetEstimatedBandwidthUsed");
  206. }
  207. return result;
  208. }
  209. public override string GetVersion()
  210. {
  211. return s_Version;
  212. }
  213. public override bool OpenVideoFromFile(string path, long offset, string httpHeaderJson, uint sourceSamplerate = 0, uint sourceChannels = 0, int forceFileFormat = 0)
  214. {
  215. bool bReturn = false;
  216. if (m_Video != null)
  217. {
  218. #if UNITY_5 || UNITY_5_4_OR_NEWER
  219. Debug.Assert(m_Width == 0 && m_Height == 0 && m_DurationMs == 0.0f);
  220. #endif
  221. bReturn = m_Video.Call<bool>("OpenVideoFromFile", path, offset, httpHeaderJson, forceFileFormat);
  222. if (!bReturn)
  223. {
  224. DisplayLoadFailureSuggestion(path);
  225. }
  226. }
  227. else
  228. {
  229. Debug.LogError("[AVProVideo] m_Video is null!");
  230. }
  231. return bReturn;
  232. }
  233. private void DisplayLoadFailureSuggestion(string path)
  234. {
  235. if (path.ToLower().Contains("http://"))
  236. {
  237. Debug.LogError("Android 8 and above require HTTPS by default, change to HTTPS or enable ClearText in the AndroidManifest.xml");
  238. }
  239. }
  240. public override TimeRange[] GetSeekableTimeRanges()
  241. {
  242. float[] rangeArray = m_Video.Call<float[]>("GetSeekableTimeRange");
  243. TimeRange[] result = new TimeRange[1];
  244. result[0].startTime = rangeArray[0];
  245. result[0].duration = rangeArray[1] - rangeArray[0];
  246. return result;
  247. }
  248. public override void CloseVideo()
  249. {
  250. if (m_Texture != null)
  251. {
  252. Texture2D.Destroy(m_Texture);
  253. m_Texture = null;
  254. }
  255. m_TextureHandle = 0;
  256. m_DurationMs = 0.0f;
  257. m_Width = 0;
  258. m_Height = 0;
  259. if (m_Video != null)
  260. {
  261. m_Video.Call("CloseVideo");
  262. }
  263. base.CloseVideo();
  264. }
  265. public override void SetLooping(bool bLooping)
  266. {
  267. if (m_Video != null)
  268. {
  269. m_Video.Call("SetLooping", bLooping);
  270. }
  271. }
  272. public override bool IsLooping()
  273. {
  274. bool result = false;
  275. if (m_Video != null)
  276. {
  277. if (m_Method_IsLooping != System.IntPtr.Zero)
  278. {
  279. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsLooping, m_Value0);
  280. }
  281. else
  282. {
  283. result = m_Video.Call<bool>("IsLooping");
  284. }
  285. }
  286. return result;
  287. }
  288. public override bool HasVideo()
  289. {
  290. bool result = false;
  291. if (m_Video != null)
  292. {
  293. if (m_Method_HasVideo != System.IntPtr.Zero)
  294. {
  295. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_HasVideo, m_Value0);
  296. }
  297. else
  298. {
  299. result = m_Video.Call<bool>("HasVideo");
  300. }
  301. }
  302. return result;
  303. }
  304. public override bool HasAudio()
  305. {
  306. bool result = false;
  307. if (m_Video != null)
  308. {
  309. if (m_Method_HasAudio != System.IntPtr.Zero)
  310. {
  311. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_HasAudio, m_Value0);
  312. }
  313. else
  314. {
  315. result = m_Video.Call<bool>("HasAudio");
  316. }
  317. }
  318. return result;
  319. }
  320. public override bool HasMetaData()
  321. {
  322. bool result = false;
  323. if (m_DurationMs > 0.0f)
  324. {
  325. result = true;
  326. if (HasVideo())
  327. {
  328. result = (m_Width > 0 && m_Height > 0);
  329. }
  330. }
  331. return result;
  332. }
  333. public override bool CanPlay()
  334. {
  335. bool result = false;
  336. #if DLL_METHODS
  337. result = Native._CanPlay(m_iPlayerIndex);
  338. #else
  339. if (m_Video != null)
  340. {
  341. result = m_Video.Call<bool>("CanPlay");
  342. }
  343. #endif
  344. return result;
  345. }
  346. public override void Play()
  347. {
  348. if (m_Video != null)
  349. {
  350. m_Video.Call("Play");
  351. }
  352. }
  353. public override void Pause()
  354. {
  355. if (m_Video != null)
  356. {
  357. m_Video.Call("Pause");
  358. }
  359. }
  360. public override void Stop()
  361. {
  362. if (m_Video != null)
  363. {
  364. // On Android we never need to actually Stop the playback, pausing is fine
  365. m_Video.Call("Pause");
  366. }
  367. }
  368. public override void Seek(float timeMs)
  369. {
  370. if (m_Video != null)
  371. {
  372. m_Video.Call("Seek", Mathf.FloorToInt(timeMs));
  373. }
  374. }
  375. public override void SeekFast(float timeMs)
  376. {
  377. if (m_Video != null)
  378. {
  379. m_Video.Call("SeekFast", Mathf.FloorToInt(timeMs));
  380. }
  381. }
  382. public override float GetCurrentTimeMs()
  383. {
  384. float result = 0.0f;
  385. if (m_Video != null)
  386. {
  387. if (m_Method_GetCurrentTimeMs != System.IntPtr.Zero)
  388. {
  389. result = AndroidJNI.CallLongMethod(m_Video.GetRawObject(), m_Method_GetCurrentTimeMs, m_Value0);
  390. }
  391. else
  392. {
  393. result = (float)m_Video.Call<long>("GetCurrentTimeMs");
  394. }
  395. }
  396. return result;
  397. }
  398. public override void SetPlaybackRate(float rate)
  399. {
  400. if (m_Video != null)
  401. {
  402. m_Video.Call("SetPlaybackRate", rate);
  403. }
  404. }
  405. public override float GetPlaybackRate()
  406. {
  407. float result = 0.0f;
  408. if (m_Video != null)
  409. {
  410. result = m_Video.Call<float>("GetPlaybackRate");
  411. }
  412. return result;
  413. }
  414. public override void SetAudioHeadRotation(Quaternion q)
  415. {
  416. if (m_Video != null)
  417. {
  418. if (!m_HeadRotationEnabled)
  419. {
  420. m_Video.Call("SetPositionTrackingEnabled", true);
  421. m_HeadRotationEnabled = true;
  422. }
  423. if (m_Method_SetHeadRotation != System.IntPtr.Zero)
  424. {
  425. m_Value4[0].f = q.x;
  426. m_Value4[1].f = q.y;
  427. m_Value4[2].f = q.z;
  428. m_Value4[3].f = q.w;
  429. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetHeadRotation, m_Value4);
  430. }
  431. else
  432. {
  433. m_Video.Call("SetHeadRotation", q.x, q.y, q.z, q.w);
  434. }
  435. }
  436. }
  437. public override void ResetAudioHeadRotation()
  438. {
  439. if (m_Video != null && m_HeadRotationEnabled)
  440. {
  441. m_Video.Call("SetPositionTrackingEnabled", false);
  442. m_HeadRotationEnabled = false;
  443. }
  444. }
  445. public override void SetAudioFocusEnabled(bool enabled)
  446. {
  447. if (m_Video != null && enabled != m_FocusEnabled)
  448. {
  449. if (m_Method_SetFocusEnabled != System.IntPtr.Zero)
  450. {
  451. m_Value1[0].z = enabled;
  452. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusEnabled, m_Value1);
  453. }
  454. else
  455. {
  456. m_Video.Call("SetFocusEnabled", enabled);
  457. }
  458. m_FocusEnabled = enabled;
  459. }
  460. }
  461. public override void SetAudioFocusProperties(float offFocusLevel, float widthDegrees)
  462. {
  463. if (m_Video != null && m_FocusEnabled)
  464. {
  465. if (m_Method_SetFocusProps != System.IntPtr.Zero)
  466. {
  467. m_Value2[0].f = offFocusLevel;
  468. m_Value2[1].f = widthDegrees;
  469. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusProps, m_Value2);
  470. }
  471. else
  472. {
  473. m_Video.Call("SetFocusProps", offFocusLevel, widthDegrees);
  474. }
  475. }
  476. }
  477. public override void SetAudioFocusRotation(Quaternion q)
  478. {
  479. if (m_Video != null && m_FocusEnabled)
  480. {
  481. if (m_Method_SetFocusRotation != System.IntPtr.Zero)
  482. {
  483. m_Value4[0].f = q.x;
  484. m_Value4[1].f = q.y;
  485. m_Value4[2].f = q.z;
  486. m_Value4[3].f = q.w;
  487. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusRotation, m_Value4);
  488. }
  489. else
  490. {
  491. m_Video.Call("SetFocusRotation", q.x, q.y, q.z, q.w);
  492. }
  493. }
  494. }
  495. public override void ResetAudioFocus()
  496. {
  497. if (m_Video != null)
  498. {
  499. if (m_Method_SetFocusProps != System.IntPtr.Zero &&
  500. m_Method_SetFocusEnabled != System.IntPtr.Zero &&
  501. m_Method_SetFocusRotation != System.IntPtr.Zero)
  502. {
  503. m_Value2[0].f = 0f;
  504. m_Value2[1].f = 90f;
  505. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusProps, m_Value2);
  506. m_Value1[0].z = false;
  507. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusEnabled, m_Value1);
  508. m_Value4[0].f = 0f;
  509. m_Value4[1].f = 0f;
  510. m_Value4[2].f = 0f;
  511. m_Value4[3].f = 1f;
  512. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusRotation, m_Value4);
  513. }
  514. else
  515. {
  516. m_Video.Call("SetFocusProps", 0f, 90f);
  517. m_Video.Call("SetFocusEnabled", false);
  518. m_Video.Call("SetFocusRotation", 0f, 0f, 0f, 1f);
  519. }
  520. }
  521. }
  522. public override float GetDurationMs()
  523. {
  524. return m_DurationMs;
  525. }
  526. public override int GetVideoWidth()
  527. {
  528. return m_Width;
  529. }
  530. public override int GetVideoHeight()
  531. {
  532. return m_Height;
  533. }
  534. public override float GetVideoFrameRate()
  535. {
  536. float result = 0.0f;
  537. if (m_Video != null)
  538. {
  539. if (m_Method_GetSourceVideoFrameRate != System.IntPtr.Zero)
  540. {
  541. result = AndroidJNI.CallFloatMethod(m_Video.GetRawObject(), m_Method_GetSourceVideoFrameRate, m_Value0);
  542. }
  543. else
  544. {
  545. result = m_Video.Call<float>("GetSourceVideoFrameRate");
  546. }
  547. }
  548. return result;
  549. }
  550. public override float GetBufferingProgress()
  551. {
  552. float result = 0.0f;
  553. if (m_Video != null)
  554. {
  555. result = m_Video.Call<float>("GetBufferingProgressPercent") * 0.01f;
  556. }
  557. return result;
  558. }
  559. public override float GetVideoDisplayRate()
  560. {
  561. float result = 0.0f;
  562. #if DLL_METHODS
  563. result = Native._GetVideoDisplayRate(m_iPlayerIndex);
  564. #else
  565. if (m_Video != null)
  566. {
  567. result = m_Video.Call<float>("GetDisplayRate");
  568. }
  569. #endif
  570. return result;
  571. }
  572. public override bool IsSeeking()
  573. {
  574. bool result = false;
  575. if (m_Video != null)
  576. {
  577. if (m_Method_IsSeeking != System.IntPtr.Zero)
  578. {
  579. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsSeeking, m_Value0);
  580. }
  581. else
  582. {
  583. result = m_Video.Call<bool>("IsSeeking");
  584. }
  585. }
  586. return result;
  587. }
  588. public override bool IsPlaying()
  589. {
  590. bool result = false;
  591. if (m_Video != null)
  592. {
  593. if (m_Method_IsPlaying != System.IntPtr.Zero)
  594. {
  595. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsPlaying, m_Value0);
  596. }
  597. else
  598. {
  599. result = m_Video.Call<bool>("IsPlaying");
  600. }
  601. }
  602. return result;
  603. }
  604. public override bool IsPaused()
  605. {
  606. bool result = false;
  607. if (m_Video != null)
  608. {
  609. if (m_Method_IsPaused != System.IntPtr.Zero)
  610. {
  611. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsPaused, m_Value0);
  612. }
  613. else
  614. {
  615. result = m_Video.Call<bool>("IsPaused");
  616. }
  617. }
  618. return result;
  619. }
  620. public override bool IsFinished()
  621. {
  622. bool result = false;
  623. if (m_Video != null)
  624. {
  625. if (m_Method_IsFinished != System.IntPtr.Zero)
  626. {
  627. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsFinished, m_Value0);
  628. }
  629. else
  630. {
  631. result = m_Video.Call<bool>("IsFinished");
  632. }
  633. }
  634. return result;
  635. }
  636. public override bool IsBuffering()
  637. {
  638. bool result = false;
  639. if (m_Video != null)
  640. {
  641. if (m_Method_IsBuffering != System.IntPtr.Zero)
  642. {
  643. result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsBuffering, m_Value0);
  644. }
  645. else
  646. {
  647. result = m_Video.Call<bool>("IsBuffering");
  648. }
  649. }
  650. return result;
  651. }
  652. public override Texture GetTexture(int index)
  653. {
  654. Texture result = null;
  655. if (GetTextureFrameCount() > 0)
  656. {
  657. result = m_Texture;
  658. }
  659. return result;
  660. }
  661. public override int GetTextureFrameCount()
  662. {
  663. int result = 0;
  664. if (m_Texture != null)
  665. {
  666. #if DLL_METHODS
  667. result = Native._GetFrameCount(m_iPlayerIndex);
  668. #else
  669. if (m_Video != null)
  670. {
  671. result = m_Video.Call<int>("GetFrameCount");
  672. }
  673. #endif
  674. }
  675. return result;
  676. }
  677. public override bool RequiresVerticalFlip()
  678. {
  679. return false;
  680. }
  681. public override void MuteAudio(bool bMuted)
  682. {
  683. if (m_Video != null)
  684. {
  685. m_Video.Call("MuteAudio", bMuted);
  686. }
  687. }
  688. public override bool IsMuted()
  689. {
  690. bool result = false;
  691. if (m_Video != null)
  692. {
  693. result = m_Video.Call<bool>("IsMuted");
  694. }
  695. return result;
  696. }
  697. public override void SetVolume(float volume)
  698. {
  699. if (m_Video != null)
  700. {
  701. m_Video.Call("SetVolume", volume);
  702. }
  703. }
  704. public override float GetVolume()
  705. {
  706. float result = 0.0f;
  707. if (m_Video != null)
  708. {
  709. result = m_Video.Call<float>("GetVolume");
  710. }
  711. return result;
  712. }
  713. public override void SetBalance(float balance)
  714. {
  715. if (m_Video != null)
  716. {
  717. m_Video.Call("SetAudioPan", balance);
  718. }
  719. }
  720. public override float GetBalance()
  721. {
  722. float result = 0.0f;
  723. if (m_Video != null)
  724. {
  725. result = m_Video.Call<float>("GetAudioPan");
  726. }
  727. return result;
  728. }
  729. public override int GetAudioTrackCount()
  730. {
  731. int result = 0;
  732. if (m_Video != null)
  733. {
  734. result = m_Video.Call<int>("GetNumberAudioTracks");
  735. }
  736. return result;
  737. }
  738. public override int GetCurrentAudioTrack()
  739. {
  740. int result = 0;
  741. if (m_Video != null)
  742. {
  743. result = m_Video.Call<int>("GetCurrentAudioTrackIndex");
  744. }
  745. return result;
  746. }
  747. public override void SetAudioTrack(int index)
  748. {
  749. if (m_Video != null)
  750. {
  751. m_Video.Call("SetAudioTrack", index);
  752. }
  753. }
  754. public override string GetCurrentAudioTrackId()
  755. {
  756. /*string id = "";
  757. if( m_Video != null )
  758. {
  759. id = m_Video.Call<string>("GetCurrentAudioTrackIndex");
  760. }
  761. return id;*/
  762. return GetCurrentAudioTrack().ToString();
  763. }
  764. public override int GetCurrentAudioTrackBitrate()
  765. {
  766. int result = 0;
  767. /*if( m_Video != null )
  768. {
  769. result = m_Video.Call<int>("GetCurrentAudioTrackIndex");
  770. }*/
  771. return result;
  772. }
  773. public override int GetVideoTrackCount()
  774. {
  775. int result = 0;
  776. if (m_Video != null)
  777. {
  778. if (HasVideo())
  779. {
  780. result = 1;
  781. }
  782. //result = m_Video.Call<int>("GetNumberVideoTracks");
  783. }
  784. return result;
  785. }
  786. public override int GetCurrentVideoTrack()
  787. {
  788. int result = 0;
  789. /*if( m_Video != null )
  790. {
  791. result = m_Video.Call<int>("GetCurrentVideoTrackIndex");
  792. }*/
  793. return result;
  794. }
  795. public override void SetVideoTrack(int index)
  796. {
  797. /*if( m_Video != null )
  798. {
  799. m_Video.Call("SetVideoTrack", index);
  800. }*/
  801. }
  802. public override string GetCurrentVideoTrackId()
  803. {
  804. string id = "";
  805. /*if( m_Video != null )
  806. {
  807. id = m_Video.Call<string>("GetCurrentVideoTrackId");
  808. }*/
  809. return id;
  810. }
  811. public override int GetCurrentVideoTrackBitrate()
  812. {
  813. int bitrate = 0;
  814. if (m_Video != null)
  815. {
  816. bitrate = m_Video.Call<int>("GetCurrentVideoTrackBitrate");
  817. }
  818. return bitrate;
  819. }
  820. public override bool WaitForNextFrame(Camera dummyCamera, int previousFrameCount)
  821. {
  822. // Mark as extracting
  823. bool isMultiThreaded = m_Video.Call<bool>("StartExtractFrame");
  824. // In single threaded Android this method won't work, so just return
  825. if (isMultiThreaded)
  826. {
  827. // Queue up render thread event to wait for the new frame
  828. IssuePluginEvent(Native.AVPPluginEvent.ExtractFrame, m_iPlayerIndex);
  829. // Force render thread to run
  830. dummyCamera.Render();
  831. // Wait for the frame to change
  832. m_Video.Call("WaitForExtract");
  833. // Return whether the frame changed
  834. return (previousFrameCount != GetTextureFrameCount());
  835. }
  836. return false;
  837. }
  838. public override long GetTextureTimeStamp()
  839. {
  840. long timeStamp = long.MinValue;
  841. if (m_Video != null)
  842. {
  843. timeStamp = m_Video.Call<long>("GetTextureTimeStamp");
  844. }
  845. return timeStamp;
  846. }
  847. public override void Render()
  848. {
  849. if (m_Video != null)
  850. {
  851. if (m_UseFastOesPath)
  852. {
  853. // This is needed for at least Unity 5.5.0, otherwise it just renders black in OES mode
  854. GL.InvalidateState();
  855. }
  856. AndroidMediaPlayer.IssuePluginEvent(Native.AVPPluginEvent.PlayerUpdate, m_iPlayerIndex);
  857. if (m_UseFastOesPath)
  858. {
  859. GL.InvalidateState();
  860. }
  861. // Check if we can create the texture
  862. // Scan for a change in resolution
  863. int newWidth = -1;
  864. int newHeight = -1;
  865. if (m_Texture != null)
  866. {
  867. #if DLL_METHODS
  868. newWidth = Native._GetWidth(m_iPlayerIndex);
  869. newHeight = Native._GetHeight(m_iPlayerIndex);
  870. #else
  871. newWidth = m_Video.Call<int>("GetWidth");
  872. newHeight = m_Video.Call<int>("GetHeight");
  873. #endif
  874. if (newWidth != m_Width || newHeight != m_Height)
  875. {
  876. m_Texture = null;
  877. m_TextureHandle = 0;
  878. }
  879. }
  880. #if DLL_METHODS
  881. int textureHandle = Native._GetTextureHandle(m_iPlayerIndex);
  882. #else
  883. int textureHandle = m_Video.Call<int>("GetTextureHandle");
  884. #endif
  885. if (textureHandle != 0 && textureHandle != m_TextureHandle)
  886. {
  887. // Already got? (from above)
  888. if (newWidth == -1 || newHeight == -1)
  889. {
  890. #if DLL_METHODS
  891. newWidth = Native._GetWidth(m_iPlayerIndex);
  892. newHeight = Native._GetHeight(m_iPlayerIndex);
  893. #else
  894. newWidth = m_Video.Call<int>("GetWidth");
  895. newHeight = m_Video.Call<int>("GetHeight");
  896. #endif
  897. }
  898. if (Mathf.Max(newWidth, newHeight) > SystemInfo.maxTextureSize)
  899. {
  900. m_Width = newWidth;
  901. m_Height = newHeight;
  902. m_TextureHandle = textureHandle;
  903. Debug.LogError("[AVProVideo] Video dimensions larger than maxTextureSize");
  904. }
  905. else if (newWidth > 0 && newHeight > 0)
  906. {
  907. m_Width = newWidth;
  908. m_Height = newHeight;
  909. m_TextureHandle = textureHandle;
  910. switch (m_API)
  911. {
  912. case Android.VideoApi.MediaPlayer:
  913. _playerDescription = "MediaPlayer";
  914. break;
  915. case Android.VideoApi.ExoPlayer:
  916. _playerDescription = "ExoPlayer";
  917. break;
  918. default:
  919. _playerDescription = "UnknownPlayer";
  920. break;
  921. }
  922. if (m_UseFastOesPath)
  923. {
  924. _playerDescription += " OES";
  925. }
  926. else
  927. {
  928. _playerDescription += " NonOES";
  929. }
  930. Helper.LogInfo("Using playback path: " + _playerDescription + " (" + m_Width + "x" + m_Height + "@" + GetVideoFrameRate().ToString("F2") + ")");
  931. // NOTE: From Unity 5.4.x when using OES textures, an error "OPENGL NATIVE PLUG-IN ERROR: GL_INVALID_OPERATION: Operation illegal in current state" will be logged.
  932. // We assume this is because we're passing in TextureFormat.RGBA32 which isn't the true texture format. This error should be safe to ignore.
  933. m_Texture = Texture2D.CreateExternalTexture(m_Width, m_Height, TextureFormat.RGBA32, false, false, new System.IntPtr(textureHandle));
  934. if (m_Texture != null)
  935. {
  936. ApplyTextureProperties(m_Texture);
  937. }
  938. Helper.LogInfo("Texture ID: " + textureHandle);
  939. }
  940. }
  941. #if AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542
  942. // In Unity 5.4.2 and above the video texture turns black when changing the TextureQuality in the Quality Settings
  943. // The code below gets around this issue. A bug report has been sent to Unity. So far we have tested and replicated the
  944. // "bug" in Windows only, but a user has reported it in Android too.
  945. // Texture.GetNativeTexturePtr() must sync with the rendering thread, so this is a large performance hit!
  946. if (_textureQuality != QualitySettings.masterTextureLimit)
  947. {
  948. if (m_Texture != null && textureHandle > 0 && m_Texture.GetNativeTexturePtr() == System.IntPtr.Zero)
  949. {
  950. //Debug.Log("RECREATING");
  951. m_Texture.UpdateExternalTexture(new System.IntPtr(textureHandle));
  952. }
  953. _textureQuality = QualitySettings.masterTextureLimit;
  954. }
  955. #endif
  956. }
  957. }
  958. protected override void ApplyTextureProperties(Texture texture)
  959. {
  960. // NOTE: According to OES_EGL_image_external: For external textures, the default min filter is GL_LINEAR and the default S and T wrap modes are GL_CLAMP_TO_EDGE
  961. // See https://www.khronos.org/registry/gles/extensions/OES/OES_EGL_image_external_essl3.txt
  962. // But there is a new extension that allows some wrap modes:
  963. // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_EGL_image_external_wrap_modes.txt
  964. // So really we need to detect whether these extensions exist when m_UseFastOesPath is true
  965. //if (!m_UseFastOesPath)
  966. {
  967. base.ApplyTextureProperties(texture);
  968. }
  969. }
  970. public override void OnEnable()
  971. {
  972. base.OnEnable();
  973. #if DLL_METHODS
  974. int textureHandle = Native._GetTextureHandle(m_iPlayerIndex);
  975. #else
  976. int textureHandle = m_Video.Call<int>("GetTextureHandle");
  977. #endif
  978. if (m_Texture != null && textureHandle > 0 && m_Texture.GetNativeTexturePtr() == System.IntPtr.Zero)
  979. {
  980. //Debug.Log("RECREATING");
  981. m_Texture.UpdateExternalTexture(new System.IntPtr(textureHandle));
  982. }
  983. #if AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542
  984. _textureQuality = QualitySettings.masterTextureLimit;
  985. #endif
  986. }
  987. public override double GetCurrentDateTimeSecondsSince1970()
  988. {
  989. double result = 0.0;
  990. if (m_Video != null)
  991. {
  992. result = m_Video.Call<double>("GetCurrentAbsoluteTimestamp");
  993. }
  994. return result;
  995. }
  996. public override void Update()
  997. {
  998. if (m_Video != null)
  999. {
  1000. if (m_Method_Update != System.IntPtr.Zero)
  1001. {
  1002. AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_Update, m_Value0);
  1003. }
  1004. else
  1005. {
  1006. m_Video.Call("Update");
  1007. }
  1008. // _lastError = (ErrorCode)( m_Video.Call<int>("GetLastErrorCode") );
  1009. _lastError = (ErrorCode)(Native._GetLastErrorCode(m_iPlayerIndex));
  1010. }
  1011. UpdateSubtitles();
  1012. if (Mathf.Approximately(m_DurationMs, 0f))
  1013. {
  1014. #if DLL_METHODS
  1015. m_DurationMs = (float)(Native._GetDuration(m_iPlayerIndex));
  1016. #else
  1017. m_DurationMs = (float)(m_Video.Call<long>("GetDurationMs"));
  1018. #endif
  1019. // if( m_DurationMs > 0.0f ) { Helper.LogInfo("Duration: " + m_DurationMs); }
  1020. }
  1021. }
  1022. public override bool PlayerSupportsLinearColorSpace()
  1023. {
  1024. return false;
  1025. }
  1026. public override float[] GetTextureTransform()
  1027. {
  1028. float[] transform = null;
  1029. if (m_Video != null)
  1030. {
  1031. transform = m_Video.Call<float[]>("GetTextureTransform");
  1032. /*if (transform != null)
  1033. {
  1034. Debug.Log("xform: " + transform[0] + " " + transform[1] + " " + transform[2] + " " + transform[3] + " " + transform[4] + " " + transform[5]);
  1035. }*/
  1036. }
  1037. return transform;
  1038. }
  1039. public override void Dispose()
  1040. {
  1041. //Debug.LogError("DISPOSE");
  1042. if (m_Video != null)
  1043. {
  1044. m_Video.Call("SetDeinitialiseFlagged");
  1045. m_Video.Dispose();
  1046. m_Video = null;
  1047. }
  1048. if (s_Interface != null)
  1049. {
  1050. s_Interface.Call("DestroyPlayer", m_iPlayerIndex);
  1051. }
  1052. if (m_Texture != null)
  1053. {
  1054. Texture2D.Destroy(m_Texture);
  1055. m_Texture = null;
  1056. }
  1057. // Deinitialise player (replaces call directly as GL textures are involved)
  1058. AndroidMediaPlayer.IssuePluginEvent(Native.AVPPluginEvent.PlayerDestroy, m_iPlayerIndex);
  1059. }
  1060. private struct Native
  1061. {
  1062. [DllImport("AVProLocal")]
  1063. public static extern IntPtr GetRenderEventFunc();
  1064. [DllImport("AVProLocal")]
  1065. public static extern int _GetWidth(int iPlayerIndex);
  1066. [DllImport("AVProLocal")]
  1067. public static extern int _GetHeight(int iPlayerIndex);
  1068. [DllImport("AVProLocal")]
  1069. public static extern int _GetTextureHandle(int iPlayerIndex);
  1070. [DllImport("AVProLocal")]
  1071. public static extern long _GetDuration(int iPlayerIndex);
  1072. [DllImport("AVProLocal")]
  1073. public static extern int _GetLastErrorCode(int iPlayerIndex);
  1074. [DllImport("AVProLocal")]
  1075. public static extern int _GetFrameCount(int iPlayerIndex);
  1076. [DllImport("AVProLocal")]
  1077. public static extern float _GetVideoDisplayRate(int iPlayerIndex);
  1078. [DllImport("AVProLocal")]
  1079. public static extern bool _CanPlay(int iPlayerIndex);
  1080. public enum AVPPluginEvent
  1081. {
  1082. Nop,
  1083. PlayerSetup,
  1084. PlayerUpdate,
  1085. PlayerDestroy,
  1086. ExtractFrame,
  1087. }
  1088. }
  1089. }
  1090. }
  1091. #endif