776 lines
27 KiB
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
|