Skip to main content
Module: LCKStreaming | Version: 1.0 | Platforms: Win64, Android (Quest)

Overview

LCKStreaming uses a device pairing flow for authentication. The user is shown a short code in-game which they enter on a web dashboard from any device (phone, PC, tablet). The SDK polls the backend until the code is confirmed, then fetches the user profile and streaming configuration. This approach avoids keyboard input in VR — the user pairs on a companion device.

Device Pairing Flow

┌──────────┐         ┌──────────────┐         ┌─────────────┐
│  Game    │         │  LIV API     │         │  User       │
│  (SDK)   │         │  Backend     │         │  (Browser)  │
└────┬─────┘         └──────┬───────┘         └──────┬──────┘
     │  StartLogin()        │                        │
     │─────────────────────>│                        │
     │  CreateDeviceLogin   │                        │
     │─────────────────────>│                        │
     │                      │                        │
     │  OnPairingCodeReceived("ABC123")              │
     │<─────────────────────│                        │
     │                      │                        │
     │  Display code in UI  │                        │
     │                      │   User visits          │
     │                      │   dashboard.liv.tv/pair│
     │                      │<───────────────────────│
     │                      │   Enters "ABC123"      │
     │                      │<───────────────────────│
     │                      │                        │
     │  Poll (every 2.5s)   │                        │
     │─────────────────────>│                        │
     │  ...                 │                        │
     │  Poll → Authenticated│                        │
     │<─────────────────────│                        │
     │                      │                        │
     │  GetUserProfile()    │                        │
     │─────────────────────>│                        │
     │  OnAuthenticated     │                        │
     │  OnStreamingConfigReceived                    │
     │<─────────────────────│                        │

Starting Login

Call StartLogin() to begin the pairing process. The SDK creates a device login attempt and broadcasts the pairing code.
ULCKStreamingSubsystem* Streaming =
    GetGameInstance()->GetSubsystem<ULCKStreamingSubsystem>();

// Bind the pairing code delegate first
Streaming->OnPairingCodeReceived.AddDynamic(
    this, &UMyAuth::OnPairingCode);
Streaming->OnAuthenticated.AddDynamic(
    this, &UMyAuth::OnAuthenticated);

// Begin login
Streaming->StartLogin();

Displaying the Pairing Code

When OnPairingCodeReceived fires, display the code prominently in your UI alongside the URL dashboard.liv.tv/pair.
UFUNCTION()
void UMyAuth::OnPairingCode(const FString& Code)
{
    UE_LOG(LogTemp, Log, TEXT("Enter code %s at dashboard.liv.tv/pair"), *Code);

    // Update your UI widget
    if (PairingWidget)
    {
        PairingWidget->SetCodeText(Code);
        PairingWidget->SetVisibility(ESlateVisibility::Visible);
    }
}
You can also retrieve the current code at any time:
FString CurrentCode = Streaming->GetPairingCode();
Design the pairing screen for VR readability — use large, high-contrast text. The user may need to read the code through a headset and type it on a phone.

Polling Mechanism

After the pairing code is issued, the SDK automatically polls the LIV backend every 2.5 seconds to check whether the user has entered the code. No manual polling is required. On success, the SDK:
  1. Stores the auth token internally
  2. Calls GetUserProfile() to fetch streaming configuration
  3. Broadcasts OnAuthenticated
  4. Broadcasts OnStreamingConfigReceived with the target type and subscription info
The pairing code has an expiration time. If the code expires before the user enters it, call StartLogin() again to generate a new code.

Canceling Login

If the user wants to cancel the pairing process:
Streaming->CancelLogin();
This stops the polling timer and clears the current login attempt.

Profile and Subscription Info

After authentication, the OnStreamingConfigReceived delegate provides the user’s streaming target and subscription status.
UFUNCTION()
void UMyAuth::OnStreamingConfigReceived(
    ELCKStreamingTargetType TargetType,
    const FLCKUserSubscription& Subscription)
{
    // TargetType: None, YouTube, Twitch, Manual
    UE_LOG(LogTemp, Log, TEXT("Target: %s, Subscription active: %s"),
        *UEnum::GetValueAsString(TargetType),
        Subscription.bIsActive ? TEXT("Yes") : TEXT("No"));
}

Types

UENUM(BlueprintType)
enum class ELCKStreamingTargetType : uint8
{
    None     UMETA(DisplayName = "None"),
    YouTube  UMETA(DisplayName = "YouTube"),
    Twitch   UMETA(DisplayName = "Twitch"),
    Manual   UMETA(DisplayName = "Manual RTMP")
};

USTRUCT(BlueprintType)
struct FLCKUserSubscription
{
    GENERATED_BODY()

    UPROPERTY(BlueprintReadOnly, Category = "LCK|Streaming")
    FString Sku;

    UPROPERTY(BlueprintReadOnly, Category = "LCK|Streaming")
    bool bIsActive = false;
};

Querying State After Login

// Check authentication
bool bLoggedIn = Streaming->IsAuthenticated();

// Check subscription
bool bSubscribed = Streaming->HasActiveSubscription();

// Check streaming target
bool bHasTarget = Streaming->HasStreamingTarget();
ELCKStreamingTargetType Target = Streaming->GetStreamingTargetType();
FString TargetName = Streaming->GetStreamingTargetName(); // "YouTube", "Twitch", "Manual"

