Files
video-record-ue-poc/Source/AudioVideoRecord/SimpleRecorder.h
2026-03-11 11:25:15 +05:30

126 lines
5.2 KiB
C++

// SimpleRecorder.h — In-game gameplay recorder (Video + Audio + Mux)
// Captures the Unreal back buffer & audio submix, pipes to FFmpeg/NVENC.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Sound/SoundSubmix.h"
#include "ISubmixBufferListener.h"
#include "SimpleRecorder.generated.h"
// ─────────────────────────────────────────────────────────────────────
// USimpleRecorder
//
// A UObject-based recorder you can create from Blueprint or C++.
// • StartRecording() — begins video + audio capture
// • StopRecording() — stops capture, writes .wav, muxes final .mp4
//
// Outputs (inside <ProjectDir>/Saved/Recordings/):
// video_only.mp4 — NVENC-encoded H.264
// audio_only.wav — PCM 16-bit
// final_video_with_audio.mp4 — muxed result
// ─────────────────────────────────────────────────────────────────────
UCLASS(BlueprintType)
class AUDIOVIDEORECORD_API USimpleRecorder : public UObject
{
GENERATED_BODY()
public:
USimpleRecorder();
// ── Blueprint-callable API ───────────────────────────────────────
/** Starts capturing video frames and audio. */
UFUNCTION(BlueprintCallable, Category = "SimpleRecorder")
void StartRecording();
/** Stops capturing. Saves audio_only.wav, then muxes the final file. */
UFUNCTION(BlueprintCallable, Category = "SimpleRecorder")
void StopRecording();
/** Returns true while recording is active. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SimpleRecorder")
bool IsRecording() const { return bIsRecording; }
// ── Configurable settings (edit before calling StartRecording) ───
/** If true, capture resolution is auto-detected from the viewport at recording start. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
bool bAutoDetectResolution = true;
/** Capture width in pixels (ignored if bAutoDetectResolution is true). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
int32 CaptureWidth = 1920;
/** Capture height in pixels (ignored if bAutoDetectResolution is true). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
int32 CaptureHeight = 1080;
/** Target frames per second. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
int32 CaptureFPS = 60;
/** Video bitrate string for FFmpeg (e.g. "8M", "12M"). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
FString VideoBitrate = TEXT("8M");
/** Directory where output files are saved (absolute or project-relative). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
FString OutputDirectory;
/** Full path to ffmpeg.exe. If empty we look on the system PATH. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
FString FFmpegPath;
/** The audio submix to record. If null, the engine master submix is used. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SimpleRecorder|Settings")
USoundSubmix* TargetSubmix = nullptr;
// ── Audio submix callback (forwarded from bridge) ──────────────
void OnNewSubmixBuffer(
const USoundSubmix* OwningSubmix,
float* AudioData,
int32 NumSamples,
int32 NumChannels,
const int32 SampleRate,
double AudioClock);
protected:
virtual void BeginDestroy() override;
private:
// ── Internal helpers ─────────────────────────────────────────────
void InitOutputPaths();
FString GetFFmpegExecutable() const;
/** Called every frame on the render thread when the back buffer is ready. */
void OnBackBufferReady(SWindow& SlateWindow, const FTextureRHIRef& BackBuffer);
/** Writes the captured audio buffer to a .wav file. */
void SaveAudioToWav();
/** Runs FFmpeg to mux video_only.mp4 + audio_only.wav → final .mp4. */
void MuxAudioVideo();
// ── State ────────────────────────────────────────────────────────
bool bIsRecording = false;
// Video
FDelegateHandle BackBufferDelegateHandle;
FILE* FFmpegVideoPipe = nullptr;
// Audio — accumulated raw PCM data
TArray<float> AudioBuffer; // interleaved float samples
int32 AudioSampleRate = 48000;
int32 AudioNumChannels = 2;
FCriticalSection AudioBufferCritSection;
// Output file paths (computed once per recording session)
FString VideoFilePath;
FString AudioFilePath;
FString FinalFilePath;
// Submix bridge (UObjects can't be TSharedRef, so we use a bridge)
TSharedPtr<ISubmixBufferListener, ESPMode::ThreadSafe> SubmixBridge;
};