MediaPlayerUI.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  1. // UnityEngine.UI was moved to a package in 2019.2.0
  2. // Unfortunately no way to test for this across all Unity versions yet
  3. // You can set up the asmdef to reference the new package, but the package doesn't
  4. // existing in Unity 2017 etc, and it throws an error due to missing reference
  5. #define AVPRO_PACKAGE_UNITYUI
  6. #if (UNITY_2019_2_OR_NEWER && AVPRO_PACKAGE_UNITYUI) || (!UNITY_2019_2_OR_NEWER)
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using UnityEngine;
  10. using UnityEngine.UI;
  11. using UnityEngine.EventSystems;
  12. using RenderHeads.Media.AVProVideo;
  13. using RenderHeads.Media.AVProVideo.Demos.UI;
  14. //-----------------------------------------------------------------------------
  15. // Copyright 2018-2021 RenderHeads Ltd. All rights reserved.
  16. //-----------------------------------------------------------------------------
  17. namespace RenderHeads.Media.AVProVideo.Demos
  18. {
  19. public class MediaPlayerUI : MonoBehaviour
  20. {
  21. [SerializeField] MediaPlayer _mediaPlayer = null;
  22. [Header("Options")]
  23. [SerializeField] float _keyVolumeDelta = 0.05f;
  24. [SerializeField] float _jumpDeltaTime = 5f;
  25. [SerializeField] bool _showOptions = true;
  26. [SerializeField] bool _autoHide = true;
  27. [SerializeField] float _userInactiveDuration = 1.5f;
  28. [SerializeField] bool _useAudioFading = true;
  29. [Header("Keyboard Controls")]
  30. [SerializeField] bool _enableKeyboardControls = true;
  31. [SerializeField] KeyCode KeyVolumeUp = KeyCode.UpArrow;
  32. [SerializeField] KeyCode KeyVolumeDown = KeyCode.DownArrow;
  33. [SerializeField] KeyCode KeyTogglePlayPause = KeyCode.Space;
  34. [SerializeField] KeyCode KeyToggleMute = KeyCode.M;
  35. [SerializeField] KeyCode KeyJumpForward = KeyCode.RightArrow;
  36. [SerializeField] KeyCode KeyJumpBack = KeyCode.LeftArrow;
  37. [Header("Optional Components")]
  38. [SerializeField] OverlayManager _overlayManager = null;
  39. [SerializeField] MediaPlayer _thumbnailMediaPlayer = null;
  40. [SerializeField] RectTransform _timelineTip = null;
  41. [Header("UI Components")]
  42. [SerializeField] RectTransform _canvasTransform = null;
  43. //[SerializeField] Image image = null;
  44. [SerializeField] Slider _sliderTime = null;
  45. [SerializeField] EventTrigger _videoTouch = null;
  46. [SerializeField] CanvasGroup _controlsGroup = null;
  47. [Header("UI Components (Optional)")]
  48. [SerializeField] GameObject _liveItem = null;
  49. [SerializeField] Text _textMediaName = null;
  50. [SerializeField] Text _textTimeDuration = null;
  51. [SerializeField] Slider _sliderVolume = null;
  52. [SerializeField] Button _buttonPlayPause = null;
  53. [SerializeField] Button _buttonVolume = null;
  54. [SerializeField] Button _buttonSubtitles = null;
  55. [SerializeField] Button _buttonOptions = null;
  56. [SerializeField] Button _buttonTimeBack = null;
  57. [SerializeField] Button _buttonTimeForward = null;
  58. [SerializeField] RawImage _imageAudioSpectrum = null;
  59. [SerializeField] GameObject _optionsMenuRoot = null;
  60. [SerializeField] HorizontalSegmentsPrimitive _segmentsSeek = null;
  61. [SerializeField] HorizontalSegmentsPrimitive _segmentsBuffered = null;
  62. [SerializeField] HorizontalSegmentsPrimitive _segmentsProgress = null;
  63. private bool _wasPlayingBeforeTimelineDrag;
  64. private float _controlsFade = 1f;
  65. private Material _playPauseMaterial;
  66. private Material _volumeMaterial;
  67. private Material _subtitlesMaterial;
  68. private Material _optionsMaterial;
  69. private Material _audioSpectrumMaterial;
  70. private float[] _spectrumSamples = new float[128];
  71. private float[] _spectrumSamplesSmooth = new float[128];
  72. private float _maxValue = 1f;
  73. private float _audioVolume = 1f;
  74. private float _audioFade = 0f;
  75. private bool _isAudioFadingUpToPlay = true;
  76. private const float AudioFadeDuration = 0.25f;
  77. private float _audioFadeTime = 0f;
  78. private readonly LazyShaderProperty _propMorph = new LazyShaderProperty("_Morph");
  79. private readonly LazyShaderProperty _propMute = new LazyShaderProperty("_Mute");
  80. private readonly LazyShaderProperty _propVolume = new LazyShaderProperty("_Volume");
  81. private readonly LazyShaderProperty _propSpectrum = new LazyShaderProperty("_Spectrum");
  82. private readonly LazyShaderProperty _propSpectrumRange = new LazyShaderProperty("_SpectrumRange");
  83. void Awake()
  84. {
  85. #if UNITY_IOS || UNITY_ANDROID
  86. Debug.Log("Setting Application.targetFrameRate to: " + Screen.currentResolution.refreshRate);
  87. Application.targetFrameRate = Screen.currentResolution.refreshRate;
  88. #endif
  89. }
  90. void Start()
  91. {
  92. if (_mediaPlayer)
  93. {
  94. _audioVolume = _mediaPlayer.AudioVolume;
  95. #if UNITY_ANDROID
  96. // Disable screen sleep timeout if the video is set to auto-start
  97. if (_mediaPlayer.AutoStart)
  98. {
  99. Screen.sleepTimeout = SleepTimeout.NeverSleep;
  100. }
  101. #endif
  102. }
  103. SetupPlayPauseButton();
  104. SetupTimeBackForwardButtons();
  105. SetupVolumeButton();
  106. SetupSubtitlesButton();
  107. SetupOptionsButton();
  108. SetupAudioSpectrum();
  109. CreateTimelineDragEvents();
  110. CreateVideoTouchEvents();
  111. CreateVolumeSliderEvents();
  112. UpdateVolumeSlider();
  113. BuildOptionsMenu();
  114. }
  115. private struct UserInteraction
  116. {
  117. public static float InactiveTime;
  118. private static Vector3 _previousMousePos;
  119. private static int _lastInputFrame;
  120. public static bool IsUserInputThisFrame()
  121. {
  122. if (Time.frameCount == _lastInputFrame)
  123. {
  124. return true;
  125. }
  126. #if (!ENABLE_INPUT_SYSTEM || ENABLE_LEGACY_INPUT_MANAGER)
  127. bool touchInput = (Input.touchSupported && Input.touchCount > 0);
  128. bool mouseInput = (Input.mousePresent && (Input.mousePosition != _previousMousePos || Input.mouseScrollDelta != Vector2.zero || Input.GetMouseButton(0)));
  129. if (touchInput || mouseInput)
  130. {
  131. _previousMousePos = Input.mousePosition;
  132. _lastInputFrame = Time.frameCount;
  133. return true;
  134. }
  135. return false;
  136. #else
  137. return true;
  138. #endif
  139. }
  140. }
  141. private Material DuplicateMaterialOnImage(Graphic image)
  142. {
  143. // Assign a copy of the material so we aren't modifying the material asset file
  144. image.material = new Material(image.material);
  145. return image.material;
  146. }
  147. private void SetupPlayPauseButton()
  148. {
  149. if (_buttonPlayPause)
  150. {
  151. _buttonPlayPause.onClick.AddListener(OnPlayPauseButtonPressed);
  152. _playPauseMaterial = DuplicateMaterialOnImage(_buttonPlayPause.GetComponent<Image>());
  153. }
  154. }
  155. private void SetupTimeBackForwardButtons()
  156. {
  157. if (_buttonTimeBack)
  158. {
  159. _buttonTimeBack.onClick.AddListener(OnPlayTimeBackButtonPressed);
  160. }
  161. if (_buttonTimeForward)
  162. {
  163. _buttonTimeForward.onClick.AddListener(OnPlayTimeForwardButtonPressed);
  164. }
  165. }
  166. private void SetupVolumeButton()
  167. {
  168. if (_buttonVolume)
  169. {
  170. _buttonVolume.onClick.AddListener(OnVolumeButtonPressed);
  171. _volumeMaterial = DuplicateMaterialOnImage(_buttonVolume.GetComponent<Image>());
  172. }
  173. }
  174. private void SetupSubtitlesButton()
  175. {
  176. if (_buttonSubtitles)
  177. {
  178. _buttonSubtitles.onClick.AddListener(OnSubtitlesButtonPressed);
  179. _subtitlesMaterial = DuplicateMaterialOnImage(_buttonSubtitles.GetComponent<Image>());
  180. }
  181. }
  182. private void SetupOptionsButton()
  183. {
  184. if (_buttonOptions)
  185. {
  186. _buttonOptions.onClick.AddListener(OnOptionsButtonPressed);
  187. _optionsMaterial = DuplicateMaterialOnImage(_buttonOptions.GetComponent<Image>());
  188. }
  189. }
  190. private void SetupAudioSpectrum()
  191. {
  192. if (_imageAudioSpectrum)
  193. {
  194. _audioSpectrumMaterial = DuplicateMaterialOnImage(_imageAudioSpectrum);
  195. }
  196. }
  197. private void OnPlayPauseButtonPressed()
  198. {
  199. TogglePlayPause();
  200. }
  201. private void OnPlayTimeBackButtonPressed()
  202. {
  203. SeekRelative(-_jumpDeltaTime);
  204. }
  205. private void OnPlayTimeForwardButtonPressed()
  206. {
  207. SeekRelative(_jumpDeltaTime);
  208. }
  209. private void OnVolumeButtonPressed()
  210. {
  211. ToggleMute();
  212. }
  213. private void OnSubtitlesButtonPressed()
  214. {
  215. ToggleSubtitles();
  216. }
  217. private void OnOptionsButtonPressed()
  218. {
  219. ToggleOptionsMenu();
  220. }
  221. private bool _isHoveringOverTimeline;
  222. private void OnTimelineBeginHover(PointerEventData eventData)
  223. {
  224. if (eventData.pointerCurrentRaycast.gameObject != null)
  225. {
  226. _isHoveringOverTimeline = true;
  227. _sliderTime.transform.localScale = new Vector3(1f, 2.5f, 1f);
  228. }
  229. }
  230. private void OnTimelineEndHover(PointerEventData eventData)
  231. {
  232. _isHoveringOverTimeline = false;
  233. _sliderTime.transform.localScale = new Vector3(1f, 1f, 1f);
  234. }
  235. private void CreateVideoTouchEvents()
  236. {
  237. EventTrigger.Entry entry = new EventTrigger.Entry();
  238. entry.eventID = EventTriggerType.PointerUp;
  239. entry.callback.AddListener((data) => { OnVideoPointerUp(); });
  240. _videoTouch.triggers.Add(entry);
  241. }
  242. private void OnVideoPointerUp()
  243. {
  244. bool controlsMostlyVisible = (_controlsGroup.alpha >= 0.5f && _controlsGroup.gameObject.activeSelf);
  245. if (controlsMostlyVisible)
  246. {
  247. TogglePlayPause();
  248. }
  249. }
  250. void UpdateAudioFading()
  251. {
  252. // Increment fade timer
  253. if (_audioFadeTime < AudioFadeDuration)
  254. {
  255. _audioFadeTime = Mathf.Clamp(_audioFadeTime + Time.deltaTime, 0f, AudioFadeDuration);
  256. }
  257. // Trigger pause when audio faded down
  258. if (_audioFadeTime >= AudioFadeDuration)
  259. {
  260. if (!_isAudioFadingUpToPlay)
  261. {
  262. Pause(skipFeedback:true);
  263. }
  264. }
  265. // Apply audio fade value
  266. if (_mediaPlayer.Control != null && _mediaPlayer.Control.IsPlaying())
  267. {
  268. _audioFade = Mathf.Clamp01(_audioFadeTime / AudioFadeDuration);
  269. if (!_isAudioFadingUpToPlay)
  270. {
  271. _audioFade = (1f - _audioFade);
  272. }
  273. ApplyAudioVolume();
  274. }
  275. }
  276. public void TogglePlayPause()
  277. {
  278. if (_mediaPlayer && _mediaPlayer.Control != null)
  279. {
  280. if (_useAudioFading && _mediaPlayer.Info.HasAudio())
  281. {
  282. if (_mediaPlayer.Control.IsPlaying())
  283. {
  284. if (_overlayManager)
  285. {
  286. _overlayManager.TriggerFeedback(OverlayManager.Feedback.Pause);
  287. }
  288. _isAudioFadingUpToPlay = false;
  289. }
  290. else
  291. {
  292. _isAudioFadingUpToPlay = true;
  293. Play();
  294. }
  295. _audioFadeTime = 0f;
  296. }
  297. else
  298. {
  299. if (_mediaPlayer.Control.IsPlaying())
  300. {
  301. Pause();
  302. }
  303. else
  304. {
  305. Play();
  306. }
  307. }
  308. }
  309. }
  310. private void Play()
  311. {
  312. if (_mediaPlayer && _mediaPlayer.Control != null)
  313. {
  314. if (_overlayManager)
  315. {
  316. _overlayManager.TriggerFeedback(OverlayManager.Feedback.Play);
  317. }
  318. _mediaPlayer.Play();
  319. #if UNITY_ANDROID
  320. Screen.sleepTimeout = SleepTimeout.NeverSleep;
  321. #endif
  322. }
  323. }
  324. private void Pause(bool skipFeedback = false)
  325. {
  326. if (_mediaPlayer && _mediaPlayer.Control != null)
  327. {
  328. if (!skipFeedback)
  329. {
  330. if (_overlayManager)
  331. {
  332. _overlayManager.TriggerFeedback(OverlayManager.Feedback.Pause);
  333. }
  334. }
  335. _mediaPlayer.Pause();
  336. #if UNITY_ANDROID
  337. Screen.sleepTimeout = SleepTimeout.SystemSetting;
  338. #endif
  339. }
  340. }
  341. public void SeekRelative(float deltaTime)
  342. {
  343. if (_mediaPlayer && _mediaPlayer.Control != null)
  344. {
  345. TimeRange timelineRange = GetTimelineRange();
  346. double time = _mediaPlayer.Control.GetCurrentTime() + deltaTime;
  347. time = System.Math.Max(time, timelineRange.startTime);
  348. time = System.Math.Min(time, timelineRange.startTime + timelineRange.duration);
  349. _mediaPlayer.Control.Seek(time);
  350. if (_overlayManager)
  351. {
  352. _overlayManager.TriggerFeedback(deltaTime > 0f ? OverlayManager.Feedback.SeekForward : OverlayManager.Feedback.SeekBack);
  353. }
  354. }
  355. }
  356. public void ChangeAudioVolume(float delta)
  357. {
  358. if (_mediaPlayer && _mediaPlayer.Control != null)
  359. {
  360. // Change volume
  361. _audioVolume = Mathf.Clamp01(_audioVolume + delta);
  362. // Update the UI
  363. UpdateVolumeSlider();
  364. // Trigger the overlays
  365. if (_overlayManager)
  366. {
  367. _overlayManager.TriggerFeedback(delta > 0f ? OverlayManager.Feedback.VolumeUp : OverlayManager.Feedback.VolumeDown);
  368. }
  369. }
  370. }
  371. public void ToggleMute()
  372. {
  373. if (_mediaPlayer && _mediaPlayer.Control != null)
  374. {
  375. if (_mediaPlayer.AudioMuted)
  376. {
  377. MuteAudio(false);
  378. }
  379. else
  380. {
  381. MuteAudio(true);
  382. }
  383. }
  384. }
  385. private void MuteAudio(bool mute)
  386. {
  387. if (_mediaPlayer && _mediaPlayer.Control != null)
  388. {
  389. // Change mute
  390. _mediaPlayer.AudioMuted = mute;
  391. // Update the UI
  392. // The UI element is constantly updated by the Update() method
  393. // Trigger the overlays
  394. if (_overlayManager)
  395. {
  396. _overlayManager.TriggerFeedback(mute ? OverlayManager.Feedback.VolumeMute : OverlayManager.Feedback.VolumeUp);
  397. }
  398. }
  399. }
  400. public void ToggleSubtitles()
  401. {
  402. if (_mediaPlayer && _mediaPlayer.TextTracks != null)
  403. {
  404. if (_mediaPlayer.TextTracks.GetTextTracks().Count > 0)
  405. {
  406. if (_mediaPlayer.TextTracks.GetActiveTextTrack() != null)
  407. {
  408. _mediaPlayer.TextTracks.SetActiveTextTrack(null);
  409. }
  410. else
  411. {
  412. // TODO: instead of activating the first one, base it on the language/track
  413. // selection stored in the MediaPlayerUI
  414. _mediaPlayer.TextTracks.SetActiveTextTrack(_mediaPlayer.TextTracks.GetTextTracks()[0]);
  415. }
  416. }
  417. }
  418. }
  419. private void ToggleOptionsMenu()
  420. {
  421. _showOptions = !_showOptions;
  422. BuildOptionsMenu();
  423. }
  424. private void BuildOptionsMenu()
  425. {
  426. if (_optionsMenuRoot)
  427. {
  428. _optionsMenuRoot.SetActive(_showOptions);
  429. }
  430. // Temporary code for now disables to touch controls while the debug menu
  431. // is shown, to stop it consuming mouse input for IMGUI
  432. _videoTouch.enabled = !_showOptions;
  433. }
  434. private void CreateTimelineDragEvents()
  435. {
  436. EventTrigger trigger = _sliderTime.gameObject.GetComponent<EventTrigger>();
  437. if (trigger != null)
  438. {
  439. EventTrigger.Entry entry = new EventTrigger.Entry();
  440. entry.eventID = EventTriggerType.PointerDown;
  441. entry.callback.AddListener((data) => { OnTimeSliderBeginDrag(); });
  442. trigger.triggers.Add(entry);
  443. entry = new EventTrigger.Entry();
  444. entry.eventID = EventTriggerType.Drag;
  445. entry.callback.AddListener((data) => { OnTimeSliderDrag(); });
  446. trigger.triggers.Add(entry);
  447. entry = new EventTrigger.Entry();
  448. entry.eventID = EventTriggerType.PointerUp;
  449. entry.callback.AddListener((data) => { OnTimeSliderEndDrag(); });
  450. trigger.triggers.Add(entry);
  451. entry = new EventTrigger.Entry();
  452. entry.eventID = EventTriggerType.PointerEnter;
  453. entry.callback.AddListener((data) => { OnTimelineBeginHover((PointerEventData)data); });
  454. trigger.triggers.Add(entry);
  455. entry = new EventTrigger.Entry();
  456. entry.eventID = EventTriggerType.PointerExit;
  457. entry.callback.AddListener((data) => { OnTimelineEndHover((PointerEventData)data); });
  458. trigger.triggers.Add(entry);
  459. }
  460. }
  461. private void CreateVolumeSliderEvents()
  462. {
  463. if (_sliderVolume != null)
  464. {
  465. EventTrigger trigger = _sliderVolume.gameObject.GetComponent<EventTrigger>();
  466. if (trigger != null)
  467. {
  468. EventTrigger.Entry entry = new EventTrigger.Entry();
  469. entry.eventID = EventTriggerType.PointerDown;
  470. entry.callback.AddListener((data) => { OnVolumeSliderDrag(); });
  471. trigger.triggers.Add(entry);
  472. entry = new EventTrigger.Entry();
  473. entry.eventID = EventTriggerType.Drag;
  474. entry.callback.AddListener((data) => { OnVolumeSliderDrag(); });
  475. trigger.triggers.Add(entry);
  476. }
  477. }
  478. }
  479. private void OnVolumeSliderDrag()
  480. {
  481. if (_mediaPlayer && _mediaPlayer.Control != null)
  482. {
  483. _audioVolume = _sliderVolume.value;
  484. ApplyAudioVolume();
  485. }
  486. }
  487. private void ApplyAudioVolume()
  488. {
  489. if (_mediaPlayer)
  490. {
  491. _mediaPlayer.AudioVolume = (_audioVolume * _audioFade);
  492. }
  493. }
  494. private void UpdateVolumeSlider()
  495. {
  496. if (_sliderVolume)
  497. {
  498. if (_mediaPlayer)
  499. {
  500. // TODO: remove this
  501. /*if (mp.Control != null)
  502. {
  503. _sliderVolume.value = mp.Control.GetVolume();
  504. }
  505. else*/
  506. {
  507. _sliderVolume.value = _audioVolume;
  508. }
  509. }
  510. }
  511. }
  512. private void UpdateAudioSpectrum()
  513. {
  514. bool showAudioSpectrum = false;
  515. #if !UNITY_IOS || UNITY_EDITOR
  516. if (_mediaPlayer && _mediaPlayer.Control != null)
  517. {
  518. AudioSource audioSource = _mediaPlayer.AudioSource;
  519. if (audioSource && _audioSpectrumMaterial)
  520. {
  521. showAudioSpectrum = true;
  522. float maxFreq = (Helper.GetUnityAudioSampleRate() / 2);
  523. // Frequencies over 18Khz generally aren't very interesting to visualise, so clamp the range
  524. const float clampFreq = 18000f;
  525. int sampleRange = Mathf.FloorToInt(Mathf.Clamp01(clampFreq / maxFreq) * _spectrumSamples.Length);
  526. // Add new samples and smooth the samples over time
  527. audioSource.GetSpectrumData(_spectrumSamples, 0, FFTWindow.BlackmanHarris);
  528. // Find the maxValue sample for normalising with
  529. float maxValue = -1.0f;
  530. for (int i = 0; i < sampleRange; i++)
  531. {
  532. if (_spectrumSamples[i] > maxValue)
  533. {
  534. maxValue = _spectrumSamples[i];
  535. }
  536. }
  537. // Chase maxValue to zero
  538. _maxValue = Mathf.Lerp(_maxValue, 0.0f, Mathf.Clamp01(2.0f * Time.deltaTime));
  539. // Update maxValue
  540. _maxValue = Mathf.Max(_maxValue, maxValue);
  541. if (_maxValue <= 0.01f)
  542. {
  543. _maxValue = 1f;
  544. }
  545. // Copy and smooth the spectrum values
  546. for (int i = 0; i < sampleRange; i++)
  547. {
  548. float newSample = _spectrumSamples[i] / _maxValue;
  549. _spectrumSamplesSmooth[i] = Mathf.Lerp(_spectrumSamplesSmooth[i], newSample, Mathf.Clamp01(15.0f * Time.deltaTime));
  550. }
  551. // Update shader
  552. _audioSpectrumMaterial.SetFloatArray(_propSpectrum.Id, _spectrumSamplesSmooth);
  553. _audioSpectrumMaterial.SetFloat(_propSpectrumRange.Id, (float)sampleRange);
  554. }
  555. }
  556. #endif
  557. if (_imageAudioSpectrum)
  558. {
  559. _imageAudioSpectrum.gameObject.SetActive(showAudioSpectrum);
  560. }
  561. }
  562. private void OnTimeSliderBeginDrag()
  563. {
  564. if (_mediaPlayer && _mediaPlayer.Control != null)
  565. {
  566. _wasPlayingBeforeTimelineDrag = _mediaPlayer.Control.IsPlaying();
  567. if (_wasPlayingBeforeTimelineDrag)
  568. {
  569. _mediaPlayer.Pause();
  570. }
  571. OnTimeSliderDrag();
  572. }
  573. }
  574. private void OnTimeSliderDrag()
  575. {
  576. if (_mediaPlayer && _mediaPlayer.Control != null)
  577. {
  578. TimeRange timelineRange = GetTimelineRange();
  579. double time = timelineRange.startTime + (_sliderTime.value * timelineRange.duration);
  580. _mediaPlayer.Control.Seek(time);
  581. _isHoveringOverTimeline = true;
  582. }
  583. }
  584. private void OnTimeSliderEndDrag()
  585. {
  586. if (_mediaPlayer && _mediaPlayer.Control != null)
  587. {
  588. if (_wasPlayingBeforeTimelineDrag)
  589. {
  590. _mediaPlayer.Play();
  591. _wasPlayingBeforeTimelineDrag = false;
  592. }
  593. }
  594. }
  595. private TimeRange GetTimelineRange()
  596. {
  597. if (_mediaPlayer.Info != null)
  598. {
  599. return Helper.GetTimelineRange(_mediaPlayer.Info.GetDuration(), _mediaPlayer.Control.GetSeekableTimes());
  600. }
  601. return new TimeRange();
  602. }
  603. private bool CanHideControls()
  604. {
  605. bool result = true;
  606. if (!_autoHide)
  607. {
  608. result = false;
  609. }
  610. #if (!ENABLE_INPUT_SYSTEM || ENABLE_LEGACY_INPUT_MANAGER)
  611. else if (Input.mousePresent)
  612. {
  613. // Check whether the mouse cursor is over the controls, in which case we can't hide the UI
  614. RectTransform rect = _controlsGroup.GetComponent<RectTransform>();
  615. Vector2 canvasPos;
  616. RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, Input.mousePosition, null, out canvasPos);
  617. Rect rr = RectTransformUtility.PixelAdjustRect(rect, null);
  618. result = !rr.Contains(canvasPos);
  619. }
  620. #endif
  621. return result;
  622. }
  623. private void UpdateControlsVisibility()
  624. {
  625. if (UserInteraction.IsUserInputThisFrame() || !CanHideControls())
  626. {
  627. UserInteraction.InactiveTime = 0f;
  628. FadeUpControls();
  629. }
  630. else
  631. {
  632. UserInteraction.InactiveTime += Time.unscaledDeltaTime;
  633. if (UserInteraction.InactiveTime >= _userInactiveDuration)
  634. {
  635. FadeDownControls();
  636. }
  637. else
  638. {
  639. FadeUpControls();
  640. }
  641. }
  642. }
  643. private void FadeUpControls()
  644. {
  645. if (!_controlsGroup.gameObject.activeSelf)
  646. {
  647. _controlsGroup.gameObject.SetActive(true);
  648. }
  649. _controlsFade = Mathf.Min(1f, _controlsFade + Time.deltaTime * 8f);
  650. _controlsGroup.alpha = Mathf.Pow(_controlsFade, 5f);
  651. }
  652. private void FadeDownControls()
  653. {
  654. if (_controlsGroup.gameObject.activeSelf)
  655. {
  656. _controlsFade = Mathf.Max(0f, _controlsFade - Time.deltaTime * 3f);
  657. _controlsGroup.alpha = Mathf.Pow(_controlsFade, 5f);
  658. if (_controlsGroup.alpha <= 0f)
  659. {
  660. _controlsGroup.gameObject.SetActive(false);
  661. }
  662. }
  663. }
  664. void Update()
  665. {
  666. if (!_mediaPlayer) return;
  667. UpdateControlsVisibility();
  668. UpdateAudioFading();
  669. UpdateAudioSpectrum();
  670. if (_mediaPlayer.Info != null)
  671. {
  672. TimeRange timelineRange = GetTimelineRange();
  673. // Update timeline hover popup
  674. #if (!ENABLE_INPUT_SYSTEM || ENABLE_LEGACY_INPUT_MANAGER)
  675. if (_timelineTip != null)
  676. {
  677. if (_isHoveringOverTimeline)
  678. {
  679. Vector2 canvasPos;
  680. RectTransformUtility.ScreenPointToLocalPointInRectangle(_canvasTransform, Input.mousePosition, null, out canvasPos);
  681. _segmentsSeek.gameObject.SetActive(true);
  682. _timelineTip.gameObject.SetActive(true);
  683. Vector3 mousePos = _canvasTransform.TransformPoint(canvasPos);
  684. _timelineTip.position = new Vector2(mousePos.x, _timelineTip.position.y);
  685. if (UserInteraction.IsUserInputThisFrame())
  686. {
  687. // Work out position on the timeline
  688. Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(this._sliderTime.GetComponent<RectTransform>());
  689. float x = Mathf.Clamp01((canvasPos.x - bounds.min.x) / bounds.size.x);
  690. double time = (double)x * timelineRange.Duration;
  691. // Seek to the new position
  692. if (_thumbnailMediaPlayer != null && _thumbnailMediaPlayer.Control != null)
  693. {
  694. _thumbnailMediaPlayer.Control.SeekFast(time);
  695. }
  696. // Update time text
  697. Text hoverText = _timelineTip.GetComponentInChildren<Text>();
  698. if (hoverText != null)
  699. {
  700. time -= timelineRange.startTime;
  701. time = System.Math.Max(time, 0.0);
  702. time = System.Math.Min(time, timelineRange.Duration);
  703. hoverText.text = Helper.GetTimeString(time, false);
  704. }
  705. {
  706. // Update seek segment when hovering over timeline
  707. if (_segmentsSeek != null)
  708. {
  709. float[] ranges = new float[2];
  710. if (timelineRange.Duration > 0.0)
  711. {
  712. double t = ((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime) / timelineRange.duration);
  713. ranges[1] = x;
  714. ranges[0] = (float)t;
  715. }
  716. _segmentsSeek.Segments = ranges;
  717. }
  718. }
  719. }
  720. }
  721. else
  722. {
  723. _timelineTip.gameObject.SetActive(false);
  724. _segmentsSeek.gameObject.SetActive(false);
  725. }
  726. }
  727. #endif
  728. // Updated stalled display
  729. if (_overlayManager)
  730. {
  731. _overlayManager.Reset();
  732. if (_mediaPlayer.Info.IsPlaybackStalled())
  733. {
  734. _overlayManager.TriggerStalled();
  735. }
  736. }
  737. // Update keyboard input
  738. if (_enableKeyboardControls)
  739. {
  740. #if (!ENABLE_INPUT_SYSTEM || ENABLE_LEGACY_INPUT_MANAGER)
  741. // Keyboard toggle play/pause
  742. if (Input.GetKeyDown(KeyTogglePlayPause))
  743. {
  744. TogglePlayPause();
  745. }
  746. // Keyboard seek 5 seconds
  747. if (Input.GetKeyDown(KeyJumpBack))
  748. {
  749. SeekRelative(-_jumpDeltaTime);
  750. }
  751. else if (Input.GetKeyDown(KeyJumpForward))
  752. {
  753. SeekRelative(_jumpDeltaTime);
  754. }
  755. // Keyboard control volume
  756. if (Input.GetKeyDown(KeyVolumeUp))
  757. {
  758. ChangeAudioVolume(_keyVolumeDelta);
  759. }
  760. else if (Input.GetKeyDown(KeyVolumeDown))
  761. {
  762. ChangeAudioVolume(-_keyVolumeDelta);
  763. }
  764. // Keyboard toggle mute
  765. if (Input.GetKeyDown(KeyToggleMute))
  766. {
  767. ToggleMute();
  768. }
  769. #endif
  770. }
  771. // Animation play/pause button
  772. if (_playPauseMaterial != null)
  773. {
  774. float t = _playPauseMaterial.GetFloat(_propMorph.Id);
  775. float d = 1f;
  776. if (_mediaPlayer.Control.IsPlaying())
  777. {
  778. d = -1f;
  779. }
  780. t += d * Time.deltaTime * 6f;
  781. t = Mathf.Clamp01(t);
  782. _playPauseMaterial.SetFloat(_propMorph.Id, t);
  783. }
  784. // Animation volume/mute button
  785. if (_volumeMaterial != null)
  786. {
  787. float t = _volumeMaterial.GetFloat(_propMute.Id);
  788. float d = 1f;
  789. if (!_mediaPlayer.AudioMuted)
  790. {
  791. d = -1f;
  792. }
  793. t += d * Time.deltaTime * 6f;
  794. t = Mathf.Clamp01(t);
  795. _volumeMaterial.SetFloat(_propMute.Id, t);
  796. _volumeMaterial.SetFloat(_propVolume.Id, _audioVolume);
  797. }
  798. // Animation subtitles button
  799. if (_subtitlesMaterial)
  800. {
  801. float t = _subtitlesMaterial.GetFloat(_propMorph.Id);
  802. float d = 1f;
  803. if (_mediaPlayer.TextTracks.GetActiveTextTrack() == null)
  804. {
  805. d = -1f;
  806. }
  807. t += d * Time.deltaTime * 6f;
  808. t = Mathf.Clamp01(t);
  809. _subtitlesMaterial.SetFloat(_propMorph.Id, t);
  810. }
  811. // Animation options button
  812. if (_optionsMaterial)
  813. {
  814. float t = _optionsMaterial.GetFloat(_propMorph.Id);
  815. float d = 1f;
  816. if (!_showOptions)
  817. {
  818. d = -1f;
  819. }
  820. t += d * Time.deltaTime * 6f;
  821. t = Mathf.Clamp01(t);
  822. _optionsMaterial.SetFloat(_propMorph.Id, t);
  823. }
  824. // Update time/duration text display
  825. if (_textTimeDuration)
  826. {
  827. string t1 = Helper.GetTimeString((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime), false);
  828. string d1 = Helper.GetTimeString(timelineRange.duration, false);
  829. _textTimeDuration.text = string.Format("{0} / {1}", t1, d1);
  830. }
  831. // Update volume slider
  832. if (!_useAudioFading)
  833. {
  834. UpdateVolumeSlider();
  835. }
  836. // Update time slider position
  837. if (_sliderTime && !_isHoveringOverTimeline)
  838. {
  839. double t = 0.0;
  840. if (timelineRange.duration > 0.0)
  841. {
  842. t = ((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime) / timelineRange.duration);
  843. }
  844. _sliderTime.value = Mathf.Clamp01((float)t);
  845. }
  846. // Update LIVE text visible
  847. if (_liveItem)
  848. {
  849. _liveItem.SetActive(double.IsInfinity(_mediaPlayer.Info.GetDuration()));
  850. }
  851. // Update subtitle button visible
  852. if (_buttonSubtitles)
  853. {
  854. _buttonSubtitles.gameObject.SetActive(_mediaPlayer.TextTracks.GetTextTracks().Count > 0);
  855. }
  856. // Update media name
  857. if (_textMediaName)
  858. {
  859. #if MEDIA_NAME
  860. string mediaName = string.Empty;
  861. if (!string.IsNullOrEmpty(_mediaPlayer.VideoPath))
  862. {
  863. mediaName = System.IO.Path.GetFileName(_mediaPlayer.VideoPath);
  864. if (mediaName.Length > 26)
  865. {
  866. mediaName = mediaName.Substring(0, 26);
  867. }
  868. }
  869. #endif
  870. string resolutionName = string.Empty;
  871. if (_mediaPlayer.Info.GetVideoWidth() > 0)
  872. {
  873. resolutionName = Helper.GetFriendlyResolutionName(_mediaPlayer.Info.GetVideoWidth(), _mediaPlayer.Info.GetVideoHeight(), _mediaPlayer.Info.GetVideoFrameRate());
  874. }
  875. #if MEDIA_NAME
  876. _textMediaName.text = string.Format("{0} {1}", mediaName, resolutionName);
  877. #else
  878. _textMediaName.text = resolutionName;
  879. #endif
  880. }
  881. // Update buffered segments
  882. if (_segmentsBuffered)
  883. {
  884. TimeRanges times = _mediaPlayer.Control.GetBufferedTimes();
  885. float[] ranges = null;
  886. if (times.Count > 0 && timelineRange.duration > 0.0)
  887. {
  888. ranges = new float[times.Count * 2];
  889. for (int i = 0; i < times.Count; i++)
  890. {
  891. ranges[i * 2 + 0] = Mathf.Max(0f, (float)((times[i].StartTime - timelineRange.startTime) / timelineRange.duration));
  892. ranges[i * 2 + 1] = Mathf.Min(1f,(float)((times[i].EndTime - timelineRange.startTime) / timelineRange.duration));
  893. }
  894. }
  895. _segmentsBuffered.Segments = ranges;
  896. }
  897. // Update progress segment
  898. if (_segmentsProgress)
  899. {
  900. TimeRanges times = _mediaPlayer.Control.GetBufferedTimes();
  901. float[] ranges = null;
  902. if (times.Count > 0 && timelineRange.Duration > 0.0)
  903. {
  904. ranges = new float[2];
  905. double x1 = (times.MinTime - timelineRange.startTime) / timelineRange.duration;
  906. double x2 = ((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime) / timelineRange.duration);
  907. ranges[0] = Mathf.Max(0f, (float)x1);
  908. ranges[1] = Mathf.Min(1f, (float)x2);
  909. }
  910. _segmentsProgress.Segments = ranges;
  911. }
  912. }
  913. }
  914. void OnGUI()
  915. {
  916. // NOTE: These this IMGUI is just temporary until we implement the UI using uGUI
  917. if (!_showOptions) return;
  918. if (!_mediaPlayer || _mediaPlayer.Control == null) return;
  919. GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(2f, 2f, 1f));
  920. GUI.backgroundColor = Color.red;
  921. GUILayout.BeginVertical(GUI.skin.box);
  922. GUI.backgroundColor = Color.white;
  923. GUILayout.Label("Duration " + _mediaPlayer.Info.GetDuration() + "s");
  924. GUILayout.BeginHorizontal();
  925. GUILayout.Label("States: ");
  926. GUILayout.Toggle(_mediaPlayer.Control.HasMetaData(), "HasMetaData", GUI.skin.button);
  927. GUILayout.Toggle(_mediaPlayer.Control.IsPaused(), "Paused", GUI.skin.button);
  928. GUILayout.Toggle(_mediaPlayer.Control.IsPlaying(), "Playing", GUI.skin.button);
  929. GUILayout.Toggle(_mediaPlayer.Control.IsBuffering(), "Buffering", GUI.skin.button);
  930. GUILayout.Toggle(_mediaPlayer.Control.IsSeeking(), "Seeking", GUI.skin.button);
  931. GUILayout.Toggle(_mediaPlayer.Control.IsFinished(), "Finished", GUI.skin.button);
  932. GUILayout.EndHorizontal();
  933. {
  934. TimeRanges times = _mediaPlayer.Control.GetBufferedTimes();
  935. if (times != null)
  936. {
  937. GUILayout.Label("Buffered Range " + times.MinTime + " - " + times.MaxTime);
  938. }
  939. }
  940. {
  941. TimeRanges times = _mediaPlayer.Control.GetSeekableTimes();
  942. if (times != null)
  943. {
  944. GUILayout.Label("Seek Range " + times.MinTime + " - " + times.MaxTime);
  945. }
  946. }
  947. {
  948. GUILayout.Label("Video Tracks: " + _mediaPlayer.VideoTracks.GetVideoTracks().Count);
  949. GUILayout.BeginVertical();
  950. VideoTrack selectedTrack = null;
  951. foreach (VideoTrack track in _mediaPlayer.VideoTracks.GetVideoTracks())
  952. {
  953. bool isSelected = (track == _mediaPlayer.VideoTracks.GetActiveVideoTrack());
  954. if (isSelected) GUI.color= Color.green;
  955. if (GUILayout.Button(track.DisplayName, GUILayout.ExpandWidth(false)))
  956. {
  957. selectedTrack = track;
  958. }
  959. if (isSelected) GUI.color= Color.white;
  960. }
  961. GUILayout.EndHorizontal();
  962. if (selectedTrack != null)
  963. {
  964. _mediaPlayer.VideoTracks.SetActiveVideoTrack(selectedTrack);
  965. }
  966. }
  967. {
  968. GUILayout.Label("Audio Tracks: " + _mediaPlayer.AudioTracks.GetAudioTracks().Count);
  969. GUILayout.BeginVertical();
  970. AudioTrack selectedTrack = null;
  971. foreach (AudioTrack track in _mediaPlayer.AudioTracks.GetAudioTracks())
  972. {
  973. bool isSelected = (track == _mediaPlayer.AudioTracks.GetActiveAudioTrack());
  974. if (isSelected) GUI.color= Color.green;
  975. if (GUILayout.Button(track.DisplayName, GUILayout.ExpandWidth(false)))
  976. {
  977. selectedTrack = track;
  978. }
  979. if (isSelected) GUI.color= Color.white;
  980. }
  981. GUILayout.EndHorizontal();
  982. if (selectedTrack != null)
  983. {
  984. _mediaPlayer.AudioTracks.SetActiveAudioTrack(selectedTrack);
  985. }
  986. }
  987. {
  988. GUILayout.Label("Text Tracks: " + _mediaPlayer.TextTracks.GetTextTracks().Count);
  989. GUILayout.BeginVertical();
  990. TextTrack selectedTrack = null;
  991. foreach (TextTrack track in _mediaPlayer.TextTracks.GetTextTracks())
  992. {
  993. bool isSelected = (track == _mediaPlayer.TextTracks.GetActiveTextTrack());
  994. if (isSelected) GUI.color= Color.green;
  995. if (GUILayout.Button(track.DisplayName, GUILayout.ExpandWidth(false)))
  996. {
  997. selectedTrack = track;
  998. }
  999. if (isSelected) GUI.color= Color.white;
  1000. }
  1001. GUILayout.EndHorizontal();
  1002. if (selectedTrack != null)
  1003. {
  1004. _mediaPlayer.TextTracks.SetActiveTextTrack(selectedTrack);
  1005. }
  1006. }
  1007. {
  1008. GUILayout.Label("FPS: " + _mediaPlayer.Info.GetVideoDisplayRate().ToString("F2"));
  1009. }
  1010. #if (UNITY_STANDALONE_WIN)
  1011. if (_mediaPlayer.PlatformOptionsWindows.bufferedFrameSelection != BufferedFrameSelectionMode.None)
  1012. {
  1013. IBufferedDisplay bufferedDisplay = _mediaPlayer.BufferedDisplay;
  1014. if (bufferedDisplay != null)
  1015. {
  1016. BufferedFramesState state = bufferedDisplay.GetBufferedFramesState();
  1017. GUILayout.BeginHorizontal();
  1018. GUILayout.Label("Buffered Frames: " + state.bufferedFrameCount);
  1019. GUILayout.HorizontalSlider(state.bufferedFrameCount, 0f, 12f);
  1020. GUILayout.EndHorizontal();
  1021. GUILayout.BeginHorizontal();
  1022. GUILayout.Label("Free Frames: " + state.freeFrameCount);
  1023. GUILayout.HorizontalSlider(state.freeFrameCount, 0f, 12f);
  1024. GUILayout.EndHorizontal();
  1025. GUILayout.Label("Min Timstamp: " + state.minTimeStamp);
  1026. GUILayout.Label("Max Timstamp: " + state.maxTimeStamp);
  1027. GUILayout.Label("Display Timstamp: " + _mediaPlayer.TextureProducer.GetTextureTimeStamp());
  1028. }
  1029. }
  1030. #endif
  1031. GUILayout.EndVertical();
  1032. }
  1033. }
  1034. }
  1035. #endif