Skip to main content

What Problem Does This Solve?

ULCKService is your main entry point for LCK functionality:
  • Start/stop/pause/resume recording
  • Configure recording resolution, framerate, and bitrate
  • Control microphone audio capture
  • Take photos
  • Monitor recording state and errors
Instead of interacting with low-level subsystems, you use this high-level service that handles the complexity for you.

When to Use This

Use ULCKService when:
  • Building custom recording UI
  • Implementing record buttons, settings menus
  • Controlling recording from gameplay code
  • Integrating recording into your game flow
Don’t use this directly if: You’re using the default LCK Tablet UI—it already handles everything.

Accessing the Service

From C++

ULCKSubsystem* Subsystem = GetWorld()->GetSubsystem<ULCKSubsystem>();
if (Subsystem)
{
    ULCKService* Service = Subsystem->GetService();
    // Use service...
}

From Blueprint

UFUNCTION(BlueprintCallable, Category = "LCK")
ULCKService* GetLCKService()
{
    if (UWorld* World = GetWorld())
    {
        if (ULCKSubsystem* Subsystem = World->GetSubsystem<ULCKSubsystem>())
        {
            return Subsystem->GetService();
        }
    }
    return nullptr;
}
Blueprint usage: Blueprint Get Service

Recording Methods

StartRecording / StopRecording

// Start recording — returns true if recording started successfully
bool bSuccess = Service->StartRecording();
if (!bSuccess)
{
    UE_LOG(LogLCK, Error, TEXT("Failed to start recording"));
}

// Stop recording — returns void
Service->StopRecording();
MethodReturnsDescription
StartRecording()boolStarts recording. Returns true on success. Only valid when in Idle state.
StopRecording()voidStops the current recording. Only valid when in Recording or Paused state.
For async recording with callbacks and progress tracking, use ULCKRecorderSubsystem::StartRecordingAsync() and StopRecordingAsync() directly. See ULCKRecorderSubsystem for details.

PauseRecording / ResumeRecording

// Pause the current recording
Service->PauseRecording();

// Resume a paused recording
Service->ResumeRecording();

IsRecording / GetCurrentRecordingDuration

// Check if currently recording
bool bIsRecording = Service->IsRecording();

// Get recording duration in seconds
float Duration = Service->GetCurrentRecordingDuration();

// Update UI
if (bIsRecording)
{
    FTimespan Time = FTimespan::FromSeconds(Duration);
    TimerText->SetText(FText::Format(
        LOCTEXT("RecordingTime", "{0}:{1:02}"),
        Time.GetMinutes(),
        Time.GetSeconds()
    ));
}

GetRecordingState

ELCKRecordingState State = Service->GetRecordingState();

switch (State)
{
    case ELCKRecordingState::Idle:
        // Ready to record
        break;
    case ELCKRecordingState::Recording:
        // Currently recording
        break;
    // ...
}

Photo Capture

// Take a photo
Service->TakePhoto();
The photo is captured from the current scene capture component. There is no completion delegate on ULCKService for photo saves.

Recording Settings

ApplyRecordingSettings

Configure resolution, framerate, and bitrate for recordings:
Service->ApplyRecordingSettings(
    1920,   // Width
    1080,   // Height
    30,     // Framerate
    8 << 20, // Video bitrate (8 Mbps)
    256 << 10, // Audio bitrate (256 Kbps)
    48000   // Sample rate (optional, defaults to 48000)
);
ParameterTypeDescription
Widthint32Video width in pixels
Heightint32Video height in pixels
Framerateint32Frames per second
VideoBitrateint32Video bitrate in bits per second
AudioBitrateint32Audio bitrate in bits per second
Samplerateint32Audio sample rate (default: 48000)
Quality and orientation presets (SD, HD, Landscape, Portrait) are managed via ULCKTabletDataModel, not ULCKService. See the Data Model Events section below.

Audio Control

Microphone

// Enable/disable microphone
Service->SetMicrophoneEnabled(true);

// Get current state
bool bMicEnabled = Service->IsMicrophoneEnabled();

// Get microphone audio level (0.0 to 1.0)
float MicLevel = Service->GetCurrentMicrophoneAudioLevel();
Volume indicator example:
void AMicIndicator::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    float Volume = Service->GetCurrentMicrophoneAudioLevel();
    VolumeBar->SetPercent(Volume);

    // Visual feedback
    if (Volume > 0.7f)
    {
        VolumeBar->SetFillColorAndOpacity(FLinearColor::Red);
    }
    else if (Volume > 0.3f)
    {
        VolumeBar->SetFillColorAndOpacity(FLinearColor::Yellow);
    }
    else
    {
        VolumeBar->SetFillColorAndOpacity(FLinearColor::Green);
    }
}
GetCurrentMicrophoneAudioLevel() is not exposed to Blueprint. It is available from C++ only.

