Files
UnrealEngine/Engine/Source/Runtime/MovieScene/Private/MovieSceneTimeController.cpp
2025-05-18 13:04:45 +08:00

246 lines
8.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MovieSceneTimeController.h"
#include "Engine/World.h"
#include "Engine/Engine.h"
#include "Engine/TimecodeProvider.h"
#include "AudioDevice.h"
#include "Misc/App.h"
static TAutoConsoleVariable<int32> CVarSequencerRelativeTimecodeSmoothing(
TEXT("Sequencer.RelativeTimecodeSmoothing"),
1,
TEXT("If nonzero, accumulate with platform time since when the timecodes were equal."),
ECVF_Default);
static TAutoConsoleVariable<float> CVarSequencerSecondsPerFrame(
TEXT("Sequencer.SecondsPerFrame"),
1.f,
TEXT("Seconds per frame to wait when in play every frame mode."),
ECVF_Default);
void FMovieSceneTimeController::Tick(float DeltaSeconds, float InPlayRate)
{
OnTick(DeltaSeconds, InPlayRate);
}
void FMovieSceneTimeController::Reset(const FQualifiedFrameTime& InStartTime)
{
if (PlaybackStartTime.IsSet())
{
StopPlaying(InStartTime);
StartPlaying(InStartTime);
}
}
void FMovieSceneTimeController::PlayerStatusChanged(EMovieScenePlayerStatus::Type InStatus, const FQualifiedFrameTime& InCurrentTime)
{
if (PlaybackStartTime.IsSet() && InStatus != EMovieScenePlayerStatus::Playing)
{
StopPlaying(InCurrentTime);
}
if (!PlaybackStartTime.IsSet() && InStatus == EMovieScenePlayerStatus::Playing)
{
StartPlaying(InCurrentTime);
}
}
void FMovieSceneTimeController::PrepareToPlay(const FQualifiedFrameTime& InStartTime)
{
UE_LOG(LogMovieScene, VeryVerbose, TEXT("TimeController PrepareToPlay: Sequence started preparing: frame %d, subframe %f. Frame rate: %f fps."), InStartTime.Time.FrameNumber.Value, InStartTime.Time.GetSubFrame(), InStartTime.Rate.AsDecimal());
OnPrepareToPlay(InStartTime);
}
bool FMovieSceneTimeController::IsReadyToPlay()
{
return OnIsReadyToPlay();
}
void FMovieSceneTimeController::StartPlaying(const FQualifiedFrameTime& InStartTime)
{
UE_LOG(LogMovieScene, VeryVerbose, TEXT("TimeController Start: Sequence started: frame %d, subframe %f. Frame rate: %f fps."), InStartTime.Time.FrameNumber.Value, InStartTime.Time.GetSubFrame(), InStartTime.Rate.AsDecimal());
PlaybackStartTime = InStartTime;
OnStartPlaying(InStartTime);
}
void FMovieSceneTimeController::StopPlaying(const FQualifiedFrameTime& InStopTime)
{
UE_LOG(LogMovieScene, VeryVerbose, TEXT("TimeController Start: Sequence stopped."));
OnStopPlaying(InStopTime);
PlaybackStartTime.Reset();
}
FFrameTime FMovieSceneTimeController::RequestCurrentTime(const FQualifiedFrameTime& InCurrentTime, float InPlayRate, FFrameRate InDisplayRate)
{
DisplayRate = InDisplayRate;
return OnRequestCurrentTime(InCurrentTime, InPlayRate);
}
void FMovieSceneTimeController_ExternalClock::OnStartPlaying(const FQualifiedFrameTime& InStartTime)
{
ClockStartTime = ClockLastUpdateTime = GetCurrentTime();
}
void FMovieSceneTimeController_ExternalClock::OnStopPlaying(const FQualifiedFrameTime& InStopTime)
{
ClockLastUpdateTime.Reset();
ClockStartTime.Reset();
AccumulatedDilation = 0.0;
}
FFrameTime FMovieSceneTimeController_ExternalClock::OnRequestCurrentTime(const FQualifiedFrameTime& InCurrentTime, float InPlayRate)
{
TOptional<FQualifiedFrameTime> StartTimeIfPlaying = GetPlaybackStartTime();
if (!StartTimeIfPlaying.IsSet())
{
return InCurrentTime.Time;
}
double StartedTime = ClockStartTime.GetValue();
double CurrentTime = GetCurrentTime();
double UndilatedDeltaTime = (CurrentTime - StartedTime);
AccumulatedDilation += (InPlayRate - 1.0) * (CurrentTime - ClockLastUpdateTime.GetValue());
ClockLastUpdateTime = CurrentTime;
double CurrentSequenceTimeSeconds = UndilatedDeltaTime + AccumulatedDilation;
FFrameTime StartTime = StartTimeIfPlaying->ConvertTo(InCurrentTime.Rate);
FFrameTime NewTime = StartTime + CurrentSequenceTimeSeconds * InCurrentTime.Rate;
#if !NO_LOGGING
UE_LOG(LogMovieScene, VeryVerbose, TEXT("TimeController Clock Start Time: %f, Clock Now: %f, Dilation Offset: %f, Sequence Start Time: frame %d, subframe %f, Sequence Offset Seconds: %f, Sequence Now: frame %d, subframe %f"),
StartedTime, CurrentTime, AccumulatedDilation, StartTime.FrameNumber.Value, StartTime.GetSubFrame(), CurrentSequenceTimeSeconds, NewTime.FrameNumber.Value, NewTime.GetSubFrame());
#endif
return NewTime;
}
double FMovieSceneTimeController_PlatformClock::GetCurrentTime() const
{
return FPlatformTime::Seconds();
}
double FMovieSceneTimeController_AudioClock::GetCurrentTime() const
{
FAudioDevice* AudioDevice = GEngine ? GEngine->GetMainAudioDevice().GetAudioDevice() : nullptr;
return AudioDevice ? AudioDevice->GetInterpolatedAudioClock() : FPlatformTime::Seconds();
}
void FMovieSceneTimeController_RelativeTimecodeClock::OnStopPlaying(const FQualifiedFrameTime& InStopTime)
{
FMovieSceneTimeController_ExternalClock::OnStopPlaying(InStopTime);
AccumulatedFrameTime = 0;
LastCurrentFrameTime.Reset();
TimeSinceCurrentFrameTime.Reset();
}
double FMovieSceneTimeController_RelativeTimecodeClock::GetCurrentTime() const
{
const TOptional<FQualifiedFrameTime> CurrentFrameTime = FApp::GetCurrentFrameTime();
if (CurrentFrameTime.IsSet())
{
return CurrentFrameTime.GetValue().AsSeconds() + AccumulatedFrameTime;
}
else
{
return FPlatformTime::Seconds();
}
}
FFrameTime FMovieSceneTimeController_RelativeTimecodeClock::OnRequestCurrentTime(const FQualifiedFrameTime& InCurrentTime, float InPlayRate)
{
TOptional<FQualifiedFrameTime> CurrentFrameTime = FApp::GetCurrentFrameTime();
// If the engine tick rate is faster than the timecode, there could be multiple ticks with the same timecode.
// That's not necessarily desirable, so accumulate with platform time since when the timecodes were equal.
AccumulatedFrameTime = 0;
if (CVarSequencerRelativeTimecodeSmoothing->GetInt() && CurrentFrameTime.IsSet())
{
if (LastCurrentFrameTime.IsSet() && LastCurrentFrameTime.GetValue() == CurrentFrameTime.GetValue().Time)
{
if (TimeSinceCurrentFrameTime.IsSet())
{
AccumulatedFrameTime = FPlatformTime::Seconds() - TimeSinceCurrentFrameTime.GetValue();
}
else
{
TimeSinceCurrentFrameTime = FPlatformTime::Seconds();
}
}
else
{
TimeSinceCurrentFrameTime = FPlatformTime::Seconds();
}
LastCurrentFrameTime = CurrentFrameTime.GetValue().Time;
}
return FMovieSceneTimeController_ExternalClock::OnRequestCurrentTime(InCurrentTime, InPlayRate);
}
FFrameTime FMovieSceneTimeController_TimecodeClock::OnRequestCurrentTime(const FQualifiedFrameTime& InCurrentTime, float InPlayRate)
{
if (GEngine && GEngine->GetTimecodeProvider() && GEngine->GetTimecodeProvider()->GetSynchronizationState() == ETimecodeProviderSynchronizationState::Synchronized)
{
FTimecode Timecode = FApp::GetTimecode();
FFrameRate FrameRate = FApp::GetTimecodeFrameRate();
// Convert timecode to raw number of frames at the timecode's framerate.
FFrameNumber FrameNumber = Timecode.ToFrameNumber(FrameRate);
return FFrameRate::TransformTime(FFrameTime(FrameNumber), FrameRate, InCurrentTime.Rate);
}
return FFrameTime(0);
}
void FMovieSceneTimeController_Tick::OnStartPlaying(const FQualifiedFrameTime& InStartTime)
{
CurrentOffsetSeconds = 0.0;
}
void FMovieSceneTimeController_Tick::OnTick(float DeltaSeconds, float InPlayRate)
{
CurrentOffsetSeconds += DeltaSeconds * InPlayRate;
}
FFrameTime FMovieSceneTimeController_Tick::OnRequestCurrentTime(const FQualifiedFrameTime& InCurrentTime, float InPlayRate)
{
TOptional<FQualifiedFrameTime> StartTimeIfPlaying = GetPlaybackStartTime();
if (!StartTimeIfPlaying.IsSet())
{
return InCurrentTime.Time;
}
else
{
FFrameTime StartTime = StartTimeIfPlaying->ConvertTo(InCurrentTime.Rate);
return StartTime + CurrentOffsetSeconds * InCurrentTime.Rate;
}
}
void FMovieSceneTimeController_PlayEveryFrame::OnStartPlaying(const FQualifiedFrameTime& InStartTime)
{
PreviousPlatformTime = FPlatformTime::Seconds();
CurrentTime = InStartTime.Time;
}
FFrameTime FMovieSceneTimeController_PlayEveryFrame::OnRequestCurrentTime(const FQualifiedFrameTime& InCurrentTime, float InPlayRate)
{
double TimeDiff = FPlatformTime::Seconds() - PreviousPlatformTime;
if (TimeDiff > CVarSequencerSecondsPerFrame->GetFloat())
{
// Update current time by moving onto the next frame
FFrameRate CurrentDisplayRate = GetDisplayRate();
FFrameTime OneDisplayFrame = CurrentDisplayRate.AsFrameTime(CurrentDisplayRate.AsInterval());
CurrentTime = CurrentTime + ConvertFrameTime(OneDisplayFrame, CurrentDisplayRate, InCurrentTime.Rate);
PreviousPlatformTime = FPlatformTime::Seconds();
}
return CurrentTime;
}