Refreshing Configuration

If the user changes their streaming target on the dashboard, refresh the config:
// Visible refresh (broadcasts OnStreamingConfigReceived on completion)
Streaming->RefreshStreamingConfig(false);

// Silent refresh (suppresses error broadcasts for transient failures)
Streaming->RefreshStreamingConfig(true);

Logout

Call Logout() to clear the stored auth token and reset the subsystem state.
Streaming->OnLoggedOut.AddDynamic(this, &UMyAuth::OnLoggedOut);

Streaming->Logout();
UFUNCTION()
void UMyAuth::OnLoggedOut()
{
    FString Reason = Streaming->GetLastLogoutReason();
    if (!Reason.IsEmpty())
    {
        UE_LOG(LogTemp, Log, TEXT("Logged out: %s"), *Reason);
    }

    // Return to login screen
    ShowLoginUI();
}
Logout() stops any active stream before clearing credentials. Ensure your UI handles the OnStreamStopped delegate if a stream is active at logout time.

LIV Hub Companion App (Android)

On Android/Quest, users can authenticate and manage streaming settings through the LIV Hub companion app instead of a web browser.
// Check if LIV Hub is installed
if (Streaming->IsHubInstalled())
{
    // Launch LIV Hub for authentication
    Streaming->LaunchHub();
}
else
{
    // Fall back to standard device pairing
    Streaming->StartLogin();
}
LaunchHub() and IsHubInstalled() are only functional on Android. On other platforms, IsHubInstalled() returns true (no separate hub dependency) and LaunchHub() is a no-op.

Error Handling

Authentication errors surface through the OnStreamError delegate and in LogLCKStreaming output. Common error scenarios:
ScenarioBehavior
Network unreachableStartLogin() fails, no pairing code issued
Pairing code expiredPolling stops, call StartLogin() again
Invalid Tracking IDAPI returns error, logged to LogLCKStreaming
Subscription inactiveHasActiveSubscription() returns false
Token revoked server-sideOnLoggedOut fires with reason via GetLastLogoutReason()

Complete Authentication Example

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

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

        Streaming = GetWorld()->GetGameInstance()->GetSubsystem<ULCKStreamingSubsystem>();
        if (!Streaming) return;

        Streaming->OnPairingCodeReceived.AddDynamic(this, &UMyAuthManager::OnPairingCode);
        Streaming->OnAuthenticated.AddDynamic(this, &UMyAuthManager::OnAuthenticated);
        Streaming->OnStreamingConfigReceived.AddDynamic(this, &UMyAuthManager::OnConfig);
        Streaming->OnLoggedOut.AddDynamic(this, &UMyAuthManager::OnLoggedOut);
        Streaming->OnStreamError.AddDynamic(this, &UMyAuthManager::OnError);
    }

    UFUNCTION(BlueprintCallable, Category = "Streaming")
    void Login()
    {
        if (Streaming->IsAuthenticated())
        {
            UE_LOG(LogTemp, Log, TEXT("Already authenticated"));
            return;
        }
        Streaming->StartLogin();
    }

    UFUNCTION(BlueprintCallable, Category = "Streaming")
    void DoLogout()
    {
        Streaming->Logout();
    }

private:
    UFUNCTION()
    void OnPairingCode(const FString& Code)
    {
        UE_LOG(LogTemp, Log, TEXT("Pairing code: %s — enter at dashboard.liv.tv/pair"), *Code);
    }

    UFUNCTION()
    void OnAuthenticated()
    {
        UE_LOG(LogTemp, Log, TEXT("Login successful"));
    }

    UFUNCTION()
    void OnConfig(ELCKStreamingTargetType TargetType, const FLCKUserSubscription& Subscription)
    {
        UE_LOG(LogTemp, Log, TEXT("Target: %s | Sub active: %s"),
            *UEnum::GetValueAsString(TargetType),
            Subscription.bIsActive ? TEXT("Yes") : TEXT("No"));
    }

    UFUNCTION()
    void OnLoggedOut()
    {
        UE_LOG(LogTemp, Log, TEXT("Logged out: %s"), *Streaming->GetLastLogoutReason());
    }

    UFUNCTION()
    void OnError(const FString& ErrorMessage)
    {
        UE_LOG(LogTemp, Error, TEXT("Stream error: %s"), *ErrorMessage);
    }

    UPROPERTY()
    ULCKStreamingSubsystem* Streaming = nullptr;
};

Troubleshooting

  1. Verify bEnableStreaming is enabled in project settings
  2. Check network connectivity to dashboard.liv.tv
  3. Ensure the Tracking ID is configured and valid
  4. Check LogLCKStreaming for API error responses
  1. The user has not configured a streaming target at dashboard.liv.tv
  2. Call RefreshStreamingConfig(false) to re-fetch
  3. Verify HasStreamingTarget() and HasActiveSubscription() return values
  1. Check GetLastLogoutReason() for details
  2. The token may have been revoked server-side (e.g., 401/403 on config refresh)
  3. Network interruptions can cause auth state to reset
  1. Verify LIV Hub is installed with IsHubInstalled()
  2. The app package is tv.liv.controlcenter
  3. Fall back to standard pairing if Hub is unavailable

See Also