// RFC6184 RTP Payload Format for H.264 Video #include "rtp-packet.h" #include "rtp-payload-internal.h" #include #include #include #include #define H264_NAL(v) (v & 0x1F) #define FU_START(v) (v & 0x80) #define FU_END(v) (v & 0x40) #define FU_NAL(v) (v & 0x1F) struct rtp_decode_h264_t { struct rtp_payload_t handler; void* cbparam; uint16_t seq; // rtp seq uint32_t timestamp; uint8_t* ptr; int size, capacity; int flags; }; static void* rtp_h264_unpack_create(struct rtp_payload_t *handler, void* param) { struct rtp_decode_h264_t *unpacker; unpacker = (struct rtp_decode_h264_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_h264_unpack_destroy(void* p) { struct rtp_decode_h264_t *unpacker; unpacker = (struct rtp_decode_h264_t *)p; if(unpacker->ptr) free(unpacker->ptr); #if defined(_DEBUG) || defined(DEBUG) memset(unpacker, 0xCC, sizeof(*unpacker)); #endif free(unpacker); } // 5.7.1. Single-Time Aggregation Packet (STAP) (p23) /* 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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |STAP-B NAL HDR | DON | NALU 1 Size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 Size | NALU 1 HDR | NALU 1 Data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : : + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | NALU 2 Size | NALU 2 HDR | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 Data | : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : ...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ static int rtp_h264_unpack_stap(struct rtp_decode_h264_t *unpacker, const uint8_t* ptr, int bytes, uint32_t timestamp, int stap_b) { int r, n; uint16_t len; uint16_t don; r = 0; n = stap_b ? 3 : 1; if (bytes < n) { assert(0); return -EINVAL; // error } don = stap_b ? nbo_r16(ptr + 1) : 0; ptr += n; // STAP-A / STAP-B HDR + DON for(bytes -= n; 0 == r && bytes > 2; bytes -= len + 2) { len = nbo_r16(ptr); if(len + 2 > bytes) { assert(0); unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST; unpacker->size = 0; return -EINVAL; // error } assert(H264_NAL(ptr[2]) > 0 && H264_NAL(ptr[2]) < 24); r = unpacker->handler.packet(unpacker->cbparam, ptr + 2, len, timestamp, unpacker->flags); unpacker->flags = 0; unpacker->size = 0; ptr += len + 2; // next NALU don = (don + 1) % 65536; } return 0 == r ? 1 : r; // packet handled } // 5.7.2. Multi-Time Aggregation Packets (MTAPs) (p27) /* 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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |MTAP16 NAL HDR | decoding order number base | NALU 1 Size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 Size | NALU 1 DOND | NALU 1 TS offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 1 HDR | NALU 1 DATA | +-+-+-+-+-+-+-+-+ + : : + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | NALU 2 SIZE | NALU 2 DOND | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NALU 2 TS offset | NALU 2 HDR | NALU 2 DATA | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : ...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ static int rtp_h264_unpack_mtap(struct rtp_decode_h264_t *unpacker, const uint8_t* ptr, int bytes, uint32_t timestamp, int n) { int r; //uint16_t dond; uint16_t donb; uint16_t len; uint32_t ts; r = 0; if (bytes < 3) { assert(0); return -EINVAL; // error } donb = nbo_r16(ptr + 1); ptr += 3; // MTAP16/MTAP24 HDR + DONB for(bytes -= 3; 0 == r && n + 3 < bytes; bytes -= len + 2) { len = nbo_r16(ptr); if(len + 2 > bytes || len < 1 /*DOND*/ + n /*TS offset*/ + 1 /*NALU*/) { assert(0); unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST; unpacker->size = 0; return -EINVAL; // error } //dond = (ptr[2] + donb) % 65536; ts = (uint16_t)nbo_r16(ptr + 3); if (3 == n) ts = (ts << 16) | ptr[5]; // MTAP24 // if the NALU-time is larger than or equal to the RTP timestamp of the packet, // then the timestamp offset equals (the NALU - time of the NAL unit - the RTP timestamp of the packet). // If the NALU - time is smaller than the RTP timestamp of the packet, // then the timestamp offset is equal to the NALU - time + (2 ^ 32 - the RTP timestamp of the packet). ts += timestamp; // wrap 1 << 32 assert(H264_NAL(ptr[n + 3]) > 0 && H264_NAL(ptr[n + 3]) < 24); r = unpacker->handler.packet(unpacker->cbparam, ptr + 1 + n, len - 1 - n, ts, unpacker->flags); unpacker->flags = 0; unpacker->size = 0; ptr += len + 2; // next NALU } return 0 == r ? 1 : r; // packet handled } // 5.8. Fragmentation Units (FUs) (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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | FU indicator | FU header | DON | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| | | | FU payload | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : ...OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ static int rtp_h264_unpack_fu(struct rtp_decode_h264_t *unpacker, const uint8_t* ptr, int bytes, uint32_t timestamp, int fu_b) { int r, n; uint8_t fuheader; //uint16_t don; r = 0; n = fu_b ? 4 : 2; if (bytes < n || unpacker->size + bytes - n > RTP_PAYLOAD_MAX_SIZE) { assert(0); return -EINVAL; // error } if (unpacker->size + bytes - n + 1 /*NALU*/ > unpacker->capacity) { void* p = NULL; int size = unpacker->size + bytes + 1; 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; // error } unpacker->ptr = (uint8_t*)p; unpacker->capacity = size; } fuheader = ptr[1]; //don = nbo_r16(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 unpacker->size = 1; // NAL unit type byte unpacker->ptr[0] = (ptr[0]/*indicator*/ & 0xE0) | (fuheader & 0x1F); assert(H264_NAL(unpacker->ptr[0]) > 0 && H264_NAL(unpacker->ptr[0]) < 24); } 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)) { if(unpacker->size > 0) r = unpacker->handler.packet(unpacker->cbparam, unpacker->ptr, unpacker->size, timestamp, unpacker->flags); unpacker->flags = 0; unpacker->size = 0; // reset } return 0 == r ? 1 : r; // packet handled } static int rtp_h264_unpack_input(void* p, const void* packet, int bytes) { int r; uint8_t nalt; struct rtp_packet_t pkt; struct rtp_decode_h264_t *unpacker; unpacker = (struct rtp_decode_h264_t *)p; if(!unpacker || 0 != rtp_packet_deserialize(&pkt, packet, bytes) || pkt.payloadlen < 1) 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; nalt = ((unsigned char *)pkt.payload)[0]; switch(nalt & 0x1F) { case 0: // reserved case 31: // reserved assert(0); return 0; // packet discard case 24: // STAP-A return rtp_h264_unpack_stap(unpacker, (const uint8_t*)pkt.payload, pkt.payloadlen, pkt.rtp.timestamp, 0); case 25: // STAP-B return rtp_h264_unpack_stap(unpacker, (const uint8_t*)pkt.payload, pkt.payloadlen, pkt.rtp.timestamp, 1); case 26: // MTAP16 return rtp_h264_unpack_mtap(unpacker, (const uint8_t*)pkt.payload, pkt.payloadlen, pkt.rtp.timestamp, 2); case 27: // MTAP24 return rtp_h264_unpack_mtap(unpacker, (const uint8_t*)pkt.payload, pkt.payloadlen, pkt.rtp.timestamp, 3); case 28: // FU-A return rtp_h264_unpack_fu(unpacker, (const uint8_t*)pkt.payload, pkt.payloadlen, pkt.rtp.timestamp, 0); case 29: // FU-B return rtp_h264_unpack_fu(unpacker, (const uint8_t*)pkt.payload, pkt.payloadlen, pkt.rtp.timestamp, 1); default: // 1-23 NAL unit r = unpacker->handler.packet(unpacker->cbparam, (const uint8_t*)pkt.payload, 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_h264_decode() { static struct rtp_payload_decode_t unpacker = { rtp_h264_unpack_create, rtp_h264_unpack_destroy, rtp_h264_unpack_input, }; return &unpacker; }