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;