stream-deploy/ZLM/3rdpart/media-server/librtmp/source/rtmp-chunk-read.c

258 lines
7.0 KiB
C++

#include "rtmp-internal.h"
#include "rtmp-msgtypeid.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#define MIN(x, y) ((x) < (y) ? (x) : (y))
static struct rtmp_packet_t* rtmp_packet_find(struct rtmp_t* rtmp, uint32_t cid)
{
uint32_t i;
struct rtmp_packet_t* pkt;
// The protocol supports up to 65597 streams with IDs 3-65599
assert(cid <= 65535 + 64 && cid >= 2 /* Protocol Control Messages */);
for (i = 0; i < N_CHUNK_STREAM; i++)
{
pkt = rtmp->in_packets + ((i + cid) % N_CHUNK_STREAM);
if (pkt->header.cid == cid)
return pkt;
}
return NULL;
}
static struct rtmp_packet_t* rtmp_packet_create(struct rtmp_t* rtmp, uint32_t cid)
{
uint32_t i;
struct rtmp_packet_t* pkt;
// The protocol supports up to 65597 streams with IDs 3-65599
assert(cid <= 65535 + 64 && cid >= 2 /* Protocol Control Messages */);
assert(NULL == rtmp_packet_find(rtmp, cid));
for (i = 0; i < N_CHUNK_STREAM; i++)
{
pkt = rtmp->in_packets + ((i + cid) % N_CHUNK_STREAM);
if (0 == pkt->header.cid)
return pkt;
}
return NULL;
}
static struct rtmp_packet_t* rtmp_packet_parse(struct rtmp_t* rtmp, const uint8_t* buffer)
{
uint8_t fmt = 0;
uint32_t cid = 0;
struct rtmp_packet_t* packet;
// chunk base header
buffer += rtmp_chunk_basic_header_read(buffer, &fmt, &cid);
// load previous header
packet = rtmp_packet_find(rtmp, cid);
if (NULL == packet)
{
if (RTMP_CHUNK_TYPE_0 != fmt && RTMP_CHUNK_TYPE_1 != fmt)
return NULL; // don't know stream length
packet = rtmp_packet_create(rtmp, cid);
if (NULL == packet)
return NULL;
}
// chunk message header
packet->header.cid = cid;
packet->header.fmt = fmt;
rtmp_chunk_message_header_read(buffer, &packet->header);
return packet;
}
static int rtmp_packet_alloc(struct rtmp_t* rtmp, struct rtmp_packet_t* packet)
{
void* p;
(void)rtmp;
// 24-bytes length
assert(0 == packet->bytes);
assert(packet->header.length < (1 << 24));
// fixed SMS (Chinacache Smart Media Server) packet->header.length = 0
if (0 == packet->capacity || packet->capacity < packet->header.length)
{
p = realloc(packet->payload, packet->header.length + 1024);
if (NULL == p)
return -ENOMEM;
packet->payload = p;
packet->capacity = packet->header.length + 1024;
}
return 0;
}
int rtmp_chunk_read(struct rtmp_t* rtmp, const uint8_t* data, size_t bytes)
{
const static uint32_t s_header_size[] = { 11, 7, 3, 0 };
int r, invalid_extended_timestamp;
size_t size, offset = 0;
uint8_t extended_timestamp_buffer[4];
uint32_t extended_timestamp = 0;
struct rtmp_parser_t* parser = &rtmp->parser;
struct rtmp_chunk_header_t header;
while (offset < bytes)
{
switch (parser->state)
{
case RTMP_PARSE_INIT:
parser->pkt = NULL;
parser->bytes = 1;
parser->buffer[0] = data[offset++];
if (0 == (parser->buffer[0] & 0x3F))
parser->basic_bytes = 2;
else if (1 == (parser->buffer[0] & 0x3F))
parser->basic_bytes = 3;
else
parser->basic_bytes = 1;
parser->state = RTMP_PARSE_BASIC_HEADER;
break;
case RTMP_PARSE_BASIC_HEADER:
assert(parser->bytes <= parser->basic_bytes);
while (parser->bytes < parser->basic_bytes && offset < bytes)
{
parser->buffer[parser->bytes++] = data[offset++];
}
assert(parser->bytes <= parser->basic_bytes);
if (parser->bytes >= parser->basic_bytes)
{
parser->state = RTMP_PARSE_MESSAGE_HEADER;
}
break;
case RTMP_PARSE_MESSAGE_HEADER:
size = s_header_size[parser->buffer[0] >> 6] + parser->basic_bytes;
assert(parser->bytes <= size);
while (parser->bytes < size && offset < bytes)
{
parser->buffer[parser->bytes++] = data[offset++];
}
assert(parser->bytes <= size);
if (parser->bytes >= size)
{
parser->pkt = rtmp_packet_parse(rtmp, parser->buffer);
parser->state = RTMP_PARSE_EXTENDED_TIMESTAMP;
}
break;
case RTMP_PARSE_EXTENDED_TIMESTAMP:
if (NULL == parser->pkt) return -ENOMEM;
assert(parser->pkt->header.timestamp <= 0xFFFFFF);
size = s_header_size[parser->pkt->header.fmt] + parser->basic_bytes;
if (parser->pkt->header.timestamp == 0xFFFFFF) size += 4; // extended timestamp
assert(parser->bytes <= size);
while (parser->bytes < size && offset < bytes)
{
parser->buffer[parser->bytes++] = data[offset++];
}
assert(parser->bytes <= size);
if (parser->bytes >= size)
{
invalid_extended_timestamp = 0;
extended_timestamp = parser->pkt->header.timestamp;
if (parser->pkt->header.timestamp == 0xFFFFFF)
{
// parse extended timestamp
rtmp_chunk_extended_timestamp_read(parser->buffer + s_header_size[parser->buffer[0] >> 6] + parser->basic_bytes, &extended_timestamp);
if (RTMP_CHUNK_TYPE_3 == parser->pkt->header.fmt && extended_timestamp != parser->pkt->delta)
{
extended_timestamp = parser->pkt->delta; // use previous delta
// fix code offset -= 4 on offset < 4;
invalid_extended_timestamp = 1;
memcpy(extended_timestamp_buffer, parser->buffer + s_header_size[parser->buffer[0] >> 6] + parser->basic_bytes, 4);
}
}
// first chunk
if (0 == parser->pkt->bytes)
{
parser->pkt->delta = extended_timestamp;
// handle timestamp/delta
if (RTMP_CHUNK_TYPE_0 == parser->pkt->header.fmt)
parser->pkt->clock = parser->pkt->delta;
else
parser->pkt->clock += parser->pkt->delta;
if (0 != rtmp_packet_alloc(rtmp, parser->pkt))
return -ENOMEM;
}
parser->state = RTMP_PARSE_PAYLOAD;
// rewind extended_timestamp_buffer
if (invalid_extended_timestamp)
{
r = rtmp_chunk_read(rtmp, extended_timestamp_buffer, 4);
if (0 != r) return r;
}
}
break;
case RTMP_PARSE_PAYLOAD:
if (NULL == parser->pkt || NULL == parser->pkt->payload
|| parser->pkt->bytes > parser->pkt->capacity
|| parser->pkt->bytes > parser->pkt->header.length
|| parser->pkt->header.length > parser->pkt->capacity)
{
assert(0);
return -ENOMEM;
}
//assert(parser->pkt->bytes <= parser->pkt->header.length);
//assert(parser->pkt->capacity >= parser->pkt->header.length);
size = MIN(rtmp->in_chunk_size - (parser->pkt->bytes % rtmp->in_chunk_size), parser->pkt->header.length - parser->pkt->bytes);
size = MIN(size, bytes - offset);
if(size > 0) memcpy(parser->pkt->payload + parser->pkt->bytes, data + offset, size);
parser->pkt->bytes += size;
offset += size;
if (parser->pkt->bytes >= parser->pkt->header.length)
{
assert(parser->pkt->bytes == parser->pkt->header.length);
parser->state = RTMP_PARSE_INIT; // reset parser state
parser->pkt->bytes = 0; // clear bytes
memcpy(&header, &parser->pkt->header, sizeof(header));
header.timestamp = parser->pkt->clock;
r = rtmp_handler(rtmp, &header, parser->pkt->payload);
if(0 != r) return r;
}
else if (0 == (parser->pkt->bytes % rtmp->in_chunk_size))
{
// next chunk
parser->state = RTMP_PARSE_INIT;
}
else
{
// need more data
assert(offset == bytes);
}
break;
default:
assert(0);
break;
}
}
return 0;
}