stream-deploy/ZLM/3rdpart/media-server/librtp/source/rtcp-xr.c

776 lines
27 KiB
C++

#include "rtp-internal.h"
#include "rtp-util.h"
#include <errno.h>
// https://www.iana.org/assignments/rtcp-xr-block-types/rtcp-xr-block-types.xhtml
/*
BT Name Reference
1 Loss RLE Report Block [RFC3611]
2 Duplicate RLE Report Block [RFC3611]
3 Packet Receipt Times Report Block [RFC3611]
4 Receiver Reference Time Report Block [RFC3611]
5 DLRR Report Block [RFC3611]
6 Statistics Summary Report Block [RFC3611]
7 VoIP Metrics Report Block [RFC3611]
8 RTCP XR [RFC5093]
9 Texas Instruments Extended VoIP Quality Block [http://focus.ti.com/general/docs/bcg/bcgdoccenter.tsp?templateId=6116&navigationId=12078#42][David_Lide]
10 Post-repair Loss RLE Report Block [RFC5725]
11 Multicast Acquisition Report Block [RFC6332]
12 IDMS Report Block [RFC7272]
13 ECN Summary Report [RFC6679]
14 Measurement Information Block [RFC6776]
15 Packet Delay Variation Metrics Block [RFC6798]
16 Delay Metrics Block [RFC6843]
17 Burst/Gap Loss Summary Statistics Block [RFC7004]
18 Burst/Gap Discard Summary Statistics Block [RFC7004]
19 Frame Impairment Statistics Summary [RFC7004]
20 Burst/Gap Loss Metrics Block [RFC6958]
21 Burst/Gap Discard Metrics Block [RFC7003][RFC Errata 3735]
22 MPEG2 Transport Stream PSI-Independent Decodability Statistics Metrics Block [RFC6990]
23 De-Jitter Buffer Metrics Block [RFC7005]
24 Discard Count Metrics Block [RFC7002]
25 DRLE (Discard RLE Report) [RFC7097]
26 BDR (Bytes Discarded Report) [RFC7243]
27 RFISD (RTP Flows Initial Synchronization Delay) [RFC7244]
28 RFSO (RTP Flows Synchronization Offset Metrics Block) [RFC7244]
29 MOS Metrics Block [RFC7266]
30 LCB (Loss Concealment Metrics Block) [RFC7294, Section 4.1]
31 CSB (Concealed Seconds Metrics Block) [RFC7294, Section 4.1]
32 MPEG2 Transport Stream PSI Decodability Statistics Metrics Block [RFC7380]
33 Post-Repair Loss Count Metrics Report Block [RFC7509]
34 Video Loss Concealment Metric Report Block [RFC7867]
35 Independent Burst/Gap Discard Metrics Block [RFC8015]
36-254 Unassigned
255 Reserved for future extensions [RFC3611]
*/
/*
Parameter Reference
pkt-loss-rle [RFC3611]
pkt-dup-rle [RFC3611]
pkt-rcpt-times [RFC3611]
stat-summary [RFC3611]
voip-metrics [RFC3611]
rcvr-rtt [RFC3611]
post-repair-loss-rle [RFC5725]
grp-sync [http://www.etsi.org/deliver/etsi_ts/183000_183099/183063/][ETSI 183 063][Miguel_Angel_Reina_Ortega]
multicast-acq [RFC6332]
ecn-sum [RFC6679]
pkt-dly-var [RFC6798]
delay [RFC6843]
burst-gap-loss-stat [RFC7004]
burst-gap-discard-stat [RFC7004]
frame-impairment-stat [RFC7004]
burst-gap-loss [RFC6958]
burst-gap-discard [RFC7003]
ts-psi-indep-decodability [RFC6990]
de-jitter-buffer [RFC7005]
pkt-discard-count [RFC7002]
discard-rle [RFC7097]
discard-bytes [RFC7243]
rtp-flow-init-syn-delay [RFC7244]
rtp-flow-syn-offset [RFC7244]
mos-metric [RFC7266]
loss-conceal [RFC7294]
conc-sec [RFC7294]
ts-psi-decodability [RFC7380]
post-repair-loss-count [RFC7509]
video-loss-concealment [RFC7867]
ind-burst-gap-discard [RFC8015]
*/
static int rtcp_xr_rrt_pack(uint64_t ntp, uint8_t* ptr, uint32_t bytes);
static int rtcp_xr_dlrr_pack(const rtcp_dlrr_t* dlrr, int count, uint8_t* ptr, uint32_t bytes);
static int rtcp_xr_ecn_pack(const rtcp_ecn_t* ecn, uint8_t* ptr, uint32_t bytes);
static int rtcp_xr_lrle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_drle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_prt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_rrt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_dlrr_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
static int rtcp_xr_ecn_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes);
// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.1
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=1 | rsvd. | T | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| begin_seq | end_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk 1 | chunk 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: ... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk n-1 | chunk n |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_lrle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
uint32_t i, j;
uint32_t source;
uint32_t len, num;
uint16_t seq, end;
uint32_t chunk;
uint8_t* v, v0[32];
len = nbo_r16(ptr + 2);
if (bytes < 8 || (len + 1) * 4 > bytes)
return -1;
len *= 4; // to bytes
if (len < 8)
return 0;
source = nbo_r32(ptr + 4);
seq = nbo_r16(ptr + 8);
end = nbo_r16(ptr + 10);
num = end - seq;
if ((num + 7) / 8 > sizeof(v0))
{
v = calloc((num + 7) / 8, sizeof(*v));
if (!v) return -ENOMEM;
}
else
{
v = v0;
memset(v, 0, (num + 7) / 8 * sizeof(v[0]));
}
ptr += 8;
len -= 8;
for(i = 0; len > 2; ptr += 2, len -= 2)
{
chunk = nbo_r16(ptr);
if (0 == (0x8000 & chunk))
{
// Run Length Chunk
for (j = 0; j < (chunk & 0x3FFF) && i < num; j++, i++)
{
if(0x4000 & chunk)
v[i/8] |= 1 << (7-(i%8));
}
}
else
{
// Bit Vector Chunk
for (j = 0; j < 15 && i < num; j++, i++)
{
if (chunk & (1 << (14 - j)))
v[i / 8] |= 1 << (7 - (i % 8));
}
}
}
msg->u.xr.u.rle.source = source;
msg->u.xr.u.rle.begin = seq;
msg->u.xr.u.rle.end = end;
msg->u.xr.u.rle.chunk = v;
msg->u.xr.u.rle.count = num;
ctx->handler.on_rtcp(ctx->cbparam, msg);
(void)ctx, (void)header;
if (v && v != v0)
free(v);
return 0;
}
// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.2
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=2 | rsvd. | T | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| begin_seq | end_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk 1 | chunk 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: ... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk n-1 | chunk n |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_drle_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
uint32_t i, j;
uint32_t source;
uint32_t len, num;
uint16_t seq, end;
uint32_t chunk;
uint8_t* v, v0[32];
len = nbo_r16(ptr + 2);
if (bytes < 8 || (len + 1) * 4 > bytes)
return -1;
len *= 4; // to bytes
if (len < 8)
return 0;
source = nbo_r32(ptr + 4);
seq = nbo_r16(ptr + 8);
end = nbo_r16(ptr + 10);
num = end - seq;
if ((num + 7) / 8 > sizeof(v0))
{
v = calloc((num + 7) / 8, sizeof(*v));
if (!v) return -ENOMEM;
}
else
{
v = v0;
memset(v, 0, (num + 7) / 8 * sizeof(v[0]));
}
ptr += 8;
len -= 8;
for (i = 0; len > 2; ptr += 2, len -= 2)
{
chunk = nbo_r16(ptr);
if (0 == (0x8000 & chunk))
{
// Run Length Chunk
for (j = 0; j < (chunk & 0x3FFF) && i < num; j++, i++)
{
if (0x4000 & chunk)
v[i / 8] |= 1 << (7 - (i % 8));
}
}
else
{
// Bit Vector Chunk
for (j = 0; j < 15 && i < num; j++, i++)
{
if (chunk & (1 << (14 - j)))
v[i / 8] |= 1 << (7 - (i % 8));
}
}
}
msg->u.xr.u.rle.source = source;
msg->u.xr.u.rle.begin = seq;
msg->u.xr.u.rle.end = end;
msg->u.xr.u.rle.chunk = v;
msg->u.xr.u.rle.count = num;
ctx->handler.on_rtcp(ctx->cbparam, msg);
(void)ctx, (void)header;
if (v && v != v0)
free(v);
return 0;
}
// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.3
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=3 | rsvd. | T | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| begin_seq | end_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Receipt time of packet begin_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Receipt time of packet (begin_seq + 1) mod 65536 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: ... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Receipt time of packet (end_seq - 1) mod 65536 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_prt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
uint32_t i;
uint32_t source;
uint32_t len, num;
uint16_t seq, end;
uint32_t* timestamp, timestamp0[32];
len = nbo_r16(ptr + 2);
if (bytes < 8 || (len + 1) * 4 > bytes)
return -1;
len *= 4; // to bytes
if (len < 8)
return 0;
source = nbo_r32(ptr + 4);
seq = nbo_r16(ptr + 8);
end = nbo_r16(ptr + 10);
num = end - seq;
if (num > sizeof(timestamp0)/sizeof(timestamp0[0]))
{
timestamp = calloc(num, sizeof(*timestamp));
if (!timestamp) return -ENOMEM;
}
else
{
timestamp = timestamp0;
memset(timestamp, 0, num * sizeof(timestamp[0]));
}
ptr += 8;
len -= 8;
for (i = 0; len > 4; ptr += 4, len -= 4)
{
timestamp[i] = nbo_r32(ptr);
}
msg->u.xr.u.prt.source = source;
msg->u.xr.u.prt.begin = seq;
msg->u.xr.u.prt.end = end;
msg->u.xr.u.prt.timestamp = timestamp;
msg->u.xr.u.prt.count = num;
ctx->handler.on_rtcp(ctx->cbparam, msg);
(void)ctx, (void)header;
if (timestamp && timestamp != timestamp0)
free(timestamp);
return 0;
}
// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.4
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=4 | reserved | block length = 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NTP timestamp, most significant word |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NTP timestamp, least significant word |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtcp_xr_rrt_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
uint32_t len;
uint64_t ntp;
len = nbo_r16(ptr + 2);
if (bytes < 12 || (len + 1) * 4 > bytes)
return -1;
ntp = nbo_r32(ptr + 4);
ntp = (ntp << 32) | nbo_r32(ptr + 8);
msg->u.xr.u.rrt = ntp;
ctx->handler.on_rtcp(ctx->cbparam, msg);
(void)ctx, (void)header;
return 0;
}
static int rtcp_xr_rrt_pack(uint64_t ntp, uint8_t* ptr, uint32_t bytes)
{
if (bytes < 12)
return -1;
nbo_w32(ptr, (RTCP_XR_RRT << 24) | 2);
nbo_w32(ptr + 4, (uint32_t)(ntp >> 32));
nbo_w32(ptr + 8, (uint32_t)ntp);
return 12;
}
// https://www.rfc-editor.org/rfc/rfc3611.html#section-4.5
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=5 | reserved | block length |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| SSRC_1 (SSRC of first receiver) | sub-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
| last RR (LRR) | 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| delay since last RR (DLRR) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| SSRC_2 (SSRC of second receiver) | sub-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
: ... : 2
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*/
static int rtcp_xr_dlrr_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
uint32_t i;
uint32_t len, num;
rtcp_dlrr_t* dlrr, dlrr0[32];
len = nbo_r16(ptr + 2);
if (bytes < 8 || (len + 1) * 4 > bytes)
return -1;
num = len / 3;
if (num > sizeof(dlrr0))
{
dlrr = calloc(num, sizeof(*dlrr));
if (!dlrr) return -ENOMEM;
}
else
{
dlrr = dlrr0;
memset(dlrr, 0, num * sizeof(dlrr[0]));
}
ptr += 4;
for (i = 0; i < num; i++, ptr += 12)
{
dlrr[i].ssrc = nbo_r32(ptr + 0);
dlrr[i].lrr = nbo_r32(ptr + 4);
dlrr[i].dlrr = nbo_r32(ptr + 8);
}
msg->u.xr.u.dlrr.dlrr = dlrr;
msg->u.xr.u.dlrr.count = num;
ctx->handler.on_rtcp(ctx->cbparam, msg);
(void)ctx, (void)header;
if (dlrr && dlrr != dlrr0)
free(dlrr);
return 0;
}
static int rtcp_xr_dlrr_pack(const rtcp_dlrr_t* dlrr, int count, uint8_t* ptr, uint32_t bytes)
{
int i;
if ((int)bytes < 4 + count * 12)
return - 1;
nbo_w32(ptr, (RTCP_XR_DLRR << 24) | (count * 3));
bytes -= 4;
ptr += 4;
for (i = 0; i < count && bytes >= 12; i++)
{
nbo_w32(ptr, dlrr[i].ssrc);
nbo_w32(ptr + 4, dlrr[i].lrr);
nbo_w32(ptr + 8, dlrr[i].dlrr);
bytes -= 12;
ptr += 12;
}
return 4 + i * 12;
}
// https://www.rfc-editor.org/rfc/rfc7097.html#section-5
// rtcp-xr: discard-rle
/*
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=25 |rsvd |E| T | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| begin_seq | end_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk 1 | chunk 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: ... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk n-1 | chunk n |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
// https://datatracker.ietf.org/doc/html/rfc7243#section-5
// rtcp-xr: discard-bytes
/*
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=26 | I |E|Reserved | Block length=2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Number of RTP payload bytes discarded |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
// https://www.rfc-editor.org/rfc/rfc6679.html#section-5.2
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=13 | Reserved | Block Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of Media Sender |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ECT (0) Counter |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ECT (1) Counter |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ECN-CE Counter | not-ECT Counter |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Lost Packets Counter | Duplication Counter |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 4: RTCP XR ECN Summary Report
*/
static int rtcp_xr_ecn_unpack(struct rtp_context* ctx, const rtcp_header_t* header, struct rtcp_msg_t* msg, const uint8_t* ptr, size_t bytes)
{
uint32_t len;
rtcp_ecn_t ecn;
len = nbo_r16(ptr + 2);
if (bytes < 24 || (len + 1) * 4 > bytes)
return -1;
ecn.ext_highest_seq = nbo_r32(ptr); // ssrc
ecn.ect[0] = nbo_r32(ptr + 4);
ecn.ect[1] = nbo_r32(ptr + 8);
ecn.ect_ce_counter = nbo_r16(ptr + 12);
ecn.not_ect_counter = nbo_r16(ptr + 14);
ecn.lost_packets_counter = nbo_r16(ptr + 16);
ecn.duplication_counter = nbo_r16(ptr + 18);
memcpy(&msg->u.xr.u.ecn, &ecn, sizeof(msg->u.xr.u.ecn));
ctx->handler.on_rtcp(ctx->cbparam, msg);
(void)ctx, (void)header;
return 0;
}
static int rtcp_xr_ecn_pack(const rtcp_ecn_t* ecn, uint8_t* ptr, uint32_t bytes)
{
if (bytes < 24)
return -1;
nbo_w32(ptr, (RTCP_XR_ECN << 24) | 5);
nbo_w32(ptr + 4, ecn->ext_highest_seq);
nbo_w32(ptr + 8, ecn->ect[0]);
nbo_w32(ptr + 12, ecn->ect[1]);
nbo_w16(ptr + 16, ecn->ect_ce_counter);
nbo_w16(ptr + 18, ecn->not_ect_counter);
nbo_w16(ptr + 20, ecn->lost_packets_counter);
nbo_w16(ptr + 22, ecn->duplication_counter);
return 24;
}
/*
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|reserved | PT=XR=207 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: report blocks :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT | type-specific | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: type-specific block contents :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
void rtcp_xr_unpack(struct rtp_context* ctx, const rtcp_header_t* header, const uint8_t* ptr, size_t bytes)
{
int r;
size_t len;
struct rtcp_msg_t msg;
struct rtp_member* sender;
if (bytes < 8 /*sizeof(rtcp_xr_t)*/)
{
assert(0);
return;
}
msg.ssrc = nbo_r32(ptr);
sender = rtp_sender_fetch(ctx, msg.ssrc);
if (!sender) return; // error
assert(sender != ctx->self);
r = 0;
ptr += 4;
bytes -= 4;
while (bytes >= 4)
{
len = nbo_r16(ptr + 2);
if (len * 4 > bytes - 4)
break; // invalid
msg.type = RTCP_XR | (ptr[0] << 8);
switch (ptr[0])
{
case RTCP_XR_LRLE:
r = rtcp_xr_lrle_unpack(ctx, header, &msg, ptr, bytes);
break;
case RTCP_XR_DRLE:
r = rtcp_xr_drle_unpack(ctx, header, &msg, ptr, bytes);
break;
case RTCP_XR_PRT:
r = rtcp_xr_prt_unpack(ctx, header, &msg, ptr, bytes);
break;
case RTCP_XR_RRT:
r = rtcp_xr_rrt_unpack(ctx, header, &msg, ptr, bytes);
break;
case RTCP_XR_DLRR:
r = rtcp_xr_dlrr_unpack(ctx, header, &msg, ptr, bytes);
break;
case RTCP_XR_ECN:
r = rtcp_xr_ecn_unpack(ctx, header, &msg, ptr, bytes);
break;
default:
//assert(0);
r = 0; // ignore
break;
}
ptr += len;
bytes -= len;
}
return;
}
int rtcp_xr_pack(struct rtp_context* ctx, uint8_t* data, int bytes, enum rtcp_xr_type_t id, const rtcp_xr_t* xr)
{
int r;
rtcp_header_t header;
(void)ctx;
if (bytes < 4 + 4)
return 4 + 4;
switch (id)
{
case RTCP_XR_RRT:
r = rtcp_xr_rrt_pack(xr->u.rrt, data + 8, bytes - 8);
break;
case RTCP_XR_DLRR:
r = rtcp_xr_dlrr_pack(xr->u.dlrr.dlrr, xr->u.dlrr.count, data + 8, bytes - 8);
break;
case RTCP_XR_ECN:
r = rtcp_xr_ecn_pack(&xr->u.ecn, data + 8, bytes - 8);
break;
case RTCP_XR_LRLE:
case RTCP_XR_DRLE:
case RTCP_XR_PRT:
default:
assert(0);
return -1;
}
header.v = 2;
header.p = 0;
header.pt = RTCP_XR;
header.rc = id;
header.length = (r + 4 + 3) / 4;
nbo_write_rtcp_header(data, &header);
nbo_w32(data + 4, ctx->self->ssrc);
//nbo_w32(data + 4, xr->sender);
//assert(8 == (header.length + 1) * 4);
return header.length * 4 + 4;
}
#if defined(_DEBUG) || defined(DEBUG)
static void rtcp_on_xr_test(void* param, const struct rtcp_msg_t* msg)
{
int r;
static uint8_t buffer[1400];
switch (msg->type & 0xFF)
{
case RTCP_XR:
switch ((msg->type >> 8) & 0xFF)
{
case RTCP_XR_RRT:
assert(0x1234567823456789 == msg->u.xr.u.rrt);
r = rtcp_xr_rrt_pack(msg->u.xr.u.rrt, buffer, sizeof(buffer));
assert(12 == r && 0 == memcmp(buffer, param, r));
break;
case RTCP_XR_DLRR:
assert(1 == msg->u.xr.u.dlrr.count);
assert(0x12345678 == msg->u.xr.u.dlrr.dlrr[0].ssrc && 0x23344556 == msg->u.xr.u.dlrr.dlrr[0].lrr && 0x33343536 == msg->u.xr.u.dlrr.dlrr[0].dlrr);
r = rtcp_xr_dlrr_pack(msg->u.xr.u.dlrr.dlrr, msg->u.xr.u.dlrr.count, buffer, sizeof(buffer));
assert(16 == r && 0 == memcmp(buffer, param, r));
break;
case RTCP_XR_ECN:
r = rtcp_xr_ecn_pack(&msg->u.xr.u.ecn, buffer, sizeof(buffer));
assert(r > 0 && 0 == memcmp(buffer, param, r));
default:
break;
}
break;
default:
assert(0);
}
}
static void rtcp_rtpfb_rrt_test(void)
{
const uint8_t data[] = { 0x04, 0x00, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89 };
struct rtcp_msg_t msg;
struct rtp_context rtp;
rtp.handler.on_rtcp = rtcp_on_xr_test;
rtp.cbparam = (void*)data;
msg.type = (RTCP_XR_RRT << 8) | RTCP_XR;
assert(0 == rtcp_xr_rrt_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}
static void rtcp_rtpfb_dlrr_test(void)
{
const uint8_t data[] = { 0x05, 0x00, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, 0x23, 0x34, 0x45, 0x56, 0x33, 0x34, 0x35, 0x36 };
struct rtcp_msg_t msg;
struct rtp_context rtp;
rtp.handler.on_rtcp = rtcp_on_xr_test;
rtp.cbparam = (void*)data;
msg.type = (RTCP_XR_DLRR << 8) | RTCP_XR;
assert(0 == rtcp_xr_dlrr_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}
static void rtcp_rtpfb_ecn_test(void)
{
const uint8_t data[] = { 0x00 };
struct rtcp_msg_t msg;
struct rtp_context rtp;
rtp.handler.on_rtcp = rtcp_on_xr_test;
rtp.cbparam = (void*)data;
msg.type = (RTCP_XR_ECN << 8) | RTCP_XR;
assert(0 == rtcp_xr_ecn_unpack(&rtp, NULL, &msg, data, sizeof(data)));
}
void rtcp_xr_test(void)
{
rtcp_rtpfb_rrt_test();
rtcp_rtpfb_dlrr_test();
//rtcp_rtpfb_ecn_test();
}
#endif