Skip to main content

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.
ParameterTypeDescription
Widthuint32Video width in pixels
Heightuint32Video height in pixels
Framerateuint32Target video framerate
Samplerateuint32Audio sample rate in Hz
NumChannelsuint32Audio channel count (typically 2)
Returns: true if the sink opened successfully.

OnVideoFormatReady

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.

OnAudioFormatReady

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.
ParameterTypeDescription
Dataconst uint8*H.264 NAL unit data
Sizeuint32Packet size in bytes
TimestampMsint64Presentation timestamp in milliseconds
bIsKeyframebooltrue 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