122 lines
4.3 KiB
C#
122 lines
4.3 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Playables;
|
|
using UnityEngine.Video;
|
|
|
|
namespace Timeline.Samples
|
|
{
|
|
// The runtime instance of a video clip player in Timeline.
|
|
public sealed class VideoPlayableBehaviour : PlayableBehaviour
|
|
{
|
|
public VideoPlayer videoPlayer;
|
|
|
|
public double preloadTime;
|
|
public double clipInTime;
|
|
public double startTime;
|
|
|
|
private bool preparing;
|
|
|
|
// Called by the mixer (VideoSchedulerPlayableBehaviour) when this is nearly active to
|
|
// give the video time to load.
|
|
public void PrepareVideo()
|
|
{
|
|
if (videoPlayer == null || videoPlayer.isPrepared || preparing)
|
|
return;
|
|
|
|
videoPlayer.targetCameraAlpha = 0.0f;
|
|
videoPlayer.time = clipInTime;
|
|
videoPlayer.Prepare();
|
|
preparing = true;
|
|
}
|
|
|
|
// Called each frame the clip is active.
|
|
//
|
|
public override void PrepareFrame(Playable playable, FrameData info)
|
|
{
|
|
if (videoPlayer == null)
|
|
return;
|
|
|
|
// Pause or Play the video to match whether the graph is being scrubbed or playing
|
|
// If we need to hold the last frame, this will treat the last frame as a pause
|
|
bool shouldBePlaying = info.evaluationType == FrameData.EvaluationType.Playback;
|
|
if (!videoPlayer.isLooping && playable.GetTime() >= videoPlayer.clip.length)
|
|
shouldBePlaying = false;
|
|
|
|
if (shouldBePlaying)
|
|
{
|
|
// this will use the timeline time to prevent drift
|
|
videoPlayer.timeReference = VideoTimeReference.ExternalTime;
|
|
if (!videoPlayer.isPlaying)
|
|
videoPlayer.Play();
|
|
videoPlayer.externalReferenceTime = playable.GetTime() / videoPlayer.playbackSpeed;
|
|
}
|
|
else
|
|
{
|
|
videoPlayer.timeReference = VideoTimeReference.Freerun;
|
|
if (!videoPlayer.isPaused)
|
|
videoPlayer.Pause();
|
|
SyncVideoToPlayable(playable);
|
|
}
|
|
|
|
// use the accumulated blend value to set the alpha and the audio volume
|
|
videoPlayer.targetCameraAlpha = info.effectiveWeight;
|
|
if (videoPlayer.audioOutputMode == VideoAudioOutputMode.Direct)
|
|
{
|
|
for (ushort i = 0; i < videoPlayer.clip.audioTrackCount; ++i)
|
|
videoPlayer.SetDirectAudioVolume(i, info.effectiveWeight);
|
|
}
|
|
}
|
|
|
|
// Called when the clip becomes active.
|
|
public override void OnBehaviourPlay(Playable playable, FrameData info)
|
|
{
|
|
if (videoPlayer == null)
|
|
return;
|
|
|
|
SyncVideoToPlayable(playable);
|
|
videoPlayer.playbackSpeed = Mathf.Clamp(info.effectiveSpeed, 1 / 10f, 10f);
|
|
videoPlayer.Play();
|
|
preparing = false;
|
|
}
|
|
|
|
// Called when the clip becomes inactive OR the timeline is 'paused'
|
|
public override void OnBehaviourPause(Playable playable, FrameData info)
|
|
{
|
|
if (videoPlayer == null)
|
|
return;
|
|
|
|
preparing = false;
|
|
|
|
// The effective weight will be greater than 0 if the graph is paused and the playhead is still on this clip.
|
|
if (info.effectiveWeight <= 0)
|
|
videoPlayer.Stop();
|
|
else
|
|
videoPlayer.Pause();
|
|
}
|
|
|
|
// Called when the playable is destroyed.
|
|
public override void OnPlayableDestroy(Playable playable)
|
|
{
|
|
if (videoPlayer != null)
|
|
{
|
|
videoPlayer.Stop();
|
|
if (Application.isPlaying)
|
|
Object.Destroy(videoPlayer.gameObject);
|
|
else
|
|
Object.DestroyImmediate(videoPlayer.gameObject);
|
|
}
|
|
}
|
|
|
|
// Syncs the video player time to playable time
|
|
private void SyncVideoToPlayable(Playable playable)
|
|
{
|
|
if (videoPlayer == null || videoPlayer.clip == null)
|
|
return;
|
|
|
|
if (videoPlayer.isLooping)
|
|
videoPlayer.time = playable.GetTime() % videoPlayer.clip.length;
|
|
else
|
|
videoPlayer.time = System.Math.Min(playable.GetTime(), videoPlayer.clip.length);
|
|
}
|
|
}
|
|
}
|