269 lines
9.5 KiB
C#
269 lines
9.5 KiB
C#
|
using UnityEngine;
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Copyright 2015-2022 RenderHeads Ltd. All rights reserved.
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
|
|||
|
namespace RenderHeads.Media.AVProVideo
|
|||
|
{
|
|||
|
public partial class MediaPlayer : MonoBehaviour
|
|||
|
{
|
|||
|
|
|||
|
#region Events
|
|||
|
|
|||
|
// Event state
|
|||
|
private bool _eventFired_MetaDataReady = false;
|
|||
|
private bool _eventFired_ReadyToPlay = false;
|
|||
|
private bool _eventFired_Started = false;
|
|||
|
private bool _eventFired_FirstFrameReady = false;
|
|||
|
private bool _eventFired_FinishedPlaying = false;
|
|||
|
private bool _eventState_PlaybackBuffering = false;
|
|||
|
private bool _eventState_PlaybackSeeking = false;
|
|||
|
private bool _eventState_PlaybackStalled = false;
|
|||
|
private int _eventState_PreviousWidth = 0;
|
|||
|
private int _eventState_PreviousHeight = 0;
|
|||
|
private int _previousSubtitleIndex = -1;
|
|||
|
private bool _finishedFrameOpenCheck = false;
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
public static MediaPlayerLoadEvent InternalMediaLoadedEvent = new MediaPlayerLoadEvent();
|
|||
|
#endif
|
|||
|
|
|||
|
private void ResetEvents()
|
|||
|
{
|
|||
|
_eventFired_MetaDataReady = false;
|
|||
|
_eventFired_ReadyToPlay = false;
|
|||
|
_eventFired_Started = false;
|
|||
|
_eventFired_FirstFrameReady = false;
|
|||
|
_eventFired_FinishedPlaying = false;
|
|||
|
_eventState_PlaybackBuffering = false;
|
|||
|
_eventState_PlaybackSeeking = false;
|
|||
|
_eventState_PlaybackStalled = false;
|
|||
|
_eventState_PreviousWidth = 0;
|
|||
|
_eventState_PreviousHeight = 0;
|
|||
|
_previousSubtitleIndex = -1;
|
|||
|
_finishedFrameOpenCheck = false;
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateEvents()
|
|||
|
{
|
|||
|
if (_events != null && _controlInterface != null && _events.HasListeners())
|
|||
|
{
|
|||
|
//NOTE: Fixes a bug where the event was being fired immediately, so when a file is opened, the finishedPlaying fired flag gets set but
|
|||
|
//is then set to true immediately afterwards due to the returned value
|
|||
|
_finishedFrameOpenCheck = false;
|
|||
|
if (IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying))
|
|||
|
{
|
|||
|
if (FireEventIfPossible(MediaPlayerEvent.EventType.FinishedPlaying, _eventFired_FinishedPlaying))
|
|||
|
{
|
|||
|
_eventFired_FinishedPlaying = !_finishedFrameOpenCheck;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Reset some event states that can reset during playback
|
|||
|
{
|
|||
|
// Keep track of whether the Playing state has changed
|
|||
|
if (_eventFired_Started && IsHandleEvent(MediaPlayerEvent.EventType.Started) &&
|
|||
|
_controlInterface != null && !_controlInterface.IsPlaying() && !_controlInterface.IsSeeking())
|
|||
|
{
|
|||
|
// Playing has stopped
|
|||
|
_eventFired_Started = false;
|
|||
|
}
|
|||
|
|
|||
|
// NOTE: We check _controlInterface isn't null in case the scene is unloaded in response to the FinishedPlaying event
|
|||
|
if (_eventFired_FinishedPlaying && IsHandleEvent(MediaPlayerEvent.EventType.FinishedPlaying) &&
|
|||
|
_controlInterface != null && _controlInterface.IsPlaying() && !_controlInterface.IsFinished())
|
|||
|
{
|
|||
|
bool reset = true;
|
|||
|
#if UNITY_EDITOR_WIN || (!UNITY_EDITOR && (UNITY_STANDALONE_WIN || UNITY_WSA))
|
|||
|
reset = false;
|
|||
|
if (_infoInterface.HasVideo())
|
|||
|
{
|
|||
|
// Some streaming HLS/Dash content don't provide a frame rate
|
|||
|
if (_infoInterface.GetVideoFrameRate() > 0f)
|
|||
|
{
|
|||
|
// Don't reset if within a frame of the end of the video, important for time > duration workaround
|
|||
|
float secondsPerFrame = 1f / _infoInterface.GetVideoFrameRate();
|
|||
|
if (_infoInterface.GetDuration() - _controlInterface.GetCurrentTime() > secondsPerFrame)
|
|||
|
{
|
|||
|
reset = true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Just check if we're not beyond the duration
|
|||
|
if (_controlInterface.GetCurrentTime() < _infoInterface.GetDuration())
|
|||
|
{
|
|||
|
reset = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// For audio only media just check if we're not beyond the duration
|
|||
|
if (_controlInterface.GetCurrentTime() < _infoInterface.GetDuration())
|
|||
|
{
|
|||
|
reset = true;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
if (reset)
|
|||
|
{
|
|||
|
//Debug.Log("Reset");
|
|||
|
_eventFired_FinishedPlaying = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Events that can only fire once
|
|||
|
{
|
|||
|
_eventFired_MetaDataReady = FireEventIfPossible(MediaPlayerEvent.EventType.MetaDataReady, _eventFired_MetaDataReady);
|
|||
|
_eventFired_ReadyToPlay = FireEventIfPossible(MediaPlayerEvent.EventType.ReadyToPlay, _eventFired_ReadyToPlay);
|
|||
|
_eventFired_Started = FireEventIfPossible(MediaPlayerEvent.EventType.Started, _eventFired_Started);
|
|||
|
_eventFired_FirstFrameReady = FireEventIfPossible(MediaPlayerEvent.EventType.FirstFrameReady, _eventFired_FirstFrameReady);
|
|||
|
}
|
|||
|
|
|||
|
// Events that can fire multiple times
|
|||
|
{
|
|||
|
// Subtitle changing
|
|||
|
if (FireEventIfPossible(MediaPlayerEvent.EventType.SubtitleChange, false))
|
|||
|
{
|
|||
|
_previousSubtitleIndex = _subtitlesInterface.GetSubtitleIndex();
|
|||
|
}
|
|||
|
|
|||
|
// Resolution changing
|
|||
|
if (FireEventIfPossible(MediaPlayerEvent.EventType.ResolutionChanged, false))
|
|||
|
{
|
|||
|
_eventState_PreviousWidth = _infoInterface.GetVideoWidth();
|
|||
|
_eventState_PreviousHeight = _infoInterface.GetVideoHeight();
|
|||
|
}
|
|||
|
|
|||
|
// Stalling
|
|||
|
if (IsHandleEvent(MediaPlayerEvent.EventType.Stalled))
|
|||
|
{
|
|||
|
bool newState = _infoInterface.IsPlaybackStalled();
|
|||
|
if (newState != _eventState_PlaybackStalled)
|
|||
|
{
|
|||
|
_eventState_PlaybackStalled = newState;
|
|||
|
|
|||
|
var newEvent = _eventState_PlaybackStalled ? MediaPlayerEvent.EventType.Stalled : MediaPlayerEvent.EventType.Unstalled;
|
|||
|
FireEventIfPossible(newEvent, false);
|
|||
|
}
|
|||
|
}
|
|||
|
// Seeking
|
|||
|
if (IsHandleEvent(MediaPlayerEvent.EventType.StartedSeeking))
|
|||
|
{
|
|||
|
bool newState = _controlInterface.IsSeeking();
|
|||
|
if (newState != _eventState_PlaybackSeeking)
|
|||
|
{
|
|||
|
_eventState_PlaybackSeeking = newState;
|
|||
|
|
|||
|
var newEvent = _eventState_PlaybackSeeking ? MediaPlayerEvent.EventType.StartedSeeking : MediaPlayerEvent.EventType.FinishedSeeking;
|
|||
|
FireEventIfPossible(newEvent, false);
|
|||
|
}
|
|||
|
}
|
|||
|
// Buffering
|
|||
|
if (IsHandleEvent(MediaPlayerEvent.EventType.StartedBuffering))
|
|||
|
{
|
|||
|
bool newState = _controlInterface.IsBuffering();
|
|||
|
if (newState != _eventState_PlaybackBuffering)
|
|||
|
{
|
|||
|
_eventState_PlaybackBuffering = newState;
|
|||
|
|
|||
|
var newEvent = _eventState_PlaybackBuffering ? MediaPlayerEvent.EventType.StartedBuffering : MediaPlayerEvent.EventType.FinishedBuffering;
|
|||
|
FireEventIfPossible(newEvent, false);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected bool IsHandleEvent(MediaPlayerEvent.EventType eventType)
|
|||
|
{
|
|||
|
return ((uint)_eventMask & (1 << (int)eventType)) != 0;
|
|||
|
}
|
|||
|
|
|||
|
private bool FireEventIfPossible(MediaPlayerEvent.EventType eventType, bool hasFired)
|
|||
|
{
|
|||
|
if (CanFireEvent(eventType, hasFired))
|
|||
|
{
|
|||
|
#if UNITY_EDITOR
|
|||
|
// Special internal global event, called when media is loaded
|
|||
|
// Currently used by the RecentItem class
|
|||
|
if (eventType == MediaPlayerEvent.EventType.Started)
|
|||
|
{
|
|||
|
string fullPath = GetResolvedFilePath(_mediaPath.Path, _mediaPath.PathType);
|
|||
|
InternalMediaLoadedEvent.Invoke(fullPath);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
hasFired = true;
|
|||
|
_events.Invoke(this, eventType, ErrorCode.None);
|
|||
|
}
|
|||
|
return hasFired;
|
|||
|
}
|
|||
|
|
|||
|
private bool CanFireEvent(MediaPlayerEvent.EventType et, bool hasFired)
|
|||
|
{
|
|||
|
bool result = false;
|
|||
|
if (_events != null && _controlInterface != null && !hasFired && IsHandleEvent(et))
|
|||
|
{
|
|||
|
switch (et)
|
|||
|
{
|
|||
|
case MediaPlayerEvent.EventType.FinishedPlaying:
|
|||
|
result = (!_controlInterface.IsLooping() && _controlInterface.CanPlay() && _controlInterface.IsFinished());
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.MetaDataReady:
|
|||
|
result = (_controlInterface.HasMetaData());
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.FirstFrameReady:
|
|||
|
// [MOZ 20/1/21] Removed HasMetaData check as preventing the event from being triggered on (i|mac|tv)OS
|
|||
|
result = (_textureInterface != null && _controlInterface.CanPlay() /*&& _controlInterface.HasMetaData()*/ && _textureInterface.GetTextureFrameCount() > 0);
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.ReadyToPlay:
|
|||
|
result = (!_controlInterface.IsPlaying() && _controlInterface.CanPlay() && !_autoPlayOnStart);
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.Started:
|
|||
|
result = (_controlInterface.IsPlaying());
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.SubtitleChange:
|
|||
|
{
|
|||
|
result = (_previousSubtitleIndex != _subtitlesInterface.GetSubtitleIndex());
|
|||
|
if (!result)
|
|||
|
{
|
|||
|
result = _baseMediaPlayer.InternalIsChangedTextCue();
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
case MediaPlayerEvent.EventType.Stalled:
|
|||
|
result = _infoInterface.IsPlaybackStalled();
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.Unstalled:
|
|||
|
result = !_infoInterface.IsPlaybackStalled();
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.StartedSeeking:
|
|||
|
result = _controlInterface.IsSeeking();
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.FinishedSeeking:
|
|||
|
result = !_controlInterface.IsSeeking();
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.StartedBuffering:
|
|||
|
result = _controlInterface.IsBuffering();
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.FinishedBuffering:
|
|||
|
result = !_controlInterface.IsBuffering();
|
|||
|
break;
|
|||
|
case MediaPlayerEvent.EventType.ResolutionChanged:
|
|||
|
result = (_infoInterface != null && (_eventState_PreviousWidth != _infoInterface.GetVideoWidth() || _eventState_PreviousHeight != _infoInterface.GetVideoHeight()));
|
|||
|
break;
|
|||
|
default:
|
|||
|
Debug.LogWarning("[AVProVideo] Unhandled event type");
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
#endregion // Events
|
|||
|
|
|||
|
}
|
|||
|
}
|