Ideas needed for possible in-game recording options

Working on a game where i need to record designated camera views(1 or more) into respective video files/ image sequence. The camera wont necessarily be the gameplay cam. Ive already managed to do it in the editor using [DeveloperTools->MediaCapture and FileMediaOutput] to preview our hardware constraints for the recording. But I’m looking for a more in game solution that can be kept after packaging.

I have tried using MovieSceneCapture | Unreal Engine Documentation
from code. But it crashes on calling initialise()

if (GEngine->GameViewport && GEngine->GameViewport->GetGameViewportWidget())
{
	scene_viewport = MakeShareable(new FSceneViewport(GEngine->GameViewport, GEngine->GameViewport->GetGameViewportWidget()));
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("SceneViewport Set"));
}
else
{
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Failed Setting SceneViewport"));
}

if (GEngine)
{
	MSC->Initialize(scene_viewport, GPlayInEditorID);
	MSC->StartWarmup();
}

Any ideas outside of the above module is also welcome :slight_smile:

Posting here cause having issues logging into answerhub

I cannot help you directly, but you essentially just need to figure out how to write to a texture. And then create a texture of every frame the camera sees. Browsing the docs I found this: Render to Texture Toolset Setup | Unreal Engine Documentation

Sorry I couldn’t be more specific.

(Also I am sure there are better ways. But I have implemented something similar to this in Unity C# before and it worked well)

I haven’t yet looked into recording in game footage options, but after seeing this thread you have peaked my interests :upside_down_face:

I will be diving into this as much as I can, because its a really powerful feature to have.

After a quick search, it seems that Unreal already has a pretty intuitive replay/ recording system. I’m taking a look at this Replay System Tutorial. It looks promising.

Thank you for the fast the replies.

I had initially done a render to texture using a camera view and saving it on every tick. But this was without the toolset you have mentioned. My method would basically freeze the gameplay fps to almost 1. I will try out your recommendation and update here.

Hey thanks for this one, we currently already have a recorder and playback system similar to this in the project, but the replay section broke during a version change. Maybe I can use this to go through it and figure out what needs changing. Will update when Ive tried it out. Thank you.

I started to take a look at the engine code for UMoveSceneCampture. Honestly, this seems like the perfect solution to what you want to achieve.

Do you have a copy of the stack trace, logs, more info when Initialize fails?

Honestly this is the point where my inexperience fails me. My work in debugging UE project code so far has been during project building in vs.

I know there is options for displaying the call stack during a crash, but I don’t think I have it enabled.

All I had on the UE console during the crash was the file name and line number where I called initialise.

Open to suggestions on this obviously.

And that is the exact reason I chose this module. Ill be back on this tomorrow, will try to setup the stack trace and post it here. Thank you.

I’m going to play around with it today and let you know if I find any success.

I was able to get the UMovieSceneCapture working on my end, so I believe I should be able to help you figure out what’s going wrong on your end.

With just the information that you provided that UMovieSceneCapture::Initialize is causing the project to crash, I would guess that the problem is that scene_vieport is null and you’re passing that into Initialize.

Here is a function I am calling in an actor component class I created that gets created in my player controller class. I’m calling InitializeSceneCapture in UMyActorComponent::BeginPlay.

void UMyActorComponent::InitializeSceneCapture()
{
    const UWorld* World = this->GetWorld();

    if (!World)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: World was invalid"));

        return;
    }

    UGameViewportClient* GameViewportClient = World->GetGameViewport();

    if (!GameViewportClient)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: Unable to get GameViewport"));

        return;
    }

    TSharedPtr<SViewport> GameViewportWidget = GameViewportClient->GetGameViewportWidget();

    if (!GameViewportWidget)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: Unable to get GameViewport widget"));

        return;
    }

    TSharedPtr<FSceneViewport> SceneViewportPtr = MakeShareable(new FSceneViewport(GameViewportClient, GameViewportWidget));

    if (!SceneViewportPtr)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: Unable to create pointer to TSharedPtr<FSceneViewport>"));

        return;
    }

    if (!this->MovieSceneCapture)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: MovieSceneCapture was invalid"));

        return;
    }

    this->InitializeSceneCaptureSettings();

    this->MovieSceneCapture->Initialize(SceneViewportPtr, 0);
    this->MovieSceneCapture->StartWarmup();
    this->MovieSceneCapture->StartCapture();
}

