PlatformMediaPlayer.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275
  1. //-----------------------------------------------------------------------------
  2. // Copyright 2015-2024 RenderHeads Ltd. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. #if UNITY_2017_2_OR_NEWER && ( UNITY_EDITOR_OSX || ( !UNITY_EDITOR && ( UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS || UNITY_ANDROID ) ) )
  5. using System;
  6. using System.Runtime.InteropServices;
  7. using UnityEngine;
  8. using UnityEngine.Rendering;
  9. namespace RenderHeads.Media.AVProVideo
  10. {
  11. public sealed partial class PlatformMediaPlayer : BaseMediaPlayer
  12. {
  13. private static DateTime Epoch = new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  14. private static IntPtr PluginRenderEventFunction;
  15. static PlatformMediaPlayer()
  16. {
  17. #if !UNITY_EDITOR && (UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS || UNITY_ANDROID)
  18. Native.AVPPluginBootstrap();
  19. #endif
  20. PluginRenderEventFunction = Native.AVPPluginGetRenderEventFunction();
  21. }
  22. private IntPtr _player;
  23. Native.AVPPlayerSettings _playerSettings;
  24. private MediaPlayer.PlatformOptions _options;
  25. public PlatformMediaPlayer(MediaPlayer.PlatformOptions options)
  26. {
  27. // Keep a handle on the options
  28. _options = options;
  29. // Configure the video output settings
  30. _playerSettings = new Native.AVPPlayerSettings();
  31. if (options is MediaPlayer.OptionsApple)
  32. {
  33. PlatformMediaPlayerInitWithOptions(options as MediaPlayer.OptionsApple);
  34. }
  35. else
  36. if (options is MediaPlayer.OptionsAndroid)
  37. {
  38. MediaPlayer.OptionsAndroid androidOptions = options as MediaPlayer.OptionsAndroid;
  39. PlatformMediaPlayerInitWithOptions(androidOptions);
  40. }
  41. // Make the player
  42. _player = Native.AVPPluginMakePlayer(_playerSettings);
  43. // Create the command buffers
  44. CreateCommandBuffers();
  45. // And execute the setup buffer
  46. Graphics.ExecuteCommandBuffer(_setupCommandBuffer);
  47. // Force an update to get our state in sync with the native
  48. Update();
  49. }
  50. /// <summary>
  51. /// Creates the command buffers
  52. /// </summary>
  53. private void CreateCommandBuffers()
  54. {
  55. #if !UNITY_EDITOR && UNITY_ANDROID
  56. // We pass setup flags alongside the player id on Android
  57. long playerID = _player.ToInt64();
  58. Debug.Log($"playerID: {playerID:X8}");
  59. long flags = 0;
  60. if ((_options as MediaPlayer.OptionsAndroid).textureFormat == MediaPlayer.PlatformOptions.TextureFormat.YCbCr420_OES)
  61. flags |= (long)Native.AVPPlayerRenderEventPlayerSetupFlags.AndroidUseOESFastPath;
  62. if (QualitySettings.activeColorSpace == ColorSpace.Linear)
  63. flags |= (long)Native.AVPPlayerRenderEventPlayerSetupFlags.LinearColourSpace;
  64. Debug.Log($"flags: {flags:X8}");
  65. long param = (playerID & Native.kAVPPlayerRenderEventDataPlayerIDMask) << Native.kAVPPlayerRenderEventDataPlayerIDShift;
  66. param |= (flags & Native.kAVPPlayerRenderEventDataOptionsMask) << Native.kAVPPlayerRenderEventDataOptionsShift;
  67. Debug.Log($"flags: {param:X8}");
  68. IntPtr setupData = new IntPtr(param);
  69. #else
  70. // Other platforms just take the player id directly
  71. IntPtr setupData = _player;
  72. #endif
  73. int eventId = Native.kAVPPlayerRenderEventId | ((int)Native.AVPPluginRenderEvent.PlayerSetup << Native.kAVPPlayerRenderEventTypeShift);
  74. _setupCommandBuffer = new CommandBuffer();
  75. _setupCommandBuffer.name = "AVPPluginRenderEvent.PlayerSetup";
  76. _setupCommandBuffer.IssuePluginEventAndData(PluginRenderEventFunction, eventId, setupData);
  77. // Render resources
  78. eventId = Native.kAVPPlayerRenderEventId | ((int)Native.AVPPluginRenderEvent.PlayerRender << Native.kAVPPlayerRenderEventTypeShift);
  79. _renderCommandBuffer = new CommandBuffer();
  80. _renderCommandBuffer.name = "AVPPluginRenderEvent.PlayerRender";
  81. _renderCommandBuffer.IssuePluginEventAndData(PluginRenderEventFunction, eventId, _player);
  82. // Free resources
  83. eventId = Native.kAVPPlayerRenderEventId | ((int)Native.AVPPluginRenderEvent.PlayerFreeResources << Native.kAVPPlayerRenderEventTypeShift);
  84. _freeResourcesCommandBuffer = new CommandBuffer();
  85. _freeResourcesCommandBuffer.name = "AVPPluginRenderEvent.PlayerFreeResources";
  86. _freeResourcesCommandBuffer.IssuePluginEventAndData(PluginRenderEventFunction, eventId, _player);
  87. }
  88. // Configure the player with Apple options
  89. private void PlatformMediaPlayerInitWithOptions(MediaPlayer.OptionsApple options)
  90. {
  91. switch (options.textureFormat)
  92. {
  93. case MediaPlayer.OptionsApple.TextureFormat.BGRA:
  94. default:
  95. _playerSettings.pixelFormat = Native.AVPPlayerVideoPixelFormat.Bgra;
  96. break;
  97. case MediaPlayer.OptionsApple.TextureFormat.YCbCr420_OES:
  98. _playerSettings.pixelFormat = Native.AVPPlayerVideoPixelFormat.YCbCr420;
  99. break;
  100. }
  101. if (options.flags.GenerateMipmaps())
  102. _playerSettings.videoFlags |= Native.AVPPlayerVideoOutputSettingsFlags.GenerateMipmaps;
  103. if (QualitySettings.activeColorSpace == ColorSpace.Linear)
  104. _playerSettings.videoFlags |= Native.AVPPlayerVideoOutputSettingsFlags.LinearColorSpace;
  105. GetWidthHeightFromResolution(
  106. options.preferredMaximumResolution,
  107. options.customPreferredMaximumResolution,
  108. out _playerSettings.preferredMaximumResolution_width,
  109. out _playerSettings.preferredMaximumResolution_height
  110. );
  111. _playerSettings.maximumPlaybackRate = options.maximumPlaybackRate;
  112. // Configure the audio output settings
  113. _playerSettings.audioOutputMode = (Native.AVPPlayerAudioOutputMode)options.audioMode;
  114. if (options.audioMode == MediaPlayer.OptionsApple.AudioMode.Unity)
  115. {
  116. _playerSettings.sampleRate = AudioSettings.outputSampleRate;
  117. int numBuffers;
  118. AudioSettings.GetDSPBufferSize(out _playerSettings.bufferLength, out numBuffers);
  119. }
  120. // Configure any network settings
  121. _playerSettings.preferredPeakBitRate = options.GetPreferredPeakBitRateInBitsPerSecond();
  122. _playerSettings.preferredForwardBufferDuration = options.preferredForwardBufferDuration;
  123. if (options.flags.PlayWithoutBuffering())
  124. _playerSettings.networkFlags |= Native.AVPPlayerNetworkSettingsFlags.PlayWithoutBuffering;
  125. if (options.flags.UseSinglePlayerItem())
  126. _playerSettings.networkFlags |= Native.AVPPlayerNetworkSettingsFlags.UseSinglePlayerItem;
  127. // Setup any other flags from the options
  128. _flags = _flags.SetAllowExternalPlayback(options.flags.AllowExternalPlayback());
  129. _flags = _flags.SetResumePlayback(options.flags.ResumePlaybackAfterAudioSessionRouteChange());
  130. }
  131. // Configure the player with Android options
  132. private void PlatformMediaPlayerInitWithOptions(MediaPlayer.OptionsAndroid options)
  133. {
  134. _playerSettings.videoApi = options.videoApi == Android.VideoApi.MediaPlayer ? Native.AVPPlayerVideoAPI.MediaPlayer : Native.AVPPlayerVideoAPI.ExoPlayer;
  135. if (options.preferSoftwareDecoder)
  136. _playerSettings.videoFlags |= Native.AVPPlayerVideoOutputSettingsFlags.PreferSoftwareDecoder;
  137. if (options.forceRtpTCP)
  138. _playerSettings.networkFlags |= Native.AVPPlayerNetworkSettingsFlags.ForceRtpTCP;
  139. if (options.forceEnableMediaCodecAsynchronousQueueing)
  140. _playerSettings.videoFlags |= Native.AVPPlayerVideoOutputSettingsFlags.ForceEnableMediaCodecAsynchronousQueueing;
  141. switch (options.textureFormat)
  142. {
  143. case MediaPlayer.OptionsAndroid.TextureFormat.BGRA:
  144. default:
  145. _playerSettings.pixelFormat = Native.AVPPlayerVideoPixelFormat.Bgra;
  146. break;
  147. case MediaPlayer.OptionsAndroid.TextureFormat.YCbCr420_OES:
  148. _playerSettings.pixelFormat = Native.AVPPlayerVideoPixelFormat.YCbCr420;
  149. break;
  150. }
  151. if (QualitySettings.activeColorSpace == ColorSpace.Linear)
  152. _playerSettings.videoFlags |= Native.AVPPlayerVideoOutputSettingsFlags.LinearColorSpace;
  153. GetWidthHeightFromResolution(
  154. options.preferredMaximumResolution,
  155. options.customPreferredMaximumResolution,
  156. out _playerSettings.preferredMaximumResolution_width,
  157. out _playerSettings.preferredMaximumResolution_height
  158. );
  159. // Configure the audio output settings
  160. _playerSettings.audioOutputMode = (Native.AVPPlayerAudioOutputMode)options.audioMode;
  161. switch( options.audioMode )
  162. {
  163. case MediaPlayer.OptionsAndroid.AudioMode.Unity:
  164. {
  165. _playerSettings.sampleRate = AudioSettings.outputSampleRate;
  166. int numBuffers;
  167. AudioSettings.GetDSPBufferSize(out _playerSettings.bufferLength, out numBuffers);
  168. }
  169. break;
  170. case MediaPlayer.OptionsAndroid.AudioMode.SystemDirectWithCapture:
  171. {
  172. // SystemDirectWithCapture does not really exist (or supported) on Android
  173. _playerSettings.audioOutputMode = (Native.AVPPlayerAudioOutputMode)(MediaPlayer.OptionsAndroid.AudioMode.FacebookAudio360);
  174. }
  175. break;
  176. }
  177. _playerSettings.audio360Channels = options.audio360ChannelMode;
  178. _playerSettings.audio360LatencyMS = options.audio360LatencyMS;
  179. // Configure any network settings
  180. _playerSettings.preferredPeakBitRate = options.GetPreferredPeakBitRateInBitsPerSecond();
  181. if (options.startWithHighestBitrate)
  182. _playerSettings.networkFlags |= Native.AVPPlayerNetworkSettingsFlags.ForceStartHighestBitrate;
  183. _playerSettings.minBufferMs = options.minBufferMs;
  184. _playerSettings.maxBufferMs = options.maxBufferMs;
  185. _playerSettings.bufferForPlaybackMs = options.bufferForPlaybackMs;
  186. _playerSettings.bufferForPlaybackAfterRebufferMs = options.bufferForPlaybackAfterRebufferMs;
  187. }
  188. private static void GetWidthHeightFromResolution(MediaPlayer.OptionsApple.Resolution resolution, Vector2Int custom, out float width, out float height)
  189. {
  190. switch (resolution)
  191. {
  192. case MediaPlayer.OptionsApple.Resolution.NoPreference:
  193. default:
  194. width = 0;
  195. height = 0;
  196. break;
  197. case MediaPlayer.OptionsApple.Resolution._480p:
  198. width = 640;
  199. height = 480;
  200. break;
  201. case MediaPlayer.OptionsApple.Resolution._720p:
  202. width = 1280;
  203. height = 720;
  204. break;
  205. case MediaPlayer.OptionsApple.Resolution._1080p:
  206. width = 1920;
  207. height = 1080;
  208. break;
  209. case MediaPlayer.OptionsApple.Resolution._1440p:
  210. width = 2560;
  211. height = 1440;
  212. break;
  213. case MediaPlayer.OptionsApple.Resolution._2160p:
  214. width = 3840;
  215. height = 2160;
  216. break;
  217. case MediaPlayer.OptionsApple.Resolution.Custom:
  218. width = custom.x;
  219. height = custom.y;
  220. break;
  221. }
  222. }
  223. }
  224. // IMediaPlayer
  225. public sealed partial class PlatformMediaPlayer
  226. {
  227. private const int MaxTexturePlanes = 2;
  228. private Native.AVPPlayerState _state = new Native.AVPPlayerState();
  229. private Native.AVPPlayerFlags _flags = Native.AVPPlayerFlags.None;
  230. private Native.AVPPlayerAssetInfo _assetInfo = new Native.AVPPlayerAssetInfo();
  231. private Native.AVPPlayerVideoTrackInfo[] _videoTrackInfo = new Native.AVPPlayerVideoTrackInfo[0];
  232. private Native.AVPPlayerAudioTrackInfo[] _audioTrackInfo = new Native.AVPPlayerAudioTrackInfo[0];
  233. private Native.AVPPlayerTextTrackInfo[] _textTrackInfo = new Native.AVPPlayerTextTrackInfo[0];
  234. private Native.AVPPlayerTexture _playerTexture;
  235. private Native.AVPPlayerText _playerText;
  236. private Texture2D[] _texturePlanes = new Texture2D[MaxTexturePlanes];
  237. private float _volume = 1.0f;
  238. private float _rate = 1.0f;
  239. private CommandBuffer _setupCommandBuffer;
  240. private CommandBuffer _renderCommandBuffer;
  241. private CommandBuffer _freeResourcesCommandBuffer;
  242. public override void OnEnable()
  243. {
  244. }
  245. public override void Update()
  246. {
  247. Native.AVPPlayerUpdate(_player);
  248. Native.AVPPlayerStatus prevStatus = _state.status;
  249. Native.AVPPlayerGetState(_player, ref _state);
  250. Native.AVPPlayerStatus changedStatus = prevStatus ^ _state.status;
  251. // Need to make sure that lastError is set when status is failed so that the Error event is triggered
  252. if (/*BaseMediaPlayer.*/_lastError == ErrorCode.None && changedStatus.HasFailed() && _state.status.HasFailed())
  253. {
  254. /*BaseMediaPlayer.*/_lastError = ErrorCode.LoadFailed;
  255. }
  256. if (_state.status.HasUpdatedAssetInfo())
  257. {
  258. Native.AVPPlayerGetAssetInfo(_player, ref _assetInfo);
  259. _videoTrackInfo = new Native.AVPPlayerVideoTrackInfo[_assetInfo.videoTrackCount];
  260. if (_state.status.HasVideo())
  261. {
  262. for (int i = 0; i < _assetInfo.videoTrackCount; ++i)
  263. {
  264. _videoTrackInfo[i] = new Native.AVPPlayerVideoTrackInfo();
  265. Native.AVPPlayerGetVideoTrackInfo(_player, i, ref _videoTrackInfo[i]);
  266. }
  267. }
  268. _audioTrackInfo = new Native.AVPPlayerAudioTrackInfo[_assetInfo.audioTrackCount];
  269. if (_state.status.HasAudio())
  270. {
  271. for (int i = 0; i < _assetInfo.audioTrackCount; ++i)
  272. {
  273. _audioTrackInfo[i] = new Native.AVPPlayerAudioTrackInfo();
  274. Native.AVPPlayerGetAudioTrackInfo(_player, i, ref _audioTrackInfo[i]);
  275. }
  276. }
  277. _textTrackInfo = new Native.AVPPlayerTextTrackInfo[_assetInfo.textTrackCount];
  278. if (_state.status.HasText())
  279. {
  280. for (int i = 0; i < _assetInfo.textTrackCount; ++i)
  281. {
  282. _textTrackInfo[i] = new Native.AVPPlayerTextTrackInfo();
  283. Native.AVPPlayerGetTextTrackInfo(_player, i, ref _textTrackInfo[i]);
  284. }
  285. }
  286. /*BaseMediaPlayer.*/UpdateTracks();
  287. }
  288. if (_state.status.HasUpdatedBufferedTimeRanges())
  289. {
  290. if (_state.bufferedTimeRangesCount > 0)
  291. {
  292. Native.AVPPlayerTimeRange[] timeRanges = new Native.AVPPlayerTimeRange[_state.bufferedTimeRangesCount];
  293. Native.AVPPlayerGetBufferedTimeRanges(_player, timeRanges, timeRanges.Length);
  294. _bufferedTimes = ConvertNativeTimeRangesToTimeRanges(timeRanges);
  295. }
  296. else
  297. {
  298. _bufferedTimes = new TimeRanges();
  299. }
  300. }
  301. if (_state.status.HasUpdatedSeekableTimeRanges())
  302. {
  303. if (_state.seekableTimeRangesCount > 0)
  304. {
  305. Native.AVPPlayerTimeRange[] timeRanges = new Native.AVPPlayerTimeRange[_state.seekableTimeRangesCount];
  306. Native.AVPPlayerGetSeekableTimeRanges(_player, timeRanges, timeRanges.Length);
  307. _seekableTimes = ConvertNativeTimeRangesToTimeRanges(timeRanges);
  308. }
  309. else
  310. {
  311. _seekableTimes = new TimeRanges();
  312. }
  313. }
  314. if (_state.status.HasUpdatedTexture())
  315. {
  316. Native.AVPPlayerGetTexture(_player, ref _playerTexture);
  317. for (int i = 0; i < _playerTexture.planeCount; ++i)
  318. {
  319. TextureFormat textureFormat = TextureFormat.BGRA32;
  320. switch (_playerTexture.planes[i].textureFormat)
  321. {
  322. case Native.AVPPlayerTextureFormat.R8:
  323. textureFormat = TextureFormat.R8;
  324. break;
  325. case Native.AVPPlayerTextureFormat.R16:
  326. textureFormat = TextureFormat.R16;
  327. break;
  328. case Native.AVPPlayerTextureFormat.RG8:
  329. textureFormat = TextureFormat.RG16;
  330. break;
  331. case Native.AVPPlayerTextureFormat.RG16:
  332. textureFormat = TextureFormat.RG32;
  333. break;
  334. case Native.AVPPlayerTextureFormat.BC1:
  335. textureFormat = TextureFormat.DXT1;
  336. break;
  337. case Native.AVPPlayerTextureFormat.BC3:
  338. textureFormat = TextureFormat.DXT5;
  339. break;
  340. case Native.AVPPlayerTextureFormat.BC4:
  341. textureFormat = TextureFormat.BC4;
  342. break;
  343. case Native.AVPPlayerTextureFormat.BC5:
  344. textureFormat = TextureFormat.BC5;
  345. break;
  346. case Native.AVPPlayerTextureFormat.BC7:
  347. textureFormat = TextureFormat.BC7;
  348. break;
  349. case Native.AVPPlayerTextureFormat.RGBA16Float:
  350. textureFormat = TextureFormat.RGBAHalf;
  351. break;
  352. case Native.AVPPlayerTextureFormat.BGRA8:
  353. default:
  354. break;
  355. }
  356. if (_texturePlanes[i] == null ||
  357. _texturePlanes[i].width != _playerTexture.planes[i].width ||
  358. _texturePlanes[i].height != _playerTexture.planes[i].height ||
  359. _texturePlanes[i].format != textureFormat)
  360. {
  361. #if !UNITY_ANDROID
  362. // Ensure any existing texture is released.
  363. if (_texturePlanes[i] != null)
  364. {
  365. _texturePlanes[i].UpdateExternalTexture(IntPtr.Zero);
  366. _texturePlanes[i] = null;
  367. }
  368. #endif
  369. _texturePlanes[i] = Texture2D.CreateExternalTexture(
  370. _playerTexture.planes[i].width,
  371. _playerTexture.planes[i].height,
  372. textureFormat,
  373. _playerTexture.flags.IsMipmapped(),
  374. _playerTexture.flags.IsLinear(),
  375. _playerTexture.planes[i].plane
  376. );
  377. base.ApplyTextureProperties(_texturePlanes[i]);
  378. }
  379. else
  380. {
  381. _texturePlanes[i].UpdateExternalTexture(_playerTexture.planes[i].plane);
  382. }
  383. }
  384. }
  385. if (_state.status.HasUpdatedTextureTransform())
  386. {
  387. // Directly grab the video track info as path of least resistance
  388. if (_state.selectedVideoTrack >= 0)
  389. {
  390. Native.AVPPlayerGetVideoTrackInfo(_player, _state.selectedVideoTrack, ref _videoTrackInfo[_state.selectedVideoTrack]);
  391. }
  392. }
  393. if (_state.status.HasUpdatedText())
  394. {
  395. Native.AVPPlayerGetText(_player, ref _playerText);
  396. /*BaseMediaPlayer.*/UpdateTextCue();
  397. }
  398. if (_flags.IsDirty())
  399. {
  400. _flags = _flags.SetDirty(false);
  401. Native.AVPPlayerSetFlags(_player, (int)_flags);
  402. }
  403. if (_options.HasChanged())
  404. {
  405. if (_options is MediaPlayer.OptionsApple)
  406. {
  407. UpdatePlayerSettingsFromOptions(_options as MediaPlayer.OptionsApple);
  408. }
  409. else
  410. if (_options is MediaPlayer.OptionsAndroid)
  411. {
  412. UpdatePlayerSettingsFromOptions(_options as MediaPlayer.OptionsAndroid);
  413. }
  414. Native.AVPPlayerSetPlayerSettings(_player, _playerSettings);
  415. _options.ClearChanges();
  416. }
  417. /*BaseMediaPlayer.*/UpdateDisplayFrameRate();
  418. /*BaseMediaPlayer.*/UpdateSubtitles();
  419. }
  420. private void UpdatePlayerSettingsFromOptions(MediaPlayer.OptionsApple options)
  421. {
  422. if (options.HasChanged(MediaPlayer.OptionsApple.ChangeFlags.PreferredPeakBitRate))
  423. {
  424. _playerSettings.preferredPeakBitRate = options.GetPreferredPeakBitRateInBitsPerSecond();
  425. }
  426. if (options.HasChanged(MediaPlayer.OptionsApple.ChangeFlags.PreferredForwardBufferDuration))
  427. {
  428. _playerSettings.preferredForwardBufferDuration = options.preferredForwardBufferDuration;
  429. }
  430. if (options.HasChanged(MediaPlayer.OptionsApple.ChangeFlags.PlayWithoutBuffering))
  431. {
  432. bool enabled = (options.flags & MediaPlayer.OptionsApple.Flags.PlayWithoutBuffering) == MediaPlayer.OptionsApple.Flags.PlayWithoutBuffering;
  433. _playerSettings.networkFlags = enabled ? _playerSettings.networkFlags | Native.AVPPlayerNetworkSettingsFlags.PlayWithoutBuffering
  434. : _playerSettings.networkFlags & ~Native.AVPPlayerNetworkSettingsFlags.PlayWithoutBuffering;
  435. }
  436. if (options.HasChanged(MediaPlayer.OptionsApple.ChangeFlags.PreferredMaximumResolution))
  437. {
  438. GetWidthHeightFromResolution(
  439. options.preferredMaximumResolution,
  440. options.customPreferredMaximumResolution,
  441. out _playerSettings.preferredMaximumResolution_width,
  442. out _playerSettings.preferredMaximumResolution_height);
  443. }
  444. if (options.HasChanged(MediaPlayer.OptionsApple.ChangeFlags.AudioMode))
  445. {
  446. if (_state.status.IsReadyToPlay() == false)
  447. {
  448. _playerSettings.audioOutputMode = (Native.AVPPlayerAudioOutputMode)options.audioMode;
  449. if (options.audioMode == MediaPlayer.OptionsApple.AudioMode.Unity)
  450. {
  451. _playerSettings.sampleRate = AudioSettings.outputSampleRate;
  452. int numBuffers;
  453. AudioSettings.GetDSPBufferSize(out _playerSettings.bufferLength, out numBuffers);
  454. }
  455. }
  456. else
  457. {
  458. Debug.LogWarning("[AVProVideo] Unable to change audio mode after media has been loaded and is ready to play");
  459. options.audioMode = options.previousAudioMode;
  460. }
  461. }
  462. }
  463. private void UpdatePlayerSettingsFromOptions(MediaPlayer.OptionsAndroid options)
  464. {
  465. if (options.HasChanged(MediaPlayer.OptionsAndroid.ChangeFlags.PreferredPeakBitRate))
  466. {
  467. _playerSettings.preferredPeakBitRate = options.GetPreferredPeakBitRateInBitsPerSecond();
  468. }
  469. if (options.HasChanged(MediaPlayer.OptionsAndroid.ChangeFlags.PreferredMaximumResolution))
  470. {
  471. GetWidthHeightFromResolution(
  472. options.preferredMaximumResolution,
  473. options.customPreferredMaximumResolution,
  474. out _playerSettings.preferredMaximumResolution_width,
  475. out _playerSettings.preferredMaximumResolution_height);
  476. }
  477. if (options.HasChanged(MediaPlayer.OptionsAndroid.ChangeFlags.AudioMode))
  478. {
  479. if (_state.status.IsReadyToPlay() == false)
  480. {
  481. _playerSettings.audioOutputMode = (Native.AVPPlayerAudioOutputMode)options.audioMode;
  482. if (options.audioMode == MediaPlayer.OptionsAndroid.AudioMode.Unity)
  483. {
  484. _playerSettings.sampleRate = AudioSettings.outputSampleRate;
  485. int numBuffers;
  486. AudioSettings.GetDSPBufferSize(out _playerSettings.bufferLength, out numBuffers);
  487. }
  488. }
  489. else
  490. {
  491. Debug.LogWarning("[AVProVideo] Unable to change audio mode after media has been loaded and is ready to play");
  492. options.audioMode = options.previousAudioMode;
  493. }
  494. }
  495. }
  496. public override void Render()
  497. {
  498. Graphics.ExecuteCommandBuffer(_renderCommandBuffer);
  499. }
  500. public override IntPtr GetNativePlayerHandle()
  501. {
  502. return _player;
  503. }
  504. private static TimeRanges ConvertNativeTimeRangesToTimeRanges(Native.AVPPlayerTimeRange[] ranges)
  505. {
  506. TimeRange[] targetRanges = new TimeRange[ranges.Length];
  507. for (int i = 0; i < ranges.Length; i++)
  508. {
  509. targetRanges[i].startTime = ranges[i].start;
  510. targetRanges[i].duration = ranges[i].duration;
  511. }
  512. return new TimeRanges(targetRanges);
  513. }
  514. }
  515. // IMediaControl
  516. public sealed partial class PlatformMediaPlayer
  517. {
  518. public override bool OpenMedia(string path, long offset, string headers, MediaHints mediaHints, int forceFileFormat, bool startWithHighestBitrate)
  519. {
  520. _mediaHints = mediaHints;
  521. Native.AVPPlayerOpenOptions options;
  522. options.fileOffset = offset;
  523. options.forceFileFormat = (Native.AVPPlayerOpenOptionsForceFileFormat)forceFileFormat;
  524. options.flags = 0;
  525. bool b = Native.AVPPlayerOpenURL(_player, path, headers, options);
  526. if (b)
  527. {
  528. Update();
  529. }
  530. return b;
  531. }
  532. public override bool OpenMediaFromBuffer(byte[] buffer)
  533. {
  534. // Unsupported
  535. return false;
  536. }
  537. public override bool StartOpenMediaFromBuffer(ulong length)
  538. {
  539. // Unsupported
  540. return false;
  541. }
  542. public override bool AddChunkToMediaBuffer(byte[] chunk, ulong offset, ulong length)
  543. {
  544. // Unsupported
  545. return false;
  546. }
  547. public override bool EndOpenMediaFromBuffer()
  548. {
  549. // Unsupported
  550. return false;
  551. }
  552. public override void CloseMedia()
  553. {
  554. Native.AVPPlayerClose(_player);
  555. Update();
  556. #if !UNITY_ANDROID
  557. // Clean up the textures
  558. for (int i = 0; i < MaxTexturePlanes; ++i)
  559. {
  560. if (_texturePlanes[i] != null)
  561. {
  562. _texturePlanes[i].UpdateExternalTexture(IntPtr.Zero);
  563. _texturePlanes[i] = null;
  564. }
  565. }
  566. _playerTexture.frameCount = 0;
  567. #endif
  568. }
  569. public override void SetLooping(bool b)
  570. {
  571. _flags = _flags.SetLooping(b);
  572. }
  573. public override bool IsLooping()
  574. {
  575. return _flags.IsLooping();
  576. }
  577. public override bool HasMetaData()
  578. {
  579. return _state.status.HasMetadata();
  580. }
  581. public override bool CanPlay()
  582. {
  583. return _state.status.IsReadyToPlay();
  584. }
  585. public override bool IsPlaying()
  586. {
  587. return _state.status.IsPlaying();
  588. }
  589. public override bool IsSeeking()
  590. {
  591. return _state.status.IsSeeking() || _state.status.HasFinishedSeeking();
  592. }
  593. public override bool IsPaused()
  594. {
  595. return _state.status.IsPaused();
  596. }
  597. public override bool IsFinished()
  598. {
  599. return _state.status.IsFinished();
  600. }
  601. public override bool IsBuffering()
  602. {
  603. return _state.status.IsBuffering();
  604. }
  605. public override void Play()
  606. {
  607. Native.AVPPlayerSetRate(_player, _rate);
  608. Update();
  609. }
  610. public override void Pause()
  611. {
  612. Native.AVPPlayerSetRate(_player, 0.0f);
  613. Update();
  614. }
  615. public override void Stop()
  616. {
  617. Pause();
  618. }
  619. public override void Rewind()
  620. {
  621. SeekWithTolerance(0.0, 0.0, 0.0);
  622. }
  623. public override void Seek(double toTime)
  624. {
  625. SeekWithTolerance(toTime, 0.0, 0.0);
  626. }
  627. public override void SeekFast(double toTime)
  628. {
  629. SeekWithTolerance(toTime, double.PositiveInfinity, double.PositiveInfinity);
  630. }
  631. public override void SeekWithTolerance(double toTime, double toleranceBefore, double toleranceAfter)
  632. {
  633. Native.AVPPlayerSeek(_player, toTime, toleranceBefore, toleranceAfter);
  634. Update();
  635. }
  636. public override double GetCurrentTime()
  637. {
  638. return _state.currentTime;
  639. }
  640. public override DateTime GetProgramDateTime()
  641. {
  642. return Epoch.AddSeconds(_state.currentDate);
  643. }
  644. public override float GetPlaybackRate()
  645. {
  646. return _rate;
  647. }
  648. public override void SetPlaybackRate(float rate)
  649. {
  650. if (rate != _rate)
  651. {
  652. _rate = rate;
  653. Native.AVPPlayerSetRate(_player, rate);
  654. Update();
  655. }
  656. }
  657. public override void MuteAudio(bool mute)
  658. {
  659. _flags = _flags.SetMuted(mute);
  660. }
  661. public override bool IsMuted()
  662. {
  663. return _flags.IsMuted();
  664. }
  665. public override void SetVolume(float volume)
  666. {
  667. if (volume != _volume)
  668. {
  669. _volume = volume;
  670. Native.AVPPlayerSetVolume(_player, volume);
  671. }
  672. }
  673. public override void SetBalance(float balance)
  674. {
  675. // Unsupported
  676. }
  677. public override float GetVolume()
  678. {
  679. return _volume;
  680. }
  681. public override float GetBalance()
  682. {
  683. // Unsupported
  684. return 0.0f;
  685. }
  686. public override long GetLastExtendedErrorCode()
  687. {
  688. return 0;
  689. }
  690. public override int GetAudioChannelCount()
  691. {
  692. int channelCount = -1;
  693. if (_state.selectedAudioTrack > -1 && _state.selectedAudioTrack < _audioTrackInfo.Length)
  694. {
  695. channelCount = (int)_audioTrackInfo[_state.selectedAudioTrack].channelCount;
  696. #if !UNITY_EDITOR && UNITY_IOS
  697. MediaPlayer.OptionsApple options = _options as MediaPlayer.OptionsApple;
  698. if (options.audioMode == MediaPlayer.OptionsApple.AudioMode.Unity)
  699. {
  700. // iOS audio capture will convert down to two channel stereo
  701. channelCount = Math.Min(channelCount, 2);
  702. }
  703. #endif
  704. }
  705. return channelCount;
  706. }
  707. public override AudioChannelMaskFlags GetAudioChannelMask()
  708. {
  709. if (_state.selectedAudioTrack != -1 && _state.selectedAudioTrack < _audioTrackInfo.Length)
  710. {
  711. return _audioTrackInfo[_state.selectedAudioTrack].channelBitmap;
  712. }
  713. return AudioChannelMaskFlags.Unspecified;
  714. }
  715. public override void AudioConfigurationChanged(bool deviceChanged)
  716. {
  717. if (_playerSettings.audioOutputMode == Native.AVPPlayerAudioOutputMode.SystemDirect)
  718. return;
  719. _playerSettings.sampleRate = AudioSettings.outputSampleRate;
  720. int numBuffers;
  721. AudioSettings.GetDSPBufferSize(out _playerSettings.bufferLength, out numBuffers);
  722. Native.AVPPlayerSetPlayerSettings(_player, _playerSettings);
  723. }
  724. public override int GrabAudio(float[] buffer, int sampleCount, int channelCount)
  725. {
  726. return Native.AVPPlayerGetAudio(_player, buffer, buffer.Length);
  727. }
  728. public override int GetAudioBufferedSampleCount()
  729. {
  730. return _state.audioCaptureBufferedSamplesCount;
  731. }
  732. public override void SetAudioHeadRotation(Quaternion q)
  733. {
  734. float[] aRotation = new float[] { q.x, q.y, q.z, q.w };
  735. Native.AVPPlayerSetAudioHeadRotation(_player, aRotation);
  736. }
  737. public override void ResetAudioHeadRotation()
  738. {
  739. Native.AVPPlayerSetPositionTrackingEnabled(_player, false);
  740. }
  741. public override void SetAudioChannelMode(Audio360ChannelMode channelMode)
  742. {
  743. // Unsupported
  744. }
  745. public override void SetAudioFocusEnabled(bool enabled)
  746. {
  747. Native.AVPPlayerSetAudioFocusEnabled(_player, enabled);
  748. }
  749. public override void SetAudioFocusProperties(float offFocusLevel, float widthDegrees)
  750. {
  751. Native.AVPPlayerSetAudioFocusProperties(_player, offFocusLevel, widthDegrees);
  752. }
  753. public override void SetAudioFocusRotation(Quaternion q)
  754. {
  755. float[] aRotation = new float[] { q.x, q.y, q.z, q.w };
  756. Native.AVPPlayerSetAudioFocusRotation(_player, aRotation);
  757. }
  758. public override void ResetAudioFocus()
  759. {
  760. Native.AVPPlayerResetAudioFocus(_player);
  761. }
  762. public override bool WaitForNextFrame(Camera camera, int previousFrameCount)
  763. {
  764. return false;
  765. }
  766. public override void SetKeyServerAuthToken(string token)
  767. {
  768. Native.AVPPlayerSetKeyServerAuthToken(_player, token);
  769. }
  770. public override void SetOverrideDecryptionKey(byte[] key)
  771. {
  772. int length = key != null ? key.Length : 0;
  773. Native.AVPPlayerSetDecryptionKey(_player, key, length);
  774. }
  775. public override bool IsExternalPlaybackActive()
  776. {
  777. return _state.status.IsExternalPlaybackActive();
  778. }
  779. public override void SetAllowsExternalPlayback(bool enable)
  780. {
  781. _flags.SetAllowExternalPlayback(enable);
  782. }
  783. public override void SetExternalPlaybackVideoGravity(ExternalPlaybackVideoGravity gravity_)
  784. {
  785. Native.AVPPlayerExternalPlaybackVideoGravity gravity;
  786. switch (gravity_)
  787. {
  788. case ExternalPlaybackVideoGravity.Resize:
  789. default:
  790. gravity = Native.AVPPlayerExternalPlaybackVideoGravity.Resize;
  791. break;
  792. case ExternalPlaybackVideoGravity.ResizeAspect:
  793. gravity = Native.AVPPlayerExternalPlaybackVideoGravity.ResizeAspect;
  794. break;
  795. case ExternalPlaybackVideoGravity.ResizeAspectFill:
  796. gravity = Native.AVPPlayerExternalPlaybackVideoGravity.ResizeAspectFill;
  797. break;
  798. }
  799. Native.AVPPlayerSetExternalPlaybackVideoGravity(_player, gravity);
  800. }
  801. }
  802. // IMediaInfo
  803. public sealed partial class PlatformMediaPlayer
  804. {
  805. public override double GetDuration()
  806. {
  807. return _assetInfo.duration;
  808. }
  809. public override int GetVideoWidth()
  810. {
  811. int width = 0;
  812. if (_videoTrackInfo.Length > 0 && _state.selectedVideoTrack >= 0)
  813. {
  814. width = (int)_videoTrackInfo[_state.selectedVideoTrack].dimensions.width;
  815. }
  816. return width;
  817. }
  818. public override int GetVideoHeight()
  819. {
  820. int height = 0;
  821. if (_videoTrackInfo.Length > 0 && _state.selectedVideoTrack >= 0)
  822. {
  823. height = (int)_videoTrackInfo[_state.selectedVideoTrack].dimensions.height;
  824. }
  825. return height;
  826. }
  827. public override float GetVideoFrameRate()
  828. {
  829. float framerate = 0.0f;
  830. if (_videoTrackInfo.Length > 0 && _state.selectedVideoTrack >= 0)
  831. {
  832. framerate = _videoTrackInfo[_state.selectedVideoTrack].frameRate;
  833. }
  834. return framerate;
  835. }
  836. public override bool HasVideo()
  837. {
  838. return _state.status.HasVideo();
  839. }
  840. public override bool HasAudio()
  841. {
  842. return _state.status.HasAudio();
  843. }
  844. public override bool PlayerSupportsLinearColorSpace()
  845. {
  846. return _playerTexture.flags.IsLinear();
  847. }
  848. public override bool IsPlaybackStalled()
  849. {
  850. return _state.status.IsStalled();
  851. }
  852. public override float[] GetAffineTransform()
  853. {
  854. if (_videoTrackInfo.Length > 0 && _state.selectedVideoTrack >= 0)
  855. {
  856. Native.AVPPlayerVideoTrackInfo videoTrackInfo = _videoTrackInfo[_state.selectedVideoTrack];
  857. Native.AVPAffineTransform transform = videoTrackInfo.transform;
  858. return new float[] { transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty };
  859. }
  860. else
  861. {
  862. return new float[] { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
  863. }
  864. }
  865. public override long GetEstimatedTotalBandwidthUsed()
  866. {
  867. return 0;
  868. }
  869. public override bool IsExternalPlaybackSupported()
  870. {
  871. return _assetInfo.flags.IsCompatibleWithAirPlay();
  872. }
  873. }
  874. // IMediaProducer
  875. public sealed partial class PlatformMediaPlayer
  876. {
  877. public override int GetTextureCount()
  878. {
  879. return _playerTexture.planeCount;
  880. }
  881. public override Texture GetTexture(int index)
  882. {
  883. return _texturePlanes[index];
  884. }
  885. public override int GetTextureFrameCount()
  886. {
  887. return _playerTexture.frameCount;
  888. }
  889. public override bool SupportsTextureFrameCount()
  890. {
  891. return true;
  892. }
  893. public override long GetTextureTimeStamp()
  894. {
  895. return _playerTexture.itemTime;
  896. }
  897. public override bool RequiresVerticalFlip()
  898. {
  899. return _playerTexture.flags.IsFlipped();
  900. }
  901. public override TransparencyMode GetTextureTransparency()
  902. {
  903. if (_videoTrackInfo.Length > 0 && _state.selectedVideoTrack >= 0)
  904. {
  905. Native.AVPPlayerVideoTrackInfo info = _videoTrackInfo[_state.selectedVideoTrack];
  906. if ((info.videoTrackFlags & Native.AVPPlayerVideoTrackFlags.HasAlpha) == Native.AVPPlayerVideoTrackFlags.HasAlpha)
  907. {
  908. return TransparencyMode.Transparent;
  909. }
  910. }
  911. return base.GetTextureTransparency();
  912. }
  913. public override Matrix4x4 GetYpCbCrTransform()
  914. {
  915. if (_videoTrackInfo.Length > 0 && _state.selectedVideoTrack >= 0)
  916. return _videoTrackInfo[_state.selectedVideoTrack].yCbCrTransform;
  917. else
  918. return Matrix4x4.identity;
  919. }
  920. internal override StereoPacking InternalGetTextureStereoPacking()
  921. {
  922. if (_videoTrackInfo.Length > 0 && _state.selectedVideoTrack >= 0)
  923. {
  924. switch (_videoTrackInfo[_state.selectedVideoTrack].stereoMode)
  925. {
  926. case Native.AVPPlayerVideoTrackStereoMode.Unknown:
  927. return StereoPacking.Unknown;
  928. case Native.AVPPlayerVideoTrackStereoMode.Monoscopic:
  929. return StereoPacking.None;
  930. case Native.AVPPlayerVideoTrackStereoMode.StereoscopicLeftRight:
  931. return StereoPacking.LeftRight;
  932. case Native.AVPPlayerVideoTrackStereoMode.StereoscopicTopBottom:
  933. return StereoPacking.TopBottom;
  934. case Native.AVPPlayerVideoTrackStereoMode.StereoscopicRightLeft:
  935. return StereoPacking.Unknown;
  936. case Native.AVPPlayerVideoTrackStereoMode.StereoscopicCustom:
  937. return StereoPacking.CustomUV;
  938. }
  939. }
  940. return StereoPacking.Unknown;
  941. }
  942. }
  943. // IDispose
  944. public sealed partial class PlatformMediaPlayer
  945. {
  946. public override void Dispose()
  947. {
  948. Graphics.ExecuteCommandBuffer(_freeResourcesCommandBuffer);
  949. Native.AVPPlayerRelease(_player);
  950. _player = IntPtr.Zero;
  951. }
  952. }
  953. // Version
  954. public sealed partial class PlatformMediaPlayer
  955. {
  956. public override string GetVersion()
  957. {
  958. return Native.GetPluginVersion();
  959. }
  960. public override string GetExpectedVersion()
  961. {
  962. #if !UNITY_EDITOR && UNITY_ANDROID
  963. return Helper.ExpectedPluginVersion.Android;
  964. #else
  965. return Helper.ExpectedPluginVersion.Apple;
  966. #endif
  967. }
  968. }
  969. // Media selection
  970. public sealed partial class PlatformMediaPlayer
  971. {
  972. internal override bool InternalIsChangedTracks(TrackType trackType)
  973. {
  974. return _state.status.HasUpdatedAssetInfo();
  975. }
  976. internal override int InternalGetTrackCount(TrackType trackType)
  977. {
  978. switch (trackType)
  979. {
  980. case TrackType.Video:
  981. return _videoTrackInfo.Length;
  982. case TrackType.Audio:
  983. return _audioTrackInfo.Length;
  984. case TrackType.Text:
  985. return _textTrackInfo.Length;
  986. default:
  987. return 0;
  988. }
  989. }
  990. internal override bool InternalSetActiveTrack(TrackType trackType, int index)
  991. {
  992. switch (trackType)
  993. {
  994. case TrackType.Video:
  995. return Native.AVPPlayerSetTrack(_player, Native.AVPPlayerTrackType.Video, index);
  996. case TrackType.Audio:
  997. return Native.AVPPlayerSetTrack(_player, Native.AVPPlayerTrackType.Audio, index);
  998. case TrackType.Text:
  999. return Native.AVPPlayerSetTrack(_player, Native.AVPPlayerTrackType.Text, index);
  1000. default:
  1001. return false;
  1002. }
  1003. }
  1004. internal override TrackBase InternalGetTrackInfo(TrackType type, int index, ref bool isActiveTrack)
  1005. {
  1006. TrackBase track = null;
  1007. switch (type)
  1008. {
  1009. case TrackType.Video:
  1010. if (index >= 0 && index < _videoTrackInfo.Length)
  1011. {
  1012. Native.AVPPlayerVideoTrackInfo trackInfo = _videoTrackInfo[index];
  1013. track = new VideoTrack(index, trackInfo.name, trackInfo.language, trackInfo.flags.IsDefault());
  1014. isActiveTrack = _state.selectedVideoTrack == index;
  1015. }
  1016. break;
  1017. case TrackType.Audio:
  1018. if (index >= 0 && index < _audioTrackInfo.Length)
  1019. {
  1020. Native.AVPPlayerAudioTrackInfo trackInfo = _audioTrackInfo[index];
  1021. track = new AudioTrack(index, trackInfo.name, trackInfo.language, trackInfo.flags.IsDefault());
  1022. isActiveTrack = _state.selectedAudioTrack == index;
  1023. }
  1024. break;
  1025. case TrackType.Text:
  1026. if (index >= 0 && index < _textTrackInfo.Length)
  1027. {
  1028. Native.AVPPlayerTextTrackInfo trackInfo = _textTrackInfo[index];
  1029. track = new TextTrack(index, trackInfo.name, trackInfo.language, trackInfo.flags.IsDefault());
  1030. isActiveTrack = _state.selectedTextTrack == index;
  1031. }
  1032. break;
  1033. default:
  1034. break;
  1035. }
  1036. return track;
  1037. }
  1038. internal override bool InternalIsChangedTextCue()
  1039. {
  1040. return _state.status.HasUpdatedText();
  1041. }
  1042. internal override string InternalGetCurrentTextCue()
  1043. {
  1044. if (_playerText.buffer != IntPtr.Zero)
  1045. return Marshal.PtrToStringUni(_playerText.buffer, _playerText.length);
  1046. else
  1047. return null;
  1048. }
  1049. }
  1050. #if !UNITY_EDITOR && ( UNITY_IOS || UNITY_ANDROID )
  1051. // Media Caching
  1052. public sealed partial class PlatformMediaPlayer
  1053. {
  1054. public override bool IsMediaCachingSupported()
  1055. {
  1056. return true;
  1057. }
  1058. public override void AddMediaToCache(string url, string headers, MediaCachingOptions options)
  1059. {
  1060. Native.MediaCachingOptions nativeOptions = new Native.MediaCachingOptions();
  1061. GCHandle artworkHandle = new GCHandle();
  1062. if (options != null)
  1063. {
  1064. nativeOptions.minimumRequiredBitRate = options.minimumRequiredBitRate;
  1065. nativeOptions.minimumRequiredResolution_width = options.minimumRequiredResolution.x;
  1066. nativeOptions.minimumRequiredResolution_height = options.minimumRequiredResolution.y;
  1067. nativeOptions.title = options.title;
  1068. if (options.artwork != null && options.artwork.Length > 0)
  1069. {
  1070. artworkHandle = GCHandle.Alloc(options.artwork, GCHandleType.Pinned);
  1071. nativeOptions.artwork = artworkHandle.AddrOfPinnedObject();
  1072. nativeOptions.artworkLength = options.artwork.Length;
  1073. }
  1074. }
  1075. Native.AVPPluginCacheMediaForURL(_player, url, headers, nativeOptions);
  1076. if (artworkHandle.IsAllocated)
  1077. {
  1078. artworkHandle.Free();
  1079. }
  1080. }
  1081. public override void CancelDownloadOfMediaToCache(string url)
  1082. {
  1083. Native.AVPPluginCancelDownloadOfMediaForURL(_player, url);
  1084. }
  1085. public override void RemoveMediaFromCache(string url)
  1086. {
  1087. Native.AVPPluginRemoveCachedMediaForURL(_player, url);
  1088. }
  1089. public override CachedMediaStatus GetCachedMediaStatus(string url, ref float progress)
  1090. {
  1091. return (CachedMediaStatus)Native.AVPPluginGetCachedMediaStatusForURL(_player, url, ref progress);
  1092. }
  1093. }
  1094. #endif
  1095. }
  1096. #endif