#include "rtp-internal.h" #include "rtp-util.h" #include // 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; 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) { 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; } } 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