You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#jira UE-74647
#jira UE-74648 #jira UE-74649 General improvements to the FHighlightRecorder class. Also, it now works in PIE too. #rb Josie.Yang [CL 6628795 by Rui Figueira in Main branch]
This commit is contained in:
+79
-70
@@ -116,6 +116,12 @@ void FHighlightRecorder::Stop()
|
||||
FGameplayMediaEncoder::Get()->UnregisterListener(this);
|
||||
State = EState::Stopped;
|
||||
|
||||
if (BackgroundSaving)
|
||||
{
|
||||
BackgroundSaving->Join();
|
||||
BackgroundSaving.Reset();
|
||||
}
|
||||
|
||||
UE_LOG(HighlightRecorder, Log, TEXT("recording stopped"));
|
||||
}
|
||||
|
||||
@@ -178,7 +184,7 @@ bool FHighlightRecorder::SaveHighlight(const TCHAR* Filename, FDoneCallback InDo
|
||||
|
||||
{
|
||||
CSV_SCOPED_TIMING_STAT(WindowsVideoRecordingSystem, HighlightRecorder_SaveThreadCreation);
|
||||
BackgroundProcessor.Reset(new FThread(TEXT("Highlight Saving"), [this, LocalFilename, MaxDurationSecs]()
|
||||
BackgroundSaving.Reset(new FThread(TEXT("Highlight Saving"), [this, LocalFilename, MaxDurationSecs]()
|
||||
{
|
||||
SaveHighlightInBackground(LocalFilename, MaxDurationSecs);
|
||||
}));
|
||||
@@ -187,6 +193,7 @@ bool FHighlightRecorder::SaveHighlight(const TCHAR* Filename, FDoneCallback InDo
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// the bool result is solely for convenience (CHECK_HR) and is ignored as it's a thread function and
|
||||
// nobody checks it's result. Actual result is notified by the callback.
|
||||
bool FHighlightRecorder::SaveHighlightInBackground(const FString& Filename, double MaxDurationSecs)
|
||||
@@ -195,69 +202,80 @@ bool FHighlightRecorder::SaveHighlightInBackground(const FString& Filename, doub
|
||||
|
||||
double T0 = FPlatformTime::Seconds();
|
||||
|
||||
bool bRes = true;
|
||||
|
||||
TArray<FGameplayMediaEncoderSample> Samples = RingBuffer.GetCopy();
|
||||
|
||||
do // once
|
||||
{
|
||||
if (!InitialiseMp4Writer(Filename))
|
||||
{
|
||||
bRes = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int SampleIndex;
|
||||
FTimespan StartTime;
|
||||
if (!GetSavingStart(Samples, FTimespan::FromSeconds(MaxDurationSecs), SampleIndex, StartTime))
|
||||
{
|
||||
bRes = false;
|
||||
break;
|
||||
}
|
||||
|
||||
checkf(Samples[SampleIndex].IsVideoKeyFrame(), TEXT("t %.3f d %.3f"), Samples[SampleIndex].GetTime().GetTotalSeconds(), Samples[SampleIndex].GetDuration().GetTotalSeconds());
|
||||
|
||||
if (SampleIndex == Samples.Num())
|
||||
{
|
||||
UE_LOG(HighlightRecorder, Error, TEXT("no samples to save to .mp4"));
|
||||
bRes = false;
|
||||
break;
|
||||
}
|
||||
|
||||
UE_LOG(HighlightRecorder, Verbose, TEXT("writting %d samples to .mp4, %.3f s, starting from %.3f s, index %d"), Samples.Num() - SampleIndex, (Samples.Last().GetTime() - StartTime + Samples.Last().GetDuration()).GetTotalSeconds(), StartTime.GetTotalSeconds(), SampleIndex);
|
||||
|
||||
// get samples starting from `StartTime` and push them into Mp4Writer
|
||||
for (; SampleIndex != Samples.Num() && !bStopSaving; ++SampleIndex)
|
||||
{
|
||||
FGameplayMediaEncoderSample& Sample = Samples[SampleIndex];
|
||||
Sample.SetTime(Sample.GetTime() - StartTime);
|
||||
if (!Mp4Writer->Write(Sample))
|
||||
{
|
||||
bRes = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
if (bRes)
|
||||
{
|
||||
if (!Mp4Writer->Finalize())
|
||||
{
|
||||
bRes = false;
|
||||
}
|
||||
}
|
||||
bool bRes = SaveHighlightInBackgroundImpl(Filename, MaxDurationSecs);
|
||||
|
||||
double PassedSecs = FPlatformTime::Seconds() - T0;
|
||||
UE_LOG(HighlightRecorder, Log, TEXT("saving to %s %s, took %.3f msecs"), *Filename, bRes ? TEXT("succeeded") : TEXT("failed"), PassedSecs);
|
||||
|
||||
bSaving = false;
|
||||
UE_LOG(HighlightRecorder, Log, TEXT("saving to %s %s, took %.3f secs"), *Filename, bRes ? TEXT("succeeded") : TEXT("failed"), PassedSecs);
|
||||
|
||||
DoneCallback(bRes);
|
||||
bSaving = false;
|
||||
|
||||
return bRes;
|
||||
}
|
||||
|
||||
bool FHighlightRecorder::InitialiseMp4Writer(const FString& Filename)
|
||||
bool FHighlightRecorder::SaveHighlightInBackgroundImpl(const FString& Filename, double MaxDurationSecs)
|
||||
{
|
||||
TArray<FGameplayMediaEncoderSample> Samples = RingBuffer.GetCopy();
|
||||
|
||||
if (Samples.Num()==0)
|
||||
{
|
||||
UE_LOG(HighlightRecorder, Error, TEXT("no samples to save to .mp4"));
|
||||
return false;
|
||||
}
|
||||
|
||||
int FirstSampleIndex;
|
||||
FTimespan StartTime;
|
||||
if (!GetSavingStart(Samples, FTimespan::FromSeconds(MaxDurationSecs), FirstSampleIndex, StartTime))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have audio, so that if we don't, we don't create an audio track
|
||||
bool bHasAudio = false;
|
||||
for (int Idx = FirstSampleIndex; Idx != Samples.Num(); ++Idx)
|
||||
{
|
||||
if (Samples[Idx].GetType() == EMediaType::Audio)
|
||||
{
|
||||
bHasAudio = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!InitialiseMp4Writer(Filename, bHasAudio))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
checkf(Samples[FirstSampleIndex].IsVideoKeyFrame(), TEXT("t %.3f d %.3f"), Samples[FirstSampleIndex].GetTime().GetTotalSeconds(), Samples[FirstSampleIndex].GetDuration().GetTotalSeconds());
|
||||
|
||||
if (FirstSampleIndex == Samples.Num())
|
||||
{
|
||||
UE_LOG(HighlightRecorder, Error, TEXT("no samples to save to .mp4"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(HighlightRecorder, Verbose, TEXT("writting %d samples to .mp4, %.3f s, starting from %.3f s, index %d"), Samples.Num() - FirstSampleIndex, (Samples.Last().GetTime() - StartTime + Samples.Last().GetDuration()).GetTotalSeconds(), StartTime.GetTotalSeconds(), FirstSampleIndex);
|
||||
|
||||
// get samples starting from `StartTime` and push them into Mp4Writer
|
||||
for (int Idx = FirstSampleIndex; Idx != Samples.Num(); ++Idx)
|
||||
{
|
||||
FGameplayMediaEncoderSample& Sample = Samples[Idx];
|
||||
Sample.SetTime(Sample.GetTime() - StartTime);
|
||||
if (!Mp4Writer->Write(Sample, (Sample.GetType()==EMediaType::Video && bHasAudio) ? 1 : 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Mp4Writer->Finalize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FHighlightRecorder::InitialiseMp4Writer(const FString& Filename, bool bHasAudio)
|
||||
{
|
||||
FString VideoCaptureDir = FPaths::VideoCaptureDir();
|
||||
auto& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
||||
@@ -287,15 +305,12 @@ bool FHighlightRecorder::InitialiseMp4Writer(const FString& Filename)
|
||||
}
|
||||
|
||||
DWORD StreamIndex;
|
||||
if (!Mp4Writer->CreateStream(AudioType, StreamIndex))
|
||||
if (bHasAudio)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StreamIndex != static_cast<decltype(StreamIndex)>(EMediaType::Audio))
|
||||
{
|
||||
UE_LOG(HighlightRecorder, Error, TEXT("Invalid audio stream index: %d"), StreamIndex);
|
||||
return false;
|
||||
if (!Mp4Writer->CreateStream(AudioType, StreamIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TRefCountPtr<IMFMediaType> VideoType;
|
||||
@@ -309,12 +324,6 @@ bool FHighlightRecorder::InitialiseMp4Writer(const FString& Filename)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StreamIndex != static_cast<decltype(StreamIndex)>(EMediaType::Video))
|
||||
{
|
||||
UE_LOG(HighlightRecorder, Error, TEXT("Invalid video stream index: %d"), StreamIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Mp4Writer->Start())
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -74,7 +74,8 @@ public:
|
||||
|
||||
private:
|
||||
bool SaveHighlightInBackground(const FString& Filename, double MaxDurationSecs);
|
||||
bool InitialiseMp4Writer(const FString& Filename);
|
||||
bool SaveHighlightInBackgroundImpl(const FString& Filename, double MaxDurationSecs);
|
||||
bool InitialiseMp4Writer(const FString& Filename, bool bHasAudio);
|
||||
bool GetSavingStart(const TArray<FGameplayMediaEncoderSample>& Samples, FTimespan MaxDuration, int& OutStartIndex, FTimespan& OutStartTime) const;
|
||||
|
||||
// takes into account if we've been paused and shifts current time back to compensate paused state
|
||||
@@ -104,10 +105,9 @@ private:
|
||||
// for how long recording has been paused since it's started
|
||||
FTimespan TotalPausedDuration = 0;
|
||||
|
||||
TUniquePtr<FThread> BackgroundProcessor;
|
||||
TUniquePtr<FThread> BackgroundSaving;
|
||||
FDoneCallback DoneCallback;
|
||||
FThreadSafeBool bSaving = false;
|
||||
FThreadSafeBool bStopSaving = false;
|
||||
|
||||
#pragma region testing
|
||||
public:
|
||||
@@ -136,6 +136,11 @@ public:
|
||||
|
||||
static void StopCmd()
|
||||
{
|
||||
if (!CheckSingleton())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Get()->Stop();
|
||||
delete Singleton;
|
||||
Singleton = nullptr;
|
||||
@@ -143,16 +148,31 @@ public:
|
||||
|
||||
static void PauseCmd()
|
||||
{
|
||||
if (!CheckSingleton())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Get()->Pause(true);
|
||||
}
|
||||
|
||||
static void ResumeCmd()
|
||||
{
|
||||
if (!CheckSingleton())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Get()->Pause(false);
|
||||
}
|
||||
|
||||
static void SaveCmd(const TArray<FString>& Args, UWorld*, FOutputDevice& Output)
|
||||
{
|
||||
if (!CheckSingleton())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Args.Num() > 2)
|
||||
{
|
||||
Output.Logf(ELogVerbosity::Error, TEXT("0-2 parameters expected: Save [filename=\"test.mp4\"] [max_duration_secs= ring buffer duration]"));
|
||||
@@ -182,6 +202,20 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static bool CheckSingleton()
|
||||
{
|
||||
if (Singleton)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(HighlightRecorder, Error, TEXT("HighlightRecorder not initialized."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static FHighlightRecorder* Get()
|
||||
{
|
||||
check(Singleton);
|
||||
|
||||
@@ -37,9 +37,9 @@ bool FWmfMp4Writer::Start()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FWmfMp4Writer::Write(const FGameplayMediaEncoderSample& Sample)
|
||||
bool FWmfMp4Writer::Write(const FGameplayMediaEncoderSample& Sample, DWORD StreamIndex)
|
||||
{
|
||||
CHECK_HR(Writer->WriteSample(static_cast<DWORD>(Sample.GetType()), const_cast<IMFSample*>(Sample.GetSample())));
|
||||
CHECK_HR(Writer->WriteSample(StreamIndex, const_cast<IMFSample*>(Sample.GetSample())));
|
||||
|
||||
UE_LOG(MP4, VeryVerbose, TEXT("stream #%d: time %.3f, duration %.3f%s"), static_cast<int>(Sample.GetType()), Sample.GetTime().GetTotalSeconds(), Sample.GetDuration().GetTotalSeconds(), Sample.IsVideoKeyFrame() ? TEXT(", key-frame") : TEXT(""));
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
bool Initialize(const TCHAR* Filename);
|
||||
bool CreateStream(IMFMediaType* StreamType, DWORD& StreamIndex);
|
||||
bool Start();
|
||||
bool Write(const FGameplayMediaEncoderSample& Sample);
|
||||
bool Write(const FGameplayMediaEncoderSample& Sample, DWORD StreamIndex);
|
||||
bool Finalize();
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user