librtmp Source Code Examination

AMF Format Fundamentals

AMF (Action Message Format) is a binary protocol for serializing ActionScript data types. It exists in two versions: AMF0 (basic specification) and AMF3 (extended version).

AMF0 Data Types

typedef enum {
    DT_NUMBER = 0,
    DT_BOOLEAN,
    DT_STRING,
    DT_OBJECT,
    DT_NULL,
    DT_UNDEFINED,
    DT_REFERENCE,
    DT_ARRAY,
    DT_OBJECT_END,
    DT_STRICT_ARRAY,
    DT_DATE,
    DT_LONG_STRING,
    DT_UNSUPPORTED,
    DT_XML_DOC,
    DT_TYPED_OBJECT,
    DT_AVMPLUS,
    DT_INVALID = 0xff
} AMFTypeCategory;

AMF3 Data Types

typedef enum {
    DT3_UNDEFINED = 0,
    DT3_NULL,
    DT3_FALSE,
    DT3_TRUE,
    DT3_INTEGER,
    DT3_DOUBLE,
    DT3_STRING,
    DT3_XML_DOC,
    DT3_DATE,
    DT3_ARRAY,
    DT3_OBJECT,
    DT3_XML,
    DT3_BYTE_ARRAY
} AMF3TypeCategory;

Core Structrues

typedef struct {
    char *data;
    int length;
} BinaryString;

typedef struct {
    int prop_count;
    struct AMFProperty *properties;
} AMFObject;

typedef struct AMFProperty {
    BinaryString key;
    AMFTypeCategory type;
    union {
        double numeric;
        BinaryString string;
        AMFObject object;
    } value;
    int16_t utc_offset;
} AMFProperty;

librtmp Implementation Analysis

Handshake Procedure

int PerformHandshake(RTMP *ctx) {
    uint8_t client_init[RTMP_SIG_SIZE + 1];
    uint8_t server_resp[RTMP_SIG_SIZE];
    client_init[0] = 0x03;  // Unencrypted

    uint32_t uptime = htonl(GetCurrentTime());
    memcpy(&client_init[1], &uptime, 4);
    memset(&client_init[5], 0, 4);
    
    for (int i = 9; i < RTMP_SIG_SIZE; i++) 
        client_init[i] = rand() % 256;

    if (!SendData(ctx, client_init, RTMP_SIG_SIZE + 1)) 
        return 0;

    uint8_t response_type;
    if (ReceiveData(ctx, &response_type, 1) != 1) 
        return 0;

    if (ReceiveData(ctx, server_resp, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 
        return 0;

    if (!SendData(ctx, server_resp, RTMP_SIG_SIZE)) 
        return 0;

    if (ReceiveData(ctx, server_resp, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 
        return 0;

    return memcmp(server_resp, &client_init[1], RTMP_SIG_SIZE) == 0;
}

Connection Establishment

void BuildConnectPacket(RTMP *ctx, Packet *pkt) {
    pkt->channel_id = 0x03;
    pkt->header_size = LARGE_HEADER;
    pkt->packet_type = INVOKE_PACKET;
    pkt->timestamp = 0;
    pkt->stream_id = 0;
    pkt->absolute_ts = 0;
    
    char *buffer = pkt->payload + MAX_HEADER;
    buffer = EncodeAMFString(buffer, "connect");
    buffer = EncodeAMFNumber(buffer, ++ctx->invoke_id);
    
    *buffer++ = AMF_OBJECT;
    buffer = EncodeNamedString(buffer, "app", &ctx->app);
    buffer = EncodeNamedString(buffer, "tcUrl", &ctx->tc_url);
    buffer = EncodeNamedNumber(buffer, "videoCodecs", ctx->video_codecs);
    *buffer++ = 0;
    *buffer++ = 0;
    *buffer++ = AMF_OBJECT_END;
}

Packet Processing

int ProcessRTMPPacket(RTMP *ctx, Packet *pkt) {
    switch (pkt->packet_type) {
        case CHUNK_SIZE_PACKET:
            UpdateChunkSize(ctx, pkt);
            break;
        case AUDIO_PACKET:
            HandleAudio(ctx, pkt);
            ctx->media_channel = pkt->channel_id;
            break;
        case VIDEO_PACKET:
            HandleVideo(ctx, pkt);
            ctx->media_channel = pkt->channel_id;
            break;
        case INVOKE_PACKET:
            return HandleCommand(ctx, pkt->payload, pkt->data_size);
        case METADATA_PACKET:
            HandleMetadata(ctx, pkt->payload, pkt->data_size);
            break;
    }
    return 1;
}

Key Structures

typedef struct {
    uint8_t header_type;
    uint8_t packet_type;
    uint8_t absolute_ts;
    int channel_id;
    uint32_t timestamp;
    int32_t stream_id;
    uint32_t data_size;
    char *payload;
} RTMPPacket;

typedef struct {
    int socket;
    char buffer[BUFFER_SIZE];
    int data_size;
    char *read_pos;
} NetworkBuffer;

Tags: librtmp rtmp AMF streaming protocol

Posted on Sat, 16 May 2026 10:45:21 +0000 by hbalagh