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:
Stores the auth token internally
Calls GetUserProfile() to fetch streaming configuration
Broadcasts OnAuthenticated
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:
Scenario Behavior Network unreachable StartLogin() fails, no pairing code issuedPairing code expired Polling stops, call StartLogin() again Invalid Tracking ID API returns error, logged to LogLCKStreaming Subscription inactive HasActiveSubscription() returns falseToken revoked server-side OnLoggedOut 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
Pairing code not appearing
Verify bEnableStreaming is enabled in project settings
Check network connectivity to dashboard.liv.tv
Ensure the Tracking ID is configured and valid
Check LogLCKStreaming for API error responses
Authentication succeeds but no streaming target
The user has not configured a streaming target at dashboard.liv.tv
Call RefreshStreamingConfig(false) to re-fetch
Verify HasStreamingTarget() and HasActiveSubscription() return values
OnLoggedOut fires unexpectedly
Check GetLastLogoutReason() for details
The token may have been revoked server-side (e.g., 401/403 on config refresh)
Network interruptions can cause auth state to reset
LIV Hub not launching on Quest
Verify LIV Hub is installed with IsHubInstalled()
The app package is tv.liv.controlcenter
Fall back to standard pairing if Hub is unavailable
See Also