MediaPlayer_Events.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. using UnityEngine;
  2. //-----------------------------------------------------------------------------
  3. // Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
  4. //-----------------------------------------------------------------------------
  5. namespace RenderHeads.Media.AVProVideo
  6. {
  7. public partial class MediaPlayer : MonoBehaviour
  8. {
  9. // Event state
  10. private bool _eventFired_MetaDataReady = false;
  11. private bool _eventFired_ReadyToPlay = false;
  12. private bool _eventFired_Started = false;
  13. private bool _eventFired_FirstFrameReady = false;
  14. private bool _eventFired_FinishedPlaying = false;
  15. private bool _eventState_PlaybackBuffering = false;
  16. private bool _eventState_PlaybackSeeking = false;
  17. private bool _eventState_PlaybackStalled = false;
  18. private int _eventState_PreviousWidth = 0;
  19. private int _eventState_PreviousHeight = 0;
  20. private int _previousSubtitleIndex = -1;
  21. private bool _finishedFrameOpenCheck = false;
  22. private bool _eventState_Paused = false;
  23. #if UNITY_EDITOR
  24. public static MediaPlayerLoadEvent InternalMediaLoadedEvent = new MediaPlayerLoadEvent();
  25. #endif
  26. private void ResetEvents()
  27. {
  28. _eventFired_MetaDataReady = false;
  29. _eventFired_ReadyToPlay = false;
  30. _eventFired_Started = false;
  31. _eventFired_FirstFrameReady = false;
  32. _eventFired_FinishedPlaying = false;
  33. _eventState_PlaybackBuffering = false;
  34. _eventState_PlaybackSeeking = false;
  35. _eventState_PlaybackStalled = false;
  36. _eventState_PreviousWidth = 0;
  37. _eventState_PreviousHeight = 0;
  38. _previousSubtitleIndex = -1;
  39. _finishedFrameOpenCheck = false;
  40. }
  41. private void CheckAndClearStartedAndFinishedEvents()
  42. {
  43. //NOTE: Fixes a bug where the event was being fired immediately, so when a file is opened, the finishedPlaying fired flag gets set but
  44. //is then set to true immediately afterwards due to the returned value
  45. _finishedFrameOpenCheck = false;
  46. if (IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying))
  47. {
  48. if (FireEventIfPossible(MediaPlayerEvent.EventType.FinishedPlaying, _eventFired_FinishedPlaying))
  49. {
  50. _eventFired_FinishedPlaying = !_finishedFrameOpenCheck;
  51. }
  52. }
  53. if (_eventFired_FinishedPlaying &&
  54. IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying) &&
  55. _controlInterface.IsPlaying() &&
  56. !_controlInterface.IsFinished())
  57. {
  58. bool reset = true;
  59. // RJT NOTE: Commented out for now as seems over-aggressive and can lead to freeze conditions as seen in: https://github.com/RenderHeads/UnityPlugin-AVProVideo/issues/1692
  60. // - If we need to reinstate then we'd likely need considerably more tolerance, especially on slower machines
  61. #if false//UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA))
  62. reset = false;
  63. if (_infoInterface.HasVideo())
  64. {
  65. // Some streaming HLS/Dash content don't provide a frame rate
  66. if (_infoInterface.GetVideoFrameRate() > 0f)
  67. {
  68. // Don't reset if within a frame of the end of the video, important for time > duration workaround
  69. float secondsPerFrame = 1f / _infoInterface.GetVideoFrameRate();
  70. if (_infoInterface.GetDuration() - _controlInterface.GetCurrentTime() > secondsPerFrame)
  71. {
  72. reset = true;
  73. }
  74. }
  75. else
  76. {
  77. // Just check if we're not beyond the duration
  78. if (_controlInterface.GetCurrentTime() < _infoInterface.GetDuration())
  79. {
  80. reset = true;
  81. }
  82. }
  83. }
  84. else
  85. {
  86. // For audio only media just check if we're not beyond the duration
  87. if (_controlInterface.GetCurrentTime() < _infoInterface.GetDuration())
  88. {
  89. reset = true;
  90. }
  91. }
  92. #endif
  93. if (reset)
  94. {
  95. //Debug.Log("Reset");
  96. _eventFired_FinishedPlaying = false;
  97. }
  98. }
  99. }
  100. private void HandleOneShotEvents()
  101. {
  102. _eventFired_MetaDataReady = FireEventIfPossible(MediaPlayerEvent.EventType.MetaDataReady, _eventFired_MetaDataReady);
  103. _eventFired_ReadyToPlay = FireEventIfPossible(MediaPlayerEvent.EventType.ReadyToPlay, _eventFired_ReadyToPlay);
  104. _eventFired_Started = FireEventIfPossible(MediaPlayerEvent.EventType.Started, _eventFired_Started);
  105. _eventFired_FirstFrameReady = FireEventIfPossible(MediaPlayerEvent.EventType.FirstFrameReady, _eventFired_FirstFrameReady);
  106. }
  107. private void HandleRecurringEvents()
  108. {
  109. // Subtitle changing
  110. if (FireEventIfPossible(MediaPlayerEvent.EventType.SubtitleChange, false))
  111. {
  112. _previousSubtitleIndex = _subtitlesInterface.GetSubtitleIndex();
  113. }
  114. // Resolution changing
  115. if (FireEventIfPossible(MediaPlayerEvent.EventType.ResolutionChanged, false))
  116. {
  117. _eventState_PreviousWidth = _infoInterface.GetVideoWidth();
  118. _eventState_PreviousHeight = _infoInterface.GetVideoHeight();
  119. }
  120. // Stalling
  121. if (IsHandleEvent(MediaPlayerEvent.EventType.Stalled))
  122. {
  123. bool newState = _infoInterface.IsPlaybackStalled();
  124. if (newState != _eventState_PlaybackStalled)
  125. {
  126. _eventState_PlaybackStalled = newState;
  127. var newEvent = _eventState_PlaybackStalled ? MediaPlayerEvent.EventType.Stalled : MediaPlayerEvent.EventType.Unstalled;
  128. FireEventIfPossible(newEvent, false);
  129. }
  130. }
  131. // Seeking
  132. if (IsHandleEvent(MediaPlayerEvent.EventType.StartedSeeking))
  133. {
  134. bool newState = _controlInterface.IsSeeking();
  135. if (newState != _eventState_PlaybackSeeking)
  136. {
  137. _eventState_PlaybackSeeking = newState;
  138. var newEvent = _eventState_PlaybackSeeking ? MediaPlayerEvent.EventType.StartedSeeking : MediaPlayerEvent.EventType.FinishedSeeking;
  139. FireEventIfPossible(newEvent, false);
  140. }
  141. }
  142. // Buffering
  143. if (IsHandleEvent(MediaPlayerEvent.EventType.StartedBuffering))
  144. {
  145. bool newState = _controlInterface.IsBuffering();
  146. if (newState != _eventState_PlaybackBuffering)
  147. {
  148. _eventState_PlaybackBuffering = newState;
  149. var newEvent = _eventState_PlaybackBuffering ? MediaPlayerEvent.EventType.StartedBuffering : MediaPlayerEvent.EventType.FinishedBuffering;
  150. FireEventIfPossible(newEvent, false);
  151. }
  152. }
  153. // Pausing
  154. if (IsHandleEvent(MediaPlayerEvent.EventType.Paused))
  155. {
  156. bool newState = _controlInterface.IsPaused();
  157. if (newState != _eventState_Paused)
  158. {
  159. _eventState_Paused = newState;
  160. var newEvent = _eventState_Paused ? MediaPlayerEvent.EventType.Paused : MediaPlayerEvent.EventType.Unpaused;
  161. FireEventIfPossible(newEvent, false);
  162. }
  163. }
  164. }
  165. private void UpdateEvents()
  166. {
  167. if (_controlInterface == null)
  168. return;
  169. if (_events == null || !_events.HasListeners())
  170. return;
  171. // Reset some event states that can reset during playback
  172. CheckAndClearStartedAndFinishedEvents();
  173. // Events that can only fire once
  174. HandleOneShotEvents();
  175. // Events that can fire multiple times
  176. HandleRecurringEvents();
  177. }
  178. protected bool IsHandleEvent(MediaPlayerEvent.EventType eventType)
  179. {
  180. return ((uint)_eventMask & (1 << (int)eventType)) != 0;
  181. }
  182. private bool FireEventIfPossible(MediaPlayerEvent.EventType eventType, bool hasFired)
  183. {
  184. if (CanFireEvent(eventType, hasFired))
  185. {
  186. #if UNITY_EDITOR
  187. // Special internal global event, called when media is loaded
  188. // Currently used by the RecentItem class
  189. if (eventType == MediaPlayerEvent.EventType.Started)
  190. {
  191. string fullPath = GetResolvedFilePath(_mediaPath.Path, _mediaPath.PathType);
  192. InternalMediaLoadedEvent.Invoke(fullPath);
  193. }
  194. #endif
  195. hasFired = true;
  196. _events.Invoke(this, eventType, ErrorCode.None);
  197. }
  198. return hasFired;
  199. }
  200. private bool CanFireEvent(MediaPlayerEvent.EventType et, bool hasFired)
  201. {
  202. if (_controlInterface == null)
  203. return false;
  204. if (_events == null)
  205. return false;
  206. if (hasFired)
  207. return false;
  208. if (!IsHandleEvent(et))
  209. return false;
  210. bool result = false;
  211. switch (et)
  212. {
  213. case MediaPlayerEvent.EventType.FinishedPlaying:
  214. result = (!_controlInterface.IsLooping() && _controlInterface.CanPlay() && _controlInterface.IsFinished());
  215. break;
  216. case MediaPlayerEvent.EventType.MetaDataReady:
  217. result = (_controlInterface.HasMetaData());
  218. break;
  219. case MediaPlayerEvent.EventType.FirstFrameReady:
  220. // [MOZ 20/1/21] Removed HasMetaData check as preventing the event from being triggered on (i|mac|tv)OS
  221. result = (_textureInterface != null && _controlInterface.CanPlay() /*&& _controlInterface.HasMetaData()*/ && _textureInterface.GetTextureFrameCount() > 0);
  222. break;
  223. case MediaPlayerEvent.EventType.ReadyToPlay:
  224. result = (!_controlInterface.IsPlaying() && _controlInterface.CanPlay() && !_autoPlayOnStart);
  225. break;
  226. case MediaPlayerEvent.EventType.Started:
  227. result = (_controlInterface.IsPlaying());
  228. break;
  229. case MediaPlayerEvent.EventType.SubtitleChange:
  230. {
  231. result = (_previousSubtitleIndex != _subtitlesInterface.GetSubtitleIndex());
  232. if (!result)
  233. {
  234. result = _baseMediaPlayer.InternalIsChangedTextCue();
  235. }
  236. break;
  237. }
  238. case MediaPlayerEvent.EventType.Stalled:
  239. result = _infoInterface.IsPlaybackStalled();
  240. break;
  241. case MediaPlayerEvent.EventType.Unstalled:
  242. result = !_infoInterface.IsPlaybackStalled();
  243. break;
  244. case MediaPlayerEvent.EventType.StartedSeeking:
  245. result = _controlInterface.IsSeeking();
  246. break;
  247. case MediaPlayerEvent.EventType.FinishedSeeking:
  248. result = !_controlInterface.IsSeeking();
  249. break;
  250. case MediaPlayerEvent.EventType.StartedBuffering:
  251. result = _controlInterface.IsBuffering();
  252. break;
  253. case MediaPlayerEvent.EventType.FinishedBuffering:
  254. result = !_controlInterface.IsBuffering();
  255. break;
  256. case MediaPlayerEvent.EventType.ResolutionChanged:
  257. result = (_infoInterface != null && (_eventState_PreviousWidth != _infoInterface.GetVideoWidth() || _eventState_PreviousHeight != _infoInterface.GetVideoHeight()));
  258. break;
  259. case MediaPlayerEvent.EventType.Paused:
  260. result = _controlInterface.IsPaused();
  261. break;
  262. case MediaPlayerEvent.EventType.Unpaused:
  263. result = !_controlInterface.IsPaused();
  264. break;
  265. default:
  266. Debug.LogWarning("[AVProVideo] Unhandled event type");
  267. break;
  268. }
  269. return result;
  270. }
  271. }
  272. } // namespace RenderHeads.Media.AVProVideo