#include "rtmp-chunk-header.h" #include "rtmp-internal.h" #include "rtmp-util.h" #include #include #include // 5.3.1. Chunk Format (p11) /* 3-bytes basic header + 11-bytes message header + 4-bytes extended timestamp */ //#define MAX_CHUNK_HEADER 18 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->out_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->out_packets + ((i + cid) % N_CHUNK_STREAM); // if (0 == pkt->header.cid) // return pkt; // } // return NULL; //} static const struct rtmp_chunk_header_t* rtmp_chunk_header_zip(struct rtmp_t* rtmp, const struct rtmp_chunk_header_t* header) { struct rtmp_packet_t* pkt; // previous saved chunk header struct rtmp_chunk_header_t h; assert(0 != header->cid && 1 != header->cid); assert(RTMP_CHUNK_TYPE_0 == header->fmt || RTMP_CHUNK_TYPE_1 == header->fmt); memcpy(&h, header, sizeof(h)); // find previous chunk header pkt = rtmp_packet_find(rtmp, h.cid); if (NULL == pkt) { //pkt = rtmp_packet_create(rtmp, h.cid); //if (NULL == pkt) // return NULL; // too many chunk stream id assert(0); return NULL; // can't find chunk stream id } h.fmt = RTMP_CHUNK_TYPE_0; if (RTMP_CHUNK_TYPE_0 != header->fmt /* enable compress */ && header->cid == pkt->header.cid /* not the first packet */ && header->timestamp >= pkt->clock /* timestamp wrap */ && header->timestamp - pkt->clock < 0xFFFFFF /* timestamp delta < 1 << 24 */ && header->stream_id == pkt->header.stream_id /* message stream id */) { h.fmt = RTMP_CHUNK_TYPE_1; h.timestamp -= pkt->clock; // timestamp delta if (header->type == pkt->header.type && header->length == pkt->header.length) { h.fmt = RTMP_CHUNK_TYPE_2; if (h.timestamp == pkt->header.timestamp) h.fmt = RTMP_CHUNK_TYPE_3; } } memcpy(&pkt->header, &h, sizeof(h)); pkt->clock = header->timestamp; // save timestamp return &pkt->header; } int rtmp_chunk_write(struct rtmp_t* rtmp, const struct rtmp_chunk_header_t* h, const uint8_t* payload) { int r = 0; uint8_t p[MAX_CHUNK_HEADER]; uint32_t chunkSize, headerSize, payloadSize; const struct rtmp_chunk_header_t* header; // compression rtmp chunk header header = rtmp_chunk_header_zip(rtmp, h); if (!header || header->length >= 0xFFFFFF) return -EINVAL; // invalid length payloadSize = header->length; headerSize = rtmp_chunk_basic_header_write(p, header->fmt, header->cid); headerSize += rtmp_chunk_message_header_write(p + headerSize, header); if(header->timestamp >= 0xFFFFFF) headerSize += rtmp_chunk_extended_timestamp_write(p + headerSize, header->timestamp); while (payloadSize > 0 && 0 == r) { chunkSize = payloadSize < rtmp->out_chunk_size ? payloadSize : rtmp->out_chunk_size; r = rtmp->send(rtmp->param, p, headerSize, payload, chunkSize); // callback payload += chunkSize; payloadSize -= chunkSize; if (payloadSize > 0) { headerSize = rtmp_chunk_basic_header_write(p, RTMP_CHUNK_TYPE_3, header->cid); if (header->timestamp >= 0xFFFFFF) headerSize += rtmp_chunk_extended_timestamp_write(p + headerSize, header->timestamp); } } return r; }