295 lines
8.4 KiB
C++
295 lines
8.4 KiB
C++
// RFC7798 RTP Payload Format for High Efficiency Video Coding (HEVC)
|
|
|
|
#include "rtp-packet.h"
|
|
#include "rtp-payload-internal.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
/*
|
|
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 || len < 3)
|
|
{
|
|
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;
|
|
}
|