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

142 lines
3.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Protocols/VideoCaptureProtocol.h"
#include "HAL/FileManager.h"
#include "Misc/CommandLine.h"
#include "Templates/Casts.h"
#include "Misc/FrameRate.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VideoCaptureProtocol)
bool UVideoCaptureProtocol::SetupImpl()
{
#if PLATFORM_UNIX
UE_LOG(LogInit, Warning, TEXT("Writing movies is not currently supported on Linux"));
#endif
return Super::SetupImpl();
}
void UVideoCaptureProtocol::ConditionallyCreateWriter()
{
#if PLATFORM_LINUX
static const TCHAR* Extension = TEXT(".unsupp");
return;
#else
#if PLATFORM_MAC
static const TCHAR* Extension = TEXT(".mov");
#else
static const TCHAR* Extension = TEXT(".avi");
#endif
FString VideoFilename = GenerateFilenameImpl(FFrameMetrics(), Extension);
if (AVIWriters.Num() && VideoFilename == AVIWriters.Last()->Options.OutputFilename)
{
return;
}
EnsureFileWritableImpl(VideoFilename);
FAVIWriterOptions Options;
Options.OutputFilename = MoveTemp(VideoFilename);
Options.CaptureFramerateNumerator = CaptureHost->GetCaptureFrameRate().Numerator;
Options.CaptureFramerateDenominator = CaptureHost->GetCaptureFrameRate().Denominator;
Options.bSynchronizeFrames = CaptureHost->GetCaptureStrategy().ShouldSynchronizeFrames();
Options.Width = InitSettings->DesiredSize.X;
Options.Height = InitSettings->DesiredSize.Y;
if (bUseCompression)
{
Options.CompressionQuality = CompressionQuality / 100.f;
float QualityOverride = 100.f;
if (FParse::Value( FCommandLine::Get(), TEXT( "-MovieQuality=" ), QualityOverride ))
{
Options.CompressionQuality = FMath::Clamp(QualityOverride, 1.f, 100.f) / 100.f;
}
Options.CompressionQuality = FMath::Clamp<float>(Options.CompressionQuality.GetValue(), 0.f, 1.f);
}
AVIWriters.Emplace(FAVIWriter::CreateInstance(Options));
AVIWriters.Last()->Initialize();
#endif
}
struct FVideoFrameData : IFramePayload
{
FFrameMetrics Metrics;
int32 WriterIndex;
};
FFramePayloadPtr UVideoCaptureProtocol::GetFramePayload(const FFrameMetrics& FrameMetrics)
{
ConditionallyCreateWriter();
TSharedRef<FVideoFrameData, ESPMode::ThreadSafe> FrameData = MakeShareable(new FVideoFrameData);
FrameData->Metrics = FrameMetrics;
FrameData->WriterIndex = AVIWriters.Num() - 1;
return FrameData;
}
void UVideoCaptureProtocol::ProcessFrame(FCapturedFrameData Frame)
{
FVideoFrameData* Payload = Frame.GetPayload<FVideoFrameData>();
const int32 WriterIndex = Payload->WriterIndex;
if (WriterIndex >= 0)
{
AVIWriters[WriterIndex]->DropFrames(Payload->Metrics.NumDroppedFrames);
AVIWriters[WriterIndex]->Update(Payload->Metrics.TotalElapsedTime, MoveTemp(Frame.ColorBuffer));
// Finalize previous writers if necessary
for (int32 Index = 0; Index < Payload->WriterIndex; ++Index)
{
TUniquePtr<FAVIWriter>& Writer = AVIWriters[Index];
if (Writer->IsCapturing())
{
Writer->Finalize();
}
}
}
}
void UVideoCaptureProtocol::FinalizeImpl()
{
for (TUniquePtr<FAVIWriter>& Writer : AVIWriters)
{
if (Writer->IsCapturing())
{
Writer->Finalize();
}
}
AVIWriters.Empty();
Super::FinalizeImpl();
}
bool UVideoCaptureProtocol::CanWriteToFileImpl(const TCHAR* InFilename, bool bOverwriteExisting) const
{
// When recording video, if the filename changes (ie due to the shot changing), we create new AVI writers.
// If we're not overwriting existing filenames we need to check if we're already recording a video of that name,
// before we can deem whether we can write to a new file (we can always write to a filename we're already writing to)
if (!bOverwriteExisting)
{
for (const TUniquePtr<FAVIWriter>& Writer : AVIWriters)
{
if (Writer->Options.OutputFilename == InFilename)
{
return true;
}
}
return IFileManager::Get().FileSize(InFilename) == -1;
}
return true;
}