What Problem Does This Solve?
ILCKPacketSink defines the transport layer for live streaming. Once the encoder produces compressed H.264 video and AAC audio packets, something needs to send them — to an RTMP server, a WebRTC peer, or a custom protocol.
This interface decouples encoding from transport, so you can stream to any custom backend or send packets to multiple destinations simultaneously.
When to Use This
Read this if:
- Building a custom streaming backend (RTMP, SRT, WebRTC)
- Implementing multi-destination streaming
- Debugging packet-level streaming issues
Skip this if: You’re using the built-in LIV streaming. The default FLCKNativePacketBridge handles RTMP transport automatically.
Interface Definition
class LCKCORE_API ILCKPacketSink
{
public:
virtual ~ILCKPacketSink();
virtual bool Open(uint32 Width, uint32 Height, uint32 Framerate,
uint32 Samplerate, uint32 NumChannels) = 0;
virtual void Close() = 0;
virtual bool IsOpen() const = 0;
virtual void OnVideoFormatReady(const uint8* ExtraData, uint32 ExtraDataSize) = 0;
virtual void OnAudioFormatReady(uint32 SampleRate, uint32 NumChannels) = 0;
virtual void SendVideoPacket(const uint8* Data, uint32 Size,
int64 TimestampMs, bool bIsKeyframe) = 0;
virtual void SendAudioPacket(const uint8* Data, uint32 Size,
int64 TimestampMs) = 0;
};
Methods
Open
virtual bool Open(uint32 Width, uint32 Height, uint32 Framerate,
uint32 Samplerate, uint32 NumChannels) = 0;
Initialize the sink and prepare for packet delivery.
| Parameter | Type | Description |
|---|
Width | uint32 | Video width in pixels |
Height | uint32 | Video height in pixels |
Framerate | uint32 | Target video framerate |
Samplerate | uint32 | Audio sample rate in Hz |
NumChannels | uint32 | Audio channel count (typically 2) |
Returns: true if the sink opened successfully.
virtual void OnVideoFormatReady(const uint8* ExtraData, uint32 ExtraDataSize) = 0;
Called when H.264 codec extra data (SPS/PPS in AVCDecoderConfigurationRecord format) is available. Store and transmit this before any video frames.
virtual void OnAudioFormatReady(uint32 SampleRate, uint32 NumChannels) = 0;
Called when audio encoder format parameters are finalized.
SendVideoPacket
virtual void SendVideoPacket(const uint8* Data, uint32 Size,
int64 TimestampMs, bool bIsKeyframe) = 0;
Deliver a compressed H.264 video packet.
| Parameter | Type | Description |
|---|
Data | const uint8* | H.264 NAL unit data |
Size | uint32 | Packet size in bytes |
TimestampMs | int64 | Presentation timestamp in milliseconds |
bIsKeyframe | bool | true if this is an IDR frame |
SendAudioPacket
virtual void SendAudioPacket(const uint8* Data, uint32 Size,
int64 TimestampMs) = 0;
Deliver a compressed AAC audio packet (raw AAC, no ADTS header).
Close / IsOpen
virtual void Close() = 0;
virtual bool IsOpen() const = 0;
Shut down the sink and release resources. IsOpen() returns current state.
Registering a Packet Sink
Add sinks to the encoder at runtime via ILCKEncoder:
Encoder->AddPacketSink(MySink.Get());
// When done:
Encoder->RemovePacketSink(MySink.Get());
Call RemovePacketSink() before destroying the sink. On Windows, late-attached sinks receive cached codec headers automatically.
Thread Safety
SendVideoPacket and SendAudioPacket are called from the encoder thread, not the game thread. Your implementation must be thread-safe. Open and Close are called from the game thread.
Implementation Example
class FMyPacketSink : public ILCKPacketSink
{
public:
virtual bool Open(uint32 Width, uint32 Height, uint32 Framerate,
uint32 Samplerate, uint32 NumChannels) override
{
bOpen = ConnectToServer(Width, Height, Framerate);
return bOpen;
}
virtual void OnVideoFormatReady(const uint8* ExtraData,
uint32 ExtraDataSize) override
{
CachedSPSPPS.SetNum(ExtraDataSize);
FMemory::Memcpy(CachedSPSPPS.GetData(), ExtraData, ExtraDataSize);
SendStreamHeader(CachedSPSPPS);
}
virtual void OnAudioFormatReady(uint32 SampleRate,
uint32 NumChannels) override
{
SendAudioHeader(SampleRate, NumChannels);
}
virtual void SendVideoPacket(const uint8* Data, uint32 Size,
int64 TimestampMs, bool bIsKeyframe) override
{
// Called from encoder thread
SendToServer(Data, Size, TimestampMs, bIsKeyframe);
}
virtual void SendAudioPacket(const uint8* Data, uint32 Size,
int64 TimestampMs) override
{
SendToServer(Data, Size, TimestampMs);
}
virtual void Close() override { DisconnectFromServer(); bOpen = false; }
virtual bool IsOpen() const override { return bOpen; }
private:
TArray<uint8> CachedSPSPPS;
bool bOpen = false;
};
Key Takeaways
Transport abstraction — Decouples encoding from delivery
H.264 + AAC — Packets are compressed, ready to transmit
Thread safety required — Packet methods called from encoder thread
Multiple sinks — Encoder supports multiple simultaneous sinks