#include "rtmp-internal.h" #include "rtmp-msgtypeid.h" #include #include #include #include #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) { // 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; }