Preview Mode

Preview mode (camera output without recording to disk) is managed by ULCKRecorderSubsystem, not ULCKService.
// Access via the recorder subsystem directly
ULCKRecorderSubsystem* Recorder = GetWorld()->GetSubsystem<ULCKRecorderSubsystem>();
Recorder->StartPreview();
Recorder->StopPreview();
See ULCKRecorderSubsystem for details.

Delegates on ULCKService

ULCKService exposes the following delegates directly:
DelegateTypeDescription
OnRecordingErrorFOnRecordingError(FString ErrorMessage, int32 ErrorCode)Fired when a recording error occurs
OnCameraModeChangedFOnCameraModeChanged(UClass* ModeClass)Fired when the camera mode class changes
OnRecordingSaveFinishedFOnRecordingSaveFinished(bool Success)Fired when the async save process completes
OnRecordingSaveProgressFOnRecordingSaveProgress(float Progress)Fired during save with progress from 0.0 to 1.0
Example: Subscribing to delegates
// Error handling
Service->OnRecordingError.AddDynamic(this, &UMyComponent::HandleRecordingError);

// Save completion
Service->OnRecordingSaveFinished.AddDynamic(this, &UMyComponent::HandleSaveFinished);

// Save progress
Service->OnRecordingSaveProgress.AddDynamic(this, &UMyComponent::HandleSaveProgress);

// Handlers
UFUNCTION()
void HandleRecordingError(FString ErrorMessage, int32 ErrorCode)
{
    UE_LOG(LogLCK, Error, TEXT("Recording error %d: %s"), ErrorCode, *ErrorMessage);
}

UFUNCTION()
void HandleSaveFinished(bool bSuccess)
{
    if (bSuccess)
    {
        ShowNotification(TEXT("Recording saved!"));
    }
}

UFUNCTION()
void HandleSaveProgress(float Progress)
{
    ProgressBar->SetPercent(Progress);
}

State Tracking via Data Model

For UI-level state notifications (recording state changes, quality changes, orientation changes, camera mode switches), use ULCKTabletDataModel.

Get the Data Model

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

    // Find tablet in world
    ALCKTablet* Tablet = Cast<ALCKTablet>(
        UGameplayStatics::GetActorOfClass(GetWorld(), ALCKTablet::StaticClass())
    );

    if (Tablet)
    {
        ULCKTabletDataModel* DataModel = Tablet->GetDataModel();

        // Subscribe to recording state changes
        DataModel->OnRecordStateChanged.AddUObject(
            this, &UMyComponent::HandleStateChanged
        );
    }
}
The recording state delegate is OnRecordStateChanged (type FOnRecordStateChange), not OnRecordingStateChanged. These are non-dynamic multicast delegates — use AddUObject or AddLambda, not AddDynamic.

Available Data Model Events

EventDelegate TypeParameterDescription
OnRecordStateChangedFOnRecordStateChangeELCKRecordingStateRecording state changed (Idle, Recording, etc.)
OnTabletCameraModeChangedFOnTabletCameraModeChangedUClass*Camera mode class changed
OnMicStateChangedFOnMicStateChangedELCKMicStateMicrophone state changed (On, Off, No_Access)
OnVideoQualityChangedFOnVideoQualityChangedELCKVideoQualityVideo quality preset changed
OnScreenOrientationChangedFOnScreenOrientationChangedELCKScreenOrientationScreen orientation changed
OnMicLevelChangedFOnMicLevelChangedfloatMicrophone audio level updated
Example: State-driven UI
void URecordingUI::HandleStateChanged(ELCKRecordingState NewState)
{
    switch (NewState)
    {
        case ELCKRecordingState::Idle:
            RecordButton->SetText(FText::FromString("Start Recording"));
            RecordButton->SetIsEnabled(true);
            ProgressPanel->SetVisibility(ESlateVisibility::Collapsed);
            break;

        case ELCKRecordingState::Recording:
            RecordButton->SetText(FText::FromString("Stop Recording"));
            RecordButton->SetIsEnabled(true);
            RecordingIndicator->SetVisibility(ESlateVisibility::Visible);
            break;

        case ELCKRecordingState::Saving:
            RecordButton->SetIsEnabled(false);
            ProgressPanel->SetVisibility(ESlateVisibility::Visible);
            StatusText->SetText(FText::FromString("Saving..."));
            break;

        case ELCKRecordingState::Error:
            RecordButton->SetIsEnabled(true);
            ShowErrorDialog();
            break;
    }
}

