> ## Documentation Index
> Fetch the complete documentation index at: https://docs.liv.tv/llms.txt
> Use this file to discover all available pages before exploring further.

# ULCKService (Unreal)

> Main public-facing service for LCK recording, photo capture, and audio management in Unreal Engine.

## 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++

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

### From Blueprint

```cpp theme={null}
UFUNCTION(BlueprintCallable, Category = "LCK")
ULCKService* GetLCKService()
{
    if (UWorld* World = GetWorld())
    {
        if (ULCKSubsystem* Subsystem = World->GetSubsystem<ULCKSubsystem>())
        {
            return Subsystem->GetService();
        }
    }
    return nullptr;
}
```

**Blueprint usage:**

<img src="https://mintlify.s3.us-west-1.amazonaws.com/liv/images/blueprint-get-service.png" alt="Blueprint Get Service" />

***

## Recording Methods

### StartRecording / StopRecording

```cpp theme={null}
// 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();
```

| Method             | Returns | Description                                                                |
| ------------------ | ------- | -------------------------------------------------------------------------- |
| `StartRecording()` | `bool`  | Starts recording. Returns true on success. Only valid when in Idle state.  |
| `StopRecording()`  | `void`  | Stops the current recording. Only valid when in Recording or Paused state. |

<Note>
  For async recording with callbacks and progress tracking, use `ULCKRecorderSubsystem::StartRecordingAsync()` and `StopRecordingAsync()` directly. See [ULCKRecorderSubsystem](/api-reference/unreal/recorder-subsystem) for details.
</Note>

***

### PauseRecording / ResumeRecording

```cpp theme={null}
// Pause the current recording
Service->PauseRecording();

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

***

### IsRecording / GetCurrentRecordingDuration

```cpp theme={null}
// 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

```cpp theme={null}
ELCKRecordingState State = Service->GetRecordingState();

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

***

## Photo Capture

```cpp theme={null}
// 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:

```cpp theme={null}
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)
);
```

| Parameter      | Type    | Description                        |
| -------------- | ------- | ---------------------------------- |
| `Width`        | `int32` | Video width in pixels              |
| `Height`       | `int32` | Video height in pixels             |
| `Framerate`    | `int32` | Frames per second                  |
| `VideoBitrate` | `int32` | Video bitrate in bits per second   |
| `AudioBitrate` | `int32` | Audio bitrate in bits per second   |
| `Samplerate`   | `int32` | Audio sample rate (default: 48000) |

<Warning>
  Quality and orientation presets (SD, HD, Landscape, Portrait) are managed via `ULCKTabletDataModel`, not `ULCKService`. See the [Data Model Events](#state-tracking-via-data-model) section below.
</Warning>

***

## Audio Control

### Microphone

```cpp theme={null}
// 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:**

```cpp theme={null}
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);
    }
}
```

<Note>
  `GetCurrentMicrophoneAudioLevel()` is not exposed to Blueprint. It is available from C++ only.
</Note>

***

## Preview Mode

Preview mode (camera output without recording to disk) is managed by `ULCKRecorderSubsystem`, not `ULCKService`.

```cpp theme={null}
// Access via the recorder subsystem directly
ULCKRecorderSubsystem* Recorder = GetWorld()->GetSubsystem<ULCKRecorderSubsystem>();
Recorder->StartPreview();
Recorder->StopPreview();
```

See [ULCKRecorderSubsystem](/api-reference/unreal/recorder-subsystem) for details.

***

## Delegates on ULCKService

`ULCKService` exposes the following delegates directly:

| Delegate                  | Type                                                       | Description                                     |
| ------------------------- | ---------------------------------------------------------- | ----------------------------------------------- |
| `OnRecordingError`        | `FOnRecordingError(FString ErrorMessage, int32 ErrorCode)` | Fired when a recording error occurs             |
| `OnCameraModeChanged`     | `FOnCameraModeChanged(UClass* ModeClass)`                  | Fired when the camera mode class changes        |
| `OnRecordingSaveFinished` | `FOnRecordingSaveFinished(bool Success)`                   | Fired when the async save process completes     |
| `OnRecordingSaveProgress` | `FOnRecordingSaveProgress(float Progress)`                 | Fired during save with progress from 0.0 to 1.0 |

**Example: Subscribing to delegates**

```cpp theme={null}
// 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

