// RFC7798 RTP Payload Format for High Efficiency Video Coding (HEVC) #include "rtp-packet.h" #include "rtp-payload-internal.h" #include #include #include #include /* 0 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |F| Type | LayerId | TID | +-------------+-----------------+ Forbidden zero(F) : 1 bit NAL unit type(Type) : 6 bits NUH layer ID(LayerId) : 6 bits NUH temporal ID plus 1 (TID) : 3 bits */ #define H265_TYPE(v) ((v >> 1) & 0x3f) #define FU_START(v) (v & 0x80) #define FU_END(v) (v & 0x40) #define FU_NAL(v) (v & 0x3F) struct rtp_decode_h265_t { struct rtp_payload_t handler; void* cbparam; uint16_t seq; // rtp seq uint32_t timestamp; uint8_t* ptr; int size, capacity; int flags; int using_donl_field; }; static void* rtp_h265_unpack_create(struct rtp_payload_t *handler, void* param) { struct rtp_decode_h265_t *unpacker; unpacker = (struct rtp_decode_h265_t *)calloc(1, sizeof(*unpacker)); if (!unpacker) return NULL; memcpy(&unpacker->handler, handler, sizeof(unpacker->handler)); unpacker->cbparam = param; unpacker->flags = -1; return unpacker; } static void rtp_h265_unpack_destroy(void* p) { struct rtp_decode_h265_t *unpacker; unpacker = (struct rtp_decode_h265_t *)p; if (unpacker->ptr) free(unpacker->ptr); #if defined(_DEBUG) || defined(DEBUG) memset(unpacker, 0xCC, sizeof(*unpacker)); #endif free(unpacker); } // 4.4.2. Aggregation Packets (APs) (p25) /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP Header | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | PayloadHdr (Type=48) | NALU 1 DONL | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 Size | NALU 1 HDR | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | NALU 1 Data . . . | | | + . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | NALU 2 DOND | NALU 2 Size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 HDR | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ NALU 2 Data | | | | . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : ...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ static int rtp_h265_unpack_ap(struct rtp_decode_h265_t *unpacker, const uint8_t* ptr, int bytes, uint32_t timestamp) { int r; int n; int len; //uint16_t donl; //uint16_t dond; //donl = unpacker->using_donl_field ? nbo_r16(ptr + 2) : 0; ptr += 2; // PayloadHdr n = 2 /*LEN*/ + (unpacker->using_donl_field ? 2 : 0); r = 0; for (bytes -= 2 /*PayloadHdr*/; 0 == r && bytes > n; bytes -= len + 2) { bytes -= n - 2; // skip DON ptr += n - 2; // skip DON len = nbo_r16(ptr); if (len + 2 > bytes) { assert(0); unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST; unpacker->size = 0; return -EINVAL; // error } assert(H265_TYPE(ptr[2]) >= 0 && H265_TYPE(ptr[2]) < 48); r = unpacker->handler.packet(unpacker->cbparam, ptr + 2, len, timestamp, unpacker->flags); unpacker->flags = 0; unpacker->size = 0; ptr += len + 2; // next NALU n = 2 /*LEN*/ + (unpacker->using_donl_field ? 1 : 0); } return 0 == r ? 1 : r; // packet handled } // 4.4.3. Fragmentation Units (p29) /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | PayloadHdr (Type=49) | FU header | DONL (cond) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| | DONL (cond) | | |-+-+-+-+-+-+-+-+ | | FU payload | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : ...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +---------------+ |0|1|2|3|4|5|6|7| +-+-+-+-+-+-+-+-+ |S|E| FuType | +---------------+ */ static int rtp_h265_unpack_fu(struct rtp_decode_h265_t *unpacker, const uint8_t* ptr, int bytes, uint32_t timestamp) { int r, n; uint8_t fuheader; r = 0; n = 1 /*FU header*/ + (unpacker->using_donl_field ? 4 : 2); if (bytes < n || unpacker->size + bytes - n > RTP_PAYLOAD_MAX_SIZE) { assert(0); return -EINVAL; } if (unpacker->size + bytes - n + 2 /*NALU*/ > unpacker->capacity) { void* p = NULL; int size = unpacker->size + bytes + 2; size += size / 4 > 128000 ? size / 4 : 128000; p = realloc(unpacker->ptr, size); if (!p) { // set packet lost flag unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST; unpacker->size = 0; return -ENOMEM; } unpacker->ptr = (uint8_t*)p; unpacker->capacity = size; } fuheader = ptr[2]; if (FU_START(fuheader)) { #if 0 if (unpacker->size > 0) { unpacker->flags |= RTP_PAYLOAD_FLAG_PACKET_CORRUPT; unpacker->handler.packet(unpacker->cbparam, unpacker->ptr, unpacker->size, unpacker->timestamp, unpacker->flags); unpacker->flags = 0; unpacker->size = 0; // reset } #endif assert(unpacker->capacity > 2); unpacker->size = 2; // NAL unit type byte unpacker->ptr[0] = (FU_NAL(fuheader) << 1) | (ptr[0] & 0x81); // replace NAL Unit Type Bits unpacker->ptr[1] = ptr[1]; assert(H265_TYPE(unpacker->ptr[0]) >= 0 && H265_TYPE(unpacker->ptr[0]) <= 63); } else { if (0 == unpacker->size) { unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST; return 0; // packet discard } assert(unpacker->size > 0); } unpacker->timestamp = timestamp; if (bytes > n) { assert(unpacker->capacity >= unpacker->size + bytes - n); memmove(unpacker->ptr + unpacker->size, ptr + n, bytes - n); unpacker->size += bytes - n; } if (FU_END(fuheader)) { r = unpacker->handler.packet(unpacker->cbparam, unpacker->ptr, unpacker->size, timestamp, unpacker->flags); unpacker->flags = 0; unpacker->size = 0; } return 0 == r ? 1 : r; // packet handled } static int rtp_h265_unpack_input(void* p, const void* packet, int bytes) { int r, nal; const uint8_t* ptr; struct rtp_packet_t pkt; struct rtp_decode_h265_t *unpacker; unpacker = (struct rtp_decode_h265_t *)p; if (!unpacker || 0 != rtp_packet_deserialize(&pkt, packet, bytes) || pkt.payloadlen < (unpacker->using_donl_field ? 5 : 3)) return -EINVAL; if (-1 == unpacker->flags) { unpacker->flags = 0; unpacker->seq = (uint16_t)(pkt.rtp.seq - 1); // disable packet lost } if ((uint16_t)pkt.rtp.seq != (uint16_t)(unpacker->seq + 1)) { unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST; unpacker->size = 0; // discard previous packets } unpacker->seq = (uint16_t)pkt.rtp.seq; assert(pkt.payloadlen > 2); ptr = (const uint8_t*)pkt.payload; nal = H265_TYPE(ptr[0]); if (nal > 50) return 0; // packet discard, Unsupported (HEVC) NAL type switch (nal) { case 48: // aggregated packet (AP) - with two or more NAL units return rtp_h265_unpack_ap(unpacker, ptr, pkt.payloadlen, pkt.rtp.timestamp); case 49: // fragmentation unit (FU) return rtp_h265_unpack_fu(unpacker, ptr, pkt.payloadlen, pkt.rtp.timestamp); case 50: // TODO: 4.4.4. PACI Packets (p32) assert(0); return 0; // packet discard case 32: // video parameter set (VPS) case 33: // sequence parameter set (SPS) case 34: // picture parameter set (PPS) case 39: // supplemental enhancement information (SEI) default: // 4.4.1. Single NAL Unit Packets (p24) r = unpacker->handler.packet(unpacker->cbparam, ptr, pkt.payloadlen, pkt.rtp.timestamp, unpacker->flags); unpacker->flags = 0; unpacker->size = 0; return 0 == r ? 1 : r; // packet handled } } struct rtp_payload_decode_t *rtp_h265_decode() { static struct rtp_payload_decode_t unpacker = { rtp_h265_unpack_create, rtp_h265_unpack_destroy, rtp_h265_unpack_input, }; return &unpacker; }