Streaming Methods

ULCKService also provides streaming functionality when the streaming feature is available:
MethodReturnsDescription
IsStreamingAvailable()boolWhether the streaming feature plugin is loaded
StartLogin()voidBegin the streaming login/pairing flow
CancelLogin()voidCancel an in-progress login
Logout()voidLog out of the streaming service
StartStreaming()boolStart streaming; returns true on success
StopStreaming()voidStop the current stream
IsStreaming()boolWhether currently streaming
IsAuthenticated()boolWhether logged in to the streaming service
LaunchHub()voidLaunch the LIV Hub companion app
IsHubInstalled()boolWhether the LIV Hub app is installed
GetLastLogoutReason()FStringReason for the last logout
GetStreamingTargetName()FStringName of the current streaming target

Streaming Delegates

These are non-dynamic multicast delegates (use AddUObject or AddLambda):
DelegateTypeDescription
OnStreamingPairingCodeFOnStreamingPairingCode(const FString&)Pairing code received during login
OnStreamingAuthenticatedFOnStreamingAuthenticated()Successfully authenticated
OnStreamingStartedFOnStreamingStarted()Streaming session started
OnStreamingStoppedFOnStreamingStopped()Streaming session stopped
OnStreamingErrorFOnStreamingError(const FString&)Streaming error occurred
OnStreamingLoggedOutFOnStreamingLoggedOut()Logged out of streaming service
OnStreamingConfigChangedFOnStreamingConfigChanged()Streaming configuration changed

Complete Example: Recording Manager

UCLASS()
class URecordingManager : public UActorComponent
{
    GENERATED_BODY()

protected:
    UPROPERTY()
    ULCKService* Service;

    UPROPERTY()
    ULCKTabletDataModel* DataModel;

public:
    virtual void BeginPlay() override
    {
        Super::BeginPlay();

        // Get service
        Service = GetLCKService();
        if (!Service)
        {
            UE_LOG(LogLCK, Error, TEXT("LCK Service not available"));
            return;
        }

        // Get data model for state tracking
        ALCKTablet* Tablet = FindTablet();
        if (Tablet)
        {
            DataModel = Tablet->GetDataModel();
            DataModel->OnRecordStateChanged.AddUObject(
                this, &URecordingManager::OnStateChanged
            );
        }

        // Subscribe to service events
        Service->OnRecordingSaveFinished.AddDynamic(
            this, &URecordingManager::OnSaveFinished
        );
        Service->OnRecordingError.AddDynamic(
            this, &URecordingManager::OnError
        );
    }

    UFUNCTION(BlueprintCallable)
    void ToggleRecording()
    {
        if (Service->IsRecording())
        {
            Service->StopRecording();
        }
        else
        {
            bool bSuccess = Service->StartRecording();
            if (!bSuccess)
            {
                UE_LOG(LogLCK, Error, TEXT("Failed to start recording"));
            }
        }
    }

    void OnStateChanged(ELCKRecordingState NewState)
    {
        // Update UI based on state
        BroadcastStateChange(NewState);
    }

    UFUNCTION()
    void OnSaveFinished(bool bSuccess)
    {
        if (bSuccess)
        {
            ShowNotification(TEXT("Recording saved!"));
        }
        else
        {
            ShowError(TEXT("Failed to save recording"));
        }
    }

    UFUNCTION()
    void OnError(FString ErrorMessage, int32 ErrorCode)
    {
        UE_LOG(LogLCK, Error, TEXT("Recording error %d: %s"), ErrorCode, *ErrorMessage);
        ShowErrorDialog(ErrorMessage);
    }

private:
    ULCKService* GetLCKService()
    {
        if (UWorld* World = GetWorld())
        {
            if (ULCKSubsystem* Subsystem = World->GetSubsystem<ULCKSubsystem>())
            {
                return Subsystem->GetService();
            }
        }
        return nullptr;
    }

    ALCKTablet* FindTablet()
    {
        return Cast<ALCKTablet>(
            UGameplayStatics::GetActorOfClass(GetWorld(), ALCKTablet::StaticClass())
        );
    }
};

Key Takeaways

Get via ULCKSubsystem — Don’t create or cache manually
Check IsRecording() — Before changing settings or starting
Use DataModel for state events — Subscribe to OnRecordStateChanged on ULCKTabletDataModel
Preview lives on ULCKRecorderSubsystem — Not on ULCKService
Quality and orientation are on DataModel — Use ULCKTabletDataModel to change presets