This is working for me, so I think

scene_viewport here is most likely null and that’s causing UMovieSceneCapture::Initialize to crash.

1 Like

Hey thanks for working on this today, yes it could be that the viewport ptr is null, but I do have it within if statements to check whether it can be created. Ill go back in tomorrow and try it out again.

Will update when I get something, also did it make a difference when u gave PIEID as 0 vs GPlayInEditorID? I had tried it with 1 and the GPlayInEditorID

@sambyte61 could you let me know what did you set in this function? As far as i understood, initialise itself would set everything.

#include "MyActorComponent.h"

UMyActorComponent::UMyActorComponent()
{
    this->MovieSceneCapture = CreateDefaultSubobject<UMovieSceneCapture>("MovieSceneCapture");

    if (!this->MovieSceneCapture)
    {
        UE_LOG(LogMyActorComponent, Fatal, TEXT("UMyActorComponent: MovieSceneCapture failed to be created"));
    }

    this->MovieSceneCapture->bUseSeparateProcess = true;

}

void UMyActorComponent::BeginPlay()
{
    Super::BeginPlay();

    this->InitializeSceneCapture();
}

void UMyActorComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    if (this->MovieSceneCapture)
    {
        this->MovieSceneCapture->Finalize();
    }
    else
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("EndPlay: MovieSceneCapture was invalid"));
    }

    Super::EndPlay(EndPlayReason);
}

void UMyActorComponent::InitializeSceneCaptureSettings()
{
    APlayerController* PlayerController = Cast<APlayerController>(this->GetOwner());

    if (!PlayerController)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCaptureSettings: Could not cast owner's controller to APlayerController"));

        return;
    }

    int32 ViewportSizeX = 0;
    int32 ViewportSizeY = 0;

    PlayerController->GetViewportSize(ViewportSizeX, ViewportSizeY);

    if (!this->MovieSceneCapture)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCaptureSettings: MovieSceneCapture was invalid"));

        return;
    }

    const TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin("MyPlugin");

    if (!Plugin)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCaptureSettings: Could not find plugin"));

        return;
    }

    FDirectoryPath PluginDirectoryPath = FDirectoryPath();
    PluginDirectoryPath.Path           = Plugin->GetContentDir();

    this->MovieSceneCapture->Settings.OutputDirectory          = PluginDirectoryPath;
    this->MovieSceneCapture->Settings.bOverwriteExisting       = false;
    this->MovieSceneCapture->Settings.bUseRelativeFrameNumbers = false;
    this->MovieSceneCapture->Settings.HandleFrames             = 60;
    this->MovieSceneCapture->Settings.bEnableTextureStreaming  = true;
    this->MovieSceneCapture->Settings.Resolution               = FCaptureResolution(ViewportSizeX, ViewportSizeY);
    this->MovieSceneCapture->Settings.bAllowMovement           = true;
    this->MovieSceneCapture->Settings.bAllowTurning            = true;
    this->MovieSceneCapture->Settings.bShowPlayer              = true;
    this->MovieSceneCapture->Settings.bShowHUD                 = true;
}

void UMyActorComponent::InitializeSceneCapture()
{
    const UWorld* World = this->GetWorld();

    if (!World)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: World was invalid"));

        return;
    }

    UGameViewportClient* GameViewportClient = World->GetGameViewport();

    if (!GameViewportClient)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: Unable to get GameViewport"));

        return;
    }

    TSharedPtr<SViewport> GameViewportWidget = GameViewportClient->GetGameViewportWidget();

    if (!GameViewportWidget)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: Unable to get GameViewport widget"));

        return;
    }

    TSharedPtr<FSceneViewport> SceneViewportPtr = MakeShareable(new FSceneViewport(GameViewportClient, GameViewportWidget));

    if (!SceneViewportPtr)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: Unable to create pointer to TSharedPtr<FSceneViewport>"));

        return;
    }

    if (!this->MovieSceneCapture)
    {
        UE_LOG(LogMyActorComponent, Error, TEXT("InitializeSceneCapture: MovieSceneCapture was invalid"));

        return;
    }

    this->InitializeSceneCaptureSettings();

    this->MovieSceneCapture->Initialize(SceneViewportPtr, 0);
    this->MovieSceneCapture->StartWarmup();
    this->MovieSceneCapture->StartCapture();
}
1 Like