// https://aomediacodec.github.io/av1-rtp-spec/#41-rtp-header-usage #include "rtp-packet.h" #include "rtp-profile.h" #include "rtp-payload-helper.h" #include "rtp-payload-internal.h" #include #include #include #include #include #define N_MAX_OBU 255 #define N_RESERVED_OBU_SIZE_FIELD 2 struct rtp_decode_av1_obu_t { int off; int len; }; struct rtp_decode_av1_t { struct rtp_payload_t handler; void* cbparam; int lost; uint16_t seq; // rtp seq uint32_t timestamp; int flags; struct { struct rtp_decode_av1_obu_t* arr; int num, cap; } obu; struct { uint8_t* ptr; int len, cap; } ptr; }; static inline const uint8_t* leb128(const uint8_t* data, size_t bytes, int64_t* size) { size_t i; for (*size = i = 0; i * 7 < 64 && i < bytes;) { *size |= ((int64_t)(data[i] & 0x7F)) << (i * 7); if (0 == (data[i++] & 0x80)) break; } return data + i; } //static inline uint8_t* leb128_write(int64_t size, uint8_t* data, size_t bytes) //{ // size_t i; // assert(3 == bytes && size <= 0x1FFFFF); // for (i = 0; i * 7 < 64 && i < bytes; i++) // { // data[i] = (uint8_t)(size & 0x7F); // size >>= 7; // data[i] |= (size > 0 || i + 1 < bytes)? 0x80 : 0; // } // return data + i; //} static inline int leb128_write(int64_t size, uint8_t* data, int bytes) { int i; for (i = 0; i * 7 < 64 && i < bytes;) { data[i] = (uint8_t)(size & 0x7F); size >>= 7; data[i++] |= size > 0 ? 0x80 : 0; if (0 == size) break; } return i; } static void* rtp_av1_unpack_create(struct rtp_payload_t* handler, void* param) { struct rtp_decode_av1_t* unpacker; unpacker = (struct rtp_decode_av1_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_av1_unpack_destroy(void* p) { struct rtp_decode_av1_t* unpacker; unpacker = (struct rtp_decode_av1_t*)p; if (unpacker->obu.arr) free(unpacker->obu.arr); if (unpacker->ptr.ptr) free(unpacker->ptr.ptr); #if defined(_DEBUG) || defined(DEBUG) memset(unpacker, 0xCC, sizeof(*unpacker)); #endif free(unpacker); } static int rtp_av1_unpack_obu_append(struct rtp_decode_av1_t* unpacker, const uint8_t* data, int bytes, int start) { void* p; int size; int64_t n; uint8_t head; const uint8_t* pend; pend = data + bytes; size = unpacker->ptr.len + bytes + 9 /*obu_size*/ + 2 /*obu temporal delimiter*/; if (size > RTP_PAYLOAD_MAX_SIZE || size < 0 || bytes < 2) return -EINVAL; if (size >= unpacker->ptr.cap) { size += size / 4 > 16000 ? size / 4 : 16000; p = realloc(unpacker->ptr.ptr, size); if (!p) { //unpacker->flags |= RTP_PAYLOAD_FLAG_PACKET_LOST; unpacker->lost = 1; //unpacker->size = 0; return -ENOMEM; } unpacker->ptr.ptr = (uint8_t*)p; unpacker->ptr.cap = size; } if (unpacker->obu.num + start >= unpacker->obu.cap) { if (unpacker->obu.cap >= N_MAX_OBU) return -E2BIG; p = realloc(unpacker->obu.arr, sizeof(struct rtp_decode_av1_obu_t) * (unpacker->obu.cap + 8)); if (!p) return -ENOMEM; memset((struct rtp_decode_av1_obu_t*)p + unpacker->obu.cap, 0, sizeof(struct rtp_decode_av1_obu_t) * 8); unpacker->obu.arr = (struct rtp_decode_av1_obu_t*)p; unpacker->obu.cap += 8; } // add temporal delimiter obu //if (0 == unpacker->ptr.len) //{ // static const uint8_t av1_temporal_delimiter[] = { 0x12, 0x00 }; // assert(0 == unpacker->ptr.len); // memcpy(unpacker->ptr.ptr, av1_temporal_delimiter, sizeof(av1_temporal_delimiter)); // unpacker->ptr.len += sizeof(av1_temporal_delimiter); //} if (start) { unpacker->obu.arr[unpacker->obu.num].off = unpacker->ptr.len; unpacker->obu.arr[unpacker->obu.num].len = 0; // obu_head head = *data++; unpacker->ptr.ptr[unpacker->ptr.len++] = head; if (head & 0x04) { // obu_extension_flag unpacker->ptr.ptr[unpacker->ptr.len++] = *data++; } if (head & 0x02) { // obu_has_size_field data = leb128(data, pend - data, &n); unpacker->ptr.len += leb128_write(n, unpacker->ptr.ptr + unpacker->ptr.len, unpacker->ptr.cap - unpacker->ptr.len); } else { unpacker->ptr.len += N_RESERVED_OBU_SIZE_FIELD; // obu_size } unpacker->obu.num++; } unpacker->obu.arr[unpacker->obu.num-1].len += (int)(intptr_t)(pend - data); // obu memcpy(unpacker->ptr.ptr + unpacker->ptr.len, data, pend - data); unpacker->ptr.len += (int)(intptr_t)(pend - data); return 0; } int rtp_av1_unpack_onframe(struct rtp_decode_av1_t* unpacker) { int i, j, r, n; uint8_t* obu, *data, obu_size_field[9]; int64_t len; r = 0; if (unpacker->obu.num < unpacker->obu.cap && unpacker->obu.arr[0].len > 0 #if !defined(RTP_ENABLE_COURRUPT_PACKET) && 0 == unpacker->lost #endif ) { // write obu length for (i = 0; i < unpacker->obu.num; i++) { obu = unpacker->ptr.ptr + unpacker->obu.arr[i].off; data = obu + ((0x04 & obu[0]) ? 2 : 1); if (0x02 & obu[0]) { // obu_has_size_field assert(unpacker->lost || (leb128(data, 9, &len) && len == unpacker->obu.arr[i].len)); continue; } obu[0] |= 0x02; // obu_has_size_field n = leb128_write(unpacker->obu.arr[i].len, obu_size_field, sizeof(obu_size_field)); if (n != N_RESERVED_OBU_SIZE_FIELD) { memmove(data + n, data + N_RESERVED_OBU_SIZE_FIELD, unpacker->ptr.ptr + unpacker->ptr.len - (data + N_RESERVED_OBU_SIZE_FIELD)); unpacker->ptr.len -= N_RESERVED_OBU_SIZE_FIELD - n; for (j = i + 1; j < unpacker->obu.num; j++) { assert(unpacker->obu.arr[j].off + n > N_RESERVED_OBU_SIZE_FIELD); unpacker->obu.arr[j].off = unpacker->obu.arr[j].off + n - N_RESERVED_OBU_SIZE_FIELD; } } memcpy(data, obu_size_field, n); } // previous packet done r = unpacker->handler.packet(unpacker->cbparam, unpacker->ptr.ptr, unpacker->ptr.len, unpacker->timestamp, unpacker->flags | (unpacker->lost ? RTP_PAYLOAD_FLAG_PACKET_CORRUPT : 0)); // RTP_PAYLOAD_FLAG_PACKET_LOST: miss unpacker->flags &= ~RTP_PAYLOAD_FLAG_PACKET_LOST; // clear packet lost flag } // set packet lost flag on next frame if (unpacker->lost) unpacker->flags |= RTP_PAYLOAD_FLAG_PACKET_LOST; // new frame start unpacker->lost = 0; unpacker->ptr.len = 0; unpacker->obu.num = 0; memset(unpacker->obu.arr, 0, sizeof(struct rtp_decode_av1_obu_t) * unpacker->obu.cap); return r; } /* 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC |M| PT | sequence number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | synchronization source (SSRC) identifier | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | contributing source (CSRC) identifiers | | .... | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | 0x100 | 0x0 | extensions length | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | 0x1(ID) | hdr_length | | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | | | | dependency descriptor (hdr_length #octets) | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | Other rtp header extensions...| +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | AV1 aggr hdr | | +-+-+-+-+-+-+-+-+ | | | | Bytes 2..N of AV1 payload | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : OPTIONAL RTP padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ static int rtp_av1_unpack_input(void* p, const void* packet, int bytes) { int lost; int64_t size; uint8_t z, y, w, n, i; const uint8_t *ptr, *pend; struct rtp_packet_t pkt; struct rtp_decode_av1_t* unpacker; unpacker = (struct rtp_decode_av1_t*)p; if (!unpacker || 0 != rtp_packet_deserialize(&pkt, packet, bytes) || pkt.payloadlen < 1) return -EINVAL; lost = 0; 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)) { lost = 1; unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST; unpacker->ptr.len = 0; // discard previous packets } // check timestamp if (pkt.rtp.timestamp != unpacker->timestamp) { rtp_av1_unpack_onframe(unpacker); // lost: // 0 - packet lost before timestamp change // 1 - packet lost on timestamp changed, can't known losted packet is at old packet tail or new packet start, so two packets mark as packet lost if (0 != lost) unpacker->lost = lost; } unpacker->seq = (uint16_t)pkt.rtp.seq; unpacker->timestamp = pkt.rtp.timestamp; ptr = (const uint8_t *)pkt.payload; pend = ptr + pkt.payloadlen; // AV1 aggregation header /* 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |Z|Y| W |N|-|-|-| +-+-+-+-+-+-+-+-+ */ z = ptr[0] & 0x80; // MUST be set to 1 if the first OBU element is an OBU fragment that is a continuation of an OBU fragment from the previous packet, and MUST be set to 0 otherwise. y = ptr[0] & 0x40; // MUST be set to 1 if the last OBU element is an OBU fragment that will continue in the next packet, and MUST be set to 0 otherwise. w = (ptr[0] & 0x30) >> 4; // two bit field that describes the number of OBU elements in the packet. This field MUST be set equal to 0 or equal to the number of OBU elements contained in the packet. If set to 0, each OBU element MUST be preceded by a length field. n = ptr[0] & 0x08; // MUST be set to 1 if the packet is the first packet of a coded video sequence, and MUST be set to 0 otherwise. (void)y; // if N equals 1 then Z must equal 0. assert(!n || 0 == z); if (0 == z && 1 == n) { // new video codec sequence rtp_av1_unpack_onframe(unpacker); } for (i = 1, ptr++; ptr < pend; ptr += size, i++) { if (i < w || 0 == w) { ptr = leb128(ptr, pend - ptr, &size); } else { size = pend - ptr; } // skip fragment frame OBU size if (ptr + size > pend) { //assert(0); //unpacker->size = 0; unpacker->lost = 1; //unpacker->flags |= RTP_PAYLOAD_FLAG_PACKET_LOST; return -1; // invalid packet } rtp_av1_unpack_obu_append(unpacker, ptr, (int)size, (1 != i || 0 == z) ? 1 : 0); } // The RTP header Marker bit MUST be set equal to 0 // if the packet is not the last packet of the temporal unit, // it SHOULD be set equal to 1 otherwise. if (pkt.rtp.m) { rtp_av1_unpack_onframe(unpacker); } return 1; // packet handled } struct rtp_payload_decode_t *rtp_av1_decode() { static struct rtp_payload_decode_t unpacker = { rtp_av1_unpack_create, rtp_av1_unpack_destroy, rtp_av1_unpack_input, }; return &unpacker; }