Skip to main content

What Problem Does This Solve?

Understanding LCK’s architecture helps you:
  • Debug issues by knowing which subsystem handles what
  • Extend the SDK with custom encoders or audio sources
  • Optimize performance by understanding the data flow
  • Build custom UI without breaking core functionality
This page maps out how LCK’s modules work together in Unreal Engine.

When to Read This

Read this when:
  • Integrating LCK for the first time
  • Building custom recording UI
  • Creating custom audio sources (FMOD, Wwise)
  • Debugging recording or encoding issues
  • Contributing to LCK development
Skip this if you’re just using the default tablet UI.

High-Level Overview

LCK is organized into modular runtime modules that load in specific phases:
Key principle: Lower modules (Core, Audio) know nothing about higher modules (Tablet, UI). This lets you build custom UI without modifying core functionality.

Module Dependency Map

This diagram shows which modules depend on which, and whether they are required or optional.

Module Classification

Load Order

Modules load in three phases, in this order:
  1. EarliestPossible — Before engine initialization. Only LCKVulkan loads here (Android only). This phase exists because Vulkan interop must be established before the RHI initializes.
  2. PostDefault — After engine init, before game modules. Core infrastructure loads here: LCKCore, LCKAudio, and all platform-specific encoder modules. These must be available before any game code tries to use the recording API.
  3. Default — Standard game module loading. All high-level and optional modules load here: LCKTablet, LCKUI, LCKStreaming, and all audio plugins. By this point, core infrastructure is guaranteed to be available.
Platform-specific modules (LCKVulkan, LCKWindowsEncoder, LCKAndroidEncoder, LCKAndroidGallery) are auto-loaded by the engine based on the target platform. You never need to add them to your .Build.cs.

Runtime Discovery

Optional modules register themselves via Unreal’s IModularFeatures system at startup. Core modules discover them at runtime without compile-time coupling:
  • Audio sources register as ILCKAudioSource modular features
  • Encoder factories register as ILCKEncoderFactory modular features
  • Streaming backends register as ILCKStreamingFeature modular features
  • Packet sinks are passed directly to encoders via ILCKEncoder::AddPacketSink()
This means you can add or remove optional modules without recompiling core code.

Platform Coverage Matrix

Linux support is limited to core, audio, UI, and Vivox modules. Encoding and streaming require Windows or Android.

Subsystem Hierarchy

LCK uses Unreal’s subsystem architecture for lifetime management:

ULCKRecorderSubsystem

Type: UTickableWorldSubsystem (ticks every frame)
Module: LCKCore
Purpose: Low-level recording control, frame capture, encoder lifecycle
When to use: Advanced scenarios where you need direct control over the encoder. Most developers should use ULCKService instead.

ULCKSubsystem

Type: UWorldSubsystem
Module: LCKTablet
Purpose: Provides access to ULCKService
When to use: This is your entry point. Get the service, use its methods.

ULCKTelemetrySubsystem

Type: UGameInstanceSubsystem
Module: LCKCore
Purpose: Analytics and usage tracking
Automatically tracks SDK events like recording start/stop, errors, quality changes.

Encoder Architecture

Encoders implement the ILCKEncoder interface and are created via modular features:
Encoders now support dual output: they can write to disk (MP4 file) and simultaneously route encoded packets to one or more ILCKPacketSink implementations (for RTMP streaming or other transports). Use SetRecordToDisk(false) for stream-only mode.

Platform Implementations

Windows Encoder

  • Uses IMFSinkWriter for muxing
  • Uses IMFTransform for H.264 encoding
  • Triple-buffered texture pool to avoid GPU stalls
  • Direct3D 11 texture interop

Android Encoder

  • Uses AMediaCodec for H.264/AAC encoding
  • Uses AMediaMuxer for MP4 container
  • Vulkan texture export via EGL
  • Hardware-accelerated on Quest devices

Encoder Factory

Encoders are discovered and created via Unreal’s modular features system:
How to find an encoder:

Audio Architecture

Audio sources also use modular features for extensibility:
In v1.0, FDelegateRenderAudio is the multicast parent delegate. FOnRenderAudioDelegate is a typedef for its inner FDelegate type. The delegate now includes SampleRate as a third parameter.

Audio Mixing

Multiple audio sources are combined via FLCKAudioMix:
Example: Game audio + microphone:

Data Flow

The encoder can output to both disk (MP4 file) and packet sinks (RTMP streaming) simultaneously. When streaming without recording, set SetRecordToDisk(false) on the encoder.

Triple-Buffered Texture Pool

Encoder uses triple-buffering to prevent GPU stalls:
Why triple buffering?
  1. GPU is rendering to texture 0
  2. Encoder is reading from texture 1
  3. Texture 2 is free for next frame
This prevents GPU-CPU synchronization stalls.

Thread Safety

LCK uses standard Unreal thread-safety patterns:
Audio callbacks come from different threads. If you need to access game thread objects (like UObject properties), use:

Modular Feature Discovery

Encoders and audio sources are discovered at runtime:

Log Categories

Enable verbose logging for debugging:

Key Takeaways

Modular design — Core functionality (encoding, audio) is separate from UI (tablet)
Subsystem-based — Uses Unreal’s subsystem architecture for clean lifetime management
Platform abstraction — Encoder interface allows platform-specific implementations
Extensible audio — Audio sources register via modular features
Thread-safe — Encoding happens on background thread, careful with audio callbacks