Skip to content

Commit f5fc469

Browse files
author
yngrtc
committed
add ReceptionReport
1 parent 406e2c5 commit f5fc469

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed

Sources/RTCP/ReceptionReport.swift

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftRTC open source project
4+
//
5+
// Copyright (c) 2024 ngRTC and the SwiftRTC project authors
6+
// Licensed under MIT License
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftRTC project authors
10+
//
11+
// SPDX-License-Identifier: MIT
12+
//
13+
//===----------------------------------------------------------------------===//
14+
import NIOCore
15+
import Shared
16+
17+
let recptionReportLength: Int = 24
18+
let fractionLostOffset: Int = 4
19+
let totalLostOffset: Int = 5
20+
let lastSeqOffset: Int = 8
21+
let jitterOffset: Int = 12
22+
let lastSrOffset: Int = 16
23+
let delayOffset: Int = 20
24+
25+
/// A ReceptionReport block conveys statistics on the reception of RTP packets
26+
/// from a single synchronization source.
27+
public struct ReceptionReport: Equatable {
28+
/// The SSRC identifier of the source to which the information in this
29+
/// reception report block pertains.
30+
public var ssrc: UInt32
31+
/// The fraction of RTP data packets from source SSRC lost since the
32+
/// previous SR or RR packet was sent, expressed as a fixed point
33+
/// number with the binary point at the left edge of the field.
34+
public var fractionLost: UInt8
35+
/// The total number of RTP data packets from source SSRC that have
36+
/// been lost since the beginning of reception.
37+
public var totalLost: UInt32
38+
/// The least significant 16 bits contain the highest sequence number received
39+
/// in an RTP data packet from source SSRC, and the most significant 16 bits extend
40+
/// that sequence number with the corresponding count of sequence number cycles.
41+
public var lastSequenceNumber: UInt32
42+
/// An estimate of the statistical variance of the RTP data packet
43+
/// interarrival time, measured in timestamp units and expressed as an
44+
/// unsigned integer.
45+
public var jitter: UInt32
46+
/// The middle 32 bits out of 64 in the NTP timestamp received as part of
47+
/// the most recent RTCP sender report (SR) packet from source SSRC. If no
48+
/// SR has been received yet, the field is set to zero.
49+
public var lastSenderReport: UInt32
50+
/// The delay, expressed in units of 1/65536 seconds, between receiving the
51+
/// last SR packet from source SSRC and sending this reception report block.
52+
/// If no SR packet has been received yet from SSRC, the field is set to zero.
53+
public var delay: UInt32
54+
}
55+
56+
extension ReceptionReport: Packet {
57+
public func header() -> Header {
58+
Header()
59+
}
60+
61+
public func destinationSsrc() -> [UInt32] {
62+
[]
63+
}
64+
65+
public func rawSize() -> Int {
66+
recptionReportLength
67+
}
68+
}
69+
70+
extension ReceptionReport: Unmarshal {
71+
/// unmarshal decodes the ReceptionReport from binary
72+
public static func unmarshal(_ buf: ByteBuffer) throws -> (Self, Int) {
73+
if buf.readableBytes < recptionReportLength {
74+
throw RtcpError.errPacketTooShort
75+
}
76+
77+
/*
78+
* 0 1 2 3
79+
* 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
80+
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
81+
* | SSRC |
82+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
83+
* | fraction lost | cumulative number of packets lost |
84+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85+
* | extended highest sequence number received |
86+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
87+
* | interarrival jitter |
88+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
89+
* | last SR (LSR) |
90+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91+
* | delay since last SR (DLSR) |
92+
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
93+
*/
94+
var reader = buf.slice()
95+
guard let ssrc: UInt32 = reader.readInteger() else {
96+
throw RtcpError.errPacketTooShort
97+
}
98+
guard let fractionLost: UInt8 = reader.readInteger() else {
99+
throw RtcpError.errPacketTooShort
100+
}
101+
102+
guard let t0: UInt8 = reader.readInteger() else {
103+
throw RtcpError.errPacketTooShort
104+
}
105+
guard let t1: UInt8 = reader.readInteger() else {
106+
throw RtcpError.errPacketTooShort
107+
}
108+
guard let t2: UInt8 = reader.readInteger() else {
109+
throw RtcpError.errPacketTooShort
110+
}
111+
// TODO: The type of `total_lost` should be `i32`, per the RFC:
112+
// The total number of RTP data packets from source SSRC_n that have
113+
// been lost since the beginning of reception. This number is
114+
// defined to be the number of packets expected less the number of
115+
// packets actually received, where the number of packets received
116+
// includes any which are late or duplicates. Thus, packets that
117+
// arrive late are not counted as lost, and the loss may be negative
118+
// if there are duplicates. The number of packets expected is
119+
// defined to be the extended last sequence number received, as
120+
// defined next, less the initial sequence number received. This may
121+
// be calculated as shown in Appendix A.3.
122+
let totalLost: UInt32 = UInt32(t2) | UInt32(t1) << 8 | UInt32(t0) << 16
123+
124+
guard let lastSequenceNumber: UInt32 = reader.readInteger() else {
125+
throw RtcpError.errPacketTooShort
126+
}
127+
guard let jitter: UInt32 = reader.readInteger() else {
128+
throw RtcpError.errPacketTooShort
129+
}
130+
guard let lastSenderReport: UInt32 = reader.readInteger() else {
131+
throw RtcpError.errPacketTooShort
132+
}
133+
guard let delay: UInt32 = reader.readInteger() else {
134+
throw RtcpError.errPacketTooShort
135+
}
136+
137+
return (
138+
ReceptionReport(
139+
ssrc: ssrc,
140+
fractionLost: fractionLost,
141+
totalLost: totalLost,
142+
lastSequenceNumber: lastSequenceNumber,
143+
jitter: jitter,
144+
lastSenderReport: lastSenderReport,
145+
delay: delay
146+
), reader.readerIndex
147+
)
148+
}
149+
}
150+
151+
extension ReceptionReport: MarshalSize {
152+
public func marshalSize() -> Int {
153+
let l = self.rawSize()
154+
// align to 32-bit boundary
155+
return l + getPadding(l)
156+
}
157+
}
158+
159+
extension ReceptionReport: Marshal {
160+
/// marshal_to encodes the ReceptionReport in binary
161+
public func marshalTo(_ buf: inout ByteBuffer) throws -> Int {
162+
/*
163+
* 0 1 2 3
164+
* 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
165+
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
166+
* | SSRC |
167+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
168+
* | fraction lost | cumulative number of packets lost |
169+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170+
* | extended highest sequence number received |
171+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172+
* | interarrival jitter |
173+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
174+
* | last SR (LSR) |
175+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
176+
* | delay since last SR (DLSR) |
177+
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
178+
*/
179+
buf.writeInteger(self.ssrc)
180+
181+
buf.writeInteger(self.fractionLost)
182+
183+
// pack TotalLost into 24 bits
184+
if self.totalLost >= (1 << 25) {
185+
throw RtcpError.errInvalidTotalLost
186+
}
187+
188+
buf.writeInteger(UInt8((self.totalLost >> 16) & 0xFF))
189+
buf.writeInteger(UInt8((self.totalLost >> 8) & 0xFF))
190+
buf.writeInteger(UInt8(self.totalLost & 0xFF))
191+
192+
buf.writeInteger(self.lastSequenceNumber)
193+
buf.writeInteger(self.jitter)
194+
buf.writeInteger(self.lastSenderReport)
195+
buf.writeInteger(self.delay)
196+
197+
putPadding(&buf, self.rawSize())
198+
199+
return self.marshalSize()
200+
}
201+
}

0 commit comments

Comments
 (0)