```cpp theme={null}
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
        );
    }
}
```

<Warning>
  The recording state delegate is `OnRecordStateChanged` (type `FOnRecordStateChange`), not `OnRecordingStateChanged`. These are non-dynamic multicast delegates — use `AddUObject` or `AddLambda`, not `AddDynamic`.
</Warning>

***

### Available Data Model Events

| Event                        | Delegate Type                 | Parameter               | Description                                     |
| ---------------------------- | ----------------------------- | ----------------------- | ----------------------------------------------- |
| `OnRecordStateChanged`       | `FOnRecordStateChange`        | `ELCKRecordingState`    | Recording state changed (Idle, Recording, etc.) |
| `OnTabletCameraModeChanged`  | `FOnTabletCameraModeChanged`  | `UClass*`               | Camera mode class changed                       |
| `OnMicStateChanged`          | `FOnMicStateChanged`          | `ELCKMicState`          | Microphone state changed (On, Off, No\_Access)  |
| `OnVideoQualityChanged`      | `FOnVideoQualityChanged`      | `ELCKVideoQuality`      | Video quality preset changed                    |
| `OnScreenOrientationChanged` | `FOnScreenOrientationChanged` | `ELCKScreenOrientation` | Screen orientation changed                      |
| `OnMicLevelChanged`          | `FOnMicLevelChanged`          | `float`                 | Microphone audio level updated                  |

**Example: State-driven UI**

```cpp theme={null}
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:

| Method                     | Returns   | Description                                    |
| -------------------------- | --------- | ---------------------------------------------- |
| `IsStreamingAvailable()`   | `bool`    | Whether the streaming feature plugin is loaded |
| `StartLogin()`             | `void`    | Begin the streaming login/pairing flow         |
| `CancelLogin()`            | `void`    | Cancel an in-progress login                    |
| `Logout()`                 | `void`    | Log out of the streaming service               |
| `StartStreaming()`         | `bool`    | Start streaming; returns true on success       |
| `StopStreaming()`          | `void`    | Stop the current stream                        |
| `IsStreaming()`            | `bool`    | Whether currently streaming                    |
| `IsAuthenticated()`        | `bool`    | Whether logged in to the streaming service     |
| `LaunchHub()`              | `void`    | Launch the LIV Hub companion app               |
| `IsHubInstalled()`         | `bool`    | Whether the LIV Hub app is installed           |
| `GetLastLogoutReason()`    | `FString` | Reason for the last logout                     |
| `GetStreamingTargetName()` | `FString` | Name of the current streaming target           |

### Streaming Delegates

These are non-dynamic multicast delegates (use `AddUObject` or `AddLambda`):

| Delegate                   | Type                                      | Description                        |
| -------------------------- | ----------------------------------------- | ---------------------------------- |
| `OnStreamingPairingCode`   | `FOnStreamingPairingCode(const FString&)` | Pairing code received during login |
| `OnStreamingAuthenticated` | `FOnStreamingAuthenticated()`             | Successfully authenticated         |
| `OnStreamingStarted`       | `FOnStreamingStarted()`                   | Streaming session started          |
| `OnStreamingStopped`       | `FOnStreamingStopped()`                   | Streaming session stopped          |
| `OnStreamingError`         | `FOnStreamingError(const FString&)`       | Streaming error occurred           |
| `OnStreamingLoggedOut`     | `FOnStreamingLoggedOut()`                 | Logged out of streaming service    |
| `OnStreamingConfigChanged` | `FOnStreamingConfigChanged()`             | Streaming configuration changed    |

***

## Complete Example: Recording Manager

```cpp theme={null}
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

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

***

## Related

* [Delegates Reference](/api-reference/unreal/delegates) — All event delegates
* [Error Codes](/api-reference/unreal/errors) — Error handling
* [Enums Reference](/api-reference/unreal/enums) — All enum values
* [Architecture](/api-reference/unreal/architecture) — How ULCKService fits into the system
